qtractor-1.5.9/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215101070305015173 xustar0030 mtime=1761898693.053858234 30 atime=1761898693.053858234 30 ctime=1761898693.053858234 qtractor-1.5.9/CMakeLists.txt0000644000175000001440000007203215101070305015167 0ustar00rncbcuserscmake_minimum_required (VERSION 3.15) project (Qtractor VERSION 1.5.9 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 (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 (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. 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) 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) show_option (" Beat-detection support (libaubio) . . . . . . . ." CONFIG_LIBAUBIO) 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.9/PaxHeaders/ChangeLog0000644000000000000000000000013215101070305014205 xustar0030 mtime=1761898693.055267553 30 atime=1761898693.053858234 30 ctime=1761898693.055267553 qtractor-1.5.9/ChangeLog0000644000175000001440000061024015101070305014200 0ustar00rncbcusersQtractor - An Audio/MIDI multi-track sequencer ---------------------------------------------- ChangeLog 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.9/PaxHeaders/LICENSE0000644000000000000000000000013215101070305013440 xustar0030 mtime=1761898693.055267553 30 atime=1761898693.055267553 30 ctime=1761898693.055267553 qtractor-1.5.9/LICENSE0000644000175000001440000004310315101070305013431 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.9/PaxHeaders/README0000644000000000000000000000013215101070305013313 xustar0030 mtime=1761898693.055267553 30 atime=1761898693.055267553 30 ctime=1761898693.055267553 qtractor-1.5.9/README0000644000175000001440000002320115101070305013301 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.9/PaxHeaders/README.VST20000644000000000000000000000013215101070305014050 xustar0030 mtime=1761898693.055267553 30 atime=1761898693.055267553 30 ctime=1761898693.055267553 qtractor-1.5.9/README.VST20000644000175000001440000000363715101070305014051 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.9/PaxHeaders/TRANSLATORS0000644000000000000000000000013215101070305014172 xustar0030 mtime=1761898693.055267553 30 atime=1761898693.055267553 30 ctime=1761898693.055267553 qtractor-1.5.9/TRANSLATORS0000644000175000001440000000105515101070305014163 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.9/PaxHeaders/src0000644000000000000000000000013215101070305013145 xustar0030 mtime=1761898693.107267718 30 atime=1761898693.056377872 30 ctime=1761898693.107267718 qtractor-1.5.9/src/0000755000175000001440000000000015101070305013212 5ustar00rncbcusersqtractor-1.5.9/src/PaxHeaders/qtractorMidiConnect.h0000644000000000000000000000013215101070305017347 xustar0030 mtime=1761898693.077267623 30 atime=1761898693.077267623 30 ctime=1761898693.077267623 qtractor-1.5.9/src/qtractorMidiConnect.h0000644000175000001440000001034515101070305017342 0ustar00rncbcusers// qtractorMidiConnect.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 __qtractorMidiConnect_h #define __qtractorMidiConnect_h #include "qtractorConnect.h" #include // Forward declarations. class qtractorMidiPortItem; class qtractorMidiClientItem; class qtractorMidiClientListView; class qtractorMidiConnect; //---------------------------------------------------------------------- // qtractorMidiPortItem -- Alsa port list item. // class qtractorMidiPortItem : public qtractorPortListItem { public: // Constructor. qtractorMidiPortItem(qtractorMidiClientItem *pClientItem); // Default destructor. ~qtractorMidiPortItem(); // Jack handles accessors. int alsaClient() const; int alsaPort() const; }; //---------------------------------------------------------------------- // qtractorMidiClientItem -- Alsa client list item. // class qtractorMidiClientItem : public qtractorClientListItem { public: // Constructor. qtractorMidiClientItem(qtractorMidiClientListView *pClientListView); // Default destructor. ~qtractorMidiClientItem(); // Jack client accessors. int alsaClient() const; // Port finder by id. qtractorMidiPortItem *findPortItem(int iAlsaPort); }; //---------------------------------------------------------------------- // qtractorMidiClientListView -- Alsa client list view. // class qtractorMidiClientListView : public qtractorClientListView { public: // Constructor. qtractorMidiClientListView(QWidget *pParent = nullptr); // Default destructor. ~qtractorMidiClientListView(); // Alsa sequencer accessor. snd_seq_t *alsaSeq() const; // Client finder by id. qtractorMidiClientItem *findClientItem(int iAlsaClient); // Client port finder by id. qtractorMidiPortItem *findClientPortItem(int iAlsaClient, int iAlsaPort); // Client:port refreshner (return newest item count). int updateClientPorts(); }; //---------------------------------------------------------------------------- // qtractorMidiConnect -- Connections model integrated object. // class qtractorMidiConnect : public qtractorConnect { public: // Constructor. qtractorMidiConnect( qtractorMidiClientListView *pOListView, qtractorMidiClientListView *pIListView, qtractorConnectorView *pConnectorView); // Default destructor. ~qtractorMidiConnect(); // ALSA sequencer accessor. snd_seq_t *alsaSeq() 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. IconCount = 4 // 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) MIDI-buses connect lists (non-virtual). void disconnectPortsUpdate( qtractorPortListItem *pOPort, qtractorPortListItem *pIPort); private: // Local pixmap-set janitor methods. void createIcons(); void deleteIcons(); // Local static pixmap-set array. static QIcon *g_apIcons[IconCount]; static int g_iIconsRefCount; }; #endif // __qtractorMidiConnect_h // end of qtractorMidiConnect.h qtractor-1.5.9/src/PaxHeaders/qtractorShortcutForm.ui0000644000000000000000000000013215101070305020000 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.088267658 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorShortcutForm.ui0000644000175000001440000000746315101070305020002 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.9/src/PaxHeaders/qtractorObserverWidget.h0000644000000000000000000000013215101070305020106 xustar0030 mtime=1761898693.085267648 30 atime=1761898693.085267648 30 ctime=1761898693.085267648 qtractor-1.5.9/src/qtractorObserverWidget.h0000644000175000001440000001120615101070305020076 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.9/src/PaxHeaders/qtractorMidiMonitor.h0000644000000000000000000000013215101070305017405 xustar0030 mtime=1761898693.083267642 30 atime=1761898693.083267642 30 ctime=1761898693.083267642 qtractor-1.5.9/src/qtractorMidiMonitor.h0000644000175000001440000000556415101070305017407 0ustar00rncbcusers// qtractorMidiMonitor.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 __qtractorMidiMonitor_h #define __qtractorMidiMonitor_h #include "qtractorMonitor.h" #include "qtractorMidiEvent.h" // Forwrad decalarations. class qtractorTimeScale; //---------------------------------------------------------------------------- // qtractorMidiMonitor -- MIDI monitor bridge value processor. class qtractorMidiMonitor : public qtractorMonitor { public: // Constructor. qtractorMidiMonitor(float fGain = 1.0f, float fPanning = 0.0f); // Copy constructor. qtractorMidiMonitor(const qtractorMidiMonitor& monitor); // Destructor. ~qtractorMidiMonitor(); // Monitor enqueue methods. void enqueue(qtractorMidiEvent::EventType type, unsigned char val, unsigned long tick = 0); // Monitor dequeue methods. float value_stamp(unsigned long iStamp); int count_stamp(unsigned long iStamp); // Clear monitor. void clear(); // Reset monitor. void reset(); // Singleton time base reset. static void resetTime(qtractorTimeScale *pTimeScale, unsigned long iFrame); // Singleton time base split (scheduled tempo change) static void splitTime(qtractorTimeScale *pTimeScale, unsigned long iFrame, unsigned long iTime); protected: // Singleton time base slot. static unsigned int timeSlot(unsigned long iTime) { return g_iTimeSlot[g_iTimeSplit >= iTime ? 0 : 1]; } // Update monitor (nothing really done here). void update(); private: // Queue iten struct. struct QueueItem { unsigned char value; unsigned char count; }; // Instance variables. QueueItem *m_pQueue; unsigned int m_iQueueIndex; unsigned long m_iFrameStart; unsigned long m_iTimeStart; QueueItem m_item; QueueItem m_prev; float m_fValue; unsigned long m_iValueStamp; unsigned long m_iCountStamp; // Singleton variables. static unsigned int g_iFrameSlot; static unsigned int g_iTimeSlot[2]; static unsigned long g_iTimeSplit; }; #endif // __qtractorMidiMonitor_h // end of qtractorMidiMonitor.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiControlPlugin.cpp0000644000000000000000000000013215101070305021110 xustar0030 mtime=1761898693.078267626 30 atime=1761898693.078267626 30 ctime=1761898693.078267626 qtractor-1.5.9/src/qtractorMidiControlPlugin.cpp0000644000175000001440000004104515101070305021104 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.9/src/PaxHeaders/qtractorFiles.cpp0000644000000000000000000000013215101070305016550 xustar0030 mtime=1761898693.072267607 30 atime=1761898693.071267604 30 ctime=1761898693.072267607 qtractor-1.5.9/src/qtractorFiles.cpp0000644000175000001440000003741015101070305016545 0ustar00rncbcusers// qtractorFiles.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 "qtractorFiles.h" #include "qtractorMainForm.h" #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #include #endif //------------------------------------------------------------------------- // qtractorFilesTabWidget - File/Groups dockable child window. // class qtractorFilesTabWidget : public QTabWidget { public: // Constructor. qtractorFilesTabWidget(QWidget *pParent) : QTabWidget(pParent) {} protected: // Minimum recommended. QSize sizeHint() const { return QTabWidget::minimumSize(); } }; //------------------------------------------------------------------------- // qtractorFiles - File/Groups dockable window. // // Constructor. qtractorFiles::qtractorFiles ( QWidget *pParent ) : QDockWidget(pParent) { // Surely a name is crucial (e.g.for storing geometry settings) QDockWidget::setObjectName("qtractorFiles"); // Create file type selection tab widget. const QFont& font = QDockWidget::font(); m_pTabWidget = new qtractorFilesTabWidget(this); m_pTabWidget->setFont(QFont(font.family(), font.pointSize() - 1)); m_pTabWidget->setTabPosition(QTabWidget::South); // Create local tabs. m_pAudioListView = new qtractorAudioListView(); m_pMidiListView = new qtractorMidiListView(); // Add respective... m_pTabWidget->addTab(m_pAudioListView, tr("Audio")); m_pTabWidget->addTab(m_pMidiListView, tr("MIDI")); // Icons... m_pTabWidget->setTabIcon(qtractorFiles::Audio, QIcon::fromTheme("trackAudio")); m_pTabWidget->setTabIcon(qtractorFiles::Midi, QIcon::fromTheme("trackMidi")); m_pTabWidget->setUsesScrollButtons(false); // Player button (initially disabled)... m_pPlayWidget = new QWidget(m_pTabWidget); m_pPlayLayout = new QHBoxLayout(/*m_pPlayWidget*/); m_pPlayLayout->setContentsMargins(2, 2, 2, 2); m_pPlayLayout->setSpacing(2); m_pPlayWidget->setLayout(m_pPlayLayout); m_pPlayButton = new QToolButton(m_pPlayWidget); m_pPlayButton->setIcon(QIcon::fromTheme("transportPlay")); m_pPlayButton->setToolTip(tr("Play file")); m_pPlayButton->setCheckable(true); m_pPlayButton->setEnabled(false); m_pPlayLayout->addWidget(m_pPlayButton); m_pTabWidget->setCornerWidget(m_pPlayWidget, Qt::BottomRightCorner); // Common file list-view actions... m_pNewGroupAction = new QAction( QIcon::fromTheme("itemGroup"), tr("New &Group..."), this); m_pOpenFileAction = new QAction( QIcon::fromTheme("itemFile"), tr("Add &Files..."), this); m_pCutItemAction = new QAction( QIcon::fromTheme("editCut"), tr("Cu&t"), nullptr); m_pCopyItemAction = new QAction( QIcon::fromTheme("editCopy"), tr("&Copy"), nullptr); m_pPasteItemAction = new QAction( QIcon::fromTheme("editPaste"), tr("&Paste"), nullptr); m_pRenameItemAction = new QAction( QIcon::fromTheme("formEdit"), tr("Re&name"), this); m_pRemoveItemAction = new QAction( QIcon::fromTheme("formRemove"), tr("&Remove"), nullptr); m_pPlayItemAction = new QAction( QIcon::fromTheme("transportPlay"), tr("Pla&y"), this); m_pPlayItemAction->setCheckable(true); m_pCleanupAction = new QAction(tr("Cl&eanup"), this); // Some actions surely need those // shortcuts firmly attached... // m_pNewGroupAction->setShortcut(tr("Ctrl+G")); // m_pOpenFileAction->setShortcut(tr("Ctrl+F")); m_pCutItemAction->setShortcut(tr("Ctrl+X")); m_pCopyItemAction->setShortcut(tr("Ctrl+C")); m_pPasteItemAction->setShortcut(tr("Ctrl+V")); // m_pRenameItemAction->setShortcut(tr("Ctrl+N")); m_pRemoveItemAction->setShortcut(tr("Del")); // m_pPlayItemAction->setShortcut(tr("Ctrl+Y")); // m_pCleanupAction->setShortcut(tr("Ctrl+E")); #if 0 // QDockWidget::addAction(m_pNewGroupAction); // QDockWidget::addAction(m_pOpenFileAction); QDockWidget::addAction(m_pCutItemAction); QDockWidget::addAction(m_pCopyItemAction); QDockWidget::addAction(m_pPasteItemAction); // QDockWidget::addAction(m_pRenameItemAction); QDockWidget::addAction(m_pRemoveItemAction); // QDockWidget::addAction(m_pPlayItemAction); // QDockWidget::addAction(m_pCleanupAction); #endif // m_pNewGroupAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); // m_pOpenFileAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); m_pCutItemAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); m_pCopyItemAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); m_pPasteItemAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); // m_pRenameItemAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); m_pRemoveItemAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); // m_pPlayItemAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); // m_pCleanupAction->setShortcutContext(Qt::WidgetWithChildrenShortcut); // Prepare the dockable window stuff. QDockWidget::setWidget(m_pTabWidget); // QDockWidget::setFeatures(QDockWidget::AllDockWidgetFeatures); QDockWidget::setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); // Some specialties to this kind of dock window... QDockWidget::setMinimumWidth(160); // Finally set the default caption and tooltip. const QString& sCaption = tr("Files"); QDockWidget::setWindowTitle(sCaption); QDockWidget::setWindowIcon(QIcon::fromTheme("viewFiles")); QDockWidget::setToolTip(sCaption); // Make it initially stable... stabilizeSlot(); // Child widgets signal/slots... QObject::connect(m_pTabWidget, SIGNAL(currentChanged(int)), SLOT(stabilizeSlot())); QObject::connect(m_pAudioListView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(stabilizeSlot())); QObject::connect(m_pAudioListView, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SLOT(stabilizeSlot())); QObject::connect(m_pMidiListView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(stabilizeSlot())); QObject::connect(m_pMidiListView, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SLOT(stabilizeSlot())); QObject::connect(m_pNewGroupAction, SIGNAL(triggered(bool)), SLOT(newGroupSlot())); QObject::connect(m_pOpenFileAction, SIGNAL(triggered(bool)), SLOT(openFileSlot())); QObject::connect(m_pCutItemAction, SIGNAL(triggered(bool)), SLOT(cutItemSlot())); QObject::connect(m_pCopyItemAction, SIGNAL(triggered(bool)), SLOT(copyItemSlot())); QObject::connect(m_pPasteItemAction, SIGNAL(triggered(bool)), SLOT(pasteItemSlot())); QObject::connect(m_pRenameItemAction, SIGNAL(triggered(bool)), SLOT(renameItemSlot())); QObject::connect(m_pRemoveItemAction, SIGNAL(triggered(bool)), SLOT(removeItemSlot())); QObject::connect(m_pPlayItemAction, SIGNAL(triggered(bool)), SLOT(playSlot(bool))); QObject::connect(m_pCleanupAction, SIGNAL(triggered(bool)), SLOT(cleanupSlot())); QObject::connect(m_pPlayButton, SIGNAL(toggled(bool)), SLOT(playSlot(bool))); QObject::connect(QApplication::clipboard(), SIGNAL(dataChanged()), SLOT(stabilizeSlot())); } // Destructor. qtractorFiles::~qtractorFiles (void) { delete m_pNewGroupAction; delete m_pOpenFileAction; delete m_pCutItemAction; delete m_pCopyItemAction; delete m_pPasteItemAction; delete m_pRenameItemAction; delete m_pRemoveItemAction; delete m_pCleanupAction; delete m_pPlayItemAction; // No need to delete child widgets, Qt does it all for us. } // Just about to notify main-window that we're closing. void qtractorFiles::closeEvent ( QCloseEvent * /*pCloseEvent*/ ) { QDockWidget::hide(); qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->stabilizeForm(); } // Audio file list view accessor. qtractorAudioListView *qtractorFiles::audioListView (void) const { return m_pAudioListView; } // MIDI file list view accessor. qtractorMidiListView *qtractorFiles::midiListView (void) const { return m_pMidiListView; } // Clear everything on sight. void qtractorFiles::clear (void) { m_pAudioListView->clear(); m_pMidiListView->clear(); setPlayState(false); } // Check whether one of the widgets has focus (oveerride method) bool qtractorFiles::hasFocus (void) const { if (m_pAudioListView->hasFocus() || m_pMidiListView->hasFocus()) return true; return QDockWidget::hasFocus(); } // Tell whether a file item is currently selected. bool qtractorFiles::isFileSelected (void) const { qtractorFileListView *pFileListView = currentFileListView(); if (pFileListView) { QTreeWidgetItem *pItem = pFileListView->currentItem(); return (pItem && pItem->type() == qtractorFileListView::FileItem); } return false; } // Audio file addition convenience method. void qtractorFiles::addAudioFile ( const QString& sFilename, bool bSelect ) { if (bSelect) m_pTabWidget->setCurrentIndex(qtractorFiles::Audio); m_pAudioListView->addFileItem(sFilename); } // MIDI file addition convenience method. void qtractorFiles::addMidiFile ( const QString& sFilename, bool bSelect ) { if (bSelect) m_pTabWidget->setCurrentIndex(qtractorFiles::Midi); m_pMidiListView->addFileItem(sFilename); } // Audio file removal convenience method. void qtractorFiles::removeAudioFile ( const QString& sFilename, bool bSelect ) { if (bSelect) m_pTabWidget->setCurrentIndex(qtractorFiles::Audio); m_pAudioListView->removeFileItem(sFilename); } // MIDI file removal convenience method. void qtractorFiles::removeMidiFile ( const QString& sFilename, bool bSelect ) { if (bSelect) m_pTabWidget->setCurrentIndex(qtractorFiles::Midi); m_pMidiListView->removeFileItem(sFilename); } // Audio file selection convenience method. void qtractorFiles::selectAudioFile ( const QString& sFilename ) { m_pTabWidget->setCurrentIndex(qtractorFiles::Audio); m_pAudioListView->selectFileItem(sFilename); } // MIDI file selection convenience method. void qtractorFiles::selectMidiFile ( const QString& sFilename, int iTrackChannel ) { m_pTabWidget->setCurrentIndex(qtractorFiles::Midi); m_pMidiListView->selectFileItem(sFilename, iTrackChannel); } // Audition/pre-listening player methods. void qtractorFiles::setPlayState ( bool bOn ) { const bool bBlockPlayAction = m_pPlayItemAction->blockSignals(true); const bool bBlockPlayButton = m_pPlayButton->blockSignals(true); m_pPlayItemAction->setChecked(bOn); m_pPlayButton->setChecked(bOn); m_pPlayButton->blockSignals(bBlockPlayButton); m_pPlayItemAction->blockSignals(bBlockPlayAction); } bool qtractorFiles::isPlayState (void) const { return m_pPlayItemAction->isChecked(); } // Retrieve current selected file list view. qtractorFileListView *qtractorFiles::currentFileListView (void) const { switch (m_pTabWidget->currentIndex()) { case qtractorFiles::Audio: return m_pAudioListView; case qtractorFiles::Midi: return m_pMidiListView; default: return nullptr; } } // Open and add a new file item below the current group one. void qtractorFiles::openFileSlot (void) { qtractorFileListView *pFileListView = currentFileListView(); if (pFileListView) pFileListView->openFile(); } // Add a new group item below the current one. void qtractorFiles::newGroupSlot (void) { qtractorFileListView *pFileListView = currentFileListView(); if (pFileListView) pFileListView->newGroup(); } // Cut current file item(s) to clipboard. void qtractorFiles::cutItemSlot (void) { qtractorFileListView *pFileListView = currentFileListView(); if (pFileListView) pFileListView->copyItem(true); } // Copy current file item(s) to clipboard. void qtractorFiles::copyItemSlot (void) { qtractorFileListView *pFileListView = currentFileListView(); if (pFileListView) pFileListView->copyItem(false); } // Paste file item(s) from clipboard. void qtractorFiles::pasteItemSlot (void) { qtractorFileListView *pFileListView = currentFileListView(); if (pFileListView) pFileListView->pasteItem(); } // Rename current group/file item. void qtractorFiles::renameItemSlot (void) { qtractorFileListView *pFileListView = currentFileListView(); if (pFileListView) pFileListView->renameItem(); } // Remove current group/file item. void qtractorFiles::removeItemSlot (void) { qtractorFileListView *pFileListView = currentFileListView(); if (pFileListView) pFileListView->removeItem(); } // Current item change slots. void qtractorFiles::stabilizeSlot (void) { qtractorFileListView *pFileListView = currentFileListView(); if (pFileListView) { QTreeWidgetItem *pItem = pFileListView->currentItem(); m_pCutItemAction->setEnabled( pItem && pItem->type() == qtractorFileListView::FileItem); m_pCopyItemAction->setEnabled( pItem && pItem->type() == qtractorFileListView::FileItem); #if QT_VERSION >= 0x0050000 const QMimeData *pMimeData = QApplication::clipboard()->mimeData(); m_pPasteItemAction->setEnabled( pMimeData && pMimeData->hasUrls()); #else m_pPasteItemAction->setEnabled(false); #endif m_pRenameItemAction->setEnabled( pItem && pItem->type() == qtractorFileListView::GroupItem); m_pRemoveItemAction->setEnabled( pItem && pItem->type() != qtractorFileListView::ChannelItem); m_pCleanupAction->setEnabled(pFileListView->topLevelItemCount() > 0); const bool bPlayEnabled = (pItem && pItem->type() != qtractorFileListView::GroupItem); m_pPlayItemAction->setEnabled(bPlayEnabled); m_pPlayButton->setEnabled(bPlayEnabled); } // Stabilize main form as well... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->stabilizeForm(); } // Audition/pre-listening player slot. void qtractorFiles::playSlot ( bool bOn ) { setPlayState(bOn); if (bOn) { qtractorFileListView *pFileListView = currentFileListView(); if (pFileListView) pFileListView->activateItem(); } } // Clean-up unused file items. void qtractorFiles::cleanupSlot (void) { qtractorFileListView *pFileListView = currentFileListView(); if (pFileListView) pFileListView->cleanup(); } // Tab page switch slots. void qtractorFiles::pageAudioSlot (void) { m_pTabWidget->setCurrentIndex(qtractorFiles::Audio); } void qtractorFiles::pageMidiSlot (void) { m_pTabWidget->setCurrentIndex(qtractorFiles::Midi); } // Context menu request event handler. void qtractorFiles::contextMenuEvent ( QContextMenuEvent *pContextMenuEvent ) { QMenu menu(this); // Construct context menu. menu.addAction(m_pNewGroupAction); menu.addAction(m_pOpenFileAction); menu.addSeparator(); menu.addAction(m_pCutItemAction); menu.addAction(m_pCopyItemAction); menu.addAction(m_pPasteItemAction); menu.addSeparator(); menu.addAction(m_pRenameItemAction); menu.addSeparator(); menu.addAction(m_pRemoveItemAction); menu.addAction(m_pCleanupAction); menu.addSeparator(); menu.addAction(m_pPlayItemAction); menu.addSeparator(); // Switch page options... switch (m_pTabWidget->currentIndex()) { case qtractorFiles::Audio: menu.addAction(QIcon::fromTheme("trackMidi"), tr("MIDI Files"), this, SLOT(pageMidiSlot())); break; case qtractorFiles::Midi: menu.addAction(QIcon::fromTheme("trackAudio"), tr("Audio Files"), this, SLOT(pageAudioSlot())); break; default: break; } stabilizeSlot(); menu.exec(pContextMenuEvent->globalPos()); } // end of qtractorFiles.cpp qtractor-1.5.9/src/PaxHeaders/qtractorOptions.h0000644000000000000000000000013215101070305016606 xustar0030 mtime=1761898693.085267648 30 atime=1761898693.085267648 30 ctime=1761898693.085267648 qtractor-1.5.9/src/qtractorOptions.h0000644000175000001440000002242015101070305016576 0ustar00rncbcusers// qtractorOptions.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 __qtractorOptions_h #define __qtractorOptions_h #include #include class QWidget; class QComboBox; class QSplitter; class QObject; //------------------------------------------------------------------------- // qtractorOptions - Prototype settings class (singleton). // class qtractorOptions { public: // Constructor. qtractorOptions(); // Default destructor. ~qtractorOptions(); // The settings object accessor. QSettings& settings(); // Explicit I/O methods. void loadOptions(); void saveOptions(); // Command line arguments parser. bool parse_args(const QStringList& args); #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) void show_error(const QString& msg); #else // Command line usage helper. void print_usage(const QString& arg0); #endif // Startup supplied session uuid. QString sSessionId; // Startup supplied session file. QStringList sessionFiles; // Display options... QString sMessagesFont; bool bMessagesLimit; int iMessagesLimitLines; bool bConfirmRemove; bool bConfirmArchive; bool bStdoutCapture; bool bCompletePath; bool bPeakAutoRemove; bool bKeepToolsOnTop; bool bKeepEditorsOnTop; int iDisplayFormat; int iBaseFontSize; // Logging options... bool bMessagesLog; QString sMessagesLogPath; // View options... bool bMenubar; bool bStatusbar; bool bFileToolbar; bool bEditToolbar; bool bTrackToolbar; bool bViewToolbar; bool bOptionsToolbar; bool bTransportToolbar; bool bTimeToolbar; bool bThumbToolbar; int iZoomMode; // Transport options... bool bCountIn; bool bMetronome; bool bFollowPlayhead; bool bAutoBackward; bool bContinuePastEnd; int iTransportMode; bool bTimebase; // Audio options... QString sAudioCaptureExt; int iAudioCaptureType; int iAudioCaptureFormat; int iAudioCaptureQuality; QString sAudioExportExt; int iAudioExportType; int iAudioExportFormat; int iAudioExportQuality; int iAudioResampleType; bool bAudioAutoTimeStretch; bool bAudioWsolaTimeStretch; bool bAudioWsolaQuickSeek; bool bAudioRubberBandFormant; bool bAudioRubberBandFinerR3; bool bAudioPlayerBus; bool bAudioMetroBus; bool bAudioMetronome; bool bAudioMasterAutoConnect; bool bAudioPlayerAutoConnect; bool bAudioMetroAutoConnect; bool bAudioSelfConnected; // Audio metronome latency offset compensation. unsigned long iAudioMetroOffset; // Audio metronome parameters. QString sMetroBarFilename; float fMetroBarGain; QString sMetroBeatFilename; float fMetroBeatGain; // Audio metronome count-in options. int iAudioCountInMode; int iAudioCountInBeats; // MIDI options... int iMidiCaptureFormat; int iMidiExportFormat; int iMidiCaptureQuantize; int iMidiQueueTimer; bool bMidiDriftCorrect; bool bMidiPlayerBus; bool bMidiControlBus; bool bMidiMetroBus; bool bMidiMetronome; int iMidiMetroOffset; int iMidiMmcDevice; int iMidiMmcMode; int iMidiSppMode; int iMidiClockMode; // Whether to reset all MIDI controllers (on playback start). bool bMidiResetAllControllers; // MIDI metronome parameters. int iMetroChannel; int iMetroBarNote; int iMetroBarVelocity; int iMetroBarDuration; int iMetroBeatNote; int iMetroBeatVelocity; int iMetroBeatDuration; // MIDI metronome count-in options. int iMidiCountInMode; int iMidiCountInBeats; // Default options... QString sSessionDir; QString sAudioDir; QString sMidiDir; QString sPresetDir; QString sInstrumentDir; QString sMidiControlDir; QString sMidiSysexDir; QString sPluginsDir; // Session options. QString sSessionExt; bool bSessionTemplate; QString sSessionTemplatePath; bool bSessionBackup; int iSessionBackupMode; bool bAutoMonitor; bool bAutoDeactivate; int iSnapPerBeat; float fTempo; int iBeatsPerBar; int iBeatDivisor; int iLoopRecordingMode; // Session directory auto-name default. bool bAutoSessionDir; // Paste-repeat convenient defaults. int iPasteRepeatCount; bool bPasteRepeatPeriod; // Plugin search string. QString sPluginSearch; int iPluginType; bool bPluginActivate; // Automation curve mode default. int iCurveMode; // Edit-range options. int iEditRangeOptions; // Keyboard modifier options. bool bShiftKeyModifier; // Mouse modifier options. bool bMidButtonModifier; // MIDI control non catch-up/hook option. bool bMidiControlSync; // Export tracks options. int iExportRangeType; unsigned long iExportRangeStart; unsigned long iExportRangeEnd; bool bExportAddTrack; // Marker color (LRU). QString sMarkerColor; // Curve color (LRU). QString sCurveColor; // Track auto-background color option. bool bAutoBackgroundColor; // Session auto-save options. bool bAutoSaveEnabled; int iAutoSavePeriod; QString sAutoSavePathname; QString sAutoSaveFilename; // Plug-in paths. QStringList ladspaPaths; QStringList dssiPaths; QStringList vst2Paths; QStringList vst3Paths; QStringList clapPaths; QStringList lv2Paths; QString sLv2PresetDir; // Plug-in instrument options. bool bAudioOutputBus; bool bAudioOutputAutoConnect; // Plug-in GUI options. bool bOpenEditor; // Whether to ask for plug-in editor (GUI) // when more than one is available. bool bQueryEditorType; // Out-of-process plugin scanning and cache option. int iDummyLadspaHash; int iDummyDssiHash; int iDummyVst2Hash; int iDummyVst3Hash; int iDummyClapHash; int iDummyLv2Hash; // The instrument file list. QStringList instrumentFiles; // The MIDI controllers file list. QStringList midiControlFiles; // Recent file list. int iMaxRecentFiles; QStringList recentFiles; // Tracks view options... int iTrackViewSelectMode; bool bTrackViewDropSpan; bool bTrackViewSnapZebra; bool bTrackViewSnapGrid; bool bTrackViewToolTips; bool bTrackViewCurveEdit; int iTrackColorSaturation; // MIDI Editor options... bool bMidiMenubar; bool bMidiStatusbar; bool bMidiFileToolbar; bool bMidiEditToolbar; bool bMidiViewToolbar; bool bMidiTransportToolbar; bool bMidiTimeToolbar; bool bMidiScaleToolbar; bool bMidiThumbToolbar; int iMidiDisplayFormat; bool bMidiNoteNames; bool bMidiNoteDuration; bool bMidiNoteColor; bool bMidiValueColor; bool bMidiPreview; bool bMidiFollow; bool bMidiEditMode; bool bMidiEditModeDraw; int iMidiZoomMode; int iMidiHorizontalZoom; int iMidiVerticalZoom; int iMidiSnapPerBeat; bool bMidiSnapZebra; bool bMidiSnapGrid; bool bMidiToolTips; int iMidiViewType; int iMidiEventType; int iMidiEventParam; int iMidiSnapToScaleKey; int iMidiSnapToScaleType; // Meter colors. QStringList audioMeterColors; QStringList midiMeterColors; // Transport view options. bool bSyncViewHold; // Global persistent user preference options. bool bUseNativeDialogs; // Run-time special non-persistent options. bool bDontUseNativeDialogs; // Custom display options. QString sCustomColorTheme; QString sCustomStyleTheme; QString sCustomStyleSheet; QString sCustomIconsTheme; // Widget geometry persistence helper prototypes. void saveWidgetGeometry(QWidget *pWidget, bool bVisible = false); void loadWidgetGeometry(QWidget *pWidget, bool bVisible = false); // Combo box history persistence helper prototypes. void loadComboBoxHistory(QComboBox *pComboBox, int iLimit = 8); void saveComboBoxHistory(QComboBox *pComboBox, int iLimit = 8); void loadComboBoxFileHistory(QComboBox *pComboBox); void saveComboBoxFileHistory(QComboBox *pComboBox); bool setComboBoxCurrentFile(QComboBox *pComboBox, const QString& sFilename); QString comboBoxCurrentFile(QComboBox *pComboBox); // Splitter widget sizes persistence helper methods. void loadSplitterSizes(QSplitter *pSplitter, QList& sizes); void saveSplitterSizes(QSplitter *pSplitter); // Action shortcut persistence helper methos. void loadActionShortcuts(QObject *pObject); void saveActionShortcuts(QObject *pObject); // Action MIDI observers persistence helper methods. void loadActionControls(QObject *pObject); void saveActionControls(QObject *pObject); // Singleton instance accessor. static qtractorOptions *getInstance(); private: // Settings member variables. QSettings m_settings; // The singleton instance. static qtractorOptions *g_pOptions; }; #endif // __qtractorOptions_h // end of qtractorOptions.h qtractor-1.5.9/src/PaxHeaders/qtractorVst2Plugin.h0000644000000000000000000000012715101070305017174 xustar0029 mtime=1761898693.09226767 29 atime=1761898693.09226767 29 ctime=1761898693.09226767 qtractor-1.5.9/src/qtractorVst2Plugin.h0000644000175000001440000001623315101070305017165 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.9/src/PaxHeaders/qtractorOptionsForm.ui0000644000000000000000000000013215101070305017620 xustar0030 mtime=1761898693.085267648 30 atime=1761898693.085267648 30 ctime=1761898693.085267648 qtractor-1.5.9/src/qtractorOptionsForm.ui0000644000175000001440000043353215101070305017622 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. qtractorOptionsForm 0 0 520 360 Options :/images/qtractor.svg true 4 8 false 0 &General 75 true Session true 50 false Default session &file format: TransportModeComboBox 50 false Default session file format (suffix) Qt::Horizontal 120 20 50 false Whether to create new sessions based on template &New session template: 1 0 320 0 50 false New session template true 22 22 24 24 50 false Browse for new session template Qt::Vertical 20 4 50 false Whether to save backup versions of existing sessions Save &backup versions of existing sessions: 50 false Which mode to rename existing session files Increment previous version (default) Increment current version 50 false Whether to enable session auto-save (crash-recovery) Auto-save current working session every: 50 false Auto-save period (minutes) minutes 2 120 5 75 true Options true 50 false Whether to ask for confirmation on removal &Confirm removals 50 false Whether to ask for confirmation on archive directory removal C&onfirm archive removals 50 false Number of &recent files: Qt::AlignRight|Qt::AlignVCenter MaxRecentFilesSpinBox 0 0 50 false The maximum number of recent files to keep in menu 0 20 5 50 false Whether to capture standard output (stdout/stderr) into messages window Capture standard &output 50 false Whether to show the complete directory path of loaded session files S&how complete path of session files 50 false Whether to remove audio peak files on session close Auto-remove audio pea&k files 50 false Whether to keep all tool windows on top of the main window Keep tool &windows always on top 50 false Whether to try dropping multiple audio files into the same track &Drop multiple audio files into the same track 50 false Whether to reverse keyboard modifiers role on transport positioning (Shift/Ctrl) Reverse &keyboard modifiers role (Shift/Ctrl) 50 false Whether to reverse mouse middle-button role on keyboard modifiers (Shift/Ctrl) Re&verse middle-button modifier role (Shift/Ctrl) 50 false Whether to keep all editor windows on top of the main window Keep &editor windows always on top Qt::Vertical 20 4 75 true Transport true 50 false Transport &mode: Qt::AlignRight|Qt::AlignVCenter TransportModeComboBox 50 false Transport control mode (JACK) None Slave Master Full 50 false Whether to start as timebase master (JACK) &Timebase Qt::Horizontal 20 20 50 false &Loop recording mode (takes): Qt::AlignRight|Qt::AlignVCenter LoopRecordingModeComboBox 50 false Loop recording mode (takes) None First Last &Audio 75 true Capture / Export true 8 4 50 false File &type: AudioCaptureTypeComboBox 50 false Audio file type to use on capture (record) and export Qt::Horizontal 20 20 50 false Sample &format: AudioCaptureFormatComboBox 50 false Audio sample format to use on capture (record) and export 50 false Qt::AlignRight|Qt::AlignVCenter &Quality: AudioCaptureQualitySpinBox 60 32767 50 false Audio compression quality to use on capture (record) and export 0 10 1 4 75 true Playback true 50 false Whether to apply time-stretching when tempo changes Aut&omatic time-stretching Qt::Horizontal 20 20 50 false Sample-&rate converter type: Qt::AlignRight|Qt::AlignVCenter AudioResampleTypeComboBox 50 false Sample-rate converter quality Sinc (Best Quality) Sinc (Medium Quality) Sinc (Fastest) Zero Order Hold Linear 50 false Whether to use WSOLA time-stretching &WSOLA time-stretching 50 false Whether to use RubberBand formant preserve RubberBand &formant preserve 50 false Whether to apply WSOLA quick seek time-stretching WSOLA quic&k seek 50 false Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine 50 false Whether to have separate audition/pre-listening player output ports Dedicated au&dition/pre-listening player outputs: 50 false Whether to auto-connect dedicated audio player outputs Auto-&connect Qt::Horizontal 120 20 75 true Connections true 50 false Whether to warn about audio self-connections &Warn about self-connections Qt::Horizontal 20 20 Qt::Vertical 20 4 75 true Metronome true 50 false Whether to enable the audio metronome &Enable audio metronome 50 false &Count-in: Qt::AlignRight|Qt::AlignVCenter AudioCountInModeComboBox 50 false Count-in mode None Playback Recording 66 0 50 false Count-in number beats 2 24 4 50 false &File (bar): MetroBarFilenameComboBox 1 0 320 0 50 false Metronome Audio filename (bar) true 22 22 24 24 50 false Qt::TabFocus Browse for sample audio file (bar) 50 false &Gain (bar): MetroBarGainSpinBox 66 0 50 false Metronome gain (bar) dB 1 -60.0 12.0 0.0 50 false &File (beat): MetroBeatFilenameComboBox 1 0 320 0 50 false Metronome Audio filename (beat) true 22 22 24 24 50 false Qt::TabFocus Browse for sample audio file (beat) 50 false &Gain (beat): MetroBeatGainSpinBox 66 0 50 false Metronome gain (beat) dB 1 -60.0 12.0 0.0 50 false Whether to have separate audio metronome output ports Dedicated a&udio metronome outputs: 50 false Whether to auto-connect dedicated audio metronome outputs Auto-co&nnect 50 false &Offset (latency): Qt::AlignRight|Qt::AlignVCenter AudioMetroOffsetSpinBox 50 false Metronome Audio offset (latency) &MIDI 75 true Capture / Export true 50 false File &format: MidiCaptureFormatComboBox 50 false MIDI file format to use on capture (record) and export Qt::Horizontal 20 20 50 false &Quantize: Qt::AlignRight|Qt::AlignVCenter MidiCaptureQuantizeComboBox 120 32767 50 false MIDI capture (record) quantization 75 true Playback true 8 4 50 false Queue &timer (resolution): MidiQueueTimerComboBox 50 false Queue timer (resolution) Qt::Horizontal 20 20 50 false Whether to enable MIDI queue time drift correction E&nable MIDI queue time drift correction 50 false Whether to have separate MIDI player output ports Dedicated MIDI p&layer outputs 50 false Whether to reset/resend all controllers on playback start &Reset all controllers on playback start 75 true Control true 9 6 50 false &MMC: MidiMmcModeComboBox 50 false MIDI Machine Control (MMC) mode None Input Output Duplex 50 false &Device: MidiMmcDeviceComboBox 50 false MIDI Machine Control (MMC) device id. Qt::Horizontal 4 20 50 false &SPP: MidiSppModeComboBox 50 false MIDI Song Position pointer (SPP) control mode None Input Output Duplex Qt::Horizontal 4 20 50 false MIDI Cloc&k: MidiClockModeComboBox 50 false MIDI Clock control mode None Input Output 50 false Whether to have separate MIDI control ports Dedicated MIDI &control input/output Qt::Vertical 20 4 75 true Metronome true 50 false Whether to enable the MIDI metronome &Enable MIDI metronome Qt::Horizontal 4 20 50 false Qt::AlignRight|Qt::AlignVCenter &Channel: MetroChannelSpinBox 50 false Metronome MIDI channel 1 16 10 Qt::Horizontal 4 20 50 false &Count-in: Qt::AlignRight|Qt::AlignVCenter AudioCountInModeComboBox 50 false Count-in mode None Playback Recording 66 0 50 false Count-in number beats 2 24 4 50 false &Note (bar): MetroBarNoteComboBox 120 0 50 false Metronome MIDI note (bar) Qt::Horizontal 4 20 50 false &Velocity (bar): MetroBarVelocitySpinBox 50 false Metronome MIDI velocity (bar) 1 127 96 Qt::Horizontal 4 20 50 false &Duration (bar): MetroBarDurationSpinBox 50 false Metronome MIDI duration (bar) 1 9999 8 48 50 false &Note (beat): MetroBeatNoteComboBox 120 0 50 false Metronome MIDI note (beat) 50 false &Velocity (beat): MetroBeatVelocitySpinBox 50 false Metronome MIDI velocity (beat) 1 127 64 50 false &Duration (beat): MetroBeatDurationSpinBox 50 false Metronome MIDI duration (beat) 1 9999 8 24 50 false Whether to have separate MIDI metronome output port Dedicated M&IDI metronome output 50 false &Offset (latency): Qt::AlignRight|Qt::AlignVCenter AudioMetroOffsetSpinBox 50 false Metronome MIDI offset (latency) 0 9999 8 0 &Display 75 true Defaults true 50 false &Time display format: DisplayFormatComboBox 50 false Time display format Frames Time BBT Qt::Horizontal 20 20 50 false &Base font size: Qt::AlignRight|Qt::AlignVCenter BaseFontSizeComboBox 50 false Base application font size (pt.) true (default) 6 7 8 9 10 11 12 75 true Options true 8 4 50 false Whether to use desktop environment native dialogs. Use desktop environment &native dialogs 50 false Whether to hold auto-scrolling (follow play-head) on edits. &Hold auto-scrolling (follow play-head) on edits Qt::Horizontal 20 20 50 false Trac&k color saturation: Qt::AlignRight|Qt::AlignVCenter TrackColorSaturationSpinBox 50 false Default new track color saturation % 0 500 100 Qt::Horizontal 20 20 75 true Custom true 8 4 50 false &Color theme: Qt::AlignRight|Qt::AlignVCenter CustomColorThemeComboBox 50 false Custom color palette theme false (default) Wonton Soup KXStudio 22 22 24 24 50 false Qt::TabFocus Manage custom color palette themes ... 50 false Qt::AlignRight|Qt::AlignVCenter &Style theme: CustomStyleThemeComboBox 50 false Custom widget style theme false (default) 50 false Qt::AlignRight|Qt::AlignVCenter &Icons theme: CustomIconsThemeComboBox 50 false Custom icons theme directory false (default) 50 false 22 22 24 24 50 false Qt::TabFocus Browse for custom icons theme directory 50 false Qt::AlignRight|Qt::AlignVCenter St&yle sheet: CustomStyleSheetComboBox 50 false Custom style sheet (*.qss) false (default) 50 false 22 22 24 24 50 false Qt::TabFocus Browse for custom style sheet (*.qss) Qt::Vertical 20 4 75 true Meters true 50 false &Audio: AudioMeterLevelComboBox 66 0 50 false Audio meter level Over 0 dB 3 dB 6 dB 10 dB Back 66 24 50 false Audio meter color 22 22 24 24 50 false Qt::TabFocus Select custom audio meter color ... Qt::Horizontal 4 20 50 false &MIDI: Qt::AlignRight|Qt::AlignVCenter MidiMeterLevelComboBox 66 0 50 false MIDI meter level Peak Over Back 66 24 50 false MIDI meter color 22 22 24 24 50 false Qt::TabFocus Select custom MIDI meter color ... Qt::Horizontal 4 20 50 false Reset meter colors to default &Reset false Qt::Vertical 20 4 75 true Messages true 8 4 180 0 180 32767 50 false Sample messages text font display true QFrame::StyledPanel QFrame::Sunken Qt::AlignCenter 50 false Select font for the messages text display &Font... false Qt::Horizontal 20 20 50 false Whether to keep a maximum number of lines in the messages window M&essages limit: 50 false The maximum number of message lines to keep in view lines 100 10000 100 1000 75 true Logging true 8 4 1 0 320 0 50 false Messages log file true 22 22 24 24 50 false Qt::TabFocus Browse for the messages log file location 50 false Whether to activate a messages logging to file. Messages &log file: &Plugins 4 8 75 true Paths true 80 16777215 50 false Plugin type 320 0 50 false Plugin path true 22 22 24 24 50 false Browse plugin path ... 66 22 60 24 50 false Add plugin path &Add Qt::ToolButtonTextBesideIcon 32767 72 50 false Plugin paths QAbstractItemView::SelectRows true 66 22 60 24 50 false Remove plugin path &Remove Qt::ToolButtonTextBesideIcon 66 22 60 24 50 false Move up path &Up Qt::ToolButtonTextBesideIcon 66 22 60 24 50 false Move down path &Down Qt::ToolButtonTextBesideIcon 50 false &LV2 Presets directory: Lv2PresetDirComboBox 360 0 1 0 50 false LV2 Presets directory (default: ~/.lv2) true 22 22 24 24 50 false Browse LV2 Presets directory ... 75 true Instruments true 8 4 50 false Whether to have separate audio output ports Dedicated audi&o outputs: 50 false Whether to auto-connect dedicated audio output ports Au&to-connect Qt::Horizontal 20 20 75 true Editor true 8 4 50 false Whether to open plugin's editor (GUI) by default Open plugin's &editor (GUI) by default 50 false Whether to select plugin's editor (GUI) if more than one are available &Select plugin's editor (GUI) if more than one are available Qt::Vertical 20 4 75 true Blacklist true 320 0 50 false Plugin blacklist path true 22 22 24 24 50 false Browse plugin blacklist path ... 66 22 60 24 50 false Add plugin blacklist path &Add Qt::ToolButtonTextBesideIcon 32767 72 50 false Plugin blacklist paths QAbstractItemView::SelectRows true 66 22 60 24 50 false Remove plugin blacklist path &Remove Qt::ToolButtonTextBesideIcon Qt::Vertical 20 4 66 22 60 24 50 false Clear plugin blacklist paths &Clear Qt::ToolButtonTextBesideIcon Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok qtractorTimeSpinBox QSpinBox
qtractorSpinBox.h
qtractorAudioFileTypeComboBox QComboBox
qtractorComboBox.h
qtractorAudioFileFormatComboBox QComboBox
qtractorComboBox.h
qtractorMidiFileFormatComboBox QComboBox
qtractorComboBox.h
OptionsTabWidget SessionFormatComboBox SessionTemplateCheckBox SessionTemplatePathComboBox SessionTemplatePathToolButton SessionBackupCheckBox SessionBackupModeComboBox SessionAutoSaveCheckBox SessionAutoSaveSpinBox ConfirmRemoveCheckBox ConfirmArchiveCheckBox StdoutCaptureCheckBox CompletePathCheckBox PeakAutoRemoveCheckBox KeepToolsOnTopCheckBox MaxRecentFilesSpinBox TrackViewDropSpanCheckBox MidButtonModifierCheckBox KeepEditorsOnTopCheckBox TransportModeComboBox TimebaseCheckBox LoopRecordingModeComboBox AudioCaptureTypeComboBox AudioCaptureFormatComboBox AudioCaptureQualitySpinBox AudioAutoTimeStretchCheckBox AudioResampleTypeComboBox AudioWsolaTimeStretchCheckBox AudioWsolaQuickSeekCheckBox AudioRubberBandFormantCheckBox AudioRubberBandFinerR3CheckBox AudioPlayerBusCheckBox AudioPlayerAutoConnectCheckBox AudioSelfConnectedCheckBox AudioMetronomeCheckBox MetroBarFilenameComboBox MetroBarFilenameToolButton MetroBarGainSpinBox MetroBeatFilenameComboBox MetroBeatFilenameToolButton MetroBeatGainSpinBox AudioMetroBusCheckBox AudioMetroAutoConnectCheckBox AudioMetroOffsetSpinBox MidiCaptureFormatComboBox MidiCaptureQuantizeComboBox MidiQueueTimerComboBox MidiDriftCorrectCheckBox MidiPlayerBusCheckBox MidiResetAllControllersCheckBox MidiMmcModeComboBox MidiMmcDeviceComboBox MidiSppModeComboBox MidiClockModeComboBox MidiControlBusCheckBox MidiMetronomeCheckBox MetroChannelSpinBox MetroBarNoteComboBox MetroBarVelocitySpinBox MetroBarDurationSpinBox MetroBeatNoteComboBox MetroBeatVelocitySpinBox MetroBeatDurationSpinBox MidiMetroBusCheckBox MidiMetroOffsetSpinBox DisplayFormatComboBox BaseFontSizeComboBox UseNativeDialogsCheckBox SyncViewHoldCheckBox TrackColorSaturationSpinBox CustomColorThemeComboBox CustomColorThemeToolButton CustomStyleThemeComboBox CustomIconsThemeComboBox CustomIconsThemeToolButton CustomStyleSheetComboBox CustomStyleSheetToolButton AudioMeterLevelComboBox AudioMeterColorLineEdit AudioMeterColorToolButton MidiMeterLevelComboBox MidiMeterColorLineEdit MidiMeterColorToolButton ResetMeterColorsPushButton MessagesFontPushButton MessagesLimitCheckBox MessagesLimitLinesSpinBox MessagesLogCheckBox MessagesLogPathComboBox MessagesLogPathToolButton PluginTypeComboBox PluginPathComboBox PluginPathToolButton PluginPathAddToolButton PluginPathListWidget PluginPathRemoveToolButton PluginPathUpToolButton PluginPathDownToolButton Lv2PresetDirComboBox Lv2PresetDirToolButton AudioOutputBusCheckBox AudioOutputAutoConnectCheckBox OpenEditorCheckBox QueryEditorTypeCheckBox PluginBlacklistComboBox PluginBlacklistToolButton PluginBlacklistAddToolButton PluginBlacklistWidget PluginBlacklistRemoveToolButton PluginBlacklistClearToolButton DialogButtonBox
qtractor-1.5.9/src/PaxHeaders/qtractorConnectForm.ui0000644000000000000000000000013215101070305017556 xustar0030 mtime=1761898693.068267594 30 atime=1761898693.068267594 30 ctime=1761898693.068267594 qtractor-1.5.9/src/qtractorConnectForm.ui0000644000175000001440000003127615101070305017557 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. qtractorConnectForm 0 0 480 240 1 1 0 0 Connections 4 4 Audio 8 4 Qt::Horizontal 2 0 4 Select output client/ports Qt::WheelFocus 0 4 Select input client/ports Qt::WheelFocus 0 4 Connect currently selected ports &Connect Disconnect currently selected ports &Disconnect Disconnect all currently connected ports Disconnect &All Qt::Horizontal QSizePolicy::Expanding 87 8 Refresh current connections view &Refresh MIDI 8 4 Qt::Horizontal 2 0 4 Select output client/ports Qt::WheelFocus 0 4 Select input client/ports Qt::WheelFocus 0 4 Connect currently selected ports &Connect Disconnect currently selected ports &Disconnect Disconnect all currently connected ports Disconnect &All Qt::Horizontal QSizePolicy::Expanding 87 8 Refresh current connections view &Refresh qtractorAudioClientListView QTreeWidget
qtractorAudioConnect.h
qtractorMidiClientListView QTreeWidget
qtractorMidiConnect.h
qtractorConnectorView QWidget
qtractorConnect.h
ConnectTabWidget AudioOClientsComboBox AudioOListView AudioIClientsComboBox AudioIListView AudioConnectPushButton AudioDisconnectPushButton AudioDisconnectAllPushButton AudioRefreshPushButton MidiOClientsComboBox MidiOListView MidiIClientsComboBox MidiIListView MidiConnectPushButton MidiDisconnectPushButton MidiDisconnectAllPushButton MidiRefreshPushButton
qtractor-1.5.9/src/PaxHeaders/qtractorPluginForm.ui0000644000000000000000000000013215101070305017423 xustar0030 mtime=1761898693.087267654 30 atime=1761898693.087267654 30 ctime=1761898693.087267654 qtractor-1.5.9/src/qtractorPluginForm.ui0000644000175000001440000003041215101070305017413 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.9/src/PaxHeaders/qtractorAudioListView.cpp0000644000000000000000000000013215101070305020236 xustar0030 mtime=1761898693.063267578 30 atime=1761898693.063267578 30 ctime=1761898693.063267578 qtractor-1.5.9/src/qtractorAudioListView.cpp0000644000175000001440000001541015101070305020227 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.9/src/PaxHeaders/qtractorClip.cpp0000644000000000000000000000013215101070305016375 xustar0030 mtime=1761898693.066267588 30 atime=1761898693.066267588 30 ctime=1761898693.066267588 qtractor-1.5.9/src/qtractorClip.cpp0000644000175000001440000010030715101070305016366 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(InQuad); setFadeOutType(OutQuad); 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; } // end of qtractorClip.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMidiBuffer.h0000644000000000000000000000012715101070305017173 xustar0029 mtime=1761898693.07626762 29 atime=1761898693.07626762 29 ctime=1761898693.07626762 qtractor-1.5.9/src/qtractorMidiBuffer.h0000644000175000001440000001073415101070305017164 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.9/src/PaxHeaders/qtractorLv2Gtk2Plugin.h0000644000000000000000000000012715101070305017531 xustar0029 mtime=1761898693.07326761 29 atime=1761898693.07326761 29 ctime=1761898693.07326761 qtractor-1.5.9/src/qtractorLv2Gtk2Plugin.h0000644000175000001440000000257515101070305017526 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.9/src/PaxHeaders/qtractorAudioMeter.h0000644000000000000000000000013215101070305017211 xustar0030 mtime=1761898693.064267582 30 atime=1761898693.064267582 30 ctime=1761898693.064267582 qtractor-1.5.9/src/qtractorAudioMeter.h0000644000175000001440000001173515101070305017210 0ustar00rncbcusers// qtractorAudioMeter.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 __qtractorAudioMeter_h #define __qtractorAudioMeter_h #include "qtractorMeter.h" #include // Forward declarations. class qtractorAudioMeter; class qtractorAudioMonitor; class QResizeEvent; class QPaintEvent; //---------------------------------------------------------------------------- // qtractorAudioMeterScale -- Audio meter bridge scale widget. class qtractorAudioMeterScale : public qtractorMeterScale { public: // Constructor. qtractorAudioMeterScale(qtractorAudioMeter *pAudioMeter); protected: // Actual scale drawing method. void paintScale(QPainter *p); }; //---------------------------------------------------------------------------- // qtractorAudioMeterValue -- Audio meter bridge value widget. class qtractorAudioMeterValue : public qtractorMeterValue { public: // Constructor. qtractorAudioMeterValue( qtractorAudioMeter *pAudioMeter, unsigned short iChannel); // Value refreshment. void refresh(unsigned long iStamp); protected: // Specific event handlers. void paintEvent(QPaintEvent *); void resizeEvent(QResizeEvent *); private: // Local instance variables. unsigned short m_iChannel; // Running variables. int m_iValue; float m_fValueDecay; int m_iPeak; int m_iPeakHold; float m_fPeakDecay; int m_iPeakColor; }; //---------------------------------------------------------------------------- // qtractorAudioMeter -- Audio meter bridge slot widget. class qtractorAudioMeter : public qtractorMeter { public: // Constructor. qtractorAudioMeter( qtractorAudioMonitor *pAudioMonitor, QWidget *pParent = nullptr); // Default destructor. ~qtractorAudioMeter(); // Virtual monitor accessor. void setMonitor(qtractorMonitor *pMonitor); qtractorMonitor *monitor() const; // Audio monitor accessor. void setAudioMonitor(qtractorAudioMonitor *pAudioMonitor); qtractorAudioMonitor *audioMonitor() const; // Monitor reset. void reset(); // IEC scale accessors. int iec_scale(float dB) const; int iec_level(int iIndex) const; // Pixmap accessors. const QPixmap& pixmap() const; void updatePixmap(); // Color/level indexes. enum { ColorOver = 0, Color0dB = 1, Color3dB = 2, Color6dB = 3, Color10dB = 4, LevelCount = 5, ColorBack = 5, ColorFore = 6, ColorCount = 7 }; // Common resource accessors. static void setColor(int iIndex, const QColor& color); static const QColor& color(int iIndex); static const QColor& defaultColor(int iIndex); // 0dBfs factor (in percent). void setScale0dB(float fScale0dB) { m_fScale0dB = fScale0dB; } float scale0dB() const { return m_fScale0dB; } protected: // Specific event handlers. void resizeEvent(QResizeEvent *); private: // Local instance variables. qtractorAudioMonitor *m_pAudioMonitor; unsigned short m_iChannels; qtractorAudioMeterValue **m_ppAudioValues; unsigned int m_iRegenerate; int m_levels[LevelCount]; float m_fScale0dB; QPixmap m_pixmap; static QColor g_defaultColors[ColorCount]; static QColor g_currentColors[ColorCount]; }; //---------------------------------------------------------------------------- // qtractorAudioMixerMeter -- Audio mixer-strip meter bridge widget. class qtractorAudioMixerMeter : public qtractorMixerMeter { Q_OBJECT public: // Constructor. qtractorAudioMixerMeter( qtractorAudioMonitor *pAudioMonitor, QWidget *pParent = nullptr); // Default destructor. ~qtractorAudioMixerMeter(); // Virtual monitor accessor. void setMonitor(qtractorMonitor *pMonitor); qtractorMonitor *monitor() const; // Audio monitor accessor. void setAudioMonitor(qtractorAudioMonitor *pAudioMonitor); qtractorAudioMonitor *audioMonitor() const; // Local slider update methods. void updatePanning(); void updateGain(); // Monitor reset. void reset(); private: // Local forward declarations. class GainSliderInterface; class GainSpinBoxInterface; // Local instance variables. qtractorAudioMeter *m_pAudioMeter; qtractorAudioMeterScale *m_pAudioScale; }; #endif // __qtractorAudioMeter_h // end of qtractorAudioMeter.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiMeter.h0000644000000000000000000000013215101070305017032 xustar0030 mtime=1761898693.083267642 30 atime=1761898693.083267642 30 ctime=1761898693.083267642 qtractor-1.5.9/src/qtractorMidiMeter.h0000644000175000001440000001477715101070305017042 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.9/src/PaxHeaders/qtractorMidiControlPluginWidget.ui0000644000000000000000000000013215101070305022107 xustar0030 mtime=1761898693.078267626 30 atime=1761898693.078267626 30 ctime=1761898693.078267626 qtractor-1.5.9/src/qtractorMidiControlPluginWidget.ui0000644000175000001440000001120315101070305022074 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.9/src/PaxHeaders/qtractorMidiManager.h0000644000000000000000000000013215101070305017330 xustar0030 mtime=1761898693.083267642 30 atime=1761898693.083267642 30 ctime=1761898693.083267642 qtractor-1.5.9/src/qtractorMidiManager.h0000644000175000001440000002741615101070305017332 0ustar00rncbcusers// qtractorMidiManager.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 __qtractorMidiManager_h #define __qtractorMidiManager_h #include "qtractorAbout.h" #include "qtractorMidiBuffer.h" #ifdef CONFIG_VST2 #include "qtractorVst2Plugin.h" #ifndef CONFIG_MIDI_PARSER #define CONFIG_MIDI_PARSER 1 #endif #endif #ifdef CONFIG_VST3 #include "qtractorVst3Plugin.h" #ifndef CONFIG_MIDI_PARSER #define CONFIG_MIDI_PARSER 1 #endif #endif #ifdef CONFIG_CLAP #include "qtractorClapPlugin.h" #ifndef CONFIG_MIDI_PARSER #define CONFIG_MIDI_PARSER 1 #endif #endif #ifdef CONFIG_LV2 #if defined(CONFIG_LV2_EVENT) || defined(CONFIG_LV2_ATOM) #include "qtractorLv2Plugin.h" #ifndef CONFIG_MIDI_PARSER #define CONFIG_MIDI_PARSER 1 #endif #endif #endif #include "qtractorInstrument.h" // Forward declarations. class qtractorTimeScale; class qtractorPluginList; class qtractorPlugin; class qtractorAudioMonitor; class qtractorAudioOutputMonitor; class qtractorAudioBus; class qtractorMidiBus; class qtractorSubject; class qtractorMidiSyncThread; //---------------------------------------------------------------------- // class qtractorMidiSyncItem -- MIDI sync item decl. // class qtractorMidiSyncItem { public: // Constructor. qtractorMidiSyncItem(); // Destructor. virtual ~qtractorMidiSyncItem(); // Sync thread state flags accessors. void setWaitSync(bool bWaitSync); bool isWaitSync() const; // Process item (in asynchronous controller thread). virtual void processSync() = 0; // Post/schedule item for process sync. static void syncItem(qtractorMidiSyncItem *pSyncItem); private: // Instance mmembers. volatile bool m_bWaitSync; // Async manager thread (singleton) static qtractorMidiSyncThread *g_pSyncThread; static unsigned int g_iSyncThreadRefCount; }; //---------------------------------------------------------------------- // class qtractorMidiInputBuffer -- MIDI input buffer decl. // class qtractorMidiInputBuffer : public qtractorMidiBuffer { public: // Constructor. qtractorMidiInputBuffer( unsigned int iBufferSize = qtractorMidiBuffer::MinBufferSize) : qtractorMidiBuffer(iBufferSize), m_pDryGainSubject(nullptr), m_pWetGainSubject(nullptr) {} // Velocity/gain accessors. void setDryGainSubject(qtractorSubject *pDryGainSubject) { m_pDryGainSubject = pDryGainSubject; } qtractorSubject *dryGainSubject() const { return m_pDryGainSubject; } void setWetGainSubject(qtractorSubject *pWetGainSubject) { m_pWetGainSubject = pWetGainSubject; } qtractorSubject *wetGainSubject() const { return m_pWetGainSubject; } // Input event enqueuer. bool enqueue(snd_seq_event_t *pEv, unsigned long iTime = 0); private: // Instance mmembers. qtractorSubject *m_pDryGainSubject; qtractorSubject *m_pWetGainSubject; }; //---------------------------------------------------------------------- // class qtractorMidiOutputBuffer -- MIDI output buffer decl. // class qtractorMidiOutputBuffer : public qtractorMidiSyncItem { public: // Constructor. qtractorMidiOutputBuffer(qtractorMidiBus *pMidiBus, unsigned int iBufferSize = qtractorMidiBuffer::MinBufferSize) : qtractorMidiSyncItem(), m_pMidiBus(pMidiBus), m_outputBuffer(iBufferSize), m_pGainSubject(nullptr) {} // Velocity/gain accessors. void setGainSubject(qtractorSubject *pGainSubject) { m_pGainSubject = pGainSubject; } qtractorSubject *gaiSubject() const { return m_pGainSubject; } // Event enqueuer. bool enqueue(snd_seq_event_t *pEv, unsigned long iTime) { return m_outputBuffer.push(pEv, iTime); } // Buffer reset. void clear() { m_outputBuffer.clear(); } // Process buffer (in asynchronous thread). void processSync(); private: // Instance mmembers. qtractorMidiBus *m_pMidiBus; qtractorMidiBuffer m_outputBuffer; qtractorSubject *m_pGainSubject; }; //---------------------------------------------------------------------- // class qtractorMidiManager -- MIDI internal plugin list manager. // class qtractorMidiManager : public qtractorList::Link { public: // Constructor. qtractorMidiManager(qtractorPluginList *pPluginList, unsigned int iBufferSize = qtractorMidiBuffer::MinBufferSize); // Destructor. ~qtractorMidiManager(); // Implementation properties. qtractorPluginList *pluginList() const { return m_pPluginList; } unsigned int bufferSize() const { return m_queuedBuffer.bufferSize(); } // Clears buffers for processing. void clear(); // Event buffers accessors. qtractorMidiBuffer *buffer_in() const { return m_ppEventBuffers[m_iEventBuffer & 1]; } qtractorMidiBuffer *buffer_out() const { return m_ppEventBuffers[(m_iEventBuffer + 1) & 1]; } // Direct buffering. bool direct(snd_seq_event_t *pEvent); // Queued buffering. bool queued(snd_seq_event_t *pEvent, unsigned long iTime, unsigned long iTimeOff = 0); // Process buffers. void process(unsigned long iTimeStart, unsigned long iTimeEnd); // Process buffers (in asynchronous controller thread). void processSync(); // Resets all buffering. void reset(); // Sync thread state flags accessors. void setWaitSync(bool bWaitSync); bool isWaitSync() const; // Factory (proxy) methods. static qtractorMidiManager *createMidiManager( qtractorPluginList *pPluginList); static void deleteMidiManager( qtractorMidiManager *pMidiManager); // Some default factory options. static void setDefaultAudioOutputBus(bool bAudioOutputBus); static bool isDefaultAudioOutputBus(); static void setDefaultAudioOutputAutoConnect(bool bAudioOutputAutoConnect); static bool isDefaultAudioOutputAutoConnect(); #ifdef CONFIG_DSSI // DSSI event buffer accessors... snd_seq_event_t *dssi_events() const { return m_pDssiEvents; } unsigned int dssi_count() const { return m_iDssiEvents; } #endif #ifdef CONFIG_VST2 // VST2 event buffer accessors... VstEvents *vst2_events_in() const { return (VstEvents *) m_ppVst2Buffers[m_iEventBuffer & 1]; } VstEvents *vst2_events_out() const { return (VstEvents *) m_ppVst2Buffers[(m_iEventBuffer + 1) & 1]; } // Copy VST2 event buffer (output)... void vst2_events_copy(VstEvents *pVst2Buffer); // Swap VST2 event buffers... void vst2_events_swap(); #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT // LV2 event buffer accessors... LV2_Event_Buffer *lv2_events_in() const { return m_ppLv2EventBuffers[m_iEventBuffer & 1]; } LV2_Event_Buffer *lv2_events_out() const { return m_ppLv2EventBuffers[(m_iEventBuffer + 1) & 1]; } // Swap LV2 event buffers... void lv2_events_swap(); #endif #ifdef CONFIG_LV2_ATOM // LV2 atom buffer accessors... LV2_Atom_Buffer *lv2_atom_buffer_in() const { return m_ppLv2AtomBuffers[m_iEventBuffer & 1]; } LV2_Atom_Buffer *lv2_atom_buffer_out() const { return m_ppLv2AtomBuffers[(m_iEventBuffer + 1) & 1]; } // Swap LV2 atom buffers... void lv2_atom_buffer_swap(); // Resize LV2 atom buffers if necessary. void lv2_atom_buffer_resize(unsigned int iMinBufferSize); #endif #endif #ifdef CONFIG_MIDI_PARSER // Parse MIDI output and swap event buffers. // (esp. used by VST3 and CLAP) void swapOutputBuffers(); #endif // Audio output bus mode accessors. void setAudioOutputBus(bool bAudioOutputBus); bool isAudioOutputBus() const { return m_bAudioOutputBus; } void setAudioOutputBusName(const QString& sAudioOutputBusName) { m_sAudioOutputBusName = sAudioOutputBusName; } const QString& audioOutputBusName() const { return m_sAudioOutputBusName; } qtractorAudioBus *audioOutputBus() const { return m_pAudioOutputBus; } void resetAudioOutputBus(); // Audio output bus defaults accessors. void setAudioOutputAutoConnect(bool bAudioOutputAutoConnect) { m_bAudioOutputAutoConnect = bAudioOutputAutoConnect; } bool isAudioOutputAutoConnect() const { return m_bAudioOutputAutoConnect; } // Audio output bus monitor accessors. void setAudioOutputMonitor(bool bAudioOutputMonitor); void setAudioOutputMonitorEx(bool bAudioOutputMonitor); bool isAudioOutputMonitor() const { return m_bAudioOutputMonitor; } qtractorAudioOutputMonitor *audioOutputMonitor() const { return m_pAudioOutputMonitor; } // Current bank selection accessors. void setCurrentBank(int iBank) { m_iCurrentBank = iBank; } int currentBank() const { return m_iCurrentBank; } // Current program selection accessors. void setCurrentProg(int iProg) { m_iCurrentProg = iProg; } int currentProg() const { return m_iCurrentProg; } // Instrument map builder. void updateInstruments(); // Instrument map accessor. const qtractorInstrumentList& instruments() const { return m_instruments; } // Direct MIDI controller helper. void setController(unsigned short iChannel, int iController, int iValue); // Shut-off MIDI channel (panic)... void shutOff(unsigned short iChannel); // Process specific MIDI buffer (merge). void processInputBuffer( qtractorMidiInputBuffer *pMidiInputBuffer, unsigned long t0 = 0); // Reset event buffers (input/output only) void resetInputBuffers(); void resetOutputBuffers(); protected: // Audio output (de)activation methods. void createAudioOutputBus(); void deleteAudioOutputBus(); // Process/decode into other/plugin event buffers... void processEventBuffers(); // Swap event buffers (in for out and vice-versa) void swapEventBuffers(); private: // MIDI process sync item class. // class SyncItem : public qtractorMidiSyncItem { public: // Constructor. SyncItem(qtractorMidiManager *pMidiManager) : qtractorMidiSyncItem(), m_pMidiManager(pMidiManager) {} // Processor sync method. void processSync() { m_pMidiManager->processSync(); } // private: // Instance member. qtractorMidiManager *m_pMidiManager; }; SyncItem *m_pSyncItem; // Instance variables qtractorPluginList *m_pPluginList; qtractorMidiBuffer m_directBuffer; qtractorMidiBuffer m_queuedBuffer; qtractorMidiBuffer m_postedBuffer; qtractorMidiBuffer m_controllerBuffer; qtractorMidiBuffer *m_ppEventBuffers[2]; unsigned short m_iEventBuffer; #ifdef CONFIG_MIDI_PARSER snd_midi_event_t *m_pMidiParser; #endif #ifdef CONFIG_DSSI snd_seq_event_t *m_pDssiEvents; unsigned int m_iDssiEvents; #endif #ifdef CONFIG_VST2 VstMidiEvent *m_ppVst2MidiBuffers[2]; unsigned char *m_ppVst2Buffers[2]; #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_EVENT LV2_Event_Buffer *m_ppLv2EventBuffers[2]; #endif #ifdef CONFIG_LV2_ATOM LV2_Atom_Buffer *m_ppLv2AtomBuffers[2]; unsigned int m_iLv2AtomBufferSize; #endif #endif bool m_bAudioOutputBus; QString m_sAudioOutputBusName; qtractorAudioBus *m_pAudioOutputBus; bool m_bAudioOutputAutoConnect; bool m_bAudioOutputMonitor; qtractorAudioOutputMonitor *m_pAudioOutputMonitor; int m_iCurrentBank; int m_iCurrentProg; int m_iPendingBankMSB; int m_iPendingBankLSB; int m_iPendingProg; qtractorInstrumentList m_instruments; // Global factory options. static bool g_bAudioOutputBus; static bool g_bAudioOutputAutoConnect; static bool g_bAudioOutputMonitor; }; #endif // __qtractorMidiManager_h // end of qtractorMidiManager.h qtractor-1.5.9/src/PaxHeaders/qtractorPluginSelectForm.ui0000644000000000000000000000013215101070305020563 xustar0030 mtime=1761898693.087267654 30 atime=1761898693.087267654 30 ctime=1761898693.087267654 qtractor-1.5.9/src/qtractorPluginSelectForm.ui0000644000175000001440000001735215101070305020563 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.9/src/PaxHeaders/qtractorBusForm.ui0000644000000000000000000000013215101070305016716 xustar0030 mtime=1761898693.066267588 30 atime=1761898693.065267585 30 ctime=1761898693.066267588 qtractor-1.5.9/src/qtractorBusForm.ui0000644000175000001440000005617015101070305016717 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. qtractorBusForm 0 0 480 320 Qt::StrongFocus Buses Qt::Horizontal 4 120 120 Bus list false true true Buses Ch Mode 4 0 10 75 true Bus 2 6 0 Properties Bus 8 4 &Name: BusNameLineEdit Bus name &Mode: BusModeComboBox Qt::Horizontal 20 20 Bus mode 2 Input Output Duplex Bus monitor (pass-through) M&onitor (pass-through) Audio 8 4 Cha&nnels: AudioChannelsSpinBox Audio channels 1 2 Qt::Horizontal 20 20 Audio auto-connect &Auto connect MIDI MIDI Instrument name MIDI SysEx setup SysE&x... Qt::AlignCenter Qt::Horizontal 20 20 Input Plugins Qt::WheelFocus Input bus plugins 90 28 Add input plugin &Add... Qt::ToolButtonTextBesideIcon 90 28 Remove input plugin &Remove Qt::ToolButtonTextBesideIcon Qt::Vertical 8 8 90 28 Move input plugin up &Up Qt::ToolButtonTextBesideIcon 90 28 Move input plugin down &Down Qt::ToolButtonTextBesideIcon Output Plugins Qt::WheelFocus Output bus plugins 90 28 Add output plugin &Add... Qt::ToolButtonTextBesideIcon 90 28 Remove output plugin &Remove Qt::ToolButtonTextBesideIcon Qt::Vertical 8 8 90 28 Move output plugin up &Up Qt::ToolButtonTextBesideIcon 90 28 Move output plugin down &Down Qt::ToolButtonTextBesideIcon 4 0 Move bus up towards the top U&p Move bus down towards the bottom Do&wn Qt::Horizontal 20 20 Create bus &Create Update bus &Update Delete bus &Delete Close this dialog Close qtractorPluginListView QListWidget
qtractorPluginListView.h
BusListView BusTabWidget BusNameLineEdit BusModeComboBox MonitorCheckBox AudioChannelsSpinBox AudioAutoConnectCheckBox InputPluginListView AddInputPluginToolButton RemoveInputPluginToolButton MoveUpInputPluginToolButton MoveDownInputPluginToolButton OutputPluginListView AddOutputPluginToolButton RemoveOutputPluginToolButton MoveUpOutputPluginToolButton MoveDownOutputPluginToolButton MoveUpPushButton MoveDownPushButton CreatePushButton UpdatePushButton DeletePushButton ClosePushButton
qtractor-1.5.9/src/PaxHeaders/qtractorMidiClip.cpp0000644000000000000000000000013115101070305017177 xustar0030 mtime=1761898693.077267623 29 atime=1761898693.07626762 30 ctime=1761898693.077267623 qtractor-1.5.9/src/qtractorMidiClip.cpp0000644000175000001440000014111415101070305017172 0ustar00rncbcusers// qtractorMidiClip.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 "qtractorMidiClip.h" #include "qtractorMidiEngine.h" #include "qtractorSession.h" #include "qtractorFileList.h" #include "qtractorDocument.h" #include "qtractorMidiManager.h" #include "qtractorPlugin.h" #include "qtractorMidiEditor.h" #include "qtractorMidiEditorForm.h" #include "qtractorMidiEditCommand.h" #include "qtractorMainForm.h" #include "qtractorOptions.h" #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(4, 5, 0) namespace Qt { const WindowFlags WindowCloseButtonHint = WindowFlags(0x08000000); } #endif //---------------------------------------------------------------------- // class qtractorMidiClip::Key -- MIDI sequence clip (hash key). // class qtractorMidiClip::Key { public: // Constructor. Key(qtractorMidiClip *pMidiClip) { update(pMidiClip); } // Key settler. void update(qtractorMidiClip *pMidiClip) { qtractorTrack *pTrack = pMidiClip->track(); m_sFilename = pMidiClip->filename(); m_iClipOffset = pMidiClip->clipOffsetTime(); m_iClipLength = pMidiClip->clipLengthTime(); m_iTrackChannel = pMidiClip->trackChannel(); m_iMidiChannel = (pTrack ? pTrack->midiChannel() : 0); } // Key accessors. const QString& filename() const { return m_sFilename; } unsigned long clipOffset() const { return m_iClipOffset; } unsigned long clipLength() const { return m_iClipLength; } unsigned short trackChannel() const { return m_iTrackChannel; } unsigned short midiChannel() const { return m_iMidiChannel; } // Match descriminator. bool operator== (const Key& other) const { return m_sFilename == other.filename() && m_iClipOffset == other.clipOffset() && m_iClipLength == other.clipLength() && m_iTrackChannel == other.trackChannel() && m_iMidiChannel == other.midiChannel(); } private: // Interesting variables. QString m_sFilename; unsigned long m_iClipOffset; unsigned long m_iClipLength; unsigned short m_iTrackChannel; unsigned short m_iMidiChannel; }; uint qHash ( const qtractorMidiClip::Key& key ) { return qHash(key.filename()) ^ qHash(key.clipOffset()) ^ qHash(key.clipLength()) ^ qHash(key.trackChannel()) ^ qHash(key.midiChannel()); } qtractorMidiClip::Hash qtractorMidiClip::g_hashTable; //---------------------------------------------------------------------- // class qtractorMidiClip::FileKey -- MIDI file hash key. // class qtractorMidiClip::FileKey { public: // Constructor. FileKey(qtractorMidiClip::Key *pKey) : m_sFilename(pKey->filename()), m_iTrackChannel(pKey->trackChannel()) {} // Key accessors. const QString& filename() const { return m_sFilename; } unsigned short trackChannel() const { return m_iTrackChannel; } // Match descriminator. bool operator== (const FileKey& other) const { return m_sFilename == other.filename() && m_iTrackChannel == other.trackChannel(); } private: // Interesting variables. QString m_sFilename; unsigned short m_iTrackChannel; }; uint qHash ( const qtractorMidiClip::FileKey& key ) { return qHash(key.filename()) ^ qHash(key.trackChannel()); } qtractorMidiClip::FileHash qtractorMidiClip::g_hashFiles; //---------------------------------------------------------------------- // class qtractorMidiClip -- MIDI sequence clip. // // Constructor. qtractorMidiClip::qtractorMidiClip ( qtractorTrack *pTrack ) : qtractorClip(pTrack) { m_pFile = nullptr; m_pKey = nullptr; m_pData = nullptr; m_iTrackChannel = 0; m_bSessionFlag = false; m_iRevision = 0; m_pMidiEditorForm = nullptr; m_iEditorHorizontalZoom = 100; m_iEditorVerticalZoom = 100; m_iEditorDrumMode = -1; m_iBeatsPerBar2 = 0; m_iBeatDivisor2 = 0; m_iStepInputHead = 0; m_iStepInputTail = 0; m_iStepInputHeadTime = 0; m_iStepInputTailTime = 0; m_iStepInputLast = 0; clearInpEvents(); } // Copy constructor. qtractorMidiClip::qtractorMidiClip ( const qtractorMidiClip& clip ) : qtractorClip(clip.track()) { m_pFile = nullptr; m_pKey = nullptr; m_pData = nullptr; setFilename(clip.filename()); setClipName(clip.clipName()); setClipGain(clip.clipGain()); setClipPanning(clip.clipPanning()); setClipMute(clip.isClipMute()); setFadeInType(clip.fadeInType()); setFadeOutType(clip.fadeOutType()); setTrackChannel(clip.trackChannel()); m_bSessionFlag = false; m_iRevision = clip.revision(); m_pMidiEditorForm = nullptr; m_iEditorHorizontalZoom = clip.editorHorizontalZoom(); m_iEditorVerticalZoom = clip.editorVerticalZoom(); m_editorHorizontalSizes = clip.editorHorizontalSizes(); m_editorVerticalSizes = clip.editorVerticalSizes(); m_iEditorDrumMode = clip.editorDrumMode(); m_iBeatsPerBar2 = clip.beatsPerBar2(); m_iBeatDivisor2 = clip.beatDivisor2(); } // Destructor. qtractorMidiClip::~qtractorMidiClip (void) { // Sure close MIDI clip editor if any... if (m_pMidiEditorForm) { m_pMidiEditorForm->close(); delete m_pMidiEditorForm; m_pMidiEditorForm = nullptr; } closeMidiFile(); } // Brand new clip contents new method. bool qtractorMidiClip::createMidiFile ( const QString& sFilename, int iTrackChannel ) { closeMidiFile(); qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiClip[%p]::createMidiFile(\"%s\", %d)", this, sFilename.toUtf8().constData(), iTrackChannel); #endif // Self holds the SMF format, const unsigned short iFormat = format(); // Which SMF format? unsigned short iTracks = 1; if (iFormat == 0) { // SMF format 0 (1 track, 1 channel) iTrackChannel = pTrack->midiChannel(); } else { // SMF format 1 (2 tracks, 1 channel) iTrackChannel = 1; ++iTracks; } // Set local properties... setFilename(sFilename); setTrackChannel(iTrackChannel); setDirty(false); // Register file path... pSession->files()->addClipItemEx(qtractorFileList::Midi, this, true); // Create and open up the MIDI file... m_pFile = new qtractorMidiFile(); if (!m_pFile->open(sFilename, qtractorMidiFile::Write)) { delete m_pFile; m_pFile = nullptr; return false; } // Initialize MIDI event container... m_pKey = new Key(this); m_pData = new Data(m_pFile->format()); m_pData->attach(this); // Right on then... insertHashKey(); qtractorMidiSequence *pSeq = m_pData->sequence(); pSeq->clear(); pSeq->setTicksPerBeat(pSession->ticksPerBeat()); pSeq->setName(shortClipName(QFileInfo(sFilename).baseName())); pSeq->setChannel(pTrack->midiChannel()); // Make it a brand new revision... setRevision(1); // Write SMF header... if (m_pFile->writeHeader(iFormat, iTracks, pSeq->ticksPerBeat())) { // Set initial local properties... if (m_pFile->tempoMap()) { m_pFile->tempoMap()->fromTimeScale( pSession->timeScale(), pSession->tickFromFrame(clipStart())); } // Sure this is a brand new file... if (iFormat == 1) m_pFile->writeTrack(nullptr); m_pFile->writeTrack(pSeq); } m_pFile->close(); // It's there now. delete m_pFile; m_pFile = nullptr; // Clip name should be clear about it all. if (clipName().isEmpty()) setClipName(pSeq->name()); if (clipName().isEmpty()) setClipName(shortClipName(QFileInfo(filename()).baseName())); // Uh oh... m_playCursor.reset(pSeq); m_drawCursor.reset(pSeq); return true; } // The main use method. bool qtractorMidiClip::openMidiFile ( const QString& sFilename, int iTrackChannel, int iMode ) { closeMidiFile(); qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiClip[%p]::openMidiFile(\"%s\", %d, %d)", this, sFilename.toUtf8().constData(), iTrackChannel, iMode); #endif // Check file primordial state... const bool bWrite = (iMode & qtractorMidiFile::Write); // Set local properties... setFilename(sFilename); setDirty(false); // Register file path... pSession->files()->addClipItemEx(qtractorFileList::Midi, this, bWrite); // New key-data sequence... if (!bWrite) { m_pKey = new Key(this); m_pData = g_hashTable.value(*m_pKey, nullptr); if (m_pData) { m_pData->attach(this); qtractorMidiSequence *pSeq = m_pData->sequence(); // Initial statistics... pTrack->setMidiNoteMin(pSeq->noteMin()); pTrack->setMidiNoteMax(pSeq->noteMax()); // Clip name should be clear about it all. if (clipName().isEmpty()) setClipName(pSeq->name()); if (clipName().isEmpty()) setClipName(shortClipName(QFileInfo(filename()).baseName())); // Uh oh... m_playCursor.reset(pSeq); m_drawCursor.reset(pSeq); return true; } // HACK: Create as new MIDI file if not exists?... if (!QFile::exists(sFilename)) { if (!createMidiFile(sFilename, iTrackChannel)) return false; setClipOffset(0); setDirty(true); qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->addMidiFile(sFilename); } } // Create and open up the real MIDI file... m_pFile = new qtractorMidiFile(); if (!m_pFile->open(sFilename, iMode)) { delete m_pFile; m_pFile = nullptr; return false; } // Initialize MIDI event container... m_pData = new Data(m_pFile->format()); m_pData->attach(this); qtractorMidiSequence *pSeq = m_pData->sequence(); pSeq->clear(); pSeq->setTicksPerBeat(pSession->ticksPerBeat()); pSeq->setTimeOffset(clipOffsetTime()); pSeq->setTimeLength(clipLengthTime()); // Initial statistics... pSeq->setNoteMin(pTrack->midiNoteMin()); pSeq->setNoteMax(pTrack->midiNoteMax()); // Are we on a pre-writing status? if (bWrite) { // On write mode, iTrackChannel holds the SMF format, // so we'll convert it here as properly. const unsigned short iFormat = qtractorMidiClip::defaultFormat(); unsigned short iTracks = 1; if (iFormat == 1) { // SMF format 1 (2 tracks, 1 channel) iTrackChannel = 1; ++iTracks; } // Write SMF header... if (m_pFile->writeHeader(iFormat, iTracks, pSeq->ticksPerBeat())) { // Set initial local properties... if (m_pFile->tempoMap()) { m_pFile->tempoMap()->fromTimeScale( pSession->timeScale(), pSeq->timeOffset()); } } // And initial clip name... pSeq->setName(shortClipName(QFileInfo(m_pFile->filename()).baseName())); pSeq->setChannel(pTrack->midiChannel()); // Nothing more as for writing... } else { // Read the event sequence in... m_pFile->readTrack(pSeq, iTrackChannel); // For immediate feedback, once... pTrack->setMidiNoteMin(pSeq->noteMin()); pTrack->setMidiNoteMax(pSeq->noteMax()); // FIXME: On demand, set session time properties from MIDI file... if (m_bSessionFlag) { #if 0 // Import eventual SysEx setup... // - take care that given track might not be currently open, // so that we'll resolve MIDI output bus somehow... qtractorMidiBus *pMidiBus = nullptr; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine) { pMidiBus = static_cast ( pMidiEngine->findOutputBus(pTrack->outputBusName())); if (pMidiBus == nullptr) { for (qtractorBus *pBus = pMidiEngine->buses().first(); pBus; pBus = pBus->next()) { if (pBus->busMode() & qtractorBus::Output) { pMidiBus = static_cast (pBus); break; } } } } // Import eventual SysEx setup... if (pMidiBus) pMidiBus->importSysexList(pSeq); #endif // Import tempo map as well... qtractorMidiFileTempo *pTempoMap = m_pFile->tempoMap(); if (pTempoMap) { pTempoMap->intoTimeScale(pSession->timeScale(), clipStartTime()); pSession->updateTimeScaleEx(); } // Reset session flag now. m_bSessionFlag = false; } // We should have events, otherwise this clip is of no use... //if (m_pSeq->events().count() < 1) // return false; // And initial clip name, // if not already set from SMF TRACKNAME meta-event... if (pSeq->name().isEmpty()) { pSeq->setName(shortClipName( QFileInfo(m_pFile->filename()).baseName())); } } // Actual track-channel is set by now... setTrackChannel(iTrackChannel); // Make it a brand new revision... // setRevision(1); // Default clip length will be whole sequence duration. if (clipLength() == 0) { const unsigned long t1 = clipStartTime() + pSeq->timeLength(); setClipLength(pSession->frameFromTick(t1) - clipStart()); } // Clip name should be clear about it all. if (clipName().isEmpty()) setClipName(pSeq->name()); if (clipName().isEmpty()) setClipName(shortClipName(QFileInfo(filename()).baseName())); // Uh oh... m_playCursor.reset(pSeq); m_drawCursor.reset(pSeq); // Something might have changed... updateHashKey(); insertHashKey(); // Update/reset MIDI clip editor if any... if (m_pMidiEditorForm) m_pMidiEditorForm->setup(this); return true; } // Private cleanup. void qtractorMidiClip::closeMidiFile (void) { clearInpEvents(); if (m_pData) { m_pData->detach(this); if (m_pData->count() < 1) { removeHashKey(); delete m_pData; } m_pData = nullptr; } if (m_pKey) { delete m_pKey; m_pKey = nullptr; } if (m_pFile) { delete m_pFile; m_pFile = nullptr; } } // Revisionist method. QString qtractorMidiClip::createFilePathRevision ( bool bForce ) { QString sFilename = filename(); qtractorTrack *pTrack = track(); if (pTrack == nullptr) return sFilename; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return sFilename; // Check file-hash reference... if (m_iRevision > 0 && m_pKey) { FileKey fkey(m_pKey); FileHash::ConstIterator fiter = g_hashFiles.constFind(fkey); if (fiter != g_hashFiles.constEnd() && fiter.value() > 1) m_iRevision = 0; } if (m_iRevision == 0 || bForce) { sFilename = pSession->createFilePath(pTrack->shortTrackName(), "mid"); sFilename = qtractorMidiFile::createFilePathRevision(sFilename); #ifdef CONFIG_DEBUG qDebug("qtractorMidiClip::createFilePathRevision(%d): \"%s\" (%d)", int(bForce), sFilename.toUtf8().constData(), m_iRevision); #endif m_iRevision = 0; } ++m_iRevision; return sFilename; } // Sync all ref-counted filenames. void qtractorMidiClip::setFilenameEx ( const QString& sFilename, bool bUpdate ) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return; if (m_pData == nullptr) return; removeHashKey(); QListIterator iter(m_pData->clips()); while (iter.hasNext()) { qtractorMidiClip *pMidiClip = iter.next(); pSession->files()->removeClipItem(qtractorFileList::Midi, pMidiClip); pMidiClip->setFilename(sFilename); pMidiClip->updateHashKey(); pSession->files()->addClipItem(qtractorFileList::Midi, pMidiClip, true); if (bUpdate) { pMidiClip->setDirty(false); pMidiClip->updateEditor(true); } } insertHashKey(); } // Sync all ref-counted clip-lengths. void qtractorMidiClip::setClipLengthEx ( unsigned long iClipLength ) { if (m_pData == nullptr) return; removeHashKey(); QListIterator iter(m_pData->clips()); while (iter.hasNext()) { qtractorMidiClip *pMidiClip = iter.next(); pMidiClip->setClipLength(iClipLength); pMidiClip->updateHashKey(); } insertHashKey(); } // Sync all ref-counted clip editors. void qtractorMidiClip::updateEditorEx ( bool bSelectClear ) { if (m_pData == nullptr) return; QListIterator iter(m_pData->clips()); while (iter.hasNext()) iter.next()->updateEditor(bSelectClear); } // Sync all ref-counted clip-dirtyness. void qtractorMidiClip::setDirtyEx ( bool bDirty ) { if (m_pData == nullptr) return; QListIterator iter(m_pData->clips()); while (iter.hasNext()) iter.next()->setDirty(bDirty); } // Manage local hash key. void qtractorMidiClip::insertHashKey (void) { if (m_pKey) { // Increment file-hash reference... FileKey fkey(m_pKey); FileHash::Iterator fiter = g_hashFiles.find(fkey); if (fiter == g_hashFiles.end()) fiter = g_hashFiles.insert(fkey, 0); ++fiter.value(); // Insert actual clip-hash reference.... g_hashTable.insert(*m_pKey, m_pData); } } void qtractorMidiClip::updateHashKey (void) { if (m_pKey == nullptr) m_pKey = new Key(this); else m_pKey->update(this); } void qtractorMidiClip::removeHashKey (void) { if (m_pKey) { // Decrement file-hash reference... FileKey fkey(m_pKey); FileHash::Iterator fiter = g_hashFiles.find(fkey); if (fiter != g_hashFiles.end()) { if (--fiter.value() < 1) g_hashFiles.remove(fkey); } // Remove actual clip-hash reference.... g_hashTable.remove(*m_pKey); } } // Unlink (clone) local hash data. void qtractorMidiClip::unlinkHashData (void) { if (m_pData == nullptr) return; if (m_pData->count() < 2) return; m_pData->detach(this); Data *pNewData = new Data(m_pData->format()); qtractorMidiSequence *pOldSeq = m_pData->sequence(); qtractorMidiSequence *pNewSeq = pNewData->sequence(); pNewSeq->setName(pOldSeq->name()); pNewSeq->setChannel(pOldSeq->channel()); pNewSeq->setBankSelMethod(pOldSeq->bankSelMethod()); pNewSeq->setBank(pOldSeq->bank()); pNewSeq->setProg(pOldSeq->prog()); pNewSeq->setTicksPerBeat(pOldSeq->ticksPerBeat()); pNewSeq->setTimeOffset(pOldSeq->timeOffset()); pNewSeq->setTimeLength(pOldSeq->timeLength()); pNewSeq->setDuration(pOldSeq->duration()); pNewSeq->setNoteMin(pOldSeq->noteMin()); pNewSeq->setNoteMax(pOldSeq->noteMax()); pNewSeq->copyEvents(pOldSeq); m_pData = pNewData; m_pData->attach(this); updateHashKey(); insertHashKey(); } // Relink local hash data. void qtractorMidiClip::relinkHashData (void) { if (m_pData == nullptr) return; if (m_pData->count() > 1) return; removeHashKey(); updateHashKey(); Data *pNewData = g_hashTable.value(*m_pKey, nullptr); if (pNewData == nullptr) { delete m_pKey; m_pKey = nullptr; } else { m_pData->detach(this); delete m_pData; m_pData = pNewData; m_pData->attach(this); } insertHashKey(); } // Whether local hash is being shared. bool qtractorMidiClip::isHashLinked (void) const { return (m_pData && m_pData->count() > 1); } // Check whether a MIDI clip is hash-linked to another. bool qtractorMidiClip::isLinkedClip ( qtractorMidiClip *pMidiClip ) const { return (m_pData ? m_pData->clips().contains(pMidiClip) : false); } // Get all hash-linked clips (including self). QList qtractorMidiClip::linkedClips (void) const { QList clips; if (m_pData) clips = m_pData->clips(); return clips; } // Make sure the clip hash-table gets reset. void qtractorMidiClip::clearHashTable (void) { g_hashTable.clear(); g_hashFiles.clear(); } // Intra-clip playback frame positioning. void qtractorMidiClip::seek ( unsigned long iFrame ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiClip[%p]::seek(%lu)", this, iFrame); #endif qtractorTrack *pTrack = track(); if (pTrack == nullptr) return; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return; qtractorMidiSequence *pSeq = sequence(); if (pSeq == nullptr) return; const unsigned long t0 = pSession->tickFromFrame(clipStart()); const unsigned long t1 = pSession->tickFromFrame(iFrame); // Seek for the nearest sequence event... m_playCursor.seek(pSeq, (t1 > t0 ? t1 - t0 : 0)); } // Reset clip state. void qtractorMidiClip::reset ( bool /* bLooping */ ) { qtractorMidiSequence *pSeq = sequence(); if (pSeq == nullptr) return; // Reset to the first sequence event... m_playCursor.reset(pSeq); } // Loop positioning. void qtractorMidiClip::setLoop ( unsigned long /* iLoopStart */, unsigned long /* iLoopEnd */ ) { // Do nothing? } // Clip close-commit (record specific) void qtractorMidiClip::close (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiClip[%p]::close(%d)\n", this); #endif qtractorTrack *pTrack = track(); if (pTrack == nullptr) return; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return; // Take pretended clip-length... unsigned long iTimeLength = 0; qtractorMidiSequence *pSeq = sequence(); if (pSeq) { if (pSeq->duration() > 0) pSeq->setTimeLength(clipLengthTime()); // Final read/write statistics... pTrack->setMidiNoteMin(pSeq->noteMin()); pTrack->setMidiNoteMax(pSeq->noteMax()); // Actual sequence closure... pSeq->close(); // Commit the final length... iTimeLength = pSeq->timeLength(); } // Now's time to write the whole thing... const bool bNewFile = (m_pFile && m_pFile->mode() == qtractorMidiFile::Write); if (bNewFile && pSeq && pSeq->duration() > 0) { // Write channel tracks... if (m_pFile->format() == 1) m_pFile->writeTrack(nullptr); // Setup track (SMF format 1). m_pFile->writeTrack(pSeq); // Channel track. } if (m_pFile) m_pFile->close(); // Sure close MIDI clip editor if any... if (m_pMidiEditorForm) { m_pMidiEditorForm->close(); delete m_pMidiEditorForm; m_pMidiEditorForm = nullptr; } // Just to be sure things get deallocated.. closeMidiFile(); // If proven empty, remove the file. if (bNewFile && iTimeLength < 1) { QFile::remove(filename()); setClipLength(0); // Nothing's recorded! } } // MIDI clip (re)open method. void qtractorMidiClip::open (void) { const QString sFilename(filename()); openMidiFile(sFilename, m_iTrackChannel); } // Audio clip special process cycle executive. void qtractorMidiClip::process ( unsigned long iFrameStart, unsigned long iFrameEnd ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiClip[%p]::process(%lu, %lu)\n", this, iFrameStart, iFrameEnd); #endif qtractorTrack *pTrack = track(); if (pTrack == nullptr) return; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; qtractorMidiSequence *pSeq = sequence(); if (pSeq == nullptr) return; // Track mute state... const bool bMute = (pTrack->isMute() || (pSession->soloTracks() && !pTrack->isSolo())); const unsigned long iClipStart = clipStart(); const unsigned long t0 = pSession->tickFromFrame(iClipStart); const unsigned long iTimeStart = pSession->tickFromFrame(iFrameStart); const unsigned long iTimeEnd = pSession->tickFromFrame(iFrameEnd); // Enqueue the requested events... const float fGain = clipGain(); qtractorMidiEvent *pEvent = m_playCursor.seek(pSeq, iTimeStart > t0 ? iTimeStart - t0 : 0); while (pEvent) { const unsigned long t1 = t0 + pEvent->time(); if (t1 >= iTimeEnd) break; if (t1 >= iTimeStart && (!bMute || pEvent->type() != qtractorMidiEvent::NOTEON)) pMidiEngine->enqueue(pTrack, pEvent, t1, fGain * fadeInOutGain(pSession->frameFromTick(t1) - iClipStart)); pEvent = pEvent->next(); } } // MIDI clip freewheeling process cycle executive (needed for export). void qtractorMidiClip::process_export ( unsigned long iFrameStart, unsigned long iFrameEnd ) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return; qtractorMidiSequence *pSeq = sequence(); if (pSeq == nullptr) return; // Track mute state... const bool bMute = (pTrack->isMute() || (pSession->soloTracks() && !pTrack->isSolo())); const unsigned long t0 = pSession->tickFromFrame(clipStart()); const unsigned long iTimeStart = pSession->tickFromFrame(iFrameStart); const unsigned long iTimeEnd = pSession->tickFromFrame(iFrameEnd); // Enqueue the requested events... const float fGain = clipGain(); qtractorMidiEvent *pEvent = m_playCursor.seek(pSeq, iTimeStart > t0 ? iTimeStart - t0 : 0); while (pEvent) { const unsigned long t1 = t0 + pEvent->time(); if (t1 >= iTimeEnd) break; if (t1 >= iTimeStart && (!bMute || pEvent->type() != qtractorMidiEvent::NOTEON)) { enqueue_export(pTrack, pEvent, t1, fGain * fadeInOutGain(pSession->frameFromTick(t1) - clipStart())); } pEvent = pEvent->next(); } } // MIDI clip paint method. void qtractorMidiClip::draw ( QPainter *pPainter, const QRect& clipRect, unsigned long iClipOffset ) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return; qtractorMidiSequence *pSeq = sequence(); if (pSeq == nullptr) return; // Check min/maximum note span... const int iNoteMin = pTrack->midiNoteMin() - 2; const int iNoteMax = pTrack->midiNoteMax() + 1; int iNoteSpan = iNoteMax - iNoteMin; if (iNoteSpan < 6) iNoteSpan = 6; const unsigned long iClipStart = clipStart(); const unsigned long iFrameStart = iClipStart + iClipOffset; const int cx = pSession->pixelFromFrame(iFrameStart); qtractorTimeScale::Cursor cursor(pSession->timeScale()); qtractorTimeScale::Node *pNode = cursor.seekFrame(iClipStart); const unsigned long t0 = pNode->tickFromFrame(iClipStart); const int cw = clipRect.width(); pNode = cursor.seekFrame(iFrameStart); const unsigned long iTimeStart = pNode->tickFromFrame(iFrameStart); pNode = cursor.seekPixel(cx + cw); const unsigned long iTimeEnd = pNode->tickFromPixel(cx + cw); const QColor& fg = pTrack->foreground(); pPainter->setPen(fg); pPainter->setBrush(fg.lighter(120)); const bool bClipRecord = (pTrack->clipRecord() == this); const int h1 = clipRect.height() - 2; const int h2 = (h1 / iNoteSpan) + 1; const bool bDrumMode = pTrack->isMidiDrums(); QVector diamond; if (bDrumMode) { const int h4 = (h2 >> 1) + 1; diamond.append(QPoint( 0, -1)); diamond.append(QPoint(-h4, h4)); diamond.append(QPoint( 0, h2 + 2)); diamond.append(QPoint( h4, h4)); pPainter->setRenderHint(QPainter::Antialiasing, true); } qtractorMidiEvent *pEvent = m_drawCursor.reset(pSeq, iTimeStart > t0 ? iTimeStart - t0 : 0); while (pEvent) { unsigned long t1 = t0 + pEvent->time(); if (t1 >= iTimeEnd) break; if (pEvent->type() == qtractorMidiEvent::NOTEON) { unsigned long t2 = t1 + pEvent->duration(); if (t2 > iTimeEnd || (t1 >= t2 && bClipRecord)) t2 = iTimeEnd; if (t1 < iTimeStart) t1 = iTimeStart; if (t2 > iTimeStart) { pNode = cursor.seekTick(t1); const int x = clipRect.x() + pNode->pixelFromTick(t1) - cx; const int y = clipRect.bottom() - (h1 * (pEvent->note() - iNoteMin)) / iNoteSpan; pNode = cursor.seekTick(t2); if (bDrumMode) { const QPolygon& polyg = QPolygon(diamond).translated(x, y); if (h2 > 3) pPainter->drawPolygon(polyg.translated(1, 0)); // shadow pPainter->drawPolygon(polyg); // diamond } else { int w = (t1 < t2 || !bClipRecord ? clipRect.x() + pNode->pixelFromTick(t2) - cx : clipRect.right()) - x; // Pending note-off? (while recording) if (w < 3) w = 3; pPainter->fillRect(x, y, w, h2, fg); if (w > 4 && h2 > 3) pPainter->fillRect(x + 1, y + 1, w - 4, h2 - 3, fg.lighter(140)); } } } pEvent = pEvent->next(); } if (bDrumMode) pPainter->setRenderHint(QPainter::Antialiasing, false); } // Clip update method. (rolling stats) void qtractorMidiClip::update (void) { qtractorTrack *pTrack = track(); if (pTrack) { qtractorMidiSequence *pSeq = sequence(); if (pSeq) { pTrack->setMidiNoteMax(pSeq->noteMax()); pTrack->setMidiNoteMin(pSeq->noteMin()); } } } // Clip editor method. bool qtractorMidiClip::startEditor ( QWidget *pParent ) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; if (m_pMidiEditorForm == nullptr) { // Build up the editor form... // What style do we create tool childs? Qt::WindowFlags wflags = Qt::Window; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bKeepEditorsOnTop) { wflags |= Qt::Tool; wflags |= Qt::WindowStaysOnTopHint; #if 0//QTRACTOR_MIDI_EDITOR_TOOL_PARENT // Make sure it has a parent... if (pParent == nullptr) pParent = qtractorMainForm::getInstance(); #else // Make sure it's a top-level window... pParent = nullptr; #endif } // Do it... m_pMidiEditorForm = new qtractorMidiEditorForm(pParent, wflags); m_pMidiEditorForm->show(); m_pMidiEditorForm->setup(this); } else { // Just show up the editor form... m_pMidiEditorForm->setup(); m_pMidiEditorForm->show(); } // Get it up any way... m_pMidiEditorForm->raise(); m_pMidiEditorForm->activateWindow(); return true; } // Clip editor update. void qtractorMidiClip::updateEditor ( bool bSelectClear ) { update(); if (m_pMidiEditorForm == nullptr) return; qtractorMidiEditor *pMidiEditor = m_pMidiEditorForm->editor(); if (pMidiEditor) { pMidiEditor->reset(bSelectClear); pMidiEditor->setOffset(clipStart()); pMidiEditor->setLength(clipLength()); qtractorTrack *pTrack = track(); if (pTrack) { pMidiEditor->setForeground(pTrack->foreground()); pMidiEditor->setBackground(pTrack->background()); const int iEditorDrumMode = editorDrumMode(); if (iEditorDrumMode < 0) pMidiEditor->setDrumMode(pTrack->isMidiDrums()); else pMidiEditor->setDrumMode(iEditorDrumMode > 0); } pMidiEditor->updateContents(); } m_pMidiEditorForm->resetDirtyCount(); m_pMidiEditorForm->updateInstrumentNames(); m_pMidiEditorForm->stabilizeForm(); } // Clip editor update. void qtractorMidiClip::updateEditorContents (void) { if (m_pMidiEditorForm == nullptr) return; qtractorMidiEditor *pMidiEditor = m_pMidiEditorForm->editor(); if (pMidiEditor) pMidiEditor->updateContents(); m_pMidiEditorForm->stabilizeForm(); } void qtractorMidiClip::updateEditorTimeScale (void) { if (m_pMidiEditorForm == nullptr) return; qtractorTrack *pTrack = track(); if (pTrack) { qtractorSession *pSession = pTrack->session(); if (pSession) pSession->updateTimeScale(); } qtractorMidiEditor *pMidiEditor = m_pMidiEditorForm->editor(); if (pMidiEditor) pMidiEditor->updateTimeScale(); } // Clip query-close method (return true if editing is done). bool qtractorMidiClip::queryEditor (void) { if (m_pMidiEditorForm) return m_pMidiEditorForm->queryClose(); // Are any dirty changes pending commit? bool bQueryEditor = qtractorClip::queryEditor(); if (!bQueryEditor) { switch (qtractorMidiEditorForm::querySave(filename())) { case QMessageBox::Save: { // Save/replace the clip track... bQueryEditor = saveCopyFile(createFilePathRevision(), true); break; } case QMessageBox::Discard: bQueryEditor = true; break; case QMessageBox::Cancel: bQueryEditor = false; break; } } return bQueryEditor; } // MIDI clip tool-tip. QString qtractorMidiClip::toolTip (void) const { QString sToolTip = qtractorClip::toolTip() + ' '; const unsigned short iFormat = format(); sToolTip += QObject::tr("(format %1)\nMIDI:\t").arg(iFormat); if (iFormat == 0) sToolTip += QObject::tr("Channel %1").arg(m_iTrackChannel + 1); else sToolTip += QObject::tr("Track %1").arg(m_iTrackChannel); if (m_pFile) { sToolTip += QObject::tr(", %1 tracks, %2 tpqn") .arg(m_pFile->tracks()) .arg(m_pFile->ticksPerBeat()); } const float fVolume = clipGain(); if (fVolume < 0.999f || fVolume > 1.001f) { sToolTip += QObject::tr(" (%1% vol)") .arg(100.0f * fVolume, 0, 'g', 3); } return sToolTip; } // Auto-save to (possible) new file revision. bool qtractorMidiClip::saveCopyFile ( const QString& sFilename, bool bUpdate ) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; // Do nothing if session is yet untitled... if (pSession->sessionName().isEmpty()) return false; // Save/replace the clip track... if (!qtractorMidiFile::saveCopyFile(sFilename, filename(), trackChannel(), format(), sequence(), pSession->timeScale(), clipStartTime())) return false; // Pre-commit dirty changes... if (bUpdate) setFilenameEx(sFilename, true); // Reference for immediate file addition... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { pMainForm->appendMessages( QObject::tr("MIDI file save: \"%1\", track-channel: %2.") .arg(sFilename).arg(trackChannel())); pMainForm->addMidiFile(sFilename); } return true; } // Virtual document element methods. bool qtractorMidiClip::loadClipElement ( qtractorDocument * /* pDocument */, QDomElement *pElement ) { // 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 track state.. if (eChild.tagName() == "filename") qtractorMidiClip::setFilename(eChild.text()); else if (eChild.tagName() == "track-channel") qtractorMidiClip::setTrackChannel(eChild.text().toUShort()); else if (eChild.tagName() == "revision") qtractorMidiClip::setRevision(eChild.text().toUShort()); else if (eChild.tagName() == "editor-pos") { const QStringList& sxy = eChild.text().split(','); m_posEditor.setX(sxy.at(0).toInt()); m_posEditor.setY(sxy.at(1).toInt()); } else if (eChild.tagName() == "editor-size") { const QStringList& swh = eChild.text().split(','); m_sizeEditor.setWidth(swh.at(0).toInt()); m_sizeEditor.setHeight(swh.at(1).toInt()); } else if (eChild.tagName() == "editor-horizontal-zoom") { m_iEditorHorizontalZoom = eChild.text().toUShort(); } else if (eChild.tagName() == "editor-vertical-zoom") { m_iEditorVerticalZoom = eChild.text().toUShort(); } else if (eChild.tagName() == "editor-horizontal-sizes") { const QStringList& hsizes = eChild.text().split(','); m_editorHorizontalSizes.clear(); m_editorHorizontalSizes.append(hsizes.at(0).toInt()); m_editorHorizontalSizes.append(hsizes.at(1).toInt()); } else if (eChild.tagName() == "editor-vertical-sizes") { const QStringList& vsizes = eChild.text().split(','); m_editorVerticalSizes.clear(); m_editorVerticalSizes.append(vsizes.at(0).toInt()); m_editorVerticalSizes.append(vsizes.at(1).toInt()); } else if (eChild.tagName() == "editor-drum-mode") { const bool bEditorDrumMode = qtractorDocument::boolFromText(eChild.text()); m_iEditorDrumMode = (bEditorDrumMode ? 1 : 0); } else if (eChild.tagName() == "ghost-track-name") { m_sGhostTrackName = eChild.text(); } else if (eChild.tagName() == "beats-per-bar-2") { m_iBeatsPerBar2 = eChild.text().toUInt(); } else if (eChild.tagName() == "beat-divisor-2") { m_iBeatDivisor2 = eChild.text().toUInt(); } } return true; } bool qtractorMidiClip::saveClipElement ( qtractorDocument *pDocument, QDomElement *pElement ) { QDomElement eMidiClip = pDocument->document()->createElement("midi-clip"); pDocument->saveTextElement("filename", qtractorMidiClip::relativeFilename(pDocument), &eMidiClip); pDocument->saveTextElement("track-channel", QString::number(qtractorMidiClip::trackChannel()), &eMidiClip); pDocument->saveTextElement("revision", QString::number(qtractorMidiClip::revision()), &eMidiClip); if (m_posEditor.x() >= 0 && m_posEditor.y() >= 0) { pDocument->saveTextElement("editor-pos", QString::number(m_posEditor.x()) + ',' + QString::number(m_posEditor.y()), &eMidiClip); } if (!m_sizeEditor.isNull() && m_sizeEditor.isValid()) { pDocument->saveTextElement("editor-size", QString::number(m_sizeEditor.width()) + ',' + QString::number(m_sizeEditor.height()), &eMidiClip); } if (m_iEditorHorizontalZoom != 100) { pDocument->saveTextElement("editor-horizontal-zoom", QString::number(m_iEditorHorizontalZoom), &eMidiClip); } if (m_iEditorVerticalZoom != 100) { pDocument->saveTextElement("editor-vertical-zoom", QString::number(m_iEditorVerticalZoom), &eMidiClip); } if (m_editorHorizontalSizes.count() >= 2) { pDocument->saveTextElement("editor-horizontal-sizes", QString::number(m_editorHorizontalSizes.at(0)) + ',' + QString::number(m_editorHorizontalSizes.at(1)), &eMidiClip); } if (m_editorVerticalSizes.count() >= 2) { pDocument->saveTextElement("editor-vertical-sizes", QString::number(m_editorVerticalSizes.at(0)) + ',' + QString::number(m_editorVerticalSizes.at(1)), &eMidiClip); } if (m_iEditorDrumMode >= 0) { pDocument->saveTextElement("editor-drum-mode", qtractorDocument::textFromBool(m_iEditorDrumMode > 0), &eMidiClip); } if (!m_sGhostTrackName.isEmpty()) { pDocument->saveTextElement("ghost-track-name", m_sGhostTrackName, &eMidiClip); } if (m_iBeatsPerBar2 > 0) { pDocument->saveTextElement("beats-per-bar-2", QString::number(m_iBeatsPerBar2), &eMidiClip); } if (m_iBeatDivisor2 > 0) { pDocument->saveTextElement("beat-divisor-2", QString::number(m_iBeatDivisor2), &eMidiClip); } pElement->appendChild(eMidiClip); return true; } // MIDI clip export method. bool qtractorMidiClip::clipExport ( ClipExport pfnClipExport, void *pvArg, unsigned long iOffset, unsigned long iLength ) const { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; qtractorMidiSequence *pSeq = sequence(); if (pSeq == nullptr) return false; if (iLength < 1) iLength = clipLength(); const unsigned short iTicksPerBeat = pSession->ticksPerBeat(); const unsigned long iTimeOffset = pSeq->timeOffset(); qtractorTimeScale::Cursor cursor(pSession->timeScale()); qtractorTimeScale::Node *pNode = cursor.seekFrame(clipStart()); const unsigned long t0 = pNode->tickFromFrame(clipStart()); unsigned long f1 = clipStart() + 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; qtractorMidiSequence seq(pSeq->name(), pSeq->channel(), iTicksPerBeat); seq.setBankSelMethod(pTrack->midiBankSelMethod()); seq.setBank(pTrack->midiBank()); seq.setProg(pTrack->midiProg()); for (qtractorMidiEvent *pEvent = pSeq->events().first(); pEvent; pEvent = pEvent->next()) { const unsigned long iTime = pEvent->time(); if (iTime >= iTimeStart && iTime < iTimeEnd) { qtractorMidiEvent *pNewEvent = new qtractorMidiEvent(*pEvent); pNewEvent->setTime(iTime - iTimeStart); if (pNewEvent->type() == qtractorMidiEvent::NOTEON) { pNewEvent->setVelocity((unsigned char) (clipGain() * float(pNewEvent->velocity())) & 0x7f); if (iTime + pEvent->duration() > iTimeEnd) pNewEvent->setDuration(iTimeEnd - iTime); } seq.insertEvent(pNewEvent); } } (*pfnClipExport)(&seq, pvArg); return true; } // MIDI clip freewheeling event enqueue method (needed for export). void qtractorMidiClip::enqueue_export ( qtractorTrack *pTrack, qtractorMidiEvent *pEvent, unsigned long iTime, float fGain ) const { qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return; snd_seq_event_t ev; snd_seq_ev_clear(&ev); snd_seq_ev_schedule_tick(&ev, 0, 0, iTime); 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; ev.data.note.duration = pEvent->duration(); 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(); ev.data.control.value = pEvent->value(); break; case qtractorMidiEvent::PGMCHANGE: ev.type = SND_SEQ_EVENT_PGMCHANGE; ev.data.control.channel = pTrack->midiChannel(); 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; snd_seq_ev_set_sysex(&ev, pEvent->sysex_len(), pEvent->sysex()); break; default: break; } qtractorTimeScale::Cursor& cursor = pSession->timeScale()->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 && ev.data.note.duration > 0) { iTime += (ev.data.note.duration - 1); pNode = cursor.seekTick(iTime); t2 += (pNode->frameFromTick(iTime) - t1); } // Do it for the MIDI track plugins... qtractorMidiManager *pMidiManager = (pTrack->pluginList())->midiManager(); if (pMidiManager) pMidiManager->queued(&ev, t1, t2); // And for the MIDI output plugins as well... // Target MIDI bus... qtractorMidiBus *pMidiBus = static_cast (pTrack->outputBus()); if (pMidiBus && pMidiBus->pluginList_out()) { pMidiManager = (pMidiBus->pluginList_out())->midiManager(); if (pMidiManager) pMidiManager->queued(&ev, t1, t2); } } // Step-input methods... void qtractorMidiClip::setStepInputHead ( unsigned long iStepInputHead ) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = nullptr; if (m_pMidiEditorForm && m_pMidiEditorForm->editor()) pTimeScale = m_pMidiEditorForm->editor()->timeScale(); if (pTimeScale == nullptr) pTimeScale = pSession->timeScale(); if (pTimeScale == nullptr) return; qtractorTimeScale::Cursor cursor(pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(iStepInputHead); m_iStepInputHead = pNode->frameSnap(iStepInputHead); const unsigned long iClipOffset = clipOffset(); unsigned long iStepInputHead2 = m_iStepInputHead; if (iStepInputHead2 >= iClipOffset) iStepInputHead2 -= iClipOffset; m_iStepInputHeadTime = pNode->tickFromFrame(iStepInputHead2); const unsigned short iSnapPerBeat = pTimeScale->snapPerBeat(); unsigned long iStepInputDuration = pNode->ticksPerBeat; if (iSnapPerBeat > 0) iStepInputDuration /= iSnapPerBeat; m_iStepInputTailTime = m_iStepInputHeadTime + iStepInputDuration; m_iStepInputTail = pNode->frameFromTick(m_iStepInputTailTime) + iClipOffset; m_iStepInputLast = 0; } // Step-input last effective frame methods. void qtractorMidiClip::setStepInputLast ( unsigned long iStepInputLast ) { if (m_iStepInputLast > 0) { const unsigned long iStepInputFrame = m_iStepInputHead + iStepInputLast - m_iStepInputLast; if (iStepInputFrame > m_iStepInputTail) { advanceStepInput(); m_iStepInputLast = iStepInputLast; } } else m_iStepInputLast = iStepInputLast; } // Step-input-head/tail advance... void qtractorMidiClip::advanceStepInput (void) { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = nullptr; if (m_pMidiEditorForm && m_pMidiEditorForm->editor()) pTimeScale = m_pMidiEditorForm->editor()->timeScale(); if (pTimeScale == nullptr) pTimeScale = pSession->timeScale(); if (pTimeScale == nullptr) return; qtractorTimeScale::Cursor cursor(pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iStepInputTail); if (pSession->isLooping() && m_iStepInputHead < pSession->loopEnd() && m_iStepInputTail >= pSession->loopEnd()) { m_iStepInputTail = pSession->loopStart(); // m_iStepInputTailTime = pSession->loopStartTime(); } m_iStepInputHead = m_iStepInputTail; const unsigned long iClipOffset = clipOffset(); unsigned long iStepInputHead2 = m_iStepInputHead; if (iStepInputHead2 >= iClipOffset) iStepInputHead2 -= iClipOffset; m_iStepInputHeadTime = pNode->tickFromFrame(iStepInputHead2); const unsigned short iSnapPerBeat = pTimeScale->snapPerBeat(); unsigned long iStepInputDuration = pNode->ticksPerBeat; if (iSnapPerBeat > 0) iStepInputDuration /= iSnapPerBeat; m_iStepInputTailTime = m_iStepInputHeadTime + iStepInputDuration; m_iStepInputTail = pNode->frameFromTick(m_iStepInputTailTime) + iClipOffset; // m_iStepInputLast = 0; } // Step-input editor update... void qtractorMidiClip::updateStepInput (void) { if (m_pMidiEditorForm && m_pMidiEditorForm->editor()) { m_pMidiEditorForm->editor()->setStepInputHead( m_iStepInputLast > 0 ? m_iStepInputTail : m_iStepInputHead); } } // Check if an event with same exact time, type, key and duration // is already in the sequence (avoid duplicates in step-input...) qtractorMidiEvent *qtractorMidiClip::findStepInputEvent ( qtractorMidiEvent *pStepInputEvent ) const { qtractorMidiSequence *pSeq = sequence(); if (pSeq == nullptr) return nullptr; qtractorMidiEvent *pEvent = pSeq->events().first(); for ( ; pEvent; pEvent = pEvent->next()) { if (pEvent->time() > pStepInputEvent->time()) break; if (pEvent->time() == pStepInputEvent->time() && pEvent->type() == pStepInputEvent->type()) { switch (pEvent->type()) { case qtractorMidiEvent::NOTEON: if (pEvent->duration() != pStepInputEvent->duration()) continue; // Fall thru... case qtractorMidiEvent::NOTEOFF: case qtractorMidiEvent::KEYPRESS: if (pEvent->note() != pStepInputEvent->note()) continue; break; case qtractorMidiEvent::CONTROLLER: case qtractorMidiEvent::REGPARAM: case qtractorMidiEvent::NONREGPARAM: case qtractorMidiEvent::CONTROL14: if (pEvent->controller() != pStepInputEvent->controller()) continue; break; case qtractorMidiEvent::CHANPRESS: case qtractorMidiEvent::PITCHBEND: break; default: break; } return pEvent; } } return nullptr; } // Step-input/overdub command control methods... // bool qtractorMidiClip::processInpEvents ( const QList& events, bool bOverdub ) { qtractorMidiSequence *pSeq = sequence(); if (pSeq == nullptr) return false; qtractorCommandList *pCommandList = nullptr; if (m_pMidiEditorForm == nullptr) { qtractorTrack *pTrack = track(); if (pTrack && pTrack->session()) pCommandList = pTrack->session()->commands(); } else { pCommandList = commands(); } if (pCommandList == nullptr) return false; qtractorMidiEditCommand *pLastCommand = static_cast (pCommandList->lastCommand()); if (( m_pInpEventsCommand && pLastCommand && m_pInpEventsCommand != pLastCommand) || ( m_bInpEventsOverdub && !bOverdub) || (!m_bInpEventsOverdub && bOverdub)) { clearInpEvents(); } if (m_pInpEventsCommand == nullptr) { m_pInpEventsCommand = new qtractorMidiEditCommand(this, bOverdub ? QObject::tr("overdub") : QObject::tr("step input")); m_iInpEventsCommand = 0; m_bInpEventsOverdub = bOverdub; } QListIterator iter(events); while (iter.hasNext()) { qtractorMidiEvent *pEvent = iter.next(); const bool bNoteOff = (pEvent->type() == qtractorMidiEvent::NOTEOFF); if (!m_bInpEventsOverdub && findStepInputEvent(pEvent)) { delete pEvent; continue; } if (m_bInpEventsOverdub) pSeq->addEvent(pEvent); else pSeq->insertEvent(pEvent); if (!bNoteOff) m_pInpEventsCommand->insertEvent(pEvent); } if (m_pInpEventsCommand->isEmpty()) { delete m_pInpEventsCommand; clearInpEvents(); return false; } m_pInpEventsCommand->adjust(); setDirtyEx(true); update(); updateEditorContents(); if (++m_iInpEventsCommand > 1) return true; return pCommandList->push(m_pInpEventsCommand); } void qtractorMidiClip::clearInpEvents (void) { m_pInpEventsCommand = nullptr; m_iInpEventsCommand = 0; m_bInpEventsOverdub = false; } // Default MIDI file format (for capture/record) accessors. unsigned short qtractorMidiClip::g_iDefaultFormat = 0; void qtractorMidiClip::setDefaultFormat ( unsigned short iFormat ) { g_iDefaultFormat = iFormat; } unsigned short qtractorMidiClip::defaultFormat (void) { return g_iDefaultFormat; } // end of qtractorMidiClip.cpp qtractor-1.5.9/src/PaxHeaders/qtractorPasteRepeatForm.h0000644000000000000000000000013215101070305020214 xustar0030 mtime=1761898693.086267651 30 atime=1761898693.086267651 30 ctime=1761898693.086267651 qtractor-1.5.9/src/qtractorPasteRepeatForm.h0000644000175000001440000000375615101070305020217 0ustar00rncbcusers// qtractorPasteRepeatForm.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 __qtractorPasteRepeatForm_h #define __qtractorPasteRepeatForm_h #include "ui_qtractorPasteRepeatForm.h" // Forward declarations. class qtractorTimeScale; //---------------------------------------------------------------------------- // qtractorPasteRepeatForm -- UI wrapper form. class qtractorPasteRepeatForm : public QDialog { Q_OBJECT public: // Constructor. qtractorPasteRepeatForm(QWidget *pParent = nullptr); // Destructor. ~qtractorPasteRepeatForm(); // Accepted dialog accessors. void setRepeatCount(unsigned short iRepeatCount); unsigned short repeatCount() const; void setRepeatPeriod(unsigned long iRepeatPeriod); unsigned long repeatPeriod() const; protected slots: void accept(); void reject(); void changed(); void formatChanged(int); void stabilizeForm(); private: // The Qt-designer UI struct... Ui::qtractorPasteRepeatForm m_ui; // Instance variables... qtractorTimeScale *m_pTimeScale; int m_iDirtyCount; }; #endif // __qtractorPasteRepeatForm_h // end of qtractorPasteRepeatForm.h qtractor-1.5.9/src/PaxHeaders/qtractorComboBox.cpp0000644000000000000000000000013215101070305017216 xustar0030 mtime=1761898693.067267591 30 atime=1761898693.067267591 30 ctime=1761898693.067267591 qtractor-1.5.9/src/qtractorComboBox.cpp0000644000175000001440000001062115101070305017206 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.9/src/PaxHeaders/qtractorMessages.h0000644000000000000000000000012715101070305016726 xustar0029 mtime=1761898693.07626762 29 atime=1761898693.07626762 29 ctime=1761898693.07626762 qtractor-1.5.9/src/qtractorMessages.h0000644000175000001440000000607515101070305016722 0ustar00rncbcusers// qtractorMessages.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 __qtractorMessages_h #define __qtractorMessages_h #include // Forward declarations. class qtractorMessagesTextView; class QSocketNotifier; class QFile; //------------------------------------------------------------------------- // qtractorMessages - Messages log dockable window. // class qtractorMessages : public QDockWidget { Q_OBJECT public: // Constructor. qtractorMessages(QWidget *pParent = nullptr); // Destructor. ~qtractorMessages(); // Stdout/stderr capture accessors. bool isCaptureEnabled() const; void setCaptureEnabled(bool bCapture); // Message font accessors. QFont messagesFont() const; void setMessagesFont(const QFont& font); // Maximum number of message lines accessors. int messagesLimit() const; void setMessagesLimit(int iMessagesLimit); // Logging settings. bool isLogging() const; void setLogging(bool bEnabled, const QString& sFilename = QString()); // The main utility methods. void appendMessages(const QString& s); void appendMessagesColor(const QString& s, const QColor& rgb); void appendMessagesText(const QString& s); // Stdout capture functions. void appendStdoutBuffer(const QString& s); void flushStdoutBuffer(); // History reset. void clear(); protected: // Message executives. void appendMessagesLine(const QString& s); void appendMessagesLog(const QString& s); // Just about to notify main-window that we're closing. void closeEvent(QCloseEvent *); // Set stdout/stderr blocking mode. bool stdoutBlock(int fd, bool bBlock) const; // Split stdout/stderr into separate lines... void processStdoutBuffer(); protected slots: // Stdout capture slot. void stdoutNotify(int fd); private: // The maximum number of message lines and current count. int m_iMessagesLines; int m_iMessagesLimit; int m_iMessagesHigh; // The textview main widget. qtractorMessagesTextView *m_pMessagesTextView; // Stdout capture variables. QSocketNotifier *m_pStdoutNotifier; QString m_sStdoutBuffer; int m_fdStdout[2]; // Logging stuff. QFile *m_pMessagesLog; }; #endif // __qtractorMessages_h // end of qtractorMessages.h qtractor-1.5.9/src/PaxHeaders/qtractorSessionCursor.h0000644000000000000000000000013215101070305017774 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.088267658 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorSessionCursor.h0000644000175000001440000000632615101070305017773 0ustar00rncbcusers// qtractorSessionCursor.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 __qtractorSessionCursor_h #define __qtractorSessionCursor_h #include "qtractorTrack.h" // Forward declarations. class qtractorClip; //---------------------------------------------------------------------- // class qtractorSessionCursor - declaration. // class qtractorSessionCursor : public qtractorList::Link { public: // Constructor. qtractorSessionCursor(qtractorSession *pSession, unsigned long iFrame = 0, qtractorTrack::TrackType syncType = qtractorTrack::None); // Destructor. ~qtractorSessionCursor(); // Session accessor. qtractorSession *session() const; // General bi-directional locate method. void seek(unsigned long iFrame, bool bSync = false); // Current frame position accessor. unsigned long frame() const; // Absolute frame-time posiion accessors. unsigned long frameTime() const; unsigned long frameTimeEx() const; // Clip sync flag accessor. void setSyncType(qtractorTrack::TrackType syncType); qtractorTrack::TrackType syncType() const; // Current track clip accessor. qtractorClip *clip(unsigned int iTrack) const; // Add a track to cursor. void addTrack (qtractorTrack *pTrack); // Update track after adding/removing a clip from cursor. void updateTrack (qtractorTrack *pTrack); // Remove a track from cursor. void removeTrack (qtractorTrack *pTrack); // Update current track clip under cursor. void updateTrackClip(qtractorTrack *pTrack); // Reset cursor. void reset(); // Reset track/clips cache. void resetClips(); // Frame-time processor (increment only). void process(unsigned int nframes); protected: // Clip locate method. qtractorClip *seekClip(qtractorTrack *pTrack, qtractorClip *pClip, unsigned long iFrame) const; // Update (stabilize) cursor. void updateClips(qtractorClip **ppClips, unsigned int iTracks); // Remove a track from cursor (by index). void removeTrack (unsigned int iTrack); private: // Instance variables. qtractorSession *m_pSession; unsigned long m_iFrame; unsigned long m_iFrameTime; unsigned long m_iFrameDelta; qtractorTrack::TrackType m_syncType; unsigned int m_iTracks; qtractorClip **m_ppClips; unsigned int m_iSize; }; #endif // __qtractorSessionCursor_h // end of qtractorSessionCursor.h qtractor-1.5.9/src/PaxHeaders/qtractorAudioConnect.h0000644000000000000000000000013215101070305017526 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.062554172 30 ctime=1761898693.062554172 qtractor-1.5.9/src/qtractorAudioConnect.h0000644000175000001440000001025315101070305017517 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.9/src/PaxHeaders/qtractor.qrc0000644000000000000000000000013215101070305015570 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.062554172 30 ctime=1761898693.062554172 qtractor-1.5.9/src/qtractor.qrc0000644000175000001440000001446715101070305015574 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.9/src/PaxHeaders/qtractorMidiEditList.cpp0000644000000000000000000000013215101070305020032 xustar0030 mtime=1761898693.079267629 30 atime=1761898693.079267629 30 ctime=1761898693.079267629 qtractor-1.5.9/src/qtractorMidiEditList.cpp0000644000175000001440000003550615101070305020033 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.9/src/PaxHeaders/qtractorTimeStretcher.cpp0000644000000000000000000000013215101070305020270 xustar0030 mtime=1761898693.090267664 30 atime=1761898693.090267664 30 ctime=1761898693.090267664 qtractor-1.5.9/src/qtractorTimeStretcher.cpp0000644000175000001440000002041315101070305020260 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.9/src/PaxHeaders/qtractorWsolaTimeStretcher.h0000644000000000000000000000013215101070305020743 xustar0030 mtime=1761898693.093267673 30 atime=1761898693.093267673 30 ctime=1761898693.093267673 qtractor-1.5.9/src/qtractorWsolaTimeStretcher.h0000644000175000001440000002246615101070305020745 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.9/src/PaxHeaders/qtractorAudioBuffer.cpp0000644000000000000000000000013215101070305017701 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.062554172 30 ctime=1761898693.062554172 qtractor-1.5.9/src/qtractorAudioBuffer.cpp0000644000175000001440000012015515101070305017675 0ustar00rncbcusers// qtractorAudioBuffer.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 "qtractorAudioBuffer.h" #include "qtractorAudioPeak.h" #include "qtractorTimeStretcher.h" #include "qtractorSession.h" #include "qtractorAudioEngine.h" #include // Glitch, click, pop-free ramp length (in frames). #define QTRACTOR_RAMP_LENGTH 32 //---------------------------------------------------------------------- // class qtractorAudioBufferThread -- Ring-cache manager thread. // // Constructor. qtractorAudioBufferThread::qtractorAudioBufferThread ( unsigned int iSyncSize ) : QThread() { m_iSyncSize = (4 << 1); while (m_iSyncSize < iSyncSize) m_iSyncSize <<= 1; m_iSyncMask = (m_iSyncSize - 1); m_ppSyncItems = new qtractorAudioBuffer * [m_iSyncSize]; m_iSyncRead = 0; m_iSyncWrite = 0; m_bRunState = false; } // Destructor. qtractorAudioBufferThread::~qtractorAudioBufferThread (void) { if (isRunning()) do { setRunState(false); // terminate(); sync(); } while (!wait(100)); delete [] m_ppSyncItems; } // Run state accessor. void qtractorAudioBufferThread::setRunState ( bool bRunState ) { QMutexLocker locker(&m_mutex); m_bRunState = bRunState; } bool qtractorAudioBufferThread::runState (void) const { return m_bRunState; } // Wake from executive wait condition (RT-safe). void qtractorAudioBufferThread::sync ( qtractorAudioBuffer *pAudioBuffer ) { if (pAudioBuffer == nullptr) { unsigned int r = m_iSyncRead; unsigned int w = m_iSyncWrite; while (r != w) { m_ppSyncItems[r]->setSyncFlag(qtractorAudioBuffer::WaitSync, false); ++r &= m_iSyncMask; w = m_iSyncWrite; } m_iSyncRead = r; } else { // !pAudioBuffer->isSyncFlag(qtractorAudioBuffer::WaitSync) 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) { pAudioBuffer->setSyncFlag(qtractorAudioBuffer::WaitSync); m_ppSyncItems[w] = pAudioBuffer; m_iSyncWrite = (w + 1) & m_iSyncMask; } } if (m_mutex.tryLock()) { m_cond.wakeAll(); m_mutex.unlock(); } #ifdef CONFIG_DEBUG_0 else qDebug("qtractorAudioBufferThread[%p]::sync(): tryLock() failed.", this); #endif } // Bypass executive wait condition (non RT-safe). void qtractorAudioBufferThread::syncExport (void) { QMutexLocker locker(&m_mutex); process(); } // Thread run executive. void qtractorAudioBufferThread::run (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioBufferThread[%p]::run(): started.", this); #endif m_mutex.lock(); m_bRunState = true; while (m_bRunState) { // Do whatever we must, then wait for more... process(); // Wait for sync... m_cond.wait(&m_mutex); } m_mutex.unlock(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioBufferThread[%p]::run(): stopped.", this); #endif } // Thread run executive. void qtractorAudioBufferThread::process (void) { unsigned int r = m_iSyncRead; unsigned int w = m_iSyncWrite; while (r != w) { m_ppSyncItems[r]->sync(); ++r &= m_iSyncMask; w = m_iSyncWrite; } m_iSyncRead = r; } // Conditional resize check. void qtractorAudioBufferThread::checkSyncSize ( unsigned int iSyncSize ) { if (iSyncSize > (m_iSyncSize - 4)) { QMutexLocker locker(&m_mutex); unsigned int iNewSyncSize = (m_iSyncSize << 1); while (iNewSyncSize < iSyncSize) iNewSyncSize <<= 1; qtractorAudioBuffer **ppNewSyncItems = new qtractorAudioBuffer * [iNewSyncSize]; qtractorAudioBuffer **ppOldSyncItems = m_ppSyncItems; ::memcpy(ppNewSyncItems, ppOldSyncItems, m_iSyncSize * sizeof(qtractorAudioBuffer *)); m_iSyncSize = iNewSyncSize; m_iSyncMask = (iNewSyncSize - 1); m_ppSyncItems = ppNewSyncItems; delete [] ppOldSyncItems; } } //---------------------------------------------------------------------- // class qtractorAudioBuffer -- Ring buffer/cache method implementation. // // Constructors. qtractorAudioBuffer::qtractorAudioBuffer ( qtractorAudioBufferThread *pSyncThread, unsigned short iChannels ) { m_pSyncThread = pSyncThread; m_iChannels = iChannels; m_pFile = nullptr; m_pRingBuffer = nullptr; m_iThreshold = 0; m_iBufferSize = 0; m_syncFlags = 0; m_iReadOffset = 0; m_iWriteOffset = 0; m_iFileLength = 0; m_bIntegral = false; m_iOffset = 0; m_iLength = 0; m_iLoopStart = 0; m_iLoopEnd = 0; m_iSeekOffset = 0; ATOMIC_SET(&m_seekPending, 0); m_ppFrames = nullptr; m_ppBuffer = nullptr; m_bTimeStretch = false; m_fTimeStretch = 1.0f; m_bPitchShift = false; m_fPitchShift = 1.0f; m_pTimeStretcher = nullptr; m_fGain = 1.0f; m_fPanning = 0.0f; m_pfGains = nullptr; m_fNextGain = 0.0f; m_iRampGain = 0; #ifdef CONFIG_LIBSAMPLERATE m_bResample = false; m_fResampleRatio = 1.0f; m_iInputPending = 0; m_ppInBuffer = nullptr; m_ppOutBuffer = nullptr; m_ppSrcState = nullptr; #endif m_pPeakFile = nullptr; // Buffer engine modes flags. m_iStretcherFlags = g_iDefaultStretcherFlags; } // Default destructor. qtractorAudioBuffer::~qtractorAudioBuffer (void) { close(); } // Return the internal file descriptor. qtractorAudioFile *qtractorAudioBuffer::file (void) const { return m_pFile; } // Buffer channels. unsigned short qtractorAudioBuffer::channels (void) const { return (m_pFile ? m_pFile->channels() : 0); } // Estimated file size in frames. unsigned long qtractorAudioBuffer::frames (void) const { return (m_pFile ? framesIn(m_pFile->frames()) : 0); } // Operational properties. unsigned int qtractorAudioBuffer::bufferSize (void) const { return m_iBufferSize; } // Working resample ratio. float qtractorAudioBuffer::resampleRatio (void) const { #ifdef CONFIG_LIBSAMPLERATE return m_fResampleRatio; #else return 1.0f; #endif } // Operational buffer initializer/terminator. bool qtractorAudioBuffer::open ( const QString& sFilename, int iMode ) { // Make sure everything starts closed. close(); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; const unsigned int iSampleRate = pSession->sampleRate(); // Get proper file type class... m_pFile = qtractorAudioFileFactory::createAudioFile( sFilename, m_iChannels, iSampleRate); if (m_pFile == nullptr) return false; // Go open it... if (!m_pFile->open(sFilename, iMode)) { delete m_pFile; m_pFile = nullptr; return false; } // Check samplerate and how many channels there really are. const unsigned short iBuffers = m_pFile->channels(); // Just one more sanity check... if (iBuffers < 1 || m_pFile->sampleRate() < 1) { m_pFile->close(); delete m_pFile; m_pFile = nullptr; return false; } #ifdef CONFIG_LIBSAMPLERATE // Compute sample rate converter stuff. m_iInputPending = 0; m_fResampleRatio = 1.0f; m_bResample = (iSampleRate != m_pFile->sampleRate()); if (m_bResample) { m_fResampleRatio = float(iSampleRate) / float(m_pFile->sampleRate()); m_ppInBuffer = new float * [iBuffers]; m_ppOutBuffer = new float * [iBuffers]; m_ppSrcState = new SRC_STATE * [iBuffers]; } #endif // FIXME: default logical length gets it total... if (m_iLength == 0) { m_iLength = frames(); if (m_iOffset < m_iLength) m_iLength -= m_iOffset; else m_iOffset = 0; } // Allocate ring-buffer now. unsigned int iBufferSize = m_iLength; if (iBufferSize == 0) iBufferSize = (iSampleRate >> 1); else if (iBufferSize > (iSampleRate << 2)) iBufferSize = (iSampleRate << 2); m_pRingBuffer = new qtractorRingBuffer (iBuffers, iBufferSize); m_iThreshold = (m_pRingBuffer->bufferSize() >> 2); m_iBufferSize = (m_iThreshold >> 2); #ifdef CONFIG_LIBSAMPLERATE if (m_bResample && m_fResampleRatio < 1.0f) { iBufferSize = (unsigned int) framesOut(m_iBufferSize); while (m_iBufferSize < iBufferSize) m_iBufferSize <<= 1; } #endif // Allocate actual buffer stuff... unsigned short i; m_ppFrames = new float * [iBuffers]; for (i = 0; i < iBuffers; ++i) m_ppFrames[i] = new float [m_iBufferSize]; // Allocate time-stretch engine whether needed... if (m_bTimeStretch || m_bPitchShift) { m_pTimeStretcher = new qtractorTimeStretcher(iBuffers, iSampleRate, m_fTimeStretch, m_fPitchShift, m_iStretcherFlags, m_iBufferSize); } #ifdef CONFIG_LIBSAMPLERATE // Sample rate converter stuff, whether needed... if (m_bResample) { int err = 0; for (i = 0; i < iBuffers; ++i) { m_ppInBuffer[i] = m_ppFrames[i]; m_ppOutBuffer[i] = new float [m_iBufferSize]; m_ppSrcState[i] = src_new(g_iDefaultResampleType, 1, &err); } } #endif // Consider it done when recording... if (m_pFile->mode() & qtractorAudioFile::Write) { setSyncFlag(InitSync); } else { // Get a reasonablebuffer size for readMix()... iBufferSize = pSession->audioEngine()->bufferSizeEx(); if (iBufferSize < (m_iBufferSize >> 2)) iBufferSize = (m_iBufferSize >> 2); // Allocate those minimal buffers for readMix()... m_ppBuffer = new float * [iBuffers]; for (i = 0; i < iBuffers; ++i) m_ppBuffer[i] = new float [iBufferSize]; } // Rebuild the whole panning-gain array... m_pfGains = new float [iBuffers]; // (Re)compute stereo-panning/balance gains... float afGains[2] = { 1.0f, 1.0f }; const float fPan = 0.5f * (1.0f + m_fPanning); if (fPan < 0.499f || fPan > 0.501f) { afGains[0] = ::cosf(fPan * M_PI_2); afGains[1] = ::sinf(fPan * M_PI_2); } // Apply to multi-channel gain array (paired fashion)... const unsigned short k = (iBuffers - (iBuffers & 1)); for (i = 0 ; i < k; ++i) m_pfGains[i] = afGains[i & 1]; for ( ; i < iBuffers; ++i) m_pfGains[i] = 1.0f; // Make it sync-managed... if (m_pSyncThread) m_pSyncThread->sync(this); return true; } // Operational buffer terminator. void qtractorAudioBuffer::close (void) { if (m_pFile == nullptr) return; // Wait for regular file close... if (m_pSyncThread) { setSyncFlag(CloseSync); m_pSyncThread->sync(this); do QThread::yieldCurrentThread(); while (isSyncFlag(CloseSync)); } // Delete old panning-gains holders... if (m_pfGains) { delete [] m_pfGains; m_pfGains = nullptr; } // Take careof remains, if applicable... if (m_pFile->mode() & qtractorAudioFile::Write) { // Close on-the-fly peak file, if applicable... if (m_pPeakFile) { m_pPeakFile->closeWrite(); m_pPeakFile = nullptr; } } // Deallocate any buffer stuff... if (m_pTimeStretcher) { delete m_pTimeStretcher; m_pTimeStretcher = nullptr; } // Release internal I/O buffers. if (m_ppBuffer && m_pRingBuffer) { const unsigned short iBuffers = m_pRingBuffer->channels(); for (unsigned short i = 0; i < iBuffers; ++i) { if (m_ppBuffer[i]) { delete [] m_ppBuffer[i]; m_ppBuffer[i] = nullptr; } } delete [] m_ppBuffer; m_ppBuffer = nullptr; } if (m_pRingBuffer) { deleteIOBuffers(); delete m_pRingBuffer; m_pRingBuffer = nullptr; } // Finally delete what we still own. if (m_pFile) { delete m_pFile; m_pFile = nullptr; } // Reset all relevant state variables. m_iThreshold = 0; m_iBufferSize = 0; m_syncFlags = 0; m_iReadOffset = 0; m_iWriteOffset = 0; m_iFileLength = 0; m_bIntegral = false; m_iSeekOffset = 0; ATOMIC_SET(&m_seekPending, 0); m_fNextGain = 0.0f; m_iRampGain = 0; m_pPeakFile = nullptr; } // Buffer data read. int qtractorAudioBuffer::read ( float **ppFrames, unsigned int iFrames, unsigned int iOffset ) { if (m_pRingBuffer == nullptr) return -1; int nread; unsigned long ro = m_iReadOffset; unsigned long ls = m_iLoopStart; unsigned long le = m_iLoopEnd; // Are we off decoded file limits (EoS)? if (ro >= m_iFileLength) { nread = iFrames; if (ls < le) { if (m_bIntegral) { const unsigned int ri = m_pRingBuffer->readIndex(); while (ri < le && ri + nread >= le) { nread -= le - ri; ro = m_iOffset + ls; m_pRingBuffer->setReadIndex(ls); } } else { ls += m_iOffset; le += m_iOffset; while (ro < le && ro + nread >= le) { nread -= le - ro; ro = ls; } } } m_iReadOffset = (ro + nread); // Force out-of-sync... setSyncFlag(ReadSync, false); return nread; } // Are we in the middle of the loop range ? if (ls < le) { if (m_bIntegral) { const unsigned int ri = m_pRingBuffer->readIndex(); while (ri < le && ri + iFrames >= le) { nread = m_pRingBuffer->read(ppFrames, le - ri, iOffset); iFrames -= nread; iOffset += nread; ro = m_iOffset + ls; m_pRingBuffer->setReadIndex(ls); } } else { ls += m_iOffset; le += m_iOffset; while (ro < le && ro + iFrames >= le) { nread = m_pRingBuffer->read(ppFrames, le - ro, iOffset); iFrames -= nread; iOffset += nread; ro = ls; } } } // Move the (remaining) data around... nread = m_pRingBuffer->read(ppFrames, iFrames, iOffset); m_iReadOffset = (ro + nread); if (m_iReadOffset >= m_iOffset + m_iLength) { // Force out-of-sync... setSyncFlag(ReadSync, false); } // Time to sync()? if (!m_bIntegral && m_pSyncThread && m_pRingBuffer->writable() > m_iThreshold) m_pSyncThread->sync(this); return nread; } // Buffer data write. int qtractorAudioBuffer::write ( float **ppFrames, unsigned int iFrames, unsigned short iChannels, unsigned int iOffset ) { if (m_pRingBuffer == nullptr) return -1; const unsigned short iBuffers = m_pRingBuffer->channels(); if (iChannels < 1) iChannels = iBuffers; unsigned int nwrite = iFrames; if (iChannels == iBuffers) { // Direct write... nwrite = m_pRingBuffer->write(ppFrames, nwrite, iOffset); } else { // Multiplexed write... const unsigned int ws = m_pRingBuffer->writable(); if (nwrite > ws) nwrite = ws; if (nwrite > 0) { const unsigned int w = m_pRingBuffer->writeIndex(); const unsigned int bs = m_pRingBuffer->bufferSize(); unsigned int n, n1, n2; if (w + nwrite > bs) { n1 = (bs - w); n2 = (w + nwrite) & m_pRingBuffer->bufferMask(); } else { n1 = nwrite; n2 = 0; } unsigned short i, j; float **ppBuffer = m_pRingBuffer->buffer(); if (iChannels > iBuffers) { for (i = 0, j = 0; i < iBuffers; ++i, ++j) { for (n = 0; n < n1; ++n) ppBuffer[j][n + w] = ppFrames[i][n] + iOffset; for (n = 0; n < n2; ++n) ppBuffer[j][n] = ppFrames[i][n + n1] + iOffset; } for (j = 0; i < iChannels; ++i) { for (n = 0; n < n1; ++n) ppBuffer[j][n + w] += ppFrames[i][n] + iOffset; for (n = 0; n < n2; ++n) ppBuffer[j][n] += ppFrames[i][n + n1] + iOffset; if (++j >= iBuffers) j = 0; } } else { // (iChannels < iBuffers) i = 0; for (j = 0; j < iBuffers; ++j) { for (n = 0; n < n1; ++n) ppBuffer[j][n + w] = ppFrames[i][n] + iOffset; for (n = 0; n < n2; ++n) ppBuffer[j][n] = ppFrames[i][n + n1] + iOffset; if (++i >= iChannels) i = 0; } } m_pRingBuffer->setWriteIndex(w + nwrite); } } // Make it statiscally correct... m_iWriteOffset += nwrite; // Time to sync()? if (m_pSyncThread && m_pRingBuffer->readable() > m_iThreshold) m_pSyncThread->sync(this); return nwrite; } // Special kind of super-read/channel-mix. int qtractorAudioBuffer::readMix ( float **ppFrames, unsigned int iFrames, unsigned short iChannels, unsigned int iOffset, float fGain ) { if (m_pRingBuffer == nullptr) return -1; int nread = iFrames; unsigned long ro = m_iReadOffset; unsigned long ls = m_iLoopStart; unsigned long le = m_iLoopEnd; // Are we off decoded file limits (EoS)? if (ro >= m_iFileLength) { if (ls < le) { if (m_bIntegral) { const unsigned int ri = m_pRingBuffer->readIndex(); while (ri < le && ri + nread >= le && nread > 0) { nread -= le - ri; ro = m_iOffset + ls; m_pRingBuffer->setReadIndex(ls); } } else { ls += m_iOffset; le += m_iOffset; while (ro < le && ro + nread >= le && nread > 0) { nread -= le - ro; ro = ls; } } } m_iReadOffset = (ro + nread); // Force out-of-sync... setSyncFlag(ReadSync, false); return nread; } // Are we in the middle of the loop range ? if (ls < le) { if (m_bIntegral) { const unsigned int ri = m_pRingBuffer->readIndex(); while (ri < le && ri + iFrames >= le && nread > 0) { m_iRampGain = -1; nread = readMixFrames(ppFrames, le - ri, iChannels, iOffset, fGain); iFrames -= nread; iOffset += nread; ro = m_iOffset + ls; m_pRingBuffer->setReadIndex(ls); } } else { ls += m_iOffset; le += m_iOffset; while (le >= ro && ro + iFrames >= le && nread > 0) { m_iRampGain = -1; nread = readMixFrames(ppFrames, le - ro, iChannels, iOffset, fGain); iFrames -= nread; iOffset += nread; ro = ls; } } } // Take care of end-of-stream... const unsigned long re = m_iOffset + m_iLength; if (ro + iFrames >= re) m_iRampGain = -1; // Mix the (remaining) data around... nread = readMixFrames(ppFrames, iFrames, iChannels, iOffset, fGain); m_iReadOffset = (ro + nread); if (m_iReadOffset >= re) { // Force out-of-sync... setSyncFlag(ReadSync, false); } // Time to sync()? if (!m_bIntegral && m_pSyncThread && m_pRingBuffer->writable() > m_iThreshold) m_pSyncThread->sync(this); return nread; } int qtractorAudioBuffer::readMux ( float **ppFrames, unsigned int iFrames, unsigned short iChannels, unsigned int iOffset, float fGain ) { if (iFrames == 0) return 0; const int nread = m_pRingBuffer->read(m_ppBuffer, iFrames); if (nread == 0) return 0; const unsigned short iBuffers = m_pRingBuffer->channels(); unsigned short i, j; int n; float *pFrames, *pBuffer; float fGainIter; const float fPrevGain = m_fGain * fGain; for (i = 0; i < iChannels; ++i) ::memset(ppFrames[i], 0, iFrames * sizeof(float)); if (iChannels == iBuffers) { for (i = 0; i < iBuffers; ++i) { pFrames = ppFrames[i] + iOffset; pBuffer = m_ppBuffer[i]; fGainIter = fPrevGain * m_pfGains[i]; for (n = 0; n < nread; ++n) *pFrames++ += fGainIter * *pBuffer++; } } else if (iChannels > iBuffers) { j = 0; for (i = 0; i < iChannels; ++i) { pFrames = ppFrames[i] + iOffset; pBuffer = m_ppBuffer[j]; fGainIter = fPrevGain * m_pfGains[j]; for (n = 0; n < nread; ++n) *pFrames++ += fGainIter * *pBuffer++; if (++j >= iBuffers) j = 0; } } else { // (iChannels < iBuffers) i = 0; for (j = 0; j < iBuffers; ++j) { pFrames = ppFrames[i] + iOffset; pBuffer = m_ppBuffer[j]; fGainIter = fPrevGain * m_pfGains[j]; for (n = 0; n < nread; ++n) *pFrames++ += fGainIter * *pBuffer++; if (++i >= iChannels) i = 0; } } return nread; } // Buffer data seek. bool qtractorAudioBuffer::seek ( unsigned long iFrame ) { if (m_pRingBuffer == nullptr) return false; // Seek is only valid on read-only mode. if (m_pFile == nullptr) return false; if (m_pFile->mode() & qtractorAudioFile::Write) return false; // Must not break while on initial sync... if (!isSyncFlag(InitSync)) return false; // Reset running gain... m_fNextGain = 0.0f; m_iRampGain = (m_iOffset == 0 && iFrame == 0 ? 0 : 1); // Are we off-limits? if (iFrame >= m_iLength) return false; // Force (premature) out-of-sync... setSyncFlag(ReadSync, false); setSyncFlag(WaitSync, false); // Special case on integral cached files... if (m_bIntegral) { m_pRingBuffer->setReadIndex(iFrame); // m_iWriteOffset = m_iOffset + iFrame; m_iReadOffset = m_iOffset + iFrame; // Maybe (always) in-sync... //setSyncFlag(ReadSync); return true; } // Adjust to logical offset... iFrame += m_iOffset; // Check if target is already cached... const unsigned int rs = m_pRingBuffer->readable(); const unsigned int ri = m_pRingBuffer->readIndex(); const unsigned long ro = m_iReadOffset; if (iFrame >= ro && iFrame < ro + rs) { m_pRingBuffer->setReadIndex(ri + iFrame - ro); // m_iWriteOffset += iFrame - ro; m_iReadOffset = iFrame; // Maybe (late) in-sync... //setSyncFlag(ReadSync); return true; } #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioBuffer[%p]::seek(%lu) pending(%d, %lu) wo=%lu ro=%lu", this, iFrame, ATOMIC_GET(&m_seekPending), m_iSeekOffset, m_iWriteOffset, m_iReadOffset); #endif // Bad luck, gotta go straight down to disk... // if (!seekSync(iFrame)) // return false; // Force (late) out-of-sync... m_iReadOffset = m_iOffset + m_iLength + 1; // An unlikely offset! m_iSeekOffset = iFrame; ATOMIC_INC(&m_seekPending); // readSync(); if (m_pSyncThread) m_pSyncThread->sync(this); return true; } // Sync thread state flags accessors (ought to be inline?). void qtractorAudioBuffer::setSyncFlag ( SyncFlag flag, bool bOn ) { if (bOn) m_syncFlags |= (unsigned char) (flag); else m_syncFlags &= ~(unsigned char) (flag); } bool qtractorAudioBuffer::isSyncFlag ( SyncFlag flag ) const { return (m_syncFlags & (unsigned char) (flag)); } // Initial thread-sync executive (if file is on read mode, // check whether it can be cache-loaded integrally). void qtractorAudioBuffer::initSync (void) { if (m_pRingBuffer == nullptr) return; // Initialization is only valid on read-only mode. if (m_pFile == nullptr) return; if (m_pFile->mode() & qtractorAudioFile::Write) return; #if 0 if (isSyncFlag(CloseSync)) return; #endif // Reset all relevant state variables. setSyncFlag(ReadSync, false); m_iReadOffset = 0; m_iWriteOffset = 0; m_iFileLength = 0; m_bIntegral = false; m_iSeekOffset = 0; ATOMIC_SET(&m_seekPending, 0); // Reset running gain... m_fNextGain = 0.0f; m_iRampGain = (m_iOffset == 0 ? 0 : 1); // Set to initial offset... m_iSeekOffset = m_iOffset; ATOMIC_INC(&m_seekPending); // Initial buffer read in... readSync(); // We're mostly done with initialization... // m_bInitSync = true; setSyncFlag(InitSync); // Check if fitted integrally... if (m_iFileLength < m_iOffset + m_pRingBuffer->bufferSize() - 1) { m_bIntegral = true; deleteIOBuffers(); } else // Re-sync if loop falls short in initial area... if (m_iLoopStart < m_iLoopEnd && m_iWriteOffset >= m_iOffset + m_iLoopEnd) { // Will do it again, but now we're // sure we aren't fit integral... m_iSeekOffset = m_iOffset; ATOMIC_INC(&m_seekPending); // Initial buffer re-read in... readSync(); } // Make sure we're not closing anymore, // of course, don't be ridiculous... setSyncFlag(CloseSync, false); } // Base-mode sync executive. void qtractorAudioBuffer::sync (void) { if (m_pFile == nullptr) return; if (!isSyncFlag(WaitSync)) return; if (!isSyncFlag(InitSync)) { initSync(); setSyncFlag(WaitSync, false); } else { setSyncFlag(WaitSync, false); const int mode = m_pFile->mode(); if (mode & qtractorAudioFile::Read) readSync(); else if (mode & qtractorAudioFile::Write) writeSync(); if (isSyncFlag(CloseSync)) { m_pFile->close(); setSyncFlag(CloseSync, false); } } } // Audio frame process synchronization predicate method. bool qtractorAudioBuffer::inSync ( unsigned long iFrameStart, unsigned long iFrameEnd ) { if (!isSyncFlag(InitSync)) return false; if (isSyncFlag(ReadSync)) return true; #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioBuffer[%p]::inSync(%lu, %lu) (%ld)", this, iFrameStart, iFrameEnd, long(m_iReadOffset) - long(iFrameStart + m_iOffset)); #endif if (m_iReadOffset == iFrameStart + m_iOffset) { setSyncFlag(ReadSync); return true; } seek(iFrameEnd); return false; } // Export-mode sync executive. void qtractorAudioBuffer::syncExport (void) { if (m_pSyncThread) m_pSyncThread->syncExport(); } // Read-mode sync executive. void qtractorAudioBuffer::readSync (void) { if (m_pRingBuffer == nullptr) return; if (isSyncFlag(CloseSync)) return; // Check whether we have some hard-seek pending... if (ATOMIC_TAZ(&m_seekPending)) { // Do it... if (!seekSync(m_iSeekOffset)) return; // Refill the whole buffer.... m_pRingBuffer->reset(); // Override with new intended offset... m_iWriteOffset = m_iSeekOffset; m_iReadOffset = m_iSeekOffset; } const unsigned int ws = m_pRingBuffer->writable(); if (ws == 0) return; unsigned int nahead = ws; unsigned int ntotal = 0; while (nahead > 0 && !ATOMIC_GET(&m_seekPending)) { // Take looping into account, if any... const unsigned long ls = m_iOffset + m_iLoopStart; const unsigned long le = m_iOffset + m_iLoopEnd; const bool bLooping = (ls < le && m_iWriteOffset < le && isSyncFlag(InitSync)); // Adjust request for sane size... if (nahead > m_iBufferSize) nahead = m_iBufferSize; // Check whether behind the loop-end point... if (bLooping && m_iWriteOffset + nahead > le) nahead = le - m_iWriteOffset; // Check whether behind the logical end-of-file... if (m_iWriteOffset + nahead > m_iOffset + m_iLength) { if (m_iWriteOffset < m_iOffset + m_iLength) { nahead = (m_iOffset + m_iLength) - m_iWriteOffset; } else { nahead = 0; } } // Read the block in... // (assume end-of-file) int nread = -1; if (nahead > 0) nread = readBuffer(nahead); if (nread > 0) { // Another block was read in... m_iWriteOffset += nread; if (m_iFileLength < m_iWriteOffset) m_iFileLength = m_iWriteOffset; if (bLooping && m_iWriteOffset >= le && seekSync(ls)) m_iWriteOffset = ls; ntotal += nread; nahead = (ws > ntotal ? ws - ntotal : 0); } else if (nread < 0) { // Think of end-of-file... nahead = 0; // But we can re-cache, if not an integral fit... if (m_iFileLength >= m_iOffset + m_pRingBuffer->bufferSize() - 1) { const unsigned long offset = (bLooping ? ls : m_iOffset); if (seekSync(offset)) m_iWriteOffset = offset; } } } } // Write-mode sync executive. void qtractorAudioBuffer::writeSync (void) { if (m_pRingBuffer == nullptr) return; const unsigned int rs = m_pRingBuffer->readable(); if (rs == 0) return; unsigned int nwrite; unsigned int nbehind = rs; unsigned int ntotal = 0; while (nbehind > 0) { // Adjust request for sane size... if (nbehind > m_iBufferSize) nbehind = m_iBufferSize; // Read the block out... nwrite = writeBuffer(nbehind); if (nwrite > 0) { // Another block was written out... m_iReadOffset += nwrite; if (m_iFileLength < m_iReadOffset) m_iFileLength = m_iReadOffset; ntotal += nwrite; } // Think of end-of-turn... if (nwrite < nbehind) { nbehind = 0; } else { nbehind = rs - ntotal; } } } // Internal-seek sync executive. bool qtractorAudioBuffer::seekSync ( unsigned long iFrame ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioBuffer[%p]::seekSync(%lu) pending(%d, %lu) wo=%lu ro=%lu", this, iFrame, ATOMIC_GET(&m_seekPending), m_iSeekOffset, m_iWriteOffset, m_iReadOffset); #endif #ifdef CONFIG_LIBSAMPLERATE if (m_bResample) { m_iInputPending = 0; const unsigned short iBuffers = m_pRingBuffer->channels(); for (unsigned short i = 0; i < iBuffers; ++i) { if (m_ppSrcState && m_ppSrcState[i]) src_reset(m_ppSrcState[i]); m_ppInBuffer[i] = m_ppFrames[i]; } } #endif if (m_pTimeStretcher) m_pTimeStretcher->reset(); return m_pFile->seek(framesOut(iFrame)); } // Last-mile frame buffer-helper processor. int qtractorAudioBuffer::writeFrames ( float **ppFrames, unsigned int iFrames ) { // Time-stretch processing... if (m_pTimeStretcher) { int nread = 0; m_pTimeStretcher->process(ppFrames, iFrames); unsigned int nwrite = m_pRingBuffer->writable(); unsigned int nahead = m_pTimeStretcher->available(); while (nahead > 0) { if (nahead > m_iBufferSize) nahead = m_iBufferSize; if (nahead > nwrite) nahead = nwrite; if (nahead > 0) nahead = m_pTimeStretcher->retrieve(ppFrames, nahead); if (nahead > 0) { nread += m_pRingBuffer->write(ppFrames, nahead); nwrite = m_pRingBuffer->writable(); nahead = m_pTimeStretcher->available(); } } // Done with time-stretching... return nread; } // Normal straight processing... return m_pRingBuffer->write(ppFrames, iFrames); } // Flush buffer-helper processor. int qtractorAudioBuffer::flushFrames ( float **ppFrames, unsigned int iFrames ) { int nread = 0; // Flush time-stretch processing... if (m_pTimeStretcher) { m_pTimeStretcher->flush(); unsigned int nwrite = m_pRingBuffer->writable(); unsigned int nahead = m_pTimeStretcher->available(); while (nahead > 0) { if (nahead > m_iBufferSize) nahead = m_iBufferSize; if (nahead > nwrite) nahead = nwrite; if (nahead > 0) nahead = m_pTimeStretcher->retrieve(ppFrames, nahead); if (nahead > 0) { nread += m_pRingBuffer->write(ppFrames, nahead); nwrite = m_pRingBuffer->writable(); nahead = m_pTimeStretcher->available(); } } } // Zero-flush till known end-of-clip (avoid sure drifting)... if ((nread < 1) && (m_iWriteOffset + iFrames > m_iOffset + m_iLength)) { const unsigned int nahead = (m_iOffset + m_iLength) - m_iWriteOffset; const unsigned short iBuffers = m_pRingBuffer->channels(); for (unsigned short i = 0; i < iBuffers; ++i) ::memset(ppFrames[i], 0, nahead * sizeof(float)); nread += m_pRingBuffer->write(ppFrames, nahead); } return (nread > 0 ? nread : -1); } // I/O buffer read process; return -1 on end-of-file. int qtractorAudioBuffer::readBuffer ( unsigned int iFrames ) { #ifdef CONFIG_DEBUG_0 qDebug("+readBuffer(%u)", iFrames); #endif int nread = 0; #ifdef CONFIG_LIBSAMPLERATE if (m_bResample) { if (iFrames > m_iInputPending) nread = m_pFile->read(m_ppInBuffer, iFrames - m_iInputPending); nread += m_iInputPending; int ngen = 0; SRC_DATA src_data; const unsigned short iBuffers = m_pRingBuffer->channels(); for (unsigned short i = 0; i < iBuffers; ++i) { // Fill all resampler parameter data... src_data.data_in = m_ppFrames[i]; src_data.data_out = m_ppOutBuffer[i]; src_data.input_frames = nread; src_data.output_frames = iFrames; src_data.end_of_input = (nread < 1); src_data.src_ratio = m_fResampleRatio; src_data.input_frames_used = 0; src_data.output_frames_gen = 0; // Do the resample work... if (src_process(m_ppSrcState[i], &src_data) == 0) { if (i == 0) { m_iInputPending = nread - src_data.input_frames_used; ngen = src_data.output_frames_gen; } if (m_iInputPending > 0 && src_data.input_frames_used > 0) { ::memmove(m_ppFrames[i], m_ppFrames[i] + src_data.input_frames_used, m_iInputPending * sizeof(float)); } m_ppInBuffer[i] = m_ppFrames[i] + m_iInputPending; } } if (ngen > 0) nread = writeFrames(m_ppOutBuffer, ngen); else if (nread < 1) nread = flushFrames(m_ppFrames, iFrames); // Maybe EoF! } else { #endif // CONFIG_LIBSAMPLERATE nread = m_pFile->read(m_ppFrames, iFrames); if (nread > 0) nread = writeFrames(m_ppFrames, nread); else nread = flushFrames(m_ppFrames, iFrames); // Maybe EoF! #ifdef CONFIG_LIBSAMPLERATE } #endif // CONFIG_LIBSAMPLERATE #ifdef CONFIG_DEBUG_0 qDebug("-readBuffer(%u) --> nread=%d", iFrames, nread); #endif return nread; } // I/O buffer write process. int qtractorAudioBuffer::writeBuffer ( unsigned int iFrames ) { int nwrite = m_pRingBuffer->read(m_ppFrames, iFrames, 0); if (nwrite > 0) { nwrite = m_pFile->write(m_ppFrames, nwrite); if (m_pPeakFile) nwrite = m_pPeakFile->write(m_ppFrames, nwrite); } return nwrite; } // Special kind of super-read/channel-mix buffer helper. int qtractorAudioBuffer::readMixFrames ( float **ppFrames, unsigned int iFrames, unsigned short iChannels, unsigned int iOffset, float fGain ) { if (iFrames == 0) return 0; const int nread = m_pRingBuffer->read(m_ppBuffer, iFrames); if (nread == 0) return 0; const unsigned short iBuffers = m_pRingBuffer->channels(); unsigned short i, j; int n; float fGainIter, fGainStep1, fGainStep2; float *pFrames, *pBuffer; // HACK: Case of clip ramp in/out-set in this run... if (m_iRampGain) { const unsigned int nramp = (nread < QTRACTOR_RAMP_LENGTH ? nread : QTRACTOR_RAMP_LENGTH); const int n0 = (m_iRampGain < 0 ? nread - nramp : nramp); const int n1 = (m_iRampGain < 0 ? n0 : 0); const int n2 = (m_iRampGain < 0 ? nread : n0); fGainStep1 = float(m_iRampGain) / float(nramp); for (i = 0; i < iBuffers; ++i) { fGainIter = (m_iRampGain < 0 ? 1.0f : 0.0f); pBuffer = m_ppBuffer[i] + n1; for (n = n1; n < n2; ++n, fGainIter += fGainStep1) *pBuffer++ *= fGainIter; } m_iRampGain = (m_iRampGain < 0 ? 1 : 0); // fPrevGain = fGain; } // Reset running gain... const float fNextGain = m_fGain * fGain; const float fPrevGain = (m_fNextGain < 1E-9f ? fNextGain : m_fNextGain); m_fNextGain = fNextGain; fGainStep1 = (m_fNextGain - fPrevGain) / float(nread); if (iChannels == iBuffers) { for (i = 0; i < iBuffers; ++i) { pFrames = ppFrames[i] + iOffset; pBuffer = m_ppBuffer[i]; fGainIter = fPrevGain * m_pfGains[i]; fGainStep2 = fGainStep1 * m_pfGains[i]; for (n = 0; n < nread; ++n, fGainIter += fGainStep2) *pFrames++ += fGainIter * *pBuffer++; } } else if (iChannels > iBuffers) { j = 0; for (i = 0; i < iChannels; ++i) { pFrames = ppFrames[i] + iOffset; pBuffer = m_ppBuffer[j]; fGainIter = fPrevGain * m_pfGains[j]; fGainStep2 = fGainStep1 * m_pfGains[j]; for (n = 0; n < nread; ++n, fGainIter += fGainStep2) *pFrames++ += fGainIter * *pBuffer++; if (++j >= iBuffers) j = 0; } } else { // (iChannels < iBuffers) i = 0; for (j = 0; j < iBuffers; ++j) { pFrames = ppFrames[i] + iOffset; pBuffer = m_ppBuffer[j]; fGainIter = fPrevGain * m_pfGains[j]; fGainStep2 = fGainStep1 * m_pfGains[j]; for (n = 0; n < nread; ++n, fGainIter += fGainStep2) *pFrames++ += fGainIter * *pBuffer++; if (++i >= iChannels) i = 0; } } return nread; } // I/O buffer release. void qtractorAudioBuffer::deleteIOBuffers (void) { const unsigned short iBuffers = m_pRingBuffer->channels(); unsigned short i; #ifdef CONFIG_LIBSAMPLERATE // Release internal and resampler buffers. for (i = 0; i < iBuffers; ++i) { if (m_ppSrcState && m_ppSrcState[i]) m_ppSrcState[i] = src_delete(m_ppSrcState[i]); if (m_ppOutBuffer && m_ppOutBuffer[i]) { delete [] m_ppOutBuffer[i]; m_ppOutBuffer[i] = nullptr; } } if (m_ppSrcState) { delete [] m_ppSrcState; m_ppSrcState = nullptr; } if (m_ppOutBuffer) { delete [] m_ppOutBuffer; m_ppOutBuffer = nullptr; } if (m_ppInBuffer) { delete [] m_ppInBuffer; m_ppInBuffer = nullptr; } m_iInputPending = 0; #endif if (m_ppFrames) { for (i = 0; i < iBuffers; ++i) { if (m_ppFrames[i]) { delete [] m_ppFrames[i]; m_ppFrames[i] = nullptr; } } delete [] m_ppFrames; m_ppFrames = nullptr; } } // Reset this buffers state. void qtractorAudioBuffer::reset ( bool bLooping ) { if (m_pRingBuffer == nullptr) return; unsigned long iFrame = 0; // If looping, we'll reset to loop-start point, // otherwise it's a buffer full-reset... if (bLooping && m_iLoopStart < m_iLoopEnd) { iFrame = m_iLoopStart; // Make sure we're not already there... // (force out-of-sync with an unlikely offset!) if (!m_bIntegral) m_iReadOffset = m_iOffset + m_iLength + 1; } seek(iFrame); } // Frame position converters. unsigned long qtractorAudioBuffer::framesIn ( unsigned long iFrames ) const { #ifdef CONFIG_LIBSAMPLERATE if (m_bResample) iFrames = (unsigned long) (float(iFrames) * m_fResampleRatio); #endif if (m_bTimeStretch) iFrames = (unsigned long) (float(iFrames) * m_fTimeStretch); return iFrames; } unsigned long qtractorAudioBuffer::framesOut ( unsigned long iFrames ) const { #ifdef CONFIG_LIBSAMPLERATE if (m_bResample) iFrames = (unsigned long) (float(iFrames) / m_fResampleRatio); #endif if (m_bTimeStretch) iFrames = (unsigned long) (float(iFrames) / m_fTimeStretch); return iFrames; } // Logical clip-offset (in frames from beginning-of-file). void qtractorAudioBuffer::setOffset ( unsigned long iOffset ) { m_iOffset = iOffset; } unsigned long qtractorAudioBuffer::offset() const { return m_iOffset; } // Logical clip-length (in frames from clip-start/offset). void qtractorAudioBuffer::setLength ( unsigned long iLength ) { m_iLength = iLength; } unsigned long qtractorAudioBuffer::length (void) const { return m_iLength; } // Current (last known) file length accessor. unsigned long qtractorAudioBuffer::fileLength (void) const { return m_iFileLength; } // Local gain/panning accessors. void qtractorAudioBuffer::setGain ( float fGain ) { m_fGain = fGain; } float qtractorAudioBuffer::gain (void) const { return m_fGain; } void qtractorAudioBuffer::setPanning ( float fPanning ) { m_fPanning = fPanning; } float qtractorAudioBuffer::panning (void) const { return m_fPanning; } float qtractorAudioBuffer::channelGain ( unsigned short i ) const { return m_pfGains[i]; } // Loop points accessors. void qtractorAudioBuffer::setLoop ( unsigned long iLoopStart, unsigned long iLoopEnd ) { // Buffer-looping magic check! if (iLoopStart < iLoopEnd) { m_iLoopStart = iLoopStart; m_iLoopEnd = iLoopEnd; } else { m_iLoopStart = 0; m_iLoopEnd = 0; } // Force out-of-sync... setSyncFlag(ReadSync, false); m_iReadOffset = m_iOffset + m_iLength + 1; // An unlikely offset! // seek(m_iReadOffset - m_iOffset); } unsigned long qtractorAudioBuffer::loopStart (void) const { return m_iLoopStart; } unsigned long qtractorAudioBuffer::loopEnd (void) const { return m_iLoopEnd; } // Time-stretch factor. void qtractorAudioBuffer::setTimeStretch ( float fTimeStretch ) { if (fTimeStretch < 0.1f) fTimeStretch = 0.1f; else if (fTimeStretch > 10.0f) fTimeStretch = 10.0f; m_bTimeStretch = (fTimeStretch < 1.0f - 1e-3f || fTimeStretch > 1.0f + 1e-3f); m_fTimeStretch = (m_bTimeStretch ? fTimeStretch : 1.0f); } float qtractorAudioBuffer::timeStretch (void) const { return m_fTimeStretch; } bool qtractorAudioBuffer::isTimeStretch (void) const { return m_bTimeStretch; } // Pitch-shift factor. void qtractorAudioBuffer::setPitchShift ( float fPitchShift ) { if (fPitchShift < 0.1f) fPitchShift = 0.1f; else if (fPitchShift > 10.0f) fPitchShift = 10.0f; m_bPitchShift = (fPitchShift < 1.0f - 1e-3f || fPitchShift > 1.0f + 1e-3f); m_fPitchShift = (m_bPitchShift ? fPitchShift : 1.0f); } float qtractorAudioBuffer::pitchShift (void) const { return m_fPitchShift; } bool qtractorAudioBuffer::isPitchShift (void) const { return m_bPitchShift; } // Internal peak descriptor accessors. void qtractorAudioBuffer::setPeakFile ( qtractorAudioPeakFile *pPeakFile ) { m_pPeakFile = pPeakFile; // Reset for building the peak file on-the-fly... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr || !m_pPeakFile->openWrite(m_iChannels, pSession->sampleRate())) m_pPeakFile = nullptr; } qtractorAudioPeakFile *qtractorAudioBuffer::peakFile (void) const { return m_pPeakFile; } // Buffer engines flags accessors (local option) void qtractorAudioBuffer::setStretcherFlags ( unsigned int iStretcherFlags ) { m_iStretcherFlags = iStretcherFlags; } unsigned int qtractorAudioBuffer::stretcherFlags (void) const { return m_iStretcherFlags; } // Buffer engines flags accessors (global option) unsigned int qtractorAudioBuffer::g_iDefaultStretcherFlags = qtractorTimeStretcher::WsolaTimeStretch; void qtractorAudioBuffer::setDefaultStretcherFlags ( unsigned int iStretcherFlags ) { g_iDefaultStretcherFlags = iStretcherFlags; } unsigned int qtractorAudioBuffer::defaultStretcherFlags (void) { return g_iDefaultStretcherFlags; } // Sample-rate converter type (global option). int qtractorAudioBuffer::g_iDefaultResampleType = 2; // SRC_SINC_FASTEST; void qtractorAudioBuffer::setDefaultResampleType ( int iResampleType ) { g_iDefaultResampleType = iResampleType; } int qtractorAudioBuffer::defaultResampleType (void) { return g_iDefaultResampleType; } // end of qtractorAudioBuffer.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMidiEditList.h0000644000000000000000000000013215101070305017477 xustar0030 mtime=1761898693.079267629 30 atime=1761898693.079267629 30 ctime=1761898693.079267629 qtractor-1.5.9/src/qtractorMidiEditList.h0000644000175000001440000000736615101070305017503 0ustar00rncbcusers// qtractorMidiEditList.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 __qtractorMidiEditList_h #define __qtractorMidiEditList_h #include "qtractorScrollView.h" #include #include // Forward declarations. class qtractorMidiEditor; class QResizeEvent; class QMouseEvent; class QKeyEvent; //---------------------------------------------------------------------------- // qtractorMidiEditList -- MIDI sequence key scale widget. class qtractorMidiEditList : public qtractorScrollView { Q_OBJECT public: // Constructor. qtractorMidiEditList(qtractorMidiEditor *pEditor, QWidget *pParent); // Destructor. ~qtractorMidiEditList(); // Item height constants (in pixels). enum { ItemHeightMin = 4, ItemHeightBase = 8, ItemHeightMax = 32 }; // Item height methods. void setItemHeight(unsigned short iItemHeight); unsigned short itemHeight() const; // Update key-list content height. void updateContentsHeight(); // Rectangular contents update. void updateContents(const QRect& rect); // Overall contents update. void updateContents(); // Piano keyboard note-on/off handlers. void dragNoteOn(int iNote, int iVelocity = 1, bool bForce = false); void dragNoteOff(); protected: // Virtual size hint. QSize sizeHint() const { return QSize(60, 240); } // Resize event handler. void resizeEvent(QResizeEvent *pResizeEvent); // Draw the time scale. void drawContents(QPainter *pPainter, const QRect& rect); // Reset drag/select/move state. void resetDragState(); // Handle item selection with mouse. void mousePressEvent(QMouseEvent *pMouseEvent); void mouseMoveEvent(QMouseEvent *pMouseEvent); void mouseReleaseEvent(QMouseEvent *pMouseEvent); // Keyboard event handler. void keyPressEvent(QKeyEvent *pKeyEvent); // Handle zoom with mouse wheel. void wheelEvent(QWheelEvent *pWheelEvent); // Trap for help/tool-tip events. bool eventFilter(QObject *pObject, QEvent *pEvent); // Piano keyboard note-on/off handlers. void dragNoteOn(const QPoint& pos, int iVelocity = 1); // Piano keyboard note descriminator. int noteAt(const QPoint& pos) const; // Piano keyboard note-key shaper. QPainterPath notePath(int iNote) const; protected slots: // To have timeline in h-sync with main track view. void contentsYMovingSlot(int cx, int cy); // (Re)create the time scale pixmap. void updatePixmap(int cx, int cy); private: // The logical parent binding. qtractorMidiEditor *m_pEditor; // Local double-buffering pixmap. QPixmap m_pixmap; // Current item height. unsigned short m_iItemHeight; // The current selecting/dragging list stuff. enum DragState { DragNone = 0, DragStart, DragSelect } m_dragState; QRect m_rectDrag; QPoint m_posDrag; // The current note being keyed on. int m_iNoteOn; int m_iNoteVel; QPainterPath m_pathNote; }; #endif // __qtractorMidiEditList_h // end of qtractorMidiEditList.h qtractor-1.5.9/src/PaxHeaders/qtractorPluginSelectForm.cpp0000644000000000000000000000013215101070305020730 xustar0030 mtime=1761898693.087267654 30 atime=1761898693.087267654 30 ctime=1761898693.087267654 qtractor-1.5.9/src/qtractorPluginSelectForm.cpp0000644000175000001440000003102415101070305020720 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.9/src/PaxHeaders/qtractorMidiThumbView.cpp0000644000000000000000000000013215101070305020223 xustar0030 mtime=1761898693.084267645 30 atime=1761898693.084267645 30 ctime=1761898693.084267645 qtractor-1.5.9/src/qtractorMidiThumbView.cpp0000644000175000001440000003430615101070305020221 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.9/src/PaxHeaders/qtractorVst3Plugin.h0000644000000000000000000000012715101070305017175 xustar0029 mtime=1761898693.09226767 29 atime=1761898693.09226767 29 ctime=1761898693.09226767 qtractor-1.5.9/src/qtractorVst3Plugin.h0000644000175000001440000001375015101070305017167 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.9/src/PaxHeaders/qtractorCtlEvent.h0000644000000000000000000000013215101070305016677 xustar0030 mtime=1761898693.068267594 30 atime=1761898693.068267594 30 ctime=1761898693.068267594 qtractor-1.5.9/src/qtractorCtlEvent.h0000644000175000001440000000410515101070305016667 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.9/src/PaxHeaders/qtractorTrackButton.h0000644000000000000000000000013215101070305017413 xustar0030 mtime=1761898693.090267664 30 atime=1761898693.090267664 30 ctime=1761898693.090267664 qtractor-1.5.9/src/qtractorTrackButton.h0000644000175000001440000000543615101070305017413 0ustar00rncbcusers// qtractorTrackButton.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 __qtractorTrackButton_h #define __qtractorTrackButton_h #include "qtractorTrack.h" #include "qtractorObserverWidget.h" #include //---------------------------------------------------------------------------- // qtractorMidiControlButton -- MIDI controller observer tool button. class qtractorMidiControlButton : public qtractorObserverWidget { Q_OBJECT public: // Constructor. qtractorMidiControlButton(QWidget *pParent = nullptr); protected slots: // MIDI controller/observer attachment (context menu) slot. void midiControlActionSlot(); void midiControlMenuSlot(const QPoint& pos); protected: // MIDI controller/observer attachment (context menu) activator. void addMidiControlAction(qtractorMidiControlObserver *pMidiObserver); private: // MIDI controller/observer leftover. QAction *m_pMidiControlAction; }; //---------------------------------------------------------------------------- // qtractorTrackButton -- Track observer tool button. class qtractorTrackButton : public qtractorMidiControlButton { Q_OBJECT public: // Constructor. qtractorTrackButton(qtractorTrack *pTrack, qtractorTrack::ToolType toolType, QWidget *pParent = nullptr); // Destructor. ~qtractorTrackButton(); // Specific accessors. void setTrack(qtractorTrack *pTrack); qtractorTrack *track() const; qtractorTrack::ToolType toolType() const; // Refresh color (palette) state buttons void updateTrackButton(); protected slots: // Special toggle slot. void toggledSlot(bool bOn); protected: // Visitor setup. void updateTrack(); // Visitors overload. void updateValue(float fValue); private: // Instance variables. qtractorTrack *m_pTrack; qtractorTrack::ToolType m_toolType; // Special background color. QColor m_rgbOn; }; #endif // __qtractorTrackButton_h // end of qtractorTrackButton.h qtractor-1.5.9/src/PaxHeaders/qtractorMessageBox.h0000644000000000000000000000012715101070305017214 xustar0029 mtime=1761898693.07626762 29 atime=1761898693.07626762 29 ctime=1761898693.07626762 qtractor-1.5.9/src/qtractorMessageBox.h0000644000175000001440000000443115101070305017202 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.9/src/PaxHeaders/qtractorPluginCommand.h0000644000000000000000000000013215101070305017710 xustar0030 mtime=1761898693.086267651 30 atime=1761898693.086267651 30 ctime=1761898693.086267651 qtractor-1.5.9/src/qtractorPluginCommand.h0000644000175000001440000002672415101070305017713 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.9/src/PaxHeaders/qtractorAtomic.h0000644000000000000000000000013215101070305016367 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.062554172 30 ctime=1761898693.062554172 qtractor-1.5.9/src/qtractorAtomic.h0000644000175000001440000001046115101070305016361 0ustar00rncbcusers// qtractorAtomic.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 __qtractorAtomic_h #define __qtractorAtomic_h #define HAVE_QATOMIC_H #if defined(HAVE_QATOMIC_H) # include #endif #if defined(__cplusplus) extern "C" { #endif #if defined(HAVE_QATOMIC_H) #ifndef QT_VERSION_CHECK #include #endif typedef QAtomicInt qtractorAtomic; #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) #define ATOMIC_GET(a) ((a)->loadRelaxed()) #define ATOMIC_SET(a,v) ((a)->storeRelaxed(v)) #elif QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #define ATOMIC_GET(a) ((a)->load()) #define ATOMIC_SET(a,v) ((a)->store(v)) #else #define ATOMIC_GET(a) ((int) *(a)) #define ATOMIC_SET(a,v) (*(a) = (v)) #endif static inline int ATOMIC_CAS ( qtractorAtomic *pVal, int iOldValue, int iNewValue ) { return pVal->testAndSetOrdered(iOldValue, iNewValue); } #else // !HAVE_QATOMIC_H #if defined(__GNUC__) #if defined(__powerpc__) || defined(__powerpc64__) static inline int ATOMIC_CAS1 ( volatile int *pValue, int iOldValue, int iNewValue ) { register int result; asm volatile ( "# ATOMIC_CAS1 \n" " lwarx r0, 0, %1 \n" " cmpw r0, %2 \n" " bne- 1f \n" " sync \n" " stwcx. %3, 0, %1 \n" " bne- 1f \n" " li %0, 1 \n" " b 2f \n" "1: \n" " li %0, 0 \n" "2: \n" : "=r" (result) : "r" (pValue), "r" (iOldValue), "r" (iNewValue) : "r0" ); return result; } #elif defined(__i386__) || defined(__x86_64__) static inline int ATOMIC_CAS1 ( volatile int *pValue, int iOldValue, int iNewValue ) { register char result; asm volatile ( "# ATOMIC_CAS1 \n" "lock ; cmpxchgl %2, %3 \n" "sete %1 \n" : "=a" (iNewValue), "=qm" (result) : "r" (iNewValue), "m" (*pValue), "0" (iOldValue) : "memory" ); return result; } #else # error "qtractorAtomic.h: unsupported target compiler processor (GNUC)." #endif #elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) static inline int ATOMIC_CAS1 ( volatile int *pValue, int iOldValue, int iNewValue ) { register char result; __asm { push ebx push esi mov esi, pValue mov eax, iOldValue mov ebx, iNewValue lock cmpxchg dword ptr [esi], ebx sete result pop esi pop ebx } return result; } #else # error "qtractorAtomic.h: unsupported target compiler processor (WIN32)." #endif typedef struct { volatile int value; } qtractorAtomic; #define ATOMIC_GET(a) ((a)->value) #define ATOMIC_SET(a,v) ((a)->value = (v)) static inline int ATOMIC_CAS ( qtractorAtomic *pVal, int iOldValue, int iNewValue ) { return ATOMIC_CAS1(&(pVal->value), iOldValue, iNewValue); } #endif // !HAVE_QATOMIC_H // Strict test-and-set primite. static inline int ATOMIC_TAS ( qtractorAtomic *pVal ) { return ATOMIC_CAS(pVal, 0, 1); } // Strict test-and-add primitive. static inline int ATOMIC_ADD ( qtractorAtomic *pVal, int iAddValue ) { volatile int iOldValue, iNewValue; do { iOldValue = ATOMIC_GET(pVal); iNewValue = iOldValue + iAddValue; } while (!ATOMIC_CAS(pVal, iOldValue, iNewValue)); return iNewValue; } #define ATOMIC_INC(a) ATOMIC_ADD((a), (+1)) #define ATOMIC_DEC(a) ATOMIC_ADD((a), (-1)) // Special test-and-zero primitive (invented here:) static inline int ATOMIC_TAZ ( qtractorAtomic *pVal ) { volatile int iOldValue; do { iOldValue = ATOMIC_GET(pVal); } while (iOldValue && !ATOMIC_CAS(pVal, iOldValue, 0)); return iOldValue; } #if defined(__cplusplus) } #endif #endif // __qtractorAtomic_h // end of qtractorAtomic.h qtractor-1.5.9/src/PaxHeaders/qtractor.cpp0000644000000000000000000000013215101070305015565 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.062554172 30 ctime=1761898693.062554172 qtractor-1.5.9/src/qtractor.cpp0000644000175000001440000004014115101070305015555 0ustar00rncbcusers// qtractor.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 "qtractor.h" #include "qtractorOptions.h" #include "qtractorMainForm.h" #include "qtractorPaletteForm.h" #include #include #include #include #include #ifndef CONFIG_PREFIX #define CONFIG_PREFIX "/usr/local" #endif #ifndef CONFIG_BINDIR #define CONFIG_BINDIR CONFIG_PREFIX "/bin" #endif #ifndef CONFIG_DATADIR #define CONFIG_DATADIR CONFIG_PREFIX "/share" #endif #ifndef CONFIG_LIBDIR #if defined(__x86_64__) #define CONFIG_LIBDIR CONFIG_PREFIX "/lib64" #else #define CONFIG_LIBDIR CONFIG_PREFIX "/lib" #endif #endif #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #define CONFIG_PLUGINSDIR CONFIG_LIBDIR "/qt4/plugins" #elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #define CONFIG_PLUGINSDIR CONFIG_LIBDIR "/qt5/plugins" #else #define CONFIG_PLUGINSDIR CONFIG_LIBDIR "/qt6/plugins" #endif #ifdef CONFIG_X11 #ifdef CONFIG_VST2 #include "qtractorVst2Plugin.h" #endif #endif //------------------------------------------------------------------------- // Singleton application instance stuff (Qt/X11 only atm.) // #ifdef CONFIG_XUNIQUE #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_X11 #define QTRACTOR_XUNIQUE "qtractorApplication" #include /* for gethostname() */ #include #include #endif // CONFIG_X11 #else #include #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) #include #endif #include #include #include #endif #endif // CONFIG_XUNIQUE // Constructor. qtractorApplication::qtractorApplication ( int& argc, char **argv ) : QApplication(argc, argv), m_pQtTranslator(nullptr), m_pMyTranslator(nullptr), m_pWidget(nullptr) #ifdef CONFIG_XUNIQUE #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_X11 , m_pDisplay(nullptr) , m_aUnique(0) , m_wOwner(0) #endif // CONFIG_X11 #else , m_pMemory(nullptr) , m_pServer(nullptr) #endif #endif // CONFIG_XUNIQUE { #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) QApplication::setApplicationName(QTRACTOR_TITLE); QApplication::setApplicationDisplayName(QTRACTOR_TITLE); // QTRACTOR_TITLE " - " + QObject::tr(QTRACTOR_SUBTITLE)); #if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0) QApplication::setDesktopFileName( QString("org.rncbc.%1").arg(PROJECT_NAME)); #endif QApplication::setApplicationVersion(PROJECT_VERSION); #endif // Load translation support. QLocale loc; if (loc.language() != QLocale::C) { // Try own Qt translation... m_pQtTranslator = new QTranslator(this); QString sLocName = "qt_" + loc.name(); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QString sLocPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath); #else QString sLocPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath); #endif if (m_pQtTranslator->load(sLocName, sLocPath)) { QApplication::installTranslator(m_pQtTranslator); } else { delete m_pQtTranslator; m_pQtTranslator = nullptr; #ifdef CONFIG_DEBUG qWarning("Warning: no translation found for '%s' locale: %s/%s.qm", loc.name().toUtf8().constData(), sLocPath.toUtf8().constData(), sLocName.toUtf8().constData()); #endif } // Try own application translation... m_pMyTranslator = new QTranslator(this); sLocName = "qtractor_" + loc.name(); if (m_pMyTranslator->load(sLocName, sLocPath)) { QApplication::installTranslator(m_pMyTranslator); } else { sLocPath = QApplication::applicationDirPath(); sLocPath.remove(CONFIG_BINDIR); sLocPath.append(CONFIG_DATADIR "/qtractor/translations"); if (m_pMyTranslator->load(sLocName, sLocPath)) { QApplication::installTranslator(m_pMyTranslator); } else { delete m_pMyTranslator; m_pMyTranslator = nullptr; #ifdef CONFIG_DEBUG qWarning("Warning: no translation found for '%s' locale: %s/%s.qm", loc.name().toUtf8().constData(), sLocPath.toUtf8().constData(), sLocName.toUtf8().constData()); #endif } } } } // Destructor. qtractorApplication::~qtractorApplication (void) { #ifdef CONFIG_XUNIQUE #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) clearServer(); #endif #endif // CONFIG_XUNIQUE if (m_pMyTranslator) delete m_pMyTranslator; if (m_pQtTranslator) delete m_pQtTranslator; } // Main application widget accessors. void qtractorApplication::setMainWidget ( QWidget *pWidget ) { m_pWidget = pWidget; #ifdef CONFIG_XUNIQUE #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_X11 m_wOwner = m_pWidget->winId(); if (m_pDisplay && m_wOwner) { XGrabServer(m_pDisplay); XSetSelectionOwner(m_pDisplay, m_aUnique, m_wOwner, CurrentTime); XUngrabServer(m_pDisplay); } #endif // CONFIG_X11 #endif #endif // CONFIG_XUNIQUE } // Check if another instance is running, // and raise its proper main widget... bool qtractorApplication::setup (void) { #ifdef CONFIG_XUNIQUE #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_X11 m_pDisplay = QX11Info::display(); if (m_pDisplay) { QString sUnique = QTRACTOR_XUNIQUE; QString sUserName = QString::fromUtf8(::getenv("USER")); if (sUserName.isEmpty()) sUserName = QString::fromUtf8(::getenv("USERNAME")); if (!sUserName.isEmpty()) { sUnique += ':'; sUnique += sUserName; } char szHostName[255]; if (::gethostname(szHostName, sizeof(szHostName)) == 0) { sUnique += '@'; sUnique += QString::fromUtf8(szHostName); } m_aUnique = XInternAtom(m_pDisplay, sUnique.toUtf8().constData(), false); XGrabServer(m_pDisplay); m_wOwner = XGetSelectionOwner(m_pDisplay, m_aUnique); XUngrabServer(m_pDisplay); if (m_wOwner != None) { // First, notify any freedesktop.org WM // that we're about to show the main widget... Screen *pScreen = XDefaultScreenOfDisplay(m_pDisplay); int iScreen = XScreenNumberOfScreen(pScreen); XEvent ev; memset(&ev, 0, sizeof(ev)); ev.xclient.type = ClientMessage; ev.xclient.display = m_pDisplay; ev.xclient.window = m_wOwner; ev.xclient.message_type = XInternAtom(m_pDisplay, "_NET_ACTIVE_WINDOW", false); ev.xclient.format = 32; ev.xclient.data.l[0] = 0; // Source indication. ev.xclient.data.l[1] = 0; // Timestamp. ev.xclient.data.l[2] = 0; // Requestor's currently active window (none) ev.xclient.data.l[3] = 0; ev.xclient.data.l[4] = 0; XSelectInput(m_pDisplay, m_wOwner, StructureNotifyMask); XSendEvent(m_pDisplay, RootWindow(m_pDisplay, iScreen), false, (SubstructureNotifyMask | SubstructureRedirectMask), &ev); XSync(m_pDisplay, false); XRaiseWindow(m_pDisplay, m_wOwner); // And then, let it get caught on destination // by QApplication::x11EventFilter... const QByteArray value = QTRACTOR_XUNIQUE; XChangeProperty( m_pDisplay, m_wOwner, m_aUnique, m_aUnique, 8, PropModeReplace, (unsigned char *) value.data(), value.length()); // Done. return true; } } #endif // CONFIG_X11 #else return setupServer(); #endif #else return false; #endif // !CONFIG_XUNIQUE } #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_X11 #ifdef CONFIG_XUNIQUE void qtractorApplication::x11PropertyNotify ( Window w ) { if (m_pDisplay && m_pWidget && m_wOwner == w) { // Always check whether our property-flag is still around... Atom aType; int iFormat = 0; unsigned long iItems = 0; unsigned long iAfter = 0; unsigned char *pData = 0; if (XGetWindowProperty( m_pDisplay, m_wOwner, m_aUnique, 0, 1024, false, m_aUnique, &aType, &iFormat, &iItems, &iAfter, &pData) == Success && aType == m_aUnique && iItems > 0 && iAfter == 0) { // Avoid repeating it-self... XDeleteProperty(m_pDisplay, m_wOwner, m_aUnique); // Just make it always shows up fine... m_pWidget->showNormal(); m_pWidget->raise(); m_pWidget->activateWindow(); } // Free any left-overs... if (iItems > 0 && pData) XFree(pData); } } #endif // CONFIG_XUNIQUE bool qtractorApplication::x11EventFilter ( XEvent *pEv ) { #ifdef CONFIG_XUNIQUE if (pEv->type == PropertyNotify && pEv->xproperty.state == PropertyNewValue) x11PropertyNotify(pEv->xproperty.window); #endif // CONFIG_XUNIQUE #ifdef CONFIG_VST2 // Let xevents be processed by VST2 plugin editors... if (qtractorVst2Plugin::x11EventFilter(pEv)) return true; #endif return QApplication::x11EventFilter(pEv); } #endif // CONFIG_X11 #else #ifdef CONFIG_XUNIQUE // Local server/shmem setup. bool qtractorApplication::setupServer (void) { clearServer(); m_sUnique = QCoreApplication::applicationName(); QString sUserName = QString::fromUtf8(::getenv("USER")); if (sUserName.isEmpty()) sUserName = QString::fromUtf8(::getenv("USERNAME")); if (!sUserName.isEmpty()) { m_sUnique += ':'; m_sUnique += sUserName; } m_sUnique += '@'; m_sUnique += QHostInfo::localHostName(); #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) const QNativeIpcKey nativeKey = QSharedMemory::legacyNativeKey(m_sUnique); #if defined(Q_OS_UNIX) m_pMemory = new QSharedMemory(nativeKey); m_pMemory->attach(); delete m_pMemory; #endif m_pMemory = new QSharedMemory(nativeKey); #else #if defined(Q_OS_UNIX) m_pMemory = new QSharedMemory(m_sUnique); m_pMemory->attach(); delete m_pMemory; #endif m_pMemory = new QSharedMemory(m_sUnique); #endif bool bServer = false; const qint64 pid = QCoreApplication::applicationPid(); struct Data { qint64 pid; }; if (m_pMemory->create(sizeof(Data))) { m_pMemory->lock(); Data *pData = static_cast (m_pMemory->data()); if (pData) { pData->pid = pid; bServer = true; } m_pMemory->unlock(); } else if (m_pMemory->attach()) { m_pMemory->lock(); // maybe not necessary? Data *pData = static_cast (m_pMemory->data()); if (pData) bServer = (pData->pid == pid); m_pMemory->unlock(); } if (bServer) { QLocalServer::removeServer(m_sUnique); m_pServer = new QLocalServer(); m_pServer->setSocketOptions(QLocalServer::UserAccessOption); m_pServer->listen(m_sUnique); QObject::connect(m_pServer, SIGNAL(newConnection()), SLOT(newConnectionSlot())); } else { QLocalSocket socket; socket.connectToServer(m_sUnique); if (socket.state() == QLocalSocket::ConnectingState) socket.waitForConnected(200); if (socket.state() == QLocalSocket::ConnectedState) { socket.write(QCoreApplication::arguments().join(' ').toUtf8()); socket.flush(); socket.waitForBytesWritten(200); } } return !bServer; } // Local server/shmem cleanup. void qtractorApplication::clearServer (void) { if (m_pServer) { m_pServer->close(); delete m_pServer; m_pServer = nullptr; } if (m_pMemory) { delete m_pMemory; m_pMemory = nullptr; } m_sUnique.clear(); } // Local server conection slot. void qtractorApplication::newConnectionSlot (void) { QLocalSocket *pSocket = m_pServer->nextPendingConnection(); QObject::connect(pSocket, SIGNAL(readyRead()), SLOT(readyReadSlot())); } // Local server data-ready slot. void qtractorApplication::readyReadSlot (void) { QLocalSocket *pSocket = qobject_cast (sender()); if (pSocket) { const qint64 nread = pSocket->bytesAvailable(); if (nread > 0) { const QByteArray data = pSocket->read(nread); // Just make it always shows up fine... if (m_pWidget) { m_pWidget->showNormal(); m_pWidget->raise(); m_pWidget->activateWindow(); } // Reset the server... setupServer(); } } } #endif // CONFIG_XUNIQUE #endif //------------------------------------------------------------------------- // stacktrace - Signal crash handler. // #ifdef CONFIG_STACKTRACE #if defined(__GNUC__) && defined(Q_OS_LINUX) #include #include #include #include #include void stacktrace ( int signo ) { pid_t pid; int rc; int status = 0; char cmd[80]; // Reinstall default handler; prevent race conditions... ::signal(signo, SIG_DFL); static const char *shell = "/bin/sh"; static const char *format = "gdb -q --batch --pid=%d" " --eval-command='thread apply all bt'"; snprintf(cmd, sizeof(cmd), format, (int) getpid()); pid = fork(); // Fork failure! if (pid < 0) return; // Fork child... if (pid == 0) { execl(shell, shell, "-c", cmd, nullptr); _exit(1); return; } // Parent here: wait for child to terminate... do { rc = waitpid(pid, &status, 0); } while ((rc < 0) && (errno == EINTR)); // Dispatch any logging, if any... QApplication::processEvents(QEventLoop::AllEvents, 3000); // Make sure everyone terminates... kill(pid, SIGTERM); _exit(1); } #endif #endif //------------------------------------------------------------------------- // main - The main program trunk. // int main ( int argc, char **argv ) { Q_INIT_RESOURCE(qtractor); #ifdef CONFIG_STACKTRACE #if defined(__GNUC__) && defined(Q_OS_LINUX) ::signal(SIGILL, stacktrace); ::signal(SIGFPE, stacktrace); ::signal(SIGSEGV, stacktrace); ::signal(SIGABRT, stacktrace); ::signal(SIGBUS, stacktrace); #endif #endif #if defined(Q_OS_LINUX) && !defined(CONFIG_WAYLAND) ::setenv("QT_QPA_PLATFORM", "xcb", 0); #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif #endif qtractorApplication app(argc, argv); // Construct default settings; override with command line arguments. qtractorOptions options; if (!options.parse_args(app.arguments())) { app.quit(); return 1; } // Have another instance running? if (app.setup()) { app.quit(); return 2; } // Custom style sheet (QSS)... if (!options.sCustomStyleSheet.isEmpty()) { app.setStyleSheet( qtractorMainForm::styleSheet(options.sCustomStyleSheet)); } // Custom style theme... QString sPluginsPath = QApplication::applicationDirPath(); sPluginsPath.remove(CONFIG_BINDIR); sPluginsPath.append(CONFIG_PLUGINSDIR); if (QDir(sPluginsPath).exists()) app.addLibraryPath(sPluginsPath); if (!options.sCustomStyleTheme.isEmpty()) app.setStyle(QStyleFactory::create(options.sCustomStyleTheme)); // Custom color theme (eg. "KXStudio")... const QChar sep = QDir::separator(); QString sPalettePath = QApplication::applicationDirPath(); sPalettePath.remove(CONFIG_BINDIR); sPalettePath.append(CONFIG_DATADIR); sPalettePath.append(sep); sPalettePath.append(PROJECT_NAME); sPalettePath.append(sep); sPalettePath.append("palette"); if (QDir(sPalettePath).exists()) { QStringList names; names.append("KXStudio"); names.append("Wonton Soup"); QStringListIterator name_iter(names); while (name_iter.hasNext()) { const QString& name = name_iter.next(); const QFileInfo fi(sPalettePath, name + ".conf"); if (fi.isReadable()) { qtractorPaletteForm::addNamedPaletteConf( &options.settings(), name, fi.absoluteFilePath()); } } } QPalette pal(app.palette()); if (qtractorPaletteForm::namedPalette( &options.settings(), options.sCustomColorTheme, pal)) app.setPalette(pal); // Set default base font... if (options.iBaseFontSize > 0) app.setFont(QFont(app.font().family(), options.iBaseFontSize)); // Set custom icon theme... QStringList icon_paths; if (!options.sCustomIconsTheme.isEmpty()) icon_paths << options.sCustomIconsTheme; icon_paths << ":images"; QIcon::setFallbackSearchPaths(icon_paths); // Construct, setup and show the main form (a pseudo-singleton). qtractorMainForm w; w.setup(&options); // w.show(); // Settle this one as application main widget... app.setMainWidget(&w); return app.exec(); } // end of qtractor.cpp qtractor-1.5.9/src/PaxHeaders/qtractorConnectForm.cpp0000644000000000000000000000013215101070305017723 xustar0030 mtime=1761898693.068267594 30 atime=1761898693.068267594 30 ctime=1761898693.068267594 qtractor-1.5.9/src/qtractorConnectForm.cpp0000644000175000001440000002530315101070305017716 0ustar00rncbcusers// qtractorConnectForm.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 "qtractorConnectForm.h" #include "qtractorAbout.h" #include "qtractorOptions.h" #include "qtractorSession.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include //---------------------------------------------------------------------------- // qtractorConnectForm -- UI wrapper form. // Constructor. qtractorConnectForm::qtractorConnectForm ( QWidget *pParent, Qt::WindowFlags wflags ) : QWidget(pParent, wflags) { // Setup UI struct... m_ui.setupUi(this); m_pAudioConnect = new qtractorAudioConnect( m_ui.AudioOListView, m_ui.AudioIListView, m_ui.AudioConnectorView); m_pMidiConnect = new qtractorMidiConnect( m_ui.MidiOListView, m_ui.MidiIListView, m_ui.MidiConnectorView); // UI signal/slot connections... QObject::connect(m_ui.AudioIClientsComboBox, SIGNAL(activated(int)), SLOT(audioIClientChanged())); QObject::connect(m_ui.AudioOClientsComboBox, SIGNAL(activated(int)), SLOT(audioOClientChanged())); QObject::connect(m_ui.AudioConnectPushButton, SIGNAL(clicked()), SLOT(audioConnectSelected())); QObject::connect(m_ui.AudioDisconnectPushButton, SIGNAL(clicked()), SLOT(audioDisconnectSelected())); QObject::connect(m_ui.AudioDisconnectAllPushButton, SIGNAL(clicked()), SLOT(audioDisconnectAll())); QObject::connect(m_ui.AudioRefreshPushButton, SIGNAL(clicked()), SLOT(audioRefresh())); QObject::connect(m_ui.MidiIClientsComboBox, SIGNAL(activated(int)), SLOT(midiIClientChanged())); QObject::connect(m_ui.MidiOClientsComboBox, SIGNAL(activated(int)), SLOT(midiOClientChanged())); QObject::connect(m_ui.MidiConnectPushButton, SIGNAL(clicked()), SLOT(midiConnectSelected())); QObject::connect(m_ui.MidiDisconnectPushButton, SIGNAL(clicked()), SLOT(midiDisconnectSelected())); QObject::connect(m_ui.MidiDisconnectAllPushButton, SIGNAL(clicked()), SLOT(midiDisconnectAll())); QObject::connect(m_ui.MidiRefreshPushButton, SIGNAL(clicked()), SLOT(midiRefresh())); // Connect it to some UI feedback slots. QObject::connect(m_ui.AudioOListView, SIGNAL(itemSelectionChanged()), SLOT(audioStabilize())); QObject::connect(m_ui.AudioIListView, SIGNAL(itemSelectionChanged()), SLOT(audioStabilize())); QObject::connect(m_ui.MidiOListView, SIGNAL(itemSelectionChanged()), SLOT(midiStabilize())); QObject::connect(m_ui.MidiIListView, SIGNAL(itemSelectionChanged()), SLOT(midiStabilize())); QObject::connect(m_ui.AudioOListView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(audioStabilize())); QObject::connect(m_ui.AudioIListView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(audioStabilize())); QObject::connect(m_ui.MidiOListView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(midiStabilize())); QObject::connect(m_ui.MidiIListView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(midiStabilize())); QObject::connect(m_pAudioConnect, SIGNAL(connectChanged()), SLOT(audioConnectChanged())); QObject::connect(m_pMidiConnect, SIGNAL(connectChanged()), SLOT(midiConnectChanged())); } // Destructor. qtractorConnectForm::~qtractorConnectForm (void) { delete m_pAudioConnect; delete m_pMidiConnect; } // Audio client name change slots. void qtractorConnectForm::audioIClientChanged (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::audioIClientChanged()"); #endif // Reset any port name pattern... m_ui.AudioIListView->setPortName(QString()); audioRefresh(); } void qtractorConnectForm::audioOClientChanged (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::audioOClientChanged()"); #endif // Reset any port name pattern... m_ui.AudioOListView->setPortName(QString()); audioRefresh(); } // Connect current selected ports. void qtractorConnectForm::audioConnectSelected (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::audioConnectSelected()"); #endif m_pAudioConnect->connectSelected(); } // Disconnect current selected ports. void qtractorConnectForm::audioDisconnectSelected (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::audioDisconnectSelected()"); #endif m_pAudioConnect->disconnectSelected(); } // Disconnect all connected ports. void qtractorConnectForm::audioDisconnectAll (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::audioDisconnectAll()"); #endif m_pAudioConnect->disconnectAll(); } // Refresh complete form by notifying the parent form. void qtractorConnectForm::audioUpdate ( bool bClear ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::audioUpdate(%d)", int(bClear)); #endif m_ui.AudioOListView->setClientName( m_ui.AudioOClientsComboBox->currentIndex() > 0 && !bClear ? m_ui.AudioOClientsComboBox->currentText() : QString()); m_ui.AudioIListView->setClientName( m_ui.AudioIClientsComboBox->currentIndex() > 0 && !bClear ? m_ui.AudioIClientsComboBox->currentText() : QString()); m_pAudioConnect->updateContents(bClear); updateClientsComboBox(m_ui.AudioOClientsComboBox, m_ui.AudioOListView, m_pAudioConnect->icon(qtractorAudioConnect::ClientOut)); updateClientsComboBox(m_ui.AudioIClientsComboBox, m_ui.AudioIListView, m_pAudioConnect->icon(qtractorAudioConnect::ClientIn)); audioStabilize(); } // Refresh complete form conditionally. void qtractorConnectForm::audioReset (void) { audioUpdate( m_ui.AudioOClientsComboBox->currentIndex() > 0 || m_ui.AudioIClientsComboBox->currentIndex() > 0); } // A helper connection change slot. void qtractorConnectForm::audioConnectChanged (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::audioConectChanged()"); #endif audioStabilize(); emit connectChanged(); } // A helper stabilization slot. void qtractorConnectForm::audioStabilize (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::audioStabilize()"); #endif m_ui.AudioConnectPushButton->setEnabled( m_pAudioConnect->canConnectSelected()); m_ui.AudioDisconnectPushButton->setEnabled( m_pAudioConnect->canDisconnectSelected()); m_ui.AudioDisconnectAllPushButton->setEnabled( m_pAudioConnect->canDisconnectAll()); } // MIDI client name change slots. void qtractorConnectForm::midiIClientChanged (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::midiIClientChanged()"); #endif // Reset any port name pattern... m_ui.MidiIListView->setPortName(QString()); midiRefresh(); } void qtractorConnectForm::midiOClientChanged (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::midiOClientChanged()"); #endif // Reset any port name pattern... m_ui.MidiOListView->setPortName(QString()); midiRefresh(); } // Connect current selected ports. void qtractorConnectForm::midiConnectSelected (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::midiConnectSelected()"); #endif m_pMidiConnect->connectSelected(); } // Disconnect current selected ports. void qtractorConnectForm::midiDisconnectSelected (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::midiDisconnectSelected()"); #endif m_pMidiConnect->disconnectSelected(); } // Disconnect all connected ports. void qtractorConnectForm::midiDisconnectAll (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::midiDisconnectAll()"); #endif m_pMidiConnect->disconnectAll(); } // Refresh complete form by notifying the parent form. void qtractorConnectForm::midiUpdate ( bool bClear ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::midiUpdate(%d)", int(bClear)); #endif m_ui.MidiOListView->setClientName( m_ui.MidiOClientsComboBox->currentIndex() > 0 && !bClear ? m_ui.MidiOClientsComboBox->currentText() : QString()); m_ui.MidiIListView->setClientName( m_ui.MidiIClientsComboBox->currentIndex() > 0 && !bClear ? m_ui.MidiIClientsComboBox->currentText() : QString()); m_pMidiConnect->updateContents(bClear); updateClientsComboBox(m_ui.MidiOClientsComboBox, m_ui.MidiOListView, m_pMidiConnect->icon(qtractorMidiConnect::ClientOut)); updateClientsComboBox(m_ui.MidiIClientsComboBox, m_ui.MidiIListView, m_pMidiConnect->icon(qtractorMidiConnect::ClientIn)); midiStabilize(); } // Refresh complete form conditionally. void qtractorConnectForm::midiReset (void) { midiUpdate( m_ui.MidiOClientsComboBox->currentIndex() > 0 || m_ui.MidiIClientsComboBox->currentIndex() > 0); } // A helper connection change slot. void qtractorConnectForm::midiConnectChanged (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::midiConectChanged()"); #endif midiStabilize(); emit connectChanged(); } // A helper stabilization slot. void qtractorConnectForm::midiStabilize (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnectForm::midiStabilize()"); #endif m_ui.MidiConnectPushButton->setEnabled( m_pMidiConnect->canConnectSelected()); m_ui.MidiDisconnectPushButton->setEnabled( m_pMidiConnect->canDisconnectSelected()); m_ui.MidiDisconnectAllPushButton->setEnabled( m_pMidiConnect->canDisconnectAll()); } // Refresh given combo-box with client names. void qtractorConnectForm::updateClientsComboBox ( QComboBox *pComboBox, qtractorClientListView *pClientListView, const QIcon& icon ) { // Refresh client names combo box contents... pComboBox->clear(); pComboBox->addItem(tr("(All)")); const QStringList& clientNames = pClientListView->clientNames(); QStringListIterator iter(clientNames); while (iter.hasNext()) pComboBox->addItem(icon, iter.next()); // Update current item/selection... const QString& sClientName = pClientListView->clientName(); if (sClientName.isEmpty()) { pComboBox->setCurrentIndex(0); } else { // Select and expand all else... pComboBox->setCurrentIndex(pComboBox->findText(sClientName)); // Select and expand all else... qtractorClientListItem *pClientItem = pClientListView->findClientItem(sClientName); if (pClientItem) pClientListView->setCurrentItem(pClientItem); pClientListView->setOpenAll(true); } } // end of qtractorConnectForm.cpp qtractor-1.5.9/src/PaxHeaders/qtractorThumbView.h0000644000000000000000000000013215101070305017065 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorThumbView.h0000644000175000001440000000523315101070305017060 0ustar00rncbcusers// qtractorThumbView.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 __qtractorThumbView_h #define __qtractorThumbView_h #include // Forward declarations. class qtractorRubberBand; class QPaintEvent; class QResizeEvent; class QMouseEvent; class QKeyEvent; //------------------------------------------------------------------------- // qtractorThumbView -- Session track line thumb view. class qtractorThumbView : public QFrame { Q_OBJECT public: // Constructor. qtractorThumbView(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); // Session track-line paint method. void paintEvent(QPaintEvent *pPaintEvent); // Session track-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 double-buffering pixmap. QPixmap m_pixmap; // Local contents length (in frames). unsigned long m_iContentsLength; // 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 // __qtractorThumbView_h // end of qtractorThumbView.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiSysex.h0000644000000000000000000000013215101070305017071 xustar0030 mtime=1761898693.083267642 30 atime=1761898693.083267642 30 ctime=1761898693.083267642 qtractor-1.5.9/src/qtractorMidiSysex.h0000644000175000001440000000664615101070305017075 0ustar00rncbcusers// qtractorMidiSysex.h // /**************************************************************************** Copyright (C) 2005-2012, rncbc aka Rui Nuno Capela. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #ifndef __qtractorMidiSysex_h #define __qtractorMidiSysex_h #include #include #include //---------------------------------------------------------------------- // class qtractorMidiSysex -- MIDI SysEx data bank item. // class qtractorMidiSysex { public: // Constructors qtractorMidiSysex(const QString& sName, unsigned char *pSysex, unsigned short iSysex) : m_sName(sName), m_pSysex(nullptr), m_iSysex(0) { setData(pSysex, iSysex); } qtractorMidiSysex(const QString& sName, const QString& sText) : m_sName(sName), m_pSysex(nullptr), m_iSysex(0) { setText(sText); } // Copy consructor. qtractorMidiSysex(const qtractorMidiSysex& sysex) : m_sName(sysex.name()), m_pSysex(nullptr), m_iSysex(0) { setData(sysex.data(), sysex.size()); } // Destructors; ~qtractorMidiSysex() { clear(); } // Name key accessors. void setName(const QString& sName) { m_sName = sName; } const QString& name() const { return m_sName; } // Binary data accessors. void setData(unsigned char *pSysex, unsigned short iSysex) { if (pSysex && iSysex > 0 && pSysex[0] == 0xf0 && pSysex[iSysex - 1] == 0xf7) { if (m_pSysex) delete [] m_pSysex; m_iSysex = iSysex; m_pSysex = new unsigned char [iSysex]; ::memcpy(m_pSysex, pSysex, iSysex); } } unsigned char *data() const { return m_pSysex; } unsigned short size() const { return m_iSysex; } // Text(hex) data accessors. void setText(const QString& sText) { const QByteArray& data = QByteArray::fromHex(sText.toLatin1()); setData((unsigned char *) data.data(), data.size()); } QString text() const { QString sText; char hex[4]; for (unsigned short i = 0; i < m_iSysex; ++i) { ::snprintf(hex, sizeof(hex), "%02x", m_pSysex[i]); sText += hex; if (i < m_iSysex - 1) sText += ' '; } return sText; } // Cleanup. void clear() { m_sName.clear(); if (m_pSysex) { delete [] m_pSysex; m_pSysex = nullptr; m_iSysex = 0; } } private: // Instance variables. QString m_sName; unsigned char *m_pSysex; unsigned short m_iSysex; }; //---------------------------------------------------------------------- // class qtractorMidiSysexList -- MIDI SysEx data bank list. // class qtractorMidiSysexList : public QList { public: // Destructor. ~qtractorMidiSysexList() { clear(); } // Destroyer. void clear() { qDeleteAll(*this); QList::clear(); } }; #endif // __qtractorMidiSysex_h // end of qtractorMidiSysex.h qtractor-1.5.9/src/PaxHeaders/qtractorActionControl.h0000644000000000000000000000013215101070305017731 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.062554172 30 ctime=1761898693.062554172 qtractor-1.5.9/src/qtractorActionControl.h0000644000175000001440000000501215101070305017717 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.9/src/PaxHeaders/qtractorOptions.cpp0000644000000000000000000000013215101070305017141 xustar0030 mtime=1761898693.085267648 30 atime=1761898693.085267648 30 ctime=1761898693.085267648 qtractor-1.5.9/src/qtractorOptions.cpp0000644000175000001440000014030015101070305017127 0ustar00rncbcusers// qtractorOptions.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 "qtractorOptions.h" #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) #include #include #if defined(Q_OS_WINDOWS) #include #endif #endif #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include #endif // Supposed to be determinant as default audio file type // (effective only for capture/record) #ifdef CONFIG_LIBVORBIS_0 #define AUDIO_DEFAULT_EXT "ogg" #else #define AUDIO_DEFAULT_EXT "wav" #endif //------------------------------------------------------------------------- // qtractorOptions - Prototype settings structure (pseudo-singleton). // // Singleton instance pointer. qtractorOptions *qtractorOptions::g_pOptions = nullptr; // Singleton instance accessor (static). qtractorOptions *qtractorOptions::getInstance (void) { return g_pOptions; } // Constructor. qtractorOptions::qtractorOptions (void) : m_settings(QTRACTOR_DOMAIN, QTRACTOR_TITLE) { // Pseudo-singleton reference setup. g_pOptions = this; loadOptions(); } // Default Destructor. qtractorOptions::~qtractorOptions (void) { saveOptions(); // Pseudo-singleton reference shut-down. g_pOptions = nullptr; } // Explicit load method. void qtractorOptions::loadOptions (void) { // And go into general options group. m_settings.beginGroup("/Options"); // Load display options... m_settings.beginGroup("/Display"); sMessagesFont = m_settings.value("/MessagesFont").toString(); bMessagesLimit = m_settings.value("/MessagesLimit", true).toBool(); iMessagesLimitLines = m_settings.value("/MessagesLimitLines", 1000).toInt(); bConfirmRemove = m_settings.value("/ConfirmRemove", true).toBool(); bConfirmArchive = m_settings.value("/ConfirmArchive", true).toBool(); bStdoutCapture = m_settings.value("/StdoutCapture", true).toBool(); bCompletePath = m_settings.value("/CompletePath", true).toBool(); bPeakAutoRemove = m_settings.value("/PeakAutoRemove", true).toBool(); bKeepToolsOnTop = m_settings.value("/KeepToolsOnTop", true).toBool(); bKeepEditorsOnTop = m_settings.value("/KeepEditorsOnTop", false).toBool(); iDisplayFormat = m_settings.value("/DisplayFormat", 1).toInt(); iMaxRecentFiles = m_settings.value("/MaxRecentFiles", 5).toInt(); iBaseFontSize = m_settings.value("/BaseFontSize", 0).toInt(); m_settings.endGroup(); // Load logging options... m_settings.beginGroup("/Logging"); bMessagesLog = m_settings.value("/MessagesLog", false).toBool(); sMessagesLogPath = m_settings.value("/MessagesLogPath", "qtractor.log").toString(); m_settings.endGroup(); // And go into view options group. m_settings.beginGroup("/View"); bMenubar = m_settings.value("/Menubar", true).toBool(); bStatusbar = m_settings.value("/Statusbar", true).toBool(); bFileToolbar = m_settings.value("/FileToolbar", true).toBool(); bEditToolbar = m_settings.value("/EditToolbar", true).toBool(); bTrackToolbar = m_settings.value("/TrackToolbar", true).toBool(); bViewToolbar = m_settings.value("/ViewToolbar", true).toBool(); bOptionsToolbar = m_settings.value("/OptionsToolbar", true).toBool(); bTransportToolbar = m_settings.value("/TransportToolbar", true).toBool(); bTimeToolbar = m_settings.value("/TimeToolbar", true).toBool(); bThumbToolbar = m_settings.value("/ThumbToolbar", true).toBool(); iZoomMode = m_settings.value("/ZoomMode", 1).toInt(); m_settings.endGroup(); // Transport options group. m_settings.beginGroup("/Transport"); bCountIn = m_settings.value("/CountIn", false).toBool(); bMetronome = m_settings.value("/Metronome", false).toBool(); bFollowPlayhead = m_settings.value("/FollowPlayhead", true).toBool(); bAutoBackward = m_settings.value("/AutoBackward", false).toBool(); bContinuePastEnd = m_settings.value("/ContinuePastEnd", true).toBool(); iTransportMode = m_settings.value("/TransportMode", 3).toInt(); bTimebase = m_settings.value("/Timebase", true).toBool(); m_settings.endGroup(); // Audio rendering options group. m_settings.beginGroup("/Audio"); sAudioCaptureExt = m_settings.value("/CaptureExt", AUDIO_DEFAULT_EXT).toString(); iAudioCaptureType = m_settings.value("/CaptureType", 0).toInt(); iAudioCaptureFormat = m_settings.value("/CaptureFormat", 0).toInt(); iAudioCaptureQuality = m_settings.value("/CaptureQuality", 4).toInt(); sAudioExportExt = m_settings.value("/ExportExt").toString(); iAudioExportType = m_settings.value("/ExportType", -1).toInt(); iAudioExportFormat = m_settings.value("/ExportFormat", -1).toInt(); iAudioExportQuality = m_settings.value("/ExportQuality", -1).toInt(); iAudioResampleType = m_settings.value("/ResampleType", 2).toInt(); bAudioAutoTimeStretch = m_settings.value("/AutoTimeStretch", false).toBool(); bAudioWsolaTimeStretch = m_settings.value("/WsolaTimeStretch", true).toBool(); bAudioWsolaQuickSeek = m_settings.value("/WsolaQuickSeek", false).toBool(); bAudioRubberBandFormant = m_settings.value("/RubberBandFormant", false).toBool(); bAudioRubberBandFinerR3 = m_settings.value("/RubberBandFinerR3", false).toBool(); bAudioPlayerBus = m_settings.value("/PlayerBus", false).toBool(); bAudioMetroBus = m_settings.value("/MetroBus", false).toBool(); bAudioMetronome = m_settings.value("/Metronome", false).toBool(); iAudioCountInMode = m_settings.value("/CountInMode", 0).toInt(); iAudioCountInBeats = m_settings.value("/CountInBeats", 4).toInt(); bAudioMasterAutoConnect = m_settings.value("/MasterAutoConnect", true).toBool(); bAudioPlayerAutoConnect = m_settings.value("/PlayerAutoConnect", true).toBool(); bAudioMetroAutoConnect = m_settings.value("/MetroAutoConnect", true).toBool(); bAudioSelfConnected = m_settings.value("/SelfConnected", true).toBool(); iAudioMetroOffset = (unsigned long) m_settings.value("/MetroOffset", 0).toUInt(); m_settings.endGroup(); // MIDI rendering options group. m_settings.beginGroup("/Midi"); iMidiCaptureFormat = m_settings.value("/CaptureFormat", 1).toInt(); iMidiExportFormat = m_settings.value("/ExportFormat", 1).toInt(); iMidiCaptureQuantize = m_settings.value("/CaptureQuantize", 0).toInt(); iMidiQueueTimer = m_settings.value("/QueueTimer", 0).toInt(); bMidiDriftCorrect = m_settings.value("/DriftCorrect", true).toBool(); bMidiPlayerBus = m_settings.value("/PlayerBus", false).toBool(); bMidiControlBus = m_settings.value("/ControlBus", false).toBool(); bMidiMetroBus = m_settings.value("/MetroBus", false).toBool(); bMidiMetronome = m_settings.value("/Metronome", true).toBool(); iMidiCountInMode = m_settings.value("/CountInMode", 0).toInt(); iMidiCountInBeats = m_settings.value("/CountInBeats", 4).toInt(); iMidiMetroOffset = m_settings.value("/MetroOffset", 0).toInt(); iMidiMmcDevice = m_settings.value("/MmcDevice", 0x7f).toInt(); iMidiMmcMode = m_settings.value("/MmcMode", 3).toInt(); iMidiSppMode = m_settings.value("/SppMode", 3).toInt(); iMidiClockMode = m_settings.value("/ClockMode", 0).toInt(); bMidiResetAllControllers = m_settings.value("/ResetAllControllers", false).toBool(); m_settings.endGroup(); // HACK: Correct MIDI Clock mode (avoid Duplex...) if (iMidiClockMode > 2) iMidiClockMode = 0; // Metronome options group. m_settings.beginGroup("/Metronome"); // Audio metronome... sMetroBarFilename = m_settings.value("/BarFilename").toString(); fMetroBarGain = float(m_settings.value("/BarGain", 1.0).toDouble()); sMetroBeatFilename = m_settings.value("/BeatFilename").toString(); fMetroBeatGain = float(m_settings.value("/BeatGain", 1.0).toDouble()); // MIDI metronome... iMetroChannel = m_settings.value("/Channel", 9).toInt(); iMetroBarNote = m_settings.value("/BarNote", 76).toInt(); iMetroBarVelocity = m_settings.value("/BarVelocity", 96).toInt(); iMetroBarDuration = m_settings.value("/BarDuration", 48).toInt(); iMetroBeatNote = m_settings.value("/BeatNote", 77).toInt(); iMetroBeatVelocity = m_settings.value("/BeatVelocity", 64).toInt(); iMetroBeatDuration = m_settings.value("/BeatDuration", 24).toInt(); m_settings.endGroup(); m_settings.endGroup(); // Options group. // Last but not least, get the defaults. m_settings.beginGroup("/Default"); sSessionExt = m_settings.value("/SessionExt", "qtr").toString(); bSessionTemplate = m_settings.value("/SessionTemplate", false).toBool(); sSessionTemplatePath = m_settings.value("/SessionTemplatePath").toString(); bSessionBackup = m_settings.value("/SessionBackup", false).toBool(); iSessionBackupMode = m_settings.value("/SessionBackupMode", 0).toInt(); sSessionDir = m_settings.value("/SessionDir").toString(); sAudioDir = m_settings.value("/AudioDir").toString(); sMidiDir = m_settings.value("/MidiDir").toString(); sPresetDir = m_settings.value("/PresetDir").toString(); sInstrumentDir = m_settings.value("/InstrumentDir").toString(); sMidiControlDir = m_settings.value("/MidiControlDir").toString(); sMidiSysexDir = m_settings.value("/MidiSysexDir").toString(); sPluginsDir = m_settings.value("/PluginsDir").toString(); bAutoMonitor = m_settings.value("/AutoMonitor", true).toBool(); bAutoDeactivate = m_settings.value("/AutoDeactivate", false).toBool(); iSnapPerBeat = m_settings.value("/SnapPerBeat", 4).toInt(); fTempo = float(m_settings.value("/Tempo", 120.0).toDouble()); iBeatsPerBar = m_settings.value("/BeatsPerBar", 4).toInt(); iBeatDivisor = m_settings.value("/BeatDivisor", 2).toInt(); iLoopRecordingMode = m_settings.value("/LoopRecordingMode", 0).toInt(); bAutoSessionDir = m_settings.value("/AutoSessionDir", true).toBool(); iPasteRepeatCount = m_settings.value("/PasteRepeatCount", 2).toInt(); bPasteRepeatPeriod = m_settings.value("/PasteRepeatPeriod", false).toInt(); sPluginSearch = m_settings.value("/PluginSearch").toString(); iPluginType = m_settings.value("/PluginType", 1).toInt(); bPluginActivate = true;//m_settings.value("/PluginActivate", true).toBool(); iCurveMode = m_settings.value("/CurveMode", 0).toInt(); iEditRangeOptions = m_settings.value("/EditRangeOptions", 3).toInt(); bShiftKeyModifier = m_settings.value("/ShiftKeyModifier", false).toBool(); bMidButtonModifier = m_settings.value("/MidButtonModifier", false).toBool(); bMidiControlSync = m_settings.value("/MidiControlSync", false).toBool(); iExportRangeType = m_settings.value("/ExportRangeType", 0).toInt(); iExportRangeStart = (unsigned long) m_settings.value("/ExportRangeStart", 0).toUInt(); iExportRangeEnd = (unsigned long) m_settings.value("/ExportRangeEnd", 0).toUInt(); bExportAddTrack = m_settings.value("/ExportAddTrack", false).toBool(); sMarkerColor = m_settings.value("/MarkerColor").toString(); sCurveColor = m_settings.value("/CurveColor").toString(); bAutoBackgroundColor = m_settings.value("/AutoBackgroundColor", false).toBool(); m_settings.endGroup(); // Session auto-save group. m_settings.beginGroup("/AutoSave"); bAutoSaveEnabled = m_settings.value("/Enabled", true).toBool(); iAutoSavePeriod = m_settings.value("/Period", 10).toInt(); sAutoSavePathname = m_settings.value("/Pathname").toString(); sAutoSaveFilename = m_settings.value("/Filename").toString(); m_settings.endGroup(); // Plug-in paths. m_settings.beginGroup("/Plugins"); ladspaPaths = m_settings.value("/LadspaPaths").toStringList(); dssiPaths = m_settings.value("/DssiPaths").toStringList(); vst2Paths = m_settings.value("/Vst2Paths").toStringList(); vst3Paths = m_settings.value("/Vst3Paths").toStringList(); clapPaths = m_settings.value("/ClapPaths").toStringList(); lv2Paths = m_settings.value("/Lv2Paths").toStringList(); sLv2PresetDir = m_settings.value("/Lv2PresetDir").toString(); bAudioOutputBus = m_settings.value("/AudioOutputBus", false).toBool(); bAudioOutputAutoConnect = m_settings.value("/AudioOutputAutoConnect", true).toBool(); bOpenEditor = m_settings.value("/OpenEditor", true).toBool(); bQueryEditorType = m_settings.value("/QueryEditorType", false).toBool(); iDummyLadspaHash = m_settings.value("/DummyLadspaHash", 0).toInt(); iDummyDssiHash = m_settings.value("/DummyDssiHash", 0).toInt(); iDummyVst2Hash = m_settings.value("/DummyVst2Hash", 0).toInt(); iDummyVst3Hash = m_settings.value("/DummyVst3Hash", 0).toInt(); iDummyClapHash = m_settings.value("/DummyClapHash", 0).toInt(); iDummyLv2Hash = m_settings.value("/DummyLv2Hash", 0).toInt(); m_settings.endGroup(); // Instrument file list. const QString sFilePrefix = "/File%1"; int iFile = 0; instrumentFiles.clear(); m_settings.beginGroup("/InstrumentFiles"); for (;;) { QString sFilename = m_settings.value( sFilePrefix.arg(++iFile)).toString(); if (sFilename.isEmpty()) break; instrumentFiles.append(sFilename); } m_settings.endGroup(); // Have some default instrument file(s)... if (sInstrumentDir.isEmpty()) { const QChar sep = QDir::separator(); sInstrumentDir = QApplication::applicationDirPath(); sInstrumentDir.remove(CONFIG_BINDIR); sInstrumentDir.append(CONFIG_DATADIR); sInstrumentDir.append(sep); sInstrumentDir.append(PROJECT_NAME); sInstrumentDir.append(sep); sInstrumentDir.append("instruments"); const QFileInfo ins(sInstrumentDir, "Standard1.ins"); if (ins.isFile() && ins.isReadable()) instrumentFiles.append(ins.absoluteFilePath()); } // MIDI controller file list. iFile = 0; midiControlFiles.clear(); m_settings.beginGroup("/MidiControlFiles"); for (;;) { QString sFilename = m_settings.value( sFilePrefix.arg(++iFile)).toString(); if (sFilename.isEmpty()) break; midiControlFiles.append(sFilename); } m_settings.endGroup(); // Recent file list. iFile = 0; recentFiles.clear(); m_settings.beginGroup("/RecentFiles"); while (iFile < iMaxRecentFiles) { QString sFilename = m_settings.value( sFilePrefix.arg(++iFile)).toString(); if (sFilename.isEmpty()) break; recentFiles.append(sFilename); } m_settings.endGroup(); // Tracks widget settings. m_settings.beginGroup("/Tracks"); iTrackViewSelectMode = m_settings.value("/TrackViewSelectMode", 0).toInt(); bTrackViewDropSpan = m_settings.value("/TrackViewDropSpan", false).toBool(); bTrackViewSnapZebra = m_settings.value("/TrackViewSnapZebra", true).toBool(); bTrackViewSnapGrid = m_settings.value("/TrackViewSnapGrid", true).toBool(); bTrackViewToolTips = m_settings.value("/TrackViewToolTips", true).toBool(); bTrackViewCurveEdit = m_settings.value("/TrackViewCurveEdit", false).toBool(); iTrackColorSaturation = m_settings.value("/TrackColorSaturation", 100).toInt(); m_settings.endGroup(); // MIDI options group. m_settings.beginGroup("/MidiEditor"); m_settings.beginGroup("/View"); bMidiMenubar = m_settings.value("/Menubar", true).toBool(); bMidiStatusbar = m_settings.value("/Statusbar", true).toBool(); bMidiFileToolbar = m_settings.value("/FileToolbar", true).toBool(); bMidiEditToolbar = m_settings.value("/EditToolbar", true).toBool(); bMidiViewToolbar = m_settings.value("/ViewToolbar", true).toBool(); bMidiTransportToolbar = m_settings.value("/TransportToolbar", false).toBool(); bMidiScaleToolbar = m_settings.value("/ScaleToolbar", false).toBool(); bMidiTimeToolbar = m_settings.value("/TimeToolbar", false).toBool(); bMidiThumbToolbar = m_settings.value("/ThumbToolbar", true).toBool(); iMidiDisplayFormat = m_settings.value("/DisplayFormat", 2).toInt(); bMidiNoteNames = m_settings.value("/NoteNames", false).toBool(); bMidiNoteDuration = m_settings.value("/NoteDuration", true).toBool(); bMidiNoteColor = m_settings.value("/NoteColor", false).toBool(); bMidiValueColor = m_settings.value("/ValueColor", false).toBool(); bMidiPreview = m_settings.value("/Preview", true).toBool(); bMidiFollow = m_settings.value("/Follow", false).toBool(); bMidiEditMode = m_settings.value("/EditMode", false).toBool(); bMidiEditModeDraw = m_settings.value("/EditModeDraw", false).toBool(); iMidiZoomMode = m_settings.value("/ZoomMode", 3).toInt(); iMidiHorizontalZoom = m_settings.value("/HorizontalZoom", 100).toInt(); iMidiVerticalZoom = m_settings.value("/VerticalZoom", 100).toInt(); iMidiSnapPerBeat = m_settings.value("/SnapPerBeat", 4).toInt(); bMidiSnapZebra = m_settings.value("/SnapZebra", false).toBool(); bMidiSnapGrid = m_settings.value("/SnapGrid", false).toBool(); bMidiToolTips = m_settings.value("/ToolTips", true).toBool(); iMidiViewType = m_settings.value("/ViewType", 0).toInt(); iMidiEventType = m_settings.value("/EventType", 0).toInt(); iMidiEventParam = m_settings.value("/EventParam", 0).toInt(); iMidiSnapToScaleKey = m_settings.value("/SnapToScaleKey", 0).toInt(); iMidiSnapToScaleType = m_settings.value("/SnapToScaleType", 0).toInt(); m_settings.endGroup(); m_settings.endGroup(); // MidiEditor // User preference options. m_settings.beginGroup("/Preferences"); // Meter colors. m_settings.beginGroup("/Colors"); audioMeterColors = m_settings.value("/AudioMeter").toStringList(); midiMeterColors = m_settings.value("/MidiMeter").toStringList(); m_settings.endGroup(); // Transport view options. m_settings.beginGroup("/Transport"); bSyncViewHold = m_settings.value("/SyncViewHold", false).toBool(); m_settings.endGroup(); // Run-time special semi/non-persistent options. m_settings.beginGroup("/Dialogs"); bUseNativeDialogs = m_settings.value("/UseNativeDialogs", false).toBool(); bDontUseNativeDialogs = !bUseNativeDialogs; m_settings.endGroup(); // Custom display options. m_settings.beginGroup("/Custom"); sCustomColorTheme = m_settings.value("/ColorTheme").toString(); sCustomStyleTheme = m_settings.value("/StyleTheme").toString(); sCustomStyleSheet = m_settings.value("/StyleSheet").toString(); sCustomIconsTheme = m_settings.value("/IconsTheme").toString(); m_settings.endGroup(); m_settings.endGroup(); // Preferences } // Explicit save method. void qtractorOptions::saveOptions (void) { // Make program version available in the future. m_settings.beginGroup("/Program"); m_settings.setValue("/Version", PROJECT_VERSION); m_settings.endGroup(); // And go into general options group. m_settings.beginGroup("/Options"); // Save display options. m_settings.beginGroup("/Display"); m_settings.setValue("/MessagesFont", sMessagesFont); m_settings.setValue("/MessagesLimit", bMessagesLimit); m_settings.setValue("/MessagesLimitLines", iMessagesLimitLines); m_settings.setValue("/ConfirmRemove", bConfirmRemove); m_settings.setValue("/ConfirmArchive", bConfirmArchive); m_settings.setValue("/StdoutCapture", bStdoutCapture); m_settings.setValue("/CompletePath", bCompletePath); m_settings.setValue("/PeakAutoRemove", bPeakAutoRemove); m_settings.setValue("/KeepToolsOnTop", bKeepToolsOnTop); m_settings.setValue("/KeepEditorsOnTop", bKeepEditorsOnTop); m_settings.setValue("/DisplayFormat", iDisplayFormat); m_settings.setValue("/MaxRecentFiles", iMaxRecentFiles); m_settings.setValue("/BaseFontSize", iBaseFontSize); m_settings.endGroup(); // Save logging options... m_settings.beginGroup("/Logging"); m_settings.setValue("/MessagesLog", bMessagesLog); m_settings.setValue("/MessagesLogPath", sMessagesLogPath); m_settings.endGroup(); // View options group. m_settings.beginGroup("/View"); m_settings.setValue("/Menubar", bMenubar); m_settings.setValue("/Statusbar", bStatusbar); m_settings.setValue("/FileToolbar", bFileToolbar); m_settings.setValue("/EditToolbar", bEditToolbar); m_settings.setValue("/TrackToolbar", bTrackToolbar); m_settings.setValue("/ViewToolbar", bViewToolbar); m_settings.setValue("/OptionsToolbar", bOptionsToolbar); m_settings.setValue("/TransportToolbar", bTransportToolbar); m_settings.setValue("/TimeToolbar", bTimeToolbar); m_settings.setValue("/ThumbToolbar", bThumbToolbar); m_settings.setValue("/ZoomMode", iZoomMode); m_settings.endGroup(); // Transport options group. m_settings.beginGroup("/Transport"); m_settings.setValue("/CountIn", bCountIn); m_settings.setValue("/Metronome", bMetronome); m_settings.setValue("/FollowPlayhead", bFollowPlayhead); m_settings.setValue("/AutoBackward", bAutoBackward); m_settings.setValue("/ContinuePastEnd", bContinuePastEnd); m_settings.setValue("/TransportMode", iTransportMode); m_settings.setValue("/Timebase", bTimebase); m_settings.endGroup(); // Audio rendering options group. m_settings.beginGroup("/Audio"); m_settings.setValue("/CaptureExt", sAudioCaptureExt); m_settings.setValue("/CaptureType", iAudioCaptureType); m_settings.setValue("/CaptureFormat", iAudioCaptureFormat); m_settings.setValue("/CaptureQuality", iAudioCaptureQuality); m_settings.setValue("/ExportExt", sAudioExportExt); m_settings.setValue("/ExportType", iAudioExportType); m_settings.setValue("/ExportFormat", iAudioExportFormat); m_settings.setValue("/ExportQuality", iAudioExportQuality); m_settings.setValue("/ResampleType", iAudioResampleType); m_settings.setValue("/AutoTimeStretch", bAudioAutoTimeStretch); m_settings.setValue("/WsolaTimeStretch", bAudioWsolaTimeStretch); m_settings.setValue("/WsolaQuickSeek", bAudioWsolaQuickSeek); m_settings.setValue("/RubberBandFormant", bAudioRubberBandFormant); m_settings.setValue("/RubberBandFinerR3", bAudioRubberBandFinerR3); m_settings.setValue("/PlayerBus", bAudioPlayerBus); m_settings.setValue("/MetroBus", bAudioMetroBus); m_settings.setValue("/Metronome", bAudioMetronome); m_settings.setValue("/CountInMode", iAudioCountInMode); m_settings.setValue("/CountInBeats", iAudioCountInBeats); m_settings.setValue("/MasterAutoConnect", bAudioMasterAutoConnect); m_settings.setValue("/PlayerAutoConnect", bAudioPlayerAutoConnect); m_settings.setValue("/MetroAutoConnect", bAudioMetroAutoConnect); m_settings.setValue("/SelfConnected", bAudioSelfConnected); m_settings.setValue("/MetroOffset", uint(iAudioMetroOffset)); m_settings.endGroup(); // MIDI rendering options group. m_settings.beginGroup("/Midi"); m_settings.setValue("/CaptureFormat", iMidiCaptureFormat); m_settings.setValue("/ExportFormat", iMidiExportFormat); m_settings.setValue("/CaptureQuantize", iMidiCaptureQuantize); m_settings.setValue("/QueueTimer", iMidiQueueTimer); m_settings.setValue("/DriftCorrect", bMidiDriftCorrect); m_settings.setValue("/PlayerBus", bMidiPlayerBus); m_settings.setValue("/ControlBus", bMidiControlBus); m_settings.setValue("/MetroBus", bMidiMetroBus); m_settings.setValue("/Metronome", bMidiMetronome); m_settings.setValue("/CountInMode", iMidiCountInMode); m_settings.setValue("/CountInBeats", iMidiCountInBeats); m_settings.setValue("/MetroOffset", iMidiMetroOffset); m_settings.setValue("/MmcDevice", iMidiMmcDevice); m_settings.setValue("/MmcMode", iMidiMmcMode); m_settings.setValue("/SppMode", iMidiSppMode); m_settings.setValue("/ClockMode", iMidiClockMode); m_settings.setValue("/ResetAllControllers", bMidiResetAllControllers); m_settings.endGroup(); // Metronome options group. m_settings.beginGroup("/Metronome"); // Audio metronome... m_settings.setValue("/BarFilename", sMetroBarFilename); m_settings.setValue("/BarGain", double(fMetroBarGain)); m_settings.setValue("/BeatFilename", sMetroBeatFilename); m_settings.setValue("/BeatGain", double(fMetroBeatGain)); // MIDI metronome... m_settings.setValue("/Channel", iMetroChannel); m_settings.setValue("/BarNote", iMetroBarNote); m_settings.setValue("/BarVelocity", iMetroBarVelocity); m_settings.setValue("/BarDuration", iMetroBarDuration); m_settings.setValue("/BeatNote", iMetroBeatNote); m_settings.setValue("/BeatVelocity", iMetroBeatVelocity); m_settings.setValue("/BeatDuration", iMetroBeatDuration); m_settings.endGroup(); m_settings.endGroup(); // Options group. // Default directories. m_settings.beginGroup("/Default"); m_settings.setValue("/SessionExt", sSessionExt); m_settings.setValue("/SessionTemplate", bSessionTemplate); m_settings.setValue("/SessionTemplatePath", sSessionTemplatePath); m_settings.setValue("/SessionBackup", bSessionBackup); m_settings.setValue("/SessionBackupMode", iSessionBackupMode); m_settings.setValue("/SessionDir", sSessionDir); m_settings.setValue("/AudioDir", sAudioDir); m_settings.setValue("/MidiDir", sMidiDir); m_settings.setValue("/PresetDir", sPresetDir); m_settings.setValue("/InstrumentDir", sInstrumentDir); m_settings.setValue("/MidiControlDir", sMidiControlDir); m_settings.setValue("/MidiSysexDir", sMidiSysexDir); m_settings.setValue("/PluginsDir", sPluginsDir); m_settings.setValue("/AutoMonitor", bAutoMonitor); m_settings.setValue("/AutoDeactivate", bAutoDeactivate); m_settings.setValue("/SnapPerBeat", iSnapPerBeat); m_settings.setValue("/Tempo", double(fTempo)); m_settings.setValue("/BeatsPerBar", iBeatsPerBar); m_settings.setValue("/BeatDivisor", iBeatDivisor); m_settings.setValue("/LoopRecordingMode", iLoopRecordingMode); m_settings.setValue("/AutoSessionDir", bAutoSessionDir); m_settings.setValue("/PasteRepeatCount", iPasteRepeatCount); m_settings.setValue("/PasteRepeatPeriod", bPasteRepeatPeriod); m_settings.setValue("/PluginSearch", sPluginSearch); m_settings.setValue("/PluginType", iPluginType); m_settings.setValue("/PluginActivate", bPluginActivate); m_settings.setValue("/CurveMode", iCurveMode); m_settings.setValue("/EditRangeOptions", iEditRangeOptions); m_settings.setValue("/ShiftKeyModifier", bShiftKeyModifier); m_settings.setValue("/MidButtonModifier", bMidButtonModifier); m_settings.setValue("/MidiControlSync", bMidiControlSync); m_settings.setValue("/ExportRangeType", iExportRangeType); m_settings.setValue("/ExportRangeStart", uint(iExportRangeStart)); m_settings.setValue("/ExportRangeEnd", uint(iExportRangeEnd)); m_settings.setValue("/ExportAddTrack", bExportAddTrack); m_settings.setValue("/MarkerColor", sMarkerColor); m_settings.setValue("/CurveColor", sCurveColor); m_settings.setValue("/AutoBackgroundColor", bAutoBackgroundColor); m_settings.endGroup(); // Session auto-save group. m_settings.beginGroup("/AutoSave"); m_settings.setValue("/Enabled", bAutoSaveEnabled); m_settings.setValue("/Period", iAutoSavePeriod); m_settings.setValue("/Pathname", sAutoSavePathname); m_settings.setValue("/Filename", sAutoSaveFilename); m_settings.endGroup(); // Plug-in paths. m_settings.beginGroup("/Plugins"); m_settings.setValue("/LadspaPaths", ladspaPaths); m_settings.setValue("/DssiPaths", dssiPaths); m_settings.setValue("/Vst2Paths", vst2Paths); m_settings.setValue("/Vst3Paths", vst3Paths); m_settings.setValue("/ClapPaths", clapPaths); m_settings.setValue("/Lv2Paths", lv2Paths); m_settings.setValue("/Lv2PresetDir", sLv2PresetDir); m_settings.setValue("/AudioOutputBus", bAudioOutputBus); m_settings.setValue("/AudioOutputAutoConnect", bAudioOutputAutoConnect); m_settings.setValue("/OpenEditor", bOpenEditor); m_settings.setValue("/QueryEditorType", bQueryEditorType); m_settings.setValue("/DummyLadspaHash", iDummyLadspaHash); m_settings.setValue("/DummyDssiHash", iDummyDssiHash); m_settings.setValue("/DummyVst2Hash", iDummyVst2Hash); m_settings.setValue("/DummyVst3Hash", iDummyVst3Hash); m_settings.setValue("/DummyClapHash", iDummyClapHash); m_settings.setValue("/DummyLv2Hash", iDummyLv2Hash); m_settings.endGroup(); // Instrument file list. const QString sFilePrefix = "/File%1"; int iFile = 0; m_settings.beginGroup("/InstrumentFiles"); QStringListIterator iter1(instrumentFiles); while (iter1.hasNext()) m_settings.setValue(sFilePrefix.arg(++iFile), iter1.next()); // Cleanup old entries, if any... while (!m_settings.value(sFilePrefix.arg(++iFile)).isNull()) m_settings.remove(sFilePrefix.arg(iFile)); m_settings.endGroup(); // MIDI controller file list. iFile = 0; m_settings.beginGroup("/MidiControlFiles"); QStringListIterator iter2(midiControlFiles); while (iter2.hasNext()) m_settings.setValue(sFilePrefix.arg(++iFile), iter2.next()); // Cleanup old entries, if any... while (!m_settings.value(sFilePrefix.arg(++iFile)).isNull()) m_settings.remove(sFilePrefix.arg(iFile)); m_settings.endGroup(); // Recent file list. iFile = 0; m_settings.beginGroup("/RecentFiles"); QStringListIterator iter3(recentFiles); while (iter3.hasNext()) m_settings.setValue(sFilePrefix.arg(++iFile), iter3.next()); m_settings.endGroup(); // Tracks widget settings. m_settings.beginGroup("/Tracks"); m_settings.setValue("/TrackViewSelectMode", iTrackViewSelectMode); m_settings.setValue("/TrackViewDropSpan", bTrackViewDropSpan); m_settings.setValue("/TrackViewSnapZebra", bTrackViewSnapZebra); m_settings.setValue("/TrackViewSnapGrid", bTrackViewSnapGrid); m_settings.setValue("/TrackViewToolTips", bTrackViewToolTips); m_settings.setValue("/TrackViewCurveEdit", bTrackViewCurveEdit); m_settings.setValue("/TrackColorSaturation", iTrackColorSaturation); m_settings.endGroup(); // MIDI Editor options group. m_settings.beginGroup("/MidiEditor"); m_settings.beginGroup("/View"); m_settings.setValue("/Menubar", bMidiMenubar); m_settings.setValue("/Statusbar", bMidiStatusbar); m_settings.setValue("/FileToolbar", bMidiFileToolbar); m_settings.setValue("/EditToolbar", bMidiEditToolbar); m_settings.setValue("/ViewToolbar", bMidiViewToolbar); m_settings.setValue("/TransportToolbar", bMidiTransportToolbar); m_settings.setValue("/TimeToolbar", bMidiTimeToolbar); m_settings.setValue("/ScaleToolbar", bMidiScaleToolbar); m_settings.setValue("/ThumbToolbar", bMidiThumbToolbar); m_settings.setValue("/DisplayFormat", iMidiDisplayFormat); m_settings.setValue("/NoteNames", bMidiNoteNames); m_settings.setValue("/NoteDuration", bMidiNoteDuration); m_settings.setValue("/NoteColor", bMidiNoteColor); m_settings.setValue("/ValueColor", bMidiValueColor); m_settings.setValue("/Preview", bMidiPreview); m_settings.setValue("/Follow", bMidiFollow); m_settings.setValue("/EditMode", bMidiEditMode); m_settings.setValue("/EditModeDraw", bMidiEditModeDraw); m_settings.setValue("/ZoomMode", iMidiZoomMode); m_settings.setValue("/HorizontalZoom", iMidiHorizontalZoom); m_settings.setValue("/VerticalZoom", iMidiVerticalZoom); m_settings.setValue("/SnapPerBeat", iMidiSnapPerBeat); m_settings.setValue("/SnapZebra", bMidiSnapZebra); m_settings.setValue("/SnapGrid", bMidiSnapGrid); m_settings.setValue("/ToolTips", bMidiToolTips); m_settings.setValue("/ViewType", iMidiViewType); m_settings.setValue("/EventType", iMidiEventType); m_settings.setValue("/EventParam", iMidiEventParam); m_settings.setValue("/SnapToScaleKey", iMidiSnapToScaleKey); m_settings.setValue("/SnapToScaleType", iMidiSnapToScaleType); m_settings.endGroup(); m_settings.endGroup(); // MidiEditor // User preference options. m_settings.beginGroup("/Preferences"); // Meter colors. m_settings.beginGroup("/Colors"); m_settings.setValue("/AudioMeter", audioMeterColors); m_settings.setValue("/MidiMeter", midiMeterColors); m_settings.endGroup(); // Transport view options. m_settings.beginGroup("/Transport"); m_settings.setValue("/SyncViewHold", bSyncViewHold); m_settings.endGroup(); // Run-time special semi/non-persistent options. m_settings.beginGroup("/Dialogs"); m_settings.setValue("/UseNativeDialogs", bUseNativeDialogs); m_settings.endGroup(); // Custom display options. m_settings.beginGroup("/Custom"); m_settings.setValue("/ColorTheme", sCustomColorTheme); m_settings.setValue("/StyleTheme", sCustomStyleTheme); m_settings.setValue("/StyleSheet", sCustomStyleSheet); m_settings.setValue("/IconsTheme", sCustomIconsTheme); m_settings.endGroup(); m_settings.endGroup(); // Preferences // Save/commit to disk. m_settings.sync(); } //------------------------------------------------------------------------- // Settings accessor. // QSettings& qtractorOptions::settings (void) { return m_settings; } //------------------------------------------------------------------------- // Command-line argument stuff. // #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) void qtractorOptions::show_error( const QString& msg ) { #if defined(Q_OS_WINDOWS) QMessageBox::information(nullptr, QApplication::applicationName(), msg); #else const QByteArray tmp = msg.toUtf8() + '\n'; ::fputs(tmp.constData(), stderr); #endif } #else // Help about command line options. void qtractorOptions::print_usage ( const QString& arg0 ) { QTextStream out(stderr); const QString sEot = "\n\t"; const QString sEol = "\n\n"; out << QObject::tr("Usage: %1" " [options] [session-file]").arg(arg0) + sEol; out << QTRACTOR_TITLE " - " + QObject::tr(QTRACTOR_SUBTITLE) + sEol; out << QObject::tr("Options:") + sEol; #ifdef CONFIG_JACK_SESSION out << " -s, --session-id=[uuid]" + sEot + QObject::tr("Set session identification (uuid)") + sEol; #endif out << " -h, --help" + sEot + QObject::tr("Show help about command line options") + sEol; out << " -v, --version" + sEot + QObject::tr("Show version information") + sEol; } #endif // Parse command line arguments into m_settings. bool qtractorOptions::parse_args ( const QStringList& args ) { #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) QCommandLineParser parser; parser.setApplicationDescription( QTRACTOR_TITLE " - " + QObject::tr(QTRACTOR_SUBTITLE)); #ifdef CONFIG_JACK_SESSION parser.addOption({{"s", "session-id"}, QObject::tr("Set session identification (uuid)"), "uuid"}); #endif const QCommandLineOption& helpOption = parser.addHelpOption(); const QCommandLineOption& versionOption = parser.addVersionOption(); parser.addPositionalArgument("session-file", QObject::tr("Session file (.qtr)"), QObject::tr("[session-file]")); if (!parser.parse(args)) { show_error(parser.errorText()); return false; } if (parser.isSet(helpOption)) { show_error(parser.helpText()); return false; } if (parser.isSet(versionOption)) { QString sVersion = QString("%1 %2\n") .arg(QTRACTOR_TITLE) .arg(QCoreApplication::applicationVersion()); sVersion += QString("Qt: %1").arg(qVersion()); #if defined(QT_STATIC) sVersion += "-static"; #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) sVersion += ' '; sVersion += '('; sVersion += QApplication::platformName(); sVersion += ')'; #endif sVersion += '\n'; show_error(sVersion); return false; } #ifdef CONFIG_JACK_SESSION if (parser.isSet("session-id")) { const QString& sVal = parser.value("session-id"); if (sVal.isEmpty()) { show_error(QObject::tr("Option -s requires an argument (uuid).")); return false; } sSessionId = sVal; } #endif foreach (const QString& sArg, parser.positionalArguments()) { sessionFiles.append(QFileInfo(sArg).absoluteFilePath()); } #else QTextStream out(stderr); const QString sEol = "\n\n"; const int argc = args.count(); int iCmdArgs = 0; for (int i = 1; i < argc; ++i) { QString sArg = args.at(i); if (iCmdArgs > 0) { sessionFiles.append(QFileInfo(sArg).absoluteFilePath()); ++iCmdArgs; continue; } #ifdef CONFIG_JACK_SESSION QString sVal; int iEqual = sArg.indexOf('='); if (iEqual >= 0) { sVal = sArg.right(sArg.length() - iEqual - 1); sArg = sArg.left(iEqual); } else if (i < argc - 1) { sVal = args.at(i + 1); if (sVal[0] == '-') sVal.clear(); } if (sArg == "-s" || sArg == "--session-id") { if (sVal.isNull()) { out << QObject::tr("Option -s requires an argument (uuid).") + sEol; return false; } sSessionId = sVal; if (iEqual < 0) ++i; } else #endif if (sArg == "-h" || sArg == "--help") { print_usage(args.at(0)); return false; } else if (sArg == "-v" || sArg == "--version") { out << QString("%1: %2\n") .arg(QTRACTOR_TITLE) .arg(PROJECT_VERSION); out << QString("Qt: %1").arg(qVersion()); #if defined(QT_STATIC) out << "-static"; #endif out << '\n'; return false; } else { // If we don't have one by now, // this will be the startup session file... sessionFiles.append(QFileInfo(sArg).absoluteFilePath()); ++iCmdArgs; } } #endif // Alright with argument parsing. return true; } //--------------------------------------------------------------------------- // Widget geometry persistence helper methods. void qtractorOptions::loadWidgetGeometry ( QWidget *pWidget, bool bVisible ) { // Try to restore old form window positioning. if (pWidget) { // if (bVisible) pWidget->show(); -- force initial exposure? m_settings.beginGroup("/Geometry/" + pWidget->objectName()); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) const QByteArray& geometry = m_settings.value("/geometry").toByteArray(); if (geometry.isEmpty()) { QWidget *pParent = pWidget->parentWidget(); if (pParent) pParent = pParent->window(); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (pParent == nullptr) pParent = QApplication::desktop(); #endif if (pParent) { QRect wrect(pWidget->geometry()); wrect.moveCenter(pParent->geometry().center()); pWidget->move(wrect.topLeft()); } } else { pWidget->restoreGeometry(geometry); } #else//--LOAD_OLD_GEOMETRY QPoint wpos; QSize wsize; wpos.setX(m_settings.value("/x", -1).toInt()); wpos.setY(m_settings.value("/y", -1).toInt()); wsize.setWidth(m_settings.value("/width", -1).toInt()); wsize.setHeight(m_settings.value("/height", -1).toInt()); if (wpos.x() > 0 && wpos.y() > 0) pWidget->move(wpos); if (wsize.width() > 0 && wsize.height() > 0) pWidget->resize(wsize); #endif // else // pWidget->adjustSize(); if (!bVisible) bVisible = m_settings.value("/visible", false).toBool(); if (bVisible) pWidget->show(); else pWidget->hide(); m_settings.endGroup(); } } void qtractorOptions::saveWidgetGeometry ( QWidget *pWidget, bool bVisible ) { // Try to save form window position... // (due to X11 window managers ideossincrasies, we better // only save the form geometry while its up and visible) if (pWidget) { m_settings.beginGroup("/Geometry/" + pWidget->objectName()); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) m_settings.setValue("/geometry", pWidget->saveGeometry()); #else//--SAVE_OLD_GEOMETRY const QPoint& wpos = pWidget->pos(); const QSize& wsize = pWidget->size(); m_settings.setValue("/x", wpos.x()); m_settings.setValue("/y", wpos.y()); m_settings.setValue("/width", wsize.width()); m_settings.setValue("/height", wsize.height()); #endif if (!bVisible) bVisible = pWidget->isVisible(); m_settings.setValue("/visible", bVisible); m_settings.endGroup(); } } //--------------------------------------------------------------------------- // Combo box history persistence helper implementation. void qtractorOptions::loadComboBoxHistory ( QComboBox *pComboBox, int iLimit ) { const bool bBlockSignals = pComboBox->blockSignals(true); // Load combobox list from configuration settings file... m_settings.beginGroup("/History/" + pComboBox->objectName()); if (m_settings.childKeys().count() > 0) { pComboBox->setUpdatesEnabled(false); pComboBox->setDuplicatesEnabled(false); pComboBox->clear(); for (int i = 0; i < iLimit; ++i) { const QString& sText = m_settings.value( "/Item" + QString::number(i + 1)).toString(); if (sText.isEmpty()) break; pComboBox->addItem(sText); } pComboBox->setUpdatesEnabled(true); } m_settings.endGroup(); pComboBox->blockSignals(bBlockSignals); } void qtractorOptions::saveComboBoxHistory ( QComboBox *pComboBox, int iLimit ) { const bool bBlockSignals = pComboBox->blockSignals(true); int iCount = pComboBox->count(); // Add current text as latest item (if not blank)... const QString& sCurrentText = pComboBox->currentText(); if (!sCurrentText.isEmpty()) { for (int i = 0; i < iCount; ++i) { const QString& sText = pComboBox->itemText(i); if (sText == sCurrentText) { pComboBox->removeItem(i); --iCount; break; } } pComboBox->insertItem(0, sCurrentText); pComboBox->setCurrentIndex(0); ++iCount; } while (iCount >= iLimit) pComboBox->removeItem(--iCount); // Save combobox list to configuration settings file... m_settings.beginGroup("/History/" + pComboBox->objectName()); for (int i = 0; i < iCount; ++i) { const QString& sText = pComboBox->itemText(i); if (sText.isEmpty()) break; m_settings.setValue("/Item" + QString::number(i + 1), sText); } m_settings.endGroup(); pComboBox->blockSignals(bBlockSignals); } void qtractorOptions::loadComboBoxFileHistory ( QComboBox *pComboBox ) { // Load combobox list from configuration settings file... const bool bBlockSignals = pComboBox->blockSignals(true); m_settings.beginGroup("/History"); const QStringList& files = m_settings.value('/' + pComboBox->objectName()).toStringList(); QStringListIterator iter(files); while (iter.hasNext()) { const QFileInfo info(iter.next()); if (info.exists() && info.isReadable()) { const QString& sPath = info.canonicalFilePath(); pComboBox->insertItem(0, info.fileName(), sPath); } } m_settings.endGroup(); pComboBox->blockSignals(bBlockSignals); } void qtractorOptions::saveComboBoxFileHistory ( QComboBox *pComboBox ) { // Save combobox list to configuration settings file... m_settings.beginGroup("/History"); QStringList files; const int iCount = pComboBox->count(); for (int i = 0; i < iCount; ++i) { const QString& sPath = pComboBox->itemData(i).toString(); if (!sPath.isEmpty()) files.prepend(sPath); } m_settings.setValue('/' + pComboBox->objectName(), files); m_settings.endGroup(); } // Combo box settter/gettter helper prototypes. bool qtractorOptions::setComboBoxCurrentFile ( QComboBox *pComboBox, const QString& sFilename ) { const bool bBlockSignals = pComboBox->blockSignals(true); const QFileInfo info(sFilename); const bool bResult = info.exists() && info.isReadable(); if (bResult) { const QString& sPath = info.canonicalFilePath(); int iIndex = pComboBox->findData(sPath); if (iIndex < 0) { pComboBox->insertItem(0, info.fileName(), sPath); iIndex = 0; } pComboBox->setCurrentIndex(iIndex); pComboBox->setToolTip(sPath); } else { pComboBox->setCurrentIndex(pComboBox->count() - 1); // pComboBox->setToolTip(pComboBox->currentText()); } pComboBox->blockSignals(bBlockSignals); return bResult; } QString qtractorOptions::comboBoxCurrentFile ( QComboBox *pComboBox ) { QString sPath; const int iIndex = pComboBox->currentIndex(); if (iIndex >= 0) sPath = pComboBox->itemData(iIndex).toString(); return sPath; } //--------------------------------------------------------------------------- // Splitter widget sizes persistence helper methods. void qtractorOptions::loadSplitterSizes ( QSplitter *pSplitter, QList& sizes ) { // Try to restore old splitter sizes... if (pSplitter) { m_settings.beginGroup("/Splitter/" + pSplitter->objectName()); QStringList list = m_settings.value("/sizes").toStringList(); if (!list.isEmpty()) { sizes.clear(); QStringListIterator iter(list); while (iter.hasNext()) sizes.append(iter.next().toInt()); } pSplitter->setSizes(sizes); m_settings.endGroup(); } } void qtractorOptions::saveSplitterSizes ( QSplitter *pSplitter ) { // Try to save current splitter sizes... if (pSplitter) { m_settings.beginGroup("/Splitter/" + pSplitter->objectName()); QStringList list; QList sizes = pSplitter->sizes(); QListIterator iter(sizes); while (iter.hasNext()) list.append(QString::number(iter.next())); if (!list.isEmpty()) m_settings.setValue("/sizes", list); m_settings.endGroup(); } } //--------------------------------------------------------------------------- // Action shortcut persistence helper methods. void qtractorOptions::loadActionShortcuts ( QObject *pObject ) { m_settings.beginGroup("/Shortcuts/" + pObject->objectName()); if (m_settings.childKeys().isEmpty()) { m_settings.endGroup(); return; } QList actions = pObject->findChildren (); QListIterator iter(actions); while (iter.hasNext()) { QAction *pAction = iter.next(); if (pAction->objectName().isEmpty()) continue; const QString& sKey = '/' + pAction->objectName(); const QString& sValue = m_settings.value(sKey).toString(); if (sValue.isEmpty() && pAction->shortcut().isEmpty()) continue; pAction->setShortcut(QKeySequence(sValue)); } m_settings.endGroup(); } void qtractorOptions::saveActionShortcuts ( QObject *pObject ) { m_settings.beginGroup("/Shortcuts/" + pObject->objectName()); QList actions = pObject->findChildren (); QListIterator iter(actions); while (iter.hasNext()) { QAction *pAction = iter.next(); if (pAction->objectName().isEmpty()) continue; const QString& sKey = '/' + pAction->objectName(); const QString& sValue = pAction->shortcut().toString(); if (!sValue.isEmpty()) m_settings.setValue(sKey, sValue); else if (m_settings.contains(sKey)) m_settings.remove(sKey); } m_settings.endGroup(); } //--------------------------------------------------------------------------- // Action MIDI observers persistence helper methods. #include "qtractorActionControl.h" void qtractorOptions::loadActionControls ( QObject *pObject ) { qtractorActionControl *pActionControl = qtractorActionControl::getInstance(); if (pActionControl == nullptr) return; qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; pActionControl->clear(); m_settings.beginGroup("/MidiObservers/" + pObject->objectName()); const QStringList& keys = m_settings.childKeys(); QStringListIterator iter(keys); while (iter.hasNext()) { const QString& sObjectName = iter.next(); const QList& actions = pObject->findChildren (sObjectName); if (actions.isEmpty()) continue; const QString& sKey = '/' + sObjectName; const QList& vlist = m_settings.value(sKey).toList(); if (vlist.count() < 6) continue; QAction *pAction = actions.first(); qtractorActionControl::MidiObserver *pMidiObserver = pActionControl->addMidiObserver(pAction); if (pMidiObserver) { pMidiObserver->setType( qtractorMidiControl::typeFromText(vlist.at(0).toString())); pMidiObserver->setChannel(vlist.at(1).toInt()); pMidiObserver->setParam(vlist.at(2).toInt()); pMidiObserver->setFeedback(vlist.at(3).toBool()); pMidiObserver->setInvert(vlist.at(4).toBool()); pMidiObserver->setHook(vlist.at(5).toBool()); pMidiObserver->setLatch(vlist.at(6).toBool()); pMidiControl->mapMidiObserver(pMidiObserver); } } m_settings.endGroup(); } void qtractorOptions::saveActionControls ( QObject *pObject ) { qtractorActionControl *pActionControl = qtractorActionControl::getInstance(); if (pActionControl == nullptr) return; qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; m_settings.beginGroup("/MidiObservers/" + pObject->objectName()); const QStringList& keys = m_settings.childKeys(); QStringListIterator iter(keys); while (iter.hasNext()) { const QString& key = iter.next(); m_settings.remove(key); } const qtractorActionControl::MidiObservers& midiObservers = pActionControl->midiObservers(); qtractorActionControl::MidiObservers::ConstIterator midi_iter = midiObservers.constBegin(); const qtractorActionControl::MidiObservers::ConstIterator midi_end = midiObservers.constEnd(); for ( ; midi_iter != midi_end; ++midi_iter) { QAction *pAction = midi_iter.key(); qtractorActionControl::MidiObserver *pMidiObserver = midi_iter.value(); if (!pMidiControl->isMidiObserverMapped(pMidiObserver)) continue; const QString& sKey = '/' + pAction->objectName(); QList vlist; vlist.append(qtractorMidiControl::textFromType(pMidiObserver->type())); vlist.append(pMidiObserver->channel()); vlist.append(pMidiObserver->param()); vlist.append(pMidiObserver->isFeedback()); vlist.append(pMidiObserver->isInvert()); vlist.append(pMidiObserver->isHook()); vlist.append(pMidiObserver->isLatch()); m_settings.setValue(sKey, vlist); } m_settings.endGroup(); } // end of qtractorOptions.cpp qtractor-1.5.9/src/PaxHeaders/qtractorTrackForm.h0000644000000000000000000000013215101070305017043 xustar0030 mtime=1761898693.090267664 30 atime=1761898693.090267664 30 ctime=1761898693.090267664 qtractor-1.5.9/src/qtractorTrackForm.h0000644000175000001440000001141515101070305017035 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.9/src/PaxHeaders/qtractorTrackList.cpp0000644000000000000000000000013215101070305017406 xustar0030 mtime=1761898693.091267667 30 atime=1761898693.090267664 30 ctime=1761898693.091267667 qtractor-1.5.9/src/qtractorTrackList.cpp0000644000175000001440000015430015101070305017401 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.9/src/PaxHeaders/qtractorMidiRpn.cpp0000644000000000000000000000013215101070305017050 xustar0030 mtime=1761898693.083267642 30 atime=1761898693.083267642 30 ctime=1761898693.083267642 qtractor-1.5.9/src/qtractorMidiRpn.cpp0000644000175000001440000003740715101070305017053 0ustar00rncbcusers// qtractorMidiRpn.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 "qtractorMidiRpn.h" #include #define RPN_MSB 0x65 #define RPN_LSB 0x64 #define NRPN_MSB 0x63 #define NRPN_LSB 0x62 #define DATA_MSB 0x06 #define DATA_LSB 0x26 #define CC14_MSB_MIN 0x00 #define CC14_MSB_MAX 0x20 #define CC14_LSB_MIN CC14_MSB_MAX #define CC14_LSB_MAX (CC14_MSB_MAX << 1) //--------------------------------------------------------------------- // xrpn_data14 - decl. // class xrpn_data14 { public: xrpn_data14() : m_msb(0), m_lsb(0) {} xrpn_data14 ( const xrpn_data14& data ) : m_msb(data.m_msb), m_lsb(data.m_lsb) {} void clear() { m_msb = m_lsb = 0; } bool is_msb () const { return (m_msb & 0x80); } void set_msb ( unsigned char msb ) { m_msb = (msb & 0x7f) | 0x80; } unsigned msb() const { return (m_msb & 0x7f); } bool is_lsb () const { return (m_lsb & 0x80); } void set_lsb ( unsigned char lsb ) { m_lsb = (lsb & 0x7f) | 0x80; } unsigned lsb() const { return (m_lsb & 0x7f); } unsigned short data() const { unsigned short val = 0; if (is_lsb()) { val += (m_lsb & 0x7f); if (is_msb()) val += (m_msb & 0x7f) << 7; } else if (is_msb()) val += (m_msb & 0x7f); return val; } bool is_any() const { return is_msb() || is_lsb(); } bool is_7bit() const { return is_any() && !is_14bit(); } bool is_14bit() const { return is_msb() && is_lsb(); } private: unsigned char m_msb; unsigned char m_lsb; }; //--------------------------------------------------------------------- // xrpn_item - decl. // class xrpn_item { public: xrpn_item() : m_time(0), m_port(0), m_status(0) {} xrpn_item ( const xrpn_item& item ) : m_time(item.m_time), m_port(item.m_port), m_status(item.m_status), m_param(item.m_param), m_value(item.m_value) {} void clear() { m_time = 0; m_port = 0; m_status = 0; m_param.clear(); m_value.clear(); } void set_time(unsigned long time) { m_time = time; } unsigned long time() const { return m_time; } void set_port(int port) { m_port = port; } int port() const { return m_port; } bool is_status () const { return (m_status & 0x80) && (m_status & 0x70); } void set_status(unsigned char status) { m_status = (status & 0x7f) | 0x80; } unsigned char status() const { return (m_status & 0x7f); } qtractorMidiRpn::Type type() const { return qtractorMidiRpn::Type(status() & 0x70); } unsigned short channel() const { return (status() & 0x0f); } bool is_param_msb() const { return m_param.is_msb(); } bool is_param_lsb() const { return m_param.is_lsb(); } void set_param_msb(unsigned char msb) { m_param.set_msb(msb); } void set_param_lsb(unsigned char lsb) { m_param.set_lsb(lsb); } unsigned char param_msb() const { return m_param.msb(); } unsigned char param_lsb() const { return m_param.lsb(); } unsigned short param() const { return m_param.data(); } bool is_value_msb() const { return m_value.is_msb(); } bool is_value_lsb() const { return m_value.is_lsb(); } void set_value_msb(unsigned char msb) { m_value.set_msb(msb); } void set_value_lsb(unsigned char lsb) { m_value.set_lsb(lsb); } unsigned char value_msb() const { return m_value.msb(); } unsigned char value_lsb() const { return m_value.lsb(); } unsigned short value() const { return m_value.data(); } bool is_any() const { return m_param.is_any() || m_value.is_any(); } bool is_ready() const { return m_param.is_any() && m_value.is_any(); } bool is_7bit() const { return m_param.is_any() && m_value.is_7bit(); } bool is_14bit() const { return m_param.is_any() && m_value.is_14bit(); } void clear_value() { m_status &= 0x7f; m_value.clear(); } private: unsigned long m_time; int m_port; unsigned char m_status; xrpn_data14 m_param; xrpn_data14 m_value; }; typedef QHash xrpn_cache; //--------------------------------------------------------------------- // xrpn_queue - decl. // class xrpn_queue { public: xrpn_queue ( unsigned int size = 0 ) : m_size(0), m_mask(0), m_read(0), m_write(0), m_events(0) { resize(size); } ~xrpn_queue () { if (m_events) delete [] m_events; } void resize ( unsigned int size ) { unsigned int new_size = 4; // must be a power-of-2... while (new_size < size) new_size <<= 1; if (new_size > m_size) { const unsigned int old_size = m_size; qtractorMidiRpn::Event *new_events = new qtractorMidiRpn::Event [new_size]; qtractorMidiRpn::Event *old_events = m_events; if (old_events) { if (m_write > m_read) { ::memcpy(new_events + m_read, old_events + m_read, (m_write - m_read) * sizeof(qtractorMidiRpn::Event)); } else if (m_write < m_read) { ::memcpy(new_events + m_read, old_events + m_read, (old_size - m_read) * sizeof(qtractorMidiRpn::Event)); if (m_write > 0) { ::memcpy(new_events + old_size, old_events, m_write * sizeof(qtractorMidiRpn::Event)); } m_write += old_size; } } m_size = new_size; m_mask = new_size - 1; m_events = new_events; if (old_events) delete [] old_events; } } void clear() { m_read = m_write = 0; } bool push ( unsigned 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 push(event); } bool push ( const qtractorMidiRpn::Event& event ) { if (count() >= m_mask) resize(m_size + 4); const unsigned int w = (m_write + 1) & m_mask; if (w == m_read) return false; m_events[m_write] = event; m_write = w; return true; } bool pop ( qtractorMidiRpn::Event& event ) { const unsigned int r = m_read; if (r == m_write) return false; event = m_events[r]; m_read = (r + 1) & m_mask; return true; } bool is_pending () const { return (m_read != m_write); } unsigned int count() const { if (m_write < m_read) return (m_write + m_size - m_read) & m_mask; else return (m_write - m_read); } private: unsigned int m_size; unsigned int m_mask; unsigned int m_read; unsigned int m_write; qtractorMidiRpn::Event *m_events; }; //--------------------------------------------------------------------- // qtractorMidiRpn::Impl - decl. // class qtractorMidiRpn::Impl { public: Impl() : m_count(0) {} bool is_pending () const { return m_queue.is_pending(); } bool dequeue ( qtractorMidiRpn::Event& event ) { return m_queue.pop(event); } void flush() { if (m_count > 0) { xrpn_cache::Iterator iter = m_cache.begin(); const xrpn_cache::Iterator& iter_end = m_cache.end(); for ( ; iter != iter_end; ++iter) enqueue(iter.value()); m_cache.clear(); // m_count = 0; } } bool process ( const qtractorMidiRpn::Event& event ) { const unsigned short channel = (event.status & 0x0f); if (event.param == RPN_MSB) { xrpn_item& item = get_item(event.port, channel); if (item.is_any() && item.type() != qtractorMidiRpn::RPN) enqueue(item); if (item.is_status() // RPN nullptr [MSB] && item.type() == qtractorMidiRpn::RPN && item.is_param_lsb() && item.param_lsb() == 0x7f && event.value == 0x7f) { item.clear(); --m_count; return true; } if (item.type() == qtractorMidiRpn::NRPN) { item.clear(); item.set_status(qtractorMidiRpn::RPN | channel); } else if (!item.is_status()) { item.set_status(qtractorMidiRpn::RPN | channel); ++m_count; } if (item.time() < event.time) item.set_time(event.time); if (item.port() != event.port) item.set_port(event.port); item.set_param_msb(event.value); return true; } else if (event.param == RPN_LSB) { xrpn_item& item = get_item(event.port, channel); if (item.is_any() && item.type() != qtractorMidiRpn::RPN) enqueue(item); if (item.is_status() // RPN nullptr [LSB] && item.type() == qtractorMidiRpn::RPN && item.is_param_msb() && item.param_msb() == 0x7f && event.value == 0x7f) { item.clear(); --m_count; return true; } if (item.type() == qtractorMidiRpn::NRPN) { item.clear(); item.set_status(qtractorMidiRpn::RPN | channel); } else if (!item.is_status()) { item.set_status(qtractorMidiRpn::RPN | channel); ++m_count; } if (item.time() < event.time) item.set_time(event.time); if (item.port() != event.port) item.set_port(event.port); item.set_param_lsb(event.value); return true; } else if (event.param == NRPN_MSB) { xrpn_item& item = get_item(event.port, channel); if (item.is_any() && item.type() != qtractorMidiRpn::NRPN) enqueue(item); if (item.type() == qtractorMidiRpn::RPN) { item.clear(); item.set_status(qtractorMidiRpn::NRPN | channel); } else if (!item.is_status()) { item.set_status(qtractorMidiRpn::NRPN | channel); ++m_count; } if (item.time() < event.time) item.set_time(event.time); if (item.port() != event.port) item.set_port(event.port); item.set_param_msb(event.value); return true; } else if (event.param == NRPN_LSB) { xrpn_item& item = get_item(event.port, channel); if (item.is_any() && item.type() != qtractorMidiRpn::NRPN) enqueue(item); if (item.type() == qtractorMidiRpn::RPN) { item.clear(); item.set_status(qtractorMidiRpn::NRPN | channel); } else if (!item.is_status()) { item.set_status(qtractorMidiRpn::NRPN | channel); ++m_count; } if (item.time() < event.time) item.set_time(event.time); if (item.port() != event.port) item.set_port(event.port); item.set_param_lsb(event.value); return true; } else if (event.param == DATA_MSB) { xrpn_item& item = get_item(event.port, channel); if (item.type() != qtractorMidiRpn::RPN && item.type() != qtractorMidiRpn::NRPN) { enqueue(item); return false; } if (!item.is_status()) item.set_status(item.type() | channel); if (item.time() < event.time) item.set_time(event.time); if (item.port() != event.port) item.set_port(event.port); item.set_value_msb(event.value); if (item.is_14bit()) enqueue(item); return true; } else if (event.param == DATA_LSB) { xrpn_item& item = get_item(event.port, channel); if (item.type() != qtractorMidiRpn::RPN && item.type() != qtractorMidiRpn::NRPN) { enqueue(item); return false; } if (!item.is_status()) item.set_status(item.type() | channel); if (item.time() < event.time) item.set_time(event.time); if (item.port() != event.port) item.set_port(event.port); item.set_value_lsb(event.value); if (item.is_14bit()) enqueue(item); return true; } else if (event.param > CC14_MSB_MIN && event.param < CC14_MSB_MAX) { xrpn_item& item = get_item(event.port, channel); if (item.is_any() && item.type() != qtractorMidiRpn::CC14) { enqueue(item); item.clear(); --m_count; } else if ((item.is_param_msb() && item.is_value_msb()) || (item.type() == qtractorMidiRpn::CC14 && item.is_param_lsb() && item.param_lsb() != event.param + CC14_LSB_MIN)) enqueue(item); if (!item.is_status()) { item.set_status(qtractorMidiRpn::CC14 | channel); ++m_count; } if (item.time() < event.time) item.set_time(event.time); if (item.port() != event.port) item.set_port(event.port); item.set_param_lsb(event.param + CC14_LSB_MIN); item.set_param_msb(event.param); item.set_value_msb(event.value); if (item.is_14bit()) enqueue(item); return true; } else if (event.param > CC14_LSB_MIN && event.param < CC14_LSB_MAX) { xrpn_item& item = get_item(event.port, channel); if (item.is_any() && item.type() != qtractorMidiRpn::CC14) { enqueue(item); item.clear(); --m_count; } else if ((item.is_param_lsb() && item.is_value_lsb()) || (item.type() == qtractorMidiRpn::CC14 && item.is_param_msb() && item.param_msb() != event.param - CC14_LSB_MIN)) enqueue(item); if (!item.is_status()) { item.set_status(qtractorMidiRpn::CC14 | channel); ++m_count; } if (item.time() < event.time) item.set_time(event.time); if (item.port() != event.port) item.set_port(event.port); item.set_param_msb(event.param - CC14_LSB_MIN); item.set_param_lsb(event.param); item.set_value_lsb(event.value); if (item.is_14bit()) enqueue(item); return true; } return false; } protected: xrpn_item& get_item ( int port, unsigned short channel ) { return m_cache[(port << 4) | channel]; } void enqueue ( xrpn_item& item ) { if (!item.is_status()) return; const unsigned long time = item.time(); const int port = item.port(); if (item.type() == qtractorMidiRpn::CC14) { if (item.is_14bit()) { m_queue.push(time, port, item.status(), item.param_msb(), item.value()); const unsigned char value_msb = item.value_msb(); item.clear_value(); item.set_value_msb(value_msb); item.set_time(0); // --m_count; } else { const unsigned char status = qtractorMidiRpn::CC | item.channel(); if (item.is_param_msb() && item.is_value_msb()) m_queue.push(time, port, status, item.param_msb(), item.value_msb()); if (item.is_param_lsb() && item.is_value_lsb()) m_queue.push(time, port, status, item.param_lsb(), item.value_lsb()); item.clear(); --m_count; } } else if (item.is_14bit()) { m_queue.push(time, port, item.status(), item.param(), item.value()); item.clear_value(); item.set_time(0); // --m_count; } else { const unsigned char status = qtractorMidiRpn::CC | item.channel(); if (item.type() == qtractorMidiRpn::RPN) { if (item.is_param_msb()) m_queue.push(time, port, status, RPN_MSB, item.param_msb()); if (item.is_param_lsb()) m_queue.push(time, port, status, RPN_LSB, item.param_lsb()); } else if (item.type() == qtractorMidiRpn::NRPN) { if (item.is_param_msb()) m_queue.push(time, port, status, NRPN_MSB, item.param_msb()); if (item.is_param_lsb()) m_queue.push(time, port, status, NRPN_LSB, item.param_lsb()); } if (item.is_value_msb()) m_queue.push(time, port, status, DATA_MSB, item.value_msb()); if (item.is_value_lsb()) m_queue.push(time, port, status, DATA_LSB, item.value_lsb()); item.clear(); --m_count; } } private: unsigned int m_count; xrpn_cache m_cache; xrpn_queue m_queue; }; //--------------------------------------------------------------------- // qtractorMidiRpn - impl. // qtractorMidiRpn::qtractorMidiRpn (void) { m_pImpl = new qtractorMidiRpn::Impl(); } qtractorMidiRpn::~qtractorMidiRpn (void) { delete m_pImpl; } bool qtractorMidiRpn::isPending (void) const { return m_pImpl->is_pending(); } bool qtractorMidiRpn::process ( const qtractorMidiRpn::Event& event ) { return m_pImpl->process(event); } bool qtractorMidiRpn::dequeue ( qtractorMidiRpn::Event& event ) { return m_pImpl->dequeue(event); } void qtractorMidiRpn::flush (void) { m_pImpl->flush(); } // end of qtractorMidiRpn.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMidiEditTime.h0000644000000000000000000000013215101070305017462 xustar0030 mtime=1761898693.079267629 30 atime=1761898693.079267629 30 ctime=1761898693.079267629 qtractor-1.5.9/src/qtractorMidiEditTime.h0000644000175000001440000000667715101070305017472 0ustar00rncbcusers// qtractorMidiEditTime.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 __qtractorMidiEditTime_h #define __qtractorMidiEditTime_h #include "qtractorScrollView.h" #include "qtractorTimeScale.h" #include // Forward declarations. class qtractorMidiEditor; class QResizeEvent; class QMouseEvent; class QKeyEvent; //---------------------------------------------------------------------------- // qtractorMidiEditTime -- MIDI sequence time scale widget. class qtractorMidiEditTime : public qtractorScrollView { Q_OBJECT public: // Constructor. qtractorMidiEditTime(qtractorMidiEditor *pEditor, QWidget *pParent); // Rectangular contents update. void updateContents(const QRect& rect); // Overall contents update. void updateContents(); protected: // Resize event handler. void resizeEvent(QResizeEvent *pResizeEvent); // Draw the time scale. void drawContents(QPainter *pPainter, const QRect& rect); // Check if some position header is to be dragged... bool dragHeadStart(const QPoint& pos); // Handle item selection with mouse. void mousePressEvent(QMouseEvent *pMouseEvent); void mouseMoveEvent(QMouseEvent *pMouseEvent); void mouseReleaseEvent(QMouseEvent *pMouseEvent); // Tempo-map dialog accessor. void mouseDoubleClickEvent(QMouseEvent *pMouseEvent); // Keyboard event handler. void keyPressEvent(QKeyEvent *pKeyEvent); // Handle zoom with mouse wheel. void wheelEvent(QWheelEvent *pWheelEvent); // Reset drag/select state. void resetDragState(); // Context menu request slot (dummy). void contextMenuEvent(QContextMenuEvent *); // Trap for help/tool-tip events. bool eventFilter(QObject *pObject, QEvent *pEvent); // Show dragging tooltip... void showToolTip(unsigned long iFrame) const; void showToolTip(const QRect& rect) const; // Edit-head/tail positioning... void selectEdit(unsigned long iFrame); protected slots: // To have timeline in h-sync with main track view. void contentsXMovingSlot(int cx, int cy); // (Re)create the time scale pixmap. void updatePixmap(int cx, int cy); private: // The logical parent binding. qtractorMidiEditor *m_pEditor; // Local double-buffering pixmap. QPixmap m_pixmap; // The current selecting/dragging head stuff. enum DragState { DragNone = 0, DragStart, DragSelect, DragPlayHead, DragMarker, DragEditHead, DragEditTail, DragLoopStart, DragLoopEnd, DragPunchIn, DragPunchOut } m_dragState, m_dragCursor; QRect m_rectDrag; QPoint m_posDrag; qtractorTimeScale::Marker *m_pDragMarker; }; #endif // __qtractorMidiEditTime_h // end of qtractorMidiEditTime.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiMonitor.cpp0000644000000000000000000000013215101070305017740 xustar0030 mtime=1761898693.083267642 30 atime=1761898693.083267642 30 ctime=1761898693.083267642 qtractor-1.5.9/src/qtractorMidiMonitor.cpp0000644000175000001440000001427315101070305017737 0ustar00rncbcusers// qtractorMidiMonitor.cpp // /**************************************************************************** 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. *****************************************************************************/ #include "qtractorMidiMonitor.h" #include "qtractorSession.h" #include "qtractorSessionCursor.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" // Module constants. const unsigned int c_iQueueSize = 16; // Must be power of 2; const unsigned int c_iQueueMask = c_iQueueSize - 1; // Singleton variables. unsigned int qtractorMidiMonitor::g_iFrameSlot = 0; unsigned int qtractorMidiMonitor::g_iTimeSlot[2] = { 0, 0 }; unsigned long qtractorMidiMonitor::g_iTimeSplit = 0; //---------------------------------------------------------------------------- // qtractorMidiMonitor -- MIDI monitor bridge value processor. // Constructor. qtractorMidiMonitor::qtractorMidiMonitor ( float fGain, float fPanning ) : qtractorMonitor(fGain, fPanning) { // Allocate actual buffer stuff... m_pQueue = new QueueItem [c_iQueueSize]; // May reset now... reset(); } // Copy constructor. qtractorMidiMonitor::qtractorMidiMonitor ( const qtractorMidiMonitor& monitor ) : qtractorMidiMonitor(monitor.gain(), monitor.panning()) { qtractorMonitor::gainSubject()->setDefaultValue( monitor.m_gainSubject.defaultValue()); qtractorMonitor::panningSubject()->setDefaultValue( monitor.m_panningSubject.defaultValue()); m_iFrameStart = monitor.m_iFrameStart; m_iTimeStart = monitor.m_iTimeStart; } // Destructor. qtractorMidiMonitor::~qtractorMidiMonitor (void) { delete [] m_pQueue; } // Monitor enqueue method. void qtractorMidiMonitor::enqueue ( qtractorMidiEvent::EventType type, unsigned char val, unsigned long tick ) { // Check whether this is a scheduled value... if (m_iTimeStart < tick && g_iTimeSlot[1] > 0) { // Find queue offset index... unsigned int iOffset = (tick - m_iTimeStart) / timeSlot(tick); // FIXME: Ignore outsiders (which would manifest as // out-of-time phantom monitor peak values...) if (iOffset > c_iQueueMask) iOffset = c_iQueueMask; const unsigned int iIndex = (m_iQueueIndex + iOffset) & c_iQueueMask; // Set the value in buffer... QueueItem& item = m_pQueue[iIndex]; if (item.value < val && type == qtractorMidiEvent::NOTEON) item.value = val; // Increment enqueued count. ++(item.count); // Done enqueueing. } else { // Alternative is sending it directly // as a non-enqueued direct value... if (m_item.value < val && type == qtractorMidiEvent::NOTEON) m_item.value = val; // Increment direct count. ++(m_item.count); // Done direct. } } // Monitor value dequeue method. float qtractorMidiMonitor::value_stamp ( unsigned long iStamp ) { // Grab-and-reset current direct value... if (m_iValueStamp != iStamp) { unsigned char val = m_item.value; m_iValueStamp = iStamp; m_item.value = 0; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession && g_iFrameSlot > 0) { // Sweep the queue until current time... const unsigned long iFrameTime = pSession->frameTimeEx(); while (m_iFrameStart < iFrameTime) { QueueItem& item = m_pQueue[m_iQueueIndex]; if (val < item.value) val = item.value; m_item.count += item.count; item.value = 0; item.count = 0; ++m_iQueueIndex &= c_iQueueMask; m_iFrameStart += g_iFrameSlot; m_iTimeStart += timeSlot(m_iTimeStart); } } // New state: same as division by 127.0f... m_fValue = (gain() * val) * 0.007874f; } // Dequeue done. return m_fValue; } // Monitor count dequeue method. int qtractorMidiMonitor::count_stamp ( unsigned long iStamp ) { // Grab latest direct/dequeued count... if (m_iCountStamp != iStamp) { m_iCountStamp = iStamp; m_prev.count = m_item.count; m_item.count = 0; } return int(m_prev.count); } // Clear monitor. void qtractorMidiMonitor::clear (void) { // (Re)initialize all... m_item.value = m_prev.value = 0; m_item.count = m_prev.count = 0; m_iQueueIndex = 0; // Time to reset buffer... for (unsigned int i = 0; i < c_iQueueSize; ++i) { m_pQueue[i].value = 0; m_pQueue[i].count = 0; } // Reset metering stamps as well... m_fValue = 0.0f; m_iValueStamp = 0; m_iCountStamp = 0; } // Reset monitor. void qtractorMidiMonitor::reset (void) { // (Re)initialize all... clear(); // Reset actual frame/time start... m_iFrameStart = 0; m_iTimeStart = 0; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) m_iFrameStart = pSession->frameTimeEx(); } // Update monitor (nothing really done here). void qtractorMidiMonitor::update (void) { // Do nothing yet... } // Singleton time base reset. void qtractorMidiMonitor::resetTime ( qtractorTimeScale *pTimeScale, unsigned long iFrame ) { g_iFrameSlot = (pTimeScale->sampleRate() >> 2); // ~250ms splitTime(pTimeScale, iFrame, 0); } // Singleton time base split (scheduled tempo change) void qtractorMidiMonitor::splitTime ( qtractorTimeScale *pTimeScale, unsigned long iFrame, unsigned long iTime ) { // Reset time references... qtractorTimeScale::Cursor cursor(pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(iFrame); const unsigned long t0 = pNode->tickFromFrame(iFrame); // Time slot: the amount of time (in ticks) // each queue slot will hold scheduled events; g_iTimeSlot[0] = g_iTimeSlot[1]; g_iTimeSlot[1] = pNode->tickFromFrame(iFrame + g_iFrameSlot) - t0; // Relative time where time splits. g_iTimeSplit = iTime; } // end of qtractorMidiMonitor.cpp qtractor-1.5.9/src/PaxHeaders/qtractorClipSelect.h0000644000000000000000000000013215101070305017202 xustar0030 mtime=1761898693.067267591 30 atime=1761898693.067267591 30 ctime=1761898693.067267591 qtractor-1.5.9/src/qtractorClipSelect.h0000644000175000001440000000534315101070305017177 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.9/src/PaxHeaders/qtractorObserver.cpp0000644000000000000000000000013215101070305017275 xustar0030 mtime=1761898693.085267648 30 atime=1761898693.085267648 30 ctime=1761898693.085267648 qtractor-1.5.9/src/qtractorObserver.cpp0000644000175000001440000001124415101070305017267 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.9/src/PaxHeaders/qtractorConnections.h0000644000000000000000000000013215101070305017435 xustar0030 mtime=1761898693.068267594 30 atime=1761898693.068267594 30 ctime=1761898693.068267594 qtractor-1.5.9/src/qtractorConnections.h0000644000175000001440000000413015101070305017423 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.9/src/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215101070305015762 xustar0030 mtime=1761898693.056377872 30 atime=1761898693.056377872 30 ctime=1761898693.056377872 qtractor-1.5.9/src/CMakeLists.txt0000644000175000001440000004116715101070305015763 0ustar00rncbcusers# project (qtractor) set (CMAKE_INCLUDE_CURRENT_DIR ON) set (CMAKE_AUTOUIC ON) set (CMAKE_AUTOMOC ON) set (CMAKE_AUTORCC ON) if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/config.h) file (REMOVE ${CMAKE_CURRENT_SOURCE_DIR}/config.h) endif () configure_file (config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) set (HEADERS qtractor.h qtractorAbout.h qtractorAtomic.h qtractorActionControl.h qtractorAudioBuffer.h qtractorAudioClip.h qtractorAudioConnect.h qtractorAudioEngine.h qtractorAudioFile.h qtractorAudioListView.h qtractorAudioMadFile.h qtractorAudioMeter.h qtractorAudioMonitor.h qtractorAudioPeak.h qtractorAudioSndFile.h qtractorAudioVorbisFile.h qtractorClapPlugin.h qtractorClip.h qtractorClipCommand.h qtractorClipSelect.h qtractorComboBox.h qtractorCommand.h qtractorConnect.h qtractorConnections.h qtractorCtlEvent.h qtractorCurve.h qtractorCurveCommand.h qtractorCurveFile.h qtractorCurveSelect.h qtractorDocument.h qtractorDssiPlugin.h qtractorEngine.h qtractorEngineCommand.h qtractorFileList.h qtractorFileListView.h qtractorFiles.h qtractorFileSystem.h qtractorInsertPlugin.h qtractorInstrument.h qtractorInstrumentMenu.h qtractorLadspaPlugin.h qtractorList.h qtractorLv2Plugin.h qtractorLv2Gtk2Plugin.h qtractorMessageBox.h qtractorMessageList.h qtractorMessages.h qtractorMeter.h qtractorMidiBuffer.h qtractorMidiClip.h qtractorMidiConnect.h qtractorMidiControl.h qtractorMidiControlCommand.h qtractorMidiControlObserver.h qtractorMidiControlTypeGroup.h qtractorMidiControlPluginWidget.h qtractorMidiControlPlugin.h qtractorMidiCursor.h qtractorMidiEditor.h qtractorMidiEditCommand.h qtractorMidiEditEvent.h qtractorMidiEditList.h qtractorMidiEditSelect.h qtractorMidiEditTime.h qtractorMidiEditView.h qtractorMidiEngine.h qtractorMidiEvent.h qtractorMidiEventList.h qtractorMidiFile.h qtractorMidiFileTempo.h qtractorMidiListView.h qtractorMidiManager.h qtractorMidiMeter.h qtractorMidiMonitor.h qtractorMidiRpn.h qtractorMidiSequence.h qtractorMidiSysex.h qtractorMidiThumbView.h qtractorMidiTimer.h qtractorMixer.h qtractorMmcEvent.h qtractorMonitor.h qtractorNsmClient.h qtractorObserver.h qtractorObserverWidget.h qtractorOptions.h qtractorPlugin.h qtractorPluginFactory.h qtractorPluginCommand.h qtractorPluginListView.h qtractorPropertyCommand.h qtractorRingBuffer.h qtractorRubberBand.h qtractorScrollView.h qtractorSession.h qtractorSessionCommand.h qtractorSessionCursor.h qtractorSpinBox.h qtractorThumbView.h qtractorTimeScale.h qtractorTimeScaleCommand.h qtractorTimeStretcher.h qtractorTrack.h qtractorTrackButton.h qtractorTrackCommand.h qtractorTrackList.h qtractorTrackTime.h qtractorTrackView.h qtractorTracks.h qtractorVst2Plugin.h qtractorVst3Plugin.h qtractorZipFile.h qtractorWsolaTimeStretcher.h qtractorAudioIOMatrixForm.h qtractorBusForm.h qtractorClipForm.h qtractorConnectForm.h qtractorEditRangeForm.h qtractorExportForm.h qtractorInstrumentForm.h qtractorMainForm.h qtractorMidiControlForm.h qtractorMidiControlObserverForm.h qtractorMidiEditorForm.h qtractorMidiSysexForm.h qtractorMidiToolsForm.h qtractorOptionsForm.h qtractorPaletteForm.h qtractorPasteRepeatForm.h qtractorPluginForm.h qtractorPluginSelectForm.h qtractorSessionForm.h qtractorShortcutForm.h qtractorTakeRangeForm.h qtractorTempoAdjustForm.h qtractorTimeScaleForm.h qtractorTrackForm.h ) set (SOURCES qtractor.cpp qtractorActionControl.cpp qtractorAudioBuffer.cpp qtractorAudioClip.cpp qtractorAudioConnect.cpp qtractorAudioEngine.cpp qtractorAudioFile.cpp qtractorAudioListView.cpp qtractorAudioMadFile.cpp qtractorAudioMeter.cpp qtractorAudioMonitor.cpp qtractorAudioPeak.cpp qtractorAudioSndFile.cpp qtractorAudioVorbisFile.cpp qtractorClapPlugin.cpp qtractorClip.cpp qtractorClipCommand.cpp qtractorClipSelect.cpp qtractorComboBox.cpp qtractorCommand.cpp qtractorConnect.cpp qtractorConnections.cpp qtractorDocument.cpp qtractorCurve.cpp qtractorCurveCommand.cpp qtractorCurveFile.cpp qtractorCurveSelect.cpp qtractorDssiPlugin.cpp qtractorEngine.cpp qtractorEngineCommand.cpp qtractorFileList.cpp qtractorFileListView.cpp qtractorFiles.cpp qtractorFileSystem.cpp qtractorInsertPlugin.cpp qtractorInstrument.cpp qtractorInstrumentMenu.cpp qtractorLadspaPlugin.cpp qtractorLv2Plugin.cpp qtractorLv2Gtk2Plugin.cpp qtractorMessageBox.cpp qtractorMessageList.cpp qtractorMessages.cpp qtractorMeter.cpp qtractorMidiClip.cpp qtractorMidiConnect.cpp qtractorMidiControl.cpp qtractorMidiControlCommand.cpp qtractorMidiControlObserver.cpp qtractorMidiControlTypeGroup.cpp qtractorMidiControlPluginWidget.cpp qtractorMidiControlPlugin.cpp qtractorMidiCursor.cpp qtractorMidiEditor.cpp qtractorMidiEditCommand.cpp qtractorMidiEditEvent.cpp qtractorMidiEditList.cpp qtractorMidiEditSelect.cpp qtractorMidiEditTime.cpp qtractorMidiEditView.cpp qtractorMidiEngine.cpp qtractorMidiEventList.cpp qtractorMidiFile.cpp qtractorMidiFileTempo.cpp qtractorMidiListView.cpp qtractorMidiManager.cpp qtractorMidiMeter.cpp qtractorMidiMonitor.cpp qtractorMidiRpn.cpp qtractorMidiSequence.cpp qtractorMidiThumbView.cpp qtractorMidiTimer.cpp qtractorMixer.cpp qtractorMmcEvent.cpp qtractorNsmClient.cpp qtractorObserver.cpp qtractorObserverWidget.cpp qtractorOptions.cpp qtractorPlugin.cpp qtractorPluginFactory.cpp qtractorPluginCommand.cpp qtractorPluginListView.cpp qtractorRubberBand.cpp qtractorScrollView.cpp qtractorSession.cpp qtractorSessionCommand.cpp qtractorSessionCursor.cpp qtractorSpinBox.cpp qtractorThumbView.cpp qtractorTimeScale.cpp qtractorTimeScaleCommand.cpp qtractorTimeStretcher.cpp qtractorTrack.cpp qtractorTrackButton.cpp qtractorTrackCommand.cpp qtractorTrackList.cpp qtractorTrackTime.cpp qtractorTrackView.cpp qtractorTracks.cpp qtractorVst2Plugin.cpp qtractorVst3Plugin.cpp qtractorWsolaTimeStretcher.cpp qtractorZipFile.cpp qtractorAudioIOMatrixForm.cpp qtractorBusForm.cpp qtractorClipForm.cpp qtractorConnectForm.cpp qtractorEditRangeForm.cpp qtractorExportForm.cpp qtractorInstrumentForm.cpp qtractorMainForm.cpp qtractorMidiControlForm.cpp qtractorMidiControlObserverForm.cpp qtractorMidiEditorForm.cpp qtractorMidiSysexForm.cpp qtractorMidiToolsForm.cpp qtractorOptionsForm.cpp qtractorPaletteForm.cpp qtractorPasteRepeatForm.cpp qtractorPluginForm.cpp qtractorPluginSelectForm.cpp qtractorSessionForm.cpp qtractorShortcutForm.cpp qtractorTakeRangeForm.cpp qtractorTempoAdjustForm.cpp qtractorTimeScaleForm.cpp qtractorTrackForm.cpp ) set (FORMS qtractorAudioIOMatrixForm.ui qtractorBusForm.ui qtractorClipForm.ui qtractorConnectForm.ui qtractorEditRangeForm.ui qtractorExportForm.ui qtractorInstrumentForm.ui qtractorMainForm.ui qtractorMidiControlForm.ui qtractorMidiControlObserverForm.ui qtractorMidiControlPluginWidget.ui qtractorMidiEditorForm.ui qtractorMidiSysexForm.ui qtractorMidiToolsForm.ui qtractorOptionsForm.ui qtractorPaletteForm.ui qtractorPasteRepeatForm.ui qtractorPluginForm.ui qtractorPluginSelectForm.ui qtractorSessionForm.ui qtractorShortcutForm.ui qtractorTakeRangeForm.ui qtractorTempoAdjustForm.ui qtractorTimeScaleForm.ui qtractorTrackForm.ui ) set (RESOURCES qtractor.qrc ) set (TRANSLATIONS translations/qtractor_cs.ts translations/qtractor_de.ts translations/qtractor_es.ts translations/qtractor_fr.ts translations/qtractor_it.ts translations/qtractor_ja.ts translations/qtractor_pt_BR.ts translations/qtractor_ru.ts translations/qtractor_uk.ts ) if (QT_VERSION VERSION_LESS 5.15.0) qt5_add_translation (QM_FILES ${TRANSLATIONS}) else () qt_add_translation (QM_FILES ${TRANSLATIONS}) endif () add_custom_target (translations ALL DEPENDS ${QM_FILES}) if (WIN32) set (RC_FILE ${CMAKE_CURRENT_SOURCE_DIR}/win32/${PROJECT_NAME}.rc) set (RES_FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.res.obj) find_program (WINDRES_EXECUTABLE NAMES windres mingw32-windres i686-mingw32-windres) if (MINGW) exec_program (${WINDRES_EXECUTABLE} ARGS "-i ${RC_FILE} -o ${RES_FILE} --include-dir=${CMAKE_CURRENT_SOURCE_DIR}/images") list (APPEND SOURCES ${RES_FILE}) else () list (APPEND SOURCES ${RC_FILE}) endif () endif () if (APPLE) set (ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/${PROJECT_NAME}.icns) list (APPEND SOURCES ${ICON_FILE}) set (MACOSX_BUNDLE_ICON_FILE ${PROJECT_NAME}.icns) set_source_files_properties (${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) endif () if (CONFIG_VST3SDK) add_definitions(-DNDEBUG) set (VST3SDK_SOURCES ${CONFIG_VST3SDK}/base/source/baseiids.cpp # ${CONFIG_VST3SDK}/base/source/fstring.cpp ${CONFIG_VST3SDK}/base/source/fobject.cpp ${CONFIG_VST3SDK}/base/thread/source/flock.cpp ${CONFIG_VST3SDK}/public.sdk/source/common/commoniids.cpp ${CONFIG_VST3SDK}/public.sdk/source/vst/vstinitiids.cpp ${CONFIG_VST3SDK}/pluginterfaces/base/coreiids.cpp ${CONFIG_VST3SDK}/pluginterfaces/base/funknown.cpp ) endif () add_executable (${PROJECT_NAME} ${HEADERS} ${SOURCES} ${FORMS} ${RESOURCES} ${VST3SDK_SOURCES} ) # Add some debugger flags. if (CONFIG_DEBUG AND UNIX AND NOT APPLE) set (CONFIG_DEBUG_OPTIONS -g -fsanitize=address -fno-omit-frame-pointer) target_compile_options (${PROJECT_NAME} PRIVATE ${CONFIG_DEBUG_OPTIONS}) target_link_options (${PROJECT_NAME} PRIVATE ${CONFIG_DEBUG_OPTIONS}) endif () add_executable (${PROJECT_NAME}_plugin_scan qtractor_plugin_scan.cpp ${VST3SDK_SOURCES}) set_target_properties (${PROJECT_NAME} PROPERTIES CXX_STANDARD 17) set_target_properties (${PROJECT_NAME}_plugin_scan PROPERTIES CXX_STANDARD 17) if (WIN32) set_target_properties (${PROJECT_NAME} PROPERTIES WIN32_EXECUTABLE true) set_target_properties (${PROJECT_NAME}_plugin_scan PROPERTIES WIN32_EXECUTABLE true) endif () if (APPLE) set_target_properties (${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE true) set_target_properties (${PROJECT_NAME}_plugin_scan PROPERTIES MACOSX_BUNDLE true) endif () target_link_libraries (${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Xml Qt${QT_VERSION_MAJOR}::Svg) target_link_libraries (${PROJECT_NAME}_plugin_scan PRIVATE Qt${QT_VERSION_MAJOR}::Core) if (NOT CONFIG_QT6) target_link_libraries (${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::X11Extras) endif () if (CONFIG_XUNIQUE) target_link_libraries (${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Network) endif () if (UNIX AND NOT APPLE) if (DL_LIBRARY) target_link_libraries (${PROJECT_NAME} PRIVATE ${DL_LIBRARY}) target_link_libraries (${PROJECT_NAME}_plugin_scan PRIVATE ${DL_LIBRARY}) endif () endif () if (CONFIG_LIBJACK) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::JACK) endif () if (CONFIG_LIBASOUND) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::ALSA) endif () if (CONFIG_LIBSNDFILE) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::SNDFILE) endif () if (CONFIG_LIBVORBIS) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::VORBIS) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::VORBISENC) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::VORBISFILE) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::OGG) endif () if (CONFIG_LIBMAD) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::MAD) endif () if (CONFIG_LIBSAMPLERATE) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::SAMPLERATE) endif () if (CONFIG_LIBRUBBERBAND) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::RUBBERBAND) endif () if (CONFIG_LIBAUBIO) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::AUBIO) endif () if (CONFIG_LIBLO) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::LIBLO) endif () if (CONFIG_LIBZ) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::ZLIB) endif () if (CONFIG_LIBLILV) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::LILV) endif () if (CONFIG_LIBSUIL) target_link_libraries (${PROJECT_NAME} PRIVATE PkgConfig::SUIL) endif () if (CONFIG_LV2) target_include_directories (${PROJECT_NAME} PRIVATE ${LV2_INCLUDE_DIRS}) target_link_directories (${PROJECT_NAME} PRIVATE ${LV2_LIBRARY_DIRS}) target_link_libraries (${PROJECT_NAME} PRIVATE ${LV2_LIBRARIES}) target_include_directories (${PROJECT_NAME}_plugin_scan PRIVATE ${LV2_INCLUDE_DIRS}) target_link_directories (${PROJECT_NAME}_plugin_scan PRIVATE ${LV2_LIBRARY_DIRS}) target_link_libraries (${PROJECT_NAME}_plugin_scan PRIVATE ${LV2_LIBRARIES}) endif () if (CONFIG_LV2_UI_GTK2) target_include_directories (${PROJECT_NAME} PRIVATE ${GTK2_INCLUDE_DIRS}) target_link_directories (${PROJECT_NAME} PRIVATE ${GTK2_LIBRARY_DIRS}) target_link_libraries (${PROJECT_NAME} PRIVATE ${GTK2_LIBRARIES}) endif () if (CONFIG_LV2_UI_GTKMM2) target_include_directories (${PROJECT_NAME} PRIVATE ${GTKMM2_INCLUDE_DIRS}) target_link_directories (${PROJECT_NAME} PRIVATE ${GTKMM2_LIBRARY_DIRS}) target_link_libraries (${PROJECT_NAME} PRIVATE ${GTKMM2_LIBRARIES}) endif () if (CONFIG_VST2 AND CONFIG_VST2SDK AND NOT CONFIG_VESTIGE) target_include_directories (${PROJECT_NAME} PRIVATE ${CONFIG_VST2SDK}) target_include_directories (${PROJECT_NAME}_plugin_scan PRIVATE ${CONFIG_VST2SDK}) endif () if (CONFIG_VST3 AND CONFIG_VST3SDK) target_include_directories (${PROJECT_NAME} PRIVATE ${CONFIG_VST3SDK}) target_include_directories (${PROJECT_NAME}_plugin_scan PRIVATE ${CONFIG_VST3SDK}) if (PTHREAD_LIBRARY) target_link_libraries (${PROJECT_NAME} PRIVATE ${PTHREAD_LIBRARY}) target_link_libraries (${PROJECT_NAME}_plugin_scan PRIVATE ${PTHREAD_LIBRARY}) endif () endif () if (CONFIG_CLAP) target_include_directories (${PROJECT_NAME} PRIVATE clap/include) target_include_directories (${PROJECT_NAME}_plugin_scan PRIVATE clap/include) endif () if (XCB_FOUND) target_include_directories (${PROJECT_NAME} PRIVATE ${XCB_INCLUDE_DIRS}) target_link_directories (${PROJECT_NAME} PRIVATE ${XCB_LIBRARY_DIRS}) target_link_libraries (${PROJECT_NAME} PRIVATE ${XCB_LIBRARIES}) endif () if (UNIX AND NOT APPLE) install (TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) install (TARGETS ${PROJECT_NAME}_plugin_scan RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}) install (FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/translations) install (FILES images/${PROJECT_NAME}.png RENAME org.rncbc.${PROJECT_NAME}.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/32x32/apps) install (FILES images/${PROJECT_NAME}.svg RENAME org.rncbc.${PROJECT_NAME}.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps) install (FILES appdata/org.rncbc.${PROJECT_NAME}.desktop DESTINATION ${CMAKE_INSTALL_DATADIR}/applications) install (FILES appdata/org.rncbc.${PROJECT_NAME}.metainfo.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo) install (FILES mimetypes/org.rncbc.${PROJECT_NAME}.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/mime/packages) install (FILES mimetypes/org.rncbc.${PROJECT_NAME}.application-x-${PROJECT_NAME}-session.png mimetypes/org.rncbc.${PROJECT_NAME}.application-x-${PROJECT_NAME}-template.png mimetypes/org.rncbc.${PROJECT_NAME}.application-x-${PROJECT_NAME}-archive.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/32x32/mimetypes) install (FILES mimetypes/org.rncbc.${PROJECT_NAME}.application-x-${PROJECT_NAME}-session.svg mimetypes/org.rncbc.${PROJECT_NAME}.application-x-${PROJECT_NAME}-template.svg mimetypes/org.rncbc.${PROJECT_NAME}.application-x-${PROJECT_NAME}-archive.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/mimetypes) install (FILES man1/${PROJECT_NAME}.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) install (FILES man1/${PROJECT_NAME}.fr.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/fr/man1 RENAME ${PROJECT_NAME}.1) install (FILES audio/metro_bar.wav audio/metro_beat.wav DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/audio) install (FILES instruments/Standard1.ins DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/instruments) install (FILES palette/KXStudio.conf palette/Wonton\ Soup.conf DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/palette) endif () if (WIN32) install (TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) install (TARGETS ${PROJECT_NAME}_plugin_scan RUNTIME DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) install (FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/translations) endif () qtractor-1.5.9/src/PaxHeaders/qtractorEditRangeForm.cpp0000644000000000000000000000013215101070305020174 xustar0030 mtime=1761898693.069267597 30 atime=1761898693.069267597 30 ctime=1761898693.069267597 qtractor-1.5.9/src/qtractorEditRangeForm.cpp0000644000175000001440000002343415101070305020172 0ustar00rncbcusers// qtractorEditRangeForm.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 "qtractorEditRangeForm.h" #include "qtractorAbout.h" #include "qtractorSession.h" #include "qtractorOptions.h" #include #include //---------------------------------------------------------------------------- // qtractorEditRangeForm -- UI wrapper form. // Constructor. qtractorEditRangeForm::qtractorEditRangeForm ( 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); // Initialize dirty control state. m_pTimeScale = nullptr; // Initial selection range (empty). m_iSelectStart = 0; m_iSelectEnd = 0; m_options = Clips | Automation; m_iUpdate = 0; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { m_pTimeScale = new qtractorTimeScale(*pSession->timeScale()); m_ui.RangeStartSpinBox->setTimeScale(m_pTimeScale); m_ui.RangeEndSpinBox->setTimeScale(m_pTimeScale); // Set proper time scales display format... m_ui.FormatComboBox->setCurrentIndex( int(m_pTimeScale->displayFormat())); // Default selection is whole session... m_iSelectStart = pSession->sessionStart(); m_iSelectEnd = pSession->sessionEnd(); // Populate range options... if (pSession->editHead() < pSession->editTail()) m_ui.EditRangeRadioButton->setChecked(true); else if (pSession->isPunching()) m_ui.PunchRangeRadioButton->setChecked(true); else if (pSession->isLooping()) m_ui.LoopRangeRadioButton->setChecked(true); else m_ui.CustomRangeRadioButton->setChecked(true); } // Update options check-boxes. qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) m_options = pOptions->iEditRangeOptions; updateOptions(); // Try to restore old window positioning. adjustSize(); // UI signal/slot connections... QObject::connect(m_ui.SelectionRangeRadioButton, SIGNAL(toggled(bool)), SLOT(rangeChanged())); QObject::connect(m_ui.LoopRangeRadioButton, SIGNAL(toggled(bool)), SLOT(rangeChanged())); QObject::connect(m_ui.PunchRangeRadioButton, SIGNAL(toggled(bool)), SLOT(rangeChanged())); QObject::connect(m_ui.EditRangeRadioButton, SIGNAL(toggled(bool)), SLOT(rangeChanged())); QObject::connect(m_ui.CustomRangeRadioButton, SIGNAL(toggled(bool)), SLOT(rangeChanged())); QObject::connect(m_ui.FormatComboBox, SIGNAL(activated(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.RangeStartSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(valueChanged())); QObject::connect(m_ui.RangeStartSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.RangeEndSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(valueChanged())); QObject::connect(m_ui.RangeEndSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.ClipsCheckBox, SIGNAL(toggled(bool)), SLOT(optionsChanged())); QObject::connect(m_ui.AutomationCheckBox, SIGNAL(toggled(bool)), SLOT(optionsChanged())); QObject::connect(m_ui.LoopCheckBox, SIGNAL(toggled(bool)), SLOT(optionsChanged())); QObject::connect(m_ui.PunchCheckBox, SIGNAL(toggled(bool)), SLOT(optionsChanged())); QObject::connect(m_ui.MarkersCheckBox, SIGNAL(toggled(bool)), SLOT(optionsChanged())); QObject::connect(m_ui.TempoMapCheckBox, SIGNAL(toggled(bool)), SLOT(optionsChanged())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(rejected()), SLOT(reject())); // Done. stabilizeForm(); } // Destructor. qtractorEditRangeForm::~qtractorEditRangeForm (void) { // Don't forget to get rid of local time-scale instance... if (m_pTimeScale) delete m_pTimeScale; } // Set the current initial selection range. void qtractorEditRangeForm::setSelectionRange ( unsigned long iSelectStart, unsigned long iSelectEnd ) { m_iSelectStart = iSelectStart; m_iSelectEnd = iSelectEnd; if (m_ui.CustomRangeRadioButton->isChecked()) { m_ui.RangeStartSpinBox->setValue(m_iSelectStart, false); m_ui.RangeEndSpinBox->setValue(m_iSelectEnd, false); } rangeChanged(); } // Retrieve the current range, if the case arises. unsigned long qtractorEditRangeForm::rangeStart (void) const { return m_ui.RangeStartSpinBox->value(); } unsigned long qtractorEditRangeForm::rangeEnd (void) const { return m_ui.RangeEndSpinBox->value(); } // Retrieve range option flags. unsigned int qtractorEditRangeForm::rangeOptions (void) const { return m_options; } // Option flags accessors. void qtractorEditRangeForm::setOption ( Option option, bool bOn ) { if (bOn) m_options |= (unsigned int) (option); else m_options &= ~(unsigned int) (option); } bool qtractorEditRangeForm::isOption ( Option option ) const { return (m_options & (unsigned int) (option)); } // Update options settings. void qtractorEditRangeForm::updateOptions (void) { ++m_iUpdate; m_ui.ClipsCheckBox->setChecked(m_options & Clips); m_ui.AutomationCheckBox->setChecked(m_options & Automation); m_ui.LoopCheckBox->setChecked((m_options & Loop)); m_ui.PunchCheckBox->setChecked(m_options & Punch); m_ui.MarkersCheckBox->setChecked(m_options & Markers); m_ui.TempoMapCheckBox->setChecked(m_options & TempoMap); --m_iUpdate; } // Options changed. void qtractorEditRangeForm::optionsChanged (void) { if (m_iUpdate > 0) return; setOption(Clips, m_ui.ClipsCheckBox->isChecked()); setOption(Automation, m_ui.AutomationCheckBox->isChecked()); setOption(Loop, m_ui.LoopCheckBox->isChecked()); setOption(Punch, m_ui.PunchCheckBox->isChecked()); setOption(Markers, m_ui.MarkersCheckBox->isChecked()); setOption(TempoMap, m_ui.TempoMapCheckBox->isChecked()); stabilizeForm(); } // Range settings have changed. void qtractorEditRangeForm::rangeChanged (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; if (m_ui.SelectionRangeRadioButton->isChecked()) { m_ui.RangeStartSpinBox->setValue(m_iSelectStart, false); m_ui.RangeEndSpinBox->setValue(m_iSelectEnd, false); } else if (m_ui.LoopRangeRadioButton->isChecked()) { m_ui.RangeStartSpinBox->setValue(pSession->loopStart(), false); m_ui.RangeEndSpinBox->setValue(pSession->loopEnd(), false); } else if (m_ui.PunchRangeRadioButton->isChecked()) { m_ui.RangeStartSpinBox->setValue(pSession->punchIn(), false); m_ui.RangeEndSpinBox->setValue(pSession->punchOut(), false); } else if (m_ui.EditRangeRadioButton->isChecked()) { m_ui.RangeStartSpinBox->setValue(pSession->editHead(), false); m_ui.RangeEndSpinBox->setValue(pSession->editTail(), false); } stabilizeForm(); } // Range values have changed. void qtractorEditRangeForm::valueChanged (void) { m_ui.CustomRangeRadioButton->setChecked(true); stabilizeForm(); } // Display format has changed. void qtractorEditRangeForm::formatChanged ( int iDisplayFormat ) { const bool bBlockSignals = m_ui.FormatComboBox->blockSignals(true); m_ui.FormatComboBox->setCurrentIndex(iDisplayFormat); qtractorTimeScale::DisplayFormat displayFormat = qtractorTimeScale::DisplayFormat(iDisplayFormat); m_ui.RangeStartSpinBox->setDisplayFormat(displayFormat); m_ui.RangeEndSpinBox->setDisplayFormat(displayFormat); if (m_pTimeScale) m_pTimeScale->setDisplayFormat(displayFormat); m_ui.FormatComboBox->blockSignals(bBlockSignals); stabilizeForm(); } // Stabilize current form state. void qtractorEditRangeForm::stabilizeForm (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = pSession->timeScale(); if (pTimeScale == nullptr) return; const bool bLooping = pSession->isLooping(); const bool bPunching = pSession->isPunching(); m_ui.SelectionRangeRadioButton->setEnabled(m_iSelectStart < m_iSelectEnd); m_ui.LoopRangeRadioButton->setEnabled(bLooping); m_ui.PunchRangeRadioButton->setEnabled(bPunching); m_ui.EditRangeRadioButton->setEnabled( pSession->editHead() < pSession->editTail()); m_ui.LoopCheckBox->setEnabled(bLooping); m_ui.PunchCheckBox->setEnabled(bPunching); m_ui.MarkersCheckBox->setEnabled(pTimeScale->markers().first() != nullptr); m_ui.TempoMapCheckBox->setEnabled(pTimeScale->nodes().count() > 1); const unsigned long iRangeStart = m_ui.RangeStartSpinBox->value(); const unsigned long iRangeEnd = m_ui.RangeEndSpinBox->value(); m_ui.RangeStartSpinBox->setMaximum(iRangeEnd); m_ui.RangeEndSpinBox->setMinimum(iRangeStart); m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled( iRangeStart < iRangeEnd); } // Dialog acceptance. void qtractorEditRangeForm::accept (void) { // Update options check-boxes. qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) pOptions->iEditRangeOptions = m_options; QDialog::accept(); } // end of qtractorEditRangeForm.cpp qtractor-1.5.9/src/PaxHeaders/qtractorTimeScale.h0000644000000000000000000000013215101070305017021 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorTimeScale.h0000644000175000001440000004745015101070305017023 0ustar00rncbcusers// qtractorTimeScale.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 __qtractorTimeScale_h #define __qtractorTimeScale_h #include "qtractorList.h" #include #include // Needed for the translation functions. #include #include //---------------------------------------------------------------------- // class qtractorTimeScale -- Time scale conversion helper class. // class qtractorTimeScale { Q_DECLARE_TR_FUNCTIONS(qtractorTimeScale) public: // Available display-formats. enum DisplayFormat { Frames = 0, Time, BBT }; // Default constructor. qtractorTimeScale() : m_displayFormat(Frames), m_iSampleRate(44100), m_cursor(this), m_markerCursor(this) { clear(); } // Copy constructor. qtractorTimeScale(const qtractorTimeScale& ts) : m_cursor(this), m_markerCursor(this) { copy(ts); } // Assignment operator, qtractorTimeScale& operator=(const qtractorTimeScale& ts) { return copy(ts); } // Node list cleaner. void reset(); // (Re)nitializer method. void clear(); // Sync method. void sync(const qtractorTimeScale& ts); // Copy method. qtractorTimeScale& copy(const qtractorTimeScale& ts); // Sample rate (frames per second) void setSampleRate(unsigned int iSampleRate) { m_iSampleRate = iSampleRate; } unsigned int sampleRate() const { return m_iSampleRate; } // Resolution (ticks per quarter note; PPQN) void setTicksPerBeat(unsigned short iTicksPerBeat) { m_iTicksPerBeat = iTicksPerBeat; } unsigned short ticksPerBeat() const { return m_iTicksPerBeat; } // Pixels per beat (width). void setPixelsPerBeat(unsigned short iPixelsPerBeat) { m_iPixelsPerBeat = iPixelsPerBeat; } unsigned short pixelsPerBeat() const { return m_iPixelsPerBeat; } // Beat divisor (snap) accessors. void setSnapPerBeat(unsigned short iSnapPerBeat) { m_iSnapPerBeat = iSnapPerBeat; } unsigned short snapPerBeat(void) const { return m_iSnapPerBeat; } // Horizontal zoom factor. void setHorizontalZoom(unsigned short iHorizontalZoom) { m_iHorizontalZoom = iHorizontalZoom; } unsigned short horizontalZoom() const { return m_iHorizontalZoom; } // Vertical zoom factor. void setVerticalZoom(unsigned short iVerticalZoom) { m_iVerticalZoom = iVerticalZoom; } unsigned short verticalZoom() const { return m_iVerticalZoom; } // Fastest rounding-from-float helper. static unsigned long uroundf(float x) { return ::lroundf(x); } // Beat divisor (snap index) accessors. static unsigned short snapFromIndex(int iSnap); static int indexFromSnap(unsigned short iSnapPerBeat); // Beat divisor (snap index) text item list. static QStringList snapItems(); // Time scale node declaration. class Node : public qtractorList::Link { public: // Constructor. Node(qtractorTimeScale *pTimeScale, unsigned long iFrame = 0, float fTempo = 120.0f, unsigned short iBeatType = 2, unsigned short iBeatsPerBar = 4, unsigned short iBeatDivisor = 2) : frame(iFrame), bar(0), beat(0), tick(0), pixel(0), tempo(fTempo), beatType(iBeatType), beatsPerBar(iBeatsPerBar), beatDivisor(iBeatDivisor), ticksPerBeat(0), ts(pTimeScale), tickRate(1.0f), beatRate(1.0f) {} // Update node scale coefficients. void update(); // Update node position metrics. void reset(Node *pNode); // Tempo accessor/convertors. void setTempoEx(float fTempo, unsigned short iBeatType = 2); float tempoEx(unsigned short iBeatType = 2) const; // Frame/bar convertors. unsigned short barFromFrame(unsigned long iFrame) const { return bar + uroundf( (beatRate * (iFrame - frame)) / (ts->frameRate() * beatsPerBar)); } unsigned long frameFromBar(unsigned short iBar) const { return frame + uroundf( (ts->frameRate() * beatsPerBar * (iBar - bar)) / beatRate); } // Frame/beat convertors. unsigned int beatFromFrame(unsigned long iFrame) const { return beat + uroundf( (beatRate * (iFrame - frame)) / ts->frameRate()); } unsigned long frameFromBeat(unsigned int iBeat) const { return frame + uroundf( (ts->frameRate() * (iBeat - beat)) / beatRate); } // Frame/tick convertors. unsigned long tickFromFrame(unsigned long iFrame) const { return tick + uroundf( (tickRate * (iFrame - frame)) / ts->frameRate()); } unsigned long frameFromTick(unsigned long iTick) const { return frame + uroundf( (ts->frameRate() * (iTick - tick)) / tickRate); } // Tick/beat convertors. unsigned int beatFromTick(unsigned long iTick) const { return beat + ((iTick - tick) / ticksPerBeat); } unsigned long tickFromBeat(unsigned int iBeat) const { return tick + (ticksPerBeat * (iBeat - beat)); } // Tick/bar convertors. unsigned short barFromTick(unsigned long iTick) const { return bar + ((iTick - tick) / (ticksPerBeat * beatsPerBar)); } unsigned long tickFromBar(unsigned short iBar) const { return tick + (ticksPerBeat * beatsPerBar * (iBar - bar)); } // Tick/pixel convertors. unsigned long tickFromPixel(int x) const { return tick + uroundf( (tickRate * (x - pixel)) / ts->pixelRate()); } int pixelFromTick(unsigned long iTick) const { return pixel + uroundf( (ts->pixelRate() * (iTick - tick)) / tickRate); } // Beat/pixel convertors. unsigned int beatFromPixel(int x) const { return beat + uroundf( (beatRate * (x - pixel)) / ts->pixelRate()); } int pixelFromBeat(unsigned int iBeat) const { return pixel + uroundf( (ts->pixelRate() * (iBeat - beat)) / beatRate); } // Pixel/beat rate convertor. unsigned short pixelsPerBeat() const { return uroundf(ts->pixelRate() / beatRate); } // Bar/pixel convertors. unsigned short barFromPixel(int x) const { return bar + uroundf( (beatRate * (x - pixel)) / (ts->pixelRate() * beatsPerBar)); } int pixelFromBar(unsigned short iBar) const { return pixel + uroundf( (ts->pixelRate() * beatsPerBar * (iBar - bar)) / beatRate); } // Bar/beat convertors. unsigned short barFromBeat(unsigned int iBeat) const { return bar + ((iBeat - beat) / beatsPerBar); } unsigned int beatFromBar(unsigned short iBar) const { return beat + (beatsPerBar * (iBar - bar)); } bool beatIsBar(unsigned int iBeat) const { return ((iBeat - beat) % beatsPerBar) == 0; } // Frame/bar quantizer. unsigned long frameSnapToBar(unsigned long iFrame) const { return frameFromBar(barFromFrame(iFrame)); } // Beat snap filters. unsigned long tickSnap(unsigned long iTick, unsigned short p = 1) const; unsigned long frameSnap(unsigned long iFrame) const { return frameFromTick(tickSnap(tickFromFrame(iFrame))); } int pixelSnap(int x) const { return pixelFromTick(tickSnap(tickFromPixel(x))); } // Alternate (secondary) time-sig helper methods unsigned short beatsPerBar2() const; unsigned short ticksPerBeat2() const; // Node keys. unsigned long frame; unsigned short bar; unsigned int beat; unsigned long tick; int pixel; // Node payload. float tempo; unsigned short beatType; unsigned short beatsPerBar; unsigned short beatDivisor; unsigned short ticksPerBeat; protected: // Node owner. qtractorTimeScale *ts; // Node cached coefficients. float tickRate; float beatRate; }; // Node list accessor. const qtractorList& nodes() const { return m_nodes; } // To optimize and keep track of current frame // position, mostly like an sequence cursor/iterator. class Cursor { public: // Constructor. Cursor(qtractorTimeScale *pTimeScale) : ts(pTimeScale), node(0) {} // Time scale accessor. qtractorTimeScale *timeScale() const { return ts; } // Reset method. void reset(Node *pNode = 0); // Seek methods. Node *seekFrame(unsigned long iFrame); Node *seekBar(unsigned short iBar); Node *seekBeat(unsigned int iBeat); Node *seekTick(unsigned long iTick); Node *seekPixel(int x); protected: // Member variables. qtractorTimeScale *ts; Node *node; }; // Internal cursor accessor. Cursor& cursor() { return m_cursor; } // Node list specifics. Node *addNode( unsigned long iFrame = 0, float fTempo = 120.0f, unsigned short iBeatType = 2, unsigned short iBeatsPerBar = 4, unsigned short iBeatDivisor = 2); void updateNode(Node *pNode); void removeNode(Node *pNode); // Complete time-scale update method. void updateScale(); // Frame/pixel convertors. int pixelFromFrame(unsigned long iFrame) const { return uroundf((m_fPixelRate * iFrame) / m_fFrameRate); } unsigned long frameFromPixel(int x) const { return uroundf((m_fFrameRate * x) / m_fPixelRate); } // Frame/bar general converters. unsigned short barFromFrame(unsigned long iFrame) { Node *pNode = m_cursor.seekFrame(iFrame); return (pNode ? pNode->barFromFrame(iFrame) : 0); } unsigned long frameFromBar(unsigned short iBar) { Node *pNode = m_cursor.seekBar(iBar); return (pNode ? pNode->frameFromBar(iBar) : 0); } // Tick/bar general converters. unsigned short barFromTick(unsigned long iTick) { Node *pNode = m_cursor.seekTick(iTick); return (pNode ? pNode->barFromTick(iTick) : 0); } unsigned long tickFromBar(unsigned short iBar) { Node *pNode = m_cursor.seekBar(iBar); return (pNode ? pNode->tickFromBar(iBar) : 0); } // Frame/beat general converters. unsigned int beatFromFrame(unsigned long iFrame) { Node *pNode = m_cursor.seekFrame(iFrame); return (pNode ? pNode->beatFromFrame(iFrame) : 0); } unsigned long frameFromBeat(unsigned int iBeat) { Node *pNode = m_cursor.seekBeat(iBeat); return (pNode ? pNode->frameFromBeat(iBeat) : 0); } // Tick/beat general converters. unsigned int beatFromTick(unsigned long iTick) { Node *pNode = m_cursor.seekTick(iTick); return (pNode ? pNode->beatFromTick(iTick) : 0); } unsigned long tickFromBeat(unsigned int iBeat) { Node *pNode = m_cursor.seekBeat(iBeat); return (pNode ? pNode->tickFromBeat(iBeat) : 0); } // Frame/tick general converters. unsigned long tickFromFrame(unsigned long iFrame) { Node *pNode = m_cursor.seekFrame(iFrame); return (pNode ? pNode->tickFromFrame(iFrame) : 0); } unsigned long frameFromTick(unsigned long iTick) { Node *pNode = m_cursor.seekTick(iTick); return (pNode ? pNode->frameFromTick(iTick) : 0); } // Tick/pixel general converters. unsigned long tickFromPixel(int x) { Node *pNode = m_cursor.seekPixel(x); return (pNode ? pNode->tickFromPixel(x) : 0); } int pixelFromTick(unsigned long iTick) { Node *pNode = m_cursor.seekTick(iTick); return (pNode ? pNode->pixelFromTick(iTick) : 0); } // Beat/pixel composite converters. unsigned int beatFromPixel(int x) { Node *pNode = m_cursor.seekPixel(x); return (pNode ? pNode->beatFromPixel(x) : 0); } int pixelFromBeat(unsigned int iBeat) { Node *pNode = m_cursor.seekBeat(iBeat); return (pNode ? pNode->pixelFromBeat(iBeat) : 0); } // Bar/beat predicate. bool beatIsBar(unsigned int iBeat) { Node *pNode = m_cursor.seekBeat(iBeat); return (pNode ? pNode->beatIsBar(iBeat) : false); } // Snap functions. unsigned long tickSnap(unsigned long iTick) { Node *pNode = m_cursor.seekTick(iTick); return (pNode ? pNode->tickSnap(iTick) : iTick); } unsigned long frameSnap(unsigned long iFrame) { Node *pNode = m_cursor.seekFrame(iFrame); return (pNode ? pNode->frameSnap(iFrame) : iFrame); } int pixelSnap(int x) { Node *pNode = m_cursor.seekPixel(x); return (pNode ? pNode->pixelSnap(x) : x); } // Display-format accessors. void setDisplayFormat(DisplayFormat displayFormat) { m_displayFormat = displayFormat; } DisplayFormat displayFormat() const { return m_displayFormat; } // Convert frames to time string and vice-versa. QString textFromFrameEx(DisplayFormat displayFormat, unsigned long iFrame, bool bDelta = false, unsigned long iDelta = 0); QString textFromFrame( unsigned long iFrame, bool bDelta = false, unsigned long iDelta = 0); unsigned long frameFromTextEx(DisplayFormat displayFormat, const QString& sText, bool bDelta = false, unsigned long iFrame = 0); unsigned long frameFromText( const QString& sText, bool bDelta = false, unsigned long iFrame = 0); // Convert ticks to time string and vice-versa. QString textFromTick( unsigned long iTick, bool bDelta = false, unsigned long iDelta = 0); unsigned long tickFromText( const QString& sText, bool bDelta = false, unsigned long iTick = 0); // Tempo (beats per minute; BPM) void setTempo(float fTempo) { Node *pNode = m_nodes.first(); if (pNode) pNode->tempo = fTempo; } float tempo() const { Node *pNode = m_nodes.first(); return (pNode ? pNode->tempo : 120.0f); } // Tempo convertors (default's quarter notes per minute) void setTempoEx(float fTempo, unsigned short iBeatType = 2) { Node *pNode = m_nodes.first(); if (pNode) pNode->setTempoEx(fTempo, iBeatType); } float tempoEx(unsigned short iBeatType = 2) const { Node *pNode = m_nodes.first(); return (pNode ? pNode->tempoEx(iBeatType) : 120.0f); } // Tempo beat type (if not standard 2=quarter note) void setBeatType(unsigned short iBeatType) { Node *pNode = m_nodes.first(); if (pNode) pNode->beatType = iBeatType; } unsigned short beatType() const { Node *pNode = m_nodes.first(); return (pNode ? pNode->beatType : 2); } // Time signature (numerator) void setBeatsPerBar(unsigned short iBeatsPerBar) { Node *pNode = m_nodes.first(); if (pNode) pNode->beatsPerBar = iBeatsPerBar; } unsigned short beatsPerBar() const { Node *pNode = m_nodes.first(); return (pNode ? pNode->beatsPerBar : 4); } // Time signature (denominator) void setBeatDivisor(unsigned short iBeatDivisor) { Node *pNode = m_nodes.first(); if (pNode) pNode->beatDivisor = iBeatDivisor; } unsigned short beatDivisor() const { Node *pNode = m_nodes.first(); return (pNode ? pNode->beatDivisor : 2); } // Secondary time signature (numerator) void setBeatsPerBar2(unsigned short iBeatsPerBar2) { m_iBeatsPerBar2 = iBeatsPerBar2; } unsigned short beatsPerBar2() const { return m_iBeatsPerBar2; } // Secondary time signature (denominator) void setBeatDivisor2(unsigned short iBeatDivisor2) { m_iBeatDivisor2 = iBeatDivisor2; } unsigned short beatDivisor2() const { return m_iBeatDivisor2; } // Tick/Frame range conversion (delta conversion). unsigned long frameFromTickRange( unsigned long iTickStart, unsigned long iTickEnd, bool bOffset); unsigned long tickFromFrameRange( unsigned long iFrameStart, unsigned long iFrameEnd, bool bOffset); // Location marker declaration. class Marker : public qtractorList::Link { public: // Constructors. Marker(unsigned long iFrame, unsigned short iBar, const QString& sText, const QColor& rgbColor = Qt::darkGray) : frame(iFrame), bar(iBar), text(sText), color(rgbColor), accidentals(MinAccidentals), mode(-1) {} Marker(unsigned long iFrame, unsigned short iBar, int iAccidentals = 0, int iMode = 0) : frame(iFrame), bar(iBar), color(Qt::darkGray), accidentals(iAccidentals), mode(iMode) {} // Copy constructor. Marker(const Marker& marker) : frame(marker.frame), bar(marker.bar), text(marker.text), color(marker.color), accidentals(marker.accidentals), mode(marker.mode) {} // Marker keys. unsigned long frame; unsigned short bar; // Location marker payload. QString text; QColor color; // Key-signature marker payload. int accidentals; int mode; }; // To optimize and keep track of current frame // position, mostly like an sequence cursor/iterator. class MarkerCursor { public: // Constructor. MarkerCursor(qtractorTimeScale *pTimeScale) : ts(pTimeScale), marker(nullptr) {} // Time scale accessor. qtractorTimeScale *timeScale() const { return ts; } // Reset method. void reset(Marker *pMarker = 0); // Seek methods. Marker *seekFrame(unsigned long iFrame); Marker *seekBar(unsigned short iBar); Marker *seekBeat(unsigned int iBeat); Marker *seekTick(unsigned long iTick); Marker *seekPixel(int x); // Notable markers accessors Marker *first() const { return ts->m_markers.first(); } Marker *last() const { return ts->m_markers.last(); } protected: // Member variables. qtractorTimeScale *ts; Marker *marker; }; // Markers list accessor. MarkerCursor& markers() { return m_markerCursor; } // Marker list specifics. Marker *addMarker( unsigned long iFrame, const QString& sText, const QColor& rgbColor = Qt::darkGray); Marker *addKeySignature( unsigned long iFrame, int iAccidentals, int iMode = 0); void updateMarker(Marker *pMarker); void removeMarker(Marker *pMarker); // Update markers from given node position. void updateMarkers(Node *pNode); // Key signature map accessors. // enum { MinAccidentals = -9, MaxAccidentals = 9 }; static bool isKeySignature(int iAccidentals, int iMode); static QString keySignatureName( int iAccidentals, int iMode, char chMinor = 'm'); // MIDI resolution constants. static const unsigned short TICKS_PER_BEAT_MIN = 24; static const unsigned short TICKS_PER_BEAT_DEF = 960; static const unsigned short TICKS_PER_BEAT_MAX = 3840; static const unsigned short TICKS_PER_BEAT_HRQ = (TICKS_PER_BEAT_MAX << 3); // MIDI time adjust to/from official high resolution queue (64bit). unsigned long timep ( unsigned long time ) const { return uint64_t(time) * TICKS_PER_BEAT_HRQ / m_iTicksPerBeat; } unsigned long timeq ( unsigned long time ) const { return uint64_t(time) * m_iTicksPerBeat / TICKS_PER_BEAT_HRQ; } protected: // Tempo-map independent coefficients. float pixelRate() const { return m_fPixelRate; } float frameRate() const { return m_fFrameRate; } private: unsigned short m_iSnapPerBeat; // Snap per beat (divisor). unsigned short m_iHorizontalZoom; // Horizontal zoom factor. unsigned short m_iVerticalZoom; // Vertical zoom factor. DisplayFormat m_displayFormat; // Textual display format. unsigned int m_iSampleRate; // Sample rate (frames per second) unsigned short m_iTicksPerBeat; // Ticks per quarter note (PPQN) unsigned short m_iPixelsPerBeat; // Pixels per beat (width). // Tempo-map node list. qtractorList m_nodes; // Internal node cursor. Cursor m_cursor; // Tempo-map independent coefficients. float m_fPixelRate; float m_fFrameRate; // Secondary time signature (numerator/denuminator) unsigned short m_iBeatsPerBar2; unsigned short m_iBeatDivisor2; // Location marker list. qtractorList m_markers; // Internal node cursor. MarkerCursor m_markerCursor; }; //------------------------------------------------------------------------- // qtractorTempoCursor -- Custom tempo tracking helper class class qtractorTempoCursor { public: // Constructor. qtractorTempoCursor() : m_pNode(nullptr) {} // Reset method. void clear() { m_pNode = nullptr; } // Predicate method. qtractorTimeScale::Node *seek( qtractorTimeScale *pTimeScale, unsigned long iFrame) { qtractorTimeScale::Cursor& cursor = pTimeScale->cursor(); qtractorTimeScale::Node *pNode = cursor.seekFrame(iFrame); return (m_pNode == pNode ? nullptr : m_pNode = pNode); } private: // Instance variables. qtractorTimeScale::Node *m_pNode; }; #endif // __qtractorTimeScale_h // end of qtractorTimeScale.h qtractor-1.5.9/src/PaxHeaders/qtractorEngineCommand.cpp0000644000000000000000000000013215101070305020212 xustar0030 mtime=1761898693.070267601 30 atime=1761898693.070267601 30 ctime=1761898693.070267601 qtractor-1.5.9/src/qtractorEngineCommand.cpp0000644000175000001440000005633015101070305020211 0ustar00rncbcusers// qtractorEngineCommand.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 "qtractorEngineCommand.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include "qtractorMidiManager.h" #include "qtractorSession.h" #include "qtractorMainForm.h" #include "qtractorTracks.h" #include "qtractorTrackList.h" #include "qtractorMixer.h" #include "qtractorMeter.h" //---------------------------------------------------------------------- // class qtractorBusCommand - implementation // // Constructor. qtractorBusCommand::qtractorBusCommand ( const QString& sName, qtractorBus *pBus, qtractorBus *pAfterBus, qtractorBus::BusMode busMode ) : qtractorCommand(sName), m_pBus(pBus), m_pAfterBus(pAfterBus), m_busMode(busMode), m_busType(qtractorTrack::None), m_bMonitor(false), m_iChannels(0), m_bAutoConnect(false) { setRefresh(false); // Set initial bus properties if any... if (m_pBus && m_busMode == qtractorBus::None) { m_busMode = m_pBus->busMode(); m_busType = m_pBus->busType(); m_sBusName = m_pBus->busName(); m_bMonitor = m_pBus->isMonitor(); // Special case typed buses... switch (m_pBus->busType()) { case qtractorTrack::Audio: { qtractorAudioBus *pAudioBus = static_cast (m_pBus); if (pAudioBus) { m_iChannels = pAudioBus->channels(); m_bAutoConnect = pAudioBus->isAutoConnect(); } break; } case qtractorTrack::Midi: { qtractorMidiBus *pMidiBus = static_cast (m_pBus); if (pMidiBus) { m_sInstrumentName = pMidiBus->instrumentName(); } break; } case qtractorTrack::None: default: break; } } } // Create a new bus. bool qtractorBusCommand::createBus (void) { if (m_pBus || m_sBusName.isEmpty()) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // We need to hold things for a while... const bool bPlaying = pSession->isPlaying(); pSession->lock(); pSession->setPlaying(false); // Create the bus of proper type... m_pBus = nullptr; qtractorAudioBus *pAudioBus = nullptr; qtractorMidiBus *pMidiBus = nullptr; switch (m_busType) { case qtractorTrack::Audio: { qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine) { pAudioBus = new qtractorAudioBus(pAudioEngine, m_sBusName, m_busMode, m_bMonitor, m_iChannels); pAudioBus->setAutoConnect(m_bAutoConnect); pAudioEngine->addBus(pAudioBus, m_pAfterBus); pAudioEngine->resetPlayerBus(); pAudioEngine->resetMetroBus(); m_pBus = pAudioBus; } break; } case qtractorTrack::Midi: { qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine) { pMidiBus = new qtractorMidiBus(pMidiEngine, m_sBusName, m_busMode, m_bMonitor); pMidiBus->setInstrumentName(m_sInstrumentName); pMidiEngine->addBus(pMidiBus, m_pAfterBus); pMidiEngine->resetControlBus(); pMidiEngine->resetMetroBus(); m_pBus = pMidiBus; } break; } default: break; } // Check if we really have a new bus... if (m_pBus == nullptr) { pSession->setPlaying(bPlaying); pSession->unlock(); return false; } // Open up the new bus... m_pBus->open(); // Yet special for audio buses... if (pAudioBus) pAudioBus->autoConnect(); // Update mixer (look for new strips...) qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer) pMixer->updateBuses(true); } // Carry on... pSession->setPlaying(bPlaying); pSession->unlock(); // Done. return true; } // Update bus properties. bool qtractorBusCommand::updateBus (void) { if (m_pBus == nullptr || m_sBusName.isEmpty()) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return false; // We need to hold things for a while... const bool bPlaying = pSession->isPlaying(); pSession->lock(); pSession->setPlaying(false); // Save current bus properties... qtractorBus::BusMode busMode = m_pBus->busMode(); QString sBusName = m_pBus->busName(); const bool bMonitor = m_pBus->isMonitor(); const bool bRenameBus = (m_sBusName != sBusName); // Save current connections... qtractorBus::ConnectList inputs; qtractorBus::ConnectList outputs; if (busMode & qtractorBus::Input) m_pBus->updateConnects(qtractorBus::Input, inputs); if (busMode & qtractorBus::Output) m_pBus->updateConnects(qtractorBus::Output, outputs); // Special case typed buses... qtractorAudioBus *pAudioBus = nullptr; qtractorMidiBus *pMidiBus = nullptr; unsigned short iChannels = 0; bool bAutoConnect = false; QString sInstrumentName; switch (m_pBus->busType()) { case qtractorTrack::Audio: pAudioBus = static_cast (m_pBus); if (pAudioBus) { iChannels = pAudioBus->channels(); bAutoConnect = pAudioBus->isAutoConnect(); } break; case qtractorTrack::Midi: pMidiBus = static_cast (m_pBus); if (pMidiBus) { sInstrumentName = pMidiBus->instrumentName(); } break; case qtractorTrack::None: default: break; } // Update (reset) all applicable mixer strips... QList strips; qtractorMixerStrip *pStrip; QList managers; qtractorMidiManager *pMidiManager; qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer) { if (m_pBus->busMode() & qtractorBus::Input) { // Find the input strips that have this bus monitored... pStrip = (pMixer->inputRack())->findStrip(m_pBus->monitor_in()); if (pStrip) { if (m_busMode & qtractorBus::Input) { strips.append(pStrip); } else { (pMixer->inputRack())->removeStrip(pStrip); } } } if (m_pBus->busMode() & qtractorBus::Output) { // Find the output strips that have this bus monitored... pStrip = (pMixer->outputRack())->findStrip(m_pBus->monitor_out()); if (pStrip) { if (m_busMode & qtractorBus::Output) { strips.append(pStrip); } else { (pMixer->outputRack())->removeStrip(pStrip); } } // Find the MIDI strips that have this (audio) bus monitored... if (m_pBus->busType() == qtractorTrack::Audio && m_iChannels != iChannels) { qtractorAudioBus *pAudioOutputBus = static_cast (m_pBus); const QList& strips2 = pMixer->findAudioOutputBusStrips(pAudioOutputBus); QListIterator iter2(strips2); while (iter2.hasNext()) { pStrip = iter2.next(); pMidiManager = pStrip->midiManager(); if (pMidiManager && pMidiManager->isAudioOutputMonitor()) { pMidiManager->setAudioOutputMonitor(false); managers.append(pMidiManager); } } } } } // Close all applicable tracks... for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() != m_pBus->busType()) continue; if (pTrack->inputBus() == m_pBus) pTrack->setInputBusName(m_sBusName); if (pTrack->outputBus() == m_pBus) pTrack->setOutputBusName(m_sBusName); if (pTrack->inputBus() == m_pBus || pTrack->outputBus() == m_pBus) { if (pMixer) { pStrip = (pMixer->trackRack())->findStrip(pTrack->monitor()); if (pStrip) strips.append(pStrip); } pTrack->close(); } } if (pMixer) { QListIterator iter(strips); while (iter.hasNext()) iter.next()->clear(); } // May close now the bus... m_pBus->close(); // Have some time slack... pSession->stabilize(); // Set new properties... m_pBus->setBusName(m_sBusName); m_pBus->setBusMode(m_busMode); m_pBus->setMonitor(m_bMonitor); // Special case for typed buses... if (pAudioBus) { pAudioBus->setChannels(m_iChannels); pAudioBus->setAutoConnect(m_bAutoConnect); if (bRenameBus) pAudioBus->updateAudioAuxSends(m_sBusName); } if (pMidiBus) { pMidiBus->setInstrumentName(m_sInstrumentName); if (bRenameBus) pMidiBus->updateMidiAuxSends(m_sBusName); } // May reopen up the bus... m_pBus->open(); // Yet special for audio buses... if (pAudioBus) pAudioBus->autoConnect(); // Restore previous connections... if (m_busMode & qtractorBus::Input) m_pBus->updateConnects(qtractorBus::Input, inputs, true); if (m_busMode & qtractorBus::Output) m_pBus->updateConnects(qtractorBus::Output, outputs, true); // (Re)open all applicable tracks // and (reset) respective mixer strips too ... qtractorTracks *pTracks = pMainForm->tracks(); for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() != m_pBus->busType()) continue; if (pTrack->inputBusName() == m_sBusName || pTrack->outputBusName() == m_sBusName) { // Reopen track back... pTrack->open(); // Update track list item... if (pTracks) (pTracks->trackList())->updateTrack(pTrack); } } // Update (reset) all applicable mixer strips... if (pMixer) { QListIterator iter(strips); while (iter.hasNext()) { pStrip = iter.next(); pStrip->clear(); if (pStrip->track()) pStrip->setTrack(pStrip->track()); else if (pStrip->bus()) pStrip->setBus(pStrip->bus()); } pMixer->updateBuses(); // Update all applicable MIDI managers too... QListIterator iter2(managers); while (iter2.hasNext()) { qtractorMidiManager *pMidiManager = iter2.next(); // pMidiManager->setAudioOutputMonitor(true); qtractorPluginList *pPluginList = pMidiManager->pluginList(); if (pPluginList) { const bool bAudioOuts = pPluginList->resetChannels(m_iChannels, false); pPluginList->setChannelsEx(m_iChannels); pMidiManager->setAudioOutputMonitorEx(bAudioOuts); } } } // Swap saved bus properties... m_busMode = busMode; m_sBusName = sBusName; m_bMonitor = bMonitor; m_iChannels = iChannels; m_bAutoConnect = bAutoConnect; m_sInstrumentName = sInstrumentName; // Carry on... pSession->setPlaying(bPlaying); pSession->unlock(); // Done. return true; } // Delete bus. bool qtractorBusCommand::deleteBus (void) { if (m_pBus == nullptr) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return false; // Get the device view root item... qtractorEngine *pEngine = nullptr; // Special case typed buses... qtractorAudioBus *pAudioBus = nullptr; qtractorMidiBus *pMidiBus = nullptr; switch (m_pBus->busType()) { case qtractorTrack::Audio: pEngine = pSession->audioEngine(); pAudioBus = static_cast (m_pBus); break; case qtractorTrack::Midi: pEngine = pSession->midiEngine(); pMidiBus = static_cast (m_pBus); break; default: break; } // Still valid? if (pEngine == nullptr) return false; // We need to hold things for a while... const bool bPlaying = pSession->isPlaying(); pSession->lock(); pSession->setPlaying(false); // Close all applicable tracks (and mixer strips)... QList strips; qtractorMixerStrip *pStrip; QList managers; qtractorMidiManager *pMidiManager; qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer && pAudioBus && (m_pBus->busMode() & qtractorBus::Output)) { // Find the MIDI strips that have this (audio) bus monitored... const QList& strips2 = pMixer->findAudioOutputBusStrips(pAudioBus); QListIterator iter2(strips2); while (iter2.hasNext()) { pStrip = iter2.next(); pMidiManager = pStrip->midiManager(); if (pMidiManager && pMidiManager->isAudioOutputMonitor()) { pMidiManager->setAudioOutputMonitor(false); managers.append(pMidiManager); } } } // Close all applicable tracks... for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() != m_pBus->busType()) continue; if (pTrack->inputBus() == m_pBus || pTrack->outputBus() == m_pBus) { pTrack->close(); if (pMixer) { pStrip = (pMixer->trackRack())->findStrip(pTrack->monitor()); if (pStrip) { pStrip->clear(); strips.append(pStrip); } } } } if (pAudioBus) pAudioBus->updateAudioAuxSends(QString()); if (pMidiBus) pMidiBus->updateMidiAuxSends(QString()); // May close now the bus... m_pBus->close(); // And remove it... pEngine->removeBus(m_pBus); m_pBus = nullptr; // Better update special buses anyway... switch (pEngine->syncType()) { case qtractorTrack::Audio: { qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine) { pAudioEngine->resetPlayerBus(); pAudioEngine->resetMetroBus(); } break; } case qtractorTrack::Midi: { qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine) { pMidiEngine->resetControlBus(); pMidiEngine->resetMetroBus(); } break; } default: break; } // (Re)open all tracks... and (reset) mixer strips too ... qtractorTracks *pTracks = pMainForm->tracks(); QListIterator iter(strips); while (iter.hasNext()) { qtractorMixerStrip *pStrip = iter.next(); qtractorTrack *pTrack = pStrip->track(); if (pTrack) { pTrack->open(); // Update track list item... if (pTracks) (pTracks->trackList())->updateTrack(pTrack); } pStrip->setTrack(pTrack); } // Update mixer (clean old strips...) if (pMixer) { pMixer->updateBuses(); QListIterator iter2(managers); while (iter2.hasNext()) { pMidiManager = iter2.next(); pMidiManager->resetAudioOutputBus(); pMidiManager->setAudioOutputMonitor(true); } } // Carry on... pSession->setPlaying(bPlaying); pSession->unlock(); // Done. return true; } // Monitor meter accessor. qtractorMixerMeter *qtractorBusCommand::meter (void) const { qtractorBus *pBus = bus(); if (pBus == nullptr) return nullptr; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return nullptr; qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer == nullptr) return nullptr; // Mixer strip determination... qtractorMixerStrip *pStrip = nullptr; if ((busMode() & qtractorBus::Input) && pBus->monitor_in()) pStrip = pMixer->inputRack()->findStrip(pBus->monitor_in()); else if ((busMode() & qtractorBus::Output) && pBus->monitor_out()) pStrip = pMixer->outputRack()->findStrip(pBus->monitor_out()); return (pStrip ? pStrip->meter() : nullptr); } //---------------------------------------------------------------------- // class qtractorCreateBusCommand - implementation. // // Constructor. qtractorCreateBusCommand::qtractorCreateBusCommand ( qtractorBus *pAfterBus ) : qtractorBusCommand(QObject::tr("create bus"), nullptr, pAfterBus) { } // Bus creation command methods. bool qtractorCreateBusCommand::redo (void) { return createBus(); } bool qtractorCreateBusCommand::undo (void) { return deleteBus(); } //---------------------------------------------------------------------- // class qtractorUpdateBusCommand - implementation. // // Constructor. qtractorUpdateBusCommand::qtractorUpdateBusCommand ( qtractorBus *pBus ) : qtractorBusCommand(QObject::tr("update bus"), pBus) { } // Bus update command methods. bool qtractorUpdateBusCommand::redo (void) { return updateBus(); } //---------------------------------------------------------------------- // class qtractorDeleteBusCommand - implementation. // // Constructor. qtractorDeleteBusCommand::qtractorDeleteBusCommand ( qtractorBus *pBus ) : qtractorBusCommand(QObject::tr("delete bus"), pBus, pBus->prev()) { } // Bus deletion command methods. bool qtractorDeleteBusCommand::redo (void) { return deleteBus(); } bool qtractorDeleteBusCommand::undo (void) { return createBus(); } //---------------------------------------------------------------------- // class qtractorMoveBusCommand - implementation. // // Constructor. qtractorMoveBusCommand::qtractorMoveBusCommand ( qtractorBus *pBus, int iDelta ) : qtractorBusCommand(QObject::tr("move bus"), pBus), m_iDelta(iDelta) { } // Bus-move command methods. bool qtractorMoveBusCommand::redo (void) { qtractorBus *pBus = bus(); if (pBus == nullptr) return false; qtractorEngine *pEngine = pBus->engine(); if (pEngine == nullptr) return false; // pSession->lock(); // Save the next bus alright... const int iDelta = -m_iDelta; // Move it... pEngine->moveBus2(pBus, m_iDelta); // Swap it nice, finally. m_iDelta = iDelta; // pSession->unlock(); // Update mixer (look for new strip order...) qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer) pMixer->updateBuses(true); } return true; } //---------------------------------------------------------------------- // class qtractorBusMonitorCommand - implementation. // // Constructor. qtractorBusMonitorCommand::qtractorBusMonitorCommand ( qtractorBus *pBus, bool bMonitor ) : qtractorBusCommand(QObject::tr("bus pass-through"), pBus) { setMonitor(bMonitor); } // Bus-gain command method. bool qtractorBusMonitorCommand::redo (void) { qtractorBus *pBus = bus(); if (pBus == nullptr) return false; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return false; // Set Bus gain (repective monitor gets set too...) const bool bMonitor = pBus->isMonitor(); pBus->setMonitor(qtractorBusCommand::isMonitor()); qtractorBusCommand::setMonitor(bMonitor); // Update (reset) all applicable mixer strips... qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer) { if (pBus->busMode() & qtractorBus::Input) { pMixer->updateBusStrip(pMixer->inputRack(), pBus, qtractorBus::Input, true); } if (pBus->busMode() & qtractorBus::Output) { pMixer->updateBusStrip(pMixer->outputRack(), pBus, qtractorBus::Output, true); } } return true; } //---------------------------------------------------------------------- // class qtractorBusGainCommand - implementation. // // Constructor. qtractorBusGainCommand::qtractorBusGainCommand ( qtractorBus *pBus, qtractorBus::BusMode busMode, float fGain ) : qtractorBusCommand(QObject::tr("bus gain"), pBus, nullptr, busMode) { m_fGain = fGain; m_fPrevGain = 1.0f; qtractorMixerMeter *pMixerMeter = meter(); if (pMixerMeter) m_fPrevGain = pMixerMeter->prevGain(); // Try replacing an previously equivalent command... static qtractorBusGainCommand *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->bus() == pBus && s_pPrevGainCommand->busMode() == busMode) { qtractorBusGainCommand *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; } // Bus-gain command method. bool qtractorBusGainCommand::redo (void) { qtractorBus *pBus = bus(); if (pBus == nullptr) return false; // Set Bus gain (repective monitor gets set too...) const float fGain = m_fPrevGain; qtractorMixerMeter *pMixerMeter = meter(); if (pMixerMeter) pMixerMeter->setGain(m_fGain); // MIDI buses are special... if (pBus->busType() == qtractorTrack::Midi) { // Now we gotta make sure of proper MIDI bus... qtractorMidiBus *pMidiBus = static_cast (pBus); if (pMidiBus) pMidiBus->setMasterVolume(m_fGain); } // Set undo value... m_fPrevGain = m_fGain; m_fGain = fGain; return true; } //---------------------------------------------------------------------- // class qtractorBusPanningCommand - implementation. // // Constructor. qtractorBusPanningCommand::qtractorBusPanningCommand ( qtractorBus *pBus, qtractorBus::BusMode busMode, float fPanning ) : qtractorBusCommand(QObject::tr("bus pan"), pBus, nullptr, busMode) { m_fPanning = fPanning; m_fPrevPanning = 0.0f; qtractorMixerMeter *pMixerMeter = meter(); if (pMixerMeter) m_fPrevPanning = pMixerMeter->prevPanning(); // Try replacing an previously equivalent command... static qtractorBusPanningCommand *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->bus() == pBus && s_pPrevPanningCommand->busMode() == busMode) { qtractorBusPanningCommand *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; } // Bus-panning command method. bool qtractorBusPanningCommand::redo (void) { qtractorBus *pBus = bus(); if (pBus == nullptr) return false; // Set Bus panning (repective monitor gets set too...) const float fPanning = m_fPrevPanning; qtractorMixerMeter *pMixerMeter = meter(); if (pMixerMeter) pMixerMeter->setPanning(m_fPanning); // MIDI buses are special... if (pBus->busType() == qtractorTrack::Midi) { // Now we gotta make sure of proper MIDI bus... qtractorMidiBus *pMidiBus = static_cast (pBus); if (pMidiBus) pMidiBus->setMasterPanning(m_fPanning); } // Set undo value... m_fPrevPanning = m_fPanning; m_fPanning = fPanning; return true; } // end of qtractorEngineCommand.cpp qtractor-1.5.9/src/PaxHeaders/qtractorConnections.cpp0000644000000000000000000000013215101070305017770 xustar0030 mtime=1761898693.068267594 30 atime=1761898693.068267594 30 ctime=1761898693.068267594 qtractor-1.5.9/src/qtractorConnections.cpp0000644000175000001440000001613615101070305017767 0ustar00rncbcusers// qtractorConnections.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 "qtractorConnections.h" #include "qtractorSession.h" #include "qtractorOptions.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include "qtractorMainForm.h" #include "qtractorConnectForm.h" #include #include #include //------------------------------------------------------------------------- // qtractorConnections - Connections dockable window. // // Constructor. qtractorConnections::qtractorConnections ( QWidget *pParent, Qt::WindowFlags wflags ) : QWidget(pParent, wflags) { // Surely a name is crucial (e.g.for storing geometry settings) QWidget::setObjectName("qtractorConnections"); // Create main inner widget. m_pConnectForm = new qtractorConnectForm(this); // Set proper tab widget icons... QTabWidget *pTabWidget = m_pConnectForm->connectTabWidget(); pTabWidget->setTabIcon(0, QIcon::fromTheme("trackAudio")); pTabWidget->setTabIcon(1, QIcon::fromTheme("trackMidi")); // Prepare the layout stuff. QHBoxLayout *pLayout = new QHBoxLayout(); pLayout->setContentsMargins(0, 0, 0, 0); pLayout->setSpacing(0); pLayout->addWidget(m_pConnectForm); QWidget::setLayout(pLayout); // Some specialties to this kind of dock window... QWidget::setMinimumWidth(480); QWidget::setMinimumHeight(240); // Finally set the default caption and tooltip. const QString& sTitle = tr("Connections"); QWidget::setWindowTitle(sTitle); QWidget::setWindowIcon(QIcon::fromTheme("qtractorConnections")); QWidget::setToolTip(sTitle); // Get previously saved splitter sizes, // (with fair default...) qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { QList sizes; sizes.append(180); sizes.append(60); sizes.append(180); pOptions->loadSplitterSizes( m_pConnectForm->audioConnectSplitter(), sizes); pOptions->loadSplitterSizes( m_pConnectForm->midiConnectSplitter(), sizes); } } // Destructor. qtractorConnections::~qtractorConnections (void) { qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { // Get previously saved splitter sizes... pOptions->saveSplitterSizes( m_pConnectForm->audioConnectSplitter()); pOptions->saveSplitterSizes( m_pConnectForm->midiConnectSplitter()); } // No need to delete child widgets, Qt does it all for us. delete m_pConnectForm; } // Just about to notify main-window that we're closing. void qtractorConnections::closeEvent ( QCloseEvent * /*pCloseEvent*/ ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnections::closeEvent()"); #endif QWidget::hide(); qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->stabilizeForm(); } // Connect form accessor. qtractorConnectForm *qtractorConnections::connectForm (void) const { return m_pConnectForm; } // Main bus mode switching. void qtractorConnections::showBus ( qtractorBus *pBus, qtractorBus::BusMode busMode ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const QString sSuffix = ".*"; switch (pBus->busType()) { case qtractorTrack::Audio: { // Show exclusive Audio engine connections... qtractorAudioBus *pAudioBus = static_cast (pBus); if (pAudioBus) { m_pConnectForm->audioClear(); m_pConnectForm->connectTabWidget()->setCurrentIndex(0); if (busMode & qtractorBus::Input) { m_pConnectForm->audioOClientsComboBox()->setCurrentIndex(0); m_pConnectForm->audioIClientsComboBox()->setCurrentIndex( m_pConnectForm->audioIClientsComboBox()->findText( pSession->audioEngine()->clientName())); m_pConnectForm->audioIListView()->setPortName( '^' + pAudioBus->busName() + '/' + sSuffix); } else { m_pConnectForm->audioIClientsComboBox()->setCurrentIndex(0); m_pConnectForm->audioOClientsComboBox()->setCurrentIndex( m_pConnectForm->audioOClientsComboBox()->findText( pSession->audioEngine()->clientName())); m_pConnectForm->audioOListView()->setPortName( '^' + pAudioBus->busName() + '/' + sSuffix); } m_pConnectForm->audioRefresh(); } break; } case qtractorTrack::Midi: { // Show exclusive MIDI engine connections... qtractorMidiBus *pMidiBus = static_cast (pBus); if (pMidiBus) { m_pConnectForm->midiClear(); m_pConnectForm->connectTabWidget()->setCurrentIndex(1); if (busMode & qtractorBus::Input) { m_pConnectForm->midiOClientsComboBox()->setCurrentIndex(0); m_pConnectForm->midiIClientsComboBox()->setCurrentIndex( m_pConnectForm->midiIClientsComboBox()->findText( QString::number(pSession->midiEngine()->alsaClient()) + ':'+ pSession->midiEngine()->clientName())); m_pConnectForm->midiIListView()->setPortName( '^' + QString::number(pMidiBus->alsaPort()) + ':' + pMidiBus->busName() + sSuffix); } else { m_pConnectForm->midiIClientsComboBox()->setCurrentIndex(0); m_pConnectForm->midiOClientsComboBox()->setCurrentIndex( m_pConnectForm->midiOClientsComboBox()->findText( QString::number(pSession->midiEngine()->alsaClient()) + ':' + pSession->midiEngine()->clientName())); m_pConnectForm->midiOListView()->setPortName( '^' + QString::number(pMidiBus->alsaPort()) + ':' + pMidiBus->busName() + sSuffix); } m_pConnectForm->midiRefresh(); } break; } default: break; } // Make it stand out, sure... show(); raise(); activateWindow(); } // Complete connections refreshment. void qtractorConnections::refresh (void) { m_pConnectForm->audioRefresh(); m_pConnectForm->midiRefresh(); } // Complete connections recycle. void qtractorConnections::clear (void) { m_pConnectForm->audioClear(); m_pConnectForm->midiClear(); } // Conditional connections recycle. void qtractorConnections::reset (void) { m_pConnectForm->audioReset(); m_pConnectForm->midiReset(); } // Keyboard event handler. void qtractorConnections::keyPressEvent ( QKeyEvent *pKeyEvent ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorConnections::keyPressEvent(%d)", pKeyEvent->key()); #endif int iKey = pKeyEvent->key(); switch (iKey) { case Qt::Key_Escape: close(); break; default: QWidget::keyPressEvent(pKeyEvent); break; } } // end of qtractorConnections.cpp qtractor-1.5.9/src/PaxHeaders/qtractorTimeScaleForm.ui0000644000000000000000000000013215101070305020033 xustar0030 mtime=1761898693.090267664 30 atime=1761898693.089267661 30 ctime=1761898693.090267664 qtractor-1.5.9/src/qtractorTimeScaleForm.ui0000644000175000001440000003647515101070305020042 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. qtractorTimeScaleForm 0 0 560 287 Qt::StrongFocus Tempo Map / Markers Qt::Horizontal 320 120 Tempo map / Markers true false true true Bar Time Tempo Key Marker 8 0 0 8 4 60 0 &Bar: BarSpinBox 60 0 Bar location 1 99999 1 120 0 T&ime: TimeSpinBox 120 0 Time/frame location Qt::Vertical QSizePolicy::MinimumExpanding 20 8 &Tempo: Qt::AlignRight|Qt::AlignVCenter TempoSpinBox 80 0 Tempo (BPM) / Time signature T&ap Qt::Vertical QSizePolicy::MinimumExpanding 20 8 &Key signature: Qt::AlignRight|Qt::AlignVCenter KeySignatureAccidentalsComboBox Key signature (accidentals) false Key signature (mode) false - Major Minor Qt::Vertical QSizePolicy::MinimumExpanding 20 20 &Marker: MarkerTextLineEdit Marker text 24 24 Marker color ... Qt::Vertical QSizePolicy::MinimumExpanding 20 20 Tempo &scale factor: Qt::AlignRight|Qt::AlignVCenter TempoFactorSpinBox Tempo scale factor true 2 0.01 10.0 0.01 1.0 App&ly 4 0 Refresh tempo map Re&fresh Qt::Horizontal QSizePolicy::MinimumExpanding 20 20 Add node &Add Update node &Update Remove node &Remove Close this dialog Close qtractorTimeSpinBox QSpinBox
qtractorSpinBox.h
qtractorTempoSpinBox QSpinBox
qtractorSpinBox.h
TimeScaleListView BarSpinBox TempoSpinBox TempoTapPushButton KeySignatureAccidentalsComboBox KeySignatureModeComboBox MarkerTextLineEdit MarkerColorToolButton TempoFactorSpinBox TempoFactorPushButton RefreshPushButton AddPushButton UpdatePushButton RemovePushButton ClosePushButton
qtractor-1.5.9/src/PaxHeaders/qtractorMidiControlForm.cpp0000644000000000000000000000013215101070305020555 xustar0030 mtime=1761898693.077267623 30 atime=1761898693.077267623 30 ctime=1761898693.077267623 qtractor-1.5.9/src/qtractorMidiControlForm.cpp0000644000175000001440000006632015101070305020554 0ustar00rncbcusers// qtractorMidiControlForm.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 "qtractorMidiControlForm.h" #include "qtractorMidiControlTypeGroup.h" #include "qtractorOptions.h" #include #include #include #include //---------------------------------------------------------------------- // qtractorMidiControlMapListItem -- Control Map item. // #include "qtractorConnect.h" class qtractorMidiControlMapListItem : public QTreeWidgetItem { public: // Contructor. qtractorMidiControlMapListItem() : QTreeWidgetItem() {} protected: bool operator< ( const QTreeWidgetItem& other ) const { QTreeWidget *parent = QTreeWidgetItem::treeWidget(); if (parent == nullptr) return false; const int col = parent->sortColumn(); if (col < 0) return false; return qtractorClientListView::lessThan(*this, other, col); } }; //---------------------------------------------------------------------- // class qtractorMidiControlForm -- MIDI controller file manager form. // // Constructor. qtractorMidiControlForm::qtractorMidiControlForm ( QWidget *pParent, Qt::WindowFlags wflags ) : QDialog(pParent, wflags) { // Setup UI struct... m_ui.setupUi(this); // Window modality (let plugin/tool windows rave around). QDialog::setWindowModality(Qt::WindowModal); m_iDirtyCount = 0; m_iDirtyMap = 0; m_iUpdating = 0; QHeaderView *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.ControlMapListView->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 m_pControlTypeGroup = new qtractorMidiControlTypeGroup(nullptr, m_ui.ControlTypeComboBox, m_ui.ParamComboBox, m_ui.ParamTextLabel); m_ui.ControlTypeComboBox->setCurrentIndex(3); // Controller (default). // m_ui.ChannelComboBox->clear(); m_ui.ChannelComboBox->addItem("*"); for (unsigned short iChannel = 0; iChannel < 16; ++iChannel) m_ui.ChannelComboBox->addItem(textFromChannel(iChannel)); const QIcon& iconCommand = QIcon::fromTheme("itemChannel"); // m_ui.CommandComboBox->clear(); m_ui.CommandComboBox->addItem(iconCommand, qtractorMidiControl::nameFromCommand(qtractorMidiControl::TRACK_GAIN)); m_ui.CommandComboBox->addItem(iconCommand, qtractorMidiControl::nameFromCommand(qtractorMidiControl::TRACK_PANNING)); m_ui.CommandComboBox->addItem(iconCommand, qtractorMidiControl::nameFromCommand(qtractorMidiControl::TRACK_MONITOR)); m_ui.CommandComboBox->addItem(iconCommand, qtractorMidiControl::nameFromCommand(qtractorMidiControl::TRACK_RECORD)); m_ui.CommandComboBox->addItem(iconCommand, qtractorMidiControl::nameFromCommand(qtractorMidiControl::TRACK_MUTE)); m_ui.CommandComboBox->addItem(iconCommand, qtractorMidiControl::nameFromCommand(qtractorMidiControl::TRACK_SOLO)); m_ui.SyncCheckBox->setChecked(qtractorMidiControl::isSync()); stabilizeTypeChange(); refreshFiles(); adjustSize(); // UI signal/slot connections... QObject::connect(m_ui.FilesListView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(stabilizeForm())); QObject::connect(m_ui.ControlMapListView, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), SLOT(stabilizeForm())); 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_pControlTypeGroup, SIGNAL(controlTypeChanged(int)), SLOT(typeChangedSlot())); QObject::connect(m_pControlTypeGroup, SIGNAL(controlParamChanged(int)), SLOT(keyChangedSlot())); QObject::connect(m_ui.ChannelComboBox, SIGNAL(activated(int)), SLOT(keyChangedSlot())); QObject::connect(m_ui.TrackParamCheckBox, SIGNAL(toggled(bool)), SLOT(keyChangedSlot())); QObject::connect(m_ui.TrackOffsetSpinBox, SIGNAL(valueChanged(int)), SLOT(valueChangedSlot())); QObject::connect(m_ui.TrackLimitSpinBox, SIGNAL(valueChanged(int)), SLOT(valueChangedSlot())); QObject::connect(m_ui.CommandComboBox, SIGNAL(activated(int)), SLOT(valueChangedSlot())); QObject::connect(m_ui.DeltaCheckBox, SIGNAL(toggled(bool)), SLOT(valueChangedSlot())); QObject::connect(m_ui.FeedbackCheckBox, SIGNAL(toggled(bool)), SLOT(valueChangedSlot())); QObject::connect(m_ui.MapPushButton, SIGNAL(clicked()), SLOT(mapSlot())); QObject::connect(m_ui.UnmapPushButton, SIGNAL(clicked()), SLOT(unmapSlot())); QObject::connect(m_ui.SyncCheckBox, SIGNAL(toggled(bool)), SLOT(syncSlot(bool))); QObject::connect(m_ui.ReloadPushButton, SIGNAL(clicked()), SLOT(reloadSlot())); QObject::connect(m_ui.ExportPushButton, SIGNAL(clicked()), SLOT(exportSlot())); QObject::connect(m_ui.ClosePushButton, SIGNAL(clicked()), SLOT(reject())); } // Destructor. qtractorMidiControlForm::~qtractorMidiControlForm (void) { delete m_pControlTypeGroup; } // Reject settings (Cancel button slot). void qtractorMidiControlForm::reject (void) { // Check if there's any pending changes... if (m_iDirtyMap > 0) reloadSlot(); if (m_iDirtyMap == 0) QDialog::reject(); } // Import new instrument file(s) into listing. void qtractorMidiControlForm::importSlot (void) { qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; QStringList files; const QString sExt("qtc"); const QString& sTitle = tr("Import Controller Files"); QStringList filters; filters.append(tr("Controller 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... files = QFileDialog::getOpenFileNames(pParentWidget, sTitle, pOptions->sMidiControlDir, sFilter, nullptr, options); #else // Construct open-files dialog... QFileDialog fileDialog(pParentWidget, sTitle, pOptions->sMidiControlDir, sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::ExistingFiles); fileDialog.setHistory(pOptions->midiControlFiles); fileDialog.setDefaultSuffix(sExt); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); urls.append(QUrl::fromLocalFile(pOptions->sMidiControlDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) files = fileDialog.selectedFiles(); #endif if (files.isEmpty()) return; // Remember this last directory... // For avery selected controller file to load... QTreeWidgetItem *pItem = nullptr; QStringListIterator iter(files); while (iter.hasNext()) { // Merge the file contents into global container... const QString& sPath = iter.next(); // 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) { QFileInfo info(sPath); pItem->setIcon(0, QIcon::fromTheme("itemFile")); pItem->setText(0, info.completeBaseName()); pItem->setText(1, sPath); m_ui.FilesListView->setCurrentItem(pItem); pOptions->sMidiControlDir = info.absolutePath(); } } // Make effect immediately. reloadSlot(); } // Remove a file from controller list. void qtractorMidiControlForm::removeSlot (void) { QTreeWidgetItem *pItem = m_ui.FilesListView->currentItem(); if (pItem == nullptr) return; // 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 controller file:\n\n" "\"%1\"\n\n" "Are you sure?") .arg(pItem->text(1)), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; } // Just do it! delete pItem; // Effect immediate. reloadSlot(); } // Move a file up on the controller list. void qtractorMidiControlForm::moveUpSlot (void) { QTreeWidgetItem *pItem = m_ui.FilesListView->currentItem(); if (pItem) { 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); } } reloadSlot(); } // Move a file down on the controller list. void qtractorMidiControlForm::moveDownSlot (void) { QTreeWidgetItem *pItem = m_ui.FilesListView->currentItem(); if (pItem) { 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); } } reloadSlot(); } // Map current channel/control command. void qtractorMidiControlForm::mapSlot (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; const qtractorMidiControl::ControlType ctype = m_pControlTypeGroup->controlType(); const unsigned short iChannel = channelFromText(m_ui.ChannelComboBox->currentText()); unsigned short iParam = m_pControlTypeGroup->controlParam(); if (m_ui.TrackParamCheckBox->isChecked() && (iChannel & qtractorMidiControl::TrackParam) == 0) iParam |= qtractorMidiControl::TrackParam; const qtractorMidiControl::Command command = qtractorMidiControl::commandFromName( m_ui.CommandComboBox->currentText()); const int iTrack = (m_ui.TrackOffsetSpinBox->value() & 0x07f) | ((m_ui.TrackLimitSpinBox->value() << 7) & 0x3fc0); int iFlags = 0; if (m_ui.DeltaCheckBox->isChecked()) iFlags |= qtractorMidiControl::MapVal::Delta; if (m_ui.FeedbackCheckBox->isChecked()) iFlags |= qtractorMidiControl::MapVal::Feedback; pMidiControl->mapChannelParam( ctype, iChannel, iParam, command, iTrack, iFlags); m_iDirtyCount = 0; ++m_iDirtyMap; refreshControlMap(); } // Unmap current channel/control command. void qtractorMidiControlForm::unmapSlot (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; const qtractorMidiControl::ControlType ctype = m_pControlTypeGroup->controlType(); const unsigned short iChannel = channelFromText(m_ui.ChannelComboBox->currentText()); unsigned short iParam = m_pControlTypeGroup->controlParam(); if (m_ui.TrackParamCheckBox->isChecked() && (iChannel & qtractorMidiControl::TrackParam) == 0) iParam |= qtractorMidiControl::TrackParam; pMidiControl->unmapChannelParam(ctype, iChannel, iParam); m_iDirtyCount = 0; ++m_iDirtyMap; refreshControlMap(); } // Export the whole state into a single controller file. void qtractorMidiControlForm::exportSlot (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; QString sPath; const QString sExt("qtc"); const QString& sTitle = tr("Export Controller File"); QStringList filters; filters.append(tr("Controller files (*.%1)").arg(sExt)); filters.append(tr("All files (*.*)")); const QString& sFilter = filters.join(";;"); if (pOptions->midiControlFiles.isEmpty()) { sPath = QFileInfo(pOptions->sMidiControlDir, tr("controller") + '.' + sExt).absoluteFilePath(); } else sPath = pOptions->midiControlFiles.last(); 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, sPath, sFilter, nullptr, options); #else // Construct open-files dialog... QFileDialog fileDialog(pParentWidget, sTitle, sPath, sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptSave); fileDialog.setFileMode(QFileDialog::AnyFile); fileDialog.setHistory(pOptions->midiControlFiles); fileDialog.setDefaultSuffix(sExt); // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); urls.append(QUrl::fromLocalFile(pOptions->sMidiControlDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sPath = fileDialog.selectedFiles().first(); else sPath.clear(); #endif if (sPath.isEmpty() || sPath.at(0) == '.') return; // Enforce .qtc extension... if (QFileInfo(sPath).suffix().isEmpty()) { sPath += '.' + sExt; // Check if already exists... if (QFileInfo(sPath).exists()) { if (QMessageBox::warning(this, tr("Warning"), tr("The controller file already exists:\n\n" "\"%1\"\n\n" "Do you want to replace it?") .arg(sPath), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; } } // Just save the whole bunch... if (pMidiControl->saveDocument(sPath)) { pOptions->sMidiControlDir = QFileInfo(sPath).absolutePath(); if (m_iDirtyMap > 0 && QMessageBox::warning(this, tr("Warning"), tr("Saved controller mappings may not be effective\n" "the next time you start this program.\n\n" "\"%1\"\n\n" "Do you want to apply to controller files?") .arg(sPath), QMessageBox::Apply | QMessageBox::Ignore) == QMessageBox::Apply) { // Apply by append... pOptions->midiControlFiles.clear(); pOptions->midiControlFiles.append(sPath); // Won't be dirty anymore. m_iDirtyMap = 0; // Make it renew... refreshFiles(); reloadSlot(); } } } // MIDI controller catchup/hook sync state. void qtractorMidiControlForm::syncSlot ( bool bOn ) { qtractorMidiControl::setSync(bOn); } // Reload the complete controller configurations, from list. void qtractorMidiControlForm::reloadSlot (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; // Check if there's any pending map changes... if (m_iDirtyMap > 0 && !pMidiControl->controlMap().isEmpty() && QMessageBox::warning(this, tr("Warning"), tr("Controller mappings have been changed.") + "\n\n" + tr("Do you want to save the changes?"), QMessageBox::Save | QMessageBox::Discard) == QMessageBox::Save) { // Export is save... :) exportSlot(); return; } qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; // Ooops... pMidiControl->clear(); pOptions->midiControlFiles.clear(); // Load each file in order... const int iItemCount = m_ui.FilesListView->topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) { QTreeWidgetItem *pItem = m_ui.FilesListView->topLevelItem(iItem); if (pItem) { const QString& sPath = pItem->text(1); if (pMidiControl->loadDocument(sPath)) pOptions->midiControlFiles.append(sPath); } } // Not dirty anymore... m_iDirtyCount = 0; m_iDirtyMap = 0; refreshControlMap(); } // Mapping type field have changed.. void qtractorMidiControlForm::typeChangedSlot (void) { if (m_iUpdating > 0) return; ++m_iDirtyCount; // stabilizeTypeChange(); stabilizeKeyChange(); } void qtractorMidiControlForm::stabilizeTypeChange (void) { m_pControlTypeGroup->updateControlType(); } // Mapping key fields have changed.. void qtractorMidiControlForm::keyChangedSlot (void) { if (m_iUpdating > 0) return; ++m_iDirtyCount; stabilizeKeyChange(); } void qtractorMidiControlForm::stabilizeKeyChange (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; const QString& sType = m_ui.ControlTypeComboBox->currentText(); const QString& sChannel = m_ui.ChannelComboBox->currentText(); const QString& sParam = m_ui.ParamComboBox->currentText(); const qtractorMidiControl::ControlType ctype = m_pControlTypeGroup->controlType(); const unsigned short iChannel = channelFromText(sChannel); unsigned short iParam = m_pControlTypeGroup->controlParam(); if (m_ui.TrackParamCheckBox->isChecked() && (iChannel & qtractorMidiControl::TrackParam) == 0) iParam |= qtractorMidiControl::TrackParam; const bool bMapped = pMidiControl->isChannelParamMapped(ctype, iChannel, iParam); if (bMapped) { QList items = m_ui.ControlMapListView->findItems(sType, Qt::MatchExactly, 0); QListIterator iter(items); while (iter.hasNext()) { QTreeWidgetItem *pItem = iter.next(); if ((iParam & qtractorMidiControl::TrackParam) == 0 && pItem->text(3)[0] == '+') continue; if (pItem->text(1) == sChannel && pItem->text(2) == sParam) { ++m_iUpdating; m_ui.ControlMapListView->setCurrentItem(pItem); --m_iUpdating; break; } } } m_ui.TrackParamCheckBox->setEnabled( (iChannel & qtractorMidiControl::TrackParam) == 0); const bool bTrackParam = (iParam & qtractorMidiControl::TrackParam); m_ui.TrackLimitTextLabel->setEnabled(bTrackParam); m_ui.TrackLimitSpinBox->setEnabled(bTrackParam); m_ui.MapPushButton->setEnabled(!bMapped && m_iDirtyCount > 0); m_ui.UnmapPushButton->setEnabled(bMapped); } // Mapping value fields have changed.. void qtractorMidiControlForm::valueChangedSlot (void) { if (m_iUpdating > 0) return; ++m_iDirtyCount; stabilizeValueChange(); } void qtractorMidiControlForm::stabilizeValueChange (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; const qtractorMidiControl::ControlType ctype = m_pControlTypeGroup->controlType(); const unsigned short iChannel = channelFromText(m_ui.ChannelComboBox->currentText()); unsigned short iParam = m_pControlTypeGroup->controlParam(); if (m_ui.TrackParamCheckBox->isChecked() && (iChannel & qtractorMidiControl::TrackParam) == 0) iParam |= qtractorMidiControl::TrackParam; const bool bMapped = pMidiControl->isChannelParamMapped(ctype, iChannel, iParam); if (bMapped) { qtractorMidiControl::Command command = qtractorMidiControl::commandFromName( m_ui.CommandComboBox->currentText()); const int iTrack = (m_ui.TrackOffsetSpinBox->value() & 0x07f) | ((m_ui.TrackLimitSpinBox->value() << 7) & 0x3fc0); int iFlags = 0; if (m_ui.DeltaCheckBox->isChecked()) iFlags |= qtractorMidiControl::MapVal::Delta; if (m_ui.FeedbackCheckBox->isChecked()) iFlags |= qtractorMidiControl::MapVal::Feedback; pMidiControl->mapChannelParam( ctype, iChannel, iParam, command, iTrack, iFlags); m_iDirtyCount = 0; ++m_iDirtyMap; refreshControlMap(); } } // Stabilize form status. void qtractorMidiControlForm::stabilizeForm (void) { if (m_iUpdating > 0) return; QTreeWidgetItem *pItem = m_ui.FilesListView->currentItem(); if (pItem) { const int iItem = m_ui.FilesListView->indexOfTopLevelItem(pItem); const int iItemCount = m_ui.FilesListView->topLevelItemCount(); 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); } pItem = m_ui.ControlMapListView->currentItem(); if (pItem) { ++m_iUpdating; m_pControlTypeGroup->setControlType( qtractorMidiControl::typeFromName(pItem->text(0))); m_pControlTypeGroup->setControlParam( // remove non-digits tail... pItem->text(2).remove(QRegularExpression("[\\D]+.*$")).toUShort()); m_ui.ChannelComboBox->setCurrentIndex( m_ui.ChannelComboBox->findText(pItem->text(1))); QString sText = pItem->text(3); QRegularExpression rx("\\+[\\D]+([\\d]+)[\\D]+([\\d]+)"); QRegularExpressionMatch match = rx.match(sText); if (match.hasMatch()) { m_ui.TrackParamCheckBox->setChecked(true); m_ui.TrackOffsetSpinBox->setValue(match.captured(1).toInt()); m_ui.TrackLimitSpinBox->setValue(match.captured(2).toInt()); } else { m_ui.TrackParamCheckBox->setChecked(false); m_ui.TrackOffsetSpinBox->setValue(sText.toInt()); m_ui.TrackLimitSpinBox->setValue(0); } m_ui.CommandComboBox->setCurrentIndex( m_ui.CommandComboBox->findText(pItem->text(4))); sText = pItem->text(5); m_ui.DeltaCheckBox->setChecked(sText.contains(tr("Delta"))); m_ui.FeedbackCheckBox->setChecked(sText.contains(tr("Feedback"))); --m_iUpdating; } m_ui.ReloadPushButton->setEnabled(m_iDirtyMap > 0); qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl) m_ui.ExportPushButton->setEnabled( !pMidiControl->controlMap().isEmpty()); stabilizeKeyChange(); } // Refresh all controller definition views. void qtractorMidiControlForm::refreshFiles (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; // Freeze... m_ui.FilesListView->setUpdatesEnabled(false); // Files list view... m_ui.FilesListView->clear(); QList files; QStringListIterator iter(pOptions->midiControlFiles); while (iter.hasNext()) { const QString& sPath = iter.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); // Bail out... m_ui.FilesListView->setUpdatesEnabled(true); refreshControlMap(); } // Refresh controller map view. void qtractorMidiControlForm::refreshControlMap (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; // Freeze... m_ui.ControlMapListView->setUpdatesEnabled(false); // Control map list view... m_ui.ControlMapListView->clear(); QList items; const qtractorMidiControl::ControlMap& controlMap = pMidiControl->controlMap(); qtractorMidiControl::ControlMap::ConstIterator it = controlMap.constBegin(); const qtractorMidiControl::ControlMap::ConstIterator& it_end = controlMap.constEnd(); const QIcon& iconControlType = QIcon::fromTheme("itemProperty"); const QIcon& iconParam = QIcon::fromTheme("itemControllers"); const QIcon& iconCommand = QIcon::fromTheme("itemChannel"); qtractorMidiControlMapListItem *pItem; for ( ; it != it_end; ++it) { const qtractorMidiControl::MapKey& key = it.key(); const qtractorMidiControl::MapVal& val = it.value(); pItem = new qtractorMidiControlMapListItem(); pItem->setIcon(0, iconControlType); pItem->setText(0, qtractorMidiControl::nameFromType(key.type())); pItem->setText(1, textFromChannel(key.channel())); pItem->setIcon(2, iconParam); pItem->setText(2, textFromParam(key.type(), key.param())); QString sText; if (key.isParamTrack()) { sText = QString("+ %1, %2") .arg(val.trackOffset()) .arg(val.trackLimit()); } else sText = QString::number(val.track()); pItem->setText(3, sText); pItem->setIcon(4, iconCommand); pItem->setText(4, qtractorMidiControl::nameFromCommand(val.command())); QStringList flags; if (val.isDelta()) flags.append(tr("Delta")); if (val.isFeedback()) flags.append(tr("Feedback")); pItem->setText(5, flags.join(", ")); items.append(pItem); } m_ui.ControlMapListView->addTopLevelItems(items); // Bail out... m_ui.ControlMapListView->setUpdatesEnabled(true); stabilizeForm(); } // Channel text conversion helpers. unsigned short qtractorMidiControlForm::channelFromText ( const QString& sText ) const { if (sText == "*" || sText.isEmpty()) return qtractorMidiControl::TrackParam; else return sText.toUShort() - 1; } QString qtractorMidiControlForm::textFromChannel ( unsigned short iChannel ) const { if (iChannel & qtractorMidiControl::TrackParam) return "*"; // "TrackParam"; else return QString::number(iChannel + 1); } // Controller parameter text conversion helpers. unsigned short qtractorMidiControlForm::paramFromText ( qtractorMidiControl::ControlType /*ctype*/, const QString& sText ) const { return sText.section(' ', 0, 0).toUShort(); } QString qtractorMidiControlForm::textFromParam ( qtractorMidiControl::ControlType ctype, unsigned short iParam ) const { iParam &= qtractorMidiControl::TrackParamMask; QString sText; const QString sTextMask("%1 - %2"); switch (ctype) { case qtractorMidiEvent::NOTEON: case qtractorMidiEvent::NOTEOFF: case qtractorMidiEvent::KEYPRESS: sText = sTextMask.arg(iParam) .arg(qtractorMidiEditor::defaultNoteName(iParam)); break; case qtractorMidiEvent::CONTROLLER: sText = sTextMask.arg(iParam) .arg(qtractorMidiEditor::defaultControllerName(iParam)); break; case qtractorMidiEvent::PGMCHANGE: sText = sTextMask.arg(iParam).arg('-'); break; case qtractorMidiEvent::REGPARAM: sText = sTextMask.arg(iParam) .arg(qtractorMidiEditor::defaultRpnNames().value(iParam)); break; case qtractorMidiEvent::NONREGPARAM: sText = sTextMask.arg(iParam) .arg(qtractorMidiEditor::defaultNrpnNames().value(iParam)); break; case qtractorMidiEvent::CONTROL14: sText = sTextMask.arg(iParam) .arg(qtractorMidiEditor::defaultControl14Name(iParam)); break; case qtractorMidiEvent::CHANPRESS: case qtractorMidiEvent::PITCHBEND: default: break; } return sText; } // end of qtractorMidiControlForm.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMidiControl.h0000644000000000000000000000013215101070305017376 xustar0030 mtime=1761898693.077267623 30 atime=1761898693.077267623 30 ctime=1761898693.077267623 qtractor-1.5.9/src/qtractorMidiControl.h0000644000175000001440000003236315101070305017375 0ustar00rncbcusers// qtractorMidiControl.h // /**************************************************************************** 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. *****************************************************************************/ #ifndef __qtractorMidiControl_h #define __qtractorMidiControl_h #include "qtractorCtlEvent.h" #include #include // Needed for the translation functions. #include // Forward declarations. class qtractorTrack; class qtractorDocument; class qtractorMidiControlObserver; class QDomElement; class QWidget; //---------------------------------------------------------------------- // qtractorMidiControl -- MIDI control map (singleton). // class qtractorMidiControl { Q_DECLARE_TR_FUNCTIONS(qtractorMidiControl) public: // Controller types. typedef qtractorMidiEvent::EventType ControlType; // Controller command types. enum Command { TRACK_GAIN = 1, TRACK_PANNING = 2, TRACK_MONITOR = 3, TRACK_RECORD = 4, TRACK_MUTE = 5, TRACK_SOLO = 6 }; // Key param masks (wildcard flags). enum { TrackParam = 0x4000, TrackParamMask = 0x3fff }; // MIDI control map key. class MapKey { public: // Constructor. MapKey(ControlType ctype = qtractorMidiEvent::CONTROLLER, unsigned short iChannel = 0, unsigned short iParam = 0) : m_ctype(ctype), m_iChannel(iChannel), m_iParam(iParam) {} // Type accessors. void setType(ControlType ctype) { m_ctype = ctype; } ControlType type() const { return m_ctype; } // Channel accessors. void setChannel(unsigned short iChannel) { m_iChannel = iChannel; } unsigned short channel() const { return m_iChannel; } bool isChannel() const { return ((m_iChannel & TrackParamMask) == m_iChannel); } bool isChannelTrack() const { return (m_iChannel & TrackParam); } // Controller accessors. void setParam(unsigned short iParam) { m_iParam = iParam; } unsigned short param() const { return m_iParam; } bool isParam() const { return ((m_iParam & TrackParamMask ) == m_iParam); } bool isParamTrack() const { return (m_iParam & TrackParam); } // Hash/map key comparator. bool operator== (const MapKey& key) const { return (key.m_ctype == m_ctype) && (key.m_iChannel == m_iChannel) && (key.m_iParam == m_iParam); } private: // Instance (key) member variables. ControlType m_ctype; unsigned short m_iChannel; unsigned short m_iParam; }; // MIDI control map data value. class MapVal { public: // Bit-wise mode flags. enum Flags { Feedback = 1, Delta = 2 }; // Constructor. MapVal(Command command = Command(0), int iTrack = 0, int iFlags = 0) : m_command(command), m_iTrack(iTrack), m_iFlags(iFlags) {} // Command accessors void setCommand(Command command) { m_command = command; } Command command() const { return m_command; } // Track offset/limit accessors. void setTrack(int iTrack) { m_iTrack = iTrack; } int track() const { return m_iTrack; } int trackOffset() const { return (m_iTrack & 0x007f); } int trackLimit() const { return (m_iTrack & 0x3fc0) >> 7; } // Feedback flag accessor. void setFeedback(bool bFeedback) { if (bFeedback) m_iFlags |= Feedback; else m_iFlags &= ~Feedback; } int isFeedback() const { return (m_iFlags & Feedback); } // Delta/momentary flag accessor. void setDelta(bool bDelta) { if (bDelta) m_iFlags |= Delta; else m_iFlags &= ~Delta; } int isDelta() const { return (m_iFlags & Delta); } // MIDI control track (catch-up) value. class Track { public: // Constructor. Track(float fValue = 0.0f) : m_fValue(fValue), m_bSync(false) {} // Toggled sync methods. bool syncToggled(float fValue, float fOldValue, bool bDelta) { if (bDelta) { // aka. momentary/non-latched... if (fValue > 0.0f) fValue = (fOldValue > 0.0f ? 0.0f : 1.0f); else fValue = fOldValue; } return sync(fValue, fOldValue); } // Scalar sync methods. bool syncDecimal(float fValue, float fOldValue, bool bDelta) { if (bDelta) { // aka. encoded... if (fValue > 0.0f) fValue = fOldValue + 0.1f; else if (fValue < 0.0f) fValue = fOldValue - 0.1f; } return sync(fValue, fOldValue); } // Tracking/catch-up methods. void syncReset() { m_bSync = false; } float value() const { return m_fValue; } protected: bool sync(float fValue, float fOldValue) { bool bSync = qtractorMidiControl::isSync(); if (!bSync) bSync = m_bSync; if (!bSync) { const float v0 = m_fValue; const float v1 = fOldValue; #if 0 if ((fValue > v0 && v1 >= v0 && fValue >= v1) || (fValue < v0 && v0 >= v1 && v1 >= fValue)) bSync = true; #else const float d1 = qAbs(v1 - fValue); const float d2 = qAbs(v1 - v0) * d1; bSync = (d2 < 0.001f); #endif } else if (qAbs(fValue - fOldValue) < 0.001f) bSync = false; if (bSync) { m_fValue = fValue; m_bSync = true; } return bSync; } private: // Tracking/catch-up members. float m_fValue; bool m_bSync; }; Track& track(int iTrack) { return m_trackMap[iTrack]; } void syncReset(int iTrack) { m_trackMap[iTrack].syncReset(); } void clear() { m_trackMap.clear(); } private: // Instance (value) member variables. Command m_command; int m_iTrack; int m_iFlags; QHash m_trackMap; }; // MIDI control map type. typedef QHash ControlMap; // Constructor. qtractorMidiControl(); // Destructor. ~qtractorMidiControl(); static qtractorMidiControl *getInstance(); // Clear control map (reset to default). void clear(); // Clear track (catch-up) map. void clearControlMap(); // Insert new controller mappings. void mapChannelParam(ControlType ctype, unsigned short iChannel, unsigned short iParam, Command command, int iTrack = 0, int iFlags = 0); void mapChannelTrack(ControlType ctype, unsigned short iParam, Command command, int iTrack = 0, int iFlags = 0); void mapChannelParamTrack(ControlType ctype, unsigned short iChannel, unsigned short iParam, Command command, int iTrack = 0, int iFlags = 0); // Remove existing controller mapping. void unmapChannelParam(ControlType ctype, unsigned short iChannel, unsigned short iParam); // Check if given channel, param triplet is currently mapped. bool isChannelParamMapped(ControlType ctype, unsigned short iChannel, unsigned short iParam) const; // Re-send all (track) controllers. void sendAllControllers(int iFirstTrack = 0) const; void sendController( ControlType ctype, unsigned short iChannel, unsigned short iParam, unsigned short iValue) const; // Process incoming controller messages. bool processEvent(const qtractorCtlEvent& ctle); // Process incoming command. void processTrackCommand( Command command, int iTrack, float fValue, bool bLogarithmic = false); void processTrackCommand( Command command, int iTrack, bool bValue); // Control map accessor. const ControlMap& controlMap() const { return m_controlMap; } // Insert/remove observer mappings. void mapMidiObserver( qtractorMidiControlObserver *pMidiObserver, QWidget *pWidget = nullptr); void unmapMidiObserver( qtractorMidiControlObserver *pMidiObserver, bool bResetWidgets = false); // Observer map predicate. bool isMidiObserverMapped(qtractorMidiControlObserver *pMidiObserver) const; // Observer finder. qtractorMidiControlObserver *findMidiObserver( ControlType ctype, unsigned short iChannel, unsigned short iParam) const; // Observer (widget) mappings. void mapMidiObserverWidget( qtractorMidiControlObserver *pMidiObserver, QWidget *pWidget); void unmapMidiObserverWidget( qtractorMidiControlObserver *pMidiObserver, QWidget *pWidget); void unmapMidiObserverWidgets( qtractorMidiControlObserver *pMidiObserver, bool bResetWidgets = false); // Forward declaration. class Document; // Document element methods. bool loadElement(Document *pDocument, QDomElement *pElement); bool saveElement(Document *pDocument, QDomElement *pElement); // Document file methods. bool loadDocument(const QString& sFilename); bool saveDocument(const QString& sFilename); // Parameter controllers (MIDI). struct Controller { QString name; unsigned long index; ControlType ctype; unsigned short channel; unsigned short param; bool logarithmic; bool feedback; bool invert; bool hook; bool latch; }; typedef QList Controllers; // Load/save meter controllers (MIDI). static void loadControllers( QDomElement *pElement, Controllers& controllers); static void saveControllers(qtractorDocument *pDocument, QDomElement *pElement, const Controllers& controllers); // Document textual helpers. static ControlType typeFromText(const QString& sText); static QString textFromType(ControlType ctype); static ControlType typeFromName(const QString& sName); static QString nameFromType(ControlType ctype); static unsigned short keyFromText(const QString& sText); static QString textFromKey(unsigned short iKey); static Command commandFromText(const QString& sText); static QString textFromCommand(Command command); static Command commandFromName(const QString& sName); static QString nameFromCommand(Command command); // MIDI control non catch-up/hook global option. static void setSync(bool bSync); static bool isSync(); protected: // Find incoming controller event map. ControlMap::Iterator findEvent(const qtractorCtlEvent& ctle); // Overloaded controller value senders. void sendTrackController( ControlType ctype, qtractorTrack *pTrack, Command command, unsigned short iChannel, unsigned short iParam) const; void sendTrackController( int iTrack, Command command, float fValue, bool bLogarithmic); // MIDI control scale (7bit vs. 14bit). class ControlScale { public: // Constructor ControlScale(ControlType ctype) { m_iMaxScale = ( ctype == qtractorMidiEvent::PITCHBEND || ctype == qtractorMidiEvent::REGPARAM || ctype == qtractorMidiEvent::NONREGPARAM || ctype == qtractorMidiEvent::CONTROL14 ? 0x3fff : 0x7f); m_fMaxScale = float(m_iMaxScale); const unsigned short mid = (m_iMaxScale >> 1); m_fMidScale = float(mid); m_iMidScale = int(mid + 1); } // Scale value converters (unsigned). float valueFromMidi(unsigned short iValue) const { return float(safeFromMidi(iValue)) / m_fMaxScale; } unsigned short midiFromValue(float fValue) const { return m_fMaxScale * safeFromValue(fValue); } // Scale value converters (signed). float valueSignedFromMidi(unsigned short iValue) const { return float(safeFromMidi(iValue) - m_iMidScale) / m_fMidScale; } unsigned short midiFromValueSigned(float fValue) const { return m_iMidScale + int(m_fMidScale * safeFromValueSigned(fValue)); } // Scale value converters (toggled). float valueToggledFromMidi(unsigned short iValue) const { return (iValue > m_iMidScale ? 1.0f : 0.0f); } unsigned short midiFromValueToggled(float fValue) const { return (fValue > 0.5f ? m_iMaxScale : 0); } protected: // Safe/sanity value cappers. int safeFromMidi(int iValue) const { if (iValue > 0) return (iValue < m_iMaxScale ? iValue : m_iMaxScale); else return 0; } float safeFromValue(float fValue) const { if (fValue > 0.0f) return (fValue < 1.0f ? fValue : 1.0f); else return 0.0f; } float safeFromValueSigned(float fValue) const { if (fValue > 0.0f) return (fValue < 1.0f ? fValue : 1.0f); else if (fValue < 0.0f) return (fValue > -1.0f ? fValue : -1.0f); else return 0.0f; } private: // Scale helpers factors. float m_fMaxScale; float m_fMidScale; int m_iMaxScale; int m_iMidScale; }; // Initialize default control types and command names hash maps. static void initControlTypes(); static void initCommandNames(); private: // MIDI control map. ControlMap m_controlMap; // MIDI observer map. typedef QHash ObserverMap; ObserverMap m_observerMap; typedef QMultiHash WidgetMap; WidgetMap m_widgetMap; // MIDI control non catch-up/hook global option. static bool g_bSync; // Pseudo-singleton instance. static qtractorMidiControl *g_pMidiControl; }; // Hash key function inline uint qHash ( const qtractorMidiControl::MapKey& key ) { return qHash(uint(key.type()) ^ key.channel() ^ key.param()); } #endif // __qtractorMidiControl_h // end of qtractorMidiControl.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiControlPluginWidget.h0000644000000000000000000000013215101070305021721 xustar0030 mtime=1761898693.078267626 30 atime=1761898693.078267626 30 ctime=1761898693.078267626 qtractor-1.5.9/src/qtractorMidiControlPluginWidget.h0000644000175000001440000000416515101070305021717 0ustar00rncbcusers// qtractorMidiControlPluginWidget.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 __qtractorMidiControlPluginWidget_h #define __qtractorMidiControlPluginWidget_h #include "ui_qtractorMidiControlPluginWidget.h" // Forward declarartions. class qtractorMidiControlTypeGroup; class qtractorMidiControlPlugin; //---------------------------------------------------------------------------- // qtractorMidiControlPluginWidget -- UI wrapper form. class qtractorMidiControlPluginWidget : public QWidget { Q_OBJECT public: // Constructor. qtractorMidiControlPluginWidget(QWidget *pParent = nullptr); // Destructor. ~qtractorMidiControlPluginWidget(); // Accessors. void setMidiControlPlugin(qtractorMidiControlPlugin *pMidiControlPlugin); qtractorMidiControlPlugin *midiControlPlugin() const; void dirtyNotify(); protected slots: void changed(); void changedBipolar(); signals: void bipolarChanged(); private: // The Qt-designer UI struct... Ui::qtractorMidiControlPluginWidget m_ui; // Instance variables. qtractorMidiControlTypeGroup *m_pControlTypeGroup; qtractorMidiControlPlugin *m_pMidiControlPlugin; int m_iDirtySetup; }; #endif // __qtractorMidiControlPluginWidget_h // end of qtractorMidiControlPluginWidget.h qtractor-1.5.9/src/PaxHeaders/qtractorAudioMonitor.cpp0000644000000000000000000000013215101070305020117 xustar0030 mtime=1761898693.064267582 30 atime=1761898693.064267582 30 ctime=1761898693.064267582 qtractor-1.5.9/src/qtractorAudioMonitor.cpp0000644000175000001440000003356015101070305020116 0ustar00rncbcusers// qtractorAudioMonitor.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 "qtractorAudioMonitor.h" #include "qtractorAudioMeter.h" #include #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 ( float *pFrames, unsigned int iFrames, float fGain, float *pfValue ) { __m128 v0 = _mm_load_ps1(&fGain); __m128 v1 = _mm_load_ps1(pfValue); __m128 v2; for (; (long(pFrames) & 15) && (iFrames > 0); --iFrames) *pFrames++ *= fGain; for (; iFrames >= 4; iFrames -= 4) { v2 = _mm_mul_ps(_mm_loadu_ps(pFrames), v0); v1 = _mm_max_ps(v2, v1); _mm_store_ps(pFrames, v2); pFrames += 4; } for (; iFrames > 0; --iFrames) *pFrames++ *= fGain; *pfValue = *(float *) &v1; // CHEAT: take 1st of 4 possible values. } static inline void sse_process_ramp ( float *pFrames, unsigned int iFrames, float fGainIter, float fGainLast, float *pfValue ) { __m128 v1 = _mm_load_ps1(pfValue); __m128 v2; const float fGainStep = 4.0f * (fGainLast - fGainIter) / float(iFrames); for (; (long(pFrames) & 15) && (iFrames > 0); --iFrames) *pFrames++ *= fGainIter; for (; iFrames >= 4; iFrames -= 4) { v2 = _mm_mul_ps(_mm_loadu_ps(pFrames), _mm_load_ps1(&fGainIter)); v1 = _mm_max_ps(v2, v1); _mm_store_ps(pFrames, v2); fGainIter += fGainStep; pFrames += 4; } for (; iFrames > 0; --iFrames) *pFrames++ *= fGainIter; *pfValue = *(float *) &v1; // CHEAT: take 1st of 4 possible values. } static inline void sse_process_meter ( float *pFrames, unsigned int iFrames, float *pfValue ) { __m128 v1 = _mm_load_ps1(pfValue); for (; (long(pFrames) & 15) && (iFrames > 0); --iFrames) ++pFrames; for (; iFrames >= 4; iFrames -= 4) { v1 = _mm_max_ps(_mm_loadu_ps(pFrames), v1); pFrames += 4; } *pfValue = *(float *) &v1; // CHEAT: take 1st of 4 possible values. } #endif // __SSE__ #if defined(__ARM_NEON__) #include "arm_neon.h" // NEON enabled processor versions. static inline void neon_process ( float *pFrames, unsigned int iFrames, float fGain, float *pfValue ) { float32x4_t v0 = vld1q_dup_f32(&fGain); float32x4_t v1 = vld1q_dup_f32(pfValue); float32x4_t v2; for (; (long(pFrames) & 15) && (iFrames > 0); --iFrames) *pFrames++ *= fGain; for (; iFrames >= 4; iFrames -= 4) { v2 = vmulq_f32(vld1q_f32(pFrames), v0); v1 = vmaxq_f32(v2, v1); vst1q_f32(pFrames, v2); pFrames += 4; } for (; iFrames > 0; --iFrames) *pFrames++ *= fGain; vst1q_lane_f32(pfValue, v1, 0); // CHEAT: take 1st of 4 possible values. } static inline void neon_process_ramp ( float *pFrames, unsigned int iFrames, float fGainIter, float fGainLast, float *pfValue ) { const float fGainStepSingle = (fGainLast - fGainIter) / float(iFrames); for (; (long(pFrames) & 15) && (iFrames > 0); --iFrames) { *pFrames++ *= fGainIter; fGainIter += fGainStepSingle; } float __attribute__ ((aligned (16))) fInitGainIter[4] = { fGainIter, fGainIter + fGainStepSingle, fGainIter + 2.0f * fGainStepSingle, fGainIter + 3.0f * fGainStepSingle }; const float fGainStep = 4.0f * fGainStepSingle; float32x4_t vGainIter = vld1q_f32(fInitGainIter); float32x4_t vGainStep = vld1q_dup_f32(&fGainStep); float32x4_t v1 = vld1q_dup_f32(pfValue); float32x4_t v2; for (; iFrames >= 4; iFrames -= 4) { v2 = vmulq_f32(vld1q_f32(pFrames), vGainIter); v1 = vmaxq_f32(v2, v1); vst1q_f32(pFrames, v2); vGainIter += vGainStep; pFrames += 4; } for (; iFrames > 0; --iFrames) { *pFrames++ *= fGainIter; fGainIter += fGainStepSingle; } vst1q_lane_f32(pfValue, v1, 3); // CHEAT: take 4th of 4 possible values. } static inline void neon_process_meter ( float *pFrames, unsigned int iFrames, float *pfValue ) { float32x4_t v1 = vld1q_dup_f32(pfValue); for (; (long(pFrames) & 15) && (iFrames > 0); --iFrames) ++pFrames; for (; iFrames >= 4; iFrames -= 4) { v1 = vmaxq_f32(vld1q_f32(pFrames), v1); pFrames += 4; } vst1q_lane_f32(pfValue, v1, 0); // CHEAT: take 1st of 4 possible values. } #endif // __ARM_NEON__ // Standard processor versions. static inline void std_process ( float *pFrames, unsigned int iFrames, float fGain, float *pfValue ) { for (unsigned int n = 0; n < iFrames; ++n) { pFrames[n] *= fGain; if (*pfValue < pFrames[n]) *pfValue = pFrames[n]; } } static inline void std_process_ramp ( float *pFrames, unsigned int iFrames, float fGainIter, float fGainLast, float *pfValue ) { const float fGainStep = (fGainLast - fGainIter) / float(iFrames); for (unsigned int n = 0; n < iFrames; ++n) { pFrames[n] *= fGainIter; if (*pfValue < pFrames[n]) *pfValue = pFrames[n]; fGainIter += fGainStep; } } static inline void std_process_meter ( float *pFrames, unsigned int iFrames, float *pfValue ) { for (unsigned int n = 0; n < iFrames; ++n) { if (*pfValue < pFrames[n]) *pfValue = pFrames[n]; } } //---------------------------------------------------------------------------- // qtractorAudioMonitor -- Audio monitor bridge value processor. // Constructor. qtractorAudioMonitor::qtractorAudioMonitor ( unsigned short iChannels, float fGain, float fPanning ) : qtractorMonitor(fGain, fPanning), m_iChannels(0), m_piStamps(nullptr), m_pfValues(nullptr), m_pfPrevValues(nullptr), m_pfGains(nullptr), m_pfPrevGains(nullptr), m_iProcessRamp(0) { qtractorMonitor::gainSubject()->setMaxValue(2.0f); // +6dB qtractorMonitor::gainObserver()->setLogarithmic(true); #if defined(__SSE__) if (sse_enabled()) { m_pfnProcess = sse_process; m_pfnProcessRamp = sse_process_ramp; m_pfnProcessMeter = sse_process_meter; } else #endif #if defined(__ARM_NEON__) m_pfnProcess = neon_process; m_pfnProcessRamp = neon_process_ramp; m_pfnProcessMeter = neon_process_meter; if (false) #endif { m_pfnProcess = std_process; m_pfnProcessRamp = std_process_ramp; m_pfnProcessMeter = std_process_meter; } setChannels(iChannels); } // Copy constructor. qtractorAudioMonitor::qtractorAudioMonitor ( const qtractorAudioMonitor& monitor ) : qtractorAudioMonitor(monitor.channels(), monitor.gain(), monitor.panning()) { qtractorMonitor::gainSubject()->setDefaultValue( monitor.m_gainSubject.defaultValue()); qtractorMonitor::panningSubject()->setDefaultValue( monitor.m_panningSubject.defaultValue()); } // Destructor. qtractorAudioMonitor::~qtractorAudioMonitor (void) { setChannels(0); } // Channel property accessors. void qtractorAudioMonitor::setChannels ( unsigned short iChannels ) { // Check if channels will really change... if (m_iChannels == iChannels) return; // Delete old value holders... if (m_piStamps) { delete [] m_piStamps; m_piStamps = nullptr; } if (m_pfValues) { delete [] m_pfValues; m_pfValues = nullptr; } if (m_pfPrevValues) { delete [] m_pfPrevValues; m_pfPrevValues = nullptr; } // Delete old panning-gains holders... if (m_pfGains) { delete [] m_pfGains; m_pfGains = nullptr; } if (m_pfPrevGains) { delete [] m_pfPrevGains; m_pfPrevGains = nullptr; } // Set new value holders... m_iChannels = iChannels; if (m_iChannels > 0) { m_piStamps = new unsigned long [m_iChannels]; m_pfValues = new float [m_iChannels]; m_pfPrevValues = new float [m_iChannels]; m_pfGains = new float [m_iChannels]; m_pfPrevGains = new float [m_iChannels]; for (unsigned short i = 0; i < m_iChannels; ++i) { m_piStamps[i] = 0; m_pfValues[i] = m_pfPrevValues[i] = 0.0f; m_pfGains[i] = m_pfPrevGains[i] = 0.0f; } // Initial population... update(); } } unsigned short qtractorAudioMonitor::channels (void) const { return m_iChannels; } // Value holder accessor. float qtractorAudioMonitor::value_stamp ( unsigned short iChannel, unsigned long iStamp ) const { if (m_piStamps[iChannel] != iStamp) { m_piStamps[iChannel] = iStamp; m_pfPrevValues[iChannel] = m_pfValues[iChannel]; m_pfValues[iChannel] = 0.0f; } return m_pfPrevValues[iChannel]; } // Reset channel gain trackers. void qtractorAudioMonitor::reset (void) { for (unsigned short i = 0; i < m_iChannels; ++i) { m_piStamps[i] = 0; m_pfValues[i] = m_pfPrevValues[i] = 0.0f; m_pfPrevGains[i] = 0.0f; } ++m_iProcessRamp; } // Batch processors. void qtractorAudioMonitor::process ( float **ppFrames, unsigned int iFrames, unsigned short iChannels ) { if (iChannels < 1) iChannels = m_iChannels; if (m_iProcessRamp > 0) { m_iProcessRamp = 0; // Do ramp-processing... if (iChannels == m_iChannels) { for (unsigned short i = 0; i < m_iChannels; ++i) { (*m_pfnProcessRamp)(ppFrames[i], iFrames, m_pfPrevGains[i], m_pfGains[i], &m_pfValues[i]); // m_pfPrevGains[i] = m_pfGains[i]; } } else if (iChannels > m_iChannels) { unsigned short i = 0; for (unsigned short j = 0; j < iChannels; ++j) { (*m_pfnProcessRamp)(ppFrames[j], iFrames, m_pfPrevGains[i], m_pfGains[i], &m_pfValues[i]); // m_pfPrevGains[i] = m_pfGains[i]; if (++i >= m_iChannels) i = 0; } } else { // (iChannels < m_iChannels) unsigned short j = 0; for (unsigned short i = 0; i < m_iChannels; ++i) { (*m_pfnProcessRamp)(ppFrames[j], iFrames, m_pfPrevGains[i], m_pfGains[i], &m_pfValues[i]); // m_pfPrevGains[i] = m_pfGains[i]; if (++j >= iChannels) j = 0; } } // Done ramp-processing. } else { // Do normal-processing... if (iChannels == m_iChannels) { for (unsigned short i = 0; i < m_iChannels; ++i) { (*m_pfnProcess)(ppFrames[i], iFrames, m_pfGains[i], &m_pfValues[i]); } } else if (iChannels > m_iChannels) { unsigned short i = 0; for (unsigned short j = 0; j < iChannels; ++j) { (*m_pfnProcess)(ppFrames[j], iFrames, m_pfGains[i], &m_pfValues[i]); if (++i >= m_iChannels) i = 0; } } else { // (iChannels < m_iChannels) unsigned short j = 0; for (unsigned short i = 0; i < m_iChannels; ++i) { (*m_pfnProcess)(ppFrames[j], iFrames, m_pfGains[i], &m_pfValues[i]); if (++j >= iChannels) j = 0; } } // Done normal-processing. } } void qtractorAudioMonitor::process_meter ( float **ppFrames, unsigned int iFrames, unsigned short iChannels ) { if (iChannels < 1) iChannels = m_iChannels; if (iChannels == m_iChannels) { for (unsigned short i = 0; i < m_iChannels; ++i) (*m_pfnProcessMeter)(ppFrames[i], iFrames, &m_pfValues[i]); } else if (iChannels > m_iChannels) { unsigned short j = 0; for (unsigned short i = 0; i < iChannels; ++i) { (*m_pfnProcessMeter)(ppFrames[i], iFrames, &m_pfValues[j]); if (++j >= m_iChannels) j = 0; } } else { // (iChannels < m_iChannels) unsigned short i = 0; for (unsigned short j = 0; j < m_iChannels; ++j) { (*m_pfnProcessMeter)(ppFrames[i], iFrames, &m_pfValues[j]); if (++i >= iChannels) i = 0; } } } // Rebuild the whole panning-gain array... void qtractorAudioMonitor::update (void) { const float fPan = 0.5f * (1.0f + panning()); const float fGain = gain(); float afGains[2] = { fGain, fGain }; // (Re)compute equal-power stereo-panning gains... if (fPan < 0.499f || fPan > 0.501f) { #ifdef QTRACTOR_MONITOR_PANNING_SQRT afGains[0] *= M_SQRT2 * ::sqrtf(1.0f - fPan); afGains[1] *= M_SQRT2 * ::sqrtf(fPan); #else afGains[0] *= M_SQRT2 * ::cosf(fPan * M_PI_2); afGains[1] *= M_SQRT2 * ::sinf(fPan * M_PI_2); #endif } // Apply to multi-channel gain array (paired fashion)... const unsigned short k = (m_iChannels - (m_iChannels & 1)); unsigned short i = 0; for ( ; i < k; ++i) { m_pfPrevGains[i] = m_pfGains[i]; m_pfGains[i] = afGains[i & 1]; } for ( ; i < m_iChannels; ++i) { m_pfPrevGains[i] = m_pfGains[i]; m_pfGains[i] = fGain; } // Trigger ramp-processing... ++m_iProcessRamp; } //---------------------------------------------------------------------------- // qtractorAudioOutputMonitor -- Audio-output monitor bridge value processor. // Constructor. qtractorAudioOutputMonitor::qtractorAudioOutputMonitor ( unsigned short iChannels, float fGain, float fPanning ) : qtractorAudioMonitor(iChannels, fGain, fPanning) { } // Destructor. qtractorAudioOutputMonitor::~qtractorAudioOutputMonitor (void) { setChannels(0); } // Channel property accessors. void qtractorAudioOutputMonitor::setChannels ( unsigned short iChannels ) { qtractorAudioMonitor::setChannels(iChannels); QListIterator iter(m_meters); while (iter.hasNext()) iter.next()->reset(); } // Associated meters (kinda observers) management methods. void qtractorAudioOutputMonitor::addAudioMeter ( qtractorAudioMeter *pAudioMeter ) { m_meters.append(pAudioMeter); } void qtractorAudioOutputMonitor::removeAudioMeter ( qtractorAudioMeter *pAudioMeter ) { m_meters.removeAll(pAudioMeter); } // end of qtractorAudioMonitor.cpp qtractor-1.5.9/src/PaxHeaders/qtractorZipFile.cpp0000644000000000000000000000013215101070305017050 xustar0030 mtime=1761898693.093267673 30 atime=1761898693.093267673 30 ctime=1761898693.093267673 qtractor-1.5.9/src/qtractorZipFile.cpp0000644000175000001440000007034215101070305017046 0ustar00rncbcusers// qtractorZipFile.cpp // /**************************************************************************** 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. *****************************************************************************/ #include "qtractorAbout.h" #ifdef CONFIG_LIBZ /* 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) */ #include "qtractorZipFile.h" #include #define QTRACTOR_PROGRESS_BAR #ifdef QTRACTOR_PROGRESS_BAR #include "qtractorMainForm.h" #include #endif #include #include #include #include #include #if defined(Q_OS_WIN) # undef S_IFREG # undef S_ISDIR # undef S_ISREG # undef S_IRUSR # undef S_IWUSR # undef S_IXUSR # define S_IFREG 0100000 # define S_ISDIR(x) ((x) & 0040000) > 0 # define S_ISREG(x) ((x) & 0170000) == S_IFREG # define S_IFLNK 020000 # define S_ISLNK(x) ((x) & S_IFLNK) > 0 # define S_IRUSR 0400 # define S_IWUSR 0200 # define S_IXUSR 0100 # define S_IRGRP 0040 # define S_IWGRP 0020 # define S_IXGRP 0010 # define S_IROTH 0004 # define S_IWOTH 0002 # define S_IXOTH 0001 #endif #include #define BUFF_SIZE 16384 #if QT_VERSION < QT_VERSION_CHECK(5, 8, 0) #define toSecsSinceEpoch toTime_t #endif static inline unsigned int read_uint ( const unsigned char *data ) { return data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24); } static inline unsigned short read_ushort ( const unsigned char *data ) { return data[0] + (data[1] << 8); } static inline void write_uint ( unsigned char *data, unsigned int i ) { data[0] = i & 0xff; data[1] = (i >> 8) & 0xff; data[2] = (i >> 16) & 0xff; data[3] = (i >> 24) & 0xff; } static inline void write_ushort ( unsigned char *data, ushort i ) { data[0] = i & 0xff; data[1] = (i >> 8) & 0xff; } static inline void copy_uint ( unsigned char *dest, const unsigned char *src ) { dest[0] = src[0]; dest[1] = src[1]; dest[2] = src[2]; dest[3] = src[3]; } static inline void copy_ushort ( unsigned char *dest, const unsigned char *src ) { dest[0] = src[0]; dest[1] = src[1]; } static void write_msdos_date ( unsigned char *data, const QDateTime& dt ) { if (dt.isValid()) { const unsigned short time = (dt.time().hour() << 11) // 5 bit hour | (dt.time().minute() << 5) // 6 bit minute | (dt.time().second() >> 1); // 5 bit double seconds data[0] = time & 0xff; data[1] = time >> 8; const unsigned short date = ((dt.date().year() - 1980) << 9) // 7 bit year 1980-based | (dt.date().month() << 5) // 4 bit month | (dt.date().day()); // 5 bit day data[2] = date & 0xff; data[3] = date >> 8; } else { data[0] = 0; data[1] = 0; data[2] = 0; data[3] = 0; } } static QDateTime read_msdos_date ( unsigned char *data ) { const unsigned short time = data[0] + (data[1] << 8); const unsigned short date = data[2] + (data[3] << 8); const unsigned int year = (date >> 9) + 1980; // 7 bit year 1980-based. const unsigned int mon = (date >> 5) & 0x0f; // 4 bit month. const unsigned int day = (date & 0x1f); // 5 bit day. const unsigned int hh = (time >> 11); // 5 bit hour const unsigned int mm = (time >> 5) & 0x3f; // 6 bit minute const unsigned int ss = (time & 0x1f) << 1; // 5 bit double (m)seconds. return QDateTime(QDate(year, mon, day), QTime(hh, mm, ss)); } static unsigned int mode_from_permissions ( QFile::Permissions perms ) { unsigned int mode = 0; if (perms & QFile::ReadOwner) mode |= S_IRUSR; if (perms & QFile::WriteOwner) mode |= S_IWUSR; if (perms & QFile::ExeOwner) mode |= S_IXUSR; if (perms & QFile::ReadUser) mode |= S_IRUSR; if (perms & QFile::WriteUser) mode |= S_IWUSR; if (perms & QFile::ExeUser) mode |= S_IXUSR; if (perms & QFile::ReadGroup) mode |= S_IRGRP; if (perms & QFile::WriteGroup) mode |= S_IWGRP; if (perms & QFile::ExeGroup) mode |= S_IXGRP; if (perms & QFile::ReadOther) mode |= S_IROTH; if (perms & QFile::WriteOther) mode |= S_IWOTH; if (perms & QFile::ExeOther) mode |= S_IXOTH; return mode; } static QFile::Permissions permissions_from_mode ( unsigned int mode ) { QFile::Permissions perms; if (mode & S_IRUSR) perms |= QFile::ReadOwner; if (mode & S_IWUSR) perms |= QFile::WriteOwner; if (mode & S_IXUSR) perms |= QFile::ExeOwner; if (mode & S_IRUSR) perms |= QFile::ReadUser; if (mode & S_IWUSR) perms |= QFile::WriteUser; if (mode & S_IXUSR) perms |= QFile::ExeUser; if (mode & S_IRGRP) perms |= QFile::ReadGroup; if (mode & S_IWGRP) perms |= QFile::WriteGroup; if (mode & S_IXGRP) perms |= QFile::ExeGroup; if (mode & S_IROTH) perms |= QFile::ReadOther; if (mode & S_IWOTH) perms |= QFile::WriteOther; if (mode & S_IXOTH) perms |= QFile::ExeOther; return perms; } struct LocalFileHeader { unsigned char signature[4]; // 0x04034b50 unsigned char version_needed[2]; unsigned char general_purpose_bits[2]; unsigned char compression_method[2]; unsigned char last_mod_file[4]; unsigned char crc_32[4]; unsigned char compressed_size[4]; unsigned char uncompressed_size[4]; unsigned char file_name_length[2]; unsigned char extra_field_length[2]; }; struct CentralFileHeader { unsigned char signature[4]; // 0x02014b50 unsigned char version_made[2]; unsigned char version_needed[2]; unsigned char general_purpose_bits[2]; unsigned char compression_method[2]; unsigned char last_mod_file[4]; unsigned char crc_32[4]; unsigned char compressed_size[4]; unsigned char uncompressed_size[4]; unsigned char file_name_length[2]; unsigned char extra_field_length[2]; unsigned char file_comment_length[2]; unsigned char disk_start[2]; unsigned char internal_file_attributes[2]; unsigned char external_file_attributes[4]; unsigned char offset_local_header[4]; }; struct EndOfDirectory { unsigned char signature[4]; // 0x06054b50 unsigned char this_disk[2]; unsigned char start_of_directory_disk[2]; unsigned char num_dir_entries_this_disk[2]; unsigned char num_dir_entries[2]; unsigned char directory_size[4]; unsigned char dir_start_offset[4]; unsigned char comment_length[2]; }; struct FileHeader { CentralFileHeader h; QByteArray file_name; QByteArray extra_field; QByteArray file_comment; }; static void copy_header ( LocalFileHeader& lfh, const CentralFileHeader& h ) { write_uint(lfh.signature, 0x04034b50); copy_ushort(lfh.version_needed, h.version_needed); copy_ushort(lfh.general_purpose_bits, h.general_purpose_bits); copy_ushort(lfh.compression_method, h.compression_method); copy_uint(lfh.last_mod_file, h.last_mod_file); copy_uint(lfh.crc_32, h.crc_32); copy_uint(lfh.compressed_size, h.compressed_size); copy_uint(lfh.uncompressed_size, h.uncompressed_size); copy_ushort(lfh.file_name_length, h.file_name_length); copy_ushort(lfh.extra_field_length, h.extra_field_length); } //---------------------------------------------------------------------------- // qtractorZipDevice -- Common ZIP I/O device class. // class qtractorZipDevice { public: qtractorZipDevice (QIODevice *pDevice, bool bOwnDevice) : device(pDevice), own_device(bOwnDevice), status(qtractorZipFile::NoError), dirty_contents(true), total_uncompressed(0), total_compressed(0), total_processed(0), buff_read(new unsigned char [BUFF_SIZE]), buff_write(new unsigned char [BUFF_SIZE]), write_offset(0) { #ifdef QTRACTOR_PROGRESS_BAR qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); progress_bar = (pMainForm ? pMainForm->progressBar() : nullptr); #endif } ~qtractorZipDevice() { delete [] buff_read; delete [] buff_write; if (own_device) delete device; } void scanFiles(); bool extractEntry(const QString& sFilename, const FileHeader& fh); bool extractAll(); void setPrefix(const QString& sPrefix); const QString& prefix() const; QString alias(const QString& sFilename, const QString& sPrefix = QString(), bool bTemp = false) const; enum EntryType { File = 0, Directory, SymLink }; bool addEntry(EntryType type, const QString& sFilename, const QString& sAlias = QString()); bool processEntry(const QString& sFilename, FileHeader& fh); bool processAll(); QIODevice *device; bool own_device; qtractorZipFile::Status status; bool dirty_contents; QString file_prefix; QMultiHash file_headers; QHash file_aliases; QByteArray comment; unsigned int total_uncompressed; unsigned int total_compressed; unsigned int total_processed; unsigned char *buff_read; unsigned char *buff_write; unsigned int write_offset; #ifdef QTRACTOR_PROGRESS_BAR QProgressBar *progress_bar; #endif }; //---------------------------------------------------------------------------- // qtractorZipDevice -- Common ZIP I/O device class. // // Scans and reads zip archive entry directory (read-only). void qtractorZipDevice::scanFiles (void) { if (!dirty_contents) return; if (!(device->isOpen() || device->open(QIODevice::ReadOnly))) { status = qtractorZipFile::FileOpenError; return; } if (!(device->openMode() & QIODevice::ReadOnly)) { status = qtractorZipFile::FileReadError; return; } total_uncompressed = 0; total_compressed = 0; total_processed = 0; file_headers.clear(); dirty_contents = false; unsigned char tmp[4]; device->read((char *) tmp, 4); if (read_uint(tmp) != 0x04034b50) { qWarning("qtractorZipDevice::scanFiles: not a zip file."); return; } // Find EndOfDirectory header... int i = 0; EndOfDirectory eod; for (;;) { const qint64 pos = device->size() - sizeof(EndOfDirectory) - i; if (pos < 0 || i > 65535) { qWarning("qtractorZipDevice::scanFiles: " "end-of-directory not found."); return; } device->seek(pos); device->read((char *) &eod, sizeof(EndOfDirectory)); if (read_uint(eod.signature) == 0x06054b50) break; ++i; } // Have the eod... const qint64 dir_start_offset = read_uint(eod.dir_start_offset); const int num_dir_entries = read_ushort(eod.num_dir_entries); const int comment_length = read_ushort(eod.comment_length); if (comment_length != i) qWarning("qtractorZipDevice::scanFiles: failed to parse zip file."); comment = device->read(qMin(comment_length, i)); device->seek(dir_start_offset); for (i = 0; i < num_dir_entries; ++i) { FileHeader fh; int nread = device->read((char *) &fh.h, sizeof(CentralFileHeader)); if (nread < (int) sizeof(CentralFileHeader)) { qWarning("qtractorZipDevice::scanFiles: " "failed to read complete header, " "index may be incomplete."); break; } if (read_uint(fh.h.signature) != 0x02014b50) { qWarning("qtractorZipDevice::scanFiles: " "invalid header signature, " "zip index may be incomplete."); break; } nread = read_ushort(fh.h.file_name_length); fh.file_name = device->read(nread); if (fh.file_name.length() != nread) { qWarning("qtractorZipDevice::scanFiles: " "failed to read filename, " "zip index may be incomplete."); break; } nread = read_ushort(fh.h.extra_field_length); fh.extra_field = device->read(nread); if (fh.extra_field.length() != nread) { qWarning("qtractorZipDevice::scanFiles: " "failed to read extra field, skipping, " "zip index may be incomplete"); break; } nread = read_ushort(fh.h.file_comment_length); fh.file_comment = device->read(nread); if (fh.file_comment.length() != nread) { qWarning("qtractorZipDevice::scanFiles: " "failed to read file comment, " "index may be incomplete"); break; } total_uncompressed += read_uint(fh.h.uncompressed_size); total_compressed += read_uint(fh.h.compressed_size); file_headers.insert(QString::fromLocal8Bit(fh.file_name), fh); } } // Extract contents of a zip archive file entry (read-only). bool qtractorZipDevice::extractEntry ( const QString& sFilename, const FileHeader& fh ) { if (!(device->isOpen() || device->open(QIODevice::ReadOnly))) { status = qtractorZipFile::FileOpenError; return false; } if (!(device->openMode() & QIODevice::ReadOnly)) { status = qtractorZipFile::FileReadError; return false; } QFileInfo info(sFilename); if (!info.dir().exists()) QDir().mkpath(info.dir().path()); QFile *pFile = nullptr; const unsigned int mode = read_uint(fh.h.external_file_attributes) >> 16; if (S_ISREG(mode)) { pFile = new QFile(info.filePath()); if (!pFile->open(QIODevice::WriteOnly)) { status = qtractorZipFile::FileError; delete pFile; return false; } } else if (S_ISDIR(mode)) return QDir().mkpath(info.filePath()); else if (pFile == nullptr) return false; const unsigned int uncompressed_size = read_uint(fh.h.uncompressed_size); const unsigned int compressed_size = read_uint(fh.h.compressed_size); if (uncompressed_size == 0 || compressed_size == 0) return false; device->seek(read_uint(fh.h.offset_local_header)); LocalFileHeader lfh; device->read((char *) &lfh, sizeof(LocalFileHeader)); const unsigned int skip = read_ushort(lfh.file_name_length) + read_ushort(lfh.extra_field_length); device->seek(device->pos() + skip); ushort compression_method = read_ushort(lfh.compression_method); if (compression_method == 8) { unsigned int nread = 0; unsigned int nwrite = 0; z_stream zstream; ::memset(&zstream, 0, sizeof(zstream)); unsigned int crc_32 = ::crc32(0, 0, 0); int zrc = ::inflateInit2(&zstream, -MAX_WBITS); while (zrc != Z_STREAM_END) { unsigned int nbuff = BUFF_SIZE; if (nread + BUFF_SIZE > compressed_size) nbuff = compressed_size - nread; device->read((char *) buff_read, nbuff); nread += nbuff; zstream.next_in = (uchar *) buff_read; zstream.avail_in = (uint) nbuff; do { nbuff = BUFF_SIZE; zstream.next_out = (uchar *) buff_write; zstream.avail_out = (uint) nbuff; zrc = ::inflate(&zstream, Z_NO_FLUSH); if (zrc != Z_STREAM_ERROR) { nbuff -= zstream.avail_out; if (nbuff > 0) { pFile->write((const char *) buff_write, nbuff); crc_32 = ::crc32(crc_32, (const uchar *) buff_write, (ulong) nbuff); nwrite += nbuff; total_processed += nbuff; } } } while (zstream.avail_out == 0); #ifdef QTRACTOR_PROGRESS_BAR if (progress_bar) progress_bar->setValue( (100.0f * float(total_processed)) / float(total_uncompressed)); #endif } // uncompressed_size = n_file_write; ::inflateEnd(&zstream); if (crc_32 != read_uint(lfh.crc_32)) qWarning("qtractorZipDevice::extractEntry: bad CRC32!"); } else { // No compression... QByteArray data = device->read(compressed_size); data.truncate(uncompressed_size); pFile->write(data); } pFile->setPermissions(permissions_from_mode(S_IRUSR | S_IWUSR | mode)); pFile->close(); delete pFile; // Set file time using utime... struct utimbuf utb; const long tse = read_msdos_date(lfh.last_mod_file).toSecsSinceEpoch(); utb.actime = tse; utb.modtime = tse; if (::utime(fh.file_name.data(), &utb)) qWarning("qtractorZipDevice::extractEntry: failed to set file time."); #ifdef CONFIG_DEBUG qDebug("qtractorZipDevice::inflate(%3.0f%%) %s", (100.0f * float(total_processed)) / float(total_uncompressed), fh.file_name.data()); #endif return true; } // Extract the full contents of the zip file (read-only). bool qtractorZipDevice::extractAll (void) { scanFiles(); #ifdef QTRACTOR_PROGRESS_BAR if (progress_bar) { progress_bar->setRange(0, 100); progress_bar->reset(); progress_bar->show(); } #endif int iExtracted = 0; QMultiHash::ConstIterator iter = file_headers.constBegin(); const QMultiHash::ConstIterator& iter_end = file_headers.constEnd(); for ( ; iter != iter_end; ++iter) { if (extractEntry(iter.key(), iter.value())) ++iExtracted; } #ifdef QTRACTOR_PROGRESS_BAR if (progress_bar) progress_bar->hide(); #endif return (iExtracted == file_headers.count()); } // Fake directory prefix accessors. void qtractorZipDevice::setPrefix ( const QString& sPrefix ) { file_prefix = sPrefix.simplified(); } const QString& qtractorZipDevice::prefix (void) const { return file_prefix; } // Returns an zip archive entry alias name avoiding duplicates (write-only). QString qtractorZipDevice::alias ( const QString& sFilename, const QString& sPrefix, bool bTemp ) const { const QFileInfo info(sFilename); QString sAliasPrefix = sPrefix.simplified(); QString sAliasName = info.completeBaseName(); QString sAliasSuffix = info.suffix(); if (!sAliasPrefix.isEmpty()) sAliasPrefix.remove(QRegularExpression("[\\/]+$")); // remove any slash tail... if (!sAliasSuffix.isEmpty()) sAliasSuffix.prepend('.'); QString sAlias = sAliasPrefix; if (!sAlias.isEmpty()) sAlias.append('/'); sAlias.append(sAliasName); sAlias.append(sAliasSuffix); if (!bTemp && !file_headers.contains(info.absoluteFilePath())) { const QRegularExpression rxDashNumber("\\-[0-9]+$"); int i = 0; while (file_aliases.contains(sAlias)) { const QString sDashNumber = '-' + QString::number(++i); if (sAliasPrefix.isEmpty()) { if (i == 0) sAliasName.remove(rxDashNumber); sAlias = sAliasName + sDashNumber; } else { if (i == 0) sAliasPrefix.remove(rxDashNumber); sAlias = sAliasPrefix + sDashNumber + '/' + sAliasName; } sAlias.append(sAliasSuffix); } } return sAlias; } // Add an entry to a zip archive (write-only). bool qtractorZipDevice::addEntry ( EntryType type, const QString& sFilename, const QString& sAlias ) { const QFileInfo info(sFilename); if (info.isDir()) type = Directory; else if (info.isSymLink()) type = File; /* OVERRIDE */ if (type == File && !info.exists()) return false; const QString& sFilepath = info.absoluteFilePath(); QString sFakepath = (sAlias.isEmpty() ? alias(sFilepath) : sAlias); if (sFakepath.isEmpty()) { if (info.isAbsolute()) { sFakepath = sFilepath; sFakepath.remove(QRegularExpression('^' + QDir::rootPath())); } else { sFakepath = info.filePath(); sFakepath.remove(QRegularExpression("^[\\./]+")); } } if (file_aliases.contains(sFakepath)) return true; FileHeader fh; ::memset(&fh.h, 0, sizeof(CentralFileHeader)); write_uint(fh.h.signature, 0x02014b50); write_ushort(fh.h.version_needed, 0x14); write_uint(fh.h.uncompressed_size, info.size()); write_msdos_date(fh.h.last_mod_file, info.lastModified()); total_uncompressed += info.size(); QString sFakename = file_prefix; if (!sFakename.isEmpty() && !sFakename.endsWith('/')) sFakename.append('/'); sFakename.append(sFakepath); fh.file_name = sFakename.toLocal8Bit(); write_ushort(fh.h.file_name_length, fh.file_name.length()); write_ushort(fh.h.version_made, 3 << 8); unsigned int mode = mode_from_permissions(info.permissions()); switch (type) { case File: mode |= S_IFREG; break; case Directory: mode |= S_IFDIR; break; case SymLink: mode |= S_IFLNK; break; } write_uint(fh.h.external_file_attributes, mode << 16); write_uint(fh.h.offset_local_header, 0); /* DEFERRED (write_offset) */ write_ushort(fh.h.compression_method, 0); /* DEFERRED */ file_headers.insert(sFilepath, fh); file_aliases.insert(sFakepath, file_aliases.count()); dirty_contents = true; return true; } // Process contents of zip archive entry (write-only). bool qtractorZipDevice::processEntry ( const QString& sFilename, FileHeader& fh ) { if (!(device->isOpen() || device->open(QIODevice::WriteOnly))) { status = qtractorZipFile::FileOpenError; return false; } if (!(device->openMode() & QIODevice::WriteOnly)) { status = qtractorZipFile::FileWriteError; return false; } QFile *pFile = nullptr; const unsigned int mode = read_uint(fh.h.external_file_attributes) >> 16; if (S_ISREG(mode)) { pFile = new QFile(sFilename); if (!pFile->open(QIODevice::ReadOnly)) { status = qtractorZipFile::FileError; delete pFile; return false; } } const unsigned int uncompressed_size = read_uint(fh.h.uncompressed_size); unsigned int compressed_size = 0; device->seek(write_offset); LocalFileHeader lfh; copy_header(lfh, fh.h); device->write((char *) &lfh, sizeof(LocalFileHeader)); device->write(fh.file_name); unsigned int crc_32 = ::crc32(0, 0, 0); if (pFile) { write_ushort(fh.h.compression_method, 8); /* DEFERRED */ unsigned int nread = 0; unsigned int nwrite = 0; z_stream zstream; ::memset(&zstream, 0, sizeof(zstream)); int zrc = ::deflateInit2(&zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); while (zrc != Z_STREAM_END) { unsigned int nbuff = BUFF_SIZE; if (nread + BUFF_SIZE > uncompressed_size) nbuff = uncompressed_size - nread; const int zflush = (nbuff < BUFF_SIZE ? Z_FINISH : Z_NO_FLUSH); pFile->read((char *) buff_read, nbuff); crc_32 = ::crc32(crc_32, (const uchar *) buff_read, (ulong) nbuff); nread += nbuff; total_processed += nbuff; zstream.next_in = (uchar *) buff_read; zstream.avail_in = (uint) nbuff; do { nbuff = BUFF_SIZE; zstream.next_out = (uchar *) buff_write; zstream.avail_out = (uint) nbuff; zrc = ::deflate(&zstream, zflush); if (zrc != Z_STREAM_ERROR) { nbuff -= zstream.avail_out; if (nbuff > 0) { device->write((const char *) buff_write, nbuff); nwrite += nbuff; } } } while (zstream.avail_out == 0); #ifdef QTRACTOR_PROGRESS_BAR if (progress_bar) progress_bar->setValue( (100.0f * float(total_processed)) / float(total_uncompressed)); #endif } compressed_size = nwrite; ::deflateEnd(&zstream); pFile->close(); delete pFile; } const unsigned int last_offset = device->pos(); // Rewrite updated header... total_compressed += compressed_size; write_uint(fh.h.compressed_size, compressed_size); write_uint(fh.h.crc_32, crc_32); device->seek(write_offset); copy_header(lfh, fh.h); device->write((char *) &lfh, sizeof(LocalFileHeader)); write_uint(fh.h.offset_local_header, write_offset); /* DEFERRED */ // Done for next item so far... write_offset = last_offset; #ifdef CONFIG_DEBUG qDebug("qtractorZipDevice::deflate(%3.0f%%) %s.", (100.0f * float(total_processed)) / float(total_uncompressed), fh.file_name.data()); #endif return true; } // Process the full contents of the zip file (write-only). bool qtractorZipDevice::processAll (void) { #ifdef QTRACTOR_PROGRESS_BAR if (progress_bar) { progress_bar->setRange(0, 100); progress_bar->reset(); progress_bar->show(); } #endif int iProcessed = 0; QMultiHash::Iterator iter = file_headers.begin(); const QMultiHash::Iterator& iter_end = file_headers.end(); for ( ; iter != iter_end; ++iter) { if (!processEntry(iter.key(), iter.value())) break; ++iProcessed; } #ifdef QTRACTOR_PROGRESS_BAR if (progress_bar) progress_bar->hide(); #endif return (iProcessed == file_headers.count()); } //---------------------------------------------------------------------------- // qtractorZipFile -- Custom ZIP file archive class. // // Constructors. qtractorZipFile::qtractorZipFile ( const QString& sFilename, QIODevice::OpenMode mode ) { QFile *pFile = new QFile(sFilename); pFile->open(mode); qtractorZipFile::Status status; if (pFile->error() == QFile::NoError) status = NoError; else if (pFile->error() == QFile::ReadError) status = FileReadError; else if (pFile->error() == QFile::WriteError) status = FileWriteError; else if (pFile->error() == QFile::OpenError) status = FileOpenError; else if (pFile->error() == QFile::PermissionsError) status = FilePermissionsError; else status = FileError; m_pZip = new qtractorZipDevice(pFile, /*bOwnDevice=*/true); m_pZip->status = status; } qtractorZipFile::qtractorZipFile ( QIODevice *pDevice ) : m_pZip(new qtractorZipDevice(pDevice, /*bOwnDevice=*/false)) { Q_ASSERT(pDevice); } // Desctructor qtractorZipFile::~qtractorZipFile (void) { close(); delete m_pZip; } // Returns a status code indicating the first error met. qtractorZipFile::Status qtractorZipFile::status (void) const { return m_pZip->status; } // Returns true if the user can read the file; // otherwise returns false. bool qtractorZipFile::isReadable (void) const { return (m_pZip->status == NoError) && m_pZip->device->isReadable(); } // Returns true if the user can write the file; // otherwise returns false. bool qtractorZipFile::isWritable (void) const { return (m_pZip->status == NoError) && m_pZip->device->isWritable(); } // Returns true if the file exists; otherwise returns false. bool qtractorZipFile::exists (void) const { QFile *pFile = qobject_cast (m_pZip->device); return (pFile ? pFile->exists() : false); } // Extract file contents from the zip archive (read-only). bool qtractorZipFile::extractFile ( const QString& sFilename ) { m_pZip->scanFiles(); if (!m_pZip->file_headers.contains(sFilename)) return false; return m_pZip->extractEntry(sFilename, m_pZip->file_headers.value(sFilename)); } // Extracts the full contents of the zip archive (read-only). bool qtractorZipFile::extractAll (void) { return m_pZip->extractAll(); } // Fake directory prefix accessors. void qtractorZipFile::setPrefix ( const QString& sPrefix ) { m_pZip->setPrefix(sPrefix); } const QString& qtractorZipFile::prefix (void) const { return m_pZip->prefix(); } // Returns the possible alias name avoiding file entry duplicates (write-only). QString qtractorZipFile::alias ( const QString& sFilename, const QString& sPrefix, bool bTemp ) const { return m_pZip->alias(sFilename, sPrefix, bTemp); } // Add a file in the zip archive (write-only). bool qtractorZipFile::addFile ( const QString& sFilename, const QString& sAlias ) { return m_pZip->addEntry(qtractorZipDevice::File, sFilename, sAlias); } // Add a directory in the zip archive (write-only). bool qtractorZipFile::addDirectory ( const QString& sDirectory ) { QString sFilename = sDirectory; if (!sFilename.endsWith('/')) sFilename.append('/'); return m_pZip->addEntry(qtractorZipDevice::Directory, sFilename); } // Process the full contents of the zip archive (write-only). bool qtractorZipFile::processAll (void) { return m_pZip->processAll(); } // Closes the zip file. void qtractorZipFile::close (void) { if (!(m_pZip->device->openMode() & QIODevice::WriteOnly)) { m_pZip->device->close(); return; } m_pZip->device->seek(m_pZip->write_offset); // Write new directory... QMultiHash::ConstIterator iter = m_pZip->file_headers.constBegin(); const QMultiHash::ConstIterator iter_end = m_pZip->file_headers.constEnd(); for ( ; iter != iter_end; ++iter) { const FileHeader& fh = iter.value(); m_pZip->device->write((const char *) &fh.h, sizeof(CentralFileHeader)); m_pZip->device->write(fh.file_name); m_pZip->device->write(fh.extra_field); m_pZip->device->write(fh.file_comment); } const int dir_size = m_pZip->device->pos() - m_pZip->write_offset; // Write end of directory... EndOfDirectory eod; ::memset(&eod, 0, sizeof(EndOfDirectory)); write_uint(eod.signature, 0x06054b50); write_ushort(eod.num_dir_entries_this_disk, m_pZip->file_headers.size()); write_ushort(eod.num_dir_entries, m_pZip->file_headers.size()); write_uint(eod.directory_size, dir_size); write_uint(eod.dir_start_offset, m_pZip->write_offset); write_ushort(eod.comment_length, m_pZip->comment.length()); m_pZip->device->write((const char *) &eod, sizeof(EndOfDirectory)); m_pZip->device->write(m_pZip->comment); m_pZip->device->close(); } // Statistical accessors. unsigned int qtractorZipFile::totalUncompressed (void) const { return m_pZip->total_uncompressed; } unsigned int qtractorZipFile::totalCompressed (void) const { return m_pZip->total_compressed; } unsigned int qtractorZipFile::totalProcessed (void) const { return m_pZip->total_processed; } #endif // CONFIG_LIBZ // end of qtractorZipFile.cpp qtractor-1.5.9/src/PaxHeaders/qtractorAudioMeter.cpp0000644000000000000000000000013215101070305017544 xustar0030 mtime=1761898693.064267582 30 atime=1761898693.064267582 30 ctime=1761898693.064267582 qtractor-1.5.9/src/qtractorAudioMeter.cpp0000644000175000001440000004206415101070305017542 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.9/src/PaxHeaders/qtractorBusForm.h0000644000000000000000000000013215101070305016530 xustar0030 mtime=1761898693.065267585 30 atime=1761898693.065267585 30 ctime=1761898693.065267585 qtractor-1.5.9/src/qtractorBusForm.h0000644000175000001440000000470315101070305016524 0ustar00rncbcusers// qtractorBusForm.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 __qtractorBusForm_h #define __qtractorBusForm_h #include "ui_qtractorBusForm.h" // Forward declarations... class qtractorBus; //---------------------------------------------------------------------------- // qtractorBusForm -- UI wrapper form. class qtractorBusForm : public QDialog { Q_OBJECT public: // Constructor. qtractorBusForm(QWidget *pParent = nullptr); void setBus(qtractorBus *pBus); qtractorBus *bus(); bool isDirty(); protected slots: void reject(); void selectBus(); void moveUpBus(); void moveDownBus(); void createBus(); void updateBus(); void deleteBus(); void changed(); void stabilizeForm(); void contextMenu(const QPoint&); void midiSysex(); void addInputPlugin(); void removeInputPlugin(); void moveUpInputPlugin(); void moveDownInputPlugin(); void addOutputPlugin(); void removeOutputPlugin(); void moveUpOutputPlugin(); void moveDownOutputPlugin(); protected: enum { Create = 1, Update = 2, Delete = 4, MoveUp = 8, MoveDown = 16 }; unsigned int flags() const; void showBus(qtractorBus *pBus); bool updateBus(qtractorBus *pBus); void updateMidiInstruments(); void updateMidiSysex(); void resetPluginLists(); void refreshBuses(); private: // The Qt-designer UI struct... Ui::qtractorBusForm m_ui; // Instance variables... qtractorBus *m_pBus; QTreeWidgetItem *m_pAudioRoot; QTreeWidgetItem *m_pMidiRoot; int m_iDirtySetup; int m_iDirtyCount; int m_iDirtyTotal; }; #endif // __qtractorBusForm_h // end of qtractorBusForm.h qtractor-1.5.9/src/PaxHeaders/qtractorTrackView.h0000644000000000000000000000013215101070305017052 xustar0030 mtime=1761898693.091267667 30 atime=1761898693.091267667 30 ctime=1761898693.091267667 qtractor-1.5.9/src/qtractorTrackView.h0000644000175000001440000004312215101070305017044 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.9/src/PaxHeaders/qtractorAudioEngine.h0000644000000000000000000000013215101070305017342 xustar0030 mtime=1761898693.063267578 30 atime=1761898693.063267578 30 ctime=1761898693.063267578 qtractor-1.5.9/src/qtractorAudioEngine.h0000644000175000001440000003545015101070305017341 0ustar00rncbcusers// qtractorAudioEngine.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 __qtractorAudioEngine_h #define __qtractorAudioEngine_h #include "qtractorAtomic.h" #include "qtractorEngine.h" #include #include // Forward declarations. class qtractorAudioBus; class qtractorAudioBuffer; class qtractorAudioMonitor; class qtractorAudioFile; class qtractorAudioExportBuffer; class qtractorPluginList; class qtractorCurveList; //---------------------------------------------------------------------- // class qtractorAudioEngineProxy -- Audio engine event proxy object. // class qtractorAudioEngineProxy : public QObject { Q_OBJECT public: // Constructor. qtractorAudioEngineProxy(QObject *pParent = nullptr) : QObject(pParent) {} // Event notifications. void notifyShutEvent() { emit shutEvent(); } void notifyXrunEvent() { emit xrunEvent(); } void notifyPortEvent() { emit portEvent(); } void notifyBuffEvent(unsigned int iBufferSize) { emit buffEvent(iBufferSize); } void notifySessEvent(void *pvSessionArg) { emit sessEvent(pvSessionArg); } void notifySyncEvent(unsigned long iPlayHead, bool bPlaying) { emit syncEvent(iPlayHead, bPlaying); } void notifyPropEvent() { emit propEvent(); } void notifySelfEvent() { emit selfEvent(); } signals: // Event signals. void shutEvent(); void xrunEvent(); void portEvent(); void buffEvent(unsigned int iBufferSize); void sessEvent(void *pvSessionArg); void syncEvent(unsigned long iPlayHead, bool bPlaying); void propEvent(); void selfEvent(); }; //---------------------------------------------------------------------- // class qtractorAudioEngine -- JACK client instance (singleton). // class qtractorAudioEngine : public qtractorEngine { public: // Constructor. qtractorAudioEngine(qtractorSession *pSession); // Engine initialization. bool init(); // Special event notifier proxy object. const qtractorAudioEngineProxy *proxy() const; // Event notifications. void notifyShutEvent(); void notifyXrunEvent(); void notifyPortEvent(); void notifyBuffEvent(unsigned int iBufferSize); void notifySessEvent(void *pvSessionArg); void notifySyncEvent(unsigned long iPlayHead, bool bPlaying); void notifyPropEvent(); void notifySelfEvent(); // JACK client descriptor accessor. jack_client_t *jackClient() const; // Process cycle executive. int process(unsigned int nframes); // Timebase master callback. void timebase(jack_position_t *pPos, int iNewPos); // Document element methods. bool loadElement(qtractorDocument *pDocument, QDomElement *pElement); bool saveElement(qtractorDocument *pDocument, QDomElement *pElement) const; // Session UUID accessors. void setSessionId(const QString& sSessionId); const QString& sessionId() const; // Internal sample-rate accessor. unsigned int sampleRate() const; // Buffer size accessors. unsigned int bufferSize() const; unsigned int bufferSizeEx() const; // Buffer offset accessor. unsigned int bufferOffset() const; // Block-stride size (in frames) accessor. unsigned int blockSize() const; // Audio (Master) bus defaults accessors. void setMasterAutoConnect(bool bMasterAutoConnect); bool isMasterAutoConnect() const; // Audio-export freewheeling (internal) state. void setFreewheel(bool bFreewheel); bool isFreewheel() const; // Audio-export active state. void setExporting(bool bExporting); bool isExporting() const; // Last known export extents accessors. unsigned long exportStart() const; unsigned long exportOffset() const; unsigned long exportLength() const; // Audio-export method. bool fileExport(const QString& sExportPath, const QList& exportBuses, unsigned long iExportStart, unsigned long iExportEnd, int iExportFormat = -1); // Special track-immediate methods. void trackMute(qtractorTrack *pTrack, bool bMute); // Metronome switching. void setMetronome(bool bMetronome); bool isMetronome() const; // Metronome enabled accessors. void setMetroEnabled(bool bMetroEnabled); bool isMetroEnabled() const; // Metronome bus mode accessors. void setMetroBus(bool bMetroBus); bool isMetroBus() const; void resetMetroBus(); // Metronome bus defaults accessors. void setMetroAutoConnect(bool bMetroAutoConnect); bool isMetroAutoConnect() const; // Metronome bar audio sample. void setMetroBarFilename(const QString& sFilename); const QString& metroBarFilename() const; // Metronome bar audio sample gain. void setMetroBarGain(float fGain); float metroBarGain() const; // Metronome beat audio sample. void setMetroBeatFilename(const QString& sFilename); const QString& metroBeatFilename() const; // Metronome beat audio sample gain. void setMetroBeatGain(float fGain); float metroBeatGain() const; // Metronome latency offset (in frames). void setMetroOffset(unsigned long iMetroOffset); unsigned long metroOffset() const; void resetMetro(bool bCountIn = false); // Metronome count-in switching. void setCountIn(bool bCountIn); bool isCountIn() const; // Metronome count-in mode. enum CountInMode { CountInNone = 0, CountInPlayback, CountInRecording }; void setCountInMode(CountInMode countInMode); CountInMode countInMode() const; // Metronome count-in number of beats. void setCountInBeats(unsigned short iCountInBeats); unsigned short countInBeats() const; // Metronome count-in status. unsigned short countIn() const; // Audition/pre-listening bus mode accessors. void setPlayerBus(bool bPlayerBus); bool isPlayerBus() const; void resetPlayerBus(); // Audition/pre-listening bus defaults accessors. void setPlayerAutoConnect(bool bPlayerAutoConnect); bool isPlayerAutoConnect() const; bool isPlayerOpen() const; bool openPlayer(const QString& sFilename); void closePlayer(); // Retrieve/restore all connections, on all audio buses; // return the effective number of connection attempts. int updateConnects(); // JACK Transport mode accessors. void setTransportMode(qtractorBus::BusMode transportMode); qtractorBus::BusMode transportMode() const; // JACK Transport latency accessors. void setTransportLatency(unsigned int iTransportLatency); unsigned int transportLatency() const; // JACK Timebase mode accessors. void setTimebase(bool bTimebase); bool isTimebase() const; bool isTimebaseEx() const; // JACK Timebase reset methods. void resetTimebase(); // Absolute number of frames elapsed since engine start. unsigned long jackFrameTime() const; // Reset all audio monitoring... void resetAllMonitors(); // Whether we're in the audio/real-time thread... static bool isProcessing(); // Time(base)/BBT info. struct TimeInfo { unsigned long frame; bool playing; unsigned int sampleRate; float tempo; unsigned int ticksPerBeat; unsigned short beatsPerBar; unsigned short beatType; float beats; unsigned short bar; unsigned int beat; unsigned int tick; float barBeats; unsigned long barTicks; }; const TimeInfo& timeInfo() const { return m_timeInfo; } // Update time(base)/BBT info. void updateTimeInfo(unsigned long iFrame); // Auxiliary audio output bus sorting method... void resetAudioOutBus( qtractorAudioBus *pAudioBus, qtractorPluginList *pPluginList); QStringList cyclicAudioOutBuses(qtractorAudioBus *pAudioBus) const; // Transport locate/reposition (timebase aware)... void transport_locate(unsigned long iFrame); protected: // Concrete device (de)activation methods. bool activate(); bool start(); void stop(); void deactivate(); void clean(); // Metronome (de)activation methods. void createMetroBus(); bool openMetroBus(); void closeMetroBus(); void deleteMetroBus(); // Audition/pre-listening player (de)activation methods. void createPlayerBus(); bool openPlayerBus(); void closePlayerBus(); void deletePlayerBus(); // Freewheeling process cycle executive (needed for export). void process_export(unsigned int nframes); // Metronome latency offset compensation. unsigned long metro_offset(unsigned long iFrame) const; private: // Special event notifier proxy object. qtractorAudioEngineProxy m_proxy; // Audio device instance variables. jack_client_t *m_pJackClient; // JACK Session UUID. QString m_sSessionId; // Initial sample-rate and buffer size. unsigned int m_iSampleRate; unsigned int m_iBufferSize; // Initial maximum safe buffer size. unsigned int m_iBufferSizeEx; // Partial buffer offset state; // careful for proper loop concatenation. unsigned int m_iBufferOffset; // Block-stride size (in frames). unsigned int m_iBlockSize; // Audio (Master) bus defaults. bool m_bMasterAutoConnect; // Audio-export freewheeling (internal) state. bool m_bFreewheel; // Common audio buffer sync thread. qtractorAudioBufferThread *m_pSyncThread; // Audio-export (in)active state. volatile bool m_bExporting; qtractorAudioFile *m_pExportFile; unsigned long m_iExportOffset; unsigned long m_iExportStart; unsigned long m_iExportEnd; volatile bool m_bExportDone; QList *m_pExportBuses; qtractorAudioExportBuffer *m_pExportBuffer; // Audio metronome stuff. bool m_bMetronome; bool m_bMetroBus; qtractorAudioBus *m_pMetroBus; bool m_bMetroAutoConnect; qtractorAudioBuffer *m_pMetroBarBuff; QString m_sMetroBarFilename; float m_fMetroBarGain; qtractorAudioBuffer *m_pMetroBeatBuff; QString m_sMetroBeatFilename; float m_fMetroBeatGain; unsigned long m_iMetroOffset; unsigned long m_iMetroBeatStart; unsigned int m_iMetroBeat; bool m_bMetroEnabled; // Count-in stuff. bool m_bCountIn; CountInMode m_countInMode; unsigned short m_iCountInBeats; unsigned short m_iCountIn; unsigned int m_iCountInBeat; unsigned long m_iCountInBeatStart; unsigned long m_iCountInFrame; unsigned long m_iCountInFrameEnd; // Audition/pre-listening player stuff. qtractorAtomic m_playerLock; bool m_bPlayerOpen; bool m_bPlayerBus; qtractorAudioBus *m_pPlayerBus; bool m_bPlayerAutoConnect; qtractorAudioBuffer *m_pPlayerBuff; unsigned long m_iPlayerFrame; // JACK Transport mode. qtractorBus::BusMode m_transportMode; // JACK Transport latency. unsigned int m_iTransportLatency; // JACK Timebase mode and control. bool m_bTimebase; unsigned int m_iTimebase; // Time(base)/BBT time info. TimeInfo m_timeInfo; }; //---------------------------------------------------------------------- // class qtractorAudioBus -- Managed JACK port set // class qtractorAudioBus : public qtractorBus { public: // Constructor. qtractorAudioBus(qtractorAudioEngine *pAudioEngine, const QString& sBusName, BusMode busMode, bool bMonitor = false, unsigned short iChannels = 2); // Destructor. ~qtractorAudioBus(); // Channel number property accessor. void setChannels(unsigned short iChannels); unsigned short channels() const; // Auto-connection predicate. void setAutoConnect(bool bAutoConnect); bool isAutoConnect() const; // Concrete activation methods. bool open(); void close(); // Auto-connect to physical ports. void autoConnect(); // Process cycle (preparator only). void process_prepare(unsigned int nframes); void process_monitor(unsigned int nframes); void process_commit(unsigned int nframes); // Bus-buffering methods. void buffer_prepare(unsigned int nframes, qtractorAudioBus *pInputBus = nullptr); void buffer_commit(unsigned int nframes); // Up-and-running predicate. bool isEnabled() const { return m_bEnabled; } // Frame buffer accessors. float **buffer() const { return m_ppYBuffer; } float **in() const { return m_ppIBuffer; } float **out() const { return m_ppOBuffer; } // Virtual I/O bus-monitor accessors. qtractorMonitor *monitor_in() const; qtractorMonitor *monitor_out() const; // Audio I/O bus-monitor accessors. qtractorAudioMonitor *audioMonitor_in() const; qtractorAudioMonitor *audioMonitor_out() const; // Plugin-chain accessors. qtractorPluginList *pluginList_in() const; qtractorPluginList *pluginList_out() const; // Audio I/O port latency accessors. unsigned int latency_in() const; unsigned int latency_out() const; // Retrieve/restore client:port connections; // return the effective number of connection attempts... int updateConnects(BusMode busMode, ConnectList& connects, bool bConnect = false) const; // Document element methods. bool loadElement(qtractorDocument *pDocument, QDomElement *pElement); bool saveElement(qtractorDocument *pDocument, QDomElement *pElement) const; // Update all aux-sends to this very bus... void updateAudioAuxSends(const QString& sAudioBusName); protected: // Bus mode/name change events. void updateBusMode(); void updateBusName(); // Create plugin-list properly. qtractorPluginList *createPluginList(int iFlags) const; // Set plugin-list title name. void updatePluginListName( qtractorPluginList *pPluginList, int iFlags) const; // Set plugin-list buffers properly. void updatePluginList( qtractorPluginList *pPluginList, int iFlags); private: // Instance variables. unsigned short m_iChannels; bool m_bAutoConnect; // Specific monitor instances. qtractorAudioMonitor *m_pIAudioMonitor; qtractorAudioMonitor *m_pOAudioMonitor; // Plugin-chain instances. qtractorPluginList *m_pIPluginList; qtractorPluginList *m_pOPluginList; // Specific JACK ports stuff. jack_port_t **m_ppIPorts; jack_port_t **m_ppOPorts; float **m_ppIBuffer; float **m_ppOBuffer; float **m_ppXBuffer; float **m_ppYBuffer; // Special under-work flag... // (r/w access should be atomic) volatile bool m_bEnabled; // Buffer mix-down processor. void (*m_pfnBufferAdd)(float **, float **, unsigned int, unsigned short, unsigned short, unsigned int); }; #endif // __qtractorAudioEngine_h // end of qtractorAudioEngine.h qtractor-1.5.9/src/PaxHeaders/qtractorTempoAdjustForm.h0000644000000000000000000000013215101070305020236 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorTempoAdjustForm.h0000644000175000001440000000602415101070305020230 0ustar00rncbcusers// qtractorTempoAdjustForm.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 __qtractorTempoAdjustForm_h #define __qtractorTempoAdjustForm_h #include "ui_qtractorTempoAdjustForm.h" // Forward declarations. class qtractorClip; class qtractorAudioClip; class QElapsedTimer; //---------------------------------------------------------------------------- // qtractorTempoAdjustForm -- UI wrapper form. class qtractorTempoAdjustForm : public QDialog { Q_OBJECT public: // Constructor. qtractorTempoAdjustForm(QWidget *pParent = nullptr); // Destructor. ~qtractorTempoAdjustForm(); // Clip accessors. void setClip(qtractorClip *pClip); qtractorClip *clip() const; qtractorAudioClip *audioClip() const; // Range accessors. void setRangeStart(unsigned long iRangeStart); unsigned long rangeStart() const; void setRangeLength(unsigned long iRangeLength); unsigned long rangeLength() const; void setRangeBeats(unsigned short iRangeBeats); unsigned short rangeBeats() const; // Accepted results accessors. float tempo() const; unsigned short beatsPerBar() const; unsigned short beatDivisor() const; // Time-scale accessor. qtractorTimeScale *timeScale() const; protected slots: void tempoChanged(); void tempoDetect(); void tempoReset(); void tempoAdjust(); void tempoTap(); void rangeStartChanged(unsigned long); void rangeLengthChanged(unsigned long); void rangeBeatsChanged(int); void formatChanged(int); void changed(); void accept(); void reject(); protected: void updateRangeStart(unsigned long iRangeStart); void updateRangeLength(unsigned long iRangeLength); void updateRangeBeats(unsigned short iRangeBeats); void updateRangeSelect(); void stabilizeForm(); private: // The Qt-designer UI struct... Ui::qtractorTempoAdjustForm m_ui; // Instance variables... qtractorTimeScale *m_pTimeScale; qtractorClip *m_pClip; qtractorAudioClip *m_pAudioClip; class ClipWidget; ClipWidget *m_pClipWidget; QElapsedTimer *m_pTempoTap; int m_iTempoTap; float m_fTempoTap; int m_iDirtySetup; int m_iDirtyCount; }; #endif // __qtractorTempoAdjustForm_h // end of qtractorTempoAdjustForm.h qtractor-1.5.9/src/PaxHeaders/qtractorClipForm.h0000644000000000000000000000013215101070305016666 xustar0030 mtime=1761898693.067267591 30 atime=1761898693.067267591 30 ctime=1761898693.067267591 qtractor-1.5.9/src/qtractorClipForm.h0000644000175000001440000000451015101070305016656 0ustar00rncbcusers// qtractorClipForm.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 __qtractorClipForm_h #define __qtractorClipForm_h #include "ui_qtractorClipForm.h" #include "qtractorClip.h" //---------------------------------------------------------------------------- // qtractorClipForm -- UI wrapper form. class qtractorClipForm : public QDialog { Q_OBJECT public: // Constructor. qtractorClipForm(QWidget *pParent = nullptr); // Destructor. ~qtractorClipForm(); void setClip(qtractorClip *pClip); qtractorClip *clip() const; protected slots: void accept(); void reject(); void changed(); void formatChanged(int); void stabilizeForm(); void browseFilename(); void clipNameChanged(const QString& sClipName); void filenameChanged(const QString& sFilename); void trackChannelChanged(int iTrackChannel); void clipStartChanged(unsigned long iClipStart); protected: qtractorClip::FadeType fadeTypeFromIndex(int iIndex) const; int indexFromFadeType(qtractorClip::FadeType fadeType) const; qtractorTrack::TrackType trackType() const; void fileChanged(const QString& sFilename, unsigned short iTrackChannel); static void initFadeTypes(); private: // The Qt-designer UI struct... Ui::qtractorClipForm m_ui; // Instance variables... qtractorClip *m_pClip; bool m_bClipNew; qtractorTimeScale *m_pTimeScale; int m_iDirtyCount; int m_iDirtySetup; }; #endif // __qtractorClipForm_h // end of qtractorClipForm.h qtractor-1.5.9/src/PaxHeaders/qtractorCurve.cpp0000644000000000000000000000013215101070305016572 xustar0030 mtime=1761898693.068267594 30 atime=1761898693.068267594 30 ctime=1761898693.068267594 qtractor-1.5.9/src/qtractorCurve.cpp0000644000175000001440000004667615101070305016605 0ustar00rncbcusers// qtractorCurve.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 "qtractorCurve.h" #include "qtractorTimeScale.h" #include "qtractorSession.h" #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; } //---------------------------------------------------------------------- // qtractorCurve::updateNode -- Node coefficients computation helpers. // // (Sample &)Hold. static inline void updateNodeHold ( qtractorCurve::Node *pNode, float y0, const qtractorCurve::Node *pPrev, const qtractorCurve::Node */*pNext*/ ) { if (pPrev) pNode->a = pPrev->value; else pNode->a = y0; pNode->b = pNode->c = pNode->d = 0.0f; } // Linear. static inline void updateNodeLinear ( qtractorCurve::Node *pNode, float y0, const qtractorCurve::Node *pPrev, const qtractorCurve::Node */*pNext*/ ) { float y1, x1 = float(pNode->frame); if (pPrev) { x1 -= float(pPrev->frame); y1 = pPrev->value; } else { y1 = y0; } pNode->b = pNode->value; if (x1 > 0.0f) pNode->a = (y1 - pNode->b) / x1; else pNode->a = 0.0f; pNode->c = pNode->d = 0.0f; } // Spline. static inline void updateNodeSpline ( qtractorCurve::Node *pNode, float y0, const qtractorCurve::Node *pPrev, const qtractorCurve::Node *pNext ) { // Shamelessly using the same reference source article as Ardour ;) // CJC Kuger, "Constrained Cubic Spline Interpolation", August 2002 // http://www.korf.co.uk/spline.pdf const float fZero = 1e-9f; const float x0 = float(pNode->frame); float y1, x1 = x0; float s1 = 0.0f; float f1 = 0.0f; float y2 = pNode->value; float s2 = 0.0f; float f2 = 0.0f; if (pPrev) { x1 -= float(pPrev->frame); y1 = pPrev->value; } else { y1 = y0; } if (qAbs(y1 - y2) > fZero) s1 = x1 / (y1 - y2); if (pPrev) { pPrev = pPrev->prev(); if (pPrev && (qAbs(y1 - pPrev->value) > fZero)) { const float s0 = (x1 - float(pPrev->frame) - x0) / (y1 - pPrev->value); if (s1 * s0 > 0.0f && qAbs(s1 + s0) > fZero) f1 = 2.0f / (s1 + s0); } } if (pNext && (qAbs(pNext->value - y2) > fZero)) s2 = (float(pNext->frame) - x0) / (pNext->value - y2); if (s2 * s1 > 0.0f && qAbs(s2 + s1) > fZero) f2 = 2.0f / (s2 + s1); const float x12 = x1 * x1; const float dy = y2 - y1; // Compute second derivative for either side of control point... const float ff1 = (((2.0f * (f2 + (2.0f * f1))) / x1)) + ((6.0f * dy) / x12); const float ff2 = (-2.0f * ((2.0f * f2) + f1) / x1) - ((6.0f * dy) / x12); // Compute and store polynomial coefficients... pNode->a = (ff1 - ff2) / (6.0f * x1); pNode->b = ff2 / 2.0f; pNode->c = - (dy / x1 + (pNode->b * x1) + (pNode->a * x12)); pNode->d = y1 - (pNode->c * x1) - (pNode->b * x12) - (pNode->a * x12 * x1); } //---------------------------------------------------------------------- // qtractorCurve::value -- Interpolation computation helpers. // // (Sample &)Hold. static inline float valueHold ( const qtractorCurve::Node *pNode, float /*x*/ ) { return pNode->a; } // Linear. static inline float valueLinear ( const qtractorCurve::Node *pNode, float x ) { return pNode->a * x + pNode->b; } // Spline. static inline float valueSpline ( const qtractorCurve::Node *pNode, float x ) { return ((pNode->a * x + pNode->b) * x + pNode->c) * x + pNode->d; } //---------------------------------------------------------------------- // class qtractorCurve -- The generic curve declaration. // // Constructor. qtractorCurve::qtractorCurve ( qtractorCurveList *pList, qtractorSubject *pSubject, Mode mode, unsigned int iMinFrameDist ) : m_pList(pList), m_mode(mode), m_iMinFrameDist(iMinFrameDist), m_observer(pSubject, this), m_state(Idle), m_cursor(this), m_bLogarithmic(false), m_color(Qt::darkRed), m_pEditList(nullptr) { m_nodes.setAutoDelete(true); m_pEditList = new qtractorCurveEditList(this); m_tail.frame = 0; // m_tail.value = m_observer.defaultValue(); clear(); m_observer.setCurve(this); if (m_pList) m_pList->append(this); } // Destructor. qtractorCurve::~qtractorCurve (void) { // if (m_pList) // m_pList->remove(this); m_observer.setCurve(nullptr); clear(); delete m_pEditList; } // Curve list reset method. void qtractorCurve::clear (void) { // m_state = Idle; // m_tail.frame = 0; m_tail.value = m_observer.defaultValue(); m_tail.a = 0.0f; m_tail.b = 0.0f; m_tail.c = 0.0f; m_tail.d = 0.0f; m_nodes.clear(); m_cursor.reset(nullptr); updateNodeEx(nullptr); } // Insert a new node, in frame order. qtractorCurve::Node *qtractorCurve::addNode ( unsigned long iFrame, float fValue, qtractorCurveEditList *pEditList ) { fValue = m_observer.safeValue(fValue); #ifdef CONFIG_DEBUG_0 qDebug("qtractorCurve[%p]::addNode(%lu, %g, %p)", this, iFrame, fValue, pEditList); #endif Node *pNode = nullptr; Node *pNext = m_cursor.seek(iFrame); Node *pPrev = (pNext ? pNext->prev() : m_nodes.last()); if (pNext && isMinFrameDist(pNext, iFrame)) pNode = pNext; else if (pPrev && isMinFrameDist(pPrev, iFrame)) pNode = pPrev; else if (m_mode != Hold && m_observer.isDecimal()) { // Smoothing... const float fThreshold = 0.5f; // (m_bLogarithmic ? 0.1f : 0.5f); float x0 = (pPrev ? float(pPrev->frame) : 0.0f); float x1 = float(iFrame); float x2 = (pNext ? float(pNext->frame) : m_tail.frame); float y0 = (pPrev ? pPrev->value : m_tail.value); float y1 = fValue; float y2 = (pNext ? pNext->value : fValue); float s1 = (x1 > x0 ? (y1 - y0) / (x1 - x0) : 0.0f); float y3 = (x2 > x1 ? s1 * (x2 - x1) + y1 : y1); if (qAbs(y3 - y2) < fThreshold * qAbs(y3 - y1)) return nullptr; #if 0// Overkill maybe?... if (pPrev && pPrev->prev()) { pNode = pPrev; pPrev = pPrev->prev(); x0 = (pPrev ? float(pPrev->frame) : 0.0f); y0 = (pPrev ? pPrev->value : m_tail.value); x1 = float(pNode->frame); y1 = pNode->value; x2 = float(iFrame); y2 = fValue; s1 = (y1 - y0) / (x1 - x0); y3 = s1 * (x2 - x1) + y1; if (qAbs(y3 - y2) > fThreshold * qAbs(y3 - y1)) pNode = nullptr; } #endif } if (pNode) { // Move/update the existing one as average... if (pEditList) pEditList->moveNode(pNode, pNode->frame, pNode->value); pNode->frame = iFrame; pNode->value = fValue; } else { // Create a brand new node, // insert it in the right frame... pNode = new Node(iFrame, fValue); if (pNext) m_nodes.insertBefore(pNode, pNext); else m_nodes.append(pNode); if (pEditList) pEditList->addNode(pNode); } updateNode(pNode); // Dirty up... if (m_pList) m_pList->notify(); return pNode; } // Insert curve node in correct frame order. void qtractorCurve::insertNode ( Node *pNode ) { if (pNode == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorCurve[%p]::insertNode(%p)", this, pNode); #endif Node *pNext = m_cursor.seek(pNode->frame); if (pNext) m_nodes.insertBefore(pNode, pNext); else m_nodes.append(pNode); updateNode(pNode); // Dirty up... if (m_pList) m_pList->notify(); } // Unlink an existing node from curve. void qtractorCurve::unlinkNode ( Node *pNode ) { if (pNode == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorCurve[%p]::unlinkNode(%p)", this, pNode); #endif m_cursor.reset(pNode); Node *pNext = pNode->next(); m_nodes.unlink(pNode); updateNode(pNext); // Dirty up... if (m_pList) m_pList->notify(); } // Remove an existing node from curve. void qtractorCurve::removeNode ( Node *pNode ) { if (pNode == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorCurve[%p]::removeNode(%p)", this, pNode); #endif m_cursor.reset(pNode); Node *pNext = pNode->next(); m_nodes.remove(pNode); updateNode(pNext); // Dirty up... if (m_pList) m_pList->notify(); } // Whether to snap to minimum distance frame. bool qtractorCurve::isMinFrameDist ( Node *pNode, unsigned long iFrame) const { return (iFrame > pNode->frame - m_iMinFrameDist && iFrame < pNode->frame + m_iMinFrameDist); } // Node interpolation coefficients updater. void qtractorCurve::updateNode ( qtractorCurve::Node *pNode ) { updateNodeEx(pNode); if (pNode) updateNodeEx(pNode->next()); } void qtractorCurve::updateNodeEx ( qtractorCurve::Node *pNode ) { Node *pPrev, *pNext = nullptr; if (pNode) { pPrev = pNode->prev(); pNext = pNode->next(); if (pNext == nullptr) pNext = &m_tail; } else { pNode = &m_tail; pPrev = m_nodes.last(); } if (m_tail.frame < pNode->frame) m_tail.frame = pNode->frame; switch (m_mode) { case Hold: updateNodeHold(pNode, m_tail.value, pPrev, pNext); break; case Linear: updateNodeLinear(pNode, m_tail.value, pPrev, pNext); break; case Spline: updateNodeSpline(pNode, m_tail.value, pPrev, pNext); break; } } // Refresh all coefficients. void qtractorCurve::update (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorCurve[%p]::update()", this); #endif for (Node *pNode = m_nodes.first(); pNode; pNode = pNode->next()) updateNodeEx(pNode); updateNodeEx(nullptr); if (m_pList) m_pList->notify(); } // Default value accessors. void qtractorCurve::setDefaultValue ( float fDefaultValue ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorCurve[%p]::setDefaultValue(%g)", this, fDefaultValue); #endif m_tail.value = fDefaultValue; Node *pLast = m_nodes.last(); updateNode(pLast); Node *pFirst = m_nodes.first(); if (pFirst != pLast) updateNode(pFirst); if (m_pList) m_pList->notify(); } // Default length accessors. void qtractorCurve::setLength ( unsigned long iLength ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorCurve[%p]::setLength(%ld)", this, iLength); #endif m_tail.frame = iLength; Node *pNode = m_nodes.last(); while (pNode && pNode->frame > m_tail.frame) { Node *pPrev = pNode->prev(); m_nodes.remove(pNode); pNode = pPrev; } updateNode(pNode); if (m_pList) m_pList->notify(); } // Intra-curve frame positioning node seeker. qtractorCurve::Node *qtractorCurve::Cursor::seek ( unsigned long iFrame ) { Node *pNode = m_pNode; if (iFrame > m_iFrame) { // Seek forward... if (pNode == nullptr) pNode = m_pCurve->nodes().first(); while (pNode && pNode->frame < iFrame) pNode = pNode->next(); } else { // Seek backward... if (pNode == nullptr) pNode = m_pCurve->nodes().last(); while (pNode && pNode->prev() && (pNode->prev())->frame > iFrame) pNode = pNode->prev(); } m_iFrame = iFrame; m_pNode = pNode; return (pNode && pNode->frame >= iFrame ? pNode : nullptr); } // Intra-curve frame positioning reset. void qtractorCurve::Cursor::reset ( qtractorCurve::Node *pNode ) { m_pNode = (pNode ? pNode->next() : nullptr); m_iFrame = (m_pNode ? m_pNode->frame : 0); } // Common interpolate method. float qtractorCurve::value ( const Node *pNode, unsigned long iFrame ) const { if (pNode == nullptr) { pNode = m_nodes.last(); if (pNode == nullptr) pNode = &m_tail; } const float x = float(pNode->frame) - float(iFrame); float y = pNode->value; if (x > 0.0f) { switch (m_mode) { case Hold: y = valueHold(pNode, x); break; case Linear: y = valueLinear(pNode, x); break; case Spline: y = valueSpline(pNode, x); break; } } return y; } float qtractorCurve::value ( unsigned long iFrame ) { return value(m_cursor.seek(iFrame), iFrame); } // Normalized scale converters. float qtractorCurve::valueFromScale ( float fScale ) const { if (m_bLogarithmic) { const float fMaxValue = m_observer.maxValue(); const float fMinValue = m_observer.minValue(); if (fMinValue < 0.0f && fMaxValue > 0.0f) { const float fMidScale = fMinValue / (fMinValue - fMaxValue); if (fScale > fMidScale) { const float fMaxScale = (1.0f - fMidScale); fScale = (fScale - fMidScale) / fMaxScale; fScale = fMidScale + fMaxScale * ::cubef2(fScale); } else { fScale = (fMidScale - fScale) / fMidScale; fScale = fMidScale - fMidScale * ::cubef2(fScale); } } else fScale = ::cubef2(fScale); } return m_observer.valueFromScale(fScale); } float qtractorCurve::scaleFromValue ( float fValue ) const { float fScale = m_observer.scaleFromValue(fValue); if (m_bLogarithmic) { const float fMaxValue = m_observer.maxValue(); const float fMinValue = m_observer.minValue(); if (fMinValue < 0.0f && fMaxValue > 0.0f) { const float fMidScale = fMinValue / (fMinValue - fMaxValue); if (fScale > fMidScale) { const float fMaxScale = (1.0f - fMidScale); fScale = (fScale - fMidScale) / fMaxScale; fScale = fMidScale + fMaxScale * ::cbrtf2(fScale); } else { fScale = (fMidScale - fScale) / fMidScale; fScale = fMidScale - fMidScale * ::cbrtf2(fScale); } } else fScale = ::cbrtf2(fScale); } return fScale; } // Copy all events from another curve (raw-copy). void qtractorCurve::copyNodes ( qtractorCurve *pCurve ) { // Check whether we're the same... if (pCurve == this) return; // Remove existing nodes. m_nodes.clear(); // Clone new ones... Node *pNode = pCurve->nodes().first(); for (; pNode; pNode = pNode->next()) m_nodes.append(new Node(pNode->frame, pNode->value)); // Done. update(); } // Convert MIDI sequence events to curve nodes. void qtractorCurve::readMidiSequence ( qtractorMidiSequence *pSeq, qtractorMidiEvent::EventType ctype, unsigned short iChannel, unsigned short iParam, qtractorTimeScale *pTimeScale ) { qtractorTimeScale ts; if (pTimeScale) ts.copy(*pTimeScale); ts.setTicksPerBeat(pSeq->ticksPerBeat()); // Cleanup all existing nodes... clear(); // Convert events to nodes... if (pSeq->channel() == iChannel) { qtractorMidiEvent *pEvent = pSeq->events().first(); while (pEvent) { if (pEvent->type() == ctype && pEvent->note() == iParam) { const unsigned long iFrame = ts.frameFromTick(pEvent->time()); float fScale; if (ctype == qtractorMidiEvent::PITCHBEND) fScale = float(pEvent->pitchBend()) / float(0x3fff); else if (ctype == qtractorMidiEvent::REGPARAM || ctype == qtractorMidiEvent::NONREGPARAM || ctype == qtractorMidiEvent::CONTROL14) fScale = float(pEvent->value()) / float(0x3fff); else fScale = float(pEvent->value()) / float(0x7f); const float fValue = m_observer.valueFromScale(fScale); m_nodes.append(new Node(iFrame, fValue)); } pEvent = pEvent->next(); } } update(); } // Convert curve node to MIDI sequence events. void qtractorCurve::writeMidiSequence ( qtractorMidiSequence *pSeq, qtractorMidiEvent::EventType ctype, unsigned short iChannel, unsigned short iParam, qtractorTimeScale *pTimeScale ) const { qtractorTimeScale ts; if (pTimeScale) ts.copy(*pTimeScale); ts.setTicksPerBeat(pSeq->ticksPerBeat()); // Set proper sequence channel... pSeq->setChannel(iChannel); // Cleanup existing nodes... qtractorMidiEvent *pEvent = pSeq->events().first(); while (pEvent) { qtractorMidiEvent *pEventNext = pEvent->next(); if (pEvent->type() == ctype && pEvent->note() == iParam) pSeq->removeEvent(pEvent); pEvent = pEventNext; } // Convert nodes to events... qtractorCurve::Node *pNode = m_nodes.first(); while (pNode) { const unsigned long iTime = ts.tickFromFrame(pNode->frame); qtractorMidiEvent *pEvent = new qtractorMidiEvent(iTime, ctype, iParam); const float fScale = m_observer.scaleFromValue(pNode->value); if (ctype == qtractorMidiEvent::PITCHBEND) pEvent->setPitchBend(float(0x3fff) * fScale); else if (ctype == qtractorMidiEvent::REGPARAM || ctype == qtractorMidiEvent::NONREGPARAM || ctype == qtractorMidiEvent::CONTROL14) pEvent->setValue(float(0x3fff) * fScale); else pEvent->setValue(float(0x7f) * fScale); pSeq->insertEvent(pEvent); pNode = pNode->next(); } pSeq->close(); } void qtractorCurve::setCapture ( bool bCapture ) { if (m_pList == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorCurve[%p]::setCapture(%d)", this, int(bCapture)); #endif const bool bOldCapture = (m_state & Capture); m_state = State(bCapture ? (m_state | Capture) : (m_state & ~Capture)); if ((bCapture && !bOldCapture) || (!bCapture && bOldCapture)) { m_pList->updateCapture(bCapture); // notify auto-plugin-deactivate qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pSession->autoDeactivatePlugins(); } } void qtractorCurve::setProcess ( bool bProcess ) { if (m_pList == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorCurve[%p]::setProcess(%d)", this, int(bProcess)); #endif const bool bOldProcess = (m_state & Process); m_state = State(bProcess ? (m_state | Process) : (m_state & ~Process)); if ((bProcess && !bOldProcess) || (!bProcess && bOldProcess)) { m_pList->updateProcess(bProcess); // notify auto-plugin-deactivate qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pSession->autoDeactivatePlugins(); } } void qtractorCurve::setLocked ( bool bLocked ) { if (m_pList == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorCurve[%p]::setLocked(%d)", this, int(bLocked)); #endif const bool bOldLocked = (m_state & Locked); m_state = State(bLocked ? (m_state | Locked) : (m_state & ~Locked)); if ((bLocked && !bOldLocked) || (!bLocked && bOldLocked)) m_pList->updateLocked(bLocked); } //---------------------------------------------------------------------- // qtractorCurveEditList -- Curve node edit list. // Curve edit list command executive. bool qtractorCurveEditList::execute ( bool bRedo ) { if (m_pCurve == nullptr) return false; const unsigned long iFrame = m_pCurve->cursor().frame(); QListIterator iter(m_items); if (!bRedo) iter.toBack(); while (bRedo ? iter.hasNext() : iter.hasPrevious()) { Item *pItem = (bRedo ? iter.next() : iter.previous()); // Execute the command item... switch (pItem->command) { case AddNode: { if (bRedo) m_pCurve->insertNode(pItem->node); else m_pCurve->unlinkNode(pItem->node); pItem->autoDelete = !bRedo; break; } case MoveNode: { qtractorCurve::Node *pNode = pItem->node; const unsigned long iFrame = pNode->frame; const float fValue = pNode->value; pNode->frame = pItem->frame; pNode->value = pItem->value; pItem->frame = iFrame; pItem->value = fValue; break; } case RemoveNode: { if (bRedo) m_pCurve->unlinkNode(pItem->node); else m_pCurve->insertNode(pItem->node); pItem->autoDelete = bRedo; break; } default: break; } } m_pCurve->cursor().seek(iFrame); m_pCurve->update(); return true; } // end of qtractorCurve.cpp qtractor-1.5.9/src/PaxHeaders/qtractorPaletteForm.h0000644000000000000000000000013215101070305017375 xustar0030 mtime=1761898693.086267651 30 atime=1761898693.086267651 30 ctime=1761898693.086267651 qtractor-1.5.9/src/qtractorPaletteForm.h0000644000175000001440000001703015101070305017366 0ustar00rncbcusers// qtractorPaletteForm.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 __qtractorPaletteForm_h #define __qtractorPaletteForm_h #include #include #include #include #include #include // Forward decls. class QListView; class QLabel; class QToolButton; //------------------------------------------------------------------------- // qtractorPaletteForm namespace Ui { class qtractorPaletteForm; } class qtractorPaletteForm: public QDialog { Q_OBJECT public: qtractorPaletteForm(QWidget *parent = nullptr, const QPalette& pal = QPalette()); virtual ~qtractorPaletteForm(); void setPalette(const QPalette& pal); const QPalette& palette() const; void setSettings(QSettings *settings, bool owner = false); QSettings *settings() const; void setPaletteName(const QString& name); QString paletteName() const; bool isDirty() const; static bool namedPalette(QSettings *settings, const QString& name, QPalette& pal, bool fixup = false); static QStringList namedPaletteList(QSettings *settings); static QString namedPaletteConf( QSettings *settings, const QString& name); static void addNamedPaletteConf( QSettings *settings, const QString& name, const QString& filename); static QPalette::ColorRole colorRole(const QString& name); class PaletteModel; class ColorDelegate; class ColorButton; class ColorEditor; class RoleEditor; protected slots: void nameComboChanged(const QString& name); void saveButtonClicked(); void deleteButtonClicked(); void generateButtonChanged(); void resetButtonClicked(); void detailsCheckClicked(); void importButtonClicked(); void exportButtonClicked(); void paletteChanged(const QPalette& pal); void accept(); void reject(); protected: void setPalette(const QPalette& pal, const QPalette& parentPal); bool namedPalette(const QString& name, QPalette& pal) const; QStringList namedPaletteList() const; QString namedPaletteConf(const QString& name) const; void addNamedPaletteConf(const QString& name, const QString& filename); void deleteNamedPaletteConf(const QString& name); static bool loadNamedPaletteConf( const QString& name, const QString& filename, QPalette& pal); static bool saveNamedPaletteConf( const QString& name, const QString& filename, const QPalette& pal); static bool loadNamedPalette( QSettings *settings, const QString& name, QPalette& pal); static bool saveNamedPalette( QSettings *settings, const QString& name, const QPalette& pal); void updateNamedPaletteList(); void updateGenerateButton(); void updateDialogButtons(); void setDefaultDir(const QString& dir); QString defaultDir() const; void setShowDetails(bool on); bool isShowDetails() const; void showEvent(QShowEvent *event); void resizeEvent(QResizeEvent *event); private: Ui::qtractorPaletteForm *p_ui; Ui::qtractorPaletteForm& m_ui; QSettings *m_settings; bool m_owner; QPalette m_palette; QPalette m_parentPalette; PaletteModel *m_paletteModel; bool m_modelUpdated; bool m_paletteUpdated; int m_dirtyCount; int m_dirtyTotal; }; //------------------------------------------------------------------------- // qtractorPaletteForm::PaletteModel class qtractorPaletteForm::PaletteModel : public QAbstractTableModel { Q_OBJECT Q_PROPERTY(QPalette::ColorRole colorRole READ colorRole) public: PaletteModel(QObject *parent = nullptr); int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QVariant data(const QModelIndex &index, int role) const; bool setData(const QModelIndex &index, const QVariant &value, int role); Qt::ItemFlags flags(const QModelIndex &index) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; void setPalette(const QPalette &palette, const QPalette &parentPalette); const QPalette& palette() const; void setGenerate(bool on) { m_generate = on; } QPalette::ColorRole colorRole() const { return QPalette::NoRole; } signals: void paletteChanged(const QPalette &palette); protected: QPalette::ColorGroup columnToGroup(int index) const; int groupToColumn(QPalette::ColorGroup group) const; private: QPalette m_palette; QPalette m_parentPalette; QMap m_roleNames; int m_nrows; bool m_generate; }; //------------------------------------------------------------------------- // qtractorPaletteForm::ColorDelegate class qtractorPaletteForm::ColorDelegate : public QItemDelegate { public: ColorDelegate(QObject *parent = nullptr) : QItemDelegate(parent) {} QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const; void setEditorData(QWidget *editor, const QModelIndex& index) const; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem& option, const QModelIndex &index) const; virtual void paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const; virtual QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const; }; //------------------------------------------------------------------------- // qtractorPaletteForm::ColorButton class qtractorPaletteForm::ColorButton : public QPushButton { Q_OBJECT Q_PROPERTY(QBrush brush READ brush WRITE setBrush) public: ColorButton (QWidget *parent = nullptr); const QBrush& brush() const; void setBrush(const QBrush& b); signals: void changed(); protected slots: void chooseColor(); protected: void paintEvent(QPaintEvent *event); private: QBrush m_brush; }; //------------------------------------------------------------------------- // PaleteEditor::ColorEditor class qtractorPaletteForm::ColorEditor : public QWidget { Q_OBJECT public: ColorEditor(QWidget *parent = nullptr); void setColor(const QColor &color); QColor color() const; bool changed() const; signals: void changed(QWidget *widget); protected slots: void colorChanged(); private: qtractorPaletteForm::ColorButton *m_button; bool m_changed; }; //------------------------------------------------------------------------- // PaleteEditor::RoleEditor class qtractorPaletteForm::RoleEditor : public QWidget { Q_OBJECT public: RoleEditor(QWidget *parent = nullptr); void setLabel(const QString &label); void setEdited(bool on); bool edited() const; signals: void changed(QWidget *widget); protected slots: void resetProperty(); private: QLabel *m_label; QToolButton *m_button; bool m_edited; }; #endif // __qtractorPaletteForm_h // end of qtractorPaletteForm.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiSysexForm.ui0000644000000000000000000000013215101070305020103 xustar0030 mtime=1761898693.084267645 30 atime=1761898693.084267645 30 ctime=1761898693.084267645 qtractor-1.5.9/src/qtractorMidiSysexForm.ui0000644000175000001440000002050415101070305020074 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. qtractorMidiSysexForm 0 0 480 320 MIDI SysEx false true false true Name Size Data (hex) Import from SysEx file &Import... Export to SysEx file E&xport... Qt::Vertical 20 4 Move SysEx item up on list order &Up Move SysEx item down on list order &Down Qt::TabFocus Open SysEx 320 0 Sysex name true Qt::TabFocus Save SysEx Qt::TabFocus Delete SysEx Create SysEx item &Add Monospace Update SysEx item Upda&te Remove SysEx item &Remove Qt::Vertical 20 4 Qt::Horizontal QDialogButtonBox::Reset|QDialogButtonBox::Cancel|QDialogButtonBox::Ok SysexListView ImportButton ExportButton MoveUpButton MoveDownButton OpenButton NameComboBox SaveButton DeleteButton SysexTextEdit AddButton UpdateButton RemoveButton DialogButtonBox qtractor-1.5.9/src/PaxHeaders/qtractorTracks.h0000644000000000000000000000012715101070305016406 xustar0029 mtime=1761898693.09226767 29 atime=1761898693.09226767 29 ctime=1761898693.09226767 qtractor-1.5.9/src/qtractorTracks.h0000644000175000001440000002047115101070305016376 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.9/src/PaxHeaders/qtractorTempoAdjustForm.cpp0000644000000000000000000000013215101070305020571 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorTempoAdjustForm.cpp0000644000175000001440000005625615101070305020577 0ustar00rncbcusers// qtractorTempoAdjustForm.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 "qtractorTempoAdjustForm.h" #include "qtractorAbout.h" #include "qtractorSession.h" #include "qtractorAudioClip.h" #include "qtractorMainForm.h" #include "qtractorTracks.h" #include #include #include #include #include #include #ifdef CONFIG_LIBAUBIO #include "qtractorAudioEngine.h" #include #include // 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) { aubio = new_aubio_tempo("default", iBlockSize, nstep, iSampleRate); ibuf = new_fvec(nstep); obuf = new_fvec(1); } // Dtor. ~audioClipTempoDetectData() { beats.clear(); del_fvec(obuf); del_fvec(ibuf); del_aubio_tempo(aubio); } // Members. unsigned int count; unsigned long offset; unsigned short channels; unsigned int nstep; aubio_tempo_t *aubio; fvec_t *ibuf; fvec_t *obuf; QList beats; }; static void audioClipTempoDetect ( float **ppFrames, unsigned int iFrames, void *pvArg ) { audioClipTempoDetectData *pData = static_cast (pvArg); 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); } } if (++(pData->count) > 100) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { QProgressBar *pProgressBar = pMainForm->progressBar(); pProgressBar->setValue(pProgressBar->value() + iFrames); } qtractorSession::stabilize(); pData->count = 0; } } #endif // CONFIG_LIBAUBIO //---------------------------------------------------------------------------- // 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); } #ifdef CONFIG_LIBAUBIO if (m_pAudioClip) { m_ui.TempoDetectPushButton->setEnabled(true); } else { m_ui.TempoDetectPushButton->setEnabled(false); m_ui.TempoDetectPushButton->hide(); } #else m_ui.TempoDetectPushButton->setEnabled(false); m_ui.TempoDetectPushButton->hide(); #endif } 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 unsigned long iBeatLength = 60.0f * float(m_pTimeScale->sampleRate()) / fTempo; if (iBeatLength > 0) { const int iRangeLength = m_ui.RangeLengthSpinBox->value(); setRangeBeats(iRangeLength / iBeatLength); } 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 #ifdef CONFIG_LIBAUBIO 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(); } float fTempoDetect = 0.0f; audioClipTempoDetectData data(iChannels, iSampleRate); 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); if (pProgressBar) pProgressBar->hide(); m_ui.TempoDetectPushButton->setEnabled(true); QApplication::restoreOverrideCursor(); #endif // CONFIG_LIBAUBIO } // 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); 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 unsigned int iRangeBeats = m_pTimeScale->beatFromFrame(iRangeLength); const unsigned long q = m_pTimeScale->beatsPerBar(); setRangeBeats(q * ((iRangeBeats + (q >> 1)) / q)); } // 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.9/src/PaxHeaders/qtractorList.h0000644000000000000000000000012715101070305016072 xustar0029 mtime=1761898693.07326761 29 atime=1761898693.07326761 29 ctime=1761898693.07326761 qtractor-1.5.9/src/qtractorList.h0000644000175000001440000001556715101070305016074 0ustar00rncbcusers// qtractorList.h // /**************************************************************************** Copyright (C) 2005-2012, rncbc aka Rui Nuno Capela. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #ifndef __qtractorList_h #define __qtractorList_h //---------------------------------------------------------------------- // class qtractorList -- Doubly-linked list base. // template class qtractorList { public: // Default constructor. qtractorList() : m_pFirst(0), m_pLast(0), m_iCount(0), m_pFreeList(0), m_bAutoDelete(false) {} // Default destructor. ~qtractorList() { clear(); } // Accessors. Node *first() const { return m_pFirst; } Node *last() const { return m_pLast; } int count() const { return m_iCount; } // Property accessors. void setAutoDelete(bool bAutoDelete) { m_bAutoDelete = bAutoDelete; } bool autoDelete() const { return m_bAutoDelete; } // Insert methods. void insertAfter(Node *pNode, Node *pPrevNode = 0); void insertBefore(Node *pNode, Node *pNextNode = 0); // Unlink/remove methods. void unlink(Node *pNode); void remove(Node *pNode); // Reset method. void clear(); // Random accessors. Node *at (int iNode) const; // Aliased methods and operators. void prepend(Node *pNode) { insertBefore(pNode); } void append(Node *pNode) { insertAfter(pNode); } Node *operator[] (int iNode) const { return at(iNode); } // Node searcher. int find(Node *pNode) const; //---------------------------------------------------------------------- // class qtractorList::Link -- Base list node. // class Link { public: // Constructor. Link() : m_pPrev(0), m_pNext(0), m_pNextFree(0) {} // Linked node getters. Node *prev() const { return m_pPrev; } Node *next() const { return m_pNext; } // Linked node setters. void setPrev(Node *pPrev) { m_pPrev = pPrev; } void setNext(Node *pNext) { m_pNext = pNext; } // Linked free node accessors. Node *nextFree() const { return m_pNextFree; } void setNextFree(Node *pNextFree) { m_pNextFree = pNextFree; } private: // Instance variables. Node *m_pPrev; Node *m_pNext; Node *m_pNextFree; }; //---------------------------------------------------------------------- // class qtractorList::Iterator -- List iterator (aka cursor). // class Iterator { public: // Constructors. Iterator(qtractorList& list) : m_list(list), m_pNode(0) {} Iterator(const Iterator& it) : m_list(it.list()), m_pNode(it.node()) {} // Iterator methods. Iterator& first() { m_pNode = m_list.first(); return *this; } Iterator& next() { m_pNode = m_pNode->next(); return *this; } Iterator& prev() { m_pNode = m_pNode->prev(); return *this; } Iterator& last() { m_pNode = m_list.last(); return *this; } // Operator methods Iterator& operator= (const Iterator& iter) { m_pNode = iter.m_pNode; return *this; } Iterator& operator= (Node *pNode) { m_pNode = pNode; return *this; } Iterator& operator++ () { return next(); } Iterator operator++ (int) { Iterator it(*this); next(); return it; } Iterator& operator-- () { return prev(); } Iterator operator-- (int) { Iterator it(*this); prev(); return it; } // Simple accessors. const qtractorList& list() const { return m_list; } Node *node() const { return m_pNode; } private: // Instance variables. qtractorList& m_list; // Current cursory node reference. Node *m_pNode; }; private: // Instance variables. Node *m_pFirst; Node *m_pLast; int m_iCount; // The reclaimed freelist. Node *m_pFreeList; bool m_bAutoDelete; }; // Insert methods. template void qtractorList::insertAfter ( Node *pNode, Node *pPrevNode ) { if (pPrevNode == 0) pPrevNode = m_pLast; pNode->setPrev(pPrevNode); if (pPrevNode) { pNode->setNext(pPrevNode->next()); if (pPrevNode->next()) (pPrevNode->next())->setPrev(pNode); else m_pLast = pNode; pPrevNode->setNext(pNode); } else { m_pFirst = m_pLast = pNode; pNode->setNext(0); } ++m_iCount; } template void qtractorList::insertBefore ( Node *pNode, Node *pNextNode ) { if (pNextNode == 0) pNextNode = m_pFirst; pNode->setNext(pNextNode); if (pNextNode) { pNode->setPrev(pNextNode->prev()); if (pNextNode->prev()) (pNextNode->prev())->setNext(pNode); else m_pFirst = pNode; pNextNode->setPrev(pNode); } else { m_pLast = m_pFirst = pNode; pNode->setPrev(0); } ++m_iCount; } // Unlink method. template void qtractorList::unlink ( Node *pNode ) { if (pNode->prev()) (pNode->prev())->setNext(pNode->next()); else m_pFirst = pNode->next(); if (pNode->next()) (pNode->next())->setPrev(pNode->prev()); else m_pLast = pNode->prev(); --m_iCount; } // Remove method. template void qtractorList::remove ( Node *pNode ) { unlink(pNode); // Add it to the alternate free list. if (m_bAutoDelete) { Node *pNextFree = m_pFreeList; pNode->setNextFree(pNextFree); m_pFreeList = pNode; } } // Reset methods. template void qtractorList::clear (void) { // Remove pending items. Node *pLast = m_pLast; while (pLast) { remove(pLast); pLast = m_pLast; } // Free the free-list altogether... Node *pFreeList = m_pFreeList; while (pFreeList) { Node *pNextFree = pFreeList->nextFree(); delete pFreeList; pFreeList = pNextFree; } // Force clen up. m_pFirst = m_pLast = 0; m_iCount = 0; m_pFreeList = 0; } // Random accessor. template Node *qtractorList::at ( int iNode ) const { int i; Node *pNode; if (iNode < 0 || iNode >= m_iCount) return 0; if (iNode > (m_iCount >> 1)) { for (i = m_iCount - 1, pNode = m_pLast; pNode && i > iNode; --i, pNode = pNode->prev()) ; } else { for (i = 0, pNode = m_pFirst; pNode && i < iNode; ++i, pNode = pNode->next()) ; } return pNode; } // Node searcher. template int qtractorList::find ( Node *pNode ) const { int iNode = 0; Node *pNextNode = m_pFirst; while (pNextNode) { if (pNode == pNextNode) return iNode; pNextNode = pNextNode->next(); ++iNode; } return (-1); } #endif // __qtractorList_h // end of qtractorList.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiEditSelect.h0000644000000000000000000000013215101070305020003 xustar0030 mtime=1761898693.079267629 30 atime=1761898693.079267629 30 ctime=1761898693.079267629 qtractor-1.5.9/src/qtractorMidiEditSelect.h0000644000175000001440000000600315101070305017772 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.9/src/PaxHeaders/qtractorMidiEditSelect.cpp0000644000000000000000000000013215101070305020336 xustar0030 mtime=1761898693.079267629 30 atime=1761898693.079267629 30 ctime=1761898693.079267629 qtractor-1.5.9/src/qtractorMidiEditSelect.cpp0000644000175000001440000000767115101070305020341 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.9/src/PaxHeaders/qtractorClapPlugin.cpp0000644000000000000000000000013215101070305017544 xustar0030 mtime=1761898693.066267588 30 atime=1761898693.066267588 30 ctime=1761898693.066267588 qtractor-1.5.9/src/qtractorClapPlugin.cpp0000644000175000001440000026023015101070305017537 0ustar00rncbcusers// qtractorClapPlugin.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_CLAP #include "qtractorClapPlugin.h" #include "qtractorSession.h" #include "qtractorAudioEngine.h" #include "qtractorMidiManager.h" #include "qtractorCurve.h" #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) #include #endif #if 0//QTRACTOR_CLAP_EDITOR_TOOL #include "qtractorOptions.h" #endif #include "qtractorMainForm.h" //----------------------------------------------------------------------------- // class qtractorClapPluginHost -- CLAP plugin host (singleton) decl. // class qtractorClapPluginHost { public: // Constructor. qtractorClapPluginHost (); // Destructor. ~qtractorClapPluginHost (); // Common CLAP host struct initialization... // static void setup ( clap_host *host, void *host_data ) { ::memset(host, 0, sizeof(clap_host)); host->host_data = host_data; host->clap_version = CLAP_VERSION; host->name = "qtractorClapPlugin"; host->version = PROJECT_VERSION; host->vendor = QTRACTOR_DOMAIN; host->url = QTRACTOR_WEBSITE; host->get_extension = get_extension; host->request_restart = request_restart; host->request_process = request_process; host->request_callback = request_callback; } // Dummy host callbacks... // 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) {} // Timer support... // void startTimer (int msecs); void stopTimer (); int timerInterval() const; bool register_timer ( const clap_host *host, uint32_t period_ms, clap_id *timer_id); bool unregister_timer ( const clap_host *host, clap_id timer_id); void process_timers(); // POSIX FD support... // bool register_posix_fd ( const clap_host_t *host, int fd, clap_posix_fd_flags_t flags); bool modify_posix_fd ( const clap_host_t *host, int fd, clap_posix_fd_flags_t flags); bool unregister_posix_fd ( const clap_host_t *host, int fd); void process_posix_fds( const clap_host *host, int fd, clap_posix_fd_flags_t flags); // Transport info accessor. const clap_event_transport *transport() const { return &m_transport; } void transportAddRef() { ++m_transportRefCount; } void transportReleaseRef() { if (m_transportRefCount > 0) --m_transportRefCount; } // Common host-time keeper (static) void updateTransport(qtractorAudioEngine *pAudioEngine); // Cleanup. void clear(const clap_host *host); void clear(); struct Key { Key(const clap_host *h, int i) : host(h), id(i) {} Key(const Key& key) : host(key.host), id(key.id) {} bool operator== (const Key& key) const { return (key.host == host && key.id == id); } const clap_host *host; int id; }; protected: class Timer; private: // Instance members. Timer *m_pTimer; uint32_t m_timerRefCount; struct TimerItem { TimerItem(uint32_t msecs) : interval(msecs), counter(0) {} uint32_t interval; uint32_t counter; }; QHash m_timer_ids; QHash m_timers; struct PosixFdItem { PosixFdItem(int fd, clap_posix_fd_flags_t flags) : r_notifier(nullptr), w_notifier(nullptr) { reset(fd, flags); } ~PosixFdItem() { reset(0, 0); } void reset(int fd, clap_posix_fd_flags_t flags) { if (r_notifier) { delete r_notifier; r_notifier = nullptr; } if (flags & CLAP_POSIX_FD_READ) r_notifier = new QSocketNotifier(fd, QSocketNotifier::Read); if (w_notifier) { delete w_notifier; w_notifier = nullptr; } if (flags & CLAP_POSIX_FD_WRITE) w_notifier = new QSocketNotifier(fd, QSocketNotifier::Write); } QSocketNotifier *r_notifier; QSocketNotifier *w_notifier; }; QHash m_posix_fds; // Transport info. clap_event_transport m_transport; unsigned int m_transportRefCount; }; static uint qHash ( const qtractorClapPluginHost::Key& key ) { return qHash(key.host) ^ qHash(key.id); } //----------------------------------------------------------------------------- // class qtractorClapPluginHost::Timer -- CLAP plugin host timer impl. // class qtractorClapPluginHost::Timer : public QTimer { public: // Constructor. Timer (qtractorClapPluginHost *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->process_timers(); } private: // Instance members. qtractorClapPluginHost *m_pHost; }; // Host singleton. static qtractorClapPluginHost g_host; //---------------------------------------------------------------------- // class qtractorClapPluginType::Impl -- CLAP plugin meta-interface impl. // class qtractorClapPluginType::Impl { public: // Constructor. Impl (qtractorPluginFile *pFile) : m_pFile(pFile), m_entry(nullptr), m_factory(nullptr), m_descriptor(nullptr) { qtractorClapPluginHost::setup(&m_host, this); } // Destructor. ~Impl () { close(); } // Executive methods. bool open (unsigned long iIndex); void close (); const clap_plugin *create_plugin(const clap_host *host); const clap_host *host() const { return &m_host; } const clap_plugin_descriptor *descriptor() const { return m_descriptor; } private: // Instance members. qtractorPluginFile *m_pFile; const clap_plugin_entry *m_entry; const clap_plugin_factory *m_factory; const clap_plugin_descriptor *m_descriptor; clap_host m_host; }; //---------------------------------------------------------------------- // class qtractorClapPluginType::Impl -- CLAP plugin interface impl. // // Executive methods. // bool qtractorClapPluginType::Impl::open ( unsigned long iIndex ) { close(); m_entry = reinterpret_cast ( m_pFile->resolve("clap_entry")); if (!m_entry) return false; const QByteArray aFilename = m_pFile->filename().toUtf8(); m_entry->init(aFilename.constData()); m_factory = static_cast ( m_entry->get_factory(CLAP_PLUGIN_FACTORY_ID)); if (!m_factory) return false; auto count = m_factory->get_plugin_count(m_factory); if (iIndex >= count) { qDebug("qtractorClapPluginType::Impl[%p]::open(%lu)" " *** Bad plug-in index.", this, iIndex); return false; } m_descriptor = m_factory->get_plugin_descriptor(m_factory, iIndex); if (!m_descriptor) { qDebug("qtractorClapPluginType::Impl[%p]::open(%lu)" " *** No plug-in descriptor.", this, iIndex); return false; } if (!clap_version_is_compatible(m_descriptor->clap_version)) { qDebug("qtractorClapPluginType::Impl[%p]::open(%lu)" " *** Incompatible CLAP version:" " plug-in is %d.%d.%d, host is %d.%d.%d.", this, iIndex, m_descriptor->clap_version.major, m_descriptor->clap_version.minor, m_descriptor->clap_version.revision, CLAP_VERSION.major, CLAP_VERSION.minor, CLAP_VERSION.revision); return false; } return true; } const clap_plugin *qtractorClapPluginType::Impl::create_plugin ( const clap_host *host ) { if (!m_factory || !m_descriptor) return nullptr; const clap_plugin *plugin = m_factory->create_plugin(m_factory, host, m_descriptor->id); if (!plugin) { qDebug("qtractorClapPluginType::Impl[%p]::create_plugin(%p)" " *** Could not create plug-in with id: %s.", this, host, m_descriptor->id); } else if (!plugin->init(plugin)) { qDebug("qtractorClapPluginType::Impl[%p]::create_plugin(%p)" " *** Could not initialize plug-in with id: %s.", this, host, m_descriptor->id); plugin->destroy(plugin); plugin = nullptr; } return plugin; } void qtractorClapPluginType::Impl::close (void) { m_descriptor = nullptr; m_factory = nullptr; if (m_entry) { m_entry->deinit(); m_entry = nullptr; } } //---------------------------------------------------------------------- // class qtractorClapPluginType -- CLAP plugin meta-interface impl. // // Constructor. qtractorClapPluginType::qtractorClapPluginType ( qtractorPluginFile *pFile, unsigned long iIndex ) : qtractorPluginType(pFile, iIndex, Clap), m_pImpl(new Impl(pFile)), m_iMidiDialectIns(0), m_iMidiDialectOuts(0) { } // Destructor. qtractorClapPluginType::~qtractorClapPluginType (void) { close(); delete m_pImpl; } // Factory method (static) qtractorClapPluginType *qtractorClapPluginType::createType ( qtractorPluginFile *pFile, unsigned long iIndex ) { return new qtractorClapPluginType(pFile, iIndex); } // Executive methods. bool qtractorClapPluginType::open (void) { close(); if (!m_pImpl->open(index())) return false; const clap_plugin_descriptor *descriptor = m_pImpl->descriptor(); if (!descriptor) return false; const clap_plugin *plugin = m_pImpl->create_plugin(m_pImpl->host()); if (!plugin) return false; m_sName = QString::fromUtf8(descriptor->name); m_sLabel = m_sName.simplified().replace(QRegularExpression("[\\s|\\.|\\-]+"), "_"); m_iUniqueID = qHash(descriptor->id); m_iAudioIns = 0; m_iAudioOuts = 0; const clap_plugin_audio_ports *audio_ports = static_cast ( plugin->get_extension(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(plugin, true); for (uint32_t i = 0; i < nins; ++i) { ::memset(&info, 0, sizeof(info)); if (audio_ports->get(plugin, i, true, &info)) { if (info.flags & CLAP_AUDIO_PORT_IS_MAIN) m_iAudioIns += info.channel_count; } } const uint32_t nouts = audio_ports->count(plugin, false); for (uint32_t i = 0; i < nouts; ++i) { ::memset(&info, 0, sizeof(info)); if (audio_ports->get(plugin, i, false, &info)) { if (info.flags & CLAP_AUDIO_PORT_IS_MAIN) m_iAudioOuts += info.channel_count; } } } m_iMidiIns = 0; m_iMidiOuts = 0; m_iMidiDialectIns = 0; m_iMidiDialectOuts = 0; const clap_plugin_note_ports *note_ports = static_cast ( plugin->get_extension(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(plugin, true); for (uint32_t i = 0; i < nins; ++i) { ::memset(&info, 0, sizeof(info)); if (note_ports->get(plugin, i, true, &info)) { if (info.supported_dialects & CLAP_NOTE_DIALECT_MIDI) ++m_iMidiDialectIns; ++m_iMidiIns; } } const uint32_t nouts = note_ports->count(plugin, false); for (uint32_t i = 0; i < nouts; ++i) { ::memset(&info, 0, sizeof(info)); if (note_ports->get(plugin, i, false, &info)) { if (info.supported_dialects & CLAP_NOTE_DIALECT_MIDI) ++m_iMidiDialectOuts; ++m_iMidiOuts; } } } m_iControlIns = 0; m_iControlOuts = 0; const clap_plugin_params *params = static_cast ( plugin->get_extension(plugin, CLAP_EXT_PARAMS)); if (params && params->count && params->get_info) { const uint32_t nparams = params->count(plugin); for (uint32_t i = 0; i < nparams; ++i) { clap_param_info param_info; ::memset(¶m_info, 0, sizeof(param_info)); if (params->get_info(plugin, i, ¶m_info)) { if (param_info.flags & CLAP_PARAM_IS_READONLY) ++m_iControlOuts; else if (param_info.flags & CLAP_PARAM_IS_AUTOMATABLE) ++m_iControlIns; } } } m_bEditor = false; const clap_plugin_gui *gui = static_cast ( plugin->get_extension(plugin, CLAP_EXT_GUI)); if (gui && gui->is_api_supported && gui->create && gui->destroy) { m_bEditor = ( gui->is_api_supported(plugin, CLAP_WINDOW_API_X11, false) || gui->is_api_supported(plugin, CLAP_WINDOW_API_X11, true) || gui->is_api_supported(plugin, CLAP_WINDOW_API_WAYLAND, false) || gui->is_api_supported(plugin, CLAP_WINDOW_API_WAYLAND, true) ); } m_bRealtime = true; const clap_plugin_state *state = static_cast ( plugin->get_extension(plugin, CLAP_EXT_STATE)); m_bConfigure = (state && state->save && state->load); plugin->destroy(plugin); m_sAboutText.clear(); #if 0 m_sAboutText += QObject::tr("Name: "); m_sAboutText += m_sName; #endif QString sText = QString::fromUtf8(descriptor->version); if (!sText.isEmpty()) { // m_sAboutText += '\n'; m_sAboutText += QObject::tr("Version: "); m_sAboutText += sText; } sText = QString("CLAP %1.%2.%3") .arg(descriptor->clap_version.major) .arg(descriptor->clap_version.minor) .arg(descriptor->clap_version.revision); if (!sText.isEmpty()) { m_sAboutText += ' '; // '\t'? m_sAboutText += '('; m_sAboutText += sText; m_sAboutText += ')'; } sText = QString::fromUtf8(descriptor->vendor); if (!sText.isEmpty()) { m_sAboutText += '\n'; m_sAboutText += QObject::tr("Vendor: "); m_sAboutText += sText; } sText = QString::fromUtf8(descriptor->url); if (!sText.isEmpty()) { m_sAboutText += '\n'; // m_sAboutText += QObject::tr("URL: "); m_sAboutText += sText; } #if 0 sText = QString::fromUtf8(descriptor->manual_url); if (!sText.isEmpty()) { m_sAboutText += '\n'; m_sAboutText += QObject::tr("Manual: "); m_sAboutText += sText; } sText = QString::fromUtf8(descriptor->support_url); if (!sText.isEmpty()) { m_sAboutText += '\n'; m_sAboutText += QObject::tr("Support: "); m_sAboutText += sText; } #endif sText = QString::fromUtf8(descriptor->description); if (!sText.isEmpty()) { m_sAboutText += '\n'; m_sAboutText += sText; } QStringList features; for (int i = 0; descriptor->features[i]; ++i) features << QString::fromUtf8(descriptor->features[i]); sText = features.join(' '); if (!sText.isEmpty()) { m_sAboutText += '\n'; // m_sAboutText += QObject::tr("Features: "); m_sAboutText += '('; m_sAboutText += sText; m_sAboutText += ')'; } return true; } void qtractorClapPluginType::close (void) { m_pImpl->close(); } //---------------------------------------------------------------------- // class qtractorClapPlugin::Impl -- CLAP plugin interface impl. // class qtractorClapPlugin::Impl { public: // Constructor. Impl (qtractorClapPlugin *pPlugin); // Destructor. ~Impl (); // Implementation accessors. const clap_plugin *plugin() const { return m_plugin; } const clap_plugin_gui *gui() const { return m_gui; } const clap_plugin_note_name *note_names () const { return m_note_names; } // Do the actual (de)activation. void activate (); void deactivate (bool force = false); // 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; // Total parameter count. unsigned long getParameterCount() const { return m_param_infos.count(); } clap_id getParameterId (unsigned long iIndex) const { return m_param_ids.value(iIndex, CLAP_INVALID_ID); } // Parameter info accessor. const clap_param_info *getParameterInfo (clap_id id) const { return m_param_infos.value(id, nullptr); } // Set/add a parameter value/point. void setParameter (clap_id id, double alue); // Get current parameter value. double getParameter (clap_id id) const; // Get current parameter value text. QString getParameterText (clap_id id, double value) const; // Events buffer decl. // class EventList { public: EventList ( uint32_t nsize = 1024, uint32_t ncapacity = 8 ) : m_nsize(0), m_eheap(nullptr), m_ehead(nullptr), m_etail(nullptr), m_ihead(0) { resize(nsize); m_elist.reserve(ncapacity); ::memset(&m_ins, 0, sizeof(m_ins)); m_ins.ctx = this; m_ins.size = &events_in_size; m_ins.get = &events_in_get; ::memset(&m_outs, 0, sizeof(m_outs)); m_outs.ctx = this; m_outs.try_push = &events_out_push; } ~EventList () { resize(0); } const clap_input_events *ins () const { return &m_ins; } const clap_output_events *outs () const { return &m_outs; } bool push ( const clap_event_header *eh ) { const uint32_t ntail = m_etail - m_eheap; const uint32_t nsize = ntail + eh->size; if (m_nsize < nsize) resize(nsize << 1); const uint32_t ncapacity = m_elist.capacity(); if (m_elist.size() >= ncapacity) m_elist.reserve(ncapacity << 1); m_elist.push_back(ntail); ::memcpy(m_etail, eh, eh->size); m_etail += eh->size; return true; } const clap_event_header *get ( uint32_t index ) const { const clap_event_header *ret = nullptr; if (index + m_ihead < m_elist.size()) { ret = reinterpret_cast ( m_eheap + m_elist.at(index + m_ihead)); } return ret; } const clap_event_header *pop () { const clap_event_header *ret = nullptr; if (m_ihead < m_elist.size() && m_ehead < m_etail) { ret = reinterpret_cast (m_ehead); m_ehead += ret->size; ++m_ihead; } else clear(); return ret; } size_t size () const { return m_elist.size() - m_ihead; } bool empty () const { return (m_etail == m_ehead); } void clear () { m_ehead = m_eheap; m_etail = m_ehead; m_ihead = 0; m_elist.clear(); } protected: void resize ( uint32_t nsize ) { uint8_t *old_eheap = m_eheap; uint8_t *old_ehead = m_ehead; uint8_t *old_etail = m_etail; m_eheap = nullptr; m_ehead = m_eheap; m_etail = m_ehead; m_nsize = nsize; if (m_nsize > 0) { m_eheap = new uint8_t [m_nsize]; m_ehead = m_eheap; m_etail = m_ehead; if (old_etail > old_ehead) { const uint32_t ntail = old_etail - old_ehead; ::memcpy(m_ehead, old_ehead, ntail); m_etail += ntail; } } if (old_eheap) delete [] old_eheap; } static uint32_t events_in_size ( const clap_input_events *ins ) { const EventList *elist = static_cast (ins->ctx); return elist->size(); } static const clap_event_header_t *events_in_get ( const struct clap_input_events *ins, uint32_t index ) { const EventList *elist = static_cast (ins->ctx); return elist->get(index); } static bool events_out_push ( const clap_output_events *outs, const clap_event_header *eh ) { EventList *elist = static_cast (outs->ctx); return elist->push(eh); } private: uint32_t m_nsize; uint8_t *m_eheap; uint8_t *m_ehead; uint8_t *m_etail; uint32_t m_ihead; std::vector m_elist; clap_input_events m_ins; clap_output_events m_outs; }; // Event buffer accessors. EventList& events_in () { return m_events_in; } EventList& events_out () { return m_events_out; } EventList& params_out () { return m_params_out; } // Plugin preset/state snapshot accessors. bool setState (const QByteArray& data); bool getState (QByteArray& data); // Plugin timer support callback. void plugin_on_timer(clap_id timer_id); // Plugin POSIX FD support callback. void plugin_on_posix_fd(int fd, clap_posix_fd_flags_t flags); // Plugin set/load state stream callbacks. static int64_t plugin_state_read ( const clap_istream *stream, void *buffer, uint64_t size); int64_t plugin_state_read_buffer(void *buffer, uint64_t size); // Plugin get/save state stream callbacks. static int64_t plugin_state_write ( const clap_ostream *stream, const void *buffer, uint64_t size); int64_t plugin_state_write_buffer(const void *buffer, uint64_t size); // Plugin parameters flush. void plugin_params_flush (); // Reinitialize the plugin instance... void plugin_request_restart (); // MIDI specific dialect port counters (accessors). int midiDialectIns() const; int midiDialectOuts() const; protected: // Plugin module (de)initializers. void initialize (); void deinitialize (); // Parameters info/ids (de)initializer. void addParamInfos(); void clearParamInfos(); // Host extensions interface. // static const void *get_extension( const clap_host *host, const char *ext_id); // Main host callbacks... // static void host_request_restart (const clap_host *host); static void host_request_process (const clap_host *host); static void host_request_callback (const clap_host *host); // void plugin_request_restart (); void plugin_request_process (); void plugin_request_callback (); // Host LOG callbacks... // static void host_log ( const clap_host *host, clap_log_severity severity, const char *msg); static const constexpr clap_host_log g_host_log = { Impl::host_log, }; // Host GUI callbacks... // static void host_gui_resize_hints_changed ( const clap_host *host); static bool host_gui_request_resize ( const clap_host *host, uint32_t width, uint32_t height); static bool host_gui_request_show ( const clap_host *host); static bool host_gui_request_hide ( const clap_host *host); static void host_gui_closed ( const clap_host *host, bool was_destroyed); static const constexpr clap_host_gui g_host_gui = { Impl::host_gui_resize_hints_changed, Impl::host_gui_request_resize, Impl::host_gui_request_show, Impl::host_gui_request_hide, Impl::host_gui_closed, }; void plugin_gui_resize_hints_changed (void); bool plugin_gui_request_resize(uint32_t width, uint32_t height); bool plugin_gui_request_show(void); bool plugin_gui_request_hide(void); void plugin_gui_closed (bool was_destroyed); // Host Parameters callbacks... // static void host_params_rescan ( const clap_host *host, clap_param_rescan_flags flags); static void host_params_clear ( const clap_host *host, clap_id param_id, clap_param_clear_flags flags); static void host_params_request_flush ( const clap_host *host); static const constexpr clap_host_params g_host_params = { Impl::host_params_rescan, Impl::host_params_clear, Impl::host_params_request_flush, }; void plugin_params_rescan (clap_param_rescan_flags flags); void plugin_params_clear (clap_id param_id, clap_param_clear_flags flags); void plugin_params_request_flush (); // Host Audio Ports support callbacks... // static bool host_audio_ports_is_rescan_flag_supported ( const clap_host *host, uint32_t flag); static void host_audio_ports_rescan ( const clap_host *host, uint32_t flags); static const constexpr clap_host_audio_ports g_host_audio_ports = { Impl::host_audio_ports_is_rescan_flag_supported, Impl::host_audio_ports_rescan, }; // Host Note Ports support callbacks... // static uint32_t host_note_ports_supported_dialects ( const clap_host *host); static void host_note_ports_rescan ( const clap_host *host, uint32_t flags); static const constexpr clap_host_note_ports g_host_note_ports = { Impl::host_note_ports_supported_dialects, Impl::host_note_ports_rescan, }; // Host Latency callbacks... // static void host_latency_changed ( const clap_host *host); static const constexpr clap_host_latency g_host_latency = { Impl::host_latency_changed, }; void plugin_latency_changed (); // Host Timer support callbacks... // static bool host_register_timer ( const clap_host *host, uint32_t period_ms, clap_id *timer_id); static bool host_unregister_timer ( const clap_host *host, clap_id timer_id); static const constexpr clap_host_timer_support g_host_timer_support = { Impl::host_register_timer, Impl::host_unregister_timer, }; // Host POSIX FD support callbacks... // static bool host_register_posix_fd ( const clap_host *host, int fd, clap_posix_fd_flags_t flags); static bool host_modify_posix_fd ( const clap_host *host, int fd, clap_posix_fd_flags_t flags); static bool host_unregister_posix_fd ( const clap_host *host, int fd); static const constexpr clap_host_posix_fd_support g_host_posix_fd_support = { Impl::host_register_posix_fd, Impl::host_modify_posix_fd, Impl::host_unregister_posix_fd, }; // Host thread-check callbacks... // static bool host_is_main_thread ( const clap_host *host); static bool host_is_audio_thread ( const clap_host *host); static const constexpr clap_host_thread_check g_host_thread_check = { Impl::host_is_main_thread, Impl::host_is_audio_thread, }; // Host thread-pool callbacks... // static bool host_thread_pool_request_exec ( const clap_host *host, uint32_t num_tasks); static const constexpr clap_host_thread_pool g_host_thread_pool = { Impl::host_thread_pool_request_exec, }; // Host state callbacks... // static void host_state_mark_dirty ( const clap_host *host); static const constexpr clap_host_state g_host_state = { Impl::host_state_mark_dirty, }; void plugin_state_mark_dirty (); // Host Note-names callbacks... // static void host_note_name_changed ( const clap_host *host); static const constexpr clap_host_note_name g_host_note_name = { Impl::host_note_name_changed, }; void plugin_note_name_changed (); // Transfer parameter changes... void process_params_out (); private: // Instance variables. qtractorClapPlugin *m_pPlugin; const clap_plugin *m_plugin; const clap_plugin_params *m_params; QHash m_param_ids; QHash m_param_infos; const clap_plugin_timer_support *m_timer_support; const clap_plugin_posix_fd_support *m_posix_fd_support; const clap_plugin_gui *m_gui; const clap_plugin_state *m_state; const clap_plugin_note_name *m_note_names; volatile bool m_params_flush; volatile bool m_activated; volatile bool m_sleeping; volatile bool m_processing; volatile bool m_restarting; clap_host m_host; // Processor parameters. unsigned int m_srate; unsigned int m_nframes; unsigned int m_nframes_max; // Audio processor buffers. clap_audio_buffer m_audio_ins; clap_audio_buffer m_audio_outs; // Events processor buffers. EventList m_events_in; EventList m_events_out; // Parameters processor queue. EventList m_params_out; // Process context. clap_process m_process; // Working state stream buffer. QByteArray m_state_data; }; //----------------------------------------------------------------------------- // class qtractorClapPluginHost -- CLAP plugin host context impl. // // Constructor. qtractorClapPluginHost::qtractorClapPluginHost (void) { m_pTimer = new Timer(this); m_timerRefCount = 0; m_transportRefCount = 0; ::memset(&m_transport, 0, sizeof(m_transport)); } // Destructor. qtractorClapPluginHost::~qtractorClapPluginHost (void) { clear(); delete m_pTimer; } // Timer support... // void qtractorClapPluginHost::startTimer ( int msecs ) { if (++m_timerRefCount == 1) m_pTimer->start(msecs); } void qtractorClapPluginHost::stopTimer (void) { if (m_timerRefCount > 0 && --m_timerRefCount == 0) m_pTimer->stop(); } int qtractorClapPluginHost::timerInterval (void) const { return m_pTimer->interval(); } bool qtractorClapPluginHost::register_timer ( const clap_host *host, uint32_t period_ms, clap_id *timer_id ) { const clap_id id = m_timer_ids.value(host, 0); m_timer_ids.insert(host, id + 1); #ifdef CONFIG_DEBUG qDebug("qtractorClapPluginHost::register_timer(%p, %u, %u)", host, period_ms, id); #endif *timer_id = id; m_timers.insert(Key(host, id), new TimerItem(period_ms)); m_pTimer->start(int(period_ms)); return true; } bool qtractorClapPluginHost::unregister_timer ( const clap_host *host, clap_id timer_id ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPluginHost::unregister_timer(%p, %u)", host, timer_id); #endif const Key key(host, timer_id); TimerItem *timer = m_timers.value(key, nullptr); if (timer) delete timer; m_timers.remove(key); if (m_timers.isEmpty()) m_pTimer->stop(); return true; } // POSIX FD support... // bool qtractorClapPluginHost::register_posix_fd ( const clap_host_t *host, int fd, clap_posix_fd_flags_t flags ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPluginHost::register_posix_fd(%p, %d, 0x%04x)", host, fd, flags); #endif const Key key(host, fd); PosixFdItem *posix_fd = m_posix_fds.value(key, nullptr); if (posix_fd) { posix_fd->reset(fd, flags); } else { posix_fd = new PosixFdItem(fd, flags); m_posix_fds.insert(key, posix_fd); } if (posix_fd->r_notifier) { QObject::connect(posix_fd->r_notifier, &QSocketNotifier::activated, [this, host, fd] { process_posix_fds(host, fd, CLAP_POSIX_FD_READ); }); } if (posix_fd->w_notifier) { QObject::connect(posix_fd->w_notifier, &QSocketNotifier::activated, [this, host, fd] { process_posix_fds(host, fd, CLAP_POSIX_FD_WRITE); }); } return true; } bool qtractorClapPluginHost::modify_posix_fd ( const clap_host_t *host, int fd, clap_posix_fd_flags_t flags ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPluginHost::modify_posix_fd(%p, %d, 0x%04x)", host, fd, flags); #endif return register_posix_fd(host, fd, flags); } bool qtractorClapPluginHost::unregister_posix_fd ( const clap_host_t *host, int fd ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPluginHost::unregister_posix_fd(%p, %d)", host, fd); #endif const Key key(host, fd); PosixFdItem *posix_fd = m_posix_fds.value(key, nullptr); if (posix_fd) delete posix_fd; m_posix_fds.remove(key); return true; } // Cleanup. void qtractorClapPluginHost::clear ( const clap_host *host ) { QList keys; QList timers; QHash::ConstIterator timer_iter = m_timers.constBegin(); const QHash::ConstIterator& timer_end = m_timers.constEnd(); for ( ; timer_iter != timer_end; ++timer_iter) { const Key& key = timer_iter.key(); TimerItem *timer = timer_iter.value(); if (key.host == host) { timers.append(timer); keys.append(key); } } foreach (const Key& key, keys) m_timers.remove(key); qDeleteAll(timers); keys.clear(); QList posix_fds; QHash::ConstIterator posix_fd_iter = m_posix_fds.constBegin(); const QHash::ConstIterator& posix_fd_end = m_posix_fds.constEnd(); for ( ; posix_fd_iter != posix_fd_end; ++posix_fd_iter) { const Key& key = posix_fd_iter.key(); PosixFdItem *posix_fd = posix_fd_iter.value(); if (key.host == host) { posix_fds.append(posix_fd); keys.append(key); } } foreach (const Key& key, keys) m_posix_fds.remove(key); qDeleteAll(posix_fds); } void qtractorClapPluginHost::clear (void) { m_timerRefCount = 0; m_transportRefCount = 0; qDeleteAll(m_timers); m_timers.clear(); m_timer_ids.clear(); qDeleteAll(m_posix_fds); m_posix_fds.clear(); } void qtractorClapPluginHost::process_timers (void) { QHash::ConstIterator iter = m_timers.constBegin(); const QHash::ConstIterator& iter_end = m_timers.constEnd(); for ( ; iter != iter_end; ++iter) { TimerItem *timer = iter.value(); timer->counter += timerInterval(); if (timer->counter >= timer->interval) { const Key& key = iter.key(); qtractorClapPlugin::Impl *host_data = static_cast (key.host->host_data); if (host_data) host_data->plugin_on_timer(key.id); timer->counter = 0; } } } void qtractorClapPluginHost::process_posix_fds ( const clap_host *host, int fd, clap_posix_fd_flags_t flags ) { qtractorClapPlugin::Impl *host_data = static_cast (host->host_data); if (host_data) host_data->plugin_on_posix_fd(fd, flags); } // Common host-time keeper (static) void qtractorClapPluginHost::updateTransport ( qtractorAudioEngine *pAudioEngine ) { if (m_transportRefCount < 1) return; const qtractorAudioEngine::TimeInfo& timeInfo = pAudioEngine->timeInfo(); if (timeInfo.playing) m_transport.flags |= CLAP_TRANSPORT_IS_PLAYING; else m_transport.flags &= ~CLAP_TRANSPORT_IS_PLAYING; m_transport.flags |= CLAP_TRANSPORT_HAS_SECONDS_TIMELINE; m_transport.song_pos_seconds = CLAP_SECTIME_FACTOR * double(timeInfo.frame) / double(pAudioEngine->sampleRate()); m_transport.flags |= CLAP_TRANSPORT_HAS_BEATS_TIMELINE; m_transport.song_pos_beats = CLAP_BEATTIME_FACTOR * double(timeInfo.beats); m_transport.bar_start = CLAP_BEATTIME_FACTOR * double(timeInfo.barBeats); m_transport.bar_number = timeInfo.bar; m_transport.flags |= CLAP_TRANSPORT_HAS_TEMPO; m_transport.tempo = timeInfo.tempo; m_transport.flags |= CLAP_TRANSPORT_HAS_TIME_SIGNATURE; m_transport.tsig_num = uint16_t(timeInfo.beatsPerBar); m_transport.tsig_denom = uint16_t(timeInfo.beatType); } //---------------------------------------------------------------------- // class qtractorClapPlugin::Impl -- CLAP plugin interface impl. // // Constructor. qtractorClapPlugin::Impl::Impl ( qtractorClapPlugin *pPlugin ) : m_pPlugin(pPlugin), m_plugin(nullptr), m_params(nullptr), m_timer_support(nullptr), m_posix_fd_support(nullptr), m_gui(nullptr), m_state(nullptr), m_note_names(nullptr), m_params_flush(false), m_activated(false), m_sleeping(false), m_processing(false), m_restarting(false), m_srate(44100), m_nframes(0), m_nframes_max(0) { qtractorClapPluginHost::setup(&m_host, this); m_host.get_extension = qtractorClapPlugin::Impl::get_extension; m_host.request_restart = qtractorClapPlugin::Impl::host_request_restart; m_host.request_process = qtractorClapPlugin::Impl::host_request_process; m_host.request_callback = qtractorClapPlugin::Impl::host_request_callback; initialize(); } // Destructor. qtractorClapPlugin::Impl::~Impl (void) { deinitialize(); } // Plugin module initializer. void qtractorClapPlugin::Impl::initialize (void) { qtractorClapPluginType *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 m_plugin = pType->impl()->create_plugin(&m_host); if (!m_plugin) return; m_params = static_cast ( m_plugin->get_extension(m_plugin, CLAP_EXT_PARAMS)); m_timer_support = static_cast ( m_plugin->get_extension(m_plugin, CLAP_EXT_TIMER_SUPPORT)); m_posix_fd_support = static_cast ( m_plugin->get_extension(m_plugin, CLAP_EXT_POSIX_FD_SUPPORT)); m_gui = static_cast ( m_plugin->get_extension(m_plugin, CLAP_EXT_GUI)); m_state = static_cast ( m_plugin->get_extension(m_plugin, CLAP_EXT_STATE)); m_note_names = static_cast ( m_plugin->get_extension(m_plugin, CLAP_EXT_NOTE_NAME)); addParamInfos(); } // Plugin module deinitializer. void qtractorClapPlugin::Impl::deinitialize (void) { g_host.clear(&m_host); deactivate(true); clearParamInfos(); if (m_plugin) { m_plugin->destroy(m_plugin); m_plugin = nullptr; } m_params = nullptr; m_timer_support = nullptr; m_posix_fd_support = nullptr; m_gui = nullptr; m_state = nullptr; m_note_names = nullptr; } // Parameters info/ids (de)initializer. void qtractorClapPlugin::Impl::addParamInfos (void) { if (m_params && m_params->count && m_params->get_info) { const uint32_t nparams = m_params->count(m_plugin); for (uint32_t i = 0; i < nparams; ++i) { clap_param_info *param_info = new clap_param_info; ::memset(param_info, 0, sizeof(clap_param_info)); if (m_params->get_info(m_plugin, i, param_info)) { m_param_ids.insert(i, param_info->id); m_param_infos.insert(param_info->id, param_info); } } } } void qtractorClapPlugin::Impl::clearParamInfos (void) { qDeleteAll(m_param_infos); m_param_infos.clear(); m_param_ids.clear(); } // Do the actual (de)activation. void qtractorClapPlugin::Impl::activate (void) { if (!m_plugin) return; if (m_sleeping) { m_sleeping = false; return; } if (m_activated) return; if (m_srate < 2 || m_nframes < 2 || m_nframes_max < 2) return; plugin_params_request_flush(); if (!m_plugin->activate(m_plugin, double(m_srate), m_nframes, m_nframes_max)) return; m_activated = true; #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl[%p]::activate() processing=%d", this, int(m_processing)); #endif } void qtractorClapPlugin::Impl::deactivate ( bool force ) { if (!m_plugin) return; if (!m_activated) return; if (!m_sleeping && !force) { m_sleeping = true; return; } m_activated = false; m_plugin->deactivate(m_plugin); #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl[%p]::deactivate() processing=%d", this, int(m_processing)); #endif } // Audio processor stuff. bool qtractorClapPlugin::Impl::process_reset ( qtractorAudioEngine *pAudioEngine ) { qtractorClapPluginType *pType = static_cast (m_pPlugin->type()); if (pType == nullptr) return false; deactivate(); m_srate = pAudioEngine->sampleRate(); m_nframes = pAudioEngine->bufferSize(); m_nframes_max = pAudioEngine->bufferSizeEx(); ::memset(&m_audio_ins, 0, sizeof(m_audio_ins)); m_audio_ins.channel_count = pType->audioIns(); ::memset(&m_audio_outs, 0, sizeof(m_audio_outs)); m_audio_outs.channel_count = pType->audioOuts(); m_events_in.clear(); m_events_out.clear(); ::memset(&m_process, 0, sizeof(m_process)); if (pType->audioIns() > 0) { m_process.audio_inputs = &m_audio_ins; m_process.audio_inputs_count = 1; } if (pType->audioOuts() > 0) { m_process.audio_outputs = &m_audio_outs; m_process.audio_outputs_count = 1; } m_process.in_events = m_events_in.ins(); m_process.out_events = m_events_out.outs(); m_process.frames_count = pAudioEngine->blockSize(); m_process.steady_time = 0; m_process.transport = g_host.transport(); activate(); return true; } void qtractorClapPlugin::Impl::process_midi_in ( unsigned char *data, unsigned int size, unsigned long offset, unsigned short port ) { const int midi_dialect_ins = midiDialectIns(); for (unsigned int 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 // after-touch if ((midi_dialect_ins > 0) && (status == 0xc0 || status == 0xd0)) { clap_event_midi ev; ev.header.space_id = CLAP_CORE_EVENT_SPACE_ID; ev.header.type = CLAP_EVENT_MIDI; ev.header.time = offset; ev.header.flags = 0; ev.header.size = sizeof(ev); ev.port_index = port; ev.data[0] = status | channel; ev.data[1] = key; ev.data[2] = 0; m_events_in.push(&ev.header); continue; } // check data size (#2) if (++i >= size) break; // channel value (normalized) const int value = (data[i] & 0x7f); // note on if (status == 0x90) { clap_event_note ev; ev.header.space_id = CLAP_CORE_EVENT_SPACE_ID; ev.header.type = CLAP_EVENT_NOTE_ON; ev.header.time = offset; ev.header.flags = 0; ev.header.size = sizeof(ev); ev.note_id = -1; ev.port_index = port; ev.key = key; ev.channel = channel; ev.velocity = value / 127.0; m_events_in.push(&ev.header); } else // note off if (status == 0x80) { clap_event_note ev; ev.header.space_id = CLAP_CORE_EVENT_SPACE_ID; ev.header.type = CLAP_EVENT_NOTE_OFF; ev.header.time = offset; ev.header.flags = 0; ev.header.size = sizeof(ev); ev.note_id = -1; ev.port_index = port; ev.key = key; ev.channel = channel; ev.velocity = value / 127.0; m_events_in.push(&ev.header); } else // key pressure/poly.aftertouch // control-change // pitch-bend if ((midi_dialect_ins > 0) && (status == 0xa0 || status == 0xb0 || status == 0xe0)) { clap_event_midi ev; ev.header.space_id = CLAP_CORE_EVENT_SPACE_ID; ev.header.type = CLAP_EVENT_MIDI; ev.header.time = offset; ev.header.flags = 0; ev.header.size = sizeof(ev); ev.port_index = port; ev.data[0] = status | channel; ev.data[1] = key; ev.data[2] = value; m_events_in.push(&ev.header); } } } void qtractorClapPlugin::Impl::process ( float **ins, float **outs, unsigned int nframes ) { if (!m_plugin) return; if (!m_activated) return; if (!m_processing && !m_sleeping) { plugin_params_flush(); g_host.transportAddRef(); m_processing = m_plugin->start_processing(m_plugin); } else if (m_processing && (m_sleeping || m_restarting)) { m_plugin->stop_processing(m_plugin); m_processing = false; g_host.transportReleaseRef(); if (m_plugin->reset && !m_restarting) m_plugin->reset(m_plugin); } if (m_processing) { // Run main processing... m_audio_ins.data32 = ins; m_audio_outs.data32 = outs; m_events_out.clear(); m_process.frames_count = nframes; m_plugin->process(m_plugin, &m_process); m_process.steady_time += nframes; m_events_in.clear(); // Transfer parameter changes... process_params_out(); } } // Plugin current latency (in frames); unsigned long qtractorClapPlugin::Impl::latency (void) const { if (m_plugin) { const clap_plugin_latency *latency = static_cast ( m_plugin->get_extension(m_plugin, CLAP_EXT_LATENCY)); if (latency && latency->get) return latency->get(m_plugin); } return 0; } // Set/add a parameter value/point. void qtractorClapPlugin::Impl::setParameter ( clap_id id, double value ) { if (m_plugin) { const clap_param_info *param_info = m_param_infos.value(id, nullptr); if (param_info) { clap_event_param_value ev; ::memset(&ev, 0, sizeof(ev)); ev.header.time = 0; ev.header.type = CLAP_EVENT_PARAM_VALUE; ev.header.space_id = CLAP_CORE_EVENT_SPACE_ID; ev.header.flags = 0; ev.header.size = sizeof(ev); ev.param_id = param_info->id; ev.cookie = param_info->cookie; ev.port_index = 0; ev.key = -1; ev.channel = -1; ev.value = value; m_events_in.push(&ev.header); } } } // Get current parameter value. double qtractorClapPlugin::Impl::getParameter ( clap_id id ) const { double value = 0.0; if (m_plugin && m_params && m_params->get_value) { const clap_param_info *param_info = m_param_infos.value(id, nullptr); if (param_info) m_params->get_value(m_plugin, param_info->id, &value); } return value; } // Get current parameter value text. QString qtractorClapPlugin::Impl::getParameterText ( clap_id id, double value ) const { QString sText; if (m_plugin && m_params && m_params->value_to_text) { const clap_param_info *param_info = m_param_infos.value(id, nullptr); if (param_info) { char szTemp[256]; if (m_params->value_to_text(m_plugin, param_info->id, value, szTemp, sizeof(szTemp))) sText = QString::fromUtf8(szTemp); } } return sText; } // Plugin preset/state snapshot accessors. bool qtractorClapPlugin::Impl::setState ( const QByteArray& data ) { if (!m_plugin) return false; if (m_state && m_state->load) { clap_istream stream; ::memset(&stream, 0, sizeof(stream)); stream.ctx = this; stream.read = Impl::plugin_state_read; m_state_data = data; if (m_state->load(m_plugin, &stream)) { m_state_data.clear(); return true; } } return false; } bool qtractorClapPlugin::Impl::getState ( QByteArray& data ) { if (!m_plugin) return false; if (m_state && m_state->save) { clap_ostream stream; ::memset(&stream, 0, sizeof(stream)); stream.ctx = this; stream.write = Impl::plugin_state_write; m_state_data.clear(); if (m_state->save(m_plugin, &stream)) { data = m_state_data; m_state_data.clear(); return (data.size() > 0); } } return false; } // Plugin set/load state stream callbacks. int64_t qtractorClapPlugin::Impl::plugin_state_read ( const clap_istream *stream, void *buffer, uint64_t size ) { Impl *pImpl = static_cast (stream->ctx); return (pImpl ? pImpl->plugin_state_read_buffer(buffer, size) : -1); } int64_t qtractorClapPlugin::Impl::plugin_state_read_buffer ( void *buffer, uint64_t size ) { if (size > m_state_data.size()) size = m_state_data.size(); ::memcpy(buffer, m_state_data.constData(), size); m_state_data.remove(0, size); return size; } // Plugin get/save state stream callbacks. int64_t qtractorClapPlugin::Impl::plugin_state_write ( const clap_ostream *stream, const void *buffer, uint64_t size ) { Impl *pImpl = static_cast (stream->ctx); return (pImpl ? pImpl->plugin_state_write_buffer(buffer, size) : -1); } int64_t qtractorClapPlugin::Impl::plugin_state_write_buffer ( const void *buffer, uint64_t size ) { m_state_data.append(QByteArray((const char *) buffer, size)); return size; } // Plugin parameters flush. void qtractorClapPlugin::Impl::plugin_params_flush (void) { if (!m_plugin) return; if (!m_params_flush || m_processing) return; m_params_flush = false; m_events_in.clear(); m_events_out.clear(); if (m_params && m_params->flush) { m_params->flush(m_plugin, m_events_in.ins(), m_events_out.outs()); process_params_out(); m_events_out.clear(); } } // Host extensions interface. // const void *qtractorClapPlugin::Impl::get_extension ( const clap_host *host, const char *ext_id ) { const Impl *host_data = static_cast (host->host_data); if (host_data) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorClapPlugin::Impl::get_extension(%p, \"%s\")", host, ext_id); #endif if (::strcmp(ext_id, CLAP_EXT_LOG) == 0) return &host_data->g_host_log; else if (::strcmp(ext_id, CLAP_EXT_GUI) == 0) return &host_data->g_host_gui; else if (::strcmp(ext_id, CLAP_EXT_PARAMS) == 0) return &host_data->g_host_params; else if (::strcmp(ext_id, CLAP_EXT_AUDIO_PORTS) == 0) return &host_data->g_host_audio_ports; else if (::strcmp(ext_id, CLAP_EXT_NOTE_PORTS) == 0) return &host_data->g_host_note_ports; else if (::strcmp(ext_id, CLAP_EXT_LATENCY) == 0) return &host_data->g_host_latency; else if (::strcmp(ext_id, CLAP_EXT_TIMER_SUPPORT) == 0) return &host_data->g_host_timer_support; else if (::strcmp(ext_id, CLAP_EXT_POSIX_FD_SUPPORT) == 0) return &host_data->g_host_posix_fd_support; else if (::strcmp(ext_id, CLAP_EXT_THREAD_CHECK) == 0) return &host_data->g_host_thread_check; else if (::strcmp(ext_id, CLAP_EXT_THREAD_POOL) == 0) return &host_data->g_host_thread_pool; else if (::strcmp(ext_id, CLAP_EXT_STATE) == 0) return &host_data->g_host_state; else if (::strcmp(ext_id, CLAP_EXT_NOTE_NAME) == 0) return &host_data->g_host_note_name; } return nullptr; } // Main host callbacks... // void qtractorClapPlugin::Impl::host_request_restart ( const clap_host *host ) { #ifdef CONFIG_DEBUG qDebug("clap_test_plugin::Impl::host_request_restart(%p)", host); #endif Impl *pImpl = static_cast (host->host_data); if (pImpl) pImpl->plugin_request_restart(); } void qtractorClapPlugin::Impl::host_request_process ( const clap_host *host ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_request_process(%p)", host); #endif Impl *pImpl = static_cast (host->host_data); if (pImpl) pImpl->plugin_request_process(); } void qtractorClapPlugin::Impl::host_request_callback ( const clap_host *host ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_request_callback(%p)", host); #endif Impl *pImpl = static_cast (host->host_data); if (pImpl) pImpl->plugin_request_callback(); } // Main plugin callbacks... // void qtractorClapPlugin::Impl::plugin_request_restart (void) { if (m_restarting || qtractorAudioEngine::isProcessing()) return; m_restarting = true; while (m_processing) { qtractorSession::stabilize(); } m_restarting = false; deactivate(true); if (m_pPlugin) m_pPlugin->restart(); activate(); } void qtractorClapPlugin::Impl::plugin_request_process (void) { // TODO: ?... // } void qtractorClapPlugin::Impl::plugin_request_callback (void) { #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) QMetaObject::invokeMethod(QCoreApplication::instance(), [this]{ m_plugin->on_main_thread(m_plugin); }, Qt::QueuedConnection); #endif } // Host LOG callbacks... // void qtractorClapPlugin::Impl::host_log ( const clap_host *host, clap_log_severity severity, const char *msg ) { switch (severity) { case CLAP_LOG_DEBUG: qDebug("clap_log: Debug: %s", msg); break; case CLAP_LOG_INFO: qInfo("clap_log: Info: %s", msg); break; case CLAP_LOG_WARNING: qWarning("clap_log: Warning: %s", msg); break; case CLAP_LOG_ERROR: qWarning("clap_log: Error: %s", msg); break; case CLAP_LOG_FATAL: qWarning("clap_log: Fatal: %s", msg); break; case CLAP_LOG_HOST_MISBEHAVING: qWarning("clap_log: Host misbehaving: %s", msg); break; default: qDebug("clap_log: Unknown: %s", msg); break; } } // Host GUI callbacks... // void qtractorClapPlugin::Impl::host_gui_resize_hints_changed ( const clap_host *host ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_gui_resize_hints_changed(%p)", host); #endif Impl *pImpl = static_cast (host->host_data); if (pImpl) pImpl->plugin_gui_resize_hints_changed(); } bool qtractorClapPlugin::Impl::host_gui_request_resize ( const clap_host *host, uint32_t width, uint32_t height ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_gui_request_resize(%p, %d, %d)", host, width, height); #endif Impl *pImpl = static_cast (host->host_data); return (pImpl ? pImpl->plugin_gui_request_resize(width, height) : false); } bool qtractorClapPlugin::Impl::host_gui_request_show ( const clap_host *host ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_gui_request_show(%p)", host); #endif Impl *pImpl = static_cast (host->host_data); return (pImpl ? pImpl->plugin_gui_request_show() : false); } bool qtractorClapPlugin::Impl::host_gui_request_hide ( const clap_host *host ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_gui_request_hide(%p)", host); #endif Impl *pImpl = static_cast (host->host_data); return (pImpl ? pImpl->plugin_gui_request_hide() : false); } void qtractorClapPlugin::Impl::host_gui_closed ( const clap_host *host, bool was_destroyed ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_gui_closed(%p)", host); #endif Impl *pImpl = static_cast (host->host_data); if (pImpl) pImpl->plugin_gui_closed(was_destroyed); } // Plugin GUI callbacks... // void qtractorClapPlugin::Impl::plugin_gui_resize_hints_changed (void) { // TODO: ?... // } bool qtractorClapPlugin::Impl::plugin_gui_request_resize ( uint32_t width, uint32_t height ) { if (m_pPlugin == nullptr) return false; QWidget *pWidget = m_pPlugin->editorWidget(); if (pWidget == nullptr) return false; const QSize& max_size = pWidget->maximumSize(); const QSize& min_size = pWidget->minimumSize(); if (min_size.width() == max_size.width() && width != max_size.width()) pWidget->setFixedWidth(width); if (min_size.height() == max_size.height() && height != max_size.height()) pWidget->setFixedHeight(height); pWidget->resize(width, height); return true; } bool qtractorClapPlugin::Impl::plugin_gui_request_show (void) { if (m_pPlugin == nullptr) return false; QWidget *pWidget = m_pPlugin->editorWidget(); if (pWidget == nullptr) return false; pWidget->show(); return true; } bool qtractorClapPlugin::Impl::plugin_gui_request_hide (void) { if (m_pPlugin == nullptr) return false; QWidget *pWidget = m_pPlugin->editorWidget(); if (pWidget == nullptr) return false; pWidget->hide(); return true; } void qtractorClapPlugin::Impl::plugin_gui_closed ( bool was_destroyed ) { if (m_pPlugin) m_pPlugin->closeEditor(); } // Host Parameters callbacks... // void qtractorClapPlugin::Impl::host_params_rescan ( const clap_host *host, clap_param_rescan_flags flags ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_params_rescan(%p, 0x%04x)", host, flags); #endif Impl *pImpl = static_cast (host->host_data); if (pImpl) pImpl->plugin_params_rescan(flags); } void qtractorClapPlugin::Impl::host_params_clear ( const clap_host *host, clap_id param_id, clap_param_clear_flags flags ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_params_clear(%p, %u, 0x%04x)", host, param_id, flags); #endif Impl *pImpl = static_cast (host->host_data); if (pImpl) pImpl->plugin_params_clear(param_id, flags); } void qtractorClapPlugin::Impl::host_params_request_flush ( const clap_host *host ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorClapPlugin::Impl::host_params_request_flush(%p)", host); #endif Impl *pImpl = static_cast (host->host_data); if (pImpl) pImpl->plugin_params_request_flush(); } // Plugin Parameters callbacks... // void qtractorClapPlugin::Impl::plugin_params_rescan ( clap_param_rescan_flags flags ) { if (m_pPlugin == nullptr) return; if (flags & CLAP_PARAM_RESCAN_VALUES) m_pPlugin->updateParamValues(false); else if (flags & (CLAP_PARAM_RESCAN_INFO | CLAP_PARAM_RESCAN_TEXT)) { m_pPlugin->closeForm(true); m_pPlugin->clearParams(); clearParamInfos(); addParamInfos(); m_pPlugin->addParams(); } else if (flags & CLAP_PARAM_RESCAN_ALL) m_pPlugin->request_restart(); } void qtractorClapPlugin::Impl::plugin_params_clear ( clap_id param_id, clap_param_clear_flags flags ) { if (m_pPlugin == nullptr) return; if (!flags || param_id == CLAP_INVALID_ID) return; // Clear all automation curves, if any... qtractorSubject::resetQueue(); // Clear any automation curves and direct access // references to the plugin parameter (param_id)... qtractorPlugin::Param *pParam = m_pPlugin->findParamId(int(param_id)); if (pParam) m_pPlugin->clearParam(pParam); // And mark it dirty, of course... m_pPlugin->updateDirtyCount(); } void qtractorClapPlugin::Impl::plugin_params_request_flush (void) { m_params_flush = true; // plugin_params_flush(); } // Host Audio Ports support callbacks... // bool qtractorClapPlugin::Impl::host_audio_ports_is_rescan_flag_supported ( const clap_host *host, uint32_t flag ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_audio_ports_is_rescan_flag_supported(%p, 0x%04x)", host, flag); #endif // Not supported. // return false; } void qtractorClapPlugin::Impl::host_audio_ports_rescan ( const clap_host *host, uint32_t flags ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_audio_ports_rescan(%p, 0x%04x)", host, flags); #endif // Not supported. // } // Host Note Ports support callbacks... // uint32_t qtractorClapPlugin::Impl::host_note_ports_supported_dialects ( const clap_host *host ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_note_ports_supported_dialects(%p)", host); #endif // Only MIDI 1.0 is scrictly supported. // return CLAP_NOTE_DIALECT_MIDI; } void qtractorClapPlugin::Impl::host_note_ports_rescan ( const clap_host *host, uint32_t flags ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_note_ports_rescan(%p, 0x%04x)", host, flags); #endif // Not supported. // } // Host Latency callbacks... // void qtractorClapPlugin::Impl::host_latency_changed ( const clap_host *host ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_latency_changed(%p)", host); #endif Impl *pImpl = static_cast (host->host_data); if (pImpl) pImpl->plugin_latency_changed(); } // Plugin Latency callbacks... // void qtractorClapPlugin::Impl::plugin_latency_changed (void) { //if (m_pPlugin) // m_pPlugin->request_restart(); } // Host Timer support callbacks... // bool qtractorClapPlugin::Impl::host_register_timer ( const clap_host *host, uint32_t period_ms, clap_id *timer_id ) { return g_host.register_timer(host, period_ms, timer_id); } bool qtractorClapPlugin::Impl::host_unregister_timer ( const clap_host *host, clap_id timer_id ) { return g_host.unregister_timer(host, timer_id); } // Plugin Timer support callbacks... // void qtractorClapPlugin::Impl::plugin_on_timer ( clap_id timer_id ) { if (m_timer_support && m_timer_support->on_timer && m_plugin) m_timer_support->on_timer(m_plugin, timer_id); } // Host POSIX FD support callbacks... // bool qtractorClapPlugin::Impl::host_register_posix_fd ( const clap_host *host, int fd, clap_posix_fd_flags_t flags ) { return g_host.register_posix_fd(host, fd, flags); } bool qtractorClapPlugin::Impl::host_modify_posix_fd ( const clap_host *host, int fd, clap_posix_fd_flags_t flags ) { return g_host.modify_posix_fd(host, fd, flags); } bool qtractorClapPlugin::Impl::host_unregister_posix_fd ( const clap_host *host, int fd ) { return g_host.unregister_posix_fd(host, fd); } // Plugin POSIX FD support callbacks... // void qtractorClapPlugin::Impl::plugin_on_posix_fd ( int fd, clap_posix_fd_flags_t flags ) { if (m_posix_fd_support && m_posix_fd_support->on_fd && m_plugin) m_posix_fd_support->on_fd(m_plugin, fd, flags); } // Host thread-check callbacks... // bool qtractorClapPlugin::Impl::host_is_main_thread ( const clap_host *host ) { return !qtractorAudioEngine::isProcessing(); } bool qtractorClapPlugin::Impl::host_is_audio_thread ( const clap_host *host ) { return qtractorAudioEngine::isProcessing(); } // Host thread-pool callbacks... // bool qtractorClapPlugin::Impl::host_thread_pool_request_exec ( const clap_host *host, uint32_t num_tasks ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_thread_pool_request_exec(%p, %d)", host, num_tasks); #endif // TODO: ?... // return false; } // Host state callbacks... // void qtractorClapPlugin::Impl::host_state_mark_dirty ( const clap_host *host ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_state_mark_dirty(%p)", host); #endif Impl *pImpl = static_cast (host->host_data); if (pImpl) pImpl->plugin_state_mark_dirty(); } // Plugin state callbacks... // void qtractorClapPlugin::Impl::plugin_state_mark_dirty (void) { if (m_pPlugin) m_pPlugin->updateDirtyCount(); } // Host Note-names callbacks... // void qtractorClapPlugin::Impl::host_note_name_changed ( const clap_host *host ) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::Impl::host_note_name_changed(%p)", host); #endif Impl *pImpl = static_cast (host->host_data); if (pImpl) pImpl->plugin_note_name_changed(); } // Plugin Note-name callbacks... // void qtractorClapPlugin::Impl::plugin_note_name_changed (void) { if (m_pPlugin) m_pPlugin->updateNoteNames(); } // Transfer parameter changes... void qtractorClapPlugin::Impl::process_params_out (void) { const uint32_t nevents = m_events_out.size(); for (uint32_t i = 0; i < nevents; ++i) { const clap_event_header *eh = m_events_out.get(i); if (eh && ( eh->type == CLAP_EVENT_PARAM_VALUE || eh->type == CLAP_EVENT_PARAM_GESTURE_BEGIN || eh->type == CLAP_EVENT_PARAM_GESTURE_END)) { m_params_out.push(eh); } } } // MIDI specific dialect port counters (accessors). int qtractorClapPlugin::Impl::midiDialectIns (void) const { qtractorClapPluginType *pType = static_cast (m_pPlugin->type()); return (pType ? pType->midiDialectIns() : 0); } int qtractorClapPlugin::Impl::midiDialectOuts (void) const { qtractorClapPluginType *pType = static_cast (m_pPlugin->type()); return (pType ? pType->midiDialectOuts() : 0); } //---------------------------------------------------------------------------- // qtractorClapPlugin::Param::Impl -- CLAP plugin parameter interface impl. // class qtractorClapPlugin::Param::Impl { public: // Constructor. Impl(const clap_param_info& param_info) : m_param_info(param_info) {} // Accessors. const clap_param_info& param_info() const { return m_param_info; } private: // Instance members. clap_param_info m_param_info; }; //---------------------------------------------------------------------- // class qtractorClapPlugin::EditorWidget -- CLAP plugin editor widget decl. // class qtractorClapPlugin::EditorWidget : public QWidget { public: EditorWidget(QWidget *pParent = nullptr, Qt::WindowFlags wflags = Qt::WindowFlags()); ~EditorWidget (); void setPlugin (qtractorClapPlugin *pPlugin) { m_pPlugin = pPlugin; } qtractorClapPlugin *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: qtractorClapPlugin *m_pPlugin; bool m_resizing; }; //---------------------------------------------------------------------- // class qtractorClapPlugin::EditorWidget -- CLAP plugin editor widget impl. // qtractorClapPlugin::EditorWidget::EditorWidget ( QWidget *pParent, Qt::WindowFlags wflags ) : QWidget(pParent, wflags), m_pPlugin(nullptr), m_resizing(false) { } qtractorClapPlugin::EditorWidget::~EditorWidget (void) { } void qtractorClapPlugin::EditorWidget::resizeEvent ( QResizeEvent *pResizeEvent ) { if (m_resizing) return; if (m_pPlugin == nullptr) return; const QSize& size = pResizeEvent->size(); #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin::EditorWidget[%p]::resizeEvent(%d, %d)", this, size.width(), size.height()); #endif const clap_plugin *plugin = m_pPlugin->impl()->plugin(); if (!plugin) return; const clap_plugin_gui *gui = m_pPlugin->impl()->gui(); if (!gui) return; bool can_resize = false; if (gui->can_resize) can_resize = gui->can_resize(plugin); uint32_t width = 0; uint32_t height = 0; if (gui->get_size) gui->get_size(plugin, &width, &height); if (can_resize) { clap_gui_resize_hints hints; ::memset(&hints, 0, sizeof(hints)); if (gui->get_resize_hints) gui->get_resize_hints(plugin, &hints); if (hints.can_resize_horizontally) width = size.width(); if (hints.can_resize_vertically) height = size.height(); if (gui->adjust_size) gui->adjust_size(plugin, &width, &height); if (gui->set_size) gui->set_size(plugin, width, height); } if (width != size.width() || height != size.height()) { m_resizing = true; QWidget::resize(width, height); m_resizing = false; } } void qtractorClapPlugin::EditorWidget::showEvent ( QShowEvent *pShowEvent ) { QWidget::showEvent(pShowEvent); if (m_pPlugin) m_pPlugin->toggleFormEditor(true); } void qtractorClapPlugin::EditorWidget::closeEvent ( QCloseEvent *pCloseEvent ) { if (m_pPlugin) m_pPlugin->toggleFormEditor(false); QWidget::closeEvent(pCloseEvent); if (m_pPlugin) m_pPlugin->closeEditor(); } //---------------------------------------------------------------------- // class qtractorClapPlugin -- CLAP plugin interface impl. // // Dynamic singleton list of CLAP plugins. static QList g_clapPlugins; // Buffer size large enough to hold a regular MIDI channel event. const long c_iMaxMidiData = 4; // Constructor. qtractorClapPlugin::qtractorClapPlugin ( qtractorPluginList *pList, qtractorClapPluginType *pType ) : qtractorPlugin(pList, pType), m_pImpl(new Impl(this)), m_bEditorCreated(false), m_bEditorVisible(false), m_pEditorWidget(nullptr), m_ppIBuffer(nullptr), m_ppOBuffer(nullptr), m_pfIDummy(nullptr), m_pfODummy(nullptr), m_pMidiParser(nullptr) { initialize(); } // Destructor. qtractorClapPlugin::~qtractorClapPlugin (void) { deinitialize(); delete m_pImpl; } // Plugin instance initializer. void qtractorClapPlugin::initialize (void) { addParams(); // 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]; if (midiIns() > 0 && snd_midi_event_new(c_iMaxMidiData, &m_pMidiParser) == 0) snd_midi_event_no_status(m_pMidiParser, 1); // Instantiate each instance properly... setChannels(channels()); } // Plugin instance de-initializer. void qtractorClapPlugin::deinitialize (void) { // Cleanup all plugin instances... cleanup(); // setChannels(0); clearParams(); clearNoteNames(); // Deallocate I/O audio buffer pointers. if (m_ppIBuffer) { delete [] m_ppIBuffer; m_ppIBuffer = nullptr; } if (m_ppOBuffer) { delete [] m_ppOBuffer; m_ppOBuffer = nullptr; } if (m_pfIDummy) { delete [] m_pfIDummy; m_pfIDummy = nullptr; } if (m_pfODummy) { delete [] m_pfODummy; m_pfODummy = nullptr; } // Deallocate MIDI decoder. if (m_pMidiParser) { snd_midi_event_free(m_pMidiParser); m_pMidiParser = nullptr; } } // Channel/instance number accessors. void qtractorClapPlugin::setChannels ( unsigned short iChannels ) { // Check our type... qtractorClapPluginType *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); // Close old instances, all the way... const int iClapPlugin = g_clapPlugins.indexOf(this); if (iClapPlugin >= 0) g_clapPlugins.removeAt(iClapPlugin); // Bail out, if none are about to be created... if (iInstances < 1) { setChannelsActivated(iChannels, bActivated); return; } #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin[%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); // Finally add it to the CLAP plugin roster... g_clapPlugins.append(this); // (Re)issue all configuration as needed... realizeConfigs(); realizeValues(); // But won't need it anymore. releaseConfigs(); releaseValues(); // Initialize note-names cache. updateNoteNames(); // (Re)activate instance if necessary... setChannelsActivated(iChannels, bActivated); } // Do the actual (de)activation. void qtractorClapPlugin::activate (void) { m_pImpl->activate(); } void qtractorClapPlugin::deactivate (void) { m_pImpl->deactivate(); } // Instance parameters initializer. void qtractorClapPlugin::addParams (void) { const unsigned long nparams = m_pImpl->getParameterCount(); #ifdef CONFIG_DEBUG qDebug(" --- Parameters (nparams = %lu) ---", nparams); #endif for (unsigned long i = 0; i < nparams; ++i) { const clap_id id = m_pImpl->getParameterId(i); if (id == CLAP_INVALID_ID) continue; const clap_param_info *param_info = m_pImpl->getParameterInfo(id); if (param_info) { if ( (param_info->flags & CLAP_PARAM_IS_AUTOMATABLE) && !(param_info->flags & CLAP_PARAM_IS_READONLY)) { Param *pParam = new Param(this, i); m_paramIds.insert(int(param_info->id), pParam); addParam(pParam); } } } } void qtractorClapPlugin::clearParams (void) { m_paramIds.clear(); m_paramValues.clear(); qtractorPlugin::clearParams(); } void qtractorClapPlugin::clearParam ( qtractorPlugin::Param *pParam ) { if (pParam == nullptr) return; qtractorSubject *pSubject = pParam->subject(); if (pSubject) { qtractorCurve *pCurve = pSubject->curve(); if (pCurve) { qtractorCurveList *pCurveList = pCurve->list(); if (pCurveList) pCurveList->removeCurve(pCurve); pSubject->setCurve(nullptr); delete pCurve; } } // Clear direct access, if any... if (directAccessParamIndex() == pParam->index()) setDirectAccessParamIndex(-1); } // Parameter update method. void qtractorClapPlugin::updateParam ( qtractorPlugin::Param *pParam, float fValue, bool /*bUpdate*/ ) { qtractorClapPluginType *pType = static_cast (type()); if (pType == nullptr) return; Param *pClapParam = static_cast (pParam); if (pClapParam == nullptr) return; if (pClapParam->impl() == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorClapPlugin[%p]::updateParam(%lu, %g, %d)", this, pParam->index(), fValue, int(bUpdate)); #endif const clap_id id = pClapParam->impl()->param_info().id; const double value = double(fValue); m_pImpl->setParameter(id, value); } // All parameters update method. void qtractorClapPlugin::updateParamValues ( bool bUpdate ) { 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()) { const clap_id id = pParam->impl()->param_info().id; const float fValue = float(m_pImpl->getParameter(id)); if (pParam->value() != fValue) { pParam->setValue(fValue, bUpdate); ++nupdate; } } } if (nupdate > 0) updateFormDirtyCount(); } // Parameter finder (by id). qtractorPlugin::Param *qtractorClapPlugin::findParamId ( int id ) const { return m_paramIds.value(id, nullptr); } // Configuration state stuff. void qtractorClapPlugin::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("qtractorClapPlugin[%p]::configure() data.size=%d", this, int(data.size())); #endif m_pImpl->setState(data); } } // Plugin configuration/state snapshot. void qtractorClapPlugin::freezeConfigs (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorClapPlugin[%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("qtractorClapPlugin[%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 qtractorClapPlugin::releaseConfigs (void) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin[%p]::releaseConfigs()", this); #endif qtractorPlugin::clearConfigs(); } // Provisional note name accessor. bool qtractorClapPlugin::getNoteName ( int iIndex, NoteName& note ) const { if (iIndex < 0 || iIndex >= m_noteNames.count()) return false; note = *m_noteNames.at(iIndex); return true; } // Update/clear instrument/note names cache. void qtractorClapPlugin::updateNoteNames (void) { clearNoteNames(); const clap_plugin *plugin = m_pImpl->plugin(); if (!plugin) return; const clap_plugin_note_name *note_names = m_pImpl->note_names();; if (note_names && note_names->count && note_names->get) { const int ncount = note_names->count(plugin); for (int i = 0; i < ncount; ++i) { clap_note_name note_name; ::memset(¬e_name, 0, sizeof(note_name)); if (note_names->get(plugin, i, ¬e_name)) { NoteName *note = new NoteName; note->bank = -1; note->prog = -1; note->note = note_name.key; note->name = note_name.name; m_noteNames.append(note); } } } } // Clear instrument/note names cache. void qtractorClapPlugin::clearNoteNames (void) { qDeleteAll(m_noteNames); m_noteNames.clear(); } // Open/close editor widget. void qtractorClapPlugin::openEditor ( QWidget *pParent ) { qtractorClapPluginType *pType = static_cast (type()); if (pType == nullptr) return; if (!pType->isEditor()) return; // Is it already there? if (m_bEditorCreated && m_pEditorWidget) { if (!m_pEditorWidget->isVisible()) { moveWidgetPos(m_pEditorWidget, editorPos()); m_pEditorWidget->show(); } m_pEditorWidget->raise(); m_pEditorWidget->activateWindow(); return; } const clap_plugin *plugin = m_pImpl->plugin(); if (!plugin) return; const clap_plugin_gui *gui = m_pImpl->gui(); if (!gui) return; // Tell the world we'll (maybe) take some time... qtractorPluginList::WaitCursor waiting; #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin[%p]::openEditor(%p)", this, pParent); #endif const WId parent_wid = (pParent ? pParent->winId() : WId(nullptr)); clap_window w; w.api = CLAP_WINDOW_API_X11; w.x11 = parent_wid; bool is_floating = false; if (!gui->is_api_supported(plugin, w.api, false)) is_floating = gui->is_api_supported(plugin, w.api, true); if (!gui->create(plugin, w.api, is_floating)) { qWarning("qtractorClapPlugin[%p]::openEditor: could not create the plugin GUI.", this); return; } const QString& sTitle = pType->name(); if (is_floating) { gui->set_transient(plugin, &w); gui->suggest_title(plugin, sTitle.toUtf8().constData()); } else { // What style do we create tool childs? Qt::WindowFlags wflags = Qt::Window; #if 0//QTRACTOR_CLAP_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(sTitle); m_pEditorWidget->setWindowIcon(QIcon::fromTheme("qtractorPlugin")); w.x11 = m_pEditorWidget->parentWinId(); if (!gui->set_parent(plugin, &w)) { qWarning("qtractorClapPlugin[%p]::openEditor: could not embbed the plugin GUI.", this); delete m_pEditorWidget; m_pEditorWidget = nullptr; gui->destroy(plugin); return; } bool can_resize = false; uint32_t width = 0; uint32_t height = 0; clap_gui_resize_hints hints; ::memset(&hints, 0, sizeof(hints)); if (gui->can_resize) can_resize = gui->can_resize(plugin); if (gui->get_resize_hints && !gui->get_resize_hints(plugin, &hints)) qWarning("qtractorClapPlugin[%p]::openEditor: could not get the resize hints of the plugin GUI.", this); if (gui->get_size && !gui->get_size(plugin, &width, &height)) qWarning("qtractorClapPlugin[%p]::openEditor: could not get the size of the plugin GUI.", this); if (width > 0 && (!hints.can_resize_horizontally || !can_resize)) m_pEditorWidget->setFixedWidth(width); if (height > 0 && (!hints.can_resize_vertically || !can_resize)) m_pEditorWidget->setFixedHeight(height); if (width > 0 && height > 0) m_pEditorWidget->resize(width, height); m_pEditorWidget->setPlugin(this); } g_host.startTimer(200); m_bEditorCreated = true; m_bEditorVisible = false; // Final stabilization... updateEditorTitle(); if (m_pEditorWidget) moveWidgetPos(m_pEditorWidget, editorPos()); setEditorVisible(true); } void qtractorClapPlugin::closeEditor (void) { if (!m_bEditorCreated) return; const clap_plugin *plugin = m_pImpl->plugin(); if (!plugin) return; const clap_plugin_gui *gui = m_pImpl->gui(); if (!gui) return; #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin[%p]::closeEditor()", this); #endif setEditorVisible(false); g_host.stopTimer(); gui->destroy(plugin); m_bEditorCreated = false; m_bEditorVisible = false; if (m_pEditorWidget) { delete m_pEditorWidget; m_pEditorWidget = nullptr; } } // Parameters post-update method. void qtractorClapPlugin::idleEditor (void) { int nupdate = 0; Impl::EventList& params_out = m_pImpl->params_out(); const clap_event_header *eh = params_out.pop(); for ( ; eh; eh = params_out.pop()) { int param_id = CLAP_INVALID_ID; double value = 0.0; // Check if we're not middle of a gesture... if (eh->type == CLAP_EVENT_PARAM_GESTURE_BEGIN) { const clap_event_param_gesture *ev = reinterpret_cast (eh); if (ev && ev->param_id != CLAP_INVALID_ID) m_paramValues.insert(int(ev->param_id), 0.0); } else if (eh->type == CLAP_EVENT_PARAM_GESTURE_END) { const clap_event_param_gesture *ev = reinterpret_cast (eh); if (ev && ev->param_id != CLAP_INVALID_ID) { param_id = int(ev->param_id); value = m_paramValues.value(param_id, 0.0); m_paramValues.remove(param_id); } } else if (eh->type == CLAP_EVENT_PARAM_VALUE) { const clap_event_param_value *ev = reinterpret_cast (eh); if (ev && ev->param_id != CLAP_INVALID_ID) { param_id = ev->param_id; value = ev->value; if (m_paramValues.contains(param_id)) { m_paramValues.insert(param_id, value); param_id = CLAP_INVALID_ID; } } } // Actual make the change... if (param_id != CLAP_INVALID_ID) { qtractorPlugin::Param *pParam = findParamId(param_id); if (pParam) { pParam->setValue(float(value), false); ++nupdate; } } } params_out.clear(); if (nupdate > 0) updateDirtyCount(); } // GUI editor visibility state. void qtractorClapPlugin::setEditorVisible ( bool bVisible ) { if (!m_bEditorCreated) return; const clap_plugin *plugin = m_pImpl->plugin(); if (!plugin) return; const clap_plugin_gui *gui = m_pImpl->gui(); if (!gui) return; if (m_pEditorWidget && !bVisible) setEditorPos(m_pEditorWidget->pos()); if (bVisible && !m_bEditorVisible) { gui->show(plugin); if (m_pEditorWidget) m_pEditorWidget->show(); m_bEditorVisible = true; } else if (!bVisible && m_bEditorVisible) { gui->hide(plugin); if (m_pEditorWidget) m_pEditorWidget->hide(); m_bEditorVisible = false; } if (m_pEditorWidget && bVisible) { m_pEditorWidget->raise(); m_pEditorWidget->activateWindow(); } } bool qtractorClapPlugin::isEditorVisible (void) const { return m_bEditorVisible; } // Update editor widget caption. void qtractorClapPlugin::setEditorTitle ( const QString& sTitle ) { qtractorPlugin::setEditorTitle(sTitle); if (!m_bEditorCreated) return; const clap_plugin *plugin = m_pImpl->plugin(); if (!plugin) return; const clap_plugin_gui *gui = m_pImpl->gui(); if (!gui) return; if (m_pEditorWidget) m_pEditorWidget->setWindowTitle(sTitle); else gui->suggest_title(plugin, sTitle.toUtf8().constData()); } // GUI editor widget handle (if not floating). QWidget *qtractorClapPlugin::editorWidget (void) const { return m_pEditorWidget; } // GUI editor created/active state. bool qtractorClapPlugin::isEditorCreated (void) const { return m_bEditorCreated; } // Processor stuff... // void qtractorClapPlugin::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 qtractorClapPlugin::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(); Impl::EventList& events_out = m_pImpl->events_out(); const uint32_t nevents = events_out.size(); for (uint32_t i = 0; i < nevents; ++i) { const clap_event_header *eh = events_out.get(i); if (eh) { snd_seq_event_t ev; snd_seq_ev_clear(&ev); switch (eh->type) { case CLAP_EVENT_NOTE_ON: { const clap_event_note *en = reinterpret_cast (eh); if (en) { ev.type = SND_SEQ_EVENT_NOTEON; ev.data.note.channel = en->channel; ev.data.note.note = en->key; ev.data.note.velocity = en->velocity; } break; } case CLAP_EVENT_NOTE_OFF: { const clap_event_note *en = reinterpret_cast (eh); if (en) { ev.type = SND_SEQ_EVENT_NOTEOFF; ev.data.note.channel = en->channel; ev.data.note.note = en->key; ev.data.note.velocity = en->velocity; } break; } case CLAP_EVENT_MIDI: { const clap_event_midi *em = reinterpret_cast (eh); if (em) { unsigned char *pMidiData = (unsigned char *) &em->data[0]; long iMidiData = sizeof(em->data); iMidiData = snd_midi_event_encode(m_pMidiParser, pMidiData, iMidiData, &ev); if (iMidiData < 1) ev.type = SND_SEQ_EVENT_NONE; } break; }} if (ev.type != SND_SEQ_EVENT_NONE) pMidiBuffer->push(&ev, eh->time); } } pMidiManager->swapOutputBuffers(); } else { pMidiManager->resetOutputBuffers(); } } } // Plugin current latency (in frames); unsigned long qtractorClapPlugin::latency (void) const { return m_pImpl->latency(); } // Plugin preset i/o (configuration from/to state files). bool qtractorClapPlugin::loadPresetFile ( const QString& sFilename ) { const QString& sExt = QFileInfo(sFilename).suffix().toLower(); if (sExt == "qtx") return qtractorPlugin::loadPresetFile(sFilename); #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin[%p]::loadPresetFile(\"%s\")", this, sFilename.toUtf8().constData()); #endif QFile file(sFilename); if (!file.open(QFile::ReadOnly)) { #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin[%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. updateParamValues(false); return bResult; } bool qtractorClapPlugin::savePresetFile ( const QString& sFilename ) { const QString& sExt = QFileInfo(sFilename).suffix().toLower(); if (sExt == "qtx") return qtractorPlugin::savePresetFile(sFilename); #ifdef CONFIG_DEBUG qDebug("qtractorClapPlugin[%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("qtractorClapPlugin[%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 qtractorClapPlugin::updateDirtyCount (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->dirtyNotifySlot(); updateFormDirtyCount(); } // Idle editor (static). void qtractorClapPlugin::idleEditorAll (void) { QListIterator iter(g_clapPlugins); while (iter.hasNext()) iter.next()->idleEditor(); } // Request to reinitialize th plugin instance... void qtractorClapPlugin::request_restart (void) { if (m_pImpl) m_pImpl->plugin_request_restart(); } void qtractorClapPlugin::restart (void) { // Clear all automation curves, if any... qtractorSubject::resetQueue(); foreach (qtractorPlugin::Param *pParam, m_paramIds) clearParam(pParam); deinitialize(); initialize(); } // Common host-time keeper (static) void qtractorClapPlugin::updateTime ( qtractorAudioEngine *pAudioEngine ) { g_host.updateTransport(pAudioEngine); } // Host cleanup (static). void qtractorClapPlugin::clearAll (void) { g_clapPlugins.clear(); g_host.clear(); } //---------------------------------------------------------------------------- // qtractorClapPlugin::Param -- CLAP plugin parameter interface decl. // // Constructors. qtractorClapPlugin::Param::Param ( qtractorClapPlugin *pPlugin, unsigned long iIndex ) : qtractorPlugin::Param(pPlugin, iIndex), m_pImpl(nullptr) { const clap_param_info *param_info = nullptr; const clap_id id = pPlugin->impl()->getParameterId(iIndex); if (id != CLAP_INVALID_ID) param_info = pPlugin->impl()->getParameterInfo(id); if (param_info) { m_pImpl = new Impl(*param_info); setName(QString::fromUtf8(param_info->name)); setMinValue(float(param_info->min_value)); setMaxValue(float(param_info->max_value)); setDefaultValue(float(param_info->default_value)); } } // Destructor. qtractorClapPlugin::Param::~Param (void) { if (m_pImpl) delete m_pImpl; } // Port range hints predicate methods. bool qtractorClapPlugin::Param::isBoundedBelow (void) const { return true; } bool qtractorClapPlugin::Param::isBoundedAbove (void) const { return true; } bool qtractorClapPlugin::Param::isDefaultValue (void) const { return true; } bool qtractorClapPlugin::Param::isLogarithmic (void) const { return false; } bool qtractorClapPlugin::Param::isSampleRate (void) const { return false; } bool qtractorClapPlugin::Param::isInteger (void) const { bool ret = false; if (m_pImpl) { const clap_param_info& param_info = m_pImpl->param_info(); ret = (param_info.flags & CLAP_PARAM_IS_STEPPED); } return ret; } bool qtractorClapPlugin::Param::isToggled (void) const { bool ret = false; if (m_pImpl) { const clap_param_info& param_info = m_pImpl->param_info(); ret = ((param_info.flags & CLAP_PARAM_IS_STEPPED) && (param_info.min_value == 0.0) && (param_info.max_value == 1.0)); } return ret; } bool qtractorClapPlugin::Param::isDisplay (void) const { return true; } // Current display value. QString qtractorClapPlugin::Param::display (void) const { QString sText; qtractorClapPlugin *pPlugin = static_cast (plugin()); if (pPlugin && m_pImpl) { const clap_id id = m_pImpl->param_info().id; const double value = pPlugin->impl()->getParameter(id); sText = pPlugin->impl()->getParameterText(id, value); } // Default parameter display value... return sText; } #endif // CONFIG_CLAP // end of qtractorClapPlugin.cpp qtractor-1.5.9/src/PaxHeaders/qtractorTrackTime.cpp0000644000000000000000000000013215101070305017371 xustar0030 mtime=1761898693.091267667 30 atime=1761898693.091267667 30 ctime=1761898693.091267667 qtractor-1.5.9/src/qtractorTrackTime.cpp0000644000175000001440000006511215101070305017366 0ustar00rncbcusers// qtractorTrackTime.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 "qtractorTrackTime.h" #include "qtractorTrackView.h" #include "qtractorSession.h" #include "qtractorTracks.h" #include "qtractorOptions.h" #include "qtractorSessionCommand.h" #include "qtractorTimeScaleCommand.h" #include "qtractorMainForm.h" #include "qtractorTimeScaleForm.h" #include #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) #define horizontalAdvance width #endif //---------------------------------------------------------------------------- // qtractorTrackTime -- Track time scale widget. // Constructor. qtractorTrackTime::qtractorTrackTime ( qtractorTracks *pTracks, QWidget *pParent ) : qtractorScrollView(pParent) { m_pTracks = pTracks; m_dragState = DragNone; m_dragCursor = DragNone; m_pDragMarker = nullptr; qtractorScrollView::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); qtractorScrollView::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); qtractorScrollView::setFrameStyle(QFrame::Panel | QFrame::Raised); 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() - 2)); // QObject::connect(this, SIGNAL(contentsMoving(int,int)), // this, SLOT(updatePixmap(int,int))); // Trap for help/tool-tips events. qtractorScrollView::viewport()->installEventFilter(this); } // (Re)create the complete track view pixmap. void qtractorTrackTime::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()); 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()); // Draw the time scale... // const QFontMetrics& fm = painter.fontMetrics(); int x, x1, y1, y2 = h - 1; 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); x = x1 = pNode->pixelFromBeat(iBeat) - cx - 1; while (x < w) { const bool bBeatIsBar = pNode->beatIsBar(iBeat); if (bBeatIsBar || iPixelsPerBeat > 16) { y1 = (bBeatIsBar && x >= x1 ? 0 : fm.ascent()); painter.setPen(pal.mid().color()); painter.drawLine(x, y1, x, y2); painter.setPen(pal.light().color()); ++x; painter.drawLine(x, y1, x, y2); } if (bBeatIsBar) { y1 = fm.ascent(); if (x >= x1) { x1 = x + 2; const unsigned short iBar = pNode->barFromBeat(iBeat); const QString& sBeat = QString::number(iBar + 1); painter.setPen(pal.windowText().color()); painter.drawText(x1, y1, sBeat); x1 += fm.horizontalAdvance(sBeat) + 2; } x1 += 2; if (iBeat == pNode->beat) { iPixelsPerBeat = pNode->pixelsPerBeat(); const QString& sTempo = QString("%1 %2/%3") .arg(pNode->tempo) .arg(pNode->beatsPerBar) .arg(1 << pNode->beatDivisor); painter.setPen(Qt::darkGray); painter.drawText(x1, y1, sTempo); x1 += fm.horizontalAdvance(sTempo) + 2; } } pNode = cursor.seekBeat(++iBeat); x = pNode->pixelFromBeat(iBeat) - cx - 1; } // Draw location markers, if any... qtractorTimeScale::Marker *pMarker = pTimeScale->markers().seekPixel(cx); while (pMarker) { x = pTimeScale->pixelFromFrame(pMarker->frame) - cx + 4; if (x > w) break; if (qtractorTimeScale::isKeySignature( pMarker->accidentals, pMarker->mode)) { const QString& sKeySignature = qtractorTimeScale::keySignatureName( pMarker->accidentals, pMarker->mode); painter.setPen(Qt::darkGray); painter.drawText(x, y2, sKeySignature); x += fm.horizontalAdvance(sKeySignature) + 4; } if (!pMarker->text.isEmpty()) { painter.setPen(pMarker->color); painter.drawText(x, y2, pMarker->text); } pMarker = pMarker->next(); } // Draw loop boundaries, if applicable... if (pSession->isLooping()) { QPolygon polyg(3); // h -= 4; const int d = (h >> 2); QRect rect(0, h - d , w, h - d); QColor color = Qt::cyan; painter.setPen(color.darker()); painter.setBrush(color); x = pTimeScale->pixelFromFrame(pSession->loopStart()) - cx; if (x >= 0 && x < w) { polyg.putPoints(0, 3, x + d, h - d, x, h, x, h - d); painter.drawPolygon(polyg); } rect.setLeft(x); x = pTimeScale->pixelFromFrame(pSession->loopEnd()) - cx; if (x >= 0 && x < w) { polyg.putPoints(0, 3, x, h - d, x, h, x - d, h - d); painter.drawPolygon(polyg); } if (rect.x() < w && x >= 0) { rect.setRight(x); color.setAlpha(120); painter.fillRect(rect, color); } } // Draw punch in/out boundaries, if applicable... if (pSession->isPunching()) { QPolygon polyg(3); // h -= 4; const int d = (h >> 2); QRect rect(0, h - d, w, h - d); QColor color = Qt::magenta; painter.setPen(color.darker()); painter.setBrush(color); x = pTimeScale->pixelFromFrame(pSession->punchIn()) - cx; if (x >= 0 && x < w) { polyg.putPoints(0, 3, x + d, h - d, x, h, x, h - d); painter.drawPolygon(polyg); } rect.setLeft(x); x = pTimeScale->pixelFromFrame(pSession->punchOut()) - cx; if (x >= 0 && x < w) { polyg.putPoints(0, 3, x, h - d, x, h, x - d, h - d); painter.drawPolygon(polyg); } if (rect.x() < w && x >= 0) { rect.setRight(x); color.setAlpha(120); painter.fillRect(rect, color); } } } // Rectangular contents update. void qtractorTrackTime::updateContents ( const QRect& rect ) { updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(rect); } // Overall contents update. void qtractorTrackTime::updateContents (void) { updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(); } // Resize event handler. void qtractorTrackTime::resizeEvent ( QResizeEvent *pResizeEvent ) { qtractorScrollView::resizeEvent(pResizeEvent); updateContents(); } // Draw the time scale. void qtractorTrackTime::drawContents ( QPainter *pPainter, const QRect& rect ) { // Render the famous pixmap region... pPainter->drawPixmap(rect, m_pixmap, rect); // Headers a-head... const int cx = qtractorScrollView::contentsX(); const int h = qtractorScrollView::height() - 4; const int d = (h >> 2); qtractorTrackView *pTrackView = m_pTracks->trackView(); // Draw edit-head line... int x = pTrackView->editHeadX() - cx; if (x >= rect.left() - d && x <= rect.right() + d) { QPolygon polyg(3); polyg.putPoints(0, 3, x + d, h - d, x, h, x, h - d); pPainter->setPen(Qt::blue); pPainter->setBrush(Qt::blue); pPainter->drawPolygon(polyg); } // Draw edit-tail line... x = pTrackView->editTailX() - cx; if (x >= rect.left() - d && x <= rect.right() + d) { QPolygon polyg(3); polyg.putPoints(0, 3, x, h - d, x, h, x - d, h - d); pPainter->setPen(Qt::blue); pPainter->setBrush(Qt::blue); pPainter->drawPolygon(polyg); } // Draw special play-head header... x = pTrackView->playHeadX() - cx; if (x >= rect.left() - d && x <= rect.right() + d) { QPolygon polyg(3); polyg.putPoints(0, 3, x - d, h - d, x, h, x + d, h - d); pPainter->setPen(Qt::red); pPainter->setBrush(Qt::red); pPainter->drawPolygon(polyg); } } // To have timeline in h-sync with main track view. void qtractorTrackTime::contentsXMovingSlot ( int cx, int /*cy*/ ) { if (qtractorScrollView::contentsX() != cx) qtractorScrollView::setContentsPos(cx, qtractorScrollView::contentsY()); } // Check if some position header is to be dragged... bool qtractorTrackTime::dragHeadStart ( const QPoint& pos ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorTimeScale *pTimeScale = pSession->timeScale(); if (pTimeScale == nullptr) return false; // Try to catch mouse clicks over the // play/edit-head/tail cursors... const int h = qtractorScrollView::height(); // - 4; const int d = (h >> 1); QRect rect(0, h - d, d << 1, d); qtractorTrackView *pTrackView = m_pTracks->trackView(); // Check play-head header... rect.moveLeft(pTrackView->playHeadX() - d); if (rect.contains(pos)) { m_dragCursor = DragPlayHead; return true; } // Check loop-point headers... if (pSession->isLooping()) { // Check loop-start header... rect.moveLeft(pTimeScale->pixelFromFrame(pSession->loopStart()) - d); if (rect.contains(pos)) { m_dragCursor = DragLoopStart; return true; } // Check loop-end header... rect.moveLeft(pTimeScale->pixelFromFrame(pSession->loopEnd()) - d); if (rect.contains(pos)) { m_dragCursor = DragLoopEnd; return true; } } // Check punch-point headers... if (pSession->isPunching()) { // Check punch-in header... rect.moveLeft(pTimeScale->pixelFromFrame(pSession->punchIn()) - d); if (rect.contains(pos)) { m_dragCursor = DragPunchIn; return true; } // Check punch-out header... rect.moveLeft(pTimeScale->pixelFromFrame(pSession->punchOut()) - d); if (rect.contains(pos)) { m_dragCursor = DragPunchOut; return true; } } // Check edit-head header... rect.moveLeft(pTrackView->editHeadX() - d); if (rect.contains(pos)) { m_dragCursor = DragEditHead; return true; } // Check edit-tail header... rect.moveLeft(pTrackView->editTailX() - d); if (rect.contains(pos)) { m_dragCursor = DragEditTail; return true; } // Check location marker headers... qtractorTimeScale::Marker *pMarker = pTimeScale->markers().seekPixel(pos.x()); if (pMarker) { rect.moveLeft(pTimeScale->pixelFromFrame(pMarker->frame) - d); if (rect.contains(pos)) { m_dragCursor = DragMarker; m_pDragMarker = pMarker; return true; } } // Reset cursor if any persist around. if (m_dragCursor != DragNone) { qtractorScrollView::unsetCursor(); m_dragCursor = DragNone; } // Nothing. return false; } // Handle selection/dragging -- mouse button press. void qtractorTrackTime::mousePressEvent ( QMouseEvent *pMouseEvent ) { // Force null state. m_dragState = DragNone; // We'll need options somehow... qtractorOptions *pOptions = qtractorOptions::getInstance(); // We need a session and a location... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { qtractorTrackView *pTrackView = m_pTracks->trackView(); // Direct snap positioning... const QPoint& pos = viewportToContents(pMouseEvent->pos()); unsigned long iFrame = pTrackView->frameSnap( pSession->frameFromPixel(pos.x() > 0 ? pos.x() : 0)); // Which mouse state? const Qt::KeyboardModifiers& modifiers = pMouseEvent->modifiers(); bool bModifier = (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)); switch (pMouseEvent->button()) { case Qt::LeftButton: // Remember what and where we'll be dragging/selecting... m_dragState = DragStart; m_posDrag = pos; // Try to catch mouse clicks over the cursor heads... if (dragHeadStart(m_posDrag)) { qtractorScrollView::setCursor(QCursor(Qt::SizeHorCursor)); // m_dragState = m_dragCursor; }/* else if (!bModifier) { // Edit-head positioning... pTrackView->setEditHead(iFrame); // Logical contents changed, just for visual feedback... m_pTracks->selectionChangeNotify(); }*/ break; case Qt::MiddleButton: // Mid-button direct positioning... m_pTracks->selectNone(); if (pOptions && pOptions->bMidButtonModifier) bModifier = !bModifier; // Reverse mid-button role... if (bModifier) { // Play-head positioning commit... pTrackView->setPlayHead(iFrame); pTrackView->setPlayHeadAutoBackward(iFrame); pSession->setPlayHead(iFrame); } else { // Edit cursor (merge) positioning... pTrackView->setEditHead(iFrame); pTrackView->setEditTail(iFrame); } // Logical contents changed, just for visual feedback... m_pTracks->selectionChangeNotify(); break; case Qt::RightButton: if (pOptions && pOptions->bShiftKeyModifier) bModifier = !bModifier; // Reverse mid-button role... if (!bModifier) { // Right-button direct positioning... pTrackView->setEditTail(iFrame); // Logical contents changed, just for visual feedback... m_pTracks->selectionChangeNotify(); } // Fall thru... default: break; } } // qtractorScrollView::mousePressEvent(pMouseEvent); } // Handle selection/dragging -- mouse pointer move. void qtractorTrackTime::mouseMoveEvent ( QMouseEvent *pMouseEvent ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { qtractorTrackView *pTrackView = m_pTracks->trackView(); // Which mouse state? const Qt::KeyboardModifiers& modifiers = pMouseEvent->modifiers(); // Are we already moving/dragging something? const QPoint& pos = viewportToContents(pMouseEvent->pos()); const unsigned long iFrame = pTrackView->frameSnap( pSession->frameFromPixel(pos.x() > 0 ? pos.x() : 0)); const int y = pTrackView->contentsY(); qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); switch (m_dragState) { case DragNone: // Try to catch mouse over the cursor heads... if (dragHeadStart(pos)) qtractorScrollView::setCursor(QCursor(Qt::PointingHandCursor)); break; case DragSelect: // Rubber-band selection... m_rectDrag.setRight(pos.x()); pTrackView->ensureVisible(pos.x(), y, 24, 0); if (pTrackView->isCurveEdit()) { // Select all current track curve/automation // nodes that fall inside range... pTrackView->selectCurveRect(m_rectDrag, qtractorTrackView::SelectRange, qtractorTrackView::selectFlags(modifiers), qtractorTrackView::EditBoth); } else { // Here we're mainly supposed to select a few // bunch of clips that fall inside range... pTrackView->selectClipRect(m_rectDrag, qtractorTrackView::SelectRange, qtractorTrackView::selectFlags(modifiers), qtractorTrackView::EditBoth); } showToolTip(m_rectDrag.normalized()); break; case DragPlayHead: // Play-head positioning... pTrackView->ensureVisible(pos.x(), y, 24, 0); pTrackView->setPlayHead(iFrame); // Let the change get some immediate visual feedback... if (pMainForm) pMainForm->updateTransportTime(iFrame); showToolTip(iFrame); break; case DragLoopStart: case DragPunchIn: case DragEditHead: // Edit-head positioning... pTrackView->ensureVisible(pos.x(), y, 24, 0); pTrackView->setEditHead(iFrame); showToolTip(iFrame); break; case DragLoopEnd: case DragPunchOut: case DragEditTail: // Edit-tail positioning... pTrackView->ensureVisible(pos.x(), y, 24, 0); pTrackView->setEditTail(iFrame); showToolTip(iFrame); break; case DragMarker: // Marker positioning... pTrackView->ensureVisible(pos.x(), y, 24, 0); showToolTip(iFrame); break; case DragStart: // Rubber-band starting... if ((m_posDrag - pos).manhattanLength() > QApplication::startDragDistance()) { // We'll start dragging alright... const int h = qtractorScrollView::height(); m_rectDrag.setTop(0); m_rectDrag.setLeft(m_posDrag.x()); m_rectDrag.setRight(pos.x()); m_rectDrag.setBottom(h); if (!dragHeadStart(m_posDrag)) m_dragCursor = DragSelect; m_dragState = m_dragCursor; qtractorScrollView::setCursor(QCursor(Qt::SizeHorCursor)); } // Fall thru... default: break; } } // qtractorScrollView::mouseMoveEvent(pMouseEvent); } // Handle selection/dragging -- mouse button release. void qtractorTrackTime::mouseReleaseEvent ( QMouseEvent *pMouseEvent ) { // qtractorScrollView::mouseReleaseEvent(pMouseEvent); // We'll need options somehow... qtractorOptions *pOptions = qtractorOptions::getInstance(); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { qtractorTrackView *pTrackView = m_pTracks->trackView(); // Which mouse state? const Qt::KeyboardModifiers& modifiers = pMouseEvent->modifiers(); bool bModifier = (modifiers & (Qt::ShiftModifier | Qt::ControlModifier)); if (pOptions && pOptions->bShiftKeyModifier) bModifier = !bModifier; // Direct snap positioning... const QPoint& pos = viewportToContents(pMouseEvent->pos()); const unsigned long iFrame = pTrackView->frameSnap( pSession->frameFromPixel(pos.x() > 0 ? pos.x() : 0)); switch (m_dragState) { case DragSelect: // Do the final range selection... if (pTrackView->isCurveEdit()) { // Select all current track curve/automation // nodes that fall inside range... pTrackView->selectCurveRect(m_rectDrag, qtractorTrackView::SelectRange, qtractorTrackView::selectFlags(modifiers) | qtractorTrackView::SelectCommit, qtractorTrackView::EditBoth); } else { // Here we're mainly supposed to select a few // bunch of clips that fall inside range... pTrackView->selectClipRect(m_rectDrag, qtractorTrackView::SelectRange, qtractorTrackView::selectFlags(modifiers) | qtractorTrackView::SelectCommit, qtractorTrackView::EditBoth); } // For immediate visual feedback... m_pTracks->selectionChangeNotify(); break; case DragPlayHead: // Play-head positioning commit... pTrackView->setPlayHead(iFrame); pTrackView->setPlayHeadAutoBackward(iFrame); pSession->setPlayHead(iFrame); // Not quite a selection, rather just // for immediate visual feedback... m_pTracks->selectionChangeNotify(); break; case DragLoopStart: // New loop-start boundary... if (pSession->editHead() < pSession->loopEnd()) { // Yep, new loop-start point... pSession->execute( new qtractorSessionLoopCommand(pSession, pSession->editHead(), pSession->loopEnd())); } break; case DragPunchIn: // New punch-in boundary... if (pSession->editHead() < pSession->punchOut()) { // Yep, new punch-in point... pSession->execute( new qtractorSessionPunchCommand(pSession, pSession->editHead(), pSession->punchOut())); // For visual feedback... m_pTracks->contentsChangeNotify(); } break; case DragEditHead: // Not quite a contents change, but for visual feedback... m_pTracks->selectionChangeNotify(); break; case DragLoopEnd: // New loop-end boundary... if (pSession->loopStart() < pSession->editTail()) { // Yep, new loop-end point... pSession->execute( new qtractorSessionLoopCommand(pSession, pSession->loopStart(), pSession->editTail())); } break; case DragPunchOut: // New punch-out boundary... if (pSession->punchIn() < pSession->editTail()) { // Yep, new punch-out point... pSession->execute( new qtractorSessionPunchCommand(pSession, pSession->punchIn(), pSession->editTail())); // For visual feedback... m_pTracks->contentsChangeNotify(); } break; case DragEditTail: // Not quite a contents change, but for visual feedback... m_pTracks->selectionChangeNotify(); break; case DragMarker: // Marker positioning commit... if (m_pDragMarker) { // Yep, new marker location... pSession->execute( new qtractorTimeScaleMoveMarkerCommand( pSession->timeScale(), m_pDragMarker, iFrame)); } break; case DragStart: // Left-button indirect positioning... if (bModifier) { // Playhead positioning... pTrackView->setPlayHead(iFrame); pTrackView->setPlayHeadAutoBackward(iFrame); pSession->setPlayHead(iFrame); } else { // Deferred left-button edit-head positioning... pTrackView->setEditHead(iFrame); } // Not quite a selection, rather just // for immediate visual feedback... m_pTracks->selectionChangeNotify(); // Fall thru... case DragNone: default: break; } } // Clean up. resetDragState(); } // Tempo-map dialog accessor. void qtractorTrackTime::mouseDoubleClickEvent ( QMouseEvent *pMouseEvent ) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Direct snap positioning... const QPoint& pos = viewportToContents(pMouseEvent->pos()); qtractorTrackView *pTrackView = m_pTracks->trackView(); const unsigned long iFrame = pTrackView->frameSnap( pSession->frameFromPixel(pos.x() > 0 ? pos.x() : 0)); // Show tempo map dialog. qtractorTimeScaleForm form(pMainForm); form.setFrame(iFrame); form.exec(); } // Keyboard event handler. void qtractorTrackTime::keyPressEvent ( QKeyEvent *pKeyEvent ) { #ifdef CONFIG_DEBUG_0 fprintf(stderr, "qtractorTrackTime::keyPressEvent(key=%d)\n", pKeyEvent->key()); #endif switch (pKeyEvent->key()) { case Qt::Key_Escape: { // Restore uncommitted play-head position?... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession && m_dragState == DragPlayHead) m_pTracks->trackView()->setPlayHead(pSession->playHead()); resetDragState(); break; } default: qtractorScrollView::keyPressEvent(pKeyEvent); break; } } // Handle zoom with mouse wheel. void qtractorTrackTime::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); } // Reset drag/select state. void qtractorTrackTime::resetDragState (void) { // Cancel any dragging/cursor out there... if (m_dragState == DragSelect) qtractorScrollView::updateContents(); if (m_dragCursor != DragNone) qtractorScrollView::unsetCursor(); // Force null state. m_dragState = DragNone; m_dragCursor = DragNone; m_pDragMarker = nullptr; // HACK: give focus to track-view... m_pTracks->trackView()->setFocus(); } // Context menu event handler (dummy). void qtractorTrackTime::contextMenuEvent ( QContextMenuEvent */*pContextMenuEvent*/ ) { } // Trap for help/tool-tip events. bool qtractorTrackTime::eventFilter ( QObject *pObject, QEvent *pEvent ) { QWidget *pViewport = qtractorScrollView::viewport(); if (static_cast (pObject) == pViewport) { if (pEvent->type() == QEvent::ToolTip && m_dragCursor != DragNone && (m_pTracks->trackView())->isToolTips()) { QHelpEvent *pHelpEvent = static_cast (pEvent); if (pHelpEvent) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { unsigned long iFrame = 0; switch (m_dragCursor) { case DragMarker: if (m_pDragMarker) iFrame = m_pDragMarker->frame; break; case DragPlayHead: iFrame = pSession->playHead(); break; case DragEditHead: iFrame = pSession->editHead(); break; case DragEditTail: iFrame = pSession->editTail(); break; case DragLoopStart: iFrame = pSession->loopStart(); break; case DragLoopEnd: iFrame = pSession->loopEnd(); break; case DragPunchIn: iFrame = pSession->punchIn(); break; case DragPunchOut: iFrame = pSession->punchOut(); break; default: break; } showToolTip(iFrame); } } } else if (pEvent->type() == QEvent::Leave && m_dragState != DragNone) { qtractorScrollView::unsetCursor(); return true; } } // Not handled here. return qtractorScrollView::eventFilter(pObject, pEvent); } // Show dragging tooltip... void qtractorTrackTime::showToolTip ( unsigned long iFrame ) const { if (!m_pTracks->trackView()->isToolTips()) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = pSession->timeScale(); if (pTimeScale == nullptr) return; QString sToolTip; switch (m_dragCursor) { case DragMarker: if (m_pDragMarker) sToolTip += m_pDragMarker->text; break; case DragPlayHead: sToolTip += tr("Play-head"); break; case DragEditHead: sToolTip += tr("Edit-head"); break; case DragEditTail: sToolTip += tr("Edit-tail"); break; case DragLoopStart: sToolTip += tr("Loop-start"); break; case DragLoopEnd: sToolTip += tr("Loop-end"); break; case DragPunchIn: sToolTip += tr("Punch-in"); break; case DragPunchOut: sToolTip += tr("Punch-out"); break; default: break; } if (!sToolTip.isEmpty()) sToolTip += '\n'; sToolTip += pTimeScale->textFromFrame(iFrame); QToolTip::showText(QCursor::pos(), sToolTip, qtractorScrollView::viewport()); } void qtractorTrackTime::showToolTip ( const QRect& rect ) const { qtractorTrackView *pTrackView = m_pTracks->trackView(); if (!pTrackView->isToolTips()) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = pSession->timeScale(); if (pTimeScale == nullptr) return; const unsigned long iFrameStart = pTrackView->frameSnap( pTimeScale->frameFromPixel(qMax(0, rect.left()))); const unsigned long iFrameEnd = pTrackView->frameSnap( pTimeScale->frameFromPixel(qMax(0, rect.right()))); 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()); } // end of qtractorTrackTime.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMessageBox.cpp0000644000000000000000000000012715101070305017547 xustar0029 mtime=1761898693.07626762 29 atime=1761898693.07626762 29 ctime=1761898693.07626762 qtractor-1.5.9/src/qtractorMessageBox.cpp0000644000175000001440000001074715101070305017544 0ustar00rncbcusers// qtractorMessageBox.cpp // /**************************************************************************** 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. *****************************************************************************/ #include "qtractorMessageBox.h" #include #include #include #include #include //---------------------------------------------------------------------------- // qtractorMessageBox -- UI wrapper form. qtractorMessageBox::qtractorMessageBox ( QWidget *pParent, Qt::WindowFlags wflags ) : QDialog(pParent, wflags), m_icon(QMessageBox::NoIcon) { QDialog::setWindowFlags(wflags | Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowCloseButtonHint); QGridLayout *pGridLayout = new QGridLayout(); pGridLayout->setContentsMargins(8, 8, 8, 8); pGridLayout->setSpacing(2); m_pIconLabel = new QLabel(); m_pIconLabel->setMargin(8); m_pTextLabel = new QLabel(); m_pTextLabel->setMargin(8); m_pTextLabel->setOpenExternalLinks(true); m_pCustomButtonLayout = new QVBoxLayout(); m_pCustomButtonLayout->setContentsMargins(8, 8, 8, 8); m_pCustomButtonLayout->setSpacing(2); m_pDialogButtonBox = new QDialogButtonBox(); pGridLayout->addWidget(m_pIconLabel, 0, 0); pGridLayout->addWidget(m_pTextLabel, 0, 1); pGridLayout->addLayout(m_pCustomButtonLayout, 1, 1); pGridLayout->addWidget(m_pDialogButtonBox, 2, 0, 1, 2); pGridLayout->setColumnStretch(1, 2); pGridLayout->setRowStretch(1, 2); QDialog::setLayout(pGridLayout); // Dialog commands... QObject::connect(m_pDialogButtonBox, SIGNAL(clicked(QAbstractButton *)), SLOT(standardButtonClicked(QAbstractButton *))); } // Accessors. void qtractorMessageBox::setText ( const QString& sText ) { m_pTextLabel->setText(sText); } QString qtractorMessageBox::text (void) const { return m_pTextLabel->text(); } void qtractorMessageBox::setIcon ( QMessageBox::Icon icon ) { if (m_icon != icon) { m_icon = icon; QMessageBox mbox; mbox.setIcon(icon); m_pIconLabel->setPixmap(mbox.iconPixmap()); } } QMessageBox::Icon qtractorMessageBox::icon (void) const { return m_icon; } void qtractorMessageBox::setIconPixmap ( const QPixmap& pixmap ) { m_pIconLabel->setPixmap(pixmap); } QPixmap qtractorMessageBox::iconPixmap (void) const { #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) return m_pIconLabel->pixmap(Qt::ReturnByValue); #else return *m_pIconLabel->pixmap(); #endif } void qtractorMessageBox::setStandardButtons ( QMessageBox::StandardButtons buttons ) { m_pDialogButtonBox->setStandardButtons( QDialogButtonBox::StandardButton(int(buttons))); } QMessageBox::StandardButtons qtractorMessageBox::standardButtons (void) const { return QMessageBox::StandardButtons( int(m_pDialogButtonBox->standardButtons())); } void qtractorMessageBox::addCustomButton ( QAbstractButton *pButton ) { m_pCustomButtonLayout->addWidget(pButton); } void qtractorMessageBox::addCustomSpacer (void) { m_pCustomButtonLayout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); } void qtractorMessageBox::addButton( QAbstractButton *pButton, QMessageBox::ButtonRole role ) { m_pDialogButtonBox->addButton(pButton, QDialogButtonBox::ButtonRole(int(role))); } // Dialog slots. void qtractorMessageBox::standardButtonClicked ( QAbstractButton *pButton ) { const QDialogButtonBox::ButtonRole role = m_pDialogButtonBox->buttonRole(pButton); switch (role) { case QDialogButtonBox::AcceptRole: // Just go with dialog acceptance. QDialog::accept(); break; case QDialogButtonBox::RejectRole: // Just go with dialog rejection. QDialog::reject(); break; default: break; } QDialog::setResult(int(m_pDialogButtonBox->standardButton(pButton))); } // end of qtractorMessageBox.cpp qtractor-1.5.9/src/PaxHeaders/qtractorPluginFactory.h0000644000000000000000000000013215101070305017741 xustar0030 mtime=1761898693.086267651 30 atime=1761898693.086267651 30 ctime=1761898693.086267651 qtractor-1.5.9/src/qtractorPluginFactory.h0000644000175000001440000001433615101070305017740 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.9/src/PaxHeaders/qtractorVst2Plugin.cpp0000644000000000000000000000012715101070305017527 xustar0029 mtime=1761898693.09226767 29 atime=1761898693.09226767 29 ctime=1761898693.09226767 qtractor-1.5.9/src/qtractorVst2Plugin.cpp0000644000175000001440000021172015101070305017516 0ustar00rncbcusers// qtractorVst2Plugin.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" #ifdef CONFIG_VST2 #include "qtractorVst2Plugin.h" #include "qtractorSession.h" #include "qtractorAudioEngine.h" #include "qtractorMidiManager.h" #include "qtractorOptions.h" #include "qtractorMainForm.h" #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(4, 5, 0) namespace Qt { const WindowFlags WindowCloseButtonHint = WindowFlags(0x08000000); } #endif #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_VST2_X11 #include #include #include typedef void (*XEventProc)(XEvent *); #endif // CONFIG_VST2_X11 #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 qtractorVst2Plugin_HostCallback (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt); // Specific extended flags that saves us // from calling canDo() in audio callbacks. enum qtractorVst2PluginFlagsEx { 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 effGetProgramNameIndexed = 29; const int effFlagsProgramChunks = 32; #endif //--------------------------------------------------------------------- // qtractorVst2Plugin::EditorWidget - Helpers for own editor widget. #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_VST2_X11 static int g_iXError = 0; static int tempXErrorHandler ( Display *, XErrorEvent * ) { ++g_iXError; return 0; } static XEventProc getXEventProc ( Display *pDisplay, Window w ) { int iSize = 0; unsigned long iBytes = 0, iCount = 0; unsigned char *pData = nullptr; XEventProc eventProc = nullptr; Atom aType, aName = XInternAtom(pDisplay, "_XEventProc", false); #if defined(__x86_64__) const long length = 2; #else const long length = 1; #endif g_iXError = 0; XErrorHandler oldErrorHandler = XSetErrorHandler(tempXErrorHandler); XGetWindowProperty(pDisplay, w, aName, 0, length, false, AnyPropertyType, &aType, &iSize, &iCount, &iBytes, &pData); if (g_iXError == 0 && iCount > 0 && pData) { if (iCount == 1) eventProc = (XEventProc) (pData); XFree(pData); } XSetErrorHandler(oldErrorHandler); return eventProc; } static Window getXChildWindow ( Display *pDisplay, Window w ) { Window wRoot = 0, wParent = 0, *pwChildren = nullptr, wChild = 0; unsigned int iChildren = 0; XQueryTree(pDisplay, w, &wRoot, &wParent, &pwChildren, &iChildren); if (iChildren > 0) { wChild = pwChildren[0]; XFree(pwChildren); } return wChild; } #endif // CONFIG_VST2_X11 #endif //---------------------------------------------------------------------------- // qtractorVst2Plugin::EditorWidget -- Plugin editor wrapper widget. // // Dynamic singleton list of VST2 editors. static QList g_vst2Editors; class qtractorVst2Plugin::EditorWidget : public QWidget { public: // Constructor. EditorWidget(QWidget *pParent = nullptr, Qt::WindowFlags wflags = Qt::WindowFlags()) : QWidget(pParent, wflags), #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_VST2_X11 m_pDisplay(QX11Info::display()), m_wVst2Editor(0), m_pVst2EventProc(nullptr), m_bButtonPress(false), #endif // CONFIG_VST2_X11 #else m_pWindow(nullptr), #endif m_pVst2Plugin(nullptr) { QWidget::setAttribute(Qt::WA_QuitOnClose, false); } // Destructor. ~EditorWidget() { close(); } // Specialized editor methods. void open(qtractorVst2Plugin *pVst2Plugin) { m_pVst2Plugin = pVst2Plugin; // Start the proper (child) editor... long value = 0; void *ptr = nullptr; #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_VST2_X11 value = (long) m_pDisplay; ptr = (void *) QWidget::winId(); #endif // CONFIG_VST2_X11 #else m_pWindow = new QWindow(); m_pWindow->create(); QWidget *pContainer = QWidget::createWindowContainer(m_pWindow, this); QVBoxLayout *pVBoxLayout = new QVBoxLayout(); pVBoxLayout->setContentsMargins(0, 0, 0, 0); pVBoxLayout->setSpacing(0); pVBoxLayout->addWidget(pContainer); QWidget::setLayout(pVBoxLayout); ptr = (void *) m_pWindow->winId(); #endif // Launch the custom GUI editor... m_pVst2Plugin->vst2_dispatch(0, effEditOpen, 0, value, ptr, 0.0f); // Make it the right size struct ERect { short top; short left; short bottom; short right; } *pRect; if (m_pVst2Plugin->vst2_dispatch(0, effEditGetRect, 0, 0, &pRect, 0.0f)) { const int w = pRect->right - pRect->left; const int h = pRect->bottom - pRect->top; if (w > 0 && h > 0) QWidget::setFixedSize(w, h); } #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_VST2_X11 m_wVst2Editor = getXChildWindow(m_pDisplay, (Window) QWidget::winId()); if (m_wVst2Editor) m_pVst2EventProc = getXEventProc(m_pDisplay, m_wVst2Editor); #endif // CONFIG_VST2_X11 #endif g_vst2Editors.append(this); } // Close the editor widget. void close() { QWidget::close(); if (m_pVst2Plugin) { m_pVst2Plugin->vst2_dispatch(0, effEditClose, 0, 0, nullptr, 0.0f); m_pVst2Plugin = nullptr; } const int iIndex = g_vst2Editors.indexOf(this); if (iIndex >= 0) g_vst2Editors.removeAt(iIndex); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) if (m_pWindow) { m_pWindow->destroy(); delete m_pWindow; } #endif } #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_VST2_X11 // Local X11 event filter. bool x11EventFilter(XEvent *pEvent) { if (m_pVst2EventProc && pEvent->xany.window == m_wVst2Editor) { // Avoid mouse tracking events... switch (pEvent->xany.type) { case ButtonPress: m_bButtonPress = true; break; case ButtonRelease: m_bButtonPress = false; break; case MotionNotify: if (!m_bButtonPress) return false; // Fall thru... default: break; } // Process as intended... (*m_pVst2EventProc)(pEvent); return true; } else { return false; } } #endif // CONFIG_VST2_X11 #endif qtractorVst2Plugin *plugin() const { return m_pVst2Plugin; } protected: // Visibility event handlers. void showEvent(QShowEvent *pShowEvent) { QWidget::showEvent(pShowEvent); if (m_pVst2Plugin) m_pVst2Plugin->toggleFormEditor(true); } void closeEvent(QCloseEvent *pCloseEvent) { if (m_pVst2Plugin) m_pVst2Plugin->toggleFormEditor(false); QWidget::closeEvent(pCloseEvent); if (m_pVst2Plugin) m_pVst2Plugin->closeEditor(); } #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_VST2_X11 void moveEvent(QMoveEvent *pMoveEvent) { QWidget::moveEvent(pMoveEvent); if (m_wVst2Editor) { XMoveWindow(m_pDisplay, m_wVst2Editor, 0, 0); // QWidget::update(); } } #endif // CONFIG_VST2_X11 #endif private: // Instance variables... #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_VST2_X11 Display *m_pDisplay; Window m_wVst2Editor; XEventProc m_pVst2EventProc; bool m_bButtonPress; #endif // CONFIG_VST2_X11 #else QWindow *m_pWindow; #endif qtractorVst2Plugin *m_pVst2Plugin; }; //---------------------------------------------------------------------------- // qtractorVst2PluginType::Effect -- VST2 AEffect instance. // class qtractorVst2PluginType::Effect { public: // Constructor. Effect(AEffect *pVst2Effect = nullptr) : m_pVst2Effect(pVst2Effect) {} // Specific methods. bool open(qtractorPluginFile *pFile, unsigned long iIndex); void close(); // Specific accessors. AEffect *vst2_effect() const { return m_pVst2Effect; } // VST2 host dispatcher. int vst2_dispatch( long opcode, long index, long value, void *ptr, float opt) const; private: // VST2 descriptor itself. AEffect *m_pVst2Effect; }; // Current working VST2 Shell identifier. static int g_iVst2ShellCurrentId = 0; // Specific methods. bool qtractorVst2PluginType::Effect::open ( qtractorPluginFile *pFile, unsigned long iIndex ) { // Do we have an effect descriptor already? if (m_pVst2Effect == nullptr) m_pVst2Effect = qtractorVst2PluginType::vst2_effect(pFile); if (m_pVst2Effect == nullptr) return false; // Check whether it's a VST2 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]) { m_pVst2Effect = nullptr; return false; } // Make it known... g_iVst2ShellCurrentId = id; // Re-allocate the thing all over again... m_pVst2Effect = qtractorVst2PluginType::vst2_effect(pFile); // Not needed anymore, hopefully... g_iVst2ShellCurrentId = 0; // Don't go further if failed... if (m_pVst2Effect == nullptr) return false; #ifdef CONFIG_DEBUG//_0 qDebug("AEffect[%p]::vst2_shell(%lu) id=0x%x name=\"%s\"", m_pVst2Effect, i, id, buf); #endif } else // Not a VST2 Shell plugin... if (iIndex > 0) { m_pVst2Effect = nullptr; return false; } #ifdef CONFIG_DEBUG_0 qDebug("AEffect[%p]::open(%p, %lu)", m_pVst2Effect, pFile, iIndex); #endif vst2_dispatch(effOpen, 0, 0, nullptr, 0.0f); // vst2_dispatch(effIdentify, 0, 0, nullptr, 0); // vst2_dispatch(effMainsChanged, 0, 0, nullptr, 0.0f); return true; } void qtractorVst2PluginType::Effect::close (void) { if (m_pVst2Effect == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("AEffect[%p]::close()", m_pVst2Effect); #endif // vst2_dispatch(effMainsChanged, 0, 0, nullptr, 0.0f); vst2_dispatch(effClose, 0, 0, nullptr, 0.0f); m_pVst2Effect = nullptr; } // VST2 host dispatcher. int qtractorVst2PluginType::Effect::vst2_dispatch ( long opcode, long index, long value, void *ptr, float opt ) const { if (m_pVst2Effect == nullptr) return 0; #ifdef CONFIG_DEBUG_0 qDebug("AEffect[%p]::vst2_dispatch(%ld, %ld, %ld, %p, %g)", m_pVst2Effect, opcode, index, value, ptr, opt); #endif return m_pVst2Effect->dispatcher(m_pVst2Effect, opcode, index, value, ptr, opt); } //---------------------------------------------------------------------------- // qtractorVst2PluginType -- VST2 plugin type instance. // // Derived methods. bool qtractorVst2PluginType::open (void) { // Do we have an effect descriptor already? if (m_pEffect == nullptr) m_pEffect = new Effect(); if (!m_pEffect->open(file(), index())) return false; AEffect *pVst2Effect = m_pEffect->vst2_effect(); if (pVst2Effect == nullptr) return false; #ifdef CONFIG_DEBUG qDebug("qtractorVst2PluginType[%p]::open() filename=\"%s\" index=%lu", this, filename().toUtf8().constData(), index()); #endif // Retrieve plugin type names. char szName[256]; ::memset(szName, 0, sizeof(szName)); vst2_dispatch(effGetEffectName, 0, 0, (void *) szName, 0.0f); if (szName[0]) m_sName = QString::fromLocal8Bit(szName); else m_sName = QFileInfo(filename()).baseName(); // Sanitize plugin label. m_sLabel = m_sName.simplified().replace(QRegularExpression("[\\s|\\.|\\-]+"), "_"); // Retrieve plugin unique identifier. #ifdef CONFIG_VESTIGE_OLD m_iUniqueID = qHash(m_sLabel); #else m_iUniqueID = pVst2Effect->uniqueID; #endif // 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; // Compute and cache port counts... m_iControlIns = pVst2Effect->numParams; m_iControlOuts = 0; m_iAudioIns = pVst2Effect->numInputs; m_iAudioOuts = pVst2Effect->numOutputs; m_iMidiIns = ((m_iFlagsEx & effFlagsExCanReceiveVstMidiEvents) || (pVst2Effect->flags & effFlagsIsSynth) ? 1 : 0); m_iMidiOuts = ((m_iFlagsEx & effFlagsExCanSendVstMidiEvents) ? 1 : 0); // Cache flags. m_bRealtime = true; m_bConfigure = (pVst2Effect->flags & effFlagsProgramChunks); m_bEditor = (pVst2Effect->flags & effFlagsHasEditor); // HACK: Some native VST2 plugins with a GUI editor // need to skip explicit shared library unloading, // on close, in order to avoid mysterious crashes // later on session and/or application exit. if (m_bEditor) file()->setAutoUnload(false); return true; } void qtractorVst2PluginType::close (void) { if (m_pEffect == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorVst2PluginType[%p]::close()", this); #endif m_pEffect->close(); delete m_pEffect; m_pEffect = nullptr; } // Factory method (static) qtractorVst2PluginType *qtractorVst2PluginType::createType ( qtractorPluginFile *pFile, unsigned long iIndex ) { // Sanity check... if (pFile == nullptr) return nullptr; // Retrieve effect instance if any... AEffect *pVst2Effect = vst2_effect(pFile); if (pVst2Effect == nullptr) return nullptr; // Yep, most probably its a valid plugin effect... return new qtractorVst2PluginType(pFile, iIndex, new Effect(pVst2Effect)); } // Effect instance method (static) AEffect *qtractorVst2PluginType::vst2_effect ( qtractorPluginFile *pFile ) { VST_GetPluginInstance pfnGetPluginInstance = (VST_GetPluginInstance) pFile->resolve("VSTPluginMain"); if (pfnGetPluginInstance == nullptr) pfnGetPluginInstance = (VST_GetPluginInstance) pFile->resolve("main"); if (pfnGetPluginInstance == nullptr) return nullptr; // Does the VST2 plugin instantiate OK? AEffect *pVst2Effect = (*pfnGetPluginInstance)(qtractorVst2Plugin_HostCallback); if (pVst2Effect == nullptr) return nullptr; if (pVst2Effect->magic != kEffectMagic) return nullptr; return pVst2Effect; } // VST2 host dispatcher. int qtractorVst2PluginType::vst2_dispatch ( long opcode, long index, long value, void *ptr, float opt ) const { if (m_pEffect == nullptr) return 0; return m_pEffect->vst2_dispatch(opcode, index, value, ptr, opt); } // VST2 flag inquirer. bool qtractorVst2PluginType::vst2_canDo ( const char *pszCanDo ) const { if (m_pEffect == nullptr) return false; return (m_pEffect->vst2_dispatch(effCanDo, 0, 0, (void *) pszCanDo, 0.0f) > 0); } // Instance cached-deferred accessors. const QString& qtractorVst2PluginType::aboutText (void) { if (m_sAboutText.isEmpty()) { char szTemp[256]; ::memset(szTemp, 0, sizeof(szTemp)); vst2_dispatch(effGetProductString, 0, 0, (void *) szTemp, 0.0f); if (szTemp[0]) { if (!m_sAboutText.isEmpty()) m_sAboutText += '\n'; m_sAboutText += QObject::tr("Product: "); m_sAboutText += QString::fromLocal8Bit(szTemp); } ::memset(szTemp, 0, sizeof(szTemp)); vst2_dispatch(effGetVendorString, 0, 0, (void *) szTemp, 0.0f); if (szTemp[0]) { if (!m_sAboutText.isEmpty()) m_sAboutText += '\n'; m_sAboutText += QObject::tr("Vendor: "); m_sAboutText += QString::fromLocal8Bit(szTemp); } const int iVersion = vst2_dispatch(effGetVendorVersion, 0, 0, nullptr, 0.0f); if (iVersion) { if (!m_sAboutText.isEmpty()) m_sAboutText += '\n'; m_sAboutText += QObject::tr("Version: "); m_sAboutText += QString::number(iVersion); } } return m_sAboutText; } //---------------------------------------------------------------------------- // qtractorVst2Plugin -- VST2 plugin instance. // // Dynamic singleton list of VST2 plugins. static QHash g_vst2Plugins; // Constructors. qtractorVst2Plugin::qtractorVst2Plugin ( qtractorPluginList *pList, qtractorVst2PluginType *pVst2Type ) : qtractorPlugin(pList, pVst2Type), m_ppEffects(nullptr), m_ppIBuffer(nullptr), m_ppOBuffer(nullptr), m_pfIDummy(nullptr), m_pfODummy(nullptr), m_pEditorWidget(nullptr), m_bEditorClosed(false) { #ifdef CONFIG_DEBUG qDebug("qtractorVst2Plugin[%p] filename=\"%s\" index=%lu typeHint=%d", this, type()->filename().toUtf8().constData(), type()->index(), int(type()->typeHint())); #endif // Allocate I/O audio buffer pointers. const unsigned short iAudioIns = pVst2Type->audioIns(); const unsigned short iAudioOuts = pVst2Type->audioOuts(); if (iAudioIns > 0) m_ppIBuffer = new float * [iAudioIns]; if (iAudioOuts > 0) m_ppOBuffer = new float * [iAudioOuts]; // Create all existing parameters... const unsigned short iParamCount = pVst2Type->controlIns(); for (unsigned short i = 0; i < iParamCount; ++i) addParam(new Param(this, i)); // Instantiate each instance properly... setChannels(channels()); } // Destructor. qtractorVst2Plugin::~qtractorVst2Plugin (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; } // Channel/instance number accessors. void qtractorVst2Plugin::setChannels ( unsigned short iChannels ) { // Check our type... qtractorVst2PluginType *pVst2Type = static_cast (type()); if (pVst2Type == nullptr) return; // Estimate the (new) number of instances... const unsigned short iOldInstances = instances(); unsigned short iInstances = 0; if (iChannels > 0) { iInstances = pVst2Type->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); // Close old instances, all the way... if (m_ppEffects) { qtractorVst2PluginType::Effect *pEffect; for (unsigned short i = 1; i < iOldInstances; ++i) { pEffect = m_ppEffects[i]; g_vst2Plugins.remove(pEffect->vst2_effect()); pEffect->close(); delete pEffect; } pEffect = m_ppEffects[0]; g_vst2Plugins.remove(pEffect->vst2_effect()); delete [] m_ppEffects; m_ppEffects = nullptr; } // Bail out, if none are about to be created... if (iInstances < 1) { setChannelsActivated(iChannels, bActivated); return; } #ifdef CONFIG_DEBUG qDebug("qtractorVst2Plugin[%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 iSampleRate = pAudioEngine->sampleRate(); const unsigned int iBufferSizeEx = pAudioEngine->bufferSizeEx(); // Allocate new instances... m_ppEffects = new qtractorVst2PluginType::Effect * [iInstances]; m_ppEffects[0] = pVst2Type->effect(); g_vst2Plugins.insert(m_ppEffects[0]->vst2_effect(), this); for (unsigned short i = 1; i < iInstances; ++i) { m_ppEffects[i] = new qtractorVst2PluginType::Effect(); m_ppEffects[i]->open(pVst2Type->file(), pVst2Type->index()); g_vst2Plugins.insert(m_ppEffects[i]->vst2_effect(), this); } // 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)); } // Setup all those instances alright... for (unsigned short i = 0; i < iInstances; ++i) { // And now all other things as well... qtractorVst2PluginType::Effect *pEffect = m_ppEffects[i]; // pEffect->vst2_dispatch(effOpen, 0, 0, nullptr, 0.0f); pEffect->vst2_dispatch(effSetSampleRate, 0, 0, nullptr, float(iSampleRate)); pEffect->vst2_dispatch(effSetBlockSize, 0, iBufferSizeEx, nullptr, 0.0f); // pEffect->vst2_dispatch(effSetProgram, 0, 0, nullptr, 0.0f); #if 0 // !VST_FORCE_DEPRECATED unsigned short j; for (j = 0; j < iAudioIns; ++j) pEffect->vst2_dispatch(effConnectInput, j, 1, nullptr, 0.0f); for (j = 0; j < iAudioOuts; ++j) pEffect->vst2_dispatch(effConnectOutput, j, 1, nullptr, 0.0f); #endif } // (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 activation. void qtractorVst2Plugin::activate (void) { for (unsigned short i = 0; i < instances(); ++i) { vst2_dispatch(i, effMainsChanged, 0, 1, nullptr, 0.0f); #ifndef CONFIG_VESTIGE vst2_dispatch(i, effStartProcess, 0, 0, nullptr, 0.0f); #endif } } // Do the actual deactivation. void qtractorVst2Plugin::deactivate (void) { for (unsigned short i = 0; i < instances(); ++i) { #ifndef CONFIG_VESTIGE vst2_dispatch(i, effStopProcess, 0, 0, nullptr, 0.0f); #endif vst2_dispatch(i, effMainsChanged, 0, 0, nullptr, 0.0f); } } // The main plugin processing procedure. void qtractorVst2Plugin::process ( float **ppIBuffer, float **ppOBuffer, unsigned int nframes ) { if (m_ppEffects == nullptr) return; // To process MIDI events, if any... qtractorMidiManager *pMidiManager = nullptr; const unsigned short iMidiIns = midiIns(); const unsigned short iMidiOuts = midiOuts(); if (iMidiIns > 0) pMidiManager = list()->midiManager(); // 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) { AEffect *pVst2Effect = vst2_effect(i); // For each instance audio input port... for (j = 0; j < iAudioIns; ++j) { if (iIChannel < iChannels) m_ppIBuffer[j] = ppIBuffer[iIChannel++]; else m_ppIBuffer[j] = m_pfIDummy; // dummy input! } // For each instance audio output port... for (j = 0; j < iAudioOuts; ++j) { if (iOChannel < iChannels) m_ppOBuffer[j] = ppOBuffer[iOChannel++]; else m_ppOBuffer[j] = m_pfODummy; // dummy output! } // Make it run MIDI, if applicable... if (pMidiManager) { pVst2Effect->dispatcher(pVst2Effect, effProcessEvents, 0, 0, pMidiManager->vst2_events_in(), 0.0f); } // Make it run audio... if (pVst2Effect->flags & effFlagsCanReplacing) { pVst2Effect->processReplacing( pVst2Effect, m_ppIBuffer, m_ppOBuffer, nframes); } #if 0 // !VST_FORCE_DEPRECATED else { pVst2Effect->process( pVst2Effect, m_ppIBuffer, m_ppOBuffer, nframes); } #endif // Wrap dangling output channels?... for (j = iOChannel; j < iChannels; ++j) ::memset(ppOBuffer[j], 0, nframes * sizeof(float)); } if (pMidiManager && iMidiOuts > 0) pMidiManager->vst2_events_swap(); } // Parameter update method. void qtractorVst2Plugin::updateParam ( qtractorPlugin::Param *pParam, float fValue, bool /*bUpdate*/ ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorVst2Plugin[%p]::updateParam(%lu, %g, %d)", this, pParam->index(), fValue, int(bUpdate)); #endif // Maybe we're not pretty instantiated yet... for (unsigned short i = 0; i < instances(); ++i) { AEffect *pVst2Effect = vst2_effect(i); if (pVst2Effect) pVst2Effect->setParameter(pVst2Effect, pParam->index(), fValue); } } // Bank/program selector. void qtractorVst2Plugin::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("qtractorVst2Plugin[%p]::selectProgram(%d, %d)", this, iBank, iProg); #endif int iIndex = 0; if (iBank >= 0 && iProg >= 0) iIndex = (iBank << 7) + iProg; for (unsigned short i = 0; i < instances(); ++i) vst2_dispatch(i, effSetProgram, 0, iIndex, nullptr, 0.0f); // Reset parameters default value... AEffect *pVst2Effect = vst2_effect(0); if (pVst2Effect) { 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(); float *pfValue = pParam->subject()->data(); *pfValue = pVst2Effect->getParameter(pVst2Effect, pParam->index()); pParam->setDefaultValue(*pfValue); } } } // Provisional program/patch accessor. bool qtractorVst2Plugin::getProgram ( int iIndex, Program& program ) const { // Iteration sanity check... AEffect *pVst2Effect = vst2_effect(0); if (pVst2Effect == nullptr) return false; if (iIndex < 0 || iIndex >= pVst2Effect->numPrograms) return false; char szName[256]; ::memset(szName, 0, sizeof(szName)); if (vst2_dispatch(0, effGetProgramNameIndexed, iIndex, 0, (void *) szName, 0.0f) == 0) { #if 0//QTRACTOR_VST2_GET_PROGRAM_OLD const int iOldIndex = vst2_dispatch(0, effGetProgram, 0, 0, nullptr, 0.0f); vst2_dispatch(0, effSetProgram, 0, iIndex, nullptr, 0.0f); vst2_dispatch(0, effGetProgramName, 0, 0, (void *) szName, 0.0f); vst2_dispatch(0, effSetProgram, 0, iOldIndex, nullptr, 0.0f); #else ::snprintf(szName, sizeof(szName) - 1, "Program %d", iIndex + 1); #endif } // Map this to that... program.bank = 0; program.prog = iIndex; program.name = QString::fromLocal8Bit(szName); return true; } // Configuration (CLOB) stuff. void qtractorVst2Plugin::configure ( const QString& sKey, const QString& sValue ) { if (sKey == "chunk") { // Load the BLOB (base64 encoded)... const QByteArray data = qUncompress(QByteArray::fromBase64(sValue.toLatin1())); const char *pData = data.constData(); const int iData = data.size(); #ifdef CONFIG_DEBUG qDebug("qtractorVst2Plugin[%p]::configure() chunk.size=%d", this, iData); #endif for (unsigned short i = 0; i < instances(); ++i) vst2_dispatch(i, effSetChunk, 0, iData, (void *) pData, 0.0f); } } // Plugin configuration/state snapshot. void qtractorVst2Plugin::freezeConfigs (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorVst2Plugin[%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); // Also, update current editor position... if (m_pEditorWidget && m_pEditorWidget->isVisible()) setEditorPos(m_pEditorWidget->pos()); if (!type()->isConfigure()) return; clearConfigs(); AEffect *pVst2Effect = vst2_effect(0); if (pVst2Effect == nullptr) return; // Save plugin state into chunk configuration... char *pData = nullptr; const int iData = vst2_dispatch(0, effGetChunk, 0, 0, (void *) &pData, 0.0f); if (iData < 1 || pData == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorVst2Plugin[%p]::freezeConfigs() chunk.size=%d", this, iData); #endif // Set special plugin configuration item (base64 encoded)... QByteArray cdata = qCompress(QByteArray(pData, iData)).toBase64(); for (int i = cdata.size() - (cdata.size() % 72); i >= 0; i -= 72) cdata.insert(i, "\n "); // Indentation. setConfig("chunk", cdata.constData()); } void qtractorVst2Plugin::releaseConfigs (void) { #ifdef CONFIG_DEBUG qDebug("qtractorVst2Plugin[%p]::releaseConfigs()", this); #endif qtractorPlugin::clearConfigs(); } // Plugin current latency (in frames); unsigned long qtractorVst2Plugin::latency (void) const { AEffect *pVst2Effect = vst2_effect(0); if (pVst2Effect == nullptr) return 0; #ifdef CONFIG_VESTIGE const VstInt32 *pInitialDelay = (VstInt32 *) &(pVst2Effect->empty3[0]); return *pInitialDelay; #else return pVst2Effect->initialDelay; #endif } // Plugin preset i/o (configuration from/to (fxp/fxb files). bool qtractorVst2Plugin::loadPresetFile ( const QString& sFilename ) { bool bResult = false; const QString& sExt = QFileInfo(sFilename).suffix().toLower(); if (sExt == "fxp" || sExt == "fxb") { bResult = qtractorVst2Preset(this).load(sFilename); } else { const int iCurrentProgram = vst2_dispatch(0, effGetProgram, 0, 0, nullptr, 0.0f); bResult = qtractorPlugin::loadPresetFile(sFilename); for (unsigned short i = 0; i < instances(); ++i) vst2_dispatch(i, effSetProgram, 0, iCurrentProgram, nullptr, 0.0f); } return bResult; } bool qtractorVst2Plugin::savePresetFile ( const QString& sFilename ) { const QString& sExt = QFileInfo(sFilename).suffix().toLower(); if (sExt == "fxp" || sExt == "fxb") return qtractorVst2Preset(this).save(sFilename); else return qtractorPlugin::savePresetFile(sFilename); } // Make up some others dirty... void qtractorVst2Plugin::updateDirtyCount (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->dirtyNotifySlot(); updateFormDirtyCount(); } // Specific accessors. AEffect *qtractorVst2Plugin::vst2_effect ( unsigned short iInstance ) const { if (m_ppEffects == nullptr) return nullptr; return m_ppEffects[iInstance]->vst2_effect(); } // VST2 host dispatcher. int qtractorVst2Plugin::vst2_dispatch( unsigned short iInstance, long opcode, long index, long value, void *ptr, float opt) const { if (m_ppEffects == nullptr) return 0; return m_ppEffects[iInstance]->vst2_dispatch(opcode, index, value, ptr, opt); } // Open editor. void qtractorVst2Plugin::openEditor ( QWidget *pParent ) { // 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("qtractorVst2Plugin[%p]::openEditor(%p)", this, pParent); #endif // Make sure it's not closed... m_bEditorClosed = false; // What style do we create tool childs? Qt::WindowFlags wflags = Qt::Window; #if 0//QTRACTOR_VST2_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 // Do it... m_pEditorWidget = new EditorWidget(pParent, wflags); m_pEditorWidget->open(this); // Final stabilization... updateEditorTitle(); moveWidgetPos(m_pEditorWidget, editorPos()); setEditorVisible(true); idleEditor(); } // Close editor. void qtractorVst2Plugin::closeEditor (void) { if (m_bEditorClosed) return; m_bEditorClosed = true; if (m_pEditorWidget == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorVst2Plugin[%p]::closeEditor()", this); #endif setEditorVisible(false); // Close the parent widget, if any. delete m_pEditorWidget; m_pEditorWidget = nullptr; } // Idle editor. void qtractorVst2Plugin::idleEditor (void) { if (m_pEditorWidget == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorVst2Plugin[%p]::idleEditor()", this); #endif vst2_dispatch(0, effEditIdle, 0, 0, nullptr, 0.0f); m_pEditorWidget->update(); } // GUI editor visibility state. void qtractorVst2Plugin::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 qtractorVst2Plugin::isEditorVisible (void) const { return (m_pEditorWidget ? m_pEditorWidget->isVisible() : false); } // Update editor widget caption. void qtractorVst2Plugin::setEditorTitle ( const QString& sTitle ) { qtractorPlugin::setEditorTitle(sTitle); if (m_pEditorWidget) { m_pEditorWidget->setWindowTitle(sTitle); m_pEditorWidget->setWindowIcon(QIcon::fromTheme("qtractorPlugin")); } } // Idle editor (static). void qtractorVst2Plugin::idleEditorAll (void) { QListIterator iter(g_vst2Editors); while (iter.hasNext()) { qtractorVst2Plugin *pVst2Plugin = iter.next()->plugin(); if (pVst2Plugin) pVst2Plugin->idleEditor(); } } // Our own editor widget accessor. QWidget *qtractorVst2Plugin::editorWidget (void) const { return static_cast (m_pEditorWidget); } // Our own editor widget size accessor. void qtractorVst2Plugin::resizeEditor ( int w, int h ) { if (m_pEditorWidget && w > 0 && h > 0) m_pEditorWidget->setFixedSize(w, h); } // Global VST2 plugin lookup. qtractorVst2Plugin *qtractorVst2Plugin::findPlugin ( AEffect *pVst2Effect ) { return g_vst2Plugins.value(pVst2Effect, nullptr); } #ifdef CONFIG_VST2_X11 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) // Global X11 event filter. bool qtractorVst2Plugin::x11EventFilter ( void *pvEvent ) { XEvent *pEvent = (XEvent *) pvEvent; QListIterator iter(g_vst2Editors); while (iter.hasNext()) { if (iter.next()->x11EventFilter(pEvent)) return true; } return false; } #endif #endif // CONFIG_VST2_X11 // All parameters update method. void qtractorVst2Plugin::updateParamValues ( bool bUpdate ) { int nupdate = 0; // Make sure all cached parameter values are in sync // with plugin parameter values; update cache otherwise. AEffect *pVst2Effect = vst2_effect(0); if (pVst2Effect) { 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 = pVst2Effect->getParameter(pVst2Effect, pParam->index()); if (pParam->value() != fValue) { pParam->setValue(fValue, bUpdate); ++nupdate; } } } if (nupdate > 0) updateFormDirtyCount(); } //---------------------------------------------------------------------------- // qtractorVst2Plugin::Param -- VST2 plugin control input port instance. // // Constructors. qtractorVst2Plugin::Param::Param ( qtractorVst2Plugin *pVst2Plugin, unsigned long iIndex ) : qtractorPlugin::Param(pVst2Plugin, iIndex) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorVst2Plugin::Param[%p] pVst2Plugin=%p iIndex=%lu", this, pVst2Plugin, iIndex); #endif qtractorVst2PluginType *pVst2Type = static_cast (pVst2Plugin->type()); char szName[64]; szName[0] = (char) 0; if (pVst2Type) pVst2Type->vst2_dispatch(effGetParamName, iIndex, 0, (void *) szName, 0.0f); if (!szName[0]) ::snprintf(szName, sizeof(szName), "Param #%lu", iIndex + 1); // Default dummy name. setName(szName); setMinValue(0.0f); setMaxValue(1.0f); ::memset(&m_props, 0, sizeof(m_props)); if (pVst2Type && pVst2Type->vst2_dispatch( effGetParameterProperties, iIndex, 0, (void *) &m_props, 0.0f)) { #ifdef CONFIG_DEBUG_0 qDebug(" VstParamProperties(%lu) {", iIndex); qDebug(" .label = \"%s\"", m_props.label); qDebug(" .shortLabel = \"%s\"", m_props.shortLabel); qDebug(" .category = %d", m_props.category); qDebug(" .categoryLabel = \"%s\"", m_props.categoryLabel); qDebug(" .minInteger = %d", int(m_props.minInteger)); qDebug(" .maxInteger = %d", int(m_props.maxInteger)); qDebug(" .stepInteger = %d", int(m_props.stepInteger)); qDebug(" .stepFloat = %g", m_props.stepFloat); #ifndef CONFIG_VESTIGE_OLD qDebug(" .smallStepFloat = %g", m_props.smallStepFloat); qDebug(" .largeStepFloat = %g", m_props.largeStepFloat); qDebug(" .largeStepInteger = %d", int(m_props.largeStepInteger)); qDebug(" >IsSwitch = %d", (m_props.flags & kVstParameterIsSwitch ? 1 : 0)); qDebug(" >UsesIntegerMinMax = %d", (m_props.flags & kVstParameterUsesIntegerMinMax ? 1 : 0)); qDebug(" >UsesFloatStep = %d", (m_props.flags & kVstParameterUsesFloatStep ? 1 : 0)); qDebug(" >UsesIntStep = %d", (m_props.flags & kVstParameterUsesIntStep ? 1 : 0)); qDebug(" >SupportsDisplayIndex = %d", (m_props.flags & kVstParameterSupportsDisplayIndex ? 1 : 0)); qDebug(" >SupportsDisplayCategory = %d", (m_props.flags & kVstParameterSupportsDisplayCategory ? 1 : 0)); qDebug(" >CanRamp = %d", (m_props.flags & kVstParameterCanRamp ? 1 : 0)); qDebug(" .displayIndex = %d", m_props.displayIndex); qDebug(" .numParametersInCategory = %d", m_props.numParametersInCategory); #endif qDebug("}"); #endif if (isBoundedBelow()) setMinValue(float(m_props.minInteger)); if (isBoundedAbove()) setMaxValue(float(m_props.maxInteger)); } // ATTN: Set default value as initial one... if (pVst2Type && pVst2Type->effect()) { AEffect *pVst2Effect = (pVst2Type->effect())->vst2_effect(); if (pVst2Effect) qtractorPlugin::Param::setValue( pVst2Effect->getParameter(pVst2Effect, iIndex), true); } setDefaultValue(qtractorPlugin::Param::value()); // Initialize port value... // reset(); } // Port range hints predicate methods. bool qtractorVst2Plugin::Param::isBoundedBelow (void) const { #ifdef CONFIG_VESTIGE_OLD return false; #else return (m_props.flags & kVstParameterUsesIntegerMinMax); #endif } bool qtractorVst2Plugin::Param::isBoundedAbove (void) const { #ifdef CONFIG_VESTIGE_OLD return false; #else return (m_props.flags & kVstParameterUsesIntegerMinMax); #endif } bool qtractorVst2Plugin::Param::isDefaultValue (void) const { return true; } bool qtractorVst2Plugin::Param::isLogarithmic (void) const { return false; } bool qtractorVst2Plugin::Param::isSampleRate (void) const { return false; } bool qtractorVst2Plugin::Param::isInteger (void) const { #ifdef CONFIG_VESTIGE_OLD return false; #else return (m_props.flags & kVstParameterUsesIntStep); #endif } bool qtractorVst2Plugin::Param::isToggled (void) const { #ifdef CONFIG_VESTIGE_OLD return false; #else return (m_props.flags & kVstParameterIsSwitch); #endif } bool qtractorVst2Plugin::Param::isDisplay (void) const { #ifdef CONFIG_VESTIGE_OLD return false; #else return true; #endif } // Current display value. QString qtractorVst2Plugin::Param::display (void) const { qtractorVst2PluginType *pVst2Type = nullptr; if (plugin()) pVst2Type = static_cast (plugin()->type()); if (pVst2Type) { QString sDisplay; char szText[64]; // Grab parameter display value... szText[0] = '\0'; pVst2Type->vst2_dispatch(effGetParamDisplay, index(), 0, (void *) szText, 0.0f); if (szText[0]) { // Got sDisplay += QString(szText).trimmed(); // Grab parameter name... szText[0] = '\0'; pVst2Type->vst2_dispatch(effGetParamLabel, index(), 0, (void *) szText, 0.0f); if (szText[0]) sDisplay += ' ' + QString(szText).trimmed(); // Got it. return sDisplay; } } // Default parameter display value... return qtractorPlugin::Param::display(); } //---------------------------------------------------------------------- // VST2 host callback file selection helpers. #ifndef CONFIG_VESTIGE static VstIntPtr qtractorVst2Plugin_openFileSelector ( AEffect *effect, VstFileSelect *pvfs ) { qtractorVst2Plugin *pVst2Plugin = qtractorVst2Plugin::findPlugin(effect); if (pVst2Plugin == nullptr) return 0; #ifdef CONFIG_DEBUG qDebug("qtractorVst2Plugin_openFileSelector(%p, %p) command=%d reserved=%d", effect, pvfs, int(pvfs->command), int(pvfs->reserved)); #endif pvfs->reserved = 0; pvfs->nbReturnPath = 0; if (pvfs->command == kVstFileLoad || pvfs->command == kVstFileSave) { QString sFilename; QStringList filters; for (int i = 0; i < pvfs->nbFileTypes; ++i) { filters.append(QObject::tr("%1 (*.%2)") .arg(pvfs->fileTypes[i].name).arg(pvfs->fileTypes[i].dosType)); } filters.append(QObject::tr("All files (*.*)")); QWidget *pParentWidget = pVst2Plugin->editorWidget(); if (pParentWidget) pParentWidget = pParentWidget->window(); const QString& sTitle = QString("%1 - %2") .arg(pvfs->title).arg(pVst2Plugin->title()); const QString& sDirectory = pvfs->initialPath; const QString& sFilter = filters.join(";;"); QFileDialog::Options options; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bDontUseNativeDialogs) options |= QFileDialog::DontUseNativeDialog; if (pvfs->command == kVstFileLoad) { sFilename = QFileDialog::getOpenFileName( pParentWidget, sTitle, sDirectory, sFilter, nullptr, options); } else { sFilename = QFileDialog::getSaveFileName( pParentWidget, sTitle, sDirectory, sFilter, nullptr, options); } if (!sFilename.isEmpty()) { if (pvfs->returnPath == nullptr) { pvfs->returnPath = new char [sFilename.length() + 1]; pvfs->reserved = 1; } ::strcpy(pvfs->returnPath, sFilename.toUtf8().constData()); pvfs->nbReturnPath = 1; } } else if (pvfs->command == kVstDirectorySelect) { QWidget *pParentWidget = pVst2Plugin->editorWidget(); if (pParentWidget) pParentWidget = pParentWidget->window(); const QString& sTitle = QString("%1 - %2") .arg(pvfs->title).arg(pVst2Plugin->title()); const QFileDialog::Options options = QFileDialog::ShowDirsOnly | QFileDialog::DontUseNativeDialog; const QString& sDirectory = QFileDialog::getExistingDirectory( pParentWidget, sTitle, pvfs->initialPath, options); if (!sDirectory.isEmpty()) { if (pvfs->returnPath == nullptr) { pvfs->returnPath = new char [sDirectory.length() + 1]; pvfs->reserved = 1; } ::strcpy(pvfs->returnPath, sDirectory.toUtf8().constData()); pvfs->nbReturnPath = 1; } } return pvfs->nbReturnPath; } static VstIntPtr qtractorVst2Plugin_closeFileSelector ( AEffect *effect, VstFileSelect *pvfs ) { qtractorVst2Plugin *pVst2Plugin = qtractorVst2Plugin::findPlugin(effect); if (pVst2Plugin == nullptr) return 0; #ifdef CONFIG_DEBUG qDebug("qtractorVst2Plugin_closeFileSelector(%p, %p) command=%d reserved=%d", effect, pvfs, int(pvfs->command), int(pvfs->reserved)); #endif if (pvfs->reserved == 1 && pvfs->returnPath) { delete [] pvfs->returnPath; pvfs->returnPath = nullptr; pvfs->reserved = 0; } return 1; } #endif // !CONFIG_VESTIGE //---------------------------------------------------------------------- // The magnificient host callback, which every VSTi plugin will call. #ifdef CONFIG_DEBUG #define VST2_HC_FMT "qtractorVst2Plugin_HostCallback(%p, %s(%d), %d, %d, %p, %g)" #define VST2_HC_DEBUG(s) qDebug(VST2_HC_FMT, \ effect, (s), int(opcode), int(index), int(value), ptr, opt) #define VST2_HC_DEBUGX(s) qDebug(VST2_HC_FMT " [%s]", \ effect, (s), int(opcode), int(index), int(value), ptr, opt, (char *) ptr) #else #define VST2_HC_DEBUG(s) #define VST2_HC_DEBUGX(s) #endif static VstIntPtr VSTCALLBACK qtractorVst2Plugin_HostCallback ( AEffect *effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void *ptr, float opt ) { VstIntPtr ret = 0; qtractorVst2Plugin *pVst2Plugin = nullptr; static VstTimeInfo s_vst2TimeInfo; qtractorSession *pSession = qtractorSession::getInstance(); switch (opcode) { // VST 1.0 opcodes... case audioMasterVersion: VST2_HC_DEBUG("audioMasterVersion"); ret = 2; // vst2.x break; case audioMasterAutomate: VST2_HC_DEBUG("audioMasterAutomate"); // effect->setParameter(effect, index, opt); pVst2Plugin = qtractorVst2Plugin::findPlugin(effect); if (pVst2Plugin) { pVst2Plugin->updateParamValue(index, opt, false); // QApplication::processEvents(); } break; case audioMasterCurrentId: VST2_HC_DEBUG("audioMasterCurrentId"); ret = (VstIntPtr) g_iVst2ShellCurrentId; break; case audioMasterIdle: VST2_HC_DEBUG("audioMasterIdle"); pVst2Plugin = qtractorVst2Plugin::findPlugin(effect); if (pVst2Plugin) { pVst2Plugin->updateParamValues(false); pVst2Plugin->idleEditor(); // QApplication::processEvents(); } break; case audioMasterGetTime: // VST2_HC_DEBUG("audioMasterGetTime"); ::memset(&s_vst2TimeInfo, 0, sizeof(s_vst2TimeInfo)); if (pSession) { qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine) { const qtractorAudioEngine::TimeInfo& timeInfo = pAudioEngine->timeInfo(); s_vst2TimeInfo.samplePos = double(timeInfo.frame); s_vst2TimeInfo.sampleRate = double(timeInfo.sampleRate); s_vst2TimeInfo.flags = 0; if (timeInfo.playing) s_vst2TimeInfo.flags |= (kVstTransportChanged | kVstTransportPlaying); s_vst2TimeInfo.flags |= kVstPpqPosValid; s_vst2TimeInfo.ppqPos = double(timeInfo.beats); s_vst2TimeInfo.flags |= kVstBarsValid; s_vst2TimeInfo.barStartPos = double(timeInfo.barBeats); s_vst2TimeInfo.flags |= kVstTempoValid; s_vst2TimeInfo.tempo = double(timeInfo.tempo); s_vst2TimeInfo.flags |= kVstTimeSigValid; s_vst2TimeInfo.timeSigNumerator = timeInfo.beatsPerBar; s_vst2TimeInfo.timeSigDenominator = timeInfo.beatType; } } ret = (VstIntPtr) &s_vst2TimeInfo; break; case audioMasterProcessEvents: VST2_HC_DEBUG("audioMasterProcessEvents"); pVst2Plugin = qtractorVst2Plugin::findPlugin(effect); if (pVst2Plugin) { qtractorMidiManager *pMidiManager = nullptr; qtractorPluginList *pPluginList = pVst2Plugin->list(); if (pPluginList) pMidiManager = pPluginList->midiManager(); if (pMidiManager) { pMidiManager->vst2_events_copy((VstEvents *) ptr); ret = 1; // supported and processed. } } break; case audioMasterIOChanged: VST2_HC_DEBUG("audioMasterIOChanged"); break; case audioMasterSizeWindow: VST2_HC_DEBUG("audioMasterSizeWindow"); pVst2Plugin = qtractorVst2Plugin::findPlugin(effect); if (pVst2Plugin) { pVst2Plugin->resizeEditor(int(index), int(value)); ret = 1; // supported. } break; case audioMasterGetSampleRate: VST2_HC_DEBUG("audioMasterGetSampleRate"); pVst2Plugin = qtractorVst2Plugin::findPlugin(effect); if (pVst2Plugin) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine) ret = (VstIntPtr) pAudioEngine->sampleRate(); } } break; case audioMasterGetBlockSize: VST2_HC_DEBUG("audioMasterGetBlockSize"); pVst2Plugin = qtractorVst2Plugin::findPlugin(effect); if (pVst2Plugin) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine) ret = (VstIntPtr) pAudioEngine->blockSize(); } } break; case audioMasterGetInputLatency: VST2_HC_DEBUG("audioMasterGetInputLatency"); break; case audioMasterGetOutputLatency: VST2_HC_DEBUG("audioMasterGetOutputLatency"); break; case audioMasterGetCurrentProcessLevel: // VST2_HC_DEBUG("audioMasterGetCurrentProcessLevel"); break; case audioMasterGetAutomationState: VST2_HC_DEBUG("audioMasterGetAutomationState"); ret = 1; // off. break; #if !defined(VST_2_3_EXTENSIONS) case audioMasterGetSpeakerArrangement: VST2_HC_DEBUG("audioMasterGetSpeakerArrangement"); break; #endif case audioMasterGetVendorString: VST2_HC_DEBUG("audioMasterGetVendorString"); ::strcpy((char *) ptr, QTRACTOR_DOMAIN); ret = 1; // ok. break; case audioMasterGetProductString: VST2_HC_DEBUG("audioMasterGetProductString"); ::strcpy((char *) ptr, QTRACTOR_TITLE); ret = 1; // ok. break; case audioMasterGetVendorVersion: VST2_HC_DEBUG("audioMasterGetVendorVersion"); break; case audioMasterVendorSpecific: VST2_HC_DEBUG("audioMasterVendorSpecific"); break; case audioMasterCanDo: VST2_HC_DEBUGX("audioMasterCanDo"); if (::strcmp("receiveVstMidiEvent", (char *) ptr) == 0 || ::strcmp("sendVstMidiEvent", (char *) ptr) == 0 || ::strcmp("sendVstTimeInfo", (char *) ptr) == 0 || ::strcmp("midiProgramNames", (char *) ptr) == 0 || ::strcmp("sizeWindow", (char *) ptr) == 0) { ret = 1; // can do. } #ifndef CONFIG_VESTIGE else if (::strcmp("openFileSelector", (char *) ptr) == 0 || ::strcmp("closeFileSelector", (char *) ptr) == 0) { ret = 1; // can do. } #endif break; case audioMasterGetLanguage: VST2_HC_DEBUG("audioMasterGetLanguage"); ret = (VstIntPtr) kVstLangEnglish; break; #if 0 // !VST_FORCE_DEPRECATED case audioMasterPinConnected: VST2_HC_DEBUG("audioMasterPinConnected"); break; // VST 2.0 opcodes... case audioMasterWantMidi: VST2_HC_DEBUG("audioMasterWantMidi"); break; case audioMasterSetTime: VST2_HC_DEBUG("audioMasterSetTime"); break; case audioMasterTempoAt: VST2_HC_DEBUG("audioMasterTempoAt"); if (pSession) ret = (VstIntPtr) (pSession->tempo() * 10000.0f); break; case audioMasterGetNumAutomatableParameters: VST2_HC_DEBUG("audioMasterGetNumAutomatableParameters"); break; case audioMasterGetParameterQuantization: VST2_HC_DEBUG("audioMasterGetParameterQuantization"); ret = 1; // full single float precision break; case audioMasterNeedIdle: VST2_HC_DEBUG("audioMasterNeedIdle"); break; case audioMasterGetPreviousPlug: VST2_HC_DEBUG("audioMasterGetPreviousPlug"); break; case audioMasterGetNextPlug: VST2_HC_DEBUG("audioMasterGetNextPlug"); break; case audioMasterWillReplaceOrAccumulate: VST2_HC_DEBUG("audioMasterWillReplaceOrAccumulate"); ret = 1; break; case audioMasterSetOutputSampleRate: VST2_HC_DEBUG("audioMasterSetOutputSampleRate"); break; case audioMasterSetIcon: VST2_HC_DEBUG("audioMasterSetIcon"); break; case audioMasterOpenWindow: VST2_HC_DEBUG("audioMasterOpenWindow"); break; case audioMasterCloseWindow: VST2_HC_DEBUG("audioMasterCloseWindow"); break; #endif case audioMasterGetDirectory: VST2_HC_DEBUG("audioMasterGetDirectory"); break; case audioMasterUpdateDisplay: VST2_HC_DEBUG("audioMasterUpdateDisplay"); pVst2Plugin = qtractorVst2Plugin::findPlugin(effect); if (pVst2Plugin) { pVst2Plugin->updateParamValues(false); // QApplication::processEvents(); ret = 1; // supported. } break; case audioMasterBeginEdit: VST2_HC_DEBUG("audioMasterBeginEdit"); break; case audioMasterEndEdit: VST2_HC_DEBUG("audioMasterEndEdit"); break; #ifndef CONFIG_VESTIGE case audioMasterOpenFileSelector: VST2_HC_DEBUG("audioMasterOpenFileSelector"); ret = qtractorVst2Plugin_openFileSelector(effect, (VstFileSelect *) ptr); break; case audioMasterCloseFileSelector: VST2_HC_DEBUG("audioMasterCloseFileSelector"); ret = qtractorVst2Plugin_closeFileSelector(effect, (VstFileSelect *) ptr); break; #endif default: VST2_HC_DEBUG("audioMasterUnknown"); break; } return ret; } //----------------------------------------------------------------------------- // Structures for VST2 presets (fxb/fxp files) // Constants copied/stringified from "vstfxstore.h" // #define cMagic "CcnK" // Root chunk identifier for Programs (fxp) and Banks (fxb). #define fMagic "FxCk" // Regular Program (fxp) identifier. #define bankMagic "FxBk" // Regular Bank (fxb) identifier. #define chunkPresetMagic "FPCh" // Program (fxp) identifier for opaque chunk data. #define chunkBankMagic "FBCh" // Bank (fxb) identifier for opaque chunk data. #define cMagic_u 0x4b6e6343 // Big-endian version of 'CcnK' // Common bank/program header structure (fxb/fxp files) // struct qtractorVst2Preset::BaseHeader { VstInt32 chunkMagic; // 'CcnK' VstInt32 byteSize; // size of this chunk, excl. magic + byteSize VstInt32 fxMagic; // 'FxCk' (regular) or 'FPCh' (opaque chunk) VstInt32 version; // format version (currently 1) VstInt32 fxID; // fx unique ID VstInt32 fxVersion; // fx version }; // Program sub-header structure (fxb/fxp files) // struct qtractorVst2Preset::ProgHeader { VstInt32 numParams; // number of parameters char prgName[28]; // program name (null-terminated ASCII string) }; // Bank sub-header structure (fxb files) // struct qtractorVst2Preset::BankHeader { VstInt32 numPrograms; // number of programs VstInt32 currentProgram; // version 2: current program number char future[124]; // reserved, should be zero }; // Common auxiliary chunk structure (chunked fxb/fxp files) // struct qtractorVst2Preset::Chunk { VstInt32 size; char *data; }; // Endianess swappers. // static inline void fx_endian_swap ( VstInt32& v ) { static bool s_endian_swap = (*(VstInt32 *) cMagic == cMagic_u); if (s_endian_swap) { const unsigned int u = v; v = (u << 24) | ((u << 8) & 0xff0000) | ((u >> 8) & 0xff00) | (u >> 24); } } static inline void fx_endian_swap ( float& v ) { fx_endian_swap(*(VstInt32 *) &v); } static inline bool fx_is_magic ( VstInt32 v, const char *c ) { #if 0 VstInt32 u = *(VstInt32 *) c; fx_endian_swap(u); return (v == u); #else return (v == *(VstInt32 *) c); #endif } //---------------------------------------------------------------------- // class qtractorVst2Preset -- VST2 preset file interface // // Constructor. qtractorVst2Preset::qtractorVst2Preset ( qtractorVst2Plugin *pVst2Plugin ) : m_pVst2Plugin(pVst2Plugin) { } // Loader methods. // bool qtractorVst2Preset::load_bank_progs ( QFile& file ) { BankHeader bank_header; const int nread_bank = sizeof(bank_header); if (file.read((char *) &bank_header, nread_bank) < nread_bank) return false; fx_endian_swap(bank_header.numPrograms); fx_endian_swap(bank_header.currentProgram); const int iNumPrograms = int(bank_header.numPrograms); // const int iCurrentProgram = int(bank_header.currentProgram); const int iCurrentProgram = m_pVst2Plugin->vst2_dispatch(0, effGetProgram, 0, 0, nullptr, 0.0f); for (int iProgram = 0; iProgram < iNumPrograms; ++iProgram) { BaseHeader base_header; const int nread = sizeof(base_header); if (file.read((char *) &base_header, nread) < nread) return false; // fx_endian_swap(base_header.chunkMagic); fx_endian_swap(base_header.byteSize); // fxendian_swap(base_header.fxMagic); fx_endian_swap(base_header.version); fx_endian_swap(base_header.fxID); fx_endian_swap(base_header.fxVersion); if (!fx_is_magic(base_header.chunkMagic, cMagic)) return false; for (unsigned short i = 0; i < m_pVst2Plugin->instances(); ++i) m_pVst2Plugin->vst2_dispatch(i, effSetProgram, 0, iProgram, nullptr, 0.0f); if (fx_is_magic(base_header.fxMagic, fMagic)) { if (!load_prog_params(file)) return false; } else if (fx_is_magic(base_header.fxMagic, chunkPresetMagic)) { if (!load_prog_chunk(file)) return false; } else return false; } for (unsigned short i = 0; i < m_pVst2Plugin->instances(); ++i) m_pVst2Plugin->vst2_dispatch(i, effSetProgram, 0, iCurrentProgram, nullptr, 0.0f); return true; } bool qtractorVst2Preset::load_prog_params ( QFile& file ) { ProgHeader prog_header; const int nread = sizeof(prog_header); if (file.read((char *) &prog_header, nread) < nread) return false; fx_endian_swap(prog_header.numParams); for (unsigned short i = 0; i < m_pVst2Plugin->instances(); ++i) m_pVst2Plugin->vst2_dispatch(i, effSetProgramName, 0, 0, (void *) prog_header.prgName, 0.0f); const int iNumParams = int(prog_header.numParams); if (iNumParams < 1) return false; const int nread_params = iNumParams * sizeof(float); float *params = new float [iNumParams]; if (file.read((char *) params, nread_params) < nread_params) return false; for (int iParam = 0; iParam < iNumParams; ++iParam) { fx_endian_swap(params[iParam]); for (unsigned short i = 0; i < m_pVst2Plugin->instances(); ++i) { AEffect *pVst2Effect = m_pVst2Plugin->vst2_effect(i); if (pVst2Effect) pVst2Effect->setParameter(pVst2Effect, iParam, params[iParam]); } } delete [] params; return true; } bool qtractorVst2Preset::load_bank_chunk ( QFile& file ) { BankHeader bank_header; const int nread = sizeof(bank_header); if (file.read((char *) &bank_header, nread) < nread) return false; const int iCurrentProgram = m_pVst2Plugin->vst2_dispatch(0, effGetProgram, 0, 0, nullptr, 0.0f); const bool bResult = load_chunk(file, 0); for (unsigned short i = 0; i < m_pVst2Plugin->instances(); ++i) m_pVst2Plugin->vst2_dispatch(i, effSetProgram, 0, iCurrentProgram, nullptr, 0.0f); return bResult; } bool qtractorVst2Preset::load_prog_chunk ( QFile& file ) { ProgHeader prog_header; const int nread = sizeof(prog_header); if (file.read((char *) &prog_header, nread) < nread) return false; return load_chunk(file, 1); } bool qtractorVst2Preset::load_chunk ( QFile& file, int preset ) { Chunk chunk; const int nread = sizeof(chunk.size); if (file.read((char *) &chunk.size, nread) < nread) return false; fx_endian_swap(chunk.size); const int ndata = int(chunk.size); chunk.data = new char [ndata]; if (file.read(chunk.data, ndata) < ndata) { delete [] chunk.data; return false; } for (unsigned short i = 0; i < m_pVst2Plugin->instances(); ++i) { m_pVst2Plugin->vst2_dispatch(i, effSetChunk, preset, chunk.size, (void *) chunk.data, 0.0f); } delete [] chunk.data; return true; } // File loader. // bool qtractorVst2Preset::load ( const QString& sFilename ) { if (m_pVst2Plugin == nullptr) return false; const QFileInfo fi(sFilename); const QString& sExt = fi.suffix().toLower(); const bool bFxBank = (sExt == "fxb"); const bool bFxProg = (sExt == "fxp"); if (!bFxBank && !bFxProg) return false; if (!fi.exists()) return false; QFile file(sFilename); if (!file.open(QIODevice::ReadOnly)) return false; BaseHeader base_header; const int nread_base = sizeof(base_header); if (file.read((char *) &base_header, nread_base) < nread_base) { file.close(); return false; } #ifdef CONFIG_DEBUG qDebug("qtractorVst2Preset::load(\"%s\")", sFilename.toUtf8().constData()); #endif // fx_endian_swap(base_header.chunkMagic); fx_endian_swap(base_header.byteSize); // fx_endian_swap(base_header.fxMagic); fx_endian_swap(base_header.version); fx_endian_swap(base_header.fxID); fx_endian_swap(base_header.fxVersion); bool bResult = false; if (!fx_is_magic(base_header.chunkMagic, cMagic)) { #ifdef CONFIG_DEBUG qDebug("qtractorVst2Presetload() header.chunkMagic is not \"%s\".", cMagic); #endif } else if (base_header.fxID != VstInt32(m_pVst2Plugin->uniqueID())) { #ifdef CONFIG_DEBUG qDebug("qtractorVst2Preset::load() header.fxID != 0x%08lx.", m_pVst2Plugin->uniqueID()); #endif } else if (fx_is_magic(base_header.fxMagic, bankMagic)) { #ifdef CONFIG_DEBUG qDebug("qtractorVst2Preset::load() header.fxMagic is \"%s\" (regular fxb)", bankMagic); #endif bResult = load_bank_progs(file); } else if (fx_is_magic(base_header.fxMagic, chunkBankMagic)) { #ifdef CONFIG_DEBUG qDebug("qtractorVst2Preset::load() header.fxMagic is \"%s\" (chunked fxb)", chunkBankMagic); #endif bResult = load_bank_chunk(file); } else if (fx_is_magic(base_header.fxMagic, fMagic)) { #ifdef CONFIG_DEBUG qDebug("qtractorVst2Preset::load() header.fxMagic is \"%s\" (regular fxp)", fMagic); #endif bResult = load_prog_params(file); } else if (fx_is_magic(base_header.fxMagic, chunkPresetMagic)) { #ifdef CONFIG_DEBUG qDebug("qtractorVst2Preset::load() header.fxMagic is \"%s\" (chunked fxp)", chunkPresetMagic); #endif bResult = load_prog_chunk(file); } #ifdef CONFIG_DEBUG else qDebug("qtractorVst2Preset::load() header.fxMagic not recognized."); #endif file.close(); // HACK: Make sure all displayed parameter values are in sync. m_pVst2Plugin->updateParamValues(false); return bResult; } // Saver methods. // bool qtractorVst2Preset::save_bank_progs ( QFile& file ) { if (m_pVst2Plugin == nullptr) return false; AEffect *pVst2Effect = m_pVst2Plugin->vst2_effect(0); if (pVst2Effect == nullptr) return false; const int iNumPrograms = pVst2Effect->numPrograms; if (iNumPrograms < 1) return false; const int iCurrentProgram = m_pVst2Plugin->vst2_dispatch(0, effGetProgram, 0, 0, nullptr, 0.0f); const int iVst2Version = m_pVst2Plugin->vst2_dispatch(0, effGetVstVersion, 0, 0, nullptr, 0.0f); BankHeader bank_header; ::memset(&bank_header, 0, sizeof(bank_header)); bank_header.numPrograms = iNumPrograms; bank_header.currentProgram = iCurrentProgram; fx_endian_swap(bank_header.numPrograms); fx_endian_swap(bank_header.currentProgram); file.write((char *) &bank_header, sizeof(bank_header)); const bool bChunked = (m_pVst2Plugin->type())->isConfigure(); bool bResult = false; for (int iProgram = 0; iProgram < iNumPrograms; ++iProgram) { m_pVst2Plugin->vst2_dispatch(0, effSetProgram, 0, iProgram, nullptr, 0.0f); BaseHeader base_header; ::memset(&base_header, 0, sizeof(base_header)); base_header.chunkMagic = *(VstInt32 *) cMagic; base_header.byteSize = 0; // FIXME! base_header.fxMagic = *(VstInt32 *) (bChunked ? chunkPresetMagic : fMagic); base_header.version = 1; base_header.fxID = m_pVst2Plugin->uniqueID(); base_header.fxVersion = iVst2Version; // Estimate size of this section... base_header.byteSize = sizeof(base_header) - sizeof(base_header.chunkMagic) - sizeof(base_header.byteSize); Chunk chunk; if (bChunked) { get_chunk(chunk, 1); base_header.byteSize += sizeof(chunk.size) + chunk.size; } else { const int iNumParams = pVst2Effect->numParams; base_header.byteSize += sizeof(ProgHeader); base_header.byteSize += iNumParams * sizeof(float); } // fx_endian_swap(base_header.chunkMagic); fx_endian_swap(base_header.byteSize); // fx_endian_swap(base_header.fxMagic); fx_endian_swap(base_header.version); fx_endian_swap(base_header.fxID); fx_endian_swap(base_header.fxVersion); file.write((char *) &base_header, sizeof(base_header)); if (bChunked) { bResult = save_prog_chunk(file, chunk); if (!bResult) break; } else { bResult = save_prog_params(file); if (!bResult) break; } } m_pVst2Plugin->vst2_dispatch(0, effSetProgram, 0, iCurrentProgram, nullptr, 0.0f); return bResult; } bool qtractorVst2Preset::save_prog_params ( QFile& file ) { if (m_pVst2Plugin == nullptr) return false; AEffect *pVst2Effect = m_pVst2Plugin->vst2_effect(0); if (pVst2Effect == nullptr) return false; const int iNumParams = pVst2Effect->numParams; if (iNumParams < 1) return false; ProgHeader prog_header; ::memset(&prog_header, 0, sizeof(prog_header)); prog_header.numParams = iNumParams; m_pVst2Plugin->vst2_dispatch(0, effGetProgramName, 0, 0, (void *) prog_header.prgName, 0.0f); fx_endian_swap(prog_header.numParams); file.write((char *) &prog_header, sizeof(prog_header)); float *params = new float [iNumParams]; for (int iParam = 0; iParam < iNumParams; ++iParam) { params[iParam] = pVst2Effect->getParameter(pVst2Effect, iParam); fx_endian_swap(params[iParam]); } file.write((char *) params, iNumParams * sizeof(float)); delete [] params; return true; } bool qtractorVst2Preset::save_bank_chunk ( QFile& file, const Chunk& chunk ) { if (m_pVst2Plugin == nullptr) return false; AEffect *pVst2Effect = m_pVst2Plugin->vst2_effect(0); if (pVst2Effect == nullptr) return false; const int iNumPrograms = pVst2Effect->numPrograms; const int iCurrentProgram = m_pVst2Plugin->vst2_dispatch(0, effGetProgram, 0, 0, nullptr, 0.0f); BankHeader bank_header; ::memset(&bank_header, 0, sizeof(bank_header)); bank_header.numPrograms = iNumPrograms; bank_header.currentProgram = iCurrentProgram; fx_endian_swap(bank_header.numPrograms); fx_endian_swap(bank_header.currentProgram); file.write((char *) &bank_header, sizeof(bank_header)); return save_chunk(file, chunk); } bool qtractorVst2Preset::save_prog_chunk ( QFile& file, const Chunk& chunk ) { if (m_pVst2Plugin == nullptr) return false; AEffect *pVst2Effect = m_pVst2Plugin->vst2_effect(0); if (pVst2Effect == nullptr) return false; const int iNumParams = pVst2Effect->numParams; ProgHeader prog_header; ::memset(&prog_header, 0, sizeof(prog_header)); prog_header.numParams = iNumParams; m_pVst2Plugin->vst2_dispatch(0, effGetProgramName, 0, 0, (void *) prog_header.prgName, 0.0f); fx_endian_swap(prog_header.numParams); file.write((char *) &prog_header, sizeof(prog_header)); return save_chunk(file, chunk); } bool qtractorVst2Preset::save_chunk ( QFile& file, const Chunk& chunk ) { const int ndata = int(chunk.size); VstInt32 chunk_size = ndata; fx_endian_swap(chunk_size); file.write((char *) &chunk_size, sizeof(chunk_size)); file.write((char *) chunk.data, ndata); return true; } bool qtractorVst2Preset::get_chunk ( Chunk& chunk, int preset ) { chunk.data = nullptr; chunk.size = m_pVst2Plugin->vst2_dispatch(0, effGetChunk, preset, 0, (void *) &chunk.data, 0.0f); return (chunk.size > 0 && chunk.data != nullptr); } // File saver. // bool qtractorVst2Preset::save ( const QString& sFilename ) { if (m_pVst2Plugin == nullptr) return false; AEffect *pVst2Effect = m_pVst2Plugin->vst2_effect(0); if (pVst2Effect == nullptr) return false; const QFileInfo fi(sFilename); const QString& sExt = fi.suffix().toLower(); const bool bFxBank = (sExt == "fxb"); const bool bFxProg = (sExt == "fxp"); if (!bFxBank && !bFxProg) return false; QFile file(sFilename); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return false; #ifdef CONFIG_DEBUG qDebug("qtractorVst2Preset::save(\"%s\")", sFilename.toUtf8().constData()); #endif const bool bChunked = (m_pVst2Plugin->type())->isConfigure(); const int iVst2Version = m_pVst2Plugin->vst2_dispatch(0, effGetVstVersion, 0, 0, nullptr, 0.0f); BaseHeader base_header; ::memset(&base_header, 0, sizeof(base_header)); base_header.chunkMagic = *(VstInt32 *) cMagic; base_header.byteSize = 0; // FIXME: see below... base_header.fxMagic = 0; // base_header.version = 1; base_header.fxID = m_pVst2Plugin->uniqueID(); base_header.fxVersion = iVst2Version; // Estimate size of this section... base_header.byteSize = sizeof(base_header) - sizeof(base_header.chunkMagic) - sizeof(base_header.byteSize); Chunk chunk; if (bFxBank) { if (bChunked) { get_chunk(chunk, 0); base_header.byteSize += sizeof(chunk.size) + chunk.size; base_header.fxMagic = *(VstInt32 *) chunkBankMagic; } else { const int iNumParams = pVst2Effect->numParams; base_header.byteSize += pVst2Effect->numPrograms * (sizeof(ProgHeader) + iNumParams * sizeof(float)); base_header.fxMagic = *(VstInt32 *) bankMagic; } } else { char szName[24]; ::memset(szName, 0, sizeof(szName)); ::strncpy(szName, fi.baseName().toUtf8().constData(), sizeof(szName) - 1); for (unsigned short i = 0; i < m_pVst2Plugin->instances(); ++i) m_pVst2Plugin->vst2_dispatch(i, effSetProgramName, 0, 0, (void *) szName, 0.0f); if (bChunked) { get_chunk(chunk, 1); base_header.byteSize += sizeof(chunk.size) + chunk.size; base_header.fxMagic = *(VstInt32 *) chunkPresetMagic; } else { const int iNumParams = pVst2Effect->numParams; base_header.byteSize += sizeof(ProgHeader); base_header.byteSize += iNumParams * sizeof(float); base_header.fxMagic = *(VstInt32 *) fMagic; } } // fx_endian_swap(base_header.chunkMagic); fx_endian_swap(base_header.byteSize); // fx_endian_swap(base_header.fxMagic); fx_endian_swap(base_header.version); fx_endian_swap(base_header.fxID); fx_endian_swap(base_header.fxVersion); file.write((char *) &base_header, sizeof(base_header)); bool bResult = false; if (bFxBank) { if (bChunked) bResult = save_bank_chunk(file, chunk); else bResult = save_bank_progs(file); } else { if (bChunked) bResult = save_prog_chunk(file, chunk); else bResult = save_prog_params(file); } file.close(); return bResult; } #endif // CONFIG_VST2 // end of qtractorVst2Plugin.cpp qtractor-1.5.9/src/PaxHeaders/qtractorCurveFile.h0000644000000000000000000000013215101070305017037 xustar0030 mtime=1761898693.069267597 30 atime=1761898693.069267597 30 ctime=1761898693.069267597 qtractor-1.5.9/src/qtractorCurveFile.h0000644000175000001440000000663715101070305017043 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.9/src/PaxHeaders/qtractorCurve.h0000644000000000000000000000013215101070305016237 xustar0030 mtime=1761898693.069267597 30 atime=1761898693.068267594 30 ctime=1761898693.069267597 qtractor-1.5.9/src/qtractorCurve.h0000644000175000001440000003410115101070305016226 0ustar00rncbcusers// qtractorCurve.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 __qtractorCurve_h #define __qtractorCurve_h #include "qtractorObserver.h" #include "qtractorMidiSequence.h" #include #include // Forward declarations. class qtractorTimeScale; class qtractorCurveList; class qtractorCurveEditList; //---------------------------------------------------------------------- // class qtractorCurve -- The generic curve declaration. // class qtractorCurve : public qtractorList::Link { public: // Curve modes. enum Mode { Hold = 0, Linear = 1, Spline = 2 }; // Constructor. qtractorCurve(qtractorCurveList *pList, qtractorSubject *pSubject, Mode mode, unsigned int iMinFrameDist = 3200); // Destructor. ~qtractorCurve(); // Curve list owner accessors. void setList(qtractorCurveList *pList) { m_pList = pList; } qtractorCurveList *list() const { return m_pList; } // Curve subject accessors. void setSubject(qtractorSubject *pSubject) { m_observer.setSubject(pSubject); } qtractorSubject *subject() const { return m_observer.subject(); } // Curve mode accessor. void setMode(Mode mode) { m_mode = (m_observer.isDecimal() ? mode : Hold); } Mode mode() const { return (m_observer.isDecimal() ? m_mode : Hold); } // Minimum distance between adjacent nodes accessors. void setMinFrameDist(unsigned int iMinFrameDist) { m_iMinFrameDist = iMinFrameDist; } unsigned int minFrameDist() const { return m_iMinFrameDist; } // The curve node declaration. struct Node : public qtractorList::Link { // Constructor. Node(unsigned long iFrame = 0, float fValue = 0.0f) : frame(iFrame), value(fValue), a(0.0f), b(0.0f), c(0.0f), d(0.0f) {} // Node members. unsigned long frame; float value; float a, b, c, d; }; // Node list accessor. const qtractorList& nodes() const { return m_nodes; } // Curve list reset method. void clear(); // Node list management methods. Node *addNode(unsigned long iFrame, float fValue, qtractorCurveEditList *pEditList = nullptr); void insertNode(Node *pNode); void unlinkNode(Node *pNode); void removeNode(Node *pNode); // Master seeker method. Node *seek(unsigned long iFrame) { return m_cursor.seek(iFrame); } // Common interpolate methods. float value(const Node *pNode, unsigned long iFrame) const; float value(unsigned long iFrame); // Normalized scale converters. float valueFromScale(float fScale) const; float scaleFromValue(float fValue) const; // Common normalized methods. float scale(const Node *pNode, unsigned long iFrame) const { return scaleFromValue(value(pNode, iFrame)); } float scale(unsigned long iFrame) { return scaleFromValue(value(iFrame)); } // Default value accessors. void setDefaultValue(float fDefaultValue); float defaultValue() const { return m_tail.value; } // Default length accessors. void setLength(unsigned long iLength); unsigned long length() const { return m_tail.frame; } // Refresh all coefficients. void update(); // To optimize and keep track of current frame // position, mostly like a sequence cursor/iterator. class Cursor { public: // Constructor. Cursor(qtractorCurve *pCurve) : m_pCurve(pCurve), m_pNode(nullptr), m_iFrame(0) {} // Accessors. qtractorCurve *curve() const { return m_pCurve; } unsigned long frame() const { return m_iFrame; } // Specific methods. Node *seek(unsigned long iFrame); void reset(Node *pNode = nullptr); // Interpolate methods. float value(const Node *pNode, unsigned long iFrame) const { return m_pCurve->value(pNode, iFrame); } float value(unsigned long iFrame) { return value(seek(iFrame), iFrame); } // Normalized methods. float scale(const Node *pNode, unsigned long iFrame) const { return m_pCurve->scale(pNode, iFrame); } float scale(unsigned long iFrame) { return scale(seek(iFrame), iFrame); } float scale(const Node *pNode) const { return m_pCurve->scaleFromValue(pNode->value); } private: // Member variables. qtractorCurve *m_pCurve; Node *m_pNode; unsigned long m_iFrame; }; // Internal cursor accessor. Cursor& cursor() { return m_cursor; } // Curve state flags. enum State { Idle = 0, Process = 1, Capture = 2, Locked = 4 }; bool isIdle() const { return (m_state == Idle); } bool isProcess() const { return (m_state & Process); } bool isCapture() const { return (m_state & Capture); } bool isLocked() const { return (m_state & Locked); } // Capture/process state settlers. void setCapture(bool bCapture); void setProcess(bool bProcess); void setLocked(bool bLocked); // The meta-processing automation procedure. void process(unsigned long iFrame) { if (isProcess()) m_observer.setValue(value(iFrame)); } void process() { process(m_cursor.frame()); } // Record automation procedure. void capture(unsigned long iFrame) { if (isCapture()) addNode(iFrame, m_observer.lastValue(), m_pEditList); } void capture() { capture(m_cursor.frame()); } qtractorCurveEditList *editList() const { return m_pEditList; } // Copy all events from another curve (raw-copy). void copyNodes(qtractorCurve *pCurve); // Convert MIDI sequence events to curve nodes. void readMidiSequence(qtractorMidiSequence *pSeq, qtractorMidiEvent::EventType ctype, unsigned short iChannel, unsigned short iParam, qtractorTimeScale *pTimeScale); // Convert curve nodes to MIDI sequence. void writeMidiSequence(qtractorMidiSequence *pSeq, qtractorMidiEvent::EventType ctype, unsigned short iChannel, unsigned short iParam, qtractorTimeScale *pTimeScale) const; // Logarithmic scale mode accessors. void setLogarithmic(bool bLogarithmic) { m_bLogarithmic = bLogarithmic; } bool isLogarithmic() const { return m_bLogarithmic; } // Curve mode accessor. void setColor(const QColor& color) { m_color = color; } const QColor& color() const { return m_color; } // Emptyness status. bool isEmpty() const { return (m_nodes.count() < 1); } protected: // Snap to minimum distance frame. bool isMinFrameDist(Node *pNode, unsigned long iFrame) const; // Node interpolation coefficients updater. void updateNode(Node *pNode); void updateNodeEx(Node *pNode); // Observer for capture. class Observer : public qtractorObserver { public: // Constructor. Observer(qtractorSubject *pSubject, qtractorCurve *pCurve) : qtractorObserver(pSubject), m_pCurve(pCurve) {} // Capture updater. void update(bool) { m_pCurve->capture(); } private: // Own curve reference. qtractorCurve *m_pCurve; }; private: // Curve list owner. qtractorCurveList *m_pList; // Curve mode. Mode m_mode; // Minimum distance between adjacent nodes. unsigned int m_iMinFrameDist; // Capture observer. Observer m_observer; // The curve node list. qtractorList m_nodes; // Default (initial/final) node. Node m_tail; // Curve state. State m_state; // Optimizing cursor. Cursor m_cursor; // Logarithmic scale mode accessors. bool m_bLogarithmic; // Curve color. QColor m_color; // Capture (record) edit list. qtractorCurveEditList *m_pEditList; }; //---------------------------------------------------------------------------- // qtractorCurveListProxy -- Automation signal/slot notifier. // class qtractorCurveListProxy : public QObject { Q_OBJECT public: qtractorCurveListProxy(QObject *pParent = nullptr) : QObject(pParent) {} void notify() { emit update(); } signals: void update(); }; //---------------------------------------------------------------------- // qtractorCurveEditList -- Curve node edit (command) list. class qtractorCurveEditList { public: // Constructor. qtractorCurveEditList(qtractorCurve *pCurve) : m_pCurve(pCurve) {} // Copy cnstructor. qtractorCurveEditList(const qtractorCurveEditList& list) : m_pCurve(list.curve()) { append(list); } // Destructor ~qtractorCurveEditList() { clear(); } // Curve accessor. qtractorCurve *curve() const { return m_pCurve; } // List predicate accessor. bool isEmpty() const { return m_items.isEmpty(); } // List methods. void addNode(qtractorCurve::Node *pNode) { m_items.append(new Item(AddNode, pNode, pNode->frame)); } void moveNode(qtractorCurve::Node *pNode, unsigned long iFrame, float fValue) { m_items.append(new Item(MoveNode, pNode, iFrame, fValue)); } void removeNode(qtractorCurve::Node *pNode) { m_items.append(new Item(RemoveNode, pNode)); } // List appender. void append(const qtractorCurveEditList& list) { QListIterator iter(list.m_items); while (iter.hasNext()) m_items.append(new Item(*iter.next())); } // List cleanup. void clear() { QListIterator iter(m_items); while (iter.hasNext()) { Item *pItem = iter.next(); if (pItem->autoDelete) delete pItem->node; } qDeleteAll(m_items); m_items.clear(); } // Curve edit list command executive. bool execute(bool bRedo = true); protected: // Primitive command types. enum Command { AddNode, MoveNode, RemoveNode }; // Curve item struct. struct Item { // Item constructor. Item(Command cmd, qtractorCurve::Node *pNode, unsigned long iFrame = 0, float fValue = 0.0f) : command(cmd), node(pNode), frame(iFrame), value(fValue), autoDelete(false) {} // Item copy constructor. Item(const Item& item) : command(item.command), node(item.node), frame(item.frame), value(item.value), autoDelete(item.autoDelete) {} // Item members. Command command; qtractorCurve::Node *node; unsigned long frame; float value; bool autoDelete; }; private: // Instance variables. qtractorCurve *m_pCurve; QList m_items; }; //---------------------------------------------------------------------------- // qtractorCurveList -- Automation item list // class qtractorCurveList : public qtractorList { public: // Constructor. qtractorCurveList() : m_iProcess(0), m_iCapture(0), m_iLocked(0), m_pCurrentCurve(nullptr) { setAutoDelete(true); } // ~Destructor. ~qtractorCurveList() { clearAll(); } // Simple list methods. void addCurve(qtractorCurve *pCurve) { append(pCurve); pCurve->setList(this); if (pCurve->isProcess()) updateProcess(true); if (pCurve->isCapture()) updateCapture(true); if (pCurve->isLocked()) updateLocked(true); } void removeCurve(qtractorCurve *pCurve) { if (m_pCurrentCurve == pCurve) m_pCurrentCurve = nullptr; if (pCurve->isProcess()) updateProcess(false); if (pCurve->isCapture()) updateCapture(false); if (pCurve->isLocked()) updateLocked(false); pCurve->setList(nullptr); unlink(pCurve); } // Set common curve length procedure. void setLength(unsigned long iLength) { qtractorCurve *pCurve = first(); while (pCurve) { pCurve->setLength(iLength); pCurve = pCurve->next(); } } // The meta-processing automation procedure. void process(unsigned long iFrame) { qtractorCurve *pCurve = first(); while (pCurve) { pCurve->process(iFrame); pCurve = pCurve->next(); } } // Process management. void updateProcess(bool bProcess) { if (bProcess) ++m_iProcess; else --m_iProcess; } void setProcessAll(bool bProcess) { qtractorCurve *pCurve = first(); while (pCurve) { pCurve->setProcess(bProcess); pCurve = pCurve->next(); } } bool isProcessAll() const { return isProcess() && m_iProcess >= count(); } bool isProcess() const { return m_iProcess > 0; } // Capture management. void updateCapture(bool bCapture) { if (bCapture) ++m_iCapture; else --m_iCapture; } void setCaptureAll(bool bCapture) { qtractorCurve *pCurve = first(); while (pCurve) { pCurve->setCapture(bCapture); pCurve = pCurve->next(); } } bool isCaptureAll() const { return isCapture() && m_iCapture >= count(); } bool isCapture() const { return m_iCapture > 0; } // Locked management. void updateLocked(bool bLocked) { if (bLocked) ++m_iLocked; else --m_iLocked; } void setLockedAll(bool bLocked) { qtractorCurve *pCurve = first(); while (pCurve) { pCurve->setLocked(bLocked); pCurve = pCurve->next(); } } bool isLockedAll() const { return isLocked() && m_iLocked >= count(); } bool isLocked() const { return m_iLocked > 0; } // Check whether there's any captured material. bool isEditListEmpty() const { qtractorCurve *pCurve = first(); while (pCurve) { qtractorCurveEditList *pEditList = pCurve->editList(); if (pEditList && !pEditList->isEmpty()) return false; pCurve = pCurve->next(); } return true; } // Emptyness status. bool isEmpty() const { return (count() < 1); } // Whole list cleaner. void clearAll() { m_pCurrentCurve = nullptr; clear(); m_iCapture = 0; m_iProcess = 0; m_iLocked = 0; } // Signal/slot notifier accessor. const qtractorCurveListProxy *proxy() const { return &m_proxy; } // Signal/slot notification. void notify() { m_proxy.notify(); } // Current curve accessors. void setCurrentCurve(qtractorCurve *pCurve) { m_pCurrentCurve = pCurve; } qtractorCurve *currentCurve() const { return m_pCurrentCurve; } private: // Mass capture/process state counters. int m_iProcess; int m_iCapture; int m_iLocked; // Signal/slot notifier. qtractorCurveListProxy m_proxy; // Current selected curve. qtractorCurve *m_pCurrentCurve; }; #endif // __qtractorCurve_h // end of qtractorCurve.h qtractor-1.5.9/src/PaxHeaders/qtractorLadspaPlugin.cpp0000644000000000000000000000012715101070305020075 xustar0029 mtime=1761898693.07326761 29 atime=1761898693.07326761 29 ctime=1761898693.07326761 qtractor-1.5.9/src/qtractorLadspaPlugin.cpp0000644000175000001440000004170715101070305020072 0ustar00rncbcusers// qtractorLadspaPlugin.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" #ifdef CONFIG_LADSPA #include "qtractorLadspaPlugin.h" #include "qtractorSession.h" #include "qtractorAudioEngine.h" #include //---------------------------------------------------------------------------- // qtractorLadspaPluginType -- LADSPA plugin type instance. // // Derived methods. bool qtractorLadspaPluginType::open (void) { // Do we have a descriptor already? if (m_pLadspaDescriptor == nullptr) m_pLadspaDescriptor = ladspa_descriptor(file(), index()); if (m_pLadspaDescriptor == nullptr) return false; #ifdef CONFIG_DEBUG qDebug("qtractorLadspaPluginType[%p]::open() filename=\"%s\" index=%lu", this, filename().toUtf8().constData(), index()); #endif // Retrieve plugin type names. m_sName = m_pLadspaDescriptor->Name; m_sLabel = m_pLadspaDescriptor->Label; // Retrieve plugin unique identifier. m_iUniqueID = m_pLadspaDescriptor->UniqueID; // Compute and cache port counts... m_iControlIns = 0; m_iControlOuts = 0; m_iAudioIns = 0; m_iAudioOuts = 0; m_iMidiIns = 0; m_iMidiOuts = 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; } } // Cache flags. m_bRealtime = LADSPA_IS_HARD_RT_CAPABLE(m_pLadspaDescriptor->Properties); // Done. return true; } void qtractorLadspaPluginType::close (void) { m_pLadspaDescriptor = nullptr; } // Factory method (static) qtractorLadspaPluginType *qtractorLadspaPluginType::createType ( qtractorPluginFile *pFile, unsigned long iIndex ) { // Sanity check... if (pFile == nullptr) return nullptr; // Retrieve descriptor if any... const LADSPA_Descriptor *pLadspaDescriptor = ladspa_descriptor(pFile, iIndex); if (pLadspaDescriptor == nullptr) return nullptr; // Yep, most probably its a valid plugin descriptor... return new qtractorLadspaPluginType(pFile, iIndex, qtractorPluginType::Ladspa, pLadspaDescriptor); } // Descriptor method (static) const LADSPA_Descriptor *qtractorLadspaPluginType::ladspa_descriptor ( qtractorPluginFile *pFile, unsigned long iIndex ) { // Retrieve the descriptor function, if any... LADSPA_Descriptor_Function pfnLadspaDescriptor = (LADSPA_Descriptor_Function) pFile->resolve("ladspa_descriptor"); if (pfnLadspaDescriptor == nullptr) return nullptr; // Retrieve descriptor if any... return (*pfnLadspaDescriptor)(iIndex); } // Instance cached-deferred accesors. const QString& qtractorLadspaPluginType::aboutText (void) { if (m_sAboutText.isEmpty() && m_pLadspaDescriptor) { if (m_pLadspaDescriptor->Maker) { if (!m_sAboutText.isEmpty()) m_sAboutText += '\n'; m_sAboutText += QObject::tr("Author: "); m_sAboutText += m_pLadspaDescriptor->Maker; } if (m_pLadspaDescriptor->Copyright) { if (!m_sAboutText.isEmpty()) m_sAboutText += '\n'; m_sAboutText += QObject::tr("Copyright: "); m_sAboutText += m_pLadspaDescriptor->Copyright; } } return m_sAboutText; } //---------------------------------------------------------------------------- // qtractorLadspaPlugin -- LADSPA plugin instance. // // Constructors. qtractorLadspaPlugin::qtractorLadspaPlugin ( qtractorPluginList *pList, qtractorLadspaPluginType *pLadspaType ) : qtractorPlugin(pList, pLadspaType), m_phInstances(nullptr), m_piControlOuts(nullptr), m_pfControlOuts(nullptr), m_piAudioIns(nullptr), m_piAudioOuts(nullptr), m_pfIDummy(nullptr), m_pfODummy(nullptr), m_pfLatency(nullptr) { #ifdef CONFIG_DEBUG qDebug("qtractorLadspaPlugin[%p] filename=\"%s\" index=%lu typeHint=%d", this, type()->filename().toUtf8().constData(), type()->index(), int(type()->typeHint())); #endif // Get some structural data first... const LADSPA_Descriptor *pLadspaDescriptor = pLadspaType->ladspa_descriptor(); if (pLadspaDescriptor) { unsigned short iControlOuts = pLadspaType->controlOuts(); unsigned short iAudioIns = pLadspaType->audioIns(); unsigned short iAudioOuts = pLadspaType->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]; } iControlOuts = iAudioIns = iAudioOuts = 0; for (unsigned long i = 0; i < pLadspaDescriptor->PortCount; ++i) { const LADSPA_PortDescriptor portType = pLadspaDescriptor->PortDescriptors[i]; if (LADSPA_IS_PORT_INPUT(portType)) { if (LADSPA_IS_PORT_AUDIO(portType)) m_piAudioIns[iAudioIns++] = i; else if (LADSPA_IS_PORT_CONTROL(portType)) addParam(new Param(this, i)); } else if (LADSPA_IS_PORT_OUTPUT(portType)) { if (LADSPA_IS_PORT_AUDIO(portType)) m_piAudioOuts[iAudioOuts++] = i; else if (LADSPA_IS_PORT_CONTROL(portType)) { m_piControlOuts[iControlOuts] = i; m_pfControlOuts[iControlOuts] = 0.0f; if (::strcmp(pLadspaDescriptor->PortNames[i], "latency") == 0) m_pfLatency = &m_pfControlOuts[iControlOuts]; ++iControlOuts; } } } // FIXME: instantiate each instance properly... qtractorLadspaPlugin::setChannels(channels()); } } // Destructor. qtractorLadspaPlugin::~qtractorLadspaPlugin (void) { // Cleanup all plugin instances... cleanup(); // setChannels(0); // Free up all the rest... 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_pfIDummy) delete [] m_pfIDummy; if (m_pfODummy) delete [] m_pfODummy; } // Channel/instance number accessors. void qtractorLadspaPlugin::setChannels ( unsigned short iChannels ) { // Check our type... qtractorLadspaPluginType *pLadspaType = static_cast (type()); if (pLadspaType == nullptr) return; // Estimate the (new) number of instances... const unsigned short iOldInstances = instances(); unsigned short iInstances = 0; if (iChannels > 0) { iInstances = pLadspaType->instances(iChannels, list()->isMidi()); // Now see if instance count changed anyhow... if (iInstances == iOldInstances && iChannels == channels()) return; } const LADSPA_Descriptor *pLadspaDescriptor = pLadspaType->ladspa_descriptor(); if (pLadspaDescriptor == nullptr) return; // Gotta go for a while... const bool bActivated = isActivated(); setChannelsActivated(iChannels, false); // Set new instance number... setInstances(iInstances); if (m_phInstances) { if (pLadspaDescriptor->cleanup) { for (unsigned short i = 0; i < iOldInstances; ++i) (*pLadspaDescriptor->cleanup)(m_phInstances[i]); } delete [] m_phInstances; m_phInstances = nullptr; } // Bail out, if none are about to be created... if (iInstances < 1) { setChannelsActivated(iChannels, bActivated); return; } #ifdef CONFIG_DEBUG qDebug("qtractorLadspaPlugin[%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 = pLadspaType->audioIns(); const unsigned short iAudioOuts = pLadspaType->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)); } // We'll need output control (not dummy anymore) port indexes... const unsigned short iControlOuts = pLadspaType->controlOuts(); unsigned short i, j; // Allocate new instances... m_phInstances = new LADSPA_Handle [iInstances]; for (i = 0; i < iInstances; ++i) { // Instantiate them properly first... LADSPA_Handle handle = (*pLadspaDescriptor->instantiate)(pLadspaDescriptor, iSampleRate); // 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(); // Just in case the plugin decides // to set the port value at this time... float *pfValue = pParam->subject()->data(); float fValue = *pfValue; (*pLadspaDescriptor->connect_port)(handle, pParam->index(), pfValue); // Make new one the default and restore port value... pParam->setDefaultValue(*pfValue); *pfValue = fValue; } // Connect all existing output control ports... for (j = 0; j < iControlOuts; ++j) { (*pLadspaDescriptor->connect_port)(handle, m_piControlOuts[j], &m_pfControlOuts[j]); } // Connect all dummy input ports... if (m_pfIDummy) for (j = iChannels; j < iAudioIns; ++j) { (*pLadspaDescriptor->connect_port)(handle, m_piAudioIns[j], m_pfIDummy); // dummy input port! } // Connect all dummy output ports... if (m_pfODummy) for (j = iChannels; j < iAudioOuts; ++j) { (*pLadspaDescriptor->connect_port)(handle, m_piAudioOuts[j], m_pfODummy); // dummy input port! } // This is it... m_phInstances[i] = handle; } // (Re)issue all configuration as needed... realizeConfigs(); realizeValues(); // But won't need it anymore. releaseConfigs(); releaseValues(); // (Re)activate instance if necessary... setChannelsActivated(iChannels, bActivated); } // Specific accessors. const LADSPA_Descriptor *qtractorLadspaPlugin::ladspa_descriptor (void) const { qtractorLadspaPluginType *pLadspaType = static_cast (type()); return (pLadspaType ? pLadspaType->ladspa_descriptor() : nullptr); } LADSPA_Handle qtractorLadspaPlugin::ladspa_handle ( unsigned short iInstance ) const { return (m_phInstances ? m_phInstances[iInstance] : nullptr); } // Do the actual activation. void qtractorLadspaPlugin::activate (void) { const LADSPA_Descriptor *pLadspaDescriptor = ladspa_descriptor(); if (pLadspaDescriptor == nullptr) return; if (m_phInstances && pLadspaDescriptor->activate) { for (unsigned short i = 0; i < instances(); ++i) (*pLadspaDescriptor->activate)(m_phInstances[i]); } } // Do the actual deactivation. void qtractorLadspaPlugin::deactivate (void) { const LADSPA_Descriptor *pLadspaDescriptor = ladspa_descriptor(); if (pLadspaDescriptor == nullptr) return; if (m_phInstances && pLadspaDescriptor->deactivate) { for (unsigned short i = 0; i < instances(); ++i) (*pLadspaDescriptor->deactivate)(m_phInstances[i]); } } // The main plugin processing procedure. void qtractorLadspaPlugin::process ( float **ppIBuffer, float **ppOBuffer, unsigned int nframes ) { if (m_phInstances == nullptr) return; const LADSPA_Descriptor *pLadspaDescriptor = ladspa_descriptor(); if (pLadspaDescriptor == 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++]); } // Make it run... (*pLadspaDescriptor->run)(handle, nframes); // Wrap dangling output channels?... for (j = iOChannel; j < iChannels; ++j) ::memset(ppOBuffer[j], 0, nframes * sizeof(float)); } } //---------------------------------------------------------------------------- // qtractorLadspaPlugin::Param -- LADSPA plugin control input port instance. // // Constructors. qtractorLadspaPlugin::Param::Param ( qtractorLadspaPlugin *pLadspaPlugin, unsigned long iIndex ) : qtractorPlugin::Param(pLadspaPlugin, iIndex) { const LADSPA_Descriptor *pLadspaDescriptor = pLadspaPlugin->ladspa_descriptor(); const LADSPA_PortRangeHint *pPortRangeHint = &(pLadspaDescriptor->PortRangeHints[iIndex]); m_portHints = pPortRangeHint->HintDescriptor; // Set nominal parameter name... setName(pLadspaDescriptor->PortNames[iIndex]); // Initialize default sample-rate... unsigned int iSampleRate = 44100; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine) iSampleRate = pAudioEngine->sampleRate(); } // Initialize range values... float fMinValue = 0.0f; if (isBoundedBelow()) { fMinValue = pPortRangeHint->LowerBound; if (isSampleRate()) fMinValue *= float(iSampleRate); } setMinValue(fMinValue); float fMaxValue = 1.0f; if (isBoundedAbove()) { fMaxValue = pPortRangeHint->UpperBound; if (isSampleRate()) fMaxValue *= float(iSampleRate); } setMaxValue(fMaxValue); // Port default value... float fDefaultValue = value(); if (isDefaultValue()) { switch (m_portHints & LADSPA_HINT_DEFAULT_MASK) { case LADSPA_HINT_DEFAULT_MINIMUM: fDefaultValue = fMinValue; break; case LADSPA_HINT_DEFAULT_LOW: if (isLogarithmic()) { fDefaultValue = ::expf( ::logf(fMinValue) * 0.75f + ::logf(fMaxValue) * 0.25f); } else { fDefaultValue = (fMinValue * 0.75f + fMaxValue * 0.25f); } break; case LADSPA_HINT_DEFAULT_MIDDLE: if (isLogarithmic()) { fDefaultValue = ::sqrt(fMinValue * fMaxValue); } else { fDefaultValue = (fMinValue + fMaxValue) * 0.5f; } break; case LADSPA_HINT_DEFAULT_HIGH: if (isLogarithmic()) { fDefaultValue = ::expf( ::logf(fMinValue) * 0.25f + ::logf(fMaxValue) * 0.75f); } else { fDefaultValue = (fMinValue * 0.25f + fMaxValue * 0.75f); } break; case LADSPA_HINT_DEFAULT_MAXIMUM: fDefaultValue = fMaxValue; break; case LADSPA_HINT_DEFAULT_0: fDefaultValue = 0.0f; break; case LADSPA_HINT_DEFAULT_1: fDefaultValue = 1.0f; break; case LADSPA_HINT_DEFAULT_100: fDefaultValue = 100.0f; break; case LADSPA_HINT_DEFAULT_440: fDefaultValue = 440.0f; break; } } setDefaultValue(fDefaultValue); // Initialize port value... // reset(); -- deferred to qtractorPlugin::addParam(); } // Port range hints predicate methods. bool qtractorLadspaPlugin::Param::isBoundedBelow (void) const { return LADSPA_IS_HINT_BOUNDED_BELOW(m_portHints); } bool qtractorLadspaPlugin::Param::isBoundedAbove (void) const { return LADSPA_IS_HINT_BOUNDED_ABOVE(m_portHints); } bool qtractorLadspaPlugin::Param::isDefaultValue (void) const { return LADSPA_IS_HINT_HAS_DEFAULT(m_portHints); } bool qtractorLadspaPlugin::Param::isLogarithmic (void) const { return LADSPA_IS_HINT_LOGARITHMIC(m_portHints); } bool qtractorLadspaPlugin::Param::isSampleRate (void) const { return LADSPA_IS_HINT_SAMPLE_RATE(m_portHints); } bool qtractorLadspaPlugin::Param::isInteger (void) const { return LADSPA_IS_HINT_INTEGER(m_portHints); } bool qtractorLadspaPlugin::Param::isToggled (void) const { return LADSPA_IS_HINT_TOGGLED(m_portHints); } bool qtractorLadspaPlugin::Param::isDisplay (void) const { return false; } #endif // CONFIG_LADSPA // end of qtractorLadspaPlugin.cpp qtractor-1.5.9/src/PaxHeaders/qtractorObserverWidget.cpp0000644000000000000000000000013215101070305020441 xustar0030 mtime=1761898693.085267648 30 atime=1761898693.085267648 30 ctime=1761898693.085267648 qtractor-1.5.9/src/qtractorObserverWidget.cpp0000644000175000001440000001174715101070305020443 0ustar00rncbcusers// qtractorObserverWidget.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 "qtractorObserverWidget.h" #include #include //---------------------------------------------------------------------- // class qtractorObserverCheckBox -- Concrete widget observer. // // Constructor. qtractorObserverCheckBox::qtractorObserverCheckBox ( QWidget *pParent ) : qtractorObserverWidget (pParent) { QObject::connect(this, SIGNAL(toggled(bool)), SLOT(checkBoxChanged(bool))); } // Visitors overload. void qtractorObserverCheckBox::updateValue ( float fValue ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorObserverCheckBox[%p]::updateValue(%g)", this, fValue); #endif const bool bBlockSignals = QCheckBox::blockSignals(true); QCheckBox::setChecked(bool(scaleFromValue(fValue))); QCheckBox::blockSignals(bBlockSignals); } // Protected slot. void qtractorObserverCheckBox::checkBoxChanged ( bool bValue ) { const float fValue = valueFromScale(bValue ? 1.0f : 0.0f); #ifdef CONFIG_DEBUG_0 qDebug("qtractorObserverCheckBox[%p]::checkBoxChanged(%g)", this, fValue); #endif observer()->setValue(fValue); emit valueChanged(fValue); } //---------------------------------------------------------------------- // class qtractorObserverSpinBox -- Concrete widget observer. // // Constructor. qtractorObserverSpinBox::qtractorObserverSpinBox ( QWidget *pParent ) : qtractorObserverWidget (pParent) { QObject::connect(this, SIGNAL(valueChangedEx(double)), SLOT(spinBoxChanged(double))); } // Visitors overload. void qtractorObserverSpinBox::updateValue ( float fValue ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorObserverSpinBox[%p]::updateValue(%g)", this, fValue); #endif const bool bBlockSignals = QDoubleSpinBox::blockSignals(true); QDoubleSpinBox::setValue(scaleFromValue(fValue)); QDoubleSpinBox::blockSignals(bBlockSignals); } // Protected slot. void qtractorObserverSpinBox::spinBoxChanged ( double value ) { const float fValue = valueFromScale(float(value)); #ifdef CONFIG_DEBUG_0 qDebug("qtractorObserverSpinBox[%p]::spinBoxChanged(%g)", this, fValue); #endif observer()->setValue(fValue); emit valueChanged(fValue); } //---------------------------------------------------------------------- // class qtractorObserverSlider -- Concrete widget observer. // // Constructor. qtractorObserverSlider::qtractorObserverSlider ( QWidget *pParent ) : qtractorObserverWidget (pParent) { QObject::connect(this, SIGNAL(valueChanged(int)), SLOT(sliderChanged(int))); } // Alternate mouse behavior event handlers. void qtractorObserverSlider::mousePressEvent ( QMouseEvent *pMouseEvent ) { if (pMouseEvent->button() == Qt::MiddleButton) mouseDoubleClickEvent(pMouseEvent); else qtractorObserverWidget::mousePressEvent(pMouseEvent); } void qtractorObserverSlider::mouseDoubleClickEvent ( QMouseEvent */*pMouseEvent*/ ) { // Reset to default value... setValue(scaleFromValue(observer()->defaultValue())); } void qtractorObserverSlider::wheelEvent ( QWheelEvent *pWheelEvent ) { int iValue = value(); const int delta = pWheelEvent->angleDelta().y(); if (pWheelEvent->modifiers() & Qt::ControlModifier) { if (delta > 0) iValue += singleStep(); else iValue -= singleStep(); } else if (delta > 0) iValue += pageStep(); else iValue -= pageStep(); if (iValue > maximum()) iValue = maximum(); else if (iValue < minimum()) iValue = minimum(); setValue(iValue); } // Visitors overload. void qtractorObserverSlider::updateValue ( float fValue ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorObserverSlider[%p]::updateValue(%g)", this, fValue); #endif const bool bBlockSignals = QSlider::blockSignals(true); QSlider::setValue(int(scaleFromValue(fValue))); QSlider::blockSignals(bBlockSignals); } // Protected slot. void qtractorObserverSlider::sliderChanged ( int iValue ) { const float fValue = valueFromScale(float(iValue)); #ifdef CONFIG_DEBUG_0 qDebug("qtractorObserverSlider[%p]::sliderChanged(%g)", this, fValue); #endif observer()->setValue(fValue); emit valueChanged(fValue); }; // end of qtractorObserverWidget.cpp qtractor-1.5.9/src/PaxHeaders/qtractorConnect.cpp0000644000000000000000000000013215101070305017077 xustar0030 mtime=1761898693.068267594 30 atime=1761898693.067267591 30 ctime=1761898693.068267594 qtractor-1.5.9/src/qtractorConnect.cpp0000644000175000001440000014310315101070305017071 0ustar00rncbcusers// qtractorConnect.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 "qtractorConnect.h" #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 #include #endif //---------------------------------------------------------------------- // qtractorPortListItem -- Port list item. // // Constructor. qtractorPortListItem::qtractorPortListItem ( qtractorClientListItem *pClientItem ) : QTreeWidgetItem(pClientItem, qtractorConnect::PortItem) { m_pClientItem = pClientItem; // m_sPortName = sPortName; m_iPortMark = 0; m_bHilite = false; // QTreeWidgetItem::setText(0, m_sPortName); } // Default destructor. qtractorPortListItem::~qtractorPortListItem (void) { m_connects.clear(); } // Instance accessors. void qtractorPortListItem::setPortName ( const QString& sPortName ) { m_sPortName = sPortName; updatePortName(); } const QString& qtractorPortListItem::clientName (void) const { return m_pClientItem->clientName(); } const QString& qtractorPortListItem::portName (void) const { return m_sPortName; } // Proto-pretty/alias display name method. void qtractorPortListItem::updatePortName (void) { setPortText(m_sPortName); } // Port display name accessors. void qtractorPortListItem::setPortText ( const QString& sPortText ) { QTreeWidgetItem::setText(0, sPortText); } QString qtractorPortListItem::portText (void) const { return QTreeWidgetItem::text(0); } // Complete client:port name helper. QString qtractorPortListItem::clientPortName (void) { return m_pClientItem->clientName() + ':' + m_sPortName; } // Connect client item accessor. qtractorClientListItem *qtractorPortListItem::clientItem (void) const { return m_pClientItem; } // Client:port set housekeeping marker. void qtractorPortListItem::markPort ( int iMark ) { setHilite(false); m_iPortMark = iMark; if (iMark > 0) m_connects.clear(); } void qtractorPortListItem::markClientPort ( int iMark ) { markPort(iMark); m_pClientItem->markClient(iMark); } int qtractorPortListItem::portMark (void) const { return m_iPortMark; } // Connected port list primitives. void qtractorPortListItem::addConnect ( qtractorPortListItem *pPortItem ) { m_connects.append(pPortItem); } void qtractorPortListItem::removeConnect ( qtractorPortListItem *pPortItem ) { QMutableListIterator iter(m_connects); while (iter.hasNext()) { qtractorPortListItem *pPortItemPtr = iter.next(); if (pPortItemPtr == pPortItem) { pPortItem->setHilite(false); iter.remove(); break; } } } // Clear the connection list, taking care of hilighting... void qtractorPortListItem::cleanConnects (void) { QMutableListIterator iter(m_connects); while (iter.hasNext()) { qtractorPortListItem *pPortItem = iter.next(); pPortItem->removeConnect(this); iter.remove(); } } // Connected port finder. qtractorPortListItem *qtractorPortListItem::findConnect ( qtractorPortListItem *pPortItem ) { QListIterator iter(m_connects); while (iter.hasNext()) { qtractorPortListItem *pPortItemPtr = iter.next(); if (pPortItemPtr == pPortItem) return pPortItem; } return nullptr; } // Connection cache list accessor. const QList& qtractorPortListItem::connects (void) const { return m_connects; } // Connectiopn highlight methods. void qtractorPortListItem::setHilite ( bool bHilite ) { // Update the port highlightning if changed... if ((m_bHilite && !bHilite) || (!m_bHilite && bHilite)) { m_bHilite = bHilite; // Propagate this to the parent... m_pClientItem->setHilite(bHilite); } // Set the new color. const QPalette& pal = QTreeWidgetItem::treeWidget()->palette(); QTreeWidgetItem::setForeground(0, m_bHilite ? (pal.base().color().value() < 0x7f ? Qt::cyan : Qt::blue) : pal.text().color()); } bool qtractorPortListItem::isHilite (void) const { return m_bHilite; } // Proxy sort override method. // - Natural decimal sorting comparator. bool qtractorPortListItem::operator< ( const QTreeWidgetItem& other ) const { QTreeWidget *pTreeWidget = QTreeWidgetItem::treeWidget(); if (pTreeWidget == nullptr) return false; const int col = pTreeWidget->sortColumn(); if (col < 0) return false; return qtractorClientListView::lessThan(*this, other, col); } //---------------------------------------------------------------------------- // qtractorClientListItem -- Client list item. // // Constructor. qtractorClientListItem::qtractorClientListItem ( qtractorClientListView *pClientListView ) : QTreeWidgetItem(pClientListView, qtractorConnect::ClientItem) { // m_sClientName = sClientName; m_iClientMark = 0; m_iHilite = 0; // QTreeWidgetItem::setText(0, m_sClientName); } // Default destructor. qtractorClientListItem::~qtractorClientListItem (void) { } // Port finder. qtractorPortListItem *qtractorClientListItem::findPortItem ( const QString& sPortName ) { const int iChildCount = QTreeWidgetItem::childCount(); for (int iChild = 0; iChild < iChildCount; ++iChild) { QTreeWidgetItem *pChild = QTreeWidgetItem::child(iChild); if (pChild->type() != qtractorConnect::PortItem) continue; qtractorPortListItem *pPortItem = static_cast (pChild); if (pPortItem && pPortItem->portName() == sPortName) return pPortItem; } return nullptr; } // Instance accessors. void qtractorClientListItem::setClientName ( const QString& sClientName ) { m_sClientName = sClientName; updateClientName(); } const QString& qtractorClientListItem::clientName (void) const { return m_sClientName; } // Proto-pretty/alias display name method. void qtractorClientListItem::updateClientName (void) { setClientText(m_sClientName); } // Client display name accessors. void qtractorClientListItem::setClientText ( const QString& sClientText ) { QTreeWidgetItem::setText(0, sClientText); } QString qtractorClientListItem::clientText (void) const { return QTreeWidgetItem::text(0); } // Readable flag client accessor. bool qtractorClientListItem::isReadable (void) const { qtractorClientListView *pClientListView = static_cast (QTreeWidgetItem::treeWidget()); return (pClientListView ? pClientListView->isReadable() : false); } // Client:port set housekeeping marker. void qtractorClientListItem::markClient ( int iMark ) { setHilite(false); m_iClientMark = iMark; } void qtractorClientListItem::markClientPorts ( int iMark ) { markClient(iMark); const int iChildCount = QTreeWidgetItem::childCount(); for (int iChild = 0; iChild < iChildCount; ++iChild) { QTreeWidgetItem *pChild = QTreeWidgetItem::child(iChild); if (pChild->type() != qtractorConnect::PortItem) continue; qtractorPortListItem *pPortItem = static_cast (pChild); if (pPortItem) pPortItem->markPort(iMark); } } void qtractorClientListItem::cleanClientPorts ( int iMark ) { int iChildCount = QTreeWidgetItem::childCount(); for (int iChild = 0; iChild < iChildCount; ++iChild) { QTreeWidgetItem *pChild = QTreeWidgetItem::child(iChild); if (pChild->type() != qtractorConnect::PortItem) continue; qtractorPortListItem *pPortItem = static_cast (pChild); if (pPortItem && pPortItem->portMark() == iMark) { pPortItem->cleanConnects(); delete pPortItem; --iChildCount; --iChild; } } } int qtractorClientListItem::clientMark (void) const { return m_iClientMark; } // Connectiopn highlight methods. void qtractorClientListItem::setHilite ( bool bHilite ) { // Update the client highlightning if changed... if (bHilite) ++m_iHilite; else if (m_iHilite > 0) --m_iHilite; // Set the new color. const QPalette& pal = QTreeWidgetItem::treeWidget()->palette(); QTreeWidgetItem::setForeground(0, m_iHilite > 0 ? (pal.base().color().value() < 0x7f ? Qt::darkCyan : Qt::darkBlue) : pal.text().color()); } bool qtractorClientListItem::isHilite (void) const { return (m_iHilite > 0); } // Client item openness status. void qtractorClientListItem::setOpen ( bool bOpen ) { QTreeWidgetItem::setExpanded(bOpen); } bool qtractorClientListItem::isOpen (void) const { return QTreeWidgetItem::isExpanded(); } // Proxy sort override method. // - Natural decimal sorting comparator. bool qtractorClientListItem::operator< ( const QTreeWidgetItem& other ) const { return qtractorClientListView::lessThan(*this, other); } //---------------------------------------------------------------------------- // qtractorClientListView -- Client list view, supporting drag-n-drop. // // Constructor. qtractorClientListView::qtractorClientListView ( QWidget *pParent ) : QTreeWidget(pParent) { m_pConnect = nullptr; m_bReadable = false; m_pAutoOpenTimer = nullptr; m_iAutoOpenTimeout = 0; m_pDragItem = nullptr; m_pDropItem = nullptr; m_pHiliteItem = nullptr; QHeaderView *pHeader = QTreeWidget::header(); pHeader->setDefaultAlignment(Qt::AlignLeft); // pHeader->setDefaultSectionSize(120); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) // pHeader->setSectionResizeMode(QHeaderView::Custom); pHeader->setSectionsMovable(false); pHeader->setSectionsClickable(true); #else // pHeader->setResizeMode(QHeaderView::Custom); pHeader->setMovable(false); pHeader->setClickable(true); #endif pHeader->setSortIndicatorShown(true); pHeader->setStretchLastSection(true); QTreeWidget::setRootIsDecorated(true); QTreeWidget::setUniformRowHeights(true); // QTreeWidget::setDragEnabled(true); QTreeWidget::setAcceptDrops(true); QTreeWidget::setDropIndicatorShown(true); QTreeWidget::setAutoScroll(true); QTreeWidget::setSelectionMode(QAbstractItemView::ExtendedSelection); QTreeWidget::setSizePolicy( QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); QTreeWidget::setSortingEnabled(true); QTreeWidget::setMinimumWidth(120); QTreeWidget::setColumnCount(1); // Trap for help/tool-tips events. QTreeWidget::viewport()->installEventFilter(this); setAutoOpenTimeout(800); } // Default destructor. qtractorClientListView::~qtractorClientListView (void) { setAutoOpenTimeout(0); } // Client item finder. qtractorClientListItem *qtractorClientListView::findClientItem ( const QString& sClientName ) { const int iItemCount = QTreeWidget::topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) { QTreeWidgetItem *pItem = QTreeWidget::topLevelItem(iItem); if (pItem->type() != qtractorConnect::ClientItem) continue; qtractorClientListItem *pClientItem = static_cast (pItem); if (pClientItem && pClientItem->clientName() == sClientName) return pClientItem; } return nullptr; } // Client:port finder. qtractorPortListItem *qtractorClientListView::findClientPortItem ( const QString& sClientPort ) { qtractorPortListItem *pPortItem = nullptr; const int iColon = sClientPort.indexOf(':'); if (iColon >= 0) { qtractorClientListItem *pClientItem = findClientItem(sClientPort.left(iColon)); if (pClientItem) { pPortItem = pClientItem->findPortItem( sClientPort.right(sClientPort.length() - iColon - 1)); } } return pPortItem; } // Main controller accessors. void qtractorClientListView::setBinding ( qtractorConnect *pConnect ) { m_pConnect = pConnect; } qtractorConnect *qtractorClientListView::binding (void) const { return m_pConnect; } // Readable flag client accessors. void qtractorClientListView::setReadable ( bool bReadable ) { m_bReadable = bReadable; QString sText; if (m_bReadable) sText = tr("Readable Clients / Output Ports"); else sText = tr("Writable Clients / Input Ports"); QTreeWidget::headerItem()->setText(0, sText); QTreeWidget::sortItems(0, Qt::AscendingOrder); QTreeWidget::setToolTip(sText); } bool qtractorClientListView::isReadable (void) const { return m_bReadable; } // Client name filter helpers. void qtractorClientListView::setClientName ( const QString& sClientName ) { m_rxClientName.setPattern(sClientName); if (sClientName.isEmpty()) m_rxPortName.setPattern(QString()); } QString qtractorClientListView::clientName (void) const { return m_rxClientName.pattern(); } // Client name filter helpers. void qtractorClientListView::setPortName ( const QString& sPortName ) { m_rxPortName.setPattern(sPortName); } QString qtractorClientListView::portName (void) const { return m_rxPortName.pattern(); } // Maintained current client name list. const QStringList& qtractorClientListView::clientNames (void) const { return m_clientNames; } // Override clear method. void qtractorClientListView::clear (void) { m_pHiliteItem = nullptr; m_clientNames.clear(); const bool bBlockSignals = QTreeWidget::blockSignals(true); QTreeWidget::clear(); QTreeWidget::blockSignals(bBlockSignals); } // Client filter regular expression; // take the chance to add to maintained client name list. bool qtractorClientListView::isClientName ( const QString& sClientName ) { if (m_clientNames.indexOf(sClientName) < 0) m_clientNames.append(sClientName); return (m_rxClientName.pattern().isEmpty() || m_rxClientName.match(sClientName).hasMatch()); } // Port filter regular expression; bool qtractorClientListView::isPortName ( const QString& sPortName ) { return (m_rxClientName.pattern().isEmpty() || m_rxPortName.pattern().isEmpty() || m_rxPortName.match(sPortName).hasMatch()); } // Whether items are all open (expanded) or closed (collapsed). void qtractorClientListView::setOpenAll ( bool bOpen ) { // For each client item... const int iItemCount = QTreeWidget::topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) { QTreeWidgetItem *pItem = QTreeWidget::topLevelItem(iItem); if (pItem->type() != qtractorConnect::ClientItem) continue; qtractorClientListItem *pClientItem = static_cast (pItem); if (pClientItem == nullptr) continue; // For each port item... const int iChildCount = pClientItem->childCount(); for (int iChild = 0; iChild < iChildCount; ++iChild) { QTreeWidgetItem *pChildItem = pClientItem->child(iChild); if (pChildItem->type() != qtractorConnect::PortItem) continue; qtractorPortListItem *pPortItem = static_cast (pChildItem); if (pPortItem) { pClientItem->setOpen(bOpen); QListIterator iter(pPortItem->connects()); while (iter.hasNext()) { qtractorPortListItem *pConnectItem = iter.next(); pConnectItem->clientItem()->setOpen(bOpen); } } } } } // Client:port set housekeeping marker. void qtractorClientListView::markClientPorts ( int iMark ) { m_clientNames.clear(); m_pHiliteItem = nullptr; const int iItemCount = QTreeWidget::topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) { QTreeWidgetItem *pItem = QTreeWidget::topLevelItem(iItem); if (pItem->type() != qtractorConnect::ClientItem) continue; qtractorClientListItem *pClientItem = static_cast (pItem); if (pClientItem) pClientItem->markClientPorts(iMark); } } void qtractorClientListView::cleanClientPorts ( int iMark ) { int iItemCount = QTreeWidget::topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) { QTreeWidgetItem *pItem = QTreeWidget::topLevelItem(iItem); if (pItem->type() != qtractorConnect::ClientItem) continue; qtractorClientListItem *pClientItem = static_cast (pItem); if (pClientItem) { if (pClientItem->clientMark() == iMark) { delete pClientItem; --iItemCount; --iItem; } else { pClientItem->cleanClientPorts(iMark); } } } } // Client:port hilite update stabilization. void qtractorClientListView::hiliteClientPorts (void) { QTreeWidgetItem *pCurrentItem = QTreeWidget::currentItem(); // Dehilite the previous selected items. if (m_pHiliteItem && pCurrentItem != m_pHiliteItem) { if (m_pHiliteItem->type() == qtractorConnect::ClientItem) { const int iChildCount = m_pHiliteItem->childCount(); for (int iChild = 0; iChild < iChildCount; ++iChild) { QTreeWidgetItem *pChild = m_pHiliteItem->child(iChild); if (pChild->type() != qtractorConnect::PortItem) continue; qtractorPortListItem *pPortItem = static_cast (pChild); if (pPortItem) { QListIterator iter(pPortItem->connects()); while (iter.hasNext()) iter.next()->setHilite(false); } } } else { qtractorPortListItem *pPortItem = static_cast (m_pHiliteItem); if (pPortItem) { QListIterator iter(pPortItem->connects()); while (iter.hasNext()) iter.next()->setHilite(false); } } } // Hilite the now current selected items. if (pCurrentItem && pCurrentItem->type() == qtractorConnect::ClientItem) { const int iChildCount = pCurrentItem->childCount(); for (int iChild = 0; iChild < iChildCount; ++iChild) { QTreeWidgetItem *pChild = pCurrentItem->child(iChild); if (pChild->type() != qtractorConnect::PortItem) continue; qtractorPortListItem *pPortItem = static_cast (pChild); if (pPortItem) { QListIterator iter(pPortItem->connects()); while (iter.hasNext()) iter.next()->setHilite(true); } } } else { qtractorPortListItem *pPortItem = static_cast (pCurrentItem); if (pPortItem) { QListIterator iter(pPortItem->connects()); while (iter.hasNext()) iter.next()->setHilite(true); } } // Do remember this one, ever. m_pHiliteItem = pCurrentItem; } // Auto-open timeout method. void qtractorClientListView::setAutoOpenTimeout ( int iAutoOpenTimeout ) { m_iAutoOpenTimeout = iAutoOpenTimeout; if (m_pAutoOpenTimer) delete m_pAutoOpenTimer; m_pAutoOpenTimer = nullptr; if (m_iAutoOpenTimeout > 0) { m_pAutoOpenTimer = new QTimer(this); QObject::connect(m_pAutoOpenTimer, SIGNAL(timeout()), SLOT(timeoutSlot())); } } // Auto-open timeout accessor. int qtractorClientListView::autoOpenTimeout (void) const { return m_iAutoOpenTimeout; } // Auto-open timer slot. void qtractorClientListView::timeoutSlot (void) { if (m_pAutoOpenTimer) { m_pAutoOpenTimer->stop(); if (m_pDropItem && m_pDropItem->type() == qtractorConnect::ClientItem) { qtractorClientListItem *pClientItem = static_cast (m_pDropItem); if (pClientItem && !pClientItem->isOpen()) pClientItem->setOpen(true); } } } // Trap for help/tool-tip events. bool qtractorClientListView::eventFilter ( QObject *pObject, QEvent *pEvent ) { QWidget *pViewport = QTreeWidget::viewport(); if (static_cast (pObject) == pViewport && pEvent->type() == QEvent::ToolTip) { QHelpEvent *pHelpEvent = static_cast (pEvent); if (pHelpEvent) { QTreeWidgetItem *pItem = QTreeWidget::itemAt(pHelpEvent->pos()); if (pItem && pItem->type() == qtractorConnect::ClientItem) { qtractorClientListItem *pClientItem = static_cast (pItem); if (pClientItem) { QToolTip::showText(pHelpEvent->globalPos(), pClientItem->clientName(), pViewport); return true; } } else if (pItem && pItem->type() == qtractorConnect::PortItem) { qtractorPortListItem *pPortItem = static_cast (pItem); if (pPortItem) { QToolTip::showText(pHelpEvent->globalPos(), pPortItem->portName(), pViewport); return true; } } } } // Not handled here. return QTreeWidget::eventFilter(pObject, pEvent); } // Drag-n-drop stuff. QTreeWidgetItem *qtractorClientListView::dragDropItem ( const QPoint& pos ) { QTreeWidgetItem *pItem = QTreeWidget::itemAt(pos); if (pItem) { if (m_pDropItem != pItem) { QTreeWidget::setCurrentItem(pItem); m_pDropItem = pItem; if (m_pAutoOpenTimer) m_pAutoOpenTimer->start(m_iAutoOpenTimeout); qtractorConnect *pConnect = binding(); if (pConnect == nullptr || !pConnect->canConnectSelected()) pItem = nullptr; } } else { m_pDropItem = nullptr; if (m_pAutoOpenTimer) m_pAutoOpenTimer->stop(); } return pItem; } void qtractorClientListView::dragEnterEvent ( QDragEnterEvent *pDragEnterEvent ) { if (pDragEnterEvent->source() != this && pDragEnterEvent->mimeData()->hasText() && #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) dragDropItem(pDragEnterEvent->position().toPoint())) { #else dragDropItem(pDragEnterEvent->pos())) { #endif pDragEnterEvent->accept(); } else { pDragEnterEvent->ignore(); } } void qtractorClientListView::dragMoveEvent ( QDragMoveEvent *pDragMoveEvent ) { if (pDragMoveEvent->source() != this && pDragMoveEvent->mimeData()->hasText() && #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) dragDropItem(pDragMoveEvent->position().toPoint())) { #else dragDropItem(pDragMoveEvent->pos())) { #endif pDragMoveEvent->accept(); } else { pDragMoveEvent->ignore(); } } void qtractorClientListView::dragLeaveEvent ( QDragLeaveEvent * ) { m_pDropItem = nullptr; if (m_pAutoOpenTimer) m_pAutoOpenTimer->stop(); } void qtractorClientListView::dropEvent( QDropEvent *pDropEvent ) { if (pDropEvent->source() != this && pDropEvent->mimeData()->hasText() && #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) dragDropItem(pDropEvent->position().toPoint())) { #else dragDropItem(pDropEvent->pos())) { #endif const QString sText = pDropEvent->mimeData()->text(); if (!sText.isEmpty() && m_pConnect) m_pConnect->connectSelected(); } dragLeaveEvent(0); } // Handle mouse events for drag-and-drop stuff. void qtractorClientListView::mousePressEvent ( QMouseEvent *pMouseEvent ) { QTreeWidget::mousePressEvent(pMouseEvent); if (pMouseEvent->button() == Qt::LeftButton) { m_posDrag = pMouseEvent->pos(); m_pDragItem = QTreeWidget::itemAt(m_posDrag); } } void qtractorClientListView::mouseMoveEvent ( QMouseEvent *pMouseEvent ) { QTreeWidget::mouseMoveEvent(pMouseEvent); if ((pMouseEvent->buttons() & Qt::LeftButton) && m_pDragItem && ((pMouseEvent->pos() - m_posDrag).manhattanLength() >= QApplication::startDragDistance())) { // We'll start dragging something alright... QMimeData *pMimeData = new QMimeData(); pMimeData->setText(m_pDragItem->text(0)); QDrag *pDrag = new QDrag(this); pDrag->setMimeData(pMimeData); pDrag->setPixmap(m_pDragItem->icon(0).pixmap(16)); pDrag->setHotSpot(QPoint(-4, -12)); pDrag->exec(Qt::LinkAction); // We've dragged and maybe dropped it by now... m_pDragItem = nullptr; } } // Context menu request event handler. void qtractorClientListView::contextMenuEvent ( QContextMenuEvent *pContextMenuEvent ) { if (m_pConnect) m_pConnect->contextMenu(pContextMenuEvent->globalPos()); } // Natural decimal sorting comparator. bool qtractorClientListView::lessThan ( const QTreeWidgetItem& item1, const QTreeWidgetItem& item2, int col ) { const QString& s1 = item1.text(col); const QString& s2 = item2.text(col); const int n1 = s1.length(); const int n2 = s2.length(); int i1, i2; for (i1 = i2 = 0; i1 < n1 && i2 < n2; ++i1, ++i2) { // skip (white)spaces... while (s1.at(i1).isSpace()) ++i1; while (s2.at(i2).isSpace()) ++i2; // Normalize (to uppercase) the next characters... QChar c1 = s1.at(i1).toUpper(); QChar c2 = s2.at(i2).toUpper(); if (c1.isDigit() && c2.isDigit()) { // Find the whole length numbers... int j1 = i1++; while (i1 < n1 && s1.at(i1).isDigit()) ++i1; int j2 = i2++; while (i2 < n2 && s2.at(i2).isDigit()) ++i2; // Compare as natural decimal-numbers... j1 = s1.mid(j1, i1 - j1).toInt(); j2 = s2.mid(j2, i2 - j2).toInt(); if (j1 != j2) return (j1 < j2); // Never go out of bounds... if (i1 >= n1 || i2 >= n2) break; // Go on with this next char... c1 = s1.at(i1).toUpper(); c2 = s2.at(i2).toUpper(); } // Compare this char... if (c1 != c2) return (c1 < c2); } // Probable exact match. return false; } // Do proper contents refresh/update. void qtractorClientListView::refresh (void) { QHeaderView *pHeader = QTreeWidget::header(); QTreeWidget::sortItems( pHeader->sortIndicatorSection(), pHeader->sortIndicatorOrder()); } //---------------------------------------------------------------------- // qtractorConnectorView -- Connector view widget. // // Constructor. qtractorConnectorView::qtractorConnectorView ( QWidget *pParent ) : QWidget(pParent) { m_pConnect = nullptr; QWidget::setMinimumWidth(20); // QWidget::setMaximumWidth(120); QWidget::setSizePolicy( QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); } // Default destructor. qtractorConnectorView::~qtractorConnectorView (void) { } // Main controller accessors. void qtractorConnectorView::setBinding ( qtractorConnect *pConnect ) { m_pConnect = pConnect; } qtractorConnect *qtractorConnectorView::binding (void) const { return m_pConnect; } // Legal client/port item position helper. int qtractorConnectorView::itemY ( QTreeWidgetItem *pItem ) const { QRect rect; QTreeWidget *pList = pItem->treeWidget(); QTreeWidgetItem *pParent = pItem->parent(); qtractorClientListItem *pClientItem = nullptr; if (pParent && pParent->type() == qtractorConnect::ClientItem) pClientItem = static_cast (pParent); if (pClientItem && !pClientItem->isOpen()) { rect = pList->visualItemRect(pClientItem); } else { rect = pList->visualItemRect(pItem); } return rect.top() + rect.height() / 2; } // Draw visible port connection relation lines void qtractorConnectorView::drawConnectionLine ( QPainter *pPainter, int x1, int y1, int x2, int y2, int h1, int h2, const QPen& pen ) { // Set apropriate pen... pPainter->setPen(pen); // Account for list view headers. y1 += h1; y2 += h2; // Invisible output ports don't get a connecting dot. if (y1 > h1) pPainter->drawLine(x1, y1, x1 + 4, y1); // Setup control points QPolygon spline(4); int cp = int(float(x2 - x1 - 8) * 0.4f); spline.putPoints(0, 4, x1 + 4, y1, x1 + 4 + cp, y1, x2 - 4 - cp, y2, x2 - 4, y2); // The connection line, it self. QPainterPath path; path.moveTo(spline.at(0)); path.cubicTo(spline.at(1), spline.at(2), spline.at(3)); pPainter->strokePath(path, pen); // Invisible input ports don't get a connecting dot. if (y2 > h2) pPainter->drawLine(x2 - 4, y2, x2, y2); } // Draw visible port connection relation arrows. void qtractorConnectorView::paintEvent ( QPaintEvent * ) { if (m_pConnect == nullptr) return; if (m_pConnect->OListView() == nullptr || m_pConnect->IListView() == nullptr) return; qtractorClientListView *pOListView = m_pConnect->OListView(); qtractorClientListView *pIListView = m_pConnect->IListView(); const int yc = QWidget::pos().y(); const int yo = pOListView->pos().y(); const int yi = pIListView->pos().y(); QPainter painter(this); int x1, y1, h1; int x2, y2, h2; int rgb[3] = { 0x33, 0x66, 0x99 }; // Draw all lines anti-aliased... painter.setRenderHint(QPainter::Antialiasing); // Inline adaptive to darker background themes... if (QWidget::palette().window().color().value() < 0x7f) for (int i = 0; i < 3; ++i) rgb[i] += 0x33; // Almost constants. x1 = 0; x2 = QWidget::width(); h1 = (pOListView->header())->sizeHint().height(); h2 = (pIListView->header())->sizeHint().height(); // For each output 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; qtractorClientListItem *pOClient = static_cast (pItem); if (pOClient == nullptr) continue; // Set new connector color. const QString& sOClientName = pOClient->clientName(); int k = m_colorMap.value(sOClientName, -1); if (k < 0) { k = m_colorMap.size() + 1; m_colorMap.insert(sOClientName, k); } QPen pen(QColor(rgb[k % 3], rgb[(k / 3) % 3], rgb[(k / 9) % 3])); // 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; qtractorPortListItem *pOPort = static_cast (pChild); if (pOPort) { // Set proposed line width... const int w1 = (pOPort->isHilite() ? 2 : 1); // Get starting connector arrow coordinates. y1 = itemY(pOPort) + (yo - yc); // Get port connections... QListIterator iter(pOPort->connects()); while (iter.hasNext()) { qtractorPortListItem *pIPort = iter.next(); // Obviously, should be a connection // from pOPort to pIPort items: y2 = itemY(pIPort) + (yi - yc); pen.setWidth(pIPort->isHilite() ? 2 : w1); drawConnectionLine(&painter, x1, y1, x2, y2, h1, h2, pen); } } } } } // Context menu request event handler. void qtractorConnectorView::contextMenuEvent ( QContextMenuEvent *pContextMenuEvent ) { if (m_pConnect) m_pConnect->contextMenu(pContextMenuEvent->globalPos()); } // Widget event slots... void qtractorConnectorView::contentsChanged (void) { QWidget::update(); } //---------------------------------------------------------------------------- // qtractorConnect -- Connections controller. // // Constructor. qtractorConnect::qtractorConnect ( qtractorClientListView *pOListView, qtractorClientListView *pIListView, qtractorConnectorView *pConnectorView ) { m_pOListView = pOListView; m_pIListView = pIListView; m_pConnectorView = pConnectorView; m_pOListView->setBinding(this); m_pOListView->setReadable(true); m_pIListView->setBinding(this); m_pIListView->setReadable(false); m_pConnectorView->setBinding(this); QObject::connect(m_pOListView, SIGNAL(itemExpanded(QTreeWidgetItem *)), m_pConnectorView, SLOT(contentsChanged())); QObject::connect(m_pOListView, SIGNAL(itemCollapsed(QTreeWidgetItem *)), m_pConnectorView, SLOT(contentsChanged())); QObject::connect(m_pOListView->verticalScrollBar(), SIGNAL(valueChanged(int)), m_pConnectorView, SLOT(contentsChanged())); QObject::connect(m_pOListView->header(), SIGNAL(sectionClicked(int)), m_pConnectorView, SLOT(contentsChanged())); QObject::connect(m_pIListView, SIGNAL(itemExpanded(QTreeWidgetItem *)), m_pConnectorView, SLOT(contentsChanged())); QObject::connect(m_pIListView, SIGNAL(itemCollapsed(QTreeWidgetItem *)), m_pConnectorView, SLOT(contentsChanged())); QObject::connect(m_pIListView->verticalScrollBar(), SIGNAL(valueChanged(int)), m_pConnectorView, SLOT(contentsChanged())); QObject::connect(m_pIListView->header(), SIGNAL(sectionClicked(int)), m_pConnectorView, SLOT(contentsChanged())); } // Default destructor. qtractorConnect::~qtractorConnect (void) { // Force end of works here. m_pOListView->setBinding(nullptr); m_pIListView->setBinding(nullptr); m_pConnectorView->setBinding(nullptr); } // Connection primitive. bool qtractorConnect::connectPortsEx ( qtractorPortListItem *pOPort, qtractorPortListItem *pIPort ) { if (pOPort->findConnect(pIPort)) return false; if (!connectPorts(pOPort, pIPort)) return false; pOPort->addConnect(pIPort); pIPort->addConnect(pOPort); return true; } // Disconnection primitive. bool qtractorConnect::disconnectPortsEx ( qtractorPortListItem *pOPort, qtractorPortListItem *pIPort ) { if (pOPort->findConnect(pIPort) == nullptr) return false; if (!disconnectPorts(pOPort, pIPort)) return false; pOPort->removeConnect(pIPort); pIPort->removeConnect(pOPort); return true; } // Test if selected ports are connectable. bool qtractorConnect::canConnectSelected (void) { // Take this opportunity to highlight any current selections. m_pOListView->hiliteClientPorts(); m_pIListView->hiliteClientPorts(); m_pConnectorView->update(); // Now with our predicate work... const QList oitems = m_pOListView->selectedItems(); const QList iitems = m_pIListView->selectedItems(); if (oitems.isEmpty() || iitems.isEmpty()) return false; QListIterator oiter(oitems); QListIterator iiter(iitems); const int iNumItems = qMax(oitems.count(), iitems.count()); for (int i = 0; i < iNumItems; ++i) { if (!oiter.hasNext()) oiter.toFront(); if (!iiter.hasNext()) iiter.toFront(); QTreeWidgetItem *pOItem = oiter.next(); QTreeWidgetItem *pIItem = iiter.next(); if (pOItem->type() == qtractorConnect::ClientItem) { qtractorClientListItem *pOClient = static_cast (pOItem); if (pOClient == nullptr) return false; if (pIItem->type() == qtractorConnect::ClientItem) { // Each-to-each connections... qtractorClientListItem *pIClient = static_cast (pIItem); if (pIClient == nullptr) return false; const int iOCount = pOClient->childCount(); const int iICount = pIClient->childCount(); int iOItem = 0; int iIItem = 0; while (iIItem < iICount && iOItem < iOCount) { pOItem = pOClient->child(iOItem); pIItem = pIClient->child(iIItem); if (pOItem && pOItem->type() == qtractorConnect::PortItem && pIItem && pIItem->type() == qtractorConnect::PortItem) { qtractorPortListItem *pOPort = static_cast (pOItem); qtractorPortListItem *pIPort = static_cast (pIItem); if (pOPort && pIPort && pOPort->findConnect(pIPort) == nullptr) return true; } ++iOItem; ++iIItem; } } else { // Many(all)-to-one connection... qtractorPortListItem *pIPort = static_cast (pIItem); if (pIPort == nullptr) return false; const int iOCount = pOClient->childCount(); int iOItem = 0; while (iOItem < iOCount) { pOItem = pOClient->child(iOItem); if (pOItem && pOItem->type() == qtractorConnect::PortItem) { qtractorPortListItem *pOPort = static_cast (pOItem); if (pOPort && pOPort->findConnect(pIPort) == nullptr) return true; } ++iOItem; } } } else { qtractorPortListItem *pOPort = static_cast (pOItem); if (pOPort == nullptr) return false; if (pIItem->type() == qtractorConnect::ClientItem) { // One-to-many(all) connection... qtractorClientListItem *pIClient = static_cast (pIItem); if (pIClient == nullptr) return false; const int iICount = pIClient->childCount(); int iIItem = 0; while (iIItem < iICount) { pIItem = pIClient->child(iIItem); if (pIItem && pIItem->type() == qtractorConnect::PortItem) { qtractorPortListItem *pIPort = static_cast (pIItem); if (pIPort && pOPort->findConnect(pIPort) == nullptr) return true; } ++iIItem; } } else { // One-to-one connection... qtractorPortListItem *pIPort = static_cast (pIItem); if (pIPort && pOPort->findConnect(pIPort) == nullptr) return true; } } } return false; } // Connect current selected ports. bool qtractorConnect::connectSelected (void) { const bool bResult = connectSelectedEx(); m_pConnectorView->update(); if (bResult) emit connectChanged(); return bResult; } bool qtractorConnect::connectSelectedEx (void) { // Now with our predicate work... const QList oitems = m_pOListView->selectedItems(); const QList iitems = m_pIListView->selectedItems(); if (oitems.isEmpty() || iitems.isEmpty()) return false; QListIterator oiter(oitems); QListIterator iiter(iitems); const int iNumItems = qMax(oitems.count(), iitems.count()); for (int i = 0; i < iNumItems; ++i) { if (!oiter.hasNext()) oiter.toFront(); if (!iiter.hasNext()) iiter.toFront(); QTreeWidgetItem *pOItem = oiter.next(); QTreeWidgetItem *pIItem = iiter.next(); if (pOItem->type() == qtractorConnect::ClientItem) { qtractorClientListItem *pOClient = static_cast (pOItem); if (pOClient == nullptr) return false; if (pIItem->type() == qtractorConnect::ClientItem) { // Each-to-each connections... qtractorClientListItem *pIClient = static_cast (pIItem); if (pIClient == nullptr) return false; const int iOCount = pOClient->childCount(); const int iICount = pIClient->childCount(); int iOItem = 0; int iIItem = 0; while (iIItem < iICount && iOItem < iOCount) { pOItem = pOClient->child(iOItem); pIItem = pIClient->child(iIItem); if (pOItem && pOItem->type() == qtractorConnect::PortItem && pIItem && pIItem->type() == qtractorConnect::PortItem) { qtractorPortListItem *pOPort = static_cast (pOItem); qtractorPortListItem *pIPort = static_cast (pIItem); connectPortsEx(pOPort, pIPort); } ++iOItem; ++iIItem; } } else { // Many(all)-to-one connection... qtractorPortListItem *pIPort = static_cast (pIItem); if (pIPort == nullptr) return false; const int iOCount = pOClient->childCount(); int iOItem = 0; while (iOItem < iOCount) { pOItem = pOClient->child(iOItem); if (pOItem && pOItem->type() == qtractorConnect::PortItem) { qtractorPortListItem *pOPort = static_cast (pOItem); connectPortsEx(pOPort, pIPort); } ++iOItem; } } } else { qtractorPortListItem *pOPort = static_cast (pOItem); if (pOPort == nullptr) return false; if (pIItem->type() == qtractorConnect::ClientItem) { // One-to-many(all) connection... qtractorClientListItem *pIClient = static_cast (pIItem); if (pIClient == nullptr) return false; const int iICount = pIClient->childCount(); int iIItem = 0; while (iIItem < iICount) { pIItem = pIClient->child(iIItem); if (pIItem && pIItem->type() == qtractorConnect::PortItem) { qtractorPortListItem *pIPort = static_cast (pIItem); connectPortsEx(pOPort, pIPort); } ++iIItem; } } else { // One-to-one connection... qtractorPortListItem *pIPort = static_cast (pIItem); connectPortsEx(pOPort, pIPort); } } } return true; } // Test if selected ports are disconnectable. bool qtractorConnect::canDisconnectSelected (void) { // Now with our predicate work... const QList oitems = m_pOListView->selectedItems(); const QList iitems = m_pIListView->selectedItems(); if (oitems.isEmpty() || iitems.isEmpty()) return false; QListIterator oiter(oitems); QListIterator iiter(iitems); const int iNumItems = qMax(oitems.count(), iitems.count()); for (int i = 0; i < iNumItems; ++i) { if (!oiter.hasNext()) oiter.toFront(); if (!iiter.hasNext()) iiter.toFront(); QTreeWidgetItem *pOItem = oiter.next(); QTreeWidgetItem *pIItem = iiter.next(); if (pOItem->type() == qtractorConnect::ClientItem) { qtractorClientListItem *pOClient = static_cast (pOItem); if (pOClient == nullptr) return false; if (pIItem->type() == qtractorConnect::ClientItem) { // Each-to-each connections... qtractorClientListItem *pIClient = static_cast (pIItem); if (pIClient == nullptr) return false; const int iOCount = pOClient->childCount(); const int iICount = pIClient->childCount(); int iOItem = 0; int iIItem = 0; while (iIItem < iICount && iOItem < iOCount) { pOItem = pOClient->child(iOItem); pIItem = pIClient->child(iIItem); if (pOItem && pOItem->type() == qtractorConnect::PortItem && pIItem && pIItem->type() == qtractorConnect::PortItem) { qtractorPortListItem *pOPort = static_cast (pOItem); qtractorPortListItem *pIPort = static_cast (pIItem); if (pOPort && pIPort && pOPort->findConnect(pIPort)) return true; } ++iOItem; ++iIItem; } } else { // Many(all)-to-one connection... qtractorPortListItem *pIPort = static_cast (pIItem); if (pIPort == nullptr) return false; const int iOCount = pOClient->childCount(); int iOItem = 0; while (iOItem < iOCount) { pOItem = pOClient->child(iOItem); if (pOItem && pOItem->type() == qtractorConnect::PortItem) { qtractorPortListItem *pOPort = static_cast (pOItem); if (pOPort && pOPort->findConnect(pIPort)) return true; } ++iOItem; } } } else { qtractorPortListItem *pOPort = static_cast (pOItem); if (pOPort == nullptr) return false; if (pIItem->type() == qtractorConnect::ClientItem) { // One-to-many(all) connection... qtractorClientListItem *pIClient = static_cast (pIItem); if (pIClient == nullptr) return false; const int iICount = pIClient->childCount(); int iIItem = 0; while (iIItem < iICount) { pIItem = pIClient->child(iIItem); if (pIItem && pIItem->type() == qtractorConnect::PortItem) { qtractorPortListItem *pIPort = static_cast (pIItem); if (pIPort && pOPort->findConnect(pIPort)) return true; } ++iIItem; } } else { // One-to-one connection... qtractorPortListItem *pIPort = static_cast (pIItem); if (pIPort && pOPort->findConnect(pIPort)) return true; } } } return false; } // Disconnect current selected ports. bool qtractorConnect::disconnectSelected (void) { const bool bResult = disconnectSelectedEx(); m_pConnectorView->update(); if (bResult) emit connectChanged(); return bResult; } bool qtractorConnect::disconnectSelectedEx (void) { // Now with our predicate work... const QList oitems = m_pOListView->selectedItems(); const QList iitems = m_pIListView->selectedItems(); if (oitems.isEmpty() || iitems.isEmpty()) return false; QListIterator oiter(oitems); QListIterator iiter(iitems); const int iNumItems = qMax(oitems.count(), iitems.count()); for (int i = 0; i < iNumItems; ++i) { if (!oiter.hasNext()) oiter.toFront(); if (!iiter.hasNext()) iiter.toFront(); QTreeWidgetItem *pOItem = oiter.next(); QTreeWidgetItem *pIItem = iiter.next(); if (pOItem->type() == qtractorConnect::ClientItem) { qtractorClientListItem *pOClient = static_cast (pOItem); if (pOClient == nullptr) return false; if (pIItem->type() == qtractorConnect::ClientItem) { // Each-to-each connections... qtractorClientListItem *pIClient = static_cast (pIItem); if (pIClient == nullptr) return false; const int iOCount = pOClient->childCount(); const int iICount = pIClient->childCount(); int iOItem = 0; int iIItem = 0; while (iIItem < iICount && iOItem < iOCount) { pOItem = pOClient->child(iOItem); pIItem = pIClient->child(iIItem); if (pOItem && pOItem->type() == qtractorConnect::PortItem && pIItem && pIItem->type() == qtractorConnect::PortItem) { qtractorPortListItem *pOPort = static_cast (pOItem); qtractorPortListItem *pIPort = static_cast (pIItem); disconnectPortsEx(pOPort, pIPort); } ++iOItem; ++iIItem; } } else { // Many(all)-to-one connection... qtractorPortListItem *pIPort = static_cast (pIItem); if (pIPort == nullptr) return false; const int iOCount = pOClient->childCount(); int iOItem = 0; while (iOItem < iOCount) { pOItem = pOClient->child(iOItem); if (pOItem && pOItem->type() == qtractorConnect::PortItem) { qtractorPortListItem *pOPort = static_cast (pOItem); disconnectPortsEx(pOPort, pIPort); } ++iOItem; } } } else { qtractorPortListItem *pOPort = static_cast (pOItem); if (pOPort == nullptr) return false; if (pIItem->type() == qtractorConnect::ClientItem) { // One-to-many(all) connection... qtractorClientListItem *pIClient = static_cast (pIItem); if (pIClient == nullptr) return false; const int iICount = pIClient->childCount(); int iIItem = 0; while (iIItem < iICount) { pIItem = pIClient->child(iIItem); if (pIItem && pIItem->type() == qtractorConnect::PortItem) { qtractorPortListItem *pIPort = static_cast (pIItem); disconnectPortsEx(pOPort, pIPort); } ++iIItem; } } else { // One-to-one connection... qtractorPortListItem *pIPort = static_cast (pIItem); disconnectPortsEx(pOPort, pIPort); } } } return true; } // Test if any port is disconnectable. bool qtractorConnect::canDisconnectAll (void) { // For each output client item... const int iItemCount = m_pOListView->topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) { QTreeWidgetItem *pItem = m_pOListView->topLevelItem(iItem); if (pItem->type() != qtractorConnect::ClientItem) continue; qtractorClientListItem *pOClient = static_cast (pItem); if (pOClient == nullptr) continue; // For each output 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; qtractorPortListItem *pOPort = static_cast (pChild); if (pOPort && pOPort->connects().count() > 0) return true; } } return false; } // Disconnect all ports. bool qtractorConnect::disconnectAll (void) { const bool bResult = disconnectAllEx(); m_pConnectorView->update(); if (bResult) emit connectChanged(); return bResult; } bool qtractorConnect::disconnectAllEx (void) { // For each output client item... const int iItemCount = m_pOListView->topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) { QTreeWidgetItem *pItem = m_pOListView->topLevelItem(iItem); if (pItem->type() != qtractorConnect::ClientItem) continue; qtractorClientListItem *pOClient = static_cast (pItem); if (pOClient == nullptr) continue; // For each output 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; qtractorPortListItem *pOPort = static_cast (pChild); if (pOPort) { QList connects = pOPort->connects(); QListIterator iter(connects); while (iter.hasNext()) { qtractorPortListItem *pIPort = iter.next(); disconnectPortsEx(pOPort, pIPort); } } } } return true; } // Complete/incremental contents rebuilder; // check dirty status if incremental. void qtractorConnect::updateContents ( bool bClear ) { int iDirtyCount = 0; // Do we do a complete rebuild? if (bClear) { m_pOListView->clear(); m_pIListView->clear(); } // Add (newer) client:ports and respective connections... if (m_pOListView->updateClientPorts() > 0) { m_pOListView->refresh(); ++iDirtyCount; } if (m_pIListView->updateClientPorts() > 0) { m_pIListView->refresh(); ++iDirtyCount; } updateConnections(); m_pConnectorView->update(); if (!bClear && iDirtyCount > 0) emit contentsChanged(); } // Incremental contents rebuilder; // check dirty status. void qtractorConnect::refresh (void) { updateContents(false); } // Context menu helper. void qtractorConnect::contextMenu ( const QPoint& gpos ) { QMenu menu(m_pConnectorView); QAction *pAction; pAction = menu.addAction( QIcon::fromTheme("formConnect"), tr("Connect"), this, SLOT(connectSelected())); pAction->setEnabled(canConnectSelected()); pAction = menu.addAction( QIcon::fromTheme("formDisconnect"), tr("Disconnect"), this, SLOT(disconnectSelected())); pAction->setEnabled(canDisconnectSelected()); pAction = menu.addAction( QIcon::fromTheme("formDisconnectAll"), tr("Disconnect All"), this, SLOT(disconnectAll())); pAction->setEnabled(canDisconnectAll()); menu.addSeparator(); pAction = menu.addAction( QIcon::fromTheme("formRefresh"), tr("Refresh"), this, SLOT(refresh())); menu.exec(gpos); } // end of qtractorConnect.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMidiControlObserverForm.ui0000644000000000000000000000013215101070305022120 xustar0030 mtime=1761898693.078267626 30 atime=1761898693.078267626 30 ctime=1761898693.078267626 qtractor-1.5.9/src/qtractorMidiControlObserverForm.ui0000644000175000001440000001555215101070305022120 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. qtractorMidiControlObserverForm 0 0 360 180 MIDI Controller &Type: Qt::AlignRight|Qt::AlignVCenter ControlTypeComboBox MIDI event type Qt::Horizontal 8 8 Cha&nnel: Qt::AlignRight|Qt::AlignVCenter ChannelSpinBox MIDI channel false 1 16 Qt::Vertical QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset &Parameter: Qt::AlignRight|Qt::AlignVCenter ParamComboBox MIDI parameter &Logarithmic &Feedback In&vert &Hook L&atch Qt::Horizontal 20 20 Control input connections &Inputs Control output connections &Outputs Qt::Horizontal 8 8 ControlTypeComboBox ChannelSpinBox ParamComboBox LogarithmicCheckBox FeedbackCheckBox InvertCheckBox HookCheckBox LatchCheckBox InputsPushButton OutputsPushButton DialogButtonBox qtractor-1.5.9/src/PaxHeaders/qtractorMidiControlCommand.h0000644000000000000000000000013215101070305020675 xustar0030 mtime=1761898693.077267623 30 atime=1761898693.077267623 30 ctime=1761898693.077267623 qtractor-1.5.9/src/qtractorMidiControlCommand.h0000644000175000001440000000603015101070305020664 0ustar00rncbcusers// qtractorMidiControlCommand.h // /**************************************************************************** Copyright (C) 2010-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 __qtractorMidiControlCommand_h #define __qtractorMidiControlCommand_h #include "qtractorCommand.h" #include "qtractorMidiControl.h" //---------------------------------------------------------------------- // class qtractorMidiControlObserverCommand - declaration. // class qtractorMidiControlObserverCommand : public qtractorCommand { public: // Constructor. qtractorMidiControlObserverCommand(const QString& sName, qtractorMidiControlObserver *pMidiObserver, QWidget *pMidiObserverWidget = nullptr); protected: // Map/unmap observer methods. bool mapMidiObserver() const; bool unmapMidiObserver() const; private: // Instance variables. qtractorMidiControlObserver *m_pMidiObserver; QWidget *m_pMidiObserverWidget; }; //---------------------------------------------------------------------- // class qtractorMidiControlObserverMapCommand - declaration. // class qtractorMidiControlObserverMapCommand : public qtractorMidiControlObserverCommand { public: // Constructor. qtractorMidiControlObserverMapCommand( qtractorMidiControlObserver *pMidiObserver, QWidget *pMidiObserverWidget = nullptr) : qtractorMidiControlObserverCommand( QObject::tr("set controller"), pMidiObserver, pMidiObserverWidget) {} // MIDI control observer command methods. bool redo() { return mapMidiObserver(); } bool undo() { return unmapMidiObserver(); } }; //---------------------------------------------------------------------- // class qtractorMidiControlObserverUnmapCommand - declaration. // class qtractorMidiControlObserverUnmapCommand : public qtractorMidiControlObserverCommand { public: // Constructor. qtractorMidiControlObserverUnmapCommand( qtractorMidiControlObserver *pMidiObserver, QWidget *pMidiObserverWidget = nullptr) : qtractorMidiControlObserverCommand( QObject::tr("reset controller"), pMidiObserver, pMidiObserverWidget) {} // MIDI control observer command methods. bool redo() { return unmapMidiObserver(); } bool undo() { return mapMidiObserver(); } }; #endif // __qtractorMidiControlCommand_h // end of qtractorMidiControlCommand.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiEditEvent.h0000644000000000000000000000013215101070305017645 xustar0030 mtime=1761898693.079267629 30 atime=1761898693.079267629 30 ctime=1761898693.079267629 qtractor-1.5.9/src/qtractorMidiEditEvent.h0000644000175000001440000001033315101070305017635 0ustar00rncbcusers// qtractorMidiEditEvent.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 __qtractorMidiEditEvent_h #define __qtractorMidiEditEvent_h #include "qtractorScrollView.h" #include "qtractorMidiEvent.h" #include #include // Forward declarations. class qtractorMidiEditor; class qtractorMidiSequence; class QResizeEvent; class QMouseEvent; class QKeyEvent; class QToolButton; //---------------------------------------------------------------------------- // qtractorMidiEditEventScale -- MIDI event scale widget. class qtractorMidiEditEventScale : public QWidget { Q_OBJECT public: // Constructor. qtractorMidiEditEventScale(qtractorMidiEditor *pEditor, QWidget *pParent); // Default destructor. ~qtractorMidiEditEventScale(); protected: // Specific event handlers. void paintEvent(QPaintEvent *); private: // Local instance variables. qtractorMidiEditor *m_pEditor; // Running variables. int m_iLastY; }; //---------------------------------------------------------------------------- // qtractorMidiEditEvent -- MIDI sequence event view widget. class qtractorMidiEditEvent : public qtractorScrollView { Q_OBJECT public: // Constructor. qtractorMidiEditEvent(qtractorMidiEditor *pEditor, QWidget *pParent); // Destructor. ~qtractorMidiEditEvent(); // Rectangular contents update. void updateContents(const QRect& rect); // Overall contents update. void updateContents(); // Current event selection accessors. void setEventType(qtractorMidiEvent::EventType eventType); qtractorMidiEvent::EventType eventType() const; void setEventParam(unsigned short param); unsigned short eventParam() const; protected: // Virtual size hint. QSize sizeHint() const { return QSize(480, 120); } // Resize event handler. void resizeEvent(QResizeEvent *pResizeEvent); // Draw the track view events. void drawEvents(QPainter& painter, int dx, int dy, qtractorMidiSequence *pSeq, unsigned long t0, unsigned long iTickStart, unsigned long iTickEnd, unsigned long iTickEnd2, bool bDrumMode, const QColor& fore, const QColor& back, int alpha = 255); // Draw the time scale. void drawContents(QPainter *pPainter, const QRect& rect); // Focus lost event. void focusOutEvent(QFocusEvent *pFocusEvent); // Keyboard event handler. void keyPressEvent(QKeyEvent *pKeyEvent); // Handle item selection with mouse. void mousePressEvent(QMouseEvent *pMouseEvent); void mouseMoveEvent(QMouseEvent *pMouseEvent); void mouseReleaseEvent(QMouseEvent *pMouseEvent); // Handle zoom with mouse wheel. void wheelEvent(QWheelEvent *pWheelEvent); // Trap for help/tool-tip and leave events. bool eventFilter(QObject *pObject, QEvent *pEvent); protected slots: // To have timeline in h-sync with main track view. void contentsXMovingSlot(int cx, int cy); // (Re)create the time scale pixmap. void updatePixmap(int cx, int cy); private: // The logical parent binding. qtractorMidiEditor *m_pEditor; // Local zoom control widgets. QToolButton *m_pHzoomOut; QToolButton *m_pHzoomIn; QToolButton *m_pHzoomReset; // Local double-buffering pixmap. QPixmap m_pixmap; // Current selection holders. qtractorMidiEvent::EventType m_eventType; unsigned short m_eventParam; // Optional edge-shadow gradient brushes. QBrush m_gradLeft; QBrush m_gradRight; }; #endif // __qtractorMidiEditEvent_h // end of qtractorMidiEditEvent.h qtractor-1.5.9/src/PaxHeaders/qtractorSessionForm.cpp0000644000000000000000000000013215101070305017755 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.088267658 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorSessionForm.cpp0000644000175000001440000003334115101070305017751 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.9/src/PaxHeaders/qtractorAudioVorbisFile.h0000644000000000000000000000013215101070305020201 xustar0030 mtime=1761898693.064267582 30 atime=1761898693.064267582 30 ctime=1761898693.064267582 qtractor-1.5.9/src/qtractorAudioVorbisFile.h0000644000175000001440000000645515101070305020203 0ustar00rncbcusers// qtractorAudioVorbisFile.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 __qtractorAudioVorbisFile_h #define __qtractorAudioVorbisFile_h #include "qtractorAudioFile.h" #ifdef CONFIG_LIBVORBIS // libvorbis API. #include #include #endif //---------------------------------------------------------------------- // class qtractorAudioVorbisFile -- Buffered audio file declaration. // class qtractorAudioVorbisFile : public qtractorAudioFile { public: // Constructor. qtractorAudioVorbisFile(unsigned short iChannels = 0, unsigned int iSampleRate = 0, unsigned int iBufferSize = 0, int iQuality = 4); // Destructor. virtual ~qtractorAudioVorbisFile(); // 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; // Translate quality index into vorbis encoder specific. static int quality(int iQuality); protected: // Flush encoder buffers. void flush(bool fEos = false); private: int m_iMode; // open mode (Read only). FILE *m_pFile; // fopen file descriptor. unsigned short m_iChannels; // estimated channel count. unsigned int m_iSampleRate; // estimated sample rate; unsigned long m_iFrames; // estimated encoded frames; #ifdef CONFIG_LIBVORBIS // Common codec variable. vorbis_info *m_ovinfo; // libvorbisfile info struct. // Encoder specific variables. vorbis_comment m_ovcomment; // struct that stores all the user comments. vorbis_dsp_state m_ovdsp; // central working state for the encoder. vorbis_block m_ovblock; // local working space for the encoder. ogg_stream_state m_ovstate; // physical pages, logical stream of packets. // Decoder specific variables. OggVorbis_File m_ovfile; // libsvorbisfile descriptor. int m_ovsect; // libvorbisfile current section. #endif // CONFIG_LIBVORBIS unsigned int m_iBufferSize; // estimated buffer size. int m_iQuality; // encoding quality (write-only). }; #endif // __qtractorAudioVorbisFile_h // end of qtractorAudioVorbisFile.h qtractor-1.5.9/src/PaxHeaders/images0000644000000000000000000000013215101070305014412 xustar0030 mtime=1761898693.060267569 30 atime=1761898693.057007007 30 ctime=1761898693.060267569 qtractor-1.5.9/src/images/0000755000175000001440000000000015101070305014457 5ustar00rncbcusersqtractor-1.5.9/src/images/PaxHeaders/transportMetro.png0000644000000000000000000000013215101070305020240 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportMetro.png0000644000175000001440000000167015101070305020234 0ustar00rncbcusers‰PNG  IHDRÄ´l; pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<EIDAT8Õ•HœuÀ?Ï«§çyéòîšÎæœó¹›y:k5ÛZË~mV$ÈÂ?”´…IQ4hEПý  Å„ еJ¨AÆ ³EP šC#uN]¶­Ó»{ï}uzOÜ’óǦ´?úÂË÷yùò|Þçù¼|yDU¹˸)Ô•‚EÄ‘|qýg`‘ºÛàè7_Ïh®ûä‘‹HÖ²dU½îîlyv\;¾R=òÙU UIÂcûSç{ƒpð¼yîmNÏͼñgw…|…µß½ÃGÌÈäñæõrö×ú="òþª‚ž/ì/óÇc“¼õF§ t¬PÅ#¯Ý·óöLÓ†/ØD¢IÑfOÙ£að‡¡ñ•êm¥9‰é FÏEè›üûoyØ›õÓ·çhh¾ËùéG‡®NÙî†DIÏ^R…H±7ËUÝ´®2åöÇ®!àó#±‰îï>~ïKUåt}ÑA<Ú7 ‰.U½¬ª³Ë‚aßËÂåùöŒƒ‘á8#¿w]îO i•üÒÝÉÚ÷­-¥°þ¥‹À"ž¼ŒÌpëêŠ"¢&üöý ª_‡w_ nij8ßš ×¾@+ë›2 ¾]D)]¢â}í>…ÏNfså’ÅÅþ ­àö]–}YƇJÄ7©5K^IY®;·ƒwë"Ì‚ áãØyÿNSCMªž@¯BÓ1(«ÍÌiãÑ>`#¼~vCý¸VîV- ÿ¬ðÔ ÀHg-¨¸}ONÑÆ“±Y"ýIð:àɧg,ë¤&½§á¹6HLŒ÷õ3ÁQÄpT?®štÒœqÀíFQ Q3eɨ1˜©l’ ©]P wÑ@6ÙÅ[r¬¡ûŸ‘½ÖzºôU8Ëü&· & ¹k‘y–yïb^Óéö¾ ˜\Ž3=8®‘ŠbÍr’ªòß!pƒX“éþf©PUK¤®?z_‡f ˜M)˜Û—Šg“piN½£ªæ\7 GSÊ5¹Ìïz¹e«ª5ó¿›yÿ!"m®E¾n±IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackIconPiano1.png0000644000000000000000000000013215101070305020162 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackIconPiano1.png0000644000175000001440000003633215101070305020161 0ustar00rncbcusers‰PNG  IHDR€€Ã>aË IDATxÚí½ieÉuöÌ»½}«}í}îž}¦g@˜8aˤHÊd@@¤,Êá¶)Ú& ‰"@ÊAQY’„MH¦Ã‚aRF$íI¦§·™éî™®êÚ«Þ«zõÖ{ofúGæ½ï¾W¯º«g〮Œx3U]÷Ý%óä9ßùÎrÃq8Çá8‡ãpŽÃq8Çá8‡ãpŽÃq8Çá8‡ãpŽÃq8Çá8‡ãpŽÃq8Ç_Aß÷¤¾æL`NÕ¡¼w÷¢¾æA €¾>%&ƒÞå UñìôõWYhà示Üo1²€ê>“¨ |÷»Oõóõ 0ìÞÔ_¦Ðûxx§Û¶MA€r`3Æ ”‚RJŽŽ·9ç$„PæÜjŸ‰U@DdÛ6|ßWCÎÜœ`Îëh(•¸G)%ÄsZ,"‚~D×NÞ§ õ~ Ÿw=@–eq!Äàd±Ä1ÛÜ JÀB°  õôØæÃÇ&?”ø0$¥ä¶c‘zÕþ»¿ýyæ¥Òldb„ß[Zå–RŠ B9¯>òìe^È¥ùèH™”y³¹ËƒPZJ)Ç\—²ù¼úÐÓO³lÚ¶òù,/çóV¥PàÕºÀaŒq¥Ô~÷†!?¿/ô=»@©T¢|.Gwäµ+ÏŸljmmù;;µÎI*Î÷òùÜb†´µµUvÛøÄä§Dœ–R €¤Ùår±åw»°¸¸øÏì*a>Ê,L˜ø=¢RÉÏOMÍÿD§ëW THD³,/ŸÍ\ûîw¿ûu-Òv½ÙÓgNÿXèûsøJ@H{ž÷f«ÙüŸ_ýõ syôرq]ï£J©PAr("FÌèßܼyí_AEâ>ÀJ§ÓªÝn £éä>šì=ÓÖ{qRÛ¶éÙgŸ¥o~ó›Ã웿Ûü”Tâ+ Jƒx@(Ûh6›F À7°ñ©©©0þ^³Ùº"ADJ)emn Õnµº^I,¶4?‡fÒ­„j%­­ú!îý‚†J1Žf¡pÀ·,)×yzyñÞÑñ»θVáDH§Ò[ùbþ5s,_]YùYbü2ç O "B±P˜ºtáÒÕW^}¥ ‹¹Ï6€0Æ9aŠÄýË„0} €côÍo~SÀ·¾õ­ó㣣ý0Lw»]Æ£l6|æo~æ3wµ, r9çèt:pÔì¿øÅÏ~þóŸ?&¥´¿ýíoÏÿ£ßø3AÀ¶¤ThµÚôò§?ý#_úÒ—ÊËËËžeÙÒ¶må¥<7 ‚ï~÷»×Nœ813;;;'„cg ““ßþíß>ÿ•¯~Õët|8¶  Ûj#•NýŸþÅ¿øÏôѪëºôõ¯ýâoþæoZÍ6¼T€DBV^~ù‡þÓo|ýëW€¼woiâïýüÏ_ºzí*R™”R`ŒÐév‘ɤŸùìO}îËŸøÄ'6«Õ*·- ®ëº»ÆkÿÍ—¾ô¿|ç;ßÙ0f'øàý‚÷Dt»]/|ìcÙR¹üåt.÷é !Bä󬬬"NC>\×…”ŒqˆÀÇÄÄ1üÄOþä§ÏŸ?ÿiÎ9nݾÀ „P®ÇˆÐ }dÒ)üÐýЇϜ9ó቉ c Žã"ƒv³ÙüÆÇÇOÍÍÍÍ„a"Îò…ž}ö2¼ßù§h6Z`ž§§·*Ëâî™S§ÿæéÓ§aÛ6^|ñEüî×¾†ju –mJ"CÕõ}*W*ÏŸ8uêy"ÂéÓ§ñÑ~W¯]U Å-8ÕÎN=ízÞKçÏC­Vˆ h|ñ‹_\ÿÎw¾ó¯ŒfºC¼9„¾kƒ½G~=àïþÜÏ=™N§/sÎÁ8çR©––—°pw1Qïygf¦qìè1D€y}m ­vܲD Æ Eˆ\>‘ÑQ€H«[À l…Àïºív{Z‘Q BB ƒŒ1ØŽ @(ºa"ß÷±¹µ ?Ðõ}¤Òiä óW@)‚c;$·oÝB}gvD„³çÎ!•JQàûˆ¿çºÔnîâÏþìϱ¶¶† Ñh4Ðl4 ¤´çç¼`@Ö|ÒÏD¦‹½×@ðÝJÀÈÈÈeÛ¶‹J)„A)¤”¸~í:ïÝ·RRˆE®¦¦¦N§>Ú­6nݾn· Ûv@ F€’H§RÈf³B ¡ù¿ÆR­V'ØÙÙ±Â0RJ) •€”Z#8¶Ý/³Œ£Ûõ±±¾)‚ @ÊK¡\®ÄÊ—`œ ,¯¬b}}€B =zår¡ßÕ‚ÀH{õÕ«XX\„ã8¡D†R¢R©\¸|ùòY9óÉHp#÷qÀkø@  !Dt£^*•zJ‘RJ(¥àØ6Zí^{í5„ÛqÌÄ‚ €eÙ8~ü8lÛ†R@ ¬­­JÂ`…øb©T ©T P*ÚÁÆÕg‚€·Z­´RÊ&êy[d´§m;pÑÖ&F`œ£ëûة!òù<&&&z:#eaccK+˰mAàÈÑ£ïËŠîxxó­·pûömض bFS)…b±0ùÒK/> o>‘¸ 7˜ïã"~ `ÐßÇo|õ«ç3™ÌYÆ””JAÁK¹ØØØÄÍ×_×Ú‚1€Œa"“É`~~ܲÀÃöv ÕjÕ¬Sÿóç²Yx® !%zŒ3AÀÚ­VJJe£>¥ `;¶¾øŒ L &ˆRJ¤Ói”Ëå¾G#b°, ;õ:VWV"„"ÄÈÈæç絟 é©u=›[[¸výüÀ‡eq@)„"TŽëzgÏž»hÌ@$i£œ!f€>ð ºÉÇO<éy©9¦Ý'–íàÞâ"Þ¼s§·ûˆ"H¢X*azfBض…µµ5¬olè‰ïà „b©„L6 Š~¦‰1øOÍVËUJqõMA«ÿÈÒ‚ŠF£!„¶ ŒP(bB€˜^ ÇqÐj6q÷î]ˆPh{ïy8wîc?¾WËâ€qõê5T«5¸n R)!c G=~îܹfçç8À ÃÞ-ð®bÛ¶#”ÊË¥â“<¥”ˆ1b "ܺsKË˰/^÷$FGG07;‡0 Á8GµZEm{`d¾¯Í€B©\B.›…Føz>4$„aHÝn×RP\ÿIõi˲ ÐØk®§b{g¾ïC abbÙl΀G‘‚e[hw:XX\D€"XÜÂÉ“'‘ÍfúÝ€ˆ@¶›¯ÝĽ{h’Bb|||ì…^8 MDÙ!8 Ép~ M@d|‘Íf @ø‹¿øKg …â–Å!¥PD€ã:h6¸úê«ý®¶‡0¨ÞÀù¹Y‹(© …ÄÒò2,ÛŽ˜• ¤Òi)ú¼2æ$(­@JÛ_DZáFÀ |Ëðÿ[[[ètõ&"LMM¡Ï!0Æ’@÷––±¹µÎÂ0Ä™³g122( E0f€ày)ܽ{×oÜ I'‘ïû*›Í9O=ýôI¥T†ˆ2F âöA6¤”¢b±Äàôé3—<Ï;Á¹e0Áuµ-¼q㦾0ãf’‚0çNœ< ×ó ´ŠÄÂÂ"Z­–Þ5‘¶P `2é48c ™bq Ãa£¤pÄä‡iÄ -îÎN]«p¦ÝË|>L& Ha„@àÜÂêê*ÖÖÖ`Ù6‚0ÀÔÔfff´ó.´va Ø–v«ƒ+W^E³Ù„ëº;H ÆΜ93}ñâÅ3ˆL€;Ä|×ÍÀ»f …½ùæ€ŽŽ‚HœÁ– ˜žžfðü‡>t:J³-¥Aiz¶Óiãúõëý¸±ÿ,z%P.•1;7!lÛÆÖæVWWâ1øcÄ B ÏõP.WÀˆâ]MªÇ„"Å9ÿždR‰´FH1ãe00ni.`g ‚ùBårIߦÑJ®ã ÕjaiiBˆ(ÖÓ§O#›ÉÄ„PdælÇFu«†ë×o@„¶í CƒyäÂÔ#ç`Y–å%4€}Bˆ> ptt„4•;=}‰16.õ„°ÛÛÛ±ý·׈š¤Ô75=…|!aìâöÎjµm€So—ƒ”zò³¹lL÷ûy„0´&±,£ z® Ô*Ø Ã9C§ÓÅööN¼Ð–e£X,Ä ¤2Ø…1`euõ0Æ ¤Àü‘£Zc(ap+ƒeÙ„À+W^E}w¶ãÄ÷!¦gfFŸxò‰ “h2(|ˆøà`€G}Œ½òÊ+þùGΗ˕'Ç…2¶™¶maeeoܺ0ËØD­NE¨=€Ç#›ÉA (ØmìÌ"2)Íz¤T½™èý¡á,ΨX€Œ à\Afáµ{Ç@Œ£Ýñ±S¯Ã$û€ˆ0>6Çó´{J,"@ŒãÞÒ ¶ªU8Ž0P*—pêÔ)E¤ Öa–nY¸~ã&ÖÖÖ`[VìÚú¾\.GÏ<ûì4€\§Ó±à=3ìÚÒüý4 ?û·>$“Í>j;6”’:g˲ܾ}ëëë°`Æ-31"޹ù9¸Ž)%”î-/£^ß…ã¸Æ¿gFåù|™\Î3ÁK1!h€"wV Õ:Ž 3Z€ Iœf³›Î9&§&‘òRÂÄY¬58·°U­bks Œ1„¡@.›Ã™3g ÕQH2Ö¶m,-/ã[·ô÷-=ýRJpÎqéÒ¥™S§NUL Y ÞϤ¿tPÛ®é]|òøy˲N(CÏ*"ØŽ®ïãêµkh·Z°-®A´ô !ÉfpüØq£Ò¤’ØÜÜD§Ó‰Ñzä5(©P,QÌç#[–?bõB@)¥¢¼ÂÁbLJÌqضƒf³+W®Â÷ØÆD~`vfvô‰'ž`›t3g@ °wÛ °wŠþÐÿñû¿ïŒŽTÆÇÇ?äyžæçcõo£V­âÊ•W¥Y82;DÂ¥R óGŽB)ni`yiPc:¶®€A*‰|>B¾ Á—v§â€Rf硇ôA*ÆR*0nÁK¥Ì=°Ø‰0CµVCøt ‰R©„|!g´UOx]×ÅN½ŽÅ{‹Fsi€yæÌ”K%øv ‰1ØŽ!%þý÷¾‡jµ ۲㉠‚ùB‘>úÑP4 ž‚†™z¿ ÞVétšˆ/|á‹Ó™Læ)ÛÑ*Ÿ÷s «kë¸uçȲÀM²D² fgfP*!„€ekÀ¸¼²¢wޱÏ0¶—1Žt:Õ‹$ÙBJáëE=7P«u×uÍy/’eYJ¡VÛŽ¹†E¥TÖç%}O AT€a,waY‚ ÄØø8Ž;È DZÀ,ËÆ›o¾………p‹kV€2ÞÀ¥K—fO:U2È?Zü$)ô®rïTP«Õ’péÂ…SŽmŸ‹Ü!͹[ o¼þ:677aYvLŠcJ*ÄÑ£GQ,†!8ã¨×wQ­ÕôÄ%ŽW °mù\Þ0uª/ÈCD*ŠÆEìžÒ>¾’JgŸj0&f¼œsH¥°SßÑ\Óo¹\A±T„¡1úž8q0b¸woÛ;;š |”Êeœ:}Z £`ܘÆà¸ªÛ;¸zý”¸Å(H¥„!ffg*O=õÔøÅæþ¥˜€8ºj@–œšzÚK¥˜”2>²8‚0Àµë×ÑjµaÙv/¨ 1ÌÍÍÂõtj°¾¾íã+3€i@J ÇuP.—a¢Œ±Ýïmq‰P„faÍ´ð Pï» ØÙÙQðÒK/uç’’½ìkbËâXYYÛo¾b\›†X=‡A€\6‹™™0bPJBJ‰­jív['bF6Úh Ëâ( :”0JéEQJA„œó8ÀÓç(ÆÛé#¢EåGÇ÷Q¯ïÆ‘J˶1==Û²†‘' M‡ckJxm}€va¥”˜™™Åôô º¢\F"¦9ÛÁÊêÞxã,“ü#labzz¦ò?ðáÉD~à +ÈÂÃô~j Ç!£þ1;?ûL*“.$„1Î8^ýulnnÁ¶sO¨æ ð13=ÊHa²l4›M,/¯@š°+‹øtb&™ÃA:•ê‰{œTbÔ¼Ô&€q޽‘@í.2ø(f™æƒÅ-´ÛììôÈ Ë²022ËâBg0k­ÁÁ9‡÷––Ñjw ô1>1“'OaJI0â±K¨½‡®^»¦©àÈ Èåóì…>vÄ„…"r.á°\Aõ¾ƒ@×u£ÙÍ–JåËžëö©Z˲ Dˆ7n ÑlÂvcÿ9HÇÃ!ý.ææfQ©ŒhÕiYh4X^^†Šls” Ä41“ɤ‘Íæ ¤ÜÃòG©\a‚›]Ú£Š©—RHðq£ò#7™x@§ÓA£±#t)% …R§DBà ï-¡ÙlÁrtŽ`¡Ç‰'LÒh`R ´ç¹ðÃ×n¼†ÝÝ]DÅ$ ‚0¤ÐÅK—f2™LÞpî>îà;ƒo›ÚÝÝUð ¿ðßK§RÏè$³$ŒLÞÜ.®¼z]?€ãº=R$RߌpôèQd³Y“À±»»‹MpÆÌ"F¶™AJ…|>‡r¥)‰ ÑN6*; XœÇyyÃPŒe“Ö»/nièúh4›±«*„ÀÄÄŠEíª’Žq›ïp“°†Z­ ‹sø~€L&‹G.\t;0Þ‹X¶ nÙ¸uçMܹófLw)$B!1äÈÈË/¿<—ÀÃD︖½M€`ªk/]ºød&›™ì?H?ä½{÷ðÖÂ7“M=“q¯8ç8yâ<ÏÓñsα¹±‰­ZÍh 1»ƒRId2‹EM¼ ‰tx5ÌNÒB&"à–¥]°dï„sŽP4[­c!1>>B>0 bAq¨×ÃÚú&Ö76cïŽ=Šr©ŒN« 1­=ÏÃæf7n¾ƒÏH8E¢P(Øùè ó‰ÑA3Àß”ñ·…,ËŠìŽ322òœëz}j˜™€7Þx›p]7~Èh×A€l6ƒ©éiIÓDÈúæ&6·ªpl7ŽFˆ HyÒé´ Qœ B‘ 2Ö&d¾3̱mMÊD¡àˆj懔 õú®Þ톞Îd³Èf3Rè°0ç1¡eÛ6j;ulllÆnb(ffæpôØt;-Ãjö² ùÉ™!0hÞ´ýÇ?å+¿q!Ÿ/œdŒÅaR3 ßÅ»‹pó£86Wãºà¶ Îm¤²lom Ý¨ab|̨s-8µj ÊÎàÌB±T·X¶ƒT:‹v'ÀÚíWàzN|4pW=ÔýÃQ‡ Ƴ'ÏâøÈQ”Ê%ížq Žç¡Ñla2kag§/•†2Ú¨X(btbç?ò×päÔiˆÀ3îj¨š[kèÒ†èt÷É©)Œ!ô¼ô㟛JAá8Z)ÑÄúÆ&ŽË¡ãG9*Œ|ßG±TN]~îù™ßûß~ïJBÜ!|ÃÞþCÍMqšõñã'žòRÞ”F¼aŒÆch4êNû±ŸÆÑSgÀ9G:›ƒëº˜˜™Æÿûノ?¨/c|l,ÞÉJJ4[-œþãxù±ç‘ˤáz.\×E©R@·]Ç7~û7`q aö…@bOÀøÑJÊØÅ<&ò&¤”xú#/àã?ùy” ú߸LÏàíko¡Ñ¬aLè8¿”:/`jf?õw¿€Ëg¦ÑB¯¡‘à­Í6šwßÐäŽã¢Óé`|l SÓ3ð½¾ðùÅTŠÐHL<p}i ݵ¾oXÒÈ „ÊóRtñ⥩J¹\ÙªV›Däbh0_0ÆØ¾› ³|õüdË•ÊóDÌRP‚@þ~óÿø[Ÿ~ÙÊ(lK3œívíjsÇâw~û·ÎäЩnN€@ÄÈïú(ËîÓÏ<;ýµ¯é6S•IDAT}Í“RvˆÈI¤ŒÙûd «wÄõä©“y^jÞÒ„#ão“ÉÆÉ ȧmT_Cuñµ¡''¯üû?ƒÝ­¢[ïb}w™T )×A>—ÇÔĸ]›´¸n§‹3gÏá?öþä¿l†p÷­;Ja~~+‡§.ÁÌôºA ÃåQØŠBªt&M.^šššš]^^n0Æú£=ÏÒ_üîw¿‹ ¸üÜs˜šC£ãcggG‡kI“A¡T˜œšÂÑãGá099¯ýë(•Kxú‰ÇÐ$¶··˜X„68 dò2žN}éSŸÆÕW¯¢/âÂÙSØiuÑhìö§[ÅauÀïú(•ÊîÓÏ<;õ{ßøFÚ÷ý.cÌ•R:C¨añnš€xƒó›7oO=óô¹t*}Ú¶lø¾OĨ?Ún¤ÕlB„!×Åë¯ÝÀøø8Òé ªÛ;h›Åø“ê»V«j<hÔ똜œÄîî.6»]ÍÿGÙ8ƒ®¯qC… ¢–2Ã8‘8sHbkk –e!—ËAH…úÎÎf±»»›ˆCÀäêâÑx©4ˆÛÈåó¸qã–Ö6t–“a “&G)…z½Žz(•+˜žC§ÛÁŸþéãÌùóèt:½FW‰Ì%2ÕÈBX™ {âÉé©É©ÊòÊrݶmÏ÷ýAR(0àÀfà DŒŒÐ•+W¤)yŒqVR¦Ã`0&.´„FЭV‹w0:2¢›'˜ŽQÄU;7XŒÂÂÆŽÓ¿¢€MßbFC„†¡€RÇØ@¢2¨0⣈!c®çÁ¦‘ãæ¥÷ÑàW P(`lt›››h·Úšßq|„éë))1==ƒééiܼq¾ï›´°¦IÎÆš-8vüÄÄO>±_ýà 89IJûOŸ>ÍkµZðƒûèl¥RyÜq½Hý'J²ö"r˲P¯×±¾¾ŽÉÉ)¸®7ƒè>PònÒ§ëõøb± ]±hG¸_£ï0Ôt®e[}ái$x¢½ Ž©tA ÝnÅ»7y )%,‹£2:ŠÝúªÕ­¸®Ôÿö8Ö×ÖðÖÛ:[ˆ±¡ékÑÏR¸®‹gž{þÈìììˆRÊ2Á¡d)9߇¦wd–––ÌÎÍ_´,{"2X”D_„¾… Æ XZ¼Ëâ7¡zn\ܾ-!íf‡mo뮚ù|J 1£^û4&™Ž]R)ÅïkÖ¯‰Þæ?Ù¬nñÚn·{&)ºH|lògEºªYAaey¥ŸzÞC£Â„zuèûÄéÓàœãÆÍëºWqd6¢›ÉXF =v|üÑLj ìW8ò@3À”ûODôçþgÁ¹3§ÇGGGŸòR^œ5Øéœ È9‡ÞB¡XD©\2Y<‰#“¥½Q^§)Ù®Ö`[Ù\¶g6ú„­— ‚¥«âC V‰:À¤Èær "´Zí~³i EeQ„0Ê•X–Õ•“ÔÂÏFýÂd6@(€Ù¹9LNMâÚU] kú×lð%~ T*Y?ðžî,º_Æ0?h ï& “Éñc?þ7æ2ÙÌãN"÷¿/+7šhó#çZ­–——066¦[ºE¯ ]Ïïþ¤É$]g_«Uá:.2™,„ÐÁ&EƒZ#a"¦ÑêSä;‘È’ÉfAD`¯÷ÿÌ`tt…b++˺žÐ2%éjÿ—‘tý•ÊŽ?;·ÞÐøÁ¤À2—}Mƒ¥€í¸xò©§çÆÆÆòø@ÆðCײÔþQ£ÑpîÜùs¶íœˆû>%v "oZl×jØÚÜÄÌôtÜzÂ÷&¥4ZaˆÚö6Ré4Ò™ „ ãÞ<‰Ì”=¡à¨i£2I)PCáI¿ça¦Üu=­šÍ8ó7žŒï#*mBÝ R©`mu­VKãê¹jÀø¾TÊÁ¹ó°¶¶†{ :=h_–Ìü„Bâȱãc>úè(×PÂê&²¯+x? <Ï‹J¿ £ãcOÇmÙúli¿pÅyœcueÝ®‰©ipÆ»W8#ÒY¹]4êudsyxQ#ˆ¤–éCØÉhŸ2I›Zõ¢"&ˆ°ÇtEy ¶­ë›Í&¤ÒÅ¥±iRýÞM2÷Àq\TFFQ­V±]ÛîGô‰z…¤9Bo„S§Oƒ3Â×uêC|n_C% HT>V*Ø/üàΚºaf`0O@=¬è+ýúüç>7–ÏåŸv]Ï„oû{öQr¢GÔܽû<×é€Ô§^i0Úh²gh6Èçó°,»¯ÆŸú„ ßwŽÜ,õË{ÆbØ‹Y¨sX&G°ÙlÆUÀ{ÝÁ~“%¥·´=oµZØØX N¼†>¯@Iø!päè1T*#¸þê«ð»>·zõéœ8bjÌÀÖWWÁLÿ ¸ih/x%B·ÓÁØÄŽ?Ž7Þx Ûµj캪¾¹é^)$„ÔfàòåËãÃûõ|( 0XúÅgfgŸÍf³ž’:t³WHÀŒtfM£ÙÀòòÆ'&5ŹiH‘zx€3†z}A¢X,ézã [ð$QJ!S±Ã>øp{L h;R)h Ãl <æÛÀè¨NlYYY6¯Ÿa‰ $ê÷µ«ƒN×G6ãáì¹óXYYÆâÝ»qšúl×>“'„@edÔù䋟:bêÞ¶ØOHJIpüÈÜH¹Tþ°›ò ¡?õuàŠn”Û6Ö×Ö°]«bvn®ëAÄYCdvëy€ »JT·¶ ¤@±T@íªêfÄ •ÎYd ¸+ R«I;¢Ÿ¡{üer-~›±>s5YDŒÀôÌ,r¹î-.ÀC°¨­ ÑžûŒžFš,êG.\‚R W®¼SÍ{BØI`ZẮ‡ËÏ=„é.ãî>¤Ð[ʰý<€(÷ÿ§¾ø3G½TêÑ¡6pÍ’I¡‹ Bbrr:®¯Ûk‚÷>¬P«VÁG¾×=¨b5 qT¢‰túqñÉ0ø¯BÚdÝ?8›É¢Ón›ö0”4\ƒ˜ÅC±TÂòò:í–¾ûó0BWÇNžD©TÆõk¯ÂOˆ ð $Òî TÀ‘cÇG>ú “‰EÌ@ôÚ½–³ûäþ 8}æìÓ™l®ÐÛü´G’»Ÿ"gáî[H¥<ŒkÔ«Ð×—qeèc)jµlÇA.—‡rÚÔádÚÑH©ø:TÌO˜+Ú'¿É\[–å J£ÓnÃ7½‚û¨z™ïJ©à¥Ò(WF°±¾Žz½®;”. í¥Ê;SS38zô^í&¶kUØ–“$zål„2I 2:–þø'^œÀÃÊÇöÅCM@¢ò7S©Œ\öR)3™ ôŸTí^â ~·ƒ{‹ ¨T*(–K ùL Ø¿ œéŠ¢íí*R©42¦ 8ZŒAAè±{Ú®ö0‹C²}A§ó¨(á`œàxÚºÝnîÝ3úèà8ÁLIØ.ÃììvëuÔª[°8õ“FóõGüÙ´3gÏauy wïÂrì„À ¯¨E%¢­A Cí=ñÄ~¬ }!¸¿Ðétü̾0Íåïqïý-Wz‹Òã±mÛÁöÎÖÖV119‰Œééë • súv“v;6v¶·‘Ëe  ûTq’HÚç¨gP¿¨8L¬c}1‡$»G ð<Ýn'nêãE{ܱHMK)ÁÌÌÍ¢Õjbc}­GiS²K)íyf©´v;ûÈâækàq)üÐ@i,BHŽž8YùØ /L˜aÑÁ¡Z€ *TÎy\ùûÜóÏ?™Ëåf¢Éí!Õ_”yÚ¶ÕÝçjzVwÞŠüê¡>x/n®9€&»»Èç °l{HŸ¿A¼çèÊàŒñøåNìD´'ÅH§Òð»¾n=¼ð ABõòÀØø|?ÀêʪÑ(ä*ÚâMr€S§Ï ›Ëáú5Ý?skˆ‘t'õ‹¸FFÇr/üàÇç°·€t¿æ’÷5d’š˜œ¼œJ¥ÓŒ^íïZŒ‹ w¥09=­#yªJë#;’AÎ]´Û-K%#<ªíàJî_T¥¤nïÆ/ßæ€ðQÒ¾öªt&ƒ0 Ñétz­iöhžÁ\†3=3 Ïs±²²„P z)ÄÞXBâÚŒ:‰™¹yÌÍÁëׯcg»¦ßÖgbiX‘|ß—^*…“§ÏŽ˜Ý>LÜ·Œœ Qÿ€Ç½8ž/Ÿ$Æú©×ÄÎOªÂˆX¸{étãããÚÿ ¬D…ÝD}ø†q ^¯ÃïvQ*— "Vý™2ƒÂG=›¬¤®;䦧€2¦!ŠæÑ`"Á*tHXJ‰v«md…íI ¡AL!€ÉÉ)d²9,-.Âïv`^–Ñ—2h†£·¥å³.NŸ=‹¥¥{X\X€m[½æƒÉ+ Áй@yd4eháafྑA¶‡R3ãgöï<]*•OrΡú‚#ªÏ¿Mú®–å Ùlbá­71>>ŽÊÈH¢o> a劘õ8Íw—‡ñ·ýÅ IAbÚÕÔ¯Ÿãqû5…~>~˜DªP,J¡ÕlôÉ›¸J …T@¶PD¹\ÁÒ½EÓö…íyÓÙÄÄÀcO<‰N§k¯¾¢©qk€¹¤=I& P(‘ÑÑÜÅ L ÁýÚÉô©/6PúŸ{rzæÏKU¢ø<†*I‚F)ý:¶Í lmn`rj ž—2-Û=Ðþâ]£ßØQ«VèD ûC:Ã+– C&%Bõãaze’nà0d¥(N8Éæò B‘ð(ôEcbúXõ\4!/åavvµê–æ18zAô8…Þkg¢ œ>{™L×®^ß ¬ õE;ãu4Ç:]‰ÊÈhé#/|lÊ,ö`/û–‘³}J¿JÅrñ)bP‚¨×ƒ¸iÛ®ûýÁ4~ã°ÂòÒ=tÚ-ÌÎ̓1Ë´oãqk00«ïCܳm(jµ*lÇF±T1Äm€[·A–þ0ËÙ6`Y ËÓax„AÛ²Ám×gþnŽ'ÛYÈÖ×$GÿÎdrY(Ív dÜqÁlÇ|l0Çsló³ù¿m8¶€¹#óØÙ‰\A€»®þŽ«?ܱÁ]̵À ̵`¹6BÇNÂÜü¼vãêͼÁr0×÷ô÷¸gƒ{¸gÃrmXžKŠ *ãeöì‡><E‰h¿‘=® 5„Ä—õW/ŒŒMœJåòœ%Ý.ÖcàúÜ0(8–—îÁ²lÌ?;mÃÓì©?Å).%3Å6\õmdsTÆÇag,(+×Ñ%Éèú‘°ki3àe2Èsàà!ï}†‹NkÈå2àÜB RðC œ½!™Íðôô4ü0D½ÕD €J1°¡6–Áq8Ξ;?ü¿þ;Í]C”âCcÉŸ%ô{æ½ðÈèÅ‹Ž\¹ò½-Ƙ-„Hz|?JÒö‡SgÎ](OLŒg³iÚCQ‹zfccélSGæaHç<ð!5fƒä4Ýh4Íç‘-ÁÀ\¾¯;xãŽc e{ixÜè<‹îûº­ä¿§290®>[Ø“S:4¢Bfv`t\§Kîìê"û÷;øû‘cÇÐj·°¾¶Ýs½a¯xÒB±”?z¤påÊ÷EËñ xYI`L¤£;µš'AÁtCãÁ4¥Þ®’ °- ç¨×ëƒ;ÕmØ®‡N«Ý ­&tQrñø­ZÍ&šÍV–‘-t%NåRä²9¬¯n Ýjbcu·—ázú%Ôƒù04Ä¥ÊçóX\\D³¹‹;·^Ç¥5(¥àû~")DõÜØDrb …|ËËËhìlãÚ_ü®<ù ün×\ßÜ©J¼ÂRõÞvι¥¿¿´Œêæ¾û­ÿå\Ͷ.®A²_¢èKA"Êg³¨m¬©êæ†ÀÌ›Zù>µ}2 ºJ¼|çOÿäWß|ó-aÛÞfqjP#Çu þίãî[oâ_þÓ‚±±1]¿‡¡TïÚ}ØLêIRbéÞ"6Ö×ñO~ýË(•+èš×±÷MBô“ê-†eÛð»mlnm‰ååeùÕ_þE'Îê¶/‰ëO¸P£À¼ü¡º¥¶67é;ÿî ĸ^€8 Ó«|Ò]OTüòk]õš­þðßþŸXzëŽy[z¸×ƪ^bˆv÷tøÝ» PJáÿ_¿†?û“oëÖ5JÆdS‡bæÃu]U(ÚµííÛ¯^¹RÀ¥”ê> {X@>þpêüùG޹Žõy!åE.A8ݽS}tjÄÖŽˆø]4[-pÆaY¨ÐfŸú …7u ©xav|c@êvÌ¿·ÍâGö?>5ÄÕTæ Ð|)J/jù¾¿m20R×àœé>|r! oy6lÕ> 9Ì:¹Ï÷†ý®öY  2çíû°5j›]ÞH|š‰],ü}™ÀhB¥T7¡x‚ó‰ ߯a¡òûíŠý&c¿Ýˆ!¼ˆºÏâï'ý.;àbªîþýžõ  h0‘$?Ô/¯i ‘âÈN ô:N°ÄÂG»ßÛ'àp? ¶ËiŸI<è¤Ðv°Ú‡H¤û\SÝ/á‹yPža¦FšiŽY—Ž†Î€ÚªM­}nJì£Æ“*ç~ï²ÃÔÝAíøƒ¾·Ÿ—÷YÄýSÝGðF•j¾ƒ ÀA=É‹„Ëi?áï.þžg³îóÑ‚'wURÚö+CR±ø ÃûÚ=,ø;¨¡éA1ÇýžMPý¿Ý¯LµHàp€æ>ÐÀ.JJ[t>€ ô‚ahü~¿«LðAí)=?<Ì‚Ðà ‹Hoó{ûy:2±Ó“‹žüûC“AUÉ>ûö{@¼ƒÝüN´Àøc¹6½÷{¿sѼ5Dä>`V=Ìíç7Óðâƒ&†þàA÷AoCŠÞ/W÷qdÖ誊öŠƒ$ïÅä<̵ޮ¾ï¾›Zàí\O=,¶8èÃí÷žZÂüUìCFø^vb¾üÿOã€Éw´ UAÊÔá8‡ãpŽÃq8Çá8‡ãpŽÃq8Çá8‡ãpŽÃq8Çá8‡ãpŽÃq8Çá8‡ãpŽïÓñÿœ%#¿_ö“IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/pluginEdit.png0000644000000000000000000000013215101070305017301 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/pluginEdit.png0000644000175000001440000000200315101070305017264 0ustar00rncbcusers‰PNG  IHDRÄ´l; pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<IDAT8µ•Ýk›e‡¯§Mšeí›Lk,4DÒMt"cÃMn¸z$ZWœ ¹!ƒy ³ÿ@œL*«Ĥàl ¬]G™+’m6¥n¬mB[]l›ôó5ï×íÁ’º¦íÜÀÝð<Ïýpqñã†[‰O¢Êžp=¬©”ò/i`TDìG&‹Èº' ¾ßqîÜï7¢ÑTWW×X}}ý€¶ÑÿÒ³þ#”µµµÝÇqql[ÚÛÛG€½ Þ0ã‘‘‘d6›uÐ3n M‹ê~U)¥*•Rê±2G)ÕúÖ¡C/6>üWíÎ5÷&'+¥n‹ÈìƒS‘O$¢]Ávå^0ÎyÀ(+£:®*óù\9@išŠìÚž)5^‹ÇgMˆØ€õ€­Ubkú™|žt*eºý~WÎ0˜Mþ‹ˆ¼ÓÒ2žµí×¥¼|È*€ŠP³Ð»‹Í}ºgÏÙÝMM/¸½^Ï@OOOqüVÍñ/7—2™#MSEТ®“J&uåñ”ûjk½ŽË…©& *+]Žã$/^l¼À‚ˆX«¦`6™¼ûçô´ž!ÌÌÏ;­­ýï54œnihøüÒùó·uÛ& d²@@óWW‡[DREè00:•H,ç•" ÌÎÌd¾?sæGà3Ÿÿª·»û'ݲȶ׫^9xp;PUÂYþcbll)äo0XõöÑ£MÀs"";Øky<,úâ"¹…kó–-•€§¼*cÉ9uê^¶™€ãõ²ïرݗººÞƆ®^n žNOLÌ%nÝŠ÷÷ß± cXx(`xpð×ß®\yi.™\J& C½½CÀ€X_ßéX_ß·@˜2"b–2TéÎSJÕ;˜åþöHËc.Ç5àÿ«þ,*:#ÀŠ÷|IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/formCreate.png0000644000000000000000000000013215101070305017264 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/formCreate.png0000644000175000001440000000026715101070305017261 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®ÎéqIDAT8ËÍR[À «»8; œ‰} >¶iÌ–5!*´UTà×P… )³‰˜¡½õ­¡‘TìóO'"'›øGÄ(wŽ(Û¹ì¿j×(šl>I »¤–1ÞÛw°ð Ëÿ @•€ªú¦ßãB…惈${ÑIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackIconViolin1.png0000644000000000000000000000013215101070305020354 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/trackIconViolin1.png0000644000175000001440000003632615101070305020356 0ustar00rncbcusers‰PNG  IHDR€€Ã>aË IDATxÚìw˜Tåù÷?Ï©3³³½/ev¥wAšØQTì=ÔX#h~I5±…Db!Ö×b šÅŽŠ H¥- Ëö2Û¦œò¼œÙŒ‰šDÝûºÎ5³Ëav®sï^è¤N꤃žDüê¤Núv@P:Ÿ×ÁE‹Ž,ä}SÀä£~5<ÿÜ3ûd ¤z–ašÚ7B§Ú8ø(Pœ4µO—«3}Æ™á6G!ÜÝõ‘òÊÖèçOTìù °°â—Û €u{p|ñ›©>½? ÐÒìÒÜl³zO3Û#a&vM¥É²Wþù‹Ò©é¦^[µ ÿÕªÏô !uΘ>/ÐéÕÏ 5[%!¨°~kËë›8£6i>œ3/&ÜèÚº–Í@°ÿ:}€ƒÄyëŸèTÕ1Á ‚íH ¨’’º6Š3¤›:[ËÛX[ÚBß@ð ~wà!EQÄ¿ í6Ô¶5†q6*BPSîR[á²g—MCÔ¢(=–6‡Ýá(1\ÂmNÈŒàß:„ø!uºª&%%žgšæI®ë&Ùªªôº®›“»gè ¢½ÌÎÎMq‡íÛ·¾ä÷û |>ߦæÐ¼Pchæ«eµ±WËjv¾¼Âªl+rÜp«Tb–Äu$‰ºJ$&éÙS'«T'ݧóZEõÀ‰_òß}‰Nü@”™™9{Ô¨±)ÑhŒ@ÀO8ÜvZ¯^Ÿ®Ë¦Û§ h‰gõÎìå×´"Ë’„m§!j;ˆp0AIp]‰+!×ïcgU˜þ=ýœ06×Ô–l µ­ÖÑd¹òkÀN|Ï”šš*|>oMÓIHH$--…ÒÒ4·4“—Û•…ÉôMÔOËé®Hêªê”@K‹Bc½M$æ` )‰:Y>¶U†ùtE ›Â-[ž+¯|¨°\Y 4}:ÃÀÀÛ·mÛ2 ýÄP(T(¥KAA}ô!MMMœ¥VÓEØ8(Ä,‰á¤f STü¦Š´¶ VÄ¥µÍ¦¶Å" ©TG£µO”ï™Oív¡x2¨ Ù¶-Âáp©¢(ÇþàòË›¥¤õšü€_ I#.±˜K,,ˆ´€kƒé¬ù¼•;šiG©Fà8ɪJ½eµÞ¿{÷{@)P­‹çä{vÒw£TUUÇIäÛG]ª5«9mš*0t…¿JÀ¯ôklÙÝÆêš>ZSÒøBQÑc1ºYg]$NJÏð#ekIÉæ™Àkq´ÅA:5ÀDRJ!„šòSzf\]èúú56EÑ4 ض$sqlØSeU}ˆê´tê5€PhŒ†e£•B(Çb$•ÌÌ Ó²b=C¡Ð[Š¢D¤”Q¾¦Ð €BWQUMJyßÐãN>&RTŒßgÚYF’ivp-jIÖ7†¨ËÈd‡eÑ串ʶ„?1Yädç’™žNkK+EE½ñû´¶6+*ö¼/„h’R¶Ð™ >`µ€â8ΓgŸ}öÉ#‡—=‹û°%+›ì+¯¤$)‰ˆ¢ Z[°ô‰Eèæ:v̆¦sȨCÉËëFFF±X”íÛ·Òµkjjj*SJéû&Q^'~2€S§N2bÄŽDÄÇÌè¡C)Ù±ƒáW^ÉÍÍ|ÑÜL›m“ªë˜–Õ¸|géSÕ•å÷ÙŽíVVVb:ݻ瓜œDssˆ ^Š”•í|ˆH)í¯Sÿ&à ôôôäp8<ÿÒK/=®¨¨)¥X¶lãÇgÕgŸ1îðÃyù†È‚jÛ&IÓèêóñxyù³1)7„y uÝmÛ&==?\ºsëÖ-ókjjÞ6ÄCÀ}ó8@ȇߘ>}úÄŒŒ iš¦X¶lcǎ峕+9ò„xæ¶Û(ˆDØ‹‘¬iôðûYÚÐðîŽHd™€]@i4]!„ÈÓ45cåÊåkkk !6ÆÃ¿2 h¾Ö ì ¿?JÞ¼æšk 299Y¼÷Þ{Œ3†•+W2ùÄ™wï½ «¬¤ZÓð) >Ea}só§oÕ×Ï'w¶Å™ë@ 1êÕÕñð¯%ÎüÎZÀ@©©©¹ ‹®»îºB™””$Þ~ûmÆŽ˪U«8æ˜cxäá‡euE…p²²¶——”¬º«ŠF+l{»€* ÛãÌÅ™jÅãü@œÑÍñ+ºç/¿6)ÑÉžïžÿŠ¢¼ã7 …Bôèуùóç3~üxJJJ6l}ôo½õƒ®ýô“Oþ·áqÞÔUñŸ#û˜o=.Ä퀰÷IüÈoòå:ðÝJ~~CCÃ’[o½µwyy¹,**o¾ù&¤®®Ž””Z[[yôÑG;vlÓG}ô°/Ÿ_—öh\ÒÃìßäùåYùmß~÷Ô­¹¹ùíY³fõÞµk—ìÛ·¯xõÕW),,¤±±!¦iòè£2fÌwõêÕóâv¾,þº+.õí6ýËEþ¯òÛ~ÉN|7”ïóù>˜={vaII ýúõóæÍ£¨¨Ã0hhh OŸ>Ìž=›Q£F±uëÖ—ÚÚÚÖǽøÒ/©ûX\­»ÿ‚Áò¿ù¢øS èkšæG·ß~{þªU«äˆ#xá…(**"33“åË—sÜqÇqÝu×1tèP*++_¯®®þXÑ.ùUq‰·ÿÉîÀC}€¥sæÌéòá‡Ê#Fˆyóæ‘““CÏž=yñÅ™1c¿øÅ/èß¿?±XlùÎ;?*¥”eqæ·þi落ƒÁ†‡zHžrÊ)òÞ{ï•ãÇ—Çwœ¼ë®»dff¦\´h‘daa¡1bÄfà!ÄÀ( ðu åAD !!aLbbbèñǗǼ{Ï=÷ÈqãÆÉ±cÇÊx@†!_yå™ 322ä˜1cö³€©Àx K'ó2*((hÁF¤¤¤Džyæyøá‡Ë‡zH>\Ž;V>ñÄóçÏ—yyy2))IN˜0¡¸¸8èÉގΰü £ÃRSS#sçΕ£G–>ø 7nœ9r¤œ;w®ä¼yóäÀ¥¦iò°Ãsý~ÿ£Àt`Pˆ—"îdþÁBÙÙÙJ\íOÉÈȰŸyæ9jÔ(÷‘G‘£G–EEEòÅ_”€|òÉ'å¸qã$ 'L˜ 333_~)„˜É|ÍäN'˜49//OΟ?_>\>òÈ#rðàÁ²¸¸X.\¸Pòþûï—§œrŠä¨Q£dQQÑRàZ!ÄéÀP /•ÛÉüƒ‰LÓ<³K—.rÁ‚²OŸ>òÉ'Ÿ”Ç—………ò¥—^’€¼é¦›ä%—\"wÈ!rèСkâÿyÀ0 0;™P—$MLì™ \;!C™=)[}pJ®úÇc³”+F§)É>E$èBdçä¨qæŸ×­[79þ|YTTä>ñÄòC‘iiiò•W^‘€¼ð åM7Ý$Y\\,ÇŽ» ¸YQ”iÀ¡xS»ÿJjÜ&•S6­¨¨H.X°@É'žxBÜ"'¿+·ÿéL›6{ï½—Y³f±eË-ZÄ)§œB¿~ý¸ôÒK9묳ƒrÈ!âóÏ?#oÀkâØ…WÓÿÚ­N|Wn{q±§L½-Q3FÖÖ¸D4Ÿ)I‚`¦A01€n긢maTž_VÉâ–lî¸ý\8m*÷ÜswÞy'›7ofþüùœþù¤¦¦ò§?ý‰É“'£ë:£GëÊËË— !öH)KñZµZùšýNüéþsÄ/WJ à¾“sïÊKðŸZS-º`h¿©¹¹ø2ºa¤ç#ü© t¤P®CJbs¿Å;Rç˜É…Ó¦òÀpÇw°jÕ*/^Ì/ùKZZZX´hS¦LA‚á#Fâ÷ùì7~TÇ <•ì­îpž-~¹¸RŽéfvõļÂ!%³¢gÄŸš‘—…Ú},JbŠb`91ܰ Dq¥$+%È#ó–ðä‡[¸õ÷7ò³ .à‘‡æ¶ÛncÆ ,Z´ˆ_ýêW”––òꫯrÁÐÖÖÆ !C8¤W&ªiÛv¨ÖT¥ÞvÜÖ•ùí^ï’Î’xîeãrW–‹„´ÑCƒJ°p,fï#AMÀ±mœX Ûrpéº ƒÇ¾Ç³ËKùÝõ×qö9çðôSOqë­·²aÃ,XÀm·ÝÆçŸμyó¸ùæ›Ùºu+…}úqD¶³²k8fduTqA¯¶¦Pó¶ê–5@KÀÔËqå‚êßGÌ~¿Ž_ O¹ôÌáYOlß&é×'Aæ„Ñg2ZV¬p¶eáX.Žãâ:.Žíô™<ºè=|^Áou üügÌ}æn½õVÖ¯_Ï /¼Àœ9søä“Oxúé§yâ‰'X¶l9]ó¹â„a\Ü·‘Í;)Y½“â4Çî £FÒ·çkÖnÞ]q+òS %q¸:ÿ ³ß¯ã´~ 'ŸsHÖÜ­[¥Ò? ²3|ÂìwJBV[ŽãàØqæ».ÒqI ú™3ïu>Üá——þ‚K.¹„§žzŠo¼‘ 6ðüóÏóÐC±téRxàÞxã /^L05ƒó&Æ5'ÄMíA÷î‰ä&·²cg ëW픣ú¥ežqʱ§7TUÙ˶Õo4}~[¸ŽëzøS¶Á7×mIe¹P ü"'ÓÀ×ïDV$g¼Ä¶]\ÇA:`Üù÷°6$¸ô¢i\xá…üíoãÖ[oå‹/¾`îܹ<óÌ3,Y²¤#ü{ê©§HJIåøÃÇpÃICpm‰‚’˜ƒ?§ˆ.9i¨øxe=AÙÈYgž8>AÆòß]¿s¹¦1ÇqÜ%ü1 å¡3º½FÔ—cššìS  ¥ûxô´|bmm8V»ä;HÇŶ’ü>î˜÷:›Ãçu\p/½ô3gÎdãÆ<þøã,^¼˜… rõÕWcwÞy'ÁÄDÆŒÁÍSF`(Ër®ƒt,:zz‚ä§6óÅ–6ÚjÊ9ñôú*-u]>ظgy²u¾~ï÷A}>ú‘“sÀõ‡¥ß›ìWUk1¸Ø±„˜Ù½‰µ¶âضã]Žíà:.I~“ÛžZ@¹’§Lá²Ë.ãå—_fæÌ™”””pÿý÷óÁðÒK/qöÙgÓ³gOn¾ùf|>½Šû1óøa˜Ò%±pbvÌŽƒÌÂnkFø3ñõǸÑYÔ†`ǧrÅ%SOž¥œŠ™©š"üBJø ×‹7µ04Kuñø¼¿–l±ÜÑÓ„¢ûHèw VÔò¼|ÛŶ={/]‰ßйåéED2zpäÄ Ì˜>_|‘™3g²iÓ&î¾ûn6nÜÈc=ÆÑGÍgœÁ%—\‚¢(ô0ßM9”ƒˆmƒë‚”HéÆ'5âó®DõÐ3Èö׳rmˆ~EÉ ì?t‚7?Þ‘”ª‚˜üšéÝN|ƒôîMÇä<þBÝP蚥­ë(ÏîG-OíÛN‡ÇŸ0¹îÑ0{ bÌÈaLŸ>… òë_ÿš’’fÏžÍÎ;yðÁ>|83fÌàœsÎAA÷ÂÞ\=ùP†ç‰Äl¤‹”P"â屢ê "¤êF)ßUËÃ'вúã ŸV…?•^°óCæ zpD¾ïè>y‰G—í‰Ñ¯È'l- =½±HØSý–ƒm»àJ†ÆŒŸ'mÀh† (æêk®áÕW_eúôélÙ²…?üáTUUqÿý÷“ŸŸÏ-·ÜÂùçŸ@F^7.>fó“ Ç,\×A:6®íຸ\ÇÓ2®ë]H‰kÛ¨I9dfhisñ ‡Ü^ÝÍIêÿ]u…$~Àƃç’þ‡¦&…Ü,M£Ë@lÛÁ±\lËŽ;}†&˜þÀsä:œü.¹üæ7¿eñâÅ̘1ƒ;vpýõׇ¹ûî» ƒ<òÈ#œwÞyD£QròºpΣ8¹O:-á(2þ™®ÓîüI¤ë~éŠÿÎqQt?fÀ(XáV’³sH2å!9¦z¤í’ÁØpPà†#Òïꙑ8tÛŽˆì[ä§Í  %çaE£qµï1Ég¨L¿ÿ9†M:•”`³fÍâ•W^áÊ+¯dëÖ­LŸ>@ À¬Y³P…—^z‰‹/¾˜P(DRJ‡2”óæÐ‰Å%ßéx•®D"‘R⺞/ ݸi„ð4»J‡ŽŠJƒí22Í8IBš€„Ê!<(kãºû=gXê Ó“&®ßc̈Dæ#¥ÏDlËÆµíµ¯Á¥y†ñ§œ‹*î¸ã^~ùe®¸â ÊÊʸì²ËèÚµ+¿ýíoxñŹöÚkÙµk¦?±‡ ã—£ p)½¨M H á ¤ˆgzå>9齺VÛrÐH¤|w9Q Ë`€€¾rï,à÷î t¸hxòQS¦½™“žÄ[4Êžù>‘žfŽDP"­¸º†c;àJ¤+¹ì¾ç9îÜ i¬­âÁÿ¯¿þ:Ó¦McÏž=L:•þýûsÕUWðØcñÐC±fÍÆãª1=1°ˆÚ.‰ ‘Š)®'¼_šÒi¦1ÅçXzÛ·mAUÁN°wP´¹Å^ÃÔ/pЙ€æˆ[¿®"ü÷µ;ëËÆÇÕŸ7c‚¦­+‘UH.»ï9Nžv9e¥<ñÄ“,Z´ˆ‹.ºˆ={öpöÙgs衇v0Μ9,]º”%K– ë:…}йdt!iº$³÷Sýn\Hé©þÕŸã^0€PoCó  àìÁßþߤ§®X݆DÁµÂÇÆv.ºë9~vÕoذn /¼ðÏ=÷—]våååL™2…cŽ9† /¼€›o¾™²²2žzê)TU¥[a/®œ8¢D•pÌò˜—|¤D( ]J\ò‘áý^QBÁ{¯iȶÊË›1tÈ|(÷^uÒ!1)I4D·8Ì8?bà_ÐêŠH{I5”U4;«[Â6†.ÒE÷‰Y6çÿù¦^}k>[Áܹsyþùç¹üòË)//稣ŽâÌ3Ïì`þ•W^‰¢(Ì™3!9Ýò¹ð°Á MUÇbÞ¾ëÚHÛFJœ¸ôÇ}Eì•zE€PЦ"cmD«(ÙÒĘã'òîo±æó |†÷Øm)ñ«" Ï 4ˆ¼ÌÁæHU`/DÞÛÑæœ>8er4,&ªh®ƒHÊåÜÙÏòËënâí%ÿ`É’7Y¸p!\p555Œ?ž‹/¾˜³Ï>€ÓO?pùå—™Û…)‡â°,–hÌóæH)Qâž¾@EÄÁvU/P¢z USÒB6—³|U=ƒ†5Ryê‘kIzΡ*[³†§žy޳Ï9—ššzõêÅŸÿügN=õT€Žo»ð 4ˆŸõM+†ãØ8Ž—KŽ®ÒF‰wîTE ªìe¼.Ð U¶b7UñÑÇÕäuÏdФÉ<øûßP²eA¿Š²óUüŠÊÎVgs<ük/¯]C• Èñ3ì¾3ò—(–/µ6dË#MÍn*¹§ÜHxý{tÍ|ðÚB¸õzXäôéÃwÞŧŸN,#%%…|‹.ºˆH$‚®ô0_ É! ‹¨åÆ9ÏÁSâ¡"TU ( Ç®é š® *š¡ Ùõ„ZY³>İqI*(bÎo®dwe‰ ¶+Ñ„ðBC ŠÛVCÛÚbñº†Ûø*ƒ Në—pÆÔ1Ù·†´ÄÆ[6Â'š¬ Êä›-…õ%¥Œ<û*Ö®»qåžèš"lUVÔrÛ@Â壒§NêŸv{´MÖT„ÝcŽë«Ìz§’«kyòñ‡i\÷.×=¶ˆ…{ˆ-/<ÀžŠJúuÏ#ç´éìXô8›7o¦OªÜ“.!Zµ“Ï^{[ðØÊ=¬“²Á•.ª¨šgÛ=û®`Æ=zCª"\)h(†|Ú@÷Ù ;ö^m /ÿý%l Ÿ‚†§òõø¥ ¼Ðˆ´ùä¼²ð³–üm@Þ*Ø?eéýn9®ë†îÙÉTÖ7óÜòê;ç®k~4.ú¥‡$Ÿ<¦gÒ•9‰þ;v:d¥À”‡6²swo¿6ŸðÊÅüöÅ•ÜyÓLv/z˜]»öPÜ=ÜÓ§³óµ¿ñźµôIO gÒÏÀŠñÁs“›™AFÀÀ•EºØR¢( ã40 Ÿ¡àói†Ä  ׊RÛ`ÓV$deu>YÙD~…dÎìßýŽ/¶7”º":˜®ÅßktÕ“ü–S¾Py®ÞrW Ø.=TÄÀï]ý¯(»¶·è>»Dž^¸dú1øi+½ üäd+T7‡›ÛbnU’©vŽj66B¨ÙåÐ!~‚ÅLøÓ'ØÑ0ïüc!5¯=Ì•ÍLüùÕ|>÷Nví©¤¸K6ygÌ`÷’çY·j%ÅYIdŒ=3%ƒ¥ßAZZYAËv¼v-!Q/†7tÃP0M…@@ů[¸á&Öml¦º&Jn~™i­a‡êzÝ0$¨9í~uÙUlÞÕH0 âŽ#Š'ýšð¡*RU´…ØË»"χlw…€r [€=|õà§8´«qÔÌc».ª,þ‚‘ˆÅ޲ˆ4 UH)±ldRP¡k®)zw÷±[ëͤÙo#­o,˜GåâÿÇÎ°Âøó¦³öÉÛ©ª®¥ON*ygÿ•ï/`õò)J6Éy$)½‡±ôÿÝŠß4ÉOO¤ÍŠ÷ð‰8ƒtC>Oòt‚z˜=»ëY¹º†ÒsøÖ®^Ãú/Jèt9°Æf•ܾýÙTá¸@@O"D|Äš š"IQ ê[ÕÚ—Ë#/ÙRn°Szûv³À€ï+ ðÍ“:ýØ~é³wï–¤§êtÉÖÑ‚yŒuõÍDb6~Ÿ&Ð}¨jÛ}ELþãB¢uÕ¼ñÆ«d¦&°ŒÆŸu«¹™ê†&ze&’sú ª>|…Ï>x—ÂÌ$RЇ‘:dÞõü†An—.¬ÞQFïÓ«ÔŠˆ%×U-‘IDAT«jÝóÔôf6n¬g{i+çÌøkKë¸âº?SZçƒíBßU»¹òäQ„«Ëxó¹7‘ºg´…Œ§†%ØW"„Bš0ÙP+×|P~oO@™ôö×Ä™o}ŸyÿïM\?1]ÌúE_!ÎûPŽÊÑ»|bÎÝ™ÿÐ-Û,Š{%È‚.¦pÌj“йçÅ÷˜TœÌľ¹4‡-LE¥ÄNç´ÛžÆ‡xñîëyæ/ضcfb-›?bÙ‚—èå³É;u:õë>dù’Åf§‘ܵ]Oº˜ewþ Tƒê@³ž{ƒ¢œúwÍàü^A„&0MÓ§à÷©$u‚F”uë먪³8÷·WñÐß^æÉyàz¹{!¼0%jK24zš>>¯Ð5Icw$â%xâ÷Hš†tôÆ·*£¯ÖÄœµq;¿ï¢ˆ¶Rí_& çœsfõÉ ž]U áˆ+þ€ÛÌÁÈéÃèëæª©`Ò nÌ9w ŽPy§Ú`ú]O‘á…]Q’®†Éö&V}T}Ø?îeÞž€†¸·oÿÐ’ÿ]š€ÌOÿÍè”_·5)bÛ6‡Þ=ýäw1ET¤óe!ìëÊ[ÕTpæðÌýbXafLìÇq½ÒчCSY ëW|Êá3ïcÝý׳ÛÕ™pÒ‰8z [V,ÄÂ!§ …®C.¡lñSlÞ´‰~]3ql WºÈp ®ùüöõõ'IÒTÅÁÔ|†‹sødE5ç^}Ï/|ç^[KzšâuøJññ–k™®i"AQ¤‚ˆ!…ÈÐu£›ž>– ÏVE^WÕ®d»ôŽ~i`ï!ÎÔ~ ÿ ÖVFä©O—u»wJî=½2O.Ýéà÷I&ŒNÂðûETI#¦ˆE¢X±–å`[.ŽaZqKKë(öÅ8jbr“ð ‰}FòÁm¿ ×É#w¬eÓéšdâ¿ bùëìÜUAAžIzωTo\ÆÚO?¢(+Å«âI‰k[de¤3w§Ãæ_ðçc Âñ¤ßŸäãOë:fõŽŸ¿üuÁdÏk—t7îÜÅ\Iº¡‘£ë²¬Õýì³fkcUÔ­`´îã³ôSƒ†¬®s>qa›ì‰‡xµxÝKè†ÿýÓµQ7MîòÕ2SK¶Ú2$HZª‰¥¥‰X1‹X´•˜åàĬøFÇ‘¸®Ã„2'ÇqˆÄlšÚZ(ºìZJž¹“pR} åýÛ§P]zŸq¡uðÁߟ ïAdšFk¨‚ßnJ c{½ù *,us™·d'õNeH¶‰åØ$ú5Lêlš›-Fœx2N¿‰˜¿{OdŠ3ßu%=&®-Êæî /ÀóâëF ám¶»{§ó~ŽO)Úv¶¨•Þ¹?<óÿkôKWÆÜ~r÷÷jª]UÇNL¶ÑÓpˆEZ±âL·-o$Û¶Gâ¸.®+‰9^·”`µ„È:ò,"»7³aÕJ޽í¯l~þê[#Œ7=9Mª_%9³jÔeż§)îÓ—Ø®­ØBõ 'F]¯±ÜùÈKä'ü¼ aÛ%àÓñû5ü>•÷–îæØóNäµ·?aUI-¾€èPûíܲ]É€ ²VùÙÇu‘7Án))÷ñ7ÄŒ‹,ßvVkÓ–4Çm~ÁÉßCrM±¦"*GäècošÜííÊ=Šž–ª3 O@DIFêÉžºÚÄ¢6–Ѳ$Žã`Ç't]é]ïm##Ôþ£yçÆŸÑûä ¡lÖ¬¦ {WºxŸÜ~QË¢÷ !äq‹¯ŸJ’©áZ-8®‹ŒÅМnÑ®~æMTàúCs@h†ÀçWIHÐØ¹;L05™ô^ƒ˜ó»+Ñüª7ô±Úw%ôúØÚä,û¬Ñz¨p%;Ø{,k»]Ç‘h€aKÔxx·oƒÇMߺxýÄläÿúÈÜ¡:ÕLMÑÔ7HsÈ¢¥¶–¦ŠÝ´6†ˆ´†‰E-¬H;ÊŰ- ×¶p éÄßۮδQpÎÿ±iîD³pèD–?{?~CeÐE7²õù{(­¨¦gv…?¿Ž%³®Â.é~pC­×¥‹,èËÍïlÁimbæ¡9ä5UA×Uü¦Š¶lidâiÇñÔs ÙQ žÓçÆ{,WÒËo°«Ùýì³FëŒ×‹¿oŠÿ¾ýØÖƒâÌŸo­Î|®Œû¦äÞŸa3c®3$EÔÕE°bшM,Ò‚e¹8Ž‚T P \¡Æ'©ãC“ÒÅq¥·£G‚ÕÚDö¤ hÙº–õ¿Ïñ·?Mɳs¨ªmàÈ‹EÓ¶õ¬úðº$xÍ_XzÏ X­Mä'°]‰PU¤”dfeñÄN—];¶rRa ƒ2L®$  |¦Ša¨4†,4Ó$SÀ¼WïÅçóT}{¨gKIž©a;¢ôÓ†èk*åþ«^#_¡Ú%_}ˆãç Lœ:´{Ê ÷¾^Maa@lÿ8B·Â$Ó•Dlp,‰m[Øvב (HUG(j|žÎs¶\WâFØ™]É>‘×gL¡xÊ4”ªRV/{¾£#­ÏP^¿qAMaô¯îdíü¿Qµy=½rӰݽÏYsm¶¦óêËó)H28£Wü¦‚¦©¦Šß¯°n}ˆ¡‡OdÙ'+(©²ILðæyÚí¿&y†ýû®ðB!Ø#%ÛñœºúÁüƒŽéÿ 2N’>ëã-æm©õêYqJ8ª»É]uº&ûiˆxc<;ïD÷©x(H!ŠŠeÙô¿èw¬{ø&¢4{ ï\ÿ3‚iY žz-ÿérZ¢Ç^1“=;·³ñÍ—éÛ-gæãغrÛ;k¸rP:¶”hñ–mU]SPAmƒMáÀ><òLJQôx&.¿W28ÑdmƒõP"%eñ¢Í×1ÿ ¥oäÒÅ'®•rnvR çá+þ)Ÿì&Râ/â/•9<ººCZhq¥(:r©^Û»”Þì|¤µ™ÝÅÇòùšµ,ÿcŽûÍŸØñÜÝì©j`âÕ³(]ô[·–2æäs‘i]øè±;èÓ5w_æ ¡©ì ‘²Ò2Æw ’PqmSõzùU ª‚¶°$˜èGÑL6ï®CW½®×säª ¢eS‹½\xŽ^EÜá‹ò#=ËïàSºÀÙ3é¢Í1¶†"ÿ¤ûjjjq#­ôïßß?çþ±H±OXµïãSìhqiIëÅÌ9²eà$¶ïÚÃûo¾ÃØŸ_† Õ²üÕ—8î0²ÆŸÈë·ÿšÞyÄ'ôöƒŸªjìŒê€Ã¸\BºÈÔ\vD}j|“‹”ñ}GJ’|*–+;Ô¿-%=ü:«¬·ðìþž}R¸?Òƒ¿Œ@PÎÒ†ôH zþ³º¯¼Çqjkk©¬¬ I‡qç_ÅÓõ©àØ(BüSéÉPÖÖ[Ü5ë*ËK9ö¸Ó¹zÖ=¼mv‹çüžÔn=vñLß6ƒ‚dUHäW²AâÆÂtó+Dºbú›¥\ûú–îjCW ü~ Çq UUpÎñã!Žô|¿¢` ÔìlsÖâuêþ`3ûÿy­ßW3_ºÄ,›¡F3þ`v ™y[i ÕCHlWb;³¡ÿtÞza GŽÀWKPu©o•di‚­­súµì=ºÝåGLßxuêðÔ™aG+zfuÍ¿¼Ç¶m à !!@ @~—r1·/'ÁïëØ˜á×VT…yowkÚéµW^fgé6îyð¯,]¾ë“ôÊNÙÏãÿ' n`bŸ®,&óÑê dù Îdb~[QÐ4Ã4HKó±Ö|ZÂøá]ùÙ‰ãÈ jˆ¦oì¿j(¢Ìñ>ÕØÇû¿pdƯ·ÖÚéoomü·7º®K0$11EUé?x+?YF¿Ë[œ…_<µ)ÄžÖžƒP/¦_¿nGY›è—á#ê~}ã‚+TÔh „˜Ø5£º%Ð#ÅÀB`†OG3¼P4'ÛOrJËWÕR»§‚S'¦÷Ðq”¾ÿAVI„÷„ó‡8@š6~p lZJE“õµ÷¶¶¶R__OCC---´45‘9ä0ê›Z:"‚ŠV›Uá¯ö%\‡¬€Æ5ÝZÈÓ"ÄÜoÚµâíìq(Ž#%1ÛEQUÕê“RÄóî5M¥ïÄIø»åóå+°zLÈ0ΔÐDǼ¾øÉ {²4u%¡®åë—W¸®K]] 466ÒÜÔˆHË%èI¦ÂSÿY‹´?åî‰:·šCZÀ öŸ*_!UEUUEEÕT4]C×5ZÃ’µ›£œpÕåTîÞÈ´ /cÑ?Þ¡U×dn@9>? nKÚ7ü¨Aðuí‘–·å›=‡––hjj"ÔÐHL¨HÍDW;›,>© ÿÓ‘@$ƒ[Fe#‘Ø®7‹/þ3þ{c]š†ª©¨º†®ë膆¢©¬ZSÇqÿŒå‹Ÿåú;žgKMŒQ—A‰†¨sbŒÍÐgÃ4E¤Š½<ŠŸ¬ˆ:X¶#­ß7«µ‡„žh µ5ì…Zšà™Í¡¯Ì¡%ÜtH–ÒÉ}žö·A{¾²/ó ÓÔ0}:;ËZé>p0Ñ–~zª ÍE’M­1ò}&–óMÉóß`»²«ô´€öSÕ`{È­oÚuÝ3|ßøƒC¡g C´4ÖÔ`c}”O+Ã(_J $Ürh6 qäÞ¤aÇÀ ÿV´‹§hïÎUU4ÍS÷†á]ºO¡R^i1ôȱ¼úìSÔ„!Aè|*´:65—$UïÉl¼#^Ô<ÓrpmMSd{Aºñ?8‹yÎ`(D¬±†6Ooò¤¿=¢“@š©ró¨Ll×Åeÿ)ÑöN\e?ÿóµ ñq/UCÓUTCCÓ=É×tÚ‡ôÜ ìh„Wn ) ¼>Õ›ä h‚²h•ˆ-o}›úSÕh^ÏywkËÂâ ƒ¬Äo‚úúzjêêÑkKù¢>ÆçuÑýþ=ÑP¸}\6ºâuã´‹²øö“rö_¦£ìs ᥅U½ÝáSÑ ÃÔÑuò=-ô9‚•¾OSkû  P(J0¨ »•ïÕD߉§€ÛÓÀ?M `»’k'd¨Ï®kz­©5jOœþ?<n£²²’îV mlÙÿ Áì1Y$ÞöŒ¦·ƒ@|I쳆ø«"ö«1¡¨Š§ú ]×Ð ñš¡a»‚Ö0äöà³ßC7¼ñ­v(BÒÍ4¨n_¼Ry¤Ñr?{ÓÁEwÏweä’­-Pµ|GÓ;§ NýÆîJð·TSŠPݶÒç÷‡dа¹ŸÔ‹}œƒŽ½{_rÃÅWÙ@SUT]í`º¦k>ÃШ®³ÉêšMSs ;Ë*ð›Mx¥bUHRtPXìYZ]$öŽqí;¿÷ÓͬÞq–w¶6=T\Æ÷JùÆà˜.&ßÒ¼Ó.ì—ÂLgÉû9tûáËæáË÷îçk†Žnzž¿áÓQ5•ʪ(EƒzóÅÊ„-oäË›áªÀ'5^«Œ¼"`·ôföwài€ƒ¢¹ó»ve²OQÞÚ~u]YóÚ_žÛîüË8à¤ÂDÖÔF÷ËãOÊrZQ"­–»ßÈUÄþ6^|É$ìkDÜø EAÕ5TMC3½°ÏˆÛ~UÓˆZÛvIÏ/dýò0õ½ T’TMò3 ñ¤~ß¾m)ø[ q%~ø“ê»s|’Ó‡eýË”0"×Ïàl?ŸÆ“>(HÒ¹l` Q÷Ÿmü—LÀ¾ÎžÂW0¿ãÿÔxاÅÕû¥™ž#ØØä”šHÔ–ì)Ýì ¾ªMU%ÂÑYÞ] ÔÇG¹Ú;€~ÔÌÿ6 †b­­²^_¼¶vþoÈ&=¨åßä–Ãrùó§{+‡º"¸iT¦êí#Í| |Ùø’·¿Ÿê—^>ßsúÔ¸Ôkè¦nêè†fh”ín¥ï¨¡lZµœpXbèJ|~‚Be{“³ Ø"¼ `ûÂ&ûÇÎüo €Ö˜ëöË2#|¿vö¶=M­÷QøO6`Æøž8£'³?©&êì}~3GeêS‰9û‡vÊ—A öWÿâKß{TUì ù Ýôì¿·ýº¡ÒÜ"A¨¤ôbÝûÿÀçWÐE{ü/Q¤Êçöz Iz’ßücvúþ¸«£`Ó-KöÌèp¹åÄŽ/Neéÿ æÚcºñê–KK[:˜{fq2ºq„×›':8»ûJÿ—ðUÒ¯¨Âc¶¡£ºçíût|>o£—iè†FÉŽ6úëMùÎÝT”–aêJ|…‹ÀTu­¢¹:ælŠ«ýöñmç§ ýü‡.™âSåî&{gUcؽðŒqW]@° »%²Î{j ¶ãõÛõÍòqûQ]i޹¨qDˆø¦¹’ß×ð‘À¾ šUUx!žé5y~Ãgà ø&¾f@§²Æ¦©)Æð“¦ðÊ÷ÓÒÔˆ¦íE–ŸW³uWØYŠ×û_ÎÞ…Mtà_%xl)Sýª²¾*ö¹´¬nÅYf¿ÂÂd–¯hdö‡»Ù^éˆþzF!¦.:ôPˆ÷ŠŽÜ½ˆ·(ÛS»ìŸæUEA‰k5žä1Lñ†©c LŸ/ÁÄôD-Áºõ uÖ$Ö¯øŒµ¼…aª{&¤ëD5ñæëí°+7áõþ×ðèú¯ãÓgEyd…‰S88Ïß‹dÙKvuÜóð¹½Ú=HÔ‘¨ªâ11¾[¿¢ý„ ÿ¹½ ÿwEñ˜®¨Mõ6uv0Þ¯cúMü~Ÿßc¾/`"—×3îèÁÄü™üãáÛPte¿Uïª"ØU¯:ŸÔYóuEìveÇ,¿ýSQÿðŸ—9%àÄ·MQñÐò†k-Ç O~j{uÿ„Â4&öLÆÕ!!Ñıl´ˆ‹S°ÕòÎös¯‰ÄqdGûvû •Á8P„·ÀQ3¼ö.ÝçI»á3=0ø WaÙŠ†. ¡ç0ž½å¤êiö¦RH[ëëÝÕ@­íÊúŸ’çÿ?ÑqHÀMõ«Î²²ðòpÄRO”<⢠]9sx&ï­¨§¡Ñ"+ÃÄç×P5Å;MCˆøa Jü°ÅÛÏ«z[¹UUñ.MASU4]íØÌmø L¿§îMŸ—z¿ß¤±ÅeùªzF-"cðDÜù+š››PUu¿²ªJª ÞØ[bËŽäOM<ö§ß22ˆØÒÒÝT[¿½.¶i@ºÒ7?ÓLÔ/…HÄe͆&š[l23 üq ´3u_f«šêmîÔ4mï:vÃP0Lͳñ~3`àxÌ7ü^¬ÿyI˜Ý»š9âÄÑh¹Å,¼ûÿ…Ðtm?/R–Ʋ]”lkµ_ží/Ç›ìíÔÿ)\‰m¨"Úqw¿µµõý]Õmþ4ÝíWœ…=‚n[›#–¯ ÑÖæTIJÖã={Ê^†·3]SÑ 5Î|ÝÔðùuÌ€Ùq‚&BU)¯vølm#ÙY>&œv";*Yxÿï°í(º®íU¨B°½Rs_ÞyVÀùÏ9ÿŸ‰ÿñgi€ßÔDrÔ–™†Ê˜ó'Ÿ}òàô±=ò’Ièl)m£´,Œ”’Þ… d¦èºRb;w_ ÝTEA3<ÀH¡Ž JËcÔÖÅÈËKbð˜A8‰]y{Ñ‹lXñ1SZ|Á“ŒoùR¤l¬7Ä“­7Ê£ökÀV¼ü5?вæØ'o£«‚G’ dc/žŸ"’"®Lò ô㇧ig꺛Qo[„m¯YDýÿí]MK1}ÙÍ~TZZ«‚xÑƒŠ¢w½øs½ù*è(hE±Vwk¥•îG;±A¶^[m»{ ¼73/³É„ @Eéx"K+ZÔ¢=ä@Ísí>î.zɹ€j; R}ÖûcCô-,ø¿M€2",QjX°µWå§u~øjí5Ï‘(ó4Àô;U\ưìq„ÌEwˆÎeœ¶¢L^Ó=]|c þì Í14BEZ¨ØÞ©òãúwzr3‡D¦$R© ïêÖÍœ|æ?Š”#„`ýÎHÜ\õ³Vu b@¤ Ð_¨Îo–{üYÀÔ.ŠCßaTª&€õÀ=ZõýfàìÖ}48CE‚!—…zdê9Nåco,ºC¡î \†¡Rˆeæ#LîäK0§ ›™eDð("èUƒ5ZMpòÞ“®œ y÷ÅŽž7zÖÍ…õúù&@YjðtT ­Ò7nHŒFJy=%2hÐÅTÙ`mn `FS'pã]ƒ*  ¥áéÒÿ· P6Dz3ú_úüà»ùZÀ­Yû‰}ÅÜx_Ðy¡èIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/formDisconnectAll.png0000644000000000000000000000013215101070305020603 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/formDisconnectAll.png0000644000175000001440000000114615101070305020575 0ustar00rncbcusers‰PNG  IHDRóÿa-IDATxÚÅ“[HÓqÇ?ÿÿ¶ÿÔVNVN›X ©5Ó¨XoÞz)#Ã.¾!…ùÒCïQ!¥ÑÅ‚̔DÍL#š /+/û»Í­­Íÿ~=”±™½8‡ó=_¾çÿÛ¤Ä@\ KsQñÙÍý¢I¹«¤D\œù± 4ý…²>îéAê ׿¼îð¨ý]|ùÛ;Z½“ÔÎG@Þ28uIr"Ø7¨ÉV((7aêPyДÏ%¯›“w."Ýð½z §kr˜ôLãr¾dj5 K£ñ: ‘8Ëcaö,ĸYeÆaKý‰‰½>ÂG?Q$­5Ù<#޳›tnÀ0Ãßãg‡N¢/¬a]! ï_bŸ^4 CÜõ©ìðv÷Å1²nÙSIßµžg-S” ‡(HØ\•bu(½“,ù£(ñ_™Lžª uùè<—ÃùLF‚<:5AÝjµºfZ·­óXzqçeÂn#aìv//fcËMä¤P¸Ó„üD¥ëž#ÇhœÙOÛÌÚ}ÕÄZ‹ÛÒˆ€ñ¸•ñW¥ˆ×¥Ä›ó9˜T¼|£Ë‰ÖSŠèv ÚŠUéÄ3+˜u2Ö¦\üŠUf&9‰¤ÒÌ•íi|-IcΞ‚G-@Ò‘Yô”åQeôkmONð¿ÙF ãŸ~㤯Íwó;Y±IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemNone.png0000644000000000000000000000013215101070305016753 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemNone.png0000644000175000001440000000017315101070305016744 0ustar00rncbcusers‰PNG  IHDR+Š>}BIDAT(Ïc`j`æÌ™!3gÎ Á)ÉÀÀðŸáZZÚ ¬’iii+ ’pELè&¯ÁjL'V+á”Ì@[Ž -T»IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportBackward.png0000644000000000000000000000013215101070305020670 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportBackward.png0000644000175000001440000000043415101070305020661 0ustar00rncbcusers‰PNG  IHDRÄ´l;ãIDAT8Ëí“?AÅ+Û¹„;Ðê5n Þ½†kì9 ÑM­ÒHÌ&FA£@ò»ɬ5B4^5Þ÷›7_fà¯ß(‘>ïM¤é|/ÈÔL¤Élçõƾ»96&Ì[°›Èkó‚×îT ÷rä@ˆp~.ig…$¬D ¬ïVMâc¶— °NH0©íO¼=Óõá° ?Å‚jp…Uk!‰Ó ˆ*®qCÁÖÀ˜bÒí¾õZÏ·Ócn‡(n¶¤Å{2å'xññëˆû÷Þ JeúŽ÷¯OêVÓ{J}$CIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportAutoBackward.png0000644000000000000000000000013215101070305021521 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportAutoBackward.png0000644000175000001440000000045315101070305021513 0ustar00rncbcusers‰PNG  IHDRÄ´l;òIDAT8Ë퓱mÃ0Eƒ‰Ë ‘â2‚=ÅyorSD#¸·´ˆÉNàÂíB—@ )RŒTúÀÄIxø÷I¬ÿÕžë_¥þ¡‡ ³Dä3 ú–§œ.Ç‚OÐ7àg<ø1è´è+Ð؂ֺB¼¬K`ƒvÜzЋAàËÖ'«gàXtà~ö¡ãC·¨èŠÚ\:³à"õÛÏFÑØªÀGäﳇgQR°¾£MÒöâðãÉ:>÷öŠC¸ò(Ñû_¦À¾|xeÓø*sŽÇÀU]ßçå)ŽjêË+»Q“ðIà N"šáø,nÓ§Oïfa 0IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/viewDrumMode.png0000644000000000000000000000013215101070305017604 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/viewDrumMode.png0000644000175000001440000000155115101070305017576 0ustar00rncbcusers‰PNG  IHDRÄ´l;sBIT|dˆ pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<æIDAT8µÕÍOãFÇñïDZ#^ é&EIÅV ”S÷ØJÝK«^¢ü+µêµ—Hý—*¨¢@Õ]…Œ”F@äÔ`X6؉Hƒ{h!6/{an£™ùè7Ï3²á™†ø„= ÀÎßmà~Þ¡yë&ðu¡Px³³³ó”Òn€>~B°‰hø>ŸÏÿiYÖýþþþÃîîî_ÀÀg³‚M[%}Ïç*‹¯LÓŒø¾/R©Ô‹~¿¿]¯×mþ/ËÄä“àÇèÅbñ«••áyý~UUÅÆÆFÒ÷ý™øSx"z}}ëº EÁ0Œ¹øÂ<Ôq,Ëâòòß÷‰Åbhš6_x‚~;¬é­×ëT«UÎÏÏlju]GÓ4âñøT|/ßäóùŸ‹Åâ«Ñõ-Ë¢Z­Òh4XZZ'ŽÇã(ŠB<©T*9 ¶êõú pü;‚¿( oööö^›¦ñ<×u¹ººâââ![[[är9LÓ$‘H ª*aòðð@Â0Œd$ÑONNJÀ‡ÅQ}ßDz,z½A ë:ëë묮®²½½M.—cqq‘ èõzt»]<ÏömÇáîîî£æÝK)¯mÛ~™N§?ÅbBQ¢Ñ(ÑhUU1M“µµ5TUEA»ÝÆqjµRʰT*½=88ø @€ÖÙÙ™íºîf6›M' 1ª¥®ë†A"‘@ÁÍÍ Íf)%ÇÇÇa¹\~wxxø+px›wÀûF£ñO»ÝÞÌf³iÃ0„¦ihš6®i§Ó¡ÙlR©T¨T*a¹\~wttô ð;ÐbøqzüŽ'⺮ EQÃÛÛ[ÇAJ9} OÄ3™LZÓ4†!­V‹Z­6¾þ4tüÞét63™L:Ñh4RÎE§ÁOq»Ûí~¹¼¼ü¢ÙlR*•Þ5?Æ?œžžž !tÛ¶ÿ>©?f¡ðŒ¿¦gÿ™C»ÖMçæIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemSessionFile.png0000644000000000000000000000013215101070305020277 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemSessionFile.png0000644000175000001440000000114215101070305020265 0ustar00rncbcusers‰PNG  IHDRóÿa)IDAT8Ë}“;hTA†¿¹ûÞDs•€]1>ˆhD ’B‚ØXYXlí-ÑÎÒV°´´° >À$˜‡IHa f×ÕìÞÝ;îcæÎX˜]vÍ?SoþsÎŒ`G—€3ôVXÙE·MIš\¾ÐÊd³÷€á]ahŒRþSª­µ™[X®Û¶}ú/À—Æ´¼NÏ.ç'iz¡ù<ó¥’N§ï™í"« 4xjÇA–D´ÅÈùsö›·“…#Û5Ñv€ !P±¢ñ$Ï^L²Q¬rêd–ñ Ã Žƒé¨| º-ÚÀ€ÍØèùü:פTÚÀïÙB‚+Á ¶N Dâ`¾ó~wŠÜüKnM\¹&D4ˆ€¯¶ŠÛÜô4ùÜOú­‰8Š$ÙCý(.Ó@(vùÎ"™ˆ Æb‘c“%„n¹þ/¥ô$ðà/\Ã\‘CwD˜IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/qtractorMixer.svg0000644000000000000000000000013215101070305020054 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/qtractorMixer.svg0000644000175000001440000005136515101070305020056 0ustar00rncbcusers qtractor-1.5.9/src/images/PaxHeaders/itemAudioPortPhysOut.png0000644000000000000000000000013215101070305021316 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemAudioPortPhysOut.png0000644000175000001440000000053615101070305021312 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®ÎéIDAT8Ë•S1N1"†ÞHí}Â=Á¢ºDéñôWE‘â J„¨¨(ö~`!JÄݶ§X ¸SrºKK–¬µgv4³>Cú²;gPàpY–c©uí4„ î`’Ê@DyEUš¦éêç©ø#Œ!03D$`óþ¦ÛË+”ó9 IWЂQUˆÓ)bþÿŠTG;S]+9§ Ò^ H{<•]`c"Ì– >­sìÙrÑfnwöðºzTò^á\_¶1*3w4”‚ýùúÆÅçðüB}—EyžËϲìY–ˆ”ÆÒ¸½¹Öcþ0sK°ŸÂf½ÒÕÚR~¢1} Cªªþo&öÞÿ u‘Ùq)TÙIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/fileOpen.png0000644000000000000000000000013215101070305016736 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/fileOpen.png0000644000175000001440000000142215101070305016725 0ustar00rncbcusers‰PNG  IHDRÄ´l;sBIT|dˆ pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<IDAT8Õ•¿kaÇ?w¹4¶©˜z*¡[k8Y:;. b)TÒ¡ÿ€£!¡C·VQpé`i‡Jë–"  fÈСâ ˆ¢IÓ˜3Éõ‡»Ä´Úvë^îîû<ïó>ïspÑPÚ|𺶠Ôq×™…5à0\u} Ø>¥–ÿEs­¸áõzñXì±®ë×mÛVUUµ ÃØ_XXxišføµ|/npû¸€ pxÇ r ‹‹‹ß€'À} ì®qà.p èmÛ=Š›õÈØØX|gggÚï÷³¹¹I©TÂï÷3;;K­VciiɑТ(ÍÌ<O-N¿K&“Ï€œ[ª&—€ñ•••²ˆÈöö¶LNNJ,“™™I$öq»hÍf«À#à2-‡®¹N¯¦i>€ÝÝ]B¡¡PˆÁÁAR©”R,ñù|ˆ8ÉÚ¶M__‘H„®®. ·¿zãðš‘TU¥\.“Éd888àðð LÓDD*• Á`H$‚ˆ(À ø d€O á&õz\.Çêê*ýýýí;UDd˜0 ÇÃOÓéôsµýÅjµÊÜÜÜ©DÛéîîV-ËêÔᦦ¦Î, °¼¼ü3“ɤœÚ<\[[3EDŠÅ⿚àD …‘®ë/€›€§‘q³¹u]?W¶Ñh4[*•Þß[uEµ@ ÐQ–Ó’H$~­¯¯¿²€‰{½{€é½½½ÚyJ`Y– ¿nãLFÀ¹ u ???ŸŠF£÷EéhÁ“PUÕÞÚÚúÏçß_p¸ePp®ã0‚3ÚçôI˜Àgà=ÎXµ[…ÖëîàÔó÷'Po½˜ü‰¿áIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/formColor.png0000644000000000000000000000013215101070305017137 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/formColor.png0000644000175000001440000000137315101070305017133 0ustar00rncbcusers‰PNG  IHDRóÿaÂIDAT8Ë•’KhTwÅ÷ÎÍ]p_ÐZÀßv±:u¹a{05]JëȆb)"/󊉡Œ½é ¹% ×^èÞZ]¾¦®g‹ÞÆðÃÅ4MuÐÑð=ÊQ(G"ÊŜʋñ±|ߥ?ÄŽ_ú‰ÍX˜ë)„*ø—E·oB÷u“ÇiK¤PH[",ÏgV•¬Ø½ÁûW}53!f-W=Ïqþ“ÃÜ XV¢ã*ŽDÚ ¤¢7fæ~¾f÷înöD–G¼•‡Ù5øŒßu€lÒ·³Bô,Ͳ­>Í€[@X%‰DÎÞú›}öÏ$ßÎ1T»°Y‹rÀ«´œçNj,;é±á¨ßäδÍÝ'Ö ŽD —u‹IÜÚÉ—µ!ã;« T,Å?€ðüŸf4P°kù7®zâskêÚ7ÚBò§ólrÁšAÏÆ¥¬õù<ÍÒQ´?P×G8ôÏtÑ·¢Ì^ÝX¨ú´Äài*@üÁ›y‚Õ÷ÑqpE<©r?Üà4pÈi³š\vhvò³ÆÀ^CÓ-[âX.†(¡¸wSºiNƒ3=˜_¤˜Í+#|]Uª}®ñwš§)FG2t]ÀÄ+Ez |@ PH äg“þÆ£>Ÿ‘`?JIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemChannel.png0000644000000000000000000000013215101070305017424 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemChannel.png0000644000175000001440000000055015101070305017414 0ustar00rncbcusers‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIMEÕ 7"´JšõIDAT8ËÕ“¡nÂP†¿»4„,d@¨A2A²†Lî0ó`xtƒÅ x0‰!´ )íÖ.½—%jº)Ž9çÞ›ÿœïOÎ…»46ÐʨÝ{ ¨K)š.ÄH:ÁD1òŒu‰¸¼P©¥„1|†0·V”‹Ìa¹µÂvÜ›©ÍF sØ#ŸSg ÀÁóÁv\ÆovGUÏÞ_SÑwGxJÐM>rjŸ²ù¯áÙSEëÃziQ­”hw¬—Vºðü^xLXBðxé+äd^ 4¿ 6†ß2›C`ûïEºÿ¿Ä°IìߎòëIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/editCut.png0000644000000000000000000000013215101070305016576 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/editCut.png0000644000175000001440000000163515101070305016573 0ustar00rncbcusers‰PNG  IHDRÄ´l;sBIT|dˆ pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<IDAT8½”ÍKcgÆ77±+©VÒ)BMaft‘"·ª¥.B](ŠY˜]ìf6.º©Ùv-BºšÒ…ÝôJ—.B i+H-I®1&—{¯ÉæãEÞ´1ãtÑ>pïùxî¹ÏûžÿÔ~EÆ\ÿ2öFbp>`^M›~ÀÙ»*ð8 =O&“V$y<Þ‘ö$‰¼H&“V(z<ÑàÆ€`,Ë!D"‘¨O÷¥=M$u!„ˆÅb (kîÀ1‚¼Y(jš¦-,,|Ì3@àsMÓÆòù|u” È[€Ïç_(ŠB8þŒŽ¦þ­­­%EQÄEÀ’5÷ ÀÊf³v^à °¶¶æíúeŽ-kî…BGËg–eµ…ÄâââoKKK¿w϶m·g2Wé'qŽ oÖååeÛï÷«›››s½ º®·éÈ0TãaÄhÅt:mûýþI€p8|çæÓé´ eî[IÑý ßçóýP,ÿ–£ ]×Û³³³ßóÏ `Øå9€À»¹\î:[Íf“ÛÛ[®¯¯©T*ìíí™ù|¾Dg*Œà€øtggç—R©ÔªV«Â¶mQ.—ÅÙÙ™8>>§§§"•Jµ¶··4I~ý£èfVVV¾>88Ø4 CÉår$‰ÖÔÔ”C×u2™LkzzÚáv»ŸÏ÷èüüü¯B¡ð+pKÖý¿ £ÑhÈ4Mt]Á`0yqqaÍf-MÓ’ªªŠjµÊêêê—À‡ýM¾yssóÊ4M<²¼¼<F'ëõ:.—‹õõõÉ““ŸÛíV ÃÀ²¬¡Û­ÿa+À^¯÷«ÃÃÃo†ât:±mÛ¶˜˜˜Àãñ ë:årYìîî~cšæ@©WŠaë®Q«Õ^¦R©æüü| R©¨WWWµýýý㣣ӹ¹¹MÓT èÅãñït]ÿ ¸¢o_ Œ¢ô¹€‡tú{t&¬ ãÀþ É0â.ÒTÙM·Péñµ¥ýx öRmƸ IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/qtractorTracks.png0000644000000000000000000000013215101070305020204 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/qtractorTracks.png0000644000175000001440000000041115101070305020170 0ustar00rncbcusers‰PNG  IHDRÄ´l;sRGB®ÎéÃIDAT8Ëí•Á à EªncùL×0c4gÆ‚Yè"«€ÛBr«%dé#};yúøW©í]`æ'`®65ÁIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportPanic.png0000644000000000000000000000013215101070305020204 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportPanic.png0000644000175000001440000000177515101070305020206 0ustar00rncbcusers‰PNG  IHDRÄ´l;sRGB®Îé·IDAT8˵•ÏO$EÇ?U=™Åd1ô4r#Ä ‚†2{`dh†Dþ{ñài³'ÿŠÍz7ѽ20€œÈø#aYŒ¡»g˜´»‹,3vOWy©1ãn<ØI¥Óý^êu½ï{þ§K¼Âþpìò}fV¨ÿnÇ*•ÊCÛ¶ÓZë¿|ƒ Hfggï{@Ô»77@Ç*•Êã¡¡¡LÇé“““´ã8À÷}FFF”eYI†\.wøð€¤ ‘×€ïT*•Ç·¯®®n÷º®{¨”’J)éºîϧ§§©(ŠzûûûooooL}¼nð 0Öl63Qõ(¥¬¥¥¥§@SJ™!4‹Å)¥RJYÍf3ÜÆ«} ÿW*•‡ÃÃÃi­µôú^@t‚EE=[[[ÏÏÏoù¾OGÂÞ1>zssóÃöžç‰ÅÅÅýr¹\L’Äl—àç333Ç¥R©èºî÷F^˜„ö½Zk©”’Zk±¼¼|´ººú®ã8är¹/:k£õzý‘‘È ”Ѫz€”ã8Âh<mÕjµðp ü¨N°‚\.w?•JµJ¥Ò{®ë~×Qíf´²²ò´T*½-¥Tóóó_Uà¸tª \~­Õj±ã8=@T­VÛ¶õÞÞÞ´)ëÄ÷}Ýjµž;Ž£}ß×ÀKàÐüu ÐÝ:Ö@5NÇBU.—g …Âçõz=Bh­µðPFÍzi’ð¦¹ÿübªOýÛ±Œ¼úã…y×oì/Lt¯›@^tDüÊ™';ŽJuõïëžÿ6šþ•5Á''Jä)IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/clipEdit.png0000644000000000000000000000013215101070305016732 xustar0030 mtime=1761898693.057007007 30 atime=1761898693.057007007 30 ctime=1761898693.057007007 qtractor-1.5.9/src/images/clipEdit.png0000644000175000001440000000032715101070305016724 0ustar00rncbcusers‰PNG  IHDRÄ´l;sRGB®Îé‘IDAT8Ëí”a €0…ŸÑQvOtLïb?V±j«Í zð~8ñsð«S À攚,y“ƨ™Yé\ˆHjEC ”ˆjIxÀTU«ð^ðB(¯š 7Ï—šÃ ñL籪æÓÀûüy*<͸’ç'³ì+6§J O côÜIÄžqë{à–_¬Õ¿^Ö |ŠTSŽÖIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemAudioClientOut.png0000644000000000000000000000013215101070305020744 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemAudioClientOut.png0000644000175000001440000000041015101070305020727 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ *53Ü/ ˆIDAT8ËÅ’Á Ä Ç(]î,.R“ï„“rî‘ý w¼àO‰™ùS³™±¨jÚ\kØ;¤&»3îä¾""¸û°‚õ¨êéÑ Ú&k(§ÈŸ\ŠͲ!¿ e:İ [_ca;k5ÓW†hž^áʵ´o™}Æ£ ༩/Î 5!¨(ãIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemGroup.png0000644000000000000000000000013215101070305017150 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemGroup.png0000644000175000001440000000146415101070305017145 0ustar00rncbcusers‰PNG  IHDRóÿaûIDAT8Ë}’Ohe‡Ÿ™o¾™ovw²Ikt·­”niSi‹ ‰‡-ÁÙ€(æ$жT*‚5¥”"´JEkQ¬7E²bkÑjZ1mÐdÛÆÄ¤uógw›ÝdwfgÇKµM-þn//Ï—‡n‰ëºòæ9‹E“ÉäS™Œ{ääÉÏ–{{{ûnÞë·ÀëÝ®ë*€x<žN$>üèƒ÷ú•)ìR©Ôí8Î}ÿ0ÆuÐð}g.—Û–Ïçgt]ßÜÞÞ¾{ïÞ=‡ö?¿/Òqg«váÂWgþäÑG2/Ž\×ý-›ÍÖµžžgrrò± hlûäÄñ~SúñŸ†ûö<ݹµSŸšúƒÓg†› ýÐ.2éqÖÝÿz†‡4¥Ô[oxí¹—úŸl3›E0 æù”çùah„ŸÎ’¼{=}é ¬•ÓXm]D¶<û*ð&€¡”ª¼òBß–† Ñ©,×8?œãÔ£+ÎÓw³iÍ»ˆÔ¤VI74M ÃÚUª W¸2soÏŽ‘_ x8ÝÍWÐj£P­ ¤@JÃR« B¯Â×Ù!pºØ˜ÚÁ3] LÁr)ÀŒ( Ã@Ó4·)ðë+äÿ*°ëÞ ú0v}†H)B ÉHç¿ußãZ¥Fà-("l!¦i"¤ul£MÓô0 o6:!p³+ŒØÈ8‡@!“%c"‘ ¶Ãªg„ !òóµ‹ÞŒEo²ýÙˆN€s ’$A’%(Š ºaB Jf„·nª:Z2¯pWw¨†!VT/‚?ËÓã‚ËeÀ0Mhš]7`z²HHZVú?ðúš²Ê§ŸX¼c*­ßŸé_{ø³¦×kÜÝÒzùrÍúuX´¸ü>Ž@¦)¯Ùðýã×/°÷H[JÏÈ–e)===ÿÀ„²ù©Gv›.¾Œ BtÃu+ù4N ECCC4˜ý½]üBk˜ÅZíàÑ7®ëºžY\\lTUU];8ÃçófÍ<šŸÒ4m´Ñc´üêA4–ÑÕÍôýy~l;Œ·™83œíH; ÁóAµDëï·8î4ÕšK²ÛäPO""ÔC¨ù tm¿¦&™?µî4^oxÔêQ€ˆPvJ,¾™cei…ÁŒE.—Ëg³Ù®……¿m •wK<˜½ËÖÙu(ÁçUØØØ˜TJ]µm{&r_í'ðü¦_ÎñuduÔ Èu1â™øÓ4s´ªpx…ð$ð¸j ‡µ‰µ.¦l¸~s~%ÐûÉB=<àÈeà)¤î¥ðh|R_|$&5áÁªAŠTìÅ€‚¥µÓvÚ™¶sŸÎ9go(XH#à~Ýù¿ýg­¯E8ç¸ß!„Ð/ÎÞ^^Xν4˜=ÛÑÑaþ§æ~àηŸ_»mKÓW¾"gMt* Y’@Eyb|bæõ'÷ýú››CŽ¿Ú7::V:øû5˜¦J,84V¯®g3‰Ü;{^è|w)-ý/·Oµ4¿—ËåK#Q(² Ë´`&’É.^ü™ªëøìô‘}&„h+©jŸ¼µ €Y0òŒ‚‰\nƒƒ¿ n§ôñ+»w;ƱßåÖ’  Ï‚g`Ü‚aX°L†l.øôxiõš@çÀÝT$²¬È(ä²HÍÍ"—Í€0Ëb0M€[@lz vž%„ Î2ƒ–YÀèÍ$çæÏf0_ÈC Œ1˜ 0‡1Ÿ->vd߯ç’©DœãæÈ$fçÒ0M „¡¸(™l–¦Õ¸X,.E$„ˆÏ=½òÙøp ¬¢³‰ ô"'·Áä6\EÁ$ÅŽdš#ž˜_ÓÖÖ¶ ¢Ñèõ%Á{ÚשúŽþ=Ž`õ¾±q%™Ž%ÑüD+ ¦ Ó²PRR‚™D2JJ\ˆÏ¥ªÃ¿}çΑHvl«}´-¼êÅ·Åžâ–O?ÿéµ]?¾Ü74´mÇvT×ÔÀåq!5¯Bó.ƒ$IH¤ lŽbpÎ1K¯°,ë aÿÖXU\eµË—U=´·lSc¨2¬’m¢Ú›I}rªë›÷ ÆRÕµ5ЋKa×ìPU n¯‡ªª"Ÿ33c‘†‡‡åÞÞÞ¢;àîïûÒ†¥–‹ŠrºaõŠz/_™MÎÖxìÏ¿¢—?<ñå©l.“Óˆ¢ Aàó— 8g¼:r#ë6l°÷ôôlf!$°nUùqBi1oyA¹g¾`@å9Aò>í‡ø÷¿õæ‘]—®\÷;¬kâÆÍiî÷ÌÚº•Ǻ»»_ ¾õë×wvvvþz»yâTü>/ü~/ü>/T»:½ÿþ ƒCCc‘H$yðåC'dYNB–wâæv9mEEv”B”PB ‚@!Í ²,3MÓ˜Ãá`çÎ+­¬¬¬ž‘lJbf2‹Åúãñ¸­­­¥R©+e­­­<•Jõ÷÷÷—µ´´X‹ãV  îžú3Š¢8ÛÛÛ×êºÕu}n©†cŒôôô üã!"' LIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemAudioPortIn.png0000644000000000000000000000013215101070305020251 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemAudioPortIn.png0000644000175000001440000000031515101070305020240 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®Îé‡IDAT8Ë¥“ÁB! D_´0Jcg´b%ëá+Fgt î…pÈc“+`^ºQPcè@«Ú#Ùéô.lB$l× 3¹ ±´¹¤XÒ€ˆŸ3«Nf ^NÏåHòy×V?>œ¸4•ž^Î7ÅäÙÄa›ˆXÉûúÙ¦#Šj¿ÊÙ‚¤Ý᯼ ÍtúśÙIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/viewMessages.png0000644000000000000000000000013215101070305017637 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/viewMessages.png0000644000175000001440000000056015101070305017630 0ustar00rncbcusers‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs  šœtIMEÖ ¸RÍãýIDAT8Ë픡nAEÏ#ü‹@ðý|’¤b7)_S‹àšR…EW´†TôªšÌTîÞ f)a˜Ý„ÖpÅ$3;9ïî{™ WYí$“Z‘æfÇÁ™´!„a¨¶‹@fjW$˜L|Çëö¾@‚éô)Éèbqû[aOu°+¹Ÿ=˜”î£Í¹{ééaôYûÖ©;.ÃïegÝVw¼+΃/Ï­2sÚ±+ƒÝÿ:nþN7IÃó—kÅö’å yxôå ‡7|€Ràƒõ;.Ò½n¼FµA3C»P¹6¸HŽEÒíxk—n{tÕßèq}ƒÁ¦àIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemBeat.png0000644000000000000000000000013215101070305016727 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemBeat.png0000644000175000001440000000022615101070305016717 0ustar00rncbcusers‰PNG  IHDR+Š>}]IDAT(Ïc` Ìœ93„äÿ´´´ L„L Xù&9sæÌÕçÎôŸ$N78;;ÃÙŒ iii+îÞ½TVV^9kÖ¬¸˜"&I½$î—DíIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/qtractor.svg0000644000000000000000000000013215101070305017047 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/qtractor.svg0000644000175000001440000003327715101070305017053 0ustar00rncbcusers qtractor-1.5.9/src/images/PaxHeaders/editModeDraw.png0000644000000000000000000000013215101070305017545 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/editModeDraw.png0000644000175000001440000000175515101070305017545 0ustar00rncbcusers‰PNG  IHDRÄ´l;sRGB®Îé§IDAT8ËÅ”MlÛdÇÿ¶c;NÒ&ëÇÚ5k¥¬”1ÄG:M* !NÀeŽÀÞhÝ b‡uœvAšú²‰ Ø$˜ªŠ!11è>`¢M´nkËÆÚ*]Ú®©Û4i>êØ¯ýrH3²5Ùʉçb[¶~ïï}ü^5ê@ÿØ´Ã/}óéñé:ØÅïc(ÇJª|u¸ðÚ;xóÙg[òkëM3÷›‚YÇL[Ð|ªÖss öD°«*Æo/vœùü×Þù™åfÑJqΦÁîy|øÏå*Cï¥ö ž¸òþå¡=Š[>àºÇ§NÛÌÎ5¶úÙØÕ©M­ „<¸§” ÕŒ›F.þõÆ•oí³Š,SÈ/™†5À(OLU•âápb1€^ /ƒº€gÉíQ’Œ9ºiX³5³SÎ9‰üÛkJéCˆWGuËEͫت&·möªÚ¡6€ˆµ G8L P BÈæTþ£–ϯ¥˜e»V×v­Ì—U·¼;jRƒoú:Ÿ{˧/t*ÌÉ–¡” ‡KÖ„PJ#¨ˆ‹PµdbuÛ…3£Ý“Ñxo*¹öüÜŒÞ!ŠbÊ[ï¾?=~ôÝG¡Ýݨ„ÒªqÛ+ @êÜä%Ëb7Œ¼lïjÙYÉ=3<Øw¤ô«S?È·%É­ ÎÝIª=åœs>0‚à„®y_‰wþrûÎÞSÔ¾På?žÀæµLºvüÕ·?Nü}s®GŸO7gVò³FÁüŒYö@©‘£ÑXÍž§“Ç:ºZ&[ýKŽítÌMëû³Ûl挱lMnÉvóö7ÞUž/òÙñùÞ×÷œRÔ¾a€¼ˆÇÙòè/Y׆–ëj¾Øþáñ_>QµC—òÒC©¨Va B AÕHm+£ñÖ©[ó@H0jIiaË IDATxÚí½yd÷}öù½û½~}ÍL÷Ü3{Ì콋cq,.RvT%ŽÊ²%&’+tTv*¥”+‰Ë‘M¹$[qbJTlQ‰bˉc%à.޽ï™ÙûžéóÝwþx¿×ó¦w$@À.º«ºzw±Õ;xŸÏï{|¾Çè¼>Ñ/ÒyËçup÷€ÎP@Ém~¥>£Ûà'F¶ƒ×:ÙT&õß#|!€|êïùôïm߇¶OÒ!ÀÇ÷ħA߀PðÏ èðûöR þyÀé÷TS¸°ô»ÚIô¡ã>˜gG Q€ò¾`@…㸒$Iƒ¶mgŽ92å8Ρ ðDðީÕê<ÏYó<Ÿ`øOð€7Øô;j5úûðƒr¼ÿgF(!¤EÑ¿E±DÜØØXY¥ù|®R(»––Ù¹¹9ìÙ³‡,--¹ýýýÜÊÊJ˜Ïçç&&&ŠõzÃ]^^žô}¿ à/”|À€KIÐN„÷M‚Ž xï~>yn!äÓ¾`a˜C}}}ë?þØ/Š¢4¶gÏž]««+l__Ÿº¾¾Î‹]Ä4-†Ea±Š¢(Š˜l6×¥ªª|äÈaµ¿¿_×uý!Ó4G†Y‹¢HàQ—@ÚÉ÷}˜;øQË’(j2BÉüµ/|áówhhp¿¢dî][[…a,˲Ä0 tuuassû÷ïƒišØ·o¶í@’D˜¦۶ɨÇ" £Loo¯Ðßß/©AôW*•<à"uä6.€tðŸzA’‰ï¹Éƒæôø3íþûïÀ§ËåRîÌ™³ÜèèVV–qøðT«ìß?Ž 088°,A¢R©"—Ë‚eYtwwƒç9¾ïˆ Ë ¢(bËå’ÔÓÓݽk×(777'A`BºÔwH!±ƒUèàÇõõï@ô…/ü½ÇûÔÏþéá#==5y¡xüø½r6›¹OQÞ²lÒ×× –e‘Édͪð<===X^^A6›Åææ&º»{ Êå°,I†<ψ¢€LFçyˆ¢Q„a€l6›ëïïÏBöonn>BI8Ic€+èà]ÐSè!¦mF¿ùû3’¢þyFÎŒ•ûz<úè3Ã’Ä ÿà¯a÷î=8wîöï߇ïÿ5ŒíÅÍ›ÓèïïÇñ(—K(•JÈå²EŽãÂó|A€€e9 €eYðâ\’a!I"B†0ccc9Ïs­z½ÎAà0i`µ½;ø1±'½}E²±YÅïý‹?þk››Æ¿á9¡KV$H¢€|!‡¡á½À•ËgÑ?0A188Ø»»»FA£Ñ€çy²¬@xtuuAE°,Çq`YA@†ÈårA"°, Žcáyúúús¢(ô,..Þ` Àå2ƒèGuÜÆôºýÎï|í?[Xªý>!LŸ¢ÈeŠ"A’Dd2 öŽí/JX_[ŸÏó`×uÁ²LÓ„(Še„…B–eƒçyAŽcÁó!à8aÂ÷=”ËeAx£Z­q¾ïÏÐR– |/1A‡; úÒ—~íp¹\þa(Œ”ŒŒŒ,CV$ȲY!I޽GŽƒcë$…Bv<†aຓ( (‹p==ݰmÇaX8Žb±ß <B°, ! ¢(Boo_·¼¼¼@ÀÍ”%ÛÜéà= c»vÿ^.§O­G †…žžn8Ž BÇqÐljÈdT2=}Ó÷n-#w°Sú àÿyàãO+ŠBòù  Iº»{P«Õ!lÛÇñ C`Y&éêê–gffæ,Ñ`0Ñnk:h³¤={v1 ÃÑjµŠF£F£Y–ašöï߇—_~™úè<ŠyÙlƒÏlehÎÞ× –%(—{qéÒ%<ÿü ˜™™ðh4ꘟ_@¡P€aè( Ø¿ÿ—022‚ÙÙY8ŽÛ¶À0 º»»aY‚ @½^§J!O …sáÂ…äÿcšÀÃVAÇü° 9pààßÊå²#¢(¢«« Žã€aX\¹rù|•J×iÎÞƒ¬*¡ØUDFàX KÀ’80dX‚ 06¶Qᥗ¾ MkbhhW®\Å3ÏüŠÅ.¬¬,ãèÑ£xã7@A½^‡i8rä(677qèÐ!èºÓ´ iMø~lDQ@ØØX‡$IáÍ›7›®ëö˜@\54SYÁ-Ú@‡ÛÁAøÝþŸ«Vk˜››E>_€®ëày@oo}}}(•zðúë¯Áó< CytwwÇ–€‚O–eÁ2abpp¹\ªš…eY8{ö,2…º‡ †P­VðÐC£R©ÀuÔj5¬­­"ŸÏ¡Ñh`ppžç!|èºMÓ!Ë2™ ÂØ¶-­®®NXÐÐDÜZ浩„ì`þ‘ÍfÆÇ÷¾P.—ÑÕÕ… E†!&&&ÏÀó<ÆÆÆðÖ[o¡ÙÔÐÛÛ AàP,À0,»–!±%`âG=00€þþ,..¢Ùlà‘GÅää$$IÂúú*66*(òX__Ãøø>”J=0M]]ݘ™™…ç¹ØÜÜDoo/BÄ*t](Š]¿>A¨ùŸ¥±@büޏ º»{Ë2£çŸÿë÷,--|~qqËËË$ år/zzJ(—Kh4šPóó Èf³(•J8uê0M ]]ÝeÝÝE†mO ‚ ˆ ª •Ê&TUÅÄÄu<ûì³´Wgffáº.VWW‘Ëå000ˆz½Ž]»vcii¶mCÓ4 µ F<ÏyW¯^M¢þÔ;ˆC¤ 0²kWÙ\Ïæ§?ÿW‡á.–]ççàûÞyçtKø)—Ë 'NÁèè(^ýu¬¯¯cdd,Ë¢¯·„)„aœ‰yžR©„ÞÞ>¼þúëX^^˲˜™™ÁÐÐ0††±¹¹Gyóóó X^^Æææ&EA†„išp•JaB’$ø¾Ï\»v͈¢è:â6ó:µf›è ŸÍõè ýÅÇÿv¹·„a13=l6‡b±€R© ˲Ðl6`Û6jµ*EAp]cc{qñâEÌÏÏcpp,Ë X,‚çy„a„0 égßP(ÑÝÝ Ïs144„+W.CU3˜ššBµZC__ÖÖÖðøãöär9T«U4› T«U°, Q!<Â0€¦5áº.æææBÏót* W©°v¨t€(J–ñ<7xôÑ'ÿË|¾ðh6§âÀÁÃè*–ðÒ·¿F£†a Š"Êå2§0‹  ( xžG¹\ÆÕ«W±¸¸„ÑÑ]´˜§§?ÜF‚(Šày>r¹<Êå^T«U¬®®àÙg?ƒJe³|Þ¼yŽcaaa½½½´ AàCÓt˜¦ðÀ² dY†ªf¢¹¹¹º®ëy×l¶eÛÁ"JaC1wÿC%›Íõ¨™ DYÀ}÷?€ã<ˆ“o¼€`rr’ê&TU…( "pEÉÀ÷}|úÓÏàå—_ÆÍ›Ó8pà|ßG©ÔÓfBÚïwüd2*Ö×7pþü9hZÓÓÓ8vì(2²¬àСØž¾ ß÷qóæ ¸®×uP*õ B0 ]7h™˜‰‰‰À4ÍkÖ¨¨ívDþ®ë‘§?ó3Ovw•¾¬ª*Ôl%Žc±oß<ýégqêW‘Q2ÈòX^^A.—G­Vƒ †¡ƒaqøðܸ1…K—.aÏž½‚¥R7Â4«H>ã Ã÷ô÷bÿþ°m/^„®¸~ý:EFEGŽEµZA¡PÄúú:ÖÖÖ iqŠÊóA@¢Ë—¯zŽc7h°I  §¤á$$P øØ‘ãÿE6›{DÍf dÈŠ EVÀr,FFwáØ=÷ãÿøßÿ-dIBÝÝEضƒL&Žã I2D1–kMÓDoo/._¾Œ³gÏb÷îÝà8Åbž‚PPÓ1\®€…¦Õñì³Ïb~~ÝÝ=¸rå2êõ:–––[=ƒ„‹]Ðu¶mÁ²âfRÇqœ .èaª´&°‘"@zºèoZÍ=¥R÷ððØ?Ïæ²]²,#“É@’%Ȳ Q’À‚±ñq<öø“ø‹ÿï?¡²Y(Jp…Bš¦ãâx ›ÍÂ4 ÜÿqìÛ·gÏžÁµk×°gÏ^p‡îî.ø~„ †@E4&<ÏC¡ÐžpîÜ;¨ÕêxôÑGa:öíÛDZÑh4°¼¼ŒZ­ÏóÀq,$IÃÄ_]o²×®]·\°ÞfÌöTíœ~DÅbùáÞÞÁ¿¯ªj”Édˆ,ËP2 $I†$Š%06¶>ö®\:¦ÖD†eˆIŽ-C­VEµZÏ XYYÁ/üÂqêÔIœ9sË2(—»á8>¢h+ L>}ßÇàà–—W°¶¶„©©˜E„°, ÃÃÃ`Y¹\žç¡^¯Á0 0 –e°¼¼âÌÏÏû¨&@£Š`Çl¥„ȲÈV«•ßuûÀþýGˆ Ä-_²LÁEˆ‚žà>ŠÅ"ž|ú¼ü—Àr DQ‚(ˆð}ªxõô”@ÁÆÆ:VVV000€sçÎa~~»wï!@OOÇO¹°Z–…ÑѽÇÅ]Ä™Œ‚ÅÅEšjà8a"“Q[£žçcuuÅ\YYehê7E PMÅ €°,KÂ0 }?øÌÃ=ü‡†ûñƒ“¯ãž{€( d ¢$BD"Ïî±,âòp}êILMN¢Q¯çx¾C×Ñh6Q*•»ÇŽÃÚÚNœxgϞéS§päÈQð<‡ÞÞ2,Ë¥ÀG­Ì ²œÃôô467×ɨT„*Áqxž]×i§PØš4:}úLdÆ<â.á”0RøDg„B4äB¡ðû_þò—÷>÷ÜÏ`nvçΞơÃ÷@zòãš;Ïqàx,Ç D„¾¾~± ’$ Õjsss0 ýý*òhš‰ ·YA˜õµwiyaŽžþ¥TêWßAþ½ûç†!AásÏ=÷÷8ŽûŸ–esù< ˆÁ'„´N¾ãºpl–eÁ2M˜¦ ß÷„!êÇsœ?08HDžû×®]Eww©UÚÂXG+à~<¶ïÞ:õè¦ÏóZK Ç˲pa “É€a8ÔëuÌÏÏa ìÚ5„FÓ„®›1é(ÂBD÷ôé““Ô쯤ªí¹ÿ-§ÿ®#!„ðŸ‡ëºèêê‚ï»e¢× 6+ ~ÃЃ?üÃßœ›½™˜þtä߸òÝ­h ;;vl´\.ÿ%!¤_ÉdbŸ| ?¤¾Ô¶íøÔt]߆AÕ;­Lº‹z­ÆZ¶í©ŠB–%%fvv=¥^8Ž Ã´¡q£¦eZ°míáïííÇø¾8{úMDQ„\N¥ £â`Ôuã Ñ4-(J¼Ʋ,,..ªšc ‡J¥â}íkÿrriin‘Fý‰òWßáô¿ëª˜»„æõ¬çyቇ>Fј¬(P%^¾Äqñ &M }ß§4 V|]‡ešð\—Ž|Ç¿!Ð ƒu]×ËçóD’$¢ª*E©)dÔ Ãh5hZ–Û¶éÛ‚®iÈå‹$ÏŸ˲0 <ÏÃ0LH’ÏsQ,v¡P(``` Z[[³¦§gQ”ž™ZmÝøã?þƒ› ³+ô™¶´¯Aó~û‡þm'çN_º%c(sìØ±?ð3²¢@UU°I´ŸòùžçÅ{ùL†—W›Í&tºŒÁqœ$“ÇÅ}É*7YŽ¥`YQ°wÏ÷ðáÃÜ®]»UU±ººŠk×&1º{_,ãA<F˜Ö¿QaYœ?û6¾ý­?‡¢dAüó²qƒI½^ |ÛvªŽã,8Žw²FÝl4êïçSŠ_%eþõOX–%A —ËÉ<òÈï3 ó+’,#›Í‚常ƒejö]σkÛ0- f¾¦¡ÙhľŸîícY–vÚòÛÀ—¤x3Ã0PUCCCÞÑ£GÙ†‚«W¯àíwÎaÿþcñ£%!qÑ(YïwÇ™ÂäÄ%ÌL_@"]7¬jµÊB´¹  hÒÑc§d\‹úûJÊì'@níü¹íöPîNŽøãí[ =öØÍ0̯p<\.†šmÂ0-ð½|åkš¿ ìݳ?ýÓ?‰‰ |ë[ßBE’$A’¤ÒVêõ:éÒ%/n˜ÑÑ]XXXÄ+ßû6¼ÇÒ5¯ÌÖú Ä{~Là jõ¦6==oyžc4›Íz šÂÕé¯u tâÓ}Ü™©ÿ®¥r~{'Íÿn#@²¾üÔOýÔà`YªªTKdàx'øÌ$àÓ4hºŽF½EQð•¯|{÷î…ïûh6›8yò$ZÝÁ”Š¢´À„¸/Àu]èºÎŸ9sÆ][[ãÆÇÇ™‡z››8yòeìÛwHl©hÞoû››«ÚòÊBu}m©fÛv’«)0í L ª™"@šnêïØØ>ú#-‘¾ @)‘.ÐøIDATÈòòrøä“O>&Šâ¿dY–ɨj,ò0L¬ðˆèÉw\6y666àû> êó³Ù,zzzP©TpàÀ¨ª IŠçí3™L‹Š¢@’$¢/`(ŠMÓÀ²¬pæÌ§V«ñŸúÔ§˜¿ñ7þ&êõ…wÞþ>FG÷GÍf]_^žY­7ªµ0°5BH#Š¢¤Zg¦R7›‚ê¤~ï¦Òºäí¥HѾ ?ЏÓb¢( 1M3|àFK¥ÒEBH.›Ï·|7a˜ØÚF| ~ðÕëu\¾xÇC’8Ž×uñ¿ñøâ¿ˆÓ§Oã«_ýjÒ$Ò^UÕÖé·rmåíqŽ 099i ²,3ßýîw½åå•uÛ¶7€ˆÖäIˆ¾™ªÔ¥Ávwøt±5Ø™˜÷ xzEì{Z' ‰ø£{ï½·¿\.¿Â²ì~5›#X¶UÚMÀ·¶eŧ”a°¸¸ˆY= osÄÓ +Ðr1ºººÀ0 ²ÙlkÅ«ªªˆÓ¼Øü' $i9é*2M ÃàÍ7ß´®]»Öt]w‰o úYIhЭ6ð½Ø¶¯yKƒŸÞbç%Ñ?ZÝäN_’$ضûï¿¿T*•þc†û3Ùl<¹“ªëGaÏ÷[+Øëµúû ¦¬Q„r…{£qîOáØ$IB±X„ PUuðÙl6Þ¬(-ÓŸ€ŸÄI¬AU½°Ñh,º®;OO&sª)uÎØÁħM¸—<Ä­7†µß Ð.òÜu÷$àÚ³gOahhè¢(úL6›…¬(`ò4ÇŽÁw¦ /»wí‚,ðøóË,üÃ_BNuù½ÂZ½ŠÈ3Qìên™üøÆìvðiЗ>ý´Ï®ëÂ4MÔj5çßøÆµµµ¤"7Cóô5J„*¶&uÁ&‰Þ¶@Îm;ý^ÛÉñî7Þ5H7u0÷Ýwß?&„üЬ(‘’É&U؉€–Ï·- Ífû÷íC__þèþ¶©ƒÅƒ€gÂ©Ì ¿çôå+€o£·¯¯~b Úý~ZJÐRùÖÖÖÌo~ó›S†a¬S]~ŽªsIe®FÍô$²OG÷;™úæÓ¬{?öÈçóÄqœè™gžù –eÿG*ôxÛJù‚øÕZ ûÆÆqðÀüöoÿ6,Ûœ°yèè:„.ìµIÈ|!ÒÑ?0ˆ|>¿-øKƒŸ “$龜4??¯½ôÒK“®ë®Q]~[ ™•šmÀ·Ÿô°-û@Nø’R©D666Â'žxâ‹’$ý;žç‘¥¥ÝV‘ñÅNŽë¢f¿T*£/àOþäã.Ÿô«0œøï€Aþæ¿Â¾¢…L6ññqضÝÊù%Ú4ÚñÇÅšXç¿zõjåÍ7ßœ Ãp [íX«m¦>ݓמª…·ñã?1Àï†aH†Ñ£=ö¬,IßàyžËåóàø¸)³UÖMÀ7-˜¦£õï|ç[·‚Ÿ¼ä!ô|Oîe &FFFðÌ3ÏÀu]¼úê«èêîni iA)Š¢(Â믿¾tåÊ•¤¿@ß)ðiÖÚ¡.» ž¢ûA×ÅQI†8ñÈ#G3²üg Ãp¹BÏÇ}wTâMŸ|Ó4àºtÍÄ7¿ù·Àž^/>=?Œ×»¸® ÏóP,ñó?ÿó-1)?‘’\×ÅK/½4{åÊ•zâ§©ÙOºq6S•¹öŽÜö+]€÷yÛ×VOù8}AD>øàጢüû.‹à©¿O¢pÏ÷[¸Eµ}Ó´Q©ÔÑhToû>|¿úk_ÂÄä,ËBžŠH«««èîªX__§mZa«q¤^¯CÓ4ÿßøÆÔòòòrª»H#ýJÛÉ7vf£ê¤ß  ™LžçE»víê*—Ëÿša˜Gó…ž¾¶V®¤¦¯L]G³QGE(Ëh6k·<ç={öà×ý×qîÜ94  …mžf³‰¥¥¥Öº•¤Ž ë: Ãðÿò/ÿr¢^¯¯BVè©_N™ýt”oaû fðQšù;!Øv#÷SO=õU–eMÍf#I’I¥`IMß¶â½8š®C§%]˲à8r¹"d9‹¹¹É–XÓß?ˆ_|‹‹óh6›ÈçóÛ ø`ü¤tý$Ç·m;:yòäüÅ‹g(àiM¿ÒVÕ3îdð?J@r¹šÍfôÀìÍd2¯3,ÛßU,Æ…B@øaˆ€vóX–ƒ¯i[­Û®‹(ŠÐÓӃ矾ïã•W^Çq­’nRÏ—$ CCC8~üx몕k×®m‹ômÛ¿ÿýïO/..&©Ý|[¤Ÿö÷ææ} @âkÔhtt´<22òÇq÷æéèV’ê…AÏ÷[5}#ݺmÛð\7öa©Q/QQ,·Uö’Š^"í ¾Öäß´,øTåãè¸Ïó- “ŸnéJúø¢$Ònòý³³³“'OÎ8޳­jÞ ¶wݦÁwïð?ì ˲LÁO<ñ1 óL&›… Š ƒréZ”d¥ŠIý¾a·?éÛK>Ó§¿½–Ÿ€ŸÌLLLTN:•ìÔ[NùûͶ`/ ~p'Eúžç[£Í'Nœø§Q}A–eðtã%D4ÕKºyLÀ‘ÌëQŸOi m$#ÕINŸX€¤‡ŸçùVE/í'Õ¼k×®mœ>}ú&;¹o¯]ÖÕ±Õ­{GûûŒ,Ë&Å™èá‡þ2˲ÿ•¬(ŒD+FQ¼n%ÝÐaÒS¯Ó¡M—Î굃ß~âÛ9Òf? ÖÀsáÂ…ZÐYO¥yIûVµ­˜“¾mã®ÿC‰!Iôàƒ~^„ÿ(J›ËåZO0ißvÝx¢¶¾®Ã¶¬ü6³ß~B€ÀOR=MÓÐh4üóçÏ/MOO'¦~[ ul¯ãß³ ‚ ÀqœèÞ{ï}„eÙ¯ ‚ÀªªŠ( [¶ð[>ŸNéZ¦ Îè¿Wð™T-ß÷}†Z­æG¬: ¯d^³wïÞÿ™ò|6›'Û|²Ÿ¬gIŠ;šÖÕj?ùiŸß~k$,¾išh6›Ñäääú¥K—æÃ0\§iÞ|[A'­éÛw[Žÿa€Bð¹ãÇÿwþ¦¢(`9.^ÇÖÚ€m%í\º¦Á²mø;˜ývð“‰Ýøæl±åï ‰—4&à_¼xqñÆó4À›ÇVëVº{环æ}\@èé'Žã„÷Ýwß‹Çý#YQ JR+Ïh_²¡£åóo~»¸Ó>¢Í¦æ“víf³¼ùæ›3«««É„N²9s [Ý;I¤Ÿ¤y.n]¦p׃ÿe„’Íf™f³ÜsìØ§EIúŽ,ËDÉdnß¶ãâõùV²ë]ÀO| øi…Ï÷}躎F£á¿öÚk“š¦¥Ó¼öÖ­dFï‘æ}€d2Òl6ƒÃ‡Dñ?¢H$YŽ%Þ¤/9ù© Ï¶ímà·Ë»I„ŸH»ñ%[_µFûö¬7ß|óæãY5ÜZʵ;à0 ªªB×õðСCû%Iú¿yž/*²ÜZœÒ5¬éhß4MØ;¤zI´ŸÈ¹;™ý´Ï§:0??ß<}úôMÏó6i¤Ÿ^žTo‹ô­OJŽÿ“&‘$ º®Gûöí–eùë¼ ìS¥%ò´oåJ–3Ù¶ ×óÀÜ&àK‹=IÀמê%eâ©©©Í .̆a¸Š­jÞêš~{5/ü¤ƒÿÃ@ÞåÁº_?èRUõå8îaEQZë׺.u›Ð“äù¾ øéÓž.ê$êËn50'ÁW®\Y¾~ýzøB*Ø«¤Ào¯æÝu‚¤ x’z8;> Ïó¾··÷Ÿ²,û9%“›€Ÿlß¶í8Ý£µüDäaæðÓï´º—.êhõœ9sfnaaa!%릫yé:~zH£þm!neSH›ÇôCKZºÂcÇŽý†eÿ®’É€eã»sÚÁë¦>˲à¤ÀgS_;è·?xêõzpþüù¹ååå%BÈZE³©‚N:Ò×p—ôí}i ¡d`雡ïm ˆ$I"¶mûGý;<Ï-C¯KMv帮 /1ûté²mÛp“õ«žnàLGûiŽö“ªžaØØØpÎ;7S­V“;qðÓkXÚ«yI¯~Ôÿö`ð”"þ:J=@—âGQä:tèiQ_’…H’Ô øü´Ï§kÙèeL1ø<¡íä§O—s“ÂNE0 +++ƹsçft]_¡>?éÛKWó4l¿"Íë{ï-Hˆ R ÀŽ¢ÈJ„¯ ‚@A@@¯1ñ©Ðc'àÓÓïù~kñ2OW°&2n»ïOž$àK¤Ý¹¹¹Æùóçg\×]¡'~1•æµÏæÙؾ&½þDp–Z,ý$ô$é¥Ré˜,Ë¿ÇsÜ (IS>ßó¼øâÓ„EÕ>ÏóZk[“;xÓ;woðñ:UMÓ033S¹pá ¶/bXÇö¾½ö‚N'Ø{i`ÔF@žºƒˆã8®\.ÿ7<Ïïe¹uaRrJ2¥cµGû·?ùL¯^Mü=Ǧ¦¦V¦¦¦’òmRÐYÅÖî½ôÞ½Nš÷cZ€°M‹¨;P0£££[„ã‚(¶|~"ñÚt·apè½xéT/½x9-ï¶ëú)ðƒk×®-ÏÎÎÎB6£(jŸÍk¯æí´‚¥þû°¶V”¶tòÞÞÞO«ªúÏó­“ïÓ+MmÚÔa[J==Ë‹‹·€Ÿöõ ’`oëÆ ›››Þ•+WVWW¬EQ”tð´ƒßIó>€³mÝN$^Ïóð /`dd¾ïãÅ_l™ø4Ú˹ øº®cqqQ;uêÔD£ÑHÚ¶nbëÜdùRzßÞínÄê€ÿ>-@Ú Xô„%ÂÊÚúúúk†aÄ  ô’•,®ëâwÞã8$ §N˲ïzò†A†h4˜­½õÖ[Wiš7ƒ­åKëØÚÄQßÁì·§yðß§Ô^`©Ð `Àb±ø«¢(Ž'Kš\×m-JVU'Nœ€,Ë8{ölëZ•öz~R÷OÖ°LOOo^½zõ9ݺUIøÆmÒ¼NŽÿc¾Øw!DØ&±¶m×X–}Ø÷}6? ö!X^^Æêêê-·m$Í›‰ÙOÀ¿~ýúêäää l-bHV°¤1´_€ÜIó~‚h'AÐF„Èqœºçy‡"€K®bMçùi³ŸÖõð]×E³Ù /^¼8????‹í›µ7Ú¤ÝF'Íûh @R¦6ùs;Š"eÙƒ¢(2éfQ[^òû$Õã8¶m£^¯ûgÏžÞØØHš5ð×wv;iÞ‡¨¤A’ AMpR)äß÷%BÈ <Ïs ÐéÏDÝKN>Ísß~ûí ×u“Yü¤o/1ù‰¸“.è¤S¼ø‚¸]pØ~úLß÷-Y–Ç%IbdYnß.ïÚ¶õõuóôéÓ“žç­akK{5/?ñ÷jÞGD€vD)‹ÐꈢÈpGWUu—ªª|~â’Þ½ÅÅÅÆÅ‹oú¾ŸÜv™ˆ;ÉTnúJ³#î|<°“%ˆRþ8à‡ahhšV¡§P(äÒÝ»–eavv¶zõêÕiºS[½ú•6ð“¦M¯þLJ·³éÕç~Ev£Ñ¨hšò<ŸEQ´mW¯^]™MF°“Rîí¦r;¥ÜIønAaR1=µQÊ%xõz½Y¯×çs¹Üž DÃ0L ð*¶/a¨ãVY·éL p;D@¤þÌ`6›Í*€ý~“Z€l-õk£þA€Hj²›mñKÁíBÜZ`«7?}Ïmû8vü9~˜;HJÉ=ÙuÄÍ$,ºýþœôMYÿ!@;@!=¢û\›ZqkèŸiØ®ìî<ìD¿- L¬€‚­ó[W¦zØùn¼Îë"@; Ò¦<ÝT"¦þäÞûNž— ]¶×ê"¤›Oƒ”ØÓ±ñ‹ü¾‡`k´,y'ãfÀ­7b‡ ðÎ'ÀN$@Š )Ò ïtï}‡w0Þ;·ŸúøwÞïDvp—à½þðïR¼W+Ðyu^ׇõúÿf -C p!IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportCountIn.png0000644000000000000000000000013215101070305020531 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportCountIn.png0000644000175000001440000000143215101070305020521 0ustar00rncbcusers‰PNG  IHDRÄ´l; pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<§IDAT8µ•]HÓaÆŸ3÷áÔùmåš_1¿²Xºp©+2K‘ÑMBˆY á…aWaÑ…f]DØ…X‘u¥‘QR!1K¦¢·¶–Û;]ôáfÌ6Ê^^8ïá÷^Îs^bf¬…$kB L¤]t23µ€£›5ió¢\~w¨Üþ·ü*VDióåa5g+ ·•\”HÎwé4²C»±°èœ=™ÆMTv¤¶*)µuœèÄ ¢„èÛ\0[³<‰HݵWšYÖT£Œk™"ª¨÷Í–1ouÁaYOdc© ÖþSDt“™!‚Ýp.¹á´ù„\‹ÀÔ#@hïúý‚® &j®Öì¹*,ÌØ&>¶¢¸~²Ì–6ÞžãÀä}f^ò,·“1.Yýô¹LÖÖ ¤ÇìtÞ~È|½ã+G©Î½Ê Ð 3²Æá-"°µ@b vó©¸OÜmÒÉä†â[×^'í4¥… Ìb½ZCiUÖ³]Ù˜íqÂõxè­\£Ìì ørþ&hlÓdô9òôc|¸á-oH¹'M£ÜpÁÁ eã©Âƒ2ÔŸ;Ù’œ~…³ó»¨èUF^®núÆyE/¼À–â ê…4^ýĺ¯Îƪ„Óœ¡ëð†ÉLwrŒ#¼§ÆÊRYíP°à9SŸ“§š·ãËü»žÙ©¡iyDr¹ùU×Á®D\Æ!=‘V¸%—õLDRylQ³S¡¦éO(bêDo:y‘+B®N´L|†TOf¸ ا+öçxbrãgÄhÿ)x8DQ43Û°Š|ÀÝcžé³GЦ<DøíìfØ;_þ¼bU‘ï×DD*N ÀÁÌîÀÿSköç}Ãü¾0†N¹IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/qtractorConnections.png0000644000000000000000000000013215101070305021237 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/qtractorConnections.png0000644000175000001440000000250315101070305021227 0ustar00rncbcusers‰PNG  IHDRÄ´l; pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<ÐIDAT8ÔYlUEðÿÌœsÏ9w§ݤl²Ô‚X« P‚"F„ ,*D} FL••J4&Š5jDyA$ÑD1bÂC5¢@µVRV{ojºÜýž{Ïœ™ñ–€!,ÎÛ<|¿ïŸÉ÷ QJáF‡Bw°ùÁŠÊÒ RÊhK[¶©±±Ñ½nÍàm¯>5sáÜÙ{ “z/ö£렚§»«{ð¥GWmùâÁõõ5¥ooÝØv–µ: ×å DÀï31mÚ]r0a¿þĺmo\«–^/í²ÅõoÚv®ìŸî^„+À¹‹d2…£GSK—»ÞoX{K0!Ä7q|ÕìήžK¨à€à9í<ÚÚN°P@çù•+ý·’¸(ò•êŒÁÉe!9QR p. \‰¬Ã@_WÙí3J·Ý ¢ñx ;‹T<;›‘ BH¸. Ðßw^k!ìfá¬äŠ ×A´#‚d<Ž\6ƒ¼“£RJ¸p¥ÏgK^kX{ßÍÂv2•HC)tDz‹§áºD „P\( ™l–pWÔÞ&„hO.¿ãåØÀߥÅ%ň%2R‚1¦1èºÓòA)ÀÉ;¥WךãUËk?®_´Á0êç/U_ïû’„Ò Åš ªiè÷3dt ‚¢’r ÞÓý‡*cöT>ßþxKËA –,œmÞD)¥ÁðØu‹êæÔÔ}ÛË´¥ÐÓ“Æž‰~˜#G¢€ù0·åÊ kLsÁ@>oo¨ªZHÀ2‚å“'Œ­ÜòªòYµ5£Æ«ò˜šu 0µsÇG_½• ©HIçÇ9n# tØ=fj~:‰€"êÔà`WÛÀ@ä´ãœú$ý‹(¥àõê³6<³b½i¡T*ŒÅ½û›šßµÑ 3§®zuÃ¦Õ ¡PWÓƒ(Üþ9üÙŽ=<3÷ÿ€.‡gîù½µX)e_õƄһ§Vl%”Vó¼ªÂy‡ÃЯœN•! ¦å½œ|×t ¶Oj†“ɤL¼×}èó%匉€·]©Ô•› ‡K§OŸé· A¿…PЇ‚ BA~ A]ÇÓ­ÝÕ¿3ð@,Öy[>ü8r$‘8{82Þq»C¡Hs Ðÿßßó×[©HþüKk“eŠQ©LNÜãfìAA5$3¯t’ÉVPîúw³Çë^®]`Ww÷2o p®˜óUŠ)i‚ó¢á©(PýŸÍ–€á×øt¸ ”J_ù·(ºæ”RñÝWK¹:·-IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/fadeOut.png0000644000000000000000000000013215101070305016564 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/fadeOut.png0000644000175000001440000000422015101070305016552 0ustar00rncbcusers‰PNG  IHDRpÎûsRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ ) ËaIDATXõ™{lTuÇ?¿Þ{™>¦Paî8ÐÚNV¢@Y¡ d‹& ®0º ºìÊ’‚mX]I¶(–n…µ‚YŒ¼/`ºaÂ#E ‹Ô†’ÐòX t ,ÚÒyœýçNÒ˜–΃ýþyïùïùsçñ»JDtÀ4)¥:ˆ"’,üÀyà?À ¥”?ÂõÜSJcàŸÌ®{€ï"åîbÿRÀüüœ€ŽÞl‘>€fÉJ öOµìõÀ>àB¤¾HXµjÕXà`ŽˆhQò'õëׯÈëõ:3‚Á`>° xGDžŽ`½ÑÐÐ0øxœè1-;;{nEEEgss³ø3P$"£°ÿ¯×›QWW—ÞØØøDggg.ð7 xEDÜXß§µµõw–ü€ìŸêv»_.//׫««ioo ¬Þ‘ÔH(»ÝnnÛ¶íÇüüü!@•RêÓ( ° “““]©©©].WÒÌ™3‹ŠŠúÙl¶D`ŸRjÓÖO:tèkõõõ6`?°1ÊSø(0áp82.\ØXRRâÎÿPJµD`ÿívû]×SRRú 0 5;;Û¿`Á‚¦ÜÜÜÁ@;°øw7¶Í-,,_ZZêV(¥ÎFÀ°ý™ ¿ÊÎθlÙ²†7Þx#ø(WJ…zSòªaæþýûËEÄ‘Â(èô²€\`ÓéÜR[[»:}N]×Í]»v}hÉ:ˆ ©À €éñxÊ,]ó#Ì(6+…§C€ÑÀ뺮›ƒþâܹsá}xD$ákŸw¹\¦õ~±CéÀl]×Ͳ²²,“"Y¬oæ‘#GÖZ ߊ!†ñP ˜G ë›ÿîù#GŽ4Àç"²Œøð¬¦iæW_}õÅ›£ 輘^¯·´‡ õ·Ûí¦Ïç«‘åV=Œ0Óáp˜>ŸïcYßk ‚À§ÀÉœœœ4¯×ÛL²ò,Aüɪ©ßO˜0!­ººú'"㻑 GjjjîUVVÞF‰ÈÈ8ð}0üféÒ¥™?¯Ç¨'Üþ*((p]¼xñðšˆdu‘kkkk»¼cÇŽN`¸•‰âA8ÜÜÜüßŠŠŠþ@Šˆ<Ó[ÃXTåçç§ïÙ³§ÁJ‡ â0æ# :77÷Ñ›7oÞþ "t#÷plñâÅ;;;Ûˆ¤ÄÈö_¸páö† B€[DžŠÓ©ïÞ½{zÖ¬YƒÁðf—w÷C¡Ðw;wîL·œÿâG+P½wï^#øq‘à3¿ß¿oöìÙá“CMìŠu·nݺ4gΜÔP(¤êA®ÒçóÝ]²d‰ß*ì/ÆÁÙ/++K³ðbœí6ž9sÆ¿víÚ6`°ˆLO@]uuuðüùó·Q!ˆµ555÷;::~MƒÀ&¿ß_5}úôôo¿ý¶x6ŽšØ l>xð`ÂæÍ›ïOuÙ|W´[***úÖÕÕݦˆÈ“q8àÐÙ³gïŸ>}Úd‰ˆ3N‡^¾yûí·SC¡Àk~¸ØÑÑqvåÊ•«™óøà¿~ýºê--'ô‡fM¬-^¼¸ÿ;w~^‘´näg¦M›–â÷ûCÀï#…ºA#p©¨¨È°ºÌ)Á©ÛB¡|òÉ'ÍÀP Ûz_¿ž«W¯¶ZõÞõø~>~ü¸†ˆ<Mÿ5ñË–––Çì@Arÿ¼víZ`Ñ¢E­ÖX²0|yàÀãÒ¥K-ÀxIÓ¡T–––ö·ºÅ'»¤ËÃÀ•‚‚‚dÀ°>öÄ8ùÎoß¾=<挈§­õÄ9'†10Ož<¹FDÖ?૘[·n Ï_ñŒ%£Gþ¬Ë\˜§SIIIf}}}¹ˆ|ð‹ eæûï¿¿Êâ['×3€ÙÖÖ¶IDJzÊF½mèaÖÄàû—^z)HrzÐqøfîܹVUUµX£Eqloø×©S§úîÞ½û2ŒŠ7­µ··ŸÞ¸qc HîòîðyIIÉc»víºjÕñ÷D¤Œ¥çp¯¸¸ø6à&Ç1›Pt=‰¯Æ&º®›_ýuøæÅÙ߆ V‹ÈY'"¹"’%"}#š5`~zzºÙÔÔ¾¡y!ŽÚ 0}âĉáÛ—ç»9Ã0Ì5kÖ¬_Xr¯‹Èh""i"’$"¶¸&išfÖÖÖ†off‹Hz×N7Ú–÷-Ã0&UVV†ïN‹•R—£LÉóÆŽ;ýرc÷5Mû\)UõùW€Ü¼¼¼ŽÕ«Wßq»ÝÎP(Ô."75M» ¬SJÝï…3 X’™™ùÄÁƒ³²²œÖŸ†Zà€RêF”>š‘‘±âĉ̓ :®”ÚÚÍ Ì3fLÒòå˯̘1#]×u# ¶w€MÓn½ØŸüÅápŒÙ½{wÃsÏ=—Ü|Àeà@´Ô€yv»}úŠ+ÖžPJ¢Ôá6 ã¯Ã† 3kjjEðëçi`¦Íf{Úív·{<žû“'OfĈMº®¨”j€s °011qؼyóšß}÷]ÃétÚ4MÛª”òF›E”R…ƒ ª÷ù|¦Rª³§} Øl·Ûm£Fº———>|x‚ËåÂív_Ñu}eö§ót]Ÿ˜““ÓR\\¬Æ—˜œœÒ4mëÿ•ݬ3ŽMôIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/editRedo.png0000644000000000000000000000013215101070305016734 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/editRedo.png0000644000175000001440000000125615101070305016730 0ustar00rncbcusers‰PNG  IHDRÄ´l;sBIT|dˆ pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<+IDAT8í”Mˆ’AǾ¯KíF®ÑB‹xpAéRìÂ^ºDuˆDºzP:ì!èÐQêàmOCr»t뼉G13PŠÀˆ" íÃÔ×w¦ƒ#ˆû.ívè´˜~ó<ÏügàXJ¶ˆ×Ô\ò @ýˆÐEà,àR`ã øaÁ6ÀxÃáðÝ­­­[ÅbѾ¿Õ!‡éÀI`XVY†www›B™N§¿jšvXæ™i«Ýª)Øà<àQë 0 ’ɤÇçóÝÅbžV«•j@ÕšÙËÓÓÀE¿ß3‰\ +n·{niiIët:x½^Ýétb·Û1M“Z­6ŠD"O+•Ê#à0²*ß܈Çãoúý¾Fò0*—Ë]àšjÝ>ÙõÍÍÍç†aH!Äàɾa²Óéˆh4ú ¸œ°Ï×wvvÃáPJ)e³Ù”¡P赦iÀ³L&30MS†!ëõº¹±±ñ¸ªZ¨Y—;{{{ƒI6Ùl¶lkŒý{;—ËýRÊB¡Ð_]]} \N1cÝiWØ€y—Ë¥!èv»Cà3ðM]J­T*ÛíöZ"‘xÒëõ^°ðò4XÂápØt]GÁº€ ÷©Tj›±õ> µ¿ïõMƒGÀ§|>߃+€¬V«Uà‹ àðv*^ZA'åO¤nàpn’!P~Ζú7Y=95$ãOÆ8*ôXÿGÌàð»¯IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackIconDrums2.png0000644000000000000000000000013215101070305020207 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackIconDrums2.png0000644000175000001440000005251715101070305020211 0ustar00rncbcusers‰PNG  IHDR€€Ã>aË IDATxÚì}wœ¤E™ÿ·ªÞØqzrÜÙ0›—Í™%-H9’JVÁA=¦C‘STäð ¨ `@PŽt ]Ø]6°lžM{¦sxcUýþèîÙÞaQ„ñŽûÝÖçó~fz¶·»ªžçù>±žŽŒ#ãÈ82ŽŒ#ãÈ82ŽŒ#ãÈ82ŽŒ#ãÈ82ŽŒ#㯠R~ŽŒÿåô cü^y„ÿmZ°·ø…ôÜS§©ÚkUK–ÎZ-O„ŽšÐtøÇJyeÏéâ9úÄŽÕ÷Ë\r:¼sNÒTF]«j„ää0E¡Â÷…€å§Â}òbŒ©D¤¶&¨&Ó…Šà¶P$BÍÊ]§Šâ­ìñ[ùb  æÙß®ê7†çÉ@¦»'÷Êí¿ñ¾g^ܺÀ0Qêp!¼7a†êïåŸòBƒÊžŒ¦  %_°@óåŸpâY'Ϻ°&$§K¿`vïË<ô¡Ïÿç?—ßï¨Ê¿ºÇ‹ˆªPêù"ðï_<õ?V,j]eL©š DÑ£LÓë6îyô3_ýůt—‘Á¦”ú¢Ä ²Ì@>B¨”²šAäßÿß^Uâù¾RÙ'F)ãBP…ú¾O(jÎÍç•RfË âV1Âÿ%uP1ØmíVeP´ìG å´'ú5on¬› šHì€g[p­4à ƒÛ(ä,lïÎä?ñ¿mÙÞNBHNJ™­ ñ[aöVõ“”’2FÉæÛþóέ³§¶/‹˜ž¡|AÕ€P€€J̹3:k#!¥ñÙ—^¯W ` „2Z”RV¤^üD€Êž*j CŸ¯kúQ–í£½%6ùîÛ¿ël­©c†‰´‡ËÂ+Æ!‹=°3=2•öÈ#OwoþÂ÷þôßI¸²$HVÕãVÿ¹Õ ‘R‚RB,ÛK=øôÖgMÍln® uj$Fø®ÏP£1Ø…fwgžÔ×w `_o„Ði¶c½F©6TªÕÀÿé×4â\«©ëbÑYuöd|ÿ«g#`RH3òV¹¡ðóûÁ {d6•";ºóÞ57?}×}Onù!°ËR^(K|®JòÝQFà;w?Ê«—-Ñc´ŽsQ[_\~ݧOú—YSc5áˆ!”@3%F+jZ»`˜A$w<§`Ñ.<ûÊ6n=ðÄoûí/)¥û„ƒeزªTÁÿÏh@‰ãÛCÝ{{ð­/­ߪGæLä€ß¥é8õÓ‘Ü;uÂ郕Æp‚ã¡§wl¼õž5È”÷+W†û €Tùg®j/ý·²—ìï€,R¥§EÅšç\¤|f닉´CƵÔN3Y*²°ó98ŽšñÇ@«Ÿ a%1kJ«<ý¤“l—³_Ù´›RâHy·Ê·¥üߢó)3•Î6­úW¬\ÚÕQ`±ñM8ŽËîÝ?»~a?2ÉÖnJä¾ð½?ýñÙµ{^PVBV¤=Sžd•ÞwF Ò˜…‚IUL@) JI­²À¤+Î_ú“—w,hm môh¡VDš'@STp» £f˜|âs߸ûwÞ÷[(!ÃBÊ|•åúVä]dzÓùÔÆÂJ2•34½ôÄoî™>uÂ\®PHEC!@nx`@Øq ãÀ€Ïr×+ϯٴc´À¹È” ]Mü «¼.^(š‚Á`ÿ[Ùƒ·“ ¨„Õ*•5M£Õ²ìŽûî½ûúBßêPƒ9L ¨:½ J° f¤LÕ¤J8yá¹Õ»Ï¹ø_¿`“ª°„çóÜ(ö͘à¯Oä33¿†šŒQ¹Є®øèç_þ±ó®ohi IáäÓ°³q{Ü@!_Äþ9´Íü'Üú›Ç_ûíoó!$-¥¬¼úÉ•õ¿5Ê›ú»lìm,¶:xSn÷}Ÿ\ý…Ï^tÜ '̹ìÊëo»ëþÕ¯FCá†Æ(*2êgàrp,‹¸Gkksíe«N9Ó*ùºM»öRJ…”òÍ‚C#±ðp(H\×SÊóW(”R&¥$å¿‘ÿ†üDulž(Š¢!*è¨P5UQ|ŸtÜ{ç×n?÷Ì“®PTM³s)“=ð³Ýàù=È%ãèé-àç¿ß°öß~òÌ#ÍS#—_þ±iwÞqçf×óúË:>YŽºVëûâ(Ôü»£«d t ÕÖÆjŲ'Ÿ~ú®î»÷…¯}ý›·ÕES®üðѧ®XÐÖ 3(¨Q°ˆb@U^X½qÝ%ÿòïL}°h9C£"Z#‹‹EÂ,•Í™ZþyÕ…GÇIûÁ‡}­¼)þ(+¸Ú¶cˆc”s^Q‡:€–žsæLMeÆoîþÏ5üi]m3ïøÑç~EMßsß¼$|+bÁÆ~ ÷=¹mÍïÞð"€"!Ä—RjÏ>ûô—ìÛ3|Ѫ~2$¤¨Ò÷Vy½þ[û%Œü_J™&ˆ=ðÀ7ÍÀÄ“O~ï…Œ±Byst€P8 O¼âÂ%ïY± }VK£ Í0%ÕkXªª£?žÈœpÎ7?¡kÊvÇõG6•‚ñù+.=ãâ÷¬¸‹jÌ–N¤SItwï>pßCÜy×Cÿ À¥4'„¨ö‰ÅÛ•’éÁÚXžL¥ W^ú¡ N~ωknjª†C°z÷Às]Üýç§o¿äÜE—B!ð½¤ÈÀ³2°-Ž{³ÖƒOo[}ï㯭.¯³Â¼qÒI'¬¸ù–[V}û›×Ýõ›ßýá.JiZ1\fôB•ä¿£  -Y²ìŸçÍŸwíÌ™ÓÂk^üË¿ùÝ=ªBT>L!­œ>ç¤÷7yÉäñ‘PA˜)-@ÓY×;úì/PÝás‘¨b5Ô`ÆöšÞwÔ„ ^±`˜ Å´¶F\ô/W¡ïµõˆvŒG–ðØÚM?¿ñ–ŸüŽ2HI !Še&òªT×߃ef§¤¹¹Yëëë3´ßôÝ뮞ÞÖt±ieŠbÒÔiøï~C¹"Ò–ãæŒÇG?y6’Ö°ä¢@lß“î¹ãþ Ϭ½w{YŠyy}VŘ+çRô[oýñ÷º»÷uí?ÐÛ}×ï~ó%ÛÊú¿Þ°1`mÑÂùÏÜpãwô_ÿòWò׿ýýmŒ±Œ”Ò/K_Åm©-JIzóŽÍ÷=ñúK<»{§©jAS“a*,-’¬³=F~÷ºj饄(¶Ï£ Zc¿‹À_¯H„¥ƒk?w%¶=ù ž~ôQ÷íD×øœ|îy .ýè%Ê[^fÆ ýõõõJ±Xï lñâEámÛ¶oøÎ·/¿ù{×ß3cü¸¹ù­ëñìýÀ¦5k0=f`åü™X»n*ņîˆd”*Éݽö™/ßòôÝ>¿óéþ¡ÜBF7Ù*w. U•çŸxìɶ+?ó©9K/Š=ùØõùBá±2¡—u}[Cy‡º`–å¼úÀ}ÍëéíÏÈ”Z¢Ì¥…2´U’º2 ¨0šOeЉë~üçWÔ´4F¢AMÌì*« ­lTq!¥ÖUúT«A–„À(Á²†˜¦aÍš—Q Â*ðô¯~Šö—žÃ©×þ˜ýêŽ;®ùÐ…žtò©§~À>]׳Žãc5“7‘ MMÊà`\_³æå®m[_ÿéÔiÓô¾ú"䆯àÀ¾}ka/>ú_øäW¯EWÔÀž"GKXÇ£«·ãÑ{^~ÀF]S×õ} ØRŽ0@¡ê±ªì5 n}îÙ¿ VW‡¢c¯+ÛT¢ÊÎñË4xÛLÀÞ‰þ/¹9LÛ¹kçSŠª¯J&Sööö¼\^€7jý6ò)-Ÿ §”)&¡‚ådÓy»ÏõDFÊ‘÷{TCU–ÍoÜQÀ*£!'(6=÷3F…¦®A2xõž_5–ËN?§ã¬ÓO;k÷žîÌλ{c¨ò4ªÕ9Ü´x|(ðñKW]öÇ{î¾g„ ­«üM<þ½¯RÝ+À¤(1<ŠDÁËO=†„Pú§è3wgì×9Ÿ=3Jê+IœBÅø%„H˶ÇOìºðá‡þëþÁûAHe_ÝQ¶üc€²ë¥uuM>IBº{÷îy±<)§ŠœQ–yuÁi¨«»´µ©ù+ÑpäÔd:óxU¸Ó-Që±íÑc*Se4 PаJA*%`„@¥ºRú]C÷KÏþ×Ö¡í¨¹Ú{N9å„í»÷dwïî(ylBŽriå(×õuµÚÐp"xÍ5_¹úã»ô:g`?yäÛ_DÿK—¾[J€”qXŒœªP”‡ú²5†:µ'ï¾L rò [—ª îXU¼Š^Λ¿àª¯®û“í8ÛªËeÊwáogŒþB×±-;µ âT6•P!¬ü;+Ã{»®gºñ^!!LSµ™RÊ ƒRbH ´¨5ú³˜JtSa0AP¥¨%w_¥” %Ð)Yž„„ NáX¶l)zŸøOøñùƒë¿võe—®ú¤ïûm”ÒXRõ²*¤‡Fîj´áD2ôÍ뮹þg½ïKý[7Éø¦—qÌ’K·ü€BJß«2R “–™ÑT) FP(LFÑd²è´Zól!¡–÷Æ-¼X…Ž^Õþqœ ŸÏÀ¡•>c–3y§6@E×K×õ¶C¡ãËÄ¥U’d”'/KhAÛ~~Ó¿ž6®µ¾+“µÎºó÷¢{ÿ ) Žã"Š|úøes/ȹÞOÿôÄŸ_™\¹ Å`3 0©PZ’ø2ÁB Ò’ (ÿô\L;átPÁæ~t¿ò™~Î*ù±óÎ>_ø¾ú‹;÷SBHÁ*à ëkcêp2¼æ‹W]ÊÑK.ëå/rã‘QÄœù<ÆuMÅÞýûÀàK •p H!¡Ð’P$¸|E—TNŽêó“6?&^teÛ¦:¿rHöŽ1&8ç‚û¾UŒáŽeUâ ܱ-¤©õœS¦¯o+!%¯HJY1èhµTsÎeccódß÷QùULàUÅX €dŒaÿ€½ï†ÏÅôúØ©_UT‚ ˆ 0ß·K@Ï‚ Z´J]„[€iM‚îÛ TGÞ—Pì,^øýâÑÿKm‰…d> V’l–tmöUJ Ó’ 1J@ËL B˜¢R&9jŽ]Ï=‰Öe'ાø“/¬Y»¯³¤‚f\2}ê”SW.™ÿÙÔÞè_¿†4j€æØPàh|)á—‰2±!%4 HIá…H ÛÐ$¡ÇvÔ^½i0ý•€¡q ¸í ß÷¥Ò© xMW²GåW=cZC1& ¥äÍqžzÅÂÊ9E€i茨ªUÕA‚°© P Õ@¨ ª`ŠB5øžÛ²±gýZlÞ¾¶/Q À„‡éM5h3^ï†ÊJge@É#d„ J¿—Ô 1 €ª0¬~öy,8ñD<÷؃7œrΟ² €«‰úËŽ^>ÿ Ÿ¼ìw}[ÖcûšÕ˜`¨>‡BÊÊ›Š %²ù¾(6LB‚@r …®xßÔ6ìJcûpaFQ4BQ5z¥¢kwsÐûò.Ý™(ôÙ¶#…nÅÀnhhìàœcTnL hÆ„Ï)d¶÷ ä»WàÔéKÛG.çÁs‹ Œ€1 FKDQ5ª¦€) (U@™P¶g"“ð! uíSÑ1aÜB~>ƒúXÅžAe”P€0Ròµ”2*¨ÕP²Ë<¦¢@T¤ó,^¾-ã'˜&NyÏÊe÷ßÿÐN*q.˜ÿÞ`0„ɳæBͥѻetM¢¨ð¥€"$Ppø’K0ø²Äp>d 0ÉáÖ¶`\m ¢õCPuŒB& ć?Ô—Í7† ‚ÀÝ=Œ¸ã¸’—¨NUU1i¼±‚Z¾›Œ@@d2Ùœ®)øþ/_¼sý–Þµ×~îôK&t6¹$°]ß'ð¹€çrØŽ‹lΆkYà¼î{ÐôT3†ž~»wö¡ÿ~äR)C!Á˜Ð9Žš‘vè3 ¨½[樔(Šƒ·BËFaÙ3PË â)‰5cñÜÅ6·ã?øN9ïB\òÁsß{ÿý=Ãs9çÚ©+»pí³O‹›6ÒË>ù DkjÚµé¡>„!6¶ê´'u‹ªAB$Šª‚2”R€„(à\"—sp`pµì9@ìÞáûHÆãÐM“ö# ˆÖVÙµðt*S½ˆö½Žz;Ëãð¤#ô·€9n&Ž»ô xí¹'0÷Ì‹ñ½Ëη xê÷wâŠo\PÏËLŸÒÕªxvàù§ž†JúõøØ7¿5÷Ý…ùÇŸ„?ÿ«ù¨#À¤H äC8‘@Á±옌X´©Á>ä’ 8Ž ¦Ê@$…gÑÑÒ>nû® †¦6 !‡u]ã¶íЦé5>÷ú“ç³úåBÿWúœg¥”Ð4G׋ë·ô¯9÷£7½>{愉gq̬ŠgOX4wqXQL8Å|kNv? Ùn¦A3fÀ‰ë¸ ”‚spîñ,dSi’I¥HjhXN›3Æâ3ˆ½å4¸˜Db8™ƒBº‡™`=ÆŸr>uÉEh­ cÖ‰ïE,Ápßlé€;xT)¥iÙ6Ž`OO¯l‹…Iû”Xÿ—gñ‡‡ÆOy'¾uݵXwçÍPUŠôÁÀAàù>Ú¢A Y.R¶Øüc‘N"±m+«„p*¥PZbš’ØP5]”Öx1BæœWjú¨®ë±T*é&RIÞ 6MӘ뺬½ ‚AÃ4”h4*£Ñ¨7c†é«ª6¸ncß²#Õw×½/ÆÌŸÕ²bÉüúM›öû÷AÓ|p߯¤‰9ÔÔÖIÝ0 ¥ ”RH)!„€.<Ï…ç:p‡ é.\„©‹ß 3„Xû´Ô+ „‚–ãE¢¡sùIøÉw¿‰Ú†FtÖ‡ñ³+?êÙ(Ú6¥øÚ?z€çyžÜ½g߇Ÿxæ1OàGH¬~æ1èëס¹¡ËÅoýÎ_¼C›Vƒ3 BØp gÌŽY TO¦°mÛv¤“)x® R(DBBb$ê@Jn£ë8Üç‚9®gú¾(¦º¢jÙL&^šÉQÅ.äÝ`×u•]vù1{º»ÏÐ5&N¼th(þÆÆF-‹é555F$Žš@]SS}c[[k½iæ¾´mhQ¨Õ†‡“¾‹xÖG[C-7y’ì9@<×/ùÁåx»ïºÈó,¸äµWÖH86䌄˜uà…&\H…Á• Ìh Lx: ä¡{¶ $°/cíÚÞ½a5!Ä•RrF©õ£_Ýuó´Žæ EÇŸ²£wó =CC * C …Á¥„$3“ RÜG¦©\Ãñ82ù"8çP(ÁHÉ3 å±( /¥©LÆÉŠÂvÓ÷y€snpUUK –5g® …Ööõõ½¼gO÷Þ*&ÀX üà?øé¢E‹ÏxuÃFê¹Nçy/´µµi±X,‡[fÏž½àÄO\¸xñ⎶ööhScKpܸ³­­ƒMœ4>k1mêtä²EßEjx˜r9øž )e 2GŒŽ’5®HN Ù •¨ëì"´¶ÅL DÕÀAa{v±€|þ«Xq̱øã½÷bI{ cxe ëoÌ>X•˜ÉJ)s”>”ÉoiІÎ}ûô•W®Š®ß°aÃÞQ¡wì¾ã‚Ea¦ïó³þò—çºù¦›n/í­uuuÑÎÎñ3O>åÔ³'wMB<>ˆ¾¾>òyPÆÀ¹÷9¦NëBggvïîÆ¶­û |ºØúêj¹eý:’ŠÇa‹â` GeMUE(h"Ë S§‘ö‰“À|îž-Ð\ n>‡\. s å,dX½§Í]¿~eçÆ¾Lþ}ú(!Y!¥œÒÞvÍÙËfŸÄ Y¼¶g/ÚC:"L jÐ5†°®CQ’.‡kDÐÛ×áDÛƒ/„,Í1¨–r•"¨0˜*EP£ˆh*b54Lž‰Æå§ 8e\%€x|pï1+V\«iÚ^×uåí·ÿâ¶d"‘ÿü¾øUB¨ˆ´”‡ÿªäÞ¶'@ß©àûÜmjnÎÈ1EA{[kTJ°ÎÎÎö˯¸âlÓ4$!;wì„mÛ ”€s)%¤€ëºåÀ A2_@çä¤sòdYSßÃ4A(… )†Ba0 JPÏ(æÉà®­èßö|߇Ö> YOÀæDJ8àŠŠ´ãË<¾ñä¦íµÍxß™sjLã„J€EH©´ÔÕ}ö¤£Ÿ”w¹üÕóëWK)á:(|îƒsœ'Г-"'UìÝwýCÃÈX..àKyÐ\—¢Œ¤lÅ0B¡™hHêñz<ƒW^Ý …9wöQãnr]7 ØÔÜÜÍð WÁ1-p¥cñ!‰áa@¿à\´¶¶Äב©TJ¶µ¶`\{;Xºtt]ƒašRÂ0tøœÃ÷}PJA@@Uº@Úr1{áRÒØ1NÄ›a‚htF 1 ]!Ð)…Æ•» Véý»‘Ú³C2¦ ÐÐ[EAPô|¤Žœ/Iwª°»'Sxñ[÷>ùø‚£ÃÇÏ;û„5 Íûô'.?S ×àG<þžœõ람µ7çqä,–ã"k{ˆç °ˆŽžÁ! &SÈ[.<.J¶,åJ¾dåU)r©é:Â-í°cmõã ´ :'Œ‡[Ì’P8‚ÞžÞ€€¨i¾þÁá’ $ªƒAâHýX3€$¥BžÍeÓ†iªžëyñ¡¡Ä§.:?»íå¿À/憰tñB,[º&t`îÜY˜üäs¤¦¾ÉtEǃ/H5"xì©/¥ MGms3‚ MLèñýØß½ ûzûp`ûëxßE«ðäC &Ôú2ÓU»º÷ôW1€7vÿø‹Ž‚8çOeÒ½Íu±ézzóz ˜‰Š$oìjF•ÂÖ—ŸDd×Èpv¤\ÑúÜ… IDATÀ%AïÀ–þëñÒ}¿C[D|ç0ÁA}Âóà€¨A2gÎ,ÄûúE|¯Bô0P̃ :‘%•Pa…@§ Å¡~YÛ>^6Mœ@©4&Î^”[k˜ J…/„o;ž³gg· E±bé’Éœep×™\ßÔˆpÀ˜‘+ÚƒeÈ;>u[2÷¨¡«§¶DB°\×óÊŒ@<9äÈ’BTÆ4 D‚†4óIbëôØ>Rœ"Á5è\bzG+ öæÝJ± 4UCbx¨PI¹u*x¬ Â ¾ã8É©7pÏÉõô„ºB¶4„‹þ×Ö¡Çâ EQ/ÚB ¥€Yû~ôU`_7:–®€½kH!ê»p9‡F("š«®ƒÍ“H@›Â4êç2D³`ž £0(…Æh) IRÉ~Ù8e&ºÚÛ1iáÑÏq#¥x>c ÅBšfʳÏûÀ<¦…ç ‚,Y²èC/®Y?uó¦õ¯¼ôâKëcnºx_{$tâ¼IãØ@*CJarô1!0B + !SG}4„U¢•y¤¦ì½@R(šÍõómôYDïn( ¥2{ú”)úúú³UzÿpíuÞÙ@Y2“{”®‰ @ ÍÚ¢n Ñltp/ZÚ&3}Á"ä’ÃHìÝ æÙ¥²0 P*ATŠÚ†F«‹bÇ®ôöÆËÄ/}®c[…Üwߘ1cÒé vìØ…Ççž{6¿ö:fÍš…I'†²Ùìw§OŸ~£êgåþýûÐØ6NÍ!BI)ñTªC UE8”a“Äœ ê!¥ ÍÐA@ÊÈCF…S($Uã>HÙ×Ö\ *QVC8LXŒE^`Ì`Êä.²oßþ^]×0y\[hÇþÞ\õ´ªBà‡L[€@¥”pî—VW&RÅŠö¥„+€bÑÇ0%0ŠIDó›ë݋ȕbÆÊSI~h}{w ÕЙ+l„kjeÛ̹´»·€½‰=èð!A)eYP˜FUÐra ç¦i@Q¤RéR|BJ,Y¶¼sÛcÿ)Šªˆ2{Þ\™ˆŽ„S U!šW”5^ž˜^ Á •J×JQÌ*ýís6¥lAc]]M6_‡!þ›•±ÿÏ2€ã8þÀÀ@A@Õ4õ­Ã“„'ÊåO‡ rK® > 9h ‘ØNgå¼E&«kDÛÜ…Œs!¹ïÈP$Hs®D½Bíˇç•Ti2ò”¹H§Ó8öø£áØ6oÞÇqŽÄ°tYjjj P‰çù#îøåíý+Æ7)”R îÙÎæfÒT…&}ˆ|nªŠªEÕÁ4£„BRB@a„’7]¿É&Þúß56Àˆ+hÙ}Û¶”$Á`À@8—dVs §¬X‚©g~¹Í/á©»ï!Õ%. oQ“UäÈå@Öå`–#}Ç—®ƒUÌÂ)æ@™U7¡iT#¦¨ å³k©m%¹l†¦~EÛ”Ãý»‰5B ßµ@Må½*Eú| }…røB€–kUQÒñ¢ª:éÍ÷9Ú[[pÎU×£¯ÝÏ=‚["L7 -“ÉdþJÔoL€c‡‚sWJUU5„ ŽàÔyÏ;W\q9~pçï±dñ"x¾È*\!ÁÈ߯ÒDUµ$(…‚{p] žgÃsm¸®×.Âs,p߃”LQ¡¨˜€esR*%UF¬ú‘eQVÆ•3§R ¸,= ôïšýìNÃÊÓÎÀŸŸz=6ÕÂ@©«­XVÁ¥ûǼ¥Þ˜1€ë¸Âå2ïû¾7wæ´ID‘yÜìˆ[F¸Ûö÷ƒ¶O°‹#Äp®ã€¼“†Ø…<ÏdI¿K)!÷=pߢ›¯–‚C7Ã¥"IÁK'’Ê»¡RT}°Žã`¬—@4V‡‰MuP žÀ”kE˜h¶m½Y+]ù®SŽëJ–ë¹V[KK AÃþí[0aÎB0"Ñ>{!¼¾Ý f¨ h¬®~& з7¦*Ò…ï»(dŽcÇžôô#•J ˜OÃ.dÁ¹WF…2T~'„Às-8v˨†¾Á$†‡Ï —7ÉSâ¹B‹æÌ„•McÜø (&ûÑ+Á‘ ‡ÔÞR‡1ßE¡‡SÉè8«­‹ B4´w"µw'>÷Ý[àmü3ž¿õ»šfi €àVá½û÷@€¢é¨oëDÑîç@U#•µ`Ù"!Å\f0 EÕ AvVÑ¥ ŽU€SÌÁ¬d0­t†AQa˜!Ô44á`ŠglRñ„1løõ-øö÷¿'ÒŒ=¯oÀ¸úø\õ=¶í؇‘zùn) =c Dzsµ‘@KIßJÔï“÷ýî|ãeŠD |¬šH ªéð¬8çeõË+)Š6€VŒp\Ue`´tAUUpî•ÔƒïA·„ëºà¾¦ê0tÏóPÖQר†`¤…\RR`œRà¦(ؽwv}á¬Ip¬ºêjÔÖÖ@I(áP‰Äpá0Vÿ»Ò™ÔÀpbo´&Ú @z>Gô¹HØáÙp™ZI“‚§â.þô/8L˜:’¿u¨ul–ϘÑ‚u¶.€Þ<”)0t u±0êëêÑØÜŽöÎ)h霎ú–‰¨o3Fc[Â&Å +ƒ]Ì¢¦¾…|m &rBx®ƒæ®¥˜½p9>÷Å/wfóEn ËçïÊ9< * ¸E„Bap!d4 )±sWwzt¸}¬]Á±Js.‹ÃeCKi¬ A­m‚¢0pÁ!«¾NH‰Y1:‡Ûµ]S°mÓ«탿1~÷Í«qò5·à†_ÿa§9aflÎâ%õ‚4Î>f:»X€…) ˜º‰tªÜ÷°oÇ:P5€ .<Û^¼N1‡ãV¾G­„Ü‹h]+jÇ#Žàž{ïëû÷ï~»{nGÓ¢3ºpÁŠy¸åÎß#¢Ñ¿-R⯹8j9AU?å(=O.œ9=êù>Š–íápìjä²¹$T©¦ %BÞ“P@‘Îfµ7bò×ቯ\‚áAsK RÙ¿Íqžƒ|¬M±žß¿·pùUŸ}ª±±)vñªUÓf5»eÒøÎúñ³æR]¡ DBx.Iô"Ô·/Ñ<®[ú6Äfaúü…ؾu t#„;0®Y ­±¾cãèÙ“[þã†oûÜ~K:Z›IЏT¯ á’Ã_%µ!¹¢ýW¼6 Éx>ºÙ³]“&Õx¾Ûq‡ýw­ˆ£Žš…Õ/¯ÝõÁsϱÇ3þËŸ¼¬!ŸÉ`ó`³#:ÜC,.$x1F‚±:,¸ðãxô×ÂSÕÐÈzÀúÝû®kÄ•;±æ†Ÿþ<üûßÛTVk&€p]s넾ñ™ã‡w†_\ó2X¿ iW‘@c}Pß_‚ áþ?Ü‹H0Ê]dûÐL-¬zÿqÆÌ¬<çò‘勯½ÿØ[÷ṇï‡+KN5yxµ%„@C4ŠYg}ÏÝöÀ0ÞÜpÒxêµÝøàÉçãî;n›z×îÝiyÞa3®ckº¦ÉD"QSàó˜½òd·è8Úû/¼HîÛ´–(É]¹api–Nî†"p=I%¡…á».SÀËçï¥p=¶cÃ÷|˜†RÓˆ©Z%‰5’}éZüä7wà`å‚Êñ8WÞ¿lÎ1ŸY6ù´ÿúÉ ìÅÞ 8UÐtHUâ½í}þ¥xøµ½ð© 3„_´ +”»Œ÷bêÜ)¸úÊËñÛ_ÝŽu?¸ ·üHÃE 'ãØÓÎÀñÇ‹ Q/®Û+“<MÕU@UËamAxR”ŽLIQ9C!8Ü‚&XM;Ú»ä²3Î!ãÆCÙÑéìÍäòö›€Ä»UÀrl~àÀ"˜Š\>ïÜûÇ{_\ºdñ²g]èžtÁGÂz1'9ˆÂ¾íH÷ì•Óç ÍZ.YÀÀ¶¡ œÎÙ˜Zõ]0# Õhã§ÒÀøiP)ƒ ‚DÑë·núÖ×.®{ÏÞ×Ë]4ÓºÂ<Ççìg>õÊøîí‹®ù̓ º‰`(‡KX\ o9è욌aGà”“OF¨¡m-Í®϶ « º@CW¾ºtt]ƒB ·­Ý߬ù.>¾t*¿çtÔ54!8m!„¢ÃŠ÷ ?Ô/Šñ>”SÿØ>”F ”N°Ö¼²º¬§õo}ãšU­m™È%ÿ|Å} „´¶4é}}y¬‚ÍâЋ…¦ª>%ÄRÚœs›x&Mj|﬉7ýüñÕ&ÔáÄå§‘GÍF}kÔŽ©°žg Ó@©o@ ©èïz›6nÂs¿ýnä9H·€ÚPà0!\‰°JÑkù¸ü§÷à_/ù š:Øøãð.ZuyÞ•«òĸŽvsÿž\yMyZ×'t]çå†aËqÐÔÔˆH$jlÙòÚÚfÌ À1WŽãEÇvráH$¨FMoOÏNß÷%íknnkg–me솔¢¿°òžêê×êÇw=ÏàB\J©Ç9§K—/ÒÕÒt“jªøá ßDÓøÉ¥fzš†¸‘:À|Ri ,ºi" ŠÀhhÁQK˜»|9.ïÞŒ—Ÿx wÿêèµ% ]{CÀ„š¯Ýv7®7ƒøÆUŸšj„kŠ?»ýÎ?—oñ*pôôŽnëZ]àÉǩ쇚H&pü 'Ô€òùífú¹WiL Ü|Ñ•BzÍÍÍõ\ps8>”ÆÁƆ•fˆ€ÕÒÖ]¿ví%—YVúçU·r] Ë !¾®ë„sa,\¼pö)'®¼iîÌihiiƒï{è÷$@(¾f PßÀ÷JV{~x¾ÏQ,a Ð5±šZD‚X SN<7.[†õün}øY© í0ÕªaâK·ü·ÕÆð…Ë?:oý†û׮߸ Ê—:H)«ïCâ8´Ï¨’n³··'1{öìºXMM ç@ÏäÝ‹ýý@Ö¶­¡ú†ú@ãñÁ,^kV9Ô蚦)¹l&ŽR´JäÊmXÞaP@ªª*C¡"%"'®ó›ñ³_Í—7ý‹g}æ…[(%ž2[^suàj&¨H6ŒD£‘(‘éLº€C›CýC.Ó¢cüy¥êà\¶ EšªbㆽåÅV˜ PvÛ,ÝÐ#Žã$qðî›4JíS†Pº!£rQBRUÕtmm­åy¾vá…çõáUß àq`ÁÜq˜7wΟ¦¨¥ÆÜ/=¢”.eî‚s¥{¯J BÁòŰtñd,_>ý©4ö¥1D ô׎ÃE—Ÿ8a>\×ÃáÊûŠDÅ\ûe2µ¡ßúÒUïB²2+½“Uk.¿Nà`¯àÌððÐ@´&Z¯ª*-‹E¼ys¨w5ÈžžÞÁy æOJ%Sˆ%’UPçV©Ÿ‚ `pùu¥mj¾êÉÈB Š¢XÍÍÍrpp0tõÕŸýä¹çœõ#„¦°bùLhzhDº÷GŽ“”v¿ì‹<¬Y‘ü‘ÿ'w!„ã…£™¤í#§†Ñ«Õ`ê?}7øt8N¹Ulµ* ÀÓ¯ïÃö§Â'=;M@©ÛªmtwðJÇp €·sûŽíÍMžã¢¯o0…7¿cY¾`dRñx¼ß4M–Ï¥Q&bµÞã„€ªš×*V_}RÝ »bx„Ï0 ’Ïåj®û·o\qüñ+?N¨"UUǼ9Þ@øJˆ”B”P@J~á¥à#į DE-€ç•BÇ®˜ iHÁ@\0à˜sðŸü<×yÃMUÑh?¹ãwhQ8>yñyLj¢Ô*·¢j+<«{WÖíårÙ¤ma<×vãŽi"h¬³ó<œ}ßC:™vG»=(ß9¸|Ù¢ €Ä¦×¶ô*xmô !ñ\?ôþó?xúÊ÷œôJUQ,Ú䨙í‡@ýHåOYâÿá2IÕû…€”¢¬J*Ãó,,œ×EK&"n ØzbÑiøÊN…mÛ‡RsHfѽùUœzô¢%(ݦD鬟‚C«Ü‘îqí8p ç@1—…»÷ìOá—kÿ¯PØßÓ·OJ ŸË¥FYÀ#]ýÌPDç>Çî=û£¸üŸ¡PóæÍ 557ÌüðGVݬ2…çsY:NÛHý_u>ê !%|î«Ñᄯ<%u)Êõ….@(º¦´cûþýÈH`Ê…ŸÂY‹fŽÜ0²x#ˆ§þð{ŒkidÇ›Ò92Ti¿¡¼Ëu=?›ÉæÛë¸(Û üú‡Úƒý½ B(<ßÍâÐ{lG3gÖÌɬdM¥F%;Yh$Ñ^}õÕà-?¼ù6]Óá¹>ã‚CQŒ*¸ç%Ý]‘xÉß ñ‰ÏGˆ^û áEåóƒ*ᾃ†z“¦´£7‘Ã@ÑÁyÿò9ŒÓÔªr6S¡x²{ô V.™?¥ÌfÙPq°“ú!û&¤àËÉ{ÞÈYìaÐïv€¢(ä‰'ŸÙ§( ‚#‰Ã75 º¡Gж-ñƘ# ÐÜÜLã¡«®úôªæ¦¦ñ…\éT óg·½QÇ2îÞõo¼K¹Dt„ð‰© .~åµ uæÌmB<›G2X‹/º†pG˜ ÔêÛÇÀÞntu´¶”U€Y¥ÇDJˆ¢mg)%~&“®áxÃú˜3€ïû€çy>²ù\ÿ¨èßÈÂápƒ”ÂÆÁ6¨‡ ujœû5'¸òjÈçó˜3³ ôï€úƒYÔŠÔW/«‹¢ê÷Ñ„¯0Påï%kÎCÎwÊû1½>aÄ3p¹Äîá :šêÊÄ7«TÀáÚ¼ÁqÙßÛ›UTÍñ¼‘<@5àÝŒ#G>£Ñˆ€SF±cûöÔ(ãgäýuuuÍ…\aðMRž²®®Ž ˜Ÿ½êÓ¨««‹¦Ó9ðr/¡Rƒ^ñ7 þ %„(<-±Bx)ø!RðßßHøCþ&8º&FÐŸÈ é œpÁ¥ˆÂƒVé\®(ÈïÛC×Õ2ìWžÊ^¼¡ÑS¡P`,Ïó­áxb¿YS¨w͹€‘Ë»&M f2Ùè­?ùáMºaàxÿùÓ¦M™ÀÔuÝÄÁ‹§5Ó D,«˜;L¥‹(½ @ÍÒ%‹/ä¢d”-œÛBÙ!pÿfVýh£|8Dqð£ÜÅêϨ&üÈ#*öC‰Ñ&OÔÈÛhž=555(¤|• …›Ï¡œþí}wxUö;åÞ¹=½€¤)E²RlWé@Š b¡èª«±ƒ(Š‚î"]Ve‘öíÊ:RQ\JH’Fê-sËÌ™óý‘™ädrEewŸïËyž<›)wæ×Þ_=¢þ#0 gÞØ¨Q£BHÂksçüÙår%vèØ¾Ó¨C(…çyVsø›VIüfÍšº³Îe'~´vÕ¦öºtdµ¨¨Èÿñº_}óÍ7¦…Ãᱺ:ô¸\Îd¿ß_^›;é°;¤˜˜˜† ¦Þ$ËA¨jEEo4Uo¨í*O JÕ´ê÷:_… j¾Â|¨•Á¤ª¸ à•bÞöH\ÕŽ%¾€lLƒ5Þ“Yš…¶mÛ8¸âbã:½kç·;Ü<@Äëõ©3f=?á¾QÃGhšæEu%Eý×€ÓAÀ‘Ó`ýºµÿjÝ.½Ýs3¦¿_ZV^˜“““Ýí–nݪãƒ{þÞ©cû[õàHœ$Ùm¹¹¹…¨¹Ã($Iâ/]º,>}ŒµÙlÖÙ³_[wàÀÁó3žŸ5v꤇'ª*qê“ÃlŒ)ù]æÿ& ëªjpýúu˼¡UëÆ ß~{þW;37+Š"»Ý1Öp$ryàÀ!/[ºbÍâ¥KÞùpÕò÷´´;ì±€Ÿ0SÉÕ›`»±eó›U¢Á"Šªß°óšV‡/OtU_ÙGUëµH<¨VÑlª©¦dRác5¢"QàNH‚M²ê{XR› ûbnªoSË[,K8¶õé{O½ûöœnÔ¨AƽÆxæÙoÄxÜ‚Ýa‡ÓaLzlÊ[|s~£}êO“ŸUU5^EO÷Ÿd.!!/++wøÃgŸ}r´uÛ¶Ék?üàƒ5k?Þ @ Ê9µAJ²‘^µjõ–În™b‘l©Óžùó*Ç#=úm 7Çq’Á‚ ðn·Ë’˜”àNJN¹^QIEÄñË+cû•aÜ*©¯TÏ?KxZ“ð†[h$‹t©g‰oÆÇCòÄ@t¸ÀsxŽGJB<öw²Ò `U%nÑ¢wWÞÕ«÷æ-›7oê×wPïK—óÏÙl’h·ÛmÇãòåÜRŽã|<üØÜ8nü„/½0s¶ªª ‚ÀÇ3¥ßÌü¯Pû\bb¢P\\ì´Ûm¶mß’Ù¼EK˪ËW/x÷ý|…?,dee]IˆOtëê/âr¹¨d“¬·Þz[šÅ*œ¶Jzè¡ÁZPJãxž·l6›ÕépI)))î²òr¿,Ñ!=5ª/_…쫾JÝG—ø B2É"ƒð•*_«Æl5®£c M?–(x»œÝ"ˆ€hÁõÍ›áà±ï³ô!š!Ä2kÖsìÛ¿çdBBróZµ,ÿc¿~t‰¶P «Åb±¢Bß3@P:~üÄ9ç²²JÚcþ[s¢¥‰¢˜ Çl&spÕLÀ_­Íw:bQQ‘³m›ÖwmÚôù¿DAÿ¶fÍç ß{5ÏóVMÓ¤YÓfñÍ[4÷èÕ;Äï÷+§Ïœ^ðý‰ãr^~Q®ð£iÓëSOŸ9õî€ýhš žç9ш;6&&)!!±U0(ƒçEÝF›]:¸Ó õ¯FAõ´¦Ä7ÎÕ%ž -›Ý?ƒQ*õcOþIDATNQ ªBG ‰"BÐ$5ÙpP)¥B»víZnødý[cî¿ïÙ[»ß>hÔ¨Ñ>ttY£Ô”6Ͻ0ëBˆK{jƒT·À ((,ôéšÓ oøðQÓ²²²ò›6kÑ僕K©ªÚX„$&ÊhùµLÀ_ä°×]wõ¶|势;œ.|½ó«’Áƒwà°X,.#óuêôéãee¥zåV¯]óø…œ,´kw³ý±I÷Š„*Ë28ªaìãFîß¿gÓĉù|~Çq‰ªJbš7¿!ÉårQ³ª7}¥ª¯ÜÕ âh5Ô½¡ê«Ù]ê+g$žeª„#åáIiÍæBYXÁàQ£ñüüEß Æ?Ømë¶/_Ñ4MlÓú¦<ψ¢¨Ìž=gãË/¿ò×ǧLy¨_ÿ~wȲl£”ZDQD«V­8¶**%9Yt9ñö}S~GÏ^Íׯÿè#BHK† $† ®ÊCà!ñùíÛ{¸}ô‘‡Þ~畼ÀÃnå×çÍŸPZîU·lùü½¾ÍsæÌÝQ1&âô3ßÚ½ko^´àðქûvï9i“¬Ü¿OŸÍ•CAÊË}Û¶ÿ#«mÛÖ³†edì>yòGÑï÷Ñöžðû}!‘jèMÕWwçØºT©{M­+ªÔ½É `$¾š+ ŠpD Šð{ˤ<\±q––”–†oïpóÌGydhZãëgŒußteš¦©ªªBEmݺ –.yDzË_à˜6mzÿ’’bmï¾y‡Ã @è߯o—M_ü}qFÆÈ©óßy{ÆåKѪu«øÏ?߸Šr#€ŽãbLšà3Á/eûùŸ~ºaÔˆg2†yËç÷i"­S—n£$Iâ4iëÖk·nÙ´åÅžª#_kÅVÁH3fô„‚‚|ä^Î;uï½£§ïøÇÎï9xìÑÉK—,Y¶ï–n]ÝØâúøçg¾ëõzbsóò)!*á9¡Ò‹b³†:6˜wÑ$žuéêwµ¾ê³ŠßvD£ˆ€C?‚‘÷Ãóï.ßGTµDpºñôSOïp‘ɉPDUU@àå—çüõÔÉ"ŸoÞôjZZZc¿Ï§°I’ÔpÑ¢÷æ<;í™™#GÝ8:àÂÄ ÌÔ4 -Z¶p?ñø”u—k¥4¹sð›ÀPý‘Ù¯ÎþfÏ®Ý qqq°[­|§ÎÝF¹\N>®È].^¼ôÿô0¤o§Î]†ìüçöO¤„Ãaü+ó«yªÆcÇ¿1bĨÙÔ§Ÿ~òý4eÉâ¥Ûþ6·íMí-_¹˜ÿçÎóÛ´iÓ8))9†çxÑf·"¿ ¼šº7o¶çìçLÐàgOjDú*Š´JFcïËÖ×UU^’ÛWæW¾ÿîxÖ¹ììKϽùí‰é¶\aª¢Œê r‹Å’·víGïÜÔ¦m «dI°X,Z×®]»lÛ¶yi||"éÑ£÷óóò.ê生ÍÊúnÔÈûf œ€ôôtâvº&h¨×YÇ_¬~nr!‡ å“ f~¾í PBбS×{Ýn·â÷ŒîÕ€âÄÄD 4døžý¿?{öÔüS§OÓò2¯uÔ¨1oe;wF¿¦R\\¤ØívèeQ¥>8~yDQѼEËÔV.òÇ$gNŸVä`°Äf³#·À•ðfφذm4›¥žµñÄH›RÂUįA$Ååü+uÿxuÄèû¶ÙlRRZtðÀÁS¡`ð²žÚeKÀŒ’0!¤hõê5vlßz()%•w¹œ¶%KMêÚõ¶?}ß1­ö»q¿j=Ìß/äq(*…¼bÅŠ]¹¹¹§ÊÊÊ.†B¡Ë¨hv)Ö%V6ưáa€dµZÝ‘HÄ À-‚Cß4:¬3_ÿ×À+ÏónMÓ›5kÚí“O6,°ÛhÛö¦¡n·û‚Ïç+ÔÏ ¡z‡Qµ¤›…D¯×ë ßš¿säèѽôô”Ïï?Çó|¹¦iAýx€@“÷-ÿ駤$'ÀïZÍ›8L‘»ša[Õ›%¾²„ì*%¾Rg ä\aw7Á¶mÛN®_ÿÉ>EQ/ƒ¡‹š¦å1êß§3KxZ‹«Í› ?`zfæ1Ž·èvHit]ƒŸþ}ã¼CßÂÂï>•sþü:s…uVÖV2@“´4g«V­2Î9»Æ[^ާžy3g=×Sç6ªŸ,3@&ÂÚ#è`w¸2¾´ÀµeëÿHIiØ8?ï’òøãO?—ŸŸwQQ”RBˆÁéÆl«Ýn¿. ¦ó;×ÀQ„B2®"½M"a9ºgqÁUªúhÇ›Ï-6„‚^dçÅ ­qzõêó™Ëå8 E.Br)¥y u ˜ì/­«œ.JáHm»„˜™À À–œœœØ©sç[m¢eÅ®ÌLrßý÷ YôמòfhJ…*h"uéÒ¹ Œ[·m›Q”ýE°nˆ\° ,ºen¦ mÚ´¶oÜøÙ~—Ó>æÉ§Ÿ_tåJ.!DÖ4-d²ua¼ªªâî=™kƒÁp˜R"J’ Ï!ë\!7Šƒ¦UŒ[1ˆPhz‘hè£URO*!T†–k^‹BøŠ€‘ X‘}>Eå8]qZZZSnhÆÖ+W~¸WÓ4£÷Ï˨mó;ú¹ÊªhM#µíÆE)§Ã•+Wü’Õ2˜ç8ÏÚ߬îÝ»w½©©Z·1ëX¸-zlaAAÊ+¯¾ò…þ™áë{MöÈx0žQý샨 Úåôc캯*Z­ÖÔHÅxYa®€Î<1»÷d~ï÷ûsúõ0@ëv¿D4BËËJ¸ââR´»Ñ†‚" ©I¯wfïÀ|ëpœ€ƒÇ#hÚ´ "JS&OÝj·;Ifæ®AçÏg£wï»pÛÐŒÀ·_ÚÎÅ]E Çh€dÅ8EUS^›ûÚ#?Ý¸ëØ±c—PZnÂÕ@´X,vEQ\z‘†ƒRjÑ 0À/lB´ÑüL–s5& aeì•C'4‹-"¢ETEuîÙ“y: ËçúÜÓoŒ(Š1V«%I–ƒÍ÷ïßõJDQhYI g±Z i\¶âbH  f&|4sÁvEs)¥C@ö% pôè±Ëo¼ñö.—ËQâ÷ËÞôô›’{õºëÆŒŒ·ßygïa‡£@–å\Tµ…×ê‚ýÎÙ°éøÉèH“Zf[ÕU³ýa/À¨ ð 1(¿.έË^Y˜p¥Ñl¯:±¢#ñ{÷í>æ÷²úÜÓw¨(Š6UU-œ.—3Éï¤:´ïõÂÂ|(‘5¾~?A~~!Ú¶T5R»»à±ÕÅÕ C+>ÿöG ž'œv~säüŠå+÷Rp…²*&„x)¥rÏž=Ó4hÐpÒ¤‰ß~{¯»Ýnw¡î‡›Ñûï¶ÓG-@’}¿Ã%gÜI™1ˤ4J”uÂX·AerÓ‘(ÜÌýL«˜YU ¦êV.11Ñ^TT”4uò”ã›>û ï¼·#FŒL·X,VEQ(×¶è&ä¦Ý{þõŠ [Н A£4<¼åÅà‚MRà¶“š„¯ܱ„§´ÂÇÏÉåÉjCfæÞìEï/ùÚáp–ÈrðŠª’ÆÝ §§§§&''7zù¥Yón»½çJ8Ž+Ñ'„°Z“\C&`…Œ¥¡1y%Ì053€™@B{þ[Tå‹Á(uá‚wC/<7iii)‘·²sr¾Ô_žq¥ÂsVJiâÃ'΋‹é°uË̘9 ^€§ªJ9BÂ!?@ˆs« Ä%Ç*Ñ@4¢ª•Ú!QÁó$«~Y eââc¸„„xœ=s¶¼¤´<ÿ¥—æ|åtÚKC¡H1!¤”‰ò¸…Úl¶ŽçlÛºi㹬 '>|äü¡CG~ŠDH‰$IeÁ`¨LÓh*¦{”26UÀ…B!@ñ¡ÃÇŽ­_ó·''?öØÔn·Þöñر÷¿ ¿ƒc ~DZïÌûe±{¨;suHimH”þNœÊb€øqŒ»ÝnsØ—.]òo&jL 1$ˆw8nY–NvÚ¾’’bdîÞõM£‘sçÎiÝ»w¿Þn·»-‹µoß¾­Z·nÝ@ŸPfä1Àqxžçrss½'Nœ(RUE‘eà‡~(,,,(£”z ¡¾òòò²HDñ¢y)¥’.cÕ?ªFÃYX>þxÝON™Ò‘dy»OÌ`üp³u­@a]t¤?×_ÆÕaϯ…ÊØbccã)¥Îòòr “s0< >%A’!NI’$¥¢Z9Ž—‡ÕjµJ‡Ã.˲ÝãñÄE"Þn·Û$I²Z­VQžR EQÏsš( ‘`0 AY–ýÁ`H‡#%¢øUBdJ©ñ|úÁAƶó,III®+W®4}p츽GÆûöÙ±`Á‚P5ÉÏh‚kÒñ{54äðß],z5\DQdgë„Ûe„?=z§‘ÇðZx}‰¢hµZ­vŽƒdµZm¢(Zyž·ò<¯ƒO ¢h„•¨E‰¢†UU¨* i RJÃL€J6Å÷ÍèÞÑ€¸—^~¹ß -[¦3f½(ŠŠªªÆÄ0V›‘k$\¿‰Kþß7y x!&ôJ­Á2ŒÕ‡/p\EA¢(Š¢PƒðV¾¢q(§U4M£!Då8N¥”²sŠÂ¦¨fØä ±ƒ*Ý0Aì„.Žã$ZáfÈŒþŠÑ5Y"þ7F6ò ”ù\W˜g¦ñ+:XE±0n§Àq1Jk†_)¥ì”ÒhƒœTÔœvSðK!„LM(¥’QWÅþ¯ÿE˜ñÛ,JQûxÞäòðu¸DóÿhÕ1Õkƪgàj ¹ª…€fLÃÆS8&ža¤ŸÖ3@íH´Ý±h-4®Žr7>J–­6€¤¡æmµ i¢u`.J<ÅÌX´^ü¶d÷ Ÿ…‹âý¬{„Ú'sÑß Í~xÊÿó p-Ÿå—Cø?Où]–€ÿ?ÖÕ2úµ$Eýª_õ«~Õ¯úU¿êWýª_õ«~Õ¯úU¿êWýª_õ«~ýG×ÿ‹„—”Ç:¸IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/editDelete.png0000644000000000000000000000013215101070305017245 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/editDelete.png0000644000175000001440000000153215101070305017236 0ustar00rncbcusers‰PNG  IHDRÄ´l;sBIT|dˆ pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<×IDAT8í”ÍoLaÆï{¯ÖiG§Ó3‰e22c†¢J£ •"íBRbCcáhÄGb)V A°±ÁJ+_ Q"ÓúÌÌÜ¡(3Õé=s룦í?àÙÝ÷½ç9Ï9çyü‡ c†sPhÀP€ T>÷{ònVb˜lX›Lv¥m;|òÀ\` °iM"Ñ™¶møÜ3Vºµ·÷*%盚¾ÔÖÔœ6›-\xþVSÓ·A¥dÿÆý@·3]õxŽÃ[¶¤¥¤òÖ0äJ4:V]]}eM<~÷}8ƒ €\åñœ6»jõL­˜$÷1àXOkëèHäÈ3ÝÉä(pˆ»ÿþCZ.‹ü –eYûÞ¼±­5MÆ Ó0Ø–ËY¦iBÉjc”±Z9bX@ò@këŽEïÞéœã𼪊le%ŽÃŠLFw®\¹XÌ™­“0åK/¾ôÙëu@N…ÃÅ`}ýÕÑèÓÇ–% WÃá¢Öú°`ÿ¨õ»Î´µ æ@Î…BÅú@àÐìlI$î?±,ç)ÈÖÆÆÇÀ: b6bhˆE"gíŠ ¹L„‚ÁË@30Òô;[‰»÷<çd,6BéqX®¨¿Êþðv54Ä^§ÓÎA¿ÿF&•: ôßÜàz{ÇŽ¬Zµ;äóUi`‚)‹H•!®õûýÍÁ@ ò2•z¼¾ò{òšÒv«s+̺IÿrÆTâÉ@¥iç)Yoª¿VvmþÇ/üã7ð¶Š¥åIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackIconGuitar2.png0000644000000000000000000000013215101070305020350 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackIconGuitar2.png0000644000175000001440000003416415101070305020350 0ustar00rncbcusers‰PNG  IHDR€€Ã>aË IDATxÚí½w”e×]çûÙ{Ÿtï­[¹+tuR«»­,Y²¢mÉ6à€81,æ=ÏcX̘0o`Æ<`ž±e°aˆÛØ–8[’¥V«ª»«»r¼ñ¤½÷ûãœ[}«Ô-9«»©ßZgÕ­Ru·îý~÷/ÿ~[°!犈SüÌ~/hCÎ’€B µÆ´ÖfyyY[kB`­µ8ËeppPZke†& C|ß§Z­šÓý~nš¦6MS›k{*°A€3\„ÖZ,,,¬‚íº®J’ÄlÚ´é<¥Ô€Ól6­BA ›Íæ“+++OÖq›¦i èS‘`ƒgºß!„¸9Š¢ÇBt þ‰”ò¥R c µZ !Äþz½þûÕjõ_€Šã8Iš¦QNÓNµñÑžZ`D)õ1!Äÿ•¦i£Ùl>Ü××w‡ëº¿¨4M¢¿¿Ÿ••´Öýžçݬ”qÏcÚÁ_#8 Äó¼A¥ÔO;´ÖI’$i±X|—”r‡™O’„žž©ÕjDQTð<ïÚ4M§Ò4=D@º¡Îq]WcÚÝÙÙù­µ…ÅÅÅÏYk—¥”#ÅbñVÇq®n…zRJÇ1õz¾¾>ŠÅ"ÕjcŒEÑJN€ ^o6pæxù¢^¯`Œ¡¿¿¿Ïqœÿ!„x¯1f¤V«}1ŽãG”RWwvvÞrùå—3<ú3ÂÁÓÓÓk¼ó8Ž…”ò¨”rBÜ$„(xž÷B­5µZb±(¬µXk1Æàº.q3>>N¡P@J à¥ü €: [ÀFpfiíJ©ß2Æ0;;ûÛÆ˜Hqcggç›|ß¿°P( c:ÇÁó<Ûl6EÇ´>RJ„¤iºúý·£(ºø6p$×µ\Ø p†ˆïû®âg„o´ã8÷Æqü  tmÚ´iÏ¥—^Êòò²ššÕjU„aˆµ–ááaÇarr¥®ë’¦)Õju>I’J›Íëý†pæ8~JJéZk»„ƒÆ˜n­õå===¿P(./ !h6›baajµŠëº vïÞMgg'cÒF£±¸²²r8 ÃÖÚQà(0 TrB%>À™#Âu]Â0L¤”‡<Ï{L)uqÿËúúú¨×ë¤iŠïûvnnN,--†!år™îînšÍ&GŽA)…1&ªÕjQÉŸŽç¯ëm¡àFx&À˜,?ÓÓÓsƒïûÓZ÷ sõÕW#„ Š"¤”bqq‘4MéîîfçÎôöö211Á‚žŸŸ_B$®ëú®ëjkí˜1æ b˜sÛ¶‘`Ã8£˜ „c­…B­5=öƤ”ÌÏϰeˤ”DQD$I---‹Å‘žžžkMZë#I’ìBT­µ•uà¯æ6ð<‹’’+¶ˆ‡Æ'ìb­oê§Çqh9yFß÷éïïÇZËÜÜqãû¾‰ã8ô}_c„Öz_Ç=ZëR† ÀµÖ6€f[ •Þ À™ o¸ùzþ÷7î5»úº;н}XÇC§ RJZ¥ß¾¾><Ïcrr’F£AÇñÔÔÔD©T* \ÇqÏâââç?1'„‹ÖÚvÛÿŒbОGùooy½øµOþƒ¸}°ó¢‹wm{_U8ª…T­Â$ ïãZk¬µôôôP­V©V«“iš>Çq1f(I™$‰F*P·ÖNç^{ ˜ œòk¯ÿ‰Uð_½gëE·¼ìŽ?ñ=÷–ãöƒvìÁ†Uëà:Æ–——) -»ßŒãø(p,ŽãÃ+++ËQi`Xʽÿzn÷[ ½@°Q xþäýwÞ&~l^Üã]úâ—¿òÏ®{Ù7í¾êZVæfm}rBtûÚq©Ç ÕJ…Z­ÖÊõë8Ž£ JAZë£Ífsožï?žƒ¿’ÇüÕ\œÒþoh€çA~ýÕwˆßùô—,À{^rãÕ…àÃ6M_Xèè°ÛÏÒq˜=„^Z±ã‹ õ™¥åÔ‘RZkm½^ŸrGôöö^`Œ‰¢èx®öÇÚ@¯æ'?ÌÕ²Þöoày¼æñOeà¿ýÚÜÔø.–J—Õ«U¾ú·m¶^p¡¼üÖ—â¸û¯þ‚Çxdúð‰™c*J©ÐZëy­µŠã¸7MS†a”ƒ[–ÛÔ~ üöv0Ö“`ƒ?"ùí×Ü!~#ÿ7^}[oWׇ0æ’F­j‹å²]œ™’BI.{ÑMLLNòÅžÚ{bæ YwRk]ÍmyÓ“.--ÑZ÷Zk[v¿‘?õ<ì‹yfÈFSèó!?ÍÅêÏ÷>­Þ{ëuoèééù“¤Ãi’Ö(Ç‘A¹Ó:R°÷ì=þÕG?Œ !Æso~¨ !bkm’ƒYο.åÿ½ÚæôÒæo˜€±¼÷†+ÔGî{T¼ïÅ׿¹¯¯ÿ•ãlŠi’&‰BH­StØ"(pÏ“ûf¾úÔCÀ¨bÌZ{Xh©wkmÔìlþU·´CÛÉ·Ïþ~ÈòÖ®t>rß#)Àÿqç­ïºþewþÏ PìÜ÷àýè$±~!qâ{Íf“ÇÆŽMß{äÄá\í[k“åñçóÞŠéM›—9È)k»},ßÁdÐ~HòŽÛnPöõûR€÷Þ|Í/mÙ¾óì¸èÒ wx3Ks3vô±G…W(âº.õZ½Ç¦¦¾±ÿð`pÜf'|ž“…œöÆNhëìÉfNçíŸN6ò?yËu—«¿øÖ^ ˆ÷Ýzݯv÷öýw¥¤ŸÆ1Q³i{‡†ÅÀ¶í4–I“„ǧfç¿üØSûóPn˜à$øK¹mo®;ẠpÍ3Ó¼vƒ?byéžóÄù#[ŧ~ÜüÂÍ×ü—R¡ø;X£’(bi~–F¥".¾á&FvœÇÁ‡àžGŸXúæø‰Ñ°Ù<œŸüS® çÚÁ=Õ÷ß±l˜€¼æò ŧÛßúð½_ýÉ;~[Xý+:IЩÆh“Uö*ö}ëÕ*ÿrÿÞ¥Ï=úÔÁ\í³VíŸêä_`oh€’¼çŽÛÄ_}kï*ï¼öÒ–Êï/–;1Æ`tš!g ¨.Ìñ¥oÞ»üùÇž>Œ Ááœ3m6¿òàÿ@d#ðýzú×_)>qÿ#-`‚ÿpûM¿så-·½ß ìð~Ò4ÁhMG8R1\ªTî~lßÁfŽ*)icŽ“œìÜiïÝû¡¿a¾ßÏ‹®~ïÃ'ÁÙÍÿk`xó;/~á‹(÷ôÚé#cbêÈ…ŽN<Ç%nÔyzz®z÷ãû'ÆŽ))Gµ1Çr›?÷£<ù&àûÿ…—‹?¿ÿQ àBç{o¾æ‹…Â;=? º¼dšµªÜqÑ%töõ³23‰DðÔÔlõóÀw)¯>³|êª;-=Å«Îß|ñ®Ý»?´çŠ«_¬”âÄè!kK¨¨YÇhM£²ÂѧŸàÀätõï{èP ‡€Cv-ø‹Ï×Éß Àw)¯Ú>(?=:iý}~ê‚í—í8oLJ·l»éÒÝŒ_(Ø¥Ù6¦)vvÕkHcxrüXås>u8ÃÀ˜€£vm¨÷¼‚¿a¾Ó“¿m@~æØ¬xýžm×oÞ<ô± (¼P*…NkŒ½CÃxA€#„µ˜œ®þó#OŽŒ%«ìÛSÇùíÕ;û£~oxy÷Õ‹¿Þļá’7lù¨ã8—¤ql¦¦D£RáŠ[_ÂðöyüaöOoÜýÄþÑzœŽ‡…`ÜÚg8|çü <‡üÚ‹¯poêýÌ¥»îܼeäK;ŽKÇV§©0&ÓÜ:Ž™?:ηz8ü§ûö®DÉzðç×%yžwð7ð,ò—ß"~û+÷Z€—÷üâ«®þèu?öò݃ԗ—­N‘¦Iþ! j óÜÿàÞðs=1¶’êCøbÜZ¦rðן|ý|ƒ¿A€g‘¯> ÀÏ\²ó5›·mûc×s·\týto°ö> Œµ¸¾F#áØÂbü¥'ŒÍ7Ã1à€qË*ø-‡¯Î)öô<ŸrN×:)j©á;ý _ºk«¨;÷ï?dÞzÙî7ôõoú#ÇsušPêì²ݽ¢³¯¿Xbzìi³Î‘ÉéôîÇöM×›‡â°°ö =™ÛŸoý€ÆóN€³Rœ_öÄR¬Ÿó÷b“}¾J …@<×§=¾Xáøü"o¼`ë[†FFþ ³¯k1:µõJE,ÍÎrñõ7på­/aeê8û¦ÿ²ïð‘ã+ÕQ„ØQ 'Ö©ýú:µoΔÏò¬ÌŒVãU,w—¼O‰ë­Ã+±6I ã3‘ÞWMôP×HO {ÊÓwÃ-7‰ûîùfæíïyï¦Á¡ß•RvZkL¡£C$I,\ÏÇ$‡z¤ZaÿèXúÙ92Ykއ%Œ™gzûuNÝ¢}FÈYcv–\1VOV?¼‹Ëþ ”?/àeÀàæCoÆBËtjí·ë©ù±FòY ªäH[OÍjãä ¶nOLLæj×/ŽìØù»ÊQ¥°^3Fk”ÊÂ/iÖª¤aˆï8L/¯è/=¹üx5ëäÉS¼“d•½²†ŽöÉÜ3ü³Æ´ƒ_¸tùÿAIñî6‘­?s¸€' $ƒJˆ«|%_Þç©+|)¨¦¦Ôc ¤PžRô•;›·w]yÁÞ¶k÷ï^{Ç˃ξ~³4;#Ó8qÔÄõ| Å®”,Tkæ«û;²\ËNþZðçÎðÏ ô:RLJRÍîw˦¢÷'Bˆÿèiû5{:m&Á—Bì*8ò'z\y¾µÔ›ÆÎ§6}wªZàm—íú@ßàà:ººåÈ®=ì¸øR±4;Mmy)a½ŽïùTµI?¿÷Ñ#£ ËûCBpØZޝ¿ÊÚ}<öLý|Ïxô®¬6c{A‡»-pÔGWŸøç4e¤’â‚¢#oõ¥¤’šéjœ.è×õWü·þMïÇZ’$fyvô âIa“„Ù¥åô‹>ytt~)ŸÚaÌÚ5Þ~ëä‡gøg<Î/yêX#6v¸C¾£þx¥¥ VdX]Šø½a):%oõ£_{ÉÎwmêï{w©«å¸DÍõ•eg¦Ùºçv_y5ËS“œ˜œÔ_yìéc£ +ó$ϘµœàdçrøúlÿŒ&Àù%OŽÖcÈÍï7ÜÏ÷­”Jè4 …4MVj Ò¦¸®ól !ÓÖŠ¢£.»zûÈ-;·o¿E*$::@ â(kiÖjHrøÀóÙ{8~x¹šlA+ÔkyûËg‹Í?kÐ(µksigðcBò?­µžr]Ib¢ÃðÙû˜8°¸rb øëOþY~nÏ<¹ ìÉCÕ8íwDA^t!p],þ$ŠÑ^úú7ó_?ü§¼ügï¢ØÛGš$¬¿'ɉָR²kxˆÝ;v`¬Å-Ùµ‡RW7Z§4kUÏ£«»‹¥j•¯<úä‰}óËò“ßnóW6yæàø~²SB8ýE÷2!xµµÇqQÊÁäË’Dðû…±1;vŒ¡Í›éìéÅ m°¹¦ÅEÛ·qÑž=:»XY˜ã/~ž©#c\zã-ì¼ìrtš’ÔªÔÃ{™~jn©5©;jYõöÛÁoßÀuÖÉi"GÙ(Ñv(p^/„x­×óQR¢Ó„¬ BdOµRáøñãxd/ãO<†Õ)R©UðSc¤dÏÈ/ºíÅôo¦Q«P]Z¤º´ÈÖÝ0²ki3dfj’¯?ñôÌC'f[ãZ‡rðÛ½ýõ'Ÿ üd[Á³Äîîð‡}%ßµ»×Åq=, uºj¤”` ˳Ó;ðÇìCG!ÊÉ2ÜÆZR­éð<.Ø2ÂÖ¡!Ï£wx©õ•”ë’F1 “'ÐIJui‘¯=ôè·'fãJˆQ“k·ÛüêiÀ·gθZ@Ÿïs¬™âH¶)Á£Ž’c2ÕÞ–öR"¬%©×R"¬ÅX‹Öšrà³{ó0çï<¡s'ŽÓ¨×ðü/ð3â5‰›!O?p?ÕúËÓ+À WŠc‰±“¹Ê_8øælÿŒó^Ú_,×-@ ÅË$”]¥p… …@BöXe@¹.B©UÛo­¥«Xࢭ[ؽk7ˆl”¾³¯¥™i*‹ ŒìÚÃàöJePŠZc¡ª” žVìÉZþâiÀ?cʺgÄ)žÌë/¹âe›:Ä¿Î7,À%eÿ¾”w)JI¥œ_µHÐFkO:~ÚÊÏ#›©P®K†¤IJßðH¶€ùÄqŠe”ç17;b…JbÍb]×€Ðf€Wò§vðÏjù›€‹;\éJ! J W€+…0bc…µ „l¤­Ù[ 5`Ô{ žµW]Vöwuºê÷„;µµ8Râ*•í@1‰Å | Z,"¿?Ç££«!%j… ÔãyÄQˆëy¥'O°4;ÇüÒ ¬X¢’ØäÞ±æøL59"`A»œƒßŠó×\¸t.È„—wúÒHW î_ Óïæßï•”† N9pdoQÉë])ß â* H!­ë8•c-Vä*K@"]´Y^ w ¤N±iJˆ ÀÑ…%Fº Ý¥õÊ2…r'…Ž2Ízb©„t<ž>ð$~¡†WpÍCÇš“+Éa²‘íIkW8Ϻ ß„WuR[ÄcÙI^Ý[³»äl*9ªì Q‚GAkm%…¤ [ % KÁNà C™g0ÆXåùÂ÷< ‹ûƒ ý '@ZCkõ­7èñRºÊ¤R°T«¡­EMW¡@Ül"„¤P,R[Z`ÿ‘c4ÓÝE—GOD“ã ñpDŽËÏìà5œcò=`[A )ñðJh]UÚ^r/u¤Ø£àB%Ø)`P`ôȬF_+%ÂÍŒÀIÇÀÚÖf¤DJiQJ8¾tZk¤(Ga•Äj°Bb„@fÆcÀ–{pJˆ¨(D¸.+µIqÞàÝR6êàyŒÏÌs|~†íÃ>Ëzñ‰Í#¬nçb:õÛ ð]íÞ9g p^Ñ•ãÄ€¶{ŠÞ¦>ß¹Ó¼T {‹„ó$6sØ”DI‰2¿Î,Ëϯz€m¹zXÍê)‘J ¡œ,îϯ@³Yà–‚›­Ç²6C<Îr™HJ´1tƒ ›8R[Ëøì[{ºéë(±h%ó2°Å‚#bcÂG&ÂcÀ1N^®´ØF€æ¹f÷¿'\Þé‹Ç*‘ÍÀG\ß¼ÆòJðrE p×q2?Z€K™yì"+â®~•2+éZkÑyæÎšl§B ò´/!prîh ÒZ<ß§«« !Zk7`QkÊ®¦ ±Ö„QÌtµÆ²ô˜Òß„6ØÑùpf¥©Û7m·Â¾Sõðÿû$À-ýEqO¢]ÔámîñÔ¯8B¼[`x®‹çy8ÊA©ìÔ ,ÂÚüÄç¯uN!².·õXSO4JJJ®Â‘+ÈT|þ;YÒ/‹!èN›4¨ HgWgëÖ,¤Äq„tâ €hTðÁ/ÑåšœcrvÎ^µû|¡e!9¾ÐX"ëák©ýåüÕ<É“¦Þj1z%š‰!Ôö¬7 ÏI€Û7•Ä¿ÌeÉ™«»ƒ+)ÿ%¸M’…hïãyJJ0¡5ƒùK!p¤Xý^½nhÃB”°'TMb@ (xì(PR’¶Ú>ƒÀ¤6M‘@_Ú¤Ô:;H¥"ÕAv‹¶’×QDÆb‚2®Q…GW8v䀨FÛ·îð&ç—JõØÔ9¹g·Öòü… íp¤­¶Ÿ{™ÊʰÎ}¼gm-àŽñ•üëz‚WJ~Ä\«¬Åw•)ŠÂs½Ìk´àHp¤@É x/Ý©¡šj¢„£µ&Çê!5#(K u•é+©%)‚Œ®Øì“ÆŽbÒ(ÄêŠ%Êxª‘Š””¸®‹Ö%VJjMæë ÆYÕåî;0Ð/DX+®T3 Èóý ®±B7µM~ý¿ Éø yw§®;ZKö¥Y{ùY?XsZÜÜW_ÏÀ¿¶'xµ'å;p¾Ä⹎ Eé*…Ð&SË2³Í*OÙ:€¥™$ì[®s°Ò`¦2ÓiË`G‰]E—aiè³)ea(ûݎĉ#tc’˜4ŽÐq„Ib¬Ñ~¡H¡£Œà%>†ºíxxž‹Ñš–¹²¼L3Šˆ¢˜$ŽÑZÓÝÝTR(Ç¥;ð\[]êk¦f6ɶt.u uc³BÏ”·?ùÍð÷´±ÿ%º¦ëém‘Aû#Î \Öˆ—šíàXÁˆ²Æx®KP(HGH„1¨<5«„@åö]é•ÄÔ£˜ý•&ˆDk¬Ñô^Ð[æ<Ç$1:lf)Ú8¢d4N°I :…TcuŠ5A~±H±£ŒŒ6)q’ˆ‚°Xß§®Wã VVVH’$»wGkjõ:ŽãPîè!é(•ðMB²²ÜQrå)dÜЦuͺük ¯ŽBóÇõоÂu$~ ªÇ+IëvŽÖÚö³6AtJàñ•°eó_ä ùA ›¥µÆu]ŠB PVSpäj&Næ?I–[NRF s±¦$ÀS’îR‘ó;Kô˜˜$Œ2/^:(•%lšdŸÈàVÂâ HŒ%µàŠxåì·W)â$A(E8a+}jʧR«çàÇqÌÜü¬™5jÌ7BžŽc|G!Ÿbà°-ðÙä)¼$Æ„Õ|©½E[Ràž¢&ݺõ KøŒÉrõŽnÆŠ>Ž é7ž4ã 8®K¢D–Ø¡ÒÕÏóK>|€°Ù$I²*£çy(åÐÙÕÅpO'•±YÛ·ë>ëd,¤ÚR.(\OÄ“ÓñRî#œ™ÁU ûŽ˲|¸’×IÁV‰ÅQJ:Žƒ°YúUå'·/ð¸b“KÅ( ä:tû.]Ò"£[K³ Yɶ• t€DÀ‚‚À@¯ÉŽR«„¨H“Õ÷J¥"A±À2‚'ñÙƒa ªã žOªSTš¢ü€å®MìYâð«o®>€ïûøÅ"Cýý¤³“ÔæPŽ BæúçôẂ[ c»&IÄ÷¸§ÿŒ#@¯'åT„¾¢Ó¿Z ^ÑÛó\”Ȫ-Rfž>Ö蔎Âñ„µ˜$Á6ky:7óØ‘­†“w›Ì)˜p d@¥0¨³ÉÎÇ^åb‘b±”‘ÂhêBñ¤vØ­ ìtYm“…³å~˜Y\þšPG)\×%ð:=‡ÊÂÖ”ãbìé³m)ú’rIêý á,'/e 9ƒÇ¾¿{ ¤ð•¼A qÄ¢”ƒ’2?ù­pÏ"ŒEÇ¡1ùÏòv­V…¯•êemûO‹Ù,wUÂ1'Û±Óo,—é:©„•r/~Á_íí“B@)—£~ ›6Ø)š”Š&K=Ü7uzð‚ Àó OZYƤ)R9«Ûé$ÖYz ÛÅ([=¾œ´Ö¼µöøGç^ÐéË'*¡Ù䩲\ӲɭV,‘ßaߪÀÙ4A“«õVÜ-Âæéß“àŸŠÃ)8Ž:P‘0æ@šX¶ê˜žr™'‹.V`l–÷Oâ¥Eß#²–#lœ/IDAT2åPð||ð…™ýw]:J%Ò¥it#dÖe„aÅ3Ì@œZ¬…íƒ>=ÝJß;^›Â®.vn•‰×_Ézv ÇËŒÜ=®“€”*+ãbÛJ¸`S y}¾½ASrjðå:Ùænó°ÎŠAã.¬Ø#(tt²Ãw(èÒú•>qã+…ïûXkQÆO6bt%äH–ÛÖÓïä0<â«5ÒQ,žŒÛ²¶B‹6๒­›ºžšnÎ_J&€Ióöä¼ë5ÀÙ™(fž®d«\$°8Jfœ\K2ÕouŠ\ýY^Üiý,à¯'† Ž8ÛYfÒ/àjÁ°N¸ÎjD3îÁqIÓ)%ÖZêµQ“¦)Ž’$Ïò}ßGH‰NSüÜ!mõž`²t+Ö‹§Ý}…@°o:\|ðh£u•Û¤ÍJÅÕs¥Tìl)¸¢™Ú(ÄŽÌik,Öh«uæxµÀ- í3Ô¼l³ùë ÐÊcBÐÑQæ„P–£NÆ`3ä…Ê¡Cù<­FH\c¨ÕjDQ„”’°Ù$ŽãÓ¾9Ïó²Â1‡ˆBÐ)BH´ÉÊˈ¬€Uô$]%Eà VBî=Ξ‹&È=å%ãeÖÞåsVW’ò±•Xw*JÎ'OÅ®vïØü´X‹Õem[gXcä)¾µÀž$€1X!(tvÒåx©eLAMZÆLŠöÛ¥ÃåQÇ-ñ´(°X­cÂ¥$µZ¥¥¥5C ­¦’öÓ/¥$IJÅ"6j†”P ¥‚$(HøÁ®ñ …RJâ8FCgw?nâȧ’¹GŽG‡Éš@'×ߺ‘?Ͷ'æäÅÍçÐdÐÉ ¶ùÐÕ3Î[¯E[Õn××k€¤^'(—ÙóºŸeëëÞÂìƒ÷’~äƒèÊržxÌF»Ó(B¨lvO§©V ‚åe¶EMbO²('A£Vc§”üâ»™k^ÿfîþÔ?ñë¿þë45ªßu³°1MS’$a°¿Ÿ@784½´òè|z4éŽ ˜±kO~½-ÁÓJõ&œ!«Ý"m^M­Ñ6{£mKu³¼¿µÏ|ßâ4¯ÉGµ½R‰KÞþ.xÓ]˜4¡´ëB†ï|ªPÀ„M0EH¥pr_@)…PаZ% ›tYÁîTÐgõzJWþü/pËk~šoë[|à7“J¥²üB¡€1f|×ó,úè¨iŸšI¦­f*OèÌØ“Ž]k Uèi%yÖ7zœsp¢ütÏDi}(pçN"š½W»jûŸ½ÜÝ*¨ÇA©Ä¥w½—Kîz/Ñâ<ÿû㈾M Þñ“˜8bê3GR­àŠ(ßÇ$¹/ %ae…¤^ËJÂBÐiaër ·»ƒîúEn~ó;yä¡ûøô_‚j­¾jó]×%‚Ìô´Nšrñž=ôêÉ™¹8†*Øy 6{…“«^šm'~=ðb½ç œ¹ØØ-×9ÞLBmÇ=)pÅI0#€}Îvk I½‰ßÑÁ¥w½‡KîH*+ìûÄÇÿÔßâuvã”ÊŒ¼úM¤õ³_ùlÖq“êÕ© °R!jÖò !1ЬÕé-wpãÛ~‘Ë~ö=<ñÀ=|ö~G•Ùµs'ÇNÇw]¤ÌH$I‚Öš0 ébw_òøõ(J„RM´nwðZj?: è- Û¯goᜠjhËö‚ëÌÆZ÷yr›'Äk•¸R’5Á„±«iÞVÊ·=,èf¿£ƒËïz—¾õÄËK<ýeüsÿ„ô\0šúáýx›†|Ù+:¥zh6M®GÔ¨7뙦‘­ XMipˆ ßúN®xë»™zà›|ö÷~ƒhtœ‘rÍþ!–Ã$ ³‰ $!MSšÍ¿ÜÉ5îf 2G}nšEœút”N´-z:UF¯½ÃWçv?=×Nhˆ>OŠùؘOöJÜ( ·E‚Õ©žÖb†¶¯¾ ›tlÝÁ•ïü%ö¼æMDQ̾¿øÆ?û%q‹%¤r‰¨î§cûyôß|;I­Bmì Q­JGYŽÈ¬ÑD•ˆžm[yáûƒ¯z=Çïý÷üæ¢qè}ÂI‚AªÒeayjRkˆ’„rOW]t!ÛâñÔ©Ì'¶>¥ÇrÏžµƒŸIr»Ó×n Zä8çÆÃ |)E55¤Ûç©«!.+*ok/öÈÕ aFÆ Üüv½þ-X­iÔjT''¨ÆD!ÒqÑi‚ãû˜°Iuß“·í ÿÆ—PŸ:ÎÊ'³²¬R˜$%i&7uríûþ#»^û&Æ¿þþíC¿Ï\býØ(B%MD³NTê¢<rŒø›¿Yýsû÷ï'Š"n¹å^÷ºŸÂÃŽóÎÃÙF4=É¡Oý •‡ï§6>Š1§£t²{½#’$ÄÅý·ÞÉàåW3;v˜é¯}f&I—ªµ]‡°¶LÝ ¸îå¯â oÿ9øó(âÞƒ‡ ”C$mœŠ¬¾o×VùÒ¯ ?l^ï7ùÖÉŸ]RrvºatE8¼ø-?‡îîãã¿ÿ‡ÄQ˜÷d͘ãããŒóº×ÿ4oç;±iÊѯ™Ù{¿Æò“bâ·à#ÝsŠjJ«Â'1Qi˜ ~â§xå«^ÍÁÇøÇéIfŽ¡YthhC§Íæµ…(l¢uJ¹£L±PÈÚ¼ ø Ć mª9š§ÈôÙ ¬×ë a¤®¥ú¡‡Vô‡/†_½ýÍo<ËÕ×óGúc‡­¿%×\W\{-³žfæ[_cú¾#Z\À)x¨rG6xqšë*„È6z,-Ñ¬× T¶z®a i 4–Ùz“AéàÚ”Ç?ÿiÒZ Ò„ýßü:ž¯°Bâ$͆®G±­ ¨[»†gí@ç› ±Vg{Z¨o¿àÂ'Þùso;~Õ /:ÿË_ý_ýÊWž~P,òú7¾‰;o)µ}óÔ߆ÊèA·\ìj~;øÏðde…Çÿî,9L8?Ïä£!”DJÅBœâ.½æ{˜¯ÚŸõ„M„ããz‚Ôf%Ö5 &kŸ±öÕnh€SKëC‰€äÿþ­ß¼ìÊk®¹ÌUŠ öìáª+¯äáGY÷…ñ¶»îâü¾nžþÇ¿æà¿}5)•J¸2Ûß—C»Úm$Nóf5XE'Tþ6³O?މc„NQ®KÁQlí- ‰›&Ø8DI£†åzD©¥·ËeÞèÚÔr² ¬Ø¬Ã÷œèü¡†ë#À¾â¯xA±PxïyζmÛØ³g=|蘚žæö;îà—Þ÷ –fù·¿ø(£ß¾?6,”ÐÖb­Y e]‚Ýä6<b qž·­EçB"‰³M¢N¤I:F&1DaF)±Ja¤$J,Å’¢Ð¥8´Í.Öõ„Œ[˜`í½¾­âÏž‹ŸùÌgf.»ì²ÞÎÎΫƒ `Ë–-bÛöíö²+®¯¸ývŽÜó¯üëÇ?ÊâÔ4~Géºc2ÀòÇ´å[Ož=‰]›–ËÈ‘m 4Jaù Y Å:÷\Bqdf“4l®þ݉Î<Ø¡!ùX×™h·6[Mþµ÷$&à9pë­·Š‰‰ i­­}ñ‹_ݾ}{ÏÀÀÀ ŠÅ"Ûvì°#›Ä×þú/ùâ'>I”$Ö-„FXs²&94–(/.µ“a}võ{ÛÒY8bŒ¡ÞŒyÑ­¼ñ?ý·üÄ«I½€ñ'#ªV°Ê%5Ö ¸B¥~èXc²š£ds}GÉJÀ­ÁÎp# ÌäY¯ŒùÆ7¾Áe—]&Èv:ýÀ>ð¡/ùËŸšš¢T*ÉûöEÿW»’XpJ‘€M¬%1ù©¶'ó¬:?õMc %±Y²¡uÓKkB×´^Ó^˜YA*¯«—]_ÌuW]É¥×]ô}šÄÚÚž^Wtö¸ìŸ ¦V’)²>[í'_o€ÿ™¦§§ÅÖ­[E¥RQ@åž{î™*W+•¾þÂŽ}óɧö ä:e›ovmµŽXÄê®_Öò–&°m}ëÛo4cEÛ÷ëzÈB‰•j•G¿ñUö?ðmâ(´CEÑ·Éeÿ\sñácÍ àp8'Áìºì߆ÈsEckGöôô¸KKK>Р쮀Z§¯ú/,»¯("Ï3ù }VDkf×L·© •£È|…|koOÞ˜~òµµÄÆ ”»˜?z5 èìVì› <Ò8F6Ê} ÿzœ“m`­Ý>öÿ;Õ- ÃŽŽâ86¹½Ì"Ä\”š#5ì‰_uŽÆ ´i%uNv­>­Ëòž¶«ÀˆuÑBnŒc©Í/°45MÑ×lÙRB¥~t²1ÿØDóXîðµìþTîù/ŸâôoÈwAØ8Ž[˜¤yL]y}”Øãs‰|IàÉ!Ï‘ÖïY³kÈÐNГߧØ6Û/Ö˜m!ÕÙ× è24г)`!ÑáGëSãóñ²eÏíàϯsüþ]Wþ¾W°þwUž@jmÊôD¶ôËJÀ¶án÷ÖáÀyI§«.ö,~’ÄÒ~£»x– ”9Õ?*…‚¤Ü¡pIU›d|!Z>8Τšc¹ªŸ +÷ζßéŽ6òÿßZ¿ßzZc*\)ð¥Ø„àÂ½Þ }sM—'w•,YmIK’Z´ÎkÖ:ˆ«á‰¸ŽÀó…@Q,HŒ°,E:œ¬$Õ£‹ñ|-4Ó¹}?Ö¦î[ó~­Á:ϼõkƒß#ÚÿŒ8!!((!Ê©±=¹“xáæ^ï’ž@îêôÔ¶N_”\YrU¶ðÉÚ“ºVÿÒ¼å\(HŒ¥›x)Ôõ™JR™©$‹abgy‘·xÛ“1þr[ÈWoKýê ðp8ÕŸm_ ä’¢èHÊQj{>`X)±½¯¤¶=9Pòd_9P=W%<Û7°k«±ŽWšºY M£êz-2-p—…`QÀ‚±«½-Ð[À¯ŸîÝÿ‡@€ÓiÙæ#RPr•è°–r¬m'W­÷‘ u:ŠbîG(,"5uñ†ù)®u%EÍZ[3–jîÕ·yÖÚN|{·ï†Íÿ`}v±]´Å@IQP’¢¢”[L´- È·}üÐ8Rh‘Ýk#kW7„è,~jµZ­eee‘mmm‡j?íVõôô܈Հçø9 ˜D ôY­VÃf³…šÍf²³³ïwwwoõf¿ˆÄ¨˜Oïïï·µ´´„ÔÖ֎ܹs§Ûd2 Ç?Z ̬¬¬¼·~ýúÀ]? `:•™™¹pçŸÃÿét:[¶l騨¨H4›Íôôô<²ÛíO·:G?Z»vmÇž={­Vk;Ð\Ê^Û°aCŒ*ÞÃCøøkàW@Ô©S§îïÝ»7Ád2E?~ü¸§¯¯Ï¦ëº£···Û›ý" ü øù¾}ûÚ·mÛ–l2™¹Ýîv÷&*o•ˆh§OŸþ4""B>Fú[½"¢•——o ×€Doúw$&&ö™@* Œð¥`Dä}Ñrrr  ˜Ä ‰HºˆhIII0ûÙ€‹HŽˆh·nÝÚ1~üøÊÏ%ÀdàU`4Dú k¦ˆh………Û”œ…@âר‘ED«©©Ù­‚ð!€óŸèºþù¤I“4àýádx‚^[[ëÑ·€¾ÑŽ?¾9,,L¦‰Èùkgggñ¨Q£4`ìPÉ­®®Þ®ìþ»â PßþÎÎÎÄÅÅijüœpFÞ{ÀÌêêꮌŒŒà ðYÆLÞ:vìØ£††jc¾©ÀÜúúú‡Ó¦M‹®ÛŒù’î¼¼¼Ã0ª”¬`(x³¬¬¬Ën·ßìƒìNþ`2™dee%_{ƒ(–4 º¸¸x„ÕjmÎ ¯Pv^æ‰'ÚçÍ›— öUAÆD9ý+V¬üøf¾1À=zô0333¨4y"2˜PRRâ¾sçÎà´/;Ó ý0Ž=šàv»ëç ßþl†¾`Á‚ïõöö6¥Aêú…®ë®“'OF¨8ô Åú¼äé¼… &»\®SATÔ»@üÊ•+]½À±aø>r»Ýá‹-Šyüøq›Ú{Ò»º®» G® Çguûöí'õõõpõÛ,`üîÝ»í---.•}ú´{ÅŠcUÀ¯áüÛ@äêÕ«#€6 #ÈîKæšÍæžýû÷Ô¯ß~¼k×.«ÛíàŸÁfND^".^¼(@÷óxCÿ;o-R—/_ÞóàÁØ9 _0*'''Âf³=¾ Âù`j[[›íÌ™3ÁÈRòFª¢ŽÈÊÊz ¸ï)B5V_¶oß>V­yÝ÷3 òÈ‘#·Åy9ž¿È "ÚáÇw¨“zêpÕ&"û¯\¹R¤ø&¡3ÔA&Ož¼ÈݰTD´uëÖ(ûR'WD6߸qãÓ¨¨(Mü`õňH¾ÝnÿBéK{¸›Kï<Õyî¼1ªóÒΟ?o[¼xq¼Ú ‡)3€èìììuæ7áÿ[ÀÜŠŠŠ»×®]K6Ä0àe`90±¼¼ÜœŸŸŸ|®F²‡^RJKKŸô÷÷7yw>êLX³fåõµHD>ðtžÊøªÀm¬ˆ¤ŠH¦ˆì‘C%%%;¼É‘DÑ*++=/3qATíѺºº žûPðÌ·‘"%"£EäU™,"KDDÓuý@QQÑŽAéÙ›a¶ˆhÓ§O×€Œ`.\I‘…êõi›zx˜éíÛp £²²ò^VVÖ«ªó>óCq$°Ì0Œ¤q¡¡¡Q­­­¹¹¹áUUU¯'U'G?0 ùqãÆd]×Oßúéx<ðŽÚÉmmm³gÏNìêêº÷6A”ý1 ãõ,ørXXXŒÂ_÷7mÚ4¾®®NÔý×8ò;‹ÅzïÞ½8 =€ÄyìO¾ Œ»téRûüùó“ è.xM`AAAÑúõë?v¹\§Ÿ¡º®mllŒ=wîœóÀ´¶¶&:ί àk/ß_œ2eÊÈæææß(cýÙi†a¤wtt8óóó­¥¥¥‰‡£YáG›öǶ¶¶&˜ÍfšššÜUUU}W¯^îííWØ·hæû/ÓÒÒÂ-ËOkÍ÷¦asìv{èåË—›7o¶ÕÔÔ$ëº~AåÂëÃCˆêÂ$tŸQÀ'ꬾ \âåo¢A¡dô²st…Ñ,À  ÎÝûóÔãò·À=wÚU<¼Ù4BjG€è;Ê~¸œîø‹ÿ\uš ż“IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackIconBass1.png0000644000000000000000000000013215101070305020004 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackIconBass1.png0000644000175000001440000003243215101070305020000 0ustar00rncbcusers‰PNG  IHDR€€Ã>aË IDATxÚíw˜gyæïû•ÓÏÌ™^¤iFÝ*–\%7Œƒí`›bƘÞ{â@BHÙe!$®ìa³K !’Œ{·,K–%ËV/#¤éýôòÕwÿ˜3òÑñHp‘Ð|×õ]«ÌŒæ¾Ÿû©ïó æž³éUÿ¯^îO8÷œÀÏöüZdÐç~¶g6èBB¡ A©TÂ÷ýW„sÏ™¾¨K$dõ¯‡B!½l¸: U¼²üŠ9e?»e^²£££ü L]ÓL!D”_0*1CùRˆ ç~îgøºnÌX¯zÇ›Wâ~ïÓWýøßÿ§ÿüá¿~÷ËñgW¹ž'MC×u]7fQYñòRˆ0¼Æ®ëÂu]š›šÄhjL>À7þá½n¼iéÖÿÕâ…ë-Ûf|bbð”e;pÊÀêÓÀv\O)å—ÿþÌ«ªH¦æà ²z!„p]—®… E,ˆ•«/l‚­Í5º®G …ããcvïñc9 4-ù¢¾e;2¥f…*èUJ0k61§¯aJ'…žR Cì?tÔ[±hiýþð³L%ß¾cÅ‚ÿü§ÿóÿ)_ÈÄ”bI}Só;3ÎJ0×|ëóoÛ°vÅâ+&‹bà[w?{Ïã÷ýb_6_º®¾ë9eup+¾ž_‘!œ¤Ú¯ndÐÐÐ jãQ‘Éæ|@MLN* ôÍ¿û›?[¶¬ûÏ4üÖáÁ¡Égvì|pÏž½=;wï92ÿ¼Kº®_3ïö?¹qñï^½$ð¶î&m]W‚ /¬]ï'ŸÝ{ð¨ï+³Œ©8•äÏ)Àklõ‘HDLLL(€@´6võå·¸®[ÜöÌNÇñ¼H.—w‚†nh°(ÍïûM×øÒØ›.lÍ·ŒP;Ê•Ç0£¬l ,yÿ%uŸxè‰Æ‘£#ã=eе ù·Ë¿¦*àD½`N^ÝôN¦Žã¸êÆ®kÿÓÏ}æ[¿(”¿> îúÇïÿà¾l*¹ÑQšž¶E̳ …¿xïµ·¼ÿª®/œßluëºd<«#dÃÐQ¾®¤4Dm$ÜŠ' ÷=½ç !„¨Ü¯"Àœ¼šà×Äã¤3™~¸/ðæoøÔŠeËþ¨©v‚‘¡!cll$RHOÞù_?»}Ö¯ÿô›/úÚÇ6¼ñ Ý š¢AM'\ÏÃõü¿G÷s}¼é‚0¢d†2ÃqQÌ€”MMƒí•ßʬÀ›Ë^EË_wþšà‹p¼5XÓØˆ$.4ÝL§óCACP_zj×®}{€º+6l¸áë7wÝ~Ë"«yM—ÞIÝ¥à›lÙ;Á¡ÙŒƒ_´A å…k%éÍ• F1¥T¨ü±Y‘ ˆªXDÌ)À+ü,èìm|Ò_Ù룟úð;ß;>1ùc}ƒÉÁá‘¿øê׿ù·ë/X{×¢®Ž Œ¦êõ]—|ñ]—®zÛ†ö.oÒ"%ɤæZIFÃ&šSäŠæ—\æ‚f‰š!0džҙ»ŸéBe`=À*Wµª ᜠx5¬¿¾¾^|å_Ýþ‰;xß­7ݶbÉ¢ÿ•iª'9>Ñ·}h(ÔÛÓ3ÜÛÓ3µáMZñ®«ºþôCW·\²¬NÆq#`°e°Aýxã¼j~ž·\Òˆ‘s¸jˆP|š‰okê+ºß}øè³Oï:8DÊ2oTUEÕ«æðJWø4Û?q‡ð… æó¹´!¨©‹„}jóæ]@ⲫ¯}×nì¸õŠEr~¤ÆK ùÊÑŦ=ÇÄŽã.L˜05 JcÈ ©Í;ÇFr…¢ÛV‰:žšøÉÆýžÜöì2¨&§n ©Ó¦'sÏË«RJá+_ à¼u«k/Z¶¤kq÷’[3Ž\ôÀ£O~ÿc×.ZzÍ…ŸíŽ–Ú¥®QòÃJ¨¢„‚øŽÏ»ŸRtGM–¶x yzéË?Ý÷Ü~úä!pT¬¦ÆÌ¦§R@°¥ÀòY $+þ›-§“VEèDV0G€W0íBȆú:m|bÒöËbé7¾ògß]×a.¿¸3P1²:žÏS£íüpS¯kÏñöËêIަpÓE „Ìj4ÅB|ÿÑ];¾ú/wm«¨ð¹åßB””R9 S&DºL€Še”¿0ç^)R ϱà'–_Õýý?xý'¯è6ÖÖ5Fjð=HÙø^ˆ‡Gyîè+A&æÑ”…3˜¯QÁÚØ~èèèßÿø‘@êšt=Ï*gP,ƒ?óæË_ª°x¯ª0¼’”ß÷ij¨cS$~ñÅ+ÿxíãÃa]1Ô—%ÔA¤òXŸ¤u¥O‹–'5¤¹£‰1Q«jCRU–‡žß7P(æs€áù¾U¶üB๲ÔÏ|sÝÀW`~{‹¼/ÿùÇW/j7†C%?ôþà?Žò÷>Ž"›/Òmæ9¯Ö£¤ ¤¢!v‚L•\æE-öö Oýøñƒ€DG)Uª:]öóÉ éÏTÈþ fk«9¼BÏt[&¦2ò±Ã£9Ë8„bwÿ¤ÜÒŸ¦gÐfpÀ¢8Ud¸çn«“Mj>);B4dÐÑy4îÜÜۗˤSåà¢Òò+ýüÌ›©P«Jfus½€W&€ÈæóR)8zðùÑß¹ìò†®ýꘗ֘Hª®ºh4} áÓ›1Ø“:WµK–.”„”ÏOûÚ¿=´OyîL—/K}¦‚é ùŸ‘þJð+€¹BЫ¢êD 8±œÈnXÔî­lkÐ?z±£&¦R¢T(1XpXp³1IC­ÆÂ50ضkÈú›ü oS3Á^…ôg*H-ÿ^±*Õ«îÀ, ¡9ðÊX?HM4FBÈSÛ>²(¯Ý¼u"Ÿ,\Ò!×,ê¤6&jhÌoйdM-Ý«ê!¤8žãkÿõüþÃ}ƒýeK.UY¦Jî+ƒ>§Âò½Ó?ç^ü_ !ÈótGGÆŒšößïM¹¦£í]ùTäаZÐFs„Sg<õŽd,³äÕGÇ‹|íέû~xÏc;‚X³HþL°7cý¥ ë÷ª|þ)ÁŸ«¾¬i¿@©éÂÚ†5«ŒÍ;w[jøäÇnÿòùQýã+Z¢DZÔž=GDc0Å•ë;Ù3褿ù“Í»yæ¹1׈š«—,LØ®ë=µí¹~ +ŽR'$?UñWàTéž:]xŽ/såÏÅëW.lßéuyßû¿´¾)zÇ¢ˆ†áZJšQr­âxïAòéÑñ;wyxÓö=3]¼™:¾8l„(TUøf¢ýlE  Òòý—bõsAàË ¾RÄ£1ͤ)ƒ¿í­·üÉEµÁ;–Eè¾KÑV"›™Â±]R¾1þµÿÚú³±É‘> ¦ ”-5Í÷§«|–‚"JUy29~áÀÿR‹Ÿ#À˨ùJ)„¢¡®^ŒOŒûiPP›øðûÞúg—4FïX5ð‹|ÑR%¥ / •Íù<ýü#c“#€ÚrŸx3à—kûùªj_uªwª4OÍàUÀ¿œê‰¶¶v1880m±µuw|àõ_[a–>²$ÀÔtÒSyeûRøÒŽï=~lèÁžÝ¹‡é‰Êà»Ñ~Q)U¨ˆî+ßÊtÏþMÁŸ‹~Mðgjý«W­–»vïòß𞎛—uþ·n=ýþ…!]Y.¸ZHXE›±L&×îýwÝóÔÓϺ˜¾B•A´ÊÀV¯ˆðKUžÙ‚¾_ü9ø5eß÷}–,Yvü ß÷é%7/^ð·í…±›……ôt•/–„ƒ†4Éã;wíÚ÷³{6oÝ „t)u×÷g¬Þ*ƒž¯øb•ÔÛ§Éñ-àçð+>RJ1s6Ùò•šføÀG?³auw×·Uö¦@ £¤«lÞ–¾&™)¦Õ}î»góÖ-€¡IiV€_YÝ«nåÎX¼[•ßWOùÎàÕ0þðÏ_³Nó ±»·×¹õƒŸ¸leGëwÛ²C+e "a•Ëe„í¢Ð=™Óñ<ØÿÀïyðñiÙaoúÙVŸ­ð*‡7ª ;þË ü\ ðkäùóˆ¾þc> n¼å=.înÿn·^\Õ®Y LU°K×uA×§¯^ûwë"\Ý‘¸J¨ìØ„PDj£øÑ OŸÚó¿xø>pKbz§e@KUÑþ©ÀŸ­…ûŠ?çNaù®ëˆ‹/X'Ÿyv‡ ðÆ÷|ú¢ šB_ï6KWu$ÊAåòiá ³`áužNîúŸ÷ùÙ+/n {‘›º´#®c‡#~v*-_¢é>Ùù÷w=ô UHg ]‹x _ ~¶ÂïçxñЦ{ÿªü£Ïyðu]Çu]qÕ† Ú›7û€÷¹?ü­]Aãoë²#ím!ŒˆÊx®°a%Ó®°2SÞc£ã[¿÷ÐSQÌeD•ªlùENîäU¦{ENžÛ÷yáØö«J‚sÞÌTø\×—®Y-ŸØ¼Ùô¥/}¬+üËÄø`c½ÇX°HKØ#ã´ˆÈ`Û?y~ï?ÛöÜÆ2XQŠéh6ð+}¾õ|þ«òœë.`º¥+¥¸ú²õrÓ¶í. âŽßÿĪhäo›r“ñš¨À2*•- ÛÐ ¸P›tþkÇîÿßÖO”?Ï̩ܙT/; øù3 üsÝ”gø¤XÐÚ$z‡¦S½[ÞyÛÇ_×Ùö·‹Ü\8®ykêÔ$žp2yâ%ÛsøÉŽ]ݹùÙ§DD@(úÄÎL‘'ýKÀ¯”þ× üs6 œ9¹#…ím'À¿ñö÷Þ~ùÂίvyùp­VÄ ÊNå„áJjjêp…ƽ»=uçæg7– VÓ_©ªÂ÷ËÀ÷ÎðÏf Nñ¾dÙÄ‚Žùâhÿ€ð;·ÜzóµÍuß\¡—š¢5 2 ¼BI(]ð} Ù¼ùû=ñPÓK/ôò«göÏxðÏ6Ìôl8!Dyl[l8Ü}ðpÃ;ßuóµóšÿ~™(Ì‹Ë<©@H9FL)V‰|&Ç/žÝµé_Ýô`K!BUJð«£ýÙÀWgøg“ §|6’œR)|¥¸hñb¹ùù.Àµo~Û-W/hûÖ Óš2\?ßÒ¡‚J Ó-*Ï’+xÜ·ûÀ–ݲýÀ FX½Pá›Ü8]ªgŸ©àŸÑi B€8qÐÒÐ5ÙØ:OC!Q*ä¤eYxŽ£\×QÅbIyž7[õL•?׉ãZ×_ýzýþÇuó=ï~ß/ND¾²«.Vb2¯„YðÒTjdX•Éý‡û7ÿßÍÛlMÊ3ý…ªëúÕàδhÿlËN|_uµµ2VS«kjÔÁ];ª?g”ÿ¬Ô4MxžWia'µPõõâ¼…ÝrÓög\@~ú÷?ÿ©Uñ×Ý^27|&µˆ²ÍPVN¹“Q*9<~äØ3ÿôäÖû€¢"\ÞÆmW”t+?U‘çŒÿL%À ¹F"º°s陵(‹ºº[ìRºšxa||jt4•ÔѰ“IgŠàŸÔJ•R2¯¹Yô ;Ìë¬ùÜ­oÿä² üR—î†CÊ"YÈ(eùB6´©’òDqp”÷öný—Í[([sX€R/Ìïef¿ÀìDzÏXðÏDˆeYÏ(¦<Ú~Éú 7,]µêm¡`¨ch°OéíMÅC¡}¡€y`çÎ]÷NM%ÊA­WñÃWÁ`PÙ¥’ò§-2ðÅ?ýóÿÖ%J_è*Žª0*Â8É,a¥ŠE‘õ46öíüö]÷ÜU¶è/|3þ>]E€B•ì;gøgZ ÁVÉ¢üäªË/K¢¥í“±úú×ùB£c#L&“ MNA}ý¥ uuÿ’Ïdïâ@IÀÕ4Í/•JàÔ_|uÝgnºîOš¼Üç[³ãÔFƒ¤|ð0ê[±ÆF…UÌóTßøÞo?ü̽àWNòÌæóϸòîÙH€ðE|ÿºk¯™W›hý¬†>d+¯nÿƒô:€oUƳñ5Sè-í¤ÒÙËuBÖ¢üR|[Ó4éyÞôxUÇÒæ;n¼ö«-nîƒ*ƒQ[Ø' †#˜ù¢ò Ia°e8wàÛ>}7ž®¿º±S=ÆuVùü3‘ ¦¦F–—*»ƒö¼ã}«&`Þ<–L±ë¹g•ž™¤%Ùp@Le jÑÂÕÄãC=»¶R Í÷¦$ !„çy (5­»¸õSo}ëg'?Øž¡±®VøaQôKع<~*-¤*òüDîà÷ænϱSLsÌ6Ã7[…ï¬ÿL¨€–æá 4”r†¥«×~=Ý|èÈa¶<ñˆ—(æÄå ;Dc}-£©IÕØºX´w,cdhèÉѱáa!e“ïyQ¦«sA)5³l¹-ï»êò/'¬Ì‡È<Õû»»·˜K¦+&y*+|™_RäqÏÔ<ÿŒW€X,*FFG`ßrà ±‘\þ¯´`è¶ÑÑ zöïñºjÂÚEmí]‹==Øf-+Ö^†ŸM>ºoû. ÊtŽŸB¸BHÏóÜ4PÿéôsñbîCöMíÞPÁ×`)L¥G„fl™ÈúÎýOÝ—ÎeSš”úІõ+ÔöÏZð_k–eÏl·6rŽÿ±h¬öÃɉ$={v©%ñ°vU[ xf¨Ÿ)eª /¿^tµÖ[¥±ÞÍd„J©°"lèzØŸNZ?pÛ­2?ýäÊ€G[, &Ç&4egÍm*Ÿ³E!5É–Þc‡ÿî¾G‘ÎeÇ ¯”W‘êÍDû•ïŒõÏ6ÊuÖÿš)ÀÌ»`0$J¥ât3æš«ßX´¬Ï§&&é9¼Ÿ–X@\ÖØJ½ ²+9ÊñL¥«Ö‹møSƒ½GŽôŒµ€'®Rªd;޳xÁü¶k®¸â]­‰º·/‰ê´Gv!i4('SÁH”=ùÂÀ?>¾å~;—™šñùê…aŽl•ìçNQá;#ZºgNœ«/•ŠàïxGëØÄø™Lª¹§÷A]qQk;Íf«@ßÄñšVë›ë?|üȾÛǧÒ^¸‚R ·¬s~Û»nyÛû†våÒ¨¤3fÒ72…0LDM ɉ”ˆkŽë‰ÿ³å¹’SS£@¬"Ú/ÌíWFü¿Uà¿–1€šž;]À¾ehdôª‘É <¡8¿qˤ‰T%K92®NCKJ¨ÂÖ­›ž›"¬ry6ÉÚÕo½úêÛM«pÁ~¸§¯\áCíg8K†9ÎÆ |EªŽçz àó׫Æ'&>p´ï8Y+Ï¢ÆzÖ„â4{à{’LÁÃGÔFqré±l2•š¦jÚ÷æ··µÞ~ÓM® ™´”&X¤9“9 ž ÍÂÊ¡ÇöF¿óÄö{w8Ô„„²ü'÷ó3³XþYSá;“ƒ@Ắ|¥”xzûö÷§Òë\Ϧ-U+bQÚ$(4,áã› ‹äÐP²hÙ {Ó]¹ü…«WÎÿÀÛn~Oq|lE­•¢{éB•ÊpF‡MLQN¼m¹ÿþÌÞ'÷õôLM“3 ™^ʧýÛþ«íªûô|âã_wðà¡›&S“„Ã&‹c5b‘0hô| M K…Ð( %vÈ`™´P\ÜÞÜvûõo¸ÎÔäÂ`²‰•-ú"gFñq±G‡žÏæSïëÙ¶÷Àþã@H€ð<ß®òù•ÒÖôóÏ嵩"‹IÀUJû?466¾ÄW6õ5qºƒ5,V&íž Ö„4ƒ€¯C>ÅÔh?>Ì_sþÚnŽD–Ìo_ðñ·¼éF™M/ fÆXº|±Êgs¢Ø7€ªi$ŒU.v©dÿäéÚò̳Âw„BS'¯^ýeàWoÛþ­^¥‘0)¥PJ‰€iR(5À迱çHïö‡dÈðÎoj“´(+4i>ºò˜ÌY”B’KnØ@}CGšÁxmû¥ç-j{Ç¥,­‰„üÝõµÊ6B"ç ©“›JªšpH$€óŸÏíÛúØög÷ªœ~Væù3 «çø*·søgsžÿš+€Bø¾/ÚÚÛ Àþë/¹­·÷è $lé0¯¶NžgÔ°ÐÔéñ¸OfJOqÓµ«ùî7¯ñý(Ýej O»î¼¥MQ)ã¥ä]]há°Ð”‡USG*Q58¢X(ðãûwܵñ©m€’Bþ‹óüô,~¿ºŸÿ[ þ+­'Íî-_¶Lï9|Ø|Ó~ªçXï'‡ÒcÄb!uQM‹¸BEX.=šë}5L%a^=æ„»¬ Žö¤ØÐÞMw{#Y+G[$ŽîùXšGÚW¤ú‡UCÀùH„Ÿ?¿wïÏy|+` ÐÕÉýüÌ,é^åªõßZŸÿjàEƒ™‘HDpßqË-oî9|ø¿Š©þòš&¹^«åRáÓ°©mð(.゙¦«.€±^ÒûŽ1?Ü.ƒô;ÊüŽyD ƒÒÄ$%3D©äRç¹"mÙühס?{tãæ²µëe@+Ó¼4'Ö,TÉþ9þ+‘ˆJé×u])E>Ÿw®½æš7ŒŽO|c*›o³]—Ö†„XnÆYä{´E\jCÉPD{ãzš~w=dÆÈìdpw‘þ$ã¾¢µ£ƒa œÆJôÄÃLEcüǦm{ؼu{P7vªe¿:à;gÀ¹àEsù†aHÏó¤ïû¾çy¾úÂÄÁùòÉ\î¯F&SKGÓ"a“5±:q‰4Ykú,ªÕ‰DJ*uÁZoz+šn‘ݵŸ#»“ì¶Ø35AKØd~MO—m(ò4G r>ühçþcw?ñÔ¶2Ø'o㪼R­Úò+S=ï\ÿ7!À¬‡1Âáˆæ8þtKÎ/KLû}Þñ;´`è«cé̼ÞABº§ÖÖ4ˆ Z-k°4êQó)…lН%vÃ-˜†Ïäö]ìõ8x<ËÑ‘Qš´ÓnxS)J¡z&S)VŽ|8Ìì<ÐÿÓ‡6> *[þ~¬ST÷ÒÌÞÓ¯Ÿsüêâ̯\Ì1 @8ŽU×’ o{ÛÍ7–œJe7$3­` –Z“¨É—*ƒ5Á"uÑqTgïªË‰^yf@0±s7ûwe8•£˜Í’¨¯%kùܹuGßÝOoÛ‰ïO ðÔÉ+ͤz•mݳïÜ}MÎèŸ1ÀL ·Ô‘Ïç…ã8•‹Š½ôâ‹–64µ\m¹î_3¯.¬š¾ÑqFG†¼æ !×&šÄùZœµžbS$´b^í-W]s9šðÁ*З °g E²¤¨‰†™5¦N2Y‘ˆÂ²m~öü‘¡»7oÝ¥ÒaÑòl³û¹Ó”wýs ü—L€™% õõ¢®¾^;tè(ÿ9oé²xScÓ*Ã0®”!ãR¥ë+D ¸HY%z{{ÇurjumD[[ÓÄdE©@—ÈoPø—,Ǽî B‹–@¾„g±ÌZ5¸"ˆ5ÒQ+ Œ2nw0šËÒL+œàß7í¹gÓ–ýLOù~ÅzBˆˆŒR~F‘Bä}ß/ü’€ïœÿ¥¸€Nè$b*™ÔÊVÃ~þóM‡½r"9ym$½ ©·8žK®X •Jbe’l‡† ÉÂp˜åZ”óJ’N¿Hc¬Hdyæu—RZÕ¡›Ä<‰ï{¤­†ïM„è90ÈŽÇžEÓ‚„ƒ ’c)bu!¥LSüèé½…=¶q7NiTá–oÖH))eÚ÷ý1`R“ä<Ÿ,Ó“ÂEÏó œ¼’­zçª~O´¶´ˆá‘oËc>õGtM±Tü ÒÄõ†ˆèÁ0–ã`M¥ðòjLÎP„ùƒÌ/ú4Ù.óâ:u«ê ^ÜEà‚•èó(¥sˆlÝPpMpJM…¨Eý;÷ðä¦>&lƒFÍFxðó¾)Ý¿w|¬÷È®²µ[J©Œ"©”šFB-[±zq{[kM¡Ý¸yóS;·|Ãl›¹^Ö¼g;@,áHXŒŽŒzï½í¶e{údßðЭ¶ã4EƒajjjѤB/©:MámºA§oЩ\ÚŠS‚ØÚn^wÁuÝh‰šé{«36ø6¾.Éç%†SDùX2ŠtBá„kè}n?[ÛA ®‘¾\†/}ïçä7W{''­óÕIDAT§ú+|ü˜!E±©¹¥¡¥{å…MMm—O÷†²™ñ™tö/wò¥˕“=§J…a¢X,’ÏåýŸþôGa]7Þùä–-_¹E×e¤­.AW¼YµJC,ñÖ¬ „X¥«}Å*©èV9jçÔ¾}=-ï~=æÚ%È@²^.‡ôl\%ÉZ&º["p±E ÝóÐuŸ’ ¢y’ºÖ8óæµ’HD9Ãb²ÅÛžÙeÖ4·$–.[lØÅ¢e•J4µ´×¯^{á…umóßèjúªîÎf£”Ÿ¢çèÑVËñ‡íBqwÙâõY¾sÆê„iB×uẮà»|úù={ÿ¡x¨3°¢½Y-Ôˆ.L±ÔóY©ÝBÒb™)鮕4†óèÝ!Â﹆øµ—  vïI‘îé£^‘¦‡"mÑ|›@ÀÁ4ßÇЦ?– ¤_Ä÷M"Mµ$L‹Ñþq._†2Ù¾s¯™É—šššçµœ·æ‚… –ž·ÒÚ2Ëʇ/^šà3·_¥jêÄsÇЄ6løÖóEËv˜>U<|µô«_3Eþ­ €0MSK%ð÷½÷Ò­ÛŸÿ‡ÃGjš5¬kkTK¥!;+l—¥Âaž ­!—¦:A}‹I4–Çoó0nº‚ðE€cã>›ÿuç“<ºi+'MwS?PgÙ„ WF¾‡!ECbhžÔñ…@×s õäÝŸÿ¹’Î7¾üaZZU>c‹ºÚx0ÞP©éަoºz¹úì{®óVÌÛŸïãP‘D8p`¸ïð.ËõýŠ÷©vðŸ3˳^”ƒAQ,•Ôç>þ¡ù÷?õôŸiiIÔ±¾¥že¾/º,ÁÍ¥-* GL¡"¡ˆ…5 G¹øçŸGtù8™0.𓇟cãÆ-ä´ mËšˆîOs^«¤q^y?ˆ°¦á`Ë( M³ñ¤"H@³!9ÅOŸÙ¡aþú‡â)‡w]·êÓßm±)Ôd•±§¯fF8>ÄW¿÷ÛvöÒƒÇ<¿ÍñÑ…5J©Ó·tÎ/«ÓÞs)¬úÇ‹‘ñS›·mžkmª‹²,Uó-ŸöOs»Gh¾_«!ÐJ(ÐMd(„Ð1p±‡û‡ùé}“tJ|ú3ã úYD(ÀszðqX±þu5;ylÇ0ödš°aãiÐÐt¦‚\–o:α‚Îï¿çõ¬[·Š>ÊvQ¾ƒ/$"EÔE!¬óôö|îoþ“‡6¤»9RVrËàèä8ˆhð’Y†TÏŧÊL_~ÜoOLNN^&4‡ŽXßQ*ÍkðÑJ¡„º0%.Ryø™q˜êci«dêNº/¿ž?þÔ‡p—žc—d2…†Î¢+obðènöîßÇšå­èM Pž˜¾a%eÛ³ûøÇÿxœ«/ZÇÇn}H_Ì#u5Q„Çãpï1~þðN~üÈ>G2ª+¡çÒ£½ÏíØ¹{ AQMk¿:Eà7[èÜl­X»bÝá¾Ãç+U¢QÄý×”õÁñ„ƒ â1½ _J‰0µið¥ƒò\„D8Øñ˯矺\AÞ1˜<Þ‡]rð<R'_´ÑƒµÄ—­gÿ½Ç8?8Ž–h™î鹑ÆZ®¼x)¿³~ õµà;Ⱥ肱±IöÜÏÆ½<±ç(}ƒ9œb1¯§GŽ$ ý}ÃÃC®çÏ ƒÎ|*«€³]º|Î)À‹ä/kç;•NMÔ7I`Ȱp…Š„B:BjøÂE“ a„®¦Ž>%QºD(ð&'±Ìc´Î‹‘ÙÆñ‚E^Æq„ƒã)Li`D5òŽÃÔd%Ë/'f¬àèÈV:CyŒP ¤J¬XÐÎWÿü£è\šëîåçogãöcéO“,)’™TQe'†ë‚Œ¥Ò“ƒÇ§ÒÉ€•R3W®ÎLÿXÌ~QÃ9WÒ+|à‰(¸od¨¨Ü’ÕDãÊR!š®ƒ "„‹M„… ª¼‹Oמ R§”˜Gd'رŒHÁc$° 'R‹!³è`˜¤]M~ç¢åô÷÷óìþCD›Ã´DÊK`êîô1Ðx€§¶îáëß{„]‡&1M“Bz$•ϦFKùôX!—Ϻ…g¬»òFÎêkZJ§ À9Õ Òg ãá°ž/¸Jè„Òñ½é lRÎüÈèBÓ–ïz(¥S 7á+—=2€—l&fôÐd™Ð×c–'YÒÚ̆ŒŽ°éÙÝ$êæ Kð&ÒÇ÷A:64‡xrû¾ø?ŸäбßËúÙþ‘ôèñáL¶˜ì@0h;¶]²3™Bø§Zæxª¶0ç ôÙ"ᶦÖ@ßkØž‹-‚¸JàÛ¾íá¹å‚ÀGøÍL ¾§tJ‘V”ëÌG†Âˆ@öè5YjÄwösHœG!ÖÂâ:“ :9Ö7Ƚo¦>á’…jµ)ð@)Ô‡ìäïï|Ž‘œF½‘í?ø|o¶PLvMm­ˆE£ÚØØXÞ÷ýL•åÏÌVÞÖUàÅ7v¨sÙœ¤‰xbhlj,iKÑ´ôTÑ Q(¸äs.‘’ŽÔA9itÍ€hJ)¥‘¯‡ð‘Ô2D™-ÈbŽ ž×ÀîeA³ƒïùXõqÎëlçð±~yz'¡€ÎÅ­MÆ8>¾0Aù! —ç÷ìæÐq‹ 5™>ºkO®äO•,•ŠEÛ÷<ËqœI^XÝ–¯’ÿJE˜í‚Fÿ\³~ª*`¢ìd{K{Ñþ†\1ßÒP­Ò”1Ç'¬AHºhÅF(Œj_Åö± {ˆÖahŒä"@…ê…’ª¶áù0Ö‹’xœÎFËÀ•€œçyiÛ¶§xaöoæ¸WåpõM]ÕçþàÌf#€~ôxoaÑ’eݹ\é Ïqe"(¨‘He!—ÁY±†©ËÞÎÏûÿ´m”ðÚ70/"¿çiBÑ8fm3ª˜ÅðSø±ž@/¥Ð‚v±ˆnÐÃA ù­QÉšÖ1m |¡q„#}¤ûõM¥’¼°Ò%S!ûiN} »W>s–2˜…BJ©+¥´d*9º|ù’„¦‰ {ô062¡æuÌ‘š8…¼D¡¤¡£\Ƕ ‹ðêPé#Q!2PR"ñJB*Ü¢‹e& ÞŠáYÝMÏBÔèlÚÓÏÝ[G™+ ÞsÐõ¼d…õW/pÌñâ%ŽÞiª|ç4ð³ ’R)%ËA¢?¯­mB*ÑT(Ù݃£#bt`H5·Ï5ÍMØ…"øO“øèØEXm”ÆkÈ”,⩃Ôéé|¾Œ‡¯ÀUX¶À­[€ “ÉNâ)0‚´HB®À¿Þˆ]ÇóxÉÁ¡ÑácU¼ëOròY¿Ê™ÿ4)Þø§Q€„Bj * Ńu uMB¨î¾ã}bld\µwuŠºæV|ÇÇW` ‰ ÑQ!3™ÄÎ'Y’ÚMØÊáµ(e£¤˜±°ÆÒÕØÁ» ó9”®'®{Ä[#<»³>|„l6ãŒÙ½¿X,Mrúí3ÙAõf9« €ÙnÛ!„L˶r†®mniJ€Zxìð9><ʼ… i[¸ÏñÑŒ íX£C M2ò0óŽmŽÔЄDª n±@ÉÊC×:bËVSRõìU+Éh–ÆÒDµ ?xà OÎaOö ?Ö[aá™Y¤¿úÈ×9s¾ÿ• À‹â!„Ô4-˜Í妺»»íöyíç ¹pÏþýŒõк “æhB'?4BoϚ͂C1Fc‡ t¡Z'ïP*ñ:W\÷zô@ÓñQ ]daƒËÓûûùÞÃÇÉçoªoO>Ÿ“R+)¥2§ @å1oÿ\-ëþ¦˜MNô¤”R)%ãÑx´®¾.æ8ŽëØVíØÐ€Þô(ÑpÉÀdš†E ©íÙIäñûѤŽˆc:’|:MΕо”ÀòÕãµH×G‹¸?FsØ%oûüõϰ½'‰7~d`rx 7‰Ùžçæ|ßOÍ"ÿsWˆ'¹ƒò$­>>1nŽŒö ¥áH8h†‚K&G†9~à mœÕ%˜ãÇ™úçŸPê™OÇŸ²ÈÙVK#á ®@ïXÀD!C:S"‰"¥¾‡4 þmÓa~ðЬÉÑìØ±ýO¥4]+Ù¶]Yñ«ÎùO×Ö{~Tg'£¼ìÑ·mÛ* Y˲ú¢‘hÀ˜K&ò&’iJ}}ôÞõ &‡†(¡ÆñÎf‚—.¦öuWP×ÙIÎrØg/à°7(yê$2âgOíWß½û°È¦‹n²ïÀþ|±8„–3 þl©ßl¾ßçܾý7îTúÍ™¦[V!„RJÝ÷}Ó²¬¡±‘á6´4ŽFo8´w/G¶í¥9Tyëñ¦W‘¶5R2B[W -ùI4#€U³ÃÖh Â.<¹Ÿ¯þp—˜JëÄT~äX>=XjúLå‡Óíð=g»z/§œJ^`Ç̪! Ï÷Kžë ÔÇãš©ËnKJYð]qõåËøÝwüSy“½EV6Å h%á°A[°Àêð$/Í=ÈW~´‰)Ÿ€3>1püÀþ‚å¤fIûfòþJ"œó=ýW‚œFf|¬/¥T@@/–ŠÅB6×cèb 6›„Òê$;‘¡>$TG"Jk£@Ú=€/u¥Ùìܹ›ÿqçsâ?6ÀTʶÌÒhïèÀÁ}™¼5¦ëº¥”ª”ýÔ,(1ûRÇ9ð_âóR7„^8E£OWr!9}ÑRŒò]Mõ+k­oÏÏ/æRá•ójX¾¬¶¦ójkÑô©"GŽñ|Ï0ÃY‡H(0åe'zÆÇÇú¦A¶¦É¼çy•ÀO“¼Ðò­Žþ½¹¼ÿå'ÀIõ*ÚÅ$2=³eú²Å`,n©kl^70’Yî3ó‰Zô@8jzC#_Êù «¶¾¡(½Lf|¤ßU¤Ê`V_ÑZiùS§©ùÏYÿËr _:ãcÝŠß« }À·lw"=5¹Õ/fv­¦otÊmw<+æ¡€†—ÕU!KÑ+¦³Ù´§NXpåbÇJù¯Ìù«¯l›Kû^aÌ–Ì`¶cUn<×c±XÐWÊswÀ.•&ìB!,!š.䂞ç @¤R陯?~©ŠÕ7U×û«»|sDx\§*—ÝQ~+ã‚°"jšf4Ô”J¥ ¶mÛð}ߨø;¢ âÌ‘­™Q¯B…ÔWŽxUvûÜ9éu PMxaxD«Š fbƒ0–RF ȸ®kzžgpò™ü×1C€™#\… ÉŸ™ð)ñâéž9ë p*È €)„”ÏâËï 1ªh«2˜N…˜y-Nžä­žæ‹ú_ÌV,­W¼Fô1Ûb†™À­Ù®x+§y½9ð_» ðtÙÁ ˆ¢"Pô*,Û¨ „Æ‹4¨*¸U {œc{üÏ8K¨.­Œ4fßÇãW ðs~Ÿï™N€ÙHP]8ªþXÌ¢>'oïð«~9ðÏ\ÌT§Œâà3‹e+f?¸1üN˜}ªHœæ÷ø%@ÏYýYF€S}î—úµæNíü–à7ýsÀÿ–à¥~Í9À_ƒçÿ?­ƒÃâ vjIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemHome.png0000644000000000000000000000013215101070305016744 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemHome.png0000644000175000001440000000131615101070305016735 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®ÎéˆIDAT8Ë¥’ÛKTQ‡¿}Îg&±‹Hx¥‚”#2K A£ˆ *+¢‚ Â§¨—^úz ¢ § |*ˆ¢ t!",¤'³;–XX©3LãLÎŒ³÷9gõ0£&ÕS öÞ‹½Xkk¯ü§)€Á‘go½•줋ˆà‹ "„'v5ª¥UóÿYÀùô=É™ë/e}¤†²yáYÁØx†SWúd`8¡êjüàÐéGÒ±z1¶mó-žœS¤èˆâz €¥ÛÖ-RNr"C(`ñl 6+Y—T:Gg[-¥%!A„²ڣûÞq”²¸ÑûÏó™y@6Dª‰Ob?5~!qê,²m&'s8(8¼5B8èä´°õôæÂÍçÌ ho®¥¼l."3[0Úàèœa8ž%sóèÚ¥·ˆáÑ{:êùMq§ç=+ê*ˆ,«¥ðE(:hãâcœñ‰§yúl€cqò` Û×-àÒýwtß}Í·h’µ+—¡,òFlKñ~hŒWo‡iXº}›h”Oè‘-õT/œÇÅÛ/xÐûŽ•õ5T•VcŒÁÑÚE)ˆF“t¶/£¡¶‚öH9mǯ‘Éêü8ÃE<9·˱yÜ7Ä‹c47Ta´‹5ÕB[sû>³eU™¬æøVŽío.´©±œþ#llYΤöЦÐBÎõ‰%s$R³…ôæKòåýHeùžÈR©Â…)—LÎE»>%ÅAšº.PRÄõdÚÿý˜!Ï#–˜ lnˆ®Î58¶×†ÆÒÂáMØ–Bm|G'ˆ§_pºv6©óW{Äu=(È5¯H™ÆþÝŸ²€csto«ú¿øMÁWxyIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackIconBass2.png0000644000000000000000000000013215101070305020005 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackIconBass2.png0000644000175000001440000003505515101070305020005 0ustar00rncbcusers‰PNG  IHDR€€Ã>aË IDATxÚì½w´åÙUç÷Ùçœ_¸ù¾TïU®ê®Žê¤VhI À%²H¶ñ kÀ°Æc3ã›åÏ0ØÌ2̰ÀöX„Á ©¡‘Z$µÔ9Vwå—ó»ùþÒ9þãüî«B¬ÕÝïôêõ:Tzwï³Ãw÷÷ÀÁ98çàœï‘0 äàcx üe¯xåo{÷·Þ œª@EáE|Ú?ð¶×þô/þèÛ{ì=ÿ¨|öÿq›Ëšÿ·?ü®Œè›€ºˆŸïúàs{áŸC§Nú†¦?0+É÷w»ó[Û×ÙÙp­áP½âôÂ7ÞxÃùã?{üaÀyùuðñ½ðÏ 5~P¥ã×dYÁÞ8ãÉ箨 ç–•i6å;ÇÞæúÜ>ݪMæZ»D€C¨Ç?3$7dΆ! 3uF£Ÿ_ãÑË›|öÏsëÃôÏò,ß)¬ë)à"À‹äô’ìD7Íé 2zㄵÝç×÷hV–×w±J+k]Õ:âk.½8À‹¤êOÒììnoȨ(XÙs~eÇw¹ÿßåí÷g»—®ØÂ&…ué'›ƒÏïú>¿ó¯~H}çÿð+PÿËOýÝÃÙ^çÈÊúÎ-[;w¬®mßÔO3Õ±z&eôG)Ã$¥7Ψ„ç6»nô'J˜'€žµv eøwô…/ˆ3ýÿå·¾üp+þÉ}ê™{7w{$ÍÂÞ(eì Þjqym“~D54LEšJ`ÐÚ`”"Ž${h©ó/±Åg§%`ýqtJÔU¡?ÊYÝR+Ÿ{Úý Ø'€'`p-p®£Sª÷Õ§rVjÿ°32Áï?³X¤Y®£JÌh”ÐeÜpâ箈û]v’Fãe¨æô²e[Ü}¬Æs+=Ò¢6zä¹ô½®«ÀLBÿ\‡çåss?°¸©~zCç|öâ £4ÕõzQ–c´â»^}†ûN̲ÕßäÓ›çiÖ3bíkHë;ýâvÉäP÷#nüF‘O—J˜„w®Ãó¦7Þ÷εõÍߎÛyž£œ£Ñ¬³¹×ç¶…6ï¾}-Âãëϱ>L±ã·žŠxti@³âØÞ‹QºÁL«xèwØù(.‰pÑ9.kÀ°ìþÂ9Àþ–Î;ßùMPkÔZ¯¸÷ö_Y\Zyÿp8l»­Vç„ñxÌînþpL+Ö|ÛÝ'Š^ýÝÏÿ&yþœÀ’ƒ \Ïç›Æ‡—È,൯¸MÞòòSêûî»Qb¤Z~¡p«~ñ‡Þ¬ïºå„šjÖå]o¸SýÃï|ƒ2ýÌÒ¦½ãŽ›¿Náî%߆!£q‚µ–J¡”bwo~oD¿?äØTÅ}ÛÝ'd«PgÿSgkωÈù²Ç_Çóò¾(¨ö |u½6ÛS¯>3j® …¢êqg4î|ðÁsçÈÒÍòv: ˜Šž:<ÿÃÖ¹®DIºý!q‚ƒ04¬®náôz}î:>Ãkn˜å\·xì#œû=VKbÆ%`§4~q=ÿÅïsǧ¿åtõUu±oŒÅ½¾G·Ú"ŸUÖhˆÃ8 hÖbZ*AµB&êÒvgðl³5­µ‡þÏ>5³“SFiŒ1ìíuh5 …,MÙÚÜ£°ŽÑhÄn9ÌM‡ê<×çÏ?ñÈó,Û»Kå×í²Í+®§èEå¿ðãß-ÿÍÏý¿Ž¹Síï8ÿàœ±?£¬­¢Dùo׉  ë0J…šÀqD£Óˆž\Þå·¾„Š+Tã~o"Äq„VÂöN‡ñ8ernûÏ-n|²4úy<{—«ËîÀ¾†ç»ßüªï« ;?:{F‰Ó"hEá‚ •àœC)E^8pŽÐ(*a€Ršέñ\/§Õ¨¢DØÝëÒj6( KhVW7:>q yǽ§\#ÒòàzòÁ'Ï^ú¸Y²Î=ÿy=þugü p3ÈsÀ·¿æ–_Œ¶7~D…Ú( çœh%d…C‹€8¬%Bn-Z E\Ùðáó›J ÎÑéôi·ØÂb]ÁâÒJ4ý~ŸÙf•oºí6Í\3R²²Õ¹¬Zç.\ƒîý­¶y/….@zós‡Þ}çñ߯æÉwimÈ-d¹%-,U£±Œˆw"(¬s¥'ˆÑ<³ºÇ‹]í&Õ0dœ$äyA­VÁ(Mg¯C§; (`8rzºÎëNÎ SÎÌ·˜›ªË¨^?áòúg¸Šë§×³ñ_, x׳¿§ÆÃ×é p‚“X+Rë0@?ÉÑ"HhÐ">­0J‘d9zb™®‰94ÓFÅÖæ.ívk-Æh–W6±ÖárK¤à޳kDdYÁ­Ç§iUB)pÜØÐ7~øê<þoÓÉ:ÀwÜwË/1¾NãœÃ‡{ë@À(Áá€QZ`´…B¤F+.í øÓç7™94K‹uŽífg§±E/-S¯Tµ¥P`Äq¬cåeÇg ´çÕ*±,ÔÍ`.0úr–/ˆèú‚v€o¸ëÔGYò_‰RˆÏï‚B¥p8?PW‚Ö ,$I …O^\ç|¿`v~†@A·7Fiazº À^§G¿?¤Q‰Ñ.'·Žfdè ¬sÜsj'Bš9´²ÔjõÈLÍM7nØÝëÅsôÆ×{$xÁÖ7Üý²Ó7Göý8*Q%^ Ea­"`‘ýv¯›d|àéUÆQ•V-ÆÃæÖ.íV…1еµ-Ò4§\‘ãpLÇ!(àÌl[NQ¯„Há°ÖR8G­¢±Ø úÜ px~†P):½Ns³S8ÃÁˆN§OFh›SXÇT†ªj¦âÈ0Ns”À8É Dh‚ÿ Ú±š?y|þöË‹ë瀨l9H_¥óÆWÜòm›þˆ³JDJáop¥®"}J´f¹7æþgWˆZS4jÂ0dem©©6Æh´R¬¬n¤9¡ÖH‘㜿õÕÀE5ÐTC52L×#"-TŒ¡Y«0??ÃÝw¡×íPä¡ÑR‚êC6>©DöÜuÞ ¾R@ô–WßògŠÑoK©p&"ˆRŒöEŸ´ïõ?yy›_Üáè±ÃDFSØ‚õõ Ž™'Ëüż²´ˆ…yN `¡^!4£¨‡šJ ‰Œb¡]A9È ‡ 4íFL«V+ÌÌÍb8œ­¹ìÕ N[çe1(à+;Õï{ým²ƒÁ÷k(A hñEžÖ 碄HköF|v…Q3Ûª£”b0b K³Yß_ÊXߨ!0†ÀYŠ¢ ÌÖbŒV¥ñ%ÄF8:]#ÐåVOè×¹¬µmBæL³²¼ŠÖŠ<+dgÌöFgð~ú—\¯QàQ¼å ÷¼Üw_/Jç¹³Æ8PJ@¨„Ô:”‘Q<±Öáá• ó³J-fñò ³sÓˆ X^Y#Ï-¡Ñ›ã,LWBêQˆVB¤¡f Z 5#Ì4c£È­% D É ´Ã$!™nçÈs'íF…Cu0%"[ι.×Ñø—>òä• Œek #8Düç¨ËâÏ(_~è¹ ÛpÉÃDFÄWõ'NÅ9pÎqéò2ÎVâó=0S¨‡F„H µÀh¡hZµˆZOÀ%­)¬·dVX’4Ça £gˆ#0Šã3S óQÕëù¢]÷phº!ÉÎÎÖç6G?X@êEïËiž†åÞˆ÷>¶DU9º0ÃÑéôH²”ùùYF£Iš²²²1qUäZ3W‰ÆhE(ꢢ…j ¨U jHîÀ:‚[–œJÀ:G’åSí6¢å'ŽCÍxViµ EêÀD¦]àK<;=ؘ<ÓN4âÿÈF+”ÀƒË»4š5ïdA€uC@!"œœmœ*% ¤àK8µZEƒ'n¾ñL;ü–ц¼@uÇ)InY82O¾;`¯€Õ•MægÈòë,[;Æ£„0 p6GrKjZu°–¬(¼ñµ7¾Ÿè ÍØpdº†ˆ`K)å ð“ÃÜ: ‚O,I­ŸÔ¢(6X눢[BÃÖACË~»7ºv)à‹8ƒÁÈqäÄÔ+¼?Wç–΀B)æfáX«†ô:´ZMÆiF†,-®’g9:и"‡¼ ‡Ÿjâ G^8*¥ñ#-hå'}‡¦|4±øŽш82ëÐåG…s„"T*!i‘’ä)•Jìy8”åòÿhSpP|‘gfvÊçJ]›ú¶ÃáýE’ÜÞMr¶zCÚs34šuœst»=zã1·>‘fĸ?äâ¥e´ |þÏrÄÌ5ªn×È ‹s–È@hA¹ÅS3B5 ˜¿Þ:( J¤ 0ƹ”Å#  Ãþ˜Áp@†(ñé% yZ„F4ðº ]w°½µ Ó í·Ýuèþ|8º¯3ÊÜ(·Ì>DžaÌÖú6µf„Œ†´t²®°Hž#â8Ún2S=ŸÏæÕ+C~ „Z fªU…säåx7КÀhœƒ¬£=ú˜ •#P0.íFL¯;`o¯Ka Q25Ý@)‰ÏÕ›\Ýñ?¨þÿÎ]wÜt×™ ïtw ÒÜ…q$­VÃ=aÀú곇úaÌÚÊ¡Q8®4‹Û]â(äÄL%BQ8 [xÇ”¼¥µ "´[*ºŽqnÑ ¢@£RgAI"µ*ZH O,-ôºƒ ŒÃqôÄó‡gé1 ÛiýÊòö$È‘BŒïD‡n8u꾦üdUç?dÓ\Z¸ñÄ‚ô“”Õ~•õm޶k,ÌÍ& ˆbey…JR¤¹%0ÂÇf ”`¯·’"G ûF´ Ë±ñÂLfl¼!Eül_YâÐ#Î:r爔Ÿ6ZU£H ‹aŒcœ¤ û­5y^Ðl68vü03ÓSÌ´›˜JYèkÀ ÷’s€o~ÃË_µ·±|öÙ+O'v_³¾¸ýÖî¼±*ÿÀv;?Û Ziœ1,ÌOóÌÒ»)Ñ¨Ï Çxri›C³mºÝ.ãјZì5ô­µTŒâX£A’dŒóŒóƒ!_ñãZ+ŽÍViÄÏùåÉ­%P£5"ÂØYZÁ(w„Ú“L¡7ÊÀÀp”î{²˜›k3Î3Ò<¥R«j©]“ÔK*üÓïy½üÓßú„{׫oú%ÓÛù‘Fèì=o}õÅxjú¡¥¥µåñÞÞéŠËßšõFáþO¿øÔ? ýÔS2ê(Ÿ>¿NœòêÙ ÇOÞÉ?z‘cÓ :›[äEA%ŽÐâñøV2Õ¨P 5ãȰÛ3L2Q(ãÁ£ÖYê±æøœŸìå…ÛŸ$jq䪡ýŽs‹àC’;Œ"P8O9ßMRÚ:bf8å#Hhêβ»Õ¥GdIJ«Q¯Wãóµ.Lxò o¼ë†ïm¸ì_‚R"ýÝé••—¹ÁàµÚ¹[EDáéó‹ê_ÿ‡Ÿ•f£É¿ûÍÿˆËRÞpfžÆT‹í΀µîo>^GMQ4à˜oV8Ôªií  FQ 㱿™±Ñhñ·³] 96Û 0ž1¤”grçPZQ  ¹uDF“ºÁs‹òª?·ÑñÑÃAaAá£oç[Æ‹—7xly÷¡Ë+ÛÏpUµ»xÉD€“7¿cNÛ_Vœà˜€€ÂZ “§ÌÆ—×ø–7üøÓŸã=§óoúßQˆ¢¦4u—ó¶ÚˆÒ´«B-18o@ 篗EJ¨åè\n?£;Lˆ´b¦1×®aKí%¹Òµ8RëhÄ(H2KlÖZpŠH+rç'O¯ìGš~RP‹ü÷ÙO3¬sh“ }Îþˆnt-1ôÅ=þòdòk¾é•·¾ûUmó)±EÃâ*žÁã?WNô [Pä9 dz±ºÍ]wüg^vœÿý×~†™é&»«ë¸,/'r~·/! á„ QDB-¾ÅÓŠ@)æÚNÏ786Wcaº¾ÿéûÛ¯ ,þƹ7x¨Iስ|JµÿqŸ¹¸e­˜çäÀZG^¤yA”ÑŽSÖ×÷ÈskGÙþSm/~>ÀÏþ¯ÿ—{Å}wßòÎWžùíÙlð;.ÏëV @W–ÞÎßRç,ØÜ1·pˆ?³H«Váæ™:¯{õ²1Løù÷ýsn½ã4ƒÁÈrΑ[‡‡QÞ!b£qZ Fk­iV#ŒVÔÂ’¶U‹=˜#x',ëp%BšûHCR8Œ€(H GÅh´[ÜásW¶PZÈ KšΑ9£´ÀZHò‚qZ0L2zIƹKëdΟ½¸¾'†^—ðU¬š•oûºÿÉ\:ø-²ô§NütLÄ¡Åç\ë½Qæ±vçhµê|âò.§çÛ´]ÂÌ샼àþÍ{9sxšw}÷›éw{<ùäyâJLfÕÐì“@`”¾x'P(Q4*‹w8oyïý_ QçÕÀP”¾@ùˆP {£Œ?³ÂÞ8ó?ÆúQ¯àGZ ¹³˜rAħ4ga{wÀf.O>uiý“ø]Á ®.м8à_|ß7ª?~Ñý×ÜõM¯>^ùôûïÌ +N•ÅP©“gÄ·U¡ÑüÞ£WØèXhD sËSŒùé:á G45ÍÎ^Ut’ŒOþÙS¼êîÓ¼ü¾; â¡GÏѨńe_.J¡Xë¨D†q^hE3pex·Ö•$òÖ+ñ‘$³>‚xã ‘öÕ5Ô\Ùîóà¥MŒÖèRW@‹¢pÞi&ߟ£¬m”¯+pÂ(õþÀsëè Æç€Ëxuüz‹_‘|øñnüŸ¼á¦Ÿ­ ;¿<ÛˆvF+ñcVM¨U2w{ãŒg×öH Ç¥í>£°F+RÌ„‚ò✻‚…½àk€ÏüÚ?‘_}ßǹï®3ßz4üiÚí/X¥•"2 'PXoðnšÓetÆ)Ã4÷»À‰é:óí:§¦¨×j\¼°ÈéNÒëõp¶ Iã–éÃ9Ï_Zç¿þÁocgmm„8Y:¿„2‹c¾óªæ91[çÏÏ­³3HxÓmGJÈÖS¾”XGê gW;|ðñ%>sa“Õ½!'gˆƒ­Á˜ÙVµì„4·äÖú(P:AQFq“ަ,(mA>Îe×ñü#çÖþ@D®à…"v¸ªèýÂu€_}ßÇyçëïøñ°ÿkXZÑÄÚC¥ý“ÔÂ(·$YNžûÉšVž&¥†IÊìü,YšÓëi´›$£•j•îî(ã'&â<O ›{|ìÓOsß=§öÇÜqçI._X¡ßhEbíjÈzoÄý]áU'ç¸÷äÃÌ– ß:% Ç9;»ÆÙµ.YaÑJؤ<±¸Å3+»lôn^hMîAÙ&NŒï—ÿâ ÀWúcÜSiÖ탗wÞ?'Ïã5'²p×å^ÀíG¿ýÛkßï‰÷ì^^üqW²eb3Ùˆ+{J¥PåmPJც"ЂFHòœÖì4Y’P©VÙÞÞ¡ÝjbÃîÖZkÿñŠBü’ÎAh®,®³±Óç]o¹—?xï<úàYšÍ c먅†Jdøßþä)òÂòÃoº ‡YëWÃD Ë»C>wiÃþ8+»  ³ ‡°²3à¶#S(å»Á§2ë]ŸVü€(³7ÎÉpã{ßúöGÍÆ…³gÏ?ÂU™˜”ëtEü‹v€w«ÿÛ­ —ÿ‹ÜZã5W¡÷!Û9/Àäo¼Pˆ°ÚPYìLèIDAT±>ô4®f­BTë –./qìÔqÆÃ£q‚+ŠrqCc”Û’gŒv´ª.]^cíÒ*O?ô•jDZ8êQ@=ù¥>Íî á'ßv³õ˜aîÛn]þšO/íqvm8´"µ–~’ï›EÄ;pšç$¹c»7â¶cSÞ‰´¢°Î3~q¾ÕsQ~2èÆ)A­R<º²÷ë|ü3:{öüÓJ©Ëι¿öÑÆŠ„ßûæWýüpuõGçl ”J&[ÑûU²”EÇøV|ŸýÈJ—QfÙŒ9|ø5£ØÜÙ¡Ùn“ ‡Tk5z;{T〩jÄL=&4šZЪ„ÌÖ#¦ªH³Ðª2ê ©U#ú㌚VÌM×ùå=ÃN?å§Þv7óÍŠ×²Þøq¨xf¹ÃJgH˜ý?÷(+È'Æô)¾¬ì™+%–qšsf¡EnmÉt¤…Wq"äiŠÎ ÚG'Ïokk»ó¨Öê pÉ9·†á#ç,!ßõM¯üŸ“µµO,„ãOò ²_û¶Ø·U¢xn³O7Éq¶àôÉ£,míÒ¨×h‰e~¦M«Q%ïì1ÓªÒ®FÄ&µÖÓµµe´òéÄ£q¨±âQ¾f-æ×?}žî8ã'Þvq¨)¬Ã"XÕ@qq½ÏZD˜}׉ÏáEIý²®ü‡R@J¬PXËÞ0C€c3uõ‹Ÿ$^… q`[SçŸ]ïþÆÊòÚ“"\°ÖM´»¼D¢þZxÕ­ÇßQ~!)œJVŒ•²ð)û`ë<ˆ¢Tù?pŒK<ýɵ¹õ ™…³ÌÏÍò¹ÇŸãÖ›O»œ|™õ|Û]û\ ¤å|`8ÎY%%AÄíãÞ…Œ†°Ô lÿ&L`”w‹c³3¦,´*%y`ØO¥é„•O<ôÔÅ÷zýçEä‚sûE_ÿzÎû_”œ¸åÔ±31¿™çvN+ñ²+åäLD!η?"ŠU޽±_¹ÒJxv«avv¥4ËË뜔û‰­x üè× 8ë(Jq(´¢°ž°¾7d®Y¡Y tÇÔUžÚð™óËQ"WÜÕW?6¹Ž„ ¿,øÁïýVyøÉs¼ñ¦Ã?=êÞÊZɤÕ)Õ˜J±Eo¬,·t“”­~Â(ÏqΑäŽs›=N?L·×' ƒÁˆ8ЍÕbž:{™qn92U£p”¢ø?N |*‘‰¸S‰»k¥hVº²Ãƒ7yËG9ܪîOõ|ñ±QŒRK†Ÿéβëç÷å$L«R<ÒBQ¦2çÀ…¥)·ˆR¬í˜©GTŒâÉíáÇŸº°úQࢃ縪<)ø^Æÿ‚ð†½ó²Ü\¸ùXä~S+Áf“NO•·ÇWû"‚8ÇÞ8£3Lå;1Žõ^‚®Ô')33Sœ¿x…›Ïœ¤×ÐŒ)òœ½aÂÞ0áèlƒÈø]?Xñغ.iÚF Ny™–aÆ»Âñéoºå0ãÜ?®¨RPû[ÝgD¡F‰B+UŠE:ïeêò¼`”F)ÈòÂ2¥Ä•ˆéùÙçÆYöÙ4I«Zhô»ct£vùc_þâ}®Äù¯mõ^0Æÿ‚ðé.|ó=§ÿµ î‰BƒÒŠ,-­'w#Ф($¹»%àâœÀlŒ2âz8 YY^ãØ±Ãìîv¨×klll£ƒ£ã4c»7faªŠ–²¿­ë¬#Ò¾G- ùØÙU¶û o¾íq¨Qe÷‘Y¿6iOá*ðú@~¼tØ2²xÍàIéêSM 5Išº~?‘ùÃs;CÇo--­ÿñ8ÉJ­ýDGIÆÏ<üÜÒï‹ÎËÁ.^ï}þ—Ìš=´pÆözï¿æ¤ÄQ©’¬`œd×Õ)ði’þæMÔ¸•°ÝO©NMÓïö‰âEA£Õ`yi 0à‚VšÍþ˜G/nñ𛿱ÂÄB%Ô(%¤…óÀŽå½!¡ö¼=ç<À3Êr¢ @9a”Y”RTBÖ;ˆ1NCäD¼á p‚UB2J™‰C‰OÔÏ>ueûÇÃÑ%YvÎm 2Úî ¥3hˆˆvÎíà_ýèp‹@}ÉpëtôÝ‘¢f'0g9ô¨„%–4+(Ä¢•"/¼ªå‚…Å1L ²Jƒ¤Óc~~†§ž9Çm·ža{{;y4É£xWäÌÖ"fª»CŽÎÕ±…%Ò†0ð,\#Ž œ×÷“…ÐåT"³ÏäçH KE)Š"§Z‰Ir‹RàláÑ@#8 ë‹Kk-Vé uõ8”m >÷Г¿lã.•S¼Ýò9vƒœYÊ\?â:VÿrS€~ùÑö¿7"m‡ïÉ&áÓYß:¡ïÅEk\‰hå -%Âb¿@†f£Æ…‹‹œ:uœ­­]šÍ:ëëÛcJϳÄJXhU™®ÅžÛ—(•0P ¿µ/µh>}nÇV?eªÐŒC%d¶ P ÑŠ½ÎˆCÓ5pÐæ*Ú'Š}'D„½½¡‹C#:£O>~nåý\Õù¿T’8%‘#Å¿$2,ÿ=¡ÿ/SÂ*3'Œ–“¥ÚêUã;‡(¿ôhDÈl)š`ü||2ðYÚQi6ɳŒþ`H­Z!I¦ÛmVV6ÛgDX¦ã€Vø'U‹«<½þ a”dˆ ç¤jü_36™©ƒslÆ<¹ÑG‹#)ûsëyn©D†çέQCŒŒLKoøH ÕȦSµXúaüÐÙ¥í‰Æí|YØMªúÉÃNEùÏz—}¡ÿ/9Àm‡¢›¤äPMŒ?qJ VfË¢©|t!sïrl­Åîî.ív‹ÕµM9Džtú]ïDe(ÖÊGU®cåEŽ*‹5­k;C:ý„ª1ûR,R¶ï¸ë§µ93ßæõ7Ì•3…Ý*eŠÜ²¸¼C- <DûÉ’r”¤0ÐH-^ùØÃç> ¢ƒsŸgükìø*¼Õ{]:€ÑZš¡ºYËà‰–dÊÔ²¯ÉBwœ‘,YKü:‡æf9wþ7ßtŠ•Õ ªµ˜½Ý¾ß¤µŽJ ©Ç¡w„RÃWáÈó£<(£”b<Î(œ¥(YÚµˆV#&+|z±Î_Ƭðð³Ÿâ@#J´Aá T°Ù³3H¨Ô+;6Í…s«­Ë½M·7¦Ù¨ð™ [Ÿ)±ûU®¾êõ’1þ~xymÍdy±)Læ|`)1z£&š°;JYîØu1®ßc~~–矿șO±¾¾ÅôT›Å•5‚rFÔ .Õ»Å}*ÆP©•€Zè'vQ ûN©Êa½„º”o)c”R>‚ Sç…"Wº\ØìhM£°0]g&+¸pn…o:Bšå ‡ q³¾óÌ¥õGKÃo—ÕýKÊøû@•ffÝÀ•ºûÎç|å (†YNZ䢸Ô͈£8ŽXߨbvf†N·ÏôÌ4Ëk~ g-U£—l•ßÕ3"TŒbfªB»R5^güjV’å¡ñHŸOI¹õ{…s¢È-Ô#ÍÊÞˆq^P ²¼àÃ-±ÕÓšmQ¯E¬,m¡µ¢ùãe¾ßz\§œ½¿˜Œ9E$´J~_®4¾òãß¼,Ä´‹q³E¯ß#ŒBú½ív€n·Wþª¥C4ë*¡MÕøˆº <Ö•‚ÎŽÂBê,ò•¾QžÑ(!s> ÆFÌ\3f®S £4ãƒ]¦3H˜Y˜ÂYGo«KP y~yëb ãî–·¿xÉ:ÀäŒÆÉò¤åIËÂÊçe¡°çŽ´äëu•~¯Ë¡¹._^åÌ ÇYߨ¢V«ÐéônmF¾´Æè‰ñ5 ÓU¬”â eÅéÊ„eèÈ ?ƒA”òkÄk÷èrvŸ­8 UiÕ"æÚªa@˜qÿÃWèR¦O1¦t;Cz½áPD\}Äù%gük‘@ØJµ¹m®ú÷<1[Äëâ(ÒÂ;ÃîÐ#Ÿ{A`¨V«lnìÐjÕI’”v«Éêê†ßÛ+ QàDŒ&PB 4q 9<]Ö¯vj¹&ΫrÙöÛC‹jÏÿŸðr•LV¾|AÙfT+ºÔöQ~Šè|}ŽÞ0a«7æÄL©™épD§Ð›ËÛ½•Ⱥ»úž//Õàòqÿù~Ά8QF jVŒR?翸;¦Öl’$E‘“¦ÍF ç{ÝX‹Ís"c¨~;(˜À¹‘faª\Ñ–¡+ç z.Ù…âåY²RÀˆPàýƒòÿçxcg¹Å–{§¡ñÓÊjèI#­JÈ\+¦‡¬í ùÈ“+ “”éCmîš«~‹ ƒyëܵ~/Ù€VJœsv®Õ¸s:Vw)QdÎèZ »ý„­AÊ(¬Óëõ847Í¥Ë+œ>}Œõ-šÍ;Û{hW íJ@l´c4šZd˜iUö‘=?Ísû·²µ«¿ò%^ç§hrJ‘¦L­WùÔJ±=L¨ÄÊ £üHZ)!0žE¤g÷¬ïèŒ2Nji­nºõäÖ#g—ÃS¸®ë>¿æPN¹Äh­¶jï.J< 2ŠÞ8¥3ÎXùѨ×Y[ÛbfºÍp4dªÕ`m}ír¬+hUüs+ãwõ›•éfŒ)CsPnóNÞï5Æoôš2|{)¡ª}‘7aõ„Ú3~BíSGoœ’—ÜÁéõ°u™F¤ÜI, ‹1 kk{C¶»#wr¾-œ]ûøÆv÷%d–ýHPL<Ó•éG¬Í>J¸Ñ…Ú+w9÷(ä®\Ùö¿…Wþ RªaÀ›½.®í~J„ËÀEüü¿Ë œÖõÕtb#n½3x¦Y«ýlÍÓJ‘äEQP Û½1õz­Í-°íjÄB=&ÔŠù©*Q试ÁSC=ôo&L µ 3GO$ÚJB¨ßÀñËšFAîlù°oõ®lõc³?³ÒªT +1gAiÙÑÓý·;ÿññ¥÷ƒ»PÞþ‰hCöR½ý¥XßRåK;ç¦jƒÐh¼åyN ë»´Ë™©ÅÌ×#ÿ®ÞT•ÀøO ÄA€hMÅ®0£JBÈÄøà;ƒ·â“Š¿,æ´.mö£™¬¥íkwì¿#$8 9?úÝ뎉ÀßüƒNoôDö¯\cü—løW:€ÛGElo­—?T¯¼³k­çþ¡„Ã͈J`ˆËeCí*u¥ÕÈàJNþdØTN™åÉ ùD†½4¶Q¾P Kñ¥°Ü T.n È\¹•„/îT©ö§QWÓÆäò—mæx˜PÔj«Ÿ|âÊ­–¬s-^bsÿ/É&~ "E–g——;£‹GÛµ·TC%‘1nW •òíU`˜®‡å2‡" •Д/gøÑï~!€²û“FðÂLÙÄøåÍ÷ÜOàÐZ¸¸9 ;NKgbŸfKol%«ýÈïoÿÎîÐÕj±Üÿè¥÷Ž’ü)ë¹ü˼„¡ß/ÅŠÐ(›dù¥+»ã+‡š•omWq¸}nø•­0 0F)¯¤å÷ó̾@$€(·ÿ°³sž R”kܶÜt¥’—AŹÕ.ç׺Ô"3Á‚÷A¤‰àŸ›¼ð e?!Âhœã 'ËÃìÑg·?\~“®ô¥ú¿X °ÎŠˆÍ‹|y½Ÿ^œ©†ß\¥Ä?§¢µ" ‚³/‰tb­ËEÒ«{ª–òry³(Ãseê!6ŠB O\Þayg€1ša’—‹£ªü}&J]¾úw×8†¢»7$jTú|øòˆgû®—Ó¿ã±0ñ Íòüâ…­Þƒ¡ ÇsWµz]ù–Ž_°Œ&Ö¼pPþ¶æ¥6_áJØF¤\2§ ¶)Ÿz~½AB`Ô>X1N ‚2âØýáÐ>”]>'ìî iTC>³Ü}ÿ^wðøAá÷•;@¡•dα¹Ú<°Ö­Åæ¦F”S½‰ñ÷[½òo¥¼ÓÒøÎMôõýΟR²/ÈøÔÒ^ÚB}*7¢R•$I  ¢,5®)ÑeN;d=ÍâÂúýJd±\ßÞ<ý_™àÈã tÝÑøáå½ä¦½qq²iâÐP'rérMáç‡;iaKáeO0 ´‡t+a˜æ\Øèò™ [lvGD)û7ºÒrÙEHÓœ ÔûÝ…”N’Û‚t˜ˆªF{ybñ?—œßðY½¦ð;p€/×®–n¬eyöñþh<i«ÛÒvŸí×评•À`J.€?»µö<¥ç–î8åñ+;d‹|Üó^QÛ¸}¾ ¥ åȕӿf (eXËùý+Ë›Ã|ÏsU¦í ïàZGÈE$Ë »1§ëv:/¯Äá|n­§ü‹|q¥HÙR^é^N(ÊåTpíØÈXýí]=oÔ@}³Ž}çDB€¢ "BHð ø'ü; êüRÐ:R(§Ã„؇ï|·;žÕí%B EÂ…ìHÓ®,ÍÛѼñÌ[=Íùý×oãDt"Áÿ‚;¼èñ¯°’ 4 ÏáÝ3oö$íÊÍžÉnÁÒpi:çwIÑ–5úż›É˜Eú¼´2×!x(b ­FadV“ª®wØû}éò ßî­£à§Û4ð*gfDT2óCcì›áFþÖ»éKE4P2+Zò~0søŸÇPÊ5!Ë24u°Ÿ4Í'vó#¡wÇRì…©ÞTð­.‚`ÈÌ<Îóü9±e•~­à·Á¼_Õ ÁÞÏô[c[Vz\M&»m;Ýcöc"ªD…{„þ¿~;$èp[ŸolØ°àžø}c³'e1xäÜ¢dïJ¥TP«=ÿ5›ŽgÓöLx|~‘³?ÃR¼i‘xþú æzF¼ˆ2,_ØVUH-1‹@–9»(ݧ[¿æXa÷à+¿®A•+(s… ;¬ñS¬ DAÛƒüOAÿpÕoIN–,Y²kµß*®w'n îIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemAudioClientIn.png0000644000000000000000000000013215101070305020543 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemAudioClientIn.png0000644000175000001440000000037515101070305020540 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ *)'Ýso}IDAT8˽’K€0D߯Íäh½.ŒŸjý ‰lHó†ÂÇ’»Ç[±»Ó˜YZ\J˜¤$§SÎ*b@Quð53[,½Ê`~dÅÝÑsÎEw«‰§€fŠ;h\´éû©ÚÀ9h ”]A÷!öó·Ìž‘‹mÿ­ á+!ƒÎ¯yIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportFastForward.png0000644000000000000000000000013215101070305021374 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportFastForward.png0000644000175000001440000000055715101070305021373 0ustar00rncbcusers‰PNG  IHDRÄ´l;6IDAT8ËíÒ½JQ†áwÆ”Þ]DÁR°ÑÆÎK› XZÙêØf/@¬,L!–6"â’B¬lÉp7þ€Ù„±È‹=n6X¤ñ+?8Ï ‡ÿL'ÙD}Uôô²k$0;¿z³ª¸úÊ8ís¼þRØ0Nú„[i¥Í½°K{¸nƒåΆ¸nFœf­¸±¸Nú¸$Ã¥»sO9ÒÃFkØ'{õçR\|¼¡¨*‚¨_óŠ@Ó¶÷7Qá죞÷àÅׇ¨¢‚ŠÑž]á«;kHŽ_× xÍÿªù¶ ‚  œ„ùð€¸sŸZaÁðÂbE@CÌfÚNQèÝÜ û*pJ1ÜF^ô׫(C ý$ç6öñ´þZ’¦MÖÿgùzÒ™ÔnQNIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemAudioPortOut.png0000644000000000000000000000013215101070305020452 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemAudioPortOut.png0000644000175000001440000000040415101070305020440 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ 1ˆí °„IDAT8ËÓÁ Ä DÑO¶0ÒCg.%8BÄ%Zl$_žÆØò§žÅþT;2Øý ¶0–4Óë¨KùÂob »óØÜ¡wD)Ig0—F͵máÆÛw1+Ž&/;ŽãßÒþeÆYʸ3C@ß\[ÒBm43› fVß;ÞtÄ¿ÏÜIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportModeFull.png0000644000000000000000000000013215101070305020661 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportModeFull.png0000644000175000001440000000130515101070305020650 0ustar00rncbcusers‰PNG  IHDRÄ´l;ŒIDAT8Ë¥ÕÁkuðÏlÖ$ µè¶•ÚZmÖ¥-µÍ^<+ƒAAaD¡7bÕþ= RO‚/*Û£èiÿ„ºíEê%RlÚÔ&K‚$Íó™0ÙllŒó~Ão¾ïýÞûþ¾3d°%¨ù6èã$‹l û°g· j}Ua8ÏsYd 8н»OÊ O£Ñh€<Ïu™L;iŸÆXBT|g–ÍgaÞOK§NÌž<‰±œ ¾Ý¼¡ÓîÀ±ÑµQ«ùµúµ0‹X{dÅã—ÆÃyá¼H¿MÃë1>ÀkgnŸ «¢­À©b¸;êýÓxï¦_¦áMaâìݳaQx Z‹O*ñžïŒÏL1íøËÇÕFjFŽ>½nž+Ò.o¥[iù¿õ¯»øÜ‡ÕJ·¾9u“{[§|ààsÏÎmôqÙgí¤ýV·cE•v'ñT1¨5/7¿îõÖW?á+q;O¿k4ÚIû$fŠv¬ÔßÇ ü^I\¯Vš~’jL6>-u¥­-‹ìF¡+·°¸·‡ðXÅ÷4/7Ã{ïg3Y¸#Ì w…?…{"ý5â´{UxXøÆ»Þ/=&½ŽÄLqߪ#«Ñ¹ÒÙ`H}‡â&Â,]ýþ*Ӆܾšêìïp߸°Ý­Èæ‘BWÞ©èʼU¶â¿‚— Ãa<_€¾7pº¦d—ÿÊrÀuìÇ¡B+ñ³)ì-“^=IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/viewFileSystem.png0000644000000000000000000000013215101070305020154 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/viewFileSystem.png0000644000175000001440000000146615101070305020153 0ustar00rncbcusers‰PNG  IHDRÄ´l;ýIDAT8˵•MHTQ†ßsïmM--µBM,#'SáZ¶°!¨\”A‹ZEH´" ªu´h.ÚDôÒµ)(J«IÑ3/ý8¦–é4ÃÜqÆsÎ×¢4Ǻ£}«ÃùÎy8¼<|øOÅ&õ Í×õ¨±U’˜Òr¡îhï@øÊ?ƒœ²lmá’¡Ò¢¦;M½p»çƒ„†í¶§Ê:ýþ7s+ EI“Ö·_@D¨.ËCqþbXD|ŽÈšwCüþš9ƒM!à)ÌÆÏrH èC`4 ƒã°-˘k?ÀÜDPçðj£ ú™c‚ K,[þWàÀû~äf¦b]z@@@€ˆÐÓ;„XÜþúWàŒw—¢¯Â›½O§–1Ûe‡²¸”ÙJŠb.ï;<Åžz=jì’$æn¹Pw¼w ÜšT·Ê OÉ)K,ðÀ6Àçói°eûuçÁ3m¥E9à†…»ÍÚ¤=ãzhÄwóti§ßÿuòÅÐÖñ&²qmî¸åʵHRˆ‘ÝïâŸûl‚˜8“V±ÏÔ>:Ù“­½¬¸¿w¼ÝƪæÙ@LaÜ¿SÕ|éCžªUÉìa ÿ¡lÏÈS7ƒ]å`U•£=¶i,œ ,¨¦Œ«>†¯:Ù3çéŽ`&L™$Œl½­½³gj¯BZ :^gòWÍÞù¡€¥å„·l¹¬ÈG0Ió&2Ó{­Ï_×7œ½‰òjYb®¦sµ‡>c÷=žm<ÿ¤|õ²ô½¥E+ÁM .ùÁ½_º}3Z¯,îêîéŸ üÛ‹GÇ`$ÑÍ¥½XÝ=;f¨ªZ’n¿UX³ÿˆ³n¶Å“Ep À ‹§6Œð`Jòa/tû|¾ªª—<šÞ‹±¬5ÃNºÅ¹ümV̦*ËË<¡¬M\¸ ˆ@.;”Æ¥Ì(‘`©ŠX¤{ë&æÊœÀ‹SÜ’¬( ,á&Ù¦ñR‹'|ßpF³ÏÆo~IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/formAdd.png0000644000000000000000000000013215101070305016551 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/formAdd.png0000644000175000001440000000036715101070305016547 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ (é!æwIDAT8Ëc`hÀˆSæ Ã8û,C:vµLx?‹„êI7€ÀD½0@ö3ÜÙ`Œ& ¼špÉ!¤‘b/°`„86'ãqˆŸê‘¤®é,Ã,†ü@@#›†FªG# ÁTH¶èaB+ 0öU“‚ÔIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/editUndo.png0000644000000000000000000000013215101070305016750 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/editUndo.png0000644000175000001440000000126115101070305016740 0ustar00rncbcusers‰PNG  IHDRÄ´l;sBIT|dˆ pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<.IDAT8í”?hSAÇ?y/jŒQC ƒB J*vßâÐ!Œ®BÀ¥o—nîB7AJij:8º„,BLJK0VÚÐç?’˜ä½wç𮱄Tkæ~áwÜÝç~÷ûsp"%ß1Ö55€<.Xÿ Єleƒ}Àià20g𿣩©©•J¥|û¯G¡giMÓ§Óé]!„ÜØØ°€û@8€rll8ý#Ð D"ó+++“Éd ßïŸæ€K@WÙÐvÕ\Œzxø’k333Këëëâñ¸_×uÇÁ²,¶¶¶ÜP(D»Ý­VË.—Ëûkkk¯kµÚKàðu~ ,‹y 9Ž#{½ž4M³Ü.–3ÀÍT*õ¶Ýn Û¶‡€£ÀBiÛ¶4 #\ íPî†ñªÑh¸¶mK×uåêêjx<Ñ4íéììì˲¤”R¹¼¼¼ÜÃKüXéxµ{;‹=+ =)¥Ìårßy¼¼,e³Ù_¯Êçó=`¯b† ;,ø ¼o6›?‰Ä§L&³°¹¹¹ |T ò;N§/„D£Q¿òÖw¼ÌvZ·Û}¾¸¸øFÍ·:Ü]÷ú+ûiù±ÁVÀ^½8ê° |©V«Õz½~ ð•J¥} ©öÿþ„ÆI.ÓÀU¼öÿ –ºx"ðü”2>(wBÞ‰&ÐoÄ(k”·¬”IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/formMoveUp.png0000644000000000000000000000013215101070305017274 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/formMoveUp.png0000644000175000001440000000030715101070305017264 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®ÎéIDAT8ËÝ’Á € E ±kcØ™ìLx¢"TŒñâ¿–þ—6-ð¥–Ù%g‘´ÒÌ"°´kéÎ\ԃЈYƒÐ¨¹¡'æÄ”$3ŸŠ¬µS% î€ès7¡Õ B3Uó” â1˜ÞÛ¦·çÑ› åÏlakðG×ÿoƒIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemFile.png0000644000000000000000000000013215101070305016733 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemFile.png0000644000175000001440000000060415101070305016723 0ustar00rncbcusers‰PNG  IHDRóÿaKIDAT8Ë“ËJÃ@†¿ Å^,4ˆP¡]ˆH *‚ˆÏ£ècø>€¯àÂMׂ¸±}‹xA…j›f.ÇEˆMÓ6úÁ0Ì|󟙉bÄ>°ÎtÞ6ð@2m¬»;[ô¥Bžùr‘© B  NÀSјÊåYªVÇ EˆwŸ ÂqA'P̧ZLŸAœ`C“%pQ‚,´ûG‚LýCèQÿÓZɘÔ;ˆ/K©è[©hÎLAuLjWè]$ç$QÿüÓ´K€VRË1 1XIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackProperties.png0000644000000000000000000000013215101070305020356 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/trackProperties.png0000644000175000001440000000046215101070305020350 0ustar00rncbcusers‰PNG  IHDRÄ´l;sRGB®ÎéìIDAT8Ëí•;Ž„0DŸWsrbâÞ3µÏÔ}&:æ8½x†å“ÀL6%!a#?Ê%ÀWoRªjšYº~ðsjf˜™Ù~œ™¯‰Pkå®ãšˆ5‚R+ªz/ÓL23Ól¾o9o3þB?-ëÁ²Aµ‰{AJUeš&†aØÁܽì&U5ךÝîšYž x{l_¸;9¥2_N»®C–¦­k?,ˆ»Ïö„Š}ßÿ.9ÌøqþïÜ&3»ÔªCð8ŽgÛ¼n¼CÛã‘7yíwpîn´©A?úkùH™äÕÙ3IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackIconMicrophone2.png0000644000000000000000000000013215101070305021220 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackIconMicrophone2.png0000644000175000001440000003115715101070305021217 0ustar00rncbcusers‰PNG  IHDR€€Ã>aË IDATxÚí½i×yßù;çÝî¾ô¾¡»FcIwQII¤¸˜R¼ÈŽSö85´'qæÛT¦*5™¥’r;‰5Ž-#Ec{‰¢$K$A €Ä4zEï{ßý¾Û9óáÞ&¯š ŠòLYÝ`¿UoÝîF7ê½÷ùŸÿ³?à“q‰óïšOè%vÞÛ'âø~ÄßPÐzÛë=ˆ¿áûÓCàz[ïÙ ¤”B)% 6û}!„ÖZ7 {³¯7ûþŽ‚ØîÏm†4 Czž·.l£þ*ë_Ëú½þ7ª~ëÆ[¡ê€hü7µwÄ6~fÙÕÕ%gffŒú÷ÒÉt:Ûß»+Ó×ß­¸aF›²M‘0 ½éÉÉâääda~~>ä àA]à‰DBÏu­µj‚Þz»AlÇçÍd³ÆÚêªY?Ù±={wÝsÏ=ûöìî±£±étúÞ¶¶Ööb¹dNMÍêÀóì]ÝFss³ CUu}?_*‡‡‡†. Ý˜šœ¼µ²¼°´¶¸´´Ì>€eY¾ïûA86Ķ‚ØnϹoß~shèFˆ-¿þ¿ñ©Žî]¿ÒÖÖvŸF˜W.]æÀ¾½ÄSiÞxýMLà¾ûN ¥Àó=¢ÑJJ„!Zm˜²`›ÖL¥Xºöæ©S§¾ó—®-¯¬Ln4 "‘ˆ»ººêÖÖïªb[‚ÀØFÙ¦&czjÊRO=óÌÃ_ùïóŶޮ¿¿87{ ½­]^¹~|—îî.^=õSc|éK/°wÿ>N½~šÙ¹9¤˜†‰ïùø®‹ÐZH!Ô-±Dâàî==ðð#{2R`W«ÕªŒD"¶4¤ÃM K±]—±]N¿”RTÊe°?ý™ÇO<ùÄ“ÿ¢«»ûɱ±‰¸mÓæâÅwÙÛߋҒÓoáÈ‘C|á™§xë­wxíµS¨ äø=Ǹïäýtwï"Ÿ[avz‚Õ¥fgY\X T)™–m÷vw÷=rèð¡ÃjÍçòþììL9 Ca†5BlâjŠÿŸ\Ñ¿µËÜ(•RÒÜÜl,..@öĉ?‰Fî BÅìì4÷=µáNÛr¸ví†08qâKË+¼uú-|ß§­£‹=ƒƒÜ}÷aP(嘜žÆ4 l¡ Ñ~•…Ùi‚@J§ìÙ;xàÅßÚ÷è¥ ^ú·¿ÿ{ßÃp° è„aX­Û ¢A-°‰J[U%Ûáô[–% …‚Äž}þùO î;ðZ²ÙÄäÔ R‡´·µ2:ðà#Ÿz*•NG&&oqõò%¦&ÉWª”<…(|%(V=V× ,/¯1;¿ÀÊê¯\ãÆëZâ$3äò9æ§'É­,‚˜†!%†eaY¶i iH)Ña(|×Å÷=Ò™tòÀ¡Ãw9Žã]¾ti1 CmÛ¶ÃPmÂb{`‡~R°ö 6OMOVð꫼qú a~‰Ã]If¦W›Ï¡}¡²ÀZñú¾‹4L”(ŸT¶™·ß9GOw7R„&“ˆuL ¬G{4BHÃÀ4-”ò! |Ÿb±€½jÑÙÝ›zþ…/ý½jÕ þòÿþæÂ0D~^ø ÈÛÄ vàã„|ë@f›š27®_¿çÓovsöt6ÑÖœdtj…’ëÑÑÞJ"#‘M¦hÊfIfZ|…|ÐPȯ1?5ÉõË—Ÿ P(S.—Ò$’L‘N§ˆØR šX4Ž¥¤4‚­'F&Óäìêïï»5;3³¼I€èvÑB±€IÿBFí œ‹/Dž~ê©çyø¡Ö{Ž$0»”£â)2©½½ÝìÞÝK6›fðÀ~~öK_â Ÿ}Š£wÝ…5I$S ôïÁ2lŠ•¶mQÌ癞˜dia…åÕ<+«+x&‘Lb)-tZ‡H)kþ^`Y‘hœt*•ˆ'b¼úÊ+c@¡!d¬> ; ðœ~#hþçÿÓ¿xþç¾ü‹ïŒz¡ÏÊÒùR••|‰\©‚«4?¤TqñCIO_ý=tõt µ"‘Jòùg_àîc'È6e(Li" ‰VËssŒ 35;OK[íÍ$ZÕähHIø‡e98Ñ(vÄ!O¤/_ºxsyyy¾.ôábµUA°Õm€u7JÿËÿåwxúÙç¿òîÅ‹Mßÿî÷ð«Žt' «!‹kE–æg`fÇv°l?¸ÂÙwÞ¦%“ƲLòù"¶ã0½Râ®ã÷røø1„ YlY@A!Ÿciaž™ùe|á@ ¥‰`bÃ40- ÐHCZ)2Ù¦¦ýîºqãj=0´~7æÂ{FoÏ`«à}0ODϼuÚëÍ×™Ÿ›£#“FúUæ¦ñ]žÎvT‚(Z[XV·°¸°H©\¤R*qñ½wkîbS­í­tvvÐ×ÛMK[3»z»9&%6È$T«'а°°Èͱ1zúzØÕÞN2•±´8Q‡ÃGŽôý—¿úË PÜú½žfö Á-ånyD£Q£R©D~ûþæ…_ü­<ñø¿ØÒ-嘘Ç)ô§5-­-´6eq}-MNÞ{‚Gx?ùÞ_¿Ìùsgð<˜žœanv’•…9V¸yå2ÒtõôÓ¿§ŸÝ»{iI%±L‰HСbuy™wß>Ïõë7ØËÑ»ïâÐD„ &€8µ,e°©Õ(x|P¤²åAÆ=õP+ï’¾ï[@âŸýÎ?ûÜÏþ—¿Øµ«¯¹R©°¼¼@µâR¬”«J nâšx2ESSšXÌajf†|±ÈgŸûþÞ¯ýúúû ý*Zh,Ë!¨z¬Î/pk|‚³çߥê…<¸Ë´ð}¥lSRÈY]]cúÖCC7©*Moß.’±8#7‡æ^}å•›un£ ÔwPì0ÀG\---baaA?ýÔS]?ûK÷ïÏÎÍ |ûO¿®æòF{²Rpk|‰ñ™ËFéCHiòÃï~Ÿ?°M2MM¦!aµärkfSÂ{Ñ3ÐG)—'·¼Æââ2¥R‰™ùyP!¶ã ü´&2-<õìçbltŒÉéYÞúáÐ:à…羈m;ë+«þ¹šõï×ËÒ6F6õŽ ø1WµZ•€¡4âÂ¥‹7.œ?päúU£­½•¦X åC*“¡×8‘:ô@K,Ë!‰bÙ–×VXZšcmi… gÞá›_ûm=ÝtutJÆiniaÏ¡ܓГ[Yayq…x*‹ïyHÀ2-Ê…"ï\|3âpäÐö9ÌÒÜ<×nÜ`âú–x„ðXµQð5‰;¹€ŸäÊçó:‹9/¿ü¥…ù¹ï¿øâo>øø§í¨W™›glr–HÌaoû:ÚÛ UÈòÚƒ{yúÉÏ’Œ§¸|õ*ož~b©Jĉ16r“«×¯qåü9¥H55ÓÔÜJGG;ý{ö°«¿ŸD"AµR!b™*ô¹pöæ–rŒOrôØ=Ü{ÏQö<ÌÄÈ0n±PyãõSsušoöf'Çø86€”R677[ù\ÎøÇÿôw>ûþñ?ýÊÁ»íNd2R†‹ ¬•ŠUŸB±Œëûä‹EÖÖJ„RâÄcØ“²ïQ©TØ¿ŸÏ¿ðEúöî%™Œ‘H%I'xåóó‹LNM21<ÆÌü<­­-dRi¤!QÊGJI<!¿¶Æøè8“LLN“imåä}÷sö­3£ôþý`­nV^×½Cl`‡>ÂÄq*-ù©O?~÷âÚÚ±¯ýù7}WÜ#”$qnÍ.’[^fqnŽ@…Â`bl„·Þ°,v}ïUzûúH¥ÒìÙ·—žÞnLiP)W˜›™axh”ÂÊ*ÕŠ‹ãØhPv]GŽ£sW㣣 ݸÉÜÄßyéÛ457³gÿÁT6Ûd¬®®?ÆÀÛQ•JED¢Q Dþê/¾1œJ§æ*¹•޶¶=1>!Òé‰d‚¶¦4!B ”Ò˜¦‰i˜¶Eàx®‹o[,-/39öÎG£–I$¥·¿Ÿ½ûhëꤣ«›ã÷ÝO¥Z"‘LÖøZHB¥8÷î»Ì-­püØÝ9v7÷œ8ÎüÜ/]`jb‚þþ=v6›Ñ««+ámr§Ûh#€Žm[€á–K©‡žül¼¹¥…ëC×ÄÔÄ-"ŽcÙ$â1Z[[‰8’B¹J*ÓÊ _xšþÞ–VVyëÌ `b17·ÀØ­Q–WVp+e®^ºÌÅóçɶ6³ÿðÝô÷vÑÖÞŒ‚P+ )1 ƒåÙÎ9Ï­¡a÷r÷Éã;q’½{™Ÿ_¬~ãkritt4_öÍ+‡wr?‰ 8yò>khè†ü…/ÿ≯¼øÛ¿>xðð;áÂ{çE>¿J__/~·¦È—KAÈÚZ7hio'qð}ŸÉ™*UÏ?ÿwxò駉Æ‚À#‹‹'A)rË+LMÍpåêebñ$ÝHYÃa¤¢1„RäWsLÝšbèÆMŠÕ*Gî¾(üéõj¡_B”€R]ÿ¯ß6@cn`K0–UéLFáñ'[ó¥âàëñŸ©”+ÚR$b ªnÀÕ¡QνûšX4Š 4г§Oc;²ÉH‰´L¦æ–éêéAŠ€¶Î.úúûJ³¼¼ÌÊò2+«yÊå Ža¢ƒ-$ßÈvvñ…/v152ÂÈè³ó‹\:û6‰d‚çž!ûéÇ>“ú³¯ý‰¯µ’AÛ"#hnÅÓpêÔkHþ¿óOÎ?óüs¯'cñç5R?ñØÃ«ĘZ\crâ KÐÜÜ‚)%¾CšBPñ\r¹<~àS.–¹ðÎY0MÚZÛÈdÓ¤²:;;éèlgàÀ>Üb‰|¾@&Û\Ó?ÂP155ÅÅËWɶ4qøÐaž:r”R>Çøè‹ùãccçºå ÔnùÛ’%a[–"Ž#ÝjÕ¬{ŽÜ<øÐá…ªäºÁÚòÙt‚¾]]ôôt¸>e/äĉcÜuèZÁ»ßãÌÙwp¬¥|™áÑa–V–™œdb|œá¡š³Ré4»÷r`ÿ ˬq´Ö„¡O¥P`ftœ‘ëÃÜ›f`p€‡~Ï?w”k—¯_ÿ“ÿøîw_úÖLý¹·OoÉËØ¢ \×5>Òþð‡/>öøg_È´µÛ…bIL ßÄs+”=ÁÜòÑ”]/ ȶµÓßßÇÞ}؛Օî}àþÌ“ô ôcXšˆcbI ¥…B‰ù©YÆGÇYZ^ãàÁ}8¶…R!Aà“ÍdiimP±¸0Ç­ñ ®£LƒÇOÊ©‰±ésgÏNHiµÖ \¿×c^C>`˱Á–e@4759±x¢óÔë¯&¾ÿ×ß'Ÿ+r¸;Ks2ÆÒB‰¹©Iš‘«7†…04gO¿Å×þè«$ÓITP­¸t^¸Áá»ÑÝÕÆîþ~bŽÍ¾ý&R—˜›žgn~‘ЫâDlР¨P£M›Þ½{èìî`q~‘‘± ÆnMðƒï|‡{ùôgžhýã?þªt«®·ö?ÊØšzw«Þ4M£££#955•îëë»ÿñ'ÿÃ08¤BôÞ–˜(•*ܘY¡P," òÔ*{cÑB˜Šy–——©”]r¹~KÄioë ½§“¾þ^:ÛZl'Z# ƒx<†)LlÛ`yy¾ú®–¿ç(­™MMYJ¥"¯¿ñ&]» +KKïü»ûoÎQë*^VêÁ5j%b¥:4¶ ³ã|ƒ SSSR2{÷]w·½ðüEwO+KóÌNM02~‹v§CBwO7éTšB¡H[G?óìhoiãÜùs|ë¿þB-ȯ•¾9ÄÜÂ,Ë ,/ÎqñÜ9ñ½{v3x`?½=$S)T¨Ph†!åµ5†nŽ09töžnŽß?Üw?_þÅÝüÑ¿ûÃ›ßøú×ož”2TJ­ëþ`Ø,Ö±cl€÷“(¿÷ûð3ÿà7¾ò¿Gâɶ¹ÅE¦ÆÆDX)+T˜˜ž£Tq 5T=åz ™Lb’ÅÕ5nMÞ¢g÷ÏÿÒ¯ðèŸ!™Œ¢µ"‘H$ðª.s33\»x•áá võõÑÚÑŠ¾[%‰ÒÔ”Æ2 ü `ei‰á¡›Üššb`ß>úzûôÙ·ÏÜ* ‹¦iV”RÅ 6€ÛwÜÀŸ ¦a˜êæÈ°þÖ·¾ÅÈðM6ÜÓßÂjÉâê•ë­¸†BeÙ þê›ß¤¹©™x"Iú$ã—yïÂU²­ÍRqàè!"ŽMàù,Î/²¶VW~€m™ ¡ ÐZ“/•ˆ$“|îÙϳ¼°Èøð8só‹ÌOëÓ§ß=ôpvß¾}öÌÌŒ/¥ÜÌýÛL÷o©’0±@¶´´ØKKKÍ@ÇSOþ+»zvýš’Ö”EÂPÌ nŒO± L¡QZa˜Ží±,ª®Ëôì «+9ŠÅ•j„ ¥­Ö¶V2™]]ôî飭¹‰üÚ…b‰ŽÎ,Ë"| o½q† —®±÷À^úw÷qôà!¤ÖܺÉÄÔTy~aáâë?|åÜÂâ¤a«a.«uýŸoˆ z jaKå¶"ˆ¥¥%500}ö¹Ž?üÈûûzûTnmQÎMÜdanž°¸DS6ASS Ým”J%%yöéÏqסÃÌÎ/ðÒw_fii‰ˆc~v–Ë—.³º²ÌÔø8ÏHbˆÄ»M´µ·1xp?ƒƒ»±¤AøèzR)¨V™›`q~+ï]æêå!ùÌcúþG‘³ï¾þç>±¼´T0-KµQ2Ú’I¡­hH ü…/ÿÒ/<óì¯$Ò™c˜frjz’ ZBƒ‘ñiŠå ¾(!©¸>e×§½½¶Ö̈ÃÜÂM-­<øøçØø.ºûzH&cÄãµÎ¡0 )¬å˜œš`||‚h,AgG;R@„H!imk¡½­BE!_`fz–‘Ña!¹ÿ¡G¡ç-?wvDJYQJ•¨Õ®çª·9ù[Šy·j=@äÏþôO–ú]˶Y^Z¡Z®pßþ¢N’Ññ)|?À‹LŽ!¥@kÍõ øj*Žiäó,;FßÛïÒÓÛK6“ ÝÖJ4• ‰â»Uæç™™š¦Pª‹Ô D?BS©VÐÒfÿ]GÙ{`…|Ñ›cÌÌL3:<„rõæÐÐR-h¨owúÕVŽn%$¾ï¤Ri3•J%¦¦&ã±xüáßúÍ·X*÷9–Áî&‡Õ5—ž¿ŽåØDmDZçû)q«++KT]J¹‚„8NaÉ$}»{éë륥¥¹Æ‘(¹|žX2AÌŽP*‰Ç¢Œ òÊß ©½…{ŽÝMï.²™´®Jâ³gsÿןýÙÓSS7ë¾ÿr]÷/5èÿu 1¸¥b[’òùœÌçsÖÀÞÁý¿ò«¿öé‡| i[’µ•rK‹,”oѳ«ËqhokÇ”¾V47µp``7RÞ½x‘‘áQü  X,±°¸ÌZn•ÒÚC—‹\z÷"±x‚G28¸—¶Öf"N„0T…”ß-sùÜ{ܸ|Þ=ýÜÿÐINÜ{œO=ú˜ùÞùóöôÔTãÉoôýC>z”ÜŽ ðú_í_ý?ÿäŸ<ó…gµ½³3*QäçY™Ÿ#—[£Xõ‰Åì •Jã)Å¡#Gxþ™/0¸¦m‰:<üØg9qß}DcQ"ŽM:À‰8øAHàyÌMÏp齋”+ýýýئAx€$™LÒ֚Ŷl°03Ëèðˆ˜šUÇxÀ9tà üæ7¾~ (!Ö}ÿÒ&ú_mbìÄ6SCRJ©”’w»·£«§ç¡²[AzŠñ‰ Vsyb©öZžŒÒÔÞI2•¡R.àØºº:ßÿ×ÖÖ(–K˜±™æf”‹XŽA>¿†òŠ…<¥b…b¡H.Ÿ#ƒÒRh­(W*ôìÞþƒX^X`vf‘ÙùyfæÅ™7^D fëžPknW`K…‚ëCž­£G´OÜj~óM÷ôñê©7IE-ú÷ìáêØ,‡öì"›ÎðòK/ÓÞÖÂcO<ÉÔä§^}•]ÝÝh¥ÐJ3?;Ãòò"‹s3¸n…h4J²5A$²›À÷©”+x¾O¶©'bªÓ²ÉçWyû­wXËéëßÅÁC‡xìÉ'¼*—/_õþÓŸýüÙ³ï\Þ@ýï-[¸Õs‘}ûö ÎÎLEo “ŽÇ¸51I_g3ùb™RÉʼnF©¸U.]¸€sß º»:ùþê/øÆ7þ3÷ÞsÏ>û †éË­" IµZ]-¢µ"ðC,Û!‘Jã8J…„aˆ¡JA>—ãúå!&ÇoqõÊU:ººxð¡‡9ùàÃv±TÉ\»vU”J%ÿÇœüÛM ßÀG\V,Í‚&–ˆcZ&Žcb;6†Ä¢6¶e¡ ISs±XŒ PZÓÜÔŠmG1 -4ÅbË4 ‚!%–maZ¦a"„@)…ïyˆõQ0a@,硇¢§»‡ÙÙ–—æ¾>Äøè¤Ê=Ÿ“ŸzìÓ{N¿ñÆÅï~÷e¿>ævÆßf‚ßÉü˜Ë™oëîFˆZØÜ‰DÑÂÄ´,¤”HËA˜6Z à\©à† Ó6„TªUÂ0Äu]Ð6B‚ãDpœ(–c#¥Ä4LlÓÄ4M £6%Ì׆„RÐÜÚJ*•âèÝ¿Ze|â7‡oÊá¡kª´–¹|ùÒ  ôÀíËÀôŽ øøjÀ}óõ×/ÿÜ/ýÝ'í Àh4B„ض‰T©z>q;Š!%¾ç²²ºŠ- RñùBŽñ‰qúöôc‹˜R"Dmü›eY˜¦Fm ebZf-† jò ]M CLÛ!›Í’J&Ùè0äâÔ©S‹¿û/ÿç³@®¾càv. b Œ–[Šû-Kâô™3£kËËÿ>pÝœëViÎfXXZ"‹!ÂÅÅy’ɶ%¸51AÔ±xôáû±m× (äs´d3Ý¿ŸîöZš›iÊfɤÒd’)2©$éTŠt*M2žÀq"H)‰F¢Ñ(‘HÇ©M¢ÖÞgÙ=»úyìñ'b>ôP¶nýo<õÛ¢`«ÅÞj­E<7}ßçÕW_Éf2nkKˉÖÖóÆÐMZZ[iÎ6ñÞÅËœ¸ï8ñhŒÓ§ÏÐÔÖ“O<ŽçyLMϱ¸´L¹RáÐáC´¶µ±kÝÂñh”h4Šã88ŽãÔì„@©Ƕ1l Ó4‰Ø&ŽmãØ¤a ´FHISS‹S©TÖÞ8uê&îhŒü5†;ø8d}üª†¡¼ysx!“Nûéêê4¯]½Ì‰ûî#ŸË1ukšçžûïòÊ+¯`˜6Ÿ{ò³,­,rêôÛLNÏ3tsˆx"Aoo/Q'‚mÙõ9?æûƒ!…a ”Â0jF¢a˜õÛx_EÔ¦ƒ ¢Ñ•ªë¿üÒ·¯\»ze²€rÃÝØ²Y#èNgЀÐZ‹0 E,3<ÏUW®^›J§3¥ûï?9˜ˆ%¢7‡orâÞ#ŒŒ12:Êg?ÿ9P‚—^ú«¹Ÿyü3tµ·píÒ%^ó ã“ÓhiÐÒœ!‘H`YR„Ha€† ôj¶€ec j´/%R˜R H)‰%ÌÎÍþÍ¿ú?ΖËå¥ Âo@cð–¶¶Þ |ß—Žãaê‹Þ›]\XœÞè`:u,¯å9tô(#7†¹rõ:>ö(ÝÝ|÷åï16<Ê}÷Ÿ`pÿZ F&fŸb~a‰X"J&•D ‰@ƒÖ¸ž‡4$ŽÁ”BBˆCˆÚã9Ñ(Ék¯¼ró;ßþÖ%jŸ…¿^°[Á·C.àGÖ½…aˆmÛ" C=66¶ðÝ—_¿÷ØqÕÔÜÒëVËÖà¾A|Ïçü»ï±wÿ Çk—¯ðÆgˆÄ¾ûýý½¥¦T\£\*’J%±-ßó@h"NSš u­ Ô˜BLÇ"12<¼ú¯þ·ÿõÍ•••ù:ý—„¿~7Ú[Þ ÜjøÐ†¡–R†Zk¥”Ê¿öê«ã¹µ•‰D<T*e³¹)kuw¶[×® aZŽÝ{7…|ž çßÃõ=ö p×áìêj%‰G']*IDATKÆÐRתˆÂ€H4‚T-ˆV(¥PJiÃ0D$ê µâÆµëk¿÷¯ÿõëÞ{w(64‚6‚‹@­Îb‹2€QQXÔÆ­E¤”1„H¨0L h=yòäࣟ~äø®þÝ»ªU/•Ë­Êîž]Dã)®]ºL4£`€J¹„eD£ªU—Pibñ(éd Û²±,˲° ‰eZ8¶ƒëz,¯,UϾýöäý‡?zol||´.ü¼Ö:äîTmT[ÖÜŠE¡K×§mÙÔæïE¥”q¥T‚ÚŽÀ$ZŸ}ö™}äþ'«år4™n"ÓÚÂÒÂZ„*žOKSš=») T*ÕZCˆÐ(¥jFŸØŽC:eavví×^»uúô›SçΛ¬ ¹(„Xo+P+üX¿‹|0$rc—ЖU[1¼ÞL)ꧨZÕÖs¨K;‚R©¸èæWC|åÂ*‹S#XNiÚh$³5‹çV ¼*ø´Ñ‚ÐG˜J­_Yòþð÷ÿðÒ™Ó§¯7êøºð7º~U~´ÿ/Ü.A ­€Æõ*µAÃÏ/7R¬ÙÝÙie2M–ïVp«•Z’G×D¯ëÁ«–'Ð#!Ôµ.` „@Ã4¸56ºzæôé‰ ~¹.üõâϺ߿M$pC4êÎ`¬YÑž®ÎL<sŠˆD1 ¥kîœÖšH<†)%*TÈú†)MÂ0 j! nµÂèÈÄlò« ¾RÍ7zU><Lmò¾vð7¸6PXÿ MÀ•‚h ¦e«H$"k‚ ý Äã ¤èÀÇõ=šh,ŽQ‹ Ñ¦ÉÜübùÔk§Æêº¼Ñ½k<ùÅ€m‘ ÜÊ6›0¸ÍëiÊ„RŠŠh/ð‰Dj[?¤eÖòý‘A07?GhHL áÂ<©dšT:‹”fbâÖÌøøØBCP§Òpâ7¾»Ééßô¿Õ`£:@ …Rkíg›Zbñh4îŠ #“ŒáWJ´µ¶ðÀ‰c´&SVçð|—d,ƒ_©P.äq+LÛ&™L±’Ë…¯½òÊPݯ6¨€Fúolþpùð^€msúa‹¥ƒ?‚ Ø`X)@†¡õôSŸëK&ÍUÏG[¶Øwð ã·(–|²Ù&l; Å{öòé‡%šl¢ä+‰$¶ 5Ó3³Kgß9;[·ø«l€Æpouáo«“¿]l€Í¼ê§_éÚŠ.}ôÐàÉT2Ùåê Ç÷Ýwœƒƒ‡hJŰ…–ɘE{[+m­í¤2­XÑÒthéèÂÐ!®[áü;çFê>½¯µöêB®nrÔ&q¶Ëéß.Ø Ú0 5Ñ66i;2L+j¶·wñ…Ïar|Œ çÎI:KE¤¬æ T†G(æsX¶ÃZ®ˆŠb¾P8öì4äô×°ñöosòÕvþvÀ‡@PËá: Cí)9K7ÃjÅ´-K_ºø®¸|ù}½$2MøJQªx_qãÚ5Ìxšˆ!R’+•HEl®\¼tk~~~µ.ÜÍ„ï±ù µOþv±nï*…iš ¥Êh6“©v··TKZû>^©‚ò:;»0 “ÜZªçQÌ(®­’N&°$¸žmÙŒŽ.A°¾íÃk‚·AøÁGXûz»}ŽÆ6{Þ I#-µÖV.·Æ#™Þþ:êÁ54·µ65·´Äggf~$ÐÄí{ýØîº» qåªh`1ãÑO?òx_o÷¯¹å²áV«"ð=T½À!ðÏw ü*4Šh,†eYD#1³¿¿/{ùâE‹7uÜnûÇqÛð™ß¯b²,ËTJ‰Ÿÿù¿ó¸çùÿÝØØDÇìܼY(–ð}Ã0Ñ–E¨©õÖÛÁ,ËÂ÷¼ZG°®MCkÿwÞ™ƒ`¹Þ¸ð!`‹|þĪ€z(X …Õh,±~àaˆÏ÷|” É4eÓ]]]Ù:¬ ªà޳Ìmzê1 CA`NSSú°m[G–ÍÙ™iÞyû-’é =þ9|­²fíƒ)ÐR u­CH ÐR B…[©D¢÷?Þ1333¼rØÀOëêèhÓÓ3âé§žnJ¦â/ …§¾ý­ï¨•¥%&§g‰&Rì;r…F˜FµØ„µ¹@Ò@"0„)©ºUÉ”½wß¾¶MTÀfž@£Ø–ê`»@F£1 0¸o°saiiOni™r¹J>—¯ ¥@¿hë€ ôCªn€)¤&|‚tSS¶nZ·ÁŽ øi_®ë jùÏ­Vßu CUsíLiÊú˜ J׺€H$Öø®_›$©O(­Ñ*$J&©Í°6‰ÜQ^Àv5õòò²Äüã?žrLó¯;Ú;‹-­M"›I¸‘¨C{[ Éx ­T]T)@…>çÖ'…I4¢Î‚À÷H§Ó‰»î¾§™ræ&ÑÀ; ܶ^@¹\Ö±hTå …ÒýöKÿ)™N~õç~þKÅÇ}„R®€ š›ÒÄlzµM ´„A@µêÖ@Õ™B)ÖÏó±;2¸_Kƒh|L5°íÀ°mU€‚r¥Ji„Åbaq~vn¤»³Û5„L$` ë"‰VëVšÆB dmœ!$¡в6-Ü'“ɤ6 ÝîôoÛxÀ¶€®ûuJ…µ<½Ðzu-G_ßnþáo¿ˆF²«gg8õÚ?DM kË&•ÖÒÀ2MB¥”Æ”6MK´µµ5o07‹lû`ÐvϾ_À©´ö½ PžëâD"HÓªÕ††YÓtBŠZVPJÂ:åBbš&¦Ö(?Ä-¤™Lz}•!„0µÖ·S;6ÀOQøë'OïûºT)£·Tdee•0Ã0ks@Tõh  k  „®MV}‘„çyÄbñhoooªã#ÔÀŽðS€´[u*MgÈF ‘´47“L$k ×[€ C|?@é÷#‹Xµå"lÇq:»»’ n üˆ@ÐN6ðoQè°Éàˆ0C­QÔu|è{ %†!вfý iÖå_Ëþy^-!d&¦%1ŒÚ&…ƶ-§)Ók`€ÆÓ¿™'°-U‚y0€RbU·Š!ß߆µW¡ÞgaµYÀµïh¤)‰h‹@+ ˲©TŒ—Þ¶†àR$=× J…œ*—+øºÖB‰8ï[þaຩæŽÞu©T²Ö'( LÃÂ2-,ÓBƒa˜F4‹l8ñ¥v¼€Ÿ’ð zz{Z~ð½ïÇ"‡={÷L%Aäó¹÷¥*Es{;Ÿyòq|ð¾ZüO „4j6‚ÖHC %˜–I$êDؼ.p§ d+\†! C¥÷îÍ´w´½07;ß4=3§¦§ç¥Ò MMjlÛ¡\©Ò±«‡Ï=óöïG‡Š TµT°Öµ(!`ÈZtÐ2M¢‘ˆ˜J)QŸHfìĶ’þ’a¨ô¾}ƒ±h$ÙÕ» ò™Ÿ[ êº^­ÐS>Ý{úyæùçéëëÁs«hà æ8ÂXw$B 1 ¥ )E*•vKk-¤”¢|aîàoóò}ÀøÖ·_š‹'’þ™Ç?=888в¼š£T,±²ºÌÔä,Ý»ûxä±Çhokíº€Æ0jQA!Äû¥aPZ!¥–mBˆuÁßNý쨀Ÿò¥ÿµ×Ný—¶ÐÞÖÖâù®çSÈç™]\!žÎ’HĨÔ;„„¤¾6¦–†D"k!±XL8Ñ(ÑXÔtlGUÝj(„¸£º‚flDZÏóp=0hÒé$‘DŠBÕÃu½ÚÉFƒÐµÔ€†¨o5H|P,•ü©©é•3o¼y¥êVK@Ð0¦~Ûìü¤0€Âé院Â@€Bãy¾ÖZ‰j  ]÷ök¥_ëÐõ@žë“ϯ”'&&æ/¼waftldizrz~vffŽÚ’Wk½q Úa€­ ‚À_]XZ¾èD£»ÒÉ„ˆF£Â÷\¤¯a-ík5šW*À’‚µÕÕðÜÛç†ß=w~z|bbua~>W.—×ø ;¸"¥,)¥Ö'„nìÞöÂb?sãj™ºžøìãþê¯üÒÓÚÒÚ½–Ë…ù’'¤°M)¤D¸Â­–4:0çç×–¿öµo¾9::2Éó×ÛÁ«|0'h}zXžw»¹Az;Ï,#‰ØaF}ßw€f OZ´&A­Ý;ÊvûȺÐ|>4~ýµqZøúÄðÍ&‡l«ÛYÀ –°qLÓŒù¾£–ÇOÕïDý~²‘Â…A]ǯ³@ãˆØ»«›?d›Î ¼cæh­ß÷]~4cG]H.T÷¬GóÞ÷"VÀ7Î ¾Ý°èFÁo{cðNÀº Ö…½^¾­~f7¨±Ñˆ¬ŸâšìñáÍàšm>1ÔÜÆB>xÕ ÄjÃÏü:é#†¿õ7ym}»)á; ðSv×ð#ñ4–t}xÙ ñUñá½@ÛrGÀ`nf Š ú¿±Žo³bøðšžO ½#ÆÄßI Ð(µÉɾ݄/øðP͇·€l wŠðïØøDC| ‘~\Ç턼ÙÞBîáß)Øì}ˆMÔCã×zÃïÞnðG…yõøÁÝÉ@Øì÷ôG°Á+ô;÷}ý$e\š;øÜÙ×ßäýi>A—ØyŸŸLÁ¯_ÿ/[s ·!á•IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackMidiOn.png0000644000000000000000000000013215101070305017401 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/trackMidiOn.png0000644000175000001440000000125015101070305017367 0ustar00rncbcusers‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIMEÔ&k;¸>5IDATxÚÍ’MkQ†ß{g2“ÉÔ6• jlÚJS‹±B#¢ƒt£D»(nŠ+Kwþ þA]¹P]Ù¸ê¢Ø&“’&£I?&“Ì÷½×…´Tƒk=«sç}xçÿ:ÈŸJ¥2fšæÕP‰« K˜êË[d(ÞÕ6j_777‡§ç¥ÓEµZ-¬®®>”¢'XÑîXËGÓögVÉM¾²8VÒ…;Nǘ¦™­Þ»ûØ_bµ[Ù©z®•îœ9Ô÷ô^fÛ¨+»óñ‰ýÌô™ËDQ´ÑívC 'Ö¯WÌöLw-Ëç–² êEõÕ$‘@‰ös^úÃúû™f±ù 8[¼q¬;„j|ß[䯶ÒT>%5ìò–€!'  óCuÿÚþù ¬ëäã$JóÂZ~Äâ±=ØÇóÑŒ†4TÐv¾í§Õôô( …Ëûè%.¤XFLb„Ô‡/;HR.„8çH’DŒ¬@ú¼a¸ºê 'ŽR]¸©¼ÔbÅW†€Ä|²>™¦}Zq@]¼Öjâ6f)O4.E²ÿK õ@ µšMôÍÈ)¡½Ë™K9å…öüŽk® 5" ˆÁ•®”ž–ZFÍxÙl6_8Žþ°m; „|[¥ ÕRóã|\žpôŽîMmLñüóüwã‹ñªÑh<³,ëÇ__¹\.OŠÓWüµ@ ò,aœ8¤Eä­Ý¶?[–uˆÿ*~^¿í¼ƒ‘IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemLedOn.png0000644000000000000000000000013215101070305017055 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemLedOn.png0000644000175000001440000000060415101070305017045 0ustar00rncbcusers‰PNG  IHDRľ‹bKGDÿÿÿ ½§“ pHYs  šœtIMEÖ &§øWØIDATÓÁ±NÂ@࿽KïJ!b$DÀA¥q³¢U׎î&ú&¾#ƒ»Oàäìh"k"R±OÊqG[¿OqY–µËw’«@›2ÁÒ\„^É›úÐn·{Øjìûk7â˜_L íÊa†¢øìH˜Õº¬ß¡Ãë“[~®\V·*Pû8(é´â™FßPpHçv i]9€É*©,!C~k C³%K»òKBå“A« ¥LUB+›1þC‡ðGG°¢dX‚Þ×¹6ÖZÈÞl Ö¿rPëm'¹9 ®$…N!6ïÍGâ’&¢”F%¹ñ®{ºšwóIñ¥8(=—žˆKš¾ïü{ ~çª0IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/formRemove.png0000644000000000000000000000013215101070305017316 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/formRemove.png0000644000175000001440000000110115101070305017277 0ustar00rncbcusers‰PNG  IHDRóÿasBIT|dˆ pHYsììu85tEXtSoftwarewww.inkscape.org›î<¾IDAT8ÅMkQ…Ÿ™LrcL˜246%t’’J‰‚I]”TJ)])]¤ ‚bE\ù ÝéBû\¹Å šEÝ‚ÁJJ Iœ|½nÓdÝwq_8Ï98m… ˆI ôýÛàÐõoC·,ëÊååÇ_Úí¿®ëÖsÚ¶7·î<:ª{ž÷ÝPÔ²¬ÊËùy¯©”ìOMõ³¶ý°è8{?R©Áa4*{ssžiš¿å ¥î®­Uº.@~†Ãò6—ü²,i…Bòäµ®ËõÅÅ*ˆ†Q¾Z.¼“oJIM)©kš|2 y‹Éz±øÙ0ŒË@dÔ¨àÂD2ùà`rRê 5ãã’I§ŸNЬ] ™Ïf3v½NS)\¥HµZD•¿ƒàâîññF£×ãI>z™é[7‰MàüÏi€}meåÝ!ÈíR©Tâñøö3Çé|Ðuqfgïæ(€\ÚÏåº;¥RØÎf"‘¸ñ¨Ppo--U´6´Af{uõM,[÷Íšÿâ@˜£À„¿E0E•|ºú¼ì‚oؤÎEIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackIconTrumpet1.png0000644000000000000000000000013215101070305020554 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/trackIconTrumpet1.png0000644000175000001440000003525215101070305020553 0ustar00rncbcusers‰PNG  IHDR€€Ã>aË IDATxÚí}wœÜeÿûù–é}g{ßl6›J D@B‘*J±œzˆzx§žzM¼SOÏSOA±âÙûyž ¨ B’Þ¶ÎNï½~û÷ùý13Ëd X~ìç•ov2;;“ý¾ßŸþyžX”EY”EY”W¨°‹·àÅ“_ÜÝA®¿ÌB29„â*@ïÊ+Dîÿn7Ó|ü¦78™x—‹[»ÂÈ6H°H†—«ÜóÍ.0-ྱh– òæLa‘egPîøWùèrýÅhÓT¸N ýé<™Û¾ßzäë?,̨0lXk"Ós²V(é@ëµH€—ºüìË}ýƒ}ÊçíNç[Ͷnµ|-š=‹Oî?i}òë?,Àvµ³z:§éší… Ábø"È{ßî¸cÕÛ­®åèZòn8¼çóWï€ËA.îö¤/¾ø<µ—a‰:5G³•U !Œ'Ðõç_a pæ„Àeš,ç­2ü=aØU kt``²-Õµ vG‡Óe×Îí7,_ª9Ór’„¢¦å9BJç­Y$ÀKLz8óª%†©FVJÕ Ê©§QÉì†X:BXXÝëálß§ËëñØŠŸ·²6,)¨ù‚zJס1„e týùq‹8ÃŒªä5›Ï5™ 0à]ÎA* iLö!ØÛ·ÙÒì*žçrñú¡J’R÷  ðuãZÓ›¹†!„!”‚ã³Õ ›{ ,kB­0¡8ÞÔ«kL|ÍÙáÊnècM{(1JQ@xŽPíÿÓ,àEµ« ý{¡*O Ãjï€Ó3£¥,c€*¦PLîA5?£µïz8…wY2ë— 1ÎO+1JQxžPMûËI°H€AÎYfînwëoäYb²»úáj[ £Ù*tµUÊT„*QÍû@8çÁÀ6CjùØÚ÷ÕbŠBóK(Çý/%Á"^ Œi=}]–·¸=OÇŒ&+X¢B—“P„0†3 ]P-Jáh_ÏÁ§— õ¢ý©ÃZDVhÎlb@)ôFL ÿ9ÙÁ"^Ù°J^2ÜýÖö®ef“Ù–¨ J ruTÀr&ÂR/Sµbš¦ÂÞ¾<ÏÀÂ¥‡{:‰sÏ!-,+zÁig¨$S½Aú§’`‘/‚\³‰[Ùßßù‡«ÇÀóP%¥:è°B€Ôs†©+¶PŽCÓTØÚV€g5Xøì¨ÛÁTB¢D+mn–Ê U[êt‘g¡ÜtµõU7ÙÝ,Ô ÄÒI½Ž7€a¥ MKNBAHO¡œ‚®ØÚ–ej° £e¥™€D*ZÍŒ"+´i p¶Èè OrÅzÙï-7¸ojkï½ÜÀP+Lj–e@Ak4žÓë_©±ša̰¸†À’"ë4W‡&—̨QY¡5‹‰èŠ:ï žS˜EhÎŒ¼ÿíNXœ.ëZŽc ”ç I)p<†%óMb €êt]¥2(Ãa <ŽÕPÉœ€"”áîÇЀÃýºKÙ79ìÜùÜ5‘Z8††‚?g;yÑœ!ydG pûßr£#޳ŒâÒ¤"8F…gÀ5êü Ìi­7! õçÂ@Ó$HBFk/8“<òm#5šPü žhj½ƒøœ–`ÑœyçͶy \9Þv!Ë(}ÐË`žcÀ2 aAN„‚R”j TÀ€0 K`0ð€šG%ëƒÑÔöî.¬_É^´a5ÿ}¢D­F1h.Y$À‹!¹‚8Éîr‘›9žpcGtp,Ã’FäÏÔa:E_)@õB0 Ï30¨Bb)I­Ž>ôöºø+/â_ë´‘Wè”dj`jà´®`ѼÀâ²3äÈ”Là“p^ØÞÎý‹Éd´r0èXNËr`~>Èkôˆÿ@HãoB ! 4]…$Ô÷QÎÄUt=1«ä ¥PŸÍ,Z€XÞt­¹ùël'·x®ÝÈÛ@¨FFË0`Ã7þÙÜu35ÔçÙÀ²BkŠQ°¸ÚX»’?w¨‡Ý [×a`l -À™”ƒ'À÷>íº´£ƒÿw«Ýnå;t©@XFÇ[À°FPZú[µŸ6=@ëŠÆŸÆ7 tB–$hº„ãˆ]Óø£Sj@š!) 8VÆ-BôÂ˰Ö®æ}&‹±ÝdiƒZ®R‰p¼ kÕèš]×@õzö¯ë¥ôð[}i¦Œ:‚RB9 jòêf‹‰Y:,-ín—WÅÓZ@§¨46i,Z€H>ó!Ù¶G¨kÿ]žw¸Üü‡ìžN–ƒMU« –! Øøª*AUu¨ ¨€,’B!I€$¢L!J’D!ÉõKVE©¿^Q)d…B5È2  ª®*5]ñÕꦭX´/´|âKõñïï~ƵÆåbþÙêtñfs·.ä¢,Õk , E• ª´  ¨ …¢¢þœh…¦šè:·„ CÀ²Íª ˆ’ Y+Ê)𤿇ÐÀ @ ·Z€E¼€ò·ew ö1ÿnµ[V8<#Pj"!M@Q dE‡$c^«ëšÝÐn•BU)T uw€ÖIÐzÕe†©“€RY£PT šN)oÔ‰ÉÀ8V¦§TÕ=’õB$XÔãE ðBË®æÿÉjáßìô.¡](Ì0ª*CÓ9ˆ²A¤¨I:D±nâE©NUtèõK@É)9ahdºF¡jšªCÑDb2(2å­â-Uá`id\à ,à…”‡¾ï¾ÅjÆ¿8½C0šzõ|ì#TóPuU¢*è¨ :j ˆu“¯R@£õPoDú„œZ    €¨´ñM :…®ÕI£ÐhÝu(å€Ï´V ð<ÉÆs­ä©U ýÐóZ‡™~Þî²[Ý+PHø˜R.YeQ)Je 媎šD!«¤V#›ÏÑôz ˆ:D™ê²B5MƒN)(C@x¬ÑH8³™¯ùtè² "èj±Lå–”ŸÅ© "ºH€çIšà?ü?ž×Øôn‹c ÇÙqJ™(ÍħIMÐQX”**ÊU¢J è¤àÑzʧ뵚Ž|I—Š%](Ut±\Õk‚¨‹¢HEYL)UY0g13F‡±¶¹XW»‡õØ­ Çs„29 Ç&¤¤¢ÐéÙÊ‹‹xå÷ÿç¾ÖjÐÿÛbqu]ˆr!EãÁ#¤\–P8”*j…¤‘ºÉ×êÚ.K:²9M‰¥µr2£3Y-ŸÎiyMCõÅ¢µF'áÔ‚ ÀÄsðŒ ðý^Ûe1—NÁFâZqjNñˆ62¹ñs§¤‹‹CŸ'Ùús×›-ò%‹}¬ÏÕ}1J¹$Â3;PÈ—P©±(‹€ ²È …®‚ #’Tk¡¨’&Ôl<¥¥d •à/$ÓPboÃç« „ Š0â÷.6¥-º€çIž¸×ý^K>ku­ö8;/F1Bpjr¹"*ªL!ª’B¡©ÕšŽpL©úBjÖRRåªk\Mð«-¹»Ü(äh-šLÀÒ ‚©ñ=±±ˆ¤Ðx?±AŒù‘£Eü™BC#„ ÌQèôÂpï7]6ñÜÇmîµ&{û«ML#8¹ùBUÉ€šJ ª€,SH¢ŽhRUNÎÈÉi¿¯Öh@ @Šd(E@©|Óì‹-à7»z­fœ4\×Èó¯Íɹñ^•XŒþ\ùâ'<óàÿëá¸q“ë?xÞü!{Ûy°zÎE*|Á©ÝÈ%ÔTD•@”)$QC&¯cbVΜ˜–㙜FÝ$' AšRä(E±RuÏ— -¿•ÍÔŽm€Ïµ@ky B‹1ÀŸ(Ÿÿ˜‹üÛç ¾ôQt¾êüöÏšŒÎÛìžu0ÚWИ/ ÏM€,}&Ò//Ðú…¦ž>àLÈ!̼,ΜFj3KH©'ŸüšV¯îý×··÷ËÝqÅ Wϵ(d|úôáû˜D,‚šjBQd+h¨VT„¢•òá“R¬R¦) !Hh:""2„ Èq¤ ë(7‚½ÖÜ^ÿ#¦þy—E °@Š'†‰s¥þæÿþ˜·»=C_pu¾¦ËÝ}5Ò±ƒúäþŸ2™L5ÍŒb• RU`äetxÍšÃém6“æ¶³„RHÉt!qôDôÈ–íêÎÃ8„z[Vp9©PÒ¥Í?£À/à4R89B\+æš°Û~nø€Ó9òiW×Uw÷UHGèÇŸú>“É&QU-(Ö]‘0ÔgÄø²68ìÝÔæ\J¬ö6ÂCÕ(ÊÅ0Jù)$S±ðžý™ß|þ[µûd¯›U2yMy1 °èrü±òí aç^{,?¼Óói‡sô¿ÜÝ×—!Ÿ¤'žþ“HPQ-(V,FëVÙ°|ùjw/':5‘x|³3Ç žD.ˆVû2˜Ífg‡G¸`õ8zö#‚ˆbM¤”nÈE ð'Êwît1ïùH}õÆ'ÿî+/v~Êîÿ «ûX\ë‘KÍÒ™c’pài”Då ¯Kêqºzƨ¤I8@ ˜UCÑ’ÊÔèím¼¡¿Ïaïéôð× Lf …œ»öe·Üùê7gT4BKpF­À+¾øÿíeÞöŒßú,úÎYæ¾ÃæZu‹§çzðÖ¥HÇ&hÔÿ4‰'f‘«2¨ @o§ŽUËÛ`s!“‰Ïw»÷g [wI )ÔkïU@ÑW,Íz¯º¸¸zåÒbÇݳɌ±aËÕ—^ ‡¶ïUªšŽ!õà–ÊÝ¢ 8#ࣛyÛR:Üû-,nÿºÃ½î mý7ƒ5 !>JÓñi’LûŽÅ ˆ:Fx¬YÙ£¹él 'OÎàþGr‰{7+³…|¨`Îð¤s˜=:MC]íºËi•ÛY–ƒÑÂsԳ뀡éU?,à È]Ÿð2ïý·:ø¿û¡ñü.¯ã[Ï—{Þ°m4æß|.B •"|¾)Hµ Æ—˜°rÅ(Óx2ƒcÇçô_o)G¶?¥6A÷ˆ‚$!ÈH) æB1=»|”_fµP‡ÁÈ€åˆ=’Pr‰´mÔZ‹@t‘/ üç‡<äßïÌÖwíþ?Çe.3ù¶Í¹öÜöá·AƒFçö’B!E…`ÎïƒP bå2 ÆÆG!©v„Ã<QîÛ\ <®Í6Á'áFš×líæYeJ‘¯Öhb°—³÷öðçð,a4 L±B•Sr¨ñúæÜ¾¶H€PÞ||ãêuý­¿h»Ñ©ß4ÛÆ—µÝM·ÑèÜS¤\)@ÖMˆÆ’ŠÇ°rÌ€¡‘qT#‚ žÚŸîÝ\óÏøõ&øs„ B)š¹FÐlíJr¼¨ëWš.a»¢èÈuõÀ1Éß Mµ¥(tƬÀ+.¼gs½®ÿÄ=žÛ Œv‡É¶¢Ý;ðF(ЉÆÃ{IM(CP-ˆÇcÐÅãX5Æ¡½s Å n»öåË<&²yêo˜û €8¥óÀ7‡9D*¥ÐÂH2uJ•õ¨Ò­): ëFÍeÜÍ>þËÎ^YÇí¸ÇþÏ–|ÒìXmut] Q2 ?JjR ɈDÔžžÀ’aÀæG¶ ÃçóaÛîbîÁmR@à'AJ‘D½¹Ó ~3­Ó µY‘dªZ,¤d6•P@Ó ”zÒgh€ßä`Ï$^öËÃïüHûüÍü»w[¬;ïµ}ÚÀϳZÚ.F©PE2:…š"£Xe NˆCX:ÂÃbA:WÃɉ)<ðH>yïfiZ0 `šø(E õ‰žtƒy´LÞ˜MDïjçh¶ +ð–ë-^»ƒíâ JP¨è"žîdÏ´ö¿ì-À›^ç ¹3M஢}ã:öÓÎò^[Ûp–q$Â3Uˆ¡™léÄ ¸-sé„δ#žÈab*¨?´µßµ_mFøB¦)Bkôõs H­Ÿ/ˆ”Du¾´¼~Üx Kø!AÖçBJ®AýY sä…. ½l pÓµvrïïJ~ø -íü<Ï»ßlõ¬á1wr'‡Ùsñ,ŠéCètE1<< Uw"Žâȉ˜òàãBôÈ„: Ð?B)Ò„ O)r Pùá]+DZIÕ4O¦À…¿÷ iû#ÛkÁ)Ø_Çý&Þòw¢bGMÈ#•ªS>%Õ•g üè¢ø äÆkä7[êàÿân¬êÿªÕ>~…Ù¹¢Ä`öè£(•ópvmB8’F5¿]%ôöŽA”͈Dxê@RüícBh.¤ÏÍk>¥† §×5?@{ìÿËù1Þ8à? ;ŽåñúwÞ,{úzÜ™¶¶Îk­‹/*H¥8p\ˆª[•€<¥¨áY†6_hyÙ6ƒü®êhúš»óÊqgçåHDŽ`òð=Hç’htÝ ·-‰á‡s % ~¿O/1.ÉÌß°C[Úßg¶Œü—Å}™ÇÖ~%2‰I:qä’Ìf+šªalDÆØÒ^PÒŽD*™™¶l«%Û¥ød(…¿~Ža×t¤¤îþ´eíÚqËñƾk­™t ñdÛžB÷>¤œ¼ìBÞ½bÌÙÕѾ…’ÉTáXÛŸ’E4ÜJŒRdqêª8³ó/i|ñ?lóà¿î2¾ðƒ®Ô\—È&{ÊÅ$õM=LÂÑ 29#ÌF ËÇŒ¬Z‹Fpb*AÚ*Äö’çY–4†Ä ©×ó“¼ùŸ|ŶiÉ€ýó,Ûs¾¨xN'èÃ;åÙ­»Ä‰«7q®÷ßê]ÖÓ=fÊX$3ÅòxôI9¶m·0 I®P£ Ø,7ËÀ­Aà¢x.ùÙ×;É_0Ià+‡ýõ×.ûDÊvûö'«ðÅ«.ÊÓ±¥ ùHjð¸ V[ÐÝ3 A2"àб´ò»­bää´âäXâW5fdþ>  vÏ7í7ôv:ï lÏXE´#òaÚ—Rx\ž8t\ž}ûÍÆ®ÛÞܱÖá5gò@<6_¨DÙ©Dö} rù5FZü}~AåðŒ€/Yüê{½ä ¥ðÕ£ëš+G?çYÞõȶ‰ÒdL„"— Õ× ‘È£«ƒÅêxÚ†Q©„‚Óxê`A|p« F”€Ç’ ªÑ€4Ç‘œªÒl#8#¿û¡ýmnï§)éî,Ux¤RÓ8>•îÝ¢œð‡”È?þeð¯^×½Æ`â)‰Ä4&çªÊæ'”ÐñIi@¨aY¢¨7‹2 ðK ´ÿŒ†½äðëï“›ß]ŸÚýþg0ºn}ï§|Öz4 Ÿ?I9¢Âáis)Ö(:ÜÎ%ȳر·TÞ¼M frj@€­ƒŸá9’STšs¸`»çnçûŽŽè貊R©<–/ýßo•£•ššýô‡ìK¯¹´o%E/âÉ ‰Y™„ÍO(þ¹ ìn!W¦Q1Ì64¿¶ü3ßy)ïw–“›ß=A`ßýXÞÝÁ·Ëµ¡K¶Ý?‹»"z›Œ®c£¸ô’Q  ÔŒ¥ÄE„B!<¶«’{x‡= À2iM‚,Ç‘œ¢Ð€ð W£÷owý«ÍÖýA ]$›-#™ cÏ¡RæÇ¿’{ݺðå»V]xÞàQé@"™E"Àãbyóve.žTüuÍG¨I®³_j€//Ð|ºH€g‘Ûß÷*ò¦÷ì¡0µÝ²¡\éúZ¡²l£»ç:tõ=¤ƒ=ÀDãÀªåF\suº‚Pkª‚‰dS3!íñ'åäûhˆR=Ä@Öt$AŽcI^Qh@ìïoÅȯõü—ÉÜûV•v •J#`çþZüž¥ÃK‡)>ñAÏú5+—ô–+.$qÄSQì>(æ¶<¡øŠ%5X÷ùk’8uF éó›c`/Ê8øKŠÛïc.}óü{¯O¦Í_Þ³OZ:5û4.¾ÌI/Ú8Ì$ãçâØñ#ذÁ„ÁÞ¨nF¾Ì#C§f…qá©#ÓaJ«>† ¤S„POó2f#)ÔDšüú§Ìç¯YnûÇ÷¼FT¼H&cGbtë!ððòñ‹ÎçÌ·¿Û½vdx©7_²""–Hbû^1þðò ¥4ØHó"š6ì5[Åüáä}1ïíYM€£[l$u’Kß<­@`ÏÈ­¡˜ñÎí»Äî={HDšÏ§È-ïzV,ï¢nÇ Ö­0ÞÐLE0àƒŠ Œ¾í½Ä–ÎjžŸüòhE§H°,)h-˜Œ¤Xi@îÇ_2_>2èþËu¯¯‰.¤’Aø‚IuËvqfÏAeò†«Œm¸µm}GǨ-5 ™F8‘×Û-EžØ#Í4z!Bm¤yÍqøƒýz_tð³x$ìðÃ=„q^€Kn8H§pýc4a½ë‘m‚wß$¨.R]ééíÅÒñ•`Ôãt¤W@gç2’/rðÏMBÒº1°ìV´w®…‘I Ãj£4'MÌ(³”"i6‘‚(Ñ(€ÚO¿bºq¨¿ã+,×»²*Xû19›”~õ°xòàquæoÞlî~ÿ­\®¥æDšE(< _8¯nyB ìÞ/Oá™na¸1–m1û<³ú÷¬ÿ¬µŸú›¼ë# ÞL× ÁøðoÎùhMìû؇øG·ûa³hT‘Aú¸òªõêÉAvj ¥£4›×ûP¨¸à꺔²†>"ˆ5è2…ÍL°q½ic<­fvï“#‚H‹Ì?ºËüÖÞî®RÒÝ^,³H§gqäd¶rÏCòñhB‹ø=Ö¡7^׳–7 0ѸŒhtÓŠ¸e‡89­Ì54¿YÝË¶äø­³~ôlÿ¬$À]ÿî%þl†yM1À@ò°™ {þ9¦×äòOÎȵ¯ÿ§ûÒ®ï{Úiªädd2s8t¢XøŸßH‡Z¸ó#öeW¾º…F{ŠV‹ÍàøtµúÐvÅŒ¨¾øaRŸ Ì.(ðÔZ‚=ýÅ ö^.à³ö4Ƶ3Çûûj5ÏWÕß½å‘,â)‚uë–R]I©Ç«6X±q=“ X±"Š£T®"žër_v&›£…ÁN·Ñhb ¡ Z T £\ÉBR¨¡§›ë¹öÕÆu}]Æ×éÄm,e$ì9TLÿè^ñ€×Cªwü«sõ¥ŽŒIJ7‘,¢±Yì?!xLž‰%´™†É >°°À³0Í;ëÀ?«pçǼäcŸ¯ƒ?Ð;î °ÿýäéæ'v—1ç+"äŸDÿàéì¶êSH_¿‚eº»P¬ÚøQª0èºCÃë º’­n{rò8/ tö ¨V‚®Æ ÊYTª:ÚœœchÀÑ[46›-"“)bǾJôW[Ä+–|övÏús×,(U½jéeIDATÛ Ç±û˜¹ÿ÷òt¡¨ÿA«xÙo]ëwÖ‚Ö¸€¿‡›|äsõ¦Î®ûȹ'§è×v=-\ôÔþ"4Mχ&‹nF?w9G— 3¥ª‰øý3¨&ô.ù+töm ȚÕí{²¡ýGqò’ Xó?ßæY;28Ö–-Ú ûK¦°s¿œØ¼MœÑui^´Ù-l‰ôËg[šwÖà_þ®ƒ|ù») ü—{;Ïýêνdõ#ÛN@’%ð à´påÕaùRŒRâe¥ªáÐ4r6ï&˜l+¡È2Ñ$6‹Î]w…ç|‡Qâ‰Ãʼn`éír¸¬œQÖ¡¨Ëjhk[ÞÚS92±%|ͦªûŸßÝyŽ×;jKfˆF&Idé¶½Jdë®ù4/Ø’æ5süN݇O;Û?+\Àíïí#_úNœÀï¾›;ºÏù7-S¹õØ»ïææŠp: ®¼¢›.r‚Ó}$‚B…âø±ÃÚSËUãŒË3ʲœ  *Ðå(J¥‰¦Šì[ß>š™ê³Û­&ÁZ«!É&#àvY`·ÁÛµ†¬™¯¹Déì\eMeX„C'1Ék<©wî•&ižÿ4i^ñYÒ<¼Hð¢àß>°ŠÜõÍÙúð6—½ýµb¡ÃˆÇŽë ’Òú• îaÙp4Žt¦Oו½ˆ±›«t뎃s¹¢¡8ØßÝaà £«hJŠšF¥¦££7¯^n³ì=P˜„òñÎN~PQ(Ïq,ÚÜ=…Z%A²" ĉ¡¶y»æ›ò)3 ðƒˆRœ¶ºwÖæøgÞp ÈÄlýñ«—Ãú£¯™>éí¾ì“C·­ž ¨V“ðÏì£ÿZ™8¹¯¼j)ƒõk–Tê&~ÿ2ym=7 oäµàÓœÅfM2Ðbâp¿£ÂÁJbº’€$g!)*8†bɀDZtɰióÖܱL®šs¹¸Ž%œÇ®jUäŠRé<ªµ28ÓvìÓ#G'J¨Oð„(Es3çìÙÖÍ{Éà=·0ä§¿®Ÿ{õÑw ýoos|ÞÛsÕ?u Ýʘ]kP.1uüqlÞü+1:Y¾ð<îÚ•vY·0ÿ,RY ¬ùBx:/Ï¡Ê(‚fSÖÙÓ©¶rq6dÌ&Î-j5ƒjM€ÁHáuY±dd·³g~¿íćUa;;ø~Ž1‚RŠrUoÀàè•èë[Žèác¡9IÆ ¥HrJu¯ÔÒÍÓÎÖÏYE€û;¯ý¨þW?ŒÁM—y¿æí}ÝÛ;‡Þ “} …Ì4ï>x_¥\ŒT®¹¬Û²jù2{M6’9ß$öLÖ©^Åá^Î PªQ5¢«1” Q«:sr¦\ØòxÂçpÚ8‡¹jSä*ï…V ·{}ƒçv¬o7]tNÈÑæ6xóE# Æxº7bpôZt÷®ƒ©Âm‹˜íÖ<¿ï°èoTúr ƒ¥§ÿ% ü#Àß¿ ä«?®ß¤ï~+ÎYßý­ö¾›®ïyŒ–>$c±÷ÉûðàC¿-=W»éÚaçÐð¨¥PÒðOãɧó•{’g'f¤how‡Õé´›–P½F¡Eˆ$'‘ÎÉ0š FY®fžÚŸšsÚYûèøeÖµü̶A¨b&£v÷(;2<Ü3>6ìµ™ËÈår ÌÜÞõ°Úz ë"d±Eg3m&³fœœÑ|¨¯ª° JOñù/yyA ð±÷±äË ÍÿéçqÁøª‘ouÞrIûÐÛÁÝÏíÀãþT{ôчs^— ¿áú垎Î>c2]…?0‡­» ÅûU}ù’î%y²TR==Ýf3o¤Z•hj‚*JŠÔDZM {Îr§;•V£É´ZØ´éºþÑדªÜŽL2 ‹IÅÞÎØ …v"—:‰™é !™w飓cXšªAUŠkIK °œÞk23L ¤úd Ña—hb”|ê‹9ìÜ_¿G÷~W-ÿf×È»Öu Ý 3'ăüX~r×®ìò%^íÊ6§«›Ä²˜õðèŽræÇ”Ùš ù !Á\¾8«é¤ÖÙîYÆ1"§+ "ŠY**tô´;¸ËF]C½ŠuåHÚ. QrøH•R.» «}F³ºZC¡XÁ[|‘ÍGü==}f‡ÍnÖ4²X„X‹!_HéÕšJìvn€ã ÅâZ€>³—ÏËÂü¿  ÁBú|ó7æoã­ý#îî}ïhûÀ›!KE?x/î»ç‡µÃGNd^½ÁÆ_uùj/olcCÁ&§cؼ­ø iFשŸa0G)B„4€T4÷™Í6x\ÆUDÏ‘š!¥² –Ü.Ü‹¹£­ìhwˆPœÅÜì^T+IXÌm0Zza±yÀs bEÝpÎ?Ü«i]¢L YÔ* ”ÊYR,«:dzÃÀ;=«Pçð"læô’ €\BØÁù­V±åûøûîÁK¾Ô³ô}íý7£\ôcßÎà?ÿi1Œäo¸ºÍvá+Ú4j#€GO$´ß>&„wí—gøYsºŽpãÆg!iß\ ØîuØÍFi™ ”`6ièj£p9(—) …,XÂB¬–P(%Q®1ÐÙ•PH?X†‘g¡«9¸¬qëªeûåyÝí6s$¡¢Z­B‘3(—ó¨Š*©I@4ª•#15Úèú•qj‹w1ivFžÙdyó÷q{Ïðëîè[ú»§÷*d‡ðÄ£ßÀÏñËL¥R,ýõM=ž5+—9«¢ÿ II¿ù½86©4Á÷kš‹(Òr„"!¤ 71•‰Z­¤}¨O¹â"–Xa`Mˆ¥9$y9,ÇÀåC[û(\Þ•èNN΂‡P ç²V–WÍ3ÓÒ¾ƒ…¼7YM|•Tkyd füjîø¤4¥i¡¾í{Ïlñþ’NŸ·f:3H¸Ñ`ó&0üÿÙ5xã‡û–ýƒÉéÝ€ðÜ£ØòÛ»µ7oO9ío}ë’ξ¾~K&§"žÄÞƒùÚC[$1?§Ò4Ú<æì™ J¥6LN¡€°#Kv­ºÙ¼v {‰—7uBÓ¦aäjâÙ*Vv¯Åêóß«}ñÈQ<¾}/Ž ƒÃÚœmP4¥2ÅL0=OWKOî ϾúB¥°zÆKUŠ™9%urJ›A}o‚H”ΟÊñ²XYý¼€[|ÆìÿŸêzÓGWÞÎZc˜=q~sßÝÒ£['ƆüÕ ã½nO—!–¨ `ç¾bñÁ­ê\±¤P¥iõ9úÖ™º2ËBèîàÔH\‘h+W`Én7^ÒßÓîU4;j…4¹‹µ:1`Êw î¶ËÀTàŸÞ™c›ÁÓ¥à¸1ä U©~€³À ¿ßÑ9’¬FŸ|jâ`2e‹¢èF´l£àoŒ{Uð‡­^úŠ'À|À÷ |¨sàõÿÞ¿üŸˆÅ>†£{¿‡Ÿÿì«å=OÇR—l4¯¿j¼Ûdédƒá4ææBض§œüÝVet¾ÇiÌÑÿÁ(õëLÚîý¢€~û ®eç-gî°ÛºoâýÅ,Äò,¢  *ƒþ¾^ì ObÇ“G‘LÝ£‘G0C4VEÿˆV3‡T:UPª¨¨Ö(lV†_½Â´Ì¬Îžœ*înüJ2€lcwëBÎf!è•m®½ÄD¶ì)üà¸ÊÓyî‡{—¾“8ÛVãЮoá{ß»#èh!yã5çk6­è¢L™› bj6®=¶³ß¾W™o¶4öÞYPr­r7]`Òß%HðÐO=ë{<ø²É<¸‰7@ª%  !ªÃEI •"Ö,+Âøª6lÝ]ÅŽ]0¤~ÒöÒÆ—Pz¥r º(ÑjU‡$QR“tä‹:ÇqĦ¨´‚ú UBPih~Ï>êõÊ$@ünÀÖÙçþ@÷’7t¶÷]ŽÙ“›qÏ/¾Q™õrï|‹Û»aý¸W”­ù}8>‘7oÇNÌïºÕ°hŽUÍï°i2yÃ9Fõñ]‚ [á¹¼ÍÉ}Å`\c0C¬†!U¦¨ÁÚK ÖpµKP”Ý03Ul<-(WÁãÛB§Fô·CƒéÔqq:TÚ¼ý‡™°%EŠPL®MÍ*>A¤Í={² ð…ð­‡;é‹. !w|×yû6njï¿årNßµý´Z‰ïzK{Çø²eöJ͈@p‡¥…·ÊÁ™€6‹gQÄšŸoßagäñVÛ±OT`ç¯Üoq˜ wÍ#ƒË ÄÒ•j!šzI¾6‚rM@›u#%ðæn¤R%XÈ,.»¨× Äšé\s‚úøÎáåc²uÝ*~¤\ÑH)=©ÎÉ mŽx7o¶ÞÜ,«x‰Œ{18½öë<=9ŒÖ¥˜9ñk¨‡qÝ®ö®\6¯ šÀÞC¹òo—ç’iÝß0û!BX0J]Pks³²ÝªkûŽ(*`cŸºŸ{¿ÅdûO“e©›7wC(¡Š%¬i)RyâqÌÆ¼nÞ´©¬Ž`àªUö¶5à vÈE …"…Ëe¶­^.¸™š(•œiPÁ99«¤¡†5 6¬QóàæÖQ/ýå¢ùÏ®>]Fsï¸ÕyT¹¢VÓ¿áFmÄhf’é*‚!?žÜ[ÌÿöqÙW­Q?«g$[ÀojšÐÞÆ*鬦fóÐÞ|³Íöáwp1›=ÿj²­àySjù§©*Æ 1­B,mE"6«1 ¯» ¬iR9Š ÿòe Ö£¦ö£¡\’P)(!6 B•౉ìþƯ!H7‚½fêÙô÷§ÛÅûeþóB€sÖaˆá–ºT©€ÔÜÝ„Ês`¸A$SÌøØþT%ý»ÇäYJŸY: Õþ|¤ßée•dFSÐï|ÅÓuî¨ö‹©óÝ&Ç9àŒ.ùƒTWªÄà¼á8E"¶sÞŽaèì(b‰¢¡cðGŠÊqŸ3ÛÝï2hNU¨¢VÕ!Š¥(–u­* êçú A©Ñî-á,]ÈyV TåX]-"þ1T)Iõ"ŸKaÚÃ#;ª±­»ž¨Œ´ÌÔ5Ǫ*„@ò¸æÁÇ?ôŽõ¶«w™Í}¯·¸ÏÇÛ ŽPM ç¸%¹ÅÜ/á4gÑÖ¹2@,A<:D’·ïCxr6ãëê:ÀZË×·9©[¢RÄ’Š29£Ìe²z¢‘Òe¤Ǭ·Þ\k©ö½,Áÿ‹KÁï¼ äðdýñÿÆv…ÇYº‰€˜«’D£Qrl2®lÙ.Åvî“§Q?:e®uË5œ:P)šMD)Ut6ÿ¤ó¼N·úm³yà5ö¶ àx „Ò$tE'Œåb”ändcƒÓ}p¶­EMéA(8Xt“sJíáŠßRg+•Ò„$I—é•äª9ª‡ŽË3ù‚>Óø?ùºC3]¨ù:^Æò”3D€wQøíwÙ›\žÏ±|Çx©b@<:‰\¡‚Ghç^a¦~ð4šßô¯!P)­Ÿ–õó/»7öt±ßu¸ûV;ÛÆA @®DÆj:Å*|ü1èÒ,¬®õ( „ü‡‘J'p|F/mÝ£ò­ñ™$B)Í`F‡íëìV}/XKeš@ýÏfm¿IÊ…Ó>ÚËYóÿR0þϾw뻬ŽcŸ' ßžÉÄã~´w¯‡·—‡mÏv¥ÑÀ 11ý™OnA1E¥´®aŸºÝÕîpr¡Œu5kê…,Å •Žå—‚µo@¡("} -Âì¼¹2vÒÙ¤Ùm{$¿$Óù8ƒRš&EJ!ÌúË1xŸ›o¹šþ^ÀK|Â÷L€À׿ñ?ïÐøòÝeE³©…ƒˆÅ"°8†qþ¦ƒ‰«aúäñŽ]{*\IDI§(6€h.jæÔ§øÖþë‰\ÂxäÒs`‘Õ1Æz)Ò™,rÑßÃ`àapl@2[D$xñTûŽ"±sŸøÌ†ÎÏ,ÜÈ5>³¹'€µñ;+ç+x‰¬â}¡äñë_ÿú7ñ™Ï|újª•î(ÕL¶H¦³Ýýt9ÍÉǨpwœ‹W]t…ýÆk0Þ¸áµÆfÈË@"d¾Š¦/1ÌßäLVµŠº9™,!Ž!“e!3ÏKˆùûÀX×!Á?û4Â1‘nÛKÃ;÷‰SnÝ !˜kâNY½“m Ü0ÿ̓Z7i|Åÿ'¿üå/‘H$Èå—_nòz½+ívÇ©ª²ºZ-#+k;÷ÌÎlÞ–;ab2l_7×Ö;|¼Ëaãýš8§ž˜…õýö*”ž²EŠžÉ?ÓO9wµ°ŒçÙkY3kÔŒ¶žM轟·T*ûÁƒAA"‘P¦¦¦¢š¦Í(B ”Ò€êî#xÀl9ΉÒ^ŸJL²ë.x;–½ý›º*…‰ëJùÐk„Z2* ËMO§Ù6häC Ä–;£™E¹F´JÈdžC'Á°µbÊOr{UƒÕ ð7¶z‹aÁàȳ¾ÜÓýЏû￟Üxã:öèÑ£¯cæýÃÃï–$ɘÍf¡ª*œN'ÒétíðáÃMÓòf³Y«T*e Ji©Ñ3¯$ñøú“dz6 }÷:¿ooßòU—alÅupw^ W»`ÐÔr BÓtè”®³Ègæ0sò;º OìŽÅŸT“nÏ·¹l6YÖI*+Uýa%¥iˆ‚€ˆNçgó-ÁÜé 8¯h€ÿ(:ÄPo½õÖ®]»výÃêÕ«/ëìì„ÏçÓ“É$ ¥R‰‰D"ÂÑ£GO >('„”(¥EÔ<)‚ÇQMQHùø,)c•౉]—¯^q|Íòe¶÷,A{×r´µÂ`´A–ÊÈgæŒA8<ƒ£Ç£¥½KÑ _½@‰kµ†…b]nìàn)(eŸÅßÓEàÿDLLL‰Ž9"f2¬^½ óÙÌäóy;v étZhÜø!$F)m¦Q9%J!* t«¼ éZ¹ÆTŸ:ÌDNÎW·»/ëë9ÜßßëìhosÚ –×4ù‚ …¢Åò\PÉG“H‹R==ã8d4 J! , •RºŽZKî^ZàïÏøi/ ìÛ·O¿å–[˜ÿýßÿmÚ´é~A®5¸è¢‹ˆÅb¡O>ù$Ù¿¿è÷ûS›Ÿo”X[˺M Ô«å8–¨VU«5U-U˜x©BŽúÂZŸ‘/öù¢‡a`¡§¨Ðj"j)Öã ZdäUBPaˆ”BÖ´ùA ¹aæk-©ÜéNÝ^$ÁŸJ€@ @;::XÚO<±ÝjµÞëóùÞÄó<Ö­[G ‰$*•J¤m7#íÖÊžØRBÕTjå 4–%ªÅLk”Ò‚ 21I!vI!ŽzˆÊTç8HT M«wã(EÒ?¨Í·¦p­Z¯/šý¿ Ã`ß¾}zoo¯1¦8ð]QÍÅbñÚd2Éf³Y!“É$Åœ8žéä•Z¯fŽ}J´­iT« A•çPdYj&fJaÀé:M§TÓ 5BRKFl©Ði-@7¯Öçµþ/”ù‹…¯Õj<€Õ]]]W™Íæsòù< …B@³ê–hDß¹†Kx¶ YÒ(5³¨ŠÌ¡~Pró1‡gÎÊ¥À)f~a¡F_Æ-æðÏ#æÁ2¼$I&Ô3ï0ŠF’i~ͼ»ŒSgåès½w ”Ü$@«V·ß 29M^¿(Ïš¥aža³®ë6NvBG)•&¿„?<èè¹À ¬Í §ÑîÓi:pS~¥  ¶až-׸ér‹þssnòGþÓ”hÉ"ØgŽ­$`ZˆÐ4ÙhñÓélüé4˜œFËA‘°ÐT3 L5]v=æx𳌧s Ïæƒ{™à¹^·úËH˜?óõ‹à/Ê¢,Ê¢,Ê¢¼<äÿ Q­„zxäIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/viewEvents.png0000644000000000000000000000013215101070305017334 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/viewEvents.png0000644000175000001440000000101715101070305017323 0ustar00rncbcusers‰PNG  IHDRÄ´l;sRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ0, Þà·IDAT8Ëí•=K#Q†Ÿ¢†Q!+‚ˆ ba![ [lëñ ,ÕB´"˜êVV‹Xn¹[l£,k!øÚXX$(%N&ã|‹(‚™t`;_88‡ûœ.÷\øÔÿ–Š©F"êpœ¦hÊy~ W¥ëZ_ÿ 0œ"¾ÿåÛª„a(G'çÕ|>? ¦»žÈÅĤ¼äÃó’Ô=»ÈßÃ0¦€¾ƒmWÄzhÄ-²wp)"•šÈ½ãÉï?»7J©Å8@&®áPž5ÉdàzÆ$Ûš£¸õ‹µÂÛ?÷éî5ª”:Ÿû뚊ÝÈ8.߯¾Rsê¹6Ú;:‘P)5ò~pÕkÞ¤«¨¹3è|UÔ€ AM{¸kš’¬êÈ~C‘Üp,™|ÙÔÜÏ›ÍýMxû‰Ž­eU–›!F@:°ó¹“ö ½ÐìØÙH鸧 J/ÐVh†ø’Îqv(¯~¼ã!µ|t»%m¸·ÆŽÝÏ"VO?<Ìû |’ñIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/viewZoomTool.png0000644000000000000000000000013215101070305017652 xustar0030 mtime=1761898693.060267569 30 atime=1761898693.060267569 30 ctime=1761898693.060267569 qtractor-1.5.9/src/images/viewZoomTool.png0000644000175000001440000000237015101070305017644 0ustar00rncbcusers‰PNG  IHDRÄ´l;sBIT|dˆ pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<uIDAT8”]LSgÇç´•2,6ÊG9 ?X§Hf6[rHÜ4Ã25:—x§‹Ù¼X¶9ï–Ì,&î†dº›-!‹,Q"0c"µÊMX O« –"ÐQúLAhËi»‹LGÐ%{’7ïÕû{>Þÿÿx}è€7€ÀªÝˆa`˜ž*^4h1P]QQqôÔ©Suûöí+,..ÒƒÁÔÐÐý§Óy¿§§§¸Lj Ó¯ë7EQ<~áÂ…oΜù¼tiiIÌÍÍ`6›)((@:::^\¼øÃo³³³W€Q`y=ðj¥õÍÍ×¾?tèyppÎή„Çã™ >UUc’$m³Ûío;vÔ$IŠâIž;wîÊÌÌÌeàñz#Ø||þüùÉgÏþN_»v5]SS3| Ôï•ÀÀg{öìù£µµ%ùðáƒtCCCLų€e½jËËËË ‡C©înWº¶¶vø(rµ1é` P»{÷n§Ûýgêξô‘#GûÅ5`P~âĉ:UU…žžÕår5Ý@PûU;q`½ÚÒÒ¶X,Øl¶€5üR UUU–P(Œ×û x€(D“RV¤€ç€ÇétÞÍËËcÇŽí9F£qûÚŠE`cAÁÃââÑh4¤A_êsH‘±±±áååå´Á`@’¤Òµ`€T<GEôz½È«µžÝ©èrrŒ¬¬¨¨ªªfƒÓZöÅéé@Ül6³uëÖ2ÒÓ¿&(²Ùl{ssÂüü<³³³­­8 Ì L&55èõúj2òY _ý3 Ÿ¾$³+Ldd–Cf‡HÀ'ìDÂéæææ´,Ën Z—µ‡••••™Íæm{÷¾§+++Ë3™Lv¯×kH&“¹Z…ÅdLR[__ÿEcc£ýÑ£QáæÍ›‹Çí _È‚ævY–¿jkk=l0Ä®.Vë[F"‘hª··wn||<˜H$V«µ¨®®Ö"Ëò†{÷îápÜŽ755ý´°°Ð<² 6Y–¿¾qã÷ÃEEÅ¢Ïçc``·Û°Ûí†]»v ………˜L&DQ$‹ ¡¯¯/ÚÞÞþs,»ø¸ µ_%ËòÙ¶¶ÖÃK‰èóùèïïgxx8åp8ºÃáð“ì·Z­eùù¦QÔ OŸ>U§§§#Š¢ÜššrdÜ™@[›ïVWW×ÒrýxI‰ôª(JÊåruú|¾Kdöl‘öYùZ—K@x¢Ý/Èr§S”ûm’Tº!ãv»Q%ÕÑÑ᜘˜¸ ‹š´VϪ4SÀŠvÿË™: ßï÷ï4›7m÷ ^¯'ÕÕåêôûý?jÐç$©AV´vW—ѺV×1¿ß?‰D¤M›òKoÝjo÷ù|—³ ©õþW¬º'ŸŒ$mž~­ýÿøžwÛÀåÁÞðIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/viewZoomIn.png0000644000000000000000000000013215101070305017303 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/viewZoomIn.png0000644000175000001440000000260215101070305017273 0ustar00rncbcusers‰PNG  IHDRÄ´l;sBIT|dˆ pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<ÿIDAT8•]LSgÇç´¥e¥ÇW) ̯ÄL[R‰ÎøÉœ^x1Ñ™¸dÆçÌ.–lf1a7$ÓÝl ºl¡3F¨ˆVB”#È‚±¥ T -‚ ØžÚö삃!·dOòæ¹zïó¼ÿçCàßM¼äÅ@.`D „€~à ð ˆ €ð  B³Ò+Vì>xðà¶²²²Ììì,-@ HÜ¿ß5ÙØØØÕÒÒR ÜVˆÊ»ÀZ OާNúúÈ‘/s_¿~-øý~&&&0›Íddd MMMÓUU?þ622rè g#­¸páü;wîLëì줹¹9*IžúýþAY–å¼¼¼\›ÍV´{÷'&«ÕŠ$=ˆŸ8qâìðððÀ³Ð¤{Nž<ùäåËÊùó(›6mê¾ߪg?ptõêÕÍuuµñ‡{”êêêˆ(ŠÇËBÑþ Ž&nÞt+[·n+Ï{{{Ã×®]‹Z,–o€åÀö•+W6¶·ßMܺժ”——÷ëÅy`P¸oß¾m‰DB¸uëvÌívÿt Àj4“b±˜V¯×$ž¾¾¾‹uuΠÅbÁf³-Šç‚ßVBII‰ett”žžž 0²hÑ¢õ_dggoQEÐëõ$''Û­Vëáôôôr är¹îF–.]¢7 K´ó"”ŒŒtÝÔÔccc!ÀÒØØx°ÿgïKÒºº¤nà±fžp&`ÕªU«v¤¥¥¬]»F“ŸŸo4™Löîîn1‡ëëë677ß ‡Ã/[EEÅÑššû£G}ÂåË—§\®«Õ@›0jì‡ã˜ÓY·K§Ó‰7n¸)..Â`0ðüùóDKË_ã^¯wT–åhQQQæŽÛ³GÒ½{÷p¹®ÊçÎûyrr²æ@m‡ã«K—þÜ•••-z½^:::hooÚívÝòåË„ÌÌLŒF#Z­–H$B  ·÷oZ[[Ç~‰D"U1eAM¿Äápw:ëvY,9¢×륭­ÞÞÞ„Ëåº 7lذ¾¸¸8ßl6éEQ#ŒÇ†††B’$Ý~úô© è@ul~XZZú}míŽ99Ö·PI’n·ûº×ë=Í̜ͬjv"ðU?­ŠÿvÐ*I]N«57) ÒÞÞŽ$I‰¦¦¦ÆÓÀ}`Š™vŸ=³”Þ¨^™[^Àìóù–¥¥¥.éï÷ ÝÝ7n¸¯û|¾ŸTè+W!oÔtcÌYEóMD|>ßP(²¦¦šsëë¼^ï™9ÐÄBÿËf'š™™eiUÿl@MÿAþÜØ2ý¡ãIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackCurveEnabled.png0000644000000000000000000000013215101070305020561 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackCurveEnabled.png0000644000175000001440000000116015101070305020547 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®Îé*IDAT8ËÍRMOQ=¯†ÒZ¬6Ö6©4˜¦€t¢Dã¸ce¨¢‰Å­&þŒ&·D7†€,‰¦Fí´£v˜iç1oæÍ{®J@t­guïÍ=ç~ÿä÷@£Ñk6›eÃøz·oÛ·¥<™œx§iÚûƒÝÝ]~:_9íT«ÕÉååå'AÝÿîøÊ§gÌuŽÄT6uGOL]×× ÃxnÛ¶7âÄGF­VSWWWŸq{W/—æ4œtž „,¶»Î¸íE8lã }Ç•óÁŸÈ(˜¦9>==}ã\œ‡r8pÅú‰‡ Ì?ÆñÐ< b'µ8çò|+=«vzˆ"…ˆÀCŸp6„`®„@²Ù,°9â,1“ɼlÔ+Š?°<êöˆïöx? ü#@ðÑϸ•JEM¥R¯ÏѲ,gfæJ)T/~i©2òUˆ€ qtractor-1.5.9/src/images/PaxHeaders/trackMidi.png0000644000000000000000000000013215101070305017104 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/trackMidi.png0000644000175000001440000000062515101070305017077 0ustar00rncbcusers‰PNG  IHDRóÿa\IDAT8ËÍ“±JÃP†¿˜Ðn•ú B‡Œm B»¸8…Rè¢`¡Ðr6Ýû¾ƒEÁÝ-ˆ/â{4S±[kÒëàMHbëäà„¸ÿ—?çüW"Òä—ˆ¨Ñht˜¿Š§Óé <ÏUa8SA0Qa8Sžç*@ ‡Ã[àd/HDÚ€ ‚‰Š"QQôí"{‚‰”mÛs ™éŽ ŒwÏs©×k8΀(gA½^Ãó\úýþ]Ñ…Q¨0œašG?1Mwt»×À+°*N’${µgA’$YÛ,²[V–eå¶«EBšî²Öø1!Ž—%Ñf³-õq¼DDÐë-â8> ^Øn?sA¯÷T‚Ç>¾ï?k ©º4õžóèÁæ9èt:ÏÀ pz(TMÛ¶çû’Øjµîµø²˜£ê8ÖÎô´ ZoÀ øÒ}€"¨QÙR¢!éŸþ|_Ÿ±˜#¿&ÕIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/qtractor.png0000644000000000000000000000013215101070305017034 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/qtractor.png0000644000175000001440000000361015101070305017024 0ustar00rncbcusers‰PNG  IHDR szzô pHYsÃÃÇo¨dtEXtSoftwarewww.inkscape.org›î<IDATX…µ—klGÇÿgwfwïµ}íëëÔŽ'Nb‡Ä¡uMhASQ‘@Ó”"…Š´A@ƒ ¤@«JZ* ¨ˆô!!^_ÊCAˆWªZ5¢JJÔ&‚Š6!ÇnÇÏëµ÷Ú÷îcfîM°‚4N8Òhw5»çÿ;gfÏÌ3c6FDâÑ]÷µ¯XÛÑa×ý;ž8àªÑÕ´··»_ýâ=®ì\òÀ‚ùÍ-R;Žcôä{FFý}ýc…=Û·ï ÿ/¹\.óÌvþiã¸ýt÷Yœ<Õß÷áyMsк`!ü`òø`>xpë¶o½®Dä<÷Ô®_ß{÷‡?ñò+¯¡çl¢(``‘‘ëtv®@¦67Ô{®ë¶Ï>þÚ•üZïIÀ–Mk6ß¹aÝG_ÿûQŒŒúR@ ­Zhe†1Žy}½Ý7ÌÉÕÿæ¹>Üv]ˆÈýÐúU&*Iå}RË`ÀF!Ž’DC% I¢pâÄqøþÀÜÒÏÑe5Þk—-]¸¼¿°,.$´IÀFØ@kUiJ1¥pòßÇ m³ö'Oï~øºÌÉÕÕ R ‰C˜$´³†QJih­a4#Ntu§ºLúK;w>¹&€tÚmJWy›,0G0*†Re`0†a Wæ#G©8ÞÔÞÒðµkPJ¥aR ‰JHÂÆÇÆ1L C¨8†Íæ"€1 f‚aÆðФ´6Í ŽuE±–B š,b`pA¥°„8 ¡“ÊhXD`°)g†01 íY[?¶¦qÖFý±Â˜”A¡€Ñá<‚`QÁ(m  v%F&3`@0(‹ÞÒåK7] £Êõ\ c`h …ÉâHÁ0ÀÌ »ìͶír˜*î QT‚y߬j‰ê?¿íæG& ç[Òž Ïõ0QŒa4`•E…-`[¶°A–A:„LA+8R Óù—Ïd2 ŸÞ¾îÍMÕ뇆óP*á5·®¦c”<Ï…ëH8ŽÇq`;ÌËbh&x© ˆ4´ÑH”š6؈HÞ÷ñµ?ÊÖg×CSΙ®ãb᢬[·ýçûaY!HWB:,!¡ @ÚÀs\¤R5P:‚Ž‹(Ã`F€oY‡n_|eI+â¦?8p@˜WŸ­[ùÏcC/ÿù¥#¯°ÖßrªõÉ'ÚvÜ´z•¬­ëÂøø8ŒaX¶ ëºÀ [3ê²ðªjQò€ecØ{gF€oüõÎ%Ÿa­»Ÿ|æ/¿«ô¥ë²õ&Jø9>t¤kåþ^}ÿ½[6n Å6‚ @q¢€0Š µ‚Ò ¥ ¼T5róXÈç b=²ÿÅC/7Í­­éáá±ágŸúÊ®D%ؽóS K·äÂ0Qff":¼÷ÇûŸo™×8oEçÍm^: ¥š% ¢°¥4„tázÕ`Xˆ¢l"t¿;t$ŽqîrjÏwñâÝoëõ]Ïq]Ï^_ÿÈù}¿ÿÛAi"RP “ûþ/ë¿ýˆÜ¹¤½mžãبªiêTÁrÒ†1Ð߃R19xøíŸ1óÄ´s톄ˆZ]W´ a}$Žu'³I57e[ü± ] “³XØ6 ¢Sa²·¾.µñË_زý–Õ+–;Ò!Ç«ãÕÂJ¥Îöœ@wשɗ^=úìëoœxœ™ 3QU.ëíZ4¿þs®ëØ:]Gº¾?^[S]å[¶m2 p¢’ ,%ïæƒÞ›–-Î|ð¶åëš››Z,Øv'ðýñ¸·oðôþ¿þcß™žó?eæ¾éÄ/ÍÀ]m j¾“òDçÅN"X–Ëšr%ëâý`¾ø«Sgò{t´Ü˜]¼¨µñFÛ"û­·Ï½“÷ o8ÌÌc3‰OÐëHi§S.ÈbX–( MAˆsƒÅxÀ‘Þ>?ÓÛç§QÞšûÌ\ºœðtTSSåÔÔ8°€ò¿mÙ—DN )05i/ffSùÞ¯´«²©"“I;5Õ X•,Xh ĨTʽZ½é²Ùlݦ;:š“«žkÛ:‰`KÒq+Y¸d*Ã’®rå5‘ܰ¾ã±»6oz¨ÿ|‚Ñ$CHÒ-·©‘Ûv9##å:i""žíù®’çt÷Àˆ°Éäês–?xŽ#á¸Ò‘®‘U^ó 0˜5R©jܺvÙî9¹ª"}o¶6JµóçV­J≆8 áH[”7D ff £5Ö  *]#²uÕwÖeäð'·íxs6 QS*%îw]§ fº× Æ‚°tñáí,3ÿvÖ@DÎÞç)ÿ=ŽóL¥öJö¤bn,+E÷IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportModeNone.png0000644000000000000000000000013215101070305020656 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportModeNone.png0000644000175000001440000000157715101070305020660 0ustar00rncbcusers‰PNG  IHDRÄ´l;FIDAT8Ë¥•ÏOG†ŸñîzƒU(%(6¤…ÊHU9p0âpàV.mU!=Q ©•ªöÒ[=£þ­9„$•Ò쥑€ƒ©Ýrp„Ph(xA NI+lKØ&¬÷ÇôÀÚuÛŠÈH³ÍÎ<3ß7Cм Àk”f“Åööö&Ðt\u@Ã. X.—ÕT*µ¼¼q¸¨6¶¶¶~UEu]W)—ËZ.—Ó{{{½©©©c  Ⱥújà‡ù={œ 9®£y®§¸ž§²§§ÇšŸŸÿ(‘H<žœœì,àðÚÕjãø8Èf³šã8º”²ºiš:ð®¦iìïï燇‡ßž¥vðxgg‡|.‡ë¹„Ê¥åR‘Û·W—«‘¥ÓiLÓüshhè–Ÿž–pQ×îÞ"‹‹‹ßžœœ(†a|<Òétzww!cccŒŒŒ´…׃C@÷ñø³ßR) ƒ ‰D°,‹£££ÚàX,Æèèh3¸¤h”["‘°7779;;C¿¦ÓÕÙ…®ëÊKbˆÅbŒ7ÂÀ­×§)%ªªÒÙÙI¨#€mÛXÖ–eý¯&“I~¾ÿ1𶱨fAmÌM¥RÁq<×Åö¼ÿö)üåý8…„ô£Gƒ@xñ’*ªåºÀ®Tj yùA A—ËHfÆdeeåËú¶§’I2¦‰ðÇIy9£ïf×:@’Œir÷Îݯ666>›žžþpZ©¢Qv7üƒÌÍ}ðC4:ä_š _­­­}£ëºœ˜˜øÄ—æ ÀnoúÎVý¯ÎÎÎîE£QYZZ: …BަiÇq¼B¡ ÕùJ(Š6^\¯uffæ`}}ý‹{÷~ú¼ÏKOJ¤ô„ã8* »»»­………§j°\¿Öúúûû‰Çãs€ÈåO­¿Ÿÿt;(¥Òóð¤äà £W¢¾¢½Êx<nå½'{•|>§Ú¶+Ãá¢X,Q,ž±ºººÜîðZyŠîçÝÞF€°ï+ªaË~~ÿh• Z¤Ç*u÷ú ðôôôôÜ0Œï|ÎøðsqÅ·²úŒ©Àu â{ÅIÕ3þð_xW–bÙZIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemLedOff.png0000644000000000000000000000013215101070305017213 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemLedOff.png0000644000175000001440000000057515101070305017212 0ustar00rncbcusers‰PNG  IHDRľ‹bKGDÿÿÿ ½§“ pHYs  šœtIMEÖ åJÏ´ IDATÓÁ?NƒPà_òÀBŠ‘8àb„8ˆMI¼£qqsö žÁ±7ðN^Âî%‘ÔšÚJ¡òï <>ðûžçñŽãœÊü cÜ þ ¹gß÷W‚ã\žÔñ#-ÈÍ>ÛIÁrÓó]u=½° Ó4Ÿ„ŠÐÒU·'†¡,‚O(ëò}ˆªìûζí\HòÒÒX >Ö°‹Shi=£P5J’Ä0Æ]°ü‚šP  *€5º!mÛv`ÞSL²x¿éþêzÖ‚,ËDÅ9ïN'ëÃñè8Ú®N-çLUÕʲ¬„ÐŒ—$)Õõ£…,Ëœ¢(LÓ´µ®ë¯¡Y†ïÿÚôˆ³ŸO IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemNotes.png0000644000000000000000000000013215101070305017144 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemNotes.png0000644000175000001440000000105315101070305017133 0ustar00rncbcusers‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIMEÕ 76e—¸IDAT8Ë¥“ÍkQÅІ %¨ ¡UJ³p!HÖ™¥àŸ .…L±ÚE ý²°g6.‹Kue§¥Tað ­Fñ,2Z›jš¯ÎdŽ‹¤u¢I‹xápßå½s¸ïÜ÷à?ÃH¬ À©>g6€WÀËýÄlýaÔÖçµ/õ‘ÑÑIÀÜW Ý–¢è7‚õªâ8Ö³«Õl6{ ÛS J j'?]]S3”jͶ–WüJ&“™F ÔZÒÆV›5iÉÿ¨­m©R—~4BÍ/,~3 ãl’”Ja_öH¥`eÒcè@šÙÛ˜)Ýaîî#r'Ìc`Œš‚½^Õ­Zâ®r½Ñ"ø¾I½Ñ$“>ÈÉÜ0ã¹C g‡4”T«¶àɔǙY ÿŠÇü»{»{ÆÎ›€ ÝÀíñ HËo¥¥rWg  øWa Ÿ®yœ¾iñ|Ê£ò¾ 7À¶í·Çlâáë‚NNŸ3w Åb±ïèzÛðuÚãè0Ó?ï—ð}×uûŠô\áøˆJ‘À,Y¼™{ÝqªKÎçóÿü™ ’.ôý…†an²ƒÅ.’!Ã0¼½Þÿ/T1<ê44IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemClear.png0000644000000000000000000000013215101070305017102 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemClear.png0000644000175000001440000000125415101070305017074 0ustar00rncbcusers‰PNG  IHDRóÿasBIT|dˆ pHYsbb8z™ÛtEXtSoftwarewww.inkscape.org›î<)IDAT8­“?k"QÅÏõOfžcÐ £–â’*‚eÀ.å’/‘Åm–ý‹VÖϰ`é$éb)&EFbce’j&(ü÷,ÄyÛìÈJRì²{ÊÃãrßù üo)¥RJ©ÔŸúôÛ ‚À˜N§u)%,Ëš8Ž#à=Ÿˆd’AWWWǦiö8縿¿oëºÞ7MƒÁàØ²¬nEp]÷@_PJ¥\×­›¦ù½ÑhèRJÄqÜ»¸¸`Œ1ضÝÙßßÏFQß÷{žç=(¥ˆ(^o ¥çœsÇÉÑéÖÖlÛN¯V+„aÎ9cë R@D±eY“———OwwwË0 ±X,P*•ÒÕj5­”Â|>Çp8\ú¾ß®Õj"Š72pGäóùó³³3FD§•J%],Q.—¡”Âh4Z===}iµZ燇‡bcƒD…BŒ1hšÃ0P.—Q*•`t]c …Baã…Á`ðaww·cÛv:Žc(¥ „Àöö6šÍfZñu8Š …TBa:Ö-Ëú¶··—Íd2Râöövu}}½šÍfÐ4 F#›Ï绞çÕ“R½¡†!¢(ÂÍÍÍòùùù³¦iRv²œs!Ëå6¿@DñãããÄuݾï÷~ñnõMÓÄåååâõõµ+„@'ÍfsMa£ÊãñØð<¯.„@­V›$i¿ç'U~£¿=¦ÖOa7…Œ§À5IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemMidiPortOut.png0000644000000000000000000000013215101070305020273 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemMidiPortOut.png0000644000175000001440000000050315101070305020261 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ +|tÚBÃIDAT8˽ÑÍ ƒ0 à—žX!Sx¶±X€#ŠØ‚8° '`„xŒ×KS¥jùR}Œü9–ðÏòÞSD˜¿=®à¦iPðô¯"Bï=I‡a`]×_›b’œç™Ø¶-·ö}Oo<Ž#Cø±…UfU…s˲`]Wt].õº=lfˆ1"ƈišò6·¹A‚96³Ÿxw@޳îèðTU`Jàu°s¹‡X–eÊø:®ªêÚÀ×ë¾[O‰­˜‡·#%IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/editSelectRange.png0000644000000000000000000000013215101070305020237 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/editSelectRange.png0000644000175000001440000000030615101070305020226 0ustar00rncbcusers‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs  šœtIMEÖ  |*±áSIDAT8Ëc`´ŒXÄþ3000644eTÝt³XðèùOã˜0LûÿŸdocÓÃD«0Æ0˜‘‘‘ôˆÂ¢‡~. ãÑ0¦]³ZŒ‚Á¬ã!aÑ/ IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportRewind.png0000644000000000000000000000013215101070305020402 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportRewind.png0000644000175000001440000000051015101070305020366 0ustar00rncbcusers‰PNG  IHDRÄ´l;IDAT8Ëí’1NÃ0†¿eë `¨8‹;ôe€c¤œ0;ŒÜ †Ne`hç.0€D2$ƒi ÄI!$†þƒ%Ïï³üdØåÿ$QíÅC¥7ó7« ~ïá@"½<*ÉÊ^øÛâmÒóQNV( ¾‰ÃĉêéÁ3y)(`Ïn©8þBV€ˆ¢-æ¸Mz…íÂA"´þŠ€ ÓQw¶,•Ë’¼×™y`= \G=‚þöHlæœ0çnLlÀD$ X2‘Tš‰mlÛ½ã10àítCÿ*éS­¶çBWÛÑ-égèóÛah)$ÈÌdi·ì†åÖ?l,¼sY^ ×§âIª` IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemMidiClientOut.png0000644000000000000000000000013215101070305020565 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemMidiClientOut.png0000644000175000001440000000066515101070305020564 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ *.¹¹æÌ5IDAT8Ë¥’1jÃ0†¿gÈä1 d6:€¶,ÍÐÜ@ó;@WuÌ<çÞ¼i „Ì9 £!à@ꪵk§ö!$ýï{¿àŸš8çâ_‹sdƘ‡‹½÷7À âìNpOLjЛÁõ‰ж-§Ó‰¦iX½?ó2bóœ×ùœ·ÙŒŽµ p½Î"Âz½F)Eº®Ùl6üô?Å.Ëk-J)´Ö(¥°ÖR–åÀ;ˆEQ µ`¿ß µ¦( D€Ëå2X.—½Â¤´Nççóyb)] išq@aн›"„ÀõzÔuýë¿HçY–}?£÷çmÛ²Ûí8,‹¯Ø)Ñáp`»ÝbŒ!ÏóÎg"«ªŠ@¬ª*ŠH¼ãJD¢1f`þÜëé;GŒY\ú§¸IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackIconGuitar1.png0000644000000000000000000000013215101070305020347 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackIconGuitar1.png0000644000175000001440000003235515101070305020347 0ustar00rncbcusers‰PNG  IHDR€€Ã>aË IDATxÚí½wœdgy&ú¼ßwBåªÎqº'Ji`ED0"gH&ذ€Y~À®7°ë»Ü]ß½Þ½{½{}íÅ`lÌšµÖ×^š$Y€Ð('$Íh‚zbOÏtî®®\'}ß{ÿ8§ºO×tÍŒÔõûuw©«¦ú<Ï›ÃGØx\Ìjû™Õ7Øx\œÀ¯õø…È`lÜË‹pZã{þeˆ 7îí|ØÔþÿDÛ×v²ˆ \üRO–e ¥”n“lŽ~ÏŒ€1"¬§)6|€ ý! ¤BÅÍDD&ií+=vK)sJ©ÀG Ãà L:ºTÛ÷3¼q‡/l©ë¨mö¿îîîô «&%U kZJúSCuÐ   ÀŠ4ýš&bÃ\˜*Ÿ°yÇ–Á×½l÷§Î”|¤££ð,Ë’ÍfÓ´3®ë¾ÜqÜE!Äqf6"À×óZÿoàÂþñöm£ÿýãû;s¸T*~NJ9P«ÕÐß?€‘‘M0MårÍfãUBÐŒÖ|2òd›¯ ÛÌÀ†¸ÐÀ·LIÛ¶måÅâRëùÎd2yM6ß‘;vüØÛææ^¡µ¢ÁÁAÞ´i9Žƒññq”Je! ­ùAD´¿¦°A€ Dò•f½X\Jôv&®íÈH»ÒP?/.•¿vìÈñ«êµÚMMÇ¥~%×uqâÄ ,,,‚ "03Gð¢ü¯áÆI@Ï#n<~ýàÛ&Ql*tô|íæ÷è¯ø¶i¼¼Z«¡£³À###ÆÇÇQ,.Á0$¤”qaî‰ÀDÌ$¬nh€óìñ+xl/¤i½dÓ¦ÑK†û3ó3sŸ±Öž@klݲ…‰ÆÇÇ177à Zå7VµæÇ# @‘)ðcW°–/°A€ó~Kòͤ…ß¹r÷ŽOßüÞ·?õÓ»úòØØQx®û_3ÙìP©ZAOw7úûû1==Ó§OC‚”2|“ˆZëýÌ8p#2¸mˆ›‚ZÀùÌ÷D`\nšÆô.»ôÒï¨ ÖïÎfs(U+ô÷S¹\ÆÔÔ˜ÃÐÌÌÌ$@Zk>¬ïÔ}"ÂUbufpͤßÎo¼p=u_¥æu~å|ãÕ†¿¿{÷î«MÛÆØñc"S““p†!ÁÌR Çñ0"‚ÅÌôlY¸ñ8?êÌÞîÂ|¥æüää©É~ßó~o÷K_2448„‡q4::Š……LÏÌ„/&‚i° ³fÚ½r÷eF¾ÐY››_¨h¨GW-úÚàDf =؈Γô3a&Ï-”lü§{öì¡Í›·`znóóó488¥ææçá RJ˜Ò€ q¢é¸{ËU¯aÛÉmý}]Ñ­oFvß‹Ù}µ–íß0ççÁ1 üÀwŒttäoÚyùåÙ¡Á!½0?/N>D"B¡€Ù¹9T*X†D>•‚i™sSs‹GÒ}àGöO™†õiØ›S¶…™Å2@Nµ Ó³³(•Ë~Óõ÷kæD6eôxŽfrfqLkÝRý-ðKmê¿Ó gw­Ÿ”Øx<·à›¦¡5¹J+øo¿ÿ¾÷ýäÞC_|z|¡W&¶÷pÙñhÉ3ШհX\hÔÍ1Çóæ³F‰ÄÈkà(€"j0s%F€Ö÷Õ5¢€µ°‘ ~>ÀïïÊ´ÓKõ¾ð™?øÈcÇþlz¾Ú H€Û¨S!•†R.¦ËÅóÅòϼ9è&“Eš òÁÜdæj›Ý_KòƒuâÞÈ<àtå1·X*ù¯ßóáŸ=rôóÅ¥F'ˆt:aˆŽ´¥JÂÄ5—.-–Šß›šçI]aZˆ@õ p¢P¯~-ö}ÜëwÛ<ÿs6ƒnà×àíÛ–‚¿ þç÷ퟸÿñc_˜œ«$mÀD:e¡æøœH$hó`¡2µ°øÝ'g&ä03êX©ê9¡¼z\òÛÕ~»ç¿áüºÁ7 ‰¤I¢Ò øÏÓ'›ú‰é’mJ S €Ö,…A—mé«.5ßüú]O‚¤Ö¬"5ÞÊì5bW=fç[À·‡|¿øxŽÁ·L ÏW€ÿô±7þΡãÓ_š˜­Ø‚–) ™¡4Á4 lꮕœæ·þþ§Oî&ÀàðµnàXhñãÀmªÿÁß Às~*a¡áx"ð‘7|òé3<½XË€)”ÖÐH$llêr—õoþýO÷= ™ã 5žá«·©ù8è~›Ô?+ð7òÏ øèîÈR¹Öf&ã̃ÿæÐ±™?š)6ÒD€) ÒÐLHÚ¶ wÃeuÛWôóǤֿ²Fv¯Þ&ýkÙûgþ†ø€¿c¤—ŽM̵ÀÿáwÞôoþOs¥ AJÒ´f$l ›:á±þþWüø#7ÍH'&õ•˜§ß>.õñO¯ãíoŒ‡ÿÚÁßÔKG'æ(ßøýôúÿãécÓ°Xq ¤T‚µô¥4–‰¡¾|Òw|ûgûô=ß&Àäð[•¼J,«×ˆIýzõ}þeÁßð~›ßUÈ`±T‚“÷|!óÅ¿øÖ¿?vröß”ª¤!AÐð ­5',“z;´–¸ã»÷Ùô->¯tñÄ3{eœÝÍs®æŽu=ø5€ŸLXhF3›ÿü¦Wýçñ3ó¿W­ù )A¬à+fpÒ²¨·§EüÃÛîÛOÓñ ç÷ÚÁoOéžKòeà7LÀ/ ¾m¢éx‘ÚŸ±þÉ×ÿÁééÒïUk‚À‡I&3)Û¤Þž`ÐÞÛïÙwÓñMŸmó«ëdõâ)Ýõ :¿Ò¸÷FCȳД=…4š®OóCö'Þñ®ÿkb²øï v2…î¾€4)ÛDwg¦-ºëÑÃ{ë W°±ç×±v>¿•žþgsÖlíÚ À¯üKFzi¾T'ªñП>õ®ñǓӥÏzÃõ=¶Iô Ž@šl)БOÃHÈ'î|äÐ]3‹U´9|çjä8g3çsúÇm<Î}®ºdHüüÈ$~ö‡]ÿçŸÝþ_ON?¾ÖZJ)AÂ`K0å2IØ)kß=ûÿðôÌR3¾~ a?î­•Ï{ö¼A€çü][ûéÀ‰ ¸ãóŸúîOÿü©ÉâÍZ^ XiMB(¥Ø"PggfÂ|rï¾£wL/T\6æUIžrD€¸ä·jøÖžãã ð<;|}š]ª€àÖÿçc÷ï}ê‹ÓK7*/@ÝqX¦†z¦A…,LÛ|ê§}of±âH‘µk×ÚB½¥uÀ?gÇsýØðÖóö-cü‡¿ò©¾ïþlÿ_œš.ݨýl˜Ü34B‰T¾°-å²i¤2‰±‡øQ~Šó[W=¾w>À¡€žÅõ iÄ„eRÓ @ðã/|´ïÏÿþῘœ-¿K0Ðp=V‚úG6ðl˜¤)“I!I½ÿ±œ˜\¬"Ìï+^Û¯àìܾs>Á¿ØM@T>ÇßÃm¯á5~ycFoGFÌE’û}¤ïkw>ñ¥ùbã½êŽÇ¾ÒdHJ$LXÈäR';2qû‘Ss¥üxa§½i3^á‹Ûü_»Ã÷BÐñí˜ñçVÆ¡eìmWüï>k‰âÎÑÞeð¿õ‡ÿhðÞñÄ—gëï5˜Qm4ÙSФ`Ö°´dÂB:›žxüè™Dà§cÞ~«{§‹óã½{Þùÿb"À²´ !)“NÇ,´TgëFCHiD™Ns 2´kÚµµ_>v¾ó_>ºõ;wí»ev¡rcBJxLš…I"ÜÚˆ´m"Í"•IMî?>ùƒ§ÇgŠmàÇ“<å6µ¿ÞÜÞó>pô!ˆÃîHºîš«ŒWlOË'ŽÎÑ’€•²€ftC}f66t&!-鸮hÓ«|ƒ‘þqôô¾üÙ›wÝyÿáÿ19[ºÁ6$jŽË]Ã[ÅÀð&”ægaKÛN ³+;wøôì÷ö=3zûБÚWô⪿†•†ŽVY÷y õ.V`e+²aÓ5Wl–ìóÀÜòªÁw]=|ÅÖûÊBBŒÚe”†®»ª4]jž<:¹ðôÝw?yº8@æÒI«îxRª½yB§l ×gÞþå»_úȉÿ~zj隤)á*æjÓ¥\¡é„fyÂ01Ð×¹xº¸ôýŸ=zä4€DçÇÇ´ÊmW½ üÈ × šD„|g·(-΀BÏÕÝŸz×Λw X7õ%õu…gsI–i€™àøW£â‰¥‰¢÷ð‡gïüÎí÷Ü T'XRJ(¥–§e„ ¥5kÿò‰×ïzêøâßLΖ¯J¾f®4]"$kX‚`%Sèë-,Í–+߿둱SQœ¯˜¹~«Kº-»ßÀÚ<ç}ï…J²L“6 öÐñSS¿þï{ù[¯ÈýûK;‚· g¹$ÌTdg@F`´öž‡bÝÃÄ’âƒSûîxìÄ·öÞ}÷]8¦Â8К}‹ÿño½rìØä—ÎÌU¯Kš­¹Üp‰Y#i°Az{ó¥ùJõŽ?2v@‚…z-{_j³ûqoß»ÐÀ¿P}’Rbt°WŸ˜RpÓ~ë-ï¸<õ××ö×îè2é€èÜ Êƒ’] ;Ø9°Û•FÚÌ w˜£Û†º^7¸iËС3å·QYbfÁ @õï¸æš33¥?·»F¯M•r‰«ŽOJ)¤,¶!AÒ@Ow¡ýæ¾[®íó¯èíȽ;ÙHæ ÊÐ ÌàH£²+ÖAèÞ6)#MBº;©iS‡Ý1ÜßóÊ\Ï ïèMv4ªõÏÎ-Õ÷$M‰rqÊUb¹„Ëð™0<Ð8×í÷‹>æ³8Û½ýµjúøˆˆ”ÒË«SÿÝ'^ó‡/Ô7w˜=;X$2Ä~ÐÑýäX_ë埉[?ëp‘¢™ m¤8e ºl0c’Æu??pòµS Ãé„A‚«ŽOVÈ%ml¿t'ËDšÒÒ÷µ¤»¾u÷“ÁËÝSím\å˜í_ü Ææ_¨‰ Š/k‚wüæÞ¸³›?ÖŸVù!v†ØkB+fëPÒYà@•‰Á:Ô ZµŠžc@Z”îèE¥îàG{Á±S3©l2)$—›)ftfR°LÉUÇ£¡¾N? ý³ïìÝwˆ5¬ès9± _¥Mõ·«ý |àÂè $`fÊò²\*û’× '?=šõsÉL”ê"øËªut/ ^ɇ‹ö£üŽ !ÁZƒr ÂÔ©|îþw=r=¹´òQñb"t¤D\÷åª ÁxmöÞT÷Ì Ë40>SÄØÄ<æJu( ¤Ò B i¢Q«áþ‡öá©£“èïÊ!“²Q®»pü)C “´0]j §#‡Ïüö xåžm¨Ö]˜’Qö_þÞ“ÓzË­wjŠˆÊÌ<`@+­Üí]¼åáŒÆs |+ž_Vù̮㮪z`kï[Þ¾ûêk·Þ¾½‹nÞœó{{Ó #Ý J÷‚¤A:ð¢x>º›ئ„(ž˜Å¡ñ9T]…Ît[ºSÈX ;•e'ñÕ۞ƾ±3ìÉ!ŸNÀñ˜†‘Û(7\¤SI|úæWáU»· Rwàù ¾w¦,zÏ«.xzüú+îøé}S̬¢0PãìYü_i2÷b'À2ðB²-‹šŽƒèF­:ý×mzý•;v ¦®ÙÔa½z8G×õ§Two°ìtª”Ìtà¯H}H)HIðÝ:ŽLÌãÀÄc[o› Ó§±÷ØiL.Õa #[ÀÂ’ƒM°D¸•Ã6,A¨6\ôvåqó¯Â WmE­éÁ„ 0@MÇç}iúÍ×í~éÁcG'&&æ )¥ÏÔ\J¸e5~Eð)ŸÏ‡‡:NÜû5÷¼ö]»®Ýœ¹n0#®êN‰]=Y9ÜaëᜩPHIXv ÊÊ3ìÒ\.æ„iÝx’¬|èZ Óó‹82ÙDÓ¶õ¦1’UøÞ}â‡?Ã\)€† C` +‡Ñ¾~ø®€ö}4ݵ¦‡lÂDg!‹÷¼nÞùêËPw|¸¾†!v‚¯@–©põöûͯxÉ®¯LLöƒ †°µ¬u‹\ƒ 噼Æ/ ¾H‘ï4¨\.¯ßÃà'Þ:òæ-¼m ‹k²b c(+i)›`Û)•TÚL“6’B IÄZ{+™$å6 jóp›5L-)T|BgÆÄæ®nÝû¾öÓƒð‚pÿNÒN„>i,–}hÌbKoœ@`¦XƒfFowÙ„±3(_½RJˆ0›´ ¥ Àõº3&®Û¹iëmýýCs33³NõZX9‹·ý(¶‹R¿ŒÔïÙ³GöÑ ÝùDÃ`\ÿæ›w½z{îÆÍ}ÓHŽwõ$i‹`'0íÈJAK,,°R˹znK• @kx%õEí¡fë€Û‡ºqàÔIÜþà!8 …€Xp}`¤fë`¥0ÜÓ‡TÊ‘@Ʋñèþ“xR¼ùå—à%;‡Q©¹ e„ŸCiB’4v 2/»tóÖ;gfŽrö%£ðÏŽ…í‡2颫«VWïÚ"züÉ_sÃ;¯xãîžoÍóod‚áž4#•°a¥ó D0ÓIhf,ûP:óó»W¬£ü½.¼ê"T³J'ê>ÁÑ©T 鄉û÷ÆREÁšmÁ0 ~Àp<)Û@±êÁ2qýžØ2Ї{ïÞbq é NO-b÷å›"ÚqTˆ41­Ñ“Oà’Ѿá;A—6‚Ø‘0£( NýB4€r¹L¯B=~È©Oüxé€ü½-9oç`H$“™ˆt'„aƒ`åÊ«|¬ZUœÈÞ¶ä>’z"n~eÚo 0ù#-8‚2É–JeLÎ-Xf GC±†e˜`0<߃ “‘Àìb›ºmÜpí%xâ±£ÈA‚¼ó‹Uhf1˜ L+$ J12–ÄPW®ÓN¦òn³n· Õvå|Á˜@™t •Jz^ÛýûÜòïîÕŸÜšsË$!rÙ^ë­Ê]X–byjÅt¼bëÁ€A} ~uZû 0ÒÊ@¦»ÀŽ Í‹0 ‚ë9À¶m( ZÁ4L€ß÷!@0 Z‡jÝWË%¸®d6C ° Rm@)@EhÅKÓÒ`dSv*aÛéuŸ7Ð8{Réâ'@"‘@­Þ *·ç­Ãÿúµ_¼ª×¿qK^Ã. @tŒÀ°SÐʇò@)0«X³†Ž´üJm„ZE!ÁZ!¨.¯-F•>€™ÌÃÊvR &1”Ö0  ;  ø0  ‚çûa}À‘3G`` ئ Ps}BÀP NÓC¨V1z²V‰#M %I!…¾å8{ÎࢋÄ39}#ý]"4Þ¯(|æ†Ï_Ýãܸ­“`\¦v ÚkBûX)hÖ!ؼºKgü–Ú´ à•gáÕ—k|Bš0s=0³]`A€ò‘±¦D±Ü@À&º °öa[ IP"‚"ìýRh4Ý&òiÂH/°þ÷v龬۹i¤@0®ÐfǨ`¥ ¼´À:rô"éçVg¯ÜXjÝYa@ùü¥)Í2ˆ"i³’° ý0R9€4X:26$sÅ*|ExÙ¶MÈ¥l€؆‚m ˜İM S*<س};·mƱSs˜/CA €(ú§@±æalbnIùŽ@àÜcçÖõêÕ*Á»ozÿ«/ïÆg7g<ØÝ[Øìì9лìÙ³æÕšŒÀAa“&$$T³·<É>`H( ˜©™Îð8t,ëdË(×\,–î0±Te,V›˜M¤ñ†«wÜ{à–j.fp ¡4#i/¿´zË«±mû6Üñ“}xôÉ“R„ÅzS ·# )ÂÙ,ˆ·ÊRœš¯`ߨéy¬ÉÎ[¹÷Ù ìËïÈRq©ª€®Ìµ›SŸI7{ÓÙ<Ì®-íCX+h%*rú–s)‹­#ëNªYSšI$%XHå:Àf:ü]µâ v&04Ü Ã4P­»85]ÄØ‰ÌO-"F>€ÏŒ@3,¥0:Ô…¾ž<|?Xvü4‚42 Ç<|õ®ýãcGŽ 4sk={|c·z!a•BÖZÀ~sû[:ÁÛ I‚™í‘Dà:·:{´ì8­¨ÿe>‘f÷Ê3€ LZ80¾„Ûî?[ íâ®{ŸÄäôö\2€­C°6ŠXª ßsái†iJ’àx†vmÊb|®ŽÛ:€C‡&pРˈàk¦Ò`ò B Z‡;Ù=–¸bçÒIµ†·ÜgjŒdÂÄ鲯ßÜuhþÖ?0 ND^¬+8¾¼Ù?‡)¸è@Qé}™ £9ñš¬áŽX–#‘C RÿˆyøíݺBH(··4^XjÕÛw\‚×^ïãgì‡(Ø&ðÔÓ'pj¾ŽBÚÂî­xÉhU×…«–aÀ”_…f6D¹æà·=†§Ÿ>tÚEu­4’DÈÚ&„ðp™!‰ ™¡¼]=9\û’03”Ò¤l Û²06çã–»Íßòí»öAyKøñšÚd·‚]üN`4–…­{nêLª«ò¶†‘Hƒ :p£ÔîÊbŽ·H@‘Ãç4à–§ }‚0-Pºù<>úîë‘0~tï~kØ–…Dçd¾Oœ8 vkØ>Ò›Bð|Í”B.e¡Òðð¥o<нžD:eÂ4dè¡I&3$3˜©4lšÂhßQ3~ãÊ­¸d´Ž« ˆ9e›ä(‰ûÇÊü×?xbüŽŸÞ÷4Â~.3Çwû«ô¢uµbØ][º7§ Œ¤L‚0á_§ tèùs4ת¤qìï:ðà•§¡¼&ˆ„iÁÊ÷ÁL¦Po4MZøàÛ®ƒ Â~ösdŒ’Iôv¦ÑLïÆþ3c²„Ë7w¡î*¨@Á6$jÍõû±÷±“°26`ÐJ! ˜¡4…€` É €+–Þ|o~å¥0Måû°-‹Ïy¸ã±ã¥[÷>yêÈÑc'±ÒêÝZøYïL^HC@¶ò–$K€֡·¿¬ÀQ´¦W ; @J°RpK³Pn=ÌðIV®7LˆÕ†‹\ÊÂûßr à{{÷afr‰„Bvø2¯L‚Ʊ}(%Òé~úðIÜ÷ÐQ¤LË2 •†` :‘¤[*Òv4˜áº^ó;±ë²A@k8ÚÀ݃ÿïÎÇ'î}ð‰q@•cv¾Ž³×¼Ä»×:œñ¢m ;+“¥´"fÌ‚¯T4”©A:LŸr”3v ·2¿Y‰€|ag U>QØzUm8Ȥ’xßwC5˸uïA˜‰4F7 £:7«0ˆ“~öÜ":ð•Æžݸ|¤OÎ!„Ÿu!`F"ãQDH´‚w!T›ØÖÃ{ÞôRte˜,ùøæCgþͽG¦''&#@ã í“¿ågPÿ¸X5€Œ •æÔã[¶u'ß[°U:•°`$sQzV­ØzŽwñ„ðkExÕy03„°s½0S0Gà/x <-»Ù9e\>Ú@k<ôØA˜©,º»{Pš:Nu·;`øUŒt'°u¸<ò$ÌL]=½˜9ò$*•*ú:RèËÛ¨5\¶¹Û;pøàN—ª Û†I@R©Ö4 ,J•:6÷uáûô›pùö~¸ŠD´¼I³shkv oíéKúyÓ0!¬Ô²HËÅ“0õë,Í hVAD0ì4«`e¦“¢<+µ"”[ ÷ùPØú(†mìÚÚ ! >9† Q…Ô17;‹@$°}Sl3LíÚÞj‚±YLkè°$tä@4ë éþÕ'ß„7¼òRœœsð×?>4ë÷Œ¨Qh|¡ã€Åèk|»g³Íñ;×PÈEíĵ€8:¶X|é®Í#ýYÚ’d"­¨sD^unu!*ó[HvôCšÉ°<»7„pµRÐX‚jV#ðZ ("‚(RàŠ-½ hì{z ©„‰ùRšL vgÜ@aûænŒ vâÀÓÓ(.V‘LÚÐB Rn eIüîGnÀoÞ × p÷áEýåïÜ;V­T°z£g)F€¸ä?ÓÆ‹üv ÔèB2³ 4J©|_b´?ÿönÓ•dØv*4„”œ:œÒ Xáöì|¬TnÕGÍBàT4JáЉÈ!¤6h‚pù–^”Øb!œÿçǧ«Èç2ØÒŸ…ë+x¾ÆÎ­½êÂSOOãÔBÕfyÓ¿øÈkðþw^Ö“¥ß¸ûðì==qgïøYŠI}üÀƵNäVm _ôå`Ù&ý‚™…”Â`fyb|¾¹ëŠ—ô¤i[Š\ˆD6”\X+4—ZÉ‚•é€íŠúTäóE]~"òðë‹¡ ‰ÞcÿÐ ´žó”†!$.ßܰÿø<Waçæ.\{ùri;Ü­¾¯qéæ^ P.»îëÄÇßÿ Üü–=ð=…@k@+]€‘Ȭ¬oCÔˆ°Ðk”CM°cWóJÛXkáS¼+—þ¬(È åÉÝ\Ú†ï‡ÿF{çEæ‡FÓ{ EØ%|f¡®œ*h 7Rï˜Ô×±ö‘m/xðãà5HÔM×4¤€»xê«?=ð‹rÒñ”~-Ų’°Ò¼L€°ZvùN^£²¬`–±o€Ã¤rœ†aæÐ š®¿Òl² ZE„VX)q  gª™…Rá9¿q4ÛB¼vOÿþz±à+Í9>öÔÃß}xâ¿©ØI –°2 iA« ´ùZ‡9DP ¯V³G…š–dŸEæer,Oµ¢Áø…ë- @ À ÓÅš[©Vqjåü6ð}¬½Øñ ~;Ú-Q­µ'ˆ|¸ÿÞ½wüàÉÙ¿¯Y0 `Y݃Ca£¥_/C^+»S÷ÜšZûUA5Çîxë‡UG<=sçuèÄ„çL-ÔšP~#ÒVvû´¤~­æŽ‹¶ÃçWÕq¼ØÀíÎ`ts¼èèÓÀâßÝ~Ï-œt›oš@cSgŽ6tªÀ‡W¯,wÛ.3ª¥ê£g–grxåçxr–jXU\ýšp¥L8ÙÙê`&›.;c'Ï,F‡·ð/ŠPïÙj€³œÁèFyQÙT9Õâ©¿ýþCùÐiudÉ5€ÆiÏa€à7+ÐDiÞe5:æ„ɃÖ-!N”ŽŠ |–FÀªç‰Ó T='ŽÏ•æçŠ-&¦ò=œ}fŸ~1I~{à™Lê²DH)„!ɬUÊsO©œÚtYoZt%¸A~ 8pëÔ±¦Ø¸5(–lˆâ}^~ž–Z¯[ζþoô•(öžtÖû A°M&ëú–ï?ztn~a +{~ãMñ¿k•w_ôXÓÛZ¤`¶šµòÜØ¬75<<¼«/ƒ¼Ô t¸*íÀÇZŽ[?S< ¡.Z¿×ZÖ#ÇòÒˆ¨–ÐbJë9° Ÿ%þᑉò·xÿa€[ žöEÏíçöê£x&¬w1‡Y>ÁÌV­¼8{²¤æúúv äÌlJ*(^‘^´k匭"B;Ø+@Ç~G¬z” li–¤màÈœ‡¯|ï±c“ÓÓ§qöY~­œ¼Çÿ‚ßë!˜€U)cfn‰ºYZœŸkÐ:¦+(^ûÇj3° øŠŠ_y µù 1D$ÈÚÓ5‰¯Üypê'÷=|4VÜi¿Ëý¿¨Õÿ³!À¹và,G`6–Š‹³cS•Sùîþ-]ÙTWÁÒÄPÐX±Õ+N|L#Ð꞊ûÑôîJ°<¤m‡mü¯ÏÔnùîÝOùž[jsüJmÒßÀúÍàY<–3g̬¥”ÌÌF­Rž}ððÌÑd¾·«#ŸÙÔ•+ù´JÚÕfñð«šFâRO hl¬…E·=¹àüÉW²¯²´0#Â|E¼ã'N€øñíþ‹1û÷«€Öp —o3sÔXj°ß,=~àȘ›èB!_ØÚ6̤¡iÙ$Z&ÁŠêoí UäˆG +ô˜Œýøp5ø“oÜs`úÌÄDu^=~¬[½Íû¿(vºÐ4­c–Ç£"ð$ÀΑc'ŽžªÈ…d¾s¨#›È ‰šWB¸˜_¿lkY•G ÐáË$$<‘ ;ÕüÏýýý;z€KDufŽ7{®u¢g<öÑJÿ¯bèNáò ´ž™>õð±ù må’v23”OÛ”µ’Vê+N^»³¸âJ’!•´°à%ð­ŸÏ×>÷¿î90~òø8VvµTÿzçú­åùo˜€ç "8ëŠ$Ü𛵥}‡Ž/éO¦2ÂLv§l i‹`ÉpÕ»Œ@.z”‚`„„A°- .l<1­ð·{OÎüå·öî+ÎÏL®~ +§{´¿:ήý¿¨Á?—$?›×·O‡t[«UÓ2¦iä|?HHAXƒ/{ée»®»bôʃÙ-#V¦?k i0LRa¸L$ÊpbÁÓ_\üÑ£c‡›T%,SS#ZäWý‹xæ#]ñb7ô¾G|ybk›¦…pÅz@ZJ™7$å\/°Cbˆîî¾¾á˶ o½d¸kd 3ÙÕ•MÒ Ã6 A~À\ª¹Í©b½rlr©¼ÿè™ùÓ§'‹@ÐàK)<¥¸pgÏóqöR‡õV»`ƒÏ (F‚vm&¢ 3§¢ç’ráeær]¹L*k™Òv½óÅ’ãÕK ¬ô&´âv+k[â‡:Vc¦ Ýëoxîß+ndLØ1mŠ®tìçdŒ(­¥Ìñ…Ì-°Vº{Z½–ˆ_qð=¬>ðiüèñ\žÚ¾%#.i:v1üfŒñÚ Ð±ÕÐïðmi‚ø2'7þ‹ªÙó| N ¾+~µ½mI°#AK;Ä¥?NB[ŸbôúVwoKÒã Ÿëõøóø¿>¬%YíW¼×°¥ ¬¸ö•ìñÍXçµíMžñFÏe§ïùòÎ&"æŠ6ÿÀˆn¶=?×ÑAÌÁk?Ĺ=Ößÿy&@»sØ)ÄɰÖÕ¾?îO¨5ÀŽƒþ‚šã¿˜ °ž6 6"´ÂÐ~ËZ$ˆ“!ú‹²ÅûB'@û¿·–yXïÔq¬¨n3 ë©ú ð/ ¬G„õ4ÄZŸQ¯ô3Œ m<.4¬÷ïÓ/øùÎ%áÀ_DøE?Ë3o€þ À³ý\ ?ÿk¬À°ðIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackCurveNone.png0000644000000000000000000000013215101070305020126 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackCurveNone.png0000644000175000001440000000120415101070305020113 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®Îé>IDAT8ËÍ’ËOQÆÏ—ƒ2m­‚BERؘh0Aƒ/bLºÀ.»5®HŒ 6îuÓÄ„.lîÜú¸2Ä "Ê7Uä¡0rÛéŒÓ¹w¦÷θ±Ä¤²Öour’ï—“ó}ÿZè/;ibbâ¤eÙCãË<à<΂ðajjjØž€D"Ñ6>>þ¤ÎQ²­³gGŠÿâº5ï{q>Q5VŽ ôB¡ðØqœJ •JÉÙlöÅ¡£'âCWo®[DZš[åÆì²gl˜ÂNÓëÚÆË硵|>ÿ q‰ÐŒŒŒ €¨ö^¹vk­3.Ÿ:ÚÊáþU”Uxë\ªªªzÏqœ3étº¿áÛ˜fõNWÏ©o‹Û6š1 ZÅv2Œqà<€ —ËY­­­ºmÛ×wÖ8çƒþ¾Ž%}#ØlŠëÛV ,ÿì*EĦˆÒ:xš¦½'„Üh0ÎÁ£®?·É°¬€ì3 ¦‹\Ã¬Š 5Ÿ ”çÉd3óÎ7ï÷}ÿ¡÷Ià 7P€ðÆ_ ‡€;À£l6ûÒ²,Ͷ횷öæÓ‹zF à™aß\×UëëëûÀ«d2ùbzzÚ2@ÌÛ{%§Ï ƒÀ“ùùù¢mÛJ)¥ …Â1_]]ýÒl6; à1]«m@?pñÆÃÉÉɵjµÚQ¦i¶€|,ûhšfóàà “L&óÀ=.±6 Ü×4íM*•Z‰ÇãïÞ—Ëå–òašf;‰¬---}N§ÓßÇQ¹\î«õÍ á»333+–e¹J)eY–;;;»­Îašf[×õr«Õrs¹œU*•N666~Ï=;Î’sJl||\F£ ŠÑÑÑ‘ *•Jmss³=77ÞÚÚr'&&Âtxf…_8äºn¿_@)ä™{{{Îðð°¬ÕjjhhH}ø’÷Û¹!¥D"°üÂ)eû—ÓBt‚DÈØØXÿáá¡Ò4 ˲\ÀÁ×$~á£ÝÝÝJ½^Wõz]U«Õý ht]g2™¾b±hOMMÉ8‚¹Pno¥”Ë¥Ré8¨Ü?¥ÓémÇq”aW–[Pƒ<¸Žñ_ÐSK7žZ:ˆký„΢kËÓl6û!‘H¼Rt-‹ÓÍÍÑ^C—žÀ p 4ßúýÅÿ&?‹e<»XG"õIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/helpShortcuts.png0000644000000000000000000000013215101070305020044 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057222416 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/helpShortcuts.png0000644000175000001440000000150315101070305020033 0ustar00rncbcusers‰PNG  IHDRÄ´l; IDAT8ËÕ•ÁK+WÆ÷Þ™I¢¢ ˆÁìÏ)BÀ]©;ÿ]ºp£DqÕ¸,t§A\èªÐ¯  ‚•ЄÔÄ"d43sçvQçòRK—oàÂeî9ß9÷û¾3ßÚ#FlnnîïïRJå…c†ÎµÖoJ©~ÿ_ççç>??7½^Ïôz=ÓívM§Ó±ëúúÚ¬¬¬|YZZÿ·|çìOÙl­5B´Ö â8 N“J¥>ù¾?ôÿ™,?äI‚ ¢!)†á‡t~ì8q³³³Ãóó³}oŒ!Žc„)Ñh`!®ëâ8Åb¥ZkŒ1cR"„ îüðää$„aH†h­-ÏR޾°°½½-]×<>>rrr¢Z­¾ï£”Âu]2™Œ½…RŠwеµ5•6›Íøìì̈ÕÕÕÏÇÇǬ¯¯_‹ÅbÒí`0ÈŽ»ï{*• Æ.//Qêoœ——ãºî“1!ÕjõÏýýýJ>Ÿ/ €¹¹¹Ó£££fffˆãØò àû>{{{4›Mvww­x777  …QQ©T~©ÕjkÎ;Wa"¥DkMEh­­½Òé4RJ¢( ÞÞÞ¸½½ell cL"ðä+’!H"é*Š"‚ °B !ˆã˜»»;J¥½^F£Áëë«Íµâ !PJqzzJ§Ó!–——™µÔH)QJá8OOO\]]1==M¿ß§Z­’ËåH¸¶'Iõz Èår8Žc;ô<)%žç¡”¢ÛíR.—9<<$ C¦¦¦Râºî°Ý¤”d2¶¶¶l‡_¯d <ÏCÁÂÂAP.— ÃÐRçyÞ0°‚v»M¿ß·>uÇî3™ B¤”Ôëu&&&†b¾Îö<ϯÕjÖø —J)KS»Ý Q­V½T*5“}×*°_¦ÅÅÅï...>ßQÏóül6ûk«ÕÊK#¾”J¥ëF£ñÛ7÷Ëã/›è„4Dì7ñIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemRpns.png0000644000000000000000000000013215101070305016776 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemRpns.png0000644000175000001440000000104515101070305016766 0ustar00rncbcusers‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIMEÕ 7¤öµ²IDAT8Ë¥“=hSQÇ7-AJ¨‚„Ö*:8Ð)¸¹º¹ØÉáÝú ¢»—‚]\|Y\qì$*(Ømá"6бh´¶•4_}yïïð}O“ñÀáÜ{¹ÿçžsü§™ØºïqgXÞï³úÃüv OkŸë#££—ñ=A µÛ¿½²^U†zû®\Íd2—€c»Z¾tá…£n|S^SÓ—jÍ@¥×Þf:žFúj-ic;ò­š4ï}ÐöŽ´Y—~4|=}>÷Ís&.JÅ7~W_YR)¸R² ìäîý'LÍ<âáìÙ#ã‡ÀäúuÁ®Wu¯Ö„°C®7ZT¾oQo4Iîg,;D.{`ävEqZµ×-wN¹\[°¬>.'Þx.7¨¨sTLÔ`¹"•V¤ùåÈoL¹êg€óW~·—,Ó']n.Z>~)3ƒµ6‘‰ëºý‹xñ¨Ëj%Ч‡'~ ÇéÙº ±3+–ƒCQ|öõžçQ,{B€ÃÃpë„K[Q<;eÐçóù¦‚¤ó=§Ð ãEœëxÜdŒy¹Ûÿÿ Ä.J PBIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/viewZoomReset.png0000644000000000000000000000013215101070305020017 xustar0030 mtime=1761898693.060267569 30 atime=1761898693.059267566 30 ctime=1761898693.060267569 qtractor-1.5.9/src/images/viewZoomReset.png0000644000175000001440000000250415101070305020010 0ustar00rncbcusers‰PNG  IHDRÄ´l;sBIT|dˆ pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<ÁIDAT8•]LSgÇç´uØÒŒïVœBEe3›-–D£&:űyAb23‰ÛÅf¼ÙwKfŒ‰»!™îfK¸%j„ΛJ;†&XB´¥Ê‚ÃZ>ÊWK‘B?hOwÁ0‚[²'9y.ÎûüÎóñŸ#ðï¦Þ £ì×"BÀ0 ÌI ¼(ÈÐ| bÛ¶mÕµµµwïÞ›ŸŸ§—=rÏÚl6w{{{3p’?˜~X lEñèùóç¿=uê+Ãüü¼022Âôô4’$¡ÓéÈÉÉAìvûÜ… ?ü:11qè¢k—2­¹víê¹Ã‡뺻»q8œ ·Û=4::ê_XXH † f³ÉX]]­Ñëõx<=©ºººËccc—€gkµ`=ðÉÙ³g‡ff^¦¯^½’®¬¬||TUÀg@ pzÇŽ¿777¥ž=@˜Eåfgg«©T*CŽyôØl¶û™™™lÞ¼)C­VoZ±¬ÏÉÉVE"³„Ãá  M-µ*‘H0??$IKƒ—€ÉþþþÞh4šV©TèõzÃj0€ÇE¥R)²Jë’$‘N§WLj€"#CÍÂB’d2™\ NË™E‘¸N§cãÆ…,JO¹|(F’¤¥ó‚ü.ßd2í\·N-LOO3111º:ã0ÖÝÝ=¢Ñh¨¬ü G©TV°(%š™™!§AXáYÀ®'Nì ‡Ã<{ÖM$CkƒV«µsv6’޵ˢ8yòd-ð`^ùýþ¹¹¹¹x$´Àžýû÷~üø§oy<=¸Ý/ð—bUŸ4@YYYÙ!N÷öÎï+ŠŠŠ25ÙëõŠ©T*zëÖ­'N§óA4} ˜jjjN744˜Ÿ>ínÞ¼im½St + ZÀl±X¾¶Z›«T*•ØÖæÀh,A­VóâÅ ©½ý)ŸÏŒÇ㉒’’ÜC‡>̳X,o<|øÖÖ;ñÆÆÆŸfgg€AaÔd±X¾¹qã·ª¼¼|ÑçóÑÕÕ…ËåJ˜ÍfÕÖ­¥Bnn.™™™(•Jb±ãããôöþIGGG¸¥¥åçX,vðqA.¿Üb±œ±Z›« EŸÏGgg'½½½Rkk«3 îÛ·o¯Ñh,Òj5¢¨¦¦¦’@`ÒãñÜnº€q ¼6ß«¨¨ø¾©éúÑÂBý2ÔãñH‡ã®Ïç»ÈâžÍôru"0AÙÏÉÃ_^ô{ qtractor-1.5.9/src/images/PaxHeaders/itemMidiClientIn.png0000644000000000000000000000013215101070305020364 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemMidiClientIn.png0000644000175000001440000000064415101070305020360 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ + œ¡3\$IDAT8Ë¥’1jÃ0†¿e<æ<é:@;ôõê)“Wõ"oÈIâDr¹Õjk-ív›N§#ù|þ†„v„ñ”RÏ‘[k16;ŸÏ­ÖÚN§S«”j^X7‰ˆŽãà8ž·uÅNÄA­8„ñ[Yí+ØäFF.nŽ­õh0,K¥Rä©þf<¯´Ö# ˆ“Ÿ΀àœ˜fÃøÀ'`v‰ävßVaéqò SB’«» ƒ8IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemCdUp.png0000644000000000000000000000013215101070305016707 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemCdUp.png0000644000175000001440000000122415101070305016676 0ustar00rncbcusers‰PNG  IHDRóÿa[IDAT8Ë}“ËK”QÆçû¾qnŒ2–]¦D…ʲ0‚6]6]p“ýAA›ˆ6QëVѪE‹ !¨eÔf"¡$.›¬S/xËQ¿9ç;çm1“SžÕY<ïçyÞs`ƒÓóyZN_}"iÔïË•û}²FˆN„Kè›]bt`Š|¾ŸîÛç[[[ýpùÞ9ÞÞÀäBÈѶ>Ìhæ~–E…ñQ^¼èeäù U¸p畜:ÜÌô¢f®Päì±>N[f—ì_ÂÂø(ù|?m»ÈlÚŽït];¡‚Å1omøýLÈü’W•5ÛØLGäóï¸yr/Ù-9ô­—(åñ´wk¾æíPÙeGDzÍ|éïc`p ãÕ²¿~žOCjO†%\<ÓN208¾‹¦=ßYùÀØÄäàë·1ZR$R)ÒõìÈåˆ'SmtÉ0>¿Êr) ~ëNé cEˆ%3ì;x˜L¶Dº–ÀÐ&"0Æ %ã*RïŸûv±4±T±dVÊ£ìÀhƒï)Œu ð¿Wcñ1tä@)`Œ!Ð:B)0‘C¨ðã<îz†ïBŠÈzk1@ä£×EБR(Øe¹)Mi%ÄEŠm›2Xça¬CP„Ú¢M%B)re€&‰ÍcwÝ"ÎZDù(?†O¡­ "è²±RŠÐÀŸ5élUR)iÍXËl¡ÈæÚD•HÁ •OΕ]Î/.#NÊŸ©õÜ]‰"»ÖAyXÖ«w |†»¯«_b_HðIPÅ‹IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/editPaste.png0000644000000000000000000000013215101070305017117 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/editPaste.png0000644000175000001440000000175215101070305017114 0ustar00rncbcusers‰PNG  IHDRÄ´l;sBIT|dˆ pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<gIDAT8•MHkGÇ37¹Æ„úx~ÔOª¯âªuUpi—nZºRhºÁ‹Šnƒ ]Vp ®táJ·~@ ¢ÐRM‘bRÔküˆ6¾Ä&ffÞÂ{c^Œ}ïý g~sæÌ9g–¨ú7aÿlQíœüî.ô(úCUƒ= XÀ+à  ¹Ð3àwà(»s€ú?ï^ÀçÀ'@×üüü·‘H¤Ý²,H$ò###¿j­€ð°\ÔƒãzùÕÌÌÌoétºœJ¥Ôññ±VJ™ZžžêT*¥R©”Z^^>F€@-Pº_ hëëëû¬±±ÑZ]]•{{{BJYkÏÉɉXYY‘WWWr`` øÔ á{÷å­€ß#Ç!³³³óЃ֚££#âñ8‰D­5@C-À÷ìRšžž‘ËåˆD"‹E(‹H)¹¿¿§¥¥…»»;²Ù¬ö¹pó"  ÒßßO¹\æòò’ÃÃC®¯¯ÑZ#„ X,²»»K&“‘À—À6ð'pïÁë‚…|>étšþþ~666ê™Ê©©©ïÃáp¾P(üüSìÁr¹ŒBP*•XXX »»[&“ÉÂáðÛ|>¿êùµ×H)…1¡ÛÞÞ&›Í’Ëåp‡\.@WW—µ¶¶ö#å1J)J¥>Ÿ¥žòÞ¶m¦§§ D£Q¢Ñ(ÍÍÍLNNâ8Žq3¤l´ÖÂq’É$RJ2™ ½½½£³³3,Ëb||Û¶Y\\äöö–B¡P橇TÀ(I)U{{;£££øýþŠ—Á`€††ö÷÷)—Ëh­ …B´µµ!„0BCtS€spp|=<<\ñв,q~~n¶m:::PJqssC&“¡^uVƒËÀß±Xì§X,öÆ•§ðÄÄÄw@«mÛ ÑÔÔTÉš¹¹9Œ1/Vžþö€?Ü ðŒ¿¶mû 5 1;;K©TƒmÛ(¥*a«co²Èc#¯VFq¼Y__¯{l€ÍÍͼ뜮{ª}RKKK?_\\d•R¯yÞpŒÏç{»µµµÆcå•©cTOÞkÒ yꈞ4ðpÜàžøcÀž¬­†›ªÁ;wäs_xRiIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemProperty.png0000644000000000000000000000013215101070305017700 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemProperty.png0000644000175000001440000000027115101070305017670 0ustar00rncbcusers‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIMEÕ +@fH?FIDAT8Ëc`ŒXÄÒè™…ÌaÁ¦âÌ™33±‰›˜˜¤£‹±à²&=UmZv‡1QoÖ08{ö,N?S=GÜÄ 4×ÝIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/formSave.png0000644000000000000000000000013215101070305016757 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/formSave.png0000644000175000001440000000111015101070305016740 0ustar00rncbcusers‰PNG  IHDRóÿasBIT|dˆ pHYsììu85tEXtSoftwarewww.inkscape.org›î<ÅIDAT8•“»ªZA†¿™}‰19nŠÁÓèCˆ…rž@Ò$¥Ï`i“—ÐÆHP°°´ÓÂW°RQ‘ã¥Ù‡íÌžqoIÈ‹ëò¯Ë¬ü†àÿ`Γ,ààvä¼h` ìmï«Õê—b±øÑ²,ã8Îá^öápp”RÖh4úÑét¾/òè{¬T*ŸÊårn³ÙŸ3™N§ôz=‚ ˆBÌÁõÒR©étšx­¢‚p­¦IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemInstrument.png0000644000000000000000000000013215101070305020224 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemInstrument.png0000644000175000001440000000033715101070305020217 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®Îé™IDAT8Ë’Ñ ƒ0 DÏ-Ù…%üí%º‹g»~´F&Ĥpy/gÁUe¼›™`áÖ ÷>ÝSFUéî$Iü5‰çìîkIGVM^yAf†¯ ‘8dßRÙ[DrU]6ÙFI†c¤Õ?)%³VKfp4É—îRRR JÉÞ®î×Ir>IžÀIË Iä?|Do®LÇ.IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackIconPiano2.png0000644000000000000000000000013215101070305020163 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackIconPiano2.png0000644000175000001440000004254715101070305020167 0ustar00rncbcusers‰PNG  IHDR€€Ã>aË IDATxÚì½Y\×u-¸Î9w¾9Tfeͨ I”š"AJ|2IÑ&EÓ,ÓÏ–äIjûYwDwôÇûv„úÙþðo·¯ÛaG¸_[ò‡[rëy %I‘ 1…šçª¬œ3ïxÎé{oâV" $HPƒŒ¸QseæÝ{¯=­½ÁÝÇOÃƒÜæïË;õÄìî½ÿÉ ŸÆØí* ùŠsW~Š,ž ”Räóy’Íf)!„A(%ñã=„N>ì ¹ûø Á½¦i|ßO¾E !„R )#”ïù(SW¯+wàgÀ긗RÎ98çt||<{ìØ±ÂÖÖ–"¥RJH)‰”’ ˜¢($V„ƒ ÿ¹„»ðc>¥ªªÂó¼äžSö /¼0šÉdžÈd2?§ëú…«W¯¾î8ŽÇ9§aJ×uÝ«W¯V}ßwp"¾$ ”»²ù衞RŠ¡¡!loo“X@tll¬ð¥/}é3‹‹‹/¼üòËŒŽŽÉd2ó»»»—‹Åb8<<Ì4MÓÆ’¢(uöìÙ )ÅI+é<¹%¸‹¡ð)¥Èd2h4ݘ¦i}îsŸ›m·ÛŸ\YYùúÖÖÖÃÛÛÛ „@UUPJ¡ë:t]ÇÐÐî¿ÿþÅ0 ÿó·¾õ­I SŸ‹%wà§Àêóù<ªÕjš|ðÁì#<òäüüüo_ºté¡r¹<-„€¦iHƒ®ë¢X,z”ÒW]×}muuu€ ˆ¯´ÅíZý]øˆ…¯( Â0ì ibbÂ~á…fwvv>qñâůîíí=Y.—aš&,Ë’Õj•€º®ãäÉ“‰‰‰—Ö×׿õúë¯ÏhpxüÔÆÀ?h,pî àu]‡ïûiᓟÿùŸÏLMM}jyyùwÏž=û‰­­­1J) ›Íbbb‚xž‡V«Û¶ƒC‡½>88ø£«W¯^¼xñâFœ9hRÊ4üÓøúС» p‡„–e%Q>²žþùãõzý¯¼òʶ··Ÿv‡€ªªÒó<¢( ²Ù,!ÈåráÑ£G_ Ãð;ßùÎw–ø”R5ƲbwJðwà ¾X,RÃ0èÆÆ`üÎïüÎ ß÷O_¾|ù™………ÇÊårAJ BEçœ@»ÝF§ÓÁáÇ]Jék¾ïÿ.\X‹Ñƒ !Dµ÷>z CwàÇlñäĉøÑ~D¨ãããöÃ?|²Ùlþþµkמ¹|ùrŽsÞ­ó+ŠJ)8çR¢^¯£Ùl:§Nzó­·Þú×¹¹¹­X&4åã‘Ê÷{¯ƒ7 ø(…ÏÃÀÀÙÛÛ#?úÑ$ãÅ_¼_JùäÒÒÒSsssOÖj5F)í \Q˜¦‰v» ß÷aYFFFÜR©´²···¼°°På!R‘~’î©+ì úî"ÀÓêcDUUìííÊñãÇíS§N\[[ûÚæææ666L×uÁ!a"Q„f³‰ÕÙÙYçá‡~§Z­µZm,—Ëe«Õª“²î´àÓQxÈ» ð ?nÈ)%âú=(ßøÆ7îmµZÏ¿öÚkO¯®®>†¡Á9O~BPJ!„€çyMÓpÿý÷û“““gßxã7,˺Ç4Í¢®ëZÊêƒ8åK_~ øPÖWÞ§åçr9Ôëõî={ê©§¦§§ýà?øJ«ÕúåryÀóÉÏ+•J‚Ê /¼03::úâåË—áêÕ«÷7Á$ºw„®Òø¾ßõûRJAJ©, WΞ=ûæüü|QïŸêºUUy~O•ÏïcùüNAÿ]è#|Ó4aÛ6Êå2*• Ð'Ÿ|rxvvöøÂÂÂg/]ºôÕÍÍÍÑN§BH7¿AÚ¹ñBô~?l6›˲2ãããcÛÛÛ{žç…º®“b±˜ÙÛÛË”Ëe‘zo¹7mñÉçø0ÖÜ%„tIš¦azz«««ÉÍT?ÿùÏO‹Å/_ºté¼téÒ3[[[¥ öùù›p)A)íæþI ¥¤r£££³ÓÓÓcŠ¢8»»»ÕÙÙÙ‘#GîÑuÝ]ZZšãœ×°¿îŸ(CºæÜ6пw INoÛ6ÇÁÞÞž@¾øÅ/?òÈ#ŸÜÜÜüÒ»ï¾û;KKK÷µZ-ƒR*“¸ oõ%~ù')_ò|žçé{{{öææfIUUò¬¥N§³“Íf‡ƒ @¹\>ßétö!I,ÎÿùyÿÝ,àƒF÷†a`tt”.--%¥Vöâ‹/Žxž÷+çÏŸÿÚ•+WŽxž§%9½â–µwJ)!ÝJ_$A ¥ív”R Ó0 ùÚÚÚf>Ÿ_7 C7 ƒ÷Xz/ÔË;!ôÏ ÐeØŽŽŽb||œ¼ýöÛ€úüóÏ—t]h}}ý‰åååÏïì삌±nŸ>þƒi‹O‚ÉD!  áðáÃ+žç½víÚµm˲pÎ¥ôŽwî*À-¬>fé­­-²µµENœ8Q|öÙg»|ùò§Þzë­gvwwï㜫 ¤sΡªjÅ…áû{²˜ø)„è  …BëôéÓÛÕjõõ7Þx〶ªªLÓ40Æ~ùýŸànpûe\EQÈÀÀ)—Ë€ñÜsÏ Z–õÌùóçÿÓ¹sçNnooÓÄ'={J)4Mƒ”ò=€1¶/+H„ŸÍf‘ÍfwÎ^¼xqaqqq]JÙÀ¥”PUU(Š¢"jõB½<èýÜU€Û¨äµZ-†!-—ËdlllàW~åWžXXXøÌ™3g>]©TN$5ûÆ%Rvývš¯Ÿ†û$êOÎC<àáááæððð6€+KKKWZ­V @‡H)ý0 ©eYbxxxF×õãkkkËRJA‘ñs‘>¿‹ï§ ÃC† )“0í×~m¸Óé<ñæ›oþ§•••G¶¶¶º¾º'jß÷uòy‚ ªªîƒø$ï×4 ¾ï#“É`ll¬ªªê;õzýúææf% ÃVo…¯Ó鄾ïÏe³Ù¤”÷f³Ù—›Í&'„y«€ã.,x]×áº.IQ³äÄÄDö©§žúôÚÚÚ/_¾|ùÑ­­­û’L ‚n˶Oú¶ï‘8wD÷„ø¾EQP,ë†a,^¹re®ÙlÖRe^@GJéB)¥sõêÕ³Åbñ^)%±, Íf³7ò'=H ï(ÿÖ„oÛ6(¥p]7¹)æoÿöoÕëõOž={öë+++O4 †ß÷áû~ºî÷2º$¥KgIÑ'“É´J¥ÒŠ®ë«•Jei}}½ “*ê8‰¤?BÐjµÚfŒ ˲zûý¹»¾!¦iÂq´Z­ÄjŒ_|ñmÛ'¯_¿þüÜÜÜ'···J)‘ÉdºõúDðº®#‚[*€¢(R"é÷+Š‚  ª*&&&ªªžo4çççka¦«ynÚúSÂw¸¶mAˆ0 ¹¦i»ƒ½ù?¹ÓJ ü¬ >y¨ª ÇqHl9æüÁ|‚sþ[¯¾úêcóóóG‚ ÐbËd<‹RÚµâd(ãVðŸŽÂ0„ªª8tèP«P(,(в²ºººT.—+‚8Ès!ÇW\Bˆ/¥ôE±(¥2ž œ© ò³,üz9çh4€þ›¿ù›³až¼råʯ.,,<¿²²¢I)Á“çœPJ¡( \×…aBt­„ @¢ªª¢P(´‹Åâ2cl¹\./onnVc!û„OJé§­¼Çê:Œ1O×u)„ŽãøDì–²ü—YÀ¾bŽ®ë( ˆ™¸dllLÿÌg>s²Õj}ýÝwß}riii”s®QJe&›0t’n^†]xÇ-\cL …öàààŠ”r©Ùln”ËåJØê})¥[¾—vúcòýn›×÷ý–¢(–e =ztxuuµ†!+i& ü¬ ß²,rüøqR©TÈÊÊ  õ«_=Þl6?qöìÙçÖ×ן¯Õj,©ÅSJIBÇJ§ké^}RâåœG³ØqTp À4 dƒL6w]aú\µZÙ¬T*ß÷½TtïK)½ÈïwuÛ¼RJ@ÎÏÏW¦¦¦þurrò˶m?7‰ZIûï¹´ï UUÉ;ï¼CÐ'žx"“ÍfŸÿÚööö/-..˜Žù÷ð+ðt¤ŸVúgÑG‰@0BaÙ&gŠR?~ìX866¶qîÜùwæW7…éNßcÙnô§Ù=ûx}RJ©(Š ÃÐßÙÙY;tèÂ0Îd2¬ÓéHÒhpp:øoNÒ³vdll «««$)è|å+_9¼³³óÔõë×~iiéιžÔß“¸ …û9‰$ßOÿL Ã4¥mYÕ\>·iæ|¥Rk¬­m4¶¶¶*=ðí¥¢{ï¨O >Í铤BÆ)$©T*RJ)2™ ÙÙÙ‘}ü>ît-@ùi|œ×“V«EVWW%öå/y6›Í>óÖ[oý‡ÅÅŧ›Íf1ï$0LŸ(ARŸOʹé6mš¼iY,˪•¶¦\ÛÞÚÚZ^Z® !œ”àz»n¤¡þ 6ïMQ¾a¤Ýn€4MS¾WÖóoÕÜDÃŽózfÛ¶ý¹Ï}n¦ÕjýÆõë׿zîܹ‘$KüzbɉpÓ<½Ôj–›š:cccÂ4ͪbG×õÅòÎîF¹\®Ç‚ìÌðúX¸ƒƒiÜi>ßA¹}÷côÉ ÿßlà¦!ËØ€}ñ‹_œü¥‹/>977÷X­VJ,9aáJ)÷Á$_§ëöš¦a``ãããÈd2Í|>¿ÁúÊÊòV½Þhƒ@ŒX T' ¡ÔHÈ€@—qÐG <Ï«×övÓ¾¾—Ä™îñ§WÄt·{A€|>O9çˆYȤO HÞ£,ü3©$É­“i±ðÙ}÷Ý7ðøãÏÌÏÏîâÅ‹¿³¾¾>Þjµºõþ„oŸXxº÷ž®×ÀÐÐ:„R©„\. ]zkkkxçwh»Ýå\”e ’"”¥Bשœ F¥Ð ‹«ZF†&,Ëæù|NflTU7–7÷~°¾µ»bz¨¨ª‡—œ"çîÂü®Sßiìï2)NIÃ0P lwÈž’æØÐRÆøôÿsæ•—ßà5›M}}ýêàààg4M»G×õW=Ï (¥”s.>*cU~‚OïϹr劀gŸ}v`zzú¹¹¹'^zé¥çÊåò‰N§3H)…ap]@´…#éÆãðáÃ(‹€N§ƒf³‰……4 4›M”Ëå›s~ ]J@ ¸($2   á3ŠP)!e¥P&UhTׄ¤‡ ðຠ(ÈZ˜eL›m/î¡cå,Í}ìSú믽r€ãyžR©T(ç\5MSõ<âÆ ò³ôÕZMÓ’Æ ÀŸyæ™Á‰‰‰VVV¾öÖ[o¨×ëÅT“G†A4MÃøø8Ž9UUaY Z­ââÅ‹ØÛÛC£Ñ@»ÝÞçû“èŸJ$BÁoDTñ-ìÞŘ–G(a•Š P˜KôBD$áQD§¦Þ-¥€ªª¡DC"£QÉdˆzÓ‡Ê ¦[êÓ®btF'¦*[ë+Ë„½ÙlR!Õu=ÙBbȯ~ÀRåÇ!ð² Ýv*=z4wúôéûÿÖ·¾õkžç=Ä9‡mÛÈd22›Í’ééi299 )%<ÏC»ÝÆêê*¶··á8šÍfzÕj76H²‚t¾¯* ¨7|„€R쿇è°4Š¢ d4¨’Bh\é¥ëI€D$ I„À% 9p%œ0"` däèÑû¥Ÿd»ÛÍi•‘aå tÎ9‹‡H£×] óQ(À¾•ÔÙ¥”$›Í’¡¡!!ˆëºt{{ñÂ#LLLL/..þÏ«««¿˜Íæ¬éé ÃÀðð0FGGI»ÝF¹\ÆÅ‹±¹¹‰Z­Ö%qôÑ®dÌ8VH¨Ù’QA!ç€$]+×`ª€¦è*ÄfÀb€¢„ÐU ( ’@h¡&@@@)@iôœ$ª C„„Ü ‚®Â÷%,[GÃ%0ŠÓDš…'L@¶øYYß÷-Ã0ιê8ŽËGâæ¥P‰køÐn@¹‚O3j4M#…BÄkQéîî.šÍf7ºÜ¸®ž|ø°2::jNMM}¼R©|ìÊ•+ê¥+ׯtÝ(T*5\››ÇêÊ .8dzÈ"ý‚‹îˆ¤ˆb8‘ºG‘… †B@cª(ª‚œª $9&¡ª: ¡2€ "$Bé#”7ì2€¨eDÈ( £qC Qà‰ 9ç(‹bM#PÅ”0m%´ HÂõ TJÅó<€U¯×…ïûÙ¬¿ï¾{Ÿ;w¡JYJ z C?v qîŠf³Ijµš¬ÕjÉ÷Õ‘‘‘ÜÌÌŒQ,õÑÑÑcBˆO-..æ¯^½ªììì<äºîÇWV–Y½Þ’R‚FïëfHGÌŠ")=眄B’d¡†@Æð"A`PƒRh°4ÀÖ$,èŠ*üîê%"¢ ‘‹¾Oâ@ŽÄ“ûœ‡‘ ‰ÌŽHPHý“$~ €ä—’‡€ØÁXèciîm ©b`ô¸ÔT3k«ÕJ±Ñh4®]»våþïû…á‘á 蜔ÂåÄ{”€b?aôí þÀËåº5µ,+399™Óu]yðÁG ÃxjuuuöêÕ«ÊÆÆÆ´ªª®¯¯›[[[]8gŒA‚CôÕ.I$Ñ) “<-y_ Ò …D~Zg:#`Èi ¶ª€H…JPÉAy­Kyƒ$ú É×ýªïû õ(éù1!²û÷ÝÌ"eŸ2QbA@8Ð,×QÙ~ù `ÐÁÓœø›“³ƒ—®^¾ Í4;Üî8¡-!ôXFJÊ (è¿!L|%ø %QªªÇqèƒ>8úøãÿ!äñµµ5¶¶¶6†á'Z­ÖÐÖÖV7âOGçIž¤ò¯Èªß4’å˜D PH¨rš„¡hŒÂT(tB¡‘HT" Ä°!„Œ©ÞB’L¹‘ªÑ8)ˆËHXa$†ÿäû„Œ0!Sô·”JÔ×€ìû‰tÁ´‹¸p¾Š••&˜F045ÀØõ•‘CSJµå•+•ìUÊÿ´³³·ë4+›’æT@ b>A€›WÅö&½c@(¥˜œœ$‹‹‹¶9òÜÞÞÞÿ"„8R«ÕºE˜$>H¸ö7Á»”Чd²kšR’HJ`pÀ ¡ SL•DþœšhŒ@ E„P@B ΂xð]@*BÆÖ+‘]õ'=ÉU”QÈîÏÒ?ODvÝÿE¦òõþ_H "B¨„@€¦…¼õ¥9P] Xv¨æ9¨vZ¢4<úèCÿܺya~ó¥¹…•E„îÞÞîúff*Çî{òÞzHí¹7Þºú÷W/¿{³¸WÅøíÆHâ*†¢( ³,«»é:—Ëahh„T*´Z­îåt:'„Dú­æÐðøÞîÎÆ:nL÷>äG@AâJáœSJ)MOÆzž‡ 8ŽƒJ¥‚l6 MÓ`Ûv·')‘H!¸ë h5á7›:¼N¢Ý}„¾ÄL|0rC(R4ÄHœ¿I§gˆ@‚pNâ¬Cvï‘‘”2(Œ‚‰$M)G0JB‚‚J &£íþŸ$< ÑçDÆ ‘A\¢ÝhÔÚ`*ZQݯ¡)–Ð,ü»š×¡( öªM¼ýîõr¹rxl|¢´»³QíQ€ô)ò‘Åq—Šø¾]×IâëmÛ†mÛh4¨V«é?4Mëná2Mº®#[ÌG½zÆ`é2…aä†'`h*$ð¸ïÁsZp[ 8í&×…ð„¾‡À÷ø>$— a$¨n"¨RØ€¦¨*À‰ — 3ÉA…šP» Š.Y4ªúÅ)j¬d’2^õ'ÁS©!¡ŒJÀCŽ0Ð-ЉqY[`cÕÇúJ-χ®qHPÃ@¾”EvüÝÕBuo—Þ~—\¹tú²Ù¬  ›jIì?B¦wŸP_eø  ¤^¯BˆÕ *Bˆ¢a]Vï˜URÃOmÜìFQŒ1èš Û4`[ ]A6_B¡4MU`YyXÃTHåN}!à{.:íZÍ&\§¯U =xž/À¥‹K'JœUK‘êÚ€‰@Q!¥"R&ˆ¨º'M ¡($jD] J%Œn ÃAä|I „à:>t“£XT¡«*l+Àöv€F‹BÍaOÅð̘ù"¸±»to¾ý6æææº1”¢(,€•*ƒöOÉûAÛV)%â=yÔu]gnnî5Ó4?¥iÚS ?ÍÎM¯Këåä'©CtÂŽƒÝ½5È Ï”B14dlË‚eê0MCƒئS3`òĈBA ï8!@«^A»VAà´xmxއ0Ã" @‡r¨"n‹¨Œ+)@X¤,ŠBÁcI&(“N`êV'§ú‰ø= Î" h&@B€#Aˆ#ÃpèˆfjÐC:|ôáiP3Ÿ ,\{?üþ˨¶Ý.Ç1¾wŒsž(@²]<8¡==ƒ;¢ûbà”ÏW‡Ã0ìµø¤A”}nªtƒ-r«d\ÈX‘v<Ô:MÔöW$ª@Q(LÓ@.›Å@>‹ŒmB·aذM¹Ò,†FƒJÁ}ˆP€s‚0ðÀýDàBømxnÛêì‚p7¢“‹"Œú ‚v£Àâ§%K q߉Ņ Fo4«×Âl+zéAœ‚(6 “³4ŽCšyZ{Þ}ûu\=÷&B¾tQ’Ú IDATÐ%6 *¥Ô±ð5*nœ*ò¾[Ç·]@LÓ$¾ïËb±hY–uºÝn¬wÀ"=˜‘ ÀM­ÛnA…€Ê¨A›4Y(¡ ’w 1éÆKd]àB4=ÍZ ë«Hn5h¹ŽžÁøØ4Eä†aÀ0,H…‚é,]…¡©‘Õ‹šß ž ßiÃs^"à†¡F$åP“Œ‹îë$4ê8†  TBA7îàB©"¤`ú `Žv BÌ]¾ˆwÏÃæÆzÔ²¦¸I–”R+@"ø´ðé-2ƒ®ÉPE†¨×ë˵ÛíÁv»Mr¹\wÓF’ç§…Ÿî×'$MO zõˆ•€£ÛêúSIRç$…E‰—Ã8ê¼³€‹°é;yÈ ƒf;@„°L{!GµZA£Ù€Œi覭Ã0 dr#0Ì<2ær:rš …HP)á‡|¿p"hBxMH¯v@¸Íoƒ „$>¤ààq»FS"á‡À Iž˜@«>ŽvSËUQߨěßÿ>Ú¾KUà !Fâ¸'Z(Ó4 ç\Iõ XꢩNìAÂÃØØÖÖÖºQåààààøøøS¾ïŸJfë·¶¶ÐétöñïÒ´íî“vËÃ7¦qUˆøp,J(‰Ü £JTÜIÈœ$©¡ØßMÒ6t…ÁPP)£>Â`è**{«+˽½(ÅTÛDÖ¶‘µmØ– …1 ( 0¦‘…ž%P©£à¡ÓDBt-øAa  \¶A(E(5t<M¿bgѪ4ñÎÙ³hù>lUA  L‰‘õãYÓ4âyëcõi¨ï§ßóž  ( FGG±¶¶( ¹{ï½wzhhè)Îù×cÇ(¥ØÛÛëþNÂàMŠCI(añÞÔTõÛ—)*¸`ŒBðˆ²Õ­°AÄ$Ìh+‡< ê!doRÌÀ!A‚1ªªžf0r"tÑrjh•Í.£®Ð4 –eÃÎdÉæ`™& ˆ]‚®1…®Q¨ `*QBÔ[ ~ mŽ?ði4Ð%`ª<ïCpˆ=2¬ªªÉ¸;é±vr»Ô±÷R244”VÈ=ûì³Ïg³Ùÿ¸³³sÒó¼ ×u±¶¶†­­-´ÛmhšÖ]´”¬OK¥/7eÉf-×uo¢gÌ+AJ•x[xÛˆ43ûâ“}©¡Ezþ ùy]:ÚÞ^9å tÝ€aÙ`Œ@S,Û@>—AÖ¶`ìB–eÃ$64FÁ- *}øM ! ¡c°¥‡•žeŒ”Ï"±¾[ø}u•÷Šü777@>öØcù‘‘‘g···ÿ§0 ªÕj¸téZ­ÇÙüõ6 Ã躆ä`…„©› B:nH£w5k‚*åU+[/ -½<µ¼ÛjvŽãô½*e02Pu 1ak‹Ê ‡ª2pïF Ýï¹0 ù|ž¬¯¯ß’è{!@÷•Œß'¥ü4M{¨ÕjáÊ•+ØÙÙéûG¥R Åb+++p]N¦ibvvÍf«««HŽSK¯] Ú¦AQ”ôlà>Aõ›º¾ÙJªŽ‘(xzÊ8(ßø]ФS ¨Ô€n"k€»S(d5är€®0ì…û Q¸4»9éÄ ’Œ•ßNïÿ¹ò›ßü&ýîw¿;²¸¸xxxxRJlmm!ŸÏ#“É Õj%G©áé§ŸÆoüÆo Cüõ_ÿ5Þxã PJño|[[[øÓ?ýÓî› Ãº®ãèÑ£ØÙÙÁîî.<σçyÉ)œÝÞo"è£x$7=-Ø´»J¾N#e,ºQñF—¤£6´ R "$„ € `j!² C&rŽ0äûž3ÝcI½>A)mú¾ô)÷ö[1û‚À}T.—cŒíííakk †aòù<yä„a€'Ÿz ¿õ›¿…|>ßý»Ó§Ocss…Bº®ã»ßý.¾öµ¯Á÷}\¿~Íf¿û»¿ !¾ùÍov'’.â+¯¼Òu+ Rd2™®/îÝîyÐ6ïP-ÐéÆÁHÐ’4¶ëº8£ ÑàIô|a¤IË•(s!Qj(´Ð¸ƒÊ4HÁ!ï[0K^­V·çææ®5›ÍVt†à‡o-,,Dƒœ2tÛ¶Q©ìᥗ^ŽçòfpæÌkÈd2( Gq°€cÇŽuÿÏ‹/¾ˆ_|R \¿~¦iâСI|ç;ßÁç?ÿy,--áùçŸÇ©S§ðÇüÇÝ´'ÙØýÌ3Ï V«áÍ7ßìZe&“®ë¨ÕjÝmŸéØ"ÉBt]‡çy}—AöSœôשónZ/Cb’(O­ãH±•ãâ(ÀD4‚F ˆnœDœ”’÷¾†¤s1œj\öí]-/@„ç¾óïàé§ŸÆðð0Iüù<Ïs±¹¹‹F½ƒÿëÿüüïÿÛÿ‘,MÄ‘#G05=‰Ã‡avv¥R ˜žž†ªê8vìx÷I>ûÙÏⳟý,ÇišXYYÁW¿úUüò/ÿ2¶¶¶pýúuÜwß}øÂ¾€?û³?Ù3gO?ý4Ž=ŠoûÛû PBˆ„¦Ž½½=$„•d&1©[ôƒúD%Ê8’%ñÂî°Iú40BÈíŒEÄ•øöSI!C N˜¹¬@añC†$Hú<Â}Áp/H)‰¢(²GèIðVHð€A€ÁÁÁMBÈÅ•••Y)ew!òƒŽ@„!ÐnwP¯×P©ìa{{/½ì¡Ói!›É —Ïalt 333ÅôÌ Ž9ŒRi³³³iš€©©)|å+_évÛí …"–——ñì³ÏâÔ©“Ðu¿ø‹¿ˆ—^z ßûÞ÷0;;‹r¹Œf³‰Ï|æ3Ä?þã?bss›››(•J˜™™ÁåË—‘ËåÐn·»78ù˜ß²¬n&’œ÷ëºn×ú“QótU3 h“­cûj ±õ&Í2MÓ k:<×…”–eïÛQœŽ5’Ô9ÉœLÓ”ñÑtë· ܱ ÀÎÎÎuÎùðD»ÝοýöÛ0MÙl”*Èf²ÈçóÁ¡CÝ¥‹¾ç£V«coo‹‹«¸vmíV €m,033‹™™LLL`xxGÅôô4†‡‡c—ÍŠNOOczzºûâÚí6>ñ‰Oàoþæoàº.._¾ŒjµŠ_ÿõ_Ç¿üË¿àÚµkX__ÇO<}ìcøö·¿Ý-V%¡¡!ÜsÏ=x÷ÝwÑl6¡ªj—–¤°‰IPÏóº…ÎùMg%O 4A‹D t]ï.ªN *a¤Ï‰,ËrFFF6×ÖÖÚ=–ßï,Á÷…ä=¾O)¥TA¨ÇŽ{È4ÍÿLy®Z­bkkë¦9=Ƙ–Ë4aYd3ù¨HbD—Âü @»ÝF½-jh4h4PU„˜¦‰©©)Œcrr‡B±Xĉ÷ajjÙl†¡AUÕîöîÞÇüü<677Ñn·ñøãc}}ò'žmlàí·ßÆ“O>‰ßû½ßÃ?ýÓ?á/þâ/ºå©S§0œ“m$é>pc7q¯¾Q´¢ûzI:›lôÑG111ÑE¢¹¹9ÌÏÏßÔù‹Ý@¦¦¦¶ ÃøïW®\¹  _µÔÕÐÂ%U!Þc‰Ä{º€äÉ`nnnþK_úÒÍår›ëëëCº®+µZY–m™¦q¬Ýn”Ëe´š ´š Û±6iÈçóQޝFhaèrÙ< ÓÓà!‡ã8PUÍf­V õZ‹ Kp\§ Í¥Á&M`ttSS‡päÈaŒcdd³³³êRÑ9‚#GŽt¡´T*áÏÿüÏá8ªÕ*._¾ŒG}‡Â¥K—ðüóÏ£Ýnã¹çžc¬›•¤§™Ož<‰ûï¿.\À¹sçºg¤-½Wèi‹NŒexxèt:Ý}Å Q&Ù¤ŠM¤P(àèÑ£Þ•+WÚ=äÞícâvÜÀ{!@Ì‘$Œ¢ !t˲†r¹Ü€a¶ÜVµ811q†3;;;¬Õj‘l6[(‹G··v¬å•¥>¬$JØv¶mò, âz»Ó4Áƒë:h6[‚ŽëÀu\ìîî Ý©CÓT‹E bhhCCC˜œšÄ±£ÇpâÄ ”J% @Qr¹|7}½ô ´ZÑi N4˜:33ƒ .â¿ý·ÿŽã`ccçÏŸÇ—¾ô«øú׿†k×®áÿðqñâEÀ=÷܃ññq¼òÊ+É ¡.œV¹\'NœÀÐÐÇA6›Åææ&^ýõ}[KÓn Ž‹¼Ã‡ÿð­·Þúa³Ù܉­=ØúÛ¸±¨ê sß·$¬mJQ !šÂŒYÜv|edä2™LFÓ4]J©–J¥âØøøqË4ŽÖjÕ£—.]Ò“€Éu])©@ÊhÂ6š’Ý€Ê4,X–…|>Û¶Éd‘Íf!…„¾ßFÇí V­¡ÓqÐj5Q«Õ ¥ÄÑ£GñÀtGÍ'''‘Ïç¡( &''1==ÿï2™ìþRp\¡L¬¸ÕjassÇŽ¥ßûÞ÷ðWõW¸|ù2>þñã‹_ü"þò/ÿÿ÷¿ïÿ âñÇG.ŸÅ¥K—±¸¸ˆ™™LMMRÚE¹=›Í憇‡_ ”þš®ë çår»»»`Œñ¡¡¡W«ÕêYÏóÃ0!DŽeJ¥Ò`»ÝfžçI×uE&“±J¥Òñ½½½‘••é¸F UU-ß>¡8yò$FFÆà¹^7GO,+ÚÄ Ç©]»ÝŽKµZØÝÞA­^Ñéa–i1˶0=5©©i;~33“(•†0>>ÁÁ"r¹<2º®ï+*U*»ø‡øñÏÿü/A¢V«w÷Õëu\ºt 75—’õõ=ôP#›Í¾õꫯžu]·[w;åÿëñçÍøûîð2$û°KxÏ÷’sïVªÒsi”Rb𦗠ïÉ“CƘ Às]7 ª Ë„©(Š „ÈF£¡I)¯3ÆJ¥8çd||l¢T¼wccú>¿ }Ï“íN“¹n>¡–Ì)\¾|W¯^!¥Òò¹S`ŠÅŠ…"FGF¡(á¢Óqâ•rU4M¼ýöYüð‡o€ U£°, Åâ :„£Gàøñ{066Š©©q”K8zì(Ξ}ÿå¿ü¯p33ÓÈç‹0M ŠÂ iZ­Êåò¾r·išÝ&ÙøøxÀ;gΜ¹äºn#¾ÏéÝÄéõ´é)a~;YÀí¸¤8èèDz¸i] èºî‡a(Ã0ܧRJIš¦…N'½W×G´Š•AÀHß÷I£ÑØÀEaRJêºî\»Ý¾$¥4†‡†©çyð\ß´mëD&“}`euUo·Û"D¡P@«ÕÀÎÎ&vv6ã1®hácÆÎÀŠ]mG½…R©„©©)H ¸® Ïsá8´;M4Mìî–±¶¶ŽW_=Wþ²Y ÃÃÃ8}ú4jµ®\™¬®®áĉ04Têž)X¯×»iaïÁ–eÁ¶í…………ËŽãÔãûÒïä‘ÞmäâvBï‡@zÞ¯MãÀz‚F]J©†á»®‹Dºì›ˆ`Áã9w?uõnÙÞ·' C €•ËåF¹\Þ PJ)A¥*€íÓ§O‡«+ËߨØ0†‡‡Ã>ÓhÔOUkåAFòù\•1µ“Ëe9Æöö6šÍ&:N³G)2Јâ•Éd‘Édºå𦧎€1!át:àœ£Z«¡Õj£\^G³ÙDÔÅE·ÁÄ9GÝÀ0îA¬6 >¥Ô¨è=qÌILx è—wÊܼÍ2Ùeƒz½î‹EÞh4H£Ñ€¦i]%PŠ¢Èøè”à-°Õ:éQ²^Z4‹Ú)¥WëøÎÎÎöîîîB†ììÙwšù"¥l°88ˆ#³‡—›­Îµ™™É‰0䃦iÈ­­m⺠…BAÓ´‘J¥‚J¥ŠŽÓB,Õ`Y&,ËÆààtC‡eÙ ”àØÑÃðƒo¿]G»Ó¼iN"©ü% §ÉÉIär9wcc£ ˆŸêwI‚ aOÉ÷¶N¿Á~J€>ä¤Ü— ‚W*•\)e†¡’®«ª EQDì‚”ÀëÑî~HÃz”A1M³]«ÕÂø&KEQêE†aèÖjµóÍfó,!ä±ô=º®CÓ´ô6ìƒ\A€ý«ÄzQ WTUUÛqÝRJÉó)¥ªªªK) “ª£¦ªœÒðU©Tö1j77773™Ìe]×¥TfìŒ9>>~„2Ê(#–U…ëº$›ÍÚ¶=]¯×ÕÍÍM¸Ž ×q÷ݼ„Û˜™L£££0MF›››¨V«ÛNg3õ¾¾ÿÖ/ï4ÜJЧè LÓÍf3¸páÂ%MÓÞp:^*Ñí¨iš&E”ö*A: ì­k“>±Gz2FÕ4Í¡”& (¥¥´w8íD¡t]‡eY<9ä‘Ü8¶»ÛTiµZ¢ÕjØÝÝe‹KKëFô`Œ1ÂSmÛ:lš¦g-ê¡C‡¦…£ëëë¤Z­J)%MÊ»£££(•JÝæUÒ\]]Ý¢”nƯ%ˆÑÑë|ÚúÅí~wBú ½7[ ”R±¥„O) ’viÒäˆ}H@ôéq§k ü臦in|'!’RšäH)¥.!$L”жmÎ9O‚,ô’-bFò^iœ‚R×u“çSvvvv(¥×MÓÔTU¥†a蚦Mjš6Öét(¥” …]×-..$d’9Ž“p ÂØï§ß{ÐGð·êþÉ—Ü2FH˜+”RoßG¶Œ³É9O§˜½JV†´¦÷Šè DQŸ1&â˜C*Š’X¥”&g2ƪœóV¬ ûfàÉù~‰BzãEJI…¬ÝnwƒÓ­­­mBˆnš&#„PÛ¶ ¥R©Y*•>e†–tÿ’ÓR¢S;;;û©J_¥|èàˆj]|¼ë1Z³TUU&©Nâ²Ù,,ËêÇoëýºï } »CWQ”€"RGÇñx¿‰? J)vww/3ÆÞ‰K«A¼Èº÷æ§›*$^Í–Ž=”¸e¾ï{B&¥dN‡ ×¯_¯;ŽS¢”>¬ëº–0‹’iªÑÑQŒÉÕÕÕôé ½=ÿ~ç}dƒ!V ºè@)•ªªJBIø”‚Ü[1\å-|]ï& +@˜»×Ò ÆnÕjuÃó¼E­Øýóž8¤÷Ô/ÒÇí°xfŸõII»± cÌ ”6 !B×õîAVÉ¥ëz‡1Vu#J˜zîàò‡Ä‡<6æ£TI)•d©T’õz]¶Z­n/\QÌÏÏcttTš¦)Çé7Ô€>ß;èç7-PNŠ¢HBH·jI)å"z€"u]OÜÒ¥hì?,ìƒ<½±ÇAY‰»O×õN†"MMv,5eÎùœ”ÒÁ£eC|™ÄOx]ü-ƒÃøÄni†l6#ÂFBxR¢ÝnËN§#4MŽãˆ>qÀûá¹÷¦¡2²LŸ˜F’螸©ªªH 8ì)¿=%iy‹ ”Þ¢FÁ(¥cÌ ‚@&Ôï„M“O¾ïW­ƒåñQòü—?m.@ök1ïíí‰$+å*• šÍ&4M£Š¢HĈ>ÜFió&EH—ŸãŒ œ¦Y·)t)këçúøØOzãJ) c^*Hî¶ãr°Œ׃«RïRŠãÓépÛ¶ý$L/CCC ÛívÐç¾WÚyKÂCb]]@ıëÞxιŒyÿò€ô³_§]FúøØNª†ß¦”v(¥^ºL)íÎ@ÆGÆò®;ný¹¸®+`kk+àœŸeŒýç|5›Íbrrru||üŸƒ ˜BôVµÄ{°[ÞWš^Y—ÿJ-›Zf>y‚÷É@n•ž}Âëq%^\Ü×ÝL:Šº®c``@Æ5‘~‚çè¿û?M.`ߣÙlJƘìt:Þ™3gΜ:uj¥T*}ZJùàèèèõ«W¯^]]]uzülðÝ­÷•uÜ85<ÙÝ·³¶”’ô.°è“u”ŽÞê5ô2©ºÛ;EñâBì%¢‹EŒŽŽbcc£_QìŽ[þEbN»$„p×u[¯½öÚ’aÁÁÁW¶··i†2¦‘ã%¼E‰ó–oŠ’-9çqÉ@ ß÷ö ©·=}€Ëy?¸èÓ #=‘:cœÂûݧ\.‡ßó¼ðtǕ࣎dü„+Bc}}}; Ã!Äíéo÷¾,Þo­;Š/n¬3t]f²™h"WDû]Ãð\H*! I¸ånÛ¹Ñý£kÁBÁ“qà»o’¹Õj­”Ëå«®ë:ñ}}rý\òý‰ÔÒ7%Éi)¥,®¸ÑøM²T¯7÷~¯8 +,]ד­"ú'ŸøÄŒ¢*¯Uë.Û ÔÂàáÃOýüsÎââBSÕDàÖ]bèLÛB½\'© _ïÆ­[­a¹ÜÔ9BÈtzš¸Î9ªÕê–b9~ÿiÅ9hêWþ´*€ìÓ%ìvû„é~Aˆzùûd¹ôZ?i·Û€ö«/~ùd£³þ»ç.]ø´¦Œæ PÍr¾päØÈ3Ããƒ+»;Û+çÎëšÁ|‰vêí„Õ¦ÊÉ·ÚÀu»¨ “ÂXÚÿËÔ2ª¸qöþ ðެ‡ùÈc€”ðÓN—YÊ Éž¨º7ý”LUUäóy”Ëe~h²Q¨ö¹Ý ü†×Ö ËÒÀ…¦ç”ÆØæÞ?@×ÌI枀d&<ø^0:95õÉâààV¹\ž_]]]ÅcÚ>áv)áw–LÇ´rÙòÅmÄ ?U•@Ò#lô|¦à¿)Þ7Á1LŒ Ðr¹¬¬mòA¿]=ê–[FÞ`(Ž®¡Ñ)ÁÊÛ* d 3øÿÛ»šß¶É0þ{íÄqš¦nºt]‹ Û˜€Ø` iBÚ‘ Äœ&þ„8ÀqåÄ©âÀ×e¡m°©ÚÄÖ±jS·¥mÚ®mR磱Ç~9ÄnÇÎGëvk×H‘ú‘ȯßçççó÷1Âó s/†>rðàˆ$ŠâèÌÌLÖÔ@Aˆv“ÐLÕ·LŠYQˆå|B ( 8ŽC"‘ ù|ž6 ?}þvh§/`Ù3Æ&lçgõ7½öóÀÀ2™ ŸxÀýàýW÷ì;-h¹á}Ýë#å(®\™ÃòB#ƒÇÁ adßG©šAEUõ€”¢þ~IDATna~‘ëK&…>QQÀì6{—éÐ 4†B-6Œ«AtppKKKÍBO[0o«@âb ¨ãwâr5{ú “Éàú,¸3oŸ|ç…$ù$ŸzOB_ê(~9/ã¦vÇÎ?Çè·ã(KEÄS}àxš*C‚0¨>ÔE…p„Aýðåçïn¸c öo•Jáp¸‡ç‹Å¢ŠÖ?øª ¶C¸9*z‹«­‰WªªÒÙlÍ¿(•J3½xU–$>ë×*ÆÁØ›¯%G†z+±¡WfA#!üK(ɤQ>÷ááç2ó:ûÝù«ÈJ*!Žh¬{6Ÿ_Y0×äޤضU†ayžçxžg¬H ‰@–eeee¥dÖD¼’P;Ò´"‘¹­k||œh`´?ÿâ'Äo„ç2r½ûÿ:üÓ×ç>zã­——+w§zRÉG=â¥È»'é±É‡Ý¸|©¿`Fw"Y*ò¿ßüg쾩žów·!Ì9ª•`N:5¨iÚëétº[UUˆ¢h§ÐÇqÜX,v=›Íêðæ?øšØN @;v[!ަiµ¡Œ,«¤\RNB­aµkyv¥rùÖÌ…Å¡ÔãGÿ±…¯¾ùáÎTúÞÊÙ÷Nœ˜“Aã#’ø|ño™—‹0”µk×þÕ4½h²ˆh§oÃ÷FH’tB×õ Õ˜ÉdpäÈôôô­,f‹(ÀW <)@6*t—\#Uê{€Ü§Ÿ}ù3€+X?†›œ*MþøGñ⽇“¹ïG½h%B Q ‹„QmR™ÜŒ&•J…cY6(Š"B¡dY†$Iey¡P(Ü-‹eŒâ†9Oà·úrqȬr¯aÒ¼—Qk›„~ìÆí»c7nOÙQUJ×ÂR¯~{§N:ÈÚgC¡!0==ÕÕU$ ”Ëe,...0 s¯Z­®¢}Àހ߆ý‰±˜4š¤ÉPJaPTQ«ÓÛ“<™@Š :ɨ?ýf*xUUÕ%EQ†(¥ÈårPUýýýUUKX' zåB|‹ûr:eN§Ò‚Z“Lª„b¯Ù+h$s8Û¯ªè¬ÃÉbMòÇ"Ã0w‰D6ôF£ÑA.¦Óéi]×54rUÔ8õsw2àˆÓ­Ð-èòv‹íí ±7c¨ðf·sDûÚŒ%–e‰®ëA¡®®®®T*ÕÇ_ŒD"GE‘'&&Ò¶€eŠÊ¨Íý)›fÌ‹šFŸUxiº† Ô¬°ÿÏ- egùØYJ^©iÚÎÚL.$«išµëSÖx3r :`§”Éhœ°ã¢€ír* ¡V›¤u~„“jlRõR“õëünÕ¬u}Íö÷VeqßœéÀ.<ñà¾ôLà~–Ñ"ßÉæ»­Ínj,@ZõÆ–%uöúÖ ´5€×FÛ©Yêçz™¿fõˆÍi8OÒ2à2Ë<¾ ÀÑd}NÀf¢XÇút¸÷øì®W«¹‡¤Cm6E\®ïÕFFàÝ«oUFp·À™}#>k™Íî±[A=9×à‡z¦à×ýÒ-¸.iòÚ£¢Ó'µ!{/ÿöÚ+ƒÙN'ôž°‹4P'”ó='ð4C["ø=<ý` Û±€ÿJâ,RWRRIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/editModeOff.png0000644000000000000000000000013215101070305017362 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/editModeOff.png0000644000175000001440000000030615101070305017351 0ustar00rncbcusers‰PNG  IHDRÄ´l;sRGB®Îé€IDAT8ËÝÔË € EÑ7ÑZÇžìÉ:,ã¹P Í0,ô&¬H$|€¯5Tæ`°D/Êch8L² ÎT4μHœ¶(œwEà,ÕŠ³V Χ¼x ³ã‚oVìæÙòòT•‘&Gµû/Cµ‚»²»êö¨¹ EXœxjÆoÚ*•fÅÙÂBIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackCurveProcess.png0000644000000000000000000000013215101070305020645 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackCurveProcess.png0000644000175000001440000000116615101070305020641 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®Îé0IDAT8ËÍ’ÍKqÇ¿Ï¼í¬‹ø¾‹«îÚ e ¥…P(D „…‡.óÒ½SñÐ!At¬X]BËÅR2kgf·qgvgg/Vó\ßÓÃßÏËø×¢¿ô´ÅÅÅŒSs§œŠ3Ź`½]Ý«ºÔsËËË;Ø©€±±±………'£tãûÙjü[Ê#_Ô¸ù‰‰Ìæ@s´6¼¶´´ôØ÷ýJÛ£¶ ˲ôùùùçï/|žJÝÏd«ƒ‡ý®Vï-›Õ¾ƒÑŸ}Û·¶;£/µþÙôì¹õõõ”6`ffæòvgÉ™Ìßí°ô!õ Å @ƒ…4BŒ:6n ãÅÛsssömvÍyðãj]­5P’»²É$qbàR’ èè(Ý)‰‘Í‘{¶ŽZ¼uý »NN´mS¨"<4•L IjM@õJÉ(9qbθ dM-µeÈ–Ò ¦Ò@¨z`zh!@J Î9NÀå[ã+;¿?^†…qÉÔ˜îCÄ< A‰HÒZÛvtÄ„žx™~×ÍBÕëfY†ñ2Eq"^TN$š¨'W’0É|uâ¾ï»“ƒ7'ÊîžQ?è’F¨Aý#3-4ROS»™™\¡Px8p]Wxž÷fZŸçLKnSŒìد˜Ý³ÚSÎ>Ëf¶2ù|>ÿȶíÃS£œN§ ˲.¹¾;4‚k8r&™¯‹ÅâGÇq"üWú Š ­ a4(IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/viewMixer.png0000644000000000000000000000013215101070305017154 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/viewMixer.png0000644000175000001440000000145415101070305017150 0ustar00rncbcusers‰PNG  IHDRÄ´l; pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<¹IDAT8µ•ÁKQÆ¿ÙÍîš Éb…(^Ûb¡ˆF0¥5BÀ["zÑjCNé 7 .… ôÐKi¡¨clž„J1K‹/­TB,&Fh 1¬i¶Ý}=˜•dµU:°‡7û½ßÌ| d³ÙåÉÉI„B!.kšö¶ep- žçÁó<ˆÈ`Wx¬`f†a·Ûa†À€jµú[E†jµZ5µº®ëuÚ†bV&‰¸ ‰D€ '''©ÅÅÅDDårù30Æ´w‚ ÜÇãIÅÆ-/H­˜€`É;È–  €ÍÊ¡+ØÕTüÜgÆ@?ß"IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportPause.png0000644000000000000000000000013215101070305020227 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportPause.png0000644000175000001440000000036015101070305020216 0ustar00rncbcusers‰PNG  IHDRÄ´l;·IDAT8Ëí“1 1Eÿ<˜7°ñ+VÅNÐCˆ§ÚÀ& `gVb‘ÕMFY°ì¯Ÿy™ùÌ£Š³NÂó½'öTpÂfÚ€H`½8XòámgG¬æ‡Þ{Ñ$×pm=È&þŽÀü¼y°q×Xœ»ŒàÚzÓiëA6®‹2’…À®ë÷iZˆu©²àóå–-jNþëË¿¤<°²1»þl?.KóF¯;çÔ9þ„©ùÎIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportLoop.png0000644000000000000000000000013215101070305020063 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportLoop.png0000644000175000001440000000056115101070305020055 0ustar00rncbcusers‰PNG  IHDRÄ´l;8IDAT8ËÝ”1NÃ@Eÿ¹t”ˆXˆîDe×(=Bô@AeWHœ 7À€DG…„DÎÆ‰h¼ EV6I6ëµLe{Foÿþ™1ð¿#ñ-mý¼1ølçÍ Þnª(Ë5Ž6{8€²¬OÈïuœ†îoƒUÒH˛ҨÓÐ$ V€èî& Ó^‚›wQR& ß+“+ž5J/p@n½ºAÊIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemReset.png0000644000000000000000000000013215101070305017136 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemReset.png0000644000175000001440000000110315101070305017121 0ustar00rncbcusers‰PNG  IHDRóÿasBIT|dˆ pHYsbb8z™ÛtEXtSoftwarewww.inkscape.org›î<ÀIDAT8ÕR¿«Ú`½_¢Ÿ_Ÿ]¤ ÁN‚ƒHš¡ØMèäP ]Ú¥€C÷þŽ‚¥S'×(t©S7¡%O24b‚H©ÁiÓ/5¡ÞË{øúclÏt‡{îsÀ? D$ˆ(üM/¹‚L§ÓéÍår)˲|¦iÚ×ß .‰çy'ÃáðŽa¯E1'“ÉmÇq("Šˆ( âÑÂÔ¡ð}ÿšeY÷‹ÅËf³)Çq ¶mßE1;›Í’l6ær¹–e}Öu=¹ô" £Ñ¨²Z­ÞU«ÕBl·[ ”c ’$Û¶¿ƒ×õzýi­Vs !ûKìv;àœçcEpÎR ”RÐ4M”$é~¯×;á9„?5 „ì‹ÅâŒsþ¸Ûí~Z¯×†!´Ûígý~ÿQ«Õ2Ã0J)AðÄó¼G"æóùH×õ7…Báa§Óy?ŸÏ÷ªª¾m4ƒJ¥ò‚1™LR©T.v$"!`ë8Ž©(Ê×uår¹|V*•¾Çc×÷}Œ¢ˆÄqü%Nï~™ƒƒ¥ç3Ï…rçºiš·6›MZ’¤¥ªª.:ñG’i†xUþsüàÖõª³ »IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/formMoveDown.png0000644000000000000000000000013215101070305017617 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/formMoveDown.png0000644000175000001440000000030615101070305017606 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®Îé€IDAT8Ëí“Á … DŸÆŽöׄeˆeHMNMxñ€¸ø^Ýæ‘Í ðr:O\—kMcÒéþÐ"KòÎ0—zÿv…P¤àEçÄðSwèA0²™ÝzUID`>ã¤4»M¼‚Ôæf•=ˆgnjHËüw‚‘ƒ‘÷?ðx.ͼ:à…·cIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackAdd.png0000644000000000000000000000013215101070305016712 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackAdd.png0000644000175000001440000000042615101070305016704 0ustar00rncbcusers‰PNG  IHDRÄ´l;sRGB®ÎéÐIDAT8ËÕT±ƒ0 |r,CMÍ$ôÏLÒLÑL¢4$ñ„"Ò¹±¥·üz ø7k"Á$=ßSÕær$©€¥ØöÔ @÷CwqÜF9-Ä q¾âÔáx&+÷„ó¶šÓ£}6oÊŸ± Γ%QÅYà¥L¥k´é¸F¹ à ]×m+‚¤Iñ]€‹Èç|QE™ 3 q>Ï3Ì,Í#-r¼Þ÷=Æq\W7äÞ§©*$TñéY¡ª· z¿ˆ÷Ötqñ"øïž]²[Œ”söÞaIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackIconSpeaker1.png0000644000000000000000000000013215101070305020506 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/trackIconSpeaker1.png0000644000175000001440000004534215101070305020506 0ustar00rncbcusers‰PNG  IHDR€€Ã>aË IDATxÚì½YŒlW–¶Î9w¾7âÆ˜ó›>¾G>gV‘U¬êbWWIvw«5´ ´È– ð þÑŸahþõŸä_† ˰!AhI=XÝRwu›EòÍ/ç2#3æ;žÁ÷FÄ|=Ébƒ¼È2#óÅ^gí½×Þgà«ÛW·¯n¿¸7ò³ðG¦IJå*Ê~ŽWç)œcÐ?Qqžývõ•Ù¾ä`šFÞýå_%ßùÁoàëï~ºn‘0˜*ÍÔLƸÿÑû8j’zs•´öwÕý;àΟý Z;*ŽBõ ¾¤·ÿùŸþsòAWѧဠÀÉ/3Ž.íWÿÎ?Ðð7~K{éõw˜e;¥”’Ÿ6ûŠ>áö_þwÿ#ùÛ¿õ_àÛ·«³[}õ­w/lœ»¸n;¥†iYUʘMaHš&a£(œŽ‡ƒ~çᣃÝGGR¸ñ⫤Rk¨û¿¯N»ò |Å ?+PJBˆÀÖ7/\þæ/ÿG¯5W7^/Wj/Û¶{I7̦¦é¡‹?GJ ÁyÄÓdÇÑQ0ìû'FÃþNçpïñþÛñP¾ €Ë×o!My°óP*¥T_á‹À¶RäRfxXÿ¿÷ß»rã…¿]­7Þ5-»L)[þSÈÓÂ=5¿«„DšÆj2n÷O{?éuÛïïm?øø~÷_|à@ì¸Ü’/O{])8—_á‹gzáÚ­7þÚ¯ÿ½ÿtóÜÅ¿éx¥!Ù"Ùø“mCYº¯ò?Z)…8Ž1 öN{Ý?Ù~xç÷ÿÕÿý¿ý{)Ä6€)U©5ŰߓJ©¯€ð òÒ7Þûwéÿpëü¥7tÀ’R©'Œ®Ô_Î.3@d_ %PRa2Œ»íý?líïüÑþî¿üƒ£öþ‡¦”2i¦H’H*¥fîá+ "ïÏê¶òê·ÿúõö»ßû'ç/\¾¦ë„àR~⊟ن’]”f_ SJÍ/`$Ó²ÍJ­quemóí«7^|¾¹¶Q»ÿÑûRjÄy*•RÔ´lòWÛWøéoë·ßþÞ?~íÍoþ£ËW®—4¦A¾0\!FS™ÅA)…¦Ðt Òi’@ B)Ó iÓ@ ËV!An¬\©n­®Ÿ{ûÖ×Þ¸ê•+ÞöƒÇ¦‚s¥¤„aZdö³_¹€gó®½üö?zåoþ“^|IwIš.Vñ܉ÏE!PJSôŽ;8=9F0 Ž#pž‚€B7 †ÇuQ*UP*û(Wª0MR)(!²¯Jæÿ(Æ&£á¤µ¿ó{ÿþ÷ÿåÿñ“?ý£ßpš§‘Ü0-‘¦‰TRþº…g€õ+·~ëÅW¾þ?}í•7Ö×Vב¤É‚ñsë«lÑCÓ ÄQ€G÷±»ó½ãÆÃ>¤`Œ RB ) ÃD©T†W*£Vo ^_AceÕZ†eAJ™3Íì_ǘ)8ú'Çûï}øÿþ?ÿûÿú…ÁôƒøÑ¡µ÷¾WBÉuatÝ¥tŽ›˜sá£ÉƒñQòlÔêM4›+ØØ:Ís—a»s—ƒL*¥ q¢s¸÷ÇüÉ¿û?ÿàwþù¿° 2-›ó4áBˆY¶ð ÃÏ2Я¿öÍÿluãÜo]¹r”K¾LyJ‹Ìª”!=uñ£?þ=n£Y©àüÆ&Ö+hVª¨–*¨xeøå2ê~+µV ¬ÖXo¬`³¹‚j¥E€““:íCuÛ8==A'°m ¦eƒP–ljBhš¿RÛ\ÛºðÚÆ¹ËöþãqL‚s®”"¦eCpþ¹«¤? †õÂ¥ç^úo¶Î]<¿¹yNQJ©b)ÂWtÍÀéé1>üáï#ô°¹º†Fµ]3 À…ʨ|v©YÔN i\ÛC½RA³VÇZ£õÕØ–áxŒVûÝN ƒþ \Ç…nZ”T2û{,ǵêÕÛç¯Ü¸<Œ‚^·u œÃv\ÂÓô&xV`W^xý7«•ß¼pá²Y­Ö”‚Ìè[åFdLGœDؾó#ŒP/Wa™6„ÈV(!º®Ãóm[°l¦iÁ²L˜¦ Ã0Àƒ”Qa”c<š M˜†‰Z½ŠJŇTa˜ÀÐ ¬ÖêXkÖ¡z'§h·Û˜Œú0t ^© ¦éPR!¸¢”¯\©®o]|ɶ=mûÁG-%å„PôT!CP_`qs7.?÷·,Ç}eeuår…H)–dV¦éH¢Ý½ˆÆ(¼ñæëøÆ7ÞÂøŠ÷¥J¥ C¥„d¹;£d^`ŒBÓ5zö}I’`2žb2™"‰hš†j­×uÀ¹€” ¾WÂzs~¹”‰z½c@)”Ê>LÓ„R’!! Ž[rë«·MÛ%Û÷?ì(¥¦‚seÚOÕW p¦è³yåæo:néÚÊê<¯¾X)™` ÑdˆÞÁCL}œ?ßû•ïb¿÷oÿ?PʲÀŽÜè™ñÙ Ùk$»ŸÇc”hZV#R" CL'S¤I ǶQ©úÐ BH躎•z«:„”8:>B§ÓFð¼l·€!¸‚Rͽ¹¾õR­¹Qº÷áÛP†i+ÎSùfüo»×/^ÿ;–ín5š«Òq<"D¹Á&ƒŽ÷‚øæ»ßD½^Ç¿ù׿‹é$€ãØsµNËÎe ”2Pš­~:BáÊ¥E0FÁ4 …1Â0„R žçÎã¨úl­4`[:Žû}´Úm„Ó1×ë•B‰B)¥ˆe;¬ÚhÞª¯nzw?ø“¶”²/¥PåJ]ÆQ ¿¿±zµ¾¶õë¦m¯6+Êv\‡Ê5…éð­Ç°¾¾Šw¾õ6?ÚÆ;÷¡ët]eŒjåç†×-¸ƒeã?†Ù”‚1@!‰¤I–9x%š®AIÏq±¾²†’ç`4£Õî`pÚƒaè(•« LÏ@ LË&ÕZóFcuË¿ÿáºRÊ~iɯ!M¢/}vð©àùµ«•æú_7mg¥^_É”4¾ÌRJŒOÛèµ÷qûÅ[¸öÜ5üÁïÿ!F£1lËÓòUO(£Ù•»ÆØÂOs—0½„dUFÆ4(¥¦€‚mÛ°, „˜†‰ÍÕ5¬Ô«¢­¬©1 ¯äƒ1FD&Ó²Qm¬^Û8eó`çA7 ƒ^‡0LBð"È/(Ö~`ÚN³^o²Ü3.€@ ~g* ðÚk/Þÿ@¦i寧9ÝÓü±JŸ4>!d.*ÍVþ“®æà¡`a\! ë:LÓ išõ:V ¤Ð ™¦K)…ü2‚àY0Àe¿¾úÝ0ÕjŽã’Y¨€“î>ʶŽ7®ãáÃmõòÜ^ËýÿÌä,03Þ<È韞1ö'¸ʲX s%…Ç„ä:Vw ””À÷ËØX]%‡ºÝOP.W`X6‘R*)1M ~µ~ů6íÿèP)5PJJM7¤”âK|j0]_«¯m}_3Ì5߯)ÛñȬꥲ@ô»hT\“~€r­yÍvËo1Mw¼ÒD Jå2LÓÍæ@B)JÒ4FÍ3P+—ðøñ68Ð cár7pg‡D è¨žeº(%Ó"PÈSP`1€̺Wš 4j5´»x´½ ÉTku¦ïmdpÜRÍ)Uý‡ýp7/ BH‘ÔÏ ìo|ãëïMùâx¡\ò”i{D!ÞeA¢Vv±V¯àÑÇH’4ßÈ–òðbO ¥4i>1âÇScƒY™xô-çûsÆ™…el¥’Å€ÌEƈ¸È ¼±¶Š²ëb{w;{ûÐC­ÑÕtH!`&Ï?§é–ÂÔiàZ4FH‘ä£\òK*HIÄ Œ1ø¾Ÿ½.Ÿ2ÜK©Å| ¥ Š1Sá[ŸØß_x<› ³)Ùû(•¿uá9¨¹Z¹èdVK›Yç‘[¾s‰QŠ0 !•Ä×ßxë?þj•2>¸‡Ã½GYÀO„à(•}ÜxéÍï_yáõ÷4¤.¥Ì 3©/;o¼ñê;ºé¼1œ†$ŠT+UBu ‚/A!]×°Zµ‡Ž{=B 1myÕ“'©ù“ÒÀÙgGæRn1˜ÿl! ó•Nfì°Tn¦K¯ÓBÜ‘••³·¶ëàüÖ&â8Á‡wîa8À÷ý,(”„¶íš†å6v|tÈ“¸C(JAœÉ ¾ÔÐn¿xëåJµúæ`ê£IˆZµBtÃó5Bxš¢æÛ(Ûvvö •‚®ë ¨@¿„,äÚ%- ,û}äà  dZäú¤˜û“‚ñiADb¤ ,îÓykzVX’BBHß÷±¹¶†“Ó|x÷>Ò$FµV‡i»B€i ¶[ªk†¥íÞÿà±Rj¤¦YçŒü2`f zéò…ËïŒÃÔ=Œ•m™Ä÷+ÈöðÍVcE¨VJX«–°ýè’”g™c‹jßœòL ÏÎ}.wi›WŸ ü– \˜KDφ®1Ôê 0¦CJÃ4a;¥saŽ{í½m)DÌ4«åzù²2ª•òúå+W¿ ¢U»½”’ÓµÕU€êH޼ñ ç°- kU£þ)†£(Ó E ¢qžì .‚ OL [ Ÿh!ÀR ‰EÊHèÂeP²Lû3×2 s Q–}|BPJ±¹±Ïqðñý‡8lÂ/•àWëótز]f9Þz{»NG‡ºnp‘m7úÂôg¡ÿÚk¯þŠã8kí㾚†1m6j0mq"¥@ ”Äj݇É÷èšVˆ¾É™È>¹àŒ˜ÇE1s/EþÅJÀò×1ë6.¸Zdˆ|4q&3(¥À9‡a8¿µ‰”§øñ"˜NQ«×aÙ„˜§†e¦›öã;?~,ï›–#E6@~™€0Œ¬7ß|ãÝRÉ»:¤?]gX]i"@šòÌR‚0NP-—Ð,ÛØÙÝ2@A‹Ÿƒ‹ÔðÏo)ê80ž5Þâwv™¢¨+P,„£§¤Ÿ³ØBÉ ¥’‡Õ&ÚÝ#|ðÑèC½¹J³¹nè0-w3 ƒa¯½÷Xð´è >÷zÁ³£Ýºuë¥jÅ•PJÛ½¾J’„lmn€é‚(@ Aœ è:C£la2a4åÍ¡l©T;§âø¤A…"ÁY©…™ƒ´ übœ%fß?ÿžÙýbX£²} ÈZ%„’h6(—<ܹ÷»û{ðËeø•ú\·,›j¦Ý8Ø~p˜DÓ–iÚ‚g,𹻂O J)Éóq²¾±¶µ¹±ñ–ç8N§7PÃq@Ê%õzQ,¦|þ†Q„ªïÁ·-fŒ¦-Ä—|5 êž}à £?ÝdÉœ²‚dîËÿ¼zæëÙûO݉”g”fƒ'x¶÷àüÖ&‰ð!F£*Õ*,§!8t]ƒå”|¦éÖÎýv8O‡¦åÁÓÏ=+øô=JÍl"“8.½øâí_r=·>òøtH•”¸pþ$¦AEF ‚(ã8ب{è!Š’yð",€9OÓrºÇÂ,†F Oi'ÿ$÷B—X.lI~æîCJ .8\×ÁÆú*ŽŽOðã>£Õz3Ï $LÓ€a9›AôO:û»ˆ¤J©ÏÏÒ¨áp¨½úê«o—<ï2e”´{}2 Bl¬­ Z­`8Á…eY×$ç ¿“Qt: ÈåÖ¥tŒ."÷¥t°ð‘ ÚÚ,Ä™¬`Ö#°Ü@r– ›O¯á +,rá"`x* ¥@³QGÉóp÷þ#lïì¢ä¹(UjPȦ¥Y¶M™¦×wîEÁ¸c9çi’ä®@~Ùôòå+×õúkŽc³Þ`,Oú#b.]8(˜Ñœ2§Q„j¹ŒßÅáá„Ù>ARìÑ[Váf+5k8§ùøX67,c´œIfjîÜxEE‘žÑ@—Ä£Oì<*þÜÂús–™³ç „bc}RüÙ‡a0¢R©ÁvKB@Óuè†UMÓ4:ܹ·ÍÓd¢fš DâóŸ Øõ*×u«—.^|ÇqO!;½!ÎmnÀ÷}œ§à<›Ê…e+U"ŽqÚï/vͤإâÌ‚ªY¾Šã4Æh<Äh4Äx2FEHÓ„ÒL·ÏÓ4•§… ƒÒ¥¨¾ØPЧø{œIç¬3‹5æÞ'­à‚ 8®ƒõ•&:G=üäã»Ð5 •jŒi€R0LTÓG­½ý`<8´§IœàŸG@øL„#øÁÁöÚë¯}«äy›¦¡ãd0&ǧC8–‰ç®^Æ4J1Os`*eU×Dëðpîh¡·ôù…¬K0šŒÐê´Ð9êâtØG4Àéé ƒ>‚0猲¼û8[™3J/Š;ŸØgðIÁ_0EÕ!Œ—¹­\R¡^«ÁµmÜ}ð‡­Ôj58%R hšÝ´ÎS²÷ð£Giθ|– xf.ॗn“n·«kׯߨVª¯Ú–Iâ„Ëöñ) Ã×®\DÅ/£{2@šf,pJ ª¶‰h:Ád:Í(}I’-tòòîd:A÷¸ƒ0 Á4º®CcZA`0`L¥`š&tݘû_rƯӂ+ÈÊÏôéåæùþ3(°ÁÌp!À¹€¦ezÈ`0ÄŸ}ø…Z­ ¦gç'˜† Í0×§½öðôhOÓô˜ó4þøÉvGÇ„ÒYFð™jÏ RJòÍ·ß¡{û{üð°ÅoÞ¼ùjÙ/_¶LƒÆ)W­£SL<ý**~‡Ý“¬#ˆ$©Ó4ÔJ6â`Š ¡1m‘®ÍEœŒRž"N9 Ý€iØÐt=;k@Óç3ƒ4M‡®éÙ´p¥0žN0žŒÁ(ƒc;`³½Ÿ@ñ‹Ôr9v8Û‘´˜~?¿ŸƒbVž¤ ”`¥YGÅøàÃ;ÃõÆ tºa‚jÆÊd<<:éìB© ò±öŸ% <3üöoÿ6ù§ÿìŸ)pûù—^úÚw=¯tÑ4 š†“Á˜ôN(—\¼ðüsOBœ F ”B£ “0B¥\BÅÒÑëÏeaP²Ü¿G(„’ ”Á4-èFFÿ†®CÓ5hLÏ»3aùE E  ”¢ä• iæáa¡£¸(&-œŠi&)èX:Õ„¨åT4MS!aZjÕ öññ½ûðË%”ý$2AÓu"”¶)ê IDAT²·ï~ðPIѦŒñ\øÌÂg€ßùß¼òÊ+ßùµ_ûõ¼¾¾þ®®ëLA)S×HÊZG=L¦!nÞ¸ŠÕ•öÛLj“Œ¤y Øð=ÄAˆÉd’+ƒ…ÊܬÃtÄ©ë0t†®C×5èsÈ Ÿ·ƒJæÙE’¦ކ DÁóJЛSxqE㉔…д|Je!<Û¶$3)Y(‰$I²aµ ¤øøî}ôû}ÔM˜¶—±€n€PV N:ýãÖ¶R* „$Ÿ%  •P+««¤ä–0è÷E1 ]ƒ¡kŒ´»Ç „àÍ×^ÁÎaJ)hÃ$ˆàº*¶‰AÿRʼ[sìØJå2,Ë\¸€ý3-›6«(ÎŒ©HÞ®¡Y†0 ³÷Ëg ªÂ¤±âB/¶š-Å…8 ;˜ àòÔ™TE18Ϻ…k• Žz=Ü{ð¦i¡\­ÍÅ)M7ˆÒ|ôÑŸÞpL(M¡ÔgR-|&øÚË/¿qûÅþ[ÛqV8O¥¦itm} \pÜp?S u†Î ¥Bçø§ý!®\:‡›Ï]Åa§‡“þ£P ’•’ ] ‡Ãy,0ûÐ ÓDµR…c9ùØX†¦CÏGÃ-M,GíÅÎ_¥¦Ó1‚pŠŠ_c;y#g‘îÉRjEP¬9câàâ1I*‡¸ˆ¢”R¸® RÜ}øGÇÇX]]‡i;ÙÉ&º)U©zÜí!‹>x&ÛÃß{ï»·Þ¨ÿº¦1 Ã0ÈJ³‰ÉtŒþéEà—+Ѓ®QL‚‡#p!ðΛ¯Âu]ÜßÞGš‹&Q’u×”LÓñœ§ÙÐ| JÑh4r*ղʚ‘1Àì²¥¸g˜Sù–Ù4—Ñh˜·µÕò±³êŒÈ³ÜaTìæVgb?õÄÈ[гH?Š"ÁaY<ÏÅþámï \öQò«yúÈ4M眓{ÜpBM ±À3cg€ê·ÞýÖ?(•J¯((H)‰nè¨7ꈢï¿ÿgƒk«ë`ŒAײ_Ù= Õébu¥o¾õN#ìt³Ù~”"ÌYÀ€Àp8˜§r*oâ¯×›¨ÕêY&`Ð5c^šwõä«Uåû d~jéìÔ!% $Ržb8Ì\Aų̊yÚJ'OôÃ,NÅ8“äûfÀ 4ÛY!˜¦Ás=ÎqçÁ# Gc¬¬­C7l() k„”NûàñN8*"„Ä9 ˆŸ%Ø|û›oÿ'Žã\RH)™n¢þä# CÔjU¸®J(t"ŒtŽ1ŽðúË/âúå x¸sˆþp]cH…a õ’…Ã(+  ÛLbYÖ×Öá¹˦‡kš>ßì9£q)3#H%ç†ÏŒŸ•l…ÙѳÁq¡^­Ã¶²C(ŠÝF8›öåF&gz¤‹ç#ªbþèNmkêõ(¥Ðs=¿?šâà° J~ùÛï@Ó>~¸ .$4FÆ)Êež©a4B*ʤB`s}Z£0ŒŒ²þÌW¼*¬z!rÃçSCgWŽáhEW­v‘3ÅžeP™].aý«3AÎŒRÄqŒ8Žaè&\ÛAœ„¸ÿhQ¢¹²¦€’R3L"¤tw~üˆ'QK)=kuðSàúµk/^¼táWMÓhä«‹PÆP«Õ ¤À½;÷0 BP*{p½”R° BtOÑêáâù-¼óÖ«h`÷  MËè>âõJL¦ÇИ@§^©Œ­Ísp]'ÏH¾qaô‚±óÏgW~¾ BpÄq„( Q­Tá:^vl-¡óLp¦Ù ¤(û-É€ã°-¿M&“,£q˜†ŽÇ»»8hwP¯¯dýJ)Æ‘JY“ñè¤×Ú}`L(MòŒà™t }ê!Ïß¼ùÊÚúÚ_ÓuÃRB Aªµ(¥xüø1úýÁ|Wm£^χB†¡a%8hu1™Nñͯ¿Ž [ë¸ópƒÑ†®!N¥¨¸6’0DGÐ4\¤HÓçÏ]ÄÚÊêÜ÷Ï|{fØÌÀs£s¾tÍŒÏEÆ œ§‡(—|Ô«õÂQ´Ë‘}Qí[tÄ©E, 8Xì5 sá™Ó)’4…eÙ°L“ɶwrzcLÏú(e,ŒBlßyÿ.€€YûØ3‘‡?5.]ºxkuuõ{†¡ûyE¿R†¦ëh·Zèv»ù‡n[6*•*„T0tàt4Á~«ƒ’ëâï} BJ|tïqæ 4† NáØ=EG ¨×š0-Bp,u|<èa±²‹î^-§^Î@2ö’c:™€i:Ç…F¶wwpÔ롹ºÛöˆRJ1¦‘4M­“nk2êï˜ÏNþÔ¨T«çÏ]8÷žašõÌdKÂ-¹°, ƒþÛ;»)‡„DÊ9jµLÄR–¡#I³:A÷¨‡×®à›o½ŠV·‡Ç{­lÈ3¡ˆRªï€œ× ¤OÆhÔ¨ÕêH“q#MS¤<åœ#Myá9ŽTð¹Ñ9ÏžÂ~«ÇõPò«™òI)Q æd2µwÜ0ÌKÅi¾¸@£lõús׿gYÖšBA)¢”„n(•KRâÁ½ùT0q’@×4ÔêµLý£ šF1žFØ;ha4žà;ï¼…×.â΃Ÿ `è!!4Ê.D!#hºŽ( 0 4ë+°L a"I’ìJS¤Š0娔]ØÁx<΋D ƒá†iÀs=$iŠ(‘ÌŒ$Ùý$A’,€&)>B’³EŠ8‰ ”Âæú9”<)O³ìBëþjÉøKã$Ôr|PPd³gŠÂ`ÐÏæ;(úýì·:0-'«fõ ¢ôñx4=||ç€A. }êòŸ^É#I’½zýêK®ë¾I#J)%•$JJº×ó@Á£‡27 éJ! ³Q±U¿¡ ]ƒm˜†1¶÷Ðéá­×_ÁË/>Ç»‡h@×.‘H…šïƒ Ž À(CG™Þ`0ŒŒN£8F'ˆóU' 0d ˆ3FàÙÙB3 DQ)8Ο»€J¥ ž¦ó@©åôn&óÉ Н+¹¤Ì&dó3WS„aÛ²áX8çx¸³ƒ0ŠÐ\ÝÓtB”’L×ILéƒxPG¢3ÙÀç Îù|KX£ÞØj4êo†áf«_)³Š›ë9Ð ÇGÇèvò’ÜÕj¶í@Ç2À(Á`4Á£í].ðÞ»o£Q«â£ÛM¹+P” á{H£ÌК¦!Š#L&Ól 9ÓÄqæ WZd…$ž3Áœf/\FµRË€Ð<\Œ”YJ甦æ T>, K™@Ö.МÂ027 ¤Ä~ëÇý>+ë°J)E#a²ÁI÷pÜïíçH>2øS °% Iš”._¹ò®iYM‘šAfr«e™p\RJl?~Œ(Š²Ù€$›³JШ×ó®cBJ…£Ó!>Þ…ï—ñËßþ¤R¸ûh)çÐCp†ŽŠk#œNÁ…€¦3Da˜±c¥ˆ“qç. ³@šÎ‘è?{.ŽCBqõòuT*ÕlŸyJWœ[Ðü¯cf’´š»H5?Qíô¤B(\·J2×¶{؆aY¨T³f"¤4ƣ᰽ûà.€AÎŸÊ |ÚQ±€œŒ'ôæ­›ïX¶u5_ Då „Âq¦‰ããzGÇPд¬4LËD¥’µJ1Fa[&☣Õ=ÆÎî>.œßÂ;o¾Šáx‚Ç{íY ¦±@Ùsà&ãq~D}æâ8ž~_¬øY,/VZd‚ì~‡0M ×®<‡rÙÏ€œ©ùŒ¬2åG-j@K+PȼÁ‚!f¬@ŒF$i ×ñ`&Ò$Åý$iŠ•µsÐ4=Ãed4òí;?þ0…Âü‹V×VÉt2rîܹ«¥Ré Æ“R*E@„JÂ4Mض±Àö’8ž7k&q‚(Šáû>\Ç禩Ã4tL‚»û‡èãö 7ñÒÍçpØ9Bë¨Æ(„Tˆ¸BÕ/A'™>€|,Lš$HâRIp.rãg†OÒéŒÒe÷'!‚0ÄÚÊ:®^¹Ã0sI¸Ì7£.Œ=3èÓÈò×àM@A`<Ás=8–(N°¸ÑtŠÕµ-– ¥¤¢”’Éd„öÞ£‡q8me¢Ðœ~ªtðS #’»DZ¾u~ëÍ\R Š%óc[5X–Ã0qrr‚ã£ã¥ AB*z­M×!‡k[`LÃpàñî. ›¯½Œs«x¼×ʤb!æ)~ D¤¦Á|âG¶=‹CJ‰”‹,(L¢\ÊAPþRž`LÁ9Çí[_ÃùsÁyú4ÿwÖ.Ô?¹3§ ½sƒË3±!H’½“#¸®×q‘$ ŽOOÐ;À¯5²MsèEa@û'GþQë!€QÎ?uð© ”"¥R I’ÈápÈ/]¹tÛq후"eFŒBH(© é:\×ÓööçÑ;ÍÓ¸Y†P«V炉g›Nc<ÚÞ…®kxû­×Pr<Ø9@%yI ¥ih”=¨4rfJä<âRRdBO Ž¢Œúgn!1žŒG!.]¸‚Û·^†eÙ’ÏW¿*Q-T¹WPóyˆªò-1Bž-È+¨¬ñ„sŽ££LÓ†ë–HL¦ìµÚ0mÕÆ H> ¡ N{ýöÎý;y0…ÒŸÆ |jÀõ\…Ï+Õêú›º®ÛBÈy0˜rÆh–§{‚ ÀáÁ!”’ó"çAÀu”Ë~6“8¶‰4•8:`goõZ_ãUp.ðx¯)$£âL7Pó\¤q„(Ž ƒ¥!Й>?VVò adêašÀÐt\8w ¯¾üf&-§é™ à',°™¸W,‰œ äœ q(È«—''Ç€J¥2ô<£y´¿ еõs`LËbFJÉppïÜý³œr|1HâdæDë°•^»~í5˶ÏÍ"‹€¤ÌŒmÛ6lÛÎFÆ÷ù?,ë˜åî¾ïÃvlpÎaê:lKGÅhw{h·;8n ¯¾t Ói€½vwޤĆiÂw,$a„8I²rnac‡©ð7û;,¶í ä•Ñl¬àê¥k¸yãEø~œóEgz²àéÞ€ÌÅ2¶/Ä3úŸÇ ²ðþ ƒaa8…_®@7LDQŒÖQ q*°ºqšaÌÔD2œ¤Û÷ð$nåª`øÓÆÏdBH¥R!Q)I¥R©û¾ÿ¦¦iºÌOhœÕå ¡Ð4ÇuA(Aë°…8Jòsû²kR Z­@׳žÛÌ6~L¦öÛ8îàúµ+¸uãúÃ:ǧ ùÌ€ °l eÛ@Lò4Ûa„ÅJ$„ÀÐM”¼êµ:Vš«X[Ù@¥Re Bð¼L –zÕ=|â'­–5‚B;šRX¤ƒr&eß3 1Q­Ô`™Ò4Ep‚Á$@cu¶íä*¢"“ÑPžµvƃ“í\(Ö>_䢤”@zxЊ.^ºð‚eÛç@”œ¹‘³€¦ið}q£Ýje-PlA×ÓéŒj¨V+Y0'%\Û‚¦eAáöîƒ!nݼËΡwÚÇI”˵ ð<%ÓD0"å<Ÿ6Šlv°*jrj–|Þ"&ç!gêÿE©gâO˜c¼$ å-i dï7qÚ?AµÚ€eYB`4¢sr¿Ö„WògoF‚`Œ£öþÞéQëAUP~îR’••L§S(¥BÃ4¬z½þ–®¦ÈŽà¦ rZ¥”Â4M”}ƒ~½ãÞü°&’Ïå¦S¦r¹<×à]Ç¡ÃÑwv„n¿x«MõNq:œd#\%r…rÉCÉÔS¤<)t÷`.ÕΨ:3Š\øêÂìÎb3Èò>€³Å¡O^|Å4²†Y{úd:ÆÑqµJ¶íJ"JÁ+ÕàWëùn5‚8‰µÞQ«ÛÝ< ê8à™í Šã˜0Q)dÚít§›[[××¾J¥EW©jYaÇqضƒ£n£Q>*.ßÍ3+ËÚŽÏu!…¥€k›PŠ ?š`{g‚ ܾu ÕJÝã>ÆÓšÆÀ…D(ü’Ç0’4šZjàWêiDž™³¬òÈb3ã4óýgAP¬ ´Û‡¨ø5x^JIDQ„ãc˜n µúÊ|{½n{pøøî‡Nó8 úB óâ眅¤Ñl¾a†§¤RRe=ÚRf:³!ОçAÓ5u†ábÞ¥ˆ£a¢ä•a;ù¦ FáZ&¸PèGØÝÍN!ñÖó({.º½a 1p¡¦eÏ…›ƒ M“|É: †!Ovú(,Wõˆzº“]2¼"s!YH8B…¡Å¯¢Œ"Ž#¶öáûø~J*Ä Gçô̰Pk®Ïf*.9>jöîÿäƒ\œä,ðWŸåæP"„€ë:$MS1è÷‡~ů”J¥WcTÊÅ-„€àb~r§_©‚R‚Þñ1’8e‹é]a"Ibø~–i§) Á±L¤\ât0ÄÁÁ!,Ë‹7oÀql÷ú™FÀ(R¡¦žëÀ3u„a€4Im^ª¼©å O ;ذ´Â—¾*,K!# =„­€Œ2Dq„ÃÖª•*•¬Y&å)ŽN ©ŽÆêf–1àBãîá$O»ü•Ág=%læÛ€ð`o¿¿¶¾~ÞqKÙɬaD)€ ž[”@ÓtTªUH!Ñ;î§iáÔPä+7…ïûÐM#KM ¶e"á½Ó>Z‡°ílö€mèÅé\¢ä9ð,Q"NâÅFOu†® 5üùóªØÛ‡¥öoU( Îɤ°mÑ šwÏf&ç/PB1NÐî Yo¢R©!k°MÑëwÁ¡¡±v>;¶&@¯{8Ù{øáÇJÊN€iÎü @6Ñ$ùà¨Ñ4˜†õFã¶ašU$ëQJ!M9”ÌÚ­4M‡_ñ‘¦ Nz'ùpåÅH¸ét .8J¥RžrX¦Û4§ǽSÂ0M†Ú)àâÄ“¥Î dÁ_¶Y„b<¢Óma¥¹¿\™ïW8éŸ %:«[Y5cPrrÜ ·ïÝM“ø0OgqÀË3W`;ái*§“ɉ¦i̯øßÐ4*%‰R3&PHÓt¾*tÃ@Ù¯ å)ú§ý|œ\>óOfÅ)%J¥lº‡–™í ŒbŽÞi?«W.Á0 ô#$i!®àØ&ªž ž¦ÂiNÅdnô¹Ÿ/¦‹(yŽ™ñå=Å6qETAB.nUùÐ)ŠÑh€îQ «+(—|Á3 O‘(ŠæÚ¹9¸ä¤×:{îEÁd?O'?MƒÈg Ò$!'•B0BA¡\mê°ŒŒ NC´ÛmX–…+—/Â0 Ƥ<“–…P„„ff R 3ñ …¤ó•»´úósv˜B*¹¬J¹h/‚$?³`0èãø¸‹µÕ ”¼R¦­(‰“Á)Rhh¬må›b€”srrÔ ¶ï݉¦ã]Â"ø3€™@¤Hæ ƒN»Ó+û~ÝvÜ«Œ±ù¹¼TYz(ó H7 ø• ‚Ñp€$‰ç'€K!LH xž—mãYO¡ihHSþ`ˆN§Ë´pñÂy†Žñ4@šf5 ©(¦ eU¯ RÑt©òWtÅL û_Z¾ BO³&¹ñ±PÿfÿöÌýeÃ#ºÝ6¦Ó ÖV7aZV¶™…§8ö¡tµæF6"/ïÆ:궦{?ü(ƒýÿ¿½+ëëÈÎß©ª»ß^¸Šâ"ɶä™AžHžòŸƒyK`ìÉL"Kq<í"-nÍæÒûÝ«*U·û²%O ±eË /Ph±)6îíúêìç| Ô®àÇÚ´[+­•šžõÎÎZöf÷ã×|c%ªªœ'J8hwÚpÓ ’¥†ªEJ…ÙljƯE‘méVð<Ï1}Fœ€1Âîî|ßÇ,ÍLTÐiHbhÇ!b׳Cô\4¬¨ÅÔÛV¾Øø·UÀ"ï¯ùµhK· 0ÙÀ߽緷¶ç¶TQäNGA ݵ­y×s% _~ûm‘¥M¾o4ðCJ)-„ ?ð)ϲáÕååy»ÝÞñƒ`§nå2ºÐœ˜ª,ç¤Óœs´Úm„Qdjèg3û>3!ßYYIDqGH%Ṿ碔 ƒÑg½ÂöömDa€,/çä Ž´R¨´¢í0‚¬*dyj»ƒõu•07ê°(îhªÕ4Õ<Ò¨í†ëkjÁ¼Ç!ËRì¼D§ÓÅÆú&ª²†ÉñÊ-´º«óI#EYR¿w<=|ùÇoÊ<;²`Òˆ~4`Á"hÇqt–fƒédrÅ­=Ïó·j¯æÕQvĪ”Ò´€1†0ŠÐj·¡”Bš¤¨dekMu±,%Â0„ã¸F¸Ü4Ÿ*`<™¡×;EU–ØÜ¼…0 PÊJ‚¬ V( ‘ÂX#¸ÜøåeUÎK¸›®|ƒv³ÕR%psÍÕ@s0E²UugýSô/ΰ½µƒVÜAYæf€f2Å0M±~ûQ Z+0Æåí^}óð1 { $• p­}À48D*I’‹ÑhØ ‚`Ã÷½]Ƹý’E”R*Û½+¡5àù>¢Ø¸€uñfmV'iŠ<Ïá>\Ï…ÖŽ`ð]JÓY‚þYi:ÃêêâVŒ²2Ÿ_³‚TÖMT$x.BÏ$dв°÷Ð ‘|Ëò_R 5@T ‚뀨ÿ/‘Võzÿ¸àØÙÞ³C$Ì}Í’)¦¥ÄÆÎ'ð<ßöD%é‡/zǯž<з7Ü@õ± NòhÇ €ÊÒôâ¼ß?ô|?ô‚à>¢Î̓Eu¹îççB Œc„Q " ,KËH*‘¤)ʲ€çysÁø†“0I2\œŸ#KSÄí6â8‚Ö@U©9‰ÀIB¡ …}èúp­j)ËRªæä‡ÆÉV×^1·üõ5,¤ÙÆÌé?99ÂöÖºÝeaT„”Hò Ú‹±º¹3Ÿ.®¡1™Npúfÿøôàù×¶(¤©>J 0—RJÅ…¨”R¥”òüì´wàxùžÿ)ܱ©ce–êÙûu 7Áu=Q /L ]:œÐ ‘IDATÈVܦiŠÙÔ”„{žkIŸ ¡ï€1$Ëqqq†ÑÕ%\×C· Åk%çY?b¹&Hmrže87RÉ´•_sý§}¡ß¥uÕ|:É\(“ÌÆxõú9?ÄÞî€LjÝÚdH«­»ˆÛ«€VàÄPÉ W}u|ðüùùÉwßZ  ˜ܬ:¨©ÔÇ—ýþ¾”rêyþ¢ÃѼƒ&j(ë eefûƒã¸ðþ€qFYH³Ìŵs a`Ify‰ápˆñhb„ {i]2j¡br¥PÉ‚×N17÷SÙJ§F¸nŠ|}ͬߌq›$3¼Þ…¢(q÷î'ðýEQX)(‘9”ëcc÷\ǃ6Œäº¨J:>ÜŸ¾üæ?'“ák› 6EäÇ, X­51¦Èè«Éx4|3 {BðÀqÜÛœs®ÉH[G·‚%c¨Gº1!à¸.„pç:Þx .˜×‚ò2C‘%ÐUe¥ ·ù¤Æªf0Èêó²*‘$ ®®.qÖïÐØÙÞ5°Ui MhTÖóhmî ³uϨ8K:)•¤«Ësí?{}²ÿì+k-fø º…\“Æ–Òš1&µÖ€Áh8|=O8猈µ‰($KñaóµoNJ-@))ç£àìPYJÇÙt<îqNp=× |—ÂÀÓŒ äEIãá³ÉÈ9N"3ê½ÎècànvᚆÓzÇG_Ž—ÏfÉw§'½×Í„#:žçFqPø b:Ër®0^1 ÝiÛj\pžà'pf€t7à·º . aŒIUåÐU9Os›qö†Ï€AI;¥¤,Q檪‚"t¶î`uïWpƒ6 Ì^Ú9åíúê·˜ .¾pIDW’°÷Öÿï.tÿù®ºXDäi­}!€À €u;­NçÓvwåï·c!cä1Ï& ò<ÏÓñèppyù ЧÊÙ©=%ñÚÚÚ_ö೿ßÜ\ÿ›8ŽÚЄ‹Á§ýŒÆ$íÝ»¸÷Ù¯°ºqŠf‰öÌ,½Œ™Ih'™k YdÈgdãKɲÌ!ËZ–ÖÀ3cåÌPhîzpƒ^ÜA÷ÖZ«[Æ~QÒª‚àBK)é켇'ÿýÙWÿöOÿà€€#[Tw½wYøÇ€æý0€CD®B`ÁèØÕ¶?ÇÂq"h°ª*3k¥K¯“FÕ YPÝÝÝÛýÍ»»»¶¶úWQwòRáìbˆþÅf³q»‹Oï?ÀîÝûˆ:k(*`2K熙±ÎD”°•DzÌQåS”é UžBU%´–¶ÞÑPÚxQ„°³/h›YÈVÂ02½ 5åÝ`4ÄÁ«'Çÿúÿ<ºì?Ð#¢c­õ©Lìsé_:š÷TKÀàÙå/­€koéUÖ(J%Su „ìgÕ’ewgwç7÷îîý]§Ûý,Ã,—è_q~1@^–XßØÂ§÷?Ç­;ZkÈ …Ù,1ímŒÍ‹‰ø5v1¶D:]¼r[î6/³õ†Á \„ÑxˆÃ7¯Ò§~ûÅÁ“¯¿pJD=­õ1€s,æTK…Š¿8à‘ÃÚShlhnWf׬qÂ'VÜîÑ +¯Û©sKÅRØŸG“ñäÍááÑóÁ`p(«jà¹q¸QxB˜Qµ'LJ]ƒé ï"nw „3ïú©ÙÈêpq­ÓÞb«û ë$›Ï¹aCÕZc8àÍÁóÙ“G_üî»§ÿýVÜ_Ù¯Oþ_¼ù«ø>Û ^¬¶–^™]´š8Ët¬M[£©^ÚVç÷öîì~¾¾¾~Ll'Yµ>K‹NQ•ç›[[ؽs­•[ð¢6˜l!g­ëöÖÆÓuJúk‹ÙÉ)0u£+¼¸|úèË?œ<duþ•=ýgËÆßÿI¼K54ÁÀÞ½$=–çé5ÿþ]¶FdW @—666ní€ñ­JÓwÜûÄŎ븼»ºŽÎê:âî&Âö¨?m’Œ!°â½I[w0×ù„$áìôP¿yù§§O?]œ=ƒ©ý¿Ñ9´î[)0YrýôùRñ Â2(–÷ö·ë_Ò2ˆjiâ.Ù¡E-%ºÂq? âί]Ïÿœ çã¼ã!âVQ{qg Q«‹¸Õ…ëp\Ïžîšú×&‡•Uéxˆáåú§‡½Þ›—OŽ_=ùÀ1€1ˆÐúÂa°´ù?hPä/ïû ú=¤JNølŸžB À޵xAô@gn Ç]w\~#ˆÛ†Ñ4ˆá‘‰0"f‚?EŽ"O‘&S5^žÎO÷ûGûßxÓ8å+îKµÍÒ¯ÿ×øPê…7TÄ2 šÒ!°¢?ˆöÏßãœo‚Ø :DsîxÂñ8‚#hBÉ*©Š<É’éU:›œÚúþck¼Îìk½FXFñcmþ þwõÒTï’^C5,Ç)Ö×_åBtj(פ¤,dUÌ”R³%‘^{5ÓÆÆOÞË2 þÐâó ï6<ù’Íà-Å%üÆû®Œcÿ¦þ\Õ0Pk÷6]rk“ÆÆWK‘>ýc?äÍõþ`àå46Û],r—\VV×Å,Å7Šê8G±´ñ?:ð ~\5Á—¤oØlIèwH²qÚßE¯?äÃÜ\?ÜxD#.Á¾çßË®ªú3ëƒmü ~ºøÄ»<Œï‹Oè}âoðóÅ&–òç6Yÿœ7zsý´ßµ¾ùjn®›ëæúy®ÿ<×Ñü&íIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportModeSlave.png0000644000000000000000000000013215101070305021031 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportModeSlave.png0000644000175000001440000000146315101070305021025 0ustar00rncbcusers‰PNG  IHDRÄ´l;úIDAT8Ë¥•KLQ†¿)ÓŒOb QÔ…1FaM"hFFkâÆ;—®  ÆhBBˆ CºÂFwR\a->P,hiKÇòh‡9.¼Š¢žäNî¼¾ùϹÿœ[FéÐÿ¥^ÖDÂ.°¨ø×ø–©,©T ‘pجÿ/õSS! ‹HX~Α°õJ}™ú€öWà™\Xr9Ķ‘l™žF2idt4$@ýÄÄ^*s-èÞäó8à‚+ j¸=Ý}»\·ÇiHéúÐ>`øþó?€{Ÿ¾$> â±PˆÊÍ}twÃÃG\^J‘ÆM‹>ý îÅvà(p¾³#$gN#ÀU 4$‡$›Eì,bg‹kï[ͯ^˜Àæ¾g{Æïw¼ãHón ÃGí.ƒšš³¬ß0ì¬-©\D[n·¡ÁÃ…LæÉ$T˜Ü fø”.ñÐÈÁC+àeÀ ë¼xÞ ùù!æòðƒß¯ ò+XÝTÇñV=0äØÑ—ƒ§3®R4[€9m ²—¥ç-€Z Ì®p…™LQºË ÍE …;÷¸¢Ê¨•´›±ØG’ßV®rpkÚÉÅóhÚ;¹600p±¥¥åà¬æŠbÛí¶©zøÚn×=^gZ*}èxÀuàKÿ Ã0¤©©é0®ÊQ(6Mª³y÷õ¶ÛuÖ:Ó"…ã'{ß›¦éøýþ¼ã8n:öƒA·¹¹ù,ð°K™{øª,4V4¼ô/ ŽóáýyãÇãå“““ÆððpyWW× 0õ`ÔX¼fÅ,Ú;9hS©äüäÄ×€ã"¢‰ëâŠðömÜ𢯱ùI{'ÕJMΊYùTjJ/¤ªj›fÛß±í,===w·x«ýú†ªû¼jŸu@UkkëÍD"¡G"‘»ª¾¯±5+VÀ|‘›cÀX2™œ‰D"·€9 ®à3Ú?n8Þ6¦[€jÕ+^Ïø:á^³ÈæIIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/qtractorPlugin.png0000644000000000000000000000013215101070305020213 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/qtractorPlugin.png0000644000175000001440000000246115101070305020206 0ustar00rncbcusers‰PNG  IHDRÄ´l; pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<¾IDAT8ÕKl•EðÿÌ|ûÝwŸ¶¥íµ-Å–––V¥È¢ Ql@Á@P“Bd¡ 1Q*jL4F‘Æ…ÂJܸPRcØ™ðBh±VJ •RJÛÛÞGïëûîý3.šCÁ³šÍùÍÉÉ93D…‚BO;°~QyÉÎùØ…½§««Ë¾gÎBð‘·_ZÕ¾¦ídA¾¯.<†"Ë ’291{ó™í‡¾ù_ðÚµ%Þß;66^:py¶m^ +V¬ä±„ñî¶]GÞÿ¯\z¯j·¬[ûadKoM†¡* ÛeÙH&S8þªÉ¼ëËÏîx ˜âydq¨m|bju,€;°²,Ó†aä00ð; øä÷utx¤âBÀS"33«ƒ[ˆààÂe9plÝÈ":3QZÛTräAà•ˆ¢¨ LCGj6CÏ€pÇá°m@8@dfn^$„°û…un ˱MŒÞ@rvY=ƒœ™£œsذ¹€•Ó‹ß9¸cõýÂF2•HCŒÞ˜B|6 Ûv@À@@ÅÜ@IÈè:±l§eA˜"í|®á­xôzIQq≠8` `ŒÁv8(\šBfÎ,YÞöló±ÚšÊ}c7'¨¦ibuKŠ ƒðøÜÈØ^Ô7oBqy3fâ<¾B F6kÞnH°¡½~™$Ë÷œî?N)ÞÿʆuG¿:ó:¥”úƒÕ»Ú7¬oúsº‘f¢uõc˜š‰âÊP/|þ¤ÓqLÇSÃwU¬©þ²ú%Õå‡^Û^ÖÚÒXYSR\’v:K}öéß~hqžZº|9BÕuˆÇÆŽ&¡‚¨®[…¨éEÔ)²øñlÿíBÀí–[;÷lÝíRÕ@*•Æâ³îï{Î|b˜Î(€ÌMu»7nÞ| OD¦rS‰„VPT”ú®»;óõáÃïÕ45=µóÔ©ÍjEE åå.\qu~ŽÁ ïéeµÞ¡‹P šÇEU!„™[EÔV>”?z}Ä‘/ŽÐ¼< ÀU9X³mÙ æ C(¸}A’?ÿÚÛ£¹œÊT&+TK±3FÌ¡’Â>ƒBóxØÄð`iÚí5¯7  þf_ß™Ÿöî­ëëL…ÃÃ.Þ1Ç„bKÿµÙ€`þïâ’¦µÚ†1  À°ºûA˜‹¿Â,Ö#g»IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/pluginSelect.png0000644000000000000000000000013215101070305017633 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/pluginSelect.png0000644000175000001440000000217715101070305017632 0ustar00rncbcusers‰PNG  IHDRÄ´l; pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î< IDAT8µ”]hÓWÆ'iªqÖºR]Y,u”U7§ RÅN]ÅÌ‚+Î qU( èE7½Ô+Ç^ Ö¦ Z7”)8TèæDim›µøÅêh›¬6IÓδibg’æãÞ]4ºÙªÛ.|áåÀyÏyxxxžW‰/¢L/ÈzÞP)µX „1þ3²ˆ<µm6Ûû'Oœè»}ëV¨¹¹ù^QQQ-ó¬÷3ûé—`:~üømÑZDkц! ½@Ù~¦Æ½½½x<®ˆÆbÜééªéš¯”zI)¥þ—Æ"¢•RG·mßþæ®Ý»_Ãb¡°°pqå®]GŠKJŠ—/êîêò~רø1àÿWÕ‹¥ª¼¼ü냇ý|ëîÝpÌ0$ªµìp:ëJJJrçåäX²²³1€4`Úl&­5áp˜P8Œeþü\ 0#Ç@XDÆf1*=~,®µDµ–ˆÖ2a2 j-wFF¤ùÌq¹Ý2 Ê Ç#õõõáÕ¥¥ß7^½Ø[SsØ4‹108èñD-Yb52, ¥0€¡¡!â~?v;[ZðŽ’Ÿ›ËÎŠŠ¼w·ný ut”=µµ¯Ü÷û*¥úEdüŸ®ðz½“@HŠ!nô´µ±rÅ ¾lhÀºlËív¬YƒãâE ¢Qâ"ª¤´´ȇ'#=yÏãO) ©I¥ôùزq#ç.\`õ¶mh‹…6™Xºnßœ:%ï¬_O÷•+2<0ÈðúXDdÀíŒ !B!//¡`#+ëñ, øÜîÈi‡£Õl6s£©ÉÕÒØøí#û=_::îDb1If'æÌ!c5›uÜ0x4›Ôܹf›Í– 0êó5çE$5 x<øý±±èT†mpbB_>}º½³³3R¹cêê1­§YÑtzÞ±cÇÖú|¾„Ûí>'"éGX3#=0ìõN&”"Œƒ±sÇùÚÚÚýùùù=p`qK }ô_¾ÌÖ… UùæÍÙ—.]2ªªª>}"â3’7÷s‡£ÿºÖrUkù!‘Šýû^/++ÛÛát¦#‘ˆ„B!™œœ”^—KÞ«¬LÔÔÔH{{»Þ·o_= fú™úèðáûqXž´Õʦêêµ?67op:M+ÊÊÞ.ZµjgŽÖ¸ûúFzoÞlCël»Ý¾[)e«®®þÄ0ŒpdÖú­»ûƯ­­o="¯÷Ïžk×z@_W×g}]]u@ ˜Èœé¶¶¶“ÉôÅÔÔÔË.—+o–9^¶€eL^=w©ƒ²Z­{ ¾¬"2ýáEÔ_r†Æ22$IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/viewZoomOut.png0000644000000000000000000000013215101070305017504 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/viewZoomOut.png0000644000175000001440000000251615101070305017500 0ustar00rncbcusers‰PNG  IHDRÄ´l;sBIT|dˆ pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<ËIDAT8•mLSgÇ÷–BQ‹–2|a" Ù¤¤83!¾Dg6£Éb6—,qâ·%3‹‰ûB2Ý—-!‹,›„—“Q NÁŒ•0µ€TÚ"ÐPè‹LK¡”öÞ}èe! nÉNrò|¸ÏýÝsÎÿœsþÝTÀ* 0Àj@¢@pcÀK ÈÂ+€‚ÕÅÅÅGN:UµsçÎ\½>/ ÀçóI< Ûl¶‡wîÜiî)ˆò«ÀiÀk¢(»téRÍÙ³ŸÌÍÍ ^¯—P(€N§#''Ahkk‹\¾üõSSS×€a`~%ðR¤Õ ׿:xð ®··—ööŽX__ߘ×ëuÅãñ¨ÑhÜh6›ß8zôˆÖh4âtö%.\¸pmrrò*0²R Öï_¼xqìÅ‹¿äë×–÷îÝ뾪€RàMà]àãÒÒÒßš›› ȵµµQQφ•¢-***úÞnß¶Ë•••£ÀGÀE¸ Å3\ jÛ¶m6‡ã©««S>|øð°KL«¢ãÇWI’$tuÝ‹ÛíöŸ€?d+®S´x:<<ÜØÜÜâ7 ”••mLi)ePúòòrÃôô4~`jݺu»t:1‘H¨e9)¸ 2D"“¡PÈmµZ¯©9ÿáæÍ›24ͦå`HöçšœœõêÙÙY‚Á`ÐÛl¶½^ŸF…D"€$I„Ãay|||ñĉçFFFžÎÏÏËjµZ0©`iaaôôtT*•ÈOžý©R×Ô@$ Ì”••ÉÌÔ333LMMyS/&€ÉÞÞ^oeåþ­{ö¼·¾±±±(×>å¹¼LAôÀÉ“'ß ƒŒŒ<Åbc©eHг³³’œN§|æÌ™iàÉ]¡4$ÛmÉÝñÁ¾}ûº¿ÜÐÐ [,P¡JN ””””Ðét·o[UXX¸Z«ÕšûûûÕ‰D"“d›é`uuõguuuæÇ‡…›7oÎZ­·jna4 0[,–s--͇ÔjµØÑaÇdzFC ”îÞ½r»Ý¾X,3™LyUU•‹Å’~ÿþ}¬Ö[ õõõ߆Ãá:à™° Zf±X>¿qã—CyyzÑårÑÓÓƒÃሙÍfõÖ­[„ÜÜ\´Z-¢(Fñù| ÑÙÙlmmý.6`APÒ/·X,ç[Zš ù¢Ë墻»›ÁÁAÉjµÞöûýÏvïÞ½Ëd2fei3DQ%<þ<>11p:÷ÆÇÇ­@"p em¾UQQñeSSã±ü|ã?P§Ó)Ùíöv—Ëu…äžÍŒJv"0LÏ”3²¼kà¨Óù°Åh,H÷ûý8œN§ÔÖÖf½<fIŽû’/µ¦,*çR‚r)ËãñlÑéÖnr»=BŸÔÑao÷x<ß(З $¡@•tã,û¥š ˆz<ž‰@ `\»6«à×_[[].×ÕePi¥ÿË–6ZÉ0*5UÒÿ_P€¿ •Eä©3nIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackMidiOff.png0000644000000000000000000000013215101070305017537 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/trackMidiOff.png0000644000175000001440000000123115101070305017524 0ustar00rncbcusers‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIMEÔ,‹îQ &IDATxÚÍ’?oÓPÅÏ{ví¸®Ò¦ÂBRŠT))´‚¡ õÀ êP± &>#_„ï€ ˆ¡Ll”©Ciì&nÓÒ:Nâçç÷‡…J-3œéÞ«{~:Ò½À¿ùs°¶¶6ãûþŒçx0¬ !¤m™»úãööö×Ñù}ã|Óh4ªφcþ†ó è.Dýø†Y'P÷n.×\­u³×ë%ß÷ç‡ÏS.Ÿ:î\e/ìz‡'î~t4ý½¹gµÃVq¶8½°´´4Í9ÿÒï÷3 ç¢û݃þc óò핺}­|•:¶ Ã4@©AÃqáÓÖÖõ0 Ÿ,..Þ=ó™gEÆóG‚ÞA3´XÆÑþÑãR(%­0ìããã+óóó›>\ð\UwÛ”ó|f¿q…ÓÁÔj5uttdËå2ZëT«ý~Ÿ››šÍ¦T«U´ÖV«õø“+à}†³ÍfSR*•¤R©ËåRÁ¾ïcŒáôô”jµŠ1& ¸N"WDDæææ(•JÌÌ̤‚ƒ `~~¥Tr¿G‡žc­M(¥F‘¦×æÒ'2ŠD‹s¯GŠãÏóxxx Š"Dä‰;k-¾ïÓívq]wzðÚÚÜÞÞR(Èçó8ÎÓÔ ¸¸¸`kkk2x¸Ôz½N½^ ÓéÐn·Ÿ9¨ÕjT*•é'µ··‡çy‹E<Ïãúúš0 ±Ö²¹¹ÉîîîtŽ“ÒZ³´´ÄÂÂQq~~Îöö6…Býý}Œ1‹¤•Rh­ÑZ#"ôz=ŠÅ"ù|cLª¼’^¼;Ék•çÔ£cà³ÜƒåPÖò´óÙ¤r>Å0¼ÈõO+XmîÙ­v?3nöð!P ª„Ì;GÝ„D€+à:Ú Ú¬ãÕ£)¼ n ø+€¬Ã6ÆýÞíqÁQÜ[ 8çžóŽc©ÖqÎiŒ7p¸yè¿ÅØÙ¸KÖ=Ð.ø‘âXެKÊ’òݾ"•¬ñÃГc¼'‡Úž^f@F8ý=XÆ•?Ÿ.aòOèÿÅ7Òf™iœI°ÑIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportPlay.png0000644000000000000000000000013215101070305020057 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportPlay.png0000644000175000001440000000041115101070305020043 0ustar00rncbcusers‰PNG  IHDRÄ´l;ÐIDAT8ËíÑ=AÆñÿ# §P9;(ÜCVBãb]B¡Ñ¹ÀÖ •f%HtеɫX_Ŭµ O3“Iæ÷Nž¾“Àì#èly4³*ê®ÃøpfÜ9€ŒW<”*ÃÛ}ÃvŒ£ Ü÷‹÷)"³oé57˜z«ãnc ®ëÌO-^ È©"ŰŒÖma÷·´Y!A˜¹ðœ*]dX =Ó†$X„þïŽiñïDÑuÓ÷¯¢ Xöýá’`1ü&X‰ñÏÏç¥åM«ò³%IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/viewFiles.png0000644000000000000000000000013215101070305017132 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/viewFiles.png0000644000175000001440000000160515101070305017124 0ustar00rncbcusers‰PNG  IHDRÄ´l;bKGDÿÿÿ ½§“ pHYs  šœtIMEÖ :;w°06IDAT8Ëí•OheÆ3ß|3ßdw³Ikt·­·ØTÚ"(EâaKp6 Š9‰R[ª ‚àEMc)‚¥­RÑZëMу¬ØZ´5­‡4mÐdmcbRȺù³»Ín²;³³ãÅCZÚf{ïs{ï—÷á}_¸£Fä8Ž\éÃáp(?—J9‡Žÿj©§§§÷fµú- ëŽã(€h4šŒÅbûú?ýì“ú”)ìB¡Ð‰DºQ½q áyÞöL&³%›ÍN뺾©­­mçž=»¼òÒÞ¦ö»[´ †¹2ýO>‘zull,ç8Îétºº’£­4ÝÝÝ‘‰‰‰§|¿¶å‹cGûLièG}ìÝýLhûæ}ròoNžddtœäc;H%%²îá·ƒ 8p}ƒ×€•Rï½³ï­_ë{¶Õ¬ç5ŸŠëQX˜ã—ÁaΜ!~ïzz“X+§°Z;izà…7wo9 ¥Té—{×°8D€Ni©Â¹¡ '~AXQv=ÝÅýkfîE ¿Š qÓà¯kš•+”ç/syú*?ž%»àóx²‹G6.£UF \BH”&†¥n‰ïÓƒédcbÏw*”? Ky¤³Ia𦏠°W]&ûoŽ¦èЇ°«ËèÔ0¤@J‰M†Áˆ€Œ4®z.WK|wDa!0M!­ÿÍ £`4£išÁê R©ºø~°-±•B)…eY3²DÌ»XÖâ|øùÏ~"‘Xç8NdÕŽk¾OÈÒ 7I ]¯£[-`¶‚ÑLÝìàÛSSÁþ÷ûƒ\n®ëÖm§ÓéÅÕÃó}BئÀÒ,t»¬µ`¶qöÏ%>8äg.MÌç §‹ÅâÙ™™ìBC3Ò´$º´1… MŸµÙð;ÿ·ßÏž›_¸X(NS@­¡ðÊåràÙ›kÑ{: Õr³ó®ý͉#¹ÜÜ¥b±øS½^Ê«]Fí:¿©½=öz¼#†”Ïóp=ft,óƒëºù;Ïä¶õ˜5§Ÿß¯IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/formEdit.png0000644000000000000000000000013215101070305016746 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/formEdit.png0000644000175000001440000000042715101070305016741 0ustar00rncbcusers‰PNG  IHDRóÿasRGB®ÎébKGDÿÿÿ ½§“ pHYs  šœtIMEÚ :/J1J—IDAT8Ëå’Á à —È¥P›ÛÀ‘ì&¨ÀEï”1yK‚•gVâ´sp·'ý(7à¡çÿV@)%I’÷¾a8ڶÿïþv{fFn%ŒÂÀÌ03ÖUT³ø÷×ç|.÷@’SJvæ¾Hz–P¡Øìs\äþú_þ º/¹@žš:ó¨áxešàx+ÂÙ< ¼Ã\yÂ*¦áIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportForward.png0000644000000000000000000000013215101070305020556 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportForward.png0000644000175000001440000000044315101070305020547 0ustar00rncbcusers‰PNG  IHDRÄ´l;êIDAT8Ëí“1n1EßGT\"‡€;Påæ\cs Љnkª)6«àH*FŠ]V ˜x¥á3’õýþÈÃ]ÿ#gv{¯3[,·†3K^rfÏ«AñÍ7Œùß6bêAbF ’âÞ}43þÄ|\ƒ`~% ò!ìOS¨ ¸‡W$Q¸Ë€:\ù€€ËÙ„d<Ž^@°h*Ÿó›uȶ¨kLXP©?¸öû3ª:psdœGJÖüþyH¯eY‚sMhýyʲ…Íô“m˜„$°?8˜ÿ˜Paã½ë–:  aVíoIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackIconDrums1.png0000644000000000000000000000013215101070305020206 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackIconDrums1.png0000644000175000001440000004776015101070305020214 0ustar00rncbcusers‰PNG  IHDR€€Ã>aË IDATxÚì½id×uøÝå-¹/µWWwWoÕ€nlI,$!–LJÔB‹Ù²,R’¢EY–bì±4ŠI+([vŒ5–Fšñ„B1Žñ8¤¡∢@ $AR ìzïÚ÷ÊÊ=_f¾íÞ;?Þ{Y¯²²ª@B´+âEUfåòòïœóïœ{“à¿ÿ|7~Èþ¯Þ,'òßÞ¸kMîÂøo8Øøaïtü·døèó’T2AR©$%„ß÷ Úw=v=þ¾Vä ø ê.^[Å«¾×½ÞÐ5â¸^tŸŒýOcŒR)•RJ©Øõˆ_õFFúz>™0úQL9ç4|øAbïû½Œ"ãKò}Rñÿ{_*ž~쿚\!$1 ®QB4<÷ÛNkö—‰¿ú÷ºÇ{ô‰ÇNܘ76[Íõ®Rç @™RŠˆ@ä{ :!pêÔ :7¿HˆŸûÙ_šÒå•ße¤ö“ùŒ2G‡L5T4%£õF{Èvýìj%ó|­¼)è±Ð¯ö‰ªo ¤Ý±%üƯÿ“û¶7®þ'Èú;R ™Ï$ÕÆ½3÷\<_œ™<¿zõêõM)aP ƒ”ÚÅ~É÷@‰Jàìéctîö<‘€ÿÓü纼ô4Zÿѱ! “c¿3¨¡¦/Jå6m4}a;æ7—,FŸñBò¦@tb'?Z¯­€lÆ@:¥ÃПQâžÊ‘·>r¦~éå[›J™†f ¡è€(þúêoQJ pèÐ$™_X¡ð?ø¡L%Ô•ÿ`°ÆûdžušÈªBÎ`J)R©ÙØØj«R¹KËuµ>»Ðz®\. ZÌÐrð¦ˆQW¿ó“ºh¿ôS­fý"c €®3$8U ÊÉ$ñèÉS3ê›ßÚZòý®K LB) ªŸ«ò·Àødh¨H¶¶J€øà‡~iÜP7þFëlXáñŒ*æ ¢”ÂvÍÆÖv[-º]õ±]§Ï¿ðÒ­K€¯…¯A/FÕöí¢=òÒµ…¿–ÿÛü§‹årùP €çK@šÆHhÐu 7™JàÑ{ÎÍ®lª•f³f)¥tJiD û.pûMiül6z½AˆþÇ¿›Í“¯ü–Nj?=6¤áÐdÅ‚I €íªÍm[Ûm¬nX¨¶øÚòZ÷¯êµm+ô~ Àïõ@¾Ù@ÐßøßÓÓgür¥úT£ÙÊ‚Jž¯ $À9A2¡!‘à`Ô# CÞ3sbì¨dÃ¥••ÕªRŠ2FépáM€¤SI´¬6 ~ð羜œRÿç¿2håçG‡¦&³*$  P*ÛØ(u°¹ÝÆÊj¥:í”k䝿fggÃÜÐà.'<üðxS€èºÎ„dxd´bI¶Uª=Y¯[ P$ï+P $R —иdr4yoqè°uýÖâZ¨‚1J Qj—Zد¾YJE€¤R)´Û@¼û£J?&~áÖ°ý?Ž 1žÊbx((…­r[ñ—ך(ש]m°/Ì/¬\UÒçáë¹lÝÐøvxŸ÷¦€‚Ð76Öèٳ產f­Öz°ÖhÃ÷„AJ  SC*¥A×4êŠ9í¡#G’K¯”–¯­8¥„Æì{PiH¾›ÆÏds°¬ .þsÅîoÿÈÿÄ保)9z8«F†’D)`«ÔÅúV›[-,¯µP®ÓNµIž™Ÿ_½"„ÍúŒß nxDoJ Œ1ª”2çÅñã'®¦R™šÕvÎT«VÊîº!ð…‚šF‘IH ŒzF6Í:{æHnq-µ­JC)0F ¿Kû’ïJØOY§®?~ÏË¿Lüß.€OÎ˱Ñ$…RØØì`mÓÂú–…¥õÊuÖª6È3 ‹+·…p"ã;¡±Û±£@<¼¹tD)E9ט”2±¶¶*m»{kbbb‘2c¬VoO6©@)Qž/‰çIpJ‘JiH¥4Å©O¦:}rz訋ÂöÚÚzI)€h ¤?ä«}"ùNdd™”ÍÊÏ€_øù÷Œxëÿz8¯ÇŽæäxhüõÍV7,µ¶Ù$Ëk* ­^©«g—Væ…ðôР‘×[ZšáßX ðßÈRðµ–4”z)!„ÐÇ¡åòöúÄääuÝHz­¶{´Z³ Çñ@¤ã*b;P@"¡‘LJ‡Î%tÍ?|h"u±PœìÞ¸µ¼¡eèÔPŠ V%ÞpãS–D>åÑÕð°ÂÇßwõ£Ä[ûwÃy•<>ÃäxŠ@Ëk–V-µ²Þ$K«¶ZisÛûüòòÒ‚RB=:òúfßÑŽ ¿|sIÁýJÑ•r¹Ón[³ã“%©ØX­ÞjµºD)!AGB ÓäÈf •0 áÄÍóÆÃÓÓGÉÕÛÍe×é8J§Œ!Ö.%ßᔤ:n õéVÙñÈ_|ßõŸ!ÞÚ¿ÊÊäñé§¡°²ÞÆâŠ¥V7dy£ƒjËX_ßê~accu5dû*4®M˜÷·û¼¿?ü«7K Ø“¢#Œº”BÕjÕáá±ÃH­¶{¤Z³¨ïK(Eพ'À5JòY©¤Î|-“¤ž>5Y¬wÌõííí†RŠk'RÊ») _ïhšNtæÒ¦%}øøÏà'‰·ö¿²~îÄtSH¬¬YX\±Ôòjƒ¬¬wPkéË+ÖÓÛ¥°Ù£BÏïô¾¦€vøÿÈûßÐðÿz ¿£×Ÿ)ÖhÔ\cóÙ\¡éyd¢Z·R®ÏSp J 2ùŒ šo"“b`ÄÏäsúCÇŽOÓ—®t ­n P›-è?ׂÞ󆇲´ÞèøðÏþÔT.ü^>íΜ>‘ÇôѬ’da¹…ù¥&–VêXÙpP³ŒËkÏת¥fh|Ñçùý¡?NúœÂÏ:,Ã^0Ùwáû'~z@ „(´Ýn·=ߛ݊kÍÎp˲!¤„ïK8®!ùœ‰|N‡Æ|-i’fNŽ·jÆZ½¶]SJqBÀ!wÃQ^ 2uhŒnnU|èC?õˆŽåßϧìó§Oæpl:§¤Rda©‰Ù…WêXÝtPo›¯¬¬Õž©U·[¡ñý>Ïoõ?ò|ç»aü×]Øþ–fo܉RJ=×õju#‘JͦR9ÒîxǪ5‹¸®!€ JPÈd4 MeêŠèÜ›92•?.¯¬®nl†ïÃÉ>(ø6@@àÌéct~aäÏüÌO_Lå?È¥ºÏžÌáä±¼R dv±‰Ûsu,.×±ºå¡Þ6_ZY­~©ZÝîÐ !~Ìó­X¾oîSïÇÿ†æý׃Âo?â ¥”4i·­†Rr®X©ú>¯Ô­ŒÕ¶!| Û‘p=Óäd¸˜Ù4#œ¸“cÃÉ …¡C7×V‚Ö4JÂÖò~|äN pß…sôêÕÛ >ô¡KÅßÏ&:œ=•ÃÌñ¼@nÏ7qs¶†ù¥:ÖK>óëË+Õ/×jÛnd|¥T$ò "|Q¹oúˆ’ïߪu»ÚÄÒB|ØÑWJù„ŽãtÛmk%_šÑõ†3U«[Äv<ØŽRŽO(#dd(¡Š9è\d 9þÈñãÇ+›j½mÕmF•Á5!;ÏÕšEÄéÓ'isû&k¶áìc¼GW Mt=7“Å™“E¥È͹®Ü¨¨Ù…YߢÖ6¿¼°TúJ³QñBã{J©n_¾ßÏóݾïè€ìë½0¤r"B ^¯Uã· Å‘–íʉZ­“jwâ ¥º]Ÿ„ò&F†Lšä¦Ž Ó‡‡Ç»Ž¹¾¾Qª !‰¦q&¥¼Si@Ž9Dçæ—T³ ÿøÈÏžáþâÌší'ÎÎdqvfH)rc¶ë·ª¸½P#›eé6ÛÆ3s‹[ÏuÚ5Š<~Läiõ)|­ás°ýï¸ñßàå`u b¿bÛ]ÛsÝ…l¦°,Ï5-{¼ÑìÏ•p=וȤuŒ¤IŒúÇÆÇ²g4£Pž_\_•RBÀï@ âãfdx8O76¶ùáüü ÝŸý´i½ûÜLçO)@‘ë·ë¸z³‚¹Å6·U·Ú2žž[XÞ±›Á0§Û·ˆ<ÞwÛøo4^M…ñd@8°Rê:Žh4j«ÙLnA72f³í®Õ-Òízètƒ^B.k`r<‰Lº©©‹ÇŽŒm–Ùj½^)‡Ÿ‘õ•Š„sN\×#ܧ~ò÷òSÉÏ~*m4>xÏL÷ß;¢yåz —®”0»ØÀV5«Mþ™¹¹å+·u3|^_K·ÙGö"ÏwߌÆÿN`¿rqPZˆ—C",Y«Õ¬ëº1—Í+]GªÕÚ)Ëê¢ÓèÚAWñðdZјüÐDþ´¢Ã•啵ž^ª‡ŠRM3uxê“ùó…/|"mÔ>zïé ¼0¦(¹|½ª¾ùÊ™]h \çÕr 67·xÝ÷hy¬>²×àùßuÂ÷fÀ lDL3”R©”bNÛk6KétnI×S™z³;Qk´Ñn»°:œ321š’ã£1u1:\0ï/ ö®Ý(¯®v© Œß=rúfŸ¸çö¿LÕ_¼g&·Ü?&½|µª¾yy‹Ü^h ÜÔ¶7·Å§çææn+%’a§Ó=Àøƒ<ÿMiüïö«°OJ©¨\„”R´ZÍ-3‘º©Ëj{‡Ê+ÑhvÑé麒L55™ é¤J§SämÇOeÖ·üµf«ÑQRš PSÇNýÝÇé?Ë%ê¿tÏLo}p\QBè‹/—Õ7¾¹AnÎ5Pij+«Î///ÎHÅ<¿Û×ÕTê¹}-Ý7ñ¿›8H>T)ˆX?¨eµ:ÍVk®P^Ñ‹µz{´ÖèËòT×H¥4rt*bŽƒÉs“C‡ëm¶Q*U6XbÜüѧŽþ\1Ýúų'M¼íÁqpFÉK¯TÔs/®“›ó T›ÆüÚ¦ýéõõ•Y+ý>ÏoÅŽx™wÀs§kðßîf¿œøŒ@À ¤j6ë¥t&7§›iÑj{G*UKk4mbµ£8r(ñº&ÏŸ2Ó“­™ã…ǦFÜœ?i²·=<Î^¼\Æs/®“›s-T[ú­•îŸmm®nÈÇŒï†FnÅÐ_æÅ›:ƒVôì7Ló=€ý–ƒRæjóÆËH €X­¦åzÞíD"[íÚ²X©Y…z£‹¦åÁóÆG“8q4SW#­f÷á„î<|áLÒxì-Ðtоµ¯s·šØ¬ÐKËkíÏTÊëe¹í‹XèïÄ<Þ:Àëï$=ï7LC¾WpЇ¤$ø¡7rÄüØ•S2|¼ é{^Âj5ë\37™Å•ÚèæF®€ã S6¶,¬®WÍó3Yí‚¡3¼p©Œ/ýÍž{aË›âöÂJóóm«ÚPïÇr~W¯=€éÇg㟱—”ƒ&©¾c`x½ççïD8ç$¶K0x¡‰3†ß<€¥t(N¤Óé|¡PÈf2™l2™Ì뺞Ó5-£F:™L$2™¬Ôu=·UªL¾øÂslsí6ÎÅÌña$ÏãÉÇ'aèϽXÂ3_]Æy ÉìqŒÛ´ínͲšÌ¶miÛŽcÛv·Óî´›­f«Ón7<Ï«ø¾»!…,QB¶•eGªJŽAS¼ÑoS e8ä'¾@Õ€Þ y= $@=@išF•RÄ÷}@†¿ûŸÇÂ0;`2•JM¥ÓéÉ‘‘‘‘ñññ±L&3V(Š©¡¡¡d6“I¥33ŸËiÙl–¦S)pM¡œ3èºÆ8 CG6“Áí¹EüÖ¿ù·øêÿ#]¼õÁcøþ'Á0(þæÅ-|þËËøÚó xëãïÅ¿øÕßÂÈPq¼Õªûž)%„p ËÂævY–JÛ^£^sJ¥Rg{s«[­UÛ¶Ui·Û[í¶Ur·DY3Lc-“No®¯­•WÖÖ;è[ànAJ)”RR)%D¿c J‹ßqìÉÑš¦Ïóéyžè{¼ K'§” €žÌ ÓããcÓãccGŽN>räHjtt456:Æ&&&Éd ë:É$‰ Ãç{NY¹BÂó<å8.<ÏC×uI§Ú$ãGNà=ïy7”õ-<|!xò0tào^ØÂ_?»†¹¥Þöð${€£U½ŽüÐ;Ôðä4˜òÀ9§”Ä®2mÙ¾Ñl4ŒÒv)»µ¹‰íR •JÛÛÛ(—·Ñ¨ÕÝz½ÖiÔk–cÛÛ¹Bq%•ɮ꺾ˤ Ã\ºqëV©V«utðkÕª'¥Œ3Æ ‚~vÙ¸ßòð×Èk|Ñ4 š¦‘N§ƒåÁ™)E !ÙÓGøRÞë8îý†iž9yøèôØ©™™ä™3§µSÇãðÔ!ò˜¦ Ý0²|@¶:¶j¶Z¤Ùj¡Õ²HǶa;q]¶íÀu="¤€” BHH)@X+sWPž÷žx÷; ™äøÊ×7ð…g—pùjgfŠø•^@B÷ñÌßTPvÎ#7õ8²¹¨²A  ëLCWš®ƒq ŒRE(RRy® »Ó%-«Iš©×j¨–@ÔkUÔëu´Z-8¶íx®Ó†Â&ãl>‘0o Å—Üœ››ßâoݺu«~f€iš9‘ ™ï§'¨7½0îÜ9â¸.››EXáÙ¯#ño?ù‰ñ­­Í³õFã-Ý®ýa÷ŒN85CO>³çÎcæô=2…|*ÑKŒõVÛ•*êõêÍ&Zí6:.×…ïûB@H ¦ÎÞ¢1EPB¥@)…f$±¾t —>sÇ\üØß‹LÚÀ³Ïmà/ÿz_}~µ†‹ï{üÞûÔqóW7q{E =|+KK¨nÜÄ̉"ž|â~àÉ)LÒqk¶†—nšé·£0qŒ*øž „ç[Ô["!8H$€>\ÛA»m¡^«¢\*aks[›¨”Ëh4ê°;((hš]Ó‘H$jÙlö•d2ù¼mÛßð}ÿúÜÜÜv»Ýn†p]×UJõo!·ß62êõ@ïZ“r¹Ü õÿøÇ_¹råÉõµµ©ÕªÊr£ã8vâNÎÌàÈÑi #™L‚q!$×E×¶áØ|ß.(!`ŒÞDHh츑•R<ä¤A®é`ÌÀæÚ,æ/}'Æêxß{ΠPHâ‹_]ßýÅ5Ü\tpám?†w>õCØÜXŧÿŸÿ/¿ø,&F4¼ë±xÏSÇðÈÅ"Êå^¼"QU‘ºf"(JxÁwðR«0BPPF{Ÿ|ÏCÛj¡RÞÆÚÊ fg1wû&66ÖÑ MÓH$N§‘Éd¶³ÙìW‰Ä—„Ï¿ð +®ë"$ž”2¾q„„»ÁtÂ9‡”’0ÆH»ÝÔ'>ñ‰ÃBˆ^ºtéWççç?Òî´OŒ™çî½€{/\Äñ'Qiš!°]V» ˲еmì iš¦AãŒa•Ò”àâ†í3~Üã4=©Ö.céÊg1™¯àGß{£#i|ñ+«øôg®âö²?žøþ‚¦Q yÜsñ‰,®_ŸÇµë‹Ø*uÀ˜†³§G0œX¿úö*<_@K CÓ @‰ð½ipjˆy˜†””A$ £c ©t£c˜>q3gÏâø©ŒŽƒ‚z½×uá8Ú–…n·›²mû¬ã8ž›žžN>|¸¶²²ÒRJé$ØU”T…Ýmt?Ä4ͳWJ ¥¿tùòO<û쳟\ZZüH¥R9’L¥è‰ÓgpâÔiŒ#_,"_("“ËÃU¼”RpÆÀ(í»?|ÆÃ+ba¶gt` #ÇqqóÒÓ¨-þNLº8{z†©ã¥—·ðÿþùU̯¼å]oçS`T¡ÓiÃu]$“ œ»ç>ž>‰õÍ*.]ºå• Ú]Àñ† ''»pšó¨Õ»Ð3SàšŽ¨bë{_*èº !¥„ï ø¾¥ 3ñ‰IÌœ9‹{/Þ§NC×u4êut»]xž»Û…/wç¨ã8ïàœŸš™™FcÓ¶m`•îcø»ÞZg?MÓ`š&±,Kø‡8úáø_ݾuë[[›§\×ÃÈØ¸:tø() cxdG¦aêÈä EhšŽ;î¨~cïçÕ ”’½y6üÍ9Çõo=ƒòÂçñØC9¼çïœã_ûÆþê¯oay[ÃÛ¿ï'ñÈcOB)®c÷€çy.””8zlçî½_i¸|é:®ßX…Õ&Çsxì­S8wÊÄöÆ*5…Dv ”ñ˜jtìönpH)!|žçAI‰t6‹c'Nâ¾ûÀ™óç¡¶6ÖaÛ6l;8W)%ít:Ç<Ï{|jj*N§—+•Š`ì0oqGì €B¡@êõº ~ó7óèýÑýÎÊÊʆF)Sùâ²¹<Éåò8sþÜwñ~Lš×õ0ü‰=/Ú3&öuï±Ãïö‚#€fbméæ_þ ö~<üè÷Áóºð}´ïu!½ûÇç'gN#[ÆK/^Æ­ÛX߬cu½‰íŠ…õÍ:ôÜ ä‡÷ÔÞ;ù Ji,aŸk¦ŸÀ=.€qŽ›×®Â¶mbLÓ”¶mß÷Oåóù‚âºeYNLÐSˆGän@…½Ý¿ggg?6??ÿËn<ÈÛT˜>~ïyßáܽ÷„ÀqœÅŒ~’„¿iÜ£Á.ƒ¯@á忊fe©tW¯WñôçðÍ—·pÓÖÿH IDATøôxüûœÓ]a?nô~CU‰Ä±'1>5Í VÖªXZ³°°l8yþíHçŠA_cßsÞ»>ÿA Ž&J¹|x¹\W_¾ŒV«‰L&K8gp]J©SíµµµÅX“iÐ,Å~Üàà@ýÚ¯ýÚäóÏ?ÿÉZ­v(úPŽí 84ŒþñŸÀ½/Âîvá¹î¾Û¾ü/2Hï¾øEÃÎXï.@Ðu®ëàÆµk¸ty—¯ncuKâÔ…'ñî÷~¹lv· ›û‘Ï ‚)L?ŽûîÇNÇä‘38tü~œ¹ï ŒŒ ¡”ìGÿçŠomÔï;âbÓî(¨¤„ã8Ð4 çÎß )%^¾ü4ÎL&¡Bb©ëz†s~­ÙlvBÈC¶¦~Àô.J¥Òt«Õ:¦B…MJ B)|äm¸çÂE8NPË߉ØõÿŠJ2þ¸Ú§°ª˜Ô %.<ô(Gà[/~†™Àù á‡ßŠT*NÛXBÞAñ‚ïûè¶Û.blüQ© % ¤€ãt …Ø{ž»Î„çE˜»ab`€¶eçóxê=?„¹Û7qí•ËA%Å9”R°m{,—Ë]YY©…Î<¨uNêðÚºð}ŸBüxˆL¦’˜™9ƒT*J¥¼Ç{ìUÑÒc²nÝ+µ·G>1ΔR¾M7ðè;ÞyŒs$&|ßE·ÓÞº8xúA«Ôîw•R¢ÛéŸ/zO){rsô\·ô w»Áwÿë‘Pk[FFÇðÈÛÃòü<„½kN)%BˆL8²ìÞe”Ç@!1x÷Õ}7iŽî¯˜¦¹ÍX)8çp7¯_Õj"J¬ÓãlxPøD‚H_XŒ3uU”îìJEµÍó\áÂLèÐ8Ýmñí]¡{òïÞˆEbé(~NJ)H! …œþçí"ša°—+ +ì}½¨‚H¦R¨×kX_]ç ŒÑ^ Lé6›MH‡ó‰p@FÃà¯9ôïø%‡††´F£ñƒŽã É ¥Àêò2(¥89sÉT*¸@Rîöä(×íÊw ò²dj‘‘DxPj— 6ÝäéÁbŸ}AIomm½×qjš&8çÐuŽëbîæ ¬­,#ÍbdlÉTŒÒ é'yƒ¼ ONEŸÌÛ_Ví§*Þõ±ò8Ð3û3ð={!d@™·h¬¹”öy=×4$’I¤’)ø¾—¿õþâOÿß|þë „ ™L"ìË`»\F·ÛUºn\³,k;Ü<+X©ºØûw mnnNššjùžw¡Z­N{žÎyp²¦ X[]Åí×P.• :òù’é44]Œ¦TÏUãäè áäNåd¼–ŽsŒÁb¯„¼»\;{·; E3wóœ^JÓgÁ›™HÂ0 x®‹…¹ÛøëÏ}_øìg°¶² 3‘€i&zÕÉövÕz¹lf«mµn8Ží4Ò‰ ©öo@!_58çTJɦq8צ*•ÊÁNÐgŒ…¡J‡ÕlbööM\¿ò 6ÖVá:64MC2™‚aÐ4­WF*¥vB7½ƒŠ›€(?ä“Ò½‘d`ßa€j¸c|:ð9ý@ŒW”RhšÃ0 i:êÕ ®^¾„/>ý9|ñé¿ÄÍkWà:.ÌDºôT<ÏC½^G¹ZCVc/äæKÕúš”2òþh}BûÕ€ï3_¦Â!N–Êä„g;%³žÔ[¯×ÑétJ¥J¥Éfáû>Z¾öìqéÅpèðaÌœ;“§Oc|b …bét €ðE8å㇀PA›dgˆzƒÒØž¶W_y‰}Âþ^/V8xËÃþs ‚Zod0Îe vU…”аåMÃ)"ßóP«”±±¶Š¥…y,ÎÞÆêò2ZV3ø¦]Æãt=‰ÃVqͲÀ…‡òºò‰²¯)_9Õÿ}ƒwú&Ò;…*øÚì¢xp8S.˜FËîÚ™ˆNºaò,¤Ói$“I$S©ÞÄüìm,ÌÍâ¹gsŸÀôñ8zì8&M!W( ™JÁ4Ó;2!ÌöI+%w]È}I_¬4Û¯¾¿“FP§€¦ú^s?M£?´G•c”%¡’ ¾ïÁjYhÔ«Ø.•°¾ºŒÅ¹Yl®¯¡Q¯Ãó\0Æ¡’É$2™ R©(¥°»]lW«¨¶,@J¼5§ãáŒî=ãy„ß/üÄ»Ú`úŽSÁí¹~7{¡œ7ͺM™† çÀÂÏóP«Õ`Y‰‰LÓD*”‚ÝíbîÖMÌß¾…D2‰BqãcÃØø$FÇÆQB:“…a˜0M³Ç¤ $O©äÎE$ ™»˜ÝïGJ©yqÃï‘©Cm‚Ò`ò‡…ZC6ßóÑívжZh6ê¨UªØÚ\ÇÖú:¶··Ð¨VƒAQÇc ‰d…B…bÃÃÃÈårÐ4 ®ë R­a£RÅÒúv?ð–ûq_svµânÙNPýªß )! X_p ú› ªÑé6uΪ9ŽÃ)5IÐ*¶þ‰Àó}Ë‚mÛ0 £7ÆœD×Åæú*ÖW—ÃrÇD:“E¾XD¡XD¾8„âÐ0ò…ò… e¦ ÍÐaè4ÝØNHøi¢¤Ü‰q‘ׯÀ¢öˆ3*¾|gA ¸A\¡ ž(žë¢Ózöm«Ëj¡Õh RÞF¹´…z­ŠFèívžë0t™LÇŽÇøø8&&&0<<Œt: ÆlÇAi{×nÝÆ›·QYYÂP­†¼ç~ëÃh~ñs({Âi»ÒI¿ñ/™ò÷騻‰ªO5’Á¼šÑ¦¼»M(EžHd9Ðð B¡«OO“$(…p§ç±ŒLáBˆ`pÒõШ×P)— ¥X±i ™J#•Ω%F6›C6ŸG*•F"™„i‘F7`h\×8ã½P€% jˆÀŽì@)„;>¤¾€ðýp×RŽcÃîtÑítÐívàØ6,«…f£V³v«…vÛB·Óé qxž Ìù%“˜œœD>ŸÇøø8¦¦¦pøðaDëcp=õz‹++¸z{¯\¹‚•ë×`Ö¶ñ°¦pÎ<6 XÍ:lÆm7PÜýs‚ßv Ph"›iK¯»©B&Ÿ¢@F#(r )†¯Ð‘ Žðá)ÌèK „R¡~®kt#¨ ç a¨õÃhÄ+Û&i76 Ãñ+J)ãàœAÓôàÐ5hº]Ó¡FȬµðqA¹ÊãŒ3H)¢/²îEDß÷á¹Áû{® ßóà ¾çÁq¸Žþσ"à+ÂV‹PÎVŸÍfqøða‹E‡†0>6†±ðÆP±ˆ\>Ó4àz>*Õ*–W×pãöm\zù\¹|¥¹[0ë\  g2ÀѤŽD×E"ŸƒÝmƒZ tXÚ¶•ˆ<½@ÔÇà/™øßüÃ÷žœíXÚvo×E S`ˆtЀ%–pƒã8ÁáùH&@)(× ]7H˜0 #ç`¶Ý“u}!zì7F·Óã8ð~•òvö†7Àøþeà«N½E‰_}á÷­“¹MÍ0Û²k§âËR|(p˜È‚¬¬+¿ýípò |å/ŸÏšž@DZѪÕPç ÌHÀL¥Éå/0<<ŒB!€#›C6“†®'‘ «ŠÈ0œ£ä<¬«i8a­Ô‰/,‰·¢k±ùè9‘,èà,ˆŒ1ÎüǺ…Ñ`†/Zí.Ú ­¶+lmmaue+‹‹ØXYBum Íu°fyœ×€©$0^dNÈLŒ»ùBÁñ„’¶ zä8Ò=Ž§ŽŸÁK…œ÷_þ¯ÿÜ…Rþ>Þ¿ß²»®öF‚µ9áŽÜ[a\k:@*ºž €$á~n àHq€t-x 8vh•4à2 ]!Ñ m!ÐîZh·-4J[(`Î0 %’0Óds9d … Jäó!0òÈçsȤÓH§RH%“0Í DaZ×uðhÉVÆI‘ÄWí-!¥Ú¹fR¶ëBÏð|¶ãk:4›-Ôëu4êuT+e”·6Q+m¡UÚB·Vª× ;]d$p‚£:0: % ¥L䜂‡`ò„„ã øRÀW R¾$Çá¾'ŸB~|s/~Ý­WÊÎñg¿ÕBx5A½°¡‡Rª¤”ĪI­‚`"$ý½Î]oˆ"ª½=ÍFTÓPL0H¥À8‡¯|¸ p„„+%l)Ññ¶ç ÓpЭÕÐ\¶HP]7à‰$’™,Ré Ré4’é’é4Ò™,RÉÉR©T C„Ñ‚R’@FY°b$V††– ~˜6\7˜v=«…vË‚/ü`Ý_»ÜW¯ÁµZp­¼¶ÕiCs:Ð}I%1MŠIŠ|Á@>¡!—БKêHœÓ@Ùò<Çóa{>|¡àKµ«œU É˜¦asm kóóŽïz.l¦µOØ—Þ)(ï`f*cÙÍF ”œWP½ ¾“";˼<…n« 5^ãÒq`p‚$ ÐN‰ÖL €+<)áI GHt…„#\áÂé¸è¶°77‚ýXIôÅŠk \Ó8˜¦ƒh¸®ƒr „1pJA9åŒÒ^_—(…^³W)@ˆ`‡Y߇ðhÒ•Âv@¥—ºHC"IR H2‚ŒÆK1dŒÒ¦Ž\BG&¡#Б48‡Æ‚ð.¥‚çù°¶ëÃ—ç ©vˆl]!%@–É‚è6çæPÚXöV})@àÛøŠ¹»á Må‹-w{«¬@z+t¡‚(Ð{ èä†ÐNRTM4³ ¸®‚p™$ !+×F)8#(A08¼–T'øRA(ß)$$ÜðBùR†./mH¥ ¿« DØ_Î-\Ò5Ž‚íC9€!Ц$#8öw~ÃÇNbû³ÿt{ Ét N‘15$M¦Áa’ ‰„Sç04 ºFÁ(“°”'`»>ǃí{ð¯Óõ…„J"¬`vÄ&¥Â4°L‚Pl,Ì£VÚ²)#.„’ Ðòqr·)`ÐâRœo7nVT¸ÔK…œº÷7 .ð–'à:†t>¦™FöÁ·¡sõ×Õ4Pø„€ª!àŒ@ç:gÐ9gœ1éÚ3ô® ë(%Ý!è„ ¦Øì!£Ãf,$~ 9ƒ¦1èºSy°’y´ß÷ÈOŸÄ[èò¥Û Ù<¸NÁu’P (Š"¾ "‰ëŽ8à‹¼¾€ëå­ÏQÊÐØˆ uÏÃÈ*Àh*f»ƒõ¹Ûh×k6¸îC8ƒvV½›=î€Ab9u|as]ߤ\ƒpEø.ª€ŠV©>!ðýO>‰³÷^€µº„¯ÿÖ¯¢zs<ÃCvœè"ø’€ÉÀÓ…T`,XtÃà ºÎaj †Æ#q Gl=K¸"Œ@‘RÂh@ú((á „QÐx„2PÜ&8¨@S¤ð­„ »caäÞ#Hú@‚Å¡28|¸>àyP®B¶ÜGð^ᇥkÔàR24~¯·¡¢½czur 0$‘ÂúÆ*K ðíN× *€x¸Óž„¯*ôËÁ þ¿§ŸqÏoqÝp…më;ÿ G¹$@®€f£Æ9OBÉsÀSéà‹]ví‹D®_à2:¤„‚øD%šð)ç \!\3H¡k ¦ÎaêTã€ÎÞïИ½ƒŒ³ðoJÂyDÚƒQ ăcé! w‰D’ '¡‘=ðHâ…€ðE „÷Œ-ÃÑrzÔÔ’*ÀMI¥ŒzÁú$tÃÄÒí›h¯-ûRÈ®r=Ÿ•,¿+ ܑ鄩¬­eጤ·)g ‘áɇvì) (5ê˜_XăÕ*„Ràf"x\£‡ú˜PôåB ?BHøT£Ô— T€ Jôø˜=àczJ €ˆˆ€Q€øàJ7L$ Ž € z†ïB…—_î>”„A3@úUt_ ü½¼í”¨n ê ,ß¼QÞrˆ (A)Rªƒö w*ïÄzWŽq®Pf ÂxU#J=;‘ ëIÈnèt:A fa™†;Eb.lÐ(²ÓºŠò!€ì\8Èx¤ LÀÜAÃÇïµ Ã«¨¢ êJ©bÞL ÎF`;Ý®âv1·ÙÂ÷Ñ8JRî¸qt[)@¨^O$Ú+r½Ï%w€ÞËý‘¡¢›X.•Q¿q¦g;]¦yã8`¯µ Ø5G~e/MdruG+UF)E‚‘”pÇ ¡“¬ëbccõFCÙ,¨fÄE£.dðÜ^ÿ9L 2äJIHIv<¥W*ÑÞ…‹f„¤`RH ˆ0´ P~Ihd^>F`P¨Y”Ä^ZÆó+6hzäsRau±„ûR |ømãHZHöˆ%ƒè°s;0>”„P2þã¿3*Ù‘ë»­ @(àP†Åù9h«sHèÜitá„þv7õÿè]®—’Hq|¬A«*B HäÑ* µÁu4%PÚØD©T‚”k1MVõNO"6ÜÑcñÜóŒà6v†D¤ ½kÇÓ” £ÔŽgŠ˜wú"`éq޼6LŸ{a¿úŸ¿Žg—< Žáô=÷¡Fó˜[)…ÏÅŽá£÷!ûn˰žß•ÿwŽ8ïÙUöE±4$ÈœST¬6ê7¯a¸]‡ÃuÛ’p£3¸ƒxW<€ÞQŽié|±M++B÷=A†-E€Fy«ëë°ín8ÜC}ô²±t,C ”ꥊޅ‰ì¨b6Ê¥‘ˆæØÃĵcè¸ÑEHØbÔ@„|JÇý÷?€·<ü†3&rš‚AH DjÇè‘çK H%‚/½½p/ÃÔ·©vÈªÈ TÌ!(ƒ´š8\YÁ°FQS̶]Ï /×ë’î*øB(t­\ïpMßdš8Z¯\‰•. (pÀ©W±¸´ŒF½¬¸%}¨ŠR€Šß·Ãw¹ËkDì¶”ÑmÙKRÆŒ­Änb&bùÚ—AA  1ß÷ÐívP­ÖP¯Õ✀оגq‡Œ—ç»€Ý û!‡Š‹V®"(BàõA U¡º°mŸì/ß±ý{·Ø%yž§àËòÇÛb\ó¤ÚÔ{ê Õ­ÛÆÒÒJ•2<ßß!ßý9jGþŒß+ {Æ—ñr1Š;›2 ¡ våâ¸ÕN$ðûÿ·5XøY<σð}´;tÛ-dôðRyb7 úS‰ˆW/j¯è#û ¨>> z#lJÉàRî7]¿ éû”@í³]œºÛ&Ы‹¾¯L]¥.Z&Œ6ãï†Ød­É r X_YÆÊê*<ljMÑ’2/Æâ¹^Å™qT;K ÿrO^=22î~£÷ÈšØeð]¢ŽTHp×óàù>êÜN Ã& ã¯'}±j@†©)8ßPæU{K?ý¿'ÿD@ ½¶PHäê¶…rÂÜÙß4ÿ§^K°7"ßü A©Šv»t€#ÕcšÂÕÍ5ܸy'šõ`<¥ÀžÒŒ‡Ã(Rȸ‘@¡ h”céN% HTPpž½SQ*Þ ˆJA  ­3H¸®Û¶±Ý° l ã)=xª/z¹¾=b¡ÂRu' ÍbMµcE"vWÙ ˜¿TƒÃÿëZî^>N«žyÇ—ªú©æŽÀ#Á°n\¿Ža¿Ž‹†2уð¨Nblc…^xßh½‹Hƒ~€R`q–-%”}H²£‰pÛþÈízKˆh@©AZg áº‡N§ƒÍ­4ßA!‘ ¯"d_Š ó~”†¬^ùÚ“w†T⽡,¸7*N$Z¾Ô\Å´Œÿ5}Ûø]¥MÓ@ýÃÿHÚÒT$vâ}{$¥`0Š)âÃ]žG§Z¥¬·ÚVô¿Š±¿=MÄJ)¥b\Kí7µS"Ê=BMT³Ë2Qôq¥Õ9×…Õnc³TBŽù(¦ô@úí¥•Ýa?R2wB{xn½€êcTéÈ] ÖèÛ$C* £”‡E ú®Åo‹ò» ÿáaw»aEF”TÁ†©½PNw4‰€gÑ †ü ŸÁãtmJA">YEÙë3ï&Ø£DÀŠ€í"ƒB*P*A% mbNÂmtzB‰¹€ÃpRGJ£pÍf¥­-цzÐä‰ë ñ0jøÄÒ€Œ‰Z=¯ßU ì( ;ZÙ©ÂŒå+Àf:ã¡ ‘án•ây_½Ö ¢e`ÈŸü¿/=%-Pâìm=‘‰ È FÆtø1v_ÙÁ8ù“1oè)g‘ÃUB"Þ\‰I®rW ¦öJµ=áFîå!9Oé($8lÇA¹ZG­RÆ‘4GÚà!T¬ÄŒ—œˆ)~AØÞ‰JñÝîã<ˆE½]‹Y\BÑ5“ÂgL`ÿ­à^ù{U)@J Ã0T2ySÚÞMRÔ.Q#"†€  DqåˆìŠƒA‘XXT;"Ê.)UÅ–Œí®v©ƒ²ßè1Ãù}yÜ—JpŒ§ ØŽ‹µ­„UÃLÑ…ôÃÍ/âù? S*4v´BŠˆÕ»>²_ëØ©„âM ]ì¾€k$ág‹. ·å ¹z-žÿê¤à`hR`Å|¾,5^•¡¤¿ü‰Œaw§+nèØ£!­zx·(¿=á'¦ª‘¹?„¤¢'Àï6Ü.µÐÈš§‡Spì.æ—×P]Ì %wkr‡ôE¹_Ä£ØæN¿¢¿ó×CbT:Œ2ZÏ( ¼áq¸ùaGÚ];¼˜ß–ìûZ½0ŸùÿÛ»¶Ø8®óüsæ¶³^—)ÙÔ=–%»‰Ø®ì(¾)I“¾¸E]ô¡@‹æ±-ÔpÑyh$móÖ>$óâ‡uS…áEÓõ¥±ÝJ¦©Ê´.IQä^¸ÜËÌÎÌ9§sf÷ìp–T%JVÒìÌîrÉÿ;ÿù¯ßôð5bÚ×½¤óVöHS­Á°¿ö)zÚSfì‰z€DÛdb='×b\³þÕVÂ…â Rþø µžŽà%•<,ƒáñ{Çáùæ/-âäÁ}…¾úØ÷¥ò „úôP5rÞL:*ˆ>{÷‡H?Š.ÂCÇÑæÜó››^¯þö&ªnIð8lŒ‹«n.÷?i£#@ T¡Œ–þ^F¶ê#"•eƒæÍ>h’»@IúVý@Q¿t _P"z–·àê\ ¾ç$Âã2;’'dæOljQeÑÂW€XdÈtÜ?±ü…–­”ƒû¼žÔJ•€A/¨á¥àAÊóÁGˆ?skKK Ïó|ådùÿÔñ¶€Øq^@r¨áFÆÛo¿Ý=räp©Þl}¡¹ “JKrB@zaKF’t{ŸÞ*ƒ›ª"LÚc÷(Ô£jâŒ5úŸ?¸Ð2y%ÚëqQÏmaé¥üÉAö²ïoqRΉq_œ-â©c@(@¢”¶àqÑG€ŠA&¸T]JqD’«×zçꈄçz.£¿} „œ#jqŒæ¤ žý5òÎG½¹wz!‚ !ÄWl ÉÐj}va°Ð-o”R ÀœÞ³ç¢ëžõMþ¤þ°ŸIDATÑc`RÄjŒèºhë¤8]íÅoôtËzáé@­€SײƒBOËÁ0ñVƒ06ŽU‹Œb[àÑÙQœ¹o2^¥!|¯²+zž ÿF² üšé‘h®Þw„ÐC±æãR"Ú11;…ý¿óXŒ(ξýV­ÓnWÕêOw›Vº»[)â&öêk¯­ìÝ3õŠcY~…X¬Uš¶AÁ¸èE¯dª¼t ÔIsuF1=12àû÷ü| $Zj µò1©„Ô41ΕEžoH‘‘,±ŸqÀ/B®| (*Ÿ$ „2Ly:‚©Þ#R¿rKö4ñ‚BÎTºØ³¿„G¾õ]Y™>LÞxõÇáÚÊÊU!D20ܦ\Þ/ ËÊd¶eýóx©ø/œG¨8%ÂgH×f a¬â¤Fé"ÓžAÏÂôû‘ZRDÆ Ð³ÒÅýkÑ q¶Pêê\¦âêµHh™¿t$±ŸèÑ£’‹”µ/AÜë`Ö÷ý8ø!¢j³GË8ó½B>þeò£|¼ûÓ•(ŠVÐ^ÝÕŽ`›žÀÝ׈'w ìÍ·Þª”§¦¾Ÿ·¬‹µZ c{ˆuâaŒŒæav%x Š?ôpq¶¥"_“)Í€ÁÕ.ôÕ=æ>¨þu0$eçR_ÉRn-èL¯ö”ú—¼ÏLÚkMW.ißèC/¿lj]¿äfp!Ñm¶À!Nœº_úÛW;ó¾óíoãü‡YàBxJоvd±¦ianyhÔ€!ˆþdÀj6›k÷Üs¿¹±ñÄf«mN=Žé#ÇÚõÂP‚0ÙkÀ ‰AHUi·2üXBõ–€=#P3>^ýuÕ±Ë@4cQ£nÓJ{-aÚsºš"ƒÁŠÄµÐâqý»àBË÷ž¸Ð BÝHƒ•K!èvD›Œ™À§Ÿ'ÿâ%D‡Ä7¾ñMüðoþ: ”ÎyžwM­ôd\}2¸zS]w=²þ†~néG À‚ÀÌ W'&'eåúõÏU«U²çØqüÜ/c¬èµ\Ä@0ëYä= ^3š¾î ›P¨Öì TÑ k^C/Pq¤’>‡`Ò’–xDy¤ýª4¹µí¨Ñ·PSž‰Ë™Có¤Lóž[üa >l5á!»Ÿ}ñO1ùâ_¢N¼ðõ¯ãå½$™i~èûþ•(ŠºÊÒßÔ„ßPÂOÆ×Ü@»€m &V™õzär¹¥r¹VÖ®?¸xé"+LíÅñ'ÏÈÙ'‰+=ðõøØ ¢¬/\¦‘<&×$ÑÈvQ® &1pÝ«ò$«Þ"|蜄2念WR«OI7“oзÑwëz+^óBÁ{>Âf–ïcßþIÜÿÏcö›ãôsøï>Æ‹øûxãõ×¥í8ç›ÍæÇžç%¬ŸÉÊ×G×70º)cðÿb7ø¾¬Y÷„RJ¤”f£Ñ–e-”Ëåz­Z=6wö¬ÛörìÔiqâÌWÈÌáð¼uð ¸ßT¨ÁbÞÓš*'}?Ÿ¥W:í _gM¶BI_ PÙ{+yµ®ˆ^µªÕnën¥ä"•wZ42^ùR¹ÄÂH ì^9˜šÇ¡gžÁ?ús8¿õºãÓòå¿û{ògòÇxÿý÷}Ã0>¬V«w:–þ&€ uÔÕã¦Zý‰úÏrån`ËsRʚͦB\Ù»wïªïûû.Ì8ñá¹sÄ(‹¾øUüÒ—•ì=x6oCnT [DÅ]qÌPZšÀ€–`t«Æ ½À .õ¶ Žœ!ýÁ3ziÚ`¼.¨î©ôâ ÊÐãI¥×¼ $N„D „~áy`¡‡‚#Q>0ƒýÏ~ 3_{îï~ ˜9†÷.\”ßûîwÈË/ýkkkuskkk‹ÝnײòÓªßËXý·ev02 A1=¹MÉK)‹ˆ‡س³³÷GQôë+++CJûôÓOã·ïkò±G#ùÐÃú;ÿ†¥Ÿ¼Žõsï¡qmž' @m+fý2(,F`1 Cµ‘›ŒÀ`4n+7LFa2fP˜ŒÂ¢¦aÄ-ç†ú=Ja1™c<¬²¿]P¸^D:Mý ë™TçHÞO>ÅQ?Õâq„A„¨€wÉaÚöD ¹ÙCÈ|æCOÇž¬Ô7å?ýøòúk¯âò¥KÂ÷ý¥k««ÕjµšÚÏ=MøY«}>à òv)OÀ@<"§„_P‡3==½×¶í'ªÕê³Ífóp±XÄ“Ï<‹çžÿMUÌ }[@&I%. ¸j •<®%5 ¨kŒ@'ÇA¦ö@N€˜9¹÷8PºÒ,¡Ó‰p}é Έù¹påÊÔëueeåê… ×ÖÖjJ'ÂOâüõ”ѧïûI, ¼áß,0Mm0222:99yÀCÕjõÁV«5îº9|êS÷á‰/<‰G?'xåѬNüúDK —>B°|ÁÚ2ÂFÝæ&ßGrrŽ­>>~Ä´¬ZÍæ}­VkšQŠÉÉI¿ÿ~|úáÏâägÂì¡Ã…kþ&ÐX6®ƒÔ–!7VãëNÂkCúmpß ײE’GÅÆHÂ)€X 1aeÔ` –êX€e86s€|²PÏ rpw;ŠÐChŒ dy¸Ò†2}Öêµ êu4 Ôju¬¯¯7×Î;wmaa¡†¡ $„RÊNJø­T '|ZøY“@äÍ s7@`h†¡¥´A@žRš—RŽH)]v©Tq]wfllì0¥ô>ÏóîBäÇÆäÄöïÛÙƒ‡pàÈQì?x“å) y8¦“pÐÈ ; Q,l‚†-Ш*º "á]PiÅ^@œr‰W5ƒd&ÀÌøœš¦a:à†ˆÙˆˆ…ˆæ‘4Á…!8g½ «T}€!ÂÐGÐí¢ã{ð9’à¯íë^êÐßî@!wKx» hš ½-$ö±E¸Úµ•Òktt´8555:222R, ®ëº¥R)_*• ù|Þq]×r‡9Žc8ŽcX–EMÓ$†aÓ4‰išD \2ƤŠ )¥ˆ¢ˆ‡aȃ àžçE›››ÝZ­Ö©×ëÝz½Þ]]]í,--µ/_¾ÜV1úto^¤l¹{}õû)0¤sü:²Ò¼»"üÝÀ°¬a¢ ÒÛ‚¡ÁÒï¤4…•:ÌÔg0v±XÌ‹E'—ËÙ¹\Îv]×Îçó–mÛ†aÔ0 fš&eŒQ5!\ª„aÈ»ÝnÔjµÂV«µZ­°ÝnóZ­Öív»AF÷mVo¾>´!Kø:º©œ~  =-ø]Wù·ò‡4µ-¤Á`¦À ŸÛ¯Y)©Ï¥©CÿÒ[¶i¨½w:??O L~®¾ºÓã]·›÷#w[åß did†iÛƒ¡­p3 }õÓ†öY,Ý8E YœˆÃŸžÎ‘Þëà ¤ŸK×óqdy¸­«þN`§m! š!Hî!`C4ÉÐrdj5¦U>Ï@Öü]Øi¡‹0ðäÐúY@ Z¥4K ;kųÔvp³²[¯ù6 6¹#L‰"%x¹ÍŠ¿í¿SØ H`˜†`Û€„Þ  @†lO雟^‘iM ÍБuÕË¿ÓDOùIå“B–qF‡ƒ¥¼ :äzØÊÇ6f ³ v:¶Ì`Äö­[ò“ÆÝ9¤ü,mÀ‘Œó,aߨõ?l+&@qƒçÃ>|Ò‚¿°dFýA–†@00DÝ“¾s–v.wx>ë÷î¡ßØÎ{ÛØ ØFàþãN1€Ôóvj|»)òn¾Ñwó¹ASn³Êoö{î´bw®üY½¹?Ëÿ÷íüNò&_û~¿‡üÅûùþžòÿÃù_8/†8K#IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackRemove.png0000644000000000000000000000013215101070305017457 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/trackRemove.png0000644000175000001440000000053715101070305017454 0ustar00rncbcusers‰PNG  IHDRÄ´l;sRGB®ÎéIDAT8Ëí”-rÃ0…?w Ë;Æ õô¦1–îL¢Å¢(æ1.ö!‚U`»•lù§IÊú˜w¥÷vß® ÿø%ô¹yRn‚`.·Lz!˜Ë‘,ÚÊ ¼¶ À0÷È<_H®»ªÎ^uÎûÖ7Z1)ðÒ‘§S¾úxZË:ôt k‰uå  êĦªNB#µ‹²"TégžS–%/ÆpÊs²,@D’Èt´‹ÁZû=(kmë½îãm-Ò¡@]ס 1àùp@)ä7— ÕnGÓ4ˆˆÿÄ%µî_VÀu¿Gm·#ÑvUÇã(žÆ—ög¼éùU-Šb$º\ñ ýµˆ=ŸîF2~wÁô5&üq{cù;|t³'±‡œdIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/formDisconnect.png0000644000000000000000000000013215101070305020152 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/formDisconnect.png0000644000175000001440000000105415101070305020142 0ustar00rncbcusers‰PNG  IHDRóÿaóIDATxÚÅ“_HSqÇ?÷îŸÝ®,u!ÙV²,‡õ ½H“zjBT/¦oå›HP”EOõP/½øVà¤p“ÄŠþFK¤\èls›Ê6·{çýõTl7…‚œ‡ßï|Ηó;çüà›T|qa|±97¿@+‰õ±q%̹l† £€øôáƒï·C©7ÜŒ-pòå2AÀÿ;ùRö#÷ã3øyÐ Å9ÔYJêɱ£ÊÆa—]ûUÔ`’g½Û¸šøJ×½(Üž£0’D]$š5¸+™žd?]ÍûYB|ÈÐîkdB‰1Š!]™!ÉÓ„ÐÍØ$*»k™j«¤"gP˜ÌÒœÒé&Ùó:ÍqàQ1/›tAb NË«e4EƺÕA(³Ê±·iNͼ¼Öh"=|ˆÓ>µ‚¨±ãl*çÉ7Àø+ü4·NÕÐÙÿƒ ¶—Qï¬ÅZÌ…NÎ,}ãZ$ƒ;°@(ª‘ݧ²³ÖA½WÁú4Åè*¹.Ì!0ëghñú¢AA|€£ËÅ—ç^Ęãrm%ÉÚQᬎ{cˆÁ݈V'pö£ÊT÷naé¡Ѭ2iÞ`Z7q}¯Â|ƒBÌ]Ƽ }æþ”[hª²—`d½ÞÉE¾ž9 ÿô7þx¼°Ú]‡‡IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/itemPatches.png0000644000000000000000000000013215101070305017443 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemPatches.png0000644000175000001440000000105615101070305017435 0ustar00rncbcusers‰PNG  IHDRóÿabKGDÿÿÿ ½§“ pHYs  šœtIMEÕ 6 º€õí»IDAT8Ë¥“=hSa†ŸŠ– %AB«W:tpÍ໹ˆ‹CnýÁ¬î)8Ú,.½Ô­â訂ƒ­E‡ UÑBÚ+ 6m%Í_or_‡ÛØMZćó¾ï}8œóøO3"ç p²Ë›uà3ði?˜¥?Ìo¶ômõ{uphè*0²/ Õ’šÍ]/®•Þ\,'‰+À‰= _º”[Q;.,®ªîK•zKóoÝx<> öTÒúVè›iÎ]ÑÖ¶´Q•~Ö|½x5ûÃ0Œ³QQ,šø-¸ž÷ˆÅàÚ}¾ýÙ «IDAT8Ë¥“OkQÅ/ ±J¨‚J«–‚]Hm â"[¿„]Ï]ûúܸšÙ¸Ä¥+±‹ m‰m`hUl Y¨`ÑTiÔ4ÿ:39.¦­MZćû.ïÃå¼÷à?aëpµÏ™m`xw”™ÕÂHŸ6?7GÇÆî¹# ¢H Ã߬~««ÛíêõÛr=›ÍÞÆ5èÒüKGûu­¼©v 5Ú‘ŠË~-“ÉÜF4:ÒöNÌï iÉÿ ]©Ö”~´ÍÍ/~5ÆÜJŠRÉ&ˆ ôÊ’JÁrÑ2t,ÍÃG/˜}ð”'ÏV8!wÌDR3”l®LºÔqm¶:Üœž¤Ùj“IçÄð0’4РÞÕ’åú Å2÷¼|°'`|bPa¯ðz2بJÅŠ´´óþ¬«Aœ¿&ºðqÝrmÚåMÉRûR¬µ=i»®;8Ä“—\ÞWãš™98ŽÓ÷êz Z»°U±œ>ÕŠåçÖc|ßÇó¼¾&=gGàò”K(ÈM¹œ»O°/Îçóÿü™ ’n÷ý…ÆXÀK†¸¸Ç$dŒY8ìýÿߨôE¤ÚVIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportPunch.png0000644000000000000000000000013215101070305020227 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportPunch.png0000644000175000001440000000062215101070305020217 0ustar00rncbcusers‰PNG  IHDRÄ´l;sRGB®ÎéLIDAT8Ëí“=KA†Ÿ½¤ó_¤±Na#X¨•…"¤´÷Ú€ÿ }@°ºóOHú U41‚A(ù¸\Anw,ŒG4·gŒ)30° 3ïλ «ø5ŽEþÓãØÎv^OÒ i=Žm®êT¥¥z¡¦t¾Ÿ·‚»ƒÈ -®?ÑDqË{3ð¬ üøüž¼Sßç8<ÙËåêää ¸* Vá+ÃB‘muÏ¥wÅ,Yñ”ÃùÑ} ´ÅÍÚ¤îû3€þÐîCö»Or›S ¹ð–V¥–¨*H1ø‡y®jUj£(N EüỪS­ jÝ ýôÁ,òõ>¦f] &ƒc¬øªx~#Sõ@Vl€Åpž@;ãp<Áî"V@@&sb”6Ÿ.³XR;9œÏÀ+o °˜€aŒÞ÷pjà-üSðvFLÞZ`+Zg<'¤p<]Ï#H/„ÔÏg3>^n-wXî®Q^ݹ4µ¯ƒMùú‘vñ8lCëÝme/TÌÑ$HEGü¼m+`E¯c-èäp6‡üd.c¼´€]¡N• ¤ nµ”½Xï×QïjØuåñtYÆÓù A·)¸ß×Pü‰˜&h°1(ñºœ@ß§®PŸ½‘ì'iW¤æ_“’s ¢ÃvC¿ÜBt-ÿìßV`ÑY¨IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/clipNew.png0000644000000000000000000000013215101070305016576 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057007007 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/clipNew.png0000644000175000001440000000101615101070305016564 0ustar00rncbcusers‰PNG  IHDRÄ´l;ÕIDAT8Ëí”ÁjQ†¿3Î$3ÁÒ„èL"t‘MPAÌÂ]7„nòváhÆöRÁ7кUȇt„"Y'=.rb&Ñt¦‚®üa˜{îå~÷Üÿž{á/éZ¦ídb¨;ÖþHf\óÀNæ¿4€›ÀuàŽª&ÀCàpÃÆk€{pM•xÜÌ“äèwm|ØÎÌÍ]`8TUN{¨ªêqÞ[ÿsà)°[$ë…×Uàñ:«²€^û@X$cÇ|»­Bu~DÀmÀË»vP¿ê/ ˜%vÈÎeÙ^ßDä‰oOb±ÊÊJ8‰¾ŠÈ;³b«ˆžm/>lðøø ™mµ¢>‡À¾mõÓZUœ[ÿ;àêÚåºÔç]U%õ 8³ø‹ezjñaæFn”¬UÆðb0<Ft»]¢(¢ÓéÐjµÇxžG»Ý¦ÙlâºËR‘x½^d½$ɨß)ÃáÉdB†Ôëuf³išâû>¥R ÇY&Çñ /{{R`Üh4¨T*¨*žç1N ‚€r¹<}T‘è&o³ºp]÷ç¤ ð}¿(¼Z*W„å©gïíŸ|ÿõõ‚ë%¾e(OIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/editSelectRect.png0000644000000000000000000000013215101070305020100 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/editSelectRect.png0000644000175000001440000000023515101070305020070 0ustar00rncbcusers‰PNG  IHDRÄ´l;dIDAT8ËíTA€ kïÏç‰ÆÍÈ’hÖ¤í  … ö ̬Ø(Oë/oœ„Ó¬Ža’44@»æŒå­ŒñuòPQfÞ‰{Û©æMeüáŒç=éãvéO¨ðl-Ó.ÿïJIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/formRefresh.png0000644000000000000000000000013215101070305017457 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/formRefresh.png0000644000175000001440000000112715101070305017450 0ustar00rncbcusers‰PNG  IHDRóÿaIDATxÚ­’]HS†ŸmžÍÖü(ZBö£7Ñ¢‚~è"¼N¨!A¢À¢èJº »©›¢ £›®Š¨IhÙ¢Ò¢ÔdË¥’†ÎÎqÆçqcÇ3·sº8¹μé½ûøxÞïµ°‘ºæ*¬û°ü™Æ"MÕƒ«ë‚ j‰Ähž1g%‡Ú¼ ÞjÎxiæGò o4Ñ×>Ê´ê 4€XÂ;ó'x¹KØÙÊØ(Ò  ÀŒz¶mÉO$иj±fÍÞH Qg+½a¬&_úH§`a Äi jösàBËZ@ÜÞAà HŸ ‚÷ÎŒ^Àï›G‚áÛíhJ›•žë@aî„Gr"¥È~½ó÷Í£žs[ÄfPXÖƒ°YÀUÝHB~b&ˆÑ@T=ñð¬ÙT Rx¿¢È¥#8¡|Ç©Ü ±ŒM›@úç[‹ô2¤UW $û±XÁU•{®k~*ïÁj+`iŽç7ꃨ1’8rùæºS »g!.Crþõß_ø5~•Ì ¸ë\œŽÐ6´;»ë–wѹ0ͤº‰û§€> ÛpS ×ÞS^wŒ²íPUŬ°Éb  v&Dëéc’™ÞÃÀ·µ°à9Ý»þ"Žb+Î ³…ŠdƉ|=Œä ùUŠ»þ,®ª“d4;ŠôøÜsà-°ÌÿÔo’JÑú•fIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/viewConnections.png0000644000000000000000000000013215101070305020352 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/viewConnections.png0000644000175000001440000000203415101070305020341 0ustar00rncbcusers‰PNG  IHDRÄ´l; pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<©IDAT8µUML\U=÷ͼÃÌ„ŸÚa†a£‰¡ Rj©cL(¦˜¦¡.ÐhL5q©‹.ÜàRn«ÆEíÒ…‰ÓX»JkC´%¬ÄV TÊPªffàuÞßýŽ f^¦DXÉMîâæžï|ß=ç»÷*’Øaì +€hóB)Ù±/ÜåHJ)µ³0’:ÜoÄý}öìOí¥ÒQÑZ‘„p#ÿó xáÜÌÌ÷ÍSccO¶íkFXB‚"Ð$V#‘_Gæç’d´žÝØ:uj¨%¶@$ ‚X­}¹\þZ)•"ù ½S(̤;:ÚLËR ñ~±Ø Àj´~Y˜˜˜ë½{÷8´V€Z#ÑÑa$D̯FG/x~ø(–Ž)•ËÞ¬RßÍ•ËWIÿiî^}üF:/“)?ÚÞnÞ®TjJO$“±ßJ%tqñ™íÔÚX©Õ¼Ÿ•úöËúâOÇñ»I~pàÀ‡{ņú윎ÉÎær®—Ïó‡lÖ‰Oëéi›N¥\7æTW—w&‘8´[|(ÅôÐÐt²R¡dÛ8uˆŒÜ .›æ•wWV^%\Èå.>kÛofcNk D($D÷#‘›omm°é:³}}óy²·—ÌfÉL†ÕTJ¾Éd6<Ö„\êìÜÜH&…É$“mmd,Æ/-ËÐ^i’r¹¿ÿÖõÍÍâµ’zÕ®aø{ÞÛî4I§Ï¥Rã¾çM™"Ql÷8WE›u…RÊ`6Yà¸GRvz£”ŠÈ4Û`dù!âÿ{ìÛŸ÷/Zy}³ ëDéIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/pluginProperties.png0000644000000000000000000000013215101070305020550 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/pluginProperties.png0000644000175000001440000000226015101070305020540 0ustar00rncbcusers‰PNG  IHDRÄ´l; pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î<=IDAT8µ•mL•eÇ7p€#|AÔÄè4PP3]È¢™ºÔ­–¥RM‹ÒOmºæÔ-5V}kôA{Ñi¹±–Œ|‰i[ ¾Í™/¢&pN¨p„Ã;çå9繯>F¬µåµÝ_®ëºûï_»n%"<Šˆz$Ô+¥’”R…J©J©è‡õ-*((™‘™¹T)¥$EdÌ“––¶jßÞ½×/]¼ØU^^Þœ‘‘ñÙ÷ü3Ó>¸Vñu»«ºÚ›ët– çÇ„Qeee—Dk­E[–ìÞ½»X0²ïµç&n»V™{¿ã‡üÐÝG[_(*Ú3\{¨¿ß¯0|>.××ßúÕŸá(^2ñýwßJÝž™;Îð‡Ùöù†+ß=ûÑðý˜± "¢•R¥Ï-]š³rõêLl6ÒÓÓ§¼´råVgV–3*Дýêüß&>žÝî …w}Óqì«ãÞÍ"òûƒ÷9nJ©|›Í6»°°0v^^Îë×?™“›;I+…iYhàÇo?aNìA2¦hÚ;CÖ®ŠŽ£ŸUx·Šˆ+R"‚Ãáx¥îÜ9o‡×kâ×Z ­e@kéÕZº´–‡>–ë‡çÊýŸòåÖ‘Ýn`)…ÓbäÎyWó!jšãÉ]½=ªbÕ‡¯;RRâmII1©sçNm¿}{ƒRꆈxGN…»Åíî70‡Ôš@ue%ãhëQn˜IÖŠR&gf’ìt:¢’’b€JLTYóæ9ɑЛ].o², <¤6 Ükk£²ÅϬ…ϲèíÍDÅÇ|Á Ý]]!Ûøñ1ÓäNS“gH×_`‘‹‹[ü–U(ÑÑ„E°”" ¬Ø¸±Ùö>8T»ÕÐÐóNAÁ§ó—-{Âf·ÇÕUUU·GÍñÏuu—|¾µq‰‰*<í7 º<CÅÅE'¥¦ÚuL !¥*!!Fkí9üx`úD$ KÈëñÜjëè0"ÎÞ^½¯´ôôËÙÙ;г³·Ø¿ÿ†aYø?•’’8>99°D¤k: 4Ýq»ƒJá¼¾;wö„‚Á]5Vá0>à>`ÙíjÎ’%3Ggønksó@ö´4Çò’’eÀc""y‹/ÇÅ1hšýýúúÂã&LHâ"ÁóXDk·li÷ÃŒ ívŠÖ­›¢¼üi ¹þÌ™‹ñ))“º[[{ÜW®¸®ž>}3lšW¾\=þ¯§NÍêñx qtractor-1.5.9/src/images/PaxHeaders/itemMidiFile.png0000644000000000000000000000013215101070305017536 xustar0030 mtime=1761898693.057267559 30 atime=1761898693.057267559 30 ctime=1761898693.057267559 qtractor-1.5.9/src/images/itemMidiFile.png0000644000175000001440000000112415101070305017524 0ustar00rncbcusers‰PNG  IHDRóÿaIDAT8Ë“ßKSaÇ?çxlÇ9ç±Ò ¶ ‰PP¨AýÐØàe$xmÔ_ÐuAt×MZt3Fu#’‚´XaX3È’¦ù«‰u<;¿ž.ÖrG—ô…^x>ï÷y¿¼ z¨¯ |äH9®'+«kf"™¼ t ð<× Ö÷­=ñ}_ÞÏ/ì†q èü' ìˆ˜V°æVÅrD~YžÌ¾~û#ßÕ!µ–àú`¹e{ðÓtpl_¥÷ü9#ûb⮢(½Õ­àx`»A[Ú û'ØØ,Ñ}6ÉÕ ]§A9ràzPvÀ¯±ÖÚjpùb sß"¬‡hŽD©ëÀö`ß_@U*=E Åþöô&¨Þ~PvaßD„Íõ^½å]>G2'•꽞z433cyƒªƒª6×WÈ<ax(Jsc™p(r#“Y*¶··ß üŠƒZ½™ex(Êììét'…Â}}‰FMk»3>þyº®ƒZåó9ÚÂeÒéN C'•Š“Í~b{{O/‹]êa€åT¢´Ýʹ£#NÃÐñÿì68ØaD"M=cuÐr*‘ö\ºÎÓìlÛellaqqGr¹­yMÓ6+ÄN‚+Ám×È~ýÀ³çÓLN.¡ë¦¦–¿-/—ž”J¥¼ò?ßYUÕ¦–––Äîîn, © Ó4ë¿)5s\ÁIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/trackAudio.png0000644000000000000000000000013215101070305017263 xustar0030 mtime=1761898693.058267562 30 atime=1761898693.058267562 30 ctime=1761898693.058267562 qtractor-1.5.9/src/images/trackAudio.png0000644000175000001440000000046515101070305017260 0ustar00rncbcusers‰PNG  IHDRóÿaüIDAT8ËÍ“¿jÃ0‡¿ü™cRè ö({ÈØ(öà¡¥£áJ‡fOö<„CÞ /Ò¥ÏÒ9Az6²%wêPÐôûî§; þÝ#"覈تªٯ⺮ŸKCÐβ,7À]$"¦ÛqHžç{`Ùê¦ãë‡tmœ]1»1ýì® ßÕ‹ë¢Í„¾÷óð:XÖ˜øÑÔ)¢/·½FEñàU}p­(ŠlÇM¸0Ó>û³,ûÞÕØ£Z¦iº ½Ä$IŽ*~r³O†.€H¬…ž±À7𠜀3p \Т×%¸(äú§Ÿï“n{ú¸ˆoIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/editSelectCurve.png0000644000000000000000000000013215101070305020267 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/editSelectCurve.png0000644000175000001440000000126415101070305020262 0ustar00rncbcusers‰PNG  IHDRÄ´l;{IDAT8Ë¥•MkQ†ŸÓ¦HÐT ˆ ¨-ØR•’dQA]ÕU"üB*‚"l·þ€€ÐWj‚+)¸»7Eè¦ue¦ ]D( ê"_­ßÒšã"wÚ›ÉL xàÀ sïÃ{ßsÎa+ºL6zM~5©Ö÷†·) |‘…e± 1`ðÛä ªE$ ¼þQà;°lhöp"¸O•Î5`¸)œ °¨ª2/Âq Ômõ\ð€˜£ýᜪ>9 S3w+Ë.37†áªªÎ‹0 ìbw ¤ìãÛ’=ÅbŽZ=&òøJ†óé$”‹E&’ГáôÈRŽŒÁáÈuÆ ò{1ª{`ä’Ó„ÆS®Yâ’&IÔåDý #QX­@a',ìj A>G¶üÕ— T–]ÊÅ"ý‰"Bnr’ ¹SÄSÙ¾þ„ö‰È`¨æ[ým±¥ ˆ›"ÝŸqзy´ìºZv]tp:q>€kÀ!ËJ‚7€Uà{X` 'ÃxšdÓ9 ôI€À’Y[·ûÙ^Wü5½[{§:þà/r­ U`8¼7C ¶•PE©‹ðôÔÊ.Údn…™À›–¨m7€o¦ù?ÁИëOhÛ£ü6E ¯%ÖwÀÏn˜‘3A› üÞvp»ª@}öw7ïçæî ."y{ŒÃÀäa¸n®Õ ¨ék[;<âGoö‹–E›0c5á!Û]}f‚<Ú,pÙƒÕÙ½--ãXéY`Èúl ¬€]A3Þ>ú,ú}4ÊótEào& 0Y«²€ @;yß¼àCˆüGü¥æÿ}~ëLIEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/transportRecord.png0000644000000000000000000000013215101070305020370 xustar0030 mtime=1761898693.059267566 30 atime=1761898693.059267566 30 ctime=1761898693.059267566 qtractor-1.5.9/src/images/transportRecord.png0000644000175000001440000000075215101070305020364 0ustar00rncbcusers‰PNG  IHDRÄ´l;±IDAT8ËíÓÁjÔPÆñÿMÒ”® S«gS,(*¤ ¸q'‚Oà‹øŠâB|WnÚb¡ JµÐŠ £3¥Í$¹÷~.’‘b§“éBW=p7çÂ/çæÀyýë2“.Ó¥¥nØj=7ssw±E”¢(²ìCÞë=]ÙÛ{}føçòòfxãæ%·rõ‚_X@A€Y‹KSòOýáööÖõÏ_ºSýNgݬ­u‹NǸ(ÂH „‘GÖ’ïìè`cãÝêîî <ú»‘ÌϯjqñJ6;kÊ4ER…x_¥„‰c´Û·Þöûï /'ÂÌÌ<;Š¢v‘$8©Jˆj¬Jüçc&†/€É°lyoØÿEq0¨¨fP%f4ÀÎZ¬µGá³aœÿå8*„0^u«î œD>ÌâF¸ÌsÊ<ÁMñ¯ p@9æ.8Cq–E¨‡SLïM›S§õuZÁ~#ìࡤ á¼àI#| Þ{ø6zûqǶBUÂÖ#x3õJ¯Ã¦ƒË@ '-ë¤E…Ž]éà4ø6t<ÈáÕ!} Wb_K¸z^ÿ§~¶*íô€_¯»IEND®B`‚qtractor-1.5.9/src/images/PaxHeaders/editCopy.png0000644000000000000000000000013215101070305016755 xustar0030 mtime=1761898693.057222416 30 atime=1761898693.057222416 30 ctime=1761898693.057222416 qtractor-1.5.9/src/images/editCopy.png0000644000175000001440000000164315101070305016751 0ustar00rncbcusers‰PNG  IHDRÄ´l;sBIT|dˆ pHYs & &Q©©3tEXtSoftwarewww.inkscape.org›î< IDAT8µ•?HkWÇ?瞘?5Å›Et© vС™*u' ÅÅÌ‚tq."t‘"D°“tÓ “*.ÄÁED}øš†ò0&¢Fóî½çtÈ5į¿årÏï÷ûœïùñ;¿ÿ“‰ªð¸ßjŸ4à6 þ+ØZ€/ðVÅ6¹à? àÖݤ¦yª¿š››û©««+Z­X)%ËËË©Têg`È׃˜ü9Èd2¿x<™ÍfµïÝJ)Ñßßã8LLLlnnþüܸ'©«Ø>ÓZ‹õõuÎÎÎDOOJ)Ùßßgcc€µµµ¯K¥ÒÌööö[à(5WÌï÷Çiooçøø˜ëëkÇauuÓ4iii!ÆoKào·$åF-0Àåå%[[[„Ãa)‹¤Ói...èíímžŸŸÿøè¬YS±mÛœŸŸ‹Å˜åööö£˜@ }>_g4O&“ØÞºÊkƒ•R lÛæîîŽX,Fww7RJ à —Ë133Ãôô4SSS¯€¹d2ùø(Öh­Ñº\²ŽŽB „ •JQ(*±ããã_$“ÉNÊ-[„5~‚†R ¥ŽãH$8==ett”¥¥%®®®´+²ÒŸÕŠ”k|ssó~Qööö°, )%}}}äóy¼ÞÊ}ÖËÕ` ‰ „@JYQì÷ûyº0MMM„B!îïïëøØòÖØØ˜/srrRŒŒà÷û°,‹……Z[[1MóEÅÀÁððð¦iöçr¹®ÉÉɸÖ: …8::²,´ÖH)I$â8•Q!êà¯t:ýk:n¾‘Rö!‚Åb‘J¥Zk„ø|>²Ù,CCC/*ÖÀ;àøxãõzïƒÁ +++ukÙÖÖ†mÛÚÍ©ÌèZ}ü´Afww÷·H$ÒlÛvõl~ŸÉdÞ§nðñ+ñázè¢@sð;à xMyø«Fà'ŸAù65ŠÓ.ìÅçê“Ø¿§1©ÿ IEND®B`‚qtractor-1.5.9/src/PaxHeaders/qtractorFileListView.cpp0000644000000000000000000000013215101070305020054 xustar0030 mtime=1761898693.071267604 30 atime=1761898693.071267604 30 ctime=1761898693.071267604 qtractor-1.5.9/src/qtractorFileListView.cpp0000644000175000001440000011624415101070305020054 0ustar00rncbcusers// qtractorFileListView.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 "qtractorFileListView.h" #include "qtractorRubberBand.h" #include "qtractorDocument.h" #include "qtractorOptions.h" #include "qtractorSession.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #include #endif //---------------------------------------------------------------------- // class qtractorFileGroupItem -- custom group list view item. // // Constructors. qtractorFileGroupItem::qtractorFileGroupItem ( const QString& sName, int iType ) : QTreeWidgetItem(iType) { QTreeWidgetItem::setIcon(0, QIcon::fromTheme("itemGroup")); QTreeWidgetItem::setText(0, sName); Qt::ItemFlags flags = QTreeWidgetItem::flags(); if (iType == qtractorFileListView::GroupItem) { flags |= Qt::ItemIsEditable; flags &= ~Qt::ItemIsSelectable; } QTreeWidgetItem::setFlags(flags); } // Default destructor. qtractorFileGroupItem::~qtractorFileGroupItem (void) { } // Instance accessors. void qtractorFileGroupItem::setName ( const QString& sName ) { QTreeWidgetItem::setText(0, sName); } QString qtractorFileGroupItem::name (void) const { return QTreeWidgetItem::text(0); } qtractorFileGroupItem *qtractorFileGroupItem::groupItem (void) const { QTreeWidgetItem *pParent = QTreeWidgetItem::parent(); while (pParent && pParent->type() != qtractorFileListView::GroupItem) pParent = pParent->parent(); return static_cast (pParent); } qtractorFileListView *qtractorFileGroupItem::listView (void) const { return static_cast (QTreeWidgetItem::treeWidget()); } // To show up whether its open or not. void qtractorFileGroupItem::setOpen ( bool bOpen ) { // Set the proper pixmap of this... if (type() == qtractorFileListView::GroupItem) { QTreeWidgetItem::setIcon(0, QIcon::fromTheme( bOpen ? "itemGroupOpen" : "itemGroup")); } // Open it up... QTreeWidgetItem::setExpanded(bOpen); // All ancestors should be also visible. if (bOpen) { qtractorFileGroupItem *pGroupItem = groupItem(); if (pGroupItem) pGroupItem->setOpen(true); } } bool qtractorFileGroupItem::isOpen (void) const { return QTreeWidgetItem::isExpanded(); } // Tooltip renderer. QString qtractorFileGroupItem::toolTip (void) const { return name(); } //---------------------------------------------------------------------- // class qtractorFileListItem -- custom file list view item. // // Constructors. qtractorFileListItem::qtractorFileListItem ( const QString& sPath ) : qtractorFileGroupItem(QFileInfo(sPath).fileName(), qtractorFileListView::FileItem) { m_sPath = sPath; } // Default destructor. qtractorFileListItem::~qtractorFileListItem (void) { } // Full path accessor. const QString& qtractorFileListItem::path (void) const { return m_sPath; } //---------------------------------------------------------------------- // class qtractorFileChannelItem -- custom channel list view item. // // Constructors. qtractorFileChannelItem::qtractorFileChannelItem ( qtractorFileListItem *pFileItem, const QString& sName, unsigned short iChannel ) : qtractorFileGroupItem(sName, qtractorFileListView::ChannelItem) { m_iChannel = iChannel; pFileItem->addChild(this); } // Default destructor. qtractorFileChannelItem::~qtractorFileChannelItem (void) { } // File channel accessor. unsigned short qtractorFileChannelItem::channel (void) const { return m_iChannel; } // Full path accessor. const QString qtractorFileChannelItem::path (void) const { QString sPath; qtractorFileListItem *pFileItem = static_cast (QTreeWidgetItem::parent()); if (pFileItem) sPath = pFileItem->path(); return sPath; } //---------------------------------------------------------------------- // class qtractorFileChannelDrag -- custom file channel drag object. // static const char *c_pszFileChannelMimeType = "qtractor/file-channel"; // Encoder method. void qtractorFileChannelDrag::encode ( QMimeData *pMimeData, const qtractorFileChannelDrag::List& items ) { // First, compute total serialized data size... unsigned int iSize = sizeof(unsigned short); QListIterator iter(items); while (iter.hasNext()) { const Item& item = iter.next(); iSize += sizeof(unsigned short); iSize += item.path.length() + 1; } // Allocate the data array... QByteArray data(iSize, (char) 0); char *pData = data.data(); // Header says how much items there are... unsigned short iCount = items.count(); ::memcpy(pData, &iCount, sizeof(unsigned short)); pData += sizeof(unsigned short); // No for actual serialization... iter.toFront(); while (iter.hasNext()) { const Item& item = iter.next(); ::memcpy(pData, &item.channel, sizeof(unsigned short)); pData += sizeof(unsigned short); unsigned int cchPath = item.path.length() + 1; ::memcpy(pData, item.path.toUtf8().constData(), cchPath); pData += cchPath; } // Ok, done. pMimeData->setData(c_pszFileChannelMimeType, data); } // Decode trial method. bool qtractorFileChannelDrag::canDecode ( const QMimeData *pMimeData ) { return pMimeData->hasFormat(c_pszFileChannelMimeType); } // Decode method. qtractorFileChannelDrag::List qtractorFileChannelDrag::decode ( const QMimeData *pMimeData ) { List items; QByteArray data = pMimeData->data(c_pszFileChannelMimeType); if (data.size() < (int) sizeof(unsigned short) + 1) return items; const char *pData = data.constData(); unsigned short iCount = 0; ::memcpy(&iCount, pData, sizeof(unsigned short)); pData += sizeof(unsigned short); Item item; for (unsigned short i = 0; i < iCount; ++i) { ::memcpy(&item.channel, pData, sizeof(unsigned short)); pData += sizeof(unsigned short); item.path = pData; pData += item.path.length() + 1; items.append(item); } return items; } //---------------------------------------------------------------------------- // qtractorFileListView -- Group/File list view, supporting drag-n-drop. // // Constructor. qtractorFileListView::qtractorFileListView ( qtractorFileList::Type iFileType, QWidget *pParent ) : QTreeWidget(pParent), m_iFileType(iFileType) { m_pAutoOpenTimer = nullptr; m_iAutoOpenTimeout = 0; m_pDragItem = nullptr; m_pDropItem = nullptr; m_pRubberBand = nullptr; QTreeWidget::setRootIsDecorated(false); QTreeWidget::setUniformRowHeights(true); QTreeWidget::setAlternatingRowColors(true); // QTreeWidget::setDragEnabled(true); QTreeWidget::setAcceptDrops(true); QTreeWidget::setDropIndicatorShown(true); QTreeWidget::setAutoScroll(true); QTreeWidget::setSelectionMode(QAbstractItemView::ExtendedSelection); QTreeWidget::setSizePolicy( QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); QTreeWidget::setSortingEnabled(false); QHeaderView *pHeader = QTreeWidget::header(); pHeader->setDefaultAlignment(Qt::AlignLeft); // pHeader->setDefaultSectionSize(160); #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); // Trap for help/tool-tips events. QTreeWidget::viewport()->installEventFilter(this); setAutoOpenTimeout(800); QObject::connect(this, SIGNAL(itemClicked(QTreeWidgetItem*,int)), SLOT(itemClickedSlot(QTreeWidgetItem*))); QObject::connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), SLOT(itemActivatedSlot(QTreeWidgetItem*))); QObject::connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), SLOT(itemExpandedSlot(QTreeWidgetItem*))); QObject::connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)), SLOT(itemCollapsedSlot(QTreeWidgetItem*))); QObject::connect(QTreeWidget::itemDelegate(), SIGNAL(commitData(QWidget*)), SLOT(itemRenamedSlot())); } // Default destructor. qtractorFileListView::~qtractorFileListView (void) { clear(); setAutoOpenTimeout(0); } // File list type property. void qtractorFileListView::setFileType ( qtractorFileList::Type iFileType ) { m_iFileType = iFileType; } qtractorFileList::Type qtractorFileListView::fileType (void) const { return m_iFileType; } // Add a new file item, optionally under a given group. qtractorFileListItem *qtractorFileListView::addFileItem ( const QString& sPath, qtractorFileGroupItem *pParentItem ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return nullptr; qtractorFileListItem *pFileItem = findFileItem(sPath); if (pFileItem == nullptr) { pFileItem = createFileItem(sPath); if (pFileItem) { // Add to file/path registry... pSession->files()->addFileItem(m_iFileType, pFileItem->path()); // Insert the new file item in place... if (pParentItem) { if (pParentItem->type() == GroupItem) { pParentItem->insertChild(0, pFileItem); } else { // It must be a group item... QTreeWidgetItem *pItem = static_cast (pParentItem); pParentItem = groupItem(pParentItem); if (pParentItem) { const int iItem = pParentItem->indexOfChild(pItem); if (iItem >= 0) pParentItem->insertChild(iItem + 1, pFileItem); else pItem->addChild(pFileItem); } else { const int iItem = QTreeWidget::indexOfTopLevelItem(pItem); if (iItem >= 0) QTreeWidget::insertTopLevelItem(iItem + 1, pFileItem); else QTreeWidget::addTopLevelItem(pFileItem); } } } else QTreeWidget::addTopLevelItem(pFileItem); emit contentsChanged(); } } #if 0 if (pFileItem) QTreeWidget::setCurrentItem(pFileItem); #endif return pFileItem; } // Add a new group item, optionally under another group. qtractorFileGroupItem *qtractorFileListView::addGroupItem ( const QString& sName, qtractorFileGroupItem *pParentItem ) { qtractorFileGroupItem *pGroupItem = findGroupItem(sName); if (pGroupItem == nullptr) { pGroupItem = new qtractorFileGroupItem(sName); if (pParentItem) pParentItem->addChild(pGroupItem); else addTopLevelItem(pGroupItem); emit contentsChanged(); } #if 0 QTreeWidget::setCurrentItem(pGroupItem); #endif return pGroupItem; } // Remove an existing group item, by name (should be unique). void qtractorFileListView::removeGroupItem ( const QString& sName ) { qtractorFileGroupItem *pGroupItem = findGroupItem(sName); if (pGroupItem) { delete pGroupItem; // emit contentsChanged(); } } // Remove an existing file item, by path (should be unique). void qtractorFileListView::removeFileItem ( const QString& sPath ) { qtractorFileListItem *pFileItem = findFileItem(sPath); if (pFileItem) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pSession->files()->removeFileItem(m_iFileType, pFileItem->path()); delete pFileItem; // emit contentsChanged(); } } // Current group item accessor... qtractorFileGroupItem *qtractorFileListView::currentGroupItem (void) const { return groupItem(QTreeWidget::currentItem()); } // Current file item accessor... qtractorFileListItem *qtractorFileListView::currentFileItem (void) const { QTreeWidgetItem *pItem = QTreeWidget::currentItem(); if (pItem && pItem->type() == FileItem) return static_cast (pItem); return nullptr; } // Make as current selection an existing file item. qtractorFileListItem *qtractorFileListView::selectFileItem ( const QString& sPath, int iChannel ) { qtractorFileListItem *pFileItem = findFileItem(sPath); if (pFileItem == nullptr) { QTreeWidget::setCurrentItem(nullptr); return nullptr; } // Shall we go deep further intto a channel item? if (iChannel >= 0) { // Open file item... pFileItem->setOpen(true); // Select channel item... const int iChildCount = pFileItem->childCount(); for (int i = 0; i < iChildCount; ++i) { QTreeWidgetItem *pItem = pFileItem->child(i); if (pItem->type() == qtractorFileListView::ChannelItem) { qtractorFileChannelItem *pChannelItem = static_cast (pItem); if (pChannelItem && pChannelItem->channel() == iChannel) { QTreeWidget::setCurrentItem(pChannelItem); return pFileItem; } } } } // Open file group, if any... qtractorFileGroupItem *pGroupItem = pFileItem->groupItem(); if (pGroupItem) pGroupItem->setOpen(true); // Nothing else than select file item... QTreeWidget::setCurrentItem(pFileItem); return pFileItem; } // Open and add a new file item below the current group one. void qtractorFileListView::openFile (void) { // Ask for the filename to open... QStringList files = openFileNames(); // Check if its a valid file... if (files.isEmpty()) return; // Find a proper group parent group item... qtractorFileListItem *pFileItem = nullptr; qtractorFileGroupItem *pParentItem = currentGroupItem(); // Pick each one of the selected files... QStringListIterator iter(files); while (iter.hasNext()) { const QString& sPath = iter.next(); // Add the new file item... pFileItem = addFileItem(sPath, pParentItem); // Make all this new open and visible. if (pParentItem && pFileItem) pParentItem->setOpen(true); } // Make the last one current... if (pFileItem) QTreeWidget::setCurrentItem(pFileItem); } // Add a new group item below the current one. void qtractorFileListView::newGroup (void) { qtractorFileGroupItem *pParentItem = currentGroupItem(); #if 0 if (pParentItem && !pParentItem->isOpen()) pParentItem = pParentItem->groupItem(); #endif qtractorFileGroupItem *pGroupItem = addGroupItem(tr("New Group"), pParentItem); if (pGroupItem) { pParentItem = pGroupItem->groupItem(); if (pParentItem) pParentItem->setOpen(true); editItem(pGroupItem, 0); } } // Copy/cut current file item(s) to clipboard. void qtractorFileListView::copyItem ( bool bCut ) { // Build URL list... QList urls; int iUpdate = 0; QList items = selectedItems(); QListIterator iter(items); while (iter.hasNext()) { QTreeWidgetItem *pItem = iter.next(); if (pItem->type() == qtractorFileListView::FileItem) { qtractorFileListItem *pFileItem = static_cast (pItem); if (pFileItem) { urls.append(QUrl::fromLocalFile(pFileItem->path())); if (bCut) { delete pFileItem; ++iUpdate; } } } } // Copy it to system clipboard... if (!urls.isEmpty()) { QMimeData *pMimeData = new QMimeData(); pMimeData->setUrls(urls); QApplication::clipboard()->setMimeData(pMimeData); } // Make proper notifications... if (iUpdate > 0) emit contentsChanged(); } // Paste file items from clipboard. void qtractorFileListView::pasteItem (void) { #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) const QMimeData *pMimeData = QApplication::clipboard()->mimeData(); if (pMimeData == nullptr) return; if (!pMimeData->hasUrls()) return; // Find a proper group parent group item... qtractorFileGroupItem *pParentItem = currentGroupItem(); int iUpdate = 0; QListIterator iter(pMimeData->urls()); while (iter.hasNext()) { const QString& sPath = iter.next().toLocalFile(); if (!sPath.isEmpty()) { qtractorFileListItem *pFileItem = addFileItem(sPath, pParentItem); // Make all this new open and visible. if (pFileItem) { ++iUpdate; if (pParentItem) pParentItem->setOpen(true); } } } // Make proper notifications... if (iUpdate > 0) emit contentsChanged(); #endif } // Rename current group/file item. void qtractorFileListView::renameItem (void) { qtractorFileGroupItem *pGroupItem = currentGroupItem(); if (pGroupItem) editItem(pGroupItem, 0); } // Remove current group/file item. void qtractorFileListView::removeItem (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; int iUpdate = 0; QList items = selectedItems(); if (!items.isEmpty()) { QMutableListIterator iter(items); while (iter.hasNext()) { QTreeWidgetItem *pItem = iter.next(); if (pItem->type() == ChannelItem) iter.remove(); } // Prompt user if he/she's sure about this... if (items.count() > 0 && pOptions->bConfirmRemove) { if (QMessageBox::warning(this, tr("Warning"), tr("About to remove %1 file item(s).\n\n" "Are you sure?") .arg(items.count()), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; } // Definite multi-delete... iter.toFront(); while (iter.hasNext()) { QTreeWidgetItem *pItem = iter.next(); // Remove from file registry, when applicable... if (pItem->type() == FileItem) { qtractorFileListItem *pFileItem = static_cast (pItem); if (pFileItem) pSession->files()->removeFileItem(m_iFileType, pFileItem->path()); } // Scrap view item... delete pItem; ++iUpdate; } } else { QTreeWidgetItem *pItem = QTreeWidget::currentItem(); if (pItem && pItem->type() != ChannelItem) { // Prompt user if he/she's sure about this... if (pOptions->bConfirmRemove) { if (QMessageBox::warning(this, tr("Warning"), tr("About to remove %1 item:\n\n" "\"%2\"\n\n" "Are you sure?") .arg(pItem->type() == GroupItem ? tr("group") : tr("file")) .arg(pItem->text(0)), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; } // Remove from file registry, when applicable... if (pItem->type() == FileItem) { if (pItem->type() == FileItem) { qtractorFileListItem *pFileItem = static_cast (pItem); if (pFileItem) pSession->files()->removeFileItem(m_iFileType, pFileItem->path()); } } // Scrap view item... delete pItem; ++iUpdate; } } if (iUpdate > 0) emit contentsChanged(); } // Emit actiovation signal for given item... void qtractorFileListView::activateItem ( QTreeWidgetItem *pItem ) { if (pItem == nullptr) pItem = QTreeWidget::currentItem(); if (pItem == nullptr) return; if (pItem->type() == FileItem) { qtractorFileListItem *pFileItem = static_cast (pItem); if (pFileItem) emit activated(pFileItem->path(), -1); } else if (pItem->type() == ChannelItem) { qtractorFileChannelItem *pChannelItem = static_cast (pItem); if (pChannelItem) emit activated(pChannelItem->path(), pChannelItem->channel()); } } // Clean-up unused file items. void qtractorFileListView::cleanupItem ( QTreeWidgetItem *pItem ) { if (pItem == nullptr) return; switch (pItem->type()) { case GroupItem: { const int iChildCount = pItem->childCount(); for (int i = 0; i < iChildCount; ++i) cleanupItem(pItem->child(i)); break; } case FileItem: { qtractorSession *pSession = qtractorSession::getInstance(); qtractorFileListItem *pFileItem = static_cast (pItem); if (pSession && pFileItem) { const QString& sPath = pFileItem->path(); qtractorFileList::Item *pFileListItem = pSession->files()->findItem(m_iFileType, sPath); if (pFileListItem && pFileListItem->clipRefCount() < 1) pItem->setSelected(true); } break; } default: break; } } void qtractorFileListView::cleanup (void) { QTreeWidget::setCurrentItem(nullptr); QTreeWidget::selectionModel()->clearSelection(); const int iItemCount = QTreeWidget::topLevelItemCount(); for (int i = 0; i < iItemCount; ++i) cleanupItem(QTreeWidget::topLevelItem(i)); removeItem(); } // Master clean-up. void qtractorFileListView::clear (void) { dragLeaveEvent(nullptr); m_pDragItem = nullptr; QTreeWidget::clear(); } // Find a group item, given its name. qtractorFileGroupItem *qtractorFileListView::findGroupItem ( const QString& sName ) const { return static_cast (findItem(sName, GroupItem)); } // Find a file item, given its name. qtractorFileListItem *qtractorFileListView::findFileItem ( const QString& sPath ) const { return static_cast (findItem(sPath, FileItem)); } // Find a list view item, given its type and name. QTreeWidgetItem *qtractorFileListView::findItem ( const QString& sText, int iType ) const { // Iterate all over the place to search for the item... QList items = QTreeWidget::findItems(sText, Qt::MatchFlags(Qt::MatchExactly | Qt::MatchRecursive), (iType == GroupItem ? 0 : pathColumn())); // Really check if it's of the intended type... QListIterator iter(items); while (iter.hasNext()) { QTreeWidgetItem *pItem = iter.next(); if (pItem->type() == iType) return pItem; } // Not found. return nullptr; } // Find and return the nearest group item... qtractorFileGroupItem *qtractorFileListView::groupItem ( QTreeWidgetItem *pItem ) const { while (pItem && pItem->type() != GroupItem) pItem = pItem->parent(); return static_cast (pItem); } // Prompt for proper file list open. QStringList qtractorFileListView::openFileNames (void) { // Ask for the filename to open... QStringList files = getOpenFileNames(); // Remember recent directory... if (!files.isEmpty()) setRecentDir(QFileInfo(files.first()).absolutePath()); return files; } // In-place toggle slot. void qtractorFileListView::itemClickedSlot ( QTreeWidgetItem *pItem ) { if (pItem == nullptr) return; if (pItem->type() == GroupItem) { qtractorFileGroupItem *pGroupItem = static_cast (pItem); if (pGroupItem) pGroupItem->setOpen(!pGroupItem->isOpen()); } else if (pItem->type() == FileItem) { qtractorFileListItem *pFileItem = static_cast (pItem); if (pFileItem) emit selected(pFileItem->path(), -1, pFileItem->isSelected()); if (pFileItem && pFileItem->childCount() > 0) pFileItem->setOpen(!pFileItem->isOpen()); } else if (pItem->type() == ChannelItem) { qtractorFileChannelItem *pChannelItem = static_cast (pItem); if (pChannelItem) emit selected(pChannelItem->path(), pChannelItem->channel(), pChannelItem->isSelected()); } } // In-place activation slot. void qtractorFileListView::itemActivatedSlot ( QTreeWidgetItem *pItem ) { activateItem(pItem); } // In-place open/close slot. void qtractorFileListView::itemExpandedSlot ( QTreeWidgetItem *pItem ) { if (pItem->type() == GroupItem) pItem->setIcon(0, QIcon::fromTheme("itemGroupOpen")); } void qtractorFileListView::itemCollapsedSlot ( QTreeWidgetItem *pItem ) { if (pItem->type() == GroupItem) pItem->setIcon(0, QIcon::fromTheme("itemGroup")); } // Tracking of item changes (e.g in-place edits). void qtractorFileListView::itemRenamedSlot (void) { // We just know that something is in edit mode... emit contentsChanged(); } // Auto-open timeout method. void qtractorFileListView::setAutoOpenTimeout ( int iAutoOpenTimeout ) { m_iAutoOpenTimeout = iAutoOpenTimeout; if (m_pAutoOpenTimer) delete m_pAutoOpenTimer; m_pAutoOpenTimer = 0; if (m_iAutoOpenTimeout > 0) { m_pAutoOpenTimer = new QTimer(this); QObject::connect(m_pAutoOpenTimer, SIGNAL(timeout()), SLOT(timeoutSlot())); } } // Auto-open timeout accessor. int qtractorFileListView::autoOpenTimeout (void) const { return m_iAutoOpenTimeout; } // Auto-open timer slot. void qtractorFileListView::timeoutSlot (void) { if (m_pAutoOpenTimer) { m_pAutoOpenTimer->stop(); qtractorFileGroupItem *pGroupItem = groupItem(m_pDropItem); if (pGroupItem && !pGroupItem->isOpen()) pGroupItem->setOpen(true); } } // Recently used directory, if any. void qtractorFileListView::setRecentDir ( const QString& sRecentDir ) { m_sRecentDir = sRecentDir; } const QString& qtractorFileListView::recentDir (void) const { return m_sRecentDir; } // Trap for help/tool-tip events. bool qtractorFileListView::eventFilter ( QObject *pObject, QEvent *pEvent ) { QWidget *pViewport = QTreeWidget::viewport(); if (static_cast (pObject) == pViewport && pEvent->type() == QEvent::ToolTip) { QHelpEvent *pHelpEvent = static_cast (pEvent); if (pHelpEvent) { QTreeWidgetItem *pItem = QTreeWidget::itemAt(pHelpEvent->pos()); qtractorFileGroupItem *pFileItem = static_cast (pItem); if (pFileItem) { QToolTip::showText(pHelpEvent->globalPos(), pFileItem->toolTip(), pViewport); return true; } } } // Not handled here. return QTreeWidget::eventFilter(pObject, pEvent); } // Handle mouse events for drag-and-drop stuff. void qtractorFileListView::mousePressEvent ( QMouseEvent *pMouseEvent ) { dragLeaveEvent(nullptr); if (pMouseEvent->button() == Qt::LeftButton) { m_posDrag = pMouseEvent->pos(); m_pDragItem = QTreeWidget::itemAt(m_posDrag); #if 0//NO_SINGLE_SELECTION_MODE if ((pMouseEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) == 0) { QTreeWidget::clearSelection(); QTreeWidget::setSelectionMode( QAbstractItemView::SingleSelection); } QTreeWidget::setCurrentItem(m_pDragItem); #endif } QTreeWidget::mousePressEvent(pMouseEvent); } void qtractorFileListView::mouseMoveEvent ( QMouseEvent *pMouseEvent ) { QTreeWidget::mouseMoveEvent(pMouseEvent); if ((pMouseEvent->buttons() & Qt::LeftButton) && m_pDragItem && ((pMouseEvent->pos() - m_posDrag).manhattanLength() >= QApplication::startDragDistance())) { // We'll start dragging something alright... QMimeData *pMimeData = nullptr; switch (m_pDragItem->type()) { case GroupItem: { pMimeData = new QMimeData(); pMimeData->setText(m_pDragItem->text(0)); break; } case FileItem: { // Selected items must only be file items... QList urls; QListIterator iter(selectedItems()); while (iter.hasNext()) { QTreeWidgetItem *pItem = iter.next(); if (pItem->type() == FileItem) { qtractorFileListItem *pFileItem = static_cast (pItem); if (pFileItem) urls.append(QUrl::fromLocalFile(pFileItem->path())); } } // Have we got something? if (!urls.isEmpty()) { pMimeData = new QMimeData(); pMimeData->setUrls(urls); } break; } case ChannelItem: { qtractorFileChannelDrag::List items; QListIterator iter(selectedItems()); while (iter.hasNext()) { QTreeWidgetItem *pItem = iter.next(); if (pItem->type() == ChannelItem) { qtractorFileChannelItem *pChannelItem = static_cast (pItem); if (pChannelItem) { qtractorFileChannelDrag::Item item; item.channel = pChannelItem->channel(); item.path = pChannelItem->path(); items.append(item); } } } if (!items.isEmpty()) { pMimeData = new QMimeData(); qtractorFileChannelDrag::encode(pMimeData, items); } break; } default: break; } // Have we got it right? if (pMimeData) { QDrag *pDrag = new QDrag(this); pDrag->setMimeData(pMimeData); pDrag->setPixmap(m_pDragItem->icon(0).pixmap(16)); pDrag->setHotSpot(QPoint(-4, -12)); pDrag->exec(Qt::LinkAction); // We've dragged and maybe dropped it by now... // QTreeWidget::reset(); #if 0//NO_SINGLE_SELECTION_MODE QTreeWidget::setSelectionMode( QAbstractItemView::ExtendedSelection); #endif dragLeaveEvent(nullptr); m_pDragItem = nullptr; } } } void qtractorFileListView::mouseReleaseEvent ( QMouseEvent *pMouseEvent ) { QTreeWidget::mouseReleaseEvent(pMouseEvent); #if 0//NO_SINGLE_SELECTION_MODE QTreeWidget::setSelectionMode( QAbstractItemView::ExtendedSelection); #endif dragLeaveEvent(nullptr); m_pDragItem = nullptr; } // Drag-n-drop stuff. bool qtractorFileListView::canDecodeEvent ( QDropEvent *pDropEvent ) { if (m_pDragItem && pDropEvent->source() != this) m_pDragItem = nullptr; return (pDropEvent->mimeData()->hasText() || pDropEvent->mimeData()->hasUrls()); } bool qtractorFileListView::canDropItem ( QTreeWidgetItem *pDropItem ) const { if (m_pDragItem) { while (pDropItem) { if (pDropItem == m_pDragItem) return false; QTreeWidgetItem *pParentItem = pDropItem->parent(); if (pParentItem == m_pDragItem) return false; pDropItem = pParentItem; } } return true; } // Ensure given item is brought to viewport visibility... void qtractorFileListView::ensureVisibleItem ( QTreeWidgetItem *pItem ) { QTreeWidgetItem *pItemAbove = pItem->parent(); if (pItemAbove) { const int iItem = pItemAbove->indexOfChild(pItem); if (iItem > 0) pItemAbove = pItemAbove->child(iItem - 1); } else { const int iItem = QTreeWidget::indexOfTopLevelItem(pItem); if (iItem > 0) { pItemAbove = QTreeWidget::topLevelItem(iItem - 1); if (pItemAbove) { const int iItemCount = pItemAbove->childCount(); if (pItemAbove->isExpanded()) pItemAbove = pItemAbove->child(iItemCount - 1); } } } if (pItemAbove) QTreeWidget::scrollToItem(pItemAbove); QTreeWidget::scrollToItem(pItem); } // Track on the current drop item position. QTreeWidgetItem *qtractorFileListView::dragDropItem ( const QPoint& pos ) { QTreeWidgetItem *pDropItem = QTreeWidget::itemAt(pos); if (pDropItem && pDropItem->type() != ChannelItem) { if (pDropItem != m_pDropItem) { m_pDropItem = pDropItem; ensureVisibleItem(m_pDropItem); if (m_pAutoOpenTimer) m_pAutoOpenTimer->start(m_iAutoOpenTimeout); } } else { m_pDropItem = nullptr; if (m_pAutoOpenTimer) m_pAutoOpenTimer->stop(); } moveRubberBand(m_pDropItem, pos.x() < QTreeWidget::indentation()); return pDropItem; } void qtractorFileListView::dragEnterEvent ( QDragEnterEvent *pDragEnterEvent ) { #if 0 if (!canDecodeEvent(pDragEnterEvent)) { pDragEnterEvent->ignore(); return; } QTreeWidgetItem *pDropItem = dragDropItem(pDragEnterEvent->pos()); if (canDropItem(pDropItem)) { if (!pDragEnterEvent->isAccepted()) { pDragEnterEvent->setDropAction(Qt::MoveAction); pDragEnterEvent->accept(); } } else { pDragEnterEvent->ignore(); } #else // Always accept the drag-enter event, // so let we deal with it during move later... pDragEnterEvent->accept(); #endif } void qtractorFileListView::dragMoveEvent ( QDragMoveEvent *pDragMoveEvent ) { if (!canDecodeEvent(pDragMoveEvent)) { pDragMoveEvent->ignore(); return; } QTreeWidgetItem *pDropItem = dragDropItem( #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) pDragMoveEvent->position().toPoint()); #else pDragMoveEvent->pos()); #endif if (canDropItem(pDropItem)) { if (!pDragMoveEvent->isAccepted()) { pDragMoveEvent->setDropAction(Qt::MoveAction); pDragMoveEvent->accept(); } } else { pDragMoveEvent->ignore(); } } void qtractorFileListView::dragLeaveEvent ( QDragLeaveEvent */*pDragLeaveEvent*/ ) { if (m_pRubberBand) delete m_pRubberBand; m_pRubberBand = nullptr; m_pDropItem = nullptr; if (m_pAutoOpenTimer) m_pAutoOpenTimer->stop(); } void qtractorFileListView::dropEvent ( QDropEvent *pDropEvent ) { const QPoint& pos #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) = pDropEvent->position().toPoint(); #else = pDropEvent->pos(); #endif QTreeWidgetItem *pDropItem = dragDropItem(pos); if (!canDropItem(pDropItem)) { dragLeaveEvent(nullptr); m_pDragItem = nullptr; return; } // Not quite parent, but the exact drop target... qtractorFileGroupItem *pParentItem = static_cast (pDropItem); const bool bOutdent = (pos.x() < QTreeWidget::indentation()); // Get access to the pertinent drop data... int iUpdate = 0; const QMimeData *pMimeData = pDropEvent->mimeData(); // Let's see how many files there are... if (pMimeData->hasUrls()) { QListIterator iter(pMimeData->urls()); iter.toBack(); while (iter.hasPrevious()) { const QString& sPath = iter.previous().toLocalFile(); // Is it one from ourselves (file item) ?... if (m_pDragItem && m_pDragItem->type() == FileItem) { if (dropItem(pDropItem, findItem(sPath, FileItem), bOutdent)) ++iUpdate; } else if (addFileItem(sPath, pParentItem)) ++iUpdate; } } else if (pMimeData->hasText()) { // Maybe its just a new convenience group... const QString& sText = pMimeData->text(); // Is it one from ourselves (group item) ?... if (m_pDragItem && m_pDragItem->type() == GroupItem) { if (dropItem(pDropItem, findItem(sText, GroupItem), bOutdent)) ++iUpdate; } else if (addGroupItem(sText, pParentItem)) ++iUpdate; } // Teke care of change modification... if (iUpdate > 0) { // Make parent open, anyway... qtractorFileGroupItem *pGroupItem = groupItem(pDropItem); if (pGroupItem) pGroupItem->setOpen(true); // Notify that we've changed something. emit contentsChanged(); } dragLeaveEvent(nullptr); m_pDragItem = nullptr; } // Drag-and-drop target method... QTreeWidgetItem *qtractorFileListView::dropItem ( QTreeWidgetItem *pDropItem, QTreeWidgetItem *pDragItem, bool bOutdent ) { // We must be dropping something... if (pDragItem == nullptr) return nullptr; // Take the item from list... int iItem; QTreeWidgetItem *pParentItem = pDragItem->parent(); if (pParentItem) { iItem = pParentItem->indexOfChild(pDragItem); if (iItem >= 0) pDragItem = pParentItem->takeChild(iItem); } else { iItem = QTreeWidget::indexOfTopLevelItem(pDragItem); if (iItem >= 0) pDragItem = QTreeWidget::takeTopLevelItem(iItem); } // Insert it back... if (pDropItem) { if (pDropItem->type() == GroupItem && !bOutdent) { pDropItem->insertChild(0, pDragItem); } else { pParentItem = pDropItem->parent(); if (pParentItem) { iItem = pParentItem->indexOfChild(pDropItem); pParentItem->insertChild(iItem + 1, pDragItem); } else { iItem = QTreeWidget::indexOfTopLevelItem(pDropItem); QTreeWidget::insertTopLevelItem(iItem + 1, pDragItem); } } } else { QTreeWidget::addTopLevelItem(pDragItem); } // Return the new item... return pDragItem; } // Draw a dragging separator line. void qtractorFileListView::moveRubberBand ( QTreeWidgetItem *pDropItem, bool bOutdent ) { // Is there any item upon we migh drop anything? if (pDropItem == nullptr) { if (m_pRubberBand) m_pRubberBand->hide(); return; } // Create the rubber-band if there's none... if (m_pRubberBand == nullptr) { m_pRubberBand = new qtractorRubberBand( QRubberBand::Line, QTreeWidget::viewport()); #if 0 QPalette pal(m_pRubberBand->palette()); pal.setColor(m_pRubberBand->foregroundRole(), Qt::blue); m_pRubberBand->setPalette(pal); m_pRubberBand->setBackgroundRole(QPalette::NoRole); #endif } // Just move it QRect rect = QTreeWidget::visualItemRect(pDropItem); if (pDropItem->type() == GroupItem && !bOutdent) rect.setX(rect.x() + QTreeWidget::indentation()); rect.setTop(rect.bottom() + 1); rect.setHeight(3); m_pRubberBand->setGeometry(rect); // Ah, and make it visible, of course... if (!m_pRubberBand->isVisible()) m_pRubberBand->show(); } // Custom file list loaders. bool qtractorFileListView::loadListElement ( qtractorDocument *pDocument, QDomElement *pElement, QTreeWidgetItem *pItem ) { if (pItem && pItem->type() != GroupItem) return false; qtractorFileGroupItem *pParentItem = static_cast (pItem); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Load children... for (QDomNode nChild = pElement->firstChild(); !nChild.isNull(); nChild = nChild.nextSibling()) { // Convert node to element... QDomElement eChild = nChild.toElement(); if (eChild.isNull()) continue; // Now it depends on item tag/type... if (eChild.tagName() == "group") { qtractorFileGroupItem *pGroupItem = new qtractorFileGroupItem(eChild.attribute("name")); if (pParentItem) pParentItem->addChild(pGroupItem); else addTopLevelItem(pGroupItem); if (!loadListElement(pDocument, &eChild, pGroupItem)) return false; pGroupItem->setOpen( qtractorDocument::boolFromText(eChild.attribute("open"))); } else if (eChild.tagName() == "file") { qtractorFileListItem *pFileItem = createFileItem( pSession->absoluteFilePath(eChild.text())); if (pFileItem) { pFileItem->setText(0, eChild.attribute("name")); if (pParentItem) pParentItem->addChild(pFileItem); else QTreeWidget::addTopLevelItem(pFileItem); if (pSession) pSession->files()->addFileItem(m_iFileType, pFileItem->path()); } } } return true; } // The elemental loader implementation. bool qtractorFileListView::loadElement ( qtractorDocument *pDocument, QDomElement *pElement ) { clear(); return loadListElement(pDocument, pElement, nullptr); } // Custom file list saver. bool qtractorFileListView::saveListElement ( qtractorDocument *pDocument, QDomElement *pElement, QTreeWidgetItem *pItem ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Create the new child element... switch (pItem->type()) { case GroupItem: { qtractorFileGroupItem *pGroupItem = static_cast (pItem); QDomElement eGroup = pDocument->document()->createElement("group"); eGroup.setAttribute("name", pGroupItem->text(0)); eGroup.setAttribute("open", qtractorDocument::textFromBool(pGroupItem->isOpen())); const int iChildCount = pItem->childCount(); for (int i = 0; i < iChildCount; ++i) saveListElement(pDocument, &eGroup, pGroupItem->child(i)); pElement->appendChild(eGroup); break; } case FileItem: { qtractorFileListItem *pFileItem = static_cast (pItem); QString sPath = pFileItem->path(); qtractorFileList::Item *pFileListItem = pSession->files()->findItem(m_iFileType, sPath); if (pFileListItem && (pFileListItem->clipRefCount() > 0 || !pFileListItem->isAutoRemove())) { // Make it all relative to archive or session directory... if (pDocument->isArchive() || pDocument->isSymLink()) sPath = pDocument->addFile(sPath); else sPath = pSession->relativeFilePath(sPath); // Save file item if valid... QDomElement eFile = pDocument->document()->createElement("file"); eFile.setAttribute("name", pFileItem->text(0)); eFile.appendChild(pDocument->document()->createTextNode(sPath)); pElement->appendChild(eFile); } break; } default: break; } return true; } // The elemental saver implementation. bool qtractorFileListView::saveElement ( qtractorDocument *pDocument, QDomElement *pElement ) { const int iItemCount = QTreeWidget::topLevelItemCount(); for (int i = 0; i < iItemCount; ++i) saveListElement(pDocument, pElement, QTreeWidget::topLevelItem(i)); return true; } // end of qtractorFileListView.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMeter.cpp0000644000000000000000000000012715101070305016566 xustar0029 mtime=1761898693.07626762 29 atime=1761898693.07626762 29 ctime=1761898693.07626762 qtractor-1.5.9/src/qtractorMeter.cpp0000644000175000001440000002601715101070305016560 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.9/src/PaxHeaders/qtractorTrackList.h0000644000000000000000000000013215101070305017053 xustar0030 mtime=1761898693.091267667 30 atime=1761898693.091267667 30 ctime=1761898693.091267667 qtractor-1.5.9/src/qtractorTrackList.h0000644000175000001440000002033615101070305017047 0ustar00rncbcusers// qtractorTrackList.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 __qtractorTrackList_h #define __qtractorTrackList_h #include "qtractorScrollView.h" #include #include // Forward declarations. class qtractorTrack; class qtractorTracks; class qtractorTrackList; class qtractorTrackButton; class qtractorRubberBand; class qtractorMidiManager; class qtractorCurveButton; class qtractorPluginListView; class qtractorMeter; class QHeaderView; class QResizeEvent; class QMouseEvent; class QWheelEvent; class QDragEnterEvent; class QDragMoveEvent; class QDropEvent; class QKeyEvent; //---------------------------------------------------------------------------- // qtractorTrackListButtons -- Track button layout widget. class qtractorTrackListButtons : public QWidget { public: // Constructor. qtractorTrackListButtons( qtractorTrack *pTrack, QWidget *pParent = nullptr); // Local child widgets accessors. qtractorTrackButton *recordButton() const { return m_pRecordButton; } qtractorTrackButton *muteButton() const { return m_pMuteButton; } qtractorTrackButton *soloButton() const { return m_pSoloButton; } qtractorCurveButton *curveButton() const { return m_pCurveButton; } // Refresh color (palette) state buttons void updateTrackButtons(); private: // The local child widgets. qtractorTrackButton *m_pRecordButton; qtractorTrackButton *m_pMuteButton; qtractorTrackButton *m_pSoloButton; qtractorCurveButton *m_pCurveButton; }; //---------------------------------------------------------------------------- // qtractorTrackList -- Track list widget. class qtractorTrackList : public qtractorScrollView { Q_OBJECT public: // Constructor. qtractorTrackList(qtractorTracks *pTracks, QWidget *pParent = nullptr); // Destructor. ~qtractorTrackList(); // Track list view column indexes. enum ColumnIndex { Number = 0, Name = 1, Bus = 2, Channel = 3, Patch = 4, Instrument = 5 }; // Main tracks widget accessor. qtractorTracks *tracks() const; // Header view accessor. QHeaderView *header() const; // Find the list view item from track pointer reference. int trackRow(qtractorTrack *pTrack) const; // Find track row/column of given viewport point... int trackRowAt(const QPoint& pos); int trackColumnAt(const QPoint& pos); // Find the track pointer reference from list view item row. qtractorTrack *track(int iTrack) const; // Insert/remove a track item; return actual track of incident. int insertTrack(int iTrack, qtractorTrack *pTrack); int removeTrack(int iTrack); // Manage current track row by index. void setCurrentTrackRow(int iTrack); int currentTrackRow() const; int trackRowCount() const; // Current selected track reference. void setCurrentTrack(qtractorTrack *pTrack); qtractorTrack *currentTrack() const; // Update the list view item from track pointer reference. void updateTrack(qtractorTrack *pTrack); // Update the list view item from MIDI manager pointer reference. void updateMidiTrackItem(qtractorMidiManager *pMidiManager); // Track-button colors (palette) update. void updateTrackButtons(); // Update all track-items/icons methods. void updateItems(); void updateIcons(); // Main table cleaner. void clear(); // Update list content height. void updateContentsHeight(); // Rectangular contents update. void updateContents(const QRect& rect); // Overall contents update. void updateContents(); // Retrieve all current seleceted tracks but one. QList selectedTracks( qtractorTrack *pTrackEx = nullptr, bool bAllTracks = false) const; protected: // Resize event handler. void resizeEvent(QResizeEvent *pResizeEvent); // Draw the time scale. void drawContents(QPainter *pPainter, const QRect& rect); // Retrive the given track row rectangular (in contents coordinates). QRect trackRect(int iTrack) const; // Draw table cell. void drawCell(QPainter *pPainter, int iRow, int iCol, const QRect& rect) const; // Context menu request slot. void contextMenuEvent(QContextMenuEvent *pContextMenuEvent); // Handle mouse double-clicks. void mouseDoubleClickEvent(QMouseEvent *pMouseEvent); // Handle item selection with mouse. void mousePressEvent(QMouseEvent *pMouseEvent); void mouseMoveEvent(QMouseEvent *pMouseEvent); void mouseReleaseEvent(QMouseEvent *pMouseEvent); // Handle zoom with mouse wheel. void wheelEvent(QWheelEvent *pWheelEvent); // Drag-n-drop event handlers. void dragEnterEvent(QDragEnterEvent *pDragEnterEvent); void dragMoveEvent(QDragMoveEvent *pDragMoveEvent); void dropEvent(QDropEvent *pDropEvent); // Show and move rubber-band item. void moveRubberBand(const QPoint& posDrag); void moveRubberBand(const QRect& rectDrag); // Make sure the given (track) rectangle is visible. bool ensureVisibleRect(const QRect& rect); // Keyboard event handler. void keyPressEvent(QKeyEvent *pKeyEvent); // Reset drag/select/move state. void resetDragState(); // Update header extents. void updateHeader(); // Selection methods. void selectTrack(int iTrack, bool bSelect = true, bool bToggle = false); void updateSelect(bool bCommit); void clearSelect(); // Track-list header model pre-decl. class HeaderModel; signals: // More like current row has changed. void selectionChanged(); protected slots: // To have timeline in h-sync with main track view. void contentsYMovingSlot(int cx, int cy); // (Re)create the time scale pixmap. void updatePixmap(int cx, int cy); // Check/update header resize. void updateHeaderSize(int iCol, int, int iColSize); // Update header extents. void resetHeaderSize(int iCol); private: // The logical parent binding. qtractorTracks *m_pTracks; // Local horizontal header. QHeaderView *m_pHeader; // Local double-buffering pixmap. QPixmap m_pixmap; // Model cache item. struct Item { // Constructor Item(qtractorTrack *pTrack); // Destructor. ~Item(); // Bank/program names helper. bool updateBankProgNames(qtractorMidiManager *pMidiManager, const QString& sInstrumentName, QString& sBankName, QString& sProgName) const; // Item updaters. void updateItem(qtractorTrackList *pTrackList); void updateButtons(qtractorTrackList *pTrackList, bool bVisible); void updatePlugins(qtractorTrackList *pTrackList, bool bVisible); void updateMeters(qtractorTrackList *pTrackList, bool bVisible); void updateIcon(qtractorTrackList *pTrackList); // Item members. qtractorTrack *track; QColor ribbon; QPixmap icon; QStringList text; unsigned int flags; // Track-list item widgets. qtractorTrackListButtons *buttons; qtractorPluginListView *plugins; QWidget *meters; }; // Model cache item list. QList m_items; // Model cache track map. QHash m_tracks; // Current selection map. QHash m_select; // Current selected row. int m_iCurrentTrack; // The current selecting/dragging item stuff. enum DragState { DragNone = 0, DragStart, DragMove, DragResize, DragSelect } m_dragState; // For whether we're resizing or moving an item; QPoint m_posDrag; int m_iDragTrack; int m_iDragY; qtractorRubberBand *m_pRubberBand; // To avoid update reentries. int m_iUpdateContents; // Track icon pixmap stuff. enum { IconAudio = 0, IconMidi = 1, IconCount = 2 }; QPixmap *m_pPixmap[IconCount]; }; #endif // __qtractorTrackList_h // end of qtractorTrackList.h qtractor-1.5.9/src/PaxHeaders/qtractorPlugin.h0000644000000000000000000000013215101070305016411 xustar0030 mtime=1761898693.086267651 30 atime=1761898693.086267651 30 ctime=1761898693.086267651 qtractor-1.5.9/src/qtractorPlugin.h0000644000175000001440000007735515101070305016422 0ustar00rncbcusers// qtractorPlugin.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 __qtractorPlugin_h #define __qtractorPlugin_h #include "qtractorEngine.h" #include "qtractorMidiControlObserver.h" #include "qtractorDocument.h" #include #include #include #include #include // Forward declarations. class qtractorPluginList; class qtractorPluginForm; class qtractorPlugin; class qtractorPluginListView; class qtractorPluginListItem; class qtractorMidiManager; class qtractorCurveList; class qtractorCurveFile; //---------------------------------------------------------------------------- // qtractorPluginFile -- Plugin file library instance. // class qtractorPluginFile { public: // Constructor. qtractorPluginFile(const QString& sFilename, bool bAutoUnload = true) : m_sFilename(sFilename), m_module(nullptr), m_bAutoUnload(bAutoUnload), m_iOpenCount(0), m_iRefCount(0) {} // Destructor. ~qtractorPluginFile() { close(); } // Helper property accessors. const QString& filename() const { return m_sFilename; } void *module() const { return m_module; } // Executive methods. bool open(); void close(); // Symbol resolver. void *resolve(const char *symbol); // Auto-unload flag accessors. void setAutoUnload(bool bAutoUnload) { m_bAutoUnload = bAutoUnload; } bool isAutoUnload() const { return m_bAutoUnload; } // Ref-counting methods. void addRef() { ++m_iRefCount; } bool removeRef() { return (--m_iRefCount < 1); } // Plugin file resgistry. typedef QHash Files; // Plugin file resgistry methods. static qtractorPluginFile *addFile(const QString& sFilename); static void removeFile(qtractorPluginFile *pFile); private: // Instance variables. QString m_sFilename; void *m_module; bool m_bAutoUnload; unsigned int m_iOpenCount; unsigned int m_iRefCount; // Global plugin-files. static Files g_files; }; //---------------------------------------------------------------------------- // qtractorPluginType -- Plugin type instance. // class qtractorPluginType { public: // Have hints for plugin paths. enum Hint { Any = 0, Ladspa, Dssi, Vst2, Vst3, Clap, Lv2, Insert, AuxSend, Control }; // Constructor. qtractorPluginType(qtractorPluginFile *pFile, unsigned long iIndex, Hint typeHint) : m_iUniqueID(0), m_iControlIns(0), m_iControlOuts(0), m_iAudioIns(0), m_iAudioOuts(0), m_iMidiIns(0), m_iMidiOuts(0), m_bRealtime(false), m_bConfigure(false), m_bEditor(false), m_pFile(pFile), m_iIndex(iIndex), m_typeHint(typeHint) {} // Destructor (virtual) virtual ~qtractorPluginType() { qtractorPluginFile::removeFile(m_pFile); } // Main properties accessors. qtractorPluginFile *file() const { return m_pFile; } unsigned long index() const { return m_iIndex; } Hint typeHint() const { return m_typeHint; } // Plugin filename accessor (default virtual). virtual QString filename() const; // Must be derived methods. virtual bool open() = 0; virtual void close() = 0; // Cache accessors. const QString& name() const { return m_sName; } const QString& label() const { return m_sLabel; } unsigned long uniqueID() const { return m_iUniqueID; } // Port count accessors.. unsigned short controlIns() const { return m_iControlIns; } unsigned short controlOuts() const { return m_iControlOuts; } unsigned short audioIns() const { return m_iAudioIns; } unsigned short audioOuts() const { return m_iAudioOuts; } unsigned short midiIns() const { return m_iMidiIns; } unsigned short midiOuts() const { return m_iMidiOuts; } // Attribute accessors. bool isRealtime() const { return m_bRealtime; } bool isConfigure() const { return m_bConfigure; } bool isEditor() const { return m_bEditor; } bool isMidi() const { return m_iMidiIns + m_iMidiOuts > 0; } // Compute the number of instances needed // for the given input/output audio channels. virtual unsigned short instances( unsigned short iChannels, bool bMidi) const; // Plugin factory method. static qtractorPlugin *createPlugin(qtractorPluginList *pList, const QString& sFilename, unsigned long iIndex, Hint typeHint); // Plugin type(hint) textual helpers. static Hint hintFromText(const QString& sText); static QString textFromHint(Hint typeHint); // Instance cached-deferred methods. virtual const QString& aboutText() { return m_sAboutText; } protected: // Cached name strings. QString m_sName; QString m_sLabel; // Cache unique identifier. unsigned long m_iUniqueID; // Cached port counts. unsigned short m_iControlIns; unsigned short m_iControlOuts; unsigned short m_iAudioIns; unsigned short m_iAudioOuts; unsigned short m_iMidiIns; unsigned short m_iMidiOuts; // Cached flags. bool m_bRealtime; bool m_bConfigure; bool m_bEditor; // Instance cached-deferred variables. QString m_sAboutText; private: // Instance variables. qtractorPluginFile *m_pFile; unsigned long m_iIndex; Hint m_typeHint; }; //---------------------------------------------------------------------------- // qtractorPlugin -- Plugin instance. // class qtractorPlugin : public qtractorList::Link { public: // Constructors. qtractorPlugin(qtractorPluginList *pList, qtractorPluginType *pType); // Destructor. virtual ~qtractorPlugin(); // DANGER: This one should be used by qtractorPluginList class only! void setPluginList(qtractorPluginList *pList) { m_pList = pList; } // Main properties accessors. qtractorPluginList *list() const { return m_pList; } qtractorPluginType *type() const { return m_pType; } unsigned short instances() const { return m_iInstances; } // Chain helper ones. unsigned short channels() const; // Unique ID methods. void setUniqueID(unsigned long iUniqueID) { m_iUniqueID = iUniqueID; } unsigned long uniqueID() const { return m_iUniqueID; } // Instance label accessors. void setLabel(const QString& sLabel) { m_sLabel = sLabel; } const QString& label() const { return m_sLabel; } // Instance alternate-alias accessors. void setAlias(const QString& sAlias) { m_sAlias = sAlias; } const QString& alias() const { return m_sAlias; } // Activation methods. void setActivated(bool bActivated); bool isActivated() const; void setActivatedEx(bool bActivated); bool isActivatedEx() const; bool isAutoActivated() const; bool isAutoDeactivated() const; // Activate subject accessors. qtractorSubject *activateSubject() { return &m_activateSubject; } qtractorMidiControlObserver *activateObserver() { return &m_activateObserver; } // Activate pseudo-parameter port index. void setActivateSubjectIndex (unsigned long iIndex) { m_iActivateSubjectIndex = iIndex; } unsigned long activateSubjectIndex() const { return m_iActivateSubjectIndex; } // An accessible list of parameters. class Param; typedef QMap Params; const Params& params() const { return m_params; } typedef QHash ParamNames; const ParamNames& paramNames() const { return m_paramNames; } // Parameters list accessors. void addParam(Param *pParam); void removeParam(Param *pParam); void clearParams(); Param *findParam(unsigned long iIndex) const { return m_params.value(iIndex, nullptr); } // Last updated parameter accessors. void setLastUpdatedParam(Param *pLastUpdatedParam) { m_pLastUpdatedParam = pLastUpdatedParam; } Param *lastUpdatedParam() const { return m_pLastUpdatedParam; } bool isLastUpdatedParam(Param *pParam) const { return (pParam == m_pLastUpdatedParam); } // Properties registry. class Property; typedef QHash Properties; const Properties& properties() const { return m_properties; } typedef QMap PropertyKeys; const PropertyKeys& propertyKeys() const { return m_propertyKeys; } // Properties registry accessors. void addProperty(Property *pProp); void removeProperty(Property *pProp); void clearProperties(); Property *findProperty(unsigned long iProperty) const { return m_properties.value(iProperty, nullptr); } // Last updated property accessors. void setLastUpdatedProperty(Property *pLastUpdatedProp) { m_pLastUpdatedProperty = pLastUpdatedProp; } Property *lastUpdatedProperty() const { return m_pLastUpdatedProperty; } bool isLastUpdatedProperty(Property *pProp) const { return (pProp == m_pLastUpdatedProperty); } // Instance capped number of audio ports. unsigned short audioIns() const { return m_pType->audioIns(); } unsigned short audioOuts() const { return m_pType->audioOuts(); } // Instance capped number of MIDI ports. unsigned short midiIns() const { return m_pType->midiIns(); } unsigned short midiOuts() const { return m_pType->midiOuts(); } // Plugin state serialization methods. void setValueList(const QStringList& vlist); QStringList valueList() const; // Reset-to-default method. void reset(); // Channel/instance number settler. virtual void setChannels(unsigned short iChannels) = 0; // Do the actual (de)activation. virtual void activate() = 0; virtual void deactivate() = 0; // The main plugin processing procedure. virtual void process( float **ppIBuffer, float **ppOBuffer, unsigned int nframes); // Parameter update method. virtual void updateParam( Param */*pParam*/, float /*fValue*/, bool /*bUpdate*/) {} // Specific MIDI instrument selector. virtual void selectProgram(int /*iBank*/, int /*iProg*/) {} // Program (patch) descriptor. struct Program { int bank; int prog; QString name; }; // Provisional program/patch accessor. virtual bool getProgram(int /*iIndex*/, Program& /*program*/) const { return false; } // Note name descriptor. struct NoteName { int bank; int prog; int note; QString name; }; // Provisional note name accessor. virtual bool getNoteName(int /*iIndex*/, NoteName& /*note*/) const { return false; } // MIDI continuous controller handler. virtual void setController(int /*iController*/, int /*iValue*/) {} // Plugin configuration handlers. virtual void configure( const QString& /*sKey*/, const QString& /*sValue*/) {} // Plugin configuration/state snapshot. virtual void freezeConfigs(); virtual void releaseConfigs(); // Plugin configure realization. virtual void realizeConfigs(); // Plugin current latency (in frames); virtual unsigned long latency() const { return 0; } // GUI Editor stuff. virtual void openEditor(QWidget */*pParent*/= nullptr) {} virtual void closeEditor() {}; virtual void idleEditor() {}; // GUI editor visibility state. virtual void setEditorVisible(bool /*bVisible*/) {} virtual bool isEditorVisible() const { return false; } // Nominal plugin user-title. virtual QString title() const; virtual void setEditorTitle(const QString& sEditorTitle) { m_sEditorTitle = sEditorTitle; } const QString& editorTitle() const { return m_sEditorTitle; } void updateEditorTitle(); void setEditorPos(const QPoint& pos) { m_posEditor = pos; } const QPoint& editorPos() const { return m_posEditor; } // Move widget to alleged parent center or else... void moveWidgetPos(QWidget *pWidget, const QPoint& pos) const; // Custom GUI editor type index preference. void setEditorType(int iEditorType) { m_iEditorType = iEditorType; } int editorType() const { return m_iEditorType; } // An accessible list of observers. const QList& items() const { return m_items; } // List of observers management. void addItem(qtractorPluginListItem *pItem); void removeItem(qtractorPluginListItem *pItem); void clearItems(); // Special plugin form methods. void openForm(QWidget *pParent = nullptr); void closeForm(bool bForce = false); bool isFormVisible() const; void toggleFormEditor(bool bOn); void updateFormDirtyCount(); void updateFormMidiControlAutoConnect(); void updateFormAuxSendBusName(); void updateFormActivated(); void refreshForm(); void freezeFormPos(); void setFormPos(const QPoint& pos) { m_posForm = pos; } const QPoint& formPos() const { return m_posForm; } // Provisional preset accessors. virtual QStringList presetList() const; virtual bool loadPreset(const QString& /*sPreset*/) { return false; } virtual bool savePreset(const QString& /*sPreset*/) { return false; } virtual bool deletePreset(const QString& /*sPreset*/) { return false; } virtual bool isReadOnlyPreset(const QString& /*sPreset*/) const { return false; } // Plugin default preset name accessor (informational) void setPreset(const QString& sPreset); const QString& preset() const; // Plugin preset group - common identification prefix. QString presetGroup() const; QString presetPrefix() const; // Default preset name (global). static const QString& defPreset() { return g_sDefPreset; } // Plugin configuration from/to xml file. virtual bool loadPresetFile(const QString& sFilename); virtual bool savePresetFile(const QString& sFilename); // Load an existing preset by name/file. bool loadPresetEx(const QString& sPreset); bool loadPresetFileEx(const QString& sFilename); // Plugin parameter lookup. Param *paramFromName(const QString& sName) const; // Plugin configuration (CLOB) stuff. typedef QHash Configs; void setConfigs(const Configs& configs) { m_configs = configs; } const Configs& configs() const { return m_configs; } void setConfig(const QString& sKey, const QString& sValue) { m_configs[sKey] = sValue; } const QString& config(const QString& sKey) { return m_configs[sKey]; } // Plugin configuration (types) stuff. typedef QHash ConfigTypes; void setConfigTypes(const ConfigTypes& ctypes) { m_ctypes = ctypes; } const ConfigTypes& configTypes() const { return m_ctypes; } void setConfigType(const QString& sKey, const QString& sType) { m_ctypes[sKey] = sType; } const QString& configType(const QString& sKey) { return m_ctypes[sKey]; } // Plugin parameter values stuff. typedef QHash ValueNames; typedef QHash ValueIndex; typedef struct { ValueNames names; ValueIndex index; } Values; void setValues(const Values& values) { m_values = values; } const Values& values() const { return m_values; } // Plugin parameter/state snapshot. void freezeValues(); void releaseValues(); // Plugin parameter/state realization. void realizeValues(); // Save partial/complete plugin state... bool savePlugin(qtractorDocument *pDocument, QDomElement *pElement); bool savePluginEx(qtractorDocument *pDocument, QDomElement *pElement); // Load plugin configuration/parameter values stuff. static void loadConfigs( QDomElement *pElement, Configs& configs, ConfigTypes& ctypes); static void loadValues(QDomElement *pElement, Values& values); // Save plugin configuration/parameter values stuff. void saveConfigs(QDomDocument *pDocument, QDomElement *pElement); void saveValues(QDomDocument *pDocument, QDomElement *pElement); // Load/save plugin parameter controllers (MIDI). static void loadControllers( QDomElement *pElement, qtractorMidiControl::Controllers& controllers); void saveControllers( qtractorDocument *pDocument, QDomElement *pElement); // Map/realize plugin parameter controllers (MIDI). void mapControllers(const qtractorMidiControl::Controllers& controllers); // Plugin automation curve serialization methods. static void loadCurveFile( QDomElement *pElement, qtractorCurveFile *pCurveFile); void saveCurveFile(qtractorDocument *pDocument, QDomElement *pElement, qtractorCurveFile *pCurveFile); void applyCurveFile (qtractorCurveFile *pCurveFile); // Direct access parameter accessors. Param *directAccessParam() const; void setDirectAccessParamIndex(long iDirectAccessParamIndex); long directAccessParamIndex() const; bool isDirectAccessParam() const; // Get all or some visual changes be announced.... void updateListViews(bool bRefresh = false); // Parameter update executive. void updateParamValue(unsigned long iIndex, float fValue, bool bUpdate); // Auto-plugin-deactivation void autoDeactivatePlugin(bool bDeactivated); bool canBeConnectedToOtherTracks() const; protected: // Instance number settler. void setInstances(unsigned short iInstances); // Internal activation methods. void setChannelsActivated(unsigned short iChannels, bool bActivated); // Activation stabilizers. void updateActivated(bool bActivated); void updateActivatedEx(bool bActivated); // Internal deactivation cleanup. void cleanup(); // Plugin configure and parameter/state clearance. void clearConfigs() { m_configs.clear(); m_ctypes.clear(); } void clearValues() { m_values.names.clear(); m_values.index.clear(); } private: // Instance variables. qtractorPluginList *m_pList; qtractorPluginType *m_pType; // Unique identifier in chain. unsigned long m_iUniqueID; // Instance label (saved). QString m_sLabel; // Instance alternate-alias (saved). QString m_sAlias; // Number of instances in chain node. unsigned short m_iInstances; // Activation flag (hard) int m_iActivated; // Activation flag. volatile bool m_bActivated; // Auto-plugin-deactivation flag bool m_bAutoDeactivated; // Activate subject value. qtractorSubject m_activateSubject; // Activate observer manager. class ActivateObserver : public qtractorMidiControlObserver { public: // Constructor. ActivateObserver(qtractorPlugin *pPlugin); protected: // Virtual observer updater. void update(bool bUpdate); private: // Instance members. qtractorPlugin *m_pPlugin; } m_activateObserver; // Activate pseudo-parameter port index. unsigned long m_iActivateSubjectIndex; // List of input control ports (parameters). Params m_params; // List of parameters (by name). ParamNames m_paramNames; // Last updated parameter. Param *m_pLastUpdatedParam; // List of properties (also parameters). Properties m_properties; // List of parameters (by name). PropertyKeys m_propertyKeys; // Last updated property. Property *m_pLastUpdatedProperty; // An accessible list of observers. QList m_items; // The plugin form reference. qtractorPluginForm *m_pForm; QString m_sPreset; QPoint m_posForm; // GUI editor stuff. QString m_sEditorTitle; QPoint m_posEditor; // Custom GUI editor type index preference. int m_iEditorType; // Plugin configuration (CLOB) stuff. Configs m_configs; // Plugin configuration (type) stuff. ConfigTypes m_ctypes; // Plugin parameter values (part of configuration). Values m_values; // Direct access parameter, if any. long m_iDirectAccessParamIndex; // Default preset name. static QString g_sDefPreset; }; //---------------------------------------------------------------------------- // qtractorPlugin::Param -- Plugin parameter (control input port) instance. // class qtractorPlugin::Param { public: // Constructor. Param(qtractorPlugin *pPlugin, unsigned long iIndex) : m_pPlugin(pPlugin), m_iIndex(iIndex), m_subject(0.0f), m_observer(this), m_iDecimals(-1) {} // Virtual destructor. virtual ~Param() {} // Main properties accessors. qtractorPlugin *plugin() const { return m_pPlugin; } unsigned long index() const { return m_iIndex; } // Parameter name accessors. void setName(const QString& sName) { m_subject.setName(sName); } const QString& name() const { return m_subject.name(); } // Parameter enablement methods. virtual void setValueEnabled(bool /*bEnabled*/) {} virtual bool isValueEnabled() const { return true; } // Parameter range hints predicate methods. virtual bool isBoundedBelow() const = 0; virtual bool isBoundedAbove() const = 0; virtual bool isDefaultValue() const = 0; virtual bool isLogarithmic() const = 0; virtual bool isSampleRate() const = 0; virtual bool isInteger() const = 0; virtual bool isToggled() const = 0; virtual bool isDisplay() const = 0; // Current display value. virtual QString display() const { return QString::number(value(), 'f', decimals()); } // Bounding range values. void setMinValue(float fMinValue) { m_subject.setMinValue(fMinValue); } float minValue() const { return m_subject.minValue(); } void setMaxValue(float fMaxValue) { m_subject.setMaxValue(fMaxValue); } float maxValue() const { return m_subject.maxValue(); } // Default value void setDefaultValue(float fDefaultValue) { m_subject.setDefaultValue(fDefaultValue); } float defaultValue() const { return m_subject.defaultValue(); } // Current parameter value. void setValue(float fValue, bool bUpdate); float value() const { return m_observer.value(); } float prevValue() const { return m_observer.prevValue(); } // Parameter update executive method. void updateValue(float fValue, bool bUpdate); // Reset-to-default method. void reset() { setValue(defaultValue(), isValueEnabled()); } // Direct parameter subject value. qtractorSubject *subject() { return &m_subject; } // Specialized observer value. qtractorMidiControlObserver *observer() { return &m_observer; } // Parameter decimals helper (cached). int decimals() const { return m_iDecimals; } protected: // Virtual observer updater. virtual void update(float fValue, bool bUpdate); private: // Instance variables. qtractorPlugin *m_pPlugin; unsigned long m_iIndex; // Port subject value. qtractorSubject m_subject; // Port observer manager. class Observer : public qtractorMidiControlObserver { public: // Constructor. Observer(Param *pParam); protected: // Virtual observer updater. void update(bool bUpdate); private: // Instance members. Param *m_pParam; } m_observer; // Decimals cache. int m_iDecimals; }; //---------------------------------------------------------------------------- // qtractorPlugin::Property -- Plugin property (aka. parameter) instance. // class qtractorPlugin::Property : public qtractorPlugin::Param { public: // Constructor. Property(qtractorPlugin *pPlugin, unsigned long iProperty) : Param(pPlugin , iProperty), m_sKey(QString::number(iProperty)) {} // Property key/id accessors. void setKey(const QString& sKey) { m_sKey = sKey; } const QString& key() const { return m_sKey; } // Property index/hash-key accessor. unsigned long key_index() const { return qHash(m_sKey); } // Property range hints predicate methods. virtual bool isString() const = 0; virtual bool isPath() const = 0; // Property special predicate methods. virtual bool isAutomatable () const { return !isString() && !isPath(); } // Main property value. void setVariant(const QVariant& value, bool bUpdate = false); const QVariant& variant() const { return m_value; } protected: // Virtual observer updater. void update(float fValue, bool bUpdate); private: // Property key id.. QString m_sKey; // Propery value. QVariant m_value; }; //---------------------------------------------------------------------------- // qtractorPluginList -- Plugin chain list instance. // class qtractorPluginList : public qtractorList { public: // Plugin-list type flags. enum Flags { // Basic flags. Audio = 0, Midi = 1, Track = 0, Bus = 2, Out = 0, In = 4, // Composite helper flags. AudioTrack = Audio | Track, AudioBus = Audio | Bus, AudioInBus = Audio | Bus | In, AudioOutBus = Audio | Bus | Out, MidiTrack = Midi | Track, MidiBus = Midi | Bus, MidiInBus = Midi | Bus | In, MidiOutBus = Midi | Bus | Out }; // Constructor. qtractorPluginList(unsigned short iChannels, unsigned int iFlags); // Destructor. ~qtractorPluginList(); // The title to show up on plugin forms... void setName(const QString& sName); const QString& name() const { return m_sName; } // Set all plugin chain number of channels. void setChannels(unsigned short iChannels, unsigned int iFlags); void setChannelsEx(unsigned short iChannels); // Reset all plugin chain number of channels. bool resetChannels(unsigned short iChannels, bool bReset = false); // Reset and (re)activate all plugin chain. void resetBuffers(); // Brainless accessors. unsigned short channels() const { return m_iChannels; } unsigned int flags() const { return m_iFlags; } // Brainless helpers. bool isMidi() const { return (m_iFlags & Midi); } bool isMidiBus() const { return (m_iFlags & MidiBus) == MidiBus; } // Specific automation curve list accessor. qtractorCurveList *curveList() const { return m_pCurveList; } // Specific MIDI manager accessor. qtractorMidiManager *midiManager() const { return m_pMidiManager; } // Special activation methods. void updateActivated(bool bActivated) { if (bActivated) ++m_iActivated; else if (m_iActivated > 0) --m_iActivated; } bool isActivatedAll() const { return (m_iActivated > 0 && m_iActivated >= (unsigned int) count()); } unsigned int isActivated() const { return (m_iActivated > 0); } // Guarded plugin methods. void addPlugin(qtractorPlugin *pPlugin); void insertPlugin(qtractorPlugin *pPlugin, qtractorPlugin *pNextPlugin); void movePlugin(qtractorPlugin *pPlugin, qtractorPlugin *pNextPlugin); void removePlugin(qtractorPlugin *pPlugin); // Clone/copy plugin method. qtractorPlugin *copyPlugin(qtractorPlugin *pPlugin); // An accessible list of views. const QList& views() const { return m_views; } // list of views management. void addView(qtractorPluginListView *pView); void removeView(qtractorPluginListView *pView); // The meta-main audio-processing plugin-chain procedure. void process(float **ppBuffer, unsigned int nframes); // Forward declarations. class Document; class WaitCursor; // Create/load plugin state. qtractorPlugin *loadPlugin(QDomElement *pElement); // Document element methods. bool loadElement(qtractorDocument *pDocument, QDomElement *pElement); bool saveElement(qtractorDocument *pDocument, QDomElement *pElement); // MIDI manager deferred load/cache specifics. void setMidiBank(int iMidiBank) { m_iMidiBank = iMidiBank; } int midiBank() const { return m_iMidiBank; } void setMidiProg(int iMidiProg) { m_iMidiProg = iMidiProg; } int midiProg() const { return m_iMidiProg; } void setAudioOutputBus(bool bAudioOutputBus) { m_bAudioOutputBus = bAudioOutputBus; } bool isAudioOutputBus() const { return m_bAudioOutputBus; } void setAudioOutputAutoConnect(bool bAudioOutputAutoConnect) { m_bAudioOutputAutoConnect = bAudioOutputAutoConnect; } bool isAudioOutputAutoConnect() const { return m_bAudioOutputAutoConnect; } void setAudioOutputBusName(const QString& sAudioOutputBusName) { m_sAudioOutputBusName = sAudioOutputBusName; } const QString& audioOutputBusName() const { return m_sAudioOutputBusName; } void setAudioOutputMonitor(bool bAudioOutputMonitor) { m_bAudioOutputMonitor = bAudioOutputMonitor; } bool isAudioOutputMonitor() const { return m_bAudioOutputMonitor; } // MIDI bank/program observable subject. class MidiProgramSubject : public qtractorSubject { public: // Constructor. MidiProgramSubject(int iBank, int iProg) : qtractorSubject(valueFromProgram(iBank, iProg)) { setMaxValue(valueFromProgram(0x3fff, 0x7f)); } // Bank/program setter. void setProgram(int iBank, int iProg) { setValue(valueFromProgram(iBank, iProg)); } // Bank/program getters. int bank() const { return bankFromValue(value()); } int prog() const { return progFromValue(value()); } protected: // Bank/program value helpers. static float valueFromProgram(int iBank, int iProg) { if (iBank < 0) iBank = 0; if (iProg < 0) iProg = 0; return float((iBank << 7) | (iProg & 0x7f)); } static int bankFromValue(float fValue) { return ((int(fValue) >> 7) & 0x3fff); } static int progFromValue(float fValue) { return (int(fValue) & 0x7f); } }; MidiProgramSubject *midiProgramSubject() const { return m_pMidiProgramSubject; } // Acquire a unique plugin identifier in chain. unsigned long createUniqueID(qtractorPluginType *pType); // Whether unique plugin identifiers are in chain. bool isUniqueID(qtractorPluginType *pType) const; // Special audio inserts activation state methods. void setAudioInsertActivated(bool bAudioInsertActivated) { if (bAudioInsertActivated) ++m_iAudioInsertActivated; else if (m_iAudioInsertActivated > 0) --m_iAudioInsertActivated; } bool isAudioInsertActivated() const { return (m_iAudioInsertActivated > 0); } // Special auto-deactivate methods void autoDeactivatePlugins(bool bDeactivated, bool bForce = false); bool isAutoDeactivated() const; // Plugin chain total latency (in frames) methods... void setLatency(bool bLatency) { m_bLatency = bLatency; } bool isLatency() const { return m_bLatency; } unsigned long latency() const { return m_iLatency; } void resetLatency() { m_iLatency = (m_bLatency ? currentLatency() : 0); } unsigned long currentLatency() const; // Plugin editors (GUI) visibility (auto-focus). void setEditorVisibleAll(bool bVisible); protected: // Check/sanitize plugin file-path. bool checkPluginFile(QString& sFilename, qtractorPluginType::Hint typeHint) const; private: // Instance variables. unsigned short m_iChannels; unsigned int m_iFlags; // Activation state. unsigned int m_iActivated; // Plugin-chain name. QString m_sName; // An accessible list of observers. QList m_views; // Specific automation curve list accessor. qtractorCurveList *m_pCurveList; // Specific MIDI manager. qtractorMidiManager *m_pMidiManager; int m_iMidiBank; int m_iMidiProg; bool m_bAudioOutputBus; bool m_bAudioOutputAutoConnect; QString m_sAudioOutputBusName; qtractorBus::ConnectList m_audioOutputs; // Audio inserts activation state. unsigned int m_iAudioInsertActivated; // Internal running buffer chain references. float **m_pppBuffers[2]; // MIDI bank/program observable subject. MidiProgramSubject *m_pMidiProgramSubject; // Plugin registry (chain unique ids.) QHash m_uniqueIDs; // Auto-plugin-deactivation. bool m_bAutoDeactivated; // Audio output monitor/meters. bool m_bAudioOutputMonitor; // Plugin chain total latency (in frames); bool m_bLatency; unsigned long m_iLatency; }; //------------------------------------------------------------------------- // qtractorPluginList::Document -- Plugins file import/export helper class. // class qtractorPluginList::Document : public qtractorDocument { public: // Constructor. Document(QDomDocument *pDocument, qtractorPluginList *pPluginList); // Default destructor. ~Document(); // Property accessors. qtractorPluginList *pluginList() const; // External storage simple methods. bool load(const QString& sFilename); bool save(const QString& sFilename); protected: // Elemental loader/savers... bool loadElement(QDomElement *pElement); bool saveElement(QDomElement *pElement); private: // Instance variables. qtractorPluginList *m_pPluginList; }; //------------------------------------------------------------------------- // qtractorPluginList::WaitCursor -- A waiting (hour-glass) helper. // class qtractorPluginList::WaitCursor { public: // Constructor. WaitCursor(); // Destructor. ~WaitCursor(); }; #endif // __qtractorPlugin_h // end of qtractorPlugin.h qtractor-1.5.9/src/PaxHeaders/qtractorLv2Plugin.h0000644000000000000000000000013215101070305016775 xustar0030 mtime=1761898693.074267613 30 atime=1761898693.074267613 30 ctime=1761898693.074267613 qtractor-1.5.9/src/qtractorLv2Plugin.h0000644000175000001440000005133415101070305016773 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.9/src/PaxHeaders/qtractorMidiFile.cpp0000644000000000000000000000013215101070305017170 xustar0030 mtime=1761898693.082267639 30 atime=1761898693.082267639 30 ctime=1761898693.082267639 qtractor-1.5.9/src/qtractorMidiFile.cpp0000644000175000001440000011410415101070305017161 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.9/src/PaxHeaders/qtractorInstrumentMenu.cpp0000644000000000000000000000013215101070305020503 xustar0030 mtime=1761898693.072267607 30 atime=1761898693.072267607 30 ctime=1761898693.072267607 qtractor-1.5.9/src/qtractorInstrumentMenu.cpp0000644000175000001440000003541215101070305020500 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.9/src/PaxHeaders/qtractorMidiControlTypeGroup.cpp0000644000000000000000000000013215101070305021610 xustar0030 mtime=1761898693.078267626 30 atime=1761898693.078267626 30 ctime=1761898693.078267626 qtractor-1.5.9/src/qtractorMidiControlTypeGroup.cpp0000644000175000001440000002765415101070305021616 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.9/src/PaxHeaders/qtractorTracks.cpp0000644000000000000000000000013015101070305016733 xustar0029 mtime=1761898693.09226767 30 atime=1761898693.091267667 29 ctime=1761898693.09226767 qtractor-1.5.9/src/qtractorTracks.cpp0000644000175000001440000027444715101070305016747 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.9/src/PaxHeaders/qtractorMmcEvent.h0000644000000000000000000000013215101070305016671 xustar0030 mtime=1761898693.085267648 30 atime=1761898693.084267645 30 ctime=1761898693.085267648 qtractor-1.5.9/src/qtractorMmcEvent.h0000644000175000001440000000745715101070305016676 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.9/src/PaxHeaders/qtractorClapPlugin.h0000644000000000000000000000013215101070305017211 xustar0030 mtime=1761898693.066267588 30 atime=1761898693.066267588 30 ctime=1761898693.066267588 qtractor-1.5.9/src/qtractorClapPlugin.h0000644000175000001440000001471615101070305017212 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.9/src/PaxHeaders/qtractorTimeScale.cpp0000644000000000000000000000013215101070305017354 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorTimeScale.cpp0000644000175000001440000006262615101070305017360 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.9/src/PaxHeaders/qtractorPaletteForm.cpp0000644000000000000000000000013215101070305017730 xustar0030 mtime=1761898693.086267651 30 atime=1761898693.085267648 30 ctime=1761898693.086267651 qtractor-1.5.9/src/qtractorPaletteForm.cpp0000644000175000001440000010157715101070305017733 0ustar00rncbcusers// qtractorPaletteForm.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 "qtractorPaletteForm.h" #include "ui_qtractorPaletteForm.h" #include "qtractorOptions.h" #include #include #include #include #include #include #include #include #include #include #include // Local static consts. static const char *ColorThemesGroup = "/ColorThemes/"; static const char *PaletteEditorGroup = "/PaletteEditor/"; static const char *DefaultDirKey = "DefaultDir"; static const char *ShowDetailsKey = "ShowDetails"; static const char *DefaultSuffix = "conf"; static struct { const char *key; QPalette::ColorRole value; } g_colorRoles[] = { { "Window", QPalette::Window }, { "WindowText", QPalette::WindowText }, { "Button", QPalette::Button }, { "ButtonText", QPalette::ButtonText }, { "Light", QPalette::Light }, { "Midlight", QPalette::Midlight }, { "Dark", QPalette::Dark }, { "Mid", QPalette::Mid }, { "Text", QPalette::Text }, { "BrightText", QPalette::BrightText }, { "Base", QPalette::Base }, { "AlternateBase", QPalette::AlternateBase }, { "Shadow", QPalette::Shadow }, { "Highlight", QPalette::Highlight }, { "HighlightedText", QPalette::HighlightedText }, { "Link", QPalette::Link }, { "LinkVisited", QPalette::LinkVisited }, { "ToolTipBase", QPalette::ToolTipBase }, { "ToolTipText", QPalette::ToolTipText }, #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0) { "PlaceholderText", QPalette::PlaceholderText }, #endif { "NoRole", QPalette::NoRole }, { nullptr, QPalette::NoRole } }; //------------------------------------------------------------------------- // qtractorPaletteForm qtractorPaletteForm::qtractorPaletteForm ( QWidget *parent, const QPalette& pal ) : QDialog(parent), p_ui(new Ui::qtractorPaletteForm), m_ui(*p_ui) { m_ui.setupUi(this); m_settings = nullptr; m_owner = false; m_modelUpdated = false; m_paletteUpdated = false; m_dirtyCount = 0; m_dirtyTotal = 0; updateGenerateButton(); m_paletteModel = new PaletteModel(this); m_ui.paletteView->setModel(m_paletteModel); ColorDelegate *delegate = new ColorDelegate(this); m_ui.paletteView->setItemDelegate(delegate); m_ui.paletteView->setEditTriggers(QAbstractItemView::AllEditTriggers); // m_ui.paletteView->setAlternatingRowColors(true); m_ui.paletteView->setSelectionBehavior(QAbstractItemView::SelectRows); m_ui.paletteView->setDragEnabled(true); m_ui.paletteView->setDropIndicatorShown(true); m_ui.paletteView->setRootIsDecorated(false); m_ui.paletteView->setColumnHidden(2, true); m_ui.paletteView->setColumnHidden(3, true); QObject::connect(m_ui.nameCombo, SIGNAL(editTextChanged(const QString&)), SLOT(nameComboChanged(const QString&))); QObject::connect(m_ui.saveButton, SIGNAL(clicked()), SLOT(saveButtonClicked())); QObject::connect(m_ui.deleteButton, SIGNAL(clicked()), SLOT(deleteButtonClicked())); QObject::connect(m_ui.generateButton, SIGNAL(changed()), SLOT(generateButtonChanged())); QObject::connect(m_ui.resetButton, SIGNAL(clicked()), SLOT(resetButtonClicked())); QObject::connect(m_ui.detailsCheck, SIGNAL(clicked()), SLOT(detailsCheckClicked())); QObject::connect(m_ui.importButton, SIGNAL(clicked()), SLOT(importButtonClicked())); QObject::connect(m_ui.exportButton, SIGNAL(clicked()), SLOT(exportButtonClicked())); QObject::connect(m_paletteModel, SIGNAL(paletteChanged(const QPalette&)), SLOT(paletteChanged(const QPalette&))); QObject::connect(m_ui.dialogButtons, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.dialogButtons, SIGNAL(rejected()), SLOT(reject())); setPalette(pal, pal); QDialog::adjustSize(); } qtractorPaletteForm::~qtractorPaletteForm (void) { setSettings(nullptr); } void qtractorPaletteForm::setPalette ( const QPalette& pal ) { m_palette = pal; #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const uint mask = pal.resolveMask(); #else const uint mask = pal.resolve(); #endif for (int i = 0; g_colorRoles[i].key; ++i) { if ((mask & (1 << i)) == 0) { const QPalette::ColorRole cr = g_colorRoles[i].value; m_palette.setBrush(QPalette::Active, cr, m_parentPalette.brush(QPalette::Active, cr)); m_palette.setBrush(QPalette::Inactive, cr, m_parentPalette.brush(QPalette::Inactive, cr)); m_palette.setBrush(QPalette::Disabled, cr, m_parentPalette.brush(QPalette::Disabled, cr)); } } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) m_palette.setResolveMask(mask); #else m_palette.resolve(mask); #endif updateGenerateButton(); m_paletteUpdated = true; if (!m_modelUpdated) m_paletteModel->setPalette(m_palette, m_parentPalette); m_paletteUpdated = false; } void qtractorPaletteForm::setPalette ( const QPalette& pal, const QPalette& parentPal ) { m_parentPalette = parentPal; setPalette(pal); } const QPalette& qtractorPaletteForm::palette (void) const { return m_palette; } void qtractorPaletteForm::setSettings ( QSettings *settings, bool owner ) { if (m_settings && m_owner) delete m_settings; m_settings = settings; m_owner = owner; m_ui.detailsCheck->setChecked(isShowDetails()); updateNamedPaletteList(); updateDialogButtons(); } QSettings *qtractorPaletteForm::settings (void) const { return m_settings; } void qtractorPaletteForm::nameComboChanged ( const QString& name ) { if (m_dirtyCount > 0 && m_ui.nameCombo->findText(name) < 0) { updateDialogButtons(); } else { resetButtonClicked(); setPaletteName(name); ++m_dirtyTotal; } } void qtractorPaletteForm::saveButtonClicked (void) { const QString& name = m_ui.nameCombo->currentText(); if (name.isEmpty()) return; QString filename = namedPaletteConf(name); if (filename.isEmpty() || !QFileInfo(filename).isWritable()) { const QString& title = tr("Save Palette - %1").arg(QDialog::windowTitle()); QStringList filters; filters.append(tr("Palette files (*.%1)").arg(DefaultSuffix)); filters.append(tr("All files (*.*)")); QString dirname = defaultDir(); if (!dirname.isEmpty()) dirname.append(QDir::separator()); dirname.append(paletteName() + '.' + DefaultSuffix); filename = QFileDialog::getSaveFileName(this, title, dirname, filters.join(";;")); } if (!filename.isEmpty() && saveNamedPaletteConf(name, filename, m_palette)) { addNamedPaletteConf(name, filename); setPalette(m_palette, m_palette); updateNamedPaletteList(); resetButtonClicked(); } } void qtractorPaletteForm::deleteButtonClicked (void) { const QString& name = m_ui.nameCombo->currentText(); if (m_ui.nameCombo->findText(name) >= 0) { deleteNamedPaletteConf(name); updateNamedPaletteList(); updateDialogButtons(); } } void qtractorPaletteForm::generateButtonChanged (void) { const QColor& color = m_ui.generateButton->brush().color(); const QPalette& pal = QPalette(color); setPalette(pal); ++m_dirtyCount; updateDialogButtons(); } void qtractorPaletteForm::resetButtonClicked (void) { const bool blocked = blockSignals(true); for (int i = 0; g_colorRoles[i].key; ++i) { const QPalette::ColorRole cr = g_colorRoles[i].value; const QModelIndex& index = m_paletteModel->index(cr, 0); m_paletteModel->setData(index, false, Qt::EditRole); } m_dirtyCount = 0; updateDialogButtons(); blockSignals(blocked); } void qtractorPaletteForm::detailsCheckClicked (void) { const int cw = (m_ui.paletteView->viewport()->width() >> 2); QHeaderView *header = m_ui.paletteView->header(); header->resizeSection(0, cw); if (m_ui.detailsCheck->isChecked()) { m_ui.paletteView->setColumnHidden(2, false); m_ui.paletteView->setColumnHidden(3, false); header->resizeSection(1, cw); header->resizeSection(2, cw); header->resizeSection(3, cw); m_paletteModel->setGenerate(false); } else { m_ui.paletteView->setColumnHidden(2, true); m_ui.paletteView->setColumnHidden(3, true); header->resizeSection(1, cw * 3); m_paletteModel->setGenerate(true); } } void qtractorPaletteForm::importButtonClicked (void) { const QString& title = tr("Import File - %1").arg(QDialog::windowTitle()); QStringList filters; filters.append(tr("Palette files (*.%1)").arg(DefaultSuffix)); filters.append(tr("All files (*.*)")); const QString& filename = QFileDialog::getOpenFileName(this, title, defaultDir(), filters.join(";;")); if (filename.isEmpty()) return; QSettings conf(filename, QSettings::IniFormat); conf.beginGroup(ColorThemesGroup); const QStringList names = conf.childGroups(); conf.endGroup(); int imported = 0; QStringListIterator name_iter(names); while (name_iter.hasNext()) { const QString& name = name_iter.next(); if (!name.isEmpty()) { addNamedPaletteConf(name, filename); setPaletteName(name); ++imported; } } if (imported > 0) { updateNamedPaletteList(); resetButtonClicked(); setDefaultDir(QFileInfo(filename).absolutePath()); } else { QMessageBox::warning(this, tr("Warning - %1").arg(QDialog::windowTitle()), tr("Could not import from file:\n\n" "%1\n\nSorry.").arg(filename)); } } void qtractorPaletteForm::exportButtonClicked (void) { const QString& title = tr("Export File - %1").arg(QDialog::windowTitle()); QStringList filters; filters.append(tr("Palette files (*.%1)").arg(DefaultSuffix)); filters.append(tr("All files (*.*)")); QString dirname = defaultDir(); if (!dirname.isEmpty()) dirname.append(QDir::separator()); dirname.append(paletteName() + '.' + DefaultSuffix); const QString& filename = QFileDialog::getSaveFileName(this, title, dirname, filters.join(";;")); if (filename.isEmpty()) return; const QFileInfo fi(filename); const QString& name = fi.baseName(); if (saveNamedPaletteConf(name, filename, m_palette)) { // addNamedPaletteConf(name, filename); setDefaultDir(fi.absolutePath()); } } void qtractorPaletteForm::paletteChanged ( const QPalette& pal ) { m_modelUpdated = true; if (!m_paletteUpdated) setPalette(pal); m_modelUpdated = false; ++m_dirtyCount; updateDialogButtons(); } void qtractorPaletteForm::setPaletteName ( const QString& name ) { const bool blocked = m_ui.nameCombo->blockSignals(true); m_ui.nameCombo->setEditText(name); QPalette pal; if (namedPalette(m_settings, name, pal, true)) setPalette(pal, pal); m_dirtyCount = 0; updateDialogButtons(); m_ui.nameCombo->blockSignals(blocked); } QString qtractorPaletteForm::paletteName (void) const { return m_ui.nameCombo->currentText(); } void qtractorPaletteForm::updateNamedPaletteList (void) { const bool blocked = m_ui.nameCombo->blockSignals(true); const QString old_name = m_ui.nameCombo->currentText(); m_ui.nameCombo->clear(); m_ui.nameCombo->insertItems(0, namedPaletteList()); // m_ui.nameCombo->model()->sort(0); const int i = m_ui.nameCombo->findText(old_name); if (i >= 0) m_ui.nameCombo->setCurrentIndex(i); else m_ui.nameCombo->setEditText(old_name); m_ui.nameCombo->blockSignals(blocked); } void qtractorPaletteForm::updateGenerateButton (void) { m_ui.generateButton->setBrush( m_palette.brush(QPalette::Active, QPalette::Button)); } void qtractorPaletteForm::updateDialogButtons (void) { const QString& name = m_ui.nameCombo->currentText(); const QString& filename = namedPaletteConf(name); const int i = m_ui.nameCombo->findText(name); m_ui.saveButton->setEnabled(!name.isEmpty() && (m_dirtyCount > 0 || i < 0)); m_ui.deleteButton->setEnabled(i >= 0); m_ui.resetButton->setEnabled(m_dirtyCount > 0); m_ui.exportButton->setEnabled(!name.isEmpty() || i >= 0); m_ui.dialogButtons->button(QDialogButtonBox::Ok)->setEnabled(i >= 0); } bool qtractorPaletteForm::namedPalette ( const QString& name, QPalette& pal ) const { return namedPalette(m_settings, name, pal); } bool qtractorPaletteForm::namedPalette ( QSettings *settings, const QString& name, QPalette& pal, bool fixup ) { int result = 0; if (!name.isEmpty() && loadNamedPalette(settings, name, pal)) { ++result; } else { const QString& filename = namedPaletteConf(settings, name); if (!filename.isEmpty() && QFileInfo(filename).isReadable() && loadNamedPaletteConf(name, filename, pal)) { ++result; } } // Dark themes grayed/disabled color group fix... if (!fixup && pal.base().color().value() < 0x7f) { const QColor& color = pal.window().color(); const int groups = int(QPalette::Active | QPalette::Inactive) + 1; for (int i = 0; i < groups; ++i) { const QPalette::ColorGroup cg = QPalette::ColorGroup(i); pal.setBrush(cg, QPalette::Light, color.lighter(140)); pal.setBrush(cg, QPalette::Midlight, color.lighter(100)); pal.setBrush(cg, QPalette::Mid, color.lighter(90)); pal.setBrush(cg, QPalette::Dark, color.darker(160)); pal.setBrush(cg, QPalette::Shadow, color.darker(180)); } pal.setColorGroup(QPalette::Disabled, pal.windowText().color().darker(), pal.button(), pal.light(), pal.dark(), pal.mid(), pal.text().color().darker(), pal.text().color().lighter(), pal.base(), pal.window()); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) pal.setColor(QPalette::Disabled, QPalette::Highlight, pal.mid().color()); pal.setColor(QPalette::Disabled, QPalette::ButtonText, pal.mid().color()); #endif ++result; } return (result > 0); } QStringList qtractorPaletteForm::namedPaletteList (void) const { return namedPaletteList(m_settings); } QStringList qtractorPaletteForm::namedPaletteList ( QSettings *settings ) { QStringList list; if (settings) { settings->beginGroup(ColorThemesGroup); list.append(settings->childKeys()); list.append(settings->childGroups()); // legacy... settings->endGroup(); } return list; } QString qtractorPaletteForm::namedPaletteConf ( const QString& name ) const { return namedPaletteConf(m_settings, name); } QString qtractorPaletteForm::namedPaletteConf ( QSettings *settings, const QString& name ) { QString ret; if (settings && !name.isEmpty()) { settings->beginGroup(ColorThemesGroup); ret = settings->value(name).toString(); settings->endGroup(); } return ret; } void qtractorPaletteForm::addNamedPaletteConf ( const QString& name, const QString& filename ) { addNamedPaletteConf(m_settings, name, filename); ++m_dirtyTotal; } void qtractorPaletteForm::addNamedPaletteConf ( QSettings *settings, const QString& name, const QString& filename ) { if (settings) { settings->beginGroup(ColorThemesGroup); settings->remove(name); // remove legacy keys! settings->setValue(name, filename); settings->endGroup(); } } void qtractorPaletteForm::deleteNamedPaletteConf ( const QString& name ) { if (m_settings) { m_settings->beginGroup(ColorThemesGroup); m_settings->remove(name); m_settings->endGroup(); ++m_dirtyTotal; } } bool qtractorPaletteForm::loadNamedPaletteConf ( const QString& name, const QString& filename, QPalette& pal ) { QSettings conf(filename, QSettings::IniFormat); return loadNamedPalette(&conf, name, pal); } bool qtractorPaletteForm::saveNamedPaletteConf ( const QString& name, const QString& filename, const QPalette& pal ) { QSettings conf(filename, QSettings::IniFormat); return saveNamedPalette(&conf, name, pal); } bool qtractorPaletteForm::loadNamedPalette ( QSettings *settings, const QString& name, QPalette& pal ) { if (settings == nullptr) return false; int result = 0; settings->beginGroup(ColorThemesGroup); QStringListIterator name_iter(settings->childGroups()); while (name_iter.hasNext() && !result) { const QString& name2 = name_iter.next(); if (name2 == name) { #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) uint mask = pal.resolve(); #endif settings->beginGroup(name + '/'); QStringListIterator iter(settings->childKeys()); while (iter.hasNext()) { const QString& key = iter.next(); const QPalette::ColorRole cr = qtractorPaletteForm::colorRole(key); const QStringList& clist = settings->value(key).toStringList(); if (clist.count() == 3) { pal.setColor(QPalette::Active, cr, QColor(clist.at(0))); pal.setColor(QPalette::Inactive, cr, QColor(clist.at(1))); pal.setColor(QPalette::Disabled, cr, QColor(clist.at(2))); #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) mask &= ~(1 << int(cr)); #endif ++result; } } #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) pal.resolve(mask); #endif settings->endGroup(); } } settings->endGroup(); return (result > 0); } bool qtractorPaletteForm::saveNamedPalette ( QSettings *settings, const QString& name, const QPalette& pal ) { if (settings == nullptr) return false; settings->beginGroup(ColorThemesGroup); settings->beginGroup(name + '/'); for (int i = 0; g_colorRoles[i].key; ++i) { const QString& key = QString::fromLatin1(g_colorRoles[i].key); const QPalette::ColorRole cr = g_colorRoles[i].value; QStringList clist; clist.append(pal.color(QPalette::Active, cr).name()); clist.append(pal.color(QPalette::Inactive, cr).name()); clist.append(pal.color(QPalette::Disabled, cr).name()); settings->setValue(key, clist); } settings->endGroup(); settings->endGroup(); return true; } QPalette::ColorRole qtractorPaletteForm::colorRole ( const QString& name ) { static QHash s_colorRoles; if (s_colorRoles.isEmpty()) { for (int i = 0; g_colorRoles[i].key; ++i) { const QString& key = QString::fromLatin1(g_colorRoles[i].key); const QPalette::ColorRole value = g_colorRoles[i].value; s_colorRoles.insert(key, value); } } return s_colorRoles.value(name, QPalette::NoRole); } bool qtractorPaletteForm::isDirty (void) const { return (m_dirtyTotal > 0); } void qtractorPaletteForm::accept (void) { setShowDetails(m_ui.detailsCheck->isChecked()); if (m_dirtyCount > 0) saveButtonClicked(); QDialog::accept(); } void qtractorPaletteForm::reject (void) { if (m_dirtyCount > 0) { const QString& name = paletteName(); if (name.isEmpty()) { if (QMessageBox::warning(this, tr("Warning - %1").arg(QDialog::windowTitle()), tr("Some settings have been changed.\n\n" "Do you want to discard the changes?"), QMessageBox::Discard | QMessageBox::Cancel) == QMessageBox::Cancel) return; } else { switch (QMessageBox::warning(this, tr("Warning - %1").arg(QDialog::windowTitle()), tr("Some settings have been changed:\n\n" "\"%1\".\n\nDo you want to save the changes?") .arg(name), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel)) { case QMessageBox::Save: saveButtonClicked(); // Fall thru... case QMessageBox::Discard: break; default: // Cancel... return; } } } QDialog::reject(); } void qtractorPaletteForm::setDefaultDir ( const QString& dir ) { if (m_settings) { m_settings->beginGroup(PaletteEditorGroup); m_settings->setValue(DefaultDirKey, dir); m_settings->endGroup(); } } QString qtractorPaletteForm::defaultDir (void) const { QString dir; if (m_settings) { m_settings->beginGroup(PaletteEditorGroup); dir = m_settings->value(DefaultDirKey).toString(); m_settings->endGroup(); } return dir; } void qtractorPaletteForm::setShowDetails ( bool on ) { if (m_settings) { m_settings->beginGroup(PaletteEditorGroup); m_settings->setValue(ShowDetailsKey, on); m_settings->endGroup(); } } bool qtractorPaletteForm::isShowDetails (void) const { bool on = false; if (m_settings) { m_settings->beginGroup(PaletteEditorGroup); on = m_settings->value(ShowDetailsKey).toBool(); m_settings->endGroup(); } return on; } void qtractorPaletteForm::showEvent ( QShowEvent *event ) { QDialog::showEvent(event); detailsCheckClicked(); } void qtractorPaletteForm::resizeEvent ( QResizeEvent *event ) { QDialog::resizeEvent(event); detailsCheckClicked(); } //------------------------------------------------------------------------- // qtractorPaletteForm::PaletteModel qtractorPaletteForm::PaletteModel::PaletteModel ( QObject *parent ) : QAbstractTableModel(parent) { for (m_nrows = 0; g_colorRoles[m_nrows].key; ++m_nrows) { const QPalette::ColorRole value = g_colorRoles[m_nrows].value; const QString& key = QString::fromLatin1(g_colorRoles[m_nrows].key); m_roleNames.insert(value, key); } m_generate = true; } int qtractorPaletteForm::PaletteModel::rowCount ( const QModelIndex& ) const { return m_nrows; } int qtractorPaletteForm::PaletteModel::columnCount ( const QModelIndex& ) const { return 4; } QVariant qtractorPaletteForm::PaletteModel::data ( const QModelIndex& index, int role ) const { if (!index.isValid()) return QVariant(); if (index.row() < 0 || index.row() >= m_nrows) return QVariant(); if (index.column() < 0 || index.column() >= 4) return QVariant(); if (index.column() == 0) { if (role == Qt::DisplayRole) return m_roleNames.value(QPalette::ColorRole(index.row())); if (role == Qt::EditRole) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) const uint mask = m_palette.resolveMask(); #else const uint mask = m_palette.resolve(); #endif return bool(mask & (1 << index.row())); } } else if (role == Qt::BackgroundRole) { return m_palette.color( columnToGroup(index.column()), QPalette::ColorRole(index.row())); } return QVariant(); } bool qtractorPaletteForm::PaletteModel::setData ( const QModelIndex& index, const QVariant& value, int role ) { if (!index.isValid()) return false; if (index.column() != 0 && role == Qt::BackgroundRole) { const QColor& color = value.value(); const QPalette::ColorRole cr = QPalette::ColorRole(index.row()); const QPalette::ColorGroup cg = columnToGroup(index.column()); m_palette.setBrush(cg, cr, color); QModelIndex index_begin = PaletteModel::index(cr, 0); QModelIndex index_end = PaletteModel::index(cr, 3); if (m_generate) { m_palette.setBrush(QPalette::Inactive, cr, color); switch (cr) { case QPalette::WindowText: case QPalette::Text: case QPalette::ButtonText: case QPalette::Base: break; case QPalette::Dark: m_palette.setBrush(QPalette::Disabled, QPalette::WindowText, color); m_palette.setBrush(QPalette::Disabled, QPalette::Dark, color); m_palette.setBrush(QPalette::Disabled, QPalette::Text, color); m_palette.setBrush(QPalette::Disabled, QPalette::ButtonText, color); index_begin = PaletteModel::index(0, 0); index_end = PaletteModel::index(m_nrows - 1, 3); break; case QPalette::Window: m_palette.setBrush(QPalette::Disabled, QPalette::Base, color); m_palette.setBrush(QPalette::Disabled, QPalette::Window, color); index_begin = PaletteModel::index(QPalette::Base, 0); break; case QPalette::Highlight: m_palette.setBrush(QPalette::Disabled, QPalette::Highlight, color.darker(120)); break; default: m_palette.setBrush(QPalette::Disabled, cr, color); break; } } emit paletteChanged(m_palette); emit dataChanged(index_begin, index_end); return true; } if (index.column() == 0 && role == Qt::EditRole) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) uint mask = m_palette.resolveMask(); #else uint mask = m_palette.resolve(); #endif const bool masked = value.value(); const int i = index.row(); if (masked) { mask |= (1 << i); } else { const QPalette::ColorRole cr = QPalette::ColorRole(i); m_palette.setBrush(QPalette::Active, cr, m_parentPalette.brush(QPalette::Active, cr)); m_palette.setBrush(QPalette::Inactive, cr, m_parentPalette.brush(QPalette::Inactive, cr)); m_palette.setBrush(QPalette::Disabled, cr, m_parentPalette.brush(QPalette::Disabled, cr)); mask &= ~(1 << i); } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) m_palette.setResolveMask(mask); #else m_palette.resolve(mask); #endif emit paletteChanged(m_palette); const QModelIndex& index_end = PaletteModel::index(i, 3); emit dataChanged(index, index_end); return true; } return false; } Qt::ItemFlags qtractorPaletteForm::PaletteModel::flags ( const QModelIndex& index ) const { if (!index.isValid()) return Qt::ItemIsEnabled; else return Qt::ItemIsEditable | Qt::ItemIsEnabled; } QVariant qtractorPaletteForm::PaletteModel::headerData ( int section, Qt::Orientation orientation, int role ) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { if (section == 0) return tr("Color Role"); else if (section == groupToColumn(QPalette::Active)) return tr("Active"); else if (section == groupToColumn(QPalette::Inactive)) return tr("Inactive"); else if (section == groupToColumn(QPalette::Disabled)) return tr("Disabled"); } return QVariant(); } const QPalette& qtractorPaletteForm::PaletteModel::palette(void) const { return m_palette; } void qtractorPaletteForm::PaletteModel::setPalette ( const QPalette& palette, const QPalette& parentPalette ) { m_palette = palette; m_parentPalette = parentPalette; const QModelIndex& index_begin = index(0, 0); const QModelIndex& index_end = index(m_nrows - 1, 3); emit dataChanged(index_begin, index_end); } QPalette::ColorGroup qtractorPaletteForm::PaletteModel::columnToGroup ( int index ) const { if (index == 1) return QPalette::Active; else if (index == 2) return QPalette::Inactive; return QPalette::Disabled; } int qtractorPaletteForm::PaletteModel::groupToColumn ( QPalette::ColorGroup group ) const { if (group == QPalette::Active) return 1; else if (group == QPalette::Inactive) return 2; return 3; } //------------------------------------------------------------------------- // qtractorPaletteForm::ColorDelegate QWidget *qtractorPaletteForm::ColorDelegate::createEditor ( QWidget *parent, const QStyleOptionViewItem&, const QModelIndex& index ) const { QWidget *editor = nullptr; if (index.column() == 0) { RoleEditor *ed = new RoleEditor(parent); QObject::connect(ed, SIGNAL(changed(QWidget *)), SIGNAL(commitData(QWidget *))); // ed->setFocusPolicy(Qt::NoFocus); // ed->installEventFilter(const_cast(this)); editor = ed; } else { ColorEditor *ed = new ColorEditor(parent); QObject::connect(ed, SIGNAL(changed(QWidget *)), SIGNAL(commitData(QWidget *))); ed->setFocusPolicy(Qt::NoFocus); ed->installEventFilter(const_cast(this)); editor = ed; } return editor; } void qtractorPaletteForm::ColorDelegate::setEditorData ( QWidget *editor, const QModelIndex& index ) const { if (index.column() == 0) { const bool masked = index.model()->data(index, Qt::EditRole).value(); RoleEditor *ed = static_cast(editor); ed->setEdited(masked); const QString& colorName = index.model()->data(index, Qt::DisplayRole).value(); ed->setLabel(colorName); } else { const QColor& color = index.model()->data(index, Qt::BackgroundRole).value(); ColorEditor *ed = static_cast(editor); ed->setColor(color); } } void qtractorPaletteForm::ColorDelegate::setModelData ( QWidget *editor, QAbstractItemModel *model, const QModelIndex& index ) const { if (index.column() == 0) { RoleEditor *ed = static_cast(editor); const bool masked = ed->edited(); model->setData(index, masked, Qt::EditRole); } else { ColorEditor *ed = static_cast(editor); if (ed->changed()) { const QColor& color = ed->color(); model->setData(index, color, Qt::BackgroundRole); } } } void qtractorPaletteForm::ColorDelegate::updateEditorGeometry ( QWidget *editor, const QStyleOptionViewItem& option, const QModelIndex& index ) const { QItemDelegate::updateEditorGeometry(editor, option, index); editor->setGeometry(editor->geometry().adjusted(0, 0, -1, -1)); } void qtractorPaletteForm::ColorDelegate::paint ( QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const { QStyleOptionViewItem opt = option; const bool masked = index.model()->data(index, Qt::EditRole).value(); if (index.column() == 0 && masked) opt.font.setBold(true); QItemDelegate::paint(painter, opt, index); // painter->setPen(opt.palette.midlight().color()); painter->setPen(Qt::darkGray); painter->drawLine(opt.rect.right(), opt.rect.y(), opt.rect.right(), opt.rect.bottom()); painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom()); } QSize qtractorPaletteForm::ColorDelegate::sizeHint ( const QStyleOptionViewItem& option, const QModelIndex &index) const { return QItemDelegate::sizeHint(option, index) + QSize(4, 4); } //------------------------------------------------------------------------- // qtractorPaletteForm::ColorButton qtractorPaletteForm::ColorButton::ColorButton ( QWidget *parent ) : QPushButton(parent), m_brush(Qt::darkGray) { QPushButton::setMinimumWidth(48); QObject::connect(this, SIGNAL(clicked()), SLOT(chooseColor())); } const QBrush& qtractorPaletteForm::ColorButton::brush (void) const { return m_brush; } void qtractorPaletteForm::ColorButton::setBrush ( const QBrush& brush ) { m_brush = brush; update(); } void qtractorPaletteForm::ColorButton::paintEvent ( QPaintEvent *event ) { QPushButton::paintEvent(event); QStyleOptionButton opt; opt.initFrom(this); const QRect& rect = style()->subElementRect(QStyle::SE_PushButtonContents, &opt, this); QPainter paint(this); paint.setBrush(QBrush(m_brush.color())); paint.drawRect(rect.adjusted(+1, +1, -2, -2)); } void qtractorPaletteForm::ColorButton::chooseColor (void) { QWidget *pParentWidget = nullptr; QColorDialog::ColorDialogOptions options; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bDontUseNativeDialogs) { options |= QColorDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } const QColor& color = QColorDialog::getColor( m_brush.color(), pParentWidget, QString(), options); if (color.isValid()) { m_brush.setColor(color); emit changed(); } } //------------------------------------------------------------------------- // qtractorPaletteForm::ColorEditor qtractorPaletteForm::ColorEditor::ColorEditor ( QWidget *parent ) : QWidget(parent) { QLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); m_button = new qtractorPaletteForm::ColorButton(this); layout->addWidget(m_button); QObject::connect(m_button, SIGNAL(changed()), SLOT(colorChanged())); setFocusProxy(m_button); m_changed = false; } void qtractorPaletteForm::ColorEditor::setColor ( const QColor& color ) { m_button->setBrush(color); m_changed = false; } QColor qtractorPaletteForm::ColorEditor::color (void) const { return m_button->brush().color(); } void qtractorPaletteForm::ColorEditor::colorChanged (void) { m_changed = true; emit changed(this); } bool qtractorPaletteForm::ColorEditor::changed (void) const { return m_changed; } //------------------------------------------------------------------------- // qtractorPaletteForm::RoleEditor qtractorPaletteForm::RoleEditor::RoleEditor ( QWidget *parent ) : QWidget(parent) { m_edited = false; QHBoxLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); m_label = new QLabel(this); layout->addWidget(m_label); m_label->setAutoFillBackground(true); m_label->setIndent(3); // HACK: it should have the same value of textMargin in QItemDelegate setFocusProxy(m_label); m_button = new QToolButton(this); m_button->setToolButtonStyle(Qt::ToolButtonIconOnly); m_button->setIcon(QIcon::fromTheme("itemReset")); m_button->setIconSize(QSize(8, 8)); m_button->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::MinimumExpanding)); layout->addWidget(m_button); QObject::connect(m_button, SIGNAL(clicked()), SLOT(resetProperty())); } void qtractorPaletteForm::RoleEditor::setLabel ( const QString& label ) { m_label->setText(label); } void qtractorPaletteForm::RoleEditor::setEdited ( bool on ) { QFont font; if (on) font.setBold(on); m_label->setFont(font); m_button->setEnabled(on); m_edited = on; } bool qtractorPaletteForm::RoleEditor::edited (void) const { return m_edited; } void qtractorPaletteForm::RoleEditor::resetProperty (void) { setEdited(false); emit changed(this); } // end of qtractorPaletteForm.cpp qtractor-1.5.9/src/PaxHeaders/qtractorAudioPeak.cpp0000644000000000000000000000013215101070305017350 xustar0030 mtime=1761898693.064267582 30 atime=1761898693.064267582 30 ctime=1761898693.064267582 qtractor-1.5.9/src/qtractorAudioPeak.cpp0000644000175000001440000006702315101070305017350 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.9/src/PaxHeaders/qtractorMidiEditTime.cpp0000644000000000000000000000013215101070305020015 xustar0030 mtime=1761898693.079267629 30 atime=1761898693.079267629 30 ctime=1761898693.079267629 qtractor-1.5.9/src/qtractorMidiEditTime.cpp0000644000175000001440000006570315101070305020020 0ustar00rncbcusers// qtractorMidiEditTime.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 "qtractorMidiEditTime.h" #include "qtractorMidiEditor.h" #include "qtractorMidiEditView.h" #include "qtractorOptions.h" #include "qtractorSessionCommand.h" #include "qtractorTimeScaleCommand.h" #include "qtractorMainForm.h" #include "qtractorTimeScaleForm.h" #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) #define horizontalAdvance width #endif //---------------------------------------------------------------------------- // qtractorMidiEditTime -- MIDI sequence time scale widget. // Constructor. qtractorMidiEditTime::qtractorMidiEditTime ( qtractorMidiEditor *pEditor, QWidget *pParent ) : qtractorScrollView(pParent) { m_pEditor = pEditor; m_dragState = DragNone; m_dragCursor = DragNone; m_pDragMarker = nullptr; qtractorScrollView::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); qtractorScrollView::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); qtractorScrollView::setFrameStyle(QFrame::Panel | QFrame::Raised); 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() - 2)); // QObject::connect(this, SIGNAL(contentsMoving(int,int)), // this, SLOT(updatePixmap(int,int))); // Trap for help/tool-tips events. qtractorScrollView::viewport()->installEventFilter(this); } // (Re)create the complete view pixmap. void qtractorMidiEditTime::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 QColor& rgbText = pal.windowText().color(); const QColor& rgbLine = pal.mid().color(); const QColor& rgbLight = pal.light().color(); m_pixmap = QPixmap(w, h); m_pixmap.fill(pal.window().color()); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); if (pTimeScale == nullptr) return; QPainter painter(&m_pixmap); // painter.initFrom(this); painter.setFont(qtractorScrollView::font()); // // Draw the time scale... // const QFontMetrics& fm = painter.fontMetrics(); int x, x1, y1, y2 = h - 1; // Account for the editing offset: const int dx = cx + pTimeScale->pixelFromFrame(m_pEditor->offset()); qtractorTimeScale::Cursor cursor(pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekPixel(dx); #if 0 unsigned short iPixelsPerBeat = pNode->pixelsPerBeat(); unsigned int iBeat = pNode->beatFromPixel(dx); if (iBeat > 0) pNode = cursor.seekBeat(--iBeat); x = x1 = pNode->pixelFromBeat(iBeat) - dx; while (x < w) { const bool bBeatIsBar = pNode->beatIsBar(iBeat); if (bBeatIsBar || iPixelsPerBeat > 8) { y1 = (bBeatIsBar && x >= x1 ? 0 : fm.ascent()); painter.setPen(rgbLine); painter.drawLine(x, y1, x, y2); painter.setPen(rgbLight); ++x; painter.drawLine(x, y1, x, y2); } if (bBeatIsBar) { y1 = fm.ascent(); if (x >= x1) { x1 = x + 2; const unsigned short iBar = pNode->barFromBeat(iBeat); const QString& sBeat = QString::number(iBar + 1); painter.setPen(rgbText); painter.drawText(x1, y1, sBeat); x1 += fm.horizontalAdvance(sBeat) + 2; } x1 += 2; if (iBeat == pNode->beat) { iPixelsPerBeat = pNode->pixelsPerBeat(); const QString& sTempo = QString("%1 %2/%3") .arg(pNode->tempo) .arg(pNode->beatsPerBar) .arg(1 << pNode->beatDivisor); painter.setPen(Qt::darkGray); painter.drawText(x1, y1, sTempo); x1 += fm.horizontalAdvance(sTempo) + 2; } } pNode = cursor.seekBeat(++iBeat); x = pNode->pixelFromBeat(iBeat) - dx; } #else unsigned short iBar = pNode->barFromPixel(dx); if (iBar > 0) pNode = cursor.seekBar(--iBar); x = x1 = pNode->pixelFromBar(iBar) - dx; while (x < w) { // Next bar... pNode = cursor.seekPixel(x + dx); const int x2 = pNode->pixelFromBar(++iBar) - dx; // Bar label... if (x >= x1) { const QString& sBar = QString::number(iBar); x1 = x; y1 = fm.ascent(); painter.setPen(rgbText); painter.drawText(x1 + 2, y1, sBar); x1 += fm.horizontalAdvance(sBar) + 2; } x1 += 2; // Tempo/time-sig. label... if (iBar == pNode->bar + 1) { const QString& sTempo = QString("%1 %2/%3") .arg(pNode->tempo) .arg(pNode->beatsPerBar) .arg(1 << pNode->beatDivisor); y1 = fm.ascent(); painter.setPen(Qt::darkGray); painter.drawText(x1 + 2, y1, sTempo); x1 += fm.horizontalAdvance(sTempo) + 2; } // Beat lines... const unsigned short iBeatsPerBar2 = pNode->beatsPerBar2(); const float q2 = float(x2 - x) / float(iBeatsPerBar2); if (q2 > 8.0f) { float p2 = float(x - 1); for (int i = 1; i < iBeatsPerBar2; ++i) { x = int(p2 += q2); if (x > w) break; if (x > x1) { y1 = fm.ascent(); painter.setPen(rgbLine); painter.drawLine(x, y1, x, y2); painter.setPen(rgbLight); ++x; painter.drawLine(x, y1, x, y2); } } } // Bar line... if (x2 > x1) { y1 = 0; painter.setPen(rgbLine); painter.drawLine(x2 - 1, y1, x2 - 1, y2); painter.setPen(rgbLight); painter.drawLine(x2, y1, x2, y2); } // Move forward... x = x2; } #endif // Draw location markers, if any... qtractorTimeScale::Marker *pMarker = pTimeScale->markers().seekPixel(dx); while (pMarker) { x = pTimeScale->pixelFromFrame(pMarker->frame) - dx + 4; if (x > w) break; if (qtractorTimeScale::isKeySignature( pMarker->accidentals, pMarker->mode)) { const QString& sKeySignature = qtractorTimeScale::keySignatureName( pMarker->accidentals, pMarker->mode); painter.setPen(Qt::darkGray); painter.drawText(x, y2, sKeySignature); x += fm.horizontalAdvance(sKeySignature) + 4; } if (!pMarker->text.isEmpty()) { painter.setPen(pMarker->color); painter.drawText(x, y2, pMarker->text); } pMarker = pMarker->next(); } // Draw loop boundaries, if applicable... if (pSession->isLooping()) { QPolygon polyg(3); // h -= 4; const int d = (h >> 2); QRect rect(0, h - d, w, h - d); QColor color = Qt::cyan; painter.setPen(color.darker()); painter.setBrush(color); x = pTimeScale->pixelFromFrame(pSession->loopStart()) - dx; if (x >= 0 && x < w) { polyg.putPoints(0, 3, x + d, h - d, x, h, x, h - d); painter.drawPolygon(polyg); } rect.setLeft(x); x = pTimeScale->pixelFromFrame(pSession->loopEnd()) - dx; if (x >= 0 && x < w) { polyg.putPoints(0, 3, x, h - d, x, h, x - d, h - d); painter.drawPolygon(polyg); } if (rect.x() < w && x >= 0) { rect.setRight(x); color.setAlpha(120); painter.fillRect(rect, color); } } // Draw punch in/out boundaries, if applicable... if (pSession->isPunching()) { QPolygon polyg(3); // h -= 4; const int d = (h >> 2); QRect rect(0, h - d, w, h - d); QColor color = Qt::magenta; painter.setPen(color.darker()); painter.setBrush(color); x = pTimeScale->pixelFromFrame(pSession->punchIn()) - dx; if (x >= 0 && x < w) { polyg.putPoints(0, 3, x + d, h - d, x, h, x, h - d); painter.drawPolygon(polyg); } rect.setLeft(x); x = pTimeScale->pixelFromFrame(pSession->punchOut()) - dx; if (x >= 0 && x < w) { polyg.putPoints(0, 3, x, h - d, x, h, x - d, h - d); painter.drawPolygon(polyg); } if (rect.x() < w && x >= 0) { rect.setRight(x); color.setAlpha(120); painter.fillRect(rect, color); } } } // Rectangular contents update. void qtractorMidiEditTime::updateContents ( const QRect& rect ) { updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(rect); } // Overall contents update. void qtractorMidiEditTime::updateContents (void) { updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(); } // Resize event handler. void qtractorMidiEditTime::resizeEvent ( QResizeEvent *pResizeEvent ) { qtractorScrollView::resizeEvent(pResizeEvent); updateContents(); } // Draw the time scale. void qtractorMidiEditTime::drawContents ( QPainter *pPainter, const QRect& rect ) { pPainter->drawPixmap(rect, m_pixmap, rect); // Draw special play/edit-head/tail headers... const int cx = qtractorScrollView::contentsX(); const int h = qtractorScrollView::height() - 4; const int d = (h >> 2); int x = m_pEditor->editHeadX() - cx; if (x >= rect.left() - d && x <= rect.right() + d) { QPolygon polyg(3); polyg.putPoints(0, 3, x, h - d, x, h, x + d, h - d); pPainter->setPen(Qt::blue); pPainter->setBrush(Qt::blue); pPainter->drawPolygon(polyg); } x = m_pEditor->editTailX() - cx; if (x >= rect.left() - d && x <= rect.right() + d) { QPolygon polyg(3); polyg.putPoints(0, 3, x - d, h - d, x, h, x, h - d); pPainter->setPen(Qt::blue); pPainter->setBrush(Qt::blue); pPainter->drawPolygon(polyg); } x = m_pEditor->playHeadX() - cx; if (x >= rect.left() - d && x <= rect.right() + d) { QPolygon polyg(3); polyg.putPoints(0, 3, x - d, h - d, x, h, x + d, h - d); pPainter->setPen(Qt::red); pPainter->setBrush(Qt::red); pPainter->drawPolygon(polyg); } } // To have timeline in h-sync with main view. void qtractorMidiEditTime::contentsXMovingSlot ( int cx, int /*cy*/ ) { if (qtractorScrollView::contentsX() != cx) qtractorScrollView::setContentsPos(cx, qtractorScrollView::contentsY()); } // Check if some position header is to be dragged... bool qtractorMidiEditTime::dragHeadStart ( const QPoint& pos ) { // Try to catch mouse clicks over the // play/edit-head/tail cursors... const int h = qtractorScrollView::height(); // - 4; const int d = (h >> 1); QRect rect(0, h - d, d << 1, d); // Check play-head header... rect.moveLeft(m_pEditor->playHeadX() - d); if (rect.contains(pos)) { m_dragCursor = DragPlayHead; return true; } // Check loop and punch points... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); if (pTimeScale == nullptr) return false; const int dx = pTimeScale->pixelFromFrame(m_pEditor->offset()) + d; // Loop points... if (pSession->isLooping()) { // Check loop-start header... rect.moveLeft(pTimeScale->pixelFromFrame(pSession->loopStart()) - dx); if (rect.contains(pos)) { m_dragCursor = DragLoopStart; return true; } // Check loop-end header... rect.moveLeft(pTimeScale->pixelFromFrame(pSession->loopEnd()) - dx); if (rect.contains(pos)) { m_dragCursor = DragLoopEnd; return true; } } // Punch in/out points... if (pSession->isPunching()) { // Check punch-in header... rect.moveLeft(pTimeScale->pixelFromFrame(pSession->punchIn()) - dx); if (rect.contains(pos)) { m_dragCursor = DragPunchIn; return true; } // Check punch-out header... rect.moveLeft(pTimeScale->pixelFromFrame(pSession->punchOut()) - dx); if (rect.contains(pos)) { m_dragCursor = DragPunchOut; return true; } } // Check edit-head header... rect.moveLeft(m_pEditor->editHeadX() - d); if (rect.contains(pos)) { m_dragCursor = DragEditHead; return true; } // Check edit-tail header... rect.moveLeft(m_pEditor->editTailX() - d); if (rect.contains(pos)) { m_dragCursor = DragEditTail; return true; } // Check location marker headers... qtractorTimeScale::Marker *pMarker = pTimeScale->markers().seekPixel(pos.x() + dx); if (pMarker) { unsigned long iFrame = pMarker->frame; rect.moveLeft(pTimeScale->pixelFromFrame(iFrame) - dx); if (rect.contains(pos)) { m_dragCursor = DragMarker; m_pDragMarker = pSession->timeScale()->markers().seekFrame(iFrame); return true; } } // Reset cursor if any persist around. if (m_dragCursor != DragNone) { qtractorScrollView::unsetCursor(); m_dragCursor = DragNone; } // Nothing. return false; } // Handle item selection/dragging -- mouse button press. void qtractorMidiEditTime::mousePressEvent ( QMouseEvent *pMouseEvent ) { // Force null state. m_dragState = DragNone; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // We'll need options somehow... qtractorOptions *pOptions = qtractorOptions::getInstance(); // Which mouse state? 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()); qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); unsigned long iFrame = m_pEditor->frameSnap(m_pEditor->offset() + pTimeScale->frameFromPixel(pos.x() > 0 ? pos.x() : 0)); switch (pMouseEvent->button()) { case Qt::LeftButton: // Remember what and where we'll be dragging/selecting... m_dragState = DragStart; m_posDrag = pos; // Try to catch mouse clicks over the cursor heads... if (dragHeadStart(m_posDrag)) { qtractorScrollView::setCursor(QCursor(Qt::SizeHorCursor)); // m_dragState = m_dragCursor; }/* else if (!bModifier) { // Edit-head positioning... m_pEditor->setEditHead(iFrame); // Logical contents changed, just for visual feedback... m_pEditor->selectionChangeNotify(); }*/ break; case Qt::MiddleButton: if (pOptions && pOptions->bMidButtonModifier) bModifier = !bModifier; // Reverse mid-button role... if (bModifier) { // Play-head positioning... m_pEditor->setPlayHead(iFrame); pSession->setPlayHead(iFrame); } else { // Edit-head/tail (merged) positioning... m_pEditor->setEditHead(iFrame); m_pEditor->setEditTail(iFrame); } // Logical contents changed, just for visual feedback... m_pEditor->selectionChangeNotify(); break; case Qt::RightButton: if (pOptions && pOptions->bShiftKeyModifier) bModifier = !bModifier; if (!bModifier) { // Edit-tail positioning... m_pEditor->setEditTail(iFrame); // Logical contents changed, just for visual feedback... m_pEditor->selectionChangeNotify(); } // Fall thru... default: break; } // qtractorScrollView::mousePressEvent(pMouseEvent); } // Handle item selection/dragging -- mouse pointer move. void qtractorMidiEditTime::mouseMoveEvent ( QMouseEvent *pMouseEvent ) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; // Are we already moving/dragging something? const QPoint& pos = viewportToContents(pMouseEvent->pos()); qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); const unsigned long iFrame = m_pEditor->frameSnap(m_pEditor->offset() + pTimeScale->frameFromPixel(pos.x() > 0 ? pos.x() : 0)); const int y = m_pEditor->editView()->contentsY(); switch (m_dragState) { case DragNone: // Try to catch mouse over the cursor heads... if (dragHeadStart(pos)) qtractorScrollView::setCursor(QCursor(Qt::PointingHandCursor)); break; case DragSelect: // Rubber-band selection... m_rectDrag.setRight( m_pEditor->pixelSnap(pos.x() > 0 ? pos.x() : 0)); m_pEditor->editView()->ensureVisible(pos.x(), y, 16, 0); m_pEditor->selectRect(m_pEditor->editView(), m_rectDrag, pMouseEvent->modifiers() & Qt::ControlModifier, false); // Edit-head/tail positioning... selectEdit(iFrame); showToolTip(m_rectDrag.normalized()); break; case DragPlayHead: // Play-head positioning... m_pEditor->editView()->ensureVisible(pos.x(), y, 16, 0); m_pEditor->setPlayHead(iFrame); // Let the change get some immediate visual feedback... pMainForm->updateTransportTime(iFrame); showToolTip(iFrame); break; case DragEditHead: case DragPunchIn: case DragLoopStart: // Edit-head positioning... m_pEditor->editView()->ensureVisible(pos.x(), y, 16, 0); m_pEditor->setEditHead(iFrame); showToolTip(iFrame); break; case DragEditTail: case DragPunchOut: case DragLoopEnd: // Edit-head positioning... m_pEditor->editView()->ensureVisible(pos.x(), y, 16, 0); m_pEditor->setEditTail(iFrame); showToolTip(iFrame); break; case DragMarker: // Marker positioning... m_pEditor->editView()->ensureVisible(pos.x(), y, 16, 0); showToolTip(iFrame); break; case DragStart: // Rubber-band starting... if ((m_posDrag - pos).manhattanLength() > QApplication::startDragDistance()) { // We'll start dragging alright... const int h = m_pEditor->editView()->contentsHeight(); m_rectDrag.setTop(0); m_rectDrag.setLeft( m_pEditor->pixelSnap(m_posDrag.x() > 0 ? m_posDrag.x() : 0)); m_rectDrag.setRight( m_pEditor->pixelSnap(pos.x() > 0 ? pos.x() : 0)); m_rectDrag.setBottom(h); if (!dragHeadStart(m_posDrag)) m_dragCursor = DragSelect; m_dragState = m_dragCursor; qtractorScrollView::setCursor(QCursor(Qt::SizeHorCursor)); } // Fall thru... default: break; } // qtractorScrollView::mouseMoveEvent(pMouseEvent); } // Handle item selection/dragging -- mouse button release. void qtractorMidiEditTime::mouseReleaseEvent ( QMouseEvent *pMouseEvent ) { // qtractorScrollView::mouseReleaseEvent(pMouseEvent); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Which mouse state? bool bModifier = (pMouseEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)); // We'll need options somehow... qtractorOptions *pOptions = qtractorOptions::getInstance(); // Direct snap positioning... const QPoint& pos = viewportToContents(pMouseEvent->pos()); qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); const unsigned long iFrame = m_pEditor->frameSnap(m_pEditor->offset() + pTimeScale->frameFromPixel(pos.x() > 0 ? pos.x() : 0)); switch (m_dragState) { case DragSelect: { // Do the final range selection... m_pEditor->selectRect(m_pEditor->editView(), m_rectDrag, pMouseEvent->modifiers() & Qt::ControlModifier, true); // Edit-head/tail positioning... selectEdit(iFrame); // Not quite a selection change, // but for visual feedback... m_pEditor->selectionChangeNotify(); break; } case DragPlayHead: // Play-head positioning commit... m_pEditor->setPlayHead(iFrame); pSession->setPlayHead(iFrame); // Fall thru... case DragEditHead: case DragEditTail: // Not quite a selection change, // but for visual feedback... m_pEditor->selectionChangeNotify(); break; case DragLoopStart: // New loop-start boundary... if (pSession->editHead() < pSession->loopEnd()) { // Yep, new loop-start point... m_pEditor->execute( new qtractorSessionLoopCommand(pSession, pSession->editHead(), pSession->loopEnd())); } break; case DragLoopEnd: // New loop-end boundary... if (pSession->loopStart() < pSession->editTail()) { // Yep, new loop-end point... m_pEditor->execute( new qtractorSessionLoopCommand(pSession, pSession->loopStart(), pSession->editTail())); } break; case DragPunchIn: // New punch-in boundary... if (pSession->editHead() < pSession->punchOut()) { // Yep, new punch-in point... m_pEditor->execute( new qtractorSessionPunchCommand(pSession, pSession->editHead(), pSession->punchOut())); } break; case DragPunchOut: // New punch-out boundary... if (pSession->punchIn() < pSession->editTail()) { // Yep, new punch-out point... m_pEditor->execute( new qtractorSessionPunchCommand(pSession, pSession->punchIn(), pSession->editTail())); } break; case DragMarker: // Marker positioning commit... if (m_pDragMarker) { // Yep, new marker location... pSession->execute( new qtractorTimeScaleMoveMarkerCommand( pSession->timeScale(), m_pDragMarker, iFrame)); } break; case DragStart: // Left-button indirect positioning... if (pOptions && pOptions->bShiftKeyModifier) bModifier = !bModifier; if (bModifier) { // Play-head positioning... m_pEditor->setPlayHead(iFrame); // Immediately commited... pSession->setPlayHead(iFrame); } else { // Deferred left-button edit-head positioning... m_pEditor->setEditHead(iFrame); } // Not quite a selection, rather just // for immediate visual feedback... m_pEditor->selectionChangeNotify(); // Fall thru... case DragNone: default: break; } // Clean up. resetDragState(); } // Tempo-map dialog accessor. void qtractorMidiEditTime::mouseDoubleClickEvent ( QMouseEvent *pMouseEvent ) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; // Direct snap positioning... const QPoint& pos = viewportToContents(pMouseEvent->pos()); qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); const unsigned long iFrame = m_pEditor->frameSnap(m_pEditor->offset() + pTimeScale->frameFromPixel(pos.x() > 0 ? pos.x() : 0)); // Show tempo map dialog. qtractorTimeScaleForm form(pMainForm); form.setFrame(iFrame); form.exec(); } // Keyboard event handler. void qtractorMidiEditTime::keyPressEvent ( QKeyEvent *pKeyEvent ) { if (pKeyEvent->key() == Qt::Key_Escape) { // Restore uncommitted play-head position?... if (m_dragState == DragPlayHead) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) m_pEditor->setPlayHead(pSession->playHead()); } resetDragState(); } if (!m_pEditor->keyPress(this, pKeyEvent->key(), pKeyEvent->modifiers())) qtractorScrollView::keyPressEvent(pKeyEvent); } // Handle zoom with mouse wheel. void qtractorMidiEditTime::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); } // Reset drag/select state. void qtractorMidiEditTime::resetDragState (void) { // Cancel any dragging/cursor out there... if (m_dragState == DragSelect) qtractorScrollView::updateContents(); if (m_dragCursor != DragNone) qtractorScrollView::unsetCursor(); // Also get rid of any meta-breadcrumbs... m_pEditor->resetDragState(this); // Force null state. m_dragState = DragNone; m_dragCursor = DragNone; m_pDragMarker = nullptr; // HACK: give focus to track-view... m_pEditor->editView()->setFocus(); } // Context menu event handler (dummy). void qtractorMidiEditTime::contextMenuEvent ( QContextMenuEvent */*pContextMenuEvent*/ ) { } // Trap for help/tool-tip events. bool qtractorMidiEditTime::eventFilter ( QObject *pObject, QEvent *pEvent ) { QWidget *pViewport = qtractorScrollView::viewport(); if (static_cast (pObject) == pViewport) { if (pEvent->type() == QEvent::ToolTip && m_dragCursor != DragNone && m_pEditor->isToolTips()) { QHelpEvent *pHelpEvent = static_cast (pEvent); if (pHelpEvent) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { unsigned long iFrame = 0; switch (m_dragCursor) { case DragMarker: if (m_pDragMarker) iFrame = m_pDragMarker->frame; break; case DragPlayHead: iFrame = pSession->playHead(); break; case DragEditHead: iFrame = pSession->editHead(); break; case DragEditTail: iFrame = pSession->editTail(); break; case DragLoopStart: iFrame = pSession->loopStart(); break; case DragLoopEnd: iFrame = pSession->loopEnd(); break; case DragPunchIn: iFrame = pSession->punchIn(); break; case DragPunchOut: iFrame = pSession->punchOut(); break; default: break; } showToolTip(iFrame); } } } else if (pEvent->type() == QEvent::Leave && m_dragState != DragNone) { qtractorScrollView::unsetCursor(); return true; } } // Not handled here. return qtractorScrollView::eventFilter(pObject, pEvent); } // Show dragging tooltip... void qtractorMidiEditTime::showToolTip ( unsigned long iFrame ) const { if (!m_pEditor->isToolTips()) return; qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); if (pTimeScale == nullptr) return; QString sToolTip; switch (m_dragCursor) { case DragMarker: if (m_pDragMarker) sToolTip += m_pDragMarker->text; break; case DragPlayHead: sToolTip += tr("Play-head"); break; case DragEditHead: sToolTip += tr("Edit-head"); break; case DragEditTail: sToolTip += tr("Edit-tail"); break; case DragLoopStart: sToolTip += tr("Loop-start"); break; case DragLoopEnd: sToolTip += tr("Loop-end"); break; case DragPunchIn: sToolTip += tr("Punch-in"); break; case DragPunchOut: sToolTip += tr("Punch-out"); break; default: break; } if (!sToolTip.isEmpty()) sToolTip += '\n'; sToolTip += pTimeScale->textFromFrame(iFrame); QToolTip::showText(QCursor::pos(), sToolTip, qtractorScrollView::viewport()); } void qtractorMidiEditTime::showToolTip ( const QRect& rect ) const { if (!m_pEditor->isToolTips()) return; qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); if (pTimeScale == nullptr) return; const unsigned long iFrameStart = m_pEditor->frameSnap( pTimeScale->frameFromPixel(qMax(0, rect.left()))); const unsigned long iFrameEnd = m_pEditor->frameSnap( pTimeScale->frameFromPixel(qMax(0, rect.right()))); 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()); } // Edit-head/tail positioning... void qtractorMidiEditTime::selectEdit ( unsigned long iFrame ) { qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); if (pTimeScale == nullptr) return; const unsigned long iFrame2 = m_pEditor->frameSnap(m_pEditor->offset() + pTimeScale->frameFromPixel(m_rectDrag.left())); if (iFrame < iFrame2) { m_pEditor->setEditHead(iFrame); m_pEditor->setEditTail(iFrame2); } else { m_pEditor->setEditHead(iFrame2); m_pEditor->setEditTail(iFrame); } } // end of qtractorMidiEditTime.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMidiListView.h0000644000000000000000000000013215101070305017524 xustar0030 mtime=1761898693.082267639 30 atime=1761898693.082267639 30 ctime=1761898693.082267639 qtractor-1.5.9/src/qtractorMidiListView.h0000644000175000001440000000527515101070305017525 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.9/src/PaxHeaders/qtractorExportForm.ui0000644000000000000000000000013215101070305017446 xustar0030 mtime=1761898693.071267604 30 atime=1761898693.071267604 30 ctime=1761898693.071267604 qtractor-1.5.9/src/qtractorExportForm.ui0000644000175000001440000004331015101070305017437 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.9/src/PaxHeaders/qtractorPluginForm.cpp0000644000000000000000000000013215101070305017570 xustar0030 mtime=1761898693.087267654 30 atime=1761898693.086267651 30 ctime=1761898693.087267654 qtractor-1.5.9/src/qtractorPluginForm.cpp0000644000175000001440000015360015101070305017565 0ustar00rncbcusers// qtractorPluginForm.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 "qtractorPluginForm.h" #include "qtractorPluginCommand.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include "qtractorPluginListView.h" #include "qtractorInsertPlugin.h" #include "qtractorMidiControlPlugin.h" #include "qtractorObserverWidget.h" #include "qtractorMidiControlObserverForm.h" #include "qtractorMidiControlPluginWidget.h" #include "qtractorAudioIOMatrixForm.h" #include "qtractorSpinBox.h" #include "qtractorBusForm.h" #include "qtractorOptions.h" #include "qtractorSession.h" #include "qtractorEngine.h" #include "qtractorCurve.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include //---------------------------------------------------------------------------- // qtractorPluginForm -- UI wrapper form. // int qtractorPluginForm::g_iIconsRefCount = 0; QIcon *qtractorPluginForm::g_pIcons[2] = { nullptr, nullptr }; // Constructor. qtractorPluginForm::qtractorPluginForm ( QWidget *pParent, Qt::WindowFlags wflags ) : QWidget(pParent, wflags) { // Setup (de)activated icon stuff... if (++g_iIconsRefCount == 1) { const QPixmap& pmLedOff = QIcon::fromTheme("itemLedOff").pixmap(8, 8); const QPixmap& pmLedOn = QIcon::fromTheme("itemLedOn").pixmap(8, 8); const QPixmap& pmLedDim = QIcon::fromTheme("itemLedDim").pixmap(8, 8); QIcon *pIconActivated = new QIcon(); pIconActivated->addPixmap(pmLedOff, QIcon::Active, QIcon::Off); pIconActivated->addPixmap(pmLedOn, QIcon::Active, QIcon::On); QIcon *pIconDeactivated = new QIcon(); pIconDeactivated->addPixmap(pmLedOff, QIcon::Active, QIcon::Off); pIconDeactivated->addPixmap(pmLedDim, QIcon::Active, QIcon::On); g_pIcons[0] = pIconActivated; g_pIcons[1] = pIconDeactivated; } // Setup UI struct... m_ui.setupUi(this); QWidget::setAttribute(Qt::WA_QuitOnClose, false); const QFont& font = QWidget::font(); QWidget::setFont(QFont(font.family(), font.pointSize() - 1)); m_pPlugin = nullptr; m_iDirtyCount = 0; m_iUpdate = 0; m_pMidiControlPluginWidget = nullptr; m_pDirectAccessParamMenu = new QMenu(); m_ui.DirectAccessParamPushButton->setMenu(m_pDirectAccessParamMenu); m_ui.PresetComboBox->setValidator( new QRegularExpressionValidator( QRegularExpression("[\\w-]+"), m_ui.PresetComboBox)); m_ui.PresetComboBox->setInsertPolicy(QComboBox::NoInsert); m_ui.PresetComboBox->setCompleter(nullptr); #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) // Some conveniency cleaner helper... m_ui.AliasLineEdit->setClearButtonEnabled(true); #endif // UI signal/slot connections... QObject::connect(m_ui.PresetComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(changePresetSlot(const QString&))); QObject::connect(m_ui.PresetComboBox, SIGNAL(activated(int)), SLOT(loadPresetSlot(int))); QObject::connect(m_ui.OpenPresetToolButton, SIGNAL(clicked()), SLOT(openPresetSlot())); QObject::connect(m_ui.SavePresetToolButton, SIGNAL(clicked()), SLOT(savePresetSlot())); QObject::connect(m_ui.DeletePresetToolButton, SIGNAL(clicked()), SLOT(deletePresetSlot())); QObject::connect(m_ui.AliasLineEdit, SIGNAL(editingFinished()), SLOT(aliasSlot())); QObject::connect(m_ui.EditToolButton, SIGNAL(toggled(bool)), SLOT(editSlot(bool))); QObject::connect(m_ui.ActivateToolButton, SIGNAL(toggled(bool)), SLOT(activateSlot(bool))); QObject::connect(m_ui.TabWidget, SIGNAL(currentChanged(int)), SLOT(currentChangedSlot(int))); QObject::connect(m_ui.SendsToolButton, SIGNAL(clicked()), SLOT(sendsSlot())); QObject::connect(m_ui.ReturnsToolButton, SIGNAL(clicked()), SLOT(returnsSlot())); QObject::connect(m_ui.AutoConnectCheckBox, SIGNAL(toggled(bool)), SLOT(autoConnectSlot(bool))); QObject::connect(m_ui.AuxSendBusNameComboBox, SIGNAL(activated(int)), SLOT(changeAuxSendBusNameSlot(int))); QObject::connect(m_ui.AuxSendBusNameToolButton, SIGNAL(clicked()), SLOT(clickAuxSendBusNameSlot())); QObject::connect(m_ui.AuxSendIOMatrixToolButton, SIGNAL(clicked()), SLOT(clickAuxSendIOMatrixSlot())); QObject::connect(m_pDirectAccessParamMenu, SIGNAL(aboutToShow()), SLOT(updateDirectAccessParamSlot())); } // Destructor. qtractorPluginForm::~qtractorPluginForm (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl && m_pPlugin) { pMidiControl->unmapMidiObserverWidget( m_pPlugin->activateObserver(), m_ui.ActivateToolButton); } clear(); delete m_pDirectAccessParamMenu; if (--g_iIconsRefCount == 0) { for (int i = 0; i < 2; ++i) { delete g_pIcons[i]; g_pIcons[i] = nullptr; } } } // Plugin accessors. void qtractorPluginForm::setPlugin ( qtractorPlugin *pPlugin ) { clear(); // Set the new reference... m_pPlugin = pPlugin; if (m_pPlugin == nullptr) return; qtractorPluginType *pType = m_pPlugin->type(); if (pType == nullptr) return; // Dispatch any pending updates. qtractorSubject::flushQueue(true); // Set activate button MIDI controller observer... addMidiControlAction( m_ui.ActivateToolButton, m_pPlugin->activateObserver()); const qtractorPluginType::Hint typeHint = pType->typeHint(); const bool bTwoColumnPage = (typeHint == qtractorPluginType::Vst2 || typeHint == qtractorPluginType::Vst3 || typeHint == qtractorPluginType::Clap); const int MaxRowsPerPage = (bTwoColumnPage ? 12 : 8); const int MaxColumnsPerPage = (bTwoColumnPage ? 2 : 3); const int MaxItemsPerPage = MaxRowsPerPage * MaxColumnsPerPage; const qtractorPlugin::Params& params = m_pPlugin->params(); const qtractorPlugin::Properties& properties = m_pPlugin->properties(); int iItems = params.count() + properties.count(); int iItemsPerPage = iItems; int iItemsOnLastPage = 0; if (iItemsPerPage > MaxItemsPerPage) { iItemsPerPage = MaxItemsPerPage; iItemsOnLastPage = (iItems % iItemsPerPage); while (iItemsOnLastPage > 0 && iItemsOnLastPage < ((3 * iItemsPerPage) >> 2)) iItemsOnLastPage = (iItems % --iItemsPerPage); } int iPages = 1; int iRowsPerPage = iItemsPerPage; int iColumnsPerPage = 1; if (iRowsPerPage > MaxRowsPerPage) { iPages = (iItems / iItemsPerPage); if (iItemsOnLastPage > 0) ++iPages; while (iRowsPerPage > MaxRowsPerPage && iColumnsPerPage < MaxColumnsPerPage) iRowsPerPage = (iItemsPerPage / ++iColumnsPerPage); if (iItemsPerPage % iColumnsPerPage) // Adjust to balance. ++iRowsPerPage; } // Maybe we need a tabbed widget... QTabWidget *pTabWidget = nullptr; QGridLayout *pGridLayout = nullptr; QWidget *pPageWidget = nullptr; int iPage = 0; const QString sPage = tr("Page %1"); if (iItems > 0) { pTabWidget = m_ui.TabWidget; pGridLayout = new QGridLayout(); pGridLayout->setContentsMargins(8, 8, 8, 8); pGridLayout->setSpacing(4); pPageWidget = new QWidget(); pPageWidget->setLayout(pGridLayout); pTabWidget->insertTab(iPage, pPageWidget, sPage.arg(iPage + 1)); ++iPage; } const qtractorPlugin::PropertyKeys& props = m_pPlugin->propertyKeys(); qtractorPlugin::PropertyKeys::ConstIterator prop = props.constBegin(); const qtractorPlugin::PropertyKeys::ConstIterator& prop_end = props.constEnd(); for ( ; prop != prop_end; ++prop) { qtractorPlugin::Property *pProp = prop.value(); qtractorPluginParamWidget *pPropWidget = new qtractorPluginParamWidget(pProp, this); if (pProp->isAutomatable()) { qtractorMidiControlObserver *pMidiObserver = pProp->observer(); if (pMidiObserver) addMidiControlAction(pPropWidget, pMidiObserver); } m_paramWidgets.append(pPropWidget); } qtractorPlugin::Params::ConstIterator param = params.constBegin(); const qtractorPlugin::Params::ConstIterator param_end = params.constEnd(); for ( ; param != param_end; ++param) { qtractorPlugin::Param *pParam = param.value(); qtractorPluginParamWidget *pParamWidget = new qtractorPluginParamWidget(pParam, this); qtractorMidiControlObserver *pMidiObserver = pParam->observer(); if (pMidiObserver) addMidiControlAction(pParamWidget, pMidiObserver); m_paramWidgets.append(pParamWidget); } // FIXME: Couldn't stand more than a hundred widgets? // or do we have one dedicated editor GUI? int iRow = 0; int iColumn = 0; qtractorMidiControlPlugin *pMidiControlPlugin = nullptr; if (typeHint == qtractorPluginType::Control && pType->index() == 0) pMidiControlPlugin = static_cast (m_pPlugin); if (pMidiControlPlugin) { m_pMidiControlPluginWidget = new qtractorMidiControlPluginWidget(this); m_pMidiControlPluginWidget->setMidiControlPlugin(pMidiControlPlugin); QObject::connect(m_pMidiControlPluginWidget, SIGNAL(bipolarChanged()), SLOT(updateParamRangeSlot())); pGridLayout->addWidget(m_pMidiControlPluginWidget, iRow, iColumn); m_ui.AutoConnectCheckBox->setChecked( pMidiControlPlugin->isControlAutoConnect()); ++iRow; } iColumnsPerPage += (iColumnsPerPage - 1); // Plus gap columns! if (!m_paramWidgets.isEmpty()) pGridLayout->setColumnStretch(iColumn, 1); QListIterator iter(m_paramWidgets); while (iter.hasNext()) { pGridLayout->addWidget(iter.next(), iRow, iColumn); if (++iRow >= iRowsPerPage) { pGridLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), iRow, 0, 1, iColumnsPerPage); iRow = 0; if (++iColumn >= iColumnsPerPage) { iColumn = 0; if (pTabWidget && iPage < iPages) { pGridLayout = new QGridLayout(); pGridLayout->setContentsMargins(8, 8, 8, 8); pGridLayout->setSpacing(4); pPageWidget = new QWidget(); pPageWidget->setLayout(pGridLayout); pTabWidget->insertTab(iPage, pPageWidget, sPage.arg(iPage + 1)); ++iPage; } } else pGridLayout->setColumnMinimumWidth(iColumn++, 8); // Gap column! pGridLayout->setColumnStretch(iColumn, 1); } } // Show editor button if available? const bool bEditor = pType->isEditor(); m_ui.EditToolButton->setVisible(bEditor); if (bEditor) toggleEditor(m_pPlugin->isEditorVisible()); // Show insert tool options... const bool bInsertPlugin = (typeHint == qtractorPluginType::Insert); const bool bMidiControlPlugin = (pMidiControlPlugin != nullptr); if (bInsertPlugin || bMidiControlPlugin) { if (pType->index() > 0) { // index == channels > 0 => Audio insert. m_ui.SendsToolButton->setIcon(QIcon::fromTheme("itemAudioPortOut")); m_ui.ReturnsToolButton->setIcon(QIcon::fromTheme("itemAudioPortIn")); } else { m_ui.SendsToolButton->setIcon(QIcon::fromTheme("itemMidiPortOut")); m_ui.ReturnsToolButton->setIcon(QIcon::fromTheme("itemMidiPortIn")); } } m_ui.SendsToolButton->setVisible(bInsertPlugin || bMidiControlPlugin); m_ui.ReturnsToolButton->setVisible(bInsertPlugin); m_ui.AutoConnectCheckBox->setVisible(bMidiControlPlugin); // Show aux-send tool options... const bool bAuxSendPlugin = (typeHint == qtractorPluginType::AuxSend); const bool bAudioAuxSendPlugin = (bAuxSendPlugin && pType->index() > 0); m_ui.AuxSendBusNameComboBox->setVisible(bAuxSendPlugin); m_ui.AuxSendBusNameLabel->setVisible(bAuxSendPlugin); m_ui.AuxSendBusNameToolButton->setVisible(bAuxSendPlugin); m_ui.AuxSendIOMatrixToolButton->setVisible(bAudioAuxSendPlugin); // Set initial plugin preset name... setPreset(m_pPlugin->preset()); // Set plugin name/caption as title, // maybe redundant but necessary... m_pPlugin->updateEditorTitle(); // About page... m_ui.NameTextLabel->setText(pType->name()); m_ui.TypeHintTextLabel->setText( qtractorPluginType::textFromHint(typeHint)); QString sAboutText = pType->aboutText(); sAboutText += '\n'; sAboutText += '\n'; sAboutText += tr("%1 [%2], %3 instance(s), %4 channel(s).") .arg(pType->filename()) .arg(pType->index()) .arg(m_pPlugin->instances()) .arg(m_pPlugin->channels()); m_ui.AboutTextLabel->setText(sAboutText); updateLatencyTextLabel(); // This should trigger paramsSlot(!bEditor) // and adjust the size of the params dialog... m_ui.DirectAccessParamPushButton->setVisible(!params.isEmpty()); // Always first tab/page selected... m_ui.TabWidget->setCurrentIndex(0); // Clear any initial param update. qtractorSubject::resetQueue(); updateActivated(); refresh(); } // Plugin accessor. qtractorPlugin *qtractorPluginForm::plugin (void) const { return m_pPlugin; } // Plugin preset accessors. void qtractorPluginForm::setPreset ( const QString& sPreset ) { QString sEditText = sPreset; if (sEditText.isEmpty()) sEditText = qtractorPlugin::defPreset(); ++m_iUpdate; const bool bBlockSignals = m_ui.PresetComboBox->blockSignals(true); const int iIndex = m_ui.PresetComboBox->findText(sEditText); if (iIndex >= 0) m_ui.PresetComboBox->setCurrentIndex(iIndex); else m_ui.PresetComboBox->setEditText(sEditText); m_ui.PresetComboBox->blockSignals(bBlockSignals); --m_iUpdate; } QString qtractorPluginForm::preset (void) const { QString sPreset = m_ui.PresetComboBox->currentText(); if (sPreset == qtractorPlugin::defPreset() || m_iDirtyCount > 0) sPreset.clear(); return sPreset; } // Update activation state. void qtractorPluginForm::updateActivated (void) { if (m_pPlugin == nullptr) return; if (m_iUpdate > 0) return; ++m_iUpdate; m_ui.ActivateToolButton->setIcon( *g_pIcons[m_pPlugin->isAutoDeactivated() ? 1 : 0]); m_ui.ActivateToolButton->setChecked(m_pPlugin->isActivated()); --m_iUpdate; } // Update port widget state. void qtractorPluginForm::updateDirtyCount (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorPluginForm[%p]::updateDirtyCount()", this); #endif // Sure is dirty... ++m_iDirtyCount; stabilize(); } // Update specific MIDI Controller send auto-connect state. void qtractorPluginForm::updateMidiControlAutoConnect (void) { if (m_pPlugin == nullptr) return; qtractorPluginType *pType = m_pPlugin->type(); if (pType == nullptr) return; bool bAutoConnect = false;; if (pType->typeHint() == qtractorPluginType::Control && pType->index() == 0) { qtractorMidiControlPlugin *pMidiControlPlugin = static_cast (m_pPlugin); if (pMidiControlPlugin) { const bool bOldAutoConnect = m_ui.AutoConnectCheckBox->isChecked(); bAutoConnect = pMidiControlPlugin->isControlAutoConnect(); if (m_pMidiControlPluginWidget && ( bAutoConnect && !bOldAutoConnect) || (!bAutoConnect && bOldAutoConnect)) { m_pMidiControlPluginWidget->dirtyNotify(); } } } m_ui.AutoConnectCheckBox->setChecked(bAutoConnect); } // Update specific aux-send bus name settings. void qtractorPluginForm::updateAuxSendBusName (void) { if (m_pPlugin == nullptr) return; qtractorPluginType *pType = m_pPlugin->type(); if (pType == nullptr) return; m_ui.AuxSendBusNameComboBox->clear(); if (pType->typeHint() != qtractorPluginType::AuxSend) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; QString sAuxSendBusName; if (pType->index() > 0) { // index == channels > 0 => Audio aux-send. qtractorAudioAuxSendPlugin *pAudioAuxSendPlugin = static_cast (m_pPlugin); if (pAudioAuxSendPlugin == nullptr) return; qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine == nullptr) return; qtractorPluginList *pPluginList = pAudioAuxSendPlugin->list(); if (pPluginList == nullptr) return; const bool bAudioOutBus = (pPluginList->flags() == qtractorPluginList::AudioOutBus); QStringList cyclicAudioOutBuses; // Cyclic bus names to avoid. const QIcon& iconAudio = QIcon::fromTheme("trackAudio"); m_ui.AuxSendBusNameComboBox->addItem(iconAudio, tr("(none)")); QListIterator iter(pAudioEngine->buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Output) { qtractorAudioBus *pAudioBus = static_cast (pBus); if (pAudioBus) { if (bAudioOutBus && // Skip current or cyclic buses... pAudioBus->pluginList_out() == pPluginList) { cyclicAudioOutBuses.append( pAudioEngine->cyclicAudioOutBuses(pAudioBus)); continue; } m_ui.AuxSendBusNameComboBox->addItem(iconAudio, pAudioBus->busName()); } } } // Avoid cyclic bus names... QStringListIterator cyclic_iter(cyclicAudioOutBuses); while (cyclic_iter.hasNext()) { const QString& sBusName = cyclic_iter.next(); if (!sBusName.isEmpty()) { const int iIndex = m_ui.AuxSendBusNameComboBox->findText(sBusName); if (iIndex >= 0) m_ui.AuxSendBusNameComboBox->removeItem(iIndex); } } // Proceed... sAuxSendBusName = pAudioAuxSendPlugin->audioBusName(); } else { qtractorMidiAuxSendPlugin *pMidiAuxSendPlugin = static_cast (m_pPlugin); if (pMidiAuxSendPlugin == nullptr) return; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; qtractorPluginList *pPluginList = pMidiAuxSendPlugin->list(); if (pPluginList == nullptr) return; const bool bMidiOutBus = (pPluginList->flags() == qtractorPluginList::MidiOutBus); const QIcon& iconMidi = QIcon::fromTheme("trackMidi"); m_ui.AuxSendBusNameComboBox->addItem(iconMidi, tr("(none)")); QListIterator iter(pMidiEngine->buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Output) { qtractorMidiBus *pMidiBus = static_cast (pBus); if (pMidiBus) { if (bMidiOutBus && pMidiBus->pluginList_out() == pPluginList) continue; m_ui.AuxSendBusNameComboBox->addItem(iconMidi, pMidiBus->busName()); } } } sAuxSendBusName = pMidiAuxSendPlugin->midiBusName(); } int iIndex = m_ui.AuxSendBusNameComboBox->findText(sAuxSendBusName); if (iIndex < 0) iIndex = 0; m_ui.AuxSendBusNameComboBox->setCurrentIndex(iIndex); m_pPlugin->updateEditorTitle(); stabilize(); } // Editor widget methods. void qtractorPluginForm::toggleEditor ( bool bOn ) { if (m_pPlugin == nullptr) return; if (m_iUpdate > 0) return; ++m_iUpdate; m_ui.EditToolButton->setChecked(bOn); --m_iUpdate; } // Preset management slots... void qtractorPluginForm::changePresetSlot ( const QString& sPreset ) { if (m_iUpdate > 0) return; if (!sPreset.isEmpty() && m_ui.PresetComboBox->findText(sPreset) >= 0) ++m_iDirtyCount; stabilize(); } void qtractorPluginForm::loadPresetSlot ( int iPreset ) { if (m_pPlugin == nullptr) return; if (m_iUpdate > 0) return; const QString& sPreset = m_ui.PresetComboBox->itemText(iPreset); if (sPreset.isEmpty()) return; m_pPlugin->loadPresetEx(sPreset); stabilize(); } void qtractorPluginForm::openPresetSlot (void) { if (m_pPlugin == nullptr) return; qtractorPluginType *pType = m_pPlugin->type(); if (pType == nullptr) return; const bool bVst2Plugin = (pType->typeHint() == qtractorPluginType::Vst2); if (!pType->isConfigure() && !bVst2Plugin) return; if (m_iUpdate > 0) return; // 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("qtx"); QStringList exts(sExt); if (bVst2Plugin) { exts.append("fxp"); exts.append("fxb"); } const QString& sTitle = tr("Open Preset"); QStringList filters; filters.append(tr("Preset files (*.%1)").arg(exts.join(" *."))); 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->sPresetDir, sFilter, nullptr, options); #else // Construct save-file dialog... QFileDialog fileDialog(pParentWidget, sTitle, pOptions->sPresetDir, 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->sPresetDir)); 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()) { if (m_pPlugin->loadPresetFile(sFilename)) { // Got it loaded alright... const QFileInfo fi(sFilename); setPreset(fi.baseName().replace(pType->label() + '-', QString())); pOptions->sPresetDir = fi.absolutePath(); } else { // Failure (maybe wrong plugin)... QMessageBox::critical(this, tr("Error"), tr("Preset could not be loaded from file:\n\n" "\"%1\".\n\n" "Sorry.").arg(sFilename), QMessageBox::Cancel); } } refresh(); } void qtractorPluginForm::savePresetSlot (void) { if (m_pPlugin == nullptr) return; qtractorPluginType *pType = m_pPlugin->type(); if (pType == nullptr) return; const QString& sPreset = m_ui.PresetComboBox->currentText(); if (sPreset.isEmpty() || sPreset == qtractorPlugin::defPreset()) return; // The current state preset is about to be saved... // this is where we'll make it! if (m_pPlugin->savePreset(sPreset)) { refresh(); return; } // We'll need this, sure. qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; QSettings& settings = pOptions->settings(); settings.beginGroup(m_pPlugin->presetGroup()); // Which mode of preset? const bool bVst2Plugin = (pType->typeHint() == qtractorPluginType::Vst2); if (pType->isConfigure() || bVst2Plugin) { // Sure, we'll have something complex enough // to make it save into an external file... const QString sExt("qtx"); QStringList exts(sExt); if (bVst2Plugin) { exts.append("fxp"); exts.append("fxb"); } QFileInfo fi(settings.value(sPreset).toString()); if (!fi.exists() || !fi.isFile()) fi.setFile(QDir(pOptions->sPresetDir), pType->label() + '-' + sPreset + '.' + sExt); QString sFilename = fi.absoluteFilePath(); // Prompt if file does not currently exist... if (!fi.exists()) { const QString& sTitle = tr("Save Preset"); QStringList filters; filters.append(tr("Preset files (*.%1)").arg(exts.join(" *."))); 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::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->sPresetDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sFilename = fileDialog.selectedFiles().first(); else sFilename.clear(); #endif } // We've a filename to save the preset... if (!sFilename.isEmpty() && sFilename.at(0) != '.') { fi.setFile(sFilename); if (fi.suffix().isEmpty()) sFilename += '.' + sExt; if (m_pPlugin->savePresetFile(sFilename)) { settings.setValue(sPreset, sFilename); pOptions->sPresetDir = fi.absolutePath(); } else { // Failure (maybe wrong suffix)... QMessageBox::critical(this, tr("Error"), tr("Preset could not be saved to file:\n\n" "\"%1\".\n\n" "Sorry.").arg(sFilename), QMessageBox::Cancel); } } } // Just leave it to simple parameter value list... else settings.setValue(sPreset, m_pPlugin->valueList()); settings.endGroup(); refresh(); } void qtractorPluginForm::deletePresetSlot (void) { if (m_pPlugin == nullptr) return; const QString& sPreset = m_ui.PresetComboBox->currentText(); if (sPreset.isEmpty() || sPreset == qtractorPlugin::defPreset()) return; // We'll need this, sure. qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; // A preset entry is about to be deleted; // 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\" (%2)\n\n" "Are you sure?") .arg(sPreset) .arg(m_pPlugin->title()), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; } // Go ahead... if (!m_pPlugin->deletePreset(sPreset)) { QSettings& settings = pOptions->settings(); settings.beginGroup(m_pPlugin->presetGroup()); #ifdef QTRACTOR_REMOVE_PRESET_FILES if ((m_pPlugin->type())->isConfigure()) { const QString& sFilename = settings.value(sPreset).toString(); if (QFileInfo(sFilename).exists()) QFile(sFilename).remove(); } #endif settings.remove(sPreset); settings.endGroup(); } refresh(); } // Alias finish-editing slot. void qtractorPluginForm::aliasSlot (void) { if (m_pPlugin == nullptr) return; if (m_iUpdate > 0) return; ++m_iUpdate; // Make it as an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pSession->execute( new qtractorPluginAliasCommand(m_pPlugin, m_ui.AliasLineEdit->text())); --m_iUpdate; } // Editor slot. void qtractorPluginForm::editSlot ( bool bOn ) { if (m_pPlugin == nullptr) return; if (m_iUpdate > 0) return; ++m_iUpdate; if (bOn) m_pPlugin->openEditor(); else m_pPlugin->closeEditor(); --m_iUpdate; } // Activation slot. void qtractorPluginForm::activateSlot ( bool bOn ) { if (m_pPlugin == nullptr) return; if (m_iUpdate > 0) return; ++m_iUpdate; // Make it a undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pSession->execute( new qtractorActivatePluginCommand(m_pPlugin, bOn)); --m_iUpdate; } // Tab page change slot. void qtractorPluginForm::currentChangedSlot ( int iTab ) { // Make sure we're in the "About" page... if (iTab < m_ui.TabWidget->count() - 1) return; updateLatencyTextLabel(); } // Outputs (Sends) slot. void qtractorPluginForm::sendsSlot (void) { qtractorPluginListView::insertPluginBus(m_pPlugin, qtractorBus::Output); } // Inputs (Returns) slot. void qtractorPluginForm::returnsSlot (void) { qtractorPluginListView::insertPluginBus(m_pPlugin, qtractorBus::Input); } // Auto-connect (MIDI Controller Send) slot. void qtractorPluginForm::autoConnectSlot ( bool bOn ) { if (m_pMidiControlPluginWidget) { qtractorMidiControlPlugin *pMidiControlPlugin = m_pMidiControlPluginWidget->midiControlPlugin(); if (pMidiControlPlugin) { pMidiControlPlugin->setControlAutoConnect(bOn); m_pMidiControlPluginWidget->dirtyNotify(); } } } // Audio bus name (aux-send) select slot. void qtractorPluginForm::changeAuxSendBusNameSlot ( int iAuxSendBusName ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; if (m_pPlugin == nullptr) return; qtractorPluginType *pType = m_pPlugin->type(); if (pType == nullptr) return; if (pType->typeHint() != qtractorPluginType::AuxSend) return; const QString& sAuxSendBusName = m_ui.AuxSendBusNameComboBox->itemText(iAuxSendBusName); if (sAuxSendBusName.isEmpty()) return; pSession->execute( new qtractorAuxSendPluginCommand(m_pPlugin, sAuxSendBusName)); qtractorPluginListView::updateAuxSendPluginBus(m_pPlugin); } // Audio bus name (aux-send) browse slot. void qtractorPluginForm::clickAuxSendBusNameSlot (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; if (m_pPlugin == nullptr) return; qtractorPluginType *pType = m_pPlugin->type(); if (pType == nullptr) return; if (pType->typeHint() != qtractorPluginType::AuxSend) return; qtractorEngine *pEngine = nullptr; if (pType->index() > 0) // index == channels > 0 => Audio aux-send. pEngine = pSession->audioEngine(); else pEngine = pSession->midiEngine(); if (pEngine == nullptr) return; // Call here the bus management form. qtractorBusForm busForm(this); // Pre-select bus... const QString& sAuxSendBusName = m_ui.AuxSendBusNameComboBox->currentText(); if (!sAuxSendBusName.isEmpty()) busForm.setBus(pEngine->findBus(sAuxSendBusName)); // Go for it... busForm.exec(); // Check if any buses have changed... if (busForm.isDirty()) updateAuxSendBusName(); } // Audio bus I/O matrix (aux-send) edit slot. void qtractorPluginForm::clickAuxSendIOMatrixSlot (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; if (m_pPlugin == nullptr) return; qtractorPluginType *pType = m_pPlugin->type(); if (pType == nullptr) return; if (pType->typeHint() != qtractorPluginType::AuxSend) return; if (pType->index() == 0) // index == 0 => MIDI aux-send. return; qtractorAudioAuxSendPlugin *pAudioAuxSendPlugin = static_cast (m_pPlugin); if (pAudioAuxSendPlugin == nullptr) return; qtractorAudioBus *pAudioBus = pAudioAuxSendPlugin->audioBus(); if (pAudioBus == nullptr) return; qtractorAudioIOMatrixForm dialog(this); dialog.setWindowTitle(m_pPlugin->editorTitle()); dialog.setChannels(pAudioAuxSendPlugin->channels(), pAudioBus->channels()); dialog.setMatrix(pAudioAuxSendPlugin->audioBusMatrix()); dialog.refresh(); if (dialog.exec() == QDialog::Accepted) { pSession->execute( new qtractorAuxSendIOMatrixCommand(m_pPlugin, dialog.matrix())); } } // Direct access parameter slots void qtractorPluginForm::updateDirectAccessParamSlot (void) { m_pDirectAccessParamMenu->clear(); if (m_pPlugin == nullptr) return; QAction *pAction; const int iDirectAccessParamIndex = m_pPlugin->directAccessParamIndex(); const qtractorPlugin::Params& params = m_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(); const int iParamIndex = int(param.key()); pAction = m_pDirectAccessParamMenu->addAction( pParam->name(), this, SLOT(changeDirectAccessParamSlot())); pAction->setCheckable(true); pAction->setChecked(iDirectAccessParamIndex == iParamIndex); pAction->setData(iParamIndex); } if (!params.isEmpty()) m_pDirectAccessParamMenu->addSeparator(); pAction = m_pDirectAccessParamMenu->addAction( tr("&None"), this, SLOT(changeDirectAccessParamSlot())); pAction->setCheckable(true); pAction->setChecked(iDirectAccessParamIndex < 0); pAction->setData(int(-1)); } void qtractorPluginForm::changeDirectAccessParamSlot (void) { if (m_pPlugin == nullptr) return; if (m_iUpdate > 0) return; // Retrieve direct access parameter index from action data... QAction *pAction = qobject_cast (sender()); if (pAction == nullptr) return; const int iDirectAccessParamIndex = pAction->data().toInt(); ++m_iUpdate; // Make it a undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pSession->execute( new qtractorDirectAccessParamCommand(m_pPlugin, iDirectAccessParamIndex)); --m_iUpdate; } void qtractorPluginForm::updateParamRangeSlot (void) { if (m_pPlugin == nullptr) return; if (m_pMidiControlPluginWidget == nullptr) return; if (m_iUpdate > 0) return; QListIterator iter(m_paramWidgets); while (iter.hasNext()) iter.next()->updateParamRange(); } // Parameter-widget refreshner-loader. void qtractorPluginForm::refresh (void) { if (m_pPlugin == nullptr) return; if (m_iUpdate > 0) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorPluginForm[%p]::refresh()", this); #endif ++m_iUpdate; const bool bBlockSignals = m_ui.PresetComboBox->blockSignals(true); qtractorSubject::flushQueue(true); const QString sOldPreset = m_ui.PresetComboBox->currentText(); m_ui.PresetComboBox->clear(); m_ui.PresetComboBox->insertItems(0, m_pPlugin->presetList()); m_ui.PresetComboBox->model()->sort(0); m_ui.PresetComboBox->addItem(qtractorPlugin::defPreset()); const int iIndex = m_ui.PresetComboBox->findText(sOldPreset); if (iIndex >= 0) m_ui.PresetComboBox->setCurrentIndex(iIndex); else m_ui.PresetComboBox->setEditText(sOldPreset); // Update the nominal user/title if any... m_ui.AliasLineEdit->setText(m_pPlugin->alias()); m_ui.ActivateToolButton->setChecked(m_pPlugin->isActivated()); // m_pPlugin->idleEditor(); QListIterator param_iter(m_paramWidgets); while (param_iter.hasNext()) param_iter.next()->refresh(); updateAuxSendBusName(); qtractorSubject::resetQueue(); updateLatencyTextLabel(); m_iDirtyCount = 0; m_ui.PresetComboBox->blockSignals(bBlockSignals); --m_iUpdate; stabilize(); } // Preset control. void qtractorPluginForm::stabilize (void) { if (m_pPlugin == nullptr) return; qtractorPluginType *pType = m_pPlugin->type(); if (pType == nullptr) return; const bool bVst2Plugin = (pType->typeHint() == qtractorPluginType::Vst2); bool bExists = false; bool bEnabled = (m_pPlugin != nullptr); m_ui.ActivateToolButton->setEnabled(bEnabled); if (bEnabled) bEnabled = (pType->controlIns() > 0 || pType->isConfigure()); m_ui.PresetComboBox->setEnabled(bEnabled); m_ui.OpenPresetToolButton->setVisible( bEnabled && (pType->isConfigure() || bVst2Plugin)); if (bEnabled) { const QString& sPreset = m_ui.PresetComboBox->currentText(); bEnabled = !sPreset.isEmpty() && sPreset != qtractorPlugin::defPreset() && !m_pPlugin->isReadOnlyPreset(sPreset); bExists = (m_ui.PresetComboBox->findText(sPreset) >= 0); } m_ui.SavePresetToolButton->setEnabled( bEnabled && (!bExists || m_iDirtyCount > 0)); m_ui.DeletePresetToolButton->setEnabled( bEnabled && bExists); if (pType->typeHint() == qtractorPluginType::AuxSend && pType->index() > 0) { // index == channels > 0 => Audio aux-send. qtractorAudioBus *pAudioBus = nullptr; qtractorAudioAuxSendPlugin *pAudioAuxSendPlugin = static_cast (m_pPlugin); if (pAudioAuxSendPlugin) pAudioBus = pAudioAuxSendPlugin->audioBus(); m_ui.AuxSendIOMatrixToolButton->setEnabled(pAudioBus != nullptr); } } // Clear up plugin form... void qtractorPluginForm::clear (void) { if (m_pPlugin) m_pPlugin->closeEditor(); qDeleteAll(m_paramWidgets); m_paramWidgets.clear(); m_pDirectAccessParamMenu->clear(); if (m_pMidiControlPluginWidget) { delete m_pMidiControlPluginWidget; m_pMidiControlPluginWidget = nullptr; } } // Keyboard event handler. void qtractorPluginForm::keyPressEvent ( QKeyEvent *pKeyEvent ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorPluginForm::keyPressEvent(%d)", pKeyEvent->key()); #endif const int iKey = pKeyEvent->key(); switch (iKey) { case Qt::Key_Escape: close(); break; default: QWidget::keyPressEvent(pKeyEvent); break; } } // Form show event (refresh). void qtractorPluginForm::showEvent ( QShowEvent *pShowEvent ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorPluginForm[%p]::showEvent()", this); #endif QWidget::showEvent(pShowEvent); // Make sure all plugin-data is up-to-date... refresh(); } // Form hide event (save visible position). void qtractorPluginForm::hideEvent ( QHideEvent *pHideEvent ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorPluginForm[%p]::hideEvent()", this); #endif // Save current form position... if (m_pPlugin) m_pPlugin->setFormPos(QWidget::pos()); QWidget::hideEvent(pHideEvent); } // MIDI controller/observer attachment (context menu) void qtractorPluginForm::addMidiControlAction ( QWidget *pWidget, qtractorMidiControlObserver *pMidiObserver ) { qtractorMidiControlObserverForm::addMidiControlAction( this, pWidget, pMidiObserver); } void qtractorPluginForm::midiControlActionSlot (void) { qtractorMidiControlObserverForm::midiControlAction( this, qobject_cast (sender())); } void qtractorPluginForm::midiControlMenuSlot ( const QPoint& pos ) { qtractorMidiControlObserverForm::midiControlMenu( qobject_cast (sender()), pos); } // Update the about text label (with some varying meta-data)... void qtractorPluginForm::updateLatencyTextLabel (void) { if (m_pPlugin == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const unsigned long iLatency = m_pPlugin->latency(); if (iLatency > 0) { const float fLatencyMs = 1000.0f * float(iLatency) / float(pSession->sampleRate()); m_ui.LatencyTextLabel->setText( tr("Latency: %1 ms (%2 frames)") .arg(QString::number(fLatencyMs, 'f', 1)) .arg(iLatency)); } else { m_ui.LatencyTextLabel->setText(tr("(no latency)")); } } //---------------------------------------------------------------------- // class qtractorPluginParamDisplay -- Observer display label. // class qtractorPluginParamDisplay : public QLabel { public: // Local observer. class Observer : public qtractorObserver { public: // Constructor. Observer(qtractorSubject *pSubject, qtractorPluginParamDisplay *pDisplay) : qtractorObserver(pSubject), m_pDisplay(pDisplay) {} // Observer updater. void update(bool bUpdate) { if (bUpdate) m_pDisplay->updateDisplay(); } private: // Members. qtractorPluginParamDisplay *m_pDisplay; }; // Constructor. qtractorPluginParamDisplay(qtractorPlugin::Param *pParam) : QLabel(), m_pParam(pParam), m_observer(pParam->subject(), this) {} // Observer accessor. Observer *observer() { return &m_observer; } protected: void updateDisplay() { QLabel::setText(m_pParam->display()); } private: // Parameter reference. qtractorPlugin::Param *m_pParam; // Observer instance. Observer m_observer; }; //---------------------------------------------------------------------- // class qtractorPluginParamWidget::SliderInterface -- Observer interface. // // Local converter interface. class qtractorPluginParamWidget::SliderInterface : public qtractorObserverSlider::Interface { public: // Constructor. SliderInterface ( qtractorPlugin::Param *pParam ) : m_pParam(pParam) {} // Formerly Pure virtuals. float scaleFromValue ( float fValue ) const { return 10000.0f * m_pParam->observer()->scaleFromValue(fValue, m_pParam->isLogarithmic()); } float valueFromScale ( float fScale ) const { return m_pParam->observer()->valueFromScale(0.0001f * fScale, m_pParam->isLogarithmic()); } private: // Instance references. qtractorPlugin::Param *m_pParam; }; //---------------------------------------------------------------------------- // qtractorPluginParamWidget -- Plugin parameter/property common widget. // // Constructors. qtractorPluginParamWidget::qtractorPluginParamWidget ( qtractorPlugin::Param *pParam, QWidget *pParent ) : QWidget(pParent), m_pParam(pParam) { m_pSlider = nullptr; m_pSpinBox = nullptr; m_pCheckBox = nullptr; m_pDisplay = nullptr; m_pCurveButton = nullptr; m_pTextEdit = nullptr; m_pComboBox = nullptr; m_pToolButton = nullptr; QGridLayout *pGridLayout = new QGridLayout(); pGridLayout->setContentsMargins(0, 0, 0, 0); pGridLayout->setSpacing(4); qtractorPlugin::Property *pProp = property(); if (m_pParam->isToggled()) { m_pCheckBox = new qtractorObserverCheckBox(/*this*/); // m_pCheckBox->setMinimumWidth(120); m_pCheckBox->setText(m_pParam->name()); m_pCheckBox->setSubject(m_pParam->subject()); // m_pCheckBox->setChecked(m_pParam->value() > 0.1f); pGridLayout->addWidget(m_pCheckBox, 0, 0); pGridLayout->setColumnStretch(0, 2); if (m_pParam->isDisplay()) { m_pDisplay = new qtractorPluginParamDisplay(m_pParam); m_pDisplay->setAlignment(Qt::AlignCenter | Qt::AlignVCenter); // m_pDisplay->setText(m_pParam->display()); m_pDisplay->setMinimumWidth(64); pGridLayout->addWidget(m_pDisplay, 0, 2); } } else { QLabel *pLabel = new QLabel(/*this*/); pLabel->setText(m_pParam->name() + ':'); if (pProp && pProp->isString()) { pLabel->setAlignment(Qt::AlignLeft | Qt::AlignBottom); // pLabel->setMinimumWidth(120); pGridLayout->addWidget(pLabel, 0, 0, 1, 3); m_pTextEdit = new QTextEdit(/*this*/); m_pTextEdit->setTabChangesFocus(true); m_pTextEdit->setMinimumWidth(120); m_pTextEdit->setMaximumHeight(60); m_pTextEdit->installEventFilter(this); // m_pTextEdit->setPlainText(pProp->variant().toString()); pGridLayout->addWidget(m_pTextEdit, 1, 0, 2, 3); } else if (pProp && pProp->isPath()) { pLabel->setAlignment(Qt::AlignLeft | Qt::AlignBottom); // pLabel->setMinimumWidth(120); pGridLayout->addWidget(pLabel, 0, 0, 1, 3); m_pComboBox = new QComboBox(/*this*/); m_pComboBox->setEditable(false); m_pComboBox->setMinimumWidth(120); // m_pComboBox->addItem(pProp->variant().toString()); pGridLayout->addWidget(m_pComboBox, 1, 0, 1, 2); pGridLayout->setColumnStretch(0, 2); m_pToolButton = new QToolButton(/*this*/); m_pToolButton->setIcon(QIcon::fromTheme("fileOpen")); pGridLayout->addWidget(m_pToolButton, 1, 2); } else if (m_pParam->isInteger()) { int iCol = 0; pLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); pGridLayout->addWidget(pLabel, 0, iCol++); m_pSpinBox = new qtractorObserverSpinBox(/*this*/); m_pSpinBox->setMinimumWidth(52); m_pSpinBox->setMaximumWidth(96); m_pSpinBox->setDecimals(0); m_pSpinBox->setMinimum(m_pParam->minValue()); m_pSpinBox->setMaximum(m_pParam->maxValue()); m_pSpinBox->setAlignment(pProp ? Qt::AlignRight : Qt::AlignHCenter); m_pSpinBox->setSubject(m_pParam->subject()); // m_pSpinBox->setValue(int(m_pParam->value())); pGridLayout->setColumnStretch(iCol, 2); if (m_pParam->isDisplay()) { m_pDisplay = new qtractorPluginParamDisplay(m_pParam); m_pDisplay->setAlignment(Qt::AlignRight | Qt::AlignVCenter); // m_pDisplay->setText(m_pParam->display()); m_pDisplay->setMinimumWidth(64); pGridLayout->addWidget(m_pDisplay, 0, iCol++); } pGridLayout->addWidget(m_pSpinBox, 0, iCol, Qt::AlignRight | Qt::AlignVCenter); } else { if (m_pParam->isDisplay()) { pLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); pLabel->setMinimumWidth(64); pGridLayout->addWidget(pLabel, 0, 0); } else { pLabel->setAlignment(Qt::AlignLeft | Qt::AlignBottom); pGridLayout->addWidget(pLabel, 0, 0, 1, 3); } m_pSlider = new qtractorObserverSlider(/*this*/); m_pSlider->setInterface(new SliderInterface(m_pParam)); m_pSlider->setOrientation(Qt::Horizontal); m_pSlider->setTickPosition(QSlider::NoTicks); m_pSlider->setMinimumWidth(120); m_pSlider->setMinimum(0); m_pSlider->setMaximum(10000); m_pSlider->setPageStep(250); m_pSlider->setSingleStep(50); m_pSlider->setSubject(m_pParam->subject()); // m_pSlider->setValue(m_pSlider->scaleFromValue(m_pParam->value())); if (m_pParam->isDisplay()) { pGridLayout->addWidget(m_pSlider, 0, 1); m_pDisplay = new qtractorPluginParamDisplay(m_pParam); m_pDisplay->setAlignment(Qt::AlignCenter | Qt::AlignVCenter); // m_pDisplay->setText(m_pParam->display()); // m_pDisplay->setFixedWidth(72); m_pDisplay->setMinimumWidth(64); pGridLayout->addWidget(m_pDisplay, 0, 2); } else { pGridLayout->addWidget(m_pSlider, 1, 0, 1, 3); const int iDecimals = m_pParam->decimals(); m_pSpinBox = new qtractorObserverSpinBox(/*this*/); m_pSpinBox->setMinimumWidth(64); m_pSpinBox->setMaximumWidth(96); m_pSpinBox->setDecimals(iDecimals); m_pSpinBox->setMinimum(m_pParam->minValue()); m_pSpinBox->setMaximum(m_pParam->maxValue()); m_pSpinBox->setSingleStep(::powf(10.0f, - float(iDecimals))); m_pSpinBox->setAccelerated(true); m_pSpinBox->setSubject(m_pParam->subject()); // m_pSpinBox->setValue(m_pParam->value()); pGridLayout->addWidget(m_pSpinBox, 1, 3); } } } QWidget::setLayout(pGridLayout); if (m_pCheckBox) { QObject::connect(m_pCheckBox, SIGNAL(valueChanged(float)), SLOT(updateValue(float))); } if (m_pSpinBox) { QObject::connect(m_pSpinBox, SIGNAL(valueChanged(float)), SLOT(updateValue(float))); } if (m_pSlider) { QObject::connect(m_pSlider, SIGNAL(valueChanged(float)), SLOT(updateValue(float))); } if (m_pComboBox) { QObject::connect(m_pComboBox, SIGNAL(activated(int)), SLOT(propertyChanged())); } if (m_pToolButton) { QObject::connect(m_pToolButton, SIGNAL(clicked()), SLOT(toolButtonClicked())); } updateCurveButton(); } // Destructor. qtractorPluginParamWidget::~qtractorPluginParamWidget (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl && m_pParam) pMidiControl->unmapMidiObserverWidget(m_pParam->observer(), this); } // Param/Property discriminator.. qtractorPlugin::Property *qtractorPluginParamWidget::property (void) const { return dynamic_cast (m_pParam); } // Refreshner-loader method. void qtractorPluginParamWidget::refresh (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorPluginParamWidget[%p]::refresh()", this); #endif qtractorPlugin::Property *pProp = property(); if (pProp && !pProp->isAutomatable()) { if (m_pCheckBox) { const bool bCheckBox = m_pCheckBox->blockSignals(true); m_pCheckBox->setChecked(pProp->variant().toBool()); m_pCheckBox->blockSignals(bCheckBox); } if (m_pSpinBox) { const bool bSpinBox = m_pSpinBox->blockSignals(true); m_pSpinBox->setValue(pProp->variant().toDouble()); m_pSpinBox->blockSignals(bSpinBox); } if (m_pTextEdit) { const bool bTextEdit = m_pTextEdit->blockSignals(true); m_pTextEdit->setPlainText(pProp->variant().toString()); m_pTextEdit->document()->setModified(false); m_pTextEdit->blockSignals(bTextEdit); } if (m_pComboBox) { const bool bComboBox = m_pComboBox->blockSignals(true); const QFileInfo fi(pProp->variant().toString().remove('\0')); const QString& sPath = fi.canonicalFilePath(); int iIndex = m_pComboBox->findData(sPath); if (iIndex < 0) { m_pComboBox->insertItem(0, fi.fileName(), sPath); iIndex = 0; } m_pComboBox->setCurrentIndex(iIndex); m_pComboBox->setToolTip(sPath); m_pComboBox->blockSignals(bComboBox); } } else { if (m_pCheckBox) m_pCheckBox->observer()->update(true); if (m_pSpinBox) m_pSpinBox->observer()->update(true); if (m_pSlider) m_pSlider->observer()->update(true); if (m_pDisplay) m_pDisplay->observer()->update(true); } updateCurveButton(); } // Special range updater. void qtractorPluginParamWidget::updateParamRange (void) { const float fValue = m_pParam->value(); if (m_pSpinBox) { const bool bSpinBox = m_pSpinBox->blockSignals(true); m_pSpinBox->setMinimum(m_pParam->minValue()); m_pSpinBox->setMaximum(m_pParam->maxValue()); m_pSpinBox->setValue(fValue); m_pSpinBox->blockSignals(bSpinBox); } if (m_pSlider) { const bool bSlider = m_pSlider->blockSignals(true); m_pSlider->setValue(m_pSlider->scaleFromValue(fValue)); m_pSlider->blockSignals(bSlider); } } // Parameter automation curve status update/refresh. void qtractorPluginParamWidget::updateCurveButton (void) { qtractorPlugin::Property *pProp = property(); if (pProp && !pProp->isAutomatable()) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; QGridLayout *pGridLayout = static_cast (QWidget::layout()); if (pGridLayout == nullptr) return; qtractorMidiControlObserver *pMidiObserver = m_pParam->observer(); if (pMidiObserver == nullptr) return; qtractorPluginList *pPluginList = (m_pParam->plugin())->list(); if (pPluginList == nullptr) return; qtractorTrack *pTrack = nullptr; qtractorCurveList *pCurveList = pPluginList->curveList(); if (pCurveList && pCurveList == pMidiObserver->curveList()) pTrack = pSession->findTrackCurveList(pCurveList); if (pTrack == nullptr) { if (m_pCurveButton) { m_pCurveButton->hide(); delete m_pCurveButton; m_pCurveButton = nullptr; pGridLayout->setColumnMinimumWidth(3, 0); } // Bail out! return; } if (m_pCurveButton == nullptr) { QSize iconSize(16, 16); m_pCurveButton = new QPushButton(/*this*/); m_pCurveButton->setFlat(true); m_pCurveButton->setIconSize(iconSize); m_pCurveButton->setMaximumSize(iconSize + QSize(4, 4)); pGridLayout->addWidget(m_pCurveButton, 0, 3, Qt::AlignRight | Qt::AlignVCenter); pGridLayout->setColumnMinimumWidth(3, 22); QObject::connect(m_pCurveButton, SIGNAL(clicked()), SLOT(curveButtonClicked())); } qtractorCurve *pCurve = nullptr; qtractorSubject *pSubject = pMidiObserver->subject(); if (pSubject) pCurve = pSubject->curve(); if (pCurve && pCurve->isCapture()) m_pCurveButton->setIcon(QIcon::fromTheme("trackCurveCapture")); else if (pCurve && pCurve->isProcess()) m_pCurveButton->setIcon(QIcon::fromTheme("trackCurveProcess")); else if (pCurve) m_pCurveButton->setIcon(QIcon::fromTheme("trackCurveEnabled")); else m_pCurveButton->setIcon(QIcon::fromTheme("trackCurveNone")); } // Parameter value change slot. void qtractorPluginParamWidget::updateValue ( float fValue ) { qtractorPlugin::Property *pProp = property(); if (pProp) propertyChanged(); else m_pParam->updateValue(fValue, true); } // Automation curve selector. void qtractorPluginParamWidget::curveButtonClicked (void) { const QPoint& pos = m_pCurveButton->geometry().bottomLeft(); qtractorMidiControlObserverForm::midiControlMenu(this, pos); } // Property file selector. void qtractorPluginParamWidget::toolButtonClicked (void) { // Sure we have this... if (m_pComboBox == nullptr) return; // We'll need this, sure. qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; // Ask for the filename to open... const int iIndex = m_pComboBox->currentIndex(); if (iIndex < 0) return; QString sFilename = m_pComboBox->itemData(iIndex).toString(); const QString& sTitle = tr("Open File"); 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) 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()) { const bool bComboBox = m_pComboBox->blockSignals(true); const QFileInfo fi(sFilename); const QString& sPath = fi.canonicalFilePath(); int iIndex = m_pComboBox->findData(sPath); if (iIndex < 0) { m_pComboBox->insertItem(0, fi.fileName(), sPath); iIndex = 0; } m_pComboBox->setCurrentIndex(iIndex); m_pComboBox->setToolTip(sPath); m_pComboBox->blockSignals(bComboBox); propertyChanged(); } } // Property value change slot. void qtractorPluginParamWidget::propertyChanged (void) { qtractorPlugin::Property *pProp = property(); if (pProp == nullptr) return; qtractorPlugin *pPlugin = pProp->plugin(); if (pPlugin == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorPluginParamWidget[%p]::propertyChanged()", this); #endif QVariant value; if (m_pCheckBox) value = bool(m_pCheckBox->isChecked()); if (m_pSpinBox) value = float(m_pSpinBox->value()); if (m_pTextEdit && m_pTextEdit->document()->isModified()) { value = m_pTextEdit->toPlainText(); m_pTextEdit->document()->setModified(false); } if (m_pComboBox) { const int iIndex = m_pComboBox->currentIndex(); if (iIndex >= 0) value = m_pComboBox->itemData(iIndex); } if (!value.isValid()) return; if (pProp->isAutomatable() && pPlugin->isLastUpdatedProperty(pProp)) { pProp->setVariant(value, true); pPlugin->updateFormDirtyCount(); pPlugin->refreshForm(); } else { // Make it as an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { pSession->execute( new qtractorPluginPropertyCommand(pProp, value)); // pPlugin->setLastUpdatedProperty(pProp); } } } // Text edit (string) event filter. bool qtractorPluginParamWidget::eventFilter ( QObject *pObject, QEvent *pEvent ) { if (qobject_cast (pObject) == m_pTextEdit) { if (pEvent->type() == QEvent::KeyPress) { QKeyEvent *pKeyEvent = static_cast (pEvent); if (pKeyEvent->key() == Qt::Key_Return && (pKeyEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) == 0) { propertyChanged(); return true; } } else if (pEvent->type() == QEvent::FocusOut) propertyChanged(); } return QWidget::eventFilter(pObject, pEvent); } // end of qtractorPluginForm.cpp qtractor-1.5.9/src/PaxHeaders/qtractorLv2Plugin.cpp0000644000000000000000000000013115101070305017327 xustar0030 mtime=1761898693.074267613 29 atime=1761898693.07326761 30 ctime=1761898693.074267613 qtractor-1.5.9/src/qtractorLv2Plugin.cpp0000644000175000001440000050710615101070305017331 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.9/src/PaxHeaders/qtractorClipForm.ui0000644000000000000000000000013215101070305017054 xustar0030 mtime=1761898693.067267591 30 atime=1761898693.067267591 30 ctime=1761898693.067267591 qtractor-1.5.9/src/qtractorClipForm.ui0000644000175000001440000005012515101070305017047 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. qtractorClipForm 0 0 420 540 Qt::StrongFocus Clip &Name: ClipNameLineEdit Clip name &File: FilenameComboBox Clip filename true 22 22 22 22 Qt::TabFocus Browse for clip file Qt::Horizontal 20 8 Track/&Channel: Qt::AlignRight|Qt::AlignVCenter TrackChannelSpinBox 40 0 Clip track/channel Parameters 60 0 &Start: ClipStartSpinBox 120 0 Clip start Qt::Horizontal 20 20 &Gain: Qt::AlignRight|Qt::AlignVCenter ClipGainSpinBox 66 0 Clip gain/volume Qt::AlignCenter true 1 0.1 60 0 Offs&et: ClipOffsetSpinBox 120 0 Clip offset &Panning: Qt::AlignRight|Qt::AlignVCenter ClipPanningSpinBox 66 0 Clip gain/volume Qt::AlignCenter false 1 0.1 60 0 &Length: ClipLengthSpinBox 120 0 Clip length Forma&t: Qt::AlignRight|Qt::AlignVCenter FormatComboBox Time display format Frames Time BBT Fade In/Out 60 0 Fade &In: FadeInLengthSpinBox 120 0 Clip fade-in length Clip fade-in type Qt::Horizontal 20 20 60 0 Fade &Out: FadeOutLengthSpinBox 120 0 Clip fade-out length Clip fade-out type Audio Qt::Horizontal 8 8 60 0 Ti&me Stretch: TimeStretchSpinBox 66 0 Clip time-stretch percentage true % 1 10.0 1000.0 0.1 100.0 Pitch S&hift: Qt::AlignRight|Qt::AlignVCenter PitchShiftSpinBox 66 0 Clip pitch-shift in semitones true semitones 2 -40.0 40.0 0.01 0.0 50 false Whether to use WSOLA time-stretching &WSOLA time-stretching 50 false Whether to use RubberBand formant preserve RubberBand &formant preserve 50 false Whether to apply WSOLA quick seek time-stretching WSOLA quic&k seek 50 false Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine &Mute Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok qtractorTimeSpinBox QSpinBox
qtractorSpinBox.h
ClipNameLineEdit FilenameComboBox FilenameToolButton TrackChannelSpinBox ClipStartSpinBox ClipOffsetSpinBox ClipLengthSpinBox ClipGainSpinBox ClipPanningSpinBox FormatComboBox FadeInLengthSpinBox FadeInTypeComboBox FadeOutLengthSpinBox FadeOutTypeComboBox TimeStretchSpinBox PitchShiftSpinBox WsolaTimeStretchCheckBox WsolaQuickSeekCheckBox RubberBandFormantCheckBox RubberBandFinerR3CheckBox ClipMuteCheckBox DialogButtonBox
qtractor-1.5.9/src/PaxHeaders/clap0000644000000000000000000000013215101070347014072 xustar0030 mtime=1761898727.394376447 30 atime=1761898693.057007007 30 ctime=1761898727.394376447 qtractor-1.5.9/src/clap/0000755000175000001440000000000015101070347014137 5ustar00rncbcusersqtractor-1.5.9/src/clap/PaxHeaders/clap.pc.in0000644000000000000000000000013215101070323016011 xustar0030 mtime=1761898707.882314571 30 atime=1761898707.882314571 30 ctime=1761898707.882314571 qtractor-1.5.9/src/clap/clap.pc.in0000644000175000001440000000032515101070323016001 0ustar00rncbcusersprefix=@CMAKE_INSTALL_PREFIX@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ Name: clap Description: The interface headers for the CLAP audio plugin API Version: @CMAKE_PROJECT_VERSION@ Cflags: -I${includedir} qtractor-1.5.9/src/clap/PaxHeaders/Contributors.md0000644000000000000000000000013215101070323017160 xustar0030 mtime=1761898707.845700915 30 atime=1761898707.845700915 30 ctime=1761898707.845700915 qtractor-1.5.9/src/clap/Contributors.md0000644000175000001440000000146615101070323017157 0ustar00rncbcusersCLAP is the result of countless conversations, brainstorms, reviews, tests, and iterations. A legion of developers with different backgrounds and points of view worked together and converged on a, well, clever solution. All of the contributors cannot be listed. But here are a few, along with a thank you to all involved: - Alexandre Bique (Bitwig and u-he/linux) - Claes Johanson (Bitwig) - David Schornsheim (Schroedingers-Cat, u-he) - Frank Hoffmann (u-he) - Giel Bremmers (MultitrackStudio) - Jan Storm (u-he) - John Schwartz (Cockos) - Nicholas Allen (Bitwig) - Paul Walker (BaconPaul, Surge) - Placidus Schelbert (Bitwig) - Robbert van der Helm (yabridge) - Robin Gareus (Ardour) - Thomas Binek (tas, u-he) - Timo Kaluza - Tor-Helge Skei (MIP2) - Urs Heckmann (u-he) - Vadim Zavalishin - William Light (LHI Audio) qtractor-1.5.9/src/clap/PaxHeaders/src0000644000000000000000000000013215101070323014653 xustar0030 mtime=1761898707.886986801 30 atime=1761898707.886701151 30 ctime=1761898707.886986801 qtractor-1.5.9/src/clap/src/0000755000175000001440000000000015101070323014720 5ustar00rncbcusersqtractor-1.5.9/src/clap/src/PaxHeaders/main.c0000644000000000000000000000013215101070323016020 xustar0030 mtime=1761898707.886986801 30 atime=1761898707.886986801 30 ctime=1761898707.886986801 qtractor-1.5.9/src/clap/src/main.c0000644000175000001440000000035615101070323016014 0ustar00rncbcusers#include // The purpose of this file is to check that all headers compile int main(int argc, char **argv) { (void)argc; (void)argv; const clap_version_t m = CLAP_VERSION; return !clap_version_is_compatible(m); } qtractor-1.5.9/src/clap/src/PaxHeaders/main.cc0000644000000000000000000000013215101070323016163 xustar0030 mtime=1761898707.886986801 30 atime=1761898707.886986801 30 ctime=1761898707.886986801 qtractor-1.5.9/src/clap/src/main.cc0000644000175000001440000000453215101070323016157 0ustar00rncbcusers#include // The purpose of this file is to check that all headers compile #if CLAP_VERSION_LT(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION) #error CLAP_VERSION_LT is inconsistent #endif #if !CLAP_VERSION_EQ(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION) #error CLAP_VERSION_EQ is inconsistent #endif #if CLAP_VERSION_EQ(CLAP_VERSION_MAJOR + 1, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION) #error CLAP_VERSION_EQ is inconsistent (MAJOR) #endif #if CLAP_VERSION_EQ(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR + 1, CLAP_VERSION_REVISION) #error CLAP_VERSION_EQ is inconsistent (MINOR) #endif #if CLAP_VERSION_EQ(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION + 1) #error CLAP_VERSION_EQ is inconsistent (REVISION) #endif #if !CLAP_VERSION_GE(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION) #error CLAP_VERSION_GE is inconsistent #endif #if CLAP_VERSION_LT(1,1,5) #error CLAP_VERSION_GE was inroduced in 1.1.5 so we should be later than that version. #endif #if !CLAP_VERSION_LT(CLAP_VERSION_MAJOR + 1, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION) #error CLAP_VERSION_LT is inconsistent (MAJOR) #endif #if !CLAP_VERSION_LT(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR + 1, CLAP_VERSION_REVISION) #error CLAP_VERSION_LT is inconsistent (MINOR) #endif #if !CLAP_VERSION_LT(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION + 1) #error CLAP_VERSION_LT is inconsistent (REVISION) #endif #if CLAP_VERSION_LT(CLAP_VERSION_MAJOR - 1, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION) #error CLAP_VERSION_LT is inconsistent (MAJOR) #endif #if CLAP_VERSION_LT(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR - 1, CLAP_VERSION_REVISION) #error CLAP_VERSION_LT is inconsistent (MINOR) #endif #if CLAP_VERSION_LT(CLAP_VERSION_MAJOR, CLAP_VERSION_MINOR, CLAP_VERSION_REVISION - 1) #error CLAP_VERSION_LT is inconsistent (REVISION) #endif #if (CLAP_COMPILE_TEST_CXX_VERSION >= 11) && ! defined(CLAP_HAS_CXX11) #error CLAP_HAS_CXX11 is not defined correctly #endif #if (CLAP_COMPILE_TEST_CXX_VERSION >= 17) && ! defined(CLAP_HAS_CXX17) #error CLAP_HAS_CXX17 is not defined correctly #endif #if (CLAP_COMPILE_TEST_CXX_VERSION >= 20) && ! defined(CLAP_HAS_CXX20) #error CLAP_HAS_CXX20 is not defined correctly #endif static const CLAP_CONSTEXPR clap_version m = CLAP_VERSION; int main(int, char **) { return !clap_version_is_compatible(m); } qtractor-1.5.9/src/clap/src/PaxHeaders/linux-my_plug.version0000644000000000000000000000013215101070323021150 xustar0030 mtime=1761898707.886701151 30 atime=1761898707.886701151 30 ctime=1761898707.886701151 qtractor-1.5.9/src/clap/src/linux-my_plug.version0000644000175000001440000000007715101070323021144 0ustar00rncbcusersMY_PLUG_ABI_1.0 { global: clap_entry; local: *; }; qtractor-1.5.9/src/clap/src/PaxHeaders/plugins.plist.in0000644000000000000000000000013215101070323020073 xustar0030 mtime=1761898707.886986801 30 atime=1761898707.886986801 30 ctime=1761898707.886986801 qtractor-1.5.9/src/clap/src/plugins.plist.in0000644000175000001440000000227615101070323020072 0ustar00rncbcusers CFBundleDevelopmentRegion English CFBundleExecutable ${MACOSX_BUNDLE_EXECUTABLE_NAME} CFBundleGetInfoString ${MACOSX_BUNDLE_INFO_STRING} CFBundleIconFile ${MACOSX_BUNDLE_ICON_FILE} CFBundleIdentifier ${MACOSX_BUNDLE_GUI_IDENTIFIER} CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString ${MACOSX_BUNDLE_LONG_VERSION_STRING} CFBundleName ${MACOSX_BUNDLE_BUNDLE_NAME} CFBundlePackageType BNDL CFBundleShortVersionString ${MACOSX_BUNDLE_SHORT_VERSION_STRING} CFBundleSignature ???? CFBundleVersion ${MACOSX_BUNDLE_BUNDLE_VERSION} CSResourcesFileMapped NSHumanReadableCopyright ${MACOSX_BUNDLE_COPYRIGHT} qtractor-1.5.9/src/clap/src/PaxHeaders/macos-symbols.txt0000644000000000000000000000013215101070323020261 xustar0030 mtime=1761898707.886986801 30 atime=1761898707.886701151 30 ctime=1761898707.886986801 qtractor-1.5.9/src/clap/src/macos-symbols.txt0000644000175000001440000000001415101070323020244 0ustar00rncbcusers_clap_entry qtractor-1.5.9/src/clap/src/PaxHeaders/plugin-template.c0000644000000000000000000000013215101070323020203 xustar0030 mtime=1761898707.886986801 30 atime=1761898707.886986801 30 ctime=1761898707.886986801 qtractor-1.5.9/src/clap/src/plugin-template.c0000644000175000001440000003266715101070323020211 0ustar00rncbcusers// This file is here to demonstrate how to wire a CLAP plugin // You can use it as a starting point, however if you are implementing a C++ // plugin, I'd encourage you to use the C++ glue layer instead: // https://github.com/free-audio/clap-helpers/blob/main/include/clap/helpers/plugin.hh #include #include #include #include #if __STDC_VERSION__ >= 201112L && !defined (__STDC_NO_THREADS__) && defined (CLAP_HAS_THREADS_H) # define CLAP_HAS_THREAD # include #endif #include static const clap_plugin_descriptor_t s_my_plug_desc = { .clap_version = CLAP_VERSION_INIT, .id = "com.your-company.YourPlugin", .name = "Plugin Name", .vendor = "Vendor", .url = "https://your-domain.com/your-plugin", .manual_url = "https://your-domain.com/your-plugin/manual", .support_url = "https://your-domain.com/support", .version = "1.4.2", .description = "The plugin description.", .features = (const char *[]){CLAP_PLUGIN_FEATURE_INSTRUMENT, CLAP_PLUGIN_FEATURE_STEREO, NULL}, }; typedef struct { clap_plugin_t plugin; const clap_host_t *host; const clap_host_latency_t *host_latency; const clap_host_log_t *host_log; const clap_host_thread_check_t *host_thread_check; const clap_host_state_t *host_state; uint32_t latency; } my_plug_t; ///////////////////////////// // clap_plugin_audio_ports // ///////////////////////////// static uint32_t my_plug_audio_ports_count(const clap_plugin_t *plugin, bool is_input) { // We just declare 1 audio input and 1 audio output return 1; } static bool my_plug_audio_ports_get(const clap_plugin_t *plugin, uint32_t index, bool is_input, clap_audio_port_info_t *info) { if (index > 0) return false; info->id = 0; snprintf(info->name, sizeof(info->name), "%s", "My Port Name"); info->channel_count = 2; info->flags = CLAP_AUDIO_PORT_IS_MAIN; info->port_type = CLAP_PORT_STEREO; info->in_place_pair = CLAP_INVALID_ID; return true; } static const clap_plugin_audio_ports_t s_my_plug_audio_ports = { .count = my_plug_audio_ports_count, .get = my_plug_audio_ports_get, }; //////////////////////////// // clap_plugin_note_ports // //////////////////////////// static uint32_t my_plug_note_ports_count(const clap_plugin_t *plugin, bool is_input) { // We just declare 1 note input return 1; } static bool my_plug_note_ports_get(const clap_plugin_t *plugin, uint32_t index, bool is_input, clap_note_port_info_t *info) { if (index > 0) return false; info->id = 0; snprintf(info->name, sizeof(info->name), "%s", "My Port Name"); info->supported_dialects = CLAP_NOTE_DIALECT_CLAP | CLAP_NOTE_DIALECT_MIDI_MPE | CLAP_NOTE_DIALECT_MIDI2; info->preferred_dialect = CLAP_NOTE_DIALECT_CLAP; return true; } static const clap_plugin_note_ports_t s_my_plug_note_ports = { .count = my_plug_note_ports_count, .get = my_plug_note_ports_get, }; ////////////////// // clap_latency // ////////////////// uint32_t my_plug_latency_get(const clap_plugin_t *plugin) { my_plug_t *plug = plugin->plugin_data; return plug->latency; } static const clap_plugin_latency_t s_my_plug_latency = { .get = my_plug_latency_get, }; //////////////// // clap_state // //////////////// bool my_plug_state_save(const clap_plugin_t *plugin, const clap_ostream_t *stream) { my_plug_t *plug = plugin->plugin_data; // TODO: write the state into stream return true; } bool my_plug_state_load(const clap_plugin_t *plugin, const clap_istream_t *stream) { my_plug_t *plug = plugin->plugin_data; // TODO: read the state from stream return true; } static const clap_plugin_state_t s_my_plug_state = { .save = my_plug_state_save, .load = my_plug_state_load, }; ///////////////// // clap_plugin // ///////////////// static bool my_plug_init(const struct clap_plugin *plugin) { my_plug_t *plug = plugin->plugin_data; // Fetch host's extensions here // Make sure to check that the interface functions are not null pointers plug->host_log = (const clap_host_log_t *)plug->host->get_extension(plug->host, CLAP_EXT_LOG); plug->host_thread_check = (const clap_host_thread_check_t *)plug->host->get_extension(plug->host, CLAP_EXT_THREAD_CHECK); plug->host_latency = (const clap_host_latency_t *)plug->host->get_extension(plug->host, CLAP_EXT_LATENCY); plug->host_state = (const clap_host_state_t *)plug->host->get_extension(plug->host, CLAP_EXT_STATE); return true; } static void my_plug_destroy(const struct clap_plugin *plugin) { my_plug_t *plug = plugin->plugin_data; free(plug); } static bool my_plug_activate(const struct clap_plugin *plugin, double sample_rate, uint32_t min_frames_count, uint32_t max_frames_count) { return true; } static void my_plug_deactivate(const struct clap_plugin *plugin) {} static bool my_plug_start_processing(const struct clap_plugin *plugin) { return true; } static void my_plug_stop_processing(const struct clap_plugin *plugin) {} static void my_plug_reset(const struct clap_plugin *plugin) {} static void my_plug_process_event(my_plug_t *plug, const clap_event_header_t *hdr) { if (hdr->space_id == CLAP_CORE_EVENT_SPACE_ID) { switch (hdr->type) { case CLAP_EVENT_NOTE_ON: { const clap_event_note_t *ev = (const clap_event_note_t *)hdr; // TODO: handle note on break; } case CLAP_EVENT_NOTE_OFF: { const clap_event_note_t *ev = (const clap_event_note_t *)hdr; // TODO: handle note off break; } case CLAP_EVENT_NOTE_CHOKE: { const clap_event_note_t *ev = (const clap_event_note_t *)hdr; // TODO: handle note choke break; } case CLAP_EVENT_NOTE_EXPRESSION: { const clap_event_note_expression_t *ev = (const clap_event_note_expression_t *)hdr; // TODO: handle note expression break; } case CLAP_EVENT_PARAM_VALUE: { const clap_event_param_value_t *ev = (const clap_event_param_value_t *)hdr; // TODO: handle parameter change break; } case CLAP_EVENT_PARAM_MOD: { const clap_event_param_mod_t *ev = (const clap_event_param_mod_t *)hdr; // TODO: handle parameter modulation break; } case CLAP_EVENT_TRANSPORT: { const clap_event_transport_t *ev = (const clap_event_transport_t *)hdr; // TODO: handle transport event break; } case CLAP_EVENT_MIDI: { const clap_event_midi_t *ev = (const clap_event_midi_t *)hdr; // TODO: handle MIDI event break; } case CLAP_EVENT_MIDI_SYSEX: { const clap_event_midi_sysex_t *ev = (const clap_event_midi_sysex_t *)hdr; // TODO: handle MIDI Sysex event break; } case CLAP_EVENT_MIDI2: { const clap_event_midi2_t *ev = (const clap_event_midi2_t *)hdr; // TODO: handle MIDI2 event break; } } } } static clap_process_status my_plug_process(const struct clap_plugin *plugin, const clap_process_t *process) { my_plug_t *plug = plugin->plugin_data; const uint32_t nframes = process->frames_count; const uint32_t nev = process->in_events->size(process->in_events); uint32_t ev_index = 0; uint32_t next_ev_frame = nev > 0 ? 0 : nframes; for (uint32_t i = 0; i < nframes;) { /* handle every events that happrens at the frame "i" */ while (ev_index < nev && next_ev_frame == i) { const clap_event_header_t *hdr = process->in_events->get(process->in_events, ev_index); if (hdr->time != i) { next_ev_frame = hdr->time; break; } my_plug_process_event(plug, hdr); ++ev_index; if (ev_index == nev) { // we reached the end of the event list next_ev_frame = nframes; break; } } /* process every samples until the next event */ for (; i < next_ev_frame; ++i) { // fetch input samples const float in_l = process->audio_inputs[0].data32[0][i]; const float in_r = process->audio_inputs[0].data32[1][i]; /* TODO: process samples, here we simply swap left and right channels */ const float out_l = in_r; const float out_r = in_l; // store output samples process->audio_outputs[0].data32[0][i] = out_l; process->audio_outputs[0].data32[1][i] = out_r; } } return CLAP_PROCESS_CONTINUE; } static const void *my_plug_get_extension(const struct clap_plugin *plugin, const char *id) { if (!strcmp(id, CLAP_EXT_LATENCY)) return &s_my_plug_latency; if (!strcmp(id, CLAP_EXT_AUDIO_PORTS)) return &s_my_plug_audio_ports; if (!strcmp(id, CLAP_EXT_NOTE_PORTS)) return &s_my_plug_note_ports; if (!strcmp(id, CLAP_EXT_STATE)) return &s_my_plug_state; // TODO: add support to CLAP_EXT_PARAMS return NULL; } static void my_plug_on_main_thread(const struct clap_plugin *plugin) {} clap_plugin_t *my_plug_create(const clap_host_t *host) { my_plug_t *p = calloc(1, sizeof(*p)); p->host = host; p->plugin.desc = &s_my_plug_desc; p->plugin.plugin_data = p; p->plugin.init = my_plug_init; p->plugin.destroy = my_plug_destroy; p->plugin.activate = my_plug_activate; p->plugin.deactivate = my_plug_deactivate; p->plugin.start_processing = my_plug_start_processing; p->plugin.stop_processing = my_plug_stop_processing; p->plugin.reset = my_plug_reset; p->plugin.process = my_plug_process; p->plugin.get_extension = my_plug_get_extension; p->plugin.on_main_thread = my_plug_on_main_thread; // Don't call into the host here return &p->plugin; } ///////////////////////// // clap_plugin_factory // ///////////////////////// static struct { const clap_plugin_descriptor_t *desc; clap_plugin_t *(CLAP_ABI *create)(const clap_host_t *host); } s_plugins[] = { { .desc = &s_my_plug_desc, .create = my_plug_create, }, }; static uint32_t plugin_factory_get_plugin_count(const struct clap_plugin_factory *factory) { return sizeof(s_plugins) / sizeof(s_plugins[0]); } static const clap_plugin_descriptor_t * plugin_factory_get_plugin_descriptor(const struct clap_plugin_factory *factory, uint32_t index) { return s_plugins[index].desc; } static const clap_plugin_t *plugin_factory_create_plugin(const struct clap_plugin_factory *factory, const clap_host_t *host, const char *plugin_id) { if (!clap_version_is_compatible(host->clap_version)) { return NULL; } const int N = sizeof(s_plugins) / sizeof(s_plugins[0]); for (int i = 0; i < N; ++i) if (!strcmp(plugin_id, s_plugins[i].desc->id)) return s_plugins[i].create(host); return NULL; } static const clap_plugin_factory_t s_plugin_factory = { .get_plugin_count = plugin_factory_get_plugin_count, .get_plugin_descriptor = plugin_factory_get_plugin_descriptor, .create_plugin = plugin_factory_create_plugin, }; //////////////// // clap_entry // //////////////// static bool entry_init(const char *plugin_path) { // perform the plugin initialization return true; } static void entry_deinit(void) { // perform the plugin de-initialization } #ifdef CLAP_HAS_THREAD static mtx_t g_entry_lock; static once_flag g_entry_once = ONCE_FLAG_INIT; #endif static int g_entry_init_counter = 0; #ifdef CLAP_HAS_THREAD // Initializes the necessary mutex for the entry guard static void entry_init_guard_init(void) { mtx_init(&g_entry_lock, mtx_plain); } #endif // Thread safe init counter static bool entry_init_guard(const char *plugin_path) { #ifdef CLAP_HAS_THREAD call_once(&g_entry_once, entry_init_guard_init); mtx_lock(&g_entry_lock); #endif const int cnt = ++g_entry_init_counter; assert(cnt > 0); bool succeed = true; if (cnt == 1) { succeed = entry_init(plugin_path); if (!succeed) g_entry_init_counter = 0; } #ifdef CLAP_HAS_THREAD mtx_unlock(&g_entry_lock); #endif return succeed; } // Thread safe deinit counter static void entry_deinit_guard(void) { #ifdef CLAP_HAS_THREAD call_once(&g_entry_once, entry_init_guard_init); mtx_lock(&g_entry_lock); #endif const int cnt = --g_entry_init_counter; assert(cnt >= 0); bool succeed = true; if (cnt == 0) entry_deinit(); #ifdef CLAP_HAS_THREAD mtx_unlock(&g_entry_lock); #endif } static const void *entry_get_factory(const char *factory_id) { #ifdef CLAP_HAS_THREAD call_once(&g_entry_once, entry_init_guard_init); #endif assert(g_entry_init_counter > 0); if (g_entry_init_counter <= 0) return NULL; if (!strcmp(factory_id, CLAP_PLUGIN_FACTORY_ID)) return &s_plugin_factory; return NULL; } // This symbol will be resolved by the host CLAP_EXPORT const clap_plugin_entry_t clap_entry = { .clap_version = CLAP_VERSION_INIT, .init = entry_init_guard, .deinit = entry_deinit_guard, .get_factory = entry_get_factory, }; qtractor-1.5.9/src/clap/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215101070323016701 xustar0030 mtime=1761898707.845700915 30 atime=1761898707.845700915 30 ctime=1761898707.845700915 qtractor-1.5.9/src/clap/CMakeLists.txt0000644000175000001440000001236715101070323016702 0ustar00rncbcuserscmake_minimum_required(VERSION 3.17) enable_testing() # Extract the version from header file file(READ "include/clap/version.h" clap_version_header) string(REGEX MATCH "CLAP_VERSION_MAJOR ([0-9]+)" _ ${clap_version_header}) set(CLAP_VERSION_MAJOR ${CMAKE_MATCH_1}) string(REGEX MATCH "CLAP_VERSION_MINOR ([0-9]+)" _ ${clap_version_header}) set(CLAP_VERSION_MINOR ${CMAKE_MATCH_1}) string(REGEX MATCH "CLAP_VERSION_REVISION ([0-9]+)" _ ${clap_version_header}) set(CLAP_VERSION_REVISION ${CMAKE_MATCH_1}) message(STATUS "CLAP version: ${CLAP_VERSION_MAJOR}.${CLAP_VERSION_MINOR}.${CLAP_VERSION_REVISION}") project(CLAP LANGUAGES C CXX VERSION ${CLAP_VERSION_MAJOR}.${CLAP_VERSION_MINOR}.${CLAP_VERSION_REVISION}) option(CLAP_BUILD_TESTS "Should CLAP build tests and the like?" OFF) # If you use clap as a submodule of your plugin you need some interface projects # to allow you to link add_library(clap INTERFACE) target_include_directories(clap INTERFACE $ $) # `clap-core` is deprecated, please `clap` instead. add_library(clap-core ALIAS clap) include(GNUInstallDirs) install(DIRECTORY "include/clap" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") install(TARGETS clap EXPORT clap INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") install(EXPORT clap DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/clap" FILE "clap-config.cmake") include(CMakePackageConfigHelpers) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/clap-config-version.cmake" VERSION "${PROJECT_VERSION}" COMPATIBILITY AnyNewerVersion) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/clap-config-version.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/clap") # In addition to the above generated `clap-config.cmake` file, we'll also # provide a pkg-config file to make it easier to consume this library in a # portable way configure_file(clap.pc.in "${CMAKE_CURRENT_BINARY_DIR}/clap.pc" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/clap.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") # clap-tests should always be available, to avoid build failing here and there # because the target doesn't exists add_custom_target(clap-tests) if (${CLAP_BUILD_TESTS}) message(STATUS "Including CLAP tests, compile tests, and versions") include(CheckIncludeFile) macro(clap_compile_cpp SUFFIX EXT STDC STDCPP) add_executable(clap-compile-${SUFFIX} EXCLUDE_FROM_ALL src/main.${EXT}) target_link_libraries(clap-compile-${SUFFIX} clap) set_target_properties(clap-compile-${SUFFIX} PROPERTIES C_STANDARD ${STDC} CXX_STANDARD ${STDCPP}) add_test(NAME test-clap-compile-${SUFFIX} COMMAND clap-compile-${SUFFIX}) add_dependencies(clap-tests clap-compile-${SUFFIX}) if (${EXT} STREQUAL "cc") target_compile_definitions(clap-compile-${SUFFIX} PRIVATE CLAP_COMPILE_TEST_CXX_VERSION=${STDCPP}) endif() if (${CMAKE_C_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_C_COMPILER_ID} STREQUAL "Clang") target_compile_options(clap-compile-${SUFFIX} PRIVATE -Wall -Wextra -pedantic) endif() if (${CMAKE_C_COMPILER_ID} STREQUAL "Clang") target_compile_options(clap-compile-${SUFFIX} PRIVATE -Werror=pragma-pack) endif() endmacro() clap_compile_cpp(c11 c 11 11) clap_compile_cpp(cpp11 cc 11 11) clap_compile_cpp(cpp14 cc 11 14) if(${CMAKE_VERSION} VERSION_LESS "3.21") message(STATUS "Skipping C17 tests due to older CMAKE_VERSION ${CMAKE_VERSION}") else() clap_compile_cpp(c17 c 17 17) endif() clap_compile_cpp(cpp17 cc 17 17) clap_compile_cpp(cpp20 cc 17 20) check_include_file(threads.h CLAP_HAS_THREADS_H) add_library(clap-plugin-template MODULE EXCLUDE_FROM_ALL src/plugin-template.c) target_link_libraries(clap-plugin-template PRIVATE clap) set_target_properties(clap-plugin-template PROPERTIES C_STANDARD 11) add_dependencies(clap-tests clap-plugin-template) if(CLAP_HAS_THREADS_H) target_compile_definitions(clap-plugin-template PRIVATE CLAP_HAS_THREADS_H) endif() if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(clap-plugin-template PRIVATE -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/src/linux-my_plug.version) target_link_libraries(clap-plugin-template PRIVATE -Wl,-z,defs) set_target_properties(clap-plugin-template PROPERTIES SUFFIX ".clap" PREFIX "") elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") target_link_options(clap-plugin-template PRIVATE -exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/src/macos-symbols.txt) set_target_properties(clap-plugin-template PROPERTIES BUNDLE True BUNDLE_EXTENSION clap MACOSX_BUNDLE_GUI_IDENTIFIER com.my_company.my_plug MACOSX_BUNDLE_BUNDLE_NAME my_plug MACOSX_BUNDLE_BUNDLE_VERSION "1" MACOSX_BUNDLE_SHORT_VERSION_STRING "1" MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/src/plugins.plist.in ) elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") set_target_properties(clap-plugin-template PROPERTIES SUFFIX ".clap" PREFIX "") endif() endif() qtractor-1.5.9/src/clap/PaxHeaders/include0000644000000000000000000000013215101070323015507 xustar0030 mtime=1761898707.883314574 30 atime=1761898707.882314571 30 ctime=1761898707.883314574 qtractor-1.5.9/src/clap/include/0000755000175000001440000000000015101070323015554 5ustar00rncbcusersqtractor-1.5.9/src/clap/include/PaxHeaders/clap0000644000000000000000000000013215101070323016426 xustar0030 mtime=1761898707.886701151 30 atime=1761898707.883314574 30 ctime=1761898707.886701151 qtractor-1.5.9/src/clap/include/clap/0000755000175000001440000000000015101070323016473 5ustar00rncbcusersqtractor-1.5.9/src/clap/include/clap/PaxHeaders/timestamp.h0000644000000000000000000000013215101070323020657 xustar0030 mtime=1761898707.886701151 30 atime=1761898707.886701151 30 ctime=1761898707.886701151 qtractor-1.5.9/src/clap/include/clap/timestamp.h0000644000175000001440000000047115101070323020651 0ustar00rncbcusers#pragma once #include "private/std.h" #include "private/macros.h" // This type defines a timestamp: the number of seconds since UNIX EPOCH. // See C's time_t time(time_t *). typedef uint64_t clap_timestamp; // Value for unknown timestamp. static const CLAP_CONSTEXPR clap_timestamp CLAP_TIMESTAMP_UNKNOWN = 0; qtractor-1.5.9/src/clap/include/clap/PaxHeaders/clap.h0000644000000000000000000000013215101070323017573 xustar0030 mtime=1761898707.883940074 30 atime=1761898707.883940074 30 ctime=1761898707.883940074 qtractor-1.5.9/src/clap/include/clap/clap.h0000644000175000001440000000424515101070323017570 0ustar00rncbcusers/* * CLAP - CLever Audio Plugin * ~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Copyright (c) 2014...2022 Alexandre BIQUE * * 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. */ #pragma once #include "entry.h" #include "factory/plugin-factory.h" #include "factory/preset-discovery.h" #include "plugin.h" #include "plugin-features.h" #include "host.h" #include "universal-plugin-id.h" #include "ext/ambisonic.h" #include "ext/audio-ports-activation.h" #include "ext/audio-ports-config.h" #include "ext/audio-ports.h" #include "ext/configurable-audio-ports.h" #include "ext/context-menu.h" #include "ext/event-registry.h" #include "ext/gui.h" #include "ext/latency.h" #include "ext/log.h" #include "ext/note-name.h" #include "ext/note-ports.h" #include "ext/param-indication.h" #include "ext/params.h" #include "ext/posix-fd-support.h" #include "ext/preset-load.h" #include "ext/remote-controls.h" #include "ext/render.h" #include "ext/state-context.h" #include "ext/state.h" #include "ext/surround.h" #include "ext/tail.h" #include "ext/thread-check.h" #include "ext/thread-pool.h" #include "ext/timer-support.h" #include "ext/track-info.h" #include "ext/voice-info.h" qtractor-1.5.9/src/clap/include/clap/PaxHeaders/entry.h0000644000000000000000000000013215101070323020015 xustar0030 mtime=1761898707.883940074 30 atime=1761898707.883940074 30 ctime=1761898707.883940074 qtractor-1.5.9/src/clap/include/clap/entry.h0000644000175000001440000001451415101070323020012 0ustar00rncbcusers#pragma once #include "version.h" #include "private/macros.h" #ifdef __cplusplus extern "C" { #endif // This interface is the entry point of the dynamic library. // // CLAP plugins standard search path: // // Linux // - ~/.clap // - /usr/lib/clap // // Windows // - %COMMONPROGRAMFILES%\CLAP // - %LOCALAPPDATA%\Programs\Common\CLAP // // MacOS // - /Library/Audio/Plug-Ins/CLAP // - ~/Library/Audio/Plug-Ins/CLAP // // In addition to the OS-specific default locations above, a CLAP host must query the environment // for a CLAP_PATH variable, which is a list of directories formatted in the same manner as the host // OS binary search path (PATH on Unix, separated by `:` and Path on Windows, separated by ';', as // of this writing). // // Each directory should be recursively searched for files and/or bundles as appropriate in your OS // ending with the extension `.clap`. // // init and deinit in most cases are called once, in a matched pair, when the dso is loaded / unloaded. // In some rare situations it may be called multiple times in a process, so the functions must be defensive, // mutex locking and counting calls if undertaking non trivial non idempotent actions. // // Rationale: // // The intent of the init() and deinit() functions is to provide a "normal" initialization patterh // which occurs when the shared object is loaded or unloaded. As such, hosts will call each once and // in matched pairs. In CLAP specifications prior to 1.2.0, this single-call was documented as a // requirement. // // We realized, though, that this is not a requirement hosts can meet. If hosts load a plugin // which itself wraps another CLAP for instance, while also loading that same clap in its memory // space, both the host and the wrapper will call init() and deinit() and have no means to communicate // the state. // // With CLAP 1.2.0 and beyond we are changing the spec to indicate that a host should make an // absolute best effort to call init() and deinit() once, and always in matched pairs (for every // init() which returns true, one deinit() should be called). // // This takes the de-facto burden on plugin writers to deal with multiple calls into a hard requirement. // // Most init() / deinit() pairs we have seen are the relatively trivial {return true;} and {}. But // if your init() function does non-trivial one time work, the plugin author must maintain a counter // and must manage a mutex lock. The most obvious implementation will maintain a static counter and a // global mutex, increment the counter on each init, decrement it on each deinit, and only undertake // the init or deinit action when the counter is zero. typedef struct clap_plugin_entry { clap_version_t clap_version; // initialized to CLAP_VERSION // Initializes the DSO. // // This function must be called first, before any-other CLAP-related function or symbol from this // DSO. // // It also must only be called once, until a later call to deinit() is made, after which init() // can be called once more to re-initialize the DSO. // This enables hosts to e.g. quickly load and unload a DSO for scanning its plugins, and then // load it again later to actually use the plugins if needed. // // As stated above, even though hosts are forbidden to do so directly, multiple calls before any // deinit() call may still happen. Implementations *should* take this into account, and *must* // do so as of CLAP 1.2.0. // // It should be as fast as possible, in order to perform a very quick scan of the plugin // descriptors. // // It is forbidden to display graphical user interfaces in this call. // It is forbidden to perform any user interaction in this call. // // If the initialization depends upon expensive computation, maybe try to do them ahead of time // and cache the result. // // Returns true on success. If init() returns false, then the DSO must be considered // uninitialized, and the host must not call deinit() nor any other CLAP-related symbols from the // DSO. // This function also returns true in the case where the DSO is already initialized, and no // actual initialization work is done in this call, as explain above. // // plugin_path is the path to the DSO (Linux, Windows), or the bundle (macOS). // // This function may be called on any thread, including a different one from the one a later call // to deinit() (or a later init()) can be made. // However, it is forbidden to call this function simultaneously from multiple threads. // It is also forbidden to call it simultaneously with *any* other CLAP-related symbols from the // DSO, including (but not limited to) deinit(). bool(CLAP_ABI *init)(const char *plugin_path); // De-initializes the DSO, freeing any resources allocated or initialized by init(). // // After this function is called, no more calls into the DSO must be made, except calling init() // again to re-initialize the DSO. // This means that after deinit() is called, the DSO can be considered to be in the same state // as if init() was never called at all yet, enabling it to be re-initialized as needed. // // As stated above, even though hosts are forbidden to do so directly, multiple calls before any // new init() call may still happen. Implementations *should* take this into account, and *must* // do so as of CLAP 1.2.0. // // Just like init(), this function may be called on any thread, including a different one from // the one init() was called from, or from the one a later init() call can be made. // However, it is forbidden to call this function simultaneously from multiple threads. // It is also forbidden to call it simultaneously with *any* other CLAP-related symbols from the // DSO, including (but not limited to) deinit(). void(CLAP_ABI *deinit)(void); // Get the pointer to a factory. See factory/plugin-factory.h for an example. // // Returns null if the factory is not provided. // The returned pointer must *not* be freed by the caller. // // Unlike init() and deinit(), this function can be called simultaneously by multiple threads. // // [thread-safe] const void *(CLAP_ABI *get_factory)(const char *factory_id); } clap_plugin_entry_t; /* Entry point */ CLAP_EXPORT extern const clap_plugin_entry_t clap_entry; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/PaxHeaders/version.h0000644000000000000000000000013215101070323020341 xustar0030 mtime=1761898707.886701151 30 atime=1761898707.886701151 30 ctime=1761898707.886701151 qtractor-1.5.9/src/clap/include/clap/version.h0000644000175000001440000000274115101070323020335 0ustar00rncbcusers#pragma once #include "private/macros.h" #include "private/std.h" #ifdef __cplusplus extern "C" { #endif typedef struct clap_version { // This is the major ABI and API design // Version 0.X.Y correspond to the development stage, API and ABI are not stable // Version 1.X.Y correspond to the release stage, API and ABI are stable uint32_t major; uint32_t minor; uint32_t revision; } clap_version_t; #ifdef __cplusplus } #endif #define CLAP_VERSION_MAJOR 1 #define CLAP_VERSION_MINOR 2 #define CLAP_VERSION_REVISION 6 #define CLAP_VERSION_INIT \ { (uint32_t)CLAP_VERSION_MAJOR, (uint32_t)CLAP_VERSION_MINOR, (uint32_t)CLAP_VERSION_REVISION } #define CLAP_VERSION_LT(maj,min,rev) ((CLAP_VERSION_MAJOR < (maj)) || \ ((maj) == CLAP_VERSION_MAJOR && CLAP_VERSION_MINOR < (min)) || \ ((maj) == CLAP_VERSION_MAJOR && (min) == CLAP_VERSION_MINOR && CLAP_VERSION_REVISION < (rev))) #define CLAP_VERSION_EQ(maj,min,rev) (((maj) == CLAP_VERSION_MAJOR) && ((min) == CLAP_VERSION_MINOR) && ((rev) == CLAP_VERSION_REVISION)) #define CLAP_VERSION_GE(maj,min,rev) (!CLAP_VERSION_LT(maj,min,rev)) static const CLAP_CONSTEXPR clap_version_t CLAP_VERSION = CLAP_VERSION_INIT; CLAP_NODISCARD static inline CLAP_CONSTEXPR bool clap_version_is_compatible(const clap_version_t v) { // versions 0.x.y were used during development stage and aren't compatible return v.major >= 1; } qtractor-1.5.9/src/clap/include/clap/PaxHeaders/string-sizes.h0000644000000000000000000000013215101070323021315 xustar0030 mtime=1761898707.886701151 30 atime=1761898707.886701151 30 ctime=1761898707.886701151 qtractor-1.5.9/src/clap/include/clap/string-sizes.h0000644000175000001440000000071515101070323021310 0ustar00rncbcusers#pragma once #ifdef __cplusplus extern "C" { #endif enum { // String capacity for names that can be displayed to the user. CLAP_NAME_SIZE = 256, // String capacity for describing a path, like a parameter in a module hierarchy or path within a // set of nested track groups. // // This is not suited for describing a file path on the disk, as NTFS allows up to 32K long // paths. CLAP_PATH_SIZE = 1024, }; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/PaxHeaders/private0000644000000000000000000000013115101070323020077 xustar0030 mtime=1761898707.886701151 29 atime=1761898707.88625123 30 ctime=1761898707.886701151 qtractor-1.5.9/src/clap/include/clap/private/0000755000175000001440000000000015101070323020145 5ustar00rncbcusersqtractor-1.5.9/src/clap/include/clap/private/PaxHeaders/std.h0000644000000000000000000000013115101070323021117 xustar0030 mtime=1761898707.886701151 29 atime=1761898707.88625123 30 ctime=1761898707.886701151 qtractor-1.5.9/src/clap/include/clap/private/std.h0000644000175000001440000000033115101070323021105 0ustar00rncbcusers#pragma once #include "macros.h" #ifdef CLAP_HAS_CXX11 # include #else # include #endif #ifdef __cplusplus # include #else # include # include #endif qtractor-1.5.9/src/clap/include/clap/private/PaxHeaders/macros.h0000644000000000000000000000012715101070323021616 xustar0029 mtime=1761898707.88625123 29 atime=1761898707.88625123 29 ctime=1761898707.88625123 qtractor-1.5.9/src/clap/include/clap/private/macros.h0000644000175000001440000000217515101070323021607 0ustar00rncbcusers#pragma once // Define CLAP_EXPORT #if !defined(CLAP_EXPORT) # if defined _WIN32 || defined __CYGWIN__ # ifdef __GNUC__ # define CLAP_EXPORT __attribute__((dllexport)) # else # define CLAP_EXPORT __declspec(dllexport) # endif # else # if __GNUC__ >= 4 || defined(__clang__) # define CLAP_EXPORT __attribute__((visibility("default"))) # else # define CLAP_EXPORT # endif # endif #endif #if !defined(CLAP_ABI) # if defined _WIN32 || defined __CYGWIN__ # define CLAP_ABI __cdecl # else # define CLAP_ABI # endif #endif #if defined(_MSVC_LANG) # define CLAP_CPLUSPLUS _MSVC_LANG #elif defined(__cplusplus) # define CLAP_CPLUSPLUS __cplusplus #endif #if defined(CLAP_CPLUSPLUS) && CLAP_CPLUSPLUS >= 201103L # define CLAP_HAS_CXX11 # define CLAP_CONSTEXPR constexpr #else # define CLAP_CONSTEXPR #endif #if defined(CLAP_CPLUSPLUS) && CLAP_CPLUSPLUS >= 201703L # define CLAP_HAS_CXX17 # define CLAP_NODISCARD [[nodiscard]] #else # define CLAP_NODISCARD #endif #if defined(CLAP_CPLUSPLUS) && CLAP_CPLUSPLUS >= 202002L # define CLAP_HAS_CXX20 #endif qtractor-1.5.9/src/clap/include/clap/PaxHeaders/process.h0000644000000000000000000000013215101070323020332 xustar0030 mtime=1761898707.886701151 30 atime=1761898707.886701151 30 ctime=1761898707.886701151 qtractor-1.5.9/src/clap/include/clap/process.h0000644000175000001440000000422215101070323020322 0ustar00rncbcusers#pragma once #include "events.h" #include "audio-buffer.h" #ifdef __cplusplus extern "C" { #endif enum { // Processing failed. The output buffer must be discarded. CLAP_PROCESS_ERROR = 0, // Processing succeeded, keep processing. CLAP_PROCESS_CONTINUE = 1, // Processing succeeded, keep processing if the output is not quiet. CLAP_PROCESS_CONTINUE_IF_NOT_QUIET = 2, // Rely upon the plugin's tail to determine if the plugin should continue to process. // see clap_plugin_tail CLAP_PROCESS_TAIL = 3, // Processing succeeded, but no more processing is required, // until the next event or variation in audio input. CLAP_PROCESS_SLEEP = 4, }; typedef int32_t clap_process_status; typedef struct clap_process { // A steady sample time counter. // This field can be used to calculate the sleep duration between two process calls. // This value may be specific to this plugin instance and have no relation to what // other plugin instances may receive. // // Set to -1 if not available, otherwise the value must be greater or equal to 0, // and must be increased by at least `frames_count` for the next call to process. int64_t steady_time; // Number of frames to process uint32_t frames_count; // time info at sample 0 // If null, then this is a free running host, no transport events will be provided const clap_event_transport_t *transport; // Audio buffers, they must have the same count as specified // by clap_plugin_audio_ports->count(). // The index maps to clap_plugin_audio_ports->get(). // Input buffer and its contents are read-only. const clap_audio_buffer_t *audio_inputs; clap_audio_buffer_t *audio_outputs; uint32_t audio_inputs_count; uint32_t audio_outputs_count; // The input event list can't be modified. // Input read-only event list. The host will deliver these sorted in sample order. const clap_input_events_t *in_events; // Output event list. The plugin must insert events in sample sorted order when inserting events const clap_output_events_t *out_events; } clap_process_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/PaxHeaders/plugin-features.h0000644000000000000000000000012715101070323021772 xustar0029 mtime=1761898707.88625123 29 atime=1761898707.88625123 29 ctime=1761898707.88625123 qtractor-1.5.9/src/clap/include/clap/plugin-features.h0000644000175000001440000000550315101070323021761 0ustar00rncbcusers#pragma once // This file provides a set of standard plugin features meant to be used // within clap_plugin_descriptor.features. // // For practical reasons we'll avoid spaces and use `-` instead to facilitate // scripts that generate the feature array. // // Non-standard features should be formatted as follow: "$namespace:$feature" ///////////////////// // Plugin category // ///////////////////// // Add this feature if your plugin can process note events and then produce audio #define CLAP_PLUGIN_FEATURE_INSTRUMENT "instrument" // Add this feature if your plugin is an audio effect #define CLAP_PLUGIN_FEATURE_AUDIO_EFFECT "audio-effect" // Add this feature if your plugin is a note effect or a note generator/sequencer #define CLAP_PLUGIN_FEATURE_NOTE_EFFECT "note-effect" // Add this feature if your plugin converts audio to notes #define CLAP_PLUGIN_FEATURE_NOTE_DETECTOR "note-detector" // Add this feature if your plugin is an analyzer #define CLAP_PLUGIN_FEATURE_ANALYZER "analyzer" ///////////////////////// // Plugin sub-category // ///////////////////////// #define CLAP_PLUGIN_FEATURE_SYNTHESIZER "synthesizer" #define CLAP_PLUGIN_FEATURE_SAMPLER "sampler" #define CLAP_PLUGIN_FEATURE_DRUM "drum" // For single drum #define CLAP_PLUGIN_FEATURE_DRUM_MACHINE "drum-machine" #define CLAP_PLUGIN_FEATURE_FILTER "filter" #define CLAP_PLUGIN_FEATURE_PHASER "phaser" #define CLAP_PLUGIN_FEATURE_EQUALIZER "equalizer" #define CLAP_PLUGIN_FEATURE_DEESSER "de-esser" #define CLAP_PLUGIN_FEATURE_PHASE_VOCODER "phase-vocoder" #define CLAP_PLUGIN_FEATURE_GRANULAR "granular" #define CLAP_PLUGIN_FEATURE_FREQUENCY_SHIFTER "frequency-shifter" #define CLAP_PLUGIN_FEATURE_PITCH_SHIFTER "pitch-shifter" #define CLAP_PLUGIN_FEATURE_DISTORTION "distortion" #define CLAP_PLUGIN_FEATURE_TRANSIENT_SHAPER "transient-shaper" #define CLAP_PLUGIN_FEATURE_COMPRESSOR "compressor" #define CLAP_PLUGIN_FEATURE_EXPANDER "expander" #define CLAP_PLUGIN_FEATURE_GATE "gate" #define CLAP_PLUGIN_FEATURE_LIMITER "limiter" #define CLAP_PLUGIN_FEATURE_FLANGER "flanger" #define CLAP_PLUGIN_FEATURE_CHORUS "chorus" #define CLAP_PLUGIN_FEATURE_DELAY "delay" #define CLAP_PLUGIN_FEATURE_REVERB "reverb" #define CLAP_PLUGIN_FEATURE_TREMOLO "tremolo" #define CLAP_PLUGIN_FEATURE_GLITCH "glitch" #define CLAP_PLUGIN_FEATURE_UTILITY "utility" #define CLAP_PLUGIN_FEATURE_PITCH_CORRECTION "pitch-correction" #define CLAP_PLUGIN_FEATURE_RESTORATION "restoration" // repair the sound #define CLAP_PLUGIN_FEATURE_MULTI_EFFECTS "multi-effects" #define CLAP_PLUGIN_FEATURE_MIXING "mixing" #define CLAP_PLUGIN_FEATURE_MASTERING "mastering" //////////////////////// // Audio Capabilities // //////////////////////// #define CLAP_PLUGIN_FEATURE_MONO "mono" #define CLAP_PLUGIN_FEATURE_STEREO "stereo" #define CLAP_PLUGIN_FEATURE_SURROUND "surround" #define CLAP_PLUGIN_FEATURE_AMBISONIC "ambisonic" qtractor-1.5.9/src/clap/include/clap/PaxHeaders/plugin.h0000644000000000000000000000012715101070323020156 xustar0029 mtime=1761898707.88625123 29 atime=1761898707.88625123 29 ctime=1761898707.88625123 qtractor-1.5.9/src/clap/include/clap/plugin.h0000644000175000001440000001167115101070323020150 0ustar00rncbcusers#pragma once #include "private/macros.h" #include "host.h" #include "process.h" #include "plugin-features.h" #ifdef __cplusplus extern "C" { #endif typedef struct clap_plugin_descriptor { clap_version_t clap_version; // initialized to CLAP_VERSION // Mandatory fields must be set and must not be blank. // Otherwise the fields can be null or blank, though it is safer to make them blank. // // Some indications regarding id and version // - id is an arbitrary string which should be unique to your plugin, // we encourage you to use a reverse URI eg: "com.u-he.diva" // - version is an arbitrary string which describes a plugin, // it is useful for the host to understand and be able to compare two different // version strings, so here is a regex like expression which is likely to be // understood by most hosts: MAJOR(.MINOR(.REVISION)?)?( (Alpha|Beta) XREV)? const char *id; // eg: "com.u-he.diva", mandatory const char *name; // eg: "Diva", mandatory const char *vendor; // eg: "u-he" const char *url; // eg: "https://u-he.com/products/diva/" const char *manual_url; // eg: "https://dl.u-he.com/manuals/plugins/diva/Diva-user-guide.pdf" const char *support_url; // eg: "https://u-he.com/support/" const char *version; // eg: "1.4.4" const char *description; // eg: "The spirit of analogue" // Arbitrary list of keywords. // They can be matched by the host indexer and used to classify the plugin. // The array of pointers must be null terminated. // For some standard features see plugin-features.h const char *const *features; } clap_plugin_descriptor_t; typedef struct clap_plugin { const clap_plugin_descriptor_t *desc; void *plugin_data; // reserved pointer for the plugin // Must be called after creating the plugin. // If init returns false, the host must destroy the plugin instance. // If init returns true, then the plugin is initialized and in the deactivated state. // Unlike in `plugin-factory::create_plugin`, in init you have complete access to the host // and host extensions, so clap related setup activities should be done here rather than in // create_plugin. // [main-thread] bool(CLAP_ABI *init)(const struct clap_plugin *plugin); // Free the plugin and its resources. // It is required to deactivate the plugin prior to this call. // [main-thread & !active] void(CLAP_ABI *destroy)(const struct clap_plugin *plugin); // Activate and deactivate the plugin. // In this call the plugin may allocate memory and prepare everything needed for the process // call. The process's sample rate will be constant and process's frame count will included in // the [min, max] range, which is bounded by [1, INT32_MAX]. // In this call the plugin may call host-provided methods marked [being-activated]. // Once activated the latency and port configuration must remain constant, until deactivation. // Returns true on success. // [main-thread & !active] bool(CLAP_ABI *activate)(const struct clap_plugin *plugin, double sample_rate, uint32_t min_frames_count, uint32_t max_frames_count); // [main-thread & active] void(CLAP_ABI *deactivate)(const struct clap_plugin *plugin); // Call start processing before processing. // Returns true on success. // [audio-thread & active & !processing] bool(CLAP_ABI *start_processing)(const struct clap_plugin *plugin); // Call stop processing before sending the plugin to sleep. // [audio-thread & active & processing] void(CLAP_ABI *stop_processing)(const struct clap_plugin *plugin); // - Clears all buffers, performs a full reset of the processing state (filters, oscillators, // envelopes, lfo, ...) and kills all voices. // - The parameter's value remain unchanged. // - clap_process.steady_time may jump backward. // // [audio-thread & active] void(CLAP_ABI *reset)(const struct clap_plugin *plugin); // process audio, events, ... // All the pointers coming from clap_process_t and its nested attributes, // are valid until process() returns. // [audio-thread & active & processing] clap_process_status(CLAP_ABI *process)(const struct clap_plugin *plugin, const clap_process_t *process); // Query an extension. // The returned pointer is owned by the plugin. // It is forbidden to call it before plugin->init(). // You can call it within plugin->init() call, and after. // [thread-safe] const void *(CLAP_ABI *get_extension)(const struct clap_plugin *plugin, const char *id); // Called by the host on the main thread in response to a previous call to: // host->request_callback(host); // [main-thread] void(CLAP_ABI *on_main_thread)(const struct clap_plugin *plugin); } clap_plugin_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/PaxHeaders/fixedpoint.h0000644000000000000000000000012715101070323021031 xustar0029 mtime=1761898707.88625123 29 atime=1761898707.88625123 29 ctime=1761898707.88625123 qtractor-1.5.9/src/clap/include/clap/fixedpoint.h0000644000175000001440000000072215101070323021016 0ustar00rncbcusers#pragma once #include "private/std.h" #include "private/macros.h" /// We use fixed point representation of beat time and seconds time /// Usage: /// double x = ...; // in beats /// clap_beattime y = round(CLAP_BEATTIME_FACTOR * x); // This will never change static const CLAP_CONSTEXPR int64_t CLAP_BEATTIME_FACTOR = 1LL << 31; static const CLAP_CONSTEXPR int64_t CLAP_SECTIME_FACTOR = 1LL << 31; typedef int64_t clap_beattime; typedef int64_t clap_sectime; qtractor-1.5.9/src/clap/include/clap/PaxHeaders/events.h0000644000000000000000000000013215101070323020160 xustar0030 mtime=1761898707.883940074 30 atime=1761898707.883940074 30 ctime=1761898707.883940074 qtractor-1.5.9/src/clap/include/clap/events.h0000644000175000001440000003467115101070323020163 0ustar00rncbcusers#pragma once #include "private/std.h" #include "fixedpoint.h" #include "id.h" #ifdef __cplusplus extern "C" { #endif // event header // All clap events start with an event header to determine the overall // size of the event and its type and space (a namespacing for types). // clap_event objects are contiguous regions of memory which can be copied // with a memcpy of `size` bytes starting at the top of the header. As // such, be very careful when designing clap events with internal pointers // and other non-value-types to consider the lifetime of those members. typedef struct clap_event_header { uint32_t size; // event size including this header, eg: sizeof (clap_event_note) uint32_t time; // sample offset within the buffer for this event uint16_t space_id; // event space, see clap_host_event_registry uint16_t type; // event type uint32_t flags; // see clap_event_flags } clap_event_header_t; // The clap core event space static const CLAP_CONSTEXPR uint16_t CLAP_CORE_EVENT_SPACE_ID = 0; enum clap_event_flags { // Indicate a live user event, for example a user turning a physical knob // or playing a physical key. CLAP_EVENT_IS_LIVE = 1 << 0, // Indicate that the event should not be recorded. // For example this is useful when a parameter changes because of a MIDI CC, // because if the host records both the MIDI CC automation and the parameter // automation there will be a conflict. CLAP_EVENT_DONT_RECORD = 1 << 1, }; // Some of the following events overlap, a note on can be expressed with: // - CLAP_EVENT_NOTE_ON // - CLAP_EVENT_MIDI // - CLAP_EVENT_MIDI2 // // The preferred way of sending a note event is to use CLAP_EVENT_NOTE_*. // // The same event must not be sent twice: it is forbidden to send a the same note on // encoded with both CLAP_EVENT_NOTE_ON and CLAP_EVENT_MIDI. // // The plugins are encouraged to be able to handle note events encoded as raw midi or midi2, // or implement clap_plugin_event_filter and reject raw midi and midi2 events. enum { // NOTE_ON and NOTE_OFF represent a key pressed and key released event, respectively. // A NOTE_ON with a velocity of 0 is valid and should not be interpreted as a NOTE_OFF. // // NOTE_CHOKE is meant to choke the voice(s), like in a drum machine when a closed hihat // chokes an open hihat. This event can be sent by the host to the plugin. Here are two use // cases: // - a plugin is inside a drum pad in Bitwig Studio's drum machine, and this pad is choked by // another one // - the user double-clicks the DAW's stop button in the transport which then stops the sound on // every track // // NOTE_END is sent by the plugin to the host. The port, channel, key and note_id are those given // by the host in the NOTE_ON event. In other words, this event is matched against the // plugin's note input port. // NOTE_END is useful to help the host to match the plugin's voice life time. // // When using polyphonic modulations, the host has to allocate and release voices for its // polyphonic modulator. Yet only the plugin effectively knows when the host should terminate // a voice. NOTE_END solves that issue in a non-intrusive and cooperative way. // // CLAP assumes that the host will allocate a unique voice on NOTE_ON event for a given port, // channel and key. This voice will run until the plugin will instruct the host to terminate // it by sending a NOTE_END event. // // Consider the following sequence: // - process() // Host->Plugin NoteOn(port:0, channel:0, key:16, time:t0) // Host->Plugin NoteOn(port:0, channel:0, key:64, time:t0) // Host->Plugin NoteOff(port:0, channel:0, key:16, t1) // Host->Plugin NoteOff(port:0, channel:0, key:64, t1) // # on t2, both notes did terminate // Host->Plugin NoteOn(port:0, channel:0, key:64, t3) // # Here the plugin finished processing all the frames and will tell the host // # to terminate the voice on key 16 but not 64, because a note has been started at t3 // Plugin->Host NoteEnd(port:0, channel:0, key:16, time:ignored) // // These four events use clap_event_note. CLAP_EVENT_NOTE_ON = 0, CLAP_EVENT_NOTE_OFF = 1, CLAP_EVENT_NOTE_CHOKE = 2, CLAP_EVENT_NOTE_END = 3, // Represents a note expression. // Uses clap_event_note_expression. CLAP_EVENT_NOTE_EXPRESSION = 4, // PARAM_VALUE sets the parameter's value; uses clap_event_param_value. // PARAM_MOD sets the parameter's modulation amount; uses clap_event_param_mod. // // The value heard is: param_value + param_mod. // // In case of a concurrent global value/modulation versus a polyphonic one, // the voice should only use the polyphonic one and the polyphonic modulation // amount will already include the monophonic signal. CLAP_EVENT_PARAM_VALUE = 5, CLAP_EVENT_PARAM_MOD = 6, // Indicates that the user started or finished adjusting a knob. // This is not mandatory to wrap parameter changes with gesture events, but this improves // the user experience a lot when recording automation or overriding automation playback. // Uses clap_event_param_gesture. CLAP_EVENT_PARAM_GESTURE_BEGIN = 7, CLAP_EVENT_PARAM_GESTURE_END = 8, CLAP_EVENT_TRANSPORT = 9, // update the transport info; clap_event_transport CLAP_EVENT_MIDI = 10, // raw midi event; clap_event_midi CLAP_EVENT_MIDI_SYSEX = 11, // raw midi sysex event; clap_event_midi_sysex CLAP_EVENT_MIDI2 = 12, // raw midi 2 event; clap_event_midi2 }; // Note on, off, end and choke events. // // Clap addresses notes and voices using the 4-value tuple // (port, channel, key, note_id). Note on/off/end/choke // events and parameter modulation messages are delivered with // these values populated. // // Values in a note and voice address are either >= 0 if they // are specified, or -1 to indicate a wildcard. A wildcard // means a voice with any value in that part of the tuple // matches the message. // // For instance, a (PCKN) of (0, 3, -1, -1) will match all voices // on channel 3 of port 0. And a PCKN of (-1, 0, 60, -1) will match // all channel 0 key 60 voices, independent of port or note id. // // Especially in the case of note-on note-off pairs, and in the // absence of voice stacking or polyphonic modulation, a host may // choose to issue a note id only at note on. So you may see a // message stream like // // CLAP_EVENT_NOTE_ON [0,0,60,184] // CLAP_EVENT_NOTE_OFF [0,0,60,-1] // // and the host will expect the first voice to be released. // Well constructed plugins will search for voices and notes using // the entire tuple. // // In the case of note on events: // - The port, channel and key must be specified with a value >= 0 // - A note-on event with a '-1' for port, channel or key is invalid and // can be rejected or ignored by a plugin or host. // - A host which does not support note ids should set the note id to -1. // // In the case of note choke or end events: // - the velocity is ignored. // - key and channel are used to match active notes // - note_id is optionally provided by the host typedef struct clap_event_note { clap_event_header_t header; int32_t note_id; // host provided note id >= 0, or -1 if unspecified or wildcard int16_t port_index; // port index from ext/note-ports; -1 for wildcard int16_t channel; // 0..15, same as MIDI1 Channel Number, -1 for wildcard int16_t key; // 0..127, same as MIDI1 Key Number (60==Middle C), -1 for wildcard double velocity; // 0..1 } clap_event_note_t; // Note Expressions are well named modifications of a voice targeted to // voices using the same wildcard rules described above. Note Expressions are delivered // as sample accurate events and should be applied at the sample when received. // // Note expressions are a statement of value, not cumulative. A PAN event of 0 followed by 1 // followed by 0.5 would pan hard left, hard right, and center. They are intended as // an offset from the non-note-expression voice default. A voice which had a volume of // -20db absent note expressions which received a +4db note expression would move the // voice to -16db. // // A plugin which receives a note expression at the same sample as a NOTE_ON event // should apply that expression to all generated samples. A plugin which receives // a note expression after a NOTE_ON event should initiate the voice with default // values and then apply the note expression when received. A plugin may make a choice // to smooth note expression streams. enum { // with 0 < x <= 4, plain = 20 * log(x) CLAP_NOTE_EXPRESSION_VOLUME = 0, // pan, 0 left, 0.5 center, 1 right CLAP_NOTE_EXPRESSION_PAN = 1, // Relative tuning in semitones, from -120 to +120. Semitones are in // equal temperament and are doubles; the resulting note would be // retuned by `100 * evt->value` cents. CLAP_NOTE_EXPRESSION_TUNING = 2, // 0..1 CLAP_NOTE_EXPRESSION_VIBRATO = 3, CLAP_NOTE_EXPRESSION_EXPRESSION = 4, CLAP_NOTE_EXPRESSION_BRIGHTNESS = 5, CLAP_NOTE_EXPRESSION_PRESSURE = 6, }; typedef int32_t clap_note_expression; typedef struct clap_event_note_expression { clap_event_header_t header; clap_note_expression expression_id; // target a specific note_id, port, key and channel, with // -1 meaning wildcard, per the wildcard discussion above int32_t note_id; int16_t port_index; int16_t channel; int16_t key; double value; // see expression for the range } clap_event_note_expression_t; typedef struct clap_event_param_value { clap_event_header_t header; // target parameter clap_id param_id; // @ref clap_param_info.id void *cookie; // @ref clap_param_info.cookie // target a specific note_id, port, key and channel, with // -1 meaning wildcard, per the wildcard discussion above int32_t note_id; int16_t port_index; int16_t channel; int16_t key; double value; } clap_event_param_value_t; typedef struct clap_event_param_mod { clap_event_header_t header; // target parameter clap_id param_id; // @ref clap_param_info.id void *cookie; // @ref clap_param_info.cookie // target a specific note_id, port, key and channel, with // -1 meaning wildcard, per the wildcard discussion above int32_t note_id; int16_t port_index; int16_t channel; int16_t key; double amount; // modulation amount } clap_event_param_mod_t; typedef struct clap_event_param_gesture { clap_event_header_t header; // target parameter clap_id param_id; // @ref clap_param_info.id } clap_event_param_gesture_t; enum clap_transport_flags { CLAP_TRANSPORT_HAS_TEMPO = 1 << 0, CLAP_TRANSPORT_HAS_BEATS_TIMELINE = 1 << 1, CLAP_TRANSPORT_HAS_SECONDS_TIMELINE = 1 << 2, CLAP_TRANSPORT_HAS_TIME_SIGNATURE = 1 << 3, CLAP_TRANSPORT_IS_PLAYING = 1 << 4, CLAP_TRANSPORT_IS_RECORDING = 1 << 5, CLAP_TRANSPORT_IS_LOOP_ACTIVE = 1 << 6, CLAP_TRANSPORT_IS_WITHIN_PRE_ROLL = 1 << 7, }; // clap_event_transport provides song position, tempo, and similar information // from the host to the plugin. There are two ways a host communicates these values. // In the `clap_process` structure sent to each processing block, the host may // provide a transport structure which indicates the available information at the // start of the block. If the host provides sample-accurate tempo or transport changes, // it can also provide subsequent inter-block transport updates by delivering a new event. typedef struct clap_event_transport { clap_event_header_t header; uint32_t flags; // see clap_transport_flags clap_beattime song_pos_beats; // position in beats clap_sectime song_pos_seconds; // position in seconds double tempo; // in bpm double tempo_inc; // tempo increment for each sample and until the next // time info event clap_beattime loop_start_beats; clap_beattime loop_end_beats; clap_sectime loop_start_seconds; clap_sectime loop_end_seconds; clap_beattime bar_start; // start pos of the current bar int32_t bar_number; // bar at song pos 0 has the number 0 uint16_t tsig_num; // time signature numerator uint16_t tsig_denom; // time signature denominator } clap_event_transport_t; typedef struct clap_event_midi { clap_event_header_t header; uint16_t port_index; uint8_t data[3]; } clap_event_midi_t; // clap_event_midi_sysex contains a pointer to a sysex contents buffer. // The lifetime of this buffer is (from host->plugin) only the process // call in which the event is delivered or (from plugin->host) only the // duration of a try_push call. // // Since `clap_output_events.try_push` requires hosts to make a copy of // an event, host implementers receiving sysex messages from plugins need // to take care to both copy the event (so header, size, etc...) but // also memcpy the contents of the sysex pointer to host-owned memory, and // not just copy the data pointer. // // Similarly plugins retaining the sysex outside the lifetime of a single // process call must copy the sysex buffer to plugin-owned memory. // // As a consequence, the data structure pointed to by the sysex buffer // must be contiguous and copyable with `memcpy` of `size` bytes. typedef struct clap_event_midi_sysex { clap_event_header_t header; uint16_t port_index; const uint8_t *buffer; // midi buffer. See lifetime comment above. uint32_t size; } clap_event_midi_sysex_t; // While it is possible to use a series of midi2 event to send a sysex, // prefer clap_event_midi_sysex if possible for efficiency. typedef struct clap_event_midi2 { clap_event_header_t header; uint16_t port_index; uint32_t data[4]; } clap_event_midi2_t; // Input event list. The host will deliver these sorted in sample order. typedef struct clap_input_events { void *ctx; // reserved pointer for the list // returns the number of events in the list uint32_t(CLAP_ABI *size)(const struct clap_input_events *list); // Don't free the returned event, it belongs to the list const clap_event_header_t *(CLAP_ABI *get)(const struct clap_input_events *list, uint32_t index); } clap_input_events_t; // Output event list. The plugin must insert events in sample sorted order when inserting events typedef struct clap_output_events { void *ctx; // reserved pointer for the list // Pushes a copy of the event // returns false if the event could not be pushed to the queue (out of memory?) bool(CLAP_ABI *try_push)(const struct clap_output_events *list, const clap_event_header_t *event); } clap_output_events_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/PaxHeaders/ext0000644000000000000000000000013215101070323017226 xustar0030 mtime=1761898707.885314581 30 atime=1761898707.883940074 30 ctime=1761898707.885314581 qtractor-1.5.9/src/clap/include/clap/ext/0000755000175000001440000000000015101070323017273 5ustar00rncbcusersqtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/tail.h0000644000000000000000000000013215101070323020405 xustar0030 mtime=1761898707.885314581 30 atime=1761898707.885314581 30 ctime=1761898707.885314581 qtractor-1.5.9/src/clap/include/clap/ext/tail.h0000644000175000001440000000112715101070323020376 0ustar00rncbcusers#pragma once #include "../plugin.h" static CLAP_CONSTEXPR const char CLAP_EXT_TAIL[] = "clap.tail"; #ifdef __cplusplus extern "C" { #endif typedef struct clap_plugin_tail { // Returns tail length in samples. // Any value greater or equal to INT32_MAX implies infinite tail. // [main-thread,audio-thread] uint32_t(CLAP_ABI *get)(const clap_plugin_t *plugin); } clap_plugin_tail_t; typedef struct clap_host_tail { // Tell the host that the tail has changed. // [audio-thread] void(CLAP_ABI *changed)(const clap_host_t *host); } clap_host_tail_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/note-name.h0000644000000000000000000000013215101070323021337 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/note-name.h0000644000175000001440000000173015101070323021330 0ustar00rncbcusers#pragma once #include "../plugin.h" #include "../string-sizes.h" #ifdef __cplusplus extern "C" { #endif static CLAP_CONSTEXPR const char CLAP_EXT_NOTE_NAME[] = "clap.note-name"; typedef struct clap_note_name { char name[CLAP_NAME_SIZE]; int16_t port; // -1 for every port int16_t key; // -1 for every key int16_t channel; // -1 for every channel } clap_note_name_t; typedef struct clap_plugin_note_name { // Return the number of note names // [main-thread] uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin); // Returns true on success and stores the result into note_name // [main-thread] bool(CLAP_ABI *get)(const clap_plugin_t *plugin, uint32_t index, clap_note_name_t *note_name); } clap_plugin_note_name_t; typedef struct clap_host_note_name { // Informs the host that the note names have changed. // [main-thread] void(CLAP_ABI *changed)(const clap_host_t *host); } clap_host_note_name_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/surround.h0000644000000000000000000000013215101070323021335 xustar0030 mtime=1761898707.885314581 30 atime=1761898707.885314581 30 ctime=1761898707.885314581 qtractor-1.5.9/src/clap/include/clap/ext/surround.h0000644000175000001440000000724115101070323021331 0ustar00rncbcusers#pragma once #include "../plugin.h" // This extension can be used to specify the channel mapping used by the plugin. // // To have consistent surround features across all the plugin instances, // here is the proposed workflow: // 1. the plugin queries the host preferred channel mapping and // adjusts its configuration to match it. // 2. the host checks how the plugin is effectively configured and honors it. // // If the host decides to change the project's surround setup: // 1. deactivate the plugin // 2. host calls clap_plugin_surround->changed() // 3. plugin calls clap_host_surround->get_preferred_channel_map() // 4. plugin eventually calls clap_host_surround->changed() // 5. host calls clap_plugin_surround->get_channel_map() if changed // 6. host activates the plugin and can start processing audio // // If the plugin wants to change its surround setup: // 1. call host->request_restart() if the plugin is active // 2. once deactivated plugin calls clap_host_surround->changed() // 3. host calls clap_plugin_surround->get_channel_map() // 4. host activates the plugin and can start processing audio static CLAP_CONSTEXPR const char CLAP_EXT_SURROUND[] = "clap.surround/4"; // The latest draft is 100% compatible. // This compat ID may be removed in 2026. static CLAP_CONSTEXPR const char CLAP_EXT_SURROUND_COMPAT[] = "clap.surround.draft/4"; static CLAP_CONSTEXPR const char CLAP_PORT_SURROUND[] = "surround"; #ifdef __cplusplus extern "C" { #endif enum { CLAP_SURROUND_FL = 0, // Front Left CLAP_SURROUND_FR = 1, // Front Right CLAP_SURROUND_FC = 2, // Front Center CLAP_SURROUND_LFE = 3, // Low Frequency CLAP_SURROUND_BL = 4, // Back (Rear) Left CLAP_SURROUND_BR = 5, // Back (Rear) Right CLAP_SURROUND_FLC = 6, // Front Left of Center CLAP_SURROUND_FRC = 7, // Front Right of Center CLAP_SURROUND_BC = 8, // Back (Rear) Center CLAP_SURROUND_SL = 9, // Side Left CLAP_SURROUND_SR = 10, // Side Right CLAP_SURROUND_TC = 11, // Top (Height) Center CLAP_SURROUND_TFL = 12, // Top (Height) Front Left CLAP_SURROUND_TFC = 13, // Top (Height) Front Center CLAP_SURROUND_TFR = 14, // Top (Height) Front Right CLAP_SURROUND_TBL = 15, // Top (Height) Back (Rear) Left CLAP_SURROUND_TBC = 16, // Top (Height) Back (Rear) Center CLAP_SURROUND_TBR = 17, // Top (Height) Back (Rear) Right CLAP_SURROUND_TSL = 18, // Top (Height) Side Left CLAP_SURROUND_TSR = 19, // Top (Height) Side Right }; typedef struct clap_plugin_surround { // Checks if a given channel mask is supported. // The channel mask is a bitmask, for example: // (1 << CLAP_SURROUND_FL) | (1 << CLAP_SURROUND_FR) | ... // [main-thread] bool(CLAP_ABI *is_channel_mask_supported)(const clap_plugin_t *plugin, uint64_t channel_mask); // Stores the surround identifier of each channel into the channel_map array. // Returns the number of elements stored in channel_map. // channel_map_capacity must be greater or equal to the channel count of the given port. // [main-thread] uint32_t(CLAP_ABI *get_channel_map)(const clap_plugin_t *plugin, bool is_input, uint32_t port_index, uint8_t *channel_map, uint32_t channel_map_capacity); } clap_plugin_surround_t; typedef struct clap_host_surround { // Informs the host that the channel map has changed. // The channel map can only change when the plugin is de-activated. // [main-thread] void(CLAP_ABI *changed)(const clap_host_t *host); } clap_host_surround_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/log.h0000644000000000000000000000013215101070323020235 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/log.h0000644000175000001440000000135015101070323020224 0ustar00rncbcusers#pragma once #include "../plugin.h" static CLAP_CONSTEXPR const char CLAP_EXT_LOG[] = "clap.log"; #ifdef __cplusplus extern "C" { #endif enum { CLAP_LOG_DEBUG = 0, CLAP_LOG_INFO = 1, CLAP_LOG_WARNING = 2, CLAP_LOG_ERROR = 3, CLAP_LOG_FATAL = 4, // These severities should be used to report misbehaviour. // The plugin one can be used by a layer between the plugin and the host. CLAP_LOG_HOST_MISBEHAVING = 5, CLAP_LOG_PLUGIN_MISBEHAVING = 6, }; typedef int32_t clap_log_severity; typedef struct clap_host_log { // Log a message through the host. // [thread-safe] void(CLAP_ABI *log)(const clap_host_t *host, clap_log_severity severity, const char *msg); } clap_host_log_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/draft0000644000000000000000000000013215101070323020326 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884347704 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/draft/0000755000175000001440000000000015101070323020373 5ustar00rncbcusersqtractor-1.5.9/src/clap/include/clap/ext/draft/PaxHeaders/gain-adjustment-metering.h0000644000000000000000000000013215101070323025456 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884347704 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/draft/gain-adjustment-metering.h0000644000175000001440000000237715101070323025457 0ustar00rncbcusers#pragma once #include "../../plugin.h" // This extension lets the plugin report the current gain adjustment // (typically, gain reduction) to the host. static CLAP_CONSTEXPR const char CLAP_EXT_GAIN_ADJUSTMENT_METERING[] = "clap.gain-adjustment-metering/0"; #ifdef __cplusplus extern "C" { #endif typedef struct clap_plugin_gain_adjustment_metering { // Returns the current gain adjustment in dB. The value is intended // for informational display, for example in a host meter or tooltip. // The returned value represents the gain adjustment that the plugin // applied to the last sample in the most recently processed block. // // The returned value is in dB. Zero means the plugin is applying no gain // reduction, or is not processing. A negative value means the plugin is // applying gain reduction, as with a compressor or limiter. A positive // value means the plugin is adding gain, as with an expander. The value // represents the dynamic gain reduction or expansion applied by the // plugin, before any make-up gain or other adjustment. A single value is // returned for all audio channels. // // [audio-thread] double(CLAP_ABI *get)(const clap_plugin_t *plugin); } clap_plugin_gain_adjustment_metering_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/draft/PaxHeaders/resource-directory.h0000644000000000000000000000013215101070323024405 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/draft/resource-directory.h0000644000175000001440000000744215101070323024404 0ustar00rncbcusers#pragma once #include "../../plugin.h" static CLAP_CONSTEXPR const char CLAP_EXT_RESOURCE_DIRECTORY[] = "clap.resource-directory/1"; #ifdef __cplusplus extern "C" { #endif /// @page Resource Directory /// /// This extension provides a way for the plugin to store its resources as file in a directory /// provided by the host and recover them later on. /// /// The plugin **must** store relative path in its state toward resource directories. /// /// Resource sharing: /// - shared directory is shared among all plugin instances, hence mostly appropriate for read-only /// content /// -> suitable for read-only content /// - exclusive directory is exclusive to the plugin instance /// -> if the plugin, then its exclusive directory must be duplicated too /// -> suitable for read-write content /// /// Keeping the shared directory clean: /// - to avoid clashes in the shared directory, plugins are encouraged to organize their files in /// sub-folders, for example create one subdirectory using the vendor name /// - don't use symbolic links or hard links which points outside of the directory /// /// Resource life-time: /// - exclusive folder content is managed by the plugin instance /// - exclusive folder content is deleted when the plugin instance is removed from the project /// - shared folder content isn't managed by the host, until all plugins using the shared directory /// are removed from the project /// /// Note for the host /// - try to use the filesystem's copy-on-write feature when possible for reducing exclusive folder /// space usage on duplication /// - host can "garbage collect" the files in the shared folder using: /// clap_plugin_resource_directory.get_files_count() /// clap_plugin_resource_directory.get_file_path() /// but be **very** careful before deleting any resources typedef struct clap_plugin_resource_directory { // Sets the directory in which the plugin can save its resources. // The directory remains valid until it is overridden or the plugin is destroyed. // If path is null or blank, it clears the directory location. // path must be absolute. // [main-thread] void(CLAP_ABI *set_directory)(const clap_plugin_t *plugin, const char *path, bool is_shared); // Asks the plugin to put its resources into the resource directory. // It is not necessary to collect files which belongs to the plugin's // factory content unless the param all is true. // [main-thread] void(CLAP_ABI *collect)(const clap_plugin_t *plugin, bool all); // Returns the number of files used by the plugin in the shared resource folder. // [main-thread] uint32_t(CLAP_ABI *get_files_count)(const clap_plugin_t *plugin); // Retrieves relative file path to the resource directory. // @param path writable memory to store the path // @param path_size number of available bytes in path // Returns the number of bytes in the path, or -1 on error // [main-thread] int32_t(CLAP_ABI *get_file_path)(const clap_plugin_t *plugin, uint32_t index, char *path, uint32_t path_size); } clap_plugin_resource_directory_t; typedef struct clap_host_resource_directory { // Request the host to setup a resource directory with the specified sharing. // Returns true if the host will perform the request. // [main-thread] bool(CLAP_ABI *request_directory)(const clap_host_t *host, bool is_shared); // Tell the host that the resource directory of the specified sharing is no longer required. // If is_shared = false, then the host may delete the directory content. // [main-thread] void(CLAP_ABI *release_directory)(const clap_host_t *host, bool is_shared); } clap_host_resource_directory_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/draft/PaxHeaders/triggers.h0000644000000000000000000000013215101070323022402 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/draft/triggers.h0000644000175000001440000001033515101070323022374 0ustar00rncbcusers#pragma once #include "../../plugin.h" #include "../../events.h" #include "../../string-sizes.h" static CLAP_CONSTEXPR const char CLAP_EXT_TRIGGERS[] = "clap.triggers/1"; #ifdef __cplusplus extern "C" { #endif /// @page Trigger events /// /// This extension enables the plugin to expose a set of triggers to the host. /// /// Some examples for triggers: /// - trigger an envelope which is independent of the notes /// - trigger a sample-and-hold unit (maybe even per-voice) enum { // Does this trigger support per note automations? CLAP_TRIGGER_IS_AUTOMATABLE_PER_NOTE_ID = 1 << 0, // Does this trigger support per key automations? CLAP_TRIGGER_IS_AUTOMATABLE_PER_KEY = 1 << 1, // Does this trigger support per channel automations? CLAP_TRIGGER_IS_AUTOMATABLE_PER_CHANNEL = 1 << 2, // Does this trigger support per port automations? CLAP_TRIGGER_IS_AUTOMATABLE_PER_PORT = 1 << 3, }; typedef uint32_t clap_trigger_info_flags; // Given that this extension is still draft, it'll use the event-registry and its own event // namespace until we stabilize it. // // #include // // uint16_t CLAP_EXT_TRIGGER_EVENT_SPACE_ID = UINT16_MAX; // if (host_event_registry->query(host, CLAP_EXT_TRIGGERS, &CLAP_EXT_TRIGGER_EVENT_SPACE_ID)) { // /* we can use trigger events */ // } // // /* later on */ // clap_event_trigger ev; // ev.header.space_id = CLAP_EXT_TRIGGER_EVENT_SPACE_ID; // ev.header.type = CLAP_EVENT_TRIGGER; enum { CLAP_EVENT_TRIGGER = 0 }; typedef struct clap_event_trigger { clap_event_header_t header; // target trigger clap_id trigger_id; // @ref clap_trigger_info.id void *cookie; // @ref clap_trigger_info.cookie // target a specific note_id, port, key and channel, -1 for global int32_t note_id; int16_t port_index; int16_t channel; int16_t key; } clap_event_trigger_t; /* This describes a trigger */ typedef struct clap_trigger_info { // stable trigger identifier, it must never change. clap_id id; clap_trigger_info_flags flags; // in analogy to clap_param_info.cookie void *cookie; // displayable name char name[CLAP_NAME_SIZE]; // the module path containing the trigger, eg:"sequencers/seq1" // '/' will be used as a separator to show a tree like structure. char module[CLAP_PATH_SIZE]; } clap_trigger_info_t; typedef struct clap_plugin_triggers { // Returns the number of triggers. // [main-thread] uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin); // Copies the trigger's info to trigger_info and returns true on success. // [main-thread] bool(CLAP_ABI *get_info)(const clap_plugin_t *plugin, uint32_t index, clap_trigger_info_t *trigger_info); } clap_plugin_triggers_t; enum { // The trigger info did change, use this flag for: // - name change // - module change // New info takes effect immediately. CLAP_TRIGGER_RESCAN_INFO = 1 << 0, // Invalidates everything the host knows about triggers. // It can only be used while the plugin is deactivated. // If the plugin is activated use clap_host->restart() and delay any change until the host calls // clap_plugin->deactivate(). // // You must use this flag if: // - some triggers were added or removed. // - some triggers had critical changes: // - is_per_note (flag) // - is_per_key (flag) // - is_per_channel (flag) // - is_per_port (flag) // - cookie CLAP_TRIGGER_RESCAN_ALL = 1 << 1, }; typedef uint32_t clap_trigger_rescan_flags; enum { // Clears all possible references to a trigger CLAP_TRIGGER_CLEAR_ALL = 1 << 0, // Clears all automations to a trigger CLAP_TRIGGER_CLEAR_AUTOMATIONS = 1 << 1, }; typedef uint32_t clap_trigger_clear_flags; typedef struct clap_host_triggers { // Rescan the full list of triggers according to the flags. // [main-thread] void(CLAP_ABI *rescan)(const clap_host_t *host, clap_trigger_rescan_flags flags); // Clears references to a trigger. // [main-thread] void(CLAP_ABI *clear)(const clap_host_t *host, clap_id trigger_id, clap_trigger_clear_flags flags); } clap_host_triggers_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/draft/PaxHeaders/tuning.h0000644000000000000000000000013215101070323022060 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/draft/tuning.h0000644000175000001440000000517515101070323022060 0ustar00rncbcusers#pragma once #include "../../plugin.h" #include "../../events.h" #include "../../string-sizes.h" static CLAP_CONSTEXPR const char CLAP_EXT_TUNING[] = "clap.tuning/2"; #ifdef __cplusplus extern "C" { #endif // Use clap_host_event_registry->query(host, CLAP_EXT_TUNING, &space_id) to know the event space. // // This event defines the tuning to be used on the given port/channel. typedef struct clap_event_tuning { clap_event_header_t header; int16_t port_index; // -1 global int16_t channel; // 0..15, -1 global clap_id tunning_id; } clap_event_tuning_t; typedef struct clap_tuning_info { clap_id tuning_id; char name[CLAP_NAME_SIZE]; bool is_dynamic; // true if the values may vary with time } clap_tuning_info_t; typedef struct clap_plugin_tuning { // Called when a tuning is added or removed from the pool. // [main-thread] void(CLAP_ABI *changed)(const clap_plugin_t *plugin); } clap_plugin_tuning_t; // This extension provides a dynamic tuning table to the plugin. typedef struct clap_host_tuning { // Gets the relative tuning in semitones against equal temperament with A4=440Hz. // The plugin may query the tuning at a rate that makes sense for *low* frequency modulations. // // If the tuning_id is not found or equals to CLAP_INVALID_ID, // then the function shall gracefully return a sensible value. // // sample_offset is the sample offset from the beginning of the current process block. // // should_play(...) should be checked before calling this function. // // [audio-thread & in-process] double(CLAP_ABI *get_relative)(const clap_host_t *host, clap_id tuning_id, int32_t channel, int32_t key, uint32_t sample_offset); // Returns true if the note should be played. // [audio-thread & in-process] bool(CLAP_ABI *should_play)(const clap_host_t *host, clap_id tuning_id, int32_t channel, int32_t key); // Returns the number of tunings in the pool. // [main-thread] uint32_t(CLAP_ABI *get_tuning_count)(const clap_host_t *host); // Gets info about a tuning // Returns true on success and stores the result into info. // [main-thread] bool(CLAP_ABI *get_info)(const clap_host_t *host, uint32_t tuning_index, clap_tuning_info_t *info); } clap_host_tuning_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/draft/PaxHeaders/mini-curve-display.h0000644000000000000000000000013215101070323024275 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/draft/mini-curve-display.h0000644000175000001440000001424715101070323024275 0ustar00rncbcusers#pragma once #include "../../plugin.h" // This extension allows a host to render a small curve provided by the plugin. // A useful application is to render an EQ frequency response in the DAW mixer view. static CLAP_CONSTEXPR const char CLAP_EXT_MINI_CURVE_DISPLAY[] = "clap.mini-curve-display/3"; #ifdef __cplusplus extern "C" { #endif enum clap_mini_curve_display_curve_kind { // If the curve's kind doesn't fit in any proposed kind, use this one // and perhaps, make a pull request to extend the list. CLAP_MINI_CURVE_DISPLAY_CURVE_KIND_UNSPECIFIED = 0, // The mini curve is intended to draw the total gain response of the plugin. // In this case the y values are in dB and the x values are in Hz (logarithmic). // This would be useful in for example an equalizer. CLAP_MINI_CURVE_DISPLAY_CURVE_KIND_GAIN_RESPONSE = 1, // The mini curve is intended to draw the total phase response of the plugin. // In this case the y values are in radians and the x values are in Hz (logarithmic). // This would be useful in for example an equalizer. CLAP_MINI_CURVE_DISPLAY_CURVE_KIND_PHASE_RESPONSE = 2, // The mini curve is intended to draw the transfer curve of the plugin. // In this case the both x and y values are in dB. // This would be useful in for example a compressor or distortion plugin. CLAP_MINI_CURVE_DISPLAY_CURVE_KIND_TRANSFER_CURVE = 3, // This mini curve is intended to draw gain reduction over time. In this case // x refers to the window in seconds and y refers to level in dB, x_min is // always 0, and x_max would be the duration of the window. // This would be useful in for example a compressor or limiter. CLAP_MINI_CURVE_DISPLAY_CURVE_KIND_GAIN_REDUCTION = 4, // This curve is intended as a generic time series plot. In this case // x refers to the window in seconds. x_min is always 0, and x_max would be the duration of the // window. // Y is not specified and up to the plugin. CLAP_MINI_CURVE_DISPLAY_CURVE_KIND_TIME_SERIES = 5, // Note: more entries could be added here in the future }; typedef struct clap_mini_curve_display_curve_hints { // Range for the x axis. double x_min; double x_max; // Range for the y axis. double y_min; double y_max; } clap_mini_curve_display_curve_hints_t; // A set of points representing the curve to be painted. typedef struct clap_mini_curve_display_curve_data { // Indicates the kind of curve those values represent, the host can use this // information to paint the curve using a meaningful color. int32_t curve_kind; // values[0] will be the leftmost value and values[data_size -1] will be the rightmost // value. // // The value 0 and UINT16_MAX won't be painted. // The value 1 will be at the bottom of the curve and UINT16_MAX - 1 will be at the top. uint16_t *values; uint32_t values_count; } clap_mini_curve_display_curve_data_t; typedef struct clap_plugin_mini_curve_display { // Returns the number of curves the plugin wants to paint. // Be aware that the space to display those curves will be small, and too much data will make // the output hard to read. uint32_t(CLAP_ABI *get_curve_count)(const clap_plugin_t *plugin); // Renders the curve into each the curves buffer. // // curves is an array, and each entries (up to curves_size) contains pre-allocated // values buffer that must be filled by the plugin. // // The host will "stack" the curves, from the first one to the last one. // curves[0] is the first curve to be painted. // curves[n + 1] will be painted over curves[n]. // // Returns the number of curves rendered. // [main-thread] uint32_t(CLAP_ABI *render)(const clap_plugin_t *plugin, clap_mini_curve_display_curve_data_t *curves, uint32_t curves_size); // Tells the plugin if the curve is currently observed or not. // When it isn't observed render() can't be called. // // When is_obseverd becomes true, the curve content and axis name are implicitly invalidated. So // the plugin don't need to call host->changed. // // [main-thread] void(CLAP_ABI *set_observed)(const clap_plugin_t *plugin, bool is_observed); // Retrives the axis name. // x_name and y_name must not to be null. // Returns true on success, if the name capacity was sufficient. // [main-thread] bool(CLAP_ABI *get_axis_name)(const clap_plugin_t *plugin, uint32_t curve_index, char *x_name, char *y_name, uint32_t name_capacity); } clap_plugin_mini_curve_display_t; enum clap_mini_curve_display_change_flags { // Informs the host that the curve content changed. // Can only be called if the curve is observed and is static. CLAP_MINI_CURVE_DISPLAY_CURVE_CHANGED = 1 << 0, // Informs the host that the curve axis name changed. // Can only be called if the curve is observed. CLAP_MINI_CURVE_DISPLAY_AXIS_NAME_CHANGED = 1 << 1, }; typedef struct clap_host_mini_curve_display { // Fills in the given clap_mini_display_curve_hints_t structure and returns // true if successful. If not, return false. // [main-thread] bool(CLAP_ABI *get_hints)(const clap_host_t *host, uint32_t kind, clap_mini_curve_display_curve_hints_t *hints); // Mark the curve as being static or dynamic. // The curve is initially considered as static, though the plugin should explicitely // initialize this state. // // When static, the curve changes will be notified by calling host->changed(). // When dynamic, the curve is constantly changing and the host is expected to // periodically re-render. // // [main-thread] void(CLAP_ABI *set_dynamic)(const clap_host_t *host, bool is_dynamic); // See clap_mini_curve_display_change_flags // [main-thread] void(CLAP_ABI *changed)(const clap_host_t *host, uint32_t flags); } clap_host_mini_curve_display_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/draft/PaxHeaders/project-location.h0000644000000000000000000000013215101070323024030 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/draft/project-location.h0000644000175000001440000000672315101070323024030 0ustar00rncbcusers#pragma once #include "../../color.h" #include "../../plugin.h" #include "../../string-sizes.h" // This extension allows a host to tell the plugin more about its position // within a project or session. static CLAP_CONSTEXPR const char CLAP_EXT_PROJECT_LOCATION[] = "clap.project-location/2"; #ifdef __cplusplus extern "C" { #endif enum clap_project_location_kind { // Represents a document/project/session. CLAP_PROJECT_LOCATION_PROJECT = 1, // Represents a group of tracks. // It can contain track groups, tracks, and devices (post processing). // The first device within a track group has the index of // the last track or track group within this group + 1. CLAP_PROJECT_LOCATION_TRACK_GROUP = 2, // Represents a single track. // It contains devices (serial). CLAP_PROJECT_LOCATION_TRACK = 3, // Represents a single device. // It can contain other nested device chains. CLAP_PROJECT_LOCATION_DEVICE = 4, // Represents a nested device chain (serial). // Its parent must be a device. // It contains other devices. CLAP_PROJECT_LOCATION_NESTED_DEVICE_CHAIN = 5, }; enum clap_project_location_track_kind { // This track is an instrument track. CLAP_PROJECT_LOCATION_INSTUMENT_TRACK = 1, // This track is an audio track. CLAP_PROJECT_LOCATION_AUDIO_TRACK = 2, // This track is both an instrument and audio track. CLAP_PROJECT_LOCATION_HYBRID_TRACK = 3, // This track is a return track. CLAP_PROJECT_LOCATION_RETURN_TRACK = 4, // This track is a master track. // Each group have a master track for processing the sum of all its children tracks. CLAP_PROJECT_LOCATION_MASTER_TRACK = 5, }; enum clap_project_location_flags { CLAP_PROJECT_LOCATION_HAS_INDEX = 1 << 0, CLAP_PROJECT_LOCATION_HAS_COLOR = 1 << 1, }; typedef struct clap_project_location_element { // A bit-mask, see clap_project_location_flags. uint64_t flags; // Kind of the element, must be one of the CLAP_PROJECT_LOCATION_* values. uint32_t kind; // Only relevant if kind is CLAP_PLUGIN_LOCATION_TRACK. // see enum CLAP_PROJECT_LOCATION_track_kind. uint32_t track_kind; // Index within the parent element. // Only usable if CLAP_PROJECT_LOCATION_HAS_INDEX is set in flags. uint32_t index; // Internal ID of the element. // This is not intended for display to the user, // but rather to give the host a potential quick way for lookups. char id[CLAP_PATH_SIZE]; // User friendly name of the element. char name[CLAP_NAME_SIZE]; // Color for this element. // Only usable if CLAP_PROJECT_LOCATION_HAS_COLOR is set in flags. clap_color_t color; } clap_project_location_element_t; typedef struct clap_plugin_project_location { // Called by the host when the location of the plugin instance changes. // // The last item in this array always refers to the device itself, and as // such is expected to be of kind CLAP_PLUGIN_LOCATION_DEVICE. // The first item in this array always refers to the project this device is in and must be of // kind CLAP_PROJECT_LOCATION_PROJECT. The path is expected to be something like: PROJECT > // TRACK_GROUP+ > TRACK > (DEVICE > NESTED_DEVICE_CHAIN)* > DEVICE // // [main-thread] void(CLAP_ABI *set)(const clap_plugin_t *plugin, const clap_project_location_element_t *path, uint32_t num_elements); } clap_plugin_project_location_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/draft/PaxHeaders/undo.h0000644000000000000000000000013215101070323021521 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/draft/undo.h0000644000175000001440000002213015101070323021507 0ustar00rncbcusers#pragma once #include "../../plugin.h" #include "../../stream.h" static CLAP_CONSTEXPR const char CLAP_EXT_UNDO[] = "clap.undo/4"; static CLAP_CONSTEXPR const char CLAP_EXT_UNDO_CONTEXT[] = "clap.undo_context/4"; static CLAP_CONSTEXPR const char CLAP_EXT_UNDO_DELTA[] = "clap.undo_delta/4"; #ifdef __cplusplus extern "C" { #endif /// @page Undo /// /// This extension enables the plugin to merge its undo history with the host. /// This leads to a single undo history shared by the host and many plugins. /// /// Calling host->undo() or host->redo() is equivalent to clicking undo/redo within the host's GUI. /// /// If the plugin uses this interface then its undo and redo should be entirely delegated to /// the host; clicking in the plugin's UI undo or redo is equivalent to clicking undo or redo in the /// host's UI. /// /// Some changes are long running changes, for example a mouse interaction will begin editing some /// complex data and it may take multiple events and a long duration to complete the change. /// In such case the plugin will call host->begin_change() to indicate the beginning of a long /// running change and complete the change by calling host->change_made(). /// /// The host may group changes together: /// [---------------------------------] /// ^-T0 ^-T1 ^-T2 ^-T3 /// Here a long running change C0 begin at T0. /// A instantaneous change C1 at T1, and another one C2 at T2. /// Then at T3 the long running change is completed. /// The host will then create a single undo step that will merge all the changes into C0. /// /// This leads to another important consideration: starting a long running change without /// terminating is **VERY BAD**, because while a change is running it is impossible to call undo or /// redo. /// /// Rationale: multiple designs were considered and this one has the benefit of having a single undo /// history. This simplifies the host implementation, leading to less bugs, a more robust design /// and maybe an easier experience for the user because there's a single undo context versus one /// for the host and one for each plugin instance. /// /// This extension tries to make it as easy as possible for the plugin to hook into the host undo /// and make it efficient when possible by using deltas. The plugin interfaces are all optional, and /// the plugin can for a minimal implementation, just use the host interface and call /// host->change_made() without providing a delta. This is enough for the host to know that it can /// capture a plugin state for the undo step. typedef struct clap_undo_delta_properties { // If true, then the plugin will provide deltas in host->change_made(). // If false, then all clap_undo_delta_properties's attributes become irrelevant. bool has_delta; // If true, then the deltas can be stored on disk and re-used in the future as long as the plugin // is compatible with the given format_version. // // If false, then format_version must be set to CLAP_INVALID_ID. bool are_deltas_persistent; // This represents the delta format version that the plugin is currently using. // Use CLAP_INVALID_ID for invalid value. clap_id format_version; } clap_undo_delta_properties_t; // Use CLAP_EXT_UNDO_DELTA. // This is an optional interface, using deltas is an optimization versus making a state snapshot. typedef struct clap_plugin_undo_delta { // Asks the plugin the delta properties. // [main-thread] void(CLAP_ABI *get_delta_properties)(const clap_plugin_t *plugin, clap_undo_delta_properties_t *properties); // Asks the plugin if it can apply a delta using the given format version. // Returns true if it is possible. // [main-thread] bool(CLAP_ABI *can_use_delta_format_version)(const clap_plugin_t *plugin, clap_id format_version); // Undo using the delta. // Returns true on success. // // [main-thread] bool(CLAP_ABI *undo)(const clap_plugin_t *plugin, clap_id format_version, const void *delta, size_t delta_size); // Redo using the delta. // Returns true on success. // // [main-thread] bool(CLAP_ABI *redo)(const clap_plugin_t *plugin, clap_id format_version, const void *delta, size_t delta_size); } clap_plugin_undo_delta_t; // Use CLAP_EXT_UNDO_CONTEXT. // This is an optional interface, that the plugin can implement in order to know about // the current undo context. typedef struct clap_plugin_undo_context { // Indicate if it is currently possible to perform an undo or redo operation. // [main-thread & plugin-subscribed-to-undo-context] void(CLAP_ABI *set_can_undo)(const clap_plugin_t *plugin, bool can_undo); void(CLAP_ABI *set_can_redo)(const clap_plugin_t *plugin, bool can_redo); // Sets the name of the next undo or redo step. // name: null terminated string. // [main-thread & plugin-subscribed-to-undo-context] void(CLAP_ABI *set_undo_name)(const clap_plugin_t *plugin, const char *name); void(CLAP_ABI *set_redo_name)(const clap_plugin_t *plugin, const char *name); } clap_plugin_undo_context_t; // Use CLAP_EXT_UNDO. typedef struct clap_host_undo { // Begins a long running change. // The plugin must not call this twice: there must be either a call to cancel_change() or // change_made() before calling begin_change() again. // [main-thread] void(CLAP_ABI *begin_change)(const clap_host_t *host); // Cancels a long running change. // cancel_change() must not be called without a preceding begin_change(). // [main-thread] void(CLAP_ABI *cancel_change)(const clap_host_t *host); // Completes an undoable change. // At the moment of this function call, plugin_state->save() would include the current change. // // name: mandatory null terminated string describing the change, this is displayed to the user // // delta: optional, it is a binary blobs used to perform the undo and redo. When not available // the host will save the plugin state and use state->load() to perform undo and redo. // The plugin must be able to perform a redo operation using the delta, though the undo operation // is only possible if delta_can_undo is true. // // Note: the provided delta may be used for incremental state saving and crash recovery. The // plugin can indicate a format version id and the validity lifetime for the binary blobs. // The host can use these to verify the compatibility before applying the delta. // If the plugin is unable to use a delta, a notification should be provided to the user and // the crash recovery should perform a best effort job, at least restoring the latest saved // state. // // Special case: for objects with shared and synchronized state, changes shouldn't be reported // as the host already knows about it. // For example, plugin parameter changes shouldn't produce a call to change_made(). // // Note: if the plugin asked for this interface, then host_state->mark_dirty() will not create an // implicit undo step. // // Note: if the plugin did load a preset or did something that leads to a large delta, // it may consider not producing a delta (pass null) and let the host make a state snapshot // instead. // // Note: if a plugin is producing a lot of changes within a small amount of time, the host // may merge them into a single undo step. // // [main-thread] void(CLAP_ABI *change_made)(const clap_host_t *host, const char *name, const void *delta, size_t delta_size, bool delta_can_undo); // Asks the host to perform the next undo or redo step. // // Note: this maybe a complex and asynchronous operation, which may complete after // this function returns. // // Note: the host may ignore this request if there is no undo/redo step to perform, // or if the host is unable to perform undo/redo at the time (eg: a long running // change is going on). // // [main-thread] void(CLAP_ABI *request_undo)(const clap_host_t *host); void(CLAP_ABI *request_redo)(const clap_host_t *host); // Subscribes to or unsubscribes from undo context info. // // This method helps reducing the number of calls the host has to perform when updating // the undo context info. Consider a large project with 1000+ plugins, we don't want to // call 1000+ times update, while the plugin may only need the context info if its GUI // is shown and it wants to display undo/redo info. // // Initial state is unsubscribed. // // is_subscribed: set to true to receive context info // // It is mandatory for the plugin to implement CLAP_EXT_UNDO_CONTEXT when using this method. // // [main-thread] void(CLAP_ABI *set_wants_context_updates)(const clap_host_t *host, bool is_subscribed); } clap_host_undo_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/draft/PaxHeaders/transport-control.h0000644000000000000000000000013215101070323024266 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/draft/transport-control.h0000644000175000001440000000405315101070323024260 0ustar00rncbcusers#pragma once #include "../../plugin.h" // This extension lets the plugin submit transport requests to the host. // The host has no obligation to execute these requests, so the interface may be // partially working. static CLAP_CONSTEXPR const char CLAP_EXT_TRANSPORT_CONTROL[] = "clap.transport-control/1"; #ifdef __cplusplus extern "C" { #endif typedef struct clap_host_transport_control { // Jumps back to the start point and starts the transport // [main-thread] void(CLAP_ABI *request_start)(const clap_host_t *host); // Stops the transport, and jumps to the start point // [main-thread] void(CLAP_ABI *request_stop)(const clap_host_t *host); // If not playing, starts the transport from its current position // [main-thread] void(CLAP_ABI *request_continue)(const clap_host_t *host); // If playing, stops the transport at the current position // [main-thread] void(CLAP_ABI *request_pause)(const clap_host_t *host); // Equivalent to what "space bar" does with most DAWs // [main-thread] void(CLAP_ABI *request_toggle_play)(const clap_host_t *host); // Jumps the transport to the given position. // Does not start the transport. // [main-thread] void(CLAP_ABI *request_jump)(const clap_host_t *host, clap_beattime position); // Sets the loop region // [main-thread] void(CLAP_ABI *request_loop_region)(const clap_host_t *host, clap_beattime start, clap_beattime duration); // Toggles looping // [main-thread] void(CLAP_ABI *request_toggle_loop)(const clap_host_t *host); // Enables/Disables looping // [main-thread] void(CLAP_ABI *request_enable_loop)(const clap_host_t *host, bool is_enabled); // Enables/Disables recording // [main-thread] void(CLAP_ABI *request_record)(const clap_host_t *host, bool is_recording); // Toggles recording // [main-thread] void(CLAP_ABI *request_toggle_record)(const clap_host_t *host); } clap_host_transport_control_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/draft/PaxHeaders/extensible-audio-ports.h0000644000000000000000000000013215101070323025162 xustar0030 mtime=1761898707.884347704 30 atime=1761898707.884347704 30 ctime=1761898707.884347704 qtractor-1.5.9/src/clap/include/clap/ext/draft/extensible-audio-ports.h0000644000175000001440000000227515101070323025160 0ustar00rncbcusers#pragma once #include "../audio-ports.h" #ifdef __cplusplus extern "C" { #endif // This extension lets the host add and remove audio ports to the plugin. static CLAP_CONSTEXPR const char CLAP_EXT_EXTENSIBLE_AUDIO_PORTS[] = "clap.extensible-audio-ports/1"; typedef struct clap_plugin_extensible_audio_ports { // Asks the plugin to add a new port (at the end of the list), with the following settings. // port_type: see clap_audio_port_info.port_type for interpretation. // port_details: see clap_audio_port_configuration_request.port_details for interpretation. // Returns true on success. // [main-thread && !is_active] bool(CLAP_ABI *add_port)(const clap_plugin_t *plugin, bool is_input, uint32_t channel_count, const char *port_type, const void *port_details); // Asks the plugin to remove a port. // Returns true on success. // [main-thread && !is_active] bool(CLAP_ABI *remove_port)(const clap_plugin_t *plugin, bool is_input, uint32_t index); } clap_plugin_extensible_audio_ports_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/draft/PaxHeaders/scratch-memory.h0000644000000000000000000000013215101070323023511 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/draft/scratch-memory.h0000644000175000001440000000702615101070323023506 0ustar00rncbcusers#pragma once #include "../../plugin.h" // This extension lets the plugin request "scratch" memory from the host. // // The scratch memory is thread-local, and can be accessed during // `clap_plugin->process()` and `clap_plugin_thread_pool->exec()`; // its content is not persistent between callbacks. // // The motivation for this extension is to allow the plugin host // to "share" a single scratch buffer across multiple plugin // instances. // // For example, imagine the host needs to process N plugins // in sequence, and each plugin requires 10K of scratch memory. // If each plugin pre-allocates its own scratch memory, then N * 10K // of memory is being allocated in total. However, if each plugin // requests 10K of scratch memory from the host, then the host can // allocate a single 10K scratch buffer, and make it available to all // plugins. // // This optimization may allow for reduced memory usage and improved // CPU cache usage. static CLAP_CONSTEXPR const char CLAP_EXT_SCRATCH_MEMORY[] = "clap.scratch-memory/1"; #ifdef __cplusplus extern "C" { #endif typedef struct clap_host_scratch_memory { // Asks the host to reserve scratch memory. // // The plugin may call this method multiple times (for // example, gradually decreasing the amount of scratch // being asked for until the host returns true), however, // the plugin should avoid calling this method un-neccesarily // since the host implementation may be relatively expensive. // If the plugin calls `reserve()` multiple times, then the // last call invalidates all previous calls. // // De-activating the plugin releases the scratch memory. // // `max_concurrency_hint` is an optional hint which indicates // the maximum number of threads concurrently accessing the scratch memory. // Set to 0 if unspecified. // // Returns true on success. // // [main-thread & being-activated] bool(CLAP_ABI *reserve)(const clap_host_t *host, uint32_t scratch_size_bytes, uint32_t max_concurrency_hint); // Returns a pointer to the "thread-local" scratch memory. // // If the scratch memory wasn't successfully reserved, returns NULL. // // If the plugin crosses `max_concurrency_hint`, then the return value // is either NULL or a valid scratch memory pointer. // // This method may only be called by the plugin from the audio thread, // (i.e. during the process() or thread_pool.exec() callback), and // the provided memory is only valid until the plugin returns from // that callback. The plugin must not hold any references to data // that lives in the scratch memory after returning from the callback, // as that data will likely be over-written by another plugin using // the same scratch memory. // // The provided memory is not initialized, and may have been used // by other plugin instances, so the plugin must correctly initialize // the memory when using it. // // The provided memory is owned by the host, so the plugin must not // free the memory. // // If the plugin wants to share the same scratch memory pointer with // many threads, it must access the the scratch at the beginning of the // `process()` callback, cache the returned pointer before calling // `clap_host_thread_pool->request_exec()` and clear the cached pointer // before returning from `process()`. // // [audio-thread] void *(CLAP_ABI *access)(const clap_host_t *host); } clap_host_scratch_memory_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/state-context.h0000644000000000000000000000013215101070323022256 xustar0030 mtime=1761898707.885314581 30 atime=1761898707.884669607 30 ctime=1761898707.885314581 qtractor-1.5.9/src/clap/include/clap/ext/state-context.h0000644000175000001440000000517015101070323022251 0ustar00rncbcusers#pragma once #include "../plugin.h" #include "../stream.h" /// @page state-context extension /// @brief extended state handling /// /// This extension lets the host save and load the plugin state with different semantics depending /// on the context. /// /// Briefly, when loading a preset or duplicating a device, the plugin may want to partially load /// the state and initialize certain things differently, like handling limited resources or fixed /// connections to external hardware resources. /// /// Save and Load operations may have a different context. /// All three operations should be equivalent: /// 1. clap_plugin_state_context.load(clap_plugin_state.save(), CLAP_STATE_CONTEXT_FOR_PRESET) /// 2. clap_plugin_state.load(clap_plugin_state_context.save(CLAP_STATE_CONTEXT_FOR_PRESET)) /// 3. clap_plugin_state_context.load( /// clap_plugin_state_context.save(CLAP_STATE_CONTEXT_FOR_PRESET), /// CLAP_STATE_CONTEXT_FOR_PRESET) /// /// If in doubt, fallback to clap_plugin_state. /// /// If the plugin implements CLAP_EXT_STATE_CONTEXT then it is mandatory to also implement /// CLAP_EXT_STATE. /// /// It is unspecified which context is equivalent to clap_plugin_state.{save,load}() #ifdef __cplusplus extern "C" { #endif static CLAP_CONSTEXPR const char CLAP_EXT_STATE_CONTEXT[] = "clap.state-context/2"; enum clap_plugin_state_context_type { // suitable for storing and loading a state as a preset CLAP_STATE_CONTEXT_FOR_PRESET = 1, // suitable for duplicating a plugin instance CLAP_STATE_CONTEXT_FOR_DUPLICATE = 2, // suitable for storing and loading a state within a project/song CLAP_STATE_CONTEXT_FOR_PROJECT = 3, }; typedef struct clap_plugin_state_context { // Saves the plugin state into stream, according to context_type. // Returns true if the state was correctly saved. // // Note that the result may be loaded by both clap_plugin_state.load() and // clap_plugin_state_context.load(). // [main-thread] bool(CLAP_ABI *save)(const clap_plugin_t *plugin, const clap_ostream_t *stream, uint32_t context_type); // Loads the plugin state from stream, according to context_type. // Returns true if the state was correctly restored. // // Note that the state may have been saved by clap_plugin_state.save() or // clap_plugin_state_context.save() with a different context_type. // [main-thread] bool(CLAP_ABI *load)(const clap_plugin_t *plugin, const clap_istream_t *stream, uint32_t context_type); } clap_plugin_state_context_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/posix-fd-support.h0000644000000000000000000000013215101070323022717 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/posix-fd-support.h0000644000175000001440000000306215101070323022710 0ustar00rncbcusers#pragma once #include "../plugin.h" // This extension let your plugin hook itself into the host select/poll/epoll/kqueue reactor. // This is useful to handle asynchronous I/O on the main thread. static CLAP_CONSTEXPR const char CLAP_EXT_POSIX_FD_SUPPORT[] = "clap.posix-fd-support"; #ifdef __cplusplus extern "C" { #endif enum { // IO events flags, they can be used to form a mask which describes: // - which events you are interested in (register_fd/modify_fd) // - which events happened (on_fd) CLAP_POSIX_FD_READ = 1 << 0, CLAP_POSIX_FD_WRITE = 1 << 1, CLAP_POSIX_FD_ERROR = 1 << 2, }; typedef uint32_t clap_posix_fd_flags_t; typedef struct clap_plugin_posix_fd_support { // This callback is "level-triggered". // It means that a writable fd will continuously produce "on_fd()" events; // don't forget using modify_fd() to remove the write notification once you're // done writing. // // [main-thread] void(CLAP_ABI *on_fd)(const clap_plugin_t *plugin, int fd, clap_posix_fd_flags_t flags); } clap_plugin_posix_fd_support_t; typedef struct clap_host_posix_fd_support { // Returns true on success. // [main-thread] bool(CLAP_ABI *register_fd)(const clap_host_t *host, int fd, clap_posix_fd_flags_t flags); // Returns true on success. // [main-thread] bool(CLAP_ABI *modify_fd)(const clap_host_t *host, int fd, clap_posix_fd_flags_t flags); // Returns true on success. // [main-thread] bool(CLAP_ABI *unregister_fd)(const clap_host_t *host, int fd); } clap_host_posix_fd_support_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/event-registry.h0000644000000000000000000000013215101070323022443 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/event-registry.h0000644000175000001440000000112415101070323022431 0ustar00rncbcusers#pragma once #include "../plugin.h" static CLAP_CONSTEXPR const char CLAP_EXT_EVENT_REGISTRY[] = "clap.event-registry"; #ifdef __cplusplus extern "C" { #endif typedef struct clap_host_event_registry { // Queries an event space id. // The space id 0 is reserved for CLAP's core events. See CLAP_CORE_EVENT_SPACE. // // Return false and sets *space_id to UINT16_MAX if the space name is unknown to the host. // [main-thread] bool(CLAP_ABI *query)(const clap_host_t *host, const char *space_name, uint16_t *space_id); } clap_host_event_registry_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/remote-controls.h0000644000000000000000000000013215101070323022610 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/remote-controls.h0000644000175000001440000000530015101070323022576 0ustar00rncbcusers#pragma once #include "../plugin.h" #include "../string-sizes.h" // This extension let the plugin provide a structured way of mapping parameters to an hardware // controller. // // This is done by providing a set of remote control pages organized by section. // A page contains up to 8 controls, which references parameters using param_id. // // |`- [section:main] // | `- [name:main] performance controls // |`- [section:osc] // | |`- [name:osc1] osc1 page // | |`- [name:osc2] osc2 page // | |`- [name:osc-sync] osc sync page // | `- [name:osc-noise] osc noise page // |`- [section:filter] // | |`- [name:flt1] filter 1 page // | `- [name:flt2] filter 2 page // |`- [section:env] // | |`- [name:env1] env1 page // | `- [name:env2] env2 page // |`- [section:lfo] // | |`- [name:lfo1] env1 page // | `- [name:lfo2] env2 page // `- etc... // // One possible workflow is to have a set of buttons, which correspond to a section. // Pressing that button once gets you to the first page of the section. // Press it again to cycle through the section's pages. static CLAP_CONSTEXPR const char CLAP_EXT_REMOTE_CONTROLS[] = "clap.remote-controls/2"; // The latest draft is 100% compatible // This compat ID may be removed in 2026. static CLAP_CONSTEXPR const char CLAP_EXT_REMOTE_CONTROLS_COMPAT[] = "clap.remote-controls.draft/2"; #ifdef __cplusplus extern "C" { #endif enum { CLAP_REMOTE_CONTROLS_COUNT = 8 }; typedef struct clap_remote_controls_page { char section_name[CLAP_NAME_SIZE]; clap_id page_id; char page_name[CLAP_NAME_SIZE]; clap_id param_ids[CLAP_REMOTE_CONTROLS_COUNT]; // This is used to separate device pages versus preset pages. // If true, then this page is specific to this preset. bool is_for_preset; } clap_remote_controls_page_t; typedef struct clap_plugin_remote_controls { // Returns the number of pages. // [main-thread] uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin); // Get a page by index. // Returns true on success and stores the result into page. // [main-thread] bool(CLAP_ABI *get)(const clap_plugin_t *plugin, uint32_t page_index, clap_remote_controls_page_t *page); } clap_plugin_remote_controls_t; typedef struct clap_host_remote_controls { // Informs the host that the remote controls have changed. // [main-thread] void(CLAP_ABI *changed)(const clap_host_t *host); // Suggest a page to the host because it corresponds to what the user is currently editing in the // plugin's GUI. // [main-thread] void(CLAP_ABI *suggest_page)(const clap_host_t *host, clap_id page_id); } clap_host_remote_controls_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/latency.h0000644000000000000000000000013215101070323021113 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/latency.h0000644000175000001440000000131715101070323021105 0ustar00rncbcusers#pragma once #include "../plugin.h" static CLAP_CONSTEXPR const char CLAP_EXT_LATENCY[] = "clap.latency"; #ifdef __cplusplus extern "C" { #endif typedef struct clap_plugin_latency { // Returns the plugin latency in samples. // [main-thread & (being-activated | active)] uint32_t(CLAP_ABI *get)(const clap_plugin_t *plugin); } clap_plugin_latency_t; typedef struct clap_host_latency { // Tell the host that the latency changed. // The latency is only allowed to change during plugin->activate. // If the plugin is activated, call host->request_restart() // [main-thread & being-activated] void(CLAP_ABI *changed)(const clap_host_t *host); } clap_host_latency_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/thread-pool.h0000644000000000000000000000013215101070323021672 xustar0030 mtime=1761898707.885314581 30 atime=1761898707.885314581 30 ctime=1761898707.885314581 qtractor-1.5.9/src/clap/include/clap/ext/thread-pool.h0000644000175000001440000000420115101070323021657 0ustar00rncbcusers#pragma once #include "../plugin.h" /// @page /// /// This extension lets the plugin use the host's thread pool. /// /// The plugin must provide @ref clap_plugin_thread_pool, and the host may provide @ref /// clap_host_thread_pool. If it doesn't, the plugin should process its data by its own means. In /// the worst case, a single threaded for-loop. /// /// Simple example with N voices to process /// /// @code /// void myplug_thread_pool_exec(const clap_plugin *plugin, uint32_t voice_index) /// { /// compute_voice(plugin, voice_index); /// } /// /// void myplug_process(const clap_plugin *plugin, const clap_process *process) /// { /// ... /// bool didComputeVoices = false; /// if (host_thread_pool && host_thread_pool.exec) /// didComputeVoices = host_thread_pool.request_exec(host, plugin, N); /// /// if (!didComputeVoices) /// for (uint32_t i = 0; i < N; ++i) /// myplug_thread_pool_exec(plugin, i); /// ... /// } /// @endcode /// /// Be aware that using a thread pool may break hard real-time rules due to the thread /// synchronization involved. /// /// If the host knows that it is running under hard real-time pressure it may decide to not /// provide this interface. static CLAP_CONSTEXPR const char CLAP_EXT_THREAD_POOL[] = "clap.thread-pool"; #ifdef __cplusplus extern "C" { #endif typedef struct clap_plugin_thread_pool { // Called by the thread pool void(CLAP_ABI *exec)(const clap_plugin_t *plugin, uint32_t task_index); } clap_plugin_thread_pool_t; typedef struct clap_host_thread_pool { // Schedule num_tasks jobs in the host thread pool. // It can't be called concurrently or from the thread pool. // Will block until all the tasks are processed. // This must be used exclusively for realtime processing within the process call. // Returns true if the host did execute all the tasks, false if it rejected the request. // The host should check that the plugin is within the process call, and if not, reject the exec // request. // [audio-thread] bool(CLAP_ABI *request_exec)(const clap_host_t *host, uint32_t num_tasks); } clap_host_thread_pool_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/params.h0000644000000000000000000000013215101070323020737 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/params.h0000644000175000001440000003764315101070323020744 0ustar00rncbcusers#pragma once #include "../plugin.h" #include "../string-sizes.h" /// @page Parameters /// @brief parameters management /// /// Main idea: /// /// The host sees the plugin as an atomic entity; and acts as a controller on top of its parameters. /// The plugin is responsible for keeping its audio processor and its GUI in sync. /// /// The host can at any time read parameters' value on the [main-thread] using /// @ref clap_plugin_params.get_value(). /// /// There are two options to communicate parameter value changes, and they are not concurrent. /// - send automation points during clap_plugin.process() /// - send automation points during clap_plugin_params.flush(), for parameter changes /// without processing audio /// /// When the plugin changes a parameter value, it must inform the host. /// It will send @ref CLAP_EVENT_PARAM_VALUE event during process() or flush(). /// If the user is adjusting the value, don't forget to mark the beginning and end /// of the gesture by sending CLAP_EVENT_PARAM_GESTURE_BEGIN and CLAP_EVENT_PARAM_GESTURE_END /// events. /// /// @note MIDI CCs are tricky because you may not know when the parameter adjustment ends. /// Also if the host records incoming MIDI CC and parameter change automation at the same time, /// there will be a conflict at playback: MIDI CC vs Automation. /// The parameter automation will always target the same parameter because the param_id is stable. /// The MIDI CC may have a different mapping in the future and may result in a different playback. /// /// When a MIDI CC changes a parameter's value, set the flag CLAP_EVENT_DONT_RECORD in /// clap_event_param.header.flags. That way the host may record the MIDI CC automation, but not the /// parameter change and there won't be conflict at playback. /// /// Scenarios: /// /// I. Loading a preset /// - load the preset in a temporary state /// - call @ref clap_host_params.rescan() if anything changed /// - call @ref clap_host_latency.changed() if latency changed /// - invalidate any other info that may be cached by the host /// - if the plugin is activated and the preset will introduce breaking changes /// (latency, audio ports, new parameters, ...) be sure to wait for the host /// to deactivate the plugin to apply those changes. /// If there are no breaking changes, the plugin can apply them them right away. /// The plugin is responsible for updating both its audio processor and its gui. /// /// II. Turning a knob on the DAW interface /// - the host will send an automation event to the plugin via a process() or flush() /// /// III. Turning a knob on the Plugin interface /// - the plugin is responsible for sending the parameter value to its audio processor /// - call clap_host_params->request_flush() or clap_host->request_process(). /// - when the host calls either clap_plugin->process() or clap_plugin_params->flush(), /// send an automation event and don't forget to wrap the parameter change(s) /// with CLAP_EVENT_PARAM_GESTURE_BEGIN and CLAP_EVENT_PARAM_GESTURE_END to define the /// beginning and end of the gesture. /// /// IV. Turning a knob via automation /// - host sends an automation point during clap_plugin->process() or clap_plugin_params->flush(). /// - the plugin is responsible for updating its GUI /// /// V. Turning a knob via plugin's internal MIDI mapping /// - the plugin sends a CLAP_EVENT_PARAM_VALUE output event, set should_record to false /// - the plugin is responsible for updating its GUI /// /// VI. Adding or removing parameters /// - if the plugin is activated call clap_host->restart() /// - once the plugin isn't active: /// - apply the new state /// - if a parameter is gone or is created with an id that may have been used before, /// call clap_host_params.clear(host, param_id, CLAP_PARAM_CLEAR_ALL) /// - call clap_host_params->rescan(CLAP_PARAM_RESCAN_ALL) /// /// CLAP allows the plugin to change the parameter range, yet the plugin developer /// should be aware that doing so isn't without risk, especially if you made the /// promise to never change the sound. If you want to be 100% certain that the /// sound will not change with all host, then simply never change the range. /// /// There are two approaches to automations, either you automate the plain value, /// or you automate the knob position. The first option will be robust to a range /// increase, while the second won't be. /// /// If the host goes with the second approach (automating the knob position), it means /// that the plugin is hosted in a relaxed environment regarding sound changes (they are /// accepted, and not a concern as long as they are reasonable). Though, stepped parameters /// should be stored as plain value in the document. /// /// If the host goes with the first approach, there will still be situation where the /// sound may inevitably change. For example, if the plugin increase the range, there /// is an automation playing at the max value and on top of that an LFO is applied. /// See the following curve: /// . /// . . /// ..... . . /// before: . . and after: . . /// /// Persisting parameter values: /// /// Plugins are responsible for persisting their parameter's values between /// sessions by implementing the state extension. Otherwise parameter value will /// not be recalled when reloading a project. Hosts should _not_ try to save and /// restore parameter values for plugins that don't implement the state /// extension. /// /// Advice for the host: /// /// - store plain values in the document (automation) /// - store modulation amount in plain value delta, not in percentage /// - when you apply a CC mapping, remember the min/max plain values so you can adjust /// - do not implement a parameter saving fall back for plugins that don't /// implement the state extension /// /// Advice for the plugin: /// /// - think carefully about your parameter range when designing your DSP /// - avoid shrinking parameter ranges, they are very likely to change the sound /// - consider changing the parameter range as a tradeoff: what you improve vs what you break /// - make sure to implement saving and loading the parameter values using the /// state extension /// - if you plan to use adapters for other plugin formats, then you need to pay extra /// attention to the adapter requirements static CLAP_CONSTEXPR const char CLAP_EXT_PARAMS[] = "clap.params"; #ifdef __cplusplus extern "C" { #endif enum { // Is this param stepped? (integer values only) // if so the double value is converted to integer using a cast (equivalent to trunc). CLAP_PARAM_IS_STEPPED = 1 << 0, // Useful for periodic parameters like a phase CLAP_PARAM_IS_PERIODIC = 1 << 1, // The parameter should not be shown to the user, because it is currently not used. // It is not necessary to process automation for this parameter. CLAP_PARAM_IS_HIDDEN = 1 << 2, // The parameter can't be changed by the host. CLAP_PARAM_IS_READONLY = 1 << 3, // This parameter is used to merge the plugin and host bypass button. // It implies that the parameter is stepped. // min: 0 -> bypass off // max: 1 -> bypass on CLAP_PARAM_IS_BYPASS = 1 << 4, // When set: // - automation can be recorded // - automation can be played back // // The host can send live user changes for this parameter regardless of this flag. // // If this parameter affects the internal processing structure of the plugin, ie: max delay, fft // size, ... and the plugins needs to re-allocate its working buffers, then it should call // host->request_restart(), and perform the change once the plugin is re-activated. CLAP_PARAM_IS_AUTOMATABLE = 1 << 5, // Does this parameter support per note automations? CLAP_PARAM_IS_AUTOMATABLE_PER_NOTE_ID = 1 << 6, // Does this parameter support per key automations? CLAP_PARAM_IS_AUTOMATABLE_PER_KEY = 1 << 7, // Does this parameter support per channel automations? CLAP_PARAM_IS_AUTOMATABLE_PER_CHANNEL = 1 << 8, // Does this parameter support per port automations? CLAP_PARAM_IS_AUTOMATABLE_PER_PORT = 1 << 9, // Does this parameter support the modulation signal? CLAP_PARAM_IS_MODULATABLE = 1 << 10, // Does this parameter support per note modulations? CLAP_PARAM_IS_MODULATABLE_PER_NOTE_ID = 1 << 11, // Does this parameter support per key modulations? CLAP_PARAM_IS_MODULATABLE_PER_KEY = 1 << 12, // Does this parameter support per channel modulations? CLAP_PARAM_IS_MODULATABLE_PER_CHANNEL = 1 << 13, // Does this parameter support per port modulations? CLAP_PARAM_IS_MODULATABLE_PER_PORT = 1 << 14, // Any change to this parameter will affect the plugin output and requires to be done via // process() if the plugin is active. // // A simple example would be a DC Offset, changing it will change the output signal and must be // processed. CLAP_PARAM_REQUIRES_PROCESS = 1 << 15, // This parameter represents an enumerated value. // If you set this flag, then you must set CLAP_PARAM_IS_STEPPED too. // All values from min to max must not have a blank value_to_text(). CLAP_PARAM_IS_ENUM = 1 << 16, }; typedef uint32_t clap_param_info_flags; /* This describes a parameter */ typedef struct clap_param_info { // Stable parameter identifier, it must never change. clap_id id; clap_param_info_flags flags; // This value is optional and set by the plugin. // Its purpose is to provide fast access to the plugin parameter object by caching its pointer. // For instance: // // in clap_plugin_params.get_info(): // Parameter *p = findParameter(param_id); // param_info->cookie = p; // // later, in clap_plugin.process(): // // Parameter *p = (Parameter *)event->cookie; // if (!p) [[unlikely]] // p = findParameter(event->param_id); // // where findParameter() is a function the plugin implements to map parameter ids to internal // objects. // // Important: // - The cookie is invalidated by a call to clap_host_params->rescan(CLAP_PARAM_RESCAN_ALL) or // when the plugin is destroyed. // - The host will either provide the cookie as issued or nullptr in events addressing // parameters. // - The plugin must gracefully handle the case of a cookie which is nullptr. // - Many plugins will process the parameter events more quickly if the host can provide the // cookie in a faster time than a hashmap lookup per param per event. void *cookie; // The display name. eg: "Volume". This does not need to be unique. Do not include the module // text in this. The host should concatenate/format the module + name in the case where showing // the name alone would be too vague. char name[CLAP_NAME_SIZE]; // The module path containing the param, eg: "Oscillators/Wavetable 1". // '/' will be used as a separator to show a tree-like structure. char module[CLAP_PATH_SIZE]; double min_value; // Minimum plain value. Must be finite (`std::isfinite` true) double max_value; // Maximum plain value. Must be finite double default_value; // Default plain value. Must be in [min, max] range. } clap_param_info_t; typedef struct clap_plugin_params { // Returns the number of parameters. // [main-thread] uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin); // Copies the parameter's info to param_info. // Returns true on success. // [main-thread] bool(CLAP_ABI *get_info)(const clap_plugin_t *plugin, uint32_t param_index, clap_param_info_t *param_info); // Writes the parameter's current value to out_value. // Returns true on success. // [main-thread] bool(CLAP_ABI *get_value)(const clap_plugin_t *plugin, clap_id param_id, double *out_value); // Fills out_buffer with a null-terminated UTF-8 string that represents the parameter at the // given 'value' argument. eg: "2.3 kHz". The host should always use this to format parameter // values before displaying it to the user. // Returns true on success. // [main-thread] bool(CLAP_ABI *value_to_text)(const clap_plugin_t *plugin, clap_id param_id, double value, char *out_buffer, uint32_t out_buffer_capacity); // Converts the null-terminated UTF-8 param_value_text into a double and writes it to out_value. // The host can use this to convert user input into a parameter value. // Returns true on success. // [main-thread] bool(CLAP_ABI *text_to_value)(const clap_plugin_t *plugin, clap_id param_id, const char *param_value_text, double *out_value); // Flushes a set of parameter changes. // This method must not be called concurrently to clap_plugin->process(). // // Note: if the plugin is processing, then the process() call will already achieve the // parameter update (bi-directional), so a call to flush isn't required, also be aware // that the plugin may use the sample offset in process(), while this information would be // lost within flush(). // // [active ? audio-thread : main-thread] void(CLAP_ABI *flush)(const clap_plugin_t *plugin, const clap_input_events_t *in, const clap_output_events_t *out); } clap_plugin_params_t; enum { // The parameter values did change, eg. after loading a preset. // The host will scan all the parameters value. // The host will not record those changes as automation points. // New values takes effect immediately. CLAP_PARAM_RESCAN_VALUES = 1 << 0, // The value to text conversion changed, and the text needs to be rendered again. CLAP_PARAM_RESCAN_TEXT = 1 << 1, // The parameter info did change, use this flag for: // - name change // - module change // - is_periodic (flag) // - is_hidden (flag) // New info takes effect immediately. CLAP_PARAM_RESCAN_INFO = 1 << 2, // Invalidates everything the host knows about parameters. // It can only be used while the plugin is deactivated. // If the plugin is activated use clap_host->restart() and delay any change until the host calls // clap_plugin->deactivate(). // // You must use this flag if: // - some parameters were added or removed. // - some parameters had critical changes: // - is_per_note (flag) // - is_per_key (flag) // - is_per_channel (flag) // - is_per_port (flag) // - is_readonly (flag) // - is_bypass (flag) // - is_stepped (flag) // - is_modulatable (flag) // - min_value // - max_value // - cookie CLAP_PARAM_RESCAN_ALL = 1 << 3, }; typedef uint32_t clap_param_rescan_flags; enum { // Clears all possible references to a parameter CLAP_PARAM_CLEAR_ALL = 1 << 0, // Clears all automations to a parameter CLAP_PARAM_CLEAR_AUTOMATIONS = 1 << 1, // Clears all modulations to a parameter CLAP_PARAM_CLEAR_MODULATIONS = 1 << 2, }; typedef uint32_t clap_param_clear_flags; typedef struct clap_host_params { // Rescan the full list of parameters according to the flags. // [main-thread] void(CLAP_ABI *rescan)(const clap_host_t *host, clap_param_rescan_flags flags); // Clears references to a parameter. // [main-thread] void(CLAP_ABI *clear)(const clap_host_t *host, clap_id param_id, clap_param_clear_flags flags); // Request a parameter flush. // // The host will then schedule a call to either: // - clap_plugin.process() // - clap_plugin_params.flush() // // This function is always safe to use and should not be called from an [audio-thread] as the // plugin would already be within process() or flush(). // // [thread-safe,!audio-thread] void(CLAP_ABI *request_flush)(const clap_host_t *host); } clap_host_params_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/render.h0000644000000000000000000000013215101070323020733 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/render.h0000644000175000001440000000220015101070323020715 0ustar00rncbcusers#pragma once #include "../plugin.h" static CLAP_CONSTEXPR const char CLAP_EXT_RENDER[] = "clap.render"; #ifdef __cplusplus extern "C" { #endif enum { // Default setting, for "realtime" processing CLAP_RENDER_REALTIME = 0, // For processing without realtime pressure // The plugin may use more expensive algorithms for higher sound quality. CLAP_RENDER_OFFLINE = 1, }; typedef int32_t clap_plugin_render_mode; // The render extension is used to let the plugin know if it has "realtime" // pressure to process. // // If this information does not influence your rendering code, then don't // implement this extension. typedef struct clap_plugin_render { // Returns true if the plugin has a hard requirement to process in real-time. // This is especially useful for plugin acting as a proxy to an hardware device. // [main-thread] bool(CLAP_ABI *has_hard_realtime_requirement)(const clap_plugin_t *plugin); // Returns true if the rendering mode could be applied. // [main-thread] bool(CLAP_ABI *set)(const clap_plugin_t *plugin, clap_plugin_render_mode mode); } clap_plugin_render_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/timer-support.h0000644000000000000000000000013215101070323022306 xustar0030 mtime=1761898707.885314581 30 atime=1761898707.885314581 30 ctime=1761898707.885314581 qtractor-1.5.9/src/clap/include/clap/ext/timer-support.h0000644000175000001440000000152315101070323022277 0ustar00rncbcusers#pragma once #include "../plugin.h" static CLAP_CONSTEXPR const char CLAP_EXT_TIMER_SUPPORT[] = "clap.timer-support"; #ifdef __cplusplus extern "C" { #endif typedef struct clap_plugin_timer_support { // [main-thread] void(CLAP_ABI *on_timer)(const clap_plugin_t *plugin, clap_id timer_id); } clap_plugin_timer_support_t; typedef struct clap_host_timer_support { // Registers a periodic timer. // The host may adjust the period if it is under a certain threshold. // 30 Hz should be allowed. // Returns true on success. // [main-thread] bool(CLAP_ABI *register_timer)(const clap_host_t *host, uint32_t period_ms, clap_id *timer_id); // Returns true on success. // [main-thread] bool(CLAP_ABI *unregister_timer)(const clap_host_t *host, clap_id timer_id); } clap_host_timer_support_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/voice-info.h0000644000000000000000000000013215101070323021512 xustar0030 mtime=1761898707.885314581 30 atime=1761898707.885314581 30 ctime=1761898707.885314581 qtractor-1.5.9/src/clap/include/clap/ext/voice-info.h0000644000175000001440000000347215101070323021510 0ustar00rncbcusers#pragma once #include "../plugin.h" // This extension indicates the number of voices the synthesizer has. // It is useful for the host when performing polyphonic modulations, // because the host needs its own voice management and should try to follow // what the plugin is doing: // - make the host's voice pool coherent with what the plugin has // - turn the host's voice management to mono when the plugin is mono static CLAP_CONSTEXPR const char CLAP_EXT_VOICE_INFO[] = "clap.voice-info"; #ifdef __cplusplus extern "C" { #endif enum { // Allows the host to send overlapping NOTE_ON events. // The plugin will then rely upon the note_id to distinguish between them. CLAP_VOICE_INFO_SUPPORTS_OVERLAPPING_NOTES = 1 << 0, }; typedef struct clap_voice_info { // voice_count is the current number of voices that the patch can use // voice_capacity is the number of voices allocated voices // voice_count should not be confused with the number of active voices. // // 1 <= voice_count <= voice_capacity // // For example, a synth can have a capacity of 8 voices, but be configured // to only use 4 voices: {count: 4, capacity: 8}. // // If the voice_count is 1, then the synth is working in mono and the host // can decide to only use global modulation mapping. uint32_t voice_count; uint32_t voice_capacity; uint64_t flags; } clap_voice_info_t; typedef struct clap_plugin_voice_info { // gets the voice info, returns true on success // [main-thread && active] bool(CLAP_ABI *get)(const clap_plugin_t *plugin, clap_voice_info_t *info); } clap_plugin_voice_info_t; typedef struct clap_host_voice_info { // informs the host that the voice info has changed // [main-thread] void(CLAP_ABI *changed)(const clap_host_t *host); } clap_host_voice_info_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/audio-ports-activation.h0000644000000000000000000000013215101070323024061 xustar0030 mtime=1761898707.884347704 30 atime=1761898707.883940074 30 ctime=1761898707.884347704 qtractor-1.5.9/src/clap/include/clap/ext/audio-ports-activation.h0000644000175000001440000000502215101070323024050 0ustar00rncbcusers#pragma once #include "../plugin.h" /// @page Audio Ports Activation /// /// This extension provides a way for the host to activate and de-activate audio ports. /// Deactivating a port provides the following benefits: /// - the plugin knows ahead of time that a given input is not present and can choose /// an optimized computation path, /// - the plugin knows that an output is not consumed by the host, and doesn't need to /// compute it. /// /// Audio ports can only be activated or deactivated when the plugin is deactivated, unless /// can_activate_while_processing() returns true. /// /// Audio buffers must still be provided if the audio port is deactivated. /// In such case, they shall be filled with 0 (or whatever is the neutral value in your context) /// and the constant_mask shall be set. /// /// Audio ports are initially in the active state after creating the plugin instance. /// Audio ports state are not saved in the plugin state, so the host must restore the /// audio ports state after creating the plugin instance. /// /// Audio ports state is invalidated by clap_plugin_audio_ports_config.select() and /// clap_host_audio_ports.rescan(CLAP_AUDIO_PORTS_RESCAN_LIST). static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_ACTIVATION[] = "clap.audio-ports-activation/2"; // The latest draft is 100% compatible. // This compat ID may be removed in 2026. static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_ACTIVATION_COMPAT[] = "clap.audio-ports-activation/draft-2"; #ifdef __cplusplus extern "C" { #endif typedef struct clap_plugin_audio_ports_activation { // Returns true if the plugin supports activation/deactivation while processing. // [main-thread] bool(CLAP_ABI *can_activate_while_processing)(const clap_plugin_t *plugin); // Activate the given port. // // It is only possible to activate and de-activate on the audio-thread if // can_activate_while_processing() returns true. // // sample_size indicate if the host will provide 32 bit audio buffers or 64 bits one. // Possible values are: 32, 64 or 0 if unspecified. // // returns false if failed, or invalid parameters // [active ? audio-thread : main-thread] bool(CLAP_ABI *set_active)(const clap_plugin_t *plugin, bool is_input, uint32_t port_index, bool is_active, uint32_t sample_size); } clap_plugin_audio_ports_activation_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/context-menu.h0000644000000000000000000000013215101070323022102 xustar0030 mtime=1761898707.884347704 30 atime=1761898707.884347704 30 ctime=1761898707.884347704 qtractor-1.5.9/src/clap/include/clap/ext/context-menu.h0000644000175000001440000001362215101070323022076 0ustar00rncbcusers#pragma once #include "../plugin.h" // This extension lets the host and plugin exchange menu items and let the plugin ask the host to // show its context menu. static CLAP_CONSTEXPR const char CLAP_EXT_CONTEXT_MENU[] = "clap.context-menu/1"; // The latest draft is 100% compatible. // This compat ID may be removed in 2026. static CLAP_CONSTEXPR const char CLAP_EXT_CONTEXT_MENU_COMPAT[] = "clap.context-menu.draft/0"; #ifdef __cplusplus extern "C" { #endif // There can be different target kind for a context menu enum { CLAP_CONTEXT_MENU_TARGET_KIND_GLOBAL = 0, CLAP_CONTEXT_MENU_TARGET_KIND_PARAM = 1, }; // Describes the context menu target typedef struct clap_context_menu_target { uint32_t kind; clap_id id; } clap_context_menu_target_t; enum { // Adds a clickable menu entry. // data: const clap_context_menu_item_entry_t* CLAP_CONTEXT_MENU_ITEM_ENTRY, // Adds a clickable menu entry which will feature both a checkmark and a label. // data: const clap_context_menu_item_check_entry_t* CLAP_CONTEXT_MENU_ITEM_CHECK_ENTRY, // Adds a separator line. // data: NULL CLAP_CONTEXT_MENU_ITEM_SEPARATOR, // Starts a sub menu with the given label. // data: const clap_context_menu_item_begin_submenu_t* CLAP_CONTEXT_MENU_ITEM_BEGIN_SUBMENU, // Ends the current sub menu. // data: NULL CLAP_CONTEXT_MENU_ITEM_END_SUBMENU, // Adds a title entry // data: const clap_context_menu_item_title_t * CLAP_CONTEXT_MENU_ITEM_TITLE, }; typedef uint32_t clap_context_menu_item_kind_t; typedef struct clap_context_menu_entry { // text to be displayed const char *label; // if false, then the menu entry is greyed out and not clickable bool is_enabled; clap_id action_id; } clap_context_menu_entry_t; typedef struct clap_context_menu_check_entry { // text to be displayed const char *label; // if false, then the menu entry is greyed out and not clickable bool is_enabled; // if true, then the menu entry will be displayed as checked bool is_checked; clap_id action_id; } clap_context_menu_check_entry_t; typedef struct clap_context_menu_item_title { // text to be displayed const char *title; // if false, then the menu entry is greyed out bool is_enabled; } clap_context_menu_item_title_t; typedef struct clap_context_menu_submenu { // text to be displayed const char *label; // if false, then the menu entry is greyed out and won't show submenu bool is_enabled; } clap_context_menu_submenu_t; // Context menu builder. // This object isn't thread-safe and must be used on the same thread as it was provided. typedef struct clap_context_menu_builder { void *ctx; // Adds an entry to the menu. // item_data type is determined by item_kind. // Returns true on success. bool(CLAP_ABI *add_item)(const struct clap_context_menu_builder *builder, clap_context_menu_item_kind_t item_kind, const void *item_data); // Returns true if the menu builder supports the given item kind bool(CLAP_ABI *supports)(const struct clap_context_menu_builder *builder, clap_context_menu_item_kind_t item_kind); } clap_context_menu_builder_t; typedef struct clap_plugin_context_menu { // Insert plugin's menu items into the menu builder. // If target is null, assume global context. // Returns true on success. // [main-thread] bool(CLAP_ABI *populate)(const clap_plugin_t *plugin, const clap_context_menu_target_t *target, const clap_context_menu_builder_t *builder); // Performs the given action, which was previously provided to the host via populate(). // If target is null, assume global context. // Returns true on success. // [main-thread] bool(CLAP_ABI *perform)(const clap_plugin_t *plugin, const clap_context_menu_target_t *target, clap_id action_id); } clap_plugin_context_menu_t; typedef struct clap_host_context_menu { // Insert host's menu items into the menu builder. // If target is null, assume global context. // Returns true on success. // [main-thread] bool(CLAP_ABI *populate)(const clap_host_t *host, const clap_context_menu_target_t *target, const clap_context_menu_builder_t *builder); // Performs the given action, which was previously provided to the plugin via populate(). // If target is null, assume global context. // Returns true on success. // [main-thread] bool(CLAP_ABI *perform)(const clap_host_t *host, const clap_context_menu_target_t *target, clap_id action_id); // Returns true if the host can display a popup menu for the plugin. // This may depend upon the current windowing system used to display the plugin, so the // return value is invalidated after creating the plugin window. // [main-thread] bool(CLAP_ABI *can_popup)(const clap_host_t *host); // Shows the host popup menu for a given parameter. // If the plugin is using embedded GUI, then x and y are relative to the plugin's window, // otherwise they're absolute coordinate, and screen index might be set accordingly. // If target is null, assume global context. // Returns true on success. // [main-thread] bool(CLAP_ABI *popup)(const clap_host_t *host, const clap_context_menu_target_t *target, int32_t screen_index, int32_t x, int32_t y); } clap_host_context_menu_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/track-info.h0000644000000000000000000000013215101070323021511 xustar0030 mtime=1761898707.885314581 30 atime=1761898707.885314581 30 ctime=1761898707.885314581 qtractor-1.5.9/src/clap/include/clap/ext/track-info.h0000644000175000001440000000423115101070323021501 0ustar00rncbcusers#pragma once #include "../plugin.h" #include "../color.h" #include "../string-sizes.h" // This extension let the plugin query info about the track it's in. // It is useful when the plugin is created, to initialize some parameters (mix, dry, wet) // and pick a suitable configuration regarding audio port type and channel count. static CLAP_CONSTEXPR const char CLAP_EXT_TRACK_INFO[] = "clap.track-info/1"; // The latest draft is 100% compatible. // This compat ID may be removed in 2026. static CLAP_CONSTEXPR const char CLAP_EXT_TRACK_INFO_COMPAT[] = "clap.track-info.draft/1"; #ifdef __cplusplus extern "C" { #endif enum { CLAP_TRACK_INFO_HAS_TRACK_NAME = (1 << 0), CLAP_TRACK_INFO_HAS_TRACK_COLOR = (1 << 1), CLAP_TRACK_INFO_HAS_AUDIO_CHANNEL = (1 << 2), // This plugin is on a return track, initialize with wet 100% CLAP_TRACK_INFO_IS_FOR_RETURN_TRACK = (1 << 3), // This plugin is on a bus track, initialize with appropriate settings for bus processing CLAP_TRACK_INFO_IS_FOR_BUS = (1 << 4), // This plugin is on the master, initialize with appropriate settings for channel processing CLAP_TRACK_INFO_IS_FOR_MASTER = (1 << 5), }; typedef struct clap_track_info { uint64_t flags; // see the flags above // track name, available if flags contain CLAP_TRACK_INFO_HAS_TRACK_NAME char name[CLAP_NAME_SIZE]; // track color, available if flags contain CLAP_TRACK_INFO_HAS_TRACK_COLOR clap_color_t color; // available if flags contain CLAP_TRACK_INFO_HAS_AUDIO_CHANNEL // see audio-ports.h, struct clap_audio_port_info to learn how to use channel count and port type int32_t audio_channel_count; const char *audio_port_type; } clap_track_info_t; typedef struct clap_plugin_track_info { // Called when the info changes. // [main-thread] void(CLAP_ABI *changed)(const clap_plugin_t *plugin); } clap_plugin_track_info_t; typedef struct clap_host_track_info { // Get info about the track the plugin belongs to. // Returns true on success and stores the result into info. // [main-thread] bool(CLAP_ABI *get)(const clap_host_t *host, clap_track_info_t *info); } clap_host_track_info_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/audio-ports.h0000644000000000000000000000013215101070323021722 xustar0030 mtime=1761898707.884347704 30 atime=1761898707.884347704 30 ctime=1761898707.884347704 qtractor-1.5.9/src/clap/include/clap/ext/audio-ports.h0000644000175000001440000001001415101070323021706 0ustar00rncbcusers#pragma once #include "../plugin.h" #include "../string-sizes.h" /// @page Audio Ports /// /// This extension provides a way for the plugin to describe its current audio ports. /// /// If the plugin does not implement this extension, it won't have audio ports. /// /// 32 bits support is required for both host and plugins. 64 bits audio is optional. /// /// The plugin is only allowed to change its ports configuration while it is deactivated. static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS[] = "clap.audio-ports"; static CLAP_CONSTEXPR const char CLAP_PORT_MONO[] = "mono"; static CLAP_CONSTEXPR const char CLAP_PORT_STEREO[] = "stereo"; #ifdef __cplusplus extern "C" { #endif enum { // This port is the main audio input or output. // There can be only one main input and main output. // Main port must be at index 0. CLAP_AUDIO_PORT_IS_MAIN = 1 << 0, // This port can be used with 64 bits audio CLAP_AUDIO_PORT_SUPPORTS_64BITS = 1 << 1, // 64 bits audio is preferred with this port CLAP_AUDIO_PORT_PREFERS_64BITS = 1 << 2, // This port must be used with the same sample size as all the other ports which have this flag. // In other words if all ports have this flag then the plugin may either be used entirely with // 64 bits audio or 32 bits audio, but it can't be mixed. CLAP_AUDIO_PORT_REQUIRES_COMMON_SAMPLE_SIZE = 1 << 3, }; typedef struct clap_audio_port_info { // id identifies a port and must be stable. // id may overlap between input and output ports. clap_id id; char name[CLAP_NAME_SIZE]; // displayable name uint32_t flags; uint32_t channel_count; // If null or empty then it is unspecified (arbitrary audio). // This field can be compared against: // - CLAP_PORT_MONO // - CLAP_PORT_STEREO // - CLAP_PORT_SURROUND (defined in the surround extension) // - CLAP_PORT_AMBISONIC (defined in the ambisonic extension) // // An extension can provide its own port type and way to inspect the channels. const char *port_type; // in-place processing: allow the host to use the same buffer for input and output // if supported set the pair port id. // if not supported set to CLAP_INVALID_ID clap_id in_place_pair; } clap_audio_port_info_t; // The audio ports scan has to be done while the plugin is deactivated. typedef struct clap_plugin_audio_ports { // Number of ports, for either input or output // [main-thread] uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin, bool is_input); // Get info about an audio port. // Returns true on success and stores the result into info. // [main-thread] bool(CLAP_ABI *get)(const clap_plugin_t *plugin, uint32_t index, bool is_input, clap_audio_port_info_t *info); } clap_plugin_audio_ports_t; enum { // The ports name did change, the host can scan them right away. CLAP_AUDIO_PORTS_RESCAN_NAMES = 1 << 0, // [!active] The flags did change CLAP_AUDIO_PORTS_RESCAN_FLAGS = 1 << 1, // [!active] The channel_count did change CLAP_AUDIO_PORTS_RESCAN_CHANNEL_COUNT = 1 << 2, // [!active] The port type did change CLAP_AUDIO_PORTS_RESCAN_PORT_TYPE = 1 << 3, // [!active] The in-place pair did change, this requires. CLAP_AUDIO_PORTS_RESCAN_IN_PLACE_PAIR = 1 << 4, // [!active] The list of ports have changed: entries have been removed/added. CLAP_AUDIO_PORTS_RESCAN_LIST = 1 << 5, }; typedef struct clap_host_audio_ports { // Checks if the host allows a plugin to change a given aspect of the audio ports definition. // [main-thread] bool(CLAP_ABI *is_rescan_flag_supported)(const clap_host_t *host, uint32_t flag); // Rescan the full list of audio ports according to the flags. // It is illegal to ask the host to rescan with a flag that is not supported. // Certain flags require the plugin to be de-activated. // [main-thread] void(CLAP_ABI *rescan)(const clap_host_t *host, uint32_t flags); } clap_host_audio_ports_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/ambisonic.h0000644000000000000000000000013215101070323021420 xustar0030 mtime=1761898707.883940074 30 atime=1761898707.883940074 30 ctime=1761898707.883940074 qtractor-1.5.9/src/clap/include/clap/ext/ambisonic.h0000644000175000001440000000366415101070323021421 0ustar00rncbcusers#pragma once #include "../plugin.h" // This extension can be used to specify the channel mapping used by the plugin. static CLAP_CONSTEXPR const char CLAP_EXT_AMBISONIC[] = "clap.ambisonic/3"; // The latest draft is 100% compatible. // This compat ID may be removed in 2026. static CLAP_CONSTEXPR const char CLAP_EXT_AMBISONIC_COMPAT[] = "clap.ambisonic.draft/3"; static CLAP_CONSTEXPR const char CLAP_PORT_AMBISONIC[] = "ambisonic"; #ifdef __cplusplus extern "C" { #endif enum clap_ambisonic_ordering { // FuMa channel ordering CLAP_AMBISONIC_ORDERING_FUMA = 0, // ACN channel ordering CLAP_AMBISONIC_ORDERING_ACN = 1, }; enum clap_ambisonic_normalization { CLAP_AMBISONIC_NORMALIZATION_MAXN = 0, CLAP_AMBISONIC_NORMALIZATION_SN3D = 1, CLAP_AMBISONIC_NORMALIZATION_N3D = 2, CLAP_AMBISONIC_NORMALIZATION_SN2D = 3, CLAP_AMBISONIC_NORMALIZATION_N2D = 4, }; typedef struct clap_ambisonic_config { uint32_t ordering; // see clap_ambisonic_ordering uint32_t normalization; // see clap_ambisonic_normalization } clap_ambisonic_config_t; typedef struct clap_plugin_ambisonic { // Returns true if the given configuration is supported. // [main-thread] bool(CLAP_ABI *is_config_supported)(const clap_plugin_t *plugin, const clap_ambisonic_config_t *config); // Returns true on success // [main-thread] bool(CLAP_ABI *get_config)(const clap_plugin_t *plugin, bool is_input, uint32_t port_index, clap_ambisonic_config_t *config); } clap_plugin_ambisonic_t; typedef struct clap_host_ambisonic { // Informs the host that the info has changed. // The info can only change when the plugin is de-activated. // [main-thread] void(CLAP_ABI *changed)(const clap_host_t *host); } clap_host_ambisonic_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/configurable-audio-ports.h0000644000000000000000000000013215101070323024360 xustar0030 mtime=1761898707.884347704 30 atime=1761898707.884347704 30 ctime=1761898707.884347704 qtractor-1.5.9/src/clap/include/clap/ext/configurable-audio-ports.h0000644000175000001440000000445715101070323024362 0ustar00rncbcusers#pragma once #include "audio-ports.h" #ifdef __cplusplus extern "C" { #endif // This extension lets the host configure the plugin's input and output audio ports. // This is a "push" approach to audio ports configuration. static CLAP_CONSTEXPR const char CLAP_EXT_CONFIGURABLE_AUDIO_PORTS[] = "clap.configurable-audio-ports/1"; // The latest draft is 100% compatible. // This compat ID may be removed in 2026. static CLAP_CONSTEXPR const char CLAP_EXT_CONFIGURABLE_AUDIO_PORTS_COMPAT[] = "clap.configurable-audio-ports.draft1"; typedef struct clap_audio_port_configuration_request { // Identifies the port by is_input and port_index bool is_input; uint32_t port_index; // The requested number of channels. uint32_t channel_count; // The port type, see audio-ports.h, clap_audio_port_info.port_type for interpretation. const char *port_type; // cast port_details according to port_type: // - CLAP_PORT_MONO: (discard) // - CLAP_PORT_STEREO: (discard) // - CLAP_PORT_SURROUND: const uint8_t *channel_map // - CLAP_PORT_AMBISONIC: const clap_ambisonic_config_t *info const void *port_details; } clap_audio_port_configuration_request_t; typedef struct clap_plugin_configurable_audio_ports { // Returns true if the given configurations can be applied using apply_configuration(). // [main-thread && !active] bool(CLAP_ABI *can_apply_configuration)( const clap_plugin_t *plugin, const struct clap_audio_port_configuration_request *requests, uint32_t request_count); // Submit a bunch of configuration requests which will atomically be applied together, // or discarded together. // // Once the configuration is successfully applied, it isn't necessary for the plugin to call // clap_host_audio_ports->changed(); and it isn't necessary for the host to scan the // audio ports. // // Returns true if applied. // [main-thread && !active] bool(CLAP_ABI *apply_configuration)(const clap_plugin_t *plugin, const struct clap_audio_port_configuration_request *requests, uint32_t request_count); } clap_plugin_configurable_audio_ports_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/note-ports.h0000644000000000000000000000013215101070323021566 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/note-ports.h0000644000175000001440000000514315101070323021561 0ustar00rncbcusers#pragma once #include "../plugin.h" #include "../string-sizes.h" /// @page Note Ports /// /// This extension provides a way for the plugin to describe its current note ports. /// If the plugin does not implement this extension, it won't have note input or output. /// The plugin is only allowed to change its note ports configuration while it is deactivated. static CLAP_CONSTEXPR const char CLAP_EXT_NOTE_PORTS[] = "clap.note-ports"; #ifdef __cplusplus extern "C" { #endif enum clap_note_dialect { // Uses clap_event_note and clap_event_note_expression. CLAP_NOTE_DIALECT_CLAP = 1 << 0, // Uses clap_event_midi, no polyphonic expression CLAP_NOTE_DIALECT_MIDI = 1 << 1, // Uses clap_event_midi, with polyphonic expression (MPE) CLAP_NOTE_DIALECT_MIDI_MPE = 1 << 2, // Uses clap_event_midi2 CLAP_NOTE_DIALECT_MIDI2 = 1 << 3, }; typedef struct clap_note_port_info { // id identifies a port and must be stable. // id may overlap between input and output ports. clap_id id; uint32_t supported_dialects; // bitfield, see clap_note_dialect uint32_t preferred_dialect; // one value of clap_note_dialect char name[CLAP_NAME_SIZE]; // displayable name, i18n? } clap_note_port_info_t; // The note ports scan has to be done while the plugin is deactivated. typedef struct clap_plugin_note_ports { // Number of ports, for either input or output. // [main-thread] uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin, bool is_input); // Get info about a note port. // Returns true on success and stores the result into info. // [main-thread] bool(CLAP_ABI *get)(const clap_plugin_t *plugin, uint32_t index, bool is_input, clap_note_port_info_t *info); } clap_plugin_note_ports_t; enum { // The ports have changed, the host shall perform a full scan of the ports. // This flag can only be used if the plugin is not active. // If the plugin active, call host->request_restart() and then call rescan() // when the host calls deactivate() CLAP_NOTE_PORTS_RESCAN_ALL = 1 << 0, // The ports name did change, the host can scan them right away. CLAP_NOTE_PORTS_RESCAN_NAMES = 1 << 1, }; typedef struct clap_host_note_ports { // Query which dialects the host supports // [main-thread] uint32_t(CLAP_ABI *supported_dialects)(const clap_host_t *host); // Rescan the full list of note ports according to the flags. // [main-thread] void(CLAP_ABI *rescan)(const clap_host_t *host, uint32_t flags); } clap_host_note_ports_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/thread-check.h0000644000000000000000000000013215101070323021776 xustar0030 mtime=1761898707.885314581 30 atime=1761898707.885314581 30 ctime=1761898707.885314581 qtractor-1.5.9/src/clap/include/clap/ext/thread-check.h0000644000175000001440000000717415101070323021777 0ustar00rncbcusers#pragma once #include "../plugin.h" static CLAP_CONSTEXPR const char CLAP_EXT_THREAD_CHECK[] = "clap.thread-check"; #ifdef __cplusplus extern "C" { #endif /// @page thread-check /// /// CLAP defines two symbolic threads: /// /// main-thread: /// This is the thread in which most of the interaction between the plugin and host happens. /// This will be the same OS thread throughout the lifetime of the plug-in. /// On macOS and Windows, this must be the thread on which gui and timer events are received /// (i.e., the main thread of the program). /// It isn't a realtime thread, yet this thread needs to respond fast enough to allow responsive /// user interaction, so it is strongly recommended plugins run long,and expensive or blocking /// tasks such as preset indexing or asset loading in dedicated background threads started by the /// plugin. /// /// audio-thread: /// This thread can be used for realtime audio processing. Its execution should be as /// deterministic as possible to meet the audio interface's deadline (can be <1ms). There are a /// known set of operations that should be avoided: malloc() and free(), contended locks and /// mutexes, I/O, waiting, and so forth. /// /// The audio-thread is symbolic, there isn't one OS thread that remains the /// audio-thread for the plugin lifetime. A host is may opt to have a /// thread pool and the plugin.process() call may be scheduled on different OS threads over time. /// However, the host must guarantee that single plugin instance will not be two audio-threads /// at the same time. /// /// Functions marked with [audio-thread] **ARE NOT CONCURRENT**. The host may mark any OS thread, /// including the main-thread as the audio-thread, as long as it can guarantee that only one OS /// thread is the audio-thread at a time in a plugin instance. The audio-thread can be seen as a /// concurrency guard for all functions marked with [audio-thread]. /// /// The real-time constraint on the [audio-thread] interacts closely with the render extension. /// If a plugin doesn't implement render, then that plugin must have all [audio-thread] functions /// meet the real time standard. If the plugin does implement render, and returns true when /// render mode is set to real-time or if the plugin advertises a hard realtime requirement, it /// must implement realtime constraints. Hosts also provide functions marked [audio-thread]. /// These can be safely called by a plugin in the audio thread. Therefore hosts must either (1) /// implement those functions meeting the real-time constraints or (2) not process plugins which /// advertise a hard realtime constraint or don't implement the render extension. Hosts which /// provide [audio-thread] functions outside these conditions may experience inconsistent or /// inaccurate rendering. /// /// Clap also tags some functions as [thread-safe]. Functions tagged as [thread-safe] can be called /// from any thread unless explicitly counter-indicated (for instance [thread-safe, !audio-thread]) /// and may be called concurrently. // This interface is useful to do runtime checks and make // sure that the functions are called on the correct threads. // It is highly recommended that hosts implement this extension. typedef struct clap_host_thread_check { // Returns true if "this" thread is the main thread. // [thread-safe] bool(CLAP_ABI *is_main_thread)(const clap_host_t *host); // Returns true if "this" thread is one of the audio threads. // [thread-safe] bool(CLAP_ABI *is_audio_thread)(const clap_host_t *host); } clap_host_thread_check_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/state.h0000644000000000000000000000013215101070323020574 xustar0030 mtime=1761898707.885314581 30 atime=1761898707.885314581 30 ctime=1761898707.885314581 qtractor-1.5.9/src/clap/include/clap/ext/state.h0000644000175000001440000000277015101070323020572 0ustar00rncbcusers#pragma once #include "../plugin.h" #include "../stream.h" /// @page State /// @brief state management /// /// Plugins can implement this extension to save and restore both parameter /// values and non-parameter state. This is used to persist a plugin's state /// between project reloads, when duplicating and copying plugin instances, and /// for host-side preset management. /// /// If you need to know if the save/load operation is meant for duplicating a plugin /// instance, for saving/loading a plugin preset or while saving/loading the project /// then consider implementing CLAP_EXT_STATE_CONTEXT in addition to CLAP_EXT_STATE. static CLAP_CONSTEXPR const char CLAP_EXT_STATE[] = "clap.state"; #ifdef __cplusplus extern "C" { #endif typedef struct clap_plugin_state { // Saves the plugin state into stream. // Returns true if the state was correctly saved. // [main-thread] bool(CLAP_ABI *save)(const clap_plugin_t *plugin, const clap_ostream_t *stream); // Loads the plugin state from stream. // Returns true if the state was correctly restored. // [main-thread] bool(CLAP_ABI *load)(const clap_plugin_t *plugin, const clap_istream_t *stream); } clap_plugin_state_t; typedef struct clap_host_state { // Tell the host that the plugin state has changed and should be saved again. // If a parameter value changes, then it is implicit that the state is dirty. // [main-thread] void(CLAP_ABI *mark_dirty)(const clap_host_t *host); } clap_host_state_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/audio-ports-config.h0000644000000000000000000000013215101070323023165 xustar0030 mtime=1761898707.884347704 30 atime=1761898707.884347704 30 ctime=1761898707.884347704 qtractor-1.5.9/src/clap/include/clap/ext/audio-ports-config.h0000644000175000001440000000766315101070323023171 0ustar00rncbcusers#pragma once #include "../string-sizes.h" #include "../plugin.h" #include "audio-ports.h" /// @page Audio Ports Config /// /// This extension let the plugin provide port configurations presets. /// For example mono, stereo, surround, ambisonic, ... /// /// After the plugin initialization, the host may scan the list of configurations and eventually /// select one that fits the plugin context. The host can only select a configuration if the plugin /// is deactivated. /// /// A configuration is a very simple description of the audio ports: /// - it describes the main input and output ports /// - it has a name that can be displayed to the user /// /// The idea behind the configurations, is to let the user choose one via a menu. /// /// Plugins with very complex configuration possibilities should let the user configure the ports /// from the plugin GUI, and call @ref clap_host_audio_ports.rescan(CLAP_AUDIO_PORTS_RESCAN_ALL). /// /// To inquire the exact bus layout, the plugin implements the clap_plugin_audio_ports_config_info_t /// extension where all busses can be retrieved in the same way as in the audio-port extension. static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_CONFIG[] = "clap.audio-ports-config"; static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_CONFIG_INFO[] = "clap.audio-ports-config-info/1"; // The latest draft is 100% compatible. // This compat ID may be removed in 2026. static CLAP_CONSTEXPR const char CLAP_EXT_AUDIO_PORTS_CONFIG_INFO_COMPAT[] = "clap.audio-ports-config-info/draft-0"; #ifdef __cplusplus extern "C" { #endif // Minimalistic description of ports configuration typedef struct clap_audio_ports_config { clap_id id; char name[CLAP_NAME_SIZE]; uint32_t input_port_count; uint32_t output_port_count; // main input info bool has_main_input; uint32_t main_input_channel_count; const char *main_input_port_type; // main output info bool has_main_output; uint32_t main_output_channel_count; const char *main_output_port_type; } clap_audio_ports_config_t; // The audio ports config scan has to be done while the plugin is deactivated. typedef struct clap_plugin_audio_ports_config { // Gets the number of available configurations // [main-thread] uint32_t(CLAP_ABI *count)(const clap_plugin_t *plugin); // Gets information about a configuration // Returns true on success and stores the result into config. // [main-thread] bool(CLAP_ABI *get)(const clap_plugin_t *plugin, uint32_t index, clap_audio_ports_config_t *config); // Selects the configuration designated by id // Returns true if the configuration could be applied. // Once applied the host should scan again the audio ports. // [main-thread & plugin-deactivated] bool(CLAP_ABI *select)(const clap_plugin_t *plugin, clap_id config_id); } clap_plugin_audio_ports_config_t; // Extended config info typedef struct clap_plugin_audio_ports_config_info { // Gets the id of the currently selected config, or CLAP_INVALID_ID if the current port // layout isn't part of the config list. // // [main-thread] clap_id(CLAP_ABI *current_config)(const clap_plugin_t *plugin); // Get info about an audio port, for a given config_id. // This is analogous to clap_plugin_audio_ports.get(). // Returns true on success and stores the result into info. // [main-thread] bool(CLAP_ABI *get)(const clap_plugin_t *plugin, clap_id config_id, uint32_t port_index, bool is_input, clap_audio_port_info_t *info); } clap_plugin_audio_ports_config_info_t; typedef struct clap_host_audio_ports_config { // Rescan the full list of configs. // [main-thread] void(CLAP_ABI *rescan)(const clap_host_t *host); } clap_host_audio_ports_config_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/param-indication.h0000644000000000000000000000013215101070323022673 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/param-indication.h0000644000175000001440000000603215101070323022664 0ustar00rncbcusers#pragma once #include "params.h" #include "../color.h" // This extension lets the host tell the plugin to display a little color based indication on the // parameter. This can be used to indicate: // - a physical controller is mapped to a parameter // - the parameter is current playing an automation // - the parameter is overriding the automation // - etc... // // The color semantic depends upon the host here and the goal is to have a consistent experience // across all plugins. static CLAP_CONSTEXPR const char CLAP_EXT_PARAM_INDICATION[] = "clap.param-indication/4"; // The latest draft is 100% compatible. // This compat ID may be removed in 2026. static CLAP_CONSTEXPR const char CLAP_EXT_PARAM_INDICATION_COMPAT[] = "clap.param-indication.draft/4"; #ifdef __cplusplus extern "C" { #endif enum { // The host doesn't have an automation for this parameter CLAP_PARAM_INDICATION_AUTOMATION_NONE = 0, // The host has an automation for this parameter, but it isn't playing it CLAP_PARAM_INDICATION_AUTOMATION_PRESENT = 1, // The host is playing an automation for this parameter CLAP_PARAM_INDICATION_AUTOMATION_PLAYING = 2, // The host is recording an automation on this parameter CLAP_PARAM_INDICATION_AUTOMATION_RECORDING = 3, // The host should play an automation for this parameter, but the user has started to adjust this // parameter and is overriding the automation playback CLAP_PARAM_INDICATION_AUTOMATION_OVERRIDING = 4, }; typedef struct clap_plugin_param_indication { // Sets or clears a mapping indication. // // has_mapping: does the parameter currently has a mapping? // color: if set, the color to use to highlight the control in the plugin GUI // label: if set, a small string to display on top of the knob which identifies the hardware // controller description: if set, a string which can be used in a tooltip, which describes the // current mapping // // Parameter indications should not be saved in the plugin context, and are off by default. // [main-thread] void(CLAP_ABI *set_mapping)(const clap_plugin_t *plugin, clap_id param_id, bool has_mapping, const clap_color_t *color, const char *label, const char *description); // Sets or clears an automation indication. // // automation_state: current automation state for the given parameter // color: if set, the color to use to display the automation indication in the plugin GUI // // Parameter indications should not be saved in the plugin context, and are off by default. // [main-thread] void(CLAP_ABI *set_automation)(const clap_plugin_t *plugin, clap_id param_id, uint32_t automation_state, const clap_color_t *color); } clap_plugin_param_indication_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/preset-load.h0000644000000000000000000000013215101070323021673 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/preset-load.h0000644000175000001440000000403115101070323021661 0ustar00rncbcusers#pragma once #include "../plugin.h" static CLAP_CONSTEXPR const char CLAP_EXT_PRESET_LOAD[] = "clap.preset-load/2"; // The latest draft is 100% compatible. // This compat ID may be removed in 2026. static CLAP_CONSTEXPR const char CLAP_EXT_PRESET_LOAD_COMPAT[] = "clap.preset-load.draft/2"; #ifdef __cplusplus extern "C" { #endif typedef struct clap_plugin_preset_load { // Loads a preset in the plugin native preset file format from a location. // The preset discovery provider defines the location and load_key to be passed to this function. // Returns true on success. // [main-thread] bool(CLAP_ABI *from_location)(const clap_plugin_t *plugin, uint32_t location_kind, const char *location, const char *load_key); } clap_plugin_preset_load_t; typedef struct clap_host_preset_load { // Called if clap_plugin_preset_load.load() failed. // os_error: the operating system error, if applicable. If not applicable set it to a non-error // value, eg: 0 on unix and Windows. // // [main-thread] void(CLAP_ABI *on_error)(const clap_host_t *host, uint32_t location_kind, const char *location, const char *load_key, int32_t os_error, const char *msg); // Informs the host that the following preset has been loaded. // This contributes to keep in sync the host preset browser and plugin preset browser. // If the preset was loaded from a container file, then the load_key must be set, otherwise it // must be null. // // [main-thread] void(CLAP_ABI *loaded)(const clap_host_t *host, uint32_t location_kind, const char *location, const char *load_key); } clap_host_preset_load_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/ext/PaxHeaders/gui.h0000644000000000000000000000013215101070323020240 xustar0030 mtime=1761898707.884669607 30 atime=1761898707.884669607 30 ctime=1761898707.884669607 qtractor-1.5.9/src/clap/include/clap/ext/gui.h0000644000175000001440000002322115101070323020230 0ustar00rncbcusers#pragma once #include "../plugin.h" /// @page GUI /// /// This extension defines how the plugin will present its GUI. /// /// There are two approaches: /// 1. the plugin creates a window and embeds it into the host's window /// 2. the plugin creates a floating window /// /// Embedding the window gives more control to the host, and feels more integrated. /// Floating window are sometimes the only option due to technical limitations. /// /// The Embedding protocol is by far the most common, supported by all hosts to date, /// and a plugin author should support at least that case. /// /// Showing the GUI works as follow: /// 1. clap_plugin_gui->is_api_supported(), check what can work /// 2. clap_plugin_gui->create(), allocates gui resources /// 3. if the plugin window is floating /// 4. -> clap_plugin_gui->set_transient() /// 5. -> clap_plugin_gui->suggest_title() /// 6. else /// 7. -> clap_plugin_gui->set_scale() /// 8. -> clap_plugin_gui->can_resize() /// 9. -> if resizable and has known size from previous session, clap_plugin_gui->set_size() /// 10. -> else clap_plugin_gui->get_size(), gets initial size /// 11. -> clap_plugin_gui->set_parent() /// 12. clap_plugin_gui->show() /// 13. clap_plugin_gui->hide()/show() ... /// 14. clap_plugin_gui->destroy() when done with the gui /// /// Resizing the window (initiated by the plugin, if embedded): /// 1. Plugins calls clap_host_gui->request_resize() /// 2. If the host returns true the new size is accepted, /// the host doesn't have to call clap_plugin_gui->set_size(). /// If the host returns false, the new size is rejected. /// /// Resizing the window (drag, if embedded)): /// 1. Only possible if clap_plugin_gui->can_resize() returns true /// 2. Mouse drag -> new_size /// 3. clap_plugin_gui->adjust_size(new_size) -> working_size /// 4. clap_plugin_gui->set_size(working_size) static CLAP_CONSTEXPR const char CLAP_EXT_GUI[] = "clap.gui"; // If your windowing API is not listed here, please open an issue and we'll figure it out. // https://github.com/free-audio/clap/issues/new // uses physical size // embed using https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setparent static const CLAP_CONSTEXPR char CLAP_WINDOW_API_WIN32[] = "win32"; // uses logical size, don't call clap_plugin_gui->set_scale() static const CLAP_CONSTEXPR char CLAP_WINDOW_API_COCOA[] = "cocoa"; // uses physical size // embed using https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html static const CLAP_CONSTEXPR char CLAP_WINDOW_API_X11[] = "x11"; // uses physical size // embed is currently not supported, use floating windows static const CLAP_CONSTEXPR char CLAP_WINDOW_API_WAYLAND[] = "wayland"; #ifdef __cplusplus extern "C" { #endif typedef void *clap_hwnd; typedef void *clap_nsview; typedef unsigned long clap_xwnd; // Represent a window reference. typedef struct clap_window { const char *api; // one of CLAP_WINDOW_API_XXX union { clap_nsview cocoa; clap_xwnd x11; clap_hwnd win32; void *ptr; // for anything defined outside of clap }; } clap_window_t; // Information to improve window resizing when initiated by the host or window manager. typedef struct clap_gui_resize_hints { bool can_resize_horizontally; bool can_resize_vertically; // if both horizontal and vertical resize are available, do we preserve the // aspect ratio, and if so, what is the width x height aspect ratio to preserve. // These flags are unused if can_resize_horizontally or vertically are false, // and ratios are unused if preserve is false. bool preserve_aspect_ratio; uint32_t aspect_ratio_width; uint32_t aspect_ratio_height; } clap_gui_resize_hints_t; // Size (width, height) is in pixels; the corresponding windowing system extension is // responsible for defining if it is physical pixels or logical pixels. typedef struct clap_plugin_gui { // Returns true if the requested gui api is supported, either in floating (plugin-created) // or non-floating (embedded) mode. // [main-thread] bool(CLAP_ABI *is_api_supported)(const clap_plugin_t *plugin, const char *api, bool is_floating); // Returns true if the plugin has a preferred api. // The host has no obligation to honor the plugin preference, this is just a hint. // The const char **api variable should be explicitly assigned as a pointer to // one of the CLAP_WINDOW_API_ constants defined above, not strcopied. // [main-thread] bool(CLAP_ABI *get_preferred_api)(const clap_plugin_t *plugin, const char **api, bool *is_floating); // Create and allocate all resources necessary for the gui. // // If is_floating is true, then the window will not be managed by the host. The plugin // can set its window to stays above the parent window, see set_transient(). // api may be null or blank for floating window. // // If is_floating is false, then the plugin has to embed its window into the parent window, see // set_parent(). // // After this call, the GUI may not be visible yet; don't forget to call show(). // // Returns true if the GUI is successfully created. // [main-thread] bool(CLAP_ABI *create)(const clap_plugin_t *plugin, const char *api, bool is_floating); // Free all resources associated with the gui. // [main-thread] void(CLAP_ABI *destroy)(const clap_plugin_t *plugin); // Set the absolute GUI scaling factor, and override any OS info. // Should not be used if the windowing api relies upon logical pixels. // // If the plugin prefers to work out the scaling factor itself by querying the OS directly, // then ignore the call. // // scale = 2 means 200% scaling. // // Returns true if the scaling could be applied // Returns false if the call was ignored, or the scaling could not be applied. // [main-thread] bool(CLAP_ABI *set_scale)(const clap_plugin_t *plugin, double scale); // Get the current size of the plugin UI. // clap_plugin_gui->create() must have been called prior to asking the size. // // Returns true if the plugin could get the size. // [main-thread] bool(CLAP_ABI *get_size)(const clap_plugin_t *plugin, uint32_t *width, uint32_t *height); // Returns true if the window is resizeable (mouse drag). // [main-thread & !floating] bool(CLAP_ABI *can_resize)(const clap_plugin_t *plugin); // Returns true if the plugin can provide hints on how to resize the window. // [main-thread & !floating] bool(CLAP_ABI *get_resize_hints)(const clap_plugin_t *plugin, clap_gui_resize_hints_t *hints); // If the plugin gui is resizable, then the plugin will calculate the closest // usable size which fits in the given size. // This method does not change the size. // // Returns true if the plugin could adjust the given size. // [main-thread & !floating] bool(CLAP_ABI *adjust_size)(const clap_plugin_t *plugin, uint32_t *width, uint32_t *height); // Sets the window size. // // Returns true if the plugin could resize its window to the given size. // [main-thread & !floating] bool(CLAP_ABI *set_size)(const clap_plugin_t *plugin, uint32_t width, uint32_t height); // Embeds the plugin window into the given window. // // Returns true on success. // [main-thread & !floating] bool(CLAP_ABI *set_parent)(const clap_plugin_t *plugin, const clap_window_t *window); // Set the plugin floating window to stay above the given window. // // Returns true on success. // [main-thread & floating] bool(CLAP_ABI *set_transient)(const clap_plugin_t *plugin, const clap_window_t *window); // Suggests a window title. Only for floating windows. // // [main-thread & floating] void(CLAP_ABI *suggest_title)(const clap_plugin_t *plugin, const char *title); // Show the window. // // Returns true on success. // [main-thread] bool(CLAP_ABI *show)(const clap_plugin_t *plugin); // Hide the window, this method does not free the resources, it just hides // the window content. Yet it may be a good idea to stop painting timers. // // Returns true on success. // [main-thread] bool(CLAP_ABI *hide)(const clap_plugin_t *plugin); } clap_plugin_gui_t; typedef struct clap_host_gui { // The host should call get_resize_hints() again. // [thread-safe & !floating] void(CLAP_ABI *resize_hints_changed)(const clap_host_t *host); // Request the host to resize the client area to width, height. // Return true if the new size is accepted, false otherwise. // The host doesn't have to call set_size(). // // Note: if not called from the main thread, then a return value simply means that the host // acknowledged the request and will process it asynchronously. If the request then can't be // satisfied then the host will call set_size() to revert the operation. // [thread-safe & !floating] bool(CLAP_ABI *request_resize)(const clap_host_t *host, uint32_t width, uint32_t height); // Request the host to show the plugin gui. // Return true on success, false otherwise. // [thread-safe] bool(CLAP_ABI *request_show)(const clap_host_t *host); // Request the host to hide the plugin gui. // Return true on success, false otherwise. // [thread-safe] bool(CLAP_ABI *request_hide)(const clap_host_t *host); // The floating window has been closed, or the connection to the gui has been lost. // // If was_destroyed is true, then the host must call clap_plugin_gui->destroy() to acknowledge // the gui destruction. // [thread-safe] void(CLAP_ABI *closed)(const clap_host_t *host, bool was_destroyed); } clap_host_gui_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/PaxHeaders/audio-buffer.h0000644000000000000000000000013215101070323021224 xustar0030 mtime=1761898707.883940074 30 atime=1761898707.883314574 30 ctime=1761898707.883940074 qtractor-1.5.9/src/clap/include/clap/audio-buffer.h0000644000175000001440000000207315101070323021216 0ustar00rncbcusers#pragma once #include "private/std.h" #ifdef __cplusplus extern "C" { #endif // Sample code for reading a stereo buffer: // // bool isLeftConstant = (buffer->constant_mask & (1 << 0)) != 0; // bool isRightConstant = (buffer->constant_mask & (1 << 1)) != 0; // // for (int i = 0; i < N; ++i) { // float l = data32[0][isLeftConstant ? 0 : i]; // float r = data32[1][isRightConstant ? 0 : i]; // } // // Note: checking the constant mask is optional, and this implies that // the buffer must be filled with the constant value. // Rationale: if a buffer reader doesn't check the constant mask, then it may // process garbage samples and in result, garbage samples may be transmitted // to the audio interface with all the bad consequences it can have. // // The constant mask is a hint. typedef struct clap_audio_buffer { // Either data32 or data64 pointer will be set. float **data32; double **data64; uint32_t channel_count; uint32_t latency; // latency from/to the audio interface uint64_t constant_mask; } clap_audio_buffer_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/PaxHeaders/host.h0000644000000000000000000000012715101070323017635 xustar0029 mtime=1761898707.88625123 29 atime=1761898707.88625123 29 ctime=1761898707.88625123 qtractor-1.5.9/src/clap/include/clap/host.h0000644000175000001440000000402315101070323017620 0ustar00rncbcusers#pragma once #include "version.h" #ifdef __cplusplus extern "C" { #endif typedef struct clap_host { clap_version_t clap_version; // initialized to CLAP_VERSION void *host_data; // reserved pointer for the host // name and version are mandatory. const char *name; // eg: "Bitwig Studio" const char *vendor; // eg: "Bitwig GmbH" const char *url; // eg: "https://bitwig.com" const char *version; // eg: "4.3", see plugin.h for advice on how to format the version // Query an extension. // The returned pointer is owned by the host. // It is forbidden to call it before plugin->init(). // You can call it within plugin->init() call, and after. // [thread-safe] const void *(CLAP_ABI *get_extension)(const struct clap_host *host, const char *extension_id); // Request the host to deactivate and then reactivate the plugin. // The operation may be delayed by the host. // [thread-safe] void(CLAP_ABI *request_restart)(const struct clap_host *host); // Request the host to activate and start processing the plugin. // This is useful if you have external IO and need to wake up the plugin from "sleep". // [thread-safe] void(CLAP_ABI *request_process)(const struct clap_host *host); // Request the host to schedule a call to plugin->on_main_thread(plugin) on the main thread. // This callback should be called as soon as practicable, usually in the host application's next // available main thread time slice. Typically callbacks occur within 33ms / 30hz. // Despite this guidance, plugins should not make assumptions about the exactness of timing for // a main thread callback, but hosts should endeavour to be prompt. For example, in high load // situations the environment may starve the gui/main thread in favor of audio processing, // leading to substantially longer latencies for the callback than the indicative times given // here. // [thread-safe] void(CLAP_ABI *request_callback)(const struct clap_host *host); } clap_host_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/PaxHeaders/stream.h0000644000000000000000000000013215101070323020147 xustar0030 mtime=1761898707.886701151 30 atime=1761898707.886701151 30 ctime=1761898707.886701151 qtractor-1.5.9/src/clap/include/clap/stream.h0000644000175000001440000000234015101070323020136 0ustar00rncbcusers#pragma once #include "private/std.h" #include "private/macros.h" /// @page Streams /// /// ## Notes on using streams /// /// When working with `clap_istream` and `clap_ostream` objects to load and save /// state, it is important to keep in mind that the host may limit the number of /// bytes that can be read or written at a time. The return values for the /// stream read and write functions indicate how many bytes were actually read /// or written. You need to use a loop to ensure that you read or write the /// entirety of your state. Don't forget to also consider the negative return /// values for the end of file and IO error codes. #ifdef __cplusplus extern "C" { #endif typedef struct clap_istream { void *ctx; // reserved pointer for the stream // returns the number of bytes read; 0 indicates end of file and -1 a read error int64_t(CLAP_ABI *read)(const struct clap_istream *stream, void *buffer, uint64_t size); } clap_istream_t; typedef struct clap_ostream { void *ctx; // reserved pointer for the stream // returns the number of bytes written; -1 on write error int64_t(CLAP_ABI *write)(const struct clap_ostream *stream, const void *buffer, uint64_t size); } clap_ostream_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/PaxHeaders/id.h0000644000000000000000000000012715101070323017254 xustar0029 mtime=1761898707.88625123 29 atime=1761898707.88625123 29 ctime=1761898707.88625123 qtractor-1.5.9/src/clap/include/clap/id.h0000644000175000001440000000024115101070323017235 0ustar00rncbcusers#pragma once #include "private/std.h" #include "private/macros.h" typedef uint32_t clap_id; static const CLAP_CONSTEXPR clap_id CLAP_INVALID_ID = UINT32_MAX; qtractor-1.5.9/src/clap/include/clap/PaxHeaders/universal-plugin-id.h0000644000000000000000000000013215101070323022552 xustar0030 mtime=1761898707.886701151 30 atime=1761898707.886701151 30 ctime=1761898707.886701151 qtractor-1.5.9/src/clap/include/clap/universal-plugin-id.h0000644000175000001440000000146215101070323022545 0ustar00rncbcusers#pragma once // Pair of plugin ABI and plugin identifier. // // If you want to represent other formats please send us an update to the comment with the // name of the abi and the representation of the id. typedef struct clap_universal_plugin_id { // The plugin ABI name, in lowercase and null-terminated. // eg: "clap", "vst3", "vst2", "au", ... const char *abi; // The plugin ID, null-terminated and formatted as follows: // // CLAP: use the plugin id // eg: "com.u-he.diva" // // AU: format the string like "type:subt:manu" // eg: "aumu:SgXT:VmbA" // // VST2: print the id as a signed 32-bits integer // eg: "-4382976" // // VST3: print the id as a standard UUID // eg: "123e4567-e89b-12d3-a456-426614174000" const char *id; } clap_universal_plugin_id_t; qtractor-1.5.9/src/clap/include/clap/PaxHeaders/factory0000644000000000000000000000013015101070323020073 xustar0029 mtime=1761898707.88625123 30 atime=1761898707.885314581 29 ctime=1761898707.88625123 qtractor-1.5.9/src/clap/include/clap/factory/0000755000175000001440000000000015101070323020142 5ustar00rncbcusersqtractor-1.5.9/src/clap/include/clap/factory/PaxHeaders/draft0000644000000000000000000000013015101070323021173 xustar0029 mtime=1761898707.88625123 30 atime=1761898707.885314581 29 ctime=1761898707.88625123 qtractor-1.5.9/src/clap/include/clap/factory/draft/0000755000175000001440000000000015101070323021242 5ustar00rncbcusersqtractor-1.5.9/src/clap/include/clap/factory/draft/PaxHeaders/plugin-state-converter.h0000644000000000000000000000013015101070323026042 xustar0029 mtime=1761898707.88625123 30 atime=1761898707.886200593 29 ctime=1761898707.88625123 qtractor-1.5.9/src/clap/include/clap/factory/draft/plugin-state-converter.h0000644000175000001440000001012015101070323026026 0ustar00rncbcusers#pragma once #include "../../id.h" #include "../../universal-plugin-id.h" #include "../../stream.h" #include "../../version.h" #ifdef __cplusplus extern "C" { #endif typedef struct clap_plugin_state_converter_descriptor { clap_version_t clap_version; clap_universal_plugin_id_t src_plugin_id; clap_universal_plugin_id_t dst_plugin_id; const char *id; // eg: "com.u-he.diva-converter", mandatory const char *name; // eg: "Diva Converter", mandatory const char *vendor; // eg: "u-he" const char *version; // eg: 1.1.5 const char *description; // eg: "Official state converter for u-he Diva." } clap_plugin_state_converter_descriptor_t; // This interface provides a mechanism for the host to convert a plugin state and its automation // points to a new plugin. // // This is useful to convert from one plugin ABI to another one. // This is also useful to offer an upgrade path: from EQ version 1 to EQ version 2. // This can also be used to convert the state of a plugin that isn't maintained anymore into // another plugin that would be similar. typedef struct clap_plugin_state_converter { const clap_plugin_state_converter_descriptor_t *desc; void *converter_data; // Destroy the converter. void (*destroy)(struct clap_plugin_state_converter *converter); // Converts the input state to a state usable by the destination plugin. // // error_buffer is a place holder of error_buffer_size bytes for storing a null-terminated // error message in case of failure, which can be displayed to the user. // // Returns true on success. // [thread-safe] bool (*convert_state)(struct clap_plugin_state_converter *converter, const clap_istream_t *src, const clap_ostream_t *dst, char *error_buffer, size_t error_buffer_size); // Converts a normalized value. // Returns true on success. // [thread-safe] bool (*convert_normalized_value)(struct clap_plugin_state_converter *converter, clap_id src_param_id, double src_normalized_value, clap_id *dst_param_id, double *dst_normalized_value); // Converts a plain value. // Returns true on success. // [thread-safe] bool (*convert_plain_value)(struct clap_plugin_state_converter *converter, clap_id src_param_id, double src_plain_value, clap_id *dst_param_id, double *dst_plain_value); } clap_plugin_state_converter_t; // Factory identifier static CLAP_CONSTEXPR const char CLAP_PLUGIN_STATE_CONVERTER_FACTORY_ID[] = "clap.plugin-state-converter-factory/1"; // List all the plugin state converters available in the current DSO. typedef struct clap_plugin_state_converter_factory { // Get the number of converters. // [thread-safe] uint32_t (*count)(const struct clap_plugin_state_converter_factory *factory); // Retrieves a plugin state converter descriptor by its index. // Returns null in case of error. // The descriptor must not be freed. // [thread-safe] const clap_plugin_state_converter_descriptor_t *(*get_descriptor)( const struct clap_plugin_state_converter_factory *factory, uint32_t index); // Create a plugin state converter by its converter_id. // The returned pointer must be freed by calling converter->destroy(converter); // Returns null in case of error. // [thread-safe] clap_plugin_state_converter_t *(*create)( const struct clap_plugin_state_converter_factory *factory, const char *converter_id); } clap_plugin_state_converter_factory_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/factory/draft/PaxHeaders/plugin-invalidation.h0000644000000000000000000000013215101070323025400 xustar0030 mtime=1761898707.886200593 30 atime=1761898707.886200593 30 ctime=1761898707.886200593 qtractor-1.5.9/src/clap/include/clap/factory/draft/plugin-invalidation.h0000644000175000001440000000335315101070323025374 0ustar00rncbcusers#pragma once #include "../../private/std.h" #include "../../private/macros.h" // Use it to retrieve const clap_plugin_invalidation_factory_t* from // clap_plugin_entry.get_factory() static const CLAP_CONSTEXPR char CLAP_PLUGIN_INVALIDATION_FACTORY_ID[] = "clap.plugin-invalidation-factory/1"; #ifdef __cplusplus extern "C" { #endif typedef struct clap_plugin_invalidation_source { // Directory containing the file(s) to scan, must be absolute const char *directory; // globing pattern, in the form *.dll const char *filename_glob; // should the directory be scanned recursively? bool recursive_scan; } clap_plugin_invalidation_source_t; // Used to figure out when a plugin needs to be scanned again. // Imagine a situation with a single entry point: my-plugin.clap which then scans itself // a set of "sub-plugins". New plugin may be available even if my-plugin.clap file doesn't change. // This interfaces solves this issue and gives a way to the host to monitor additional files. typedef struct clap_plugin_invalidation_factory { // Get the number of invalidation source. uint32_t(CLAP_ABI *count)(const struct clap_plugin_invalidation_factory *factory); // Get the invalidation source by its index. // [thread-safe] const clap_plugin_invalidation_source_t *(CLAP_ABI *get)( const struct clap_plugin_invalidation_factory *factory, uint32_t index); // In case the host detected a invalidation event, it can call refresh() to let the // plugin_entry update the set of plugins available. // If the function returned false, then the plugin needs to be reloaded. bool(CLAP_ABI *refresh)(const struct clap_plugin_invalidation_factory *factory); } clap_plugin_invalidation_factory_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/factory/PaxHeaders/plugin-factory.h0000644000000000000000000000012715101070323023272 xustar0029 mtime=1761898707.88625123 29 atime=1761898707.88625123 29 ctime=1761898707.88625123 qtractor-1.5.9/src/clap/include/clap/factory/plugin-factory.h0000644000175000001440000000302515101070323023256 0ustar00rncbcusers#pragma once #include "../plugin.h" // Use it to retrieve const clap_plugin_factory_t* from // clap_plugin_entry.get_factory() static const CLAP_CONSTEXPR char CLAP_PLUGIN_FACTORY_ID[] = "clap.plugin-factory"; #ifdef __cplusplus extern "C" { #endif // Every method must be thread-safe. // It is very important to be able to scan the plugin as quickly as possible. // // The host may use clap_plugin_invalidation_factory to detect filesystem changes // which may change the factory's content. typedef struct clap_plugin_factory { // Get the number of plugins available. // [thread-safe] uint32_t(CLAP_ABI *get_plugin_count)(const struct clap_plugin_factory *factory); // Retrieves a plugin descriptor by its index. // Returns null in case of error. // The descriptor must not be freed. // [thread-safe] const clap_plugin_descriptor_t *(CLAP_ABI *get_plugin_descriptor)( const struct clap_plugin_factory *factory, uint32_t index); // Create a clap_plugin by its plugin_id. // The returned pointer must be freed by calling plugin->destroy(plugin); // The plugin is not allowed to use the host callbacks in the create method. // Returns null in case of error. // [thread-safe] const clap_plugin_t *(CLAP_ABI *create_plugin)(const struct clap_plugin_factory *factory, const clap_host_t *host, const char *plugin_id); } clap_plugin_factory_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/factory/PaxHeaders/preset-discovery.h0000644000000000000000000000012715101070323023636 xustar0029 mtime=1761898707.88625123 29 atime=1761898707.88625123 29 ctime=1761898707.88625123 qtractor-1.5.9/src/clap/include/clap/factory/preset-discovery.h0000644000175000001440000003636515101070323023637 0ustar00rncbcusers/* Preset Discovery API. Preset Discovery enables a plug-in host to identify where presets are found, what extensions they have, which plug-ins they apply to, and other metadata associated with the presets so that they can be indexed and searched for quickly within the plug-in host's browser. This has a number of advantages for the user: - it allows them to browse for presets from one central location in a consistent way - the user can browse for presets without having to commit to a particular plug-in first The API works as follow to index presets and presets metadata: 1. clap_plugin_entry.get_factory(CLAP_PRESET_DISCOVERY_FACTORY_ID) 2. clap_preset_discovery_factory_t.create(...) 3. clap_preset_discovery_provider.init() (only necessary the first time, declarations can be cached) `-> clap_preset_discovery_indexer.declare_filetype() `-> clap_preset_discovery_indexer.declare_location() `-> clap_preset_discovery_indexer.declare_soundpack() (optional) `-> clap_preset_discovery_indexer.set_invalidation_watch_file() (optional) 4. crawl the given locations and monitor file system changes `-> clap_preset_discovery_indexer.get_metadata() for each presets files Then to load a preset, use ext/draft/preset-load.h. TODO: create a dedicated repo for other plugin abi preset-load extension. The design of this API deliberately does not define a fixed set tags or categories. It is the plug-in host's job to try to intelligently map the raw list of features that are found for a preset and to process this list to generate something that makes sense for the host's tagging and categorization system. The reason for this is to reduce the work for a plug-in developer to add Preset Discovery support for their existing preset file format and not have to be concerned with all the different hosts and how they want to receive the metadata. VERY IMPORTANT: - the whole indexing process has to be **fast** - clap_preset_provider->get_metadata() has to be fast and avoid unnecessary operations - the whole indexing process must not be interactive - don't show dialogs, windows, ... - don't ask for user input */ #pragma once #include "../private/std.h" #include "../private/macros.h" #include "../timestamp.h" #include "../version.h" #include "../universal-plugin-id.h" // Use it to retrieve const clap_preset_discovery_factory_t* from // clap_plugin_entry.get_factory() static const CLAP_CONSTEXPR char CLAP_PRESET_DISCOVERY_FACTORY_ID[] = "clap.preset-discovery-factory/2"; // The latest draft is 100% compatible. // This compat ID may be removed in 2026. static const CLAP_CONSTEXPR char CLAP_PRESET_DISCOVERY_FACTORY_ID_COMPAT[] = "clap.preset-discovery-factory/draft-2"; #ifdef __cplusplus extern "C" { #endif enum clap_preset_discovery_location_kind { // The preset are located in a file on the OS filesystem. // The location is then a path which works with the OS file system functions (open, stat, ...) // So both '/' and '\' shall work on Windows as a separator. CLAP_PRESET_DISCOVERY_LOCATION_FILE = 0, // The preset is bundled within the plugin DSO itself. // The location must then be null, as the preset are within the plugin itself and then the plugin // will act as a preset container. CLAP_PRESET_DISCOVERY_LOCATION_PLUGIN = 1, }; enum clap_preset_discovery_flags { // This is for factory or sound-pack presets. CLAP_PRESET_DISCOVERY_IS_FACTORY_CONTENT = 1 << 0, // This is for user presets. CLAP_PRESET_DISCOVERY_IS_USER_CONTENT = 1 << 1, // This location is meant for demo presets, those are preset which may trigger // some limitation in the plugin because they require additional features which the user // needs to purchase or the content itself needs to be bought and is only available in // demo mode. CLAP_PRESET_DISCOVERY_IS_DEMO_CONTENT = 1 << 2, // This preset is a user's favorite CLAP_PRESET_DISCOVERY_IS_FAVORITE = 1 << 3, }; // Receiver that receives the metadata for a single preset file. // The host would define the various callbacks in this interface and the preset parser function // would then call them. // // This interface isn't thread-safe. typedef struct clap_preset_discovery_metadata_receiver { void *receiver_data; // reserved pointer for the metadata receiver // If there is an error reading metadata from a file this should be called with an error // message. // os_error: the operating system error, if applicable. If not applicable set it to a non-error // value, eg: 0 on unix and Windows. void(CLAP_ABI *on_error)(const struct clap_preset_discovery_metadata_receiver *receiver, int32_t os_error, const char *error_message); // This must be called for every preset in the file and before any preset metadata is // sent with the calls below. // // If the preset file is a preset container then name and load_key are mandatory, otherwise // they are optional. // // The load_key is a machine friendly string used to load the preset inside the container via a // the preset-load plug-in extension. The load_key can also just be the subpath if that's what // the plugin wants but it could also be some other unique id like a database primary key or a // binary offset. It's use is entirely up to the plug-in. // // If the function returns false, then the provider must stop calling back into the receiver. bool(CLAP_ABI *begin_preset)(const struct clap_preset_discovery_metadata_receiver *receiver, const char *name, const char *load_key); // Adds a plug-in id that this preset can be used with. void(CLAP_ABI *add_plugin_id)(const struct clap_preset_discovery_metadata_receiver *receiver, const clap_universal_plugin_id_t *plugin_id); // Sets the sound pack to which the preset belongs to. void(CLAP_ABI *set_soundpack_id)(const struct clap_preset_discovery_metadata_receiver *receiver, const char *soundpack_id); // Sets the flags, see clap_preset_discovery_flags. // If unset, they are then inherited from the location. void(CLAP_ABI *set_flags)(const struct clap_preset_discovery_metadata_receiver *receiver, uint32_t flags); // Adds a creator name for the preset. void(CLAP_ABI *add_creator)(const struct clap_preset_discovery_metadata_receiver *receiver, const char *creator); // Sets a description of the preset. void(CLAP_ABI *set_description)(const struct clap_preset_discovery_metadata_receiver *receiver, const char *description); // Sets the creation time and last modification time of the preset. // If one of the times isn't known, set it to CLAP_TIMESTAMP_UNKNOWN. // If this function is not called, then the indexer may look at the file's creation and // modification time. void(CLAP_ABI *set_timestamps)(const struct clap_preset_discovery_metadata_receiver *receiver, clap_timestamp creation_time, clap_timestamp modification_time); // Adds a feature to the preset. // // The feature string is arbitrary, it is the indexer's job to understand it and remap it to its // internal categorization and tagging system. // // However, the strings from plugin-features.h should be understood by the indexer and one of the // plugin category could be provided to determine if the preset will result into an audio-effect, // instrument, ... // // Examples: // kick, drum, tom, snare, clap, cymbal, bass, lead, metalic, hardsync, crossmod, acid, // distorted, drone, pad, dirty, etc... void(CLAP_ABI *add_feature)(const struct clap_preset_discovery_metadata_receiver *receiver, const char *feature); // Adds extra information to the metadata. void(CLAP_ABI *add_extra_info)(const struct clap_preset_discovery_metadata_receiver *receiver, const char *key, const char *value); } clap_preset_discovery_metadata_receiver_t; typedef struct clap_preset_discovery_filetype { const char *name; const char *description; // optional // `.' isn't included in the string. // If empty or NULL then every file should be matched. const char *file_extension; } clap_preset_discovery_filetype_t; // Defines a place in which to search for presets typedef struct clap_preset_discovery_location { uint32_t flags; // see enum clap_preset_discovery_flags const char *name; // name of this location uint32_t kind; // See clap_preset_discovery_location_kind // Actual location in which to crawl presets. // For FILE kind, the location can be either a path to a directory or a file. // For PLUGIN kind, the location must be null. const char *location; } clap_preset_discovery_location_t; // Describes an installed sound pack. typedef struct clap_preset_discovery_soundpack { uint32_t flags; // see enum clap_preset_discovery_flags const char *id; // sound pack identifier const char *name; // name of this sound pack const char *description; // optional, reasonably short description of the sound pack const char *homepage_url; // optional, url to the pack's homepage const char *vendor; // optional, sound pack's vendor const char *image_path; // optional, an image on disk clap_timestamp release_timestamp; // release date, CLAP_TIMESTAMP_UNKNOWN if unavailable } clap_preset_discovery_soundpack_t; // Describes a preset provider typedef struct clap_preset_discovery_provider_descriptor { clap_version_t clap_version; // initialized to CLAP_VERSION const char *id; // see plugin.h for advice on how to choose a good identifier const char *name; // eg: "Diva's preset provider" const char *vendor; // optional, eg: u-he } clap_preset_discovery_provider_descriptor_t; // This interface isn't thread-safe. typedef struct clap_preset_discovery_provider { const clap_preset_discovery_provider_descriptor_t *desc; void *provider_data; // reserved pointer for the provider // Initialize the preset provider. // It should declare all its locations, filetypes and sound packs. // Returns false if initialization failed. bool(CLAP_ABI *init)(const struct clap_preset_discovery_provider *provider); // Destroys the preset provider void(CLAP_ABI *destroy)(const struct clap_preset_discovery_provider *provider); // reads metadata from the given file and passes them to the metadata receiver // Returns true on success. bool(CLAP_ABI *get_metadata)(const struct clap_preset_discovery_provider *provider, uint32_t location_kind, const char *location, const clap_preset_discovery_metadata_receiver_t *metadata_receiver); // Query an extension. // The returned pointer is owned by the provider. // It is forbidden to call it before provider->init(). // You can call it within provider->init() call, and after. const void *(CLAP_ABI *get_extension)(const struct clap_preset_discovery_provider *provider, const char *extension_id); } clap_preset_discovery_provider_t; // This interface isn't thread-safe typedef struct clap_preset_discovery_indexer { clap_version_t clap_version; // initialized to CLAP_VERSION const char *name; // eg: "Bitwig Studio" const char *vendor; // optional, eg: "Bitwig GmbH" const char *url; // optional, eg: "https://bitwig.com" const char *version; // optional, eg: "4.3", see plugin.h for advice on how to format the version void *indexer_data; // reserved pointer for the indexer // Declares a preset filetype. // Don't callback into the provider during this call. // Returns false if the filetype is invalid. bool(CLAP_ABI *declare_filetype)(const struct clap_preset_discovery_indexer *indexer, const clap_preset_discovery_filetype_t *filetype); // Declares a preset location. // Don't callback into the provider during this call. // Returns false if the location is invalid. bool(CLAP_ABI *declare_location)(const struct clap_preset_discovery_indexer *indexer, const clap_preset_discovery_location_t *location); // Declares a sound pack. // Don't callback into the provider during this call. // Returns false if the sound pack is invalid. bool(CLAP_ABI *declare_soundpack)(const struct clap_preset_discovery_indexer *indexer, const clap_preset_discovery_soundpack_t *soundpack); // Query an extension. // The returned pointer is owned by the indexer. // It is forbidden to call it before provider->init(). // You can call it within provider->init() call, and after. const void *(CLAP_ABI *get_extension)(const struct clap_preset_discovery_indexer *indexer, const char *extension_id); } clap_preset_discovery_indexer_t; // Every methods in this factory must be thread-safe. // It is encouraged to perform preset indexing in background threads, maybe even in background // process. // // The host may use clap_plugin_invalidation_factory to detect filesystem changes // which may change the factory's content. typedef struct clap_preset_discovery_factory { // Get the number of preset providers available. // [thread-safe] uint32_t(CLAP_ABI *count)(const struct clap_preset_discovery_factory *factory); // Retrieves a preset provider descriptor by its index. // Returns null in case of error. // The descriptor must not be freed. // [thread-safe] const clap_preset_discovery_provider_descriptor_t *(CLAP_ABI *get_descriptor)( const struct clap_preset_discovery_factory *factory, uint32_t index); // Create a preset provider by its id. // The returned pointer must be freed by calling preset_provider->destroy(preset_provider); // The preset provider is not allowed to use the indexer callbacks in the create method. // It is forbidden to call back into the indexer before the indexer calls provider->init(). // Returns null in case of error. // [thread-safe] const clap_preset_discovery_provider_t *(CLAP_ABI *create)( const struct clap_preset_discovery_factory *factory, const clap_preset_discovery_indexer_t *indexer, const char *provider_id); } clap_preset_discovery_factory_t; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/PaxHeaders/color.h0000644000000000000000000000013215101070323017772 xustar0030 mtime=1761898707.883940074 30 atime=1761898707.883940074 30 ctime=1761898707.883940074 qtractor-1.5.9/src/clap/include/clap/color.h0000644000175000001440000000046115101070323017763 0ustar00rncbcusers#pragma once #include "private/std.h" #ifdef __cplusplus extern "C" { #endif typedef struct clap_color { uint8_t alpha; uint8_t red; uint8_t green; uint8_t blue; } clap_color_t; static const CLAP_CONSTEXPR clap_color_t CLAP_COLOR_TRANSPARENT = { 0, 0, 0, 0 }; #ifdef __cplusplus } #endif qtractor-1.5.9/src/clap/include/clap/PaxHeaders/all.h0000644000000000000000000000013215101070323017424 xustar0030 mtime=1761898707.883314574 30 atime=1761898707.883314574 30 ctime=1761898707.883314574 qtractor-1.5.9/src/clap/include/clap/all.h0000644000175000001440000000100615101070323017411 0ustar00rncbcusers#pragma once #include "clap.h" #include "factory/draft/plugin-invalidation.h" #include "factory/draft/plugin-state-converter.h" #include "ext/draft/extensible-audio-ports.h" #include "ext/draft/gain-adjustment-metering.h" #include "ext/draft/mini-curve-display.h" #include "ext/draft/project-location.h" #include "ext/draft/resource-directory.h" #include "ext/draft/scratch-memory.h" #include "ext/draft/transport-control.h" #include "ext/draft/triggers.h" #include "ext/draft/tuning.h" #include "ext/draft/undo.h" qtractor-1.5.9/src/clap/PaxHeaders/CMakePresets.json0000644000000000000000000000013215101070323017362 xustar0030 mtime=1761898707.845700915 30 atime=1761898707.845700915 30 ctime=1761898707.845700915 qtractor-1.5.9/src/clap/CMakePresets.json0000644000175000001440000000353415101070323017357 0ustar00rncbcusers{ "version": 3, "cmakeMinimumRequired": { "major": 3, "minor": 17, "patch": 0 }, "configurePresets": [ { "name": "ninja", "displayName": "Ninja", "description": "Configure and generate Ninja project files for all configurations", "binaryDir": "${sourceDir}/builds/${presetName}", "generator": "Ninja Multi-Config", "cacheVariables": { "CMAKE_EXPORT_COMPILE_COMMANDS": { "type": "boolean", "value": true }, "CLAP_BUILD_TESTS": { "type": "boolean", "value": true } } }, { "name": "msvc", "displayName": "MSVC", "description": "Configure and generate MSVC project files for all configurations", "binaryDir": "${sourceDir}/builds/${presetName}", "generator": "Visual Studio 17 2022", "cacheVariables": { "CMAKE_EXPORT_COMPILE_COMMANDS": { "type": "boolean", "value": true }, "CLAP_BUILD_TESTS": { "type": "boolean", "value": true } } } ], "buildPresets": [ { "name": "ninja-release", "configurePreset": "ninja", "displayName": "Build ninja-release", "description": "Build ninja Release configuration", "configuration": "RelWithDebInfo", "targets": ["clap-tests"] }, { "name": "msvc-release", "configurePreset": "msvc", "displayName": "Build msvc-release", "description": "Build msvc Release configuration", "configuration": "RelWithDebInfo", "targets": ["clap-tests"] } ], "testPresets": [ { "name": "ninja-release", "configurePreset": "ninja", "configuration": "RelWithDebInfo" }, { "name": "msvc-release", "configurePreset": "msvc", "configuration": "RelWithDebInfo" } ] }qtractor-1.5.9/src/clap/PaxHeaders/README.md0000644000000000000000000000013215101070323015420 xustar0030 mtime=1761898707.846314457 30 atime=1761898707.846314457 30 ctime=1761898707.846314457 qtractor-1.5.9/src/clap/README.md0000644000175000001440000002006615101070323015414 0ustar00rncbcusers

CLAP

- [Learn about CLAP](#learn-about-clap) - [Entry point](#entry-point) - [Extensions](#extensions) - [Fundamental extensions](#fundamental-extensions) - [Support extensions](#support-extensions) - [Deeper Host integration](#deeper-host-integration) - [Third-party extensions](#third-party-extensions) - [Adapters](#adapters) - [Resources](#resources) - [Examples](#examples) - [Community related projects](#community-related-projects) - [Programming Language Bindings](#programming-language-bindings) - [Artwork](#artwork) # Learn about CLAP CLAP stands for **CL**ever **A**udio **P**lugin. It is an interface that provides a stable ABI to define a standard for *Digital Audio Workstations* and audio plugins (synthesizers, audio effects, ...) to work together. The ABI, or **A**pplication **B**inary **I**nterface, serves as a means of communication between a host and a plugin. It provides backwards compatibility, that is, a plugin binary compiled with CLAP 1.x can be loaded by any other CLAP 1.y. To work with CLAP, include [clap/clap.h](include/clap/clap.h). To also include the draft extensions, include [clap/all.h](include/clap/all.h). The two most important objects are `clap_host` and `clap_plugin`. [src/plugin-template.c](src/plugin-template.c) is a very minimal example which demonstrates how to wire a CLAP plugin. ## Entry point The entry point is declared in [entry.h](include/clap/entry.h). ## Extensions Most features come from extensions, which are in fact C interfaces. ```C // host extension const clap_host_log *log = host->extension(host, CLAP_EXT_LOG); if (log) log->log(host, CLAP_LOG_INFO, "Hello World! ;^)"); // plugin extension const clap_plugin_params *params = plugin->extension(plugin, CLAP_EXT_PARAMS); if (params) { uint32_t paramsCount = params->count(plugin); // ... } ``` The extensions are defined in the [ext](include/clap/ext) folder. Some extensions are still in the progress of being designed and they are in the [draft](include/clap/ext/draft) folder. An extension comes with: - a header `#include ` - an extension identifier: `#define CLAP_EXT_XXX "clap/XXX"` - host interfaces are named like: `struct clap_host_xxx` - plugin interfaces are named like: `struct clap_plugin_xxx` - each method must have a clear thread specification You can create your own extensions and share them. Make sure that the extension identifier: - includes versioning in case the ABI breaks - is a unique identifier **All strings are valid UTF-8**. ## Fundamental extensions This is a list of the extensions that you most likely want to implement and use to get a basic plugin experience: - [state](include/clap/ext/state.h), save and load the plugin state - [state-context](include/clap/ext/state-context.h), same as state but with additional context info (preset, duplicate, project) - [resource-directory](include/clap/ext/draft/resource-directory.h), host provided folder for the plugin to save extra resource like multi-samples, ... (draft) - [params](include/clap/ext/params.h), parameters management - [note-ports](include/clap/ext/note-ports.h), define the note ports - [audio-ports](include/clap/ext/audio-ports.h), define the audio ports - [surround](include/clap/ext/surround.h), inspect surround channel mapping - [ambisonic](include/clap/ext/draft/ambisonic.h), inspect ambisonic channel mapping - [configurable-audio-ports](include/clap/ext/configurable-audio-ports.h), request the plugin to apply a given configuration - [audio-ports-config](include/clap/ext/audio-ports-config.h), simple list of pre-defined audio ports configurations, meant to be exposed to the user - [audio-ports-activation](include/clap/ext/audio-ports-activation.h), activate and deactivate a given audio port - [extensible-audio-ports](include/clap/ext/draft/extensible-audio-ports.h), let the host add audio ports to the plugin, this is useful for dynamic number of audio inputs (draft) - [render](include/clap/ext/render.h), renders realtime or offline - [latency](include/clap/ext/latency.h), report the plugin latency - [tail](include/clap/ext/tail.h), processing tail length - [gui](include/clap/ext/gui.h), generic gui controller - [voice-info](include/clap/ext/voice-info.h), let the host know how many voices the plugin has, this is important for polyphonic modulations - [track-info](include/clap/ext/track-info.h), give some info to the plugin about the track it belongs to - [tuning](include/clap/ext/draft/tuning.h), host provided microtuning (draft) - [triggers](include/clap/ext/draft/triggers.h), plugin's triggers, similar to parameters but stateless ## Support extensions - [thread-check](include/clap/ext/thread-check.h), check which thread you are currently on, useful for correctness validation - [thread-pool](include/clap/ext/thread-pool.h), use the host thread pool - [log](include/clap/ext/log.h), lets the host aggregate plugin logs - [timer-support](include/clap/ext/timer-support.h), lets the plugin register timer handlers - [posix-fd-support](include/clap/ext/posix-fd-support.h), lets the plugin register I/O handlers ## Deeper Host integration - [remote-controls](include/clap/ext/remote-controls.h), bank of controls that can be mapped on a controlles with 8 knobs - [preset-discovery](include/clap/factory/preset-discovery.h), let the host index the plugin's preset in their native file format - [preset-load](include/clap/ext/preset-load.h), let the host ask the plugin to load a preset - [param-indication](include/clap/ext/param-indication.h), let the plugin know when a physical control is mapped to a parameter and if there is automation data - [note-name](include/clap/ext/note-name.h), give a name to notes, useful for drum machines - [transport-control](include/clap/ext/draft/transport-control.h), let the plugin control the host's transport (draft) - [context-menu](include/clap/ext/context-menu.h), exchange context menu entries between host and plugin, let the plugin ask the host to popup its own context menu ## Third-party extensions - [`cockos.reaper_extension`](https://github.com/justinfrankel/reaper-sdk/blob/main/reaper-plugins/reaper_plugin.h#L138), access the [REAPER](http://reaper.fm) API # Adapters - [clap-wrapper](https://github.com/free-audio/clap-wrapper), wrappers for using CLAP in other plugin environments # Resources - [clap-validator](https://github.com/robbert-vdh/clap-validator), a validator and automatic test suite for CLAP plugins. - [clapdb](https://clapdb.tech), a list of plugins and DAWs which supports CLAP ## Examples - [clap-host](https://github.com/free-audio/clap-host), very simple host - [clap-plugins](https://github.com/free-audio/clap-plugins), very simple plugins ## Community related projects - [clap-juce-extension](https://github.com/free-audio/clap-juce-extension), juce add-on - [MIP2](https://github.com/skei/MIP2), host and plugins - [Avendish](https://github.com/celtera/avendish), a reflection-based API for media plug-ins in C++ which supports Clap - [NIH-plug](https://github.com/robbert-vdh/nih-plug), an API-agnostic, Rust-based plugin framework aiming to reduce boilerplate without getting in your way - [iPlug2](https://iplug2.github.io), a liberally licensed C++ audio plug-in framework that supports Clap ## Programming Language Bindings - [clap-sys](https://github.com/glowcoil/clap-sys), rust binding - [CLAP-for-Delphi](https://github.com/Bremmers/CLAP-for-Delphi), Delphi binding - [clap-zig-bindings](https://sr.ht/~interpunct/clap-zig-bindings/), Zig bindings - [CLAP for Ada](https://github.com/ficorax/cfa), Ada 2012 binding ## Artwork - [CLAP Logo Pack.zip](https://github.com/free-audio/clap/files/8805281/CLAP.Logo.Pack.zip) qtractor-1.5.9/src/clap/PaxHeaders/.clang-format0000644000000000000000000000013215101070323016514 xustar0030 mtime=1761898707.844314451 30 atime=1761898707.844314451 30 ctime=1761898707.844314451 qtractor-1.5.9/src/clap/.clang-format0000644000175000001440000000752115101070323016511 0ustar00rncbcusers--- Language: Cpp # BasedOnStyle: LLVM AccessModifierOffset: -3 AlignAfterOpenBracket: Align AlignConsecutiveMacros: false AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: true AlignEscapedNewlines: Right AlignOperands: true AlignTrailingComments: true AllowAllArgumentsOnNextLine: true AllowAllConstructorInitializersOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: Never AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: All AllowShortLambdasOnASingleLine: All AllowShortIfStatementsOnASingleLine: Never AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes BinPackArguments: false BinPackParameters: false BraceWrapping: AfterCaseLabel: false AfterClass: true AfterControlStatement: true AfterEnum: true AfterFunction: true AfterNamespace: true AfterObjCDeclaration: true AfterStruct: true AfterUnion: true AfterExternBlock: true BeforeCatch: true BeforeElse: true IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: false SplitEmptyNamespace: false BreakBeforeBinaryOperators: None BreakBeforeBraces: Attach BreakBeforeInheritanceComma: false BreakInheritanceList: BeforeColon BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true ColumnLimit: 100 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: true ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 3 ContinuationIndentWidth: 3 Cpp11BracedListStyle: true DeriveLineEnding: false DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IncludeBlocks: Preserve IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 SortPriority: 0 - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 SortPriority: 0 - Regex: '.*' Priority: 1 SortPriority: 0 IncludeIsMainRegex: '(Test)?$' IncludeIsMainSourceRegex: '' IndentCaseLabels: false IndentGotoLabels: true IndentPPDirectives: AfterHash IndentWidth: 3 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true MacroBlockBegin: '' MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: All ObjCBinPackProtocolList: Auto ObjCBlockIndentWidth: 3 ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakFirstLessLess: 120 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Right ReflowComments: true SortIncludes: false SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyBlock: false SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 SpacesInAngles: false SpacesInConditionalStatement: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false SpaceBeforeSquareBrackets: false Standard: Latest StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION TabWidth: 8 UseCRLF: false UseTab: Never ... qtractor-1.5.9/src/clap/PaxHeaders/ChangeLog.md0000644000000000000000000000013215101070323016312 xustar0030 mtime=1761898707.845700915 30 atime=1761898707.845700915 30 ctime=1761898707.845700915 qtractor-1.5.9/src/clap/ChangeLog.md0000644000175000001440000004034615101070323016311 0ustar00rncbcusers# Changes in 1.2.6 * [mini-curve-display.h](include/clap/ext/draft/mini-curve-display.h): third iteration, added curve stacking, curve kind and curve hints. # Changes in 1.2.5 * [mini-curve-display.h](include/clap/ext/draft/mini-curve-display.h): second iteration, remove `host->cuve_changed()` and improve documentation * [project-location.h](include/clap/ext/draft/project-location.h): fix typo # Changes in 1.2.4 * [mini-curve-display.h](include/clap/ext/draft/mini-curve-display.h): new extension to let the plugin render a mini murve to be displayed in the host's mixer, eg: EQ frequency response * [project-location.h](include/clap/ext/draft/project-location.h): rename the extension and second iteration on the interface * [surround.h](include/clap/ext/surround.h): add `CLAP_SURROUND_TSL`, `CLAP_SURROUND_TSR`, and improve documentation * [thread-check.h](include/clap/ext/thread-check.h): adjust documentation # Changes in 1.2.3 ## New draft extensions * [scratch-memory](include/clap/ext/draft/scratch-memory.h): host provided scratch memory within the process call * [location](include/clap/ext/draft/location.h): better info about the plugin location within the project * [gain-adjustment-metering](include/clap/ext/draft/gain-adjustment-metering.h): gain reduction feedback ## Documention * [events.h](include/clap/events.h): clarify sysex lifetime * [host.h](include/clap/host.h): clarify `request_callback()` * [ambisonic.h](include/clap/ext/ambisonic.h): remove bad comment ## Fixes * [plugin-template.c](src/plugin-template.c): fix bad assertion # Changes in 1.2.2 * [thread-check.h](include/clap/ext/thread-check.h): expand the thread-doc to clarify and expand realtime * [latency.h](include/clap/ext/latency.h): adjust latency extension requirements * [undo.h](include/clap/ext/draft/undo.h): re-design the interface * the plugin interfaces have been separated into 2 independent ones * the plugin interfaces are optional * simplification of the design # Changes in 1.2.1 ## New draft extensions * [undo.h](include/clap/ext/draft/undo.h): shared undo stack for the host and plugin. ## Documentation * [events.h](include/clap/events.h): clarification for note on events. # Changes in 1.2.0 ## New conventions * [extension-id](conventions/extension-id.md): introduce some rules about extension ID naming. ## Stabilize extensions * `CLAP_EXT_AMBISONIC` * `CLAP_EXT_AUDIO_PORTS_ACTIVATION` * `CLAP_EXT_CONFIGURABLE_AUDIO_PORTS` * `CLAP_EXT_CONTEXT_MENU` * `CLAP_EXT_PARAM_INDICATION` * `CLAP_EXT_PRESET_LOAD` * `CLAP_EXT_REMOTE_CONTROLS` * `CLAP_EXT_STATE_CONTEXT` * `CLAP_EXT_SURROUND` * `CLAP_EXT_TRACK_INFO` ### Notes regarding extension ID change after draft stabilization We changed the extension ID in the process of stabilization which leads to a **break**. To mitigate this transition, we've provided compatibility extension IDs which can be used to match and use the latest draft extensions as they are 100% compatible. For example, `CLAP_EXT_CONTEXT_MENU` for the stable ID and `CLAP_EXT_CONTEXT_MENU_COMPAT` for the draft ID. As you can see in [extension-id](conventions/extension-id.md), we introduced some rules, so this kind of break won't happen again. We may decide to remove the `*_COMPAT` IDs in the future once their usage becomes antiquated. ## Removed draft extensions * `CLAP_EXT_CHECK_FOR_UPDATE` wasn't used and it's design needed more thought. * `CLAP_EXT_MIDI_MAPPING` wasn't used. MIDI2 seems to do it better, and the interface wasn't satisfying. * `CLAP_EXT_CV` the interface wasn't satisfying. ## Stabilize factory * `CLAP_PRESET_DISCOVERY_FACTORY_ID` Note: we kept the last draft factory ID in order to not break plugins already using it. ## Plugin State Converter * Introduction of a new factory which provides a plugin state convertion mechanism. ## Refactoring * `clap_plugin_id_t` was renamed to `clap_universal_plugin_id_t` to make it clear that it can describe more than just a CLAP plugin ID. * `clap_timestamp_t` was renamed to `clap_timestamp` to be consistent with other types, like e.g. `clap_id`. Also it was moved to a separate header as `CLAP_PRESET_DISCOVERY_FACTORY_ID` was stabilized. ## Documentation * [events.h](include/clap/events.h): Clarify how "Port Channel Key NoteID" matching works * [events.h](include/clap/events.h): Clarify how `clap_event_note` fields map to MIDI, Host, etc... * [events.h](include/clap/events.h): Expand clap note expression documentation * [plugin.h](include/clap/plugin.h): Style cleanup * [params.h](include/clap/ext/params.h): Fix incorrect function name reference * [latency.h](include/clap/ext/latency.h): Require the plugin to be activated to get the latency and clarify that the latency can only be fetched when the plugin is activated ## Plugin Template * [plugin-template.c](src/plugin-template.c): implement thread-safe plugin entry init counter ## Organization * `clap.h` no longer includes headers from `ext/draft` or `factory/draft`. Draft extension and factory headers must now be explicitly included, either individually or via the `all.h` header. ## Other changes * [voice-info.h](include/clap/ext/voice-info.h): Make the voice info id `CLAP_CONSTEXPR` like all other ids * [preset-load.h](include/clap/ext/preset-load.h): Make the preset load id and compat id `CLAP_CONSTEXPR` like all other ids # Changes in 1.1.10 * [params.h](include/clap/ext/params.h): add `CLAP_PARAM_IS_ENUM` flag. * various documentation improvements: * clarification on the return value of many functions * typos # Changes in 1.1.9 * [entry.h](include/clap/entry.h): clarify what the `plugin_path` is on macOS * [surround.h](include/clap/ext/draft/surround.h): simplify the design * [ambisonic.h](include/clap/ext/draft/ambisonic.h): simplify the design * [configurable-audio-ports.h](include/clap/ext/draft/configurable-audio-ports.h): simplify the design * [gui.h](include/clap/ext/gui.h): documentation clarifications * [entry.h](include/clap/entry.h): documentation clarifications * [audio-ports-activation.h](include/clap/ext/draft/audio-ports-activation.h): specify the sample size to be used when activating the audio port. # Changes in 1.1.8 * [params.h](include/clap/ext/params.h): document how persisting parameter values between sessions should be implemented * [state.h](include/clap/ext/state.h): add basic documentation regarding what state should be saved and how plugins should interact with buffers * various documentation fixes (essentially typos) ## Draft extensions * [extensible-audio-ports.h](include/clap/ext/draft/extensible-audio-ports.h): new extension which lets the host add ports to a plugin * [configurable-audio-ports.h](include/clap/ext/draft/configurable-audio-ports.h): new extension allowing the host to **push** an audio ports configuration request, resulting in a simpler workflow for surround host and plugins * [surround.h](include/clap/ext/draft/surround.h): * remove `get_preferred_channel_map()` in favor of the push approach via [configurable-audio-ports.h](include/clap/ext/draft/configurable-audio-ports.h) * remove `config_id` argument from `get_info()` * [ambisonic.h](include/clap/ext/draft/ambisonic.h): remove `config_id` argument from `get_info()` * [preset-load.h](include/clap/ext/draft/preset-load.h): use a location_kind + location approach instead of URI ## Draft factories * [preset-discovery.h](include/clap/factory/draft/preset-discovery.h): * use a location_kind + location approach instead of URI * document which descriptor fields are optional * allow optional preset names in the metadata for non-container presets # Changes in 1.1.7 * Add a [factory](include/clap/factory) folder for better organization and move our factories there * [params.h](include/clap/ext/params.h): fix typos * CMake: disable C17 targets for CMake < 3.21 * [plugin-features.h](include/clap/plugin-features.h): adds `note-detector` category for plugins which converts audio to notes ## Draft extensions * [context-menu.h](include/clap/ext/draft/context-menu.h): add "title" menu entry * [preset-load.h](include/clap/ext/draft/preset-load.h): load from URI instead of path, making the extension more powerful * [remote-controls.h](include/clap/ext/draft/remote-controls.h): distinguish between device pages and preset pages * [audio-ports-activation.h](include/clap/ext/draft/audio-ports-activation.h): `set_active()` now returns bool instead of void, this helps catching problems earlier especially with invalid arguments * [audio-ports-config.h](include/clap/ext/audio-ports-config.h): add new draft extension: `clap_plugin_audio_ports_config_info` which lets the host query detailed port information in a given configuration. * [surround.h](include/clap/ext/draft/surround.h): add `config_id` parameter when fetching port info * [ambisonic.h](include/clap/ext/draft/ambisonic.h): add `config_id` parameter when fetching port info ## Draft factories * [preset-discovery.h](include/clap/factory/draft/preset-discovery.h): new factory which allows the host to index the plugin presets which are stored on disk. # Changes in 1.1.6 * [version.h](include/clap/version.h) `CLAP_VERSION_LT` was backwards (comparing current with arg vs arg with current). Correct and enhance tests. # Changes in 1.1.5 * [plugin.h](include/clap/plugin.h): clarify plugin state after init() * [plugin.h](include/clap/plugin.h): clarify when it is allowed to call get_extension() * [plugin.h](include/clap/plugin.h): advice for plugin id and version strings * [host.h](include/clap/host.h): clarify when it is allowed to call get_extension() * [CMakeLists.txt](CMakeLists.txt): the target `clap-test` now includes `clap-plugin-template` * Remove UTF-8 BOM from a few files * [plugin-template.c](src/plugin-template.c): add state impl and some comments * [audio-ports-activation.h](include/clap/ext/draft/audio-ports-activation.h): improved documentation * [version.h](include/clap/version.h): * Add a CLAP_VERSION_GE(maj,min,rev), _EQ and _LT macro. * Remove the uint32_t cast from CLAP_VERSION_MAJOR, _MINOR, and _REVISION macro, and introduce it to the CLAP_VERSION_INIT macro. * If you rely on these macros being a uint32_t or parse this header using external software, this may be a breaking change. # Changes in 1.1.4 * CMake: update some targets to link against `clap` instead of `clap-core` * [params.h](include/clap/ext/params.h): clarify parameter range change, fix documentation typos, add missing items * [plugin.h](include/clap/plugin.h): clarify data lifetime in `process()` * [audio-ports-config.h](include/clap/ext/audio-ports-config.h): clarify `select()`: if succeed, the host should rescan the audio ports ## Draft extensions ### New * [audio-ports-activation.h](include/clap/ext/draft/audio-ports-activation.h): new draft extension which allows a host to inform a plugin whether an audio port is an active port in the host audio context, and allow the host to respond accordingly * [context-menu.h](include/clap/ext/draft/context-menu.h): new draft extension which let the host and plugin exchange context menu entries and popup the menu * [param-indication.h](include/clap/ext/draft/param-indication.h): new draft extension which let the host inform the plugin that a parameter is currently mapped to a physical controller * [remote-controls.h](include/clap/ext/draft/remote-controls.h): new draft extension which replaces `quick-controls.h` * [resource-directory.h](include/clap/ext/draft/resource-directory.h): new draft extension which lets the plugin save resources in a directory provided by the host * [triggers.h](include/clap/ext/draft/triggers.h): new draft extension which exposes triggers to the host, triggers are data-less events ### Improved * [track-info](include/clap/ext/draft/track-info.h): refine the draft extension ### Removed * `file-reference.h`: removed in favor of [resource-directory.h](include/clap/ext/draft/resource-directory.h) * `quick-controls.h`: removed in favor of [remote-controls.h](include/clap/ext/draft/remote-controls.h) # Changes in 1.1.3 * CMake: generate CMake and pkg-config package files on install * CMake: `clap-core` target is now deprecated, use `clap` target instead * [plugin.h](include/clap/plugin.h): make feature list on clap_plugin_descriptor_t const * [entry.h](include/clap/entry.h): fix bad location on windows: replaced `%CommonFilesFolder%/CLAP/` by `%COMMONPROGRAMFILES%\CLAP` # Changes in 1.1.2 * [macros.h](include/clap/private/macros.h): add `CLAP_ABI` which defines the calling convention and use it everywhere * [events.h](include/clap/events.h): clarify `clap_input_events.size()` * [check-for-update.h](include/clap/ext/draft/check-for-update.h): fix typedef * [file-reference.h](include/clap/ext/draft/file-reference.h): improve documentation * [params.h](include/clap/ext/params.h): clarify how the cookie works and add some notes about `flush()` * [process.h](include/clap/process.h): clarify how the audio buffer mapping works * [gui.h](include/clap/ext/gui.h): clarify `clap_plugin_gui.get_preferred_api()` * [plugin-factory.h](include/clap/plugin-factory.h): mention `clap_plugin_invalidation_factory` which can be use to invalidate cached entries # Changes in 1.1.1 * [clap.h](include/clap/clap.h): missing include for [state-context.h](include/clap/ext/draft/state-context.h). # Changes in 1.1.0 * [state-context.h](include/clap/ext/draft/state-context.h): save/load state for duplicate or preset. * [std.h](include/clap/private/std.h): fix compatibility with C++98. * [note-name.h](include/clap/ext/note-name.h): missing typedef `clap_plugin_note_name_t`. * [params.h](include/clap/ext/params.h): clarify `request_flush()` documentation. * [thread-check.h](include/clap/ext/thread-check.h): clarify documentation. * [voice-info.h](include/clap/ext/voice-info.h): promote `voice-info` out of draft. # Changes in 1.0.3 * [plugin.h](include/clap/plugin.h): fix an inconsistency in `clap_plugin->destroy()` documentation: it is now **required** to deactivate the plugin before destroying it. * [params.h](include/clap/ext/params.h): improve documentation for `clap_host_params->request_flush()`. * [entry.h](include/clap/entry.h): improve documentation regarding `init()`, `deinit()` and CLAP search path. * [gui.h](include/clap/gui.h): fix typo `clap_gui_resize_hints.preserve_aspect_ratio` * [plugin-template](src/plugin-template.c): missing impl of plugin destroy. * various documentation improvements # Changes in 1.0.2 * CMake: add `CLAP_BUILD_TESTS` which enables the tests. * Fixes compilation for Visual Studio 2019 and GCC9. # Changes in 1.0.1 * [gui.h](include/clap/ext/gui.h): fix doc: set_scale must be provided * [events.h](include/clap/events.h): remove `clap_event_type` which was never used * [draft/transport-control.h](include/clap/ext/draft/transport-control.h): rename from `CLAP_EXT_CV` to `CLAP_EXT_TRANSPORT_CONTROL` * [draft/tuning.h](include/clap/ext/draft/tuning.h): rename `clap_client_tuning` to `clap_plugin_tuning` * [macros.h](include/clap/private/macros.h): fix compatibility with C17 # Changes in 1.0.0 ## New stable interfaces * [audio-ports-config.h](include/clap/ext/audio-ports-config.h) * [audio-ports.h](include/clap/ext/audio-ports.h) * [event-registry.h](include/clap/ext/event-registry.h) * [gui.h](include/clap/ext/gui.h) * [latency.h](include/clap/ext/latency.h) * [log.h](include/clap/ext/log.h) * [note-name.h](include/clap/ext/note-name.h) * [note-ports.h](include/clap/ext/note-ports.h) * [params.h](include/clap/ext/params.h) * [posix-fd-support.h](include/clap/ext/posix-fd-support.h) * [render.h](include/clap/ext/render.h) * [state.h](include/clap/ext/state.h) * [tail.h](include/clap/ext/tail.h) * [thread-check.h](include/clap/ext/thread-check.h) * [thread-pool.h](include/clap/ext/thread-pool.h) * [timer-support.h](include/clap/ext/time-support.h) ## New draft interfaces * [ambisonic.h](include/clap/ext/draft/ambisonic.h) * [check-for-update.h](include/clap/ext/draft/check-for-update.h) * [cv.h](include/clap/ext/draft/cv.h) * [file-reference.h](include/clap/ext/draft/file-reference.h) * [midi-mappings.h](include/clap/ext/draft/midi-mappings.h) * [preset-load.h](include/clap/ext/draft/preset-load.h) * [quick-controls.h](include/clap/ext/draft/quick-controls.h) * [surround.h](include/clap/ext/draft/surround.h) * [track-info.h](include/clap/ext/draft/track-info.h) * [transport-control.h](include/clap/ext/draft/transport-control.h) * [tuning.h](include/clap/ext/draft/tuning.h) * [voice-info.h](include/clap/ext/draft/voice-info.h) qtractor-1.5.9/src/clap/PaxHeaders/LICENSE0000644000000000000000000000013215101070323015146 xustar0030 mtime=1761898707.846314457 30 atime=1761898707.845700915 30 ctime=1761898707.846314457 qtractor-1.5.9/src/clap/LICENSE0000644000175000001440000000206015101070323015134 0ustar00rncbcusersMIT License Copyright (c) 2021 Alexandre BIQUE 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.9/src/clap/PaxHeaders/conventions0000644000000000000000000000013215101070323016431 xustar0030 mtime=1761898707.882314571 30 atime=1761898707.882314571 30 ctime=1761898707.882314571 qtractor-1.5.9/src/clap/conventions/0000755000175000001440000000000015101070323016476 5ustar00rncbcusersqtractor-1.5.9/src/clap/conventions/PaxHeaders/extension-id.md0000644000000000000000000000013215101070323021436 xustar0030 mtime=1761898707.882314571 30 atime=1761898707.882314571 30 ctime=1761898707.882314571 qtractor-1.5.9/src/clap/conventions/extension-id.md0000644000175000001440000000265315101070323021434 0ustar00rncbcusers# Extension ID ## Naming The extension shall be named in the form: `clap.$NAME/$REV`. Where: - `$NAME` is the name of the exension. - `$REV` is the revision of the extension. This is an integer that is incremented for each iteration. It should start at 1. For extensions made by third-parties and not officially published by the CLAP project, please use the following form: `$REVERSE_URI.$NAME/$REV`. Where: - `$REVERSE_URI` would be something like `com.bitwig`. ## Draft An extension is considered a draft extension if it is in the [draft](../include/clap/ext/draft/) folder. Make sure to also include it in [all.h](../include/clap/all.h). When the extension is migrating from draft to stable, its extension ID must not change. Move its inclusion from [all.h](../include/clap/all.h) into [clap.h](../include/clap/clap.h). All extensions must go though the draft phase first. ## For factory ID Everything about the extension id symmetrically applies to factory id. ## History Before version 1.2.0 when this document was written, existing extensions didn't honor these rules. We wanted to stabilize some draft extensions without breaking compatibility, yet their extension IDs contained the string `draft`. While these strings weren't user-facing, we still wanted to remove them, so we updated the extension IDs according to this document and introduced IDs with `_COMPAT` suffixes to provide backward compatibility with the draft versions. qtractor-1.5.9/src/PaxHeaders/qtractorMidiConnect.cpp0000644000000000000000000000013215101070305017702 xustar0030 mtime=1761898693.077267623 30 atime=1761898693.077267623 30 ctime=1761898693.077267623 qtractor-1.5.9/src/qtractorMidiConnect.cpp0000644000175000001440000003376715101070305017712 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.9/src/PaxHeaders/qtractorPropertyCommand.h0000644000000000000000000000013215101070305020276 xustar0030 mtime=1761898693.087267654 30 atime=1761898693.087267654 30 ctime=1761898693.087267654 qtractor-1.5.9/src/qtractorPropertyCommand.h0000644000175000001440000000333715101070305020274 0ustar00rncbcusers// qtractorPropertyCommand.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 __qtractorPropertyCommand_h #define __qtractorPropertyCommand_h #include "qtractorCommand.h" //---------------------------------------------------------------------- // class qtractorPropertyCommand - template declaration. // template class qtractorPropertyCommand : public qtractorCommand { public: // Constructor. qtractorPropertyCommand(const QString& sName, T& ref, const T& val) : qtractorCommand(sName), m_ref(ref), m_val(val) {} // Cannonical command methods. bool redo() { T val = m_ref; m_ref = m_val; m_val = val; return true; } bool undo() { return qtractorPropertyCommand::redo(); } private: // Instance variables. T& m_ref; T m_val; }; #endif // __qtractorPropertyCommand_h // end of qtractorPropertyCommand.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiTimer.h0000644000000000000000000000013215101070305017036 xustar0030 mtime=1761898693.084267645 30 atime=1761898693.084267645 30 ctime=1761898693.084267645 qtractor-1.5.9/src/qtractorMidiTimer.h0000644000175000001440000000504315101070305017030 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.9/src/PaxHeaders/qtractorCurveSelect.cpp0000644000000000000000000000013215101070305017732 xustar0030 mtime=1761898693.069267597 30 atime=1761898693.069267597 30 ctime=1761898693.069267597 qtractor-1.5.9/src/qtractorCurveSelect.cpp0000644000175000001440000000757015101070305017733 0ustar00rncbcusers// qtractorCurveSelect.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 "qtractorCurveSelect.h" //------------------------------------------------------------------------- // qtractorCurveSelect -- MIDI event selection capsule. // Constructor. qtractorCurveSelect::qtractorCurveSelect (void) { m_pCurve = nullptr; m_pAnchorNode = nullptr; } // Default destructor. qtractorCurveSelect::~qtractorCurveSelect (void) { clear(); } // Event selection item lookup. qtractorCurveSelect::Item *qtractorCurveSelect::findItem ( qtractorCurve::Node *pNode ) { // Check if this very event already exists... return m_items.value(pNode, nullptr); } // Item insertion method. void qtractorCurveSelect::addItem ( qtractorCurve::Node *pNode, const QRect& rectNode ) { m_items.insert(pNode, new Item(rectNode)); m_rect = m_rect.united(rectNode); if (m_pAnchorNode == nullptr || m_pAnchorNode->frame > pNode->frame) m_pAnchorNode = pNode; } // Item removal method. void qtractorCurveSelect::removeItem ( qtractorCurve::Node *pNode ) { ItemList::Iterator iter = m_items.find(pNode); if (iter != m_items.end()) { delete iter.value(); m_items.erase(iter); commit(); } } // Item selection method. void qtractorCurveSelect::selectItem ( qtractorCurve *pCurve, qtractorCurve::Node *pNode, const QRect& rectNode, bool bSelect, bool bToggle ) { Item *pItem = findItem(pNode); 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) { if (m_pCurve == nullptr) m_pCurve = pCurve; if (m_pCurve == pCurve) addItem(pNode, rectNode); } } // Selection commit method. void qtractorCurveSelect::update ( bool bCommit ) { // Remove unselected... int iUpdate = 0; 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 & 3) == 0) { delete pItem; iter = m_items.erase(iter); ++iUpdate; } else ++iter; } // Did we remove any? if (iUpdate > 0) commit(); } // Selection commit method. void qtractorCurveSelect::commit (void) { // Reset united selection rectangle... m_rect.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(); m_rect = m_rect.united(pItem->rectNode); } if (m_items.isEmpty()) { m_pAnchorNode = nullptr; m_pCurve = nullptr; } } // Reset event selection. void qtractorCurveSelect::clear (void) { m_rect.setRect(0, 0, 0, 0); qDeleteAll(m_items); m_items.clear(); m_pAnchorNode = nullptr; m_pCurve = nullptr; } // end of qtractorCurveSelect.cpp qtractor-1.5.9/src/PaxHeaders/config.h.cmake0000644000000000000000000000013215101070305015717 xustar0030 mtime=1761898693.057007007 30 atime=1761898693.057007007 30 ctime=1761898693.057007007 qtractor-1.5.9/src/config.h.cmake0000644000175000001440000001764115101070305015720 0ustar00rncbcusers#ifndef CONFIG_H #define CONFIG_H /* Define to the title of this package. */ #cmakedefine PROJECT_TITLE "@PROJECT_TITLE@" /* Define to the name of this package. */ #cmakedefine PROJECT_NAME "@PROJECT_NAME@" /* Define to the version of this package. */ #cmakedefine PROJECT_VERSION "@PROJECT_VERSION@" /* Define to the description of this package. */ #cmakedefine PROJECT_DESCRIPTION "@PROJECT_DESCRIPTION@" /* Define to the homepage of this package. */ #cmakedefine PROJECT_HOMEPAGE_URL "@PROJECT_HOMEPAGE_URL@" /* Define to the copyright of this package. */ #cmakedefine PROJECT_COPYRIGHT "@PROJECT_COPYRIGHT@" /* Define to the domain of this package. */ #cmakedefine PROJECT_DOMAIN "@PROJECT_DOMAIN@" /* Default installation prefix. */ #cmakedefine CONFIG_PREFIX "@CONFIG_PREFIX@" /* Define to target installation dirs. */ #cmakedefine CONFIG_BINDIR "@CONFIG_BINDIR@" #cmakedefine CONFIG_LIBDIR "@CONFIG_LIBDIR@" #cmakedefine CONFIG_DATADIR "@CONFIG_DATADIR@" #cmakedefine CONFIG_MANDIR "@CONFIG_MANDIR@" /* Define if debugging is enabled. */ #cmakedefine CONFIG_DEBUG @CONFIG_DEBUG@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SIGNAL_H @HAVE_SIGNAL_H@ /* Define if IEEE 32bit float optimizations are enabled. */ #cmakedefine CONFIG_FLOAT32 @CONFIG_FLOAT32@ /* Define if round is available. */ #cmakedefine CONFIG_ROUND @CONFIG_ROUND@ /* Define if JACK library is available. */ #cmakedefine CONFIG_LIBJACK @CONFIG_LIBJACK@ /* Define if ALSA library is available. */ #cmakedefine CONFIG_LIBASOUND @CONFIG_LIBASOUND@ /* Define if SNDFILE library is available. */ #cmakedefine CONFIG_LIBSNDFILE @CONFIG_LIBSNDFILE@ /* Define if VORBIS library is available. */ #cmakedefine CONFIG_LIBVORBIS @CONFIG_LIBVORBIS@ /* Define if MAD library is available. */ #cmakedefine CONFIG_LIBMAD @CONFIG_LIBMAD@ /* Define if SAMPLERATE library is available. */ #cmakedefine CONFIG_LIBSAMPLERATE @CONFIG_LIBSAMPLERATE@ /* Define if RUBBERBAND library is available. */ #cmakedefine CONFIG_LIBRUBBERBAND @CONFIG_LIBRUBBERBAND@ /* Define if RUBBERBAND_R3 library is available. */ #cmakedefine CONFIG_LIBRUBBERBAND_R3 @CONFIG_LIBRUBBERBAND_R3@ /* Define if AUBIO library is available. */ #cmakedefine CONFIG_LIBAUBIO @CONFIG_LIBAUBIO@ /* Define if LIBLO library is available. */ #cmakedefine CONFIG_LIBLO @CONFIG_LIBLO@ /* Define if ZLIB library is available. */ #cmakedefine CONFIG_LIBZ @CONFIG_LIBZ@ /* Define if LILV library is available. */ #cmakedefine CONFIG_LIBLILV @CONFIG_LIBLILV@ /* Define if lilv_file_uri_parse is available. */ #cmakedefine CONFIG_LILV_FILE_URI_PARSE @CONFIG_LILV_FILE_URI_PARSE@ /* Define if lilv_world_unload_resource is available. */ #cmakedefine CONFIG_LILV_WORLD_UNLOAD_RESOURCE @CONFIG_LILV_WORLD_UNLOAD_RESOURCE@ /* Define if SUIL library is enabled. */ #cmakedefine CONFIG_LIBSUIL @CONFIG_LIBSUIL@ /* Define if suil_instance_get_handle is available. */ #cmakedefine CONFIG_SUIL_INSTANCE_GET_HANDLE @CONFIG_SUIL_INSTANCE_GET_HANDLE@ /* Define if JACK latency support is available. */ #cmakedefine CONFIG_JACK_LATENCY @CONFIG_JACK_LATENCY@ /* Define if LADSPA header is available. */ #cmakedefine CONFIG_LADSPA @CONFIG_LADSPA@ /* Define if DSSI header is available. */ #cmakedefine CONFIG_DSSI @CONFIG_DSSI@ /* Define if VST2 header is available. */ #cmakedefine CONFIG_VST2 @CONFIG_VST2@ /* Define if VeSTige header is available. */ #cmakedefine CONFIG_VESTIGE @CONFIG_VESTIGE@ /* Define if LV2 headers are available. */ #cmakedefine CONFIG_LV2 @CONFIG_LV2@ /* Define if LV2 old headers are enabled. */ #cmakedefine CONFIG_LV2_OLD_HEADERS @CONFIG_LV2_OLD_HEADERS@ /* Define if LV2 UI support is available. */ #cmakedefine CONFIG_LV2_UI @CONFIG_LV2_UI@ /* Define if LV2 Event/MIDI support is available. */ #cmakedefine CONFIG_LV2_EVENT @CONFIG_LV2_EVENT@ /* Define if LV2 Atom/MIDI aupport is available. */ #cmakedefine CONFIG_LV2_ATOM @CONFIG_LV2_ATOM@ /* Define if LV2 CVPort aupport is available. (DUMMY) */ #cmakedefine CONFIG_LV2_CVPORT @CONFIG_LV2_CVPORT@ /* Define if lv2_atom_forge_object is available. */ #cmakedefine CONFIG_LV2_ATOM_FORGE_OBJECT @CONFIG_LV2_ATOM_FORGE_OBJECT@ /* Define if lv2_atom_forge_key is available. */ #cmakedefine CONFIG_LV2_ATOM_FORGE_KEY @CONFIG_LV2_ATOM_FORGE_KEY@ /* Define if LV2 Worker/Schedule aupport is available. */ #cmakedefine CONFIG_LV2_WORKER @CONFIG_LV2_WORKER@ /* Define if LV2 External UI extension is available. */ #cmakedefine CONFIG_LV2_EXTERNAL_UI @CONFIG_LV2_EXTERNAL_UI@ /* Define if LV2 State extension is available. */ #cmakedefine CONFIG_LV2_STATE @CONFIG_LV2_STATE@ /* Define if LV2 State Files feature is available. */ #cmakedefine CONFIG_LV2_STATE_FILES @CONFIG_LV2_STATE_FILES@ /* Define if LV2 State Make Path feature is available. */ #cmakedefine CONFIG_LV2_STATE_MAKE_PATH @CONFIG_LV2_STATE_MAKE_PATH@ /* Define if LV2 State Free Path feature is available. */ #cmakedefine CONFIG_LV2_STATE_FREE_PATH @CONFIG_LV2_STATE_FREE_PATH@ /* Define if LV2 Programs extension is available. */ #cmakedefine CONFIG_LV2_PROGRAMS @CONFIG_LV2_PROGRAMS@ /* Define if LV2 MIDNAM extension is available. */ #cmakedefine CONFIG_LV2_MIDNAM @CONFIG_LV2_MIDNAM@ /* Define if LV2 Presets are supported. */ #cmakedefine CONFIG_LV2_PRESETS @CONFIG_LV2_PRESETS@ /* Define if LV2 Patch is supported. */ #cmakedefine CONFIG_LV2_PATCH @CONFIG_LV2_PATCH@ /* Define if LV2 Port-event is supported. */ #cmakedefine CONFIG_LV2_PORT_EVENT @CONFIG_LV2_PORT_EVENT@ /* Define if LV2 Port-change request is supported. */ #cmakedefine CONFIG_LV2_PORT_CHANGE_REQUEST @CONFIG_LV2_PORT_CHANGE_REQUEST@ /* Define if LV2 Time is supported. */ #cmakedefine CONFIG_LV2_TIME @CONFIG_LV2_TIME@ /* Define if LV2 Options is supported. */ #cmakedefine CONFIG_LV2_OPTIONS @CONFIG_LV2_OPTIONS@ /* Define if LV2 Buf-size is supported. */ #cmakedefine CONFIG_LV2_BUF_SIZE @CONFIG_LV2_BUF_SIZE@ /* Define if LV2 Parameters is supported. */ #cmakedefine CONFIG_LV2_PARAMETERS @CONFIG_LV2_PARAMETERS@ /* Define if LV2 Time/position support is available. */ #cmakedefine CONFIG_LV2_TIME_POSITION @CONFIG_LV2_TIME_POSITION@ /* Define if LV2 UI Touch interface support is available. */ #cmakedefine CONFIG_LV2_UI_TOUCH @CONFIG_LV2_UI_TOUCH@ /* Define if LV2 UI Request-value support is available. */ #cmakedefine CONFIG_LV2_UI_REQ_VALUE @CONFIG_LV2_UI_REQ_VALUE@ /* Define if LV2 UI Request-value support is available. (FAKE) */ #cmakedefine CONFIG_LV2_UI_REQ_VALUE_FAKE @CONFIG_LV2_UI_REQ_VALUE_FAKE@ /* Define if LV2 UI Idle interface support is available. */ #cmakedefine CONFIG_LV2_UI_IDLE @CONFIG_LV2_UI_IDLE@ /* Define if LV2 UI Show interface support is available. */ #cmakedefine CONFIG_LV2_UI_SHOW @CONFIG_LV2_UI_SHOW@ /* Define if LV2 UI GTK2 native support is available. */ #cmakedefine CONFIG_LV2_UI_GTK2 @CONFIG_LV2_UI_GTK2@ /* Define if LV2 UI GTKMM2 native support is available. */ #cmakedefine CONFIG_LV2_UI_GTKMM2 @CONFIG_LV2_UI_GTKMM2@ /* Define if LV2 UI X11 native support is available. */ #cmakedefine CONFIG_LV2_UI_X11 @CONFIG_LV2_UI_X11@ /* Define if VST3 plug-in support is avilable. */ #cmakedefine CONFIG_VST3 @CONFIG_VST3@ /* Define if CLAP plug-in support is avilable. */ #cmakedefine CONFIG_CLAP @CONFIG_CLAP@ /* Define if JACK session support is available. */ #cmakedefine CONFIG_JACK_SESSION @CONFIG_JACK_SESSION@ /* Define if JACK metadata support is available. */ #cmakedefine CONFIG_JACK_METADATA @CONFIG_JACK_METADATA@ /* Define if jack_set_port_rename_callback is available. */ #cmakedefine CONFIG_JACK_PORT_RENAME @CONFIG_JACK_PORT_RENAME@ /* Define if NSM support is available. */ #cmakedefine CONFIG_NSM @CONFIG_NSM@ /* Define if unique/single instance is enabled. */ #cmakedefine CONFIG_XUNIQUE @CONFIG_XUNIQUE@ /* Define if gradient eye-candy is enabled. */ #cmakedefine CONFIG_GRADIENT @CONFIG_GRADIENT@ /* Define if debugger stack-trace is enabled. */ #cmakedefine CONFIG_STACKTRACE @CONFIG_STACKTRACE@ /* Define if Wayland is supported (NOT RECOMMENDED) */ #cmakedefine CONFIG_WAYLAND @CONFIG_WAYLAND@ #endif /* CONFIG_H */ qtractor-1.5.9/src/PaxHeaders/qtractorMidiToolsForm.h0000644000000000000000000000013215101070305017702 xustar0030 mtime=1761898693.084267645 30 atime=1761898693.084267645 30 ctime=1761898693.084267645 qtractor-1.5.9/src/qtractorMidiToolsForm.h0000644000175000001440000000547415101070305017704 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.9/src/PaxHeaders/qtractorObserver.h0000644000000000000000000000013215101070305016742 xustar0030 mtime=1761898693.085267648 30 atime=1761898693.085267648 30 ctime=1761898693.085267648 qtractor-1.5.9/src/qtractorObserver.h0000644000175000001440000001716215101070305016741 0ustar00rncbcusers// qtractorObserver.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 __qtractorObserver_h #define __qtractorObserver_h #include #include #include // Forward declarations. class qtractorSubject; class qtractorObserver; class qtractorCurve; //--------------------------------------------------------------------------- // qtractorSubject - Scalar parameter value model. class qtractorSubject { public: // Constructor. qtractorSubject(float fValue = 0.0f, float fDefaultValue = 0.0f); // Destructor. ~qtractorSubject(); // Direct value accessors. void setValue(float fValue, qtractorObserver *pSender = nullptr); float value() const { return m_fValue; } float prevValue() const { return m_fPrevValue; } float lastValue() const { return m_fLastValue; } // Observers notification. void notify(qtractorObserver *pSender, float fValue, bool bUpdate); // Observer list accessors. void attach(qtractorObserver *pObserver) { m_observers.append(pObserver); } void detach(qtractorObserver *pObserver) { m_observers.removeAll(pObserver); } const QList& observers() const { return m_observers; } // Queue status accessors. void setQueued(bool bQueued) { m_bQueued = bQueued; } bool isQueued() const { return m_bQueued; } // Direct address accessor. float *data() { return &m_fValue; } // Parameter name accessors. void setName(const QString& sName) { m_sName = sName.trimmed(); } const QString& name() const { return m_sName; } // Value limits accessors. void setMaxValue(float fMaxValue) { m_fMaxValue = fMaxValue; } float maxValue() const { return m_fMaxValue; } void setMinValue(float fMinValue) { m_fMinValue = fMinValue; } float minValue() const { return m_fMinValue; } // Default value accessor. void setDefaultValue(float fDefaultValue) { m_fDefaultValue = fDefaultValue; } float defaultValue() const { return m_fDefaultValue; } void resetValue(qtractorObserver *pSender = nullptr) { setValue(m_fDefaultValue, pSender); } // Toggled mode accessors. void setToggled(bool bToggled) { m_bToggled = bToggled; } bool isToggled() const { return m_bToggled; } // Integer mode accessors. void setInteger(bool bInteger) { m_bInteger = bInteger; } bool isInteger() const { return m_bInteger; } // Non toggled nor integer mode helper. bool isDecimal() const { return !m_bToggled && !m_bInteger; } // Filter value within legal bounds. float safeValue (float fValue) const { if (m_bToggled) { const float fThreshold = 0.5f * (m_fMinValue + m_fMaxValue); fValue = (fValue > fThreshold ? m_fMaxValue : m_fMinValue); } else if (fValue > m_fMaxValue) fValue = m_fMaxValue; else if (fValue < m_fMinValue) fValue = m_fMinValue; else if (m_bInteger) fValue = ::truncf(fValue); return fValue; } // Normalized scale converters. float valueFromScale ( float fScale ) const { return m_fMinValue + fScale * (m_fMaxValue - m_fMinValue); } float scaleFromValue ( float fValue ) const { return (fValue - m_fMinValue) / (m_fMaxValue - m_fMinValue); } // Automation curve association. void setCurve(qtractorCurve *pCurve) { m_pCurve = pCurve; } qtractorCurve *curve() const { return m_pCurve; } // Queue flush (singleton) -- notify all pending observers. static bool flushQueue(bool bUpdate); // Queue reset (clear). static void resetQueue(); static void clearQueue(); // Queue status (index). static bool isQueueEmpty (); private: // Instance variables. float m_fValue; bool m_bQueued; float m_fPrevValue; float m_fLastValue; // Human readable name/label. QString m_sName; // Value limits. float m_fMinValue; float m_fMaxValue; // Default value. float m_fDefaultValue; // Toggled value mode (max or min). bool m_bToggled; // Integer value mode. bool m_bInteger; // Automation curve association. qtractorCurve *m_pCurve; // List of observers (obviously) QList m_observers; }; //--------------------------------------------------------------------------- // qtractorObserver - Scalar parameter value control/view. class qtractorObserver { public: // Constructor. qtractorObserver(qtractorSubject *pSubject = nullptr) : m_pSubject(pSubject) { if (m_pSubject) m_pSubject->attach(this); } // Virtual destructor. virtual ~qtractorObserver() { if (m_pSubject) m_pSubject->detach(this); } // Subject value accessor. void setSubject(qtractorSubject *pSubject) { if (m_pSubject /* && pSubject*/) m_pSubject->detach(this); m_pSubject = pSubject; if (m_pSubject) m_pSubject->attach(this); } qtractorSubject *subject() const { return m_pSubject; } // Indirect value accessors. void setValue(float fValue) { if (m_pSubject) m_pSubject->setValue(fValue, this); } float value() const { return (m_pSubject ? m_pSubject->value() : 0.0f); } float prevValue() const { return (m_pSubject ? m_pSubject->prevValue() : 0.0f); } float lastValue() const { return (m_pSubject ? m_pSubject->lastValue() : 0.0f); } // Value limits accessors. float maxValue() const { return (m_pSubject ? m_pSubject->maxValue() : 1.0f); } float minValue() const { return (m_pSubject ? m_pSubject->minValue() : 0.0f); } // Default value accessor. void setDefaultValue(float fDefaultValue) { if (m_pSubject) m_pSubject->setDefaultValue(fDefaultValue); } float defaultValue() const { return (m_pSubject ? m_pSubject->defaultValue() : 0.0f); } void resetValue() { if (m_pSubject) m_pSubject->resetValue(this); } // Queue status accessors. bool isQueued() const { return (m_pSubject ? m_pSubject->isQueued() : false); } // Toggled mode accessors. bool isToggled() const { return (m_pSubject ? m_pSubject->isToggled() : false); } // Integer mode accessors. bool isInteger() const { return (m_pSubject ? m_pSubject->isInteger() : false); } // Non toggled nor integer mode helper. bool isDecimal() const { return (m_pSubject ? m_pSubject->isDecimal() : false); } // Filter value within legal bounds. float safeValue(float fValue) const { return (m_pSubject ? m_pSubject->safeValue(fValue) : 0.0f); } // Normalized scale converters. float valueFromScale ( float fScale ) const { return (m_pSubject ? m_pSubject->valueFromScale(fScale) : 0.0f); } float scaleFromValue ( float fValue ) const { return (m_pSubject ? m_pSubject->scaleFromValue(fValue) : 0.0f); } // Automation curve association. void setCurve(qtractorCurve *pCurve) { if (m_pSubject) m_pSubject->setCurve(pCurve); } qtractorCurve *curve() const { return (m_pSubject ? m_pSubject->curve() : nullptr); } // Pure virtual view updater. virtual void update(bool bUpdate) = 0; private: // Instance variables. qtractorSubject *m_pSubject; }; #endif // __qtractorObserver_h // end of qtractorObserver.h qtractor-1.5.9/src/PaxHeaders/qtractorDssiPlugin.h0000644000000000000000000000013215101070305017234 xustar0030 mtime=1761898693.069267597 30 atime=1761898693.069267597 30 ctime=1761898693.069267597 qtractor-1.5.9/src/qtractorDssiPlugin.h0000644000175000001440000001035615101070305017231 0ustar00rncbcusers// qtractorDssiPlugin.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 __qtractorDssiPlugin_h #define __qtractorDssiPlugin_h #include "qtractorLadspaPlugin.h" #include #include //---------------------------------------------------------------------------- // qtractorDssiPluginType -- DSSI plugin type instance. // class qtractorDssiPluginType : public qtractorLadspaPluginType { public: // Constructor. qtractorDssiPluginType(qtractorPluginFile *pFile, unsigned long iIndex, const DSSI_Descriptor *pDssiDescriptor = nullptr) : qtractorLadspaPluginType(pFile, iIndex, qtractorPluginType::Dssi), m_pDssiDescriptor(pDssiDescriptor) {} // Destructor. ~qtractorDssiPluginType() { close(); } // Derived methods. bool open(); void close(); // Factory method (static) static qtractorDssiPluginType *createType( qtractorPluginFile *pFile, unsigned long iIndex); // DSSI descriptor method (static) static const DSSI_Descriptor *dssi_descriptor( qtractorPluginFile *pFile, unsigned long iIndex); // Specific accessors. const DSSI_Descriptor *dssi_descriptor() const { return m_pDssiDescriptor; } const QString& dssi_editor() const { return m_sDssiEditor; } private: // DSSI descriptor itself. const DSSI_Descriptor *m_pDssiDescriptor; // DSSI GUI excutable filename. QString m_sDssiEditor; }; //---------------------------------------------------------------------------- // qtractorDssiPlugin -- DSSI plugin instance. // class qtractorDssiPlugin : public qtractorLadspaPlugin { public: // Constructors. qtractorDssiPlugin(qtractorPluginList *pList, qtractorDssiPluginType *pDssiType); // Destructor. ~qtractorDssiPlugin(); // Channel/instance 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; // Continuous controller handler. void setController(int iController, int iValue); // Configuration (CLOB) stuff. void configure(const QString& sKey, const QString& sValue); // GUI Editor stuff. void openEditor(QWidget */*pParent*/); void closeEditor(); // GUI editor visibility state. void setEditorVisible(bool bVisible); bool isEditorVisible() const; // Specific accessors. const DSSI_Descriptor *dssi_descriptor() const; // Update all control output ports... void updateControlOuts(bool bForce = false); // Reset(null) internal editor reference. void clearEditor(); // Idle editor update (static) static void idleEditorAll(); protected: // Post-(re)initializer. void resetChannels(); private: // Care of multiple instances here. class DssiMulti *m_pDssiMulti; // Internal editor structure accessor... struct DssiEditor *m_pDssiEditor; // GUI editor visiability status. bool m_bEditorVisible; // Controller port map. qtractorPlugin::Param *m_apControllerMap[128]; // Tracking changes on output control ports. float *m_pfControlOutsLast; }; #endif // __qtractorDssiPlugin_h // end of qtractorDssiPlugin.h qtractor-1.5.9/src/PaxHeaders/qtractorAudioClip.h0000644000000000000000000000013215101070305017024 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.062554172 30 ctime=1761898693.062554172 qtractor-1.5.9/src/qtractorAudioClip.h0000644000175000001440000001461215101070305017020 0ustar00rncbcusers// qtractorAudioClip.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 __qtractorAudioClip_h #define __qtractorAudioClip_h #include "qtractorClip.h" #include "qtractorAudioBuffer.h" // Forward declarations. class qtractorAudioPeak; //---------------------------------------------------------------------- // class qtractorAudioClip -- Audio file/buffer clip. // class qtractorAudioClip : public qtractorClip { public: // Constructor. qtractorAudioClip(qtractorTrack *pTrack); // Copy constructor. qtractorAudioClip(const qtractorAudioClip& clip); // Destructor. ~qtractorAudioClip(); // Time-stretching. void setTimeStretch(float fTimeStretch) { m_fTimeStretch = fTimeStretch; } float timeStretch() const { return m_fTimeStretch; } // Pitch-shifting. void setPitchShift(float fPitchShift) { m_fPitchShift = fPitchShift; } float pitchShift() const { return m_fPitchShift; } // Time-stretch/pitch-shifting engine mode flags. void setStretcherFlags(unsigned int iStretcherFlags) { m_iStretcherFlags = iStretcherFlags; } unsigned int stretcherFlags() const { return m_iStretcherFlags; } void setStretcherFlag(unsigned int iStretcherFlag, bool bOn) { if (bOn) m_iStretcherFlags |= iStretcherFlag; else m_iStretcherFlags &= ~iStretcherFlag; } bool isStretcherFlag (unsigned int iStretcherFlag) const { return (m_iStretcherFlags & iStretcherFlag); } // Alternating overlap tag. unsigned int overlap() const { return m_iOverlap; } // Clip (re)open method. void open(); // The main use method. bool openAudioFile(const QString& sFilename, int iMode = qtractorAudioFile::Read); // Sequence properties accessors. qtractorAudioBuffer *buffer() const { return (m_pData ? m_pData->buffer() : nullptr); } // Direct write method. void write(float **ppBuffer, unsigned int iFrames, unsigned short iChannels = 0, unsigned int iOffset = 0); // Intra-clip frame positioning. void seek(unsigned long iFrame); // Reset clip state. void reset(bool bLooping); // Loop positioning. void setLoop(unsigned long iLoopStart, unsigned long iLoopEnd); // Clip close-commit (record specific) void close(); // Audio clip special process cycle executive. void process(unsigned long iFrameStart, unsigned long iFrameEnd); // Audio clip freewheeling process cycle executive (needed for export). void process_export(unsigned long iFrameStart, unsigned long iFrameEnd); // Clip paint method. void draw(QPainter *pPainter, const QRect& clipRect, unsigned long iClipOffset); // Clip update method (no-op). void update() {} // Audio clip tool-tip. QString toolTip() const; // Audio clip export method. typedef void (*ClipExport)(float **, unsigned int, void *); bool clipExport(ClipExport pfnClipExport, void *pvArg, unsigned long iOffset = 0, unsigned long iLength = 0) const; // Most interesting key/data (ref-counted?)... class Key; class Data { public: // Constructor. Data(qtractorTrack *pTrack, unsigned short iChannels) : m_pBuff(new qtractorAudioBuffer( pTrack->syncThread(), iChannels)) {} // Destructor. ~Data() { clear(); delete m_pBuff; } // Buffer accessor. qtractorAudioBuffer *buffer() const { return m_pBuff; } // Direct write method. void write (float **ppBuffer, unsigned int iFrames, unsigned short iChannels, unsigned int iOffset) { m_pBuff->write(ppBuffer, iFrames, iChannels, iOffset); } // Direct sync method. void syncExport() { m_pBuff->syncExport(); } // Intra-clip frame positioning. void seek(unsigned long iFrame) { m_pBuff->seek(iFrame); } // Reset buffer state. void reset(bool bLooping) { m_pBuff->reset(bLooping); } // Loop positioning. void setLoop(unsigned long iLoopStart, unsigned long iLoopEnd) { m_pBuff->setLoop(iLoopStart, iLoopEnd); } // Ref-counting related methods. void attach(qtractorAudioClip *pAudioClip) { m_clips.append(pAudioClip); } void detach(qtractorAudioClip *pAudioClip) { m_clips.removeAll(pAudioClip); } unsigned short count() const { return m_clips.count(); } const QList& clips() const { return m_clips; } void clear() { m_clips.clear(); } private: // Interesting variables. qtractorAudioBuffer *m_pBuff; // Ref-counting related stuff. QList m_clips; }; typedef QHash Hash; // Manage local hash key. void insertHashKey(); void updateHashKey(); void removeHashKey(); // Un/relink (de/clone) local hash data. void unlinkHashData(); void relinkHashData(); // Whether local hash is being shared. bool isHashLinked() const; // Make sure the clip hash-table gets reset. static void clearHashTable(); protected: // Virtual document element methods. bool loadClipElement(qtractorDocument *pDocument, QDomElement *pElement); bool saveClipElement(qtractorDocument *pDocument, QDomElement *pElement); // Private cleanup. void closeAudioFile(); // Alternating overlap test. bool isOverlap(unsigned int iOverlapSize) const; // Gain/panning fractionalizer(tm)... void updateFractGains(qtractorAudioBuffer *pBuff); private: // Instance variables. qtractorAudioPeak *m_pPeak; float m_fTimeStretch; float m_fPitchShift; unsigned int m_iStretcherFlags; // Alternate overlap tag. unsigned int m_iOverlap; // Gain fractionalizers(tm)... typedef struct { int num, den; } FractGain; FractGain *m_pFractGains; // Most interesting key/data (ref-counted?)... Key *m_pKey; Data *m_pData; static Hash g_hashTable; }; #endif // __qtractorAudioClip_h // end of qtractorAudioClip.h qtractor-1.5.9/src/PaxHeaders/qtractorLadspaPlugin.h0000644000000000000000000000012715101070305017542 xustar0029 mtime=1761898693.07326761 29 atime=1761898693.07326761 29 ctime=1761898693.07326761 qtractor-1.5.9/src/qtractorLadspaPlugin.h0000644000175000001440000001064715101070305017536 0ustar00rncbcusers// qtractorLadspaPlugin.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 __qtractorLadspaPlugin_h #define __qtractorLadspaPlugin_h #include "qtractorPlugin.h" #include //---------------------------------------------------------------------------- // qtractorLadspaPluginType -- LADSPA plugin type instance. // class qtractorLadspaPluginType : public qtractorPluginType { public: // Constructor. qtractorLadspaPluginType(qtractorPluginFile *pFile, unsigned long iIndex, qtractorPluginType::Hint typeHint = qtractorPluginType::Ladspa, const LADSPA_Descriptor *pLadspaDescriptor = nullptr) : qtractorPluginType(pFile, iIndex, typeHint), m_pLadspaDescriptor(pLadspaDescriptor) {} // Destructor. ~qtractorLadspaPluginType() { close(); } // Derived methods. bool open(); void close(); // Factory method (static) static qtractorLadspaPluginType *createType( qtractorPluginFile *pFile, unsigned long iIndex); // LADSPA descriptor method (static) static const LADSPA_Descriptor *ladspa_descriptor( qtractorPluginFile *pFile, unsigned long iIndex); // Specific accessors. const LADSPA_Descriptor *ladspa_descriptor() const { return m_pLadspaDescriptor; } // Instance cached-deferred accesors. const QString& aboutText(); protected: // LADSPA descriptor itself. const LADSPA_Descriptor *m_pLadspaDescriptor; }; //---------------------------------------------------------------------------- // qtractorLadspaPlugin -- LADSPA plugin instance. // class qtractorLadspaPlugin : public qtractorPlugin { public: // Constructors. qtractorLadspaPlugin(qtractorPluginList *pList, qtractorLadspaPluginType *pLadspaType); // Destructor. ~qtractorLadspaPlugin(); // 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. const LADSPA_Descriptor *ladspa_descriptor() const; LADSPA_Handle ladspa_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]; } // Plugin current latency (in frames); unsigned long latency() const { return (m_pfLatency ? *m_pfLatency : 0.0f); } protected: // Instance variables. LADSPA_Handle *m_phInstances; // List of output control port indexes and data. unsigned long *m_piControlOuts; float *m_pfControlOuts; // List of audio port indexes. unsigned long *m_piAudioIns; unsigned long *m_piAudioOuts; // Dummy I/O buffers. float *m_pfIDummy; float *m_pfODummy; // Plugin current latency output control port; float *m_pfLatency; }; //---------------------------------------------------------------------------- // qtractorLadspaPlugin::Param -- LADSPA plugin control input port instance. // class qtractorLadspaPlugin::Param : public qtractorPlugin::Param { public: // Constructors. Param(qtractorLadspaPlugin *pLadspaPlugin, 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; private: // Instance variables. LADSPA_PortRangeHintDescriptor m_portHints; }; #endif // __qtractorLadspaPlugin_h // end of qtractorLadspaPlugin.h qtractor-1.5.9/src/PaxHeaders/qtractorSessionCommand.h0000644000000000000000000000013215101070305020075 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.088267658 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorSessionCommand.h0000644000175000001440000000650015101070305020066 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.9/src/PaxHeaders/qtractorMidiEditorForm.ui0000644000000000000000000000013215101070305020216 xustar0030 mtime=1761898693.081267635 30 atime=1761898693.081267635 30 ctime=1761898693.081267635 qtractor-1.5.9/src/qtractorMidiEditorForm.ui0000644000175000001440000014720315101070305020215 0ustar00rncbcusers rncbc aka Rui Nuno Capela qtractor - A MIDI/Audio 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. qtractorMidiEditorForm 0 0 900 600 MIDI Editor 0 0 840 23 &File &Track Instrum&ent &View &Toolbars &Windows Not&e Type Val&ue Type &Ghost Track &Zoom S&nap Sc&ale &Tools &Edit Select &Mode &Select I&nsert Remo&ve T&ransport &Note St&ep &Help Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false Qt::Horizontal BottomToolBarArea false &Save Save Save Save current MIDI clip to existing file name Save &As... Save As Save as Save current MIDI clip with another file name true &Mute Mute Mute Mute current MIDI clip &Unlink Unlink Unlink Unlink current MIDI clip true Recor&d Record Record Record current MIDI clip (overdub) &Inputs Track Inputs Track inputs Show current MIDI clip/track input bus connections &Outputs Track Outputs Track outputs Show current MIDI clip/track output bus connections &Properties... Track Properties Track properties Edit current MIDI clip/track properties Shift+F2 &Properties... Properties Properties Edit current MIDI clip properties F4 &Range Set Clip Range Clip range Set edit-range from clip extents true &Loop Set Clip Loop Clip loop Set loop-range from clip extents &Close Close Close Close this MIDI clip editor &Undo Undo Undo Undo last edit operation Ctrl+Z &Redo Redo Redo Redo last edit operation Ctrl+Shift+Z Cu&t Cut Cut Cut current selection into the local clipboard Ctrl+X &Copy Copy Copy Copy current selection to the local clipboard Ctrl+C &Paste Paste Paste Paste local clipboard contents into the current MIDI clip Ctrl+V Past&e Repeat... Paste Repeat Paste repeat Paste/repeat local clipboard contents into the current MIDI clip Ctrl+Shift+V &Delete Delete Delete Delete current selection Del true Edit Of&f Edit Off Edit off Set edit mode off true Edit &On Edit On Edit on Set edit mode on true Edit &Draw Edit draw mode Edit draw mode (notes) &All Select All Select all Select all Ctrl+A &None Select None Select none Select none Ctrl+Shift+A &Invert Select Invert Select invert Select invert Ctrl+I &Range Select Range Select range Mark range as selected Ctrl+R &Range Insert Range Insert range Insert range as selected Ctrl+Ins &Step Insert step Insert step (rest) Insert step (rest) Right &Range Remove Range Remove range Remove range as selected Ctrl+Del &Quantize... Quantize Quantize Quantize selection &Transpose... Transpose Transpose Transpose selection &Normalize... Normalize Normalize Normalize selection &Randomize... Randomize Randomize Randomize selection Resi&ze... Resize Resize Resize selection Re&scale... Rescale Rescale Rescale selection T&imeshift... Timeshift Timeshift Timeshift selection T&empo ramp... Tempo ramp Tempo ramp Tempo ramp selection true &Menubar Menubar Menubar Show/hide the menubar Ctrl+M true &Statusbar Statusbar Statusbar Show/hide the statusbar true &File File Toolbar File toolbar Show/hide the file toolbar true &Edit Edit Toolbar Edit toolbar Show/hide the edit toolbar true &View View Toolbar View toolbar Show/hide the view toolbar true &Transport Transport Toolbar Transport toolbar Show/hide the transport toolbar true T&ime Time Toolbar Time toolbar Show/hide the time toolbar true &Scale Scale Toolbar Scale toolbar Show/hide the scale toolbar true Thum&b Thumb Toolbar Thumb toolbar Show/hide the thumb view toolbar true &Events View events View events Show/hide the events list true Note &Names Note Names Note names Whether to show note names true Note &Duration Note Duration Note duration Whether note events are shown proportional to duration true Note &Color Note Color Note color Whether note events are colored according to pitch true &Drum Mode Drum Mode Drum mode Whether note onset events are displayed as diamonds true &Value Color Value Color Value color Whether note events are colored according to value (velocity) &In Zoom In Zoom in Zoom in Ctrl++ &Out Zoom Out Zoom out Zoom out Ctrl+- &Reset Zoom Reset Zoom reset Zoom reset Ctrl+1 true &Horizontal Horizontal Zoom Horizontal zoom Horizontal zoom mode true &Vertical Vertical Zoom Vertical zoom Vertical zoom mode true &All All Zoom All zoom All zoom mode true &Zebra Zebra Zebra Bar zebra view mode true &Grid Grid Grid Snap grid view mode true Too&l Tips Tool tips Tool tips Floating tool tips view mode &Refresh Refresh Refresh Refresh views F5 true &Preview Notes Preview Notes Preview notes Preview notes while editing (scrub) true F&ollow Playhead Follow Playhead Follow playhead Follow playhead &Backward Backward Backward Transport backward Backspace true Re&wind Rewind Rewind Transport rewind true F&ast Forward Fast Forward Fast forward Transport fast forward &Forward Forward Forward Transport forward &Backward Step Backward Step backward Transport step backward &Forward Step Forward Step forward Transport step forward &Backward Note Backward Step note backward Transport step note backward &Forward Note Forward Step note forward Transport step note forward true &Loop Loop Loop Transport loop Ctrl+Shift+L Loop &Set Loop Set Loop set Transport loop set Ctrl+L &Stop Stop Stop Transport stop true &Play Play Play Transport play/pause Space true &Record Record Record Transport record true &Punch Punch Punch in/out Transport punch in/out Ctrl+Shift+P Punch Se&t Punch Set Punch in/out set Transport punch in/out set Ctrl+P Pa&nic Panic Panic All MIDI tracks shut off (panic) &Shortcuts... Shortcuts Shortcuts Keyboard shortcuts &About... About About Show information about this application program About &Qt... About Qt About Qt Show information about the Qt toolkit qtractor-1.5.9/src/PaxHeaders/qtractorInsertPlugin.cpp0000644000000000000000000000013215101070305020131 xustar0030 mtime=1761898693.072267607 30 atime=1761898693.072267607 30 ctime=1761898693.072267607 qtractor-1.5.9/src/qtractorInsertPlugin.cpp0000644000175000001440000013412715101070305020131 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 = 2; 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 = 2; 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); // 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; } // 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 // 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; } //---------------------------------------------------------------------------- // 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.9/src/PaxHeaders/qtractorMidiControlTypeGroup.h0000644000000000000000000000013215101070305021255 xustar0030 mtime=1761898693.078267626 30 atime=1761898693.078267626 30 ctime=1761898693.078267626 qtractor-1.5.9/src/qtractorMidiControlTypeGroup.h0000644000175000001440000000522615101070305021252 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.9/src/PaxHeaders/qtractorAudioSndFile.h0000644000000000000000000000013215101070305017461 xustar0030 mtime=1761898693.064267582 30 atime=1761898693.064267582 30 ctime=1761898693.064267582 qtractor-1.5.9/src/qtractorAudioSndFile.h0000644000175000001440000000512015101070305017447 0ustar00rncbcusers// qtractorAudioSndFile.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 __qtractorAudioSndFile_h #define __qtractorAudioSndFile_h #include "qtractorAudioFile.h" // libsndfile API. #include //---------------------------------------------------------------------- // class qtractorAudioSndFile -- Buffered audio file declaration. // class qtractorAudioSndFile : public qtractorAudioFile { public: // Constructor. qtractorAudioSndFile(unsigned short iChannels = 0, unsigned int iSampleRate = 0, unsigned int iBufferSize = 0, int iFormat = 0); // Destructor. virtual ~qtractorAudioSndFile(); // 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; // Check whether given file type/format is valid. (static) static bool isValidFormat(int iType, int iFormat); // Translate format index into libsndfile specific. (static) static int format(int iType, int iFormat); protected: // De/interleaving buffer (re)allocation check. void allocBufferCheck(unsigned int iBufferSize); private: int m_iMode; // open mode (Read|Write). SNDFILE *m_pSndFile; // libsndfile descriptor. SF_INFO m_sfinfo; // libsndfile info struct. // De/interleaving buffer stuff. float *m_pBuffer; unsigned int m_iBufferSize; }; #endif // __qtractorAudioSndFile_h // end of qtractorAudioSndFile.h qtractor-1.5.9/src/PaxHeaders/qtractorZipFile.h0000644000000000000000000000013215101070305016515 xustar0030 mtime=1761898693.093267673 30 atime=1761898693.093267673 30 ctime=1761898693.093267673 qtractor-1.5.9/src/qtractorZipFile.h0000644000175000001440000000516115101070305016510 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.9/src/PaxHeaders/qtractorCurveCommand.h0000644000000000000000000000013215101070305017536 xustar0030 mtime=1761898693.069267597 30 atime=1761898693.069267597 30 ctime=1761898693.069267597 qtractor-1.5.9/src/qtractorCurveCommand.h0000644000175000001440000002071215101070305017530 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.9/src/PaxHeaders/qtractorInstrumentForm.cpp0000644000000000000000000000013215101070305020502 xustar0030 mtime=1761898693.072267607 30 atime=1761898693.072267607 30 ctime=1761898693.072267607 qtractor-1.5.9/src/qtractorInstrumentForm.cpp0000644000175000001440000005421415101070305020500 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.9/src/PaxHeaders/qtractorTrackTime.h0000644000000000000000000000013215101070305017036 xustar0030 mtime=1761898693.091267667 30 atime=1761898693.091267667 30 ctime=1761898693.091267667 qtractor-1.5.9/src/qtractorTrackTime.h0000644000175000001440000000651515101070305017035 0ustar00rncbcusers// qtractorTrackTime.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 __qtractorTrackTime_h #define __qtractorTrackTime_h #include "qtractorScrollView.h" #include "qtractorTimeScale.h" #include // Forward declarations. class qtractorTracks; class QResizeEvent; class QMouseEvent; class QKeyEvent; //---------------------------------------------------------------------------- // qtractorTrackTime -- Track time scale widget. class qtractorTrackTime : public qtractorScrollView { Q_OBJECT public: // Constructor. qtractorTrackTime(qtractorTracks *pTracks, QWidget *pParent = nullptr); // Rectangular contents update. void updateContents(const QRect& rect); // Overall contents update. void updateContents(); protected: // Resize event handler. void resizeEvent(QResizeEvent *pResizeEvent); // Draw the time scale. void drawContents(QPainter *pPainter, const QRect& rect); // Check if some position header is to be dragged... bool dragHeadStart(const QPoint& pos); // Handle selection with mouse. void mousePressEvent(QMouseEvent *pMouseEvent); void mouseMoveEvent(QMouseEvent *pMouseEvent); void mouseReleaseEvent(QMouseEvent *pMouseEvent); // Tempo-map dialog accessor. void mouseDoubleClickEvent(QMouseEvent *pMouseEvent); // Handle zoom with mouse wheel. void wheelEvent(QWheelEvent *pWheelEvent); // Reset drag/select state. void resetDragState(); // Keyboard event handler. void keyPressEvent(QKeyEvent *pKeyEvent); // Context menu request slot (dummy). void contextMenuEvent(QContextMenuEvent *); // Trap for help/tool-tip events. bool eventFilter(QObject *pObject, QEvent *pEvent); // Show dragging tooltip... void showToolTip(unsigned long iFrame) const; void showToolTip(const QRect& rect) const; protected slots: // To have timeline in h-sync with main track view. void contentsXMovingSlot(int cx, int cy); // (Re)create the time scale pixmap. void updatePixmap(int cx, int cy); private: // The logical parent binding. qtractorTracks *m_pTracks; // Local double-buffering pixmap. QPixmap m_pixmap; // The current selecting/dragging head stuff. enum DragState { DragNone = 0, DragStart, DragSelect, DragPlayHead, DragMarker, DragEditHead, DragEditTail, DragLoopStart, DragLoopEnd, DragPunchIn, DragPunchOut } m_dragState, m_dragCursor; QRect m_rectDrag; QPoint m_posDrag; qtractorTimeScale::Marker *m_pDragMarker; }; #endif // __qtractorTrackTime_h // end of qtractorTrackTime.h qtractor-1.5.9/src/PaxHeaders/qtractorTrackButton.cpp0000644000000000000000000000013215101070305017746 xustar0030 mtime=1761898693.090267664 30 atime=1761898693.090267664 30 ctime=1761898693.090267664 qtractor-1.5.9/src/qtractorTrackButton.cpp0000644000175000001440000001260015101070305017735 0ustar00rncbcusers// qtractorTrackButton.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 "qtractorTrackButton.h" #include "qtractorMidiControlObserver.h" #include "qtractorMidiControlObserverForm.h" #include //---------------------------------------------------------------------------- // qtractorMidiControlButton -- MIDI controller observer tool button. // Constructor. qtractorMidiControlButton::qtractorMidiControlButton ( QWidget *pParent ) : qtractorObserverWidget (pParent), m_pMidiControlAction(nullptr) { QPushButton::setFocusPolicy(Qt::NoFocus); // QPushButton::setToolButtonStyle(Qt::ToolButtonTextOnly); QPushButton::setCheckable(true); } // MIDI controller/observer attachment (context menu) activator. void qtractorMidiControlButton::addMidiControlAction ( qtractorMidiControlObserver *pMidiObserver ) { if (m_pMidiControlAction) removeAction(m_pMidiControlAction); m_pMidiControlAction = qtractorMidiControlObserverForm::addMidiControlAction( this, this, pMidiObserver); } void qtractorMidiControlButton::midiControlActionSlot (void) { qtractorMidiControlObserverForm::midiControlAction( this, qobject_cast (sender())); } void qtractorMidiControlButton::midiControlMenuSlot ( const QPoint& pos ) { qtractorMidiControlObserverForm::midiControlMenu( qobject_cast (sender()), pos); } //---------------------------------------------------------------------------- // qtractorTrackButton -- Track tool button (observer). // Constructor. qtractorTrackButton::qtractorTrackButton ( qtractorTrack *pTrack, qtractorTrack::ToolType toolType, QWidget *pParent ) : qtractorMidiControlButton(pParent) { m_pTrack = pTrack; m_toolType = toolType; switch (m_toolType) { case qtractorTrack::Record: QPushButton::setText("R"); m_rgbOn = Qt::red; break; case qtractorTrack::Mute: QPushButton::setText("M"); m_rgbOn = Qt::yellow; break; case qtractorTrack::Solo: QPushButton::setText("S"); m_rgbOn = Qt::cyan; break; } updateTrack(); // Visitor setup. QObject::connect(this, SIGNAL(toggled(bool)), SLOT(toggledSlot(bool))); } // Destructor. qtractorTrackButton::~qtractorTrackButton (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; qtractorMidiControlObserver *pMidiObserver = nullptr; switch (m_toolType) { case qtractorTrack::Record: pMidiObserver = m_pTrack->recordObserver(); break; case qtractorTrack::Mute: pMidiObserver = m_pTrack->muteObserver(); break; case qtractorTrack::Solo: pMidiObserver = m_pTrack->soloObserver(); break; } if (pMidiObserver) pMidiControl->unmapMidiObserverWidget(pMidiObserver, this); } // Visitors overload. void qtractorTrackButton::updateValue ( float fValue ) { const bool bBlockSignals = QPushButton::blockSignals(true); QPalette pal; const QColor rgbText = pal.buttonText().color(); const QColor rgbOff = pal.button().color(); const bool bOn = (fValue > 0.0f); pal.setColor(QPalette::ButtonText, bOn ? m_rgbOn.darker() : rgbText); pal.setColor(QPalette::Button, bOn ? m_rgbOn : rgbOff); QPushButton::setPalette(pal); QPushButton::setChecked(bOn); QPushButton::blockSignals(bBlockSignals); } // Special toggle slot. void qtractorTrackButton::toggledSlot ( bool bOn ) { // Just emit proper signal... m_pTrack->stateChangeNotify(m_toolType, bOn); } // Specific accessors. void qtractorTrackButton::setTrack ( qtractorTrack *pTrack ) { m_pTrack = pTrack; updateTrack(); } qtractorTrack *qtractorTrackButton::track (void) const { return m_pTrack; } qtractorTrack::ToolType qtractorTrackButton::toolType (void) const { return m_toolType; } // Track state (record, mute, solo) button setup. void qtractorTrackButton::updateTrack (void) { qtractorMidiControlObserver *pMidiObserver = nullptr; switch (m_toolType) { case qtractorTrack::Record: setSubject(m_pTrack->recordSubject()); pMidiObserver = m_pTrack->recordObserver(); break; case qtractorTrack::Mute: setSubject(m_pTrack->muteSubject()); pMidiObserver = m_pTrack->muteObserver(); break; case qtractorTrack::Solo: setSubject(m_pTrack->soloSubject()); pMidiObserver = m_pTrack->soloObserver(); break; } if (pMidiObserver) { pMidiObserver->setCurveList(m_pTrack->curveList()); addMidiControlAction(pMidiObserver); } observer()->update(true); } // Refresh color (palette) state buttons void qtractorTrackButton::updateTrackButton (void) { updateValue(isChecked() ? 1.0f : 0.0f); } // end of qtractorTrackButton.cpp qtractor-1.5.9/src/PaxHeaders/qtractorScrollView.h0000644000000000000000000000013215101070305017244 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.088267658 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorScrollView.h0000644000175000001440000000551715101070305017244 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.9/src/PaxHeaders/qtractorMidiEngine.cpp0000644000000000000000000000013215101070305017516 xustar0030 mtime=1761898693.082267639 30 atime=1761898693.081267635 30 ctime=1761898693.082267639 qtractor-1.5.9/src/qtractorMidiEngine.cpp0000644000175000001440000045354315101070305017524 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.9/src/PaxHeaders/qtractorMidiEventList.h0000644000000000000000000000013215101070305017673 xustar0030 mtime=1761898693.082267639 30 atime=1761898693.082267639 30 ctime=1761898693.082267639 qtractor-1.5.9/src/qtractorMidiEventList.h0000644000175000001440000000715615101070305017674 0ustar00rncbcusers// qtractorMidiEventList.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 __qtractorMidiEventList_h #define __qtractorMidiEventList_h #include #include // Forwards. class qtractorMidiEditor; class qtractorMidiSequence; class qtractorMidiEvent; class QAbstractItemModel; //---------------------------------------------------------------------------- // qtractorMidiEventListView -- Custom (tree) list view. class qtractorMidiEventListView : public QTreeView { public: // Constructor. qtractorMidiEventListView(QWidget *pParent = nullptr); // Destructor. ~qtractorMidiEventListView(); // Settlers. void setEditor(qtractorMidiEditor *pEditor); qtractorMidiEditor *editor() const; // Refreshener. void refresh(); // Locators. 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; // Selections. void selectEvent(qtractorMidiEvent *pEvent, bool bSelect = true); protected: // Custom editor closing stub. void closeEditor(QWidget */*pEditor*/, QAbstractItemDelegate::EndEditHint /*hint*/); private: // Forward decls. class ItemModel; class ItemDelegate; // Instance variables. ItemModel *m_pItemModel; ItemDelegate *m_pItemDelegate; }; //---------------------------------------------------------------------------- // qtractorMidiEventListView -- MIDI Event List dockable window. class qtractorMidiEventList : public QDockWidget { Q_OBJECT public: // Constructor. qtractorMidiEventList(QWidget *pParent = nullptr); // Destructor. ~qtractorMidiEventList(); // Settlers. void setEditor(qtractorMidiEditor *pEditor); qtractorMidiEditor *editor() const; // Event list view refreshner. void refresh(); protected: // Full update when show up. void showEvent(QShowEvent *); // Just about to notify main-window that we're closing. void closeEvent(QCloseEvent *); // Context menu request event handler. void contextMenuEvent(QContextMenuEvent *); protected slots: // Internal list view changes slots. void currentRowChangedSlot(const QModelIndex&, const QModelIndex&); void selectionChangedSlot(const QItemSelection&, const QItemSelection&); // External editor notification slots. void selectNotifySlot(qtractorMidiEditor *); void changeNotifySlot(qtractorMidiEditor *); private: // Instance variables. qtractorMidiEventListView *m_pListView; // Cross-selection fake-mutex. int m_iSelectUpdate; }; #endif // __qtractorMidiEventList_h // end of qtractorMidiEventList.h qtractor-1.5.9/src/PaxHeaders/qtractorCurveFile.cpp0000644000000000000000000000013215101070305017372 xustar0030 mtime=1761898693.069267597 30 atime=1761898693.069267597 30 ctime=1761898693.069267597 qtractor-1.5.9/src/qtractorCurveFile.cpp0000644000175000001440000002171715101070305017372 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.9/src/PaxHeaders/qtractorSpinBox.h0000644000000000000000000000013215101070305016535 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorSpinBox.h0000644000175000001440000001564215101070305016535 0ustar00rncbcusers// qtractorSpinBox.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 __qtractorSpinBox_h #define __qtractorSpinBox_h #include #include #include // Forward declartions. class QLineEdit; class QShowEvent; class QContextMenuEvent; class QKeyEvent; //------------------------------------------------------------------------- // qtractorSpinBox - A better QDoubleSpinBox widget. class qtractorSpinBox : public QDoubleSpinBox { Q_OBJECT public: // Constructor. qtractorSpinBox(QWidget *pParent = nullptr); // Edit mode behavior: // DefaultMode - default (immediate value changes) behavior. // DeferredMode - deferred value changes (to when editing is finished). enum EditMode { DefaultMode = 0, DeferredMode }; // Set spin-box edit mode behavior. static void setEditMode(EditMode editMode); static EditMode editMode(); protected slots: // Alternate value change behavior handlers. void lineEditTextChanged(const QString&); void spinBoxEditingFinished(); void spinBoxValueChanged(double); signals: // Alternate value change signal. void valueChangedEx(double); protected: // Inherited/override methods. QValidator::State validate(QString& sText, int& iPos) const; private: // Alternate edit behavior tracking. int m_iTextChanged; // Spin-box edit mode behavior. static EditMode g_editMode; }; //---------------------------------------------------------------------------- // qtractorTimeSpinBox -- A time-scale formatted spin-box widget. class qtractorTimeSpinBox : public QAbstractSpinBox { Q_OBJECT public: // Constructor. qtractorTimeSpinBox(QWidget *pParent = nullptr); // Time-scale accessors. void setTimeScale(qtractorTimeScale *pTimeScale); qtractorTimeScale *timeScale() const; // Display-format accessors. void setDisplayFormat(qtractorTimeScale::DisplayFormat displayFormat); qtractorTimeScale::DisplayFormat displayFormat() const; void updateDisplayFormat(); // Nominal value (in frames) accessors. void setValue(unsigned long iValue, bool bNotifyChange = true); unsigned long value() const; // Minimum value (in frames) accessors. void setMinimum(unsigned long iMinimum); unsigned long minimum() const; // Maximum value (in frames) accessors. void setMaximum(unsigned long iMaximum); unsigned long maximum() const; // Differential value mode (BBT format only) accessor. void setDeltaValue(bool bDeltaValue, unsigned long iDeltaValue = 0); bool isDeltaValue() const; unsigned long deltaValue() const; // Editing stabilizer. unsigned long valueFromText() const; signals: // Common value change notification. void valueChanged(unsigned long); void valueChanged(const QString&); // Display format change notification. void displayFormatChanged(int); protected: // Mark that we got actual value. void showEvent(QShowEvent *); // Inherited/override methods. QValidator::State validate(QString& sText, int& iPos) const; void fixup(QString& sText) const; void stepBy(int iSteps); StepEnabled stepEnabled() const; // Value/text format converters. unsigned long valueFromText(const QString& sText) const; QString textFromValue(unsigned long iValue) const; // Common value/text setlers. bool updateValue(unsigned long iValue, bool bNotifyChange); void updateText(); // Local context menu handler. void contextMenuEvent(QContextMenuEvent *pContextMenuEvent); // Keyboard event handler. void keyPressEvent(QKeyEvent *pKeyEvent); protected slots: // Pseudo-fixup slot. void editingFinishedSlot(); void valueChangedSlot(const QString&); private: // Instance variables. qtractorTimeScale *m_pTimeScale; qtractorTimeScale::DisplayFormat m_displayFormat; unsigned long m_iValue; unsigned long m_iDefaultValue; unsigned long m_iMinimumValue; unsigned long m_iMaximumValue; unsigned long m_iDeltaValue; bool m_bDeltaValue; int m_iValueChanged; }; //---------------------------------------------------------------------------- // qtractorTempoSpinBox -- A tempo/time-signature formatted spin-box widget. class qtractorTempoSpinBox : public QAbstractSpinBox { Q_OBJECT public: // Constructor. qtractorTempoSpinBox(QWidget *pParent = nullptr); // Nominal tempo value (BPM) accessors. void setTempo(float fTempo, bool bNotifyChange = true); float tempo() const; // Nominal time-signature numerator (beats/bar) accessors. void setBeatsPerBar(unsigned short iBeatsPerBar, bool bNotifyChange = true); unsigned short beatsPerBar() const; // Nominal time-signature denominator (beat-divisor) accessors. void setBeatDivisor(unsigned short iBeatDivisor, bool bNotifyChange = true); unsigned short beatDivisor() const; signals: // Common value change notification. void valueChanged(float, unsigned short, unsigned short); void valueChanged(const QString&); protected: // Mark that we got actual value. void showEvent(QShowEvent *); // Inherited/override methods. QValidator::State validate(QString& sText, int& iPos) const; void fixup(QString& sText) const; void stepBy(int iSteps); StepEnabled stepEnabled() const; // Value/text format converters. float tempoFromText(const QString& sText) const; unsigned short beatsPerBarFromText(const QString& sText) const; unsigned short beatDivisorFromText(const QString& sText) const; QString textFromValue(float fTempo, unsigned short iBeatsPerBar, unsigned short iBeatDivisor) const; // Common value/text setlers. bool updateValue(float fTempo, unsigned short iBeatsPerBar, unsigned short iBeatDivisor, bool bNotifyChange); void updateText(); // Keyboard event handler. void keyPressEvent(QKeyEvent *pKeyEvent); protected slots: // Pseudo-fixup slot. void valueChangedSlot(const QString&); void editingFinishedSlot(); private: // Instance variables. float m_fTempo; unsigned short m_iBeatsPerBar; unsigned short m_iBeatDivisor; float m_fDefaultTempo; unsigned short m_iDefaultBeatsPerBar; unsigned short m_iDefaultBeatDivisor; int m_iValueChanged; QChar m_decp; }; #endif // __qtractorSpinBox_h // end of qtractorSpinBox.h qtractor-1.5.9/src/PaxHeaders/qtractorAudioConnect.cpp0000644000000000000000000000013215101070305020061 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.062554172 30 ctime=1761898693.062554172 qtractor-1.5.9/src/qtractorAudioConnect.cpp0000644000175000001440000003221015101070305020047 0ustar00rncbcusers// qtractorAudioConnect.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 "qtractorAudioConnect.h" #include "qtractorAudioEngine.h" #include "qtractorSession.h" #include #ifdef CONFIG_JACK_METADATA #include #include static QString prettyName ( jack_uuid_t uuid, const QString& sDefaultName ) { QString sPrettyName = sDefaultName; char *pszValue = nullptr; char *pszType = nullptr; if (::jack_get_property(uuid, JACK_METADATA_PRETTY_NAME, &pszValue, &pszType) == 0) { if (pszValue) { sPrettyName = QString::fromUtf8(pszValue); ::jack_free(pszValue); } if (pszType) ::jack_free(pszType); } return sPrettyName; } #endif //---------------------------------------------------------------------- // class qtractorAudioPortItem -- Jack port list item. // // Constructor. qtractorAudioPortItem::qtractorAudioPortItem ( qtractorAudioClientItem *pClientItem, unsigned long ulPortFlags ) : qtractorPortListItem(pClientItem) { if (ulPortFlags & JackPortIsInput) { QTreeWidgetItem::setIcon(0, qtractorAudioConnect::icon(ulPortFlags & JackPortIsPhysical ? qtractorAudioConnect::PortPhysIn : qtractorAudioConnect::PortIn)); } else { QTreeWidgetItem::setIcon(0, qtractorAudioConnect::icon(ulPortFlags & JackPortIsPhysical ? qtractorAudioConnect::PortPhysOut : qtractorAudioConnect::PortOut)); } } // Default destructor. qtractorAudioPortItem::~qtractorAudioPortItem (void) { } // Proto-pretty/alias display name method (virtual override). void qtractorAudioPortItem::updatePortName (void) { #ifdef CONFIG_JACK_METADATA jack_client_t *pJackClient = nullptr; qtractorAudioClientListView *pAudioClientListView = static_cast ( QTreeWidgetItem::treeWidget()); if (pAudioClientListView) pJackClient = pAudioClientListView->jackClient(); if (pJackClient) { const QString& sPortName = portName(); const QString& sClientPort = clientName() + ':' + sPortName; const QByteArray aClientPort = sClientPort.toUtf8(); jack_port_t *pJackPort = jack_port_by_name(pJackClient, aClientPort.constData()); if (pJackPort) { jack_uuid_t port_uuid = ::jack_port_uuid(pJackPort); setPortText(prettyName(port_uuid, sPortName)); return; } } #endif qtractorPortListItem::updatePortName(); } //---------------------------------------------------------------------- // qtractorAudioClientItem -- Jack client list item. // // Constructor. qtractorAudioClientItem::qtractorAudioClientItem ( qtractorAudioClientListView *pClientListView ) : qtractorClientListItem(pClientListView) { if (pClientListView->isReadable()) { QTreeWidgetItem::setIcon(0, qtractorAudioConnect::icon(qtractorAudioConnect::ClientOut)); } else { QTreeWidgetItem::setIcon(0, qtractorAudioConnect::icon(qtractorAudioConnect::ClientIn)); } } // Default destructor. qtractorAudioClientItem::~qtractorAudioClientItem (void) { } // Proto-pretty/alias display name method (virtual override). void qtractorAudioClientItem::updateClientName (void) { #ifdef CONFIG_JACK_METADATA jack_client_t *pJackClient = nullptr; qtractorAudioClientListView *pAudioClientListView = static_cast ( QTreeWidgetItem::treeWidget()); if (pAudioClientListView) pJackClient = pAudioClientListView->jackClient(); if (pJackClient) { const QString& sClientName = clientName(); const QByteArray aClientName = sClientName.toUtf8(); const char *pszClientUuid = ::jack_get_uuid_for_client_name(pJackClient, aClientName.constData()); if (pszClientUuid) { jack_uuid_t client_uuid = 0; ::jack_uuid_parse(pszClientUuid, &client_uuid); setClientText(prettyName(client_uuid, sClientName)); ::jack_free((void *) pszClientUuid); return; } } #endif qtractorClientListItem::updateClientName(); } //---------------------------------------------------------------------- // qtractorAudioClientListView -- Jack client list view. // // Constructor. qtractorAudioClientListView::qtractorAudioClientListView ( QWidget *pParent ) : qtractorClientListView(pParent) { } // Default destructor. qtractorAudioClientListView::~qtractorAudioClientListView (void) { } // Jack client accessor. jack_client_t *qtractorAudioClientListView::jackClient (void) const { qtractorAudioConnect *pAudioConnect = static_cast (binding()); return (pAudioConnect ? pAudioConnect->jackClient() : nullptr); } // Client:port refreshner. int qtractorAudioClientListView::updateClientPorts (void) { jack_client_t *pJackClient = jackClient(); if (pJackClient == nullptr) return 0; int iDirtyCount = 0; markClientPorts(0); const char **ppszClientPorts = jack_get_ports(pJackClient, 0, JACK_DEFAULT_AUDIO_TYPE, isReadable() ? JackPortIsOutput : JackPortIsInput); if (ppszClientPorts) { int iClientPort = 0; while (ppszClientPorts[iClientPort]) { const QString sClientPort = QString::fromUtf8(ppszClientPorts[iClientPort]); qtractorAudioClientItem *pClientItem = nullptr; qtractorAudioPortItem *pPortItem = nullptr; const int iColon = sClientPort.indexOf(':'); if (iColon >= 0) { const QString sClientName = sClientPort.left(iColon); if (isClientName(sClientName)) { const QString sPortName = sClientPort.right(sClientPort.length() - iColon - 1); if (isPortName(sPortName)) { pClientItem = static_cast ( findClientItem(sClientName)); if (pClientItem) { pPortItem = static_cast ( pClientItem->findPortItem(sPortName)); } if (pClientItem == nullptr) { pClientItem = new qtractorAudioClientItem(this); pClientItem->setClientName(sClientName); ++iDirtyCount; } if (pClientItem && pPortItem == nullptr) { jack_port_t *pJackPort = jack_port_by_name( pJackClient, ppszClientPorts[iClientPort]); if (pJackPort) { pPortItem = new qtractorAudioPortItem( pClientItem, jack_port_flags(pJackPort)); pPortItem->setPortName(sPortName); ++iDirtyCount; } } if (pPortItem) pPortItem->markClientPort(1); } } } ++iClientPort; } ::free(ppszClientPorts); } cleanClientPorts(0); return iDirtyCount; } //---------------------------------------------------------------------------- // qtractorAudioConnect -- Jack connections model integrated object. // // Constructor. qtractorAudioConnect::qtractorAudioConnect ( qtractorAudioClientListView *pOListView, qtractorAudioClientListView *pIListView, qtractorConnectorView *pConnectorView ) : qtractorConnect(pOListView, pIListView, pConnectorView) { createIcons(); } // Default destructor. qtractorAudioConnect::~qtractorAudioConnect (void) { deleteIcons(); } // Local icon-set janitor methods. QIcon *qtractorAudioConnect::g_apIcons[qtractorAudioConnect::IconCount]; int qtractorAudioConnect::g_iIconsRefCount = 0; void qtractorAudioConnect::createIcons (void) { if (++g_iIconsRefCount == 1) { g_apIcons[ClientIn] = new QIcon(QIcon::fromTheme("itemAudioClientIn")); g_apIcons[ClientOut] = new QIcon(QIcon::fromTheme("itemAudioClientOut")); g_apIcons[PortIn] = new QIcon(QIcon::fromTheme("itemAudioPortIn")); g_apIcons[PortOut] = new QIcon(QIcon::fromTheme("itemAudioPortOut")); g_apIcons[PortPhysIn] = new QIcon(QIcon::fromTheme("itemAudioPortPhysIn")); g_apIcons[PortPhysOut] = new QIcon(QIcon::fromTheme("itemAudioPortPhysOut")); } } void qtractorAudioConnect::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& qtractorAudioConnect::icon ( int iIcon ) { return *g_apIcons[iIcon]; } // JACK client accessor. jack_client_t *qtractorAudioConnect::jackClient (void) const { jack_client_t *pJackClient = nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession && pSession->audioEngine()) pJackClient = (pSession->audioEngine())->jackClient(); return pJackClient; } // Connection primitive. bool qtractorAudioConnect::connectPorts ( qtractorPortListItem *pOPort, qtractorPortListItem *pIPort ) { qtractorAudioPortItem *pOAudioPort = static_cast (pOPort); qtractorAudioPortItem *pIAudioPort = static_cast (pIPort); if (pOAudioPort == nullptr || pIAudioPort == nullptr) return false; jack_client_t *pJackClient = jackClient(); if (pJackClient == nullptr) return false; return (jack_connect(pJackClient, pOAudioPort->clientPortName().toUtf8().constData(), pIAudioPort->clientPortName().toUtf8().constData()) == 0); } // Disconnection primitive. bool qtractorAudioConnect::disconnectPorts ( qtractorPortListItem *pOPort, qtractorPortListItem *pIPort ) { qtractorAudioPortItem *pOAudioPort = static_cast (pOPort); qtractorAudioPortItem *pIAudioPort = static_cast (pIPort); if (pOAudioPort == nullptr || pIAudioPort == nullptr) return false; jack_client_t *pJackClient = jackClient(); if (pJackClient == nullptr) return false; disconnectPortsUpdate(pOPort, pIPort); return (jack_disconnect(pJackClient, pOAudioPort->clientPortName().toUtf8().constData(), pIAudioPort->clientPortName().toUtf8().constData()) == 0); } // Update (clear) audio-buses connect lists (non-virtual). void qtractorAudioConnect::disconnectPortsUpdate ( qtractorPortListItem *pOPort, qtractorPortListItem *pIPort ) { qtractorAudioEngine *pAudioEngine = nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pAudioEngine = pSession->audioEngine(); if (pAudioEngine == nullptr) return; QString sPortName; qtractorBus::BusMode busMode = qtractorBus::None; if (pOPort->clientName() == pAudioEngine->clientName()) { busMode = qtractorBus::Output; sPortName = pOPort->portName().section('/', 0, 0); } else if (pIPort->clientName() == pAudioEngine->clientName()) { busMode = qtractorBus::Input; sPortName = pIPort->portName().section('/', 0, 0); } if (busMode == qtractorBus::None) return; for (qtractorBus *pBus = pAudioEngine->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(); } break; } } } // Update port connection references. void qtractorAudioConnect::updateConnections (void) { jack_client_t *pJackClient = jackClient(); if (pJackClient == nullptr) return; // For each client item... const int iItemCount = OListView()->topLevelItemCount(); for (int iItem = 0; iItem < iItemCount; ++iItem) { QTreeWidgetItem *pItem = OListView()->topLevelItem(iItem); if (pItem->type() != qtractorConnect::ClientItem) continue; qtractorAudioClientItem *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; qtractorAudioPortItem *pOPort = static_cast (pChild); if (pOPort == nullptr) continue; // Are there already any connections? if (pOPort->connects().count() > 0) continue; // Get port connections... const QString& sClientPort = pOPort->clientName() + ':' + pOPort->portName(); const QByteArray aClientPort = sClientPort.toUtf8(); const jack_port_t *pJackPort = jack_port_by_name(pJackClient, aClientPort.constData()); if (pJackPort) { const char **ppszClientPorts = jack_port_get_all_connections(pJackClient, pJackPort); if (ppszClientPorts) { // Now, for each input client port... int iClientPort = 0; while (ppszClientPorts[iClientPort]) { qtractorPortListItem *pIPort = IListView()->findClientPortItem( ppszClientPorts[iClientPort]); if (pIPort) { pOPort->addConnect(pIPort); pIPort->addConnect(pOPort); } ++iClientPort; } ::free(ppszClientPorts); } } } } } // end of qtractorAudioConnect.cpp qtractor-1.5.9/src/PaxHeaders/qtractorSessionCommand.cpp0000644000000000000000000000013215101070305020430 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.088267658 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorSessionCommand.cpp0000644000175000001440000001537115101070305020427 0ustar00rncbcusers// qtractorSessionCommand.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 "qtractorSessionCommand.h" #include "qtractorTimeScaleCommand.h" //---------------------------------------------------------------------- // class qtractorSessionCommand - implementation // // Constructor. qtractorSessionCommand::qtractorSessionCommand ( const QString& sName, qtractorSession *pSession ) : qtractorCommand(sName), m_pSession(pSession) { } // Destructor. qtractorSessionCommand::~qtractorSessionCommand (void) { } //---------------------------------------------------------------------- // class qtractorSessionLoopCommand - implementation // // Constructor. qtractorSessionLoopCommand::qtractorSessionLoopCommand ( qtractorSession *pSession, unsigned long iLoopStart, unsigned long iLoopEnd ) : qtractorSessionCommand(QObject::tr("session loop"), pSession), m_iLoopStart(iLoopStart), m_iLoopEnd(iLoopEnd) { } // Session-loop command methods. bool qtractorSessionLoopCommand::redo (void) { qtractorSession *pSession = session(); if (pSession == nullptr) return false; // Save the previous session loop state alright... const unsigned long iLoopStart = pSession->loopStart(); const unsigned long iLoopEnd = pSession->loopEnd(); // Just set new bounds... pSession->setLoop(m_iLoopStart, m_iLoopEnd); #if 0 // Restore edit cursors too... if (m_iLoopStart < m_iLoopEnd) { pSession->setEditHead(m_iLoopStart); pSession->setEditTail(m_iLoopEnd); } #endif // Swap it nice, finally. m_iLoopStart = iLoopStart; m_iLoopEnd = iLoopEnd; return true; } bool qtractorSessionLoopCommand::undo (void) { // As we swap the prev/loop this is non-idempotent. return redo(); } //---------------------------------------------------------------------- // class qtractorSessionPunchCommand - implementation // // Constructor. qtractorSessionPunchCommand::qtractorSessionPunchCommand ( qtractorSession *pSession, unsigned long iPunchIn, unsigned long iPunchOut ) : qtractorSessionCommand(QObject::tr("session punch"), pSession), m_iPunchIn(iPunchIn), m_iPunchOut(iPunchOut) { } // Session-punch command methods. bool qtractorSessionPunchCommand::redo (void) { qtractorSession *pSession = session(); if (pSession == nullptr) return false; // Save the previous session punch state alright... const unsigned long iPunchIn = pSession->punchIn(); const unsigned long iPunchOut = pSession->punchOut(); // Just set new bounds... pSession->setPunch(m_iPunchIn, m_iPunchOut); #if 0 // Restore edit cursors too... if (m_iPunchIn < m_iPunchOut) { pSession->setEditHead(m_iPunchIn); pSession->setEditTail(m_iPunchOut); } #endif // Swap it nice, finally. m_iPunchIn = iPunchIn; m_iPunchOut = iPunchOut; return true; } bool qtractorSessionPunchCommand::undo (void) { // As we swap the prev/tempo this is non-idempotent. return redo(); } //---------------------------------------------------------------------- // class qtractorSessionEditCommand - implementation // // Constructor. qtractorSessionEditCommand::qtractorSessionEditCommand ( qtractorSession *pSession, const qtractorSession::Properties& properties ) : qtractorSessionCommand(QObject::tr("session properties"), pSession), m_pPropertiesCommand(nullptr), m_pTempoCommand(nullptr), m_iTicksPerBeat(0) { // Actual properties command... m_pPropertiesCommand = new qtractorPropertyCommand ( name(), pSession->properties(), properties); // Append tempo/time-siganture changes... const float fTempo = properties.timeScale.tempo(); const unsigned short iBeatsPerBar = properties.timeScale.beatsPerBar(); const unsigned short iBeatDivisor = properties.timeScale.beatDivisor(); if (pSession->tempo() != fTempo || pSession->beatsPerBar() != iBeatsPerBar || pSession->beatDivisor() != iBeatDivisor) { qtractorTimeScale *pTimeScale = pSession->timeScale(); qtractorTimeScale::Cursor& cursor = pTimeScale->cursor(); qtractorTimeScale::Node *pNode = cursor.seekFrame(0); m_pTempoCommand = new qtractorTimeScaleUpdateNodeCommand( pTimeScale, pNode->frame, fTempo, 2, iBeatsPerBar, iBeatDivisor); } // Append time resolution changes too... const unsigned short iTicksPerBeat = properties.timeScale.ticksPerBeat(); if (pSession->ticksPerBeat() != iTicksPerBeat) m_iTicksPerBeat = iTicksPerBeat; // To track session-name changes... m_sSessionName = pSession->sessionName(); } // Destructor. qtractorSessionEditCommand::~qtractorSessionEditCommand (void) { if (m_pTempoCommand) delete m_pTempoCommand; if (m_pPropertiesCommand) delete m_pPropertiesCommand; } // Session-edit command methods. bool qtractorSessionEditCommand::redo (void) { bool bResult = false; qtractorSession *pSession = session(); if (pSession) bResult = true; if (bResult && m_pPropertiesCommand) bResult = m_pPropertiesCommand->redo(); if (bResult && m_pTempoCommand) bResult = m_pTempoCommand->redo(); if (bResult && m_iTicksPerBeat > 0) pSession->updateTimeResolution(); if (bResult && !m_sSessionName.isEmpty()) { const QString& sSessionName = pSession->sessionName(); if (sSessionName != m_sSessionName) { pSession->renameSession(m_sSessionName, sSessionName); m_sSessionName = sSessionName; } } return bResult; } bool qtractorSessionEditCommand::undo (void) { bool bResult = false; qtractorSession *pSession = session(); if (pSession) bResult = true; if (bResult && m_pPropertiesCommand) bResult = m_pPropertiesCommand->undo(); if (bResult && m_pTempoCommand) bResult = m_pTempoCommand->undo(); if (bResult && m_iTicksPerBeat > 0) pSession->updateTimeResolution(); if (bResult && !m_sSessionName.isEmpty()) { const QString& sSessionName = pSession->sessionName(); if (sSessionName != m_sSessionName) { pSession->renameSession(m_sSessionName, sSessionName); m_sSessionName = sSessionName; } } return bResult; } // end of qtractorSessionCommand.cpp qtractor-1.5.9/src/PaxHeaders/qtractorInstrumentMenu.h0000644000000000000000000000013215101070305020150 xustar0030 mtime=1761898693.072267607 30 atime=1761898693.072267607 30 ctime=1761898693.072267607 qtractor-1.5.9/src/qtractorInstrumentMenu.h0000644000175000001440000000474515101070305020152 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.9/src/PaxHeaders/qtractorWsolaTimeStretcher.cpp0000644000000000000000000000013215101070305021276 xustar0030 mtime=1761898693.093267673 30 atime=1761898693.093267673 30 ctime=1761898693.093267673 qtractor-1.5.9/src/qtractorWsolaTimeStretcher.cpp0000644000175000001440000005675215101070305021305 0ustar00rncbcusers// qtractorWsolaTimeStretcher.cpp // /**************************************************************************** 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. *****************************************************************************/ #include "qtractorWsolaTimeStretcher.h" #include // Cross-correlation value calculation over the overlap period. // #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 version. static inline float sse_cross_corr ( const float *pV1, const float *pV2, unsigned int iOverlapLength ) { __m128 vCorr, vNorm, vTemp, *pVec2; // Note. It means a major slow-down if the routine needs to tolerate // unaligned __m128 memory accesses. It's way faster if we can skip // unaligned slots and use _mm_load_ps instruction instead of _mm_loadu_ps. // This can mean up to ~ 10-fold difference (incl. part of which is // due to skipping every second round for stereo sound though). // // Little cheating allowed, return valid correlation only for // aligned locations, meaning every second round for stereo sound. //if (((unsigned long) pV1) & 15) return -1e38f; // Skip unaligned locations. // No cheating allowed, use unaligned load & take the resulting // performance hit. -- use _mm_loadu_ps() instead of _mm_load_ps(); // Ensure overlapLength is divisible by 8 // assert((m_iOverlapLength % 8) == 0); iOverlapLength >>= 4; // Calculates the cross-correlation value between 'pV1' and 'pV2' vectors // Note: pV2 _must_ be aligned to 16-bit boundary, pV1 need not. pVec2 = (__m128 *) pV2; vCorr = _mm_setzero_ps(); vNorm = _mm_setzero_ps(); // Unroll the loop by factor of 4 * 4 operations for (unsigned int i = 0; i < iOverlapLength; ++i) { // vCorr += pV1[0..3] * pV2[0..3] vTemp = _mm_loadu_ps(pV1); vCorr = _mm_add_ps(vCorr, _mm_mul_ps(vTemp, pVec2[0])); vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp, vTemp)); // vCorr += pV1[4..7] * pV2[4..7] vTemp = _mm_loadu_ps(pV1 + 4); vCorr = _mm_add_ps(vCorr, _mm_mul_ps(vTemp, pVec2[1])); vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp, vTemp)); // vCorr += pV1[8..11] * pV2[8..11] vTemp = _mm_loadu_ps(pV1 + 8); vCorr = _mm_add_ps(vCorr, _mm_mul_ps(vTemp, pVec2[2])); vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp, vTemp)); // vCorr += pV1[12..15] * pV2[12..15] vTemp = _mm_loadu_ps(pV1 + 12); vCorr = _mm_add_ps(vCorr, _mm_mul_ps(vTemp, pVec2[3])); vNorm = _mm_add_ps(vNorm, _mm_mul_ps(vTemp, vTemp)); pV1 += 16; pVec2 += 4; } float *pvNorm = (float *) &vNorm; float fNorm = (pvNorm[0] + pvNorm[1] + pvNorm[2] + pvNorm[3]); if (fNorm < 1e-9f) fNorm = 1.0f; // avoid div by zero float *pvCorr = (float *) &vCorr; return (pvCorr[0] + pvCorr[1] + pvCorr[2] + pvCorr[3]) / ::sqrtf(fNorm); } #endif // Standard (slow) version. static inline float std_cross_corr ( const float *pV1, const float *pV2, unsigned int iOverlapLength ) { float fCorr = 0.0f; float fNorm = 0.0f; for (unsigned int i = 0; i < iOverlapLength; ++i) { fCorr += pV1[i] * pV2[i]; fNorm += pV1[i] * pV1[i]; } if (fNorm < 1e-9f) fNorm = 1.0f; // avoid div by zero return fCorr / ::sqrtf(fNorm); } //--------------------------------------------------------------------------- // qtractorWsolaTimeStretcher - Time-stretch (tempo change) effect for processed sound. // // Constructor. qtractorWsolaTimeStretcher::qtractorWsolaTimeStretcher ( unsigned short iChannels, unsigned int iSampleRate ) : m_iChannels(0) { setChannels(iChannels); m_fTempo = 1.0f; m_bQuickSeek = false; m_bMidBufferDirty = false; m_ppMidBuffer = nullptr; m_ppRefMidBuffer = nullptr; m_ppRefMidBufferUnaligned = nullptr; m_ppFrames = nullptr; m_iOverlapLength = 0; #if defined(__SSE__) if (sse_enabled()) m_pfnCrossCorr = sse_cross_corr; else #endif m_pfnCrossCorr = std_cross_corr; setParameters(iSampleRate); } // Destructor. qtractorWsolaTimeStretcher::~qtractorWsolaTimeStretcher (void) { if (m_ppFrames) { for (unsigned short i = 0; i < m_iChannels; ++i) { delete [] m_ppMidBuffer[i]; delete [] m_ppRefMidBufferUnaligned[i]; } delete [] m_ppMidBuffer; delete [] m_ppRefMidBufferUnaligned; delete [] m_ppRefMidBuffer; delete [] m_ppFrames; } } // Sets the number of channels, 1=mono, 2=stereo. void qtractorWsolaTimeStretcher::setChannels ( unsigned short iChannels ) { if (m_iChannels == iChannels) return; m_iChannels = iChannels; m_inputBuffer.setChannels(m_iChannels); m_outputBuffer.setChannels(m_iChannels); } // Get the assigne number of channels, 1=mono, 2=stereo. unsigned short qtractorWsolaTimeStretcher::channels (void) const { return m_iChannels; } // Sets new target tempo; less than 1.0 values represent // slower tempo, greater than 1.0 represents faster tempo. void qtractorWsolaTimeStretcher::setTempo ( float fTempo ) { // Set new is tempo scaling. m_fTempo = fTempo; calcSeekWindowLength(); calcOverlapLength(); // Calculate ideal skip length (according to tempo value) m_fNominalSkip = m_fTempo * (m_iSeekWindowLength - m_iOverlapLength); m_fSkipFract = 0; // Calculate how many samples are needed in the input buffer // to process another batch of samples. m_iFramesReq = (unsigned int) (m_fNominalSkip + 0.5f) + m_iOverlapLength; if (m_iFramesReq < m_iSeekWindowLength) m_iFramesReq = m_iSeekWindowLength; m_iFramesReq += m_iSeekLength; clear(); // These will be enough for most purposes, and // should avoid in-the-fly buffer re-allocations... m_inputBuffer.ensureCapacity(m_iFramesReq); m_outputBuffer.ensureCapacity(m_iFramesReq); } // Get assigned target tempo. float qtractorWsolaTimeStretcher::tempo (void) const { return m_fTempo; } // Set quick-seek mode (hierachical search). void qtractorWsolaTimeStretcher::setQuickSeek ( bool bQuickSeek ) { m_bQuickSeek = bQuickSeek; } // Get quick-seek mode. bool qtractorWsolaTimeStretcher::isQuickSeek (void) const { return m_bQuickSeek; } // 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 = seeking window length for scanning the best // overlapping position. // iOverlapMs = overlapping length. void qtractorWsolaTimeStretcher::setParameters ( unsigned int iSampleRate, unsigned int iSequenceMs, unsigned int iSeekWindowMs, unsigned int iOverlapMs ) { m_iSampleRate = iSampleRate; m_iSequenceMs = iSequenceMs; m_iSeekWindowMs = iSeekWindowMs; m_iOverlapMs = iOverlapMs; m_bAutoSequenceMs = (iSequenceMs < 1); m_bAutoSeekWindowMs = (iSeekWindowMs < 1); // Set tempo to recalculate required frames... setTempo(m_fTempo); } // 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 qtractorWsolaTimeStretcher::getParameters ( unsigned int *piSampleRate, unsigned int *piSequenceMs, unsigned int *piSeekWindowMs, unsigned int *piOverlapMs ) { if (piSampleRate) *piSampleRate = m_iSampleRate; if (piSequenceMs) *piSequenceMs = m_iSequenceMs; if (piSeekWindowMs) *piSeekWindowMs = m_iSeekWindowMs; if (piOverlapMs) *piOverlapMs = m_iOverlapMs; } // Clears mid sample frame buffer. void qtractorWsolaTimeStretcher::clearMidBuffer (void) { if (m_bMidBufferDirty) { for (unsigned short i = 0; i < m_iChannels; ++i) ::memset(m_ppMidBuffer[i], 0, 2 * m_iOverlapLength * sizeof(float)); m_bMidBufferDirty = false; } } // Clears input and mid sample frame buffers. void qtractorWsolaTimeStretcher::clearInput (void) { m_inputBuffer.clear(); clearMidBuffer(); } // Clears all sample frame buffers. void qtractorWsolaTimeStretcher::clear (void) { m_outputBuffer.clear(); m_inputBuffer.clear(); clearMidBuffer(); } // Seeks for the optimal overlap-mixing position. // // The best position is determined as the position where // the two overlapped sample sequences are 'most alike', // in terms of the highest cross-correlation value over // the overlapping period. unsigned int qtractorWsolaTimeStretcher::seekBestOverlapPosition (void) { float fBestCorr, fCorr; unsigned int iBestOffs, iPrevBestOffs; unsigned short i, iStep; int iOffs, j, k; // Slopes the amplitude of the 'midBuffer' samples calcCrossCorrReference(); fBestCorr = -1e38f; // A reasonable lower limit. // Scans for the best correlation value by testing each // possible position over the permitted range. if (m_bQuickSeek) { // Hierachical search... iPrevBestOffs = (m_iSeekLength + 1) >> 1; iOffs = iBestOffs = iPrevBestOffs; for (iStep = 64; iStep > 0; iStep >>= 2) { for (k = -1; k <= 1; k += 2) { for (j = 1; j < 4 || iStep == 64; ++j) { iOffs = iPrevBestOffs + k * j * iStep; if (iOffs < 0 || iOffs >= (int) m_iSeekLength) break; for (i = 0; i < m_iChannels; ++i) { // Calculates correlation value for the mixing // position corresponding to iOffs. fCorr = (*m_pfnCrossCorr)( m_inputBuffer.ptrBegin(i) + iOffs, m_ppRefMidBuffer[i], m_iOverlapLength); // Checks for the highest correlation value. if (fCorr > fBestCorr) { fBestCorr = fCorr; iBestOffs = iOffs; } } } } iPrevBestOffs = iBestOffs; } } else { // Linear search... iBestOffs = 0; for (iOffs = 0; iOffs < (int) m_iSeekLength; ++iOffs) { for (i = 0; i < m_iChannels; ++i) { // Calculates correlation value for the mixing // position corresponding to iOffs. fCorr = (*m_pfnCrossCorr)( m_inputBuffer.ptrBegin(i) + iOffs, m_ppRefMidBuffer[i], m_iOverlapLength); // Checks for the highest correlation value. if (fCorr > fBestCorr) { fBestCorr = fCorr; iBestOffs = iOffs; } } } } return iBestOffs; } // Processes as many processing frames of the samples // from input-buffer, store the result into output-buffer. void qtractorWsolaTimeStretcher::processFrames (void) { unsigned short i; unsigned int j, k; float *pInput, *pOutput; unsigned int iSkip, iOffset; int iTemp; // If mid-buffer is empty, move the first // frames of the input stream into it... if (!m_bMidBufferDirty) { // Wait until we've got overlapLength samples if (m_inputBuffer.frames() < m_iOverlapLength) return; m_inputBuffer.receiveFrames(m_ppMidBuffer, m_iOverlapLength); m_bMidBufferDirty = true; } // Process frames as long as there are enough in // input-buffer to form a processing block. while (m_inputBuffer.frames() >= m_iFramesReq) { // If tempo differs from the nominal, // scan for the best overlapping position... iOffset = seekBestOverlapPosition(); // Mix the frames in the input-buffer at position of iOffset // with the samples in mid-buffer using sliding overlapping; // first partially overlap with the end of the previous // sequence (that's in the mid-buffer). m_outputBuffer.ensureCapacity(m_iOverlapLength); // Overlap... for (i = 0; i < m_iChannels; ++i) { pInput = m_inputBuffer.ptrBegin(i); pOutput = m_outputBuffer.ptrEnd(i); for (j = 0; j < m_iOverlapLength ; ++j) { k = m_iOverlapLength - j; pOutput[j] = (pInput[j + iOffset] * j + m_ppMidBuffer[i][j] * k) / m_iOverlapLength; } } // Commit... m_outputBuffer.putFrames(m_iOverlapLength); // Then copy sequence samples from input-buffer to output... iTemp = (m_iSeekWindowLength - 2 * m_iOverlapLength); if (iTemp > 0) { // Temporary mapping... for (i = 0; i < m_iChannels; ++i) { m_ppFrames[i] = m_inputBuffer.ptrBegin(i) + (iOffset + m_iOverlapLength); } m_outputBuffer.putFrames(m_ppFrames, iTemp); } // Copies the end of the current sequence from input-buffer to // mid-buffer for being mixed with the beginning of the next // processing sequence and so on // assert(iOffset + m_iSeekWindowLength <= m_inputBuffer.frames()); m_inputBuffer.readFrames(m_ppMidBuffer, m_iOverlapLength, (iOffset + m_iSeekWindowLength - m_iOverlapLength)); m_bMidBufferDirty = true; // Remove the processed samples from the input-buffer. Update // the difference between integer & nominal skip step to skip-fract // in order to prevent the error from accumulating over time. m_fSkipFract += m_fNominalSkip; // real skip size iSkip = (int) m_fSkipFract; // rounded to integer skip // Maintain the fraction part, i.e. real vs. integer skip m_fSkipFract -= iSkip; m_inputBuffer.receiveFrames(iSkip); } } // Adds frames of samples into the input of the object. void qtractorWsolaTimeStretcher::putFrames ( float **ppFrames, unsigned int iFrames ) { // Add the frames into the input buffer. m_inputBuffer.putFrames(ppFrames, iFrames); // Process the samples in input buffer. processFrames(); } // 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 qtractorWsolaTimeStretcher::receiveFrames ( float **ppFrames, unsigned int iFrames ) { return m_outputBuffer.receiveFrames(ppFrames, iFrames); } // Returns number of frames currently available. unsigned int qtractorWsolaTimeStretcher::frames() const { return m_outputBuffer.frames(); } // Flush any last samples that are hiding in the internal processing pipeline. void qtractorWsolaTimeStretcher::flushInput (void) { if (m_bMidBufferDirty) { // Prepare a dummy empty buffer... unsigned short i; float dummy[256]; ::memset(&dummy[0], 0, sizeof(dummy)); for (i = 0; i < m_iChannels; ++i) m_ppFrames[i] = &dummy[0]; // Push the last active frames out from the pipeline // by feeding blank samples into processing until // new samples appear in the output... const unsigned int iFrames = frames(); for (i = 0; i < 128; ++i) { putFrames(m_ppFrames, 256); // Any new samples appeared in the output? if (frames() > iFrames) break; } } clearInput(); } //--------------------------------------------------------------------------- // Floating point arithmetics specific algorithm implementations. // // Slopes the amplitude of the mid-buffer samples // so that cross correlation is faster to calculate void qtractorWsolaTimeStretcher::calcCrossCorrReference (void) { for (unsigned int j = 0 ; j < m_iOverlapLength ; ++j) { const float fTemp = (float) j * (float) (m_iOverlapLength - j); for (unsigned short i = 0; i < m_iChannels; ++i) m_ppRefMidBuffer[i][j] = (float) (m_ppMidBuffer[i][j] * fTemp); } } // Calculates overlap period length in frames and reallocate ref-mid-buffer. void qtractorWsolaTimeStretcher::calcOverlapLength (void) { // Must be divisible by 8... unsigned int iNewOverlapLength = (m_iSampleRate * m_iOverlapMs) / 1000; if (iNewOverlapLength < 16) iNewOverlapLength = 16; iNewOverlapLength -= (iNewOverlapLength % 8); const unsigned int iOldOverlapLength = m_iOverlapLength; m_iOverlapLength = iNewOverlapLength; if (m_iOverlapLength > iOldOverlapLength) { unsigned short i; if (m_ppFrames) { for (i = 0; i < m_iChannels; ++i) { delete [] m_ppMidBuffer; delete [] m_ppRefMidBufferUnaligned; } delete [] m_ppMidBuffer; delete [] m_ppRefMidBufferUnaligned; delete [] m_ppRefMidBuffer; delete [] m_ppFrames; } m_ppFrames = new float * [m_iChannels]; m_ppMidBuffer = new float * [m_iChannels]; m_ppRefMidBufferUnaligned = new float * [m_iChannels]; m_ppRefMidBuffer = new float * [m_iChannels]; for (i = 0; i < m_iChannels; ++i) { m_ppMidBuffer[i] = new float [2 * m_iOverlapLength]; m_ppRefMidBufferUnaligned[i] = new float[2 * m_iOverlapLength + 16 / sizeof(float)]; // Ensure that ref-mid-buffer is aligned // to 16 byte boundary for efficiency m_ppRefMidBuffer[i] = (float *) ((((unsigned long) m_ppRefMidBufferUnaligned[i]) + 15) & -16); } m_bMidBufferDirty = true; clearMidBuffer(); } } // Calculates processing sequence length according to tempo setting. void qtractorWsolaTimeStretcher::calcSeekWindowLength (void) { // Adjust tempo param according to tempo, // so that variating processing sequence length is used... // at varius tempo settings, between the given low...top limits #define AUTO_TEMPO_MIN 0.5f // auto setting low tempo range (-50%) #define AUTO_TEMPO_MAX 2.0f // auto setting top tempo range (+100%) #define AUTO_TEMPO_DIFF (AUTO_TEMPO_MAX - AUTO_TEMPO_MIN) // iSequenceMs setting values at above low & top tempo. #define AUTO_SEQ_MIN 40.0f #define AUTO_SEQ_MAX 125.0f #define AUTO_SEQ_DIFF (AUTO_SEQ_MAX - AUTO_SEQ_MIN) #define AUTO_SEQ_K (AUTO_SEQ_DIFF / AUTO_TEMPO_DIFF) #define AUTO_SEQ_C (AUTO_SEQ_MIN - (AUTO_SEQ_K * AUTO_TEMPO_MIN)) // iSeekWindowMs setting values at above low & top tempo. #define AUTO_SEEK_MIN 15.0f #define AUTO_SEEK_MAX 25.0f #define AUTO_SEEK_DIFF (AUTO_SEEK_MAX - AUTO_SEEK_MIN) #define AUTO_SEEK_K (AUTO_SEEK_DIFF / AUTO_TEMPO_DIFF) #define AUTO_SEEK_C (AUTO_SEEK_MIN - (AUTO_SEEK_K * AUTO_TEMPO_MIN)) #define AUTO_LIMITS(x, a, b) ((x) < (a) ? (a) : ((x) > (b) ? (b) : (x))) if (m_bAutoSequenceMs) { float fSeq = AUTO_SEQ_C + AUTO_SEQ_K * m_fTempo; fSeq = AUTO_LIMITS(fSeq, AUTO_SEQ_MIN, AUTO_SEQ_MAX); m_iSequenceMs = (unsigned int) (fSeq + 0.5f); } if (m_bAutoSeekWindowMs) { float fSeek = AUTO_SEEK_C + AUTO_SEEK_K * m_fTempo; fSeek = AUTO_LIMITS(fSeek, AUTO_SEEK_MIN, AUTO_SEEK_MAX); m_iSeekWindowMs = (unsigned int) (fSeek + 0.5f); } // Update seek window lengths. m_iSeekLength = (m_iSampleRate * m_iSeekWindowMs) / 1000; m_iSeekWindowLength = (m_iSampleRate * m_iSequenceMs) / 1000; } //---------------------------------------------------------------------- // class qtractorWsolaTimeStretcher::FifoBuffer -- FIFO buffer/cache method implementation. // // Constructor. qtractorWsolaTimeStretcher::FifoBuffer::FifoBuffer ( unsigned short iChannels ) : m_iChannels(0), m_ppBuffer(nullptr), m_ppBufferUnaligned(nullptr), m_iFrameCount(0), m_iFramePos(0) { setChannels(iChannels); } // Destructor. qtractorWsolaTimeStretcher::FifoBuffer::~FifoBuffer (void) { clearFifoBuffer(); } // Sets number of channels, 1=mono, 2=stereo, ... void qtractorWsolaTimeStretcher::FifoBuffer::setChannels ( unsigned short iChannels ) { if (m_iChannels == iChannels) return; clearFifoBuffer(); m_iChannels = iChannels; m_iBufferSize = 0; ensureCapacity(1024); } // Read frames from beginning of the sample buffer. unsigned int qtractorWsolaTimeStretcher::FifoBuffer::readFrames ( float **ppFrames, unsigned int iFrames, unsigned int iOffset ) const { const unsigned int iMaxFrames = (iFrames > m_iFrameCount ? m_iFrameCount : iFrames); for (unsigned short i = 0; i < m_iChannels; ++i) ::memcpy(ppFrames[i], ptrBegin(i) + iOffset, iMaxFrames * sizeof(float)); return iMaxFrames; } // Adjusts book-keeping so that given number of frames are removed // from beginning of the sample buffer without copying them anywhere. // // Used to reduce the number of samples in the buffer when accessing // the sample buffer directly with ptrBegin() function. unsigned int qtractorWsolaTimeStretcher::FifoBuffer::receiveFrames ( float **ppFrames, unsigned int iFrames, unsigned int iOffset ) { return receiveFrames(readFrames(ppFrames, iFrames, iOffset)); } unsigned int qtractorWsolaTimeStretcher::FifoBuffer::receiveFrames ( unsigned int iFrames ) { if (iFrames >= m_iFrameCount) { const unsigned int iFrameCount = m_iFrameCount; m_iFrameCount = 0; return iFrameCount; } m_iFrameCount -= iFrames; m_iFramePos += iFrames; return iFrames; } // Write samples/frames to the end of sample frame buffer. unsigned int qtractorWsolaTimeStretcher::FifoBuffer::writeFrames ( float **ppFrames, unsigned int iFrames, unsigned int iOffset ) { ensureCapacity(iFrames); for (unsigned short i = 0; i < m_iChannels; ++i) ::memcpy(ptrEnd(i), ppFrames[i] + iOffset, iFrames * sizeof(float)); return iFrames; } // Adjusts the book-keeping to increase number of frames // in the buffer without copying any actual frames. // // This function is used to update the number of frames in the // sample buffer when accessing the buffer directly with ptrEnd() // function. Please be careful though. void qtractorWsolaTimeStretcher::FifoBuffer::putFrames ( float **ppFrames, unsigned int iFrames, unsigned int iOffset ) { m_iFrameCount += writeFrames(ppFrames, iFrames, iOffset); } void qtractorWsolaTimeStretcher::FifoBuffer::putFrames ( unsigned int iFrames ) { ensureCapacity(iFrames); m_iFrameCount += iFrames; } // Ensures that the buffer has capacity for at least this many frames. void qtractorWsolaTimeStretcher::FifoBuffer::ensureCapacity ( unsigned int iCapacity ) { const unsigned int iBufferSize = m_iFramePos + m_iFrameCount + (iCapacity << 2); if (iBufferSize > m_iBufferSize) { // Enlarge the buffer in 4KB steps (round up to next 4KB boundary) const unsigned int iSizeInBytes = ((iBufferSize << 1) * sizeof(float) + 4095) & -4096; // assert(iSizeInBytes % 2 == 0); float **ppTemp = new float * [m_iChannels]; float **ppTempUnaligned = new float * [m_iChannels]; m_iBufferSize = (iSizeInBytes / sizeof(float)) + (16 / sizeof(float)); for (unsigned short i = 0; i < m_iChannels; ++i) { ppTempUnaligned[i] = new float [m_iBufferSize]; ppTemp[i] = (float *) (((unsigned long) ppTempUnaligned[i] + 15) & -16); if (m_ppBuffer) { if (m_iFrameCount > 0) { ::memcpy(ppTemp[i], ptrBegin(i), m_iFrameCount * sizeof(float)); } delete [] m_ppBufferUnaligned[i]; } } if (m_ppBuffer) { delete [] m_ppBufferUnaligned; delete [] m_ppBuffer; } m_ppBufferUnaligned = ppTempUnaligned; m_ppBuffer = ppTemp; // Done realloc. m_iFramePos = 0; } else if (m_iFramePos > (m_iBufferSize >> 2)) { // Rewind the buffer by moving data... if (m_iFrameCount > 0) { for (unsigned short i = 0; i < m_iChannels; ++i) { ::memmove(m_ppBuffer[i], ptrBegin(i), m_iFrameCount * sizeof(float)); } } // Done rewind. m_iFramePos = 0; } } // Destroy all current allocated buffers. void qtractorWsolaTimeStretcher::FifoBuffer::clearFifoBuffer (void) { if (m_ppBufferUnaligned) { for (unsigned short i = 0; i < m_iChannels; ++i) delete [] m_ppBufferUnaligned[i]; delete [] m_ppBufferUnaligned; m_ppBufferUnaligned = nullptr; } if (m_ppBuffer) { delete [] m_ppBuffer; m_ppBuffer = nullptr; } } // end of qtractorWsolaTimeStretcher.cpp qtractor-1.5.9/src/PaxHeaders/qtractorScrollView.cpp0000644000000000000000000000013215101070305017577 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.087267654 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorScrollView.cpp0000644000175000001440000001327615101070305017600 0ustar00rncbcusers// qtractorScrollView.cpp // /**************************************************************************** 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. *****************************************************************************/ #include "qtractorScrollView.h" #include #include #include //---------------------------------------------------------------------------- // qtractorScrollView -- abstract scroll view widget. // Constructor. qtractorScrollView::qtractorScrollView ( QWidget *pParent ) : QAbstractScrollArea(pParent) { // Make it non-sense to the viewport background attribute... QWidget *pViewport = QAbstractScrollArea::viewport(); pViewport->setAttribute(Qt::WA_StaticContents); pViewport->setAttribute(Qt::WA_NoSystemBackground); pViewport->setBackgroundRole(QPalette::NoRole); pViewport->setAutoFillBackground(false); } // Destructor. qtractorScrollView::~qtractorScrollView (void) { } // Virtual contents methods. void qtractorScrollView::setContentsPos ( int cx, int cy ) { if (cx < 0) cx = 0; if (cx != m_rectContents.x()) QAbstractScrollArea::horizontalScrollBar()->setSliderPosition(cx); if (cy < 0) cy = 0; if (cy != m_rectContents.y()) QAbstractScrollArea::verticalScrollBar()->setSliderPosition(cy); } void qtractorScrollView::resizeContents ( int cw, int ch ) { if (cw >= 0 && m_rectContents.width() != cw) m_rectContents.setWidth(cw); if (ch >= 0 && m_rectContents.height() != ch) m_rectContents.setHeight(ch); updateScrollBars(); } // Scrolls contents so that given point is visible. void qtractorScrollView::ensureVisible ( int cx, int cy, int mx, int my ) { QWidget *pViewport = QAbstractScrollArea::viewport(); const int w = pViewport->width(); const int h = pViewport->height(); int dx = m_rectContents.x(); int dy = m_rectContents.y(); int cw = m_rectContents.width(); int ch = m_rectContents.height(); if (w < (mx << 1)) mx = (w >> 1); if (h < (my << 1)) my = (h >> 1); if (cw <= w) { mx = 0; dx = 0; } if (ch <= h) { my = 0; dy = 0; } if (cx < mx + dx) dx = cx - mx; else if (cx >= w - mx + dx) dx = cx + mx - w; if (cy < my + dy) dy = cy - my; else if (cy >= h - my + dy) dy = cy + my - h; if (dx < 0) dx = 0; else if (dx >= cw - w && w >= cw) dx = cw - w; if (dy < 0) dy = 0; else if (dy >= ch - h && h >= ch) dy = ch - h; setContentsPos(dx, dy); } // Scrollbar stabilization. void qtractorScrollView::updateScrollBars (void) { QWidget *pViewport = QAbstractScrollArea::viewport(); const int w = pViewport->width(); const int h = pViewport->height(); const int cw = (m_rectContents.width() > w ? m_rectContents.width() - w : 0); QScrollBar *pHScrollBar = QAbstractScrollArea::horizontalScrollBar(); if (pHScrollBar->sliderPosition() > cw) pHScrollBar->setSliderPosition(cw); pHScrollBar->setRange(0, cw); pHScrollBar->setSingleStep((w >> 4) + 1); pHScrollBar->setPageStep(w); const int ch = (m_rectContents.height() > h ? m_rectContents.height() - h : 0); QScrollBar *pVScrollBar = QAbstractScrollArea::verticalScrollBar(); if (pVScrollBar->sliderPosition() > ch) pVScrollBar->setSliderPosition(ch); pVScrollBar->setRange(0, ch); pVScrollBar->setSingleStep((h >> 4) + 1); pVScrollBar->setPageStep(h); } // Specialized event handlers. void qtractorScrollView::resizeEvent ( QResizeEvent *pResizeEvent ) { QAbstractScrollArea::resizeEvent(pResizeEvent); updateScrollBars(); } void qtractorScrollView::paintEvent ( QPaintEvent *pPaintEvent ) { QPainter painter(QAbstractScrollArea::viewport()); drawContents(&painter, pPaintEvent->rect().adjusted(0, 0, 1, 1)); } void qtractorScrollView::wheelEvent ( QWheelEvent *pWheelEvent ) { if (pWheelEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) { setContentsPos( m_rectContents.x() - pWheelEvent->angleDelta().y(), m_rectContents.y()); } else QAbstractScrollArea::wheelEvent(pWheelEvent); } // Scroll area updater. void qtractorScrollView::scrollContentsBy ( int dx, int dy ) { int iUpdate = 0; if (dx) { m_rectContents.moveLeft(m_rectContents.x() - dx); ++iUpdate; } if (dy) { m_rectContents.moveTop(m_rectContents.y() - dy); ++iUpdate; } if (iUpdate > 0) { updateContents(); emit contentsMoving(m_rectContents.x(), m_rectContents.y()); } } // Rectangular contents update. void qtractorScrollView::updateContents ( const QRect& rect ) { QAbstractScrollArea::viewport()->update( QRect(contentsToViewport(rect.topLeft()), rect.size())); } // Overall contents update. void qtractorScrollView::updateContents (void) { QAbstractScrollArea::viewport()->update(); } // Viewport/contents position converters. QPoint qtractorScrollView::viewportToContents ( const QPoint& pos ) const { return QPoint(pos.x() + m_rectContents.x(), pos.y() + m_rectContents.y()); } QPoint qtractorScrollView::contentsToViewport ( const QPoint& pos ) const { return QPoint(pos.x() - m_rectContents.x(), pos.y() - m_rectContents.y()); } // end of qtractorScrollView.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMainForm.cpp0000644000000000000000000000013215101070305017216 xustar0030 mtime=1761898693.075267616 30 atime=1761898693.074267613 30 ctime=1761898693.075267616 qtractor-1.5.9/src/qtractorMainForm.cpp0000644000175000001440000102277415101070305017223 0ustar00rncbcusers// qtractorMainForm.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 "qtractorMainForm.h" #include "qtractorAbout.h" #include "qtractorOptions.h" #include "qtractorInstrument.h" #include "qtractorInstrumentMenu.h" #include "qtractorMessages.h" #include "qtractorFileSystem.h" #include "qtractorFiles.h" #include "qtractorConnections.h" #include "qtractorMixer.h" #include "qtractorTracks.h" #include "qtractorTrackList.h" #include "qtractorTrackView.h" #include "qtractorThumbView.h" #include "qtractorSpinBox.h" #include "qtractorMonitor.h" #include "qtractorAudioPeak.h" #include "qtractorAudioBuffer.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include "qtractorTimeStretcher.h" #include "qtractorSessionCommand.h" #include "qtractorTimeScaleCommand.h" #include "qtractorClipCommand.h" #include "qtractorAudioMeter.h" #include "qtractorMidiMeter.h" #include "qtractorMidiManager.h" #include "qtractorActionControl.h" #include "qtractorExportForm.h" #include "qtractorSessionForm.h" #include "qtractorOptionsForm.h" #include "qtractorPaletteForm.h" #include "qtractorConnectForm.h" #include "qtractorShortcutForm.h" #include "qtractorMidiControlForm.h" #include "qtractorInstrumentForm.h" #include "qtractorTimeScaleForm.h" #include "qtractorBusForm.h" #include "qtractorTakeRangeForm.h" #include "qtractorMidiClip.h" #include "qtractorMidiEditor.h" #include "qtractorMidiEditorForm.h" #include "qtractorTrackCommand.h" #include "qtractorCurveCommand.h" #include "qtractorMessageList.h" #include "qtractorPluginFactory.h" #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 #ifdef CONFIG_JACK_SESSION #include #endif #ifdef CONFIG_NSM #include "qtractorNsmClient.h" #endif #include #include #include #include #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 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #if !defined(QT_NO_STYLE_GTK) #include #endif #endif #include #include // Timer constants (magic) stuff. #define QTRACTOR_TIMER_MSECS 66 #define QTRACTOR_TIMER_DELAY 233 #if QT_VERSION < QT_VERSION_CHECK(4, 5, 0) namespace Qt { const WindowFlags WindowCloseButtonHint = WindowFlags(0x08000000); } #endif #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) #define horizontalAdvance width #endif #if defined(__WIN32__) || defined(_WIN32) || defined(WIN32) #undef HAVE_SIGNAL_H #endif //------------------------------------------------------------------------- // LADISH Level 1 support stuff. #ifdef HAVE_SIGNAL_H #include #include #include #include #include // File descriptor for SIGUSR1 notifier. static int g_fdSigusr1[2] = { -1, -1 }; // Unix SIGUSR1 signal handler. static void qtractor_sigusr1_handler ( int /* signo */ ) { char c = 1; (void) (::write(g_fdSigusr1[0], &c, sizeof(c)) > 0); } // File descriptor for SIGTERM notifier. static int g_fdSigterm[2] = { -1, -1 }; // Unix SIGTERM signal handler. static void qtractor_sigterm_handler ( int /* signo */ ) { char c = 1; (void) (::write(g_fdSigterm[0], &c, sizeof(c)) > 0); } #endif // HAVE_SIGNAL_H //------------------------------------------------------------------------- // qtractorMainForm -- Main window form implementation. // Kind of singleton reference. qtractorMainForm *qtractorMainForm::g_pMainForm = nullptr; // Constructor. qtractorMainForm::qtractorMainForm ( QWidget *pParent, Qt::WindowFlags wflags ) : QMainWindow(pParent, wflags) { // Setup UI struct... m_ui.setupUi(this); #if QT_VERSION < QT_VERSION_CHECK(6, 1, 0) QMainWindow::setWindowIcon(QIcon(":/images/qtractor.png")); #endif // Pseudo-singleton reference setup. g_pMainForm = this; // Initialize some pointer references. m_pOptions = nullptr; // FIXME: This gotta go, somewhere in time... m_pSession = new qtractorSession(); m_pTempoCursor = new qtractorTempoCursor(); m_pMessageList = new qtractorMessageList(); m_pAudioFileFactory = new qtractorAudioFileFactory(); m_pPluginFactory = new qtractorPluginFactory(); // Custom track/instrument proxy menu. m_pInstrumentMenu = new qtractorInstrumentMenu(this); // All child forms are to be created later, not earlier than setup. m_pMessages = nullptr; m_pFileSystem = nullptr; m_pFiles = nullptr; m_pMixer = nullptr; m_pConnections = nullptr; m_pTracks = nullptr; // To remember last time we've shown the playhead. m_iPlayHead = 0; // We'll start clean. m_iUntitled = 0; m_iDirtyCount = 0; m_iBackupCount = 0; m_iTransportUpdate = 0; m_iTransportRolling = 0; m_bTransportPlaying = false; m_fTransportShuttle = 0.0f; m_iTransportStep = 0; m_iXrunCount = 0; m_iXrunSkip = 0; m_iXrunTimer = 0; m_iAudioPeakTimer = 0; m_iAudioRefreshTimer = 0; m_iMidiRefreshTimer = 0; m_iPlayerTimer = 0; m_pNsmClient = nullptr; m_bNsmDirty = false; m_iAudioPropertyChange = 0; m_iAudioSelfConnected = 0; m_iStabilizeTimer = 0; // Configure the audio file peak factory... qtractorAudioPeakFactory *pAudioPeakFactory = m_pSession->audioPeakFactory(); if (pAudioPeakFactory) { QObject::connect(pAudioPeakFactory, SIGNAL(peakEvent()), SLOT(audioPeakNotify())); } // Configure the audio engine event handling... const qtractorAudioEngineProxy *pAudioEngineProxy = nullptr; qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine) pAudioEngineProxy = pAudioEngine->proxy(); if (pAudioEngineProxy) { QObject::connect(pAudioEngineProxy, SIGNAL(shutEvent()), SLOT(audioShutNotify())); QObject::connect(pAudioEngineProxy, SIGNAL(xrunEvent()), SLOT(audioXrunNotify())); QObject::connect(pAudioEngineProxy, SIGNAL(portEvent()), SLOT(audioPortNotify())); QObject::connect(pAudioEngineProxy, SIGNAL(buffEvent(unsigned int)), SLOT(audioBuffNotify(unsigned int))); QObject::connect(pAudioEngineProxy, SIGNAL(sessEvent(void *)), SLOT(audioSessNotify(void *))); QObject::connect(pAudioEngineProxy, SIGNAL(syncEvent(unsigned long, bool)), SLOT(audioSyncNotify(unsigned long, bool))); QObject::connect(pAudioEngineProxy, SIGNAL(propEvent()), SLOT(audioPropNotify())); QObject::connect(pAudioEngineProxy, SIGNAL(selfEvent()), SLOT(audioSelfNotify())); } // Configure the MIDI engine event handling... const qtractorMidiEngineProxy *pMidiEngineProxy = nullptr; qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine) pMidiEngineProxy = pMidiEngine->proxy(); if (pMidiEngineProxy) { qRegisterMetaType ("qtractorMmcEvent"); qRegisterMetaType ("qtractorCtlEvent"); QObject::connect(pMidiEngineProxy, SIGNAL(mmcEvent(const qtractorMmcEvent&)), SLOT(midiMmcNotify(const qtractorMmcEvent&))); QObject::connect(pMidiEngineProxy, SIGNAL(ctlEvent(const qtractorCtlEvent&)), SLOT(midiCtlNotify(const qtractorCtlEvent&))); QObject::connect(pMidiEngineProxy, SIGNAL(sppEvent(int, unsigned short)), SLOT(midiSppNotify(int, unsigned short))); QObject::connect(pMidiEngineProxy, SIGNAL(clkEvent(float)), SLOT(midiClkNotify(float))); QObject::connect(pMidiEngineProxy, SIGNAL(inpEvent(unsigned short)), SLOT(midiInpNotify(unsigned short))); } // Add the midi controller map... m_pMidiControl = new qtractorMidiControl(); #ifdef HAVE_SIGNAL_H // Set to ignore any fatal "Broken pipe" signals. ::signal(SIGPIPE, SIG_IGN); // LADISH Level 1 suport. // Initialize file descriptors for SIGUSR1 socket notifier. ::socketpair(AF_UNIX, SOCK_STREAM, 0, g_fdSigusr1); m_pSigusr1Notifier = new QSocketNotifier(g_fdSigusr1[1], QSocketNotifier::Read, this); QObject::connect(m_pSigusr1Notifier, SIGNAL(activated(int)), SLOT(handle_sigusr1())); // Install SIGUSR1 signal handler. struct sigaction sigusr1; sigusr1.sa_handler = qtractor_sigusr1_handler; sigemptyset(&sigusr1.sa_mask); sigusr1.sa_flags = 0; sigusr1.sa_flags |= SA_RESTART; ::sigaction(SIGUSR1, &sigusr1, nullptr); // LADISH termination suport. // Initialize file descriptors for SIGTERM socket notifier. ::socketpair(AF_UNIX, SOCK_STREAM, 0, g_fdSigterm); m_pSigtermNotifier = new QSocketNotifier(g_fdSigterm[1], QSocketNotifier::Read, this); QObject::connect(m_pSigtermNotifier, SIGNAL(activated(int)), SLOT(handle_sigterm())); // Install SIGTERM/SIGQUIT signal handlers. struct sigaction sigterm; sigterm.sa_handler = qtractor_sigterm_handler; sigemptyset(&sigterm.sa_mask); sigterm.sa_flags = 0; sigterm.sa_flags |= SA_RESTART; ::sigaction(SIGTERM, &sigterm, nullptr); ::sigaction(SIGQUIT, &sigterm, nullptr); // Ignore SIGHUP/SIGINT signals. ::signal(SIGHUP, SIG_IGN); ::signal(SIGINT, SIG_IGN); #else // HAVE_SIGNAL_H m_pSigusr1Notifier = nullptr; m_pSigtermNotifier = nullptr; #endif // !HAVE_SIGNAL_H // Also the (QAction) MIDI observer map... m_pActionControl = new qtractorActionControl(this); // Get edit selection mode action group up... m_pSelectModeActionGroup = new QActionGroup(this); m_pSelectModeActionGroup->setExclusive(true); m_pSelectModeActionGroup->addAction(m_ui.editSelectModeClipAction); m_pSelectModeActionGroup->addAction(m_ui.editSelectModeRangeAction); m_pSelectModeActionGroup->addAction(m_ui.editSelectModeRectAction); m_pSelectModeActionGroup->addAction(m_ui.editSelectModeCurveAction); // And the corresponding tool-button drop-down menu... m_pSelectModeToolButton = new QToolButton(this); m_pSelectModeToolButton->setPopupMode(QToolButton::InstantPopup); m_pSelectModeToolButton->setMenu(m_ui.editSelectModeMenu); // Add/insert this on its proper place in the edit-toobar... m_ui.editToolbar->insertWidget(m_ui.clipNewAction, m_pSelectModeToolButton); m_ui.editToolbar->insertSeparator(m_ui.clipNewAction); QObject::connect( m_pSelectModeActionGroup, SIGNAL(triggered(QAction*)), m_pSelectModeToolButton, SLOT(setDefaultAction(QAction*))); // Get transport mode action group up... m_pTransportModeActionGroup = new QActionGroup(this); m_pTransportModeActionGroup->setExclusive(true); m_pTransportModeActionGroup->addAction(m_ui.transportModeNoneAction); m_pTransportModeActionGroup->addAction(m_ui.transportModeSlaveAction); m_pTransportModeActionGroup->addAction(m_ui.transportModeMasterAction); m_pTransportModeActionGroup->addAction(m_ui.transportModeFullAction); // And the corresponding tool-button drop-down menu... m_pTransportModeToolButton = new QToolButton(this); m_pTransportModeToolButton->setPopupMode(QToolButton::InstantPopup); m_pTransportModeToolButton->setMenu(m_ui.transportModeMenu); // Add/insert this on its proper place in the options-toobar... m_ui.optionsToolbar->insertWidget(m_ui.transportPanicAction, m_pTransportModeToolButton); m_ui.optionsToolbar->insertSeparator(m_ui.transportPanicAction); QObject::connect( m_pTransportModeActionGroup, SIGNAL(triggered(QAction*)), m_pTransportModeToolButton, SLOT(setDefaultAction(QAction*))); // Additional time-toolbar controls... // m_ui.timeToolbar->addSeparator(); // View/Snap-to-beat actions initialization... int iSnap = 0; const QIcon& snapIcon = QIcon::fromTheme("itemBeat"); const QString sSnapObjectName("viewSnapPerBeat%1"); const QString sSnapStatusTip(tr("Set current snap to %1")); const QStringList& snapItems = qtractorTimeScale::snapItems(); QStringListIterator snapIter(snapItems); while (snapIter.hasNext()) { const QString& sSnapText = snapIter.next(); QAction *pAction = new QAction(sSnapText, this); pAction->setObjectName(sSnapObjectName.arg(iSnap)); pAction->setStatusTip(sSnapStatusTip.arg(sSnapText)); pAction->setCheckable(true); // pAction->setIcon(snapIcon); pAction->setData(iSnap++); QObject::connect(pAction, SIGNAL(triggered(bool)), SLOT(viewSnap())); m_snapPerBeatActions.append(pAction); m_ui.viewSnapMenu->addAction(pAction); } // m_ui.viewSnapMenu->addSeparator(); m_ui.viewSnapMenu->addAction(m_ui.viewSnapZebraAction); m_ui.viewSnapMenu->addAction(m_ui.viewSnapGridAction); // Automation/curve mode menu setup... m_ui.trackCurveModeMenu->addAction(m_ui.trackCurveLogarithmicAction); m_ui.trackCurveModeMenu->addAction(m_ui.trackCurveColorAction); QIcon iconProcess; iconProcess.addPixmap( QIcon::fromTheme("trackCurveProcess").pixmap(16, 16), QIcon::Normal, QIcon::On); iconProcess.addPixmap( QIcon::fromTheme("trackCurveEnabled").pixmap(16, 16), QIcon::Normal, QIcon::Off); iconProcess.addPixmap( QIcon::fromTheme("trackCurveNone").pixmap(16, 16), QIcon::Disabled, QIcon::Off); m_ui.trackCurveProcessAction->setIcon(iconProcess); QIcon iconCapture; iconCapture.addPixmap( QIcon::fromTheme("trackCurveCapture").pixmap(16, 16), QIcon::Normal, QIcon::On); iconCapture.addPixmap( QIcon::fromTheme("trackCurveEnabled").pixmap(16, 16), QIcon::Normal, QIcon::Off); iconCapture.addPixmap( QIcon::fromTheme("trackCurveNone").pixmap(16, 16), QIcon::Disabled, QIcon::Off); m_ui.trackCurveCaptureAction->setIcon(iconCapture); // Editable toolbar widgets special palette. QPalette pal; // Outrageous HACK: GTK+ ppl won't see green on black thing... #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #if !defined(QT_NO_STYLE_GTK) if (qobject_cast (style()) == nullptr) { #endif #endif // pal.setColor(QPalette::Window, Qt::black); pal.setColor(QPalette::Base, Qt::black); pal.setColor(QPalette::Text, Qt::green); // pal.setColor(QPalette::Button, Qt::darkGray); // pal.setColor(QPalette::ButtonText, Qt::green); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #if !defined(QT_NO_STYLE_GTK) } #endif #endif const QSize pad(4, 0); const QFont& font0 = qtractorMainForm::font(); const QFont font(font0.family(), font0.pointSize() + 2); const QFontMetrics fm(font); const int d = fm.height() + fm.leading() + 8; // Transport time. const QString sTime("+99:99:99.999"); m_pTimeSpinBox = new qtractorTimeSpinBox(m_ui.timeToolbar); m_pTimeSpinBox->setTimeScale(m_pSession->timeScale()); m_pTimeSpinBox->setFont(font); m_pTimeSpinBox->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_pTimeSpinBox->setMinimumSize(QSize(fm.horizontalAdvance(sTime) + d, d) + pad); m_pTimeSpinBox->setPalette(pal); // m_pTimeSpinBox->setAutoFillBackground(true); m_pTimeSpinBox->setToolTip(tr("Current time (play-head)")); // m_pTimeSpinBox->setContextMenuPolicy(Qt::CustomContextMenu); m_ui.timeToolbar->addWidget(m_pTimeSpinBox); // m_ui.timeToolbar->addSeparator(); // Tempo spin-box. const QString sTempo("+999 9/9"); m_pTempoSpinBox = new qtractorTempoSpinBox(m_ui.timeToolbar); // m_pTempoSpinBox->setFont(font); m_pTempoSpinBox->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_pTempoSpinBox->setMinimumSize(QSize(fm.horizontalAdvance(sTempo) + d, d) + pad); m_pTempoSpinBox->setPalette(pal); // m_pTempoSpinBox->setAutoFillBackground(true); m_pTempoSpinBox->setToolTip(tr("Current tempo (BPM)")); m_pTempoSpinBox->setContextMenuPolicy(Qt::CustomContextMenu); m_ui.timeToolbar->addWidget(m_pTempoSpinBox); m_ui.timeToolbar->addSeparator(); // Snap-per-beat combo-box. m_pSnapPerBeatComboBox = new QComboBox(m_ui.timeToolbar); m_pSnapPerBeatComboBox->setEditable(false); // m_pSnapPerBeatComboBox->insertItems(0, snapItems); m_pSnapPerBeatComboBox->setIconSize(QSize(8, 16)); snapIter.toFront(); if (snapIter.hasNext()) m_pSnapPerBeatComboBox->addItem( QIcon::fromTheme("itemNone"), snapIter.next()); while (snapIter.hasNext()) m_pSnapPerBeatComboBox->addItem(snapIcon, snapIter.next()); m_pSnapPerBeatComboBox->setToolTip(tr("Snap/beat")); m_ui.timeToolbar->addWidget(m_pSnapPerBeatComboBox); // Track-line thumbnail view... m_pThumbView = new qtractorThumbView(); m_ui.thumbViewToolbar->addWidget(m_pThumbView); m_ui.thumbViewToolbar->setAllowedAreas( Qt::TopToolBarArea | Qt::BottomToolBarArea); QObject::connect(m_pTimeSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(transportTimeFormatChanged(int))); QObject::connect(m_pTimeSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(transportTimeChanged(unsigned long))); QObject::connect(m_pTimeSpinBox, SIGNAL(editingFinished()), SLOT(transportTimeFinished())); QObject::connect(m_pTempoSpinBox, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(transportTempoContextMenu(const QPoint&))); QObject::connect(m_pTempoSpinBox, SIGNAL(valueChanged(float, unsigned short, unsigned short)), SLOT(transportTempoChanged(float, unsigned short, unsigned short))); QObject::connect(m_pTempoSpinBox, SIGNAL(editingFinished()), SLOT(transportTempoFinished())); QObject::connect(m_pSnapPerBeatComboBox, SIGNAL(activated(int)), SLOT(snapPerBeatChanged(int))); // Create some statusbar labels... QStatusBar *pStatusBar = statusBar(); const QPalette& spal = pStatusBar->palette(); QLabel *pLabel; QPalette *pPalette = new QPalette(spal); m_paletteItems[PaletteNone] = pPalette; pPalette = new QPalette(spal); pPalette->setColor(QPalette::WindowText, Qt::darkRed); pPalette->setColor(QPalette::Window, Qt::red); m_paletteItems[PaletteRed] = pPalette; pPalette = new QPalette(spal); pPalette->setColor(QPalette::WindowText, Qt::darkYellow); pPalette->setColor(QPalette::Window, Qt::yellow); m_paletteItems[PaletteYellow] = pPalette; pPalette = new QPalette(spal); pPalette->setColor(QPalette::WindowText, Qt::darkCyan); pPalette->setColor(QPalette::Window, Qt::cyan); m_paletteItems[PaletteCyan] = pPalette; pPalette = new QPalette(spal); pPalette->setColor(QPalette::WindowText, Qt::darkGreen); pPalette->setColor(QPalette::Window, Qt::green); m_paletteItems[PaletteGreen] = pPalette; // Track status. pLabel = new QLabel(tr("Track")); pLabel->setAlignment(Qt::AlignLeft); pLabel->setMinimumWidth(120); pLabel->setToolTip(tr("Current track name")); pLabel->setAutoFillBackground(true); m_statusItems[StatusName] = pLabel; pStatusBar->addWidget(pLabel, 2); // Hideous progress bar... m_pProgressBar = new QProgressBar(); m_pProgressBar->setFixedHeight(pLabel->sizeHint().height()); m_pProgressBar->setMinimumWidth(120); pStatusBar->addPermanentWidget(m_pProgressBar); m_pProgressBar->hide(); // Session modification status. pLabel = new QLabel(tr("MOD")); pLabel->setAlignment(Qt::AlignHCenter); pLabel->setMinimumSize(pLabel->sizeHint() + pad); pLabel->setToolTip(tr("Session modification state")); pLabel->setAutoFillBackground(true); m_statusItems[StatusMod] = pLabel; pStatusBar->addPermanentWidget(pLabel); // Session recording status. pLabel = new QLabel(tr("REC")); pLabel->setAlignment(Qt::AlignHCenter); pLabel->setMinimumSize(pLabel->sizeHint() + pad); pLabel->setToolTip(tr("Session record state")); pLabel->setAutoFillBackground(true); m_statusItems[StatusRec] = pLabel; pStatusBar->addPermanentWidget(pLabel); // Session muting status. pLabel = new QLabel(tr("MUTE")); pLabel->setAlignment(Qt::AlignHCenter); pLabel->setMinimumSize(pLabel->sizeHint() + pad); pLabel->setToolTip(tr("Session muting state")); pLabel->setAutoFillBackground(true); m_statusItems[StatusMute] = pLabel; pStatusBar->addPermanentWidget(pLabel); // Session soloing status. pLabel = new QLabel(tr("SOLO")); pLabel->setAlignment(Qt::AlignHCenter); pLabel->setMinimumSize(pLabel->sizeHint() + pad); pLabel->setToolTip(tr("Session soloing state")); pLabel->setAutoFillBackground(true); m_statusItems[StatusSolo] = pLabel; pStatusBar->addPermanentWidget(pLabel); // Session looping status. pLabel = new QLabel(tr("LOOP")); pLabel->setAlignment(Qt::AlignHCenter); pLabel->setMinimumSize(pLabel->sizeHint() + pad); pLabel->setToolTip(tr("Session looping state")); pLabel->setAutoFillBackground(true); m_statusItems[StatusLoop] = pLabel; pStatusBar->addPermanentWidget(pLabel); // XRUN count. pLabel = new QLabel("XRUN"); pLabel->setAlignment(Qt::AlignHCenter); pLabel->setMinimumSize(pLabel->sizeHint() + pad); pLabel->setAutoFillBackground(true); pLabel->setToolTip(tr("Session XRUN state")); m_statusItems[StatusXrun] = pLabel; pStatusBar->addPermanentWidget(pLabel); // Session length time. pLabel = new QLabel(sTime); pLabel->setAlignment(Qt::AlignHCenter); pLabel->setMinimumSize(pLabel->sizeHint() + pad); pLabel->setToolTip(tr("Session total time")); pLabel->setAutoFillBackground(true); m_statusItems[StatusTime] = pLabel; pStatusBar->addPermanentWidget(pLabel); // Session buffer size. pLabel = new QLabel("1999"); pLabel->setAlignment(Qt::AlignHCenter); pLabel->setMinimumSize(pLabel->sizeHint() + pad); pLabel->setAutoFillBackground(true); pLabel->setToolTip(tr("Session buffer size")); m_statusItems[StatusSize] = pLabel; pStatusBar->addPermanentWidget(pLabel); // Session sample rate. pLabel = new QLabel("199999"); pLabel->setAlignment(Qt::AlignHCenter); pLabel->setMinimumSize(pLabel->sizeHint() + pad); pLabel->setAutoFillBackground(true); pLabel->setToolTip(tr("Session sample rate")); m_statusItems[StatusRate] = pLabel; pStatusBar->addPermanentWidget(pLabel); m_ui.transportLoopAction->setAutoRepeat(false); m_ui.transportLoopSetAction->setAutoRepeat(false); m_ui.transportPlayAction->setAutoRepeat(false); m_ui.transportRecordAction->setAutoRepeat(false); m_ui.transportPunchAction->setAutoRepeat(false); m_ui.transportPunchSetAction->setAutoRepeat(false); // Some actions surely need those // shortcuts firmly attached... addAction(m_ui.viewMenubarAction); #if 0 const QList& actions = findChildren (); QListIterator iter(actions); while (iter.hasNext()) iter.next()->setShortcutContext(Qt::ApplicationShortcut); #else m_ui.viewFileSystemAction->setShortcutContext(Qt::ApplicationShortcut); m_ui.viewFilesAction->setShortcutContext(Qt::ApplicationShortcut); m_ui.viewConnectionsAction->setShortcutContext(Qt::ApplicationShortcut); m_ui.viewMixerAction->setShortcutContext(Qt::ApplicationShortcut); m_ui.viewMessagesAction->setShortcutContext(Qt::ApplicationShortcut); #endif // Ah, make it stand right. setFocus(); // UI signal/slot connections... QObject::connect(m_ui.fileNewAction, SIGNAL(triggered(bool)), SLOT(fileNew())); QObject::connect(m_ui.fileOpenAction, SIGNAL(triggered(bool)), SLOT(fileOpen())); QObject::connect(m_ui.fileSaveAction, SIGNAL(triggered(bool)), SLOT(fileSave())); QObject::connect(m_ui.fileSaveAsAction, SIGNAL(triggered(bool)), SLOT(fileSaveAs())); QObject::connect(m_ui.filePropertiesAction, SIGNAL(triggered(bool)), SLOT(fileProperties())); QObject::connect(m_ui.fileExitAction, SIGNAL(triggered(bool)), SLOT(fileExit())); QObject::connect(m_ui.editUndoAction, SIGNAL(triggered(bool)), SLOT(editUndo())); QObject::connect(m_ui.editRedoAction, SIGNAL(triggered(bool)), SLOT(editRedo())); QObject::connect(m_ui.editCutAction, SIGNAL(triggered(bool)), SLOT(editCut())); QObject::connect(m_ui.editCopyAction, SIGNAL(triggered(bool)), SLOT(editCopy())); QObject::connect(m_ui.editPasteAction, SIGNAL(triggered(bool)), SLOT(editPaste())); QObject::connect(m_ui.editPasteRepeatAction, SIGNAL(triggered(bool)), SLOT(editPasteRepeat())); QObject::connect(m_ui.editDeleteAction, SIGNAL(triggered(bool)), SLOT(editDelete())); QObject::connect(m_ui.editSelectModeClipAction, SIGNAL(triggered(bool)), SLOT(editSelectModeClip())); QObject::connect(m_ui.editSelectModeRangeAction, SIGNAL(triggered(bool)), SLOT(editSelectModeRange())); QObject::connect(m_ui.editSelectModeRectAction, SIGNAL(triggered(bool)), SLOT(editSelectModeRect())); QObject::connect(m_ui.editSelectModeCurveAction, SIGNAL(triggered(bool)), SLOT(editSelectModeCurve())); QObject::connect(m_ui.editSelectAllAction, SIGNAL(triggered(bool)), SLOT(editSelectAll())); QObject::connect(m_ui.editSelectNoneAction, SIGNAL(triggered(bool)), SLOT(editSelectNone())); QObject::connect(m_ui.editSelectInvertAction, SIGNAL(triggered(bool)), SLOT(editSelectInvert())); QObject::connect(m_ui.editSelectTrackAction, SIGNAL(triggered(bool)), SLOT(editSelectTrack())); QObject::connect(m_ui.editSelectTrackRangeAction, SIGNAL(triggered(bool)), SLOT(editSelectTrackRange())); QObject::connect(m_ui.editSelectRangeAction, SIGNAL(triggered(bool)), SLOT(editSelectRange())); QObject::connect(m_ui.editInsertRangeAction, SIGNAL(triggered(bool)), SLOT(editInsertRange())); QObject::connect(m_ui.editInsertTrackRangeAction, SIGNAL(triggered(bool)), SLOT(editInsertTrackRange())); QObject::connect(m_ui.editRemoveRangeAction, SIGNAL(triggered(bool)), SLOT(editRemoveRange())); QObject::connect(m_ui.editRemoveTrackRangeAction, SIGNAL(triggered(bool)), SLOT(editRemoveTrackRange())); QObject::connect(m_ui.editSplitAction, SIGNAL(triggered(bool)), SLOT(editSplit())); QObject::connect(m_ui.trackAddAction, SIGNAL(triggered(bool)), SLOT(trackAdd())); QObject::connect(m_ui.trackRemoveAction, SIGNAL(triggered(bool)), SLOT(trackRemove())); QObject::connect(m_ui.trackDuplicateAction, SIGNAL(triggered(bool)), SLOT(trackDuplicate())); QObject::connect(m_ui.trackPropertiesAction, SIGNAL(triggered(bool)), SLOT(trackProperties())); QObject::connect(m_ui.trackInputsAction, SIGNAL(triggered(bool)), SLOT(trackInputs())); QObject::connect(m_ui.trackOutputsAction, SIGNAL(triggered(bool)), SLOT(trackOutputs())); QObject::connect(m_ui.trackStateRecordAction, SIGNAL(triggered(bool)), SLOT(trackStateRecord(bool))); QObject::connect(m_ui.trackStateMuteAction, SIGNAL(triggered(bool)), SLOT(trackStateMute(bool))); QObject::connect(m_ui.trackStateSoloAction, SIGNAL(triggered(bool)), SLOT(trackStateSolo(bool))); QObject::connect(m_ui.trackStateMonitorAction, SIGNAL(triggered(bool)), SLOT(trackStateMonitor(bool))); QObject::connect(m_ui.trackNavigateFirstAction, SIGNAL(triggered(bool)), SLOT(trackNavigateFirst())); QObject::connect(m_ui.trackNavigatePrevAction, SIGNAL(triggered(bool)), SLOT(trackNavigatePrev())); QObject::connect(m_ui.trackNavigateNextAction, SIGNAL(triggered(bool)), SLOT(trackNavigateNext())); QObject::connect(m_ui.trackNavigateLastAction, SIGNAL(triggered(bool)), SLOT(trackNavigateLast())); QObject::connect(m_ui.trackNavigateNoneAction, SIGNAL(triggered(bool)), SLOT(trackNavigateNone())); QObject::connect(m_ui.trackMoveTopAction, SIGNAL(triggered(bool)), SLOT(trackMoveTop())); QObject::connect(m_ui.trackMoveUpAction, SIGNAL(triggered(bool)), SLOT(trackMoveUp())); QObject::connect(m_ui.trackMoveDownAction, SIGNAL(triggered(bool)), SLOT(trackMoveDown())); QObject::connect(m_ui.trackMoveBottomAction, SIGNAL(triggered(bool)), SLOT(trackMoveBottom())); QObject::connect(m_ui.trackHeightIncreaseAction, SIGNAL(triggered(bool)), SLOT(trackHeightIncrease())); QObject::connect(m_ui.trackHeightDecreaseAction, SIGNAL(triggered(bool)), SLOT(trackHeightDecrease())); QObject::connect(m_ui.trackHeightMinimizeAction, SIGNAL(triggered(bool)), SLOT(trackHeightMinimize())); QObject::connect(m_ui.trackHeightResetAction, SIGNAL(triggered(bool)), SLOT(trackHeightReset())); QObject::connect(m_ui.trackAutoMonitorAction, SIGNAL(triggered(bool)), SLOT(trackAutoMonitor(bool))); QObject::connect(m_ui.trackAutoDeactivateAction, SIGNAL(triggered(bool)), SLOT(trackAutoDeactivate(bool))); QObject::connect(m_ui.trackImportAudioAction, SIGNAL(triggered(bool)), SLOT(trackImportAudio())); QObject::connect(m_ui.trackImportMidiAction, SIGNAL(triggered(bool)), SLOT(trackImportMidi())); QObject::connect(m_ui.trackExportAudioAction, SIGNAL(triggered(bool)), SLOT(trackExportAudio())); QObject::connect(m_ui.trackExportMidiAction, SIGNAL(triggered(bool)), SLOT(trackExportMidi())); QObject::connect(m_ui.trackCurveSelectMenu, SIGNAL(triggered(QAction *)), SLOT(trackCurveSelect(QAction *))); QObject::connect(m_ui.trackCurveModeMenu, SIGNAL(triggered(QAction *)), SLOT(trackCurveMode(QAction *))); QObject::connect(m_ui.trackCurveLogarithmicAction, SIGNAL(triggered(bool)), SLOT(trackCurveLogarithmic(bool))); QObject::connect(m_ui.trackCurveColorAction, SIGNAL(triggered(bool)), SLOT(trackCurveColor())); QObject::connect(m_ui.trackCurveLockedAction, SIGNAL(triggered(bool)), SLOT(trackCurveLocked(bool))); QObject::connect(m_ui.trackCurveProcessAction, SIGNAL(triggered(bool)), SLOT(trackCurveProcess(bool))); QObject::connect(m_ui.trackCurveCaptureAction, SIGNAL(triggered(bool)), SLOT(trackCurveCapture(bool))); QObject::connect(m_ui.trackCurveClearAction, SIGNAL(triggered(bool)), SLOT(trackCurveClear())); QObject::connect(m_ui.trackCurveLockedAllAction, SIGNAL(triggered(bool)), SLOT(trackCurveLockedAll(bool))); QObject::connect(m_ui.trackCurveProcessAllAction, SIGNAL(triggered(bool)), SLOT(trackCurveProcessAll(bool))); QObject::connect(m_ui.trackCurveCaptureAllAction, SIGNAL(triggered(bool)), SLOT(trackCurveCaptureAll(bool))); QObject::connect(m_ui.trackCurveClearAllAction, SIGNAL(triggered(bool)), SLOT(trackCurveClearAll())); QObject::connect(m_ui.clipNewAction, SIGNAL(triggered(bool)), SLOT(clipNew())); QObject::connect(m_ui.clipEditAction, SIGNAL(triggered(bool)), SLOT(clipEdit())); QObject::connect(m_ui.clipMuteAction, SIGNAL(triggered(bool)), SLOT(clipMute())); QObject::connect(m_ui.clipUnlinkAction, SIGNAL(triggered(bool)), SLOT(clipUnlink())); QObject::connect(m_ui.clipRecordExAction, SIGNAL(triggered(bool)), SLOT(clipRecordEx(bool))); QObject::connect(m_ui.clipSplitAction, SIGNAL(triggered(bool)), SLOT(clipSplit())); QObject::connect(m_ui.clipMergeAction, SIGNAL(triggered(bool)), SLOT(clipMerge())); QObject::connect(m_ui.clipNormalizeAction, SIGNAL(triggered(bool)), SLOT(clipNormalize())); QObject::connect(m_ui.clipTempoAdjustAction, SIGNAL(triggered(bool)), SLOT(clipTempoAdjust())); QObject::connect(m_ui.clipCrossFadeAction, SIGNAL(triggered(bool)), SLOT(clipCrossFade())); QObject::connect(m_ui.clipRangeSetAction, SIGNAL(triggered(bool)), SLOT(clipRangeSet())); QObject::connect(m_ui.clipLoopSetAction, SIGNAL(triggered(bool)), SLOT(clipLoopSet())); QObject::connect(m_ui.clipImportAction, SIGNAL(triggered(bool)), SLOT(clipImport())); QObject::connect(m_ui.clipExportAction, SIGNAL(triggered(bool)), SLOT(clipExport())); QObject::connect(m_ui.clipToolsQuantizeAction, SIGNAL(triggered(bool)), SLOT(clipToolsQuantize())); QObject::connect(m_ui.clipToolsTransposeAction, SIGNAL(triggered(bool)), SLOT(clipToolsTranspose())); QObject::connect(m_ui.clipToolsNormalizeAction, SIGNAL(triggered(bool)), SLOT(clipToolsNormalize())); QObject::connect(m_ui.clipToolsRandomizeAction, SIGNAL(triggered(bool)), SLOT(clipToolsRandomize())); QObject::connect(m_ui.clipToolsResizeAction, SIGNAL(triggered(bool)), SLOT(clipToolsResize())); QObject::connect(m_ui.clipToolsRescaleAction, SIGNAL(triggered(bool)), SLOT(clipToolsRescale())); QObject::connect(m_ui.clipToolsTimeshiftAction, SIGNAL(triggered(bool)), SLOT(clipToolsTimeshift())); QObject::connect(m_ui.clipToolsTemporampAction, SIGNAL(triggered(bool)), SLOT(clipToolsTemporamp())); QObject::connect(m_ui.clipTakeSelectMenu, SIGNAL(triggered(QAction *)), SLOT(clipTakeSelect(QAction *))); QObject::connect(m_ui.clipTakeFirstAction, SIGNAL(triggered(bool)), SLOT(clipTakeFirst())); QObject::connect(m_ui.clipTakePrevAction, SIGNAL(triggered(bool)), SLOT(clipTakePrev())); QObject::connect(m_ui.clipTakeNextAction, SIGNAL(triggered(bool)), SLOT(clipTakeNext())); QObject::connect(m_ui.clipTakeLastAction, SIGNAL(triggered(bool)), SLOT(clipTakeLast())); QObject::connect(m_ui.clipTakeResetAction, SIGNAL(triggered(bool)), SLOT(clipTakeReset())); QObject::connect(m_ui.clipTakeRangeAction, SIGNAL(triggered(bool)), SLOT(clipTakeRange())); QObject::connect(m_ui.viewMenubarAction, SIGNAL(triggered(bool)), SLOT(viewMenubar(bool))); QObject::connect(m_ui.viewStatusbarAction, SIGNAL(triggered(bool)), SLOT(viewStatusbar(bool))); QObject::connect(m_ui.viewToolbarFileAction, SIGNAL(triggered(bool)), SLOT(viewToolbarFile(bool))); QObject::connect(m_ui.viewToolbarEditAction, SIGNAL(triggered(bool)), SLOT(viewToolbarEdit(bool))); QObject::connect(m_ui.viewToolbarTrackAction, SIGNAL(triggered(bool)), SLOT(viewToolbarTrack(bool))); QObject::connect(m_ui.viewToolbarViewAction, SIGNAL(triggered(bool)), SLOT(viewToolbarView(bool))); QObject::connect(m_ui.viewToolbarOptionsAction, SIGNAL(triggered(bool)), SLOT(viewToolbarOptions(bool))); QObject::connect(m_ui.viewToolbarTransportAction, SIGNAL(triggered(bool)), SLOT(viewToolbarTransport(bool))); QObject::connect(m_ui.viewToolbarTimeAction, SIGNAL(triggered(bool)), SLOT(viewToolbarTime(bool))); QObject::connect(m_ui.viewToolbarThumbAction, SIGNAL(triggered(bool)), SLOT(viewToolbarThumb(bool))); QObject::connect(m_ui.viewFileSystemAction, SIGNAL(triggered(bool)), SLOT(viewFileSystem(bool))); QObject::connect(m_ui.viewFilesAction, SIGNAL(triggered(bool)), SLOT(viewFiles(bool))); QObject::connect(m_ui.viewMessagesAction, SIGNAL(triggered(bool)), SLOT(viewMessages(bool))); QObject::connect(m_ui.viewConnectionsAction, SIGNAL(triggered(bool)), SLOT(viewConnections(bool))); QObject::connect(m_ui.viewMixerAction, SIGNAL(triggered(bool)), SLOT(viewMixer(bool))); QObject::connect(m_ui.viewZoomInAction, SIGNAL(triggered(bool)), SLOT(viewZoomIn())); QObject::connect(m_ui.viewZoomOutAction, SIGNAL(triggered(bool)), SLOT(viewZoomOut())); QObject::connect(m_ui.viewZoomResetAction, SIGNAL(triggered(bool)), SLOT(viewZoomReset())); QObject::connect(m_ui.viewZoomHorizontalAction, SIGNAL(triggered(bool)), SLOT(viewZoomHorizontal())); QObject::connect(m_ui.viewZoomVerticalAction, SIGNAL(triggered(bool)), SLOT(viewZoomVertical())); QObject::connect(m_ui.viewZoomAllAction, SIGNAL(triggered(bool)), SLOT(viewZoomAll())); QObject::connect(m_ui.viewSnapZebraAction, SIGNAL(triggered(bool)), SLOT(viewSnapZebra(bool))); QObject::connect(m_ui.viewSnapGridAction, SIGNAL(triggered(bool)), SLOT(viewSnapGrid(bool))); QObject::connect(m_ui.viewToolTipsAction, SIGNAL(triggered(bool)), SLOT(viewToolTips(bool))); QObject::connect(m_ui.viewRefreshAction, SIGNAL(triggered(bool)), SLOT(viewRefresh())); QObject::connect(m_ui.viewInstrumentsAction, SIGNAL(triggered(bool)), SLOT(viewInstruments())); QObject::connect(m_ui.viewControllersAction, SIGNAL(triggered(bool)), SLOT(viewControllers())); QObject::connect(m_ui.viewBusesAction, SIGNAL(triggered(bool)), SLOT(viewBuses())); QObject::connect(m_ui.viewTempoMapAction, SIGNAL(triggered(bool)), SLOT(viewTempoMap())); QObject::connect(m_ui.viewOptionsAction, SIGNAL(triggered(bool)), SLOT(viewOptions())); QObject::connect(m_ui.transportBackwardAction, SIGNAL(triggered(bool)), SLOT(transportBackward())); QObject::connect(m_ui.transportRewindAction, SIGNAL(triggered(bool)), SLOT(transportRewind())); QObject::connect(m_ui.transportFastForwardAction, SIGNAL(triggered(bool)), SLOT(transportFastForward())); QObject::connect(m_ui.transportForwardAction, SIGNAL(triggered(bool)), SLOT(transportForward())); QObject::connect(m_ui.transportStepBackwardAction, SIGNAL(triggered(bool)), SLOT(transportStepBackward())); QObject::connect(m_ui.transportStepForwardAction, SIGNAL(triggered(bool)), SLOT(transportStepForward())); QObject::connect(m_ui.transportLoopAction, SIGNAL(triggered(bool)), SLOT(transportLoop())); QObject::connect(m_ui.transportLoopSetAction, SIGNAL(triggered(bool)), SLOT(transportLoopSet())); QObject::connect(m_ui.transportStopAction, SIGNAL(triggered(bool)), SLOT(transportStop())); QObject::connect(m_ui.transportPlayAction, SIGNAL(triggered(bool)), SLOT(transportPlay())); QObject::connect(m_ui.transportRecordAction, SIGNAL(triggered(bool)), SLOT(transportRecord())); QObject::connect(m_ui.transportPunchAction, SIGNAL(triggered(bool)), SLOT(transportPunch())); QObject::connect(m_ui.transportPunchSetAction, SIGNAL(triggered(bool)), SLOT(transportPunchSet())); QObject::connect(m_ui.transportCountInAction, SIGNAL(triggered(bool)), SLOT(transportCountIn())); QObject::connect(m_ui.transportMetroAction, SIGNAL(triggered(bool)), SLOT(transportMetro())); QObject::connect(m_ui.transportFollowAction, SIGNAL(triggered(bool)), SLOT(transportFollow())); QObject::connect(m_ui.transportAutoBackwardAction, SIGNAL(triggered(bool)), SLOT(transportAutoBackward())); QObject::connect(m_ui.transportContinueAction, SIGNAL(triggered(bool)), SLOT(transportContinue())); QObject::connect(m_ui.transportModeNoneAction, SIGNAL(triggered(bool)), SLOT(transportModeNone())); QObject::connect(m_ui.transportModeSlaveAction, SIGNAL(triggered(bool)), SLOT(transportModeSlave())); QObject::connect(m_ui.transportModeMasterAction, SIGNAL(triggered(bool)), SLOT(transportModeMaster())); QObject::connect(m_ui.transportModeFullAction, SIGNAL(triggered(bool)), SLOT(transportModeFull())); QObject::connect(m_ui.transportPanicAction, SIGNAL(triggered(bool)), SLOT(transportPanic())); QObject::connect(m_ui.helpShortcutsAction, SIGNAL(triggered(bool)), SLOT(helpShortcuts())); QObject::connect(m_ui.helpAboutAction, SIGNAL(triggered(bool)), SLOT(helpAbout())); QObject::connect(m_ui.helpAboutQtAction, SIGNAL(triggered(bool)), SLOT(helpAboutQt())); QObject::connect(m_ui.fileMenu, SIGNAL(aboutToShow()), SLOT(updateRecentFilesMenu())); QObject::connect(m_ui.trackMenu, SIGNAL(aboutToShow()), SLOT(updateTrackMenu())); QObject::connect(m_ui.clipMenu, SIGNAL(aboutToShow()), SLOT(updateClipMenu())); QObject::connect(m_ui.clipTakeMenu, SIGNAL(aboutToShow()), SLOT(updateTakeMenu())); QObject::connect(m_ui.clipTakeSelectMenu, SIGNAL(aboutToShow()), SLOT(updateTakeSelectMenu())); QObject::connect(m_ui.trackExportMenu, SIGNAL(aboutToShow()), SLOT(updateExportMenu())); QObject::connect(m_ui.trackCurveMenu, SIGNAL(aboutToShow()), SLOT(updateCurveMenu())); QObject::connect(m_ui.trackCurveModeMenu, SIGNAL(aboutToShow()), SLOT(updateCurveModeMenu())); QObject::connect(m_ui.trackInstrumentMenu, SIGNAL(aboutToShow()), SLOT(updateTrackInstrumentMenu())); QObject::connect(m_ui.viewZoomMenu, SIGNAL(aboutToShow()), SLOT(updateZoomMenu())); QObject::connect(m_ui.viewSnapMenu, SIGNAL(aboutToShow()), SLOT(updateSnapMenu())); QObject::connect(m_pSession->commands(), SIGNAL(updateNotifySignal(unsigned int)), SLOT(updateNotifySlot(unsigned int))); // Already handled in files widget... // QObject::connect(QApplication::clipboard(), // SIGNAL(dataChanged()), // SLOT(stabilizeForm())); } // Destructor. qtractorMainForm::~qtractorMainForm (void) { #ifdef HAVE_SIGNAL_H if (m_pSigtermNotifier) delete m_pSigtermNotifier; if (m_pSigusr1Notifier) delete m_pSigusr1Notifier; #endif // View/Snap-to-beat actions termination... qDeleteAll(m_snapPerBeatActions); m_snapPerBeatActions.clear(); // Drop any widgets around (not really necessary)... if (m_pMixer) delete m_pMixer; if (m_pConnections) delete m_pConnections; if (m_pFiles) delete m_pFiles; if (m_pFileSystem) delete m_pFileSystem; if (m_pMessages) delete m_pMessages; if (m_pTracks) delete m_pTracks; // Get select mode action group down. if (m_pSelectModeActionGroup) delete m_pSelectModeActionGroup; if (m_pSelectModeToolButton) delete m_pSelectModeToolButton; // Get transport mode action group down. if (m_pTransportModeActionGroup) delete m_pTransportModeActionGroup; if (m_pTransportModeToolButton) delete m_pTransportModeToolButton; // Reclaim status items palettes... for (int i = 0; i < PaletteItems; ++i) delete m_paletteItems[i]; // Destroy instrument menu proxy. if (m_pInstrumentMenu) delete m_pInstrumentMenu; // Custom tempo cursor. if (m_pTempoCursor) delete m_pTempoCursor; // Remove midi controllers. if (m_pMidiControl) delete m_pMidiControl; // Remove (QAction) MIDI observer map. if (m_pActionControl) delete m_pActionControl; // Remove plugin path/files registry. if (m_pPluginFactory) delete m_pPluginFactory; // Remove audio file formats registry. if (m_pAudioFileFactory) delete m_pAudioFileFactory; // Remove message list buffer. if (m_pMessageList) delete m_pMessageList; // And finally the session object. if (m_pSession) delete m_pSession; // Pseudo-singleton reference shut-down. g_pMainForm = nullptr; } // kind of singleton reference. qtractorMainForm *qtractorMainForm::getInstance (void) { return g_pMainForm; } // Make and set a proper setup options step. void qtractorMainForm::setup ( qtractorOptions *pOptions ) { // We got options? m_pOptions = pOptions; // Some child/dockable forms are to be created right now. m_pFileSystem = new qtractorFileSystem(this); m_pFiles = new qtractorFiles(this); m_pFiles->audioListView()->setRecentDir(m_pOptions->sAudioDir); m_pFiles->midiListView()->setRecentDir(m_pOptions->sMidiDir); // Setup messages logging appropriately... m_pMessages = new qtractorMessages(this); m_pMessages->setLogging( m_pOptions->bMessagesLog, m_pOptions->sMessagesLogPath); // What style do we create tool childs? QWidget *pParent = nullptr; Qt::WindowFlags wflags = Qt::Window; if (m_pOptions->bKeepToolsOnTop) { wflags |= Qt::Tool; // wflags |= Qt::WindowStaysOnTopHint; pParent = this; } // Other child/tools forms are also created right away... m_pConnections = new qtractorConnections(pParent, wflags); m_pMixer = new qtractorMixer(pParent, wflags); // Make those primordially docked... addDockWidget(Qt::LeftDockWidgetArea, m_pFileSystem, Qt::Vertical); addDockWidget(Qt::RightDockWidgetArea, m_pFiles, Qt::Vertical); addDockWidget(Qt::BottomDockWidgetArea, m_pMessages, Qt::Horizontal); // Time to create the main session track list view... m_pTracks = new qtractorTracks(this); // Make it shine :-) setCentralWidget(m_pTracks); // Set message defaults... updateMessagesFont(); updateMessagesLimit(); updateMessagesCapture(); // Track view select mode... const qtractorTrackView::SelectMode selectMode = qtractorTrackView::SelectMode(pOptions->iTrackViewSelectMode); switch (selectMode) { case qtractorTrackView::SelectRect: m_ui.editSelectModeRectAction->setChecked(true); break; case qtractorTrackView::SelectRange: m_ui.editSelectModeRangeAction->setChecked(true); break; case qtractorTrackView::SelectClip: default: m_ui.editSelectModeClipAction->setChecked(true); break; } qtractorTrackView *pTrackView = m_pTracks->trackView(); pTrackView->setSelectMode(selectMode); pTrackView->setDropSpan(m_pOptions->bTrackViewDropSpan); pTrackView->setSnapGrid(m_pOptions->bTrackViewSnapGrid); pTrackView->setToolTips(m_pOptions->bTrackViewToolTips); pTrackView->setCurveEdit(m_pOptions->bTrackViewCurveEdit); if (pTrackView->isCurveEdit()) m_ui.editSelectModeCurveAction->setChecked(true); // Set initial select mode... m_pSelectModeToolButton->setDefaultAction( m_pSelectModeActionGroup->checkedAction()); // Initial zoom mode... m_pTracks->setZoomMode(m_pOptions->iZoomMode); // Initial auto time-stretching, loop-recording modes... m_pSession->setAutoTimeStretch(m_pOptions->bAudioAutoTimeStretch); m_pSession->setLoopRecordingMode(m_pOptions->iLoopRecordingMode); // Initial auto plugin deactivation m_pSession->setAutoDeactivate(m_pOptions->bAutoDeactivate); // Initial decorations toggle state. m_ui.viewMenubarAction->setChecked(m_pOptions->bMenubar); m_ui.viewStatusbarAction->setChecked(m_pOptions->bStatusbar); m_ui.viewToolbarFileAction->setChecked(m_pOptions->bFileToolbar); m_ui.viewToolbarEditAction->setChecked(m_pOptions->bEditToolbar); m_ui.viewToolbarTrackAction->setChecked(m_pOptions->bTrackToolbar); m_ui.viewToolbarViewAction->setChecked(m_pOptions->bViewToolbar); m_ui.viewToolbarOptionsAction->setChecked(m_pOptions->bOptionsToolbar); m_ui.viewToolbarTransportAction->setChecked(m_pOptions->bTransportToolbar); m_ui.viewToolbarTimeAction->setChecked(m_pOptions->bTimeToolbar); m_ui.viewToolbarThumbAction->setChecked(m_pOptions->bThumbToolbar); m_ui.viewSnapZebraAction->setChecked(pOptions->bTrackViewSnapZebra); m_ui.viewSnapGridAction->setChecked(pOptions->bTrackViewSnapGrid); m_ui.viewToolTipsAction->setChecked(pOptions->bTrackViewToolTips); m_ui.transportCountInAction->setChecked(m_pOptions->bCountIn); m_ui.transportMetroAction->setChecked(m_pOptions->bMetronome); m_ui.transportFollowAction->setChecked(m_pOptions->bFollowPlayhead); m_ui.transportAutoBackwardAction->setChecked(m_pOptions->bAutoBackward); m_ui.transportContinueAction->setChecked(m_pOptions->bContinuePastEnd); m_ui.trackAutoMonitorAction->setChecked(m_pOptions->bAutoMonitor); m_ui.trackAutoDeactivateAction->setChecked(m_pOptions->bAutoDeactivate); // Initial decorations visibility state. viewMenubar(m_pOptions->bMenubar); viewStatusbar(m_pOptions->bStatusbar); viewToolbarFile(m_pOptions->bFileToolbar); viewToolbarEdit(m_pOptions->bEditToolbar); viewToolbarTrack(m_pOptions->bTrackToolbar); viewToolbarView(m_pOptions->bViewToolbar); viewToolbarOptions(m_pOptions->bOptionsToolbar); viewToolbarTransport(m_pOptions->bTransportToolbar); viewToolbarTime(m_pOptions->bTimeToolbar); viewToolbarThumb(m_pOptions->bThumbToolbar); // Restore whole dock windows state. const QByteArray aDockables = m_pOptions->settings().value("/Layout/DockWindows").toByteArray(); if (aDockables.isEmpty()) { // Some windows are forced initially as is... insertToolBarBreak(m_ui.transportToolbar); } else { // Make it as the last time. restoreState(aDockables); } // Restore file-system dock-window state. if (m_pFileSystem) { const QByteArray aFileSystem = m_pOptions->settings().value("/FileSystem/State").toByteArray(); if (aFileSystem.isEmpty()) { // Should be hidden first time... viewFileSystem(false); } else { // Make it as the last time visible. m_pFileSystem->restoreState(aFileSystem); } } // Try to restore old window positioning. m_pOptions->loadWidgetGeometry(this); m_pOptions->loadWidgetGeometry(m_pMixer); m_pOptions->loadWidgetGeometry(m_pConnections); // Set MIDI control non catch-up/hook global option... qtractorMidiControl::setSync(m_pOptions->bMidiControlSync); // Load MIDI controller configuration files... QStringListIterator it(m_pOptions->midiControlFiles); while (it.hasNext()) m_pMidiControl->loadDocument(it.next()); // Load instrument definition files... QStringListIterator iter(m_pOptions->instrumentFiles); while (iter.hasNext()) (m_pSession->instruments())->load(iter.next()); // Load custom meter colors, if any... int iColor; for (iColor = 0; iColor < m_pOptions->audioMeterColors.count(); ++iColor) qtractorAudioMeter::setColor(iColor, QColor(m_pOptions->audioMeterColors[iColor])); for (iColor = 0; iColor < m_pOptions->midiMeterColors.count(); ++iColor) qtractorMidiMeter::setColor(iColor, QColor(m_pOptions->midiMeterColors[iColor])); // Primary startup stabilization... updateRecentFilesMenu(); updatePeakAutoRemove(); updateDisplayFormat(); updateTransportModePre(); updateTransportModePost(); updateTimebase(); updateAudioPlayer(); updateAudioMetronome(); updateMidiControlModes(); updateMidiQueueTimer(); updateMidiDriftCorrect(); updateMidiPlayer(); updateMidiControl(); updateMidiMetronome(); updateSyncViewHold(); // FIXME: This is what it should ever be, // make it right from this very moment... qtractorAudioFileFactory::setDefaultType( m_pOptions->sAudioCaptureExt, m_pOptions->iAudioCaptureType, m_pOptions->iAudioCaptureFormat, m_pOptions->iAudioCaptureQuality); qtractorMidiClip::setDefaultFormat( m_pOptions->iMidiCaptureFormat); // Set default MIDI (plugin) instrument audio output mode. qtractorMidiManager::setDefaultAudioOutputBus( m_pOptions->bAudioOutputBus); qtractorMidiManager::setDefaultAudioOutputAutoConnect( m_pOptions->bAudioOutputAutoConnect); // Set default audio-buffer quality... qtractorAudioBuffer::setDefaultResampleType( m_pOptions->iAudioResampleType); unsigned int iStretcherFlags = 0; if (m_pOptions->bAudioWsolaTimeStretch) iStretcherFlags |= qtractorTimeStretcher::WsolaTimeStretch; if (m_pOptions->bAudioWsolaQuickSeek) iStretcherFlags |= qtractorTimeStretcher::WsolaQuickSeek; #ifdef CONFIG_LIBRUBBERBAND if (m_pOptions->bAudioRubberBandFormant) iStretcherFlags |= qtractorTimeStretcher::RubberBandFormant; #ifdef CONFIG_LIBRUBBERBAND_R3 if (m_pOptions->bAudioRubberBandFinerR3) iStretcherFlags |= qtractorTimeStretcher::RubberBandFinerR3; #endif #endif qtractorAudioBuffer::setDefaultStretcherFlags(iStretcherFlags); qtractorTrack::setTrackColorSaturation( m_pOptions->iTrackColorSaturation); // Set default custom spin-box edit mode (deferred)... qtractorSpinBox::setEditMode(qtractorSpinBox::DeferredMode); // Load (action) keyboard shortcuts... m_pOptions->loadActionShortcuts(this); m_pOptions->loadActionControls(this); // Initial thumb-view background (empty) m_pThumbView->updateContents(); // Some special defaults... qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine) pAudioEngine->setMasterAutoConnect(m_pOptions->bAudioMasterAutoConnect); // Final widget slot connections.... QObject::connect(m_pFileSystem->toggleViewAction(), SIGNAL(triggered(bool)), SLOT(stabilizeForm())); QObject::connect(m_pFiles->toggleViewAction(), SIGNAL(triggered(bool)), SLOT(stabilizeForm())); QObject::connect(m_pMessages->toggleViewAction(), SIGNAL(triggered(bool)), SLOT(stabilizeForm())); QObject::connect(m_pConnections->connectForm(), SIGNAL(connectChanged()), SLOT(contentsChanged())); QObject::connect(m_pMixer->trackRack(), SIGNAL(selectionChanged()), SLOT(mixerSelectionChanged())); QObject::connect(m_pFiles->audioListView(), SIGNAL(selected(const QString&, int, bool)), SLOT(selectAudioFile(const QString&, int, bool))); QObject::connect(m_pFiles->audioListView(), SIGNAL(activated(const QString&, int)), SLOT(activateAudioFile(const QString&, int))); QObject::connect(m_pFiles->midiListView(), SIGNAL(selected(const QString&, int, bool)), SLOT(selectMidiFile(const QString&, int, bool))); QObject::connect(m_pFiles->midiListView(), SIGNAL(activated(const QString&, int)), SLOT(activateMidiFile(const QString&, int))); QObject::connect(m_pFiles->audioListView(), SIGNAL(contentsChanged()), SLOT(contentsChanged())); QObject::connect(m_pFiles->midiListView(), SIGNAL(contentsChanged()), SLOT(contentsChanged())); QObject::connect(m_pFileSystem, SIGNAL(activated(const QString&)), SLOT(activateFile(const QString&))); QObject::connect(m_pTracks->trackList(), SIGNAL(selectionChanged()), SLOT(trackSelectionChanged())); // Other dedicated signal/slot connections... QObject::connect(m_pTracks->trackView(), SIGNAL(contentsMoving(int,int)), m_pThumbView, SLOT(updateThumb())); #ifdef CONFIG_NSM // Check whether to participate into a NSM session... const QString& sNsmUrl = QString::fromLatin1(::getenv("NSM_URL")); if (!sNsmUrl.isEmpty()) { m_pNsmClient = new qtractorNsmClient(sNsmUrl); QObject::connect(m_pNsmClient, SIGNAL(open()), SLOT(openNsmSession())); QObject::connect(m_pNsmClient, SIGNAL(save()), SLOT(saveNsmSession())); QObject::connect(m_pNsmClient, SIGNAL(show()), SLOT(showNsmSession())); QObject::connect(m_pNsmClient, SIGNAL(hide()), SLOT(hideNsmSession())); m_pNsmClient->announce(QTRACTOR_TITLE, ":switch:dirty:optional-gui:"); m_sNsmExt = m_pOptions->sSessionExt; if (m_sNsmExt.isEmpty()) m_sNsmExt = qtractorDocument::defaultExt(); // Run-time special non-persistent options. //m_pOptions->bDontUseNativeDialogs = true; QMainWindow::hide(); m_pConnections->hide(); m_pMixer->hide(); } else #endif QMainWindow::show(); // Make it ready :-) statusBar()->showMessage(tr("Ready"), 3000); // Is any session identification to get loaded? const bool bSessionId = !m_pOptions->sSessionId.isEmpty(); if (bSessionId && pAudioEngine) { pAudioEngine->setSessionId(m_pOptions->sSessionId); m_pOptions->sSessionId.clear(); } // Is any session pending to be loaded? if (!m_pOptions->sessionFiles.isEmpty()) { // We're supposedly clean... m_iDirtyCount = 0; // Just load the prabable startup session... const int iFlags = qtractorDocument::Default; if (loadSessionFileEx(m_pOptions->sessionFiles, iFlags, !bSessionId)) { m_pOptions->sessionFiles.clear(); // Take appropriate action when session is loaded from // some foreign session manager (eg. JACK session)... const QString& sLadishAppName = QString::fromLatin1(::getenv("LADISH_APP_NAME")); const bool bLadishApp = !sLadishAppName.isEmpty(); if (bSessionId || bLadishApp) { // JACK session manager will take care of audio connections... if (pAudioEngine) pAudioEngine->clearConnects(); // LADISH session manager will take care of MIDI connections... if (bLadishApp) m_pSession->midiEngine()->clearConnects(); } } } else { // Change to last known session dir... if (!m_pOptions->sSessionDir.isEmpty()) { QFileInfo info(m_pOptions->sSessionDir); while (!info.exists() && !info.isRoot()) info.setFile(info.absolutePath()); if (info.exists() && !info.isRoot()) m_pOptions->sSessionDir = info.absoluteFilePath(); if (!QDir::setCurrent(m_pOptions->sSessionDir)) { appendMessagesError( tr("Could not set default session directory:\n\n" "%1\n\nSorry.").arg(m_pOptions->sSessionDir)); m_pOptions->sSessionDir.clear(); } } // Open up with a new empty session... if (!autoSaveOpen()) newSession(); } autoSaveReset(); // Register the first timer slots. QTimer::singleShot(QTRACTOR_TIMER_DELAY, this, SLOT(slowTimerSlot())); QTimer::singleShot(QTRACTOR_TIMER_DELAY, this, SLOT(fastTimerSlot())); } // LADISH Level 1 -- SIGUSR1 signal handler. void qtractorMainForm::handle_sigusr1 (void) { #ifdef HAVE_SIGNAL_H char c; if (::read(g_fdSigusr1[1], &c, sizeof(c)) > 0) saveSession(false); #endif } // LADISH termination -- SIGTERM signal handler. void qtractorMainForm::handle_sigterm (void) { #ifdef HAVE_SIGNAL_H char c; if (::read(g_fdSigterm[1], &c, sizeof(c)) > 0) { #ifdef CONFIG_NSM if (m_pNsmClient && m_pNsmClient->is_active()) m_iDirtyCount = 0; #endif if (queryClose()) { #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QApplication::exit(0); #else QApplication::quit(); #endif } } #endif } // Window close event handlers. bool qtractorMainForm::queryClose (void) { bool bQueryClose = closeSession(); // Try to save current general state... if (m_pOptions) { // Some windows default fonts is here on demand too. if (bQueryClose && m_pMessages) m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString(); // Save recent directories... if (bQueryClose && m_pFiles) { m_pOptions->sAudioDir = m_pFiles->audioListView()->recentDir(); m_pOptions->sMidiDir = m_pFiles->midiListView()->recentDir(); } // Try to save current positioning. if (bQueryClose) { // Save decorations state. if (QMainWindow::isVisible()) { m_pOptions->bMenubar = m_ui.menuBar->isVisible(); m_pOptions->bStatusbar = statusBar()->isVisible(); m_pOptions->bFileToolbar = m_ui.fileToolbar->isVisible(); m_pOptions->bEditToolbar = m_ui.editToolbar->isVisible(); m_pOptions->bTrackToolbar = m_ui.trackToolbar->isVisible(); m_pOptions->bViewToolbar = m_ui.viewToolbar->isVisible(); m_pOptions->bOptionsToolbar = m_ui.optionsToolbar->isVisible(); m_pOptions->bTransportToolbar = m_ui.transportToolbar->isVisible(); m_pOptions->bTimeToolbar = m_ui.timeToolbar->isVisible(); m_pOptions->bThumbToolbar = m_ui.thumbViewToolbar->isVisible(); } m_pOptions->bTrackViewSnapZebra = m_ui.viewSnapZebraAction->isChecked(); m_pOptions->bTrackViewSnapGrid = m_ui.viewSnapGridAction->isChecked(); m_pOptions->bTrackViewToolTips = m_ui.viewToolTipsAction->isChecked(); m_pOptions->bTrackViewCurveEdit = m_ui.editSelectModeCurveAction->isChecked(); m_pOptions->bCountIn = m_ui.transportCountInAction->isChecked(); m_pOptions->bMetronome = m_ui.transportMetroAction->isChecked(); m_pOptions->bFollowPlayhead = m_ui.transportFollowAction->isChecked(); m_pOptions->bAutoBackward = m_ui.transportAutoBackwardAction->isChecked(); m_pOptions->bContinuePastEnd = m_ui.transportContinueAction->isChecked(); m_pOptions->bAutoMonitor = m_ui.trackAutoMonitorAction->isChecked(); m_pOptions->bAutoDeactivate = m_ui.trackAutoDeactivateAction->isChecked(); // Final zoom mode... if (m_pTracks) m_pOptions->iZoomMode = m_pTracks->zoomMode(); // Save instrument definition file list... m_pOptions->instrumentFiles = (m_pSession->instruments())->files(); // Save custom meter colors, if any... int iColor; m_pOptions->audioMeterColors.clear(); for (iColor = 0; iColor < qtractorAudioMeter::ColorCount - 1; ++iColor) m_pOptions->audioMeterColors.append( qtractorAudioMeter::color(iColor).name()); m_pOptions->midiMeterColors.clear(); for (iColor = 0; iColor < qtractorMidiMeter::ColorCount - 1; ++iColor) m_pOptions->midiMeterColors.append( qtractorMidiMeter::color(iColor).name()); // Make sure there will be defaults... m_pOptions->iSnapPerBeat = m_pSnapPerBeatComboBox->currentIndex(); m_pOptions->fTempo = m_pTempoSpinBox->tempo(); m_pOptions->iBeatsPerBar = m_pTempoSpinBox->beatsPerBar(); m_pOptions->iBeatDivisor = m_pTempoSpinBox->beatDivisor(); // Save MIDI control non catch-up/hook global option... m_pOptions->bMidiControlSync = qtractorMidiControl::isSync(); // Save the file-system dock-window state... if (m_pFileSystem && m_pFileSystem->isVisible()) { m_pOptions->settings().setValue( "/FileSystem/State", m_pFileSystem->saveState()); } // Save the dock windows state... m_pOptions->settings().setValue("/Layout/DockWindows", saveState()); // Audio master bus auto-connection option... qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine) m_pOptions->bAudioMasterAutoConnect = pAudioEngine->isMasterAutoConnect(); // And the main windows state. bool bSaveVisibility = true; #ifdef CONFIG_NSM if (m_pNsmClient && m_pNsmClient->is_active()) bSaveVisibility = false; #endif m_pOptions->saveWidgetGeometry(m_pConnections, !bSaveVisibility); m_pOptions->saveWidgetGeometry(m_pMixer, !bSaveVisibility); m_pOptions->saveWidgetGeometry(this, bSaveVisibility); } } return bQueryClose; } void qtractorMainForm::closeEvent ( QCloseEvent *pCloseEvent ) { #ifdef CONFIG_NSM // Just hide if under NSM auspice... if (m_pNsmClient && m_pNsmClient->is_active()) { pCloseEvent->ignore(); QMainWindow::hide(); m_pConnections->hide(); m_pMixer->hide(); } else #endif // Let's be sure about that... if (queryClose()) { pCloseEvent->accept(); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QApplication::exit(0); #else QApplication::quit(); #endif } else { pCloseEvent->ignore(); } } // Window drag-n-drop event handlers. void qtractorMainForm::dragEnterEvent ( QDragEnterEvent *pDragEnterEvent ) { // Accept external drags only... if (pDragEnterEvent->source() == nullptr && pDragEnterEvent->mimeData()->hasUrls()) { pDragEnterEvent->accept(); } else { pDragEnterEvent->ignore(); } } void qtractorMainForm::dropEvent ( QDropEvent *pDropEvent ) { #if 0 // Accept externally originated drops only... if (pDropEvent->source()) return; #endif const QMimeData *pMimeData = pDropEvent->mimeData(); if (pMimeData->hasUrls()) { const QString sFilename = pMimeData->urls().first().toLocalFile(); // Close current session and try to load the new one... if (!sFilename.isEmpty() && closeSession()) loadSessionFile(sFilename); } } // Context menu event handler. void qtractorMainForm::contextMenuEvent ( QContextMenuEvent *pContextMenuEvent ) { stabilizeForm(); // Primordial edit menu should be available... m_ui.editMenu->exec(pContextMenuEvent->globalPos()); } // Optional main widget visibility handler. void qtractorMainForm::showEvent ( QShowEvent *pShowEvent ) { QMainWindow::showEvent(pShowEvent); #ifdef CONFIG_NSM if (m_pNsmClient) m_pNsmClient->visible(true); #endif } void qtractorMainForm::hideEvent ( QHideEvent *pHideEvent ) { #ifdef CONFIG_NSM if (m_pNsmClient) m_pNsmClient->visible(false); #endif QMainWindow::hideEvent(pHideEvent); } //------------------------------------------------------------------------- // qtractorMainForm -- Brainless public property accessors. // The global session tracks reference. qtractorTracks *qtractorMainForm::tracks (void) const { return m_pTracks; } // The global file-system reference. qtractorFileSystem *qtractorMainForm::fileSystem (void) const { return m_pFileSystem; } // The global session file(lists) reference. qtractorFiles *qtractorMainForm::files (void) const { return m_pFiles; } // The global session connections reference. qtractorConnections *qtractorMainForm::connections (void) const { return m_pConnections; } // The global session mixer reference. qtractorMixer *qtractorMainForm::mixer (void) const { return m_pMixer; } // The session thumb-view widget accessor. qtractorThumbView *qtractorMainForm::thumbView (void) const { return m_pThumbView; } // The session transport fast-rolling direction accessor. int qtractorMainForm::rolling (void) const { return m_iTransportRolling; } //------------------------------------------------------------------------- // qtractorMainForm -- Session file stuff. // Format the displayable session filename. QString qtractorMainForm::sessionName ( const QString& sFilename ) const { const bool bCompletePath = (m_pOptions && m_pOptions->bCompletePath); QString sSessionName = sFilename; if (sSessionName.isEmpty() && m_pSession) sSessionName = m_pSession->sessionName(); if (sSessionName.isEmpty()) sSessionName = untitledName(); else if (!bCompletePath) sSessionName = QFileInfo(sSessionName).baseName(); return sSessionName; } // Retrieve current untitled sessoin name. QString qtractorMainForm::untitledName (void) const { return tr("Untitled%1").arg(m_iUntitled); } // Clear current filename; prompt for a new one when saving. void qtractorMainForm::clearFilename (void) { m_sFilename.clear(); } // Create a new session file from scratch. bool qtractorMainForm::newSession (void) { // Check if we can do it. if (!closeSession()) return false; #ifdef CONFIG_LV2 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); qtractorLv2PluginType::lv2_open(); QApplication::restoreOverrideCursor(); #endif // We're supposedly clean... m_iDirtyCount = 0; #ifdef CONFIG_NSM if (m_pNsmClient && m_pNsmClient->is_active()) return true; #endif // Check whether we start the new session // based on existing template... if (m_pOptions && m_pOptions->bSessionTemplate) { const QStringList files(m_pOptions->sSessionTemplatePath); const int iFlags = qtractorDocument::Template; return loadSessionFileEx(files, iFlags, false); } // Prepare the session engines... updateSessionPre(); // Ok, increment untitled count. ++m_iUntitled; // Stabilize form. m_sFilename.clear(); // m_iDirtyCount = 0; appendMessages(tr("New session: \"%1\".").arg(sessionName(m_sFilename))); // Give us what we got, right now... updateSessionPost(); return true; } // Open an existing sampler session. bool qtractorMainForm::openSession (void) { if (m_pOptions == nullptr) return false; // Ask for the filename to open... QString sFilename; QString sExt("qtr"); QStringList filters; #ifdef CONFIG_LIBZ filters.append(tr("Session files (*.%1 *.%2 *.%3)") .arg(sExt).arg(qtractorDocument::defaultExt()) .arg(qtractorDocument::archiveExt())); #else filters.append(tr("Session files (*.%1 *.%2)") .arg(sExt).arg(qtractorDocument::defaultExt())); #endif filters.append(tr("Template files (*.%1)") .arg(qtractorDocument::templateExt())); #ifdef CONFIG_LIBZ filters.append(tr("Archive files (*.%1)") .arg(qtractorDocument::archiveExt())); #endif filters.append(tr("All files (*.*)")); sExt = m_pOptions->sSessionExt; // Default session file format... const QString& sTitle = tr("Open Session"); 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, m_pOptions->sSessionDir, sFilter, nullptr, options); #else // Construct open-file session/template dialog... QFileDialog fileDialog(pParentWidget, sTitle, m_pOptions->sSessionDir, sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::ExistingFile); fileDialog.setHistory(m_pOptions->recentFiles); fileDialog.setDefaultSuffix(sExt); #ifdef CONFIG_LIBZ // Special case for archive by default... if (sExt == qtractorDocument::archiveExt()) fileDialog.setNameFilter(filters.last()); #endif // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(m_pOptions->sSessionDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (!fileDialog.exec()) return false; // Have the open-file name... sFilename = fileDialog.selectedFiles().first(); #endif // Have we cancelled? if (sFilename.isEmpty()) return false; // Check if we're going to discard safely the current one... if (!closeSession()) return false; // Load it right away. return loadSessionFile(sFilename); } // Save current sampler session with another name. bool qtractorMainForm::saveSession ( bool bPrompt ) { if (m_pOptions == nullptr) return false; // It must be a session name... if (m_pSession->sessionName().isEmpty() && !editSession()) return false; // Suggest a filename, if there's none... QString sFilename = m_sFilename; if (sFilename.isEmpty()) { sFilename = QFileInfo(m_pSession->sessionDir(), qtractorSession::sanitize(m_pSession->sessionName())).absoluteFilePath(); bPrompt = true; } // Ask for the file to save... if (bPrompt) { // Prompt the guy... QString sExt("qtr"); QStringList filters; #ifdef CONFIG_LIBZ filters.append(tr("Session files (*.%1 *.%2 *.%3)") .arg(sExt).arg(qtractorDocument::defaultExt()) .arg(qtractorDocument::archiveExt())); #else filters.append(tr("Session files (*.%1 *.%2)") .arg(sExt).arg(qtractorDocument::defaultExt())); #endif filters.append(tr("Template files (*.%1)") .arg(qtractorDocument::templateExt())); #ifdef CONFIG_LIBZ filters.append(tr("Archive files (*.%1)") .arg(qtractorDocument::archiveExt())); #endif filters.append(tr("All files (*.*)")); sExt = m_pOptions->sSessionExt; // Default session file format... const QString& sTitle = tr("Save Session"); const QString& sFilter = filters.join(";;"); QWidget *pParentWidget = nullptr; QFileDialog::Options options; if (m_pOptions->bDontUseNativeDialogs) { options |= QFileDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } // Always avoid to store session on extracted directories... sFilename = sessionArchivePath(sFilename); // Try to rename as if a backup is about... sFilename = sessionBackupPath(sFilename); #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0) sFilename = QFileDialog::getSaveFileName(pParentWidget, sTitle, sFilename, sFilter, nullptr, options); #else // Construct save-file session/template dialog... QFileDialog fileDialog(pParentWidget, sTitle, sFilename, sFilter); // Set proper save-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptSave); fileDialog.setFileMode(QFileDialog::AnyFile); fileDialog.setHistory(m_pOptions->recentFiles); fileDialog.setDefaultSuffix(sExt); #ifdef CONFIG_LIBZ // Special case for archive by default... if (sExt == qtractorDocument::archiveExt()) fileDialog.setNameFilter(filters.last()); #endif // Stuff sidebar... QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(m_pOptions->sSessionDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show save-file dialog... if (!fileDialog.exec()) return false; // Have the save-file name... sFilename = fileDialog.selectedFiles().first(); // Check whether we're on the template or archive filter... switch (filters.indexOf(fileDialog.selectedNameFilter())) { case 1: sExt = qtractorDocument::templateExt(); break; case 2: sExt = qtractorDocument::archiveExt(); break; } #endif // Have we cancelled it? if (sFilename.isEmpty() || sFilename.at(0) == '.') return false; // Enforce extension... if (QFileInfo(sFilename).suffix().isEmpty()) { sFilename += '.' + sExt; // Check if already exists... if (sFilename != m_sFilename && QFileInfo(sFilename).exists()) { if (QMessageBox::warning(this, tr("Warning"), tr("The file already exists:\n\n" "\"%1\"\n\n" "Do you want to replace it?") .arg(sFilename), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return false; } } // Reset backup count, anyway... m_iBackupCount = 0; } else // Backup versioning? if (m_pOptions->bSessionBackup) { const QString& sBackupPath = sessionBackupPath(sFilename); if (m_pOptions->iSessionBackupMode > 0) { // Remove from recent files list... const int iIndex = m_pOptions->recentFiles.indexOf(sFilename); if (iIndex >= 0) m_pOptions->recentFiles.removeAt(iIndex); // Also remove from the file system...? if (m_iBackupCount > 0) QFile::remove(sFilename); // Make it a brand new one... sFilename = sBackupPath; ++m_iBackupCount; } else { const QFileInfo fi(sBackupPath); if (QFile(sFilename).rename(sBackupPath)) { appendMessages( tr("Backup session: \"%1\" as \"%2\".") .arg(sFilename).arg(fi.fileName())); } else { appendMessagesError( tr("Could not backup existing session:\n\n" "%1 as %2\n\nSorry.").arg(sFilename).arg(fi.fileName())); } } } // Save it right away. return saveSessionFileEx(sFilename, qtractorDocument::Default, true); } // Edit session properties. bool qtractorMainForm::editSession (void) { // Session Properties... qtractorSessionForm sessionForm(this); const bool bSessionDir #ifdef CONFIG_NSM = (m_pNsmClient == nullptr || !m_pNsmClient->is_active()); #else = true; #endif sessionForm.setSession(m_pSession, bSessionDir); if (!sessionForm.exec()) return false; // If currently playing, we need to do a stop and go... const bool bPlaying = m_pSession->isPlaying(); if (bPlaying) m_pSession->lock(); // Now, express the change as a undoable command... m_pSession->execute( new qtractorSessionEditCommand(m_pSession, sessionForm.properties())); // Restore playback state, if needed... if (bPlaying) m_pSession->unlock(); // Transport status needs an update too... ++m_iTransportUpdate; // Done. return true; } // Close current session. bool qtractorMainForm::closeSession (void) { bool bClose = true; // Are we dirty enough to prompt it? if (bClose && m_iDirtyCount > 0) { switch (QMessageBox::warning(this, tr("Warning"), tr("The current session has been changed:\n\n" "\"%1\"\n\n" "Do you want to save the changes?") .arg(sessionName(m_sFilename)), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel)) { case QMessageBox::Save: bClose = saveSession(false); // Fall thru.... case QMessageBox::Discard: break; default: // Cancel. bClose = false; break; } } // If we may close it, do it! if (bClose) { // Just in case we were in the middle of something... setPlaying(false); // Reset (soft) subject/observer queue. qtractorSubject::resetQueue(); // HACK: Track-list plugins-view must get wiped first... m_pTracks->trackList()->clear(); // Reset all dependables to default. m_pMixer->clear(); m_pFiles->clear(); // Close session engines. m_pSession->close(); m_pSession->clear(); m_pTempoCursor->clear(); // And last but not least. m_pConnections->clear(); m_pTracks->clear(); // Clear (hard) subject/observer queue. qtractorSubject::clearQueue(); // Reset playhead. m_iPlayHead = 0; #ifdef CONFIG_VST3 qtractorVst3Plugin::clearAll(); #endif #ifdef CONFIG_CLAP qtractorClapPlugin::clearAll(); #endif #ifdef CONFIG_LV2 qtractorLv2PluginType::lv2_close(); #endif #ifdef CONFIG_LIBZ // Is it time to cleanup extracted archives? const QStringList& paths = qtractorDocument::extractedArchives(); if (!paths.isEmpty()) { bool bRemoveArchive = true; bool bConfirmArchive = (m_pOptions && m_pOptions->bConfirmArchive); #ifdef CONFIG_NSM if (m_pNsmClient && m_pNsmClient->is_active()) bConfirmArchive = false; #endif if (bConfirmArchive) { const QString& sTitle = tr("Warning"); const QString& sText = tr( "About to remove archive directory:\n\n" "\"%1\"\n\n" "Are you sure?") .arg(paths.join("\",\n\"")); #if 0 bArchiveRemove = (QMessageBox::warning(this, sTitle, sText, QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok); #else QMessageBox mbox(this); mbox.setIcon(QMessageBox::Warning); mbox.setWindowTitle(sTitle); mbox.setText(sText); mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); QCheckBox cbox(tr("Don't ask this again")); cbox.setChecked(false); cbox.blockSignals(true); mbox.addButton(&cbox, QMessageBox::ActionRole); bRemoveArchive = (mbox.exec() == QMessageBox::Ok); if (cbox.isChecked()) m_pOptions->bConfirmArchive = false; #endif } qtractorDocument::clearExtractedArchives(bRemoveArchive); } #endif // Some defaults are due... if (m_pOptions) { m_pSession->setSessionDir(m_pOptions->sSessionDir); m_pSession->setSnapPerBeat( qtractorTimeScale::snapFromIndex(m_pOptions->iSnapPerBeat)); m_pSession->setTempo(m_pOptions->fTempo); m_pSession->setBeatsPerBar(m_pOptions->iBeatsPerBar); m_pSession->setBeatDivisor(m_pOptions->iBeatDivisor); } // We're now clean, for sure. m_iDirtyCount = 0; m_iBackupCount = 0; autoSaveClose(); appendMessages(tr("Session closed.")); } return bClose; } // Load a session from specific file path. bool qtractorMainForm::loadSessionFile ( const QString& sFilename ) { bool bUpdate = true; // We're supposedly clean... m_iDirtyCount = 0; #ifdef CONFIG_NSM if (m_pNsmClient && m_pNsmClient->is_active()) { m_pSession->setClientName(m_pNsmClient->client_id()); bUpdate = false; } #endif const bool bLoadSessionFile = loadSessionFileEx( QStringList(sFilename), qtractorDocument::Default, bUpdate); #ifdef CONFIG_NSM if (m_pNsmClient && m_pNsmClient->is_active()) { m_pSession->setSessionName(m_pNsmClient->display_name()); m_pSession->setSessionDir(m_pNsmClient->path_name()); m_sNsmExt = QFileInfo(sFilename).suffix(); updateDirtyCount(true); ++m_iStabilizeTimer; } #endif return bLoadSessionFile; } bool qtractorMainForm::loadSessionFileEx ( const QStringList& files, int iFlags, bool bUpdate ) { if (files.isEmpty()) return false; const QString& sFilename = files.first(); #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::loadSessionFileEx(\"%s\", %d, %d)", sFilename.toUtf8().constData(), iFlags, int(bUpdate)); #endif // Flag whether we're about to load a template or archive... QFileInfo info(sFilename); const QString& sSuffix = info.suffix(); if (sSuffix == qtractorDocument::templateExt()) iFlags |= qtractorDocument::Template; #ifdef CONFIG_LIBZ if (sSuffix == qtractorDocument::archiveExt()) { iFlags |= qtractorDocument::Archive; // Take special precaution for already // existing non-temporary archive directory... if (!bUpdate) { iFlags |= qtractorDocument::Temporary; } else { info.setFile(info.path() + QDir::separator() + info.completeBaseName()); if (info.exists() && info.isDir()) { bool bRemoveArchive = true; if (m_pOptions && m_pOptions->bConfirmArchive) { const QString& sTitle = tr("Warning"); const QString& sText = tr( "The directory already exists:\n\n" "\"%1\"\n\n" "Do you want to replace it?") .arg(info.filePath()); #if 0 bRemoveArchive (QMessageBox::warning(this, sTitle, sText, QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok); #else QMessageBox mbox(this); mbox.setIcon(QMessageBox::Warning); mbox.setWindowTitle(sTitle); mbox.setText(sText); mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); QCheckBox cbox(tr("Don't ask this again")); cbox.setChecked(false); cbox.blockSignals(true); mbox.addButton(&cbox, QMessageBox::ActionRole); bRemoveArchive = (mbox.exec() == QMessageBox::Ok); if (cbox.isChecked()) m_pOptions->bConfirmArchive = false; #endif // Restarting?... if (!bRemoveArchive) { #ifdef CONFIG_LV2 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); qtractorLv2PluginType::lv2_open(); QApplication::restoreOverrideCursor(); #endif updateSessionPre(); ++m_iUntitled; m_sFilename.clear(); updateSessionPost(); return false; } } } } } #endif // Tell the world we'll take some time... appendMessages(tr("Opening \"%1\"...").arg(sFilename)); #ifdef CONFIG_LV2 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); qtractorLv2PluginType::lv2_open(); QApplication::restoreOverrideCursor(); #endif // Warm-up the session engines... updateSessionPre(); // We'll take some time anyhow... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Read the file... // // Check first whether it's a media file... bool bLoadSessionFileEx = false; if (iFlags == qtractorDocument::Default) bLoadSessionFileEx = m_pTracks->importTracks(files, 0); // Have we succeeded (or not at all)? if (bLoadSessionFileEx) { iFlags |= qtractorDocument::Template; // HACK! } else { // Load regular session file... QDomDocument doc("qtractorSession"); bLoadSessionFileEx = qtractorSession::Document(&doc, m_pSession, m_pFiles) .load(sFilename, qtractorDocument::Flags(iFlags)); } // We're formerly done. QApplication::restoreOverrideCursor(); if (bLoadSessionFileEx) { // Got something loaded... // we're not dirty anymore. if ((iFlags & qtractorDocument::Template) == 0 && bUpdate) { updateRecentFiles(sFilename); // m_iDirtyCount = 0; } // Save as default session directory... if (m_pOptions && bUpdate) { // Do not set (next) default session directory on zip/archives... if ((iFlags & qtractorDocument::Archive) == 0) m_pOptions->sSessionDir = sessionDir(sFilename); // Save also some Audio engine hybrid-properties... qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine) { m_pOptions->iTransportMode = int(pAudioEngine->transportMode()); m_pOptions->bTimebase = pAudioEngine->isTimebase(); updateTransportModePre(); } // Save also some MIDI engine hybrid-properties... qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine) { m_pOptions->iMidiMmcMode = int(pMidiEngine->mmcMode()); m_pOptions->iMidiMmcDevice = pMidiEngine->mmcDevice(); m_pOptions->iMidiSppMode = int(pMidiEngine->sppMode()); m_pOptions->iMidiClockMode = int(pMidiEngine->clockMode()); m_pOptions->bMidiResetAllControllers = pMidiEngine->isResetAllControllers(); } // Save it good... m_pOptions->saveOptions(); } } else { // Something went wrong... appendMessagesError( tr("Session could not be loaded\n" "from \"%1\".\n\n" "Sorry.").arg(sFilename)); } // Stabilize form title... if (iFlags & qtractorDocument::Template) { ++m_iUntitled; m_sFilename.clear(); } else if (bUpdate) { m_sFilename = sFilename; } appendMessages(tr("Open session: \"%1\".").arg(sessionName(sFilename))); // Now we'll try to create (update) the whole GUI session. updateSessionPost(); // Do initial auto-deactivate as late as possible to give tracks/plugins // the chance to perform initial program-change events if (m_pSession->isAutoDeactivate()) { m_pSession->stabilize(); m_pSession->autoDeactivatePlugins(); } return bLoadSessionFileEx; } // Save current session to specific file path. bool qtractorMainForm::saveSessionFileEx ( const QString& sFilename, int iFlags, bool bUpdate ) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::saveSessionFileEx(\"%s\", %d, %d)", sFilename.toUtf8().constData(), iFlags, int(bUpdate)); #endif // Flag whether we're about to save as template or archive... QFileInfo info(sFilename); const QString& sSuffix = info.suffix(); if (sSuffix == qtractorDocument::templateExt()) iFlags |= qtractorDocument::Template; #ifdef CONFIG_LIBZ if (sSuffix == qtractorDocument::archiveExt()) { iFlags |= qtractorDocument::Archive; // Warn when saving an archive session with // same name of an existing directory... info.setFile(info.path() + QDir::separator() + info.completeBaseName()); if (info.exists() && info.isDir() && !qtractorDocument::extractedArchives().contains(info.filePath())) { bool bConfirmArchive = true; if (m_pOptions && m_pOptions->bConfirmArchive) { const QString& sTitle = tr("Warning"); const QString& sText = tr( "A directory with same name already exists:\n\n" "\"%1\"\n\n" "This directory will be replaced, " "erasing all its current data,\n" "when opening and extracting " "this archive in the future.\n\n" "Do you want to continue?") .arg(info.filePath()); #if 0 bConfirmArchive (QMessageBox::warning(this, sTitle, sText, QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok); #else QMessageBox mbox(this); mbox.setIcon(QMessageBox::Warning); mbox.setWindowTitle(sTitle); mbox.setText(sText); mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); QCheckBox cbox(tr("Don't ask this again")); cbox.setChecked(false); cbox.blockSignals(true); mbox.addButton(&cbox, QMessageBox::ActionRole); bConfirmArchive = (mbox.exec() == QMessageBox::Ok); if (cbox.isChecked()) m_pOptions->bConfirmArchive = false; #endif } // Aborting?... if (!bConfirmArchive) return false; } } // Warn when saving some type of sessions // into an extracted archive directory... if ((iFlags & qtractorDocument::Temporary) == 0) { info.setFile(info.path()); if (info.exists() && info.isDir() && qtractorDocument::extractedArchives().contains(info.filePath())) { bool bConfirmArchive = true; if (m_pOptions && m_pOptions->bConfirmArchive) { const QString& sTitle = tr("Warning"); const QString& sText = tr( "The directory is an extracted archive:\n\n" "\"%1\"\n\n" "This directory will be removed,\n" "erased from all its current data,\n" "when closing this session.\n\n" "Do you want to continue?") .arg(info.filePath()); #if 0 bConfirmArchive (QMessageBox::warning(this, sTitle, sText, QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok); #else QMessageBox mbox(this); mbox.setIcon(QMessageBox::Warning); mbox.setWindowTitle(sTitle); mbox.setText(sText); mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); QCheckBox cbox(tr("Don't ask this again")); cbox.setChecked(false); cbox.blockSignals(true); mbox.addButton(&cbox, QMessageBox::ActionRole); bConfirmArchive = (mbox.exec() == QMessageBox::Ok); if (cbox.isChecked()) m_pOptions->bConfirmArchive = false; #endif } // Aborting?... if (!bConfirmArchive) return false; } } #endif // Tell the world we'll take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); appendMessages(tr("Saving \"%1\"...").arg(sFilename)); // Trap dirty clips (only MIDI at this time...) const bool bTemporary = (iFlags & qtractorDocument::Temporary); typedef QHash MidiClipFilenames; MidiClipFilenames midiClips; for (qtractorTrack *pTrack = m_pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { // Only MIDI track/clips... if (pTrack->trackType() != qtractorTrack::Midi) continue; for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { // Are any dirty changes pending commit? if (pClip->isDirty()) { qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip) { if (bTemporary) midiClips.insert(pMidiClip, pMidiClip->filename()); // Have a new filename revision... const QString& sFilename = pMidiClip->createFilePathRevision(bTemporary); // Save/replace the clip track... pMidiClip->saveCopyFile(sFilename, !bTemporary); } } } } // Soft-house-keeping... m_pSession->files()->cleanup(false); // Write the file... QDomDocument doc("qtractorSession"); bool bResult = qtractorSession::Document(&doc, m_pSession, m_pFiles) .save(sFilename, qtractorDocument::Flags(iFlags)); #ifdef CONFIG_LIBZ if ((iFlags & qtractorDocument::Archive) == 0 && bUpdate) qtractorDocument::clearExtractedArchives(); #endif // Restore old clip filenames, saved previously... MidiClipFilenames::ConstIterator iter = midiClips.constBegin(); const MidiClipFilenames::ConstIterator& iter_end = midiClips.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorMidiClip *pMidiClip = iter.key(); const QString& sFilename = iter.value(); pMidiClip->setFilenameEx(sFilename, false); m_pFiles->removeMidiFile(sFilename, false); } // We're formerly done. QApplication::restoreOverrideCursor(); if (bResult) { // Got something saved... // we're not dirty anymore. if ((iFlags & qtractorDocument::Template) == 0 && bUpdate) { updateRecentFiles(sFilename); autoSaveReset(); m_iDirtyCount = 0; } // Save some default session properties... if (m_pOptions && bUpdate) { qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine) { m_pOptions->iTransportMode = int(pAudioEngine->transportMode()); m_pOptions->bTimebase = pAudioEngine->isTimebase(); } qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine) { m_pOptions->iMidiMmcMode = int(pMidiEngine->mmcMode()); m_pOptions->iMidiMmcDevice = int(pMidiEngine->mmcDevice()); m_pOptions->iMidiSppMode = int(pMidiEngine->sppMode()); m_pOptions->iMidiClockMode = int(pMidiEngine->clockMode()); m_pOptions->bMidiResetAllControllers = pMidiEngine->isResetAllControllers(); } // Do not set (next) default session directory on zip/archives... if ((iFlags & qtractorDocument::Archive) == 0) m_pOptions->sSessionDir = sessionDir(sFilename); // Sync m_pOptions->saveOptions(); } } else { // Something went wrong... appendMessagesError( tr("Session could not be saved\n" "to \"%1\".\n\n" "Sorry.").arg(sFilename)); } // Stabilize form title... if ((iFlags & qtractorDocument::Template) == 0 && bUpdate) m_sFilename = sFilename; appendMessages(tr("Save session: \"%1\".").arg(sessionName(sFilename))); // Show static results... ++m_iStabilizeTimer; return bResult; } QString qtractorMainForm::sessionBackupPath ( const QString& sFilename ) const { QFileInfo fi(sFilename); QString sBackupName = fi.completeBaseName(); if (fi.isDir()) fi.setFile(QDir(fi.filePath()), sBackupName); if (fi.exists()) { int iBackupNo = 0; QRegularExpression rxBackupNo("\\.([0-9]+)$"); QRegularExpressionMatch match = rxBackupNo.match(sBackupName); if (match.hasMatch()) { iBackupNo = match.captured(1).toInt(); sBackupName.remove(rxBackupNo); } sBackupName += ".%1"; const QString& sExt = fi.suffix(); if (!sExt.isEmpty()) sBackupName += '.' + sExt; const QDir dir = fi.absoluteDir(); fi.setFile(dir, sBackupName.arg(++iBackupNo)); while (fi.exists()) fi.setFile(dir, sBackupName.arg(++iBackupNo)); } return fi.absoluteFilePath(); } // Whenever on some Save As... situation: // better check whether the target directory // is one of the extracted archives/zip ones... // QString qtractorMainForm::sessionArchivePath ( const QString& sFilename ) const { QFileInfo fi(sFilename); #ifdef CONFIG_LIBZ const QStringList& paths = qtractorDocument::extractedArchives(); if (!paths.isEmpty()) { QStringListIterator iter(paths); while (iter.hasNext()) { const QString& sPath = iter.next(); if (sPath == fi.absolutePath()) { const QString& sDir = QFileInfo(sPath).absolutePath(); fi.setFile(sDir, fi.fileName()); } } } #endif return fi.absoluteFilePath(); } //------------------------------------------------------------------------- // qtractorMainForm -- NSM client slots. void qtractorMainForm::openNsmSession (void) { #ifdef CONFIG_NSM if (m_pNsmClient == nullptr) return; if (!m_pNsmClient->is_active()) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::openNsmSession()"); #endif openNsmSessionEx(true); #endif // CONFIG_NSM } void qtractorMainForm::openNsmSessionEx ( bool bOpenReply ) { #ifdef CONFIG_NSM // We're supposedly clean... m_iDirtyCount = 0; m_bNsmDirty = false; m_sNsmFile.clear(); bool bLoaded = false; if (closeSession()) { const QString& path_name = m_pNsmClient->path_name(); const QString& display_name = m_pNsmClient->display_name(); const QString& client_id = m_pNsmClient->client_id(); const QDir dir(path_name); if (!dir.exists()) { dir.mkpath(path_name); } else { QStringList filters; const QString& prefix_dot = display_name + "*."; filters << prefix_dot + qtractorDocument::defaultExt(); filters << prefix_dot + qtractorDocument::templateExt(); filters << prefix_dot + qtractorDocument::archiveExt(); filters << prefix_dot + "qtr"; const QStringList& files = dir.entryList(filters, QDir::Files | QDir::NoSymLinks | QDir::Readable, QDir::Time); if (!files.isEmpty()) m_sNsmExt = QFileInfo(files.first()).suffix(); } m_pSession->setClientName(client_id); m_pSession->setSessionName(display_name); m_pSession->setSessionDir(path_name); if (bOpenReply) m_pNsmClient->open_reply(qtractorNsmClient::ERR_OK); QFileInfo fi(path_name, "session." + m_sNsmExt); if (!fi.exists()) fi.setFile(path_name, display_name + '.' + m_sNsmExt); const QString& sFilename = fi.absoluteFilePath(); if (fi.exists()) { const int iFlags = qtractorDocument::Default; bLoaded = loadSessionFileEx(QStringList(sFilename), iFlags, false); if (bLoaded) m_sNsmFile = sFilename; } else { updateSessionPre(); #ifdef CONFIG_LV2 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); qtractorLv2PluginType::lv2_open(); QApplication::restoreOverrideCursor(); #endif appendMessages(tr("New session: \"%1\".") .arg(sessionName(sFilename))); updateSessionPost(); bLoaded = true; } } if (bLoaded) m_pNsmClient->dirty(false); m_pNsmClient->visible(QMainWindow::isVisible()); #endif // CONFIG_NSM } void qtractorMainForm::saveNsmSession (void) { #ifdef CONFIG_NSM if (m_pNsmClient == nullptr) return; if (!m_pNsmClient->is_active()) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::saveNsmSession()"); #endif saveNsmSessionEx(true); #endif // CONFIG_NSM } void qtractorMainForm::saveNsmSessionEx ( bool bSaveReply ) { #ifdef CONFIG_NSM bool bSaved = true; if (!m_sNsmFile.isEmpty()) { const QFileInfo fi(m_sNsmFile); if (fi.exists() && fi.suffix() != m_sNsmExt) { QFile(m_sNsmFile).remove(); m_sNsmFile.clear(); } } if (!bSaveReply || m_bNsmDirty) { m_iDirtyCount = 0; m_bNsmDirty = false; const QString& path_name = m_pNsmClient->path_name(); const QString& display_name = m_pNsmClient->display_name(); // const QString& client_id = m_pNsmClient->client_id(); // m_pSession->setClientName(client_id); m_pSession->setSessionName(display_name); m_pSession->setSessionDir(path_name); // const QFileInfo fi(path_name, display_name + '.' + m_sNsmExt); const QFileInfo fi(path_name, "session." + m_sNsmExt); const QString& sFilename = fi.absoluteFilePath(); const int iFlags = qtractorDocument::SymLink; bSaved = saveSessionFileEx(sFilename, iFlags, false); if (bSaved) m_sNsmFile = sFilename; } if (bSaveReply) { m_pNsmClient->save_reply(bSaved ? qtractorNsmClient::ERR_OK : qtractorNsmClient::ERR_GENERAL); } if (bSaved) m_pNsmClient->dirty(false); #endif // CONFIG_NSM } void qtractorMainForm::showNsmSession (void) { #ifdef CONFIG_NSM if (m_pNsmClient == nullptr) return; if (!m_pNsmClient->is_active()) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::showNsmSession()"); #endif QMainWindow::show(); QMainWindow::raise(); QMainWindow::activateWindow(); #endif // CONFIG_NSM } void qtractorMainForm::hideNsmSession (void) { #ifdef CONFIG_NSM if (m_pNsmClient == nullptr) return; if (!m_pNsmClient->is_active()) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::hideNsmSession()"); #endif QMainWindow::hide(); m_pConnections->hide(); m_pMixer->hide(); #endif // CONFIG_NSM } //------------------------------------------------------------------------- // qtractorMainForm -- auot-save executive methods. // Reset auto-save stats. void qtractorMainForm::autoSaveReset (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::autoSaveReset()"); #endif m_iAutoSaveTimer = 0; if (m_pOptions->bAutoSaveEnabled) m_iAutoSavePeriod = 60000 * m_pOptions->iAutoSavePeriod; else m_iAutoSavePeriod = 0; } // Execute auto-save routine... void qtractorMainForm::autoSaveSession (void) { #ifdef CONFIG_NSM if (m_pNsmClient && m_pNsmClient->is_active()) return; #endif QString sAutoSaveDir = m_pSession->sessionDir(); if (sAutoSaveDir.isEmpty()) sAutoSaveDir = m_pOptions->sSessionDir; if (sAutoSaveDir.isEmpty() || !QFileInfo(sAutoSaveDir).isWritable()) sAutoSaveDir = QDir::tempPath(); QString sAutoSaveName = m_pSession->sessionName(); if (sAutoSaveName.isEmpty()) sAutoSaveName = untitledName(); const QString& sAutoSavePathname = QFileInfo(sAutoSaveDir, qtractorSession::sanitize(sAutoSaveName)).filePath() + ".auto-save." + qtractorDocument::defaultExt(); const QString& sOldAutoSavePathname = m_pOptions->sAutoSavePathname; if (!sOldAutoSavePathname.isEmpty() && sOldAutoSavePathname != sAutoSavePathname && QFileInfo(sOldAutoSavePathname).exists()) QFile(sOldAutoSavePathname).remove(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::autoSaveSession(\"%s\")", sAutoSavePathname.toUtf8().constData()); #endif const int iFlags = qtractorDocument::Default | qtractorDocument::Temporary; if (saveSessionFileEx(sAutoSavePathname, iFlags, false)) { m_pOptions->sAutoSavePathname = sAutoSavePathname; m_pOptions->sAutoSaveFilename = m_sFilename; m_pOptions->saveOptions(); } } // Auto-save/crash-recovery setup... bool qtractorMainForm::autoSaveOpen (void) { #ifdef CONFIG_NSM if (m_pNsmClient && m_pNsmClient->is_active()) return false; #endif const QString& sAutoSavePathname = m_pOptions->sAutoSavePathname; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::autoSaveOpen(\"%s\")", sAutoSavePathname.toUtf8().constData()); #endif if (!sAutoSavePathname.isEmpty() && QFileInfo(sAutoSavePathname).exists()) { if (QMessageBox::warning(this, tr("Warning"), tr("Oops!\n\n" "Looks like it crashed or did not close " "properly last time it was run... however, " "an auto-saved session file exists:\n\n" "\"%1\"\n\n" "Do you want to crash-recover from it?") .arg(sAutoSavePathname), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) { const QStringList files(sAutoSavePathname); const int iFlags = qtractorDocument::Default; if (loadSessionFileEx(files, iFlags, false)) { m_sFilename = m_pOptions->sAutoSaveFilename; ++m_iDirtyCount; return true; } } } return false; } // Auto-save/crash-recovery cleanup. void qtractorMainForm::autoSaveClose (void) { #ifdef CONFIG_NSM if (m_pNsmClient && m_pNsmClient->is_active()) return; #endif const QString& sAutoSavePathname = m_pOptions->sAutoSavePathname; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::autoSaveClose(\"%s\")", sAutoSavePathname.toUtf8().constData()); #endif if (!sAutoSavePathname.isEmpty() && QFileInfo(sAutoSavePathname).exists()) QFile(sAutoSavePathname).remove(); m_pOptions->sAutoSavePathname.clear(); m_pOptions->sAutoSaveFilename.clear(); autoSaveReset(); } // Execute auto-save as soon as possible (quasi-immediately please). void qtractorMainForm::autoSaveAsap (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::autoSaveAsap()"); #endif if (m_pOptions->bAutoSaveEnabled) m_iAutoSaveTimer = m_iAutoSavePeriod; } // Make it sane a session directory... QString qtractorMainForm::sessionDir ( const QString& sFilename ) const { QFileInfo fi(sFilename); const QDir& dir = fi.dir(); if (fi.completeBaseName() == dir.dirName()) fi.setFile(dir.absolutePath()); return fi.absolutePath(); } //------------------------------------------------------------------------- // qtractorMainForm -- File Action slots. // Create a new sampler session. void qtractorMainForm::fileNew (void) { #ifdef CONFIG_NSM if (m_pNsmClient && m_pNsmClient->is_active()) { // openNsmSessionEx(false); return; } #endif // Of course we'll start clean new. newSession(); } // Open an existing sampler session. void qtractorMainForm::fileOpen (void) { // Open it right away. openSession(); } // Open a recent file session. void qtractorMainForm::fileOpenRecent (void) { // Retrive filename index from action data... QAction *pAction = qobject_cast (sender()); if (pAction && m_pOptions) { int iIndex = pAction->data().toInt(); if (iIndex >= 0 && iIndex < m_pOptions->recentFiles.count()) { QString sFilename = m_pOptions->recentFiles[iIndex]; // Check if we can safely close the current session... if (!sFilename.isEmpty() && closeSession()) loadSessionFile(sFilename); } } } // Save current sampler session. void qtractorMainForm::fileSave (void) { #ifdef CONFIG_NSM if (m_pNsmClient && m_pNsmClient->is_active()) { saveNsmSessionEx(false); return; } #endif // Save it right away. saveSession(false); } // Save current sampler session with another name. void qtractorMainForm::fileSaveAs (void) { #ifdef CONFIG_NSM if (m_pNsmClient && m_pNsmClient->is_active()) { // saveNsmSessionEx(false); return; } #endif // Save it right away, maybe with another name. saveSession(true); } // Edit session properties. void qtractorMainForm::fileProperties (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::fileProperties()"); #endif // Session Properties... editSession(); } // Exit application program. void qtractorMainForm::fileExit (void) { // Go for close the whole thing. close(); } //------------------------------------------------------------------------- // qtractorMainForm -- Edit Action slots. // Undo last action. void qtractorMainForm::editUndo (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editUndo()"); #endif (m_pSession->commands())->undo(); } // Redo last action. void qtractorMainForm::editRedo (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editRedo()"); #endif (m_pSession->commands())->redo(); } // Cut selection to clipboard. void qtractorMainForm::editCut (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editCut()"); #endif // Cut from files... if (m_pFiles && m_pFiles->hasFocus()) m_pFiles->cutItemSlot(); else // Cut selection... if (m_pTracks) m_pTracks->cutClipboard(); } // Copy selection to clipboard. void qtractorMainForm::editCopy (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editCopy()"); #endif // Copy from files... if (m_pFiles && m_pFiles->hasFocus()) m_pFiles->copyItemSlot(); else // Copy selection... if (m_pTracks) m_pTracks->copyClipboard(); ++m_iStabilizeTimer; } // Paste clipboard contents. void qtractorMainForm::editPaste (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editPaste()"); #endif // Paste to files... if (m_pFiles && m_pFiles->hasFocus()) m_pFiles->pasteItemSlot(); else // Paste selection... if (m_pTracks) m_pTracks->pasteClipboard(); } // Paste/repeat clipboard contents. void qtractorMainForm::editPasteRepeat (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editPasteRepeat()"); #endif // Paste/repeat selection... if (m_pTracks) m_pTracks->pasteRepeatClipboard(); } // Delete selection. void qtractorMainForm::editDelete (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editDelete()"); #endif // Delete from files... if (m_pFiles && m_pFiles->hasFocus()) m_pFiles->removeItemSlot(); else // Delete selection... if (m_pTracks) m_pTracks->deleteSelect(); } // Set selection to whole clip mode. void qtractorMainForm::editSelectModeClip (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editSelectModeClip()"); #endif // Select clip mode... const qtractorTrackView::SelectMode selectMode = qtractorTrackView::SelectClip; if (m_pTracks) { qtractorTrackView *pTrackView = m_pTracks->trackView(); if (pTrackView->selectMode() == selectMode && !pTrackView->isCurveEdit()) { m_ui.editSelectModeCurveAction->trigger(); } else { pTrackView->setSelectMode(selectMode); pTrackView->setCurveEdit(false); } } if (m_pOptions) m_pOptions->iTrackViewSelectMode = int(selectMode); ++m_iStabilizeTimer; } // Set selection to range mode. void qtractorMainForm::editSelectModeRange (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editSelectModeRange()"); #endif // Select range mode... const qtractorTrackView::SelectMode selectMode = qtractorTrackView::SelectRange; if (m_pTracks) { qtractorTrackView *pTrackView = m_pTracks->trackView(); if (pTrackView->selectMode() == selectMode && !pTrackView->isCurveEdit()) { m_ui.editSelectModeCurveAction->trigger(); } else { pTrackView->setSelectMode(selectMode); pTrackView->setCurveEdit(false); } } if (m_pOptions) m_pOptions->iTrackViewSelectMode = int(selectMode); ++m_iStabilizeTimer; } // Set selection to rectangularmode. void qtractorMainForm::editSelectModeRect (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editSelectModeRect()"); #endif // Select rectangle mode... const qtractorTrackView::SelectMode selectMode = qtractorTrackView::SelectRect; if (m_pTracks && m_pOptions) { qtractorTrackView *pTrackView = m_pTracks->trackView(); if (pTrackView->selectMode() == selectMode && !pTrackView->isCurveEdit()) { m_ui.editSelectModeCurveAction->trigger(); } else { pTrackView->setSelectMode(selectMode); pTrackView->setCurveEdit(false); } } if (m_pOptions) m_pOptions->iTrackViewSelectMode = int(selectMode); ++m_iStabilizeTimer; } // Special automation curve node edit mode. void qtractorMainForm::editSelectModeCurve (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editSelectModeCurve()"); #endif if (m_pTracks) { qtractorTrackView *pTrackView = m_pTracks->trackView(); if (pTrackView->isCurveEdit()) { switch (pTrackView->selectMode()) { case qtractorTrackView::SelectRect: m_ui.editSelectModeRectAction->trigger(); break; case qtractorTrackView::SelectRange: m_ui.editSelectModeRangeAction->trigger(); break; case qtractorTrackView::SelectClip: default: m_ui.editSelectModeClipAction->trigger(); break; } } else { // Select curve mode... pTrackView->setCurveEdit(true); } } ++m_iStabilizeTimer; } // Mark all as selected. void qtractorMainForm::editSelectAll (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editSelectAll()"); #endif // Select all... if (m_pTracks) m_pTracks->selectAll(); ++m_iStabilizeTimer; } // Mark all as unselected. void qtractorMainForm::editSelectNone (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editSelectNone()"); #endif // Select nothing... if (m_pTracks) m_pTracks->selectNone(); ++m_iStabilizeTimer; } // Invert current selection. void qtractorMainForm::editSelectInvert (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editSelectInvert()"); #endif // Invert selection... if (m_pTracks) m_pTracks->selectInvert(); ++m_iStabilizeTimer; } // Mark track as selected. void qtractorMainForm::editSelectTrack (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editSelectTrack()"); #endif // Select current track... if (m_pTracks) m_pTracks->selectCurrentTrack(); ++m_iStabilizeTimer; } // Mark track-range as selected. void qtractorMainForm::editSelectTrackRange (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editSelectTrackRange()"); #endif // Select track-range... if (m_pTracks) m_pTracks->selectCurrentTrackRange(); ++m_iStabilizeTimer; } // Mark range as selected. void qtractorMainForm::editSelectRange (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editSelectRange()"); #endif // Select edit-range... if (m_pTracks) m_pTracks->selectEditRange(); ++m_iStabilizeTimer; } // Insert range as selected. void qtractorMainForm::editInsertRange (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editInsertRange()"); #endif // Insert edit-range... if (m_pTracks) m_pTracks->insertEditRange(); ++m_iStabilizeTimer; } // Insert track-range as selected. void qtractorMainForm::editInsertTrackRange (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editInsertTrackRange()"); #endif // Select track-range... if (m_pTracks) m_pTracks->insertEditRange(m_pTracks->currentTrack()); ++m_iStabilizeTimer; } // Remove range as selected. void qtractorMainForm::editRemoveRange (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editRemoveRange()"); #endif // Insert edit-range... if (m_pTracks) m_pTracks->removeEditRange(); ++m_iStabilizeTimer; } // Remove track-range as selected. void qtractorMainForm::editRemoveTrackRange (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editRemoveTrackRange()"); #endif // Select track-range... if (m_pTracks) m_pTracks->removeEditRange(m_pTracks->currentTrack()); ++m_iStabilizeTimer; } // Split (clip) selection... void qtractorMainForm::editSplit (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::editSplit()"); #endif // Split selection... if (m_pTracks) m_pTracks->splitSelect(); ++m_iStabilizeTimer; } //------------------------------------------------------------------------- // qtractorMainForm -- Track Action slots. // Add a new track to session. void qtractorMainForm::trackAdd (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackAdd()"); #endif // Add Track... if (m_pTracks) m_pTracks->addTrack(); } // Remove current track from session. void qtractorMainForm::trackRemove (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackRemove()"); #endif // Remove Track... if (m_pTracks) m_pTracks->removeTrack(); } // Duplicate/copy track on session. void qtractorMainForm::trackDuplicate (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackDuplicate()"); #endif // Track Properties... if (m_pTracks) m_pTracks->copyTrack(); } // Edit track properties on session. void qtractorMainForm::trackProperties (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackProperties()"); #endif // Track Properties... if (m_pTracks) m_pTracks->editTrack(); } // Show current track input bus connections. void qtractorMainForm::trackInputs (void) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; if (pTrack->inputBus() == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackInputs()"); #endif // Make sure session is activated?... //checkRestartSession(); if (m_pConnections) m_pConnections->showBus(pTrack->inputBus(), qtractorBus::Input); } // Show current track output bus connections. void qtractorMainForm::trackOutputs (void) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; if (pTrack->outputBus() == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackOutputs()"); #endif // Make sure session is activated?... //checkRestartSession(); if (m_pConnections) m_pConnections->showBus(pTrack->outputBus(), qtractorBus::Output); } // Arm current track for recording. void qtractorMainForm::trackStateRecord ( bool bOn ) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackStateRecord(%d)", int(bOn)); #endif m_pSession->execute( new qtractorTrackStateCommand(pTrack, qtractorTrack::Record, bOn)); } // Mute current track. void qtractorMainForm::trackStateMute ( bool bOn ) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackStateMute(%d)", int(bOn)); #endif m_pSession->execute( new qtractorTrackStateCommand(pTrack, qtractorTrack::Mute, bOn)); } // Solo current track. void qtractorMainForm::trackStateSolo ( bool bOn ) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackStateSolo(%d)", int(bOn)); #endif m_pSession->execute( new qtractorTrackStateCommand(pTrack, qtractorTrack::Solo, bOn)); } // Monitor current track. void qtractorMainForm::trackStateMonitor ( bool bOn ) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackStateMonitor(%d)", int(bOn)); #endif m_pSession->execute( new qtractorTrackMonitorCommand(pTrack, bOn)); } // Make current the first track on list. void qtractorMainForm::trackNavigateFirst (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackNavigateFirst()"); #endif if (m_pTracks) m_pTracks->trackList()->setCurrentTrackRow(0); } // Make current the previous track on list. void qtractorMainForm::trackNavigatePrev (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackNavigatePrev()"); #endif if (m_pTracks) { qtractorTrackList *pTrackList = m_pTracks->trackList(); const int iTrackCount = pTrackList->trackRowCount(); int iTrack = pTrackList->currentTrackRow() - 1; if (iTrack < 0) iTrack = iTrackCount - 1; if (iTrack >= 0 && iTrackCount >= iTrack) pTrackList->setCurrentTrackRow(iTrack); } } // Make current the next track on list. void qtractorMainForm::trackNavigateNext (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackNavigateNext()"); #endif if (m_pTracks) { qtractorTrackList *pTrackList = m_pTracks->trackList(); const int iTrackCount = pTrackList->trackRowCount(); int iTrack = pTrackList->currentTrackRow() + 1; if (iTrack >= iTrackCount) iTrack = 0; if (iTrack >= 0 && iTrackCount >= iTrack) pTrackList->setCurrentTrackRow(iTrack); } } // Make current the last track on list. void qtractorMainForm::trackNavigateLast (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackNavigateLast()"); #endif if (m_pTracks) { qtractorTrackList *pTrackList = m_pTracks->trackList(); const int iTrack = pTrackList->trackRowCount() - 1; if (iTrack >= 0) pTrackList->setCurrentTrackRow(iTrack); } } // Make none current track on list. void qtractorMainForm::trackNavigateNone (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackNavigateNone()"); #endif if (m_pTracks) m_pTracks->trackList()->setCurrentTrackRow(-1); } // Move current track to top of list. void qtractorMainForm::trackMoveTop (void) { if (m_pSession == nullptr) return; qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackMoveTop()"); #endif m_pSession->execute( new qtractorMoveTrackCommand(pTrack, m_pSession->tracks().first())); } // Move current track up towards the top of list. void qtractorMainForm::trackMoveUp (void) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; qtractorTrack *pNextTrack = pTrack->prev(); if (pNextTrack == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackMoveUp()"); #endif m_pSession->execute( new qtractorMoveTrackCommand(pTrack, pNextTrack)); } // Move current track down towards the bottom of list void qtractorMainForm::trackMoveDown (void) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; qtractorTrack *pNextTrack = pTrack->next(); if (pNextTrack == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackMoveDown()"); #endif m_pSession->execute( new qtractorMoveTrackCommand(pTrack, pNextTrack->next())); } // Move current track to bottom of list. void qtractorMainForm::trackMoveBottom (void) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackMoveBottom()"); #endif m_pSession->execute( new qtractorMoveTrackCommand(pTrack, nullptr)); } // Increase current track height. void qtractorMainForm::trackHeightIncrease (void) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackHeightIncrease()"); #endif const int iZoomHeight = (150 * pTrack->zoomHeight()) / 100; m_pSession->execute( new qtractorResizeTrackCommand(pTrack, iZoomHeight)); } // Decreate current track height. void qtractorMainForm::trackHeightDecrease (void) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackHeightDecrease()"); #endif const int iZoomHeight = (75 * pTrack->zoomHeight()) / 100; m_pSession->execute( new qtractorResizeTrackCommand(pTrack, iZoomHeight)); } // Minimize current track height. void qtractorMainForm::trackHeightMinimize (void) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackHeightMinimize()"); #endif m_pSession->execute( new qtractorResizeTrackCommand(pTrack, pTrack->minimizeZoomHeight())); } // Reset current track height. void qtractorMainForm::trackHeightReset (void) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackHeightReset()"); #endif const int iVerticalZoom = m_pSession->verticalZoom(); const int iZoomHeight = (iVerticalZoom * qtractorTrack::HeightBase) / 100; m_pSession->execute( new qtractorResizeTrackCommand(pTrack, iZoomHeight)); } // Auto-monitor current track. void qtractorMainForm::trackAutoMonitor ( bool bOn ) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackAutoMonitor(%d)", int(bOn)); #endif qtractorTrack *pTrack = nullptr; if (bOn && m_pTracks) pTrack = m_pTracks->currentTrack(); m_pSession->setCurrentTrack(pTrack); } // Auto-deactivate plugins not producing sound. void qtractorMainForm::trackAutoDeactivate ( bool bOn ) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackAutoDeactivate(%d)", int(bOn)); #endif m_pSession->setAutoDeactivate(bOn); } // Import some tracks from Audio file. void qtractorMainForm::trackImportAudio (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackImportAudio()"); #endif // Import Audio files into tracks... if (m_pTracks) { const unsigned long iClipStart = m_pSession->editHead(); qtractorTrack *pTrack = m_pTracks->currentTrack(); m_pTracks->addAudioTracks( m_pFiles->audioListView()->openFileNames(), iClipStart, 0, 0, pTrack); m_pTracks->trackView()->ensureVisibleFrame(iClipStart); } } // Import some tracks from MIDI file. void qtractorMainForm::trackImportMidi (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackImportMidi()"); #endif // Import MIDI files into tracks... if (m_pTracks) { const unsigned long iClipStart = m_pSession->editHead(); qtractorTrack *pTrack = m_pTracks->currentTrack(); m_pTracks->addMidiTracks( m_pFiles->midiListView()->openFileNames(), iClipStart, 0, 0, pTrack); m_pTracks->trackView()->ensureVisibleFrame(iClipStart); } } // Export tracks to audio file. void qtractorMainForm::trackExportAudio (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackExportAudio()"); #endif // Disable auto-deactivation during export. Disabling here has a nice side // effect: While the user makes the selections in the export dialog, the // reactivated plugins have time to finish sounds which were active when // plugins were deactivated and those remnants don't make it into exported // file. const bool bAutoDeactivate = m_pSession->isAutoDeactivate(); m_pSession->setAutoDeactivate(false); qtractorExportTrackForm exportForm(this); exportForm.setExportType(qtractorTrack::Audio); exportForm.exec(); m_pSession->setAutoDeactivate(bAutoDeactivate); } // Export tracks to MIDI file. void qtractorMainForm::trackExportMidi (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackExportMidi()"); #endif qtractorExportTrackForm exportForm(this); exportForm.setExportType(qtractorTrack::Midi); exportForm.exec(); } // Track automation curve selection menu. Q_DECLARE_METATYPE(qtractorMidiControlObserver *); void qtractorMainForm::trackCurveSelect ( QAction *pAction, bool bOn ) { qtractorSubject *pSubject = nullptr; qtractorCurveList *pCurveList = nullptr; qtractorMidiControlObserver *pMidiObserver = pAction->data().value (); if (pMidiObserver) { pCurveList = pMidiObserver->curveList(); pSubject = pMidiObserver->subject(); } else { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack) pCurveList = pTrack->curveList(); } if (pCurveList == nullptr) return; qtractorCurve *pCurve = nullptr; if (bOn && pSubject) { pCurve = pSubject->curve(); if (pCurve == nullptr) { qtractorCurve::Mode mode = qtractorCurve::Hold; if (m_pOptions && pSubject->isDecimal()) mode = qtractorCurve::Mode(m_pOptions->iCurveMode); pCurve = new qtractorCurve(pCurveList, pSubject, mode); pCurve->setLogarithmic(pMidiObserver->isLogarithmic()); if (m_pOptions && !m_pOptions->sCurveColor.isEmpty()) { #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) pCurve->setColor(QColor::fromString(m_pOptions->sCurveColor)); #else pCurve->setColor(QColor(m_pOptions->sCurveColor)); #endif } } } #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackCurveSelect(%p)", pCurve); #endif m_pSession->execute(new qtractorCurveSelectCommand(pCurveList, pCurve)); } void qtractorMainForm::trackCurveSelect ( bool bOn ) { QAction *pAction = qobject_cast (sender()); if (pAction) trackCurveSelect(pAction, bOn); } void qtractorMainForm::trackCurveMode ( QAction *pAction ) { const int iMode = pAction->data().toInt(); if (iMode < 0) return; qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; qtractorCurve *pCurrentCurve = pTrack->currentCurve(); if (pCurrentCurve == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackCurveMode(%d)", iMode); #endif // Save as future default... if (m_pOptions) m_pOptions->iCurveMode = iMode; const qtractorCurve::Mode mode = qtractorCurve::Mode(iMode); m_pSession->execute(new qtractorCurveModeCommand(pCurrentCurve, mode)); } // Track automation curve lock toggle. void qtractorMainForm::trackCurveLocked ( bool bOn ) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; qtractorCurve *pCurrentCurve = pTrack->currentCurve(); if (pCurrentCurve == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackCurveLocked(%d)", int(bOn)); #endif pCurrentCurve->setLocked(bOn); m_pTracks->updateTrackView(); updateDirtyCount(true); ++m_iStabilizeTimer; } // Track automation curve playback toggle. void qtractorMainForm::trackCurveProcess ( bool bOn ) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; qtractorCurve *pCurrentCurve = pTrack->currentCurve(); if (pCurrentCurve == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackCurveProcess(%d)", int(bOn)); #endif m_pSession->execute(new qtractorCurveProcessCommand(pCurrentCurve, bOn)); } // Track automation curve record toggle. void qtractorMainForm::trackCurveCapture ( bool bOn ) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; qtractorCurve *pCurrentCurve = pTrack->currentCurve(); if (pCurrentCurve == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackCurveCapture(%d)", int(bOn)); #endif m_pSession->execute(new qtractorCurveCaptureCommand(pCurrentCurve, bOn)); } // Track automation curve logarithmic toggle. void qtractorMainForm::trackCurveLogarithmic ( bool bOn ) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; qtractorCurve *pCurrentCurve = pTrack->currentCurve(); if (pCurrentCurve == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackCurveLogarithmic(%d)", int(bOn)); #endif m_pSession->execute(new qtractorCurveLogarithmicCommand(pCurrentCurve, bOn)); } // Track automation curve color picker. void qtractorMainForm::trackCurveColor (void) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; qtractorCurve *pCurrentCurve = pTrack->currentCurve(); if (pCurrentCurve == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackCurveColor()"); #endif QWidget *pParentWidget = nullptr; QColorDialog::ColorDialogOptions options; if (m_pOptions && m_pOptions->bDontUseNativeDialogs) { options |= QColorDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } const QString& sTitle = pCurrentCurve->subject()->name(); const QColor& color = QColorDialog::getColor( pCurrentCurve->color(), pParentWidget, sTitle, options); if (!color.isValid()) return; // Save as future default... if (m_pOptions) m_pOptions->sCurveColor = color.name(); m_pSession->execute(new qtractorCurveColorCommand(pCurrentCurve, color)); } // Track automation curve clear. void qtractorMainForm::trackCurveClear (void) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; qtractorCurve *pCurrentCurve = pTrack->currentCurve(); if (pCurrentCurve == nullptr) return; if (pCurrentCurve->isEmpty()) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackCurveClear()"); #endif if (m_pOptions && m_pOptions->bConfirmRemove) { if (QMessageBox::warning(this, tr("Warning"), tr("About to clear automation:\n\n" "\"%1\"\n\n" "Are you sure?") .arg(pCurrentCurve->subject()->name()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; } m_pSession->execute(new qtractorCurveClearCommand(pCurrentCurve)); } // Track automation all curves lock toggle. void qtractorMainForm::trackCurveLockedAll ( bool bOn ) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackCurveLockedAll(%d)", int(bOn)); #endif pCurveList->setLockedAll(bOn); m_pTracks->updateTrackView(); updateDirtyCount(true); ++m_iStabilizeTimer; } // Track automation all curves playback toggle. void qtractorMainForm::trackCurveProcessAll ( bool bOn ) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackCurveProcessAll(%d)", int(bOn)); #endif m_pSession->execute(new qtractorCurveProcessAllCommand(pCurveList, bOn)); } // Track automation all curves record toggle. void qtractorMainForm::trackCurveCaptureAll ( bool bOn ) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackCurveCaptureAll(%d)", int(bOn)); #endif m_pSession->execute(new qtractorCurveCaptureAllCommand(pCurveList, bOn)); } // Track automation all curves clear. void qtractorMainForm::trackCurveClearAll (void) { qtractorTrack *pTrack = nullptr; if (m_pTracks) pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return; qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList == nullptr) return; if (pCurveList->isEmpty()) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::trackCurveClearAll()"); #endif if (m_pOptions && m_pOptions->bConfirmRemove) { if (QMessageBox::warning(this, tr("Warning"), tr("About to clear all automation:\n\n" "\"%1\"\n\n" "Are you sure?") .arg(pTrack->shortTrackName()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; } m_pSession->execute(new qtractorCurveClearAllCommand(pCurveList)); } //------------------------------------------------------------------------- // qtractorMainForm -- Clip Action slots. // Enter in clip create mode. void qtractorMainForm::clipNew (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipNew()"); #endif // New clip: we must have a session name... if (m_pSession->sessionName().isEmpty() && !editSession()) return; // Start editing a new clip... if (m_pTracks) m_pTracks->newClip(); } // Enter in clip edit mode. void qtractorMainForm::clipEdit (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipEdit()"); #endif // Start editing the current clip, if any... if (m_pTracks) m_pTracks->editClip(); } // Mute a clip. void qtractorMainForm::clipMute (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipMute()"); #endif // Mute the current clip, if any... if (m_pTracks) m_pTracks->muteClip(); } // Unlink a (MIDI) linked clip. void qtractorMainForm::clipUnlink (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipUnlink()"); #endif // Unlink the current clip, if any... if (m_pTracks) m_pTracks->unlinkClip(); } // Enter in clip record/ overdub mode. void qtractorMainForm::clipRecordEx ( bool bOn ) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipRecordEx(%d)", int(bOn)); #endif // Start record/overdub the current clip, if any... if (m_pTracks) { qtractorClip *pClip = m_pTracks->currentClip(); if (pClip) { qtractorTrack *pTrack = pClip->track(); if (pTrack && pTrack->trackType() == qtractorTrack::Midi) m_pSession->execute(new qtractorClipRecordExCommand(pClip, bOn)); } } } // Split current clip at playhead. void qtractorMainForm::clipSplit (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipSplit()"); #endif // Split current clip, if any... if (m_pTracks) m_pTracks->splitClip(); } // Merge selected (MIDI) clips. void qtractorMainForm::clipMerge (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipMerge()"); #endif // Merge clip selection, if any... if (m_pTracks) m_pTracks->mergeClips(); } // Normalize current clip. void qtractorMainForm::clipNormalize (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipNormalize()"); #endif // Normalize current clip, if any... if (m_pTracks) m_pTracks->normalizeClip(); } // Adjust current tempo from clip selection or interactive tapping... void qtractorMainForm::clipTempoAdjust (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipTempoAdjust()"); #endif if (m_pTracks) m_pTracks->tempoClip(); } // Cross-fade current overllaping clips... void qtractorMainForm::clipCrossFade (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipCrossFade()"); #endif if (m_pTracks) m_pTracks->crossFadeClip(); } // Set edit-range from current clip. void qtractorMainForm::clipRangeSet (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipRangeSet()"); #endif if (m_pTracks) m_pTracks->rangeClip(); } // Set loop from current clip range. void qtractorMainForm::clipLoopSet (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipLoopSet()"); #endif if (m_pTracks) m_ui.clipLoopSetAction->setChecked(m_pTracks->loopClip()); } // Import (audio) clip. void qtractorMainForm::clipImport (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipImport()"); #endif // Import (audio) clip(s)... if (m_pTracks) { // Depending on current track type (default to audio)... const unsigned long iClipStart = m_pSession->editHead(); QStringList files; qtractorTrack *pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) pTrack = m_pSession->tracks().first(); if (pTrack && pTrack->trackType() == qtractorTrack::Midi) files = m_pFiles->midiListView()->openFileNames(); else files = m_pFiles->audioListView()->openFileNames(); m_pTracks->importClips(files, iClipStart); m_pTracks->trackView()->ensureVisibleFrame(iClipStart); } } // Export current clip. void qtractorMainForm::clipExport (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipExport()"); #endif // Export current clip, if any... if (m_pTracks) m_pTracks->exportClips(); } // Quantize current clip. void qtractorMainForm::clipToolsQuantize (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipToolsQuantize()"); #endif // Quantize current clip events, if any... if (m_pTracks) m_pTracks->executeClipTool(qtractorMidiEditor::Quantize); } // Transpose current clip. void qtractorMainForm::clipToolsTranspose (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipToolsTranspose()"); #endif // Tranpose current clip events, if any... if (m_pTracks) m_pTracks->executeClipTool(qtractorMidiEditor::Transpose); } // Normalize current clip. void qtractorMainForm::clipToolsNormalize (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipToolsNormalize()"); #endif // Normalize current clip events, if any... if (m_pTracks) m_pTracks->executeClipTool(qtractorMidiEditor::Normalize); } // Randomize current clip. void qtractorMainForm::clipToolsRandomize (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipToolsRandomize()"); #endif // Randomize current clip events, if any... if (m_pTracks) m_pTracks->executeClipTool(qtractorMidiEditor::Randomize); } // Resize current clip. void qtractorMainForm::clipToolsResize (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipToolsResize()"); #endif // Resize current clip events, if any... if (m_pTracks) m_pTracks->executeClipTool(qtractorMidiEditor::Resize); } // Rescale current clip. void qtractorMainForm::clipToolsRescale (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipToolsRescale()"); #endif // Rescale current clip events, if any... if (m_pTracks) m_pTracks->executeClipTool(qtractorMidiEditor::Rescale); } // Timeshift current clip. void qtractorMainForm::clipToolsTimeshift (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipToolsTimeshift()"); #endif // Timeshift current clip events, if any... if (m_pTracks) m_pTracks->executeClipTool(qtractorMidiEditor::Timeshift); } // Temporamp current clip. void qtractorMainForm::clipToolsTemporamp (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipToolsTemporamp()"); #endif // Temporamp current clip events, if any... if (m_pTracks) m_pTracks->executeClipTool(qtractorMidiEditor::Temporamp); } // Select current clip take. void qtractorMainForm::clipTakeSelect ( QAction *pAction ) { const int iTake = pAction->data().toInt(); #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipTakeSelect(%d)", iTake); #endif qtractorClip *pClip = nullptr; if (m_pTracks) pClip = m_pTracks->currentClip(); qtractorClip::TakeInfo *pTakeInfo = (pClip ? pClip->takeInfo() : nullptr); if (pTakeInfo && pTakeInfo->currentTake() != iTake) { m_pSession->execute( new qtractorClipTakeCommand(pTakeInfo, pClip->track(), iTake)); } } // Select first clip take. void qtractorMainForm::clipTakeFirst (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipTakeFirst()"); #endif qtractorClip *pClip = nullptr; if (m_pTracks) pClip = m_pTracks->currentClip(); qtractorClip::TakeInfo *pTakeInfo = (pClip ? pClip->takeInfo() : nullptr); if (pTakeInfo) { m_pSession->execute( new qtractorClipTakeCommand(pTakeInfo, pClip->track(), 0)); } } // Select previous clip take. void qtractorMainForm::clipTakePrev (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipTakePrev()"); #endif qtractorClip *pClip = nullptr; if (m_pTracks) pClip = m_pTracks->currentClip(); qtractorClip::TakeInfo *pTakeInfo = (pClip ? pClip->takeInfo() : nullptr); if (pTakeInfo) { int iTake = pTakeInfo->currentTake() - 1; if (iTake < 0) iTake = pTakeInfo->takeCount() - 1; // Wrap to last take. m_pSession->execute( new qtractorClipTakeCommand(pTakeInfo, pClip->track(), iTake)); } } // Select next clip take. void qtractorMainForm::clipTakeNext (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipTakeNext()"); #endif qtractorClip *pClip = nullptr; if (m_pTracks) pClip = m_pTracks->currentClip(); qtractorClip::TakeInfo *pTakeInfo = (pClip ? pClip->takeInfo() : nullptr); if (pTakeInfo) { int iTake = pTakeInfo->currentTake() + 1; if (iTake >= pTakeInfo->takeCount()) iTake = 0; // Wrap to first take. m_pSession->execute( new qtractorClipTakeCommand(pTakeInfo, pClip->track(), iTake)); } } // Select last clip take. void qtractorMainForm::clipTakeLast (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipTakeLast()"); #endif qtractorClip *pClip = nullptr; if (m_pTracks) pClip = m_pTracks->currentClip(); qtractorClip::TakeInfo *pTakeInfo = (pClip ? pClip->takeInfo() : nullptr); if (pTakeInfo) { const int iTake = pTakeInfo->takeCount() - 1; m_pSession->execute( new qtractorClipTakeCommand(pTakeInfo, pClip->track(), iTake)); } } // Unfold current clip takes. void qtractorMainForm::clipTakeReset (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipTakeReset()"); #endif qtractorClip *pClip = nullptr; if (m_pTracks) pClip = m_pTracks->currentClip(); qtractorClip::TakeInfo *pTakeInfo = (pClip ? pClip->takeInfo() : nullptr); if (pTakeInfo) { m_pSession->execute( new qtractorClipTakeCommand(pTakeInfo)); } } // Fold current clip into takes. void qtractorMainForm::clipTakeRange (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::clipTakeRange()"); #endif qtractorClip *pClip = nullptr; if (m_pTracks) pClip = m_pTracks->currentClip(); qtractorClip::TakeInfo *pTakeInfo = (pClip ? pClip->takeInfo() : nullptr); if (pClip && pTakeInfo == nullptr) { qtractorTakeRangeForm form(this); form.setClip(pClip); if (form.exec()) { const unsigned long iTakeStart = form.takeStart(); const unsigned long iTakeEnd = form.takeEnd(); pTakeInfo = new qtractorClip::TakeInfo( pClip->clipStart(), pClip->clipOffset(), pClip->clipLength(), iTakeStart, iTakeEnd, 0); qtractorClipCommand *pClipCommand = new qtractorClipCommand(tr("take range")); pClipCommand->takeInfoClip(pClip, pTakeInfo); pTakeInfo->setClipPart(qtractorClip::TakeInfo::ClipTake, pClip); int iTake = form.currentTake(); iTake = pTakeInfo->select(pClipCommand, pClip->track(), iTake); pTakeInfo->setCurrentTake(iTake); if (m_pSession->execute(pClipCommand)) { m_pSession->setEditHead(iTakeStart); m_pSession->setEditTail(iTakeEnd); selectionNotifySlot(nullptr); } } } } //------------------------------------------------------------------------- // qtractorMainForm -- View Action slots. // Show/hide the main program window menubar. void qtractorMainForm::viewMenubar ( bool bOn ) { m_ui.menuBar->setVisible(bOn); } // Show/hide the main program window statusbar. void qtractorMainForm::viewStatusbar ( bool bOn ) { statusBar()->setVisible(bOn); } // Show/hide the file-toolbar. void qtractorMainForm::viewToolbarFile ( bool bOn ) { m_ui.fileToolbar->setVisible(bOn); } // Show/hide the edit-toolbar. void qtractorMainForm::viewToolbarEdit ( bool bOn ) { m_ui.editToolbar->setVisible(bOn); } // Show/hide the track-toolbar. void qtractorMainForm::viewToolbarTrack ( bool bOn ) { m_ui.trackToolbar->setVisible(bOn); } // Show/hide the view-toolbar. void qtractorMainForm::viewToolbarView ( bool bOn ) { m_ui.viewToolbar->setVisible(bOn); } // Show/hide the options toolbar. void qtractorMainForm::viewToolbarOptions ( bool bOn ) { m_ui.optionsToolbar->setVisible(bOn); } // Show/hide the transport toolbar. void qtractorMainForm::viewToolbarTransport ( bool bOn ) { m_ui.transportToolbar->setVisible(bOn); } // Show/hide the time toolbar. void qtractorMainForm::viewToolbarTime ( bool bOn ) { m_ui.timeToolbar->setVisible(bOn); } // Show/hide the thumb (track-line)ime toolbar. void qtractorMainForm::viewToolbarThumb ( bool bOn ) { m_ui.thumbViewToolbar->setVisible(bOn); } // Show/hide the file-system window view. void qtractorMainForm::viewFileSystem ( bool bOn ) { m_pFileSystem->setVisible(bOn); } // Show/hide the files window view. void qtractorMainForm::viewFiles ( bool bOn ) { m_pFiles->setVisible(bOn); } // Show/hide the messages window logger. void qtractorMainForm::viewMessages ( bool bOn ) { m_pMessages->setVisible(bOn); } // Show/hide the mixer window. void qtractorMainForm::viewMixer ( bool bOn ) { if (m_pOptions) m_pOptions->saveWidgetGeometry(m_pMixer); m_pMixer->setVisible(bOn); } // Show/hide the connections window. void qtractorMainForm::viewConnections ( bool bOn ) { if (m_pOptions) m_pOptions->saveWidgetGeometry(m_pConnections); if (bOn) m_pConnections->reset(); m_pConnections->setVisible(bOn); } // Horizontal and/or vertical zoom-in. void qtractorMainForm::viewZoomIn (void) { if (m_pTracks) m_pTracks->zoomIn(); } // Horizontal and/or vertical zoom-out. void qtractorMainForm::viewZoomOut (void) { if (m_pTracks) m_pTracks->zoomOut(); } // Reset zoom level to default. void qtractorMainForm::viewZoomReset (void) { if (m_pTracks) m_pTracks->zoomReset(); } // Set horizontal zoom mode void qtractorMainForm::viewZoomHorizontal (void) { if (m_pTracks) m_pTracks->setZoomMode(qtractorTracks::ZoomHorizontal); } // Set vertical zoom mode void qtractorMainForm::viewZoomVertical (void) { if (m_pTracks) m_pTracks->setZoomMode(qtractorTracks::ZoomVertical); } // Set all zoom mode void qtractorMainForm::viewZoomAll (void) { if (m_pTracks) m_pTracks->setZoomMode(qtractorTracks::ZoomAll); } // Set zebra mode void qtractorMainForm::viewSnapZebra ( bool bOn ) { if (m_pTracks) m_pTracks->trackView()->setSnapZebra(bOn); } // Set grid mode void qtractorMainForm::viewSnapGrid ( bool bOn ) { if (m_pTracks) m_pTracks->trackView()->setSnapGrid(bOn); } // Set floating tool-tips view mode void qtractorMainForm::viewToolTips ( bool bOn ) { if (m_pTracks) m_pTracks->trackView()->setToolTips(bOn); } // Change snap-per-beat setting via menu. void qtractorMainForm::viewSnap (void) { // Retrieve snap-per-beat index from from action data... QAction *pAction = qobject_cast (sender()); if (pAction) { // Commit the change as usual... snapPerBeatChanged(pAction->data().toInt()); // Update the other toolbar control... m_pSnapPerBeatComboBox->setCurrentIndex( qtractorTimeScale::indexFromSnap(m_pSession->snapPerBeat())); } } // Refresh view display. void qtractorMainForm::viewRefresh (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::viewRefresh()"); #endif // Tell the world we'll take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Update the whole session view dependables... m_pTempoCursor->clear(); m_pSession->updateTimeScale(); m_pSession->updateSession(); // Initialize toolbar widgets... // m_pTempoSpinBox->setTempo(m_pSession->tempo(), false); // m_pTempoSpinBox->setBeatsPerBar(m_pSession->beatsPerBar(), false); // m_pTempoSpinBox->setBeatDivisor(m_pSession->beatDivisor(), false); m_pSnapPerBeatComboBox->setCurrentIndex( qtractorTimeScale::indexFromSnap(m_pSession->snapPerBeat())); // Read session edit-head/tails... const unsigned long iEditHead = m_pSession->editHead(); const unsigned long iEditTail = m_pSession->editTail(); if (m_pTracks) { qtractorTrackView *pTrackView = m_pTracks->trackView(); pTrackView->setEditHead(iEditHead); pTrackView->setEditTail(iEditTail); pTrackView->setPlayHeadAutoBackward( m_pSession->playHeadAutoBackward()); m_pTracks->updateContents(true); } if (m_pConnections) m_pConnections->refresh(); if (m_pMixer) { m_pMixer->updateBuses(); m_pMixer->updateTracks(); } if (m_pThumbView) m_pThumbView->updateContents(); // Update other editors contents... QListIterator iter(m_editors); while (iter.hasNext()) { qtractorMidiEditorForm *pForm = iter.next(); pForm->updateTimeScale(); qtractorMidiEditor *pEditor = pForm->editor(); pEditor->setEditHead(iEditHead, false); pEditor->setEditTail(iEditTail, false); } // Reset XRUN counters... m_iXrunCount = 0; m_iXrunSkip = 0; m_iXrunTimer = 0; ++m_iStabilizeTimer; // We're formerly done. QApplication::restoreOverrideCursor(); } // Show instruments dialog. void qtractorMainForm::viewInstruments (void) { // Just set and show the instruments dialog... qtractorInstrumentForm(this).exec(); } // Show MIDI controllers dialog. void qtractorMainForm::viewControllers (void) { // Just set and show the MIDI controllers dialog... qtractorMidiControlForm(this).exec(); } // Show buses dialog. void qtractorMainForm::viewBuses (void) { // Just set and show the buses dialog... qtractorBusForm(this).exec(); } // Show tempo-map dialog. void qtractorMainForm::viewTempoMap (void) { // Just set and show the tempo-map dialog... qtractorTimeScaleForm form(this); form.setFrame(m_iPlayHead); form.exec(); } // Show options dialog. void qtractorMainForm::viewOptions (void) { if (m_pOptions == nullptr) return; // Check out some initial nullities(tm)... if (m_pOptions->sMessagesFont.isEmpty() && m_pMessages) m_pOptions->sMessagesFont = m_pMessages->messagesFont().toString(); // To track down deferred or immediate changes. const bool bOldMessagesLog = m_pOptions->bMessagesLog; const QString sOldMessagesLogPath = m_pOptions->sMessagesLogPath; const QString sOldMessagesFont = m_pOptions->sMessagesFont; const bool bOldStdoutCapture = m_pOptions->bStdoutCapture; const int bOldMessagesLimit = m_pOptions->bMessagesLimit; const int iOldMessagesLimitLines = m_pOptions->iMessagesLimitLines; const bool bOldCompletePath = m_pOptions->bCompletePath; const bool bOldPeakAutoRemove = m_pOptions->bPeakAutoRemove; const bool bOldKeepToolsOnTop = m_pOptions->bKeepToolsOnTop; const bool bOldKeepEditorsOnTop = m_pOptions->bKeepEditorsOnTop; const int iOldMaxRecentFiles = m_pOptions->iMaxRecentFiles; const int iOldDisplayFormat = m_pOptions->iDisplayFormat; const int iOldBaseFontSize = m_pOptions->iBaseFontSize; const int iOldResampleType = m_pOptions->iAudioResampleType; const bool bOldWsolaTimeStretch = m_pOptions->bAudioWsolaTimeStretch; const bool bOldWsolaQuickSeek = m_pOptions->bAudioWsolaQuickSeek; const bool bOldRubberBandFormant = m_pOptions->bAudioRubberBandFormant; const bool bOldRubberBandFinerR3 = m_pOptions->bAudioRubberBandFinerR3; const bool bOldAudioPlayerAutoConnect = m_pOptions->bAudioPlayerAutoConnect; const bool bOldAudioPlayerBus = m_pOptions->bAudioPlayerBus; const bool bOldAudioMetronome = m_pOptions->bAudioMetronome; const int iOldTransportMode = m_pOptions->iTransportMode; const bool bOldTimebase = m_pOptions->bTimebase; const int iOldMidiMmcDevice = m_pOptions->iMidiMmcDevice; const int iOldMidiMmcMode = m_pOptions->iMidiMmcMode; const int iOldMidiSppMode = m_pOptions->iMidiSppMode; const int iOldMidiClockMode = m_pOptions->iMidiClockMode; const bool bOldMidiResetAllControllers = m_pOptions->bMidiResetAllControllers; const int iOldMidiCaptureQuantize = m_pOptions->iMidiCaptureQuantize; const int iOldMidiQueueTimer = m_pOptions->iMidiQueueTimer; const bool bOldMidiDriftCorrect = m_pOptions->bMidiDriftCorrect; const bool bOldMidiPlayerBus = m_pOptions->bMidiPlayerBus; const QString sOldMetroBarFilename = m_pOptions->sMetroBarFilename; const float fOldMetroBarGain = m_pOptions->fMetroBarGain; const QString sOldMetroBeatFilename = m_pOptions->sMetroBeatFilename; const float fOldMetroBeatGain = m_pOptions->fMetroBeatGain; const bool bOldAudioMetroBus = m_pOptions->bAudioMetroBus; const bool bOldAudioMetroAutoConnect = m_pOptions->bAudioMetroAutoConnect; const unsigned long iOldAudioMetroOffset = m_pOptions->iAudioMetroOffset; const int iOldAudioCountInMode = m_pOptions->iAudioCountInMode; const int iOldAudioCountInBeats = m_pOptions->iAudioCountInBeats; const bool bOldMidiControlBus = m_pOptions->bMidiControlBus; const bool bOldMidiMetronome = m_pOptions->bMidiMetronome; const int iOldMidiCountInMode = m_pOptions->iMidiCountInMode; const int iOldMidiCountInBeats = m_pOptions->iMidiCountInBeats; const int iOldMetroChannel = m_pOptions->iMetroChannel; const int iOldMetroBarNote = m_pOptions->iMetroBarNote; const int iOldMetroBarVelocity = m_pOptions->iMetroBarVelocity; const int iOldMetroBarDuration = m_pOptions->iMetroBarDuration; const int iOldMetroBeatNote = m_pOptions->iMetroBeatNote; const int iOldMetroBeatVelocity = m_pOptions->iMetroBeatVelocity; const int iOldMetroBeatDuration = m_pOptions->iMetroBeatDuration; const bool bOldMidiMetroBus = m_pOptions->bMidiMetroBus; const int iOldMidiMetroOffset = m_pOptions->iMidiMetroOffset; const bool bOldSyncViewHold = m_pOptions->bSyncViewHold; const int iOldTrackColorSaturation = m_pOptions->iTrackColorSaturation; const QString sOldCustomColorTheme = m_pOptions->sCustomColorTheme; const QString sOldCustomStyleTheme = m_pOptions->sCustomStyleTheme; const QString sOldCustomStyleSheet = m_pOptions->sCustomStyleSheet; const QString sOldCustomIconsTheme = m_pOptions->sCustomIconsTheme; #ifdef CONFIG_LV2 const QString sep(':'); const QString sOldLv2Paths = m_pOptions->lv2Paths.join(sep); #endif // Load the current setup settings. qtractorOptionsForm optionsForm(this); optionsForm.setOptions(m_pOptions); // Show the setup dialog... if (optionsForm.exec()) { enum { RestartSession = 1, RestartProgram = 2, RestartAny = 3 }; int iNeedRestart = 0; // Check wheather something immediate has changed. if (iOldResampleType != m_pOptions->iAudioResampleType) { qtractorAudioBuffer::setDefaultResampleType( m_pOptions->iAudioResampleType); iNeedRestart |= RestartSession; } if (( bOldWsolaTimeStretch && !m_pOptions->bAudioWsolaTimeStretch) || (!bOldWsolaTimeStretch && m_pOptions->bAudioWsolaTimeStretch) || ( bOldWsolaQuickSeek && !m_pOptions->bAudioWsolaQuickSeek) || (!bOldWsolaQuickSeek && m_pOptions->bAudioWsolaQuickSeek) || ( bOldRubberBandFormant && !m_pOptions->bAudioRubberBandFormant) || (!bOldRubberBandFormant && m_pOptions->bAudioRubberBandFormant) || ( bOldRubberBandFinerR3 && !m_pOptions->bAudioRubberBandFinerR3) || (!bOldRubberBandFinerR3 && m_pOptions->bAudioRubberBandFinerR3)) { unsigned int iStretcherFlags = 0; if (m_pOptions->bAudioWsolaTimeStretch) iStretcherFlags |= qtractorTimeStretcher::WsolaTimeStretch; if (m_pOptions->bAudioWsolaQuickSeek) iStretcherFlags |= qtractorTimeStretcher::WsolaQuickSeek; #ifdef CONFIG_LIBRUBBERBAND if (m_pOptions->bAudioRubberBandFormant) iStretcherFlags |= qtractorTimeStretcher::RubberBandFormant; #ifdef CONFIG_LIBRUBBERBAND_R3 if (m_pOptions->bAudioRubberBandFinerR3) iStretcherFlags |= qtractorTimeStretcher::RubberBandFinerR3; #endif #endif qtractorAudioBuffer::setDefaultStretcherFlags(iStretcherFlags); iNeedRestart |= RestartSession; } // Audio engine control modes... if (iOldTransportMode != m_pOptions->iTransportMode) { ++m_iDirtyCount; // Fake session properties change. updateTransportModePre(); updateTransportModePost(); // iNeedRestart |= RestartSession; } if (( bOldTimebase && !m_pOptions->bTimebase) || (!bOldTimebase && m_pOptions->bTimebase)) { ++m_iDirtyCount; // Fake session properties change. updateTimebase(); // iNeedRestart |= RestartSession; } // MIDI engine queue timer... if (iOldMidiQueueTimer != m_pOptions->iMidiQueueTimer) { updateMidiQueueTimer(); iNeedRestart |= RestartSession; } #ifdef CONFIG_LV2 if (sOldLv2Paths != m_pOptions->lv2Paths.join(sep)) iNeedRestart |= RestartProgram; #endif if (( bOldStdoutCapture && !m_pOptions->bStdoutCapture) || (!bOldStdoutCapture && m_pOptions->bStdoutCapture)) { updateMessagesCapture(); iNeedRestart |= RestartProgram; } if (iOldBaseFontSize != m_pOptions->iBaseFontSize) iNeedRestart |= RestartProgram; if (sOldCustomIconsTheme != m_pOptions->sCustomIconsTheme) iNeedRestart |= RestartProgram; if (sOldCustomStyleSheet != m_pOptions->sCustomStyleSheet) updateCustomStyleSheet(); if (sOldCustomStyleTheme != m_pOptions->sCustomStyleTheme) { if (m_pOptions->sCustomStyleTheme.isEmpty()) iNeedRestart |= RestartProgram; else updateCustomStyleTheme(); } if ((sOldCustomColorTheme != m_pOptions->sCustomColorTheme) || (optionsForm.isDirtyCustomColorThemes())) { if (m_pOptions->sCustomColorTheme.isEmpty()) iNeedRestart |= RestartProgram; else updateCustomColorTheme(); } if (optionsForm.isDirtyMeterColors()) qtractorMeterValue::updateAll(); if (( bOldCompletePath && !m_pOptions->bCompletePath) || (!bOldCompletePath && m_pOptions->bCompletePath) || (iOldMaxRecentFiles != m_pOptions->iMaxRecentFiles)) updateRecentFilesMenu(); if (( bOldPeakAutoRemove && !m_pOptions->bPeakAutoRemove) || (!bOldPeakAutoRemove && m_pOptions->bPeakAutoRemove)) updatePeakAutoRemove(); if (( bOldKeepToolsOnTop && !m_pOptions->bKeepToolsOnTop) || (!bOldKeepToolsOnTop && m_pOptions->bKeepToolsOnTop)) iNeedRestart |= RestartProgram; if (( bOldKeepEditorsOnTop && !m_pOptions->bKeepEditorsOnTop) || (!bOldKeepEditorsOnTop && m_pOptions->bKeepEditorsOnTop)) updateEditorForms(); if (sOldMessagesFont != m_pOptions->sMessagesFont) updateMessagesFont(); if (( bOldMessagesLimit && !m_pOptions->bMessagesLimit) || (!bOldMessagesLimit && m_pOptions->bMessagesLimit) || (iOldMessagesLimitLines != m_pOptions->iMessagesLimitLines)) updateMessagesLimit(); if (iOldDisplayFormat != m_pOptions->iDisplayFormat) updateDisplayFormat(); if (( bOldMessagesLog && !m_pOptions->bMessagesLog) || (!bOldMessagesLog && m_pOptions->bMessagesLog) || (sOldMessagesLogPath != m_pOptions->sMessagesLogPath)) m_pMessages->setLogging( m_pOptions->bMessagesLog, m_pOptions->sMessagesLogPath); // FIXME: This is what it should ever be, // make it right from this very moment... qtractorAudioFileFactory::setDefaultType( m_pOptions->sAudioCaptureExt, m_pOptions->iAudioCaptureType, m_pOptions->iAudioCaptureFormat, m_pOptions->iAudioCaptureQuality); qtractorMidiClip::setDefaultFormat( m_pOptions->iMidiCaptureFormat); // Set default MIDI (plugin) instrument audio output mode. qtractorMidiManager::setDefaultAudioOutputBus( m_pOptions->bAudioOutputBus); qtractorMidiManager::setDefaultAudioOutputAutoConnect( m_pOptions->bAudioOutputAutoConnect); // Auto time-stretching, loop-recording global modes... if (m_pSession) { m_pSession->setAutoTimeStretch(m_pOptions->bAudioAutoTimeStretch); m_pSession->setLoopRecordingMode(m_pOptions->iLoopRecordingMode); } // Special track-view drop-span mode... if (m_pTracks) m_pTracks->trackView()->setDropSpan(m_pOptions->bTrackViewDropSpan); // MIDI engine control modes... if ((iOldMidiMmcDevice != m_pOptions->iMidiMmcDevice) || (iOldMidiMmcMode != m_pOptions->iMidiMmcMode) || (iOldMidiSppMode != m_pOptions->iMidiSppMode) || (iOldMidiClockMode != m_pOptions->iMidiClockMode) || ( bOldMidiResetAllControllers && !m_pOptions->bMidiResetAllControllers) || (!bOldMidiResetAllControllers && m_pOptions->bMidiResetAllControllers) || (iOldMidiCaptureQuantize != m_pOptions->iMidiCaptureQuantize)) { ++m_iDirtyCount; // Fake session properties change. updateMidiControlModes(); } // Audio engine audition/pre-listening player options... if (( bOldAudioPlayerBus && !m_pOptions->bAudioPlayerBus) || (!bOldAudioPlayerBus && m_pOptions->bAudioPlayerBus) || ( bOldAudioPlayerAutoConnect && !m_pOptions->bAudioPlayerAutoConnect) || (!bOldAudioPlayerAutoConnect && m_pOptions->bAudioPlayerAutoConnect)) updateAudioPlayer(); // MIDI engine drift correction option... if (( bOldMidiDriftCorrect && !m_pOptions->bMidiDriftCorrect) || (!bOldMidiDriftCorrect && m_pOptions->bMidiDriftCorrect)) updateMidiDriftCorrect(); // MIDI engine player options... if (( bOldMidiPlayerBus && !m_pOptions->bMidiPlayerBus) || (!bOldMidiPlayerBus && m_pOptions->bMidiPlayerBus)) updateMidiPlayer(); // MIDI engine control options... if (( bOldMidiControlBus && !m_pOptions->bMidiControlBus) || (!bOldMidiControlBus && m_pOptions->bMidiControlBus)) updateMidiControl(); // Audio engine metronome options... if (( bOldAudioMetronome && !m_pOptions->bAudioMetronome) || (!bOldAudioMetronome && m_pOptions->bAudioMetronome) || (sOldMetroBarFilename != m_pOptions->sMetroBarFilename) || (fOldMetroBarGain != m_pOptions->fMetroBarGain) || (sOldMetroBeatFilename != m_pOptions->sMetroBeatFilename) || (fOldMetroBeatGain != m_pOptions->fMetroBeatGain) || (iOldAudioMetroOffset != m_pOptions->iAudioMetroOffset) || ( bOldAudioMetroBus && !m_pOptions->bAudioMetroBus) || (!bOldAudioMetroBus && m_pOptions->bAudioMetroBus) || (iOldAudioCountInMode != m_pOptions->iAudioCountInMode) || (iOldAudioCountInBeats != m_pOptions->iAudioCountInBeats) || ( bOldAudioMetroAutoConnect && !m_pOptions->bAudioMetroAutoConnect) || (!bOldAudioMetroAutoConnect && m_pOptions->bAudioMetroAutoConnect)) updateAudioMetronome(); // MIDI engine metronome options... if (( bOldMidiMetronome && !m_pOptions->bMidiMetronome) || (!bOldMidiMetronome && m_pOptions->bMidiMetronome) || (iOldMidiCountInMode != m_pOptions->iMidiCountInMode) || (iOldMidiCountInBeats != m_pOptions->iMidiCountInBeats) || (iOldMetroChannel != m_pOptions->iMetroChannel) || (iOldMetroBarNote != m_pOptions->iMetroBarNote) || (iOldMetroBarVelocity != m_pOptions->iMetroBarVelocity) || (iOldMetroBarDuration != m_pOptions->iMetroBarDuration) || (iOldMetroBeatNote != m_pOptions->iMetroBeatNote) || (iOldMetroBeatVelocity != m_pOptions->iMetroBeatVelocity) || (iOldMetroBeatDuration != m_pOptions->iMetroBeatDuration) || (iOldMidiMetroOffset != m_pOptions->iMidiMetroOffset) || ( bOldMidiMetroBus && !m_pOptions->bMidiMetroBus) || (!bOldMidiMetroBus && m_pOptions->bMidiMetroBus)) updateMidiMetronome(); // Transport display options... if (( bOldSyncViewHold && !m_pOptions->bSyncViewHold) || (!bOldSyncViewHold && m_pOptions->bSyncViewHold)) updateSyncViewHold(); // Default track color saturation factor [0..400]. if (iOldTrackColorSaturation != m_pOptions->iTrackColorSaturation) qtractorTrack::setTrackColorSaturation( m_pOptions->iTrackColorSaturation); // Warn if something will be only effective on next time. if (iNeedRestart & RestartAny) { QString sNeedRestart; if (iNeedRestart & RestartSession) sNeedRestart += tr("session"); if (iNeedRestart & RestartProgram) { if (!sNeedRestart.isEmpty()) sNeedRestart += tr(" or "); sNeedRestart += tr("program"); } // Show restart needed message... QMessageBox::information(this, tr("Information"), tr("Some settings may be only effective\n" "next time you start this %1.") .arg(sNeedRestart)); } // Things that must be reset anyway... autoSaveReset(); } // This makes it. ++m_iStabilizeTimer; } //------------------------------------------------------------------------- // qtractorMainForm -- Transport Action slots. // Transport backward. void qtractorMainForm::transportBackward (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportBackward()"); #endif // Make sure session is activated?... //checkRestartSession(); // Move playhead to edit-tail, head or full session-start. bool bShiftKeyModifier = QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier); if (m_pOptions && m_pOptions->bShiftKeyModifier) bShiftKeyModifier = !bShiftKeyModifier; if (bShiftKeyModifier) { m_pSession->setPlayHead(0); } else { const unsigned long iPlayHead = m_pSession->playHead(); const bool bPlaying = m_pSession->isPlaying(); QList list; list.append(0); if (iPlayHead > m_pSession->playHeadAutoBackward()) list.append(m_pSession->playHeadAutoBackward()); if (iPlayHead > m_pSession->editHead()) list.append(m_pSession->editHead()); if (iPlayHead > m_pSession->editTail() && !bPlaying) list.append(m_pSession->editTail()); if (m_pSession->isLooping()) { if (iPlayHead > m_pSession->loopStart()) list.append(m_pSession->loopStart()); if (iPlayHead > m_pSession->loopEnd() && !bPlaying) list.append(m_pSession->loopEnd()); } if (m_pSession->isPunching()) { if (iPlayHead > m_pSession->punchIn()) list.append(m_pSession->punchIn()); if (iPlayHead > m_pSession->punchOut() && !bPlaying) list.append(m_pSession->punchOut()); } if (iPlayHead > m_pSession->sessionStart()) list.append(m_pSession->sessionStart()); // if (iPlayHead > m_pSession->sessionEnd() && !bPlaying) // list.append(m_pSession->sessionEnd()); qtractorTimeScale::Marker *pMarker = m_pSession->timeScale()->markers().seekFrame(iPlayHead); while (pMarker && pMarker->frame >= iPlayHead) pMarker = pMarker->prev(); if (pMarker && iPlayHead > pMarker->frame) list.append(pMarker->frame); std::sort(list.begin(), list.end()); m_pSession->setPlayHead(list.last()); } ++m_iTransportUpdate; ++m_iStabilizeTimer; } // Transport rewind. void qtractorMainForm::transportRewind (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportRewind()"); #endif // Make sure session is activated?... //checkRestartSession(); qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine == nullptr) return; // Rolling direction and speed (negative)... bool bShiftKeyModifier = QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier); if (m_pOptions && m_pOptions->bShiftKeyModifier) bShiftKeyModifier = !bShiftKeyModifier; const int iRolling = (bShiftKeyModifier ? -3 : -1); // Toggle rolling backward... if (setRolling(iRolling) >= 0) { // Send MMC REWIND command... pMidiEngine->sendMmcCommand(qtractorMmcEvent::REWIND); } else if (m_pSession->isPlaying()) { // setPlayingEx(false); // Avoid double update (while on fastTimerSlot...) m_iPlayHead = m_pSession->playHead(); if (m_iPlayHead > 0) { const unsigned int iSongPos // Schedule ahead... = m_pSession->songPosFromFrame(m_iPlayHead) + 1; pMidiEngine->sendSppCommand(SND_SEQ_EVENT_SONGPOS, iSongPos); pMidiEngine->sendSppCommand(SND_SEQ_EVENT_CONTINUE, iSongPos); } pMidiEngine->sendMmcLocate( m_pSession->locateFromFrame(m_iPlayHead)); } ++m_iStabilizeTimer; } // Transport fast-forward void qtractorMainForm::transportFastForward (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportFastForward()"); #endif // Make sure session is activated?... //checkRestartSession(); qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine == nullptr) return; // Rolling direction and speed (positive)... bool bShiftKeyModifier = QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier); if (m_pOptions && m_pOptions->bShiftKeyModifier) bShiftKeyModifier = !bShiftKeyModifier; const int iRolling = (bShiftKeyModifier ? +3 : +1); // Toggle rolling backward... if (0 >= setRolling(iRolling)) { // Send MMC FAST_FORWARD command... pMidiEngine->sendMmcCommand(qtractorMmcEvent::FAST_FORWARD); } else if (m_pSession->isPlaying()) { // setPlayingEx(false); // Avoid double update (while on fastTimerSlot...) m_iPlayHead = m_pSession->playHead(); if (m_iPlayHead > 0) { const unsigned int iSongPos // Schedule ahead... = m_pSession->songPosFromFrame(m_iPlayHead) + 1; pMidiEngine->sendSppCommand(SND_SEQ_EVENT_SONGPOS, iSongPos); pMidiEngine->sendSppCommand(SND_SEQ_EVENT_CONTINUE, iSongPos); } pMidiEngine->sendMmcLocate( m_pSession->locateFromFrame(m_iPlayHead)); } ++m_iStabilizeTimer; } // Transport forward void qtractorMainForm::transportForward (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportForward()"); #endif // Make sure session is activated?... //checkRestartSession(); // Move playhead to edit-head, tail or full session-end. bool bShiftKeyModifier = QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier); if (m_pOptions && m_pOptions->bShiftKeyModifier) bShiftKeyModifier = !bShiftKeyModifier; if (bShiftKeyModifier) { m_pSession->setPlayHead(m_pSession->sessionEnd()); } else { const unsigned long iPlayHead = m_pSession->playHead(); QList list; if (iPlayHead < m_pSession->playHeadAutoBackward()) list.append(m_pSession->playHeadAutoBackward()); if (iPlayHead < m_pSession->editHead()) list.append(m_pSession->editHead()); if (iPlayHead < m_pSession->editTail()) list.append(m_pSession->editTail()); if (m_pSession->isLooping()) { if (iPlayHead < m_pSession->loopStart()) list.append(m_pSession->loopStart()); if (iPlayHead < m_pSession->loopEnd()) list.append(m_pSession->loopEnd()); } if (m_pSession->isPunching()) { if (iPlayHead < m_pSession->punchIn()) list.append(m_pSession->punchIn()); if (iPlayHead < m_pSession->punchOut()) list.append(m_pSession->punchOut()); } if (iPlayHead < m_pSession->sessionStart()) list.append(m_pSession->sessionStart()); if (iPlayHead < m_pSession->sessionEnd()) list.append(m_pSession->sessionEnd()); qtractorTimeScale::Marker *pMarker = m_pSession->timeScale()->markers().seekFrame(iPlayHead); while (pMarker && iPlayHead >= pMarker->frame) pMarker = pMarker->next(); if (pMarker && iPlayHead < pMarker->frame) list.append(pMarker->frame); if (!list.isEmpty()) { std::sort(list.begin(), list.end()); m_pSession->setPlayHead(list.first()); } } ++m_iTransportUpdate; ++m_iStabilizeTimer; } // Transport step-backward void qtractorMainForm::transportStepBackward (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportStepBackward()"); #endif // Make sure session is activated?... //checkRestartSession(); qtractorTimeScale *pTimeScale = m_pSession->timeScale(); if (pTimeScale == nullptr) return; // Just make one step backward... unsigned long iPlayHead = m_pSession->playHead(); const unsigned short iSnapPerBeat = pTimeScale->snapPerBeat(); if (iSnapPerBeat > 0) { // Step-backward a beat/fraction... const unsigned long t0 = pTimeScale->tickFromFrame(iPlayHead); const unsigned int iBeat = pTimeScale->beatFromTick(t0); const unsigned long t1 = pTimeScale->tickFromBeat(iBeat > 0 ? iBeat : iBeat + 1); const unsigned long t2 = pTimeScale->tickFromBeat(iBeat > 0 ? iBeat - 1 : iBeat); const unsigned long dt = (t1 - t2) / iSnapPerBeat; iPlayHead = pTimeScale->frameFromTick( pTimeScale->tickSnap(t0 > dt ? t0 - dt : 0)); } else { // Step-backward a bar... const unsigned short iBar = pTimeScale->barFromFrame(iPlayHead); iPlayHead = pTimeScale->frameFromBar(iBar > 0 ? iBar - 1 : iBar); } m_pSession->setPlayHead(iPlayHead); ++m_iTransportUpdate; ++m_iStabilizeTimer; } // Transport step-forward void qtractorMainForm::transportStepForward (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportStepForward()"); #endif // Make sure session is activated?... //checkRestartSession(); qtractorTimeScale *pTimeScale = m_pSession->timeScale(); if (pTimeScale == nullptr) return; // Just make one step forward... unsigned long iPlayHead = m_pSession->playHead(); const unsigned short iSnapPerBeat = pTimeScale->snapPerBeat(); if (iSnapPerBeat > 0) { // Step-forward a beat/fraction... const unsigned long t0 = pTimeScale->tickFromFrame(iPlayHead); const unsigned int iBeat = pTimeScale->beatFromTick(t0); const unsigned long t1 = pTimeScale->tickFromBeat(iBeat); const unsigned long t2 = pTimeScale->tickFromBeat(iBeat + 1); const unsigned long dt = (t2 - t1) / iSnapPerBeat; iPlayHead = pTimeScale->frameFromTick( pTimeScale->tickSnap(t0 + dt)); } else { // Step-forward a bar... const unsigned short iBar = pTimeScale->barFromFrame(iPlayHead); iPlayHead = pTimeScale->frameFromBar(iBar + 1); } m_pSession->setPlayHead(iPlayHead); ++m_iTransportUpdate; ++m_iStabilizeTimer; } // Transport loop. void qtractorMainForm::transportLoop (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportLoop()"); #endif // Make sure session is activated?... //checkRestartSession(); // Do the loop toggle switch... unsigned long iLoopStart = 0; unsigned long iLoopEnd = 0; if (!m_pSession->isLooping()) { iLoopStart = m_pSession->editHead(); iLoopEnd = m_pSession->editTail(); } // Now, express the change as an undoable command... m_pSession->execute( new qtractorSessionLoopCommand(m_pSession, iLoopStart, iLoopEnd)); } // Transport loop setting. void qtractorMainForm::transportLoopSet (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportLoopSet()"); #endif // Make sure session is activated?... //checkRestartSession(); // Do the loop-set toggle switch... unsigned long iLoopStart = m_pSession->editHead(); unsigned long iLoopEnd = m_pSession->editTail(); if (m_pSession->isLooping() && m_pSession->loopStart() == m_pSession->editHead() && m_pSession->loopEnd() == m_pSession->editTail()) { iLoopStart = 0; iLoopEnd = 0; } // Now, express the change as an undoable command... m_pSession->execute( new qtractorSessionLoopCommand(m_pSession, iLoopStart, iLoopEnd)); } // Transport stop. void qtractorMainForm::transportStop (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportStop()"); #endif // Make sure session is activated... if (!checkRestartSession()) return; // Stop playing... if (setPlayingEx(false)) { // Auto-backward reset feature... if (m_ui.transportAutoBackwardAction->isChecked()) m_pSession->setPlayHead(m_pSession->playHeadAutoBackward()); } ++m_iStabilizeTimer; } // Transport play. void qtractorMainForm::transportPlay (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportPlay()"); #endif // Make sure session is activated... if (!checkRestartSession()) return; // Toggle playing... const bool bPlaying = !m_pSession->isPlaying(); if (setPlayingEx(bPlaying)) { // Save auto-backward return position... if (bPlaying) { const unsigned long iPlayHead = m_pSession->playHead(); qtractorTrackView *pTrackView = m_pTracks->trackView(); pTrackView->setPlayHeadAutoBackward(iPlayHead); pTrackView->setSyncViewHoldOn(false); } else // Auto-backward reset feature... if (m_ui.transportAutoBackwardAction->isChecked()) m_pSession->setPlayHead(m_pSession->playHeadAutoBackward()); } ++m_iStabilizeTimer; } // Transport record. void qtractorMainForm::transportRecord (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportRecord()"); #endif // Make sure session is activated... if (!checkRestartSession()) return; // Don't hold on anymore... if (m_pTracks) m_pTracks->trackView()->setSyncViewHoldOn(false); // Toggle recording... const bool bRecording = !m_pSession->isRecording(); if (setRecording(bRecording)) { // Send MMC RECORD_STROBE/EXIT command... m_pSession->midiEngine()->sendMmcCommand(bRecording ? qtractorMmcEvent::RECORD_STROBE : qtractorMmcEvent::RECORD_EXIT); } ++m_iStabilizeTimer; } // Transport punch in/out. void qtractorMainForm::transportPunch (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportPunch()"); #endif // Make sure session is activated?... //checkRestartSession(); // Do the punch in/out toggle switch... unsigned long iPunchIn = 0; unsigned long iPunchOut = 0; if (!m_pSession->isPunching()) { iPunchIn = m_pSession->editHead(); iPunchOut = m_pSession->editTail(); } // Now, express the change as an undoable command... m_pSession->execute( new qtractorSessionPunchCommand(m_pSession, iPunchIn, iPunchOut)); } // Transport punch set. void qtractorMainForm::transportPunchSet (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportPunchSet()"); #endif // Make sure session is activated?... //checkRestartSession(); // Now, express the change as an undoable command... m_pSession->execute( new qtractorSessionPunchCommand(m_pSession, m_pSession->editHead(), m_pSession->editTail())); } // Count-in metronome transport option. void qtractorMainForm::transportCountIn (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportCountIn()"); #endif // Toggle Audio count-in metronome... if (m_pOptions->bAudioMetronome) { qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine) pAudioEngine->setCountIn(!pAudioEngine->isCountIn()); } // Toggle MIDI count-in metronome... if (m_pOptions->bMidiMetronome) { qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine) pMidiEngine->setCountIn(!pMidiEngine->isCountIn()); } ++m_iStabilizeTimer; } // Metronome transport option. void qtractorMainForm::transportMetro (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportMetro()"); #endif // Toggle Audio metronome... if (m_pOptions->bAudioMetronome) { qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine) pAudioEngine->setMetronome(!pAudioEngine->isMetronome()); } // Toggle MIDI metronome... if (m_pOptions->bMidiMetronome) { qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine) pMidiEngine->setMetronome(!pMidiEngine->isMetronome()); } ++m_iStabilizeTimer; } // Follow playhead transport option. void qtractorMainForm::transportFollow (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportFollow()"); #endif // Can't hold on anymore... if (m_pTracks) m_pTracks->trackView()->setSyncViewHoldOn(false); // Toggle follow-playhead... ++m_iStabilizeTimer; } // Auto-backward transport option. void qtractorMainForm::transportAutoBackward (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportAutoBackward()"); #endif // Toggle auto-backward... ++m_iStabilizeTimer; } // Set Transport mode option to None. void qtractorMainForm::transportModeNone (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportModeNone()"); #endif // Set Transport mode to None... if (m_pOptions) { m_pOptions->iTransportMode = int(qtractorBus::None); updateTransportModePost(); } } // Set Transport mode option to Slave. void qtractorMainForm::transportModeSlave (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportModeSlave()"); #endif // Set Transport mode to Slave... if (m_pOptions) { m_pOptions->iTransportMode = int(qtractorBus::Input); updateTransportModePost(); } } // Set Transport mode option to Master. void qtractorMainForm::transportModeMaster (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportModeMaster()"); #endif // Set Transport mode to Master... if (m_pOptions) { m_pOptions->iTransportMode = int(qtractorBus::Output); updateTransportModePost(); } ++m_iStabilizeTimer; } // Set Transport mode option to Full. void qtractorMainForm::transportModeFull (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportModeFull()"); #endif // Set Transport mode to Full... if (m_pOptions) { m_pOptions->iTransportMode = int(qtractorBus::Duplex); updateTransportModePost(); } } // Continue past end transport option. void qtractorMainForm::transportContinue (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportContinue()"); #endif // Toggle continue-past-end... ++m_iStabilizeTimer; } // All tracks shut-off (panic). void qtractorMainForm::transportPanic (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportPanic()"); #endif qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine == nullptr) return; qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine == nullptr) return; // All players must end now... if (pAudioEngine->isPlayerOpen() || pMidiEngine->isPlayerOpen()) { m_iPlayerTimer = 0; if (m_pFiles && m_pFiles->isPlayState()) m_pFiles->setPlayState(false); if (m_pFileSystem && m_pFileSystem->isPlayState()) m_pFileSystem->setPlayState(false); appendMessages(tr("Player panic!")); pAudioEngine->closePlayer(); pMidiEngine->closePlayer(); } // All (MIDI) tracks shut-off (panic)... pMidiEngine->shutOffAllTracks(); // Reset XRUN counters... m_iXrunCount = 0; m_iXrunSkip = 0; m_iXrunTimer = 0; ++m_iStabilizeTimer; } //------------------------------------------------------------------------- // qtractorMainForm -- Help Action slots. // Show (and edit) keyboard shortcuts. void qtractorMainForm::helpShortcuts (void) { if (m_pOptions == nullptr) return; const QList& actions = findChildren (QString(), Qt::FindDirectChildrenOnly); qtractorShortcutForm shortcutForm(actions, this); shortcutForm.setActionControl(m_pActionControl); if (shortcutForm.exec()) { if (shortcutForm.isDirtyActionShortcuts()) m_pOptions->saveActionShortcuts(this); if (shortcutForm.isDirtyActionControls()) m_pOptions->saveActionControls(this); } } // Show information about application program. void qtractorMainForm::helpAbout (void) { QStringList list; #ifdef CONFIG_DEBUG list << tr("Debugging option enabled."); #endif #ifndef CONFIG_LIBVORBIS list << tr("Ogg Vorbis (libvorbis) file support disabled."); #endif #ifndef CONFIG_LIBMAD list << tr("MPEG-1 Audio Layer 3 (libmad) file support disabled."); #endif #ifndef CONFIG_LIBSAMPLERATE list << tr("Sample-rate conversion (libsamplerate) disabled."); #endif #ifndef CONFIG_LIBRUBBERBAND list << tr("Pitch-shifting support (librubberband) disabled."); #endif #ifndef CONFIG_LIBAUBIO list << tr("Beat-detection support (libaubio) disabled."); #endif #ifndef CONFIG_LIBLO list << tr("OSC service support (liblo) disabled."); #endif #ifndef CONFIG_LADSPA list << tr("LADSPA Plug-in support disabled."); #endif #ifndef CONFIG_DSSI list << tr("DSSI Plug-in support disabled."); #endif #ifndef CONFIG_VST2 list << tr("VST2 Plug-in support disabled."); #endif #ifndef CONFIG_VST3 list << tr("VST3 Plug-in support disabled."); #endif #ifndef CONFIG_CLAP list << tr("CLAP Plug-in support disabled."); #endif #ifndef CONFIG_LV2 list << tr("LV2 Plug-in support disabled."); #else #ifndef CONFIG_LIBLILV list << tr("LV2 Plug-in support (liblilv) disabled."); #endif #ifndef CONFIG_LV2_UI list << tr("LV2 Plug-in UI support disabled."); #else #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #ifndef CONFIG_LIBSUIL list << tr("LV2 Plug-in UI support (libsuil) disabled."); #endif #endif #ifndef CONFIG_LV2_EXTERNAL_UI list << tr("LV2 Plug-in External UI support disabled."); #endif #endif // CONFIG_LV2_UI #ifdef CONFIG_LV2_EVENT list << tr("LV2 Plug-in MIDI/Event support (DEPRECATED) enabled."); #endif #ifndef CONFIG_LV2_ATOM list << tr("LV2 Plug-in MIDI/Atom support disabled."); #endif #ifndef CONFIG_LV2_WORKER list << tr("LV2 Plug-in Worker/Schedule support disabled."); #endif #ifndef CONFIG_LV2_STATE list << tr("LV2 Plug-in State support disabled."); #endif #ifdef CONFIG_LV2_STATE_FILES #ifdef CONFIG_LV2_STATE_MAKE_PATH list << tr("LV2 plug-in State Make Path support (DANGEROUS) enabled."); #endif #else list << tr("LV2 Plug-in State Files support disabled."); #endif // CONFIG_LV2_STATE_FILES #ifndef CONFIG_LV2_PROGRAMS list << tr("LV2 Plug-in Programs support disabled."); #endif #ifndef CONFIG_LV2_MIDNAM list << tr("LV2 Plug-in MIDNAM support disabled."); #endif #ifndef CONFIG_LV2_PRESETS list << tr("LV2 Plug-in Presets support disabled."); #endif #ifndef CONFIG_LV2_PATCH list << tr("LV2 Plug-in Patch support disabled."); #endif #ifndef CONFIG_LV2_TIME list << tr("LV2 Plug-in Time/position support disabled."); #endif #ifndef CONFIG_LV2_OPTIONS list << tr("LV2 Plug-in Options support disabled."); #endif #ifndef CONFIG_LV2_BUF_SIZE list << tr("LV2 Plug-in Buf-size support disabled."); #endif #ifdef CONFIG_LV2_UI #ifndef CONFIG_LV2_UI_TOUCH list << tr("LV2 Plug-in UI Touch interface support disabled."); #endif #ifndef CONFIG_LV2_UI_REQ_VALUE list << tr("LV2 Plug-in UI Request-value support disabled."); #endif #ifndef CONFIG_LV2_UI_IDLE list << tr("LV2 Plug-in UI Idle interface support disabled."); #endif #ifndef CONFIG_LV2_UI_SHOW list << tr("LV2 Plug-in UI Show interface support disabled."); #endif #ifndef CONFIG_LV2_UI_GTK2 list << tr("LV2 Plug-in UI GTK2 native support disabled."); #endif #ifndef CONFIG_LV2_UI_GTKMM2 list << tr("LV2 Plug-in UI GTKMM2 native support disabled."); #endif #ifndef CONFIG_LV2_UI_X11 list << tr("LV2 Plug-in UI X11 native support disabled."); #endif #endif // CONFIG_LV2_UI #endif // CONFIG_LV2 #ifndef CONFIG_JACK_SESSION list << tr("JACK Session support disabled."); #endif #ifndef CONFIG_JACK_LATENCY list << tr("JACK Latency support disabled."); #endif #ifndef CONFIG_JACK_METADATA list << tr("JACK Metadata support disabled."); #endif #ifndef CONFIG_NSM list << tr("NSM support disabled."); #endif // Stuff the about box text... QString sText = "

" QTRACTOR_TITLE "

\n"; sText += "

" + tr(QTRACTOR_SUBTITLE) + "
\n"; sText += "
\n"; sText += tr("Version") + ": " PROJECT_VERSION "
\n"; // sText += "" + tr("Build") + ": " CONFIG_BUILD_DATE "
\n"; if (!list.isEmpty()) { sText += ""; sText += list.join("
\n"); sText += "
\n"; } sText += "
\n"; sText += tr("Using: Qt %1").arg(qVersion()); #if defined(QT_STATIC) sText += "-static"; #endif #if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) sText += ' '; sText += '('; sText += QApplication::platformName(); sText += ')'; #endif sText += "
\n"; sText += "
\n"; sText += tr("Website") + ": " QTRACTOR_WEBSITE "
\n"; sText += "
\n"; sText += ""; sText += QTRACTOR_COPYRIGHT "
\n"; sText += "
\n"; sText += tr("This program is free software; you can redistribute it and/or modify it") + "
\n"; sText += tr("under the terms of the GNU General Public License version 2 or later."); sText += "
"; sText += "
\n"; sText += "

\n"; QMessageBox::about(this, tr("About"), sText); } // Show information about the Qt toolkit. void qtractorMainForm::helpAboutQt (void) { QMessageBox::aboutQt(this); } //------------------------------------------------------------------------- // qtractorMainForm -- Internal transport stabilization. bool qtractorMainForm::setPlaying ( bool bPlaying ) { // Toggle engine play status... m_pSession->setPlaying(bPlaying); // We must start/stop certain things... if (bPlaying) { // In case of (re)starting playback, send now // all tracks MIDI bank select/program changes... m_pSession->resetAllMidiControllers(true); // Start something?... ++m_iTransportUpdate; } else { // Shutdown recording anyway... if (m_pSession->isRecording() && setRecording(false)) { // Send MMC RECORD_EXIT command... m_pSession->midiEngine()->sendMmcCommand( qtractorMmcEvent::RECORD_EXIT); ++m_iTransportUpdate; } // Stop transport rolling, immediately... setRolling(0); // Session tracks automation/overdub recording... qtractorCurveCaptureListCommand *pCurveCommand = nullptr; for (qtractorTrack *pTrack = m_pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList && pCurveList->isCapture() && !pCurveList->isEditListEmpty()) { if (pCurveCommand == nullptr) pCurveCommand = new qtractorCurveCaptureListCommand(); pCurveCommand->addCurveList(pCurveList); } if (pTrack->trackType() == qtractorTrack::Midi && pTrack->isClipRecordEx()) { qtractorMidiClip *pMidiClip = static_cast (pTrack->clipRecord()); if (pMidiClip) pMidiClip->clearInpEvents(); } } if (pCurveCommand) m_pSession->commands()->push(pCurveCommand); if (m_pTracks) m_pTracks->updateContents(true); } // Done with playback switch... return true; } bool qtractorMainForm::setPlayingEx ( bool bPlaying ) { if (!setPlaying(bPlaying)) return false; qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine) { // Avoid double update (while on fastTimerSlot...) m_iPlayHead = m_pSession->playHead(); // Send MMC PLAY/STOP command... pMidiEngine->sendMmcCommand(bPlaying ? qtractorMmcEvent::PLAY : qtractorMmcEvent::STOP); // Send MMC LOCATE and MIDI SPP commands on stop/pause... if (bPlaying) { if (m_iPlayHead > 0) { const unsigned int iSongPos // Schedule ahead... = m_pSession->songPosFromFrame(m_iPlayHead) + 1; pMidiEngine->sendSppCommand(SND_SEQ_EVENT_SONGPOS, iSongPos); pMidiEngine->sendSppCommand(SND_SEQ_EVENT_CONTINUE, iSongPos); } else { pMidiEngine->sendSppCommand(SND_SEQ_EVENT_START); } } else { pMidiEngine->sendSppCommand(SND_SEQ_EVENT_STOP); pMidiEngine->sendMmcLocate( m_pSession->locateFromFrame(m_iPlayHead)); pMidiEngine->sendSppCommand(SND_SEQ_EVENT_SONGPOS, m_pSession->songPosFromFrame(m_iPlayHead)); } } return true; } bool qtractorMainForm::setRecording ( bool bRecording ) { // Avoid if no tracks are armed... if (m_pSession->recordTracks() < 1) return false; if (bRecording) { // Starting recording: we must have a session name... if (m_pSession->sessionName().isEmpty() && !editSession()) return false; // Will start recording... } else { // Stopping recording: fetch and commit // all new clips as a composite command... int iUpdate = 0; qtractorClipCommand *pClipCommand = new qtractorClipCommand(tr("record clip")); // For all non-empty clip on record... const unsigned long iFrameTime = m_pSession->frameTimeEx(); for (qtractorTrack *pTrack = m_pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pClipCommand->addClipRecord(pTrack, iFrameTime)) ++iUpdate; } // Put it in the form of an undoable command... if (iUpdate > 0) { m_pSession->execute(pClipCommand); } else { // The allocated command is unhelpful... delete pClipCommand; // Try to postpone an overall refresh... if (m_iAudioPeakTimer < 2) ++m_iAudioPeakTimer; } } // Finally, toggle session record status... m_pSession->setRecording(bRecording); // Also force some kind of a checkpoint, // next time, whenever applicable... if (m_iAutoSavePeriod > 0 && !bRecording) m_iAutoSaveTimer += m_iAutoSavePeriod; // Done with record switch... return true; } int qtractorMainForm::setRolling ( int iRolling ) { const int iOldRolling = m_iTransportRolling; // Avoid if recording is armed... if (m_pSession->isRecording() || iOldRolling == iRolling) iRolling = 0; // Set the rolling flag. m_iTransportRolling = iRolling; m_fTransportShuttle = float(iRolling); m_iTransportStep = 0; // We've started/stopped something... if (m_iTransportRolling) { if (!m_bTransportPlaying) m_bTransportPlaying = m_pSession->isPlaying(); if (m_bTransportPlaying) m_pSession->setPlaying(false); ++m_iTransportUpdate; } else { if (m_bTransportPlaying) m_pSession->setPlaying(true); m_bTransportPlaying = false; } // Done with rolling switch... return iOldRolling; } void qtractorMainForm::setLocate ( unsigned int iLocate ) { m_pSession->setPlayHead(m_pSession->frameFromLocate(iLocate)); ++m_iTransportUpdate; } void qtractorMainForm::setShuttle ( float fShuttle ) { const float fOldShuttle = m_fTransportShuttle; if (fShuttle < 0.0f && fOldShuttle >= 0.0f) setRolling(-1); else if (fShuttle > 0.0f && 0.0f >= fOldShuttle) setRolling(+1); m_fTransportShuttle = fShuttle; ++m_iTransportUpdate; } void qtractorMainForm::setStep ( int iStep ) { m_iTransportStep += iStep; ++m_iTransportUpdate; } void qtractorMainForm::setTrack ( int scmd, int iTrack, bool bOn ) { if (m_pTracks) { // Find which ordinal track... qtractorTrack *pTrack = m_pTracks->trackList()->track(iTrack); if (pTrack) { // Set session track mode state... switch (qtractorMmcEvent::SubCommand(scmd)) { case qtractorMmcEvent::TRACK_RECORD: pTrack->setRecord(bOn); break; case qtractorMmcEvent::TRACK_MUTE: pTrack->setMute(bOn); break; case qtractorMmcEvent::TRACK_SOLO: pTrack->setSolo(bOn); break; case qtractorMmcEvent::TRACK_MONITOR: pTrack->setMonitor(bOn); break; default: break; } // Done. ++m_iStabilizeTimer; } } } void qtractorMainForm::setSongPos ( unsigned int iSongPos ) { m_pSession->setPlayHead(m_pSession->frameFromSongPos(iSongPos)); ++m_iTransportUpdate; } //------------------------------------------------------------------------- // qtractorMainForm -- Main window stabilization. void qtractorMainForm::updateTransportTime ( unsigned long iPlayHead ) { m_pTimeSpinBox->setValue(iPlayHead, false); m_pThumbView->updatePlayHead(iPlayHead); // Update other editors thumb-views... QListIterator iter(m_editors); while (iter.hasNext()) iter.next()->updatePlayHead(iPlayHead); // Tricky stuff: node's non-null iif tempo changes... qtractorTimeScale::Node *pNode = m_pTempoCursor->seek(m_pSession->timeScale(), iPlayHead); if (pNode) { m_pTempoSpinBox->setTempo(pNode->tempo, false); m_pTempoSpinBox->setBeatsPerBar(pNode->beatsPerBar, false); m_pTempoSpinBox->setBeatDivisor(pNode->beatDivisor, false); } } void qtractorMainForm::stabilizeForm (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::stabilizeForm()"); #endif // Update the main application caption... QString sSessionName = sessionName(m_sFilename); if (m_iDirtyCount > 0) sSessionName += ' ' + tr("[modified]"); setWindowTitle(sSessionName); // Update the main menu state... m_ui.fileSaveAction->setEnabled(m_iDirtyCount > 0); #ifdef CONFIG_NSM const bool bNsmActive = (m_pNsmClient && m_pNsmClient->is_active()); m_ui.fileNewAction->setEnabled(!bNsmActive); m_ui.fileSaveAsAction->setEnabled(!bNsmActive); #endif const unsigned long iPlayHead = m_pSession->playHead(); const unsigned long iSessionEnd = m_pSession->sessionEnd(); const bool bTracks = (m_pTracks && m_pSession->tracks().count() > 0); qtractorTrack *pTrack = (bTracks ? m_pTracks->currentTrack() : nullptr); const bool bEnabled = (pTrack != nullptr); const bool bSelected = (m_pTracks && m_pTracks->isSelected()) || (m_pFiles && m_pFiles->hasFocus() && m_pFiles->isFileSelected()); const bool bSelectable = (m_pSession->editHead() < m_pSession->editTail()); const bool bClipboard = qtractorTrackView::isClipboard(); const bool bPlaying = m_pSession->isPlaying(); const bool bRecording = m_pSession->isRecording(); const bool bPunching = m_pSession->isPunching(); const bool bLooping = m_pSession->isLooping(); const bool bRolling = (bPlaying && bRecording); const bool bBumped = (!bRolling && (iPlayHead > 0 || bPlaying)); // Update edit menu state... if (bRolling) { m_ui.editUndoAction->setEnabled(false); m_ui.editRedoAction->setEnabled(false); } else { qtractorCommandList *pCommands = m_pSession->commands(); pCommands->updateAction(m_ui.editUndoAction, pCommands->lastCommand()); pCommands->updateAction(m_ui.editRedoAction, pCommands->nextCommand()); } // m_ui.editCutAction->setEnabled(bSelected); // m_ui.editCopyAction->setEnabled(bSelected); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) const QMimeData *pMimeData = QApplication::clipboard()->mimeData(); m_ui.editPasteAction->setEnabled(bClipboard || (pMimeData && pMimeData->hasUrls())); #else m_ui.editPasteAction->setEnabled(bClipboard); #endif m_ui.editPasteRepeatAction->setEnabled(bClipboard); // m_ui.editDeleteAction->setEnabled(bSelected); m_ui.editSelectAllAction->setEnabled(iSessionEnd > 0); m_ui.editSelectInvertAction->setEnabled(iSessionEnd > 0); m_ui.editSelectTrackRangeAction->setEnabled(bEnabled && bSelectable); m_ui.editSelectTrackAction->setEnabled(bEnabled); m_ui.editSelectRangeAction->setEnabled(iSessionEnd > 0 && bSelectable); m_ui.editSelectNoneAction->setEnabled(bSelected); const bool bInsertable = m_pSession->editHead() < iSessionEnd; m_ui.editInsertTrackRangeAction->setEnabled(bEnabled && bInsertable); m_ui.editInsertRangeAction->setEnabled(bInsertable); m_ui.editRemoveTrackRangeAction->setEnabled(bEnabled && bInsertable); m_ui.editRemoveRangeAction->setEnabled(bInsertable); m_ui.editSplitAction->setEnabled(bSelected); // Top-level menu/toolbar items stabilization... updateTrackMenu(); updateClipMenu(); // Update view menu state... m_ui.viewFileSystemAction->setChecked( m_pFileSystem && m_pFileSystem->isVisible()); m_ui.viewFilesAction->setChecked( m_pFiles && m_pFiles->isVisible()); m_ui.viewMessagesAction->setChecked( m_pMessages && m_pMessages->isVisible()); m_ui.viewConnectionsAction->setChecked( m_pConnections && m_pConnections->isVisible()); m_ui.viewMixerAction->setChecked( m_pMixer && m_pMixer->isVisible()); // Recent files menu. m_ui.fileOpenRecentMenu->setEnabled(m_pOptions->recentFiles.count() > 0); // Always make the latest message visible. if (m_pMessages) m_pMessages->flushStdoutBuffer(); // Session status... updateTransportTime(iPlayHead); if (pTrack) m_statusItems[StatusName]->setText(pTrack->trackName().simplified()); else m_statusItems[StatusName]->clear(); if (m_iDirtyCount > 0) m_statusItems[StatusMod]->setText(tr("MOD")); else m_statusItems[StatusMod]->clear(); if (bRecording && m_pSession->recordTracks() > 0) m_statusItems[StatusRec]->setText(tr("REC")); else m_statusItems[StatusRec]->clear(); if (m_pSession->muteTracks() > 0) m_statusItems[StatusMute]->setText(tr("MUTE")); else m_statusItems[StatusMute]->clear(); if (m_pSession->soloTracks() > 0) m_statusItems[StatusSolo]->setText(tr("SOLO")); else m_statusItems[StatusSolo]->clear(); if (m_pSession->isLooping()) m_statusItems[StatusLoop]->setText(tr("LOOP")); else m_statusItems[StatusLoop]->clear(); if (m_iXrunCount > 0) m_statusItems[StatusXrun]->setText(tr("XRUN")); else m_statusItems[StatusXrun]->clear(); m_statusItems[StatusTime]->setText( m_pSession->timeScale()->textFromFrame(0, true, iSessionEnd)); m_statusItems[StatusSize]->setText( QString::number(m_pSession->audioEngine()->bufferSize())); m_statusItems[StatusRate]->setText( QString::number(m_pSession->sampleRate())); m_statusItems[StatusRec]->setPalette(*m_paletteItems[ bRecording && bRolling ? PaletteRed : PaletteNone]); m_statusItems[StatusMute]->setPalette(*m_paletteItems[ m_pSession->muteTracks() > 0 ? PaletteYellow : PaletteNone]); m_statusItems[StatusSolo]->setPalette(*m_paletteItems[ m_pSession->soloTracks() > 0 ? PaletteCyan : PaletteNone]); m_statusItems[StatusLoop]->setPalette(*m_paletteItems[ bLooping ? PaletteGreen : PaletteNone]); m_statusItems[StatusXrun]->setPalette(*m_paletteItems[ m_iXrunCount > 0 ? PaletteRed : PaletteNone]); // Transport stuff... m_ui.transportBackwardAction->setEnabled(bBumped); m_ui.transportRewindAction->setEnabled(bBumped); m_ui.transportFastForwardAction->setEnabled(!bRolling); m_ui.transportForwardAction->setEnabled( !bRolling && (iPlayHead < iSessionEnd || iPlayHead < m_pSession->editHead() || iPlayHead < m_pSession->editTail())); m_ui.transportStepBackwardAction->setEnabled(bBumped); m_ui.transportStepForwardAction->setEnabled(!bRolling); m_ui.transportLoopAction->setEnabled( !bRolling && (bLooping || bSelectable)); m_ui.transportLoopSetAction->setEnabled( !bRolling && (bSelectable || bLooping)); m_ui.transportStopAction->setEnabled(bPlaying); m_ui.transportRecordAction->setEnabled(m_pSession->recordTracks() > 0); m_ui.transportPunchAction->setEnabled(bPunching || bSelectable); m_ui.transportPunchSetAction->setEnabled(bSelectable); m_ui.transportCountInAction->setEnabled( (m_pOptions->bAudioMetronome && int(m_pSession->audioEngine()->countInMode()) > 0) || (m_pOptions->bMidiMetronome && int(m_pSession->midiEngine()->countInMode()) > 0)); m_ui.transportMetroAction->setEnabled( m_pOptions->bAudioMetronome || m_pOptions->bMidiMetronome); m_ui.transportPanicAction->setEnabled(bTracks || (m_pFiles && m_pFiles->isPlayState()) || (m_pFileSystem && m_pFileSystem->isPlayState())); m_ui.transportRewindAction->setChecked(m_iTransportRolling < 0); m_ui.transportFastForwardAction->setChecked(m_iTransportRolling > 0); m_ui.transportLoopAction->setChecked(bLooping); m_ui.transportPlayAction->setChecked(bPlaying); m_ui.transportRecordAction->setChecked(bRecording); m_ui.transportPunchAction->setChecked(bPunching); // Special record mode settlement. m_pTimeSpinBox->setReadOnly(bRecording); m_pTempoSpinBox->setReadOnly(bRecording); // Stabilize thumb-view... m_pThumbView->update(); m_pThumbView->updateThumb(); // Update editors too... QListIterator iter(m_editors); while (iter.hasNext()) iter.next()->stabilizeForm(); } // Actually start all session engines. bool qtractorMainForm::startSession (void) { m_iTransportUpdate = 0; m_iTransportRolling = 0; m_bTransportPlaying = false; m_fTransportShuttle = 0.0f; m_iTransportStep = 0; m_iXrunCount = 0; m_iXrunSkip = 0; m_iXrunTimer = 0; m_iAudioRefreshTimer = 0; m_iMidiRefreshTimer = 0; m_iPlayerTimer = 0; m_iAudioPropertyChange = 0; m_iAudioSelfConnected = 0; m_iStabilizeTimer = 0; QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); const bool bResult = m_pSession->init(); QApplication::restoreOverrideCursor(); autoSaveReset(); if (bResult) { // Get on with the special ALSA sequencer notifier... qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine && pMidiEngine->alsaNotifier()) { QObject::connect(pMidiEngine->alsaNotifier(), SIGNAL(activated(int)), SLOT(alsaNotify())); } // Game on... appendMessages(tr("Session started.")); } else { // Uh-oh, we can't go on like this... appendMessagesError( tr("The audio/MIDI engine could not be started.\n\n" "Make sure the JACK/Pipewire audio service and\n" "the ALSA Sequencer kernel module (snd-seq-midi)\n" "are up and running and then restart the session.")); } return bResult; } // Check and restart session, if applicable. bool qtractorMainForm::checkRestartSession (void) { // Whether session is currently activated, // try to (re)open the whole thing... if (!m_pSession->isActivated()) { // Save current playhead position, if any... const unsigned long iPlayHead = m_pSession->playHead(); QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_pSession->close(); QApplication::restoreOverrideCursor(); // Bail out if can't start it... if (!startSession()) { // Can go on with no-business... ++m_iStabilizeTimer; return false; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_pSession->open(); QApplication::restoreOverrideCursor(); // Restore previous playhead position... m_pSession->setPlayHead(iPlayHead); // Done restart. } else { // Reset XRUN counters... m_iXrunCount = 0; m_iXrunSkip = 0; m_iXrunTimer = 0; // HACK: When applicable, make sure we're // always the (JACK) Timebase master... if (m_pSession->audioEngine()) m_pSession->audioEngine()->resetTimebase(); // Done checking. } return true; } // Prepare session start. void qtractorMainForm::updateSessionPre (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::updateSessionPre()"); #endif // Actually (re)start session engines, no matter what... startSession(); #if 0 // (Re)set playhead... if (m_ui.transportAutoBackwardAction->isChecked()) m_pSession->setPlayHead(playHeadBackward()); #endif // Start collection of nested messages... qtractorMessageList::clear(); } // Finalize session start. void qtractorMainForm::updateSessionPost (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::updateSessionPost()"); #endif QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_pSession->open(); QApplication::restoreOverrideCursor(); // HACK: Special treatment for disparate sample rates, // and only for (just loaded) non empty sessions... const unsigned int iSampleRate = m_pSession->audioEngine()->sampleRate(); if (m_pSession->sampleRate() != iSampleRate) { appendMessagesError( tr("The original session sample rate (%1 Hz)\n" "is not the same as the current audio engine (%2 Hz).\n\n" "Saving and reloading from a new session file\n" "is highly recommended.") .arg(m_pSession->sampleRate()) .arg(iSampleRate)); // We'll doing the conversion right here and right now... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); m_pSession->updateSampleRate(iSampleRate); QApplication::restoreOverrideCursor(); // Prompt for a brand new filename (ie. Save As...) // whenever session Save is invoked next time. m_sFilename.clear(); updateDirtyCount(true); } // We're definitely clean... qtractorSubject::resetQueue(); // Update the session views... viewRefresh(); // Sync all process-enabled automation curves... m_pSession->process_curve(m_iPlayHead); // Check for any pending nested messages... if (!qtractorMessageList::isEmpty()) { if (QMessageBox::warning(this, tr("Warning"), tr("The following issues were detected:\n\n%1\n\n" "Saving into another session file is highly recommended.") .arg(qtractorMessageList::items().join("\n")), QMessageBox::Save | QMessageBox::Ignore) == QMessageBox::Save) { saveSession(true); } else { // Prompt for a brand new filename (ie. Save As...) // whenever session Save is invoked next time. m_sFilename.clear(); updateDirtyCount(true); } qtractorMessageList::clear(); } // Reset/reset all MIDI controllers (conditional)... m_pSession->resetAllMidiControllers(false); // Ah, make it stand right. if (m_pTracks) m_pTracks->trackView()->setFocus(); // Of course!... ++m_iTransportUpdate; } // Update the track export menu. void qtractorMainForm::updateExportMenu (void) { // Special export enablement... int iAudioClips = 0; int iMidiClips = 0; if (!m_pSession->isPlaying()) { for (qtractorTrack *pTrack = m_pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { const int iClips = pTrack->clips().count(); switch (pTrack->trackType()) { case qtractorTrack::Audio: iAudioClips += iClips; break; case qtractorTrack::Midi: iMidiClips += iClips; // Fall thru... default: break; } } } // nb. audio export also applies to MIDI instrument tracks... m_ui.trackExportAudioAction->setEnabled(iAudioClips > 0 || iMidiClips > 0); m_ui.trackExportMidiAction->setEnabled(iMidiClips > 0); } // Update the recent files list and menu. void qtractorMainForm::updateRecentFiles ( const QString& sFilename ) { if (m_pOptions == nullptr) return; // Remove from list if already there (avoid duplicates) const int iIndex = m_pOptions->recentFiles.indexOf(sFilename); if (iIndex >= 0) m_pOptions->recentFiles.removeAt(iIndex); // Put it to front... m_pOptions->recentFiles.push_front(sFilename); } // Update the recent files list and menu. void qtractorMainForm::updateRecentFilesMenu (void) { if (m_pOptions == nullptr) return; // Time to keep the list under limits. int iRecentFiles = m_pOptions->recentFiles.count(); while (iRecentFiles > m_pOptions->iMaxRecentFiles) { m_pOptions->recentFiles.pop_back(); --iRecentFiles; } // Rebuild the recent files menu... m_ui.fileOpenRecentMenu->clear(); for (int i = 0; i < iRecentFiles; ++i) { const QString& sFilename = m_pOptions->recentFiles.at(i); if (QFileInfo(sFilename).exists()) { QAction *pAction = m_ui.fileOpenRecentMenu->addAction( QString("&%1 %2").arg(i + 1).arg(sessionName(sFilename)), this, SLOT(fileOpenRecent())); pAction->setData(i); } } // Settle as enabled? m_ui.fileOpenRecentMenu->setEnabled(!m_ui.fileOpenRecentMenu->isEmpty()); } // Force update of the peak-files auto-remove mode. void qtractorMainForm::updatePeakAutoRemove (void) { if (m_pOptions == nullptr) return; qtractorAudioPeakFactory *pPeakFactory = m_pSession->audioPeakFactory(); if (pPeakFactory) pPeakFactory->setAutoRemove(m_pOptions->bPeakAutoRemove); } // Update main transport-time display format. void qtractorMainForm::updateDisplayFormat (void) { if (m_pOptions == nullptr) return; // Main transport display format is due... const qtractorTimeScale::DisplayFormat displayFormat = qtractorTimeScale::DisplayFormat(m_pOptions->iDisplayFormat); (m_pSession->timeScale())->setDisplayFormat(displayFormat); m_pTimeSpinBox->setDisplayFormat(displayFormat); } // Update audio player parameters. void qtractorMainForm::updateAudioPlayer (void) { if (m_pOptions == nullptr) return; // Configure the Audio engine player handling... qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine == nullptr) return; pAudioEngine->setPlayerAutoConnect(m_pOptions->bAudioPlayerAutoConnect); pAudioEngine->setPlayerBus(m_pOptions->bAudioPlayerBus); } // Update Audio engine control mode settings. void qtractorMainForm::updateTransportModePre (void) { if (m_pOptions == nullptr) return; switch (qtractorBus::BusMode(m_pOptions->iTransportMode)) { case qtractorBus::None: // None m_ui.transportModeNoneAction->setChecked(true); break; case qtractorBus::Input: // Slave m_ui.transportModeSlaveAction->setChecked(true); break; case qtractorBus::Output: // Master m_ui.transportModeMasterAction->setChecked(true); break; case qtractorBus::Duplex: // Full default: m_ui.transportModeFullAction->setChecked(true); break; } // Set initial transport mode... m_pTransportModeToolButton->setDefaultAction( m_pTransportModeActionGroup->checkedAction()); } void qtractorMainForm::updateTransportModePost (void) { if (m_pOptions == nullptr) return; // Configure the Audio engine handling... qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine == nullptr) return; pAudioEngine->setTransportMode( qtractorBus::BusMode(m_pOptions->iTransportMode)); } // Update JACK Timebase master mode. void qtractorMainForm::updateTimebase (void) { if (m_pOptions == nullptr) return; // Configure the Audio engine handling... qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine == nullptr) return; pAudioEngine->setTimebase(m_pOptions->bTimebase); pAudioEngine->resetTimebase(); } // Update MIDI engine control mode settings. void qtractorMainForm::updateMidiControlModes (void) { if (m_pOptions == nullptr) return; // Configure the MIDI engine handling... qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine == nullptr) return; pMidiEngine->setCaptureQuantize( qtractorTimeScale::snapFromIndex(m_pOptions->iMidiCaptureQuantize)); pMidiEngine->setMmcDevice(m_pOptions->iMidiMmcDevice); pMidiEngine->setMmcMode(qtractorBus::BusMode(m_pOptions->iMidiMmcMode)); pMidiEngine->setSppMode(qtractorBus::BusMode(m_pOptions->iMidiSppMode)); pMidiEngine->setClockMode(qtractorBus::BusMode(m_pOptions->iMidiClockMode)); pMidiEngine->setResetAllControllers(m_pOptions->bMidiResetAllControllers); } // Update MIDI playback queue timersetting. void qtractorMainForm::updateMidiQueueTimer (void) { if (m_pOptions == nullptr) return; // Configure the MIDI engine queue timer... m_pSession->midiEngine()->setAlsaTimer(m_pOptions->iMidiQueueTimer); } // Update MIDI playback drift correction. void qtractorMainForm::updateMidiDriftCorrect (void) { if (m_pOptions == nullptr) return; // Configure the MIDI engine drift correction... m_pSession->midiEngine()->setDriftCorrect(m_pOptions->bMidiDriftCorrect); } // Update MIDI player parameters. void qtractorMainForm::updateMidiPlayer (void) { if (m_pOptions == nullptr) return; // Configure the MIDI engine player handling... m_pSession->midiEngine()->setPlayerBus(m_pOptions->bMidiPlayerBus); } // Update MIDI control parameters. void qtractorMainForm::updateMidiControl (void) { if (m_pOptions == nullptr) return; // Configure the MIDI engine control handling... m_pSession->midiEngine()->setControlBus(m_pOptions->bMidiControlBus); } // Update audio metronome parameters. void qtractorMainForm::updateAudioMetronome (void) { if (m_pOptions == nullptr) return; // Configure the Audio engine metronome handling... qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine == nullptr) return; pAudioEngine->setMetroBarFilename(m_pOptions->sMetroBarFilename); pAudioEngine->setMetroBarGain(m_pOptions->fMetroBarGain); pAudioEngine->setMetroBeatFilename(m_pOptions->sMetroBeatFilename); pAudioEngine->setMetroBeatGain(m_pOptions->fMetroBeatGain); pAudioEngine->setMetroOffset(m_pOptions->iAudioMetroOffset); const bool bAudioMetronome = m_pOptions->bAudioMetronome; pAudioEngine->setMetroAutoConnect(m_pOptions->bAudioMetroAutoConnect); pAudioEngine->setMetroEnabled(bAudioMetronome); pAudioEngine->setMetroBus( bAudioMetronome && m_pOptions->bAudioMetroBus); pAudioEngine->setMetronome( bAudioMetronome && m_ui.transportMetroAction->isChecked()); pAudioEngine->setCountInMode( qtractorAudioEngine::CountInMode(m_pOptions->iAudioCountInMode)); pAudioEngine->setCountInBeats(m_pOptions->iAudioCountInBeats); pAudioEngine->setCountIn(m_pOptions->iAudioCountInMode > 0 && bAudioMetronome && m_ui.transportCountInAction->isChecked()); } // Update MIDI metronome parameters. void qtractorMainForm::updateMidiMetronome (void) { if (m_pOptions == nullptr) return; // Configure the MIDI engine metronome handling... qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine == nullptr) return; pMidiEngine->setMetroChannel(m_pOptions->iMetroChannel); pMidiEngine->setMetroBar( m_pOptions->iMetroBarNote, m_pOptions->iMetroBarVelocity, m_pOptions->iMetroBarDuration); pMidiEngine->setMetroBeat( m_pOptions->iMetroBeatNote, m_pOptions->iMetroBeatVelocity, m_pOptions->iMetroBeatDuration); pMidiEngine->setMetroOffset(m_pOptions->iMidiMetroOffset); const bool bMidiMetronome = m_pOptions->bMidiMetronome; pMidiEngine->setMetroEnabled(bMidiMetronome); pMidiEngine->setMetroBus( bMidiMetronome && m_pOptions->bMidiMetroBus); pMidiEngine->setMetronome( bMidiMetronome && m_ui.transportMetroAction->isChecked()); pMidiEngine->setCountInMode( qtractorMidiEngine::CountInMode(m_pOptions->iMidiCountInMode)); pMidiEngine->setCountInBeats(m_pOptions->iMidiCountInBeats); pMidiEngine->setCountIn(m_pOptions->iMidiCountInMode > 0 && bMidiMetronome && m_ui.transportCountInAction->isChecked()); } // Update transport display options. void qtractorMainForm::updateSyncViewHold (void) { if (m_pOptions == nullptr) return; if (m_pTracks) m_pTracks->trackView()->setSyncViewHold(m_pOptions->bSyncViewHold); // Update editors ... QListIterator iter(m_editors); while (iter.hasNext()) (iter.next()->editor())->setSyncViewHold(m_pOptions->bSyncViewHold); } // Track menu stabilizer. void qtractorMainForm::updateTrackMenu (void) { const bool bTracks = (m_pTracks && m_pSession->tracks().count() > 0); qtractorTrack *pTrack = (bTracks ? m_pTracks->currentTrack() : nullptr); const bool bEnabled = (pTrack != nullptr); const bool bPlaying = m_pSession->isPlaying(); const bool bRecording = m_pSession->isRecording(); const bool bRolling = (bPlaying && bRecording); // Update track menu state... m_ui.trackRemoveAction->setEnabled( bEnabled && (!bRolling || !pTrack->isRecord())); m_ui.trackDuplicateAction->setEnabled(bEnabled); m_ui.trackPropertiesAction->setEnabled( bEnabled && (!bRolling || !pTrack->isRecord())); m_ui.trackInputsAction->setEnabled( bEnabled && pTrack->inputBus() != nullptr); m_ui.trackOutputsAction->setEnabled( bEnabled && pTrack->outputBus() != nullptr); m_ui.trackStateMenu->setEnabled(bEnabled); m_ui.trackNavigateMenu->setEnabled(bTracks); m_ui.trackNavigateFirstAction->setEnabled(bTracks); m_ui.trackNavigatePrevAction->setEnabled(bTracks); m_ui.trackNavigateNextAction->setEnabled(bTracks); m_ui.trackNavigateLastAction->setEnabled(bTracks); m_ui.trackNavigateNoneAction->setEnabled(bEnabled); m_ui.trackMoveMenu->setEnabled(bEnabled); m_ui.trackMoveTopAction->setEnabled(bEnabled && pTrack->prev() != nullptr); m_ui.trackMoveUpAction->setEnabled(bEnabled && pTrack->prev() != nullptr); m_ui.trackMoveDownAction->setEnabled(bEnabled && pTrack->next() != nullptr); m_ui.trackMoveBottomAction->setEnabled(bEnabled && pTrack->next() != nullptr); m_ui.trackHeightMenu->setEnabled(bEnabled); m_ui.trackCurveMenu->setEnabled(bEnabled); // m_ui.trackImportAudioAction->setEnabled(m_pTracks != nullptr); // m_ui.trackImportMidiAction->setEnabled(m_pTracks != nullptr); // m_ui.trackAutoMonitorAction->setEnabled(m_pTracks != nullptr); m_ui.trackInstrumentMenu->setEnabled( bEnabled && pTrack->trackType() == qtractorTrack::Midi); // Update track menu state... if (bEnabled) { m_ui.trackStateRecordAction->setChecked(pTrack->isRecord()); m_ui.trackStateMuteAction->setChecked(pTrack->isMute()); m_ui.trackStateSoloAction->setChecked(pTrack->isSolo()); m_ui.trackStateMonitorAction->setChecked(pTrack->isMonitor()); } } // Curve/automation menu stabilizer. void qtractorMainForm::updateCurveMenu (void) { qtractorTrack *pTrack = (m_pTracks ? m_pTracks->currentTrack(): nullptr); qtractorCurveList *pCurveList = (pTrack ? pTrack->curveList() : nullptr); qtractorCurve *pCurrentCurve = (pCurveList ? pCurveList->currentCurve() : nullptr); bool bEnabled = trackCurveSelectMenuReset(m_ui.trackCurveSelectMenu); m_ui.trackCurveMenu->setEnabled(bEnabled); m_ui.trackCurveSelectMenu->setEnabled(bEnabled); if (bEnabled) bEnabled = (pCurveList && !pCurveList->isEmpty()); const bool bCurveEnabled = bEnabled && pCurrentCurve != nullptr; m_ui.trackCurveModeMenu->setEnabled(bCurveEnabled); m_ui.trackCurveLockedAction->setEnabled(bCurveEnabled); m_ui.trackCurveLockedAction->setChecked( pCurrentCurve && pCurrentCurve->isLocked()); m_ui.trackCurveProcessAction->setEnabled(bCurveEnabled); m_ui.trackCurveProcessAction->setChecked( pCurrentCurve && pCurrentCurve->isProcess()); m_ui.trackCurveCaptureAction->setEnabled(bCurveEnabled); m_ui.trackCurveCaptureAction->setChecked( pCurrentCurve && pCurrentCurve->isCapture()); m_ui.trackCurveClearAction->setEnabled(bCurveEnabled); m_ui.trackCurveLockedAllAction->setEnabled(bEnabled); m_ui.trackCurveLockedAllAction->setChecked( pCurveList && pCurveList->isLockedAll()); m_ui.trackCurveLockedAllAction->setEnabled(bEnabled); m_ui.trackCurveProcessAllAction->setEnabled(bEnabled); m_ui.trackCurveProcessAllAction->setChecked( pCurveList && pCurveList->isProcessAll()); m_ui.trackCurveProcessAllAction->setEnabled(bEnabled); m_ui.trackCurveCaptureAllAction->setEnabled(bEnabled); m_ui.trackCurveCaptureAllAction->setChecked( pCurveList && pCurveList->isCaptureAll()); m_ui.trackCurveCaptureAllAction->setEnabled(bEnabled); m_ui.trackCurveClearAllAction->setEnabled( pCurveList && !pCurveList->isEmpty()); } // Curve/automation mode menu stabilizer. void qtractorMainForm::updateCurveModeMenu (void) { trackCurveModeMenuReset(m_ui.trackCurveModeMenu); } // Track curve/automation select item builder. void qtractorMainForm::trackCurveSelectMenuAction ( QMenu *pMenu, qtractorMidiControlObserver *pObserver, qtractorSubject *pCurrentSubject ) const { QIcon icon(QIcon::fromTheme("trackCurveNone")); QString text(tr("&None")); qtractorSubject *pSubject = (pObserver ? pObserver->subject() : nullptr); if (pSubject) { text = pSubject->name(); qtractorCurve *pCurve = pSubject->curve(); if (pCurve) { if (pCurve->isCapture()) icon = QIcon::fromTheme("trackCurveCapture"); else if (pCurve->isProcess()) icon = QIcon::fromTheme("trackCurveProcess"); else // if (pCurve->isEnabled()) icon = QIcon::fromTheme("trackCurveEnabled"); text += '*'; } } QAction *pAction = pMenu->addAction(icon, text); pAction->setCheckable(true); pAction->setChecked(pCurrentSubject == pSubject); pAction->setData(QVariant::fromValue(pObserver)); } // Track curve/automation select menu builder. bool qtractorMainForm::trackCurveSelectMenuReset ( QMenu *pMenu ) const { pMenu->clear(); if (m_pTracks == nullptr) return false; qtractorTrack *pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return false; qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList == nullptr) return false; qtractorMonitor *pMonitor = pTrack->monitor(); if (pMonitor == nullptr) return false; qtractorCurve *pCurrentCurve = pCurveList->currentCurve(); qtractorSubject *pCurrentSubject = (pCurrentCurve ? pCurrentCurve->subject() : nullptr); trackCurveSelectMenuAction(pMenu, pTrack->monitorObserver(), pCurrentSubject); trackCurveSelectMenuAction(pMenu, pMonitor->panningObserver(), pCurrentSubject); trackCurveSelectMenuAction(pMenu, pMonitor->gainObserver(), pCurrentSubject); pMenu->addSeparator(); trackCurveSelectMenuAction(pMenu, pTrack->recordObserver(), pCurrentSubject); trackCurveSelectMenuAction(pMenu, pTrack->muteObserver(), pCurrentSubject); trackCurveSelectMenuAction(pMenu, pTrack->soloObserver(), pCurrentSubject); qtractorPluginList *pPluginList = pTrack->pluginList(); if (pPluginList->count() > 0) { pMenu->addSeparator(); qtractorPlugin *pPlugin = pPluginList->first(); while (pPlugin) { QMenu *pPluginMenu = pMenu->addMenu(pPlugin->title()); trackCurveSelectMenuAction(pPluginMenu, pPlugin->activateObserver(), pCurrentSubject); const qtractorPlugin::Params& params = pPlugin->params(); if (params.count() > 0) { pPluginMenu->addSeparator(); qtractorPlugin::Params::ConstIterator param = params.constBegin(); const qtractorPlugin::Params::ConstIterator& param_end = params.constEnd(); for ( ; param != param_end; ++param) { trackCurveSelectMenuAction(pPluginMenu, param.value()->observer(), pCurrentSubject); } } const qtractorPlugin::PropertyKeys& props = pPlugin->propertyKeys(); if (props.count() > 0) { pPluginMenu->addSeparator(); qtractorPlugin::PropertyKeys::ConstIterator prop = props.constBegin(); const qtractorPlugin::PropertyKeys::ConstIterator& prop_end = props.constEnd(); for ( ; prop != prop_end; ++prop) { qtractorPlugin::Property *pProp = prop.value(); if (pProp->isAutomatable()) { trackCurveSelectMenuAction(pPluginMenu, pProp->observer(), pCurrentSubject); } } } pPlugin = pPlugin->next(); } } pMenu->addSeparator(); trackCurveSelectMenuAction(pMenu, nullptr, pCurrentSubject); return true; } bool qtractorMainForm::trackCurveModeMenuReset ( QMenu *pMenu ) const { pMenu->clear(); qtractorTrack *pTrack = m_pTracks->currentTrack(); if (pTrack == nullptr) return false; qtractorCurve *pCurrentCurve = pTrack->currentCurve(); if (pCurrentCurve == nullptr) return false; const qtractorCurve::Mode mode = pCurrentCurve->mode(); const bool bDecimal = (pCurrentCurve->subject())->isDecimal(); QAction *pAction; pAction = pMenu->addAction(tr("&Hold")); pAction->setCheckable(true); pAction->setChecked(mode == qtractorCurve::Hold); pAction->setData(int(qtractorCurve::Hold)); pAction = pMenu->addAction(tr("&Linear")); pAction->setCheckable(true); pAction->setChecked(mode == qtractorCurve::Linear); pAction->setData(int(qtractorCurve::Linear)); pAction->setEnabled(bDecimal); pAction = pMenu->addAction(tr("&Spline")); pAction->setCheckable(true); pAction->setChecked(mode == qtractorCurve::Spline); pAction->setData(int(qtractorCurve::Spline)); pAction->setEnabled(bDecimal); pMenu->addSeparator(); pMenu->addAction(m_ui.trackCurveLogarithmicAction); m_ui.trackCurveLogarithmicAction->setChecked( pCurrentCurve && pCurrentCurve->isLogarithmic()); m_ui.trackCurveLogarithmicAction->setData(-1); m_ui.trackCurveLogarithmicAction->setEnabled(bDecimal); pMenu->addAction(m_ui.trackCurveColorAction); m_ui.trackCurveColorAction->setData(-1); return true; } // Clip menu stabilizer. void qtractorMainForm::updateClipMenu (void) { const unsigned long iPlayHead = m_pSession->playHead(); qtractorClip *pClip = nullptr; qtractorTrack *pTrack = nullptr; const bool bTracks = (m_pTracks && m_pSession->tracks().count() > 0); if (bTracks) { pClip = m_pTracks->currentClip(); pTrack = (pClip ? pClip->track() : m_pTracks->currentTrack()); } const bool bEnabled = (pTrack != nullptr); const bool bSelected = (m_pTracks && m_pTracks->isSelected()); const bool bClipSelected = (pClip != nullptr) || (m_pTracks && m_pTracks->isClipSelected()); const bool bClipSelectable = bClipSelected && (m_pSession->editHead() < m_pSession->editTail()); const bool bSingleTrackSelected = bClipSelected && (pTrack && m_pTracks->singleTrackSelected() == pTrack); m_ui.editCutAction->setEnabled(bSelected); m_ui.editCopyAction->setEnabled(bSelected); m_ui.editDeleteAction->setEnabled(bSelected); m_ui.clipNewAction->setEnabled(bEnabled); m_ui.clipEditAction->setEnabled(pClip != nullptr); m_ui.clipMuteAction->setEnabled(bClipSelected); m_ui.clipMuteAction->setChecked(pClip && pClip->isClipMute()); // Special unlink (MIDI) clip... qtractorMidiClip *pMidiClip = nullptr; if (pTrack && pTrack->trackType() == qtractorTrack::Midi) pMidiClip = static_cast (pClip); m_ui.clipUnlinkAction->setEnabled(pMidiClip && pMidiClip->isHashLinked()); m_ui.clipRecordExAction->setEnabled(pMidiClip != nullptr); m_ui.clipRecordExAction->setChecked(pTrack && pTrack->isClipRecordEx() && static_cast (pTrack->clipRecord()) == pMidiClip); m_ui.clipSplitAction->setEnabled(bClipSelected || pTrack != nullptr || (pClip != nullptr && iPlayHead > pClip->clipStart() && iPlayHead < pClip->clipStart() + pClip->clipLength())); m_ui.clipMergeAction->setEnabled(bSingleTrackSelected); m_ui.clipNormalizeAction->setEnabled(bClipSelected); m_ui.clipTempoAdjustAction->setEnabled(bClipSelected); m_ui.clipCrossFadeAction->setEnabled(bClipSelected); m_ui.clipRangeSetAction->setEnabled(bClipSelected); m_ui.clipLoopSetAction->setEnabled(bClipSelected); // m_ui.clipImportAction->setEnabled(bTracks); m_ui.clipExportAction->setEnabled(bSingleTrackSelected); const bool bClipToolsEnabled = bClipSelected && pTrack && pTrack->trackType() == qtractorTrack::Midi; m_ui.clipToolsMenu->setEnabled(bClipToolsEnabled); if (bClipToolsEnabled) { m_ui.clipToolsTimeshiftAction->setEnabled(bClipSelectable); m_ui.clipToolsTemporampAction->setEnabled(bClipSelectable); } m_ui.clipTakeMenu->setEnabled(pClip != nullptr); if (pClip) updateTakeMenu(); } // Take menu stabilizers. void qtractorMainForm::updateTakeMenu (void) { qtractorClip *pClip = nullptr; if (m_pTracks) pClip = m_pTracks->currentClip(); qtractorClip::TakeInfo *pTakeInfo = (pClip ? pClip->takeInfo() : nullptr); const int iCurrentTake = (pTakeInfo ? pTakeInfo->currentTake() : -1); const int iTakeCount = (pTakeInfo ? pTakeInfo->takeCount() : 0); // m_ui.clipTakeMenu->setEnabled(pTakeInfo != nullptr); m_ui.clipTakeSelectMenu->setEnabled(iTakeCount > 0); m_ui.clipTakeFirstAction->setEnabled(iCurrentTake != 0 && iTakeCount > 0); m_ui.clipTakePrevAction->setEnabled(iTakeCount > 0); m_ui.clipTakeNextAction->setEnabled(iTakeCount > 0); m_ui.clipTakeLastAction->setEnabled(iCurrentTake < iTakeCount - 1); m_ui.clipTakeResetAction->setEnabled(iCurrentTake >= 0); m_ui.clipTakeRangeAction->setEnabled(pTakeInfo == nullptr); } // Take selection menu stabilizer. void qtractorMainForm::updateTakeSelectMenu (void) { m_ui.clipTakeSelectMenu->clear(); qtractorClip *pClip = nullptr; if (m_pTracks) pClip = m_pTracks->currentClip(); qtractorClip::TakeInfo *pTakeInfo = (pClip ? pClip->takeInfo() : nullptr); if (pTakeInfo) { QAction *pAction; const int iCurrentTake = pTakeInfo->currentTake(); const int iTakeCount = pTakeInfo->takeCount(); for (int iTake = 0; iTake < iTakeCount; ++iTake) { pAction = m_ui.clipTakeSelectMenu->addAction( tr("Take %1").arg(iTake + 1)); pAction->setCheckable(true); pAction->setChecked(iTake == iCurrentTake); pAction->setData(iTake); } if (iTakeCount > 0) m_ui.clipTakeSelectMenu->addSeparator(); pAction = m_ui.clipTakeSelectMenu->addAction(tr("None")); pAction->setCheckable(true); pAction->setChecked(iCurrentTake < 0); pAction->setData(-1); } } // Zoom view menu stabilizer. void qtractorMainForm::updateZoomMenu (void) { int iZoomMode = qtractorTracks::ZoomNone; if (m_pTracks) iZoomMode = m_pTracks->zoomMode(); m_ui.viewZoomHorizontalAction->setChecked( iZoomMode == qtractorTracks::ZoomHorizontal); m_ui.viewZoomVerticalAction->setChecked( iZoomMode == qtractorTracks::ZoomVertical); m_ui.viewZoomAllAction->setChecked( iZoomMode == qtractorTracks::ZoomAll); } // Snap-per-beat view menu builder. void qtractorMainForm::updateSnapMenu (void) { m_ui.viewSnapMenu->clear(); const int iSnapCurrent = qtractorTimeScale::indexFromSnap(m_pSession->snapPerBeat()); int iSnap = 0; QListIterator iter(m_snapPerBeatActions); while (iter.hasNext()) { QAction *pAction = iter.next(); pAction->setChecked(iSnap == iSnapCurrent); m_ui.viewSnapMenu->addAction(pAction); ++iSnap; } m_ui.viewSnapMenu->addSeparator(); m_ui.viewSnapMenu->addAction(m_ui.viewSnapZebraAction); m_ui.viewSnapMenu->addAction(m_ui.viewSnapGridAction); } // Track/Instrument sub-menu stabilizers. void qtractorMainForm::updateTrackInstrumentMenu (void) { if (m_pTracks) { m_pInstrumentMenu->updateTrackMenu( m_pTracks->currentTrack(), m_ui.trackInstrumentMenu); } } //------------------------------------------------------------------------- // qtractorMainForm -- Messages window form handlers. // Messages output methods. void qtractorMainForm::appendMessages( const QString& s ) { if (m_pMessages) m_pMessages->appendMessages(s); statusBar()->showMessage(s, 3000); } void qtractorMainForm::appendMessagesColor( const QString& s, const QColor& rgb ) { if (m_pMessages) m_pMessages->appendMessagesColor(s, rgb); statusBar()->showMessage(s, 3000); } void qtractorMainForm::appendMessagesText( const QString& s ) { if (m_pMessages) m_pMessages->appendMessagesText(s); } void qtractorMainForm::appendMessagesError( const QString& s ) { if (m_pMessages) m_pMessages->show(); appendMessagesColor(s.simplified(), Qt::red); QMessageBox::critical(this, tr("Error"), s); } // Force update of the messages font. void qtractorMainForm::updateMessagesFont (void) { if (m_pOptions == nullptr) return; if (m_pMessages && !m_pOptions->sMessagesFont.isEmpty()) { QFont font; if (font.fromString(m_pOptions->sMessagesFont)) m_pMessages->setMessagesFont(font); } } // Update messages window line limit. void qtractorMainForm::updateMessagesLimit (void) { if (m_pOptions == nullptr) return; if (m_pMessages) { if (m_pOptions->bMessagesLimit) m_pMessages->setMessagesLimit(m_pOptions->iMessagesLimitLines); else m_pMessages->setMessagesLimit(-1); } } // Enablement of the messages capture feature. void qtractorMainForm::updateMessagesCapture (void) { if (m_pOptions == nullptr) return; if (m_pMessages) m_pMessages->setCaptureEnabled(m_pOptions->bStdoutCapture); } // Update/reset custome color (palette) theme.. void qtractorMainForm::updateCustomColorTheme (void) { if (m_pOptions == nullptr) return; if (m_pOptions->sCustomColorTheme.isEmpty()) return; QPalette pal(QApplication::palette()); if (qtractorPaletteForm::namedPalette( &m_pOptions->settings(), m_pOptions->sCustomColorTheme, pal)) { QApplication::setPalette(pal); if (m_pTracks) m_pTracks->trackList()->updateTrackButtons(); if (m_pMixer) { m_pMixer->updateTracks(true); m_pMixer->updateBuses(true); } QTimer::singleShot(QTRACTOR_TIMER_MSECS, this, SLOT(viewRefresh())); } } // Update/reset custom (widget) style theme.. void qtractorMainForm::updateCustomStyleTheme (void) { if (m_pOptions == nullptr) return; if (m_pOptions->sCustomStyleTheme.isEmpty()) return; QApplication::setStyle( QStyleFactory::create(m_pOptions->sCustomStyleTheme)); } // Update/reset custom style sheet (QSS).. void qtractorMainForm::updateCustomStyleSheet (void) { if (m_pOptions == nullptr) return; qApp->setStyleSheet(styleSheet(m_pOptions->sCustomStyleSheet)); } //------------------------------------------------------------------------- // qtractorMainForm -- Editors stuff. void qtractorMainForm::addEditorForm ( qtractorMidiEditorForm *pEditorForm ) { if (m_editors.indexOf(pEditorForm) < 0) m_editors.append(pEditorForm); } void qtractorMainForm::removeEditorForm ( qtractorMidiEditorForm *pEditorForm ) { const int iEditorForm = m_editors.indexOf(pEditorForm); if (iEditorForm >= 0) m_editors.removeAt(iEditorForm); } void qtractorMainForm::updateEditorForms (void) { if (m_pOptions == nullptr) return; Qt::WindowFlags wflags = Qt::Window; if (m_pOptions->bKeepEditorsOnTop) { wflags |= Qt::Tool; wflags |= Qt::WindowStaysOnTopHint; } QListIterator iter(m_editors); while (iter.hasNext()) { qtractorMidiEditorForm *pForm = iter.next(); const bool bVisible = pForm->isVisible(); #if 0//QTRACTOR_MIDI_EDITOR_TOOL_PARENT if (m_pOptions->bKeepEditorsOnTop) pForm->setParent(this); else pForm->setParent(nullptr); #endif pForm->setWindowFlags(wflags); if (bVisible) pForm->show(); } } //------------------------------------------------------------------------- // qtractorMainForm -- Timer stuff. // Fast-timer slot funtion. void qtractorMainForm::fastTimerSlot (void) { // Currrent state... const bool bPlaying = m_pSession->isPlaying(); long iPlayHead = long(m_pSession->playHead()); qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); // Playhead status... if (iPlayHead != long(m_iPlayHead)) { // Update tracks-view play-head... m_pTracks->trackView()->setPlayHead(iPlayHead, m_ui.transportFollowAction->isChecked()); // Update editors play-head... QListIterator iter(m_editors); while (iter.hasNext()) (iter.next()->editor())->setPlayHead(iPlayHead); // Update transport status anyway... if (!bPlaying && m_iTransportRolling == 0 && m_iTransportStep == 0) { ++m_iTransportUpdate; // Send MMC LOCATE and MIDI SPP command... if (!pAudioEngine->isFreewheel()) { pMidiEngine->sendMmcLocate( m_pSession->locateFromFrame(iPlayHead)); pMidiEngine->sendSppCommand(SND_SEQ_EVENT_SONGPOS, m_pSession->songPosFromFrame(iPlayHead)); } } else if (iPlayHead < long(m_iPlayHead) && m_pSession->isLooping()) { // On probable loop turn-around: // Send MMC LOCATE command... pMidiEngine->sendMmcLocate( m_pSession->locateFromFrame(iPlayHead)); pMidiEngine->sendSppCommand(SND_SEQ_EVENT_SONGPOS, m_pSession->songPosFromFrame(iPlayHead)); } // Current position update... m_iPlayHead = iPlayHead; } // Transport status... if (m_iTransportUpdate > 0) { // Do some transport related tricks... if (m_iTransportRolling == 0) { m_iTransportUpdate = 0; if (m_iTransportStep) { // Transport stepping over... iPlayHead += (m_iTransportStep * long(m_pSession->frameFromTick(m_pSession->ticksPerBeat()))); if (iPlayHead < 0) iPlayHead = 0; m_iTransportStep = 0; // Make it thru... m_pSession->setPlayHead(m_pSession->frameSnap(iPlayHead)); } } else { // Transport rolling over... iPlayHead += long(m_fTransportShuttle * float(m_pSession->sampleRate())) >> 1; if (iPlayHead < 0) { iPlayHead = 0; m_iTransportUpdate = 0; // Stop playback for sure... setPlaying(false); } // Make it thru... m_pSession->setPlayHead(iPlayHead); } // Ensure track-view into visibility... if (m_ui.transportFollowAction->isChecked()) m_pTracks->trackView()->ensureVisibleFrame(iPlayHead); // Take the chance to give some visual feedback... if (m_iTransportUpdate > 0) { updateTransportTime(iPlayHead); m_pThumbView->updateThumb(); } // Ensure the main form is stable later on... ++m_iStabilizeTimer; // Done with transport tricks. } // Always update meter values... qtractorMeterValue::refreshAll(); // Asynchronous observer update... if (qtractorSubject::flushQueue(true)) ++m_iStabilizeTimer; #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_TIME // Update plugin LV2 Time designated ports, if any... qtractorLv2Plugin::updateTimePost(); #endif #ifdef CONFIG_LV2_UI // Crispy plugin LV2 UI idle-updates... qtractorLv2Plugin::idleEditorAll(); #endif #endif #ifdef CONFIG_CLAP // Crispy plugin CLAP UI idle-updates... qtractorClapPlugin::idleEditorAll(); #endif #ifdef CONFIG_VST2 // Crispy plugin VST2 UI idle-updates... qtractorVst2Plugin::idleEditorAll(); #endif // Register the next fast-timer slot. QTimer::singleShot(QTRACTOR_TIMER_MSECS, this, SLOT(fastTimerSlot())); } // Slow-timer slot funtion. void qtractorMainForm::slowTimerSlot (void) { // Avoid stabilize re-entrancy... if (m_pSession->isBusy() || m_iTransportUpdate > 0) { // Register the next timer slot. QTimer::singleShot(QTRACTOR_TIMER_DELAY, this, SLOT(slowTimerSlot())); return; } // Currrent state... const bool bPlaying = m_pSession->isPlaying(); unsigned long iPlayHead = m_pSession->playHead(); qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); // Read JACK transport state... jack_client_t *pJackClient = pAudioEngine->jackClient(); if (pJackClient && !pAudioEngine->isFreewheel()) { jack_position_t pos; jack_transport_state_t state = jack_transport_query(pJackClient, &pos); // React if out-of-sync... if (pAudioEngine->transportMode() & qtractorBus::Input) { // 1. Check on external transport state request changes... if ((state == JackTransportStopped && bPlaying) || (state == JackTransportRolling && !bPlaying)) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::slowTimerSlot() playing=%d state=%d", int(bPlaying), int(state == JackTransportRolling)); #endif iPlayHead = pos.frame; transportPlay(); // Toggle playing! if (!bPlaying) m_pSession->seek(iPlayHead, true); } // 2. Watch for tempo/time-sig changes on JACK transport... if ((pos.valid & JackPositionBBT) && pAudioEngine->isTimebaseEx()) { qtractorTimeScale *pTimeScale = m_pSession->timeScale(); qtractorTimeScale::Cursor& cursor = pTimeScale->cursor(); qtractorTimeScale::Node *pNode = cursor.seekFrame(pos.frame); if (pNode && pos.frame >= pNode->frame && ( qAbs(pNode->tempo - pos.beats_per_minute) > 0.001f || pNode->beatsPerBar != (unsigned short) pos.beats_per_bar || (1 << pNode->beatDivisor) != (unsigned short) pos.beat_type)) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::slowTimerSlot() tempo=%g %u/%u", pos.beats_per_minute, (unsigned short) pos.beats_per_bar, (unsigned short) pos.beat_type); #endif m_pTracks->clearSelect(true); m_pSession->lock(); pNode->tempo = pos.beats_per_minute; pNode->beatsPerBar = pos.beats_per_bar; pNode->beatDivisor = 0; unsigned short i = pos.beat_type; while (i > 1) { ++(pNode->beatDivisor); i >>= 1; } pTimeScale->updateNode(pNode); m_pTempoSpinBox->setTempo(pNode->tempo, false); m_pTempoSpinBox->setBeatsPerBar(pNode->beatsPerBar, false); m_pTempoSpinBox->setBeatDivisor(pNode->beatDivisor, false); pAudioEngine->resetMetro(); pMidiEngine->resetTempo(); m_pSession->unlock(); updateContents(nullptr, true); } } } } // Check if its time to refresh playhead timer... if (bPlaying) { updateTransportTime(iPlayHead); // If recording update track view and session length, anyway... if (m_pSession->isRecording()) { // HACK: Care of punch-out... if (m_pSession->isPunching()) { const unsigned long iFrameTime = m_pSession->frameTimeEx(); const unsigned long iLoopStart = m_pSession->loopStart(); const unsigned long iLoopEnd = m_pSession->loopEnd(); unsigned long iPunchOut = m_pSession->punchOut(); if (iLoopStart < iLoopEnd && iLoopStart < iFrameTime && m_pSession->loopRecordingMode() > 0) { const unsigned long iLoopLength = iLoopEnd - iLoopStart; const unsigned long iLoopCount = (iFrameTime - iLoopStart) / iLoopLength; iPunchOut += (iLoopCount + 1) * iLoopLength; } // Make sure it's really about to punch-out... if (iFrameTime > iPunchOut && setRecording(false)) { // Send MMC RECORD_EXIT command... pMidiEngine->sendMmcCommand( qtractorMmcEvent::RECORD_EXIT); ++m_iTransportUpdate; } } // Recording visual feedback... m_pTracks->updateContentsRecord(); m_pSession->updateSession(0, iPlayHead); m_statusItems[StatusTime]->setText( m_pSession->timeScale()->textFromFrame( 0, true, m_pSession->sessionEnd())); } else // Whether to continue past end... if (!m_ui.transportContinueAction->isChecked()) { unsigned long iSessionEnd = m_pSession->sessionEnd(); if (m_pSession->isLooping()) { const unsigned long iLoopEnd = m_pSession->loopEnd(); if (iSessionEnd < iLoopEnd) iSessionEnd = iLoopEnd; } if (iPlayHead > iSessionEnd) transportPlay(); // Stop at once! } } // Check if we've got some XRUN callbacks... if (m_iXrunTimer > 0 && --m_iXrunTimer < 1) { m_iXrunTimer = 0; // Reset audio/MIDI drift correction... if (bPlaying) pMidiEngine->resetDrift(); // Did we skip any? if (m_iXrunSkip > 0) { appendMessagesColor( tr("XRUN(%1 skipped)").arg(m_iXrunSkip), "#cc99cc"); m_iXrunSkip = 0; } // Just post an informative message... appendMessagesColor( tr("XRUN(%1): some frames might have been lost.") .arg(m_iXrunCount), "#cc0033"); // Let the XRUN status item get an update... ++m_iStabilizeTimer; } // Check if its time to refresh some tracks... if (m_iAudioPeakTimer > 0 && --m_iAudioPeakTimer < 1) { m_iAudioPeakTimer = 0; m_pTracks->trackView()->updateContents(); } // Check if its time to refresh Audio connections... if (m_iAudioRefreshTimer > 0 && --m_iAudioRefreshTimer < 1) { m_iAudioRefreshTimer = 0; if (pAudioEngine->updateConnects() == 0) { appendMessagesColor( tr("Audio connections change."), "#cc9966"); if (m_iAudioPropertyChange > 0) { m_iAudioPropertyChange = 0; m_pConnections->connectForm()->audioClear(); } else { m_pConnections->connectForm()->audioRefresh(); } if (m_iAudioSelfConnected > 0) { m_iAudioSelfConnected = 0; const QString& sTitle = tr("Warning"); const QString& sText = tr("Audio self-connection detected!\n\n" "In general, connecting an output bus (or insert send),\n" "directly into any input bus (or insert return),\n" "is not advisable. It often doesn't work, if at all.\n"); #if 0 QMessageBox::warning(this, sTitle, sText); #else if (m_pOptions->bAudioSelfConnected) { QMessageBox mbox(this); mbox.setIcon(QMessageBox::Warning); mbox.setWindowTitle(sTitle); mbox.setText(sText); mbox.setStandardButtons(QMessageBox::Ok); QCheckBox cbox(tr("Don't show this again")); cbox.setChecked(false); cbox.blockSignals(true); mbox.addButton(&cbox, QMessageBox::ActionRole); mbox.exec();// == QMessageBox::Ok if (cbox.isChecked()) m_pOptions->bAudioSelfConnected = false; } #endif } } } // MIDI connections should be checked too... if (m_iMidiRefreshTimer > 0 && --m_iMidiRefreshTimer < 1) { m_iMidiRefreshTimer = 0; if (pMidiEngine->updateConnects() == 0) { appendMessagesColor( tr("MIDI connections change."), "#66cc99"); m_pConnections->connectForm()->midiRefresh(); } } // Check if its time to refresh audition/pre-listening status... if (m_iPlayerTimer > 0 && --m_iPlayerTimer < 1) { m_iPlayerTimer = 0; const bool bPlayerOpen = (pAudioEngine->isPlayerOpen() || pMidiEngine->isPlayerOpen()); if (bPlayerOpen) { if ((m_pFiles && m_pFiles->isPlayState()) && (m_pFileSystem && m_pFileSystem->isPlayState())) ++m_iPlayerTimer; } if (m_iPlayerTimer < 1) { if (m_pFiles && m_pFiles->isPlayState()) m_pFiles->setPlayState(false); if (m_pFileSystem && m_pFileSystem->isPlayState()) m_pFileSystem->setPlayState(false); if (bPlayerOpen) { appendMessages(tr("Playing ended.")); pAudioEngine->closePlayer(); pMidiEngine->closePlayer(); } } } // Slower plugin UI idle cycle... #ifdef CONFIG_DSSI #ifdef CONFIG_LIBLO qtractorDssiPlugin::idleEditorAll(); #endif #endif // Auto-save option routine... if (m_iAutoSavePeriod > 0 && m_iDirtyCount > 0) { m_iAutoSaveTimer += QTRACTOR_TIMER_DELAY; if (m_iAutoSaveTimer > m_iAutoSavePeriod && !bPlaying) { m_iAutoSaveTimer = 0; autoSaveSession(); } } // Check if its time to stabilize main form... if (m_iStabilizeTimer > 0/* && --m_iStabilizeTimer < 1 */) { m_iStabilizeTimer = 0; stabilizeForm(); } // Register the next slow-timer slot. QTimer::singleShot(QTRACTOR_TIMER_DELAY, this, SLOT(slowTimerSlot())); } //------------------------------------------------------------------------- // qtractorMainForm -- MIDI engine notifications. // ALSA sequencer notification slot. void qtractorMainForm::alsaNotify (void) { // This specialty needs acknowledgement... m_pSession->midiEngine()->alsaNotifyAck(); // A MIDI graph change has just been occurred; // try to postpone the event effect a little more... if (m_iMidiRefreshTimer < 2) ++m_iMidiRefreshTimer; } // Audio file peak notification slot. void qtractorMainForm::audioPeakNotify (void) { // An audio peak file has just been (re)created; // try to postpone the event effect a little more... if (m_iAudioPeakTimer < 2) ++m_iAudioPeakTimer; } // Custom audio shutdown event handler. void qtractorMainForm::audioShutNotify (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::audioShutNotify()"); #endif // HACK: The audio engine (jackd) most probably // is down, not up and running anymore, anyway... m_pSession->lock(); // Always do auto-save here, hence... autoSaveSession(); // Engines shutdown is on demand... m_pSession->shutdown(); m_pConnections->clear(); // HACK: Done. m_pSession->unlock(); // Send an informative message box... appendMessagesError( tr("The audio engine has been shutdown.\n\n" "Make sure the JACK audio server (jackd)\n" "is up and running and then restart session.")); // Try an immediate restart, though... checkRestartSession(); // Make things just bearable... ++m_iStabilizeTimer; } // Custom audio XRUN event handler. void qtractorMainForm::audioXrunNotify (void) { // An XRUN has just been notified... ++m_iXrunCount; // Skip this one, maybe we're under some kind of storm; if (m_iXrunTimer > 0) ++m_iXrunSkip; // Defer the informative effect... ++m_iXrunTimer; } // Custom audio port/graph change event handler. void qtractorMainForm::audioPortNotify (void) { // An Audio graph change has just been issued; // try to postpone the event effect a little more... if (m_iAudioRefreshTimer < 2) ++m_iAudioRefreshTimer; } // Custom audio buffer size change event handler. void qtractorMainForm::audioBuffNotify ( unsigned int iBufferSize ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::audioBuffNotify(%u)", iBufferSize); #endif qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine == nullptr) return; qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine == nullptr) return; // HACK: The audio engine (jackd) is still up // and running, just with bigger buffer size... m_pSession->lock(); // Always do auto-save here, hence... autoSaveSession(); // Connections snapshot stuff... qtractorBus::Connections audio_connections; qtractorBus::Connections midi_connections; // Get all connections snapshot... audio_connections.save(pAudioEngine); midi_connections.save(pMidiEngine); // Engines shutdown is on demand... m_pSession->shutdown(); m_pConnections->clear(); // HACK: Done. m_pSession->unlock(); // Send an informative message box... appendMessagesError( tr("The audio engine buffer size has changed,\n" "increased from %1 to %2 frames/period.\n\n" "Reloading the current session file\n" "is highly recommended.") .arg(pAudioEngine->bufferSize()) .arg(iBufferSize)); #if 0 // Reload the previously auto-saved session... const QString& sAutoSavePathname = m_pOptions->sAutoSavePathname; if (!sAutoSavePathname.isEmpty() && QFileInfo(sAutoSavePathname).exists()) { // Reset (soft) subject/observer queue. qtractorSubject::resetQueue(); // Reset all dependables to default. m_pMixer->clear(); m_pFiles->clear(); // Close session engines. m_pSession->close(); m_pSession->clear(); m_pTempoCursor->clear(); // And last but not least. m_pConnections->clear(); m_pTracks->clear(); // Clear (hard) subject/observer queue. qtractorSubject::clearQueue(); // Reset playhead. m_iPlayHead = 0; #ifdef CONFIG_LV2 qtractorLv2PluginType::lv2_close(); #endif // Reload the auto-saved session alright... const int iFlags = qtractorDocument::Default; if (loadSessionFileEx(sAutoSavePathname, iFlags, false)) { m_sFilename = m_pOptions->sAutoSaveFilename; ++m_iDirtyCount; } // Final view update, just in case... selectionNotifySlot(nullptr); } else #else // Try an immediate restart, and restore connections snapshot... if (checkRestartSession()) { audio_connections.load(pAudioEngine); midi_connections.load(pMidiEngine); } // Shall make it dirty anyway... updateDirtyCount(true); #endif // Make things just bearable... ++m_iStabilizeTimer; } #ifdef CONFIG_JACK_SESSION #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #endif // Custom (JACK) session event handler. void qtractorMainForm::audioSessNotify ( void *pvSessionArg ) { #ifdef CONFIG_JACK_SESSION if (m_pOptions == nullptr) return; qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine == nullptr) return; jack_client_t *pJackClient = pAudioEngine->jackClient(); if (pJackClient == nullptr) return; jack_session_event_t *pJackSessionEvent = (jack_session_event_t *) pvSessionArg; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::audioSessNotify()" " type=%d client_uuid=\"%s\" session_dir=\"%s\"", int(pJackSessionEvent->type), pJackSessionEvent->client_uuid, pJackSessionEvent->session_dir); #endif const bool bTemplate = (pJackSessionEvent->type == JackSessionSaveTemplate); const bool bQuit = (pJackSessionEvent->type == JackSessionSaveAndQuit); const QString sOldSessionDir = m_pSession->sessionDir(); const QString sSessionDir = QString::fromUtf8(pJackSessionEvent->session_dir); m_pSession->setSessionDir(sSessionDir); if (m_pSession->sessionName().isEmpty()) m_pSession->setSessionName(::getenv("LADISH_PROJECT_NAME")); if (m_pSession->sessionName().isEmpty()) editSession(); QString sSessionName = m_pSession->sessionName(); if (sSessionName.isEmpty()) sSessionName = tr("Untitled%1").arg(m_iUntitled); const QString sSessionExt = (bTemplate ? qtractorDocument::templateExt() : m_pOptions->sSessionExt); const QString sSessionFile = sSessionName + '.' + sSessionExt; QStringList args; args << QApplication::applicationFilePath(); args << QString("--session-id=%1").arg(pJackSessionEvent->client_uuid); const QString sFilename = QFileInfo(sSessionDir, sSessionFile).absoluteFilePath(); const int iFlags = (bTemplate ? qtractorDocument::Template : qtractorDocument::Default); if (saveSessionFileEx(sFilename, iFlags | qtractorDocument::SymLink, false)) args << QString("${SESSION_DIR}%1").arg(sSessionFile); const QByteArray aCmdLine = args.join(" ").toUtf8(); pJackSessionEvent->command_line = ::strdup(aCmdLine.constData()); jack_session_reply(pJackClient, pJackSessionEvent); jack_session_event_free(pJackSessionEvent); m_pSession->setSessionDir(sOldSessionDir); if (bQuit) { m_iDirtyCount = 0; close(); } #endif // CONFIG_JACK_SESSION } #ifdef CONFIG_JACK_SESSION #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic pop #endif #endif // Custom (JACK) transport sync event handler. void qtractorMainForm::audioSyncNotify ( unsigned long iPlayHead, bool bPlaying ) { if (m_pSession->isBusy()) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::audioSyncNotify(%lu, %d)", iPlayHead, int(bPlaying)); #endif if (( bPlaying && !m_pSession->isPlaying()) || (!bPlaying && m_pSession->isPlaying())) { // Toggle playing! transportPlay(); } m_pSession->setPlayHeadEx(iPlayHead); ++m_iTransportUpdate; } // Custom (JACK) audio property change event handler. void qtractorMainForm::audioPropNotify (void) { // An Audio property change has just been issued; // try to postpone the event effect a little more... if (m_iAudioRefreshTimer < 2) ++m_iAudioRefreshTimer; // Mark that a complete refresh is needed... ++m_iAudioPropertyChange; } // Custom (JACK) audio port selft-connect event handler. void qtractorMainForm::audioSelfNotify (void) { // An Audio port connection was made to ourselves; // try to postpone the event effect a little more... if (m_iAudioRefreshTimer < 2) ++m_iAudioRefreshTimer; // Mark that a complete refresh is needed... ++m_iAudioSelfConnected; } // Custom MMC event handler. void qtractorMainForm::midiMmcNotify ( const qtractorMmcEvent& mmce ) { QString sMmcText("MIDI MMC: "); switch (mmce.cmd()) { case qtractorMmcEvent::STOP: case qtractorMmcEvent::PAUSE: sMmcText += tr("STOP"); if (setPlaying(false) // Auto-backward reset feature... && m_ui.transportAutoBackwardAction->isChecked()) m_pSession->setPlayHead(m_pSession->playHeadAutoBackward()); break; case qtractorMmcEvent::PLAY: case qtractorMmcEvent::DEFERRED_PLAY: sMmcText += tr("PLAY"); setPlaying(true); break; case qtractorMmcEvent::FAST_FORWARD: sMmcText += tr("FFWD"); setRolling(+1); break; case qtractorMmcEvent::REWIND: sMmcText += tr("REW"); setRolling(-1); break; case qtractorMmcEvent::RECORD_STROBE: case qtractorMmcEvent::RECORD_PAUSE: sMmcText += tr("REC ON"); if (!setRecording(true)) { // Send MMC RECORD_EXIT command immediate reply... m_pSession->midiEngine()->sendMmcCommand( qtractorMmcEvent::RECORD_EXIT); } break; case qtractorMmcEvent::RECORD_EXIT: sMmcText += tr("REC OFF"); setRecording(false); break; case qtractorMmcEvent::MMC_RESET: sMmcText += tr("RESET"); setRolling(0); break; case qtractorMmcEvent::LOCATE: sMmcText += tr("LOCATE %1").arg(mmce.locate()); setLocate(mmce.locate()); break; case qtractorMmcEvent::SHUTTLE: sMmcText += tr("SHUTTLE %1").arg(mmce.shuttle()); setShuttle(mmce.shuttle()); break; case qtractorMmcEvent::STEP: sMmcText += tr("STEP %1").arg(mmce.step()); setStep(mmce.step()); break; case qtractorMmcEvent::MASKED_WRITE: switch (mmce.scmd()) { case qtractorMmcEvent::TRACK_RECORD: sMmcText += tr("TRACK RECORD %1 %2") .arg(mmce.track()) .arg(mmce.isOn()); break; case qtractorMmcEvent::TRACK_MUTE: sMmcText += tr("TRACK MUTE %1 %2") .arg(mmce.track()) .arg(mmce.isOn()); break; case qtractorMmcEvent::TRACK_SOLO: sMmcText += tr("TRACK SOLO %1 %2") .arg(mmce.track()) .arg(mmce.isOn()); break; case qtractorMmcEvent::TRACK_MONITOR: sMmcText += tr("TRACK MONITOR %1 %2") .arg(mmce.track()) .arg(mmce.isOn()); break; default: sMmcText += tr("Unknown sub-command"); break; } setTrack(mmce.scmd(), mmce.track(), mmce.isOn()); break; default: sMmcText += tr("Not implemented"); break; } appendMessages(sMmcText); ++m_iStabilizeTimer; } // Custom controller event handler. void qtractorMainForm::midiCtlNotify ( const qtractorCtlEvent& ctle ) { #ifdef CONFIG_DEBUG QString sCtlText(tr("MIDI CTL: %1, Channel %2, Param %3, Value %4") .arg(qtractorMidiControl::nameFromType(ctle.type())) .arg(ctle.channel() + 1) .arg(ctle.param()) .arg(ctle.value())); qDebug("qtractorMainForm::midiCtlNotify() %s.", sCtlText.toUtf8().constData()); #endif // Check if controller is used as MIDI controller... if (m_pMidiControl->processEvent(ctle)) { #ifdef CONFIG_DEBUG appendMessages(sCtlText); #endif return; } if (ctle.type() == qtractorMidiEvent::CONTROLLER) { /* FIXME: JLCooper faders (as from US-224)... if (ctle.channel() == 15) { // Event translation... int iTrack = int(ctle.controller()) & 0x3f; float fGain = float(ctle.value()) / 127.0f; // Find the track by number... qtractorTrack *pTrack = m_pSession->tracks().at(iTrack); if (pTrack && qAbs(fGain - pTrack->gain()) > 0.001f) { m_pSession->execute( new qtractorTrackGainCommand(pTrack, fGain, true)); #ifdef CONFIG_DEBUG sCtlText += ' '; sCtlText += tr("(track %1, gain %2)") .arg(iTrack).arg(fGain); appendMessages(sCtlText); #endif } } else */ // Handle volume controls... if (ctle.param() == 7) { int iTrack = 0; const float fGain = float(ctle.value()) / 127.0f; for (qtractorTrack *pTrack = m_pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() == qtractorTrack::Midi && pTrack->midiChannel() == ctle.channel() && qAbs(fGain - pTrack->gain()) > 0.001f) { m_pSession->execute( new qtractorTrackGainCommand(pTrack, fGain, true)); #ifdef CONFIG_DEBUG sCtlText += ' '; sCtlText += tr("(track %1, gain %2)") .arg(iTrack).arg(fGain); appendMessages(sCtlText); #endif } ++iTrack; } } else // Handle pan controls... if (ctle.param() == 10) { int iTrack = 0; const float fPanning = (float(ctle.value()) - 64.0f) / 63.0f; for (qtractorTrack *pTrack = m_pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() == qtractorTrack::Midi && pTrack->midiChannel() == ctle.channel() && qAbs(fPanning - pTrack->panning()) > 0.001f) { m_pSession->execute( new qtractorTrackPanningCommand(pTrack, fPanning, true)); #ifdef CONFIG_DEBUG sCtlText += ' '; sCtlText += tr("(track %1, panning %2)") .arg(iTrack).arg(fPanning); appendMessages(sCtlText); #endif } ++iTrack; } } } } // Custom MIDI SPP event handler. void qtractorMainForm::midiSppNotify ( int iSppCmd, unsigned short iSongPos ) { #ifdef CONFIG_DEBUG QString sSppText("MIDI SPP: "); #endif switch (iSppCmd) { case SND_SEQ_EVENT_START: #ifdef CONFIG_DEBUG sSppText += tr("START"); #endif setSongPos(0); setPlaying(true); break; case SND_SEQ_EVENT_STOP: #ifdef CONFIG_DEBUG sSppText += tr("STOP"); #endif if (setPlaying(false) // Auto-backward reset feature... && m_ui.transportAutoBackwardAction->isChecked()) m_pSession->setPlayHead(m_pSession->playHeadAutoBackward()); break; case SND_SEQ_EVENT_CONTINUE: #ifdef CONFIG_DEBUG sSppText += tr("CONTINUE"); #endif setPlaying(true); break; case SND_SEQ_EVENT_SONGPOS: #ifdef CONFIG_DEBUG sSppText += tr("SONGPOS %1").arg(iSongPos); #endif setSongPos(iSongPos); break; default: #ifdef CONFIG_DEBUG sSppText += tr("Not implemented"); #endif break; } #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::midiSppNotify() %s.", sSppText.toUtf8().constData()); appendMessages(sSppText); #endif ++m_iStabilizeTimer; } // Custom MIDI Clock event handler. void qtractorMainForm::midiClkNotify ( float fTempo ) { #ifdef CONFIG_DEBUG QString sClkText("MIDI CLK: "); sClkText += tr("%1 BPM").arg(fTempo); qDebug("qtractorMainForm::midiClkNotify() %s.", sClkText.toUtf8().constData()); appendMessages(sClkText); #endif if (m_pTracks) m_pTracks->clearSelect(true); // Find appropriate node... qtractorTimeScale *pTimeScale = m_pSession->timeScale(); qtractorTimeScale::Cursor& cursor = pTimeScale->cursor(); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_pSession->playHead()); // Now, express the change immediately... if (pNode->prev()) { pNode->tempo = fTempo; pTimeScale->updateNode(pNode); } else { m_pSession->setTempo(fTempo); } ++m_iTransportUpdate; updateContents(nullptr, true); ++m_iStabilizeTimer; } // Custom MIDI Step input event handler. void qtractorMainForm::midiInpNotify ( unsigned short flags ) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::midiInpNotify(%u)", flags); #endif // Update current step-input location // for all the in-recording clips out there... if (!m_pSession->isPlaying() && ( flags & (qtractorMidiEngine::InpReset | qtractorMidiEngine::InpEvent))) { const unsigned long iStepInputHead = m_pSession->playHead(); for (qtractorTrack *pTrack = m_pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() == qtractorTrack::Midi && pTrack->isClipRecordEx()) { qtractorMidiClip *pMidiClip = static_cast (pTrack->clipRecord()); if (pMidiClip) { if (flags & qtractorMidiEngine::InpReset) pMidiClip->setStepInputHead(iStepInputHead); pMidiClip->updateStepInput(); } } } } // Handle pending step-input events... if (flags & qtractorMidiEngine::InpEvent) m_pSession->midiEngine()->processInpEvents(); ++m_iStabilizeTimer; } //------------------------------------------------------------------------- // qtractorMainForm -- General contents change stuff. // Audio file addition slot funtion. void qtractorMainForm::addAudioFile ( const QString& sFilename ) { // Add the just dropped audio file... if (m_pFiles) m_pFiles->addAudioFile(sFilename, true); ++m_iStabilizeTimer; } // Audio file selection slot funtion. void qtractorMainForm::selectAudioFile ( const QString& sFilename, int iTrackChannel, bool bSelect ) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::selectAudioFile(\"%s\", %d, %d)", sFilename.toUtf8().constData(), iTrackChannel, int(bSelect)); #endif // Select audio file... if (m_pTracks) { m_pTracks->trackView()->selectClipFile( qtractorTrack::Audio, sFilename, iTrackChannel, bSelect); } ++m_iStabilizeTimer; } // Audio file activation slot funtion. void qtractorMainForm::activateAudioFile ( const QString& sFilename, int /*iTrackChannel*/ ) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::activateAudioFile(\"%s\")", sFilename.toUtf8().constData()); #endif // Make sure session is activated... checkRestartSession(); // We'll start playing if the file is valid, otherwise // the player is stopped (eg. empty filename)... qtractorAudioEngine *pAudioEngine = m_pSession->audioEngine(); if (pAudioEngine && pAudioEngine->openPlayer(sFilename)) { // Try updating player status anyway... if (m_pFiles) m_pFiles->setPlayState(true); if (m_pFileSystem) m_pFileSystem->setPlayState(true); ++m_iPlayerTimer; appendMessages(tr("Playing \"%1\"...") .arg(QFileInfo(sFilename).fileName())); } ++m_iStabilizeTimer; } // MIDI file addition slot funtion. void qtractorMainForm::addMidiFile ( const QString& sFilename ) { // Add the just dropped MIDI file... if (m_pFiles) m_pFiles->addMidiFile(sFilename, true); ++m_iStabilizeTimer; } // MIDI file selection slot funtion. void qtractorMainForm::selectMidiFile ( const QString& sFilename, int iTrackChannel, bool bSelect ) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::selectMidiFile(\"%s\", %d, %d)", sFilename.toUtf8().constData(), iTrackChannel, int(bSelect)); #endif // Select MIDI file track/channel... if (m_pTracks) { m_pTracks->trackView()->selectClipFile( qtractorTrack::Midi, sFilename, iTrackChannel, bSelect); } ++m_iStabilizeTimer; } // MIDI file activation slot funtion. void qtractorMainForm::activateMidiFile ( const QString& sFilename, int iTrackChannel ) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::activateMidiFile(\"%s\", %d)", sFilename.toUtf8().constData(), iTrackChannel); #endif // Make sure session is activated... checkRestartSession(); // We'll start playing if the file is valid, otherwise // the player is stopped (eg. empty filename)... qtractorMidiEngine *pMidiEngine = m_pSession->midiEngine(); if (pMidiEngine && pMidiEngine->openPlayer(sFilename, iTrackChannel)) { // Try updating player status anyway... if (m_pFiles) m_pFiles->setPlayState(true); if (m_pFileSystem) m_pFileSystem->setPlayState(true); ++m_iPlayerTimer; appendMessages(tr("Playing \"%1\"...") .arg(QFileInfo(sFilename).fileName())); } ++m_iStabilizeTimer; } // Generic file activation slot funtion. void qtractorMainForm::activateFile ( const QString& sFilename ) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::activateFile(\"%s\")", sFilename.toUtf8().constData()); #endif // First test if it's an audio file ? qtractorAudioFile *pFile = qtractorAudioFileFactory::createAudioFile(sFilename); if (pFile) { if (pFile->open(sFilename)) { pFile->close(); delete pFile; activateAudioFile(sFilename); } else { delete pFile; } } else { // Then whether it's a MIDI file... qtractorMidiFile file; if (file.open(sFilename)) { file.close(); activateMidiFile(sFilename); } else // Maybe a session file?... if (closeSession()) loadSessionFile(sFilename); } } // Tracks view selection change slot. void qtractorMainForm::trackSelectionChanged (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::trackSelectionChanged()"); #endif // Select sync to mixer... if (m_pTracks && m_pMixer) { // Doesn't matter whether current track is none... qtractorTrack *pTrack = m_pTracks->trackList()->currentTrack(); m_pMixer->setCurrentTrack(pTrack); // HACK: Set current session track for monitoring purposes... if (m_ui.trackAutoMonitorAction->isChecked()) m_pSession->setCurrentTrack(pTrack); } ++m_iStabilizeTimer; } // Mixer view selection change slot. void qtractorMainForm::mixerSelectionChanged (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::mixerSelectionChanged()"); #endif // Select sync to tracks... if (m_pTracks && m_pMixer && m_pMixer->trackRack()) { qtractorTrack *pTrack = nullptr; qtractorMixerStrip *pStrip = (m_pMixer->trackRack())->selectedStrip(); if (pStrip) pTrack = pStrip->track(); m_pTracks->trackList()->setCurrentTrack(pTrack); } ++m_iStabilizeTimer; } // Tracks view selection change slot. void qtractorMainForm::selectionNotifySlot ( qtractorMidiEditor *pMidiEditor ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::selectionNotifySlot()"); #endif // Read session edit-head/tails... const unsigned long iEditHead = m_pSession->editHead(); const unsigned long iEditTail = m_pSession->editTail(); // Track-view is due... if (m_pTracks) { qtractorTrackView *pTrackView = m_pTracks->trackView(); pTrackView->setEditHead(iEditHead); pTrackView->setEditTail(iEditTail); pTrackView->setPlayHeadAutoBackward( m_pSession->playHeadAutoBackward()); // if (pMidiEditor) m_pTracks->clearSelect(); } // Update editors edit-head/tails... QListIterator iter(m_editors); while (iter.hasNext()) { qtractorMidiEditor *pEditor = (iter.next())->editor(); if (pEditor != pMidiEditor) { pEditor->setEditHead(iEditHead, false); pEditor->setEditTail(iEditTail, false); } } // Normal status ahead... ++m_iStabilizeTimer; } // Clip editors update helper. void qtractorMainForm::changeNotifySlot ( qtractorMidiEditor *pMidiEditor ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::changeNotifySlot(%p)", pMidiEditor); #endif updateContents(pMidiEditor, true); } // Command update helper. void qtractorMainForm::updateNotifySlot ( unsigned int flags ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::updateNotifySlot(0x%02x)", int(flags)); #endif // Always reset any track view selection... // (avoid change/update notifications, again) if (m_pTracks && (flags & qtractorCommand::ClearSelect)) m_pTracks->clearSelect(flags & qtractorCommand::Reset); // Proceed as usual... updateContents(nullptr, (flags & qtractorCommand::Refresh)); } // Common update helper. void qtractorMainForm::updateContents ( qtractorMidiEditor *pMidiEditor, bool bRefresh ) { // Maybe, just maybe, we've made things larger... m_pTempoCursor->clear(); m_pSession->updateTimeScale(); m_pSession->updateSession(); // Refresh track-view? if (m_pTracks) m_pTracks->updateContents(bRefresh); // Update other editors contents... QListIterator iter(m_editors); while (iter.hasNext()) { qtractorMidiEditorForm *pForm = iter.next(); if (pForm->editor() != pMidiEditor) pForm->updateTimeScale(); } // Notify who's watching... contentsChanged(); } void qtractorMainForm::updateDirtyCount ( bool bDirtyCount ) { if (bDirtyCount) { ++m_iDirtyCount; } else { m_iDirtyCount = 0; } #ifdef CONFIG_NSM if (m_pNsmClient && m_pNsmClient->is_active()) { if (!m_bNsmDirty && bDirtyCount) { m_pNsmClient->dirty(true); m_bNsmDirty = true; } else if (m_bNsmDirty && !bDirtyCount) { m_pNsmClient->dirty(false); m_bNsmDirty = false; } } #endif } // Tracks view contents change slot. void qtractorMainForm::contentsChanged (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::contentsChanged()"); #endif // HACK: Force immediate stabilization later... //m_iStabilizeTimer = 0; // Stabilize session toolbar widgets... // m_pTempoSpinBox->setTempo(m_pSession->tempo(), false); // m_pTempoSpinBox->setBeatsPerBar(m_pSession->beatsPerBar(), false); // m_pTempoSpinBox->setBeatDivisor(m_pSession->beatDivisor(), false); m_pSnapPerBeatComboBox->setCurrentIndex( qtractorTimeScale::indexFromSnap(m_pSession->snapPerBeat())); m_pThumbView->updateContents(); dirtyNotifySlot(); } // Tracks view contents dirty up slot. void qtractorMainForm::dirtyNotifySlot (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMainForm::dirtyNotifySlot()"); #endif updateDirtyCount(true); selectionNotifySlot(nullptr); } // Tempo spin-box change slot. void qtractorMainForm::transportTempoChanged ( float fTempo, unsigned short iBeatsPerBar, unsigned short iBeatDivisor ) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportTempoChanged(%g, %u, %u)", fTempo, iBeatsPerBar, iBeatDivisor); #endif // Find appropriate node... qtractorTimeScale *pTimeScale = m_pSession->timeScale(); qtractorTimeScale::Cursor& cursor = pTimeScale->cursor(); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_pSession->playHead()); // Now, express the change as an undoable command... m_pSession->execute(new qtractorTimeScaleUpdateNodeCommand( pTimeScale, pNode->frame, fTempo, 2, iBeatsPerBar, iBeatDivisor)); ++m_iTransportUpdate; } void qtractorMainForm::transportTempoFinished (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportTempoFinished()"); #endif const bool bBlockSignals = m_pTempoSpinBox->blockSignals(true); m_pTempoSpinBox->clearFocus(); m_pTempoSpinBox->blockSignals(bBlockSignals); } // Snap-per-beat spin-box change slot. void qtractorMainForm::snapPerBeatChanged ( int iSnap ) { // Avoid bogus changes... const unsigned short iSnapPerBeat = qtractorTimeScale::snapFromIndex(iSnap); if (iSnapPerBeat == m_pSession->snapPerBeat()) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::snapPerBeatChanged(%u)", iSnapPerBeat); #endif // No need to express this change as a undoable command... m_pSession->setSnapPerBeat(iSnapPerBeat); } // Time format custom context menu. void qtractorMainForm::transportTimeFormatChanged ( int iDisplayFormat ) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportTimeFormatChanged(%d)", iDisplayFormat); #endif if (m_pOptions) { m_pOptions->iDisplayFormat = iDisplayFormat; updateDisplayFormat(); } ++m_iStabilizeTimer; } // Real thing: the playhead has been changed manually! void qtractorMainForm::transportTimeChanged ( unsigned long iPlayHead ) { if (m_iTransportUpdate > 0) return; #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportTimeChanged(%lu)", iPlayHead); #endif m_pSession->setPlayHead(iPlayHead); ++m_iTransportUpdate; ++m_iStabilizeTimer; } void qtractorMainForm::transportTimeFinished (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMainForm::transportTimeFinished()"); #endif const bool bBlockSignals = m_pTimeSpinBox->blockSignals(true); m_pTimeSpinBox->clearFocus(); m_pTimeSpinBox->blockSignals(bBlockSignals); } // Tempo-map custom context menu. void qtractorMainForm::transportTempoContextMenu ( const QPoint& /*pos*/ ) { viewTempoMap(); } // Open and retrieve style-sheet from file (*.qss). QString qtractorMainForm::styleSheet ( const QString& sFilename ) { QString sStyleSheet; if (sFilename.isEmpty()) return sStyleSheet; QFile file(sFilename); if (file.open(QFile::ReadOnly)) { sStyleSheet = QString::fromUtf8(file.readAll()); file.close(); } if (!sStyleSheet.isEmpty()) { QStringList subs; const QDir& dir = QFileInfo(sFilename).absoluteDir(); const QRegularExpression rx( "url\\([\\s]?[\"']?([^\"')]+)[\"']?[\\s]?\\)", QRegularExpression::CaseInsensitiveOption); QRegularExpressionMatchIterator iter = rx.globalMatch(sStyleSheet); while (iter.hasNext()) { const QRegularExpressionMatch& match = iter.next(); const QString& sPath = match.captured(1).trimmed(); if (sPath.isEmpty()) continue; const QFileInfo info(dir, sPath); if (!info.exists()) continue; const QString& sBefore = match.captured(0); if (!subs.contains(sBefore)) { QString sAfter = sBefore; sAfter.replace(sPath, info.absoluteFilePath()); sStyleSheet.replace(sBefore, sAfter); subs.append(sBefore); } } } return sStyleSheet; } // end of qtractorMainForm.cpp qtractor-1.5.9/src/PaxHeaders/qtractorConnect.h0000644000000000000000000000013215101070305016544 xustar0030 mtime=1761898693.068267594 30 atime=1761898693.068267594 30 ctime=1761898693.068267594 qtractor-1.5.9/src/qtractorConnect.h0000644000175000001440000002577715101070305016556 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.9/src/PaxHeaders/qtractorRubberBand.cpp0000644000000000000000000000013215101070305017514 xustar0030 mtime=1761898693.087267654 30 atime=1761898693.087267654 30 ctime=1761898693.087267654 qtractor-1.5.9/src/qtractorRubberBand.cpp0000644000175000001440000000447115101070305017512 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.9/src/PaxHeaders/qtractorSessionForm.ui0000644000000000000000000000013215101070305017610 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.088267658 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorSessionForm.ui0000644000175000001440000003776715101070305017624 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.9/src/PaxHeaders/qtractorMixer.h0000644000000000000000000000013215101070305016237 xustar0030 mtime=1761898693.084267645 30 atime=1761898693.084267645 30 ctime=1761898693.084267645 qtractor-1.5.9/src/qtractorMixer.h0000644000175000001440000002646615101070305016245 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.9/src/PaxHeaders/qtractorMidiControlForm.ui0000644000000000000000000000013215101070305020410 xustar0030 mtime=1761898693.078267626 30 atime=1761898693.078267626 30 ctime=1761898693.078267626 qtractor-1.5.9/src/qtractorMidiControlForm.ui0000644000175000001440000003411715101070305020406 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. qtractorMidiControlForm 0 0 640 480 Controllers Controller files false true false true Files Path Import controller files &Import... Remove controller file &Remove Move controller file up on list order &Up Move controller file down on list order &Down &Type Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft ControlTypeComboBox &Channel Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft ChannelComboBox &Parameter Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft ParamComboBox Trac&k Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft TrackOffsetSpinBox offse&t Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft TrackOffsetSpinBox &limit Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft TrackLimitSpinBox C&ommand Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft CommandComboBox Flags Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft Qt::Vertical 20 8 MIDI Event type MIDI Channel MIDI Controller (parameter) MIDI parameter (track offset) + Track offset 0 127 Track limit 0 127 Command action Command delta/momentary D&elta Command feedback &Feedback Map/update controller command &Map 0 160 Controller map false true false true true Type Channel Parameter Track Command Flags Unmap/remove controller command U&nmap Qt::Vertical 20 8 Enable all controllers immediate sync (hook) &Sync Qt::Vertical 20 8 Reload/apply all controller files Relo&ad Export to controller file E&xport... Close this dialog Close FilesListView ImportPushButton RemovePushButton MoveUpPushButton MoveDownPushButton ControlTypeComboBox ChannelComboBox ParamComboBox TrackParamCheckBox TrackOffsetSpinBox TrackLimitSpinBox CommandComboBox DeltaCheckBox FeedbackCheckBox MapPushButton ControlMapListView UnmapPushButton SyncCheckBox ReloadPushButton ExportPushButton ClosePushButton qtractor-1.5.9/src/PaxHeaders/qtractorClipSelect.cpp0000644000000000000000000000013215101070305017535 xustar0030 mtime=1761898693.067267591 30 atime=1761898693.067267591 30 ctime=1761898693.067267591 qtractor-1.5.9/src/qtractorClipSelect.cpp0000644000175000001440000001107215101070305017526 0ustar00rncbcusers// qtractorClipSelect.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 "qtractorClipSelect.h" #include "qtractorSession.h" #include "qtractorTrack.h" #include "qtractorClip.h" //------------------------------------------------------------------------- // qtractorClipSelect -- Track clip selection capsule. // Constructor. qtractorClipSelect::qtractorClipSelect (void) { m_bTrackSingle = false; m_pTrackSingle = nullptr; } // Default constructor. qtractorClipSelect::~qtractorClipSelect (void) { clear(); } // Clip selection method. void qtractorClipSelect::selectItem ( qtractorClip *pClip, const QRect& rect, bool bSelect ) { // Add/remove clip selection... Item *pClipItem = nullptr; ItemList::Iterator iter = m_items.find(pClip); if (iter != m_items.end()) pClipItem = iter.value(); if (pClipItem && !bSelect) { iter = m_items.erase(iter); delete pClipItem; pClip->setClipSelected(false); m_bTrackSingle = false; // Reset united selection rectangle... m_rect.setRect(0, 0, 0, 0); const ItemList::Iterator& iter_end = m_items.end(); for (iter = m_items.begin(); iter != iter_end; ++iter) m_rect = m_rect.united(iter.value()->rect); // Done with clip deselection. } else if (bSelect) { pClip->setClipSelected(true); unsigned long offset = 0; if (pClip->isClipSelected()) offset = pClip->clipSelectStart() - pClip->clipStart(); if (pClipItem) { pClipItem->rect = rect; pClipItem->offset = offset; } else { m_items.insert(pClip, new Item(rect, offset)); } // Special optimization: no need to recache // our single track reference if we add some outsider clip... if (m_bTrackSingle) { if (m_pTrackSingle && m_pTrackSingle != pClip->track()) m_pTrackSingle = nullptr; } else if (m_pTrackSingle == nullptr) { m_pTrackSingle = pClip->track(); m_bTrackSingle = true; } // Unite whole selection reactangular area... m_rect = m_rect.united(rect); } } // Clip addition (no actual selection). void qtractorClipSelect::addItem ( qtractorClip *pClip, const QRect& rect, unsigned long offset ) { m_items.insert(pClip, new Item(rect, offset)); m_rect = m_rect.united(rect); } // The united selection rectangle. const QRect& qtractorClipSelect::rect (void) const { return m_rect; } // Dynamic helper: // Do all selected clips belong to the same track? qtractorTrack *qtractorClipSelect::singleTrack (void) { // Check if predicate is already cached... if (!m_bTrackSingle) { m_pTrackSingle = nullptr; ItemList::ConstIterator iter = m_items.constBegin(); const ItemList::ConstIterator& iter_end = m_items.constEnd(); for ( ; iter != iter_end; ++iter) { qtractorClip *pClip = iter.key(); if (m_pTrackSingle == nullptr) m_pTrackSingle = pClip->track(); else if (m_pTrackSingle != pClip->track()) { m_pTrackSingle = nullptr; break; } } m_bTrackSingle = true; } // Return current selected single track (if any)... return m_pTrackSingle; } // Selection list accessor. const qtractorClipSelect::ItemList& qtractorClipSelect::items (void) const { return m_items; } // Clip selection item lookup. qtractorClipSelect::Item *qtractorClipSelect::findItem ( qtractorClip *pClip ) const { return m_items.value(pClip, nullptr); } // Reset clip selection. void qtractorClipSelect::reset (void) { ItemList::ConstIterator iter = m_items.constBegin(); const ItemList::ConstIterator& iter_end = m_items.constEnd(); for ( ; iter != iter_end; ++iter) iter.key()->setClipSelected(false); clear(); } // Clear clip selection. void qtractorClipSelect::clear (void) { m_bTrackSingle = false; m_pTrackSingle = nullptr; m_rect.setRect(0, 0, 0, 0); qDeleteAll(m_items); m_items.clear(); } // end of qtractorClipSelect.cpp qtractor-1.5.9/src/PaxHeaders/lv20000644000000000000000000000013215101070305013650 xustar0030 mtime=1761898693.061342007 30 atime=1761898693.060267569 30 ctime=1761898693.061342007 qtractor-1.5.9/src/lv2/0000755000175000001440000000000015101070305013715 5ustar00rncbcusersqtractor-1.5.9/src/lv2/PaxHeaders/lv2_midnam.h0000644000000000000000000000013215101070305016126 xustar0030 mtime=1761898693.061342007 30 atime=1761898693.061342007 30 ctime=1761898693.061342007 qtractor-1.5.9/src/lv2/lv2_midnam.h0000644000175000001440000000427215101070305016123 0ustar00rncbcusers/* LV2 MIDNAM Extension Copyright 2016 Robin Gareus Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file lv2_midnam.h C header for the LV2 MIDNAM extension . */ #ifndef LV2_MIDNAM_H #define LV2_MIDNAM_H #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2.h" #else #include "lv2/core/lv2.h" #endif #define LV2_MIDNAM_URI "http://ardour.org/lv2/midnam" #define LV2_MIDNAM_PREFIX LV2_MIDNAM_URI "#" #define LV2_MIDNAM__interface LV2_MIDNAM_PREFIX "interface" #define LV2_MIDNAM__update LV2_MIDNAM_PREFIX "update" #ifdef __cplusplus extern "C" { #endif typedef void* LV2_Midnam_Handle; /** a LV2 Feature provided by the Host to the plugin */ typedef struct { /** Opaque host data */ LV2_Midnam_Handle handle; /** Request from run() that the host should re-read the midnam */ void (*update)(LV2_Midnam_Handle handle); } LV2_Midnam; typedef struct { /** Query midnam document. The plugin * is expected to return a null-terminated XML * text which is a valid midnam desciption * (or NULL in case of error). * * The midnam \ must be unique and * specific for the given plugin-instance. */ char* (*midnam)(LV2_Handle instance); /** The unique model id used ith the midnam, * (or NULL). */ char* (*model)(LV2_Handle instance); /** free allocated strings. The host * calls this for every value returned by * \ref midnam and \ref model. */ void (*free)(char*); } LV2_Midnam_Interface; #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LV2_MIDNAM_H */ qtractor-1.5.9/src/lv2/PaxHeaders/lv2_programs.h0000644000000000000000000000013215101070305016513 xustar0030 mtime=1761898693.061342007 30 atime=1761898693.061342007 30 ctime=1761898693.061342007 qtractor-1.5.9/src/lv2/lv2_programs.h0000644000175000001440000001440715101070305016511 0ustar00rncbcusers/* LV2 Programs Extension Copyright 2012 Filipe Coelho Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file lv2_programs.h C header for the LV2 programs extension . */ #ifndef LV2_PROGRAMS_H #define LV2_PROGRAMS_H #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2.h" #include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #else #include "lv2/core/lv2.h" #include "lv2/ui/ui.h" #endif #define LV2_PROGRAMS_URI "http://kxstudio.sf.net/ns/lv2ext/programs" #define LV2_PROGRAMS_PREFIX LV2_PROGRAMS_URI "#" #define LV2_PROGRAMS__Host LV2_PROGRAMS_PREFIX "Host" #define LV2_PROGRAMS__Interface LV2_PROGRAMS_PREFIX "Interface" #define LV2_PROGRAMS__UIInterface LV2_PROGRAMS_PREFIX "UIInterface" #ifdef __cplusplus extern "C" { #endif typedef void* LV2_Programs_Handle; typedef struct _LV2_Program_Descriptor { /** Bank number for this program. Note that this extension does not support MIDI-style separation of bank LSB and MSB values. There is no restriction on the set of available banks: the numbers do not need to be contiguous, there does not need to be a bank 0, etc. */ uint32_t bank; /** Program number (unique within its bank) for this program. There is no restriction on the set of available programs: the numbers do not need to be contiguous, there does not need to be a program 0, etc. */ uint32_t program; /** Name of the program. */ const char * name; } LV2_Program_Descriptor; /** Programs extension, plugin data. When the plugin's extension_data is called with argument LV2_PROGRAMS__Interface, the plugin MUST return an LV2_Programs_Instance structure, which remains valid for the lifetime of the plugin. */ typedef struct _LV2_Programs_Interface { /** * get_program() * * This member is a function pointer that provides a description * of a program (named preset sound) available on this plugin. * * The index argument is an index into the plugin's list of * programs, not a program number as represented by the Program * field of the LV2_Program_Descriptor. (This distinction is * needed to support plugins that use non-contiguous program or * bank numbers.) * * This function returns a LV2_Program_Descriptor pointer that is * guaranteed to be valid only until the next call to get_program * or deactivate, on the same plugin instance. This function must * return NULL if passed an index argument out of range, so that * the host can use it to query the number of programs as well as * their properties. */ const LV2_Program_Descriptor *(*get_program)(LV2_Handle handle, uint32_t index); /** * select_program() * * This member is a function pointer that selects a new program * for this plugin. The program change should take effect * immediately at the start of the next run() call. (This * means that a host providing the capability of changing programs * between any two notes on a track must vary the block size so as * to place the program change at the right place. A host that * wanted to avoid this would probably just instantiate a plugin * for each program.) * * Plugins should ignore a select_program() call with an invalid * bank or program. * * A plugin is not required to select any particular default * program on activate(): it's the host's duty to set a program * explicitly. * * A plugin is permitted to re-write the values of its input * control ports when select_program is called. The host should * re-read the input control port values and update its own * records appropriately. (This is the only circumstance in which * a LV2 plugin is allowed to modify its own control-input ports.) */ void (*select_program)(LV2_Handle handle, uint32_t bank, uint32_t program); } LV2_Programs_Interface; /** Programs extension, UI data. When the UI's extension_data is called with argument LV2_PROGRAMS__UIInterface, the UI MUST return an LV2_Programs_UI_Interface structure, which remains valid for the lifetime of the UI. */ typedef struct _LV2_Programs_UI_Interface { /** * select_program() * * This is exactly the same as select_program in LV2_Programs_Instance, * but this struct relates to the UI instead of the plugin. * * When called, UIs should update their state to match the selected program. */ void (*select_program)(LV2UI_Handle handle, uint32_t bank, uint32_t program); } LV2_Programs_UI_Interface; /** Feature data for LV2_PROGRAMS__Host. */ typedef struct _LV2_Programs_Host { /** * Opaque host data. */ LV2_Programs_Handle handle; /** * program_changed() * * Tell the host to reload a plugin's program. * Parameter handle MUST be the 'handle' member of this struct. * Parameter index is program index to change. * When index is -1, host should reload all the programs. * * The plugin MUST NEVER call this function on a RT context or during run(). * * NOTE: This call is to inform the host about a program's bank, program or name change. * It DOES NOT change the current selected program. */ void (*program_changed)(LV2_Programs_Handle handle, int32_t index); } LV2_Programs_Host; #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LV2_PROGRAMS_H */ qtractor-1.5.9/src/lv2/PaxHeaders/lv2_port_change_request.h0000644000000000000000000000013215101070305020722 xustar0030 mtime=1761898693.061342007 30 atime=1761898693.061342007 30 ctime=1761898693.061342007 qtractor-1.5.9/src/lv2/lv2_port_change_request.h0000644000175000001440000000627015101070305020717 0ustar00rncbcusers/* LV2 ControlInputPort change request extension Copyright 2020 Filipe Coelho Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /** @file control-input-port-change-request.h C header for the LV2 ControlInputPort change request extension . */ #ifndef LV2_CONTROL_INPUT_PORT_CHANGE_REQUEST_H #define LV2_CONTROL_INPUT_PORT_CHANGE_REQUEST_H #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2.h" #else #include "lv2/core/lv2.h" #endif #define LV2_CONTROL_INPUT_PORT_CHANGE_REQUEST_URI "http://kx.studio/ns/lv2ext/control-input-port-change-request" #define LV2_CONTROL_INPUT_PORT_CHANGE_REQUEST_PREFIX LV2_CONTROL_INPUT_PORT_CHANGE_REQUEST_URI "#" #include #ifdef __cplusplus extern "C" { #else #include #endif /** A status code for LV2_CONTROL_INPUT_PORT_CHANGE_REQUEST_URI functions. */ typedef enum { LV2_CONTROL_INPUT_PORT_CHANGE_SUCCESS = 0, /**< Completed successfully. */ LV2_CONTROL_INPUT_PORT_CHANGE_ERR_UNKNOWN = 1, /**< Unknown error. */ LV2_CONTROL_INPUT_PORT_CHANGE_ERR_INVALID_INDEX = 2 /**< Failed due to invalid port index. */ } LV2_ControlInputPort_Change_Status; /** * Opaque handle for LV2_CONTROL_INPUT_PORT_CHANGE_REQUEST_URI feature. */ typedef void* LV2_ControlInputPort_Change_Request_Handle; /** * On instantiation, host must supply LV2_CONTROL_INPUT_PORT_CHANGE_REQUEST_URI feature. * LV2_Feature::data must be pointer to LV2_ControlInputPort_Change_Request. */ typedef struct _LV2_ControlInputPort_Change_Request { /** * Opaque host data. */ LV2_ControlInputPort_Change_Request_Handle handle; /** * request_change() * * Ask the host to change a plugin's control input port value. * Parameter handle MUST be the 'handle' member of this struct. * Parameter index is port index to change. * Parameter value is the requested value to change the control port input to. * * Returns status of the request. * The host may decline this request, if e.g. it is currently automating this port. * * The plugin MUST call this function during run(). */ LV2_ControlInputPort_Change_Status (*request_change)(LV2_ControlInputPort_Change_Request_Handle handle, uint32_t index, float value); } LV2_ControlInputPort_Change_Request; #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LV2_CONTROL_INPUT_PORT_CHANGE_REQUEST_H */ qtractor-1.5.9/src/lv2/PaxHeaders/lv2_atom_helpers.h0000644000000000000000000000013215101070305017343 xustar0030 mtime=1761898693.060267569 30 atime=1761898693.060267569 30 ctime=1761898693.060267569 qtractor-1.5.9/src/lv2/lv2_atom_helpers.h0000644000175000001440000001452415101070305017341 0ustar00rncbcusers// lv2_atom_helpers.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. *****************************************************************************/ /* Helper functions for LV2 atom:Sequence event buffer. * * tentatively adapted from: * * - lv2_evbuf.h,c - An abstract/opaque LV2 event buffer implementation. * * - event-helpers.h - Helper functions for the LV2 Event extension. * * * Copyright 2008-2012 David Robillard */ #ifndef LV2_ATOM_HELPERS_H #define LV2_ATOM_HELPERS_H #include #include #include #include #include #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/ext/atom/atom.h" #else #include "lv2/atom/atom.h" #endif // An abstract/opaque LV2 atom:Sequence buffer. // typedef struct _LV2_Atom_Buffer { uint32_t capacity; uint32_t chunk_type; uint32_t sequence_type; uint32_t _alignment_padding; LV2_Atom_Sequence aseq; } LV2_Atom_Buffer; // Pad a size to 64 bits (for LV2 atom:Sequence event sizes). // static inline uint32_t lv2_atom_buffer_pad_size ( uint32_t size ) { return (size + 7) & (~7); } // Clear and initialize an existing LV2 atom:Sequenece buffer. // static inline void lv2_atom_buffer_reset ( LV2_Atom_Buffer *abuf, bool input ) { if (input) { abuf->aseq.atom.size = sizeof(LV2_Atom_Sequence_Body); abuf->aseq.atom.type = abuf->sequence_type; } else { abuf->aseq.atom.size = abuf->capacity; abuf->aseq.atom.type = abuf->chunk_type; } } // Allocate a new, empty LV2 atom:Sequence buffer. // static inline LV2_Atom_Buffer *lv2_atom_buffer_new ( uint32_t capacity, uint32_t chunk_type, uint32_t sequence_type, bool input ) { LV2_Atom_Buffer *abuf = (LV2_Atom_Buffer *) malloc(sizeof(LV2_Atom_Buffer) + sizeof(LV2_Atom_Sequence) + capacity); abuf->capacity = capacity; abuf->chunk_type = chunk_type; abuf->sequence_type = sequence_type; lv2_atom_buffer_reset(abuf, input); return abuf; } // Free an LV2 atom:Sequenece buffer allocated with lv2_atome_buffer_new. // static inline void lv2_atom_buffer_free ( LV2_Atom_Buffer *abuf ) { free(abuf); } // Return the total padded size of events stored in a LV2 atom:Sequence buffer. // static inline uint32_t lv2_atom_buffer_get_size ( LV2_Atom_Buffer *abuf ) { if (abuf->aseq.atom.type == abuf->sequence_type) return abuf->aseq.atom.size - sizeof(LV2_Atom_Sequence_Body); else return 0; } // Return the actual LV2 atom:Sequence implementation. // static inline LV2_Atom_Sequence *lv2_atom_buffer_get_sequence ( LV2_Atom_Buffer *abuf ) { return &abuf->aseq; } // An iterator over an atom:Sequence buffer. // typedef struct _LV2_Atom_Buffer_Iterator { LV2_Atom_Buffer *abuf; uint32_t offset; } LV2_Atom_Buffer_Iterator; // Reset an iterator to point to the start of an LV2 atom:Sequence buffer. // static inline bool lv2_atom_buffer_begin ( LV2_Atom_Buffer_Iterator *iter, LV2_Atom_Buffer *abuf ) { iter->abuf = abuf; iter->offset = 0; return (abuf->aseq.atom.size > 0); } // Reset an iterator to point to the end of an LV2 atom:Sequence buffer. // static inline bool lv2_atom_buffer_end ( LV2_Atom_Buffer_Iterator *iter, LV2_Atom_Buffer *abuf ) { iter->abuf = abuf; iter->offset = lv2_atom_buffer_pad_size(lv2_atom_buffer_get_size(abuf)); return (iter->offset < abuf->capacity - sizeof(LV2_Atom_Event)); } // Check if a LV2 atom:Sequenece buffer iterator is valid. // static inline bool lv2_atom_buffer_is_valid ( LV2_Atom_Buffer_Iterator *iter ) { return iter->offset < lv2_atom_buffer_get_size(iter->abuf); } // Advance a LV2 atom:Sequenece buffer iterator forward one event. // static inline bool lv2_atom_buffer_increment ( LV2_Atom_Buffer_Iterator *iter ) { if (!lv2_atom_buffer_is_valid(iter)) return false; LV2_Atom_Buffer *abuf = iter->abuf; LV2_Atom_Sequence *aseq = &abuf->aseq; uint32_t size = ((LV2_Atom_Event *) ((char *) LV2_ATOM_CONTENTS(LV2_Atom_Sequence, aseq) + iter->offset))->body.size; iter->offset += lv2_atom_buffer_pad_size(sizeof(LV2_Atom_Event) + size); return true; } // Get the event currently pointed at a LV2 atom:Sequence buffer iterator. // static inline LV2_Atom_Event *lv2_atom_buffer_get ( LV2_Atom_Buffer_Iterator *iter, uint8_t **data ) { if (!lv2_atom_buffer_is_valid(iter)) return NULL; LV2_Atom_Buffer *abuf = iter->abuf; LV2_Atom_Sequence *aseq = &abuf->aseq; LV2_Atom_Event *aev = (LV2_Atom_Event *) ((char *) LV2_ATOM_CONTENTS(LV2_Atom_Sequence, aseq) + iter->offset); *data = (uint8_t *) LV2_ATOM_BODY(&aev->body); return aev; } // Write an event at a LV2 atom:Sequence buffer iterator. // #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wstringop-overflow" #endif static inline bool lv2_atom_buffer_write ( LV2_Atom_Buffer_Iterator *iter, uint32_t frames, uint32_t /*subframes*/, uint32_t type, uint32_t size, const uint8_t *data ) { LV2_Atom_Buffer *abuf = iter->abuf; LV2_Atom_Sequence *aseq = &abuf->aseq; if (abuf->capacity - sizeof(LV2_Atom) - aseq->atom.size < sizeof(LV2_Atom_Event) + size) return false; LV2_Atom_Event *aev = (LV2_Atom_Event*) ((char *) LV2_ATOM_CONTENTS(LV2_Atom_Sequence, aseq) + iter->offset); aev->time.frames = frames; aev->body.type = type; aev->body.size = size; memcpy(LV2_ATOM_BODY(&aev->body), data, size); size = lv2_atom_buffer_pad_size(sizeof(LV2_Atom_Event) + size); aseq->atom.size += size; iter->offset += size; return true; } #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic pop #endif #endif // LV2_ATOM_HELPERS_H // end of lv2_atom_helpers.h qtractor-1.5.9/src/lv2/PaxHeaders/lv2_external_ui.h0000644000000000000000000000013215101070305017200 xustar0030 mtime=1761898693.061342007 30 atime=1761898693.060267569 30 ctime=1761898693.061342007 qtractor-1.5.9/src/lv2/lv2_external_ui.h0000644000175000001440000000702315101070305017172 0ustar00rncbcusers/* LV2 External UI extension This work is in public domain. This file 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. If you have questions, contact Filipe Coelho (aka falkTX) or ask in #lad channel, FreeNode IRC network. */ /** @file lv2_external_ui.h C header for the LV2 External UI extension . */ #ifndef LV2_EXTERNAL_UI_H #define LV2_EXTERNAL_UI_H #ifdef CONFIG_LV2_OLD_HEADERS #include "lv2/lv2plug.in/ns/extensions/ui/ui.h" #else #include "lv2//ui/ui.h" #endif #define LV2_EXTERNAL_UI_URI "http://kxstudio.sf.net/ns/lv2ext/external-ui" #define LV2_EXTERNAL_UI_PREFIX LV2_EXTERNAL_UI_URI "#" #define LV2_EXTERNAL_UI__Host LV2_EXTERNAL_UI_PREFIX "Host" #define LV2_EXTERNAL_UI__Widget LV2_EXTERNAL_UI_PREFIX "Widget" /** This extension used to be defined by a lv2plug.in URI */ #define LV2_EXTERNAL_UI_DEPRECATED_URI "http://lv2plug.in/ns/extensions/ui#external" #ifdef __cplusplus extern "C" { #endif /** * When LV2_EXTERNAL_UI__Widget UI is instantiated, the returned * LV2UI_Widget handle must be cast to pointer to LV2_External_UI_Widget. * UI is created in invisible state. */ typedef struct _LV2_External_UI_Widget { /** * Host calls this function regulary. UI library implementing the * callback may do IPC or redraw the UI. * * @param _this_ the UI context */ void (*run)(struct _LV2_External_UI_Widget * _this_); /** * Host calls this function to make the plugin UI visible. * * @param _this_ the UI context */ void (*show)(struct _LV2_External_UI_Widget * _this_); /** * Host calls this function to make the plugin UI invisible again. * * @param _this_ the UI context */ void (*hide)(struct _LV2_External_UI_Widget * _this_); } LV2_External_UI_Widget; #define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr) #define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr) #define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr) /** * On UI instantiation, host must supply LV2_EXTERNAL_UI__Host feature. * LV2_Feature::data must be pointer to LV2_External_UI_Host. */ typedef struct _LV2_External_UI_Host { /** * Callback that plugin UI will call when UI (GUI window) is closed by user. * This callback will be called during execution of LV2_External_UI_Widget::run() * (i.e. not from background thread). * * After this callback is called, UI is defunct. Host must call LV2UI_Descriptor::cleanup(). * If host wants to make the UI visible again, the UI must be reinstantiated. * * @note When using the depreated URI LV2_EXTERNAL_UI_DEPRECATED_URI, * some hosts will not call LV2UI_Descriptor::cleanup() as they should, * and may call show() again without re-initialization. * * @param controller Host context associated with plugin UI, as * supplied to LV2UI_Descriptor::instantiate(). */ void (*ui_closed)(LV2UI_Controller controller); /** * Optional (may be NULL) "user friendly" identifier which the UI * may display to allow a user to easily associate this particular * UI instance with the correct plugin instance as it is represented * by the host (e.g. "track 1" or "channel 4"). * * If supplied by host, the string will be referenced only during * LV2UI_Descriptor::instantiate() */ const char * plugin_human_id; } LV2_External_UI_Host; #ifdef __cplusplus } /* extern "C" */ #endif #endif /* LV2_EXTERNAL_UI_H */ qtractor-1.5.9/src/PaxHeaders/qtractorTrackForm.ui0000644000000000000000000000013215101070305017231 xustar0030 mtime=1761898693.090267664 30 atime=1761898693.090267664 30 ctime=1761898693.090267664 qtractor-1.5.9/src/qtractorTrackForm.ui0000644000175000001440000006043015101070305017224 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. qtractorTrackForm 0 0 380 320 Qt::StrongFocus Track 0 Track &Name: TrackNameTextEdit 0 0 280 48 Track name description 48 48 48 48 Qt::TabFocus Track icon Qt::Vertical QSizePolicy::Expanding 8 0 4 0 Type 4 8 Audio track type &Audio MIDI track type &MIDI Input / Output 4 8 0 0 80 0 Input bus name 0 0 80 0 Output bus name 22 22 24 24 Qt::TabFocus Manage buses ... MIDI / Instrument 4 8 MIDI Omni: Capture All Channels &Omni &Channel: Qt::AlignRight|Qt::AlignVCenter ChannelSpinBox 48 24 MIDI Channel (1-16) 1 16 MIDI Patch: Instrument Bank &Select Method: Qt::AlignRight|Qt::AlignVCenter BankSelMethodComboBox 0 0 MIDI Patch: Bank Select Method MIDI Patch: Drum Mode &Drums &Bank: BankComboBox MIDI Patch: Bank true &Program: ProgComboBox MIDI Patch: Program true View / Colors 4 8 &Foreground: ForegroundColorComboBox 64 0 Foreground color true 22 22 24 24 Qt::TabFocus Select custom track foreground color ... Qt::Horizontal QSizePolicy::Expanding 8 8 Bac&kground: Qt::AlignRight|Qt::AlignVCenter BackgroundColorComboBox 64 0 Background color true 22 22 24 24 Qt::TabFocus Select custom track background color ... Auto false Plugins Qt::WheelFocus Track plugins 90 28 Add plugin &Add... Qt::ToolButtonTextBesideIcon 90 28 Remove plugin &Remove Qt::ToolButtonTextBesideIcon Qt::Vertical 8 8 90 28 Move plugin up &Up Qt::ToolButtonTextBesideIcon 90 28 Move plugin down &Down Qt::ToolButtonTextBesideIcon Whether to enable plugin latency/delay compensation &Latency compensation 0 0 Qt::Horizontal QSizePolicy::Expanding 8 8 Current total latency true Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok qtractorPluginListView QListWidget
qtractorPluginListView.h
TrackNameTextEdit TrackIconToolButton AudioRadioButton MidiRadioButton InputBusNameComboBox OutputBusNameComboBox BusNameToolButton OmniCheckBox ChannelSpinBox InstrumentComboBox BankSelMethodComboBox DrumsCheckBox BankComboBox ProgComboBox ForegroundColorComboBox ForegroundColorToolButton BackgroundColorComboBox BackgroundColorToolButton AutoBackgroundColorCheckBox PluginListView AddPluginToolButton RemovePluginToolButton MoveUpPluginToolButton MoveDownPluginToolButton PluginListLatencyCheckBox DialogButtonBox
qtractor-1.5.9/src/PaxHeaders/qtractorMidiControlCommand.cpp0000644000000000000000000000013215101070305021230 xustar0030 mtime=1761898693.077267623 30 atime=1761898693.077267623 30 ctime=1761898693.077267623 qtractor-1.5.9/src/qtractorMidiControlCommand.cpp0000644000175000001440000000446015101070305021224 0ustar00rncbcusers// qtractorMidiControlCommand.cpp // /**************************************************************************** Copyright (C) 2010-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 "qtractorMidiControlCommand.h" #include "qtractorMidiControlObserver.h" //---------------------------------------------------------------------- // class qtractorMidiControlObserverCommand - declaration. // // Constructor. qtractorMidiControlObserverCommand::qtractorMidiControlObserverCommand ( const QString& sName, qtractorMidiControlObserver *pMidiObserver, QWidget *pMidiObserverWidget ) : qtractorCommand(sName), m_pMidiObserver(pMidiObserver), m_pMidiObserverWidget(pMidiObserverWidget) { setRefresh(false); } // Map/unmap observer methods. bool qtractorMidiControlObserverCommand::mapMidiObserver (void) const { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return false; if (pMidiControl->isMidiObserverMapped(m_pMidiObserver)) return false; pMidiControl->mapMidiObserver(m_pMidiObserver, m_pMidiObserverWidget); return true; } // Unmap observer methods. bool qtractorMidiControlObserverCommand::unmapMidiObserver (void) const { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return false; if (!pMidiControl->isMidiObserverMapped(m_pMidiObserver)) return false; pMidiControl->unmapMidiObserver(m_pMidiObserver, true); return true; } // end of qtractorMidiControlCommand.cpp qtractor-1.5.9/src/PaxHeaders/qtractorInstrumentForm.ui0000644000000000000000000000013215101070305020335 xustar0030 mtime=1761898693.072267607 30 atime=1761898693.072267607 30 ctime=1761898693.072267607 qtractor-1.5.9/src/qtractorInstrumentForm.ui0000644000175000001440000001565115101070305020335 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.9/src/PaxHeaders/qtractorEngine.cpp0000644000000000000000000000013215101070305016713 xustar0030 mtime=1761898693.070267601 30 atime=1761898693.070267601 30 ctime=1761898693.070267601 qtractor-1.5.9/src/qtractorEngine.cpp0000644000175000001440000006462215101070305016715 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.9/src/PaxHeaders/qtractorCurveCommand.cpp0000644000000000000000000000013215101070305020071 xustar0030 mtime=1761898693.069267597 30 atime=1761898693.069267597 30 ctime=1761898693.069267597 qtractor-1.5.9/src/qtractorCurveCommand.cpp0000644000175000001440000003314315101070305020065 0ustar00rncbcusers// qtractorCurveCommand.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 "qtractorCurveCommand.h" #include "qtractorSession.h" #include "qtractorMainForm.h" #include "qtractorTracks.h" #include "qtractorPlugin.h" //---------------------------------------------------------------------- // class qtractorCurveBaseCommand - declaration. // // Constructor. qtractorCurveBaseCommand::qtractorCurveBaseCommand ( const QString& sName ) : qtractorCommand(sName) { } // Virtual command methods. bool qtractorCurveBaseCommand::redo (void) { return execute(true); } bool qtractorCurveBaseCommand::undo (void) { return execute(false); } // Virtual command methods. bool qtractorCurveBaseCommand::execute ( bool /*bRedo*/ ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { pSession->updateSession(); if (!pSession->isPlaying()) pSession->process_curve(pSession->playHead()); } qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks) { pTracks->clearSelect(); qtractorTrack *pTrack = pTracks->currentTrack(); if (pTrack) { pTracks->updateTrackList(pTrack); pTrack->refreshPluginForms(); } } } return true; } //---------------------------------------------------------------------- // class qtractorCurveCommand - declaration. // // Constructor. qtractorCurveCommand::qtractorCurveCommand ( const QString& sName, qtractorCurve *pCurve ) : qtractorCurveBaseCommand(sName), m_pCurve(pCurve) { } //---------------------------------------------------------------------- // class qtractorCurveListCommand - declaration. // // Constructor. qtractorCurveListCommand::qtractorCurveListCommand ( const QString& sName, qtractorCurveList *pCurveList ) : qtractorCurveBaseCommand(sName), m_pCurveList(pCurveList) { } //---------------------------------------------------------------------- // class qtractorCurveSelectCommand - declaration. // // Constructor. qtractorCurveSelectCommand::qtractorCurveSelectCommand ( qtractorCurveList *pCurveList, qtractorCurve *pCurrentCurve ) : qtractorCurveListCommand(QObject::tr("automation select"), pCurveList), m_pCurrentCurve(pCurrentCurve) { } // Virtual command methods. bool qtractorCurveSelectCommand::execute ( bool bRedo ) { if (m_pCurveList == nullptr) return false; qtractorCurve *pCurrentCurve = m_pCurveList->currentCurve(); m_pCurveList->setCurrentCurve(m_pCurrentCurve); m_pCurrentCurve = pCurrentCurve; return qtractorCurveBaseCommand::execute(bRedo); } //---------------------------------------------------------------------- // class qtractorCurveModeCommand - declaration. // // Constructor. qtractorCurveModeCommand::qtractorCurveModeCommand ( qtractorCurve *pCurve, qtractorCurve::Mode mode ) : qtractorCurveCommand(QObject::tr("automation mode"), pCurve), m_mode(mode) { } // Virtual command methods. bool qtractorCurveModeCommand::execute ( bool bRedo ) { if (m_pCurve == nullptr) return false; qtractorCurve::Mode mode = m_pCurve->mode(); m_pCurve->setMode(m_mode); m_pCurve->update(); m_mode = mode; return qtractorCurveBaseCommand::execute(bRedo); } //---------------------------------------------------------------------- // class qtractorCurveProcessCommand - declaration. // // Constructor. qtractorCurveProcessCommand::qtractorCurveProcessCommand ( qtractorCurve *pCurve, bool bProcess ) : qtractorCurveCommand(QObject::tr("automation play"), pCurve), m_bProcess(bProcess) { } // Virtual command methods. bool qtractorCurveProcessCommand::execute ( bool bRedo ) { if (m_pCurve == nullptr) return false; const bool bProcess = m_pCurve->isProcess(); if (!m_bProcess) m_pCurve->setCapture(false); m_pCurve->setProcess(m_bProcess); m_bProcess = bProcess; return qtractorCurveBaseCommand::execute(bRedo); } //---------------------------------------------------------------------- // class qtractorCurveCaptureCommand - declaration. // // Constructor. qtractorCurveCaptureCommand::qtractorCurveCaptureCommand ( qtractorCurve *pCurve, bool bCapture ) : qtractorCurveCommand(QObject::tr("automation record"), pCurve), m_bCapture(bCapture) { } // Virtual command methods. bool qtractorCurveCaptureCommand::execute ( bool bRedo ) { if (m_pCurve == nullptr) return false; const bool bCapture = m_pCurve->isCapture(); if (m_bCapture) m_pCurve->setProcess(true); m_pCurve->setCapture(m_bCapture); m_bCapture = bCapture; return qtractorCurveBaseCommand::execute(bRedo); } //---------------------------------------------------------------------- // class qtractorCurveLogarithmicCommand - declaration. // // Constructor. qtractorCurveLogarithmicCommand::qtractorCurveLogarithmicCommand ( qtractorCurve *pCurve, bool bLogarithmic ) : qtractorCurveCommand(QObject::tr("automation logarithmic"), pCurve), m_bLogarithmic(bLogarithmic) { } // Virtual command methods. bool qtractorCurveLogarithmicCommand::execute ( bool /*bRedo*/ ) { if (m_pCurve == nullptr) return false; const bool bLogarithmic = m_pCurve->isLogarithmic(); m_pCurve->setLogarithmic(m_bLogarithmic); m_bLogarithmic = bLogarithmic; return true; } //---------------------------------------------------------------------- // class qtractorCurveCaptureCommand - declaration. // // Constructor. qtractorCurveColorCommand::qtractorCurveColorCommand ( qtractorCurve *pCurve, const QColor& color ) : qtractorCurveCommand(QObject::tr("automation color"), pCurve), m_color(color) { } // Virtual command methods. bool qtractorCurveColorCommand::execute ( bool /*bRedo*/ ) { if (m_pCurve == nullptr) return false; const QColor color = m_pCurve->color(); m_pCurve->setColor(m_color); m_color = color; return true; } //---------------------------------------------------------------------- // class qtractorCurveProcessAllCommand - declaration. // // Constructor. qtractorCurveProcessAllCommand::qtractorCurveProcessAllCommand ( qtractorCurveList *pCurveList, bool bProcessAll ) : qtractorCurveListCommand(QObject::tr("automation play all"), pCurveList), m_bProcessAll(bProcessAll) { } // Virtual command methods. bool qtractorCurveProcessAllCommand::execute ( bool bRedo ) { if (m_pCurveList == nullptr) return false; const bool bProcessAll = m_pCurveList->isProcessAll(); if (!m_bProcessAll) m_pCurveList->setCaptureAll(false); m_pCurveList->setProcessAll(m_bProcessAll); m_bProcessAll = bProcessAll; return qtractorCurveBaseCommand::execute(bRedo); } //---------------------------------------------------------------------- // class qtractorCurveCaptureAllCommand - declaration. // // Constructor. qtractorCurveCaptureAllCommand::qtractorCurveCaptureAllCommand ( qtractorCurveList *pCurveList, bool bCaptureAll ) : qtractorCurveListCommand(QObject::tr("automation record all"), pCurveList), m_bCaptureAll(bCaptureAll) { } // Virtual command methods. bool qtractorCurveCaptureAllCommand::execute ( bool bRedo ) { if (m_pCurveList == nullptr) return false; const bool bCaptureAll = m_pCurveList->isCaptureAll(); if (m_bCaptureAll) m_pCurveList->setProcessAll(true); m_pCurveList->setCaptureAll(m_bCaptureAll); m_bCaptureAll = bCaptureAll; return qtractorCurveBaseCommand::execute(bRedo); } //---------------------------------------------------------------------- // class qtractorCurveEditCommand - declaration. // // Constructors. qtractorCurveEditCommand::qtractorCurveEditCommand ( const QString& sName, qtractorCurve *pCurve ) : qtractorCurveCommand(sName, pCurve), m_edits(pCurve) { } qtractorCurveEditCommand::qtractorCurveEditCommand ( qtractorCurve *pCurve ) : qtractorCurveCommand(QObject::tr("automation edit"), pCurve), m_edits(pCurve) { } // Destructor. qtractorCurveEditCommand::~qtractorCurveEditCommand (void) { m_edits.clear(); } // Primitive command methods. void qtractorCurveEditCommand::addNode ( qtractorCurve::Node *pNode ) { m_edits.addNode(pNode); } void qtractorCurveEditCommand::moveNode ( qtractorCurve::Node *pNode, unsigned long iFrame, float fValue ) { m_edits.moveNode(pNode, iFrame, fValue); } void qtractorCurveEditCommand::removeNode ( qtractorCurve::Node *pNode ) { m_edits.removeNode(pNode); } void qtractorCurveEditCommand::addEditList ( qtractorCurveEditList *pEditList ) { if (pEditList) m_edits.append(*pEditList); } // Composite predicate. bool qtractorCurveEditCommand::isEmpty (void) const { return m_edits.isEmpty(); } // Common executive method. bool qtractorCurveEditCommand::execute ( bool bRedo ) { if (!m_edits.execute(bRedo)) return false; return qtractorCurveBaseCommand::execute(bRedo); } //---------------------------------------------------------------------- // class qtractorCurveClearCommand - declaration. // // Constructor. qtractorCurveClearCommand::qtractorCurveClearCommand ( qtractorCurve *pCurve ) : qtractorCurveEditCommand(QObject::tr("automation clear"), pCurve) { qtractorCurve::Node *pNode = m_pCurve->nodes().first(); while (pNode) { qtractorCurveEditCommand::removeNode(pNode); pNode = pNode->next(); } } //---------------------------------------------------------------------- // class qtractorCurveClearAllCommand - declaration. // // Constructor. qtractorCurveClearAllCommand::qtractorCurveClearAllCommand ( qtractorCurveList *pCurveList ) : qtractorCurveListCommand(QObject::tr("automation clear all"), pCurveList) { if (m_pCurveList) { qtractorCurve *pCurve = m_pCurveList->first(); while (pCurve) { m_commands.append(new qtractorCurveClearCommand(pCurve)); pCurve = pCurve->next(); } } } // Destructor. qtractorCurveClearAllCommand::~qtractorCurveClearAllCommand (void) { qDeleteAll(m_commands); m_commands.clear(); } // Composite predicate. bool qtractorCurveClearAllCommand::isEmpty (void) const { return m_commands.isEmpty(); } // Virtual executive method. bool qtractorCurveClearAllCommand::execute ( bool bRedo ) { if (m_pCurveList == nullptr) return false; QListIterator iter(m_commands); while (iter.hasNext()) { qtractorCurveClearCommand *pCommand = iter.next(); if (bRedo) pCommand->redo(); else pCommand->undo(); } return true; } //---------------------------------------------------------------------- // class qtractorCurveEditListCommand - declaration. // // Constructor. qtractorCurveEditListCommand::qtractorCurveEditListCommand ( qtractorCurveList *pCurveList ) : qtractorCurveListCommand(QObject::tr("automation edit list"), pCurveList) { if (m_pCurveList) { qtractorCurve *pCurve = m_pCurveList->first(); while (pCurve) { qtractorCurveEditList *pCurveEditList = pCurve->editList(); if (pCurveEditList && !pCurveEditList->isEmpty()) { qtractorCurveEditCommand *pCurveEditCommand = new qtractorCurveEditCommand(pCurve); pCurveEditCommand->addEditList(pCurveEditList); m_curveEditCommands.append(pCurveEditCommand); pCurveEditList->clear(); } pCurve = pCurve->next(); } } } // Destructor. qtractorCurveEditListCommand::~qtractorCurveEditListCommand (void) { qDeleteAll(m_curveEditCommands); m_curveEditCommands.clear(); } // Composite predicate. bool qtractorCurveEditListCommand::isEmpty (void) const { return m_curveEditCommands.isEmpty(); } // Virtual executive method. bool qtractorCurveEditListCommand::execute ( bool bRedo ) { if (m_pCurveList == nullptr) return false; QListIterator iter(m_curveEditCommands); while (iter.hasNext()) { qtractorCurveEditCommand *pCurveEditCommand = iter.next(); if (bRedo) pCurveEditCommand->redo(); else pCurveEditCommand->undo(); } return true; } //---------------------------------------------------------------------- // class qtractorCurveCaptureListCommand - declaration. // // Constructor. qtractorCurveCaptureListCommand::qtractorCurveCaptureListCommand (void) : qtractorCommand(QObject::tr("automation record")) { } // Destructor. qtractorCurveCaptureListCommand::~qtractorCurveCaptureListCommand (void) { qDeleteAll(m_commands); m_commands.clear(); } // Curve list adder. void qtractorCurveCaptureListCommand::addCurveList ( qtractorCurveList *pCurveList ) { qtractorCurveEditListCommand *pCommand = new qtractorCurveEditListCommand(pCurveList); if (pCommand->isEmpty()) delete pCommand; else m_commands.append(pCommand); } // Composite predicate. bool qtractorCurveCaptureListCommand::isEmpty (void) const { return m_commands.isEmpty(); } // Virtual command methods. bool qtractorCurveCaptureListCommand::redo (void) { QListIterator iter(m_commands); while (iter.hasNext()) iter.next()->redo(); return true; } bool qtractorCurveCaptureListCommand::undo (void) { QListIterator iter(m_commands); while (iter.hasNext()) iter.next()->undo(); return true; } // end of qtractorCurveCommand.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMidiEventList.cpp0000644000000000000000000000013215101070305020226 xustar0030 mtime=1761898693.082267639 30 atime=1761898693.082267639 30 ctime=1761898693.082267639 qtractor-1.5.9/src/qtractorMidiEventList.cpp0000644000175000001440000007270015101070305020224 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.9/src/PaxHeaders/qtractorSession.cpp0000644000000000000000000000013215101070305017131 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.088267658 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorSession.cpp0000644000175000001440000021107715101070305017131 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.9/src/PaxHeaders/mimetypes0000644000000000000000000000013215101070305015161 xustar0030 mtime=1761898693.061679615 30 atime=1761898693.061597574 30 ctime=1761898693.061679615 qtractor-1.5.9/src/mimetypes/0000755000175000001440000000000015101070305015226 5ustar00rncbcusersqtractor-1.5.9/src/mimetypes/PaxHeaders/org.rncbc.qtractor.application-x-qtractor-archive.svg0000644000000000000000000000013215101070305027515 xustar0030 mtime=1761898693.061679615 30 atime=1761898693.061597574 30 ctime=1761898693.061679615 qtractor-1.5.9/src/mimetypes/org.rncbc.qtractor.application-x-qtractor-archive.svg0000644000175000001440000030167315101070305027517 0ustar00rncbcusers qtractor-1.5.9/src/mimetypes/PaxHeaders/org.rncbc.qtractor.application-x-qtractor-template.png0000644000000000000000000000013215101070305027674 xustar0030 mtime=1761898693.061679615 30 atime=1761898693.061679615 30 ctime=1761898693.061679615 qtractor-1.5.9/src/mimetypes/org.rncbc.qtractor.application-x-qtractor-template.png0000644000175000001440000000270315101070305027666 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-1.5.9/src/mimetypes/PaxHeaders/org.rncbc.qtractor.application-x-qtractor-session.svg0000644000000000000000000000013215101070305027557 xustar0030 mtime=1761898693.061679615 30 atime=1761898693.061679615 30 ctime=1761898693.061679615 qtractor-1.5.9/src/mimetypes/org.rncbc.qtractor.application-x-qtractor-session.svg0000644000175000001440000004223115101070305027551 0ustar00rncbcusers qtractor-1.5.9/src/mimetypes/PaxHeaders/org.rncbc.qtractor.application-x-qtractor-archive.png0000644000000000000000000000013215101070305027502 xustar0030 mtime=1761898693.061597574 30 atime=1761898693.061597574 30 ctime=1761898693.061597574 qtractor-1.5.9/src/mimetypes/org.rncbc.qtractor.application-x-qtractor-archive.png0000644000175000001440000000354515101070305027501 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.9/src/mimetypes/PaxHeaders/org.rncbc.qtractor.xml0000644000000000000000000000013215101070305021473 xustar0030 mtime=1761898693.061679615 30 atime=1761898693.061679615 30 ctime=1761898693.061679615 qtractor-1.5.9/src/mimetypes/org.rncbc.qtractor.xml0000644000175000001440000000126015101070305021462 0ustar00rncbcusers Qtractor session Qtractor template Qtractor archive qtractor-1.5.9/src/mimetypes/PaxHeaders/org.rncbc.qtractor.application-x-qtractor-session.png0000644000000000000000000000013215101070305027544 xustar0030 mtime=1761898693.061679615 30 atime=1761898693.061679615 30 ctime=1761898693.061679615 qtractor-1.5.9/src/mimetypes/org.rncbc.qtractor.application-x-qtractor-session.png0000644000175000001440000000272315101070305027540 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 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.9/src/PaxHeaders/qtractorAudioPeak.h0000644000000000000000000000013215101070305017015 xustar0030 mtime=1761898693.064267582 30 atime=1761898693.064267582 30 ctime=1761898693.064267582 qtractor-1.5.9/src/qtractorAudioPeak.h0000644000175000001440000001442415101070305017012 0ustar00rncbcusers// qtractorAudioPeak.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 __qtractorAudioPeak_h #define __qtractorAudioPeak_h #include #include #include #include #include // Forward declarations. class qtractorAudioPeakThread; //---------------------------------------------------------------------- // class qtractorAudioPeakFile -- Audio peak file (ref'counted) // class qtractorAudioPeakFile { public: // Constructor. qtractorAudioPeakFile(const QString& sFilename, float fTimeStretch); // Default destructor. ~qtractorAudioPeakFile(); // Audio properties accessors. const QString& filename() const; float timeStretch() const; QString peakName() const; // Peak cache properties accessors. QString name() const; unsigned short period(); unsigned short channels(); // Audio peak file header. struct Header { unsigned short period; unsigned short channels; }; // Audio peak file frame record. struct Frame { unsigned char max; unsigned char min; unsigned char rms; }; // Peak cache file methods. bool openRead(); Frame *read(unsigned long iPeakOffset, unsigned int iPeakLength); void closeRead(); // Write peak from audio frame methods. bool openWrite(unsigned short iChannels, unsigned int iSampleRate); int write(float **ppAudioFrames, unsigned int iAudioFrames); void closeWrite(); // Reference count methods. void addRef(); void removeRef(); // Physical removal. void remove(); // Clean/close method. void cleanup(bool bAutoRemove = false); // Sync thread state flags accessors. void setWaitSync(bool bWaitSync); bool isWaitSync() const; // Peak filename standard. static QString peakName(const QString& sFilename, float fTimeStretch); protected: // Internal creational methods. void writeFrame(); // Read frames from peak file into local buffer cache. unsigned int readBuffer(unsigned int iBuffOffset, unsigned long iPeakOffset, unsigned int iPeakFrames); private: // Instance variables. QString m_sFilename; float m_fTimeStretch; QFile m_peakFile; enum { None = 0, Read = 1, Write = 2 } m_openMode; Header m_peakHeader; Frame *m_pBuffer; unsigned int m_iBuffSize; unsigned int m_iBuffLength; unsigned long m_iBuffOffset; QMutex m_mutex; volatile bool m_bWaitSync; // Current reference count. unsigned int m_iRefCount; // Peak-writer context state. struct Writer { unsigned long offset; float *amax; float *amin; float *arms; unsigned long period_p; unsigned int period_q; unsigned int period_r; unsigned long nframe; unsigned short npeak; unsigned long nread; unsigned long nwrite; } *m_pWriter; }; //---------------------------------------------------------------------- // class qtractorAudioPeak -- Audio Peak file pseudo-cache. // class qtractorAudioPeak { public: // Constructor. qtractorAudioPeak(qtractorAudioPeakFile *pPeakFile); // Copy onstructor. qtractorAudioPeak(const qtractorAudioPeak& peak); // Default destructor. ~qtractorAudioPeak(); // Reference accessor. qtractorAudioPeakFile *peakFile() const { return m_pPeakFile; } // Peak file accessors. const QString& filename() const { return m_pPeakFile->filename(); } // Peak cache properties. unsigned short period() const { return m_pPeakFile->period(); } unsigned short channels() const { return m_pPeakFile->channels(); } // Peak frame buffer reader-cache executive. qtractorAudioPeakFile::Frame *peakFrames( unsigned long iFrameOffset, unsigned long iFrameLength, int width); // Peak frame buffer length (in frames). unsigned int peakLength() const { return m_iPeakLength; } private: // Instance variable (ref'counted). qtractorAudioPeakFile *m_pPeakFile; // Interim scaling buffer and hash. qtractorAudioPeakFile::Frame *m_pPeakFrames; unsigned int m_iPeakLength; unsigned int m_iPeakHash; }; //---------------------------------------------------------------------- // class qtractorAudioPeakFactory -- Audio peak file factory (singleton). // class qtractorAudioPeakFactory : public QObject { Q_OBJECT public: // Constructor. qtractorAudioPeakFactory(QObject *pParent = nullptr); // Default destructor. ~qtractorAudioPeakFactory(); // The peak period accessors. void setPeakPeriod(unsigned short iPeakPeriod); unsigned short peakPeriod() const; // The peak file factory-methods. qtractorAudioPeak *createPeak( const QString& sFilename, float fTimeStretch = 1.0f); // Auto-delete property. void setAutoRemove(bool bAutoRemove); bool isAutoRemove() const; // Peak ready event notification. void notifyPeakEvent(); // Base sync method. void sync(qtractorAudioPeakFile *pPeakFile = nullptr); // Cleanup method. void cleanup(); // Singleton instance accessor. static qtractorAudioPeakFactory *getInstance(); signals: // Peak ready signal. void peakEvent(); private: // Factory mutex. QMutex m_mutex; // The list of managed peak files. typedef QHash PeakFiles; PeakFiles m_peaks; // Auto-delete property. bool m_bAutoRemove; // The peak file creation detached thread. qtractorAudioPeakThread *m_pPeakThread; // The current running peak-period. unsigned short m_iPeakPeriod; // The pseudo-singleton instance. static qtractorAudioPeakFactory *g_pPeakFactory; }; #endif // __qtractorAudioPeak_h // end of qtractorAudioPeak.h qtractor-1.5.9/src/PaxHeaders/qtractorExportForm.h0000644000000000000000000000013215101070305017260 xustar0030 mtime=1761898693.071267604 30 atime=1761898693.070267601 30 ctime=1761898693.071267604 qtractor-1.5.9/src/qtractorExportForm.h0000644000175000001440000000770315101070305017257 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.9/src/PaxHeaders/qtractorMessageList.h0000644000000000000000000000012715101070305017377 xustar0029 mtime=1761898693.07626762 29 atime=1761898693.07626762 29 ctime=1761898693.07626762 qtractor-1.5.9/src/qtractorMessageList.h0000644000175000001440000000320315101070305017361 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.9/src/PaxHeaders/qtractorTrackView.cpp0000644000000000000000000000013215101070305017405 xustar0030 mtime=1761898693.091267667 30 atime=1761898693.091267667 30 ctime=1761898693.091267667 qtractor-1.5.9/src/qtractorTrackView.cpp0000644000175000001440000052077015101070305017410 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.9/src/PaxHeaders/qtractorMidiEditorForm.h0000644000000000000000000000013215101070305020030 xustar0030 mtime=1761898693.081267635 30 atime=1761898693.081267635 30 ctime=1761898693.081267635 qtractor-1.5.9/src/qtractorMidiEditorForm.h0000644000175000001440000001751515101070305020031 0ustar00rncbcusers// qtractorMidiEditorForm.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 __qtractorMidiEditorForm_h #define __qtractorMidiEditorForm_h #include "ui_qtractorMidiEditorForm.h" // Forward declarations... class qtractorMidiEditor; class qtractorMidiClip; class qtractorMidiSequence; class qtractorTimeScale; class qtractorMidiEventList; class qtractorMidiControlTypeGroup; class qtractorInstrumentMenu; class qtractorTimeSpinBox; class qtractorTempoSpinBox; class qtractorTempoCursor; class QContextMenuEvent; class QActionGroup; class QToolButton; class QComboBox; class QLabel; class QPalette; //---------------------------------------------------------------------------- // qtractorMidiEditorForm -- UI wrapper form. class qtractorMidiEditorForm : public QMainWindow { Q_OBJECT public: // Constructor. qtractorMidiEditorForm(QWidget *pParent = nullptr, Qt::WindowFlags wflags = Qt::WindowFlags()); // Destructor. ~qtractorMidiEditorForm(); // MIDI editor widget accessor. qtractorMidiEditor *editor() const; // Local time-scale accessor. qtractorTimeScale *timeScale() const; unsigned long timeOffset() const; // MIDI clip sequence accessors. qtractorMidiClip *midiClip() const; // MIDI clip properties accessors. const QString& filename() const; unsigned short trackChannel() const; unsigned short format() const; qtractorMidiSequence *sequence() const; // Special executive setup method. void setup(qtractorMidiClip *pMidiClip = nullptr); // Reset coomposite dirty flag. void resetDirtyCount(); // Instrument/controller names update. void updateInstrumentNames(); // View/drum-mode update. void updateDrumMode(); // Pre-close event handler. bool queryClose(); // Edit menu accessor. QMenu *editMenu() const; // Save(as) warning message box. static int querySave(const QString& sFilename, QWidget *pParent = nullptr); // Update thumb-view play-head... void updatePlayHead(unsigned long iPlayHead); // Update local time-scale... void updateTimeScale(); public slots: void stabilizeForm(); protected slots: void fileSave(); void fileSaveAs(); void fileMute(); void fileUnlink(); void fileRecordEx(bool bOn); void fileTrackInputs(); void fileTrackOutputs(); void fileTrackProperties(); void fileProperties(); void fileRangeSet(); void fileLoopSet(); void fileClose(); void editUndo(); void editRedo(); void editCut(); void editCopy(); void editPaste(); void editPasteRepeat(); void editDelete(); void editModeOn(bool bOn); void editModeOff(bool bOn); void editModeDraw(bool bOn); void editSelectAll(); void editSelectNone(); void editSelectInvert(); void editSelectRange(); void editInsertRange(); void editInsertStep(); void editRemoveRange(); void toolsQuantize(); void toolsTranspose(); void toolsNormalize(); void toolsRandomize(); void toolsResize(); void toolsRescale(); void toolsTimeshift(); void toolsTemporamp(); void viewMenubar(bool bOn); void viewStatusbar(bool bOn); void viewToolbarFile(bool bOn); void viewToolbarEdit(bool bOn); void viewToolbarView(bool bOn); void viewToolbarTransport(bool bOn); void viewToolbarTime(bool bOn); void viewToolbarScale(bool bOn); void viewToolbarThumb(bool bOn); void viewEvents(bool bOn); void viewNoteNames(bool bOn); void viewNoteDuration(bool bOn); void viewNoteColor(bool bOn); void viewNoteType(); void viewDrumMode(bool bOn); void viewValueColor(bool bOn); void viewValueType(); void viewGhostTrack(); void viewZoomIn(); void viewZoomOut(); void viewZoomReset(); void viewZoomHorizontal(); void viewZoomVertical(); void viewZoomAll(); void viewSnap(); void viewScaleKey(); void viewScaleType(); void viewSnapZebra(bool bOn); void viewSnapGrid(bool bOn); void viewToolTips(bool bOn); void viewRefresh(); void viewPreview(bool bOn); void viewFollow(bool bOn); void transportStepBackward(); void transportStepForward(); void transportStepNoteBackward(); void transportStepNoteForward(); void helpShortcuts(); void helpAbout(); void helpAboutQt(); void updateNoteTypeMenu(); void updateValueTypeMenu(); void updateGhostTrackMenu(); void updateZoomMenu(); void updateSnapMenu(); void updateScaleMenu(); void updateTrackInstrumentMenu(); void sendNote(int iNote, int iVelocity, bool bForce); void sendNoteEx(int iNote, int iVelocity, unsigned long iDuration); void selectionChanged(qtractorMidiEditor *); void contentsChanged(qtractorMidiEditor *); // Event type selection slots. void viewTypeChanged(int); void eventTypeChanged(int); void eventParamChanged(int); void snapToScaleKeyChanged(int iSnapToScaleKey); void snapToScaleTypeChanged(int iSnapToScaleType); void transportTimeFormatChanged(int iDisplayFormat); void transportTimeChanged(unsigned long iPlayHead); void transportTimeFinished(); void transportTempoChanged(float fTempo, unsigned short iBeatsPerBar, unsigned short iBeatDivisor); void transportTempoFinished(); void transportTempoContextMenu(const QPoint& pos); void timeSig2ResetClicked(); void snapPerBeatChanged(int iSnapPerBeat); // Top-level window geometry related slots. void posChanged(); void sizeChanged(); protected: // On-close event handler. void closeEvent(QCloseEvent *pCloseEvent); // Context menu request. void contextMenuEvent(QContextMenuEvent *pContextMenuEvent); // Save current clip track-channel sequence. bool saveClipFile(bool bPrompt); // Secondary time-signature reset slot. void resetTimeSig2( unsigned short iBeatsPerBar2 = 0, unsigned short iBeatDivisor2 = 0); private: // The Qt-designer UI struct... Ui::qtractorMidiEditorForm m_ui; // Instance variables... qtractorMidiEditor *m_pMidiEditor; // Event list dockable widget. qtractorMidiEventList *m_pMidiEventList; int m_iDirtyCount; // Edit-mode action group up. QActionGroup *m_pEditModeActionGroup; QToolButton *m_pEditModeToolButton; // Custom track/instrument proxy menu. qtractorInstrumentMenu *m_pInstrumentMenu; // Transport tempo/time-signature tracker. qtractorTempoCursor *m_pTempoCursor; // Transport time/tempo widgets. qtractorTimeSpinBox *m_pTimeSpinBox; qtractorTempoSpinBox *m_pTempoSpinBox; // Secondary time-signature reset button. QToolButton *m_pTimeSig2ResetButton; // View/Snap-to-beat actions (for shortcuts access) QList m_snapPerBeatActions; // Edit snap mode. QComboBox *m_pSnapPerBeatComboBox; // Event type selection widgets... QComboBox *m_pViewTypeComboBox; QComboBox *m_pEventTypeComboBox; QComboBox *m_pEventParamComboBox; qtractorMidiControlTypeGroup *m_pEventTypeGroup; // Snap-to-scale/quantize selection widgets... QComboBox *m_pSnapToScaleKeyComboBox; QComboBox *m_pSnapToScaleTypeComboBox; // Status items. QLabel *m_pTrackNameLabel; QLabel *m_pFileNameLabel; QLabel *m_pTrackChannelLabel; QLabel *m_pStatusModLabel; QLabel *m_pStatusRecLabel; QLabel *m_pStatusMuteLabel; QLabel *m_pDurationLabel; QPalette *m_pRedPalette; QPalette *m_pYellowPalette; }; #endif // __qtractorMidiEditorForm_h // end of qtractorMidiEditorForm.h qtractor-1.5.9/src/PaxHeaders/qtractorConnectForm.h0000644000000000000000000000013215101070305017370 xustar0030 mtime=1761898693.068267594 30 atime=1761898693.068267594 30 ctime=1761898693.068267594 qtractor-1.5.9/src/qtractorConnectForm.h0000644000175000001440000000700315101070305017360 0ustar00rncbcusers// qtractorConnectForm.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 __qtractorConnectForm_h #define __qtractorConnectForm_h #include "ui_qtractorConnectForm.h" #include "qtractorAudioConnect.h" #include "qtractorMidiConnect.h" // Forward declarations... class qtractorSession; class qtractorOptions; //---------------------------------------------------------------------------- // qtractorConnectForm -- UI wrapper form. class qtractorConnectForm : public QWidget { Q_OBJECT public: // Constructor. qtractorConnectForm(QWidget *pParent = nullptr, Qt::WindowFlags wflags = Qt::WindowFlags()); // Destructor. ~qtractorConnectForm(); QTabWidget *connectTabWidget() const { return m_ui.ConnectTabWidget; } QSplitter *audioConnectSplitter() const { return m_ui.AudioConnectSplitter; } QSplitter *midiConnectSplitter() const { return m_ui.MidiConnectSplitter; } QComboBox *audioOClientsComboBox() const { return m_ui.AudioOClientsComboBox; } QComboBox *audioIClientsComboBox() const { return m_ui.AudioIClientsComboBox; } qtractorClientListView *audioOListView() const { return m_ui.AudioOListView; } qtractorClientListView *audioIListView() const { return m_ui.AudioIListView; } QComboBox *midiOClientsComboBox() const { return m_ui.MidiOClientsComboBox; } QComboBox *midiIClientsComboBox() const { return m_ui.MidiIClientsComboBox; } qtractorClientListView *midiOListView() const { return m_ui.MidiOListView; } qtractorClientListView *midiIListView() const { return m_ui.MidiIListView; } public slots: void audioRefresh() { audioUpdate(false); } void audioClear() { audioUpdate(true); } void midiRefresh() { midiUpdate(false); } void midiClear() { midiUpdate(true); } void audioReset(); void midiReset(); protected slots: void audioIClientChanged(); void audioOClientChanged(); void audioConnectSelected(); void audioDisconnectSelected(); void audioDisconnectAll(); void audioConnectChanged(); void audioStabilize(); void midiIClientChanged(); void midiOClientChanged(); void midiConnectSelected(); void midiDisconnectSelected(); void midiDisconnectAll(); void midiConnectChanged(); void midiStabilize(); signals: void connectChanged(); protected: void updateClientsComboBox(QComboBox *pComboBox, qtractorClientListView *pClientListView, const QIcon& icon); void audioUpdate(bool bClear); void midiUpdate(bool bClear); private: // The Qt-designer UI struct... Ui::qtractorConnectForm m_ui; // Instance variables... qtractorAudioConnect *m_pAudioConnect; qtractorMidiConnect *m_pMidiConnect; }; #endif // __qtractorConnectForm_h // end of qtractorConnectForm.h qtractor-1.5.9/src/PaxHeaders/qtractorAudioBuffer.h0000644000000000000000000000013215101070305017346 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.062554172 30 ctime=1761898693.062554172 qtractor-1.5.9/src/qtractorAudioBuffer.h0000644000175000001440000002026115101070305017337 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.9/src/PaxHeaders/qtractorEditRangeForm.h0000644000000000000000000000013215101070305017641 xustar0030 mtime=1761898693.070267601 30 atime=1761898693.070267601 30 ctime=1761898693.070267601 qtractor-1.5.9/src/qtractorEditRangeForm.h0000644000175000001440000000514715101070305017640 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.9/src/PaxHeaders/qtractorInsertPlugin.h0000644000000000000000000000013215101070305017576 xustar0030 mtime=1761898693.072267607 30 atime=1761898693.072267607 30 ctime=1761898693.072267607 qtractor-1.5.9/src/qtractorInsertPlugin.h0000644000175000001440000003167615101070305017603 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; protected: // Plugin configuration (connections). void freezeConfigs(int iBusMode); private: // Instance variables. qtractorAudioBus *m_pAudioBus; Param *m_pSendGainParam; Param *m_pDryGainParam; Param *m_pWetGainParam; // 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; } }; //---------------------------------------------------------------------------- // 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.9/src/PaxHeaders/qtractorTimeScaleCommand.cpp0000644000000000000000000000013215101070305020653 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorTimeScaleCommand.cpp0000644000175000001440000006705615101070305020661 0ustar00rncbcusers// qtractorTimeScaleCommand.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 "qtractorTimeScaleCommand.h" #include "qtractorClipCommand.h" #include "qtractorCurveCommand.h" #include "qtractorSession.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include "qtractorAudioClip.h" #include "qtractorMidiClip.h" //---------------------------------------------------------------------- // class qtractorTimeScaleNodeCommand - implementation. // // Constructor. qtractorTimeScaleNodeCommand::qtractorTimeScaleNodeCommand ( const QString& sName, qtractorTimeScale *pTimeScale, unsigned long iFrame, float fTempo, unsigned short iBeatType, unsigned short iBeatsPerBar, unsigned short iBeatDivisor) : qtractorCommand(sName), m_pTimeScale(pTimeScale), m_iFrame(iFrame), m_fTempo(fTempo), m_iBeatType(iBeatType), m_iBeatsPerBar(iBeatsPerBar), m_iBeatDivisor(iBeatDivisor), m_bAutoTimeStretch(false), m_pClipCommand(nullptr) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) m_bAutoTimeStretch = pSession->isAutoTimeStretch(); setClearSelectReset(true); } // Destructor. qtractorTimeScaleNodeCommand::~qtractorTimeScaleNodeCommand (void) { if (m_pClipCommand) delete m_pClipCommand; qDeleteAll(m_curveEditCommands); m_curveEditCommands.clear(); } // Add time-scale node command method. bool qtractorTimeScaleNodeCommand::addNode (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorTimeScale::Cursor& cursor = m_pTimeScale->cursor(); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iFrame); if (pNode == nullptr) return false; // If currently playing, we need to do a stop and go... const bool bPlaying = pSession->isPlaying(); const unsigned long iPlayHead = pSession->playHead(); pSession->lock(); if (bPlaying) pSession->seek(0, false); qtractorTimeScale::Node *pNext = pNode->next(); const unsigned long iFrameStart = m_iFrame; const unsigned long iFrameEnd = (pNext ? pNext->frame : pSession->sessionEnd()); const float fOldTempo = pNode->tempo; const float fNewTempo = m_fTempo; const bool bRedoClipCommand = (m_pClipCommand == nullptr); if (bRedoClipCommand) { m_pClipCommand = createClipCommand( iFrameStart, iFrameEnd, fNewTempo, fOldTempo); } else if (m_pClipCommand) { m_pClipCommand->undo(); delete m_pClipCommand; m_pClipCommand = nullptr; } pNode = m_pTimeScale->addNode( m_iFrame, m_fTempo, m_iBeatType, m_iBeatsPerBar, m_iBeatDivisor); m_iFrame = pNode->frame; const bool bRedoCurveEditCommands = m_curveEditCommands.isEmpty(); if (bRedoCurveEditCommands) { addCurveEditCommands(iFrameStart, iFrameEnd, fNewTempo, fOldTempo); } else { QListIterator undos(m_curveEditCommands); while (undos.hasNext()) undos.next()->undo(); qDeleteAll(m_curveEditCommands); m_curveEditCommands.clear(); } if (m_pClipCommand && bRedoClipCommand) m_pClipCommand->redo(); if (bRedoCurveEditCommands) { QListIterator redos(m_curveEditCommands); while (redos.hasNext()) redos.next()->redo(); } // Restore playback state, if needed... if (bPlaying) { // Resync all clips on play-head... pSession->seek(iPlayHead, true); // The Audio engine too... if (pSession->audioEngine()) pSession->audioEngine()->resetMetro(); // The MIDI engine queue needs a reset... if (pSession->midiEngine()) pSession->midiEngine()->resetTempo(); } else { // Force JACK Timebase state, if applicable... if (pSession->audioEngine()) pSession->audioEngine()->resetTimebase(); } pSession->updateSession(); pSession->unlock(); return true; } // Update time-scale node command method. bool qtractorTimeScaleNodeCommand::updateNode (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iFrame); if (pNode == nullptr) return false; if (pNode->frame != m_iFrame) return false; // If currently playing, we need to do a stop and go... const bool bPlaying = pSession->isPlaying(); const unsigned long iPlayHead = pSession->playHead(); pSession->lock(); if (bPlaying) pSession->seek(0, false); const float fTempo = pNode->tempo; const unsigned short iBeatType = pNode->beatType; const unsigned short iBeatsPerBar = pNode->beatsPerBar; const unsigned short iBeatDivisor = pNode->beatDivisor; qtractorTimeScale::Node *pNext = pNode->next(); const unsigned long iFrameStart = pNode->frame; const unsigned long iFrameEnd = (pNext ? pNext->frame : pSession->sessionEnd()); const float fOldTempo = pNode->tempo; const float fNewTempo = m_fTempo; const bool bRedoClipCommand = (m_pClipCommand == nullptr); if (bRedoClipCommand) { m_pClipCommand = createClipCommand( iFrameStart, iFrameEnd, fNewTempo, fOldTempo); } else if (m_pClipCommand) { m_pClipCommand->undo(); delete m_pClipCommand; m_pClipCommand = nullptr; } const bool bRedoCurveEditCommands = m_curveEditCommands.isEmpty(); if (bRedoCurveEditCommands) { addCurveEditCommands(iFrameStart, iFrameEnd, fNewTempo, fOldTempo); } else { QListIterator undos(m_curveEditCommands); while (undos.hasNext()) undos.next()->undo(); qDeleteAll(m_curveEditCommands); m_curveEditCommands.clear(); } pNode->tempo = m_fTempo; pNode->beatType = m_iBeatType; pNode->beatsPerBar = m_iBeatsPerBar; pNode->beatDivisor = m_iBeatDivisor; m_pTimeScale->updateNode(pNode); m_fTempo = fTempo; m_iBeatType = iBeatType; m_iBeatsPerBar = iBeatsPerBar; m_iBeatDivisor = iBeatDivisor; if (m_pClipCommand && bRedoClipCommand) m_pClipCommand->redo(); if (bRedoCurveEditCommands) { QListIterator redos(m_curveEditCommands); while (redos.hasNext()) redos.next()->redo(); } // Restore playback state, if needed... if (bPlaying) { // Resync all clips on play-head... pSession->seek(iPlayHead, true); // The Audio engine too... if (pSession->audioEngine()) pSession->audioEngine()->resetMetro(); // The MIDI engine queue needs a reset... if (pSession->midiEngine()) pSession->midiEngine()->resetTempo(); } else { // Force JACK Timebase state, if applicable... if (pSession->audioEngine()) pSession->audioEngine()->resetTimebase(); } pSession->updateSession(); pSession->unlock(); return true; } // Remove time-scale node command method. bool qtractorTimeScaleNodeCommand::removeNode (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(m_iFrame); if (pNode == nullptr) return false; if (pNode->frame != m_iFrame) return false; // If currently playing, we need to do a stop and go... const bool bPlaying = pSession->isPlaying(); const unsigned long iPlayHead = pSession->playHead(); pSession->lock(); if (bPlaying) pSession->seek(0, false); qtractorTimeScale::Node *pNext = pNode->next(); const unsigned long iFrameStart = pNode->frame; const unsigned long iFrameEnd = (pNext ? pNext->frame : pSession->sessionEnd()); qtractorTimeScale::Node *pPrev = pNode->prev(); const float fOldTempo = pNode->tempo; const float fNewTempo = (pPrev ? pPrev->tempo : m_pTimeScale->tempo()); const bool bRedoClipCommand = (m_pClipCommand == nullptr); if (bRedoClipCommand) { m_pClipCommand = createClipCommand( iFrameStart, iFrameEnd, fNewTempo, fOldTempo); } else if (m_pClipCommand) { m_pClipCommand->undo(); delete m_pClipCommand; m_pClipCommand = nullptr; } const bool bRedoCurveEditCommands = m_curveEditCommands.isEmpty(); if (bRedoCurveEditCommands) { addCurveEditCommands(iFrameStart, iFrameEnd, fNewTempo, fOldTempo); } else { QListIterator undos(m_curveEditCommands); while (undos.hasNext()) undos.next()->undo(); qDeleteAll(m_curveEditCommands); m_curveEditCommands.clear(); } m_fTempo = pNode->tempo; m_iBeatType = pNode->beatType; m_iBeatsPerBar = pNode->beatsPerBar; m_iBeatDivisor = pNode->beatDivisor; m_pTimeScale->removeNode(pNode); if (m_pClipCommand && bRedoClipCommand) m_pClipCommand->redo(); if (bRedoCurveEditCommands) { QListIterator redos(m_curveEditCommands); while (redos.hasNext()) redos.next()->redo(); } // Restore playback state, if needed... if (bPlaying) { // Resync all clips on play-head... pSession->seek(iPlayHead, true); // The Audio engine too... if (pSession->audioEngine()) pSession->audioEngine()->resetMetro(); // The MIDI engine queue needs a reset... if (pSession->midiEngine()) pSession->midiEngine()->resetTempo(); } else { // Force JACK Timebase state, if applicable... if (pSession->audioEngine()) pSession->audioEngine()->resetTimebase(); } pSession->updateSession(); pSession->unlock(); return true; } // Make it automatic clip time-stretching command (static). qtractorClipCommand *qtractorTimeScaleNodeCommand::createClipCommand ( unsigned long iFrameStart, unsigned long iFrameEnd, float fNewTempo, float fOldTempo ) { if (iFrameStart >= iFrameEnd) return nullptr; if (qAbs(fNewTempo - fOldTempo) < 0.1f) return nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return nullptr; qtractorClipCommand *pClipCommand = nullptr; for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { for (qtractorClip *pClip = pTrack->clips().first(); pClip; pClip = pClip->next()) { if (pClip->clipStart() < iFrameStart || pClip->clipStart() >= iFrameEnd) continue; if (pTrack->trackType() == qtractorTrack::Audio) { qtractorAudioClip *pAudioClip = static_cast (pClip); if (pAudioClip) { if (pClipCommand == nullptr) pClipCommand = new qtractorClipCommand(name()); if (m_bAutoTimeStretch) { const float fTimeStretch = (fOldTempo * pAudioClip->timeStretch()) / fNewTempo; pClipCommand->timeStretchClip(pClip, fTimeStretch); } else { pClipCommand->resetClip(pClip); } } } } } // Take care of possible empty commands... if (pClipCommand && pClipCommand->isEmpty()) { delete pClipCommand; pClipCommand = nullptr; } return pClipCommand; } // Automation curve time-stretching command (static). void qtractorTimeScaleNodeCommand::addCurveEditCommands ( unsigned long iFrameStart, unsigned long iFrameEnd, float fNewTempo, float fOldTempo ) { if (iFrameStart >= iFrameEnd) return; if (qAbs(fNewTempo - fOldTempo) < 0.1f) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const float fFactor = (fOldTempo / fNewTempo); const bool bReverse = (fOldTempo > fNewTempo); const long iFrameDelta = long(iFrameStart) - long(iFrameEnd) + long(float(iFrameEnd - iFrameStart) * fFactor); for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { qtractorCurveList *pCurveList = pTrack->curveList(); if (pCurveList == nullptr) continue; qtractorCurve *pCurve = pCurveList->first(); while (pCurve) { int iCurveEditUpdate = 0; qtractorCurveEditCommand *pCurveEditCommand = new qtractorCurveEditCommand(QString(), pCurve); qtractorCurve::Node *pCurveNode = (bReverse ? pCurve->nodes().last() : pCurve->seek(iFrameStart)); while (pCurveNode) { if (pCurveNode->frame >= iFrameEnd) { const unsigned long iFrame = pCurveNode->frame + iFrameDelta; const float fValue = pCurveNode->value; pCurveEditCommand->moveNode(pCurveNode, iFrame, fValue); ++iCurveEditUpdate; } else if (pCurveNode->frame >= iFrameStart) { const unsigned long iFrame = iFrameStart + (unsigned long) (float(pCurveNode->frame - iFrameStart) * fFactor); const float fValue = pCurveNode->value; pCurveEditCommand->moveNode(pCurveNode, iFrame, fValue); ++iCurveEditUpdate; } else if (bReverse) break; if (bReverse) pCurveNode = pCurveNode->prev(); else pCurveNode = pCurveNode->next(); } if (iCurveEditUpdate > 0) m_curveEditCommands.append(pCurveEditCommand); else delete pCurveEditCommand; pCurve = pCurve->next(); } } } //---------------------------------------------------------------------- // class qtractorTimeScaleAddNodeCommand - implementation. // // Constructor. qtractorTimeScaleAddNodeCommand::qtractorTimeScaleAddNodeCommand ( qtractorTimeScale *pTimeScale, unsigned long iFrame, float fTempo, unsigned short iBeatType, unsigned short iBeatsPerBar, unsigned short iBeatDivisor ) : qtractorTimeScaleNodeCommand( QObject::tr("add tempo node"), pTimeScale, iFrame, fTempo, iBeatType, iBeatsPerBar, iBeatDivisor) { } // Time-scale node command methods. bool qtractorTimeScaleAddNodeCommand::redo (void) { return addNode(); } bool qtractorTimeScaleAddNodeCommand::undo (void) { return removeNode(); } //---------------------------------------------------------------------- // class qtractorTimeScaleUpdateNodeCommand - implementation. // // Constructor. qtractorTimeScaleUpdateNodeCommand::qtractorTimeScaleUpdateNodeCommand ( qtractorTimeScale *pTimeScale, unsigned long iFrame, float fTempo, unsigned short iBeatType, unsigned short iBeatsPerBar, unsigned short iBeatDivisor) : qtractorTimeScaleNodeCommand( QObject::tr("update tempo node"), pTimeScale, iFrame, fTempo, iBeatType, iBeatsPerBar, iBeatDivisor) { } // Time-scale node command methods. bool qtractorTimeScaleUpdateNodeCommand::redo (void) { return updateNode(); } bool qtractorTimeScaleUpdateNodeCommand::undo (void) { return redo(); } //---------------------------------------------------------------------- // class qtractorTimeScaleRemoveNodeCommand - implementation. // // Constructor. qtractorTimeScaleRemoveNodeCommand::qtractorTimeScaleRemoveNodeCommand ( qtractorTimeScale *pTimeScale, qtractorTimeScale::Node *pNode ) : qtractorTimeScaleNodeCommand( QObject::tr("remove tempo node"), pTimeScale, pNode->frame) { } // Time-scale node command methods. bool qtractorTimeScaleRemoveNodeCommand::redo (void) { return removeNode(); } bool qtractorTimeScaleRemoveNodeCommand::undo (void) { return addNode(); } //---------------------------------------------------------------------- // class qtractorTimeScaleMoveNodeCommand - implementation. // // Constructor. qtractorTimeScaleMoveNodeCommand::qtractorTimeScaleMoveNodeCommand ( qtractorTimeScale *pTimeScale, qtractorTimeScale::Node *pNode, unsigned long iFrame ) : qtractorTimeScaleNodeCommand( QObject::tr("move tempo node"), pTimeScale, pNode->frame, pNode->tempo, pNode->beatType, pNode->beatsPerBar, pNode->beatDivisor) { // The new location. m_iNewFrame = pTimeScale->frameFromBar(pTimeScale->barFromFrame(iFrame)); m_iOldFrame = frame(); // Replaced node salvage. qtractorTimeScale::Cursor cursor(pTimeScale); pNode = cursor.seekFrame(m_iNewFrame); if (pNode && pNode->frame == m_iNewFrame) { m_bOldNode = true; m_fOldTempo = pNode->tempo; m_iOldBeatType = pNode->beatType; m_iOldBeatsPerBar = pNode->beatsPerBar; m_iOldBeatDivisor = pNode->beatDivisor; } else { m_bOldNode = false; } } // Time-scale node command methods. bool qtractorTimeScaleMoveNodeCommand::redo (void) { qtractorTimeScale *pTimeScale = timeScale(); if (pTimeScale == nullptr) return false; const unsigned long iNewFrame = m_iNewFrame; const unsigned long iOldFrame = m_iOldFrame; qtractorTimeScale::Cursor cursor(pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(iOldFrame); if (pNode && pNode->frame == iOldFrame) pTimeScale->removeNode(pNode); pTimeScale->addNode(iNewFrame, tempo(), beatType(), beatsPerBar(), beatDivisor()); m_iNewFrame = iOldFrame; m_iOldFrame = iNewFrame; return true; } bool qtractorTimeScaleMoveNodeCommand::undo (void) { qtractorTimeScale *pTimeScale = timeScale(); if (pTimeScale == nullptr) return false; const bool bResult = redo(); if (bResult && m_bOldNode) { pTimeScale->addNode(m_iNewFrame, m_fOldTempo, m_iOldBeatType, m_iOldBeatsPerBar, m_iOldBeatDivisor); } return bResult; } //---------------------------------------------------------------------- // class qtractorTimeScaleMarkerCommand - implementation. // // Constructors. qtractorTimeScaleMarkerCommand::qtractorTimeScaleMarkerCommand ( const QString& sName, qtractorTimeScale *pTimeScale, unsigned long iFrame, const QString& sText, const QColor& rgbColor, int iAccidentals, int iMode ) : qtractorCommand(sName), m_pTimeScale(pTimeScale), m_iFrame(iFrame), m_sText(sText), m_rgbColor(rgbColor), m_iAccidentals(iAccidentals), m_iMode(iMode) { } // Add time-scale marker command method. bool qtractorTimeScaleMarkerCommand::addMarker (void) { return (m_pTimeScale->addMarker(m_iFrame, m_sText, m_rgbColor) != nullptr); } // Update time-scale marker command method. bool qtractorTimeScaleMarkerCommand::updateMarker (void) { qtractorTimeScale::Marker *pMarker = m_pTimeScale->markers().seekFrame(m_iFrame); if (pMarker == nullptr) return false; if (pMarker->frame != m_iFrame) return false; const QString sText = pMarker->text; const QColor rgbColor = pMarker->color; pMarker->text = m_sText; pMarker->color = m_rgbColor; m_pTimeScale->updateMarker(pMarker); m_sText = sText; m_rgbColor = rgbColor; return true; } // Add key-signature command method. bool qtractorTimeScaleMarkerCommand::addKeySignature (void) { return (m_pTimeScale->addKeySignature(m_iFrame, m_iAccidentals, m_iMode) != nullptr); } // Update key-signature command method. bool qtractorTimeScaleMarkerCommand::updateKeySignature (void) { qtractorTimeScale::Marker *pMarker = m_pTimeScale->markers().seekFrame(m_iFrame); if (pMarker == nullptr) return false; if (pMarker->frame != m_iFrame) return false; const int iAccidentals = pMarker->accidentals; const int iMode = pMarker->mode; pMarker->accidentals = m_iAccidentals; pMarker->mode = m_iMode; m_pTimeScale->updateMarker(pMarker); m_iAccidentals = iAccidentals; m_iMode = iMode; return true; } // Remove time-scale marker/key-signature command method. bool qtractorTimeScaleMarkerCommand::removeMarker (void) { qtractorTimeScale::Marker *pMarker = m_pTimeScale->markers().seekFrame(m_iFrame); if (pMarker == nullptr) return false; if (pMarker->frame != m_iFrame) return false; // m_iFrame = pMarker->frame; m_sText = pMarker->text; m_rgbColor = pMarker->color; m_iAccidentals = pMarker->accidentals; m_iMode = pMarker->mode; m_pTimeScale->removeMarker(pMarker); return true; } //---------------------------------------------------------------------- // class qtractorTimeScaleAddMarkerCommand - implementation. // // Constructor. qtractorTimeScaleAddMarkerCommand::qtractorTimeScaleAddMarkerCommand ( qtractorTimeScale *pTimeScale, unsigned long iFrame, const QString& sText, const QColor& rgbColor ) : qtractorTimeScaleMarkerCommand( QObject::tr("add marker"), pTimeScale, iFrame, sText, rgbColor) { } // Time-scale marker command methods. bool qtractorTimeScaleAddMarkerCommand::redo (void) { return addMarker(); } bool qtractorTimeScaleAddMarkerCommand::undo (void) { return removeMarker(); } //---------------------------------------------------------------------- // class qtractorTimeScaleUpdateMarkerCommand - implementation. // // Constructor. qtractorTimeScaleUpdateMarkerCommand::qtractorTimeScaleUpdateMarkerCommand ( qtractorTimeScale *pTimeScale, unsigned long iFrame, const QString& sText, const QColor& rgbColor ) : qtractorTimeScaleMarkerCommand( QObject::tr("update marker"), pTimeScale, iFrame, sText, rgbColor) { } // Time-scale marker command methods. bool qtractorTimeScaleUpdateMarkerCommand::redo (void) { return updateMarker(); } bool qtractorTimeScaleUpdateMarkerCommand::undo (void) { return redo(); } //---------------------------------------------------------------------- // class qtractorTimeScaleRemoveMarkerCommand - implementation. // // Constructor. qtractorTimeScaleRemoveMarkerCommand::qtractorTimeScaleRemoveMarkerCommand ( qtractorTimeScale *pTimeScale, qtractorTimeScale::Marker *pMarker ) : qtractorTimeScaleMarkerCommand( QObject::tr("remove marker"), pTimeScale, pMarker->frame) { } // Time-scale marker command methods. bool qtractorTimeScaleRemoveMarkerCommand::redo (void) { return removeMarker(); } bool qtractorTimeScaleRemoveMarkerCommand::undo (void) { return addMarker(); } //---------------------------------------------------------------------- // class qtractorTimeScaleAddKeySignatureCommand - implementation. // // Constructor. qtractorTimeScaleAddKeySignatureCommand::qtractorTimeScaleAddKeySignatureCommand ( qtractorTimeScale *pTimeScale, unsigned long iFrame, int iAccidentals, int iMode ) : qtractorTimeScaleMarkerCommand( QObject::tr("add key signature"), pTimeScale, iFrame, QString(), Qt::darkGray, iAccidentals, iMode) { } // Time-scale marker command methods. bool qtractorTimeScaleAddKeySignatureCommand::redo (void) { return addKeySignature(); } bool qtractorTimeScaleAddKeySignatureCommand::undo (void) { return removeMarker(); } //---------------------------------------------------------------------- // class qtractorTimeScaleUpdateKeySignatureCommand - implementation. // // Constructor. qtractorTimeScaleUpdateKeySignatureCommand::qtractorTimeScaleUpdateKeySignatureCommand ( qtractorTimeScale *pTimeScale, unsigned long iFrame, int iAccidentals, int iMode ) : qtractorTimeScaleMarkerCommand( QObject::tr("update key signature"), pTimeScale, iFrame, QString(), Qt::darkGray, iAccidentals, iMode) { } // Time-scale marker command methods. bool qtractorTimeScaleUpdateKeySignatureCommand::redo (void) { return updateKeySignature(); } bool qtractorTimeScaleUpdateKeySignatureCommand::undo (void) { return redo(); } //---------------------------------------------------------------------- // class qtractorTimeScaleRemoveKeySignatureCommand - implementation. // // Constructor. qtractorTimeScaleRemoveKeySignatureCommand::qtractorTimeScaleRemoveKeySignatureCommand ( qtractorTimeScale *pTimeScale, qtractorTimeScale::Marker *pMarker ) : qtractorTimeScaleMarkerCommand( QObject::tr("remove key signature"), pTimeScale, pMarker->frame) { } // Time-scale marker command methods. bool qtractorTimeScaleRemoveKeySignatureCommand::redo (void) { return removeMarker(); } bool qtractorTimeScaleRemoveKeySignatureCommand::undo (void) { return addKeySignature(); } //---------------------------------------------------------------------- // class qtractorTimeScaleMoveMarkerCommand - implementation. // // Constructor. qtractorTimeScaleMoveMarkerCommand::qtractorTimeScaleMoveMarkerCommand ( qtractorTimeScale *pTimeScale, qtractorTimeScale::Marker *pMarker, unsigned long iFrame ) : qtractorTimeScaleMarkerCommand( QObject::tr("move marker"), pTimeScale, pMarker->frame, pMarker->text, pMarker->color, pMarker->accidentals, pMarker->mode) { // The new location. m_iNewFrame = pTimeScale->frameFromBar(pTimeScale->barFromFrame(iFrame)); m_iOldFrame = frame(); // Replaced marker salvage. pMarker = pTimeScale->markers().seekFrame(m_iNewFrame); if (pMarker && pMarker->frame == m_iNewFrame) { m_bOldMarker = true; m_sOldText = pMarker->text; m_rgbOldColor = pMarker->color; m_iOldAccidentals = pMarker->accidentals; m_iOldMode = pMarker->mode; } else { m_bOldMarker = false; } } // Time-scale marker command methods. bool qtractorTimeScaleMoveMarkerCommand::redo (void) { qtractorTimeScale *pTimeScale = timeScale(); if (pTimeScale == nullptr) return false; const unsigned long iNewFrame = m_iNewFrame; const unsigned long iOldFrame = m_iOldFrame; qtractorTimeScale::Marker *pMarker = pTimeScale->markers().seekFrame(iOldFrame); if (pMarker && pMarker->frame == iOldFrame) pTimeScale->removeMarker(pMarker); if (!text().isEmpty()) pTimeScale->addMarker(iNewFrame, text(), color()); if (accidentals() || mode()) pTimeScale->addKeySignature(iNewFrame, accidentals(), mode()); m_iNewFrame = iOldFrame; m_iOldFrame = iNewFrame; return true; } bool qtractorTimeScaleMoveMarkerCommand::undo (void) { qtractorTimeScale *pTimeScale = timeScale(); if (pTimeScale == nullptr) return false; const bool bResult = redo(); if (bResult && m_bOldMarker) { if (!m_sOldText.isEmpty()) pTimeScale->addMarker(m_iNewFrame, m_sOldText, m_rgbOldColor); if (m_iOldAccidentals || m_iOldMode) pTimeScale->addKeySignature(m_iNewFrame, m_iOldAccidentals, m_iOldMode); } return bResult; } //---------------------------------------------------------------------- // class qtractorTimeScaleCommand - declaration. // // Constructor. qtractorTimeScaleCommand::qtractorTimeScaleCommand ( const QString& sName ) : qtractorCommand(sName) { } // Destructor. qtractorTimeScaleCommand::~qtractorTimeScaleCommand (void) { qDeleteAll(m_nodeCommands); m_nodeCommands.clear(); } // Node commands. void qtractorTimeScaleCommand::addNodeCommand ( qtractorTimeScaleNodeCommand *pNodeCommand ) { m_nodeCommands.append(pNodeCommand); } // Time-scale command methods. bool qtractorTimeScaleCommand::redo (void) { int iRedos = 0; QListIterator iter(m_nodeCommands); iter.toFront(); while (iter.hasNext()) { if (iter.next()->redo()) ++iRedos; } return (iRedos > 0); } bool qtractorTimeScaleCommand::undo (void) { int iUndos = 0; QListIterator iter(m_nodeCommands); iter.toBack(); while (iter.hasPrevious()) { if (iter.previous()->undo()) ++iUndos; } return (iUndos > 0); } //---------------------------------------------------------------------- // class qtractorTimeScaleCommand - declaration. // // Constructor. qtractorTimeScaleTimeSig2Command::qtractorTimeScaleTimeSig2Command ( qtractorTimeScale *pTimeScale, qtractorMidiClip *pMidiClip, unsigned short iBeatsPerBar2, unsigned short iBeatDivisor2 ) : qtractorCommand(QObject::tr("change time-sig.")), m_pTimeScale(pTimeScale), m_pMidiClip(pMidiClip), m_iBeatsPerBar2(iBeatsPerBar2), m_iBeatDivisor2(iBeatDivisor2) { } // Time-scale command methods. bool qtractorTimeScaleTimeSig2Command::redo (void) { if (m_pTimeScale == nullptr) return false; const unsigned short iBeatsPerBar2 = m_pTimeScale->beatsPerBar2(); const unsigned short iBeatDivisor2 = m_pTimeScale->beatDivisor2(); m_pTimeScale->setBeatsPerBar2(m_iBeatsPerBar2); m_pTimeScale->setBeatDivisor2(m_iBeatDivisor2); if (m_pMidiClip) { m_pMidiClip->setBeatsPerBar2(m_iBeatsPerBar2); m_pMidiClip->setBeatDivisor2(m_iBeatDivisor2); } m_iBeatsPerBar2 = iBeatsPerBar2; m_iBeatDivisor2 = iBeatDivisor2; return true; } bool qtractorTimeScaleTimeSig2Command::undo (void) { return redo(); } // end of qtractorTimeScaleCommand.cpp qtractor-1.5.9/src/PaxHeaders/qtractorCurveSelect.h0000644000000000000000000000013215101070305017377 xustar0030 mtime=1761898693.069267597 30 atime=1761898693.069267597 30 ctime=1761898693.069267597 qtractor-1.5.9/src/qtractorCurveSelect.h0000644000175000001440000000570015101070305017371 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.9/src/PaxHeaders/qtractorAudioSndFile.cpp0000644000000000000000000000013215101070305020014 xustar0030 mtime=1761898693.064267582 30 atime=1761898693.064267582 30 ctime=1761898693.064267582 qtractor-1.5.9/src/qtractorAudioSndFile.cpp0000644000175000001440000001417515101070305020014 0ustar00rncbcusers// qtractorAudioSndFile.cpp // /**************************************************************************** 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. *****************************************************************************/ #include "qtractorAbout.h" #include "qtractorAudioSndFile.h" //---------------------------------------------------------------------- // class qtractorAudioSndFile -- Buffered audio file implementation. // // Constructor. qtractorAudioSndFile::qtractorAudioSndFile ( unsigned short iChannels, unsigned int iSampleRate, unsigned int iBufferSize, int iFormat ) { // Need a minimum of specification, at least for write mode. ::memset(&m_sfinfo, 0, sizeof(m_sfinfo)); m_sfinfo.channels = iChannels; m_sfinfo.samplerate = iSampleRate; m_sfinfo.format = iFormat; // Initialize other stuff. m_pSndFile = nullptr; m_iMode = qtractorAudioSndFile::None; m_pBuffer = nullptr; m_iBufferSize = 1024; // Adjust size the next nearest power-of-two. while (m_iBufferSize < iBufferSize) m_iBufferSize <<= 1; } // Destructor. qtractorAudioSndFile::~qtractorAudioSndFile (void) { close(); } // Open method. bool qtractorAudioSndFile::open ( const QString& sFilename, int iMode ) { #ifdef DEBUG_0 qDebug("qtractorAudioSndFile::open(\"%s\", %d)", sFilename.toUtf8().constData(), iMode); #endif close(); // Whether for Read or Write... int sfmode; switch (iMode) { case qtractorAudioSndFile::Read: sfmode = SFM_READ; break; case qtractorAudioSndFile::Write: sfmode = SFM_WRITE; break; default: return false; } // As said, need a minimum of specification for write mode. if (sfmode & SFM_WRITE) { if (m_sfinfo.channels == 0 || m_sfinfo.samplerate == 0) return false; if (m_sfinfo.format == 0) m_sfinfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; } // Now open it. QByteArray aFilename = sFilename.toUtf8(); m_pSndFile = ::sf_open(aFilename.constData(), sfmode, &m_sfinfo); if (m_pSndFile == nullptr) return false; // Set open mode (deterministically). m_iMode = iMode; // Allocate initial de/interleaving buffer stuff. m_pBuffer = new float [m_sfinfo.channels * m_iBufferSize]; return true; } // Read method. int qtractorAudioSndFile::read ( float **ppFrames, unsigned int iFrames ) { #ifdef DEBUG_0 qDebug("qtractorAudioSndFile::read(%p, %d)", ppFrames, iFrames); #endif allocBufferCheck(iFrames); int nread = ::sf_readf_float(m_pSndFile, m_pBuffer, iFrames); if (nread > 0) { unsigned short i; unsigned int n, k = 0; for (n = 0; n < (unsigned int) nread; ++n) { for (i = 0; i < (unsigned short) m_sfinfo.channels; ++i) ppFrames[i][n] = m_pBuffer[k++]; } } return nread; } // Write method. int qtractorAudioSndFile::write ( float **ppFrames, unsigned int iFrames ) { #ifdef DEBUG_0 qDebug("qtractorAudioSndFile::write(%p, %d)", ppFrames, iFrames); #endif allocBufferCheck(iFrames); unsigned short i; unsigned int n, k = 0; for (n = 0; n < iFrames; ++n) { for (i = 0; i < (unsigned short) m_sfinfo.channels; ++i) m_pBuffer[k++] = ppFrames[i][n]; } return ::sf_writef_float(m_pSndFile, m_pBuffer, iFrames); } // Seek method. bool qtractorAudioSndFile::seek ( unsigned long iOffset ) { #ifdef DEBUG_0 qDebug("qtractorAudioSndFile::seek(%d)", iOffset); #endif return (::sf_seek(m_pSndFile, iOffset, SEEK_SET) == long(iOffset)); } // Close method. void qtractorAudioSndFile::close (void) { #ifdef DEBUG_0 qDebug("qtractorAudioSndFile::close()"); #endif if (m_pSndFile) { ::sf_close(m_pSndFile); m_pSndFile = nullptr; m_iMode = qtractorAudioSndFile::None; } if (m_pBuffer) { delete [] m_pBuffer; m_pBuffer = nullptr; } } // Open mode accessor. int qtractorAudioSndFile::mode (void) const { return m_iMode; } // Open channel(s) accessor. unsigned short qtractorAudioSndFile::channels (void) const { return m_sfinfo.channels; } // Total number of frames specialty. unsigned long qtractorAudioSndFile::frames (void) const { return m_sfinfo.frames; } // Sample rate specialty. unsigned int qtractorAudioSndFile::sampleRate (void) const { return m_sfinfo.samplerate; } // De/interleaving buffer stuff. void qtractorAudioSndFile::allocBufferCheck ( unsigned int iBufferSize ) { // Only reallocate a new buffer if new size is greater... if (iBufferSize > m_iBufferSize && m_sfinfo.channels > 0) { // Destroy previously existing buffer. if (m_pBuffer) delete [] m_pBuffer; // Allocate new extended one; again, // adjust size the next nearest power-of-two. while (m_iBufferSize < iBufferSize) m_iBufferSize <<= 1; m_pBuffer = new float [m_sfinfo.channels * m_iBufferSize]; } } // Check whether given file type/format is valid. (static) bool qtractorAudioSndFile::isValidFormat ( int iType, int iFormat ) { SF_INFO sfinfo; ::memset(&sfinfo, 0, sizeof(sfinfo)); sfinfo.samplerate = 44100; // Dummy samplerate. sfinfo.channels = 2; // Dummy stereo. sfinfo.format = format(iType, iFormat); return bool(::sf_format_check(&sfinfo)); } // Translate format index into libsndfile specific. (static) int qtractorAudioSndFile::format ( int iType, int iFormat ) { iType &= SF_FORMAT_TYPEMASK; // Translate this to some libsndfile slang... switch (iFormat) { case 4: return iType | SF_FORMAT_DOUBLE; case 3: return iType | SF_FORMAT_FLOAT; case 2: return iType | SF_FORMAT_PCM_32; case 1: return iType | SF_FORMAT_PCM_24; case 0: default: return iType | SF_FORMAT_PCM_16; } } // end of qtractorAudioSndFile.cpp qtractor-1.5.9/src/PaxHeaders/qtractorPluginForm.h0000644000000000000000000000013215101070305017235 xustar0030 mtime=1761898693.087267654 30 atime=1761898693.087267654 30 ctime=1761898693.087267654 qtractor-1.5.9/src/qtractorPluginForm.h0000644000175000001440000001241215101070305017225 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.9/src/PaxHeaders/qtractorSession.h0000644000000000000000000000013215101070305016576 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.088267658 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorSession.h0000644000175000001440000004055015101070305016572 0ustar00rncbcusers// qtractorSession.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 __qtractorSession_h #define __qtractorSession_h #include "qtractorAtomic.h" #include "qtractorTrack.h" #include "qtractorTimeScale.h" #include "qtractorDocument.h" #include // Forward declarations. class qtractorClip; // Special forward declarations. class qtractorMidiEngine; class qtractorAudioEngine; class qtractorAudioPeakFactory; class qtractorSessionCursor; class qtractorMidiManager; class qtractorInstrumentList; class qtractorCommandList; class qtractorCommand; class qtractorFileList; class qtractorFiles; //------------------------------------------------------------------------- // qtractorSession -- Session container (singleton). class qtractorSession { public: // Constructor. qtractorSession(); // Default destructor. ~qtractorSession(); // Open/close session engine(s). bool init(); bool open(); void close(); // Reset session. void clear(); // Session directory path accessors. void setSessionDir(const QString& sSessionDir); const QString& sessionDir() const; // Session filename accessors. void setSessionName(const QString& sSessionName); const QString& sessionName() const; // Session description accessors. void setDescription(const QString& sDescription); const QString& description() const; // Session startlength mod-accessors. void updateSession( unsigned long iSessionStart = 0, unsigned long iSessionEnd = 0); unsigned long sessionStart() const; unsigned long sessionEnd() const; // Time-scale helper accessors. qtractorTimeScale *timeScale(); // Device engine common client name accessors. void setClientName(const QString& sClientName); const QString& clientName() const; // Sample rate accessors. void setSampleRate(unsigned int iSampleRate); unsigned int sampleRate() const; // Session tempo accessors. void setTempo(float fTempo); float tempo() const; // Tempo beat type accessors. void setBeatType(unsigned short iBeatType); unsigned short beatType() const; // Resolution accessors. void setTicksPerBeat(unsigned short iTicksPerBeat); unsigned short ticksPerBeat() const; // Beats/Bar(measure) accessors. void setBeatsPerBar(unsigned short iBeatsPerBar); unsigned short beatsPerBar() const; // Time signature (denominator) accessors. void setBeatDivisor(unsigned short iBeatDivisor); unsigned short beatDivisor() const; // Horizontal zoom factor. void setHorizontalZoom(unsigned short iHorizontalZoom); unsigned short horizontalZoom() const; // Vertical zoom factor. void setVerticalZoom(unsigned short iVerticalZoom); unsigned short verticalZoom() const; // Pixels per beat (width). void setPixelsPerBeat(unsigned short iPixelsPerBeat); unsigned short pixelsPerBeat() const; // Beat divisor (snap) accessors. void setSnapPerBeat(unsigned short iSnapPerBeat); unsigned short snapPerBeat(void) const; // Pixel/Tick number conversion. unsigned long tickFromPixel(unsigned int x); unsigned int pixelFromTick(unsigned long iTick); // Pixel/Frame number conversion. unsigned long frameFromPixel(unsigned int x) const; unsigned int pixelFromFrame(unsigned long iFrame) const; // Beat/frame conversion. unsigned long frameFromBeat(unsigned int iBeat); unsigned int beatFromFrame(unsigned long iFrame); // Tick/Frame number conversion. unsigned long frameFromTick(unsigned long iTick); unsigned long tickFromFrame(unsigned long iFrame); // Tick/Frame range conversion (delta conversion). unsigned long frameFromTickRange( unsigned long iTickStart, unsigned long iTickEnd, bool bOffset = false); unsigned long tickFromFrameRange( unsigned long iFrameStart, unsigned long iFrameEnd, bool bOffset = false); // Beat/frame snap filters. unsigned long tickSnap(unsigned long iTick); unsigned long frameSnap(unsigned long iFrame); unsigned int pixelSnap(unsigned int x); // Frame/locate (SMPTE) conversion. unsigned long frameFromLocate(unsigned int iLocate) const; unsigned int locateFromFrame(unsigned long iFrame) const; // Song position pointer (SPP=MIDI beats) to frame converters. unsigned long frameFromSongPos(unsigned int iSongPos); unsigned int songPosFromFrame(unsigned long iFrame); // Update time scale divisor factors. void updateTimeScale(); void updateTimeScaleEx(); // Update time resolution divisor factors. void updateTimeResolution(); // Update from disparate sample-rate. void updateSampleRate(unsigned int iSampleRate); // Track list management methods. const qtractorList& tracks() const; void addTrack(qtractorTrack *pTrack); void insertTrack(qtractorTrack *pTrack, qtractorTrack *pPrevTrack = nullptr); void moveTrack(qtractorTrack *pTrack, qtractorTrack *pNextTrack); void updateTrack(qtractorTrack *pTrack); void unlinkTrack(qtractorTrack *pTrack); qtractorTrack *trackAt(int iTrack) const; // Current number of record-armed tracks. void setRecordTracks(bool bRecord); unsigned int recordTracks() const; // Current number of mued tracks. void setMuteTracks(bool bMute); unsigned int muteTracks() const; // Current number of solo tracks. void setSoloTracks(bool bSolo); unsigned int soloTracks() const; // Temporary current track accessors. void setCurrentTrack(qtractorTrack *pTrack); qtractorTrack *currentTrack() const; // Temporary current track predicates. bool isTrackMonitor(qtractorTrack *pTrack) const; bool isTrackMidiChannel(qtractorTrack *pTrack, unsigned short iChannel) const; // Session cursor factory methods. qtractorSessionCursor *createSessionCursor(unsigned long iFrame = 0, qtractorTrack::TrackType syncType = qtractorTrack::None); void unlinkSessionCursor(qtractorSessionCursor *pSessionCursor); // Reset (reactivate) all plugin chains... void resetAllPlugins(); // Device engine accessors. qtractorMidiEngine *midiEngine() const; qtractorAudioEngine *audioEngine() const; // Wait for application stabilization. static void stabilize(int msecs = 20); // Consolidated session engine activation status. bool isActivated() const; // Session RT-safe pseudo-locking primitives. bool acquire(); void release(); void lock(); void unlock(); // Re-entrancy check. bool isBusy() const; // Consolidated session engine start status. void setPlaying(bool bPlaying); bool isPlaying() const; // Shutdown procedure. void shutdown(); // (Hazardous) bi-directional locate method. void seek(unsigned long iFrame, bool bSync = false); // Playhead positioning. void setPlayHead(unsigned long iPlayHead); void setPlayHeadEx(unsigned long iPlayHead); unsigned long playHead() const; // Auto-backward play-head positioning. void setPlayHeadAutoBackward(unsigned long iPlayHead); unsigned long playHeadAutoBackward() const; // Edit-head positioning. void setEditHead(unsigned long iEditHead); unsigned long editHead() const; // Edit-tail positioning. void setEditTail(unsigned long iEditTail); unsigned long editTail() const; // Session loop points accessors. void setLoop(unsigned long iLoopStart, unsigned long iLoopEnd); unsigned long loopStart() const; unsigned long loopEnd() const; bool isLooping() const; unsigned long loopStartTime() const; unsigned long loopEndTime() const; // Session punch points accessors. void setPunch(unsigned long iPunchIn, unsigned long iPunchOut); unsigned long punchIn() const; unsigned long punchOut() const; bool isPunching() const; unsigned long punchInTime() const; unsigned long punchOutTime() const; // Absolute frame time and offset. unsigned long frameTime() const; unsigned long frameTimeEx() const; // Sanitize a given name. static QString sanitize(const QString& s); // Provide an unique track-name if applicable, // append an incremental numerical suffix... QString uniqueTrackName(const QString& sTrackName) const; void acquireTrackName(qtractorTrack *pTrack); void releaseTrackName(qtractorTrack *pTrack); // Transient file-name registry methods as far to // avoid duplicates across load/save/record cycles... void acquireFilePath(const QString& sFilename); void releaseFilePath(const QString& sFilename); // Create a brand new filename (absolute file path). QString createFilePath( const QString& sBaseName, const QString& sExt, bool bAcquire = false); // Session directory relative/absolute file path helpers. QString relativeFilePath(const QString& sFilename) const; QString absoluteFilePath(const QString& sFilename) const; // Consolidated session record state. void setRecording(bool bRecording); bool isRecording() const; // Loop-recording/take mode. void setLoopRecordingMode(int iLoopRecordingMode); int loopRecordingMode() const; // Track recording specifics. unsigned short audioRecord() const; unsigned short midiRecord() const; // Special track-immediate methods. void trackRecord(qtractorTrack *pTrack, bool bRecord, unsigned long iClipStart, unsigned long iFrameTime); void trackMute(qtractorTrack *pTrack, bool bMute); void trackSolo(qtractorTrack *pTrack, bool bSolo); // Special auto-plugin-deactivation void autoDeactivatePlugins(bool bForce = false); void setAutoDeactivate(bool bOn); bool isAutoDeactivate() const; // Audio peak factory accessor. qtractorAudioPeakFactory *audioPeakFactory() const; // MIDI track tagging specifics. unsigned short midiTag() const; void acquireMidiTag(qtractorTrack *pTrack); void releaseMidiTag(qtractorTrack *pTrack); // MIDI session/tracks instrument/controller patching (conditional). void resetAllMidiControllers(bool bForceImmediate); // MIDI manager list accessors. void addMidiManager(qtractorMidiManager *pMidiManager); void removeMidiManager(qtractorMidiManager *pMidiManager); const qtractorList& midiManagers() const; // Auto time-stretching global flag (when tempo changes) void setAutoTimeStretch(bool bAutoTimeStretch); bool isAutoTimeStretch() const; // Session special process cycle executive. void process(qtractorSessionCursor *pSessionCursor, unsigned long iFrameStart, unsigned long iFrameEnd); // Session special process record executive (audio recording only). void process_record( unsigned long iFrameStart, unsigned long iFrameEnd); // Session special process automation executive. void process_curve(unsigned long iFrame); // Forward declaration. class Document; // Document element methods. bool loadElement(Document *pDocument, QDomElement *pElement); bool saveElement(Document *pDocument, QDomElement *pElement); // Session property 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 sessionDir; QString sessionName; QString description; // Intrinsic time scale. qtractorTimeScale timeScale; }; // Alternate properties accessor. Properties& properties(); // Session command executive (undo/redo) bool execute(qtractorCommand *pCommand); // Session command list reference (undo/redo) qtractorCommandList *commands() const; // Instrument names mapping. qtractorInstrumentList *instruments() const; // Manage curve-lists to specific tracks. void acquireTrackCurveList(qtractorTrack *pTrack); void releaseTrackCurveList(qtractorTrack *pTrack); // Find track of specific curve-list. qtractorTrack *findTrackCurveList(qtractorCurveList *pCurveList) const; // Find track of specific name. qtractorTrack *findTrack(const QString& sTrackName) const; // Session files registry accessor. qtractorFileList *files() const; // Rename session files... void renameSession(const QString& sOldName, const QString& sNewName); // MIDI time adjust to/from official high resolution queue (64bit). unsigned long timep ( unsigned long time ) const { return m_props.timeScale.timep(time); } unsigned long timeq ( unsigned long time ) const { return m_props.timeScale.timeq(time); } // Pseudo-singleton instance accessor. static qtractorSession *getInstance(); private: // check if plugin can be auto deactivated // for now private - maybe helpful for others? bool canTrackBeAutoDeactivated(qtractorTrack *pTrack) const; // Restore activation state void undoAutoDeactivatePlugins(); Properties m_props; // Session properties. unsigned long m_iSessionStart; // Session start in frames. unsigned long m_iSessionEnd; // Session end in frames. unsigned int m_iRecordTracks; // Current number of record-armed tracks. unsigned int m_iMuteTracks; // Current number of muted tracks. unsigned int m_iSoloTracks; // Current number of solo tracks. qtractorTrack *m_pCurrentTrack; // Temporary current track. // The track list. qtractorList m_tracks; // Managed session cursors. qtractorList m_cursors; // Device engine common client name. QString m_sClientName; // Device engine instances. qtractorMidiEngine *m_pMidiEngine; qtractorAudioEngine *m_pAudioEngine; // Audio peak factory (singleton) instance. qtractorAudioPeakFactory *m_pAudioPeakFactory; // Track recording counts. unsigned short m_iAudioRecord; unsigned short m_iMidiRecord; // MIDI track tagging specifics. unsigned short m_iMidiTag; QList m_midiTags; // Base edit members. unsigned long m_iEditHead; unsigned long m_iEditTail; // Session time-normalized edit points. unsigned long m_iEditHeadTime; unsigned long m_iEditTailTime; // Session loop points. unsigned long m_iLoopStart; unsigned long m_iLoopEnd; // Session time-normalized loop points. unsigned long m_iLoopStartTime; unsigned long m_iLoopEndTime; // Session punch points. unsigned long m_iPunchIn; unsigned long m_iPunchOut; // Session time-normalized punch points. unsigned long m_iPunchInTime; unsigned long m_iPunchOutTime; unsigned long m_iPlayHeadAutoBackward; // Consolidated record state. bool m_bRecording; // Loop-recording/take mode. int m_iLoopRecordingMode; // Auto time-stretching global flag (when tempo changes) bool m_bAutoTimeStretch; // Auto disable plugins flag bool m_bAutoDeactivate; // MIDI plugin manager list. qtractorList m_midiManagers; // RT-safeness hackish lock-mutex. qtractorAtomic m_locks; qtractorAtomic m_mutex; // Instrument names mapping. qtractorInstrumentList *m_pInstruments; // Session command executive (undo/redo) qtractorCommandList *m_pCommands; // Curve-to-track mapping. QHash m_curves; // File registry. qtractorFileList *m_pFiles; // Transient file-name registry. QStringList m_filePaths; // Track-name registry. QHash m_trackNames; // The pseudo-singleton instance. static qtractorSession *g_pSession; }; //------------------------------------------------------------------------- // qtractorSession::Document -- Session file import/export helper class. // class qtractorSession::Document : public qtractorDocument { public: // Constructor. Document(QDomDocument *pDocument, qtractorSession *pSession, qtractorFiles *pFiles); // Default destructor. ~Document(); // Property accessors. qtractorSession *session() const; qtractorFiles *files() const; protected: // Elemental loader/savers... bool loadElement(QDomElement *pElement); bool saveElement(QDomElement *pElement); private: // Instance variables. qtractorSession *m_pSession; qtractorFiles *m_pFiles; }; #endif // __qtractorSession_h // end of qtractorSession.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiControl.cpp0000644000000000000000000000013215101070305017731 xustar0030 mtime=1761898693.077267623 30 atime=1761898693.077267623 30 ctime=1761898693.077267623 qtractor-1.5.9/src/qtractorMidiControl.cpp0000644000175000001440000010024415101070305017722 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.9/src/PaxHeaders/qtractorTrackForm.cpp0000644000000000000000000000013215101070305017376 xustar0030 mtime=1761898693.090267664 30 atime=1761898693.090267664 30 ctime=1761898693.090267664 qtractor-1.5.9/src/qtractorTrackForm.cpp0000644000175000001440000014226315101070305017376 0ustar00rncbcusers// qtractorTrackForm.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 "qtractorTrackForm.h" #include "qtractorAbout.h" #include "qtractorTrack.h" #include "qtractorSession.h" #include "qtractorInstrument.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include "qtractorMidiManager.h" #include "qtractorPlugin.h" #include "qtractorOptions.h" #include "qtractorCommand.h" #include "qtractorBusForm.h" #include #include #include #include #include #include #include //---------------------------------------------------------------------- // class qtractorColorItemDelegate -- Custom color view item delegate. // class qtractorColorItemDelegate : public QItemDelegate { public: // Constructor. qtractorColorItemDelegate ( QComboBox *pComboBox ) : QItemDelegate(pComboBox), m_pComboBox(pComboBox) {} // Overridden paint method. void paint(QPainter *pPainter, const QStyleOptionViewItem& option, const QModelIndex& index) const { // Item data has the color... const QPoint delta(2, 2); QRect rect(option.rect); rect.setTopLeft(rect.topLeft() + delta); rect.setBottomRight(rect.bottomRight() - delta); QColor color(m_pComboBox->itemText(index.row())); pPainter->save(); if (option.state & QStyle::State_Selected) pPainter->setPen(QPen(option.palette.highlight().color(), 2)); else pPainter->setPen(QPen(option.palette.base().color(), 2)); pPainter->setBrush(color); pPainter->drawRect(rect); pPainter->restore(); if (option.state & QStyle::State_HasFocus) QItemDelegate::drawFocus(pPainter, option, option.rect); } private: // Item color spec. QComboBox *m_pComboBox; }; //---------------------------------------------------------------------------- // qtractorTrackForm::MidiProgramObserver -- Local dedicated observer. class qtractorTrackForm::MidiProgramObserver : public qtractorObserver { public: // Constructor. MidiProgramObserver(qtractorTrackForm *pTrackForm, qtractorSubject *pSubject) : qtractorObserver(pSubject), m_pTrackForm(pTrackForm) {} protected: // Update feedback. void update(bool bUpdate) { if (bUpdate) { const int iValue = int(value()); const int iBank = (iValue >> 7) & 0x3fff; const int iProg = (iValue & 0x7f); m_pTrackForm->setMidiProgram(iBank, iProg); } } private: // Members. qtractorTrackForm *m_pTrackForm; }; //---------------------------------------------------------------------------- // qtractorTrackForm -- UI wrapper form. // Constructor. qtractorTrackForm::qtractorTrackForm ( QWidget *pParent ) : QDialog(pParent) { // Setup UI struct... m_ui.setupUi(this); // Window modality (let plugin/tool windows rave around). QDialog::setWindowModality(Qt::WindowModal); // No settings descriptor initially (the caller will set it). m_pTrack = nullptr; // Current MIDI output bus to be cached. m_pMidiBus = nullptr; // Set some dialog validators... m_ui.BankComboBox->setValidator(new QIntValidator(m_ui.BankComboBox)); m_ui.ProgComboBox->setValidator(new QIntValidator(m_ui.ProgComboBox)); // Bank select methods. const QIcon& icon = QIcon::fromTheme("itemProperty"); m_ui.BankSelMethodComboBox->clear(); m_ui.BankSelMethodComboBox->addItem(icon, tr("Normal")); m_ui.BankSelMethodComboBox->addItem(icon, tr("Bank MSB")); m_ui.BankSelMethodComboBox->addItem(icon, tr("Bank LSB")); m_ui.BankSelMethodComboBox->addItem(icon, tr("Patch")); // Custom colors. m_ui.ForegroundColorComboBox->setItemDelegate( new qtractorColorItemDelegate(m_ui.ForegroundColorComboBox)); m_ui.BackgroundColorComboBox->setItemDelegate( new qtractorColorItemDelegate(m_ui.BackgroundColorComboBox)); m_ui.ForegroundColorComboBox->clear(); m_ui.BackgroundColorComboBox->clear(); for (int i = 1; i < 28; ++i) { const QColor& rgbBack = qtractorTrack::trackColor(i); const QColor& rgbFore = rgbBack.darker(); m_ui.ForegroundColorComboBox->addItem(rgbFore.name()); m_ui.BackgroundColorComboBox->addItem(rgbBack.name()); } // To save and keep bus/channel patching consistency. m_pOldMidiBus = nullptr; m_iOldChannel = -1; m_sOldInstrumentName.clear(); m_iOldBankSelMethod = -1; m_iOldBank = -1; m_iOldProg = -1; // No last acceptable command yet. m_pLastCommand = nullptr; // MIDI ban/program observer. m_pMidiProgramObserver = nullptr; // Initialize dirty control state. m_iDirtySetup = 0; m_iDirtyCount = 0; m_iDirtyPatch = 0; // Add generic/standard track icons drop-down menu... m_pIconMenu = new QMenu(this); m_pIconMenu->addAction(QIcon::fromTheme("fileOpen"), tr("Custom &Icon..."), this, SLOT(trackIconClicked())); m_pIconMenu->addSeparator(); addIconMenuAction(tr("&Drums"), "trackIconDrums1"); addIconMenuAction(tr("Drum &Kit"), "trackIconDrums2"); addIconMenuAction(tr("&Bass"), "trackIconBass1"); addIconMenuAction(tr("A&coustic Bass"), "trackIconBass2"); addIconMenuAction(tr("&Guitar"), "trackIconGuitar1"); addIconMenuAction(tr("&Electric Guitar"), "trackIconGuitar2"); addIconMenuAction(tr("&Piano"), "trackIconPiano1"); addIconMenuAction(tr("&Acoustic Piano"), "trackIconPiano2"); addIconMenuAction(tr("&Microphone"), "trackIconMicrophone1"); addIconMenuAction(tr("Vi&ntage Microphone"), "trackIconMicrophone2"); addIconMenuAction(tr("&Speaker"), "trackIconSpeaker1"); addIconMenuAction(tr("&Trumpet"), "trackIconTrumpet1"); addIconMenuAction(tr("&Violin"), "trackIconViolin1"); m_pIconMenu->addSeparator(); m_pIconMenu->addAction(tr("(None)"), this, SLOT(trackIconAction())); m_ui.TrackIconToolButton->setMenu(m_pIconMenu); m_ui.TrackIconToolButton->setPopupMode(QToolButton::InstantPopup); // UI signal/slot connections... QObject::connect(m_ui.TrackNameTextEdit, SIGNAL(textChanged()), SLOT(changed())); QObject::connect(m_ui.TrackIconToolButton, SIGNAL(clicked()), SLOT(trackIconClicked())); QObject::connect(m_ui.AudioRadioButton, SIGNAL(clicked()), SLOT(trackTypeChanged())); QObject::connect(m_ui.MidiRadioButton, SIGNAL(clicked()), SLOT(trackTypeChanged())); QObject::connect(m_ui.InputBusNameComboBox, SIGNAL(activated(int)), SLOT(inputBusNameChanged(int))); QObject::connect(m_ui.OutputBusNameComboBox, SIGNAL(activated(int)), SLOT(outputBusNameChanged(int))); QObject::connect(m_ui.BusNameToolButton, SIGNAL(clicked()), SLOT(busNameClicked())); QObject::connect(m_ui.OmniCheckBox, SIGNAL(clicked()), SLOT(changed())); QObject::connect(m_ui.ChannelSpinBox, SIGNAL(valueChanged(int)), SLOT(channelChanged(int))); QObject::connect(m_ui.InstrumentComboBox, SIGNAL(activated(int)), SLOT(instrumentChanged(int))); QObject::connect(m_ui.BankSelMethodComboBox, SIGNAL(activated(int)), SLOT(bankSelMethodChanged(int))); QObject::connect(m_ui.DrumsCheckBox, SIGNAL(clicked()), SLOT(changed())); QObject::connect(m_ui.BankComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(bankChanged())); QObject::connect(m_ui.ProgComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(progChanged())); QObject::connect(m_ui.ForegroundColorComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(foregroundColorChanged(const QString&))); QObject::connect(m_ui.ForegroundColorToolButton, SIGNAL(clicked()), SLOT(selectForegroundColor())); QObject::connect(m_ui.AutoBackgroundColorCheckBox, SIGNAL(clicked()), SLOT(autoBackgroundColorChanged())); QObject::connect(m_ui.BackgroundColorComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(backgroundColorChanged(const QString&))); QObject::connect(m_ui.BackgroundColorToolButton, SIGNAL(clicked()), SLOT(selectBackgroundColor())); QObject::connect(m_ui.PluginListView, SIGNAL(currentRowChanged(int)), SLOT(stabilizeForm())); QObject::connect(m_ui.PluginListView, SIGNAL(contentsChanged()), SLOT(pluginListChanged())); QObject::connect(m_ui.AddPluginToolButton, SIGNAL(clicked()), SLOT(addPlugin())); QObject::connect(m_ui.RemovePluginToolButton, SIGNAL(clicked()), SLOT(removePlugin())); QObject::connect(m_ui.MoveUpPluginToolButton, SIGNAL(clicked()), SLOT(moveUpPlugin())); QObject::connect(m_ui.MoveDownPluginToolButton, SIGNAL(clicked()), SLOT(moveDownPlugin())); QObject::connect(m_ui.PluginListLatencyCheckBox, SIGNAL(clicked()), SLOT(changed())); QObject::connect(m_ui.PluginListLatencyPushButton, SIGNAL(clicked()), SLOT(updatePluginListLatency())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(rejected()), SLOT(reject())); } // Destructor. qtractorTrackForm::~qtractorTrackForm (void) { // Free up the MIDI bank/program observer... if (m_pMidiProgramObserver) delete m_pMidiProgramObserver; // Maybe the track icon menu perhaps... delete m_pIconMenu; } // Populate (setup) dialog controls from settings descriptors. void qtractorTrackForm::setTrack ( qtractorTrack *pTrack ) { // Set target track reference descriptor. m_pTrack = pTrack; if (m_pTrack == nullptr) return; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return; // Avoid dirty this all up. ++m_iDirtySetup; // Track properties cloning... m_props = m_pTrack->properties(); // Get reference of the last acceptable command... qtractorCommandList *pCommands = pSession->commands(); m_pLastCommand = pCommands->lastCommand(); // Set plugin list stuff... m_ui.PluginListView->setPluginList(m_pTrack->pluginList()); m_ui.PluginListLatencyCheckBox->setChecked(m_props.pluginListLatency); // Initialize dialog widgets... m_ui.TrackNameTextEdit->setPlainText(m_props.trackName); qtractorEngine *pEngine = nullptr; switch (m_props.trackType) { case qtractorTrack::Audio: pEngine = pSession->audioEngine(); m_ui.AudioRadioButton->setChecked(true); break; case qtractorTrack::Midi: pEngine = pSession->midiEngine(); m_ui.MidiRadioButton->setChecked(true); break; default: break; } updateTrackType(m_props.trackType); if (pEngine && pEngine->findInputBus(m_props.inputBusName)) m_ui.InputBusNameComboBox->setCurrentIndex( m_ui.InputBusNameComboBox->findText(m_props.inputBusName)); if (pEngine && pEngine->findOutputBus(m_props.outputBusName)) m_ui.OutputBusNameComboBox->setCurrentIndex( m_ui.OutputBusNameComboBox->findText(m_props.outputBusName)); // Track original output bus name... m_sOldOutputBusName = m_props.outputBusName; // Force MIDI output bus recaching. m_pMidiBus = midiBus(); // Make sure this will be remembered for backup. m_pOldMidiBus = m_pMidiBus; m_iOldChannel = m_props.midiChannel; m_sOldInstrumentName.clear(); if (m_pOldMidiBus) { const qtractorMidiBus::Patch& patch = m_pOldMidiBus->patch(m_iOldChannel); if (patch.isValid()) m_sOldInstrumentName = patch.instrumentName; } m_iOldBankSelMethod = m_props.midiBankSelMethod; m_iOldBank = m_props.midiBank; m_iOldProg = m_props.midiProg; // Already time for instrument caching... updateInstruments(); m_ui.OmniCheckBox->setChecked(m_props.midiOmni); m_ui.ChannelSpinBox->setValue(m_props.midiChannel + 1); updateChannel(m_ui.ChannelSpinBox->value(), m_props.midiBankSelMethod, m_props.midiBank, m_props.midiProg); m_ui.DrumsCheckBox->setChecked(m_props.midiDrums); // Update colors... updateColorItem(m_ui.ForegroundColorComboBox, m_props.foreground); updateColorItem(m_ui.BackgroundColorComboBox, m_props.background); trackIconChanged(); // Finally get the plugin-list latency estimate... updatePluginListLatency(); // Cannot change track type, if track is already chained in session.. m_ui.AudioRadioButton->setEnabled(m_props.trackType != qtractorTrack::Midi); m_ui.MidiRadioButton->setEnabled(m_props.trackType != qtractorTrack::Audio); // A bit of parental control... QObject::connect(pCommands, SIGNAL(updateNotifySignal(unsigned int)), SLOT(changed())); // Backup clean. m_iDirtyCount = 0; --m_iDirtySetup; // Done. stabilizeForm(); } // Retrieve the editing track, if the case arises. qtractorTrack *qtractorTrackForm::track (void) const { return m_pTrack; } // Retrieve the accepted track properties, if the case arises. const qtractorTrack::Properties& qtractorTrackForm::properties (void) const { return m_props; } // Selected track type determinator. qtractorTrack::TrackType qtractorTrackForm::trackType (void) const { qtractorTrack::TrackType trackType = qtractorTrack::None; if (m_ui.AudioRadioButton->isChecked()) trackType = qtractorTrack::Audio; else if (m_ui.MidiRadioButton->isChecked()) trackType = qtractorTrack::Midi; return trackType; } // Accept settings (OK button slot). void qtractorTrackForm::accept (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; // Save options... if (m_iDirtyCount > 0) { // Make sure one has unique track names... pSession->releaseTrackName(m_pTrack); const QString& sTrackName = pSession->uniqueTrackName( m_ui.TrackNameTextEdit->toPlainText().trimmed()); pSession->acquireTrackName(m_pTrack); // Make changes permanent... m_props.trackName = sTrackName; m_props.trackType = trackType(); m_props.inputBusName = m_ui.InputBusNameComboBox->currentText(); m_props.outputBusName = m_ui.OutputBusNameComboBox->currentText(); // Plugin latency compensation stuff... m_props.pluginListLatency = m_ui.PluginListLatencyCheckBox->isChecked(); // Special case for MIDI settings... m_props.midiOmni = m_ui.OmniCheckBox->isChecked(); m_props.midiChannel = (m_ui.ChannelSpinBox->value() - 1); m_props.midiBankSelMethod = m_ui.BankSelMethodComboBox->currentIndex(); m_props.midiBank = midiBank(); m_props.midiProg = midiProg(); m_props.midiDrums = m_ui.DrumsCheckBox->isChecked(); // View colors... m_props.foreground = colorItem(m_ui.ForegroundColorComboBox); m_props.background = colorItem(m_ui.BackgroundColorComboBox); m_props.background.setAlpha(192); // Restore original... if (m_props.outputBusName != m_sOldOutputBusName) updateOutputBusName(m_sOldOutputBusName); // Save default bus names... saveDefaultBusNames(m_props.trackType); // Reset dirty flag. m_iDirtyCount = 0; } pOptions->bAutoBackgroundColor = m_ui.AutoBackgroundColorCheckBox->isChecked(); // Just go with dialog acceptance. QDialog::accept(); } // Reject settings (Cancel button slot). void qtractorTrackForm::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; break; } } if (bReject) { // Bogus track? Unlikely, but... qtractorSession *pSession = nullptr; if (m_pTrack) pSession = m_pTrack->session(); if (pSession) { // Backout all commands made this far... (pSession->commands())->backout(m_pLastCommand); // Restore old output bus... updateOutputBusName(m_sOldOutputBusName); // Try to restore the previously saved patch... if (m_pOldMidiBus && m_iDirtyPatch > 0) { m_pOldMidiBus->setPatch(m_iOldChannel, m_sOldInstrumentName, m_iOldBankSelMethod, m_iOldBank, m_iOldProg, m_pTrack); } } // Flush any pending stuff befor too late... qtractorSubject::flushQueue(false); // Reset plugin list, before too late... m_ui.PluginListView->setPluginList(nullptr); // Just go away. QDialog::reject(); } } // Stabilize current form state. void qtractorTrackForm::stabilizeForm (void) { const bool bEnabled = (trackType() != qtractorTrack::None); const bool bValid = (m_iDirtyCount > 0) && bEnabled && !m_ui.TrackNameTextEdit->toPlainText().isEmpty(); m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(bValid); // Stabilize current auto-background color option.... const bool bAutoBackgroundColor = m_ui.AutoBackgroundColorCheckBox->isChecked(); m_ui.BackgroundColorTextLabel->setEnabled(!bAutoBackgroundColor); m_ui.BackgroundColorComboBox->setEnabled(!bAutoBackgroundColor); m_ui.BackgroundColorToolButton->setEnabled(!bAutoBackgroundColor); // Stabilize current plugin list state. m_ui.PluginListView->setEnabled(bEnabled); const int iItemCount = m_ui.PluginListView->count(); int iItem = -1; qtractorPlugin *pPlugin = nullptr; qtractorPluginListItem *pItem = static_cast ( m_ui.PluginListView->currentItem()); if (pItem) { iItem = m_ui.PluginListView->row(pItem); pPlugin = pItem->plugin(); } m_ui.AddPluginToolButton->setEnabled(bEnabled); m_ui.RemovePluginToolButton->setEnabled(pPlugin != nullptr); m_ui.MoveUpPluginToolButton->setEnabled(pItem && iItem > 0); m_ui.MoveDownPluginToolButton->setEnabled(pItem && iItem < iItemCount - 1); } // Retrieve currently assigned MIDI output-bus, if applicable. qtractorMidiBus *qtractorTrackForm::midiBus (void) const { if (m_pTrack == nullptr) return nullptr; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return nullptr; // If it ain't MIDI, bail out... if (trackType() != qtractorTrack::Midi) return nullptr; // MIDI engine... qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return nullptr; // MIDI bus... const QString& sOutputBusName = m_ui.OutputBusNameComboBox->currentText(); return static_cast ( pMidiEngine->findOutputBus(sOutputBusName)); } // Retrieve currently selected MIDI bank number. int qtractorTrackForm::midiBank (void) const { const QString& sBankText = m_ui.BankComboBox->currentText(); const int iBankIndex = m_ui.BankComboBox->findText(sBankText); if (iBankIndex >= 0 && m_banks.contains(iBankIndex) && m_ui.BankComboBox->itemText(iBankIndex) == sBankText) return m_banks[iBankIndex]; return sBankText.toInt(); } // Retrieve currently selected MIDI program number. int qtractorTrackForm::midiProg (void) const { const QString& sProgText = m_ui.ProgComboBox->currentText(); const int iProgIndex = m_ui.ProgComboBox->findText(sProgText); if (iProgIndex >= 0 && m_progs.contains(iProgIndex) && m_ui.ProgComboBox->itemText(iProgIndex) == sProgText) return m_progs[iProgIndex]; return sProgText.toInt(); } // Refresh instrument list. void qtractorTrackForm::updateInstruments (void) { if (m_pTrack == nullptr) return; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return; qtractorInstrumentList *pInstruments = pSession->instruments(); if (pInstruments == nullptr) return; // Avoid superfluous change notifications... ++m_iDirtySetup; m_ui.InstrumentComboBox->clear(); m_ui.InstrumentComboBox->addItem(tr("(No instrument)")); const QIcon& icon = QIcon::fromTheme("itemInstrument"); // Take care of MIDI plugin instrument names... updateInstrumentsAdd(icon, (m_pTrack->pluginList())->midiManager()); // And, maybe some from the MIDI output bus... if (m_pMidiBus && m_pMidiBus->pluginList_out()) updateInstrumentsAdd(icon, (m_pMidiBus->pluginList_out())->midiManager()); // Regular instrument names... qtractorInstrumentList::ConstIterator iter = pInstruments->constBegin(); const qtractorInstrumentList::ConstIterator& iter_end = pInstruments->constEnd(); for ( ; iter != iter_end; ++iter) m_ui.InstrumentComboBox->addItem(icon, iter.value().instrumentName()); // Done. --m_iDirtySetup; } // Refresh instrument list of given MIDI buffer manager. void qtractorTrackForm::updateInstrumentsAdd ( const QIcon& icon, qtractorMidiManager *pMidiManager ) { if (pMidiManager == nullptr) return; 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.InstrumentComboBox->addItem(icon, iter.value().instrumentName()); } // Update track type and buses. void qtractorTrackForm::updateTrackType ( qtractorTrack::TrackType trackType ) { if (m_pTrack == nullptr) return; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; // Avoid superfluos change notifications... ++m_iDirtySetup; // Renew the MIDI bank/program observer. if (m_pMidiProgramObserver) { delete m_pMidiProgramObserver; m_pMidiProgramObserver = nullptr; } // Make changes due to track type change. qtractorEngine *pEngine = nullptr; QIcon icon; switch (trackType) { case qtractorTrack::Audio: pEngine = pSession->audioEngine(); icon = QIcon::fromTheme("trackAudio"); m_ui.MidiGroupBox->hide(); m_ui.MidiGroupBox->setEnabled(false); m_ui.InputBusNameComboBox->setEnabled(true); m_ui.OutputBusNameComboBox->setEnabled(true); break; case qtractorTrack::Midi: pEngine = pSession->midiEngine(); icon = QIcon::fromTheme("trackMidi"); m_ui.MidiGroupBox->show(); m_ui.MidiGroupBox->setEnabled(true); m_ui.InputBusNameComboBox->setEnabled(true); m_ui.OutputBusNameComboBox->setEnabled(true); if (m_pTrack->pluginList() && (m_pTrack->pluginList())->midiProgramSubject()) { m_pMidiProgramObserver = new MidiProgramObserver(this, (m_pTrack->pluginList())->midiProgramSubject()); } break; case qtractorTrack::None: default: m_ui.MidiGroupBox->hide(); m_ui.MidiGroupBox->setEnabled(false); m_ui.InputBusNameComboBox->setEnabled(false); m_ui.OutputBusNameComboBox->setEnabled(false); m_ui.AutoBackgroundColorCheckBox->setChecked(pOptions->bAutoBackgroundColor); break; } m_ui.InputBusNameComboBox->clear(); m_ui.OutputBusNameComboBox->clear(); if (pEngine) { QListIterator iter(pEngine->buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Input) m_ui.InputBusNameComboBox->addItem(icon, pBus->busName()); if (pBus->busMode() & qtractorBus::Output) m_ui.OutputBusNameComboBox->addItem(icon, pBus->busName()); } } // Load default bus names... loadDefaultBusNames(trackType); // Shake it a little bit first, but // make it as tight as possible... resize(width() - 1, height() - 1); adjustSize(); // Done. --m_iDirtySetup; } // Refresh channel instrument banks list. void qtractorTrackForm::updateChannel ( int iChannel, int iBankSelMethod, int iBank, int iProg ) { // Regular channel offset if (--iChannel < 0) return; // MIDI bus... if (m_pMidiBus == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorTrackForm::updateChannel(%d, %d, %d, %d)", iChannel, iBankSelMethod, iBank, iProg); #endif // Avoid superfluos change notifications... ++m_iDirtySetup; // MIDI channel patch... const qtractorMidiBus::Patch& patch = m_pMidiBus->patch(iChannel); QString sInstrumentName = patch.instrumentName; if (sInstrumentName.isEmpty()) sInstrumentName = m_pMidiBus->instrumentName(); if (iBankSelMethod < 0) iBankSelMethod = patch.bankSelMethod; #if 0 if (iBank < 0) iBank = patch.bank; if (iProg < 0) iProg = patch.prog; #endif // Select instrument... int iInstrumentIndex = m_ui.InstrumentComboBox->findText(sInstrumentName); if (iInstrumentIndex < 0) { iInstrumentIndex = 0; sInstrumentName.clear(); } m_ui.InstrumentComboBox->setCurrentIndex(iInstrumentIndex); // Go and update the bank and program listings... updateBanks(sInstrumentName, iBankSelMethod, iBank, iProg); // Done. --m_iDirtySetup; } // Refresh instrument banks list. void qtractorTrackForm::updateBanks ( const QString& sInstrumentName, int iBankSelMethod, int iBank, int iProg ) { if (m_pTrack == nullptr) return; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return; // if (sInstrumentName.isEmpty()) // return; #ifdef CONFIG_DEBUG qDebug("qtractorTrackForm::updateBanks(\"%s\", %d, %d, %d)", sInstrumentName.toUtf8().constData(), iBankSelMethod, iBank, iProg); #endif qtractorInstrumentList *pInstruments = pSession->instruments(); if (pInstruments == nullptr) return; // Avoid superfluos change notifications... ++m_iDirtySetup; // Default (none) patch bank list... int iBankIndex = 0; const QIcon& icon = QIcon::fromTheme("itemPatches"); m_banks.clear(); m_ui.BankComboBox->clear(); m_ui.BankComboBox->addItem(icon, tr("(None)")); m_banks[iBankIndex++] = -1; // Care of MIDI plugin instrument banks... bool bMidiManager = updateBanksAdd(icon, (m_pTrack->pluginList())->midiManager(), sInstrumentName, iBank, iBankIndex); if (!bMidiManager && m_pMidiBus && m_pMidiBus->pluginList_out()) { bMidiManager = updateBanksAdd(icon, (m_pMidiBus->pluginList_out())->midiManager(), sInstrumentName, iBank, iBankIndex); } // Get instrument set alright... if (!bMidiManager && pInstruments->contains(sInstrumentName)) { // Instrument reference... const qtractorInstrument& instr = pInstruments->value(sInstrumentName); // Bank selection method... if (iBankSelMethod < 0) iBankSelMethod = instr.bankSelMethod(); // Refresh patch bank mapping... const qtractorInstrumentPatches& patches = instr.patches(); qtractorInstrumentPatches::ConstIterator it = patches.constBegin(); const qtractorInstrumentPatches::ConstIterator& it_end = patches.constEnd(); for (; it != it_end; ++it) { if (it.key() >= 0) { m_ui.BankComboBox->addItem(icon, it.value().name()); m_banks[iBankIndex++] = it.key(); } } #if 0 // In case bank address is generic... if (m_ui.BankComboBox->count() < 2) { const qtractorInstrumentData& patch = instr.patch(iBank); if (!patch.name().isEmpty()) { m_ui.BankComboBox->addItem(icon, patch.name()); m_banks[iBankIndex] = iBank; } } #endif // For proper bank selection... iBankIndex = -1; if (iBank >= 0) { const qtractorInstrumentData& patch = instr.patch(iBank); if (!patch.name().isEmpty()) iBankIndex = m_ui.BankComboBox->findText(patch.name()); } } else if (!bMidiManager) iBankIndex = -1; // Bank selection method... if (iBankSelMethod < 0) iBankSelMethod = 0; m_ui.BankSelMethodComboBox->setCurrentIndex(iBankSelMethod); #if 0 // If there's banks we must choose at least one... if (iBank < 0 && m_banks.count() > 1) { iBankIndex = 1; iBank = m_banks[iBankIndex]; } #endif // Do the proper bank selection... if (iBank < 0) { m_ui.BankComboBox->setCurrentIndex(0); } else if (iBankIndex < 0) { m_ui.BankComboBox->setEditText(QString::number(iBank)); } else { m_ui.BankComboBox->setCurrentIndex(iBankIndex); } // And update the bank and program listing... updatePrograms(sInstrumentName, iBank, iProg); // Done. --m_iDirtySetup; } // Refresh bank programs list. void qtractorTrackForm::updatePrograms ( const QString& sInstrumentName, int iBank, int iProg ) { if (m_pTrack == nullptr) return; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return; // if (sInstrumentName.isEmpty()) // return; #ifdef CONFIG_DEBUG qDebug("qtractorTrackForm::updatePrograms(\"%s\", %d, %d)", sInstrumentName.toUtf8().constData(), iBank, iProg); #endif qtractorInstrumentList *pInstruments = pSession->instruments(); if (pInstruments == nullptr) return; // Avoid superfluos change notifications... ++m_iDirtySetup; // Default (none) patch program list... // Refresh patch program mapping... int iProgIndex = 0; const QIcon& icon = QIcon::fromTheme("itemChannel"); m_progs.clear(); m_ui.ProgComboBox->clear(); m_ui.ProgComboBox->addItem(icon, tr("(None)")); m_progs[iProgIndex++] = -1; // Take care of MIDI plugin instrument programs... bool bMidiManager = updateProgramsAdd(icon, (m_pTrack->pluginList())->midiManager(), sInstrumentName, iBank, iProg, iProgIndex); if (!bMidiManager && m_pMidiBus && m_pMidiBus->pluginList_out()) { bMidiManager = updateProgramsAdd(icon, (m_pMidiBus->pluginList_out())->midiManager(), sInstrumentName, iBank, iProg, iProgIndex); } // Get instrument set alright... if (!bMidiManager && pInstruments->contains(sInstrumentName)) { // Instrument reference... const qtractorInstrument& instr = pInstruments->value(sInstrumentName); // Bank reference... const qtractorInstrumentData& bank = instr.patch(iBank); // Enumerate the explicit given program list... qtractorInstrumentData::ConstIterator it = bank.constBegin(); const qtractorInstrumentData::ConstIterator& it_end = bank.constEnd(); for (; it != it_end; ++it) { if (it.key() >= 0 && !it.value().isEmpty()) { m_ui.ProgComboBox->addItem(icon, it.value()); m_progs[iProgIndex++] = it.key(); } } // For proper program selection... iProgIndex = -1; if (iProg >= 0 && bank.contains(iProg)) iProgIndex = m_ui.ProgComboBox->findText(bank[iProg]); } else if (!bMidiManager) iProgIndex = -1; #if 0 // If there's programs we must choose at least one... if (iProg < 0 && m_progs.count() > 1) { iProgIndex = 1; iProg = m_progs[iProgIndex]; } #endif // In case program address is generic... if (m_ui.ProgComboBox->count() < 2) { // Just make a generic program list... for (int i = 0; i < 128; ++i) { m_ui.ProgComboBox->addItem(icon, QString("%1 - -").arg(i + 1)); m_progs[i + 1] = i; } if (iProg >= 0) iProgIndex = iProg + 1; } // Do the proper program selection... if (iProg < 0) { m_ui.ProgComboBox->setCurrentIndex(0); } else if (iProgIndex < 0) { m_ui.ProgComboBox->setEditText(QString::number(iProg)); } else { m_ui.ProgComboBox->setCurrentIndex(iProgIndex); } // Done. --m_iDirtySetup; } // Update instrument banks. bool qtractorTrackForm::updateBanksAdd ( const QIcon& icon, qtractorMidiManager *pMidiManager, const QString& sInstrumentName, int iBank, int& iBankIndex ) { if (pMidiManager == nullptr) return false; const qtractorInstrumentList& instruments = pMidiManager->instruments(); if (!instruments.contains(sInstrumentName)) return false; // Get instrument set alright... const qtractorInstrument& instr = instruments.value(sInstrumentName); // Refresh patch bank mapping... const qtractorInstrumentPatches& patches = instr.patches(); qtractorInstrumentPatches::ConstIterator iter = patches.constBegin(); const qtractorInstrumentPatches::ConstIterator& iter_end = patches.constEnd(); for ( ; iter != iter_end; ++iter) { if (iter.key() >= 0) { m_ui.BankComboBox->addItem(icon, iter.value().name()); m_banks[iBankIndex++] = iter.key(); } } // Reset given bank combobox index. iBankIndex = -1; if (iBank >= 0) { const qtractorInstrumentData& patch = instr.patch(iBank); if (!patch.name().isEmpty()) iBankIndex = m_ui.BankComboBox->findText(patch.name()); } // Mark that we've have something. return true; } // Update instrument programs. bool qtractorTrackForm::updateProgramsAdd ( const QIcon& icon, qtractorMidiManager *pMidiManager, const QString& sInstrumentName, int iBank, int iProg, int& iProgIndex ) { if (pMidiManager == nullptr) return false; const qtractorInstrumentList& instruments = pMidiManager->instruments(); if (!instruments.contains(sInstrumentName)) return false; // Common program label... const QString sProg("%1 - %2"); // Instrument reference... const qtractorInstrument& instr = instruments.value(sInstrumentName); // Bank reference... const qtractorInstrumentData& bank = instr.patch(iBank); // Enumerate the explicit given program list... qtractorInstrumentData::ConstIterator iter = bank.constBegin(); const qtractorInstrumentData::ConstIterator& iter_end = bank.constEnd(); for ( ; iter != iter_end; ++iter) { if (iter.key() >= 0 && !iter.value().isEmpty()) { m_ui.ProgComboBox->addItem(icon, sProg.arg(iter.key()).arg(iter.value())); m_progs[iProgIndex++] = iter.key(); } } // For proper program selection, thru label... iProgIndex = -1; if (iProg >= 0 && bank.contains(iProg)) { iProgIndex = m_ui.ProgComboBox->findText( sProg.arg(iProg).arg(bank[iProg])); } // Mark that we've have something. return true; } // Update and set a color item. void qtractorTrackForm::updateColorItem ( QComboBox *pComboBox, const QColor& color ) { // Have some immediate feedback... updateColorText(pComboBox, color); // Check if already exists... const int iItem = pComboBox->findText(color.name()); if (iItem >= 0) { pComboBox->setCurrentIndex(iItem); return; } // Nope, we'll add it custom... pComboBox->addItem(color.name()); pComboBox->setCurrentIndex(pComboBox->count() - 1); } // Update color item visual text. void qtractorTrackForm::updateColorText ( QComboBox *pComboBox, const QColor& color ) { QPalette pal; // pal.setColor(QPalette::Window, color); pal.setColor(QPalette::Base, color); pal.setColor(QPalette::Text, color.value() < 0x7f ? color.lighter(200) : color.darker(300)); // pComboBox->lineEdit()->setPalette(pal); pComboBox->setPalette(pal); } // Retreieve currently selected color item. QColor qtractorTrackForm::colorItem ( QComboBox *pComboBox ) { return QColor(pComboBox->currentText()); } // Make more changes due. void qtractorTrackForm::foregroundColorChanged ( const QString& sText ) { if (m_iDirtySetup > 0) return; m_props.foreground = QColor(sText); updateColorText(m_ui.ForegroundColorComboBox, m_props.foreground); autoBackgroundColorChanged(); } void qtractorTrackForm::backgroundColorChanged ( const QString& sText ) { if (m_iDirtySetup > 0) return; m_props.background = QColor(sText); updateColorText(m_ui.BackgroundColorComboBox, m_props.background); trackIconChanged(); } void qtractorTrackForm::autoBackgroundColorChanged (void) { if (m_ui.AutoBackgroundColorCheckBox->isChecked()) { const QColor& background = m_props.foreground.lighter(200); updateColorText(m_ui.BackgroundColorComboBox, background); m_ui.BackgroundColorComboBox->setCurrentText(background.name()); } trackIconChanged(); } void qtractorTrackForm::pluginListChanged (void) { updateInstruments(); updatePluginListLatency(); changed(); } void qtractorTrackForm::changed (void) { if (m_iDirtySetup > 0) return; ++m_iDirtyCount; stabilizeForm(); } // Make changes to track icon. void qtractorTrackForm::trackIconAction (void) { QAction *pAction = qobject_cast (sender()); if (pAction) { m_props.trackIcon = pAction->data().toString(); trackIconChanged(); } } void qtractorTrackForm::trackIconClicked (void) { QString sFilename = m_props.trackIcon; if (!sFilename.isEmpty() && !QIcon::fromTheme(sFilename).isNull()) sFilename.clear(); const QString& sTitle = tr("Track Icon"); QStringList filters; filters.append(tr("Image files (%1)").arg("*.png *.xpm *.jpg *.jpeg")); 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 open... sFilename = 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::AnyFile); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sFilename = fileDialog.selectedFiles().first(); #endif if (!sFilename.isEmpty()) { m_props.trackIcon = sFilename; trackIconChanged(); } } // Make changes due to track type. void qtractorTrackForm::trackTypeChanged (void) { if (m_iDirtySetup > 0) return; if (m_pTrack == nullptr) return; // Restore previous output bus, if any... updateOutputBusName(m_sOldOutputBusName); if (m_pOldMidiBus) { // Restore previously current/saved patch... m_pOldMidiBus->setPatch(m_iOldChannel, m_sOldInstrumentName, m_iOldBankSelMethod, m_iOldBank, m_iOldProg, m_pTrack); // Reset restorable patch reference... m_pOldMidiBus = nullptr; m_iOldChannel = -1; m_sOldInstrumentName.clear(); m_iOldBankSelMethod = -1; m_iOldBank = -1; m_iOldProg = -1; } const qtractorTrack::TrackType trackType = qtractorTrackForm::trackType(); m_pTrack->setTrackType(trackType); updateTrackType(trackType); // inputBusNameChanged(m_ui.InputBusNameComboBox->currentIndex()); outputBusNameChanged(m_ui.OutputBusNameComboBox->currentIndex()); // FIXME: Plugin-list initial number of audio output channels... qtractorPluginList *pPluginList = m_pTrack->pluginList(); if (pPluginList && m_pTrack->session()) { unsigned short iChannels = pPluginList->channels(); // Somehow switching track-types might zero it... if (iChannels == 0) { qtractorAudioEngine *pAudioEngine = (m_pTrack->session())->audioEngine(); if (pAudioEngine) { QListIterator iter(pAudioEngine->buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Output) { qtractorAudioBus *pAudioBus = static_cast (pBus); if (pAudioBus) iChannels = pAudioBus->channels(); break; } } } } // Have we some?... if (iChannels > 0) { const unsigned int iFlags = pPluginList->flags(); pPluginList->setChannels(iChannels, iFlags); } } } // Make changes due to input-bus name. void qtractorTrackForm::inputBusNameChanged ( int /* iInputBusName */ ) { changed(); } // Make changes due to output-bus name. void qtractorTrackForm::outputBusNameChanged ( int iOutputBusName ) { if (m_iDirtySetup > 0) return; if (m_pTrack == nullptr) return; // (Re)initialize output bus properly... const QString& sBusName = m_ui.OutputBusNameComboBox->itemText(iOutputBusName); const QString& sOutputBusName = m_pTrack->outputBusName(); if (sOutputBusName != sBusName) { updateOutputBusName(sBusName); // Recache the applicable MIDI output bus ... if (trackType() == qtractorTrack::Midi) { m_pMidiBus = midiBus(); updateInstruments(); } } channelChanged(m_ui.ChannelSpinBox->value()); } // Manage buses. void qtractorTrackForm::busNameClicked (void) { if (m_iDirtySetup > 0) return; if (m_pTrack == nullptr) return; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return; // Depending on track type... qtractorEngine *pEngine = nullptr; switch (trackType()) { case qtractorTrack::Audio: pEngine = pSession->audioEngine(); break; case qtractorTrack::Midi: pEngine = pSession->midiEngine(); break; case qtractorTrack::None: default: break; } // Call here the bus management form. qtractorBusForm busForm(this); // Pre-select bus... const QString& sBusName = m_ui.OutputBusNameComboBox->currentText(); if (pEngine && !sBusName.isEmpty()) busForm.setBus(pEngine->findBus(sBusName)); // Go for it... busForm.exec(); // Check if any buses have changed... if (pEngine && busForm.isDirty()) { // Try to preserve current selected names... const QString sInputBusName = m_ui.InputBusNameComboBox->currentText(); const QString sOutputBusName = m_ui.OutputBusNameComboBox->currentText(); // Update the comboboxes... trackTypeChanged(); // Restore old current selected ones... if (pEngine->findInputBus(sInputBusName)) m_ui.InputBusNameComboBox->setCurrentIndex( m_ui.InputBusNameComboBox->findText(sInputBusName)); if (pEngine->findOutputBus(sOutputBusName)) m_ui.OutputBusNameComboBox->setCurrentIndex( m_ui.OutputBusNameComboBox->findText(sOutputBusName)); } } // Make changes due to MIDI channel. void qtractorTrackForm::channelChanged ( int iChannel ) { if (m_iDirtySetup > 0) return; // First update channel instrument mapping... updateChannel(iChannel, -1, // m_ui.BankSelMethodComboBox->currentItem(), -1, // midiBank(), -1);// midiProgram()); progChanged(); } // Make changes due to MIDI instrument. void qtractorTrackForm::instrumentChanged ( int iInstrument ) { if (m_iDirtySetup > 0) return; updateBanks(m_ui.InstrumentComboBox->itemText(iInstrument), -1, // m_ui.BankSelMethodComboBox->currentItem(), midiBank(), midiProg()); progChanged(); } // Make changes due to MIDI bank selection method. void qtractorTrackForm::bankSelMethodChanged ( int /* iBankSelMethod */ ) { if (m_iDirtySetup > 0) return; progChanged(); } // Make changes due to MIDI bank. void qtractorTrackForm::bankChanged (void) { if (m_iDirtySetup > 0) return; QString sInstrumentName; if (m_ui.InstrumentComboBox->currentIndex() > 0) sInstrumentName = m_ui.InstrumentComboBox->currentText(); updatePrograms(sInstrumentName, midiBank(), midiProg()); progChanged(); } // Make changes due to MIDI program. void qtractorTrackForm::progChanged (void) { if (m_iDirtySetup > 0) return; // Of course, only applicable on MIDI tracks... if (m_pMidiBus) { // Patch parameters... const unsigned short iChannel = m_ui.ChannelSpinBox->value() - 1; QString sInstrumentName; if (m_ui.InstrumentComboBox->currentIndex() > 0) sInstrumentName = m_ui.InstrumentComboBox->currentText(); const int iBankSelMethod = m_ui.BankSelMethodComboBox->currentIndex(); const int iBank = midiBank(); const int iProg = midiProg(); // Keep old bus/channel patching consistency. if (m_pMidiBus != m_pOldMidiBus || iChannel != m_iOldChannel) { // Restore previously saved patch... if (m_pOldMidiBus) { m_pOldMidiBus->setPatch(m_iOldChannel, m_sOldInstrumentName, m_iOldBankSelMethod, m_iOldBank, m_iOldProg, m_pTrack); } // Save current channel patch... const qtractorMidiBus::Patch& patch = m_pMidiBus->patch(iChannel); if (patch.isValid()) { m_pOldMidiBus = m_pMidiBus; m_iOldChannel = iChannel; m_sOldInstrumentName = patch.instrumentName; m_iOldBankSelMethod = patch.bankSelMethod; m_iOldBank = patch.bank; m_iOldProg = patch.prog; } } // Patch it directly... m_pMidiBus->setPatch(iChannel, sInstrumentName, iBankSelMethod, iBank, iProg, m_pTrack); // Whether in drum-mode auto-magically... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { qtractorInstrumentList *pInstruments = pSession->instruments(); if (pInstruments) { const qtractorInstrument& instr = pInstruments->value(sInstrumentName); m_ui.DrumsCheckBox->setChecked( iChannel == 9 || instr.isDrum(iBank, iProg)); } } // Make it dirty. ++m_iDirtyPatch; } // Flag that it changed anyhow! changed(); } // Select custom track foreground color. void qtractorTrackForm::selectForegroundColor (void) { const QString& sTitle = tr("Foreground Color"); QWidget *pParentWidget = nullptr; QColorDialog::ColorDialogOptions options; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bDontUseNativeDialogs) { options |= QColorDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } const QColor& color = QColorDialog::getColor( colorItem(m_ui.ForegroundColorComboBox), pParentWidget, sTitle, options); if (color.isValid()) { m_props.foreground = color; updateColorItem(m_ui.ForegroundColorComboBox, color); autoBackgroundColorChanged(); } } // Select custom track background color. void qtractorTrackForm::selectBackgroundColor (void) { const QString& sTitle = tr("Background Color"); QWidget *pParentWidget = nullptr; QColorDialog::ColorDialogOptions options; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bDontUseNativeDialogs) { options |= QColorDialog::DontUseNativeDialog; pParentWidget = QWidget::window(); } const QColor& color = QColorDialog::getColor( colorItem(m_ui.BackgroundColorComboBox), pParentWidget, sTitle, options); if (color.isValid()) { m_props.background = color; updateColorItem(m_ui.BackgroundColorComboBox, color); trackIconChanged(); } } // Plugin list slots. void qtractorTrackForm::addPlugin (void) { m_ui.PluginListView->addPlugin(); } void qtractorTrackForm::removePlugin (void) { m_ui.PluginListView->removePlugin(); } void qtractorTrackForm::moveUpPlugin (void) { m_ui.PluginListView->moveUpPlugin(); } void qtractorTrackForm::moveDownPlugin (void) { m_ui.PluginListView->moveDownPlugin(); } // MIDI bank/program settlers. void qtractorTrackForm::setMidiProgram ( int iBank, int iProg ) { QString sInstrumentName; if (m_ui.InstrumentComboBox->currentIndex() > 0) sInstrumentName = m_ui.InstrumentComboBox->currentText(); updateBanks( sInstrumentName, -1, // m_ui.BankSelMethodComboBox->currentIndex() iBank, iProg ); changed(); } // Update/reset output bus name... void qtractorTrackForm::updateOutputBusName ( const QString& sBusName ) { if (sBusName.isEmpty()) return; if (m_pTrack == nullptr) return; if (sBusName == m_pTrack->outputBusName()) return; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return; pSession->lock(); m_pTrack->setOutputBusName(sBusName); m_pTrack->open(); // re-open... pSession->unlock(); } // Update current plugins latency... void qtractorTrackForm::updatePluginListLatency (void) { if (m_pTrack == nullptr) return; qtractorSession *pSession = m_pTrack->session(); if (pSession == nullptr) return; qtractorPluginList *pPluginList = m_ui.PluginListView->pluginList(); if (pPluginList == nullptr) return; qtractorSubject::flushQueue(true); const unsigned long iLatency = pPluginList->currentLatency(); if (iLatency > 0) { const float fLatencyMs = 1000.0f * float(iLatency) / float(pSession->sampleRate()); m_ui.PluginListLatencyPushButton->setText( tr("%1 ms (%2 frames)") .arg(QString::number(fLatencyMs, 'f', 1)) .arg(iLatency)); } else { m_ui.PluginListLatencyPushButton->setText(tr("(no latency)")); } } //---------------------------------------------------------------------------- // Default bus names... QString qtractorTrackForm::g_sAudioInputBusName; QString qtractorTrackForm::g_sAudioOutputBusName; QString qtractorTrackForm::g_sMidiInputBusName; QString qtractorTrackForm::g_sMidiOutputBusName; // Load default bus names... void qtractorTrackForm::loadDefaultBusNames ( qtractorTrack::TrackType trackType ) { QString sInputBusName; QString sOutputBusName; switch (trackType) { case qtractorTrack::Audio: sInputBusName = g_sAudioInputBusName;; sOutputBusName = g_sAudioOutputBusName; break; case qtractorTrack::Midi: sInputBusName = g_sMidiInputBusName; sOutputBusName = g_sMidiOutputBusName; break; default: break; } const int iInputBusIndex = m_ui.InputBusNameComboBox->findText(sInputBusName); m_ui.InputBusNameComboBox->setCurrentIndex( iInputBusIndex < 0 ? 0 : iInputBusIndex); const int iOutputBusIndex = m_ui.OutputBusNameComboBox->findText(sOutputBusName); m_ui.OutputBusNameComboBox->setCurrentIndex( iOutputBusIndex < 0 ? 0 : iOutputBusIndex); } // Save default bus names... void qtractorTrackForm::saveDefaultBusNames ( qtractorTrack::TrackType trackType ) const { const int iInputBusIndex = m_ui.InputBusNameComboBox->currentIndex(); const int iOutputBusIndex = m_ui.OutputBusNameComboBox->currentIndex(); switch (trackType) { case qtractorTrack::Audio: if (iInputBusIndex > 0) g_sAudioInputBusName = m_ui.InputBusNameComboBox->currentText(); if (iOutputBusIndex > 0) g_sAudioOutputBusName = m_ui.OutputBusNameComboBox->currentText(); break; case qtractorTrack::Midi: if (iInputBusIndex > 0) g_sMidiInputBusName = m_ui.InputBusNameComboBox->currentText(); if (iOutputBusIndex > 0) g_sMidiOutputBusName = m_ui.OutputBusNameComboBox->currentText(); break; default: break; } } // Track icon generic/standard action setup. void qtractorTrackForm::addIconMenuAction ( const QString& sIconText, const QString& sTrackIcon ) { QAction *pAction = m_pIconMenu->addAction( QIcon::fromTheme(sTrackIcon), sIconText, this, SLOT(trackIconAction())); pAction->setData(sTrackIcon); } // Track icon refresh. void qtractorTrackForm::trackIconChanged (void) { QPalette pal(m_ui.TrackIconToolButton->palette()); pal.setColor(QPalette::ButtonText, m_props.background); pal.setColor(QPalette::Button, m_props.foreground.lighter()); m_ui.TrackIconToolButton->setPalette(pal); QIcon icon = QIcon::fromTheme(m_props.trackIcon); if (icon.isNull()) icon = QIcon(m_props.trackIcon); if (!icon.isNull()) { const QSize& size = m_ui.TrackIconToolButton->size() - QSize(8, 8); m_ui.TrackIconToolButton->setIconSize(size); } m_ui.TrackIconToolButton->setIcon(icon); changed(); } // end of qtractorTrackForm.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMessages.cpp0000644000000000000000000000012715101070305017261 xustar0029 mtime=1761898693.07626762 29 atime=1761898693.07626762 29 ctime=1761898693.07626762 qtractor-1.5.9/src/qtractorMessages.cpp0000644000175000001440000002422015101070305017245 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.9/src/PaxHeaders/qtractorClipCommand.cpp0000644000000000000000000000013215101070305017674 xustar0030 mtime=1761898693.067267591 30 atime=1761898693.066267588 30 ctime=1761898693.067267591 qtractor-1.5.9/src/qtractorClipCommand.cpp0000644000175000001440000011574615101070305017702 0ustar00rncbcusers// qtractorClipCommand.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 "qtractorClipCommand.h" #include "qtractorTrackCommand.h" #include "qtractorMainForm.h" #include "qtractorSession.h" #include "qtractorAudioClip.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include "qtractorMidiClip.h" #include "qtractorTracks.h" #include "qtractorFiles.h" #include "qtractorMidiEditCommand.h" #include "qtractorTimeScaleCommand.h" #include "qtractorSessionCommand.h" #include "qtractorCurveCommand.h" //---------------------------------------------------------------------- // class qtractorClipCommand - declaration. // // Constructor. qtractorClipCommand::qtractorClipCommand ( const QString& sName ) : qtractorCommand(sName) { } // Destructor. qtractorClipCommand::~qtractorClipCommand (void) { QListIterator iter(m_items); while (iter.hasNext()) { Item *pItem = iter.next(); if (pItem->takeInfo) pItem->takeInfo->releaseRef(); if (pItem->editCommand) delete pItem->editCommand; if (pItem->autoDelete) delete pItem->clip; } qDeleteAll(m_items); m_items.clear(); qDeleteAll(m_trackCommands); m_trackCommands.clear(); m_clips.clear(); } // Primitive command methods. void qtractorClipCommand::addClip ( qtractorClip *pClip, qtractorTrack *pTrack ) { pClip->setTrack(pTrack); pClip->open(); m_items.append(new Item(AddClip, pClip, pTrack)); // setClearSelectReset(true); } void qtractorClipCommand::removeClip ( qtractorClip *pClip ) { m_items.append(new Item(RemoveClip, pClip, pClip->track())); // setClearSelectReset(true); } // Edit command methods. void qtractorClipCommand::fileClip ( qtractorClip *pClip, const QString& sFilename, unsigned short iTrackChannel ) { Item *pItem = new Item(FileClip, pClip, pClip->track()); pItem->filename = sFilename; pItem->trackChannel = iTrackChannel; m_items.append(pItem); if (isAudioClip(pClip)) reopenClip(pClip, true); } void qtractorClipCommand::renameClip ( qtractorClip *pClip, const QString& sClipName ) { Item *pItem = new Item(RenameClip, pClip, pClip->track()); pItem->clipName = sClipName; m_items.append(pItem); } void qtractorClipCommand::moveClip ( qtractorClip *pClip, qtractorTrack *pTrack, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength ) { Item *pItem = new Item(MoveClip, pClip, pTrack); // HACK: convert/override MIDI clip-offset times // across potential tempo/time-sig changes... qtractorSession *pSession = pTrack->session(); if (pSession && pTrack->trackType() == qtractorTrack::Midi) { const unsigned long iOldClipStart = pClip->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); } pItem->clipStart = iClipStart; pItem->clipOffset = iClipOffset; pItem->clipLength = iClipLength; if (iClipOffset == pClip->clipOffset()) pItem->fadeInLength = pClip->fadeInLength(); if (iClipOffset + iClipLength == pClip->clipOffset() + pClip->clipLength()) pItem->fadeOutLength = pClip->fadeOutLength(); m_items.append(pItem); // ATTN: when moving across tracks: // reset all take(record) descriptors... if (pTrack != pClip->track()) { qtractorClip::TakeInfo *pTakeInfo = pClip->takeInfo(); if (pTakeInfo) { pClip = pTakeInfo->clipPart(qtractorClip::TakeInfo::ClipHead); if (pClip) takeInfoClip(pClip, nullptr); pClip = pTakeInfo->clipPart(qtractorClip::TakeInfo::ClipTake); if (pClip) takeInfoClip(pClip, nullptr); } } // setClearSelect(true); } void qtractorClipCommand::resizeClip ( qtractorClip *pClip, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength, float fTimeStretch, float fPitchShift ) { Item *pItem = new Item(ResizeClip, pClip, pClip->track()); // FIXME: convert/override MIDI clip-offset times // across potential tempo/time-sig changes... qtractorTrack *pTrack = pClip->track(); qtractorSession *pSession = pTrack->session(); if (pSession && pTrack->trackType() == qtractorTrack::Midi) { const unsigned long iOldClipStart = pClip->clipStart(); const unsigned long iClipOffsetTime = pSession->tickFromFrameRange( iOldClipStart, iOldClipStart + iClipOffset, true); const unsigned long iClipStartTime = pSession->tickFromFrame(iClipStart); iClipOffset = pSession->frameFromTickRange( iClipStartTime, iClipStartTime + iClipOffsetTime, true); } pItem->clipStart = iClipStart; pItem->clipOffset = iClipOffset; pItem->clipLength = iClipLength; long iFadeInLength = long(pClip->fadeInLength()); if (iFadeInLength > 0) { if (fTimeStretch > 0.0f) iFadeInLength = long(fTimeStretch * float(iFadeInLength)); else iFadeInLength += long(pClip->clipStart()) - long(iClipStart); if (iFadeInLength > 0) pItem->fadeInLength = iFadeInLength; } long iFadeOutLength = long(pClip->fadeOutLength()); if (iFadeOutLength > 0) { if (fTimeStretch > 0.0f) iFadeOutLength = long(fTimeStretch * float(iFadeOutLength)); else iFadeOutLength += long(iClipStart + iClipLength) - long(pClip->clipStart() + pClip->clipLength()); if (iFadeOutLength > 0) pItem->fadeOutLength = iFadeOutLength; } if (fTimeStretch > 0.0f) { pItem->timeStretch = fTimeStretch; switch ((pClip->track())->trackType()) { case qtractorTrack::Audio: { qtractorAudioClip *pAudioClip = static_cast (pClip); const float fRatio = fTimeStretch / pAudioClip->timeStretch(); pItem->clipOffset = (unsigned long) (fRatio * float(iClipOffset)); break; } case qtractorTrack::Midi: { qtractorMidiClip *pMidiClip = static_cast (pClip); pItem->editCommand = createMidiEditCommand(pMidiClip, fTimeStretch); break; } default: break; } } if (fPitchShift > 0.0f) pItem->pitchShift = fPitchShift; m_items.append(pItem); if (isAudioClip(pClip) || pItem->editCommand == nullptr) reopenClip(pClip, fTimeStretch > 0.0f); // else // setClearSelect(true); } void qtractorClipCommand::gainClip ( qtractorClip *pClip, float fGain ) { Item *pItem = new Item(GainClip, pClip, pClip->track()); pItem->clipGain = fGain; m_items.append(pItem); if (isAudioClip(pClip)) reopenClip(pClip); } void qtractorClipCommand::panningClip ( qtractorClip *pClip, float fPanning ) { Item *pItem = new Item(PanningClip, pClip, pClip->track()); pItem->clipPanning = fPanning; m_items.append(pItem); if (isAudioClip(pClip)) reopenClip(pClip); } void qtractorClipCommand::muteClip ( qtractorClip *pClip, bool bMute ) { Item *pItem = new Item(MuteClip, pClip, pClip->track()); pItem->clipMute = bMute; m_items.append(pItem); } void qtractorClipCommand::fadeInClip ( qtractorClip *pClip, unsigned long iFadeInLength, qtractorClip::FadeType fadeInType ) { Item *pItem = new Item(FadeInClip, pClip, pClip->track()); pItem->fadeInLength = iFadeInLength; pItem->fadeInType = fadeInType; m_items.append(pItem); } void qtractorClipCommand::fadeOutClip ( qtractorClip *pClip, unsigned long iFadeOutLength, qtractorClip::FadeType fadeOutType ) { Item *pItem = new Item(FadeOutClip, pClip, pClip->track()); pItem->fadeOutLength = iFadeOutLength; pItem->fadeOutType = fadeOutType; m_items.append(pItem); } void qtractorClipCommand::timeStretchClip ( qtractorClip *pClip, float fTimeStretch ) { Item *pItem = new Item(TimeStretchClip, pClip, pClip->track()); pItem->timeStretch = fTimeStretch; m_items.append(pItem); if (isAudioClip(pClip)) reopenClip(pClip, true); } void qtractorClipCommand::pitchShiftClip ( qtractorClip *pClip, float fPitchShift ) { Item *pItem = new Item(PitchShiftClip, pClip, pClip->track()); pItem->pitchShift = fPitchShift; m_items.append(pItem); if (isAudioClip(pClip)) reopenClip(pClip); } void qtractorClipCommand::takeInfoClip ( qtractorClip *pClip, qtractorClip::TakeInfo *pTakeInfo ) { Item *pItem = new Item(TakeInfoClip, pClip, pClip->track()); pItem->takeInfo = pTakeInfo; m_items.append(pItem); if (pTakeInfo) pTakeInfo->addRef(); } void qtractorClipCommand::resetClip ( qtractorClip *pClip ) { Item *pItem = new Item(ResetClip, pClip, pClip->track()); pItem->clipOffset = pClip->clipOffset(); pItem->clipLength = pClip->clipLength(); m_items.append(pItem); // setClearSelect(true); } void qtractorClipCommand::stretcherFlagsClip ( qtractorClip *pClip, unsigned int iStretcherFlags ) { Item *pItem = new Item(StretcherFlagsClip, pClip, pClip->track()); pItem->stretcherFlags = iStretcherFlags; m_items.append(pItem); if (isAudioClip(pClip)) reopenClip(pClip, true); } // Conveniency helper: whether a clip is certain type. bool qtractorClipCommand::isAudioClip ( qtractorClip *pClip ) const { if (pClip->track()) return ((pClip->track())->trackType() == qtractorTrack::Audio); else return false; } // When clips need to get reopenned. void qtractorClipCommand::reopenClip ( qtractorClip *pClip, bool bClose ) { QHash::ConstIterator iter = m_clips.constFind(pClip); if (iter == m_clips.constEnd()) m_clips.insert(pClip, bClose); // setClearSelect(true); } // Special clip record nethods. bool qtractorClipCommand::addClipRecord ( qtractorTrack *pTrack, unsigned long iFrameTime ) { qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; qtractorClip *pClip = pTrack->clipRecord(); if (pClip == nullptr) return false; // Recording clip extents... const unsigned long iClipEnd = pTrack->clipRecordEnd(iFrameTime); const unsigned long iClipStart = pClip->clipStart(); if (iClipStart >= iClipEnd) return false; const qtractorTrack::TrackType trackType = pTrack->trackType(); // HACK: Exclusive MIDI clip recording/overdub... if (pTrack->isClipRecordEx()) { if (trackType == qtractorTrack::Midi) { qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip) { // HACK: Make all final adjustments... qtractorMidiEditCommand(pMidiClip, name()).adjust(); // Have a new filename revision... const QString& sFilename = pMidiClip->createFilePathRevision(true); // Save/replace the overdubbed clip... pMidiClip->saveCopyFile(sFilename, false); // Just change filename/track-channel... fileClip(pMidiClip, sFilename, pMidiClip->trackChannel()); // Post-commit dirty changes... pSession->files()->addClipItem(qtractorFileList::Midi, sFilename, true); } } // Can get rid of the recorded clip. pTrack->setClipRecord(nullptr); return true; } // Recorded clip length fixup... pClip->setClipLength(iClipEnd - iClipStart); // Time to close the clip... pClip->close(); // Actual clip length might have changed on close. const unsigned long iClipLength = pClip->clipLength(); if (iClipLength < 1) return false; // Recorded clip offset... unsigned long iClipOffset = pClip->clipOffset(); // Audio clips may need some record latency compensation... if (trackType == qtractorTrack::Audio) { qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine) iClipOffset += pAudioEngine->transportLatency(); } // Check whether in loop-recording/takes mode... const unsigned long iLoopStart = pSession->loopStart(); const unsigned long iLoopEnd = pSession->loopEnd(); if (iLoopStart < iLoopEnd && pSession->loopRecordingMode() > 0 && iClipStart < iLoopEnd && iClipEnd > iLoopEnd) { // HACK: Take care of punch-in/out... unsigned long iTakeStart = iLoopStart; unsigned long iTakeEnd = iLoopEnd; unsigned long iTakeGap = 0; if (pSession->isPunching()) { const unsigned long iPunchIn = pSession->punchIn(); const unsigned long iPunchOut = pSession->punchOut(); if (iTakeStart < iPunchIn && iPunchIn < iTakeEnd) { iTakeGap += (iPunchIn - iTakeStart); iTakeStart = iPunchIn; } if (iTakeStart < iPunchOut && iPunchOut < iTakeEnd) { iTakeGap += (iTakeEnd - iPunchOut); iTakeEnd = iPunchOut; } } qtractorClip::TakeInfo *pTakeInfo = new qtractorClip::TakeInfo( iClipStart, iClipOffset, iClipLength, iTakeStart, iTakeEnd, iTakeGap); const int iTake = (pSession->loopRecordingMode() == 1 ? 0 : -1); pTakeInfo->setCurrentTake(pTakeInfo->select(this, pTrack, iTake)); } else addClipRecordTake(pTrack, pClip, iClipStart, iClipOffset, iClipLength); // Make sure this brand new filename is not reused! // (see qtractroSession::createFilePath()...) pSession->releaseFilePath(pClip->filename()); // Can get rid of the recorded clip. pTrack->setClipRecord(nullptr); // Done. return true; } bool qtractorClipCommand::addClipRecordTake ( qtractorTrack *pTrack, qtractorClip *pClip, unsigned long iClipStart, unsigned long iClipOffset, unsigned long iClipLength, qtractorClip::TakePart *pTakePart ) { #ifdef CONFIG_DEBUG qDebug("qtractorClipCommand[%p]::addClipRecordTake(%p, %p, %lu, %lu, %lu, %p)", this, pTrack, pClip, iClipStart, iClipOffset, iClipLength, pTakePart); #endif // Now, its imperative to make a proper copy of those clips... // -- formerly a cloning clip factory method. // Reference for immediate file addition... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); switch (pTrack->trackType()) { case qtractorTrack::Audio: { qtractorAudioClip *pAudioClip = static_cast (pClip); if (pAudioClip) { pAudioClip = new qtractorAudioClip(*pAudioClip); pAudioClip->setClipStart(iClipStart); pAudioClip->setClipOffset(iClipOffset); pAudioClip->setClipLength(iClipLength); addClip(pAudioClip, pTrack); if (pTakePart) { takeInfoClip(pAudioClip, pTakePart->takeInfo()); pTakePart->setClip(pAudioClip); } if (pMainForm) { pMainForm->addAudioFile(pAudioClip->filename()); qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks) pTracks->setCurrentClip(pAudioClip); } } break; } case qtractorTrack::Midi: { qtractorMidiClip *pMidiClip = static_cast (pClip); if (pMidiClip) { pMidiClip = new qtractorMidiClip(*pMidiClip); pMidiClip->setClipStart(iClipStart); pMidiClip->setClipOffset(iClipOffset); pMidiClip->setClipLength(iClipLength); addClip(pMidiClip, pTrack); if (pTakePart) { takeInfoClip(pMidiClip, pTakePart->takeInfo()); pTakePart->setClip(pMidiClip); } if (pMainForm) pMainForm->addMidiFile(pMidiClip->filename()); qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks) pTracks->setCurrentClip(pMidiClip); } break; } default: return false; } return true; } // When new tracks are needed. void qtractorClipCommand::addTrack ( qtractorTrack *pTrack ) { m_trackCommands.append( new qtractorAddTrackCommand(pTrack)); } // When MIDI clips are stretched. qtractorMidiEditCommand *qtractorClipCommand::createMidiEditCommand ( qtractorMidiClip *pMidiClip, float fTimeStretch ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return nullptr; // Make it like an undoable command... qtractorMidiEditCommand *pEditCommand = new qtractorMidiEditCommand(pMidiClip, name()); qtractorMidiSequence *pSeq = pMidiClip->sequence(); const unsigned long iTimeOffset = pSeq->timeOffset(); qtractorTimeScale::Cursor cursor(pSession->timeScale()); qtractorTimeScale::Node *pNode = cursor.seekFrame(pMidiClip->clipStart()); const unsigned long t0 = pNode->tickFromFrame(pMidiClip->clipStart()); unsigned long f1 = pMidiClip->clipStart() + pMidiClip->clipOffset(); 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 += pMidiClip->clipLength()); const unsigned long iTimeEnd = iTimeStart + pNode->tickFromFrame(f1) - t1; for (qtractorMidiEvent *pEvent = pSeq->events().first(); pEvent; pEvent = pEvent->next()) { unsigned long iTime = pEvent->time(); if (iTime >= iTimeStart && iTime < iTimeEnd) { iTime = qtractorTimeScale::uroundf( fTimeStretch * float(iTime)); unsigned long iDuration = pEvent->duration(); if (pEvent->type() == qtractorMidiEvent::NOTEON) { iDuration = qtractorTimeScale::uroundf( fTimeStretch * float(iDuration)); } pEditCommand->resizeEventTime(pEvent, iTime, iDuration); } } // Must have brand new revision... pMidiClip->setRevision(0); // That's it... return pEditCommand; } // Composite predicate. bool qtractorClipCommand::isEmpty (void) const { return m_trackCommands.isEmpty() && m_items.isEmpty(); } // Common executive method. bool qtractorClipCommand::execute ( bool bRedo ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; pSession->lock(); QListIterator track(m_trackCommands); while (track.hasNext()) { qtractorTrackCommand *pTrackCommand = track.next(); if (bRedo) pTrackCommand->redo(); else pTrackCommand->undo(); } // Pre-close needed clips once... QHash::ConstIterator clip = m_clips.constBegin(); const QHash::ConstIterator& clip_end = m_clips.constEnd(); for ( ; clip != clip_end; ++clip) { if (clip.value()) // Scrap peak file (audio). clip.key()->close(); } QListIterator iter(m_items); while (iter.hasNext()) { Item *pItem = iter.next(); qtractorClip *pClip = pItem->clip; qtractorTrack *pTrack = pItem->track; // Execute the command item... switch (pItem->command) { case AddClip: { if (bRedo) pTrack->addClip(pClip); else pTrack->removeClip(pClip); pItem->autoDelete = !bRedo; pSession->updateTrack(pTrack); setClearSelectReset(!bRedo); break; } case RemoveClip: { if (bRedo) pTrack->removeClip(pClip); else pTrack->addClip(pClip); pItem->autoDelete = bRedo; pSession->updateTrack(pTrack); setClearSelectReset(bRedo); break; } case FileClip: { const QString sOldFilename = pClip->filename(); unsigned short iOldTrackChannel = 0; pClip->setFilename(pItem->filename); qtractorMidiClip *pMidiClip = nullptr; if (pTrack->trackType() == qtractorTrack::Midi) pMidiClip = static_cast (pClip); if (pMidiClip) { iOldTrackChannel = pMidiClip->trackChannel(); pMidiClip->setTrackChannel(pItem->trackChannel); } pItem->filename = sOldFilename; if (pMidiClip) pItem->trackChannel = iOldTrackChannel; //pSession->updateTrack(pTrack); break; } case RenameClip: { const QString sOldName = pClip->clipName(); pClip->setClipName(pItem->clipName); pItem->clipName = sOldName; break; } case MoveClip: { qtractorTrack *pOldTrack = pClip->track(); const unsigned long iOldStart = pClip->clipStart(); const unsigned long iOldOffset = pClip->clipOffset(); const unsigned long iOldLength = pClip->clipLength(); const unsigned long iOldFadeIn = pClip->fadeInLength(); const unsigned long iOldFadeOut = pClip->fadeOutLength(); pOldTrack->removeClipEx(pClip); pClip->setClipStart(pItem->clipStart); pClip->setClipOffset(pItem->clipOffset); pClip->setClipLength(pItem->clipLength); pClip->setFadeInLength(pItem->fadeInLength); pClip->setFadeOutLength(pItem->fadeOutLength); pTrack->addClipEx(pClip); pItem->track = pOldTrack; pItem->clipStart = iOldStart; pItem->clipOffset = iOldOffset; pItem->clipLength = iOldLength; pItem->fadeInLength = iOldFadeIn; pItem->fadeOutLength = iOldFadeOut; if (pOldTrack != pTrack) pSession->updateTrack(pOldTrack); pSession->updateTrack(pTrack); setClearSelect(true); break; } case ResizeClip: { const unsigned long iOldStart = pClip->clipStart(); const unsigned long iOldOffset = pClip->clipOffset(); const unsigned long iOldLength = pClip->clipLength(); const unsigned long iOldFadeIn = pClip->fadeInLength(); const unsigned long iOldFadeOut = pClip->fadeOutLength(); float fOldTimeStretch = 0.0f; float fOldPitchShift = 0.0f; qtractorAudioClip *pAudioClip = nullptr; if (pTrack->trackType() == qtractorTrack::Audio) { pAudioClip = static_cast (pClip); if (pAudioClip) { if (pItem->timeStretch > 0.0f) { fOldTimeStretch = pAudioClip->timeStretch(); //--pAudioClip->close(); // Scrap peak file. } if (pItem->pitchShift > 0.0f) fOldPitchShift = pAudioClip->pitchShift(); } } if (iOldStart != pItem->clipStart) pTrack->removeClip(pClip); pClip->setClipStart(pItem->clipStart); pClip->setClipOffset(pItem->clipOffset); pClip->setClipLength(pItem->clipLength); pClip->setFadeInLength(pItem->fadeInLength); pClip->setFadeOutLength(pItem->fadeOutLength); if (pAudioClip) { if (pItem->timeStretch > 0.0f) { pAudioClip->setTimeStretch(pItem->timeStretch); pItem->timeStretch = fOldTimeStretch; } if (pItem->pitchShift > 0.0f) { pAudioClip->setPitchShift(pItem->pitchShift); pItem->pitchShift = fOldPitchShift; } } if (pItem->editCommand) { if (bRedo) (pItem->editCommand)->redo(); else (pItem->editCommand)->undo(); } if (iOldStart != pItem->clipStart) pTrack->insertClip(pClip); pItem->clipStart = iOldStart; pItem->clipOffset = iOldOffset; pItem->clipLength = iOldLength; pItem->fadeInLength = iOldFadeIn; pItem->fadeOutLength = iOldFadeOut; pSession->updateTrack(pTrack); break; } case GainClip: { const float fOldGain = pClip->clipGain(); pClip->setClipGain(pItem->clipGain); pItem->clipGain = fOldGain; break; } case PanningClip: { const float fOldPanning = pClip->clipPanning(); pClip->setClipPanning(pItem->clipPanning); pItem->clipPanning = fOldPanning; break; } case MuteClip: { const bool bOldMute = pClip->isClipMute(); pClip->setClipMute(pItem->clipMute); pItem->clipMute = bOldMute; break; } case FadeInClip: { const unsigned long iOldFadeIn = pClip->fadeInLength(); qtractorClip::FadeType oldFadeInType = pClip->fadeInType(); pClip->setFadeInType(pItem->fadeInType); pClip->setFadeInLength(pItem->fadeInLength); pItem->fadeInLength = iOldFadeIn; pItem->fadeInType = oldFadeInType; break; } case FadeOutClip: { const unsigned long iOldFadeOut = pClip->fadeOutLength(); qtractorClip::FadeType oldFadeOutType = pClip->fadeOutType(); pClip->setFadeOutType(pItem->fadeOutType); pClip->setFadeOutLength(pItem->fadeOutLength); pItem->fadeOutLength = iOldFadeOut; pItem->fadeOutType = oldFadeOutType; break; } case TimeStretchClip: { qtractorAudioClip *pAudioClip = nullptr; if (pTrack->trackType() == qtractorTrack::Audio) pAudioClip = static_cast (pClip); if (pAudioClip) { const float fOldTimeStretch = pAudioClip->timeStretch(); pAudioClip->setTimeStretch(pItem->timeStretch); pAudioClip->updateClipTime(); // Care of tempo change. pItem->timeStretch = fOldTimeStretch; } break; } case PitchShiftClip: { qtractorAudioClip *pAudioClip = nullptr; if (pTrack->trackType() == qtractorTrack::Audio) pAudioClip = static_cast (pClip); if (pAudioClip) { const float fOldPitchShift = pAudioClip->pitchShift(); pAudioClip->setPitchShift(pItem->pitchShift); pItem->pitchShift = fOldPitchShift; } break; } case TakeInfoClip: { qtractorClip::TakeInfo *pTakeInfo = pClip->takeInfo(); if (pTakeInfo) pTakeInfo->addRef(); pClip->setTakeInfo(pItem->takeInfo); if (pItem->takeInfo) pItem->takeInfo->releaseRef(); pItem->takeInfo = pTakeInfo; break; } case ResetClip: { const unsigned long iClipStartTime = pClip->clipStartTime(); const unsigned long iClipLengthTime = pClip->clipLengthTime(); pItem->clipLength = pSession->frameFromTickRange( iClipStartTime, iClipStartTime + iClipLengthTime); pClip->setClipLength(pItem->clipLength); #if 1// FIXUP: Don't quantize to MIDI metronomic time-scale... const unsigned long iClipOffset = pClip->clipOffset(); pClip->setClipOffset(pItem->clipOffset); pItem->clipOffset = iClipOffset; #else const unsigned long iClipOffsetTime = pClip->clipOffsetTime(); pClip->setClipOffset(pItem->clipOffset); pItem->clipOffset = pSession->frameFromTickRange( iClipStartTime, iClipStartTime + iClipOffsetTime, true); #endif break; } case StretcherFlagsClip: { qtractorAudioClip *pAudioClip = nullptr; if (pTrack->trackType() == qtractorTrack::Audio) pAudioClip = static_cast (pClip); if (pAudioClip) { const unsigned int bOldStretcherFlags = pAudioClip->stretcherFlags(); pAudioClip->setStretcherFlags(pItem->stretcherFlags); pItem->stretcherFlags = bOldStretcherFlags; } break; } default: break; } } // Re-open needed clips, just once... for (clip = m_clips.constBegin(); clip != clip_end; ++clip) clip.key()->open(); pSession->unlock(); return true; } // Virtual command methods. bool qtractorClipCommand::redo (void) { return execute(true); } bool qtractorClipCommand::undo (void) { return execute(false); } //---------------------------------------------------------------------- // class qtractorClipTakeCommand - declaration. // // Constructor. qtractorClipTakeCommand::qtractorClipTakeCommand ( qtractorClip::TakeInfo *pTakeInfo, qtractorTrack *pTrack, int iCurrentTake ) : qtractorClipCommand(QString()), m_pTakeInfo(pTakeInfo), m_iCurrentTake(iCurrentTake) { if (pTrack) qtractorCommand::setName(QObject::tr("take %1").arg(iCurrentTake + 1)); else qtractorCommand::setName(QObject::tr("reset takes")); if (pTrack && iCurrentTake >= 0) m_pTakeInfo->select(this, pTrack, iCurrentTake); else m_pTakeInfo->reset(this, pTrack == nullptr); } // Executive override. bool qtractorClipTakeCommand::execute ( bool bRedo ) { const int iCurrentTake = m_pTakeInfo->currentTake(); m_pTakeInfo->setCurrentTake(m_iCurrentTake); m_iCurrentTake = iCurrentTake;; return qtractorClipCommand::execute(bRedo); } //---------------------------------------------------------------------- // class qtractorClipRangeCommand - declaration. // // Constructor. qtractorClipRangeCommand::qtractorClipRangeCommand ( const QString& sName ) : qtractorClipCommand(sName) { } // Destructor. qtractorClipRangeCommand::~qtractorClipRangeCommand (void) { qDeleteAll(m_sessionCommands); m_sessionCommands.clear(); qDeleteAll(m_curveEditCommands); m_curveEditCommands.clear(); qDeleteAll(m_timeScaleMarkerCommands); m_timeScaleMarkerCommands.clear(); qDeleteAll(m_timeScaleNodeCommands); m_timeScaleNodeCommands.clear(); } // When Loop/Punch changes are needed. void qtractorClipRangeCommand::addSessionCommand ( qtractorSessionCommand *pSessionCommand ) { m_sessionCommands.append(pSessionCommand); } // When automation curves are needed. void qtractorClipRangeCommand::addCurveEditCommand ( qtractorCurveEditCommand *pCurveEditCommand ) { m_curveEditCommands.append(pCurveEditCommand); } // When location markers are needed. void qtractorClipRangeCommand::addTimeScaleMarkerCommand ( qtractorTimeScaleMarkerCommand *pTimeScaleMarkerCommand ) { m_timeScaleMarkerCommands.append(pTimeScaleMarkerCommand); } // When tempo-map/time-sig nodes are needed. void qtractorClipRangeCommand::addTimeScaleNodeCommand ( qtractorTimeScaleNodeCommand *pTimeScaleNodeCommand ) { m_timeScaleNodeCommands.append(pTimeScaleNodeCommand); } // Executive override. bool qtractorClipRangeCommand::execute ( bool bRedo ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; // Loop/Punch... if (!m_sessionCommands.isEmpty()) { QListIterator iter(m_sessionCommands); while (iter.hasNext()) { qtractorSessionCommand *pSessionCommand = iter.next(); if (bRedo) pSessionCommand->redo(); else pSessionCommand->undo(); } } // Automation... if (!m_curveEditCommands.isEmpty()) { QListIterator curve_iter(m_curveEditCommands); if (bRedo) { while (curve_iter.hasNext()) { qtractorCurveEditCommand *pCurveEditCommand = curve_iter.next(); pCurveEditCommand->redo(); pCurveEditCommand->curve()->update(); } } else { curve_iter.toBack(); while (curve_iter.hasPrevious()) { qtractorCurveEditCommand *pCurveEditCommand = curve_iter.previous(); pCurveEditCommand->undo(); pCurveEditCommand->curve()->update(); } } } // Markers... if (!m_timeScaleMarkerCommands.isEmpty()) { QListIterator marker_iter(m_timeScaleMarkerCommands); if (bRedo) { while (marker_iter.hasNext()) { qtractorTimeScaleMarkerCommand *pTimeScaleMarkerCommand = marker_iter.next(); pTimeScaleMarkerCommand->redo(); } } else { marker_iter.toBack(); while (marker_iter.hasPrevious()) { qtractorTimeScaleMarkerCommand *pTimeScaleMarkerCommand = marker_iter.previous(); pTimeScaleMarkerCommand->undo(); } } } // Time-map... if (!m_timeScaleNodeCommands.isEmpty()) { // If currently playing, we need to do a stop and go... const bool bPlaying = pSession->isPlaying(); if (bPlaying) pSession->lock(); QListIterator node_iter(m_timeScaleNodeCommands); if (bRedo) { while (node_iter.hasNext()) { qtractorTimeScaleNodeCommand *pTimeScaleNodeCommand = node_iter.next(); pTimeScaleNodeCommand->redo(); } } else { node_iter.toBack(); while (node_iter.hasPrevious()) { qtractorTimeScaleNodeCommand *pTimeScaleNodeCommand = node_iter.previous(); pTimeScaleNodeCommand->undo(); } } // Restore playback state, if needed... if (bPlaying) { // The Audio engine too... if (pSession->audioEngine()) pSession->audioEngine()->resetMetro(); // The MIDI engine queue needs a reset... if (pSession->midiEngine()) pSession->midiEngine()->resetTempo(); pSession->unlock(); } } // Clips... return qtractorClipCommand::execute(bRedo); } //---------------------------------------------------------------------- // class qtractorClipContextCommand - declaration. (virtual class) // // Constructor. qtractorClipContextCommand::qtractorClipContextCommand ( const QString& sName ) : qtractorCommand(sName), m_iRedoCount(0) { } // Destructor. qtractorClipContextCommand::~qtractorClipContextCommand (void) { } // Composite command methods. void qtractorClipContextCommand::addMidiClipContext ( qtractorMidiClip *pMidiClip ) { MidiClipCtx& mctx = m_midiClipCtxs[pMidiClip]; mctx.filename = pMidiClip->filename(); mctx.offset = pMidiClip->clipOffset(); mctx.length = pMidiClip->clipLength(); } // Composite predicate. bool qtractorClipContextCommand::isEmpty (void) const { return m_midiClipCtxs.isEmpty(); } // Main executive method. bool qtractorClipContextCommand::execute ( bool bRedo ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; ++m_iRedoCount; MidiClipCtxs::Iterator iter = m_midiClipCtxs.begin(); const MidiClipCtxs::Iterator& iter_end = m_midiClipCtxs.end(); for ( ; iter != iter_end; ++iter) { qtractorMidiClip *pMidiClip = iter.key(); MidiClipCtx& mctx = iter.value(); const QString filename = pMidiClip->filename(); const unsigned long offset = pMidiClip->clipOffset(); const unsigned long length = pMidiClip->clipLength(); if (m_iRedoCount == 1 && bRedo) { const QString& sFilename = pMidiClip->createFilePathRevision(true); if (!pMidiClip->saveCopyFile(sFilename, false)) continue; pMidiClip->setDirty(false); mctx.filename = sFilename; } pSession->files()->removeClipItem(qtractorFileList::Midi, pMidiClip); if (executeMidiClipContext(pMidiClip, mctx, bRedo)) { mctx.filename = filename; mctx.offset = offset; mctx.length = length; } pSession->files()->addClipItem(qtractorFileList::Midi, pMidiClip, true); } return true; } // Virtual command methods. bool qtractorClipContextCommand::redo (void) { return execute(true); } bool qtractorClipContextCommand::undo (void) { return execute(false); } //---------------------------------------------------------------------- // class qtractorClipSaveFileCommand - declaration. // // Constructor. qtractorClipSaveFileCommand::qtractorClipSaveFileCommand (void) : qtractorClipContextCommand(QObject::tr("clip save")) { } // Context (visitor) executive method. bool qtractorClipSaveFileCommand::executeMidiClipContext ( qtractorMidiClip *pMidiClip, const MidiClipCtx& mctx, bool /*bRedo*/ ) { pMidiClip->setClipLength(mctx.length); pMidiClip->setClipOffset(mctx.offset); pMidiClip->setFilenameEx(mctx.filename, true); return true; } //---------------------------------------------------------------------- // class qtractorClipUnlinkCommand - declaration. // // Constructor. qtractorClipUnlinkCommand::qtractorClipUnlinkCommand (void) : qtractorClipContextCommand(QObject::tr("clip unlink")) { } // Context (visitor) executive method. bool qtractorClipUnlinkCommand::executeMidiClipContext ( qtractorMidiClip *pMidiClip, const MidiClipCtx& mctx, bool bRedo ) { pMidiClip->setClipLength(mctx.length); pMidiClip->setClipOffset(mctx.offset); pMidiClip->setFilename(mctx.filename); if (bRedo) pMidiClip->unlinkHashData(); else pMidiClip->relinkHashData(); return true; } //---------------------------------------------------------------------- // class qtractorClipToolCommand - declaration. // // Constructor. qtractorClipToolCommand::qtractorClipToolCommand ( const QString& sName ) : qtractorCommand(QObject::tr("clip tool %1").arg(sName)), m_iRedoCount(0), m_pClipSaveFileCommand(nullptr) { } // Destructor. qtractorClipToolCommand::~qtractorClipToolCommand (void) { qDeleteAll(m_midiEditCommands); m_midiEditCommands.clear(); qDeleteAll(m_timeScaleNodeCommands); m_timeScaleNodeCommands.clear(); if (m_pClipSaveFileCommand) delete m_pClipSaveFileCommand; } // Check if a clip is already part of the iting set. bool qtractorClipToolCommand::isLinkedMidiClip ( qtractorMidiClip *pMidiClip ) const { QListIterator iter(m_midiEditCommands); while (iter.hasNext()) { qtractorMidiClip *pMidiClipIter = iter.next()->midiClip(); if (pMidiClipIter && pMidiClipIter->isLinkedClip(pMidiClip)) return true; } return false; } // Composite command methods. void qtractorClipToolCommand::addMidiEditCommand ( qtractorMidiEditCommand *pMidiEditCommand ) { m_midiEditCommands.append(pMidiEditCommand); } // When additional tempo-map/time-sig nodes are needed. void qtractorClipToolCommand::addTimeScaleNodeCommand ( qtractorTimeScaleNodeCommand *pTimeScaleNodeCommand ) { m_timeScaleNodeCommands.append(pTimeScaleNodeCommand); } // Composite predicate. bool qtractorClipToolCommand::isEmpty (void) const { return m_midiEditCommands.isEmpty(); } // Virtual command methods. bool qtractorClipToolCommand::redo (void) { ++m_iRedoCount; executeTimeScaleNodeCommands(true); QListIterator iter(m_midiEditCommands); while (iter.hasNext()) { qtractorMidiEditCommand *pMidiEditCommand = iter.next(); if (pMidiEditCommand) { qtractorMidiClip *pMidiClip = pMidiEditCommand->midiClip(); if (pMidiClip) { // Save if dirty... if (m_iRedoCount == 1/* && pMidiClip->isDirty()*/) { if (m_pClipSaveFileCommand == nullptr) m_pClipSaveFileCommand = new qtractorClipSaveFileCommand(); m_pClipSaveFileCommand->addMidiClipContext(pMidiClip); } // Redo as you told... pMidiEditCommand->redo(); } } } if (m_pClipSaveFileCommand) m_pClipSaveFileCommand->redo(); return true; } bool qtractorClipToolCommand::undo (void) { if (m_pClipSaveFileCommand) m_pClipSaveFileCommand->undo(); QListIterator iter(m_midiEditCommands); iter.toBack(); while (iter.hasPrevious()) { qtractorMidiEditCommand *pMidiEditCommand = iter.previous(); if (pMidiEditCommand) { qtractorMidiClip *pMidiClip = pMidiEditCommand->midiClip(); if (pMidiClip) { // Undo as you told... pMidiEditCommand->undo(); } } } executeTimeScaleNodeCommands(false); return (m_iRedoCount > 0); } // Execute tempo-map/time-sig commands. void qtractorClipToolCommand::executeTimeScaleNodeCommands ( bool bRedo ) { int iTimeScaleUpdate = 0; QListIterator iter(m_timeScaleNodeCommands); if (bRedo) iter.toFront(); else iter.toBack(); while (bRedo ? iter.hasNext() : iter.hasPrevious()) { qtractorTimeScaleNodeCommand *pTimeScaleNodeCommand = (bRedo ? iter.next() : iter.previous()); if (bRedo) pTimeScaleNodeCommand->redo(); else pTimeScaleNodeCommand->undo(); ++iTimeScaleUpdate; } qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm && iTimeScaleUpdate > 0) pMainForm->updateNotifySlot(qtractorCommand::ClearSelect); } //---------------------------------------------------------------------- // class qtractorClipRecordExCommand - implementation. // // Constructor. qtractorClipRecordExCommand::qtractorClipRecordExCommand ( qtractorClip *pClipRecordEx, bool bClipRecordEx ) : qtractorCommand(QObject::tr("clip record")) { m_pTrack = pClipRecordEx->track(); m_pClipRecordEx = pClipRecordEx; m_bClipRecordEx = bClipRecordEx; setRefresh(true); } // Clip-record command method. bool qtractorClipRecordExCommand::redo (void) { if (m_pTrack == nullptr) return false; // Carry on... qtractorClip *pClipRecordEx = m_pTrack->clipRecord(); const bool bClipRecordEx = m_pTrack->isClipRecordEx(); m_pTrack->setRecord(m_bClipRecordEx); m_pTrack->setClipRecord(m_bClipRecordEx ? m_pClipRecordEx : nullptr); m_pTrack->setClipRecordEx(m_bClipRecordEx); if (m_bClipRecordEx && m_pClipRecordEx && m_pTrack->trackType() == qtractorTrack::Midi) { qtractorSession *pSession = m_pTrack->session(); qtractorMidiClip *pMidiClip = static_cast (m_pClipRecordEx); if (pMidiClip && pSession) { pMidiClip->setStepInputHead(pSession->playHead()); pMidiClip->updateStepInput(); } } // Reset for undo. m_bClipRecordEx = bClipRecordEx; m_pClipRecordEx = pClipRecordEx; return true; } bool qtractorClipRecordExCommand::undo (void) { return redo(); } // end of qtractorClipCommand.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMmcEvent.cpp0000644000000000000000000000013215101070305017224 xustar0030 mtime=1761898693.084267645 30 atime=1761898693.084267645 30 ctime=1761898693.084267645 qtractor-1.5.9/src/qtractorMmcEvent.cpp0000644000175000001440000000713215101070305017217 0ustar00rncbcusers// qtractorMmcEvent.cpp // /**************************************************************************** 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. *****************************************************************************/ #include "qtractorMmcEvent.h" //---------------------------------------------------------------------- // qtractorMmcEvent - MMC custom event. // // Retrieve MMC time-code and standard frame position (SMPTE?). unsigned long qtractorMmcEvent::locate (void) const { unsigned long iLocate = 0; unsigned char *data = (unsigned char *) m_data.constData(); if (m_cmd == LOCATE && m_data.length() > 4 && data[0] == 0x01) { iLocate = (3600 * 30) * int(data[1] & 0x1f) // hh - hours [0..23] + ( 60 * 30) * int(data[2]) // mm - minutes [0..59] + ( 30) * int(data[3]) // ss - seconds [0..59] + int(data[4]); // ff - frames [0..29] } return iLocate; } // Retrieve MMC shuttle-speed and direction. float qtractorMmcEvent::shuttle (void) const { float fShuttle = 0.0f; if (m_cmd == SHUTTLE && m_data.length() > 2) { const unsigned char *data = (const unsigned char *) m_data.constData(); const unsigned char sh = data[0]; const unsigned char sm = data[1]; const unsigned char sl = data[2]; const unsigned int n = (sh & 0x38); const unsigned int p = ((sh & 0x07) << n) | (sm >> (7 - n)); const unsigned int q = ((sm << n) << 7) | sl; fShuttle = float(p) + float(q) / (1 << (14 - n)); if (sh & 0x40) fShuttle = -(fShuttle); } return fShuttle; } // Retrieve MMC step and direction. int qtractorMmcEvent::step (void) const { int iStep = 0; if (m_cmd == STEP && m_data.length() > 0) { const unsigned char *data = (const unsigned char *) m_data.constData(); iStep = (data[0] & 0x3f); if (data[0] & 0x40) iStep = -(iStep); } return iStep; } // Retrieve MMC masked-write sub-command data. qtractorMmcEvent::SubCommand qtractorMmcEvent::scmd (void) const { SubCommand scmd = TRACK_NONE; if (m_cmd == MASKED_WRITE && m_data.length() > 3) { const unsigned char *data = (const unsigned char *) m_data.constData(); scmd = SubCommand(data[0]); } return scmd; } int qtractorMmcEvent::track (void) const { int iTrack = 0; if (m_cmd == MASKED_WRITE && m_data.length() > 3) { const unsigned char *data = (unsigned char *) m_data.constData(); iTrack = (data[1] > 0 ? (data[1] * 7) : 0) - 5; for (int i = 0; i < 7; ++i) { const int iMask = (1 << i); if (data[2] & iMask) break; ++iTrack; } } return iTrack; } bool qtractorMmcEvent::isOn (void) const { bool bOn = false; if (m_cmd == MASKED_WRITE && m_data.length() > 3) { const unsigned char *data = (unsigned char *) m_data.constData(); for (int i = 0; i < 7; ++i) { const int iMask = (1 << i); if (data[2] & iMask) { bOn = (data[3] & iMask); break; } } } return bOn; } // end of qtractorMmcEvent.cpp qtractor-1.5.9/src/PaxHeaders/qtractorPluginListView.h0000644000000000000000000000013215101070305020100 xustar0030 mtime=1761898693.087267654 30 atime=1761898693.087267654 30 ctime=1761898693.087267654 qtractor-1.5.9/src/qtractorPluginListView.h0000644000175000001440000001614015101070305020072 0ustar00rncbcusers// qtractorPluginListView.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 __qtractorPluginListView_h #define __qtractorPluginListView_h #include // Forward declarations. class qtractorPlugin; class qtractorPluginList; class qtractorPluginForm; class qtractorRubberBand; //---------------------------------------------------------------------------- // qtractorPluginListItem -- Plugin list item. class qtractorPluginListItem : public QListWidgetItem { public: // Constructors. qtractorPluginListItem(qtractorPlugin *pPlugin); // Destructor. ~qtractorPluginListItem(); // Plugin container accessor. qtractorPlugin *plugin() const; // Special plugin form accessor. qtractorPluginForm *pluginForm(); // Activation methods. void updateActivated(); // Last known item rectangle. void setDirectAccessWidth(int iDirectAccessWidth) { m_iDirectAccessWidth = iDirectAccessWidth; } int directAccessWidth() const { return m_iDirectAccessWidth; } protected: // Common item initializer. void initItem(qtractorPlugin *pPlugin); private: // The plugin reference. qtractorPlugin *m_pPlugin; // Last known item logical width. int m_iDirectAccessWidth; // Common (de)activated icon/pixmap stuff. static QIcon *g_pIcons[3]; static int g_iIconsRefCount; }; //---------------------------------------------------------------------------- // qtractorPluginListView -- Plugin chain list widget instance. // class qtractorPluginListView : public QListWidget { Q_OBJECT public: // Construcctor. qtractorPluginListView(QWidget *pParent = nullptr); // Destructor. ~qtractorPluginListView(); // Plugin list accessors. void setPluginList(qtractorPluginList *pPluginList); qtractorPluginList *pluginList() const; // Common tiny scrollbar style decl. class TinyScrollBarStyle; // Special scrollbar style accessors. void setTinyScrollBar(bool bTinyScrollBar); bool isTinyScrollBar() const; // Plugin list refreshner; void refresh(); // Master clean-up. void clear(); // Get an item index, given the plugin reference... int pluginItem(qtractorPlugin *pPlugin); // Show insert pseudo-plugin audio bus connections. static void insertPluginBus(qtractorPlugin *pPlugin, int iBusMode); // Show selected Aux-Send bus on the mixer outputs pane. static void updateAuxSendPluginBus(qtractorPlugin *pPlugin); signals: // Plugin chain changed somehow. void contentsChanged(); public slots: // User interaction slots. void addPlugin(); void removePlugin(); void moveUpPlugin(); void moveDownPlugin(); protected slots: // Generic interaction slots. void activatePlugin(); void activateAllPlugins(); void deactivateAllPlugins(); void removeAllPlugins(); void loadPresetPlugin(); void directAccessPlugin(); void propertiesPlugin(); void editPlugin(); // Import/export plugin-list slots. void importPlugins(); void exportPlugins(); // Audio inserts specific slots. void addAudioInsertPlugin(); void addAudioAuxSendPlugin(); // MIDI inserts specific slots. void addMidiInsertPlugin(); void addMidiAuxSendPlugin(); void addMidiControlPlugin(); // Send/return insert specific slots. void insertPluginOutputs(); void insertPluginInputs(); void midiControlAutoConnect(); // Audio specific slots. void audioOutputs(); void audioOutputBus(); void audioOutputBusName(); void audioOutputAutoConnect(); // Drop item slots. void dropMove(); void dropCopy(); void dropCancel(); // Double/simple-click handler. void itemDoubleClickedSlot(QListWidgetItem *); void itemClickedSlot(QListWidgetItem *); // Row-change handler. void currentRowChangedSlot(int); protected: // Focus-in handler. void focusInEvent(QFocusEvent *pFocusEvent); // Move item on list. void moveItem(qtractorPluginListItem *pItem, qtractorPluginListItem *pNextItem); // Copy item on list. void copyItem(qtractorPluginListItem *pItem, qtractorPluginListItem *pNextItem); // Trap for help/tool-tip events. bool eventFilter(QObject *pObject, QEvent *pEvent); // To get precize clicking for in-place (de)activation, // and for 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 *); void dropEvent(QDropEvent *pDropEvent); void wheelEvent(QWheelEvent *pWheelEvent); // Drag-n-drop stuff. bool canDropEvent(QDropEvent *pDropEvent); bool canDropItem(QDropEvent *pDropEvent); // Ensure given item is brought to viewport visibility... void ensureVisibleItem(qtractorPluginListItem *pItem); // Show and move rubber-band item. void moveRubberBand(qtractorPluginListItem *pDropItem); // Context menu event handler. void contextMenuEvent(QContextMenuEvent *pContextMenuEvent); // MIDI codec methods (static). static void encodeItem (QMimeData *pMimeData, qtractorPluginListItem *pItem); static bool canDecodeItem(const QMimeData *pMimeData); static qtractorPluginListItem *decodeItem(const QMimeData *pMimeData); // Show insert pseudo-plugin audio bus connections. void insertPluginBus(int iBusMode); // Direct access parameter handle. void dragDirectAccess(const QPoint& pos); // Direct access parameter reset (to default value). void resetDirectAccess(const QPoint& pos); // Initial size-policy hints. QSize sizeHint() const; private: // Instance variables. qtractorPluginList *m_pPluginList; // The current dragging item stuff. enum DragState { DragNone = 0, DragDirectAccess } m_dragState, m_dragCursor; // The mouse clicked item for in-place (de)activation. qtractorPluginListItem *m_pClickedItem; // The point from where drag started. QPoint m_posDrag; // Item we'll eventually drag around. qtractorPluginListItem *m_pDragItem; // Item we'll eventually drop something. qtractorPluginListItem *m_pDropItem; // To show the point where drop will go. qtractorRubberBand *m_pRubberBand; // Common tiny scrollbar style stuff. TinyScrollBarStyle *m_pTinyScrollBarStyle; // To track the current item... qtractorPluginListItem *m_pCurrentItem; }; #endif // __qtractorPluginListView_h // end of qtractorPluginListView.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiEditCommand.h0000644000000000000000000000013215101070305020142 xustar0030 mtime=1761898693.079267629 30 atime=1761898693.079267629 30 ctime=1761898693.079267629 qtractor-1.5.9/src/qtractorMidiEditCommand.h0000644000175000001440000000753415101070305020143 0ustar00rncbcusers// qtractorMidiEditCommand.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 __qtractorMidiEditCommand_h #define __qtractorMidiEditCommand_h #include "qtractorCommand.h" #include "qtractorMidiEvent.h" #include // Forward declarations. class qtractorMidiClip; class qtractorTimeScaleNodeCommand; //---------------------------------------------------------------------- // class qtractorMidiEditCommand - declaration. // class qtractorMidiEditCommand : public qtractorCommand { public: // Constructor. qtractorMidiEditCommand(qtractorMidiClip *pMidiClip, const QString& sName); // Destructor. ~qtractorMidiEditCommand(); // Sequence accessor. qtractorMidiClip *midiClip() const { return m_pMidiClip; } // Primitive command types. enum CommandType { InsertEvent, MoveEventTime, MoveEventNote, MoveEventValue, ResizeEventTime, ResizeEventValue, UpdateEvent, RemoveEvent }; // Primitive command methods. void insertEvent(qtractorMidiEvent *pEvent); void moveEventTime(qtractorMidiEvent *pEvent, unsigned long iTime); void moveEventNote(qtractorMidiEvent *pEvent, int iNote, unsigned long iTime); void moveEventValue(qtractorMidiEvent *pEvent, int iValue, unsigned long iTime); void resizeEventTime(qtractorMidiEvent *pEvent, unsigned long iTime, unsigned long iDuration); void resizeEventValue(qtractorMidiEvent *pEvent, int iValue); void updateEvent(qtractorMidiEvent *pEvent, int iNote, unsigned long iTime, unsigned long iDuration, int iValue); void removeEvent(qtractorMidiEvent *pEvent); // Check whether the event is already in chain. bool findEvent(qtractorMidiEvent *pEvent, CommandType cmd) const; // Tell whether there are any items to edit. bool isEmpty() const; // Virtual command methods. bool redo(); bool undo(); // Adjust edit-command result to prevent event overlapping. bool adjust(); // Add a command to the execution list... void addTimeScaleNodeCommand(qtractorTimeScaleNodeCommand *pTimeScaleNodeCommand); protected: // Common executive method. bool execute(bool bRedo); // Execute tempo-map/time-sig commands. void executeTimeScaleNodeCommands(bool bRedo); private: // Event item struct. struct Item { // Item constructor. Item(CommandType cmd, qtractorMidiEvent *pEvent, int iNote = 0, unsigned long iTime = 0, unsigned long iDuration = 0, unsigned int iValue = 0) : command(cmd), event(pEvent), note(iNote), time(iTime), duration(iDuration), value(iValue), autoDelete(false) {} // Item members. CommandType command; qtractorMidiEvent *event; int note; unsigned long time; unsigned long duration; int value; bool autoDelete; }; // Instance variables. qtractorMidiClip *m_pMidiClip; QList m_items; bool m_bAdjusted; unsigned long m_iDuration; QList m_timeScaleNodeCommands; }; #endif // __qtractorMidiEditCommand_h // end of qtractorMidiEditCommand.h qtractor-1.5.9/src/PaxHeaders/qtractorVst3Plugin.cpp0000644000000000000000000000012715101070305017530 xustar0029 mtime=1761898693.09226767 29 atime=1761898693.09226767 29 ctime=1761898693.09226767 qtractor-1.5.9/src/qtractorVst3Plugin.cpp0000644000175000001440000026507415101070305017532 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.9/src/PaxHeaders/qtractorFileListView.h0000644000000000000000000000013215101070305017521 xustar0030 mtime=1761898693.071267604 30 atime=1761898693.071267604 30 ctime=1761898693.071267604 qtractor-1.5.9/src/qtractorFileListView.h0000644000175000001440000002174415101070305017521 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.9/src/PaxHeaders/qtractorMidiControlPluginWidget.cpp0000644000000000000000000000013215101070305022254 xustar0030 mtime=1761898693.078267626 30 atime=1761898693.078267626 30 ctime=1761898693.078267626 qtractor-1.5.9/src/qtractorMidiControlPluginWidget.cpp0000644000175000001440000001101015101070305022235 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.9/src/PaxHeaders/qtractorClipCommand.h0000644000000000000000000000013215101070305017341 xustar0030 mtime=1761898693.067267591 30 atime=1761898693.067267591 30 ctime=1761898693.067267591 qtractor-1.5.9/src/qtractorClipCommand.h0000644000175000001440000002513415101070305017336 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.9/src/PaxHeaders/qtractorOptionsForm.cpp0000644000000000000000000000013215101070305017765 xustar0030 mtime=1761898693.085267648 30 atime=1761898693.085267648 30 ctime=1761898693.085267648 qtractor-1.5.9/src/qtractorOptionsForm.cpp0000644000175000001440000023343315101070305017765 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 "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 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.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.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->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(sPath).exists(); } 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.9/src/PaxHeaders/appdata0000644000000000000000000000013215101070305014557 xustar0030 mtime=1761898693.056842155 30 atime=1761898693.056377872 30 ctime=1761898693.056842155 qtractor-1.5.9/src/appdata/0000755000175000001440000000000015101070305014624 5ustar00rncbcusersqtractor-1.5.9/src/appdata/PaxHeaders/org.rncbc.qtractor.metainfo.xml0000644000000000000000000000013215101070305022672 xustar0030 mtime=1761898693.056842155 30 atime=1761898693.056795325 30 ctime=1761898693.056842155 qtractor-1.5.9/src/appdata/org.rncbc.qtractor.metainfo.xml0000644000175000001440000000400015101070305022654 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.9/src/appdata/PaxHeaders/org.rncbc.qtractor.desktop0000644000000000000000000000013215101070305021742 xustar0030 mtime=1761898693.056795325 30 atime=1761898693.056795325 30 ctime=1761898693.056795325 qtractor-1.5.9/src/appdata/org.rncbc.qtractor.desktop0000644000175000001440000000146015101070305021733 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.9/src/PaxHeaders/qtractorMidiRpn.h0000644000000000000000000000013215101070305016515 xustar0030 mtime=1761898693.083267642 30 atime=1761898693.083267642 30 ctime=1761898693.083267642 qtractor-1.5.9/src/qtractorMidiRpn.h0000644000175000001440000000314315101070305016506 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.9/src/PaxHeaders/vestige0000644000000000000000000000013215101070305014613 xustar0030 mtime=1761898693.107267718 30 atime=1761898693.107267718 30 ctime=1761898693.107267718 qtractor-1.5.9/src/vestige/0000755000175000001440000000000015101070305014660 5ustar00rncbcusersqtractor-1.5.9/src/vestige/PaxHeaders/vestige.h0000644000000000000000000000013215101070305016507 xustar0030 mtime=1761898693.107267718 30 atime=1761898693.107267718 30 ctime=1761898693.107267718 qtractor-1.5.9/src/vestige/vestige.h0000644000175000001440000002340515101070305016503 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.9/src/PaxHeaders/qtractorAudioClip.cpp0000644000000000000000000000013215101070305017357 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.062554172 30 ctime=1761898693.062554172 qtractor-1.5.9/src/qtractorAudioClip.cpp0000644000175000001440000005627515101070305017366 0ustar00rncbcusers// qtractorAudioClip.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 "qtractorAudioClip.h" #include "qtractorAudioEngine.h" #include "qtractorAudioPeak.h" #include "qtractorTimeStretcher.h" #include "qtractorDocument.h" #include "qtractorSession.h" #include "qtractorFileList.h" #include #include #include #include #include //---------------------------------------------------------------------- // class qtractorAudioClip::Key -- Audio buffered clip (hash key). // class qtractorAudioClip::Key { public: // Constructor. Key(qtractorAudioClip *pAudioClip) { update(pAudioClip); } // Key settler. void update(qtractorAudioClip *pAudioClip) { m_pTrack = pAudioClip->track(); m_sFilename = pAudioClip->filename();; m_iClipOffset = pAudioClip->clipOffset(); m_iClipLength = pAudioClip->clipLength(); m_fClipGain = pAudioClip->clipGain(); m_fClipPanning = pAudioClip->clipPanning(); m_fTimeStretch = pAudioClip->timeStretch(); m_fPitchShift = pAudioClip->pitchShift(); m_iOverlap = pAudioClip->overlap(); } // Key accessors. qtractorTrack *track() const { return m_pTrack; } const QString& filename() const { return m_sFilename; } unsigned long clipOffset() const { return m_iClipOffset; } unsigned long clipLength() const { return m_iClipLength; } float clipGain() const { return m_fClipGain; } float clipPanning() const { return m_fClipPanning; } float timeStretch() const { return m_fTimeStretch; } float pitchShift() const { return m_fPitchShift; } unsigned int overlap() const { return m_iOverlap; } // Match descriminator. bool operator== (const Key& other) const { return m_pTrack == other.track() && m_sFilename == other.filename() && m_iClipOffset == other.clipOffset() && m_iClipLength == other.clipLength() && m_fClipGain == other.clipGain() && m_fClipPanning == other.clipPanning() && m_fTimeStretch == other.timeStretch() && m_fPitchShift == other.pitchShift() && m_iOverlap == other.overlap(); } private: // Interesting variables. qtractorTrack *m_pTrack; QString m_sFilename; unsigned long m_iClipOffset; unsigned long m_iClipLength; float m_fClipGain; float m_fClipPanning; float m_fTimeStretch; float m_fPitchShift; unsigned int m_iOverlap; }; uint qHash ( const qtractorAudioClip::Key& key ) { return qHash(key.track()) ^ qHash(key.filename()) ^ qHash(key.clipOffset()) ^ qHash(key.clipLength()) ^ qHash(long(1000.0f * key.clipGain())) ^ qHash(long(1000.0f * key.clipPanning())) ^ qHash(long(1000.0f * key.timeStretch())) ^ qHash(long(1000.0f * key.pitchShift())) ^ qHash(key.overlap()); } qtractorAudioClip::Hash qtractorAudioClip::g_hashTable; //---------------------------------------------------------------------- // class qtractorAudioClip -- Audio file/buffer clip. // // Constructor. qtractorAudioClip::qtractorAudioClip ( qtractorTrack *pTrack ) : qtractorClip(pTrack) { m_pPeak = nullptr; m_pKey = nullptr; m_pData = nullptr; m_fTimeStretch = 1.0f; m_fPitchShift = 1.0f; m_iStretcherFlags = qtractorAudioBuffer::defaultStretcherFlags(); m_iOverlap = 0; m_pFractGains = nullptr; } // Copy constructor. qtractorAudioClip::qtractorAudioClip ( const qtractorAudioClip& clip ) : qtractorClip(clip.track()) { m_pPeak = nullptr; m_pKey = nullptr; m_pData = nullptr; m_fTimeStretch = clip.timeStretch(); m_fPitchShift = clip.pitchShift(); m_iStretcherFlags = clip.stretcherFlags(); m_iOverlap = clip.overlap(); m_pFractGains = nullptr; setFilename(clip.filename()); setClipName(clip.clipName()); setClipGain(clip.clipGain()); setClipPanning(clip.clipPanning()); setClipMute(clip.isClipMute()); setFadeInType(clip.fadeInType()); setFadeOutType(clip.fadeOutType()); // Clone the audio peak, if any... if (clip.m_pPeak) m_pPeak = new qtractorAudioPeak(*clip.m_pPeak); } // Destructor. qtractorAudioClip::~qtractorAudioClip (void) { close(); if (m_pPeak) delete m_pPeak; } // Alternating overlap test. bool qtractorAudioClip::isOverlap ( unsigned int iOverlapSize ) const { if (m_pData == nullptr) return false; const unsigned long iClipStart = clipStart(); const unsigned long iClipEnd = iClipStart + clipLength() + iOverlapSize; QListIterator iter(m_pData->clips()); while (iter.hasNext()) { qtractorAudioClip *pClip = iter.next(); const unsigned long iClipStart2 = pClip->clipStart(); const unsigned long iClipEnd2 = iClipStart2 + pClip->clipLength() + iOverlapSize; if ((iClipStart >= iClipStart2 && iClipEnd2 > iClipStart) || (iClipEnd > iClipStart2 && iClipEnd2 >= iClipEnd)) return true; } return false; } // The main use method. bool qtractorAudioClip::openAudioFile ( const QString& sFilename, int iMode ) { closeAudioFile(); #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioClip[%p]::openAudioFile(\"%s\", %d)", this, sFilename.toUtf8().constData(), iMode); #endif qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; // Check file buffer number of channels... unsigned short iChannels = 0; const bool bWrite = (iMode & qtractorAudioFile::Write); qtractorAudioBus *pAudioBus = static_cast (bWrite ? pTrack->inputBus() : pTrack->outputBus()); if (pAudioBus) iChannels = pAudioBus->channels(); // However, this number of channels is only useful // only when recording, otherwise we'll stick with // source audio file's one... if (bWrite && iChannels < 1) return false; // Save old property (need for peak file ignition)... const bool bFilenameChanged = (sFilename != filename()); // Set local properties... setFilename(sFilename); setDirty(false); // Register file path... pSession->files()->addClipItemEx(qtractorFileList::Audio, this, bWrite); // New key-data sequence... if (!bWrite) { m_pKey = new Key(this); m_pData = g_hashTable.value(*m_pKey, nullptr); if (m_pData) { // Check if current clip overlaps any other... const unsigned int iOverlapSize = pSession->audioEngine()->bufferSize() << 2; bool bOverlap = isOverlap(iOverlapSize); while (bOverlap) { ++m_iOverlap; m_pKey->update(this); m_pData = g_hashTable.value(*m_pKey, nullptr); bOverlap = isOverlap(iOverlapSize); } // Only if it doesn't overlap any... if (m_pData && !bOverlap) { m_pData->attach(this); // Peak files should also be created on-the-fly... qtractorAudioBuffer *pBuff = m_pData->buffer(); if (m_pPeak == nullptr || bFilenameChanged) { qtractorAudioPeakFactory *pPeakFactory = pSession->audioPeakFactory(); if (pPeakFactory) { if (m_pPeak) delete m_pPeak; m_pPeak = pPeakFactory->createPeak( sFilename, pBuff->timeStretch()); } } // Gain/panning fractionalizer(tm)... updateFractGains(pBuff); // Clip name should be clear about it all. if (clipName().isEmpty()) setClipName(shortClipName(QFileInfo(filename()).baseName())); return true; } } } // Initialize audio buffer container... m_pData = new Data(pTrack, iChannels); m_pData->attach(this); qtractorAudioBuffer *pBuff = m_pData->buffer(); pBuff->setOffset(clipOffset()); pBuff->setLength(clipLength()); pBuff->setGain(clipGain()); pBuff->setPanning(clipPanning()); pBuff->setTimeStretch(m_fTimeStretch); pBuff->setPitchShift(m_fPitchShift); pBuff->setStretcherFlags(m_iStretcherFlags); if (!pBuff->open(sFilename, iMode)) { delete m_pData; m_pData = nullptr; if (m_pFractGains) { delete [] m_pFractGains; m_pFractGains = nullptr; } return false; } // Gain/panning fractionalizer(tm)... updateFractGains(pBuff); // Default clip length will be the whole file length. if (clipLength() == 0) setClipLength(pBuff->length() - pBuff->offset()); // Peak files should also be created on-the-fly? if (m_pPeak == nullptr || bFilenameChanged) { qtractorAudioPeakFactory *pPeakFactory = pSession->audioPeakFactory(); if (pPeakFactory) { if (m_pPeak) delete m_pPeak; m_pPeak = pPeakFactory->createPeak( sFilename, pBuff->timeStretch()); if (bWrite) pBuff->setPeakFile(m_pPeak->peakFile()); } } // Clip name should be clear about it all. if (clipName().isEmpty()) setClipName(shortClipName(QFileInfo(filename()).baseName())); // Something might have changed... updateHashKey(); insertHashKey(); return true; } // Private cleanup. void qtractorAudioClip::closeAudioFile (void) { if (m_pData) { m_pData->detach(this); if (m_pData->count() < 1) { removeHashKey(); delete m_pData; #if 0 // ATTN: If proven empty, remove the file... if (clipLength() < 1) QFile::remove(filename()); #endif } m_pData = nullptr; } if (m_pKey) { delete m_pKey; m_pKey = nullptr; } if (m_pFractGains) { delete [] m_pFractGains; m_pFractGains = nullptr; } } // Manage local hash key. void qtractorAudioClip::insertHashKey (void) { if (m_pKey) g_hashTable.insert(*m_pKey, m_pData); } void qtractorAudioClip::updateHashKey (void) { if (m_pKey == nullptr) m_pKey = new Key(this); else m_pKey->update(this); } void qtractorAudioClip::removeHashKey (void) { if (m_pKey) g_hashTable.remove(*m_pKey); } // Unlink (clone) local hash data. void qtractorAudioClip::unlinkHashData (void) { if (m_pData == nullptr) return; if (m_pData->count() < 2) return; qtractorAudioBuffer *pBuff = m_pData->buffer(); Data *pNewData = new Data(track(), pBuff->channels()); qtractorAudioBuffer *pNewBuff = pNewData->buffer(); pNewBuff->setOffset(pBuff->offset()); pNewBuff->setLength(pBuff->length()); pNewBuff->setTimeStretch(pBuff->timeStretch()); pNewBuff->setPitchShift(pBuff->pitchShift()); if (pNewBuff->open(filename())) { m_pData->detach(this); m_pData = pNewData; if (m_pKey) { delete m_pKey; m_pKey = nullptr; } m_pData->attach(this); } else { delete pNewData; } } // Relink local hash data. void qtractorAudioClip::relinkHashData (void) { if (m_pData == nullptr) return; if (m_pData->count() > 1) return; removeHashKey(); updateHashKey(); Data *pNewData = g_hashTable.value(*m_pKey, nullptr); if (pNewData == nullptr) { delete m_pKey; m_pKey = nullptr; } else { m_pData->detach(this); delete m_pData; m_pData = pNewData; m_pData->attach(this); } insertHashKey(); } // Whether local hash is being shared. bool qtractorAudioClip::isHashLinked (void) const { return (m_pData && m_pData->count() > 1); } // Make sure the clip hash-table gets reset. void qtractorAudioClip::clearHashTable (void) { g_hashTable.clear(); } // Gain/panning fractionalizer(tm)... void qtractorAudioClip::updateFractGains ( qtractorAudioBuffer *pBuff ) { if (m_pFractGains) { delete [] m_pFractGains; m_pFractGains = nullptr; } const unsigned short iChannels = pBuff->channels(); m_pFractGains = new FractGain [iChannels]; for (unsigned short i = 0; i < iChannels; ++i) { FractGain& fractGain = m_pFractGains[i]; fractGain.num = 1; fractGain.den = 8; float fGain = clipGain() * pBuff->channelGain(i); while(fGain != int(fGain) && fractGain.den < 20) { fractGain.den += 2; fGain *= 4.0f; } fractGain.num = int(fGain); } } // Direct write method. void qtractorAudioClip::write ( float **ppBuffer, unsigned int iFrames, unsigned short iChannels, unsigned int iOffset ) { if (m_pData) m_pData->write(ppBuffer, iFrames, iChannels, iOffset); } // Intra-clip frame positioning. void qtractorAudioClip::seek ( unsigned long iFrame ) { if (m_pData) m_pData->seek(iFrame); } // Reset clip state. void qtractorAudioClip::reset ( bool bLooping ) { if (m_pData) m_pData->reset(bLooping); } // Loop positioning. void qtractorAudioClip::setLoop ( unsigned long iLoopStart, unsigned long iLoopEnd ) { if (m_pData == nullptr) return; qtractorAudioBuffer *pBuff = m_pData->buffer(); if (pBuff == nullptr) return; if (iLoopStart == 0 && iLoopEnd >= pBuff->length()) iLoopStart = iLoopEnd = 0; if (iLoopStart == pBuff->loopStart() && iLoopEnd == pBuff->loopEnd()) return; // Check if buffer data has been hash-linked, // unlink (clone) it for proper loop isolation... if (iLoopStart < iLoopEnd) { // Unlink (clone) buffer data... unlinkHashData(); } else { // Relink buffer data... relinkHashData(); } // A brand new one is up! pBuff = m_pData->buffer(); pBuff->setLoop(iLoopStart, iLoopEnd); } // Clip close-commit (record specific) void qtractorAudioClip::close (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorAudioClip[%p]::close()", this); #endif // Take pretended clip-length... qtractorAudioBuffer *pBuff = buffer(); if (pBuff) { // Commit the final clip length (record specific)... if (clipLength() < 1) setClipLength(pBuff->fileLength()); else // Shall we ditch the current peak file? // (don't if closing from recording) if (m_pPeak && pBuff->peakFile() == nullptr) { delete m_pPeak; m_pPeak = nullptr; } } // Close and ditch stuff... closeAudioFile(); } // Audio clip (re)open method. void qtractorAudioClip::open (void) { const QString sFilename(filename()); openAudioFile(sFilename); } // Audio clip special process cycle executive. void qtractorAudioClip::process ( unsigned long iFrameStart, unsigned long iFrameEnd ) { qtractorAudioBuffer *pBuff = buffer(); if (pBuff == nullptr) return; qtractorAudioBus *pAudioBus = static_cast (track()->outputBus()); if (pAudioBus == nullptr) return; // Get the next bunch from the clip... const unsigned long iClipStart = clipStart(); if (iClipStart > iFrameEnd) return; const unsigned long iClipEnd = iClipStart + clipLength(); if (iClipEnd < iFrameStart) return; const unsigned long iOffset = (iFrameEnd < iClipEnd ? iFrameEnd : iClipEnd) - iClipStart; if (iClipStart > iFrameStart) { if (pBuff->inSync(0, iOffset)) { pBuff->readMix( pAudioBus->buffer(), iOffset, pAudioBus->channels(), iClipStart - iFrameStart, fadeInOutGain(iOffset)); } } else { if (pBuff->inSync(iFrameStart - iClipStart, iOffset)) { pBuff->readMix( pAudioBus->buffer(), (iFrameEnd < iClipEnd ? iFrameEnd : iClipEnd) - iFrameStart, pAudioBus->channels(), 0, fadeInOutGain(iOffset)); } } } // Audio clip freewheeling process cycle executive (needed for export). void qtractorAudioClip::process_export ( unsigned long iFrameStart, unsigned long iFrameEnd ) { // Direct sync method. if (m_pData) m_pData->syncExport(); // Normal clip processing... process(iFrameStart, iFrameEnd); } // Audio clip paint method. void qtractorAudioClip::draw ( QPainter *pPainter, const QRect& clipRect, unsigned long iClipOffset ) { qtractorSession *pSession = track()->session(); if (pSession == nullptr) return; if (m_pFractGains == nullptr) return; // Cache some peak data... if (m_pPeak == nullptr) return; const unsigned long iFrameOffset = iClipOffset + clipOffset(); const int x0 = pSession->pixelFromFrame(iClipOffset); const unsigned long iFrameLength = pSession->frameFromPixel(x0 + clipRect.width()) - iClipOffset; // Grab them in... qtractorAudioPeakFile::Frame *pPeakFrames = m_pPeak->peakFrames(iFrameOffset, iFrameLength, clipRect.width()); if (pPeakFrames == nullptr) return; // Make some expectations... const unsigned int iPeakLength = m_pPeak->peakLength(); if (iPeakLength < 1) return; // Polygon init... unsigned short k; const unsigned short iChannels = m_pPeak->channels(); const unsigned int iPolyPoints = (iPeakLength << 1); QPolygon **pPolyMax = new QPolygon* [iChannels]; QPolygon **pPolyRms = new QPolygon* [iChannels]; for (k = 0; k < iChannels; ++k) { pPolyMax[k] = new QPolygon(iPolyPoints); pPolyRms[k] = new QPolygon(iPolyPoints); } // Draw peak chart... const int h1 = (clipRect.height() / iChannels); const int h2 = (h1 >> 1); int x, y, ymax, ymin, yrms; // Build polygonal vertexes... const int n2 = int(iPeakLength); for (int n = 0; n < n2; ++n) { x = clipRect.x() + (n * clipRect.width()) / n2; y = clipRect.y() + h2; for (k = 0; k < iChannels; ++k) { const FractGain& fractGain = m_pFractGains[k]; const int h2gain = (h2 * fractGain.num); ymax = (h2gain * pPeakFrames->max) >> fractGain.den; ymin = (h2gain * pPeakFrames->min) >> fractGain.den; yrms = (h2gain * pPeakFrames->rms) >> fractGain.den; pPolyMax[k]->setPoint(n, x, y - ymax); pPolyMax[k]->setPoint(iPolyPoints - n - 1, x, y + ymin); pPolyRms[k]->setPoint(n, x, y - yrms); pPolyRms[k]->setPoint(iPolyPoints - n - 1, x, y + yrms); y += h1; ++pPeakFrames; } } // Close, draw and free the polygons... QColor fg(track()->foreground()); fg.setAlpha(200); pPainter->setPen(fg.lighter(140)); pPainter->setBrush(fg); for (k = 0; k < iChannels; ++k) { pPainter->drawPolygon(*pPolyMax[k]); pPainter->drawPolygon(*pPolyRms[k]); delete pPolyRms[k]; delete pPolyMax[k]; } // Done on polygons. delete [] pPolyRms; delete [] pPolyMax; } // Audio clip tool-tip. QString qtractorAudioClip::toolTip (void) const { QString sToolTip = qtractorClip::toolTip(); qtractorAudioBuffer *pBuff = buffer(); if (pBuff) { qtractorAudioFile *pFile = pBuff->file(); if (pFile) { sToolTip += QObject::tr("\nAudio:\t%1 channels, %2 Hz") .arg(pFile->channels()) .arg(pFile->sampleRate()); const float fGain = clipGain(); if (fGain < 0.999f || fGain > 1.001f) sToolTip += QObject::tr(" (%1 dB)") .arg(20.0f * ::log10f(fGain), 0, 'g', 2); const float fPanning = clipPanning(); if (fPanning < -0.001f || fPanning > +0.001f) sToolTip += QObject::tr(" (%1 pan)") .arg(fPanning, 0, 'g', 1); if (pBuff->isTimeStretch()) sToolTip += QObject::tr("\n\t(%1% time stretch)") .arg(100.0f * pBuff->timeStretch(), 0, 'g', 3); if (pBuff->isPitchShift()) sToolTip += QObject::tr("\n\t(%1 semitones pitch shift)") .arg(12.0f * ::logf(pBuff->pitchShift()) / M_LN2, 0, 'g', 2); } } return sToolTip; } // Virtual document element methods. bool qtractorAudioClip::loadClipElement ( qtractorDocument */*pDocument*/, QDomElement *pElement ) { // 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 clip properties.. if (eChild.tagName() == "filename") qtractorAudioClip::setFilename(eChild.text()); else if (eChild.tagName() == "time-stretch") qtractorAudioClip::setTimeStretch(eChild.text().toFloat()); else if (eChild.tagName() == "pitch-shift") qtractorAudioClip::setPitchShift(eChild.text().toFloat()); else if (eChild.tagName() == "wsola-time-stretch") qtractorAudioClip::setStretcherFlag( qtractorTimeStretcher::WsolaTimeStretch, qtractorDocument::boolFromText(eChild.text())); else if (eChild.tagName() == "wsola-quick-seek") qtractorAudioClip::setStretcherFlag( qtractorTimeStretcher::WsolaQuickSeek, qtractorDocument::boolFromText(eChild.text())); #ifdef CONFIG_LIBRUBBERBAND else if (eChild.tagName() == "rubberband-formant") qtractorAudioClip::setStretcherFlag( qtractorTimeStretcher::RubberBandFormant, qtractorDocument::boolFromText(eChild.text())); #ifdef CONFIG_LIBRUBBERBAND_R3 else if (eChild.tagName() == "rubberband-finer-r3") qtractorAudioClip::setStretcherFlag( qtractorTimeStretcher::RubberBandFinerR3, qtractorDocument::boolFromText(eChild.text())); #endif #endif } return true; } bool qtractorAudioClip::saveClipElement ( qtractorDocument *pDocument, QDomElement *pElement ) { QDomElement eAudioClip = pDocument->document()->createElement("audio-clip"); pDocument->saveTextElement("filename", qtractorAudioClip::relativeFilename(pDocument), &eAudioClip); pDocument->saveTextElement("time-stretch", QString::number(qtractorAudioClip::timeStretch()), &eAudioClip); pDocument->saveTextElement("pitch-shift", QString::number(qtractorAudioClip::pitchShift()), &eAudioClip); pDocument->saveTextElement("wsola-time-stretch", qtractorDocument::textFromBool( qtractorAudioClip::isStretcherFlag( qtractorTimeStretcher::WsolaTimeStretch)), &eAudioClip); pDocument->saveTextElement("wsola-quick-seek", qtractorDocument::textFromBool( qtractorAudioClip::isStretcherFlag( qtractorTimeStretcher::WsolaQuickSeek)), &eAudioClip); #ifdef CONFIG_LIBRUBBERBAND pDocument->saveTextElement("rubberband-formant", qtractorDocument::textFromBool( qtractorAudioClip::isStretcherFlag( qtractorTimeStretcher::RubberBandFormant)), &eAudioClip); #ifdef CONFIG_LIBRUBBERBAND_R3 pDocument->saveTextElement("rubberband-finer-r3", qtractorDocument::textFromBool( qtractorAudioClip::isStretcherFlag( qtractorTimeStretcher::RubberBandFinerR3)), &eAudioClip); #endif #endif pElement->appendChild(eAudioClip); return true; } // Audio clip export method. bool qtractorAudioClip::clipExport ( ClipExport pfnClipExport, void *pvArg, unsigned long iOffset, unsigned long iLength ) const { qtractorTrack *pTrack = track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; unsigned short iChannels = 0; qtractorAudioBus *pAudioBus = static_cast (pTrack->outputBus()); if (pAudioBus) iChannels = pAudioBus->channels(); if (iChannels < 1) return false; iOffset += clipOffset(); if (iLength < 1) iLength = clipLength(); qtractorAudioBuffer *pBuff = new qtractorAudioBuffer(pTrack->syncThread(), iChannels); pBuff->setOffset(iOffset); pBuff->setLength(iLength); pBuff->setTimeStretch(timeStretch()); pBuff->setPitchShift(pitchShift()); if (!pBuff->open(filename())) { delete pBuff; return false; } unsigned short i; const unsigned int iFrames = pSession->audioEngine()->bufferSizeEx(); float **ppFrames = new float * [iChannels]; for (i = 0; i < iChannels; ++i) ppFrames[i] = new float[iFrames]; const float fGain = clipGain(); unsigned long iFrameStart = 0; while (iFrameStart < iLength) { pBuff->syncExport(); if (pBuff->inSync(iFrameStart, iFrameStart + iFrames)) { const int nread = pBuff->readMux(ppFrames, iFrames, iChannels, 0, fGain); if (nread < 1) break; (*pfnClipExport)(ppFrames, nread, pvArg); iFrameStart += nread; } } for (i = 0; i < iChannels; ++i) delete [] ppFrames[i]; delete [] ppFrames; delete pBuff; return true; } // end of qtractorAudioClip.cpp qtractor-1.5.9/src/PaxHeaders/translations0000644000000000000000000000013215101070305015666 xustar0030 mtime=1761898693.105267712 30 atime=1761898693.093267673 30 ctime=1761898693.105267712 qtractor-1.5.9/src/translations/0000755000175000001440000000000015101070305015733 5ustar00rncbcusersqtractor-1.5.9/src/translations/PaxHeaders/qtractor_ru.ts0000644000000000000000000000013215101070305020660 xustar0030 mtime=1761898693.105267712 30 atime=1761898693.104267708 30 ctime=1761898693.105267712 qtractor-1.5.9/src/translations/qtractor_ru.ts0000644000175000001440000235551315101070305020666 0ustar00rncbcusers QObject Audio: %1 channels, %2 Hz Звук: каналов: %1, %2 Гц (%1 dB) (%1 Дб) (%1 pan) (%1% time stretch) (%1 semitones pitch shift) %1 In Вход %1 %1 Out Выход %1 Audio files (%1) Звуковые файлы (%1) All files (*.*) Ð’Ñе файлы (*.*) %1 (%2) %3 channels, %4 frames, %5 Hz %6 %1 (%2) %3 каналов, %4 выборок, %5 Гц %6 (take %1/%2) (дубль %1/%2) [Mute] Name: %1 Ðазвание: %1 Start: %1 Offset: %2 End: %3 Length: %4 Ðачало: %1 Смещение: %2 Конец: %3 ДлительноÑть: %4 File: %1 Файл: %1 create bus Ñоздание шины update bus обновление шины delete bus удаление шины move bus перемещение шины bus pass-through ÑÐºÐ²Ð¾Ð·Ð½Ð°Ñ ÑˆÐ¸Ð½Ð° bus gain уÑиление шины bus pan панорама шины Cakewalk Instrument Definition File Файл определений инÑтрументов Cakewalk File Файл Date Дата %1 Bank %2 %1 банк %2 %1 - Bank %2 %1 - Банк %2 (format %1) MIDI: (формат %1) MIDI: Channel %1 Канал %1 Track %1 Дорожка %1 , %1 tracks, %2 tpqn , %1 дорожек, %2 tpqn (%1% vol) (%1% громк) MIDI file save: "%1", track-channel: %2. Сохранение файла MIDI: "%1", дорожка-канал: %2. %1 (format %2) %3 tracks, %4 tpqn %5 %1 (формат %2) %3 дорожек, %4 tpqn %5 %1 (format %2) %3 %1 (формат %2) %3 Usage: %1 [options] [session-file] ИÑпользование: %1 [параметры] [файл ÑеÑÑии] Options: Параметры: Set session identification (uuid) УÑтановить идентификатор ÑеÑÑии (uuid) Show help about command line options Показать Ñправку о параметрах командной Ñтроки Show version information Показать верÑию программы Session file (.qtr) [session-file] Option -s requires an argument (uuid). Signed 16-Bit Signed 16-Bit Signed 24-Bit Signed 24-Bit Signed 32-Bit Signed 32-Bit Float 32-Bit Float 32-Bit Float 64-Bit Float 64-Bit SMF Format 0 SMF Format 0 SMF Format 1 SMF Format 1 (Any) (Любой) Activate Включить Aux Send: %1 %1(%2): %3 plugin not found. %1(%2): плагин %3 не найден. add plugin добавление Ñффекта add insert добавление возврата add aux-send добавление внешнего поÑыла add MIDI controller aux-send bus шина внешнего поÑыла aux-send matrix remove plugin удаление Ñффекта move plugin перемещение Ñффекта activate plugin Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ð¸Ñ Ñффекта preset plugin reset plugin plugin program plugin alias dedicated audio outputs ÑобÑтвенный выход Ñффекта direct access param import plugins session loop Ð¿ÐµÑ‚Ð»Ñ ÑеÑÑии session properties ÑвойÑтва ÑеÑÑии Duplex Ð”ÑƒÐ¿Ð»ÐµÐºÑ Output Выход Input Вход None Ðет Beat Такт add track добавление дорожки remove track удаление дорожки duplicate track дублирование дорожки move track перемещение дорожки resize track Ñмена размера дорожки import track импорт дорожки track properties ÑвойÑтва дорожки Track assignment failed: Track: "%1" Input: "%2" Output: "%3" track record запиÑÑŒ дорожки track mute приглушение дорожки track solo Ñолирование дорожки track monitor мониторинг дорожки track gain уÑиление дорожки track pan панорама дорожки track instrument Automation (%1) ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ð¸Ñ (%1) none нет Automation ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ð¸Ñ Unknown ÐеизвеÑтно Product: Продукт: Vendor: Производитель: Manual: Support: Version: ВерÑиÑ: %1 (*.%2) %1 (*.%2) add tempo node добавление Ñмены темпа update tempo node обновление Ñмены темпа remove tempo node удаление Ñмены темпа move tempo node перемещение Ñмены темпа add marker добавление маркера update marker обновление маркера remove marker удаление маркера add key signature update key signature remove key signature move marker перемещение маркера change time-sig. session punch врезка в ÑеÑÑии %1 Monitor Insert Send/Return pseudo-plugin (Audio) Insert Send/Return pseudo-plugin (MIDI) Send Gain Dry Gain Wet Gain Aux Send (Audio) Aux Send pseudo-plugin (Audio) Aux Send pseudo-plugin (MIDI) (none) (нет) %1 (Audio) %1 (MIDI) set controller reset controller (default) (по умолчанию) %1 Hz %1 Гц slave ведомый %1 (%2) %1 (%2) step input overdub %1 Volume ГромкоÑть %1 %1 Gain %1 Pan take %1 дубль %1 reset takes clip save clip unlink clip tool %1 clip record запиÑÑŒ клипа automation select automation mode режим автоматихации automation play automation record automation logarithmic automation color цвет автоматизации automation play all automation record all automation edit automation clear automation clear all automation edit list Copyright: ÐвторÑкие права: Project: Проект: Select plug-in's editor (GUI): External X11 X11 X11 (native) Gtk2 Gtk2 Gtk2 (native) Qt4 Qt4 Qt5 Qt5 Other Другое Don't ask this again Больше не Ñпрашивать plugin parameters Параметры лпагина Open File lv2_ui_request_parameter Открыть файл Author: Ðвтор: %1: Automation/curve file not found. Name: Ðазвание: Category: КатегориÑ: Categories: Категории: %1 Record %1 Mute %1 Solo MIDI Controller: %1, %2, %3 Control (MIDI) MIDI Controller Send pseudo-plugin Value Значение qtractorAudioIOMatrixForm Aux-Send I/O Matrix Warning Предупреждение Some settings have been changed. Do you want to apply the changes? Ðекоторые параметры изменилиÑÑŒ. Ð’Ñ‹ хотите применить изменениÑ? qtractorAudioListView Name Ðазвание Ch К Frames Выборок Rate ЧаÑтота Time Ð’Ñ€ÐµÐ¼Ñ Path Путь Open Audio Files Открыть звуковые файлы %1: Audio file not found. %1: звуковой файл не найден. qtractorAudioMixerMeter Gain (dB) УÑиление (Дб) dB Дб Pan: %1 Gain: %1 dB УÑиление: %1 Дб qtractorBusForm Audio Звук MIDI MIDI Bus Шина Warning Предупреждение Some settings have been changed. Do you want to apply the changes? Ðекоторые параметры изменилиÑÑŒ. Ð’Ñ‹ хотите применить изменениÑ? About to remove bus: "%1" (%2) Are you sure? Будет удалена шина: "%1" (%2) Ð’Ñ‹ уверены? Some settings have been changed. Do you want to discard the changes? Ðекоторые параметры изменилиÑÑŒ. Ð’Ñ‹ хотите применить изменениÑ? Move &Up &Выше Move &Down &Ðиже (No instrument) (Ðет инÑтрумента) (none) (нет) (1 item) (%1 items) &Create &Создать &Update О&бновить &Delete &Удалить Bus list СпиÑок шин Buses Шины Ch К Mode Режим Properties СвойÑтва &Name: Ð&азвание: Bus name Ðазвание шины &Mode: &Режим: Bus mode Режим работы шины Input Вход Output Выход Duplex Ð”ÑƒÐ¿Ð»ÐµÐºÑ Bus monitor (pass-through) M&onitor (pass-through) &Мониторинг (pass-through) Cha&nnels: &Каналов: Audio channels КоличеÑтво звуковых каналов Audio auto-connect &Auto connect &ÐвтоÑоединение MIDI Instrument name Ðазвание MIDI-инÑтрумента MIDI SysEx setup ÐаÑтройка MIDI SysEx SysE&x... SysE&x... Input Plugins Эффекты на входе Input bus plugins Эффекты шины на входе Add input plugin Добавить Ñффект на вход &Add... &Добавить Remove input plugin Удалить Ñффект на входе &Remove &Удалить Move input plugin up ПоднÑть Ñффект в ÑпиÑке &Up &Выше Move input plugin down ОпуÑтить Ñффект в ÑпиÑке &Down &Ðиже Output Plugins Эффекты на выходе Output bus plugins Эффекты шины на выходе Add output plugin Добавить Ñффект на выход Remove output plugin Удалить Ñффект на выходе Move output plugin up ПоднÑть Ñффект в ÑпиÑке Move output plugin down ОпуÑтить Ñффект в ÑпиÑке Move bus up towards the top U&p &ПоднÑть Move bus down towards the bottom Do&wn Оп&уÑтить Create bus Создать шину Update bus Обновить шину Delete bus Удалить шину Close this dialog Закрыть Ñтот диалог Close Закрыть qtractorClientListView Readable Clients / Output Ports Порты выхода Writable Clients / Input Ports Порты входа qtractorClipForm &Gain: &УÑиление: dB Дб &Volume: &ГромкоÑть: % % Linear Линейное Quadratic 1 Quadratic 2 Quadratic 3 Cubic 1 Cubic 2 Cubic 3 new clip Ñоздание клипа edit clip правка клипа Warning Предупреждение Some settings have been changed. Do you want to apply the changes? Ðекоторые параметры изменилиÑÑŒ. Ð’Ñ‹ хотите применить изменениÑ? Audio Звук MIDI MIDI MIDI files (*.%1 *.smf *.midi) Файлы MIDI (*.%1 *.smf *.midi) All files (*.*) Ð’Ñе файлы (*.*) %1 Clip File &Name: &Ðазвание: Clip name Ðазвание клипа &File: &Файл: Clip filename Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° клипа Browse for clip file Указать файл клипа Track/&Channel: Дорожка/&Канал: Clip track/channel Дорожка/канал клипа Clip gain/volume УÑиление/громкоÑть клипа Parameters Параметры Clip start Ðачало клипа Clip offset Смещение клипа &Panning: &Панорама: Clip length ДлительноÑть клипа Offs&et: С&мещение: &Length: &ДлительноÑть: &Start: &Ðачало: Clip Клип Forma&t: &Формат: Time display format Формат показа времени Frames Выборки Time Ð’Ñ€ÐµÐ¼Ñ BBT BBT Fade In/Out Плавное нараÑтание и затухание Ñигнала Fade &In: &ÐараÑтание: Clip fade-in length ДлительноÑть нараÑÑ‚Ð°Ð½Ð¸Ñ Ñигнала клипа Clip fade-in type Тип нараÑÑ‚Ð°Ð½Ð¸Ñ Ñигнала клипа Fade &Out: &Затухание: Clip fade-out length ДлительноÑть Ð·Ð°Ñ‚ÑƒÑ…Ð°Ð½Ð¸Ñ Ñигнала клипа Clip fade-out type Тип Ð·Ð°Ñ‚ÑƒÑ…Ð°Ð½Ð¸Ñ Ñигнала клипа Ti&me Stretch: &РаÑÑ‚Ñгивание во времени: Clip time-stretch percentage РаÑÑ‚Ñгивание клипа во времени в процентах Pitch S&hift: Ð’&Ñ‹Ñота тона: Clip pitch-shift in semitones Ð’Ñ‹Ñота тона клипа в полутонах semitones полутонов Whether to use WSOLA time-stretching ИÑпользовать ли WSOLA Ð´Ð»Ñ Ñ€Ð°ÑÑ‚ÑÐ³Ð¸Ð²Ð°Ð½Ð¸Ñ Ð²Ð¾ времени &WSOLA time-stretching Р&аÑÑ‚Ñгивание во времени при помощи WSOLA Whether to use RubberBand formant preserve RubberBand &formant preserve Whether to apply WSOLA quick seek time-stretching WSOLA quic&k seek &БыÑÑ‚Ñ€Ð°Ñ Ð¿Ñ€Ð¾ÐºÑ€ÑƒÑ‚ÐºÐ° Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ WSOLA Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine &Mute &Приглушение qtractorConnect Connect Соединить Disconnect Разъединить Disconnect All Разъединить вÑе Refresh Обновить qtractorConnectForm (All) (Ð’Ñе) Connections Ð¡Ð¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Audio Звук Select output client/ports Выберите порты клиента наружу Select input client/ports Выберите порты клиента внутрь Connect currently selected ports Соединить выбранные ÑÐµÐ¹Ñ‡Ð°Ñ Ð¿Ð¾Ñ€Ñ‚Ñ‹ &Connect &Соединить Disconnect currently selected ports РаÑÑоединить выбранные ÑÐµÐ¹Ñ‡Ð°Ñ Ð¿Ð¾Ñ€Ñ‚Ñ‹ &Disconnect &РаÑÑоединить Disconnect all currently connected ports РаÑÑоединить вÑе Ñоединённые ÑÐµÐ¹Ñ‡Ð°Ñ Ð¿Ð¾Ñ€Ñ‚Ñ‹ Disconnect &All РаÑÑоединить &вÑе Refresh current connections view Обновить отображение текущих Ñоединений &Refresh О&бновить MIDI MIDI qtractorConnections Connections Ð¡Ð¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ qtractorEditRangeForm Range ОблаÑть Selection range ОблаÑть Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ &Selection Ð’&ыделение Loop range ОблаÑть петли &Loop &ÐŸÐµÑ‚Ð»Ñ Punch range ОблаÑть врезки &Punch &Врезка Edit range Изменить облаÑть &Edit &Изменить Custom range Ð—Ð°ÐºÐ°Ð·Ð½Ð°Ñ Ð¾Ð±Ð»Ð°Ñть &Custom &Ð—Ð°ÐºÐ°Ð·Ð½Ð°Ñ St&art: &Ðачало: Clip start Ðачало клипа En&d: &Конец: Clip offset Смещение клипа Te&mpo Map Кар&та темпа &Format &Формат Time display format Формат показа времени: Time Ð’Ñ€ÐµÐ¼Ñ BBT BBT Frames Options Параметры Apply to clips in range Применить к клипам в диапазоне Cl&ips &Клипы A&utomation &ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ð¸Ñ Apply to Automation nodes in range Edit Range Изменить диапазон Apply to Loop points in range L&oop П&ÐµÑ‚Ð»Ñ Apply to Punch In/Out points in range Pu&nch Ð’&резка Mar&kers &Маркеры Apply to Tempo Map nodes in range Apply to location Markers in range qtractorExportClipForm %1 %2 Clips qtractorExportForm Audio звуковых данных MIDI MIDI Export %1 File ЭкÑпорт %1 файла MIDI files (*.%1 *.smf *.midi) Файлы MIDI (*.%1 *.smf *.midi) All files (*.*) Ð’Ñе файлы (*.*) &File: &Файл: Export file name Ð˜Ð¼Ñ ÑкÑпортируемого файла Browse export file name Указать Ð¸Ð¼Ñ ÐºÐ¾Ð½ÐµÑ‡Ð½Ð¾Ð³Ð¾ файла File &type: Т&ип файла: Audio file type to use on export Sample &format: &Формат ÑÑмпла: Audio sample format to use on export &Quality: &КачеÑтво: Audio compression quality to use on export File &format: &Формат файлов: MIDI file format to use on export Range ОблаÑть Session range ОблаÑть ÑеÑÑии &Session &СеÑÑÐ¸Ñ Loop range Ð’ÑÑŽ петлю &Loop &ÐŸÐµÑ‚Ð»Ñ Punch range Ð’ÑÑŽ врезку &Punch &Врезка Edit range ОблаÑть Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ &Edit &Изменить Custom range Другую облаÑть &Custom &Ð”Ñ€ÑƒÐ³Ð°Ñ St&art: &Ðачало: En&d: &Конец: Outputs Выходы Output bus names ÐÐ°Ð·Ð²Ð°Ð½Ð¸Ñ ÑˆÐ¸Ð½ выхода Time Ð’Ñ€ÐµÐ¼Ñ BBT BBT Whether to add/import new track(s) with export result Импортировать ли Ñозданные файлы обратно в проект &Add new track(s) &Добавить дорожки Format Формат Export ЭкÑпортировать Custom start Заказное начало Custom end Заказной конец Time display format Формат показа времени Frames Выборки qtractorExportTrackForm %1 %2 Tracks Warning Предупреждение The file already exists: "%1" Do you want to replace it? Такой файл уже ÑущеÑтвует: "%1" Ð’Ñ‹ хотите его заменить? Audio file export: "%1" started... ЭкÑпорт звукового файла: "%1" начат... Audio file export: "%1" complete. ЭкÑпорт звукового файла "%1" завершен. Audio file export: "%1" failed. ЭкÑпорт звукового файла: не удалоÑÑŒ ÑкÑпортировать "%1" MIDI file export: "%1" started... ЭкÑпорт файла MIDI: "%1" начат... MIDI file export: "%1" complete. ЭкÑпорт файла MIDI: "%1" завершен... MIDI file export: "%1" failed. ЭкÑпорт файла MIDI: не удалоÑÑŒ ÑкÑпортировать "%1" qtractorFileListView New Group ÐÐ¾Ð²Ð°Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð° Warning Предупреждение About to remove %1 file item(s). Are you sure? About to remove %1 item: "%2" Are you sure? Будет удален %1 файл: "%2" Ð’Ñ‹ уверены? group группа file файл qtractorFileSystem &Home &Домой &Up &Выше Al&l Files Ð’Ñ&е файлы &Session &СеÑÑÐ¸Ñ &Audio &Ðудио &MIDI &MIDI H&idden С&крытые &Play Ð’&оÑпроизведение File System Ð¤Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема qtractorFiles Audio Звук MIDI MIDI Play file ВоÑпроизвеÑти файл Add &Files... &Добавить файлы... Cu&t &Вырезать &Copy &Копировать Ctrl+X Ctrl+X Ctrl+C Ctrl+C Ctrl+V Ctrl+V New &Group... Создать &группу… &Paste Ð’ÑÑ‚&авить Re&name &Переименовать &Remove &Удалить Pla&y Ð’&оÑпроизвеÑти Cl&eanup О&чиÑтить Del Del Files Файлы MIDI Files Файлы MIDI Audio Files Звуковые файлы qtractorInstrumentForm Import Instrument Files Импортировать файлы инÑтрументов Instrument files (*.%1 *.sf2 *.sf3 *.midnam) Файлы инÑтрументов (*.%1 *.sf2 *.sf3 *.midnam) Instrument files (*.%1) Файлы инÑтрументов (*.%1) Export Instrument File ЭкÑпортировать файлы инÑтрументов All files (*.*) Ð’Ñе файлы (*.*) Warning Предупреждение The instrument file already exists: "%1" Do you want to replace it? Такой файл инÑтрументов уже ÑущеÑтвует: "%1" Ð’Ñ‹ хотите его заменить? Instrument settings have been changed. Do you want to apply the changes? Параметры инÑтрумента поменÑлиÑÑŒ. Применить Ñти изменениÑ? Patch Names for Banks ÐÐ°Ð·Ð²Ð°Ð½Ð¸Ñ Ð¿Ð°Ñ‚Ñ‡ÐµÐ¹ Ð´Ð»Ñ Ð±Ð°Ð½ÐºÐ¾Ð² Controller Names = %1 ÐÐ°Ð·Ð²Ð°Ð½Ð¸Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»Ð»ÐµÑ€Ð¾Ð² = %1 RPN Names = %1 ÐÐ°Ð·Ð²Ð°Ð½Ð¸Ñ RPN = %1 NRPN Names = %1 ÐÐ°Ð·Ð²Ð°Ð½Ð¸Ñ NRPN = %1 Bank Select Method = %1 СпоÑоб выбора банка = %1 Patch Names ÐÐ°Ð·Ð²Ð°Ð½Ð¸Ñ Ð¿Ð°Ñ‚Ñ‡ÐµÐ¹ Note Names ÐÐ°Ð·Ð²Ð°Ð½Ð¸Ñ Ð½Ð¾Ñ‚ Controller Names ÐÐ°Ð·Ð²Ð°Ð½Ð¸Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»Ð»ÐµÑ€Ð¾Ð² RPN Names ÐÐ°Ð·Ð²Ð°Ð½Ð¸Ñ RPN NRPN Names ÐÐ°Ð·Ð²Ð°Ð½Ð¸Ñ NRPN Bank Select Methods СпоÑобы выбора банка %1 = %2 %1 = %2 Based On = %1 ОÑновано на = %1 Normal Обычный Bank MSB Банк MSB Bank LSB Банк LSB Patch Патч Unknown ÐеизвеÑтно Instruments ИнÑтрументы Files Файлы Path РаÑположение 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 &Ðиже Export to instrument file ЭкÑпортировать в файл инÑтрументов Close this dialog Закрыть Ñтот диалог Close Закрыть E&xport... &ЭкÑпортировать... qtractorInstrumentMenu (None) (нет) qtractorMainForm Snap/beat Прилипание к долÑм Track Дорожка Current track name Ðазвание текущей дорожки MOD ИЗМ Session modification state СоÑтоÑние измененноÑти ÑеÑÑии REC ЗÐП Session record state СоÑтоÑние запиÑываемоÑти ÑеÑÑии MUTE ТИХО Session muting state СоÑтоÑние приглушенноÑти ÑеÑÑии SOLO СОЛО Session soloing state СоÑтоÑние ÑÐ¾Ð»Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² ÑеÑÑии LOOP ПЕТЛЯ Session looping state СоÑтоÑние Ð·Ð°Ñ†Ð¸ÐºÐ»Ð¸Ð²Ð°Ð½Ð¸Ñ Ð² ÑеÑÑии Session total time ÐžÐ±Ñ‰Ð°Ñ Ð´Ð»Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñть ÑеÑÑии Session sample rate ЧаÑтота ÑÑÐ¼Ð¿Ð»Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ÑеÑÑии Could not set default session directory: %1 Sorry. Ðе удалоÑÑŒ уÑтановить каталог Ñ ÑеÑÑией по умолчанию: %1 Извините. Ready Готово Untitled%1 Session files (*.%1 *.%2 *.%3) Файлы ÑеÑÑий (*.%1 *.%2 *.%3) Archive files (*.%1) Ðрхивные файлы (*.%1) About to remove archive directory: "%1" Are you sure? Don't ask this again Больше не Ñпрашивать The directory already exists: "%1" Do you want to replace it? Opening "%1"... ОткрываетÑÑ "%1"... Saving "%1"... СохранÑетÑÑ "%1"... About to clear automation: "%1" Are you sure? About to clear all automation: "%1" Are you sure? take range LV2 Plug-in support disabled. Поддержка плагинов LV2 отключена. LV2 Plug-in UI support disabled. LV2 Plug-in UI support (libsuil) disabled. LV2 Plug-in support (liblilv) disabled. Current time (play-head) Session XRUN state Session buffer size All files (*.*) Ð’Ñе файлы (*.*) Backup session: "%1" as "%2". Сделать резервную копию ÑеÑÑии: "%1" как "%2". Could not backup existing session: %1 as %2 Sorry. Ðе удалоÑÑŒ Ñделать резервную копию ÑущеÑтвующей ÑеÑÑии: %1 как %2 Извините! A directory with same name already exists: "%1" This directory will be replaced, erasing all its current data, when opening and extracting this archive in the future. Do you want to continue? The directory is an extracted archive: "%1" This directory will be removed, erased from all its current data, when closing this session. Do you want to continue? Oops! Looks like it crashed or did not close properly last time it was run... however, an auto-saved session file exists: "%1" Do you want to crash-recover from it? Player panic! Beat-detection support (libaubio) disabled. VST2 Plug-in support disabled. Поддержка плагинов VST3 отключена. {2 ?} VST3 Plug-in support disabled. Поддержка плагинов VST3 отключена. CLAP Plug-in support disabled. Поддержка плагинов CLAP отключена. LV2 Plug-in External UI support disabled. Поддержка External UI в плагинах LV2 отключена. LV2 Plug-in MIDI/Atom support disabled. Поддержка MIDI/Atom в плагинах LV2 отключена. LV2 Plug-in Worker/Schedule support disabled. Поддержка Worker/Schedule в плагинах LV2 отключена. LV2 Plug-in State support disabled. Поддержка State в плагинах LV2 отключена. LV2 plug-in State Make Path support (DANGEROUS) enabled. Поддержка State Make Path в плагинах LV2 включена (ОПÐСÐО). LV2 Plug-in Programs support disabled. Поддержка Programs в плагинах LV2 отключена. LV2 Plug-in MIDNAM support disabled. Поддержка MIDNAM в плагинах LV2 отключена. LV2 Plug-in Presets support disabled. Поддержка Presets в плагинах LV2 отключена. LV2 Plug-in Patch support disabled. Поддержка Patch в плагинах LV2 отключена. LV2 Plug-in Time/position support disabled. Поддержка Time/Position в плагинах LV2 отключена. LV2 Plug-in Options support disabled. Поддержка Options в плагинах LV2 отключена. LV2 Plug-in Buf-size support disabled. Поддержка Buf-size в плагинах LV2 отключена. LV2 Plug-in UI Touch interface support disabled. Поддержка UI Touch Interface в плагинах LV2 отключена. LV2 Plug-in UI Idle interface support disabled. Поддержка UI Idle Interface в плагинах LV2 отключена. LV2 Plug-in UI Show interface support disabled. Поддержка UI Show Interface в плагинах LV2 отключена. LV2 Plug-in UI GTKMM2 native support disabled. JACK Session support disabled. JACK Latency support disabled. JACK Metadata support disabled. NSM support disabled. Using: Qt %1 ИÑпользует: Qt %1 XRUN &Hold &Linear &Spline Take %1 Дубль %1 None Ðет The audio engine has been shutdown. Make sure the JACK audio server (jackd) is up and running and then restart session. Звуковой движок выключен. УбедитеÑÑŒ в том, что звуковой Ñервер JACK (jackd) запущен и функционирует, а затем начните ÑеÑÑию заново. STOP СТОП PLAY FFWD REW REC ON ЗÐП ВКЛ REC OFF ЗÐП ВЫКЛ RESET СБРОС LOCATE %1 SHUTTLE %1 STEP %1 ШÐГ %1 TRACK RECORD %1 %2 TRACK MUTE %1 %2 TRACK SOLO %1 %2 Unknown sub-command Not implemented Ðе реализовано MIDI CTL: %1, Channel %2, Param %3, Value %4 %1 BPM (track %1, gain %2) (дорожка %1, уÑиление %2) Audio self-connection detected! In general, connecting an output bus (or insert send), directly into any input bus (or insert return), is not advisable. It often doesn't work, if at all. (track %1, panning %2) (дорожка %1, панорамирование %2) START CONTINUE ПРОДОЛЖИТЬ SONGPOS %1 New session: "%1". ÐÐ¾Ð²Ð°Ñ ÑеÑÑиÑ: "%1". Session files (*.%1 *.%2) Файлы ÑеÑÑий (*.%1 *.%2) Template files (*.%1) Файлы шаблонов (*.%1) Open Session Открыть ÑеÑÑию Save Session Сохранить ÑеÑÑию Warning Предупреждение The file already exists: "%1" Do you want to replace it? Такой файл уже ÑущеÑтвует: "%1" Ð’Ñ‹ хотите его заменить? The current session has been changed: "%1" Do you want to save the changes? ÐÐºÑ‚Ð¸Ð²Ð½Ð°Ñ ÑеÑÑÐ¸Ñ Ð±Ñ‹Ð»Ð° изменена: "%1" Ð’Ñ‹ хотите Ñохранить изменениÑ? Save Сохранить Session closed. СеÑÑÐ¸Ñ Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ð°. Session could not be loaded from "%1". Sorry. Ðе удалоÑÑŒ загрузить ÑеÑÑию из "%1". Извините! Open session: "%1". Открыть ÑеÑÑию: "%1". Session could not be saved to "%1". Sorry. Ðе удалоÑÑŒ Ñохранить ÑеÑÑию в файл "%1". Извините. Save session: "%1". Сохранить ÑеÑÑию: "%1". session ÑеÑÑÐ¸Ñ or или program программа Information Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Some settings may be only effective next time you start this %1. Ðекоторые Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð²Ð¾Ð·Ñ‹Ð¼ÐµÑŽÑ‚ Ñилу только при Ñледующем запуÑке %1. Version ВерÑÐ¸Ñ Debugging option enabled. Отладка включена Set current snap to %1 Ogg Vorbis (libvorbis) file support disabled. Поддержка файлов Ogg Vorbis (libvorbis) отключена. MPEG-1 Audio Layer 3 (libmad) file support disabled. Поддержка MPEG-1 Audio Layer 3 (libmad) отключена. Sample-rate conversion (libsamplerate) disabled. Поддержка Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ‡Ð°Ñтоты ÑÑÐ¼Ð¿Ð»Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ (libsamplerate) отключена. Pitch-shifting support (librubberband) disabled. Поддержка Ñмена выÑоты тона (librubberband) отключена. OSC service support (liblo) disabled. Поддержка Ñлужбы OSC (liblo) отключена. LADSPA Plug-in support disabled. Поддержка Ñффектов LADSPA отключена. DSSI Plug-in support disabled. Поддержка Ñффектов DSSI отключена. LV2 Plug-in MIDI/Event support (DEPRECATED) enabled. LV2 Plug-in State Files support disabled. LV2 Plug-in UI Request-value support disabled. LV2 Plug-in UI GTK2 native support disabled. LV2 Plug-in UI X11 native support disabled. Website Веб-Ñайт This program is free software; you can redistribute it and/or modify it Эта программа ÑвлÑетÑÑ Ñвободной; вы можете раÑпроÑтранÑть и/или under the terms of the GNU General Public License version 2 or later. изменÑть ее на уÑловиÑÑ… GNU GPL верÑии 2 или новее. About О программе record clip запиÑÑŒ клипа [modified] [изменен] Session started. СеÑÑÐ¸Ñ Ð½Ð°Ñ‡Ð°Ñ‚Ð°. The audio/MIDI engine could not be started. Make sure the JACK/Pipewire audio service and the ALSA Sequencer kernel module (snd-seq-midi) are up and running and then restart the session. The original session sample rate (%1 Hz) is not the same as the current audio engine (%2 Hz). Saving and reloading from a new session file is highly recommended. ИÑÑ…Ð¾Ð´Ð½Ð°Ñ Ñ‡Ð°Ñтота ÑÑÐ¼Ð¿Ð»Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ ÑеÑÑии (%1 Гц) не Ñовпадает Ñ Ñ‚ÐµÐºÑƒÑ‰ÐµÐ¹ чаÑтотой звукового движка (%2 Гц). ÐаÑтоÑтельно рекомендуетÑÑ Ñохранить ÑеÑÑию и заново открыть ее из нового файла. The following issues were detected: %1 Saving into another session file is highly recommended. Error Ошибка XRUN(%1 skipped) XRUN(%1): some frames might have been lost. Audio connections change. Смена звуковых Ñоединений Don't show this again MIDI connections change. Смена Ñоединений MIDI Playing ended. ВоÑпроизведение завершено The audio engine buffer size has changed, increased from %1 to %2 frames/period. Reloading the current session file is highly recommended. TRACK MONITOR %1 %2 Playing "%1"... ВоÑпроизводитÑÑ "%1"... &Track &Дорожка &State &СоÑтоÑние &Navigate &ÐÐ°Ð²Ð¸Ð³Ð°Ñ†Ð¸Ñ Impor&t Tracks &Импортировать дорожки E&xport Tracks &ЭкÑпортировать дорожки &View &Вид &Toolbars &Панели инÑтрументов &Windows &Окна &Zoom МаÑш&таб S&nap При&липание T&ransport &ТранÑпорт &Help &Справка &Edit &Правка Select &Mode &Режим Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ &Select Ð’Ñ‹&делить T&ools &ИнÑтрументы &File &Файл Open &Recent Открыть &недавние I&nsert Ð’&Ñтавить Remo&ve &Удалить Mo&ve Пере&меÑтить &Height &Ð’Ñ‹Ñота M&ode &Режим A&utomation Ðв&Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ð¸Ñ Ta&ke &Дубль &New &Создать New Создать New session Создать ÑеÑÑию New session file Создать файл новой ÑеÑÑии Ctrl+N Ctrl+N &Open... &Открыть... Open Открыть Open session Открыть ÑеÑÑию Open session from file Открыть ÑеÑÑию из файла Ctrl+O Ctrl+O &Save &Сохранить Save session Сохранить ÑеÑÑию Save session to file Сохранить ÑеÑÑию в файл Ctrl+S Ctrl+S Save &As... Сохранить &как Save As Сохранить как Save as Сохранить как Save current session with another file name Сохранить активную ÑеÑÑию под другим именем файла &Properties... С&войÑтва... Session Properties СвойÑтва ÑеÑÑии Session properties СвойÑтва ÑеÑÑии Edit current session properties Изменить ÑвойÑтва активной ÑеÑÑии F2 F2 E&xit Ð’&ыход Exit Выход Exit this application program Завершить работу Ñ Ñтой программой &Undo &Отменить Undo Отменить Undo last action Отменить поÑледнее дейÑтвие Ctrl+Z Ctrl+Z &Redo Ве&рнуть Redo Вернуть Redo last action Вернуть отмененное дейÑтвие Ctrl+Shift+Z Ctrl+Shift+Z Cu&t &Вырезать Cut Вырезать Cut selection to clipboard Вырезать выделение в буфер обмена Ctrl+X Ctrl+X &Copy &Копировать Copy Копировать Copy selection to clipboard Скопировать выделение в буфер обмена Ctrl+C Ctrl+C &Paste Ð’ÑÑ‚&авить Paste Ð’Ñтавить Paste clipboard contents Ð’Ñтавить Ñодержимое буфера обмена Ctrl+V Ctrl+V Past&e Repeat... Ð’Ñтав&ить Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð¾Ð¼ Paste Repeat Ð’Ñтавить Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð¾Ð¼ Paste repeat Ð’Ñтавить Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð¾Ð¼ Paste/repeat clipboard contents Ð’Ñтавить Ñодержимое буфера обмена Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð¾Ð¼ Ctrl+Shift+V Ctrl+Shift+V &Delete &Удалить Delete Удалить Delete selection Удалить выделение Del Del &Clip &Клип Clip Клип Select clip Выбрать клип Clip selection mode режим Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ ÐºÐ»Ð¸Ð¿Ð¾Ð² &Range &ОблаÑть Range ОблаÑть Select range Выделить облаÑть Range selection mode Режим Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¾Ð±Ð»Ð°Ñти R&ectangle &ПрÑмоугольник Rect Select rectangle Rectangular selection mode ПрÑмоугольный режим Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ &Automation &ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ð¸Ñ Automation ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ð¸Ñ Automation edit mode Режим правки автоматизации &None &СнÑть выделение Select None СнÑть выделение Select none СнÑть выделение Mark all as unselected СнÑть выделение Ñо вÑех Ñлементов Ctrl+Shift+A Ctrl+Shift+A &Invert &Инвертировать Select Invert Инвертировать выделение Select invert Инвертировать выделение Invert selection Инвертировать выделение Ctrl+I Ctrl+I Select Range Выделить облаÑть Mark range as selected Пометить облаÑть выделенной Ctrl+R Ctrl+R Select Track Выделить дорожку Select track Выделить дорожку Mark track as selected Пометить дорожку выделенной Ctrl+T Ctrl+T Trac&k Range Select Track Range Select track range Mark track range as selected Ctrl+Shift+R &All &Ð’ÑÑ‘ Select All Выделить вÑÑ‘ Select all Выбрать вÑÑ‘ Mark all as selected Пометить вÑÑ‘ выделенным Ctrl+A Ctrl+A &New... &Создать... New Clip Создать клип New clip Создать клип Create new clip Создать новый клип &Edit... &Изменить... Edit Clip Изменить клип Edit clip Изменить клип Edit current clip Изменить текущий клип F4 F4 &Split &Разделить Split Clip Разделить клип Split clip Разделить клип Split current clip at playhead Разделить клип по курÑору воÑÐ¿Ñ€Ð¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Normalize Clip Ðормализовать клип Normalize clip Ðормализовать клип Normalize current clip (gain/volume) ВыровнÑть уÑиление/громкоÑть выбранного клипа Quantize Clip Отквантовать клип E&xport... &ЭкÑпортировать... Export Clip ЭкÑпортировать клип Export clip ЭкÑпортировать клип Export current clip to file ЭкÑпортировать клип в файл &Add Track... &Добавить дорожку... Instrum&ent И&нÑтрумент &Range... &Диапазон... Remove Range Удалить облаÑть Remove range Удалить облаÑть Remove range as selected Удалить выбранную облаÑть Ctrl+Del Ctrl+Del Remove Track Range Remove track range Remove track range as selected Ctrl+Shift+Del Ctrl+Shift+Del Sp&lit Ра&зделить Split Selection Разделить выделение Split selection Разделить выделение Split current selection Разделить активное выделение Ctrl+Y Ctrl+Y Add Track Добавить дорожку Add track Добавить дорожку Add a new track to session Добавить новую дорожку в ÑеÑÑию Shift+Ins Shift+Ins &Remove Track &Удалить дорожку Remove Track Удалить дорожку Remove track Удалить дорожку Remove current track from session Удалить текущую дорожку из ÑеÑÑии Shift+Del Shift+Del &Duplicate Track Про&дублировать дорожку Duplicate Track Продублировать дорожку Duplicate track Продублировать дорожку Duplicate current track Создать копии текущей дорожки Track &Properties... С&войÑтва дорожки... Track Properties СвойÑтва дорожки Track properties СвойÑтва дорожки Edit current track properties Изменить ÑвойÑтва текущей дорожки Shift+F2 Shift+F2 &Inputs &Входы Track Inputs Входы дорожки Track inputs Входы дорожки Show current track input bus connections Показать активные ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ ÑˆÐ¸Ð½Ð¾Ð¹ входа &Outputs Ð’&ыходы Track Outputs Выходы дорожки Track outputs Выходы дорожки Show current track output bus connections Показать активные ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ ÑˆÐ¸Ð½Ð¾Ð¹ выхода &Record &ЗапиÑÑŒ Record Track ЗапиÑать дорожку Record track ЗапиÑать дорожку Arm current track for recording Включить режим запиÑи Ð´Ð»Ñ Ñтой дорожки &Mute &Приглушение Mute Track Приглушить дорожку Mute track Приглушить дорожку Mute current track Приглушить текущую дорожку &Solo &Солирование Solo Track Сделать дорожку Ñолирующей Solo track Сделать дорожку Ñолирующей Solo current track Сделать текущую дорожку Ñолирующей M&onitor &Мониторинг Monitor Track Включить мониторинг дорожки Monitor track Включить мониторинг дорожки Monitor current track Включить мониторинг текущей дорожки &First П&ÐµÑ€Ð²Ð°Ñ First Track К первой дорожке First track К первой дорожке Make current the first track Перейти к первой в ÑпиÑке дорожке &Previous &ÐŸÑ€ÐµÐ´Ñ‹Ð´ÑƒÑ‰Ð°Ñ Previous Track К предыдущей дорожке Previous track К предыдущей дорожке Make current the previous track Перейти к предыдущей в ÑпиÑке дорожке &Next &Ð¡Ð»ÐµÐ´ÑƒÑŽÑ‰Ð°Ñ Next Track К Ñледующей дорожке Next track К Ñледующей дорожке Make current the next track Перейти к Ñледующей в ÑпиÑке дорожке &Last П&оÑледнÑÑ Last Track К поÑледней дорожке Last track К поÑледней дорожке Make current the last track Перейти к поÑледней в ÑпиÑке дорожке N&one None Track None track None current track &Top Ð’ Ñамый &верх Move Top ПеремеÑтить в Ñамый верх Move top ПеремеÑтить в Ñамый верх Move current track to top ПеремеÑтить текущую дорожку в Ñамый верх &Up &Выше Move Up ПеремеÑтить выше Move up ПеремеÑтить выше Move current track up ПеремеÑтить текущую дорожку выше &Down &Ðиже Move Down ПеремеÑтить ниже Move down ПеремеÑтить ниже Move current track down ПеремеÑтить текущую дорожку ниже &Bottom Ð’ Ñ&амый низ Move Bottom ПеремеÑтить в Ñамый низ Move bottom ПеремеÑтить в Ñамый низ Move current track to bottom ПеремеÑтить текущую дорожку в Ñамый низ &Minimize Minimize Height Minimize height Minimize track height Auto Monitor Ðвтомониторинг Auto monitor Ðвтомониторинг Auto-monitor current track Ðвтомониторинг текущей дорожки F6 F6 Auto Dea&ctivate Ðвто&выключение плагинов Auto Deactivate Ðвтовыключение плагинов Auto-deactivate plugins Ðвтовыключение плагинов Auto-deactivate plugins not producing sound ÐвтоматичеÑки выключать плагины, которые не производÑÑ‚ звук Shift+F6 Shift+F6 &Audio... &Звук... Inport Audio File Импортировать звуковой файл Import Audio file Импортировать звуковой файл Import tracks from Audio file Импортировать дорожки из звукового файла &MIDI... &MIDI... Import MIDI File Импортировать файл MIDI Import MIDI file Импортировать файл MIDI Import tracks from MIDI file Импортировать дорожки из файла MIDI Export Audio File ЭкÑпортировать звуковой файл Export Audio file ЭкÑпортировать звуковой файл Export tracks to Audio file ЭкÑпортировать дорожки в звуковой файл Export MIDI File ЭкÑпортировать файл MIDI Export MIDI file ЭкÑпортировать файл MIDI Export tracks to MIDI file ЭкÑпортировать дорожки в файл MIDI Log&arithmic Automation logarithmic Automation curve logarithmic scale C&olor... Automation color Automation curve color &Lock &Заблокировать Automation lock Lock automation curve Automation playback Playback automation curve Automation record Record automation curve &Clear О&чиÑтить Automation clear Clear automation curve Loc&k All За&блокировать вÑе Automation lock all Lock all automation curves Play &All &ВоÑпроизводить вÑе Automation playback all Playback all automation curves Rec&ord All &ЗапиÑывать вÑе Automation record all Record all automation curves C&lear All О&чиÑтить вÑе Automation clear all Clear all automation curves Mute Clip Mute clip Mute current clip &Unlink &Разгруппировать Unlink Clip Unlink clip Unlink current clip Recor&d &ЗапиÑать Record Clip Record clip Record current clip (overdub) Normali&ze Ðорм&ировать T&empo ramp... &ÐŸÐ»Ð°Ð²Ð½Ð°Ñ Ñмена темпа… Tempo ramp Clip Tempo ramp clip events Tempo ramp current MIDI clip events &Tempo Adjust... &ÐšÐ¾Ñ€Ñ€ÐµÐºÑ†Ð¸Ñ Ñ‚ÐµÐ¼Ð¿Ð°... &Cross Fade &КроÑÑфейд Clip Cross-fade Clip cross-fade Cross-fade current overlapped clips &Range Set УÑтановить &диапазон Clip Range Clip range Set edit-range from current clip extents &Loop Set УÑтановить &петлю Clip Loop Clip loop Set loop-range from current clip extents First Take First take Select current clip first take Previous Take Previous take Select current clip previous take Next Take Next take Select current clip next take Shift+T Last Take Last take Select current clip last take Reset Takes Reset takes Reset (unfold) current clip takes R&ange... Д&иапазон... Take Range Take range Range (fold) current clip into takes &Menubar Строка &меню Menubar Строка меню Show/hide the main program window menubar Показать или Ñкрыть меню оÑновного окна программы Ctrl+M Ctrl+M &Statusbar &Строка ÑоÑтоÑÐ½Ð¸Ñ Statusbar Строка ÑоÑтоÑÐ½Ð¸Ñ Show/hide the main program window statusbar Показать или Ñкрыть Ñтроку ÑоÑтоÑÐ½Ð¸Ñ Ð¾Ñновного окна программы File Toolbar Панель файлов File toolbar Панель файлов Show/hide main program window file toolbar Показать или Ñкрыть панель файлов в оÑновном окне программы Edit Toolbar Панель правки Edit toolbar Панель правки Show/hide main program window edit toolbar Показать или Ñкрыть панель правки в оÑновном окне программы Track Toolbar Панель дорожек Track toolbar Панель дорожек Show/hide main program window track toolbar Показать или Ñкрыть панель параметров в оÑновном окне программы View Toolbar Панель вида View toolbar Панель вида Show/hide main program window view toolbar Показать или Ñкрыть панель вида в оÑновном окне программы &Options &Параметры Options Toolbar Панель параметров Options toolbar Панель параметров Show/hide main program window options toolbar Показать или Ñкрыть панель параметров в оÑновном окне программы Transport Toolbar Панель транÑпорта Transport toolbar Панель транÑпорта Show/hide main program window transport toolbar Показать или Ñкрыть панель транÑпорта в оÑновном окне программы T&ime Сч&етчик времени Time Toolbar Панель Ñчетчика времени Time toolbar Панель Ñчетчика времени Show/hide main program window time toolbar Показать или Ñкрыть Ñчетчик времени в оÑновном окне программы Thum&b О&бзор проекта Thumb Toolbar Панель обзора проекта Thumb toolbar Панель обзора проекта Show/hide main program window thumb toolbar Показать или Ñкрыть панель обзора проекта в оÑновном окне программы File &System &Браузер файлов File System Ð¤Ð°Ð¹Ð»Ð¾Ð²Ð°Ñ ÑиÑтема File system Браузер файлов Show/hide the file system window &Files &Файлы Files Файлы Show/hide the files window Показать или Ñкрыть панель доÑтупа к файлам M&essages Соо&Ð±Ñ‰ÐµÐ½Ð¸Ñ Messages Ð¡Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Show/hide the messages window Показать или Ñкрыть панель Ñообщений &Connections &Ð¡Ð¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Connections Ð¡Ð¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Show/hide the connections window Показать или Ñкрыть диалог ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ ÑоединениÑми F8 F8 Mi&xer Микш&ер Mixer Микшер Show/hide the mixer window Показать или Ñкрыть диалог микшера F9 F9 &In &Приблизить Zoom In Приблизить отображение Zoom in Приблизить отображение дорожек Ctrl++ Ctrl++ &Out &Отдалить Zoom Out Отдалить отображение Zoom out Отдалить отображение дорожек Ctrl+- Ctrl+- &Reset &СброÑить Mo&de St&ep Insert Range Insert range Insert range as selected Ctrl+Ins Ctrl+Ins Insert Track Range Insert track range Insert track range as selected Ctrl+Shift+Ins Ctrl+Shift+Ins &Increase У&величить Increase Height Увеличить выÑоту Increase height Увеличить выÑоту Increase track height Увеличить выÑоту дорожки Ctrl+Shift++ Ctrl+Shift++ &Decrease У&меньшить Decrease Height Уменьшить выÑоту Decrease height Уменьшить выÑоту Decrease track height Уменьшить выÑоту дорожки Ctrl+Shift+- Ctrl+Shift+- Height Reset СброÑить выÑоту Height reset СброÑить выÑоту Reset track height СброÑить выÑоту дорожки Ctrl+Shift+1 Ctrl+Shift+1 Auto &Monitor Ðвто&мониторинг Zoom Reset Обычный маÑштаб Zoom reset СброÑить вÑе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¼Ð°Ñштаба Ctrl+1 Ctrl+1 &Horizontal По &горизонтали Horizontal Zoom МаÑштабирование по горизонтали Horizontal zoom МаÑштабирование по горизонтали Horizontal zoom mode Включить или выключить режим маÑÑˆÑ‚Ð°Ð±Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ горизонтали &Vertical По &вертикали Vertical Zoom МаÑштабирование по вертикали Vertical zoom МаÑштабирование по вертикали Vertical zoom mode Включить или выключить режим маÑÑˆÑ‚Ð°Ð±Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ вертикали All Zoom МаÑштабирование во вÑех направлениÑÑ… All zoom МаÑштабирование во вÑех направлениÑÑ… All zoom mode Включить или выключить режим маÑÑˆÑ‚Ð°Ð±Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð²Ð¾ вÑех направлениÑÑ… &Grid К &Ñетке Grid Сетка Snap grid view mode &Zebra &Зебра Zebra Чередовать ÑркоÑть тактов Bar zebra view mode Чередовать ÑркоÑть тактов Too&l Tips Ð’Ñплываю&щие подÑказки Tool tips Ð’Ñплывающие подÑказки Floating tool tips view mode &Refresh О&бновить Refresh Обновить Refresh views Обновить вид проекта F5 F5 &Instruments... &ИнÑтрументы... Instruments ИнÑтрументы Change instrument definitions and files Изменить Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð¸ файлы инÑтрументов &Buses... &Шины... Buses Шины Change session bus definitions Изменить Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ ÑˆÐ¸Ð½Ñ‹ ÑеÑÑии &Options... &Параметры... Options Параметры Change general application program options Изменить общие параметры работы программы F12 F12 &Backward Ð’ &начало Backward Ð’ начало Transport backward Перемотать в начало Backspace Backspace Re&wind Ð&азад Rewind Ðазад Transport rewind Перемотать назад F&ast Forward &Вперед Fast Forward Вперед Fast forward Вперед Transport fast forward Перемотать вперед &Forward Ð’ &конец Forward Ð’ конец Transport forward Перемотать в конец &Loop &ÐŸÐµÑ‚Ð»Ñ Loop ÐŸÐµÑ‚Ð»Ñ Transport loop ÐŸÐµÑ‚Ð»Ñ Ñ‚Ñ€Ð°Ð½Ñпорта Ctrl+Shift+L Ctrl+Shift+L Step Backward Step backward Transport step backward Step Forward Step forward Transport step forward Ctrl+Shift+P Ctrl+Shift+P Loop &Set УÑтановить п&етлю Loop Set УÑтановить петлю Loop set УÑтановить петлю Transport loop set УÑтановить петлю Ctrl+L Ctrl+L &Stop &Стоп Stop Стоп Transport stop ОÑтановить транÑпорт &Play Ð’&оÑпроизведение Tempo M&ap / Markers... Карта темпа / Маркеры... Tempo Map / Markers Карта темпа / Маркеры Tempo map / markers Карта темпа / Маркеры Change session tempo map / markers Изменить карту темпа / маркеры ÑеÑÑии Play ВоÑпроизведение Transport play/pause ВоÑпроизведение/пауза Space Пробел Record ЗапиÑÑŒ Transport record ЗапиÑÑŒ &Punch &Врезка Punch Врезка Punch in/out Ðачало/конец врезки Transport punch in/out Ðачало/конец врезки транÑпорта Punch Se&t УÑтановить &врезку Punch Set УÑтановить врезку Punch in/out set УÑтановить начало/конец врезки Transport punch in/out set УÑтановить начало/конец врезки Ctrl+P Ctrl+P &Count-in Count-in &Metronome М&етроном Metronome Метроном F&ollow Playhead С&ледовать за курÑором воÑÐ¿Ñ€Ð¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Follow Playhead Следовать за курÑором воÑÐ¿Ñ€Ð¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Follow playhead Следовать за курÑором воÑÐ¿Ñ€Ð¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ A&uto Backward Ðв&товозврат Auto Backward Ðвтовозврат Auto backward Ðвтовозврат &Continue Past End &Играть поÑле конца Continue Past End Играть поÑле конца Continue past end Играть поÑле конца Transport mode: None Режим транÑпорта: не уÑтановлен Transport mode set to None &Slave Ведо&мый Slave Ведомый Transport mode: Slave Режим транÑпорта: ведомый Transport mode set to Slave &Master Ð’&едущий Master Ведущий Transport mode: Master Режим транÑпорта: ведущий Transport mode set to Master &Full &Полный Full Полный Transport mode: Full Режим транÑпорта: полный Transport mode set to Full Pa&nic Па&ника Panic Паника All MIDI tracks shut off (panic) &Shortcuts... &Клавиатурные комбинации... Shortcuts Клавиатурные комбинации Keyboard shortcuts Клавиатурные комбинации &About... &О программе... Show information about this application program Показать информацию об Ñтом приложении About &Qt... О &Qt... About Qt О Qt Show information about the Qt toolkit Показать ифнормацию об инÑтрументарии Qt Current tempo (BPM) Текущий темп (BPM) &Merge... &Объединить... Merge Clips Объединить клипы Merge clips Объединить клипы Merge selected clips Объединить выделенные клипы &Quantize... &Отквантовать... Quantize clip events Quantize current MIDI clip events &Transpose... &ТранÑпонировать... Transpose Clip Transpose clip events Transpose current MIDI clip events &Normalize... &Ðормировать... Normalize clip events Normalize current MIDI clip events &Randomize... &Случайные значениÑ... Randomize Clip Randomize clip events Randomize current MIDI clip events Resi&ze... Resize Clip Resize clip events Resize current MIDI clip events Re&scale... Rescale Clip Rescale clip events Rescale current MIDI clip events T&imeshift... Timeshift Clip Timeshift clip events Timeshift current MIDI clip events Tempo Adjust Скорректировать темп Adjust session tempo from current clip selection F7 F7 &Import... &Импортировать... Import Clip Импортировать клип Import clip Импортировать клип Import clip from file(s) Импортировать клип из файла &Controllers... К&онтроллеры... Controllers Контроллеры Change MIDI controllers configuration Изменить конфигурацию контроллеров MIDI qtractorMessages Messages Ð¡Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Logging stopped --- %1 --- Ведение журнала оÑтановлено --- %1 --- Logging started --- %1 --- Ведение журнала начато --- %1 --- qtractorMidiControl Note On Note Off Key Press Controller Pgm Change Chan Press Pitch Bend RPN NRPN Control 14 Track Gain Track Panning Track Monitor Track Record Track Mute Track Solo qtractorMidiControlForm Warning Предупреждение Controller mappings have been changed. Карта ÑвÑзанных каналов/контроллеров изменилаÑÑŒ. Do you want to save the changes? Ð’Ñ‹ хотите Ñохранить Ñти изменениÑ? Import Controller Files Импорт конфигурации контроллеров Controller files (*.%1) Файлы Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸ÐµÐ¹ контроллеров (*.%1) All files (*.*) Ð’Ñе файлы (*.*) Export Controller File ЭкÑпорт конфигурации контроллеров controller контроллер The controller file already exists: "%1" Do you want to replace it? Этот файл Ñ ÐºÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸ÐµÐ¹ уже добавлен: "%1" Ð’Ñ‹ хотите заменить его? Delta Дельта Controller files Файлы контроллеров Files Файлы Path РаÑположение &Import... &Импортировать... Remove controller file Удалить файл Ñ Ð¾Ð¿Ð¸Ñанием контроллеров &Remove &Удалить Move controller file up on list order ПеремеÑтить файл конфигурации вверх по ÑпиÑку &Up &Выше Move controller file down on list order ПеремеÑтить файл конфигурации вниз по ÑпиÑку &Down &Ðиже Trac&k &Дорожка offse&t Ñме&щение &limit пре&дел C&ommand Ко&манда &Parameter &Параметр Flags Флаги MIDI Channel Канал MIDI &Feedback Отк&лик &Map &СвÑзать U&nmap &Удалить Controller map Карта контроллеров Channel Канал Command Команда Parameter Параметр Controllers Контроллеры &Type Т&ип &Channel &Канал MIDI Event type Тип MIDI-ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ MIDI Controller (parameter) MIDI-контроллер (параметр) MIDI parameter (track offset) MIDI-контроллер (Ñмещение дорожки) + + Track offset Смещение дорожки Track limit Предел дорожки Command delta/momentary D&elta Д&ельта Type Тип Track Дорожка Feedback Отклик Enable all controllers immediate sync (hook) &Sync Син&Ñ…Ñ€. Reload/apply all controller files Потоврно загрузить и применить конфигурации контроллеров Relo&ad Пере&загрузить Export to controller file ЭкÑпортировать в файл конфигурации контроллеров E&xport... &ЭкÑпортировать... Close this dialog Закрыть Ñтот диалог Close Закрыть Saved controller mappings may not be effective the next time you start this program. "%1" Do you want to apply to controller files? Import controller files Импорт файлов конфигурации контроллеров Command action ДейÑтвие команды Command feedback Отклик команды Map/update controller command СвÑзать/обновить команду контроллера Unmap/remove controller command Убрать ÑвÑзывание команды контроллера About to remove controller file: "%1" Are you sure? qtractorMidiControlObserverForm &Type: Т&ип: MIDI event type Тип MIDI-ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Cha&nnel: &Канал: MIDI channel MIDI-канал In&vert И&нвертировать &Hook L&atch Control input connections &Inputs &Входы Control output connections &Outputs Ð’&ыходы &Parameter: &Параметры: MIDI parameter MIDI-параметр &Logarithmic &ЛогарифмичеÑкий &Feedback Отк&лик MIDI Controller MIDI-контроллер MIDI controller is already assigned. Do you want to replace the mapping? Some settings have been changed. Do you want to apply the changes? Ðекоторые параметры изменилиÑÑŒ. Ð’Ñ‹ хотите применить изменениÑ? &MIDI Controller... MIDI-контро&ллер… &Automation &ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ð¸Ñ &Lock &Заблокировать &Play Ð’&оÑпроизвеÑти &Record З&апиÑать &Clear О&чиÑтить qtractorMidiControlPluginWidget &Type: Т&ип: MIDI event type Тип MIDI-ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Cha&nnel: &Канал: MIDI channel MIDI-канал &Parameter: &Параметры: MIDI parameter MIDI-параметр &Logarithmic &ЛогарифмичеÑкий In&vert И&нвертировать &Bipolar qtractorMidiEditEvent Zoom in (horizontal) Приблизить по горизонтали Zoom out (horizontal) Отдалить по горизонтали Zoom reset (horizontal) ИÑходный маÑштаб по горизонтали qtractorMidiEditList C%1 C%1 qtractorMidiEditTime Play-head Edit-head Edit-tail Loop-start Loop-end Punch-in Punch-out Start: %1 End: %2 Length: %3 Ðачало: %1 Конец: %2 ДлительноÑть: %3 qtractorMidiEditView Zoom in (vertical) Приблизить по вертикали Zoom out (vertical) Отдалить по вертикали Zoom reset (vertical) ИÑходный маÑштаб по вертикали qtractorMidiEditor MIDI Editor Редактор MIDI cut вырезание delete удаление insert range remove range move перемещение edit правка resize Ñмена размера rescale paste вÑтавка Time: %1 Type: ВремÑ: %1 Тип: Key Press (%1) %2 Value: %3 Controller (%1) Name: %2 Value: %3 Контроллер (%1) Ðазвание: %2 Значение: %3 RPN (%1) Name: %2 Value: %3 NRPN (%1) Name: %2 Value: %3 Control 14 (%1) Name: %2 Value: %3 Pgm Change (%1) Chan Press (%1) Pitch Bend (%1) Start: %1 End: %2 Length: %3 Ðачало: %1 Конец: %2 ДлительноÑть: %3 SysEx (%1 bytes) Data: SysEx (%1 байтов) Данные: C C C#/Db C#/Db D D D#/Eb D#/Eb E E F F F#/Gb F#/Gb G G G#/Ab G#/Ab A A A#/Bb A#/Bb B B Acoustic Bass Drum Bass Drum 1 Side Stick Acoustic Snare Hand Clap Electric Snare Low Floor Tom Closed Hi-Hat High Floor Tom Pedal Hi-Hat Low Tom Open Hi-Hat Low-Mid Tom Hi-Mid Tom Crash Cymbal 1 High Tom Ride Cymbal 1 Chinese Cymbal Ride Bell Tambourine Splash Cymbal Cowbell Crash Cymbal 2 Vibraslap Ride Cymbal 2 Hi Bongo Low Bongo Mute Hi Conga Open Hi Conga Low Conga High Timbale Low Timbale High Agogo Low Agogo Cabasa Maracas Short Whistle Long Whistle Short Guiro Long Guiro Claves Hi Wood Block Low Wood Block Mute Cuica Open Cuica Mute Triangle Open Triangle Bank Select (coarse) Modulation Wheel (coarse) Breath Controller (coarse) Foot Pedal (coarse) Portamento Time (coarse) Data Entry (coarse) Volume (coarse) Balance (coarse) Pan Position (coarse) Expression (coarse) Effect Control 1 (coarse) Effect Control 2 (coarse) General Purpose Slider 1 General Purpose Slider 2 General Purpose Slider 3 General Purpose Slider 4 Bank Select (fine) Modulation Wheel (fine) Breath Controller (fine) Foot Pedal (fine) Portamento Time (fine) Data Entry (fine) Volume (fine) Balance (fine) Pan Position (fine) Expression (fine) Effect Control 1 (fine) Effect Control 2 (fine) Hold Pedal (on/off) Portamento (on/off) Soft Pedal (on/off) Legato Pedal (on/off) Hold 2 Pedal (on/off) Sound Variation General Purpose Button 1 (on/off) General Purpose Button 2 (on/off) General Purpose Button 3 (on/off) General Purpose Button 4 (on/off) Effects Level Chorus Level Celeste Level Phaser Level Data Button Increment Data Button Decrement Non-Registered Parameter (fine) Non-Registered Parameter (coarse) Registered Parameter (fine) Registered Parameter (coarse) All Sound Off All Controllers Off Local Keyboard (on/off) All Notes Off Omni Mode Off Omni Mode On Mono Operation Poly Operation Pitch Bend Sensitivity Fine Tune Coarse Tune Tuning Program Tuning Bank Vibrato Rate Vibrato Depth Vibrato Delay Filter Cutoff Filter Resonance Sostenuto Pedal (on/off) Release Time Attack Time Brightness Decay Time Tremolo Level EG Attack EG Decay EG Release Drum Filter Cutoff Drum Filter Resonance Drum EG Attack Drum EG Decay Drum Pitch Coarse Drum Pitch Fine Drum Level Drum Pan Drum Reverb Send Drum Chorus Send Drum Variation Send Modulation Wheel (14bit) Breath Controller (14bit) Foot Pedal (14bit) Portamento Time (14bit) Volume (14bit) Balance (14bit) Pan Position (14bit) Expression (14bit) Effect Control 1 (14bit) Effect Control 2 (14bit) General Purpose Slider 1 (14bit) General Purpose Slider 2 (14bit) General Purpose Slider 3 (14bit) General Purpose Slider 4 (14bit) Chromatic Major Мажор Minor Минор Melodic Minor (Asc) МелодичеÑкий минор (воÑходÑщего порÑдка) Melodic Minor (Desc) МелодичеÑкий минор (ниÑходÑщего порÑдка) Whole Tone Pentatonic Major Pentatonic Minor Pentatonic Blues Pentatonic Neutral Octatonic (H-W) Octatonic (W-H) Ionian ИонийÑкий Dorian ДорийÑкий Phrygian ФригийÑкий Lydian ЛидийÑкий Mixolydian МикÑолидийÑкий Aeolian ЭолийÑкий Locrian ЛокрийÑкий Egyptian Eight Tone Spanish ВоÑьмитоновый иÑпанÑкий Hawaiian ГавайÑкий Hindu Hirajoshi Hungarian Major Hungarian Minor Hungarian Gypsy Japanese (A) Japanese (B) Jewish (Adonai Malakh) Jewish (Ahaba Rabba) Jewish (Magen Abot) Oriental (A) Oriental (B) Oriental (C) Roumanian Minor ГуцульÑкий минор Neapolitan ÐеаполитанÑкий Neapolitan Major ÐеаполитанÑкий мажор Neapolitan Minor ÐеаполитанÑкий минор Overtone Leading Whole Tone Nine Tone Scale Dominant Seventh Augmented Algerian Arabian (A) Arabian (B) Balinese Chinese КитайÑкий Diminished Japanese (Ichikosucho) Japanese (Taishikicho) Javaneese Marva Theta Mela Bhavapriya Mela Chakravakam Mela Chalanata Mela Chitrambari Mela Dharmavati Mela Dhatuvardhani Mela Dhavalambari Mela Divyamani Mela Ganamurti Mela Gangeyabhusani Mela Gavambodhi Mela Gayakapriya Mela Hatakambari Mela Jalarnavam Mela Jhalavarali Mela Jhankaradhvani Mela Jyotisvarupini Mela Kamavarardhani Mela Kantamani Mela Kosalam Mela Latangi Mela Manavati Mela Mararanjani Mela Naganandini Mela Namanarayani Mela Navanitam Mela Nitimati Mela Pavani Mela Ragavardhani Mela Raghupriya Mela Ramapriya Mela Rasikapriya Mela Ratnangi Mela Risabhapriya Mela Rupavati Mela Sadvidhamargini Mela Salagam Mela Sanmukhapriya Mela Sarasangi Mela Senavati Mela Subhapantuvarali Mela Sucharitra Mela Sulini Mela Suryakantam Mela Syamalangi Mela Tanarupi Mela Vagadhisvari Mela Vanaspati Mela Varunapriya Mela Yagapriya Persian Purvi Theta Spanish Gypsy Todi Theta Enigmatic Kumoi Lydian Augmented ЛидийÑкий увеличенный Pelog Prometheus Prometheus Neapolitan Six Tone Symmetrical Super Locrian Lydian Minor Lydian Diminished ЛидийÑкий уменьшенный Half Diminished Bhairav Yaman Todi Jog Multani Darbari Malkauns Bhoopali Shivaranjani Marwa Minor 5 Major 5 5 5 45 45 457 457 M 6 Note On (%1) %2 Velocity: %3 Duration: %4 Note On (%1) %2 Сила нажатиÑ: %3 ДлительноÑть: %4 Unknown (%1) ÐеизвеÑтно (%1) qtractorMidiEditorForm Snap/beat Прилипание к долÑм Set current snap to %1 Current time (play-head) Current tempo (BPM) Текущий темп (BPM) Reset time-sig. Note Velocity Сила Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ Ð½Ð¾Ñ‚ Note type Тип нот Value type Тип Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Parameter type Scale key Scale type MIDI clip name Ð˜Ð¼Ñ ÐºÐ»Ð¸Ð¿Ð° MIDI MIDI file name Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð° MIDI MIDI track/channel Дорожка/канал MIDI MOD РЕЖ MIDI modification state СоÑтоÑние измененноÑти MIDI REC ЗÐП MIDI clip record state MUTE ТИХО MIDI clip mute state 00:00:00.000 00:00:00.000 MIDI clip duration ДлительноÑть клипа MIDI Warning Предупреждение The current MIDI clip has been changed: "%1" Do you want to save the changes? Открытый клип был изменен: "%1" Ð’Ñ‹ хотите Ñохранить изменениÑ? Save Сохранить Save MIDI Clip Сохранить клип MIDI MIDI files (*.%1 *.smf *.midi) Файлы MIDI (*.%1 *.smf *.midi) All files (*.*) Ð’Ñе файлы (*.*) MIDI Editor Редактор MIDI Channel %1 Канал %1 Track %1 Дорожка %1 [modified] [изменен] &Track &Дорожка &File &Файл Select &Mode &Режим Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ &Select Ð’Ñ‹&деление &Tools &ИнÑтрументы &Edit &Правка &View &Вид &Toolbars &Панели инÑтрументов &Windows &Окна &Zoom МаÑш&таб S&nap При&липание T&ransport &ТранÑпорт &Help &Справка &Save &Сохранить Save current MIDI clip to existing file name Сохранить активный клип MIDI в уже ÑущеÑтвующий файл Save &As... Сохранить &как... Save As Сохранить как Save as Сохранить как Save current MIDI clip with another file name Сохранить активный клип MIDI в файл Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼ именем &Unlink &Разгруппировать Unlink Разгруппировать Unlink current MIDI clip Recor&d За&пиÑать Record current MIDI clip (overdub) &Inputs &Входы Track Inputs Входы дорожки Track inputs Входы дорожки Show current MIDI clip/track input bus connections Показать активные ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ ÑˆÐ¸Ð½Ð¾Ð¹ входа MIDI-клипа или дорожки &Outputs Ð’&ыходы Track Outputs Выходы дорожки Track outputs Выходы дорожки Show current MIDI clip/track output bus connections Показать активные ÑÐ¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Ñ ÑˆÐ¸Ð½Ð¾Ð¹ выхода MIDI-клипа или дорожки &Properties... С&войÑтва... Track Properties СвойÑтва дорожки Track properties СвойÑтва дорожки Edit current MIDI clip/track properties Изменить ÑвойÑтва активного клипа/дорожки MIDI Shift+F2 Shift+F2 Properties СвойÑтва Edit current MIDI clip properties Изменить ÑвойÑтва активного клипа MIDI F4 F4 &Range Set УÑтановить &диапазон Clip Range Clip range Set edit-range from clip extents &Loop Set УÑтановить &петлю Clip Loop Clip loop Set loop-range from clip extents &Close &Закрыть Close Закрыть Close this MIDI clip editor Закрыть Ñтот диалог Edit Of&f Edit Off Edit off Set edit mode off Edit &On Edit On Edit on Set edit mode on Edit &Draw Edit draw mode Edit draw mode (notes) &Undo &Отменить Sc&ale Undo Отменить Undo last edit operation Отменить поÑледнее дейÑтвие Ctrl+Z Ctrl+Z &Redo Ве&рнуть Redo Вернуть Redo last edit operation Вернуть отмененное дейÑтвие Ctrl+Shift+Z Ctrl+Shift+Z Cu&t &Вырезать Cut Вырезать Cut current selection into the local clipboard Вырезать активное выделение в буфер обмена Ctrl+X Ctrl+X &Copy &Копировать Copy Копировать Copy current selection to the local clipboard Скопировать активное выделение в буфер обмена Ctrl+C Ctrl+C &Paste Ð’ÑÑ‚&авить Paste Ð’Ñтавить Paste local clipboard contents into the current MIDI clip Ð’Ñтавить Ñодержимое буфера обмена Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð¾Ð¼ в текущий клип MIDI Ctrl+V Ctrl+V Past&e Repeat... Ð’Ñтав&ить Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð¾Ð¼... Paste Repeat Ð’Ñтавить Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð¾Ð¼ Paste repeat Ð’Ñтавить Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð¾Ð¼ Paste/repeat local clipboard contents into the current MIDI clip Ð’Ñтавить Ñодержимое буфера обмена Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð¾Ð¼ в текущий клип MIDI Ctrl+Shift+V Ctrl+Shift+V &Delete &Удалить Delete Удалить Delete current selection Удалить активное выделение Del Del Select None СнÑть выделение Select none СнÑть выделение Ctrl+Shift+A Ctrl+Shift+A Select Invert Инвертировать выделение Select invert Инвертировать выделение Ctrl+I Ctrl+I Select All Выделить вÑÑ‘ Instrum&ent И&нÑтрумент Not&e Type &Тип нот Val&ue Type Т&ип Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ &Ghost Track &Note St&ep &Mute &Приглушение Mute Mute current MIDI clip Select all Выбрать вÑÑ‘ Ctrl+A Ctrl+A &Range &ОблаÑть Select Range Выделить облаÑть Select range Выделить облаÑть Mark range as selected Пометить облаÑть выделенной Ctrl+R Ctrl+R &Step Insert step Insert step (rest) Right DO NOT TRANSLATE &Quantize... &Отквантовать... Quantize Отквантовать Quantize selection Отквантовать выделение &Transpose... &ТранÑпонировать... Transpose ТранÑпонировать Transpose selection ТранÑпонировать выделение &Normalize... &Ðормализовать... Normalize Ðормализовать Normalize selection Ðормализовать выделение &Randomize... &Случайные значениÑ... Randomize Применить Ñлучайные Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Randomize selection Применить к выделению Ñлучайные Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Resize Изменить размер Resize selection Изменить размер Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Re&scale... Rescale Rescale selection T&imeshift... Timeshift Смещение во времени Timeshift selection T&empo ramp... &ÐŸÐ»Ð°Ð²Ð½Ð°Ñ Ñмена темпа… Tempo ramp ÐŸÐ»Ð°Ð²Ð½Ð°Ñ Ñмена темпа Tempo ramp selection &Menubar Строка &меню Menubar Строка меню Show/hide the menubar Показать или Ñкрыть Ñтроку меню Ctrl+M Ctrl+M &Statusbar &Строка ÑоÑтоÑÐ½Ð¸Ñ Statusbar Строка ÑоÑтоÑÐ½Ð¸Ñ Show/hide the statusbar Показать или Ñкрыть Ñтроку ÑоÑтоÑÐ½Ð¸Ñ File Toolbar Панель файлов File toolbar Панель файлов Show/hide the file toolbar Показать или Ñкрыть панель доÑтупа к файлам Edit Toolbar Панель правки Edit toolbar Панель правки Show/hide the edit toolbar Показать или Ñкрыть панель доÑтупа к файлам View Toolbar Панель вида View toolbar Панель вида Show/hide the view toolbar Показать или Ñкрыть панель вида &Transport &ТранÑпорт Transport Toolbar Панель транÑпорта Transport toolbar Панель транÑпорта Show/hide the transport toolbar Показать или Ñкрыть панель транÑпорта T&ime Сч&етчик времени Time Toolbar Панель Ñчетчика времени Time toolbar Панель Ñчетчика времени Show/hide the time toolbar &Scale Scale Toolbar Scale toolbar Show/hide the scale toolbar Thum&b О&бзор проекта Thumb Toolbar Панель обзора проекта Thumb toolbar Панель обзора проекта Show/hide the thumb view toolbar Note &Names &ÐÐ°Ð·Ð²Ð°Ð½Ð¸Ñ Ð½Ð¾Ñ‚ Note Names ÐÐ°Ð·Ð²Ð°Ð½Ð¸Ñ Ð½Ð¾Ñ‚ Note names Whether to show note names Note &Duration &ДлительноÑть нот Note Duration ДлительноÑть нот Note duration ДлительноÑть нот Whether note events are shown proportional to duration Отображать ли ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð¿Ñ€Ð¾Ð¿Ð¾Ñ€Ñ†Ð¸Ð¾Ð½Ð°Ð»ÑŒÐ½Ð¾ длительноÑти нот Note &Color &Цвет выÑоты тона Note Color Цветные ноты Note color Цветные ноты Whether note events are colored according to pitch РаÑкрашивать ли ноты ÑоглаÑно их выÑоте тона &Value Color Цвет Ñилы &Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ Value Color Цвет Ñилы Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ Value color Цвет Ñилы Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ Whether note events are colored according to value (velocity) РаÑкрашивать ли ноты ÑоглаÑно их Ñиле Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ &Drum Mode &ПеркуÑÑионный режим Drum Mode ПеркуÑÑионный режим Drum mode Whether note onset events are displayed as diamonds &Events &Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ View events ПроÑмотреть ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Show/hide the events list &Preview Notes &ВоÑпроизводить при правке Preview Notes ВоÑпроизводить при правке Preview notes ВоÑпроизводить ноты при правке Preview notes while editing (scrub) ВоÑпроизводить ноты при редактировании F&ollow Playhead С&ледовать за курÑором воÑÐ¿Ñ€Ð¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Follow Playhead Следовать за курÑором воÑÐ¿Ñ€Ð¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Follow playhead Следовать за курÑором воÑÐ¿Ñ€Ð¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ &In &Приблизить Zoom In Приблизить отображение Zoom in Приблизить отображение Ctrl++ Ctrl++ &Out &Отдалить Zoom Out Отдалить отображение Zoom out Отдалить отображение Ctrl+- Ctrl+- &Reset &СброÑить Zoom Reset Обычный маÑштаб Zoom reset Обычный маÑштаб Ctrl+1 Ctrl+1 &Horizontal По &горизонтали Horizontal Zoom МаÑштабирование по горизонтали Horizontal zoom МаÑштабирование по горизонтали Horizontal zoom mode Включить или выключить режим маÑÑˆÑ‚Ð°Ð±Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ горизонтали &Vertical По &вертикали Vertical Zoom МаÑштабирование по вертикали Vertical zoom МаÑштабирование по вертикали Vertical zoom mode Включить или выключить режим маÑÑˆÑ‚Ð°Ð±Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ вертикали &Zebra &Зебра Zebra Чередовать ÑркоÑть тактов Bar zebra view mode Чередовать ÑркоÑть тактов Ctrl+Shift+L Ctrl+Shift+L Ctrl+Shift+P Ctrl+Shift+P &All &Во вÑех направлениÑÑ… I&nsert Ð’&Ñтавить Remo&ve &Удалить &None &Invert &Инвертировать Insert Range Insert range Insert range as selected Ctrl+Ins Ctrl+Ins Remove Range Удалить облаÑть Remove range Удалить облаÑть Remove range as selected Удалить выбранную облаÑть Ctrl+Del Ctrl+Del All Zoom МаÑштабирование во вÑех направлениÑÑ… All zoom МаÑштабирование во вÑех направлениÑÑ… All zoom mode Включить или выключить режим маÑÑˆÑ‚Ð°Ð±Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð²Ð¾ вÑех направлениÑÑ… Snap grid view mode Too&l Tips Ð’Ñплываю&щие подÑказки Tool tips Ð’Ñплывающие подÑказки Floating tool tips view mode &Refresh О&бновить Refresh Обновить Refresh views Обновить вид клипа F5 F5 &Backward Ð’ &начало Backward Ð’ начало Transport backward Перемотать в начало Backspace Backspace Re&wind Ð&азад Rewind Ðазад Transport rewind Перемотать назад F&ast Forward &Вперед Fast Forward Вперед Fast forward Вперед Transport fast forward Перемотать вперед &Forward Ð’ &конец Forward Ð’ конец Transport forward Перемотать в конец Step Backward Step backward Transport step backward Step Forward Step forward Transport step forward Note Backward Step note backward Transport step note backward Note Forward Step note forward Transport step note forward &Loop &ÐŸÐµÑ‚Ð»Ñ Loop ÐŸÐµÑ‚Ð»Ñ Transport loop ÐŸÐµÑ‚Ð»Ñ Ñ‚Ñ€Ð°Ð½Ñпорта Loop &Set УÑтановить п&етлю Loop Set УÑтановить петлю Loop set УÑтановить петлю Transport loop set УÑтановить петлю Ctrl+L Ctrl+L &Stop &Стоп Stop Стоп Transport stop ОÑтановить транÑпорт &Play Ð’&оÑпроизведение Play ВоÑпроизведение Transport play/pause ВоÑпроизведение/пауза Space Пробел &Record &ЗапиÑÑŒ Record ЗапиÑÑŒ Transport record ЗапиÑÑŒ &Punch &Врезка Punch Врезка Punch in/out Ðачало/конец врезки Transport punch in/out Ðачало/конец врезки транÑпорта Punch Se&t УÑтановить &врезку Punch Set УÑтановить врезку Punch in/out set УÑтановить начало/конец врезки Transport punch in/out set УÑтановить начало/конец врезки Ctrl+P Ctrl+P Pa&nic Па&ника Panic Паника All MIDI tracks shut off (panic) &Shortcuts... &Клавиатурные комбинации... Shortcuts Клавиатурные комбинации Keyboard shortcuts Клавиатурные комбинации &About... &О программе... About О программе Show information about this application program Показать информацию об Ñтом приложении About &Qt... О &Qt... About Qt О Qt Show information about the Qt toolkit Показать ифнормацию об инÑтрументарии Qt &Grid К &Ñетке Resi&ze... Grid Сетка qtractorMidiEventList Events Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ qtractorMidiEventListView::ItemDelegate edit %1 qtractorMidiEventListView::ItemModel Time Ð’Ñ€ÐµÐ¼Ñ Type Тип Name Ðазвание Value Значение Duration/Data ДлительноÑть Frame Кадр BBT BBT Note On (%1) Note Off (%1) Key Press (%1) Controller (%1) Control 14 (%1) RPN (%1) NRPN (%1) Pgm Change Chan Press Pitch Bend SysEx SysEx Meta (%1) Мета (%1) Unknown (%1) ÐеизвеÑтно (%1) qtractorMidiListView Name Ðазвание Fmt Фмт Tracks Дорожки tpqn Path РаÑположение %1: MIDI file not found. Open MIDI Files Открыть файлы MIDI MIDI files (*.%1 *.smf *.midi) Файлы MIDI (*.%1 *.smf *.midi) All files (*.*) Ð’Ñе файлы (*.*) qtractorMidiMixerMeter Volume (%) % % Pan: %1 Volume: %1% ГромкоÑть: %1% qtractorMidiSysexForm MIDI SysEx Name Ðазвание Size Размер Data (hex) Данные (hex) Import from SysEx file ЭкÑпортировать из файла SysEx &Import... &Импортировать... Export to SysEx file ЭкÑпортировать в файл SysEx E&xport... &ЭкÑпортировать... Move SysEx item up on list order &Up &ПоднÑть Move SysEx item down on list order &Down Оп&уÑтить Open SysEx Открыть SysEx Sysex name Ðазвание SysEx Save SysEx Сохранить SysEx Delete SysEx Удалить SysEx Create SysEx item &Add &Добавить Update SysEx item Upda&te О&бновить Remove SysEx item &Remove &Удалить SysEx files (*.%1) Файлы SysEx (*.%1) MIDI files (*.mid *.smf *.midi) Файлы MIDI (*.mid *.smf *.midi) All files (*.*) Ð’Ñе файлы (*.*) Import SysEx Files Импортировать файлы SysEx Export SysEx File ЭкÑпортировать файлы SysEx Warning Предупреждение The SysEx file already exists: "%1" Do you want to replace it? About to replace SysEx: "%1" Are you sure? About to delete SysEx: "%1" Are you sure? SysEx settings have been changed. Do you want to apply the changes? Error Ошибка SysEx could not be loaded: "%1". Sorry. qtractorMidiThumbView MIDI Thumb view qtractorMidiToolsForm (default) (по умолчанию) Warning Предупреждение About to delete preset: "%1" Are you sure? Эта предуÑтановка будет удалена: "%1" Ð’Ñ‹ уверены? none нет quantize квантование transpose транÑпонирование normalize Ð½Ð¾Ñ€Ð¼Ð°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ randomize Ñлучайные Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ resize Ñмена размера rescale timeshift temporamp MIDI Tools ИнÑтрументы MIDI Preset name Ðазвание предуÑтановки Save preset Сохранить предуÑтановку Delete preset Удалить предуÑтановку &Quantize &Квантование Quantize selected events Квантование выделенных Ñобытий &Time: &ВремÑ: Quantize time Ð’Ñ€ÐµÐ¼Ñ ÐºÐ²Ð°Ð½Ñ‚Ð¾Ð²Ð°Ð½Ð¸Ñ Quantize time percent &Duration: &ДлительноÑть: Quantize duration ДлительноÑть ÐºÐ²Ð°Ð½Ñ‚Ð¾Ð²Ð°Ð½Ð¸Ñ Quantize duration percent Swing-quantize time Swing-quantize percent Swing-quantize type Linear Линейное Quadratic Квадратичное Cubic КубичеÑкое &Scale: Scale-quantize key Scale-quantize type &Transpose Т&ранÑпонирование Transpose selected events ТранÑпонирование выделенных Ñобытий &Note: &Ðота: Transpose note Величина транÑÐ¿Ð¾Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð² нотах Transpose time Ð’Ñ€ÐµÐ¼Ñ Ñ‚Ñ€Ð°Ð½ÑÐ¿Ð¾Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Transpose time format Формат времени транÑÐ¿Ð¾Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Frames Выборки Time Ð’Ñ€ÐµÐ¼Ñ BBT BBT &Reverse Ð’ о&братном направлении &Normalize &Ðормировка Normalize selected events Ðормировать выделенные ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ &Percent: &Процент: Normalize percent Процент нормировки &Compress Resi&ze ПоменÑть &размер Resize value mode СпоÑоб Ñмены темпа Flat Ð ÐµÐ·ÐºÐ°Ñ Ramp ÐŸÐ»Ð°Ð²Ð½Ð°Ñ Resize final value Целевое значение Ñилы Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ &Legato: Legato trim/extend type Normal Обычный Trim Extend Legato trim/extend length Legato mode Mono Poly &Join &Split: Split notes length Split notes offset Relative Absolute Re&scale Rescale selected events Rescale time Rescale duration Rescale value &Invert &Инвертировать T&imeshift С&мещение Timeshift selected events СмеÑтить выделенные ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð²Ð¾ времени Timeshift Смещение во времени P: Timeshift parameter Timeshift parameter (log) Timeshift curve P = 0 : no change. P > 0 : accelerating shift. P < 0 : slowing down shift. Edit head/tail (blue) markers define the shift range. Timeshift duration T&empo ramp П&Ð»Ð°Ð²Ð½Ð°Ñ Ñмена темпа Tempo ramp selected events Плавно поменÑть темп выбранных Ñобытий Tempo ramp ÐŸÐ»Ð°Ð²Ð½Ð°Ñ Ñмена темпа From Из Tempo ramp start Ðачало to в Temporamp end Конец Edit head/tail (blue) markers define the ramp range. Оконечные Ñиние маркеры определÑÑŽÑ‚ границы плавной Ñмены темпа. Tempo ramp duration % % S&wing: С&винг: &Value: &Сила нажатиÑ: Normalize value Значение нормализации &Randomize &Случайные Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Randomize selected events Применение Ñлучайных значений к выделению Randomize note/pitch Случайные ноты Randomize time Случайное Ð²Ñ€ÐµÐ¼Ñ Randomize duration Ð¡Ð»ÑƒÑ‡Ð°Ð¹Ð½Ð°Ñ Ð´Ð»Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñть Randomize value Ð¡Ð»ÑƒÑ‡Ð°Ð¹Ð½Ð°Ñ Ñила Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ Resize selected events Смена размера выделенных Ñобытий Resize duration Смена длительноÑти Resize duration format Формат значений Resize value Множитель qtractorMixer Inputs Входы Tracks Дорожки Outputs Выходы Mixer Микшер qtractorMixerMeter Pan qtractorMixerRackWidget &Inputs &Входы &Outputs Ð’&ыходы &Monitor &Мониторинг &Buses... &Шины... &Audio &MIDI &MIDI qtractorMixerStrip inputs входы outputs выходы Connect %1 Соединить %1 (Audio) (аудио) (MIDI) (MIDI) (None) (нет) In Out qtractorMonitorButton monitor монитор qtractorOptionsForm XML Default (*.%1) XML Regular (*.%1) ZIP Archive (*.%1) Ðрхив ZIP (*.%1) (Any) (Любой) Warning Предупреждение Some settings have been changed. Do you want to apply the changes? Ðекоторые параметры изменилиÑÑŒ. Ð’Ñ‹ хотите применить изменениÑ? Metronome Bar Audio File Звуковой файл такта Ð´Ð»Ñ Ð¼ÐµÑ‚Ñ€Ð¾Ð½Ð¾Ð¼Ð° Metronome Beat Audio File Звуковой файл доли Ð´Ð»Ñ Ð¼ÐµÑ‚Ñ€Ð¾Ð½Ð¾Ð¼Ð° Open Style Sheet Style Sheet files (*.%1) Icons Theme Directory Audio Meter Color Цвет индикатора аудио MIDI Meter Color Цвет индикатора MIDI Plug-in Directory Каталог Ñ Ñффектами LV2 Presets Directory Каталог Ñ Ð¿Ñ€ÐµÑетами LV2 Plug-in Blacklist Черный ÑпиÑок плагинов Plug-in files (*.%1) Файлы плагинов (*.%1) Messages Font Шрифт Ñообщений Messages Log Журнал Ñообщений Log files (*.%1) Файлы журнала (*.%1) All files (*.*) Ð’Ñе файлы (*.*) Session Template Шаблон ÑеÑÑии Session template files (*.qtr *.qts *.%1) Файлы шаблонов ÑеÑÑий (*.qtr *.qts *.%1) &General О&бщие Default session &file format: &Формат ÑеÑÑии по умолчанию: Default session file format (suffix) &New session template: &Шаблон новой ÑеÑÑии: Save &backup versions of existing sessions: СохранÑть &резервные копии ÑеÑÑий: Increment previous version (default) Увеличить предыдущую верÑию Increment current version Увеличить текущую верÑию &Audio &Звук Capture / Export Захват/ЭкÑпорт Audio compression quality to use on capture (record) and export Сжатие звуковых данных при запиÑи и ÑкÑпорте Audio sample format to use on capture (record) and export Формат ÑÑмплов при запиÑи и ÑкÑпорте Audio file type to use on capture (record) and export Формат файлов, иÑпользуемый при запиÑи и ÑкÑпорте File &type: Т&ип файла: Sample &format: &Формат ÑÑмпла: &Quality: &КачеÑтво: Playback ВоÑпроизведение Sample-&rate converter type: Преобразование &чаÑтоты ÑÑмплированиÑ: Sample-rate converter quality КачеÑтво Ð¿Ñ€ÐµÐ¾Ð±Ñ€Ð°Ð·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ñ‡Ð°Ñтоты ÑÑÐ¼Ð¿Ð»Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Sinc (Best Quality) Sinc (наилучшее качеÑтво) Sinc (Medium Quality) Sinc (Ñреднее качеÑтво) Sinc (Fastest) Sinc (ÑÐ°Ð¼Ð°Ñ Ð±Ñ‹ÑÑ‚Ñ€Ð°Ñ Ð¾Ð±Ñ€Ð°Ð±Ð¾Ñ‚ÐºÐ°) Zero Order Hold Linear Линейное Transport control mode (JACK) None Ðет Slave Ведомый Master Ведущий Full Полный Whether to apply time-stretching when tempo changes РаÑÑ‚Ñгивать ли во времени при Ñмене темпа Aut&omatic time-stretching &ÐвтоматичеÑки раÑÑ‚Ñгивать во времени Whether to use WSOLA time-stretching ИÑпользовать ли WSOLA Ð´Ð»Ñ Ñ€Ð°ÑÑ‚ÑÐ³Ð¸Ð²Ð°Ð½Ð¸Ñ Ð²Ð¾ времени &WSOLA time-stretching &РаÑÑ‚Ñгивание во времени при помощи WSOLA Whether to apply WSOLA quick seek time-stretching WSOLA quic&k seek &БыÑÑ‚Ñ€Ð°Ñ Ð¿Ñ€Ð¾ÐºÑ€ÑƒÑ‚ÐºÐ° Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ WSOLA Whether to have separate audition/pre-listening player output ports Metronome Метроном Whether to enable the audio metronome ИÑпользовать ли звуковой метроном &Enable audio metronome &Включить звуковой метроном &File (bar): Фай&л (такт): Metronome Audio filename (bar) Звуковой файл такта Ð´Ð»Ñ Ð¼ÐµÑ‚Ñ€Ð¾Ð½Ð¾Ð¼Ð° Browse for sample audio file (bar) Указать звуковой файл Ð´Ð»Ñ Ð¾Ð±Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ñ‚Ð°ÐºÑ‚Ð° &Gain (bar): &УÑиление (такт): Metronome gain (bar) dB Дб &File (beat): Фай&л (&долÑ): Metronome Audio filename (beat) Звуковой файл доли Ð´Ð»Ñ Ð¼ÐµÑ‚Ñ€Ð¾Ð½Ð¾Ð¼Ð° Browse for sample audio file (beat) Указать звуковой файл Ð´Ð»Ñ Ð¾Ð±Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð¾Ð»Ð¸ &Gain (beat): У&Ñиление (долÑ): Metronome gain (beat) Whether to have separate audio metronome output ports Создавать ли раздельные звуковые порты выхода Ð´Ð»Ñ Ð¼ÐµÑ‚Ñ€Ð¾Ð½Ð¾Ð¼Ð° &MIDI &MIDI MIDI capture (record) quantization &Quantize: &Квантование: MIDI file format to use on capture (record) and export File &format: &Формат файлов: Queue &timer (resolution): Queue timer (resolution) Whether to enable MIDI queue time drift correction E&nable MIDI queue time drift correction Control Управление &MMC: &MMC: MIDI Machine Control (MMC) mode Input Вход Output Выход Duplex Ð”ÑƒÐ¿Ð»ÐµÐºÑ &Device: &УÑтройÑтво: MIDI Machine Control (MMC) device id. &SPP: &SPP: MIDI Song Position pointer (SPP) control mode MIDI Clock control mode Whether to have separate MIDI control ports Создавать ли раздельные порты ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ MIDI Dedicated MIDI &control input/output &Раздельные порты ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ MIDI Whether to have separate MIDI metronome output port Dedicated M&IDI metronome output Раздельный выход Ð´Ð»Ñ MIDI-&метронома &Note (beat): &Ðота (долÑ): Metronome MIDI note (beat) Metronome MIDI velocity (beat) &Duration (beat): &ДлительноÑть (beat): &Velocity (beat): &Сила Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ (beat): Metronome MIDI velocity (bar) &Duration (bar): &ДлительноÑть (такт): &Velocity (bar): &Сила Ð½Ð°Ð¶Ð°Ñ‚Ð¸Ñ (такт): Metronome MIDI note (bar) &Note (bar): Ðота (Ñ‚&акт): &Channel: Кана&л: Metronome MIDI channel MIDI-канал метронома Whether to enable the MIDI metronome ИÑпользовать ли MIDI-метроном &Enable MIDI metronome &Включить MIDI-метроном &Display &Ð˜Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Options Параметры Whether to ask for confirmation on removal Запрашивать ли подтверждение на удаление &Confirm removals Спрашивать подтверждение на &удаление The maximum number of recent files to keep in menu Whether to capture standard output (stdout/stderr) into messages window Capture standard &output За&хватывать оÑновной порт вывода Whether to show the complete directory path of loaded session files Whether to remove audio peak files on session close Auto-remove audio pea&k files &ÐвтоматичеÑки удалÑть пиковые звуковые файлы Whether to keep all tool windows on top of the main window Time display format Формат Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð¸: Frames Выборки Time Ð’Ñ€ÐµÐ¼Ñ BBT BBT Whether to try dropping multiple audio files into the same track &Base font size: &Кегль шрифта в окнах: Base application font size (pt.) Кегль шрифта в окнах Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ (pt) (default) (по умолчанию) 6 6 7 7 8 8 9 9 10 10 11 11 12 12 Session СеÑÑÐ¸Ñ Whether to create new sessions based on template Создавать ли новые ÑеÑÑии на оÑнове шаблона New session template Шаблон новых ÑеÑÑий Browse for new session template Указать шаблон новых ÑеÑÑий Whether to save backup versions of existing sessions Which mode to rename existing session files Whether to enable session auto-save (crash-recovery) Auto-save current working session every: ÐвтоÑохранение ÑеÑÑии каждые: Auto-save period (minutes) ЧаÑтота автоÑÐ¾Ñ…Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð² минутах minutes минут Whether to ask for confirmation on archive directory removal C&onfirm archive removals Подтверждать удаление &архивов Number of &recent files: &Сколько поÑледних файлов помнить: S&how complete path of session files По&казывать полный путь к файлам ÑеÑÑии Keep tool &windows always on top Окна &инÑтрументов вÑегда наверху &Drop multiple audio files into the same track &БроÑать больше одного аудиофайла на одну дорожку Whether to reverse keyboard modifiers role on transport positioning (Shift/Ctrl) Whether to reverse mouse middle-button role on keyboard modifiers (Shift/Ctrl) Re&verse middle-button modifier role (Shift/Ctrl) ПоменÑть &роль модификатора Shift/Ctrl Ð´Ð»Ñ Ð¡ÐšÐœ Whether to keep all editor windows on top of the main window Keep &editor windows always on top Окна редакторов вÑегда &наверху Transport ТранÑпорт Transport &mode: &Режим транÑпорта: Whether to start as timebase master (JACK) &Timebase &Loop recording mode (takes): &Цикличный режим запиÑи (дубли): Loop recording mode (takes) First Первый Last ПоÑледний Whether to use RubberBand formant preserve RubberBand &formant preserve Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine Dedicated au&dition/pre-listening player outputs: От&дельные выходы проÑлушиваниÑ: Whether to auto-connect dedicated audio player outputs Auto-&connect Ð&втоÑоединение Connections Ð¡Ð¾ÐµÐ´Ð¸Ð½ÐµÐ½Ð¸Ñ Whether to warn about audio self-connections &Warn about self-connections &Count-in: Count-in mode Recording ЗапиÑÑŒ Count-in number beats Dedicated a&udio metronome outputs: Отдельные аудиовыходы &метронома: Whether to auto-connect dedicated audio metronome outputs Auto-co&nnect Ð&втоÑоединение Whether to have separate MIDI player output ports Dedicated MIDI p&layer outputs Whether to reset/resend all controllers on playback start &Reset all controllers on playback start MIDI Cloc&k: MIDI Cloc&k: Defaults По умолчанию &Time display format: &Формат показа времени: Whether to use desktop environment native dialogs. Use desktop environment &native dialogs ИÑпользовать &родные файловые диалоги Manage custom color palette themes &Icons theme: Custom icons theme directory Browse for custom icons theme directory St&yle sheet: Custom style sheet (*.qss) Browse for custom style sheet (*.qss) Move down path Blacklist Черный ÑпиÑок Plugin blacklist path Browse plugin blacklist path Add plugin blacklist path Plugin blacklist paths Remove plugin blacklist path Clear plugin blacklist paths &Clear О&чиÑтить Reverse &keyboard modifiers role (Shift/Ctrl) ПоменÑть &меÑтами роль модификаторов Shift/Ctrl &Offset (latency): С&мещение (задержка): Metronome Audio offset (latency) Metronome MIDI duration (bar) Metronome MIDI duration (beat) Metronome MIDI offset (latency) Custom Ðа заказ &Color theme: Ð¦Ð²ÐµÑ‚Ð¾Ð²Ð°Ñ Ñ&хема: Custom color palette theme Wonton Soup DO NOT TRANSLATE KXStudio DO NOT TRANSLATE Custom widget style theme Meters Счетчики &Audio: &Звуковой: Audio meter level Уровень звукового Ñчетчика Over 0 dB 0 Дб 3 dB 3 Дб 6 dB 6 Дб 10 dB 10 Дб Audio meter color Цвет звукового Ñчетчика Select custom audio meter color Указать иной цвет Ð´Ð»Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¾Ð³Ð¾ Ñчетчика ... ... &MIDI: &MIDI: MIDI meter level Уровень Ñчетчика MIDI Peak Пик MIDI meter color Цвет Ñчетчика MIDI Select custom MIDI meter color Указать иной цвет Ð´Ð»Ñ Ñчетчика MIDI Reset meter colors to default СброÑить цвета Ñчетчиков до иÑходных значений &Reset &СброÑить Messages Ð¡Ð¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Sample messages text font display Пример текÑта Ñообщений Select font for the messages text display Указать шрифт Ð´Ð»Ñ Ñообщений &Font... &Шрифт... Whether to keep a maximum number of lines in the messages window УÑтанавливать ли предел хранимых Ñообщений M&essages limit: Пр&едел Ñообщений: Whether to hold auto-scrolling (follow play-head) on edits. &Style theme: &Тема интерфейÑа: &LV2 Presets directory: &Каталог Ñ Ð¿Ñ€ÐµÑетами LV2: LV2 Presets directory (default: ~/.lv2) Browse LV2 Presets directory Dedicated audi&o outputs: СобÑтвенные &аудиовыходы Whether to auto-connect dedicated audio output ports Au&to-connect &ÐвтоматичеÑки подключать Editor Редактор Whether to open plugin's editor (GUI) by default Open plugin's &editor (GUI) by default Открывать ÑобÑтвенный &Ð¸Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ Ð¿Ð»Ð°Ð³Ð¸Ð½Ð° по умолчанию Whether to select plugin's editor (GUI) if more than one are available The maximum number of message lines to keep in view МакÑимальное чиÑло хранимых Ñтрок Ñообщений &Hold auto-scrolling (follow play-head) on edits Пр&идерживать автопрокрутку при редактировании Trac&k color saturation: ÐаÑыщенноÑть &цвета дорожек: Default new track color saturation % % Back lines Ñтрок Logging Ведение журнала Messages log file Файл ждурнала Ñообщений Browse for the messages log file location Указать раÑположение файла журнала Ñообщений Whether to activate a messages logging to file. ВеÑти ли журнал Ñообщений в файле Messages &log file: Файл &журнала Ñообщений: &Plugins &Эффекты Paths РаÑположение Plugin type Тип Ñффекта Plugin path РаÑположение Ñффектов Browse plugin path Указать раÑположение Ñффектов Add plugin path Добавить путь к раÑширениÑм &Add &Добавить Plugin paths РаÑположение Ñффектов Remove plugin path Удалить указатель раÑÐ¿Ð¾Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ñффектов &Remove &Удалить Move up path ПоднÑть указатель в ÑпиÑке &Up &Выше &Down &Ðиже Instruments ИнÑтрументы Whether to have separate audio output ports Создавать ли порты выхода Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ инÑтрумента &Select plugin's editor (GUI) if more than one are available &Выбирать ÑобÑтвенный редактор плагина, еÑли доÑтупно больше одного qtractorPaletteForm Color Themes Цветовые темы Name Ðазвание Current color palette name Ðазвание текущей цветовой темы Save Сохранить Save current color palette name Delete Удалить Delete current color palette name Palette Палитра Current color palette Generate: Сгенерировать: Base color to generate palette Reset СброÑить Reset all current palette colors Import a custom color theme (palette) from file Import... Импорт… Export a custom color theme (palette) to file Export... ЭкÑпорт… Show Details ПодробноÑти Import File - %1 Импортировать файл - %1 Palette files (*.%1) Файлы палитр (*.%1) Save Palette - %1 All files (*.*) Ð’Ñе файлы (*.*) Warning - %1 Предупреждение - %1 Could not import from file: %1 Sorry. Export File - %1 ЭкÑпортировать файл - %1 Some settings have been changed. Do you want to discard the changes? Ðекоторые параметры изменилиÑÑŒ. Ð’Ñ‹ хотите применить изменениÑ? Some settings have been changed: "%1". Do you want to save the changes? qtractorPaletteForm::PaletteModel Color Role Роль цвета Active Ðктивно Inactive Ðеактивно Disabled Отключено qtractorPasteRepeatForm Warning Предупреждение Some settings have been changed. Do you want to apply the changes? Ðекоторые параметры изменилиÑÑŒ. Ð’Ñ‹ хотите применить изменениÑ? Paste Repeat Ð’Ñтавить Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð¾Ð¼ Repeat Повторы &Count: &КоличеÑтво: Repeat count КоличеÑтво повторов &Period: &Период: Repeat period ПериодичноÑть повторов Repeat period format Формат периода повторов Frames Выборки Time Ð’Ñ€ÐµÐ¼Ñ BBT BBT qtractorPluginForm Page %1 Страница: %1 %1 [%2], %3 instance(s), %4 channel(s). (none) (нет) Open Preset Открыть предуÑтановку Preset files (*.%1) Файлы предуÑтановок (*.%1) All files (*.*) Ð’Ñе файлы (*.*) Error Ошибка Preset could not be loaded from file: "%1". Sorry. Save Preset Сохранить предуÑтановку Preset could not be saved to file: "%1". Sorry. Warning Предупреждение About to delete preset: "%1" (%2) Are you sure? Эта предуÑтановка будет удалена: "%1" (%2) Ð’Ñ‹ уверены? Latency: %1 ms (%2 frames) (no latency) &None &Ðет Plugin Properties СвойÑтва плагина Open preset Открыть предуÑтановку Preset name Ðазвание предуÑтановки Save preset Сохранить предуÑтановку Delete preset Удалить предуÑтановку Alias: Plugin alias Edit plugin Изменить параметры Ñффекта Edit Изменить Active Ðктивен About О плагине Outputs (Sends) Выходы (поÑылы) Sends ПоÑылы Inputs (Returns) Входы (возвраты) Returns Возвраты Auto-connect Aux Send Bus: Шина внешнего поÑыла: Manage buses ÐаÑтроить шины ... ... Audio bus I/O matrix I/O Matrix... Direct Access Parameter Direct Access ПрÑмой доÑтуп qtractorPluginListView copy plugin копирование Ñффекта activate all plugins Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ð¸Ñ Ð²Ñех Ñффектов deactivate all plugins Ð´ÐµÐ°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ð¸Ñ Ð²Ñех Ñффектов remove all plugins удаление вÑех Ñффектов Import Plugins Импортировать плагины XML files (*.%1) Файлы XML (*.%1) All files (*.*) Ð’Ñе файлы (*.*) Warning Предупреждение About to remove and import all plugins: "%1" Are you sure? Export Plugins ЭкÑпортировать плагины Aux Send: &Move Here &ПеремеÑтить Ñюда &Copy Here &Скопировать Ñюда C&ancel О&тменить &Add Plugin... &Добавить Ñффект... &Audio &Ðудио Add &Insert Добавить &возврат Add &Aux Send Добавить Aux-&поÑыл &Sends &ПоÑылы &Returns &Возвраты &MIDI &MIDI Add &Controller Ac&tivate &Ðктивировать &Remove &Удалить Pre&set Пре&Ñет Dire&ct Access Пр&Ñмой доÑтуп &None &Ðет &Properties... С&войÑтва... &Edit &Изменить &Import... &Импортировать... E&xport... &ЭкÑпортировать... &Auto-connect Ðвто&Ñоединение Acti&vate All Ðктивировать &вÑе I&nserts Во&звраты Deactivate Al&l Д&еактивировать вÑе Re&move All Уда&лить вÑе Move &Up ПеремеÑтить &выше Move &Down ПеремеÑтить &ниже &Outputs Ð’&ыходы &Dedicated &СобÑтвенный выход qtractorPluginParamWidget Open File Открыть файл qtractorPluginSelectForm GUI GUI EXT EXT RT RT Plugins Плагины Reset filter СброÑить фильтр X X Plugin search string (regular expression) Строка поиÑка Ñффектов (поддерживает регулÑрные выражениÑ) Plugin type Тип Ñффекта Available plugins ДоÑтупные Ñффекты Name Ðазвание Audio Звук MIDI MIDI Control Управление Modes Режимы Path РаÑположение Index Instances Копий Type Тип Plugin scanning in progress... ВыполнÑетÑÑ Ñканирование Ñффектов Rescan for available plugins (refresh) &Rescan &ПроÑканировать заново qtractorSessionForm Warning Предупреждение Session directory does not exist: "%1" Do you want to create it? Каталог ÑеÑÑии не ÑущеÑтвует. "%1" Создать его? Some settings have been changed. Do you want to apply the changes? Ðекоторые параметры изменилиÑÑŒ. Ð’Ñ‹ хотите применить изменениÑ? Session Directory Каталог файлов ÑеÑÑии Session СеÑÑÐ¸Ñ &Name: &Ðазвание: Session name Ðазвание ÑеÑÑии &Directory: &Каталог: Whether to auto-name the session directory Давать ли каталогу ÑеÑÑии название автоматичеÑки &Auto &Ðвто Session directory Каталог файлов ÑеÑÑии Browse for session directory Указать каталог Ñ Ñ„Ð°Ð¹Ð»Ð°Ð¼Ð¸ ÑеÑÑии ... ... &Description: О&пиÑание: Session description ОпиÑание ÑеÑÑии Properties СвойÑтва Time Ð’Ñ€ÐµÐ¼Ñ Sample &Rate: &ЧаÑтота ÑÑмплированиÑ: Sample rate (Hz) ЧаÑтота ÑÑÐ¼Ð¿Ð»Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ (Гц) 44100 44100 48000 48000 96000 96000 192000 192000 &Tempo: Т&емп: T&icks/Beat: &Тиков на долю: Resolution (ticks/beat; tpqn) View Вид &Snap/Beat: Прилипание к &долÑм: Snap/beat Прилипание к долÑм &Pixels/Beat: П&икÑелов на долю: Pixels/beat ПикÑелов на долю &Horizontal Zoom: МаÑштаб по &горизонтали: Horizontal Zoom (%) МаÑштаб по горизонтали (%) % % &Vertical Zoom: МаÑштаб по &вертикали: Vertical Zoom (%) МаÑштаб по вертикали (%) Tempo (BPM) / Signature Темп (BPM) / Размер qtractorShortcutForm Search shortcuts Warning Предупреждение Keyboard shortcut (%1) already assigned (%2). Keyboard shortcuts have been changed. Do you want to apply the changes? MIDI Controller shortcuts have been changed. Do you want to apply the changes? &MIDI Controller... MIDI-контро&ллер… Shortcuts Клавиатурные комбинации Shortcut search string (regular expression) Menu/Action Меню/ДейÑтвие Description ОпиÑание Keyboard Клавиатура MIDI Controller MIDI-контроллер qtractorTakeRangeForm Range ОблаÑть Selection range ОблаÑть Ð²Ñ‹Ð´ÐµÐ»ÐµÐ½Ð¸Ñ &Selection Ð’&ыделение Loop range ОблаÑть петли &Loop &ÐŸÐµÑ‚Ð»Ñ Punch range ОблаÑть врезки &Punch &Врезка Time Ð’Ñ€ÐµÐ¼Ñ BBT BBT Edit range ОблаÑть Ñ€ÐµÐ´Ð°ÐºÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ &Edit &Редактирование Custom range Ð—Ð°ÐºÐ°Ð·Ð½Ð°Ñ Ð¾Ð±Ð»Ð°Ñть &Custom &Ð”Ñ€ÑƒÐ³Ð°Ñ St&art: &Ðачало: Clip start Ðачало клипа Take Range En&d: &Конец: Clip offset Смещение клипа Select Выбрать Current take Текущий дубль Format Формат Time display format Формат показа времени Frames Take %1 Дубль %1 qtractorTempoAdjustForm Tempo Adjust Скорректировать темп Metronome Метроном Tempo/Time signature Темп/Тактовый размер &Detect О&пределить T&ap &ÐаÑтучать Range ОблаÑть &Start: &Ðачало: Range start Ðачало облаÑти &Beats: &Долей: Time Ð’Ñ€ÐµÐ¼Ñ BBT BBT &Length: &ДлительноÑть: &Tempo: Т&емп: R&eset Range length ДлительноÑть облаÑти Range beats A&djust &Поправить Format Формат Time display format Формат показа времени: Frames Warning Предупреждение Some settings have been changed. Do you want to apply the changes? Ðекоторые параметры изменилиÑÑŒ. Ð’Ñ‹ хотите применить изменениÑ? qtractorThumbView Thumb view Панель обзора проекта qtractorTimeScale C C B# B# C# C# Db Db D D D# D# Eb Eb E Fb Fb F F E# E# F# F# Gb Gb G G G# G# Ab Ab A A A# A# Bb Bb B B Cb Cb qtractorTimeScaleForm Warning Предупреждение Some settings have been changed. Do you want to apply the changes? Ðекоторые параметры изменилиÑÑŒ. Ð’Ñ‹ хотите применить изменениÑ? About to remove tempo node: %1 (%2) %3 %4/%5 Are you sure? Some settings have been changed. Do you want to discard the changes? Ðекоторые параметры изменилиÑÑŒ. Ð’Ñ‹ хотите применить изменениÑ? tempo factor Marker Color Цвет маркера &Add &Добавить &Update Об&новить пункт &Remove &Удалить &Refresh О&бновить Bar Такт Time Ð’Ñ€ÐµÐ¼Ñ Tempo Темп &Tempo: Т&емп: T&ap &ÐаÑтучать ритм Marker text ТекÑÑ‚ маркера ... ... Marker color Цвет маркера Tempo Map / Markers Карта темпа / Маркеры Key Tempo (BPM) / Time signature &Key signature: &Размер такта: Key signature (accidentals) Key signature (mode) - Major Мажор Minor Минор &Marker: &Маркер: Tempo &scale factor: &КоÑффициент маÑштаба темпа: Tempo scale factor КоÑффициент маÑштаба темпа App&ly При&менить Refresh tempo map Обновить карту темпа Re&fresh О&бновить Add node Добавить пункт Update node Обновить пункт Remove node Удалить пункт Close this dialog Закрыть Ñтот диалог Close Закрыть Tempo map / Markers Карта темпа / Маркеры Marker Маркер &Bar: &Такт: Bar location Ð’ каком такте T&ime: Ð’Ñ€&ÐµÐ¼Ñ Time/frame location qtractorTimeSpinBox &Frames Ð’Ñ‹&борки &Time &Ð’Ñ€ÐµÐ¼Ñ &BBT &BBT qtractorTrackForm Normal Обычный Bank MSB Банк MSB Bank LSB Банк LSB Patch Патч Custom &Icon... &Drums &Барабаны Drum &Kit &Ð‘Ð°Ñ€Ð°Ð±Ð°Ð½Ð½Ð°Ñ ÑƒÑтановка &Bass &БаÑ-гитара A&coustic Bass &ÐšÐ¾Ð½Ñ‚Ñ€Ð°Ð±Ð°Ñ &Guitar Г&итара &Electric Guitar &Электрогитара &Piano &Пианино &Acoustic Piano &ÐкуÑтичеÑкое фортепиано &Microphone &Микрофон Vi&ntage Microphone &Винтажный микрофон &Speaker &Громкоговоритель &Trumpet Тр&уба &Violin Скр&ипка Warning Предупреждение Some settings have been changed. Do you want to apply the changes? Ðекоторые параметры изменилиÑÑŒ. Ð’Ñ‹ хотите применить изменениÑ? (No instrument) (Ðет инÑтрумента) Image files (%1) Файлы изображений (%1) All files (*.*) Ð’Ñе файлы (*.*) %1 ms (%2 frames) (no latency) Track Icon Значок дорожки Foreground Color Цвет переднего плана Background Color Цвет фона (None) (нет) Track Дорожка &Name: Ð&азвание: Track name description ОпиÑание дорожки Track icon Значок дорожки Type Тип Audio track type Ð—Ð²ÑƒÐºÐ¾Ð²Ð°Ñ Ð´Ð¾Ñ€Ð¾Ð¶ÐºÐ° &Audio &Ð—Ð²ÑƒÐºÐ¾Ð²Ð°Ñ MIDI track type MIDI-дорожка &MIDI &MIDI Input / Output Вход / выход Input bus name Ðазвание шины на входе Output bus name Ðазвание шины выхода Manage buses ÐаÑтроить шины ... ... MIDI / Instrument MIDI / инÑтрумент &Program: &Программа: &Bank: &Банк: Bank &Select Method: СпоÑоб &выбора банка: &Omni Ом&ни MIDI Omni: Capture All Channels MIDI-омни: захватить вÑе каналы &Channel: &Канал: MIDI Channel (1-16) Канал MIDI (1-16) MIDI Patch: Instrument Патч MIDI: инÑтрумент MIDI Patch: Bank Select Method Патч MIDI: ÑпоÑоб выбора банка MIDI Patch: Drum Mode Патч MIDI: режим ударных MIDI Patch: Bank Патч MIDI: банк MIDI Patch: Program Патч MIDI: прогромма View / Colors Вид / цвета &Foreground: П&ередний план: Foreground color Цвет переднего плана Select custom track foreground color Укажите другой цвет переднего плана дорожки Bac&kground: &Фон: Background color Цвет фона Select custom track background color Укажите другой цвет фона дорожки Auto Plugins Эффекты Track plugins Эффекты дорожки Add plugin Добавить Ñффект &Add... &Добавить... Remove plugin Удалить Ñффект &Remove &Удалить Move plugin up ПеремеÑтить Ñффект на одно положение вверх &Up &Выше Move plugin down ПеремеÑтить Ñффект на одно положение вниз &Down &Ðиже Whether to enable plugin latency/delay compensation &Latency compensation &КомпенÑÐ°Ñ†Ð¸Ñ Ð·Ð°Ð´ÐµÑ€Ð¶ÐºÐ¸ Current total latency qtractorTrackList Nr â„– Track Name Ðазвание Bus Шина Ch К Patch Патч Instrument ИнÑтрумент qtractorTrackTime Play-head Edit-head Edit-tail Loop-start Loop-end Punch-in Punch-out Start: %1 End: %2 Length: %3 Ðачало: %1 Конец: %2 ДлительноÑть: %3 qtractorTrackView Zoom in (horizontal) Приблизить по горизонтали Zoom out (horizontal) Отдалить по горизонтали Zoom in (vertical) Приблизить по вертикали Zoom out (vertical) Отдалить по вертикали Zoom reset ИÑходный маÑштаб add clip добавление клипа Start: %1 End: %2 Length: %3 Ðачало: %1 Конец: %2 ДлительноÑть: %3 clip %1 клип %1 fade-in нараÑтание fade-out угаÑание clip stretch раÑÑ‚Ñгивание клипа clip resize Ñмена размера клипа clip repeat повтор клипа %1 automation Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ð¸Ñ %1 %1 clip move automation Ñмещение автоматизации paste automation вÑтавка автоматизации cut вырезание delete удаление split разделение move clip перемещение клипа paste clip вÑтавка клипа qtractorTracks Tracks Дорожки new clip Ñоздание клипа split clip разделение клипа clip normalize Ð½Ð¾Ñ€Ð¼Ð°Ð»Ð¸Ð·Ð°Ñ†Ð¸Ñ ÐºÐ»Ð¸Ð¿Ð° Warning Предупреждение About to remove track: "%1" Are you sure? Будет удалена дорожка: "%1" Ð’Ñ‹ уверены? Audio file import "%1" on %2 %3. Импорт звукового файла "%1" в %2 %3. mute clip quantize квантование transpose транÑпонирование normalize нормировка randomize Ñлучайные Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ resize Ñмена размера rescale timeshift clip import импорт клипа Audio file import: "%1". Импорт звукового файла: "%1". MIDI file import "%1" track-channel %2 on %3 %4. Merge/Export СовмеÑтить/ЭкÑпортировать MIDI files (*.mid *.smf *.midi) Файлы MIDI (*.mid *.smf *.midi) All files (*.*) Ð’Ñе файлы (*.*) clip cross-fade кроÑÑфейд клипа Insert Range insert range insert track range Remove Range Удалить облаÑть remove range remove track range MIDI file import "%1" on %2 %3. Импорт файла MIDI "%1" в %2 %3. MIDI file import: "%1". Импорт файла MIDI: "%1". MIDI file import: "%1", track-channel: %2. Импорт файла MIDI "%1", дорожка-канал: %2. tempo ramp clip merge объединение клипов Merge/Export Audio Clip Объединить/ÑкÑпортировать звуковые клипы Audio clip merge/export: "%1" started... Объединение/ÑкÑпорт звукового клипа: "%1" начат... Audio clip merge/export: "%1" complete. Объединение/ÑкÑпорт звукового клипа: "%1" завершен... Merge/Export MIDI Clip Объединить/ÑкÑпортировать клипы MIDI MIDI clip merge/export: "%1" started... Объединение/ÑкÑпорт клипа MIDI: "%1" начат... MIDI clip merge/export: "%1" complete. Объединение/ÑкÑпорт клипа MIDI: "%1" завершен... qtractor-1.5.9/src/translations/PaxHeaders/qtractor_de.ts0000644000000000000000000000013215101070305020622 xustar0030 mtime=1761898693.096267683 30 atime=1761898693.094267677 30 ctime=1761898693.096267683 qtractor-1.5.9/src/translations/qtractor_de.ts0000644000175000001440000227065515101070305020633 0ustar00rncbcusers QObject Audio: %1 channels, %2 Hz Audio: %1 Kanäle, %2 Hz (%1 dB) (%1 dB) (%1 pan) (%1 Pan) (%1% time stretch) (%1% Zeitdehnung) (%1 semitones pitch shift) (%1 Halbton-Tonhöhenänderung) %1 In %1 Ein %1 Out %1 Aus Audio files (%1) Audiodateien (%1) All files (*.*) Alle Dateien (*.*) %1 (%2) %3 channels, %4 frames, %5 Hz %6 %1 (%2) %3 Kanäle, %4 Frames, %5 Hz %6 Duplex Duplex Output Ausgang Input Eingang None Freie Position (take %1/%2) (Aufnahme %1/%2) [Mute] Name: %1 Name: %1 Start: %1 Offset: %2 End: %3 Length: %4 Anfang: %1 Versatz: %2 Ende: %3 Länge: %4 File: %1 Datei: %1 take %1 Aufnahme %1 reset takes Aufnahmen zurücksetzen clip save clip unlink clip tool %1 Clip-Werkzeug %1 clip record Clip-Aufnahme automation select Automations-Auswahl automation mode Automations-Modus automation play Automation abspielen automation record Automation aufnehmen automation logarithmic Logarithmische Automatisierung automation color Automations-Farbe automation play all Alle Automationen abspielen automation record all Alle Automationen aufzeichnen automation edit Automation bearbeiten automation clear Automation löschen automation clear all Alle Automationen entfernen automation edit list Automationsliste bearbeiten %1 Monitor create bus Erzeuge Bus update bus Aktualisiere Bus delete bus Lösche Bus move bus Verschiebe Bus bus pass-through bus gain bus pan Insert Send/Return pseudo-plugin (Audio) Insert Send/Return pseudo-plugin (MIDI) Send Gain Dry Gain Wet Gain Aux Send (Audio) Aux Send pseudo-plugin (Audio) Aux Send pseudo-plugin (MIDI) (none) (keines) %1 (Audio) %1 (MIDI) Cakewalk Instrument Definition File Cakewalk Instrumenten-Datei File Datei Date Datum %1 Bank %2 %1 - Bank %2 (format %1) MIDI: (Format %1) MIDI: Channel %1 Kanal %1 Track %1 Spur %1 , %1 tracks, %2 tpqn , %1 Spuren, %2 tpqn (%1% vol) MIDI file save: "%1", track-channel: %2. set controller reset controller %1 (format %2) %3 tracks, %4 tpqn %5 %1 (format %2) %3 %1 (Format %2) %3 (default) (voreingestellt) %1 Hz %1 Hz slave %1 (%2) Usage: %1 [options] [session-file] Benutzung: %1 [Einstellungen] [Projektdatei] Options: Einstellungen: Set session identification (uuid) Setze Projektidentifikation (uuid) Show help about command line options Zeige Hilfe zu Kommandozeilenargumenten an Show version information Zeige Versionsinformation an Session file (.qtr) [session-file] Option -s requires an argument (uuid). Signed 16-Bit Signed 24-Bit Signed 32-Bit Float 32-Bit Float 64-Bit SMF Format 0 SMF Format 1 (Any) (Jedes) Activate Aktivieren Aux Send: %1 %1(%2): %3 plugin not found. %1(%2): %3 PlugIn nicht gefunden. add plugin PlugIn hinzufügen add insert Insert hinzufügen add aux-send Aux-Send hinzufügen add MIDI controller aux-send bus Aux-Send-Bus aux-send matrix remove plugin PlugIn entfernen move plugin PlugIn verschieben activate plugin PlugIn aktivieren preset plugin reset plugin PlugIn zurücksetzen plugin program plugin alias dedicated audio outputs Festgelegte Audioausgänge direct access param import plugins session loop session punch session properties Beat Taktschlag add tempo node update tempo node remove tempo node move tempo node add marker Markierung hinzufügen update marker Markierung aktualisieren remove marker Markierung entfernen add key signature update key signature remove key signature move marker Markierung verschieben change time-sig. step input overdub %1 Volume %1 Lautstärke %1 Gain %1 Pan add track Spur hinzufügen remove track Spur löschen duplicate track Spur duplizieren move track Spur verschieben resize track Spurgröße ändern import track Spur impoertieren track properties Spureigenschaften Track assignment failed: Track: "%1" Input: "%2" Output: "%3" Spurzuweisung schlug fehl: Spur: "%1" Eingang: "%2" Ausgang: "%3" track record Spuraufnahme track mute Spur stummschalten track solo Spur auf Solo schalten track monitor Spurüberwachung track gain Spurverstärkung track pan Spurpanorama track instrument Spur-Instrument Automation (%1) Automatisierung (%1) none keiner Automation Automatisierung Unknown Unbenannt Product: Produkt: Vendor: Anbieter: Manual: Support: Version: Version: %1 (*.%2) %1 (*.%2) Copyright: Copyright: Project: Projekt: Select plug-in's editor (GUI): External Extern X11 X11 X11 (native) X11 (nativ) Gtk2 Gtk2 Gtk2 (native) Gtk2 (nativ) Qt4 Qt4 Qt5 Qt5 Other Andere Don't ask this again Nicht mehr nachfragen plugin parameters Open File lv2_ui_request_parameter Datei öffnen Author: Autor: %1: Automation/curve file not found. Name: Name: Category: Kategorie: Categories: Kategorien: %1 Record %1 Mute %1 Solo MIDI Controller: %1, %2, %3 Control (MIDI) MIDI Controller Send pseudo-plugin Value Wert qtractorAudioIOMatrixForm Aux-Send I/O Matrix Warning Warnung Some settings have been changed. Do you want to apply the changes? Einige Einstellungen wurden verändert. Wollen Sie diese übernehmen? qtractorAudioListView Name Name Ch Kanal Frames Rate Rate Time Zeit Path Pfad Open Audio Files Audiodateien öffnen %1: Audio file not found. %1: Audiodatei nicht gefunden. qtractorAudioMixerMeter Gain (dB) Verstärkung (dB) dB dB Pan: %1 Gain: %1 dB Verstärkung: %1 dB qtractorBusForm Bus list Busliste Buses Busse Ch Kanal Mode Modus Bus Bus Properties Eigenschaften &Name: &Name: Bus name Busname &Mode: &Modus: Bus mode Busmodus Input Eingang Output Ausgang Duplex Duplex Bus monitor (pass-through) M&onitor (pass-through) Audio Audio Cha&nnels: Ka&näle: Audio channels Audiokanäle Audio auto-connect &Auto connect &Automatisch verbinden MIDI MIDI MIDI Instrument name MIDI-Instrumentname MIDI SysEx setup MIDI SysEx Konfiguration SysE&x... SysE&x... Input Plugins Input bus plugins Add input plugin &Add... &Hinzufügen... Remove input plugin &Remove En&tfernen Move input plugin up &Up Au&f Move input plugin down &Down A&b Output Plugins Output bus plugins Add output plugin Remove output plugin Move output plugin up Move output plugin down Move bus up towards the top U&p &Auf Move bus down towards the bottom Bus nach unten verschieben Do&wn A&b Create bus Bus erzeugen &Create &Erzeugen Update bus Bus aktualisieren &Update A&ktualisieren Delete bus Bus löschen &Delete &Löschen Close this dialog Diesen Dialog schließen Close Schließen Warning Warnung Some settings have been changed. Do you want to apply the changes? Einige Einstellungen wurden verändert. Wollen Sie diese übernehmen? About to remove bus: "%1" (%2) Are you sure? Bus soll entfernt werden: "%1" (%2) Sind Sie sicher? Some settings have been changed. Do you want to discard the changes? Einige Einstellungen wurden verändert. Wollen Sie diese verwerfen? Move &Up Nach &oben Move &Down Nach &unten (No instrument) (Kein Instrument) (none) (keines) (1 item) (1 Element) (%1 items) (%1 Elemente) qtractorClientListView Readable Clients / Output Ports Lesbare Clients/Ausgänge Writable Clients / Input Ports Beschreibbare Clients/Eingänge qtractorClipForm &Name: &Name: Clip name &File: &Datei: Clip filename Browse for clip file Track/&Channel: Spur/&Kanal: Clip track/channel Clip gain/volume Parameters Parameter Clip start Clip-Anfang Clip offset Clip-Versatz &Panning: Clip length Cliplänge Offs&et: Vers&atz: &Length: &Länge: &Start: &Start: Clip Ausschnitt Forma&t: Forma&t: Time display format Zeitformat Frames Time Zeit BBT Fade In/Out Ein-/Ausblenden Fade &In: &Einblenden: Clip fade-in length Clip fade-in type Fade &Out: &Ausblenden: Clip fade-out length Clip fade-out type Audio Audio Ti&me Stretch: &Zeitdehnung: Clip time-stretch percentage Clip-Zeitdehnung prozentual % % Pitch S&hift: &Tonhöhenverschiebung: Clip pitch-shift in semitones Tonhöhenverschiebung in Halbtönen semitones Halbtöne Whether to use WSOLA time-stretching Aktivieren, um WSOLA-Zeitdehnung zu verwenden &WSOLA time-stretching &WSOLA-Zeitdehnung Whether to use RubberBand formant preserve RubberBand &formant preserve Whether to apply WSOLA quick seek time-stretching Aktivieren, um WSOLA Quick-Seek Zeitdehnung anzuwenden WSOLA quic&k seek WSOLA Quick-Seek Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine &Mute &Stumm &Gain: &Verstärkung: Linear Linear Quadratic 1 Quadratisch 1 Quadratic 2 Quadratisch 2 Quadratic 3 Quadratisch 3 Cubic 1 Kubisch 1 Cubic 2 Kubisch 2 Cubic 3 Kubisch 3 dB dB &Volume: &Lautstärke: new clip Neuen Clip anlegen edit clip Clip bearbeiten Warning Warnung Some settings have been changed. Do you want to apply the changes? Einige Einstellungen wurden verändert. Wollen Sie diese übernehmen? MIDI MIDI MIDI files (*.%1 *.smf *.midi) MIDI-Dateien (*.%1 *.smf *.midi) All files (*.*) Alle Dateien (*.*) %1 Clip File %1 Clip-Datei qtractorConnect Connect Verbinden Disconnect Trennen Disconnect All Alle trennen Refresh Auffrischen qtractorConnectForm Connections Verbindungen Audio Audio Select output client/ports Ausgang Client/Anschluß wählen Select input client/ports Eingang Client/Anschluß wählen Connect currently selected ports Ausgewählte Anschlüsse verbinden &Connect &Verbinden Disconnect currently selected ports Ausgewählte Anschlüsse trennen &Disconnect &Trennen Disconnect all currently connected ports Alle verbundenen Anschlüsse trennen Disconnect &All &Alle trennen Refresh current connections view Ansicht der bestehenden Verbindungen erneuern &Refresh Auf&frischen MIDI MIDI (All) (Alle) qtractorConnections Connections Verbindungen qtractorEditRangeForm Range Bereich Selection range Auswahlbereich &Selection &Auswahl Loop range Schleifenbereich &Loop &Endlosschleife Punch range &Punch Edit range Bearbeitungsbereich &Edit &Bearbeiten Custom range &Custom St&art: St&art: Clip start Clip-Anfang En&d: En&de: Clip offset Clip-Versatz Apply to Loop points in range Auf Schleifen-Punkte innerhalb des Bereichs anwenden L&oop &Endlosschleife Apply to Punch In/Out points in range Auf Punch-In/Out-Punkte innerhalb des Bereichs anwenden Pu&nch Mar&kers Mar&kierungen Te&mpo Map Te&mpo-Map &Format &Format Time display format Zeitformat Time Zeit BBT Frames Edit Range Bearbeitungsbereich Options Einstellungen Apply to clips in range Cl&ips A&utomation A&utomatisierung Apply to Automation nodes in range Apply to Tempo Map nodes in range Apply to location Markers in range qtractorExportClipForm %1 %2 Clips qtractorExportForm Export Exportieren &File: &Datei: Export file name Dateiname für Export Browse export file name Dateiname für Export auswählen File &type: Datei&typ: Audio file type to use on export Dateityp für Export Sample &format: Sample&format: Audio sample format to use on export Sampleformat für Export &Quality: &Qualität: Audio compression quality to use on export Audioqualität für Export File &format: Datei&format: MIDI file format to use on export MIDI-Dateiformat für Export Range Bereich Session range Projektbereich &Session &Projekt Loop range Schleifenbereich &Loop &Endlosschleife Punch range &Punch Edit range Bearbeitungsbereich &Edit Bea&rbeiten Custom range &Custom St&art: &Start: Custom start Custom end En&d: En&de: Outputs Ausgänge Output bus names Bus-Namen der Ausgänge Format Format Time display format Zeitformat Frames Time Zeit BBT Whether to add/import new track(s) with export result &Add new track(s) &Neue Spur(en) hinzufügen Audio Audio MIDI MIDI Export %1 File Exportiere %1-Datei MIDI files (*.%1 *.smf *.midi) MIDI-Dateien (*.%1 *.smf *.midi) All files (*.*) Alle Dateien (*.*) qtractorExportTrackForm %1 %2 Tracks %1 %2 Spuren Warning Warnung The file already exists: "%1" Do you want to replace it? Die Datei existiert bereits: "%1" Ersetzen? Audio file export: "%1" started... Export der Audiodatei: "%1" gestartet... Audio file export: "%1" complete. Export der Audiodatei: "%1" vollständig. Audio file export: "%1" failed. Export der Audiodatei: "%1" gescheitert. MIDI file export: "%1" started... Export der MIDI-Datei: "%1" gestartet... MIDI file export: "%1" complete. Export der MIDI-Datei: "%1" vollständig. MIDI file export: "%1" failed. Export der MIDI-Datei: "%1" gescheitert. qtractorFileListView New Group Neue Gruppe Warning Warnung About to remove %1 file item(s). Are you sure? About to remove %1 item: "%2" Are you sure? group Gruppe file Datei qtractorFileSystem &Home &Up &Auf Al&l Files Al&le Dateien &Session &Projekt &Audio &Audio &MIDI &MIDI H&idden Ver&borgen &Play &Abspielen File System Dateisystem qtractorFiles Audio Audio MIDI MIDI Play file Datei abspielen New &Group... Neue &Gruppe... Add &Files... Dateien hinzu&fügen... Cu&t &Ausschneiden &Copy &Kopieren &Paste &Einfügen Re&name &Umbenennen &Remove En&tfernen Pla&y Abs&pielen Cl&eanup Auf&räumen Ctrl+X Ctrl+X Ctrl+C Ctrl+C Ctrl+V Ctrl+V Del Entf Files Dateien MIDI Files MIDI-Dateien Audio Files Audiodateien qtractorInstrumentForm Instruments Instrumente Files Dateien Path Pfad Names Namen Import from instrument file Aus Instrumentdatei importieren &Import... &Importieren... Remove instrument file Instrumentdatei löschen &Remove En&tfernen Move instrument file up on list order Instrumentdatei nach oben verschieben &Up A&uf Move instrument file down on list order Instrumentdatei nach unten verschieben &Down A&b Export to instrument file Nach Instrumentdatei exportieren E&xport... E&xportieren... Close this dialog Diesen Dialog schließen Close Schließen Import Instrument Files Instrumentdatei importieren Instrument files (*.%1 *.sf2 *.sf3 *.midnam) Instrumentdateien (*.%1 *.sf2 *.sf3 *.midnam) All files (*.*) Alle Dateien (*.*) Export Instrument File Exportiere Instrumentdatei Instrument files (*.%1) Instrumentdateien (*.%1) Warning Warnung The instrument file already exists: "%1" Do you want to replace it? Die Instrumentdatei existiert bereits: "%1" Ersetzen? Instrument settings have been changed. Do you want to apply the changes? Die Einstellungen für die Instrumente wurden geändert. Sollen die Änderungen übernommen werden? Patch Names for Banks Controller Names = %1 Controller-Namen = %1 RPN Names = %1 NRPN Names = %1 Bank Select Method = %1 Patch Names Note Names Controller Names Controller Namen RPN Names NRPN Names Bank Select Methods %1 = %2 %1 = %2 Based On = %1 Normal Bank MSB Bank LSB Patch Patch Unknown Unbenannt qtractorInstrumentMenu (None) (Keine) qtractorMainForm &File &Datei Open &Recent &Zuletzt geöffnet &Edit Bea&rbeiten Select &Mode Auswahl&modus &Select A&uswahl I&nsert &Einfügen Remo&ve E&ntfernen &Track &Spur &State &Status &Navigate &Navigieren Mo&ve &Verschieben &Height &Höhe Impor&t Tracks Spur impor&tieren E&xport Tracks Spur e&xportieren M&ode M&odus A&utomation A&utomatisierung Instrum&ent Instrum&ent T&ools Werk&zeuge Ta&ke &View &Ansicht &Toolbars Werkzeugleisten &Windows &Fenster &Zoom Darstellungsgröße S&nap Einrasten/Taktschlag T&ransport T&ransport Mo&de St&ep &Help &Hilfe &New &Neu New Neu New session Neues Projekt New session file Projektdatei Ctrl+N Strg+N &Open... &Öffnen... Open Öffnen Open session Projekt öffnen Open session from file Projekt aus Datei laden Ctrl+O Strg+O &Save &Speichern Save Speichern Save session Projekt speichern Save session to file Projekt in Datei speichern Ctrl+S Strg+S Save &As... Speichern &unter... Save As Speichern unter Save as Speichern unter Save current session with another file name Aktuelles Projekt unter anderem Dateinamen speichern &Properties... Ei&genschaften... Session Properties Projekteigenschaften Session properties Projekteigenschaften Edit current session properties Bearbeite Eigenschaften des aktuellen Projekts F2 F2 E&xit B&eenden Exit Beenden Exit this application program Dieses Programm beenden &Undo &Rückgängig Undo Rückgängig Undo last action Letzte Aktion rückgängig machen Ctrl+Z Strg+Z &Redo &Wiederherstellen Redo Wiederherstellen Redo last action Letzte Aktion wiederherstellen Ctrl+Shift+Z Strg+Umschalt+Z Cu&t &Ausschneiden Cut Ausschneiden Cut selection to clipboard Auswahl in Zwischenablage ausschneiden Ctrl+X Strg+X &Copy &Kopieren Copy Kopieren Copy selection to clipboard Auswahl in Zwischenablage kopieren Ctrl+C Strg+C &Paste &Einfügen Paste Einfügen Paste clipboard contents Inhalt der Zwischenablage einfügen Ctrl+V Strg+V Past&e Repeat... Wieder&holt einfügen... Paste Repeat Wiederholt einfügen Paste repeat Wiederholt einfügen Paste/repeat clipboard contents Füge Inhalt der Zwischenablage wiederholt ein Ctrl+Shift+V Strg+Umschalt+V &Delete &Löschen Delete Löschen Delete selection Auswahl löschen Del Entf. &Clip Au&sschnitt Clip Ausschnitt Select clip Ausschnitt auswählen Clip selection mode Ausschnittauswahlmodus &Range &Bereich Range Bereich Select range Bereich auswählen Range selection mode Bereichsauswahlmodus R&ectangle R&echteck Rect Select rectangle Rechteck auswählen Rectangular selection mode Rechteckauswahlmodus &Automation &Automatisierung Automation Automatisierung Automation edit mode Automations-Bearbeitungs-Modus &All &Alles Select All Alles auswählen Select all Alles auswählen Mark all as selected Alles als ausgewählt markieren Ctrl+A Strg+A &None &Nichts Select None Nichts auswählen Select none Nichts auswählen Mark all as unselected Alles als nicht ausgewählt markieren Ctrl+Shift+A Strg+Umschalt+A &Invert &Invertieren Select Invert Invertiert auswählen Select invert Invertiert auswählen Invert selection Auswahl invertieren Ctrl+I Strg+I Select Track Spur auswählen Select track Spur auswählen Mark track as selected Markiere Spur als ausgewählt Ctrl+T Strg+T Trac&k Range Sp&urbereich Select Track Range Spurbereich auswählen Select track range Spurbereich auswählen Mark track range as selected Spurbereich als ausgewählt markieren Ctrl+Shift+R Strg+Umschalt+R Select Range Bereich auswählen Mark range as selected Bereich als ausgewählt markieren Ctrl+R Strg+R &Range... &Bereich... Remove Range Bereich entfernen Remove range Bereich entfernen Remove range as selected Markierung wie ausgewählt entfernen Ctrl+Del Strg+Entf Remove Track Range Spurbereich entfernen Remove track range Spurbereich entfernen Remove track range as selected Spurbereich wie ausgewählt entfernen Ctrl+Shift+Del Strg+Umschalt+Entf Insert Range Bereich einfügen Insert range Bereich einfügen Insert range as selected Bereich wie ausgewählt einfügen Ctrl+Ins Strg+Einfg Insert Track Range Spurbereich einfügen Insert track range Spurbereich einfügen Insert track range as selected Spurbereich wie ausgewählt einfügen Ctrl+Shift+Ins Strg+Umschalt+Einfg Sp&lit &Teilen Split Selection Auswahl teilen Split selection Auswahl teilen Split current selection Aktuelle Auswahl teilen Ctrl+Y Strg+Y &Add Track... Spur &hinzufügen... Add Track Spur hinzufügen Add track Spur hinzufügen Add a new track to session Neue Spur zum Projekt hinzufügen Shift+Ins Umschalt+Einfg &Remove Track Spur &löschen Remove Track Spur löschen Remove track Spur löschen Remove current track from session Aktuelle Spur vom Projekt entfernen Shift+Del Umschalt+Entf &Duplicate Track Spur &duplizieren Duplicate Track Spur duplizieren Duplicate track Spur duplizieren Duplicate current track Aktuelle Spur duplizieren Track &Properties... Spurein&geschaften... Track Properties Spureingeschaften Track properties Spureingeschaften Edit current track properties Bearbeite Eigenschaften der aktuellen Spur Shift+F2 Umschalt+F2 &Inputs &Eingänge Track Inputs Spureingänge Track inputs Spureingänge Show current track input bus connections Zeige die Bus-Verbindungen der aktuellen Spur &Outputs &Ausgänge Track Outputs Spurausgänge Track outputs Spurausgänge Show current track output bus connections Zeige die Ausgangs-Bus-Verbindungen der aktuellen Spur &Record &Aufnehmen Record Track Spur aufnehmen Record track Spur aufnehmen Arm current track for recording Aufnahme auf aktueller Spur aktivieren &Mute &Stumm Mute Track Spurr stumm schalten Mute track Spurr stumm schalten Mute current track Aktuelle Spur stumm schalten &Solo Solo Track Solo-Spur Solo track Solo-Spur Solo current track Aktuelle Spur auf Solo schalten M&onitor Monitor Track Monitor track Monitor current track Aktuelle Spur überwachen &First &Erste First Track Erste Spur First track Erste Spur Make current the first track Aktuelle Spur zur ersten Spur machen &Previous &Vorherige Previous Track Vorherige Spur Previous track Vorherige Spur Make current the previous track Aktuelle Spur zur vorherigen Spur machen &Next &Nächste Next Track Nächste Spur Next track Nächste Spur Make current the next track Aktuelle Spur zur nächsten Spur machen &Last &Letzte Last Track Letzte Spur Last track Letzte Spur Make current the last track Aktuelle Spur zur letzten Spur machen N&one &Keine None Track Keine Spur None track Keine Spur None current track Keine aktuelle Spur &Top &Anfang Move Top An den Anfang verschieben Move top An den Anfang verschieben Move current track to top Aktuelle Spur an den Anfang verschieben &Up A&uf Move Up Nach oben verschieben Move up Nach oben verschieben Move current track up Aktuelle Spur nach oben verschieben &Down A&b Move Down Nach unten verschieben Move down Nach unten verschieben Move current track down Aktuelle Spur nach unten verschieben &Bottom &Ende Move Bottom An das Ende verschieben Move bottom An das Ende verschieben Move current track to bottom Aktuelle Spur nach unten verschieben &Increase &Vergrößern Increase Height Höhe vergrößern Increase height Höhe vergrößern Increase track height Spurhöhe vergrößern Ctrl+Shift++ Strg+Umschalt++ &Decrease &Reduzieren Decrease Height Höhe verringern Decrease height Höhe verringern Decrease track height Spurhöhe verringern Ctrl+Shift+- Strg+Umschalt+- &Minimize Minimize Height Minimize height Minimize track height &Reset &Zurücksetzen Height Reset Höhe zurücksetzen Height reset Höhe zurücksetzen Reset track height Setze Spurhöhe zurück Ctrl+Shift+1 Strg+Umschalt+1 Auto &Monitor Automatisch überwachen Auto Monitor Automatisch überwachen Auto monitor Automatisch überwachen Auto-monitor current track Aktuelle Spur automatisch überwachen F6 F6 Auto Dea&ctivate Automatisch deaktivieren Auto Deactivate Automatisch deaktivieren Auto-deactivate plugins PlugIns automatisch deaktivieren Auto-deactivate plugins not producing sound PlugIns, die keinen Klang wiedergeben automatisch deaktivieren Shift+F6 Umschalt+F6 &Audio... &Audio... Inport Audio File Audiodatei importieren Import Audio file Audiodatei importieren Import tracks from Audio file Spur aus Audiodatei importieren &MIDI... &MIDI... Import MIDI File MIDI-Datei importieren Import MIDI file MIDI-Datei importieren Import tracks from MIDI file Spur aus MIDI-Datei importieren Export Audio File Exportiere Audiodatei Export Audio file Exportiere Audiodatei Export tracks to Audio file Spuren in Audiodatei exportieren Export MIDI File MIDI-Datei exportieren Export MIDI file MIDI-Datei exportieren Export tracks to MIDI file Spuren in MIDI-Datei exportieren Log&arithmic Log&arithmisch Automation logarithmic Logarithmische Automatisierung Automation curve logarithmic scale Logarithmische Skala der Automatisierungskurve C&olor... &Farbe... Automation color Automatisierungsfarbe Automation curve color Farbe der Automationskurve &Lock &Sperren Automation lock Automation sperren Lock automation curve Automatisierungskurve sperren &Play &Abspielen Automation playback Automatisierung wiedergeben Playback automation curve Automatisierungskurve wiedergeben Automation record Automatisierung aufzeichnen Record automation curve Automatisierungskurve aufzeichnen &Clear &Löschen Automation clear Automatisierung löschen Clear automation curve Automatisierungskurve löschen Loc&k All Alles s&perren Automation lock all Gesamte Automation sperren Lock all automation curves Alle Automationskurven sperren Play &All All&es abspielen Automation playback all Gesamte Automatisierung wiedergeben Playback all automation curves Alle Automatisierungskurven wiedergeben Rec&ord All Alles a&ufnehmen Automation record all Record all automation curves Alle Automatisierungskurven aufzeichnen C&lear All Alles lösc&hen Automation clear all Clear all automation curves Alle Automatisierungskurven löschen &New... &Neu... New Clip Neuen Clip anlegen New clip Neuen Clip anlegen Create new clip Erzeuge neuen Clip &Edit... &Bearbeiten... Edit Clip Clip bearbeiten Edit clip Clip bearbeiten Edit current clip Aktuellen Clip bearbeiten F4 F4 Mute Clip Mute clip Mute current clip &Unlink &Trennen Unlink Clip Clip trennen Unlink clip Clip trennen Unlink current clip Aktuellen Clip trennen Recor&d &Aufnehmen Record Clip Clip aufnehmen Record clip Clip aufnehmen Record current clip (overdub) Aktuellen Clip aufnehmen (Overdub) &Split Tei&len Split Clip Clip teilen Split clip Clip teilen Split current clip at playhead Aktuellen Clip an Abspielposition teilen &Merge... &Zusammenfügen... Merge Clips Clips zusammenfügen Merge clips Clips zusammenfügen Merge selected clips Ausgewählte Clips zusammenfügen Normali&ze Normali&sieren Normalize Clip Clip normalisieren Normalize clip Clip normalisieren Normalize current clip (gain/volume) Aktuellen Clip normalisieren (Gain/Volume) &Quantize... &Quantisieren... Quantize Clip Clip quantisieren Quantize clip events Quantisiere Clip-Elemente Quantize current MIDI clip events Quantisiere die aktuellen Clip-Elemente &Transpose... &Transponieren... Transpose Clip Clip transponieren Transpose clip events Transponiere Clip-Elemente Transpose current MIDI clip events Transponiere die aktuellen Clip-Elemente &Normalize... N&ormalisieren... Normalize clip events Normalisiere Clip-Elemente Normalize current MIDI clip events Normalisiere die aktuellen Clip-Elemente &Randomize... &Zufällig anordnen... Randomize Clip Clip zufällig anordnen Randomize clip events Clip-Elemente zufällig anordnen Randomize current MIDI clip events Die aktuellen Clip-Elemente zufällig anordnen Resi&ze... Größe &anpassen... Resize Clip Größe anpassen Resize clip events Größe der Clip-Elemente anpassen Resize current MIDI clip events Größe der aktuellen Clip-Elemente anpassen Re&scale... &Umskalieren... Rescale Clip Clip umskalieren Rescale clip events Clip-Elemente umskalieren Rescale current MIDI clip events Die aktuellen Clip-Elemente umskalieren T&imeshift... Zeitverschiebung Timeshift Clip Clip-Zeitverschiebung Timeshift clip events Clip-Elemente-Zeitverschiebung Timeshift current MIDI clip events Clip-Elemente-Zeitverschiebung aller MIDI-Elemente T&empo ramp... Tempo ramp Clip Tempo ramp clip events Tempo ramp current MIDI clip events &Tempo Adjust... &Tempoanpassung... Tempo Adjust Tempoanpassung Adjust session tempo from current clip selection Projektgeschwindigkeit aus der aktuellen Clip-Auswahl anpassen F7 F7 &Cross Fade Überkreuz-Blenden Clip Cross-fade Clip Überkreuz-Blenden Clip cross-fade Clip Überkreuz-Blenden Cross-fade current overlapped clips Aktuelle, überlappende Clips überkreuz-blenden &Range Set Bereich festlegen Clip Range Clip-Bereich Clip range Clip-Bereich Set edit-range from current clip extents Bearbeitungsbereich aus den aktuellen Clip-Ausmaßen festlegen &Loop Set Schleife festlegen Clip Loop Clip-Schleife Clip loop Clip-Schleife Set loop-range from current clip extents Schleifenbereich über aktuelle Clip-Ausmaße setzen &Import... &Importieren... Import Clip Clip importieren Import clip Clip importieren Import clip from file(s) Clip aus Datei(en) importieren E&xport... E&xportieren... Export Clip Clip exportieren Export clip Clip exportieren Export current clip to file Aktuellen Clip in Datei exportieren First Take First take Select current clip first take Ersten "Take" des aktuellen Clips auswählen Previous Take Vorheriger "Take" Previous take Vorheriger "Take" Select current clip previous take Vorherigen "Take" des aktuellen Clips auswählen Next Take Nächster "Take" Next take Nächster "Take" Select current clip next take Nächsten "Take" des aktuellen Clips auswählen Shift+T Umschalt+T Last Take Letzer "Take" Last take Letzer "Take" Select current clip last take Letzen "Take" des aktuellen Clips auswählen Reset Takes "Takes" zurücksetzen Reset takes "Takes" zurücksetzen Reset (unfold) current clip takes "Takes" des aktuellen Clips zurücksetzen (aufklappen) R&ange... &Bereich... Take Range "Take"-Bereich Take range "Take"-Bereich Range (fold) current clip into takes Aktuellen Clip in "Takes" einteilen &Menubar &Menüzeile Menubar Menüzeile Show/hide the main program window menubar Menüzeile des Programmfensters ein-/ausblenden Ctrl+M Strg+M &Statusbar &Statuszeile Statusbar Statuszeile Show/hide the main program window statusbar Statuszeile des Programmfensters ein-/ausblenden File Toolbar Datei-Werkzeugleiste File toolbar Datei-Werkzeugleiste Show/hide main program window file toolbar Datei-Werkzeugleiste des Programmfensters ein-/ausblenden Edit Toolbar Werkzeugleiste bearbeiten Edit toolbar Werkzeugleiste bearbeiten Show/hide main program window edit toolbar Bearbeitungs-Werkzeugleiste des Programmfensters ein-/ausblenden Track Toolbar Spur-Werkzeugleiste Track toolbar Spur-Werkzeugleiste Show/hide main program window track toolbar Spur-Werkzeugleiste des Programmfensters ein-/ausblenden View Toolbar Werkzeugleiste anzeigen View toolbar Werkzeugleiste anzeigen Show/hide main program window view toolbar Ansichts-Werkzeugleiste des Programmfensters ein-/ausblenden &Options &Einstellungen Options Toolbar Options toolbar Show/hide main program window options toolbar Einstellungen-Werkzeugleiste des Programmfensters ein-/ausblenden Transport Toolbar Transport toolbar Show/hide main program window transport toolbar Transport-Werkzeugleiste des Programmfensters ein-/ausblenden T&ime &Zeit Time Toolbar Time toolbar Show/hide main program window time toolbar Zeit-Werkzeugleiste des Programmfensters ein-/ausblenden Thum&b Miniaturansicht Thumb Toolbar Thumb toolbar Show/hide main program window thumb toolbar Miniaturansicht-Werkzeugleiste des Programmfensters ein-/ausblenden File &System Dateisystem File System Dateisystem File system Dateisystem Show/hide the file system window Dateisystem-Fenster ein-/ausblenden &Files &Dateien Files Dateien Show/hide the files window Dateien-Fenster ein-/ausblenden M&essages &Meldungen Messages Meldungen Show/hide the messages window Meldungsfenster ein-/ausblenden &Connections &Verbindungen Connections Verbindungen Show/hide the connections window Verbindungsübersicht ein-/ausblenden F8 F8 Mi&xer Mi&schpult Mixer Mischpult Show/hide the mixer window Mischpultfenster ein-/ausblenden F9 F9 &In Vergrößern Zoom In Vergrößern Zoom in Vergrößern Ctrl++ Strg++ &Out Verkleinern Zoom Out Verkleinern Zoom out Verkleinern Ctrl+- Strg+- Zoom Reset Normale Größe Zoom reset Normale Größe Ctrl+1 Strg+1 &Horizontal &Horizontal Horizontal Zoom Horizontale Vergrößerung Horizontal zoom Horizontale Vergrößerung Horizontal zoom mode Modus der horizontalen Vergrößerung &Vertical &Vertikal Vertical Zoom Vertikale Vergrößerung Vertical zoom Vertikale Vergrößerung Vertical zoom mode Modus der vertikalen Vergrößerung All Zoom Alles vergrößern All zoom Alles vergrößern All zoom mode Modus für "Alles vergrößern" &Grid &Gitter Grid Gitter Snap grid view mode Gitter im Rasteransichtsmodus &Zebra &Zebra Zebra Zebra Bar zebra view mode Takte im Zebra-Ansichtsmodus Too&l Tips Tooltips Tool tips Tooltips Floating tool tips view mode Schwebender Tooltip-Ansichtsmodus &Refresh Auf&frischen Refresh Auffrischen Refresh views Ansichten auffrischen F5 F5 &Instruments... &Instrumente... Instruments Instrumente Change instrument definitions and files Instrumentdefinitionen und -dateien anpassen &Controllers... &Controller... Controllers Controller Change MIDI controllers configuration Konfiguration der MIDI-Controller anpassen &Buses... &Busse... Buses Busse Change session bus definitions Projekt-Bus-Definitionen ändern Tempo M&ap / Markers... Tempo-M&ap / Markierungen... Tempo Map / Markers Tempo-Map / Markierungen Tempo map / markers Tempo-Map / Markierungen Change session tempo map / markers Tempo-Map / Markierungen ändern &Options... &Einstellungen... Options Einstellungen Change general application program options Allgemeine Programmeinstellungen ändern F12 F12 &Backward &Rückwärts Backward Rückwärts Transport backward Transport rückwärts Backspace Re&wind &Zurückspulen Rewind Zurückspulen Transport rewind Transport zurückspulen F&ast Forward Vorspu&len Fast Forward Vorspulen Fast forward Vorspulen Transport fast forward Transport vorspulen &Forward &Vorwärts Forward Vorwärts Transport forward Transport vorwärts &Loop &Endlosschleife Loop Endlosschleife Transport loop Transport-Endlosschleife Ctrl+Shift+L Strg+Umschalt+L Step Backward Step backward Transport step backward Step Forward Step forward Transport step forward Loop &Set Schleife festlegen Loop Set Schleife festlegen Loop set Schleife festlegen Transport loop set Transportschleife festlegen Ctrl+L Strg+L &Stop &Stopp Stop Stopp Transport stop Play Abspielen Transport play/pause Transport Abspielen/Anhalten Space Leertaste Record Aufnahme Transport record &Punch Punch Punch in/out Punch in/out Transport punch in/out Transport Punch in/out Ctrl+Shift+P Strg+Umschalt+P Punch Se&t Punch festlegen Punch Set Punch festlegen Punch in/out set Punch in/out festlegen Transport punch in/out set Transport Punch in/out festlegen Ctrl+P Strg+P &Count-in Count-in &Metronome &Metronom/Taktell Metronome Metronom/Taktell F&ollow Playhead Abspielposition folgen Follow Playhead Abspielposition folgen Follow playhead Abspielposition folgen A&uto Backward Automatisch an den Anfang Auto Backward Automatisch an den Anfang Auto backward Automatisch an den Anfang &Continue Past End Am Ende des Songs fortfahren Continue Past End Am Ende des Songs fortfahren Continue past end Am Ende des Songs fortfahren Transport mode: None Transport-Modus: Keiner Transport mode set to None Transport-Modus eingestellt auf: Keiner &Slave Slave Transport mode: Slave Transport-Modus: Slave Transport mode set to Slave Transport-Modus eingestellt auf: Slave &Master Master Ausgang Transport mode: Master Transport-Modus: Master Transport mode set to Master Transport-Modus eingestellt auf: Master &Full &Voll Full Voll Transport mode: Full Transportmodus: Voll Transport mode set to Full Transportmodus eingestellt auf: Voll Pa&nic Pa&nik Panic Panik All MIDI tracks shut off (panic) Alle MIDI-Spuren ausschalten (Panik) &Shortcuts... &Tastaturkürzel... Shortcuts Tastaturkürzel Keyboard shortcuts Tastaturkürzel &About... &Über Qtractor... About Über Qtractor Show information about this application program Information über Qtractor anzeigen About &Qt... Über &Qt... About Qt Über Qt Show information about the Qt toolkit Information über die Qt-Bibliothek anzeigen Set current snap to %1 Aktuelles Raster auf %1 einstellen Current time (play-head) Aktuelle Zeit (Abspielposition) Current tempo (BPM) Aktuelles Tempo (BPM) und Taktart Snap/beat Einrasten/Taktschlag Track Spur Current track name Aktueller Spurname MOD Session modification state REC Session record state MUTE Stumm Session muting state SOLO Solo Session soloing state LOOP Schleife Session looping state Session total time Session sample rate Projekt-Samplerate Could not set default session directory: %1 Sorry. Ready Fertig Session XRUN state Projekt Xrun-Status Session buffer size Untitled%1 Unbenannt%1 New session: "%1". Neues Projekt: "%1". Session files (*.%1 *.%2 *.%3) Projektdateien (*.%1 *.%2 *.%3) Session files (*.%1 *.%2) Projektdateien (*.%1 *.%2) Template files (*.%1) Vorlagendateien (*.%1) Archive files (*.%1) Archivdateien (*.%1) All files (*.*) Alle Dateien (*.*) Open Session Projekt öffnen Save Session Projekt speichern Warning Warnung The file already exists: "%1" Do you want to replace it? Die Datei existiert bereits: "%1" Ersetzen? Backup session: "%1" as "%2". Kopie des Projekts: "%1" als "%2". Could not backup existing session: %1 as %2 Sorry. The current session has been changed: "%1" Do you want to save the changes? About to remove archive directory: "%1" Are you sure? Don't ask this again Nicht mehr nachfragen Session closed. Projekt geschlossen. The directory already exists: "%1" Do you want to replace it? Das Verzeichnis existiert bereits: "%1" Wollen sie es ersetzen? Opening "%1"... Öffne "%1"... Session could not be loaded from "%1". Sorry. Projekt konnte von "%1" nicht geladen werden. Open session: "%1". Projekt öffnen: "%1". A directory with same name already exists: "%1" This directory will be replaced, erasing all its current data, when opening and extracting this archive in the future. Do you want to continue? The directory is an extracted archive: "%1" This directory will be removed, erased from all its current data, when closing this session. Do you want to continue? Saving "%1"... Speichere "%1"... Session could not be saved to "%1". Sorry. Projekt konnte nicht nach "%1" gespeichert werden. Save session: "%1". Speichere Projekt: "%1". Oops! Looks like it crashed or did not close properly last time it was run... however, an auto-saved session file exists: "%1" Do you want to crash-recover from it? Hmm! Es sieht so aus, als ob das letzte Projekt nicht geschlossen wurde... wie auch immer, eine automatisch gespeicherte Version existiert: "%1" Möchten Sie die automatisch gespeicherte Version laden? About to clear automation: "%1" Are you sure? About to clear all automation: "%1" Are you sure? take range session Projekt or oder program Programm Information Information Some settings may be only effective next time you start this %1. Einige Einstellungen sind möglicherweise erst aktiv, wenn sie %1 das nächste Mal starten. Player panic! Debugging option enabled. Debugging-Option aktiviert. Ogg Vorbis (libvorbis) file support disabled. Ogg Vorbis (libvorbis) Dateiunterstützung deaktiviert. MPEG-1 Audio Layer 3 (libmad) file support disabled. MPEG-1 Audio Layer 3 (libmad) Dateiunterstützung deaktiviert. Sample-rate conversion (libsamplerate) disabled. Samplerate-konvertierung (libsamplerate) deaktiviert. Pitch-shifting support (librubberband) disabled. Tonhöhenveränderung (librubberband) deaktiviert. Beat-detection support (libaubio) disabled. Taktschlag-Ermittlung (libaubio) deaktiviert. OSC service support (liblo) disabled. LADSPA Plug-in support disabled. LADSPA PlugIn-Unterstützung deaktiviert. DSSI Plug-in support disabled. DSSI PlugIn-Unterstützung deaktiviert. VST2 Plug-in support disabled. VST3 Plug-in support disabled. CLAP Plug-in support disabled. LV2 Plug-in support disabled. LV2 Plug-in support (liblilv) disabled. LV2 PlugIn-Unterstützung deaktiviert. LV2 Plug-in UI support disabled. LV2 PlugIn-UI-Unterstützung deaktiviert. LV2 Plug-in UI support (libsuil) disabled. LV2 PlugIn-UI-Unterstützung (libsuil) deaktiviert. LV2 Plug-in External UI support disabled. LV2 Plug-in MIDI/Event support (DEPRECATED) enabled. LV2 Plug-in MIDI/Atom support disabled. LV2 Plug-in Worker/Schedule support disabled. LV2 Plug-in State support disabled. LV2 plug-in State Make Path support (DANGEROUS) enabled. LV2 Plug-in State Files support disabled. LV2 Plug-in Programs support disabled. LV2 Plug-in MIDNAM support disabled. LV2 Plug-in Presets support disabled. LV2 Plug-in Patch support disabled. LV2 Plug-in Time/position support disabled. LV2 Plug-in Options support disabled. LV2 Plug-in Buf-size support disabled. LV2 Plug-in UI Touch interface support disabled. LV2 Plug-in UI Request-value support disabled. LV2 Plug-in UI Idle interface support disabled. LV2 Plug-in UI Show interface support disabled. LV2 Plug-in UI GTK2 native support disabled. LV2 Plug-in UI GTKMM2 native support disabled. LV2 Plug-in UI X11 native support disabled. JACK Session support disabled. JACK Session nicht unterstützt. JACK Latency support disabled. JACK Metadata support disabled. NSM support disabled. Version Version Using: Qt %1 Website Webseite This program is free software; you can redistribute it and/or modify it Dieses Programm ist freie Software; Sie können es gemäß der under the terms of the GNU General Public License version 2 or later. GNU General Public License weiterverteilen und/oder modifizieren. record clip [modified] [verändert] XRUN Session started. Projekt/Sitzung gestartet. The audio/MIDI engine could not be started. Make sure the JACK/Pipewire audio service and the ALSA Sequencer kernel module (snd-seq-midi) are up and running and then restart the session. The original session sample rate (%1 Hz) is not the same as the current audio engine (%2 Hz). Saving and reloading from a new session file is highly recommended. Die originale Samplerate des Projekts (%1 Hz) ist nicht identisch mit der aktuellen Samplerate (%2 Hz). Speichern unter neuem Namen und erneutes Laden wird dringendst empfohlen. The following issues were detected: %1 Saving into another session file is highly recommended. Die folgenden Probleme sind aufgetreten: %1 Speichern unter neuem Namen wird dringendst empfohlen. &Hold Behalten &Linear &Linear &Spline Take %1 Don't show this again TRACK MONITOR %1 %2 None Keiner Error Fehler XRUN(%1 skipped) XRUN(%1 übersprungen) XRUN(%1): some frames might have been lost. Audio connections change. Audio-Verbindung geändert. Audio self-connection detected! In general, connecting an output bus (or insert send), directly into any input bus (or insert return), is not advisable. It often doesn't work, if at all. MIDI connections change. MIDI-Verbindung geändert. Playing ended. Abspielen beendet. The audio engine has been shutdown. Make sure the JACK audio server (jackd) is up and running and then restart session. Der Audiobetrieb wurde abgeschaltet. Stellen Sie sicher, daß der JACK Audioserver (jackd) gestartet wurde und aktiv ist. Laden Sie das Projekt anschließend erneut. The audio engine buffer size has changed, increased from %1 to %2 frames/period. Reloading the current session file is highly recommended. STOP PLAY FFWD REW REC ON REC OFF RESET LOCATE %1 SHUTTLE %1 STEP %1 TRACK RECORD %1 %2 TRACK MUTE %1 %2 TRACK SOLO %1 %2 Unknown sub-command Unbekanntes Unterkommando Not implemented Nicht implementiert MIDI CTL: %1, Channel %2, Param %3, Value %4 (track %1, gain %2) (track %1, panning %2) START CONTINUE SONGPOS %1 %1 BPM Playing "%1"... Spiele "%1" ab... qtractorMessages Messages Meldungen Logging stopped --- %1 --- Log-Aufzeichung angehalten --- %1 --- Logging started --- %1 --- Log-Aufzeichnung gestartet --- %1 --- qtractorMidiControl Note On Note an Note Off Note aus Key Press Tastendruck Controller Pgm Change Programmwechsel Chan Press Pitch Bend Tonhöhenbeugung RPN NRPN Control 14 Track Gain Spurverstärkung Track Panning Spurpanorama Track Monitor Spurüberwachung Track Record Spuraufnahme Track Mute Spur stummschalten Track Solo Spur auf Solo schalten qtractorMidiControlForm Controllers Controller Controller files Controller-Dateien Files Dateien Path Pfad Import controller files Importiere Controller-Dateien &Import... &Importieren... Remove controller file Entferne Controller-Datei &Remove En&tfernen Move controller file up on list order &Up A&uf Move controller file down on list order &Down A&b &Type T&yp &Channel &Kanal &Parameter &Parameter Trac&k Sp&ur offse&t &limit C&ommand K&ommando Flags MIDI Event type MIDI Channel MIDI-Kanal MIDI Controller (parameter) MIDI parameter (track offset) MIDI-Parameter (Spurversatz) + Track offset Spurversatz Track limit Command action Command delta/momentary D&elta Command feedback &Feedback &Rückmeldung Map/update controller command &Map Controller map Controller-Map Type Typ Channel Kanal Parameter Parameter Track Spur Command Kommando Feedback Rückmeldung Unmap/remove controller command U&nmap Enable all controllers immediate sync (hook) &Sync Reload/apply all controller files Relo&ad Neu &laden Export to controller file Exportiere in Controller-Datei E&xport... E&xportieren... Close this dialog Diesen Dialog schließen Close Schließen Import Controller Files Importiere Controller-Dateien Controller files (*.%1) Controller-Dateien (*.%1) All files (*.*) Alle Dateien (*.*) Warning Warnung About to remove controller file: "%1" Are you sure? Export Controller File Controller-Datei exportieren controller The controller file already exists: "%1" Do you want to replace it? Die Controller-Datei existiert bereits: "%1" Soll diese überschrieben werden? Saved controller mappings may not be effective the next time you start this program. "%1" Do you want to apply to controller files? Controller mappings have been changed. Do you want to save the changes? Wollen sie die Änderungen speichern? Delta qtractorMidiControlObserverForm &Type: T&yp: MIDI event type Cha&nnel: Ka&nal: MIDI channel MIDI Kanal &Parameter: &Parameter: MIDI parameter MIDI Parameter &Logarithmic &Logarithmisch &Feedback &Rückmeldung In&vert &Invertieren &Hook L&atch Control input connections &Inputs &Eingänge Control output connections &Outputs &Ausgänge MIDI Controller MIDI Controller MIDI controller is already assigned. Do you want to replace the mapping? MIDI Controller ist schon zugeordnet. Wollen sie die Zuordnung ersetzen? Some settings have been changed. Do you want to apply the changes? Einige Einstellungen wurden verändert. Wollen Sie diese übernehmen? &MIDI Controller... &MIDI Controller... &Automation &Automatisierung &Lock &Sperren &Play &Abspielen &Record Auf&nehmen &Clear &Löschen qtractorMidiControlPluginWidget &Type: T&yp: MIDI event type Cha&nnel: Ka&nal: MIDI channel MIDI Kanal &Parameter: &Parameter: MIDI parameter MIDI Parameter &Logarithmic &Logarithmisch In&vert &Invertieren &Bipolar qtractorMidiEditEvent Zoom in (horizontal) Vergrößern (horizontal) Zoom out (horizontal) Verkleinern (horizontal) Zoom reset (horizontal) Normale Größe (horizontal) qtractorMidiEditList C%1 C%1 qtractorMidiEditTime Play-head Edit-head Edit-tail Loop-start Loop-end Punch-in Punch-out Start: %1 End: %2 Length: %3 Start: %1 Ende: %2 Länge: %3 qtractorMidiEditView Zoom in (vertical) Vergrößern (vertikal) Zoom out (vertical) Verkleinern (vertikal) Zoom reset (vertical) Normale Größe (vertical) qtractorMidiEditor C C#/Db D D#/Eb E F F#/Gb G G#/Ab A A#/Bb B Acoustic Bass Drum Bass Drum 1 Side Stick Acoustic Snare Hand Clap Electric Snare Low Floor Tom Closed Hi-Hat High Floor Tom Pedal Hi-Hat Low Tom Open Hi-Hat Low-Mid Tom Hi-Mid Tom Crash Cymbal 1 High Tom Ride Cymbal 1 Chinese Cymbal Ride Bell Tambourine Splash Cymbal Cowbell Crash Cymbal 2 Vibraslap Ride Cymbal 2 Hi Bongo Low Bongo Mute Hi Conga Open Hi Conga Low Conga High Timbale Low Timbale High Agogo Low Agogo Cabasa Maracas Short Whistle Long Whistle Short Guiro Long Guiro Claves Hi Wood Block Low Wood Block Mute Cuica Open Cuica Mute Triangle Open Triangle Bank Select (coarse) Modulation Wheel (coarse) Breath Controller (coarse) Foot Pedal (coarse) Portamento Time (coarse) Data Entry (coarse) Volume (coarse) Balance (coarse) Pan Position (coarse) Expression (coarse) Effect Control 1 (coarse) Effect Control 2 (coarse) General Purpose Slider 1 General Purpose Slider 2 General Purpose Slider 3 General Purpose Slider 4 Bank Select (fine) Modulation Wheel (fine) Breath Controller (fine) Foot Pedal (fine) Portamento Time (fine) Data Entry (fine) Volume (fine) Balance (fine) Pan Position (fine) Expression (fine) Effect Control 1 (fine) Effect Control 2 (fine) Hold Pedal (on/off) Portamento (on/off) Soft Pedal (on/off) Legato Pedal (on/off) Hold 2 Pedal (on/off) Sound Variation General Purpose Button 1 (on/off) General Purpose Button 2 (on/off) General Purpose Button 3 (on/off) General Purpose Button 4 (on/off) Effects Level Chorus Level Celeste Level Phaser Level Data Button Increment Data Button Decrement Non-Registered Parameter (fine) Non-Registered Parameter (coarse) Registered Parameter (fine) Registered Parameter (coarse) All Sound Off All Controllers Off Local Keyboard (on/off) All Notes Off Omni Mode Off Omni Mode On Mono Operation Poly Operation Pitch Bend Sensitivity Fine Tune Coarse Tune Tuning Program Tuning Bank Vibrato Rate Vibrato Depth Vibrato Delay Filter Cutoff Filter Resonance Sostenuto Pedal (on/off) Release Time Attack Time Brightness Decay Time Tremolo Level EG Attack EG Decay EG Release Drum Filter Cutoff Drum Filter Resonance Drum EG Attack Drum EG Decay Drum Pitch Coarse Drum Pitch Fine Drum Level Drum Pan Drum Reverb Send Drum Chorus Send Drum Variation Send Modulation Wheel (14bit) Breath Controller (14bit) Foot Pedal (14bit) Portamento Time (14bit) Volume (14bit) Balance (14bit) Pan Position (14bit) Expression (14bit) Effect Control 1 (14bit) Effect Control 2 (14bit) General Purpose Slider 1 (14bit) General Purpose Slider 2 (14bit) General Purpose Slider 3 (14bit) General Purpose Slider 4 (14bit) Chromatic Major Dur Minor Moll Melodic Minor (Asc) Melodic Minor (Desc) Whole Tone Pentatonic Major Pentatonic Minor Pentatonic Blues Pentatonic Neutral Octatonic (H-W) Octatonic (W-H) Ionian Dorian Phrygian Lydian Mixolydian Aeolian Locrian Egyptian Eight Tone Spanish Hawaiian Hindu Hirajoshi Hungarian Major Hungarian Minor Hungarian Gypsy Japanese (A) Japanese (B) Jewish (Adonai Malakh) Jewish (Ahaba Rabba) Jewish (Magen Abot) Oriental (A) Oriental (B) Oriental (C) Roumanian Minor Neapolitan Neapolitan Major Neapolitan Minor Overtone Leading Whole Tone Nine Tone Scale Dominant Seventh Augmented Algerian Arabian (A) Arabian (B) Balinese Chinese Diminished Japanese (Ichikosucho) Japanese (Taishikicho) Javaneese Marva Theta Mela Bhavapriya Mela Chakravakam Mela Chalanata Mela Chitrambari Mela Dharmavati Mela Dhatuvardhani Mela Dhavalambari Mela Divyamani Mela Ganamurti Mela Gangeyabhusani Mela Gavambodhi Mela Gayakapriya Mela Hatakambari Mela Jalarnavam Mela Jhalavarali Mela Jhankaradhvani Mela Jyotisvarupini Mela Kamavarardhani Mela Kantamani Mela Kosalam Mela Latangi Mela Manavati Mela Mararanjani Mela Naganandini Mela Namanarayani Mela Navanitam Mela Nitimati Mela Pavani Mela Ragavardhani Mela Raghupriya Mela Ramapriya Mela Rasikapriya Mela Ratnangi Mela Risabhapriya Mela Rupavati Mela Sadvidhamargini Mela Salagam Mela Sanmukhapriya Mela Sarasangi Mela Senavati Mela Subhapantuvarali Mela Sucharitra Mela Sulini Mela Suryakantam Mela Syamalangi Mela Tanarupi Mela Vagadhisvari Mela Vanaspati Mela Varunapriya Mela Yagapriya Persian Purvi Theta Spanish Gypsy Todi Theta Enigmatic Kumoi Lydian Augmented Pelog Prometheus Prometheus Neapolitan Six Tone Symmetrical Super Locrian Lydian Minor Lydian Diminished Half Diminished Bhairav Yaman Todi Jog Multani Darbari Malkauns Bhoopali Shivaranjani Marwa Minor 5 Major 5 5 5 45 45 457 457 M 6 MIDI Editor cut delete insert range Bereich einfügen remove range Bereich entfernen move edit resize rescale paste Time: %1 Type: Note On (%1) %2 Velocity: %3 Duration: %4 Key Press (%1) %2 Value: %3 Controller (%1) Name: %2 Value: %3 RPN (%1) Name: %2 Value: %3 NRPN (%1) Name: %2 Value: %3 Control 14 (%1) Name: %2 Value: %3 Pgm Change (%1) Chan Press (%1) Pitch Bend (%1) SysEx (%1 bytes) Data: Unknown (%1) Unbekannt (%1) Start: %1 End: %2 Length: %3 Start: %1 Ende: %2 Länge: %3 qtractorMidiEditorForm MIDI Editor &Track &Spur &File &Datei Select &Mode Auswahl&modus &Select Au&swahl I&nsert &Einfügen Remo&ve E&ntfernen &Tools &Werkzeuge &Edit &Bearbeiten &View &Ansicht Instrum&ent Instrum&ent &Toolbars Werkzeugleisten &Windows &Fenster Not&e Type Val&ue Type &Ghost Track Geister-Spur &Zoom Darstellungsgröße S&nap Einrasten/Taktschlag Sc&ale Sk&alieren T&ransport T&ransport &Note St&ep &Help &Hilfe &Save &Speichern Save Speichern Save current MIDI clip to existing file name Save &As... Speichern &unter... Save As Speichern unter Save as Speichern unter Save current MIDI clip with another file name &Mute &Stumm Mute Mute current MIDI clip &Unlink &Trennen Unlink Trennen Unlink current MIDI clip Recor&d A&ufnehmen Record current MIDI clip (overdub) &Inputs &Eingänge Track Inputs Spureingänge Track inputs Spureingänge Show current MIDI clip/track input bus connections &Outputs &Ausgänge Track Outputs Spurausgänge Track outputs Spurausgänge Show current MIDI clip/track output bus connections &Properties... Ei&genschaften... Track Properties Spureingeschaften Track properties Spureingeschaften Edit current MIDI clip/track properties Shift+F2 Umschalt+F2 Properties Eigenschaften Edit current MIDI clip properties F4 F4 &Range Set Bereich festlegen Clip Range Clip-Bereich Clip range Clip-Bereich Set edit-range from clip extents &Loop Set Schleife festlegen Clip Loop Clip-Schleife Clip loop Clip-Schleife Set loop-range from clip extents &Close &Schließen Close Schließen Close this MIDI clip editor &Undo &Rückgängig Undo Rückgängig Undo last edit operation Ctrl+Z Strg+Z &Redo &Wiederherstellen Redo Wiederherstellen Redo last edit operation Ctrl+Shift+Z Strg+Umschalt+Z Cu&t &Ausschneiden Cut Ausschneiden Cut current selection into the local clipboard Ctrl+X Strg+X &Copy &Kopieren Copy Kopieren Copy current selection to the local clipboard Aktuelle Auswahl in Zwischenablage kopieren Ctrl+C Strg+C &Paste &Einfügen Paste Einfügen Paste local clipboard contents into the current MIDI clip Ctrl+V Strg+V Past&e Repeat... Wiederhol&t einfügen... Paste Repeat Wiederholt einfügen Paste repeat Wiederholt einfügen Paste/repeat local clipboard contents into the current MIDI clip Ctrl+Shift+V Strg+Umschalt+V &Delete &Löschen Delete Löschen Delete current selection Aktuelle Auswahl löschen Del Entf Edit Of&f Bearbeiten a&us Edit Off Bearbeiten aus Edit off Bearbeiten aus Set edit mode off Bearbeitungsmodus ausschalten Edit &On Bearbeiten &an Edit On Bearbeiten an Edit on Bearbeiten an Set edit mode on Bearbeitungsmodus einschalten Edit &Draw Edit draw mode Edit draw mode (notes) &All &Alles Select All Alles auswählen Select all Alles auswählen Ctrl+A Strg+A &None &Nichts Select None Nichts auswählen Select none Nichts auswählen Ctrl+Shift+A Strg+Umschalt+A &Invert &Invertieren Select Invert Invertieren auswählen Select invert Invertieren auswählen Ctrl+I Strg+I &Range &Bereich Select Range Bereich auswählen Select range Bereich auswählen Mark range as selected Bereich als ausgewählt markieren Ctrl+R Strg+R Insert Range Bereich einfügen Insert range Bereich einfügen Insert range as selected Bereich wie ausgewählt einfügen Ctrl+Ins Strg+Einfg &Step Insert step Insert step (rest) Right DO NOT TRANSLATE Remove Range Bereich entfernen Remove range Bereich entfernen Remove range as selected Markierten Bereich entfernen Ctrl+Del Strg+Entf &Quantize... &Quantisieren... Quantize Quantisieren Quantize selection Auswahl quantisieren &Transpose... &Transponieren... Transpose Transponieren Transpose selection Auswahl transponieren &Normalize... N&ormalisieren... Normalize Normalisieren Normalize selection Auswahl normalisieren &Randomize... &Zufällig anordnen... Randomize Zufällig anordnen Randomize selection Auswahl zufällig anordnen Resi&ze... Länge/Dauer &anpassen... Resize Größe anpassen Resize selection Größe des gewählten Bereichs anpassen Re&scale... &Umskalieren... Rescale Umskalieren Rescale selection Auswahl umskalieren T&imeshift... Zeitverschiebung Timeshift Zeitverschiebung Timeshift selection Zeitverschiebung der Auswahl T&empo ramp... Tempo ramp Tempo ramp selection &Menubar &Menüzeile Menubar Menüzeile Show/hide the menubar Menüzeile ein-/ausblenden Ctrl+M Strg+M &Statusbar &Statuszeile Statusbar Statuszeile Show/hide the statusbar Statuszeile ein-/ausblenden File Toolbar Datei-Werkzeugleiste File toolbar Datei-Werkzeugleiste Show/hide the file toolbar Edit Toolbar Werkzeugleiste bearbeiten Edit toolbar Werkzeugleiste bearbeiten Show/hide the edit toolbar View Toolbar Werkzeugleiste anzeigen View toolbar Werkzeugleiste anzeigen Show/hide the view toolbar &Transport &Transport Transport Toolbar Transport toolbar Show/hide the transport toolbar T&ime &Zeit Time Toolbar Time toolbar Show/hide the time toolbar &Scale &Skalieren Scale Toolbar Scale toolbar Show/hide the scale toolbar Thum&b Miniaturansicht Thumb Toolbar Thumb toolbar Show/hide the thumb view toolbar Note &Names Note Names Note names Whether to show note names Note &Duration Note Duration Note duration Whether note events are shown proportional to duration Note &Color Note Color Note color Whether note events are colored according to pitch &Value Color Value Color Value color Whether note events are colored according to value (velocity) &Drum Mode Schlagzeug-Modus Drum Mode Schlagzeug-Modus Drum mode Schlagzeug-Modus Whether note onset events are displayed as diamonds Aktivieren, um Schlagzeugnoten als Diamanten darzustellen &Events View events Show/hide the events list &Preview Notes Notenvorschau Preview Notes Notenvorschau Preview notes Notenvorschau Preview notes while editing (scrub) Notenvorschau während der Bearbeitung (Scrub) F&ollow Playhead Abspielposition folgen Follow Playhead Abspielposition folgen Follow playhead Abspielposition folgen &In Vergrößern Zoom In Vergrößern Zoom in Vergrößern Ctrl++ Strg++ &Out Verkleinern Zoom Out Verkleinern Zoom out Verkleinern Ctrl+- Strg+- &Reset &Zurücksetzen Zoom Reset Normale Größe Zoom reset Normale Größe Ctrl+1 Strg+1 &Horizontal &Horizontal Horizontal Zoom Horizontale Vergrößerung Horizontal zoom Horizontale Vergrößerung Horizontal zoom mode Modus der horizontalen Vergrößerung &Vertical &Vertikal Vertical Zoom Vertikale Vergrößerung Vertical zoom Vertikale Vergrößerung Vertical zoom mode Modus der vertikalen Vergrößerung All Zoom Alles vergrößern All zoom Alles vergrößern All zoom mode Modus für "Alles vergrößern" &Zebra &Zebra Zebra Zebra Bar zebra view mode Takte im Zebra-Ansichtsmodus &Grid &Gitter Grid Gitter Snap grid view mode Gitter im Rasteransichtsmodus Too&l Tips Tooltips Tool tips Tooltips Floating tool tips view mode Schwebender Tooltip-Ansichtsmodus &Refresh Au&ffrischen Refresh Erneuern Refresh views Ansichten auffrischen F5 F5 &Backward &Rückwärts Backward Rückwärts Transport backward Transport rückwärts Backspace Re&wind &Zurückspulen Rewind Zurückspulen Transport rewind Transport zurückspulen F&ast Forward Vorspu&len Fast Forward Vorspulen Fast forward Vorspulen Transport fast forward Transport vorspulen &Forward &Vorwärts Forward Vorwärts Transport forward Transport vorwärts Step Backward Step backward Transport step backward Step Forward Step forward Transport step forward Note Backward Step note backward Transport step note backward Note Forward Step note forward Transport step note forward &Loop &Endlosschleife Loop Endlosschleife Transport loop Transport-Endlosschleife Ctrl+Shift+L Strg+Umschalt+L Loop &Set Schleife festlegen Loop Set Schleife festlegen Loop set Schleife festlegen Transport loop set Transportschleife festlegen Ctrl+L Strg+L &Stop &Stopp Stop Stopp Transport stop &Play &Abspielen Play Abspielen Transport play/pause Transport Abspielen/Anhalten Space Leertaste &Record A&ufnehmen Record Aufnahme Transport record &Punch Punch Punch in/out Punch in/out Transport punch in/out Transport Punch in/out Ctrl+Shift+P Strg+Umschalt+P Punch Se&t Punch festlegen Punch Set Punch festlegen Punch in/out set Punch in/out festlegen Transport punch in/out set Transport Punch in/out festlegen Ctrl+P Strg+P Pa&nic Pa&nik Panic Panik All MIDI tracks shut off (panic) Alle MIDI-Spuren ausschalten (Panik) &Shortcuts... &Tastaturkürzel... Shortcuts Tastaturkürzel Keyboard shortcuts Tastaturkürzel &About... &Über Qtractor... About Über Qtractor Show information about this application program Information über Qtractor anzeigen About &Qt... Über &Qt... About Qt Über Qt Show information about the Qt toolkit Information über die Qt-Bibliothek anzeigen Current time (play-head) Aktuelle Zeit (Abspielposition) Current tempo (BPM) Aktuelles Tempo (BPM) und Taktart Reset time-sig. Set current snap to %1 Aktuelles Raster auf %1 einstellen Note Velocity Anschlagsstärke Snap/beat Einrasten/Taktschlag Note type Notentyp Value type Parameter type Scale key Scale type MIDI clip name MIDI-Clip-Name MIDI file name MIDI-Dateiname MIDI track/channel MIDI Spur/Kanal MOD MIDI modification state REC MIDI clip record state MIDI-Clip-Aufnahmestatus MUTE Stumm MIDI clip mute state 00:00:00.000 00:00:00.000 MIDI clip duration MIDI-Clip-Dauer Warning Warnung The current MIDI clip has been changed: "%1" Do you want to save the changes? Der aktuelle MIDI-Clip wurde verändert: "%1" Möchten Sie die Änderungen speichern? Save MIDI Clip MIDI-Clip speichern MIDI files (*.%1 *.smf *.midi) MIDI-Dateien (*.%1 *.smf *.midi) All files (*.*) Alle Dateien (*.*) Channel %1 Kanal %1 Track %1 Spur %1 [modified] [verändert] qtractorMidiEventList Events Elemente qtractorMidiEventListView::ItemDelegate edit %1 bearbeite %1 qtractorMidiEventListView::ItemModel Time Zeit Type Typ Name Name Value Wert Duration/Data Dauer/Daten Frame BBT Note On (%1) Note an (%1) Note Off (%1) Note aus (%1) Key Press (%1) Controller (%1) Control 14 (%1) RPN (%1) NRPN (%1) Pgm Change Programmwechsel Chan Press Pitch Bend Tonhöhenbeugung SysEx Meta (%1) Unknown (%1) Unbekannt (%1) qtractorMidiListView Name Name Fmt Format Tracks Spuren tpqn Path Pfad %1: MIDI file not found. %1: MIDI-Datei nicht gefunden. Open MIDI Files MIDI-Dateien öffnen MIDI files (*.%1 *.smf *.midi) MIDI-Dateien (*.%1 *.smf *.midi) All files (*.*) Alle Dateien (*.*) qtractorMidiMixerMeter Volume (%) % % Pan: %1 Volume: %1% Lautstärke: %1% qtractorMidiSysexForm MIDI SysEx Name Name Size Größe Data (hex) Daten (hex) Import from SysEx file Importieren aus SysEx-Datei &Import... &Importieren... Export to SysEx file Exportieren in SysEx-Datei E&xport... E&xportieren... Move SysEx item up on list order &Up A&uf Move SysEx item down on list order &Down A&b Open SysEx Sysex name Save SysEx Delete SysEx Create SysEx item &Add &Hinzufügen Update SysEx item Upda&te A&ktualisieren Remove SysEx item &Remove En&tfernen SysEx files (*.%1) SysEx-Dateien (*.%1) MIDI files (*.mid *.smf *.midi) MIDI-Dateien (*.mid *.smf *.midi) All files (*.*) Alle Dateien (*.*) Import SysEx Files SysEx-Dateien importieren Export SysEx File SysEx-Dateien exportieren Warning Warnung The SysEx file already exists: "%1" Do you want to replace it? About to replace SysEx: "%1" Are you sure? About to delete SysEx: "%1" Are you sure? SysEx settings have been changed. Do you want to apply the changes? Error Fehler SysEx could not be loaded: "%1". Sorry. qtractorMidiThumbView MIDI Thumb view MIDI-Miniaturansicht qtractorMidiToolsForm MIDI Tools MIDI-Werkzeuge Preset name Save preset Delete preset &Quantize &Quantisieren Quantize selected events Quantisiere ausgewählte Elemente &Time: &Zeit: Quantize time Zeitlich quantisieren Quantize time percent Zeitlich quantisieren (%) % % &Duration: &Dauer: Quantize duration Länge/Dauer quantisieren Quantize duration percent Länge/Dauer quantisieren (%) S&wing: Swing Swing-quantize time Swing quantisieren Swing-quantize percent Swing quantisieren (%) Swing-quantize type Swing-Quantisierungs-Typ Linear Linear Quadratic Quadratisch Cubic Kubisch &Scale: Skalieren Scale-quantize key Scale-quantize type &Transpose &Transponieren Transpose selected events Transponiere ausgewählte Elemente &Note: Transpose note Transponiere Note Transpose time Transpose time format Frames Time Zeit BBT &Reverse Umkehren &Normalize &Normalisieren Normalize selected events Normalisiere ausgewählte Elemente &Percent: &Prozent: Normalize percent Normalisiere prozentual &Value: &Wert: Normalize value &Compress &Randomize &Zufällig anordnen Randomize selected events Ausgewählte Elemente zufällig anordnen Randomize note/pitch Randomize time Randomize duration Randomize value Resi&ze Länge/Dauer &anpassen Resize selected events Größenänderung ausgewählter Elemente Resize duration Dauer anpassen Resize duration format Resize value Resize value mode Flat Ramp Resize final value &Legato: Legato trim/extend type Normal Trim Extend Legato trim/extend length Legato mode Mono Poly &Join &Split: Split notes length Split notes offset Relative Absolute Re&scale &Umskalieren Rescale selected events Ausgewählte Elemente umskalieren Rescale time Rescale duration Rescale value &Invert &Invertieren T&imeshift Zeitverschiebung Timeshift selected events Zeitverschiebung ausgewählter Elemente Timeshift Zeitverschiebung P: Timeshift parameter Parameter der Zeitverschiebung Timeshift parameter (log) Parameter der Zeitverschiebung (log) Timeshift curve Zeitverschiebungskurve P = 0 : no change. P > 0 : accelerating shift. P < 0 : slowing down shift. Edit head/tail (blue) markers define the shift range. Timeshift duration Zeitverschiebungsdauer T&empo ramp Tempo ramp selected events Tempo ramp From Tempo ramp start to Temporamp end Edit head/tail (blue) markers define the ramp range. Tempo ramp duration (default) (voreingestellt) Warning Warnung About to delete preset: "%1" Are you sure? none keiner quantize Quantisieren transpose Transponieren normalize Normalisieren randomize resize rescale timeshift Zeitverschiebung temporamp qtractorMixer Inputs Eingänge Tracks Spuren Outputs Ausgänge Mixer Mischpult qtractorMixerMeter Pan qtractorMixerRackWidget &Inputs &Eingänge &Outputs &Ausgänge &Monitor &Monitor &Buses... &Busse... &Audio &Audio &MIDI &MIDI qtractorMixerStrip inputs Eingänge outputs Ausgänge Connect %1 Verbinde %1 (Audio) (Audio) (MIDI) (MIDI) (None) (Keine) In Out qtractorMonitorButton monitor Monitor qtractorOptionsForm &General All&gemeines Session Projekt Default session &file format: Standardformat der Projektdatei: Default session file format (suffix) Standardformat der Projektdatei (Dateiendung) Whether to create new sessions based on template Neues Projekt basierend auf einer Vorlage erstellen &New session template: Neue Projektvorlage: New session template Neue Projektvorlage Browse for new session template Projektvorlage aussuchen Whether to save backup versions of existing sessions Save &backup versions of existing sessions: Sicherheitskopien von Projekten speichern Which mode to rename existing session files Modus der Umbenennung von Projekten Increment previous version (default) Erhöhung der vorherigen Version (Standard) Increment current version Erhöhung der aktuellen Version Whether to enable session auto-save (crash-recovery) Aktivieren, um die automatische Speicherung zu aktivieren Auto-save current working session every: Projekt automatisch speichern, alle Auto-save period (minutes) Zeitintervall der automatischen Speicherung minutes Minuten Options Einstellungen Whether to ask for confirmation on removal Aktivieren, um das Löschen zu bestätigen &Confirm removals &Löschen bestätigen Number of &recent files: Anzahl zuletzt geladener Dateien The maximum number of recent files to keep in menu Maximale Anzahl zuletzt geladener Dateien im Menü Whether to capture standard output (stdout/stderr) into messages window Aktivieren, um die Standardausgabe (stdout/stderr) in das Meldungsfenster umzuleiten Capture standard &output Standardausgabe &umleiten Whether to show the complete directory path of loaded session files Aktivieren, um den vollständigen Pfad der geladenen Projektdateien anzuzeigen S&how complete path of session files &Vollständigen Pfad der geladenen Projektdateien anzeigen Whether to remove audio peak files on session close Aktivieren, um Peak-Dateien automatisch zu entfernen Auto-remove audio pea&k files Peak-Dateien automatisch entfernen Whether to keep all tool windows on top of the main window Aktivieren, um Werkzeugfenster immer auf oberster Ebene zu halten Keep tool &windows always on top Werkzeugfenster immer auf oberster Ebene halten Whether to try dropping multiple audio files into the same track Aktivieren, um mehrere Audiodateien in dieselbe Spur einzufügen &Drop multiple audio files into the same track Mehrere Audiodateien in dieselbe Spur einfügen Whether to reverse keyboard modifiers role on transport positioning (Shift/Ctrl) Aktivieren, um die Funktion der Tastaturmodifikatoren umzukehren (Shift/Ctrl) Whether to reverse mouse middle-button role on keyboard modifiers (Shift/Ctrl) Aktivieren, um die Funktion der mittleren Taste umzukehren (Shift/Ctrl) Re&verse middle-button modifier role (Shift/Ctrl) Funktion der mittleren Taste umkehren (Shift/Ctrl) Transport Transport Transport &mode: Transport&modus: Transport control mode (JACK) None Keiner Slave Master Ausgang Full Voll &Loop recording mode (takes): Schleifenaufnahme-Modus (Takes) Loop recording mode (takes) Schleifenaufnahme-Modus (Takes) First Erster Last Letzter &Audio &Audio Capture / Export Aufnehmen / Exportieren Audio compression quality to use on capture (record) and export Audioqualität für Aufnahme und Export Audio sample format to use on capture (record) and export Sampleformat für Aufnahme und Export Audio file type to use on capture (record) and export Dateityp für Aufnahme und Export File &type: Datei&typ: Whether to keep all editor windows on top of the main window Aktivieren, um Editorfenster immer auf oberster Ebene zu halten Keep &editor windows always on top Editorfenster immer auf oberster Ebene halten Sample &format: Sampleformat &Quality: &Qualität: Playback Wiedergabe Whether to apply time-stretching when tempo changes Aktivieren, um Zeitdehnung bei Tempowechsel hinzuzufügen Aut&omatic time-stretching Automatische Zeitdehnung Sample-&rate converter type: Typ der Samplerate-Konvertierung Sample-rate converter quality Qualität der Samplerate-Konvertierung Sinc (Best Quality) Sinc (beste Qualität) Sinc (Medium Quality) Sinc (mittlere Qualität) Sinc (Fastest) Sinc (geringste Qualität) Zero Order Hold Linear Linear Whether to use WSOLA time-stretching Aktivieren, um WSOLA Zeitdehnung zu verwenden &WSOLA time-stretching WSOLA Zeitdehnung Whether to use RubberBand formant preserve RubberBand &formant preserve Whether to apply WSOLA quick seek time-stretching Aktivieren, um WSOLA Quick-Seek Zeitdehnung anzuwenden WSOLA quic&k seek WSOLA Quick-Seek Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine Whether to have separate audition/pre-listening player output ports Aktivieren, um separate Vorhören-Audioausgänge zur Verfügung zu haben Dedicated au&dition/pre-listening player outputs: Separate Vorhören-Audioausgänge Whether to auto-connect dedicated audio player outputs Aktivieren, um festgelegte Ausgänge für den Audioplayer automatisch zu verbinden Auto-&connect Automatisch verbinden Connections Verbindungen Whether to warn about audio self-connections &Warn about self-connections Metronome Metronom/Taktell Whether to enable the audio metronome Audio-Metronom/Taktell aktivieren &Enable audio metronome &Audio-Metronom/Taktell aktivieren &Count-in: Count-in mode Recording Count-in number beats &File (bar): &Datei (Takt): Metronome Audio filename (bar) Audio-Metronom/Taktell Dateiname (Takt) Browse for sample audio file (bar) Audiodatei auswählen (Takt) &Gain (bar): Verstärkung (Takt) Metronome gain (bar) Metronom/Taktell-Verstärkung (Takt) dB dB &File (beat): Da&tei (Taktschlag): Metronome Audio filename (beat) Audio-Metronom/Taktell Dateiname (Taktschlag) Browse for sample audio file (beat) Audiodatei auswählen (Taktschlag) &Gain (beat): Verstärkung (Taktschlag) Metronome gain (beat) Metronom/Taktell-Verstärkung (Taktschlag) Whether to have separate audio metronome output ports Aktivieren, um separate Ausgänge für das Audio-Metronom/Taktell zur Verfügung zu haben Dedicated a&udio metronome outputs: &Festgelegte Audio-Metronom/Taktell-Ausgänge: Whether to auto-connect dedicated audio metronome outputs Aktivieren, um separate Ausgänge für das Audio-Metronom/Taktell automatisch zu verbinden Auto-co&nnect Automatisch &verbinden &MIDI &MIDI File &format: Datei&format: MIDI file format to use on capture (record) and export &Quantize: &Quantisieren: MIDI capture (record) quantization Queue &timer (resolution): Warteschleifen-Zeitgeber (Auflösung) Queue timer (resolution) Warteschleifen-Zeitgeber (Auflösung) Whether to have separate MIDI player output ports Aktivieren, um separate Ausgänge für den MIDI-Player zur Verfügung zu haben Dedicated MIDI p&layer outputs &Festgelegte MIDI-Player Ausgänge Whether to reset/resend all controllers on playback start Aktivieren, um alle Controller beim Abspielen zurückzusetzen &Reset all controllers on playback start Alle Controller beim Abspielen zurücksetzen Control &MMC: &MMC: MIDI Machine Control (MMC) mode MIDI Machine Control (MMC) Modus Input Eingang Output Ausgang Duplex Duplex &Device: &Gerät: MIDI Machine Control (MMC) device id. MIDI Machine Control (MMC) Geräte ID. &SPP: &SPP: MIDI Song Position pointer (SPP) control mode Kontrollmodus des Song-Positionszeigers MIDI Clock control mode Kontrollmodus des MIDI-Zeitgebers Whether to have separate MIDI control ports Aktivieren, um separate MIDI-Control-Ports zur Verfügung zu haben Dedicated MIDI &control input/output Festgelegter MIDI-Control Ein-/Ausgang Whether to enable the MIDI metronome MIDI-Metronom/Taktell aktivieren &Enable MIDI metronome MI&DI-Metronom/Taktell aktivieren &Channel: &Kanal: Metronome MIDI channel Metronom/Taktell MIDI-Kanal &Note (bar): Note (Takt): Metronome MIDI note (bar) Metronom/Taktell MIDI-Note (Takt) &Velocity (bar): Anschlagsstärke (Takt): Metronome MIDI velocity (bar) MIDI-Metronom/Taktell Anschlagsstärke (Takt) &Duration (bar): &Dauer (Takt): Metronome MIDI duration (bar) MIDI-Metronom/Taktell Dauer (Takt) &Note (beat): Note (Taktschlag): Metronome MIDI note (beat) Metronom/Taktell MIDI-Note (Taktschlag) &Velocity (beat): Anschlagsstärke (Taktschlag): Metronome MIDI velocity (beat) MIDI-Metronom/Taktell Anschlagsstärke (Taktschlag) &Duration (beat): Da&uer (Taktschlag): Metronome MIDI duration (beat) MIDI-Metronom/Taktell Dauer (Taktschlag) Whether to have separate MIDI metronome output port Aktivieren, um einen separaten Ausgang für das MIDI-Metronom/Taktell zur Verfügung zu haben Dedicated M&IDI metronome output Fest&gelegter MIDI-Metronom/Taktell Ausgang Metronome MIDI offset (latency) MIDI-Metronom/Taktell Versatz (Latenz) &Display An&zeige Defaults Voreinstellungen &Time display format: Zeitanzeigeformat Time display format Zeitanzeigeformat Frames Time Zeit BBT &Base font size: &Basisschriftgröße: Base application font size (pt.) Generelle Schriftgröße (pt.) für die Anwendung festlegen (default) (voreingestellt) 6 6 7 7 8 8 9 9 10 10 11 11 12 12 Whether to use desktop environment native dialogs. Aktivieren, um die Standarddialoge der grafischen Arbeitsoberfläche zu verwenden Use desktop environment &native dialogs Verwende die Standarddialoge der grafischen Arbeitsoberfläche Manage custom color palette themes &Icons theme: Custom icons theme directory Browse for custom icons theme directory St&yle sheet: Custom style sheet (*.qss) Browse for custom style sheet (*.qss) Move down path &LV2 Presets directory: Whether to select plugin's editor (GUI) if more than one are available Aktivieren, um die PlugIn-Oberfläche auszuwählen, wenn mehrere vorhanden sind Blacklist Plugin blacklist path Pfad der PlugIn-Blacklist Browse plugin blacklist path Pfad der PlugIn-Blacklist auswählen Add plugin blacklist path Pfad zur PlugIn-Blacklist hinzufügen Plugin blacklist paths Pfade der PlugIn-Blacklist Remove plugin blacklist path Pfad aus der PlugIn-Blacklist entfernen Clear plugin blacklist paths Pfade der PlugIn-Blacklist löschen &Clear &Löschen Custom Benutzerdefiniert &Color theme: Farbthema Custom color palette theme Benutzerdefiniertes Farbpaletten-Thema Custom widget style theme Benutzerdefiniertes Widget-Stil-Thema Meters Messgeräte &Audio: &Audio: Audio meter level Audiopegelanzeige Over 0 dB 0 dB 3 dB 3 dB 6 dB 6 dB 10 dB 10 dB Audio meter color Select custom audio meter color Farbgebung der Anzeige anpassen ... &MIDI: &MIDI: MIDI meter level Peak MIDI meter color Select custom MIDI meter color Reset meter colors to default &Reset &Zurücksetzen Messages Meldungen Sample messages text font display Beispielhafte Darstellung des Textes im Meldungsfenster Select font for the messages text display Schriftart für Text im Meldungsfenster wählen &Font... &Schriftart... Whether to keep a maximum number of lines in the messages window Maximale Anzahl der im Meldungsfenster angezeigten Zeilen festlegen M&essages limit: Nachrichten maximum: The maximum number of message lines to keep in view Maximale Anzahl der Nachrichten im Meldungsfenster lines Zeilen Whether to hold auto-scrolling (follow play-head) on edits. Aktivieren, um Beibehaltung von "Abspielposition folgen" zu aktivieren Whether to ask for confirmation on archive directory removal Aktivieren,um das Löschen von Archiven zu bestätigen C&onfirm archive removals Löschen von Archiven bestätigen Reverse &keyboard modifiers role (Shift/Ctrl) Funktion der Tastaturmodifikatoren umkehren (Shift/Ctrl) Whether to start as timebase master (JACK) Aktivieren, um als Master-Zeitbasis (JACK) zu starten &Timebase Zeitbasis &Offset (latency): Versatz (Latenz) Metronome Audio offset (latency) Audio-Metronom/Taktell-Versatz (Latenz) Whether to enable MIDI queue time drift correction Aktivieren, um die Korrektur der Abdrift der MIDI-Warteschlangenzeit zu aktivieren E&nable MIDI queue time drift correction Korrektur der Abdrift der MIDI-Warteschlangenzeit aktivieren MIDI Cloc&k: MIDI-Zeitgeber &Hold auto-scrolling (follow play-head) on edits Abspielposition folgen bei Bearbeitungen beibehalten Trac&k color saturation: Spurfarbensättigung Default new track color saturation Standard Spurfarbensättigung % % Wonton Soup DO NOT TRANSLATE KXStudio DO NOT TRANSLATE &Style theme: Stilthema Back Zurück Logging Protokollierung Messages log file Protokolldatei für Meldungen Browse for the messages log file location Speicherort für Protokolldatei wählen Whether to activate a messages logging to file. Protokollierung der Meldungen in eine Datei festlegen. Messages &log file: Protokoll&datei: &Plugins &Plugins Paths Pfade Plugin type PlugIn-Typ Plugin path PlugIn-Pfad Browse plugin path PlugIn-Pfad auswählen Add plugin path PlugIn-Pfad hinzufügen &Add &Hinzufügen Plugin paths PlugIn-Pfade Remove plugin path PlugIn-Pfad entfernen &Remove En&tfernen Move up path PlugIn-Pfad nach oben verschieben &Up Au&f &Down A&b LV2 Presets directory (default: ~/.lv2) LV2-Presets Verzeichnis (Standard: ~/.lv2) Browse LV2 Presets directory LV2-Presets Verzeichnis auswählen Instruments Instrumente Whether to have separate audio output ports Aktivieren, um separate Audioausgänge zur Verfügung zu haben Dedicated audi&o outputs: Festgelegte Audioausgänge Whether to auto-connect dedicated audio output ports Aktivieren, um festgelegte Audioausgänge automatisch zu verbinden Au&to-connect Au&tomatisch verbinden Editor Editor Whether to open plugin's editor (GUI) by default Aktivieren, um die PlugIn-Oberfläche standardmäßig zu öffnen Open plugin's &editor (GUI) by default Öffne PlugIn-Oberfläche standardmäßig &Select plugin's editor (GUI) if more than one are available PlugIn-Oberfläche auswählen, wenn mehrere vorhanden sind XML Default (*.%1) XML Regular (*.%1) ZIP Archive (*.%1) (Any) (Jedes) Warning Warnung Some settings have been changed. Do you want to apply the changes? Einige Einstellungen wurden verändert. Wollen Sie diese übernehmen? Metronome Bar Audio File Metronome Beat Audio File Open Style Sheet Style Sheet files (*.%1) Icons Theme Directory Audio Meter Color MIDI Meter Color Plug-in Directory LV2 Presets Directory Plug-in Blacklist Plug-in files (*.%1) Messages Font Messages Log Meldungsprotokoll Log files (*.%1) Log-Dateien (*.%1) All files (*.*) Alle Dateien (*.*) Session Template Session template files (*.qtr *.qts *.%1) qtractorPaletteForm Color Themes Name Name Current color palette name Save Speichern Save current color palette name Delete Löschen Delete current color palette name Palette Current color palette Generate: Base color to generate palette Reset Reset all current palette colors Import a custom color theme (palette) from file Import... Export a custom color theme (palette) to file Export... Exportieren... Show Details Details anzeigen Import File - %1 Palette files (*.%1) Save Palette - %1 All files (*.*) Alle Dateien (*.*) Warning - %1 Warnung - %1 Could not import from file: %1 Sorry. Export File - %1 Some settings have been changed. Do you want to discard the changes? Einige Einstellungen wurden verändert. Wollen Sie diese verwerfen? Some settings have been changed: "%1". Do you want to save the changes? Einige Einstellungen wurden verändert: "%1". Wollen Sie diese speichern? qtractorPaletteForm::PaletteModel Color Role Active Aktiv Inactive Inaktiv Disabled Deaktiviert qtractorPasteRepeatForm Paste Repeat Wiederholt einfügen Repeat Wiederholung &Count: An&zahl: Repeat count &Period: &Periode: Repeat period Repeat period format Frames Time Zeit BBT Warning Warnung Some settings have been changed. Do you want to apply the changes? Einige Einstellungen wurden verändert. Wollen Sie diese übernehmen? qtractorPluginForm Plugin Properties Open preset Preset name Save preset Delete preset Alias: Plugin alias Edit plugin Plugin bearbeiten Edit Bearbeiten Active Aktiv About Über Outputs (Sends) Sends Inputs (Returns) Returns Auto-connect Aux Send Bus: Manage buses Busse verwalten ... Audio bus I/O matrix I/O Matrix... Direct Access Parameter Direct Access Page %1 Seite %1 %1 [%2], %3 instance(s), %4 channel(s). (none) (keines) Open Preset Preset files (*.%1) All files (*.*) Alle Dateien (*.*) Error Fehler Preset could not be loaded from file: "%1". Sorry. Save Preset Preset could not be saved to file: "%1". Sorry. Warning Warnung About to delete preset: "%1" (%2) Are you sure? &None &Nichts Latency: %1 ms (%2 frames) (no latency) qtractorPluginListView copy plugin activate all plugins deactivate all plugins remove all plugins Import Plugins XML files (*.%1) All files (*.*) Alle Dateien (*.*) Warning Warnung About to remove and import all plugins: "%1" Are you sure? Export Plugins Aux Send: &Move Here Hierher &verschieben &Copy Here Hierher &kopieren C&ancel &Abbrechen &Add Plugin... &Plugin hinzufügen... I&nserts &Audio &Audio Add &Insert Add &Aux Send &Sends &Returns &MIDI &MIDI Add &Controller Ac&tivate Ak&tivieren Acti&vate All Alle Akti&vieren Deactivate Al&l A&lle Deaktivieren &Remove En&tfernen Re&move All &Alle Entfernen Move &Up Nach &oben Move &Down Nach &unten Pre&set Dire&ct Access &None &Nichts &Properties... &Eigenschaften... &Edit &Bearbeiten &Import... &Importieren... E&xport... E&xportieren... &Outputs &Ausgänge &Dedicated &Auto-connect &Automatisch verbinden qtractorPluginParamWidget Open File Datei öffnen qtractorPluginSelectForm Plugins PlugIns Reset filter Filter zurücksetzen X Plugin search string (regular expression) Plugin type PlugIn-Typ Available plugins Verfügbare PlugIns Name Name Audio Audio MIDI MIDI Control Modes Modi Path Pfad Index Index Instances Instanzen Type Typ Plugin scanning in progress... Rescan for available plugins (refresh) &Rescan GUI EXT RT RT qtractorSessionForm Session Projekt/Sitzung &Name: &Name: Session name Projektname &Directory: &Verzeichnis: Whether to auto-name the session directory Aktivieren, um das Projektverzeichnis automatisch zu benennen &Auto Automatisch Session directory Projektverzeichnis Browse for session directory Projektverzeichnis auswählen ... &Description: &Beschreibung: Session description Projektbeschreibung Properties Eigenschaften Time Zeit Sample &Rate: Abtast&rate: Sample rate (Hz) Abtastrate (Hz) 44100 44100 48000 48000 96000 96000 192000 192000 &Tempo: &Tempo und Taktart: Tempo (BPM) / Signature Tempo (BPM) und Taktart T&icks/Beat: Ticks/Taktschlag: Resolution (ticks/beat; tpqn) Auflösung (Ticks/Taktschlag; tpqn) View Ansicht &Snap/Beat: Einrasten/Taktschlag: Snap/beat Einrasten/Taktschlag &Pixels/Beat: Pixel/Taktschlag: Pixels/beat Pixel/Taktschlag &Horizontal Zoom: Horizontale Vergrößerung: Horizontal Zoom (%) Horizontale Vergrößerung (%) % % &Vertical Zoom: &Vertikale Vergrößerung: Vertical Zoom (%) Vertikale Vergrößerung (%) Warning Warnung Session directory does not exist: "%1" Do you want to create it? Projektverzeichnis existiert nicht: "%1" Wollen sie es anlegen? Some settings have been changed. Do you want to apply the changes? Einige Einstellungen wurden verändert. Wollen Sie diese übernehmen? Session Directory Projektverzeichnis qtractorShortcutForm Shortcuts Tastaturkürzel Shortcut search string (regular expression) Menu/Action Menü/Aktion Description Beschreibung Keyboard Tastatur MIDI Controller MIDI-Controller Search shortcuts Warning Warnung Keyboard shortcut (%1) already assigned (%2). Tastenkürzel (%1) ist bereits zugeordnet (%2). Keyboard shortcuts have been changed. Do you want to apply the changes? Tastaturkürzel wurden verändert. Wollen Sie diese übernehmen? MIDI Controller shortcuts have been changed. Do you want to apply the changes? &MIDI Controller... &MIDI-Controller... qtractorTakeRangeForm Range Bereich Selection range Auswahlbereich &Selection &Auswahl Loop range Schleifenbereich &Loop &Endlosschleife Punch range &Punch Time Zeit BBT Edit range Bearbeitungsbereich &Edit &Bearbeiten Custom range &Custom St&art: St&art: Clip start Clip-Anfang Take Range "Take"-Bereich En&d: En&de: Clip offset Clip-Versatz Select Auswählen Current take Aktueller "Take" Format Format Time display format Zeitanzeigeformat Frames Take %1 qtractorTempoAdjustForm Tempo Adjust Tempoanpassung Metronome Metronom/Taktell Tempo/Time signature &Detect T&ap Range Bereich &Start: &Start: Range start &Beats: Taktschläge Time Zeit BBT &Length: &Länge: &Tempo: &Tempo: R&eset Range length Range beats A&djust An&passen Format Format Time display format Zeitformat Frames Warning Warnung Some settings have been changed. Do you want to apply the changes? Einige Einstellungen wurden verändert. Wollen Sie diese übernehmen? qtractorThumbView Thumb view qtractorTimeScale C B# C# Db D D# Eb E Fb F E# F# Gb G G# Ab A A# Bb B Cb qtractorTimeScaleForm Tempo map / Markers Tempo-Map / Markierungen Bar Takt Time Zeit Tempo Geschwindigkeit Marker Markierung &Bar: Takt: Bar location Taktort T&ime: &Zeit: Time/frame location &Tempo: &Tempo: T&ap Marker text ... Marker color Tempo Map / Markers Tempo-Map / Markierungen Key Taste Tempo (BPM) / Time signature Tempo (BPM) / Taktart &Key signature: Key signature (accidentals) Key signature (mode) - Major Dur Minor Moll &Marker: Tempo &scale factor: Tempo scale factor App&ly An&wenden Refresh tempo map Re&fresh Au&ffrischen Add node &Add &Hinzufügen Update node &Update A&ktualisieren Remove node &Remove En&tfernen Close this dialog Diesen Dialog schließen Close Schließen Warning Warnung Some settings have been changed. Do you want to apply the changes? Einige Einstellungen wurden verändert. Wollen Sie diese übernehmen? About to remove tempo node: %1 (%2) %3 %4/%5 Are you sure? Some settings have been changed. Do you want to discard the changes? Einige Einstellungen wurden verändert. Wollen Sie diese verwerfen? tempo factor Marker Color &Refresh Au&ffrischen qtractorTimeSpinBox &Frames &Time &Zeit &BBT qtractorTrackForm Track Spur &Name: &Name: Track name description Spurname-Beschreibung Track icon Spur-Ikone Type Typ Audio track type Audio-Spurtyp &Audio &Audio MIDI track type MIDI-Spurtyp &MIDI &MIDI Input / Output Eingang/Ausgang Input bus name Output bus name Manage buses Busse verwalten ... MIDI / Instrument MIDI/Instrument &Program: &Programm: &Bank: &Bank: Bank &Select Method: Bank-Auswahlmethode: &Omni Alle (Omni) MIDI Omni: Capture All Channels MIDI Alle: alle Kanäle aufzeichnen &Channel: &Kanal: MIDI Channel (1-16) MIDI-Kanal (1-16) MIDI Patch: Instrument MIDI Patch: Bank Select Method MIDI-Patch: Bank-Auswahlmethode MIDI Patch: Drum Mode MIDI Patch: Bank MIDI Patch: Program View / Colors Ansicht/Farben &Foreground: &Vordergrund: Foreground color Vordergrundfarbe Select custom track foreground color Bac&kground: Hin&tergrund: Background color Hintergrundfarbe Select custom track background color Auto Plugins PlugIns Track plugins Add plugin PlugIn hinzufügen &Add... &Hinzufügen... Remove plugin Plugin entfernen &Remove En&tfernen Move plugin up &Up A&uf Move plugin down &Down A&b Whether to enable plugin latency/delay compensation &Latency compensation Current total latency Normal Bank MSB Bank LSB Patch Patch &Drums &Schlagzeug Drum &Kit &Bass &Bass A&coustic Bass &Guitar &Gitarre &Electric Guitar &Elektrische Gitarre &Piano &Klavier &Acoustic Piano &Akustisches Klavier &Microphone &Mikrofon Vi&ntage Microphone &Speaker &Lautsprecher &Trumpet &Trompete &Violin &Violine Warning Warnung Some settings have been changed. Do you want to apply the changes? Einige Einstellungen wurden verändert. Wollen Sie diese übernehmen? (No instrument) (Kein Instrument) %1 ms (%2 frames) (no latency) (None) (Keine) Custom &Icon... Image files (%1) All files (*.*) Alle Dateien (*.*) Track Icon Foreground Color Vordergrundfarbe Background Color Hintergrundfarbe qtractorTrackList Nr Nr Track Name Spurname Bus Bus Ch Kanal Patch Patch Instrument Instrument qtractorTrackTime Play-head Edit-head Edit-tail Loop-start Loop-end Punch-in Punch-out Start: %1 End: %2 Length: %3 Start: %1 Ende: %2 Länge: %3 qtractorTrackView Zoom in (horizontal) Vergrößern (horizontal) Zoom out (horizontal) Verkleinern (horizontal) Zoom in (vertical) Vergrößern (vertikal) Zoom out (vertical) Verkleinern (vertikal) Zoom reset Normale Größe add clip Start: %1 End: %2 Length: %3 Start: %1 Ende: %2 Länge: %3 clip %1 fade-in fade-out clip stretch clip resize clip repeat %1 automation %1 clip move automation paste automation cut delete split move clip paste clip qtractorTracks Tracks Spuren new clip Neuen Clip anlegen mute clip split clip Clip teilen clip normalize Clip normalisieren quantize Quantisieren transpose Transponieren normalize Normalisieren randomize resize rescale timeshift Zeitverschiebung clip import Clip Import Audio file import "%1" on %2 %3. Audio-Datei Import "%1" auf %2 %3. Audio file import: "%1". Audio-Datei Import: "%1". MIDI file import "%1" track-channel %2 on %3 %4. MIDI-Datei Import "%1" Spurkanal %2 auf %3 %4. MIDI file import: "%1", track-channel: %2. MIDI-Datei Import: "%1", Spurkanal: %2. clip merge Merge/Export Vereinigen/Exportieren Merge/Export Audio Clip Audio Clip vereinigen/exportieren MIDI files (*.mid *.smf *.midi) MIDI-Dateien (*.mid *.smf *.midi) All files (*.*) Alle Dateien (*.*) Audio clip merge/export: "%1" started... Audio-Clip Vereinigung/Export: "%1" gestartet... tempo ramp Audio clip merge/export: "%1" complete. Audio-Clip Vereinigung/Export: "%1" abgeschlossen. Merge/Export MIDI Clip MIDI-Clip vereinigen/exportieren MIDI clip merge/export: "%1" started... MIDI-Clip Vereinigung/Export: "%1" gestartet... MIDI clip merge/export: "%1" complete. MIDI-Clip Vereinigung/Export: "%1" abgeschlossen. clip cross-fade Clip Clip Überkreuz-Blenden Insert Range Bereich einfügen insert range Bereich einfügen insert track range Spurbereich einfügen Remove Range Bereich entfernen remove range Bereich entfernen remove track range Spurbereich entfernen Warning Warnung About to remove track: "%1" Are you sure? Sie sind dabei, diese Spur zu entfernen: "%1" Sind sie sicher? MIDI file import "%1" on %2 %3. MIDI-Datei Import "%1" auf %2 %3. MIDI file import: "%1". MIDI-Datei Import: "%1". qtractor-1.5.9/src/translations/PaxHeaders/qtractor_cs.ts0000644000000000000000000000013215101070305020637 xustar0030 mtime=1761898693.094267677 30 atime=1761898693.093267673 30 ctime=1761898693.094267677 qtractor-1.5.9/src/translations/qtractor_cs.ts0000644000175000001440000231060415101070305020635 0ustar00rncbcusers QObject Audio: %1 channels, %2 Hz Zvuk: %1 kanály, %2 Hz (%1 dB) (%1 dB) (%1 pan) (%1 Vyvážení (Pan) (%1% time stretch) (%1% protažení Äasu) (%1 semitones pitch shift) (posunutí výšky tónu v půltónech %1) %1 In %1 Vstup %1 Out %1 Výstup Audio files (%1) Zvukové soubory (%1) All files (*.*) VÅ¡echny soubory (*.*) %1 (%2) %3 channels, %4 frames, %5 Hz %6 %1 (%2) %3 kanály, %4 snímky, %5 Hz %6 Name: %1 Název: %1 (take %1/%2) (zábÄ›r %1/%2) [Mute] [Ztlumit] Start: %1 Offset: %2 End: %3 Length: %4 ZaÄátek: %1 Posun %2 Konec: %3 Délka: %4 File: %1 Soubor: %1 create bus VytvoÅ™it sbÄ›rnici update bus Zaktualizovat sbÄ›rnici delete bus Smazat sbÄ›rnici move bus Posunout sbÄ›rnici bus pass-through Průchodnost sbÄ›rnice bus gain Zesílení sbÄ›rnice bus pan Vyvážení sbÄ›rnice Cakewalk Instrument Definition File Soubor s Cakewalk definicí nástroje File Soubor Date Datum %1 Bank %2 %1 Banka %2 %1 - Bank %2 %1 - Banka %2 (format %1) MIDI: (formát %1) MIDI: Channel %1 Kanál %1 Track %1 Stopa %1 , %1 tracks, %2 tpqn , %1 stopy, %2 tpqn (%1% vol) (%1% hlasitost) MIDI file save: "%1", track-channel: %2. Uložení souboru MIDI: "%1", stopa-kanál: %2. %1 (format %2) %3 tracks, %4 tpqn %5 %1 (formát %2) %3 stop, %4 tpqn %5 %1 (format %2) %3 %1 (formát %2) %3 Usage: %1 [options] [session-file] Užití: %1 [volby] [soubor sezení] Options: Volby: Set session identification (uuid) Nastavit oznaÄení sezení (uuid) Show help about command line options Ukázat nápovÄ›du k volbám (argumentům) příkazového řádku Show version information Ukázat informace o verzi Session file (.qtr) Soubor se sezením (.qtr) [session-file] [soubor se sezením] Option -s requires an argument (uuid). Volba -s vyžaduje argument (uuid). Signed 16-Bit OznaÄený znaménkem 16-Bit Signed 24-Bit OznaÄený znaménkem 24-Bit Signed 32-Bit OznaÄený znaménkem 32-Bit Float 32-Bit Plovoucí 32-Bit Float 64-Bit Plovoucí 64-Bit SMF Format 0 SMF Format 0 SMF Format 1 SMF Format 1 (Any) (Jakýkoli) Activate Zapnout Aux Send: %1 Aux Send: %1 %1(%2): %3 plugin not found. %1(%2): %3 přídavný modul nenalezen. add plugin PÅ™idat přídavný modul add insert PÅ™idat vložku add aux-send PÅ™idat aux-send add MIDI controller PÅ™idat ovladaÄ MIDI aux-send bus SbÄ›rnice aux-send aux-send matrix Matrice aux-send remove plugin Odstranit přídavný modul move plugin PÅ™esunout přídavný modul activate plugin Zapnout přídavný modul preset plugin PÅ™ednastavit přídavný modul reset plugin Znovu spustit přídavný modul plugin program Program přídavného modulu plugin alias PÅ™ezdívka přídavného modulu dedicated audio outputs JednoúÄelové zvukové výstupy direct access param Parametr pro přímý přístup import plugins Přídavné moduly k zavedení session loop SmyÄka sezení session punch ZaÄátek nahrávání sezení session properties Vlastnosti sezení Duplex Duplexní režim Output Výstup Input Vstup None Žádný Beat Doba add track PÅ™idat stopu remove track Odstratit stopu duplicate track Zdvojit stopu move track PÅ™esunout stopu resize track ZmÄ›nit velikost stopy import track Zavést stopu track properties Vlastnosti stopy Track assignment failed: Track: "%1" Input: "%2" Output: "%3" Selhalo pÅ™iÅ™azení k stopÄ›: Stopa: "%1" Vstup: "%2" Výstup: "%3" track record Nahrání stopy track mute Ztlumení stopy track solo Sólo pro stopu track monitor Sledování stopy track gain Zesílení stopy track pan Vyváženost stopy track instrument Nástroj stopy Automation (%1) Automatizace (%1) none Žádná Automation Automatizace Unknown Neznámý Product: Výrobek: Vendor: Prodejce: Manual: PříruÄka: Support: Podpora: Version: Verze: %1 (*.%2) %1 (*.%2) (default) (výchozí) %1 Hz %1 Hz slave Řízený %1 (%2) %1 (%2) add tempo node PÅ™idat uzel s tempem update tempo node Obnovit uzel s tempem remove tempo node Odstranit uzel s tempem move tempo node PÅ™esunout uzel s tempem add marker PÅ™idat znaÄku update marker Obnovit znaÄku remove marker Odstranit znaÄku add key signature PÅ™idat pÅ™edznamenání update key signature Aktualizovat pÅ™edznamenání remove key signature Odstranit pÅ™edznamenání move marker PÅ™esunout znaÄku change time-sig. ZmÄ›nit taktové oznaÄení %1 Monitor %1 Sledovat Insert Send/Return pseudo-plugin (Audio) Vložit nepravý přídavný modul pro odeslání/vrácení (Audio) Insert Send/Return pseudo-plugin (MIDI) Vložit nepravý přídavný modul pro odeslání/vrácení (MIDI) Send Gain Poslat zesílení Dry Gain ZkuÅ¡ební zesílení Wet Gain Ostré zesílení Aux Send (Audio) Pomocné odeslání (Audio) Aux Send pseudo-plugin (Audio) Pomocný nepravý přídavný modul pro odeslání (Audio) Aux Send pseudo-plugin (MIDI) Pomocný nepravý přídavný modul pro odeslání (MIDI) (none) (žádný) %1 (Audio) %1 (Audio) %1 (MIDI) %1 (MIDI) set controller Nastavit ovladaÄ reset controller Nastavit ovladaÄ znovu step input krokový vstup overdub nahrávat (v overdub režimu) %1 Volume %1 Hlasitost %1 Gain %1 Zesílení %1 Pan %1 Vyvážení (Pan) automation select Automatizace Vybrat automation mode Automatizace Režim automation play Automatizace PÅ™ehrát automation record Automatizace Nahrát automation logarithmic Automatizace Logaritmická automation color Automatizace Barva automation play all Automatizace PÅ™ehrát vÅ¡e automation record all Automatizace Nahrát vÅ¡e automation edit Automatizace Upravit automation clear Automatizace Smazat automation clear all Automatizace Smazat vÅ¡e automation edit list Automatizace Upravit seznam take %1 ZábÄ›r %1 reset takes Nastavit zábÄ›ry znovu clip save Uložení zábÄ›ru clip unlink Odpojení zábÄ›ru clip tool %1 Nástroj na zábÄ›ry %1 clip record Nahrávání zábÄ›ru Copyright: Autorské právo: Project: Projekt: Select plug-in's editor (GUI): Vybrat editor (rozhraní) přídavného modulu: External VnÄ›jší X11 X11 X11 (native) X11 (nativní) Gtk2 Gtk2 Gtk2 (native) Gtk2 (nativní) Qt4 Qt4 Qt5 Qt5 Other Jiné Don't ask this again Neptat se znovu plugin parameters Parametry přídavného modulu Open File lv2_ui_request_parameter Otevřít soubor Author: Autor: %1: Automation/curve file not found. %1:Soubor s automatizací/kÅ™ivkou nenalezen. Name: Název: Category: Kategorie: Categories: Kategorie: %1 Record %1 Nahrát %1 Mute %1 Ztlumit %1 Solo %1 Sólo MIDI Controller: %1, %2, %3 OvladaÄ MIDI: %1, %2, %3 Control (MIDI) OvladaÄ (MIDI) MIDI Controller Send pseudo-plugin Nepravý přídavný modul pro odeslání ovladaÄe MIDI Value Hodnota qtractorAudioIOMatrixForm Aux-Send I/O Matrix Matrice vstupu/výstupu aux-send Warning UpozornÄ›ní Some settings have been changed. Do you want to apply the changes? NÄ›která nastavení byla zmÄ›nÄ›na. Chcete použít tyto zmÄ›ny? qtractorAudioListView Name Název Ch Kanál Frames Snímky Rate Frekvence Time ÄŒas Path Cesta Open Audio Files Otevřít zvukové soubory %1: Audio file not found. %1: Zvukový soubor nebyl nalezen. qtractorAudioMixerMeter Gain (dB) Zesílení (dB) dB dB Pan: %1 Vyvážení (Pan): %1 Gain: %1 dB Zesílení: %1 dB qtractorBusForm Audio Zvuk MIDI MIDI Bus SbÄ›rnice Warning UpozornÄ›ní Some settings have been changed. Do you want to apply the changes? NÄ›která nastavení byla zmÄ›nÄ›na. Chcete použít tyto zmÄ›ny? Move &Up Posunout &nahoru Move &Down Posunout &dolů (No instrument) (Žádný nástroj) (none) (Žádný) (1 item) (1 položka) (%1 items) (%1 položky) About to remove bus: "%1" (%2) Are you sure? Chystáte se odstranit sbÄ›rnici: "%1" (%2) Jste si jistý? Some settings have been changed. Do you want to discard the changes? NÄ›která nastavení byla zmÄ›nÄ›na. Chcete zahodit tyto zmÄ›ny? &Create &VytvoÅ™it &Update &Zaktualizovat &Remove &Odstranit Bus list Seznam sbÄ›rnic Buses SbÄ›rnice Ch Kanál Mode Režim Properties Vlastnosti &Name: &Název: Bus name Název sbÄ›rnice &Mode: &Režim: Bus mode Režim sbÄ›rnice Input Vstup Output Výstup Duplex Duplexní režim Bus monitor (pass-through) Sledování sbÄ›rnice (průchodnost) M&onitor (pass-through) &Pass-through (monitor) &Sledování (průchodnost) Cha&nnels: Ka&nály: Audio channels Zvukové kanály Audio auto-connect Automatické pÅ™ipojení zvuku &Auto connect &PÅ™ipojit automaticky MIDI Instrument name Název nástroje MIDI MIDI SysEx setup Nastavení MIDI SysEx SysE&x... SysE&x... Input Plugins Vstupní přídavné moduly Input bus plugins Vstupní sbÄ›rnicové přídavné moduly Add input plugin PÅ™idat vstupní přídavný modul &Add... &PÅ™idat... Remove input plugin Odstranit vstupní přídavný modul Move input plugin up PÅ™esunout vstupní přídavný modul nahoru &Up &Nahoru Move input plugin down PÅ™esunout vstupní přídavný modul dolů &Down &Dolů Output Plugins Výstupní přídavné moduly Output bus plugins Výstupní sbÄ›rnicové přídavné moduly Add output plugin PÅ™idat výstupní přídavný modul Remove output plugin Odstranit výstupní přídavný modul Move output plugin up PÅ™esunout výstupní přídavný modul nahoru Move output plugin down PÅ™esunout výstupní přídavný modul dolů Move bus up towards the top Posunout sbÄ›rnici nahoru U&p &Nahoru Move bus down towards the bottom Posunout sbÄ›rnici dolů Do&wn &Dolů Create bus VytvoÅ™it sbÄ›rnici Update bus Zaktualizovat sbÄ›rnici Delete bus Smazat sbÄ›rnici &Delete &Smazat Close this dialog Zavřít tento dialog Close Zavřít qtractorClientListView Readable Clients / Output Ports ÄŒitelní klienti/Výstupní přípojky Writable Clients / Input Ports Zapisovatelní klienti/Vstupní přípojky qtractorClipForm &Gain: &Zesílení: dB dB &Volume: &Hlasitost: % % Linear Lineární Quadratic 1 Kvadratická 1 Quadratic 2 Kvadratická 2 Quadratic 3 Kvadratická 3 Cubic 1 Kubická 1 Cubic 2 Kubická 2 Cubic 3 Kubická 3 new clip Nový zábÄ›r edit clip Upravit zábÄ›r Warning UpozornÄ›ní Some settings have been changed. Do you want to apply the changes? NÄ›která nastavení byla zmÄ›nÄ›na. Chcete použít tyto zmÄ›ny? Audio Zvuk MIDI MIDI MIDI files (*.%1 *.smf *.midi) Soubory MIDI (*.%1 *.smf *.midi) All files (*.*) VÅ¡echny soubory (*.*) %1 Clip File %1 Soubor se zábÄ›rem &Name: &Název: Clip name Název zábÄ›ru &File: &Soubor: Clip filename Název souboru se zábÄ›rem Browse for clip file Procházet pro soubor se zábÄ›rem Track/&Channel: Stopa/&Kanál: Clip track/channel Stopa se zábÄ›rem/kanál Clip gain/volume Zesílení zábÄ›ru/hlasitost Parameters Parametry Clip start ZaÄátek zábÄ›ru Clip offset Posun zábÄ›ru &Panning: &Vyvažování: Clip length Délka zábÄ›ru Offs&et: Pos&un: &Length: &Délka: &Start: &ZaÄátek: Clip ZábÄ›r Forma&t: &Formát: Time display format Formát zobrazení Äasu Frames Snímky Time ÄŒas BBT TDT (takty-doby-tiky) Fade In/Out Postupné zesílení/zeslabení signálu Fade &In: Postupné &zesílení signálu: Clip fade-in length Délka postupného zesílení signálu zábÄ›ru Clip fade-in type Druh postupného zesílení signálu zábÄ›ru Fade &Out: Postupné &zeslabení signálu: Clip fade-out length Délka postupného zeslabení signálu zábÄ›ru Clip fade-out type Druh postupného zeslabení signálu zábÄ›ru Ti&me Stretch: &Protažení Äasu: Clip time-stretch percentage Procentní podíl natažení Äasu zábÄ›ru Pitch S&hift: Posunutí výš&ky tónu: Clip pitch-shift in semitones Posunutí výšky tónu zábÄ›ru v půltónech semitones Půltóny Whether to use WSOLA time-stretching Zda se má použít WSOLA natahování Äasu &WSOLA time-stretching &WSOLA natahování Äasu Whether to use RubberBand formant preserve Zda má být použito zachování formantu RubberBand RubberBand &formant preserve Zachování &formantu RubberBand Whether to apply WSOLA quick seek time-stretching Zda se má použít WSOLA rychlé vyhledání natahování Äasu WSOLA quic&k seek WSOLA rych&lé vyhledání Whether to use RubberBand R3 finer engine Zda používat jemnÄ›jší nástroj RubberBand R3 RubberBand R&3 finer engine JemnÄ›jší nástroj RubberBand R&3 &Mute &Ztlumit qtractorConnect Connect PÅ™ipojit Disconnect Odpojit Disconnect All Odpojit vÅ¡e Refresh Obnovit qtractorConnectForm (All) (vÅ¡e) Connections PÅ™ipojení Audio Zvuk Select output client/ports Vybrat výstupní klienty/přípojky Select input client/ports Vybrat vstupní klienty/přípojky Connect currently selected ports PÅ™ipojit nyní vybrané přípojky &Connect &PÅ™ipojit Disconnect currently selected ports Odpojit nyní vybrané přípojky &Disconnect &Odpojit Disconnect all currently connected ports Odpojit vÅ¡echny nyní vybrané přípojky Disconnect &All Odpojit &vÅ¡e Refresh current connections view Obnovit náhled na nynÄ›jší pÅ™ipojení &Refresh &Obnovit MIDI MIDI qtractorConnections Connections PÅ™ipojení qtractorEditRangeForm Range Rozsah Selection range Rozsah výbÄ›ru &Selection &VýbÄ›r Loop range Rozsah smyÄky &Loop &SmyÄka Punch range Rozsah pÅ™epsání &Punch &PÅ™epsání Edit range Rozsah úprav &Edit Úp&ravy Custom range Vlastní rozsah &Custom &Vlastní St&art: Za&Äátek: Clip start ZaÄátek zábÄ›ru En&d: Ko&nec: Clip offset Posun zábÄ›ru Apply to Loop points in range Použít na body smyÄky v rozsahu L&oop &SmyÄka Apply to Punch In/Out points in range Použít na body zaÄátku/konce pÅ™epsání v rozsahu Pu&nch &PÅ™epsání Mar&kers &ZnaÄky Te&mpo Map &Zobrazení tempa &Format &Formát Time display format Formát zobrazení Äasu Time ÄŒas BBT TDT (takty-doby-tiky) Frames Snímky Edit Range Rozsah úprav Options Volby Apply to clips in range Použít na zábÄ›ry v rozsahu Cl&ips &ZábÄ›ry A&utomation A&utomatizace Apply to Automation nodes in range Použít na uzly automatizace v rozsahu Apply to Tempo Map nodes in range Použít na uzly zobrazení tempa v rozsahu Apply to location Markers in range Použít na znaÄky umístÄ›ní v rozsahu qtractorExportClipForm %1 %2 Clips %1 %2 zábÄ›ry qtractorExportForm Audio Zvuk MIDI MIDI Export %1 File Vyvést %1 soubor MIDI files (*.%1 *.smf *.midi) Soubory MIDI (*.%1 *.smf *.midi) All files (*.*) VÅ¡echny soubory (*.*) &File: &Soubor: Export file name Vyvést název souboru Browse export file name Procházet vyvedeným názvem souboru File &type: Druh &souboru: Audio file type to use on export Typ zvukového souboru k použití pÅ™i vyvedení Sample &format: Formát &vzorku: Audio sample format to use on export Formát zvukového vzorku k použití pÅ™i vyvedení &Quality: &Kvalita: Audio compression quality to use on export Kvalitu zvukové komprese k použití pÅ™i vyvedení File &format: Formát &souboru: MIDI file format to use on export Formát souboru MIDI k použití pÅ™i vyvedení Range Rozsah Session range Rozsah sezení &Session &Sezení Loop range Rozsah smyÄky &Loop &SmyÄka Punch range Rozsah pÅ™epsání &Punch &PÅ™epsání Edit range Rozsah úprav &Edit &Úprava Custom range Vlastní rozsah &Custom &Vlastní St&art: Za&Äátek: En&d: Ko&nec: Outputs Výstupy Output bus names Názvy výstupních sbÄ›rnic Time ÄŒas BBT TDT (takty-doby-tiky) Whether to add/import new track(s) with export result Zda se má/mají pÅ™idat/zavést nová stopa/nové stopy s výsledkem vyvedení &Add new track(s) &PÅ™idat novou stopu(y) Format Formát Export Vyvést Custom start Vlastní zaÄátek Custom end Vlastní konec Time display format Formát zobrazení Äasu Frames Snímky qtractorExportTrackForm %1 %2 Tracks %1 %2 stop Warning Varování The file already exists: "%1" Do you want to replace it? Soubor již existuje: "%1" Chcete jej nahradit? Audio file export: "%1" started... Vyvedení zvukového souboru: "%1" právÄ› zaÄalo... Audio file export: "%1" complete. Vyvedení zvukového souboru: "%1" je hotovo. Audio file export: "%1" failed. Vyvedení zvukového souboru: "%1" selhalo. MIDI file export: "%1" started... Vyvedení souboru MIDI: "%1" právÄ› zaÄalo... MIDI file export: "%1" complete. Vyvedení souboru MIDI: "%1" je hotovo. MIDI file export: "%1" failed. Vyvedení souboru MIDI: "%1" selhalo. qtractorFileListView New Group Nová skupina Warning UpozornÄ›ní About to remove %1 file item(s). Are you sure? Chystáte se odstranit %1 položek souborů: Jste si jistý? About to remove %1 item: "%2" Are you sure? Chystáte se odstranit %1 položku: "%2" Jste si jistý? group Skupina file Soubor qtractorFileSystem &Home &Domovská složka &Up &Nahoru Al&l Files &VÅ¡echny soubory &Session &Sezení &Audio &Zvuk &MIDI &MIDI H&idden &Skryto &Play &PÅ™ehrát File System Souborový systém qtractorFiles Audio Zvuk MIDI MIDI Play file PÅ™ehrát soubor Add &Files... PÅ™idat &soubory... Cu&t Vyj&mout &Copy &Kopírovat Ctrl+X Ctrl+X Ctrl+C Ctrl+C Ctrl+V Ctrl+V New &Group... Nová &skupina... &Paste &Vložit Re&name &PÅ™ejmenovat &Remove &Odstranit Pla&y PÅ™e&hrát Cl&eanup UdÄ›lat pořá&dek Del Smazat Files Soubory MIDI Files Soubory MIDI Audio Files Zvukové soubory qtractorInstrumentForm Import Instrument Files Zavést soubory s nástroji Instrument files (*.%1 *.sf2 *.sf3 *.midnam) Soubory nástrojů (*.%1 *.sf2 *.sf3 *.midnam)) Instrument files (*.%1) Soubory s nástroji (*.%1) Export Instrument File Vyvést soubor s nástrojem All files (*.*) VÅ¡echny soubory (*.*) Warning UpozornÄ›ní The instrument file already exists: "%1" Do you want to replace it? Soubor nástroje již existuje: "%1" PÅ™ejete si jej nahradit? Instrument settings have been changed. Do you want to apply the changes? Nastavení nástrojů byla zmÄ›nÄ›na. Chcete použít tyto zmÄ›ny? Patch Names for Banks Názvy políÄek pro Banky Controller Names = %1 Názvy ovladaÄů = %1 RPN Names = %1 RPN Názvy = %1 NRPN Names = %1 NRPN Názvy = %1 Bank Select Method = %1 Metoda výbÄ›ru zvukové banky = %1 Patch Names Názvy políÄek Note Names Názvy not Controller Names Názvy ovladaÄů RPN Names Názvy RPN NRPN Names Názvy NRPN Bank Select Methods Metody výbÄ›ru zvukové banky %1 = %2 %1 = %2 Based On = %1 Založeno na = %1 Normal Normální Bank MSB Banka MSB Bank LSB Banka LSB Patch PolíÄko Unknown Neznámý Instruments Nástroje Files Soubory Path Cesta Names Názvy Import from instrument file Zavést ze souboru s nástrojem &Import... &Zavést... Remove instrument file Odstranit soubor s nástrojem &Remove &Odstranit Move instrument file up on list order Posunout soubor s nástrojem nahoru v seznamu &Up &Nahoru Move instrument file down on list order PÅ™esunout soubor s nástrojem dolů na seznamu příkazů &Down &Dolů E&xport... V&yvést... Export to instrument file Vyvést do souboru s nástrojem Close this dialog Zavřít tento dialog Close Zavřít qtractorInstrumentMenu (None) (Žádný) qtractorMainForm Current tempo (BPM) NynÄ›jší tempo (MM) Snap/beat Zapadnout/doba Track Stopa Current track name Název nynÄ›jší stopy MOD MOD Session modification state Stav úprav bÄ›hem sezení REC NAHRÃT Session record state Stav nahrávání bÄ›hem sezení MUTE ZTLUMIT Session muting state Stav ztlumení bÄ›hem sezení SOLO SÓLO Session soloing state Stav sóla bÄ›hem sezení LOOP SMYÄŒKA Session looping state Stav smyÄky bÄ›hem sezení Session total time Celkový Äas sezení Session sample rate Vzorkovací kmitoÄet bÄ›hem sezení Could not set default session directory: %1 Sorry. Nemohla být nastavena výchozí složka sezení: %1 Promiňte. Ready PÅ™ipraven Session buffer size Velikost vyrovnávací pamÄ›ti sezení Session files (*.%1 *.%2 *.%3) Soubory se sezením (*.%1 *.%2 *.%3) Archive files (*.%1) Soubory s archivy (*.%1) All files (*.*) VÅ¡echny soubory (*.*) Backup session: "%1" as "%2". Zazálohovat sezení: "%1" jako "%2". Could not backup existing session: %1 as %2 Sorry. NepodaÅ™ilo se zazálohovat stávající sezení: %1 jako %2 Promiňte. About to remove archive directory: "%1" Are you sure? Chystáte se odstranit soubor s archivem: "%1") Jste si jistý? The directory already exists: "%1" Do you want to replace it? Adresář již existuje: "%1" Chcete jej nahradit? Opening "%1"... Otevírá se "%1"... A directory with same name already exists: "%1" This directory will be replaced, erasing all its current data, when opening and extracting this archive in the future. Do you want to continue? Adresář se stejným názvem již existuje: "%1" Tento adresář bude nahrazen. PÅ™i otevírání a rozbalování tohoto archivu v budoucnu budou smazána vÅ¡echna jeho nynÄ›jší data. Chcete pokraÄovat? The directory is an extracted archive: "%1" This directory will be removed, erased from all its current data, when closing this session. Do you want to continue? Adresář je rozbalený archiv: "%1" Tento adresář bude odstranÄ›n, vymazána vÅ¡echna jeho nynÄ›jší data, pÅ™i ukonÄení tohoto sezení. Chcete pokraÄovat? Saving "%1"... Ukládá se "%1"... Oops! Looks like it crashed or did not close properly last time it was run... however, an auto-saved session file exists: "%1" Do you want to crash-recover from it? Fuj, to jsem se lekl! Vypadá to, jako by program spadl, nebo nebyl naposledy uzavÅ™en správnÄ›... nicménÄ›, soubor automaticky uloženým sezením stále existuje: "%1" Do you want to crash-recover from it? About to clear automation: "%1" Are you sure? Chystáte se odstranit automatizaci: "%1" Jste si jistý? About to clear all automation: "%1" Are you sure? Chystáte se odstranit vÅ¡echnu automatizaci: "%1" Jste si jistý? Player panic! Nouzové zastavení pÅ™ehrávaÄe! Beat-detection support (libaubio) disabled. Podpora pro rozpoznávání dob (libaubio) byla zakázána. VST2 Plug-in support disabled. Podpora pro přídavné moduly VST2 byla zakázána. CLAP Plug-in support disabled. Podpora pro přídavné moduly CLAP byla zakázána. LV2 Plug-in UI support disabled. Podpora pro uživatelské rozhraní přídavného modulu LV2 byla zakázána. LV2 Plug-in UI support (libsuil) disabled. Podpora pro uživatelské rozhraní přídavného modulu LV2 (libsuil) byla zakázána. LV2 Plug-in MIDI/Atom support disabled. Podpora pro MIDI/Atom přídavného modulu LV2 byla zakázána. LV2 Plug-in Worker/Schedule support disabled. Podpora pro pracovník/rozvrh přídavného modulu LV2 byla zakázána. LV2 Plug-in State support disabled. Podpora pro stav přídavného modulu LV2 byla zakázána. LV2 Plug-in State Files support disabled. Podpora pro soubory stavu přídavného modulu LV2 byla zakázána. LV2 Plug-in MIDNAM support disabled. Podpora pro MIDNAM přídavného modulu LV2 byla zakázána. LV2 Plug-in Options support disabled. Podpora pro volby přídavného modulu LV2 byla zakázána. LV2 Plug-in Buf-size support disabled. Podpora pro velikost vyrovnávací pamÄ›ti přídavného modulu LV2 byla zakázána. LV2 Plug-in Programs support disabled. Podpora pro programy přídavného modulu LV2 byla zakázána. Session XRUN state Stav XRUN sezení LV2 Plug-in MIDI/Event support (DEPRECATED) enabled. Podpora pro MIDI/událost přídavného modulu LV2 (NESCHVÃLENO) byla povolena. LV2 plug-in State Make Path support (DANGEROUS) enabled. Podpora pro cestu Make stavu přídavného modulu LV2 (NEBEZPEÄŒNÉ) byla povolena. LV2 Plug-in Presets support disabled. Podpora pro pÅ™ednastavení přídavného modulu LV2 byla zakázána. LV2 Plug-in Patch support disabled. Podpora pro záplatu přídavného modulu LV2 byla zakázána. LV2 Plug-in Time/position support disabled. Podpora pro Äas/poloha přídavného modulu LV2 byla zakázána. LV2 Plug-in UI Touch interface support disabled. Podpora pro rozhraní dotykového uživatelského rozhraní přídavného modulu LV2 byla zakázána. LV2 Plug-in UI Request-value support disabled. Podpora pro hodnotu-požadavek rozhraní přídavného modulu LV2 byla zakázána. LV2 Plug-in UI Idle interface support disabled. Podpora pro rozhraní IDLE přídavného modulu LV2 byla zakázána. LV2 Plug-in UI Show interface support disabled. Podpora pro rozhraní IDLE přídavného modulu LV2 Show byla zakázána. JACK Latency support disabled. Podpora pro prodlevu JACK byla zakázána. JACK Metadata support disabled. Podpora pro popisná data JACK byla zakázána. NSM support disabled. Podpora pro NSM byla zakázána. XRUN XRUN &Hold &Držet &Linear &Přímka &Spline &KÅ™ivka Take %1 ZábÄ›r %1 None Žádný The audio engine has been shutdown. Make sure the JACK audio server (jackd) is up and running and then restart session. Provoz zvukového stroje byl zastaven. UjistÄ›te se, že je zvukový server Jack (jackd) spuÅ¡tÄ›n a běží, a potom sezení zaÄnÄ›te znovu. STOP ZASTAVIT PLAY PŘEHRÃT FFWD RYCHLE DOPŘEDU REW PŘETOÄŒIT REC ON NAHRÃVÃNà ZAPNUTO REC OFF NAHRÃVÃNà VYPNUTO RESET NASTAVIT ZNOVU LOCATE %1 NAJÃT %1 SHUTTLE %1 JEZDIT TAM A ZPÄšT %1 STEP %1 INTERVAL %1 TRACK RECORD %1 %2 NAHRÃVAT STOPU %1 %2 TRACK MUTE %1 %2 ZTLUMIT STOPU %1 %2 TRACK SOLO %1 %2 SÓLO PRO STOPU %1 %2 Unknown sub-command Neznámý pod-příkaz Not implemented Neprovedeno MIDI CTL: %1, Channel %2, Param %3, Value %4 MIDI CTL %1, Kanál %2, Parametr %3, Hodnota %4 %1 BPM %1 MM (track %1, gain %2) (stopa %1, zesílení %2) Audio self-connection detected! In general, connecting an output bus (or insert send), directly into any input bus (or insert return), is not advisable. It often doesn't work, if at all. ZjiÅ¡tÄ›no vlastní pÅ™ipojení zvuku! ObecnÄ› platí, že pÅ™ipojení výstupní sbÄ›rnice (nebo insert send), přímo do libovolné vstupní sbÄ›rnice (nebo insert return), se nedoporuÄuje. ÄŒasto to nefunguje, pokud vůbec. (track %1, panning %2) (stopa %1, vyvažování %2) START SPUSTIT CONTINUE POKRAÄŒOVAT SONGPOS %1 POLOHA V PÃSNI %1 Untitled%1 Bez názvu %1 New session: "%1". Nové sezení: "%1". Session files (*.%1 *.%2) Soubory se sezeními (*.%1 *.%2) Template files (*.%1) Sobory s pÅ™edlohami (*.%1) Open Session Otevřít sezení Save Session Uložit sezení Warning UpozornÄ›ní The file already exists: "%1" Do you want to replace it? Soubor již existuje: "%1" Chcete jej nahradit? The current session has been changed: "%1" Do you want to save the changes? NynÄ›jší sezení bylo zmÄ›nÄ›no: "%1" Chcete uložit zmÄ›ny? Save Uložit Session closed. Sezení uzavÅ™eno. Session could not be loaded from "%1". Sorry. Sezení nemohlo být nahráno z "%1". Promiňte. Open session: "%1". Otevřít sezení: "%1". Session could not be saved to "%1". Sorry. Sezení nemohlo být uloženo do "%1". Promiňte. Save session: "%1". Uložit sezení: "%1". take range Rozsah zábÄ›ru session Sezení or nebo program programová Information informace Some settings may be only effective next time you start this %1. NÄ›která nastavení mohou být úÄinná teprve, až pÅ™i příštím spuÅ¡tÄ›ní %1. Version Verze Debugging option enabled. Povolena volba umožňující hlášení chyb. Set current snap to %1 Nastavit nynÄ›jší zapadnutí na %1 Current time (play-head) NynÄ›jší Äas (hrací hlava) Don't ask this again Neptat se znovu Ogg Vorbis (libvorbis) file support disabled. Podpora pro soubory Ogg Vorbis (libvorbis) byla zakázána. MPEG-1 Audio Layer 3 (libmad) file support disabled. Podpora pro soubory MPEG-1 Audio Layer 3 (libmad) byla zakázána. Sample-rate conversion (libsamplerate) disabled. PÅ™evod vzorkovacího kmitoÄtu (libsamplerate) byl zakázán. Pitch-shifting support (librubberband) disabled. Podpora pro posunutí výšky tónu (librubberband) byla zakázána. OSC service support (liblo) disabled. Podpora pro službu OSC (liblo) byla zakázána. LADSPA Plug-in support disabled. Podpora pro přídavné moduly LADSPA byla zakázána. DSSI Plug-in support disabled. Podpora pro přídavné moduly DSSI byla zakázána. VST3 Plug-in support disabled. Podpora pro přídavné moduly VST3 byla zakázána. LV2 Plug-in support disabled. Podpora pro přídavné moduly LV2 byla zakázána. LV2 Plug-in support (liblilv) disabled. Podpora přídavných modulů LV2 (liblilv) byla zakázána. LV2 Plug-in External UI support disabled. Podpora pro rozhraní přídavných modulů LV2 byla zakázána. LV2 Plug-in UI GTK2 native support disabled. Nativní podpora pro uživatelské rozhraní přídavných modulů LV2 GTK2 byla zakázána. LV2 Plug-in UI GTKMM2 native support disabled. Nativní podpora pro uživatelské rozhraní přídavných modulů LV2 GTKMM2 byla zakázána. LV2 Plug-in UI X11 native support disabled. Nativní podpora pro uživatelské rozhraní přídavných modulů LV2 X11 byla zakázána. JACK Session support disabled. Podpora pro sezení JACK byla zakázána. Using: Qt %1 Použitím: Qt %1 Website Stránky This program is free software; you can redistribute it and/or modify it Tento program je svobodným programem. Můžete jej šířit a/nebo upravit under the terms of the GNU General Public License version 2 or later. za podmínek GNU General Public License ve verzi 2 nebo pozdÄ›jší. About O record clip Nahrát zábÄ›r [modified] (upraveno) Session started. Sezení zaÄalo. The audio/MIDI engine could not be started. Make sure the JACK/Pipewire audio service and the ALSA Sequencer kernel module (snd-seq-midi) are up and running and then restart the session. Zvukový/MIDI stroj se nepodaÅ™ilo spustit. UjistÄ›te se, že zvuková služba JACK/Pipewire a modul jádra ALSA sekvenceru (snd-seq-midi) jsou spuÅ¡tÄ›ny a restartujte sezení. The original session sample rate (%1 Hz) is not the same as the current audio engine (%2 Hz). Saving and reloading from a new session file is highly recommended. Původní vzorkovací kmitoÄet sezení (%1 Hz) není stejný jako je vzorkovací kmitoÄet nynÄ›jšího zvukového stroje (%2 Hz). Velmi se doporuÄuje uložit sezení a znovu nahrát z nového souboru sezení. The following issues were detected: %1 Saving into another session file is highly recommended. Byly zjiÅ¡tÄ›ny následující problémy: %1 DůraznÄ› se doporuÄuje uložit do jiného souboru se sezením. Error Chyba XRUN(%1 skipped) XRUN(%1 pÅ™eskoÄeno) XRUN(%1): some frames might have been lost. XRUN(%1): nÄ›které snímky mohly být ztraceny. Audio connections change. ZmÄ›na zvukových pÅ™ipojení. Don't show this again Nezobrazovat znovu MIDI connections change. ZmÄ›na MIDI pÅ™ipojení. Playing ended. PÅ™ehrávání ukonÄeno. The audio engine buffer size has changed, increased from %1 to %2 frames/period. Reloading the current session file is highly recommended. Vyrovnávací paměť zvukového stroje se zmÄ›nila, zvýšila se z %1 na %2 snímků/perioda. Velmi se doporuÄuje nahrát nynÄ›jší soubor se sezením znovu. TRACK MONITOR %1 %2 SLEDOVÃNà STOPY %1 %2 Playing "%1"... PÅ™ehrává "%1"... &Track Stop&a &State &Stav &Navigate &NavádÄ›ní Impor&t Tracks Zavést stop&y E&xport Tracks V&yvést stopy &View Pohle&d &Toolbars &Nástrojové panely &Windows &Okna &Zoom &PÅ™iblížit S&nap PÅ™i&chytit T&ransport PÅ™e&hrávání &Help &NápovÄ›da &Edit Úp&ravy Select &Mode Vybrat reži&m &Select Vy&brat Mo&ve PÅ™&esunout &Height Výš&ka M&ode Reži&m A&utomation Automatiza&ce T&ools Nástroj&e &File &Soubor Open &Recent Otevřít ne&dávné &New &Nový New Nový New session Nové sezení New session file Nový soubor se sezením Ctrl+N Ctrl+N &Open... &Otevřít... Open Otevřít Open session Otevřít sezení Open session from file Otevřít sezení ze souboru Ctrl+O Ctrl+O &Save &Uložit Save session Uložit sezení Save session to file Uložit sezení do souboru Ctrl+S Ctrl+S Save &As... Uložit &jako... Save As Uložit jako Save as Uložit jako Save current session with another file name Uložit nynÄ›jší sezení pod jiným názvem souboru &Properties... &Vlastnosti... Session Properties Vlastnosti sezení Session properties Vlastnosti sezení Edit current session properties Upravit nynÄ›jší vlastnosti sezení F2 F2 E&xit U&konÄit Exit UkonÄit Exit this application program UkonÄit tento program &Undo &ZpÄ›t Undo Vrátit zpÄ›t Undo last action Vrátit zpÄ›t poslední krok Ctrl+Z Ctrl+Z &Redo Z&novu Redo UdÄ›lat znovu Redo last action UdÄ›lat znovu poslední krok Ctrl+Shift+Z Ctrl+Shift+Z Cu&t &Vyjmout Cut Vyjmout Cut selection to clipboard Vyjmout výbÄ›r do schránky Ctrl+X Ctrl+X &Copy &Kopírovat Copy Kopírovat Copy selection to clipboard Kopírovat výbÄ›r do schránky Ctrl+C Ctrl+C &Paste Vloži&t Paste Vložit Paste clipboard contents Vložit obsah schránky Ctrl+V Ctrl+V Past&e Repeat... Opakovat vlož&ení... Paste Repeat Opakovat vložení Paste repeat Opakovat vložení Paste/repeat clipboard contents Vložit/Opakovat obsah schránky Ctrl+Shift+V Ctrl+Shift+V &Delete &Smazat Delete Smazat Delete selection Smazat výbÄ›r Del Smazat &Clip &ZábÄ›r Clip ZábÄ›r Select clip Vybrat zábÄ›r Clip selection mode Režim výbÄ›ru zábÄ›ru &Range &Rozsah Range Rozsah Select range Vybrat rozsah Range selection mode Režim rozsahu výbÄ›ru R&ectangle O&bdélníkový Rect Obdélníkový Select rectangle Vybrat obdélník Rectangular selection mode Režim obdélníkového výbÄ›ru &Automation &Automatizace Automation Automatizace Automation edit mode Režim úprav automatizace &None &Žádný Select None Žádný výbÄ›r Select none Žádný výbÄ›r Mark all as unselected OznaÄit vÅ¡e jako nevybrané Ctrl+Shift+A Ctrl+Shift+A &Invert &Obrátit Select Invert Obrátit výbÄ›r Select invert Obrátit výbÄ›r Invert selection Obrátit výbÄ›r Ctrl+I Ctrl+I Select Range Vybrat rozsah Mark range as selected OznaÄit rozsah jako vybraný Ctrl+R Ctrl+R Select Track Vybrat stopu Select track Vybrat stopu Mark track as selected OznaÄit stopu jako vybranou Ctrl+T Ctrl+T Trac&k Range Rozsah &stopy Select Track Range Vybrat rozsah stopy Select track range Vybrat rozsah stopy Mark track range as selected OznaÄit rozsah stopy jako vybraný Ctrl+Shift+R Ctrl+Shift+R &All &VÅ¡e Select All Vybrat vÅ¡e Select all Vybrat vÅ¡e Mark all as selected OznaÄit vÅ¡e jako vybrané Ctrl+A Ctrl+A &New... &Nový... New Clip Nový zábÄ›r New clip Nový zábÄ›r Create new clip VytvoÅ™it nový zábÄ›r &Edit... &Upravit... Edit Clip Upravit zábÄ›r Edit clip Upravit zábÄ›r Edit current clip Upravit nynÄ›jší zábÄ›r F4 F4 &Split &RozdÄ›lit Split Clip RozdÄ›lit zábÄ›r Split clip RozdÄ›lit zábÄ›r Split current clip at playhead RozdÄ›lit nynÄ›jší zábÄ›r na ukazateli pÅ™ehrávání &Merge... &SlouÄit... Merge Clips SlouÄit zábÄ›ry Merge clips SlouÄit zábÄ›ry Merge selected clips SlouÄit vybrané zábÄ›ry Normalize Clip Normalizovat zábÄ›r Normalize clip Normalizovat zábÄ›r Normalize current clip (gain/volume) Normalizovat nynÄ›jší zábÄ›r (zesílení/hlasitost) Quantize Clip Kvantizovat zábÄ›r &Quantize... &Kvantizovat... Quantize clip events Kvantizovat události zábÄ›ru Quantize current MIDI clip events Kvantizovat události zábÄ›ru nynÄ›jšího MIDI &Transpose... &PÅ™evést... Transpose Clip PÅ™evést zábÄ›r Transpose clip events PÅ™evést události zábÄ›ru Transpose current MIDI clip events PÅ™evést události zábÄ›ru nynÄ›jšího MIDI &Normalize... &Normalizovat... Normalize clip events Normalizovat události zábÄ›ru Normalize current MIDI clip events Normalizovat události zábÄ›ru nynÄ›jšího MIDI &Randomize... &NáhodnÄ› vybrat... Randomize Clip NáhodnÄ› vybrat zábÄ›r Randomize clip events NáhodnÄ› vybrat události zábÄ›ru Randomize current MIDI clip events NáhodnÄ› vybrat události zábÄ›ru nynÄ›jšího MIDI Resi&ze... ZmÄ›&nit velikost... Resize Clip ZmÄ›nit velikost zábÄ›ru Resize clip events ZmÄ›nit velikost událostí zábÄ›ru Resize current MIDI clip events ZmÄ›nit velikost událostí zábÄ›ru nynÄ›jšího MIDI Re&scale... ZmÄ›nit &měřítko... Rescale Clip ZmÄ›nit měřítko zábÄ›ru Rescale clip events ZmÄ›nit měřítko událostí zábÄ›ru Rescale current MIDI clip events ZmÄ›nit měřítko událostí zábÄ›ru nynÄ›jšího MIDI T&imeshift... ÄŒa&sovÄ› posunout... Timeshift Clip ÄŒasovÄ› posunout zábÄ›r Timeshift clip events ÄŒasovÄ› posunout události zábÄ›ru Timeshift current MIDI clip events ÄŒasovÄ› posunout události zábÄ›ru nynÄ›jšího MIDI Tempo Adjust Úprava tempa Adjust session tempo from current clip selection Upravit tempo sezení podle nynÄ›jšího výbÄ›ru zábÄ›ru F7 F7 &Range Set Nastavení rozsa&hu Clip Range Rozsah zábÄ›ru Clip range Rozsah zábÄ›ru Set edit-range from current clip extents Nastavit rozsah úprav z rozsahu nynÄ›jšího zábÄ›ru &Loop Set Nastavení s&myÄky Clip Loop SmyÄka zábÄ›ru Clip loop SmyÄka zábÄ›ru Set loop-range from current clip extents Nastavit rozsah smyÄky z rozsahu nynÄ›jšího zábÄ›ru &Import... Z&avést... Import Clip Zavést zábÄ›r Import clip Zavést zábÄ›r Import clip from file(s) Zavést zábÄ›r ze souboru(ů) E&xport... V&yvést... Export Clip Vyvést zábÄ›r Export clip Vyvést zábÄ›r Export current clip to file Vyvést nynÄ›jší zábÄ›r do souboru &Add Track... PÅ™i&dat stopu... Instrum&ent &Nástroj Add Track PÅ™idat stopu Add track PÅ™idat stopu Add a new track to session PÅ™idat novou stopu do sezení Shift+Ins Shift+Ins &Remove Track &Odstranit stopu Remove Track Odstranit stopu Remove track Odstranit stopu Remove current track from session Odstranit nynÄ›jší stopu ze sezení Shift+Del Shift+Del &Duplicate Track &Zdvojit stopu Duplicate Track Zdvojit stopu Duplicate track Zdvojit stopu Duplicate current track Zdvojit nynÄ›jší stopu Track &Properties... &Vlastnosti stopy... Track Properties Vlastnosti stopy Track properties Vlastnosti stopy Edit current track properties Upravit vlastnosti nynÄ›jší stopy Shift+F2 Shift+F2 &Inputs Vs&tupy Track Inputs Vstupy stop Track inputs Vstupy stop Show current track input bus connections Ukázat pÅ™ipojení vstupní sbÄ›rnice nynÄ›jší stopy &Outputs Výst&upy Track Outputs Výstupy stop Track outputs Výstupy stop Show current track output bus connections Ukázat pÅ™ipojení výstupní sbÄ›rnice nynÄ›jší stopy &Record &Nahrávat Record Track Nahrávat stopu Record track Nahrávat stopu Arm current track for recording Zapnout nynÄ›jší stopu pro nahrávání &Mute &Ztlumit Mute Track Ztlumit stopu Mute track Ztlumit stopu Mute current track Ztlumit nynÄ›jší stopu &Solo &Sólo Solo Track Sólo stopy Solo track Sólo stopy Solo current track Sólo nynÄ›jší stopy M&onitor S&ledovat Monitor Track Sledovat stopu Monitor track Sledovat stopu Monitor current track Sledovat nynÄ›jší stopu &First &První First Track První stopa First track První stopa Make current the first track UdÄ›lat z nynÄ›jší stopy první stopu &Previous &PÅ™edchozí Previous Track PÅ™edchozí stopa Previous track PÅ™edchozí stopa Make current the previous track UdÄ›lat z nynÄ›jší stopy pÅ™edchozí stopu &Next &Další Next Track Další stopa Next track Další stopa Make current the next track UdÄ›lat z nynÄ›jší stopy další stopu &Last &Poslední Last Track Poslední stopa Last track Poslední stopa Make current the last track UdÄ›lat z nynÄ›jší stopy poslední stopu N&one Žá&dná None Track Žádná stopa None track Žádná stopa None current track Žádná nynÄ›jší stopa &Top &Ç“plnÄ› nahoru Move Top PÅ™esunout úplnÄ› nahoru Move top PÅ™esunout úplnÄ› nahoru Move current track to top PÅ™esunout nynÄ›jší stopu úplnÄ› nahoru &Up &Nahoru Move Up Posunout nahoru Move up Posunout nahoru Move current track up PÅ™esunout nynÄ›jší stopu nahoru &Down &Dolů Move Down Posunout dolů Move down Posunout dolů Move current track down PÅ™esunout nynÄ›jší stopu dolů &Bottom &Ç“plnÄ› dolů Move Bottom PÅ™esunout úplnÄ› dolů Move bottom PÅ™esunout úplnÄ› dolů Move current track to bottom PÅ™esunout nynÄ›jší stopu úplnÄ› dolů &Minimize &ZmenÅ¡it Minimize Height ZmenÅ¡it výšku Minimize height ZmenÅ¡it výšku Minimize track height ZmenÅ¡it výšku stopy Auto Monitor Automaticky sledovat Auto monitor Automaticky sledovat Auto-monitor current track Automaticky sledovat nynÄ›jší stopu F6 F6 Auto Dea&ctivate &Vypnout automaticky Auto Deactivate Vypnout automaticky Auto-deactivate plugins Vypnout automaticky přídavné moduly Auto-deactivate plugins not producing sound Vypnout automaticky zvuk netvořící přídavné moduly Shift+F6 Shift+F6 &Audio... &Zvuk... Inport Audio File Zavést zvukový soubor Import Audio file Zavést zvukový soubor Import tracks from Audio file Zavést stopy ze zvukového souboru &MIDI... &MIDI... Import MIDI File Zavést soubor MIDI Import MIDI file Zavést soubor MIDI Import tracks from MIDI file Zavést stopy z MIDI souboru Export Audio File Vyvést zvukový soubor Export Audio file Vyvést zvukový soubor Export tracks to Audio file Vyvést stopy do zvukového souboru Export MIDI File Vyvést jako soubor MIDI Export MIDI file Vyvést jako soubor MIDI Export tracks to MIDI file Vyvést stopy do souboru MIDI Mute Clip Ztlumit zábÄ›r Mute clip Ztlumit zábÄ›r Mute current clip Ztlumit nynÄ›jší zábÄ›r Normali&ze &Normalizovat T&empo ramp... T&empový nájezd... Tempo ramp Clip ZábÄ›r s tempovým nájezdem Tempo ramp clip events Události zábÄ›ru s tempovým nájezdem Tempo ramp current MIDI clip events Události zábÄ›ru nynÄ›jšího MIDI s tempovým nájezdem &Cross Fade &Prolínat Clip Cross-fade Prolínání zábÄ›ru Clip cross-fade Prolínání zábÄ›ru Cross-fade current overlapped clips Prolínat nyní se pÅ™ekrývající se zábÄ›ry First Take První zábÄ›r First take První zábÄ›r Select current clip first take Vybrat první zábÄ›r nynÄ›jšího zábÄ›ru Previous Take PÅ™edchozí zábÄ›r Previous take PÅ™edchozí zábÄ›r Select current clip previous take Vybrat pÅ™edchozí zábÄ›r nynÄ›jšího zábÄ›ru Next Take Další zábÄ›r Next take Další zábÄ›r Select current clip next take Vybrat další zábÄ›r nynÄ›jšího zábÄ›ru Last Take Poslední zábÄ›r Last take Poslední zábÄ›r Select current clip last take Vybrat poslední zábÄ›r nynÄ›jšího zábÄ›ru Reset Takes Nastavit zábÄ›ry znovu Reset takes Nastavit zábÄ›ry znovu Reset (unfold) current clip takes Nastavit znovu (zruÅ¡it složení) zábÄ›ry nynÄ›jšího zábÄ›ru Range (fold) current clip into takes Řadit (složit) nynÄ›jší zábÄ›r do zábÄ›rů &Menubar &Hlavní nabídka Menubar Hlavní nabídka Show/hide the main program window menubar Ukázat/Skrýt pruh se seznamy nabídek v hlavním oknÄ› programu Ctrl+M Ctrl+M &Statusbar &Stavový řádek Statusbar Stavový řádek Show/hide the main program window statusbar Ukázat/Skrýt stavový řádek v hlavním oknÄ› programu File Toolbar Nástrojový pruh pro soubor File toolbar Nástrojový pruh pro soubor Show/hide main program window file toolbar Ukázat/Skrýt nástrojový pruh pro soubor v hlavním oknÄ› programu Edit Toolbar Nástrojový pruh pro úpravy Edit toolbar Nástrojový pruh pro úpravy Show/hide main program window edit toolbar UkázatSkrýt nástrojový pruh pro úpravy v hlavním oknÄ› programu Track Toolbar Nástrojový pruh pro stopy Track toolbar Nástrojový pruh pro stopy Show/hide main program window track toolbar Ukázat/Skrýt nástrojový pruh pro stopy v hlavním oknÄ› programu View Toolbar Nástrojový pruh pro pohled View toolbar Nástrojový pruh pro pohled Show/hide main program window view toolbar Ukázat/Skrýt nástrojový pruh pro pohled v hlavním oknÄ› programu &Options &Volby Options Toolbar Nástrojový pruh pro volby Options toolbar Nástrojový pruh pro volby Show/hide main program window options toolbar Ukázat/Skrýt nástrojový pruh pro volby v hlavním oknÄ› programu Transport Toolbar Nástrojový pruh pro pÅ™ehrávání Transport toolbar Nástrojový pruh pro pÅ™ehrávání Show/hide main program window transport toolbar Ukázat/Skrýt nástrojový pruh pro pÅ™ehrávání v hlavním oknÄ› programu T&ime ÄŒ&as Time Toolbar Nástrojový pruh pro Äas Time toolbar Nástrojový pruh pro Äas Show/hide main program window time toolbar Ukázat/Skrýt nástrojový pruh pro Äas v hlavním oknÄ› programu Thum&b Ná&hled Thumb Toolbar Nástrojový pruh pro náhled Thumb toolbar Nástrojový pruh pro náhled Show/hide main program window thumb toolbar Ukázat/Skrýt nástrojový pruh pro náhled v hlavním oknÄ› programu File &System Souborový &systém File System Souborový systém File system Souborový systém Show/hide the file system window Ukázat/Skrýt okno se souborovým systémem &Files &Soubory Files Soubory Show/hide the files window Ukázat/Skrýt okno se soubory M&essages Z&právy Messages Zprávy Show/hide the messages window Ukázat/Skrýt okno se zprávami &Connections &PÅ™ipojení Connections PÅ™ipojení Show/hide the connections window Ukázat/Skrýt okno s pÅ™ipojením F8 F8 Mi&xer Mix&ážní pult Mixer Mixážní pult Show/hide the mixer window Ukázat/Skrýt okno s mixážním pultem F9 F9 &In &PÅ™iblížit Zoom In PÅ™iblížit Zoom in PÅ™iblížit Ctrl++ Ctrl++ &Out &Oddálit Zoom Out Oddálit Zoom out Oddálit Ctrl+- Ctrl+- &Reset &Nastavit znovu &Unlink ZruÅ¡&it odkaz Unlink Clip Odpojit zábÄ›r Unlink clip Odpojit zábÄ›r Unlink current clip Odpojit nynÄ›jší zábÄ›r &Increase &Zvýšit I&nsert &Vložit Remo&ve &Odstranit Ta&ke Zá&bÄ›r Mo&de Reži&m St&ep K&rok &Range... &Rozsah... Remove Range Odstranit rozsah Remove range Odstranit rozsah Remove range as selected Odstranit rozsah jako vybraný Ctrl+Del Ctrl+Delete Remove Track Range Odstranit rozsah stopy Remove track range Odstranit rozsah stopy Remove track range as selected Odstranit rozsah stopy jako vybraný Ctrl+Shift+Del Ctrl+Shift+Del Insert Range Vložit rozsah Insert range Vložit rozsah Insert range as selected Vložit rozsah jako vybraný Ctrl+Ins Ctrl+Ins Insert Track Range Vložit rozsah stopy Insert track range Vložit rozsah stopy Insert track range as selected Vložit rozsah stopy jako vybraný Ctrl+Shift+Ins Ctrl+Shift+Ins Sp&lit &RozdÄ›lit Split Selection RozdÄ›lit výbÄ›r Split selection RozdÄ›lit výbÄ›r Split current selection RozdÄ›lit nynÄ›jší výbÄ›r Ctrl+Y Ctrl+Y Increase Height Zvýšit výšku Increase height Zvýšit výšku Increase track height Zvýšit výšku stopy Ctrl+Shift++ Ctrl+Shift++ &Decrease &Snížit Decrease Height Snížit výšku Decrease height Snížit výšku Decrease track height Snížit výšku stopy Ctrl+Shift+- Ctrl+Shift+- Height Reset Nastavení výšky znovu Height reset Nastavení výšky znovu Reset track height Nastavit znovu výšku stopy Ctrl+Shift+1 Ctrl+Shift+1 Auto &Monitor Automatické sledování Log&arithmic &Logaritmický Automation logarithmic Automatizace Logaritmická Automation curve logarithmic scale Logaritmická stupnice kÅ™ivky automatizace C&olor... Ba&rva... Automation color Barva automatizace Automation curve color Barva kÅ™ivky automatizace &Lock &Zamknout Automation lock Automatizace zamknout Lock automation curve Zamknout kÅ™ivku automatizace Automation playback Automatizace pÅ™ehrát Playback automation curve PÅ™ehrát kÅ™ivku automatizace Automation record Automatizace nahrát Record automation curve Nahrát kÅ™ivku automatizace &Clear &Smazat Automation clear Automatizace smazat Clear automation curve Smazat kÅ™ivku automatizace Loc&k All Zam&knout vÅ¡e Automation lock all Automatizace zamknout vÅ¡e Lock all automation curves Zamknout vÅ¡echny kÅ™ivky automatizace Play &All PÅ™ehrát &vÅ¡e Automation playback all Automatizace pÅ™ehrát vÅ¡e Playback all automation curves PÅ™ehrát vÅ¡echny kÅ™ivky automatizace Rec&ord All Nah&rát vÅ¡e Automation record all Automatizace nahrát vÅ¡e Record all automation curves Nahrát vÅ¡echny kÅ™ivky automatizace C&lear All S&mazat vÅ¡e Automation clear all Automatizace smazat vÅ¡e Clear all automation curves Smazat vÅ¡echny kÅ™ivky automatizace Recor&d &Nahrávat Record Clip Nahrávat zábÄ›r Record clip Nahrávat zábÄ›r Record current clip (overdub) Nahrávat nynÄ›jší zábÄ›r (overdub) &Tempo Adjust... Úprava &tempa... Shift+T Shift+T R&ange... &Rozsah... Take Range Rozsah zábÄ›ru Take range Rozsah zábÄ›ru Zoom Reset Nastavit znovu zvÄ›tÅ¡ení Zoom reset Nastavit znovu zvÄ›tÅ¡ení Ctrl+1 Ctrl+1 &Horizontal &Vodorovné Horizontal Zoom Vodorovné zvÄ›tÅ¡ení Horizontal zoom Vodorovné zvÄ›tÅ¡ení Horizontal zoom mode Režim vodorovného zvÄ›tÅ¡ení &Vertical &Svislé Vertical Zoom Svislé zvÄ›tÅ¡ení Vertical zoom Svislé zvÄ›tÅ¡ení Vertical zoom mode Režim svislého zvÄ›tÅ¡ení All Zoom ZvÄ›tÅ¡it vÅ¡e All zoom ZvÄ›tÅ¡it vÅ¡e All zoom mode Režim zvÄ›tÅ¡ení vÅ¡eho &Grid &Mřížka Grid Mřížka Snap grid view mode Pohledový režim se zapadáváním do mřížky &Zebra &Zebra Zebra Zebra Bar zebra view mode Pohledový režim se zebrovými pruhy Too&l Tips Nástrojové &rady Tool tips Rady k nástrojům Floating tool tips view mode Pohledový režim s plovoucími radami k nástrojům &Refresh &Obnovit Refresh Obnovit Refresh views Obnovit náhledy F5 F5 &Instruments... &Nástroje... Instruments Nástroje Change instrument definitions and files ZmÄ›nit vymezení nástrojů a souborů &Controllers... O&vladaÄe... Controllers OvladaÄe Change MIDI controllers configuration ZmÄ›nit nastavení ovladaÄů MIDI &Buses... S&bÄ›rnice... Buses SbÄ›rnice Change session bus definitions ZmÄ›nit vymezení sbÄ›rnice sezení &Options... Vo&lby... Options Volby Change general application program options ZmÄ›nit vÅ¡eobecné programové volby aplikace F12 F12 &Backward &Dozadu Backward Dozadu Transport backward PÅ™ehrávat zpÄ›t Backspace Posunout o znak zpÄ›t Re&wind PÅ™e&toÄit Rewind PÅ™etoÄit Transport rewind PÅ™etáÄení pÅ™ehrávání F&ast Forward &Rychle vpÅ™ed Fast Forward Rychle vpÅ™ed Fast forward Rychle vpÅ™ed Transport fast forward PÅ™ehrávat rychle dopÅ™edu &Forward Do&pÅ™edu Forward DopÅ™edu Transport forward PÅ™ehrávat dopÅ™edu &Loop &SmyÄka Loop SmyÄka Transport loop SmyÄka pÅ™ehrávání Ctrl+Shift+L Ctrl+Shift+L Step Backward Krok zpÄ›t Step backward Krok zpÄ›t Transport step backward PÅ™ehrávání o krok zpÄ›t Step Forward Krok vpÅ™ed Step forward Krok vpÅ™ed Transport step forward PÅ™ehrávání o krok vpÅ™ed Ctrl+Shift+P Ctrl+Shift+P &Count-in &PoÄítání Count-in PoÄítání Transport mode: None Režim pÅ™ehrávání: Žádný Transport mode set to None Režim pÅ™ehrávání nastaven na žádný &Slave &Řízený Slave Řízený Transport mode: Slave Režim pÅ™ehrávání: Řízený Transport mode set to Slave Režim pÅ™ehrávání nastaven na řízený &Master &Řídící Master Řídící Transport mode: Master Režim pÅ™ehrávání: Řídící Transport mode set to Master Režim pÅ™ehrávání nastaven na řídící &Full Ú&plný Full Úplný Transport mode: Full Režim pÅ™ehrávání: Úplný Transport mode set to Full Režim pÅ™ehrávání nastaven na úplný Pa&nic N&ouzové zastavení Loop &Set &Nastavení smyÄky Loop Set Nastavení smyÄky Loop set Nastavení smyÄky Transport loop set Nastavení smyÄky pÅ™ehrávání Ctrl+L Ctrl+L &Stop Z&astavit Stop Zastavit Transport stop Zastavení pÅ™ehrávání &Play PÅ™&ehrát Tempo M&ap / Markers... Zo&brazení tempa/znaÄky... Tempo Map / Markers Zobrazení tempa/znaÄky Tempo map / markers Zobrazení tempa/znaÄky Change session tempo map / markers ZmÄ›nit zobrazení tempa sezení/znaÄky Play PÅ™ehrát Transport play/pause PÅ™ehrávání/Pozastavení pÅ™ehrávání Space Mezerník Record Nahrát Transport record Náhrávání pÅ™ehrávání &Punch PÅ™epsa&t Punch PÅ™epsat Punch in/out ZaÄátek/Konec pÅ™episu Transport punch in/out ZaÄátek/Konec pÅ™episu pÅ™ehrávání Punch Se&t Nastavení pÅ™epis&u Punch Set Nastavení pÅ™episu Punch in/out set Nastavení zaÄátku/konce pÅ™episu Transport punch in/out set Nastavení zaÄátku/konce pÅ™episu pÅ™ehrávání Ctrl+P Ctrl+P &Metronome &Metronom Metronome Metronom F&ollow Playhead Následovat u&kazatele pÅ™ehrávání Follow Playhead Následovat ukazatele pÅ™ehrávání Follow playhead Následovat ukazatele pÅ™ehrávání A&uto Backward A&utomaticky zpÄ›t Auto Backward Automaticky zpÄ›t Auto backward Automaticky zpÄ›t &Continue Past End PokraÄovat po kone&c Continue Past End PokraÄovat po konec Continue past end PokraÄovat po konec Panic Nouzové zastavení All MIDI tracks shut off (panic) Vypnout vÅ¡echny stopy MIDI (nouzové zastavení) &Shortcuts... &Zkratky... Shortcuts Zkratky Keyboard shortcuts Klávesové zkratky &About... &O programu... Show information about this application program Ukázat informaci o tomto programu About &Qt... O &Qt... About Qt O Qt Show information about the Qt toolkit Ukázat informaci o sadÄ› softwarových nástrojů Qt qtractorMessages Messages Zprávy Logging stopped --- %1 --- Zapisování vzkazů bylo zastaveno --- %1 --- Logging started --- %1 --- Zapisování vzkazů zaÄalo --- %1 --- qtractorMidiControl Note On Nota zapnuta Note Off Nota vypnuta Key Press Stisknutí klávesy Controller OvladaÄ Pgm Change ZmÄ›na Pgm Chan Press Stisknutí kanálu Pitch Bend ZmÄ›na výšky tónu RPN RPN NRPN NRPN Control 14 Kontrol 14 Track Gain Zesílení stopy Track Panning PÅ™ejíždÄ›ní stopy Track Monitor Sledování stopy Track Record Nahrávání stopy Track Mute Ztlumení stopy Track Solo Sólo pro stopu qtractorMidiControlForm Controller files Soubory s ovladaÄi Files Soubory Path Cesta Import controller files Zavést soubory s ovladaÄi &Import... &Zavést... Remove controller file Odstranit soubor s ovladaÄem &Remove &Odstranit Move controller file up on list order Posunout soubor s ovladaÄem nahoru v seznamu &Up &Nahoru Move controller file down on list order Posunout soubor s ovladaÄem dolů v seznamu &Down &Dolů Trac&k &Stopa offse&t &posun &limit &omezení C&ommand Pří&kaz &Parameter &Parametr Controllers OvladaÄe Flags Příznaky MIDI Channel Kanál MIDI Track limit Omezení stopy Command action Krok příkazu Command delta/momentary Příkaz delta/chvilkový D&elta D&elta Command feedback ZpÄ›tná vazba příkazu &Feedback &ZpÄ›tná vazba Map/update controller command PÅ™iÅ™adit/Obnovit příkaz pro ovladaÄ &Map &PÅ™iÅ™adit Controller map Zobrazení ovladaÄe Channel Kanál Command Příkaz Parameter Parametr &Type &Typ &Channel &Kanál MIDI Event type Typ události MIDI MIDI Controller (parameter) OvladaÄ MIDI (parametr) MIDI parameter (track offset) Parametr MIDI (posun stopy) + + Track offset Posun stopy Type Typ Track Stopa Feedback ZpÄ›tná vazba Unmap/remove controller command ZruÅ¡it pÅ™iÅ™azení/Odstranit příkaz pro ovladaÄ U&nmap Zr&uÅ¡it pÅ™iÅ™azení Enable all controllers immediate sync (hook) Povolit vÅ¡em ovladaÄům okamžité seřízení (hák) &Sync &Seřídit Reload/apply all controller files Nahrát znovu/Použít vÅ¡echny soubory s ovladaÄi Relo&ad Na&hrát znovu Export to controller file Vyvést do souboru s ovladaÄem E&xport... V&yvést... Close this dialog Zavřít tento dialog Close Zavřít Import Controller Files Zavést soubory s ovladaÄi Controller files (*.%1) Soubory s ovladaÄi (*.%1) All files (*.*) VÅ¡echny soubory (*.*) Warning UpozornÄ›ní About to remove controller file: "%1" Are you sure? Chystáte se odstranit soubor s ovladaÄem: "%1" Jste si jistý? Export Controller File Vyvést do souboru s ovladaÄem controller OvladaÄ The controller file already exists: "%1" Do you want to replace it? Soubor s ovladaÄem již existuje: "%1" PÅ™ejete si jej nahradit? Saved controller mappings may not be effective the next time you start this program. "%1" Do you want to apply to controller files? Uložená pÅ™iÅ™azení ovladaÄů nezaÄnou působit, dokud znovu program nespustíte. "%1" Chcete použít soubory s ovladaÄi? Controller mappings have been changed. PÅ™iÅ™azení ovladaÄů byla zmÄ›nÄ›na. Do you want to save the changes? Chcete uložit zmÄ›ny? Delta Delta qtractorMidiControlObserverForm &Type: &Typ: MIDI event type Typ události MIDI Cha&nnel: &Kanál: MIDI channel Kanál MIDI In&vert &Obrátit &Hook &Hák L&atch &Zaklapnout Control input connections Ovládat spojení vstupů &Inputs &Vstupy Control output connections Ovládat spojení výstupů &Outputs &Výstupy &Parameter: &Parametr: MIDI parameter Parametr MIDI &Logarithmic &Logaritmický &Feedback &ZpÄ›tná vazba MIDI Controller OvladaÄ MIDI MIDI controller is already assigned. Do you want to replace the mapping? OvladaÄ MIDI je již pÅ™iÅ™azen. Chcete nahradit pÅ™iÅ™azení? Some settings have been changed. Do you want to apply the changes? NÄ›která nastavení byla zmÄ›nÄ›na. Chcete použít tyto zmÄ›ny? &MIDI Controller... OvladaÄ &MIDI... &Automation &Automatizace &Lock &Zamknout &Play &PÅ™ehrát &Record &Nahrávat &Clear &Smazat qtractorMidiControlPluginWidget &Type: &Typ: MIDI event type Typ události MIDI Cha&nnel: &Kanál: MIDI channel Kanál MIDI &Parameter: &Parametr: MIDI parameter Parametr MIDI &Logarithmic &Logaritmický In&vert &Obrátit &Bipolar &Bipolární qtractorMidiEditEvent Zoom in (horizontal) PÅ™iblížit (vodorovnÄ›) Zoom out (horizontal) Oddálit (vodorovnÄ›) Zoom reset (horizontal) Nastavit pÅ™ibližování (vodorovné) znovu qtractorMidiEditList C%1 C%1 qtractorMidiEditTime Play-head ZaÄátek pÅ™ehrávání Edit-head ZaÄátek úprav Edit-tail Konec úprav Loop-start ZaÄátek smyÄky Loop-end Konec smyÄky Punch-in ZaÄátek pÅ™episu Punch-out Konec pÅ™episu Start: %1 End: %2 Length: %3 ZaÄátek: %1 Konec: %2 Délka: %3 qtractorMidiEditView Zoom in (vertical) PÅ™iblížit (svisle) Zoom out (vertical) Oddálit (svisle) Zoom reset (vertical) Nastavit zvÄ›tÅ¡ení (svislé) znovu qtractorMidiEditor C C C#/Db Cis/Des D D D#/Eb Dis/Es E E F F F#/Gb Fis/Ges G G G#/Ab Gis/As A A A#/Bb Ais/B B H Acoustic Bass Drum Akustické basové bicí Bass Drum 1 Velký buben 1 Side Stick Postranní úder Acoustic Snare Malý buben Hand Clap Tlesknutí Electric Snare Elektrický malý buben Low Floor Tom Hluboký floor tom Closed Hi-Hat Hi-Hat (zavÅ™ená) High Floor Tom Vysoký floor tom Pedal Hi-Hat Hi-Hat (pedál) Low Tom Hluboký tom tom Open Hi-Hat Hi-Hat (otevÅ™ená) Low-Mid Tom StÅ™ední tom tom (nižší) Hi-Mid Tom StÅ™ední tom tom (vyšší) Crash Cymbal 1 ÄŒinel crash 1 High Tom Vysoký tom tom Ride Cymbal 1 ÄŒinel ride 1 Chinese Cymbal Čínský Äinel Ride Bell Ride Bell Tambourine Tamburína Splash Cymbal ÄŒinely splash Cowbell Kravský zvonec Crash Cymbal 2 ÄŒinel crash 2 Vibraslap Vibraslap Ride Cymbal 2 ÄŒinel ride 2 Hi Bongo Vysoké bongo Low Bongo Hluboké bongo Mute Hi Conga Vysoké kongo (mute) Open Hi Conga Vysoké kongo (open) Low Conga Hluboké kongo High Timbale Vysoký timbál Low Timbale Hluboký timbál High Agogo Vysoké agogo Low Agogo Hluboké agogo Cabasa Cabasa Maracas Rumba koule Short Whistle Krátký hvizd Long Whistle Dlouhý hvizd Short Guiro Krátké Guiro Long Guiro Dlouhé Guiro Claves Claves Hi Wood Block Vysoký dÅ™evÄ›ný blok Low Wood Block Hluboký dÅ™evÄ›ný blok Mute Cuica Cuíca (mute) Open Cuica Cuíca (open) Mute Triangle Triangl (mute) Open Triangle Triangl (open) Bank Select (coarse) VýbÄ›r banky (hrubý) Modulation Wheel (coarse) ModulaÄní koleÄko (hrubé) Breath Controller (coarse) Ovládání dýchání (hrubé) Foot Pedal (coarse) Nožní pedál (hrubé) Portamento Time (coarse) ÄŒas portamenta (hrubé) Data Entry (coarse) Zadání dat (hrubé) Volume (coarse) Hlasitost (hrubé) Balance (coarse) Vyvážení (hrubé) Pan Position (coarse) Poloha vyvážení (hrubé) Expression (coarse) Výraz (hrubé) Effect Control 1 (coarse) Ovládání efektu 1 (hrubé) Effect Control 2 (coarse) Ovládání efektu 2 (hrubé) General Purpose Slider 1 Obecný posuvník 1 General Purpose Slider 2 Obecný posuvník 2 General Purpose Slider 3 Obecný posuvník 3 General Purpose Slider 4 Obecný posuvník 4 Bank Select (fine) VýbÄ›r banky (jemný) Modulation Wheel (fine) ModulaÄní koleÄko (jemné) Breath Controller (fine) Ovládání dýchání (jemné) Foot Pedal (fine) Nožní pedál (jemné) Portamento Time (fine) ÄŒas portamenta (jemné) Data Entry (fine) Zadání dat (jemné) Volume (fine) Hlasitost (jemné) Balance (fine) Vyvážení (jemné) Pan Position (fine) Poloha vyvážení (jemné) Expression (fine) Výraz (jemné) Effect Control 1 (fine) Ovládání efektu 1 (jemné) Effect Control 2 (fine) Ovládání efektu 2 (jemné) Hold Pedal (on/off) Držení pedálu (zapnuto/vypnuto) Portamento (on/off) Portamento (zapnuto/vypnuto) Soft Pedal (on/off) MÄ›kký pedál (zapnuto/vypnuto) Legato Pedal (on/off) Pedál legato (zapnuto/vypnuto) Hold 2 Pedal (on/off) Držení pedálu 2 (zapnuto/vypnuto) Sound Variation Zvuková obmÄ›na (variace) General Purpose Button 1 (on/off) Obecné tlaÄítko 1 (zapnuto/vypnuto) General Purpose Button 2 (on/off) Obecné tlaÄítko 2 (zapnuto/vypnuto) General Purpose Button 3 (on/off) Obecné tlaÄítko 3 (zapnuto/vypnuto) General Purpose Button 4 (on/off) Obecné tlaÄítko 4 (zapnuto/vypnuto) Effects Level Úroveň efektu Chorus Level Úroveň sboru Celeste Level Úroveň nebeské modÅ™i Phaser Level Úroveň fázeru Data Button Increment Nárůst datového tlaÄítka Data Button Decrement Pokles datového tlaÄítka Non-Registered Parameter (fine) Neregistrovaný parametr (jemné) Non-Registered Parameter (coarse) Neregistrovaný parametr (hrubé) Registered Parameter (fine) Registrovaný parametr (jemné) Registered Parameter (coarse) Registrovaný parametr (hrubé) All Sound Off VÅ¡echny zvuky vypnuty All Controllers Off VÅ¡echny ovladaÄe vypnuty Local Keyboard (on/off) Místní klávesnice (zapnuto/vypnuto) All Notes Off VÅ¡echny noty vypnuty Omni Mode Off Celkový režim vypnut Omni Mode On Celkový režim zapnut Mono Operation Jedna operace Poly Operation Více operací Pitch Bend Sensitivity Citlivost výšky tónu Fine Tune Jemné ladÄ›ní Coarse Tune Hrubé ladÄ›ní Tuning Program Ladicí program Tuning Bank Ladicí banka Vibrato Rate Míra vibrata Vibrato Depth Hloubka vibrata Vibrato Delay ZpoždÄ›ní vibrata Filter Cutoff PÅ™eruÅ¡ení filtru Filter Resonance OzvÄ›na filtru Sostenuto Pedal (on/off) Pedál sostenuto (zapnuto/vypnuto) Release Time ÄŒas uvolnÄ›ní Attack Time ÄŒas nábÄ›hu Brightness Jas Decay Time ÄŒas poklesu Tremolo Level Úroveň tremola EG Attack NábÄ›h EG EG Decay Pokles EG EG Release UvolnÄ›ní EG Drum Filter Cutoff PÅ™eruÅ¡ení filtru bicích Drum Filter Resonance OzvÄ›na filtru bicích Drum EG Attack NábÄ›h EG bicích Drum EG Decay Pokles EG bicích Drum Pitch Coarse Hrubá výška tónu bicích Drum Pitch Fine Jemná výška tónu bicích Drum Level Úroveň bicích Drum Pan Vyvážení bicích Drum Reverb Send Poslání dozvuku (Reverb) bicích Drum Chorus Send Poslání sboru bicích Drum Variation Send Poslání variace bicích Modulation Wheel (14bit) ModulaÄní koleÄko (14bitů) Breath Controller (14bit) Ovládání dýchání (14bitů) Foot Pedal (14bit) Nožní pedál (14bitů) Portamento Time (14bit) ÄŒas portamenta (14bitů) Volume (14bit) Hlasitost (14bitů) Balance (14bit) Vyvážení (14bitů) Pan Position (14bit) Poloha vyvážení (14bitů) Expression (14bit) Výraz (14bitů) Effect Control 1 (14bit) Ovládání efektu 1 (14bitů) Effect Control 2 (14bit) Ovládání efektu 2 (14bitů) General Purpose Slider 1 (14bit) Obecný posuvník 1 (14bitů) General Purpose Slider 2 (14bit) Obecný posuvník 2 (14bitů) General Purpose Slider 3 (14bit) Obecný posuvník 3 (14bitů) General Purpose Slider 4 (14bit) Obecný posuvník 4 (14bitů) Chromatic Chromaticky Major Durový Minor Mollový Melodic Minor (Asc) Melodická mollová (vzestupnÄ›) Melodic Minor (Desc) Melodická mollová (sestupnÄ›) Whole Tone Celá nota Pentatonic Major Pentatonická durová Pentatonic Minor Pentatonická mollová Pentatonic Blues Pentatonická bluesová Pentatonic Neutral Pentatonická neutrální Octatonic (H-W) Oktatonická (H-W) Octatonic (W-H) Oktatonická (W-H) Ionian Jónský Dorian Dórský Phrygian Frygický Lydian Lydický Mixolydian Mixolydický Aeolian Aiolský Locrian Lokrický Egyptian Egyptský Eight Tone Spanish Osmitónový Å¡panÄ›lský Hawaiian Havajský Hindu Hindský Hirajoshi Hirajoshi Hungarian Major MaÄarský durový Hungarian Minor MaÄarský mollový Hungarian Gypsy MaÄarský cikánský Japanese (A) Japonský (A) Japanese (B) Japonský (B) Jewish (Adonai Malakh) Židovský (Adonai Malakh) Jewish (Ahaba Rabba) Židovský (Ahaba Rabba) Jewish (Magen Abot) Židovský (Magen Abot) Oriental (A) Orientální (A) Oriental (B) Orientální (B) Oriental (C) Orientální (C) Roumanian Minor Rumunský mollový Neapolitan Neapolský Neapolitan Major Neapolský durový Neapolitan Minor Neapolský mollový Overtone Podtón Leading Whole Tone Celá nota na zaÄátku Nine Tone Scale Devítitónová stupnice Dominant Seventh Dominanta Septima Augmented ZvÄ›tÅ¡ená Algerian Alžírský Arabian (A) Arabský (A) Arabian (B) Arabský (B) Balinese Balijský Chinese Čínský Diminished ZmenÅ¡ená Japanese (Ichikosucho) Japonský (Ichikosucho) Japanese (Taishikicho) Japonský (Taishikicho) Javaneese Jávský Marva Theta Marva Theta Mela Bhavapriya Mela Bhavapriya Mela Chakravakam Mela Chakravakam Mela Chalanata Mela Chalanata Mela Chitrambari Mela Chitrambari Mela Dharmavati Mela Dharmavati Mela Dhatuvardhani Mela Dhatuvardhani Mela Dhavalambari Mela Dhavalambari Mela Divyamani Mela Divyamani Mela Ganamurti Mela Ganamurti Mela Gangeyabhusani Mela Gangeyabhusani Mela Gavambodhi Mela Gavambodhi Mela Gayakapriya Mela Gayakapriya Mela Hatakambari Mela Hatakambari Mela Jalarnavam Mela Jalarnavam Mela Jhalavarali Mela Jhalavarali Mela Jhankaradhvani Mela Jhankaradhvani Mela Jyotisvarupini Mela Jyotisvarupini Mela Kamavarardhani Mela Kamavarardhani Mela Kantamani Mela Kantamani Mela Kosalam Mela Kosalam Mela Latangi Mela Latangi Mela Manavati Mela Manavati Mela Mararanjani Mela Mararanjani Mela Naganandini Mela Naganandini Mela Namanarayani Mela Namanarayani Mela Navanitam Mela Navanitam Mela Nitimati Mela Nitimati Mela Pavani Mela Pavani Mela Ragavardhani Mela Ragavardhani Mela Raghupriya Mela Raghupriya Mela Ramapriya Mela Ramapriya Mela Rasikapriya Mela Rasikapriya Mela Ratnangi Mela Ratnangi Mela Risabhapriya Mela Risabhapriya Mela Rupavati Mela Rupavati Mela Sadvidhamargini Mela Sadvidhamargini Mela Salagam Mela Salagam Mela Sanmukhapriya Mela Sanmukhapriya Mela Sarasangi Mela Sarasangi Mela Senavati Mela Senavati Mela Subhapantuvarali Mela Subhapantuvarali Mela Sucharitra Mela Sucharitra Mela Sulini Mela Sulini Mela Suryakantam Mela Suryakantam Mela Syamalangi Mela Syamalangi Mela Tanarupi Mela Tanarupi Mela Vagadhisvari Mela Vagadhisvari Mela Vanaspati Mela Vanaspati Mela Varunapriya Mela Varunapriya Mela Yagapriya Mela Yagapriya Persian Perský Purvi Theta Purvi Theta Spanish Gypsy Å panÄ›lský cikánský Todi Theta Todi Theta Enigmatic Enigmaticky Kumoi Kumoi Lydian Augmented Lydická zvÄ›tÅ¡ená Pelog Pelog Prometheus Prometheus Prometheus Neapolitan Neapolský Prometheus Six Tone Symmetrical Å estitónová symetrická Super Locrian Superlokrický Lydian Minor Lydická mollová Lydian Diminished Lydická zmenÅ¡ená Half Diminished Napůl zmenÅ¡ená Bhairav Bhairav Yaman Yaman Todi Todi Jog Jog Multani Multani Darbari Darbari Malkauns Malkauns Bhoopali Bhoopali Shivaranjani Shivaranjani Marwa Marwa Minor 5 Mollový 5 Major 5 Durový 5 5 5 45 45 457 457 M 6 M 6 MIDI Editor MIDI Editor cut Vyjmout delete Smazat insert range Vložit rozsah remove range Odstranit rozsah move PÅ™esunout edit Upravit resize ZmÄ›nit velikost rescale ZmÄ›nit měřítko paste Vložit Time: %1 Type: ÄŒas: %1 Typ: Note On (%1) %2 Velocity: %3 Duration: %4 Nota zapnuta (%1) %2 Dynamika: %3 Doba trvání: %4 Key Press (%1) %2 Value: %3 ZmáÄknout klávesu (%1) %2 Hodnota: %3 Controller (%1) Name: %2 Value: %3 OvladaÄ (%1) Název: %2 Hodnota: %3 RPN (%1) Name: %2 Value: %3 RPN (%1) Název: %2 Hodnota: %3 NRPN (%1) Name: %2 Value: %3 NRPN (%1) Název: %2 Hodnota: %3 Control 14 (%1) Name: %2 Value: %3 OvladaÄ 14 (%1) Název: %2 Hodnota: %3 Pgm Change (%1) ZmÄ›nit Pgm (%1) Chan Press (%1) Stisknout kanál (%1) Pitch Bend (%1) ZmÄ›na výšky tónu (%1) SysEx (%1 bytes) Data: SysEx (%1 bajtů) Data: Unknown (%1) Neznámý (%1) Start: %1 End: %2 Length: %3 ZaÄátek: %1 Konec: %2 Délka: %3 qtractorMidiEditorForm Snap/beat Zapadnout/doba Set current snap to %1 Nastavit nynÄ›jší zapadnutí na %1 Current time (play-head) NynÄ›jší Äas (hrací hlava) Current tempo (BPM) NynÄ›jší tempo (MM) Reset time-sig. Obnovit výchozí taktové oznaÄení Note Velocity Rychlost noty Note type Typ noty Value type Typ hodnoty Parameter type Typ parametru Scale key KlÃ­Ä měřítka Scale type Typ měřítka MIDI clip name Název zábÄ›ru MIDI MIDI file name Název MIDI souboru MIDI track/channel Stopa s MIDI/kanál MOD REŽIM MIDI modification state Stav MIDI úprav REC NAHRÃT MIDI clip record state Stav nahrávání zábÄ›ru MIDI MUTE ZTLUMIT MIDI clip mute state Stav ztlumení zábÄ›ru MIDI 00:00:00.000 00:00:00.000 MIDI clip duration Doba trvání zábÄ›ru MIDI Warning UpozornÄ›ní The current MIDI clip has been changed: "%1" Do you want to save the changes? ZábÄ›r nynÄ›jšího MIDI byl zmÄ›nÄ›n: "%1" Chcete uložit zmÄ›ny? Save Uložit Save MIDI Clip Uložit zábÄ›r MIDI MIDI files (*.%1 *.smf *.midi) Soubory MIDI (*.%1 *.smf *.midi) All files (*.*) VÅ¡echny soubory (*.*) MIDI Editor MIDI Editor Channel %1 Kanál %1 Track %1 Stopa %1 [modified] (upraveno) &Track Sto&pa &File &Soubor Select &Mode Režim &výbÄ›ru &Select V&ybrat &Tools &Nástroje &Edit &Upravit &View &Zobrazit &Toolbars &Nástrojové liÅ¡ty &Zoom &PÅ™iblížit S&nap Z&apadnout T&ransport PÅ™e&hrávání &Help &NápovÄ›da &Save &Uložit Save current MIDI clip to existing file name Uložit zábÄ›r nynÄ›jšího MIDI pod již existujícím názvem souboru Save &As... Uložit &jako... Save As Uložit jako Save as Uložit jako Save current MIDI clip with another file name Uložit zábÄ›r nynÄ›jšího MIDI pod jiným názvem souboru &Inputs &Vstupy Track Inputs Vstupy stop Track inputs Vstupy stop Show current MIDI clip/track input bus connections Ukázat pÅ™ipojení vstupní sbÄ›rnice zábÄ›ru/stopy nynÄ›jšího MIDI &Outputs &Výstupy Track Outputs Výstupy stop Track outputs Výstupy stop Show current MIDI clip/track output bus connections Ukázat pÅ™ipojení výstupní sbÄ›rnice zábÄ›ru/stopy nynÄ›jšího MIDI &Properties... Vlastnosti&... Track Properties Vlastnosti stopy Track properties Vlastnosti stopy Edit current MIDI clip/track properties Upravit vlastnosti zábÄ›ru/stopy nynÄ›jšího MIDI Shift+F2 Shift+F2 Properties Vlastnosti Edit current MIDI clip properties Upravit vlastnosti zábÄ›ru nynÄ›jšího MIDI F4 F4 &Close &Zavřít Close Zavřít Close this MIDI clip editor Zavřít tento editor zábÄ›rů MIDI Edit Of&f Úpravy &vypnuty Edit Off Úpravy vypnuty Edit off Úpravy vypnuty Set edit mode off Nastavit režim úprav na: vypnuto Edit &On Úpravy &zapnuty Edit On Úpravy zapnuty Edit on Úpravy zapnuty Set edit mode on Nastavit režim úprav na: zapnuto Edit &Draw &Kreslení úprav Edit draw mode Režim kreslení úprav Edit draw mode (notes) Režim kreslení úprav (noty) &Undo &ZpÄ›t Undo ZpÄ›t Undo last edit operation Vrátit zpÄ›t poslední úpravu Ctrl+Z Ctrl+Z &Redo &Znovu Redo Znovu Redo last edit operation UdÄ›lat znovu poslední úpravu Ctrl+Shift+Z Ctrl+Shift+Z Cu&t Vyjmou&t Cut Vyjmout Cut current selection into the local clipboard Vyjmout nynÄ›jší výbÄ›r do místní schránky Ctrl+X Ctrl+X &Copy &Kopírovat Copy Kopírovat Copy current selection to the local clipboard Kopírovat nynÄ›jší výbÄ›r do místní schránky Ctrl+C Ctrl+C &Paste &Vložit Paste Vložit Paste local clipboard contents into the current MIDI clip Vložit obsah místní schránky do zábÄ›ru nynÄ›jšího MIDI Ctrl+V Ctrl+V Past&e Repeat... Opakovat v&ložení... Paste Repeat Opakovat vložení Paste repeat Opakovat vložení Paste/repeat local clipboard contents into the current MIDI clip Vložit/Opakovat obsah místní schránky do zábÄ›ru nynÄ›jšího MIDI Ctrl+Shift+V Ctrl+Shift+V &Delete &Smazat Delete Smazat Delete current selection Smazat nynÄ›jší výbÄ›r Del Smazat Select None Žádný výbÄ›r Select none Žádný výbÄ›r Ctrl+Shift+A Ctrl+Shift+A Select Invert Obrátit výbÄ›r Select invert Obrátit výbÄ›r Ctrl+I Ctrl+I Select All Vybrat vÅ¡e Select all Vybrat vÅ¡e Ctrl+A Ctrl+A &Range &Rozsah Select Range Vybrat rozsah Select range Vybrat rozsah Mark range as selected OznaÄit rozsah jako vybraný Ctrl+R Ctrl+R &Quantize... &Kvantizovat... Quantize Kvantizovat Quantize selection Kvantizovat výbÄ›r &Transpose... &PÅ™evést... Transpose PÅ™evést Transpose selection PÅ™evést výbÄ›r &Normalize... &Normalizovat... Normalize Normalizovat Normalize selection Normalizovat výbÄ›r &Randomize... &NáhodnÄ› vybrat... Randomize NáhodnÄ› vybrat Randomize selection NáhodnÄ› vybrat výbÄ›r Snap grid view mode Pohledový režim se zapadáváním do mřížky Too&l Tips Rady k &nástrojům Tool tips Rady k nástrojům Floating tool tips view mode Pohledový režim s plovoucími radami k nástrojům &Windows &Okna I&nsert &Vložka Remo&ve &Odstranit Sc&ale &Měřítko Instrum&ent &Nástroj Not&e Type Typ &poznámky Val&ue Type Typ &hodnoty &Ghost Track &Stínová stopa &Note &Nota St&ep K&rok &Mute &Ztlumit Mute Ztlumit Mute current MIDI clip Ztlumit nynÄ›jší zábÄ›r MIDI &Unlink ZruÅ¡it &odkaz Unlink ZruÅ¡it odkaz Unlink current MIDI clip ZruÅ¡it odkaz na zábÄ›r nynÄ›jšího MIDI Recor&d &Nahrát Record current MIDI clip (overdub) Nahrávat zábÄ›r nynÄ›jšího MIDI (overdub) &Range Set Nastavení rozsa&hu Clip Range Rozsah zábÄ›ru Clip range Rozsah zábÄ›ru Set edit-range from clip extents Nastavit rozsah úprav z rozsahu nynÄ›jšího zábÄ›ru &Loop Set Nastavení s&myÄky Clip Loop SmyÄka zábÄ›ru Clip loop SmyÄka zábÄ›ru Set loop-range from clip extents Nastavit rozsah smyÄky z rozsahu nynÄ›jšího zábÄ›ru &None &Žádný &Invert &Obrátit Insert Range Vložit rozsah Insert range Vložit rozsah Insert range as selected Vložit rozsah jako vybraný Ctrl+Ins Ctrl+Ins &Step &Krok Insert step Vložit krok Insert step (rest) Vložit krok (pomlka) Right DO NOT TRANSLATE Remove Range Odstranit rozsah Remove range Odstranit rozsah Remove range as selected Odstranit rozsah jako vybraný Ctrl+Del Ctrl+Delete Resi&ze... ZmÄ›&nit velikost... Resize ZmÄ›nit velikost Resize selection ZmÄ›nit velikost výbÄ›ru Re&scale... ZmÄ›nit &měřítko... Rescale ZmÄ›nit měřítko Rescale selection ZmÄ›nit měřítko výbÄ›ru T&imeshift... ÄŒa&sový posun... Timeshift ÄŒasový posun Timeshift selection VýbÄ›r Äasového posunu T&empo ramp... T&empový nájezd... Tempo ramp Tempový nájezd Tempo ramp selection VýbÄ›r tempového nájezdu &Menubar &Hlavní nabídka Menubar Hlavní nabídka Show/hide the menubar Ukázat/Skrýt nabídkový pruh Ctrl+M Ctrl+M &Statusbar &Stavový řádek Statusbar Stavový řádek Show/hide the statusbar Ukázat/Skrýt stavový řádek File Toolbar Nástrojový pruh pro soubor File toolbar Nástrojový pruh pro soubor Show/hide the file toolbar Ukázat/Skrýt nástrojový pruh k souboru Edit Toolbar Nástrojový pruh pro úpravy Edit toolbar Nástrojový pruh pro úpravy Show/hide the edit toolbar Ukázat/Skrýt nástrojový pruh pro úpravy View Toolbar Nástrojový pruh pro zobrazení View toolbar Nástrojový pruh pro zobrazení Show/hide the view toolbar Ukázat/Skrýt nástrojový pruh pro zobrazování &Transport PÅ™e&hrávat Transport Toolbar Nástrojový pruh pro pÅ™ehrávání Transport toolbar Nástrojový pruh pro pÅ™ehrávání Show/hide the transport toolbar Ukázat/Skrýt nástrojový pruh pro pÅ™ehrávání T&ime ÄŒ&as Time Toolbar Nástrojový pruh pro Äas Time toolbar Nástrojový pruh pro Äas Show/hide the time toolbar Ukázat/Skrýt nástrojový pruh pro Äas &Scale &Měřítko Scale Toolbar Nástrojový pruh pro měřítko Scale toolbar Nástrojový pruh pro měřítko Show/hide the scale toolbar Ukázat/Skrýt nástrojový pruh pro měřítko Thum&b Ná&hled Thumb Toolbar Nástrojový pruh pro náhled Thumb toolbar Nástrojový pruh pro náhled Show/hide the thumb view toolbar Ukázat/Skrýt nástrojový pruh pro náhled Note &Names &Názvy not Note Names Názvy not Note names Názvy not Whether to show note names Zda ukázat názvy not Note &Duration &Doba trvání noty Note Duration Doba trvání noty Note duration Doba trvání noty Whether note events are shown proportional to duration Zda události vztahující se k notÄ› jsou ukazovány úmÄ›rnÄ› k jejich trvání Note &Color Barva &noty Note Color Barva noty Note color Barva noty Whether note events are colored according to pitch Zda jsou události vztahující se k notÄ› zbarveny podle podle výšky tónu noty &Value Color Barva &hodnoty Value Color Barva hodnoty Value color Barva hodnoty Whether note events are colored according to value (velocity) Zda jsou události vztahující se k notÄ› zbarveny podle hodnoty (rychlost) &Drum Mode Režim &bicích Drum Mode Režim bicích Drum mode Režim bicích Whether note onset events are displayed as diamonds Zda jsou události vztahující se k zaÄátkům not zobrazeny jako diamanty &Events &Události View events Zobrazit události Show/hide the events list Ukázat/Skrýt seznam s událostmi &Preview Notes &Náhled na noty Preview Notes Náhled na noty Preview notes Náhled na noty Preview notes while editing (scrub) Náhled na noty pÅ™i úpravách F&ollow Playhead N&ásledovat ukazatele pÅ™ehrávání Follow Playhead Následovat ukazatele pÅ™ehrávání Follow playhead Následovat ukazatele pÅ™ehrávání &In &PÅ™iblížit Zoom In PÅ™iblížit Zoom in PÅ™iblížit Ctrl++ Ctrl++ &Out &Oddálit Zoom Out Oddálit Zoom out Oddálit Ctrl+- Ctrl+- &Reset &Nastavit znovu Zoom Reset Nastavit znovu zvÄ›tÅ¡ení Zoom reset Nastavit znovu zvÄ›tÅ¡ení Ctrl+1 Ctrl+1 &Horizontal &Vodorovné Horizontal Zoom Vodorovné zvÄ›tÅ¡ení Horizontal zoom Vodorovné zvÄ›tÅ¡ení Horizontal zoom mode Režim vodorovného zvÄ›tÅ¡ení &Vertical &Svislé Vertical Zoom Svislé zvÄ›tÅ¡ení Vertical zoom Svislé zvÄ›tÅ¡ení Vertical zoom mode Režim svislého zvÄ›tÅ¡ení &Zebra &Zebra Zebra Zebra Bar zebra view mode Pohledový režim se zebrovými pruhy Ctrl+Shift+L Ctrl+Shift+L Ctrl+Shift+P Ctrl+Shift+P &All &VÅ¡e All Zoom ZvÄ›tÅ¡it vÅ¡e All zoom ZvÄ›tÅ¡it vÅ¡e All zoom mode Režim zvÄ›tÅ¡ení vÅ¡eho &Grid &Mřížka Grid Mřížka &Refresh &Obnovit Refresh Obnovit Refresh views Obnovit zobrazení F5 F5 &Backward &ZpÄ›t Backward ZpÄ›t Transport backward PÅ™ehrávat zpÄ›t Backspace Posunout se o znak zpÄ›t Re&wind PÅ™e&toÄit Rewind PÅ™etoÄit Transport rewind PÅ™ehrávat pÅ™etoÄení F&ast Forward R&ychle dopÅ™edu Fast Forward Rychle dopÅ™edu Fast forward Rychle dopÅ™edu Transport fast forward PÅ™ehrávat rychle dopÅ™edu &Forward &DopÅ™edu Forward DopÅ™edu Transport forward PÅ™ehrávat dopÅ™edu Step Backward Krok zpÄ›t Step backward Krok zpÄ›t Transport step backward PÅ™ehrávat o krok zpÄ›t Step Forward Krok vpÅ™ed Step forward Krok vpÅ™ed Transport step forward PÅ™ehrávat o krok vpÅ™ed Note Backward O notu zpÄ›t Step note backward Krok o notu zpÄ›t Transport step note backward PÅ™ehrávat krok o notu zpÄ›t Note Forward O notu vpÅ™ed Step note forward Krok o notu vpÅ™ed Transport step note forward PÅ™ehrávat krok o notu vpÅ™ed &Loop &SmyÄka Loop SmyÄka Transport loop SmyÄka pÅ™ehrávání Loop &Set Umístit &smyÄku Loop Set Umístit smyÄku Loop set Umístit smyÄku Transport loop set Nastavení smyÄky pÅ™ehrávání Ctrl+L Ctrl+L &Stop &Zastavit Stop Zastavit Transport stop Zastavení pÅ™ehrávání &Play &PÅ™ehrát Play PÅ™ehrát Transport play/pause PÅ™ehrávání/Pozastavení pÅ™ehrávání Space Mezera &Record &Nahrávat Record Nahrávat Transport record Náhrávání pÅ™ehrávání &Punch &Důraz Punch Důraz Punch in/out Důraz: zapnutý/vypnutý Transport punch in/out ZaÄátek/Konec pÅ™episu pÅ™ehrávání Punch Se&t Nastavení pÅ™epis&u Punch Set Nastavení pÅ™episu Punch in/out set Nastavení zaÄátku/konce pÅ™episu Transport punch in/out set Nastavení zaÄátku/konce pÅ™episu pÅ™ehrávání Ctrl+P Ctrl+P Pa&nic No&uzové zastavení Panic Nouzové zastavení All MIDI tracks shut off (panic) Vypnout vÅ¡echny stopy MIDI (nouzové zastavení) &Shortcuts... &Zkratky... Shortcuts Zkratky Keyboard shortcuts Klávesové zkratky &About... &O... About O Show information about this application program Ukázat informaci o tomto programu About &Qt... O &Qt... About Qt O Qt Show information about the Qt toolkit Ukázat informaci o sadÄ› softwarových nástrojů Qt qtractorMidiEventList Events Události qtractorMidiEventListView::ItemDelegate edit %1 Upravit %1 qtractorMidiEventListView::ItemModel Time ÄŒas Type Typ Name Název Value Hodnota Duration/Data Doba trvání/Data Frame Snímek BBT TDT (takty-doby-tiky) Note On (%1) Nota zapnuta (%1) Note Off (%1) Nota vypnuta (%1) Key Press (%1) Stisknutí klávesy (%1) Controller (%1) OvladaÄ (%1) Control 14 (%1) OvladaÄ 14 (%1) RPN (%1) RPN (%1) NRPN (%1) NRPN (%1) Pgm Change ZmÄ›na Pgm Chan Press Stisknutí kanálu Pitch Bend ZmÄ›na výšky tónu SysEx SysEx Meta (%1) Meta (%1) Unknown (%1) Neznámý (%1) qtractorMidiListView Name Název Fmt Fmt Tracks Stopy tpqn tpqn Path Cesta %1: MIDI file not found. %1: Soubor MIDI nebyl nalezen. Open MIDI Files Otevřít soubory MIDI MIDI files (*.%1 *.smf *.midi) Soubory MIDI (*.%1 *.smf *.midi) All files (*.*) VÅ¡echny soubory (*.*) qtractorMidiMixerMeter Volume (%) Hlasitost (%) % % Pan: %1 Vyvážení (Pan): %1 Volume: %1% Hlasitost: %1% qtractorMidiSysexForm MIDI SysEx MIDI SysEx Name Název Size Velikost Data (hex) Data (hex) Import from SysEx file Zavést ze souboru SysEx &Import... Z&avést... Export to SysEx file Vyvést do souboru SysEx E&xport... V&yvést... Move SysEx item up on list order Posunout položku SysEx nahoru v seznamu &Up &Nahoru Move SysEx item down on list order Posunout položku SysEx dolů v seznamu &Down &Dolů Open SysEx Otevřít SysEx Sysex name Název SysEx Save SysEx Uložit SysEx Delete SysEx Smazat SysEx Create SysEx item VytvoÅ™it položku SysEx &Add &PÅ™idat Update SysEx item Obnovit položku SysEx Upda&te Obnovi&t Remove SysEx item Odstranit položku SysEx &Remove &Odstranit SysEx files (*.%1) Soubory SysEx (*.%1) MIDI files (*.mid *.smf *.midi) Soubory MIDI (*.mid *.smf *.midi) All files (*.*) VÅ¡echny soubory (*.*) Import SysEx Files Zavést soubory SysEx Export SysEx File Vyvést do souboru SysEx Warning UpozornÄ›ní The SysEx file already exists: "%1" Do you want to replace it? Soubor SysEx již existuje: "%1" Chcete jej nahradit? About to replace SysEx: "%1" Are you sure? Chystáte se nahradit SysEx: "%1" Jste si jistý? About to delete SysEx: "%1" Are you sure? Chystáte se smazat SysEx: "%1" Jste si jistý? SysEx settings have been changed. Do you want to apply the changes? Nastavení SysEx byla zmÄ›nÄ›na. Chcete použít tyto zmÄ›ny? Error Chyba SysEx could not be loaded: "%1". Sorry. NepodaÅ™ilo se nahrát SysEx: "%1". Promiňte. qtractorMidiThumbView MIDI Thumb view Zobrazit náhled na MIDI qtractorMidiToolsForm (default) (výchozí) Warning UpozornÄ›ní About to delete preset: "%1" Are you sure? Chystáte se smazat pÅ™ednastavení: "%1" Jste si jistý? none Žádný quantize Kvantizovat transpose PÅ™evést normalize Normalizovat randomize NáhodnÄ› vybrat resize ZmÄ›nit velikost rescale ZmÄ›nit měřítko timeshift ÄŒasový posun temporamp tempovýnájezd MIDI Tools Nástroje MIDI Preset name Název pÅ™ednastavení Save preset Uložit pÅ™ednastavení Delete preset Smazat pÅ™ednastavení &Quantize &Kvantizovat Quantize selected events Kvantizovat výbrané události &Time: ÄŒ&as: Quantize time Kvantizovat Äas Quantize time percent Kvantizovat procento Äasu &Duration: &Doba trvání: Quantize duration Kvantizovat dobu trvání Quantize duration percent Kvantizovat procento doby trvání Swing-quantize time ÄŒas výkyvu-kvantizování Swing-quantize percent Procento výkyvu-kvantizování Swing-quantize type Typ výkyvu-kvantizování Linear Lineární Quadratic Kvadratická Cubic Kubická &Scale: &Měřítko: Scale-quantize key KlÃ­Ä měřítka-kvantizace Scale-quantize type Typ měřítka-kvantizace &Transpose &PÅ™evést Transpose selected events PÅ™evést výbrané události &Note: &Nota: Transpose note PÅ™evést noty Transpose time PÅ™esunout Äas Transpose time format PÅ™esunout formát Äasu Frames Snímky Time ÄŒas BBT TDT (takty-doby-tiky) &Reverse &Obrátit &Normalize &Normalizovat Normalize selected events Normalizovat výbrané události &Percent: &Procento: Normalize percent Normalizovat procento &Compress &Komprimovat Resi&ze &ZmÄ›nit velikost Resize value mode Režim zmÄ›ny velikosti hodnoty Flat Rovný Ramp Nájezd Resize final value ZmÄ›nit koneÄnou velikost &Legato: &Legato: Legato trim/extend type Typ zkrácení/prodloužení legata Normal Normální Trim Zkrátit Extend Prodloužit Legato trim/extend length Délka zkrácení/prodloužení legata Legato mode Režim legato Mono Jedno Poly Více &Join &Spojit &Split: &RozdÄ›lit: Split notes length RozdÄ›lit délku not Split notes offset RozdÄ›lit posun not Relative Relativní Absolute Absolutní Re&scale ZmÄ›nit &měřítko Rescale selected events ZmÄ›nit měřítko vybraných událostí Rescale time ZmÄ›nit měřítko Äasu Rescale duration ZmÄ›nit měřítko doby trvání Rescale value ZmÄ›nit měřítko hodnoty &Invert &Obrátit T&imeshift ÄŒa&sový posun Timeshift selected events ÄŒasovÄ› posunout vybrané události Timeshift ÄŒasovÄ› posunout P: P: Timeshift parameter Parametr pro Äasový posun Timeshift parameter (log) Parametr pro Äasový posun (log) Timeshift curve KÅ™ivka Äasového posunu P = 0 : no change. P > 0 : accelerating shift. P < 0 : slowing down shift. Edit head/tail (blue) markers define the shift range. P = 0 : žádná zmÄ›na. P > 0 : zrychlení posunu. P < 0 : zpomalení posunu. (Modré) znaÄky pro úpravy zaÄátku/konce vymezují rozsah posunu. Timeshift duration Doba trvání posunu T&empo ramp T&empový nájezd Tempo ramp selected events Vybrané události tempového nájezdu Tempo ramp Tempový nájezd From Od Tempo ramp start ZaÄátek tempového nájezdu to do Temporamp end Konec tempového nájezdu Edit head/tail (blue) markers define the ramp range. ZnaÄky úpravy zaÄátku/konce (modré) vymezují rozsah tempa. Tempo ramp duration Doba trvání tempa % % S&wing: &Výkyv: &Value: &Délka trvání: Normalize value Normalizovat délku trvání &Randomize &NáhodnÄ› vybrat Randomize selected events NáhodnÄ› vybrat výbrané události Randomize note/pitch NáhodnÄ› vybrat notu/výšku tónu Randomize time NáhodnÄ› vybrat Äas Randomize duration NáhodnÄ› vybrat dobu trvání Randomize value NáhodnÄ› vybrat délku trvání tónu Resize selected events ZmÄ›nit velikost výbraných událostí Resize duration ZmÄ›nit velikost doby trvání Resize duration format ZmÄ›nit velikost formátu doby trvání Resize value ZmÄ›nit velikost hodnoty qtractorMixer Inputs Vstupy Tracks Stopy Outputs Výstupy Mixer Mixážní pult qtractorMixerMeter Pan Pan qtractorMixerRackWidget &Inputs &Vstupy &Outputs &Výstupy &Monitor &Sledování &Buses... &SbÄ›rnice... &Audio &Zvuk &MIDI &MIDI qtractorMixerStrip inputs Vstupy outputs Výstupy Connect %1 PÅ™ipojit %1 (Audio) (Zvuk) (MIDI) (MIDI) (None) (Žádný) In Vstup Out Výstup qtractorMonitorButton monitor Sledovat qtractorOptionsForm XML Default (*.%1) Výchozí XML (*.%1) XML Regular (*.%1) Běžné XML (*.%1) ZIP Archive (*.%1) Archiv ZIP (*.%1) (Any) (Jakýkoli) Warning UpozornÄ›ní Some settings have been changed. Do you want to apply the changes? NÄ›která nastavení byla zmÄ›nÄ›na. Chcete použít tyto zmÄ›ny? Metronome Bar Audio File Zvukový soubor s taktem pro přístroj sloužící k stanovení tempa v hudbÄ› (metronom) Metronome Beat Audio File Zvukový soubor s dobou pro přístroj sloužící k stanovení tempa v hudbÄ› (metronom) Open Style Sheet Otevřít list stylů Style Sheet files (*.%1) Soubory s listy stylů (*.%1) Icons Theme Directory Adresář s motivy ikon Audio Meter Color Barva měřidla zvuku MIDI Meter Color Barva měřidla MIDI Plug-in Directory Adresář s přídavnými moduly LV2 Presets Directory Adresář s pÅ™ednastaveními LV2 Plug-in Blacklist ÄŒerná listina přídavných modulů Plug-in files (*.%1) Soubory přídavných modulů (*.%1) Messages Font Písmo zpráv Messages Log Záznam zpráv Log files (*.%1) Soubory se záznamy (*.%1) All files (*.*) VÅ¡echny soubory (*.*) Session Template PÅ™edloha pro sezení Session template files (*.qtr *.qts *.%1) Soubory pÅ™edloh (*.qtr *.qts *.%1) pro sezení Which mode to rename existing session files Který režim pro pÅ™ejmenování stávajících souborů sezení &Audio &Zvuk Capture / Export Zachytávání/Vyvedení Audio compression quality to use on capture (record) and export Kvalitu zvukové komprese k použití na zachytávání (nahrávání) a pro vyvedení Audio sample format to use on capture (record) and export Formát zvukového vzorku použít na zachytávání (nahrávání) a pro vyvedení Audio file type to use on capture (record) and export Druh zvukového souboru k použití na zachytávání (nahrávání) a pro vyvedení File &type: Druh &souboru: Sample &format: Formát &vzorku: &Quality: &Kvalita: Playback PÅ™ehrávání Sample-&rate converter type: Druh pÅ™evodníku vzorkovacího &kmitoÄtu: Sample-rate converter quality Kvalita pÅ™evodníku vzorkovacího kmitoÄtu Sinc (Best Quality) Sinc (nejlepší kvalita) Sinc (Medium Quality) Sinc (stÅ™ední kvalita) Sinc (Fastest) Sinc (nejrychlejší) Zero Order Hold Držení nulového pořádku Linear Lineární Transport control mode (JACK) Režim ovládání pÅ™ehrávání (JACK) None Žádný Slave Řízený Master Řídící Full Úplný Whether to apply time-stretching when tempo changes Zda se má použít natahování Äasu, když se zmÄ›ní tempo Aut&omatic time-stretching Auto&matické natahování Äasu Whether to use WSOLA time-stretching Zda se má použít WSOLA natahování Äasu &WSOLA time-stretching &WSOLA natahování Äasu Whether to apply WSOLA quick seek time-stretching Zda se má použít WSOLA rychlé vyhledání natahování Äasu WSOLA quic&k seek WSOLA rych&lé vyhledání Whether to have separate audition/pre-listening player output ports Zda mít oddÄ›lené poslechové/pÅ™ed-poslechové výstupní přípojky pÅ™ehrávacího zařízení Metronome Metronom Whether to enable the audio metronome Zda povolit zvukový metronom &Enable audio metronome &Povolit zvukový metronom &File (bar): &Soubor (takt): Metronome Audio filename (bar) Název zvukového souboru metronomu (takt) Browse for sample audio file (bar) Procházet kvůli zvukovému souboru se vzorkem (takt) Metronome gain (bar) Zesílení metronomu (takt) dB dB &File (beat): &Soubor (doba): Metronome Audio filename (beat) Název zvukového souboru metronomu (doba) Browse for sample audio file (beat) Procházet kvůli zvukovému souboru se vzorkem (doba) &Gain (beat): &Zesílení (doba): Metronome gain (beat) Zesílení metronomu (doba) Whether to have separate audio metronome output ports Zda mít pro metronom oddÄ›lené zvukové výstupní přípojky &MIDI &MIDI MIDI capture (record) quantization MIDI zachytávací (nahrávací) kvantování &Quantize: &Kvantizovat: MIDI file format to use on capture (record) and export Druh MIDI souboru k použití na zachytávání (nahrávání) a pro vyvedení File &format: Formát &souboru: Queue &timer (resolution): ÄŒa&sovaÄ Å™ady (rozliÅ¡ení): Queue timer (resolution) ÄŒasovaÄ Å™ady (rozliÅ¡ení) Whether to enable MIDI queue time drift correction Zda se má povolit oprava Äasového posunu Å™ady MIDI E&nable MIDI queue time drift correction &Povolit opravu Äasového posunu Å™ady MIDI Control Kontrola &MMC: &OSM: MIDI Machine Control (MMC) mode Režim ovládání stroje MIDI (OSM) Input Vstup Output Výstup Duplex Duplexní režim &Device: &Zařízení: MIDI Machine Control (MMC) device id. ID zařízení na ovládání stroje MIDI (OSM). &SPP: &UPP: MIDI Song Position pointer (SPP) control mode Režim ovládání ukazovátka polohy písnÄ› MIDI (UPP) MIDI Clock control mode Ovládací režim &hodin MIDI: Whether to have separate MIDI control ports Zda mít oddÄ›lené ovládací přípojky MIDI Dedicated MIDI &control input/output &JednoúÄelový ovládací vstup/výstup pro MIDI &General &Obecné Number of &recent files: PoÄet &naposledy otevÅ™ených souborů: S&how complete path of session files Ukázat úplnou &cestu souborů se sezením Keep tool &windows always on top Držet vÅ¡echna nástrojová &okna vždy navrchu &Drop multiple audio files into the same track &Upustit mnohonásobné zvukové soubory do stejné stopy Default session &file format: Výchozí formát &souboru se sezením: Default session file format (suffix) Výchozí formát souboru se sezením (přípona) &New session template: PÅ™edloha pro &nové sezení: Save &backup versions of existing sessions: Uložit &zálohovací verze stávajících sezení: Increment previous version (default) Zvýšení pÅ™edchozí verze (výchozí) Increment current version Zvýšení nynÄ›jší verze Whether to enable session auto-save (crash-recovery) Zda se má povolit automatické uložení sezení (obnova po pádu) Auto-save current working session every: Automaticky uložit nynÄ›jší sezení každých: Auto-save period (minutes) Automatické uložení (doba v minutách) minutes minut Whether to ask for confirmation on archive directory removal Zda se má požádat o potvrzení pÅ™i odstranÄ›ní adresáře s archivem C&onfirm archive removals &Potvrdit odstranÄ›ní archivu Whether to reverse mouse middle-button role on keyboard modifiers (Shift/Ctrl) Zda zmÄ›nit úlohu prostÅ™edního tlaÄítka myÅ¡i na modifikátory klávesnice (Shift/Ctrl) Re&verse middle-button modifier role (Shift/Ctrl) &ZmÄ›nit úlohu prostÅ™edního tlaÄítka myÅ¡i na modifikátor (Shift/Ctrl) Transport PÅ™ehrávání Transport &mode: &Režim pÅ™ehrávání: Whether to start as timebase master (JACK) Zda zaÄít jako hlavní Äasová základna (JACK) &Timebase ÄŒ&asová základna &Loop recording mode (takes): Režim nahrávání &smyÄky (zábÄ›ry): Dedicated au&dition/pre-listening player outputs: JednoúÄelové po&slechové/pÅ™ed-poslechové výstupy pÅ™ehrávaÄe: Whether to auto-connect dedicated audio player outputs Zda pÅ™ipojit automaticky jednoúÄelové zvukové výstupy pÅ™ehrávaÄe Auto-&connect &PÅ™ipojit automaticky &Gain (bar): &Zesílení (takt): Dedicated a&udio metronome outputs: JednoúÄelové zv&ukové výstupy metronomu: Whether to auto-connect dedicated audio metronome outputs Zda pÅ™ipojit automaticky jednoúÄelové zvukové výstupy metronomu Auto-co&nnect PÅ™ip&ojit automaticky Whether to have separate MIDI player output ports Zda mít oddÄ›lené výstupní přípojky pÅ™ehrávaÄe MIDI Dedicated MIDI p&layer outputs JednoúÄelové výstupy pÅ™e&hrávaÄe MIDI MIDI Cloc&k: &Hodiny MIDI: Whether to have separate MIDI metronome output port Zda mít oddÄ›lenou výstupní přípojku metronomu pro MIDI Dedicated M&IDI metronome output JenoúÄelový výstup metronomu pro M&IDI &Note (beat): &Nota (doba): Metronome MIDI note (beat) Nota (doba) MIDI pro metronom Metronome MIDI velocity (beat) Rychlost (doba) MIDI pro metronom &Duration (beat): &Doba trvání (doba): &Velocity (beat): &Rychlost (doba): Metronome MIDI velocity (bar) Rychlost (takt) MIDI pro metronom &Duration (bar): &Doba trvání (takt): &Velocity (bar): &Rychlost (takt): Metronome MIDI note (bar) Nota MIDI pro metronom (takt) &Note (bar): &Nota (takt): &Channel: &Kanál: Metronome MIDI channel Kanál MIDI metronomu Whether to enable the MIDI metronome Zda se má povolit MIDI metronom &Enable MIDI metronome &Povolit MIDI metronom &Display &Zobrazit Whether to use desktop environment native dialogs. Zda se mají použít původní dialogy prostÅ™edí plochy. Use desktop environment &native dialogs Použít pů&vodní dialogy prostÅ™edí plochy &Style theme: Téma &stylu: M&essages limit: Nejvyšší poÄet &zpráv: Options Volby Whether to ask for confirmation on removal Zda se má požádat o potvrzení pÅ™i odstranÄ›ní &Confirm removals &Potvrdit odstranÄ›ní The maximum number of recent files to keep in menu Nejvyšší poÄet naposledy otevÅ™ených souborů, které mají být podrženy v seznamu nabídek Whether to capture standard output (stdout/stderr) into messages window Zda se má zachytit obvyklý výstup (stdout/stderr) do okna se zprávami Capture standard &output Zachytit obvyklý &výstup Whether to show the complete directory path of loaded session files Zda se má ukázat úplnou adresářovou cestu nahraných souborů se sezením Loop recording mode (takes) Režim nahrávání &smyÄky (zábÄ›ry) First První Last Poslední Whether to remove audio peak files on session close Zda se mají na závÄ›r sezení odstranit nejvyšší zvukové soubory Auto-remove audio pea&k files Automatické odstranÄ›ní nej&vyšších zvukových souborů Whether to keep all tool windows on top of the main window Zda se mají držet vÅ¡echna nástrojová okna navrchu hlavního okna Time display format Formát zobrazení Äasu Frames Snímky Time ÄŒas BBT TDT Whether to try dropping multiple audio files into the same track Zda se má zkusit upustit mnohonásobné zvukové soubory do stejné stopy &Base font size: &Základní velikost fontu: Base application font size (pt.) Základní velikost fontu aplikace (v bodech) (default) (výchozí) 6 6 7 7 8 8 9 9 10 10 11 11 12 12 Session Sezení Whether to create new sessions based on template Zda se mají vytvoÅ™it nová sezení založená na pÅ™edlohách New session template PÅ™edloha pro nové sezení Browse for new session template Procházet kvůli pÅ™edloze pro nové sezení Whether to save backup versions of existing sessions Zda se mají uložit zálohovací verze stávajících sezení Reverse &keyboard modifiers role (Shift/Ctrl) &ZmÄ›nit úlohu &klávesového modifikátoru (Shift/Ctrl) Defaults Výchozí &Time display format: Formát zobrazení Ä&asu: Custom Vlastní &Color theme: &Barevné téma: Custom color palette theme Vlastní téma barevné palety Wonton Soup DO NOT TRANSLATE KXStudio DO NOT TRANSLATE Custom widget style theme Vlastní téma stylu prvku Meters Měřidla &Audio: &Zvuk: Audio meter level Úroveň měřidla zvuku Over Nad 0 dB 0 dB 3 dB 3 dB 6 dB 6 dB 10 dB 10 dB Audio meter color Barva měřidla zvuku Select custom audio meter color Vybrat vlastní barvu měřidla zvuku ... ... &MIDI: &MIDI: MIDI meter level Úroveň měřidla MIDI Peak Vrchol MIDI meter color Barva měřidla MIDI Select custom MIDI meter color Vybrat vlastní barvu měřidla MIDI Reset meter colors to default Nastavit barvy měřidel znovu na výchozí &Reset &Nastavit znovu Messages Zprávy Sample messages text font display Font zobrazení textu u ukázkových zpráv Select font for the messages text display Vybrat písmo pro zobrazení textu u zpráv &Font... &Písmo... Whether to keep a maximum number of lines in the messages window Zda se má podržet nejvyšší poÄet řádků v oknÄ› se zprávami The maximum number of message lines to keep in view Nejvyšší poÄet řádků zprávy, který se ponechá v náhledu lines Řádky Whether to hold auto-scrolling (follow play-head) on edits. Zda pÅ™i úpravách podržet automatické projíždÄ›ní (sledovat polohu pÅ™ehrávání). Whether to reverse keyboard modifiers role on transport positioning (Shift/Ctrl) Zda zmÄ›nit úlohu klávesového modifikátoru na umístÄ›ní pÅ™ehrávání (Shift/Ctrl) Whether to keep all editor windows on top of the main window Zda se mají držet vÅ¡echna okna editoru navrchu hlavního okna Keep &editor windows always on top Držet vÅ¡echna okna &editoru vždy navrchu Whether to use RubberBand formant preserve Zda má být použito zachování formantu RubberBand RubberBand &formant preserve Zachování &formantu RubberBand Whether to use RubberBand R3 finer engine Zda používat jemnÄ›jší nástroj RubberBand R3 RubberBand R&3 finer engine JemnÄ›jší nástroj RubberBand R&3 Connections PÅ™ipojení Whether to warn about audio self-connections Zda upozorňovat na vlastní pÅ™ipojení zvuku &Warn about self-connections &Upozorňovat na vlastní pÅ™ipojení zvuku &Count-in: &PoÄítání: Count-in mode Režim poÄítání Recording Nahrávání Count-in number PoÄet poÄítání beats dob &Offset (latency): &Posun (prodleva): Metronome Audio offset (latency) Posun zvuku metronomu (prodleva) Whether to reset/resend all controllers on playback start Zda obnovit výchozí/znovu odeslat vÅ¡echny ovladaÄe pÅ™i spuÅ¡tÄ›ní pÅ™ehrávání &Reset all controllers on playback start &Ã’bnovit výchozí vÅ¡echny ovladaÄe pÅ™i spuÅ¡tÄ›ní pÅ™ehrávání Metronome MIDI duration (bar) Doba trvání MIDI pro metronom (takt) Metronome MIDI duration (beat) Doba trvání MIDI pro metronom (doba) Metronome MIDI offset (latency) Posun MIDI pro metronom (prodleva) &Hold auto-scrolling (follow play-head) on edits PÅ™i úpravách po&držet automatické posouvání (sledovat ukazatel pÅ™ehrávání) Trac&k color saturation: Sytost barvy &stopy: Default new track color saturation Výchozí sytost barvy nové stopy % % Manage custom color palette themes Spravovat vlastní motivy palety barev &Icons theme: Motiv &ikon: Custom icons theme directory Adresář s vlastními motivy ikon Browse for custom icons theme directory Procházet kvůli adresáři s vlastními motivy ikon St&yle sheet: List st&ylů: Custom style sheet (*.qss) Vlastní list stylů (*.qss) Browse for custom style sheet (*.qss) Procházet kvůli vlastnímu souboru stylů (*.qss) Back ZpÄ›t Logging Zaznamenávání Messages log file Soubor se záznamy zpráv Browse for the messages log file location Procházet kvůli umístÄ›ní souboru se záznamy zpráv Whether to activate a messages logging to file. Zda se má zapnout zaznamenávání zpráv do souboru. Messages &log file: Soubor se &záznamy zpráv: &Plugins &Přídavné moduly Paths Cesty Plugin type Druh přídavného modulu Plugin path Cesta k přídavnému modulu Browse plugin path Procházet cestu k přídavnému modulu Add plugin path PÅ™idat cestu k přídavnému modulu &Add &PÅ™idat Plugin paths Cesty k přídavnému modulu Remove plugin path Odstranit cestu k přídavnému modulu &Remove &Odstranit Move up path PÅ™esunout cestu nahoru &Up &Nahoru Move down path PÅ™esunout cestu dolů &Down &Dolů &LV2 Presets directory: Adresář s pÅ™ednastaveními &LV2: LV2 Presets directory (default: ~/.lv2) Adresář s pÅ™ednastaveními LV2 (výchozí: ~/.lv2) Browse LV2 Presets directory Procházet adresář s pÅ™ednastaveními LV2 Instruments Nástroje Whether to have separate audio output ports Zda mít oddÄ›lené zvukové výstupní přípojky Dedicated audi&o outputs: JednoúÄelové zvuk&ové výstupy: Whether to auto-connect dedicated audio output ports Zda pÅ™ipojit automaticky jednoúÄelové zvukové výstupní přípojky Au&to-connect PÅ™ipoji&t automaticky Editor Editor Whether to open plugin's editor (GUI) by default Zda ve výchozím nastavení otevřít editor (rozhraní) přídavného modulu Open plugin's &editor (GUI) by default Otevřít &editor (rozhraní) přídavného modulu &Select plugin's editor (GUI) if more than one are available &Vybrat editor (rozhraní) přídavného modulu, pokud je dostupný více než jeden Blacklist ÄŒerná listina Plugin blacklist path Cesta k Äerné listinÄ› přídavných modulů Browse plugin blacklist path Procházet cestu k Äerné listinÄ› přídavných modulů Add plugin blacklist path PÅ™idat cestu k Äerné listinÄ› přídavných modulů Plugin blacklist paths Cesty k Äerným listinám přídavných modulů Remove plugin blacklist path Odstranit cestu k Äerné listinÄ› přídavných modulů Clear plugin blacklist paths Smazat cesty k Äerným listinám přídavných modulů &Clear &Smazat Whether to select plugin's editor (GUI) if more than one are available Zda vybrat editor (rozhraní) přídavného modulu, pokud je dostupný více než jeden qtractorPaletteForm Color Themes Barevná témata Name Název Current color palette name NynÄ›jší název barevné palety Save Uložit Save current color palette name Uložit nynÄ›jší název barevné palety Delete Smazat Delete current color palette name Smazat nynÄ›jší název barevné palety Palette Paleta Current color palette NynÄ›jší barevná paleta Generate: VytvoÅ™it: Base color to generate palette Základní barva pro vytvoÅ™ení palety Reset Nastavit znovu Reset all current palette colors Nastavit znovu vÅ¡echny barvy nynÄ›jší palety Import a custom color theme (palette) from file Zavést vlastní barevné téma (paletu) ze souboru Import... Zavést... Export a custom color theme (palette) to file Vyvést vlastní barevné téma (paletu) do souboru Export... Vyvést... Show Details Ukázat podrobnosti Import File - %1 Zavést soubor - %1 Palette files (*.%1) Soubory palet (*.%1) Save Palette - %1 Uložit paletu - %1 All files (*.*) VÅ¡echny soubory (*.*) Warning - %1 Varování - %1 Could not import from file: %1 Sorry. NepodaÅ™ilo se zavést ze souboru: "%1". Promiňte. Export File - %1 Vyvést soubor - %1 Some settings have been changed. Do you want to discard the changes? NÄ›která nastavení byla zmÄ›nÄ›na. Chcete zahodit tyto zmÄ›ny? Some settings have been changed: "%1". Do you want to save the changes? NÄ›která nastavení byla zmÄ›nÄ›na. "%1". Chcete uložit tyto zmÄ›ny? qtractorPaletteForm::PaletteModel Color Role Úloha barvy Active ÄŒinná Inactive NeÄinná Disabled Zakázáno qtractorPasteRepeatForm Warning UpozornÄ›ní Some settings have been changed. Do you want to apply the changes? NÄ›která nastavení byla zmÄ›nÄ›na. Chcete použít tyto zmÄ›ny? Paste Repeat Opakovat vložení Repeat Opakovat &Count: &PoÄet: Repeat count PoÄet opakování (repetic) &Period: &Perioda (doba): Repeat period Doba opakování (repetice) Repeat period format Formát doby opakování (repetice) Frames Snímky Time ÄŒas BBT TDT qtractorPluginForm Page %1 Strana %1 %1 [%2], %3 instance(s), %4 channel(s). %1 [%2], %3 instance, %4 kanál(y). (none) (Žádné) Open Preset Otevřít pÅ™ednastavení Preset files (*.%1) Soubory s pÅ™ednastaveními (*.%1) All files (*.*) VÅ¡echny soubory (*.*) Error Chyba Latency: %1 ms (%2 frames) Prodleva: %1 ms (%2 snímků) (no latency) (bez prodlevy) &None &Žádné Save Preset Uložit pÅ™ednastavení Preset could not be loaded from file: "%1". Sorry. PÅ™ednastavení nemohlo být nahráno ze souboru: "%1". Promiňte. Preset could not be saved to file: "%1". Sorry. PÅ™ednastavení nemohlo být uloženo do souboru: "%1". Promiňte. Warning UpozornÄ›ní About to delete preset: "%1" (%2) Are you sure? Chystáte se smazat pÅ™ednastavení: "%1" (%2) Jste si jistý? Open preset Otevřít pÅ™ednastavení Preset name Název pÅ™ednastavení Save preset Uložit pÅ™ednastavení Delete preset Smazat pÅ™ednastavení Alias: PÅ™ezdívka: Plugin alias PÅ™ezdívka přídavného modulu Edit plugin Upravit přídavný modul Edit upravit About O Outputs (Sends) Výstupy (Poslání) Sends Poslání Inputs (Returns) Vstupy (Navrácení) Returns Navrácení Auto-connect PÅ™ipojit automaticky Aux Send Bus: SbÄ›rnice aux-send: Manage buses Spravovat sbÄ›rnice ... ... Audio bus I/O matrix Matrice vstupu/výstupu zvukové sbÄ›rnice I/O Matrix... Matrice vstupu/výstupu... Direct Access Parameter Parametr pro přímý přístup Direct Access Přímý přístup Plugin Properties Vlastnosti přídavného modulu Active Aktivní qtractorPluginListView copy plugin Kopírovat přídavný modul activate all plugins Zapnout vÅ¡echny přídavné moduly deactivate all plugins Vypnout vÅ¡echny přídavné moduly remove all plugins Odstranit vÅ¡echny přídavné moduly Import Plugins Zavést přídavné moduly XML files (*.%1) Soubory XML (*.%1) All files (*.*) VÅ¡echny soubory (*.*) Warning Varování About to remove and import all plugins: "%1" Are you sure? Chystáte se odstranit a zavést vÅ¡echny přídavné moduly: "%1") Jste si jistý? Export Plugins Vyvést přídavné moduly Aux Send: Aux Send: &Move Here &PÅ™esunout na toto místo &Copy Here &Kopírovat na toto místo C&ancel Z&ruÅ¡it &Add Plugin... &PÅ™idat přídavný modul... &Audio &Zvuk Add &Insert PÅ™idat &vložku Add &Aux Send PÅ™idat &aux-send &Sends &Poslání &Returns &Navrácení &MIDI &MIDI Add &Controller PÅ™idat &ovladaÄ Ac&tivate &Zapnout &Remove &Odstranit Pre&set &PÅ™ednastavení Dire&ct Access Pří&mý přístup &None &Žádný &Properties... Vlastnosti&... &Edit &Upravit &Import... &Zavést... E&xport... V&yvést... &Auto-connect &PÅ™ipojit automaticky Acti&vate All Z&apnout vÅ¡e I&nserts &Vložky Deactivate Al&l Vypnout v&Å¡e Re&move All Od&stranit vÅ¡e Move &Up Posunout &nahoru Move &Down Posunout &dolů &Outputs &Výstupy &Dedicated &JednoúÄelové qtractorPluginParamWidget Open File Otevřít soubor qtractorPluginSelectForm GUI GUI EXT EXT RT RT Plugins Přídavné moduly Reset filter Nastavit filtr znovu X X Plugin search string (regular expression) Vyhledávací Å™etÄ›zec pro přídavný modul (regulární výraz) Plugin type Druh přídavného modulu Available plugins Dostupné přídavné moduly Name Název Audio Zvuk MIDI MIDI Control Kontrola Modes Mode Režimy Path Cesta Index Rejstřík Instances Příklady Type Druh Plugin scanning in progress... Běží hledání přídavných modulů... Rescan for available plugins (refresh) Prohledat znovu na dostupné přídavné moduly (obnovit) &Rescan &Prohledat znovu... qtractorSessionForm Warning UpozornÄ›ní Session directory does not exist: "%1" Do you want to create it? Adresář se sezením neexistuje: "%1" Chcete jej vytvoÅ™it? Some settings have been changed. Do you want to apply the changes? NÄ›která nastavení byla zmÄ›nÄ›na. Chcete použít tyto zmÄ›ny? Session Directory Adresář sezení Session Sezení &Name: &Název: Session name Název sezení &Directory: &Adresář: Whether to auto-name the session directory Zda automaticky pojmenovat adresář se sezením &Auto &Automaticky Session directory Adresář sezení Browse for session directory Procházet kvůli adresáři sezení ... ... &Description: &Popis: Session description Popis sezení Properties Vlastnosti Time ÄŒas Sample &Rate: Vzorkovací &kmitoÄet: Sample rate (Hz) Vzorkovací kmitoÄet (Hz) 44100 44100 48000 48000 96000 96000 192000 192000 &Tempo: &Tempo: Tempo (BPM) / Signature Tempo (MM)/Taktové oznaÄení Resolution (ticks/beat; tpqn) RozliÅ¡ení (tiky/doby; tpqn) T&icks/Beat: T&iky/doby: View Zobrazit &Snap/Beat: &Zapadnout/doba: Snap/beat Zapadnout/doba &Pixels/Beat: &Obrazové body/doba: Pixels/beat Obrazové body/doba &Horizontal Zoom: &Vodorovné pÅ™iblížení: Horizontal Zoom (%) Vodorovné pÅ™iblížení (%) % % &Vertical Zoom: &Svislé pÅ™iblížení: Vertical Zoom (%) Svislé pÅ™iblížení (%) qtractorShortcutForm Search shortcuts Hledat klávesové zkratky Warning UpozornÄ›ní Keyboard shortcut (%1) already assigned (%2). Klávesová zkratka (%1) již pÅ™iÅ™azena (%2). Keyboard shortcuts have been changed. Do you want to apply the changes? Klávesové zkratky byly zmÄ›nÄ›ny. Chcete použít tyto zmÄ›ny? MIDI Controller shortcuts have been changed. Do you want to apply the changes? Klávesové zkratky ovladaÄe MIDI byly zmÄ›nÄ›ny. Chcete použít tyto zmÄ›ny? &MIDI Controller... OvladaÄ &MIDI... Shortcuts Zkratky Shortcut search string (regular expression) Vyhledávací Å™etÄ›zec pro klávesovou zkratku (regulární výraz) Menu/Action Nabídka/ÄŒinnost Description Popis Keyboard Klávesnice MIDI Controller OvladaÄ MIDI qtractorTakeRangeForm Range Rozsah Selection range Rozsah výbÄ›ru &Selection &VýbÄ›r Loop range Rozsah smyÄky &Loop &SmyÄka Punch range Rozsah pÅ™epsání &Punch &PÅ™epsání Time ÄŒas BBT TDT (takty-doby-tiky) Edit range Rozsah úprav &Edit Úp&ravy Custom range Vlastní rozsah &Custom &Vlastní St&art: Za&Äátek: Clip start ZaÄátek zábÄ›ru Take Range Rozsah zábÄ›ru En&d: Ko&nec: Clip offset Posun zábÄ›ru Select Vybrat Current take NynÄ›jší zábÄ›r Format Formát Time display format Formát zobrazení Äasu Frames Snímky Take %1 ZábÄ›r %1 qtractorTempoAdjustForm Tempo Adjust Úprava tempa Metronome Metronom Tempo/Time signature Tempo/Taktové oznaÄení &Detect &Rozpoznat T&ap ZaÅ¥u&kání Range Rozsah &Start: &ZaÄátek: Range start ZaÄátek rozsahu &Beats: &Doby: Time ÄŒas BBT TDT (takty-doby-tiky) &Length: &Délka: &Tempo: &Tempo: R&eset Obnovit &výchozí Range length Délka rozsahu Range beats Doby rozsahu A&djust &Upravit Format Formát Time display format Formát zobrazení Äasu Frames Snímky Warning Varování Some settings have been changed. Do you want to apply the changes? NÄ›která nastavení byla zmÄ›nÄ›na. Chcete použít tyto zmÄ›ny? qtractorThumbView Thumb view Zobrazit náhled qtractorTimeScale C C B# His C# Cis Db Des D D D# Dis Eb Es E E Fb Fes F F E# Eis F# Fis Gb Ges G G G# Gis Ab As A A A# Ais Bb B B H Cb Ces qtractorTimeScaleForm Bar Takt Time ÄŒas Tempo Tempo &Bar: &Takt: Tempo map / Markers Zobrazení tempa/ZnaÄky Marker ZnaÄka Bar location UmístÄ›ní taktu T&ime: ÄŒ&as: Time/frame location UmístÄ›ní Äasu/snímku &Tempo: &Tempo: T&ap ZaÅ¥u&kání Marker text Text znaÄky ... ... Marker color Barva znaÄky Tempo Map / Markers Zobrazení tempa/znaÄky Key KlÃ­Ä Tempo (BPM) / Time signature Tempo (MM)/Taktové oznaÄení &Key signature: &PÅ™edznamenání: Key signature (accidentals) PÅ™edznamenání (posuvky) Key signature (mode) PÅ™edznamenání (režim) - - Major Durový Minor Mollový &Marker: &ZnaÄka: Tempo &scale factor: Násobek &zmÄ›ny tempa: Tempo scale factor Násobek zmÄ›ny tempa App&ly &Použít Refresh tempo map Obnovit zobrazení tempa Re&fresh &Obnovit Add node PÅ™idat uzel &Add &PÅ™idat Update node Obnovit uzel &Update &Obnovit Remove node Odstranit uzel &Remove &Odstranit Close this dialog Zavřít tento dialog Close Zavřít Warning UpozornÄ›ní Some settings have been changed. Do you want to apply the changes? NÄ›která nastavení byla zmÄ›nÄ›na. Chcete použít tyto zmÄ›ny? About to remove tempo node: %1 (%2) %3 %4/%5 Are you sure? Chystáte se odstranit uzel s tempem: %1 (%2) %3 %4/%5 Jste si jistý? Some settings have been changed. Do you want to discard the changes? NÄ›která nastavení byla zmÄ›nÄ›na. Chcete zahodit tyto zmÄ›ny? tempo factor Násobek tempa Marker Color Barva znaÄky &Refresh &Obnovit qtractorTimeSpinBox &Frames &Snímky &Time ÄŒ&as &BBT &TDT qtractorTrackForm Normal Normální Bank MSB Banka MSB Bank LSB Banka LSB Patch PolíÄko Custom &Icon... Vlastní &ikona... &Drums &Bicí Drum &Kit &Sada bicích &Bass &Bas A&coustic Bass &Akustický bas &Guitar &Kytara &Electric Guitar &Elektrická kytara &Piano &Klavír &Acoustic Piano A&kustické piano &Microphone &Mikrofon Vi&ntage Microphone Å &piÄkový mikrofon &Speaker &Reproduktor &Trumpet &Trubka &Violin &Housle Warning UpozornÄ›ní Some settings have been changed. Do you want to apply the changes? NÄ›která nastavení byla zmÄ›nÄ›na. Chcete použít tyto zmÄ›ny? (No instrument) (Žádný nástroj) Image files (%1) Obrázkové soubory (%1) All files (*.*) VÅ¡echny soubory (*.*) %1 ms (%2 frames) %1 ms (%2 snímků) (no latency) (bez prodlevy) Track Icon Ikona stopy Foreground Color Barva popÅ™edí Background Color Barva pozadí (None) (Žádný) Track Stopa &Name: &Název: Track name description Popis názvu stopy Track icon Ikona stopy Type Druh Audio track type Druh zvukové stopy &Audio &Zvuk MIDI track type Druh MIDI stopy &MIDI &MIDI Input / Output Vstup/Výstup Input bus name Název vstupní sbÄ›rnice Output bus name Název výstupní sbÄ›rnice Manage buses Spravovat sbÄ›rnice ... ... MIDI / Instrument MIDI/nástroj &Program: &Program: &Bank: &Banka: Bank &Select Method: Metoda &výbÄ›ru zvukové banky: &Omni &Omni MIDI Omni: Capture All Channels MIDI Omni: zachytávání vÅ¡ech kanálů &Channel: &Kanál: MIDI Channel (1-16) MIDI kanál (1-16) MIDI Patch: Instrument MIDI políÄko: nástroj MIDI Patch: Bank Select Method MIDI políÄko: metoda výbÄ›ru zvukové banky MIDI Patch: Drum Mode MIDI políÄko: režim bicích MIDI Patch: Bank MIDI políÄko: zvuková banka MIDI Patch: Program MIDI políÄko: program(ovat) View / Colors Pohled/Barvy &Foreground: &PopÅ™edí: Foreground color Barva popÅ™edí Select custom track foreground color Vybrat vlastní barvu popÅ™edí stopy Bac&kground: Po&zadí: Background color Barva pozadí Select custom track background color Vybrat vlastní barvu pozadí stopy Auto Automaticky Plugins Přídavné moduly Track plugins Přídavné moduly stopy Add plugin PÅ™idat přídavný modul &Add... &PÅ™idat... Remove plugin Odstranit přídavný modul &Remove &Odstranit Move plugin up Posunout přídavný modul nahoru &Up &Nahoru Move plugin down Posunout přídavný modul dolů &Down &Dolů Whether to enable plugin latency/delay compensation Zda se má povolit vyrovnání prodlevy/zpoždÄ›ní přídavného modulu &Latency compensation &Vyrovnání prodlevy/zpoždÄ›ní Current total latency NynÄ›jší celková prodleva qtractorTrackList Nr ÄŒ. Track Name Název stopy Bus SbÄ›rnice Ch Kanál Patch PolíÄko Instrument Nástroj qtractorTrackTime Play-head ZaÄátek pÅ™ehrávání Edit-head ZaÄátek úprav Edit-tail Konec úprav Loop-start ZaÄátek smyÄky Loop-end Konec smyÄky Punch-in ZaÄátek pÅ™episu Punch-out Konec pÅ™episu Start: %1 End: %2 Length: %3 ZaÄátek: %1 Konec: %2 Délka: %3 qtractorTrackView Zoom in (horizontal) PÅ™iblížit (vodorovnÄ›) Zoom out (horizontal) Oddálit (vodorovnÄ›) Zoom in (vertical) PÅ™iblížit (svisle) Zoom out (vertical) Oddálit (svisle) Zoom reset Nastavit zvÄ›tÅ¡ení znovu add clip PÅ™idat zábÄ›r Start: %1 End: %2 Length: %3 ZaÄátek: %1 Konec: %2 Délka: %3 clip %1 ZábÄ›r %1 fade-in Postupné zesílení signálu fade-out Postupné zeslabení signálu clip stretch Natáhnout zábÄ›r clip resize ZmÄ›nit velikost zábÄ›ru clip repeat Opakování zábÄ›ru %1 automation %1 automatizace %1 clip %1 zábÄ›r move automation PÅ™esunout automatizaci paste automation Vložit automatizaci cut Vyjmout delete Smazat split RozdÄ›lit move clip PÅ™esunout zábÄ›r paste clip Vložit zábÄ›r qtractorTracks Tracks Stopy new clip Nový zábÄ›r split clip RozdÄ›lení zábÄ›ru clip normalize Normalizace zábÄ›ru tempo ramp tempový nájezd clip import Zavedení zábÄ›ru Warning UpozornÄ›ní About to remove track: "%1" Are you sure? Chystáte se odstranit stopu: "%1" Jste si jistý? quantize Kvantizovat mute clip Ztlumit zábÄ›r transpose PÅ™evést normalize Normalizovat randomize Vybrat náhodnÄ› resize ZmÄ›nit velikost rescale ZmÄ›nit měřítko timeshift ÄŒasový posun Audio file import "%1" on %2 %3. Zavedení zvukového souboru "%1" do %2 %3. Audio file import: "%1". Zavedení zvukového souboru: "%1". MIDI file import "%1" track-channel %2 on %3 %4. Zavedení MIDI souboru: "%1", stopa-kanál %2 %3 %4. clip merge SlouÄení zábÄ›ru Merge/Export SlouÄit/Vyvést Merge/Export Audio Clip SlouÄit/Vyvést zvukový zábÄ›r Audio clip merge/export: "%1" started... SlouÄení/Vyvedení zvukového zábÄ›ru: "%1" právÄ› zaÄalo... Audio clip merge/export: "%1" complete. SlouÄení/Vyvedení zvukového zábÄ›ru: "%1" je hotovo. Merge/Export MIDI Clip SlouÄit/Vyvést zábÄ›r MIDI MIDI files (*.mid *.smf *.midi) Soubory MIDI (*.mid *.smf *.midi) All files (*.*) VÅ¡echny soubory (*.*) MIDI clip merge/export: "%1" started... SlouÄení/Vyvedení zábÄ›ru MIDI: "%1" právÄ› zaÄalo... MIDI clip merge/export: "%1" complete. SlouÄení/Vyvedení zábÄ›ru MIDI: "%1" je hotovo. clip cross-fade Prolínání zábÄ›ru Insert Range Vložit rozsah insert track range Vložit rozsah stopy Remove Range Odstranit rozsah insert range Vložit rozsah remove range Odstranit rozsah remove track range Odstranit rozsah stopy MIDI file import "%1" on %2 %3. Zavedení MIDI souboru "%1" do %2 %3. MIDI file import: "%1". Zavedení MIDI souboru: "%1". MIDI file import: "%1", track-channel: %2. Zavedení MIDI souboru: "%1", stopa-kanál: %2. qtractor-1.5.9/src/translations/PaxHeaders/qtractor_es.ts0000644000000000000000000000013215101070305020641 xustar0030 mtime=1761898693.097267686 30 atime=1761898693.096267683 30 ctime=1761898693.097267686 qtractor-1.5.9/src/translations/qtractor_es.ts0000644000175000001440000227611015101070305020642 0ustar00rncbcusers QObject Audio: %1 channels, %2 Hz Audio: %1 canales, %2 Hz (%1 dB) (%1 dB) (%1 pan) (%1 pan) (%1% time stretch) (%1% time stretch) (%1 semitones pitch shift) (%1 tranponer semitonos) %1 In %1 Entrada %1 Out %1 Salida Audio files (%1) Archivos de audio (%1) All files (*.*) Todos los archivos (*.*) %1 (%2) %3 channels, %4 frames, %5 Hz %6 %1 (%2) %3 canales, %4 cuadros, %5 Hz %6 Duplex Duplex Output Salida Input Entrada None Ninguno (take %1/%2) (toma %1/%2) [Mute] Name: %1 Nombre: %1 Start: %1 Offset: %2 End: %3 Length: %4 Inicio: %1 Offset: %2 Fin: %3 Duración: %4 File: %1 Archivo: %1 take %1 toma %1 reset takes reiniciar tomas clip save clip unlink clip tool %1 herramienta de clip %1 clip record grabar clip automation select seleccionar automatización automation mode modo de automatización automation play reproducir automatización automation record grabar automatización automation logarithmic automatización logarítmica automation color color de automatización automation play all reproducir toda la automatización automation record all grabar toda la automatización automation edit editar automatización automation clear limpiar automatización automation clear all limpiar toda la automatización automation edit list editar lista de automatización %1: Automation/curve file not found. %1: Archivo de automatización/curva no encontrado. %1 Monitor %1 Monitor create bus crear bus update bus actualizar bus delete bus eliminar bus move bus mover bus bus pass-through bus sin filtrar bus gain ganancia de bus bus pan paneo de bus Insert Send/Return pseudo-plugin (Audio) Insertar Envío/Retorno pseudo-plugin (Audio) Insert Send/Return pseudo-plugin (MIDI) Insertar Envío/Retorno pseudo-plugin (MIDI) Send Gain Ganancia de Envío Dry Gain Ganancia Sin Efecto Wet Gain Ganancia de Efecto Aux Send (Audio) Envío Auxiliar (Audio) Aux Send pseudo-plugin (Audio) Envío Auxiliar pseudo-plugin (Audio) Aux Send pseudo-plugin (MIDI) Envío Auxiliar pseudo-plugin (MIDI) (none) (ninguno) %1 (Audio) %1 (Audio) %1 (MIDI) %1 (MIDI) Cakewalk Instrument Definition File Archivo de Definición de Instrumento Cakewalk File Archivo Date Fecha %1 Bank %2 %1 Banco %2 Author: Autor: Copyright: Derechos de copia: Project: Proyecto: Select plug-in's editor (GUI): Seleccionar editor del plug-in (GUI): External Externo X11 X11 X11 (native) X11 (nativo) Gtk2 Gtk2 Gtk2 (native) Gtk2 (nativo) Qt4 Qt4 Qt5 Qt5 Other Otro Don't ask this again No preguntar esto de nuevo plugin parameters parametros del plugin Open File lv2_ui_request_parameter Abrir Archivo %1 - Bank %2 %1 - Banco %2 (format %1) MIDI: (formato %1) MIDI: Channel %1 Canal %1 Track %1 Pista %1 , %1 tracks, %2 tpqn , %1 pistas, %2 tpqn (%1% vol) (%1% vol) MIDI file save: "%1", track-channel: %2. Guardar archivo MIDI: "%1", pista/canal %2. set controller configurar controlador reset controller reiniciar controlador %1 (format %2) %3 tracks, %4 tpqn %5 %1 (formato %2) %3 pistas, %4 tpqn %5 %1 (format %2) %3 %1 (formato %2) %3 (default) (default) %1 Hz %1 Hz slave esclavo %1 (%2) %1 (%2) Usage: %1 [options] [session-file] Modo de uso: %1 [opciones] [archivo-de-sesión] Options: Opciones: Set session identification (uuid) Configurar identificación de sesión (uuid) Show help about command line options Mostrar ayuda sobre las opciones de la línea de comandos Show version information Mostrar infromación de la versión Session file (.qtr) Archivo de sesión (.qtr) [session-file] [archivo-de-sesión] Option -s requires an argument (uuid). Opción -s requiere un argumento (uuid). Signed 16-Bit 16-Bit con signo Signed 24-Bit 24-Bit con signo Signed 32-Bit 32-Bit con signo Float 32-Bit 32-Bit flotante Float 64-Bit 64-Bit flotante SMF Format 0 SMF Formato 0 SMF Format 1 SMF Formato 1 (Any) (Cualquiera) Activate Activar Aux Send: %1 %1(%2): %3 plugin not found. %1(%2): %3 plugin no encontrado. add plugin añadir plugin add insert añadir inserción add aux-send añadir envío auxiliar add MIDI controller aux-send bus bus de envío auxiliar aux-send matrix remove plugin quitar plugin move plugin mover plugin activate plugin activar plugin preset plugin plugin preestablecido reset plugin reiniciar plugin plugin program programar plugin plugin alias dedicated audio outputs salidas de audio dedicadas direct access param parámetro de acceso directo import plugins importar plugins session loop bucle de sesión session punch pinchazo de sesión session properties propiedades de sesión Beat Pulso add tempo node añadir nodo de tempo update tempo node actualizar nodo de tempo remove tempo node quitar nodo de tempo move tempo node mover nodo de tempo add marker añadir marcador update marker actualizar marcador remove marker quitar marcador add key signature añadir armadura update key signature actualizar armadura remove key signature remover armadura move marker mover marcador change time-sig. cambiar compás. step input overdub %1 Volume %1 Volumen %1 Gain %1 Ganancia %1 Pan %1 Panorámica add track añadir pista remove track quitar pista duplicate track duplicar pista move track mover pista resize track redimensionar pista import track importar pista track properties propiedades de pista Track assignment failed: Track: "%1" Input: "%2" Output: "%3" Falló la asignación de pista: Pista: "%1" Entrada: "%2" Salida: "%3" track record grabar pista track mute silenciar pista track solo pista a solas track monitor monitorear pista track gain ganacia de pista track pan panorámica de pista track instrument pista instrumento Automation (%1) Automatización (%1) none ninguno Automation Automatización Unknown Desconocido Product: Producto: Vendor: Fabricante: Manual: Manual: Support: Soporte: Version: Versión: %1 (*.%2) %1 (*.%2) Name: Nombre: Category: Categoría: Categories: Categorías: %1 Record %1 Mute %1 Solo MIDI Controller: %1, %2, %3 Control (MIDI) MIDI Controller Send pseudo-plugin Value Valor qtractorAudioIOMatrixForm Aux-Send I/O Matrix Warning Advertencia Some settings have been changed. Do you want to apply the changes? qtractorAudioListView Name Nombre Ch Canal Frames Cuadros Rate Muestreo Time Tiempo Path Ruta Open Audio Files Abrir Archivos deAudio %1: Audio file not found. %1: Archivo de audio no encontrado. qtractorAudioMixerMeter Gain (dB) Ganancia (dB) dB dB Pan: %1 Gain: %1 dB Ganancia: %1 db qtractorBusForm Bus list Lista de Bus Buses Buses Ch Canal Mode Modo Bus Bus Properties Propiedades &Name: &Nombre: Bus name Nombre del Bus &Mode: &Modo: Bus mode Modo del Bus Input Entrada Output Salida Duplex Dúplex Bus monitor (pass-through) Bus de monitoreo (sin filtrar) M&onitor (pass-through) M&onitoreo (sin filtrar) Audio Audio Cha&nnels: Ca&nales: Audio channels Canales de Audio Audio auto-connect Auto-conectar Audio &Auto connect &Auto conectar MIDI MIDI MIDI Instrument name Nombre de instrumento MIDI MIDI SysEx setup Configuración de MIDI SysEx SysE&x... SysE&x... Input Plugins Plugins de Entrada Input bus plugins Plugins del bus de entrada Add input plugin Añadir plugin de entrada &Add... &Añadir... Remove input plugin Quitar plugin de entrada &Remove &Quitar Move input plugin up Mover plugin de entrada hacia arriba &Up A&rriba Move input plugin down Mover plugin de entrada hacia abajo &Down A&bajo Output Plugins Plugins de Salida Output bus plugins Plugins del bus de salida Add output plugin Añadir plugin de salida Remove output plugin Quitar plugin de salida Move output plugin up Mover plugin de salida hacia arriba Move output plugin down Mover plugin de salida hacia abajo Move bus up towards the top Mover el bus hacia arriba U&p A&rriba Move bus down towards the bottom Mover bus hacia abajo Do&wn A&bajo Create bus Crear bus &Create &Crear Update bus Actualizar bus &Update Act&ualizar Delete bus Eliminar bus &Delete &Eliminar Close this dialog Cerrar este diálogo Close Cerrar Warning Advertencia Some settings have been changed. Do you want to apply the changes? Algunas configuraciones han cambiado. ¿Desea aplicar los cambios? About to remove bus: "%1" (%2) Are you sure? A punto de eliminar bus: "%1" (%2) ¿Esta seguro? Some settings have been changed. Do you want to discard the changes? Algunas configuraciones han cambiado. ¿Desea descartar los cambios? Move &Up Mo&ver Hacía Arriba Move &Down Mover &Hacía Abajo (No instrument) (No hay instrumento) (none) (ninguno) (1 item) (1 elemento) (%1 items) (%1 elementos) qtractorClientListView Readable Clients / Output Ports Clientes de lectura / Puertos de Salida Writable Clients / Input Ports Clientes de Escritura / Puertos de Entrada qtractorClipForm &Name: &Nombre: Clip name Nombre del clip &File: &Archivo: Clip filename Nombre de archivo del clip Browse for clip file Buscar el archivo del clip Clip track/channel Pista del clip/canal Parameters Parámetros Clip start Inicio del clip Clip offset Offset del clip &Panning: &Paneo: Clip length Duración del clip Offs&et: Offs&et: Track/&Channel: Pista/&Canal: &Length: &Duración: &Start: &Inicio: Clip Clip Clip gain/volume Ganancia/volumen del clip Forma&t: Forma&to: Time display format Formato para desplegar tiempo Frames Cuadros Time Tiempo BBT BBT Fade In/Out Fade In/Out Fade &In: Fade &In: Clip fade-in length Duración del fade in del clip Clip fade-in type Tipo de fade in del clip Fade &Out: Fade &Out: Clip fade-out length Duración del fade out del clip Clip fade-out type Tipo de fade out del clip Audio Audio Ti&me Stretch: Ti&me Stretch: Clip time-stretch percentage Porcentaje de time stretch del clip % % Pitch S&hift: &Transponer: Clip pitch-shift in semitones Transponer clip en semitonos semitones semitonos Whether to use WSOLA time-stretching Si se usa estiramiento de tiempo WSOLA &WSOLA time-stretching Estiramiento de tiempo &WSOLA Whether to use RubberBand formant preserve RubberBand &formant preserve Whether to apply WSOLA quick seek time-stretching Si se aplica estiramiento de tiempo de busqueda rápida WSOLA WSOLA quic&k seek Bús&queda rápida WSOLA Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine &Mute &Silenciar &Gain: &Ganancia: Linear Lineal Quadratic 1 Cuadrática 1 Quadratic 2 Cuadrática 2 Quadratic 3 Cuadrática 3 Cubic 1 Cúbico 1 Cubic 2 Cúbico 2 Cubic 3 Cúbico 3 dB dB &Volume: &Volumen: new clip nuevo clip edit clip editar clip Warning Advertencia Some settings have been changed. Do you want to apply the changes? Algunos ajustes han cambiado. ¿Desea aplicar los cambios? MIDI MIDI MIDI files (*.%1 *.smf *.midi) archivos MIDI (*.%1 *.smf *.midi) All files (*.*) Todos los archivos (*.*) %1 Clip File %1 Archivo del Clip qtractorConnect Connect Conectar Disconnect Desconectar Disconnect All Desconectar Todo Refresh Actualizar qtractorConnectForm Connections Conexiones Audio Audio Select output client/ports Seleccionar clientes/puertos de salida Select input client/ports Seleccionar clientes/puertos de entrada Connect currently selected ports Conectar los puertos actualmente seleccionados &Connect &Conectar Disconnect currently selected ports Desconectar los puertos actualmente seleccionados &Disconnect &Desconectar Disconnect all currently connected ports Desconectar todos los puertos actualmente seleccionados Disconnect &All Desconectar &Todo Refresh current connections view Actualizar la vista de conexiones &Refresh &Actualizar MIDI MIDI (All) (Todo) qtractorConnections Connections Conexiones qtractorEditRangeForm Edit Range Rango de edición Range Rango Selection range Rango de selección &Selection &Selección Loop range Rango de bucle &Loop &Bucle Punch range Rango de pinchazo &Punch &Pinchazo Edit range Rango de edición &Edit &Edición Custom range Rango Personalizado &Custom &Personalizado St&art: &Inicio: Clip start Inicio del clip En&d: &Fin: Clip offset Offset del clip Options Opciones Apply to clips in range Aplicar a los clips en este rango Cl&ips Cli&ps Apply to Automation nodes in range Aplicar a los nodos de Automatización en el rango A&utomation A&utomatización Apply to Loop points in range Aplicar a los puntos de bucle en el rango L&oop B&ucle Apply to Punch In/Out points in range Aplicar a los puntos de Pinchazos de entrada/salida en el rango Pu&nch Pi&nchazo Apply to location Markers in range Aplicar a los Marcadores de locación en el rango Mar&kers Mar&cadores Apply to Tempo Map nodes in range Aplicar a los nodos del Mapa de Tempo en el rango Te&mpo Map Mapa de Te&mpo &Format &Formato Time display format Formato para desplegar tiempo Frames Cuadros Time Tiempo BBT BBT qtractorExportClipForm %1 %2 Clips %1 %2 Clips qtractorExportForm Export Exportar &File: &Archivo: Export file name Nombre de archivo a exportar Browse export file name Buscar nombre de archivo a exportar File &type: &Tipo de archivo: Audio file type to use on export Tipo de archivo de audio para usar al exportar Sample &format: &Formato de muestreo: Audio sample format to use on export Formato de muestreo de audio para usar al exportar &Quality: &Calidad: Audio compression quality to use on export Calidad de compresión de audio para usar al exportar File &format: &Formato de archivo: MIDI file format to use on export Formato de archivo MIDI para usar al exportar Range Rango Session range Rango de sesión &Session &Session Loop range Rango de bucle &Loop &Bucle Punch range Rango de pinchazo &Punch &Pinchazo Edit range Rango de edición &Edit &Edición Custom range Rango Personalizado &Custom &Personalizado St&art: &Inicio: Custom start Inicio Personalizado Custom end Fin personalizado En&d: &Fin: Outputs Salidas Output bus names Nombres de bus de salida Format Formato Time display format Formato para desplegar tiempo Frames Cuadros Time Tiempo BBT BBT Whether to add/import new track(s) with export result Si se deben añadir/importar nueva(s) pista(s) con resultado de la exportación &Add new track(s) &Añadir nueva(s) pista(s) Audio Audio MIDI MIDI Export %1 File Exportar %1 Archivo MIDI files (*.%1 *.smf *.midi) Archivos MIDI (*.%1 *.smf *.midi) All files (*.*) Todos los archivos (*.*) qtractorExportTrackForm %1 %2 Tracks %1 %2 Pistas Warning Advertencia The file already exists: "%1" Do you want to replace it? El archivo ya existe: "%1" ¿Desea reemplazarlo? Audio file export: "%1" started... Exportar archivo de audio: "%1" iniciado... Audio file export: "%1" complete. Exportar archivo de audio: "%1" completado. Audio file export: "%1" failed. Exportar archivo de audio: "%1" falló. MIDI file export: "%1" started... Exportar archivo MIDI: "%1" iniciado... MIDI file export: "%1" complete. Exportar archivo MIDI: "%1" completado. MIDI file export: "%1" failed. Exportar archivo MIDI: "%1" falló. qtractorFileListView New Group Grupo nuevo Warning Advertencia About to remove %1 file item(s). Are you sure? A punto de eliminar %1 elemento(s) de archivo: ¿Esta seguro? About to remove %1 item: "%2" Are you sure? A punto de eliminar %1 elemento: "%2" ¿Esta seguro? group grupo file archivo qtractorFileSystem &Home &Inicio &Up &Arriba Al&l Files &Todos los archivos &Session &Sesión &Audio &Audio &MIDI &MIDI H&idden &Oculto &Play &Reproducir File System Sistema de archivos qtractorFiles Audio Audio MIDI MIDI Play file Reproducir archivo New &Group... &Grupo Nuevo... Add &Files... &Añadir Archivos... Cu&t Cor&tar &Copy &Copiar &Paste &Pegar Re&name Re&nombrar &Remove &Quitar Pla&y R&eproducir Cl&eanup L&impiar Ctrl+X Ctrl+X Ctrl+C Ctrl+C Ctrl+V Ctrl+V Del Supr Files Archivos MIDI Files Archivos MIDI Audio Files Archivos de Audio qtractorInstrumentForm Instruments Instrumentos Files Archivos Path Ruta Names Nombres Import from instrument file Importar desde archivo de instrumento &Import... &Importar... Remove instrument file Remover archivo de instrumento &Remove &Remove Move instrument file up on list order Mover archivo de instrumento hacía arriba en el orden de la lista &Up &Arriba Move instrument file down on list order Mover archivo de instrumento hacía abajo en el orden de la lista &Down A&bajo Export to instrument file Exportar a archivo de instrumento E&xport... E&xportar... Close this dialog Cerrar este diálogo Close Cerrar Import Instrument Files Importar Archivos de Instrumento Instrument files (*.%1 *.sf2 *.sf3 *.midnam) Archivos de Instrumento (*.%1 *.sf2 *.sf3 *.midnam) All files (*.*) Todos los archivos (*.*) Export Instrument File Exportar Archivo de Instrumento Instrument files (*.%1) Archivos de instrumento (*.%1) Warning Advertencia The instrument file already exists: "%1" Do you want to replace it? El archivo de instrumento ya existe: "%1" ¿Desea reemplazarlo? Instrument settings have been changed. Do you want to apply the changes? Los ajustes del instrumento han cambiado. ¿Desea aplicar los cambios? Patch Names for Banks Nombres de Programa para Bancos Controller Names = %1 Nombres de Controladores = %1 RPN Names = %1 Nombres RPN = %1 NRPN Names = %1 Nombres NRPN = %1 Bank Select Method = %1 Método de Selección de Banco = %1 Patch Names Nombres de Programas Note Names Nombres de Notas Controller Names Nombres de Controladores RPN Names Nombres RPN NRPN Names Nombres NRPN Bank Select Methods Métodos de Selección de Banco %1 = %2 %1 = %2 Based On = %1 Basado En = %1 Normal Normal Bank MSB Banco MSB Bank LSB Banco LSB Patch Programa Unknown Desconocido qtractorInstrumentMenu (None) (Ninguno) qtractorMainForm &File &Archivo Open &Recent Abrir &Recientes &Edit &Editar Select &Mode &Modo de Selección &Select &Seleccionar I&nsert I&nsertar Remo&ve Remo&ver &Track Pis&ta &State E&stado &Navigate &Navegar Mo&ve Mo&ver &Height Alt&ura Impor&t Tracks Impor&tar Pistas E&xport Tracks E&xportar Pistas M&ode M&odo A&utomation A&utomatización Instrum&ent Instrum&ento T&ools &Herramientas Ta&ke T&oma &View &Ver &Toolbars Bar&ras de heramientas &Windows Ventana&s &Zoom &Zoom S&nap A&juste T&ransport T&ransporte Mo&de St&ep &Help A&yuda &New &Nuevo New Nuevo New session Nueva Sesión New session file Nuevo Archivo de Sesión Ctrl+N Ctrl+N &Open... &Abrir... Open Abrir Open session Abrir Sesión Open session from file Abrir sesión desde archivo Ctrl+O Ctrl+A &Save &Guardar Save Guardar Save session Guardar sesión Save session to file Guardar sesión a archivo Ctrl+S Ctrl+G Save &As... Guardar &Como... Save As Guardar Como Save as Guardar Como Save current session with another file name Guardar sesión actual con otro nombre de archivo &Properties... &Propiedades... Session Properties Propiedades de Sesión Session properties Propiedades de sesión Edit current session properties Editar las propiedades de la sesión actual F2 F2 E&xit &Salir Exit Salir Exit this application program Salir de esta aplicación &Undo &Deshacer Undo Deshacer Undo last action Deshacer última acción Ctrl+Z Ctrl+Z &Redo &Rehacer Redo Rehacer Redo last action Rehacer última acción Ctrl+Shift+Z Ctrl+Shift+Z Cu&t Cor&tar Cut Cortar Cut selection to clipboard Cortar selección a portapapeles Ctrl+X Ctrl+X &Copy &Copiar Copy Copiar Copy selection to clipboard Copiar selección a portapapeles Ctrl+C Ctrl+C &Paste &Pegar Paste Pegar Paste clipboard contents Pegar contenido del portapapeles Ctrl+V Ctrl+V Past&e Repeat... P&egar Repetido... Paste Repeat Pegar Repetido Paste repeat Pegar Repetido Paste/repeat clipboard contents Pegar/repetido el contenido del portapapeles Ctrl+Shift+V Ctrl+Shift+V &Delete &Borrar Delete Borrar Delete selection Borrar selección Del Supr &Clip &Clip Clip Clip Select clip Seleccionar clip Clip selection mode Modo de selección de clip &Range &Rango Range Rango Select range Seleccionar rango Range selection mode Modo de seleccion de rango R&ectangle R&ectángulo Rect Rect Select rectangle Seleccionar rectángulo Rectangular selection mode Modo de selección rectángular &Automation &Automatización Automation Automatización Automation edit mode Editar modo de automatización &All &Todo Select All Seleccionar Todo Select all Seleccionar todo Mark all as selected Marcar todo como seleccionado Ctrl+A Ctrl+A &None &Nada Select None Seleccionar Nada Select none Seleccionar nada Mark all as unselected Marcar todo como no seleccionado Ctrl+Shift+A Ctrl+Shift+A &Invert &Inverso Select Invert Seleccionar Inverso Select invert Seleccionar inverso Invert selection Invertir selección Ctrl+I Ctrl+I Select Track Seleccionar Pista Select track Seleccionar pista Mark track as selected Marcar pista como seleccionada Ctrl+T Ctrl+T Trac&k Range Ran&go de Pista Select Track Range Selecionar Rango de Pista Select track range Selecionar rango de pista Mark track range as selected Marcar rango de pista como seleccionado Ctrl+Shift+R Ctrl+Shift+R Select Range Seleccionar Rango Mark range as selected Marcar rango como seleccionado Ctrl+R Ctrl+R &Range... &Rango... Remove Range Quitar Rango Remove range Quitar Rango Remove range as selected Remover rango como seleccionado Ctrl+Del Ctrl+Del Remove Track Range Remover Rango de Pista Remove track range Remover rango de pista Remove track range as selected Remover rango de pista como seleccionado Ctrl+Shift+Del Ctrl+Shift+Del Insert Range Insertar Rango Insert range Insertar rango Insert range as selected insertar rango como seleccionado Ctrl+Ins Ctrl+Ins Insert Track Range Insert Rango de Pista Insert track range Insert rango de pista Insert track range as selected Insertar rango de pista como seleccionado Ctrl+Shift+Ins Ctrl+Shift+Ins Sp&lit Se&parar Split Selection Separar Seleccionado Split selection Separar seleccionado Split current selection Separar selección actual Ctrl+Y Ctrl+Y &Add Track... &Añadir Pista... Add Track Añadir Pista Add track Añadir Pista Add a new track to session Añadir una nueva pista a la sesión Shift+Ins Shift+Ins &Remove Track &Quitar Pista Remove Track Quitar Pista Remove track Quitar Pista Remove current track from session Quitar pista actual de la sesión Shift+Del Shift+Del &Duplicate Track &Duplicar Pista Duplicate Track Duplicar Pista Duplicate track Duplicar Pista Duplicate current track Duplicar pista actual Track &Properties... &Propiedades de Pista... Track Properties Propiedades de Pista Track properties Propiedades de pista Edit current track properties Editar las propiedades de la pista actual Shift+F2 Shift+F2 &Inputs &Entradas Track Inputs Entradas de Pista Track inputs Entradas de pista Show current track input bus connections Muestra las conexiones del bus de entrada de la pista actual &Outputs &Salidas Track Outputs Salidas de Pista Track outputs Salidas de pista Show current track output bus connections Muestra las conexiones del bus de salida de la pista actual &Record &Grabar Record Track Grabar Pista Record track Grabar pista Arm current track for recording Armar pista actual para grabación &Mute &Silenciar Mute Track Silenciar Pista Mute track Silenciar pista Mute current track Silenciar pista actual &Solo &Solo Solo Track Pista a solas Solo track Pista a solas Solo current track Poner a solas la pista actual M&onitor M&onitorizar Monitor Track Monitorizar Pista Monitor track Monitorizar pista Monitor current track Monitorizar pista actual &First &Primera First Track Primera Pista First track Primera pista Make current the first track Hacer pista actual la primera &Previous P&revia Previous Track Pista Previa Previous track Pista previa Make current the previous track Hacer pista actual la pista previa &Next &Siguiente Next Track Siguiente Pista Next track Siguiente pista Make current the next track Hacer pista actual la siguiente &Last Ú&ltima Last Track Última Pista Last track ültima pista Make current the last track Hacer pista actual la última N&one Ningun&a None Track Ninguna Pista None track Ninguna pista None current track Ninguna pista actual &Top &Inicio Move Top Mover al Inicio Move top Mover al inicio Move current track to top Mover pista actual al inicio &Up &Arriba Move Up Mover Hacía Arriba Move up Mover hacía arriba Move current track up Mover pista actual hacía arriba &Down &Abajo Move Down Mover Hacía Abajo Move down Mover hacía abajo Move current track down Mover pista actual hacía abajo &Bottom &Final Move Bottom Mover al Final Move bottom Mover al final Move current track to bottom Mover pista actual al final &Increase &Incrementar Increase Height Incrementar Altura Increase height Incrementar altura Increase track height Incrementar altura de la pista Ctrl+Shift++ Ctrl+Shift++ &Decrease &Disminuir Decrease Height Disminuir Altura Decrease height Disminuir altura Decrease track height Disminuir altura de la pista Ctrl+Shift+- Ctrl+Shift+- &Minimize Minimize Height Minimize height Minimize track height &Reset &Reiniciar Height Reset Reiniciar Altura Height reset Reiniciar altura Reset track height Reiniciar altura de la pista Ctrl+Shift+1 Ctrl+Shift+1 Auto &Monitor Auto &Monitorización Auto Monitor Auto Monitorización Auto monitor Auto monitorización Auto-monitor current track Auto monitorización de la pista actual F6 F6 Auto Dea&ctivate Auto Desa&ctivar Auto Deactivate Auto Desactivar Auto-deactivate plugins Auto-desactivar plugins Auto-deactivate plugins not producing sound Auto-desactivar plugins que no estén produciendo sonido Shift+F6 Mayús+F6 &Audio... &Audio... Inport Audio File Importar Archivos de Audio Import Audio file Importar archivos de audio Import tracks from Audio file Importar pistas de archivo de Audio &MIDI... &MIDI... Import MIDI File Importar archivo MIDI Import MIDI file Importar archivo MIDI Import tracks from MIDI file Importar pistas desde archivo MIDI Export Audio File Export Archivo de Audio Export Audio file Export archivo de Audio Export tracks to Audio file Exportar pistas a archivo de Audio Export MIDI File Exportar Archivo MIDI Export MIDI file Exportar archivo MIDI Export tracks to MIDI file Exportar pistas a archivo MIDI Log&arithmic Log&arítmico Automation logarithmic Automatización logarítmica Automation curve logarithmic scale Curva de automatización en escala logarítmica C&olor... C&olor... Automation color Color de automatización Automation curve color Color de curva de automatización &Lock &Bloquear Automation lock Bloquear Automatización Lock automation curve Bloquear curva de automatización &Play &Reproducir Automation playback Reproducir automatización Playback automation curve Reproducir curva de automatización Automation record Grabar automatización Record automation curve Grabar curva de automatización &Clear &Limpiar Automation clear Limpiar automatización Clear automation curve Limpiar curva de automatización Loc&k All Blo&quear Todo Automation lock all Bloquear toda la automatización Lock all automation curves Bloquear todas las curvas de automatización Play &All R&eproducir Todo Automation playback all Reproducir toda la automatización Playback all automation curves Reproducir todas las curvas de automatización Rec&ord All Grabar T&odo Automation record all Grabar toda la automatización Record all automation curves Grabar todas las curvas de automatización C&lear All &Limpiar Todo Automation clear all Limpiar toda la automatización Clear all automation curves Limpiar todas las curvas de automatización &New... &Nuevo... New Clip Clip Nuevo New clip Nuevo clip Create new clip Crear clip nuevo &Edit... &Editar... Edit Clip Editar clip Edit clip Editar clip Edit current clip Editar clip actual F4 F4 Mute Clip Mute clip Mute current clip &Unlink Desvinc&ular Unlink Clip Desvincular Clip Unlink clip Desvincular clip Unlink current clip Desvincular clip actual Recor&d Gra&bar Record Clip Grabar Clip Record clip Grabar clip Record current clip (overdub) Grabar clip actual (doblaje) &Split &Separar Split Clip Separar Clip Split clip Separar clip Split current clip at playhead Separar clip actual en cabezal de reproducción &Merge... &Fundir... Merge Clips Fundir Clips Merge clips Fundir clips Merge selected clips Fundir clips seleccionados Normali&ze Normali&zar Normalize Clip Normalizar Clip Normalize clip Normalizar clip Normalize current clip (gain/volume) Normalizar clip actual (ganancia/volumen) &Quantize... &Cuantización... Quantize Clip Cuantizar Clip Quantize clip events Cuantizar eventos de clip Quantize current MIDI clip events Cuantizar eventos MIDI del clip actual &Transpose... &Transponer... Transpose Clip Transponer Clip Transpose clip events Transponer eventos de clip Transpose current MIDI clip events Transponer eventos MIDI del clip actual &Normalize... &Normalizar... Normalize clip events Normalizar eventos de clip Normalize current MIDI clip events Normalizar eventos MIDI del clip actual &Randomize... Aleato&rio... Randomize Clip Aleatorizar Clip Randomize clip events Aleatorizar eventos de clip Randomize current MIDI clip events Aleatorizar eventos MIDI del clip actual Resi&ze... Redimen&sionar... Resize Clip Redimensionar Clip Resize clip events Redimensionar eventos de clip Resize current MIDI clip events Redimensionar eventos MIDI del clip actual Re&scale... Ree&scalar... Rescale Clip Reescalar Clip Rescale clip events Reescalar eventos de clip Rescale current MIDI clip events Reescalar eventos MIDI del clip actual T&imeshift... Desplazamiento de &tiempo... Timeshift Clip Desplazamiento de tiempo de Clip Timeshift clip events Desplazamiento de tiempo eventos de clip Timeshift current MIDI clip events Desplazamiento de tiempo eventos MIDI del clip actual T&empo ramp... Tempo ramp Clip Tempo ramp clip events Tempo ramp current MIDI clip events &Tempo Adjust... Ajustar &Tempo... Tempo Adjust Ajustar Tempo Adjust session tempo from current clip selection Ajustar tempo de la sesión desde el clip seleccionado actualmente F7 F7 &Cross Fade &Cross Fade Clip Cross-fade Crossfade de Clip Clip cross-fade Crossfade de Clip Cross-fade current overlapped clips Crossfade en los clips solapados actualmente &Range Set &Fijar Rango Clip Range Rango de Clip Clip range Rango de clip Set edit-range from current clip extents Fijar rango de edición desde la extensión del clip actual &Loop Set Fijar Buc&le Clip Loop Bucle de Loop Clip loop Bucle de loop Set loop-range from current clip extents Fijar rango de bucle desde la extensión del clip actual &Import... &Importar... Import Clip Importar Clip Import clip Importar clip Import clip from file(s) Importar clip desde archivo(s) E&xport... E&xportar... Export Clip Exportar Clip Export clip Exportar clip Export current clip to file Exportar clip actual a archivo First Take Primera Toma First take Primera toma Select current clip first take Seleccionar primera toma del clip actual Previous Take Toma Previa Previous take Toma previa Select current clip previous take Seleccionar toma previa del clip actual Next Take Siguiente Toma Next take Siguiente toma Select current clip next take Seleccionar siguiente toma del clip actual Shift+T Shift+T Last Take Última Toma Last take Última toma Select current clip last take Seleccionar última toma del clip actual Reset Takes Reiniciar Tomas Reset takes Reiniciar tomas Reset (unfold) current clip takes Reiniciar (desplegar) tomas del clip actual R&ange... R&ango... Take Range Rango de Toma Take range Rango de toma Range (fold) current clip into takes Rango (plegar) clip actual en tomas &Menubar Barra de &menú Menubar Barra de menú Show/hide the main program window menubar Mostrar/ocultar la barra de menú de la ventana principal del programa Ctrl+M Ctrl+M &Statusbar B&arra de estado Statusbar Barra de estado Show/hide the main program window statusbar Mostrar/ocultar la barra de estado de la ventana principal del programa File Toolbar Barra de Heramientas de Archivo File toolbar Barra de heramientas de archivo Show/hide main program window file toolbar Mostrar/ocultar la barra de herramientas de la ventana principal del programa Edit Toolbar Barra de Heramientas de Edición Edit toolbar Barra de Heramientas de Edición Show/hide main program window edit toolbar Mostrar/ocultar la barra de herramientas de edición de la ventana principal del programa Track Toolbar Barra de Herramientas de Pista Track toolbar Barra de herramientas de pista Show/hide main program window track toolbar Mostrar/ocultar la barra de herramientas de pista de la ventana principal del programa View Toolbar Barra de Herramientas de Vista View toolbar Barra de herramientas de vista Show/hide main program window view toolbar Mostrar/ocultar la barra de herramientas de vista de la ventana principal del programa &Options &Opciones Options Toolbar Barra de Herramientas de Opciones Options toolbar Barra de herramientas de opciones Show/hide main program window options toolbar Mostrar/ocultar la barra de herramientas de opciones de la ventana principal del programa Transport Toolbar Barra de Herramientas de Transporte Transport toolbar Barra de herramientas de transporte Show/hide main program window transport toolbar Mostrar/ocultar la barra de herramientas de transporte de la ventana principal del programa T&ime T&iempo Time Toolbar Barra de Heramientas de Tiempo Time toolbar Barra de heramientas de tiempo Show/hide main program window time toolbar Mostrar/ocultar la barra de herramientas de tiempo de la ventana principal del programa Thum&b &Miniatura Thumb Toolbar Barra de Herramientas de Miniatura Thumb toolbar Barra de herramientas de miniatura Show/hide main program window thumb toolbar Mostrar/ocultar la barra de herramientas de miniatura de la ventana principal del programa File &System &Sistema de Archivos File System Sistema de Archivos File system Sistema de archivos Show/hide the file system window Mostrar/ocultar la ventana de sistema de archivos &Files &Archivos Files Archivos Show/hide the files window Mostrar/ocultar la ventana de archivos M&essages M&ensajes Messages Mensajes Show/hide the messages window Mostrar/ocultar la ventana de mensajes &Connections &Conexiones Connections Conexiones Show/hide the connections window Mostrar/ocultar la ventana de conexiones F8 F8 Mi&xer Me&zcladora Mixer Mezcladora Show/hide the mixer window Mostrar/ocultar la ventana de mezcaldora F9 F9 &In &Acercar Zoom In Acercar Zoom Zoom in Acercar zoom Ctrl++ Ctrl++ &Out A&lejar Zoom Out Alejar Zoom Zoom out Alejar zoom Ctrl+- Ctrl+- Zoom Reset Reiniciar Zoom Zoom reset Reiniciar zoom Ctrl+1 Ctrl+1 &Horizontal &Horizontal Horizontal Zoom Zoom Horizontal Horizontal zoom Zoom horizontal Horizontal zoom mode Modo de zoom horizontal &Vertical &Vertical Vertical Zoom Zoom Vertical Vertical zoom Zoom vertical Vertical zoom mode Modo de zoom vertical All Zoom Zoom Todo All zoom Zoom todo All zoom mode Modo de zoom todo &Grid &Rejilla Grid Rejilla Snap grid view mode Modo de vista ajustar a rejilla &Zebra &Cebra Zebra Cebra Bar zebra view mode Modo de vista barras cebra Too&l Tips A&yuda emergente Tool tips Ayuda emergente Floating tool tips view mode Modo de vista flotante de la ayuda contextual &Refresh A&ctualizar Refresh Actualizar Refresh views Actualizar vistas F5 F5 &Instruments... &Instrumentos... Instruments Instrumentos Change instrument definitions and files Cambiar definiciones de instrumentos y archivos &Controllers... &Controladores... Controllers Controladores Change MIDI controllers configuration Cambiar configuración de controladores MIDI &Buses... &Buses... Buses Buses Change session bus definitions Cambiar definiciones de bus de la sesión Tempo M&ap / Markers... M&apa de tempo / Marcadores... Tempo Map / Markers Mapa de tempo / Marcadores Tempo map / markers Mapa de tempo / marcadores Change session tempo map / markers Cambiar mapa de tempo / marcadores de la sesión &Options... &Opciones... Options Opciones Change general application program options Cambiar opciones generales de la aplicación F12 F12 &Backward &Hacia Atrás Backward Hacia Atrás Transport backward Transporte hacia atrás Backspace Retroceso Re&wind Re&bobinar Rewind Rebobinar Transport rewind Rebobinar transporte F&ast Forward Adel&antar rápido Fast Forward Adelantar rápido Fast forward Adelantar rápido Transport fast forward Adelantar rápido transporte &Forward Ha&cia Adelante Forward Hacia adelante Transport forward Transporte hacia adelante &Loop &Bucle Loop Bucle Transport loop Bucle de transporte Ctrl+Shift+L Ctrl+Shift+L Step Backward Step backward Transport step backward Step Forward Step forward Transport step forward Loop &Set Fijar Buc&le Loop Set Fijar Bucle Loop set Fijar Bucle Transport loop set Fijar transporte de bucle Ctrl+L Ctrl+L &Stop &Detener Stop Detener Transport stop Detener transporte Play Reproducir Transport play/pause Reproducir/pausar transporte Space Espacio Record Grabar Transport record Transporte a grabación &Punch &Pinchazo Punch Pinchazo Punch in/out Punch in/out Transport punch in/out Transporte punch in/out Ctrl+Shift+P Ctrl+Shift+P Punch Se&t Fi&jar Punch Punch Set Fijar Punch Punch in/out set Fijar punch in/out Transport punch in/out set Fijar punch in/out de transporte Ctrl+P Ctrl+P &Count-in &Conteo de entrada Count-in Conteo de entrada &Metronome &Metrónomo Metronome Metrónomo F&ollow Playhead &Seguir Cabezal de Reproducción Follow Playhead Seguir Cabezal de Reproducción Follow playhead Seguir Cabezal de Reproducción A&uto Backward Re&troceso automático Auto Backward Retroceso Automático Auto backward Retroceso automático &Continue Past End &Continuar Pasado el Final Continue Past End Continuar Pasado el Final Continue past end Continuar pasado el final Transport mode: None Modo de Transporte: Ninguno Transport mode set to None Modo de transporte establecido en Ninguno &Slave E&sclavo Slave Esclavo Transport mode: Slave Modo de transporte: Esclavo Transport mode set to Slave Modo de transporte establecido en Esclavo &Master &Maestro Master Maestro Transport mode: Master Modo de transporte: Maestro Transport mode set to Master Modo de transporte establecido en Maestro &Full &Completo Full Completo Transport mode: Full Modo de transporte: Completo Transport mode set to Full Modo de transporte establecido en Completo Pa&nic Pá&nico Panic Pánico All MIDI tracks shut off (panic) Apagar todas las pistas MIDI (pánico) &Shortcuts... Atajo&s... Shortcuts Atajos Keyboard shortcuts Atajos de teclado &About... &Acerca de... About Acerca de Show information about this application program Muestra información acerca de este porgrama de aplicación About &Qt... Acerca de &Qt... About Qt Acerca de Qt Show information about the Qt toolkit Mostrar información acerca del kit de herramientas Qt Set current snap to %1 Fijar el snap (ajuste) actual a %1 Current time (play-head) Tiempo actual (cabezal de reproducción) Current tempo (BPM) Tempo actual (PPM) Snap/beat Snap/pulso Track Pista Current track name Nombre actual de pista MOD MOD Session modification state Estado de modificación de la sesión REC REC Session record state Estado de grabación de la sesión MUTE MUTE Session muting state Estado de silenciamiento de la sesión SOLO SOLO Session soloing state Estado del solo de la sesión LOOP LOOP Session looping state Estado del bucle de la sesión Session total time Timepo total de la sesión Session sample rate Frecuenciade muestreo de la sesión Could not set default session directory: %1 Sorry. No se pudo fijar el directorio por defecto de la sesión: %1 Lo siento. Ready Listo Session XRUN state Estado de XRUN de sesión Session buffer size Tamaño del búfer de sesión Untitled%1 Sin título%1 New session: "%1". Nueva Sesión:" %1". Session files (*.%1 *.%2 *.%3) Archivos de sesión (*.%1 *.%2 *.%3) Session files (*.%1 *.%2) Archivos de sesión (*.%1 *.%2) Template files (*.%1) Archivos de plantilla (*.%1) Archive files (*.%1) Archivo de registro (*.%1) All files (*.*) Todos los archivos (*.*) Open Session Abrir Sesión Save Session Guardar Sesión Warning Advertencia The file already exists: "%1" Do you want to replace it? El archivo ya existe: "%1" ¿Desea reemplazarlo? Backup session: "%1" as "%2". Respaldar sesión: "%1" como "%2". Could not backup existing session: %1 as %2 Sorry. No se pudo respaldar la sesión existente: %1 como %2 Lo siento. The current session has been changed: "%1" Do you want to save the changes? La sesión actual ha sido cambiada: "%1" ¿Desea guardar los cambios? About to remove archive directory: "%1" Are you sure? Esta a punto de quitar directorio de archivo: "%1" ¿Está seguro? Don't ask this again No preguntar de nuevo Session closed. Sesión cerrada. The directory already exists: "%1" Do you want to replace it? El directorio ya existe: "%1" ¿Desea reemplazarlo? Opening "%1"... Abriendo "%1"... Session could not be loaded from "%1". Sorry. No se pudo cargar la sesión desde "%1". Lo siento. Open session: "%1". Sesión abierta: "%1". A directory with same name already exists: "%1" This directory will be replaced, erasing all its current data, when opening and extracting this archive in the future. Do you want to continue? Ya existe un directorio con el mismo nombre: "%1" Este directorio será reemplazado, borrando todos sus datos actuales, al abrir y extraer este archivo en el futuro. ¿Quiere continuar? The directory is an extracted archive: "%1" This directory will be removed, erased from all its current data, when closing this session. Do you want to continue? Saving "%1"... Guardando "%1"... Session could not be saved to "%1". Sorry. No se pudo guardar la sesión a "%1". Lo siento. Save session: "%1". Guardar sesión: "%1". Oops! Looks like it crashed or did not close properly last time it was run... however, an auto-saved session file exists: "%1" Do you want to crash-recover from it? Oops! Parece que crasheo ó que no se cerro bien la última vez que fue ejecutado,... sin embargo, existe un archivo de sesión de auto-guardado: "%1" ¿Desea recuperar sesión de este archivo? About to clear automation: "%1" Are you sure? A punto de limpiar automatización: "%1" ¿Esta seguro? About to clear all automation: "%1" Are you sure? A punto de limpiar toda la automatización: "%1" ¿Esta seguro? take range rango de toma session sesión or o program programa Information información Some settings may be only effective next time you start this %1. Algunos ajustes solo podrán hacerse efectivos la próxima vez que inicie este %1. Player panic! Pánico en la reproducción! Debugging option enabled. Opción de debugging habilitada. Ogg Vorbis (libvorbis) file support disabled. Soporte de archivo Ogg Vorbis (libvorbis) deshabilitado. MPEG-1 Audio Layer 3 (libmad) file support disabled. Soporte de archivo MPEG-1 Audio Layer 3 (libmad) deshabilitado. Sample-rate conversion (libsamplerate) disabled. Conversión de frencuencia de muestreo (libsamplerate) deshabilitado. Pitch-shifting support (librubberband) disabled. Soporte de transposición de tono (librubberband) deshabilitado. Beat-detection support (libaubio) disabled. Soporte de detección de tempo (libaubio) deshabilitado. OSC service support (liblo) disabled. Soporte de servicio OSC (liblo) deshabilitado. LADSPA Plug-in support disabled. Compatibilidad con plug-ins LADSPA deshabilitada. DSSI Plug-in support disabled. Compatibilidad con plug-ins DSSI deshabilitada. VST2 Plug-in support disabled. Compatibilidad con plug-ins VST2 deshabilitada. VST3 Plug-in support disabled. Compatibilidad con plug-ins VST3 deshabilitada. CLAP Plug-in support disabled. LV2 Plug-in support disabled. Compatibilidad con plug-ins LV2 deshabilitada. LV2 Plug-in support (liblilv) disabled. Compatibilidad con plug-ins LV2 (liblilv) deshabilitada. LV2 Plug-in UI support disabled. Soporte de UI de Plug-in LV2 deshabilitado. LV2 Plug-in UI support (libsuil) disabled. Soporte de UI de Plug-in LV2 (libsuil) deshabilitado. LV2 Plug-in External UI support disabled. Soporte de UI Externo de Plug-in LV2 deshabilitado. LV2 Plug-in MIDI/Event support (DEPRECATED) enabled. Soporte MIDI/Evento de plugin LV2 (EN DESUSO) habilitado. LV2 Plug-in MIDI/Atom support disabled. Soporte de MIDI/Atom de Plug-in LV2 deshabilitado. LV2 Plug-in Worker/Schedule support disabled. Soporte de Worker/Schedule de Plug-in LV2 deshabilitado. LV2 Plug-in State support disabled. Soporte de Estado de Plug-in LV2 deshabilitado. LV2 plug-in State Make Path support (DANGEROUS) enabled. Soporte para State Make Path de plug in LV2 (PELIGROSO) habilitado. LV2 Plug-in State Files support disabled. Soporte de Archivos de Estado de Plug-in LV2 deshabilitado. LV2 Plug-in Programs support disabled. Soporte de Programas de Plug-in LV2 deshabilitado. LV2 Plug-in MIDNAM support disabled. Soporte de Plug-in LV2 MIDNAM deshabilitado. LV2 Plug-in Presets support disabled. Soporte de Preajustes de Plug-in LV2 deshabilitado. LV2 Plug-in Patch support disabled. Soporte para Patch de Plug in LV2 deshabilitado. LV2 Plug-in Time/position support disabled. Soporte de Time/position de Plug-in LV2 deshabilitado. LV2 Plug-in Options support disabled. Soporte de Opciones de Plug-in LV2 deshabilitado. LV2 Plug-in Buf-size support disabled. Soporte de Buf-size de Plug-in LV2 deshabilitado. LV2 Plug-in UI Touch interface support disabled. Soporte de Plug in LV2 para la interfaz táctil de la interfaz de usuario deshabilitada. LV2 Plug-in UI Request-value support disabled. Soporte de UI Request-value de plugin-LV2 deshabilitado. LV2 Plug-in UI Idle interface support disabled. Soporte para Plug in interface UI idle deshabilitado. LV2 Plug-in UI Show interface support disabled. Soporte para Plug in mostrar interface UI deshabilitado. LV2 Plug-in UI GTK2 native support disabled. Soporte de UI de plugin-LV2 en UI GTK2 deshabilitado. LV2 Plug-in UI GTKMM2 native support disabled. Soporte nativo de plugin-LV2 enUI GTKMM2 deshabilitado. LV2 Plug-in UI X11 native support disabled. Soporte de UI de plugin-LV2 en UI X11 deshabilitado. JACK Session support disabled. Soporte de Sesión JACK deshabilitado. JACK Latency support disabled. Soporte de Latencia de JACK deshabilitado. JACK Metadata support disabled. Soporte de Metadatos de JACK deshabilitado. NSM support disabled. Soporte NSM deshabilitado. Version Versión Using: Qt %1 Usando: Qt %1 Website Sitio web This program is free software; you can redistribute it and/or modify it Este programa es software libre: usted puede redistribuirlo y/o modificarlo under the terms of the GNU General Public License version 2 or later. conforme a los términos de la Licencia Pública General de GNU versión 2 ó mayor. record clip grabar clip [modified] [modificado] XRUN XRUN Session started. Sesión iniciada. The audio/MIDI engine could not be started. Make sure the JACK/Pipewire audio service and the ALSA Sequencer kernel module (snd-seq-midi) are up and running and then restart the session. The original session sample rate (%1 Hz) is not the same as the current audio engine (%2 Hz). Saving and reloading from a new session file is highly recommended. La frecuencia de muestreo de la sesión original (%1 Hz) no es la mismo del motor de audio actual (%2 Hz). Guardar y recargar desde un nuevo archivo de sesión es muy recomendable. The following issues were detected: %1 Saving into another session file is highly recommended. Se detectaron los siguientes problemas: %1 Guardar en otro archivo de sesión es muy recomendable. &Hold &Mantener &Linear &Lineal &Spline &Spline Take %1 Toma %1 Don't show this again TRACK MONITOR %1 %2 MONITOREAR PISTA %1 %2 None Ninguno Error Error XRUN(%1 skipped) XRUN(%1 omitido) XRUN(%1): some frames might have been lost. XRUN(%1): algunos cuadros se pueden haber perdido. Audio connections change. Cambio en las conexiones de Audio. Audio self-connection detected! In general, connecting an output bus (or insert send), directly into any input bus (or insert return), is not advisable. It often doesn't work, if at all. MIDI connections change. Cambio en las conexiones MIDI. Playing ended. Reproducción terminada. The audio engine has been shutdown. Make sure the JACK audio server (jackd) is up and running and then restart session. El motor de audio ha sido apagado. Asegúrese de que el servidor de audio JACK (jackd) este en funcionamiento y reinicie la sesión. The audio engine buffer size has changed, increased from %1 to %2 frames/period. Reloading the current session file is highly recommended. El tamaño del búfer del motor de audio ha cambiado, aumentó del %1 al %2 cuadros/período. Se recomienda que se vuelva a cargar el archivo de sesión actual. STOP STOP PLAY PLAY FFWD FFWD REW REW REC ON REC ON REC OFF REC OFF RESET RESET LOCATE %1 LOCATE %1 SHUTTLE %1 SHUTTLE %1 STEP %1 STEP %1 TRACK RECORD %1 %2 TRACK RECORD %1 %2 TRACK MUTE %1 %2 TRACK MUTE %1 %2 TRACK SOLO %1 %2 TRACK SOLO %1 %2 Unknown sub-command Sub-comando desconocido Not implemented No implementado MIDI CTL: %1, Channel %2, Param %3, Value %4 MIDI CTL: %1, Canal %2, Param %3, Valor %4 (track %1, gain %2) (pista %1, ganancia %2) (track %1, panning %2) (pista %1, paneo %2) START INICIAR CONTINUE CONTINUAR SONGPOS %1 SONGPOS %1 %1 BPM %1 PPM Playing "%1"... Reproduciendo "%1"... qtractorMessages Messages Mensajes Logging stopped --- %1 --- Se ha detenido la bitácora --- %1 --- Logging started --- %1 --- Se ha iniciado la bitácora --- %1 --- qtractorMidiControl Note On Encendido de nota Note Off Apagado de nota Key Press Pulsación de tecla Controller Controlador Pgm Change Cambio de programa Chan Press Chan Press Pitch Bend Portamento RPN RPN NRPN NRPN Control 14 Control 14 Track Gain Ganancia de pista Track Panning Paneo de pista Track Monitor Monitor de pista Track Record Grabación de pista Track Mute Silenciar pista Track Solo Solo de pista qtractorMidiControlForm Controllers Controladores Controller files Archivos de Controladores Files Archivos Path Ruta Import controller files Importar archivos de controladores &Import... &Importar... Remove controller file Remover archivo de controlador &Remove &Quitar Move controller file up on list order Mover archivo de controlador hacía arriba en el orden de la lista &Up A&rriba Move controller file down on list order Mover archivo de controlador hacía abajo en el orden de la lista &Down A&bajo &Type &Tipo &Channel &Canal &Parameter &Parámetro Trac&k Pi&sta offse&t offse&t &limit &límite C&ommand C&omando Flags Banderas MIDI Event type Tipo de Evento MIDI MIDI Channel Canal MIDI MIDI Controller (parameter) Controlador MIDI (parámetro) MIDI parameter (track offset) Parámetro MIDI (offset de pista) + + Track offset Offset de la Pista Track limit Límite de pista Command action Acción del comando Command delta/momentary Comando delta/momentáneo D&elta D&elta Command feedback Retroalimentación del comando &Feedback &Feedback Map/update controller command Mapear/actualizar comando del controlador &Map &Mapear Controller map Mapa del controlador Type Tipo Channel Canal Parameter Parámetro Track Pista Command Comando Feedback Feedback Unmap/remove controller command Des-mapear/quitar comando del controlador U&nmap De&s-mapear Enable all controllers immediate sync (hook) Habilitar sincronia inmediata a todos los controladores (hook) &Sync &Sync Reload/apply all controller files Recargar/aplicar todos los archivos del controlador Relo&ad Rec&argar Export to controller file Exportar a archivo de controlador E&xport... E&xportar... Close this dialog Cerrar este diálogo Close Cerrar Import Controller Files Importar Archivos de Controlador Controller files (*.%1) Archivos de controlador (*.%1) All files (*.*) Todos los archivos (*.*) Warning Advertencia About to remove controller file: "%1" Are you sure? Esta a punto de quitar archivo de controlador: "%1" ¿Está seguro? Export Controller File Exportar Archivo de Controlador controller controlador The controller file already exists: "%1" Do you want to replace it? El archivo de controlador ya existe: "%1" ¿Desea reemplazarlo? Saved controller mappings may not be effective the next time you start this program. "%1" Do you want to apply to controller files? Los mapeos de controlador podrían no tener efecto la siguiente ves que inicie este programa. "%1" ¿Desea aplicar a los archivos de controlador? Controller mappings have been changed. Los mapeos del controlador han cambiado. Do you want to save the changes? ¿Desea guardar los cambios? Delta Delta qtractorMidiControlObserverForm &Type: &Tipo: MIDI event type Tipo de evento MIDI Cha&nnel: Ca&nal: MIDI channel Canal MIDI &Parameter: &Parámetro: MIDI parameter Parámetro MIDI &Logarithmic &Logarítmico &Feedback &Feedback In&vert In&vertir &Hook &Hook L&atch L&atch Control input connections Controlar las conexiones de entrada &Inputs &Entradas Control output connections Controlar las conexiones de salida &Outputs &Salidas MIDI Controller Controlador MIDI MIDI controller is already assigned. Do you want to replace the mapping? El controlador MIDI controller ya se encuentra asigando. ¿Desea reemplazar el mapeo? Some settings have been changed. Do you want to apply the changes? Algunas configuraciones han cambiado. ¿Desea aplicar los cambios? &MIDI Controller... Controlador &MIDI... &Automation &Automatización &Lock &Bloquear &Play &Reproducir &Record &Grabar &Clear &Limpiar qtractorMidiControlPluginWidget &Type: &Tipo: MIDI event type Tipo de evento MIDI Cha&nnel: Ca&nal: MIDI channel Canal MIDI &Parameter: &Parámetro: MIDI parameter Parámetro MIDI &Logarithmic &Logarítmico In&vert In&vertir &Bipolar qtractorMidiEditEvent Zoom in (horizontal) Acercar zoom (horizontal) Zoom out (horizontal) Alejar zoom (horizontal) Zoom reset (horizontal) Reiniciar zoom (horizontal) qtractorMidiEditList C%1 C%1 qtractorMidiEditTime Play-head Cabezal de reproducción Edit-head Cabezal de edición Edit-tail Editar cola Loop-start Inicio de bucle Loop-end Fin del bucle Punch-in Punch-in Punch-out Punch-out Start: %1 End: %2 Length: %3 Inicio: %1 Fin: %2 Duración: %3 qtractorMidiEditView Zoom in (vertical) Acercar zoom (vertical) Zoom out (vertical) Alejar zoom (vertical) Zoom reset (vertical) Reiniciar zoom (vertical) qtractorMidiEditor C Do C#/Db Do#/Reb D Re D#/Eb Re#/Mib E Mi F Fa F#/Gb Fa#/Solb G Sol G#/Ab Sol#/Lab A La A#/Bb La#/Sib B Si Acoustic Bass Drum Bombo Acústico Bass Drum 1 Bombo 1 Side Stick Acoustic Snare Caja Acústica Hand Clap Aplauso Electric Snare Caja Eléctrica Low Floor Tom Tom de Piso Bajo Closed Hi-Hat Charles Cerrado High Floor Tom Tom de Piso Alto Pedal Hi-Hat Pedal de Charles Low Tom Tom grave Open Hi-Hat Charles Abierto Low-Mid Tom Tom medio-grave Hi-Mid Tom Tom Medio-Alto Crash Cymbal 1 Platillo Crash 1 High Tom Tom Alto Ride Cymbal 1 PLatillo Ride 1 Chinese Cymbal Platillo Chinese Ride Bell Campana de Ride Tambourine Pandero Splash Cymbal Platillo Splash Cowbell Cencerro Crash Cymbal 2 Platillo Crash 2 Vibraslap Quijada Ride Cymbal 2 Platillo Ride 2 Hi Bongo Bongo Alto Low Bongo Bongo Bajo Mute Hi Conga Conga Alta Silenciada Open Hi Conga Conga Alta Abierta Low Conga Conga Baja High Timbale Timbal Alto Low Timbale Timbal Bajo High Agogo Agogo Alto Low Agogo Agogo Bajo Cabasa Cabaza Maracas Maracas Short Whistle Silbido Corto Long Whistle Silbido Largo Short Guiro Güiro Corto Long Guiro Güiro Largo Claves Claves Hi Wood Block Bloque Alto Low Wood Block Bloque Bajo Mute Cuica Cuica Silenciada Open Cuica Cuica Abierta Mute Triangle Triángulo Silenciado Open Triangle Triángulo Abierto Bank Select (coarse) Seleccion de Banco (amplio) Modulation Wheel (coarse) Breath Controller (coarse) Foot Pedal (coarse) Portamento Time (coarse) Data Entry (coarse) Volume (coarse) Volumen (control amplio) Balance (coarse) Balance (control amplio) Pan Position (coarse) Posición del Paneo (control amplio) Expression (coarse) Expresión (control amplio) Effect Control 1 (coarse) Control de Efecto 1 (control amplio) Effect Control 2 (coarse) Control de Efecto 2 (control amplio) General Purpose Slider 1 Deslizador de Propósito General 1 General Purpose Slider 2 Deslizador de Propósito General 2 General Purpose Slider 3 Deslizador de Propósito General 3 General Purpose Slider 4 Deslizador de Propósito General 4 Bank Select (fine) Seleccion de Banco (detallado) Modulation Wheel (fine) Rueda de Modulacióin (detallado) Breath Controller (fine) Control de Aliento (detallado) Foot Pedal (fine) Pedal (detallado) Portamento Time (fine) Tiempo de Portamento (detallado) Data Entry (fine) Entradad de Datos (detallado) Volume (fine) Volumen (control fino) Balance (fine) Balance (control fino) Pan Position (fine) Posición del Paneo (control fino) Expression (fine) Expresión (control fino) Effect Control 1 (fine) Control de Efecto 1 (fino) Effect Control 2 (fine) Control de Efecto 2 (fino) Hold Pedal (on/off) Pedal de Sostener (enc/apag) Portamento (on/off) Portamento (enc/apag) Soft Pedal (on/off) Pedal suave (enc/apag) Legato Pedal (on/off) Pedal legato (enc/apag) Hold 2 Pedal (on/off) Pedal de Sostener2 (enc/apag) Sound Variation Variación del Sonido General Purpose Button 1 (on/off) Botón de Propósito General 1 (enc/apag) General Purpose Button 2 (on/off) Botón de Propósito General 2 (enc/apag) General Purpose Button 3 (on/off) Botón de Propósito General 3 (enc/apag) General Purpose Button 4 (on/off) Botón de Propósito General 4 (enc/apag) Effects Level Nivel de Efectos Chorus Level Nivel de Coro Celeste Level Nivel de Celeste Phaser Level Nivel de Phaser Data Button Increment Incremento del botón de datos Data Button Decrement Decremento del botón de datos Non-Registered Parameter (fine) Parámetro No Registrado (control fino) Non-Registered Parameter (coarse) Parámetro No Registrado (control amplio) Registered Parameter (fine) Parámetro Registrado (control fino) Registered Parameter (coarse) Parámetro Registrado (control amplio) All Sound Off Apagar Todos los Sonidos All Controllers Off Apagar todos los COntroladores Local Keyboard (on/off) Teclado Local (enc/apag) All Notes Off Apagar Todas las Notas Omni Mode Off Modo Omni Apagado Omni Mode On Modo Omni Encendido Mono Operation Operación Mono Poly Operation Operación Poli Pitch Bend Sensitivity Sensibilidad de Inflexión de Tono Fine Tune Ajuste Fino Coarse Tune Ajuste Amplio Tuning Program Programa de Afinación Tuning Bank Banco de Afinación Vibrato Rate Velocidad del Vibrato Vibrato Depth Profundidad del Vibrato Vibrato Delay Retraso del Vibrato Filter Cutoff Corte del Filtro Filter Resonance Resonacia del Filtro Sostenuto Pedal (on/off) Pedal de Sostenuto Release Time Tiempo de Caída Attack Time Tiempo de Ataque Brightness Brillo Decay Time Tiempo de Caída Tremolo Level Nivel de Trémolo EG Attack EG Decay EG Release Drum Filter Cutoff Drum Filter Resonance Drum EG Attack Drum EG Decay Drum Pitch Coarse Drum Pitch Fine Drum Level Drum Pan Drum Reverb Send Drum Chorus Send Drum Variation Send Modulation Wheel (14bit) Rueda de Modulacióin (14bit) Breath Controller (14bit) Control de Aliento (14bit) Foot Pedal (14bit) Pedal (14bit) Portamento Time (14bit) Tiempo de Portamento (14bit) Volume (14bit) Volumen (14 bit) Balance (14bit) Balance (14 bit) Pan Position (14bit) Posición del Paneo (14 bit) Expression (14bit) Expresión (14bit) Effect Control 1 (14bit) Control de Efecto 1 (14bit) Effect Control 2 (14bit) Control de Efecto 2 (14bit) General Purpose Slider 1 (14bit) Deslizador de Propósito General 1 (14 bit) General Purpose Slider 2 (14bit) Deslizador de Propósito General 2 14bit) General Purpose Slider 3 (14bit) Deslizador de Propósito General 3 (14bit) General Purpose Slider 4 (14bit) Deslizador de Propósito General (14bit) Chromatic Cromático Major Mayor Minor Menor Melodic Minor (Asc) Menor Melódica (Asc) Melodic Minor (Desc) Menor Melódica (Desc) Whole Tone Tonos Completos Pentatonic Major Pentatónica Mayor Pentatonic Minor Pentatónica Menor Pentatonic Blues Pentatónica Blues Pentatonic Neutral Pentatónica Neutral Octatonic (H-W) Octatónica (S-T) Octatonic (W-H) Octatónica (T-S) Ionian Jónica Dorian Dórica Phrygian Frigio Lydian Lidio Mixolydian Mixolidio Aeolian Eólico Locrian Locrio Egyptian Egipcia Eight Tone Spanish Española de 8 tonos Hawaiian Hawaiana Hindu Hindú Hirajoshi HirajÅshi Hungarian Major Húngara Mayor Hungarian Minor Húngara Menor Hungarian Gypsy Gitana Húngara Japanese (A) Japonesa (A) Japanese (B) Japonesa (B) Jewish (Adonai Malakh) Judía (Adonai Malach) Jewish (Ahaba Rabba) Judía (Ahaba Rabba) Jewish (Magen Abot) Judía (Magen Abot) Oriental (A) Oriental (B) Oriental (C) Roumanian Minor Rumana Menor Neapolitan Napolitana Neapolitan Major Napolitana Mayor Neapolitan Minor Napolitana Menor Overtone Lidia b7 (Overtone) Leading Whole Tone Tonos Completos Nine Tone Scale Escala de Nueve Tonos Dominant Seventh Séptima Dominante Augmented Aumentada Algerian Argelina Arabian (A) Ãrabe (A) Arabian (B) Ãrabe (B) Balinese Balinesa Chinese China Diminished Disminuida Japanese (Ichikosucho) Japonesa (Ichikosucho) Japanese (Taishikicho) Japonesa (Taishikicho) Javaneese Javanesa Marva Theta Mela Bhavapriya Mela Chakravakam Mela Chalanata Mela Chitrambari Mela Dharmavati Mela Dhatuvardhani Mela Dhavalambari Mela Divyamani Mela Ganamurti Mela Gangeyabhusani Mela Gavambodhi Mela Gayakapriya Mela Hatakambari Mela Jalarnavam Mela Jhalavarali Mela Jhankaradhvani Mela Jyotisvarupini Mela Kamavarardhani Mela Kantamani Mela Kosalam Mela Latangi Mela Manavati Mela Mararanjani Mela Naganandini Mela Namanarayani Mela Navanitam Mela Nitimati Mela Pavani Mela Ragavardhani Mela Raghupriya Mela Ramapriya Mela Rasikapriya Mela Ratnangi Mela Risabhapriya Mela Rupavati Mela Sadvidhamargini Mela Salagam Mela Sanmukhapriya Mela Sarasangi Mela Senavati Mela Subhapantuvarali Mela Sucharitra Mela Sulini Mela Suryakantam Mela Syamalangi Mela Tanarupi Mela Vagadhisvari Mela Vanaspati Mela Varunapriya Mela Yagapriya Persian Persa Purvi Theta Spanish Gypsy Gitana Española Todi Theta Enigmatic Enigmática Kumoi Lydian Augmented Lidia Aumentada Pelog Prometheus Prometeo Prometheus Neapolitan Prometeo Napolitana Six Tone Symmetrical Seis Tonos Simétrica Super Locrian Super Locria Lydian Minor Lidia Menor Lydian Diminished Lidia Disminuida Half Diminished Media Disminuida Bhairav Yaman Todi Jog Multani Darbari Malkauns Bhoopali Shivaranjani Marwa Minor 5 Menor 5 Major 5 Mayor 5 5 5 45 45 457 457 M 6 M 6 MIDI Editor Editor MIDI cut cortar delete borrar insert range insertar rango remove range borrar rango move mover edit editar resize redimensionar rescale re-escalar paste pegar Time: %1 Type: Tiempo: %1 Tipo: Note On (%1) %2 Velocity: %3 Duration: %4 Key Press (%1) %2 Value: %3 Pulsación de tecla (%1) %2 Valor: %3 Controller (%1) Name: %2 Value: %3 Controlador (%1) Nombre: %2 Valor: %3 RPN (%1) Name: %2 Value: %3 RPN (%1) Nombre: %2 Valor: %3 NRPN (%1) Name: %2 Value: %3 NRPN (%1) Nombre: %2 Valor: %3 Control 14 (%1) Name: %2 Value: %3 Control 14 (%1) Nombre: %2 Valor: %3 Pgm Change (%1) Cambio de programa (%1) Chan Press (%1) Chan Press (%1) Pitch Bend (%1) Portamento (%1) SysEx (%1 bytes) Data: SysEx (%1 bytes) Datos: Unknown (%1) Desconocido (%1) Start: %1 End: %2 Length: %3 Inicio: %1 Fin: %2 Duración: %3 qtractorMidiEditorForm MIDI Editor Editor MIDI &Track Pis&ta &File &Archivo Select &Mode &Modo de Selección &Select &Seleccionar I&nsert I&nsertar Remo&ve Remo&ver &Tools &Herramientas &Edit &Editar &View &Ver Instrum&ent Instrum&ento &Toolbars Bar&ras de heramientas &Windows Ventana&s Not&e Type Tipo de no&ta Val&ue Type Tipo de va&lor &Ghost Track &Pista fantasma &Zoom &Zoom S&nap A&juste Sc&ale &Escala T&ransport T&ransporte &Note St&ep &Help A&yuda &Save &Guardar Save Guardar Save current MIDI clip to existing file name Guardar clip MIDI actual a nombre de archivo existente Save &As... Guardar &Como... Save As Guardar Como Save as Guardar Como Save current MIDI clip with another file name Guardar clip MIDI actual con otro nombre de archivo &Mute &Silenciar Mute Mute current MIDI clip &Unlink Desvinc&ular Unlink Desvincular Unlink current MIDI clip Desvincular clip MIDI actual Recor&d Gra&bar Record Grabar Record current MIDI clip (overdub) Grabar clip MIDI actual (doblaje) &Inputs &Entradas Track Inputs Entradas de Pista Track inputs Entradas de Pista Show current MIDI clip/track input bus connections Muestra las conexiones al bus de entrada del clip/track MIDI actual &Outputs &Salidas Track Outputs Salidas de Pista Track outputs Salidas de Pista Show current MIDI clip/track output bus connections Muestra las conexiones al bus de salida del clip/track MIDI actual &Properties... &Propiedades... Track Properties Propiedades de Pista Track properties Propiedades de pista Edit current MIDI clip/track properties Editar las propiedades del clip/pista MIDI actual Shift+F2 Shift+F2 Properties Propiedades Edit current MIDI clip properties Editar las propiedades del clip MIDI actual F4 F4 &Range Set &Fijar Rango Clip Range Rango de Clip Clip range Rango de clip Set edit-range from clip extents Fijar rango de edición desde la extensión del clip &Loop Set Fijar Buc&le Clip Loop Bucle de Loop Clip loop Bucle de loop Set loop-range from clip extents Fijar rango de bucle desde la extensión del clip &Close &Cerrar Close Cerrar Close this MIDI clip editor Cerrar este editor de clip MIDI &Undo &Deshacer Undo Deshacer Undo last edit operation Deshacer última operación de edición Ctrl+Z Ctrl+Z &Redo &Rehacer Redo Rehacer Redo last edit operation Reshacer última operación de edición Ctrl+Shift+Z Ctrl+Shift+Z Cu&t Cor&tar Cut Cortar Cut current selection into the local clipboard Cortar selección actual a portapapeles Ctrl+X Ctrl+X &Copy &Copiar Copy Copiar Copy current selection to the local clipboard Copiar selección actual al portapapeles local Ctrl+C Ctrl+C &Paste &Pegar Paste Pegar Paste local clipboard contents into the current MIDI clip Pegar el contenido del portapapeles local en el clip MIDI actual Ctrl+V Ctrl+V Past&e Repeat... P&egar Repetido... Paste Repeat Pegar Repetido Paste repeat Pegar repetido Paste/repeat local clipboard contents into the current MIDI clip Pegar/repetido el contenido del portapapeles local en el clip MIDI actual Ctrl+Shift+V Ctrl+Shift+V &Delete &Eliminar Delete Borrar Delete current selection Borrar selección actual Del Supr Edit Of&f A&pagar edición Edit Off Apagar edición Edit off Apagar edición Set edit mode off Fijar modo de edición en apagado Edit &On E&ncender Edición Edit On Encender edición Edit on Encender edición Set edit mode on Fijar modo de edición en encendido Edit &Draw Editar &Dibujo Edit draw mode Editar modo de dibujo Edit draw mode (notes) Editar modo de dibujo (notas) &All &Todo Select All Seleccionar Todo Select all Seleccionar Todo Ctrl+A Ctrl+A &None &Nada Select None Seleccionar Nada Select none Seleccionar Nada Ctrl+Shift+A Ctrl+Shift+A &Invert &Inverso Select Invert Seleccionar Inverso Select invert Seleccionar Inverso Ctrl+I Ctrl+I &Range &Rango Select Range Seleccionar rango Select range Seleccionar rango Mark range as selected Marcar rango como seleccionado Ctrl+R Ctrl+R Insert Range Insertar Rango Insert range Insertar Rango Insert range as selected Insertar rango como seleccionado Ctrl+Ins Ctrl+Ins &Step Insert step Insert step (rest) Right DO NOT TRANSLATE Remove Range Quitar Rango Remove range Quitar Rango Remove range as selected Remover rango como seleccionado Ctrl+Del Ctrl+Del &Quantize... &Cuantización... Quantize Cuantizar Quantize selection Cuantizar selección &Transpose... &Transponer... Transpose Transponer Transpose selection Transponer selección &Normalize... &Normalizar... Normalize Normalizar Normalize selection Normalizar selección &Randomize... Aleato&rio... Randomize Aleatorio Randomize selection Aleatorizar selección Resi&ze... Redimen&sionar... Resize Redimensionar Resize selection Redimensionar selección Re&scale... Ree&scalar... Rescale Re-escalar Rescale selection Reescalar selección T&imeshift... Desplazamiento de &tiempo... Timeshift Desplazamiento de tiempo Timeshift selection Selección del desplazamiento de tiempo T&empo ramp... Tempo ramp Tempo ramp selection &Menubar Barra de &menú Menubar Barra de menú Show/hide the menubar Mostrar/ocultar la barra de menú Ctrl+M Ctrl+M &Statusbar B&arra de estado Statusbar Barra de estado Show/hide the statusbar Mostrar/ocultar la barra de estado File Toolbar Barra de Herramientas de Archivo File toolbar Barra de herramientas de archivo Show/hide the file toolbar Mostrar/ocultar la barra de herramientas de archivos Edit Toolbar Barra de Heramientas de Edición Edit toolbar Barra de herramientas de edición Show/hide the edit toolbar Mostrar/ocultar la barra de herramientas de edición View Toolbar Barra de Herramientas de Vista View toolbar Barra de herramientas de vista Show/hide the view toolbar Mostrar/ocultar la barra de herramientas de vista &Transport &Transporte Transport Toolbar Barra de Herramientas de Transporte Transport toolbar Barra de herramientas de transporte Show/hide the transport toolbar Mostrar/ocultar la barra de herramientas de transporte T&ime T&iempo Time Toolbar Barra de Heramientas de Tiempo Time toolbar Barra de heramientas de tiempo Show/hide the time toolbar Mostrar/ocultar la barra de herramientas de tiempo &Scale &Escala Scale Toolbar Barra de Heramientas de Escala Scale toolbar Barra de heramientas de escala Show/hide the scale toolbar Mostrar/ocultar la barra de herramientas de escala Thum&b &Miniatura Thumb Toolbar Barra de Herramientas de Miniatura Thumb toolbar Barra de herramientas de miniatura Show/hide the thumb view toolbar Mostrar/ocultar la barra de herramientas de miniatura Note &Names &Nombres de Notas Note Names Nombres de Notas Note names Nombres de notas Whether to show note names Si se deben mostrar los nombres de nota Note &Duration &Duración de Nota Note Duration Duración de Nota Note duration Duración de nota Whether note events are shown proportional to duration Si los eventos de nota se muestran proporcionales a la duración Note &Color &Color de Nota Note Color Color de Nota Note color Color de nota Whether note events are colored according to pitch Si los eventos de nota se colorean de acuerdo al tono &Value Color &Valor de Color Value Color Valor de Color Value color Valor de color Whether note events are colored according to value (velocity) Si los eventos de nota se colorean de acuerdo al valor (velocidad) &Drum Mode &Modo Batería Drum Mode Modo Batería Drum mode Modo Batería Whether note onset events are displayed as diamonds Si los eventos de inicio de nota se muestran como diamantes &Events &Eventos View events Vista de eventos Show/hide the events list Mostrar/ocultar la lista de eventos &Preview Notes Vista &Previa de Notas Preview Notes Vista Previa de Notas Preview notes Vista previa de notas Preview notes while editing (scrub) Vista previa de notas mientras se edita (scrub) F&ollow Playhead &Seguir Cabezal de Reproducción Follow Playhead Seguir Cabezal de Reproducción Follow playhead Seguir cabezal de reproducción &In &Acercar Zoom In Acercar Zoom Zoom in Acercar Zoom Ctrl++ Ctrl++ &Out A&lejar Zoom Out Alejar Zoom Zoom out Alejar Zoom Ctrl+- Ctrl+- &Reset &Reiniciar Zoom Reset Reiniciar Zoom Zoom reset Reiniciar Zoom Ctrl+1 Ctrl+1 &Horizontal &Horizontal Horizontal Zoom Zoom Horizontal Horizontal zoom Zoom horizontal Horizontal zoom mode Modo de zoom horizontal &Vertical &Vertical Vertical Zoom Zoom Vertical Vertical zoom Zoom vertical Vertical zoom mode Modo de zoom vertical All Zoom Zoom Todo All zoom Zoom todo All zoom mode Modo de zoom todo &Zebra &Cebra Zebra Cebra Bar zebra view mode Modo de vista barras cebra &Grid &Rejilla Grid Rejilla Snap grid view mode Modo de vista ajustar a rejilla Too&l Tips A&yuda emergente Tool tips Ayuda emergente Floating tool tips view mode Modo de vista flotante de la ayuda contextual &Refresh A&ctualizar Refresh Actualizar Refresh views Actualizar vistas F5 F5 &Backward &Hacia Atrás Backward Hacia Atrás Transport backward Transporte hacia atrás Backspace Retroceso Re&wind Re&bobinar Rewind Rebobinar Transport rewind Rebobinar transporte F&ast Forward Adel&antar rápido Fast Forward Adelantar rápido Fast forward Adelantar rápido Transport fast forward Adelantar rápido transporte &Forward Ha&cia Adelante Forward Hacia adelante Transport forward Transporte hacia adelante Step Backward Step backward Transport step backward Step Forward Step forward Transport step forward Note Backward Step note backward Transport step note backward Note Forward Step note forward Transport step note forward &Loop &Bucle Loop Bucle Transport loop Bucle de transporte Ctrl+Shift+L Ctrl+Shift+L Loop &Set Fijar Buc&le Loop Set Fijar Bucle Loop set Fijar Bucle Transport loop set Fijar transporte de bucle Ctrl+L Ctrl+L &Stop &Detener Stop Detener Transport stop Detener transporte &Play &Reproducir Play Reproducir Transport play/pause Reproducir/pausar transporte Space Espacio &Record &Grabar Transport record Transporte a grabación &Punch &Pinchazo Punch Pinchazo Punch in/out Punch in/out Transport punch in/out Transporte punch in/out Ctrl+Shift+P Ctrl+Shift+P Punch Se&t Fi&jar Punch Punch Set Fijar Punch Punch in/out set Fijar punch in/out Transport punch in/out set Fijar punch in/out de transporte Ctrl+P Ctrl+P Pa&nic Pá&nico Panic Pánico All MIDI tracks shut off (panic) Apagar todas las pistas MIDI (pánico) &Shortcuts... Atajo&s... Shortcuts Atajos Keyboard shortcuts Atajos de teclado &About... &Acerca de... About Acerca de Show information about this application program Muestra información acerca de este porgrama de aplicación About &Qt... Acerca de &Qt... About Qt Acerca de Qt Show information about the Qt toolkit Mostrar información acerca del kit de herramientas Qt Current time (play-head) Tiempo actual (cabezal de reproducción) Current tempo (BPM) Tempo actual (PPM) Reset time-sig. Reiniciar compás. Set current snap to %1 Fijar el snap (ajuste) actual a %1 Note Velocity Velocidad de nota Snap/beat Snap/pulso Note type Tipo de nota Value type Tipo de valor Parameter type Tipo de parámetro Scale key Clave de la escala Scale type Tipo de escala MIDI clip name Nombre de clip MIDI MIDI file name Nombre de archivo MIDI MIDI track/channel Pista/canal MIDI MOD MOD MIDI modification state Estado de modificación MIDI REC REC MIDI clip record state Estado de grabación del clip MIDI MUTE MUTE MIDI clip mute state 00:00:00.000 00:00:00.000 MIDI clip duration Duración del clip MIDI Warning Advertencia The current MIDI clip has been changed: "%1" Do you want to save the changes? El clip MIDI actual ha cambiado: "%1" ¿Desea guardar los cambios? Save MIDI Clip Guardar clip MIDI MIDI files (*.%1 *.smf *.midi) Archivos MIDI (*.%1 *.smf *.midi) All files (*.*) Todos los archivos (*.*) Channel %1 Canal %1 Track %1 Pista %1 [modified] [modificado] qtractorMidiEventList Events Eventos qtractorMidiEventListView::ItemDelegate edit %1 editar %1 qtractorMidiEventListView::ItemModel Time Tiempo Type Tipo Name Nombre Value Valor Duration/Data Duración/Datos Frame Cuadro BBT BBT Note On (%1) Encendido de nota (%1) Note Off (%1) Apagado de nota (%1) Key Press (%1) Pulsación de tecla (%1) Controller (%1) Controlador (%1) Control 14 (%1) Control 14 (%1) RPN (%1) RPN (%1) NRPN (%1) NRPN (%1) Pgm Change Cambio de Programa Chan Press Chan Press Pitch Bend Portamento SysEx SysEx Meta (%1) Meta (%1) Unknown (%1) Desconocido (%1) qtractorMidiListView Name Nombre Fmt Frmt Tracks Pistas tpqn tpqn Path Ruta %1: MIDI file not found. %1: Archivo MIDI no encontrado. Open MIDI Files Abrir archivos MIDI MIDI files (*.%1 *.smf *.midi) Archivos MIDI (*.%1 *.smf *.midi) All files (*.*) Todos los archivos (*.*) qtractorMidiMixerMeter Volume (%) % % Pan: %1 Volume: %1% Volumen: %1% qtractorMidiSysexForm MIDI SysEx MIDI SysEx Name Nombre Size Tamaño Data (hex) Datos (hexa) Import from SysEx file Importar desde archivo SysEx &Import... &Importar... Export to SysEx file Exportar a archivo SysEx E&xport... E&xportar... Move SysEx item up on list order Mover elemento SysEx hacia arriba en el orden de la lista &Up A&rriba Move SysEx item down on list order Mover elemento SysEx hacia abajo en el orden de la lista &Down A&bajo Open SysEx Abrir SysEx Sysex name Nombre SysEx Save SysEx Guardar SysEx Delete SysEx Borrar SysEx Create SysEx item Crear elemento SysEx &Add &Añadir Update SysEx item Actualizar elemento SysEx Upda&te Act&ualizar Remove SysEx item Bprrar elemento SysEx &Remove &Quitar SysEx files (*.%1) Archivos SysEx (*.%1) MIDI files (*.mid *.smf *.midi) Archivos MIDI (*.mid *.smf *.midi) All files (*.*) Todos los archivos (*.*) Import SysEx Files Importar archivos SysEx Export SysEx File Exportar archivos SysEx Warning Advertencia The SysEx file already exists: "%1" Do you want to replace it? El archivo SysEx ya existe: "%1" ¿Desea reemplazarlo? About to replace SysEx: "%1" Are you sure? A punto de reemplazar SysEx: "%1" ¿Esta seguro? About to delete SysEx: "%1" Are you sure? A punto de eliminar SysEx: "%1" ¿Esta seguro? SysEx settings have been changed. Do you want to apply the changes? Ajustes SysEx han cambiado. ¿Desea aplicar los cambios? Error Error SysEx could not be loaded: "%1". Sorry. No se pudo cargar SysEx: "%1". Lo siento. qtractorMidiThumbView MIDI Thumb view Vista previa MIDI qtractorMidiToolsForm MIDI Tools Herramientas MIDI Preset name Nombre del preajuste Save preset Guardar preajuste Delete preset Borrar preajuste &Quantize &Cuantizar Quantize selected events Cuantizar eventos seleccionados &Time: &Tiempo: Quantize time Tiempo de cuantización Quantize time percent Porcentaje de cuantización de tiempo % % &Duration: &Duración: Quantize duration Cuantizar duración Quantize duration percent Porcentaje de cuantización de duración S&wing: S&wing: Swing-quantize time Cuantización de tiempo del swing Swing-quantize percent Porcentaje de cuantización del swing Swing-quantize type Tipo de cuantización del swing Linear Lineal Quadratic Cuadrático Cubic Cúbico &Scale: &Escala: Scale-quantize key Clave de la cuantización de la escala Scale-quantize type Tipo de la cuantización de la escala &Transpose &Transponer Transpose selected events Transponer eventos seleccionados &Note: &Nota: Transpose note Transponer nota Transpose time Transponer tiempo Transpose time format Transponer formato de tiempo Frames Cuadros Time Tiempo BBT BBT &Reverse &Reversa &Normalize &Normalizar Normalize selected events Normalizar eventos seleccionados &Percent: &Porcentaje: Normalize percent Normalizar porcentaje &Value: &Valor: Normalize value Normalizar value &Compress &Randomize Aleato&rio Randomize selected events Aleatorizar eventos seleccionados Randomize note/pitch Aleatorizar nota/tono Randomize time Aleatorizar tiempo Randomize duration Aleatorizar duración Randomize value Aleatorizar valor Resi&ze Redimen&sionar Resize selected events Redimensionar eventos seleccionados Resize duration Redimensionar duración Resize duration format Formato de duración para redimensionar Resize value Valor para redimensionar Resize value mode Modo del valor para redimensionar Flat Plano Ramp Rampa Resize final value Valor final a redimensionar &Legato: Legato trim/extend type Normal Normal Trim Extend Legato trim/extend length Legato mode Mono Poly &Join &Split: Split notes length Split notes offset Relative Absolute Re&scale Rescale selected events Reescalar eventos seleccionados Rescale time Reescalar tiempo Rescale duration Reescalar duración Rescale value Valor para redimensionar &Invert &Inverso T&imeshift Desplazamiento de t&iempo Timeshift selected events Desplazamiento de tiempo de los eventos seleccionados Timeshift Desplazamiento de tiempo P: P: Timeshift parameter Parámetro para el desplazamiento de tiempo Timeshift parameter (log) Parámetro para el desplazamiento de tiempo (log) Timeshift curve Curva del desplazamiento de tiempo P = 0 : no change. P > 0 : accelerating shift. P < 0 : slowing down shift. Edit head/tail (blue) markers define the shift range. P = 0 : sin cambio. P > 0 : acelerando. P < 0 : ralentizando. Editar los marcadores de cabeza/cola (azul) definen el rango de desplazamiento. Timeshift duration Duración del desplazamiento de tiempo T&empo ramp Tempo ramp selected events Tempo ramp From Tempo ramp start to Temporamp end Edit head/tail (blue) markers define the ramp range. Tempo ramp duration (default) (default) Warning Advertencia About to delete preset: "%1" Are you sure? A punto de eliminar preajuste: "%1" ¿Esta seguro? none ninguno quantize cuantizar transpose transponer normalize normalizar randomize aleatorio resize redimensionar rescale reescalar timeshift desplazamiento de tiempo temporamp qtractorMixer Inputs Entradas Tracks Pistas Outputs Salidas Mixer Mezcladora qtractorMixerMeter Pan qtractorMixerRackWidget &Inputs &Entradas &Outputs &Salidas &Monitor &Monitor &Buses... &Buses... &Audio &Audio &MIDI &MIDI qtractorMixerStrip inputs entradas outputs salidas Connect %1 Conectar %1 (Audio) (Audio) (MIDI) (MIDI) (None) (Ninguno) In Out qtractorMonitorButton monitor monitor qtractorOptionsForm &General &General Session Sesión Default session &file format: &Formato de archivo de sesión por defecto: Default session file format (suffix) Formato de archivo de sesión por defecto (sufijo) Whether to create new sessions based on template Si se crean nuevas nuevas sesiones basadas en plantilla &New session template: Plantilla de &nueva sesión: New session template Plantilla de nueva sesión Browse for new session template Buscar plantilla de nueva sesión Whether to save backup versions of existing sessions Si se guardan versiones de respaldo de las sesiones existentes Save &backup versions of existing sessions: &Guardar versiones de respaldo de las sesiones existentes: Which mode to rename existing session files Cuál modo se usa para renombrar los archivos de sesión existentes Increment previous version (default) Incrementar versión anterior (por defecto) Increment current version Incrementar versión actual Whether to enable session auto-save (crash-recovery) Si se habilita el auto guardado de sesión (recuperación de crash) Auto-save current working session every: Auto guardar la sesión actual cada: Auto-save period (minutes) Período de auto guardado (minutos) minutes minutos Options Opciones Whether to ask for confirmation on removal Si se pide confirmación al quitar &Confirm removals &Confirmar al quitar Whether to ask for confirmation on archive directory removal Si se pide confirmación al quitar directorio de registro C&onfirm archive removals C&onfirmar quitar registro Number of &recent files: Número de archivos &recientes: The maximum number of recent files to keep in menu El número máximo de archivos recientes a conservar en el menú Whether to capture standard output (stdout/stderr) into messages window Si se captura la salida estándar (stdout/stderr) en la ventana de mensajes Capture standard &output Capt&urar salida estándar Whether to show the complete directory path of loaded session files Si se muestra la ruta completa del directorio de sesiones cargadas S&how complete path of session files Mo&strar ruta completa de archivos de sesión Whether to remove audio peak files on session close Si se quitan los achivos de picos de audio al cerrar sesión Auto-remove audio pea&k files Auto-&quitar archivos de picos de audio Whether to keep all tool windows on top of the main window Si se mantiene la ventana con todas las herramientas encima de la ventana principal Keep tool &windows always on top Mantener &ventanas de herramientas siempre encima Whether to try dropping multiple audio files into the same track Si se intenta dejar caer multiples archivos de audio en la misma pista &Drop multiple audio files into the same track &Dejar caer multiples archivos de audio en la misma pista Whether to reverse keyboard modifiers role on transport positioning (Shift/Ctrl) Si se revierte la función de las teclas modificadoras al posicionar el transporte (Mayús/Ctrl) Reverse &keyboard modifiers role (Shift/Ctrl) Re&vertir la función de las teclas modificadoras (Mayús/Ctrl) Whether to reverse mouse middle-button role on keyboard modifiers (Shift/Ctrl) Si se invierte el papel del botón del medio del ratón en modificadores de teclado (Mayus/Ctrl) Re&verse middle-button modifier role (Shift/Ctrl) Inv&ertir papel del botón del medio (Mayus/Ctrl) Transport Transporte Transport &mode: &Modo de transporte: Transport control mode (JACK) Modo de control de transporte (JACK) None Ninguno Slave Esclavo Master Maestro Full Completo Whether to start as timebase master (JACK) Si se debe iniciar cómo base de tiempo maestro (JACK) &Timebase Base de &tiempo &Loop recording mode (takes): M&odo de grabación en bucle (tomas): Loop recording mode (takes) Modo de grabación en bucle (tomas) First Primera Last Última &Audio &Audio Capture / Export Captura / Exportar Audio compression quality to use on capture (record) and export Calidad de compresión de audio al capturar (grabar) y al exportar Audio sample format to use on capture (record) and export Formato de muestra de audio al capturar (grabar) y al exportar Audio file type to use on capture (record) and export Formato de archivo de audio al capturar (grabar) y al exportar File &type: &Tipo de archivo: Whether to keep all editor windows on top of the main window Si se mantienen las ventanas del editor encima de la ventana principal Keep &editor windows always on top Mantener ventanas del &editor siempre encima Sample &format: &Formato de muestreo: &Quality: &Calidad: Playback Reproducción Whether to apply time-stretching when tempo changes Si se aplica estiramiento de tiempo cual el tempo cambia Aut&omatic time-stretching E&stirar tiempo automático Sample-&rate converter type: Tipo de conver&sión de frecuencia de muestreo: Sample-rate converter quality Calidad del conversor de frencuencia de muestreo Sinc (Best Quality) Sinc (Mejor Calidad) Sinc (Medium Quality) Sin (Calidad Media) Sinc (Fastest) Sinc (La Más Rápida) Zero Order Hold Retenedor de Orden Cero Linear Lineal Whether to use WSOLA time-stretching Si se usa estiramiento de tiempo WSOLA &WSOLA time-stretching Estiramiento de tiempo &WSOLA Whether to use RubberBand formant preserve RubberBand &formant preserve Whether to apply WSOLA quick seek time-stretching Si se aplica estiramiento de tiempo de busqueda rápida WSOLA WSOLA quic&k seek Bús&queda rápida WSOLA Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine Whether to have separate audition/pre-listening player output ports Si se tienen puertos de salida separadas para la reproducción auditar/pre-escucha Dedicated au&dition/pre-listening player outputs: Sali&das de reproducción dedicadas para auditar/pre-escucha: Whether to auto-connect dedicated audio player outputs Si se auto-conectan las salidas dedicadas de reproducción de audio Auto-&connect Auto-&conectar Connections Conexiones Whether to warn about audio self-connections &Warn about self-connections Metronome Metrónomo Whether to enable the audio metronome Si se habilita el audio del metrónomo &Enable audio metronome &Habilitar audio del metrónomo &Count-in: &Conteo de entrada: Count-in mode Modo de conteo de entrada Recording Grabación Count-in number Número de conteo de entrada beats golpes &File (bar): &Archivo (compás): Metronome Audio filename (bar) Archivo de audio del metrónomo (compás) Browse for sample audio file (bar) Buscar el archivo de audio (compás) &Gain (bar): &Ganancia (compás): Metronome gain (bar) Ganancia del metrónomo (compás) dB dB &File (beat): &Archivo (pulso): Metronome Audio filename (beat) Archivo de audio del metroÅ„omo (pulso) Browse for sample audio file (beat) Buscar el archivo de audio (pulso) &Gain (beat): &Ganancia (pulso): Metronome gain (beat) Ganancia del metrónomo (pulso) Whether to have separate audio metronome output ports Si se tienen puertos de salida separadas para el metrónomo Dedicated a&udio metronome outputs: S&alidas de audio dedicadas para el metrónomo: Whether to auto-connect dedicated audio metronome outputs Si se auto-conectan las salidas dedicadas de audio del metrónomo Auto-co&nnect Auto-co&nectar &Offset (latency): &Compensación (latencia): Metronome Audio offset (latency) Compensación de Metrónomo Audio (latencia) &MIDI &MIDI File &format: &Formato de archivo: MIDI file format to use on capture (record) and export Formato de archivo MIDI al capturar (grabar) y al exportar &Quantize: &Cuantizar: MIDI capture (record) quantization Cuantización de la captura (grabación) MIDI Queue &timer (resolution): &Temporizador de la cola (resolución): Queue timer (resolution) Temporizador de la cola (resolución) Whether to enable MIDI queue time drift correction Si se habilita la corrección del desplazamiento de tiempo de la cola MIDI E&nable MIDI queue time drift correction Ha&bilitar la corrección de desplazamiento de tiempo de la cola MIDI Whether to have separate MIDI player output ports Si se tienen puertos de salida separadas para reproducción MIDI Dedicated MIDI p&layer outputs Sa&lidas dedicadas de reproducción MIDI Whether to reset/resend all controllers on playback start Si se reinician/reenvían todos los controladores al iniciar la reproducción &Reset all controllers on playback start &Reiniciar todos los controladores al iniciar la reproducción Control Control &MMC: &MMC: MIDI Machine Control (MMC) mode Modo de MIDI Machine Control (MMC) Input Entrada Output Salida Duplex Duplex &Device: &Dispositivo: MIDI Machine Control (MMC) device id. Id de dsipositivo MIDI Machine Control (MMC). &SPP: &SSP: MIDI Song Position pointer (SPP) control mode Modo de control del apuntador de posición de canción (SSP) MIDI MIDI Cloc&k: Relo&j MIDI: MIDI Clock control mode Modo de control del reloj MIDI Whether to have separate MIDI control ports Si se tienen puertos de control MIDI separados Dedicated MIDI &control input/output Entrada/salida &dedicadas para control MIDI Whether to enable the MIDI metronome Si se debe habilitar metrónomo MIDI &Enable MIDI metronome &Habilitar metrónomo MIDI &Channel: &Canal: Metronome MIDI channel Canal de metrónomo MIDI &Note (bar): &Nota (compás): Metronome MIDI note (bar) Nota (compás) metrónomo MIDI &Velocity (bar): &Velocidad (compás): Metronome MIDI velocity (bar) Velocidad MIDI del metrónomo (compás) &Duration (bar): &Duración (compás): Metronome MIDI duration (bar) Duración de metrónomo MIDI (compás) &Note (beat): &Nota (pulso): Metronome MIDI note (beat) Nota (pulso) metrónomo MIDI &Velocity (beat): &Velocidad (pulso): Metronome MIDI velocity (beat) Velocidad MIDI del metrónomo (pulso) &Duration (beat): &Duración (pulso): Metronome MIDI duration (beat) Duración de metrónomo MIDI (pulso) Whether to have separate MIDI metronome output port Si se tienen puertos de salida separados para el metrónomo MIDI Dedicated M&IDI metronome output S&alidas dedicadas para el metrónomo MIDI Metronome MIDI offset (latency) Compensación de Metrónomo MIDI (latencia) &Display &Visualización Defaults Predeterminados &Time display format: &Formato para desplegar tiempo: Time display format Formato para desplegar tiempo Frames Cuadros Time Tiempo BBT BBT &Base font size: Tamaño &base de la fuente: Base application font size (pt.) Tamaño base de la fuente para la aplicación (pt.) (default) (default) 6 6 7 7 8 8 9 9 10 10 11 11 12 12 Whether to hold auto-scrolling (follow play-head) on edits. Si se mantiene el auto-desplazamiento (seguir cabezal reproducción) en ediciones. &Icons theme: Custom icons theme directory Browse for custom icons theme directory St&yle sheet: Custom style sheet (*.qss) Browse for custom style sheet (*.qss) Move down path Mover ruta hacia abajo Blacklist Lista negra Plugin blacklist path Ruta de la lista negra de Plug-ins Browse plugin blacklist path Navegar ruta de la lista negra de Plug-ins Add plugin blacklist path Añadir ruta de lista negra de Plug-ins Plugin blacklist paths Rutas de lista negra de Plug-ins Remove plugin blacklist path Quitar ruta de lista negra de Plug-ins Clear plugin blacklist paths Limpiar ruta de la lista negra de Plug-ins &Clear &Limpiar Whether to use desktop environment native dialogs. Si se usan diálogos nativos del entorno de escritorio. Use desktop environment &native dialogs Usar diálogos &nativos del entorno de escritorio Custom Personalizado &Color theme: &Color del tema: Custom color palette theme Paleta de colores de tema personalizado Wonton Soup DO NOT TRANSLATE KXStudio DO NOT TRANSLATE Manage custom color palette themes Gestionar los temas de las paletas de colores personalizados &Style theme: E&stilo del tema: Custom widget style theme Estilo de tema widget personalizado Meters Medidores &Audio: &Audio: Audio meter level Nivel de medidor de Audio Over Pasado 0 dB 0 dB 3 dB 3 dB 6 dB 6 dB 10 dB 10 dB Audio meter color Color del medidor de audio Select custom audio meter color Seleccionar color personalizado del medidor de audio ... ... &Hold auto-scrolling (follow play-head) on edits &Mantener el desplazamiento automático (sigue la reprodución) en las ediciones Trac&k color saturation: &Saturación de color de pista: Default new track color saturation Saturación de color por defecto de nueva pista % % Back Atrás &MIDI: &MIDI: MIDI meter level Nivel del medidor MIDI Peak Pico MIDI meter color Color del medidor MIDI Select custom MIDI meter color Seleccionar color personalizado del medidor MIDI Reset meter colors to default Reiniciar colores de los medidores a los predeterminados &Reset &Reiniciar Messages Mensajes Sample messages text font display Fuente del texto del visualizador de mensajes Select font for the messages text display Seleccionar fuente para el visualizador de mensajes de texto &Font... &Fuente... Whether to keep a maximum number of lines in the messages window Si se debe mantener un límite máximo de líneas en la ventana de mensajes M&essages limit: Límite de &mensajes: The maximum number of message lines to keep in view El número máximo de mensajes que se deben conservar en la vista lines líneas Logging Registro Messages log file Archivos de registro de mensajes Browse for the messages log file location Buscar ubicación del archivo de mensajes de registro Whether to activate a messages logging to file. Si se debe activar un registro de mensajes a archivo. Messages &log file: Archivo de registro de &mensajes: &Plugins &Plugins Paths Rutas Plugin type Tipo de plugin Plugin path Ruta de plugin Browse plugin path Buscar ruta de plugin Add plugin path Añadir ruta de plugin &Add &Añadir Plugin paths Rutas de plugins Remove plugin path Quitar ruta de plugin &Remove &Quitar Move up path Mover ruta hacía arriba &Up A&rriba &Down A&bajo &LV2 Presets directory: Directorio de preajustes &LV2: LV2 Presets directory (default: ~/.lv2) Directorio de preajustes LV2 (por defecto: ~/.lv2) Browse LV2 Presets directory Buscar directorio de preajustes LV2 Instruments Instrumentos Whether to have separate audio output ports Si se tienen puertos de salida de audio separados Dedicated audi&o outputs: Salidas de audi&o dedicadas: Whether to auto-connect dedicated audio output ports Si se auto-conectan los puertos de salida de audio dedicados Au&to-connect Auto-&conectar Editor Editor Whether to open plugin's editor (GUI) by default Si se debe abrir el editor del plugin (GUI) por defecto Open plugin's &editor (GUI) by default Abrir el &editor del plugin (GUI) por defecto Whether to select plugin's editor (GUI) if more than one are available Si se debe seleccionar el editor del plugin (GUI) si más de uno está disponible &Select plugin's editor (GUI) if more than one are available &Seleccionar el editor del plugin (GUI) si más de uno está disponible XML Default (*.%1) XML Por Defecto (*.%1) XML Regular (*.%1) XML Normal (*.%1) ZIP Archive (*.%1) Archive ZIP (*.%1) (Any) (Cualquiera) Warning Advertencia Some settings have been changed. Do you want to apply the changes? Algunas configuraciones han cambiado. ¿Desea aplicar los cambios? Metronome Bar Audio File Archivo de audio de compás del metrónomo Metronome Beat Audio File Archivo de audio de pulso del metrónomo Open Style Sheet Style Sheet files (*.%1) Icons Theme Directory Audio Meter Color Color del Medidor de Audio MIDI Meter Color Color del Medidor MIDI Plug-in Directory Directorio de Plug-in LV2 Presets Directory Directorio de Preajustes LV2 Plug-in Blacklist Lista negra de Plug-ins Plug-in files (*.%1) Archivos de Plug-ins (*.%1) Messages Font Fuente de Mensajes Messages Log Registro de Mensajes Log files (*.%1) Archivos de registro (log) (*.%1) All files (*.*) Todos los archivos (*.*) Session Template Plantilla de Sesión Session template files (*.qtr *.qts *.%1) Archivos de plantilla sesión (*.qtr *.qts *.%1) qtractorPaletteForm Color Themes Temas de Colores Name Nombre Current color palette name Nombre de la paleta de colores actual Save Guardar Save current color palette name Guardar el nombre de la paleta de colores actual Delete Borrar Delete current color palette name Borrar nombre de la paleta de colores actual Palette Paleta Current color palette Paleta de colores actual Generate: Generar: Base color to generate palette Color base para generar la paleta Reset Reset Reset all current palette colors Reiniciar todas las paletas de colores actuales Import a custom color theme (palette) from file Importar un tema (paleta) de colores de personalizado Import... Importar... Export a custom color theme (palette) to file Exportar un tema (paleta) de colores de personalizado a archivo Export... Exportar... Show Details Mostrar Detalles Import File - %1 Importar Archivo - %1 Palette files (*.%1) Archivos de paletas (*.%1) Save Palette - %1 All files (*.*) Todos los archivos (*.*) Warning - %1 Advertencia - %1 Could not import from file: %1 Sorry. No se pudo importar el archivo: %1 Lo siento. Export File - %1 Exportar Archivo - %1 Some settings have been changed. Do you want to discard the changes? Algunas configuraciones han cambiado. ¿Desea descartar los cambios? Some settings have been changed: "%1". Do you want to save the changes? Algunas configuraciones han cambiado: "%1" ¿Desea descartar los cambios? qtractorPaletteForm::PaletteModel Color Role Rol del Color Active Activo Inactive Inactivo Disabled Desactivado qtractorPasteRepeatForm Paste Repeat Pegar Repetido Repeat Repetir &Count: &Cuenta: Repeat count Cuenta de repeticiones &Period: &Periodo: Repeat period Periodo de repetición Repeat period format Formato del periodo de repetición Frames Cuadros Time Tiempo BBT BBT Warning Advertencia Some settings have been changed. Do you want to apply the changes? Algunas configuraciones han cambiado. ¿Desea aplicar los cambios? qtractorPluginForm Plugin Properties Propiedades de Plugin Open preset Abrir preajuste Preset name Nombre de preajuste Save preset Guardar preajuste Delete preset Borrar preajuste Alias: Plugin alias Edit plugin Editar plugin Edit Editar Active Activar About Acerca de Outputs (Sends) Salidas (Envíos) Sends Envíos Inputs (Returns) Entradas (Retornos) Returns Retornos Auto-connect Aux Send Bus: Bus de Envío Auxiliar: Manage buses Administrar buses ... ... Audio bus I/O matrix I/O Matrix... Direct Access Parameter Parametro de Acceso Directo Direct Access Acceso Directo Page %1 Página %1 %1 [%2], %3 instance(s), %4 channel(s). %1 [%2], %3 instancia(s), %4 canal(es). (none) (ninguno) Open Preset Abrir Preajuste Preset files (*.%1) Archivos de preajuste (*.%1) All files (*.*) Todos los archivos (*.*) Error Error Preset could not be loaded from file: "%1". Sorry. No se pudo cargar el Preset desde el archivo: "%1". Lo siento. Save Preset Guardar Preajuste Preset could not be saved to file: "%1". Sorry. No se pudo guardar el Preset al archivo: "%1". Lo siento. Warning Advertencia About to delete preset: "%1" (%2) Are you sure? A punto de eliminar preajuste: "%1" (%2) ¿Esta seguro? Latency: %1 ms (%2 frames) Latencia: %1 ms (%2 cuadros) (no latency) (sin latencia) &None &Nada qtractorPluginListView copy plugin copiar plugin activate all plugins activar todos los plugins deactivate all plugins desactivar todos los plugins remove all plugins quitar todos los plugins Import Plugins Importar Plugins XML files (*.%1) archivos XML (*.%1) All files (*.*) Todos los archivos (*.*) Warning Advertencia About to remove and import all plugins: "%1" Are you sure? Esta a punto de quitar e importar todos los plugins: "%1" ¿Está seguro? Export Plugins Exportar Plugins Aux Send: &Move Here &Mover Aquí &Copy Here &Copiar Aquí C&ancel C&ancelar &Add Plugin... &Añadir Plugin... I&nserts I&nserciones &Audio &Audio Add &Insert Añadir &Inserción Add &Aux Send Añadir Envío &Auxiliar &Sends &Envíos &Returns &Retornos &MIDI &MIDI Add &Controller Ac&tivate Ac&tivar Acti&vate All Acti&var todo Deactivate Al&l Desactivar &Todo &Remove &Quitar Re&move All Qui&tar Todo Move &Up Mo&ver Hacía Arriba Move &Down Mover &Hacía Abajo Pre&set Pree&stablecido Dire&ct Access A&cceso Directo &None &Nada &Properties... &Propiedades... &Edit &Edición &Import... &Importar... E&xport... E&xportar... &Outputs &Salidas &Dedicated &Dedicado &Auto-connect &Auto-conectar qtractorPluginParamWidget Open File Abrir Archivo qtractorPluginSelectForm Plugins Plugins Reset filter Reiniciar filtro X X Plugin search string (regular expression) Cadena de busqueda de plugin (expresión regular) Plugin type Tipo de Plugin Available plugins PLugins disponibles Name Nombre Audio Audio MIDI MIDI Control Control Modes Modos Path Ruta Index índice Instances Instancias Type Tipo Plugin scanning in progress... Exploración de plugins en progreso... Rescan for available plugins (refresh) Reescanear plugins disponibles (refrescar) &Rescan &Reescanear GUI GUI EXT EXT RT RT qtractorSessionForm Session Sesión &Name: &Nombre: Session name Nombre de sesión &Directory: &Directorio: Whether to auto-name the session directory Si se debe auto-nombrar el directorio de la sesión &Auto &Automático Session directory Directorio de sesión Browse for session directory Buscar directorio de sesión ... ... &Description: &Descripción: Session description Descripción de sesión Properties Propiedades Time Tiempo Sample &Rate: &Frecuencia de muestreo: Sample rate (Hz) Frecuencia de muestreo (Hz) 44100 44100 48000 48000 96000 96000 192000 192000 &Tempo: &Tempo: Tempo (BPM) / Signature Tempo (PPM) / Metrica T&icks/Beat: T&icks por pulso: Resolution (ticks/beat; tpqn) Resolución (ticks/pulso; tpqn) View Visualización &Snap/Beat: &Snap/pulso: Snap/beat Snap/pulso &Pixels/Beat: &Píxeles/Pulso: Pixels/beat Píxeles/Pulso &Horizontal Zoom: Zoom &Horizontal: Horizontal Zoom (%) Zoom Horizontal (%) % % &Vertical Zoom: Zoom &Vertical: Vertical Zoom (%) Zoom Vertical (%) Warning Advertencia Session directory does not exist: "%1" Do you want to create it? El directorio de sesión no existe: "%1" ¿Desea crearlo? Some settings have been changed. Do you want to apply the changes? Algunas configuraciones han cambiado. ¿Desea aplicar los cambios? Session Directory Directorio de Sesión qtractorShortcutForm Shortcuts Atajos Shortcut search string (regular expression) Menu/Action Menú/Acción Description Descripción Keyboard Teclado MIDI Controller Controlador MIDI Search shortcuts Warning Advertencia Keyboard shortcut (%1) already assigned (%2). Atajo de teclado (%1) ya asignado (%2). Keyboard shortcuts have been changed. Do you want to apply the changes? Los atajos de teclado han cambiado. ¿Desea aplicar los cambios? MIDI Controller shortcuts have been changed. Do you want to apply the changes? Los atajos del Controlador MIDI han cambiado. ¿Desea aplicar los cambios? &MIDI Controller... Controlador &MIDI... qtractorTakeRangeForm Take Range Rango de Toma Range Rango En&d: &Fin: Selection range Rango de selección &Selection &Selección Edit range Rango de edición &Edit &Editar Custom range Rango Personalizado &Custom &Personalizado Loop range Rango de bucle &Loop &Bucle Clip start Inicio del clip Clip offset Offset del clip St&art: &Inicio: Punch range Rango de pinchazo &Punch &Pinchazo Select Seleccionar Current take Toma actual Format Formato Time display format Formato para desplegar tiempo Frames Cuadros Time Tiempo BBT BBT Take %1 Toma %1 qtractorTempoAdjustForm Metronome Metrónomo Tempo/Time signature Tempo/Compás T&ap T&ap Range Rango &Length: &Duración: Range beats Rango de pulsos A&djust A&justar &Start: &Inicio: Tempo Adjust Ajustar Tempo Range length Rango de duración Range start Rango de inicio &Tempo: &Tempo: &Detect &Detectar R&eset &Beats: &Beats: Format Formato Time display format Formato para desplegar tiempo Frames Cuadros Time Tiempo BBT BBT Warning Advertencia Some settings have been changed. Do you want to apply the changes? Algunas configuraciones han cambiado. ¿Desea aplicar los cambios? qtractorThumbView Thumb view Vista previa qtractorTimeScale C Do B# Si# C# Do# Db Reb D Re D# Re# Eb Mib E Mi Fb Fab F Fa E# Mi# F# Fa# Gb Solb G Sol G# Sol# Ab Lab A La A# La# Bb Sib B Si Cb Dob qtractorTimeScaleForm Tempo Map / Markers Mapa de tempo / Marcadores Tempo map / Markers Mapa de tempo / Marcadores Bar Compás Time Tiempo Tempo Tempo Key Clave Marker Marcador &Bar: &Compás: Bar location Ubicación del compás T&ime: T&iempo: Time/frame location Ubicación del tiempo/cuadro &Tempo: &Tempo: Tempo (BPM) / Time signature Tempo (PPM) / Compás T&ap T&ap &Key signature: &Armadura: Key signature (accidentals) Armadura (alteraciones) Key signature (mode) Armadura (modo) - - Major Mayor Minor Menor &Marker: &Marcador: Marker text Texto del marcador Marker color Color del marcador ... ... Tempo &scale factor: Factor de e&scalado de Tempo: Tempo scale factor Factor de escalado de Tempo App&ly Ap&licar Refresh tempo map Refrescar mapa de tempo Re&fresh Re&frescar Add node Añadir nodo &Add &Añadir Update node Actualizar nodo &Update &Actualizar Remove node Remover nodo &Remove &Remover Close this dialog Cerrar este diálogo Close Cerrar Warning Advertencia Some settings have been changed. Do you want to apply the changes? Algunas configuraciones han cambiado. ¿Desea aplicar los cambios? About to remove tempo node: %1 (%2) %3 %4/%5 Are you sure? A punto de eliminar nodo de tempo: %1 (%2) %3 %4/%5 ¿Esta seguro? Some settings have been changed. Do you want to discard the changes? Algunas configuraciones han cambiado. ¿Desea descartar los cambios? tempo factor factor de tempo Marker Color Color del Marcador &Refresh A&ctualizar qtractorTimeSpinBox &Frames &Cuadros &Time &Tiempo &BBT &BBT qtractorTrackForm Track Pista &Name: &Nombre: Track name description Descricpción del nombre de la pista Track icon Icono de pista Type Tipo Audio track type PIsta de tipo Audio &Audio &Audio MIDI track type Pista de tipo MIDI &MIDI &MIDI Input / Output Entrada / Salida Input bus name Nombre de bus de entrada Output bus name Nombre de bus de salida Manage buses Administrar buses ... ... MIDI / Instrument MIDI / Instrumento &Program: &Programa: &Bank: &Banco: Bank &Select Method: Método de &Selección de Banco: &Omni &Omni MIDI Omni: Capture All Channels MIDI Omni: Captura en Todos los Canales &Channel: &Canal: MIDI Channel (1-16) Canal MIDI (1-16) MIDI Patch: Instrument Patch MIDI: Instrumento MIDI Patch: Bank Select Method Patch MIDI: Método de Selección de Banco MIDI Patch: Drum Mode Patch MIDI: Batería MIDI Patch: Bank Patch MIDI: Banco MIDI Patch: Program Patch MIDI: Programa View / Colors Vista / Colores &Foreground: &Destacado: Foreground color Color destacado Select custom track foreground color Seleccionar color personalizado destacado para pista Bac&kground: &Fondo: Background color Color de fondo Select custom track background color Seleccionar color personalizado de fondo para pista Auto Plugins Plugins Track plugins Plugins de pista Add plugin Añadir plugin &Add... &Añadir... Remove plugin Quitar plugin &Remove &Quitar Move plugin up Mover plugin hacía arriba &Up &Arriba Move plugin down Mover plugin hacía abajo &Down A&bajo Whether to enable plugin latency/delay compensation Si se habilita el plugin de compensación de latencia/retraso &Latency compensation Compensación de &latencia Current total latency Normal Normal Bank MSB Banco MSB Bank LSB Banco LSB Patch Patch &Drums &Tambores Drum &Kit &Batería &Bass &Bajo A&coustic Bass Bajo A&cústico &Guitar &Guitarra &Electric Guitar Guitarra &Eléctrica &Piano &Piano &Acoustic Piano Piano &Acústico &Microphone &Micrófono Vi&ntage Microphone Micrófono Antig&uo &Speaker &Altavoz &Trumpet &Trompeta &Violin &Violín Warning Advertencia Some settings have been changed. Do you want to apply the changes? Algunas configuraciones han cambiado. ¿Desea aplicar los cambios? (No instrument) (No hay instrumento) %1 ms (%2 frames) (no latency) (sin latencia) (None) (Ninguno) Custom &Icon... &Icono personalizado... Image files (%1) Archivos de imagen (%1) All files (*.*) Todos los archivos (*.*) Track Icon Icono de pista Foreground Color Color destacado Background Color Color de Fondo qtractorTrackList Nr Nr Track Name Nombre de Pista Bus Bus Ch Canal Patch Instrument Instrumento qtractorTrackTime Play-head Cabezal de reproducción Edit-head Cabezal de edición Edit-tail Editar cola Loop-start Inicio de bucle Loop-end Fin del bucle Punch-in Punch-in Punch-out Punch-out Start: %1 End: %2 Length: %3 Inicio: %1 Fin: %2 Duración: %3 qtractorTrackView Zoom in (horizontal) Acercar zoom (horizontal) Zoom out (horizontal) Alejar zoom (horizontal) Zoom in (vertical) Acercar zoom (vertical) Zoom out (vertical) Alejar zoom (vertical) Zoom reset Reiniciar zoom add clip añadir clip Start: %1 End: %2 Length: %3 Inicio: %1 Fin: %2 Duración: %3 clip %1 clip %1 fade-in fade-in fade-out fade-out clip stretch estirar clip clip resize redimensionar clip clip repeat repetir clip %1 automation %1 automatización cut cortar delete borrar %1 clip %1 clip split separar move clip mover clip paste clip pegar clip move automation mover automatización paste automation pegar automatización qtractorTracks Tracks Pistas new clip nuevo clip mute clip split clip separar clip clip normalize normalizar clip quantize cuantizar transpose transponer normalize normalizar randomize aleatorio resize redimensionar rescale reescalar timeshift desplazamiento de tiempo clip import importar clip Audio file import "%1" on %2 %3. Importar archivo de audio "%1" en %2 %3. Audio file import: "%1". Importar archivo de audio: "%1". MIDI file import "%1" track-channel %2 on %3 %4. Importar archivo MIDI "%1" pista/canal %2 en %3 %4. MIDI file import: "%1", track-channel: %2. Importar archivo MIDI: "%1", pista/canal %2. clip merge fundir clip Merge/Export Fundir/Exportar Merge/Export Audio Clip Fundir/Exportar Clip de Audio MIDI files (*.mid *.smf *.midi) Archivos MIDI (*.mid *.smf *.midi) All files (*.*) Todos los archivos (*.*) Audio clip merge/export: "%1" started... Fundir/exportar clip de Audio : "%1" iniciado... tempo ramp Audio clip merge/export: "%1" complete. Fundir/exportar clip de Audio : "%1" completado. Merge/Export MIDI Clip Fundir/Exportar Clip MIDI MIDI clip merge/export: "%1" started... Fundir/exportar clip MIDI: "%1" iniciado... MIDI clip merge/export: "%1" complete. Fundir/exportar clip MIDI: "%1" completado. clip cross-fade crossfade de clip Insert Range Insertar Rango insert range insertar rango insert track range Insert Rango de Pista Remove Range Quitar Rango remove range borrar rango remove track range Remover rango de pista Warning Advertencia About to remove track: "%1" Are you sure? A punto de eliminar pista: "%1" ¿Está seguro? MIDI file import "%1" on %2 %3. Importar archivo MIDI "%1" en %2 %3. MIDI file import: "%1". Importar archivo MIDI: "%1". qtractor-1.5.9/src/translations/PaxHeaders/qtractor_it.ts0000644000000000000000000000013215101070305020646 xustar0030 mtime=1761898693.101267699 30 atime=1761898693.099267692 30 ctime=1761898693.101267699 qtractor-1.5.9/src/translations/qtractor_it.ts0000644000175000001440000227535715101070305020663 0ustar00rncbcusers QObject Audio: %1 channels, %2 Hz Audio: %1 canali, %2 Hz (%1 dB) (%1 dB) (%1 pan) (%1% time stretch) (%1% allarga tempo) (%1 semitones pitch shift) (%1 semitoni trasposti) %1 In %1 Ingressi %1 Out %1 Uscite Audio files (%1) File audio (%1) All files (*.*) Tutti i file (*.*) %1 (%2) %3 channels, %4 frames, %5 Hz %6 %1 (%2) %3 canali, %4 campioni, %5 Hz %6 Duplex Duplex Output Uscita Input Ingresso None Nessuno (take %1/%2) (ripresa %1/%2) [Mute] Name: %1 Nome: %1 Start: %1 Offset: %2 End: %3 Length: %4 Inizio: %1 Offset: %2 Fine: %3 Lunghezza: %4 File: %1 File: %1 take %1 ripresa %1 reset takes reset riprese clip save clip unlink clip tool %1 strumento clip %1 clip record registrazione clip automation select selezione automazione automation mode modalità automazione automation play riproduzione automazione automation record registra automazione automation logarithmic automazione logaritmica automation color colore automazione automation play all riproduci tutte le automazioni automation record all registra tutte le automazioni automation edit modifica automazione automation clear pulisci automazione automation clear all pulisci tutte le automazioni automation edit list lista modifiche automazioni %1 Monitor %1 Monitor create bus crea bus update bus aggiorna bus delete bus rimuovi bus move bus sposta bus bus pass-through pass-through bus bus gain guadagno bus bus pan bilanciamento bus Insert Send/Return pseudo-plugin (Audio) Inserisci una pseudo plugin di Send/Return (Audio) Insert Send/Return pseudo-plugin (MIDI) Inserisci una pseudo plugin di Send/Return (MIDI) Send Gain Mandata Guadagno Dry Gain Guadagno di dry Wet Gain Guadagno di wet Aux Send (Audio) Aux Send (Audio) Aux Send pseudo-plugin (Audio) Aux Send pseudo-plugin (Audio) Aux Send pseudo-plugin (MIDI) Aux Send pseudo-plugin (MIDI) (none) (nessuno) %1 (Audio) %1 (Audio) %1 (MIDI) %1 (MIDI) Cakewalk Instrument Definition File File di definizione strumento Cakewalk File File Date Data %1 Bank %2 %1 Banco %2 %1 - Bank %2 %1 - Banco %2 (format %1) MIDI: (formato %1) MIDI: Channel %1 Canale %1 Track %1 Traccia %1 , %1 tracks, %2 tpqn , %1 tracce, %2 tpqn (%1% vol) (%1% vol) MIDI file save: "%1", track-channel: %2. Salvataggio file MIDI: "%1", traccia-canale: %2. set controller imposta controller reset controller resetta controller %1 (format %2) %3 tracks, %4 tpqn %5 %1 (formato %2) %3 tracce, %4 tpqn %5 %1 (format %2) %3 %1 (formato %2) %3 (default) (predefinito) %1 Hz %1 Hz slave slave %1 (%2) %1 (%2) Usage: %1 [options] [session-file] Utilizzo: %1 [opzioni] [file di sessione] Options: Opzioni: Set session identification (uuid) Imposta identificativo sessione (uuid) Show help about command line options Mostra opzioni della riga di comando Show version information Mostra informazioni di versione Session file (.qtr) [session-file] Option -s requires an argument (uuid). Signed 16-Bit 16-Bit con segno Signed 24-Bit 24-Bit con segno Signed 32-Bit 32-Bit con segno Float 32-Bit 32-Bit Float Float 64-Bit 64-Bit Float SMF Format 0 Formato 0 SMF SMF Format 1 Formato 1 SMF (Any) (Qualsiasi) Activate Attiva Aux Send: %1 %1(%2): %3 plugin not found. %1(%2): %3 plugin non trovata. add plugin aggiungi plugin add insert aggiungi insert add aux-send aggiungi send-aux add MIDI controller aux-send bus bus send-aux aux-send matrix remove plugin rimuovi plugin move plugin sposta plugin activate plugin attiva plugin preset plugin plugin di preset reset plugin resetta plugin plugin program plugin di programma plugin alias dedicated audio outputs uscite audio dedicate direct access param parametro di accesso diretto import plugins session loop loop sessione session punch punch sessione session properties proprietà sessione Beat Battito add tempo node aggiungi nodo tempo update tempo node aggiorna nodo tempo remove tempo node rimuovi nodo tempo move tempo node sposta nodo tempo add marker aggiungi marker update marker aggiorna marker remove marker elimina marker add key signature update key signature remove key signature move marker sposta marker change time-sig. step input overdub %1 Volume %1 Volume %1 Gain %1 Guadagno %1 Pan %1 Bilanciamento add track aggiungi traccia remove track rimuovi traccia duplicate track duplica traccia move track sposta traccia resize track ridimensiona traccia import track importa traccia track properties proprietà traccia Track assignment failed: Track: "%1" Input: "%2" Output: "%3" Assegnazione traccia fallita: Traccia: "%1" Ingresso: "%2" Uscita: "%3" track record registra traccia track mute silenzia traccia track solo traccia in solo track monitor monitor traccia track gain guadagno traccia track pan bilanciamento traccia track instrument strumento della traccia Automation (%1) Automazione (%1) none nessuno Automation Automazione Unknown Sconosciuto Product: Prodotto: Vendor: Produttore: Manual: Support: Version: Versione: %1 (*.%2) %1 (*.%2) Copyright: Copyright: Project: Progetto: Select plug-in's editor (GUI): Selezione editor (GUI) per la plugin: External Esterna X11 X11 X11 (native) Gtk2 Gtk2 Gtk2 (native) Qt4 Qt4 Qt5 Qt5 Other Altra Don't ask this again Non chiederlo di nuovo plugin parameters parametri della plugin Open File lv2_ui_request_parameter Apri file Author: Autore: %1: Automation/curve file not found. %1: File di automazione/curva non trovato. Name: Category: Categories: %1 Record %1 Mute %1 Solo MIDI Controller: %1, %2, %3 Control (MIDI) MIDI Controller Send pseudo-plugin Value Valore qtractorAudioIOMatrixForm Aux-Send I/O Matrix Warning Attenzione Some settings have been changed. Do you want to apply the changes? Alcune impostazioni sono state modificate. Vuoi applicare le modifiche? qtractorAudioListView Name Nome Ch Canale Frames Campioni Rate Campionamento Time Tempo Path Percorso Open Audio Files Apri File Audio %1: Audio file not found. %1: File audio non trovato. qtractorAudioMixerMeter Gain (dB) Guadagno (dB) dB dB Pan: %1 Gain: %1 dB Guadagno: %1 dB qtractorBusForm Bus list Lista bus Buses Bus Ch Canali Mode Modalità Bus Bus Properties Proprietà &Name: &Nome: Bus name Nome bus &Mode: &Modalità: Bus mode Modalità bus Input Ingresso Output Uscita Duplex Duplex Bus monitor (pass-through) Monitor bus (pass-through) M&onitor (pass-through) M&onitor (pass-through) Audio Audio Cha&nnels: Ca&nali: Audio channels Canali audio Audio auto-connect Connessione automatica audio &Auto connect Connessione &Automatica MIDI MIDI MIDI Instrument name Nome strumento MIDI MIDI SysEx setup Configurazione SysEx MIDI SysE&x... SysE&x... Input Plugins Plugin di Ingresso Input bus plugins Plugin bus di ingresso Add input plugin Aggiungi plugin in ingresso &Add... &Aggiungi... Remove input plugin Rimuovi plugin di ingresso &Remove &Rimuovi Move input plugin up Sposta in su plugin ingresso &Up S&U Move input plugin down Sposta in giù plugin di ingresso &Down &Giù Output Plugins Plugin di Uscita Output bus plugins Plugin bus di uscita Add output plugin Aggiungi plugin di uscita Remove output plugin Rimuovi plugin di uscita Move output plugin up Sposta in su plugin di output Move output plugin down Sposta in giù plugin di output Move bus up towards the top Sposta il bus in su verso la cima U&p Su Move bus down towards the bottom Sposta il bus in giù verso il fondo Do&wn Giù Create bus Crea bus &Create &Crea Update bus Aggiorna bus &Update &Aggiorna Delete bus Rimuovi bus &Delete &Rimuovi Close this dialog Chiudi questa finestra Close Chiudi Warning Attenzione Some settings have been changed. Do you want to apply the changes? Alcune impostazioni sono state modificate. Vuoi applicare le modifiche? About to remove bus: "%1" (%2) Are you sure? Sto per rimuovere il bus: "%1" (%2) Sei sicuro? Some settings have been changed. Do you want to discard the changes? Alcune impostazioni sono state modificate. Vuoi annullare le modifiche? Move &Up Sposta S&u Move &Down Sposta Giù (No instrument) (Nessuno strumento) (none) (nessuno) (1 item) (1 elemento) (%1 items) (%1 elementi) qtractorClientListView Readable Clients / Output Ports Client Leggibili / Porte di Uscita Writable Clients / Input Ports Client Scrivibili / Porte di Ingresso qtractorClipForm &Name: &Nome: Clip name Nome clip &File: &File: Clip filename Nome del clip Browse for clip file Sfoglia file di clip Track/&Channel: Clip track/channel Traccia clip/canale Clip gain/volume Guadagno clip/volume Parameters Parametri Clip start Inizio clip Clip offset Posizione clip &Panning: Clip length Lunghezza clip Offs&et: Posizion&e: &Length: &Lunghezza: &Start: &Inizio: Clip Clip Forma&t: Time display format Formato di visualizzazione del tempo Frames Campioni Time Tempo BBT BBT Fade In/Out Fade In/Out Fade &In: Fade &In: Clip fade-in length Fade-in clip Clip fade-in type Tipo di fade-in clip Fade &Out: Fade &Out: Clip fade-out length Fade-out clip Clip fade-out type Tipo di fade-out clip Audio Audio Ti&me Stretch: Clip time-stretch percentage Percentuale di allrgamento tempo del clip % % Pitch S&hift: Pitch S&hift: Clip pitch-shift in semitones Pitch shift del clip in semitoni semitones semitoni Whether to use WSOLA time-stretching Usa adattamento tempo WSOLA &WSOLA time-stretching Adattamento tempo &WSOLA Whether to use RubberBand formant preserve RubberBand &formant preserve Whether to apply WSOLA quick seek time-stretching Applica ricerca veloce adattamento tempo WSOLA WSOLA quic&k seek Ricerca veloce WSOLA Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine &Mute &Mute &Gain: &Guadagno: Linear Lineare Quadratic 1 Quadratic 2 Quadratic 3 Cubic 1 Cubic 2 Cubic 3 dB dB &Volume: &Volume: new clip nuovo clip edit clip modifica clip Warning Attenzione Some settings have been changed. Do you want to apply the changes? Alcune impostazioni sono state modificate. Vuoi applicare le modifiche? MIDI MIDI MIDI files (*.%1 *.smf *.midi) File MIDI (*.%1 *.smf *.midi) All files (*.*) Tutti i file (*.*) %1 Clip File %1 File di Clip qtractorConnect Connect Connetti Disconnect Disconnetti Disconnect All Disconnetti Tutti Refresh Aggiorna qtractorConnectForm Connections Connessioni Audio Audio Select output client/ports Seleziona client/porta di uscita Select input client/ports Seleziona client/porta di ingresso Connect currently selected ports Connetti porte attualmente selezionate &Connect &Connetti Disconnect currently selected ports Disconnetti porte attualmente selezionate &Disconnect &Disconnetti Disconnect all currently connected ports Disconnetti tutte le porte attualmente connesse Disconnect &All Disconnetti &Tutto Refresh current connections view Aggiorna vista corrente delle connessioni &Refresh Aggio&rna MIDI MIDI (All) (Tutto) qtractorConnections Connections Connessioni qtractorEditRangeForm Range Intervallo Selection range Intervallo della selezione &Selection &Selezione Loop range Intervallo del loop &Loop &Loop Punch range Intervallo di punch &Punch &Punch Edit range Intervallo di modifica &Edit &Modifica Custom range Intervallo personalizzato &Custom &Personalizzato St&art: &Inizio: Clip start Inizio del clip En&d: &Fine: Clip offset Offset del clip Apply to Loop points in range Applica a punti di loop nell'intervallo L&oop L&oop Apply to Punch In/Out points in range Applica a punti di punch in/out nell'intervallo Pu&nch Pu&nch Mar&kers Mar&ker Te&mpo Map Mappa Te&mpo &Format &Formato Time display format Formato di visualizzazione del tempo Time Tempo BBT BBT Frames Frame Edit Range Options Opzioni Apply to clips in range Applica ai clip nell'intervallo Cl&ips Cl&ip A&utomation A&utomazione Apply to Automation nodes in range Applica a nodi di automazione nell'intervallo Apply to Tempo Map nodes in range Applica ai nodi della mappa del tempo nell'intervallo Apply to location Markers in range Applica ai marker di posizione nell'intervallo qtractorExportClipForm %1 %2 Clips qtractorExportForm Export &File: &File: Export file name Esporta nome file Browse export file name Sfoglia esporta nome file File &type: &Tipo file: Audio file type to use on export Sample &format: &Formato campionamento: Audio sample format to use on export &Quality: &Qualità: Audio compression quality to use on export File &format: &Formato file: MIDI file format to use on export Range Intervallo Session range Intervallo sessione &Session &Sessione Loop range Intervallo di loop &Loop &Loop Punch range Intervallo di punch &Punch &Punch Edit range Modifica intervallo &Edit &Modifica Custom range Intervallo personalizzato &Custom &Personalizzato St&art: &Inizio: Custom start Custom end En&d: &Fine: Outputs Uscite Output bus names Nomi dei bus di uscita Format Formato Time display format Formato di visualizzazione del tempo Frames Campioni Time Tempo BBT BBT Whether to add/import new track(s) with export result Se aggiungere/importare nuove tracce con i risultati di esportazione &Add new track(s) &Aggiungi una nuova traccia Audio Audio MIDI MIDI Export %1 File Esporta %1 File MIDI files (*.%1 *.smf *.midi) File MIDI (*.%1 *.smf *.midi) All files (*.*) Tutti i file (*.*) qtractorExportTrackForm %1 %2 Tracks Warning Attenzione The file already exists: "%1" Do you want to replace it? Il file esiste già: "%1" Vuoi sostituirlo? Audio file export: "%1" started... Esportazione file audio: "%1" iniziata... Audio file export: "%1" complete. Esportazione file audio: "%1" completata. Audio file export: "%1" failed. Esportazione file audio: "%1" fallita. MIDI file export: "%1" started... Esportazione file MIDI: "%1" iniziata... MIDI file export: "%1" complete. Esportazione file MIDI: "%1" completata. MIDI file export: "%1" failed. Esportazione file MIDI: "%1" fallita. qtractorFileListView New Group Nuovo Gruppo Warning Attenzione About to remove %1 file item(s). Are you sure? Si sta per rimuovere %1 file. Sei sicuro? About to remove %1 item: "%2" Are you sure? Si sta per rimuovere %1 elemento: "%2" Sei sicuro? group gruppo file file qtractorFileSystem &Home &Up Al&l Files &Session &Sessione &Audio &Audio &MIDI &MIDI H&idden &Play File System qtractorFiles Audio Audio MIDI MIDI Play file Riproduci file Add &Files... Aggiungi &File... Cu&t &Taglia &Copy &Copia Ctrl+X Ctrl+X Ctrl+C Ctrl+C Ctrl+V Ctrl+V New &Group... Nuovo &gruppo... &Paste &Incolla Re&name Ri&nomina &Remove &Elimina Pla&y Ri&produci Cl&eanup &Pulizia Del Canc Files File MIDI Files File MIDI Audio Files File Audio qtractorInstrumentForm Instruments Strumenti Files File Path Percorso Names Nomi Import from instrument file Importa da file strumenti &Import... &Importa... Remove instrument file Elimina file strumenti &Remove &Elimina Move instrument file up on list order Sposta file strumenti più su nella lista &Up S&u Move instrument file down on list order Sposta file strumenti più giù nella lista &Down &Giù Export to instrument file Esporta su file strumenti E&xport... &Esporta... Close this dialog Chiudi questa finestra Close Chiudi Import Instrument Files Importa file strumenti Instrument files (*.%1 *.sf2 *.sf3 *.midnam) File strumenti (*.%1 *.sf2 *.sf3 *.midnam) All files (*.*) Tutti i file (*.*) Export Instrument File Esporta file strumenti Instrument files (*.%1) File strumenti (*.%1) Warning Attenzione The instrument file already exists: "%1" Do you want to replace it? Il file degli strumenti esiste già: "%1" Vuoi sostituirlo? Instrument settings have been changed. Do you want to apply the changes? I parametri degli strumenti sono stati modificati. Vuoi applicare le modifiche? Patch Names for Banks Nome della patch per i banchi Controller Names = %1 Nomi dei controller = %1 RPN Names = %1 Nomi degli RPN = %1 NRPN Names = %1 Nomi degli NRPN = %1 Bank Select Method = %1 Metodo di selezione del banco = %1 Patch Names Nomi Patch Note Names Nomi Note Controller Names Nomi Controller RPN Names Nomi RPN NRPN Names Nomi NRPN Bank Select Methods Metodi di selezione del banco %1 = %2 %1 = %2 Based On = %1 Basato su = %1 Normal Normale Bank MSB MSB Banco Bank LSB LSB Banco Patch Patch Unknown Sconosciuto qtractorInstrumentMenu (None) (nessuno) qtractorMainForm &File &File Open &Recent Apri &Recente &Edit &Modifica Select &Mode Seleziona &Modalità &Select &Seleziona I&nsert I&nsert Remo&ve &Rimuovi &Track &Traccia &State &Stato &Navigate &Navigazione Mo&ve S&posta &Height &Altezza Impor&t Tracks Impor&ta Tracce E&xport Tracks &Esporta Tracce M&ode M&odalità A&utomation A&utomazione Instrum&ent Strum&ento T&ools &Strumenti Ta&ke &Ripresa &View &Vista &Toolbars Barre S&trumenti &Windows &Finestre &Zoom &Zoom S&nap Alli&neamento T&ransport T&rasporto Mo&de St&ep &Help Ai&uto &New &Nuovo New Nuovo New session Nuiova sessione New session file Nuovo file sessione Ctrl+N Ctrl+N &Open... &Apri... Open Apri Open session Apri sessione Open session from file Apri file sessione Ctrl+O Ctrl+O &Save &Salva Save Salva Save session Salva sessione Save session to file Salva sessione su file Ctrl+S Ctrl+S Save &As... S&alva Come... Save As Salva Come Save as Salva come Save current session with another file name Salva sessione corrente con un altro nome file &Properties... &Proprietà... Session Properties Proprietà Sessione Session properties Proprietà sessione Edit current session properties Modifica proprietà sessione corrente F2 F2 E&xit E&sci Exit Esci Exit this application program Esci da questa applicazione &Undo Ann&ulla Undo Annulla Undo last action Annulla l'ultima azione Ctrl+Z Ctrl+Z &Redo &Ripristina Redo Ripristina Redo last action Ripristina l'ultima azione Ctrl+Shift+Z Ctrl+Shift+Z Cu&t &Taglia Cut Taglia Cut selection to clipboard Taglia la selezione nella clipboard Ctrl+X Ctrl+X &Copy &Copia Copy Copia Copy selection to clipboard Copia la selezione nella clipboard Ctrl+C Ctrl+C &Paste &Incolla Paste Incolla Paste clipboard contents Incolla il contenuto della clipboard Ctrl+V Ctrl+V Past&e Repeat... Rip&eti incolla... Paste Repeat Ripeti Incolla Paste repeat Ripeti incolla Paste/repeat clipboard contents Incolla nuovamente il contenuto della clipboard Ctrl+Shift+V Ctrl+Shift+V &Delete Elimina Delete Elimina Delete selection Elimina la selezione Del Canc &Clip &Clip Clip Clip Select clip Seleziona clip Clip selection mode Modalità selezione clip &Range Inte&rvallo Range Intervallo Select range Seleziona intervallo Range selection mode Modalità selezione intervallo R&ectangle R&ettangolo Rect Rettangolo Select rectangle Seleziona rettangolo Rectangular selection mode Modalità selezione rettangolo &Automation &Automazione Automation Automazione Automation edit mode Modalità modifica automazione &All Tutto Select All Seleziona Tutto Select all Seleziona Tutto Mark all as selected Contrassegna tutto come selezionato Ctrl+A Ctrl+A &None &Nessuno Select None Seleziona nessuno Select none Seleziona nessuno Mark all as unselected Contrassegna tutto come non selezionato Ctrl+Shift+A Ctrl+Shift+A &Invert &Inverti Select Invert Inverti Selezione Select invert Inverti selezione Invert selection Inverti selezione Ctrl+I Ctrl+I Select Track Seleziona Traccia Select track Seleziona traccia Mark track as selected Contrassegna traccia come selezionata Ctrl+T Ctrl+T Trac&k Range Intervallo Tra&ccia Select Track Range Seleziona Intervallo Traccia Select track range Seleziona intervallo traccia Mark track range as selected Contrassegna intervallo traccia come selezionato Ctrl+Shift+R Ctrl+Shift+R Select Range Seleziona Intervallo Mark range as selected Contrassegna intervallo come selezionato Ctrl+R Ctrl+R &Range... &Intervallo... Remove Range Elimina intervallo Remove range Elimina intervallo Remove range as selected Elimina intervallo come selezionato Ctrl+Del Ctrl+Canc Remove Track Range Elimina intervallo traccia Remove track range Elimina intervallo traccia Remove track range as selected Elimina intervallo della traccia come selezionato Ctrl+Shift+Del Ctrl+Shift+Canc Insert Range Inserisci intervallo Insert range Inserisci intervallo Insert range as selected Inserisci intervallo come selezionato Ctrl+Ins Ctrl+Ins Insert Track Range Inserisci intervallo traccia Insert track range Inserisci intervallo traccia Insert track range as selected Inserisci intervallo traccia come selezionato Ctrl+Shift+Ins Ctrl+Shift+Ins Sp&lit Di&vidi Split Selection Dividi la selezione Split selection Dividi la selezione Split current selection Dividi la selezione corrente Ctrl+Y Ctrl+Y &Add Track... &Aggiungi Traccia... Add Track Aggiungi Traccia Add track Aggiungi traccia Add a new track to session Aggiunge una nuova traccia alla sessione Shift+Ins Shift+Ins &Remove Track &Rimuovi Traccia Remove Track Rimuovi Traccia Remove track Rimuovi traccia Remove current track from session Rimuovi traccia corrente dalla sessione Shift+Del Shift+Canc &Duplicate Track &Duplica Traccia Duplicate Track Duplica Traccia Duplicate track Duplica traccia Duplicate current track Duplica la traccia corrente Track &Properties... &Proprietà Traccia... Track Properties Proprietà Traccia Track properties Proprietà traccia Edit current track properties Modifica le proprietà della traccia corrente Shift+F2 Shift+F2 &Inputs &Ingressi Track Inputs Ingressi Traccia Track inputs Ingressi traccia Show current track input bus connections Mostra connessioni bus di ingresso della traccia corrente &Outputs Uscite Track Outputs Uscite Traccia Track outputs Uscite traccia Show current track output bus connections Mostra connessioni bus di uscita della traccia corrente &Record &Registra Record Track Registra Traccia Record track Registra traccia Arm current track for recording Imposta la traccia corrente per registrare &Mute &Mute Mute Track Silenzia Traccia Mute track Silenzia traccia Mute current track Silenzia la traccia corrente &Solo &Solo Solo Track Traccia in solo Solo track Traccia in solo Solo current track Attiva solo la traccia corrente M&onitor M&onitor Monitor Track Monitorizza Traccia Monitor track Monitorizza traccia Monitor current track Monitorizza la traccia corrente &First Prima First Track Prima Traccia First track Prima traccia Make current the first track Sposta la traccia corrente come prima traccia &Previous &Precedente Previous Track Traccia Precedente Previous track Traccia precedente Make current the previous track Sposta traccia corrente prima della precedente &Next Successiva Next Track Traccia Successiva Next track Traccia successiva Make current the next track Sposta traccia corrente dopo la successiva &Last U&ltima Last Track Ultima Traccia Last track Ultima traccia Make current the last track Sposta la traccia corrente come ultima N&one Nessuna None Track Nessuna Traccia None track Nessuna traccia None current track Nessuna traccia corrente &Top Cima Move Top Sposta in cima Move top Sposta in cima Move current track to top Sposta la traccia corrente in cima &Up S&u Move Up Sposta Su Move up Sposta su Move current track up Sposta su la traccia corrente &Down Giù Move Down Sposta Giù Move down Sposta giù Move current track down Sposta giù la traccia corrente &Bottom Fondo Move Bottom Sposta in fondo Move bottom Sposta in fondo Move current track to bottom Sposta in fondo la traccia corrente &Increase Aumenta Increase Height Aumenta Altezza Increase height Aumenta altezza Increase track height Aumenta altezza traccia Ctrl+Shift++ Ctrl+Shift++ &Decrease &Diminuisci Decrease Height Diminuisci Altezza Decrease height Diminuisci altezza Decrease track height Diminuisci altezza traccia Ctrl+Shift+- Ctrl+Shift+- &Minimize Minimize Height Minimize height Minimize track height &Reset &Resetta Height Reset Resetta Altezza Height reset Resetta altezza Reset track height Resetta altezza traccia Ctrl+Shift+1 Ctrl+Shift+1 Auto &Monitor &Monitor Automatico Auto Monitor Monitor Automatico Auto monitor Monitor automatico Auto-monitor current track Monitor automatico della traccia corrente F6 F6 Auto Dea&ctivate Auto Deactivate Auto-deactivate plugins Auto-deactivate plugins not producing sound Shift+F6 Shift+F6 &Audio... &Audio... Inport Audio File Importa File Audio Import Audio file Importa file audio Import tracks from Audio file Importa tracce da file audio &MIDI... &MIDI... Import MIDI File Importa File MIDI Import MIDI file Importa file MIDI Import tracks from MIDI file Importa tracce da file MIDI Export Audio File Esporta File Audio Export Audio file Esporta file audio Export tracks to Audio file Esporta tracce su file audio Export MIDI File Esporta File MIDI Export MIDI file Esporta file MIDI Export tracks to MIDI file Esporta tracce su file MIDI Log&arithmic Log&aritmico Automation logarithmic Automazione logaritmica Automation curve logarithmic scale Curva di automazione con scala logaritmica C&olor... C&olore... Automation color Colore automazione Automation curve color Colore curva automazione &Lock B&locca Automation lock Blocca automazione Lock automation curve Blocca curva di automazione &Play Ri&produzione Automation playback Riproduzione automazione Playback automation curve Riproduzione curva di automazione Automation record Registra automazione Record automation curve Registra curva di automazione &Clear Pulis&ci Automation clear Pulisci automazione Clear automation curve Pulisci curva di automazione Loc&k All Blocca Tutto Automation lock all Blocca tutta le automazioni Lock all automation curves Blocca tutta le curve di automazione Play &All Riproduci Tutte Automation playback all Riproduci tutte le automazioni Playback all automation curves Rec&ord All Riproduci tutte le curve di automazione Automation record all Registra tutte le automazioni Record all automation curves Registra tutte le curve di automazione C&lear All Pu&lisci Tutto Automation clear all Pulisci tutte le automazioni Clear all automation curves Pulisci tutte le curve di automazione &New... &Nuovo... New Clip Nuovo Clip New clip Nuovo clip Create new clip Crea nuovo clip &Edit... Modifica... Edit Clip Modifica Clip Edit clip Modifica clip Edit current clip Modifica clip corrente F4 F4 Mute Clip Mute clip Mute current clip &Unlink Disconnetti Unlink Clip Scollega Clip Unlink clip Scollega clip Unlink current clip Scollega clip corrente Recor&d Registra Record Clip Registra Clip Record clip Registra clip Record current clip (overdub) Registra sul clip corrente (overdub) &Split Dividi Split Clip Dividi Clip Split clip Dividi clip Split current clip at playhead Dividi clip corrente alla barra di riproduzione &Merge... Unisci Merge Clips Unisci Clip Merge clips Unisci clip Merge selected clips Unisci i clip selezionati Normali&ze Normali&zza Normalize Clip Normalizza Clip Normalize clip Normalizza clip Normalize current clip (gain/volume) Normalizza clip corrente (guadagno/volume) &Quantize... &Quantizza... Quantize Clip Quantizza Clip Quantize clip events Quantizza eventi clip Quantize current MIDI clip events Quantizza eventi del clip MIDI corrente &Transpose... &Trasponi... Transpose Clip Trasponi Clip Transpose clip events Trasponi eventi clip Transpose current MIDI clip events Trasponi eventi clip MIDI corrente &Normalize... &Normalizza... Normalize clip events Normalizza eventi clip Normalize current MIDI clip events Normalizza eventi clip MIDI corrente &Randomize... &Randomizza... Randomize Clip Randomizza Clip Randomize clip events Randomizza eventi clip Randomize current MIDI clip events Randomizza eventi clip MIDI corrente Resi&ze... Ridimensiona Resize Clip Ridimensiona Clip Resize clip events Ridimensiona eventi clip Resize current MIDI clip events Ridimensiona eventi clip MIDI corrente Re&scale... &Scala... Rescale Clip Scala Clip Rescale clip events Scala eventi clip Rescale current MIDI clip events Scala eventi clip MIDI corrente T&imeshift... T&imeshift... Timeshift Clip Clip Timeshift Timeshift clip events Timeshift eventi clip Timeshift current MIDI clip events Timeshift eventi clip MIDI corrente T&empo ramp... Tempo ramp Clip Tempo ramp clip events Tempo ramp current MIDI clip events &Tempo Adjust... Aggiusta &tempo Tempo Adjust Aggiusta Tempo Adjust session tempo from current clip selection Aggiusta tempo sessione da selezione clip corrente F7 F7 &Cross Fade &Cross Fade Clip Cross-fade Cross-fade del clip Clip cross-fade Cross-fade del clip Cross-fade current overlapped clips Applica un cross fade tra i clip sovrapposti &Range Set Imposta Inte&rvallo Clip Range Intervallo Clip Clip range Intervallo clip Set edit-range from current clip extents Imposta intervallo modifica da estensione clip corrente &Loop Set Imposta &Loop Clip Loop Loop Clip Clip loop Loop clip Set loop-range from current clip extents Imposta intervallo loop da estensione clip corrente &Import... &Importa... Import Clip Importa Clip Import clip Importa clip Import clip from file(s) Importa clip da file E&xport... Esporta... Export Clip Esporta Clip Export clip Esporta clip Export current clip to file Esporta clip su file First Take Prima ripresa First take Prima ripresa Select current clip first take Seleziona prima ripresa del clip corrente Previous Take Ripresa Precedente Previous take Ripresa precedente Select current clip previous take Seleziona ripresa precedente del clip corrente Next Take Ripresa Successiva Next take Ripresa successiva Select current clip next take Seleziona ripresa successiva del clip corrente Shift+T Shift+T Last Take Ultima Ripresa Last take Ultima ripresa Select current clip last take Seleziona ultima ripresa del clip corrente Reset Takes Reset Riprese Reset takes Reset riprese Reset (unfold) current clip takes Reset riprese clip corrente R&ange... Interv&allo... Take Range Intervallo Ripresa Take range Intervallo ripresa Range (fold) current clip into takes Raggruppa clip corrente in riprese &Menubar Barra &Menu Menubar Barra Menu Show/hide the main program window menubar Mostra/nascondi barra dei menu della finestra principale Ctrl+M Ctrl+M &Statusbar Barra di &Stato Statusbar Barra di stato Show/hide the main program window statusbar Mostra/nascondi barra di stato della finestra principale File Toolbar Barra Strumenti File File toolbar Barra Strumenti file Show/hide main program window file toolbar Mostra/nascondi barra strumenti file della finestra principale Edit Toolbar Modifica Barra Strumenti Edit toolbar Modifica barra strumenti Show/hide main program window edit toolbar Mostra/nascondi modifica barra strumenti della finestra principale Track Toolbar Barra Strumenti Traccia Track toolbar Barra strumenti traccia Show/hide main program window track toolbar Mostra/nascondi barra strumenti traccia della finestra principale View Toolbar Barra Strumenti Vista View toolbar Barra strumenti vista Show/hide main program window view toolbar Mostra/nascondi barra strumenti vista della finestra principale &Options &Opzioni Options Toolbar Barra Strumenti Opzioni Options toolbar Barra strumenti opzioni Show/hide main program window options toolbar Mostra/nascondi barra strumenti opzioni della finestra principale Transport Toolbar Barra Strumenti Avanzamento Transport toolbar Barra strumenti avanzamento Show/hide main program window transport toolbar Mostra/nascondi barra strumenti avanzamento della finestra principale T&ime Tempo Time Toolbar Barra Strumenti Tempo Time toolbar Barra strumenti tempo Show/hide main program window time toolbar Mostra/nascondi barra strumenti tempo della finestra principale Thum&b Anterpime Thumb Toolbar Barra Strumenti Anteprime Thumb toolbar Barra strumenti anteprime Show/hide main program window thumb toolbar Mostra/nascondi barra strumenti anteprime della finestra principale File &System File System File system Show/hide the file system window &Files &File Files File Show/hide the files window Mostra/nascondi la finestra dei file M&essages M&essaggi Messages Messaggi Show/hide the messages window Mostra/nascondi la finestra dei messaggi &Connections &Connessioni Connections Connessioni Show/hide the connections window Mostra/nascondi la finestra delle connessioni F8 F8 Mi&xer Mi&xer Mixer Mixer Show/hide the mixer window Mostra/nascondi la finestra del mixer F9 F9 &In &Ingrandimento Zoom In Ingrandimento Zoom in Ingrandimento Ctrl++ Ctrl++ &Out Rimpicciolimento Zoom Out Rimpicciolimento Zoom out Rimpicciolimento Ctrl+- Ctrl+- Zoom Reset Reset Ridimensionamento Zoom reset Reset ridimensionamento Ctrl+1 Ctrl+1 &Horizontal Orizzontale Horizontal Zoom Orizzontale Horizontal zoom Ridimensionamento orizzontale Horizontal zoom mode Modalità ridimensionamento orizzontale &Vertical &Verticale Vertical Zoom Ridimensionamento Verticale Vertical zoom Ridimensionamento verticale Vertical zoom mode Modalità ridimensionamento verticale All Zoom Ridimensiona Tutto All zoom Ridimensiona tutto All zoom mode Modalità ridimensiona tutto &Grid &Griglia Grid Griglia Snap grid view mode Vista griglia di allineamento &Zebra Strisce Zebra Strisce Bar zebra view mode Modalità vista a strisce Too&l Tips Suggerimenti Tool tips Suggerimenti Floating tool tips view mode Modalità vista suggerimenti &Refresh Aggio&rna Refresh Aggiorna Refresh views Aggiorna viste F5 F5 &Instruments... Strument&i... Instruments Strumenti Change instrument definitions and files Modifica file e definizioni strumenti &Controllers... &Controller... Controllers Controller Change MIDI controllers configuration Modifica configurazione controller MIDI &Buses... &Bus... Buses Bus Change session bus definitions Modifica definizioni dei bus della sessione Tempo M&ap / Markers... M&appa del tempo / Marcatori... Tempo Map / Markers Mappa del tempo / Marcatori Tempo map / markers Mappa del tempo / marcatori Change session tempo map / markers Modifica la mappa del tempo / i marcatori della sessione &Options... &Opzioni... Options Opzioni Change general application program options Modifica le opzioni generali del programma F12 F12 &Backward Indietro Backward Indietro Transport backward Avanzamento indietro Backspace Backspace Re&wind Riavvolgimento Rewind Riavvolgimento Transport rewind Riavvolgimento avanzamento F&ast Forward F&ast Forward Fast Forward Fast Forward Fast forward Fast forward Transport fast forward Fast forward avanzamento &Forward &Avanti Forward Avanti Transport forward Avanzamento avanti &Loop &Loop Loop Loop Transport loop Avanzamento ciclico Ctrl+Shift+L Ctrl+Shift+L Step Backward Step backward Transport step backward Step Forward Step forward Transport step forward Loop &Set Impo&sta Loop Loop Set Imposta Loop Loop set Imposta loop Transport loop set Imposta loop avanzamento Ctrl+L Ctrl+L &Stop &Stop Stop Stop Transport stop Interrompi avanzamento Play Play Transport play/pause Avvia avanzamento Space Spazio Record Registra Transport record Registra avanzamento &Punch amp;Punch Punch Punch Punch in/out Punch in/out Transport punch in/out Punch in/out avanzamento Ctrl+Shift+P Ctrl+Shift+P Punch Se&t Impo&sta Punch Punch Set Imposta Punch Punch in/out set Impostazione punch in/out Transport punch in/out set Impostazione punch in/out avanzamento Ctrl+P Ctrl+P &Count-in Count-in &Metronome &Metronomo Metronome Metronomo F&ollow Playhead Segui la barra di ripr&oduzione Follow Playhead Segui la barra di riproduzione Follow playhead Segui la barra di riproduzione A&uto Backward Riavvolgimento A&utomatico Auto Backward Riavvolgimento Automatico Auto backward Riavvolgimento automatico &Continue Past End &Continua Oltre la Fine Continue Past End Continua Oltre la Fine Continue past end Continua oltre la fine Transport mode: None Modalità di avanzamento: Nessuna Transport mode set to None Modalità di avanzamento impostata su 'Nessuna' &Slave &Slave Slave Slave Transport mode: Slave Modalità di avanzamento: Slave Transport mode set to Slave Modalità di avanzamento impostata su 'Slave' &Master &Master Master Master Transport mode: Master Modalità di avanzamento: Master Transport mode set to Master Modalità di avanzamento impostata su 'Master' &Full Completa Full Completa Transport mode: Full Modalità di avanzamento: Completa Transport mode set to Full Modalità di avanzamento impostata su 'Completa' Pa&nic Pa&nico Panic Panico All MIDI tracks shut off (panic) Spegni tutte le tracce MIDI (panico) &Shortcuts... &Scorciatoie... Shortcuts Scorciatoie Keyboard shortcuts Scorciatoie tastiera &About... Inform&azioni su... About Informazioni su Show information about this application program Mostra informazioni riguardo questo programma About &Qt... Informazioni su &Qt... About Qt Informazioni su Qt Show information about the Qt toolkit Mostra informazioni sulle librerie Qt Current tempo (BPM) Tempo corrente (BPM) Snap/beat Allineamento/battito Track Traccia Current track name Nome traccia corrente MOD MOD Session modification state Stato modifica sessione REC REC Session record state Stato registrazione sessione MUTE MUTE Session muting state Stato silenziamento sessione SOLO SOLO Session soloing state Stato solo sessione LOOP LOOP Session looping state Stato loop sessione Session total time Tempo totale sessione Session sample rate Frequenza di campionamento sessione Could not set default session directory: %1 Sorry. Impossibile utilizzare la directory di default della sessione: %1 Spiacente. Ready Pronto Session XRUN state Stato della sessione XRUN Session buffer size Untitled%1 SenzaTitolo%1 New session: "%1". Nuova sessione: "%1". Session files (*.%1 *.%2 *.%3) File di sessione (*.%1 *.%2 *.%3) Session files (*.%1 *.%2) File di sessione (*.%1 *.%2) Template files (*.%1) File di template (*.%1) Archive files (*.%1) File di archivio (*.%1) All files (*.*) Tutti i file (*.*) Open Session Apri Sessione Save Session Salva Sessione Warning Attenzione The file already exists: "%1" Do you want to replace it? Il file esiste già: "%1" Vuoi sostituirlo? Backup session: "%1" as "%2". Esegui backup della sessione: "%1" su "%2". Could not backup existing session: %1 as %2 Sorry. Impossibile effettuare il backup della sessione esistente: %1 su %2 Spiacente. The current session has been changed: "%1" Do you want to save the changes? La sessione corrente è stata modificata: "%1" Vuoi salvare le modifiche? About to remove archive directory: "%1" Are you sure? Sto per rimuovere la cartella di archivio: "%1" Sei sicuro? Session closed. Sessione chiusa. The directory already exists: "%1" Do you want to replace it? La cartella esiste già: "%1" Vuoi sostituirla? Opening "%1"... Apertura "%1"... Session could not be loaded from "%1". Sorry. La sessione non può essere caricata da "%1". Spiacente. Open session: "%1". Sessione aperta: "%1". A directory with same name already exists: "%1" This directory will be replaced, erasing all its current data, when opening and extracting this archive in the future. Do you want to continue? The directory is an extracted archive: "%1" This directory will be removed, erased from all its current data, when closing this session. Do you want to continue? Saving "%1"... Salvataggio "%1"... Session could not be saved to "%1". Sorry. La sessione non può essere salvata su "%1". Spiacente. Save session: "%1". Sessione salvata: "%1". Oops! Looks like it crashed or did not close properly last time it was run... however, an auto-saved session file exists: "%1" Do you want to crash-recover from it? Oops! Sembra che sia avvenuto un crash o che il programma non sia stato chiuso correttamente l'ultima volta che è stato eseguito... tuttavia, esiste un salvataggio automatico di un file di sessione: "%1" Vuoi recuperare il crash da questo file ? About to clear automation: "%1" Are you sure? Si sta per eliminare l'automazione: "%1" Sei sicuro? About to clear all automation: "%1" Are you sure? Si sta per eliminare tutte le automazioni: "%1" Sei sicuro? take range intervallo ripresa session sessione or o program programma Information Informazioni Some settings may be only effective next time you start this %1. Alcune impostazioni potrebbero avere effetto solo al prossimo avvio di questo %1. Player panic! Debugging option enabled. Opzione di debug attiva. Ogg Vorbis (libvorbis) file support disabled. Supporto file Ogg Vorbis (libvorbis) disabilitato. MPEG-1 Audio Layer 3 (libmad) file support disabled. Supporto file MPEG-1 Audio Layer 3 (libmad) disabilitato. Sample-rate conversion (libsamplerate) disabled. Conversione frequenza di campionamento (libsamplerate) disabilitata. Pitch-shifting support (librubberband) disabled. Supporto pitch-shift (librubberband) disabilitato. Beat-detection support (libaubio) disabled. OSC service support (liblo) disabled. Supporto servizio OSC (liblo) disabilitato. LADSPA Plug-in support disabled. Supporto Plug-in LADSPA disabilitato. DSSI Plug-in support disabled. Supporto Plug-in DSSI disabilitato. CLAP Plug-in support disabled. LV2 Plug-in support disabled. Supporto Plug-in LV2 disabilitato. LV2 Plug-in UI support disabled. Supporto UI della plugin LV2 disabilitato. LV2 Plug-in UI support (libsuil) disabled. Supporto UI plugin LV2 (libsuil) disabilitato. LV2 Plug-in MIDI/Event support (DEPRECATED) enabled. Supporto MIDI/Eventi (DEPRECATO) della plugin LV2 abilitato. LV2 plug-in State Make Path support (DANGEROUS) enabled. Supporto State Make Path (PERICOLOSO) della plugin LV2 abilitato. LV2 Plug-in State Files support disabled. Supporto dei file di stato della plugin LV2 disabilitato. LV2 Plug-in MIDNAM support disabled. LV2 Plug-in Patch support disabled. Supporto alle patch della plugin LV2 disabilitato. LV2 Plug-in UI Touch interface support disabled. Supporto all'interfaccia touch della plugin LV2 disabilitato. LV2 Plug-in UI Idle interface support disabled. Supporto dell'interfaccia UI Idle della plugin LV2 disabilitato. LV2 Plug-in UI Show interface support disabled. Supporto dell'interfaccia UI Show della plugin LV2 disabilitato. LV2 Plug-in UI GTKMM2 native support disabled. JACK Metadata support disabled. Supporto ai metadata di JACK disabilitato. Using: Qt %1 XRUN XRUN The following issues were detected: %1 Saving into another session file is highly recommended. I seguenti problemi sono stati identificati: %1 E' fortemente consigliato il salvataggio in un nuovo file di sessione. Don't show this again The audio engine buffer size has changed, increased from %1 to %2 frames/period. Reloading the current session file is highly recommended. La dimensione del buffer del motore audio è cambiata, ed è incrementata da %1 a %2 frame per periodo. E' altamente consigliato ricaricaricare il file della sessione corrente. TRACK MONITOR %1 %2 Set current snap to %1 Imposta allineamento corrente a %1 Current time (play-head) Tempo corrente (barra di riproduzione) VST2 Plug-in support disabled. VST3 Plug-in support disabled. LV2 Plug-in support (liblilv) disabled. Supporto Plug-in LV2 (liblilv) disabilitato. LV2 Plug-in External UI support disabled. Supporto UI esterna Plug-in LV2 disabilitato. LV2 Plug-in MIDI/Atom support disabled. Supporto MIDI/Atom plugin LV2 disabilitato. LV2 Plug-in Worker/Schedule support disabled. Supporto Worker/Schedule plugin LV2 disabilitato. LV2 Plug-in State support disabled. Supporto stato Plug-in LV2 disabilitato. LV2 Plug-in Programs support disabled. Supporto programmi Plug-in LV2 disabilitato. LV2 Plug-in Presets support disabled. Supporto preset plugin LV2 disabilitato. LV2 Plug-in Time/position support disabled. Supporto Tempo/posizione plugin LV2 disabilitato. LV2 Plug-in Options support disabled. Supporto delle opzioni della plugin LV2 disabilitato. LV2 Plug-in Buf-size support disabled. Supporto dimensione buffer della plugin LV2 disabilitato. LV2 Plug-in UI Request-value support disabled. JACK Session support disabled. Supporto sessione JACK disabilitato. JACK Latency support disabled. Supporto latenza JACK disabilitato. Version Versione Website Sito This program is free software; you can redistribute it and/or modify it Questo programma è un software gratuito; è possibile ridistribuirlo e/o modificarlo under the terms of the GNU General Public License version 2 or later. nei termini della GNU General Public License versione 2 o successiva. record clip registra clip [modified] [modificato] Session started. Sessione avviata. The audio/MIDI engine could not be started. Make sure the JACK/Pipewire audio service and the ALSA Sequencer kernel module (snd-seq-midi) are up and running and then restart the session. The original session sample rate (%1 Hz) is not the same as the current audio engine (%2 Hz). Saving and reloading from a new session file is highly recommended. La frequenza di campionamento originale della sessione (%1 Hz) non è la stessa del motore audio corrente (%2 Hz). Salvare e ricaricare da un nuovo file di sessione è particolarmente raccomandato. Don't ask this again Non chiederlo di nuovo LV2 Plug-in UI GTK2 native support disabled. Supporto nativo per UI GTK2 della plugin LV2 disabilitato. LV2 Plug-in UI X11 native support disabled. Supporto nativo per UI X11 della plugin LV2 disabilitato. NSM support disabled. Supporto NSM disabilitato. &Hold &Hold &Linear &Lineare &Spline &Spline Take %1 Ripresa %1 None Nessuno Error Errore XRUN(%1 skipped) XRUN(%1 saltato) XRUN(%1): some frames might have been lost. XRUN(%1): alcuni campioni potrebbero essere stati persi. Audio connections change. Cambiamento connessioni audio. Audio self-connection detected! In general, connecting an output bus (or insert send), directly into any input bus (or insert return), is not advisable. It often doesn't work, if at all. MIDI connections change. Cambiamento connessioni MIDI. Playing ended. Riproduzione terminata. The audio engine has been shutdown. Make sure the JACK audio server (jackd) is up and running and then restart session. Il motore audio è stato fermato. Accertati che il server audio JACK (jackd) sia avviato ed in esecuzione, poi riavvia la sessione. STOP STOP PLAY PLAY FFWD FFWD REW REW REC ON REC ON REC OFF REC OFF RESET RESET LOCATE %1 TROVA %1 SHUTTLE %1 SHUTTLE %1 STEP %1 STEP %1 TRACK RECORD %1 %2 REGISTRA TRACCIA %1 %2 TRACK MUTE %1 %2 MUTE TRACCIA %1 %2 TRACK SOLO %1 %2 SOLO TRACCIA %1 %2 Unknown sub-command Sotto-comando sconosciuto Not implemented Non implementato MIDI CTL: %1, Channel %2, Param %3, Value %4 MIDI CTL: %1, Canale %2, Parametro %3, Valore %4 (track %1, gain %2) (traccia %1, guadagno %2) (track %1, panning %2) (traccia %1, bilanciamento %2) START AVVIA CONTINUE CONTINUA SONGPOS %1 SONGPOS %1 %1 BPM %1 BPM Playing "%1"... Riproduzione "%1"... qtractorMessages Messages Messaggi Logging stopped --- %1 --- Log interrotto --- %1 --- Logging started --- %1 --- Log avviato --- %1 --- qtractorMidiControl Note On Note Off Key Press Controller Pgm Change Pgm Change Chan Press Chan Press Pitch Bend Pitch Bend RPN NRPN Control 14 Track Gain Track Panning Track Monitor Track Record Track Mute Track Solo qtractorMidiControlForm Controllers Controller Controller files File dei controller Files File Path Percorso Import controller files Importa file del controller &Import... &Importa... Remove controller file Elimina file del controller &Remove &Elimina Move controller file up on list order Sposta in su nella lista il file del controller &Up S&u Move controller file down on list order Sposta in giù nella lista il file del controller &Down &Giù &Type &Tipo &Channel &Canale &Parameter &Parametro Trac&k offse&t &limit C&ommand C&omando Flags MIDI Event type Tipo di evento MIDI MIDI Channel Canale MIDI MIDI Controller (parameter) Controller MIDI (parametro) MIDI parameter (track offset) Parametro MIDI (posizione traccia) + + Track offset Posizione traccia Track limit Command action Azione comando Command delta/momentary D&elta Command feedback Feedback comando &Feedback &Feedback Map/update controller command Mappa/aggiorna comando controller &Map &Mappa Controller map Mappa controller Type Tipo Channel Canale Parameter Parametro Track Traccia Command Comando Feedback Feedback Unmap/remove controller command Pulisci/rimuovi comando controller U&nmap Pulisci Enable all controllers immediate sync (hook) Abilita il sync immediato su tutti i controller (aggancio) &Sync &Sincronizza Reload/apply all controller files Ricarica/applica tutti i file dei controller Relo&ad Ric&arica Export to controller file Esporta su file di controller E&xport... &Esporta... Close this dialog Chiudi questa finestra Close Chiudi Import Controller Files Importa File di Controller Controller files (*.%1) File di controller (*.%1) All files (*.*) Tutti i file (*.*) Warning Attenzione About to remove controller file: "%1" Are you sure? Stai per rimuovere il file del controller: "%1" Sei sicuro? Export Controller File Esporta File di Controller controller controller The controller file already exists: "%1" Do you want to replace it? Il file del controller esiste già: "%1" Vuoi sostituirlo? Saved controller mappings may not be effective the next time you start this program. "%1" Do you want to apply to controller files? La mappa del controller potrebbe non essere attiva al prossimo riavvio di questo programma. "%1" Vuoi applicarla ai file dei controller? Controller mappings have been changed. La mappa del controller è stata modificata. Do you want to save the changes? Vuoi salvare le modifiche? Delta qtractorMidiControlObserverForm &Type: &Tipo: MIDI event type Tipo di evento MIDI Cha&nnel: Ca&nale: MIDI channel Canale MIDI &Parameter: &Parametro: MIDI parameter Parametro MIDI &Logarithmic &Logaritmico &Feedback &Feedback In&vert In&verso &Hook Aggancia L&atch L&atch Control input connections Controlla le connessioni di ingresso &Inputs &Ingressi Control output connections Controlla le connessioni di uscita &Outputs Uscite MIDI Controller Controller MIDI MIDI controller is already assigned. Do you want to replace the mapping? Il controller MIDI è già assegnato. Vuoi sostituire la mappatura? Some settings have been changed. Do you want to apply the changes? Alcune impostazioni sono state modificate. Vuoi applicare le modifiche? &MIDI Controller... Controller &MIDI... &Automation &Automazione &Lock B&locca &Play Ri&produci &Record &Registra &Clear Pulis&ci qtractorMidiControlPluginWidget &Type: &Tipo: MIDI event type Tipo di evento MIDI Cha&nnel: Ca&nale: MIDI channel Canale MIDI &Parameter: &Parametro: MIDI parameter Parametro MIDI &Logarithmic &Logaritmico In&vert In&verso &Bipolar qtractorMidiEditEvent Zoom in (horizontal) Ingrandimento (orizzontale) Zoom out (horizontal) Rimpicciolimento (orizzontale) Zoom reset (horizontal) Ripristina ingrandimento (orizzontale) qtractorMidiEditList C%1 C%1 qtractorMidiEditTime Play-head Barra di riproduzione Edit-head Barra di inizio modifica Edit-tail Barra di fine modifica Loop-start Inizio loop Loop-end Fine loop Punch-in Punch ingresso Punch-out Punch uscita Start: %1 End: %2 Length: %3 Inizio: %1 Fine: %2 Durata: %3 qtractorMidiEditView Zoom in (vertical) Ingrandimento (verticale) Zoom out (vertical) Rimpicciolimento (verticale) Zoom reset (vertical) Ripristina ingrandimento(verticale) qtractorMidiEditor C C#/Db D D#/Eb E F F#/Gb G G#/Ab A A#/Bb B Acoustic Bass Drum Bass Drum 1 Side Stick Acoustic Snare Hand Clap Electric Snare Low Floor Tom Closed Hi-Hat High Floor Tom Pedal Hi-Hat Low Tom Open Hi-Hat Low-Mid Tom Hi-Mid Tom Crash Cymbal 1 High Tom Ride Cymbal 1 Chinese Cymbal Ride Bell Tambourine Splash Cymbal Cowbell Crash Cymbal 2 Vibraslap Ride Cymbal 2 Hi Bongo Low Bongo Mute Hi Conga Open Hi Conga Low Conga High Timbale Low Timbale High Agogo Low Agogo Cabasa Maracas Short Whistle Long Whistle Short Guiro Long Guiro Claves Hi Wood Block Low Wood Block Mute Cuica Open Cuica Mute Triangle Open Triangle Bank Select (coarse) Modulation Wheel (coarse) Breath Controller (coarse) Foot Pedal (coarse) Portamento Time (coarse) Data Entry (coarse) Volume (coarse) Balance (coarse) Pan Position (coarse) Expression (coarse) Effect Control 1 (coarse) Effect Control 2 (coarse) General Purpose Slider 1 General Purpose Slider 2 General Purpose Slider 3 General Purpose Slider 4 Bank Select (fine) Modulation Wheel (fine) Breath Controller (fine) Foot Pedal (fine) Portamento Time (fine) Data Entry (fine) Volume (fine) Balance (fine) Pan Position (fine) Expression (fine) Effect Control 1 (fine) Effect Control 2 (fine) Hold Pedal (on/off) Portamento (on/off) Soft Pedal (on/off) Legato Pedal (on/off) Hold 2 Pedal (on/off) Sound Variation General Purpose Button 1 (on/off) General Purpose Button 2 (on/off) General Purpose Button 3 (on/off) General Purpose Button 4 (on/off) Effects Level Chorus Level Celeste Level Phaser Level Data Button Increment Data Button Decrement Non-Registered Parameter (fine) Non-Registered Parameter (coarse) Registered Parameter (fine) Registered Parameter (coarse) All Sound Off All Controllers Off Local Keyboard (on/off) All Notes Off Omni Mode Off Omni Mode On Mono Operation Poly Operation Pitch Bend Sensitivity Fine Tune Coarse Tune Tuning Program Tuning Bank Vibrato Rate Vibrato Depth Vibrato Delay Filter Cutoff Filter Resonance Sostenuto Pedal (on/off) Release Time Attack Time Brightness Decay Time Tremolo Level EG Attack EG Decay EG Release Drum Filter Cutoff Drum Filter Resonance Drum EG Attack Drum EG Decay Drum Pitch Coarse Drum Pitch Fine Drum Level Drum Pan Drum Reverb Send Drum Chorus Send Drum Variation Send Modulation Wheel (14bit) Breath Controller (14bit) Foot Pedal (14bit) Portamento Time (14bit) Volume (14bit) Balance (14bit) Pan Position (14bit) Expression (14bit) Effect Control 1 (14bit) Effect Control 2 (14bit) General Purpose Slider 1 (14bit) General Purpose Slider 2 (14bit) General Purpose Slider 3 (14bit) General Purpose Slider 4 (14bit) Chromatic Major Minor Melodic Minor (Asc) Melodic Minor (Desc) Whole Tone Pentatonic Major Pentatonic Minor Pentatonic Blues Pentatonic Neutral Octatonic (H-W) Octatonic (W-H) Ionian Dorian Phrygian Lydian Mixolydian Aeolian Locrian Egyptian Eight Tone Spanish Hawaiian Hindu Hirajoshi Hungarian Major Hungarian Minor Hungarian Gypsy Japanese (A) Japanese (B) Jewish (Adonai Malakh) Jewish (Ahaba Rabba) Jewish (Magen Abot) Oriental (A) Oriental (B) Oriental (C) Roumanian Minor Neapolitan Neapolitan Major Neapolitan Minor Overtone Leading Whole Tone Nine Tone Scale Dominant Seventh Augmented Algerian Arabian (A) Arabian (B) Balinese Chinese Diminished Japanese (Ichikosucho) Japanese (Taishikicho) Javaneese Marva Theta Mela Bhavapriya Mela Chakravakam Mela Chalanata Mela Chitrambari Mela Dharmavati Mela Dhatuvardhani Mela Dhavalambari Mela Divyamani Mela Ganamurti Mela Gangeyabhusani Mela Gavambodhi Mela Gayakapriya Mela Hatakambari Mela Jalarnavam Mela Jhalavarali Mela Jhankaradhvani Mela Jyotisvarupini Mela Kamavarardhani Mela Kantamani Mela Kosalam Mela Latangi Mela Manavati Mela Mararanjani Mela Naganandini Mela Namanarayani Mela Navanitam Mela Nitimati Mela Pavani Mela Ragavardhani Mela Raghupriya Mela Ramapriya Mela Rasikapriya Mela Ratnangi Mela Risabhapriya Mela Rupavati Mela Sadvidhamargini Mela Salagam Mela Sanmukhapriya Mela Sarasangi Mela Senavati Mela Subhapantuvarali Mela Sucharitra Mela Sulini Mela Suryakantam Mela Syamalangi Mela Tanarupi Mela Vagadhisvari Mela Vanaspati Mela Varunapriya Mela Yagapriya Persian Purvi Theta Spanish Gypsy Todi Theta Enigmatic Kumoi Lydian Augmented Pelog Prometheus Prometheus Neapolitan Six Tone Symmetrical Super Locrian Lydian Minor Lydian Diminished Half Diminished Bhairav Yaman Todi Jog Multani Darbari Malkauns Bhoopali Shivaranjani Marwa Minor 5 Major 5 5 5 45 45 457 457 M 6 MIDI Editor Editor MIDI cut taglia delete cancella insert range inserisci intervallo remove range elimina intervallo move sposta edit modifica resize ridimensiona rescale scala paste incolla Time: %1 Type: Tempo: %1 Tipo: Note On (%1) %2 Velocity: %3 Duration: %4 Nota On (%1) %2 Velocity: %3 Durata: %4 Key Press (%1) %2 Value: %3 Tasto Premuto (%1) %2 Valore: %3 Controller (%1) Name: %2 Value: %3 Controller (%1) Nome: %2 Valore: %3 RPN (%1) Name: %2 Value: %3 RPN (%1) Nome: %2 Valore: %3 NRPN (%1) Name: %2 Value: %3 NRPN (%1) Nome: %2 Valore: %3 Control 14 (%1) Name: %2 Value: %3 Control 14 (%1) Nome: %2 Valore: %3 Pgm Change (%1) Pgm Change (%1) Chan Press (%1) Chan Press (%1) Pitch Bend (%1) Pitch Bend (%1) SysEx (%1 bytes) Data: SysEx (%1 bytes) Dati: Unknown (%1) Sconosciuto (%1) Start: %1 End: %2 Length: %3 Inizio: %1 Fine: %2 Durata: %3 qtractorMidiEditorForm MIDI Editor Editor MIDI &Track &Traccia &File &File Select &Mode Seleziona &Modalità &Select &Selezione I&nsert I&nserimento Remo&ve &Rimuovi &Tools S&trumenti &Edit &Modifica &View &Vista Instrum&ent Strum&ento &Toolbars Barre S&trumenti &Windows &Finestre Not&e Type Tipo di nota Val&ue Type Tipo di valore &Ghost Track &Zoom &Zoom S&nap Alli&neamento Sc&ale Sc&alamento T&ransport &Riproduzione &Note St&ep &Help &Aiuto &Save &Salva Save Salva Save current MIDI clip to existing file name Salva il clip MIDI corrente su un file esistente Save &As... S&alva Come... Save As Salva Come Save as Salva come Save current MIDI clip with another file name Salva il clip MIDI corrente su un altro file &Mute &Mute Mute Mute current MIDI clip &Unlink &Disconnetti Unlink Disconnetti Unlink current MIDI clip Disconnetti il clip MIDI corrente Recor&d Registra Record current MIDI clip (overdub) Registra sul clip MIDI corrente (overdub) &Inputs &Ingressi Track Inputs Ingressi Traccia Track inputs Ingressi traccia Show current MIDI clip/track input bus connections Mostra le connessioni dei bus di ingresso della traccia/clip MIDI corrente &Outputs &Uscite Track Outputs Uscite Traccia Track outputs Uscite traccia Show current MIDI clip/track output bus connections Mostra le connessioni dei bus di uscita della traccia/clip MIDI corrente &Properties... &Proprietà... Track Properties Proprietà Traccia Track properties Proprietà traccia Edit current MIDI clip/track properties Modifica le proprietà della traccia/clip MIDI corrente Shift+F2 Shift+F2 Properties Proprietà Edit current MIDI clip properties Modifica le proprietà del clip MIDI corrente F4 F4 &Range Set Imposta Inte&rvallo Clip Range Intervallo Clip Clip range Intervallo clip Set edit-range from clip extents Imposta intervallo di modifica dagli estremi del clip &Loop Set Imposta &Loop Clip Loop Loop Clip Clip loop Loop clip Set loop-range from clip extents Imposta intervallo del loop dagli estremi del clip &Close &Chiudi Close Chiudi Close this MIDI clip editor Chiudi questo editor di clip MIDI &Undo Ann&ulla Undo Annulla Undo last edit operation Annulla l'ultima operazione di modifica Ctrl+Z Ctrl+Z &Redo &Ripristina Redo Ripristina Redo last edit operation Ripristina l'ultima operazione di modifica Ctrl+Shift+Z Ctrl+Shift+Z Cu&t &Taglia Cut Taglia Cut current selection into the local clipboard Taglia la selezione corrente nella clipboard locale Ctrl+X Ctrl+X &Copy &Copia Copy Copia Copy current selection to the local clipboard Copia la selezione corrente nella clipboard locale Ctrl+C Ctrl+C &Paste &Incolla Paste Incolla Paste local clipboard contents into the current MIDI clip Incolla il contenuto della clipboard nel clip MIDI corrente Ctrl+V Ctrl+V Past&e Repeat... Rip&eti Incolla... Paste Repeat Ripeti Incolla Paste repeat Ripeti incolla Paste/repeat local clipboard contents into the current MIDI clip Ripeti/incolla il contenuto della clipboard nel clip MIDI corrente Ctrl+Shift+V Ctrl+Shift+V &Delete &Elimina Delete Cancella Delete current selection Cancella la selezione corrente Del Canc Edit Of&f Disabilita Modi&fiche Edit Off Disabilita Modifiche Edit off Disabilita modifiche Set edit mode off Imposta modalità di modifiche disabilitate Edit &On Abilita M&odifiche Edit On Abilita Modifiche Edit on Abilita modifiche Set edit mode on Imposta modalità di modifiche abilitate Edit &Draw Modifca &Disegno Edit draw mode Modalità modifica disegno Edit draw mode (notes) Modalità modifica disegno (note) &All &Tutto Select All Seleziona Tutto Select all Seleziona tutto Ctrl+A Ctrl+A &None &Niente Select None Seleziona Niente Select none Seleziona niente Ctrl+Shift+A Ctrl+Shift+A &Invert &Inverti Select Invert Selezione Inversa Select invert Selezione inversa Ctrl+I Ctrl+I &Range Inte&rvallo Select Range Seleziona Intervallo Select range Seleziona intervallo Mark range as selected Marca intervallo come selezionato Ctrl+R Ctrl+R Insert Range Inserisci intervallo Insert range Inserisci intervallo Insert range as selected Inserisci intervallo come selezionato Ctrl+Ins Ctrl+Ins &Step Insert step Insert step (rest) Right DO NOT TRANSLATE Remove Range Elimina intervallo Remove range Elimina intervallo Remove range as selected Elimina intervallo come selezionato Ctrl+Del Ctrl+Canc &Quantize... &Quantizza... Quantize Quantizza Quantize selection Quantizza selezione &Transpose... &Trasposizione... Transpose Trasposizione Transpose selection Trasponi selezione &Normalize... &Normalizza... Normalize Normalizza Normalize selection Normalizza selezione &Randomize... &Randomizza... Randomize Randomizza Randomize selection Randomizza selezione Resi&ze... Ridimensiona... Resize Ridimensiona Resize selection Ridimensiona selezione Re&scale... &Scala... Rescale Scala Rescale selection Scala selezione T&imeshift... T&imeshift... Timeshift Timeshift Timeshift selection Timeshift selezione T&empo ramp... Tempo ramp Tempo ramp selection &Menubar Barra del &Menu Menubar Barra del menu Show/hide the menubar Mostra/nascondi barra del menu Ctrl+M Ctrl+M &Statusbar Barra di &Stato Statusbar Barra di Stato Show/hide the statusbar Mostra/nascondi barra di stato File Toolbar Barra Strumenti File File toolbar Barra strumenti file Show/hide the file toolbar Mostra/nascondi barra strumenti file Edit Toolbar Barra Strumenti Modifica Edit toolbar Barra strumenti modifica Show/hide the edit toolbar Mostra/nascondi barra strumenti modifica View Toolbar Barra Strumenti Vista View toolbar Barra strumenti vista Show/hide the view toolbar Mostra/nascondi barra strumenti vista &Transport Avanzamen&to Transport Toolbar Barra Strumenti Avanzamento Transport toolbar Barra strumenti avanzamento Show/hide the transport toolbar Mostra/nascondi barra strumenti avanzamento T&ime Tempo Time Toolbar Barra Strumenti Tempo Time toolbar Barra strumenti tempo Show/hide the time toolbar &Scale &Scalamento Scale Toolbar Barra Strumenti Scalamento Scale toolbar Barra strumenti scalamento Show/hide the scale toolbar Mostra/nascondi barra strumenti scalamento Thum&b Antep&rima Thumb Toolbar Barra Strumenti Anteprima Thumb toolbar Barra strumenti anteprima Show/hide the thumb view toolbar Mostra/nascondi barra degli strumenti delle anteprime Note &Names Note Names Nomi Note Note names Whether to show note names Note &Duration &Durata Nota Note Duration Durata Nota Note duration Durata nota Whether note events are shown proportional to duration Se mostrare gli eventi delle note proporzionali alla durata Note &Color &Colore Nota Note Color Colore Nota Note color Colore nota Whether note events are colored according to pitch Se le note sono colorate in base alla loro altezza &Value Color Colore &Valore Value Color Colore Valore Value color Colore valore Whether note events are colored according to value (velocity) Se le note sono colorate in base al loro valore (intensit�) &Drum Mode Drum Mode Drum mode Whether note onset events are displayed as diamonds &Events &Eventi View events Mostra eventi Show/hide the events list Mostra/nascondi la lista degli eventi &Preview Notes Ante&prima Note Preview Notes Anteprima Note Preview notes Anteprima note Preview notes while editing (scrub) Anteprima note durante la modifica F&ollow Playhead Segui la barra di ripr&oduzione Follow Playhead Segui la barra di riproduzione Follow playhead Segui la barra di riproduzione &In &In Zoom In Ingrandisci Zoom in Ingrandisci Ctrl++ Ctrl++ &Out &Out Zoom Out Rimpicciolisci Zoom out Rimpicciolisci Ctrl+- Ctrl+- &Reset &Ripristina Zoom Reset Ripristina Ingrandimento Zoom reset Ripristina ingrandimento Ctrl+1 Ctrl+1 &Horizontal Orizzontale Horizontal Zoom Ingrandimento Orizzontale Horizontal zoom Ingrandimento orizzontale Horizontal zoom mode Modalità ingrandimento orizzontale &Vertical &Verticale Vertical Zoom Ingrandimento Verticale Vertical zoom Ingrandimento verticale Vertical zoom mode Modalità ingrandimento verticale All Zoom Ingrandisci Tutto All zoom Ingrandisci tutto All zoom mode Modalità ingrandisci tutto &Zebra Colonne Zebra Colonne Bar zebra view mode Modalità vista a colonne &Grid &Griglia Grid Griglia Snap grid view mode Modalità allineamento alla griglia Too&l Tips Suggerimenti Tool tips Suggerimenti Floating tool tips view mode Modalità vista suggerimenti &Refresh Aggio&rna Refresh Aggiorna Refresh views Aggiorna viste F5 F5 &Backward Indietro Backward Indietro Transport backward Avanzamento indietro Backspace Backspace Re&wind Riavvolgi Rewind Riavvolgi Transport rewind Riavvolgi Avanzamento F&ast Forward &Avanti Veloce Fast Forward Avanti Veloce Fast forward Avanti veloce Transport fast forward Avanti veloce avanzamento &Forward Avanti Forward Avanti Transport forward Avanti avanzamento Step Backward Step backward Transport step backward Step Forward Step forward Transport step forward Note Backward Step note backward Transport step note backward Note Forward Step note forward Transport step note forward &Loop Loop Loop Loop Transport loop Loop avanzamento Ctrl+Shift+L Ctrl+Shift+L Loop &Set Impo&sta Loop Loop Set Imposta Loop Loop set Imposta loop Transport loop set Imposta loop avanzamento Ctrl+L Ctrl+L &Stop &Stop Stop Stop Transport stop Ferma avanzamento &Play &Play Play Play Transport play/pause Avvia avanzamento Space Barra spazio &Record &Registra Record Registra Transport record Registra avanzamento &Punch Punch Punch Punch Punch in/out Punch in/out Transport punch in/out Punch in/out avanzamento Ctrl+Shift+P Ctrl+Shift+P Punch Se&t Impo&sta Punch Punch Set Imposta Punch Punch in/out set Imposta punch in/out Transport punch in/out set Imposta punch in/out avanzamento Ctrl+P Ctrl+P Pa&nic Pa&nico Panic Panico All MIDI tracks shut off (panic) Spegni tutte le tracce MIDI (panico) &Shortcuts... &Scorciatoie... Shortcuts Scorciatoie Keyboard shortcuts Scorciatoie tastiera &About... Inform&azioni su... About Informazioni su Show information about this application program Mostra informazioni su questo programma About &Qt... Informazioni su &Qt... About Qt Informazioni su Qt Show information about the Qt toolkit Mostra informazioni su Qt Current time (play-head) Tempo corrente (barra di riproduzione) Current tempo (BPM) Tempo corrente (BPM) Reset time-sig. Set current snap to %1 Imposta allineamento corrente a %1 Note Velocity Intensità Nota Snap/beat Allineamento/Battito Note type Tipo nota Value type Tipo valore Parameter type Tipo di parametro Scale key Chiave scala Scale type Tipo scala MIDI clip name Nome clip MIDI MIDI file name Nome file MIDI MIDI track/channel Traccia/Canale MIDI MOD MOD MIDI modification state Stato modifica MIDI REC REC MIDI clip record state Stato registrazione clip MIDI MUTE MUTE MIDI clip mute state 00:00:00.000 00:00:00.000 MIDI clip duration Durata clip MIDI Warning Attenzione The current MIDI clip has been changed: "%1" Do you want to save the changes? Il clip MIDI corrente è stato modificato: "%1" Vuoi salvare i cambiamenti? Save MIDI Clip Salva Clip MIDI MIDI files (*.%1 *.smf *.midi) File MIDI (*.%1 *.smf *.midi) All files (*.*) Tutti i file (*.*) Channel %1 Canale %1 Track %1 Traccia %1 [modified] [modificato] qtractorMidiEventList Events Eventi qtractorMidiEventListView::ItemDelegate edit %1 modifica %1 qtractorMidiEventListView::ItemModel Time Tempo Type Tipo Name Nome Value Valore Duration/Data Durata Frame Campione BBT BBT Note On (%1) Nota On (%1) Note Off (%1) Nota Off (%1) Key Press (%1) Tasto Premuto (%1) Controller (%1) Controller (%1) Control 14 (%1) Control 14 (%1) RPN (%1) RPN (%1) NRPN (%1) NRPN (%1) Pgm Change Pgm Change Chan Press Chan Press Pitch Bend Pitch Bend SysEx SysEx Meta (%1) Meta (%1) Unknown (%1) Sconosciuto (%1) qtractorMidiListView Name Nome Fmt Formato Tracks Tracce tpqn tpqn Path Percorso %1: MIDI file not found. %1: File MIDI non trovato. Open MIDI Files Apri File MIDI MIDI files (*.%1 *.smf *.midi) File MIDI (*.%1 *.smf *.midi) All files (*.*) Tutti i file (*.*) qtractorMidiMixerMeter Volume (%) % % Pan: %1 Volume: %1% Volume: %1% qtractorMidiSysexForm MIDI SysEx Name Nome Size Dimensione Data (hex) Dati (esa) Import from SysEx file Importa da file SysEx &Import... &Importa... Export to SysEx file Esporta su file SysEx E&xport... &Esporta... Move SysEx item up on list order Sposta su elemento SysEx nella lista &Up S&u Move SysEx item down on list order Sposta giù elemento SysEx nella lista &Down &Giù Open SysEx Apri SysEx Sysex name Nome SysEx Save SysEx Salva SysEx Delete SysEx Elimina SysEx Create SysEx item Crea SysEx &Add &Aggiungi Update SysEx item Aggiorna elemento SysEx Upda&te &Aggiorna Remove SysEx item Rimuovi elemento SysEx &Remove &Rimuovi SysEx files (*.%1) File SysEx (*.%1) MIDI files (*.mid *.smf *.midi) File MIDI (*.mid *.smf *.midi) All files (*.*) Tutti i file (*.*) Import SysEx Files Importa File SysEx Export SysEx File Esporta File SysEx Warning Attenzione The SysEx file already exists: "%1" Do you want to replace it? Il file SysEx esiste già: "%1" Vuoi sostituirlo? About to replace SysEx: "%1" Are you sure? Sto per sostituire il SysEx: "%1" Sei sicuro? About to delete SysEx: "%1" Are you sure? Sto per cancellare il SysEx: "%1" Sei sicuro? SysEx settings have been changed. Do you want to apply the changes? Le impostazioni SysEx sono state modificate. Vuoi applicare i cambiamenti? Error Errore SysEx could not be loaded: "%1". Sorry. Impossibile caricare SysEx: "%1". Spiacente. qtractorMidiThumbView MIDI Thumb view Vista anteprima MIDI qtractorMidiToolsForm MIDI Tools Strumenti MIDI Preset name Nome Preset Save preset Salva Preset Delete preset Elimina Preset &Quantize &Quantizza Quantize selected events Quantizza gli eventi selezionati &Time: &Tempo: Quantize time Quantizza tempo Quantize time percent Percentuale quantizzazione tempo % % &Duration: &Durata: Quantize duration Quantizza durata Quantize duration percent Percentuale quantizzazione durata S&wing: S&wing: Swing-quantize time Tempo quantizzazione swing Swing-quantize percent Percentuale quantizzazione swing Swing-quantize type Tipo quantizzazione swing Linear Lineare Quadratic Quadratica Cubic Cubica &Scale: &Scala: Scale-quantize key Chiave scala quantizzazione Scale-quantize type Tipo scala quantizzazione &Transpose &Trasposizione Transpose selected events Trasponi eventi selezionati &Note: &Nota: Transpose note Trasponi nota Transpose time Trasponi tempo Transpose time format Formato trasposizione tempo Frames Campioni Time Tempo BBT BBT &Reverse Inve&rtita &Normalize &Normalizza Normalize selected events Normalizza eventi selezionati &Percent: &Percentuale: Normalize percent Percentuale normalizzazione &Value: &Valore: Normalize value Valore normalizzazione &Compress &Randomize &Randomizza Randomize selected events Randomizza eventi selezionati Randomize note/pitch Randomizza nota/altezza Randomize time Randomizza tempo Randomize duration Randomizza durata Randomize value Valore randomizzazione Resi&ze &Ridimensiona Resize selected events Ridimensiona eventi selezionati Resize duration Ridimensiona durata Resize duration format Formato ridimensionamento durata Resize value Valore ridimensionamento Resize value mode Modalità valore ridimensionamento Flat Piatto Ramp Rampa Resize final value Valore ridimensionamento finale &Legato: Legato trim/extend type Normal Normale Trim Extend Legato trim/extend length Legato mode Mono Poly &Join &Split: Split notes length Split notes offset Relative Absolute Re&scale &Scala Rescale selected events Scala eventi selezionati Rescale time Scala tempo Rescale duration Scala durata Rescale value Valore scalamento &Invert &Inverti T&imeshift T&imeshift Timeshift selected events Eventi selezionati del timeshift Timeshift Timeshift P: P: Timeshift parameter Parametro timeshift Timeshift parameter (log) Parametro timeshift (log) Timeshift curve Curva timeshift P = 0 : no change. P > 0 : accelerating shift. P < 0 : slowing down shift. Edit head/tail (blue) markers define the shift range. P = 0 : nessun cambiamento. P > 0 : accelerazione shift. P < 0 : decelerazione shift. Modificando i marcatori di testa/coda (blu) viene definito l'intervallo di shift. Timeshift duration Durata timeshift T&empo ramp Tempo ramp selected events Tempo ramp From Tempo ramp start to Temporamp end Edit head/tail (blue) markers define the ramp range. Tempo ramp duration (default) (default) Warning Attenzione About to delete preset: "%1" Are you sure? Sto per eliminare il preset: "%1" Sei sicuro? none nessuno quantize quantizza transpose trasposizione normalize normalizza randomize randomizza resize ridimensiona rescale scala timeshift timeshift temporamp qtractorMixer Inputs Ingressi Tracks Tracce Outputs Uscite Mixer Mixer qtractorMixerMeter Pan qtractorMixerRackWidget &Inputs &Ingressi &Outputs Uscite &Monitor &Monitor &Buses... &Bus... &Audio &Audio &MIDI &MIDI qtractorMixerStrip inputs ingressi outputs uscite Connect %1 Connetti %1 (Audio) (Audio) (MIDI) (MIDI) (None) (Nessuno) In Out qtractorMonitorButton monitor monitor qtractorOptionsForm &General &Generale Session Sessione Default session &file format: Formato &file sessione predefinita: Default session file format (suffix) Formato file sessione predefinita (suffisso) Whether to create new sessions based on template Quando creare una nuova sessione da un template &New session template: &Nuovo template di sessione: New session template Nuovo template di sessione Browse for new session template Sfoglia per nuovo template di sessione Whether to save backup versions of existing sessions Quando salvare delle versioni di backup delle sessioni correnti Save &backup versions of existing sessions: Salva versioni di &backup delle sessioni correnti: Which mode to rename existing session files Quale modalità per rinominare file di sessioni correnti Increment previous version (default) Incrementa versione precedente (predefinito) Increment current version Incrementa la versione corrente Whether to enable session auto-save (crash-recovery) Quando abilitare il salvataggio automatico della sessione (recupero da crash) Auto-save current working session every: Salva automaticamente la sessione di lavoro ogni: Auto-save period (minutes) Intervallo di salvataggio automatico (minuti) minutes minuti Options Opzioni Whether to ask for confirmation on removal Quando chiedere una conferma per la eliminazione &Confirm removals &Conferma rimozioni Number of &recent files: Numero di file &recenti: The maximum number of recent files to keep in menu Numero massimo di file recenti da mostrare nel menu Whether to capture standard output (stdout/stderr) into messages window Cattura lo standard output (stdout/stderr) nella finestra messaggi Capture standard &output Cattura standard &output Whether to show the complete directory path of loaded session files Mostra percorso completo dei file di sessione caricati S&how complete path of session files Mostra percorso completo dei file di sessione caricati Whether to remove audio peak files on session close Rimuovi file audio picchi alla chiusura della sessione Auto-remove audio pea&k files Rimuovi automaticamente file di picco Whether to keep all tool windows on top of the main window Mantieni tutte le finestre strumenti sopra la finestra principale Keep tool &windows always on top Mantieni finestre strumenti sempre sopra Whether to try dropping multiple audio files into the same track Cerca di caricare file audio multipli nella stessa traccia &Drop multiple audio files into the same track Caricare file audio multipli nella stessa traccia Whether to reverse keyboard modifiers role on transport positioning (Shift/Ctrl) Whether to reverse mouse middle-button role on keyboard modifiers (Shift/Ctrl) Se invertire il comportamento del bottone centrale del mouse tramite modificatori da tastiera (Shift/Ctrl) Re&verse middle-button modifier role (Shift/Ctrl) Invertire il comportamento del bottone centrale del mouse tramite modificatori da tastiera (Shift/Ctrl) Transport Avanzamento Transport &mode: Modalità avanzamento Transport control mode (JACK) Modalità controllo avanzamento (JACK) None Nessuno Slave Slave Master Master Full Full &Loop recording mode (takes): Modalità registrazione &loop (riprese): Loop recording mode (takes) Modalità registrazione loop (riprese) First Primo Last Ultimo &Audio &Audio Capture / Export Cattura / Esporta Audio compression quality to use on capture (record) and export Qualità compressione audio da usare durante la cattura (registrazione) e esportazione Audio sample format to use on capture (record) and export Formato campionamento audio da usare durante la cattura (registrazione) e esportazione Audio file type to use on capture (record) and export Tipo file audio da usare durante la cattura (registrazione) e esportazione File &type: &Tipo file: Whether to keep all editor windows on top of the main window Keep &editor windows always on top Sample &format: &Formato campionamento: &Quality: &Qualità: Playback Riproduzione Whether to apply time-stretching when tempo changes Applica adattamento tempo quando il tempo cambia Aut&omatic time-stretching Adattament&o automatico tempo Sample-&rate converter type: Tipo di conve&rsione frequanza campionamento: Sample-rate converter quality Qualità conversione frequenza di campionamento Sinc (Best Quality) Sinc (Qualità migliore) Sinc (Medium Quality) Sinc (Qualità media) Sinc (Fastest) Sinc (Più veloce) Zero Order Hold Zero Order Hold Linear Lineare Whether to use WSOLA time-stretching Usa adattamento tempo WSOLA &WSOLA time-stretching Adattamento tempo &WSOLA Whether to use RubberBand formant preserve RubberBand &formant preserve Whether to apply WSOLA quick seek time-stretching Applica ricerca veloce adattamento tempo WSOLA WSOLA quic&k seek Ricerca veloce WSOLA Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine Whether to have separate audition/pre-listening player output ports Porte di uscita separate per anteprima/pre-ascolto Dedicated au&dition/pre-listening player outputs: Porte di uscita &dedicate per anteprima/pre-ascolto: Whether to auto-connect dedicated audio player outputs Connessione automatica porte di uscita dedicate Auto-&connect &Connessione automatica Connections Connessioni Whether to warn about audio self-connections &Warn about self-connections Metronome Metronomo Whether to enable the audio metronome Abilita metronomo audio &Enable audio metronome Abilita m&etronomo audio &Count-in: Count-in mode Recording Count-in number beats &File (bar): &File (barra): Metronome Audio filename (bar) Nome file audio metronomo (barra) Browse for sample audio file (bar) Sfoglia file audio campione (barra) &Gain (bar): &Guadagno (barra): Metronome gain (bar) Guadagno metronomo (barra) dB dB &File (beat): &File (battito): Metronome Audio filename (beat) Nome file audio metronomo (battito) Browse for sample audio file (beat) Sfoglia file audio campione (battito) &Gain (beat): &Gain (battito): Metronome gain (beat) Guadagno metronomo (battito) Whether to have separate audio metronome output ports Porte audio separate per metronomo Dedicated a&udio metronome outputs: Uscite a&udio dedicate metronomo: Whether to auto-connect dedicated audio metronome outputs Connessione automatica uscite audio dedicate metronomo Auto-co&nnect Co&nnessione automatica &MIDI &MIDI File &format: &Formato file: MIDI file format to use on capture (record) and export Formato file MIDI da usare durante la cattura (registrazione) e esportazione &Quantize: &Quantizzazione: MIDI capture (record) quantization Quantizzazione cattura (registrazione) MIDI Queue &timer (resolution): &Timer coda (risoluzione) Queue timer (resolution) Timer coda (risoluzione) Whether to have separate MIDI player output ports Porte di uscita separate per MIDI Dedicated MIDI p&layer outputs Porte di uscita dedicate per MIDI Whether to reset/resend all controllers on playback start &Reset all controllers on playback start Control Controllo &MMC: &MMC: MIDI Machine Control (MMC) mode Modalità MIDI Machine Control (MMC) Input Ingresso Output Uscita Duplex Duplex &Device: &Dispositivo: MIDI Machine Control (MMC) device id. ID dispositivo MIDI Machine Control (MMC) &SPP: &SPP: MIDI Song Position pointer (SPP) control mode Modalità controllo MIDI Song Position pointer (SPP) MIDI Clock control mode Modalità controllo clock MIDI Whether to have separate MIDI control ports Porte di controllo separate per MIDI Dedicated MIDI &control input/output Ingresso/uscita controllo dedicata per MIDI Whether to enable the MIDI metronome Abilita metronomo MIDI &Enable MIDI metronome Abilita m&etronomo MIDI &Channel: &Canale: Metronome MIDI channel Canale metronomo MIDI &Note (bar): &Nota (barra): Metronome MIDI note (bar) Nota metronomo MIDI (barra) &Velocity (bar): Intensità (barra): Metronome MIDI velocity (bar) Intensità metronomo MIDI (barra) &Duration (bar): &Durata (barra): Metronome MIDI duration (bar) Durata del metronomo MIDI (barra) &Note (beat): &Nota (battito): Metronome MIDI note (beat) Nota metronomo MIDI (battito) &Velocity (beat): Intensità (battito): Metronome MIDI velocity (beat) Intensità metronomo MIDI (battito) &Duration (beat): &Durata (battito): Metronome MIDI duration (beat) Durata del metronomo MIDI (battito) Whether to have separate MIDI metronome output port Porta di uscita separata per metronomo MIDI Dedicated M&IDI metronome output Uscita dedicata metronomo M&IDI Metronome MIDI offset (latency) Offset del metronomo MIDI (latenza) &Display Visualizzazione Defaults Predefinite &Time display format: Formato visualizzazione &tempo: Time display format >Formato visualizzazione tempo Frames Battute Time Tempo BBT BBT &Base font size: Dimensione &base font: Base application font size (pt.) Dimensione base font applicazione (punti) (default) (predefinito) 6 6 7 7 8 8 9 9 10 10 11 11 12 12 Whether to use desktop environment native dialogs. Decidi se utilizzare le finestre di dialogo native del tuo ambiente desktop. Use desktop environment &native dialogs Utilizza le finestre di dialogo &native del tuo ambiente desktop Manage custom color palette themes &Icons theme: Custom icons theme directory Browse for custom icons theme directory St&yle sheet: Custom style sheet (*.qss) Browse for custom style sheet (*.qss) Move down path Whether to select plugin's editor (GUI) if more than one are available Selezione dell'editor (GUI) per la plugin se più di uno è disponibile. Blacklist Plugin blacklist path Browse plugin blacklist path Add plugin blacklist path Plugin blacklist paths Remove plugin blacklist path Clear plugin blacklist paths &Clear Pulis&ci Custom Personalizzato &Color theme: Tema dei &colori: Custom color palette theme Palette personalizzata per il tema dei colori Custom widget style theme Tema personalizzato per lo stile degli oggetti Meters VU Meter &Audio: &Audio: Audio meter level Livello VU meter audio Over Sopra 0 dB 0 dB 3 dB 3 dB 6 dB 6 dB 10 dB 10 dB Audio meter color Colore VU meter audio Select custom audio meter color Seleziona colore personalizzato VU meter audio ... ... &MIDI: &MIDI: MIDI meter level Livello VU meter MIDI Peak Picco MIDI meter color Colore VU meter MIDI Select custom MIDI meter color >Seleziona colore personalizzato VU meter MIDI Reset meter colors to default Ripristina colori predefiniti VU meter &Reset &Ripristina Messages Messaggi Sample messages text font display Font messaggi campione Select font for the messages text display Seleziona font messaggi &Font... &Font... Whether to keep a maximum number of lines in the messages window Mantieni numero massimo di linee nella finestra messaggi M&essages limit: Limite m&essaggi: The maximum number of message lines to keep in view Il numero massimo di linee da mostrare nella vista lines lines Whether to hold auto-scrolling (follow play-head) on edits. Decidi se mantenere lo scorrimento automatico (segue la barra di riproduzione) durante le modifiche. Whether to ask for confirmation on archive directory removal Decidi se chiedere conferma per la rimozione di una cartella di archivio C&onfirm archive removals C&onferma rimozione di archivi Reverse &keyboard modifiers role (Shift/Ctrl) Whether to start as timebase master (JACK) Decidi se avviare un master di timebase (JACK) &Timebase &Timebase &Offset (latency): &Offset (latenza) Metronome Audio offset (latency) Offset del metronomo audio (latenza) Whether to enable MIDI queue time drift correction Decidi se abilitare una correzione per i tempi della coda MIDI E&nable MIDI queue time drift correction Abilita correzione dei tempi della coda MIDI MIDI Cloc&k: Cloc&k MIDI: &Hold auto-scrolling (follow play-head) on edits Trac&k color saturation: Default new track color saturation % % Wonton Soup DO NOT TRANSLATE KXStudio DO NOT TRANSLATE &Style theme: &Stile del tema: Back Logging Log Messages log file File messaggi di log Browse for the messages log file location Sfoglia locazione file messaggi di log Whether to activate a messages logging to file. Archivia messaggi di log su file. Messages &log file: File messaggi di &log: &Plugins &Plugins Paths Percorsi Plugin type Tipo plugin Plugin path Percorso plugin Browse plugin path Sfoglia percorso plugin Add plugin path Aggiungi percorso plugin &Add &Aggiungi Plugin paths Percorsi plugin Remove plugin path Rimuovi percorso plugin &Remove &Rimuovi Move up path Sposta su percorso &Up S&u &Down Giù &LV2 Presets directory: Directory preset &LV2: LV2 Presets directory (default: ~/.lv2) Directory preset LV2 (default: ~/.lv2) Browse LV2 Presets directory Sfoglia directory preset LV2 Instruments Strumenti Whether to have separate audio output ports Porte audio di uscita separate Dedicated audi&o outputs: Porte audi&o di uscita dedicate: Whether to auto-connect dedicated audio output ports Connessione automatica porte audio di uscita Au&to-connect Connessione au&tomatica Editor Editor Whether to open plugin's editor (GUI) by default Apri di default la GUI dell'editor delle plugin Open plugin's &editor (GUI) by default Apri di default la GUI dell'&editor delle plugin &Select plugin's editor (GUI) if more than one are available &Seleziona l'editor (GUI) per la plugin se più di uno è disponibile XML Default (*.%1) XML Regular (*.%1) ZIP Archive (*.%1) (Any) (Qualsiasi) Warning Attenzione Some settings have been changed. Do you want to apply the changes? Alcune impostazioni sono state modificate. Vuoi applicare le modifiche? Metronome Bar Audio File File Audio Barra Metronomo Metronome Beat Audio File File Audio Battito Metronomo Open Style Sheet Style Sheet files (*.%1) Icons Theme Directory Audio Meter Color Colore del meter audio MIDI Meter Color Colore del meter MIDI Plug-in Directory Cartella Plug-in LV2 Presets Directory Directory dei Preset LV2 Plug-in Blacklist Plug-in files (*.%1) Messages Font Font dei messaggi Messages Log Messaggi di Log Log files (*.%1) File di Log (*.%1) All files (*.*) Tutti i file (*.*) Session Template Template Sessione Session template files (*.qtr *.qts *.%1) File Template Sessione (*.qtr *.qts *.%1) qtractorPaletteForm Color Themes Name Nome Current color palette name Save Salva Save current color palette name Delete Delete current color palette name Palette Current color palette Generate: Base color to generate palette Reset Reset all current palette colors Import a custom color theme (palette) from file Import... Export a custom color theme (palette) to file Export... Show Details Import File - %1 Palette files (*.%1) Save Palette - %1 All files (*.*) Tutti i file (*.*) Warning - %1 Could not import from file: %1 Sorry. Export File - %1 Some settings have been changed. Do you want to discard the changes? Some settings have been changed: "%1". Do you want to save the changes? qtractorPaletteForm::PaletteModel Color Role Active Attiva Inactive Disabled qtractorPasteRepeatForm Paste Repeat Ripeti Incolla Repeat Ripeti &Count: &Conta: Repeat count Ripeti conta &Period: &Periodo: Repeat period Ripeti periodo Repeat period format Ripeti formato periodo Frames Campioni Time Tempo BBT BBT Warning Attenzione Some settings have been changed. Do you want to apply the changes? Alcune impostazioni sono state modificate. Vuoi applicare le modifiche? qtractorPluginForm Plugin Properties Proprietà delle plugin Open preset Apri preset Preset name Nome preset Save preset Salva preset Delete preset Elimina preset Alias: Plugin alias Edit plugin Modifica plugin Edit Modifica Active Attiva About Informazioni su Outputs (Sends) Uscite (Mandate) Sends Mandate Inputs (Returns) Ingressi (Ritorni) Returns Ritorni Auto-connect Aux Send Bus: Bus Mandata Aux: Manage buses Gestione bus ... ... Audio bus I/O matrix I/O Matrix... Direct Access Parameter Parametro Accesso Diretto Direct Access Accesso Diretto Page %1 Pagina %1 %1 [%2], %3 instance(s), %4 channel(s). %1 [%2], %3 istanze, %4 canali. (none) (nessuno) Open Preset Apri Preset Preset files (*.%1) File preset (*.%1) All files (*.*) Tutti i file (*.*) Error Errore Preset could not be loaded from file: "%1". Sorry. Il preset non può essere caricato dal file: "%1". Spiacente. Save Preset Salva Preset Preset could not be saved to file: "%1". Sorry. Il preset non può essere salvato sul file: "%1". Spiacente. Warning Attenzione About to delete preset: "%1" (%2) Are you sure? Sto per cancellare il preset: "%1" (%2) Sei sicuro? Latency: %1 ms (%2 frames) (no latency) &None &Nessuno qtractorPluginListView copy plugin copia plugin activate all plugins attiva tutte le plugin deactivate all plugins disattiva tutte le plugin remove all plugins rimuovi tutte le plugin Import Plugins XML files (*.%1) All files (*.*) Tutti i file (*.*) Warning Attenzione About to remove and import all plugins: "%1" Are you sure? Export Plugins Aux Send: &Move Here Sposta qui &Copy Here &Copia qui C&ancel &Annulla &Add Plugin... &Aggiungi plugin... I&nserts &Audio &Audio Add &Insert Aggiungi &Insert Add &Aux Send Aggiungi Mandata &Aux &Sends Mandate &Returns &Ritorni &MIDI &MIDI Add &Controller Ac&tivate A&ttiva Acti&vate All Atti&va Tutto Deactivate Al&l Disattiva Tutto &Remove &Rimuovi Re&move All Ri&muovi Tutto Move &Up Sposta S&u Move &Down Sposta Giù Pre&set Dire&ct Access &None &Nessuno &Properties... &Proprietà... &Edit Modifica &Import... &Importa... E&xport... &Outputs Uscite &Dedicated &Dedicate &Auto-connect Connessione &Automatica qtractorPluginParamWidget Open File Apri file qtractorPluginSelectForm Plugins Plugins Reset filter Ripristina filtro X X Plugin search string (regular expression) Stringa ricerca plugin (espressione regolare) Plugin type Tipo plugin Available plugins Plugin disponibili Name Nome Audio Audio MIDI MIDI Control Controllo Modes Modalità Path Percorso Index Indice Instances Istanze Type Tipo Plugin scanning in progress... Scansione plugin in corso... Rescan for available plugins (refresh) Ricerca plugin disponibili (refresh) &Rescan &Ricerca GUI GUI EXT EXT RT RT qtractorSessionForm Session Sessione &Name: &Nome: Session name Nome sessione &Directory: &Directory: Whether to auto-name the session directory &Auto Session directory Cartella sessione Browse for session directory Sflogia cartella sessione ... ... &Description: &Descrizione: Session description Descrizione sessione Properties Proprietà Time Tempo Sample &Rate: F&requenza di campionamento: Sample rate (Hz) Frequenza campionamento (Hz) 44100 44100 48000 48000 96000 96000 192000 192000 &Tempo: &Tempo: Tempo (BPM) / Signature Tempo (BPM) / Signature T&icks/Beat: T&icks/Battito: Resolution (ticks/beat; tpqn) Risoluzione (ticks/battito; tpqn) View Vista &Snap/Beat: Allineamento/Battito Snap/beat Allineamento/battito &Pixels/Beat: &Pixel/Battito Pixels/beat Pixel/battito &Horizontal Zoom: Ingrandimento Orizzontale: Horizontal Zoom (%) Ingrandimento Orizzontale (%) % % &Vertical Zoom: Ingrandimento &Verticale: Vertical Zoom (%) Ingrandimento Verticale (%) Warning Attenzione Session directory does not exist: "%1" Do you want to create it? La cartella della sessione non esiste: "%1" Vuoi crearla? Some settings have been changed. Do you want to apply the changes? Alcune impostazioni sono state modificate. Vuoi applicare le modifiche? Session Directory Cartella Sessione qtractorShortcutForm Shortcuts Scorciatoie Shortcut search string (regular expression) Menu/Action Menu/Azione Description Descrizione Keyboard Tastiera MIDI Controller Controller MIDI Search shortcuts Warning Attenzione Keyboard shortcut (%1) already assigned (%2). Scorciatoia della tastiera (%1) già assegnata (%2). Keyboard shortcuts have been changed. Do you want to apply the changes? Le scorciatoie della tastiera sono state modificate. Vuoi applicare le modifiche? MIDI Controller shortcuts have been changed. Do you want to apply the changes? Le scorciatoie del controller MIDI sono state modificate. Vuoi applicare i cambiamenti ? &MIDI Controller... Controller &MIDI... qtractorTakeRangeForm Range Intervallo Selection range Intervallo selezione &Selection &Selezione Loop range Intervallo loop &Loop &Loop Punch range Intervallo punch &Punch &Punch Time Tempo BBT BBT Edit range Intervallo modifica &Edit Modifica Custom range Intervallo custom &Custom &Custom St&art: Inizio Clip start Inizio clip Take Range Intervallo Ripresa En&d: Fine Clip offset Posizione clip Select Seleziona Current take Ripresa corrente Format Formato Time display format Formato di visualizzazione del tempo Frames Campioni Take %1 Ripresa %1 qtractorTempoAdjustForm Tempo Adjust Aggiusta Tempo Metronome Metronomo Tempo/Time signature Tempo/Time signature &Detect T&ap T&ap Range Intervallo &Start: Inizio: Range start Inizio intervallo &Beats: Time Tempo BBT BBT &Length: &Lunghezza &Tempo: &Tempo: R&eset Range length Lunghezza intervallo Range beats Intervallo battiti A&djust Aggiusta Format Formato Time display format Formato di visualizzazione del tempo Frames Campioni Warning Attenzione Some settings have been changed. Do you want to apply the changes? Alcune impostazioni sono state modificate. Vuoi applicare le modifiche? qtractorThumbView Thumb view Vista anteprime qtractorTimeScale C B# C# Db D D# Eb E Fb F E# F# Gb G G# Ab A A# Bb B Cb qtractorTimeScaleForm Bar Battuta Time Tempo Tempo Tempo &Bar: &Battuta: Tempo map / Markers Mappa del tempo / Marcatori Marker Marcatore Bar location Posizione della barra T&ime: Tempo: Time/frame location Posizione del tempo/campione &Tempo: &Tempo: T&ap T&ap Marker text Testo del marcatore ... ... Marker color Colore del marcatore Tempo Map / Markers Mappa del tempo / Marcatori Key Tempo (BPM) / Time signature &Key signature: Key signature (accidentals) Key signature (mode) - Major Minor &Marker: Tempo &scale factor: Tempo scale factor Fattore di scala del tempo App&ly App&lica Refresh tempo map Aggiorna mappa tempo Re&fresh Aggiorna Add node Aggiungi nodo &Add &Aggiungi Update node Aggiorna nodo &Update Aggiorna Remove node Rimuovi nodo &Remove &Rimuovi Close this dialog Chiudi questa finestra Close Chiudi Warning Attenzione Some settings have been changed. Do you want to apply the changes? Alcune impostazioni sono state modificate. Vuoi applicare le modifiche? About to remove tempo node: %1 (%2) %3 %4/%5 Are you sure? Sto per rimuovere il nodo temporale: %1 (%2) %3 %4/%5 Sei sicuro? Some settings have been changed. Do you want to discard the changes? Alcune impostazioni sono state modificate. Vuoi scartare le modifiche? tempo factor fattore del tempo Marker Color Colore del marker &Refresh Aggiorna qtractorTimeSpinBox &Frames &Campioni &Time &Tempo &BBT &BBT qtractorTrackForm Track Traccia &Name: &Nome: Track name description Descrizione nome traccia Track icon Icona della traccia Type Tipo Audio track type Tipo traccia audio &Audio &Audio MIDI track type Tipo traccia MIDI &MIDI &MIDI Input / Output Ingresso / Uscita Input bus name Nome bus ingresso Output bus name Nome bus uscita Manage buses Gestione bus ... ... MIDI / Instrument MIDI / Strumento &Program: &Programma: &Bank: &Banco: Bank &Select Method: Metodo &Selezione Banco: &Omni &Omni MIDI Omni: Capture All Channels MIDI Omni: Cattura Tutti i Canali &Channel: &Canale: MIDI Channel (1-16) Canale MIDI (1-16) MIDI Patch: Instrument Patch MIDI: Strumento MIDI Patch: Bank Select Method Patch MIDI: Metodo Selezione Banco MIDI Patch: Drum Mode MIDI Patch: Bank Patch MIDI: Banco MIDI Patch: Program Patch MIDI: Programma View / Colors Vista / Colori &Foreground: &Primo piano: Foreground color Colore primo piano Select custom track foreground color Seleziona colore personalizzato primo piano traccia Bac&kground: Sfondo Background color Colore sfondo Select custom track background color Seleziona colore personalizzato sfondo traccia Auto Plugins Plugins Track plugins Plugin traccia Add plugin Aggiungi plugin &Add... &Aggiungi... Remove plugin Rimuovi plugin &Remove &Rimuovi Move plugin up Sposta su plugin &Up S&u Move plugin down Sposta giù plugin &Down Giù Whether to enable plugin latency/delay compensation &Latency compensation Current total latency Normal Normale Bank MSB MSB Banco Bank LSB LSB Banco Patch Patch &Drums Batteria Drum &Kit &Kit batteria &Bass &Basso A&coustic Bass Basso a&custico &Guitar Chitarra &Electric Guitar Chitarra &elettrica &Piano &Piano &Acoustic Piano Piano &acustico &Microphone &Microfono Vi&ntage Microphone Microfono vi&ntage &Speaker Altoparlante &Trumpet &Tromba &Violin &Violino Warning Attenzione Some settings have been changed. Do you want to apply the changes? Alcune impostazioni sono state modificate. Vuoi applicare le modifiche? (No instrument) (Nessuno strumento) %1 ms (%2 frames) (no latency) (None) (Nessuno) Custom &Icon... &Icona personalizzata... Image files (%1) File di immagini (%1) All files (*.*) Tutti i file (*.*) Track Icon Icona della traccia Foreground Color Colore primo piano Background Color Colore di sfondo qtractorTrackList Nr Nr Track Name Nome Traccia Bus Bus Ch Patch Patch Instrument Strumento qtractorTrackTime Play-head Barra di riproduzione Edit-head Barra di modifica Edit-tail Barra di fine modifica Loop-start Inizio loop Loop-end Fine loop Punch-in Punch ingresso Punch-out Punch uscita Start: %1 End: %2 Length: %3 Inizio: %1 Fine: %2 Durata: %3 qtractorTrackView Zoom in (horizontal) Ingrandimento (orizzontale) Zoom out (horizontal) Rimpicciolimento (orizzontale) Zoom in (vertical) Ingrandimento (verticale) Zoom out (vertical) Rimpicciolimento (verticale) Zoom reset Ripristina ingrandimento add clip aggiungi clip Start: %1 End: %2 Length: %3 Inizio: %1 Fine: %2 Lunghezza: %3 clip %1 clip %1 fade-in fade-in fade-out fade-out clip stretch allarga clip clip resize ridimensiona clip clip repeat ripeti clip %1 automation %1 automazione %1 clip %1 clip move automation sposta automazione paste automation incolla automazione cut taglia delete elimina split dividi move clip sposta clip paste clip incolla clip qtractorTracks Tracks Tracce new clip nuovo clip mute clip split clip dividi clip clip normalize normalizza clip quantize quantizza transpose trasponi normalize normalizza randomize randomizza resize ridimensiona rescale scala timeshift timeshift clip import importa clip Audio file import "%1" on %2 %3. Importa file audio "%1" su %2 %3. Audio file import: "%1". Importa file audio: "%1". MIDI file import "%1" track-channel %2 on %3 %4. Importa file MIDI "%1" traccia-canale %2 on %3 %4. MIDI file import: "%1", track-channel: %2. Importa file MIDI: "%1", traccia-canale: %2. clip merge fondi clip Merge/Export Merge/Export Audio Clip Fondi/Esporta Clip Audio MIDI files (*.mid *.smf *.midi) File MIDI (*.mid *.smf *.midi) All files (*.*) Tutti i file (*.*) Audio clip merge/export: "%1" started... Fusione/esportazione clip audio: "%1" in corso... tempo ramp Audio clip merge/export: "%1" complete. Fusione/esportazione clip audio: "%1" completa. Merge/Export MIDI Clip Fondi/Esporta Clip MIDI MIDI clip merge/export: "%1" started... Fusione/esportazione clip MIDI: "%1" in corso... MIDI clip merge/export: "%1" complete. Fusione/esportazione clip MIDI: "%1" completa. clip cross-fade cross-fade del clip Insert Range Inserisci intervallo insert range inserisci intervallo insert track range inserisci intervallo traccia Remove Range Elimina intervallo remove range elimina intervallo remove track range elimina intervallo traccia Warning Attenzione About to remove track: "%1" Are you sure? Sto per rimuovere la traccia: "%1" Sei sicuro? MIDI file import "%1" on %2 %3. Importazione file MIDI "%1" su %2 %3. MIDI file import: "%1". Importazione file MIDI: "%1". qtractor-1.5.9/src/translations/PaxHeaders/qtractor_pt_BR.ts0000644000000000000000000000013215101070305021240 xustar0030 mtime=1761898693.104267708 30 atime=1761898693.102267702 30 ctime=1761898693.104267708 qtractor-1.5.9/src/translations/qtractor_pt_BR.ts0000644000175000001440000230556115101070305021244 0ustar00rncbcusers QObject Beat Pulso Date Data File Arquivo None Nenhuma none nenhum %1 Hz %1 Hz %1 In %1 Entrada [Mute] Start: %1 Offset: %2 End: %3 Length: %4 Início: %1 Diferença: %2 Fim: %3 Tamanho: %4 File: %1 Arquivo: %1 (Any) (Todos) Aux Send: %1 track record gravação da pista Input Entrada Signed 16-Bit Assinatura de 16-Bits Signed 24-Bit Assinatura de 24-Bits Signed 32-Bit Assinatura de 32-Bits Float 32-Bit Decimal de 32-Bits Float 64-Bit Decimal de 64-Bits SMF Format 0 Formato SMF 0 SMF Format 1 Formato SMF 1 slave escravo automation play all executar todas as automações resize track redimensionar pista remove track remover pista reset plugin resetar plugin preset plugin pré-configurações do plugin import track importar pista Show version information Mostrar informações da versão Session file (.qtr) Arquivo do projeto (.qtr) [session-file] [arquivo do projeto] Option -s requires an argument (uuid). Opção -s requer argumento (uuid). %1 Pan %1 Panorâmico %1 Out %1 Saída %1 (*.%2) %1 (*.%2) Show help about command line options Mostrar ajuda sobre opções em linha de comando (%1% vol) (%1% vol) MIDI file save: "%1", track-channel: %2. Salvar arquivo MIDI: "%1", canal da pista: %2. session loop sessão de loop Insert Send/Return pseudo-plugin (Audio) Insert Mandada/Retorno pseudo-plugin (Ãudio) Insert Send/Return pseudo-plugin (MIDI) Insert Mandada/Retorno pseudo-plugin (MIDI) Send Gain Volume da Mandada Dry Gain Ganho-Seco Wet Gain Ganho-Molhado Aux Send (Audio) Mandada Aux (Ãudio) Aux Send pseudo-plugin (Audio) Mandada Aux pseudo-plugin (Ãudio) Aux Send pseudo-plugin (MIDI) Mandada Aux pseudo-plugin (MIDI) (none) (nenhum) %1 (Audio) %1 (Ãudio) %1 (MIDI) %1 (MIDI) %1 Monitor %1 Monitoração automation record gravar automação automation select seleção de automação automation edit list editor de lista de automações Duplex Duplex Usage: %1 [options] [session-file] Uso: %1 [opções] [arquivo-da-sessão] Channel %1 Canal %1 session properties propriedades da sessão Output Saída %1 (format %2) %3 tracks, %4 tpqn %5 %1 (formato %2) %3 pistas, %4 tpqn %5 (format %1) MIDI: (formato %1) MIDI: Product: Produto: step input overdub %1 Volume %1 Volume reset takes resetar tentativas clip save clip unlink clip record gravador de sequência Project: Projeto: Select plug-in's editor (GUI): External X11 X11 (native) X11 (nativo) Gtk2 Gtk2 (native) Gtk2 (nativo) Qt4 Qt4 Qt5 Qt5 Other Outro Don't ask this again Não pergunte novamente plugin parameters parametros do plugin Open File lv2_ui_request_parameter parametro_para_requisição_da_interface_lv2 Abrir Arquivo Automation Automação automation clear apagar automação automation color cor da automação add track adicionar pista add insert adicionar Insertor add MIDI controller aux-send matrix move plugin mover plugin plugin program programação do plugin plugin alias import plugins importar plugins add marker adicionar marcador add key signature adicionar fórmula de compasso update key signature atualizar fórmula de compasso remove key signature remover fórmula de compasso move marker mover marcador change time-sig. alterar formula-compasso. add plugin adicionar plugin move track mover pista %1 (%2) %3 channels, %4 frames, %5 Hz %6 %1 (%2) %3 canais, %4 amostras, %5 Hz %6 dedicated audio outputs saídas de áudio dedicadas track pan panorâmico da pista Options: Opções: set controller setar controlador reset controller resetar controlador direct access param acesso direto a parâmetros (take %1/%2) (toma %1/%2) %1 (%2) %1 (%2) %1 Gain %1 Ganho Name: %1 Nome: %1 (%1 semitones pitch shift) (%1 mudança de altura em semitons) Track %1 Pista %1 %1 Bank %2 %1 Banco %2 update bus atualizar canal primário move bus mover canal primário create bus criar canal primário Copyright: Direitos: Set session identification (uuid) Nomear sessão (uuid) Version: Versão: Manual: Manual: Support: Ajuda: session punch renderizar sessão bus pan panorâmico do canal primário duplicate track duplicar pista track solo pista solo track gain ganho da pista track instrument pista de instrumento track mute deixar pista muda automation record all gravar todas as automações track monitor monitoração da pista take %1 toma %1 (%1 dB) (%1 dB) (%1 pan) (%1 panorâmico) (%1% time stretch) (%1% distorcer-tempo) add aux-send adicionar mandada %1 - Bank %2 %1 - Banco %2 remove tempo node remover nó de tempo All files (*.*) Tudo (*.*) update marker atualizar marcador move tempo node mover nó de tempo Audio: %1 channels, %2 Hz Ãudio: %1 canais, %2 Hz (default) (padrão) Audio files (%1) Arquivos de áudio (%1) Vendor: Fornecedor: Unknown Desconhecido bus gain volume do canal primário activate plugin ativar plugin Track assignment failed: Track: "%1" Input: "%2" Output: "%3" A nomeação da Pista falhou: Pista: "%1" Entrada: "%2" Saída: "%3" delete bus apagar canal primário Author: Autor: update tempo node nó de atualização de tempo , %1 tracks, %2 tpqn , %1 pistas, %2 tpqn add tempo node adicionar nó de tempo bus pass-through repassar canal primário clip tool %1 ferramentas para sequências %1 Cakewalk Instrument Definition File Arquivo de definições de Instrumento Cakewalk aux-send bus mandada de canal principal track properties propriedades da pista Automation (%1) Automação (%1) automation play executar automação automation edit editor de automação automation mode modo de automação automation logarithmic automação logarítmica automation clear all apagar todas as automações remove plugin remover plugin %1 (format %2) %3 %1 (formato %2) %3 remove marker remover marcador Activate Ativar %1: Automation/curve file not found. %1: Arquivo de automação/curva não encontrado. %1(%2): %3 plugin not found. %1(%2): %3 plugin não encontrado. Name: Nome: Category: Categoria: Categories: Categorias: %1 Record %1 Mute %1 Solo MIDI Controller: %1, %2, %3 Control (MIDI) MIDI Controller Send pseudo-plugin Value Valor qtractorAudioIOMatrixForm Aux-Send I/O Matrix Warning Atenção Some settings have been changed. Do you want to apply the changes? qtractorAudioListView Ch Cnl Name Nome Path Local Rate Taxa de Amostragem Time Tempo Open Audio Files Abrir Arquivos de Ãudio Frames Amostras %1: Audio file not found. %1: Arquivo de áudio não encontrado. qtractorAudioMixerMeter Gain (dB) Volume (dB) dB dB Pan: %1 Gain: %1 dB Volume: %1 dB qtractorBusForm Ch Cnl &Up Para Ci&ma Bus Canal Primário MIDI MIDI Mode Modo &Down &Para Baixo Audio Ãudio Buses Canais Primários Close Fechar Input Entrada Move output plugin up Mover Plugin na Saída para Cima Add input plugin Adicionar Plugin na entrada Input bus plugins Plugins aplicados na entrada do Canal Primário MIDI SysEx setup Opções do MIDI SysEx Close this dialog Fechar esta caixa de diálogo Add output plugin Adicionar Plugin na Saída MIDI Instrument name Nome do Instrumento MIDI &Mode: &Modo: &Name: &Nome: Move &Up Mover Para &Cima Move &Down Mover para &Baixo (none) (nenhum) (1 item) Bus monitor (pass-through) Monitoração de canal primário (segue o passo) Duplex Duplex &Auto connect &Auto-conectar Some settings have been changed. Do you want to apply the changes? Algumas configurações foram modificadas. Você quer mesmo manter as modificações? Output Saída Audio channels Canais de Ãudio Output bus plugins Plugins na Saída do Canal Primário Properties Propriedades (%1 items) (%1 itens) Input Plugins Plugins da Entrada SysE&x... Update bus Atualizar lista de Canais Primários Create bus Criar Canal Primário (No instrument) (Sem instumento) Remove input plugin Retirar Plugin da entrada M&onitor (pass-through) M&onitoração de Canal Primário (segue o passo) &Add... &Adicionar... Move bus up towards the top Mover Canal Primário em direção ao topo U&p &Sobe Move bus down towards the bottom Mover Canal Primário em direção ao fim Do&wn &Desce &Create &Criar &Delete A&pagar Move input plugin up Mover Plugin para Cima About to remove bus: "%1" (%2) Are you sure? Serão excluidos os Canais Primários: "%1" (%2) Posso continuar? Output Plugins Plugins da Saída &Remove &Remover &Update &Atualizar Bus list Lista de Canais Primários Bus mode Tipo de canal primário Bus name Nome do canal primário Audio auto-connect Auto-conectar Ãudio Delete bus Eliminar Canal Primário Cha&nnels: Ca&nais: Warning Atenção Remove output plugin Retirar Plugin da Saída Move input plugin down Mover Plugin para Baixo Move output plugin down Mover Plugin da Saída para Baixo Some settings have been changed. Do you want to discard the changes? Algumas configurações foram modificadas. Você deseja descartar essas alterações? qtractorClientListView Readable Clients / Output Ports Saídas Writable Clients / Input Ports Entradas qtractorClipForm % dB BBT CPS (Compasso Pulso Subpulso) MIDI MIDI Time Tempo (h:m:s) Clip name Nome da Sequência Audio Ãudio edit clip editar sequência Clip fade-out length Extensão da diminuição de volume no fim da Sequência Clip start Começo da sequência %1 Clip File %1 Arquivo de Sequência &File: &Arquivo: &Gain: &Ganho: &Name: &Nome: Pitch S&hift: &Mudança de Altura: &Length: Com&primento: semitones semitons Clip gain/volume Volume/Ganho da Sequência Clip fade-out type Tipo de Fade de Saída Fade In/Out Fade de Entrada/Saída Frames Amostras Some settings have been changed. Do you want to apply the changes? Algumas configurações foram modificadas. Você quer mesmo manter as modificações? Clip filename Nome do arquivo da Sequência Browse for clip file Procurar por arquivo para sequência Clip track/channel Pista da sequência/canal &Panning: &Panorâmico: Forma&t: &Formato: Ti&me Stretch: Time-&stretch: Clip time-stretch percentage Porcentagem de "distorcer-tempo" de uma Sequência (Devagar/Rápido) Whether to use WSOLA time-stretching Utilizar ou não "esticar tempo" (time-stretching) do tipo WSOLA &WSOLA time-stretching "Esticar-tempo" do tipo &WSOLA Whether to use RubberBand formant preserve RubberBand &formant preserve Whether to apply WSOLA quick seek time-stretching Se for usar WSOLA mudança rápida para esticar-tempo (time-stretching) WSOLA quic&k seek WSOLA mudança &rápida Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine &Mute &Mudo Clip offset Deslocamento da Sequência Fade &Out: Fade de &Saída: Clip length Tamanho da Sequência Clip pitch-shift in semitones Mudança de altura em semitons de uma Sequência (Grave/Agudo) Parameters Parâmetros Clip Sequência Track/&Channel: Pista/&Canal: Fade &In: Fade &Entrada: Clip fade-in type Tipo de Fade de Entrada &Start: &Início: new clip nova sequência Clip fade-in length Extensão do aumento de volume no começo da Sequência Offs&et: De&slocamento: Linear Linear Quadratic 1 Quadratica 1 Quadratic 2 Quadratica 2 Quadratic 3 Quadratica 3 Cubic 1 Cúbica 1 Cubic 2 Cúbica 2 Cubic 3 Cúbica 3 &Volume: MIDI files (*.%1 *.smf *.midi) Arquivos MIDI (*.%1 *.smf *.midi) All files (*.*) Todos os arquivos (*.*) Time display format Formato de exibição do tempo Warning Atenção qtractorConnect Refresh Atualizar Disconnect Desconectar Connect Conectar Disconnect All Desconectar Tudo qtractorConnectForm MIDI MIDI (All) (Todas) Audio Ãudio Select input client/ports Selecionar Entradas Disconnect &All Desconectar &Tudo &Refresh &Atualizar Connect currently selected ports Conectar item selecionado Connections Conexões Disconnect currently selected ports Desconectar item selecionado Refresh current connections view Atualizar conexões de Entrada e Saída &Connect &Conectar &Disconnect &Desconectar Select output client/ports Selecionar Saídas Disconnect all currently connected ports Desconectar todas as Entradas e Saídas qtractorConnections Connections Conexões qtractorEditRangeForm BBT CPS (Compasso Pulso Subpulso) Time Tempo (h:m:s) &Edit &Editar &Loop &Loop Edit Range Limites da Edição En&d: &Final: L&oop L&oop Range Limites Apply to Loop points in range Aplicar ao Loop dentro do Limite Loop range Limites do Loop Apply to Tempo Map nodes in range Aplicar aos nós do Mapa de Tempo dentro do Limite Clip start Começo da Sequência &Punch Gravação &Localizada &Selection &Seleção Selection range Limites da Seleção Apply to Punch In/Out points in range Aplicar Gravação Localizada nos pontos e Início e Fim Apply to Automation nodes in range Aplicar nós da Automação dentro do Limite Te&mpo Map Mapa de Te&mpo Cl&ips Se&quência Frames Amostras Pu&nch Gravação &Localizada Options Opções A&utomation &Automação Edit range Limites da Edição Clip offset Final da Sequência Mar&kers Mar&cadores Apply to location Markers in range Aplicar aos Marcadores de localização dentro do Limite Punch range Limites da Gravação Localizada Apply to clips in range Aplicar às Sequências dentro dos Limites St&art: &Início: &Custom &Personalizado &Format &Formato Custom range Limites Personalizados Time display format Mostra o formato de Tempo qtractorExportClipForm %1 %2 Clips %1 %2 Sequências qtractorExportForm BBT CPS (Compasso Pulso Subpulso) MIDI MIDI Time Tempo (h:m:s) &Edit &Editar Export Exportar File &type: &Tipo de arquivo: Audio file type to use on export Tipo de arquivo de áudio utilizado na exportação Sample &format: &Formato da Amostra: Audio sample format to use on export Formato da amostra de áudio para usado na exportação &Quality: &Qualidade: Audio compression quality to use on export Qualidade da compressão de áudio a ser usada na exportação File &format: &Formato do arquivo: MIDI file format to use on export Formato do arquivo arquivo MIDI a ser usado na exportação &Loop &Loop Audio Ãudio En&d: Fin&al: Range Limites Loop range Limites do Loop &File: &Arquivo: Output bus names Nomes das saídas dos Canais Primários &Punch Gravação &Localizada All files (*.*) Todos os arquivos (*.*) Format Formato Frames Amostras Export file name Nome do arquivo Renderizado MIDI files (*.%1 *.smf *.midi) Arquivos MIDI (*.%1 *.smf *.midi) Edit range Limites da Edição Outputs Saídas Export %1 File Renderizando %1 do Arquivo Session range Limites da Sessão &Session &Sessão Punch range Limites da Gravação Localizada St&art: &Início: Custom start Início padrão Custom end Final padrão Whether to add/import new track(s) with export result Sempre que adicionar/importar novas pista(s) exportar com os resultados &Add new track(s) &Adicionar nova pista &Custom Limites &Personalizados Custom range Limites da Personalização Time display format Mostra o formato do tempo Browse export file name Procurar nome do arquivo Renderizado qtractorExportTrackForm %1 %2 Tracks %1 %2 Pistas Warning Atenção The file already exists: "%1" Do you want to replace it? Esse arquivo já existe: "%1" Você quer substituí-lo? Audio file export: "%1" started... A Renderização do arquivo de áudio: "%1" começou... Audio file export: "%1" complete. A Renderização do arquivo de áudio: "%1" terminou. Audio file export: "%1" failed. A Renderização do arquivo de áudio: "%1" falhou. MIDI file export: "%1" started... A Renderização do arquivo MIDI: "%1" começou... MIDI file export: "%1" complete. A Renderização do arquivo MIDI: "%1" terminou. MIDI file export: "%1" failed. A Renderização do arquivo MIDI: "%1" falhou. qtractorFileListView file arquivo group grupo About to remove %1 file item(s). Are you sure? Sobre apagar i item(s) %1. Posso continuar? About to remove %1 item: "%2" Are you sure? Será excluido o item %1 : "%2" Posso continuar? New Group Novo Grupo Warning Atenção qtractorFileSystem &Home &Raiz &Up &Acima Al&l Files &Todos os Arquivos &Session &Sessão &Audio &Ãudio &MIDI &MIDI H&idden &Esconder &Play &Tocar File System Sistema de Arquivos qtractorFiles Del Cu&t &Recortar MIDI MIDI &Copy &Copiar Audio Ãudio Files Arquivos Pla&y &Tocar Audio Files Arquivos de Ãudio &Paste Co&lar Ctrl+C Ctrl+V Ctrl+X Re&name Re&nomear Play file Tocar arquivo Add &Files... &Adicionar Arquivos... MIDI Files Arquivos MIDI &Remove &Remover New &Group... Novo &Grupo... Cl&eanup L&impar qtractorInstrumentForm &Up Para Ci&ma Path Local &Down &Para Baixo Close Fechar Files Arquivos NRPN Names Nomes dos NRPN's RPN Names Nomes dos RPN's Names Nomes Patch Programação Close this dialog Fechar esta Janela de diálogo The instrument file already exists: "%1" Do you want to replace it? Já existe o Arquivo de Instrumento: "%1" Você quer substituí-lo? Export to instrument file Exportar para um Arquivo de Instrumento Import Instrument Files Importar Arquivo de Instrumento Normal Normal Move instrument file down on list order Move Instrumento para baixo na lista Controller Names = %1 Nome do Controlador = %1 Bank Select Method = %1 Método para seleção de Bancos = %1 Note Names Nomes das Notas Bank Select Methods Método para seleção de Bancos &Import... &Importar... E&xport... E&xportar... Instrument files (*.%1 *.sf2 *.sf3 *.midnam) Arquivos de instrumento (*.%1 *.sf2 *.sf3 *.midnam) All files (*.*) Todos os arquivos (*.*) %1 = %2 %1 = %2 Bank LSB Banco LSB Bank MSB Banco MSB Remove instrument file Excluir arquivo com Instrumento Export Instrument File Exportar Arquivo de Instrumento RPN Names = %1 Nomes dos RPN = %1 NRPN Names = %1 Nomes dos NRPN = %1 Instruments Instrumentos Patch Names Nomes das Programações Instrument settings have been changed. Do you want to apply the changes? Algumas configurações do Instrumento foram modificadas. Você quer mesmo manter as modificações? &Remove &Remover Move instrument file up on list order Move instrumento para cima na lista Unknown Desconhecido Patch Names for Banks Nomes das Programações para os Bancos Warning Atenção Instrument files (*.%1) Arquivos de instrumento (*.%1) Based On = %1 Baseado em = %1 Controller Names Nomes dos Controladores Import from instrument file Importar de um Arquivo com Instrumento qtractorInstrumentMenu (None) (Nada) qtractorMainForm F2 F2 F4 F4 F5 F5 F6 F6 Instrum&ent &Instrumto &Duplicate Track &Duplicar Pista Duplicate Track Duplicar Pista Duplicate track Duplicar Pistar Duplicate current track Duplicar pista selecionada &Minimize Minimize Height Minimize height Minimize track height Auto Dea&ctivate &Auto Desativar Auto Deactivate Auto Desativar Auto-deactivate plugins Auto desativar plugins Auto-deactivate plugins not producing sound Auto desativar plugins que não estão produzindo sons Shift+F6 Shift+F6 Mute Clip Mute clip Mute current clip Recor&d &Gravar Record Clip Gravar Sequência Record clip Gravar sequência Record current clip (overdub) Gravar sequência selecionada (overdub) Normali&ze &Normalizar T&empo ramp... Tempo ramp Clip Tempo ramp clip events Tempo ramp current MIDI clip events F7 F7 &Cross Fade &Crossfade Clip Cross-fade Crossfade da Sequência Clip cross-fade Crossfade da sequência Cross-fade current overlapped clips Crossfade as sequências sobrepostas File &System &Sistema de Arquivos File System Sistema de Arquivos File system Sistema de arquivos Show/hide the file system window Mostrar/Esconder a janela do sistema de arquivos F8 F8 F9 F9 &In &Aumentar &Up Para &Cima F12 F12 Del Auto Desativar Cut Recortar MOD MOD New Novo REC GRAVAR REW RETROCEDER or ou &All &Tudo &New &Novo &Out &Diminuir &Top &Topo Clip Sequência Copy Copiar Cu&t &Recortar FFWD ANTERIOR Exit Sair Grid Grade LOOP LOOP MUTE MUDO Loop Loop PLAY TOCAR None Nenhuma Open Abrir Play Tocar SOLO SOLO Session XRUN state Condição de XRUN na Sessão Session buffer size Tamanho do buffer do Projeto All files (*.*) Todos os arquivos (*.*) Don't ask this again Não pergunte novamente A directory with same name already exists: "%1" This directory will be replaced, erasing all its current data, when opening and extracting this archive in the future. Do you want to continue? Pasta com o mesmo nome já existe: "%1" Esta pasta será substituída apagando to o conteúdo anterior, quando abrir ou extrair este conteúdo no futuro. Você deseja continuar assim mesmo? The directory is an extracted archive: "%1" This directory will be removed, erased from all its current data, when closing this session. Do you want to continue? Player panic! Pânico na execução! Beat-detection support (libaubio) disabled. Suporte a Detecção de ritmo (libaudio) desabilitado. VST2 Plug-in support disabled. Suporte a Pugins VST3 desabilitado. {2 ?} VST3 Plug-in support disabled. Suporte a Pugins VST3 desabilitado. CLAP Plug-in support disabled. Suporte a plugins CLAP desabilitado. LV2 Plug-in MIDI/Event support (DEPRECATED) enabled. Plugin MIDI LV2/Suporte a eventos (SEM SUPORTE) ativado. LV2 plug-in State Make Path support (DANGEROUS) enabled. Suporte a State Make Path de plugins LV2 (PERIGOSO) ativado. LV2 Plug-in MIDNAM support disabled. Suporte a LV2 Plug-in MIDINAM desabilitado. LV2 Plug-in UI GTK2 native support disabled. Suporte nativo a GTK2 para Interface de Plugin LV2 desativado. LV2 Plug-in UI GTKMM2 native support disabled. LV2 Plug-in UI X11 native support disabled. Suporte nativo a X11 para Interface de Plugin LV2 desativado. Using: Qt %1 Usando: Qt %1 Don't show this again The audio engine buffer size has changed, increased from %1 to %2 frames/period. Reloading the current session file is highly recommended. O tamanho do buffer da interface da áudio foi modificado, aumente de %1 para %2 amostras/período. É altamente recomendável recarrgar a sessão atual. STOP PARAR Rect Ret Redo Refazer Save Salve Stop Parar Transport fast forward Botão de Adiantar dos Controles de Gravação e Reprodução Undo Desfazer Template files (*.%1) Arquivos de Modelos (*.%1) Cut selection to clipboard Recortar seleção para a área de transferência Monitor track Monitorar pista Clip Loop Loop da Sequência Clip loop Loop da sequência &Clip &Sequências &Copy &Copiar &Down Para &Baixo &Edit &Editar &File &Arquivo &Grid &Grade &Help &Ajuda &Hold &Reter &Last Ú&ltima &Lock &Trancar &Loop &Loop &Mute &Mudo &Next &Próxima &None &Nada &Play &Executar &Redo &Refazer &Save &Salvar &Solo &Solo &Stop &Parar &Undo &Desfazer &View &Visualização &Zoom &Zoom Resize Clip Diminuir Sequência The file already exists: "%1" Do you want to replace it? Esse arquivo já existe: "%1" Você quer substituí-lo? The current session has been changed: "%1" Do you want to save the changes? A seguinte Sessão foi modificada: "%1" Deseja salvar essas modificações? Insert range as selected Inserir limite às pistas que estiverem selecionadas Monitor Track Monitorar Pista Step Backward Step backward Transport step backward Step Forward Step forward Transport step forward &Count-in Count-in Transport mode: None Módulo de Controles: Nenhum Transport mode set to None Módulo de Controle determinado como Nenhum &Slave &Escravo Slave Escravo Transport mode: Slave Módulo de Controles: Escravo Transport mode set to Slave Módulo de Controle determinado como Escravo &Master &Mestre Master Mestre Transport mode: Master Módulo de Controle: Mestre Transport mode set to Master Módulo de Controle determinado como Mestre &Full &Completo Full Completo Transport mode: Full Módulo de Controles: Completo Transport mode set to Full Módulo de Controle determinado como Completo About Sobre E&xit &Sair Buses Canais Primários Error Erro Files Arquivos M&ode M&odo N&one &Nenhuma Mixer Mixer Mo&ve Mo&ver S&nap &Divisão Panic Pânico Paste Colar RESET RESETAR T&ime T&empo Punch Gravação Localizada Range Limites START COMEÇAR Ready Pronto Ta&ke &Gravação por Tentativas Space Space Track Pista Remove current track from session Remover pista atual da sessão Zebra Zebra Split Clip Quebrar Sequência Split clip Quebrar sequência Edit Clip Editar Sequência Edit clip Editar sequência Past&e Repeat... R&epetir Colar... Edit current session properties Editar propriedades da sessão atual Session started. Sessão iniciada. &Options... &Opções... Change general application program options Modificar opções gerais do programa NSM support disabled. Suporte a NSM desabilitado. View toolbar Barra de visualizações View Toolbar Barra de Visualizações LV2 Plug-in Programs support disabled. Suporte a LV2 Plug-in Programs desabilitado. Untitled%1 Sem nome%1 Automation playback Executar automação Clear all automation curves Limpar todas as curvas de Automação Exit this application program Sair deste aplicativo Paste Repeat Repetir Colar Time toolbar Barra de Ferramentas para informações de tempo Time Toolbar Barra de Ferramentas para Tempo TRACK MUTE %1 %2 MUTAR PISTAS %1 %2 Select rectangle Seleção em retângulo Transport Toolbar Barra de Controles de Gravação e Reprodução Transport toolbar Barra de ferramentas para os Controles de Gravação e Reprodução &Horizontal &Horizontal Automation curve color Cor da curva de automação &Shortcuts... Atalho&s... About to remove archive directory: "%1" Are you sure? Será removido o diretório do inventário: "%1" Posso continuar? Track Inputs Entradas da Pista Solo Track Pista Solo Zoom In Aumentar Zoom Zoom in Aumentar Zoom Some settings may be only effective next time you start this %1. Algumas das alterações só surtirão efeito na próxima vez que inciar este %1. Solo track Pista solo Set current snap to %1 Selecionar grade atual para %1 Follow playhead Seguir a reprodução Follow Playhead Seguir a Reprodução Thumb Toolbar Barra de Miniatura Thumb toolbar Barra de vista em miniatura do arranjo Split selection Quebrar seleção Split Selection Quebrar Seleção F&ollow Playhead Seguir a Reproduçã&o Impor&t Tracks Impor&tar Pistas LV2 Plug-in Worker/Schedule support disabled. Suporte a LV2 Plug-in Worker/Schedule desabilitado. Session files (*.%1 *.%2 *.%3) Arquivos de Sessões (*.%1 *.%2 *.%3) Tempo Adjust Ajuste de Andamento &MIDI... &MIDI... Make current the last track Fazer da última pista a atual Make current the next track Fazer da próxima pista a atual Transport stop Botão de Parar dos Controles de Gravação e Reprodução Transport loop Botão de Loop dos Controles de Gravação e Reprodução Transpose Clip Transpor Sequência &Add Track... &Adicionar Pista... Normalize current clip (gain/volume) Normalizar sequência atual (ganho/volume) This program is free software; you can redistribute it and/or modify it Este aplicativo é de software livre; você pode redistribuí-lo e/ou modificá-lo Show/hide the messages window Mostrar/esconder a janela de Mensagens Transport rewind Botão de Retroceder dos Controles de Gravação e Reprodução Transport record Botão de Gravar dos Controles de Gravação e Reprodução Show/hide the main program window menubar Mostrar/esconder os menus da janela do programa principal Mark range as selected Marcar limite que estiver selecionado Show/hide main program window transport toolbar Mostrar/esconder a barra de ferramentas para controles da janela do programa principal Session could not be loaded from "%1". Sorry. A Sessão não pode ser carregada de "%1". Desculpe. Clip Range Limites da Sequência Range selection mode Modo de seleção de Limites Pitch-shifting support (librubberband) disabled. Suporte a mudança de altura (librubberband) desabilitado. Options toolbar Barra de ferramentas para opções Options Toolbar Barra de Opções Clip range Limites da sequência %1 BPM %1 BPM Height reset Resetar altura Session muting state Sessão muda &Clear &Limpar XRUN(%1 skipped) XRUN(%1 ignorado(s)) &Files &Arquivos &First &Primeira Height Reset Resetar Altura Transport loop set Determinar Loop dos Controle de Reprodução &Paste &Colar &Punch Gravação &Localizada &Range &Limites &Reset &Resetar &Split &Quebrar &State &Condição &Track &Pistas JACK Session support disabled. Suporte a JACK Session desabilitado. &Zebra &Zebra Refresh views Atualizar visualizações Remove Range Remover Limites Remove Track Remover Pista All MIDI tracks shut off (panic) Interrompe todas as pistas MIDI (pânico) Track Toolbar Barra de Ferramentas para Pistas Track toolbar Barra de ferramentas para pistas Paste repeat Repetir colar All Zoom Todo o Zoom All zoom Todo o zoom Rescale current MIDI clip events Redimensionar eventos na sequência MIDI atual Save &As... S&alvar Como... Timeshift current MIDI clip events Mudança de velocidade dos eventos na sequência MIDI atual Remove range Remover limites Remove track Remover Pista Import tracks from MIDI file Importar pista de um arquivo MIDI JACK Latency support disabled. Suporte a JACK Latency desabilitado. About to clear automation: "%1" Are you sure? As seguintes automações serão resetadas: "%1" Posso continuar? Select current clip previous take Selecionar a tentativa anterior da sequência atual Automation playback all Executar todas as automações Show current track output bus connections Mostrar as saídas de canais primários da pista atual Could not set default session directory: %1 Sorry. Não foi possível definir o diretório para a Sessão: %1 Desculpe. Insert track range as selected Inserir limite às pistas que estiverem selecionadas Transport backward Botão de Voltar dos Controles de Gravação e Reprodução Track inputs Entradas da Pista Insert Range Inserir Limite Timeshift clip events Mudança de velocidade dos eventos na sequência Session looping state Sessão em Loop Insert range Inserir limite Edit current clip Editar sequência atual LV2 Plug-in Buf-size support disabled. Suporte a LV2 Plug-in Buf-size desabilitado. Punch in/out Gravação Localizada (Começo/Fim) New session file Novo arquivo de sessão Show information about this application program Mostre-me informações sobre este aplicativo Select &Mode Selecionar &Modo Select Range Selecionar Limite Menubar Barra de menu &Menubar Barra de &Menu Select Track Selecionar Pista Record Track Gravar Pista Select current clip first take Selecionar a primeira tentativa da sequência atual Audio connections change. Conexões de áudio modificadas. Show/hide the mixer window Mostrar/esconder a janela do Mixer Select range Selecionar Limites Select track Selecionar pista Record track Gravar pista Set loop-range from current clip extents Determinar os limites do loop na extensão da sequência atual Backup session: "%1" as "%2". Backup da sessão: "%1" até "%2". Show/hide the files window Mostrar/esconder a janela de Arquivos Show/hide the main program window statusbar Mostrar/esconder a barra de estado na janela do programa principal Transport punch in/out Gravação Localizada dos Controles de Gravação e Reprodução Resize clip events Diminuir eventos em uma sequência Playing ended. Reprodução completada. TRACK MONITOR %1 %2 MONITOR DA PISTA %1-%2 CONTINUE CONTINUAR Automation record Gravação de automação Session modification state Sessão em modificação Ctrl+Shift+Ins Ctrl+Shift+Ins Ctrl+Shift+Del Ctrl+Shift+Del Mo&de St&ep Move Up Mover Para Cima Move up Mover para cima Unlink Clip Desgrudar Sequências Unlink clip Desgrudar sequências Automation edit mode Modo de Edição de Automações &Controllers... &Controladores... Sample-rate conversion (libsamplerate) disabled. Conversão de taxa de resolução (libsamplerate) desabilitado. Session closed. Sessão encerrada. Punch Set Determinar Gravação Localizada &Increase &Aumentar Ctrl++ Ctrl++ Ctrl+- Ctrl+- Ctrl+1 Ctrl+1 Ctrl+A Ctrl+T Ctrl+C Ctrl+C Ctrl+I Ctrl+I Ctrl+L Ctrl+L Ctrl+M Ctrl+M Ctrl+N Ctrl+N Ctrl+O Ctrl+A Ctrl+P Ctrl+P Ctrl+R Ctrl+L Ctrl+S Ctrl+S Ctrl+T Ctrl+F Ctrl+V Ctrl+V Ctrl+X Ctrl+X Ctrl+Y Ctrl+Y Ctrl+Z Ctrl+Z Delete Apagar Remove track range as selected Remover os limites das pistas que estiverem selecionados &Range Set &Determinar Limites Adjust session tempo from current clip selection Ajustar o andamento da sessão para a sequência selecionada atualmente Reset track height Resetar altura da pista Move Bottom Mover para Trás LV2 Plug-in External UI support disabled. Suporte a LV2 Plug-in External UI desabilitado. Make current the previous track Fazer da pista anterior a atual Change session tempo map / markers Modificar mapa de tempo da sessão / marcadores The audio engine has been shutdown. Make sure the JACK audio server (jackd) is up and running and then restart session. O aparato de áudio foi desligado. Certifique-se que JACK audio server (jackd) está rodando e reinicie a sessão. Auto-monitor current track Auto monitorar pista atual Randomize current MIDI clip events Aleatorizar eventos na Sequência MIDI atual Randomize clip events Aleatorizar eventos na Sequência Mi&xer Mi&xer under the terms of the GNU General Public License version 2 or later. licenciado sob os termos da GNU General Public License versão 2 ou superior. LV2 Plug-in support (liblilv) disabled. Suporte a LV2 Plug-in (liblilv) desabilitado. Pa&nic Pâ&nico REC ON GRAVAR LIGADO T&ools &Ferramentas Record Gravar Rewind Retroceder Set edit-range from current clip extents Determinar edição de limite na extensão da sequência atual Show information about the Qt toolkit Mostre-me informações sobre o Qt toolkit F&ast Forward &Adiantar Sp&lit &Quebrar Thum&b Miniat&ura &Tempo Adjust... Ajus&te de Andamento... New session: "%1". Nova Sessão: "%1". Open session from file Abrir sessão do arquivo Tempo M&ap / Markers... M&apa de Tempo / Marcadores... Loc&k All Tran&car Tudo Paste clipboard contents Colar conteúdo da área de transferência Tool tips Comentários LV2 Plug-in UI support disabled. Suporte a LV2 Plug-in UI desabilitado. Reset Takes Resetar Tentativas Gravadas &Automation &Automação First Track Primeira Pista Reset takes Resetar tentativas Loop &Set Determinar &Loop Quantize current MIDI clip events Quantizar os eventos da sequência MIDI atual C&olor... C&or... First track Primeira pista Automation Automação Automation clear Limpar automação Automation color Cor da Automação &Continue Past End &Continuar de onde parou Add Track Adicionar Pista Last Track Última Pista Next Track Próxima Pista Zoom Out Diminuir Zoom Zoom out Diminuir zoom Loop Set Determinar Loop &Loop Set Determinar &Loop Loop set Determinar Loop &Toolbars &Barras de Ferramentas &Open... &Abrir... Options Opções &Options &Opções A&utomation A&utomação Add track Adicionar Pista Session total time Tempo total da Sessão Last track Última pista Next track Próxima pista Split current clip at playhead Quebrar sequência atual no início da reprodução Unknown sub-command Sub-comando desconhecido REC OFF GRAVAR DESLIGADO Shortcuts Atalhos Resize current MIDI clip events Diminuir eventos na sequência MIDI atual About &Qt... Sobre o &Qt... Move Top Mover para o Topo Move top Mover para o topo &Outputs &Saídas TRACK RECORD %1 %2 GRAVAR PISTAS %1 %2 Rec&ord All Gravar T&odas Open session: "%1". Abrir sessão: "%1". Move bottom Mover para trás Normalize clip Normalizar sequência Normalize Clip Normalizar Sequência Add a new track to session Adicionar uma nova pista à sessão TRACK SOLO %1 %2 PISTAS SOLO %1 %2 LV2 Plug-in Options support disabled. Suporte a LV2 Plug-in Options desabilitado. Redo last action Refazer a última ação Undo last action Desfazer a última ação LV2 Plug-in Presets support disabled. Suporte a LV2 Plug-in Presets desabilitado. Merge Clips Unir Sequências Create new clip Criar nova sequência Inport Audio File Importar Arquivo de Ãudio Import Audio file Importar arquivo de áudio Transport forward Botão de Próximo dos Controles de Gravação e Reprodução Merge clips Unir sequências Export Audio File Renderizar Arquivo de Ãudio Export Audio file Renderizar arquivo de audio Session Properties Propriedades da Sessão Session properties Propriedades da sessão Take Range Limites de Gravação por Tentativas Mute Track Emudecer Pista None Track Nenhuma Pista LOCATE %1 LOCALIZAR %1 &Import... &Importar... E&xport... &Renderizar... Take range Limites da tentativa Mute track Emudecer pista None track Nenhuma pista take range limites da tentativa program aplicativo LV2 Plug-in Patch support disabled. Suporte a Predefinições em plugins LV2 desabilitado. LV2 Plug-in UI Touch interface support disabled. Suporte a Interfaces sensíveis ao toque procedentes de Plug-in LV2 desabilitado. LV2 Plug-in UI Request-value support disabled. Requisição da interface do Plugin LV2 - suporte a valor desabilitado. LV2 Plug-in UI Idle interface support disabled. Suporte a Interfaces procedentes de Plug-in LV2 desabilitado. LV2 Plug-in UI Show interface support disabled. Desabilitar exibição de interfaces de Plug-in LV2. JACK Metadata support disabled. Suporte a Metadata do JACK desabilitado. XRUN XRUN The audio/MIDI engine could not be started. Make sure the JACK/Pipewire audio service and the ALSA Sequencer kernel module (snd-seq-midi) are up and running and then restart the session. Audio self-connection detected! In general, connecting an output bus (or insert send), directly into any input bus (or insert return), is not advisable. It often doesn't work, if at all. (track %1, panning %2) (pista %1, panorâmico %2) Backward Anterior &Backward &Anterior Automation lock all Trancar todas as automações Session could not be saved to "%1". Sorry. Não foi possível salvar a Sessão em "%1". Desculpe. Rescale Clip Redimensionar Sequência record clip gravar sequência Current track name Nome da pista atual Clip selection mode Modo de seleção de Sequências T&imeshift... Mudança de Veloc&idade... Snap/beat Ajuste à grade de acordo com a subdivisão do pulso Randomize Clip Aleatorizar Sequência R&ange... &Limite... &Range... &Limites... E&xport Tracks &Renderizar Pistas T&ransport &Controles Export current clip to file Renderizar a sequência atual para um arquivo Debugging option enabled. Opção de depuração habilitada. Re&wind Re&troceder STEP %1 PASSO %1 Normalize clip events Normalizar eventos na sequência Play &All Executar &Todas Continue Past End Continuar de onde parou Continue past end Continuar de onde parou Statusbar Barra de estado Refresh Atualizar &Refresh &Atualizar &Statusbar Barra de E&stado Remo&ve &Remover Quantize Clip Quantizar Sequência &Previous &Anterior &Navigate &Navegar Opening "%1"... Abrindo "%1"... Trac&k Range Limites da &Pista Auto monitor Auto monitoração Auto Monitor Auto Monitoração Floating tool tips view mode Visualização de comentários flutuantes Move current track to top Mover pista atual para o topo LV2 Plug-in support disabled. Suporte a LV2 Plug-in habilitado. &Connections &Conexões Save session: "%1". Sessão salva: "%1". Range (fold) current clip into takes Limites de Tentativas (ocultas) da sequência atual The directory already exists: "%1" Do you want to replace it? Este diretório já existe: "%1" Deseja subtituí-lo? Solo current track Solo para a pista atual Horizontal zoom mode Modo de zoom horizontal Export Clip Renderizar Sequência Controllers Controladores Export clip Renderizar sequência Show/hide main program window edit toolbar Mostrar/esconder a barra de ferramentas de edição da janela do programa principal Import Clip Importar Sequência Import clip Importar sequência Re&scale... Redimen&sionar... Paste/repeat clipboard contents Colar repetidamente o conteúdo da área de tranferência Vertical zoom mode Modo de zoom vertical Save session to file Salvar sessão em arquivo Import clip from file(s) Importar sequência(s) de um arquivo(s) Show/hide main program window file toolbar Mostrar/esconder a barra de ferramentas para arquivos da janela do programa principal Information Informação &About... &Sobre... Change instrument definitions and files Modificar definições de instrumentos e de arquivos About Qt Sobre o Qt Rescale clip events Redimensionar eventos da sequência Save As Salvar Como Save as Salvar como SHUTTLE %1 RETRIBUIR %1 &Randomize... &Aleatório... New session Nova sessão Punch Se&t &Determinar Gravação Localizada Playback all automation curves Executar todas as curvas de automação Fast Forward Adiantar Fast forward Adiantar Monitor current track Monitorar pista atual &Quantize... &Quantizar... session sessão LV2 Plug-in Time/position support disabled. Suporte a LV2 Plug-in Time/position desabilitado. Automation record all Gravar todas as automações None current track Nenhuma pista atual Shift+T Shift+T Instruments Instrumentos Transpose clip events Transpor eventos da sequência Mute current track Emudecer pista atual Export tracks to Audio file Renderizar pistas para um Arquivo de áudio Quantize clip events Quantizar eventos na sequência Backspace Backspace Select None Não selecionar nada Select clip Selecionar sequência Select none Não selecionar nada Show/hide main program window time toolbar Mostrar/esconder a barra de ferramentas para tempo da janela do programa principal Move current track down Mover pista atual para baixo First Take Primeira Tentativa First take Primeira tentativa Show/hide main program window view toolbar Mostrar/esconder a barra de ferramentas de visualizações da janela do programa principal Last Take Última Tentativa &Normalize... &Normalizar... Last take Última tentativa A&uto Backward A&uto Retroceder Take %1 Tentativa %1 The original session sample rate (%1 Hz) is not the same as the current audio engine (%2 Hz). Saving and reloading from a new session file is highly recommended. A Taxa de amostragem original da sessão (%1 Hz) não é a mesma da interface de áudio (%2 Hz). Salvar e recarregar a partir de um novo arquivo de sessão é altamente recomendado. &Bottom Para &Trás R&ectangle R&etangular &Delete A&pagar Tempo map / markers Mapa de Tempo / marcadores Tempo Map / Markers Mapa de Tempo / Marcadores (track %1, gain %2) (pista %1, ganho %2) LV2 Plug-in State Files support disabled. Suporte a LV2 Plug-in State Files desabilitado. &Height &Dimensão Vertical Track &Properties... &Propriedades da Pista... [modified] (modificado) Keyboard shortcuts Atalhos no teclado &Inputs &Entradas &Invert &Inverter Vertical Zoom Zoom Vertical Vertical zoom Zoom vertical &Linear &Linear LADSPA Plug-in support disabled. Suporte a LADSPA Plug-in desabilitado. &New... &Nova... C&lear All &Limpar Tudo Transport play/pause Botão de tocar nos Controles de Gravação e Reprodução &Record &Gravar Ctrl+Ins Ctrl+Ins Ctrl+Del Ctrl+Del Metronome Metrônomo &Metronome &Metrônomo &Select &Selecionar Select All Selecionar Tudo Select all Selecionar tudo Copy selection to clipboard Copiar seleção para a área de transferência &Spline &Spline &Decrease &Diminuir &Audio... &Ãudio... &Edit... &Editar... Session sample rate Taxa de amostragem da Sessão &Unlink &Desgrudar Transpose current MIDI clip events Transpor os eventos da sequência MIDI atual I&nsert I&nserir Export tracks to MIDI file Renderizar pistas para um arquivo MIDI Decrease track height Diminuir altura da pista Increase track height Aumentar altura da pista Previous Take Tentativa Anterior Previous take Tentativa anterior Connections Conexões Resi&ze... Di&minuir... Select current clip last take Selecionar última tentativa da sequência atual Make current the first track Fazer desta a primeira pista Move current track to bottom Mover pista atual para trás DSSI Plug-in support disabled. Suporte a DSSI Plug-in desabilitado. Mark track as selected Marcar pista que estiver selecionada SONGPOS %1 POSIÇÃO DA MÚSICA %1 Save Session Salvar Sessão Save session Salvar sessão Saving "%1"... Salvando "%1"... Increase height Aumentar altura Decrease height Diminuir altura Move Down Mover Pista atual para Baixo Move down Mover para baixo Session soloing state Sessão em solo &Merge... U&nir... New Clip Nova Sequência New clip Nova sequência &Vertical &Vertical &Buses... &Canais Primários... Edit current track properties Editar propriedades da pista atual Reset (unfold) current clip takes Resetar tentativas (à vista) da sequência atual M&essages M&ensagens Messages Mensagens Move current track up Mover pista atual para cima Playback automation curve Executar curva de automação Open Session Abrir Sessão Open session Abrir sessão Auto &Monitor Auto-&Monitoração Version Versão &Remove Track &Remover Pista Split current selection Quebrar esta seleção Too&l Tips &Comentários Import tracks from Audio file Importar pista de um arquivo de áudio Archive files (*.%1) Arquivos no Inventário (*.%1) OSC service support (liblo) disabled. Suporte a serviço OSC (liblo) desabilitado. Ctrl+Shift++ Ctrl+Shift++ Ctrl+Shift+- Ctrl+Shift+- Ctrl+Shift+1 Ctrl+Shift+1 Ctrl+Shift+A Ctrl+Shift+T Ctrl+Shift+L Ctrl+Shift+L Ctrl+Shift+P Ctrl+Shift+P Ctrl+Shift+R Ctrl+Shift+L Ctrl+Shift+V Ctrl+Shift+V Ctrl+Shift+Z Ctrl+Shift+Z Mark track range as selected Marcar limite das pistas que estiverem selecionadas Select Invert Selecionar o Inverso Oops! Looks like it crashed or did not close properly last time it was run... however, an auto-saved session file exists: "%1" Do you want to crash-recover from it? Ops! Parece que ele está quebrado ou não foi fechado corretamente... De qualquer forma, existe uma sessão salva automaticamente em: "%1" Você deseja que eu restaure a partir dali? Track Outputs Saídas da Pista Track outputs Saídas da pista Show/hide main program window thumb toolbar Mostrar/esconder a barra de ferramentas para miniatura das janela do programa principal Timeshift Clip Mudança de Velocidade da Sequência Session record state Sessão em gravação Could not backup existing session: %1 as %2 Sorry. Não foi possível fazer backup da sessão: %1 até %2 Desculpe. M&onitor M&onitoração Forward Próximo &Forward &Próximo &Properties... &Propriedades... Playing "%1"... Tocando "%1"... Lock automation curve Trancar curva de automação Warning Atenção Show/hide main program window options toolbar Mostrar/esconder a barra de Opções da janela do programa principal Bar zebra view mode Modo de visualização em Listras de zebra Previous Track Pista Anterior Current tempo (BPM) Andamento atual (BPM) e Fórmula de Compasso Show current track input bus connections Mostrar as entradas de canais primários da pista atual Change session bus definitions Modificar definições dos canais primários da sessão About to clear all automation: "%1" Are you sure? Todas as automações serão resetadas: "%1" Posso continuar? Previous track Pista anterior Website Página na Internet Record all automation curves Gravar todas as curvas de automação LV2 Plug-in UI support (libsuil) disabled. Suporte a LV2 Plug-in UI (libsuil) desabilitado. All zoom mode Modo de todo o zoom Normalize current MIDI clip events Normalizar eventos na sequência MIDI atual Ogg Vorbis (libvorbis) file support disabled. Suporte a arquivos Ogg Vorbis (libvorbis) desabilitado. &Windows &Janelas Insert Track Range Inserir Limite à Pista Insert track range Inserir limite à pista Clear automation curve Limpar curva de automação Delete selection Apagar seleção Export MIDI file Renderizar arquivo MIDI Import MIDI file Importar arquivo MIDI Export MIDI File Renderizar Arquivo MIDI Import MIDI File Importar Arquivo MIDI MIDI connections change. Conexões MIDI modificadas. Snap grid view mode Modo de exibição de ajuste à grade Invert selection Inverter seleção Log&arithmic Log&arítmico Not implemented Não implementado Save current session with another file name Salvar a sessão atual com outro nome Show/hide main program window track toolbar Mostrar/esconder a barra de ferramentas para pistas da janela do programa principal Increase Height Aumentar Altura Decrease Height Diminuir Altura Record automation curve Gravação de curva de automação Transport punch in/out set Seleção de limites para Gravação Localizada nos Controles de Gravação e Reprodução Select Track Range Selecionar Limites da Pista Select track range Selecionar limites da pista Mark all as selected Marcar tudo o que está selecionado Merge selected clips Unir sequências selecionados &Transpose... &Transpor... Lock all automation curves Trancar todas as curvas de automação Current time (play-head) Tempo atual (reprodução) MIDI CTL: %1, Channel %2, Param %3, Value %4 MIDI CTL: %1, Canal %2, Param %3, Valor %4 Show/hide the connections window Mostrar/esconder a janela de Conexões Open &Recent Abrir &Recentes Horizontal Zoom Zoom Horizontal Horizontal zoom Zoom horizontal Shift+F2 Shift+F2 Select current clip next take Selecionar próxima tentativa da sequência atual Track Properties Propriedades da Pista Track properties Propriedades da Pista Mark all as unselected Marcar tudo o que não estiver selecionado Automation curve logarithmic scale Curva de automação em escala logarítmica Select invert Selecionar o inverso Change MIDI controllers configuration Modificar configurações de controladores MIDI Zoom Reset Resetar Zoom Automation logarithmic Automação logarítmica Punch in/out set Determinação dos limites para a Gravação Localizada Next Take Próxima Tentativa Automation lock Trancar automação Next take Próxima tentativa Auto Backward Auto Retroceder Auto backward Auto retroceder Automation clear all Limpar todas as Automações Zoom reset Voltar ao Zoom inicial LV2 Plug-in MIDI/Atom support disabled. Suporte a LV2 Plug-in MIDI/Atom desabilitado. Arm current track for recording Engatilhar a pista atual para gravação Remove Track Range Remover Limites da Pista Remove track range Remover limite da pista File Toolbar Barra de Ferramentas para Arquivos File toolbar Barra de ferramentas para arquivos Unlink current clip Desgrudar sequência atual Session files (*.%1 *.%2) Arquivos de Sessões (*.%1 *.%2) LV2 Plug-in State support disabled. Suporte a LV2 Plug-in State desabilitado. Rectangular selection mode Modo de seleção retangular Remove range as selected Remover limites que estiverem selecionados MPEG-1 Audio Layer 3 (libmad) file support disabled. Suporte a arquivos MPEG-1 Audio Layer 3 (libmad) desabilitado. Shift+Del Shift+Del Shift+Ins Shift+Ins &Instruments... &Instrumentos... XRUN(%1): some frames might have been lost. XRUN(%1): algumas amostras podem ter sido perdidas. Edit Toolbar Editar Barra de Ferramentas Edit toolbar Editar barra de ferramentas The following issues were detected: %1 Saving into another session file is highly recommended. Os seguintes problemas foram detectados: %1 Salvar esta sessão em um novo arquivo é altamente recomendável. qtractorMessages Logging stopped --- %1 --- Autenticação interrompida --- %1 --- Logging started --- %1 --- Autenticação iniciada --- %1 --- Messages Mensagens qtractorMidiControl Note On Nota Ligada Note Off Nota Desligada Key Press Tecla Pressionada Controller Controlador Pgm Change Mudança de Programa Chan Press Cnl Pressionado Pitch Bend Roda de Altura RPN RPN NRPN NRPN Control 14 Controle 14 Track Gain Ganho da Pista Track Panning Panorâmico da Pista Track Monitor Monitor da Pista Track Record Gravação da Pista Track Mute Emudecer Pista Track Solo Pista Solo qtractorMidiControlForm + + &Up Para Ci&ma &Map &Mapear Path Local Type Tipo &Down &Para Baixo Controllers Controladores &Type &Tipo Trac&k &Pista offse&t &deslocamento &limit &limite Flags Marcações Track limit Limite da pista Command action Ação do comando Command delta/momentary Comando de Delta/instantâneo D&elta &Delta Enable all controllers immediate sync (hook) Habilitar todos os controladores em sync (hook) &Sync &Sync Close Fechar Files Arquivos Track Pista The controller file already exists: "%1" Do you want to replace it? Já existe o Arquivo de controlador: "%1" Você quer substituí-lo? Saved controller mappings may not be effective the next time you start this program. "%1" Do you want to apply to controller files? O mapeamento do controlador salvo não será efetivo até a próxima inicialização deste aplicativo. "%1" Você deseja aplicar ao arquivo do controlador? MIDI Controller (parameter) Controlador MIDI (parâmetro) Close this dialog Fechar esta janela Unmap/remove controller command Não mapear/remover comando do controlador Export to controller file Exportar para um arquivo de Controlador Controller mappings have been changed. O mapeamento do controlador foi modificado. Controller map Mapa do Controlador Track offset Deslocamento da Pista U&nmap &Não mapear Do you want to save the changes? Você deseja salvar as alterações? MIDI parameter (track offset) Parâmetro MIDI (deslocamento da pista) Import Controller Files Importar arquivo de Controlador Import controller files Importar Arquivo de Controlador Move controller file down on list order Move arquivo do controlador para baixo na lista &Import... &Importar... E&xport... E&xportar... Command feedback Resultado do comando &Parameter &Parâmetro Parameter Parâmetro Relo&ad Recarreg&ar Reload/apply all controller files Atualizar/aplicar todos os arquivos do controlador Channel Canal &Channel &Canal Command Comando Map/update controller command Mapear/atualizar comando do controlador &Remove &Remover All files (*.*) Todos os arquivos (*.*) controller controlador Delta Delta Feedback Resultado &Feedback &Resultado MIDI Channel Canal MIDI Controller files (*.%1) Arquivos de Controlador (*.%1) C&ommand Co&mando Warning Atenção MIDI Event type Tipo de evento MIDI About to remove controller file: "%1" Are you sure? Será removido o arquivo de controlador: "%1" Posso continuar? Move controller file up on list order Move arquivo do controlador para cima na lista Remove controller file Excluir arquivo de Controlador Export Controller File Exportar Arquivo de Controlador Controller files Arquivos do Controlador qtractorMidiControlObserverForm In&vert &Inverter &Hook &Gancho &Lock &Travar &Play &Tocar &MIDI Controller... Controlador &MIDI... &Clear &Limpar &Type: &Tipo: MIDI Controller Controlador MIDI Cha&nnel: Ca&nal: Some settings have been changed. Do you want to apply the changes? Algumas modificações foram feitas. Você deseja aplicar essas mudanças? MIDI parameter Parâmtero MIDI &Automation &Automação &Outputs &Saídas &Parameter: &Parâmetro: L&atch &Trava Control input connections Controlar conexões de entrada &Inputs &Entradas &Record Grava&r &Logarithmic &Logarítmico &Feedback &Resultado MIDI controller is already assigned. Do you want to replace the mapping? O controlador MIDI já está assinalado. Você deseja subtituir o mapeamento? Control output connections Controlar conexões de saída MIDI channel Canal MIDI MIDI event type Tipo de evento MIDI qtractorMidiControlPluginWidget &Type: &Tipo: MIDI event type Tipo de evento MIDI Cha&nnel: Ca&nal: MIDI channel Canal MIDI &Parameter: &Parâmetro: MIDI parameter Parâmtero MIDI &Logarithmic &Logarítmico In&vert &Inverter &Bipolar qtractorMidiEditEvent Zoom in (horizontal) Aumentar Zoom (horizontal) Zoom reset (horizontal) Resetar Zoom (horizontal) Zoom out (horizontal) Diminuir Zoom (horizontal) qtractorMidiEditList C%1 C%1 qtractorMidiEditTime Edit-head Editar começo Edit-tail Editar final Start: %1 End: %2 Length: %3 Início: %1 Final: %2 Tamanho: %3 Loop-start Começo do Loop Punch-out Final da Gravação Localizada Loop-end Final do Loop Play-head Começo da reprodução Punch-in Começo da Gravação Localizada qtractorMidiEditView Zoom reset (vertical) Resetar Zoom (vertical) Zoom in (vertical) Aumentar Zoom (vertical) Zoom out (vertical) Diminuir Zoom (vertical) qtractorMidiEditor cut recortar edit editar move mover paste colar remove range remover limite Start: %1 End: %2 Length: %3 Início: %1 Final: %2 Tamanho: %3 insert range inserir limite Pgm Change (%1) Mudança de Programa (%1) Chan Press (%1) Cnl Press (%1) MIDI Editor Editor MIDI delete apagar NRPN (%1) Name: %2 Value: %3 NRPN (%1) Nome: %2 Valor: %3 RPN (%1) Name: %2 Value: %3 RPN (%1) Nome: %2 Valor: %3 C C#/Db D D#/Eb E F F#/Gb G G#/Ab A A#/Bb B Acoustic Bass Drum Bumbo Bass Drum 1 Bumbo 1 Side Stick Aro da Caixa Acoustic Snare Caixa Hand Clap Palma Electric Snare Caixa Eletrônica Low Floor Tom Surdo Closed Hi-Hat Chimbal Fechado High Floor Tom Tom 1 Pedal Hi-Hat Pedal Chinbal Low Tom Tom 2 Open Hi-Hat Chimbal aberto Low-Mid Tom Tom 4 Hi-Mid Tom Tom 3 Crash Cymbal 1 Ataque 1 High Tom Tom 1 Ride Cymbal 1 Condução Chinese Cymbal Gongo Chinês Ride Bell Sino de Condução Tambourine Tambourine Splash Cymbal Splash Cowbell Cowbell Crash Cymbal 2 Ataque 2 Vibraslap Ride Cymbal 2 Condução 2 Hi Bongo Bongô 1 Low Bongo Bongô 2 Mute Hi Conga Conga 1 Abafada Open Hi Conga Conga 1 Aberta Low Conga Conga 2 High Timbale Timbale 1 Low Timbale Timbale 2 High Agogo Agogô 1 Low Agogo Agogô 2 Cabasa Cabaça Maracas Maracas Short Whistle Apito Curto Long Whistle Apito Longo Short Guiro Guiro Curto Long Guiro Guiro Longo Claves Hi Wood Block Woodblock 1 Low Wood Block Woodblock 2 Mute Cuica Cuíca 1 Open Cuica Cuíca 2 Mute Triangle Triângulo 1 Open Triangle Triângulo 2 Bank Select (coarse) Selecionar Banco (tosco) Modulation Wheel (coarse) Roda de Modulação (tosco) Breath Controller (coarse) Controle de Respiração (tosco) Foot Pedal (coarse) Pedal (tosco) Portamento Time (coarse) Tempo do Portamento (tosco) Data Entry (coarse) Entrada de dados (tosco) Volume (coarse) Volume (tosco) Balance (coarse) Balanço (tosco) Pan Position (coarse) Posição do Pan (tosco) Expression (coarse) Expressão (tosco) Effect Control 1 (coarse) Controle de Efeitos 1 (tosco) Effect Control 2 (coarse) Controle de Efeitos 2 (tosco) General Purpose Slider 1 Slider de propósito Geral 1 General Purpose Slider 2 Slider de propósito Geral 2 General Purpose Slider 3 Slider de propósito Geral 3 General Purpose Slider 4 Slider de propósito Geral 4 Bank Select (fine) Selecionar Banco (fino) Modulation Wheel (fine) Roda de Modulação (fino) Breath Controller (fine) Controle de Respiração (fino) Foot Pedal (fine) Pedal (fino) Portamento Time (fine) Tempo do Portamento (fino) Data Entry (fine) Entrada de dados (fino) Volume (fine) Volume (fino) Balance (fine) Balanço (fino) Pan Position (fine) Posição do Pan (fino) Expression (fine) Expressão (fino) Effect Control 1 (fine) Controle de Efeitos 1 (fino) Effect Control 2 (fine) Controle de Efeitos 2 (fino) Hold Pedal (on/off) Pedal de Sustentação (liga/desl) Portamento (on/off) Portamento (liga/desl) Soft Pedal (on/off) Pedal Suave (liga/desl) Legato Pedal (on/off) Pedal de Legato (liga/desl) Hold 2 Pedal (on/off) Pedal de Sustentação 2 (liga/desl) Sound Variation Variação Sonora General Purpose Button 1 (on/off) Botão de propósito Geral 1 (liga/desl) General Purpose Button 2 (on/off) Botão de propósito Geral 2 (liga/desl) General Purpose Button 3 (on/off) Botão de propósito Geral 3 (liga/desl) General Purpose Button 4 (on/off) Botão de propósito Geral 4 (liga/desl) Effects Level Nível de Efeito Chorus Level Nível de Chorus Celeste Level Nível de Celeste Phaser Level Nível de Phaser Data Button Increment Botão de Incremento de Dados Data Button Decrement Botão de Decremento de Dados Non-Registered Parameter (fine) Parâmetro não Registrado (fino) Non-Registered Parameter (coarse) Parâmetro não Registrado (tosco) Registered Parameter (fine) Parâmetro Registrado (fino) Registered Parameter (coarse) Parâmetro Registrado (tosco) All Sound Off Desliga Todos os Sons All Controllers Off Desliga Todos os Controladores Local Keyboard (on/off) Teclado Local (liga/desl) All Notes Off Desliga Todas as Notas Omni Mode Off Modo Omni Desligado Omni Mode On Modo Omni Ligado Mono Operation Operação em Mono Poly Operation Operação em Poly Pitch Bend Sensitivity Sensibilidade da Roda de Altura Fine Tune Afinação Fina Coarse Tune Afinação Tosca Tuning Program Afinando Programa Tuning Bank Afinando Banco Vibrato Rate Taxa de Vibrato Vibrato Depth Intesidade do Vibrato Vibrato Delay Atraso do Vibrato Filter Cutoff Corte do Filtro Filter Resonance Ressonância do Filtro Sostenuto Pedal (on/off) Release Time Attack Time Brightness Decay Time Tremolo Level EG Attack Ataque EG EG Decay Decaimento EG EG Release Relaxamento EG Drum Filter Cutoff Corte do Filtro da Bateria Drum Filter Resonance Ressonância do Filtro da Bateria Drum EG Attack Ataque EG da Bateria Drum EG Decay Decaimento EG da Bateria Drum Pitch Coarse Altura Tosca da Bateria Drum Pitch Fine Altura Fina da Bateria Drum Level Nível da Bateria Drum Pan Pan da Bateria Drum Reverb Send Mandada da Reverberação da Bateria Drum Chorus Send Mandada do Chorus da Bateria Drum Variation Send Mandada da Variação da Bateria Modulation Wheel (14bit) Roda de Modulação (14bit) Breath Controller (14bit) Controle de Respiração (14bit) Foot Pedal (14bit) Pedal (14bit) Portamento Time (14bit) Tempo do Portamento (14bit) Volume (14bit) Volume (14bit) Balance (14bit) Balanço (14bit) Pan Position (14bit) Posição do Pan (14bit) Expression (14bit) Expressão (14bit) Effect Control 1 (14bit) Controle de Efeitos 1 (14bit) Effect Control 2 (14bit) Controle de Efeitos 2 (14bit) General Purpose Slider 1 (14bit) Slider de propósito Geral 1 (14bit) General Purpose Slider 2 (14bit) Slider de propósito Geral 2 (14bit) General Purpose Slider 3 (14bit) Slider de propósito Geral 3 (14bit) General Purpose Slider 4 (14bit) Slider de propósito Geral 4 (14bit) Chromatic Cromática Major Maior Minor Menor Melodic Minor (Asc) Melódica Maior (Asc) Melodic Minor (Desc) Melódica Menor (Desc) Whole Tone Tons Inteiros Pentatonic Major Pentatônica Maior Pentatonic Minor Pentatônica Menor Pentatonic Blues Pentatônica de Blues Pentatonic Neutral Pentatônica Neutra Octatonic (H-W) Octatonica (H-W) Octatonic (W-H) Octatonica (W-H) Ionian Jônico Dorian Dórico Phrygian Frígio Lydian Lídio Mixolydian Mixolídio Aeolian Eólico Locrian Lócrio Egyptian Egípsia Eight Tone Spanish Oito Tons Espanhola Hawaiian Havaiana Hindu Hirajoshi Hungarian Major Húngara Maior Hungarian Minor Húngara Menor Hungarian Gypsy Gypsy Húngara Japanese (A) Japonesa (A) Japanese (B) Japonesa(B) Jewish (Adonai Malakh) Judaica (Adonai Malakh) Jewish (Ahaba Rabba) Judaica (Ahaba Rabba) Jewish (Magen Abot) Judaica (Magen Abot) Oriental (A) Oriental (A) Oriental (B) Oriental (B) Oriental (C) Oriental (C) Roumanian Minor Romena Menor Neapolitan Napolitana Neapolitan Major Napolitana Maior Neapolitan Minor Napolitana Menor Overtone Supertom Leading Whole Tone Tons Inteiros Conduzida Nine Tone Scale Escala de 9 Tons Dominant Seventh Sétima Dominante Augmented Aumentada Algerian Arabian (A) Arábica (A) Arabian (B) Arábica (B) Balinese Balinesa Chinese Chinesa Diminished Diminuta Japanese (Ichikosucho) Japonesa (Ichikosucho) Japanese (Taishikicho) Japonesa (Taishikicho) Javaneese Javanesa Marva Theta Mela Bhavapriya Mela Chakravakam Mela Chalanata Mela Chitrambari Mela Dharmavati Mela Dhatuvardhani Mela Dhavalambari Mela Divyamani Mela Ganamurti Mela Gangeyabhusani Mela Gavambodhi Mela Gayakapriya Mela Hatakambari Mela Jalarnavam Mela Jhalavarali Mela Jhankaradhvani Mela Jyotisvarupini Mela Kamavarardhani Mela Kantamani Mela Kosalam Mela Latangi Mela Manavati Mela Mararanjani Mela Naganandini Mela Namanarayani Mela Navanitam Mela Nitimati Mela Pavani Mela Ragavardhani Mela Raghupriya Mela Ramapriya Mela Rasikapriya Mela Ratnangi Mela Risabhapriya Mela Rupavati Mela Sadvidhamargini Mela Salagam Mela Sanmukhapriya Mela Sarasangi Mela Senavati Mela Subhapantuvarali Mela Sucharitra Mela Sulini Mela Suryakantam Mela Syamalangi Mela Tanarupi Mela Vagadhisvari Mela Vanaspati Mela Varunapriya Mela Yagapriya Persian Persa Purvi Theta Spanish Gypsy Gypsy Espânica Todi Theta Enigmatic Enigmática Kumoi Lydian Augmented Lídia Aumentada Pelog Prometheus Prometeus Prometheus Neapolitan Prometeus Napolitana Six Tone Symmetrical Seis Tons Simétrica Super Locrian Super Lócria Lydian Minor Lídia Menor Lydian Diminished Lídia Diminuta Half Diminished Meia Diminuta Bhairav Yaman Todi Jog Multani Darbari Malkauns Bhoopali Shivaranjani Marwa Minor 5 Menor 5 Major 5 Maior 5 5 5 45 45 457 457 M 6 resize redimensionar rescale reescalonar Note On (%1) %2 Velocity: %3 Duration: %4 Nota Ativa (%1) %2 Intensidade: %3 Duração: %4 Unknown (%1) Desconhecido (%1) SysEx (%1 bytes) Data: SysEx (%1 bytes) Data: Controller (%1) Name: %2 Value: %3 Controlador (%1) Nome: %2 Valor: %3 Time: %1 Type: Tempo: %1 Tipo: Control 14 (%1) Name: %2 Value: %3 Controle 14 (%1) Nome: %2 Valor: %3 Pitch Bend (%1) Roda de Altura (%1) Key Press (%1) %2 Value: %3 Pressionamento da Tecla (%1) %2 Valor: %3 qtractorMidiEditorForm F4 F4 F5 F5 &In &Aumentar Del Del Cut Recortar Current time (play-head) Tempo atual (reprodução) Current tempo (BPM) Andamento atual (BPM) e Fórmula de Compasso Reset time-sig. Resetar formula-compasso. MOD MOD REC GRAVAR MIDI clip record state Estado de gravação da sequência MIDI MUTE MUDO MIDI clip mute state All files (*.*) Todos os arquivos (*.*) &All &Tudo &Out &Diminuir Copy Copiar Cu&t &Recortar Grid Grade Loop Loop Play Tocar Redo Refazer Save Salvar Stop Parar Transport fast forward Botão de Adiantar nos Controle de Reprodução Undo Desfazer Clip Loop Loop da Sequência Clip loop Loop da sequência &Copy &Copiar &Edit &Editar &File &Arquivos &Grid &Grade &Help &Ajuda &Loop &Loop &None &Nada &Play &Tocar &Redo &Refazer &Save &Salvar &Stop &Parar &Undo &Desfazer &View &Visualização &Zoom &Zoom Show/hide the thumb view toolbar Mostrar/esconder a barra de miniatura Edit draw mode (notes) Modo de edição em desenho (notas) Insert range as selected Inserir limite às pistas que estiverem selecionadas About Sobre Close Fechar S&nap Di&visão Panic Pânico Paste Colar Punch Gravação Localizada Space Space Zebra Zebra Edit Of&f &Desligar Edição Save MIDI Clip Salvar Sequência MIDI Past&e Repeat... R&epetir Colar... View toolbar Barra de visualizações View Toolbar Barra de Visualizações Instrum&ent &Instrumento Not&e Type Tipo de &Nota Val&ue Type &Valor do Tipo &Ghost Track Pista &Oculta &Note St&ep &Mute &Mudo Mute Mute current MIDI clip Recor&d &Gravar Record current MIDI clip (overdub) Gravar sequência MIDI selecionada (overdub) Paste Repeat Repetir Colar &Step Insert step Insert step (rest) Right DO NOT TRANSLATE T&empo ramp... Tempo ramp Tempo ramp selection Transport Toolbar Barra de Controles de Gravação e Reprodução Transport toolbar Barra de ferramentas para Controles de Gravação e Reprodução T&ime T&empo Time Toolbar Barra de Ferramentas para Tempo Time toolbar Barra de Ferramentas para informações de tempo Show/hide the time toolbar Mostrar/esconder barra de tempo Note &Names Nomes das &Notas Note Names Nomes das Notas Note names Nomes das notas Whether to show note names Quando mostrar nome das notas &Drum Mode &Modo de Percussão Drum Mode Modo de Percussão Drum mode Modo de percussão Whether note onset events are displayed as diamonds Se eventos assinalados das notas serão mostrados como losangos &Horizontal &Horizontal Step Backward Step backward Transport step backward Step Forward Step forward Transport step forward Note Backward Step note backward Transport step note backward Note Forward Step note forward Transport step note forward &Shortcuts... Atalho&s... Track Inputs Entradas da Pista Zoom In Aumentar Zoom Zoom in Aumentar Zoom Set current snap to %1 Selecionar divisão atual para %1 Follow playhead Seguir a reprodução Follow Playhead Seguir a Reprodução Thumb Toolbar Barra de Miniatura Thumb toolbar Barra de Miniatura F&ollow Playhead Seguir a Reproduçã&o MIDI clip duration Duração da sequência MIDI Transport stop Botão de Parar dos Controles de Gravação e Reprodução Transport loop Botão de Loop dos Controles de Gravação e Reprodução Show/hide the transport toolbar Mostrar/esconder a barra de controles Set loop-range from clip extents Determinar os limites do loop na extensão da sequência Transport rewind Botão de Retroceder dos Controles de Gravação e Reprodução Transport record Botão de Gravar dos Controles de Gravação e Reprodução Mark range as selected Marcar limite que estiver selecionado Clip Range Limites do Sequência Scale type Tipo de escala Show/hide the menubar Mostrar/esconder a barra de menu Clip range Limites da sequência Redo last edit operation Refazer a última ação &Close &Fechar Transport loop set Determinar Loop dos Controles de Gravação e Reprodução &Paste &Colar &Punch Gravação &Localizada &Range &Limites &Reset &Resetar &Scale E&scala &Tools &Ferramentas &Track &Pistas Undo last edit operation Desfazer a última ação &Zebra &Zebra Refresh views Atualizar visualizações Copy current selection to the local clipboard Copiar seleção para a área de transferência local Remove Range Remover Limites All MIDI tracks shut off (panic) Interrompe todas as pistas MIDI (pânico) Paste repeat Repetir colar All Zoom Todo o Zoom All zoom Todo o zoom Save &As... S&alvar Como... Remove range Remover limites Note &Color &Cor da Nota Show/hide the scale toolbar Mostrar/esconder a barra de ferramentas para escala Transport backward Botão de Voltar dos Controles de Gravação e Reprodução Track inputs Entradas da Pista Insert Range Inserir Limite Show current MIDI clip/track output bus connections Mostrar as saídas de canais primários da Sequência/Pista MIDI atual Insert range Inserir limite Edit current MIDI clip properties Editar propriedades da sequência MIDI atual Edit draw mode Modo de edição em desenho Punch in/out Gravação Localizada (Começo/Fim) Show information about this application program Mostre-me informações sobre este aplicativo Select &Mode Selecionar &Modo Resize selection Redimensionar seleção Select Range Selecionar Limites Menubar Barra de menu &Menubar Barra de &Menu Timeshift Mudança de Velocidade Select range Selecionar Limites Transport punch in/out Gravação de Gravação Localizada dos Controles de Gravação e Reprodução Quantize selection Quantizar seleção Preview notes while editing (scrub) Prever notar enquanto edita Punch Set Determinar Gravação Localizada Ctrl++ Ctrl++ Ctrl+- Ctrl+- Ctrl+1 Ctrl+1 Ctrl+A Ctrl+T Ctrl+C Ctrl+C Ctrl+I Ctrl+I Ctrl+L Ctrl+L Ctrl+M Ctrl+M Ctrl+P Ctrl+P Ctrl+R Ctrl+L Ctrl+V Ctrl+V Ctrl+X Ctrl+X Ctrl+Z Ctrl+Z Delete Apagar Randomize Aleatório Parameter type Tipo de parâmetro Normalize selection Normalizar seleção &Range Set &Determinação Limites Scale Toolbar Barra de Ferramentas para Escala Scale toolbar Barra de Ferramentas para escala MIDI Editor Editor MIDI Channel %1 Canal %1 Pa&nic Pâ&nico Record Gravar Resize Redimensionar Rewind Retroceder Sc&ale Esc&ala Show information about the Qt toolkit Mostre-me informações sobre o Qt toolkit F&ast Forward &Adiantar Thum&b Miniat&ura The current MIDI clip has been changed: "%1" Do you want to save the changes? A seguinte sequência MIDI foi modificada: "%1" Deseja salvar essas alterações? Unlink Desgrudar Tool tips Comentários Whether note events are colored according to pitch Se eventos das notas forem coloridos de acordo com a altura &Value Color Cor do &Valor Set edit-range from clip extents Determinar o limite de edição na extensão da sequência atual Loop &Set Determinar &Loop Edit &Draw Editar e &Desenhar Zoom Out Diminuir Zoom Zoom out Diminuir zoom Loop Set Determinar Loop &Loop Set Determinar&Loop Loop set Determinar loop &Toolbars Barras de Ferramen&tas Timeshift selection Mudar velocidade da seleção View events Mostrar eventos Set edit mode on Ligar modo de edição Shortcuts Atalhos About &Qt... Sobre o &Qt... &Outputs &Saídas Properties Propriedades Value Color Cor do Valor Transport forward Botão de Próximo dos Controles de Gravação e Reprodução Value color Cor do valor Note Color Cor da Nota Note color Cor da nota MIDI track/channel Pista MIDI/canal Backward Anterior &Backward &Anterior Show/hide the statusbar Mostrar/esconder a barra de estado Paste/repeat local clipboard contents into the current MIDI clip Repetir Colar seleção para a área de transferência local Scale key Armadura da escala T&imeshift... Mudança de Veloc&idade... Snap/beat Ajuste à grade de acordo com a subdivisão do pulso &Transport Con&troles de Gravação e Reprodução Transpose Transposição T&ransport &Controles Track %1 Pista %1 Re&wind R&etroceder MIDI modification state Estado das modificações MIDI Show current MIDI clip/track input bus connections Mostrar as entradas de canais primários da Sequência/Pista MIDI atual Statusbar Barra de estado Refresh Atualizar &Refresh &Atualizar &Statusbar Barra de E&stado Remo&ve Remo&ver Rescale Reescalonar Floating tool tips view mode Visualização de comentários flutuantes Horizontal zoom mode Modo de zoom horizontal Normalize Normalizar Re&scale... Ree&scalonar... Vertical zoom mode Modo de zoom vertical Save current MIDI clip to existing file name Salvar a Sequência MIDI atual para um arquivo existente &About... &Sobre... About Qt Sobre o Qt Whether note events are colored according to value (velocity) Se eventos das notas forem coloridos de acordo com a intensidade Save As Salvar Como Save as Salvar como Show/hide the view toolbar Mostrar/esconder a barra de visualizações &Randomize... &Aleatório... Punch Se&t &Determinar Gravação Localizada Fast Forward Adiantar Fast forward Adiantar Set edit mode off Desligar modo de edição &Quantize... &Quantizar... Note duration Duração da nota Note Duration Duração da nota Backspace Backspace Note &Duration &Duração da nota Select None Não selecionar nada Edit current MIDI clip/track properties Editar propriedades da pista/sequência MIDI atual Select none Não selecionar nada &Normalize... &Normalizar... Show/hide the events list Mostrar/esconder a lista de eventos Note type Tipo de nota Show/hide the file toolbar Mostrar/esconder a barra de ferramentas para arquivos &Delete A&pagar Rescale selection Reescalonar seleção &Events &Eventos [modified] (modificado) Keyboard shortcuts Atalhos no teclado &Inputs &Entradas &Invert &Inverter Edit &On &Ligar Edição Edit Off Desligar Edição Edit off Modo de seleção Show/hide the edit toolbar Mostrar/esconder a barra de ferramentas Close this MIDI clip editor Fechar este editor de sequências MIDI Delete current selection Apagar seleção atual Vertical Zoom Zoom Vertical Vertical zoom Zoom vertical Randomize selection Aleatorizar seleção Value type Tipo de informação Transport play/pause Botão de Tocar dos Controles de Gravação e Reprodução Quantize Quantizar &Record &Gravar Ctrl+Ins Ctrl+Ins Ctrl+Del Ctrl+Del &Select &Selecionar Select All Selecionar Tudo Select all Selecionar tudo Edit On Ligar Edição Edit on Modo de edição &Unlink &Desgrudar I&nsert I&nserir Resi&ze... Redimen&sionar... Unlink current MIDI clip Desgrudar sequência MIDI &Vertical &Vertical Too&l Tips &Comentários Ctrl+Shift+A Ctrl+Shift+T Ctrl+Shift+L Ctrl+Shift+L Ctrl+Shift+P Ctrl+Shift+P Ctrl+Shift+V Ctrl+Shift+V Ctrl+Shift+Z Ctrl+Shift+Z Select Invert Selecionar o Inverso Track Outputs Saídas da Pista Track outputs Saídas da pista Cut current selection into the local clipboard Recortar seleção para a área de transferência local MIDI files (*.%1 *.smf *.midi) Arquivos MIDI (*.%1 *.smf *.midi) Forward Próximo &Forward &Próximo &Properties... &Propriedades... Warning Atenção Bar zebra view mode Modo de visualização em Listras de zebra MIDI clip name Nome da sequência MIDI All zoom mode Modo de todo o zoom 00:00:00.000 00:00:00.000 &Windows &Janelas Whether note events are shown proportional to duration Se eventos das notas forem mostrados proporcionais à duração Snap grid view mode Modo de exibição de ajuste à grade Transport punch in/out set Seleção de limites para Gravação Localizada dos Controles de Gravação e Reprodução &Transpose... &Transpor... Preview Notes Ativar som das notas &Preview Notes &Ativar o som das notas Horizontal Zoom Zoom Horizontal Horizontal zoom Zoom horizontal Preview notes Tocar o som das notas enquanto faz a edição Shift+F2 Shift+F2 Track Properties Propriedades da Pista Track properties Propriedades da Pista Select invert Selecionar o inverso Zoom Reset Resetar Zoom Note Velocity Intensidade da Nota Punch in/out set Seleção de limites para Gravação Localizada Zoom reset Resetar Zoom File Toolbar Barra de Ferramentas para Arquivos File toolbar Barra de ferramentas para arquivos Transpose selection Transpor seleção Save current MIDI clip with another file name Salvar Sequência MIDI atual com outro nome Remove range as selected Remover limites que estiverem selecionados Paste local clipboard contents into the current MIDI clip Colar conteúdo da área de transferência local dentro da sequência MIDI atual MIDI file name Nome do arquivo MIDI Edit Toolbar Editar Barra de Ferramentas Edit toolbar Editar barra de ferramentas qtractorMidiEventList Events Eventos qtractorMidiEventListView::ItemDelegate edit %1 editar %1 qtractorMidiEventListView::ItemModel Time Type Tipo Name Nome Value Valor Duration/Data Duração/Data Frame Amostra BBT CPS (Compasso Pulso Subpulso) Note On (%1) Nota Ligada (%1) Note Off (%1) Nota Desligada (%1) Key Press (%1) Tecla Pressionada (%1) Controller (%1) Controlador (%1) Control 14 (%1) Controle 14 (%1) RPN (%1) RPN (%1) NRPN (%1) NRPN (%1) Pgm Change Mudança de Programa Chan Press Cnl Pressionado Pitch Bend Roda de Altura SysEx SysEx Meta (%1) Meta (%1) Unknown (%1) Desconhecido (%1) qtractorMidiListView Fmt Fmt Name Nome Path Local tpqn tpqn Tracks Pistas Open MIDI Files Abrir Arquivos MIDI MIDI files (*.%1 *.smf *.midi) Arquivos MIDI (*.%1 *.smf *.midi) All files (*.*) Todos os arquivos (*.*) %1: MIDI file not found. %1: Arquivo MIDI não encontrado. qtractorMidiMixerMeter Volume (%) % % Pan: %1 Volume: %1% Volume: %1% qtractorMidiSysexForm &Up Para Ci&ma &Add &Adicionar Name Nome MIDI SysEx Midi SysEx Size Dimensão &Down &Para Baixo Error Erro Open SysEx Abrir SysEx Remove SysEx item Apagar item SysEx SysEx files (*.%1) Arquivos SysEx (*.%1) Data (hex) Data (hex) About to replace SysEx: "%1" Are you sure? O seguinte arquivo SysEx será substituído: "%1" Posso continuar? Delete SysEx Apagar SysEx Export SysEx File Exportar arquivo SysEx SysEx could not be loaded: "%1". Sorry. O seguinte SysEx não pode ser aberto: "%1". Desculpe. Update SysEx item Atulaizar item SysEx Create SysEx item Criar item SysEx Move SysEx item up on list order Mover item SysEx para cima na lista MIDI files (*.mid *.smf *.midi) Arquivos MIDI (*.mid *.smf *.midi) Save SysEx Salvar SysEx The SysEx file already exists: "%1" Do you want to replace it? Esse arquivo SysEx já existe: "%1" Você quer substituí-lo? &Import... &Importar... E&xport... E&xportar... Import SysEx Files Importar arquivo SysEx Move SysEx item down on list order Mover item SysEx para baixo na lista Export to SysEx file Exportar para um arquivo SysEx All files (*.*) Tudo (*.*) &Remove &Remover Upda&te A&tualizar Sysex name Nome do SysEx Warning Atenção About to delete SysEx: "%1" Are you sure? O seguinte arquivo SysEx será apagado: "%1" Posso continuar? SysEx settings have been changed. Do you want to apply the changes? Algumas configurações do SysEx foram modificadas. Você quer mesmo manter essas alterações? Import from SysEx file Importar de um arquivo SysEx qtractorMidiThumbView MIDI Thumb view Vista em miniatura MIDI qtractorMidiToolsForm % % P: P: BBT CPS (Compasso Pulso Subpulso) Flat Neutro Ramp Rampa Time Tempo (m:s:ms) none nenhum temporamp Cubic Cúbico Randomize note/pitch Aleatorize nota/altura Timeshift selected events Eventos de mudança de velocidade About to delete preset: "%1" Are you sure? A seguinte pré configuração será apagada: "%1" Posso continuar? Scale-quantize type Tipo de quantização da escala Transpose time Transpor tempo Transpose note Transpor nota Scale-quantize key Armadura da escala a quantizar Normalize selected events Normalizar eventos selecionados Swing-quantize type Tipo de suingue da quantização Swing-quantize time Tempo de suingue da quantização &Note: &Nota: &Time: &Tempo: Resize value Redimensionar valor Quantize selected events Quantizar eventos selecionados timeshift mudança de tempo Timeshift Mudança de Velocidade Randomize value Aleatorize Valor T&imeshift &Mudança de Velocidade P = 0 : no change. P > 0 : accelerating shift. P < 0 : slowing down shift. Edit head/tail (blue) markers define the shift range. P = 0 : sem mudancas. P > 0 : acelera (mais rápido). P < 0 : desascelera (mais devagar). Edição do começo/tamanho dos marcadores (azul) definem a mudança de velocidade. randomize aleatorizar &Randomize Aleato&rizar Frames Amostras Rescale duration Reescalonar duração Linear Linear Resize selected events Redimensionar eventos selecionados Timeshift curve Curva de Mudança de Velocidade S&wing: S&uingue: Resize duration Redimensionar duração Quantize duration percent Percentual da duração da quantização Save preset Salvar pré configuração resize redimensionar Quadratic Quadrática Timeshift duration Duração da mudança de velocidade Re&scale Ree&scalonar Swing-quantize percent Percentual de suingue da quantização Quantize duration Duração da quantização Rescale time Reescalonar tempo Randomize time Aleatorizar tempo &Transpose &Transposição transpose transpor &Duration: &Duração: Timeshift parameter (log) Parâmetro de mudança de velocidade (log) rescale reescalonar Resi&ze Redimen&sionar Quantize time Tempo de quantização &Normalize &Normalizar normalize normalizar Quantize time percent Percentual de tempo de quantização Transpose selected events Transpor eventos selecionados Transpose time format Transpor formato de tempo &Reverse &Reverso &Compress Randomize duration Aleatorizar duração Resize value mode Redimensionar o modo do valor Legato mode Mono Poly Split notes length Split notes offset Relative Absolute &Invert &Inverter T&empo ramp Tempo ramp selected events Tempo ramp From Tempo ramp start to Temporamp end Edit head/tail (blue) markers define the ramp range. Tempo ramp duration MIDI Tools Ferramentas MIDI &Quantize &Quantizar quantize quantizar &Scale: E&scala: &Value: &Valor: (default) (padrão) Rescale selected events Reescalonar eventos selecionados Preset name Nome das pré configurações &Percent: &Percentual: Resize final value Redimensionar valor final &Legato: Legato trim/extend type Normal Normal Trim Extend Legato trim/extend length &Join &Split: Timeshift parameter Parâmetro de mudança de velocidade Warning Atenção Delete preset Apagar pré configuração Randomize selected events Aleatorize os eventos selecionados Normalize value Valor de normalização Normalize percent Percentual de normalização Resize duration format Redimensionar o formato de duração Rescale value Reescalonar valor qtractorMixer Mixer Mixer Inputs Entradas Tracks Pistas Outputs Saídas qtractorMixerMeter Pan qtractorMixerRackWidget &Inputs &Entradas &Outputs &Saídas &Monitor &Monitorar &Buses... &Canais Primários... &Audio &MIDI &MIDI qtractorMixerStrip (MIDI) (MIDI) (None) (Nenhuma) Connect %1 Conectar %1 outputs saídas inputs entradas (Audio) (Ãudio) In Out qtractorMonitorButton monitor monitorar qtractorOptionsForm 6 6 7 7 8 8 9 9 10 10 11 11 12 12 dB dB &Up Para Ci&ma ... ... BBT CPS (Compasso Pulso Subpulso) &Add &Adicionar 0 dB 0 dB 3 dB 3 dB 6 dB 6 dB Full Completo Last Última None Nenhuma Peak Pico Over Over Time Tempo (H:m:s) Audio sample format to use on capture (record) and export Formato da amostra de áudio utilizada na captura (gravação) e na renderização &Down &Para Baixo &MIDI &MIDI &MMC: &MMC: &SPP: &SPP: (Any) (Todos) 10 dB 10 dB &Gain (bar): &Vol.: Reverse &keyboard modifiers role (Shift/Ctrl) Inverter regras das &teclas modificadoras (Shift/Ctrl) Whether to keep all editor windows on top of the main window Sempre manter todas as janelas de edição na frente da janela principal Keep &editor windows always on top Manter janela de &edição sempre na frente Whether to use RubberBand formant preserve RubberBand &formant preserve Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine Connections Conexões Whether to warn about audio self-connections &Warn about self-connections &Count-in: Count-in mode Recording Count-in number beats &File (bar): &Arquivo (compasso): &Offset (latency): &Desvio (latência): Metronome Audio offset (latency) Desvio do Metronomo de áudio (latência) Whether to reset/resend all controllers on playback start Sempre que reiniciar/reenviar todos os controladores ao iniciar a reprodução &Reset all controllers on playback start &Reiniciar todos os controladores na reprodução Metronome MIDI duration (bar) Duração do metrônomo MIDI (compasso) Metronome MIDI duration (beat) Duração do metrônomo MIDI (pulsação) Metronome MIDI offset (latency) Desvio do metrônomo MIDI (latência) &Hold auto-scrolling (follow play-head) on edits &Manter rolagem automática (Segue indicador de progresso) nas edições Trac&k color saturation: Saturação de cor da &Pista: Default new track color saturation Nova saturação de cor padrão % % Custom Personalizado &Color theme: Tema de &Cores: Custom color palette theme Tema de cores com paleta padrão Wonton Soup DO NOT TRANSLATE KXStudio DO NOT TRANSLATE Custom widget style theme Tema padrão para widgets &Icons theme: Custom icons theme directory Browse for custom icons theme directory St&yle sheet: Custom style sheet (*.qss) Browse for custom style sheet (*.qss) Move down path Move para baixo no caminho Blacklist Lista Maldita Plugin blacklist path Caminho de Plugins malditos Browse plugin blacklist path Navegar caminho de plugins malditos Add plugin blacklist path Adicionar caminho à lista de Plugins Malditos Plugin blacklist paths Caminhos da lista de Plugins Malditos Remove plugin blacklist path Remover caminho da lista de Plugins malditos Clear plugin blacklist paths Limpar caminhos na Lista de Plugins malditos &Clear &Limpar Whether to enable MIDI queue time drift correction Correção de fluxo de tempo de espera MIDI sempre habilitado E&nable MIDI queue time drift correction &Habilitar Correção de fluxo de tempo e espera MIDI First Primeira Plugin path Local do plugin Input Entrada Plugin type Tipo de Plugin Paths Locais LV2 Presets Directory Pasta de pré configurações LV2 Slave Escravo &Note (bar): &Nota (compasso): Auto-save period (minutes) Período de tempo em que a sessão é salva automaticamente (minutos) Metronome Audio filename (bar) Nome do arquivo de áudio para metrônomo (compasso) Whether to have separate MIDI control ports Sempre ter portas de controle MIDI separadas Whether to keep all tool windows on top of the main window Sempre manter todas as janelas de ferramentas na frente da janela principal &Loop recording mode (takes): Modo de gravação em &loop (tentativas): Whether to have separate MIDI player output ports Sempre ter portas de saída MIDI separadas Audio compression quality to use on capture (record) and export Qualidade de compressão de áudio para usar na captura (gravação) e na renderização Sample-rate converter quality Qualidade da conversão de taxa de amostragem Metronome Beat Audio File Arquivo de áudio do pulso do metrônomo &Enable MIDI metronome &Habilitar metrônomo MIDI &Velocity (bar): &Intensidade (compasso): Reset meter colors to default Voltar a cor original do medidor Default session file format (suffix) Formato padrão de arquivo de sessão (extensão) Session template files (*.qtr *.qts *.%1) Arquivos de modelo de sessão (*.qtr *.qts *.%1) Messages log file Arquivo com o diário de bordo Au&to-connect Auto &conectar The maximum number of message lines to keep in view O máximo de linhas de mensagens mantidas para exibição Whether to enable session auto-save (crash-recovery) Para habilitar o salvamento automático da sessão (recuperação de dados) Session Template Modelo de Sessão Whether to create new sessions based on template Se quiser criar novas sessões baseadas em modelo específico Browse for new session template Procurar por novos modelos para sessão Whether to ask for confirmation on archive directory removal Sempre pergunta por confirmação quando apagar pastas C&onfirm archive removals &Confirmar a remoção de pastas Whether to reverse keyboard modifiers role on transport positioning (Shift/Ctrl) Inverte a função das regras das teclas (Shift/Ctrl) em relação a barra de controle de reprodução/gravação Whether to start as timebase master (JACK) Sempre utilizar tempo base mestre (JACK) &Timebase &Tempo Base File &format: &Formato do arquivo: Queue &timer (resolution): &Tempo de espera (resolução): MIDI Cloc&k: &Pulso MIDI: Whether to use desktop environment native dialogs. Sempre utilizar diálogos nativos do ambiente de trabalho. Use desktop environment &native dialogs Utilizar diálogos &nativos do ambiente de trabalho Manage custom color palette themes Gerenciar Tema de Cores com paleta padrão &Style theme: &Estilo do tema: Back Voltar lines linhas Sample messages text font display Exemplo de fonte para texto de mensagens Select font for the messages text display Selecionar fonte para mensagens de texto exibidas Playback Reprodução Whether to enable the MIDI metronome Sempre habilitar metrônomo MIDI File &type: &Tipo de arquivo: &Audio Ã&udio Auto-&connect Auto &conectar &MIDI: &MIDI: MIDI Machine Control (MMC) mode Modo de Controle da Máquina MIDI (MMC) &Reset &Resetar Whether to show the complete directory path of loaded session files Sempre mostra o endereço completo dos arquivos de sessão carregados Messages Font Fonte das Mensagens All files (*.*) Todos os arquivos (*.*) Queue timer (resolution) Tempo de espera (resolução) Whether to activate a messages logging to file. Sempre ativar a criação de diário de bordo em um arquivo. Logging Diário de bordo &New session template: &Novo modelo de sessão: Whether to auto-connect dedicated audio metronome outputs Sempre auto conectar saídas de áudio dedidados ao metrônomo Re&verse middle-button modifier role (Shift/Ctrl) Re&funcionalizar rolagem do mouse (Shift/Ctrl) Base application font size (pt.) Tamanho das fontes padrão do aplicativo (pt.) minutes minutos Sinc (Medium Quality) Sinc (Média Qualidade) &Gain (beat): &Vol.: &File (beat): &Arquivo (pulso): Log files (*.%1) Arquivos de Diário de Bordo (*.%1) Save &backup versions of existing sessions: Salvar versões de &backup de sessões existentes: Editor Editor Duplex Duplex Audio file type to use on capture (record) and export Tipo de arquivo de áudio utilizada na captura (gravação) e na renderização LV2 Presets directory (default: ~/.lv2) Pasta de pré configurações de LV2 (padrão: ~/.lv2) Dedicated au&dition/pre-listening player outputs: Saídas de repro&dução para pré escuta/audição: Whether to auto-connect dedicated audio player outputs Sempre auto conectar saídas para pré escuta Frames Número de Amostras Some settings have been changed. Do you want to apply the changes? Algumas configurações foram modificadas. Você quer mesmo manter as alterações? Plugin paths Locais dos Plugins Select custom audio meter color Selecionar uma cor padrão para o medidor de áudio Whether to have separate MIDI metronome output port Sempre ter portas de saída separadas para o metrônomo MIDI Capture standard &output Saídas padrã&o de captura Linear Linear Master Master Meters Medidores Zero Order Hold Extrapolador de Ordem Zero Audio meter color Cor do medidor de áudio Audio meter level Medidor do nível de áudio Output Saída &LV2 Presets directory: Pasta de pré configurações para &LV2: Dedicated MIDI p&layer outputs Saídas de &reprodução MIDI dedicadas &Note (beat): &Nota (pulso): Dedicated audi&o outputs: Saídas de áudi&o dedicadas: &Duration (beat): &Duração (pulso): Whether to apply WSOLA quick seek time-stretching Se usa WSOLA para mudança rápida de esticar-tempo (time-stretching) Metronome Bar Audio File Arquivo de áudio do compasso do metrônomo Dedicated a&udio metronome outputs: Saídas de áudio &dedicadas para metrônomo: Metronome MIDI note (beat) Nota do metrônomo MIDI (pulso) Options Opções Whether to enable the audio metronome Sempre habilitar o som do metrônomo Metronome MIDI velocity (bar) Intensidade do metrônomo MIDI (compasso) Messages &log file: Arquivo do &diário de bordo: &Time display format: Forma de exibição do &Tempo: &Confirm removals &Confirmar a remoção de arquivos &Plugins &Plugins Default session &file format: &Formato padrão de arquivo de sessão: Whether to have separate audition/pre-listening player output ports Quando for necessário ter saídas separadas para uma pré audição Dedicated MIDI &control input/output Entrada/saída de &controle MIDI dedicadas Transport &mode: &Modo dos Controles: Metronome MIDI channel Canal do Metrônomo MIDI Whether to keep a maximum number of lines in the messages window Sempre manter o máximo de linhas dentro da janela de mensagens MIDI Clock control mode Modo de controle da pulsação MIDI (Clock) Loop recording mode (takes) Modos de gravação em loop (tentativas) Whether to use WSOLA time-stretching Sempre utilizar "esticar-tempo" do tipo WSOLA Whether to auto-connect dedicated audio output ports Sempre auto conectar saídas de áudio dedicadas Dedicated M&IDI metronome output Saídas dedicadas para o metrônomo M&IDI &Quantize: &Quantização: Transport Controles de Gravação e Reprodução Metronome MIDI note (bar) Nota do metrônomo MIDI (compasso) Transport control mode (JACK) Modo de controle de reprodução com JACK Open plugin's &editor (GUI) by default Abrir &editor de plugins (GUI) por padrão Sinc (Fastest) Sinc (Mais rápida) Whether to capture standard output (stdout/stderr) into messages window Sempre exibe saídas padrão de captura (stdout/stderr) na janela de mensagens Messages Log Mensagens do Diário de Bordo Select custom MIDI meter color Selecionar cor padrão para medidores MIDI Sample &format: &Formato da Amostra: Auto-co&nnect Auto co&nectar Increment current version Incrementa a versão atual Aut&omatic time-stretching "Esticar-tempo" au&tomático Whether to ask for confirmation on removal Sempre pergunta por confirmação quando apagar arquivos Move up path Mover local Para Cima Whether to remove audio peak files on session close Sempre exclui os arquivos com as formas de onda dos clips de áudio ao fechar a sessão MIDI file format to use on capture (record) and export Formato de arquivo MIDI utilizado na captura (gravação) e renderização Session Sessão Sinc (Best Quality) Sinc (Melhor Qualidade) Instruments Instrumentos Keep tool &windows always on top Manter janelas de ferramentas &sempre na frente Whether to apply time-stretching when tempo changes Sempre aplicar "esticar-tempo" (time-stretching) quando o tempo mudar Browse for sample audio file (beat) Procurar por arquivo de áudio com som para marcação do pulso &Select plugin's editor (GUI) if more than one are available &Selecionar o editor do plugin (GUI) se houver mais de uma opção &Duration (bar): &Duração (compasso): Control Controle Add plugin path Adicionar local de Plugins &Audio: Ã&udio: XML Default (*.%1) XML Padrão (*.%1) XML Regular (*.%1) XML Habitual (*.%1) ZIP Archive (*.%1) Arquivo ZIP (*.%1) Open Style Sheet Style Sheet files (*.%1) Icons Theme Directory Plug-in Blacklist Lista de Plugins malditos Plug-in files (*.%1) Arquivos de Plug-in(*.%1) &Device: &Dispositivo: Auto-save current working session every: Salvamento automático a cada: &Display &Exibição Plug-in Directory Pasta de Efeitos &WSOLA time-stretching "Esticar-tempo" do tipo &WSOLA &Velocity (beat): &Intensidade (pulso): &Quality: &Qualidade: &Remove &Remover Metronome Metrônomo (default) (padrão) Defaults Padrões Whether to reverse mouse middle-button role on keyboard modifiers (Shift/Ctrl) Modifica função da rolagem do mouse sempre que o teclado for acionado (Shift/Ctrl) (Rolagem horizontal) MIDI capture (record) quantization Quantização de captura de MIDI (gravação) Metronome MIDI velocity (beat) Intensidade do metrônomo MIDI (pulso) S&how complete path of session files &Endereço completo do arquivo de sessão Messages Mensagens Whether to select plugin's editor (GUI) if more than one are available Sempre utiliza o editor do plugin (GUI) se houver mais de uma opção MIDI Machine Control (MMC) device id. Identificação do dispositivo MMC (Controle de Máquina MIDI). Metronome Audio filename (beat) Nome do arquivo de áudio para metrônomo (pulso) Whether to try dropping multiple audio files into the same track Sempre tenta colocar multiplos arquivos de áudio na mesma pista &Font... &Fontes... Time display format Formato de exibição do tempo Warning Atenção M&essages limit: Limite de m&ensagens: &General &Geral Browse plugin path Procurar local do plugin The maximum number of recent files to keep in menu O número máximo de arquivos que permanecem em arquivos recentes no menu Metronome gain (beat) Volume da marcação do pulso do metrônomo &Drop multiple audio files into the same track &Põe múltiplos arquivos de áudio na mesma pista Browse for the messages log file location Procurar por arquivo com diário de bordo Sample-&rate converter type: &Tipo de conversão de taxa de amostragem: Whether to have separate audio output ports Sempre ter saídas de áudio separadas Whether to open plugin's editor (GUI) by default Sempre abrir o editor do plugin (GUI) por padrão &Channel: &Canal: Which mode to rename existing session files Como irá renomear arquivos de sessões existentes Browse LV2 Presets directory Procurar por pasta com pré configurações de efeitos LV2 Increment previous version (default) Incrementa a versão anterior (padrão) Remove plugin path Remover local de Plugins Capture / Export Capturar / Renderizar &Base font size: &Tamanho dos caracteres: MIDI meter color Cor dos medidores MIDI MIDI meter level Medidor do nível MIDI New session template Novo modelo de sessão Number of &recent files: Quantidade de arquivos &recentes: Whether to save backup versions of existing sessions Se quiser salvar versões de backup de sessões existentes MIDI Song Position pointer (SPP) control mode Modo de controle do ponteiro de posição de música MIDI (SPP) Whether to have separate audio metronome output ports Sempre utilizar saídas de áudio dedicadas somente ao metrônomo &Enable audio metronome &Habilitar som do metrônomo Browse for sample audio file (bar) Procurar por arquivo de áudio com som para marcação de compasso Auto-remove audio pea&k files &Eliminar aquivos de forma de onda Metronome gain (bar) Volume da marcação de compasso do metrônomo WSOLA quic&k seek WSOLA mudança &rápida Audio Meter Color Cor do medidor de áudio MIDI Meter Color Cor dos medidores MIDI Whether to hold auto-scrolling (follow play-head) on edits. Sempre manter a rolagem automática (seguir o cursor de reprodução) na edição. qtractorPaletteForm Color Themes Cores dos Temas Name Nome Current color palette name Nome da cor da paleta atual Save Salvar Save current color palette name Gravar o nome da cor da paleta atual Delete Apagar Delete current color palette name Apagar o nome da cor da paleta atual Palette Paleta Current color palette Cor da paleta atual Generate: Gerar: Base color to generate palette Cor base para geração da paleta Reset Resetar Reset all current palette colors Resetar todas as cores de paleta atuais Import a custom color theme (palette) from file Importar tema de cores (paleta) personalizado de um arquivo Import... Importar... Export a custom color theme (palette) to file Exportar um tema de cores (paleta) para um arquivo Export... Exportar... Show Details Mostrar Detalhes Import File - %1 Importar Arquivo - %1 Palette files (*.%1) Arquivos de Paleta (* %1) Save Palette - %1 All files (*.*) Todos os arquivos (*.*) Warning - %1 Cuidado - %1 Could not import from file: %1 Sorry. Não foi possível importar arquivo: %1 Desculpe. Export File - %1 Exportar Arquivo - %1 Some settings have been changed. Do you want to discard the changes? Algumas configurações foram modificadas. Você deseja descartar essas alterações? Some settings have been changed: "%1". Do you want to save the changes? Algumas configurações foram modificadas. "%1". Você deseja salvar essas alterações? qtractorPaletteForm::PaletteModel Color Role Rolo de Cor Active Ativar Inactive Desativar Disabled Desabilitar qtractorPasteRepeatForm BBT CPS (Compasso Pulso Subpulso) Time Tempo Paste Repeat Repetir Colar Repeat count Número de repetições Frames Amostras Some settings have been changed. Do you want to apply the changes? Algumas configurações foram modificadas. Você quer mesmo manter as alterações? Repeat Repetição &Period: &Período: &Count: &Contagem: Repeat period format Formato de repetição de período Warning Atenção Repeat period Repetir período qtractorPluginForm ... ... Edit Editar Inputs (Returns) Entradas (Retornos) &None &Nada About Sobre Error Erro Sends Mandadas %1 [%2], %3 instance(s), %4 channel(s). %1 [%2], %3 instância(s), %4 canal(s). Manage buses Gerenciar Canais Primários (none) (nenhum) Preset files (*.%1) Arquivos com pré configurações (*.%1) Active Ativar Save Preset Salvar pré configuração Open Preset Abrir pré configuração Page %1 Página %1 All files (*.*) Todos os arquivos (*.*) Preset could not be loaded from file: "%1". Sorry. Não foi possível carregar o preset do arquivo: "%1". Desculpe. Preset could not be saved to file: "%1". Sorry. Não foi possível salvar o preset para o arquivo: "%1". Desculpe. Latency: %1 ms (%2 frames) Latência: %1 ms (%2 amostras) (no latency) (sem latência) Outputs (Sends) Saídas (Mandadas) Save preset Salvar pré configuração Plugin Properties Propriedades de Plugins Open preset Abrir pré configuração Alias: Plugin alias Edit plugin Editar Plugin About to delete preset: "%1" (%2) Are you sure? A seguinte pré configuração será apagada: "%1" (%2) Posso continuar? Returns Retornos Preset name Nome da pré configuração Aux Send Bus: Mandada Auxiliar em um Canal Primário: Warning Atenção Delete preset Apagar pré configuração Auto-connect Audio bus I/O matrix I/O Matrix... Direct Access Parameter Acesso Direto a Parâmetros Direct Access Acesso Direto qtractorPluginListView &Edit &Editar &None &Nada &Sends &Mandadas &Auto-connect &Auto-conectar C&ancel C&ancelar Import Plugins Importar Plugins XML files (*.%1) Arquivos XML (*.%1) All files (*.*) Todos os arquivos (*.*) Warning Atenção About to remove and import all plugins: "%1" Are you sure? Sobre remover e importar os plugins: "%1" Posso continuar? Export Plugins Exportar Plugins Aux Send: I&nserts I&nsertores &Audio &Ãudio &MIDI &MIDI Add &Controller Re&move All Re&mover Tudo Move &Up Mover Para &Cima Pre&set P&ré-configurações Dire&ct Access A&cesso Direto &Import... &Importar... E&xport... E&xportar... &Outputs &Saídas copy plugin copiar plugin Move &Down Mover para &Baixo &Copy Here &Copiar para Cá remove all plugins remover todos os plugins &Returns &Retornos Add &Insert Adicionar &Insertor &Dedicated &Dedicadas Add &Aux Send Adicionar Mandada &Auxiliar &Remove &Remover &Move Here &Mover para Cá &Add Plugin... &Adicionar Plugin... activate all plugins ativar todos os plugins deactivate all plugins desativar todos os plugins &Properties... &Propriedades... Ac&tivate A&tivar Deactivate Al&l Desativar T&udo Acti&vate All Ati&var Tudo qtractorPluginParamWidget Open File Abrir Arquivo qtractorPluginSelectForm X X RT RT EXT EXT GUI GUI MIDI MIDI Name Nome Path Local Type Tipo Plugin search string (regular expression) Plugin de busca de palavra (expressão regular) Audio Ãudio Index Ãndice Plugin type Tipo de plugin Modes Modos Reset filter Resetar filtros Available plugins Plugins disponíveis Control Controle Plugins Plugins Instances Instâncias Plugin scanning in progress... Procura por plugins em processo... Rescan for available plugins (refresh) Procurar novamente por plugins (atualizar) &Rescan &Atualizar qtractorSessionForm % % ... ... Time Tempo View Visualização 44100 44100 Whether to auto-name the session directory Sempre nomear automaticamente a pasta da sessão &Auto &Automático 48000 48000 96000 96000 Horizontal Zoom (%) Zoom Horizontal (%) T&icks/Beat: T&ics/Compasso: Session name Nome da sessão Session description Descrição da sessão &Name: &Nome: &Vertical Zoom: Zoom &Vertical: Pixels/beat Subdivisão/pulso &Snap/Beat: &Ajuste à Grade: 192000 192000 Some settings have been changed. Do you want to apply the changes? Algumas configurações foram modificadas. Você quer mesmo manter as alterações? Properties Propriedades Tempo (BPM) / Signature Andamento (BPM) Snap/beat Ajuste à grade de acordo com a subdivisão do pulso Resolution (ticks/beat; tpqn) Resolução (tics/compasso; tpqn) Session directory Pasta da sessão Session Directory Pasta da Sessão Session Sessão &Description: &Descrição: Sample &Rate: Taxa de Amost&ragem: &Directory: &Pasta: Vertical Zoom (%) Zoom Vertical (%) &Tempo: &Andamento: Sample rate (Hz) Taxa de amostragem (Hz) Warning Atenção Browse for session directory Procurar por pasta da sessão Session directory does not exist: "%1" Do you want to create it? A seguinte pasta de sessão não existe: "%1" Deseja criá-la? &Horizontal Zoom: Zoom &Horizontal: &Pixels/Beat: &Subdivisão/Pulso: qtractorShortcutForm Search shortcuts Keyboard shortcut (%1) already assigned (%2). Atalho de teclado (%1) já utilizado por (%2) Keyboard shortcuts have been changed. Do you want to apply the changes? Atalhos no teclado foram modificados. Você quer aplicar essas alterações? MIDI Controller shortcuts have been changed. Do you want to apply the changes? Os atalhos do Controlador MIDI foram modificados. Deseja aplicar as modificações? &MIDI Controller... Controlador &MIDI... Shortcuts Atalhos Shortcut search string (regular expression) Menu/Action Menu/Ação Description Descrição Keyboard Teclado MIDI Controller Controlador MIDI Warning Atenção qtractorTakeRangeForm BBT CPS (Compasso Pulso Subpulso) Time Tempo (h:m:s) &Edit &Editar &Loop &Loop En&d: &Final: Range Limites Current take Tentativa atual Loop range Limites do Loop Clip start Começo da sequência &Punch Gravação &Localizada &Selection &Seleção Take Range Limites de Gravação por Tentativas Selection range Limites da Seleção Format Formato Frames Amostras Select Selecionar Edit range Limites da Edição Clip offset Deslocamento da Sequência Punch range Limites da Gravação Localizada St&art: &Início: Take %1 Tentativa %1 &Custom &Personalizado Custom range Limites Personalizados Time display format Formato de exibição do tempo qtractorTempoAdjustForm BBT CPS (Compasso Pulso Subpulso) T&ap T&ap Time Tempo (h:m:s) Range Limites &Tempo: &Andamento: &Detect &Detectar R&eset &Length: Com&primento: &Beats: &Pulsação: A&djust A&juste Range length Tamanho dos limites Format Formato Frames Amostras Some settings have been changed. Do you want to apply the changes? Algumas configurações foram modificadas. Você quer mesmo manter as alterações? Range start Começo do limite Range beats Tamanho do compasso Tempo/Time signature Andamento e Fórmula de Compasso Tempo Adjust Ajuste de Andamento Metronome Metrônomo &Start: &Início: Time display format Formato de exibição do tempo Warning Atenção qtractorThumbView Thumb view Vista em miniatura qtractorTimeScale C B# C# Db D D# Eb E Fb F E# F# Gb G G# Ab A A# Bb B Cb qtractorTimeScaleForm ... ... Bar Compasso &Add &Adicionar T&ap T&ap Tempo Map / Markers Mapa de Tempo / Marcadores Time Tempo Key Tom &Bar: &Compasso: Tempo (BPM) / Time signature Andamento (BPM) / Fórmula de Compasso &Key signature: &Fórmula de Compasso: Key signature (accidentals) Fórmula de Compasso (acidentes) Key signature (mode) Fómula de compasso (modo) - Major Maior Minor Menor &Marker: &Marcador: Tempo &scale factor: &Fator de Proporção do tempo: Close Fechar Tempo Andamento Bar location Local da cabeça do compasso Marker color Cor do marcador tempo factor fator de tempo Close this dialog Fechar este diálogo App&ly Ap&licar Some settings have been changed. Do you want to apply the changes? Algumas configurações foram modificadas. Você quer mesmo manter as alterações? Tempo scale factor Fator de escalonamento do Andamento Marker Marcador T&ime: T&empo: Time/frame location Tempo/localização na amostra Re&fresh Atual&izar Refresh tempo map Atualizar mapa de tempo &Refresh &Atualizar Update node Atualizar nó Add node Adicionar nó Tempo map / Markers Mapa de Tempo / Marcadores &Remove &Remover &Tempo: &Andamento: &Update &Atualizar Marker text Marcador de texto Remove node Remover nó Warning Atenção About to remove tempo node: %1 (%2) %3 %4/%5 Are you sure? Será excluido o nó de tempo: %1 (%2) %3 %4/%5 Posso continuar? Some settings have been changed. Do you want to discard the changes? Algumas configurações foram modificadas. Você deseja descartar essas alterações? Marker Color Cor do marcador qtractorTimeSpinBox &BBT &CPS &Time &Tempo &Frames &Amostras qtractorTrackForm &Up Para &Cima ... ... Track icon Figura da pista Type Tipo MIDI Patch: Drum Mode Programação MIDI: Modo de Percussão Auto &Down Para &Baixo Whether to enable plugin latency/delay compensation Sempre ativar o plugin de compensação de latência/atraso &Latency compensation &Compensação de latência Current total latency &MIDI &MIDI &Omni &Omni Patch Programação Track Pista MIDI Patch: Bank Programação MIDI: Banco MIDI track type Pista MIDI MIDI Omni: Capture All Channels Modo Omni: Captura Todos os Canais de vez Input / Output Entrada / Saída Select custom track foreground color Selecionar cor padrão do primeiro plano das pistas Manage buses Gerenciar Canais Primários &Bank: &Banco: &Audio &Ãudio &Name: &Nome: Custom &Icon... Padrão &Figura... &Drums &Bateria Drum &Kit &Kit de percussão &Bass &Baixo A&coustic Bass Baixo A&cústico &Guitar &Guitarra &Electric Guitar Guitarra &Elétrica &Piano &Teclas &Acoustic Piano &Piano &Microphone &Microfone Vi&ntage Microphone Microfone &Vintage &Speaker &Alto falante &Trumpet &Trompete &Violin &Violino (None) (Nada) Some settings have been changed. Do you want to apply the changes? Algumas configurações foram modificadas. Você quer mesmo manter as alterações? Image files (%1) Arquivos de Imagem (%1) All files (*.*) Todos os arquivos (*.*) %1 ms (%2 frames) (no latency) (sem latência) Track Icon Figura da Pista Normal Normal Output bus name Nomes das saídas dos Canais Primários Plugins Plugins Add plugin Adicionar plugin Background color Cor do segundo plano Bank &Select Method: Método de &Seleção de Banco: Track name description Descrição do nome da pista Select custom track background color Selecionar cor padrão do segundo plano das pistas Bank LSB Banco LSB Bank MSB Banco MSB View / Colors Visualização / Cores (No instrument) (Sem instumento) MIDI / Instrument MIDI / Instrumento Input bus name Nomes das entradas dos Canais Primários &Program: &Programa: Move plugin down Mover Plugin para Baixo Audio track type Pista de áudio Bac&kground: &Segundo Plano: &Add... &Adicionar... MIDI Patch: Instrument Programação MIDI: Instrumento &Remove &Remover MIDI Patch: Bank Select Method Programação MIDI: Método de seleção do Banco &Foreground: &Primeiro Plano: MIDI Channel (1-16) Canal MIDI (1-16) Move plugin up Mover Plugin para Cima Foreground color Cor do Primeiro Plano Warning Atenção &Channel: &Canal: Track plugins Plugins na pista MIDI Patch: Program Programação MIDI: Programa Remove plugin Remover plugin Foreground Color Cor do Primeiro Plano Background Color Cor do segundo plano qtractorTrackList Nr Nº Track Name Nome da Pista Bus Canal Primário Ch Cnl Patch Programação Instrument Instrumento qtractorTrackTime Edit-head Editar começo Edit-tail Editar final Start: %1 End: %2 Length: %3 Início: %1 Final: %2 Tamanho: %3 Loop-start Começo do Loop Punch-out Final da Gravação Localizada Loop-end Final do Loop Play-head Começo da reprodução Punch-in Começo da Gravação Localizada qtractorTrackView cut recortar paste automation colar automação split quebrar Start: %1 End: %2 Length: %3 Início: %1 Final: %2 Tamanho: %3 Zoom in (vertical) Aumentar Zoom (vertical) Zoom out (vertical) Diminuir Zoom (vertical) Zoom in (horizontal) Aumentar Zoom (horizontal) delete apagar fade-out fade de saída clip resize redimensionar sequência clip repeat repetidor de sequência %1 clip %1 sequência Zoom out (horizontal) Diminuir Zoom (horizontal) clip %1 sequência %1 move automation mover automação add clip adicionar sequência paste clip colar sequência clip stretch mudar tamanho da sequência move clip mover sequência fade-in fade de entrada %1 automation %1 automação Zoom reset Resetar Zoom qtractorTracks MIDI file import: "%1", track-channel: %2. Importar arquivo MIDI: "%1", canal da pista: %2. split clip quebrar sequência tempo ramp clip cross-fade crossfade da sequência remove range remover limite insert range inserir limite Audio file import: "%1". Importar arquivo de áudio: "%1". MIDI file import "%1" on %2 %3. Importar arquivo MIDI "%1" em %2 %3. clip merge unir sequências Remove Range Remover Limite MIDI file import "%1" track-channel %2 on %3 %4. Importar arquivo MIDI: "%1", canal da pista: %2 em%3 %4. Insert Range Inserir Limite Merge/Export MIDI Clip Unir/Renderizar Sequência de MIDI timeshift mudança de tempo About to remove track: "%1" Are you sure? Será excluida a pista: "%1" Posso continuar? randomize aleatorizar Audio file import "%1" on %2 %3. Renderização do arquivo de áudio "%1" em %2 %3. Audio clip merge/export: "%1" started... União/renderização de sequências de áudio: "%1" iniciado... Tracks Pistas clip import importar sequência MIDI file import: "%1". Importar arquivo de MIDI: "%1". resize redimensionar transpose transpor rescale reescalonar normalize normalizar MIDI clip merge/export: "%1" complete. União/renderização de sequências MIDI: "%1" completo. quantize quantizar insert track range inserir limite à pista clip normalize normalizar sequência new clip nova sequência mute clip Merge/Export Unir/Exportar Audio clip merge/export: "%1" complete. União/renderização de sequências de áudio: "%1" completo. MIDI files (*.mid *.smf *.midi) Arquivos MIDI (*.mid *.smf *.midi) All files (*.*) Todos os arquivos (*.*) MIDI clip merge/export: "%1" started... União/renderização de sequências MIDI: "%1" iniciado... Warning Atenção remove track range remover limite da pista Merge/Export Audio Clip Unir/Renderizar Sequência de Ãudio qtractor-1.5.9/src/translations/PaxHeaders/qtractor_ja.ts0000644000000000000000000000013215101070305020624 xustar0030 mtime=1761898693.102267702 30 atime=1761898693.101267699 30 ctime=1761898693.102267702 qtractor-1.5.9/src/translations/qtractor_ja.ts0000644000175000001440000232510415101070305020623 0ustar00rncbcusers QObject Audio: %1 channels, %2 Hz 音声: %1 ãƒãƒ£ãƒ³ãƒãƒ«ã€%2 Hz (%1 dB) (%1 pan) (%1 パン) (%1% time stretch) (%1% タイムストレッãƒ) (%1 semitones pitch shift) %1 In %1 入力 %1 Out %1 出力 Audio files (%1) 音声ファイル (%1) All files (*.*) 全ファイル (*.*) %1 (%2) %3 channels, %4 frames, %5 Hz %6 %1 (%2) %3 ãƒãƒ£ãƒ³ãƒãƒ«ã€%4 フレームã€%5 Hz %6 Duplex 入出力 Output 出力 Input 入力 None ãªã— Name: %1 åå‰: %1 (take %1/%2) (テイク %1/%2) [Mute] Start: %1 Offset: %2 End: %3 Length: %4 é–‹å§‹: %1 オフセット: %2 終了: %3 é•·ã•: %4 File: %1 ファイル: %1 take %1 テイク %1 reset takes テイクをリセット clip save clip unlink clip tool %1 クリップツール %1 clip record クリップã®éŒ²éŸ³ automation select オートメーションã®é¸æŠž automation mode オートメーションã®ãƒ¢ãƒ¼ãƒ‰ automation play オートメーションã®å†ç”Ÿ automation record オートメーションã®è¨˜éŒ² automation logarithmic オートメーションã®å¯¾æ•°è¡¨ç¤º automation color オートメーションã®é…色 automation play all オートメーションをã™ã¹ã¦å†ç”Ÿ automation record all オートメーションをã™ã¹ã¦è¨˜éŒ² automation edit オートメーションã®ç·¨é›† automation clear オートメーションã®ã‚¯ãƒªã‚¢ automation clear all オートメーションをã™ã¹ã¦ã‚¯ãƒªã‚¢ automation edit list オートメーションã®ç·¨é›†ãƒªã‚¹ãƒˆ %1 Monitor %1 モニター create bus ãƒã‚¹ã®ä½œæˆ update bus ãƒã‚¹ã®æ›´æ–° delete bus ãƒã‚¹ã®å‰Šé™¤ move bus ãƒã‚¹ã®ç§»å‹• bus pass-through ãƒã‚¹ã®ãƒ‘ススルー bus gain ãƒã‚¹ã®ã‚²ã‚¤ãƒ³ bus pan ãƒã‚¹ã®ãƒ‘ン Insert Send/Return pseudo-plugin (Audio) 擬似センド/リターンプラグイン(音声)を挿入ã™ã‚‹ Insert Send/Return pseudo-plugin (MIDI) 擬似センド/リターンプラグイン(MIDI)を挿入ã™ã‚‹ Send Gain センドã®ã‚²ã‚¤ãƒ³ Dry Gain ドライã®ã‚²ã‚¤ãƒ³ Wet Gain ウェットã®ã‚²ã‚¤ãƒ³ Aux Send (Audio) Auxセンド(音声) Aux Send pseudo-plugin (Audio) 擬似Auxセンドプラグイン(音声) Aux Send pseudo-plugin (MIDI) 擬似Auxセンドプラグイン(MIDI) (none) (ãªã—) %1 (Audio) %1 (音声) %1 (MIDI) %1 (MIDI) Cakewalk Instrument Definition File Cakewalkã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ«ãƒ¡ãƒ³ãƒˆå®šç¾©ãƒ•ァイル File ファイル Date 日付 %1 Bank %2 %1 ãƒãƒ³ã‚¯ %2 %1 - Bank %2 %1 - ãƒãƒ³ã‚¯ %2 (format %1) MIDI: (フォーマット %1) MIDI: Channel %1 ãƒãƒ£ãƒ³ãƒãƒ« %1 Track %1 トラック %1 , %1 tracks, %2 tpqn , %1 トラック〠%2 tpqn (%1% vol) (%1% ボリューム) MIDI file save: "%1", track-channel: %2. MIDIファイルã®ä¿å­˜: %1, トラックãƒãƒ£ãƒ³ãƒãƒ«: %2。 set controller コントローラーをセット reset controller コントローラーをリセット %1 (format %2) %3 tracks, %4 tpqn %5 %1 (フォーマット %2) %3 トラックã€%4 tpqn %5 %1 (format %2) %3 %1 (フォーマット %2) %3 (default) (デフォルト) %1 Hz %1 Hz slave スレーブ %1 (%2) %1 (%2) Usage: %1 [options] [session-file] 使用法: %1 [オプション] [セッションファイル] Options: オプション: Set session identification (uuid) セッションã®ID (uuid) をセット Show help about command line options コマンドラインオプションã«é–¢ã™ã‚‹ãƒ˜ãƒ«ãƒ—を表示 Show version information ãƒãƒ¼ã‚¸ãƒ§ãƒ³æƒ…報を表示ã—ã¾ã™ Session file (.qtr) [session-file] Option -s requires an argument (uuid). Signed 16-Bit Signed 24-Bit Signed 32-Bit Float 32-Bit Float 64-Bit SMF Format 0 SMF Format 1 (Any) (ä»»æ„) Activate アクティベート Aux Send: %1 %1(%2): %3 plugin not found. %1(%2): %3 プラグインãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。 add plugin プラグインã®è¿½åŠ  add insert インサートã®è¿½åŠ  add aux-send Auxセンドã®è¿½åŠ  add MIDI controller aux-send bus Auxセンドãƒã‚¹ã®è¿½åŠ  aux-send matrix remove plugin プラグインã®å‰Šé™¤ move plugin プラグインã®ç§»å‹• activate plugin プラグインを有効化 preset plugin プラグインã®ãƒ—リセット reset plugin プラグインã®ãƒªã‚»ãƒƒãƒˆ plugin program プラグインã®ãƒ—ログラム plugin alias dedicated audio outputs 音声出力専用 direct access param ダイレクトアクセスパラメーター import plugins プラグインã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ session loop セッションをループ session punch セッションパンムsession properties セッションã®ãƒ—ロパティー Beat add tempo node テンãƒãƒŽãƒ¼ãƒ‰ã‚’追加 update tempo node テンãƒãƒŽãƒ¼ãƒ‰ã‚’æ›´æ–° remove tempo node テンãƒãƒŽãƒ¼ãƒ‰ã‚’削除 move tempo node テンãƒãƒŽãƒ¼ãƒ‰ã‚’移動 add marker マーカーã®è¿½åŠ  update marker ãƒžãƒ¼ã‚«ãƒ¼ã®æ›´æ–° remove marker マーカーã®å‰Šé™¤ add key signature 調å·ã®è¿½åŠ  update key signature 調å·ã®æ›´æ–° remove key signature 調å·ã®å‰Šé™¤ move marker マーカーã®ç•°å‹• change time-sig. æ‹å­ã®å¤‰æ›´ã€‚ step input overdub %1 Volume %1 ボリューム %1 Gain %1 ゲイン %1 Pan %1 パニング add track トラックã®è¿½åŠ  remove track トラックã®å‰Šé™¤ duplicate track トラックã®è¤‡è£½ move track トラックã®ç§»å‹• resize track トラックã®ãƒªã‚µã‚¤ã‚º import track トラックã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ track properties トラックã®ãƒ—ロパティー Track assignment failed: Track: "%1" Input: "%2" Output: "%3" トラックã®å‰²ã‚Šå½“ã¦ã«å¤±æ•—ã—ã¾ã—ãŸ: トラック: "%1" 入力: "%2" 出力: "%3" track record トラックを録音 track mute トラックミュート track solo トラックをソロ track monitor トラックモニター track gain トラックゲイン track pan トラックパニング track instrument トラックã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ«ãƒ¡ãƒ³ãƒˆ Automation (%1) オートメーション (%1) none ãªã— Automation オートメーション Unknown 䏿˜Ž Product: プロダクト: Vendor: ベンダー: Manual: Support: Version: ãƒãƒ¼ã‚¸ãƒ§ãƒ³: %1 (*.%2) Copyright: コピーライト: Project: プロジェクト: Select plug-in's editor (GUI): プラグインã®ã‚¨ãƒ‡ã‚£ã‚¿ãƒ¼(GUI)ã‚’é¸æŠž: External 外部 X11 X11 (native) Gtk2 Gtk2 (native) Qt4 Qt5 Other ãã®ä»– Don't ask this again å†ç¢ºèªã—ãªã„ plugin parameters プラグインã®ãƒ‘ラメータ Open File lv2_ui_request_parameter ファイルを開ã Author: 作者: %1: Automation/curve file not found. %1: オートメーション/カーブファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。 Name: åå‰: Category: カテゴリー: Categories: カテゴリー: %1 Record %1 Mute %1 Solo MIDI Controller: %1, %2, %3 Control (MIDI) MIDI Controller Send pseudo-plugin Value 値 qtractorAudioIOMatrixForm Aux-Send I/O Matrix Warning 警告 Some settings have been changed. Do you want to apply the changes? 設定ã®ä¸€éƒ¨ãŒå¤‰æ›´ã•れã¾ã—㟠ã“ã®å¤‰æ›´ã‚’é©ç”¨ã—ã¾ã™ã‹? qtractorAudioListView Name åå‰ Ch ãƒãƒ£ãƒ³ãƒãƒ« Frames フレーム Rate レート Time 時間 Path パス Open Audio Files 音声ファイルを開ã %1: Audio file not found. %1; オーディオファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。 qtractorAudioMixerMeter Gain (dB) ゲイン (dB) dB dB Pan: %1 Gain: %1 dB ゲイン: %1 dB qtractorBusForm Bus list ãƒã‚¹ãƒªã‚¹ãƒˆ Buses ãƒã‚¹ Ch ãƒãƒ£ãƒ³ãƒãƒ« Mode モード Bus ãƒã‚¹ Properties プロパティ &Name: åå‰(&N): Bus name ãƒã‚¹ã®åå‰ &Mode: モード(&M): Bus mode ãƒã‚¹ã®ãƒ¢ãƒ¼ãƒ‰ Input 入力 Output 出力 Duplex 入出力 Bus monitor (pass-through) ãƒã‚¹ã‚’モニターã—ã¾ã™ (パススルー) M&onitor (pass-through) モニター(パススルー)(&O) Audio 音声 Cha&nnels: ãƒãƒ£ãƒ³ãƒãƒ«(&N): Audio channels 音声ãƒãƒ£ãƒ³ãƒãƒ« Audio auto-connect 音声を自動接続 &Auto connect 自動接続(&A) MIDI MIDI MIDI Instrument name MIDIインストルメントå MIDI SysEx setup MIDI SysExã®è¨­å®š SysE&x... Input Plugins 入力プラグイン Input bus plugins 入力ãƒã‚¹ãƒ—ラグイン Add input plugin 入力プラグインã®è¿½åŠ  &Add... 追加(&A)... Remove input plugin 入力プラグインを削除 &Remove 削除(&R) Move input plugin up 入力プラグインを上ã¸ç§»å‹• &Up 上ã¸(&U) Move input plugin down 入力プラグインを下ã¸ç§»å‹• &Down 下ã¸(&D) Output Plugins 出力プラグイン Output bus plugins 出力ãƒã‚¹ã®ãƒ—ラグイン Add output plugin 出力プラグインを追加 Remove output plugin 出力プラグインを削除 Move output plugin up 出力プラグインを下ã¸ç§»å‹• Move output plugin down 出力プラグインを下ã¸ç§»å‹• Move bus up towards the top ã“ã®ãƒã‚¹ã‚’上ã«ç§»å‹• U&p 上ã«ç§»å‹•(&P) Move bus down towards the bottom ã“ã®ãƒã‚¹ã‚’下ã«ç§»å‹• Do&wn 下ã«ç§»å‹•(&W) Create bus ãƒã‚¹ã‚’ä½œæˆ &Create 作æˆ(&C) Update bus ãƒã‚¹ã‚’アップデート &Update アップデート(&U) Delete bus ãƒã‚¹ã‚’削除 &Delete 削除(&D) Close this dialog ã“ã®ãƒ€ã‚¤ã‚¢ãƒ­ã‚°ã‚’é–‰ã˜ã¾ã™ Close é–‰ã˜ã‚‹ Warning 警告 Some settings have been changed. Do you want to apply the changes? 設定ã®ä¸€éƒ¨ãŒå¤‰æ›´ã•れã¾ã—㟠ã“ã®å¤‰æ›´ã‚’é©ç”¨ã—ã¾ã™ã‹? About to remove bus: "%1" (%2) Are you sure? ã“ã®ãƒã‚¹ã‚’削除ã—ã¾ã™: "%1" (%2) よã‚ã—ã„ã§ã™ã‹ï¼Ÿ Some settings have been changed. Do you want to discard the changes? 設定ã®ä¸€éƒ¨ãŒå¤‰æ›´ã•れã¾ã—㟠ã“ã®å¤‰æ›´ã‚’破棄ã—ã¾ã™ã‹ï¼Ÿ Move &Up 上ã«ç§»å‹•(&U) Move &Down 下ã«ç§»å‹•(&D) (No instrument) (インストルメントã¯ã‚りã¾ã›ã‚“) (none) (ãªã—) (1 item) (1アイテム) (%1 items) (%1アイテム) qtractorClientListView Readable Clients / Output Ports 出力å¯èƒ½ãªã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆ / 出力ãƒãƒ¼ãƒˆ Writable Clients / Input Ports 入力å¯èƒ½ãªã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆ / 入力ãƒãƒ¼ãƒˆ qtractorClipForm &Name: åå‰(&N): Clip name クリップã®åå‰ &File: ファイル(&F): Clip filename クリップã®ãƒ•ァイルå Browse for clip file クリップファイルã®é¸æŠž Track/&Channel: Clip track/channel クリップã®ãƒˆãƒ©ãƒƒã‚¯/ãƒãƒ£ãƒ³ãƒãƒ« Clip gain/volume クリップã®ã‚²ã‚¤ãƒ³/ボリューム Parameters パラメーター Clip start クリップã®é–‹å§‹ä½ç½® Clip offset クリップã®ã‚ªãƒ•セットä½ç½® &Panning: パニング(&P): Clip length クリップã®é•·ã• Offs&et: オフセット(&E): &Length: é•·ã•(&L): &Start: é–‹å§‹(&S): Clip クリップ Forma&t: フォーマット(&F): Time display format 時間表示フォーマット Frames フレーム Time 時間 BBT Fade In/Out フェードイン/アウト Fade &In: フェードイン(&I): Clip fade-in length クリップã®ãƒ•ェードインã®é•·ã• Clip fade-in type クリップã®ãƒ•ェードインã®ã‚¿ã‚¤ãƒ— Fade &Out: フェードアウト(&O): Clip fade-out length クリップã®ãƒ•ェードアウトã®é•·ã• Clip fade-out type クリップã®ãƒ•ェードアウトã®ã‚¿ã‚¤ãƒ— Audio 音声 Ti&me Stretch: タイムストレッãƒ(&M): Clip time-stretch percentage クリップã®ã‚¿ã‚¤ãƒ ã‚¹ãƒˆãƒ¬ãƒƒãƒã®ãƒ‘ーセンテージ % Pitch S&hift: ピッãƒã‚·ãƒ•ト(&H): Clip pitch-shift in semitones semitones Whether to use WSOLA time-stretching WSOLAタイムストレッãƒã‚’使ã†ã‹ã©ã†ã‹ &WSOLA time-stretching WSOLAタイムストレッãƒ(&W) Whether to use RubberBand formant preserve RubberBand &formant preserve Whether to apply WSOLA quick seek time-stretching WSOLAクイックシークタイムストレッãƒãƒ³ã‚°ã‚’é©ç”¨ã™ã‚‹ã‹ã©ã†ã‹ WSOLA quic&k seek WSOLAクイックシーク(&K) Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine &Mute ミュート(&M) &Gain: ゲイン(&G): Linear Quadratic 1 Quadratic 2 Quadratic 3 Cubic 1 Cubic 2 Cubic 3 dB dB &Volume: 音é‡(&V): new clip æ–°ã—ã„クリップ edit clip クリップを編集 Warning 警告 Some settings have been changed. Do you want to apply the changes? 設定ã®ä¸€éƒ¨ãŒå¤‰æ›´ã•れã¾ã—㟠ã“ã®å¤‰æ›´ã‚’é©ç”¨ã—ã¾ã™ã‹? MIDI MIDI MIDI files (*.%1 *.smf *.midi) MIDIファイル (*.%1 *.smf *.midi) All files (*.*) 全ファイル (*.*) %1 Clip File %1 クリップファイル qtractorConnect Connect 接続 Disconnect 切断 Disconnect All ã™ã¹ã¦åˆ‡æ–­ Refresh æ›´æ–° qtractorConnectForm Connections 接続 Audio 音声 Select output client/ports 出力クライアント/ãƒãƒ¼ãƒˆã®é¸æŠž Select input client/ports 入力クライアント/ãƒãƒ¼ãƒˆã®é¸æŠž Connect currently selected ports ç¾åœ¨é¸æŠžã•れã¦ã„ã‚‹ãƒãƒ¼ãƒˆã‚’接続 &Connect 接続(&C) Disconnect currently selected ports ç¾åœ¨é¸æŠžã•れã¦ã„ã‚‹ãƒãƒ¼ãƒˆã‚’切断 &Disconnect 切断(&D) Disconnect all currently connected ports ç¾åœ¨æŽ¥ç¶šã•れã¦ã„ã‚‹ã™ã¹ã¦ã®ãƒãƒ¼ãƒˆã‚’切断 Disconnect &All ã™ã¹ã¦åˆ‡æ–­(&A) Refresh current connections view ç¾åœ¨ã®æŽ¥ç¶šç”»é¢ã‚’æ›´æ–° &Refresh æ›´æ–°(&R) MIDI MIDI (All) (ã™ã¹ã¦) qtractorConnections Connections 接続 qtractorEditRangeForm Range レンジ Selection range é¸æŠžãƒ¬ãƒ³ã‚¸ &Selection é¸æŠž(&S) Loop range ループレンジ &Loop ループ(&L) Punch range パンãƒãƒ¬ãƒ³ã‚¸ &Punch パンãƒ(&P) Edit range 編集レンジ &Edit 編集(&E) Custom range ä»»æ„ã®ãƒ¬ãƒ³ã‚¸ &Custom カスタム(&C) St&art: é–‹å§‹(&A): Clip start クリップã®é–‹å§‹ä½ç½® En&d: 終了(&D): Clip offset クリップã®ã‚ªãƒ•セットä½ç½® Apply to Loop points in range レンジをループãƒã‚¤ãƒ³ãƒˆã«é©ç”¨ L&oop ループ(&O) Apply to Punch In/Out points in range レンジをパンãƒã‚¤ãƒ³/パンãƒã‚¢ã‚¦ãƒˆã«é©ç”¨ Pu&nch パンãƒ(&N) Mar&kers マーカー(&K) Te&mpo Map テンãƒãƒžãƒƒãƒ—(&M) &Format フォーマット(&F) Time display format 時間表示フォーマット Time 時間 BBT Frames フレーム Edit Range レンジã®ç·¨é›† Options オプション Apply to clips in range レンジをクリップã«é©ç”¨ Cl&ips クリップ(&I) A&utomation オートメーション(&U) Apply to Automation nodes in range レンジをオートメーションノードã«é©ç”¨ Apply to Tempo Map nodes in range レンジをテンãƒãƒžãƒƒãƒ—ã«é©ç”¨ Apply to location Markers in range レンジをロケーションマーカーã«é©ç”¨ qtractorExportClipForm %1 %2 Clips %1 %2 クリップ qtractorExportForm Export エクスãƒãƒ¼ãƒˆ &File: ファイル(&F): Export file name エクスãƒãƒ¼ãƒˆã™ã‚‹ãƒ•ァイルå Browse export file name エクスãƒãƒ¼ãƒˆã™ã‚‹ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠž File &type: ファイルタイプ(&T): Audio file type to use on export エクスãƒãƒ¼ãƒˆã§ä½¿ç”¨ã™ã‚‹éŸ³å£°ãƒ•ァイルタイプ Sample &format: サンプルフォーマット(&F): Audio sample format to use on export エクスãƒãƒ¼ãƒˆã§ä½¿ç”¨ã™ã‚‹éŸ³å£°ã‚µãƒ³ãƒ—ルフォーマット &Quality: 圧縮å“質(&Q): Audio compression quality to use on export エクスãƒãƒ¼ãƒˆã§ä½¿ç”¨ã™ã‚‹éŸ³å£°åœ§ç¸®å“質 File &format: ファイルフォーマット(&F): MIDI file format to use on export エクスãƒãƒ¼ãƒˆã§ä½¿ç”¨ã™ã‚‹MIDIファイルフォーマット Range レンジ Session range セッションレンジ &Session セッション(&S) Loop range ループレンジ &Loop ループ(&L) Punch range パンãƒãƒ¬ãƒ³ã‚¸ &Punch パンãƒ(&P) Edit range 編集レンジ &Edit 編集(&E) Custom range ä»»æ„ã®ãƒ¬ãƒ³ã‚¸ &Custom ä»»æ„(&C) St&art: é–‹å§‹(&A): Custom start ä»»æ„ã®é–‹å§‹ä½ç½® Custom end ä»»æ„ã®çµ‚了ä½ç½® En&d: 終了(&D): Outputs 出力 Output bus names 出力ãƒã‚¹å Format フォーマット Time display format 時間表示フォーマット Frames フレーム Time 時間 BBT Whether to add/import new track(s) with export result エキスãƒãƒ¼ãƒˆã®çµæžœã‚’æ–°ã—ã„トラックã¨ã—ã¦è¿½åŠ /インãƒãƒ¼ãƒˆã™ã‚‹ã‹ã©ã†ã‹ &Add new track(s) æ–°è¦ãƒˆãƒ©ãƒƒã‚¯ã¨ã—ã¦è¿½åŠ (&A) Audio 音声 MIDI MIDI Export %1 File %1 ファイルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ MIDI files (*.%1 *.smf *.midi) MIDIファイル (*.%1 *.smf *.midi) All files (*.*) 全ファイル (*.*) qtractorExportTrackForm %1 %2 Tracks %1 %2 トラック Warning 警告 The file already exists: "%1" Do you want to replace it? ファイルã¯ã™ã§ã«å­˜åœ¨ã—ã¦ã„ã¾ã™: "%1" ç½®ãã‹ãˆã¾ã™ã‹ï¼Ÿ Audio file export: "%1" started... 音声ファイルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ: "%1" é–‹å§‹... Audio file export: "%1" complete. 音声ファイルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ: "%1" 完了。 Audio file export: "%1" failed. 音声ファイルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ: "%1" 失敗ã—ã¾ã—ãŸã€‚ MIDI file export: "%1" started... MIDIファイルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ: "%1" é–‹å§‹... MIDI file export: "%1" complete. MIDIファイルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ: "%1" 完了。 MIDI file export: "%1" failed. MIDIファイルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ: "%1" 失敗ã—ã¾ã—ãŸã€‚ qtractorFileListView New Group æ–°ã—ã„グループ Warning 警告 About to remove %1 file item(s). Are you sure? %1ファイルアイテムを削除ã—ã¾ã™ã€‚ よã‚ã—ã„ã§ã™ã‹ï¼Ÿ About to remove %1 item: "%2" Are you sure? %1アイテムを削除ã—ã¾ã™: "%2" よã‚ã—ã„ã§ã™ã‹ï¼Ÿ group グループ file ファイル qtractorFileSystem &Home ホーム(&H) &Up 上(&U) Al&l Files 全ファイル(&L) &Session セッション(&S) &Audio 音声(&A) &MIDI MIDI(&M) H&idden éš ã™(&I) &Play å†ç”Ÿ(&P) File System ファイルシステム qtractorFiles Audio 音声 MIDI MIDI Play file ファイルをå†ç”Ÿ Add &Files... ファイルを追加(&F)... Cu&t 切りå–り(&T) &Copy コピー(&C) Ctrl+X Ctrl+C Ctrl+V New &Group... æ–°ã—ã„グループ(&G)... &Paste 貼り付ã‘(&P) Re&name åå‰ã®å¤‰æ›´(&N) &Remove 削除(&R) Pla&y å†ç”Ÿ(&Y) Cl&eanup クリーンアップ(&E) Del Files ファイル MIDI Files MIDIファイル Audio Files 音声ファイル qtractorInstrumentForm Instruments インストルメント Files ファイル Path パス Names åå‰ Import from instrument file インストルメントファイルã‹ã‚‰ã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ &Import... インãƒãƒ¼ãƒˆ(&I)... Remove instrument file インストルメントファイルã®å‰Šé™¤ &Remove 削除(&R) Move instrument file up on list order インストルメントファイルを上ã«ç§»å‹• &Up 上ã¸(&U) Move instrument file down on list order インストルメントファイルを下ã«ç§»å‹• &Down 下ã¸(&D) Export to instrument file インストルメントファイルã«ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ E&xport... エクスãƒãƒ¼ãƒˆ(&X)... Close this dialog ダイアログを閉ã˜ã¾ã™ Close é–‰ã˜ã‚‹ Import Instrument Files インストルメントファイルã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ Instrument files (*.%1 *.sf2 *.sf3 *.midnam) インストルメントファイル (*.%1 *.sf2 *.sf3 *.midnam) All files (*.*) 全ファイル (*.*) Export Instrument File インストルメントファイルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ Instrument files (*.%1) インストルメントファイル (*.%1) Warning 警告 The instrument file already exists: "%1" Do you want to replace it? インストルメントファイルã¯ã™ã§ã«å­˜åœ¨ã—ã¾ã™: "%1" ç½®ãã‹ãˆã¾ã™ã‹ï¼Ÿ Instrument settings have been changed. Do you want to apply the changes? インストルメントã®è¨­å®šãŒå¤‰æ›´ã•れã¾ã—ãŸã€‚ 変更をé©ç”¨ã—ã¾ã™ã‹ï¼Ÿ Patch Names for Banks ãƒãƒ³ã‚¯ã®ãƒ‘ッãƒå Controller Names = %1 コントローラーå = %1 RPN Names = %1 RPNå = %1 NRPN Names = %1 NRPNå = %1 Bank Select Method = %1 ãƒãƒ³ã‚¯ã‚»ãƒ¬ã‚¯ãƒˆæ–¹æ³• = %1 Patch Names パッãƒå Note Names ノートå Controller Names コントローラーå RPN Names RPNå NRPN Names NRPNå Bank Select Methods ãƒãƒ³ã‚¯ã‚»ãƒ¬ã‚¯ãƒˆæ–¹æ³• %1 = %2 Based On = %1 基準 = %1 Normal ノーマル Bank MSB ãƒãƒ³ã‚¯MSB Bank LSB ãƒãƒ³ã‚¯LSB Patch パッムUnknown 䏿˜Ž qtractorInstrumentMenu (None) (ãªã—) qtractorMainForm &File ファイル(&F) Open &Recent 最近ã®ãƒ•ァイルを開ã(&R) &Edit 編集(&E) Select &Mode é¸æŠžãƒ¢ãƒ¼ãƒ‰(&M) &Select é¸æŠž(&S) I&nsert 挿入(&N) Remo&ve 削除(&V) &Track トラック(&T) &State 状態(&S) &Navigate ナビゲート(&N) Mo&ve 移動(&V) &Height 高ã•(&H) Impor&t Tracks トラックã¸ã‚¤ãƒ³ãƒãƒ¼ãƒˆ(&T) E&xport Tracks トラックをエクスãƒãƒ¼ãƒˆ(&X) M&ode モード(&O) A&utomation オートメーション(&U) Instrum&ent インストルメント(&E) T&ools ツール(&O) Ta&ke テイク(&K) &View 表示(&V) &Toolbars ツールãƒãƒ¼(&T) &Windows ウィンドウ(&W) &Zoom ズーム(&Z) S&nap スナップ(&N) T&ransport トランスãƒãƒ¼ãƒˆ(&R) Mo&de St&ep &Help ヘルプ(&H) &New æ–°è¦(&N) New æ–°è¦ New session æ–°è¦ã‚»ãƒƒã‚·ãƒ§ãƒ³ New session file æ–°ã—ã„セッションファイルを作æˆã—ã¾ã™ Ctrl+N &Open... é–‹ã(&O)... Open é–‹ã Open session セッションを開ã Open session from file ファイルã‹ã‚‰ã‚»ãƒƒã‚·ãƒ§ãƒ³ã‚’é–‹ãã¾ã™ Ctrl+O &Save ä¿å­˜(&S) Save ä¿å­˜ Save session セッションをä¿å­˜ Save session to file セッションをファイルã«ä¿å­˜ã—ã¾ã™ Ctrl+S Save &As... åå‰ã‚’付ã‘ã¦ä¿å­˜(&A)... Save As åå‰ã‚’付ã‘ã¦ä¿å­˜ Save as åå‰ã‚’付ã‘ã¦ä¿å­˜ Save current session with another file name ç¾åœ¨ã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ã‚’別ãªãƒ•ァイルåã§ä¿å­˜ã—ã¾ã™ &Properties... プロパティ(&P)... Session Properties セッションã®ãƒ—ロパティ Session properties セッションã®ãƒ—ロパティ Edit current session properties ç¾åœ¨ã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ã®ãƒ—ロパティを編集ã—ã¾ã™ F2 E&xit 終了(&X) Exit 終了 Exit this application program ã“ã®ã‚½ãƒ•トウェアを終了ã—ã¾ã™ &Undo å…ƒã«æˆ»ã™(&U) Undo å…ƒã«æˆ»ã™ Undo last action 最後ã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’å…ƒã«æˆ»ã—ã¾ã™ Ctrl+Z &Redo やり直ã™(&R) Redo やり直㙠Redo last action 最後ã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’やり直ã—ã¾ã™ Ctrl+Shift+Z Cu&t 切りå–り(&T) Cut 切りå–り Cut selection to clipboard レンジをクリップボードã¸åˆ‡ã‚Šå–りã¾ã™ Ctrl+X &Copy コピー(&C) Copy コピー Copy selection to clipboard レンジをクリップボードã«ã‚³ãƒ”ーã—ã¾ã™ Ctrl+C &Paste 貼り付ã‘(&P) Paste 貼り付㑠Paste clipboard contents クリップボードã®å†…容を貼り付ã‘ã¾ã™ Ctrl+V Past&e Repeat... 繰り返ã—ã¦è²¼ã‚Šä»˜ã‘(&E)... Paste Repeat 繰り返ã—ã¦è²¼ã‚Šä»˜ã‘ Paste repeat 繰り返ã—ã¦è²¼ã‚Šä»˜ã‘ã‚‹ Paste/repeat clipboard contents クリップボードã®å†…容を繰り返ã—貼付ã‘ã¾ã™ Ctrl+Shift+V &Delete 削除(&D) Delete 削除 Delete selection レンジを削除ã—ã¾ã™ Del 削除 &Clip クリップ(&C) Clip クリップ Select clip ã‚¯ãƒªãƒƒãƒ—ã‚’é¸æŠž Clip selection mode クリップã§é¸æŠžã™ã‚‹ãƒ¢ãƒ¼ãƒ‰ã§ã™ &Range レンジ(&R) Range レンジ Select range ãƒ¬ãƒ³ã‚¸ã‚’é¸æŠž Range selection mode ãƒ¬ãƒ³ã‚¸ã‚’é¸æŠžã™ã‚‹ãƒ¢ãƒ¼ãƒ‰ã§ã™ R&ectangle 矩形(&E) Rect çŸ©å½¢é¸æŠž Select rectangle 矩形ã§é¸æŠžã™ã‚‹ãƒ¢ãƒ¼ãƒ‰ã§ã™ Rectangular selection mode 矩形ã§é¸æŠžã™ã‚‹ãƒ¢ãƒ¼ãƒ‰ã§ã™ &Automation オートメーション(&A) Automation オートメーション Automation edit mode オートメーションを編集ã™ã‚‹ãƒ¢ãƒ¼ãƒ‰ã§ã™ &All ã™ã¹ã¦(&A) Select All ã™ã¹ã¦ã‚’é¸æŠž Select all ã™ã¹ã¦é¸æŠž Mark all as selected ã™ã¹ã¦ã‚’é¸æŠžæ¸ˆã¿ã«ã—ã¾ã™ Ctrl+A &None ãªã—(&N) Select None é¸æŠžè§£é™¤ Select none é¸æŠžè§£é™¤ Mark all as unselected ã™ã¹ã¦ã®é¸æŠžã‚’解除ã—ã¾ã™ Ctrl+Shift+A &Invert å転(&I) Select Invert é¸æŠžã®å転 Select invert é¸æŠžã®å転 Invert selection é¸æŠžéƒ¨åˆ†ã‚’å転ã—ã¾ã™ Ctrl+I Select Track ãƒˆãƒ©ãƒƒã‚¯ã‚’é¸æŠž Select track ãƒˆãƒ©ãƒƒã‚¯ã‚’é¸æŠž Mark track as selected ãƒˆãƒ©ãƒƒã‚¯ã‚’é¸æŠžæ¸ˆã¿ã«ã—ã¾ã™ Ctrl+T Trac&k Range トラックレンジ(&K) Select Track Range トラックレンジã®é¸æŠž Select track range ãƒˆãƒ©ãƒƒã‚¯ãƒ¬ãƒ³ã‚¸ã‚’é¸æŠžã—ã¾ã™ Mark track range as selected ãƒˆãƒ©ãƒƒã‚¯ãƒ¬ãƒ³ã‚¸ã‚’é¸æŠžæ¸ˆã¿ã«ã—ã¾ã™ Ctrl+Shift+R Select Range é¸æŠžãƒ¬ãƒ³ã‚¸ Mark range as selected ãƒ¬ãƒ³ã‚¸ã‚’é¸æŠžæ¸ˆã¿ã«ã—ã¾ã™ Ctrl+R &Range... レンジ(&R)... Remove Range レンジã®å‰Šé™¤ Remove range レンジを削除ã—ã¾ã™ Remove range as selected é¸æŠžã•れã¦ã„るレンジã®å‰Šé™¤ Ctrl+Del Remove Track Range トラックレンジã®å‰Šé™¤ Remove track range トラックレンジを削除ã—ã¾ã™ Remove track range as selected é¸æŠžã•れã¦ã„るトラックレンジã®å‰Šé™¤ Ctrl+Shift+Del Insert Range ãƒ¬ãƒ³ã‚¸ã®æŒ¿å…¥ Insert range レンジを挿入 Insert range as selected é¸æŠžãƒ¬ãƒ³ã‚¸ã‚’æŒ¿å…¥ã—ã¾ã™ Ctrl+Ins Insert Track Range ãƒˆãƒ©ãƒƒã‚¯ãƒ¬ãƒ³ã‚¸ã®æŒ¿å…¥ Insert track range トラックレンジを挿入 Insert track range as selected é¸æŠžã—ãŸãƒˆãƒ©ãƒƒã‚¯ãƒ¬ãƒ³ã‚¸ã‚’挿入ã—ã¾ã™ Ctrl+Shift+Ins Sp&lit 分割(&L) Split Selection é¸æŠžã®åˆ†å‰² Split selection é¸æŠžã®åˆ†å‰² Split current selection ç¾åœ¨ã®é¸æŠžç¯„囲を分割ã—ã¾ã™ Ctrl+Y &Add Track... トラックを追加(&A)... Add Track トラックを追加 Add track トラックを追加 Add a new track to session ã‚»ãƒƒã‚·ãƒ§ãƒ³ã«æ–°è¦ãƒˆãƒ©ãƒƒã‚¯ã‚’追加ã—ã¾ã™ Shift+Ins &Remove Track トラックを削除(&R) Remove Track トラックを削除 Remove track トラックを削除 Remove current track from session セッションã‹ã‚‰ãƒˆãƒ©ãƒƒã‚¯ã‚’削除ã—ã¾ã™ Shift+Del &Duplicate Track トラックã®è¤‡è£½(&D) Duplicate Track トラックã®è¤‡è£½ Duplicate track トラックを複製 Duplicate current track ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’複製ã—ã¾ã™ Track &Properties... トラックã®ãƒ—ロパティ(&P)... Track Properties トラックã®ãƒ—ロパティ Track properties トラックã®ãƒ—ロパティ Edit current track properties é¸æŠžã—ã¦ã„るトラックã®ãƒ—ロパティを編集ã—ã¾ã™ Shift+F2 &Inputs 入力(&I) Track Inputs トラック入力 Track inputs トラック入力 Show current track input bus connections ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã¸ã®å…¥åŠ›ãƒã‚¹æŽ¥ç¶šã‚’表示ã—ã¾ã™ &Outputs 出力(&O) Track Outputs トラック出力 Track outputs トラック出力 Show current track output bus connections ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã‹ã‚‰ã®å‡ºåŠ›ãƒã‚¹æŽ¥ç¶šã‚’表示ã—ã¾ã™ &Record 録音(&R) Record Track トラックã®éŒ²éŸ³ Record track トラックã®éŒ²éŸ³ Arm current track for recording ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’録音状態ã«ã—ã¾ã™ &Mute ミュート(&M) Mute Track トラックã®ãƒŸãƒ¥ãƒ¼ãƒˆ Mute track トラックã®ãƒŸãƒ¥ãƒ¼ãƒˆ Mute current track ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’ミュートã«ã—ã¾ã™ &Solo ソロ(&S) Solo Track トラックã®ã‚½ãƒ­ Solo track トラックã®ã‚½ãƒ­ Solo current track ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’ソロã«ã—ã¾ã™ M&onitor モニター(&O) Monitor Track トラックã®ãƒ¢ãƒ‹ã‚¿ãƒ¼ Monitor track トラックã®ãƒ¢ãƒ‹ã‚¿ãƒ¼ Monitor current track ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’モニターã—ã¾ã™ &First 先頭ã¸(&F) First Track 先頭ã®ãƒˆãƒ©ãƒƒã‚¯ First track 先頭ã®ãƒˆãƒ©ãƒƒã‚¯ Make current the first track 先頭ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’é¸æŠžã—ã¾ã™ &Previous å‰ã¸(&P) Previous Track å‰ã®ãƒˆãƒ©ãƒƒã‚¯ Previous track å‰ã®ãƒˆãƒ©ãƒƒã‚¯ Make current the previous track å‰ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’é¸æŠžã—ã¾ã™ &Next 次ã¸(&N) Next Track 次ã®ãƒˆãƒ©ãƒƒã‚¯ Next track 次ã®ãƒˆãƒ©ãƒƒã‚¯ Make current the next track 次ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’é¸æŠžã—ã¾ã™ &Last 末尾ã¸(&L) Last Track 末尾ã®ãƒˆãƒ©ãƒƒã‚¯ Last track 末尾ã®ãƒˆãƒ©ãƒƒã‚¯ Make current the last track 末尾ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’é¸æŠžã—ã¾ã™ N&one é¸æŠžè§£é™¤(&O) None Track é¸æŠžè§£é™¤ None track é¸æŠžè§£é™¤ None current track トラックã®é¸æŠžã‚’解除ã—ã¾ã™ &Top 最上段ã«(&T) Move Top 最上段ã«ç§»å‹• Move top 最上段ã«ç§»å‹• Move current track to top ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’最上段ã«ç§»å‹•ã—ã¾ã™ &Up 上ã«(&U) Move Up 上ã«ç§»å‹• Move up 上ã«ç§»å‹• Move current track up ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’上ã«ç§»å‹•ã—ã¾ã™ &Down 下ã«(&D) Move Down 下ã«ç§»å‹• Move down 下ã«ç§»å‹• Move current track down ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’下ã«ç§»å‹•ã—ã¾ã™ &Bottom 最下段ã«(&B) Move Bottom 最下段ã«ç§»å‹• Move bottom 最下段ã«ç§»å‹• Move current track to bottom ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’最下段ã«ç§»å‹•ã—ã¾ã™ &Increase 広ã’ã‚‹(&I) Increase Height 高ã•を広ã’ã‚‹ Increase height 高ã•を広ã’ã‚‹ Increase track height トラックã®é«˜ã•を広ã’ã¾ã™ Ctrl+Shift++ &Decrease ç‹­ã‚ã‚‹(&D) Decrease Height 高ã•ã‚’ç‹­ã‚ã‚‹ Decrease height 高ã•ã‚’ç‹­ã‚ã‚‹ Decrease track height トラックã®é«˜ã•ã‚’ç‹­ã‚ã¾ã™ Ctrl+Shift+- &Minimize Minimize Height Minimize height Minimize track height &Reset リセット(&R) Height Reset 高ã•ã®ãƒªã‚»ãƒƒãƒˆ Height reset 高ã•ã®ãƒªã‚»ãƒƒãƒˆ Reset track height トラックã®é«˜ã•をリセットã—ã¾ã™ Ctrl+Shift+1 Auto &Monitor オートモニター(&M) Auto Monitor オートモニター Auto monitor オートモニター Auto-monitor current track ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã‚’オートモニタリングã—ã¾ã™ F6 Auto Dea&ctivate 自動無効化(&C) Auto Deactivate 自動ã§ç„¡åŠ¹åŒ–ã—ã¾ã™ Auto-deactivate plugins プラグインã®è‡ªå‹•無効化 Auto-deactivate plugins not producing sound 自動ã§ç„¡åŠ¹åŒ–ã•れãŸãƒ—ラグインã¯éŸ³å£°ã‚’発ã—ã¾ã›ã‚“ Shift+F6 &Audio... 音声(&A)... Inport Audio File 音声ファイルã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ Import Audio file 音声ファイルã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ Import tracks from Audio file 音声ファイルã‹ã‚‰ãƒˆãƒ©ãƒƒã‚¯ã‚’インãƒãƒ¼ãƒˆã—ã¾ã™ &MIDI... MIDI(&M)... Import MIDI File MIDIファイルをインãƒãƒ¼ãƒˆ Import MIDI file MIDIファイルをインãƒãƒ¼ãƒˆ Import tracks from MIDI file MIDIファイルã‹ã‚‰ãƒˆãƒ©ãƒƒã‚¯ã‚’インãƒãƒ¼ãƒˆã—ã¾ã™ Export Audio File 音声ファイルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ Export Audio file 音声ファイルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ Export tracks to Audio file 音声ファイルã¸ãƒˆãƒ©ãƒƒã‚¯ã‚’エクスãƒãƒ¼ãƒˆã—ã¾ã™ Export MIDI File MIDIファイルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ Export MIDI file MIDIファイルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ Export tracks to MIDI file トラックをMIDIファイルã«ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã—ã¾ã™ Log&arithmic 対数表示(&A) Automation logarithmic オートメーションã®å¯¾æ•°è¡¨ç¤º Automation curve logarithmic scale オートメーションカーブを対数表示ã—ã¾ã™ C&olor... é…色(&O)... Automation color オートメーションã®é…色 Automation curve color オートメーションカーブã®é…色を指定ã—ã¾ã™ &Lock ロック(&L) Automation lock オートメーションã®ãƒ­ãƒƒã‚¯ Lock automation curve オートメーションカーブをロックã—ã¾ã™ &Play å†ç”Ÿ(&P) Automation playback オートメーションã®å†ç”Ÿ Playback automation curve オートメーションカーブをå†ç”Ÿã—ã¾ã™ Automation record オートメーションを記録 Record automation curve オートメーションカーブを記録ã—ã¾ã™ &Clear クリアー(&C) Automation clear オートメーションã®ã‚¯ãƒªã‚¢ãƒ¼ Clear automation curve オートメーションカーブをクリアーã—ã¾ã™ Loc&k All ã™ã¹ã¦ãƒ­ãƒƒã‚¯(&K) Automation lock all 全オートメーションをロックã—ã¾ã™ Lock all automation curves ã™ã¹ã¦ã®ã‚ªãƒ¼ãƒˆãƒ¡ãƒ¼ã‚·ãƒ§ãƒ³ã‚«ãƒ¼ãƒ–をロックã—ã¾ã™ Play &All ã™ãºã¦å†ç”Ÿ(&A) Automation playback all 全オートメーションå†ç”Ÿ Playback all automation curves ã™ã¹ã¦ã®ã‚ªãƒ¼ãƒˆãƒ¡ãƒ¼ã‚·ãƒ§ãƒ³ã‚«ãƒ¼ãƒ–ã‚’å†ç”Ÿã—ã¾ã™ Rec&ord All ã™ã¹ã¦è¨˜éŒ²(&O) Automation record all 全オートメーションã®è¨˜éŒ² Record all automation curves ã™ã¹ã¦ã®ã‚ªãƒ¼ãƒˆãƒ¡ãƒ¼ã‚·ãƒ§ãƒ³ã‚«ãƒ¼ãƒ–を記録ã—ã¾ã™ C&lear All ã™ã¹ã¦ã‚¯ãƒªã‚¢ãƒ¼(&L) Automation clear all 全オートメーションクリアー Clear all automation curves ã™ã¹ã¦ã®ã‚ªãƒ¼ãƒˆãƒ¡ãƒ¼ã‚·ãƒ§ãƒ³ã‚«ãƒ¼ãƒ–をクリアーã—ã¾ã™ &New... æ–°è¦(&N)... New Clip æ–°è¦ã‚¯ãƒªãƒƒãƒ— New clip æ–°è¦ã‚¯ãƒªãƒƒãƒ— Create new clip æ–°è¦ã«ã‚¯ãƒªãƒƒãƒ—を作æˆã—ã¾ã™ &Edit... 編集(&E)... Edit Clip クリップを編集 Edit clip クリップを編集 Edit current clip é¸æŠžã—ã¦ã„るクリップを編集ã—ã¾ã™ F4 Mute Clip Mute clip Mute current clip &Unlink リンク解除(&U) Unlink Clip クリップã®ãƒªãƒ³ã‚¯ã‚’解除 Unlink clip クリップã®ãƒªãƒ³ã‚¯ã‚’解除 Unlink current clip ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—ã®ãƒªãƒ³ã‚¯ã‚’解除ã—ã¾ã™ Recor&d Record Clip クリップã®éŒ²éŸ³ Record clip クリップã®éŒ²éŸ³ Record current clip (overdub) ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—を録音ã—ã¾ã™(オーãƒãƒ¼ãƒ€ãƒ–) &Split 分割(&S) Split Clip クリップを分割 Split clip クリップを分割 Split current clip at playhead æ¼”å¥ä½ç½®ã‹ã‚‰ã‚¯ãƒªãƒƒãƒ—を分割ã—ã¾ã™ &Merge... çµåˆ(&M)... Merge Clips クリップをçµåˆ Merge clips クリップをçµåˆ Merge selected clips é¸æŠžã—ã¦ã„るクリップをçµåˆã—ã¾ã™ Normali&ze ノーマライズ(&Z) Normalize Clip クリップをノーマライズ Normalize clip クリップをノーマライズ Normalize current clip (gain/volume) クリップã®ã‚²ã‚¤ãƒ³/ボリュームをノーマライズã—ã¾ã™ &Quantize... クォンタイズ(&Q)... Quantize Clip クリップをクオンタイズ Quantize clip events クリップã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’クォンタイズ Quantize current MIDI clip events MIDIクリップã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’クォンタイズã—ã¾ã™ &Transpose... トランスãƒãƒ¼ã‚º(&T)... Transpose Clip クリップã®ãƒˆãƒ©ãƒ³ã‚¹ãƒãƒ¼ã‚º Transpose clip events クリップイベントã®ãƒˆãƒ©ãƒ³ã‚¹ãƒãƒ¼ã‚º Transpose current MIDI clip events ç¾åœ¨ã®MIDIクリップイベントをトランスãƒãƒ¼ã‚ºã—ã¾ã™ &Normalize... ノーマライズ(&N)... Normalize clip events クリップイベントã®ãƒŽãƒ¼ãƒžãƒ©ã‚¤ã‚º Normalize current MIDI clip events ç¾åœ¨ã®MIDIクリップイベントをノーマライズã—ã¾ã™ &Randomize... ランダマイズ(&R)... Randomize Clip クリップã®ãƒ©ãƒ³ãƒ€ãƒžã‚¤ã‚º Randomize clip events クリップイベントã®ãƒ©ãƒ³ãƒ€ãƒžã‚¤ã‚º Randomize current MIDI clip events ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—イベントをランダムã«ã—ã¾ã™ Resi&ze... リサイズ(&Z)... Resize Clip クリップã®ãƒªã‚µã‚¤ã‚º Resize clip events クリップイベントã®ãƒªã‚µã‚¤ã‚º Resize current MIDI clip events ç¾åœ¨ã®MIDIクリップイベントをリサイズã—ã¾ã™ Re&scale... リスケール(&S)... Rescale Clip クリップã®ãƒªã‚¹ã‚±ãƒ¼ãƒ« Rescale clip events クリップイベントã®ãƒªã‚¹ã‚±ãƒ¼ãƒ« Rescale current MIDI clip events ç¾åœ¨ã®MIDIクリップã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’リスケールã—ã¾ã™ T&imeshift... タイムシフト(&I)... Timeshift Clip クリップã®ã‚¿ã‚¤ãƒ ã‚·ãƒ•ト Timeshift clip events クリップイベントã®ã‚¿ã‚¤ãƒ ã‚·ãƒ•ト Timeshift current MIDI clip events ç¾åœ¨ã®MIDIクリップã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’タイムシフトã—ã¾ã™ T&empo ramp... Tempo ramp Clip Tempo ramp clip events Tempo ramp current MIDI clip events &Tempo Adjust... テンãƒèª¿æ•´(&T)... Tempo Adjust テンãƒã®èª¿æ•´ Adjust session tempo from current clip selection ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—é¸æŠžã‹ã‚‰ã‚»ãƒƒã‚·ãƒ§ãƒ³ã®ãƒ†ãƒ³ãƒã‚’調整ã—ã¾ã™ F7 &Cross Fade クロスフェード (&C) Clip Cross-fade クリップをクロスフェード Clip cross-fade クリップã®ã‚¯ãƒ­ã‚¹ãƒ•ェード Cross-fade current overlapped clips ç¾åœ¨é‡è¤‡ã—ã¦ã„るクリップをクロスフェードã—ã¾ã™ &Range Set レンジを設定(&R) Clip Range クリップレンジ Clip range クリップレンジ Set edit-range from current clip extents ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—ã®é•·ã•をレンジã«é¸æŠžã—ã¾ã™ &Loop Set ループを設定(&L) Clip Loop クリップループ Clip loop クリップループ Set loop-range from current clip extents ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—ã®é•·ã•ã ã‘ループレンジを設定ã—ã¾ã™ &Import... インãƒãƒ¼ãƒˆ(&I)... Import Clip クリップã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ Import clip クリップã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ Import clip from file(s) ファイルã‹ã‚‰ã‚¯ãƒªãƒƒãƒ—をインãƒãƒ¼ãƒˆã—ã¾ã™ E&xport... エクスãƒãƒ¼ãƒˆ(&X)... Export Clip クリップã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ Export clip クリップã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ Export current clip to file ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—をファイルã«ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã—ã¾ã™ First Take 最åˆã®ãƒ†ã‚¤ã‚¯ First take 最åˆã®ãƒ†ã‚¤ã‚¯ Select current clip first take ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—ã®æœ€åˆã®ãƒ†ã‚¤ã‚¯ã‚’é¸æŠžã—ã¾ã™ Previous Take å‰ã®ãƒ†ã‚¤ã‚¯ Previous take å‰ã®ãƒ†ã‚¤ã‚¯ Select current clip previous take ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—ã®å‰ã®ãƒ†ã‚¤ã‚¯ã‚’é¸æŠžã—ã¾ã™ Next Take 次ã®ãƒ†ã‚¤ã‚¯ Next take 次ã®ãƒ†ã‚¤ã‚¯ Select current clip next take ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—ã®æ¬¡ã®ãƒ†ã‚¤ã‚¯ã‚’é¸æŠžã—ã¾ã™ Shift+T Last Take 最後ã®ãƒ†ã‚¤ã‚¯ Last take 最後ã®ãƒ†ã‚¤ã‚¯ Select current clip last take ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—ã®æœ€å¾Œã®ãƒ†ã‚¤ã‚¯ã‚’é¸æŠžã—ã¾ã™ Reset Takes テイクã®ãƒªã‚»ãƒƒãƒˆ Reset takes テイクã®ãƒªã‚»ãƒƒãƒˆ Reset (unfold) current clip takes ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—ã®ãƒ†ã‚¤ã‚¯ã‚’リセットã—ã¾ã™ R&ange... レンジ(&R)... Take Range テイクã®ãƒ¬ãƒ³ã‚¸ Take range テイクレンジ Range (fold) current clip into takes ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—ã«ãƒ¬ãƒ³ã‚¸ã‚’設定ã—ã¾ã™ &Menubar メニューãƒãƒ¼(&M) Menubar メニューãƒãƒ¼ Show/hide the main program window menubar メインウィンドウã®ãƒ„ールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ Ctrl+M &Statusbar ステータスãƒãƒ¼(&S) Statusbar ステータスãƒãƒ¼ Show/hide the main program window statusbar メインウィンドウã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ File Toolbar ファイルツールãƒãƒ¼ File toolbar ファイルツールãƒãƒ¼ Show/hide main program window file toolbar メインウィンドウã®ãƒ•ァイルツールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ Edit Toolbar エディットツールãƒãƒ¼ Edit toolbar エディットツールãƒãƒ¼ Show/hide main program window edit toolbar エディットツールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ Track Toolbar トラックツールãƒãƒ¼ Track toolbar トラックツールãƒãƒ¼ Show/hide main program window track toolbar トラックツールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ View Toolbar ビューツールãƒãƒ¼ View toolbar ビューツールãƒãƒ¼ Show/hide main program window view toolbar ビューツールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ &Options オプション(&O) Options Toolbar オプションツールãƒãƒ¼ Options toolbar オプションツールãƒãƒ¼ Show/hide main program window options toolbar オプションツールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ Transport Toolbar トランスãƒãƒ¼ãƒˆãƒ„ールãƒãƒ¼ Transport toolbar トランスãƒãƒ¼ãƒˆãƒ„ールãƒãƒ¼ Show/hide main program window transport toolbar トランスãƒãƒ¼ãƒˆãƒ„ールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ T&ime 時間(&I) Time Toolbar 時間ツールãƒãƒ¼ Time toolbar 時間ツールãƒãƒ¼ Show/hide main program window time toolbar 時間表示ツールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ Thum&b 縮å°è¡¨ç¤º(&B) Thumb Toolbar 縮å°è¡¨ç¤ºãƒ„ールãƒãƒ¼ Thumb toolbar 縮å°è¡¨ç¤ºãƒ„ールãƒãƒ¼ Show/hide main program window thumb toolbar 縮å°è¡¨ç¤ºãƒ„ールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ File &System ファイルシステム(&S) File System ファイルシステム File system ファイルシステム Show/hide the file system window ファイルシステムウィンドウã®è¡¨ç¤º &Files ファイル(&F) Files ファイル Show/hide the files window ファイルウィンドウを表示/éš ã™ M&essages メッセージ(&E) Messages メッセージ Show/hide the messages window メッセージウィンドウを表示/éš ã™ &Connections 接続(&C) Connections 接続 Show/hide the connections window 接続ウィンドウを表示/éš ã™ F8 Mi&xer ミキサー(&X) Mixer ミキサー Show/hide the mixer window ミキサーウィンドウを表示/éš ã™ F9 &In イン(&I) Zoom In ズームイン Zoom in ズームイン Ctrl++ &Out アウト(&O) Zoom Out ズームアウト Zoom out ズームアウト Ctrl+- Zoom Reset ズームリセット Zoom reset ズームリセット Ctrl+1 &Horizontal 水平方å‘(&H) Horizontal Zoom 水平方å‘ズーム Horizontal zoom 水平方å‘ズーム Horizontal zoom mode 水平方å‘ã«ã‚ºãƒ¼ãƒ ã™ã‚‹ãƒ¢ãƒ¼ãƒ‰ã§ã™ &Vertical 垂直方å‘(&V) Vertical Zoom 垂直方å‘ズーム Vertical zoom 垂直方å‘ズーム Vertical zoom mode 垂直方å‘ã«ã‚ºãƒ¼ãƒ ã™ã‚‹ãƒ¢ãƒ¼ãƒ‰ã§ã™ All Zoom 垂直水平ズーム All zoom 垂直水平ズーム All zoom mode 垂直水平ã«ã‚ºãƒ¼ãƒ ã™ã‚‹ãƒ¢ãƒ¼ãƒ‰ã§ã™ &Grid グリッド(&G) Grid グリッド Snap grid view mode グリッドビューã«ã‚¹ãƒŠãƒƒãƒ—ã™ã‚‹ãƒ¢ãƒ¼ãƒ‰ã§ã™ &Zebra Zebra Bar zebra view mode Too&l Tips ツールãƒãƒƒãƒ—(&L) Tool tips ツールãƒãƒƒãƒ— Floating tool tips view mode ツールãƒãƒƒãƒ—をフローティング表示ã—ã¾ã™ &Refresh æ›´æ–°(&R) Refresh æ›´æ–° Refresh views ビューを更新 F5 &Instruments... インストルメント(&I)... Instruments インストルメント Change instrument definitions and files インストルメントã®è¨­å®šã¨ãƒ•ァイルã®å¤‰æ›´ã‚’ã—ã¾ã™ &Controllers... コントローラー(&C)... Controllers コントローラー Change MIDI controllers configuration MIDIコントローラー設定を変更ã—ã¾ã™ &Buses... ãƒã‚¹(&B)... Buses ãƒã‚¹ Change session bus definitions セッションãƒã‚¹å®šç¾©ã‚’変更ã—ã¾ã™ Tempo M&ap / Markers... テンãƒãƒžãƒƒãƒ—/マーカー(&A)... Tempo Map / Markers テンãƒãƒžãƒƒãƒ—/マーカー Tempo map / markers テンãƒãƒžãƒƒãƒ—/マーカー Change session tempo map / markers セッションã®ãƒ†ãƒ³ãƒãƒžãƒƒãƒ—/マーカーを変更ã—ã¾ã™ &Options... オプション(&O)... Options オプション Change general application program options ソフトウェア全般ã®ã‚ªãƒ—ションを変更ã—ã¾ã™ F12 &Backward 先頭ã¸(&B) Backward 先頭㸠Transport backward トランスãƒãƒ¼ãƒˆã‚’先頭ã¸ç§»å‹•ã—ã¾ã™ Backspace ãƒãƒƒã‚¯ã‚¹ãƒšãƒ¼ã‚¹ Re&wind 逆å†ç”Ÿ(&W) Rewind 逆å†ç”Ÿ Transport rewind トランスãƒãƒ¼ãƒˆã‚’逆å†ç”Ÿã—ã¾ã™ F&ast Forward æ—©é€ã‚Š(&A) Fast Forward æ—©é€ã‚Š Fast forward æ—©é€ã‚Š Transport fast forward トランスãƒãƒ¼ãƒˆã‚’æ—©é€ã‚Šã—ã¾ã™ &Forward 末尾ã¸(&F) Forward 末尾㸠Transport forward トランスãƒãƒ¼ãƒˆã‚’末尾ã¸ç§»å‹•ã—ã¾ã™ &Loop ループ(&L) Loop ループ Transport loop トランスãƒãƒ¼ãƒˆã‚’ループã—ã¾ã™ Ctrl+Shift+L Step Backward Step backward Transport step backward Step Forward Step forward Transport step forward Loop &Set ループセット(&S) Loop Set ループセット Loop set ループセット Transport loop set Ctrl+L &Stop åœæ­¢(&S) Stop åœæ­¢ Transport stop トランスãƒãƒ¼ãƒˆã‚’åœæ­¢ã—ã¾ã™ Play å†ç”Ÿ Transport play/pause トランスãƒãƒ¼ãƒˆã‚’å†ç”Ÿ/ä¸€æ™‚åœæ­¢ã—ã¾ã™ Space スペース Record 記録 Transport record トランスãƒãƒ¼ãƒˆã‚’録音ã—ã¾ã™ &Punch パンãƒ(&P) Punch パンムPunch in/out パンãƒã‚¤ãƒ³/アウト Transport punch in/out トランスãƒãƒ¼ãƒˆã«ãƒ‘ンãƒã‚¤ãƒ³/アウトを設定ã—ã¾ã™ Ctrl+Shift+P Punch Se&t パンãƒã‚»ãƒƒãƒˆ(&P) Punch Set パンãƒã‚»ãƒƒãƒˆ Punch in/out set パンãƒã‚¤ãƒ³/アウト設定 Transport punch in/out set トランスãƒãƒ¼ãƒˆã«ãƒ‘ンãƒã‚¤ãƒ³/アウトを設定ã—ã¾ã™ Ctrl+P &Count-in Count-in &Metronome メトロノーム(&M) Metronome メトロノーム F&ollow Playhead å†ç”Ÿã«è¿½éš(&O) Follow Playhead å†ç”Ÿã«è¿½éš Follow playhead å†ç”Ÿã‚’追ã„ã‹ã‘ã¾ã™ A&uto Backward 自動ã§å…ˆé ­ã«æˆ»ã‚‹(&U) Auto Backward 自動ã§å…ˆé ­ã«æˆ»ã™ Auto backward 自動ã§å…ˆé ­ã«æˆ»ã‚Šã¾ã™ &Continue Past End 終了ä½ç½®ã‚’無視(&C) Continue Past End 終了ä½ç½®ã‚’無視 Continue past end 終了ä½ç½®ã‚’無視ã—ã¾ã™ Transport mode: None トランスãƒãƒ¼ãƒˆãƒ¢ãƒ¼ãƒ‰: ãªã— Transport mode set to None トランスãƒãƒ¼ãƒˆãƒ¢ãƒ¼ãƒ‰ã‚’無効ã«ã—ã¾ã™ &Slave スレーブ(&S) Slave スレーブ Transport mode: Slave トランスãƒãƒ¼ãƒˆãƒ¢ãƒ¼ãƒ‰: スレーブ Transport mode set to Slave トランスãƒãƒ¼ãƒˆãƒ¢ãƒ¼ãƒ‰ã‚’スレーブã«ã—ã¾ã™ &Master マスター(&M) Master マスター Transport mode: Master トランスãƒãƒ¼ãƒˆãƒ¢ãƒ¼ãƒ‰: マスター Transport mode set to Master トランスãƒãƒ¼ãƒˆãƒ¢ãƒ¼ãƒ‰ã‚’マスターã«ã—ã¾ã™ &Full フル(&F) Full フル Transport mode: Full トランスãƒãƒ¼ãƒˆãƒ¢ãƒ¼ãƒ‰: フル Transport mode set to Full トランスãƒãƒ¼ãƒˆãƒ¢ãƒ¼ãƒ‰ã‚’フルã«ã—ã¾ã™ Pa&nic パニック(&N) Panic パニック All MIDI tracks shut off (panic) ã™ã¹ã¦ã®MIDIトラックをオフã«ã—ã¾ã™ (パニック) &Shortcuts... ショートカット(&S)... Shortcuts ショートカット Keyboard shortcuts キーボードショートカットを表示ã—ã¾ã™ &About... Qtractorã«ã¤ã„ã¦(&A)... About Qtractorã«ã¤ã„㦠Show information about this application program ã“ã®ã‚½ãƒ•トウェアã«é–¢ã™ã‚‹æƒ…報を表示ã—ã¾ã™ About &Qt... Qtã«ã¤ã„ã¦(&Q)... About Qt Qtã«ã¤ã„㦠Show information about the Qt toolkit Qtツールキットã«é–¢ã™ã‚‹æƒ…報を表示ã—ã¾ã™ Current tempo (BPM) ç¾åœ¨ã®ãƒ†ãƒ³ãƒ (BPM) Snap/beat スナップ/beat Track トラック Current track name ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯å MOD 変更 Session modification state セッションã®å¤‰æ›´çŠ¶æ…‹ REC 記録 Session record state セッションã®éŒ²éŸ³çŠ¶æ…‹ MUTE ミュート Session muting state セッションã®ãƒŸãƒ¥ãƒ¼ãƒˆçŠ¶æ…‹ SOLO ソロ Session soloing state セッションã®ã‚½ãƒ­çŠ¶æ…‹ LOOP ループ Session looping state セッションã®ãƒ«ãƒ¼ãƒ—状態 Session total time セッションã®åˆè¨ˆæ™‚é–“ Session sample rate セッションã®ã‚µãƒ³ãƒ—リング周波数 Could not set default session directory: %1 Sorry. デフォルトã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’設定ã§ãã¾ã›ã‚“ã§ã—㟠%1 ã”ã‚ã‚“ãªã•ã„。 Ready 準備ãŒã§ãã¾ã—㟠Session XRUN state セッションã®XRUN状態 Session buffer size Untitled%1 タイトルãªã—%1 New session: "%1". æ–°ã—ã„セッション: "%1"。 Session files (*.%1 *.%2 *.%3) セッションファイル(*.%1 *.%2 *.%3) Session files (*.%1 *.%2) セッションファイル(*.%1 *.%2) Template files (*.%1) テンプレートファイル(*.%1) Archive files (*.%1) アーカイブファイル(*.%1) All files (*.*) 全ファイル (*.*) Open Session セッションを開ã Save Session セッションをä¿å­˜ Warning 警告 The file already exists: "%1" Do you want to replace it? ファイルã¯ã™ã§ã«å­˜åœ¨ã—ã¦ã„ã¾ã™: "%1" ç½®ãã‹ãˆã¾ã™ã‹ï¼Ÿ Backup session: "%1" as "%2". セッションã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—: "%1" ã‚’ "%2"ã«ã€‚ Could not backup existing session: %1 as %2 Sorry. 既存ã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ã‚’ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã§ãã¾ã›ã‚“ã§ã—ãŸ: %1 ã‚’ %2 ã« ã”ã‚ã‚“ãªã•ã„。 The current session has been changed: "%1" Do you want to save the changes? ç¾åœ¨ã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ã¯å¤‰æ›´ã•れã¦ã„ã¾ã™: "%1" 変更をä¿å­˜ã—ã¾ã™ã‹ï¼Ÿ About to remove archive directory: "%1" Are you sure? アーカイブディレクトリを削除ã—ã¾ã™: "%1" よã‚ã—ã„ã§ã™ã‹ï¼Ÿ Session closed. セッションを閉ã˜ã¾ã—ãŸã€‚ The directory already exists: "%1" Do you want to replace it? ã“ã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã¯ã™ã§ã«å­˜åœ¨ã—ã¦ã„ã¾ã™: "%1" ç½®ãã‹ãˆã¾ã™ã‹ï¼Ÿ Opening "%1"... "%1"ã‚’é–‹ã„ã¦ã„ã¾ã™... Session could not be loaded from "%1". Sorry. セッションã¯"%1"ã‹ã‚‰ ロードã•れã¾ã›ã‚“ã§ã—ãŸã€‚ ã”ã‚ã‚“ãªã•ã„。 Open session: "%1". セッションを開ã: "%1"。 A directory with same name already exists: "%1" This directory will be replaced, erasing all its current data, when opening and extracting this archive in the future. Do you want to continue? The directory is an extracted archive: "%1" This directory will be removed, erased from all its current data, when closing this session. Do you want to continue? Saving "%1"... "%1"ã‚’ä¿å­˜ã—ã¦ã„ã¾ã™... Session could not be saved to "%1". Sorry. セッションã¯%1ã« ä¿å­˜ã•れã¾ã›ã‚“ã§ã—ãŸã€‚ ã”ã‚ã‚“ãªã•ã„。 Save session: "%1". セッションã®ä¿å­˜: "%1"。 Oops! Looks like it crashed or did not close properly last time it was run... however, an auto-saved session file exists: "%1" Do you want to crash-recover from it? ã‚らら å‰å›žèµ·å‹•時ã«ã€ã‚¯ãƒ©ãƒƒã‚·ãƒ¥ã—ãŸã‹é©åˆ‡ã«çµ‚了ã—ãªã‹ã£ãŸã‚ˆã†ã§ã™ã€‚自動ä¿å­˜ã•れãŸã‚»ãƒƒã‚·ãƒ§ãƒ³ãƒ•ァイルãŒå­˜åœ¨ã—ã¦ã„ã¾ã™: "%1" ã“ã®ãƒ•ァイã‹ã‚‰å¾©å…ƒã—ã¾ã™ã‹ï¼Ÿ About to clear automation: "%1" Are you sure? オートメーションをクリアーã—ã¾ã™: "%1" よã‚ã—ã„ã§ã™ã‹ï¼Ÿ About to clear all automation: "%1" Are you sure? ã™ã¹ã¦ã®ã‚ªãƒ¼ãƒˆãƒ¡ãƒ¼ã‚·ãƒ§ãƒ³ã‚’クリアーã—ã¾ã™: "%1" よã‚ã—ã„ã§ã™ã‹ï¼Ÿ take range テイクレンジ session セッション or ã‚ã‚‹ã„㯠program プログラム Information インフォメーション Some settings may be only effective next time you start this %1. 設定ã®ã†ã¡ã„ãã¤ã‹ã¯ã€æ¬¡å›žä»¥é™ã« ã“ã®%1ã‚’é–‹å§‹ã—ãŸæ™‚ã«æœ‰åйã¨ãªã‚Šã¾ã™ã€‚ Player panic! プレイヤーã®ãƒ‘ニック! Debugging option enabled. デãƒãƒƒã‚®ãƒ³ã‚°ã‚ªãƒ—ã‚·ãƒ§ãƒ³ã¯æœ‰åйã§ã™ã€‚ Ogg Vorbis (libvorbis) file support disabled. Ogg Vorbis (libvorbis) ファイルサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ MPEG-1 Audio Layer 3 (libmad) file support disabled. MPEG-1 Audio Layer 3 (libmad) ファイルサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ Sample-rate conversion (libsamplerate) disabled. ã‚µãƒ³ãƒ—ãƒªãƒ³ã‚°å‘¨æ³¢æ•°å¤‰æ› (libsamplerate) ã¯ç„¡åйã§ã™ã€‚ Pitch-shifting support (librubberband) disabled. ピッãƒã‚·ãƒ•トサãƒãƒ¼ãƒˆ (librubberband) ã¯ç„¡åйã§ã™ã€‚ Beat-detection support (libaubio) disabled. ビート検出 (libaudio) ã¯ç„¡åйã§ã™ã€‚ OSC service support (liblo) disabled. OSCサービスサãƒãƒ¼ãƒˆ (liblo) ã¯ç„¡åйã§ã™ã€‚ LADSPA Plug-in support disabled. LADSPAプラグインサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ DSSI Plug-in support disabled. DSSIプラグインサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ CLAP Plug-in support disabled. LV2 Plug-in support disabled. LV2プラグインサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in UI support disabled. LV2プラグインUIサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in UI support (libsuil) disabled. LV2プラグインUIサãƒãƒ¼ãƒˆ (libsuil) ã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in MIDI/Event support (DEPRECATED) enabled. LV2プラグインã®MIDI/Eventサãƒãƒ¼ãƒˆï¼ˆéžæŽ¨å¥¨ï¼‰ãŒæœ‰åйã§ã™ã€‚ LV2 plug-in State Make Path support (DANGEROUS) enabled. LV2プラグインã®çŠ¶æ…‹ä½œæˆãƒ‘ス(å±é™ºã§ã™)ãŒæœ‰åйã§ã™ã€‚ LV2 Plug-in State Files support disabled. LV2プラグインã®çŠ¶æ…‹ãƒ•ã‚¡ã‚¤ãƒ«ã‚µãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in MIDNAM support disabled. LV2プラグインã®MIDNAMサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in Patch support disabled. LV2プラグインã®ãƒ‘ッãƒã‚µãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in UI Touch interface support disabled. LV2プラグインã®UIタッãƒã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェイスサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in UI Idle interface support disabled. LV2プラグインUIã®ã‚¢ã‚¤ãƒ‰ãƒ«ã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェイスサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in UI Show interface support disabled. LV2プラグインUIã®è¡¨ç¤ºã‚¤ãƒ³ã‚¿ãƒ¼ãƒ•ェイスã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in UI GTKMM2 native support disabled. JACK Metadata support disabled. JACK Metadata サãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ Using: Qt %1 Qt %1 を使用 XRUN The following issues were detected: %1 Saving into another session file is highly recommended. 以下ã®å•é¡ŒãŒæ¤œå‡ºã•れã¾ã—ãŸ: %1 別ãªã‚»ãƒƒã‚·ãƒ§ãƒ³ãƒ•ァイルã«ä¿å­˜ã—ã¦ãã ã•ã„。 Don't show this again The audio engine buffer size has changed, increased from %1 to %2 frames/period. Reloading the current session file is highly recommended. 音声エンジンã®ãƒãƒƒãƒ•ァーサイズãŒå¤‰æ›´ã•れ〠%1 ã‹ã‚‰ %2 frame/periodã¨ãªã‚Šã¾ã—ãŸã€‚ ç¾åœ¨ã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ãƒ•ァイルをå†ãƒ­ãƒ¼ãƒ‰ ã—ã¦ãã ã•ã„。 TRACK MONITOR %1 %2 トラックモニター%1 %2 Set current snap to %1 %1 ã«ã‚¹ãƒŠãƒƒãƒ—ã—ã¾ã™ Current time (play-head) ç¾åœ¨ã®æ™‚é–“ (å†ç”Ÿã®å…ˆé ­) VST2 Plug-in support disabled. VST3 Plug-in support disabled. LV2 Plug-in support (liblilv) disabled. LV2プラグインサãƒãƒ¼ãƒˆ (liblilv) ã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in External UI support disabled. LV2プラグインã®å¤–部ユーザーインターフェイスサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in MIDI/Atom support disabled. LV2プラグインã®MIDI/Atomサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in Worker/Schedule support disabled. LV2プラグインã®ãƒ¯ãƒ¼ã‚«ãƒ¼/スケジューラーサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in State support disabled. LV2プラグインã®ã‚¹ãƒ†ãƒ¼ãƒˆã‚µãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in Programs support disabled. LV2プラグインã®ãƒ—ログラムサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in Presets support disabled. LV2プラグインã®ãƒ—リセットサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in Time/position support disabled. LV2プラグインã®ã‚¿ã‚¤ãƒ /ãƒã‚¸ã‚·ãƒ§ãƒ³ã‚µãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in Options support disabled. LV2プラグインオプションサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in Buf-size support disabled. LV2 Plug-in UI Request-value support disabled. LV2プラグインUI Request valueサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ JACK Session support disabled. JACKセッションサãƒãƒ¼ãƒˆã¯ç„¡åй. JACK Latency support disabled. JACKレイテンシサãƒãƒ¼ãƒˆç„¡åй. Version ãƒãƒ¼ã‚¸ãƒ§ãƒ³ Website ウェブサイト This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 or later. record clip クリップã®éŒ²éŸ³ [modified] [変更ã•れã¦ã„ã¾ã™] Session started. セッションã¯é–‹å§‹ã—ã¾ã—ãŸã€‚ The audio/MIDI engine could not be started. Make sure the JACK/Pipewire audio service and the ALSA Sequencer kernel module (snd-seq-midi) are up and running and then restart the session. The original session sample rate (%1 Hz) is not the same as the current audio engine (%2 Hz). Saving and reloading from a new session file is highly recommended. セッションã®ã‚ªãƒªã‚¸ãƒŠãƒ«ã‚µãƒ³ãƒ—リング周波数 (%1 Hz) 㯠ç¾åœ¨ã®éŸ³å£°ã‚¨ãƒ³ã‚¸ãƒ³ã®ã‚‚ã® (%2 Hz) ã¨ç•°ãªã‚Šã¾ã™ã€‚ セッションをä¿å­˜ã—ã€å†åº¦èµ·å‹•ã™ã‚‹ã“ã¨ã‚’ãŠã™ã™ã‚ã—ã¾ã™ã€‚ Don't ask this again å†ç¢ºèªã—ãªã„ LV2 Plug-in UI GTK2 native support disabled. LV2プラグインUIã®Gtk2ãƒã‚¤ãƒ†ã‚£ãƒ–サãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ LV2 Plug-in UI X11 native support disabled. LV2プラグインUIã®X11ãƒã‚¤ãƒ†ã‚£ãƒ–サãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ NSM support disabled. NSMサãƒãƒ¼ãƒˆã¯ç„¡åйã§ã™ã€‚ &Hold ホールド(&H) &Linear リニア(&L) &Spline スプライン(&S) Take %1 テイク %1 None ãªã— Error エラー XRUN(%1 skipped) XRUN(%1スキップ) XRUN(%1): some frames might have been lost. XRUN(%1): ã„ãã¤ã‹ã®ãƒ•レームãŒå¤±ã‚れã¾ã—ãŸã€‚ Audio connections change. 音声接続ãŒå¤‰æ›´ã•れã¾ã—ãŸã€‚ Audio self-connection detected! In general, connecting an output bus (or insert send), directly into any input bus (or insert return), is not advisable. It often doesn't work, if at all. MIDI connections change. MIDI接続ãŒå¤‰æ›´ã•れã¾ã—ãŸã€‚ Playing ended. å†ç”ŸãŒçµ‚了ã—ã¾ã—ãŸã€‚ The audio engine has been shutdown. Make sure the JACK audio server (jackd) is up and running and then restart session. 音声エンジンã¯çµ‚了ã—ã¾ã—ãŸã€‚ JACK音声サーãƒãƒ¼(jackd)ãŒèµ·å‹•ã—実行ã•れã¦ã„ã‚‹ãªã‚‰ セッションをå†å§‹å‹•ã—ã¦ãã ã•ã„。 STOP åœæ­¢ PLAY å†ç”Ÿ FFWD 進む REW 戻る REC ON 録音オン REC OFF 録音オフ RESET リセット LOCATE %1 SHUTTLE %1 STEP %1 ステップ %1 TRACK RECORD %1 %2 TRACK MUTE %1 %2 トラック ミュート %1 %2 TRACK SOLO %1 %2 トラック ソロ %1 %2 Unknown sub-command 䏿˜Žãªã‚µãƒ–コマンドã§ã™ Not implemented 実装ã•れã¦ã„ã¾ã›ã‚“ MIDI CTL: %1, Channel %2, Param %3, Value %4 MIDI コントロール: %1, ãƒãƒ£ãƒ³ãƒãƒ« %2, パラメーター %3, 値 %4 (track %1, gain %2) (トラック %1, ゲイン %2) (track %1, panning %2) (トラック %1, パン %2) START é–‹å§‹ CONTINUE 継続 SONGPOS %1 ソングãƒã‚¸ã‚·ãƒ§ãƒ³ %1 %1 BPM Playing "%1"... "%1"ã‚’å†ç”Ÿ... qtractorMessages Messages メッセージ Logging stopped --- %1 --- ロギングã®åœæ­¢ --- %1 --- Logging started --- %1 --- ロギングã®é–‹å§‹ --- %1 --- qtractorMidiControl Note On ノートオン Note Off ノートオフ Key Press キープレス Controller コントローラー Pgm Change Pgmãƒã‚§ãƒ³ã‚¸ Chan Press ãƒãƒ£ãƒ³ãƒãƒ«ãƒ—レス Pitch Bend ピッãƒãƒ™ãƒ³ãƒ‰ RPN NRPN Control 14 コントロール14 Track Gain トラックゲイン Track Panning トラックパニング Track Monitor トラックモニター Track Record トラックã®éŒ²éŸ³ Track Mute トラックã®ãƒŸãƒ¥ãƒ¼ãƒˆ Track Solo トラックã®ã‚½ãƒ­ qtractorMidiControlForm Controllers コントローラー Controller files コントローラーファイル Files ファイル Path パス Import controller files コントローラーファイルã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ &Import... インãƒãƒ¼ãƒˆ(&I)... Remove controller file コントローラーファイルã®å‰Šé™¤ &Remove 削除(&R) Move controller file up on list order コントローラーファイルを上ã«ç§»å‹• &Up 上ã¸(&U) Move controller file down on list order コントローラーファイルを下ã«ç§»å‹• &Down 下ã¸(&D) &Type タイプ(&T) &Channel ãƒãƒ£ãƒ³ãƒãƒ«(&C) &Parameter パラメーター(&P) Trac&k トラック(&K) offse&t オフセット(&T) &limit リミット(&L) C&ommand コマンド(&O) Flags フラグ MIDI Event type MIDIイベントタイプ MIDI Channel MIDIãƒãƒ£ãƒ³ãƒãƒ« MIDI Controller (parameter) MIDIコントローラー(パラメーター) MIDI parameter (track offset) MIDIパラメーター(トラックオフセット) + Track offset トラックã®ã‚ªãƒ•セット Track limit トラックã®ãƒªãƒŸãƒƒãƒˆ Command action コマンドã®ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ Command delta/momentary コマンドã®ãƒ‡ãƒ«ã‚¿/モーメンタリー D&elta デルタ(&E) Command feedback コマンドã®ãƒ•ィードãƒãƒƒã‚¯ &Feedback フィードãƒãƒƒã‚¯(&F) Map/update controller command コントローラーコマンドをマップ/æ›´æ–° &Map マップ(&M) Controller map コントローラーマップ Type タイプ Channel ãƒãƒ£ãƒ³ãƒãƒ« Parameter パラメータ Track トラック Command コマンド Feedback フィードãƒãƒƒã‚¯ Unmap/remove controller command コントローラーコマンドをアンマップ/削除ã—ã¾ã™ U&nmap アンマップ(&N) Enable all controllers immediate sync (hook) 全コントローラーã®å³æ™‚åŒæœŸã‚’有効ã«ã™ã‚‹ (フック) &Sync åŒæœŸ (&S) Reload/apply all controller files ã™ã¹ã¦ã®ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ©ãƒ¼ãƒ•ァイルをリロード/é©ç”¨ã—ã¾ã™ Relo&ad リロード(&A) Export to controller file コントローラーファイルã«ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã—ã¾ã™ E&xport... エクスãƒãƒ¼ãƒˆ(&X)... Close this dialog ダイアログを閉ã˜ã¾ã™ Close é–‰ã˜ã‚‹ Import Controller Files コントローラーファイルをインãƒãƒ¼ãƒˆã—ã¾ã™ Controller files (*.%1) コントローラーファイル(*.%1) All files (*.*) 全ファイル (*.*) Warning 警告 About to remove controller file: "%1" Are you sure? コントローラーファイルを削除ã—ã¾ã™: %1 よã‚ã—ã„ã§ã™ã‹ï¼Ÿ Export Controller File コントローラーファイルをエクスãƒãƒ¼ãƒˆã—ã¾ã™ controller コントローラー The controller file already exists: "%1" Do you want to replace it? コントローラーファイルã¯ã™ã§ã«å­˜åœ¨ã—ã¦ã„ã¾ã™: %1 ç½®ãã‹ãˆã¾ã™ã‹ï¼Ÿ Saved controller mappings may not be effective the next time you start this program. "%1" Do you want to apply to controller files? ä¿å­˜ã•れã¦ã„ã‚‹ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ©ãƒ¼ãƒžãƒƒãƒ”ãƒ³ã‚°ã¯æ¬¡å›žèµ·å‹•時㫠効果ãŒãªã„ã§ã—ょã†ã€‚ %1 コントローラーファイルをé©ç”¨ã—ã¾ã™ã‹ï¼Ÿ Controller mappings have been changed. コントローラーマッピングã¯å¤‰æ›´ã•れã¾ã—ãŸã€‚ Do you want to save the changes? 変更をä¿å­˜ã—ã¾ã™ã‹ï¼Ÿ Delta デルタ qtractorMidiControlObserverForm &Type: タイプ(&T): MIDI event type MIDIイベントタイプ Cha&nnel: ãƒãƒ£ãƒ³ãƒãƒ«(&N): MIDI channel MIDIãƒãƒ£ãƒ³ãƒãƒ« &Parameter: パラメーター(&P): MIDI parameter MIDIパラメーター &Logarithmic 対数表記(&L) &Feedback フィードãƒãƒƒã‚¯(&F) In&vert å転(&I) &Hook フック(&H) L&atch ラッãƒ(&L) Control input connections ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«å…¥åŠ›ã®æŽ¥ç¶š &Inputs 入力(&I) Control output connections ã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«å‡ºåŠ›ã®æŽ¥ç¶š &Outputs 出力(&O) MIDI Controller MIDIコントローラー MIDI controller is already assigned. Do you want to replace the mapping? MIDIコントローラーã¯ã™ã§ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„ã¾ã™ã€‚ マッピングを置ãã‹ãˆã¾ã™ã‹ï¼Ÿ Some settings have been changed. Do you want to apply the changes? 設定ã®ä¸€éƒ¨ãŒå¤‰æ›´ã•れã¾ã—㟠ã“ã®å¤‰æ›´ã‚’é©ç”¨ã—ã¾ã™ã‹? &MIDI Controller... MIDIコントローラー(&M)... &Automation オートメーション(&A) &Lock ロック(&L) &Play å†ç”Ÿ(&P) &Record 録音(&R) &Clear クリアー(&C) qtractorMidiControlPluginWidget &Type: タイプ(&T): MIDI event type MIDIイベントタイプ Cha&nnel: ãƒãƒ£ãƒ³ãƒãƒ«(&N): MIDI channel MIDIãƒãƒ£ãƒ³ãƒãƒ« &Parameter: パラメーター(&P): MIDI parameter MIDIパラメーター &Logarithmic 対数表記(&L) In&vert å転(&I) &Bipolar qtractorMidiEditEvent Zoom in (horizontal) ズームイン(水平方å‘) Zoom out (horizontal) ズームアウト(水平方å‘) Zoom reset (horizontal) ズームリセット(水平方å‘) qtractorMidiEditList C%1 qtractorMidiEditTime Play-head Edit-head Edit-tail Loop-start ループã®å…ˆé ­ Loop-end ãƒ«ãƒ¼ãƒ—ã®æœ«å°¾ Punch-in パンãƒã‚¤ãƒ³ Punch-out パンãƒã‚¢ã‚¦ãƒˆ Start: %1 End: %2 Length: %3 é–‹å§‹: %1 終了: %2 é•·ã•: %3 qtractorMidiEditView Zoom in (vertical) ズームイン(垂直方å‘) Zoom out (vertical) ズームアウト(垂直方å‘) Zoom reset (vertical) ズームリセット(垂直方å‘) qtractorMidiEditor C C#/Db D D#/Eb E F F#/Gb G G#/Ab A A#/Bb B Acoustic Bass Drum Bass Drum 1 Side Stick サイドスティック Acoustic Snare Hand Clap Electric Snare Low Floor Tom Closed Hi-Hat High Floor Tom Pedal Hi-Hat Low Tom Open Hi-Hat Low-Mid Tom Hi-Mid Tom Crash Cymbal 1 High Tom Ride Cymbal 1 Chinese Cymbal Ride Bell Tambourine Splash Cymbal Cowbell Crash Cymbal 2 Vibraslap Ride Cymbal 2 Hi Bongo Low Bongo Mute Hi Conga Open Hi Conga Low Conga High Timbale Low Timbale High Agogo Low Agogo Cabasa Maracas Short Whistle Long Whistle Short Guiro Long Guiro Claves Hi Wood Block Low Wood Block Mute Cuica Open Cuica Mute Triangle Open Triangle Bank Select (coarse) Modulation Wheel (coarse) Breath Controller (coarse) Foot Pedal (coarse) Portamento Time (coarse) Data Entry (coarse) Volume (coarse) Balance (coarse) Pan Position (coarse) Expression (coarse) Effect Control 1 (coarse) Effect Control 2 (coarse) General Purpose Slider 1 General Purpose Slider 2 General Purpose Slider 3 General Purpose Slider 4 Bank Select (fine) Modulation Wheel (fine) Breath Controller (fine) Foot Pedal (fine) Portamento Time (fine) Data Entry (fine) Volume (fine) Balance (fine) Pan Position (fine) Expression (fine) Effect Control 1 (fine) Effect Control 2 (fine) Hold Pedal (on/off) Portamento (on/off) Soft Pedal (on/off) Legato Pedal (on/off) Hold 2 Pedal (on/off) Sound Variation General Purpose Button 1 (on/off) General Purpose Button 2 (on/off) General Purpose Button 3 (on/off) General Purpose Button 4 (on/off) Effects Level Chorus Level Celeste Level Phaser Level Data Button Increment Data Button Decrement Non-Registered Parameter (fine) Non-Registered Parameter (coarse) Registered Parameter (fine) Registered Parameter (coarse) All Sound Off All Controllers Off Local Keyboard (on/off) All Notes Off Omni Mode Off Omni Mode On Mono Operation Poly Operation Pitch Bend Sensitivity Fine Tune Coarse Tune Tuning Program Tuning Bank Vibrato Rate Vibrato Depth Vibrato Delay Filter Cutoff Filter Resonance Sostenuto Pedal (on/off) Release Time Attack Time Brightness Decay Time Tremolo Level EG Attack EG Decay EG Release Drum Filter Cutoff Drum Filter Resonance Drum EG Attack Drum EG Decay Drum Pitch Coarse Drum Pitch Fine Drum Level Drum Pan Drum Reverb Send Drum Chorus Send Drum Variation Send Modulation Wheel (14bit) Breath Controller (14bit) Foot Pedal (14bit) Portamento Time (14bit) Volume (14bit) Balance (14bit) Pan Position (14bit) Expression (14bit) Effect Control 1 (14bit) Effect Control 2 (14bit) General Purpose Slider 1 (14bit) General Purpose Slider 2 (14bit) General Purpose Slider 3 (14bit) General Purpose Slider 4 (14bit) Chromatic Major Minor Melodic Minor (Asc) Melodic Minor (Desc) Whole Tone Pentatonic Major Pentatonic Minor Pentatonic Blues Pentatonic Neutral Octatonic (H-W) Octatonic (W-H) Ionian Dorian Phrygian Lydian Mixolydian Aeolian Locrian Egyptian Eight Tone Spanish Hawaiian Hindu Hirajoshi Hungarian Major Hungarian Minor Hungarian Gypsy Japanese (A) Japanese (B) Jewish (Adonai Malakh) Jewish (Ahaba Rabba) Jewish (Magen Abot) Oriental (A) Oriental (B) Oriental (C) Roumanian Minor Neapolitan Neapolitan Major Neapolitan Minor Overtone Leading Whole Tone Nine Tone Scale Dominant Seventh Augmented Algerian Arabian (A) Arabian (B) Balinese Chinese Diminished Japanese (Ichikosucho) Japanese (Taishikicho) Javaneese Marva Theta Mela Bhavapriya Mela Chakravakam Mela Chalanata Mela Chitrambari Mela Dharmavati Mela Dhatuvardhani Mela Dhavalambari Mela Divyamani Mela Ganamurti Mela Gangeyabhusani Mela Gavambodhi Mela Gayakapriya Mela Hatakambari Mela Jalarnavam Mela Jhalavarali Mela Jhankaradhvani Mela Jyotisvarupini Mela Kamavarardhani Mela Kantamani Mela Kosalam Mela Latangi Mela Manavati Mela Mararanjani Mela Naganandini Mela Namanarayani Mela Navanitam Mela Nitimati Mela Pavani Mela Ragavardhani Mela Raghupriya Mela Ramapriya Mela Rasikapriya Mela Ratnangi Mela Risabhapriya Mela Rupavati Mela Sadvidhamargini Mela Salagam Mela Sanmukhapriya Mela Sarasangi Mela Senavati Mela Subhapantuvarali Mela Sucharitra Mela Sulini Mela Suryakantam Mela Syamalangi Mela Tanarupi Mela Vagadhisvari Mela Vanaspati Mela Varunapriya Mela Yagapriya Persian Purvi Theta Spanish Gypsy Todi Theta Enigmatic Kumoi Lydian Augmented Pelog Prometheus Prometheus Neapolitan Six Tone Symmetrical Super Locrian Lydian Minor Lydian Diminished Half Diminished Bhairav Yaman Todi Jog Multani Darbari Malkauns Bhoopali Shivaranjani Marwa Minor 5 Major 5 5 45 457 M 6 MIDI Editor MIDIエディタ cut 切りå–り delete 削除 insert range ãƒ¬ãƒ³ã‚¸ã®æŒ¿å…¥ remove range レンジã®å‰Šé™¤ move 移動 edit 編集 resize リサイズ rescale リスケール paste 貼り付㑠Time: %1 Type: 時間: %1 種類: Note On (%1) %2 Velocity: %3 Duration: %4 ノートオン (%1) %2 ベロシティ: %3 デュレーション: %4 Key Press (%1) %2 Value: %3 キープレス (%1) %2 値: %3 Controller (%1) Name: %2 Value: %3 コントローラー (%1) åå‰: %2 値: %3 RPN (%1) Name: %2 Value: %3 RPN (%1) åå‰: %2 値: %3 NRPN (%1) Name: %2 Value: %3 NRPN (%1) åå‰: %2 値: %3 Control 14 (%1) Name: %2 Value: %3 コントロール14 (%1) åå‰: %2 値: %3 Pgm Change (%1) Pgmãƒã‚§ãƒ³ã‚¸ (%1) Chan Press (%1) ãƒãƒ£ãƒ³ãƒãƒ«ãƒ—レス (%1) Pitch Bend (%1) ピッãƒãƒ™ãƒ³ãƒ‰ (%1) SysEx (%1 bytes) Data: SysEx (%1ãƒã‚¤ãƒˆ) データ: Unknown (%1) 䏿˜Ž (%1) Start: %1 End: %2 Length: %3 é–‹å§‹: %1 終了: %2 é•·ã•: %3 qtractorMidiEditorForm MIDI Editor MIDIエディタ &Track トラック(&T) &File ファイル(&F) Select &Mode é¸æŠžãƒ¢ãƒ¼ãƒ‰(&M) &Select é¸æŠž(&S) I&nsert 挿入(&N) Remo&ve 削除(&V) &Tools ツール(&T) &Edit 編集(&E) &View 表示(&V) Instrum&ent インストルメント(&E) &Toolbars ツールãƒãƒ¼(&T) &Windows ウィンドウ(&W) Not&e Type ノートタイプ(&E) Val&ue Type 値ã®ç¨®é¡ž(&U) &Ghost Track ゴーストトラック(&G) &Zoom ズーム(&Z) S&nap スナップ(&N) Sc&ale スケール(&A) T&ransport トランスãƒãƒ¼ãƒˆ(&R) &Note St&ep &Help ヘルプ(&H) &Save ä¿å­˜(&S) Save ä¿å­˜ Save current MIDI clip to existing file name ç¾åœ¨ã®MIDIクリップをファイルã¸ä¿å­˜ã—ã¾ã™ Save &As... åå‰ã‚’付ã‘ã¦ä¿å­˜(&A)... Save As åå‰ã‚’付ã‘ã¦ä¿å­˜ Save as åå‰ã‚’付ã‘ã¦ä¿å­˜ Save current MIDI clip with another file name ç¾åœ¨ã®MIDIクリップを別ãªãƒ•ァイルåã§ä¿å­˜ã—ã¾ã™ &Mute ミュート(&M) Mute Mute current MIDI clip &Unlink リンク解除(&U) Unlink リンク解除 Unlink current MIDI clip ç¾åœ¨ã®MIDIクリップã®ãƒªãƒ³ã‚¯ã‚’解除 Recor&d 記録(&D) Record current MIDI clip (overdub) ç¾åœ¨ã®MIDIクリップを記録ã™ã‚‹(オーãƒãƒ¼ãƒ€ãƒ–) &Inputs 入力(&I) Track Inputs トラック入力 Track inputs トラック入力 Show current MIDI clip/track input bus connections ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã®MIDI入力ãƒã‚¹æŽ¥ç¶šã‚’表示ã—ã¾ã™ &Outputs 出力(&O) Track Outputs トラック出力 Track outputs トラック出力 Show current MIDI clip/track output bus connections ç¾åœ¨ã®ãƒˆãƒ©ãƒƒã‚¯ã®MIDI出力ãƒã‚¹æŽ¥ç¶šã‚’表示ã—ã¾ã™ &Properties... プロパティ(&P)... Track Properties トラックã®ãƒ—ロパティ Track properties トラックã®ãƒ—ロパティ Edit current MIDI clip/track properties é¸æŠžã—ã¦ã„ã‚‹MIDIトラックã®ãƒ—ロパティを編集ã—ã¾ã™ Shift+F2 Properties プロパティ Edit current MIDI clip properties ç¾åœ¨ã®MIDIクリップã®ãƒ—ロパティを編集ã—ã¾ã™ F4 &Range Set レンジã®è¨­å®š(&R) Clip Range クリップレンジ Clip range クリップレンジ Set edit-range from clip extents ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—ã®é•·ã•をレンジã«ã—ã¾ã™ &Loop Set ループã®è¨­å®š(&L) Clip Loop クリップループ Clip loop クリップループ Set loop-range from clip extents ç¾åœ¨ã®ã‚¯ãƒªãƒƒãƒ—ã®é•·ã•ã ã‘ループレンジを設定ã—ã¾ã™ &Close é–‰ã˜ã‚‹(&C) Close é–‰ã˜ã‚‹ Close this MIDI clip editor ã“ã®MIDIクリップエディターを閉ã˜ã¾ã™ &Undo å…ƒã«æˆ»ã™(&U) Undo å…ƒã«æˆ»ã™ Undo last edit operation 最後ã®ç·¨é›†æ“ä½œã‚’å…ƒã«æˆ»ã—ã¾ã™ Ctrl+Z &Redo やり直ã™(&R) Redo やり直㙠Redo last edit operation 最後ã®ç·¨é›†æ“作をやり直ã—ã¾ã™ Ctrl+Shift+Z Cu&t 切りå–り(&T) Cut 切りå–り Cut current selection into the local clipboard ç¾åœ¨ã®é¸æŠžã‚’ローカルクリップボードã¸åˆ‡ã‚Šå–りã¾ã™ Ctrl+X &Copy コピー(&C) Copy コピー Copy current selection to the local clipboard ç¾åœ¨ã®é¸æŠžã‚’ローカルクリップボードã¸ã‚³ãƒ”ーã—ã¾ã™ Ctrl+C &Paste 貼り付ã‘(&P) Paste 貼り付㑠Paste local clipboard contents into the current MIDI clip ローカルクリップボードã®å†…容をç¾åœ¨ã®MIDIクリップã«è²¼ã‚Šä»˜ã‘ã¾ã™ Ctrl+V Past&e Repeat... 繰り返ã—貼り付ã‘(&E)... Paste Repeat 繰り返ã—ã¦è²¼ã‚Šä»˜ã‘ Paste repeat 繰り返ã—ã¦è²¼ã‚Šä»˜ã‘ Paste/repeat local clipboard contents into the current MIDI clip ローカルクリップボードã®å†…容をç¾åœ¨ã®MIDIクリップã«ç¹°ã‚Šè¿”ã—貼付ã‘ã¾ã™ Ctrl+Shift+V &Delete 削除(&D) Delete 削除 Delete current selection ç¾åœ¨ã®é¸æŠžã‚’削除ã—ã¾ã™ Del 削除 Edit Of&f 編集オフ(&F) Edit Off 編集ã®ã‚ªãƒ• Edit off 編集ã®ã‚ªãƒ• Set edit mode off 編集モードをオフã«ã—ã¾ã™ Edit &On 編集オン(&O) Edit On 編集オン Edit on 編集オン Set edit mode on 編集モードをオンã«ã—ã¾ã™ Edit &Draw ドロー編集(&D) Edit draw mode ドロー編集モード Edit draw mode (notes) ノートをæã編集モードã§ã™ &All ã™ã¹ã¦(&A) Select All ã™ã¹ã¦ã‚’é¸æŠž Select all ã™ã¹ã¦ã‚’é¸æŠžã—ã¾ã™ Ctrl+A &None ãªã—(&N) Select None é¸æŠžè§£é™¤ Select none é¸æŠžã‚’è§£é™¤ã—ã¾ã™ Ctrl+Shift+A &Invert å転(&I) Select Invert é¸æŠžã®å転 Select invert é¸æŠžã‚’å転ã—ã¾ã™ Ctrl+I &Range レンジ(&R) Select Range ãƒ¬ãƒ³ã‚¸é¸æŠž Select range ãƒ¬ãƒ³ã‚¸ã‚’é¸æŠž Mark range as selected ãƒ¬ãƒ³ã‚¸ã‚’é¸æŠžæ¸ˆã¿ã«ã—ã¾ã™ Ctrl+R Insert Range ãƒ¬ãƒ³ã‚¸ã®æŒ¿å…¥ Insert range レンジを挿入 Insert range as selected é¸æŠžãƒ¬ãƒ³ã‚¸ã‚’æŒ¿å…¥ã—ã¾ã™ Ctrl+Ins &Step Insert step Insert step (rest) Right DO NOT TRANSLATE Remove Range レンジã®å‰Šé™¤ Remove range レンジを削除ã—ã¾ã™ Remove range as selected é¸æŠžã•れã¦ã„るレンジを削除ã—ã¾ã™ Ctrl+Del &Quantize... クォンタイズ(&Q)... Quantize クォンタイズ Quantize selection é¸æŠžã‚’ã‚¯ã‚ªãƒ³ã‚¿ã‚¤ã‚ºã—ã¾ã™ &Transpose... トランスãƒãƒ¼ã‚º(&T)... Transpose トランスãƒãƒ¼ã‚º Transpose selection é¸æŠžã‚’ãƒˆãƒ©ãƒ³ã‚¹ãƒãƒ¼ã‚ºã—ã¾ã™ &Normalize... ノーマライズ(&N)... Normalize ノーマライズ Normalize selection é¸æŠžã‚’ãƒŽãƒ¼ãƒžãƒ©ã‚¤ã‚ºã—ã¾ã™ &Randomize... ランダマイズ(&R)... Randomize ランダマイズ Randomize selection é¸æŠžã‚’ãƒ©ãƒ³ãƒ€ãƒžã‚¤ã‚ºã—ã¾ã™ Resi&ze... リサイズ(&Z)... Resize リサイズ Resize selection é¸æŠžã‚’ãƒªã‚µã‚¤ã‚ºã—ã¾ã™ Re&scale... リスケール(&S)... Rescale リスケール Rescale selection é¸æŠžã‚’ãƒªã‚¹ã‚±ãƒ¼ãƒ«ã—ã¾ã™ T&imeshift... タイムシフト(&I)... Timeshift タイムシフト Timeshift selection é¸æŠžã‚’ã‚¿ã‚¤ãƒ ã‚·ãƒ•ãƒˆã—ã¾ã™ T&empo ramp... Tempo ramp Tempo ramp selection &Menubar メニューãƒãƒ¼(&M) Menubar メニューãƒãƒ¼ Show/hide the menubar メインウィンドウã®ãƒ¡ãƒ‹ãƒ¥ãƒ¼ãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ Ctrl+M &Statusbar ステータスãƒãƒ¼(&S) Statusbar ステータスãƒãƒ¼ Show/hide the statusbar メインウィンドウã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ File Toolbar ファイルツールãƒãƒ¼ File toolbar ファイルツールãƒãƒ¼ Show/hide the file toolbar メインウィンドウã®ãƒ•ァイルツールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ Edit Toolbar エディットツールãƒãƒ¼ Edit toolbar エディットツールãƒãƒ¼ Show/hide the edit toolbar エディットツールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ View Toolbar ビューツールãƒãƒ¼ View toolbar ビューツールãƒãƒ¼ Show/hide the view toolbar ビューツールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ &Transport トランスãƒãƒ¼ãƒˆ(&T) Transport Toolbar トランスãƒãƒ¼ãƒˆãƒ„ールãƒãƒ¼ Transport toolbar トランスãƒãƒ¼ãƒˆãƒ„ールãƒãƒ¼ Show/hide the transport toolbar トランスãƒãƒ¼ãƒˆãƒ„ールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ T&ime 時間(&I) Time Toolbar 時間ツールãƒãƒ¼ Time toolbar 時間ツールãƒãƒ¼ Show/hide the time toolbar 時間ツールãƒãƒ¼ã®è¡¨ç¤ºåˆ‡ã‚Šæ›¿ãˆ &Scale スケール(&S) Scale Toolbar スケールツールãƒãƒ¼ Scale toolbar スケールツールãƒãƒ¼ Show/hide the scale toolbar 音階ツールãƒãƒ¼ã‚’表示/éš ã—ã¾ã™ Thum&b 縮å°è¡¨ç¤º(&B) Thumb Toolbar 縮å°è¡¨ç¤ºãƒ„ールãƒãƒ¼ Thumb toolbar 縮å°è¡¨ç¤ºãƒ„ールãƒãƒ¼ Show/hide the thumb view toolbar 縮å°è¡¨ç¤ºãƒ„ールãƒãƒ¼ã®æœ‰åй/無効 Note &Names ノートå(&N) Note Names ノートå Note names ノートå Whether to show note names ノートåを表示ã™ã‚‹ã‹ã©ã†ã‹ Note &Duration ノートデュレーション(&D) Note Duration ノートデュレーション Note duration ノートデュレーション Whether note events are shown proportional to duration デュレーションã«å¾“ã„ノートイベントを表示ã—ã¾ã™ Note &Color ピッãƒç€è‰²(&C) Note Color ピッãƒã®ç€è‰² Note color ピッãƒã®ç€è‰² Whether note events are colored according to pitch ピッãƒã«å¾“ã„ノートイベントをç€è‰²ã—ã¾ã™ &Value Color ベロシティーç€è‰²(&V) Value Color ベロシティーç€è‰² Value color ベロシティーç€è‰² Whether note events are colored according to value (velocity) ベロシティã«å¾“ã„ノートイベントをç€è‰²ã—ã¾ã™ &Drum Mode ドラムモード(&D) Drum Mode ドラムモード Drum mode ドラムモード Whether note onset events are displayed as diamonds ノートã®ã‚ªãƒ³ã‚»ãƒƒãƒˆã‚¤ãƒ™ãƒ³ãƒˆã‚’ã²ã—形記å·ã§è¡¨ç¤ºã™ã‚‹ã‹ã©ã†ã‹ &Events イベント(&E) View events イベントã®è¡¨ç¤º Show/hide the events list イベントリストを表示/éš ã—ã¾ã™ &Preview Notes ノートã®ãƒ—レビュー(&P) Preview Notes ノートã®ãƒ—レビュー Preview notes ノートã®ãƒ—レビュー Preview notes while editing (scrub) 編集中ã«ãƒŽãƒ¼ãƒˆã‚’プレビューã—ã¾ã™ F&ollow Playhead å†ç”Ÿã«è¿½éš(&O) Follow Playhead å†ç”Ÿã«è¿½éš Follow playhead å†ç”Ÿç®‡æ‰€ã‚’追ã„ã‹ã‘ã¾ã™ &In イン(&I) Zoom In ズームイン Zoom in ズームイン Ctrl++ &Out アウト(&O) Zoom Out ズームアウト Zoom out ズームアウト Ctrl+- &Reset リセット(&R) Zoom Reset ズームリセット Zoom reset ズームリセット Ctrl+1 &Horizontal 水平方å‘(&H) Horizontal Zoom 水平方å‘ズーム Horizontal zoom 水平方å‘ズーム Horizontal zoom mode 水平方å‘ã«ã‚ºãƒ¼ãƒ ã™ã‚‹ãƒ¢ãƒ¼ãƒ‰ã§ã™ &Vertical 垂直方å‘(&V) Vertical Zoom 垂直方å‘ズーム Vertical zoom 垂直方å‘ズーム Vertical zoom mode 垂直方å‘ã«ã‚ºãƒ¼ãƒ ã™ã‚‹ãƒ¢ãƒ¼ãƒ‰ã§ã™ All Zoom 垂直水平ズーム All zoom 垂直水平ズーム All zoom mode 垂直方å‘ã«ã‚‚水平方å‘ã«ã‚‚ズームã™ã‚‹ãƒ¢ãƒ¼ãƒ‰ã§ã™ &Zebra Zebra Bar zebra view mode &Grid グリッド(&G) Grid グリッド Snap grid view mode グリッドã«ã‚¹ãƒŠãƒƒãƒ—ã™ã‚‹ãƒ“ューモードã§ã™ Too&l Tips ツールãƒãƒƒãƒ—(&L) Tool tips ツールãƒãƒƒãƒ— Floating tool tips view mode ツールãƒãƒƒãƒ—をフローティング表示ã—ã¾ã™ &Refresh æ›´æ–°(&R) Refresh æ›´æ–° Refresh views ビューを更新ã—ã¾ã™ F5 &Backward 先頭ã¸(&B) Backward 先頭㸠Transport backward トランスãƒãƒ¼ãƒˆã‚’先頭ã¸ç§»å‹•ã—ã¾ã™ Backspace ãƒãƒƒã‚¯ã‚¹ãƒšãƒ¼ã‚¹ Re&wind 逆å†ç”Ÿ(&W) Rewind 逆å†ç”Ÿ Transport rewind トランスãƒãƒ¼ãƒˆã‚’逆å†ç”Ÿã—ã¾ã™ F&ast Forward æ—©é€ã‚Š(&A) Fast Forward æ—©é€ã‚Š Fast forward æ—©é€ã‚Š Transport fast forward トランスãƒãƒ¼ãƒˆã‚’æ—©é€ã‚Šã—ã¾ã™ &Forward 末尾ã¸(&F) Forward 末尾㸠Transport forward トランスãƒãƒ¼ãƒˆã‚’末尾ã¸ç§»å‹•ã—ã¾ã™ Step Backward Step backward Transport step backward Step Forward Step forward Transport step forward Note Backward Step note backward Transport step note backward Note Forward Step note forward Transport step note forward &Loop ループ(&L) Loop ループ Transport loop トランスãƒãƒ¼ãƒˆã‚’ループã—ã¾ã™ Ctrl+Shift+L Loop &Set ループセット(&S) Loop Set ループセット Loop set ループセット Transport loop set ループセットをトランスãƒãƒ¼ãƒˆã—ã¾ã™ Ctrl+L &Stop åœæ­¢(&S) Stop åœæ­¢ Transport stop トランスãƒãƒ¼ãƒˆã‚’åœæ­¢ã—ã¾ã™ &Play å†ç”Ÿ(&P) Play å†ç”Ÿ Transport play/pause トランスãƒãƒ¼ãƒˆã‚’å†ç”Ÿ/ä¸€æ™‚åœæ­¢ã—ã¾ã™ Space スペース &Record 録音(&R) Record 録音 Transport record トランスãƒãƒ¼ãƒˆã‚’録音ã—ã¾ã™ &Punch パンãƒ(&P) Punch パンムPunch in/out パンãƒã‚¤ãƒ³/アウト Transport punch in/out Ctrl+Shift+P Punch Se&t パンãƒã‚»ãƒƒãƒˆ(&T) Punch Set パンãƒã‚»ãƒƒãƒˆ Punch in/out set パンãƒã‚¤ãƒ³/アウトを設定 Transport punch in/out set トランスãƒãƒ¼ãƒˆã®ãƒ‘ンãƒã‚¤ãƒ³/アウトを設定ã—ã¾ã™ Ctrl+P Pa&nic パニック(&N) Panic パニック All MIDI tracks shut off (panic) ã™ã¹ã¦ã®MIDIトラックをオフã«ã—ã¾ã™ (パニック) &Shortcuts... ショートカット(&S)... Shortcuts ショートカット Keyboard shortcuts キーボードショートカット &About... Qtractorã«ã¤ã„ã¦(&A)... About ã«ã¤ã„㦠Show information about this application program ã“ã®ã‚½ãƒ•トウェアã«é–¢ã™ã‚‹æƒ…報を表示ã—ã¾ã™ About &Qt... Qtã«ã¤ã„ã¦(&Q)... About Qt Qtã«ã¤ã„㦠Show information about the Qt toolkit Qtツールキットã«é–¢ã™ã‚‹æƒ…報を表示ã—ã¾ã™ Current time (play-head) ç¾åœ¨ã®æ™‚é–“ (å†ç”Ÿã®å…ˆé ­) Current tempo (BPM) ç¾åœ¨ã®ãƒ†ãƒ³ãƒ (BPM) Reset time-sig. æ‹å­ã®å¤‰æ›´ã€‚ Set current snap to %1 %1 ã«ã‚¹ãƒŠãƒƒãƒ—ã—ã¾ã™ Note Velocity ノートã®ãƒ™ãƒ­ã‚·ãƒ†ã‚£ãƒ¼ Snap/beat スナップ/beat Note type ノートタイプ Value type 値ã®ã‚¿ã‚¤ãƒ— Parameter type パラメータータイプ Scale key スケールキー Scale type スケールタイプ MIDI clip name MIDIクリップå MIDI file name MIDIファイルå MIDI track/channel MIDIトラック/ãƒãƒ£ãƒ³ãƒãƒ« MOD 変更 MIDI modification state MIDIã®å¤‰æ›´çŠ¶æ…‹ REC 記録 MIDI clip record state MIDIクリップã®è¨˜éŒ²çŠ¶æ…‹ MUTE ミュート MIDI clip mute state 00:00:00.000 MIDI clip duration MIDIクリップã®ãƒ‡ãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ Warning 警告 The current MIDI clip has been changed: "%1" Do you want to save the changes? ç¾åœ¨ã®MIDIクリップã¯å¤‰æ›´ã•れã¦ã„ã¾ã™: "%1" 変更をä¿å­˜ã—ã¾ã™ã‹ï¼Ÿ Save MIDI Clip MIDIクリップã®ä¿å­˜ MIDI files (*.%1 *.smf *.midi) MIDIファイル(*.%1 *.smf *.midi) All files (*.*) 全ファイル (*.*) Channel %1 ãƒãƒ£ãƒ³ãƒãƒ« %1 Track %1 トラック %1 [modified] [変更ã•れã¾ã—ãŸ] qtractorMidiEventList Events イベント qtractorMidiEventListView::ItemDelegate edit %1 編集 %1 qtractorMidiEventListView::ItemModel Time 時間 Type Name åå‰ Value 値 Duration/Data デュレーション/データ Frame フレーム BBT Note On (%1) ノートオン (%1) Note Off (%1) ノートオフ (%1) Key Press (%1) キープレス (%1) Controller (%1) コントローラー (%1) Control 14 (%1) コントロール 14 (%1) RPN (%1) RPN (%1) NRPN (%1) NRPN (%1) Pgm Change Pgmãƒã‚§ãƒ³ã‚¸ Chan Press ãƒãƒ£ãƒ³ãƒãƒ«ãƒ—レス Pitch Bend ピッãƒãƒ™ãƒ³ãƒ‰ SysEx SysEx Meta (%1) メタ (%1) Unknown (%1) 䏿˜Ž (%1) qtractorMidiListView Name åå‰ Fmt Tracks トラック tpqn tpqn Path パス %1: MIDI file not found. %1: MIDI ファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。 Open MIDI Files MIDIファイルを開ã MIDI files (*.%1 *.smf *.midi) MIDIファイル (*.%1 *.smf *.midi) All files (*.*) 全ファイル (*.*) qtractorMidiMixerMeter Volume (%) % % Pan: %1 Volume: %1% ボリューム: %1% qtractorMidiSysexForm MIDI SysEx MIDIシステムエクスクルーシブ Name åå‰ Size サイズ Data (hex) データ (16進数表記) Import from SysEx file SysExファイルã‹ã‚‰ã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ &Import... インãƒãƒ¼ãƒˆ(&I)... Export to SysEx file SysExファイルã¸ã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ E&xport... エクスãƒãƒ¼ãƒˆ(&X)... Move SysEx item up on list order SysExアイテムを上ã«ç§»å‹• &Up 上ã¸(&U) Move SysEx item down on list order SysExアイテムを下ã«ç§»å‹• &Down 下ã¸(&D) Open SysEx SysExã‚’é–‹ã Sysex name SysExå Save SysEx SysExã®ä¿å­˜ Delete SysEx SysExã®å‰Šé™¤ Create SysEx item SysExアイテムã®å‰Šé™¤ &Add 追加(&A) Update SysEx item SysExã‚¢ã‚¤ãƒ†ãƒ ã®æ›´æ–° Upda&te æ›´æ–°(&T) Remove SysEx item SysExアイテムã®å‰Šé™¤ &Remove 削除(&R) SysEx files (*.%1) SysExファイル (*.%1) MIDI files (*.mid *.smf *.midi) MIDIファイル (*.mid *.smf *.midi) All files (*.*) 全ファイル (*.*) Import SysEx Files SysExファイルã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ Export SysEx File SysExファイルã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ Warning 警告 The SysEx file already exists: "%1" Do you want to replace it? SysExファイルã¯ã™ã§ã«å­˜åœ¨ã—ã¦ã„ã¾ã™: "%1" ç½®ãã‹ãˆã¾ã™ã‹ï¼Ÿ About to replace SysEx: "%1" Are you sure? SysExã‚’ç½®ãã‹ãˆã¾ã™: "%1" よã‚ã—ã„ã§ã™ã‹ï¼Ÿ About to delete SysEx: "%1" Are you sure? SysExを削除ã—ã¾ã™: "%1" よã‚ã—ã„ã§ã™ã‹ï¼Ÿ SysEx settings have been changed. Do you want to apply the changes? SysEx設定ã¯å¤‰æ›´ã•れã¾ã—ãŸã€‚ 変更をé©ç”¨ã—ã¾ã™ã‹ï¼Ÿ Error エラー SysEx could not be loaded: "%1". Sorry. SysExã¯ãƒ­ãƒ¼ãƒ‰ã•れã¾ã›ã‚“: "%1" ã”ã‚ã‚“ãªã•ã„。 qtractorMidiThumbView MIDI Thumb view MIDI縮å°ãƒ“ュー qtractorMidiToolsForm MIDI Tools MIDIツール Preset name プリセットå Save preset プリセットã®ä¿å­˜ Delete preset プリセットã®å‰Šé™¤ &Quantize クオンタイズ(&Q) Quantize selected events é¸æŠžã—ãŸã‚¤ãƒ™ãƒ³ãƒˆã‚’クオンタイズ &Time: 時間(&T): Quantize time クオンタイズ時間 Quantize time percent ã‚¯ã‚ªãƒ³ã‚¿ã‚¤ã‚ºæ™‚é–“å‰²åˆ % &Duration: デュレーション(&D): Quantize duration クオンタイズã®ãƒ‡ãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ Quantize duration percent クオンタイズã®ãƒ‡ãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³å‰²åˆ S&wing: スウィング(&W): Swing-quantize time スウィングクオンタイズ時間 Swing-quantize percent ã‚¹ã‚¦ã‚£ãƒ³ã‚°ã‚¯ã‚ªãƒ³ã‚¿ã‚¤ã‚ºå‰²åˆ Swing-quantize type スウィングクオンタイズã®ã‚¿ã‚¤ãƒ— Linear Quadratic Cubic &Scale: スケール(&S): Scale-quantize key スケールクオンタイズキー Scale-quantize type スケールクオンタイズã®ã‚¿ã‚¤ãƒ— &Transpose トランスãƒãƒ¼ã‚º(&T) Transpose selected events é¸æŠžã•れãŸã‚¤ãƒ™ãƒ³ãƒˆã®ãƒˆãƒ©ãƒ³ã‚¹ãƒãƒ¼ã‚º &Note: ノート(&N): Transpose note ノートã®ãƒˆãƒ©ãƒ³ã‚¹ãƒãƒ¼ã‚º Transpose time 時間ã®ãƒˆãƒ©ãƒ³ã‚¹ãƒãƒ¼ã‚º Transpose time format トランスãƒãƒ¼ã‚ºæ™‚間フォーマット Frames フレーム Time 時間 BBT &Reverse å転(&R) &Normalize ノーマライズ(&N) Normalize selected events é¸æŠžã•れãŸã‚¤ãƒ™ãƒ³ãƒˆã®ãƒŽãƒ¼ãƒžãƒ©ã‚¤ã‚º &Percent: 割åˆ(&P): Normalize percent 割åˆã®ãƒŽãƒ¼ãƒžãƒ©ã‚¤ã‚º &Value: 値(&V): Normalize value 値ã®ãƒŽãƒ¼ãƒžãƒ©ã‚¤ã‚º &Compress &Randomize ランダマイズ(&R) Randomize selected events é¸æŠžã•れãŸã‚¤ãƒ™ãƒ³ãƒˆã®ãƒŽãƒ¼ãƒžãƒ©ã‚¤ã‚º Randomize note/pitch ノート/ピッãƒã®ãƒŽãƒ¼ãƒžãƒ©ã‚¤ã‚º Randomize time 時間ã®ãƒŽãƒ¼ãƒžãƒ©ã‚¤ã‚º Randomize duration デュレーションã®ãƒ©ãƒ³ãƒ€ãƒžã‚¤ã‚º Randomize value 値ã®ãƒ©ãƒ³ãƒ€ãƒžã‚¤ã‚º Resi&ze リサイズ(&Z) Resize selected events é¸æŠžã•れãŸã‚¤ãƒ™ãƒ³ãƒˆã®ãƒªã‚µã‚¤ã‚º Resize duration デュレーションã®ãƒªã‚µã‚¤ã‚º Resize duration format デュレーションã®ãƒ•ォーマット Resize value 値ã®ãƒªã‚µã‚¤ã‚º Resize value mode 値ã®ãƒªã‚µã‚¤ã‚ºãƒ¢ãƒ¼ãƒ‰ Flat Ramp Resize final value 最後ã®å€¤ã®ãƒªã‚µã‚¤ã‚º &Legato: Legato trim/extend type Normal ノーマル Trim Extend Legato trim/extend length Legato mode Mono Poly &Join &Split: Split notes length Split notes offset Relative Absolute Re&scale リスケール(&S) Rescale selected events é¸æŠžã•れãŸã‚¤ãƒ™ãƒ³ãƒˆã®ãƒªã‚¹ã‚±ãƒ¼ãƒ« Rescale time ãƒªã‚¹ã‚±ãƒ¼ãƒ«ã®æ™‚é–“ Rescale duration リスケールã®ãƒ‡ãƒ¥ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ Rescale value リスケールã®å€¤ &Invert å転(&I) T&imeshift タイムシフト(&I) Timeshift selected events é¸æŠžã•れãŸã‚¤ãƒ™ãƒ³ãƒˆã®ã‚¿ã‚¤ãƒ ã‚·ãƒ•ト Timeshift タイムシフト P: P: Timeshift parameter タイムシフトã®ãƒ‘ラメーター Timeshift parameter (log) タイムシフトパラメーター (対数) Timeshift curve タイムシフトカーブ P = 0 : no change. P > 0 : accelerating shift. P < 0 : slowing down shift. Edit head/tail (blue) markers define the shift range. P = 0 : 変化ãªã— P > 0: 加速シフト P < 0: 減速シフト (é’色ã®)編集マーカーã®å…ˆé ­ã‹ã‚‰æœ«å°¾ã¾ã§ãŒã‚·ãƒ•ト範囲ã§ã™ã€‚ Timeshift duration タイムシフトデュレーション T&empo ramp Tempo ramp selected events Tempo ramp From Tempo ramp start to Temporamp end Edit head/tail (blue) markers define the ramp range. Tempo ramp duration (default) (デフォルト) Warning 警告 About to delete preset: "%1" Are you sure? プリセットを削除ã—ã¾ã™: "%1" よã‚ã—ã„ã§ã™ã‹ï¼Ÿ none ãªã— quantize クォンタイズ transpose トランスãƒãƒ¼ã‚º normalize ノーマライズ randomize ランダマイズ resize リサイズ rescale リスケール timeshift タイムシフト temporamp qtractorMixer Inputs 入力 Tracks トラック Outputs 出力 Mixer ミキサー qtractorMixerMeter Pan qtractorMixerRackWidget &Inputs 入力(&I) &Outputs 出力(&O) &Monitor モニター(&M) &Buses... ãƒã‚¹(&B)... &Audio 音声(&A) &MIDI MIDI(&M) qtractorMixerStrip inputs 入力 outputs 出力 Connect %1 %1 ã®æŽ¥ç¶š (Audio) (音声) (MIDI) (None) (ãªã—) In Out qtractorMonitorButton monitor モニター qtractorOptionsForm &General 全般(&G) Session セッション Default session &file format: デフォルトã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ãƒ•ァイルフォーマット(&F): Default session file format (suffix) デフォルトã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ãƒ•ァイルフォーマット(æ‹¡å¼µå­) Whether to create new sessions based on template テンプレートã«åŸºã¥ã„ã¦æ–°ã—ã„セッションを作æˆã™ã‚‹ã‹ã©ã†ã‹ &New session template: æ–°è¦ã‚»ãƒƒã‚·ãƒ§ãƒ³ãƒ†ãƒ³ãƒ—レート(&N): New session template æ–°è¦ã‚»ãƒƒã‚·ãƒ§ãƒ³ãƒ†ãƒ³ãƒ—レート Browse for new session template æ–°è¦ã‚»ãƒƒã‚·ãƒ§ãƒ³ãƒ†ãƒ³ãƒ—レートを一覧 Whether to save backup versions of existing sessions 既存ã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’ä¿å­˜ã™ã‚‹ã‹ã©ã†ã‹ Save &backup versions of existing sessions: スペースãŒç‹­ãã¦å®Œè¨³ã™ã‚‹ã¨åŽã¾ã‚‰ãªã„ ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã‚’ãƒãƒ¼ã‚¸ãƒ§ãƒ³åˆ¥ã«ä¿å­˜(&B): Which mode to rename existing session files 既存ã®ã‚»ãƒƒã‚·ãƒ§ãƒ³ãƒ•ァイルをリãƒãƒ¼ãƒ ã™ã‚‹ãƒ¢ãƒ¼ãƒ‰ Increment previous version (default) å‰ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’インクリメント(デフォルト) Increment current version ç¾åœ¨ã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’インクリメント Whether to enable session auto-save (crash-recovery) セッションã®è‡ªå‹•ä¿å­˜ã‚’有効ã«ã™ã‚‹ã‹ã©ã†ã‹ (クラッシュã‹ã‚‰ã®å¾©å…ƒã®ãŸã‚) Auto-save current working session every: ç¾åœ¨ã®ãƒ¯ãƒ¼ã‚­ãƒ³ã‚°ã‚»ãƒƒã‚·ãƒ§ãƒ³ã‚’自動ä¿å­˜: Auto-save period (minutes) 自動ä¿å­˜ã®é–“éš” (分) minutes 分 Options オプション Whether to ask for confirmation on removal 削除ã®ç¢ºèªã‚’ã™ã‚‹ã‹ã©ã†ã‹ &Confirm removals 削除ã®ç¢ºèª(&C) Whether to ask for confirmation on archive directory removal アーカイブディレクトリã®å‰Šé™¤æ™‚ã«ç¢ºèªã™ã‚‹ã‹ã©ã†ã‹ C&onfirm archive removals アーカイブã®å‰Šé™¤ã‚’確èª(&O) Number of &recent files: 最近使ã£ãŸãƒ•ァイルã®è¡¨ç¤ºæ•°(&R): The maximum number of recent files to keep in menu 最近ã®ãƒ•ァイルメニューã«è¡¨ç¤ºã™ã‚‹ãƒ•ã‚¡ã‚¤ãƒ«ã®æœ€å¤§æ•° Whether to capture standard output (stdout/stderr) into messages window 標準出力(stdout/stderror)をキャプãƒãƒ£ãƒ¼ã—メッセージウィンドウã«è¡¨ç¤ºã™ã‚‹ã‹ã©ã†ã‹ Capture standard &output 標準出力ã®ã‚­ãƒ£ãƒ—ãƒãƒ£ãƒ¼(&O) Whether to show the complete directory path of loaded session files ロードã—ãŸã‚»ãƒƒã‚·ãƒ§ãƒ³ãƒ•ァイルã®ãƒ•ルパスを表示ã™ã‚‹ã‹ã©ã†ã‹ S&how complete path of session files セッションファイルã®ãƒ•ルパス表示(&H) Whether to remove audio peak files on session close セッション終了ã®éš›ã«éŸ³å£°ãƒ”ークファイルを削除ã™ã‚‹ã‹ã©ã†ã‹ Auto-remove audio pea&k files 音声ピークファイルã®è‡ªå‹•削除(&K) Whether to keep all tool windows on top of the main window ã™ã¹ã¦ã®ãƒ„ãƒ¼ãƒ«ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã‚’ãƒ¡ã‚¤ãƒ³ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã‚ˆã‚Šã‚‚å¸¸ã«æ‰‹å‰ã«è¡¨ç¤ºã™ã‚‹ã‹ã©ã†ã‹ Keep tool &windows always on top ãƒ„ãƒ¼ãƒ«ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã‚’å¸¸ã«æ‰‹å‰ã«è¡¨ç¤º(&W) Whether to try dropping multiple audio files into the same track 複数ã®éŸ³å£°ãƒ•ァイルをåŒã˜ãƒˆãƒ©ãƒƒã‚¯ã«ãƒ‰ãƒ­ãƒƒãƒ—ã§ãるよã†ã«ã™ã‚‹ã‹ã©ã†ã‹ &Drop multiple audio files into the same track 複数ã®éŸ³å£°ãƒ•ァイルをåŒã˜ãƒˆãƒ©ãƒƒã‚¯ã«ãƒ‰ãƒ­ãƒƒãƒ—(&D) Reverse &keyboard modifiers role (Shift/Ctrl) キーボードã®ä¿®é£¾ã‚­ãƒ¼ã®å½¹å‰²ã‚’入れ替ãˆã‚‹ (Shift/Ctrl)(&K) Whether to reverse mouse middle-button role on keyboard modifiers (Shift/Ctrl) マウスã®ãƒŸãƒ‰ãƒ«ãƒœã‚¿ãƒ³ã®å½¹å‰²ã‚’ã€ã‚­ãƒ¼ãƒœãƒ¼ãƒ‰ã®ä¿®é£¾ã‚­ãƒ¼ï¼ˆShift/Ctl)ã§é€†è»¢ã•ã›ã‚‹ã‹ã©ã†ã‹ Re&verse middle-button modifier role (Shift/Ctrl) 修飾キーã§ãƒŸãƒ‰ãƒ«ãƒœã‚¿ãƒ³ã®å½¹å‰²ã‚’逆転(&V) Transport トランスãƒãƒ¼ãƒˆ Transport &mode: トランスãƒãƒ¼ãƒˆãƒ¢ãƒ¼ãƒ‰(&M): Transport control mode (JACK) トランスãƒãƒ¼ãƒˆã‚³ãƒ³ãƒˆãƒ­ãƒ¼ãƒ«ãƒ¢ãƒ¼ãƒ‰(JACK) None ãªã— Slave スレーブ Master マスター Full フル Whether to start as timebase master (JACK) タイムベースマスター(JACK)ã¨ã—ã¦é–‹å§‹ã™ã‚‹ã‹ã©ã†ã‹ &Timebase タイムベース(&T) &Loop recording mode (takes): ループ録音モード (テイク)(&L): Loop recording mode (takes) ループ録音モード (テイク) First æœ€åˆ Last 最後 &Audio 音声(&A) Capture / Export キャプãƒãƒ£ / エクスãƒãƒ¼ãƒˆ Audio compression quality to use on capture (record) and export キャプãƒãƒ£ãƒ¼(録音)ã¨ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã®éš›ã®éŸ³å£°åœ§ç¸®ã‚¯ã‚ªãƒªãƒ†ã‚£ãƒ¼ Audio sample format to use on capture (record) and export キャプãƒãƒ£ãƒ¼(録音)ã¨ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã®éš›ã®éŸ³å£°ã‚µãƒ³ãƒ—ルフォーマット Audio file type to use on capture (record) and export キャプãƒãƒ£ãƒ¼(録音)ã¨ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã®éš›ã®éŸ³å£°ãƒ•ァイルタイプ File &type: ファイルタイプ(&T): Sample &format: サンプルフォーマット(&F): &Quality: 圧縮クオリティー(&Q): Playback å†ç”Ÿ Whether to apply time-stretching when tempo changes テンãƒãŒå¤‰æ›´ã•れãŸéš›ã«ã‚¿ã‚¤ãƒ ã‚¹ãƒˆãƒ¬ãƒƒãƒã‚’é©ç”¨ã™ã‚‹ã‹ã©ã†ã‹ Aut&omatic time-stretching 自動タイムストレッãƒ(&O) Sample-&rate converter type: サンプリング周波数変更タイプ(&R): Sample-rate converter quality サンプリング周波数変æ›ã‚¯ã‚ªãƒªãƒ†ã‚£ãƒ¼ Sinc (Best Quality) Sinc (ベストクオリティー) Sinc (Medium Quality) Sinc (中ãらã„ã®ã‚¯ã‚ªãƒªãƒ†ã‚£ãƒ¼) Sinc (Fastest) Sinc (最速) Zero Order Hold Linear Whether to use WSOLA time-stretching WSOLAタイムストレッãƒã‚’使ã†ã‹ã©ã†ã‹ &WSOLA time-stretching WSOLAタイムストレッãƒ(&W) &Offset (latency): オフセット(レイテンシ)(&O): Metronome Audio offset (latency) メトロノームã®éŸ³å£°ã‚ªãƒ•セット(レイテンシ) Metronome MIDI duration (bar) メトロノームã®MIDIデュレーション(bar) Metronome MIDI duration (beat) メトロノームã®MIDIデュレーション(beat) Metronome MIDI offset (latency) メトロノームã®MIDIオフセット(レイテンシ) &Style theme: スタイルテーマ(&S): Whether to apply WSOLA quick seek time-stretching WSOLAクイックシークタイムストレッãƒãƒ³ã‚°ã‚’é©ç”¨ã™ã‚‹ã‹ã©ã†ã‹ Whether to reverse keyboard modifiers role on transport positioning (Shift/Ctrl) トランスãƒãƒ¼ãƒˆãƒã‚¸ã‚·ãƒ§ãƒ‹ãƒ³ã‚°ã«ãŠã‘るキーボードã®ä¿®é£¾ã‚­ãƒ¼ã®å½¹å‰²ã‚’å転ã™ã‚‹ã‹ã©ã†ã‹ (Shift/Ctrl) Whether to keep all editor windows on top of the main window ã™ã¹ã¦ã®ã‚¨ãƒ‡ã‚£ã‚¿ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã‚’ãƒ¡ã‚¤ãƒ³ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã‚ˆã‚Šã‚‚å¸¸ã«æ‰‹å‰ã«è¡¨ç¤ºã™ã‚‹ã‹ã©ã†ã‹ Keep &editor windows always on top ã‚¨ãƒ‡ã‚£ã‚¿ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã‚’å¸¸ã«æ‰‹å‰ã«è¡¨ç¤º(&E) Whether to use RubberBand formant preserve RubberBand &formant preserve WSOLA quic&k seek WSOLAクイックシーク(&K) Whether to use RubberBand R3 finer engine RubberBand R&3 finer engine Whether to have separate audition/pre-listening player output ports オーディション/プレリスティングプレイヤー専用ã«å‡ºåŠ›ãƒãƒ¼ãƒˆã‚’å€‹åˆ¥ã«æŒã¤ã‹ã©ã†ã‹ Dedicated au&dition/pre-listening player outputs: オーディション/プレリスティングプレイヤー専用出力(&D): Whether to auto-connect dedicated audio player outputs 音声プレイヤー専用出力ã«è‡ªå‹•接続ã™ã‚‹ã‹ã©ã†ã‹ Auto-&connect 自動接続(&C) Connections 接続 Whether to warn about audio self-connections &Warn about self-connections Metronome メトロノーム Whether to enable the audio metronome 音声メトロノームを有効ã«ã™ã‚‹ã‹ã©ã†ã‹ &Enable audio metronome 音声メトロノームを有効(&E) &Count-in: Count-in mode Recording Count-in number beats &File (bar): ファイル (bar) (&F): Metronome Audio filename (bar) メトロノーム用音声ファイルå (bar) Browse for sample audio file (bar) éŸ³å£°ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠž (bar) &Gain (bar): ゲイン (bar) (&G): Metronome gain (bar) メトロノームゲイン (bar) dB &File (beat): ファイル (beat) (&F): Metronome Audio filename (beat) メトロノーム用音声ファイルå (beat) Browse for sample audio file (beat) éŸ³å£°ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠž (beat) &Gain (beat): ゲイン (beat) (&G): Metronome gain (beat) メトロノームゲイン (beat) Whether to have separate audio metronome output ports 音声メトロノームã®å‡ºåŠ›ãƒãƒ¼ãƒˆã‚’分離ã™ã‚‹ã‹ã©ã†ã‹ Dedicated a&udio metronome outputs: 音声メトロノーム専用出力(&U): Whether to auto-connect dedicated audio metronome outputs 音声メトロノーム専用出力を自動接続 Auto-co&nnect 自動接続(&N) &MIDI MIDI(&M) File &format: ファイルフォーマット(&F): MIDI file format to use on capture (record) and export キャプãƒãƒ£ãƒ¼(録音)ã¨ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã«ä½¿ã†MIDIファイルフォーマット &Quantize: クォンタイズ(&Q): MIDI capture (record) quantization MIDIキャプãƒãƒ£ãƒ¼(録音)ã®ã‚¯ã‚ªãƒ³ã‚¿ã‚¤ã‚º Queue &timer (resolution): キュータイマー (è§£åƒåº¦) (&T): Queue timer (resolution) キュータイマー (è§£åƒåº¦) Whether to enable MIDI queue time drift correction MIDIキュータイムドリフト訂正を有効ã«ã™ã‚‹ã‹ã©ã†ã‹ E&nable MIDI queue time drift correction MIDIキュータイムドリフト訂正(&N) Whether to have separate MIDI player output ports MIDIプレイヤー出力用ã®ãƒãƒ¼ãƒˆã‚’設ã‘ã‚‹ã‹ã©ã†ã‹ Dedicated MIDI p&layer outputs MIDIプレイヤー専用出力(&L) Whether to reset/resend all controllers on playback start &Reset all controllers on playback start Control コントロール &MMC: MIDI Machine Control (MMC) mode MIDIマシンコントロール(MMC)モード Input 入力 Output 出力 Duplex 入出力 &Device: デãƒã‚¤ã‚¹(&D): MIDI Machine Control (MMC) device id. MIDIマシンコントロール (MMC) デãƒã‚¤ã‚¹ID。 &SPP: MIDI Song Position pointer (SPP) control mode MIDIソングãƒã‚¸ã‚·ãƒ§ãƒ³ãƒã‚¤ãƒ³ã‚¿ãƒ¼ (SPP) コントロールモード MIDI Cloc&k: MIDIクロック(&K): MIDI Clock control mode MIDIクロックコントロールモード Whether to have separate MIDI control ports MIDIコントロール用ã®åˆ¥ãªãƒãƒ¼ãƒˆã‚’設ã‘ã‚‹ã‹ã©ã†ã‹ Dedicated MIDI &control input/output MIDIコントロール専用入出力(&C) Whether to enable the MIDI metronome MIDIメトロノームを有効ã«ã™ã‚‹ã‹ã©ã†ã‹ &Enable MIDI metronome MIDIメトロノーム有効(&E) &Channel: ãƒãƒ£ãƒ³ãƒãƒ«(&C): Metronome MIDI channel メトロノームMIDIãƒãƒ£ãƒ³ãƒãƒ« &Note (bar): ノート(bar) (&N): Metronome MIDI note (bar) メトロノームMIDIノート (bar) &Velocity (bar): ベロシティー(bar) (&V): Metronome MIDI velocity (bar) メトロノームMIDIベロシティー (bar) &Duration (bar): デュレーション(bar) (&D): &Note (beat): ノート(beat) (&N): Metronome MIDI note (beat) メトロノームMIDIノート(beat) &Velocity (beat): ベロシティー(beat) (&V): Metronome MIDI velocity (beat) メトロノームMIDIベロシティー(beat) &Duration (beat): デュレーション(beat) (&D): Whether to have separate MIDI metronome output port MIDIメトロノーム出力用ã®åˆ¥ãªãƒãƒ¼ãƒˆã‚’設ã‘ã‚‹ã‹ã©ã†ã‹ Dedicated M&IDI metronome output MIDIメトロノーム専用出力(&I) &Display 表示(&D) Defaults デフォルト &Time display format: 時間表示フォーマット(&T): Time display format 時間表示フォーマット Frames フレーム Time 時間 BBT &Base font size: 基本フォントサイズ(&B): Base application font size (pt.) フォントã®ãƒ™ãƒ¼ã‚¹ã‚µã‚¤ã‚º(pt.) (default) (デフォルト) 6 7 8 9 10 11 12 Whether to use desktop environment native dialogs. デスクトップ環境ãƒã‚¤ãƒ†ã‚£ãƒ–ãªãƒ€ã‚¤ã‚¢ãƒ­ã‚°ã‚’使ã†ã‹ã©ã†ã‹. Use desktop environment &native dialogs デスクトップ環境ãƒã‚¤ãƒ†ã‚£ãƒ–ãªãƒ€ã‚¤ã‚¢ãƒ­ã‚°ã‚’使ã†(&N) Manage custom color palette themes カスタムカラーパレットテーマã®ç®¡ç† &Icons theme: Custom icons theme directory Browse for custom icons theme directory St&yle sheet: Custom style sheet (*.qss) Browse for custom style sheet (*.qss) Move down path Whether to select plugin's editor (GUI) if more than one are available プラグインã«å¯¾ã—ã¦ä¸€ã¤ä»¥ä¸Šã®ã‚¨ãƒ‡ã‚£ã‚¿ãƒ¼(GUI)ãŒåˆ©ç”¨å¯èƒ½ãªå ´åˆã«é¸æŠžã™ã‚‹ã‹ã©ã†ã‹ Blacklist ブラックリスト Plugin blacklist path プラグインã®ãƒ–ラックリストã¸ã®ãƒ‘ス Browse plugin blacklist path プラグインã®ãƒ–ラックリストを見ã¾ã™ Add plugin blacklist path プラグインã®ãƒ–ラックリストã¸ã®ãƒ‘スを追加ã—ã¾ã™ Plugin blacklist paths プラグインã®ãƒ–ラックリストã¸ã®ãƒ‘ス Remove plugin blacklist path プラグインã®ãƒ–ラックリストã¸ã®ãƒ‘スを削除ã—ã¾ã™ Clear plugin blacklist paths プラグインã®ãƒ–ラックリストã¸ã®ãƒ‘スを全削除ã—ã¾ã™ &Clear 全削除(&C) Custom カスタマイズ &Color theme: カラーテーマ(&C): Custom color palette theme カラーパレットテーマã®ã‚«ã‚¹ã‚¿ãƒžã‚¤ã‚º Wonton Soup DO NOT TRANSLATE KXStudio DO NOT TRANSLATE Custom widget style theme ウィジェットスタイルテーマã®ã‚«ã‚¹ã‚¿ãƒžã‚¤ã‚º Meters メーター &Audio: 音声(&A): Audio meter level 音声メーターレベル Over オーãƒãƒ¼ 0 dB 3 dB 6 dB 10 dB Audio meter color 音声メーターã®é…色 Select custom audio meter color 音声メーターã«ä»»æ„ã®é…è‰²ã‚’é¸æŠž ... &MIDI: MIDI meter level MIDIメーターレベル Peak ピーク MIDI meter color MIDIメーターé…色 Select custom MIDI meter color MIDIメーターã«ä»»æ„ã®é…è‰²ã‚’é¸æŠž Reset meter colors to default メーターã®é…色をデフォルトã«ãƒªã‚»ãƒƒãƒˆ &Reset リセット(&R) Messages メッセージ Sample messages text font display テキストフォントã®ã‚µãƒ³ãƒ—ル表示 Select font for the messages text display メッセージテキストã®ãƒ•ã‚©ãƒ³ãƒˆã‚’é¸æŠž &Font... フォント(&F)... Whether to keep a maximum number of lines in the messages window ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã®æœ€å¤§è¡Œæ•°ã‚’ç¶­æŒã™ã‚‹ã‹ã©ã†ã‹ M&essages limit: メッセージリミット(&E): The maximum number of message lines to keep in view ビューã«è¡¨ç¤ºã•ã‚Œã‚‹ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã®æœ€å¤§æ•° lines 行 Whether to hold auto-scrolling (follow play-head) on edits. 編集中ã«è‡ªå‹•スクロール (å†ç”Ÿä½ç½®ã«è¿½éš) ã™ã‚‹ã‹ã©ã†ã‹ã€‚ &Hold auto-scrolling (follow play-head) on edits 編集中ã¯è‡ªå‹•スクロール(å†ç”Ÿã®å…ˆé ­ã«è¿½éš)ã‚’æ­¢ã‚ã‚‹(&H) Trac&k color saturation: æ–°è¦ãƒˆãƒ©ãƒƒã‚¯ã®è‰²ã®å½©åº¦(&K): Default new track color saturation æ–°ã—ã„トラックã®è‰²ã®ãƒ‡ãƒ•ォルトã®å½©åº¦ % % Back ãƒãƒƒã‚¯ Logging ログ Messages log file メッセージログファイル Browse for the messages log file location メッセージログファイルã®å ´æ‰€ã‚’é¸æŠž Whether to activate a messages logging to file. メッセージã®ãƒ­ã‚°ã‚’ãƒ•ã‚¡ã‚¤ãƒ«ã«æ®‹ã™ã‹ã©ã†ã‹ã€‚ Messages &log file: メッセージログファイル(&L): &Plugins プラグイン(&P) Paths パス Plugin type プラグインã®ç¨®é¡ž Plugin path プラグインã®ãƒ‘ス Browse plugin path ãƒ—ãƒ©ã‚°ã‚¤ãƒ³ãƒ‘ã‚¹ã‚’é¸æŠž Add plugin path プラグインパスã®è¿½åŠ  &Add 追加(&A) Plugin paths プラグインパス Remove plugin path プラグインパスã®å‰Šé™¤ &Remove 削除(&R) Move up path パスを上ã«ç§»å‹• &Up 上ã¸(&U) &Down 下ã¸(&D) &LV2 Presets directory: LV2プリセットディレクトリー(&L): LV2 Presets directory (default: ~/.lv2) LV2プリセットディレクトリー(デフォルト: ~/.lv2) Browse LV2 Presets directory LV2プリセットディレクトリーã®é¸æŠž Instruments インストルメント Whether to have separate audio output ports 音声出力用ã«åˆ¥ãªãƒãƒ¼ãƒˆã‚’設ã‘ã‚‹ã‹ã©ã†ã‹ Dedicated audi&o outputs: 音声専用出力(&O): Whether to auto-connect dedicated audio output ports 音声専用出力ã«è‡ªå‹•接続ã™ã‚‹ã‹ã©ã†ã‹ Au&to-connect 自動接続(&T) Editor エディター Whether to open plugin's editor (GUI) by default デフォルトã§ãƒ—ラグインã®ã‚¨ãƒ‡ã‚£ã‚¿ãƒ¼(GUI)ã‚’é–‹ãã‹ã©ã†ã‹ Open plugin's &editor (GUI) by default プラグインã®ã‚¨ãƒ‡ã‚£ã‚¿ãƒ¼(GUI)をデフォルトã§é–‹ã(&E) &Select plugin's editor (GUI) if more than one are available プラグインã®ã‚¨ãƒ‡ã‚£ã‚¿ãŒè¤‡æ•°åˆ©ç”¨å¯èƒ½ãªã‚‰é¸æŠžã™ã‚‹(&S) XML Default (*.%1) XMLデフォルト (*.%1) XML Regular (*.%1) XMLレギュラー (*.%1) ZIP Archive (*.%1) ZIPアーカイブ (*.%1) (Any) (ä»»æ„) Warning 警告 Some settings have been changed. Do you want to apply the changes? 設定ã®ä¸€éƒ¨ãŒå¤‰æ›´ã•れã¾ã—㟠ã“ã®å¤‰æ›´ã‚’é©ç”¨ã—ã¾ã™ã‹? Metronome Bar Audio File メトロノームã®bar用音声ファイル Metronome Beat Audio File メトロノームã®Beat用ã®éŸ³å£°ãƒ•ァイル Open Style Sheet Style Sheet files (*.%1) Icons Theme Directory Audio Meter Color オーディオメーターã®è‰² MIDI Meter Color MIDIメーターã®è‰² Plug-in Directory プラグインディレクトリー LV2 Presets Directory LV2プリセットディレクトリー Plug-in Blacklist プラグインã®ãƒ–ラックリスト Plug-in files (*.%1) プラグインファイル (*.%1) Messages Font メッセージã®ãƒ•ォント Messages Log メッセージログ Log files (*.%1) ログファイル (*.%1) All files (*.*) 全ファイル (*.*) Session Template セッションã®ãƒ†ãƒ³ãƒ—レート Session template files (*.qtr *.qts *.%1) セッションã®ãƒ†ãƒ³ãƒ—レートファイル(*.qtr *.qts *.%1) qtractorPaletteForm Color Themes カラーテーマ Name åå‰ Current color palette name カスタムカラーパレットå Save ä¿å­˜ Save current color palette name カスタムカラーパレットåã‚’ä¿å­˜ã—ã¾ã™ Delete 削除 Delete current color palette name カスタムカラーパレットåを削除ã—ã¾ã™ Palette パレット Current color palette ç¾åœ¨ã®ã‚«ã‚¹ã‚¿ãƒ ã‚«ãƒ©ãƒ¼ãƒ‘レット Generate: 作æˆ: Base color to generate palette パレットを作æˆã™ã‚‹ãŸã‚ã®ãƒ™ãƒ¼ã‚¹ã‚«ãƒ©ãƒ¼ Reset リセット Reset all current palette colors ç¾åœ¨ã®ã‚«ãƒ©ãƒ¼ãƒ‘レットをã™ã¹ã¦ãƒªã‚»ãƒƒãƒˆã—ã¾ã™ Import a custom color theme (palette) from file ファイルã‹ã‚‰ã‚«ã‚¹ã‚¿ãƒ ã‚«ãƒ©ãƒ¼ãƒ†ãƒ¼ãƒž(パレット)をインãƒãƒ¼ãƒˆã—ã¾ã™ Import... インãƒãƒ¼ãƒˆ... Export a custom color theme (palette) to file ファイルã¸ã‚«ã‚¹ã‚¿ãƒ ã‚«ãƒ©ãƒ¼ãƒ†ãƒ¼ãƒž(パレット)をエクスãƒãƒ¼ãƒˆã—ã¾ã™ Export... エクスãƒãƒ¼ãƒˆ... Show Details 詳細ã®è¡¨ç¤º Import File - %1 インãƒãƒ¼ãƒˆãƒ•ァイル %1 Palette files (*.%1) パレットファイル (*.%1) Save Palette - %1 All files (*.*) 全ファイル (*.*) Warning - %1 警告 - %1 Could not import from file: %1 Sorry. ファイルã‹ã‚‰ã‚¤ãƒ³ãƒãƒ¼ãƒˆã§ãã¾ã›ã‚“ã§ã—ãŸ: %1 ã”ã‚ã‚“ãªã•ã„. Export File - %1 エクスãƒãƒ¼ãƒˆãƒ•ァイル - %1 Some settings have been changed. Do you want to discard the changes? 設定ãŒå¤‰æ›´ã«ãªã‚Šã¾ã—ãŸ. 変更を破棄ã—ã¾ã™ã‹? Some settings have been changed: "%1". Do you want to save the changes? 設定ãŒå¤‰æ›´ã«ãªã‚Šã¾ã—ãŸ: "%1". 変更をä¿å­˜ã—ã¾ã™ã‹? qtractorPaletteForm::PaletteModel Color Role カラーロール Active アクティブ Inactive インアクティブ Disabled 破棄 qtractorPasteRepeatForm Paste Repeat 繰り返ã—貼り付㑠Repeat 繰り返㙠&Count: 回数(&C): Repeat count 繰り返ã—回数 &Period: é•·ã•(&P): Repeat period 繰り返ã™é•·ã• Repeat period format 繰り返ã—é•·ã®ãƒ•ォーマット Frames フレーム Time 時間 BBT Warning 警告 Some settings have been changed. Do you want to apply the changes? 設定ã®ä¸€éƒ¨ãŒå¤‰æ›´ã•れã¾ã—㟠ã“ã®å¤‰æ›´ã‚’é©ç”¨ã—ã¾ã™ã‹? qtractorPluginForm Plugin Properties プラグインã®ãƒ—ロパティ Open preset プリセットを開ã Preset name プリセットå Save preset プリセットã®ä¿å­˜ Delete preset プリセットã®å‰Šé™¤ Alias: Plugin alias Edit plugin プラグインã®ç·¨é›† Edit 編集 Active アクティベート About 詳細 Outputs (Sends) 出力 (センド) Sends センド Inputs (Returns) インプット (リターン) Returns リターン Auto-connect Aux Send Bus: Auxセンドãƒã‚¹: Manage buses ãƒã‚¹ã®ç®¡ç† ... Audio bus I/O matrix I/O Matrix... Direct Access Parameter ダイレクトアクセスã®ãƒ‘ラメーター Direct Access ダイレクトアクセス Page %1 ページ %1 %1 [%2], %3 instance(s), %4 channel(s). %1 [%2], %3 インスタンス, %4 ãƒãƒ£ãƒ³ãƒãƒ«. (none) (ãªã—) Open Preset プリセットを開ã Preset files (*.%1) プリセットファイル (*.%1) All files (*.*) 全ファイル (*.*) Error エラー Preset could not be loaded from file: "%1". Sorry. プリセットをファイルã‹ã‚‰ã‚ーã©ã§ãã¾ã›ã‚“ã§ã—ãŸ: "%1". ã”ã‚ã‚“ãªã•ã„。 Save Preset プリセットã®ä¿å­˜ Preset could not be saved to file: "%1". Sorry. プリセットをファイルã«ä¿å­˜ã§ãã¾ã›ã‚“ã§ã—ãŸ: "%1". ã”ã‚ã‚“ãªã•ã„。 Warning 警告 About to delete preset: "%1" (%2) Are you sure? プリセットを削除ã—ã¾ã™: "%1" (%2) よã‚ã—ã„ã§ã™ã‹ï¼Ÿ Latency: %1 ms (%2 frames) é…å»¶: %1 ms (%2 フレーム) (no latency) (é…å»¶ãªã—) &None ãªã—(&N) qtractorPluginListView copy plugin プラグインをコピー activate all plugins ã™ã¹ã¦ã®ãƒ—ラグインを有効化 deactivate all plugins ã™ã¹ã¦ã®ãƒ—ラグインを無効化 remove all plugins ã™ã¹ã¦ã®ãƒ—ラグインを削除 Import Plugins プラグインã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ XML files (*.%1) XMLファイル (*.%1) All files (*.*) 全ファイル (*.*) Warning 警告 About to remove and import all plugins: "%1" Are you sure? 全プラグインを削除ã¾ãŸã¯ã‚¤ãƒ³ãƒãƒ¼ãƒˆã—ã¾ã™: "%1" よã‚ã—ã„ã§ã™ã‹? Export Plugins プラグインã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ Aux Send: &Move Here ã“ã“ã¸ç§»å‹•(&M) &Copy Here ã“ã“ã¸ã‚³ãƒ”ー(&C) C&ancel キャンセル(&A) &Add Plugin... プラグインを追加(&A)... I&nserts 挿入(&N) &Audio 音声(&A) Add &Insert インサートを追加(&I) Add &Aux Send Auxセンドを追加(&A) &Sends センド(&S) &Returns リターン(&R) &MIDI MIDI(&M) Add &Controller Ac&tivate アクティベート(&T) Acti&vate All ã™ã¹ã¦ã‚’有効化(&V) Deactivate Al&l ã™ã¹ã¦ã‚’無効化(&L) &Remove 削除(&R) Re&move All ã™ã¹ã¦ã‚’削除(&M) Move &Up 上ã«ç§»å‹•(&U) Move &Down 下ã«ç§»å‹•(&D) Pre&set プリセット(&S) Dire&ct Access ダイレクトアクセス(&C) &None ãªã—(&N) &Properties... プロパティ(&P)... &Edit 編集(&E) &Import... インãƒãƒ¼ãƒˆ(&I)... E&xport... エクスãƒãƒ¼ãƒˆ(&X)... &Outputs 出力(&O) &Dedicated 専用(&D) &Auto-connect 自動接続(&A) qtractorPluginParamWidget Open File ファイルを開ã qtractorPluginSelectForm Plugins プラグイン Reset filter フィルターã®ãƒªã‚»ãƒƒãƒˆ X Plugin search string (regular expression) プラグイン検索キーワード(正è¦è¡¨ç¾ã‚’使用å¯èƒ½ï¼‰ Plugin type プラグインã®ã‚¿ã‚¤ãƒ— Available plugins 利用å¯èƒ½ãªãƒ—ラグイン Name åå‰ Audio 音声 MIDI MIDI Control コントロール Modes モード Path パス Index インデックス Instances インスタンス Type タイプ Plugin scanning in progress... プラグインを検索中ã§ã™... Rescan for available plugins (refresh) 利用å¯èƒ½ãªãƒ—ãƒ©ã‚°ã‚¤ãƒ³ã‚’å†æ¤œç´¢ã—ã¾ã™(æ›´æ–°) &Rescan 冿¤œç´¢(&R) GUI EXT RT qtractorSessionForm Session セッション &Name: åå‰(&N): Session name セッションå &Directory: ディレクトリ(&D): Whether to auto-name the session directory セッションディレクトリåã‚’è‡ªå‹•ã§æ±ºå®šã™ã‚‹ã‹ã©ã†ã‹ &Auto 自動(&A) Session directory セッションã®ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒª Browse for session directory ã‚»ãƒƒã‚·ãƒ§ãƒ³ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’é¸æŠžã—ã¾ã™ ... &Description: 説明(&D): Session description セッションã®èª¬æ˜Ž Properties プロパティ Time 時間 Sample &Rate: サンプルレート(&R): Sample rate (Hz) サンプルレート(Hz) 44100 48000 96000 192000 &Tempo: テンãƒ(&T): Tempo (BPM) / Signature テンム(BPM) / æ‹å­è¨˜å· T&icks/Beat: T&icks/Beat: Resolution (ticks/beat; tpqn) è§£åƒåº¦ (ticks/beat; tpqn) View 表示 &Snap/Beat: スナップ/Beat(&S): Snap/beat スナップ/Beat &Pixels/Beat: ピクセル/Beat(&P): Pixels/beat ピクセル/Beat &Horizontal Zoom: 水平ズーム率(&H): Horizontal Zoom (%) 水平ズーム率 (%) % % &Vertical Zoom: 垂直ズーム率(&V): Vertical Zoom (%) 垂直ズーム率 (%) Warning 警告 Session directory does not exist: "%1" Do you want to create it? セッションディレクトリãŒã‚りã¾ã›ã‚“: "%1" ディレクトリを作æˆã—ã¾ã™ã‹ï¼Ÿ Some settings have been changed. Do you want to apply the changes? 設定ã®ä¸€éƒ¨ãŒå¤‰æ›´ã•れã¾ã—㟠ã“ã®å¤‰æ›´ã‚’é©ç”¨ã—ã¾ã™ã‹? Session Directory セッションディレクトリ qtractorShortcutForm Shortcuts ショートカット Shortcut search string (regular expression) Menu/Action メニュー/動作 Description 説明 Keyboard キーボード MIDI Controller MIDIコントローラー Search shortcuts Warning 警告 Keyboard shortcut (%1) already assigned (%2). キーボードショートカット(%1)ã¯æ—¢ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦ã„ã¾ã™(%2)。 Keyboard shortcuts have been changed. Do you want to apply the changes? キーボードショートカットãŒå¤‰æ›´ã•れã¾ã—㟠ã“ã®å¤‰æ›´ã‚’é©ç”¨ã—ã¾ã™ã‹? MIDI Controller shortcuts have been changed. Do you want to apply the changes? MIDIコントローラーã®ã‚·ãƒ§ãƒ¼ãƒˆã‚«ãƒƒãƒˆãŒå¤‰æ›´ã•れã¾ã—ãŸã€‚ ã“ã®å¤‰æ›´ã‚’é©ç”¨ã—ã¾ã™ã‹? &MIDI Controller... MIDIコントローラー(&M)... qtractorTakeRangeForm Range レンジ Selection range é¸æŠžãƒ¬ãƒ³ã‚¸ &Selection é¸æŠž(&S) Loop range ループレンジ &Loop ループ(&L) Punch range パンãƒãƒ¬ãƒ³ã‚¸ &Punch パンãƒ(&P) Time 時間 BBT Edit range 編集レンジ &Edit 編集(&E) Custom range ä»»æ„ã®ãƒ¬ãƒ³ã‚¸ &Custom カスタム(&C) St&art: é–‹å§‹(&A): Clip start クリップã®é–‹å§‹æ™‚é–“ Take Range テイクã®ãƒ¬ãƒ³ã‚¸ En&d: 終了(&D): Clip offset クリップã®ã‚ªãƒ•セット Select テイクã®é¸æŠž Current take ç¾åœ¨ã®ãƒ†ã‚¤ã‚¯ Format フォーマット Time display format 時間表示フォーマット Frames フレーム Take %1 テイク %1 qtractorTempoAdjustForm Tempo Adjust テンãƒã®èª¿æ•´ Metronome メトロノーム Tempo/Time signature テンム/ æ‹å­è¨˜å· &Detect 検出(&D) T&ap タップ(&A) Range レンジ &Start: é–‹å§‹(&S): Range start レンジã®é–‹å§‹ &Beats: ビート(&B): Time 時間 BBT &Length: é•·ã•(&L): &Tempo: テンãƒ(&T): R&eset Range length レンジã®é•·ã• Range beats レンジã®beat A&djust 調整(&D) Format フォーマット Time display format 時間表示フォーマット Frames フレーム Warning 警告 Some settings have been changed. Do you want to apply the changes? 設定ã®ä¸€éƒ¨ãŒå¤‰æ›´ã•れã¾ã—㟠ã“ã®å¤‰æ›´ã‚’é©ç”¨ã—ã¾ã™ã‹? qtractorThumbView Thumb view サムãƒã‚¤ãƒ«ãƒ“ュー qtractorTimeScale C B# C# Db D D# Eb E Fb F E# F# Gb G G# Ab A A# Bb B Cb qtractorTimeScaleForm Bar Bar Time 時間 Tempo テンム&Bar: &Bar: Tempo map / Markers テンãƒãƒžãƒƒãƒ— / マーカー Marker マーカー Bar location Bar ã®ãƒ­ã‚±ãƒ¼ã‚·ãƒ§ãƒ³ T&ime: 時間(&I): Time/frame location 時間/フレームã®ãƒ­ã‚±ãƒ¼ã‚·ãƒ§ãƒ³ &Tempo: テンãƒ(&T): T&ap タップ(&A) Marker text マーカーテキスト ... Marker color マーカーã®è‰² Tempo Map / Markers テンãƒãƒžãƒƒãƒ—/マーカー Key キー Tempo (BPM) / Time signature テンム(BPM) / æ‹å­è¨˜å· &Key signature: キー記å·(&K): Key signature (accidentals) キー記å·(臨時記å·) Key signature (mode) キー記å·(調å­) - Major メジャー Minor マイナー &Marker: マーカー(&M): Tempo &scale factor: テンãƒã‚¹ã‚±ãƒ¼ãƒ«ãƒ•ァクター(&S): Tempo scale factor テンãƒã‚¹ã‚±ãƒ¼ãƒ«ãƒ•ァクター App&ly é©ç”¨ (&L) Refresh tempo map テンãƒãƒžãƒƒãƒ—ã®æ›´æ–° Re&fresh æ›´æ–°(&F) Add node ノードを追加 &Add 追加(&A) Update node ノードをアップデート &Update アップデート(&U) Remove node ノードを削除 &Remove 削除(&R) Close this dialog ダイアログを閉ã˜ã¾ã™ Close é–‰ã˜ã‚‹ Warning 警告 Some settings have been changed. Do you want to apply the changes? 設定ã®ä¸€éƒ¨ãŒå¤‰æ›´ã•れã¾ã—㟠ã“ã®å¤‰æ›´ã‚’é©ç”¨ã—ã¾ã™ã‹? About to remove tempo node: %1 (%2) %3 %4/%5 Are you sure? テンãƒãƒŽãƒ¼ãƒ‰ã‚’削除ã—ã¾ã™: %1 (%2) %3 %4/%5% よã‚ã—ã„ã§ã™ã‹? Some settings have been changed. Do you want to discard the changes? 設定ã®ä¸€éƒ¨ãŒå¤‰æ›´ã•れã¾ã—㟠ã“ã®å¤‰æ›´ã‚’破棄ã—ã¾ã™ã‹? tempo factor テンãƒãƒ•ァクター Marker Color マーカーã®è‰² &Refresh æ›´æ–°(&R) qtractorTimeSpinBox &Frames フレーム(&F) &Time 時間(&T) &BBT qtractorTrackForm Track トラック &Name: åå‰(&N): Track name description トラックåã®èª¬æ˜Ž Track icon トラックã®ã‚¢ã‚¤ã‚³ãƒ³ Type 種類 Audio track type 音声トラックタイプ &Audio 音声(&A) MIDI track type MIDIトラックタイプ &MIDI MIDI(&M) Input / Output 入力 / 出力 Input bus name 入力ãƒã‚¹å Output bus name 出力ãƒã‚¹å Manage buses ãƒã‚¹ã®ç®¡ç† ... MIDI / Instrument MIDI/インストルメント &Program: プログラム(&P): &Bank: ãƒãƒ³ã‚¯(&B): Bank &Select Method: ãƒãƒ³ã‚¯ã‚»ãƒ¬ã‚¯ãƒˆæ–¹æ³•(&S): &Omni MIDI Omni: Capture All Channels MIDI Omni: å…¨ãƒãƒ£ãƒ³ãƒãƒ«ã‚’記録 &Channel: ãƒãƒ£ãƒ³ãƒãƒ«(&C): MIDI Channel (1-16) MIDIãƒãƒ£ãƒ³ãƒãƒ« (1-16) MIDI Patch: Instrument MIDIパッãƒ:インストルメント MIDI Patch: Bank Select Method MIDIパッãƒ:ãƒãƒ³ã‚¯ã‚»ãƒ¬ã‚¯ãƒˆæ–¹æ³• MIDI Patch: Drum Mode MIDI パッãƒ: ドラムモード MIDI Patch: Bank MIDIパッãƒ:ãƒãƒ³ã‚¯ MIDI Patch: Program MIDIパッãƒ:プログラム View / Colors 表示 / 色 &Foreground: 剿™¯(&F): Foreground color 剿™¯è‰² Select custom track foreground color ä»»æ„ã®ãƒˆãƒ©ãƒƒã‚¯å‰æ™¯è‰²ã‚’é¸æŠž Bac&kground: 背景(&K): Background color 背景色 Select custom track background color ä»»æ„ã®èƒŒæ™¯è‰²ã‚’é¸æŠž Auto Plugins プラグイン Track plugins トラックプラグイン Add plugin プラグインを追加 &Add... 追加(&A)... Remove plugin プラグインを削除 &Remove 削除(&R) Move plugin up プラグインを上ã¸ç§»å‹• &Up 上ã¸(&U) Move plugin down プラグインを下ã¸ç§»å‹• &Down 下ã¸(&D) Whether to enable plugin latency/delay compensation プラグインã®ãƒ¬ã‚¤ãƒ†ãƒ³ã‚·ãƒ¼/ディレイ補償を有効ã«ã™ã‚‹ã‹ã©ã†ã‹ &Latency compensation レイテンシー補償(&L) Current total latency Normal ノーマル Bank MSB ãƒãƒ³ã‚¯MSB Bank LSB ãƒãƒ³ã‚¯LSB Patch パッム&Drums ドラム(&D) Drum &Kit ドラムキット(&K) &Bass ãƒã‚¹(&B) A&coustic Bass アコースティックãƒã‚¹(&C) &Guitar ギター(&G) &Electric Guitar エレクトリックギター(&E) &Piano ピアノ(&P) &Acoustic Piano アコースティックピアノ(&A) &Microphone メトロノーム(&M) Vi&ntage Microphone ビンテージメトロノーム(&N) &Speaker スピーカー(&S) &Trumpet トランペット(&T) &Violin ãƒã‚¤ã‚ªãƒªãƒ³(&V) Warning 警告 Some settings have been changed. Do you want to apply the changes? 設定ã®ä¸€éƒ¨ãŒå¤‰æ›´ã•れã¾ã—㟠ã“ã®å¤‰æ›´ã‚’é©ç”¨ã—ã¾ã™ã‹? (No instrument) (インストルメントãªã—) %1 ms (%2 frames) (no latency) (é…å»¶ãªã—) (None) (ãªã—) Custom &Icon... 自å‰ã‚¢ã‚¤ã‚³ãƒ³(&I)... Image files (%1) ç”»åƒãƒ•ァイル (%1) All files (*.*) 全ファイル (*.*) Track Icon トラックアイコン Foreground Color 全景ã®è‰² Background Color 背景ã®è‰² qtractorTrackList Nr ç•ªå· Track Name トラックå Bus ãƒã‚¹ Ch ãƒãƒ£ãƒ³ãƒãƒ« Patch パッムInstrument インストルメント qtractorTrackTime Play-head å†ç”Ÿå…ˆé ­ Edit-head 編集先頭 Edit-tail 編集末尾 Loop-start ループスタート Loop-end ループエンド Punch-in パンãƒã‚¤ãƒ³ Punch-out パンãƒã‚¢ã‚¦ãƒˆ Start: %1 End: %2 Length: %3 é–‹å§‹: %1 終了: %2 é•·ã•: %3 qtractorTrackView Zoom in (horizontal) ズームイン(水平方å‘) Zoom out (horizontal) ズームアウト(水平方å‘) Zoom in (vertical) ズームイン(垂直方å‘) Zoom out (vertical) ズームアウト(垂直方å‘) Zoom reset ズームリセット add clip クリップを追加 Start: %1 End: %2 Length: %3 é–‹å§‹: %1 終了: %2 é•·ã•: %3 clip %1 クリップ %1 fade-in フェードイン fade-out フェードアウト clip stretch クリップã®ä¼¸é•· clip resize クリップリサイズ clip repeat クリップã®ãƒªãƒ”ート %1 automation %1 オートメーション %1 clip %1 クリップ move automation オートメーションã®ç§»å‹• paste automation オートメーションã®è²¼ã‚Šä»˜ã‘ cut 切りå–り delete 削除 split 分割 move clip クリップã®ç§»å‹• paste clip クリップã®è²¼ã‚Šä»˜ã‘ qtractorTracks Tracks トラック new clip æ–°è¦ã‚¯ãƒªãƒƒãƒ— mute clip split clip クリップã®åˆ†å‰² clip normalize クリップã®ãƒŽãƒ¼ãƒžãƒ©ã‚¤ã‚º quantize クォンタイズ transpose トランスãƒãƒ¼ã‚º normalize ノーマライズ randomize ランダマイズ resize リサイズ rescale リスケール timeshift タイムシフト clip import クリップã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ Audio file import "%1" on %2 %3. 音声ファイル "%1" ã‚’ %2 %3 ã¸ã‚¤ãƒ³ãƒãƒ¼ãƒˆã€‚ Audio file import: "%1". 音声ファイルã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ: "%1"。 MIDI file import "%1" track-channel %2 on %3 %4. MIDIファイル "%1" をトラックãƒãƒ£ãƒ³ãƒãƒ« %2 ã®%3 %4 ã¸ã‚¤ãƒ³ãƒãƒ¼ãƒˆã€‚ MIDI file import: "%1", track-channel: %2. MIDIファイルã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ: "%1", トラックãƒãƒ£ãƒ³ãƒãƒ«: %2。 clip merge クリップã®ãƒžãƒ¼ã‚¸ Merge/Export マージ/エクスãƒãƒ¼ãƒˆ Merge/Export Audio Clip 音声クリップã®ãƒžãƒ¼ã‚¸/エクスãƒãƒ¼ãƒˆ MIDI files (*.mid *.smf *.midi) MIDIファイル (*.mid *.smf *.midi) All files (*.*) 全ファイル (*.*) Audio clip merge/export: "%1" started... 音声クリップã®ãƒžãƒ¼ã‚¸/エクスãƒãƒ¼ãƒˆ: "%1" ã‚’é–‹å§‹... tempo ramp Audio clip merge/export: "%1" complete. 音声クリップã®ãƒžãƒ¼ã‚¸/エクスãƒãƒ¼ãƒˆ: "%1" 完了。 Merge/Export MIDI Clip MIDIクリップをマージ/エクスãƒãƒ¼ãƒˆ MIDI clip merge/export: "%1" started... MIDIクリップã®ãƒžãƒ¼ã‚¸/エクスãƒãƒ¼ãƒˆ: "%1" é–‹å§‹... MIDI clip merge/export: "%1" complete. MIDIクリップã®ãƒžãƒ¼ã‚¸/エクスãƒãƒ¼ãƒˆ: "%1" 完了。 clip cross-fade クリップã®ã‚¯ãƒ­ã‚¹ãƒ•ェード Insert Range ãƒ¬ãƒ³ã‚¸ã®æŒ¿å…¥ insert range ãƒ¬ãƒ³ã‚¸ã®æŒ¿å…¥ insert track range ãƒˆãƒ©ãƒƒã‚¯ãƒ¬ãƒ³ã‚¸ã®æŒ¿å…¥ Remove Range レンジã®å‰Šé™¤ remove range レンジを削除ã—ã¾ã™ remove track range トラックレンジを削除ã—ã¾ã™ Warning 警告 About to remove track: "%1" Are you sure? トラックを削除ã—ã¾ã™: "%1" よã‚ã—ã„ã§ã™ã‹? MIDI file import "%1" on %2 %3. MIDIファイル "%1" ã‚’ %2 %3 ã¸ã‚¤ãƒ³ãƒãƒ¼ãƒˆã€‚ MIDI file import: "%1". MIDIファイルをインãƒãƒ¼ãƒˆ: "%1"。 qtractor-1.5.9/src/translations/PaxHeaders/qtractor_uk.ts0000644000000000000000000000013215101070305020651 xustar0030 mtime=1761898693.107267718 30 atime=1761898693.105267712 30 ctime=1761898693.107267718 qtractor-1.5.9/src/translations/qtractor_uk.ts0000644000175000001440000243262615101070305020660 0ustar00rncbcusers QObject Audio: %1 channels, %2 Hz Звук: %1 каналів, %2 Гц (%1 dB) (%1 дБ) (%1 pan) (Ð¿Ð°Ð½Ð¾Ñ€Ð°Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ %1) (%1% time stretch) (%1% розтÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу) (%1 semitones pitch shift) (зÑÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð¾Ð½Ñƒ півтонів %1) %1 In Вхід %1 %1 Gain ПідÑÐ¸Ð»ÐµÐ½Ð½Ñ %1 %1 Pan ÐŸÐ°Ð½Ð¾Ñ€Ð°Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ %1 %1 Out Вихід %1 Audio files (%1) звукові файли (%1) All files (*.*) уÑÑ– файли (*.*) %1 (%2) %3 channels, %4 frames, %5 Hz %6 %1 (%2) %3 каналів, %4 кадрів, %5 Гц %6 Duplex Двобічний Output Вихід Input Вхід None Ðемає Name: Ðазва: Version: ВерÑÑ–Ñ: Vendor: Виробник: Manual: Підручник: Support: Підтримка: (take %1/%2) (дубль %1 з %2) [Mute] [Звук вимкнено] Name: %1 Ðазва: %1 Start: %1 Offset: %2 End: %3 Length: %4 Початок: %1 ЗміщеннÑ: %2 Кінець: %3 ТриваліÑть: %4 File: %1 Файл: %1 take %1 дубль %1 reset takes Ñкинути дублі clip save Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñƒ clip unlink від'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñƒ clip tool %1 інÑтрумент кліпу %1 clip record Ð·Ð°Ð¿Ð¸Ñ ÐºÐ»Ñ–Ð¿Ñƒ Signed 16-Bit Зі знаком 16 біт Signed 24-Bit Зі знаком 24 біт Signed 32-Bit Зі знаком 32 біт Float 32-Bit ДійÑне 32-бітове Float 64-Bit ДійÑне 64-бітове SMF Format 0 Формат SMF 0 SMF Format 1 Формат SMF 1 automation select позначене автоматизації automation mode режим автоматизації automation play Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ— automation record Ð·Ð°Ð¿Ð¸Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ— automation logarithmic логарифмічне автоматизації automation color колір автоматизації automation play all Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÑƒÑього автоматизації automation record all Ð·Ð°Ð¿Ð¸Ñ ÑƒÑього автоматизації automation edit Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ— automation clear Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ— automation clear all Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ ÑƒÑього автоматизації automation edit list Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ ÑпиÑку автоматизації %1: Automation/curve file not found. %1: не знайдено файла автоматизації/кривої. %1 Monitor Монітор %1 create bus Ñтворити шину update bus оновити шину delete bus вилучити шину move bus переÑунути шину bus pass-through пропуÑÐºÐ°Ð½Ð½Ñ ÑˆÐ¸Ð½Ð¸ bus gain підÑÐ¸Ð»ÐµÐ½Ð½Ñ ÑˆÐ¸Ð½Ð¸ bus pan Ð¿Ð°Ð½Ð¾Ñ€Ð°Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ¸Ð½Ð¸ Insert Send/Return pseudo-plugin (Audio) Ð’Ñтавити пÑевдододаток ÐадÑиланнÑ/ÐŸÐ¾Ð²ÐµÑ€Ð½ÐµÐ½Ð½Ñ (звук) Insert Send/Return pseudo-plugin (MIDI) Ð’Ñтавити пÑевдододаток ÐадÑиланнÑ/ÐŸÐ¾Ð²ÐµÑ€Ð½ÐµÐ½Ð½Ñ (MIDI) Send Gain ПідÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð°Ð´ÑÐ¸Ð»Ð°Ð½Ð½Ñ Dry Gain ПідÑÐ¸Ð»ÐµÐ½Ð½Ñ Ð²Ð¸Ñоких чаÑтот Wet Gain ПідÑÐ¸Ð»ÐµÐ½Ð½Ñ Ð½Ð¸Ð·ÑŒÐºÐ¸Ñ… чаÑтот Aux Send (Audio) ÐадÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð¾Ð¿Ð¾Ð¼Ñ–Ð¶Ð½Ð¾Ð³Ð¾ (звук) Aux Send pseudo-plugin (Audio) ПÑевдододаток надÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð¾Ð¿Ð¾Ð¼Ñ–Ð¶Ð½Ð¾Ð³Ð¾ (звук) Aux Send pseudo-plugin (MIDI) ПÑевдододаток надÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð¾Ð¿Ð¾Ð¼Ñ–Ð¶Ð½Ð¾Ð³Ð¾ (MIDI) (none) (немає) %1 (Audio) %1 (звук) %1 (MIDI) %1 (MIDI) Cakewalk Instrument Definition File Файл Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ–Ð½Ñтрумента Cakewalk File Файл Date Дата %1 Bank %2 Банк %1 %2 Author: Ðвтор: Copyright: ÐвторÑькі права: Project: Проєкт: Select plug-in's editor (GUI): Виберіть редактор додатка (інтерфейÑ): External Зовнішній X11 X11 X11 (native) X11 (природний) Gtk2 Gtk2 (native) Gtk2 (природний) Qt4 Qt4 Qt5 Qt5 Other Інше Don't ask this again Більше не питати plugin parameters параметри додатка Open File lv2_ui_request_parameter Відкрити файл (format %1) MIDI: (формат %1) MIDI: Channel %1 Канал %1 Track %1 Доріжка %1 , %1 tracks, %2 tpqn , %1 доріжок, %2 tpqn (%1% vol) (гучн. %1%) MIDI file save: "%1", track-channel: %2. Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð° MIDI: «%1», доріжка-канал: %2. set controller вÑтановити контролер reset controller Ñкинути контролер step input кроковий вхід overdub Ð¿ÐµÑ€ÐµÐºÑ€Ð¸Ñ‚Ñ‚Ñ %1 Volume ГучніÑть %1 %1 (format %2) %3 tracks, %4 tpqn %5 %1 (формат %2) %3 доріжок, %4 tpqn %5 %1 (format %2) %3 %1 (формат %2) %3 %1 - Bank %2 %1 - банк %2 (default) (default) %1 Hz %1 Гц slave підлеглий %1 (%2) %1 (%2) Usage: %1 [options] [session-file] КориÑтуваннÑ: %1 [параметри] [файл ÑеанÑу] Options: Параметри: Set session identification (uuid) Ð’Ñтановити ідентифікацію ÑеанÑу (uuid) Show help about command line options Показати довідку щодо параметрів командного Ñ€Ñдка Show version information Показати інформацію про верÑÑ–ÑŽ Session file (.qtr) Файл ÑеанÑу (.qtr) [session-file] [файл-ÑеанÑу] Option -s requires an argument (uuid). Параметр -s потребує аргументу (uuid). (Any) (Будь-Ñкий) Activate ЗадіÑти Aux Send: %1 ÐадÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð¾Ð¿Ð¾Ð¼Ñ–Ð¶Ð½Ð¾Ð³Ð¾: %1 %1(%2): %3 plugin not found. %1(%2): не знайдено додаток %3. add plugin додати додаток add insert додати вÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ add aux-send додати надÑÐ¸Ð»Ð°Ð½Ð½Ñ aux add MIDI controller додати MIDI-контролер aux-send bus шина надÑÐ¸Ð»Ð°Ð½Ð½Ñ aux aux-send matrix Ð¼Ð°Ñ‚Ñ€Ð¸Ñ†Ñ Ð½Ð°Ð´ÑÐ¸Ð»Ð°Ð½Ð½Ñ aux remove plugin вилучити додаток move plugin переÑунути додаток activate plugin задіÑти додаток preset plugin додаток набору reset plugin Ñкинути додаток plugin program програма додатка plugin alias альтернативна назва додатка dedicated audio outputs визначені виходи звуку direct access param параметр безпоÑереднього доÑтупу import plugins імпортувати додатки session loop цикл ÑеанÑу session punch врізка ÑеанÑу session properties влаÑтивоÑті ÑеанÑу Beat Біт add tempo node додати вузол ритму update tempo node оновити вузол ритму remove tempo node вилучити вузол ритму move tempo node переÑунути вузол ритму add marker додати позначку update marker оновити позначку remove marker вилучити позначку add key signature додати Ð¿Ñ–Ð´Ð¿Ð¸Ñ ÐºÐ»ÑŽÑ‡Ð° update key signature оновити Ð¿Ñ–Ð´Ð¿Ð¸Ñ ÐºÐ»ÑŽÑ‡Ð° remove key signature вилучити Ð¿Ñ–Ð´Ð¿Ð¸Ñ ÐºÐ»ÑŽÑ‡Ð° move marker переÑунути позначку change time-sig. змінити чаÑовий підпиÑ. add track додати доріжку remove track вилучити доріжку duplicate track здублювати доріжку move track переÑунути доріжку resize track змінити розмір доріжки import track імпортувати доріжку track properties влаÑтивоÑті доріжки Track assignment failed: Track: "%1" Input: "%2" Output: "%3" Ðе вдалоÑÑ Ð¿Ð¾Ð²'Ñзати доріжку: Доріжка: «%1» вхід: «%2» вихід: «%3» track record доріжка, Ð·Ð°Ð¿Ð¸Ñ track mute доріжка, вимкнути звук track solo доріжка, Ñоло track monitor доріжка, монітор track gain доріжка, підÑÐ¸Ð»ÐµÐ½Ð½Ñ track pan доріжка, Ð¿Ð°Ð½Ð¾Ñ€Ð°Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ track instrument доріжка, інÑтрумент Automation (%1) ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ (%1) none немає Automation ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ Unknown Ðевідомо Category: КатегоріÑ: Categories: Категорії: Product: Продукт: %1 (*.%2) %1 (*.%2) %1 Record Ð—Ð°Ð¿Ð¸Ñ %1 %1 Mute Ð’Ð¸Ð¼Ð¸ÐºÐ°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÑƒ %1 %1 Solo Соло %1 MIDI Controller: %1, %2, %3 MIDI-контролер: %1, %2, %3 Control (MIDI) ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ (MIDI) MIDI Controller Send pseudo-plugin ПÑевдододаток надиÑÐ»Ð°Ð½Ð½Ñ MIDI-контролера Value Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ qtractorAudioIOMatrixForm Aux-Send I/O Matrix ÐœÐ°Ñ‚Ñ€Ð¸Ñ†Ñ Ð½Ð°Ð´ÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð²Ð²ÐµÐ´ÐµÐ½Ð½Ñ-Ð²Ð¸Ð²ÐµÐ´ÐµÐ½Ð½Ñ Aux Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Some settings have been changed. Do you want to apply the changes? До деÑких параметрів було внеÑено зміни. Хочете заÑтоÑувати ці зміни? qtractorAudioListView Name Ðазва Ch Кан Frames Кадри Rate ЧаÑтота Time Ð§Ð°Ñ Path ШлÑÑ… Open Audio Files Ð’Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¸Ñ… файлів %1: Audio file not found. %1: звуковий файл не знайдено. qtractorAudioMixerMeter Gain (dB) ПідÑÐ¸Ð»ÐµÐ½Ð½Ñ (дБ) dB дБ Pan: %1 ПанорамуваннÑ: %1 Gain: %1 dB ПідÑил.: %1 дБ qtractorBusForm Buses ÐвтобуÑи Bus list СпиÑок шин Ch Кан Mode Режим Bus Шина Properties ВлаÑтивоÑті &Name: &Ðазва: Bus name Ðазва каналу &Mode: &Режим: Bus mode Режим шини Input Вхід Output Вихід Duplex Двобічний Bus monitor (pass-through) Монітор шини (пропуÑканнÑ) M&onitor (pass-through) М&онітор (пропуÑканнÑ) Audio Звук Cha&nnels: &Канали: Audio channels Звукові канали Audio auto-connect Ðвтоз'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÑƒ &Auto connect &Ðвтоз'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ MIDI MIDI MIDI Instrument name Ðазва інÑтрумента MIDI MIDI SysEx setup ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ SysEx MIDI SysE&x... SysE&x... Input Plugins Додатки входу Input bus plugins Додатки шини входу Add input plugin Додати додаток входу &Add... &Додати… Remove input plugin Вилучити додаток входу &Remove Ви&лучити Move input plugin up ПереÑунути додаток входу вище &Up &Вище Move input plugin down ПереÑунути додаток входу нижче &Down &Ðижче Output Plugins Додатки виводу Output bus plugins Додатки шини виходу Add output plugin Додати додаток виходу Remove output plugin Вилучити додаток виходу Move output plugin up ПереÑунути додаток виходу вище Move output plugin down ПереÑунути додаток виходу нижче Move bus up towards the top ПереÑунути шину вище U&p Ð’&ище Move bus down towards the bottom ПереÑунути шину нижче Do&wn Ðи&жче Create bus Створити шину &Create С&творити Update bus Оновити шину &Update &Оновити Delete bus Вилучити шину &Delete &Вилучити Close this dialog Закрити це вікно Close Закрити Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Some settings have been changed. Do you want to apply the changes? До деÑких параметрів було внеÑено зміни. Хочете заÑтоÑувати ці зміни? About to remove bus: "%1" (%2) Are you sure? Ðаказано вилучити шину: "%1" (%2) Ви Ñправді цього хочете? Some settings have been changed. Do you want to discard the changes? Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´ÐµÑких параметрів було змінено. Хочете відкинути внеÑені зміни? Move &Up ПереÑунути &вище Move &Down ПереÑунути &нижче (No instrument) (Ðемає інÑтрумента) (none) (немає) (1 item) (1 запиÑ) (%1 items) (%1 запиÑів) qtractorClientListView Readable Clients / Output Ports Придатні до Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð¿Ð¾Ñ€Ñ‚Ð¸ клієнтів/Ð²Ð¸Ð²ÐµÐ´ÐµÐ½Ð½Ñ Writable Clients / Input Ports Придатні до запиÑу клієнти та вхідні порти qtractorClipForm Clip Кліп &Name: &Ðазва: Clip name Ðазва кліпу &File: &Файл: Clip filename Ðазва файла кліпу Browse for clip file Вказати файл кліпу Track/&Channel: Доріжка/&Канал: Clip track/channel Доріжка або канал кліпу Parameters Параметри &Start: &Початок: Clip start Початок кліпу &Gain: П&ідÑиленнÑ: Clip gain/volume ПідÑÐ¸Ð»ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ гучніÑть кліпу Offs&et: З&міщеннÑ: Clip offset Ð—Ð¼Ñ–Ñ‰ÐµÐ½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñƒ &Panning: Па&норамуваннÑ: &Length: &ТриваліÑть: Clip length ТриваліÑть Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñƒ Forma&t: &Формат: Time display format Формат показу чаÑу Frames Кадри Time Ð§Ð°Ñ BBT BBT Fade In/Out ÐароÑтаннÑ/Ð¡Ð¿Ð°Ð´Ð°Ð½Ð½Ñ Fade &In: &ÐароÑтаннÑ: Clip fade-in length ТриваліÑть нароÑÑ‚Ð°Ð½Ð½Ñ Ð³ÑƒÑ‡Ð½Ð¾Ñті кліпу Clip fade-in type Тип нароÑÑ‚Ð°Ð½Ð½Ñ Ð³ÑƒÑ‡Ð½Ð¾Ñті кліпу Fade &Out: З&гаÑаннÑ: Clip fade-out length ТриваліÑть згаÑÐ°Ð½Ð½Ñ Ð³ÑƒÑ‡Ð½Ð¾Ñті кліпу Clip fade-out type Тип згаÑÐ°Ð½Ð½Ñ Ð³ÑƒÑ‡Ð½Ð¾Ñті кліпу Audio Звук Ti&me Stretch: &РозтÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу: Clip time-stretch percentage ЧаÑтка розтÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу кліпу % % Pitch S&hift: ЗÑ&ув тону: Clip pitch-shift in semitones ЗÑув тону кліпу у напівтонах semitones напівтонів Whether to use WSOLA time-stretching Визначає, чи Ñлід викориÑтовувати розтÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу WSOLA &WSOLA time-stretching РозтÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу &WSOLA Whether to use RubberBand formant preserve Визначає, чи Ñлід викориÑтовувати Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñ„Ð¾Ñ€Ð¼Ð°Ð½Ñ‚Ð¸ RubberBand RubberBand &formant preserve Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ &форманти RubberBand Whether to apply WSOLA quick seek time-stretching Визначає, чи Ñлід заÑтоÑовувати розтÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñту при швидкому позиціюванні WSOLA WSOLA quic&k seek &Швидке Ð¿Ð¾Ð·Ð¸Ñ†Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ WSOLA Whether to use RubberBand R3 finer engine Визначає, чи Ñлід викориÑтовувати точний рушій R3 RubberBand RubberBand R&3 finer engine Точний рушій R&3 RubberBand &Mute &Вимкнути звук Linear Лінійне Quadratic 1 Квадратичне 1 Quadratic 2 Квадратичне 2 Quadratic 3 Квадратичне 3 Cubic 1 Кубічне 1 Cubic 2 Кубічне 2 Cubic 3 Кубічне 3 dB дБ &Volume: &ГучніÑть: new clip новий кліп edit clip змінити кліп Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Some settings have been changed. Do you want to apply the changes? До деÑких параметрів було внеÑено зміни. Хочете заÑтоÑувати ці зміни? MIDI MIDI MIDI files (*.%1 *.smf *.midi) файли MIDI (*.%1 *.smf *.midi) All files (*.*) уÑÑ– файли (*.*) %1 Clip File Файл кліпу %1 qtractorConnect Connect З'єднати Disconnect Від’єднати Disconnect All Роз'єднати уÑÑ– Refresh Оновити qtractorConnectForm Connections З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Audio Звук Select output client/ports Вибрати клієнт/порти Ð²Ð¸Ð²ÐµÐ´ÐµÐ½Ð½Ñ Select input client/ports Вибрати клієнт/порти входу Connect currently selected ports З'єднати поточні позначені порти &Connect &З'єднати Disconnect currently selected ports Роз'єднати поточні позначені порти &Disconnect Роз'Ñ”&днати Disconnect all currently connected ports Роз'єднати уÑÑ– поточні з'єднані порти Disconnect &All Роз'Ñ”&днати вÑе Refresh current connections view ОÑвіжити вміÑÑ‚ панелі поточних з'єднань &Refresh &Оновити MIDI MIDI (All) (УÑÑ–) qtractorConnections Connections З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ qtractorEditRangeForm Edit Range Ð ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ñ–Ð°Ð¿Ð°Ð·Ð¾Ð½Ñƒ Range Діапазон Selection range Діапазон Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ &Selection &ÐŸÐ¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Loop range Діапазон циклу &Loop &Зациклити Punch range Діапазон врізки &Punch Ð’&різка Edit range Змінити діапазон &Edit З&міни Custom range Ðетиповий діапазон &Custom Ð&етиповий St&art: &Початок: Clip start Початок кліпу En&d: &Кінець: Clip offset Ð—Ð¼Ñ–Ñ‰ÐµÐ½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñƒ Options Параметри Apply to clips in range ЗаÑтоÑувати до кліпів у діапазоні Cl&ips &Кліпи Apply to Automation nodes in range ЗаÑтоÑувати до автоматизації вузли у діапазоні A&utomation &ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ 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 BBT qtractorExportClipForm %1 %2 Clips Кліпи %1 %2 qtractorExportForm Export ЕкÑпорт &File: &Файл: Export file name Ðазва файла Ð´Ð»Ñ ÐµÐºÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Browse export file name Вказати назву файла Ð´Ð»Ñ ÐµÐºÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ File &type: &Тип файла: Audio file type to use on export Тип звукового файла, Ñким Ñлід ÑкориÑтатиÑÑ Ð¿Ñ€Ð¸ екÑпортуванні Sample &format: &Формат диÑкретизації: Audio sample format to use on export Формат звукових Ñемплів, Ñким Ñлід ÑкориÑтатиÑÑ Ð¿Ñ€Ð¸ екÑпортуванні &Quality: &ЯкіÑть: Audio compression quality to use on export ЯкіÑть ÑтиÑÐºÐ°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¸Ñ… даних, Ñкою Ñлід ÑкориÑтатиÑÑ Ð¿Ñ€Ð¸ екÑпортуванні File &format: Фор&мат файла: MIDI file format to use on export Формат файла MIDI, Ñким Ñлід ÑкориÑтатиÑÑ Ð¿Ñ€Ð¸ екÑпортуванні Range Діапазон Session range Діапазон ÑеанÑу &Session &Ð¡ÐµÐ°Ð½Ñ Loop range Діапазон циклу &Loop &Зациклити Punch range Діапазон врізки &Punch Ð’&різка Edit range Змінити діапазон &Edit З&міни Custom range Ðетиповий діапазон &Custom Ð&етиповий St&art: &Початок: Custom start Ðетиповий початок En&d: &Кінець: Custom end Ðетиповий кінець Outputs Виходи Output bus names Ðазви шини виходу Format Формат Time display format Формат показу чаÑу Frames Кадри Time Ð§Ð°Ñ BBT BBT Whether to add/import new track(s) with export result Визначає, чи Ñлід додавати/імпортувати нові доріжки із результатом екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ &Add new track(s) &Додати нові доріжки Audio Звук MIDI MIDI Export %1 File ЕкÑпорт файла %1 MIDI files (*.%1 *.smf *.midi) файли MIDI (*.%1 *.smf *.midi) All files (*.*) уÑÑ– файли (*.*) qtractorExportTrackForm %1 %2 Tracks Доріжки %1 %2 Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ The file already exists: "%1" Do you want to replace it? Файл з такою назвою вже Ñ–Ñнує. «%1» Хочете замінити його? Audio file export: "%1" started... ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¾Ð³Ð¾ файла: розпочато «%1»… Audio file export: "%1" complete. ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¾Ð³Ð¾ файла: завершено «%1». Audio file export: "%1" failed. ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¾Ð³Ð¾ файла: «%1» помилка. MIDI file export: "%1" started... ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð° MIDI: розпочато «%1»... MIDI file export: "%1" complete. ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð° MIDI: завершено «%1». MIDI file export: "%1" failed. ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð° MIDI: «%1» помилка. qtractorFileListView New Group Ðова група Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ About to remove %1 file item(s). Are you sure? Ðаказано вилучити %1 запиÑів файлів. Ви впевнені? About to remove %1 item: "%2" Are you sure? Ðаказано вилучити %1 запиÑ: «%2» Ви Ñправді хочете це зробити? group група file файл qtractorFileSystem &Home &Домівка &Up &Вище Al&l Files &УÑÑ– файли &Session &Ð¡ÐµÐ°Ð½Ñ &Audio Зву&к &MIDI &MIDI H&idden При&ховано &Play Ð’Ñ–&дтворити File System Файлова ÑиÑтема qtractorFiles Audio Звук MIDI MIDI Play file Відтворити файл New &Group... С&творити групу… Add &Files... &Додати файли… Cu&t Виріза&ти &Copy &Копіювати &Paste &Ð’Ñтавити Re&name Пере&йменувати &Remove Ви&лучити Pla&y Від&творити Cl&eanup О&чиÑтити Ctrl+X Ctrl+X Ctrl+C Ctrl+C Ctrl+V Ctrl+V Del Del Files Файли MIDI Files файли MIDI Audio Files файли звукових даних qtractorInstrumentForm Instruments ІнÑтрументи Files Файли Path ШлÑÑ… 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 &Ðижче Export to instrument file ЕкÑпортувати до файла інÑтрумента E&xport... Е&кÑпортувати… Close this dialog Закрити це вікно Close Закрити Import Instrument Files Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð² інÑтрументів Instrument files (*.%1 *.sf2 *.sf3 *.midnam) файли інÑтрументів (*.%1 *.sf2 *.sf3 *.midnam) All files (*.*) уÑÑ– файли (*.*) Export Instrument File ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð° інÑтрумента Instrument files (*.%1) файли інÑтрументів (*.%1) Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ The instrument file already exists: "%1" Do you want to replace it? Файл інÑтрумента вже Ñ–Ñнує: «%1» Хочете замінити його? Instrument settings have been changed. Do you want to apply the changes? Параметри інÑтрумента було змінено. Хочете заÑтоÑувати зміни? Patch Names for Banks Ðазви латок Ð´Ð»Ñ Ð±Ð°Ð½ÐºÑ–Ð² Controller Names = %1 Ðазви контролерів = %1 RPN Names = %1 Ðазви RPN = %1 NRPN Names = %1 Ðазви NRPN = %1 Bank Select Method = %1 Метод вибору банків = %1 Patch Names Ðазви латок Note Names Ðазви нот Controller Names Ðазви контролерів RPN Names Ðазви RPN NRPN Names Ðазви NRPN Bank Select Methods Методи вибору банків %1 = %2 %1 = %2 Based On = %1 ОÑнова = %1 Normal Звичайний Bank MSB MSB банку Bank LSB LSB банку Patch Латка Unknown Ðевідомо qtractorInstrumentMenu (None) (Ðемає) qtractorMainForm &File &Файл Open &Recent Відкрити &недавні &Track Д&оріжка &State С&тан &Navigate &ÐÐ°Ð²Ñ–Ð³Ð°Ñ†Ñ–Ñ Mo&ve П&ереÑунути &Height &ВиÑота Impor&t Tracks І&мпортувати доріжки E&xport Tracks Е&кÑпортувати доріжки Instrum&ent І&нÑтрумент A&utomation &ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ &Select Поз&Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ M&ode &Режим &Clip &Кліп T&ools &ІнÑтрументи Ta&ke Д&убль &Edit З&міни Select &Mode Режим поз&Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ I&nsert Ð’Ñ&тавити Remo&ve Ви&лучити &View П&ереглÑд &Toolbars П&анелі інÑтрументів &Windows &Вікна &Zoom &МаÑштаб S&nap П&Ñ€Ð¸Ð»Ð¸Ð¿Ð°Ð½Ð½Ñ T&ransport &ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Mo&de &Режим St&ep &Крок &Help &Довідка &New С&творити New Створити New session Ðовий ÑÐµÐ°Ð½Ñ New session file Ðовий файл ÑеанÑу Ctrl+N Ctrl+N &Open... Від&крити… Open Відкрити Open session Відкрити ÑÐµÐ°Ð½Ñ Open session from file Відкрити ÑÐµÐ°Ð½Ñ Ð· файла Ctrl+O Ctrl+O &Save &Зберегти Save Зберегти Save session Зберегти ÑÐµÐ°Ð½Ñ Save session to file Зберегти ÑÐµÐ°Ð½Ñ Ð´Ð¾ файла Ctrl+S Ctrl+S Save &As... Зберегти &Ñк… Save As Зберегти Ñк Save as Зберегти Ñк Save current session with another file name Зберегти поточний ÑÐµÐ°Ð½Ñ Ñƒ файлі з іншою назвою &Properties... Вл&аÑтивоÑті… Session Properties ВлаÑтивоÑті ÑеанÑу Session properties ВлаÑтивоÑті ÑеанÑу Edit current session properties Змінити влаÑтивоÑті поточного ÑеанÑу F2 F2 E&xit Ви&йти Exit Вийти Exit this application program Вийти з цієї програми &Undo &Вернути Undo СкаÑувати Undo last action СкаÑувати оÑтанню дію Ctrl+Z Ctrl+Z &Redo &Повторити Redo Повторити Redo last action Повторити оÑтанню дію Ctrl+Shift+Z Ctrl+Shift+Z Cu&t Виріза&ти Cut Вирізати Cut selection to clipboard Вирізати позначені об’єкти до буфера обміну даними Ctrl+X Ctrl+X &Copy &Копіювати Copy Копіювати Copy selection to clipboard Копіювати позначений фрагмент до буфера обміну даними Ctrl+C Ctrl+C &Paste &Ð’Ñтавити Paste Ð’Ñтавити Paste clipboard contents Ð’Ñтавити вміÑÑ‚ буфера обміну даними Ctrl+V Ctrl+V Past&e Repeat... Ð’Ñтавити повто&реннÑ… Paste Repeat Ð’Ñтавити Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ Paste repeat Ð’Ñтавити Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ Paste/repeat clipboard contents Ð’Ñтавити/Повторити вміÑÑ‚ буфера обміну даними Ctrl+Shift+V Ctrl+Shift+V &Delete Ви&лучити Delete Вилучити Delete selection Вилучити позначене Del Del Clip Кліп Select clip Позначити кліп Clip selection mode Режим Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñƒ &Range Ді&апазон Range Діапазон Select range ÐŸÐ¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ñ–Ð°Ð¿Ð°Ð·Ð¾Ð½Ñƒ Range selection mode Режим вибору діапазону R&ectangle ПрÑмо&кутник Rect ПрÑмокутник Select rectangle Позначити прÑмокутну ділÑнку Rectangular selection mode Режим Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¿Ñ€Ñмокутником &Automation &ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ Automation ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ Automation edit mode Режим автоматизованого Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ &All &Ð’ÑÑ– Select All Позначити вÑе Select all Позначити вÑе Mark all as selected Позначити уÑе Ctrl+A Ctrl+A &None &Ðемає Select None ЗнÑти Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Select none ЗнÑти Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Mark all as unselected ЗнÑти Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð· уÑього Ctrl+Shift+A Ctrl+Shift+A &Invert &Інвертувати Select Invert Інвертувати Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Select invert Інвертувати Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Invert selection Інвертувати Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ctrl+I Ctrl+I Select Track Позначити доріжку Select track Позначити доріжку Mark track as selected Позначити доріжку Ctrl+T Ctrl+T Trac&k Range Діапазон дорі&жки Select Track Range Позначити діапазон доріжки Select track range Позначити діапазон доріжки Mark track range as selected Позначити діапазон доріжки Ctrl+Shift+R Ctrl+Shift+R Select Range Вибір діапазону Mark range as selected Позначити діапазон Ctrl+R Ctrl+R &Range... Д&іапазон… Remove Range Вилучити діапазон Remove range Вилучити діапазон Remove range as selected Вилучити діапазон Ctrl+Del Ctrl+Del Remove Track Range Вилучити діапазон доріжки Remove track range Вилучити діапазон доріжки Remove track range as selected Вилучити діапазон доріжки Ñк позначений Ctrl+Shift+Del Ctrl+Shift+Del Insert Range Ð’Ñтавити діапазон Insert range Ð’Ñтавити діапазон Insert range as selected Ð’Ñтавити діапазон Ñк позначений Ctrl+Ins Ctrl+Ins Insert Track Range Ð’Ñтавити діапазон доріжки Insert track range Ð’Ñтавити діапазон доріжки Insert track range as selected Ð’Ñтавити діапазон доріжки Ñк позначений Ctrl+Shift+Ins Ctrl+Shift+Ins Sp&lit &Розділити Split Selection Розділити позначене Split selection Розділити позначене Split current selection Розділити поточне позначене Ctrl+Y Ctrl+Y &Add Track... &Додати доріжку… Add Track Додати доріжку Add track Додати доріжку Add a new track to session Додати до ÑеанÑу нову доріжку Shift+Ins Shift+Ins &Remove Track Ви&лучити доріжку Remove Track Вилучити доріжку Remove track Вилучити маршрути Remove current track from session Вилучити поточну доріжку з ÑеанÑу Shift+Del Shift+Del &Duplicate Track Д&ублювати доріжку Duplicate Track Дублювати доріжку Duplicate track Дублювати доріжку Duplicate current track Дублювати поточну доріжку Track &Properties... Ð’&лаÑтивоÑті доріжки... Track Properties ВлаÑтивоÑті доріжки Track properties ВлаÑтивоÑті доріжки Edit current track properties Змінити влаÑтивоÑті поточної доріжки Shift+F2 Shift+F2 &Inputs Ð’&ходи Track Inputs Входи доріжки Track inputs Входи доріжки Show current track input bus connections Показати з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ ÑˆÐ¸Ð½Ð¸ входу поточної доріжки &Outputs Ð’&иходи Track Outputs Виходи доріжки Track outputs Виходи доріжки Show current track output bus connections Показати з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ ÑˆÐ¸Ð½Ð¸ виходу поточної доріжки &Record За&Ð¿Ð¸Ñ Record Track ЗапиÑати доріжку Record track ЗапиÑати доріжку Arm current track for recording Призначити поточну доріжку Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñу &Mute &Вимкнути звук Mute Track Вимкнути звук на доріжці Mute track Вимкнути звук на доріжці Mute current track Вимкнути поточну доріжку &Solo С&оло Solo Track Ð’Ñтановити Ð´Ð»Ñ Ð´Ð¾Ñ€Ñ–Ð¶ÐºÐ¸ режим Ñоло Solo track Ð’Ñтановити Ð´Ð»Ñ Ð´Ð¾Ñ€Ñ–Ð¶ÐºÐ¸ режим Ñоло Solo current track Ð’Ñтановити Ð´Ð»Ñ Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¾Ñ— доріжки режим Ñоло M&onitor &Монітор Monitor Track Монітор доріжки Monitor track Монітор доріжки Monitor current track СпоÑÑ‚ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð·Ð° поточною доріжкою &First Пер&ша First Track Перша доріжка First track Перша доріжка Make current the first track Зробити поточну доріжку першою &Previous &Ðазад Previous Track ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð½Ñ ÐºÐ¾Ð¼Ð¿Ð¾Ð·Ð¸Ñ†Ñ–Ñ Previous track ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð½Ñ Ð´Ð¾Ñ€Ñ–Ð¶ÐºÐ° Make current the previous track Зробити поточну доріжку попередньою доріжкою &Next &Далі Next Track ÐаÑтупна доріжка Next track ÐаÑтупна доріжка Make current the next track Зробити поточну доріжку наÑтупною &Last &ОÑÑ‚Ð°Ð½Ð½Ñ Last Track ОÑÑ‚Ð°Ð½Ð½Ñ Ð´Ð¾Ñ€Ñ–Ð¶ÐºÐ° Last track ОÑÑ‚Ð°Ð½Ð½Ñ ÐºÐ¾Ð¼Ð¿Ð¾Ð·Ð¸Ñ†Ñ–Ñ Make current the last track Зробити поточну доріжку оÑтанньою N&one Ðе&Ñка None Track ÐÑ–Ñка доріжка None track ÐÑ–Ñка доріжка None current track ÐÑ–Ñка поточна доріжка &Top Ðа&гору Move Top ПереÑунути вгору Move top ПереÑунути на вершину Move current track to top ПереÑунути поточну доріжку на вершину ÑтоÑу &Up &Вище Move Up ПереÑунути вище Move up ПереÑунути вище Move current track up ПереÑунути поточну доріжку вище &Down &Ðижче Move Down ПереÑунути нижче Move down ПереÑунути нижче Move current track down ПереÑунути поточну доріжку нижче &Bottom Ðа&низ Move Bottom ПереÑунути вниз Move bottom ПереÑунути на низ Move current track to bottom ПереÑунути поточну доріжку на дно ÑтоÑу &Increase З&більшити Increase Height Збільшити виÑоту Increase height Збільшити виÑоту Increase track height Збільшити виÑоту доріжки Ctrl+Shift++ Ctrl+Shift++ &Decrease З&меншити Decrease Height Зменшити виÑоту Decrease height Зменшити виÑоту Decrease track height Зменшити виÑоту доріжки Ctrl+Shift+- Ctrl+Shift+- &Minimize &Мінімізувати Minimize Height Мінімізувати виÑоту Minimize height Мінімізувати виÑоту Minimize track height Мінімізувати виÑоту доріжки &Reset &Скинути Height Reset Відновити типове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð¸Ñоти Height reset Відновити типове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð¸Ñоти Reset track height Відновити типове Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð¸Ñоти доріжки Ctrl+Shift+1 Ctrl+Shift+1 Auto &Monitor Ðвто&монітор Auto Monitor Ðвтомонітор Auto monitor Ðвтомонітор Auto-monitor current track Ðвтоматичне ÑпоÑÑ‚ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð·Ð° поточною доріжкою F6 F6 Auto Dea&ctivate Ðвтод&ÐµÐ°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ñ–Ñ Auto Deactivate Ðвтоматична Ð´ÐµÐ°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ñ–Ñ Auto-deactivate plugins Ðвтоматична Ð´ÐµÐ°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ñ–Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÑ–Ð² Auto-deactivate plugins not producing sound Ðвтоматично вимикати додатки, Ñкі не дають звукових даних Shift+F6 Shift+F6 &Audio... Зв&ук… Inport Audio File Імпортувати звуковий файл Import Audio file Імпортувати звуковий файл Import tracks from Audio file Імпортувати доріжки зі звукового файла &MIDI... &MIDI… Import MIDI File Імпортувати файл MIDI Import MIDI file Імпортувати файл MIDI Import tracks from MIDI file Імпортувати доріжки з файла MIDI Export Audio File ЕкÑпортувати звуковий файл Export Audio file ЕкÑпортувати звуковий файл Export tracks to Audio file ЕкÑпортувати доріжки до звукового файла Export MIDI File ЕкÑпортувати файл MIDI Export MIDI file ЕкÑпортувати файл MIDI Export tracks to MIDI file ЕкÑпортувати доріжки до файла MIDI Log&arithmic Логари&фмічний Automation logarithmic Логарифмічний маÑштаб автоматизації Automation curve logarithmic scale Логарифмічний маÑштаб кривої автоматизації C&olor... Ко&лір… Automation color Колір автоматизації Automation curve color Колір кривої автоматизації &Lock За&блокувати Automation lock Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ— Lock automation curve Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ ÐºÑ€Ð¸Ð²Ð¾Ñ— автоматизації &Play Ð’Ñ–&дтворити Automation playback Ð’Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ— Playback automation curve Ð’Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÑ€Ð¸Ð²Ð¾Ñ— автоматизації Automation record Ð—Ð°Ð¿Ð¸Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ— Record automation curve Ð—Ð°Ð¿Ð¸Ñ ÐºÑ€Ð¸Ð²Ð¾Ñ— автоматизації &Clear Сп&орожнити Automation clear Ð¡Ð¿Ð¾Ñ€Ð¾Ð¶Ð½ÐµÐ½Ð½Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ— Clear automation curve Ð¡Ð¿Ð¾Ñ€Ð¾Ð¶Ð½ÐµÐ½Ð½Ñ ÐºÑ€Ð¸Ð²Ð¾Ñ— автоматизації Loc&k All Заб&локувати уÑе Automation lock all Заблокувати уÑе в автоматизації Lock all automation curves Заблокувати уÑÑ– криві автоматизації Play &All Відтво&рити вÑе Automation playback all Відтворити уÑÑŽ автоматизацію Playback all automation curves Відтворити уÑÑ– криві автоматизації Rec&ord All За&пиÑати уÑе Automation record all ЗапиÑати уÑÑŽ автоматизацію Record all automation curves ЗапиÑати уÑÑ– криві автоматизації C&lear All З&нÑти Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð· уÑÑ–Ñ… Automation clear all Спорожнити уÑÑŽ автоматизацію Clear all automation curves Спорожнити уÑÑ– криві автоматизації &New... С&творити… New Clip Ðовий кліп New clip Створити кліп Create new clip Створити новий кліп &Edit... З&мінити… Edit Clip Змінити кліп Edit clip Змінити кліп Edit current clip Змінити поточний кліп F4 F4 Mute Clip Вимкнути звук кліпу Mute clip Вимкнути звук кліпу Mute current clip Вимкнути звук поточного кліпу &Unlink Від’&єднати Unlink Clip Від'єднати кліп Unlink clip Від'єднати кліп Unlink current clip Від'єднати поточний кліп Recor&d ЗапиÑ&ати Record Clip ЗапиÑати кліп Record clip ЗапиÑати кліп Record current clip (overdub) ЗапиÑати поточний кліп (наклаÑти) &Split &Розділити Split Clip Поділити кліп Split clip Поділити кліп Split current clip at playhead Поділити поточний кліп на позиції Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ &Merge... О&б'єднати… Merge Clips Об'єднати кліпи Merge clips Об'єднати кліпи Merge selected clips Об'єднатиÑÑ Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ñ– кліпи Normali&ze &Ðормалізувати Normalize Clip Ðормалізувати кліп Normalize clip Ðормалізувати кліп Normalize current clip (gain/volume) Ðормалізувати поточний кліп (підÑиленнÑ/гучніÑть) &Quantize... К&вантизувати… Quantize Clip Квантизувати кліп Quantize clip events Квантизувати події кліпу Quantize current MIDI clip events Квантизувати події поточного кліпу MIDI &Transpose... Т&ранÑпонувати… Transpose Clip ТранÑпонувати кліп Transpose clip events ТранÑпонувати події кліпу Transpose current MIDI clip events ТранÑпонувати події поточного кліпу MIDI &Normalize... &Ðормалізувати… Normalize clip events Ðормалізувати події кліпу Normalize current MIDI clip events Ðормалізувати події поточного кліпу MIDI &Randomize... &Рандомізувати... Randomize Clip Рандомізувати кліп Randomize clip events Рандомізувати події кліпу Randomize current MIDI clip events Рандомізувати події поточного кліпу MIDI Resi&ze... Змі&нити розмір… Resize Clip Змінити розміри кліпу Resize clip events Змінити розміри подій кліпу Resize current MIDI clip events Змінити розміри подій поточного кліпу MIDI Re&scale... Змінити маÑ&штаб… Rescale Clip Змінити маÑштаб кліпу Rescale clip events Змінити маÑштаб подій кліпу Rescale current MIDI clip events Змінити маÑштаб подій поточного кліпу MIDI T&imeshift... ЗÑунути за &чаÑом... Timeshift Clip ЗÑунути кліп за чаÑом Timeshift clip events ЗÑунути події кліпу за чаÑом Timeshift current MIDI clip events ЗÑунути події поточного кліпу MIDI за чаÑом T&empo ramp... &Ухил ритму… Tempo ramp Clip Ухил ритму у кліпі Tempo ramp clip events Ухил ритму у подіÑÑ… кліпу Tempo ramp current MIDI clip events Ухил ритму у подіÑÑ… поточного кліпу MIDI &Tempo Adjust... Ско&ригувати ритм... Tempo Adjust Скоригувати ритм Adjust session tempo from current clip selection Скоригувати ритм ÑеанÑу з поточного позначеного у кліпі F7 F &Cross Fade Пере&хреÑне згаÑÐ°Ð½Ð½Ñ Clip Cross-fade ПерехреÑне згаÑÐ°Ð½Ð½Ñ Ñƒ кліпі Clip cross-fade ПерехреÑне згаÑÐ°Ð½Ð½Ñ Ñƒ кліпі Cross-fade current overlapped clips ПерехреÑне згаÑÐ°Ð½Ð½Ñ Ñƒ поточних накладених кліпах &Range Set Ð’Ñта&новити діапазон Clip Range Діапазон кліпу Clip range Діапазон кліпу Set edit-range from current clip extents Ð’Ñтановити діапазон Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð° розмірами поточного кліпу &Loop Set Ð’Ñтановити &цикл Clip Loop Зациклити кліп Clip loop Зациклити кліп Set loop-range from current clip extents Ð’Ñтановити діапазон Ð·Ð°Ñ†Ð¸ÐºÐ»ÐµÐ½Ð½Ñ Ð·Ð° розмірами поточного кліпу &Import... &Імпортувати… Import Clip Імпортувати кліп Import clip Імпортувати кліп Import clip from file(s) Імпортувати кліп з файлів E&xport... Е&кÑпортувати… Export Clip ЕкÑпортувати кліп Export clip ЕкÑпортувати кліп Export current clip to file ЕкÑпортувати поточний кліп до файла First Take Перший дубль First take Перший дубль Select current clip first take Вибрати перший дубль поточного кліпу Previous Take Попередній дубль Previous take Попередній дубль Select current clip previous take Вибрати попередній дубль поточного кліпу Next Take ÐаÑтупний дубль Next take ÐаÑтупний дубль Select current clip next take Вибрати наÑтупний дубль поточного кліпу Shift+T Shift+T Last Take ОÑтанній дубль Last take ОÑтанній дубль Select current clip last take ОÑтанній дубль поточного кліпу Reset Takes Скинути дублі Reset takes Скинути дублі Reset (unfold) current clip takes Скинути (розгорнути) дублі поточного кліпу R&ange... Ді&апазон… Take Range Діапазон Ð´ÑƒÐ±Ð»Ñ Take range Діапазон Ð´ÑƒÐ±Ð»Ñ Range (fold) current clip into takes Створити діапазон (згорнути) Ð´Ð»Ñ Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¾Ð³Ð¾ кліпу за дублÑми &Menubar Па&нель меню Menubar Панель меню Show/hide the main program window menubar Показати або приховати панель меню головного вікна програми Ctrl+M Ctrl+M &Statusbar С&мужка Ñтану Statusbar Смужка Ñтану Show/hide the main program window statusbar Показати або приховати Ñмужку Ñтану головного вікна програми File Toolbar Панель інÑтрументів файлів File toolbar Панель інÑтрументів файлів Show/hide main program window file toolbar Показати або приховати панель інÑтрументів головного вікна програми Edit Toolbar Пенал Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Edit toolbar Панель Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Show/hide main program window edit toolbar Показати або приховати панель Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð³Ð¾Ð»Ð¾Ð²Ð½Ð¾Ð³Ð¾ вікна програми Track Toolbar Панель інÑтрументів доріжки Track toolbar Панель інÑтрументів доріжки Show/hide main program window track toolbar Показати або приховати панель інÑтрументів доріжки головного вікна програми View Toolbar Панель переглÑду View toolbar Панель переглÑду Show/hide main program window view toolbar Показати або приховати панель інÑтрументів переглÑду головного вікна програми &Options П&араметри Options Toolbar Панель інÑтрументів параметрів Options toolbar Панель інÑтрументів параметрів Show/hide main program window options toolbar Показати або приховати панель інÑтрументів параметрів головного вікна програми Transport Toolbar Панель переходу Transport toolbar Панель переходу Show/hide main program window transport toolbar Показати або приховати панель інÑтрументів переходу головного вікна програми T&ime &Ð§Ð°Ñ Time Toolbar Панель чаÑу Time toolbar Панель чаÑу Show/hide main program window time toolbar Показати або приховати панель інÑтрументів чаÑу головного вікна програми Thum&b &Мініатюра Thumb Toolbar Панель інÑтрументів мініатюр Thumb toolbar Панель інÑтрументів мініатюр Show/hide main program window thumb toolbar Показати або приховати панель інÑтрументів мініатюр головного вікна програми File &System Фа&йлова ÑиÑтема File System Файлова ÑиÑтема File system Файлова ÑиÑтема Show/hide the file system window Показати або приховати вікно файлової ÑиÑтеми &Files &Файли Files Файли Show/hide the files window Показати або приховати вікно файлів M&essages П&Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Messages ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Show/hide the messages window Показати або приховати вікно повідомлень &Connections З'&Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Connections З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Show/hide the connections window Показати або приховати вікно з'єднань F8 F8 Mi&xer Мік&шер Mixer Мікшер Show/hide the mixer window Показати або приховати вікно мікшера F9 F9 &In Ð’&хід Zoom In Збільшити Zoom in Збільшити Ctrl++ Ctrl++ &Out Ð’&ихід Zoom Out Зменшити Zoom out Зменшити Ctrl+- Ctrl+- Zoom Reset Відновити початковий маÑштаб Zoom reset Відновити початковий маÑштаб Ctrl+1 Ctrl+1 &Horizontal &Горизонтально Horizontal Zoom МаÑштабувати за горизонталлю Horizontal zoom МаÑштабувати за горизонталлю Horizontal zoom mode Режим маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð° горизонталлю &Vertical &Вертикально Vertical Zoom Вертикальне маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ Vertical zoom Вертикальне маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ Vertical zoom mode Режим вертикального маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ All Zoom Рівномірне маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ All zoom Рівномірне маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ All zoom mode Режим рівномірного маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ &Grid &Сітка Grid Сітка Snap grid view mode Режим переглÑду із прилипаннÑм до Ñітки &Zebra Зе&бра Zebra Зебра Bar zebra view mode Режим переглÑду із зеброю Ñтовпчиків Too&l Tips Під&казки Tool tips Підказки Floating tool tips view mode Режим переглÑду із рухомими підказками &Refresh &Оновити Refresh Оновити Refresh views ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ð°Ð½ÐµÐ»ÐµÐ¹ переглÑду F5 F5 &Instruments... І&нÑтрументи… Instruments ІнÑтрументи Change instrument definitions and files Змінити Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ– файли інÑтрументів &Controllers... &Контролери… Controllers Контролери Change MIDI controllers configuration Змінити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ‚Ñ€Ð¾Ð»ÐµÑ€Ñ–Ð² MIDI &Buses... &Шини... Buses ÐвтобуÑи Change session bus definitions Змінити Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ ÑˆÐ¸Ð½ ÑеанÑу Tempo M&ap / Markers... К&арта / Позначки ритму... Tempo Map / Markers Карта / Позначки ритму Tempo map / markers Карта / Позначки ритму Change session tempo map / markers Змінити карту/позначки ритму ÑеанÑу &Options... П&араметри… Options Параметри Change general application program options Змінити загальні параметри програми F12 F12 &Backward &Ðазад Backward Ðазад Transport backward Перехід назад Backspace Backspace Re&wind Пов&ний назад Rewind Повний назад Transport rewind ÐŸÐµÑ€ÐµÐ¼Ð¾Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ð°Ð·Ð°Ð´ F&ast Forward П&овний вперед Fast Forward Повний вперед Fast forward Швидкий перехід вперед Transport fast forward Швидкий перехід вперед &Forward &Вперед Forward Вперед Transport forward Перехід вперед &Loop &Зациклити Loop Цикл Transport loop Ð—Ð°Ñ†Ð¸ÐºÐ»ÐµÐ½Ð½Ñ Ctrl+Shift+L Ctrl+Shift+L Step Backward Крок назад Step backward Крок назад Transport step backward Перейти на крок назад Step Forward Крок вперед Step forward Крок вперед Transport step forward Перейти на крок вперед Loop &Set &Ð’Ñтановити цикл Loop Set Ð’Ñтановити цикл Loop set Ð’Ñтановити цикл Transport loop set Ð—Ð°Ñ†Ð¸ÐºÐ»ÐµÐ½Ð½Ñ Ctrl+L Ctrl+L &Stop &Зупинити Stop Зупинити Transport stop Зупинити перехід Play Відтворити Transport play/pause Відтворити/Призупинити Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Space Пробіл Record ЗапиÑати Transport record Ð—Ð°Ð¿Ð¸Ñ &Punch Ð’&різка Punch Врізка Punch in/out Вхід/Вихід врізки Transport punch in/out Вхід/Вихід врізки Ctrl+Shift+P Ctrl+Shift+P Punch Se&t Ð’ÑÑ‚&ановити врізку Punch Set Ð’Ñтановити врізку Punch in/out set Ð’Ñтановити вхід/вихід врізки Transport punch in/out set Ð’Ñтановити вхід/вихід врізки Ctrl+P Ctrl+P &Count-in Ð’&ключити Count-in Включити &Metronome &Метроном Metronome Метроном F&ollow Playhead С&лідувати за позицією Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Follow Playhead Слідувати за позицією Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Follow playhead Слідувати за позицією Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ A&uto Backward &ÐÐ²Ñ‚Ð¾Ð¿Ð¾Ð²ÐµÑ€Ð½ÐµÐ½Ð½Ñ Auto Backward ÐÐ²Ñ‚Ð¾Ð¿Ð¾Ð²ÐµÑ€Ð½ÐµÐ½Ð½Ñ Auto backward ÐÐ²Ñ‚Ð¾Ð¿Ð¾Ð²ÐµÑ€Ð½ÐµÐ½Ð½Ñ &Continue Past End П&родовжувати за кінцем Continue Past End Продовжувати за кінцем Continue past end Продовжувати за кінцем None Ðемає Transport mode: None Режим переходу: немає Transport mode set to None Ð’Ñтановити Ð´Ð»Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ñƒ переходу Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«Ð½ÐµÐ¼Ð°Ñ”Â» &Slave Під&леглий Slave Підлеглий Transport mode: Slave Режим переходу: підлеглий Transport mode set to Slave Ð’Ñтановити Ð´Ð»Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ñƒ переходу Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«Ð¿Ñ–Ð´Ð»ÐµÐ³Ð»Ð¸Ð¹Â» &Master &ОÑновний Master ОÑновний Transport mode: Master Режим переходу: оÑновний Transport mode set to Master Ð’Ñтановити Ð´Ð»Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ñƒ переходу Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«Ð¾Ñновний» &Full П&овний Full Повний Transport mode: Full Режим переходу: повний Transport mode set to Full Ð’Ñтановити Ð´Ð»Ñ Ñ€ÐµÐ¶Ð¸Ð¼Ñƒ переходу Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Â«Ð¿Ð¾Ð²Ð½Ð¸Ð¹Â» Pa&nic Па&ніка Panic Паніка All MIDI tracks shut off (panic) Повне Ð²Ð¸Ð¼Ð¸ÐºÐ°Ð½Ð½Ñ ÑƒÑÑ–Ñ… доріжок MIDI (паніка) &Shortcuts... С&короченнÑ… Shortcuts Ð¡ÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ Keyboard shortcuts Клавіатурні ÑÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ &About... П&ро програму… About Про програму Show information about this application program Показати відомоÑті щодо цієї програми About &Qt... Про &Qt… About Qt Про Qt Show information about the Qt toolkit Показати відомоÑті щодо набору інÑтрументів Qt Set current snap to %1 Ð’Ñтановити Ð´Ð»Ñ Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¾Ð³Ð¾ Ð¿Ñ€Ð¸Ð»Ð¸Ð¿Ð°Ð½Ð½Ñ %1 Current time (play-head) Поточний Ñ‡Ð°Ñ (Ð¿Ð¾Ð·Ð¸Ñ†Ñ–Ñ Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ) Current tempo (BPM) Поточний ритм (біти за Ñекунду) Snap/beat ПрилипаннÑ/Біт Track Доріжка Current track name Ðазва поточної доріжки MOD МОД Session modification state Стан модифікації ÑеанÑу REC ЗÐП Session record state Стан запиÑу ÑеанÑу MUTE ВИМ Session muting state Стан Ð²Ð¸Ð¼Ð¸ÐºÐ°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÑƒ у ÑеанÑÑ– SOLO СОЛО Session soloing state Стан Ñоло у ÑеанÑÑ– LOOP ЦИКЛ Session looping state Стан циклічноÑті у ÑеанÑÑ– Session XRUN state Стан XRUN у ÑеанÑÑ– Session total time Загальний Ñ‡Ð°Ñ ÑеанÑу Session buffer size Розмір буфера ÑеанÑу Session sample rate ЧаÑтота диÑкретизації ÑеанÑу Ready Готово Could not set default session directory: %1 Sorry. Ðе вдалоÑÑ Ð²Ñтановити типовий каталог ÑеанÑу: %1 Вибачте. Untitled%1 Без назви%1 New session: "%1". Ðовий ÑеанÑ: «%1». Session files (*.%1 *.%2 *.%3) файли ÑеанÑів (*.%1 *.%2 *.%3) Session files (*.%1 *.%2) файли ÑеанÑів (*.%1 *.%2) Template files (*.%1) файли шаблонів (*.%1) Archive files (*.%1) файли архівів (*.%1) All files (*.*) уÑÑ– файли (*.*) Open Session Ð’Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ ÑеанÑу Save Session Зберегти ÑÐµÐ°Ð½Ñ Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ The file already exists: "%1" Do you want to replace it? Файл з такою назвою вже Ñ–Ñнує: «%1» Хочете замінити його? Backup session: "%1" as "%2". Резервна ÐºÐ¾Ð¿Ñ–Ñ ÑеанÑу: «%1» Ñк «%2». Could not backup existing session: %1 as %2 Sorry. Ðе вдалоÑÑ Ñтворити резервну копію наÑвного ÑеанÑу: %1 Ñк %2 Вибачте. The current session has been changed: "%1" Do you want to save the changes? Поточний ÑÐµÐ°Ð½Ñ Ð·Ð¼Ñ–Ð½ÐµÐ½Ð¾: «%1» Хочете зберегти зміни? About to remove archive directory: "%1" Are you sure? Ðаказано вилучити каталог архіву: «%1» Ви Ñправді цього хочете? Don't ask this again Більше не питати Session closed. Ð¡ÐµÐ°Ð½Ñ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¾. The directory already exists: "%1" Do you want to replace it? Каталог з такою назвою вже Ñ–Ñнує. «%1» Хочете замінити його? Opening "%1"... Відкриваємо «%1»... Session could not be loaded from "%1". Sorry. Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ ÑÐµÐ°Ð½Ñ Ð· «%1». Вибачте. Open session: "%1". Відкритий ÑеанÑ: «%1». A directory with same name already exists: "%1" This directory will be replaced, erasing all its current data, when opening and extracting this archive in the future. Do you want to continue? Каталог з такою назвою вже Ñ–Ñнує: «%1» Цей каталог буде замінено, витерто уÑÑ– дані у ньому, а потім відкрито Ñ– видобуто до нього цей архів. Хочете продовжити Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ñ†Ð¸Ñ… дій? The directory is an extracted archive: "%1" This directory will be removed, erased from all its current data, when closing this session. Do you want to continue? Каталог Ñ” результатом Ð²Ð¸Ð´Ð¾Ð±ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… з архіву: «%1» Цей каталог буде вилучено, очищено від уÑÑ–Ñ… поточних даних, при закритті цього ÑеанÑу. Продовжити Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð´Ñ–Ñ—? Saving "%1"... Зберігаємо «%1»... Session could not be saved to "%1". Sorry. Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³ÐµÑ‚Ð¸ ÑÐµÐ°Ð½Ñ Ð´Ð¾ «%1». Вибачте. Save session: "%1". Збережено ÑеанÑ: «%1». Oops! Looks like it crashed or did not close properly last time it was run... however, an auto-saved session file exists: "%1" Do you want to crash-recover from it? Оце тобі! ЗдаєтьÑÑ, минулого разу робота у програмі завершилаÑÑ Ð°Ð²Ð°Ñ€Ñ–Ð¹Ð½Ð¸Ð¼ чином... втім, виÑвлено автоматично збережений файл ÑеанÑу: «%1» Хочете відновити дані з цього файла? About to clear automation: "%1" Are you sure? Ðаказано очиÑтити автоматизацію: «%1» Ви Ñправді цього хочете? About to clear all automation: "%1" Are you sure? Ðаказано очиÑтити автоматизацію: «%1» Ви Ñправді цього хочете? take range діапазон Ð´ÑƒÐ±Ð»Ñ session ÑеанÑу or або program програми Information Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Some settings may be only effective next time you start this %1. ДеÑкі параметри можуть запрацювати лише під Ñ‡Ð°Ñ Ð½Ð°Ñтупного запуÑку %1. Player panic! Програвач у паніці! Debugging option enabled. Увімкнено параметр діагноÑтики. Ogg Vorbis (libvorbis) file support disabled. Вимкнено підтримку файлів Ogg Vorbis (libvorbis). MPEG-1 Audio Layer 3 (libmad) file support disabled. Вимкнено підтримку файлів MPEG-1 Audio Layer 3 (libmad). Sample-rate conversion (libsamplerate) disabled. Вимкнено підтримку Ð¿ÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ñ– зміною чаÑтоти диÑкретизації (libsamplerate). Pitch-shifting support (librubberband) disabled. Вимкнено підтримку Ð·Ð¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ñ‚Ð¾Ð½Ñƒ (librubberband). Beat-detection support (libaubio) disabled. Вимкнено підтримку виÑÐ²Ð»ÐµÐ½Ð½Ñ Ð±Ñ–Ñ‚Ñ–Ð² (libaubio). OSC service support (liblo) disabled. Вимкнено підтримку Ñлужби OSC (liblo). LADSPA Plug-in support disabled. Вимкнено підтримку додатків LADSPA. DSSI Plug-in support disabled. Вимкнено підтримку додатків DSSI. VST2 Plug-in support disabled. Вимкнено підтримку додатків VST2. VST3 Plug-in support disabled. Вимкнено підтримку додатків VST3. CLAP Plug-in support disabled. Вимкнено підтримку додатків CLAP. LV2 Plug-in support disabled. Вимкнено підтримку додатків LV2. LV2 Plug-in support (liblilv) disabled. Вимкнено підтримку додатків LV2 (liblilv). LV2 Plug-in UI support disabled. Вимкнено підтримку додатків LV2 в інтерфейÑÑ–. LV2 Plug-in UI support (libsuil) disabled. Вимкнено підтримку додатків LV2 в інтерфейÑÑ– (libsuil). LV2 Plug-in External UI support disabled. Вимкнено підтримку додатків LV2 в зовнішньому інтерфейÑÑ–. LV2 Plug-in MIDI/Event support (DEPRECATED) enabled. Увімкнено підтримку додатків MIDI/подій (ЗÐСТÐРІЛÐ). LV2 Plug-in MIDI/Atom support disabled. Вимкнено підтримку додатків MIDI/Atom LV2. LV2 Plug-in Worker/Schedule support disabled. Вимкнено підтримку додатків обробника/Ð¿Ð»Ð°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ LV2. LV2 Plug-in State support disabled. Вимкнено підтримку додатків Ñтану LV2. LV2 plug-in State Make Path support (DANGEROUS) enabled. Підтримка додатка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÑˆÐ»Ñхів Ð´Ð»Ñ Ñтану LV2 (ÐЕБЕЗПЕЧÐО) увімкнено. LV2 Plug-in State Files support disabled. Вимкнено підтримку додатків файлів Ñтану LV2. LV2 Plug-in Programs support disabled. Вимкнено підтримку додатків програм LV2. LV2 Plug-in MIDNAM support disabled. Вимкнено підтримку додатків MIDNAM LV2. LV2 Plug-in Presets support disabled. Вимкнено підтримку додатків наборів налаштувань LV2. LV2 Plug-in Patch support disabled. Вимкнено підтримку додатків латок LV2. LV2 Plug-in Time/position support disabled. Вимкнено підтримку додатків чаÑу/позиції LV2. LV2 Plug-in Options support disabled. Вимкнено підтримку додатків параметрів LV2. LV2 Plug-in Buf-size support disabled. Вимкнено підтримку додатків розміру буфера LV2. LV2 Plug-in UI Touch interface support disabled. Вимкнено підтримку додатків ÑенÑорного інтерфейÑу LV2. LV2 Plug-in UI Request-value support disabled. Вимкнено підтримку додатків запитів до інтерфейÑу LV2. LV2 Plug-in UI Idle interface support disabled. Вимкнено підтримку додатків інтерфейÑу бездіÑльноÑті LV2. LV2 Plug-in UI Show interface support disabled. Вимкнено підтримку додатків показу інтерфейÑу LV2. LV2 Plug-in UI GTK2 native support disabled. Вимкнено підтримку додатків влаÑного інтерфейÑу GTK2 LV2. LV2 Plug-in UI GTKMM2 native support disabled. Вимкнено вбудовану підтримку інтерфейÑу GTKMM2 додатків LV2. LV2 Plug-in UI X11 native support disabled. Вимкнено підтримку додатків влаÑного інтерфейÑу X11 LV2. JACK Session support disabled. Підтримку ÑеанÑів JACK вимкнено. JACK Latency support disabled. Вимкнено підтримку латентноÑті JACK. JACK Metadata support disabled. Вимкнено підтримку метаданих JACK. NSM support disabled. Вимкнено підтримку NSM. Version ВерÑÑ–Ñ Using: Qt %1 ВикориÑтовуємо Qt %1 Website Сайт This program is free software; you can redistribute it and/or modify it Ð¦Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð° Ñ” вільним програмним забезпеченнÑм, ви можете поширювати Ñ—Ñ— Ñ– вноÑити до неї зміни under the terms of the GNU General Public License version 2 or later. за умов Ð´Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ GNU General Public License верÑÑ–Ñ— 2 або новіших верÑій. record clip запиÑати кліп [modified] [змінено] XRUN XRUN Session started. Ð¡ÐµÐ°Ð½Ñ Ñ€Ð¾Ð·Ð¿Ð¾Ñ‡Ð°Ñ‚Ð¾. The audio/MIDI engine could not be started. Make sure the JACK/Pipewire audio service and the ALSA Sequencer kernel module (snd-seq-midi) are up and running and then restart the session. Ðе вдалоÑÑ Ð·Ð°Ð¿ÑƒÑтити звуковий рушій або рушій MID. ПереконайтеÑÑ, що запущено звукову Ñлужбу JACK/Pipewire та модуль Ñдра планувальника ALSA (snd-seq-midi), Ñ– вони працюють, потім перезапуÑтіть ÑеанÑ. The original session sample rate (%1 Hz) is not the same as the current audio engine (%2 Hz). Saving and reloading from a new session file is highly recommended. Початкова чаÑтота диÑкретизації ÑеанÑу (%1 Гц) не збігаєтьÑÑ Ñ–Ð· чаÑтотою у поточному рушії обробки (%2 Гц). Ðаполегливо рекомендуємо зберегти дані Ñ– перезавантажитиÑÑ Ñ–Ð· нового файла ÑеанÑу. The following issues were detected: %1 Saving into another session file is highly recommended. ВиÑвлено такі проблеми: %1 Ðаполегливо рекомендуємо зберегти дані до іншого файла ÑеанÑу. &Hold &Ð£Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ &Linear &Лінійний &Spline С&плайни Take %1 Дубль %1 Error Помилка XRUN(%1 skipped) XRUN(%1 пропущено) XRUN(%1): some frames might have been lost. XRUN(%1): деÑкі кадри могло бути втрачено. Audio connections change. Зміна звукових з'єднань. Audio self-connection detected! In general, connecting an output bus (or insert send), directly into any input bus (or insert return), is not advisable. It often doesn't work, if at all. ВиÑвлено Ñамоз'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¸Ñ… даних! Загалом, з'Ñ”Ð´Ð½Ð°Ð½Ñ ÐºÐ°Ð½Ð°Ð»Ñƒ Ð²Ð¸Ð²ÐµÐ´ÐµÐ½Ð½Ñ (або надÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð²ÑтавленнÑ) безпоÑереднього з будь-Ñким каналом вхідних даних (або Ð¿Ð¾Ð²ÐµÑ€Ð½ÐµÐ½Ð½Ñ Ð²ÑтавленнÑ) Ñ” тим, що ми не радимо робити. ЧаÑто такі з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð½ÐµÐ¿Ñ€Ð°Ñ†ÐµÐ·Ð´Ð°Ñ‚Ð½Ñ–, Ñкщо взагалі можливі. Don't show this again Більше не показувати MIDI connections change. Зміна з'єднань MIDI. Playing ended. Ð’Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¾. The audio engine has been shutdown. Make sure the JACK audio server (jackd) is up and running and then restart session. Рушій обробки звукових даних завершив роботу. ПереконайтеÑÑ, що звуковий Ñервер JACK (jackd) запущено Ñ– визнано працездатним, а потім перезапуÑтіть ÑеанÑ. The audio engine buffer size has changed, increased from %1 to %2 frames/period. Reloading the current session file is highly recommended. Було змінено розмір буфера Ñ€ÑƒÑˆÑ–Ñ Ð¾Ð±Ñ€Ð¾Ð±ÐºÐ¸ звукових дани, збільшено з %1 до %2 кадрів за період. Ðаполегливо рекомендуємо перезавантажити файл поточного ÑеанÑу. STOP СТОП PLAY ПУСК FFWD ШВП REW ÐÐЗ REC ON ЗÐП УВ REC OFF ЗÐП ВИМ RESET СКИÐУТИ LOCATE %1 ЗÐÐЙТИ %1 SHUTTLE %1 SHUTTLE %1 STEP %1 КРОК %1 TRACK RECORD %1 %2 ЗÐПИС ДОРІЖКИ %1 %2 TRACK MUTE %1 %2 ВИМК. ЗВУК ДОРІЖКИ %1 %2 TRACK SOLO %1 %2 ДОРІЖКРСОЛО %1 %2 TRACK MONITOR %1 %2 МОÐІТОР ДОРІЖКИ %1 %2 Unknown sub-command Ðевідома підкоманда Not implemented Ðе реалізовано MIDI CTL: %1, Channel %2, Param %3, Value %4 КОÐТРОЛЬ MIDI: %1, канал %2, парам %3, знач. %4 (track %1, gain %2) (доріжка %1, підÑÐ¸Ð»ÐµÐ½Ð½Ñ %2) (track %1, panning %2) (доріжка %1, Ð¿Ð°Ð½Ð¾Ñ€Ð°Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ %2) START ПУСК CONTINUE ПРОДОВЖИТИ SONGPOS %1 ПОЗКОМП %1 %1 BPM %1 такт/хв Playing "%1"... Відтворюємо «%1»... qtractorMessages Messages ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Logging stopped --- %1 --- ЗапиÑÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾ журналу зупинено --- %1 --- Logging started --- %1 --- Розпочато запиÑÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾ журналу --- %1 --- qtractorMidiControl Note On Ðоту увімкнено Note Off Ðоту вимкнено Key Press ÐатиÑÐºÐ°Ð½Ð½Ñ ÐºÐ»Ð°Ð²Ñ–ÑˆÑ– Controller Контролер Pgm Change Зміна pgm Chan Press ÐатиÑк. кан Pitch Bend Виг. тону RPN RPN NRPN NRPN Control 14 ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ 14 Track Gain ПідÑÐ¸Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ñ€Ñ–Ð¶ÐºÐ¸ Track Panning ÐŸÐ°Ð½Ð¾Ñ€Ð°Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ñ€Ñ–Ð¶ÐºÐ¸ Track Monitor Монітор доріжки Track Record Ð—Ð°Ð¿Ð¸Ñ Ð´Ð¾Ñ€Ñ–Ð¶ÐºÐ¸ Track Mute Ð’Ð¸Ð¼Ð¸ÐºÐ°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÑƒ доріжки Track Solo Соло доріжки qtractorMidiControlForm Controllers Контролери Controller files Files Файли Path ШлÑÑ… Import controller files Імпортувати файли контролера &Import... &Імпортувати… Remove controller file Вилучити файл контролера &Remove Ви&лучити Move controller file up on list order ПереÑунути файл контролера вище у ÑпиÑку &Up &Вище Move controller file down on list order ПереÑунути файл контролера нижче у ÑпиÑку &Down &Ðижче &Type &Тип &Channel &Канал &Parameter П&араметр Trac&k Д&оріжка offse&t з&Ð¼Ñ–Ñ‰ÐµÐ½Ð½Ñ &limit ме&жа C&ommand К&оманда Flags Прапорці MIDI Event type Тип події MIDI MIDI Channel Канал MIDI MIDI Controller (parameter) Контролер MIDI (параметр) MIDI parameter (track offset) Параметр MIDI (Ð·Ð¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð´Ð¾Ñ€Ñ–Ð¶ÐºÐ¸) + + Track offset Ð—Ð¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð´Ð¾Ñ€Ñ–Ð¶ÐºÐ¸ Track limit Межа доріжки Command action Ð”Ñ–Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¸ Command delta/momentary Дельта команди або миттєвіÑть D&elta Д&ельта Command feedback Відгук команди &Feedback Від&гук Map/update controller command Прив'Ñзка/ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¸ контролера &Map Прив'&Ñзати Controller map Карта контролера Type Тип Channel Канал Parameter Параметр Track Доріжка Command Команда Unmap/remove controller command Відв'Ñзати/Вилучити команду контролера U&nmap Відв'Ñз&ати Enable all controllers immediate sync (hook) Увімкнути негайну Ñинхронізацію уÑÑ–Ñ… контролерів (Ñкрипт) &Sync Син&Ñ…Ñ€Ð¾Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Reload/apply all controller files Перезавантажити/ЗаÑтоÑувати уÑÑ– файли контролера Relo&ad П&ерезавантажити Export to controller file ЕкÑпортувати до файла контролера E&xport... Е&кÑпортувати… Close this dialog Закрити це вікно Close Закрити Import Controller Files Імпорт файлів контролера Controller files (*.%1) файли контролера (*.%1) All files (*.*) уÑÑ– файли (*.*) Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ About to remove controller file: "%1" Are you sure? Ðаказано вилучити файл контролера: «%1» Ви Ñправді цього хочете? Export Controller File ЕкÑпорт файла контролера controller контролер The controller file already exists: "%1" Do you want to replace it? Файл контролера із такою назвою вже Ñ–Ñнує: «%1» Хочете замінити його? Saved controller mappings may not be effective the next time you start this program. "%1" Do you want to apply to controller files? Збережену прив'Ñзку контролерів може бути не заÑтоÑовано до наÑтупного запуÑку програми. «%1» Хочете заÑтоÑувати файли контролера? Controller mappings have been changed. Прив'Ñзки контролерів було змінено. Do you want to save the changes? Чи бажаєте ви зберегти зміни? Delta Дельта Feedback Відгук qtractorMidiControlObserverForm MIDI Controller MIDI-контролер &Type: &Тип: MIDI event type Тип події MIDI Cha&nnel: &Канал: MIDI channel Канал MIDI &Parameter: &Параметр: MIDI parameter Параметр MIDI &Logarithmic &Логарифмічний &Feedback Від&гук In&vert &Інвертувати &Hook С&крипт L&atch З&аÑув Control input connections Керувати вхідними з'єднаннÑми &Inputs Ð’&ходи Control output connections Керувати вихідними з'єднаннÑми &Outputs Ð’&иходи MIDI controller is already assigned. Do you want to replace the mapping? MIDI-контролер вже пов'Ñзано. Хочете замінити прив'Ñзку? Some settings have been changed. Do you want to apply the changes? До деÑких параметрів було внеÑено зміни. Хочете заÑтоÑувати ці зміни? &MIDI Controller... &MIDI-контролер... &Automation &ÐÐ²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ &Lock За&блокувати &Play Ð’Ñ–&дтворити &Record За&Ð¿Ð¸Ñ &Clear Сп&орожнити qtractorMidiControlPluginWidget &Type: &Тип: MIDI event type Тип події MIDI Cha&nnel: &Канал: MIDI channel Канал MIDI &Parameter: &Параметр: MIDI parameter Параметр MIDI &Logarithmic &Логарифмічний In&vert &Інвертувати &Bipolar &БіполÑрний qtractorMidiEditEvent Zoom in (horizontal) Збільшити (горизонтально) Zoom out (horizontal) Зменшити (горизонтально) Zoom reset (horizontal) Скинути маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ (горизонтально) qtractorMidiEditList C%1 К%1 qtractorMidiEditTime Play-head ÐŸÐ¾Ð·Ð¸Ñ†Ñ–Ñ Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Edit-head Редагувати позицію Edit-tail Редагувати хвіÑÑ‚ Loop-start Початок циклу Loop-end Кінець циклу Punch-in Вхід врізки Punch-out Вихід врізки Start: %1 End: %2 Length: %3 Початок: %1 Кінець: %2 ТриваліÑть: %3 qtractorMidiEditView Zoom in (vertical) Збільшити (вертикально) Zoom out (vertical) Зменшити (вертикально) Zoom reset (vertical) Скинути маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ (вертикально) qtractorMidiEditor C C C#/Db C#/Db D D D#/Eb D#/Eb E E F F F#/Gb F#/Gb G G G#/Ab G#/Ab A A A#/Bb A#/Bb B B Acoustic Bass Drum ÐкуÑтичний баÑовий барабан Bass Drum 1 БаÑовий барабан 1 Side Stick Бічна ручка Acoustic Snare ÐкуÑтична Ð¿ÐµÑ‚Ð»Ñ Hand Clap Electric Snare Малі електричні барабани Low Floor Tom Closed Hi-Hat Закритий гай-гет High Floor Tom Pedal Hi-Hat Low Tom Open Hi-Hat Low-Mid Tom Hi-Mid Tom Crash Cymbal 1 High Tom Ride Cymbal 1 Chinese Cymbal КитайÑькі цимбали Ride Bell Тарілка Tambourine Бубон Splash Cymbal Cowbell Дзвіночок Crash Cymbal 2 Vibraslap Ride Cymbal 2 Hi Bongo Low Bongo Mute Hi Conga Open Hi Conga Low Conga High Timbale Low Timbale High Agogo Low Agogo Cabasa КабаÑа Maracas ÐœÐ°Ñ€Ð°ÐºÐ°Ñ Short Whistle Короткий ÑвиÑток Long Whistle Довгий ÑвиÑток Short Guiro Long Guiro Claves Hi Wood Block Low Wood Block Mute Cuica Open Cuica Mute Triangle Open Triangle Bank Select (coarse) Modulation Wheel (coarse) Breath Controller (coarse) Foot Pedal (coarse) Portamento Time (coarse) Data Entry (coarse) Ð—Ð°Ð¿Ð¸Ñ Ð´Ð°Ð½Ð¸Ñ… (грубо) Volume (coarse) ГучніÑть (грубо) Balance (coarse) Ð‘Ð°Ð»Ð°Ð½Ñ (грубо) Pan Position (coarse) ÐŸÐ¾Ð·Ð¸Ñ†Ñ–Ñ Ð¿Ð°Ð½Ð¾Ñ€Ð°Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ (грубо) Expression (coarse) Вираз (грубо) Effect Control 1 (coarse) Контроль ефекту 1 (грубо) Effect Control 2 (coarse) Контроль ефекту 2 (грубо) General Purpose Slider 1 Повзунок загального Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ 1 General Purpose Slider 2 Повзунок загального Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ 2 General Purpose Slider 3 Повзунок загального Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ 3 General Purpose Slider 4 Повзунок загального Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ 4 Bank Select (fine) Вибір банку (тонко) Modulation Wheel (fine) КолеÑо модулÑції (тонко) Breath Controller (fine) Контролер Ð´Ð¸Ñ…Ð°Ð½Ð½Ñ (тонко) Foot Pedal (fine) Ðожна педаль (тонко) Portamento Time (fine) Ð§Ð°Ñ Ð¿Ð¾Ñ€Ñ‚Ð°Ð¼ÐµÐ½Ñ‚Ð¾ (тонко) Data Entry (fine) Ð’Ð²ÐµÐ´ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… (тонко) Volume (fine) ГучніÑть (тонко) Balance (fine) Ð‘Ð°Ð»Ð°Ð½Ñ (тонко) Pan Position (fine) ÐŸÐ¾Ð·Ð¸Ñ†Ñ–Ñ Ð¿Ð°Ð½Ð¾Ñ€Ð°Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ (тонко) Expression (fine) Вираз (тонко) Effect Control 1 (fine) Контроль ефекту 1 (тонко) Effect Control 2 (fine) Контроль ефекту 2 (тонко) Hold Pedal (on/off) Ð£Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¿ÐµÐ´Ð°Ð»Ñ– (увімкн./вимкн.) Portamento (on/off) Портаменто (увімкн./вимкн.) Soft Pedal (on/off) М'Ñка педаль (увімкн./вимкн.) Legato Pedal (on/off) Педаль легато (увімкн./вимкн.) Hold 2 Pedal (on/off) Ð£Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¿ÐµÐ´Ð°Ð»Ñ– 2 (увімкн./вимкн.) Sound Variation Звукова Ð²Ð°Ñ€Ñ–Ð°Ñ†Ñ–Ñ General Purpose Button 1 (on/off) Кнопка загального Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ 1 (увімкн./вимкн.) General Purpose Button 2 (on/off) Кнопка загального Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ 2 (увімкн./вимкн.) General Purpose Button 3 (on/off) Кнопка загального Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ 3 (увімкн./вимкн.) General Purpose Button 4 (on/off) Кнопка загального Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ 4 (увімкн./вимкн.) Effects Level Рівень ефектів Chorus Level Рівень хору Celeste Level Рівень модераторної педалі Phaser Level Рівень фазера Data Button Increment ÐŸÑ€Ð¸Ñ€Ñ–Ñ ÐºÐ½Ð¾Ð¿ÐºÐ¸ даних Data Button Decrement Ð—Ð¼ÐµÐ½ÑˆÐµÐ½Ð½Ñ ÐºÐ½Ð¾Ð¿ÐºÐ¸ даних Non-Registered Parameter (fine) ÐезареєÑтрований параметр (тонко) Non-Registered Parameter (coarse) ÐезареєÑтрований параметр (грубо) Registered Parameter (fine) ЗареєÑтрований параметр (тонко) Registered Parameter (coarse) ЗареєÑтрований параметр (грубо) All Sound Off Вимкнути увеÑÑŒ звук All Controllers Off Вимкнути уÑÑ– контролери Local Keyboard (on/off) Локальна клавіатура (увімкн./вимкн.) All Notes Off Вимкнути уÑÑ– ноти Omni Mode Off Вимкнути омнірежим Omni Mode On Увімкнути омнірежим Mono Operation ÐœÐ¾Ð½Ð¾Ð´Ñ–Ñ Poly Operation ÐŸÐ¾Ð»Ñ–Ð´Ñ–Ñ Pitch Bend Sensitivity ЧутливіÑть до зміни тону Fine Tune Тонке ÐºÐ¾Ñ€Ð¸Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Coarse Tune ЖорÑтке ÐºÐ¾Ñ€Ð¸Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Tuning Program Програма ÐºÐ¾Ñ€Ð¸Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Tuning Bank Банк ÐºÐ¾Ñ€Ð¸Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Vibrato Rate Vibrato Depth Vibrato Delay Filter Cutoff Filter Resonance Sostenuto Pedal (on/off) Release Time Ð§Ð°Ñ Ð²Ñ–Ð´Ð¿ÑƒÑÐºÐ°Ð½Ð½Ñ Attack Time Ð§Ð°Ñ Ð°Ñ‚Ð°ÐºÐ¸ Brightness СвіліÑть Decay Time Ð§Ð°Ñ Ð·Ð³Ð°ÑÐ°Ð½Ð½Ñ Tremolo Level EG Attack EG Decay EG Release Drum Filter Cutoff Drum Filter Resonance Drum EG Attack Drum EG Decay Drum Pitch Coarse Drum Pitch Fine Drum Level Рівень ударних Drum Pan Панель ударних Drum Reverb Send Drum Chorus Send Drum Variation Send Modulation Wheel (14bit) КолеÑо модулÑції (14-бітове) Breath Controller (14bit) Контролер Ð´Ð¸Ñ…Ð°Ð½Ð½Ñ (14-бітовий) Foot Pedal (14bit) Ðожна педаль (14-бітова) Portamento Time (14bit) Volume (14bit) ГучніÑть (14-бітова) Balance (14bit) Ð‘Ð°Ð»Ð°Ð½Ñ (14-бітовий) Pan Position (14bit) Expression (14bit) Effect Control 1 (14bit) Effect Control 2 (14bit) General Purpose Slider 1 (14bit) Повзунок загального Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ 1 (14-бітовий) General Purpose Slider 2 (14bit) Повзунок загального Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ 2 (14-бітовий) General Purpose Slider 3 (14bit) Повзунок загального Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ 3 (14-бітовий) General Purpose Slider 4 (14bit) Повзунок загального Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ 4 (14-бітовий) Chromatic Хроматична Major Мажор Minor Мінор Melodic Minor (Asc) Мелодична мінор (зроÑÑ‚.) Melodic Minor (Desc) Мелодична мінор (Ñпад.) Whole Tone Повний тон Pentatonic Major Пентатонічний мажор Pentatonic Minor Пентатонічний мінор Pentatonic Blues Пентатонічний блюз Pentatonic Neutral Пентатонічний нейтральний Octatonic (H-W) Октатонічний (H-W) Octatonic (W-H) Октатонічний (W-H) Ionian Іонічний Dorian Доричний Phrygian ФрігійÑький Lydian лідійÑька Mixolydian МікÑолідійÑький Aeolian ЕолійÑький Locrian ЛоклійÑький Egyptian ЄгипетÑький Eight Tone Spanish ВоÑьмитоновий Ñ–ÑпанÑький Hawaiian ГавайÑький Hindu ІндуїÑÑ‚Ñький Hirajoshi Hungarian Major Hungarian Minor Hungarian Gypsy Japanese (A) ЯпонÑький (A) Japanese (B) ЯпонÑький (B) Jewish (Adonai Malakh) Jewish (Ahaba Rabba) Jewish (Magen Abot) Oriental (A) Oriental (B) Oriental (C) Roumanian Minor Neapolitan ÐеаполітанÑький Neapolitan Major Neapolitan Minor Overtone Leading Whole Tone Nine Tone Scale Dominant Seventh Augmented Збільшений Algerian ÐлжирÑька Arabian (A) Arabian (B) Balinese БалійÑький Chinese КитайÑький Diminished Зменшений Japanese (Ichikosucho) Japanese (Taishikicho) Javaneese ЯванÑька Marva Theta Mela Bhavapriya Mela Chakravakam Mela Chalanata Mela Chitrambari Mela Dharmavati Mela Dhatuvardhani Mela Dhavalambari Mela Divyamani Mela Ganamurti Mela Gangeyabhusani Mela Gavambodhi Mela Gayakapriya Mela Hatakambari Mela Jalarnavam Mela Jhalavarali Mela Jhankaradhvani Mela Jyotisvarupini Mela Kamavarardhani Mela Kantamani Mela Kosalam Mela Latangi Mela Manavati Mela Mararanjani Mela Naganandini Mela Namanarayani Mela Navanitam Mela Nitimati Mela Pavani Mela Ragavardhani Mela Raghupriya Mela Ramapriya Mela Rasikapriya Mela Ratnangi Mela Risabhapriya Mela Rupavati Mela Sadvidhamargini Mela Salagam Mela Sanmukhapriya Mela Sarasangi Mela Senavati Mela Subhapantuvarali Mela Sucharitra Mela Sulini Mela Suryakantam Mela Syamalangi Mela Tanarupi Mela Vagadhisvari Mela Vanaspati Mela Varunapriya Mela Yagapriya Persian ПерÑька Purvi Theta Spanish Gypsy Todi Theta Enigmatic Kumoi Lydian Augmented Pelog Prometheus Прометей Prometheus Neapolitan Six Tone Symmetrical Super Locrian Lydian Minor Lydian Diminished Half Diminished Bhairav Yaman Todi Jog Джоґ Multani Мултані Darbari Malkauns Bhoopali Shivaranjani Marwa Minor 5 Мінор 5 Major 5 Мажор 5 5 5 45 45 457 457 M 6 M 6 MIDI Editor Редактор MIDI cut вирізати delete вилучити insert range вÑтавити діапазон remove range вилучити діапазон move переÑунути edit змінити resize змінити розмір rescale rescale paste вÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ Time: %1 Type: ЧаÑ: %1 Тип: Note On (%1) %2 Velocity: %3 Duration: %4 Ðота (%1) %2 ШвидкіÑть: %3 ТриваліÑть: %4 Key Press (%1) %2 Value: %3 ÐатиÑÐºÐ°Ð½Ð½Ñ ÐºÐ»Ð°Ð²Ñ–ÑˆÑ– (%1) %2 ЗначеннÑ: %3 Controller (%1) Name: %2 Value: %3 Контролер (%1) Ðазва: %2 ЗначеннÑ: %3 RPN (%1) Name: %2 Value: %3 RPN (%1) Ðазва: %2 ЗначеннÑ: %3 NRPN (%1) Name: %2 Value: %3 NRPN (%1) Ðазва: %2 ЗначеннÑ: %3 Control 14 (%1) Name: %2 Value: %3 ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ 14 (%1) Ðазва: %2 ЗначеннÑ: %3 Pgm Change (%1) Зміна Pgm (%1) Chan Press (%1) ÐатиÑÐºÐ°Ð½Ð½Ñ ÐºÐ°Ð½Ð°Ð»Ñƒ (%1) Pitch Bend (%1) Зміна тональноÑті (%1) SysEx (%1 bytes) Data: SysEx (%1 байтів) Дані: Unknown (%1) Ðевідомо (%1) Start: %1 End: %2 Length: %3 Початок: %1 Кінець: %2 ТриваліÑть: %3 qtractorMidiEditorForm MIDI Editor Редактор MIDI &File &Файл &Track Д&оріжка Instrum&ent І&нÑтрумент &View П&ереглÑд &Toolbars П&анелі інÑтрументів &Windows &Вікна Not&e Type &Тип ноти Val&ue Type Тип зн&Ð°Ñ‡ÐµÐ½Ð½Ñ &Ghost Track Д&оріжка-привид &Zoom Змінити &маÑштаб S&nap При&Ð»Ð¸Ð¿Ð°Ð½Ð½Ñ Sc&ale &МаÑштаб &Tools &ІнÑтрументи &Edit З&міни Select &Mode Режим поз&Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ &Select Поз&Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ I&nsert Ð’Ñ&тавити Remo&ve Ви&лучити T&ransport &ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ &Note &Ðота St&ep &Крок &Help &Довідка &Save З&берегти Save Зберегти Save current MIDI clip to existing file name Зберегти поточний кліп MIDI до наÑвного файла Save &As... Зберегти &Ñк… Save As Зберегти Ñк Save as Зберегти Ñк Save current MIDI clip with another file name Зберегти поточний кліп MIDI до файла з іншою назвою &Mute &Вимкнути звук Mute Вимкнути звук Mute current MIDI clip Вимкнути звук поточного кліпу MIDI &Unlink Ð’&ід’єднати Unlink Від’єднати Unlink current MIDI clip Від'єднати поточний кліп MIDI Recor&d За&пиÑати Record ЗапиÑати Record current MIDI clip (overdub) ЗапиÑати поточний кліп MIDI (накладаннÑ) &Inputs Ð’&ходи Track Inputs Входи доріжки Track inputs Входи доріжки Show current MIDI clip/track input bus connections Показати з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð²Ñ…Ñ–Ð´Ð½Ð¾Ñ— шини Ð´Ð»Ñ Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¾Ð³Ð¾ кліпу або доріжки MIDI &Outputs Ð’&иходи Track Outputs Виходи доріжки Track outputs Виходи доріжки Show current MIDI clip/track output bus connections Показати з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð²Ð¸Ñ…Ñ–Ð´Ð½Ð¾Ñ— шини поточного кліпу або доріжки MIDI &Properties... Вл&аÑтивоÑті… Track Properties ВлаÑтивоÑті доріжки Track properties ВлаÑтивоÑті доріжки Edit current MIDI clip/track properties Редагувати влпÑтивоÑті поточного кліпу або доріжки MIDI Shift+F2 Shift+F2 Properties ВлаÑтивоÑті Edit current MIDI clip properties Редагувати влаÑтивоÑті поточного кліпу MIDI F4 F4 &Range Set Ð’Ñта&новити діапазон Clip Range Діапазон кліпу Clip range Діапазон кліпу Set edit-range from clip extents Ð’Ñтановити діапазон Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð° розміром кліпу &Loop Set Ð’Ñтановити &цикл Clip Loop Зациклити кліп Clip loop Зациклити кліп Set loop-range from clip extents Ð’Ñтановити діапазон циклу за розмірами кліпу &Close &Закрити Close Закрити Close this MIDI clip editor Закрити це вікно Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñƒ MIDI &Undo &Вернути Undo СкаÑувати Undo last edit operation СкаÑувати оÑтанню дію з Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ctrl+Z Ctrl+Z &Redo &Повторити Redo Повторити Redo last edit operation Повторити оÑтанню дію з Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ctrl+Shift+Z Ctrl+Shift+Z Cu&t Виріза&ти Cut Вирізати Cut current selection into the local clipboard Вирізати поточний позначений фрагмент до локального буфера обміну Ctrl+X Ctrl+X &Copy &Копіювати Copy Копіювати Copy current selection to the local clipboard Копіювати поточний позначений фрагмент до локального буфера Ctrl+C Ctrl+C &Paste &Ð’Ñтавити Paste Ð’Ñтавити Paste local clipboard contents into the current MIDI clip Ð’Ñтавити вміÑÑ‚ локального буфера обміну даними до поточного кліпу MIDI Ctrl+V Ctrl+V Past&e Repeat... Ð’Ñтавити повто&реннÑ… Paste Repeat Ð’Ñтавити Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ Paste repeat Ð’Ñтавити Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ Paste/repeat local clipboard contents into the current MIDI clip Ð’Ñтавити/Повторити вміÑÑ‚ локального буфера обміну даними до поточного кліпу MIDI Ctrl+Shift+V Ctrl+Shift+V &Delete Ви&лучити Delete Вилучити Delete current selection Вилучити поточне позначене Del Del Edit Of&f Ви&мкнути Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Edit Off Вимкнути Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Edit off Вимкнути Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Set edit mode off Ð’Ñтановити режим Ð²Ð¸Ð¼Ð¸ÐºÐ°Ð½Ð½Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Edit &On &Увімкнути Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Edit On Увімкнути Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Edit on Увімкнути Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Set edit mode on Ð’Ñтановити режим Ð²Ð¼Ð¸ÐºÐ°Ð½Ð½Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Edit &Draw &ÐœÐ°Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Edit draw mode Режим Ð¼Ð°Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸ редагуванні Edit draw mode (notes) Режим Ð¼Ð°Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸ редагуванні (ноти) &All &Ð’Ñе Select All Позначити вÑе Select all Вибрати вÑе Ctrl+A Ctrl+A &None &Ðемає Select None ЗнÑти Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Select none Ðічого не вибирати Ctrl+Shift+A Ctrl+Shift+A &Invert &Інвертувати Select Invert Інвертувати Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Select invert Інвертувати Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ctrl+I Ctrl+I &Range Ді&апазон Select Range Вибір діапазону Select range ÐŸÐ¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ñ–Ð°Ð¿Ð°Ð·Ð¾Ð½Ñƒ Mark range as selected Позначити діапазон Ctrl+R Ctrl+R Insert Range Ð’Ñтавити діапазон Insert range Ð’Ñтавити діапазон Insert range as selected Ð’Ñтавити діапазон Ñк позначений Ctrl+Ins Ctrl+Ins &Step &Крок Insert step Ð’Ñтавити крок Insert step (rest) Ð’Ñтавити крок (паузу) Right DO NOT TRANSLATE Remove Range Вилучити діапазон Remove range Вилучити діапазон Remove range as selected Вилучити діапазон Ctrl+Del Ctrl+Del &Quantize... К&вантизувати… Quantize Квантизувати Quantize selection Квантизувати позначене &Transpose... Т&ранÑпонувати… Transpose ТранÑпонувати Transpose selection ТранÑпонувати позначене &Normalize... &Ðормалізувати… Normalize Ðормалізувати Normalize selection Ðормалізувати позначене &Randomize... &Рандомізувати... Randomize Випадково Randomize selection Рандомізувати позначене Resi&ze... Змі&нити розмір… Resize Змінити розмір Resize selection Змінити розміри позначеного Re&scale... Змінити маÑ&штаб… Rescale Змінити маÑштаб Rescale selection Змінити маÑштаб позначеного T&imeshift... ЗÑунути за &чаÑом... Timeshift ЗÑув за чаÑом Timeshift selection ЗÑунути за чаÑом позначене T&empo ramp... &Ухил ритму… Tempo ramp Ухил ритму Tempo ramp selection Ухил ритму у позначеному &Menubar Панель &меню Menubar Панель меню Show/hide the menubar Показати або приховати Ñмужку меню Ctrl+M Ctrl+M &Statusbar С&мужка Ñтану Statusbar Смужка Ñтану Show/hide the statusbar Показати або приховати Ñмужку Ñтану File Toolbar Панель інÑтрументів файлів File toolbar Панель інÑтрументів файлів Show/hide the file toolbar Показати або приховати панель інÑтрументів файлів Edit Toolbar Панель Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Edit toolbar Панель Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Show/hide the edit toolbar Показати або приховати панель інÑтрументів Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ View Toolbar Панель інÑтрументів переглÑду View toolbar Панель інÑтрументів переглÑду Show/hide the view toolbar Показати/Приховати панель інÑтрументів переглÑду &Transport П&ÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñ Transport Toolbar Панель інÑтрументів Ð¿ÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñ Transport toolbar Панель інÑтрументів Ð¿ÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñ Show/hide the transport toolbar Показати/Приховати панель інÑтрументів Ð¿ÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñ T&ime &Ð§Ð°Ñ Time Toolbar Панель інÑтрументів чаÑу Time toolbar Панель інÑтрументів чаÑу Show/hide the time toolbar Показати/Приховати панель інÑтрументів чаÑу &Scale &МаÑштаб Scale Toolbar Панель інÑтрументів маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ Scale toolbar Панель інÑтрументів маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ Show/hide the scale toolbar Показати/Приховати панель інÑтрументів маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ Thum&b Мі&ніатюра Thumb Toolbar Панель інÑтрументів мініатюр Thumb toolbar Панель інÑтрументів мініатюр Show/hide the thumb view toolbar Показати/Приховати панель інÑтрументів мініатюр &Events &Події View events ПереглÑд подій Show/hide the events list Показати/Приховати ÑпиÑок подій Note &Names &Ðазви нот Note Names Ðазви нот Note names Whether to show note names Визначає, чи Ñлід показувати назви нот Note &Duration &ТриваліÑть нот Note Duration ТриваліÑть нот Note duration ТриваліÑть нот Whether note events are shown proportional to duration Визначає, чи Ñлід показувати події нот пропорційно до тривалоÑті Note &Color &Колір нот Note Color Колір ноти Note color Колір ноти Whether note events are colored according to pitch Визначає, чи Ñлід розфарбовувати події нот за тоном &Drum Mode Режим &ударних Drum Mode Режим ударних інÑтрументів Drum mode Режим ударних інÑтрументів Whether note onset events are displayed as diamonds Визначає, чи Ñлід показувати наÑтупні події нот ромбами &Value Color Колір зна&Ñ‡ÐµÐ½Ð½Ñ Value Color Колір Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Value color Колір Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Whether note events are colored according to value (velocity) Визначає, чи Ñлід викориÑтовувати Ð´Ð»Ñ Ð¿Ð¾Ð´Ñ–Ð¹ нот колір за значеннÑм (швидкіÑтю) &In &Більше Zoom In Збільшити Zoom in Збільшити Ctrl++ Ctrl++ &Out &Менше Zoom Out Зменшити Zoom out Зменшити Ctrl+- Ctrl+- &Reset &Скинути Zoom Reset Відновити початковий маÑштаб Zoom reset Відновити початковий маÑштаб Ctrl+1 Ctrl+1 &Horizontal &Горизонтально Horizontal Zoom МаÑштабувати за горизонталлю Horizontal zoom МаÑштабувати за горизонталлю Horizontal zoom mode Режим маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð° горизонталлю &Vertical &Вертикально Vertical Zoom Вертикальне маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ Vertical zoom Вертикальне маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ Vertical zoom mode Режим вертикального маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ All Zoom Рівномірне маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ All zoom Рівномірне маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ All zoom mode Режим рівномірного маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ &Zebra Зе&бра Zebra Зебра Bar zebra view mode Режим переглÑду із зеброю Ñтовпчиків &Grid &Сітка Grid Сітка Snap grid view mode Режим переглÑду із прилипаннÑм до Ñітки Too&l Tips Під&казки Tool tips Підказки Floating tool tips view mode Режим переглÑду із рухомими підказками &Refresh &Оновити Refresh Оновити Refresh views ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ð°Ð½ÐµÐ»ÐµÐ¹ переглÑду F5 F5 &Preview Notes П&опередній переглÑд нот Preview Notes Попередній переглÑд нот Preview notes Попередній переглÑд нот Preview notes while editing (scrub) Попереднє проÑÐ»ÑƒÑ…Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ð¾Ñ‚ при редагуванні (гортанні) F&ollow Playhead С&лідувати за позицією Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Follow Playhead Слідувати за позицією Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Follow playhead Слідувати за позицією Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ &Backward &Ðазад Backward Ðазад Transport backward Перехід назад Backspace Backspace Re&wind Пов&ний назад Rewind Повний назад Transport rewind ÐŸÐµÑ€ÐµÐ¼Ð¾Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð½Ð°Ð·Ð°Ð´ F&ast Forward П&овний вперед Fast Forward Повний вперед Fast forward Повний вперед Transport fast forward Швидкий перехід вперед &Forward Ð’&перед Forward Вперед Transport forward Перехід вперед Step Backward Крок назад Step backward Крок назад Transport step backward Перейти на крок назад Step Forward Крок вперед Step forward Крок вперед Transport step forward Перейти на крок вперед Note Backward Ðа ноту назад Step note backward Перейти на ноту назад Transport step note backward Перейти на ноту назад Note Forward Ðа ноту вперед Step note forward Перейти на ноту вперед Transport step note forward Перейти на ноту вперед &Loop &Зациклити Loop Цикл Transport loop Ð—Ð°Ñ†Ð¸ÐºÐ»ÐµÐ½Ð½Ñ Ctrl+Shift+L Ctrl+Shift+L Loop &Set &Ð’Ñтановити цикл Loop Set Ð’Ñтановити цикл Loop set Ð’Ñтановити цикл Transport loop set Ð—Ð°Ñ†Ð¸ÐºÐ»ÐµÐ½Ð½Ñ Ctrl+L Ctrl+L &Stop З&упинити Stop Зупинити Transport stop Зупинити перехід &Play Від&творити Play Відтворити Transport play/pause Відтворити/Призупинити Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Space Пробіл &Record За&Ð¿Ð¸Ñ Transport record Ð—Ð°Ð¿Ð¸Ñ &Punch Ð’&різка Punch Врізка Punch in/out Вхід/Вихід врізки Transport punch in/out Вхід/Вихід врізки Ctrl+Shift+P Ctrl+Shift+P Punch Se&t Ð’ÑÑ‚&ановити врізку Punch Set Ð’Ñтановити врізку Punch in/out set Ð’Ñтановити вхід/вихід врізки Transport punch in/out set Ð’Ñтановити вхід/вихід врізки Ctrl+P Ctrl+P Pa&nic Па&ніка Panic Паніка All MIDI tracks shut off (panic) Повне Ð²Ð¸Ð¼Ð¸ÐºÐ°Ð½Ð½Ñ ÑƒÑÑ–Ñ… доріжок MIDI (паніка) &Shortcuts... С&короченнÑ… Shortcuts Ð¡ÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ Keyboard shortcuts Клавіатурні ÑÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ &About... П&ро програму… About Про програму Show information about this application program Показати відомоÑті щодо цієї програми About &Qt... П&ро Qt… About Qt Про Qt Show information about the Qt toolkit Показати відомоÑті щодо набору інÑтрументів Qt Current time (play-head) Поточний Ñ‡Ð°Ñ (Ð¿Ð¾Ð·Ð¸Ñ†Ñ–Ñ Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ) Current tempo (BPM) Поточний ритм (біти за Ñекунду) Reset time-sig. Скинути позначку чаÑу. Set current snap to %1 Ð’Ñтановити Ð´Ð»Ñ Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¾Ð³Ð¾ Ð¿Ñ€Ð¸Ð»Ð¸Ð¿Ð°Ð½Ð½Ñ %1 Note Velocity ШвидкіÑть ноти Snap/beat ПрилипаннÑ/Біт Note type Тип ноти Value type Тип Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Parameter type Тип параметра Scale key Ключ гами Scale type Тип маÑштабу MIDI clip name Ðазва кліпу MIDI MIDI file name Ðазва файла MIDI MIDI track/channel Доріжка/Канал MIDI MOD МОД MIDI modification state Стан внеÑÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½ до MIDI REC ЗÐП MIDI clip record state Стан запиÑу кліпу MIDI MUTE ВИМ MIDI clip mute state Стан Ð²Ð¸Ð¼Ð¸ÐºÐ°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÑƒ кліпу MIDI 00:00:00.000 00:00:00.000 MIDI clip duration ТриваліÑть кліпу MIDI Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ The current MIDI clip has been changed: "%1" Do you want to save the changes? До поточного кліпу MIDI внеÑено зміни: «%1» Хочете зберегти ці зміни? Save MIDI Clip Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñƒ MIDI MIDI files (*.%1 *.smf *.midi) файли MIDI (*.%1 *.smf *.midi) All files (*.*) уÑÑ– файли (*.*) Channel %1 Канал %1 Track %1 Доріжка %1 [modified] [змінено] qtractorMidiEventList Events Події qtractorMidiEventListView::ItemDelegate edit %1 змінити %1 qtractorMidiEventListView::ItemModel Time Ð§Ð°Ñ Type Тип Name Ðазва Value Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Duration/Data ТриваліÑть/Дані Frame Кадр BBT BBT Note On (%1) Ðоту увімкнено (%1) Note Off (%1) Ðоту вимкнено (%1) Key Press (%1) ÐатиÑÐºÐ°Ð½Ð½Ñ ÐºÐ»Ð°Ð²Ñ–ÑˆÑ– (%1) Controller (%1) Контролер (%1) Control 14 (%1) ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ 14 (%1) RPN (%1) RPN (%1) NRPN (%1) NRPN (%1) Pgm Change Зміна pgm Chan Press ÐатиÑк. кан Pitch Bend Виг. тону SysEx SysEx Meta (%1) Мета (%1) Unknown (%1) Ðевідомо (%1) qtractorMidiListView Name Ðазва Fmt Фмт Tracks Доріжки tpqn tpqn Path ШлÑÑ… %1: MIDI file not found. %1: файл MIDI не знайдено. Open MIDI Files Ð’Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñ„Ð°Ð¹Ð»Ñ–Ð² MIDI MIDI files (*.%1 *.smf *.midi) файли MIDI (*.%1 *.smf *.midi) All files (*.*) уÑÑ– файли (*.*) qtractorMidiMixerMeter Volume (%) ГучніÑть (%) % % Pan: %1 ПанорамуваннÑ: %1 Volume: %1% ГучніÑть %1% qtractorMidiSysexForm MIDI SysEx MIDI SysEx Name Ðазва Size Розмір Data (hex) Дані (шіÑтн.) Import from SysEx file Імпортувати з файла SysEx &Import... &Імпортувати… Export to SysEx file ЕкÑпортувати до файла SysEx E&xport... Е&кÑпортувати… Move SysEx item up on list order ПереÑунути Ð·Ð°Ð¿Ð¸Ñ SysEx вище у ÑпиÑку &Up &Вище Move SysEx item down on list order ПереÑунути Ð·Ð°Ð¿Ð¸Ñ SysEx нижче у ÑпиÑку &Down &Ðижче Open SysEx Відкрити SysEx Sysex name Ðазва Sysex Save SysEx Зберегти SysEx Delete SysEx Вилучити SysEx Create SysEx item Створити Ð·Ð°Ð¿Ð¸Ñ SysEx &Add &Додати Update SysEx item Оновити Ð·Ð°Ð¿Ð¸Ñ SysEx Upda&te О&новити Remove SysEx item Вилучити Ð·Ð°Ð¿Ð¸Ñ SysEx &Remove Ви&лучити Import SysEx Files Імпорт файлів SysEx SysEx files (*.%1) файли SysEx (*.%1) MIDI files (*.mid *.smf *.midi) файли MIDI (*.mid *.smf *.midi) All files (*.*) уÑÑ– файли (*.*) Export SysEx File ЕкÑпорт файла SysEx Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ The SysEx file already exists: "%1" Do you want to replace it? Файл SysEx з такою назвою вже Ñ–Ñнує. «%1» Хочете замінити його? About to replace SysEx: "%1" Are you sure? Ðаказано замінити SysEx: «%1» Ви Ñправді цього хочете? About to delete SysEx: "%1" Are you sure? Ðаказано вилучити SysEx: «%1» Ви Ñправді цього хочете? SysEx settings have been changed. Do you want to apply the changes? До деÑких параметрів SysEx було внеÑено зміни. Хочете заÑтоÑувати ці зміни? Error Помилка SysEx could not be loaded: "%1". Sorry. Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ SysEx: «%1». Вибачте. qtractorMidiThumbView MIDI Thumb view ПереглÑд мініатюр MIDI qtractorMidiToolsForm MIDI Tools ІнÑтрументи MIDI Preset name Ðазва набору Save preset Зберегти шаблон Delete preset Вилучити шаблон &Quantize К&вантизувати Quantize selected events Квантизувати позначені події &Time: &ЧаÑ: Quantize time Квантизувати Ñ‡Ð°Ñ Quantize time percent Квантизувати чаÑтку чаÑу % % &Duration: &ТриваліÑть: Quantize duration Квантизувати триваліÑть Quantize duration percent Квантизувати чаÑтку тривалоÑті S&wing: &КоливаннÑ: Swing-quantize time Коливна ÐºÐ²Ð°Ð½Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ Ñ‡Ð°Ñу Swing-quantize percent Коливна ÐºÐ²Ð°Ð½Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ Ñ‡Ð°Ñтки Swing-quantize type Тип коливної квантизації Linear Лінійна Quadratic Квадратичний Cubic Кубічна &Scale: &МаÑштаб: Scale-quantize key Ключ маÑштабової квантизації Scale-quantize type Тип маÑштабової квантизації &Transpose &ТранÑÐ¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Transpose selected events ТранÑпонувати позначені події &Note: &Ðота: Transpose note Ðота транÑÐ¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Transpose time Ð§Ð°Ñ Ñ‚Ñ€Ð°Ð½ÑÐ¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Transpose time format Формат чаÑу транÑÐ¿Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Frames Кадри Time Ð§Ð°Ñ BBT BBT &Reverse Зворо&тній &Normalize &ÐÐ¾Ñ€Ð¼Ð°Ð»Ñ–Ð·Ð°Ñ†Ñ–Ñ Normalize selected events Ðормалізувати позначені події &Percent: &ВідÑотки: Normalize percent ЧаÑтка нормалізації &Value: &ЗначеннÑ: Normalize value Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð½Ð¾Ñ€Ð¼Ð°Ð»Ñ–Ð·Ð°Ñ†Ñ–Ñ— &Compress С&тиÑнути &Randomize &Випадково Randomize selected events Рандомізувати позначені події Randomize note/pitch Рандомізувати ноту/тон Randomize time Рандомізувати Ñ‡Ð°Ñ Randomize duration Рандомізувати триваліÑть Randomize value Рандомізувати Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Resi&ze Змін&а розміру Resize selected events Змінити розмір позначених подій Resize duration Змінити розмір тривалоÑті Resize duration format Формат зміни розмірів тривалоÑті Resize value Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½Ð¸ розмірів Resize value mode Режим зміни розмірів Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Flat Ðейтральний Ramp Трамплін Resize final value ОÑтаточне Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½Ð¸ розмірів &Legato: &Легато: Legato trim/extend type Тип обрізаннÑ/Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Ð»ÐµÐ³Ð°Ñ‚Ð¾ Normal Звичайний Trim ÐžÐ±Ñ€Ñ–Ð·Ð°Ð½Ð½Ñ Extend Ð Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Legato trim/extend length Триваліть обрізанннÑ/Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Ð»ÐµÐ³Ð°Ñ‚Ð¾ Legato mode Режим легато Mono Моно Poly Полі &Join Об'&єднати &Split: &Поділ: Split notes length Поділ за довжиною нот Split notes offset Поділ за зÑувом нот Relative ВідноÑний Absolute ÐбÑолютний Re&scale Зміна &маÑштабу Rescale selected events Змінити маÑштаб позначених подій Rescale time Ð§Ð°Ñ Ð·Ð¼Ñ–Ð½Ð¸ маÑштабу Rescale duration ТриваліÑть зміни маÑштабу Rescale value Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð¼Ñ–Ð½Ð¸ маÑштабу &Invert &Інвертувати T&imeshift &ЗÑув чаÑу Timeshift selected events ЗÑунути за чаÑом позначені події Timeshift ЗÑув за чаÑом P: P: Timeshift parameter Параметр зÑуву за чаÑом Timeshift parameter (log) Параметр зÑуву за чаÑом (лог.) Timeshift curve Крива зÑуву за чаÑом P = 0 : no change. P > 0 : accelerating shift. P < 0 : slowing down shift. Edit head/tail (blue) markers define the shift range. P = 0 : без змін. P > 0 : пришвидшений зÑув. P < 0 : уповільнений зÑув. Зміною позначок голови Ñ– хвоÑта (Ñині) можна визначити діапазон зÑуву. Timeshift duration ТриваліÑть зÑуву за чаÑом T&empo ramp &Ухил ритму Tempo ramp selected events Tempo ramp Ухил ритму From Від Tempo ramp start Початок ухилу ритму to до Temporamp end Кінець ухилу ритму Edit head/tail (blue) markers define the ramp range. Ð ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð·Ð½Ð°Ñ‡Ð¾Ðº початку/ÐºÑ–Ð½Ñ†Ñ (Ñині) визначає діапазон ухилу. Tempo ramp duration ТриваліÑть ухилу ритму (default) (типове) Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ About to delete preset: "%1" Are you sure? Ðаказано вилучити набір налаштувань: «%1» Ви Ñправді цього хочете? none немає quantize квантизувати transpose transpose normalize Ð½Ð¾Ñ€Ð¼Ð°Ð»Ñ–Ð·Ð°Ñ†Ñ–Ñ randomize рандомізувати resize змінити розмір rescale rescale timeshift зÑунути за чаÑом temporamp temporamp qtractorMixer Inputs ПриÑтрої Ð²Ð²ÐµÐ´ÐµÐ½Ð½Ñ Tracks Доріжки Outputs Виходи Mixer Мікшер qtractorMixerMeter Pan ÐŸÐ°Ð½Ð¾Ñ€Ð°Ð¼ÑƒÐ²Ð°Ð½Ð½Ñ qtractorMixerRackWidget &Inputs Ð’&ходи &Outputs Ð’&иходи &Monitor Сп&оÑтерігати &Buses... &Шини... &Audio З&вук &MIDI &MIDI qtractorMixerStrip inputs входи outputs виходи Connect %1 З'єднати %1 (Audio) (Звук) (MIDI) (MIDI) (None) (Ðемає) In Вхід Out Вихід qtractorMonitorButton monitor монітор qtractorOptionsForm Options ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ &General &Загальне Session Ð¡ÐµÐ°Ð½Ñ Default session &file format: Типовий &формат файлів ÑеанÑу: Default session file format (suffix) Типовий формат файлів ÑеанÑу (ÑуфікÑ) Whether to create new sessions based on template Визначає, чи Ñлід Ñтворювати ÑеанÑи на оÑнові шаблону &New session template: Шаблон &нового ÑеанÑу: New session template Шаблон нового ÑеанÑу Browse for new session template Вказати шаблон нового ÑеанÑу Whether to save backup versions of existing sessions Визначає, чи Ñлід зберігати резервні копії наÑвних ÑеанÑів Save &backup versions of existing sessions: З&берігати резервні копії наÑвних ÑеанÑів: Which mode to rename existing session files Визначає, у Ñкому режимі перейментовувати наÑвні файли ÑеанÑів Increment previous version (default) Збільшувати номер верÑÑ–Ñ— попереднього (типово) Increment current version Збільшувати поточну верÑÑ–ÑŽ Whether to enable session auto-save (crash-recovery) Визначає, чи Ñлід вмикати Ð°Ð²Ñ‚Ð¾Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ ÑеанÑів (Ð²Ñ–Ð´Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ñ–ÑÐ»Ñ Ð°Ð²Ð°Ñ€Ñ–Ñ—) Auto-save current working session every: Ðвтозберігати поточний робочий ÑÐµÐ°Ð½Ñ ÐºÐ¾Ð¶Ð½Ñ–: Auto-save period (minutes) Період Ð°Ð²Ñ‚Ð¾Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ (у хвилинах) minutes хвилин Whether to ask for confirmation on removal Визначає, чи Ñлід питати Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸ вилученні &Confirm removals &Підтверджувати Ð²Ð¸Ð»ÑƒÑ‡ÐµÐ½Ð½Ñ Whether to ask for confirmation on archive directory removal Визначає, чи Ñлід підтверджувати Ð²Ð¸Ð»ÑƒÑ‡ÐµÐ½Ð½Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ñ–Ð² архівів C&onfirm archive removals П&ідтверджувати Ð²Ð¸Ð»ÑƒÑ‡ÐµÐ½Ð½Ñ Ð°Ñ€Ñ…Ñ–Ð²Ñ–Ð² Number of &recent files: КількіÑть &недавніх файлів: The maximum number of recent files to keep in menu МакÑимальна кілкьіÑть пунктів у меню недавніх файлів Whether to capture standard output (stdout/stderr) into messages window Чи Ñлід захоплювати дані зі Ñтандартного Ð²Ð¸Ð²ÐµÐ´ÐµÐ½Ð½Ñ (stdout/stderr) до вікна повідомлень Capture standard &output З&ахоплювати Ñтандартне Ð²Ð¸Ð²ÐµÐ´ÐµÐ½Ð½Ñ Whether to show the complete directory path of loaded session files Визначає, чи Ñлід показувати повний шлÑÑ… до каталогу завантажених файлів ÑеанÑів S&how complete path of session files П&оказувати повний шлÑÑ… до файлів ÑеанÑів Whether to remove audio peak files on session close Визначає, чи Ñлід вилучати файли звукових піків при закритті ÑеанÑу Auto-remove audio pea&k files Ðвтовилучати файли зв&укових піків Whether to keep all tool windows on top of the main window Визначає, чи Ñлід утримувати уÑÑ– вікна інÑтрументів над головним вікном Keep tool &windows always on top &Тримати вікна інÑтрументів згори Whether to try dropping multiple audio files into the same track Визначає, чи Ñлід намагатиÑÑ Ñкинути декілька звукових файлів на одну доріжку &Drop multiple audio files into the same track С&кидати декілька звукових файлів на одну доріжку Whether to reverse keyboard modifiers role on transport positioning (Shift/Ctrl) Визначає, чи Ñлід помінÑти міÑцÑми ролі клавіш-модифікаторів при позиціюванні (Shift/Ctrl) Reverse &keyboard modifiers role (Shift/Ctrl) &Ð ÐµÐ²ÐµÑ€Ñ ÐºÐ»Ð°Ð²Ñ–Ñˆ-модифікаторів (Shift/Ctrl) Whether to reverse mouse middle-button role on keyboard modifiers (Shift/Ctrl) Визначає, чи Ñлід помінÑти міÑцÑми роль Ñередньої кнопки миші при натиÑканні клавіш модифікаторів (Shift/Ctrl) Re&verse middle-button modifier role (Shift/Ctrl) Р&ÐµÐ²ÐµÑ€Ñ Ñ€Ð¾Ð»Ñ– Ñередньої кнопки (Shift/Ctrl) Whether to keep all editor windows on top of the main window Визначає, чи Ñлід утримувати уÑÑ– вікна редактора над головним вікном Keep &editor windows always on top Т&римати вікна редактора згори Transport ÐŸÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñ Transport &mode: Ре&жим передаваннÑ: Transport control mode (JACK) Режим ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÐ´Ð°Ð²Ð°Ð½Ð½Ñм (JACK) None Ðемає Slave Підлеглий Master ОÑновний Full Повний Whether to start as timebase master (JACK) Визначає, чи Ñлід запуÑкати Ñк оÑновний на оÑнові чаÑу (JACK) &Timebase Ðа оÑнові &чаÑу &Loop recording mode (takes): Режим запиÑу &циклів (Ñпроб): Loop recording mode (takes) Режим запиÑу циклів (Ñпроб) First Перша Last ОÑтанній &Audio Зв&ук Capture / Export Ð—Ð°Ñ…Ð¾Ð¿Ð»ÐµÐ½Ð½Ñ / ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ File &type: &Тип файла: Audio file type to use on capture (record) and export Тип звукових файлів, Ñким Ñлід ÑкориÑтатиÑÑ Ð´Ð»Ñ Ð·Ð°Ñ…Ð¾Ð¿Ð»ÐµÐ½Ð½Ñ (запиÑу) та екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Sample &format: &Формат диÑкретизації: Audio sample format to use on capture (record) and export Формат диÑкретизації звуку при захопленні (запиÑуванні) та екÑпортуванні &Quality: &ЯкіÑть: Audio compression quality to use on capture (record) and export ЯкіÑть ÑтиÑÐºÐ°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¸Ñ… даних при захопленні (запиÑуванні) та екÑпортуванні Playback Ð’Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Whether to apply time-stretching when tempo changes Визначає, чи Ñлід заÑтоÑовувати розтÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу при зміні ритму Aut&omatic time-stretching &Ðвтоматичне розтÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу Sample-&rate converter type: Тип перетворювача диÑ&кретизації: Sample-rate converter quality ЯкіÑть перетворювача диÑкретизації Sinc (Best Quality) Sinc (найкраща ÑкіÑть) Sinc (Medium Quality) Sinc (поÑÐµÑ€ÐµÐ´Ð½Ñ ÑкіÑть) Sinc (Fastest) Sinc (найшвидший) Zero Order Hold Ð£Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð½ÑƒÐ»ÑŒÐ¾Ð²Ð¾Ð³Ð¾ порÑдку Linear Лінійний Whether to use WSOLA time-stretching Визначає, чи Ñлід викориÑтовувати розтÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу WSOLA &WSOLA time-stretching РозтÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу &WSOLA Whether to use RubberBand formant preserve Визначає, чи Ñлід викориÑтовувати Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñ„Ð¾Ñ€Ð¼Ð°Ð½Ñ‚Ð¸ RubberBand RubberBand &formant preserve Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ &форманти RubberBand Whether to apply WSOLA quick seek time-stretching Визначає, чи Ñлід заÑтоÑовувати розтÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡Ð°Ñу при швидкому позиціюванні WSOLA WSOLA quic&k seek &Швидке Ð¿Ð¾Ð·Ð¸Ñ†Ñ–ÑŽÐ²Ð°Ð½Ð½Ñ WSOLA Whether to use RubberBand R3 finer engine Визначає, чи Ñлід викориÑтовувати точний рушій R3 RubberBand RubberBand R&3 finer engine Точний рушій R&3 RubberBand Whether to have separate audition/pre-listening player output ports Визначає, чи Ñлід викориÑтовувати окремі порти Ð²Ð¸Ð²ÐµÐ´ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð²Ð°Ñ‡Ð° попереднього проÑÐ»ÑƒÑ…Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Dedicated au&dition/pre-listening player outputs: ВлаÑні виходи програвача попе&реднього проÑлуховуваннÑ: Whether to auto-connect dedicated audio player outputs Визначає, чи Ñлід автоматично з'єднуватиÑÑ Ð·Ñ– Ñпеціальними виходами звукового програвача Auto-&connect &Ðвтоматичне з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Connections З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Whether to warn about audio self-connections Визначає, чи Ñлід попереджати про Ñамоз'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¸Ñ… каналів &Warn about self-connections &Попереджати про Ñамоз'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Metronome Метроном Whether to enable the audio metronome Визначає, чи Ñлід вмикати звуковий метроном &Enable audio metronome &Увімкнути звуковий метроном &Count-in: Відлі&к: Count-in mode Режим відліку Recording Ð—Ð°Ð¿Ð¸Ñ Count-in number ЧиÑло Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ beats бітів &File (bar): &Файл (такт): Metronome Audio filename (bar) Ðазва звукового файла метронома (такт) Browse for sample audio file (bar) Перейти до звукового файла зразка (такт) &Gain (bar): &ПідÑÐ¸Ð»ÐµÐ½Ð½Ñ (такт): Metronome gain (bar) ПідÑÐ¸Ð»ÐµÐ½Ð½Ñ Ð¼ÐµÑ‚Ñ€Ð¾Ð½Ð¾Ð¼Ð° (такт) dB дБ &File (beat): &Файл (біт): Metronome Audio filename (beat) Ðазва звукового файла метронома (біт) Browse for sample audio file (beat) Перейти до звукового файла зразка (біт) &Gain (beat): &ПідÑÐ¸Ð»ÐµÐ½Ð½Ñ (біт): Metronome gain (beat) ПідÑÐ¸Ð»ÐµÐ½Ð½Ñ Ð¼ÐµÑ‚Ñ€Ð¾Ð½Ð¾Ð¼Ð° (біт) Whether to have separate audio metronome output ports Визначає, чи Ñлід викориÑтовувати окремі звукові порти виходу метронома Dedicated a&udio metronome outputs: &Окремі виходи звукового метронома: Whether to auto-connect dedicated audio metronome outputs Визначає, чи Ñлід автоматично з'єднуватиÑÑ Ñ–Ð· окремими виходами метронома Auto-co&nnect &Ðвтоматичне з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ &Offset (latency): ЗÑ&ув (латентніÑть): Metronome Audio offset (latency) ЗÑув (латентніÑть) звукового метронома &MIDI &MIDI File &format: Фор&мат файла: MIDI file format to use on capture (record) and export Формат файлів MIDI Ð´Ð»Ñ Ð·Ð°Ñ…Ð¾Ð¿Ð»ÐµÐ½Ð½Ñ (запиÑуваннÑ) та екÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ &Quantize: К&вантизувати: MIDI capture (record) quantization ÐšÐ²Ð°Ð½Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¸ захопленні (запиÑуванні) MIDI Queue &timer (resolution): &Таймер черги (роздільніÑть): Queue timer (resolution) Таймер черги (роздільніÑть) Whether to enable MIDI queue time drift correction Визначає, чи Ñлід увімкнути Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ñуву за чаÑом черги MIDI E&nable MIDI queue time drift correction Уві&мкнути Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ñуву за чаÑом черги MIDI Whether to have separate MIDI player output ports Визначає, чи Ñлід викориÑтовувати окремі порти виходу Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð²Ð°Ñ‡Ð° MIDI Dedicated MIDI p&layer outputs Окремі виходу Ð´Ð»Ñ Ð¿Ñ€Ð¾&гравача MIDI Whether to reset/resend all controllers on playback start Визначає, чи Ñлід відновлювати початкові параметри Ð´Ð»Ñ ÑƒÑÑ–Ñ… контролерів на початку Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ &Reset all controllers on playback start С&кинути уÑÑ– контролери на початку Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Control ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ &MMC: &MMC: MIDI Machine Control (MMC) mode Режим MIDI Machine Control (MMC) Input Вхід Output Вихід Duplex Двобічний &Device: П&риÑтрій: MIDI Machine Control (MMC) device id. Ідентифікатор приÑтрою MIDI Machine Control (MMC). &SPP: &SPP: MIDI Song Position pointer (SPP) control mode Режим ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ MIDI Song Position pointer (SPP) MIDI Cloc&k: &Годинник MIDI: MIDI Clock control mode Режим ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð³Ð¾Ð´Ð¸Ð½Ð½Ð¸ÐºÐ¾Ð¼ MIDI Whether to have separate MIDI control ports Визначає, чи Ñлід викориÑтовувати окремі порти ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ MIDI Dedicated MIDI &control input/output &Окреме ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ñ…Ð¾Ð´Ð¾Ð¼-виходом MIDI Whether to enable the MIDI metronome Визначає, чи Ñлід вмикати метроном MIDI &Enable MIDI metronome &Увімкнути метроном MIDI &Channel: &Канал: Metronome MIDI channel Канал MIDI метронома &Note (bar): &Ðота (такт): Metronome MIDI note (bar) Ðота метронома MIDI (такт) &Velocity (bar): &ШвидкіÑть (такт): Metronome MIDI velocity (bar) ШвидкіÑть метронома MIDI (такт) &Duration (bar): &ТриваліÑть (такт): Metronome MIDI duration (bar) ТриваліÑть метронома MIDI (такт) &Note (beat): &Ðота (біт): Metronome MIDI note (beat) Ðота метронома MIDI (біт) &Velocity (beat): &ШвидкіÑть (біт): Metronome MIDI velocity (beat) ШвидкіÑть метронома MIDI (біт) &Duration (beat): &ТриваліÑть (біт): Metronome MIDI duration (beat) ТриваліÑть метронома MID (біт) Whether to have separate MIDI metronome output port Визначає, чи Ñлід викориÑтовувати окремий порт виходу метронома MIDI Dedicated M&IDI metronome output Окремий вихід метронома M&IDI Metronome MIDI offset (latency) ЗÑув (латентніÑть) метронома MIDI &Display &Показ Defaults Типові параметри &Time display format: Формат показу &чаÑу: Time display format Формат показу чаÑу Frames Кадри Time Ð§Ð°Ñ BBT BBT &Base font size: &ОÑновний розмір шрифту: Base application font size (pt.) Базовий розмір шрифту програми (у пунктах) (default) (типово) 6 6 7 7 8 8 9 9 10 10 11 11 12 12 Whether to use desktop environment native dialogs. Визначає, чи Ñлід викориÑтовувати влаÑні вікна Ñтільничного Ñередовища. Use desktop environment &native dialogs &ВлаÑні вікна Ñтільничного Ñередовища Whether to hold auto-scrolling (follow play-head) on edits. Визначає, чи Ñлід автоматично гортати вміÑÑ‚ (Ñлідувати за позицією відтвореннÑ) при редагуванні. &Hold auto-scrolling (follow play-head) on edits &ÐÐ²Ñ‚Ð¾Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ (за відтвореннÑм) при редагуванні Trac&k color saturation: &ÐаÑиченіÑть кольору доріжки: Default new track color saturation Типова наÑиченіÑть кольору нової доріжки % % Custom Ðетипове &Color theme: Тема &кольорів: Custom color palette theme Ðетипова тема палітри кольорів Wonton Soup Wonton Soup KXStudio KXStudio Manage custom color palette themes Керувати нетиповими темами палітри кольорів ... ... &Style theme: Тема Ñ&тилю: Custom widget style theme Ðетипова тема Ñтилю віджетів &Icons theme: Тема п&іктограм: Custom icons theme directory Каталог нетипової теми піктограм Browse for custom icons theme directory Вказати каталог нетипової теми піктограм St&yle sheet: &Ð¢Ð°Ð±Ð»Ð¸Ñ†Ñ Ñтилів: Custom style sheet (*.qss) Ðетипова Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ Ñтилів (*.qss) Browse for custom style sheet (*.qss) Вибрати нетипову таблицю Ñтилів (*.qss) Meters Індикатори &Audio: &Звук: Audio meter level Рівень індикатора звуку Over Згори 0 dB 0 дБ 3 dB 3 дБ 6 dB 6 дБ 10 dB 10 дБ Back Ðазад Audio meter color Колір індикатора звуку Select custom audio meter color Виберіть нетиповий колір індикатора звуку &MIDI: &MIDI: MIDI meter level Рівень індикатора MIDI Peak Пік MIDI meter color Колір індикатора MIDI Select custom MIDI meter color Виберіть нетиповий колір індикатора MIDI Reset meter colors to default Відновити типові кольори індикаторів &Reset &Скинути Messages ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Sample messages text font display Зразок показу шрифту текÑту повідомлень Select font for the messages text display Вибрати шрифт Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ текÑту повідомлень &Font... &Шрифт… Whether to keep a maximum number of lines in the messages window Чи Ñлід обмежувати макÑимальну кількіÑть Ñ€Ñдків у вікні повідомлень M&essages limit: &ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ: The maximum number of message lines to keep in view МакÑимальна кількіÑть Ñ€Ñдків повідомлень, Ñкі Ñлід зберігати на панелі переглÑду lines Ñ€Ñдків Logging Ð–ÑƒÑ€Ð½Ð°Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Messages log file Файл журналу повідомлень Browse for the messages log file location Вибрати Ñ€Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð° журналу повідомлень Whether to activate a messages logging to file. Чи Ñлід активувати Ð¶ÑƒÑ€Ð½Ð°Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ до файла. Messages &log file: Файл повідомлень &журналу: &Plugins Д&одатки Paths ШлÑхи Plugin type Тип додатка Plugin path ШлÑÑ… до додатків Browse plugin path Вказати шлÑÑ… до додатків Add plugin path Додати шлÑÑ… до додатків &Add &Додати Plugin paths ШлÑхи до додатків Remove plugin path Вилучити шлÑÑ… до додатків &Remove Ви&лучити Move up path ПереÑунути шлÑÑ… вище &Up &Вище Move down path ПереÑунути шлÑÑ… нижче &Down &Ðижче &LV2 Presets directory: Каталог шаблонів &LV2: LV2 Presets directory (default: ~/.lv2) Каталог шаблонів LV2 (типовим Ñ” ~/.lv2) Browse LV2 Presets directory Вказати каталог шаблонів LV2 Instruments ІнÑтрументи Whether to have separate audio output ports Визначає, чи Ñлід викориÑтовувати окремі порти виходу звуку Dedicated audi&o outputs: Окремі виходи зв&уку: Whether to auto-connect dedicated audio output ports Визначає, чи Ñлід автоматично з'єднуватиÑÑ Ð· окремими портами виходу звуку Au&to-connect &Ðвтоматичне з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Editor Редактор Whether to open plugin's editor (GUI) by default Визначає, чи Ñлід типово відкривати редактор додатка (графічний інтерфейÑ) Open plugin's &editor (GUI) by default Типово від&кривати редактор додатка Whether to select plugin's editor (GUI) if more than one are available Визначає, чи Ñлід вибирати редактор додатка, Ñкщо доÑтупними Ñ” декілька редакторів &Select plugin's editor (GUI) if more than one are available Ви&бирати редактор додатка, Ñкщо доÑтупні декілька Blacklist «Чорний» ÑпиÑок Plugin blacklist path ШлÑÑ… «чорного» ÑпиÑку додатків Browse plugin blacklist path Вказати шлÑÑ… «чорного» ÑпиÑку додатків Add plugin blacklist path Додати шлÑÑ… «чорного» ÑпиÑку додатків Plugin blacklist paths ШлÑхи «чорного» ÑпиÑку додатків Remove plugin blacklist path Вилучити шлÑÑ… «чорного» ÑпиÑку додатків Clear plugin blacklist paths Спорожнити шлÑхи «чорного» ÑпиÑку додатків &Clear Сп&орожнити XML Default (*.%1) типовий XML (*.%1) XML Regular (*.%1) звичайний XML (*.%1) ZIP Archive (*.%1) архів ZIP (*.%1) (Any) (будь-Ñкий) Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Some settings have been changed. Do you want to apply the changes? До деÑких параметрів було внеÑено зміни. Хочете заÑтоÑувати ці зміни? Metronome Bar Audio File Звуковий файл такту метронома Metronome Beat Audio File Звуковий файл біту метронома Open Style Sheet Ð’Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ– Ñтилів Style Sheet files (*.%1) файли таблиць Ñтилів (*.%1) Icons Theme Directory Каталог нетипової теми піктограм Audio Meter Color Колір звукового індикатора MIDI Meter Color Колір індикатора MIDI Plug-in Directory Каталог додатків LV2 Presets Directory Каталог шаблонів LV2 Plug-in Blacklist «Чорний» ÑпиÑок додатків Plug-in files (*.%1) файли додатків (*.%1) All files (*.*) уÑÑ– файли (*.*) Messages Font Шрифт повідомлень Messages Log Журнал повідомлень Log files (*.%1) Файли журналів (*.%1) Session Template Шаблон ÑеанÑу Session template files (*.qtr *.qts *.%1) файли шаблонів ÑеанÑу (*.qtr *.qts *.%1) qtractorPaletteForm Color Themes Теми кольорів Name Ðазва Current color palette name Ðазва поточної палітри кольорів Save current color palette name Зберегти назву поточної палітри кольорів Save Зберегти Delete current color palette name Вилучити назву поточної палітри кольорів Delete Вилучити Palette Палітра Current color palette Поточна палітра кольорів Generate: Створити: Base color to generate palette Базовий колір Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð°Ð»Ñ–Ñ‚Ñ€Ð¸ Reset all current palette colors Скинути уÑÑ– кольори поточної палітри Reset Скинути Import a custom color theme (palette) from file Імпортувати нетипову тему кольорів (палітру) з файла Import... Імпортувати… Export a custom color theme (palette) to file ЕкÑпортувати нетипову тему кольорів (палітру) до файла Export... ЕкÑпортувати… Show Details Показати подробиці Import File - %1 Імпорт файла – %1 Palette files (*.%1) файли палітр (*.%1) Save Palette - %1 Ð—Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ð°Ð»Ñ–Ñ‚Ñ€Ð¸ – %1 All files (*.*) уÑÑ– файли (*.*) Warning - %1 ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ â€“ %1 Could not import from file: %1 Sorry. Ðе вдалоÑÑ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ñ‚Ð¸ дані з файла: %1 Вибачте. Export File - %1 ЕкÑпорт файла – %1 Some settings have been changed. Do you want to discard the changes? Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´ÐµÑких параметрів було змінено. Хочете відкинути внеÑені зміни? Some settings have been changed: "%1". Do you want to save the changes? ДеÑкі з параметрів було змінено: «%1». Хочете зберегти внеÑені зміни? qtractorPaletteForm::PaletteModel Color Role Роль кольору Active Ðктивний Inactive Ðеактивний Disabled Вимкнений qtractorPasteRepeatForm Paste Repeat Ð’Ñтавити Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ Repeat ÐŸÐ¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ &Count: &КількіÑть: Repeat count КількіÑть повторів &Period: &Період: Repeat period Період Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ Repeat period format Формат періоду Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ Frames Кадри Time Ð§Ð°Ñ BBT BBT Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Some settings have been changed. Do you want to apply the changes? До деÑких параметрів було внеÑено зміни. Хочете заÑтоÑувати ці зміни? qtractorPluginForm Plugin Properties ВлаÑтивоÑті додатка Open preset Відкрити набір налаштувань Preset name Ðазва набору Save preset Зберегти шаблон Delete preset Вилучити шаблон Alias: Ðльтернативна назва: Plugin alias Ðльтернативна назва додатка Edit plugin Ð ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ° Edit Змінити Active Ðктивний About Про програму Outputs (Sends) Виходи (ÐадÑиланнÑ) Sends ÐадÑилає Inputs (Returns) Входи (ПоверненнÑ) Returns Повертає Auto-connect Ðвтоз'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Aux Send Bus: Доп. шина надÑиланнÑ: Manage buses ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ¸Ð½Ð°Ð¼Ð¸ ... ... Audio bus I/O matrix ÐœÐ°Ñ‚Ñ€Ð¸Ñ†Ñ Ð’/Ð’ звукової шини I/O Matrix... ÐœÐ°Ñ‚Ñ€Ð¸Ñ†Ñ Ð’/В… Direct Access Parameter Параметр безпоÑереднього доÑтупу Direct Access БезпоÑередній доÑтуп Page %1 Сторінка %1 %1 [%2], %3 instance(s), %4 channel(s). %1 [%2], %3 екземплÑрів, %4 каналів. (none) (немає) Open Preset Відкрити набір Preset files (*.%1) файли наборів (*.%1) All files (*.*) уÑÑ– файли (*.*) Error Помилка Preset could not be loaded from file: "%1". Sorry. Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ набір налаштувань з файла: «%1». Вибачте. Save Preset Зберегти шаблон Preset could not be saved to file: "%1". Sorry. Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ набір налаштувань до файла: «%1». Вибачте. Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ About to delete preset: "%1" (%2) Are you sure? Ðаказано вилучити набір налаштувань: «%1» (%2) Ви Ñправді цього хочете? &None &Ðемає Latency: %1 ms (%2 frames) ЛатентніÑть: %1 Ð¼Ñ (%2 кадрів) (no latency) (немає латентноÑті) qtractorPluginListView copy plugin копіювати додаток activate all plugins задіÑти уÑÑ– додатки deactivate all plugins вимкнути уÑÑ– додатки remove all plugins вилучити уÑÑ– додатки Import Plugins Імпорт додатків XML files (*.%1) файли XML (*.%1) All files (*.*) уÑÑ– файли (*.*) Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ About to remove and import all plugins: "%1" Are you sure? Ðаказано вилучити та імпортувати уÑÑ– додатки: «%1» Ви Ñправді цього хочете? Export Plugins ЕкÑпорт додатків Aux Send: ÐадÑÐ¸Ð°Ð»Ð°Ð½Ð½Ñ Ð´Ð¾Ð¿Ð¾Ð¼Ñ–Ð¶Ð½Ð¾Ð³Ð¾: &Move Here ПереÑ&унути Ñюди &Copy Here С&копіювати Ñюди C&ancel &СкаÑувати &Add Plugin... &Додати додаток… I&nserts &Ð’ÑÑ‚Ð°Ð²Ð»ÐµÐ½Ð½Ñ &Audio Зв&ук Add &Insert Додати вÑÑ‚&Ð°Ð²Ð»ÐµÐ½Ð½Ñ Add &Aux Send Додати &доп. надÑÐ¸Ð»Ð°Ð½Ð½Ñ &Sends &ÐадÑÐ¸Ð»Ð°Ð½Ð½Ñ &Returns &ÐŸÐ¾Ð²ÐµÑ€Ð½ÐµÐ½Ð½Ñ &MIDI &MIDI Add &Controller Додати &контролер Ac&tivate &Увімкнути Acti&vate All Уві&мкнути уÑÑ– Deactivate Al&l Ð’&имкнути уÑÑ– &Remove Ви&лучити Re&move All Вилу&чити вÑÑ– Move &Up ПереÑунути &вище Move &Down ПереÑунути &нижче Pre&set &Ðабір Dire&ct Access &БезпоÑередній доÑтуп &None &Ðемає &Properties... Вл&аÑтивоÑті… &Edit З&міни &Import... &Імпортувати… E&xport... Е&кÑпортувати… &Outputs Ð’&иходи &Dedicated &Призначений &Auto-connect &Ðвтоматичне з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ qtractorPluginParamWidget Open File Ð’Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñ„Ð°Ð¹Ð»Ð° qtractorPluginSelectForm Plugins Додатки Reset filter Скинути Ñ„Ñ–Ð»ÑŒÑ‚Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ X X Plugin search string (regular expression) РÑдок пошуку додатка (формальний вираз) Plugin type Тип додатка Available plugins ÐаÑвні додатки Name Ðазва Audio Звук MIDI MIDI Control ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Modes Режими Path ШлÑÑ… Index Покажчик Instances ЕкземплÑри Type Тип Rescan for available plugins (refresh) Повторний пошук доÑтупних додатків (оÑвіженнÑ) &Rescan П&ереÑканувати Plugin scanning in progress... Виконуємо пошук додатків… GUI Ð†Ð½Ñ‚ÐµÑ€Ñ„ÐµÐ¹Ñ EXT EXT RT РЧ qtractorSessionForm Session Ð¡ÐµÐ°Ð½Ñ &Name: &Ðазва: Session name Ðазва ÑеанÑу &Directory: &Каталог: Whether to auto-name the session directory Чи Ñлід автоматично називати каталог ÑеанÑу &Auto &Ðвто Session directory Каталог ÑеанÑів Browse for session directory Перейти до каталогу ÑеанÑу ... ... &Description: &ОпиÑ: Session description ÐžÐ¿Ð¸Ñ ÑеанÑу Properties ВлаÑтивоÑті Time Ð§Ð°Ñ Sample &Rate: &ЧаÑтота диÑкретизації: Sample rate (Hz) ЧаÑтота диÑкретизації (Гц) 44100 44100 48000 48000 96000 96000 192000 192000 &Tempo: &Ритм: Tempo (BPM) / Signature Ритм (біт/Ñ) / ÐŸÑ–Ð´Ð¿Ð¸Ñ T&icks/Beat: &Такти/Біти: Resolution (ticks/beat; tpqn) РоздільніÑть (такти/біти; tpqn) View ПереглÑд &Snap/Beat: &ПрилипаннÑ/біт: Snap/beat ПрилипаннÑ/біт &Pixels/Beat: Пі&кÑелі/біт: Pixels/beat ПікÑелі/біт &Horizontal Zoom: &Горизонтальний маÑштаб: Horizontal Zoom (%) Горизонтальний маÑштаб (%) % % &Vertical Zoom: &Вертикальний маÑштаб: Vertical Zoom (%) Вертикальний маÑштаб (%) Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Session directory does not exist: "%1" Do you want to create it? Каталогу ÑеанÑу не Ñ–Ñнує: «%1» Хочете його Ñтворити? Some settings have been changed. Do you want to apply the changes? До деÑких параметрів було внеÑено зміни. Хочете заÑтоÑувати ці зміни? Session Directory Каталог ÑеанÑу qtractorShortcutForm Shortcuts Ð¡ÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ Shortcut search string (regular expression) РÑдок пошуку ÑÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ (формальний вираз) Menu/Action Меню/Ð”Ñ–Ñ Description ÐžÐ¿Ð¸Ñ Keyboard Клавіатура MIDI Controller MIDI-контролер Search shortcuts Пошук Ñкорочень Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Keyboard shortcut (%1) already assigned (%2). Клавіатурне ÑÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ (%1) вже пов'Ñзано із дією (%2). Keyboard shortcuts have been changed. Do you want to apply the changes? Клавіатурні ÑÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ Ð±ÑƒÐ»Ð¾ змінено. Хочете заÑтоÑувати зміни? MIDI Controller shortcuts have been changed. Do you want to apply the changes? Клавіатурні ÑÐºÐ¾Ñ€Ð¾Ñ‡ÐµÐ½Ð½Ñ MIDI-контролера було змінено. Хочете заÑтоÑувати зміни? &MIDI Controller... &MIDI-контролер... qtractorTakeRangeForm Take Range Діапазон Ð´ÑƒÐ±Ð»Ñ Range Діапазон En&d: &Кінець: Selection range Діапазон Ð¿Ð¾Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ &Selection Поз&Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Edit range Змінити діапазон &Edit З&міни Custom range Ðетиповий діапазон &Custom Ð&етиповий Loop range Діапазон циклу &Loop &Зациклити Clip start Початок кліпу Clip offset Ð—Ð¼Ñ–Ñ‰ÐµÐ½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñƒ St&art: &Початок: Punch range Діапазон врізки &Punch Ð’&різка Select Вибір Current take Поточний дубль Format Формат Time display format Формат показу чаÑу Frames Кадри Time Ð§Ð°Ñ BBT BBT Take %1 Дубль %1 qtractorTempoAdjustForm Tempo Adjust Скоригувати ритм Metronome Метроном &Tempo: &Ритм: Tempo/Time signature Ритм/ЧаÑовий Ð¿Ñ–Ð´Ð¿Ð¸Ñ &Detect Ви&Ñвити T&ap R&eset С&кинути Range Діапазон &Start: &Початок: Range start Початок діапазону &Length: &ТриваліÑть: Range length ТриваліÑть діапазону &Beats: &Біти: Range beats Діапазон бітів A&djust С&коригувати Format Формат Time display format Формат показу чаÑу Frames Кадри Time Ð§Ð°Ñ BBT BBT Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Some settings have been changed. Do you want to apply the changes? До деÑких параметрів було внеÑено зміни. Хочете заÑтоÑувати ці зміни? qtractorThumbView Thumb view ПереглÑд мініатюр qtractorTimeScale C C B# B# C# C# Db Db D D D# D# Eb Eb E E Fb Fb F F E# E# F# F# Gb Gb G G G# G# Ab Ab A A A# A# Bb Bb B B Cb Cb qtractorTimeScaleForm Tempo Map / Markers Карта / Позначки ритму Tempo map / Markers Карта / Позначки ритму Bar Стовпчик Time Ð§Ð°Ñ Tempo Ритм Key Ключ Marker Позначка &Bar: С&товпчик: Bar location Ð Ð¾Ð·Ñ‚Ð°ÑˆÑƒÐ²Ð°Ð½Ð½Ñ Ñтовпчика T&ime: &ЧаÑ: Time/frame location МіÑце за чаÑом/кадром &Tempo: &Ритм: Tempo (BPM) / Time signature ÐŸÑ–Ð´Ð¿Ð¸Ñ Ñ€Ð¸Ñ‚Ð¼Ñƒ (біт/хв) / чаÑу T&ap &Відбити &Key signature: &ÐŸÑ–Ð´Ð¿Ð¸Ñ ÐºÐ»Ð°Ð²Ñ–ÑˆÑ–: Key signature (accidentals) Ключ (неÑуттєвий) Key signature (mode) Ключ (режим) - - Major Мажор Minor Мінор &Marker: Поз&начка: Marker text ТекÑÑ‚ позначки Marker color Колір позначки ... ... Tempo &scale factor: Коефіцієнт &маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€Ð¸Ñ‚Ð¼Ñƒ: Tempo scale factor Коефіцієнт маÑÑˆÑ‚Ð°Ð±ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€Ð¸Ñ‚Ð¼Ñƒ App&ly З&аÑтоÑувати Refresh tempo map ОÑвіжити карту ритму Re&fresh О&новити Add node Додати вузол &Add &Додати Update node Оновити вузол &Update &Оновити Remove node Вилучити вузол &Remove Ви&лучити Close this dialog Закрити це вікно Close Закрити Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Some settings have been changed. Do you want to apply the changes? До деÑких параметрів було внеÑено зміни. Хочете заÑтоÑувати ці зміни? About to remove tempo node: %1 (%2) %3 %4/%5 Are you sure? Ðаказано вилучити вузол ритму: %1 (%2) %3 %4/%5 Ви Ñправді цього хочете? Some settings have been changed. Do you want to discard the changes? Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´ÐµÑких параметрів було змінено. Хочете відкинути внеÑені зміни? tempo factor коефіцієнт ритму Marker Color Колір маркера &Refresh &Оновити qtractorTimeSpinBox &Frames &Кадри &Time &Ð§Ð°Ñ &BBT &BBT qtractorTrackForm Track Доріжка &Name: &Ðазва: Track name description ÐžÐ¿Ð¸Ñ Ð½Ð°Ð·Ð²Ð¸ доріжки Track icon Піктограма доріжки Type Тип Audio track type Тип звукової доріжки &Audio Зв&уковий MIDI track type Тип доріжки MIDI &MIDI &MIDI Input / Output Вхід/Вихід Input bus name Ðазва вхідної шини Output bus name Ðазва вихідної шини Manage buses ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÑˆÐ¸Ð½Ð°Ð¼Ð¸ ... ... MIDI / Instrument MIDI / ІнÑтрумент &Program: &Програма: &Bank: &Банк: Bank &Select Method: СпоÑіб ви&бору банку: MIDI Omni: Capture All Channels MIDI Omni: захопити уÑÑ– канали &Omni &УÑÑ– &Channel: &Канал: MIDI Channel (1-16) Канал MIDI (1-16) MIDI Patch: Instrument MIDI-патч: інÑтрумент MIDI Patch: Bank Select Method MIDI-патч: ÑпоÑіб вибору банку MIDI Patch: Drum Mode MIDI-патч: режим ударних &Drums &Ударні MIDI Patch: Bank MIDI-патч: банк MIDI Patch: Program MIDI-патч: програма View / Colors ПереглÑд/Кольори &Foreground: П&ередній план: Foreground color Колір переднього плану Select custom track foreground color Вибрати нетиповий колір переднього плану доріжки Bac&kground: &Тло: Background color Колір тла Select custom track background color Вибрати нетиповий колір тла доріжки Auto Ðвто Plugins Додатки Track plugins Додатки доріжок Add plugin Додати додаток &Add... &Додати… Remove plugin Вилучити додаток &Remove Ви&лучити Move plugin up ПереÑунути додаток вище &Up &Вище Move plugin down ПереÑунути додаток нижче &Down &Вниз Whether to enable plugin latency/delay compensation Визначає, чи Ñлід вмикати компенÑацію латентноÑті/затримки додатка &Latency compensation КомпенÑÐ°Ñ†Ñ–Ñ &латентноÑті Current total latency Поточна загальна латентніÑть Normal Звичайний Bank MSB MSB банку Bank LSB LSB банку Patch Латка Custom &Icon... Ðетипова &піктограма... Drum &Kit &Ðабір ударних &Bass &БаÑові A&coustic Bass Ðк&уÑтична баÑ-гітара &Guitar &Гітара &Electric Guitar &Електрична гітара &Piano К&лавішні &Acoustic Piano &ÐкуÑтичні клавішні &Microphone &Мікрофон Vi&ntage Microphone Ста&ровинний мікрофон &Speaker &Гучномовець &Trumpet &Труба &Violin С&крипка (None) (Ðемає) Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Some settings have been changed. Do you want to apply the changes? До деÑких параметрів було внеÑено зміни. Хочете заÑтоÑувати ці зміни? (No instrument) (Ðемає інÑтрумента) Track Icon Піктограма доріжки Image files (%1) файли зображень (%1) All files (*.*) уÑÑ– файли (*.*) Foreground Color Колір переднього плану Background Color Колір тла %1 ms (%2 frames) %1 Ð¼Ñ (%2 кадрів) (no latency) (немає латентноÑті) qtractorTrackList Nr â„– Track Name Ðазва доріжки Bus Шина Ch Кан Patch Латка Instrument ІнÑтрумент qtractorTrackTime Play-head ÐŸÐ¾Ð·Ð¸Ñ†Ñ–Ñ Ð²Ñ–Ð´Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Edit-head ÐŸÐ¾Ð·Ð¸Ñ†Ñ–Ñ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Edit-tail ХвіÑÑ‚ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Loop-start Початок циклу Loop-end Кінець циклу Punch-in Вхід врізки Punch-out Вихід врізки Start: %1 End: %2 Length: %3 Початок: %1 Кінець: %2 ТриваліÑть: %3 qtractorTrackView Zoom in (horizontal) Збільшити (горизонтально) Zoom out (horizontal) Зменшити (горизонтально) Zoom in (vertical) Збільшити (вертикально) Zoom out (vertical) Зменшити (вертикально) Zoom reset Відновити початковий маÑштаб add clip додати кліп Start: %1 End: %2 Length: %3 Початок: %1 Кінець: %2 ТриваліÑть: %3 clip %1 кліп %1 fade-in нароÑÑ‚Ð°Ð½Ð½Ñ fade-out згаÑÐ°Ð½Ð½Ñ clip stretch розтÑÐ³Ð½ÐµÐ½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñƒ clip resize зміна розмірів кліпу clip repeat Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñƒ %1 automation Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ð·Ð°Ñ†Ñ–Ñ %1 cut вирізати delete вилучити %1 clip %1 кліп split розділити move clip переÑунути кліп paste clip вÑтавити кліп move automation переÑунути автоматизацію paste automation вÑтавити автоматизацію qtractorTracks Tracks Доріжки new clip новий кліп mute clip вимкнути звук кліпу split clip розділити кліп clip normalize нормалізувати кліп quantize квантизувати transpose transpose normalize Ð½Ð¾Ñ€Ð¼Ð°Ð»Ñ–Ð·Ð°Ñ†Ñ–Ñ randomize рандомізувати resize змінити розмір rescale rescale timeshift зÑунути за чаÑом tempo ramp ухил ритму clip import імпортувати кліп Audio file import "%1" on %2 %3. Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¾Ð³Ð¾ файла «%1» на %2 %3. Audio file import: "%1". Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¾Ð³Ð¾ файла: «%1». MIDI file import "%1" track-channel %2 on %3 %4. Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð° MIDI «%1», доріжка-канал %2 на %3 %4. MIDI file import: "%1", track-channel: %2. Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð° MIDI: «%1», доріжка-канал: %2. clip merge об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñ–Ð² Merge/Export Об'єднаннÑ/ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Merge/Export Audio Clip Об'єднаннÑ/ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¸Ñ… кліпів Audio clip merge/export: "%1" started... Об'єднаннÑ/ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¸Ñ… кліпів: розпочато «%1»... Audio clip merge/export: "%1" complete. Об'єднаннÑ/ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÐ¾Ð²Ð¸Ñ… кліпів: завершено «%1». Merge/Export MIDI Clip Об'єднаннÑ/ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñƒ MIDI MIDI files (*.mid *.smf *.midi) файли MIDI (*.mid *.smf *.midi) All files (*.*) уÑÑ– файли (*.*) MIDI clip merge/export: "%1" started... Об'єднаннÑ/ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñ–Ð² MIDI: розпочато «%1»... MIDI clip merge/export: "%1" complete. Об'єднаннÑ/ЕкÑÐ¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñ–Ð² MIDI: завершено «%1». clip cross-fade перехреÑне згаÑÐ°Ð½Ð½Ñ ÐºÐ»Ñ–Ð¿Ñ–Ð² Insert Range Ð’Ñтавити діапазон insert range вÑтавити діапазон insert track range вÑтавити діапазон доріжки Remove Range Вилучити діапазон remove range вилучити діапазон remove track range вилучити діапазон доріжки Warning ÐŸÐ¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ About to remove track: "%1" Are you sure? Ðаказано вилучити доріжку: «%1» Ви Ñправді цього хочете? MIDI file import "%1" on %2 %3. Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð° MIDI «%1» на %2 %3. MIDI file import: "%1". Ð†Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ð° MIDI: «%1». qtractor-1.5.9/src/translations/PaxHeaders/qtractor_fr.ts0000644000000000000000000000013215101070305020641 xustar0030 mtime=1761898693.099267692 30 atime=1761898693.097267686 30 ctime=1761898693.099267692 qtractor-1.5.9/src/translations/qtractor_fr.ts0000644000175000001440000231370415101070305020643 0ustar00rncbcusers QObject Audio: %1 channels, %2 Hz Audio : %1 canaux, %2 Hz (%1 dB) (%1 dB) (%1 pan) (%1 pan) (%1% time stretch) (%1% étirement temporel) (%1 semitones pitch shift) (%1 demi-tons de décalage de la tonalité) %1 In %1 Entrée %1 Out %1 Sortie Audio files (%1) Fichiers audio (%1) All files (*.*) Tous les fichiers (*.*) %1 (%2) %3 channels, %4 frames, %5 Hz %6 %1 (%2) %3 canaux, %4 trames, %5 Hz %6 Duplex Duplex Output Sortie Input Entrée None Rien (take %1/%2) (prise %1/%2) [Mute] [Muet] Name: %1 Nom : %1 Start: %1 Offset: %2 End: %3 Length: %4 Début : %1 Décalage : %2 Fin : %3 Longueur : %4 File: %1 Fichier : %1 take %1 prise %1 reset takes réinitialiser les prises clip save sauvegarde du clip clip unlink délier le clip clip tool %1 outil clip %1 clip record enregistrer clip automation select automation sélection automation mode mode automation automation play automation jouer automation record automation enregistrer automation logarithmic automation logarithmique automation color automation couleur automation play all automation tout jouer automation record all automation tout enregistrer automation edit automation éditer automation clear automation nettoyer automation clear all automation tout nettoyer automation edit list automation éditer la liste %1 Monitor %1 Moniteur create bus créer un bus update bus mettre à jour le bus delete bus supprimer le bus move bus déplacer le bus bus pass-through bus transite bus gain gain du bus bus pan panoramique du bus Insert Send/Return pseudo-plugin (Audio) Pseudo-greffon insert envoi/retour (audio) Insert Send/Return pseudo-plugin (MIDI) Pseudo-greffon insert envoi/retour (MIDI) Send Gain Gain d'envoi Dry Gain Gain original (dry) Wet Gain Gain traité (wet) Aux Send (Audio) Envoi auxiliaire (audio) Aux Send pseudo-plugin (Audio) Pseudo-greffon envoi auxiliaire (audio) Aux Send pseudo-plugin (MIDI) Pseudo-greffon envoi auxiliaire (MIDI) (none) (rien) %1 (Audio) %1 (audio) %1 (MIDI) %1 (MIDI) Cakewalk Instrument Definition File Fichier de définition d'instrument Cakewalk File Fichier Date Date %1 Bank %2 %1 banque %2 %1 - Bank %2 %1 - banque %2 (format %1) MIDI: (format %1) MIDI: Channel %1 Canal %1 Track %1 Piste %1 , %1 tracks, %2 tpqn , %1 pistes, %2 tpqn (%1% vol) (%1% vol) MIDI file save: "%1", track-channel: %2. Sauvegarde fichier MIDI : "%1", piste-canal : %2. set controller active le contrôleur reset controller réinitialise le contrôleur %1 (format %2) %3 tracks, %4 tpqn %5 %1 (format %2) %3 pistes, %4 tpqn %5 %1 (format %2) %3 %1 (format %2) %3 (default) (défaut) %1 Hz %1 Hz slave esclave %1 (%2) %1 (%2) Usage: %1 [options] [session-file] Utilisation: %1 [options] [fichier-session] Options: Options : Set session identification (uuid) Définir l'identificateur de session (uuid) Show help about command line options Afficher l'aide à propos des options de ligne de commande Show version information Afficher les informations de version Session file (.qtr) Fichier de session (.qtr) [session-file] [fichier-de-session] Option -s requires an argument (uuid). L'option -s nécessite un argument (uuid). Signed 16-Bit 16-bits signé Signed 24-Bit 24-bits signé Signed 32-Bit 32-bits signé Float 32-Bit Flottant 32-Bits Float 64-Bit Flottant 64-Bits SMF Format 0 Format SMF 0 SMF Format 1 Format SMF 1 (Any) (Tous) Activate Activer Aux Send: %1 Envoi aux : %1 %1(%2): %3 plugin not found. %1(%2): greffon %3 introuvable. add plugin ajouter un greffon add insert ajouter un insert add aux-send ajouter un aux-send add MIDI controller ajouter un controleur MIDI aux-send bus bus aux-send aux-send matrix matrice aux-send remove plugin enlever le greffon move plugin déplacer le greffon activate plugin activer le greffon preset plugin pré-réglage du greffon reset plugin réinitialiser le greffon plugin program programme du greffon plugin alias alis de greffon dedicated audio outputs sorties audio dédiées direct access param paramètre accès direct import plugins importer des greffons session loop session boucle session punch session punch session properties propriétés de la session Beat Battement add tempo node ajouter un noeud tempo update tempo node mise à jour du noeud tempo remove tempo node enlever le noeud tempo move tempo node déplacer le noeud tempo add marker ajouter un marqueur update marker mettre à jour un marqueur remove marker enlever un marqueur add key signature ajouter une clef de signature update key signature mettre à jour la clef de signature remove key signature enlever la clef de signature move marker déplacer un marqueur change time-sig. modifier la signature temporelle. step input entrée par pas overdub overdub %1 Volume %1 Volume %1 Gain %1 Gain %1 Pan %1 Pan add track ajouter une piste remove track enlever une piste duplicate track piste dupliquée move track déplacer une piste resize track redimensionner une piste import track importer une piste track properties propriétés de la piste Track assignment failed: Track: "%1" Input: "%2" Output: "%3" Echec de l'affectation de la piste: Piste : "%1" Entrée: "%2" Sortie: "%3" track record piste enregistrer track mute piste muet track solo piste solo track monitor piste contrôler track gain piste gain track pan piste panoramique track instrument piste instrument Automation (%1) Automation (%1) none rien Automation Automation Unknown Inconnu Product: Produit : Vendor: Vendeur : Manual: Manuel : Support: Support : Version: Version : %1 (*.%2) %1 (*.%2) Copyright: Copyright : Project: Projet: Select plug-in's editor (GUI): Sélectionner l'éditeur de greffon (interface graphique) : External Externe X11 X11 X11 (native) X11 (natif) Gtk2 Gtk2 Gtk2 (native) Gtk2 (natif) Qt4 Qt4 Qt5 Qt5 Other Autre Don't ask this again Ne pas redemander plugin parameters paramètres du greffon Open File lv2_ui_request_parameter Ouvrir un fichier Author: Auteur : %1: Automation/curve file not found. %1 : fichier automation/courbe introuvable. Name: Nom : Category: Catégorie : Categories: Catégories : %1 Record Enregistrement %1 %1 Mute Muet %1 %1 Solo Solo %1 MIDI Controller: %1, %2, %3 Contrôleur MIDI : %1, %2, %3 Control (MIDI) Conrôle (MIDI) MIDI Controller Send pseudo-plugin pseudo-greffon d'envoi de contrôleur MIDI Value Valeur qtractorAudioIOMatrixForm Aux-Send I/O Matrix Matrice d'E/S Aux-Send Warning Attention Some settings have been changed. Do you want to apply the changes? Certains paramètres ont été modifiés. Souhaitez-vous appliquer ces modifications ? qtractorAudioListView Name Nom Ch Can Frames Trames Rate Vitesse Time Temps Path Chemin Open Audio Files Ouvrir fichiers audio %1: Audio file not found. %1 : fichier audio introuvable. qtractorAudioMixerMeter Gain (dB) Gain (dB) dB dB Pan: %1 Pan : %1 Gain: %1 dB Gain : %1 dB qtractorBusForm Bus list Liste des bus Buses Bus Ch Can Mode Mode Bus Bus Properties Propriétés &Name: &Nom : Bus name Nom de bus &Mode: &Mode : Bus mode Mode du bus Input Entrée Output Sortie Duplex Duplex Bus monitor (pass-through) Bus de contrôle (transit) M&onitor (pass-through) C&ontrôle (transit) Audio Audio Cha&nnels: Ca&naux : Audio channels Canaux audio Audio auto-connect Auto-connexion audio &Auto connect &Auto connecte MIDI MIDI MIDI Instrument name Nom d'instrument MIDI MIDI SysEx setup Configuration MIDI SysEx SysE&x... SysE&x... Input Plugins Greffons d'entrée Input bus plugins Bus d'entrée greffons Add input plugin Ajouter un greffon d'entrée &Add... &Ajouter... Remove input plugin Enlever le greffon d'entrée &Remove &Enlever Move input plugin up Déplacer le greffon d'entrée vers le haut &Up &Haut Move input plugin down Déplacer le greffon d'entrée vers le bas &Down &Bas Output Plugins Greffons de sortie Output bus plugins Bus de sortie greffons Add output plugin Ajouter un greffon de sortie Remove output plugin Enlever un greffon de sortie Move output plugin up Déplacer le greffon de sortie vers le haut Move output plugin down Déplacer le greffon de sortie vers le bas Move bus up towards the top Déplacer le bus vers le haut U&p Ha&ut Move bus down towards the bottom Déplacer le bus vers le bas Do&wn B&as Create bus Créer un bus &Create &Créer Update bus Mise à jour du bus &Update &Mettre à jour Delete bus Supprimer le bus &Delete &Supprimer Close this dialog Fermer ce dialogue Close Fermer Warning Attention Some settings have been changed. Do you want to apply the changes? Certains réglages ont été modifiés. Voulez vous appliquer les changements ? About to remove bus: "%1" (%2) Are you sure? Sur le point de supprimer un bus : "%1" (%2) Êtes-vous sûr ? Some settings have been changed. Do you want to discard the changes? Certains réglages ont été modifiés. Voulez-vous abandonner les changements ? Move &Up Déplacer vers le ha&ut Move &Down &Déplacer vers le bas (No instrument) (Pas d'instrument) (none) (rien) (1 item) (1 élément) (%1 items) (%1 éléments) qtractorClientListView Readable Clients / Output Ports Clients en lecture / Ports de Sortie Writable Clients / Input Ports Clients en écriture / Ports d'entrée qtractorClipForm &Name: &Nom : Clip name Nom du clip &File: &Fichier : Clip filename Nom de fichier du clip Browse for clip file Parcourir les fichiers clip Track/&Channel: Piste/&canal : Clip track/channel Clip piste/canal Clip gain/volume Clip gain/volume Parameters Paramètres Clip start Début du clip Clip offset Décalage du clip &Panning: &Panoramisation : Clip length Longueur du clip Offs&et: Décalag&e : &Length: &Longueur : &Start: &Début : Clip Clip Forma&t: Forma&t : Time display format Format d'affichage du temps Frames Trames Time Temps BBT BBT Fade In/Out Fondu en ouverture/fermeture Fade &In: Fondu en &ouverture : Clip fade-in length Clip longueur du fondu en ouverture Clip fade-in type Clip type de fondu en ouverture Fade &Out: Fondu en &fermeture : Clip fade-out length Clip longueur du fondu en fermeture Clip fade-out type Clip type de fondu en fermeture Audio Audio Ti&me Stretch: Étire&ment temporel : Clip time-stretch percentage Clip pourcentage d'étirement temporel % % Pitch S&hift: Décalage de t&onalité : Clip pitch-shift in semitones Clip décalage de tonalité en demi-tons semitones demi-tons Whether to use WSOLA time-stretching Si on doit utiliser l'étirement temporel WSOLA &WSOLA time-stretching Étirement temporel &WSOLA Whether to use RubberBand formant preserve Utilisation ou non de la préservation des formants RubberBand RubberBand &formant preserve Préservation des &formants RubberBand Whether to apply WSOLA quick seek time-stretching Si on doit appliquer la recherche rapide WSOLA pour l'étirement temporel WSOLA quic&k seek Rec&herche rapide WSOLA Whether to use RubberBand R3 finer engine Utilisation ou non du moteur plus fin RubberBand R3 RubberBand R&3 finer engine Moteur plus fin RubberBand R&3 &Mute &Muet &Gain: &Gain : Linear Linéaire Quadratic 1 Quadratique 1 Quadratic 2 Quadratique 2 Quadratic 3 Quadratique 3 Cubic 1 Cubique 1 Cubic 2 Cubique 2 Cubic 3 Cubique 3 dB dB &Volume: &Volume : new clip nouveau clip edit clip éditer le clip Warning Attention Some settings have been changed. Do you want to apply the changes? Certains réglages ont été modifiés. Voulez vous appliquer les changements ? MIDI MIDI MIDI files (*.%1 *.smf *.midi) Fichiers MIDI (*.%1 *.smf *.midi) All files (*.*) Tous les fichiers (*.*) %1 Clip File %1 fichier clip qtractorConnect Connect Connecter Disconnect Déconnecter Disconnect All Tout déconnecter Refresh Rafraîchir qtractorConnectForm Connections Connexions Audio Audio Select output client/ports Sélectionner la sortie client/ports Select input client/ports Sélectionner l'entrée client/ports Connect currently selected ports Connecter les ports actuellements sélectionnés &Connect &Connecter Disconnect currently selected ports Déconnecter les ports actuellements sélectionnés &Disconnect &Déconnecter Disconnect all currently connected ports Déconnecter tous les ports actuellements sélectionnés Disconnect &All &Tout déconnecter Refresh current connections view Rafraichir l'affichage courante des connexions &Refresh &Rafraichir MIDI MIDI (All) (Tout) qtractorConnections Connections Connexions qtractorEditRangeForm Range Plage Selection range Plage de sélection &Selection &Sélection Loop range Plage de boucle &Loop Bouc&le Punch range Plage de punch &Punch &Punch Edit range Éditer la plage &Edit &Éditer Custom range Plage personnalisée &Custom &Personnalisé St&art: &Début : Clip start Début du clip En&d: F&in : Clip offset Décalage du clip Apply to Loop points in range Appliquer aux points de la boucle dans la plage L&oop B&oucle Apply to Punch In/Out points in range Appliquer aux points punch in/out de la plage Pu&nch Pu&nch Mar&kers Mar&queurs Te&mpo Map Carte de Te&mpo &Format &Format Time display format Format d'affichage du temps Time Temps BBT BBT Frames Trames Edit Range Éditer la plage Options Options Apply to clips in range Appliquer aux clips de la plage Cl&ips Cl&ips A&utomation A&utomation Apply to Automation nodes in range Appliquer aux noeuds d'Automation de la plage Apply to Tempo Map nodes in range Appliquer aux noeuds de carte de tempo de la plage Apply to location Markers in range Appliquer à la localisation des marqueurs de la plage qtractorExportClipForm %1 %2 Clips %1 %2 clips qtractorExportForm Export Export &File: &Fichier : Export file name Nom de fichier d'exportation Browse export file name Parcourir les fichiers d'exportation File &type: &Type de fichier : Audio file type to use on export Type de fichier audio à utiliser lors de l'exportation Sample &format: &Format d'échantillon : Audio sample format to use on export Format d'échantillon audio à utiliser lors de l'exportation &Quality: &Qualité : Audio compression quality to use on export Qualité de compression audio à utiliser lors de l'exportation File &format: Format de &fichier : MIDI file format to use on export Format de fichier MIDI à utiliser lors de l'exportation Range Plage Session range Plage de session &Session &Session Loop range Plage de boucle &Loop Bouc&le Punch range Plage de punch &Punch &Punch Edit range Éditer la plage &Edit &Éditer Custom range Plage personnalisée &Custom &Personnalisée St&art: &Début : Custom start Démarrage personnalisé Custom end Fin personnalisée En&d: F&in : Outputs Sorties Output bus names Nom des bus de sortie Format Format Time display format Format d'affichage du temps Frames Trames Time Temps BBT BBT Whether to add/import new track(s) with export result Ajouter/importer ou non de nouvelle(s) piste(s) avec le résultat exporté &Add new track(s) &Ajouter de nouvelle(s) piste(s) Audio Audio MIDI MIDI Export %1 File Exporter %1 fichier MIDI files (*.%1 *.smf *.midi) Fichiers MIDI (*.%1 *.smf *.midi) All files (*.*) Tous les fichiers (*.*) qtractorExportTrackForm %1 %2 Tracks %1 %2 pistes Warning Attention The file already exists: "%1" Do you want to replace it? Le fichier existe déjà : "%1" Voulez-vous le remplacer ? Audio file export: "%1" started... Exportation du fichier audio : "%1" commencée... Audio file export: "%1" complete. Exportation du fichier audio : "%1" terminée. Audio file export: "%1" failed. Exportation du fichier audio : "%1" échec. MIDI file export: "%1" started... Exportation du fichier MIDI : "%1" commencée... MIDI file export: "%1" complete. Exportation du fichier MIDI : "%1" terminée. MIDI file export: "%1" failed. Exportation du fichier MIDI : "%1" échec. qtractorFileListView New Group Nouveau Groupe Warning Attention About to remove %1 file item(s). Are you sure? Sur le point de supprimer %1 élément(s) de type fichier Êtes-vous sûr ? About to remove %1 item: "%2" Are you sure? Sur le point de supprimer %1 élément: "%2" Êtes-vous sûr ? group groupe file fichier qtractorFileSystem &Home Dossier personnel &Up Ha&ut Al&l Files Tous &les fichiers &Session &Session &Audio &Audio &MIDI &MIDI H&idden Caché &Play &Jouer File System Système de fichier qtractorFiles Audio Audio MIDI MIDI Play file Jouer le fichier Add &Files... Ajouter des &fichiers... Cu&t Cou&per &Copy &Copier Ctrl+X Ctrl+P Ctrl+C Ctrl+C Ctrl+V Ctrl+L New &Group... Nouveau &Groupe... &Paste Co&ller Re&name Re&nommer &Remove &Supprimer Pla&y J&ouer Cl&eanup N&ettoyer Del Suppr Files Fichiers MIDI Files Fichiers MIDI Audio Files Fichiers audio qtractorInstrumentForm Instruments Instruments Files Fichiers Path Chemin Names Noms Import from instrument file Importer à partir d'un fichier instrument &Import... &Importer... Remove instrument file Supprimer le fichier instrument &Remove &Supprimer Move instrument file up on list order Déplacer le fichier instrument vers le haut de la liste &Up &Haut Move instrument file down on list order Déplacer le fichier instrument vers le bas de la liste &Down &Bas Export to instrument file Exporter vers un fichier instrument E&xport... E&xporter... Close this dialog Fermer ce dialogue Close Fermer Import Instrument Files Importer des fichiers instrument Instrument files (*.%1 *.sf2 *.sf3 *.midnam) Fichiers instrument (*.%1 *.sf2 *.sf3 *.midnam) All files (*.*) Tous les fichiers (*.*) Export Instrument File Exporter le fichier instrument Instrument files (*.%1) Fichiers instrument (*.%1) Warning Attention The instrument file already exists: "%1" Do you want to replace it? Le fichier instrument existe déjà : "%1" Voulez-vous le remplacer ? Instrument settings have been changed. Do you want to apply the changes? Les réglages de l'instrument ont été modifiés. Voulez-vous appliquer les changements ? Patch Names for Banks Noms de patches pour les banques Controller Names = %1 Noms de contrôleur = %1 RPN Names = %1 Noms RPN = %1 NRPN Names = %1 Noms NRPN = %1 Bank Select Method = %1 Méthode de sélection de banque = %1 Patch Names Noms de Patch Note Names Noms de note Controller Names Noms de contrôleur RPN Names Noms RPN NRPN Names Noms NRPN Bank Select Methods Méthodes de sélection de banque %1 = %2 %1 = %2 Based On = %1 Basé sur = %1 Normal Normal Bank MSB Banque MSB Bank LSB Banque LSB Patch Patch Unknown Inconnu qtractorInstrumentMenu (None) (Rien) qtractorMainForm &File &Fichier Open &Recent Ouvrir &récent &Edit &Éditer Select &Mode Sélectionner &mode &Select &Sélectionner I&nsert I&nsérer Remo&ve Enle&ver &Track &Piste &State &État &Navigate &Naviguer Mo&ve &Déplacer &Height &Hauteur Impor&t Tracks &Importer pistes E&xport Tracks &Exporter pistes M&ode M&ode A&utomation A&utomation Instrum&ent Instrum&ent T&ools &Outils Ta&ke &Prise &View &Affichage &Toolbars &Barre d'outils &Windows &Fenêtres &Zoom &Zoom S&nap &Ancrer T&ransport &Transport Mo&de Mo&de St&ep Pa&s &Help A&ide &New &Nouveau New Nouveau New session Nouvelle session New session file Nouveau fichier de session Ctrl+N Ctrl+N &Open... &Ouvrir... Open Ouvrir Open session Ouvrir une session Open session from file Ouvrir session à partir d'un fichier Ctrl+O Ctrl+O &Save &Sauvegarder Save Sauvegarder Save session Sauvegarder la session Save session to file Sauvegarder la session dans un fichier Ctrl+S Ctrl+S Save &As... Sauveg&arder sous... Save As Sauvegarder sous Save as Sauvegarder sous Save current session with another file name Sauvegarder la session courante sous un autre nom de fichier &Properties... &Propriétés... Session Properties Propriétés de Session Session properties Propriétés de session Edit current session properties Éditer les propriétés de la session courante F2 F2 E&xit So&rtir Exit Quitter Exit this application program Quitter de cette application &Undo &Défaire Undo Défaire Undo last action Défaire la dernière action Ctrl+Z Ctrl+Z &Redo &Refaire Redo Refaire Redo last action Refaire la dernière action Ctrl+Shift+Z Ctrl+Maj+Z Cu&t Co&uper Cut Couper Cut selection to clipboard Couper la sélection vers le presse-papiers Ctrl+X Ctrl+X &Copy &Copier Copy Copier Copy selection to clipboard Copier la sélection vers le presse-papiers Ctrl+C Ctrl+C &Paste Co&ller Paste Coller Paste clipboard contents Coller le contenu du presse-papiers Ctrl+V Ctrl+V Past&e Repeat... Répéter Coll&er... Paste Repeat Répéter Coller Paste repeat Répéter coller Paste/repeat clipboard contents Répéter/coller le contenu du presse-papiers Ctrl+Shift+V Ctrl+Maj+V &Delete &Supprimer Delete Supprimer Delete selection Supprimer la sélection Del Suppr &Clip &Clip Clip Clip Select clip Sélectionner le clip Clip selection mode Mode de sélection du clip &Range &Plage Range Plage Select range Sélectionner la plage Range selection mode Mode de sélection de la plage R&ectangle R&ectangle Rect Rect Select rectangle Sélectionner le rectangle Rectangular selection mode Mode de sélection rectangulaire &Automation &Automation Automation Automation Automation edit mode Mode d'édition de l'automation &All &Tout Select All Sélectionner tout Select all Sélectionner tout Mark all as selected Marquer comme sélectionné Ctrl+A Ctrl+A &None &Rien Select None Sélectionner rien Select none Sélectionner rien Mark all as unselected Marquer tout comme désélectionné Ctrl+Shift+A Ctrl+Maj+A &Invert &Inverser Select Invert Sélectionner inverser Select invert Sélectionner inverser Invert selection Inverser la sélection Ctrl+I Ctrl+I Select Track Sélectionner piste Select track Sélectionner piste Mark track as selected Marquer la piste comme sélectionnée Ctrl+T Ctrl+T Trac&k Range Pla&ge de piste Select Track Range Sélectionner la plage de piste Select track range Sélectionner la plage de piste Mark track range as selected Marquer la plage de piste comme sélectionnée Ctrl+Shift+R Ctrl+Maj+R Select Range Sélectionner la plage Mark range as selected Marquer la plage comme sélectionnée Ctrl+R Ctrl+R &Range... &Plage... Remove Range Enlever Plage Remove range Enlever plage Remove range as selected Enlever la plage sélectionnée Ctrl+Del Ctrl+Suppr Remove Track Range Enlever la plage de piste Remove track range Enlever la plage de piste Remove track range as selected Enlever la plage de piste sélectionnée Ctrl+Shift+Del Ctrl+Maj+Suppr Insert Range Insérer plage Insert range Insérer plage Insert range as selected Insérer plage comme sélectionnée Ctrl+Ins Ctrl+Ins Insert Track Range Insérer plage de piste Insert track range Insérer plage de piste Insert track range as selected Insérer plage de piste comme sélectionné Ctrl+Shift+Ins Ctrl+Maj+Ins Sp&lit Di&viser Split Selection Diviser la sélection Split selection Diviser la sélection Split current selection Diviser la sélection courante Ctrl+Y Ctrl+Y &Add Track... &Ajouter une piste... Add Track Ajouter une piste Add track Ajouter une piste Add a new track to session Ajouter une nouvelle piste à la session Shift+Ins Maj+Ins &Remove Track &Supprimer une piste Remove Track Supprimer une piste Remove track Supprimer une piste Remove current track from session Supprimer la piste courante de la session Shift+Del Maj+Suppr &Duplicate Track &Dupliquer une piste Duplicate Track Dupliquer une piste Duplicate track Dupliquer une piste Duplicate current track Dupliquer la piste courante Track &Properties... &Propriétés de la piste... Track Properties Propriétés de la piste Track properties Propriétés de la piste Edit current track properties Éditer les propriétés de la piste courante Shift+F2 Maj+F2 &Inputs &Entrées Track Inputs Entrées de la piste Track inputs Entrées de la piste Show current track input bus connections Afficher les connexions du bus d'entrée de la piste courante &Outputs &Sorties Track Outputs Sorties de la piste Track outputs Sorties de la piste Show current track output bus connections Afficher les connexions du bus de sortie de la piste courante &Record &Enregistrer Record Track Enregistrer la piste Record track Enregistrer la piste Arm current track for recording Armer la piste courante pour l'enregistrement &Mute &Muet Mute Track Mettre la piste en sourdine Mute track Mettre la piste en sourdine Mute current track Mettre la piste courante en sourdine &Solo &Solo Solo Track Mettre la piste en solo Solo track Mettre la piste en solo Solo current track Mettre la piste courante en solo M&onitor M&oniteur Monitor Track Monitorer la piste Monitor track Monitorer la piste Monitor current track Monitorer la piste courante &First &Première First Track Première piste First track Première piste Make current the first track Rendre courante la première piste &Previous &Précédente Previous Track Piste précédente Previous track Piste précédente Make current the previous track Rendre courante la piste précédente &Next &Suivante Next Track Piste suivante Next track Piste suivante Make current the next track Rendre courante la piste suivante &Last &Dernière Last Track Dernière piste Last track Dernière piste Make current the last track Rendre courante la dernière piste N&one &Aucune None Track Aucune piste None track Aucune piste None current track Aucune piste courante &Top &Sommet Move Top Déplacer vers le sommet Move top Déplacer au sommet Move current track to top Déplacer la piste courante au sommet &Up &Haut Move Up Déplacer vers le haut Move up Déplacer vers le haut Move current track up Déplacer la piste courante vers le haut &Down &Bas Move Down Déplacer vers le bas Move down Déplacer vers le bas Move current track down Déplacer la piste courante vers le bas &Bottom &Fond Move Bottom Déplacer au fond Move bottom Déplacer au fond Move current track to bottom Déplacer la piste courante au fond &Increase &Augmenter Increase Height Augmenter la hteur Increase height Augmenter la hauteur Increase track height Augmenter la hauteur de la piste Ctrl+Shift++ Ctrl+Maj++ &Decrease &Diminuer Decrease Height Diminuer la hauteur Decrease height Diminuer la hauteur Decrease track height Diminuer la hauteur de la piste Ctrl+Shift+- Ctrl+Maj+- &Minimize &Minimiser Minimize Height Minimiser la hauteur Minimize height Minimiser la hauteur Minimize track height Minimiser la hauteur de piste &Reset &Réinitialiser Height Reset Réinitialiser la hauteur Height reset Réinitialiser la hauteur Reset track height Réinitialiser la hauteur de la piste Ctrl+Shift+1 Ctrl+Maj+1 Auto &Monitor &Moniteur automatique Auto Monitor Moniteur automatique Auto monitor Moniteur automatique Auto-monitor current track Monitorer automatiquement la piste courante F6 F6 Auto Dea&ctivate Désa&ctivation automatique Auto Deactivate Désactivation automatique Auto-deactivate plugins Désactivation automatique des greffons Auto-deactivate plugins not producing sound Désactivation automatique des greffons ne produisant pas de son Shift+F6 Maj+F6 &Audio... &Audio... Inport Audio File Importer un fichier audio Import Audio file Importer un fichier audio Import tracks from Audio file Importer des pistes à partir d'un fichier audio &MIDI... &MIDI... Import MIDI File Importer un fichier MIDI Import MIDI file Importer un fichier MIDI Import tracks from MIDI file Importer des pistes à partir d'un fichier MIDI Export Audio File Exporter un fichier audio Export Audio file Exporter un fichier audio Export tracks to Audio file Exporter des pistes vers un fichier audio Export MIDI File Exporter un fichier MIDI Export MIDI file Exporter un fichier MIDI Export tracks to MIDI file Exporter des pistes vers un fichier MIDI Log&arithmic Log&arithmique Automation logarithmic Automation logarithmique Automation curve logarithmic scale Échelle logarithmique de la courbe d'automation C&olor... C&ouleur... Automation color Couleur pour l'automation Automation curve color Couleur de la courbe d'automation &Lock &Verrouiller Automation lock Verrouillage automation Lock automation curve Verrouillage de la courbe d'automation &Play &Jouer Automation playback Lecture de l'automation Playback automation curve Lecture de la courbe d'automation Automation record Enregistrement automation Record automation curve Enregistrement de la courbe d'automation &Clear &Nettoyer Automation clear Nettoyer l'automation Clear automation curve Nettoyer la courbe d'automation Loc&k All &Verrouiller tout Automation lock all Verrouiller toutes les automations Lock all automation curves Verrouiller toutes les courbes d'automation Play &All &Jouer tout Automation playback all Lecture complète de l'automation Playback all automation curves Lecture de toutes les courbes d'automation Rec&ord All &Enregistrer tout Automation record all Enregistrer toutes les automations Record all automation curves Enregistrer toutes les courbes d'automation C&lear All N&ettoyer tout Automation clear all Nettoyer toutes les automations Clear all automation curves Nettoyer toutes les courbes d'automation &New... &Nouveau... New Clip Nouveau clip New clip Nouveau clip Create new clip Créer un nouveau clip &Edit... &Éditer... Edit Clip Éditer le clip Edit clip Éditer le clip Edit current clip Éditer le clip courant F4 F4 Mute Clip Silencer le clip Mute clip Silencer le clip Mute current clip Silencer le clip actuel &Unlink &Délier Unlink Clip Délier le clip Unlink clip Délier le clip Unlink current clip Délier le clip courant Recor&d Enre&gistrer Record Clip Enregistrer le clip Record clip Enregistrer le clip Record current clip (overdub) Enregistrer le clip courant (overdub) &Split &Diviser Split Clip Diviser le clip Split clip Diviser le clip Split current clip at playhead Diviser le clip courant au niveau de la tête de lecture &Merge... &Fusionner... Merge Clips Fusionner les clips Merge clips Fusionner les clips Merge selected clips Fusionner les clips sélectionnés Normali&ze Normaliser (&z) Normalize Clip Normaliser le clip Normalize clip Normaliser le clip Normalize current clip (gain/volume) Normaliser le clip courant (gain/volume) &Quantize... &Quantifier... Quantize Clip Quantifier le clip Quantize clip events Quantifier les évènements du clip Quantize current MIDI clip events Quantifier les éléments du clip MIDI courant &Transpose... &Transposer... Transpose Clip Transposer le clip Transpose clip events Transposer les évènements du clip Transpose current MIDI clip events Transposer les évènements du clip MIDI courant &Normalize... &Normaliser... Normalize clip events Normaliser les évènements du clip Normalize current MIDI clip events Normaliser les évènements du clip MIDI courant &Randomize... Aléato&riser... Randomize Clip Aléatoriser le clip Randomize clip events Aléatoriser les évènements du clip Randomize current MIDI clip events Aléatoriser les évènements du clip MIDI courants Resi&ze... Re&dimensionner... Resize Clip Redimensionner le clip Resize clip events Redimensionner les évènements du clip Resize current MIDI clip events Redimensionner les évènements du clip MIDI courants Re&scale... Re&mettre à l'échelle... Rescale Clip Remettre le clip à l'échelle Rescale clip events Remettre les évènements du clip à l'échelle Rescale current MIDI clip events Remettre les évènements du clip MIDI courant à l'échelle T&imeshift... Dé&calage temporel... Timeshift Clip Décalage temporel du clip Timeshift clip events Décalage temporel des évènements du clip Timeshift current MIDI clip events Décalage temporel des évènements du clip MIDI courant T&empo ramp... Rampe de t&empo... Tempo ramp Clip Clip de la rampe de tempo Tempo ramp clip events Événements du clip de la rampe de tempo Tempo ramp current MIDI clip events Rampe de tempo des événements actuels du clip MIDI &Tempo Adjust... Ajustement du &tempo... Tempo Adjust Ajustement du tempo Adjust session tempo from current clip selection Ajuster le tempo de la session à partir de la sélection du clip courant F7 F7 &Cross Fade Fondu-en&chaîné Clip Cross-fade Clip fondu-enchaîné Clip cross-fade Clip fondu-enchaîné Cross-fade current overlapped clips Fondu enchaîné des clips courants en superposition &Range Set &Réglage de plage Clip Range Plage de clip Clip range Plage de clip Set edit-range from current clip extents Régler la plage d'édition à partir de la longueur courante du clip &Loop Set Réglage de bouc&le Clip Loop Boucle clip Clip loop Boucle clip Set loop-range from current clip extents Régler la plage de bouclage à partir de la longueur du clip &Import... &Importer... Import Clip Importer clip Import clip Importer clip Import clip from file(s) Importer clip à partir du fichier(s) E&xport... E&xporter... Export Clip Exporter clip Export clip Exporter clip Export current clip to file Exporter le clip courant vers un fichier First Take Première Prise First take Première prise Select current clip first take Sélectionner la première prise du clip courant Previous Take Précédente Prise Previous take Précédente prise Select current clip previous take Sélectionner la précédente prise du clip courant Next Take Prochaine Prise Next take Prochaine prise Select current clip next take Sélectionner la prochaine prise du clip courant Shift+T Maj+T Last Take Dernière Prise Last take Dernière prise Select current clip last take Sélectionner la dernière prise du clip courant Reset Takes Nettoyer les Prises Reset takes Nettoyer les prises Reset (unfold) current clip takes Nettoyer (déplier) les prises du clip courant R&ange... Pl&age... Take Range Plage de prise Take range Plage de prise Range (fold) current clip into takes Plage (plier) le clip courant en prises &Menubar Barre de &menu Menubar Barre de menu Show/hide the main program window menubar Montrer/cacher la barre de menu de la fenêtre du programme principal Ctrl+M Ctrl+M &Statusbar Barre de &status Statusbar Barre de status Show/hide the main program window statusbar Montrer/cacher la barre de status de la fenêtre du programme principal File Toolbar Barre d'outils fichier File toolbar Barre d'outils fichier Show/hide main program window file toolbar Montrer/cacher la barre d'outils fichier de la fenêtre du programme principal Edit Toolbar Éditer la barre d'outils Edit toolbar Éditer la barre d'outils Show/hide main program window edit toolbar Montrer/cacher l'édition de la barre d'outils de la fenêtre du programme principal Track Toolbar Barre d'outils piste Track toolbar Barre d'outils piste Show/hide main program window track toolbar Montrer/cacher la barre d'outils piste de la fenêtre du programme principal View Toolbar Affichage de la barre d'outils View toolbar Affichage de la barre d'outils Show/hide main program window view toolbar Montrer/cacher la barre d'outils de l'affichage de la fenêtre du programme principal &Options &Options Options Toolbar Barre d'outils Options Options toolbar Barre d'outils options Show/hide main program window options toolbar Montrer/cacher la barre d'outils options de la fenêtre du programme principal Transport Toolbar Barre d'outils Transport Transport toolbar Barre d'outils transport Show/hide main program window transport toolbar Montrer/cacher la barre d'outils transport de la fenêtre du programme principal T&ime T&emps Time Toolbar Barre d'outils Temps Time toolbar Barre d'outils temps Show/hide main program window time toolbar Montrer/cacher la barre d'outils temps de la fenêtre du programme principal Thum&b V&ignette Thumb Toolbar Barre d'outils Vignette Thumb toolbar Barre d'outils vignette Show/hide main program window thumb toolbar Montrer/cacher la barre d'outils vignette de la fenêtre du programme principal File &System &Système de fichier File System Système de fichier File system Système de fichier Show/hide the file system window Afficher/cacher la fenêtre de système de fichier &Files &Fichiers Files Fichiers Show/hide the files window Montrer/cacher la fenêtre des fichiers M&essages M&essages Messages Messages Show/hide the messages window Montrer/cacher la fenêtre des messages &Connections &Connexions Connections Connexions Show/hide the connections window Montrer/cacher la fenêtre des connexions F8 F8 Mi&xer Mi&xeur Mixer Mixeur Show/hide the mixer window Montrer/cacher la fenêtre mixeur F9 F9 &In &Entrée Zoom In Zoom avant Zoom in Zoom avant Ctrl++ Ctrl++ &Out &Sortie Zoom Out Zoom arrière Zoom out Zoom arrière Ctrl+- Ctrl+- Zoom Reset Réinitialiser le zoom Zoom reset Réinitialiser le zoom Ctrl+1 Ctrl+1 &Horizontal &Horizontal Horizontal Zoom Zoom horizontal Horizontal zoom Zoom horizontal Horizontal zoom mode Mode zoom horizontal &Vertical &Vertical Vertical Zoom Zoom vertical Vertical zoom Zoom vertical Vertical zoom mode Mode zoom vertical All Zoom Tout zoomer All zoom Tout zoomer All zoom mode Mode zoom total &Grid &Grille Grid Grille Snap grid view mode Mode d'affichage collant à la grille magnétique &Zebra &Zèbrures Zebra Zèbrures Bar zebra view mode Mode d'affichage barre zèbrée Too&l Tips Info-bu&lles Tool tips Info-bulles Floating tool tips view mode Mode d'affichage des astuces d'info-bulles flottantes &Refresh &Rafraichir Refresh Rafraichir Refresh views Rafraichir l'affichage F5 F5 &Instruments... &Instruments... Instruments Instruments Change instrument definitions and files Changer les définitions et fichiers des instruments &Controllers... &Contrôleurs... Controllers Contrôleurs Change MIDI controllers configuration Changer la configuration des contrôleurs MIDI &Buses... &Bus... Buses Bus Change session bus definitions Changer les définitions des bus de session Tempo M&ap / Markers... C&arte de tempo / marqueurs... Tempo Map / Markers Carte de Tempo / Marqueurs Tempo map / markers Carte de tempo / marqueurs Change session tempo map / markers Changer la carte de tempo de la session / marqueurs &Options... &Options... Options Options Change general application program options Changer les options générales de l'application F12 F12 &Backward &Arrière Backward Arrière Transport backward Transport en arrière Backspace Backspace Re&wind Re&mbobinner Rewind Rembobinner Transport rewind Transport rembobinner F&ast Forward Av&ance Rapide Fast Forward Avance Rapide Fast forward Avance rapide Transport fast forward Transport avance rapide &Forward &Avance Forward Avance Transport forward Transport avance &Loop &Boucle Loop Boucle Transport loop Transport boucle Ctrl+Shift+L Ctrl+Maj+L Step Backward Reculer d'un pas Step backward Reculer d'un pas Transport step backward Transport en arrière Step Forward Avancer d'un pas Step forward Avancer d'un pas Transport step forward Transport en avant Loop &Set &Réglage boucle Loop Set Réglage Boucle Loop set Réglage boucle Transport loop set Paramètre de la boucle de transport Ctrl+L Ctrl+L &Stop &Stop Stop Stop Transport stop Transport stop Play Jouer Transport play/pause Transport jouer/pause Space Espace Record Enregistrer Transport record Transport enregistrer &Punch &Punch Punch Punch Punch in/out Punch in/out Transport punch in/out Transport punch in/out Ctrl+Shift+P Ctrl+Maj+P Punch Se&t Ré&glage Punch Punch Set Réglage punch Punch in/out set Réglage punch in/out Transport punch in/out set Paramètre du punch in/out du transport Ctrl+P Ctrl+P &Count-in Dé&compte Count-in Décompte &Metronome &Métronome Metronome Métronome F&ollow Playhead S&uivre la tête de lecture Follow Playhead Suivre la tête de lecture Follow playhead Suivre la tête de lecture A&uto Backward A&uto arrière Auto Backward Auto arrière Auto backward Auto arrière &Continue Past End &Continuer au delà de la fin Continue Past End Continuer au delà de la fin Continue past end Continuer au delà de la fin Transport mode: None Mode de transport : aucun Transport mode set to None Mode de transport paramétré sur Aucun &Slave E&sclave Slave Esclave Transport mode: Slave Mode de transport : Esclave Transport mode set to Slave Mode de transport paramétré sur Esclave &Master &Maître Master Maître Transport mode: Master Mode de transport : Maître Transport mode set to Master Mode de transport paramétré sur Maître &Full &Complet Full Complet Transport mode: Full Mode de transport : Complet Transport mode set to Full Mode de transport paramétré sur Complet Pa&nic Pa&nique Panic Panique All MIDI tracks shut off (panic) Toutes les pistes MIDI éteintes (panique) &Shortcuts... &Raccourcis... Shortcuts Raccourcis Keyboard shortcuts Raccourcis clavier &About... À &propos... About À propos Show information about this application program Afficher les informations à propos de ce programme About &Qt... À propos de &Qt... About Qt À propos de Qt Show information about the Qt toolkit Afficher les informations à propos du toolkit Qt Current tempo (BPM) Tempo courant (BPM) Snap/beat Snap/battement Track Piste Current track name Nom de la piste actuelle MOD MOD Session modification state État de modification de la session REC ENR Session record state État d'enregistrement de la session MUTE MUET Session muting state État muet de la session SOLO SOLO Session soloing state État solo de la session LOOP BOUCLE Session looping state État de bouclage de la session Session total time Durée totale de la session Session sample rate Fréquence d'échantillonnage de la session Could not set default session directory: %1 Sorry. Ne peut pas sélectionner le répertoire par défaut de la session: %1 Désolé. Ready Prêt Session XRUN state État XRUN de la session Session buffer size Taille de la mémoire tampon de la session Untitled%1 Sans titre%1 New session: "%1". Nouvelle session: "%1". Session files (*.%1 *.%2 *.%3) Fichiers session (*.%1 *.%2 *.%3) Session files (*.%1 *.%2) Fichiers session (*.%1 *.%2) Template files (*.%1) Fichiers modèle (*.%1) Archive files (*.%1) Fichiers archive (*.%1) All files (*.*) Tous les fichiers (*.*) Open Session Ouvrir une session Save Session Sauvegarder une session Warning Attention The file already exists: "%1" Do you want to replace it? Le fichier existe déjà : "%1" Voulez-vous le remplacer ? Backup session: "%1" as "%2". Session de sauvegarde: "%1" en "%2". Could not backup existing session: %1 as %2 Sorry. Ne peut pas sauvegarder la session existante : %1 en %2 Désolé. The current session has been changed: "%1" Do you want to save the changes? La session courante a été modifiée : "%1" Voulez-vous sauvegarder les changements ? About to remove archive directory: "%1" Are you sure? Sur le point de supprimer le répertoire archive : "%1" Êtes-vous sûr ? Session closed. Session fermée. The directory already exists: "%1" Do you want to replace it? Le répertoire existe déjà : "%1" Voulez-vous le remplacer ? Opening "%1"... Ouvre "%1"... Session could not be loaded from "%1". Sorry. La session n'a pas pu être chargée à partir de "%1". Désolé. Open session: "%1". Ouvre la session: "%1". A directory with same name already exists: "%1" This directory will be replaced, erasing all its current data, when opening and extracting this archive in the future. Do you want to continue? Un répertoire du même nom existe déjà : "%1" Ce répertoire sera remplacé, en effaçant toutes ses données actuelles, lors de l'ouverture et de l'extraction de cette archive dans le futur. Souhaitez-vous continuer ? The directory is an extracted archive: "%1" This directory will be removed, erased from all its current data, when closing this session. Do you want to continue? Le répertoire est une archive extraite : "%1" Ce répertoire sera supprimé, effacé de toutes ses données actuelles, lors de la fermeture de cette session. Voulez-vous continuer ? Saving "%1"... Sauvegarde "%1"... Session could not be saved to "%1". Sorry. La session n'a pas pu être sauvegardée vers "%1". Désolé. Save session: "%1". Sauvegarde la session : "%1". Oops! Looks like it crashed or did not close properly last time it was run... however, an auto-saved session file exists: "%1" Do you want to crash-recover from it? Oups! Il semblerait qu'il y ait eu un crash ou que l'application n'est pas été correctement fermé la dernière fois... cependant un fichier de sauvegarde automatique existe : "%1" Voulez-vous redémarrer à partir de celui-ci ? About to clear automation: "%1" Are you sure? Sur le point de nettoyer l'automation: "%1" Êtes-vous sûr ? About to clear all automation: "%1" Are you sure? Sur le point de nettoyer toutes les automations: "%1" Êtes-vous sûr ? take range plage de prise session session or ou program programme Information Information Some settings may be only effective next time you start this %1. Certains réglages ne seront effectifs que la prochaine fois que vous démarrerez %1. Player panic! Panique du lecteur ! Debugging option enabled. Option de débuggage activée. Ogg Vorbis (libvorbis) file support disabled. Support des fichiers Ogg Vorbis (libvorbis) désactivé. MPEG-1 Audio Layer 3 (libmad) file support disabled. Support des fichiers MPEG-1 Audio Layer 3 (libmad) désactivé. Sample-rate conversion (libsamplerate) disabled. Conversion de fréquence d'échantillonnage (libsamplerate) désactivé. Pitch-shifting support (librubberband) disabled. Support du décalage de tonalité (librubberband) désactivé. Beat-detection support (libaubio) disabled. Support de détection de battement (libaubio) désactivé. OSC service support (liblo) disabled. Support du service OSC (liblo) désactivé. LADSPA Plug-in support disabled. Support des greffons LADSPA désactivé. DSSI Plug-in support disabled. Support des greffons DSSI désactivé. CLAP Plug-in support disabled. Le support des greffons CLAP est désactivé. LV2 Plug-in support disabled. Support du greffon LV2 désactivé. LV2 Plug-in UI support disabled. Support de l'interface graphique des greffons LV2 désactivé. LV2 Plug-in UI support (libsuil) disabled. Support de l'interface graphique des greffons (libsuil) LV2 désactivé. LV2 Plug-in MIDI/Event support (DEPRECATED) enabled. Support MIDI/Event greffon LV2 (PÉRIMÉ) activé. LV2 Plug-in MIDI/Atom support disabled. SupporT MIDI/Atom greffon LV2 désactivé. LV2 Plug-in State Files support disabled. Support fichiers état greffon LV2 désactivé. LV2 Plug-in MIDNAM support disabled. Support LV2 Plug-in MIDNAM désactivé. LV2 Plug-in Patch support disabled. Support Patch greffon LV2 désactivé. LV2 Plug-in UI Touch interface support disabled. Support UI Touch interface greffon LV2 désactivé. LV2 Plug-in UI Idle interface support disabled. Support UI Idle greffon LV2 désactivé. LV2 Plug-in UI Show interface support disabled. Support UI Show interface greffon LV2 désactivé. LV2 Plug-in UI GTKMM2 native support disabled. Support natif de l'interface graphique GTKMM2 des greffons LV2 désactivé. JACK Metadata support disabled. Support des métadonnées JACK désactivé. Using: Qt %1 Utilisant : Qt %1 XRUN XRUN The following issues were detected: %1 Saving into another session file is highly recommended. Les problèmes suivants ont été détectés: %1 Sauvegarder dans un autre fichier session est fortement recommandé. Don't show this again Ne plus montrer ceci The audio engine buffer size has changed, increased from %1 to %2 frames/period. Reloading the current session file is highly recommended. La taille du tampon du moteur audio a été modifiée, aumgentant de %1 à %2 trames/période. Le rechargement du fichier de session courant est hautement recommandé. TRACK MONITOR %1 %2 MONITEUR DE PISTE %1 %2 Set current snap to %1 Placer l'accroche courante sur %1 Current time (play-head) Temps courant (tête de lecture) VST2 Plug-in support disabled. Support des greffons VST2 désactivé. VST3 Plug-in support disabled. Support des greffons VST3 désactivé. LV2 Plug-in support (liblilv) disabled. Support greffon LV2 (liblilv) désactivé. LV2 Plug-in External UI support disabled. Support UI externe greffon LV2 désactivé. LV2 Plug-in Worker/Schedule support disabled. Support Worker/Schedule greffon LV2 désactivé. LV2 Plug-in State support disabled. Support état greffon LV2 désactivé. LV2 Plug-in Programs support disabled. Support des programmes greffon LV2 désactivé. LV2 Plug-in Presets support disabled. Support des préréglages des greffos LV2 désactivé. LV2 Plug-in Time/position support disabled. Support Temps/position greffon LV2 désactivé. LV2 Plug-in Options support disabled. Support pour les options du greffon LV2 désactivé. LV2 Plug-in Buf-size support disabled. Support du Buf-size pour les greffons LV2 désactivé. LV2 Plug-in UI Request-value support disabled. Support du UI Request-value pour les greffons LV2 désactivé. JACK Session support disabled. Support de session JACK désactivé. JACK Latency support disabled. Support de la latence JACK désactivé. Version Version Website Site web This program is free software; you can redistribute it and/or modify it Ce programme est un logiciel libre; vous pouvez le redistribuer et/ou le modifier under the terms of the GNU General Public License version 2 or later. sous les conditions de la license générale GNU version 2 ou plus. record clip enregistre clip [modified] [modifié] Session started. Session démarrée. The audio/MIDI engine could not be started. Make sure the JACK/Pipewire audio service and the ALSA Sequencer kernel module (snd-seq-midi) are up and running and then restart the session. Le moteur audio/MIDI n'a pas pu être démarré. Assurez-vous que le service audio JACK/Pipewire et le module noyau ALSA Sequencer (snd-seq-midi) sont opérationnels, puis redémarrez la session. The original session sample rate (%1 Hz) is not the same as the current audio engine (%2 Hz). Saving and reloading from a new session file is highly recommended. La fréquence originale de la session (%1 Hz) n'est pas la même que celle du moteur audio courant (%2 Hz). Sauvegarder et recharger à partir d'un nouveau fichier session est fortement recommandé. Don't ask this again Ne pas redemander LV2 plug-in State Make Path support (DANGEROUS) enabled. support de LV2 plug-in State Make Path activé (DANGEREUX) LV2 Plug-in UI GTK2 native support disabled. Support natif LV2 Plug-in UI GTK2 désactivé LV2 Plug-in UI X11 native support disabled. support natif LV2 Plug-in UI X11 désactivé NSM support disabled. Support de NSM désactivé. &Hold &Maintien &Linear &Linéaire &Spline &Spline Take %1 Prise %1 None Rien Error Erreur XRUN(%1 skipped) XRUN(%1 sauté) XRUN(%1): some frames might have been lost. XRUN(%1): des trames peuvent avoir été perdues. Audio connections change. Les connexions audio changent. Audio self-connection detected! In general, connecting an output bus (or insert send), directly into any input bus (or insert return), is not advisable. It often doesn't work, if at all. Autoconnexion audio détectée ! En général, il n'est pas conseillé de connecter un bus de sortie (ou un envoi d'insert), directement à un bus d'entrée (ou un retour d'insert). Souvent, cela ne fonctionne pas. MIDI connections change. Les connexions MIDI changent. Playing ended. Lecture terminée. The audio engine has been shutdown. Make sure the JACK audio server (jackd) is up and running and then restart session. Le moteur audio a été arrêté. Assurez-vous que le serveur audio JACK (jackd) est près et en fonctionnement puis redémarrez la session. STOP STOP PLAY JOUER FFWD FFWD REW REW REC ON ENR ON REC OFF ENR OFF RESET REINIT LOCATE %1 LOCALISE %1 SHUTTLE %1 NAVETTE %1 STEP %1 PAS %1 TRACK RECORD %1 %2 PISTE ENREGISTREMENT %1 %2 TRACK MUTE %1 %2 PISTE MUET %1 %2 TRACK SOLO %1 %2 PISTE SOLO %1 %2 Unknown sub-command Sous-commande inconnue Not implemented Pas implémenté MIDI CTL: %1, Channel %2, Param %3, Value %4 MIDI CTL : %1, Canal %2, Param %3, Valeur %4 (track %1, gain %2) (piste %1, gain %2) (track %1, panning %2) (piste %1, panoramique %2) START DEMARRE CONTINUE CONTINUE SONGPOS %1 SONGPOS %1 %1 BPM %1 BPM Playing "%1"... Joue "%1"... qtractorMessages Messages Messages Logging stopped --- %1 --- Journalisation arrêtée --- %1 --- Logging started --- %1 --- Journalisation démarrée --- %1 --- qtractorMidiControl Note On Note On Note Off Note Off Key Press Key Press Controller Contrôleur Pgm Change Pgm Change Chan Press Chan Press Pitch Bend Pitch Bend RPN RPN NRPN NRPN Control 14 Control 14 Track Gain Gain de piste Track Panning Panoramique de piste Track Monitor Moniteur de piste Track Record Enregistrement de piste Track Mute Silencage de piste Track Solo Mise en solo de piste qtractorMidiControlForm Controllers Contrôleurs Controller files Fichiers de contrôleur Files Fichiers Path Chemin Import controller files Importer des fichiers de contrôleur &Import... &Importer... Remove controller file Supprimer un ficher de contrôleur &Remove &Supprimer Move controller file up on list order Déplacer le fichier contrôleur vers le haut de la liste &Up &Haut Move controller file down on list order Déplacer le fichier contrôleur vers le bas de la liste &Down &Bas &Type &Type &Channel &Canal &Parameter &Paramètre Trac&k Piste (&k) offse&t décalage (&t) &limit &limite C&ommand C&ommande Flags Drapeaux MIDI Event type Type d'évènement MIDI MIDI Channel Canal MIDI MIDI Controller (parameter) Contrôleur MIDI (paramètre) MIDI parameter (track offset) Paramètre MIDI (décalage de piste) + + Track offset Décalage de piste Track limit Limite de piste Command action Commande d'action Command delta/momentary Commande delta/momentanée D&elta D&elta Command feedback Commande de retour &Feedback &Retour Map/update controller command Associer/mise à jour de la commande du contrôleur &Map &Associer Controller map Association de contrôleur Type Type Channel Canal Parameter Paramètre Track Piste Command Commande Feedback Retour Unmap/remove controller command Dés-associer/supprimer la commande du contrôleur U&nmap &Dés-associer Enable all controllers immediate sync (hook) Activer la synchro immédiate de tous les contrôleurs (hook) &Sync &Sync Reload/apply all controller files Recharger/appliquer tous les fichiers contrôleur Relo&ad Rech&arger Export to controller file Exporter vers un fichier contrôleur E&xport... E&xporter... Close this dialog Fermer cette fenêtre Close Fermer Import Controller Files Importer des fichiers contrôleur Controller files (*.%1) Fichiers contrôleur (*.%1) All files (*.*) Tous les fichiers (*.*) Warning Attention About to remove controller file: "%1" Are you sure? Sur le point de supprimer un fichier contrôleur: "%1" Êtes-vous sûr ? Export Controller File Exporter le fichier contrôleur controller contrôleur The controller file already exists: "%1" Do you want to replace it? Le fichier contrôleur existe déjà : "%1" Voulez-vous le remplacer ? Saved controller mappings may not be effective the next time you start this program. "%1" Do you want to apply to controller files? L'assignation des contrôleurs sauvegardée pourrait ne pas être effective la prochaine fois que vous démarrerez ce programme. "%1" Voulez-vous mettre à jour les fichiers contrôleur ? Controller mappings have been changed. L'association des contrôleurs a été modifiée. Do you want to save the changes? Voulez-vous sauvegarder les changements ? Delta Delta qtractorMidiControlObserverForm &Type: &Type : MIDI event type MIDI Type d'évènement Cha&nnel: Ca&nal : MIDI channel Canal MIDI &Parameter: &Paramètre : MIDI parameter Paramètre MIDI &Logarithmic &Logarithmique &Feedback &Retour In&vert In&verser &Hook &Ancrer L&atch Verr&ou Control input connections Connexions des entrées de contrôles &Inputs &Entrée Control output connections Connexions des sorties de contrôle &Outputs &Sorties MIDI Controller Contrôleur MIDI MIDI controller is already assigned. Do you want to replace the mapping? Le contrôleur MIDI est déjà associé. Voulez-vous remplacer l'association ? Some settings have been changed. Do you want to apply the changes? Certains réglages ont été modifiés. Voulez-vous appliquer les changements ? &MIDI Controller... Contrôleur &MIDI... &Automation &Automation &Lock Verroui&ller &Play &Jouer &Record En&registrer &Clear &Nettoyer qtractorMidiControlPluginWidget &Type: &Type : MIDI event type MIDI Type d'évènement Cha&nnel: Ca&nal : MIDI channel Canal MIDI &Parameter: &Paramètre : MIDI parameter Paramètre MIDI &Logarithmic &Logarithmique In&vert In&verser &Bipolar &Bipolaire qtractorMidiEditEvent Zoom in (horizontal) Zoom - agrandir (horizontal) Zoom out (horizontal) Zoom - rétrécir (horizontal) Zoom reset (horizontal) Zoom - réinitialiser (horizontal) qtractorMidiEditList C%1 C%1 qtractorMidiEditTime Play-head Jouer le début Edit-head Éditer le début Edit-tail Éditer la fin Loop-start Boucle-début Loop-end Boucle-fin Punch-in Punch-in Punch-out Punch-out Start: %1 End: %2 Length: %3 Début: %1 Fin: %2 Longueur: %3 qtractorMidiEditView Zoom in (vertical) Zoom - agrandir (vertical) Zoom out (vertical) Zoom - rétrécir (vertical) Zoom reset (vertical) Zoom - réinitialiser (vertical) qtractorMidiEditor C Do C#/Db Do#/Réb D Ré D#/Eb Ré#/Mib E Mi F Fa F#/Gb Fa#/Solb G Sol G#/Ab Sol#/Lab A La A#/Bb La#/Sib B Si Acoustic Bass Drum Tom basse acoustique Bass Drum 1 Tom basse 1 Side Stick Côté de baguette Acoustic Snare Caisse claire acoustique Hand Clap Clap de main Electric Snare Caisse claire électrique Low Floor Tom Tom basse grave Closed Hi-Hat Charley fermé High Floor Tom Tom basse aigu Pedal Hi-Hat Pédale de charley Low Tom Tom grave Open Hi-Hat Charley ouvert Low-Mid Tom Tom médium-grave Hi-Mid Tom Tom médium-aigu Crash Cymbal 1 Cymbale crash 1 High Tom Tom aigu Ride Cymbal 1 Cymbale ride 1 Chinese Cymbal Cymbale chinoise 1 Ride Bell Cloche de ride Tambourine Tambourine Splash Cymbal Cymbale splash Cowbell Cloche à vache Crash Cymbal 2 Cymbale crash 2 Vibraslap Vibraslap Ride Cymbal 2 Cymbale ride 2 Hi Bongo Bongo aigu Low Bongo Bongo grave Mute Hi Conga Bongo aigu silencié Open Hi Conga Bongo aigu ouvert Low Conga Conga grave High Timbale Timbale aigue Low Timbale Timbale grave High Agogo Agogo aigu Low Agogo Agogo grave Cabasa Cabasa Maracas Maracas Short Whistle Sifflet court Long Whistle Sifflet long Short Guiro Guiro court Long Guiro Guiro long Claves Clave Hi Wood Block Wood block aigu Low Wood Block Wood block grave Mute Cuica Cuica silenciée Open Cuica Cuica ouverte Mute Triangle Triangle silencié Open Triangle Triangle ouvert Bank Select (coarse) Sélection de banque (grossier) Modulation Wheel (coarse) Molette de modulation (grossier) Breath Controller (coarse) Contrôleur de respiration (grossier) Foot Pedal (coarse) Pédale de pied (grossier) Portamento Time (coarse) Temps de portamento (grossier) Data Entry (coarse) Entrée de donnée (grossier) Volume (coarse) Volume (grossier) Balance (coarse) Balance (grossier) Pan Position (coarse) Position de panoramique (grossier) Expression (coarse) Expression (grossier) Effect Control 1 (coarse) Contrôle de l'effet 1 (grossier) Effect Control 2 (coarse) Contrôle de l'effet 2 (grossier) General Purpose Slider 1 Charriot d'utilisation générale 1 General Purpose Slider 2 Charriot d'utilisation générale 2 General Purpose Slider 3 Charriot d'utilisation générale 3 General Purpose Slider 4 Charriot d'utilisation générale 4 Bank Select (fine) Sélection de banque (fin) Modulation Wheel (fine) Molette de modulation (fin) Breath Controller (fine) Contrôleur de respiration (fin) Foot Pedal (fine) Pédale de pied (fin) Portamento Time (fine) Temps de portamento (fin) Data Entry (fine) Entrée de donnée (fin) Volume (fine) Volume (fin) Balance (fine) Balance (fin) Pan Position (fine) Position de panoramique (fin) Expression (fine) Expression (fin) Effect Control 1 (fine) Contrôle de l'effet 1 (grossier) Effect Control 2 (fine) Contrôle de l'effet 2 (grossier) Hold Pedal (on/off) Maintien de la pédale (on/off) Portamento (on/off) Portamento (on/off) Soft Pedal (on/off) Pédale molle (on/off) Legato Pedal (on/off) Pédale de legato (on/off) Hold 2 Pedal (on/off) Maintien 2 de la pédale (on/off) Sound Variation Variation de son General Purpose Button 1 (on/off) Bouton d'utilisation générale 1 (on/off) General Purpose Button 2 (on/off) Bouton d'utilisation générale 2 (on/off) General Purpose Button 3 (on/off) Bouton d'utilisation générale 3 (on/off) General Purpose Button 4 (on/off) Bouton d'utilisation générale 4 (on/off) Effects Level Niveau d'effets Chorus Level Niveau de chorus Celeste Level Niveau de celeste Phaser Level Niveau de phaser Data Button Increment Incrémentation du bouton de donnée Data Button Decrement Décrémentation du bouton de donnée Non-Registered Parameter (fine) Paramètre non-enregistré (fin) Non-Registered Parameter (coarse) Paramètre non-enregistré (grossier) Registered Parameter (fine) Paramètre enregistré (fin) Registered Parameter (coarse) Paramètre enregistré (grossier) All Sound Off Extinction de tous les sons All Controllers Off Extinction de tous les contrôleurs Local Keyboard (on/off) Clavier local (on/off) All Notes Off Extinction de toutes les notes Omni Mode Off Mode omni off Omni Mode On Mode omni on Mono Operation Mono opération Poly Operation Poly opération Pitch Bend Sensitivity Sensibilité de molette de hauteur Fine Tune Accordage fin Coarse Tune Accordage grossier Tuning Program Programme d'accordage Tuning Bank Banque d'accordage Vibrato Rate Taux de vibrato Vibrato Depth Profondeur de vibrato Vibrato Delay Délai de vibrato Filter Cutoff Coupure de filtre Filter Resonance Résonnance de filtre Sostenuto Pedal (on/off) Pédale de soutien (on/off) Release Time Temps de relâche Attack Time Temps d'attaque Brightness Luminosité Decay Time Temps de déclin Tremolo Level Niveau de trémolo EG Attack Attaque d'égalisation EG Decay Déclin d'égalisation EG Release Relâche d'égalisation Drum Filter Cutoff Coupure de filtre de batterie Drum Filter Resonance Résonnance de filtre de batterie Drum EG Attack Attaque d'égalisation de batterie Drum EG Decay Déclin d'égalisation de batterie Drum Pitch Coarse Hauteur grossière de batterie Drum Pitch Fine Hauteur fine de batterie Drum Level Niveau de batterie Drum Pan Panoramique de batterie Drum Reverb Send Envoi de réverbétation de batterie Drum Chorus Send Envoi de chorus de batterie Drum Variation Send Envoi de variation de batterie Modulation Wheel (14bit) Molette de modulation (14 bit) Breath Controller (14bit) Contrôleur de respiration (14 bit) Foot Pedal (14bit) Pédale de pied (14 bit) Portamento Time (14bit) Temps de portamento (14 bit) Volume (14bit) Volume (14 bit) Balance (14bit) Balance (14 bit) Pan Position (14bit) Position de panoramique (14 bit) Expression (14bit) Expression (14 bit) Effect Control 1 (14bit) Effet de contrôle 1 (14 bit) Effect Control 2 (14bit) Effet de contrôle 2 (14 bit) General Purpose Slider 1 (14bit) Charriot d'utilisation générale 1 (14 bit) General Purpose Slider 2 (14bit) Charriot d'utilisation générale 2 (14 bit) General Purpose Slider 3 (14bit) Charriot d'utilisation générale 3 (14 bit) General Purpose Slider 4 (14bit) Charriot d'utilisation générale 4 (14 bit) Chromatic Chromatique Major Majeure Minor Mineure Melodic Minor (Asc) Mélodique mineure (Asc) Melodic Minor (Desc) Mélodique mineure (Desc) Whole Tone Ton complet Pentatonic Major Pentatonique majeure Pentatonic Minor Pentatonique mineure Pentatonic Blues Pentatonique blues Pentatonic Neutral Pentatonique neutre Octatonic (H-W) Octatonique (H-W) Octatonic (W-H) Octatonique (H-W) Ionian Ionienne Dorian Dorienne Phrygian Phrygienne Lydian Lydienne Mixolydian Mixolydienne Aeolian Aéolienne Locrian Locrienne Egyptian Égyptienne Eight Tone Spanish Espagnole à huit tons Hawaiian Hawaïenne Hindu Hindou Hirajoshi Hirajoshi Hungarian Major Hongroise majeure Hungarian Minor Hongroise mineure Hungarian Gypsy Hongroise tzigane Japanese (A) Japonaise (A) Japanese (B) Japonaise (B) Jewish (Adonai Malakh) Juive (Adonai Malakh) Jewish (Ahaba Rabba) Juive (Ahaba Rabba) Jewish (Magen Abot) Juive (Magen Abo) Oriental (A) Orientale (A) Oriental (B) Orientale (B) Oriental (C) Orientale (C) Roumanian Minor Roumaine mineure Neapolitan Napolitaine Neapolitan Major Napolitaine majeure Neapolitan Minor Napolitaine mineure Overtone Harmonique Leading Whole Tone Tonalité principale entière Nine Tone Scale Échelle à neuf tons Dominant Seventh Dominante septième Augmented Augmentée Algerian Algérienne Arabian (A) Arabe (A) Arabian (B) Arabe (B) Balinese Balinaise Chinese Chinoise Diminished Diminuée Japanese (Ichikosucho) Japonaise (Ichikosucho) Japanese (Taishikicho) Japonaise (Taishikicho) Javaneese Javanaise Marva Theta Marva Thêta Mela Bhavapriya Mela Chakravakam Mela Chalanata Mela Chitrambari Mela Dharmavati Mela Dhatuvardhani Mela Dhavalambari Mela Divyamani Mela Ganamurti Mela Gangeyabhusani Mela Gavambodhi Mela Gayakapriya Mela Hatakambari Mela Jalarnavam Mela Jhalavarali Mela Jhankaradhvani Mela Jyotisvarupini Mela Kamavarardhani Mela Kantamani Mela Kosalam Mela Latangi Mela Manavati Mela Mararanjani Mela Naganandini Mela Namanarayani Mela Navanitam Mela Nitimati Mela Pavani Mela Ragavardhani Mela Raghupriya Mela Ramapriya Mela Rasikapriya Mela Ratnangi Mela Risabhapriya Mela Rupavati Mela Sadvidhamargini Mela Salagam Mela Sanmukhapriya Mela Sarasangi Mela Senavati Mela Subhapantuvarali Mela Sucharitra Mela Sulini Mela Suryakantam Mela Syamalangi Mela Tanarupi Mela Vagadhisvari Mela Vanaspati Mela Varunapriya Mela Yagapriya Persian Persienne Purvi Theta Spanish Gypsy Espagnole tzigane Todi Theta Enigmatic Énigmatique Kumoi Lydian Augmented Lydienne augmentée Pelog Prometheus Prométéenne Prometheus Neapolitan Napolitaine prométéenne Six Tone Symmetrical Symétrique à six tons Super Locrian Super locrienne Lydian Minor Lydienne mineure Lydian Diminished Lydienne diminuée Half Diminished Semi-diminuée Bhairav Yaman Todi Jog Multani Darbari Malkauns Bhoopali Shivaranjani Marwa Minor 5 Mineure 5 Major 5 Majeure 5 5 5 45 45 457 457 M 6 M 6 MIDI Editor Éditeur MIDI cut couper delete supprimer insert range Insérer plage remove range enlever plage move déplacer edit éditer resize redimensionner rescale remise à l'échelle paste coller Time: %1 Type: Temps: %1 Type: Note On (%1) %2 Velocity: %3 Duration: %4 Note On (%1) %2 Velocité: %3 Durée: %4 Key Press (%1) %2 Value: %3 Touche Appuyée (%1) %2 Valeur: %3 Controller (%1) Name: %2 Value: %3 Contrôleur (%1) Nom: %2 Valeur: %3 RPN (%1) Name: %2 Value: %3 RPN (%1) Nom: %2 Valeur: %3 NRPN (%1) Name: %2 Value: %3 NRPN (%1) Nom: %2 Valeur: %3 Control 14 (%1) Name: %2 Value: %3 Contrôle 14 (%1) Nom: %2 Valeur: %3 Pgm Change (%1) Pgm Change (%1) Chan Press (%1) Can Appuyé (%1) Pitch Bend (%1) Pitch Bend (%1) SysEx (%1 bytes) Data: SysEx (%1 bytes) Données : Unknown (%1) Inconnu (%1) Start: %1 End: %2 Length: %3 Début: %1 Fin: %2 Longueur: %3 qtractorMidiEditorForm MIDI Editor Éditeur MIDI &Track &Piste &File &Fichier Select &Mode &Mode sélection &Select &Sélection I&nsert I&nsérer Remo&ve Enle&ver &Tools &Outils &Edit &Éditer &View &Affichage Instrum&ent Instrum&ent &Toolbars &Barre d'outils &Windows &Fenêtres Not&e Type Typ&e note Val&ue Type Type vale&ur &Ghost Track Piste &fantôme &Zoom &Zoom S&nap A&ncrer Sc&ale É&chelle T&ransport T&ransport &Note &Note St&ep Pa&s &Help &Aide &Save &Sauvegarder Save Sauvegarder Save current MIDI clip to existing file name Sauvegarder le clip MIDI courant vers le nom de fichier existant Save &As... S&auvegarder sous... Save As Sauvegarder Sous Save as Sauvegarder sous Save current MIDI clip with another file name Sauvegarder le clip MIDI courant vers un autre nom de fichier &Mute &Muet Mute Silence Mute current MIDI clip Silencer le clip MIDI actuel &Unlink &Délier Unlink Délier Unlink current MIDI clip Délier le clip MIDI courant Recor&d Enre&gistrer Record current MIDI clip (overdub) Enregistrer clip MIDI courant (overdub) &Inputs &Entrées Track Inputs Entrées piste Track inputs Entrées piste Show current MIDI clip/track input bus connections Afficher les connexions du bus d'entrée du clip/piste MIDI courant &Outputs &Sorties Track Outputs Sorties piste Track outputs Sorties piste Show current MIDI clip/track output bus connections Afficher les connexions du bus de sortie du clip/piste MIDI courant &Properties... &Propriétés... Track Properties Propriétés piste Track properties Propriétés piste Edit current MIDI clip/track properties Éditer les propriétés MIDI du clip/piste courant Shift+F2 Maj+F2 Properties Propriétés Edit current MIDI clip properties Éditer les propriétés du clip MIDI courant F4 F4 &Range Set &Réglage plage Clip Range Plage clip Clip range Plage clip Set edit-range from clip extents Régler la plage d'édition à partir des dimensions du clip &Loop Set Réglage &Boucle Clip Loop Boucle clip Clip loop Boucle clip Set loop-range from clip extents Régler la plage de boucle à partir des dimensions du clip &Close &Fermer Close Fermer Close this MIDI clip editor Fermer cet éditeur de clip MIDI &Undo &Défaire Undo Défaire Undo last edit operation Défaire la dernière opération d'édition Ctrl+Z Ctrl+Z &Redo &Refaire Redo Refaire Redo last edit operation Refaire la dernière opération d'édition Ctrl+Shift+Z Ctrl+Maj+Z Cu&t Cou&per Cut Couper Cut current selection into the local clipboard Couper la sélection courante dans le presse-papiers local Ctrl+X Ctrl+X &Copy &Copier Copy Copier Copy current selection to the local clipboard Copier la sélection courante dans le presse-papiers local Ctrl+C Ctrl+C &Paste Co&ller Paste Coller Paste local clipboard contents into the current MIDI clip Coller le contenu du presse-papiers local dans le clip MIDI courant Ctrl+V Ctrl+V Past&e Repeat... Répét&er Coller... Paste Repeat Répéter Coller Paste repeat Répéter coller Paste/repeat local clipboard contents into the current MIDI clip Coller/répéter le contenu du presse-papiers local dans le clip MIDI courant Ctrl+Shift+V Ctrl+Maj+V &Delete &Supprimer Delete Supprimer Delete current selection Supprimer la sélection courante Del Suppr Edit Of&f Edition Of&f Edit Off Edition Off Edit off Edition off Set edit mode off Régler le mode édition à off Edit &On Edition &On Edit On Edition On Edit on Edition on Set edit mode on Régler le mode édition à on Edit &Draw Edition &Dessin Edit draw mode Mode édition dessin Edit draw mode (notes) Mode édition dessin (notes) &All &Tout Select All Sélectionner tout Select all Sélectionner tout Ctrl+A Ctrl+A &None &Rien Select None Sélectionner rien Select none Sélectionner rien Ctrl+Shift+A Ctrl+Maj+A &Invert &Inverser Select Invert Sélectionner inverse Select invert Sélectionner inverse Ctrl+I Ctrl+I &Range &Plage Select Range Sélectionner plage Select range Sélectionner plage Mark range as selected Marquer la plage comme sélectionnée Ctrl+R Ctrl+P Insert Range Insérer plage Insert range Insérer plage Insert range as selected Insérer plage comme sélectionnée Ctrl+Ins Ctrl+Ins &Step Éta&pe Insert step Insérer une étape Insert step (rest) Insérer une étape (repos) Right DO NOT TRANSLATE Remove Range Enlever plage Remove range Enlever plage Remove range as selected Enlever la plage sélectionnée Ctrl+Del Ctrl+Suppr &Quantize... &Quantifier... Quantize Quantifier Quantize selection Quantifier la sélection &Transpose... &Transposer... Transpose Transposer Transpose selection Transposer la sélection &Normalize... &Normaliser... Normalize Normaliser Normalize selection Normaliser la sélection &Randomize... &Randomiser... Randomize Randomiser Randomize selection Randomiser la sélection Resi&ze... Redimen&sionner... Resize Redimensionner Resize selection Redimensionner la sélection Re&scale... Remi&se à l'échelle... Rescale Remise à l'échelle Rescale selection Remise à l'échelle de la sélection T&imeshift... Déc&alage temporel... Timeshift Décalage temporel Timeshift selection Décalage temporel de la sélection T&empo ramp... Rampe de t&empo... Tempo ramp Rampe de tempo Tempo ramp selection Sélection de rampe de tempo &Menubar Barre de &menu Menubar Barre de menu Show/hide the menubar Montrer/cacher la barre de menu Ctrl+M Ctrl+M &Statusbar Barre de &Status Statusbar Barre de status Show/hide the statusbar Montrer/cacher la barre de status File Toolbar Barre d'outils fichier File toolbar Barre d'outils fichier Show/hide the file toolbar Montrer/cacher la barre d'outils fichier Edit Toolbar Barre d'outils Edition Edit toolbar Barre d'outils édition Show/hide the edit toolbar Montrer/cacher la barre d'outils édition View Toolbar Affichage de la barre d'outils View toolbar Affichage de la barre d'outils Show/hide the view toolbar Montrer/cacher l'affichage de la barre d'outils &Transport &Transport Transport Toolbar Barre d'outils Transport Transport toolbar Barre d'outils transport Show/hide the transport toolbar Montrer/cacher la barre d'outils transport T&ime T&emps Time Toolbar Barre d'outils de temps Time toolbar Barre d'outils de temps Show/hide the time toolbar Monter/cacher la barre d'outils de temps &Scale &Echelle Scale Toolbar Barre d'outils Echelle Scale toolbar Barre d'outils échelle Show/hide the scale toolbar Montrer/cacher la barre d'outils échelle Thum&b V&ignette Thumb Toolbar Barre d'outils Vignette Thumb toolbar Barre d'outils vignette Show/hide the thumb view toolbar Montrer/cacher la barre d'outils vignette Note &Names &Noms de note Note Names Noms de note Note names Noms de note Whether to show note names Si l'on affiche les noms de note Note &Duration &Durée de note Note Duration Durée de note Note duration Durée de note Whether note events are shown proportional to duration Si les évènements de note sont affichés proportionnellement à leur durée Note &Color &Couleur Note Note Color Couleur Note Note color Couleur note Whether note events are colored according to pitch Si les évènements de note sont coloriés en accord avec leur hauteur &Value Color &Valeur Couleur Value Color Valeur Couleur Value color Valeur couleur Whether note events are colored according to value (velocity) Si les évènements de note sont coloriés en accord avec le valeur (vélocité) &Drum Mode Mode &Batterie Drum Mode Mode Batterie Drum mode Mode batterie Whether note onset events are displayed as diamonds Si les évènements de note onset sont affichés en tant que diamants &Events &Evènements View events Voir évènements Show/hide the events list Montrer/cacher la liste des évènements &Preview Notes &Prévisualisation Notes Preview Notes Prévisualisation Notes Preview notes Prévisualisation notes Preview notes while editing (scrub) Prévisualiser les notes pendant l'édition (scrub) F&ollow Playhead S&uivre la Tête de lecture Follow Playhead Suivre la Tête de lecture Follow playhead Suivre la tête de lecture &In &Entrée Zoom In Zoom avant Zoom in Zoom avant Ctrl++ Ctrl++ &Out &Sortie Zoom Out Zoom arrière Zoom out Zoom arrière Ctrl+- Ctrl+- &Reset &Réinitialiser Zoom Reset Réinitialiser zoom Zoom reset Réinitialiser zoom Ctrl+1 Ctrl+1 &Horizontal &Horizontal Horizontal Zoom Zoom horizontal Horizontal zoom Zoom horizontal Horizontal zoom mode Mode de zoom horizontal &Vertical &Vertical Vertical Zoom Zoom vertical Vertical zoom Zoom vertical Vertical zoom mode Mode de zoom vertical All Zoom Tout zoomer All zoom Tout zoomer All zoom mode Mode de zoom total &Zebra &Zèbrures Zebra Zèbrures Bar zebra view mode Mode de visualisation barres zèbrées &Grid &Grille Grid Grille Snap grid view mode Mode de l'affichage de la grille magnétique Too&l Tips Info-bu&lles Tool tips Info-bulles Floating tool tips view mode Mode d'affichage des info-bulles flottantes &Refresh &Rafraichir Refresh Rafraichir Refresh views Rafraîchir l'affichage F5 F5 &Backward &Arrière Backward Arrière Transport backward Transport arrière Backspace Backspace Re&wind Re&mbobinner Rewind Rembobinner Transport rewind Transport rembobinner F&ast Forward Av&ance Rapide Fast Forward Avance Rapide Fast forward Avance rapide Transport fast forward Transport avance rapide &Forward A&vance Forward Avance Transport forward Transport avance Step Backward Reculer d'un pas Step backward Reculer d'un pas Transport step backward Transport en arrière Step Forward Avancer d'un pas Step forward Avancer d'un pas Transport step forward Transport en avant Note Backward Note en arrière Step note backward Note d'étape vers l'arrière Transport step note backward Transport de la note d'étape vers l'arrière Note Forward Note suivante Step note forward Note d'étape vers l'avant Transport step note forward Transport de la note d'étape vers l'avant &Loop Bouc&le Loop Boucle Transport loop Transport boucle Ctrl+Shift+L Ctrl+Maj+L Loop &Set Ré&glage Boucle Loop Set Réglage Boucle Loop set Réglage boucle Transport loop set Paramètre de la boucle de transport Ctrl+L Ctrl+L &Stop &Stop Stop Stop Transport stop Transport stop &Play &Jouer Play Jouer Transport play/pause Transport jouer/pause Space Espace &Record &Enregistrer Record Enregistrer Transport record Transport enregistrer &Punch &Punch Punch Punch Punch in/out Punch in/out Transport punch in/out Transport punch in/out Ctrl+Shift+P Ctrl+Maj+P Punch Se&t Réglage pu&nch Punch Set Réglage punch Punch in/out set Réglage punch in/out Transport punch in/out set Paramètre du punch in/out du transport Ctrl+P Ctrl+P Pa&nic Pa&nique Panic Panique All MIDI tracks shut off (panic) Toutes les pistes MIDI éteintes (panique) &Shortcuts... &Raccourcis... Shortcuts Raccourcis Keyboard shortcuts Raccourcis clavier &About... À &propos... About À propos Show information about this application program Afficher des informations à propos de cette application About &Qt... À propos de &Qt... About Qt À propos de Qt Show information about the Qt toolkit Afficher des informations à propos du toolkit Qt Current time (play-head) Temps actuel (tête de lecture) Current tempo (BPM) Tempo actuel (BPM) Reset time-sig. Réinitialiser la signature-remporelle Set current snap to %1 Placer l'accroche actuelle sur %1 Note Velocity Vélocité Note Snap/beat Capture/Battement Note type Type note Value type Type valeur Parameter type Type de paramètre Scale key Clef échelle Scale type Type échelle MIDI clip name Nom du clip MIDI MIDI file name Nom du fichier MIDI MIDI track/channel Piste/canal MIDI MOD MOD MIDI modification state État de modification MIDI REC ENR MIDI clip record state État d'enregistrement du clip MIDI MUTE MUET MIDI clip mute state État muet du clip MIDI 00:00:00.000 00:00:00.000 MIDI clip duration Durée du clip MIDI Warning Attention The current MIDI clip has been changed: "%1" Do you want to save the changes? Le clip MIDI actuel a été modifié : "%1" Voulez-vous sauvegarder les changements ? Save MIDI Clip Sauvegarder le clip MIDI MIDI files (*.%1 *.smf *.midi) Fichiers MIDI (*.%1 *.smf *.midi) All files (*.*) Tous les fichiers (*.*) Channel %1 Canal %1 Track %1 Piste %1 [modified] [modifié] qtractorMidiEventList Events Evènements qtractorMidiEventListView::ItemDelegate edit %1 éditer %1 qtractorMidiEventListView::ItemModel Time Temps Type Type Name Nom Value Valeur Duration/Data Durée/donnée Frame Trame BBT BBT Note On (%1) Note On (%1) Note Off (%1) Note Off (%1) Key Press (%1) Touche appuyée (%1) Controller (%1) Contrôleur (%1) Control 14 (%1) Contrôle 14 (%1) RPN (%1) RPN (%1) NRPN (%1) NRPN (%1) Pgm Change Pgm Change Chan Press Chan Press Pitch Bend Pitch Bend SysEx SysEx Meta (%1) Méta (%1) Unknown (%1) Inconnu (%1) qtractorMidiListView Name Nom Fmt Fmt Tracks Pistes tpqn tpqn Path Chemin %1: MIDI file not found. %1 : fichier MIDI introuvable. Open MIDI Files Ouvrir fichiers MIDI MIDI files (*.%1 *.smf *.midi) Fichiers MIDI (*.%1 *.smf *.midi) All files (*.*) Tous les fichiers (*.*) qtractorMidiMixerMeter Volume (%) Volume (%) % % Pan: %1 Pan : %1 Volume: %1% Volume : %1% qtractorMidiSysexForm MIDI SysEx SysEx MIDI Name Nom Size Taille Data (hex) Donnée (hex) Import from SysEx file Importer à partir d'un fichier SysEx &Import... &Importer... Export to SysEx file Exporter vers un fichier SysEx E&xport... E&xporter... Move SysEx item up on list order Déplacer l'élément SysEx vers le haut dans la liste &Up &Haut Move SysEx item down on list order Déplacer l'élément SysEx vers le bas dans la liste &Down &Bas Open SysEx Ouvrir SysEx Sysex name Nom SysEx Save SysEx Sauvegarder SysEx Delete SysEx Supprimer SysEx Create SysEx item Créer un élément SysEx &Add &Ajouter Update SysEx item Mettre à jour un élément SysEx Upda&te Me&ttre à jour Remove SysEx item Supprimer un élément SysEx &Remove &Supprimer SysEx files (*.%1) Fichiers SysEx (*.%1) MIDI files (*.mid *.smf *.midi) Fichiers MIDI (*.mid *.smf *.midi) All files (*.*) Tous les fichiers (*.*) Import SysEx Files Importer fichiers SysEx Export SysEx File Exporter fichier SysEx Warning Attention The SysEx file already exists: "%1" Do you want to replace it? Le fichier SysEx existe déjà : "%1" Voulez-vous le remplacer ? About to replace SysEx: "%1" Are you sure? Sur le point de remplacer SysEx: "%1" Êtes-vous sûr ? About to delete SysEx: "%1" Are you sure? Sur le point de supprimer SysEx: "%1" Êtes-vous sûr ? SysEx settings have been changed. Do you want to apply the changes? Les réglages SysEx ont été modifiés. Voulez-vous appliquer les changements ? Error Erreur SysEx could not be loaded: "%1". Sorry. SysEx n'a pas pu être chargé: "%1". Désolé. qtractorMidiThumbView MIDI Thumb view Affichage vignette MIDI qtractorMidiToolsForm MIDI Tools Outils MIDI Preset name Nom du préréglage Save preset Sauvegarder le préréglage Delete preset Supprimer le préréglage &Quantize &Quantifier Quantize selected events Quantifier les évènements sélectionnés &Time: &Temps : Quantize time Quantifier temps Quantize time percent Quantifier temps pourcent % % &Duration: &Durée : Quantize duration Quantifier durée Quantize duration percent Quantifier durée pourcent S&wing: F&luctuation : Swing-quantize time Fluctuation-quantifier temps Swing-quantize percent Fluctuation-quantifier pourcent Swing-quantize type Fluctuation-quantifier type Linear Linéaire Quadratic Quadratique Cubic Cubique &Scale: &Echelle : Scale-quantize key Echelle-quantifier clef Scale-quantize type Echelle-quantifier type &Transpose &Transpose Transpose selected events Transpose les évènements sélectionnés &Note: &Note : Transpose note Transposer note Transpose time Transposer temps Transpose time format Transposer format du temps Frames Trames Time Temps BBT BBT &Reverse &Renverser &Normalize &Normaliser Normalize selected events Normaliser les évènements sélectionnés &Percent: &Pourcent : Normalize percent Normaliser pourcent &Value: &Valeur : Normalize value Normaliser valeur &Compress &Compresser &Randomize &Randomiser Randomize selected events Randomiser les évènements sélectionnés Randomize note/pitch Randomiser note/hauteur Randomize time Randomiser temps Randomize duration Randomiser durée Randomize value Randomiser valeur Resi&ze Redimen&sionner Resize selected events Redimensionner les évènements sélectionnés Resize duration Redimensionner durée Resize duration format Redimensionner format de la durée Resize value Redimensionner valeur Resize value mode Mode redimensionnement valeur Flat Plat Ramp Rampe Resize final value Redimensionner valeur finale &Legato: &Legato : Legato trim/extend type Type de legato réduire/étendre Normal Normal Trim Réduire Extend Étendre Legato trim/extend length Longueur de legato réduire/étendre Legato mode Mode de legato Mono Mono Poly Poly &Join &Joindre &Split: &Séparer : Split notes length Séparer la longueur de notes Split notes offset Séparer le décalage de notes Relative Relatif Absolute Absolue Re&scale R&emettre à l'échelle Rescale selected events Remettre à l'échelle les évènements sélectionnés Rescale time Remettre à l'échelle temps Rescale duration Remettre à l'échelle durée Rescale value Remettre à l'échelle valeur &Invert &Inverser T&imeshift Dé&calage temporel Timeshift selected events Décalage temporel des évènements sélectionnés Timeshift Décalage temporel P: P : Timeshift parameter Paramètre de décalage temporel Timeshift parameter (log) Paramètre de décalage temporel (log) Timeshift curve Courbe de décalage temporel P = 0 : no change. P > 0 : accelerating shift. P < 0 : slowing down shift. Edit head/tail (blue) markers define the shift range. P = 0 : pas de changement. P > 0 : décalage accéléré. P < 0 : décalage ralenti. Éditer les marqueurs tête/queue (bleu) pour définir la plage de décalage. Timeshift duration Durée de décalage temporel T&empo ramp Rampe de t&empo Tempo ramp selected events Rampe de tempo des événements sélectionnés Tempo ramp Rampe de tempo From Depuis Tempo ramp start Départ de la rampe de tempo to vers Temporamp end Fin de la rampe de tempo Edit head/tail (blue) markers define the ramp range. Les marqueurs de tête/queue d'édition (bleus) définissent la plage de rampe. Tempo ramp duration Durée de la rampe de tempo (default) (défaut) Warning Attention About to delete preset: "%1" Are you sure? Sur le point de supprimé un préréglage : "%1" Êtes-vous sûr ? none rien quantize quantifier transpose transposer normalize normaliser randomize randomiser resize redimensionner rescale remise à l'échelle timeshift décaler temporellement temporamp rampe de tempo qtractorMixer Inputs Entrées Tracks Pistes Outputs Sorties Mixer Mixeur qtractorMixerMeter Pan Pan qtractorMixerRackWidget &Inputs &Entrées &Outputs &Sorties &Monitor &Moniteur &Buses... &Bus... &Audio &Audio &MIDI &MIDI qtractorMixerStrip inputs entrées outputs sorties Connect %1 Connecter %1 (Audio) (Audio) (MIDI) (MIDI) (None) (Rien) In Entrée Out Sortie qtractorMonitorButton monitor moniteur qtractorOptionsForm &General &Général Session Session Default session &file format: Format de &fichier par défaut de la session : Default session file format (suffix) Format de fichier par défaut de la session (suffixe) Whether to create new sessions based on template Si on doit créer une nouvelle session à partir d'un modèle &New session template: &Nouveau modèle de session : New session template Nouveau modèle de session Browse for new session template Parcourir pour le nouveau modèle de session Whether to save backup versions of existing sessions S'il faut sauvegarder des versions de sauvegarde des sessions existantes Save &backup versions of existing sessions: Sauvegarder &des versions de sauvegarde de sessions existantes : Which mode to rename existing session files Quel mode pour le renommage de fichiers de session existants Increment previous version (default) Incrémente la version précédente (défaut) Increment current version Incrémente la version actuelle Whether to enable session auto-save (crash-recovery) S'il faut activer la sauvegarde automatique de session (récupération de plantage) Auto-save current working session every: Sauvegarder automatiquement la session actuelle toutes les : Auto-save period (minutes) Période de sauvegarde automatique (minutes) minutes minutes Options Options Whether to ask for confirmation on removal Si on doit demander une confirmation en cas de suppression &Confirm removals &Confirmer les suppressions Number of &recent files: Nombre de fichiers &récents : The maximum number of recent files to keep in menu Le nombre maximum de fichiers récent à conserver dans le menu Whether to capture standard output (stdout/stderr) into messages window Si on doit capturer la sortie standard (stdout/stderr) vers la fenêtre de messages Capture standard &output Capturer la s&ortie standard Whether to show the complete directory path of loaded session files Si on doit afficher le chemin complet des répertoires des fichiers de session chargés S&how complete path of session files Afficher le c&hemin complet des fichiers de session Whether to remove audio peak files on session close Si on doit supprimer les fichiers de pic audio lors de la fermeture de session Auto-remove audio pea&k files Auto-supprime les &fichiers pic audio Whether to keep all tool windows on top of the main window Si on doit conserver toutes les fenêtres d'outils au dessus de la fenêtre principale Keep tool &windows always on top Conser&ver les fenêtres outils toujours au dessus Whether to try dropping multiple audio files into the same track Si on doit essayer de déposer plusieurs fichiers audio dans la même piste &Drop multiple audio files into the same track &Déposer plusieurs fichiers audio dans la même piste Whether to ask for confirmation on archive directory removal Si on doit demander une confirmation lors de la suppression d'un répertoire archive C&onfirm archive removals C&onfirmer les suppressions d'archives Reverse &keyboard modifiers role (Shift/Ctrl) Inverse le rôle des modificateurs de clavier (Maj/Ctrl) (&k) Whether to reverse mouse middle-button role on keyboard modifiers (Shift/Ctrl) Si on doit inverser le rôle du bouton du milieu avec les modificateur clavier (Maj/Ctrl) Re&verse middle-button modifier role (Shift/Ctrl) In&verser le rôle du bouton du milieu avec le modificateur (Maj/Ctrl) Transport Transport Transport &mode: &Mode de transport : Transport control mode (JACK) Mode de contrôle du transport (JACK) None Rien Slave Esclave Master Maitre Full Complet Whether to start as timebase master (JACK) Si on doit démarrer comme base de temps maître (JACK) &Timebase Base de &temps &Loop recording mode (takes): Mode d'enregistrement de bouc&les (prises) : Loop recording mode (takes) Mode d'enregistrement de boucles (prises) First Première Last Dernière &Audio &Audio Capture / Export Capture / Exporte Audio compression quality to use on capture (record) and export Qualité de compression audio à utiliser lors de la capture (enregistrement) et de l'exportation Audio sample format to use on capture (record) and export Format d'échantillon audio à utiliser lors de la capture (enregistrement) et de l'exportation Audio file type to use on capture (record) and export Type de fichier audio à utiliser lors de la capture (enregistrement) et de l'exportation File &type: &Type de fichier : Sample &format: &Format d'échantillon : &Quality: &Qualité : Playback Jouer Whether to apply time-stretching when tempo changes Si on doit appliquer l'étirement temporel lorsque le tempo change Aut&omatic time-stretching Étirement temporel aut&omatique Sample-&rate converter type: Type de conve&rtisseur de fréquence d'échantillonnage : Sample-rate converter quality Qualité du convertisseur de fréquence d'échantillonnage Sinc (Best Quality) Sinc (Meilleure qualité) Sinc (Medium Quality) Sinc (Qualité moyenne) Sinc (Fastest) Sinc (Rapide) Zero Order Hold Bloqueur d'ordre zéro Linear Linéaire Whether to use WSOLA time-stretching Si on doit utiliser l'étirement temporel WSOLA &WSOLA time-stretching Étirement temporel &WSOLA &Offset (latency): Décalage (latence) &O : Metronome Audio offset (latency) Décalage audio métronome (latence) &Style theme: &Style de thème : Whether to apply WSOLA quick seek time-stretching Si on doit appliquer la recherche rapide WSOLA pour l'étirement temporel Whether to reverse keyboard modifiers role on transport positioning (Shift/Ctrl) Si on doit inverser le rôle des modificateurs de clavier sur le positionnement de transport (Maj/Ctrl) Whether to keep all editor windows on top of the main window S'il faut conserver toutes les fenêtres de l'éditeur au dessus de la fenêtre principale Keep &editor windows always on top Conserver les fenêtres de l'édit&eur toujours au dessus Whether to use RubberBand formant preserve Utilisation ou non de la préservation des formants RubberBand RubberBand &formant preserve Préservation des &formants RubberBand WSOLA quic&k seek Rec&herche rapide WSOLA Whether to use RubberBand R3 finer engine Utilisation ou non du moteur plus fin RubberBand R3 RubberBand R&3 finer engine Moteur plus fin RubberBand R&3 Whether to have separate audition/pre-listening player output ports Si on doit avoir des ports de sortie séparés pour le lecteur audio pour l'audition/pré-écoute Dedicated au&dition/pre-listening player outputs: Sorties dédiées pour le lecteur audio pour l'au&dition/pré-écoute : Whether to auto-connect dedicated audio player outputs Si on doit auto-connecter les sorties dédiées pour le lecteur audio Auto-&connect Auto-&connecter Connections Connexions Whether to warn about audio self-connections Avertir ou non de l'existence d'autoconnexions audio &Warn about self-connections A&vertir des autoconnexions Metronome Métronome Whether to enable the audio metronome Si on doit activer le métronome audio &Enable audio metronome Activ&er le métronome audio &Count-in: Dé&compte : Count-in mode Mode décompte Recording Enregistrement Count-in number Numéro de décompte beats battements &File (bar): &Fichier (barre) : Metronome Audio filename (bar) Fichier audio métronome (barre) Browse for sample audio file (bar) Parcourir pour le fichier audio échantillon (barre) &Gain (bar): &Gain (barre) : Metronome gain (bar) Métronome gain (barre) dB dB &File (beat): &Fichier (battement) : Metronome Audio filename (beat) Nom de fichier audio métronome (battement) Browse for sample audio file (beat) Parcourir pour le fichier échantillon audio (battement) &Gain (beat): &Gain (battement) : Metronome gain (beat) Métronome gain (battement) Whether to have separate audio metronome output ports Si on doit avoir des ports de sortie audio dédiés pour le métronome Dedicated a&udio metronome outputs: Sorties a&udio métronome dédiées : Whether to auto-connect dedicated audio metronome outputs Si on doit auto-connecter les sorties audio métronome dédiées Auto-co&nnect Auto-co&nnecter &MIDI &MIDI File &format: Format de &fichier : MIDI file format to use on capture (record) and export Format de fichier MIDI à utiliser lors de la capture (enregistrement) et l'exportation &Quantize: &Quantifier : MIDI capture (record) quantization Quantification de la capture MIDI (enregistrement) Queue &timer (resolution): File d'attente minu&teur (résolution) : Queue timer (resolution) File d'attente minuteur (résolution) Whether to enable MIDI queue time drift correction Si on doit activer la correction de décalage temporel de la queue MIDI E&nable MIDI queue time drift correction Ac&tiver la correction du décalage temporelle de la queue MIDI Whether to have separate MIDI player output ports Si on doit avoir des ports de sorties séparés pour le lecteur MIDI Dedicated MIDI p&layer outputs Sorties &lecteur MIDI dédiées Whether to reset/resend all controllers on playback start S'il faut réinitialiser/renvoyer tous les contrôleurs au début de la lecture &Reset all controllers on playback start &Réinitialiser tous les contrôleurs au début de la lecture Control Contrôle &MMC: &MMC : MIDI Machine Control (MMC) mode Mode de contrôle machine MIDI (MMC) Input Entrée Output Sortie Duplex Duplex &Device: &Périphérique : MIDI Machine Control (MMC) device id. ID du périphérique de contrôle machine MIDI (MMC). &SPP: &SPP : MIDI Song Position pointer (SPP) control mode Mode de contrôle pointeur de position de chanson MIDI (SPP) MIDI Cloc&k: Horlo&ge MIDI : MIDI Clock control mode Mode de contrôle horloge MIDI Whether to have separate MIDI control ports Si on doit avoir des ports de contrôle MIDI séparés Dedicated MIDI &control input/output Entrée/sortie de &contrôle MIDI dédiée Whether to enable the MIDI metronome Si on doit activer le métronome MIDI &Enable MIDI metronome &Activer le métronome MIDI &Channel: &Canal : Metronome MIDI channel Canal du métronome MIDI &Note (bar): &Note (barre) : Metronome MIDI note (bar) Note MIDI métronome (barre) &Velocity (bar): &Vélocité (barre) : Metronome MIDI velocity (bar) Vélocité du métronome MIDI (barre) &Duration (bar): &Durée (barre) : Metronome MIDI duration (bar) Durée MIDI métronome (barre) &Note (beat): &Note (battement) : Metronome MIDI note (beat) Note MIDI métronome (battement) &Velocity (beat): &Vélocité (battement) : Metronome MIDI velocity (beat) Vélocité MIDI métronome (battement) &Duration (beat): &Durée (battement) : Metronome MIDI duration (beat) Durée MIDI métronome (pulsation) Whether to have separate MIDI metronome output port Si on doit avoir un port de sortie MIDI séparé pour le métronome Dedicated M&IDI metronome output Sortie M&IDI métronome dédiée Metronome MIDI offset (latency) Décalage MIDI métronome (latence) &Display A&fficher Defaults Défauts &Time display format: Format d'affichage du &temps : Time display format Format d'affichage du temps Frames Trames Time Temps BBT BBT &Base font size: Taille de la police de &base : Base application font size (pt.) Taille de base de la police de l'application (pt.) (default) (défaut) 6 6 7 7 8 8 9 9 10 10 11 11 12 12 Whether to use desktop environment native dialogs. Si on doit utiliser des dialogues natifs de l'environnement de bureau. Use desktop environment &native dialogs Utiliser des dialogues &natifs de l'environnement de bureau Manage custom color palette themes Gérer les thèmes de palette de couleurs personnalisés &Icons theme: Thème d'&icones : Custom icons theme directory Répertoire des icônes personnalisées Browse for custom icons theme directory Recherche de répertoire de thème d'icônes personnalisées St&yle sheet: Feuille de st&yle : Custom style sheet (*.qss) Feuille de style personnalisée (*.qss) Browse for custom style sheet (*.qss) Recherche d'une feuille de style personnalisée (*.qss) Move down path Déplacement vers le bas du chemin Whether to select plugin's editor (GUI) if more than one are available Permettre la sélection de l'éditeur de greffon (interface graphique) si plusieurs sont disponibles Blacklist Liste noire Plugin blacklist path Chemin de la liste noire des greffons Browse plugin blacklist path Parcourir le chemin de la liste noire des greffons Add plugin blacklist path Ajouter le chemin de la liste noire des greffons Plugin blacklist paths Chemins de la liste noire des greffons Remove plugin blacklist path Supprimer le chemin de la liste noire des greffons Clear plugin blacklist paths Effacer les chemins de la liste noire des greffons &Clear Effa&cer Custom Personnalisée &Color theme: Thème de &couleur : Custom color palette theme Palette de couleur personnalisée Wonton Soup DO NOT TRANSLATE KXStudio DO NOT TRANSLATE Custom widget style theme Style de thème de widget personnalisé Meters Mesureurs &Audio: &Audio : Audio meter level Niveau audiomètre Over Au delà 0 dB 0 dB 3 dB 3 dB 6 dB 6 dB 10 dB 10 dB Audio meter color Couleur audiomètre Select custom audio meter color Sélectionner une couleur personnalisée pour l'audiomètre ... ... &MIDI: &MIDI : MIDI meter level Niveau MIDImètre Peak Pic MIDI meter color Couleur MIDImètre Select custom MIDI meter color Sélectionner une couleur personnalisée pour le MIDImètre Reset meter colors to default Réinitialiser les couleurs avec les valeurs par défaut &Reset &Réinitialiser Messages Messages Sample messages text font display Exemple d'affichage des polices de texte des messages Select font for the messages text display Sélectionner une police pour l'affichage du texte des messages &Font... &Police... Whether to keep a maximum number of lines in the messages window Si on doit conserver un maximum de nombre de lignes dans la fenêtre de messages M&essages limit: Limite des m&essages : The maximum number of message lines to keep in view Le nombre maximum de lignes de messages à conserver dans l'affichage lines lignes Whether to hold auto-scrolling (follow play-head) on edits. Si on doit maintenir le défilement automatique (suivre la tête de lecture) lors de l'édition. &Hold auto-scrolling (follow play-head) on edits &Maintenir le défilement automatique (suivre la tête de lecture) lors des modifications Trac&k color saturation: Saturation de co&uleur de piste : Default new track color saturation Saturation par défaut de la couleur d'une nouvelle piste % % Back Retour Logging Enregistrement Messages log file Fichier d'enregistrement des messages Browse for the messages log file location Parcourir pour le fichier d'enregistrement des messages Whether to activate a messages logging to file. Si on doit activer l'enregistrement des messages vers un fichier. Messages &log file: Fichier d'enregis&trement des messages : &Plugins &Greffons Paths Chemins Plugin type Type de greffon Plugin path Chemin greffon Browse plugin path Parcourir le chemin du greffon Add plugin path Ajouter un chemin de greffon &Add &Ajouter Plugin paths Chemins de greffons Remove plugin path Supprimer un chemin de greffon &Remove &Supprimer Move up path Déplacer vers le haut &Up &Haut &Down &Bas &LV2 Presets directory: Répertoire des préréglages &LV2 : LV2 Presets directory (default: ~/.lv2) Répertoire des préréglages LV2 (défaut: ~/.lv2) Browse LV2 Presets directory Parcourir le répertoire des préréglages LV2 Instruments Instruments Whether to have separate audio output ports Si on doit avoir des ports audio séparés Dedicated audi&o outputs: Sorties audi&o dédiées : Whether to auto-connect dedicated audio output ports Si on doit auto-connecter les sorties audio dédiées Au&to-connect Au&to-connecter Editor Éditeur Whether to open plugin's editor (GUI) by default Si on doit ouvrir l'éditeur (interface graphique) du greffon par défaut Open plugin's &editor (GUI) by default Ouvrir l'édit&eur (interface graphique) du greffon par défaut &Select plugin's editor (GUI) if more than one are available &Sélectionner l'éditeur (interface graphique) du greffon si plusieurs sont disponibles XML Default (*.%1) Défaut XML (*.%1) XML Regular (*.%1) Régulier XML (*.%1) ZIP Archive (*.%1) Archive ZIP (*.%1) (Any) (Tout) Warning Attention Some settings have been changed. Do you want to apply the changes? Certains réglages ont été modifiés. Voulez-vous appliquer les changements ? Metronome Bar Audio File Fichier audio métronome - barre Metronome Beat Audio File Fichier audio métronome - battement Open Style Sheet Ouvrir la feuille de style Style Sheet files (*.%1) Fichiers de feuilles de style (*.%1) Icons Theme Directory Répertoire de thèmes d'icônes Audio Meter Color Couleur Audiomètre MIDI Meter Color Couleur MIDImètre Plug-in Directory Répertoire Greffon LV2 Presets Directory Répertoire des préréglage LV2 Plug-in Blacklist Liste noire de greffon Plug-in files (*.%1) Fichiers de greffon (*.%1) Messages Font Police de messages Messages Log Journal des messages Log files (*.%1) Fichiers journal (*.%1) All files (*.*) Tous les fichiers (*.*) Session Template Modèle de session Session template files (*.qtr *.qts *.%1) Fichiers de modèle de session (*.qtr *.qts *.%1) qtractorPaletteForm Color Themes Thèmes de couleurs Name Nom Current color palette name Nom de la palette de couleurs actuelle Save Sauvegarder Save current color palette name Sauvegarder le nom de la palette de couleurs actuelle Delete Supprimer Delete current color palette name Supprimer le nom de la palette de couleurs courante Palette Palette Current color palette Palette de couleurs actuelles Generate: Générer : Base color to generate palette Couleur de base pour générer la palette Reset Réinitialiser Reset all current palette colors Réinitialiser toutes les couleurs de la palette actuelle Import a custom color theme (palette) from file Importer un thème de couleur personnalisé (palette) à partir d'un fichier Import... Importer... Export a custom color theme (palette) to file Exporter un thème de couleur personnalisé (palette) vers un fichier Export... Exporter... Show Details Afficher les détails Import File - %1 Importer fichier - %1 Palette files (*.%1) Fichiers de palette (*.%1) Save Palette - %1 Sauvegarder la palette - %1 All files (*.*) Tous les fichiers (*.*) Warning - %1 Attention - %1 Could not import from file: %1 Sorry. N'a pas pu importer depuis le fichier : %1 Navré. Export File - %1 Exporter fichier - %1 Some settings have been changed. Do you want to discard the changes? Certains paramètres ont été modifiés. Souhaitez-vous abandonner les modifications ? Some settings have been changed: "%1". Do you want to save the changes? Certains paramètres ont été modifiés. "%1". Souhaitez-vous sauvegarder les modifications ? qtractorPaletteForm::PaletteModel Color Role Rôle de couleur Active Actif Inactive Inactif Disabled Désactivé qtractorPasteRepeatForm Paste Repeat Répéter le coller Repeat Répéter &Count: &Compte : Repeat count Répéter compte &Period: &Période : Repeat period Répéter période Repeat period format Format répétition période Frames Trames Time Temps BBT BBT Warning Attention Some settings have been changed. Do you want to apply the changes? Certains réglages ont été modifiés. Voulez-vous appliquer les changements ? qtractorPluginForm Plugin Properties Propriétés du greffon Open preset Ouvrir un préréglage Preset name Nom du préréglage Save preset Sauvegarder le préréglage Delete preset Supprimer le préréglage Alias: Alias : Plugin alias Alias de greffon Edit plugin Éditer greffon Edit Éditer Active Activer About À propos Outputs (Sends) Sorties (Envois) Sends Envois Inputs (Returns) Entrées (Retours) Returns Retours Auto-connect Auto-connexion Aux Send Bus: Bus d'envoi aux : Manage buses Gèrer les bus ... ... Audio bus I/O matrix Matrice d'E/S de bus audio I/O Matrix... Marice d'E/S... Direct Access Parameter Paramètre d'accès direct Direct Access Accès direct Page %1 Page %1 %1 [%2], %3 instance(s), %4 channel(s). %1 [%2], %3 instance(s), %4 canaux. (none) (rien) Open Preset Ouvrir le préréglage Preset files (*.%1) Fichiers de préréglages (*.%1) All files (*.*) Tous les fichiers (*.*) Error Erreur Preset could not be loaded from file: "%1". Sorry. Le préréglage n'a pas pu être chargé à partir du fichier: "%1". Désolé. Save Preset Sauvegarder le préréglage Preset could not be saved to file: "%1". Sorry. Le préréglage n'a pas pu être sauvegardé dans le fichier : "%1". Désolé. Warning Attention About to delete preset: "%1" (%2) Are you sure? Sur le point de supprimer le préréglage : "%1" (%2) Êtes-vous sûr ? Latency: %1 ms (%2 frames) Latence : %1 ms (%2 trames) (no latency) (pas de latence) &None &Rien qtractorPluginListView copy plugin copie greffon activate all plugins activer tous les greffons deactivate all plugins désactiver tous les greffons remove all plugins supprimer tous les greffons Import Plugins Importer des greffons XML files (*.%1) Fichiers XML (*.%1) All files (*.*) Tous les fichiers (*.*) Warning Attention About to remove and import all plugins: "%1" Are you sure? Prêt à retirer et importer tous les greffons : "%1" Êtes-vous sûr ? Export Plugins Exporter les greffons Aux Send: Envoi aux : &Move Here &Déplacer Ici &Copy Here &Copier Ici C&ancel Ab&andon &Add Plugin... &Ajouter Greffon... I&nserts I&nserts &Audio &Audio Add &Insert Ajouter &Insert Add &Aux Send Ajouter envoi &auxiliaire &Sends &Envois &Returns &Retours &MIDI &MIDI Add &Controller Ajouter un &contrôleur Ac&tivate Ac&tiver Acti&vate All Acti&ver tout Deactivate Al&l Désactiver &tout &Remove &Supprimer Re&move All Suppri&mer tout Move &Up Déplacer vers le &Haut Move &Down Déplacer vers le &Bas Pre&set Préréglage&s Dire&ct Access Accès dire&ct &None &Rien &Properties... &Propriétés... &Edit &Éditer &Import... &Importer... E&xport... E&xporter... &Outputs &Sorties &Dedicated &Dédié &Auto-connect &Auto-connecter qtractorPluginParamWidget Open File Ouvrir un fichier qtractorPluginSelectForm Plugins Greffons Reset filter Réinitialiser le filtre X X Plugin search string (regular expression) Chaîne de recherche de greffon (expression régulière) Plugin type Type de greffon Available plugins Greffons disponibles Name Nom Audio Audio MIDI MIDI Control Contrôle Modes Modes Path Chemin Index Index Instances Instances Type Type Plugin scanning in progress... Scan des greffons en cours... Rescan for available plugins (refresh) Re-scanner les greffons disponibles (rafraîchir) &Rescan &Re-scanner GUI IUG EXT EXT RT RT qtractorSessionForm Session Session &Name: &Nom : Session name Nom de session &Directory: &Répertoire : Whether to auto-name the session directory S'il faut nommer automatiquement le répertoire de session &Auto &Auto Session directory Répertoire de session Browse for session directory Parcourir le répertoire de session ... ... &Description: &Description : Session description Description de la session Properties Propriétés Time Temps Sample &Rate: &Fréquence d'échantillonnage : Sample rate (Hz) Fréquence d'échantillonnage (Hz) 44100 44100 48000 48000 96000 96000 192000 192000 &Tempo: &Tempo : Tempo (BPM) / Signature Tempo (BPM) / Signature T&icks/Beat: C&oches/Battement : Resolution (ticks/beat; tpqn) Résolution (coches/battement; tpqn) View Affichage &Snap/Beat: &Capture/Battement : Snap/beat Capture/battement &Pixels/Beat: &Pixels/Battement : Pixels/beat Pixels/Battement &Horizontal Zoom: Zoom &horizontal : Horizontal Zoom (%) Zoom horizontal (%) % % &Vertical Zoom: Zoom &vertical : Vertical Zoom (%) Zoom vertical (%) Warning Attention Session directory does not exist: "%1" Do you want to create it? Le répertoire de session n'existe pas: "%1" Voulez-vous le créer ? Some settings have been changed. Do you want to apply the changes? Certains réglages ont été modifiés. Voulez-vous appliquer les changements ? Session Directory Répertoire de session qtractorShortcutForm Shortcuts Raccourcis Shortcut search string (regular expression) Chaîne de recherche raccourcie (expression régulière) Menu/Action Menu/Action Description Description Keyboard Clavier MIDI Controller Contrôleur MIDI Search shortcuts Raccourcis de recherche Warning Attention Keyboard shortcut (%1) already assigned (%2). Le raccourci clavier (%1) est déjà affecté (%2). Keyboard shortcuts have been changed. Do you want to apply the changes? Des raccourcis clavier ont été modifiés. Voulez-vous appliquer les changements ? MIDI Controller shortcuts have been changed. Do you want to apply the changes? Des raccourcis de contrôleur MIDI ont été modifiés. Voulez-vous appliquer les changements ? &MIDI Controller... Contrôleur &MIDI... qtractorTakeRangeForm Range Plage Selection range Plage de sélection &Selection &Sélection Loop range Plage de boucle &Loop &Boucle Punch range Plage de punch &Punch &Punch Time Temps BBT BBT Edit range Plage d'édition &Edit &Edition Custom range Plage personnalisée &Custom &Personalisé St&art: &Début : Clip start Début clip Take Range Plage de prise En&d: &Fin : Clip offset Décalage clip Select Sélection Current take Prise actuelle Format Format Time display format Format d'affichage du temps Frames Trames Take %1 Prise %1 qtractorTempoAdjustForm Tempo Adjust Ajustement du tempo Metronome Métronome Tempo/Time signature Tempo/Signature temporelle &Detect &Détecter T&ap T&ape Range Plage &Start: &Début : Range start Plage de départ &Beats: &Battements : Time Temps BBT BBT &Length: &Longueur : &Tempo: &Tempo : R&eset R&einitialisation Range length Longueur de la plage Range beats Plage de battements A&djust A&juster Format Format Time display format Format d'affichage du temps Frames Trames Warning Attention Some settings have been changed. Do you want to apply the changes? Certains réglages ont été modifiés. Voulez-vous appliquer les changements ? qtractorThumbView Thumb view Affichage vignette qtractorTimeScale C Do B# Si# C# Do# Db Réb D Ré D# Ré# Eb Mib E Mi Fb Fab F Fa E# Mi# F# Fa# Gb Solb G Sol G# Sol# Ab Lab A La A# La# Bb Sib B Si Cb Dob qtractorTimeScaleForm Bar Barre Time Temps Tempo Tempo &Bar: &Barre : Tempo map / Markers Carte de Tempo / Marqueurs Marker Marqueur Bar location Position de la mesure T&ime: T&emps : Time/frame location Position du temps / cadre &Tempo: &Tempo : T&ap T&ape Marker text Texte du marqueur ... ... Marker color Couleur du marqueur Tempo Map / Markers Carte de tempo / marqueurs Key Clef Tempo (BPM) / Time signature Tempo (BPM) / signature temporelle &Key signature: Signature de clef (&K) Key signature (accidentals) Signature de clef (altérations) Key signature (mode) Signature de clef (mode) - - Major Majeure Minor Mineure &Marker: &Marqueur : Tempo &scale factor: Tempo &facteur d'échelle : Tempo scale factor Facteur d'échelle du tempo App&ly App&liquer Refresh tempo map Rafraichir la carte tempo Re&fresh Ra&fraichir Add node Ajouter un noeud &Add &Ajouter Update node Mettre à jour un noeud &Update &Mettre à jour Remove node Supprimer un noeud &Remove &Supprimer Close this dialog Fermer cette fenêtre Close Fermer Warning Attention Some settings have been changed. Do you want to apply the changes? Certains réglages ont été modifiés. Voulez-vous appliquer les changements ? About to remove tempo node: %1 (%2) %3 %4/%5 Are you sure? Sur le point de supprimer un noeud tempo: %1 (%2) %3 %4/%5 Êtes-vous sûr ? Some settings have been changed. Do you want to discard the changes? Certains réglages ont été modifiés. Voulez-vous abandonner ces changements ? tempo factor Facteur de tempo Marker Color Couleur du Marqueur &Refresh &Rafraichir qtractorTimeSpinBox &Frames T&rames &Time &Temps &BBT &BBT qtractorTrackForm Track Piste &Name: &Nom : Track name description Description du nom de piste Track icon Icône de piste Type Type Audio track type Type piste audio &Audio &Audio MIDI track type Type piste MIDI &MIDI &MIDI Input / Output Entrée / Sortie Input bus name Nom du bus d'entrée Output bus name Nom du bus de sortie Manage buses Gérer les bus ... ... MIDI / Instrument MIDI / Instrument &Program: &Programme : &Bank: &Banque : Bank &Select Method: Méthode de &sélection de banque : &Omni &Omni MIDI Omni: Capture All Channels MIDI Omni: Capture tous les canaux &Channel: &Canal : MIDI Channel (1-16) Canal MIDI (1-16) MIDI Patch: Instrument MIDI Patch: Instrument MIDI Patch: Bank Select Method MIDI Patch: Méthode de sélection de banque MIDI Patch: Drum Mode Patche MIDI : mode batterie MIDI Patch: Bank Patche MIDI : banque MIDI Patch: Program Patches MIDI : programme View / Colors Affichage / Couleurs &Foreground: &Premier plan : Foreground color Couleur du premier plan Select custom track foreground color Sélectionner une couleur de piste personnalisée pour le premier plan Bac&kground: &Arrière-plan : Background color Couleur d'arrière-plan Select custom track background color Sélectionner une couleur de piste personnalisée pour l'arrière-plan Auto Auto Plugins Greffons Track plugins Greffons piste Add plugin Ajouter greffon &Add... &Ajouter... Remove plugin Supprimer greffon &Remove &Supprimer Move plugin up Déplacer le greffon vers le haut &Up &Haut Move plugin down Déplacer le greffon vers le bas &Down &Bas Whether to enable plugin latency/delay compensation S'il faut activer la compensation de latence/délai de greffon &Latency compensation Compensation de &latence Current total latency Latence totale actuelle Normal Normal Bank MSB Banque MSB Bank LSB Banque LSB Patch Patch Custom &Icon... &Icône personnalisée... &Drums &Tambours Drum &Kit &Kit de batterie &Bass &Basse A&coustic Bass Basse a&coustique &Guitar &Guitare &Electric Guitar Guitare &électrique &Piano &Piano &Acoustic Piano Piano &acoustique &Microphone &Microphone Vi&ntage Microphone Microphone vi&ntage &Speaker &Haut parleur &Trumpet &Trompette &Violin &Violon Warning Attention Some settings have been changed. Do you want to apply the changes? Certains réglages ont été modifiés. Voulez-vous appliquer les changements ? (No instrument) (Pas d'instrument) %1 ms (%2 frames) %1 ms (%2 trames) (no latency) (pas de latence) (None) (Rien) Image files (%1) Fichiers image (%1) All files (*.*) Tous les fichiers (*.*) Track Icon Icône de piste Foreground Color Couleur d'avant-plan Background Color Couleur d'arrière-plan qtractorTrackList Nr Nr Track Name Nom de la piste Bus Bus Ch Ch Patch Patch Instrument Instrument qtractorTrackTime Play-head Jouer le début Edit-head Éditer le début Edit-tail Éditer la fin Loop-start Boucle-début Loop-end Boucle-fin Punch-in Punch-in Punch-out Punch-out Start: %1 End: %2 Length: %3 Début: %1 Fin: %2 Longueur: %3 qtractorTrackView Zoom in (horizontal) Zoom avant (horizontal) Zoom out (horizontal) Zoom arrière (horizontal) Zoom in (vertical) Zoom avant (vertical) Zoom out (vertical) Zoom arrière (vertical) Zoom reset Réinitialiser le zoom add clip ajouter un clip Start: %1 End: %2 Length: %3 Début: %1 Fin: %2 Longueur: %3 clip %1 clip %1 fade-in fondu en ouverture fade-out fondu en fermeture clip stretch étirement clip clip resize redimensionnement clip clip repeat répéter clip %1 automation %1 automation %1 clip %1 clip move automation déplacer l'automation paste automation coller l'automation cut couper delete supprimer split diviser move clip déplacer clip paste clip coller clip qtractorTracks Tracks Pistes new clip nouveau clip mute clip silencer le clip split clip diviser clip clip normalize normaliser clip quantize quantifier transpose transposer normalize normaliser randomize randomiser resize redimensionner rescale remise à l'échelle timeshift décalage temporel clip import importer clip Audio file import "%1" on %2 %3. Importation fichier audio"%1" sur %2 %3. Audio file import: "%1". Importation fichier audio: "%1". MIDI file import "%1" track-channel %2 on %3 %4. Importation fichier MIDI "%1" piste-canal %2 sur %3 %4. MIDI file import: "%1", track-channel: %2. Importation fichier MIDI: "%1", piste-canal: %2. clip merge fusionner clip Merge/Export Fusion/export Merge/Export Audio Clip Fusionner/exporter clip audio Audio clip merge/export: "%1" started... Fusion/Exportation du clip audio: "%1" démarrée... Audio clip merge/export: "%1" complete. Fusion/exportation du clip audio: "%1" terminée. Merge/Export MIDI Clip Fusionner/exporter clip MIDI MIDI clip merge/export: "%1" started... Fusion/Exportation clip MIDI: "%1" démarrée... MIDI clip merge/export: "%1" complete. Fusion/Exportation clip MIDI: "%1" terminée. clip cross-fade clip fondu-enchaîné Insert Range Insérer plage insert track range Insérer plage de piste Remove Range Enlever plage insert range Insérer plage tempo ramp rampe de tempo MIDI files (*.mid *.smf *.midi) Fichiers MIDI (*.mid *.smf *.midi) All files (*.*) Tous les fichiers (*.*) remove range enlever plage remove track range enlever la plage de piste Warning Attention About to remove track: "%1" Are you sure? Sur le point de supprimer une piste : "%1" Êtes-vous sûr ? MIDI file import "%1" on %2 %3. Importation fichier MIDI "%1" sur %2 %3. MIDI file import: "%1". Importation fichier MIDI: "%1". qtractor-1.5.9/src/PaxHeaders/qtractorMidiListView.cpp0000644000000000000000000000013215101070305020057 xustar0030 mtime=1761898693.082267639 30 atime=1761898693.082267639 30 ctime=1761898693.082267639 qtractor-1.5.9/src/qtractorMidiListView.cpp0000644000175000001440000001622615101070305020056 0ustar00rncbcusers// qtractorMidiListView.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 "qtractorMidiListView.h" #include "qtractorMidiFile.h" #include "qtractorOptions.h" #include "qtractorMessageList.h" #include #include #include //---------------------------------------------------------------------- // class qtractorMidiFileItem -- audio file list view item. // // Constructors. qtractorMidiFileItem::qtractorMidiFileItem ( const QString& sPath, qtractorMidiFile *pFile ) : qtractorFileListItem(sPath) { QTreeWidgetItem::setTextAlignment( qtractorMidiListView::Format, Qt::AlignRight); QTreeWidgetItem::setTextAlignment( qtractorMidiListView::Tracks, Qt::AlignRight); QTreeWidgetItem::setTextAlignment( qtractorMidiListView::Resolution, Qt::AlignRight); QTreeWidgetItem::setIcon(qtractorMidiListView::Name, QIcon::fromTheme("itemMidiFile")); QTreeWidgetItem::setText(qtractorMidiListView::Format, QString::number(pFile->format())); QTreeWidgetItem::setText(qtractorMidiListView::Tracks, QString::number(pFile->tracks())); QTreeWidgetItem::setText(qtractorMidiListView::Resolution, QString::number(pFile->ticksPerBeat())); QTreeWidgetItem::setText(qtractorMidiListView::Path, sPath); if (pFile->format() == 1) { // Add track sub-items... for (int iTrack = 0; iTrack < pFile->tracks(); ++iTrack) { new qtractorMidiChannelItem(this, QObject::tr("Track %1").arg(iTrack), iTrack); } } else { // Add channel sub-items... for (int iChannel = 0; iChannel < 16; ++iChannel) { new qtractorMidiChannelItem(this, QObject::tr("Channel %1").arg(iChannel + 1), iChannel); } } } // Tooltip renderer. QString qtractorMidiFileItem::toolTip (void) const { return QObject::tr("%1 (format %2)\n%3 tracks, %4 tpqn\n%5") .arg(QTreeWidgetItem::text(qtractorMidiListView::Name)) .arg(QTreeWidgetItem::text(qtractorMidiListView::Format)) .arg(QTreeWidgetItem::text(qtractorMidiListView::Tracks)) .arg(QTreeWidgetItem::text(qtractorMidiListView::Resolution)) .arg(QTreeWidgetItem::text(qtractorMidiListView::Path)); } //---------------------------------------------------------------------- // class qtractorMidiChannelItem -- MIDI track/channel view item. // // Constructor. qtractorMidiChannelItem::qtractorMidiChannelItem ( qtractorMidiFileItem *pFileItem, const QString& sName, unsigned short iChannel ) : qtractorFileChannelItem(pFileItem, sName, iChannel) { QTreeWidgetItem::setIcon(qtractorMidiListView::Name, QIcon::fromTheme("itemChannel")); } // Tooltip renderer. QString qtractorMidiChannelItem::toolTip (void) const { qtractorMidiFileItem *pFileItem = static_cast (QTreeWidgetItem::parent()); if (pFileItem == nullptr) return qtractorFileChannelItem::toolTip(); return QObject::tr("%1 (format %2)\n%3") .arg(pFileItem->text(qtractorMidiListView::Name)) .arg(pFileItem->text(qtractorMidiListView::Format)) .arg(QTreeWidgetItem::text(qtractorMidiListView::Name)); } //---------------------------------------------------------------------------- // qtractorMidiListView -- Group/File list view, supporting drag-n-drop. // // Constructor. qtractorMidiListView::qtractorMidiListView ( QWidget *pParent ) : qtractorFileListView(qtractorFileList::Midi, pParent) { QTreeWidget::setColumnCount(qtractorMidiListView::LastColumn + 1); QTreeWidgetItem *pHeaderItem = QTreeWidget::headerItem(); pHeaderItem->setText(qtractorMidiListView::Name, tr("Name")); pHeaderItem->setText(qtractorMidiListView::Format, tr("Fmt")); pHeaderItem->setText(qtractorMidiListView::Tracks, tr("Tracks")); pHeaderItem->setText(qtractorMidiListView::Resolution, tr("tpqn")); pHeaderItem->setText(qtractorMidiListView::Path, tr("Path")); pHeaderItem->setText(qtractorMidiListView::LastColumn, QString()); pHeaderItem->setTextAlignment( qtractorMidiListView::Format, Qt::AlignRight); pHeaderItem->setTextAlignment( qtractorMidiListView::Tracks, Qt::AlignRight); pHeaderItem->setTextAlignment( qtractorMidiListView::Resolution, Qt::AlignRight); QHeaderView *pHeader = QTreeWidget::header(); pHeader->resizeSection(qtractorMidiListView::Name, 160); QTreeWidget::resizeColumnToContents(qtractorMidiListView::Format); QTreeWidget::resizeColumnToContents(qtractorMidiListView::Tracks); QTreeWidget::resizeColumnToContents(qtractorMidiListView::Resolution); pHeader->resizeSection(qtractorMidiListView::Path, 160); } // File item factory method. qtractorFileListItem *qtractorMidiListView::createFileItem ( const QString& sPath ) { qtractorFileListItem *pFileItem = nullptr; qtractorMidiFile file; if (file.open(sPath)) { pFileItem = new qtractorMidiFileItem(sPath, &file); file.close(); } else { qtractorMessageList::append( tr("%1: MIDI file not found.").arg(sPath)); } return pFileItem; } // Prompt for proper file list open. QStringList qtractorMidiListView::getOpenFileNames (void) { QStringList files; const QString sExt("mid"); const QString& sTitle = tr("Open MIDI Files"); QStringList filters; filters.append(tr("MIDI files (*.%1 *.smf *.midi)").arg(sExt)); 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 filenames 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(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()) files = fileDialog.selectedFiles(); #endif return files; } // end of qtractorMidiListView.cpp qtractor-1.5.9/src/PaxHeaders/qtractorInstrument.cpp0000644000000000000000000000013215101070305017656 xustar0030 mtime=1761898693.072267607 30 atime=1761898693.072267607 30 ctime=1761898693.072267607 qtractor-1.5.9/src/qtractorInstrument.cpp0000644000175000001440000007553315101070305017663 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.9/src/PaxHeaders/qtractorMidiControlObserver.h0000644000000000000000000000013215101070305021106 xustar0030 mtime=1761898693.078267626 30 atime=1761898693.078267626 30 ctime=1761898693.078267626 qtractor-1.5.9/src/qtractorMidiControlObserver.h0000644000175000001440000000771315101070305021106 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.9/src/PaxHeaders/qtractorMidiEditor.h0000644000000000000000000000013215101070305017204 xustar0030 mtime=1761898693.080267632 30 atime=1761898693.080267632 30 ctime=1761898693.080267632 qtractor-1.5.9/src/qtractorMidiEditor.h0000644000175000001440000005006515101070305017202 0ustar00rncbcusers// qtractorMidiEditor.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 __qtractorMidiEditor_h #define __qtractorMidiEditor_h #include "qtractorMidiCursor.h" #include "qtractorMidiEditSelect.h" #include "qtractorMidiEvent.h" #include #include #include // Forward declarations. class qtractorScrollView; class qtractorRubberBand; class qtractorCommandList; class qtractorCommand; class qtractorMidiEditList; class qtractorMidiEditTime; class qtractorMidiEditView; class qtractorMidiEditEventScale; class qtractorMidiEditEvent; class qtractorMidiThumbView; class qtractorMidiEditCommand; class qtractorMidiClip; class qtractorTimeScale; class qtractorTrack; class QFrame; class QComboBox; class QCloseEvent; class QCursor; //---------------------------------------------------------------------------- // qtractorMidiEditor -- The main session track listview widget. class qtractorMidiEditor : public QSplitter { Q_OBJECT public: // Constructor. qtractorMidiEditor(QWidget *pParent); // Destructor. ~qtractorMidiEditor(); // MIDI clip sequence accessors. void setMidiClip(qtractorMidiClip *pMidiClip); qtractorMidiClip *midiClip() const; // MIDI clip properties accessors. const QString& filename() const; unsigned short trackChannel() const; unsigned short format() const; qtractorMidiSequence *sequence() const; // Event foreground (outline) color. void setForeground(const QColor& fore); const QColor& foreground() const; // Event background (fill) color. void setBackground(const QColor& back); const QColor& background() 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; // Drum mode (UI). void setDrumMode(bool bDrumMode); bool isDrumMode() const; // Edit (creational) mode. void setEditMode(bool bEditMode); bool isEditMode() const; // Edit draw (notes) mode. void setEditModeDraw(bool bEditModeDraw); bool isEditModeDraw() const; // Zoom (view) modes. enum { ZoomNone = 0, ZoomHorizontal = 1, ZoomVertical = 2, ZoomAll = 3 }; void setZoomMode(int iZoomMode); int zoomMode() const; // Zoom ratio accessors. void setHorizontalZoom(unsigned short iHorizontalZoom); unsigned short horizontalZoom() const; void setVerticalZoom(unsigned short iVerticalZoom); unsigned short verticalZoom() const; // Splitter sizes accessors. void setHorizontalSizes(const QList& sizes); QList horizontalSizes() const; void setVerticalSizes(const QList& sizes); QList verticalSizes() const; // Local time scale accessors. qtractorTimeScale *timeScale() const; unsigned long timeOffset() const; // Time-scale offset (in frames) accessors. void setOffset(unsigned long iOffset); unsigned long offset() const; // Time-scale length (in frames) accessors. void setLength(unsigned long iLength); unsigned long length() const; // Clip recording/overdub status. bool isClipRecordEx() const; // Ghost track accessors. void setGhostTrack(qtractorTrack *pGhostTrack); qtractorTrack *ghostTrack() const; // Minimum event width accessor. int minEventWidth() const; // Child widgets accessors. QFrame *editListHeader() const; qtractorMidiEditList *editList() const; qtractorMidiEditTime *editTime() const; qtractorMidiEditView *editView() const; qtractorMidiEditEventScale *editEventScale() const; qtractorMidiEditEvent *editEvent() const; QFrame *editEventFrame() const; qtractorMidiThumbView *thumbView() const; // Check whether step-input is on. bool isStepInputHead() const; // Step-input positioning. void setStepInputHead(unsigned long iStepInputHead, bool bSyncView = true); int stepInputHeadX() const; // Edit-head/tail accessors. void setEditHead(unsigned long iEditHead, bool bSyncView = true); int editHeadX() const; void setEditTail(unsigned long iEditTail, bool bSyncView = true); int editTailX() const; // Play-head positioning. void setPlayHead(unsigned long iPlayHead, bool bSyncView = true); int playHeadX() const; // Update time-scale to master session. void updateTimeScale(); // Play-head follow-ness. void setSyncView(bool bSyncView); bool isSyncView() const; // Note autition while editing. void setSendNotes(bool bSendNotes); bool isSendNotes() const; bool isSendNotesEx() const; // Note names display. void setNoteNames(bool bNoteNames); bool isNoteNames() const; // Note event value vs. duration display. void setNoteDuration(bool bNoteDuration); bool isNoteDuration() const; // Note event coloring. void setNoteColor(bool bNoteColor); bool isNoteColor() const; // Note event coloring. void setValueColor(bool bValueColor); bool isValueColor() const; // Snap-to-scale/quantize key accessor. void setSnapToScaleKey(int iSnapToScaleKey); int snapToScaleKey() const; // Snap-to-scale/quantize type accessor. void setSnapToScaleType(int iSnapToScaleType); int snapToScaleType() const; // Command predicate status. bool canUndo() const; bool canRedo() const; // Undo/redo last edit command. void undoCommand(); void redoCommand(); // Whether there's any items currently selected. bool isSelected() const; // Whether there's any items on the clipboard. static bool isClipboard(); // Clipboard commands. void cutClipboard(); void copyClipboard(); void pasteClipboard( unsigned short iPasteCount = 1, unsigned long iPastePeriod = 0); // Retrieve current paste period. // (as from current clipboard width) unsigned long pastePeriod() const; // Execute event removal. void deleteSelect(); // Select all/none contents. void selectAll(qtractorScrollView *pScrollView, bool bSelect = true, bool bToggle = false); // Select range view contents. void selectRange(qtractorScrollView *pScrollView, bool bToggle = false, bool bCommit = false); // Select everything between a given view rectangle. void selectRect(qtractorScrollView *pScrollView, const QRect& rect, bool bToggle = false, bool bCommit = false); // Add/remove one single event to current selection. void selectEvent(qtractorMidiEvent *pEvent, bool bSelect = true); // Retrieve current selection. QList selectedEvents() const; // Whether there's any events beyond the insertion point (edit-tail). bool isInsertable() const; // Whether there's any selected range (edit-head/tail). bool isSelectable() const; // Insert/remove edit range. void insertEditRange(); void removeEditRange(); // Update/sync integral contents. void updateContents(); // Try to center vertically the edit-view... void centerContents(); // Reset event cursors. void reset(bool bSelectClear); // To optimize and keep track of current (re)draw // position, mostly like an sequence cursor/iterator. qtractorMidiEvent *seekEvent( qtractorMidiSequence *pSeq, unsigned long iTime); // Get event from given contents position. qtractorMidiEvent *eventAt(qtractorScrollView *pScrollView, const QPoint& pos, QRect *pRect = nullptr); // Start immediate some drag-edit mode... qtractorMidiEvent *dragEditEvent(qtractorScrollView *pScrollView, const QPoint& pos, const Qt::KeyboardModifiers& modifiers); // Track drag-move-select cursor and mode... qtractorMidiEvent *dragMoveEvent(qtractorScrollView *pScrollView, const QPoint& pos, const Qt::KeyboardModifiers& modifiers); // Start drag-move-selecting... void dragMoveStart(qtractorScrollView *pScrollView, const QPoint& pos, const Qt::KeyboardModifiers& modifiers); // Update drag-move-selection... void dragMoveUpdate(qtractorScrollView *pScrollView, const QPoint& pos, const Qt::KeyboardModifiers& modifiers); // Commit drag-move-selection... void dragMoveCommit(qtractorScrollView *pScrollView, const QPoint& pos, const Qt::KeyboardModifiers& modifiers); // Trap for help/tool-tip and leave events. bool dragMoveFilter(qtractorScrollView *pScrollView, QObject *pObject, QEvent *pEvent); // Keyboard event handler (common). bool keyPress(qtractorScrollView *pScrollView, int iKey, const Qt::KeyboardModifiers& modifiers); // Keyboard step handler. bool keyStep( qtractorScrollView *pScrollView, int iKey, const Qt::KeyboardModifiers& modifiers); // Lost focus handler. void focusOut(qtractorScrollView *pScrollView); // Show selection tooltip... void showToolTip(qtractorScrollView *pScrollView, const QRect& rect) const; // MIDI event tool tip helper. QString eventToolTip(qtractorMidiEvent *pEvent, long iTimeDelta = 0, int iNoteDelta = 0, int iValueDelta = 0) const; // Temporary sync-view/follow-playhead hold state. void setSyncViewHoldOn(bool bOn); void setSyncViewHold(bool bSyncViewHold); bool isSyncViewHold() const; // Return either 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; // Make given frame position visible in view. void ensureVisibleFrame(qtractorScrollView *pScrollView, unsigned long iFrame); // Visualize the event selection drag-move. void paintDragState(qtractorScrollView *pScrollView, QPainter *pPainter); // Reset drag/select/move state. void resetDragState(qtractorScrollView *pScrollView); // Tool indexes. enum Tool { Quantize = 0, Transpose = 1, Normalize = 2, Randomize = 3, Resize = 4, Rescale = 5, Timeshift = 6, Temporamp = 7 }; // Edit tools form page selector. void executeTool(int iToolIndex); // Command list accessor. qtractorCommandList *commands() const; // Command executioner... bool execute(qtractorCommand *pCommand); // Note name map accessor. const QString noteName(unsigned char note) const; // Program name map accessor. const QString& programName(unsigned char prog) const; // Controller name map accessor. const QString& controllerName(unsigned char controller) const; // RPN/NRPN map accessors. const QMap& rpnNames() const; const QMap& nrpnNames() const; // Control-14 map accessors. const QString& control14Name(unsigned char controller) const; // Default note name map accessor. static const QString defaultNoteName(unsigned char note, bool fDrums = false); // Default controller name accessor. static const QString& defaultControllerName(unsigned char controller); // Default RPN/NRPN map accessors. static const QMap& defaultRpnNames(); static const QMap& defaultNrpnNames(); // Default Control-14 map accessors. static const QString& defaultControl14Name(unsigned char controller); // Default scale key/type names accessors. static const QStringList& scaleKeyNames(); static const QStringList& scaleTypeNames(); // Scale quantizer method. static unsigned char snapToScale( unsigned char note, int iKey, int iScale); // Redirect selection notification. void selectionChangeNotify(); public slots: // Redirect note on/off; void sendNote(int iNote, int iVelocity, bool bForce = false); // Update instrument defined names for current clip/track. void updateInstrumentNames(); // Zoom view slots. void zoomIn(); void zoomOut(); void zoomReset(); protected: // Update instrument default note names (nb. drum key names). void updateDefaultDrumNoteNames(); void updateDefaultControllerNames(); void updateDefaultRpnNames(); void updateDefaultNrpnNames(); // 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, item; unsigned long frame; }; // Zoom centering prepare and post methods. void zoomCenterPre(ZoomCenter& zc) const; void zoomCenterPost(const ZoomCenter& zc); // Ensure point visibility depending on view. void ensureVisible(qtractorScrollView *pScrollView, const QPoint& pos); // Selection flags enum { SelectNone = 0, SelectClear = 1, SelectToggle = 2, SelectCommit = 4 }; // Clear all selection. void clearSelect(); // Update all selection rectangular areas. void updateSelect(bool bSelectReset); // Compute current drag time/duration snap (in ticks). long timeSnap(long iTime) const; long durationSnap(long iTime, long iDuration) const; // Compute current drag time delta (in ticks). long timeDelta(qtractorScrollView *pScrollView) const; // Compute current drag note delta. int noteDelta(qtractorScrollView *pScrollView) const; // Compute current drag value delta. int valueDelta(qtractorScrollView *pScrollView) const; // Safe/capped value helpers. int safeNote(int iNote) const; int safeValue(int iValue) const; int safeValue14(int iValue14) const; int safePitchBend(int iPitchBend) const; // (Un)set edit mode cursors. void setEditCursor(const QCursor& cursr); void unsetEditCursor(); // Apply the event drag-resize (also editing). void resizeEvent(qtractorMidiEvent *pEvent, long iTimeDelta, int iValueDelta, qtractorMidiEditCommand *pEditCommand = nullptr); // Update event selection rectangle. void updateEvent(qtractorMidiEvent *pEvent); // Update event visual rectangles. void updateEventRects(qtractorMidiEvent *pEvent, QRect& rectEvent, QRect& rectView) const; // Update the event selection list. void updateDragSelect(qtractorScrollView *pScrollView, const QRect& rectSelect, int flags); // Drag-move current selection. void updateDragMove(qtractorScrollView *pScrollView, const QPoint& pos); // Finalize the event drag-move. void executeDragMove(qtractorScrollView *pScrollView, const QPoint& pos); // Drag-resize current selection (also editing). void updateDragResize(qtractorScrollView *pScrollView, const QPoint& pos); // Finalize the event drag-resize (also editing). void executeDragResize(qtractorScrollView *pScrollView, const QPoint& pos); // Drag-rescale current selection. void updateDragRescale(qtractorScrollView *pScrollView, const QPoint& pos); // Finalize the event drag-rescale. void executeDragRescale(qtractorScrollView *pScrollView, const QPoint& pos); // Finalize the event drag-paste. void executeDragPaste(qtractorScrollView *pScrollView, const QPoint& pos); // Drag(draw) event value-resize check. bool isDragEventResize(Qt::KeyboardModifiers modifiers) const; // Drag(draw) event value-resize to current selection... void updateDragEventResize(const QPoint& pos); // Apply drag(draw) event value-resize to current selection. void executeDragEventResize(const QPoint& pos); // Vertical line position drawing. void drawPositionX(int& iPositionX, int x, bool bSyncView); // Specialized drag/time-scale (draft)... struct DragTimeScale; // Initialize default names hash maps. static void initDefaultNoteNames(); static void initDefaultControllerNames(); static void initDefaultRpnNames(); static void initDefaultNrpnNames(); static void initDefaultControl14Names(); // Scale key/note resolver. static int scaleTabNote(int iScale, int n); protected slots: // Horizontal zoom view slots. void horizontalZoomInSlot(); void horizontalZoomOutSlot(); void horizontalZoomResetSlot(); // Vertical zoom view slots. void verticalZoomInSlot(); void verticalZoomOutSlot(); void verticalZoomResetSlot(); // Splitters moved slots. void horizontalSplitterSlot(); void verticalSplitterSlot(); // Command execution notification slot. void updateNotifySlot(unsigned int flags); signals: // Emitted on selection/changes. void selectNotifySignal(qtractorMidiEditor *); void changeNotifySignal(qtractorMidiEditor *); // Send note event signale. void sendNoteSignal(int, int, bool); private: // The editing sequence. qtractorMidiClip *m_pMidiClip; // Event fore/background colors. QColor m_foreground; QColor m_background; // The main widget splitters. QSplitter *m_pHSplitter; QSplitter *m_pVSplitter; // The main child widgets. QFrame *m_pEditListHeader; qtractorMidiEditList *m_pEditList; qtractorMidiEditTime *m_pEditTime; qtractorMidiEditView *m_pEditView; qtractorMidiEditEventScale *m_pEditEventScale; qtractorMidiEditEvent *m_pEditEvent; QFrame *m_pEditEventFrame; qtractorMidiThumbView *m_pThumbView; // The local time scale. qtractorTimeScale *m_pTimeScale; // The local time-scale offset/length. unsigned long m_iOffset; unsigned long m_iLength; // Event cursors (main time-line). qtractorMidiCursor m_cursor; qtractorMidiCursor m_cursorAt; // The current selection list. qtractorMidiEditSelect m_select; // Common drag state. enum DragState { DragNone = 0, DragStart, DragSelect, DragMove, DragRescale, DragResize, DragEventResize, DragPaste, DragStep } m_dragState, m_dragCursor; // Common drag-resize mode. enum ResizeMode { ResizeNone = 0, ResizeNoteRight, ResizeNoteLeft, ResizeValue, ResizeValue14, ResizePitchBend, ResizePgmChange } m_resizeMode; // The current selecting/dragging stuff. qtractorMidiEvent *m_pEventDrag; QPoint m_posDrag; QRect m_rectDrag; // Differential drag-move position. QPoint m_posDelta; // Step (keyboard) drag-move position QPoint m_posStep; QPoint m_posStepDelta; // Which widget holds focus while drag-step/paste? qtractorScrollView *m_pDragStep; // Drag(draw) event-value position. QPoint m_posDragEventResize; // Viewport rubber-banding stuff. qtractorRubberBand *m_pRubberBand; // Zoom mode flag. int m_iZoomMode; // Drum mode (UI). bool m_bDrumMode; // Edit mode flags. bool m_bEditMode; bool m_bEditModeDraw; bool m_bEventDragEdit; // Snap-to-beat/bar grid/zebra mode. bool m_bSnapZebra; bool m_bSnapGrid; // Floating tool-tips mode. bool m_bToolTips; // Last useful editing values. struct { unsigned char note; unsigned short value; unsigned long duration; unsigned short pitchBend; } m_last; // Local step-input-hea positioning. int m_iStepInputHeadX; // Local edit-head/tail positioning. int m_iEditHeadX; int m_iEditTailX; // Local playhead positioning. int m_iPlayHeadX; bool m_bSyncView; // Note autition while editing. bool m_bSendNotes; // Note names display (inside rectangles). bool m_bNoteNames; // Event value stick vs. duration rectangle. bool m_bNoteDuration; // Event (note, velocity) coloring. bool m_bNoteColor; bool m_bValueColor; // The local clipboard stuff (singleton). static struct ClipBoard { // Constructor. ClipBoard() {} // Destructor. ~ClipBoard() { clear(); } // Clipboard clear. void clear() { qDeleteAll(items); items.clear(); } // Clipboard members. QList items; // Singleton declaration. } g_clipboard; // Instrument defined names for current clip/track. QHash m_noteNames; QHash m_programNames; QHash m_controllerNames; QMap m_rpnNames; QMap m_nrpnNames; // Snap-to-scale (aka.in-place scale-quantize) stuff. int m_iSnapToScaleKey; int m_iSnapToScaleType; // Temporary sync-view/follow-playhead hold state. bool m_bSyncViewHold; int m_iSyncViewHold; // Ghost track setting. qtractorTrack *m_pGhostTrack; // Minimum event width. int m_iMinEventWidth; }; #endif // __qtractorMidiEditor_h // end of qtractorMidiEditor.h qtractor-1.5.9/src/PaxHeaders/man10000644000000000000000000000013215101070305014001 xustar0030 mtime=1761898693.061597574 30 atime=1761898693.061342007 30 ctime=1761898693.061597574 qtractor-1.5.9/src/man1/0000755000175000001440000000000015101070305014046 5ustar00rncbcusersqtractor-1.5.9/src/man1/PaxHeaders/qtractor.10000644000000000000000000000013215101070305015777 xustar0030 mtime=1761898693.061342007 30 atime=1761898693.061342007 30 ctime=1761898693.061342007 qtractor-1.5.9/src/man1/qtractor.10000644000175000001440000000211515101070305015766 0ustar00rncbcusers.TH QTRACTOR 1 "June 17, 2014" .SH NAME Qtractor \- An Audio/MIDI multi\-track sequencer .SH SYNOPSIS .B qtractor [\fIoptions\fR] [\fIsession-file\fR] .SH DESCRIPTION This manual page documents briefly the .B qtractor command. .PP \fBQtractor\fP is an audio/MIDI multi-track sequencer application written in C++ with the Qt framework. Target platform is 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. .SH OPTIONS .HP \fB\-s, \fB\-\-session-id\fR=[\fIuuid\fR] .IP Set session identification (uuid) .HP \fB\-?, \fB\-\-help\fR .IP Show help about command line options .HP \fB\-v, \fB\-\-version\fR .IP Show version information .SH FILES Configuration settings are stored in ~/.config/rncbc.org/Qtractor.conf .SH AUTHOR Qtractor was written by Rui Nuno Capela. .PP This manual page was written by Gürkan Sengün , for the Debian project (but may be used by others). qtractor-1.5.9/src/man1/PaxHeaders/qtractor.fr.10000644000000000000000000000013215101070305016405 xustar0030 mtime=1761898693.061597574 30 atime=1761898693.061342007 30 ctime=1761898693.061597574 qtractor-1.5.9/src/man1/qtractor.fr.10000644000175000001440000000273215101070305016401 0ustar00rncbcusers.TH QTRACTOR 1 "Juin 17, 2014" .SH NOM qtractor \- un séquenceur audio/MIDI multi\-piste .SH SYNOPSIS .B qtractor [\fIoptions\fR] [\fIfichier-sessions\fR] .SH DESCRIPTION Cette page de manuel décrit rapidement la commande .B qtractor . .PP \fBqtractor\fP est une application de séquenceur multi-piste audio/MIDI écrite en C++ avec le système Qt. La plateforme cible est Linux, pour laquelle le kit de connexion audio Jack (JACK) pour l'audio et l'architecture audio linux avancée (ALSA) pour le MIDI sont les principales infrastructures permettant l'évolution de l'interface graphique de station de travail audio-numérique audio sur un bureau Linux plutôt bien fournie en possibilités, spécialement dédiée au home-studio personnel. .SH OPTIONS .HP \fB\-s, \fB\-\-session-id\fR=[\fIuuid\fR] .IP Paramètre l'identification de session .HP \fB\-?, \fB\-\-help\fR .IP Affiche de l'aide à propos des options de ligne de commande .HP \fB\-v, \fB\-\-version\fR .IP Affiche des informations de version .SH FICHIERS Les paramètres de configuration sont stockés dans ~/.config/rncbc.org/Qtractor.conf .SH AUTEUR Qtractor a été écrit par Rui Nuno Capela. .PP Cette page de manuel a été écrite par Gürkan Sengün , pour le projet Debian (mais peut être utilisée par d'autres). .PP Cette page en français a était traduite en français à partir de la version anglaise par Olivier Humbert pour le projet LibraZiK (et peut également être utilisée par d'autres). qtractor-1.5.9/src/PaxHeaders/qtractorDocument.cpp0000644000000000000000000000013215101070305017264 xustar0030 mtime=1761898693.069267597 30 atime=1761898693.069267597 30 ctime=1761898693.069267597 qtractor-1.5.9/src/qtractorDocument.cpp0000644000175000001440000003440015101070305017255 0ustar00rncbcusers// qtractorDocument.cpp // /**************************************************************************** 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. *****************************************************************************/ #include "qtractorAbout.h" #include "qtractorDocument.h" #ifdef CONFIG_LIBZ #include "qtractorZipFile.h" #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #endif #endif #include #include #include #include #include // Deprecated QTextStreamFunctions/Qt namespaces workaround. #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) #define endl Qt::endl #endif // Local prototypes. static void remove_dir_list(const QList& list); static void remove_dir(const QString& sDir); // Remove specific file path. static void remove_dir ( const QString& sDir ) { const QDir dir(sDir); remove_dir_list( dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)); QDir cwd = QDir::current(); if (cwd.absolutePath() == dir.absolutePath()) { cwd.cdUp(); QDir::setCurrent(cwd.path()); } dir.rmdir(sDir); } static void remove_dir_list ( const QList& list ) { QListIterator iter(list); while (iter.hasNext()) { const QFileInfo& info = iter.next(); const QString& sPath = info.absoluteFilePath(); if (info.isDir()) { remove_dir(sPath); } else { QFile::remove(sPath); } } } //------------------------------------------------------------------------- // qtractorDocument -- Session file import/export helper class. // // Default document type suffixes (file name extensions). QString qtractorDocument::g_sDefaultExt = "qts"; QString qtractorDocument::g_sTemplateExt = "qtt"; QString qtractorDocument::g_sArchiveExt = "qtz"; // Extracted archive paths (static). QStringList qtractorDocument::g_extractedArchives; // Extra-ordinary archive files (static). qtractorDocument *qtractorDocument::g_pDocument = nullptr; // Constructor. qtractorDocument::qtractorDocument ( QDomDocument *pDocument, const QString& sTagName, Flags flags ) : m_pDocument(pDocument), m_sTagName(sTagName), m_flags(flags), m_pZipFile(nullptr) { } // Default destructor. qtractorDocument::~qtractorDocument (void) { #ifdef CONFIG_LIBZ if (m_pZipFile) delete m_pZipFile; #endif QStringListIterator iter(m_tempFiles); while (iter.hasNext()) { const QFileInfo temp(iter.next()); QFile::remove(temp.absoluteFilePath()); QDir::temp().rmpath(temp.absolutePath()); } } //------------------------------------------------------------------------- // qtractorDocument -- accessors. // QDomDocument *qtractorDocument::document (void) const { return m_pDocument; } // Document root tag-name. const QString& qtractorDocument::tagName (void) const { return m_sTagName; } // Regular text element factory method. void qtractorDocument::saveTextElement ( const QString& sTagName, const QString& sText, QDomElement *pElem ) { QDomElement eTag = m_pDocument->createElement(sTagName); eTag.appendChild(m_pDocument->createTextNode(sText)); pElem->appendChild(eTag); } // Document flags property. void qtractorDocument::setFlags ( Flags flags ) { m_flags = flags; } qtractorDocument::Flags qtractorDocument::flags (void) const { return m_flags; } bool qtractorDocument::isTemplate (void) const { return (m_flags & Template); } bool qtractorDocument::isArchive (void) const { return (m_flags & Archive); } bool qtractorDocument::isTemporary (void) const { return (m_flags & Temporary); } bool qtractorDocument::isSymLink (void) const { return (m_flags & SymLink); } //------------------------------------------------------------------------- // qtractorDocument -- loaders. // // External storage simple load method. bool qtractorDocument::load ( const QString& sFilename, Flags flags ) { // Hold template mode. setFlags(flags); #ifdef CONFIG_LIBZ // Was it an archive previously? if (m_pZipFile) { delete m_pZipFile; m_pZipFile = nullptr; } #endif // Is it an archive about to stuff? const QFileInfo info(sFilename); m_sName = info.completeBaseName(); QString sDocname = info.filePath(); const QIODevice::OpenMode mode = QIODevice::ReadOnly; #ifdef CONFIG_LIBZ if (isArchive()) { // ATTN: Always move to session file's directory first... if (!info.isWritable() || isTemporary()) { // Read-only/temporary media? const QString& sPath = QDir::temp().path() + QDir::separator() + QTRACTOR_TITLE; QDir dir(sPath); if (!dir.exists()) dir.mkpath(sPath); QDir::setCurrent(sPath); } else QDir::setCurrent(info.path()); m_pZipFile = new qtractorZipFile(sDocname, mode); if (!m_pZipFile->isReadable()) { delete m_pZipFile; m_pZipFile = nullptr; return false; } m_pZipFile->setPrefix(m_sName); m_pZipFile->extractAll(); m_pZipFile->close(); delete m_pZipFile; m_pZipFile = nullptr; // ATTN: Archived sub-directory must exist! if (!QDir(m_sName).exists()) { const QStringList& dirs = QDir().entryList( QStringList() << info.baseName() + '*', QDir::Dirs | QDir::NoDotAndDotDot, QDir::Time | QDir::Reversed); if (!dirs.isEmpty()) m_sName = dirs.first(); } sDocname = m_sName + '.' + g_sDefaultExt; if (QDir::setCurrent(m_sName)) g_extractedArchives.append(QDir::currentPath()); } else #endif QDir::setCurrent(info.absolutePath()); // Open file... QFile file(sDocname); if (!file.open(mode)) return false; // Parse it a-la-DOM :-) if (!m_pDocument->setContent(&file)) { file.close(); return false; } file.close(); // Get root element and check for proper taqg name. QDomElement elem = m_pDocument->documentElement(); if (elem.tagName() != m_sTagName) return false; return loadElement(&elem); } //------------------------------------------------------------------------- // qtractorDocument -- savers. // // External storage simple save method. bool qtractorDocument::save ( const QString& sFilename, Flags flags ) { // Hold template mode. setFlags(flags); // We must have a valid tag name... if (m_sTagName.isEmpty()) return false; #ifdef CONFIG_LIBZ // Was it an archive previously? if (m_pZipFile) { delete m_pZipFile; m_pZipFile = nullptr; } #endif // Is it an archive about to stuff? const QFileInfo info(sFilename); m_sName = info.completeBaseName(); QString sDocname = info.filePath(); QDir cwd = QDir::current(); QDir::setCurrent(info.absolutePath()); const QIODevice::OpenMode mode = QIODevice::WriteOnly | QIODevice::Truncate; #ifdef CONFIG_LIBZ if (isArchive()) { m_pZipFile = new qtractorZipFile(sDocname, mode); if (!m_pZipFile->isWritable()) { delete m_pZipFile; m_pZipFile = nullptr; return false; } sDocname = m_sName + '.' + g_sDefaultExt; m_pZipFile->setPrefix(m_sName); } #endif // Officially saving now... g_pDocument = this; // Save spec... QDomElement elem = m_pDocument->createElement(m_sTagName); if (!saveElement(&elem)) { g_pDocument = nullptr; return false; } m_pDocument->appendChild(elem); // Not saving anymore... g_pDocument = nullptr; // Finally, we're ready to save to external file. QFile file(sDocname); #ifdef CONFIG_LIBZ const bool bRemove = !file.exists(); #endif if (!file.open(mode)) return false; QTextStream ts(&file); ts << m_pDocument->toString() << endl; file.close(); #ifdef CONFIG_LIBZ // Commit to archive. if (m_pZipFile) { // The session document itself, at last... m_pZipFile->addFile(sDocname); m_pZipFile->processAll(); m_pZipFile->close(); delete m_pZipFile; m_pZipFile = nullptr; // Kill temporary, if didn't exist... if (bRemove) file.remove(); } #endif QDir::setCurrent(cwd.absolutePath()); return true; } QString qtractorDocument::addFile ( const QString& sFilename ) { if (!isArchive() && !isSymLink()) return sFilename; const QDir& cwd = QDir::current(); QString sAlias = cwd.relativeFilePath(sFilename); QFileInfo info(sFilename); QString sPath = info.absoluteFilePath(); const QString sName = info.completeBaseName(); const QString sSuffix = info.suffix().toLower(); if (isSymLink() && info.absolutePath() != cwd.absolutePath()) { const QString& sLink = sName + '-' + QString::number(qHash(sPath), 16) + '.' + sSuffix; QFile(sPath).link(sLink); info.setFile(cwd, sLink); sPath = info.absoluteFilePath(); sAlias = sLink; } else if (info.isSymLink()) { info.setFile(info.symLinkTarget()); sPath = info.absoluteFilePath(); } #ifdef CONFIG_LIBZ if (isArchive() && m_pZipFile) { if (sSuffix == "sfz") { // SFZ archive conversion... sAlias = m_pZipFile->alias(sPath, sName, true); const QChar sep = QDir::separator(); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) QTemporaryDir temp_dir(QDir::tempPath() + sep + m_sName + '.'); temp_dir.setAutoRemove(false); const QFileInfo temp(temp_dir.path() + sep + sAlias); #else const QString& sTempDir = QDir::tempPath() + sep + m_sName + ".%1"; unsigned int i = qHash(this) >> 7; QDir temp_dir(sTempDir.arg(i, 0, 16)); while (temp_dir.exists()) temp_dir.setPath(sTempDir.arg(++i, 0, 16)); const QFileInfo temp(temp_dir, sAlias); #endif const QString& sTempname = temp.absoluteFilePath(); #ifdef CONFIG_DEBUG qDebug("qtractorDocument::addFile(\"%s\") SFZ: sTempname=\"%s\"...", sPath.toUtf8().constData(), sTempname.toUtf8().constData()); #endif // Check if temporary file already exists... if (!m_tempFiles.contains(sTempname) && !temp.exists()) { // Create the new temporary path... QDir::temp().mkpath(temp.absolutePath()); // Prepare and open both file streams... QFile ifile(sFilename); QFile ofile(sTempname); if (ifile.open(QIODevice::ReadOnly) && ofile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { // Ready, set, go... const QFileInfo alias(sAlias); const QRegularExpression rxComment("//.*$"); const QRegularExpression rxDefaultPath("default_path[\\s]*=(.+)$"); const QRegularExpression rxSample("sample[\\s]*=(.+\\.[\\w]+)"); // Care for default path... QDir dir(QFileInfo(ifile).absoluteDir()); // Prepare the text streams... QTextStream its(&ifile); QTextStream ots(&ofile); while (!its.atEnd()) { // Read the line. QString sLine = its.readLine(); QString sTemp = sLine.simplified(); // Remove any comments... sTemp.remove(rxComment); // While not empty... while (!sTemp.isEmpty()) { QRegularExpressionMatch match = rxDefaultPath.match(sTemp); if (match.hasMatch()) { const QFileInfo fi(dir, match.captured(1)); const QString& sDefaultPath = fi.absoluteFilePath(); dir.setPath(sDefaultPath); sTemp.remove(rxDefaultPath); sLine.remove(rxDefaultPath); sTemp = sTemp.simplified(); } else { match = rxSample.match(sTemp); if (match.hasMatch()) { const QFileInfo fi(dir, match.captured(1)); const QString& sSamplePath = fi.absoluteFilePath(); const QString& sSampleAlias = m_pZipFile->alias(sSamplePath, alias.path()); m_pZipFile->addFile(sSamplePath, sSampleAlias); sTemp.remove(rxSample); sLine.replace(rxSample, "sample=" + fi.fileName()); sTemp = sTemp.simplified(); } else sTemp.clear(); } } // Write possibly altered line... ots << sLine << endl; } // Almost done. ots.flush(); ofile.close(); ifile.close(); // Done. m_pZipFile->addFile(sTempname, sAlias); } // Cache the temporary file-path for removal... m_tempFiles.append(sTempname); } } else { // Regular file archiving... sAlias = m_pZipFile->alias(sPath); m_pZipFile->addFile(sPath, sAlias); } } #endif // CONFIG_LIBZ return sAlias; } //------------------------------------------------------------------------- // qtractorDocument -- helpers. // bool qtractorDocument::boolFromText ( const QString& sText ) { return (sText == "true" || sText == "on" || sText == "yes" || sText == "1"); } QString qtractorDocument::textFromBool ( bool bBool ) { return QString::number(bBool ? 1 : 0); } //------------------------------------------------------------------------- // qtractorDocument -- filename extensions (suffix) accessors. // void qtractorDocument::setDefaultExt ( const QString& sDefaultExt ) { g_sDefaultExt = sDefaultExt; } void qtractorDocument::setTemplateExt ( const QString& sTemplateExt ) { g_sTemplateExt = sTemplateExt; } void qtractorDocument::setArchiveExt ( const QString& sArchiveExt ) { g_sArchiveExt = sArchiveExt; } const QString& qtractorDocument::defaultExt (void) { return g_sDefaultExt; } const QString& qtractorDocument::templateExt (void) { return g_sTemplateExt; } const QString& qtractorDocument::archiveExt (void) { return g_sArchiveExt; } //------------------------------------------------------------------------- // qtractorDocument -- extracted archive paths simple management. // const QStringList& qtractorDocument::extractedArchives (void) { return g_extractedArchives; } void qtractorDocument::clearExtractedArchives ( bool bRemove ) { if (bRemove) { QStringListIterator iter(g_extractedArchives); while (iter.hasNext()) remove_dir(iter.next()); } g_extractedArchives.clear(); } //------------------------------------------------------------------------- // qtractorDocument -- extra-ordinary archive files management. // QString qtractorDocument::addFile ( const QString& sDir, const QString& sFilename ) { if (g_pDocument) { const QFileInfo info(QDir(sDir), sFilename); return g_pDocument->addFile(info.absoluteFilePath()); } return sFilename; } // end of qtractorDocument.cpp qtractor-1.5.9/src/PaxHeaders/qtractorAudioMonitor.h0000644000000000000000000000013215101070305017564 xustar0030 mtime=1761898693.064267582 30 atime=1761898693.064267582 30 ctime=1761898693.064267582 qtractor-1.5.9/src/qtractorAudioMonitor.h0000644000175000001440000000646315101070305017565 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.9/src/PaxHeaders/qtractorMonitor.h0000644000000000000000000000013215101070305016602 xustar0030 mtime=1761898693.085267648 30 atime=1761898693.085267648 30 ctime=1761898693.085267648 qtractor-1.5.9/src/qtractorMonitor.h0000644000175000001440000000664615101070305016606 0ustar00rncbcusers// qtractorMonitor.h // /**************************************************************************** Copyright (C) 2006-2016, 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 __qtractorMonitor_h #define __qtractorMonitor_h #include "qtractorMidiControlObserver.h" //---------------------------------------------------------------------------- // qtractorMonitor -- Monitor bridge value processor. class qtractorMonitor { public: // Constructor. qtractorMonitor(float fGain = 1.0f, float fPanning = 0.0f) : m_gainSubject(fGain, 1.0f), m_panningSubject(fPanning, 0.0f), m_gainObserver(this), m_panningObserver(this) { m_panningSubject.setMinValue(-1.0f); } // Virtual destructor. virtual ~qtractorMonitor() {} // Gain accessors. qtractorSubject *gainSubject() { return &m_gainSubject; } qtractorMidiControlObserver *gainObserver() { return static_cast (&m_gainObserver); } void setGain(float fGain) { m_gainSubject.setValue(fGain); update(); } float gain() const { return m_gainSubject.value(); } float prevGain() const { return m_gainSubject.prevValue(); } // Stereo panning accessors. qtractorSubject *panningSubject() { return &m_panningSubject; } qtractorMidiControlObserver *panningObserver() { return static_cast (&m_panningObserver); } void setPanning(float fPanning) { m_panningSubject.setValue(fPanning); update(); } float panning() const { return m_panningSubject.value(); } float prevPanning() const { return m_panningSubject.prevValue(); } // Rebuild the whole panning-gain array... virtual void update() = 0; protected: // Observer -- Local dedicated observers. class Observer : public qtractorMidiControlObserver { public: // Constructor. Observer(qtractorMonitor *pMonitor, qtractorSubject *pSubject) : qtractorMidiControlObserver(pSubject), m_pMonitor(pMonitor) {} protected: // Update feedback. void update(bool bUpdate) { m_pMonitor->update(); qtractorMidiControlObserver::update(bUpdate); } private: // Members. qtractorMonitor *m_pMonitor; }; class GainObserver : public Observer { public: // Constructor. GainObserver(qtractorMonitor *pMonitor) : Observer(pMonitor, pMonitor->gainSubject()) {} }; class PanningObserver : public Observer { public: // Constructor. PanningObserver(qtractorMonitor *pMonitor) : Observer(pMonitor, pMonitor->panningSubject()) {} }; // Instance variables. qtractorSubject m_gainSubject; qtractorSubject m_panningSubject; GainObserver m_gainObserver; PanningObserver m_panningObserver; }; #endif // __qtractorMonitor_h // end of qtractorMonitor.h qtractor-1.5.9/src/PaxHeaders/qtractorNsmClient.h0000644000000000000000000000013215101070305017047 xustar0030 mtime=1761898693.085267648 30 atime=1761898693.085267648 30 ctime=1761898693.085267648 qtractor-1.5.9/src/qtractorNsmClient.h0000644000175000001440000000646515101070305017052 0ustar00rncbcusers// qtractorNsmClient.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 __qtractorNsmClient_h #define __qtractorNsmClient_h #include #ifdef CONFIG_LIBLO #include #endif //--------------------------------------------------------------------------- // qtractorNsmClient - NSM OSC client agent. class qtractorNsmClient : public QObject { Q_OBJECT public: // Constructor. qtractorNsmClient(const QString& nsm_url, QObject *pParent = nullptr); // Destructor. ~qtractorNsmClient(); // Session activation accessor. bool is_active() const; // Session manager accessors. const QString& manager() const; const QString& capabilities() const; // Session client accessors. const QString& path_name() const; const QString& display_name() const; const QString& client_id() const; // Session client methods. void announce(const QString& app_name, const QString& capabilities); void dirty(bool is_dirty); void visible(bool is_visible); void progress(float percent); void message(int priority, const QString& mesg); // Status/error codes enum ReplyCode { ERR_OK = 0, ERR_GENERAL = -1, ERR_INCOMPATIBLE_API = -2, ERR_BLACKLISTED = -3, ERR_LAUNCH_FAILED = -4, ERR_NO_SUCH_FILE = -5, ERR_NO_SESSION_OPEN = -6, ERR_UNSAVED_CHANGES = -7, ERR_NOT_NOW = -8, ERR_BAD_PROJECT = -9, ERR_CREATE_FAILED = -10 }; // Session client reply methods. void open_reply(ReplyCode reply_code = ERR_OK); void save_reply(ReplyCode reply_code = ERR_OK); // Server methods response methods. void nsm_announce_error( const char *mesg); void nsm_announce_reply( const char *mesg, const char *manager, const char *capabilities); void nsm_open( const char *path_name, const char *display_name, const char *client_id); void nsm_save(); void nsm_loaded(); void nsm_show(); void nsm_hide(); protected: void reply(const QString& path, ReplyCode reply_code); signals: // Session client callbacks. void active(bool is_active); void open(); void save(); void loaded(); void show(); void hide(); private: // Instance variables. #ifdef CONFIG_LIBLO lo_address m_address; lo_server_thread m_thread; lo_server m_server; #endif bool m_active; QString m_manager; QString m_capabilities; QString m_path_name; QString m_display_name; QString m_client_id; }; #endif // __qtractorNsmClient_h // end of qtractorNsmClient.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiEditCommand.cpp0000644000000000000000000000013215101070305020475 xustar0030 mtime=1761898693.079267629 30 atime=1761898693.079267629 30 ctime=1761898693.079267629 qtractor-1.5.9/src/qtractorMidiEditCommand.cpp0000644000175000001440000003601015101070305020465 0ustar00rncbcusers// qtractorMidiEditCommand.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 "qtractorMidiEditCommand.h" #include "qtractorMidiClip.h" #include "qtractorMidiEngine.h" #include "qtractorTimeScaleCommand.h" #include "qtractorSession.h" //---------------------------------------------------------------------- // class qtractorMidiEditCommand - implementation. // // Constructor. qtractorMidiEditCommand::qtractorMidiEditCommand ( qtractorMidiClip *pMidiClip, const QString& sName ) : qtractorCommand(sName), m_pMidiClip(pMidiClip), m_bAdjusted(false), m_iDuration((pMidiClip->sequence())->duration()) { } // Destructor. qtractorMidiEditCommand::~qtractorMidiEditCommand (void) { QListIterator iter(m_items); while (iter.hasNext()) { Item *pItem = iter.next(); if (pItem->autoDelete) delete pItem->event; } qDeleteAll(m_items); m_items.clear(); qDeleteAll(m_timeScaleNodeCommands); m_timeScaleNodeCommands.clear(); } // Primitive command methods. void qtractorMidiEditCommand::insertEvent ( qtractorMidiEvent *pEvent ) { m_items.append(new Item(InsertEvent, pEvent)); } void qtractorMidiEditCommand::moveEventTime ( qtractorMidiEvent *pEvent, unsigned long iTime ) { m_items.append(new Item(MoveEventTime, pEvent, 0, iTime)); } void qtractorMidiEditCommand::moveEventNote ( qtractorMidiEvent *pEvent, int iNote, unsigned long iTime ) { m_items.append(new Item(MoveEventNote, pEvent, iNote, iTime)); } void qtractorMidiEditCommand::moveEventValue ( qtractorMidiEvent *pEvent, int iValue, unsigned long iTime ) { m_items.append(new Item(MoveEventValue, pEvent, 0, iTime, 0, iValue)); } void qtractorMidiEditCommand::resizeEventTime ( qtractorMidiEvent *pEvent, unsigned long iTime, unsigned long iDuration ) { m_items.append(new Item(ResizeEventTime, pEvent, 0, iTime, iDuration)); } void qtractorMidiEditCommand::resizeEventValue ( qtractorMidiEvent *pEvent, int iValue ) { if (pEvent->type() == qtractorMidiEvent::NOTEON && iValue < 1) iValue = 1; // Avoid zero velocity (aka. NOTEOFF) m_items.append(new Item(ResizeEventValue, pEvent, 0, 0, 0, iValue)); } void qtractorMidiEditCommand::updateEvent ( qtractorMidiEvent *pEvent, int iNote, unsigned long iTime, unsigned long iDuration, int iValue ) { if (pEvent->type() == qtractorMidiEvent::NOTEON && iValue < 1) iValue = 1; // Avoid zero velocity (aka. NOTEOFF) m_items.append(new Item(UpdateEvent, pEvent, iNote, iTime, iDuration, iValue)); } void qtractorMidiEditCommand::removeEvent ( qtractorMidiEvent *pEvent ) { m_items.append(new Item(RemoveEvent, pEvent)); } // Check whether the event is already in chain. bool qtractorMidiEditCommand::findEvent ( qtractorMidiEvent *pEvent, qtractorMidiEditCommand::CommandType cmd ) const { QListIterator iter(m_items); while (iter.hasNext()) { Item *pItem = iter.next(); if (pItem->event == pEvent && (pItem->command == InsertEvent || pItem->command == cmd)) return true; } return false; } // Tell whether there are any items to edit. bool qtractorMidiEditCommand::isEmpty (void) const { return m_items.isEmpty(); } // Common executive method. bool qtractorMidiEditCommand::execute ( bool bRedo ) { if (m_pMidiClip == nullptr) return false; qtractorMidiSequence *pSeq = m_pMidiClip->sequence(); if (pSeq == nullptr) return false; // Execute all additional commands.... executeTimeScaleNodeCommands(bRedo); // Dropped enqueued events... qtractorSession *pSession = nullptr; qtractorTrack *pTrack = m_pMidiClip->track(); if (pTrack) pSession = pTrack->session(); #if 0 if (pSession && pSession->isPlaying()) pSession->midiEngine()->trackMute(pTrack, true); #endif // Track sequence duration changes... const unsigned long iOldDuration = pSeq->duration(); int iSelectClear = 0; // Changes are due... QListIterator iter(m_items); if (!bRedo) iter.toBack(); while (bRedo ? iter.hasNext() : iter.hasPrevious()) { Item *pItem = (bRedo ? iter.next() : iter.previous()); qtractorMidiEvent *pEvent = pItem->event; // Execute the command item... switch (pItem->command) { case InsertEvent: { if (bRedo) pSeq->insertEvent(pEvent); else pSeq->unlinkEvent(pEvent); pItem->autoDelete = !bRedo; ++iSelectClear; break; } case MoveEventTime: { const unsigned long iOldTime = pEvent->time(); pSeq->unlinkEvent(pEvent); pEvent->setTime(pItem->time); pSeq->insertEvent(pEvent); pItem->time = iOldTime; break; } case MoveEventNote: { const unsigned long iOldTime = pEvent->time(); const int iOldNote = int(pEvent->note()); pSeq->unlinkEvent(pEvent); pEvent->setTime(pItem->time); pEvent->setNote(pItem->note); pSeq->insertEvent(pEvent); pItem->note = iOldNote; pItem->time = iOldTime; break; } case MoveEventValue: { const unsigned long iOldTime = pEvent->time(); pSeq->unlinkEvent(pEvent); pEvent->setTime(pItem->time); if (pEvent->type() == qtractorMidiEvent::PITCHBEND) { const int iOldValue = pEvent->pitchBend(); pEvent->setPitchBend(pItem->value); pItem->value = iOldValue; } else if (pEvent->type() == qtractorMidiEvent::PGMCHANGE) { const int iOldValue = pEvent->param(); pEvent->setParam(pItem->value); pItem->value = iOldValue; } else { const int iOldValue = pEvent->value(); pEvent->setValue(pItem->value); pItem->value = iOldValue; } pSeq->insertEvent(pEvent); pItem->time = iOldTime; break; } case ResizeEventTime: { const unsigned long iOldTime = pEvent->time(); const unsigned long iOldDuration = pEvent->duration(); pSeq->unlinkEvent(pEvent); pEvent->setTime(pItem->time); if (pEvent->type() == qtractorMidiEvent::NOTEON) pEvent->setDuration(pItem->duration); pSeq->insertEvent(pEvent); pItem->time = iOldTime; pItem->duration = iOldDuration; break; } case ResizeEventValue: { if (pEvent->type() == qtractorMidiEvent::PITCHBEND) { const int iOldValue = pEvent->pitchBend(); pEvent->setPitchBend(pItem->value); pItem->value = iOldValue; } else if (pEvent->type() == qtractorMidiEvent::PGMCHANGE) { const int iOldValue = pEvent->param(); pEvent->setParam(pItem->value); pItem->value = iOldValue; } else { const int iOldValue = pEvent->value(); pEvent->setValue(pItem->value); pItem->value = iOldValue; } break; } case UpdateEvent: { const unsigned long iOldTime = pEvent->time(); if (iOldTime != pItem->time) { pSeq->unlinkEvent(pEvent); pEvent->setTime(pItem->time); pSeq->insertEvent(pEvent); pItem->time = iOldTime; } if (pEvent->type() == qtractorMidiEvent::NOTEON) { const int iOldNote = int(pEvent->note()); if (iOldNote != pItem->note) { pEvent->setNote(pItem->note); pItem->note = iOldNote; } const unsigned long iOldDuration = pEvent->duration(); if (iOldDuration != pItem->duration && pEvent->type() == qtractorMidiEvent::NOTEON) { pEvent->setDuration(pItem->duration); pItem->duration = iOldDuration; } } if (pEvent->type() == qtractorMidiEvent::PITCHBEND) { const int iOldValue = pEvent->pitchBend(); if (iOldValue != pItem->value) { pEvent->setPitchBend(pItem->value); pItem->value = iOldValue; } } else if (pEvent->type() == qtractorMidiEvent::PGMCHANGE) { const int iOldValue = pEvent->param(); if (iOldValue != pItem->value) { pEvent->setParam(pItem->value); pItem->value = iOldValue; } } else { const int iOldValue = pEvent->value(); if (iOldValue != pItem->value) { pEvent->setValue(pItem->value); pItem->value = iOldValue; } } break; } case RemoveEvent: { if (bRedo) pSeq->unlinkEvent(pEvent); else pSeq->insertEvent(pEvent); pItem->autoDelete = bRedo; ++iSelectClear; break; } default: break; } } // It's dirty, definitely... m_pMidiClip->setDirtyEx(true); // Have we changed on something less durable? if (m_iDuration != iOldDuration) { pSeq->setDuration(m_iDuration); m_iDuration = iOldDuration; } // Adjust edit-command result to prevent event overlapping. if (bRedo && !m_bAdjusted) m_bAdjusted = adjust(); // Or are we changing something more durable? if (pSeq->duration() != iOldDuration) { pSeq->setTimeLength(pSeq->duration()); if (pSession) { m_pMidiClip->setClipLengthEx( pSession->frameFromTick(pSession->tickFromFrame( m_pMidiClip->clipStart()) + pSeq->duration()) - m_pMidiClip->clipStart()); } } // Just reset/update editor internals... m_pMidiClip->updateEditorEx(iSelectClear > 0); // Re-enqueue dropped events... if (pSession && pSession->isPlaying()) { // Reset all current running event cursors, // make them play it right and sound again... m_pMidiClip->reset(pSession->isLooping()); // Re-enqueueing in proper sense... pSession->midiEngine()->trackMute(pTrack, false); } return true; } // Adjust edit-command result to prevent event overlapping. bool qtractorMidiEditCommand::adjust (void) { if (m_pMidiClip == nullptr) return false; qtractorMidiSequence *pSeq = m_pMidiClip->sequence(); if (pSeq == nullptr) return false; // HACK: What we're going to do here is about checking the // whole sequence, fixing any overlapping (note) events and // adjusting the issued command for proper undo/redo... QHash events; // For each event, do rescan... qtractorMidiEvent *pEvent = pSeq->events().first(); while (pEvent) { qtractorMidiEvent *pNextEvent = pEvent->next(); // Whether event is (time) adjustable... int key = int(pEvent->type()) << 14; switch (pEvent->type()) { case qtractorMidiEvent::NOTEON: case qtractorMidiEvent::NOTEOFF: case qtractorMidiEvent::KEYPRESS: key += int(pEvent->note()); break; case qtractorMidiEvent::CONTROLLER: case qtractorMidiEvent::REGPARAM: case qtractorMidiEvent::NONREGPARAM: case qtractorMidiEvent::CONTROL14: key += int(pEvent->controller()); break; case qtractorMidiEvent::CHANPRESS: case qtractorMidiEvent::PITCHBEND: break; default: key = 0; // Non adjustable! break; } // Adjustable? if (key) { // Already there? qtractorMidiEvent *pPrevEvent = events.value(key, nullptr); if (pPrevEvent) { const unsigned long iTime = pEvent->time(); const unsigned long iPrevTime = pPrevEvent->time(); // NOTEON: Find previous note event and check overlaps... if (pEvent->type() == qtractorMidiEvent::NOTEON) { const unsigned long iTimeEnd = iTime + pEvent->duration(); const unsigned long iPrevTimeEnd = iPrevTime + pPrevEvent->duration(); // Inner operlap... if (iTime > iPrevTime && iTime < iPrevTimeEnd) { // Left-side outer event... const unsigned long iDuration = pPrevEvent->duration(); pPrevEvent->setDuration(iTime - iPrevTime); if (!findEvent(pPrevEvent, ResizeEventTime)) resizeEventTime(pPrevEvent, iPrevTime, iDuration); // Right-side outer event... if (iTimeEnd < iPrevTimeEnd) { qtractorMidiEvent *pNewEvent = new qtractorMidiEvent(*pPrevEvent); pNewEvent->setTime(iTimeEnd); pNewEvent->setDuration(iPrevTimeEnd - iTimeEnd); insertEvent(pNewEvent); pSeq->insertEvent(pNewEvent); pNextEvent = pNewEvent->next(); // Keep or set as last note... pEvent = pNewEvent; } } else // Loose overlap?... if (iTime == iPrevTime) { // Exact overlap... if (iTimeEnd == iPrevTimeEnd) { pSeq->unlinkEvent(pPrevEvent); if (!findEvent(pPrevEvent, RemoveEvent)) removeEvent(pPrevEvent); } else { // Partial overlap... if (iTimeEnd < iPrevTimeEnd) { // Short over large... unsigned long iDuration = pPrevEvent->duration(); pPrevEvent->setDuration(pEvent->duration()); if (!findEvent(pPrevEvent, ResizeEventTime)) resizeEventTime(pPrevEvent, iPrevTime, iDuration); iDuration = pEvent->duration(); pSeq->unlinkEvent(pEvent); pEvent->setTime(iTimeEnd); pEvent->setDuration(iPrevTimeEnd - iTimeEnd); pSeq->insertEvent(pEvent); if (!findEvent(pEvent, ResizeEventTime)) { resizeEventTime(pEvent, iTime, iDuration); } } else { // Large over short... const unsigned long iDuration = pEvent->duration(); pSeq->unlinkEvent(pEvent); pEvent->setTime(iPrevTimeEnd); pEvent->setDuration(iTimeEnd - iPrevTimeEnd); pSeq->insertEvent(pEvent); if (!findEvent(pEvent, ResizeEventTime)) { resizeEventTime(pEvent, iTime, iDuration); } } // We've move it ahead... pEvent = pPrevEvent; } } } else // All other channel events... if (iTime == iPrevTime) { pSeq->unlinkEvent(pPrevEvent); if (!findEvent(pPrevEvent, RemoveEvent)) removeEvent(pPrevEvent); } } // Remember last one... events.insert(key, pEvent); } // Iterate next... pEvent = pNextEvent; } // MIDI track note-range might have been also updated... qtractorTrack *pTrack = m_pMidiClip->track(); if (pTrack) { pTrack->setMidiNoteMin(pSeq->noteMin()); pTrack->setMidiNoteMax(pSeq->noteMax()); } return true; } // Add a time-scale command to the execution list... void qtractorMidiEditCommand::addTimeScaleNodeCommand ( qtractorTimeScaleNodeCommand *pTimeScaleNodeCommand ) { m_timeScaleNodeCommands.append(pTimeScaleNodeCommand); } // Execute tempo-map/time-sig commands. void qtractorMidiEditCommand::executeTimeScaleNodeCommands ( bool bRedo ) { int iTimeScaleUpdate = 0; QListIterator iter(m_timeScaleNodeCommands); if (bRedo) iter.toFront(); else iter.toBack(); while (bRedo ? iter.hasNext() : iter.hasPrevious()) { qtractorTimeScaleNodeCommand *pTimeScaleNodeCommand = (bRedo ? iter.next() : iter.previous()); if (bRedo) pTimeScaleNodeCommand->redo(); else pTimeScaleNodeCommand->undo(); ++iTimeScaleUpdate; } if (m_pMidiClip && iTimeScaleUpdate > 0) m_pMidiClip->updateEditorTimeScale(); } // Virtual command methods. bool qtractorMidiEditCommand::redo (void) { return execute(true); } bool qtractorMidiEditCommand::undo (void) { return execute(false); } // end of qtractorMidiEditCommand.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMidiSysexForm.h0000644000000000000000000000013215101070305017715 xustar0030 mtime=1761898693.083267642 30 atime=1761898693.083267642 30 ctime=1761898693.083267642 qtractor-1.5.9/src/qtractorMidiSysexForm.h0000644000175000001440000000534015101070305017707 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.9/src/PaxHeaders/qtractorPasteRepeatForm.cpp0000644000000000000000000000013215101070305020547 xustar0030 mtime=1761898693.086267651 30 atime=1761898693.086267651 30 ctime=1761898693.086267651 qtractor-1.5.9/src/qtractorPasteRepeatForm.cpp0000644000175000001440000001364515101070305020550 0ustar00rncbcusers// qtractorPasteRepeatForm.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 "qtractorPasteRepeatForm.h" #include "qtractorAbout.h" #include "qtractorTimeScale.h" #include "qtractorSession.h" #include "qtractorOptions.h" #include #include //---------------------------------------------------------------------------- // qtractorPasteRepeatForm -- UI wrapper form. // Constructor. qtractorPasteRepeatForm::qtractorPasteRepeatForm ( QWidget *pParent ) : QDialog(pParent) { // Setup UI struct... m_ui.setupUi(this); // Window modality (let plugin/tool windows rave around). QDialog::setWindowModality(Qt::WindowModal); // Initialize dirty control state. m_pTimeScale = nullptr; m_iDirtyCount = 0; // Copy from global time-scale instance... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { m_pTimeScale = new qtractorTimeScale(*pSession->timeScale()); m_ui.RepeatPeriodSpinBox->setTimeScale(m_pTimeScale); m_ui.RepeatPeriodSpinBox->setDeltaValue(true, pSession->playHead()); } // Initialize conveniency options... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { m_ui.RepeatCountSpinBox->setValue(pOptions->iPasteRepeatCount); m_ui.RepeatPeriodCheckBox->setChecked(pOptions->bPasteRepeatPeriod); } // Choose BBT to be default format here. formatChanged(qtractorTimeScale::BBT); // Try to set minimal window positioning. adjustSize(); // UI signal/slot connections... QObject::connect(m_ui.RepeatCountSpinBox, SIGNAL(valueChanged(int)), SLOT(changed())); QObject::connect(m_ui.RepeatPeriodCheckBox, SIGNAL(toggled(bool)), SLOT(changed())); QObject::connect(m_ui.RepeatPeriodSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(changed())); QObject::connect(m_ui.RepeatPeriodSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.RepeatFormatComboBox, SIGNAL(activated(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.DialogButtonBox, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(rejected()), SLOT(reject())); } // Destructor. qtractorPasteRepeatForm::~qtractorPasteRepeatForm (void) { // Don't forget to get rid of local time-scale instance... if (m_pTimeScale) delete m_pTimeScale; } // Access accepted the accepted values. void qtractorPasteRepeatForm::setRepeatCount ( unsigned short iRepeatCount ) { m_ui.RepeatCountSpinBox->setValue(iRepeatCount); } unsigned short qtractorPasteRepeatForm::repeatCount (void) const { return m_ui.RepeatCountSpinBox->value(); } void qtractorPasteRepeatForm::setRepeatPeriod ( unsigned long iRepeatPeriod ) { m_ui.RepeatPeriodSpinBox->setValue(iRepeatPeriod, false); } unsigned long qtractorPasteRepeatForm::repeatPeriod (void) const { return (m_ui.RepeatPeriodCheckBox->isChecked() ? m_ui.RepeatPeriodSpinBox->value() : 0); } // Accept settings (OK button slot). void qtractorPasteRepeatForm::accept (void) { // Save settings... if (m_iDirtyCount > 0) { // Save conveniency options... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { pOptions->iPasteRepeatCount = repeatCount(); pOptions->bPasteRepeatPeriod = (repeatPeriod() > 0); } // Reset dirty flag. m_iDirtyCount = 0; } // Just go with dialog acceptance. QDialog::accept(); } // Reject settings (Cancel button slot). void qtractorPasteRepeatForm::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 qtractorPasteRepeatForm::changed (void) { ++m_iDirtyCount; stabilizeForm(); } // Display format has changed. void qtractorPasteRepeatForm::formatChanged ( int iDisplayFormat ) { const bool bBlockSignals = m_ui.RepeatFormatComboBox->blockSignals(true); m_ui.RepeatFormatComboBox->setCurrentIndex(iDisplayFormat); const qtractorTimeScale::DisplayFormat displayFormat = qtractorTimeScale::DisplayFormat(iDisplayFormat); m_ui.RepeatPeriodSpinBox->setDisplayFormat(displayFormat); if (m_pTimeScale) m_pTimeScale->setDisplayFormat(displayFormat); m_ui.RepeatFormatComboBox->blockSignals(bBlockSignals); stabilizeForm(); } // Stabilize current form state. void qtractorPasteRepeatForm::stabilizeForm (void) { bool bEnabled = m_ui.RepeatPeriodCheckBox->isChecked(); m_ui.RepeatPeriodSpinBox->setEnabled(bEnabled); m_ui.RepeatFormatComboBox->setEnabled(bEnabled); m_ui.DialogButtonBox->button( QDialogButtonBox::Ok)->setEnabled(m_pTimeScale != nullptr); } // end of qtractorPasteRepeatForm.cpp qtractor-1.5.9/src/PaxHeaders/qtractorSessionCursor.cpp0000644000000000000000000000013215101070305020327 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.088267658 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorSessionCursor.cpp0000644000175000001440000001764715101070305020336 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.9/src/PaxHeaders/qtractorMidiControlObserver.cpp0000644000000000000000000000013215101070305021441 xustar0030 mtime=1761898693.078267626 30 atime=1761898693.078267626 30 ctime=1761898693.078267626 qtractor-1.5.9/src/qtractorMidiControlObserver.cpp0000644000175000001440000001463015101070305021435 0ustar00rncbcusers// qtractorMidiControlObserver.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 "qtractorMidiControlObserver.h" #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; } //---------------------------------------------------------------------- // class qtractorMidiControlObserver -- MIDI controller observers. // // Constructor. qtractorMidiControlObserver::qtractorMidiControlObserver ( qtractorSubject *pSubject ) : qtractorObserver(pSubject), m_ctype(qtractorMidiEvent::CONTROLLER), m_iChannel(0), m_iParam(0), m_bLogarithmic(false), m_bFeedback(false), m_bInvert(false), m_bHook(true), m_bLatch(true), m_bTriggered(false), m_fMidiValue(0.0f), m_bMidiSync(false), m_pCurveList(nullptr) { } // Destructor (virtual). qtractorMidiControlObserver::~qtractorMidiControlObserver (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl) { pMidiControl->unmapMidiObserverWidgets(this); if (pMidiControl->isMidiObserverMapped(this)) pMidiControl->unmapMidiObserver(this); } } // MIDI scale type (7bit vs. 14bit). unsigned short qtractorMidiControlObserver::midiScale (void) const { if (m_ctype == qtractorMidiEvent::PITCHBEND || m_ctype == qtractorMidiEvent::CONTROL14 || m_ctype == qtractorMidiEvent::REGPARAM || m_ctype == qtractorMidiEvent::NONREGPARAM) return 0x3fff; else return 0x7f; } // MIDI mapped value converters. void qtractorMidiControlObserver::setMidiValue ( unsigned short iValue ) { const unsigned short iScale = midiScale(); // setScaleValue(float(iValue) / float(iScale)); if (iValue > iScale) iValue = iScale; const float fScale = float(m_bInvert ? iScale - iValue : iValue) / float(iScale); float fValue = valueFromScale(fScale, m_bLogarithmic); const bool bToggled = qtractorObserver::isToggled(); if (bToggled || m_bTriggered) { const float vmax = qtractorObserver::maxValue(); const float vmin = qtractorObserver::minValue(); const float vmid = 0.5f * (vmax + vmin); if (bToggled && m_bLatch) // latched / non-momentary... fValue = (fValue > vmid ? vmax : vmin); else // momentary / non-latched... if (fValue > vmid) fValue = (m_fMidiValue > vmid ? vmin : vmax); else if (fValue > vmin) fValue = (m_fMidiValue < vmid ? vmax : vmin); else fValue = m_fMidiValue; } bool bSync = (m_bHook || !qtractorObserver::isDecimal()); if (!bSync) bSync = m_bMidiSync; if (!bSync) { const float v0 = m_fMidiValue; const float v1 = qtractorObserver::value(); #if 0 if ((fValue > v0 && v1 >= v0 && fValue >= v1) || (fValue < v0 && v0 >= v1 && v1 >= fValue)) bSync = true; #else const float d1 = qAbs(v1 - fValue); const float d2 = qAbs(v1 - v0) * d1; bSync = (d2 < 0.001f); #endif if (bSync) m_bMidiSync = true; } if (bSync) { m_fMidiValue = fValue; qtractorObserver::subject()->setValue(fValue); } } unsigned short qtractorMidiControlObserver::midiValue (void) const { const unsigned short iScale = midiScale(); unsigned short iValue = float(iScale) * scaleValue(); if (iValue > iScale) iValue = iScale; return (m_bInvert ? iScale - iValue : iValue); } // Normalized scale converters. float qtractorMidiControlObserver::valueFromScale ( float fScale, bool bLogarithmic ) const { if (bLogarithmic) { const float fMaxValue = qtractorObserver::maxValue(); const float fMinValue = qtractorObserver::minValue(); if (fMinValue < 0.0f && fMaxValue > 0.0f) { const float fMidScale = fMinValue / (fMinValue - fMaxValue); if (fScale > fMidScale) { const float fMaxScale = (1.0f - fMidScale); fScale = (fScale - fMidScale) / fMaxScale; fScale = fMidScale + fMaxScale * ::cubef2(fScale); } else { fScale = (fMidScale - fScale) / fMidScale; fScale = fMidScale - fMidScale * ::cubef2(fScale); } } else fScale = ::cubef2(fScale); } return qtractorObserver::valueFromScale(fScale); } float qtractorMidiControlObserver::scaleFromValue ( float fValue, bool bLogarithmic ) const { float fScale = qtractorObserver::scaleFromValue(fValue); if (bLogarithmic) { const float fMaxValue = qtractorObserver::maxValue(); const float fMinValue = qtractorObserver::minValue(); if (fMinValue < 0.0f && fMaxValue > 0.0f) { const float fMidScale = fMinValue / (fMinValue - fMaxValue); if (fScale > fMidScale) { const float fMaxScale = (1.0f - fMidScale); fScale = (fScale - fMidScale) / fMaxScale; fScale = fMidScale + fMaxScale * ::cbrtf2(fScale); } else { fScale = (fMidScale - fScale) / fMidScale; fScale = fMidScale - fMidScale * ::cbrtf2(fScale); } } else fScale = ::cbrtf2(fScale); } return fScale; } // Updater (feedback). void qtractorMidiControlObserver::update ( bool bUpdate ) { if (bUpdate && m_bFeedback) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl && pMidiControl->isMidiObserverMapped(this)) { pMidiControl->sendController( m_ctype, m_iChannel, m_iParam, midiValue()); } } // m_bMidiSync = false; } // end of qtractorMidiControlObserver.cpp qtractor-1.5.9/src/PaxHeaders/qtractorPluginFactory.cpp0000644000000000000000000000013215101070305020274 xustar0030 mtime=1761898693.086267651 30 atime=1761898693.086267651 30 ctime=1761898693.086267651 qtractor-1.5.9/src/qtractorPluginFactory.cpp0000644000175000001440000010133215101070305020264 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.9/src/PaxHeaders/qtractor.h0000644000000000000000000000013215101070305015232 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.062554172 30 ctime=1761898693.062554172 qtractor-1.5.9/src/qtractor.h0000644000175000001440000000566115101070305015232 0ustar00rncbcusers// qtractor.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 __qtractor_h #define __qtractor_h #include "qtractorAbout.h" #include #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #if defined(Q_WS_X11) #define CONFIG_X11 #endif #endif // Forward decls. class QWidget; class QTranslator; #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_XUNIQUE #ifdef CONFIG_X11 #include typedef unsigned long Window; typedef unsigned long Atom; #endif // CONFIG_X11 #endif // CONFIG_XUNIQUE #else #ifdef CONFIG_XUNIQUE class QSharedMemory; class QLocalServer; #endif // CONFIG_XUNIQUE #endif //------------------------------------------------------------------------- // Singleton application instance stuff (Qt/X11 only atm.) // class qtractorApplication : public QApplication { Q_OBJECT public: // Constructor. qtractorApplication(int& argc, char **argv); // Destructor. ~qtractorApplication(); // Main application widget accessors. void setMainWidget(QWidget *pWidget); QWidget *mainWidget() const { return m_pWidget; } // Check if another instance is running, // and raise its proper main widget... bool setup(); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_X11 #ifdef CONFIG_XUNIQUE void x11PropertyNotify(Window w); #endif // CONFIG_XUNIQUE bool x11EventFilter(XEvent *pEv); #endif // CONFIG_X11 #else #ifdef CONFIG_XUNIQUE protected slots: // Local server slots. void newConnectionSlot(); void readyReadSlot(); protected: // Local server/shmem setup/cleanup. bool setupServer(); void clearServer(); #endif // CONFIG_XUNIQUE #endif private: // Translation support. QTranslator *m_pQtTranslator; QTranslator *m_pMyTranslator; // Instance variables. QWidget *m_pWidget; #ifdef CONFIG_XUNIQUE #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #ifdef CONFIG_X11 Display *m_pDisplay; Atom m_aUnique; Window m_wOwner; #endif // CONFIG_X11 #else QString m_sUnique; QSharedMemory *m_pMemory; QLocalServer *m_pServer; #endif #endif // CONFIG_XUNIQUE }; #endif // __qtractor_h // end of qtractor.h qtractor-1.5.9/src/PaxHeaders/qtractorEngine.h0000644000000000000000000000013215101070305016360 xustar0030 mtime=1761898693.070267601 30 atime=1761898693.070267601 30 ctime=1761898693.070267601 qtractor-1.5.9/src/qtractorEngine.h0000644000175000001440000002360615101070305016357 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.9/src/PaxHeaders/qtractorMidiEditView.h0000644000000000000000000000013215101070305017476 xustar0030 mtime=1761898693.080267632 30 atime=1761898693.080267632 30 ctime=1761898693.080267632 qtractor-1.5.9/src/qtractorMidiEditView.h0000644000175000001440000001010415101070305017462 0ustar00rncbcusers// qtractorMidiEditView.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 __qtractorMidiEditView_h #define __qtractorMidiEditView_h #include "qtractorScrollView.h" #include "qtractorMidiEvent.h" #include #include // Forward declarations. class qtractorMidiEditor; class qtractorMidiSequence; class QResizeEvent; class QMouseEvent; class QKeyEvent; class QToolButton; //---------------------------------------------------------------------------- // qtractorMidiEditView -- MIDI sequence main view widget. class qtractorMidiEditView : public qtractorScrollView { Q_OBJECT public: // Constructor. qtractorMidiEditView(qtractorMidiEditor *pEditor, QWidget *pParent); // Destructor. ~qtractorMidiEditView(); // Update sequence view content height. void updateContentsHeight(); // Update sequence view content width. void updateContentsWidth(int iContentsWidth = 0); // Contents update overloaded methods. void updateContents(const QRect& rect); void updateContents(); // Current event selection accessors. void setEventType(qtractorMidiEvent::EventType eventType); qtractorMidiEvent::EventType eventType() const; // Single note-on/off handlers. void dragNoteOn(int iNote, int iVelocity = 1); void dragNoteOff(); protected: // Virtual size hint. QSize sizeHint() const { return QSize(480, 240); } // Scrollbar/tools layout management. void setVBarGeometry(QScrollBar& vbar, int x, int y, int w, int h); // Resize event handler. void resizeEvent(QResizeEvent *pResizeEvent); // Draw the track view events. void drawEvents(QPainter& painter, int dx, int dy, qtractorMidiSequence *pSeq, unsigned long t0, unsigned long iTickStart, unsigned long iTickEnd, unsigned long iTickEnd2, bool bDrumMode, const QColor& fore, const QColor& back, int alpha = 255); // Draw the track view void drawContents(QPainter *pPainter, const QRect& rect); // Focus lost event. void focusOutEvent(QFocusEvent *pFocusEvent); // Keyboard event handler (made public explicitly). void keyPressEvent(QKeyEvent *pKeyEvent); // Handle item selection with mouse. void mousePressEvent(QMouseEvent *pMouseEvent); void mouseMoveEvent(QMouseEvent *pMouseEvent); void mouseReleaseEvent(QMouseEvent *pMouseEvent); // Handle zoom with mouse wheel. void wheelEvent(QWheelEvent *pWheelEvent); // Trap for help/tool-tip and leave events. bool eventFilter(QObject *pObject, QEvent *pEvent); protected slots: // To have track view in sync with track list. void contentsXMovingSlot(int cx, int cy); void contentsYMovingSlot(int cx, int cy); // (Re)create the complete track view pixmap. void updatePixmap(int cx, int cy); private: // The logical parent binding. qtractorMidiEditor *m_pEditor; // Local zoom control widgets. QToolButton *m_pVzoomIn; QToolButton *m_pVzoomOut; QToolButton *m_pVzoomReset; // Local double-buffering pixmap. QPixmap m_pixmap; // Current selection holder. qtractorMidiEvent::EventType m_eventType; // Optional edge-shadow gradient brushes. QBrush m_gradLeft; QBrush m_gradRight; // The current note being keyed on. int m_iNoteOn; int m_iNoteVel; QRect m_rectNote; }; #endif // __qtractorMidiEditView_h // end of qtractorMidiEditView.h qtractor-1.5.9/src/PaxHeaders/qtractorFiles.h0000644000000000000000000000013215101070305016215 xustar0030 mtime=1761898693.072267607 30 atime=1761898693.072267607 30 ctime=1761898693.072267607 qtractor-1.5.9/src/qtractorFiles.h0000644000175000001440000001020515101070305016203 0ustar00rncbcusers// qtractorFiles.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 __qtractorFiles_h #define __qtractorFiles_h #include "qtractorAudioListView.h" #include "qtractorMidiListView.h" #include // Forward declarations. class qtractorFilesTabWidget; class QHBoxLayout; class QToolButton; //------------------------------------------------------------------------- // qtractorFiles - File/Groups dockable window. // class qtractorFiles : public QDockWidget { Q_OBJECT public: // Constructor. qtractorFiles(QWidget *pParent); // Destructor. ~qtractorFiles(); // The fixed tab page indexes. enum PageIndex { Audio = 0, Midi = 1 }; // File list view accessors. qtractorAudioListView *audioListView() const; qtractorMidiListView *midiListView() const; // Clear evrything on sight. void clear(); // Check whether one of the widgets has focus (oveerride method). bool hasFocus() const; // Tell whether a file item is currently selected. bool isFileSelected() const; // File addition convenience helper methods. void addAudioFile (const QString& sFilename, bool bSelect); void addMidiFile (const QString& sFilename, bool bSelect); // File removal convenience helper methods. void removeAudioFile (const QString& sFilename, bool bSelect); void removeMidiFile (const QString& sFilename, bool bSelect); // File selection convenience helper methods. void selectAudioFile (const QString& sFilename); void selectMidiFile (const QString& sFilename, int iTrackChannel); // Audition/pre-listening player methods. void setPlayState(bool bOn); bool isPlayState() const; public slots: // Cut current file item(s) to clipboard. void cutItemSlot(); // Copy current file item(s) to clipboard. void copyItemSlot(); // Paste file item(s) from clipboard. void pasteItemSlot(); // Remove current group/file item(s). void removeItemSlot(); protected slots: // Add a new group item below the current one. void newGroupSlot(); // Add a new file item below the current group one. void openFileSlot(); // Rename current group/file item. void renameItemSlot(); // Audition/pre-listening player slots. void playSlot(bool bOn); // Clean-up unused file items. void cleanupSlot(); // Tab page switch slots. void pageAudioSlot(); void pageMidiSlot(); // Usual stabilizing slot. void stabilizeSlot(); protected: // Just about to notify main-window that we're closing. void closeEvent(QCloseEvent *); // Context menu request event handler. void contextMenuEvent(QContextMenuEvent *); // Retrieve current selected file list view. qtractorFileListView *currentFileListView() const; private: // File type selection tab widget. qtractorFilesTabWidget *m_pTabWidget; // Specific file type widgets. qtractorAudioListView *m_pAudioListView; qtractorMidiListView *m_pMidiListView; // Audition/pre-listening controls. QWidget *m_pPlayWidget; QHBoxLayout *m_pPlayLayout; QToolButton *m_pPlayButton; // List view actions. QAction *m_pNewGroupAction; QAction *m_pOpenFileAction; QAction *m_pCutItemAction; QAction *m_pCopyItemAction; QAction *m_pPasteItemAction; QAction *m_pRenameItemAction; QAction *m_pRemoveItemAction; QAction *m_pPlayItemAction; QAction *m_pCleanupAction; }; #endif // __qtractorFiles_h // end of qtractorFiles.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiFileTempo.h0000644000000000000000000000013215101070305017642 xustar0030 mtime=1761898693.082267639 30 atime=1761898693.082267639 30 ctime=1761898693.082267639 qtractor-1.5.9/src/qtractorMidiFileTempo.h0000644000175000001440000001335015101070305017634 0ustar00rncbcusers// qtractorMidiFileTempo.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 __qtractorMidiFileTempo_h #define __qtractorMidiFileTempo_h #include "qtractorList.h" #include // Forward decls. class qtractorMidiFile; class qtractorTimeScale; //---------------------------------------------------------------------- // class qtractorMidiFileTempo -- MIDI tempo/time-signature map class. // class qtractorMidiFileTempo { public: // Constructor. qtractorMidiFileTempo(qtractorMidiFile *pMidiFile) : m_pMidiFile(pMidiFile) { clear(); } // Destructor. ~qtractorMidiFileTempo() { m_nodes.clear(); m_markers.clear(); } // (Re)nitializer method. void clear(); // Tempo-map node declaration. class Node : public qtractorList::Link { public: // Constructor. Node(unsigned long iTick = 0, float fTempo = 120.0f, unsigned short iBeatsPerBar = 4, unsigned short iBeatDivisor = 2) : tick(iTick), bar(0), tempo(fTempo), beatsPerBar(iBeatsPerBar), beatDivisor(iBeatDivisor), ticksPerBeat(0) {} // Update node coefficients. void update(qtractorMidiFile *pMidiFile); // Update node position metrics. void reset(Node *pNode); // Tick/bar convertors. unsigned short barFromTick(unsigned long iTick) const { return bar + ((iTick - tick) / (ticksPerBeat * beatsPerBar)); } unsigned long tickFromBar(unsigned short iBar) const { return tick + (ticksPerBeat * beatsPerBar * (iBar - bar)) ; } // Tick/bar quantizer. unsigned long tickSnapToBar(unsigned long iTick) const { return tickFromBar(barFromTick(iTick)); } // Node keys. unsigned long tick; unsigned short bar; // Node payload. float tempo; unsigned short beatsPerBar; unsigned short beatDivisor; // Node coefficients. unsigned short ticksPerBeat; }; // Node list accessors. const qtractorList& nodes() const { return m_nodes; } // Node list seeker. Node *seekNode(unsigned long iTick) const; // Node list specifics. Node *addNode( unsigned long iTick = 0, float fTempo = 120.0f, unsigned short iBeatsPerBar = 4, unsigned short iBeatDivisor = 2); void updateNode(Node *pNode); void removeNode(Node *pNode); // More node list specifics (fit to corresp.META events). Node *addNodeTempo(unsigned long iTick, float fTempo) { Node *pNode = seekNode(iTick); return addNode(iTick, fTempo, (pNode ? pNode->beatsPerBar : 4), (pNode ? pNode->beatDivisor : 2)); } Node *addNodeTime(unsigned long iTick, unsigned short iBeatsPerBar, unsigned short iBeatDivisor) { Node *pNode = seekNode(iTick); return addNode(iTick, (pNode ? pNode->tempo : 120.0f), iBeatsPerBar, iBeatDivisor); } // Tempo convertors (default's quarter notes per minute) void setTempo(float fTempo) { Node *pNode = m_nodes.first(); if (pNode) pNode->tempo = fTempo; } float tempo() const { Node *pNode = m_nodes.first(); return (pNode ? pNode->tempo : 120.0f); } // Time signature (numerator) void setBeatsPerBar(unsigned short iBeatsPerBar) { Node *pNode = m_nodes.first(); if (pNode) pNode->beatsPerBar = iBeatsPerBar; } unsigned short beatsPerBar() const { Node *pNode = m_nodes.first(); return (pNode ? pNode->beatsPerBar : 4); } // Time signature (denominator) void setBeatDivisor(unsigned short iBeatDivisor) { Node *pNode = m_nodes.first(); if (pNode) pNode->beatDivisor = iBeatDivisor; } unsigned short beatDivisor() const { Node *pNode = m_nodes.first(); return (pNode ? pNode->beatDivisor : 2); } // Resolution (ticks per beat) unsigned short ticksPerBeat() const { Node *pNode = m_nodes.first(); return (pNode ? pNode->ticksPerBeat : 960); } // Location marker declaration. class Marker : public qtractorList::Link { public: // Constructors. Marker(unsigned long iTick, const QString& sText, int iAccidentals = 0, int iMode = 0) : tick(iTick), text(sText), accidentals(iAccidentals), mode(iMode) {} // Marker key. unsigned long tick; // Marker payload. QString text; int accidentals; int mode; }; // Marker list accessors. const qtractorList& markers() const { return m_markers; } // Location marker seeker (by tick). Marker *seekMarker(unsigned long iTick) const; // Marker list specifics. Marker *addMarker( unsigned long iTick, const QString& sText, int iAccidentals = -9, // = qtractorTimeScale::MinAccidentals int iMode = -1); void removeMarker(Marker *pMarker); // Time-scale sync methods. void fromTimeScale( qtractorTimeScale *pTimeScale, unsigned long iTimeOffset = 0); void intoTimeScale( qtractorTimeScale *pTimeScale, unsigned long iTimeOffset = 0); private: // Tempo-map owner. qtractorMidiFile *m_pMidiFile; // Tempo-map node list. qtractorList m_nodes; // Location markers list. qtractorList m_markers; }; #endif // __qtractorMidiFileTempo_h // end of qtractorMidiFileTempo.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiEvent.h0000644000000000000000000000013215101070305017037 xustar0030 mtime=1761898693.082267639 30 atime=1761898693.082267639 30 ctime=1761898693.082267639 qtractor-1.5.9/src/qtractorMidiEvent.h0000644000175000001440000001212515101070305017030 0ustar00rncbcusers// qtractorMidiEvent.h // /**************************************************************************** Copyright (C) 2005-2013, 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 __qtractorMidiEvent_h #define __qtractorMidiEvent_h #include "qtractorList.h" #include #include //---------------------------------------------------------------------- // class qtractorMidiEvent -- The generic MIDI event element. // class qtractorMidiEvent : public qtractorList::Link { public: // Typical event types. enum EventType { NOTEOFF = 0x80, NOTEON = 0x90, KEYPRESS = 0xa0, CONTROLLER = 0xb0, PGMCHANGE = 0xc0, CHANPRESS = 0xd0, PITCHBEND = 0xe0, SYSEX = 0xf0, META = 0xff, // Extra-ordinary event types... REGPARAM = 0x10, NONREGPARAM = 0x20, CONTROL14 = 0x30 }; // Meta event types. enum MetaType { SEQUENCE = 0x00, TEXT = 0x01, COPYRIGHT = 0x02, TRACKNAME = 0x03, INSTRUMENT = 0x04, LYRIC = 0x05, MARKER = 0x06, CUE = 0x07, CHANNEL = 0x20, PORT = 0x21, EOT = 0x2f, TEMPO = 0x51, SMPTE = 0x54, TIMESIG = 0x58, KEYSIG = 0x59, PROPRIETARY = 0x7f }; // Constructor. qtractorMidiEvent(unsigned long time, EventType type, unsigned short param = 0, unsigned short value = 0, unsigned long duration = 0) : m_time(time), m_type(type) { m_v.param = param; m_v.value = value; m_u.duration = duration; } // Copy constructor. qtractorMidiEvent(const qtractorMidiEvent& e) : m_time(e.m_time), m_type(e.m_type) { if (m_type == SYSEX) { m_v.iSysex = e.m_v.iSysex; m_u.pSysex = new unsigned char [m_v.iSysex]; ::memcpy(m_u.pSysex, e.m_u.pSysex, m_v.iSysex); } else { m_v.param = e.m_v.param; m_v.value = e.m_v.value; m_u.duration = e.m_u.duration; } } // Destructor. ~qtractorMidiEvent() { if (m_type == SYSEX && m_u.pSysex) delete [] m_u.pSysex; } // Event properties accessors (getters). unsigned long time() const { return m_time; } EventType type() const { return m_type; } // Underloaded accessors (getters). unsigned char note() const { return m_v.param; } unsigned char velocity() const { return m_v.value; } unsigned char controller() const { return m_v.param; } unsigned short param() const { return m_v.param; } unsigned short value() const { return m_v.value; } unsigned long duration() const { return m_u.duration; } // Event properties accessors (setters). void setTime(unsigned long time) { m_time = time; } void setType(EventType type) { m_type = type; } // Special event time offset adjust. void adjustTime(unsigned long iOffset) { m_time = (m_time > iOffset ? m_time - iOffset : 0); } // Underloaded accessors (setters). void setNote(unsigned char note) { m_v.param = note; } void setVelocity(unsigned char velocity) { m_v.value = velocity; } void setController(unsigned char controller) { m_v.param = controller; } void setParam(unsigned short param) { m_v.param = param; } void setValue(unsigned short value) { m_v.value = value; } // Duration accessors (NOTEON). void setDuration(unsigned long duration) { m_u.duration = duration; } // Sysex data accessors (SYSEX). unsigned char *sysex() const { return m_u.pSysex; } unsigned short sysex_len() const { return m_v.iSysex; } // Allocate and set a new sysex buffer. void setSysex(unsigned char *pSysex, unsigned short iSysex) { if (m_type == SYSEX && m_u.pSysex) delete [] m_u.pSysex; m_v.iSysex = iSysex; m_u.pSysex = new unsigned char [m_v.iSysex]; ::memcpy(m_u.pSysex, pSysex, m_v.iSysex); } // Special accessors for pitch-bend event types. int pitchBend() const { return int(m_v.value) - 0x2000; } void setPitchBend(int iPitchBend) { m_v.value = (unsigned short) (0x2000 + iPitchBend); } private: // Event instance members. unsigned long m_time; EventType m_type; // Nominal event data. union { struct { // type != SYSEX unsigned short param; unsigned short value; }; unsigned short iSysex; // type == SYSEX } m_v; // Extra event data. union { unsigned long duration; // type == NOTEON unsigned char *pSysex; // type == SYSEX } m_u; }; #endif // __qtractorMidiEvent_h // end of qtractorMidiEvent.h qtractor-1.5.9/src/PaxHeaders/qtractorLv2Gtk2Plugin.cpp0000644000000000000000000000012715101070305020064 xustar0029 mtime=1761898693.07326761 29 atime=1761898693.07326761 29 ctime=1761898693.07326761 qtractor-1.5.9/src/qtractorLv2Gtk2Plugin.cpp0000644000175000001440000000407115101070305020052 0ustar00rncbcusers// qtractorLv2Gtk2Plugin.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 "qtractorLv2Gtk2Plugin.h" #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_UI #ifdef CONFIG_LV2_UI_GTK2 #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #include #ifdef CONFIG_LV2_UI_GTKMM2 #include #endif #pragma GCC diagnostic pop namespace qtractorLv2Gtk2Plugin { static unsigned int g_lv2_ui_gtk2_init = 0; #ifdef CONFIG_LV2_UI_GTKMM2 static Gtk::Main *g_lv2_ui_gtkmm2_main = nullptr; #endif // Module entry point. void init_main (void) { if (++g_lv2_ui_gtk2_init == 1) { ::gtk_init(nullptr, nullptr); #ifdef CONFIG_LV2_UI_GTKMM2 if (g_lv2_ui_gtkmm2_main == nullptr) g_lv2_ui_gtkmm2_main = new Gtk::Main(nullptr, nullptr); #endif } } // Module exit point. void exit_main (void) { if (--g_lv2_ui_gtk2_init == 0) { #ifdef CONFIG_LV2_UI_GTKMM2 if (g_lv2_ui_gtkmm2_main) { delete g_lv2_ui_gtkmm2_main; g_lv2_ui_gtkmm2_main = nullptr; } #endif } } } // namespace qtractorLv2Gtk2Plugin #endif // CONFIG_LV2_UI_GTK2 #endif // CONFIG_LV2_UI #endif // CONFIG_LV2 // end of qtractorLv2Gtk2Plugin.cpp qtractor-1.5.9/src/PaxHeaders/qtractor_plugin_scan.h0000644000000000000000000000013215101070305017614 xustar0030 mtime=1761898693.093267673 30 atime=1761898693.093267673 30 ctime=1761898693.093267673 qtractor-1.5.9/src/qtractor_plugin_scan.h0000644000175000001440000001537615101070305017620 0ustar00rncbcusers// qtractor_plugin_scan.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 __qtractor_plugin_scan_h #define __qtractor_plugin_scan_h #include // Forward decls. class QLibrary; #ifdef CONFIG_LADSPA #include //---------------------------------------------------------------------- // class qtractor_ladspa_scan -- LADSPA plugin (bare bones) interface. // class qtractor_ladspa_scan { public: // Constructor. qtractor_ladspa_scan(); // destructor. ~qtractor_ladspa_scan(); // File loader. bool open(const QString& sFilename); bool open_descriptor(unsigned long iIndex = 0); void close_descriptor(); void close(); // Properties. bool isOpen() const; const QString& name() const { return m_sName; } unsigned int uniqueID() const; int controlIns() const { return m_iControlIns; } int controlOuts() const { return m_iControlOuts; } int audioIns() const { return m_iAudioIns; } int audioOuts() const { return m_iAudioOuts; } bool isRealtime() const; private: // Instance variables. QLibrary *m_pLibrary; const LADSPA_Descriptor *m_pLadspaDescriptor; QString m_sName; int m_iControlIns; int m_iControlOuts; int m_iAudioIns; int m_iAudioOuts; }; #endif // CONFIG_LADSPA #ifdef CONFIG_DSSI #include #include //---------------------------------------------------------------------- // class qtractor_dssi_scan -- DSSI plugin (bare bones) interface. // class qtractor_dssi_scan { public: // Constructor. qtractor_dssi_scan(); // destructor. ~qtractor_dssi_scan(); // File loader. bool open(const QString& sFilename); bool open_descriptor(unsigned long iIndex = 0); void close_descriptor(); void close(); // Properties. bool isOpen() const; const QString& name() const { return m_sName; } unsigned int uniqueID() const; int controlIns() const { return m_iControlIns; } int controlOuts() const { return m_iControlOuts; } int audioIns() const { return m_iAudioIns; } int audioOuts() const { return m_iAudioOuts; } bool isRealtime() const; bool isConfigure() const; bool isEditor() const { return m_bEditor; } private: // Instance variables. QLibrary *m_pLibrary; const DSSI_Descriptor *m_pDssiDescriptor; const LADSPA_Descriptor *m_pLadspaDescriptor; QString m_sName; int m_iControlIns; int m_iControlOuts; int m_iAudioIns; int m_iAudioOuts; bool m_bEditor; }; #endif // CONFIG_DSSI #ifdef CONFIG_VST2 //---------------------------------------------------------------------- // class qtractor_vst2_scan -- VST2 plugin (bare bones) interface. // #ifdef CONFIG_VESTIGE typedef struct _AEffect AEffect; #else struct AEffect; #endif class qtractor_vst2_scan { public: // Constructor. qtractor_vst2_scan(); // destructor. ~qtractor_vst2_scan(); // File loader. bool open(const QString& sFilename); bool open_descriptor(unsigned long iIndex = 0); void close_descriptor(); void close(); // Properties. bool isOpen() const; const QString& name() const { return m_sName; } unsigned int uniqueID() const; int numPrograms() const; int numParams() const; int numInputs() const; int numOutputs() const; int numMidiInputs() const; int numMidiOutputs() const; bool hasEditor() const; bool hasProgramChunks() const; // VST2 host dispatcher. int vst2_dispatch( long opcode, long index, long value, void *ptr, float opt) const; protected: // VST2 flag inquirer. bool vst2_canDo(const char *pszCanDo) const; private: // Instance variables. QLibrary *m_pLibrary; AEffect *m_pEffect; unsigned int m_iFlagsEx; bool m_bEditor; QString m_sName; }; #endif // CONFIG_VST2 #ifdef CONFIG_VST3 //---------------------------------------------------------------------- // class qtractor_vst3_scan -- VST3 plugin (bare bones) interface. // class qtractor_vst3_scan { public: // Constructor. qtractor_vst3_scan(); // destructor. ~qtractor_vst3_scan(); // File loader. bool open(const QString& sFilename); bool open_descriptor(unsigned long iIndex); void close_descriptor(); void close(); // Properties. bool isOpen() const; // 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; } protected: // Forward decls. class Impl; // Cleaner/wiper. void clear(); private: // Instance variables. Impl *m_pImpl; 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; }; #endif // CONFIG_VST3 #ifdef CONFIG_CLAP //---------------------------------------------------------------------- // class qtractor_clap_scan -- CLAP plugin (bare bones) interface. // class qtractor_clap_scan { public: // Constructor. qtractor_clap_scan(); // destructor. ~qtractor_clap_scan(); // File loader. bool open(const QString& sFilename); bool open_descriptor(unsigned long iIndex); void close_descriptor(); void close(); // Properties. bool isOpen() const; // Properties. const QString& name() const; unsigned int uniqueID() const; int controlIns() const; int controlOuts() const; int audioIns() const; int audioOuts() const; int midiIns() const; int midiOuts() const; bool hasEditor() const; bool hasState() const; protected: // Forward decls. class Impl; private: // Instance variables. Impl *m_pImpl; }; #endif // CONFIG_CLAP #endif // __qtractor_plugin_scan_h // end of qtractor_plugin_scan.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiCursor.h0000644000000000000000000000013215101070305017233 xustar0030 mtime=1761898693.079267629 30 atime=1761898693.079267629 30 ctime=1761898693.079267629 qtractor-1.5.9/src/qtractorMidiCursor.h0000644000175000001440000000344615101070305017232 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.9/src/PaxHeaders/qtractorExportForm.cpp0000644000000000000000000000013215101070305017613 xustar0030 mtime=1761898693.070267601 30 atime=1761898693.070267601 30 ctime=1761898693.070267601 qtractor-1.5.9/src/qtractorExportForm.cpp0000644000175000001440000006663515101070305017623 0ustar00rncbcusers// qtractorExportForm.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 "qtractorExportForm.h" #include "qtractorAbout.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include "qtractorAudioFile.h" #include "qtractorMainForm.h" #include "qtractorTracks.h" #include "qtractorSession.h" #include "qtractorOptions.h" #include #include #include #include #include //---------------------------------------------------------------------------- // qtractorExportForm -- UI wrapper form. // Constructor. qtractorExportForm::qtractorExportForm ( 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); // Initialize dirty control state. m_exportType = qtractorTrack::None; m_pTimeScale = nullptr; // Deafult title prefix. m_sExportTitle = tr("Export"); // Try to restore old window positioning. m_ui.ExportTypeWidget->hide(); // adjustSize(); // UI signal/slot connections... QObject::connect(m_ui.ExportPathComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(exportPathChanged(const QString&))); QObject::connect(m_ui.ExportPathToolButton, SIGNAL(clicked()), SLOT(exportPathClicked())); QObject::connect(m_ui.AudioExportTypeComboBox, SIGNAL(activated(int)), SLOT(audioExportTypeChanged(int))); QObject::connect(m_ui.AudioExportFormatComboBox, SIGNAL(activated(int)), SLOT(stabilizeForm())); QObject::connect(m_ui.SessionRangeRadioButton, SIGNAL(toggled(bool)), SLOT(rangeChanged())); QObject::connect(m_ui.LoopRangeRadioButton, SIGNAL(toggled(bool)), SLOT(rangeChanged())); QObject::connect(m_ui.PunchRangeRadioButton, SIGNAL(toggled(bool)), SLOT(rangeChanged())); QObject::connect(m_ui.EditRangeRadioButton, SIGNAL(toggled(bool)), SLOT(rangeChanged())); QObject::connect(m_ui.CustomRangeRadioButton, SIGNAL(toggled(bool)), SLOT(rangeChanged())); QObject::connect(m_ui.ExportBusNameListBox, SIGNAL(currentRowChanged(int)), SLOT(stabilizeForm())); QObject::connect(m_ui.ExportStartSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(valueChanged())); QObject::connect(m_ui.ExportStartSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.ExportEndSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(valueChanged())); QObject::connect(m_ui.ExportEndSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.FormatComboBox, SIGNAL(activated(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.AddTrackCheckBox, SIGNAL(toggled(bool)), SLOT(stabilizeForm())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(rejected()), SLOT(reject())); } // Destructor. qtractorExportForm::~qtractorExportForm (void) { // Don't forget to get rid of local time-scale instance... if (m_pTimeScale) delete m_pTimeScale; } // Default window title (prefix). void qtractorExportForm::setExportTitle ( const QString& sExportTitle ) { m_sExportTitle = sExportTitle; } const QString& qtractorExportForm::exportTitle (void) const { return m_sExportTitle; } // Populate (setup) dialog controls from settings descriptors. void qtractorExportForm::setExportType ( qtractorTrack::TrackType exportType ) { // Export type... m_exportType = exportType; QIcon icon; qtractorEngine *pEngine = nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { // Copy from global time-scale instance... if (m_pTimeScale) delete m_pTimeScale; m_pTimeScale = new qtractorTimeScale(*pSession->timeScale()); m_ui.ExportStartSpinBox->setTimeScale(m_pTimeScale); m_ui.ExportEndSpinBox->setTimeScale(m_pTimeScale); switch (m_exportType) { case qtractorTrack::Audio: pEngine = pSession->audioEngine(); icon = QIcon::fromTheme("trackAudio"); m_sExportType = tr("Audio"); m_sExportExt = qtractorAudioFileFactory::defaultExt(); m_ui.ExportTypeWidget->removeWidget(m_ui.MidiExportTypePage); break; case qtractorTrack::Midi: pEngine = pSession->midiEngine(); icon = QIcon::fromTheme("trackMidi"); m_sExportType = tr("MIDI"); m_sExportExt = "mid"; m_ui.ExportTypeWidget->removeWidget(m_ui.AudioExportTypePage); break; case qtractorTrack::None: default: m_sExportType.clear(); m_sExportExt.clear(); break; } } // Grab export file history, one that might me useful... m_ui.ExportPathComboBox->setObjectName( m_sExportType + m_ui.ExportPathComboBox->objectName()); qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { pOptions->loadComboBoxHistory(m_ui.ExportPathComboBox); switch (m_exportType) { case qtractorTrack::Audio: { // Audio options... QString sAudioExportExt = pOptions->sAudioExportExt; int iAudioExportType = pOptions->iAudioExportType; int iAudioExportFormat = pOptions->iAudioExportFormat; int iAudioExportQuality = pOptions->iAudioExportQuality; if (sAudioExportExt.isEmpty()) sAudioExportExt = pOptions->sAudioCaptureExt; if (iAudioExportType < 0) iAudioExportType = pOptions->iAudioCaptureType; if (iAudioExportFormat < 0) iAudioExportFormat = pOptions->iAudioCaptureFormat; if (iAudioExportQuality < 0) iAudioExportQuality = pOptions->iAudioCaptureQuality; m_ui.AudioExportTypeComboBox->setCurrentType( sAudioExportExt, iAudioExportType); m_ui.AudioExportFormatComboBox->setCurrentIndex(iAudioExportFormat); m_ui.AudioExportQualitySpinBox->setValue(iAudioExportQuality); m_sExportExt = m_ui.AudioExportTypeComboBox->currentExt(); m_ui.AddTrackCheckBox->setChecked(pOptions->bExportAddTrack); m_ui.AddTrackCheckBox->show(); break; } case qtractorTrack::Midi: { // Initial MIDI options... int iMidiExportFormat = pOptions->iMidiExportFormat; if (iMidiExportFormat < 0) iMidiExportFormat = pOptions->iMidiCaptureFormat; m_ui.MidiExportFormatComboBox->setCurrentIndex(iMidiExportFormat); m_ui.AddTrackCheckBox->hide(); break; } case qtractorTrack::None: default: break; } } // Suggest a brand new export filename... if (pSession) { m_ui.ExportPathComboBox->setEditText( pSession->createFilePath("export", m_sExportExt)); } // Fill in the output bus names list... m_ui.ExportBusNameListBox->clear(); m_ui.ExportBusNameListBox->setIconSize(QSize(16, 16)); if (pEngine) { QDialog::setWindowIcon(icon); QDialog::setWindowTitle( windowTitleEx(m_sExportTitle, m_sExportType)); for (qtractorBus *pBus = pEngine->buses().first(); pBus; pBus = pBus->next()) { if (pBus->busMode() & qtractorBus::Output) m_ui.ExportBusNameListBox->addItem( new QListWidgetItem(icon, pBus->busName())); } } // Set proper time scales display format... if (m_pTimeScale) { m_ui.FormatComboBox->setCurrentIndex( int(m_pTimeScale->displayFormat())); } // Populate range values... switch (pOptions ? RangeType(pOptions->iExportRangeType) : Session) { case Custom: m_ui.CustomRangeRadioButton->setChecked(true); break; case Edit: if (pSession && pSession->editHead() < pSession->editTail()) { m_ui.EditRangeRadioButton->setChecked(true); break; } // Fall thru... case Punch: if (pSession && pSession->isPunching()) { m_ui.PunchRangeRadioButton->setChecked(true); break; } // Fall thru... case Loop: if (pSession && pSession->isLooping()) { m_ui.LoopRangeRadioButton->setChecked(true); break; } // Fall thru... case Session: default: m_ui.SessionRangeRadioButton->setChecked(true); break; } rangeChanged(); // Shake it a little bit first, but // make it as tight as possible... m_ui.ExportTypeWidget->show(); QDialog::resize(width() - 1, height() - 1); // adjustSize(); // Done. stabilizeForm(); } // Retrieve the current export type, if the case arises. qtractorTrack::TrackType qtractorExportForm::exportType (void) const { return m_exportType; } // Choose the target filename of export. void qtractorExportForm::exportPathClicked (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; QString sExportPath = m_ui.ExportPathComboBox->currentText(); if (sExportPath.isEmpty()) sExportPath = pSession->sessionDir(); // Actual browse for the file... const QString& sTitle = tr("Export %1 File").arg(m_sExportType); QStringList filters; switch (m_exportType) { case qtractorTrack::Audio: filters.append(qtractorAudioFileFactory::filters()); break; case qtractorTrack::Midi: filters.append(tr("MIDI files (*.%1 *.smf *.midi)").arg(m_sExportExt)); // Fall-thru... case qtractorTrack::None: default: filters.append(tr("All files (*.*)")); break; } 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) sExportPath = QFileDialog::getSaveFileName(pParentWidget, sTitle, sExportPath, sFilter, nullptr, options); #else QFileDialog fileDialog(pParentWidget, sTitle, sExportPath, sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptSave); fileDialog.setFileMode(QFileDialog::AnyFile); fileDialog.setDefaultSuffix(m_sExportExt); // Stuff sidebar... if (pOptions) { QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); switch (m_exportType) { case qtractorTrack::Audio: urls.append(QUrl::fromLocalFile(pOptions->sAudioDir)); break; case qtractorTrack::Midi: urls.append(QUrl::fromLocalFile(pOptions->sMidiDir)); break; case qtractorTrack::None: default: break; } fileDialog.setSidebarUrls(urls); } fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sExportPath = fileDialog.selectedFiles().first(); else sExportPath.clear(); #endif // Have we cancelled it? if (sExportPath.isEmpty() || sExportPath.at(0) == '.') return; // Enforce default file extension... if (QFileInfo(sExportPath).suffix().isEmpty()) sExportPath += '.' + m_sExportExt; // Finallly set as wanted... m_ui.ExportPathComboBox->setEditText(sExportPath); m_ui.ExportPathComboBox->setFocus(); stabilizeForm(); } // Export path changed. void qtractorExportForm::exportPathChanged ( const QString& sExportPath ) { if (m_exportType == qtractorTrack::Audio) { const QString& sExportExt = QFileInfo(sExportPath).suffix().toLower(); if (sExportExt != m_sExportExt) { const int iIndex = m_ui.AudioExportTypeComboBox->indexOf(sExportExt); if (iIndex >= 0) { m_ui.AudioExportTypeComboBox->setCurrentIndex(iIndex); m_sExportExt = sExportExt; audioExportTypeUpdate(iIndex); } } } stabilizeForm(); } // Audio file type changed. void qtractorExportForm::audioExportTypeChanged ( int iIndex ) { if (m_exportType == qtractorTrack::Audio) { const void *handle = m_ui.AudioExportTypeComboBox->handleOf(iIndex); if (handle) { m_sExportExt = m_ui.AudioExportTypeComboBox->currentExt(handle); QString sExportPath = m_ui.ExportPathComboBox->currentText(); const QString& sExportExt = QFileInfo(sExportPath).suffix().toLower(); if (sExportExt != m_sExportExt) { if (sExportExt.isEmpty()) sExportPath += '.' + m_sExportExt; else sExportPath.replace('.' + sExportExt, '.' + m_sExportExt); m_ui.ExportPathComboBox->setEditText(sExportPath); } } audioExportTypeUpdate(iIndex); } stabilizeForm(); } // Range type has changed. void qtractorExportForm::rangeChanged (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; unsigned long iExportStart = 0; unsigned long iExportEnd = 0; if (m_ui.CustomRangeRadioButton->isChecked()) { iExportStart = pOptions->iExportRangeStart; iExportEnd = pOptions->iExportRangeEnd; } else if (m_ui.EditRangeRadioButton->isChecked()) { iExportStart = pSession->editHead(); iExportEnd = pSession->editTail(); } else if (m_ui.PunchRangeRadioButton->isChecked()) { iExportStart = pSession->punchIn(); iExportEnd = pSession->punchOut(); } else if (m_ui.LoopRangeRadioButton->isChecked()) { iExportStart = pSession->loopStart(); iExportEnd = pSession->loopEnd(); } if (iExportStart >= iExportEnd) { iExportStart = pSession->sessionStart(); iExportEnd = pSession->sessionEnd(); } #if 0 if (iExportEnd > pSession->sessionEnd()) iExportEnd = pSession->sessionEnd(); #endif m_ui.ExportStartSpinBox->setValue(iExportStart, false); m_ui.ExportEndSpinBox->setValue(iExportEnd, false); stabilizeForm(); } // Range values have changed. void qtractorExportForm::valueChanged (void) { m_ui.CustomRangeRadioButton->setChecked(true); stabilizeForm(); } // Display format has changed. void qtractorExportForm::formatChanged ( int iDisplayFormat ) { const bool bBlockSignals = m_ui.FormatComboBox->blockSignals(true); m_ui.FormatComboBox->setCurrentIndex(iDisplayFormat); qtractorTimeScale::DisplayFormat displayFormat = qtractorTimeScale::DisplayFormat(iDisplayFormat); m_ui.ExportStartSpinBox->setDisplayFormat(displayFormat); m_ui.ExportEndSpinBox->setDisplayFormat(displayFormat); if (m_pTimeScale) m_pTimeScale->setDisplayFormat(displayFormat); m_ui.FormatComboBox->blockSignals(bBlockSignals); stabilizeForm(); } // Stabilize current form state. void qtractorExportForm::stabilizeForm (void) { // General options validy check... const QString& sExportPath = m_ui.ExportPathComboBox->currentText(); bool bValid = !sExportPath.isEmpty() && QFileInfo(sExportPath).dir().exists(); // Audio options stabilizing and validy check... if (m_exportType == qtractorTrack::Audio) { const qtractorAudioFileFactory::FileFormat *pFormat = static_cast ( m_ui.AudioExportTypeComboBox->currentHandle()); const bool bSndFile = (pFormat && pFormat->type == qtractorAudioFileFactory::SndFile); m_ui.AudioExportFormatTextLabel->setEnabled(bSndFile); m_ui.AudioExportFormatComboBox->setEnabled(bSndFile); const bool bVorbisFile = (pFormat && pFormat->type == qtractorAudioFileFactory::VorbisFile); m_ui.AudioExportQualityTextLabel->setEnabled(bVorbisFile); m_ui.AudioExportQualitySpinBox->setEnabled(bVorbisFile); if (bValid && pFormat == nullptr) bValid = false; if (bValid) { const int iFormat = m_ui.AudioExportFormatComboBox->currentIndex(); bValid = qtractorAudioFileFactory::isValidFormat(pFormat, iFormat); } } m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(bValid); } // Audio file type changed aftermath. void qtractorExportForm::audioExportTypeUpdate ( int iIndex ) { const void *handle = m_ui.AudioExportTypeComboBox->handleOf(iIndex); const qtractorAudioFileFactory::FileFormat *pFormat = static_cast (handle); if (handle && pFormat) { const bool bBlockSignals = m_ui.AudioExportFormatComboBox->blockSignals(true); int iFormat = m_ui.AudioExportFormatComboBox->currentIndex(); while (iFormat > 0 && // Retry down to PCM Signed 16-Bit... !qtractorAudioFileFactory::isValidFormat(pFormat, iFormat)) --iFormat; m_ui.AudioExportFormatComboBox->setCurrentIndex(iFormat); m_ui.AudioExportFormatComboBox->blockSignals(bBlockSignals); } } // Retrieve current audio file suffix. const QString& qtractorExportForm::exportExt (void) const { return m_sExportExt; } // Retrieve current aliased file format index... int qtractorExportForm::audioExportFormat (void) const { if (m_exportType != qtractorTrack::Audio) return -1; const qtractorAudioFileFactory::FileFormat *pFormat = static_cast ( m_ui.AudioExportTypeComboBox->currentHandle()); if (pFormat == nullptr) return -1; if (pFormat->type == qtractorAudioFileFactory::VorbisFile) return m_ui.AudioExportQualitySpinBox->value(); else return m_ui.AudioExportFormatComboBox->currentIndex(); } int qtractorExportForm::midiExportFormat (void) const { if (m_exportType != qtractorTrack::Midi) return -1; return m_ui.MidiExportFormatComboBox->currentIndex(); } // Save export options (settings). void qtractorExportForm::saveExportOptions (void) { qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; pOptions->saveComboBoxHistory(m_ui.ExportPathComboBox); switch (m_exportType) { case qtractorTrack::Audio: { // Audio options... const void *handle = m_ui.AudioExportTypeComboBox->currentHandle(); pOptions->sAudioExportExt = m_ui.AudioExportTypeComboBox->currentExt(handle);; pOptions->iAudioExportType = m_ui.AudioExportTypeComboBox->currentType(handle); pOptions->iAudioExportFormat = m_ui.AudioExportFormatComboBox->currentIndex(); pOptions->iAudioExportQuality = m_ui.AudioExportQualitySpinBox->value(); pOptions->bExportAddTrack = m_ui.AddTrackCheckBox->isChecked(); break; } case qtractorTrack::Midi: // MIDI options... pOptions->iMidiExportFormat = m_ui.MidiExportFormatComboBox->currentIndex(); // Fall-thru... case qtractorTrack::None: default: break; } if (m_ui.CustomRangeRadioButton->isChecked()) { pOptions->iExportRangeType = Custom; pOptions->iExportRangeStart = m_ui.ExportStartSpinBox->value(); pOptions->iExportRangeEnd = m_ui.ExportEndSpinBox->value(); } else if (m_ui.EditRangeRadioButton->isChecked()) pOptions->iExportRangeType = Edit; else if (m_ui.PunchRangeRadioButton->isChecked()) pOptions->iExportRangeType = Punch; else if (m_ui.LoopRangeRadioButton->isChecked()) pOptions->iExportRangeType = Loop; else // if (m_ui.SessionRangeRadioButton->isChecked()) pOptions->iExportRangeType = Session; } //---------------------------------------------------------------------------- // qtractorExportTrackForm -- UI wrapper form. // Constructor. qtractorExportTrackForm::qtractorExportTrackForm ( QWidget *pParent ) : qtractorExportForm(pParent) { } // Destructor. qtractorExportTrackForm::~qtractorExportTrackForm (void) { } // Make up window/dialog title. QString qtractorExportTrackForm::windowTitleEx ( const QString& sExportTitle, const QString& sExportType ) const { return tr("%1 %2 Tracks").arg(sExportTitle).arg(sExportType); } // Stabilize current form state. void qtractorExportTrackForm::stabilizeForm (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Range options stabilizing... m_ui.LoopRangeRadioButton->setEnabled(pSession->isLooping()); m_ui.PunchRangeRadioButton->setEnabled(pSession->isPunching()); m_ui.EditRangeRadioButton->setEnabled( pSession->editHead() < pSession->editTail()); const unsigned long iExportStart = m_ui.ExportStartSpinBox->value(); const unsigned long iExportEnd = m_ui.ExportEndSpinBox->value(); m_ui.ExportStartSpinBox->setMaximum(iExportEnd); m_ui.ExportEndSpinBox->setMinimum(iExportStart); qtractorExportForm::stabilizeForm(); QPushButton *pPushButton = m_ui.DialogButtonBox->button(QDialogButtonBox::Ok); if (pPushButton && pPushButton->isEnabled()) { pPushButton->setEnabled( m_ui.ExportBusNameListBox->currentItem() != nullptr && m_ui.ExportStartSpinBox->value() < m_ui.ExportEndSpinBox->value()); } } // Executive slots -- accept settings (OK button slot). void qtractorExportTrackForm::accept (void) { // Must always be a export bus target... const QList& exportBusNameItems = m_ui.ExportBusNameListBox->selectedItems(); if (exportBusNameItems.isEmpty()) return; // Enforce (again) default file extension... QString sExportPath = m_ui.ExportPathComboBox->currentText(); if (QFileInfo(sExportPath).suffix().isEmpty()) sExportPath += '.' + m_sExportExt; // Check (again) wether the file already exists... if (QFileInfo(sExportPath).exists()) { if (QMessageBox::warning(this, tr("Warning"), tr("The file already exists:\n\n" "\"%1\"\n\n" "Do you want to replace it?") .arg(sExportPath), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) { m_ui.ExportPathComboBox->setFocus(); return; } } qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; // It can take a minute... m_ui.ExportPathTextLabel->setEnabled(false); m_ui.ExportPathComboBox->setEnabled(false); m_ui.ExportPathToolButton->setEnabled(false); m_ui.ExportTypeWidget->setEnabled(false); m_ui.ExportBusGroupBox->setEnabled(false); m_ui.ExportRangeGroupBox->setEnabled(false); m_ui.FormatGroupBox->setEnabled(false); m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false); // Carry on... switch (m_exportType) { case qtractorTrack::Audio: { // Audio file export... qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine) { // Get the export buses by name... QList exportBuses; QListIterator iter(exportBusNameItems); while (iter.hasNext()) { qtractorAudioBus *pExportBus = static_cast ( pAudioEngine->findOutputBus(iter.next()->text())); if (pExportBus) exportBuses.append(pExportBus); } // Log this event... pMainForm->appendMessages( tr("Audio file export: \"%1\" started...") .arg(sExportPath)); // Do the export as commanded... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Go... const bool bResult = pAudioEngine->fileExport( sExportPath, exportBuses, m_ui.ExportStartSpinBox->value(), m_ui.ExportEndSpinBox->value(), audioExportFormat()); // Done. QApplication::restoreOverrideCursor(); if (bResult) { // Add new tracks if necessary... qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks && m_ui.AddTrackCheckBox->isChecked()) { pTracks->addAudioTracks( QStringList(sExportPath), pAudioEngine->exportStart(), pAudioEngine->exportOffset(), pAudioEngine->exportLength(), pTracks->currentTrack()); } else pMainForm->addAudioFile(sExportPath); // Log the success... pMainForm->appendMessages( tr("Audio file export: \"%1\" complete.") .arg(sExportPath)); } else { // Log the failure... pMainForm->appendMessagesError( tr("Audio file export:\n\n\"%1\"\n\nfailed.") .arg(sExportPath)); } // HACK: Reset all (internal) MIDI controllers... qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine) pMidiEngine->resetAllControllers(true); // Force immediate. } break; } case qtractorTrack::Midi: { // MIDI file export... qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine) { // Get the export buses by name... QList exportBuses; QListIterator iter(exportBusNameItems); while (iter.hasNext()) { qtractorMidiBus *pExportBus = static_cast ( pMidiEngine->findOutputBus(iter.next()->text())); if (pExportBus) exportBuses.append(pExportBus); } // Log this event... pMainForm->appendMessages( tr("MIDI file export: \"%1\" started...") .arg(sExportPath)); // Do the export as commanded... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Go... const bool bResult = pMidiEngine->fileExport( sExportPath, exportBuses, m_ui.ExportStartSpinBox->value(), m_ui.ExportEndSpinBox->value(), midiExportFormat()); // Done. QApplication::restoreOverrideCursor(); if (bResult) { #if 0 // Add new tracks if necessary... qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks && m_ui.AddTrackCheckBox->isChecked()) { pTracks->addMidiTracks( QStringList(sExportPath), m_ui.ExportStartSpinBox->value(), pTracks->currentTrack()); } else #endif pMainForm->addMidiFile(sExportPath); // Log the success... pMainForm->appendMessages( tr("MIDI file export: \"%1\" complete.") .arg(sExportPath)); } else { // Log the failure... pMainForm->appendMessagesError( tr("MIDI file export:\n\n\"%1\"\n\nfailed.") .arg(sExportPath)); } } break; } case qtractorTrack::None: default: break; } // Save other conveniency options... saveExportOptions(); // Just go with dialog acceptance. qtractorExportForm::accept(); } // Executive slots -- reject settings (Cancel button slot). void qtractorExportTrackForm::reject (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { // It can take a minute... m_ui.DialogButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); switch (m_exportType) { case qtractorTrack::Audio: { // Audio file export... qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine) pAudioEngine->setExporting(false); break; } case qtractorTrack::Midi: default: break; } } // Bail out... qtractorExportForm::reject(); } //---------------------------------------------------------------------------- // qtractorExportClipForm -- UI wrapper form. // Constructor. qtractorExportClipForm::qtractorExportClipForm ( QWidget *pParent ) : qtractorExportForm(pParent) { // Hide and disable all unecessary stuff. m_ui.ExportRangeGroupBox->setEnabled(false); m_ui.ExportRangeGroupBox->setVisible(false); m_ui.ExportBusGroupBox->setEnabled(false); m_ui.ExportBusGroupBox->setVisible(false); m_ui.FormatGroupBox->setEnabled(false); m_ui.FormatGroupBox->setVisible(false); m_ui.AddTrackCheckBox->setEnabled(false); m_ui.AddTrackCheckBox->setVisible(false); adjustSize(); } // Destructor. qtractorExportClipForm::~qtractorExportClipForm (void) { } // Make up window/dialog title. QString qtractorExportClipForm::windowTitleEx ( const QString& sExportTitle, const QString& sExportType ) const { return tr("%1 %2 Clips").arg(sExportTitle).arg(sExportType); } // Settle/retrieve the export path. void qtractorExportClipForm::setExportPath ( const QString& sExportPath ) { m_ui.ExportPathComboBox->setCurrentText(sExportPath); } QString qtractorExportClipForm::exportPath (void) const { return m_ui.ExportPathComboBox->currentText(); } // Executive slots -- accept settings (OK button slot). void qtractorExportClipForm::accept (void) { saveExportOptions(); qtractorExportForm::accept(); } // end of qtractorExportForm.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMidiEngine.h0000644000000000000000000000013215101070305017163 xustar0030 mtime=1761898693.082267639 30 atime=1761898693.082267639 30 ctime=1761898693.082267639 qtractor-1.5.9/src/qtractorMidiEngine.h0000644000175000001440000004331615101070305017162 0ustar00rncbcusers// qtractorMidiEngine.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 __qtractorMidiEngine_h #define __qtractorMidiEngine_h #include "qtractorEngine.h" #include "qtractorTimeScale.h" #include "qtractorMmcEvent.h" #include "qtractorCtlEvent.h" #include #include #include #include // Forward declarations. class qtractorMidiBus; class qtractorMidiClip; class qtractorMidiEvent; class qtractorMidiSequence; class qtractorMidiInputThread; class qtractorMidiOutputThread; class qtractorMidiMonitor; class qtractorMidiSysexList; class qtractorMidiInputBuffer; class qtractorMidiPlayer; class qtractorPluginList; class qtractorCurveList; class QSocketNotifier; //---------------------------------------------------------------------- // class qtractorMidiEngineProxy -- MIDI engine event proxy object. // class qtractorMidiEngineProxy : public QObject { Q_OBJECT public: // Constructor. qtractorMidiEngineProxy(QObject *pParent = nullptr) : QObject(pParent) {} // Event notifications. void notifyMmcEvent(const qtractorMmcEvent& mmce) { emit mmcEvent(mmce); } void notifyCtlEvent(const qtractorCtlEvent& ctle) { emit ctlEvent(ctle); } void notifySppEvent(int iSppCmd, unsigned short iSongPos) { emit sppEvent(iSppCmd, iSongPos); } void notifyClkEvent(float fTempo) { emit clkEvent(fTempo); } void notifyInpEvent(unsigned short flags) { emit inpEvent(flags); } signals: // Event signals. void mmcEvent(const qtractorMmcEvent& mmce); void ctlEvent(const qtractorCtlEvent& ctle); void sppEvent(int iSppCmd, unsigned short iSongPos); void clkEvent(float fTempo); void inpEvent(unsigned short flags); }; //---------------------------------------------------------------------- // class qtractorMidiEngine -- ALSA sequencer client instance (singleton). // class qtractorMidiEngine : public qtractorEngine { public: // Constructor. qtractorMidiEngine(qtractorSession *pSession); // Engine initialization. bool init(); // Special event notifier proxy object. qtractorMidiEngineProxy *proxy(); // ALSA client descriptor accessor. snd_seq_t *alsaSeq() const; int alsaClient() const; int alsaQueue() const; // ALSA queue time accessor. unsigned long queueTime() const; // ALSA subscription port notifier. QSocketNotifier *alsaNotifier() const; void alsaNotifyAck(); // Read ahead frames configuration. void setReadAhead(unsigned int iReadAhead); unsigned int readAhead() const; // MIDI output process cycle iteration. void process(); // Special slave sync method. void sync(); // Reset queue time. void resetTime(); void resetSync(); // Reset queue tempo. void resetTempo(); // Reset all MIDI monitoring... void resetAllMonitors(); // Reset all MIDI controllers... void resetAllControllers(bool bForceImmediate); bool isResetAllControllersPending() const; // Shut-off all MIDI buses (stop)... void shutOffAllBuses(bool bClose = false); // Shut-off all MIDI tracks (panic)... void shutOffAllTracks(); // ALSA port input registry methods. void addInputBus(qtractorMidiBus *pMidiBus); void removeInputBus(qtractorMidiBus *pMidiBus); void addInputBuffer(int iAlsaPort, qtractorMidiInputBuffer *pMidiInputBuffer); void removeInputBuffer(int iAlsaPort); // MIDI event capture method. void capture(snd_seq_event_t *pEv); // MIDI event enqueue method. void enqueue(qtractorTrack *pTrack, qtractorMidiEvent *pEvent, unsigned long iTime, float fGain = 1.0f); // Flush ouput queue (if necessary)... void flush(); // The delta-time/frame accessors. long timeStart() const; // The absolute-time/frame accessors. unsigned long timeStartEx() const; // Special track-immediate methods. void trackMute(qtractorTrack *pTrack, bool bMute); // Special metronome-immediate methods. void metroMute(bool bMute); // Metronome switching. void setMetronome(bool bMetronome); bool isMetronome() const; // Metronome enabled accessors. void setMetroEnabled(bool bMetroEnabled); bool isMetroEnabled() const; // Metronome bus accessors. void setMetroBus(bool bMetroBus); bool isMetroBus() const; void resetMetroBus(); // Metronome parameters. void setMetroChannel (unsigned short iChannel); unsigned short metroChannel() const; void setMetroBar(int iNote, int iVelocity, unsigned long iDuration); int metroBarNote() const; int metroBarVelocity() const; unsigned long metroBarDuration() const; void setMetroBeat(int iNote, int iVelocity, unsigned long iDuration); int metroBeatNote() const; int metroBeatVelocity() const; unsigned long metroBeatDuration() const; // Metronome latency offset (in ticks). void setMetroOffset(unsigned long iMetroOffset); unsigned long metroOffset() const; // Process metronome clicks. void processMetro(unsigned long iFrameStart, unsigned long iFrameEnd); // Access to current tempo/time-signature cursor. qtractorTimeScale::Cursor *metroCursor() const; // Metronome count-in switching. void setCountIn(bool bCountIn); bool isCountIn() const; // Metronome count-in mode. enum CountInMode { CountInNone = 0, CountInPlayback, CountInRecording }; void setCountInMode(CountInMode countInMode); CountInMode countInMode() const; // Metronome count-in number of beats. void setCountInBeats(unsigned short iCountInBeats); unsigned short countInBeats() const; // Metronome count-in status. unsigned short countIn(unsigned int nframes); unsigned short countIn() const; // Control bus accessors. void setControlBus(bool bControlBus); bool isControlBus() const; void resetControlBus(); // Control buses accessors. qtractorMidiBus *controlBus_in() const; qtractorMidiBus *controlBus_out() const; // Audition/pre-listening bus mode accessors. void setPlayerBus(bool bPlayerBus); bool isPlayerBus() const; bool isPlayerOpen() const; bool openPlayer(const QString& sFilename, int iTrackChannel = -1); void closePlayer(); // MMC dispatch special commands. void sendMmcLocate(unsigned long iLocate) const; void sendMmcMaskedWrite(qtractorMmcEvent::SubCommand scmd, int iTrack, bool bOn) const; void sendMmcCommand(qtractorMmcEvent::Command cmd, unsigned char *pMmcData = nullptr, unsigned short iMmcData = 0) const; // SPP dispatch special command. void sendSppCommand(int iCmdType, unsigned int iSongPos = 0) const; // Document element methods. bool loadElement(qtractorDocument *pDocument, QDomElement *pElement); bool saveElement(qtractorDocument *pDocument, QDomElement *pElement) const; // MIDI-export method. bool fileExport(const QString& sExportPath, const QList& exportBuses, unsigned long iExportStart, unsigned long iExportEnd, int iExportFormat = -1); // Retrieve/restore all connections, on all buses; // return the effective number of connection attempts. int updateConnects(); // Capture (record) quantization accessors. void setCaptureQuantize(unsigned short iCaptureQuantize); unsigned short captureQuantize() const; // ALSA device queue timer. void setAlsaTimer(int iAlsaTimer); int alsaTimer() const; // Drift check/correction accessors. void setDriftCorrect(bool bDriftCorrect); bool isDriftCorrect() const; // MMC device-id accessors. void setMmcDevice(unsigned char mmcDevice); unsigned char mmcDevice() const; // MMC mode accessors. void setMmcMode(qtractorBus::BusMode mmcMode); qtractorBus::BusMode mmcMode() const; // SPP mode accessors. void setSppMode(qtractorBus::BusMode sppMode); qtractorBus::BusMode sppMode() const; // MIDI Clock mode accessors. void setClockMode(qtractorBus::BusMode clockMode); qtractorBus::BusMode clockMode() const; // Whether to reset all MIDI controllers (on playback start). void setResetAllControllers(bool bResetAllControllers); bool isResetAllControllers() const; // Reset ouput queue drift stats (audio vs. MIDI)... void resetDrift(); // Step-input event flags... enum InpFlag { InpNone = 0, InpReset = 1, InpEvent = 2 }; // Process pending step-input events... void processInpEvents(); protected: // Concrete device (de)activation methods. bool activate(); bool start(); void stop(); void deactivate(); void clean(); // Metronome (de)activation methods. void createMetroBus(); bool openMetroBus(); void closeMetroBus(); void deleteMetroBus(); // Control (de)activation methods. void createControlBus(); bool openControlBus(); void closeControlBus(); void deleteControlBus(); // Player (de)activation methods. void createPlayerBus(); bool openPlayerBus(); void closePlayerBus(); void deletePlayerBus(); // MIDI/Audio sync-check predicate. qtractorSessionCursor *midiCursorSync(bool bStart = false); // Process metronome count-ins. void processCountIn(unsigned long iFrameStart, unsigned long iFrameEnd); // Do ouput queue drift stats (audio vs. MIDI)... void driftCheck(); private: // Special event notifier proxy object. qtractorMidiEngineProxy m_proxy; // Device instance variables. snd_seq_t *m_pAlsaSeq; int m_iAlsaClient; int m_iAlsaQueue; int m_iAlsaTimer; // Subscription notification stuff. snd_seq_t *m_pAlsaSubsSeq; int m_iAlsaSubsPort; QSocketNotifier *m_pAlsaNotifier; // The number of frames to read-ahead. unsigned int m_iReadAhead; // Name says it all. qtractorMidiInputThread *m_pInputThread; qtractorMidiOutputThread *m_pOutputThread; // ALSA port input registries. QHash m_inputBuses; QHash m_inputBuffers; // Whether to check for time drift. bool m_bDriftCorrect; // The number of times we check for time drift. unsigned int m_iDriftCheck; unsigned int m_iDriftCount; long m_iTimeDrift; long m_iFrameDrift; // The delta-time/frame when playback started. long m_iTimeStart; long m_iFrameStart; // The absolute-time/frame when playback started. unsigned long m_iTimeStartEx; unsigned long m_iAudioFrameStart; // The assigned control buses. bool m_bControlBus; qtractorMidiBus *m_pIControlBus; qtractorMidiBus *m_pOControlBus; // Metronome enablement. bool m_bMetronome; bool m_bMetroBus; qtractorMidiBus *m_pMetroBus; unsigned short m_iMetroChannel; int m_iMetroBarNote; int m_iMetroBarVelocity; unsigned long m_iMetroBarDuration; int m_iMetroBeatNote; int m_iMetroBeatVelocity; unsigned long m_iMetroBeatDuration; unsigned long m_iMetroOffset; bool m_bMetroEnabled; // Time-scale cursor (tempo/time-signature map) qtractorTimeScale::Cursor *m_pMetroCursor; // Track down tempo changes. float m_fMetroTempo; // Count-in stuff. bool m_bCountIn; CountInMode m_countInMode; unsigned short m_iCountInBeats; unsigned short m_iCountIn; unsigned long m_iCountInFrame; unsigned long m_iCountInFrameStart; unsigned long m_iCountInFrameEnd; long m_iCountInTimeStart; // SMF player enablement. bool m_bPlayerBus; qtractorMidiBus *m_pPlayerBus; qtractorMidiPlayer *m_pPlayer; // Input quantization (aka. record snap-per-beat). unsigned short m_iCaptureQuantize; // Controller update pending flagger. int m_iResetAllControllersPending; // MMC Device ID. unsigned char m_mmcDevice; // MMC/SPP modes. qtractorBus::BusMode m_mmcMode; qtractorBus::BusMode m_sppMode; // MIDI Clock mode. qtractorBus::BusMode m_clockMode; // Whether to reset all MIDI controllers (on playback start). bool m_bResetAllControllers; // MIDI Clock tempo tracking. unsigned short m_iClockCount; float m_fClockTempo; // Step-input event data... typedef QMultiHash InpEvents; InpEvents m_inpEvents; QMutex m_inpMutex; }; //---------------------------------------------------------------------- // class qtractorMidiBus -- Managed ALSA sequencer port set // class qtractorMidiBus : public qtractorBus { public: // Constructor. qtractorMidiBus(qtractorMidiEngine *pMidiEngine, const QString& sBusName, BusMode busMode, bool bMonitor = false); // Destructor. ~qtractorMidiBus(); // ALSA sequencer port accessor. int alsaPort() const; // Activation methods. bool open(); void close(); // Shut-off everything out there. void shutOff(bool bClose = false); // SysEx setup list accessors. qtractorMidiSysexList *sysexList() const; // Default instrument name accessors. void setInstrumentName(const QString& sInstrumentName); const QString& instrumentName() const; // Channel map payload. struct Patch { // Default payload constructor. Patch() : bankSelMethod(-1), bank(-1), prog(-1) {} // Validate members. bool isValid() const { return (bankSelMethod >= 0 || bank >= 0 || prog >= 0); } // Payload members. QString instrumentName; int bankSelMethod; int bank; int prog; }; // Channel patch map accessors. const Patch& patch(unsigned iChannel) { return m_patches[iChannel & 0x0f]; } // Direct MIDI bank/program selection helper. void setPatch(unsigned short iChannel, const QString& sInstrumentName, int iBankSelMethod, int iBank, int iProg, qtractorTrack *pTrack); // Direct MIDI controller helpers. void setController(qtractorTrack *pTrack, int iController, int iValue = 0) const; void setController(unsigned short iChannel, int iController, int iValue = 0) const; // Direct MIDI channel event helper. void sendEvent(qtractorMidiEvent::EventType etype, unsigned short iChannel, unsigned short iParam, unsigned short iValue) const; // Direct MIDI note helper. void sendNote(qtractorTrack *pTrack, int iNote, int iVelocity, bool bForce) const; // Direct SysEx helpers. void sendSysex(unsigned char *pSysex, unsigned int iSysex) const; void sendSysexList() const; // Import/export SysEx setup from/into event sequence. bool importSysexList(qtractorMidiSequence *pSeq); bool exportSysexList(qtractorMidiSequence *pSeq); // Virtual I/O bus-monitor accessors. qtractorMonitor *monitor_in() const; qtractorMonitor *monitor_out() const; // MIDI I/O bus-monitor accessors. qtractorMidiMonitor *midiMonitor_in() const; qtractorMidiMonitor *midiMonitor_out() const; // Plugin-chain accessors. qtractorPluginList *pluginList_in() const; qtractorPluginList *pluginList_out() const; // Retrieve/restore client:port connections. // return the effective number of connection attempts. int updateConnects(BusMode busMode, ConnectList& connects, bool bConnect = false) const; // MIDI master volume. void setMasterVolume(float fVolume); // MIDI master panning (balance). void setMasterPanning(float fPanning); // MIDI channel volume. void setVolume(qtractorTrack *pTrack, float fVolume); // MIDI channel stereo panning. void setPanning(qtractorTrack *pTrack, float fPanning); // Document element methods. bool loadElement(qtractorDocument *pDocument, QDomElement *pElement); bool saveElement(qtractorDocument *pDocument, QDomElement *pElement) const; // Pending note-off processing methods. void enqueueNoteOff(snd_seq_event_t *pEv, unsigned long iTimeOn, unsigned long iTimeOff); void dequeueNoteOffs(unsigned long iQueueTime); // Update all aux-sends to this very bus... void updateMidiAuxSends(const QString& sMidiBusName); protected: // Direct MIDI controller common helper. void setControllerEx(unsigned short iChannel, int iController, int iValue = 0, qtractorTrack *pTrack = nullptr) const; // Bus mode/name change events. void updateBusMode(); void updateBusName(); // Create plugin-list properly. qtractorPluginList *createPluginList(int iFlags) const; // Set plugin-list title name properly. void updatePluginListName( qtractorPluginList *pPluginList, int iFlags) const; // Set plugin-list buffers properly. void updatePluginList(qtractorPluginList *pPluginList, int iFlags); // Document instrument map methods. bool loadMidiMap(qtractorDocument *pDocument, QDomElement *pElement); bool saveMidiMap(qtractorDocument *pDocument, QDomElement *pElement) const; // Document SysEx setup list methods. bool loadSysexList(qtractorDocument *pDocument, QDomElement *pElement); bool saveSysexList(qtractorDocument *pDocument, QDomElement *pElement) const; private: // Instance variables. int m_iAlsaPort; // Specific monitor instances. qtractorMidiMonitor *m_pIMidiMonitor; qtractorMidiMonitor *m_pOMidiMonitor; // Plugin-chain instances. qtractorPluginList *m_pIPluginList; qtractorPluginList *m_pOPluginList; // SysEx setup list. qtractorMidiSysexList *m_pSysexList; // Default instrument name. QString m_sInstrumentName; // Channel patch mapper. QHash m_patches; // Pending note-off processing data structure. // struct NoteOff { NoteOff(unsigned long on, unsigned long off) : time_on(on), time_off(off) {} NoteOff(const NoteOff& other) : time_on(other.time_on), time_off(other.time_off) {} unsigned long time_on; unsigned long time_off; }; typedef QHash NoteOffs; NoteOffs m_noteOffs; }; #endif // __qtractorMidiEngine_h // end of qtractorMidiEngine.h qtractor-1.5.9/src/PaxHeaders/qtractorSpinBox.cpp0000644000000000000000000000013215101070305017070 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorSpinBox.cpp0000644000175000001440000005277215101070305017075 0ustar00rncbcusers// qtractorSpinBox.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 "qtractorSpinBox.h" #include #include #if 1//QTRACTOR_TEMPO_SPINBOX_LOCALE #include #endif #include #include #include //------------------------------------------------------------------------- // qtractorSpinBox - A better QDoubleSpinBox widget. qtractorSpinBox::EditMode qtractorSpinBox::g_editMode = qtractorSpinBox::DefaultMode; // Set spin-box edit mode behavior. void qtractorSpinBox::setEditMode ( EditMode editMode ) { g_editMode = editMode; } qtractorSpinBox::EditMode qtractorSpinBox::editMode (void) { return g_editMode; } // Constructor. qtractorSpinBox::qtractorSpinBox ( QWidget *pParent ) : QDoubleSpinBox(pParent), m_iTextChanged(0) { QObject::connect(QDoubleSpinBox::lineEdit(), SIGNAL(textChanged(const QString&)), SLOT(lineEditTextChanged(const QString&))); QObject::connect(this, SIGNAL(editingFinished()), SLOT(spinBoxEditingFinished())); QObject::connect(this, SIGNAL(valueChanged(double)), SLOT(spinBoxValueChanged(double))); } // Alternate value change behavior handlers. void qtractorSpinBox::lineEditTextChanged ( const QString& ) { if (g_editMode == DeferredMode) ++m_iTextChanged; } void qtractorSpinBox::spinBoxEditingFinished (void) { if (g_editMode == DeferredMode) { m_iTextChanged = 0; emit valueChangedEx(QDoubleSpinBox::value()); } } void qtractorSpinBox::spinBoxValueChanged ( double value ) { if (g_editMode != DeferredMode || m_iTextChanged == 0) emit valueChangedEx(value); } // Inherited/override methods. QValidator::State qtractorSpinBox::validate ( QString& sText, int& iPos ) const { const QValidator::State state = QDoubleSpinBox::validate(sText, iPos); if (state == QValidator::Acceptable && g_editMode == DeferredMode && m_iTextChanged == 0) return QValidator::Intermediate; return state; } //---------------------------------------------------------------------------- // qtractorTimeSpinBox -- A time-scale formatted spin-box widget. // Constructor. qtractorTimeSpinBox::qtractorTimeSpinBox ( QWidget *pParent ) : QAbstractSpinBox(pParent), m_pTimeScale(nullptr), m_displayFormat(qtractorTimeScale::Frames), m_iValue(0), m_iDefaultValue(0), m_iMinimumValue(0), m_iMaximumValue(0), m_iDeltaValue(0), m_bDeltaValue(false), m_iValueChanged(0) { QAbstractSpinBox::setAccelerated(true); QObject::connect(this, SIGNAL(editingFinished()), SLOT(editingFinishedSlot())); QObject::connect(QAbstractSpinBox::lineEdit(), SIGNAL(textChanged(const QString&)), SLOT(valueChangedSlot(const QString&))); } // Mark that we got actual value. void qtractorTimeSpinBox::showEvent ( QShowEvent */*pShowEvent*/ ) { QLineEdit *pLineEdit = QAbstractSpinBox::lineEdit(); const bool bBlockSignals = pLineEdit->blockSignals(true); pLineEdit->setText(textFromValue(m_iValue)); QAbstractSpinBox::interpretText(); pLineEdit->blockSignals(bBlockSignals); } // Time-scale accessors. void qtractorTimeSpinBox::setTimeScale ( qtractorTimeScale *pTimeScale ) { m_pTimeScale = pTimeScale; setDisplayFormat(m_pTimeScale ? m_pTimeScale->displayFormat() : qtractorTimeScale::Frames); } qtractorTimeScale *qtractorTimeSpinBox::timeScale (void) const { return m_pTimeScale; } // Display-format accessors. void qtractorTimeSpinBox::setDisplayFormat ( qtractorTimeScale::DisplayFormat displayFormat ) { m_displayFormat = displayFormat; updateDisplayFormat(); } qtractorTimeScale::DisplayFormat qtractorTimeSpinBox::displayFormat (void) const { return m_displayFormat; } void qtractorTimeSpinBox::updateDisplayFormat (void) { updateText(); } // Nominal value (in frames) accessors. void qtractorTimeSpinBox::setValue ( unsigned long iValue, bool bNotifyChange ) { if (updateValue(iValue, bNotifyChange)) updateText(); m_iDefaultValue = m_iValue; } unsigned long qtractorTimeSpinBox::value (void) const { return m_iValue; } // Minimum value (in frames) accessors. void qtractorTimeSpinBox::setMinimum ( unsigned long iMinimum ) { m_iMinimumValue = iMinimum; } unsigned long qtractorTimeSpinBox::minimum (void) const { return m_iMinimumValue; } // Maximum value (in frames) accessors. void qtractorTimeSpinBox::setMaximum ( unsigned long iMaximum ) { m_iMaximumValue = iMaximum; } unsigned long qtractorTimeSpinBox::maximum (void) const { return m_iMaximumValue; } // Differential value mode (BBT format only) accessor. void qtractorTimeSpinBox::setDeltaValue ( bool bDeltaValue, unsigned long iDeltaValue ) { m_bDeltaValue = bDeltaValue; m_iDeltaValue = iDeltaValue; } bool qtractorTimeSpinBox::isDeltaValue (void) const { return m_bDeltaValue; } unsigned long qtractorTimeSpinBox::deltaValue (void) const { return m_iDeltaValue; } // Inherited/override methods. QValidator::State qtractorTimeSpinBox::validate ( QString& sText, int& iPos ) const { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTimeSpinBox[%p]::validate(\"%s\",%d)", this, sText.toUtf8().constData(), iPos); #endif if (iPos == 0) return QValidator::Acceptable; const QChar& ch = sText[iPos - 1]; switch (m_displayFormat) { case qtractorTimeScale::Time: if (ch == ':') return QValidator::Acceptable; // Fall thru. case qtractorTimeScale::BBT: if (ch == '.') return QValidator::Acceptable; // Fall thru. case qtractorTimeScale::Frames: default: if (ch.isDigit()) return QValidator::Acceptable; break; } return QValidator::Invalid; } void qtractorTimeSpinBox::fixup ( QString& sText ) const { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTimeSpinBox[%p]::fixup(\"%s\")", this, sText.toUtf8().constData()); #endif sText = textFromValue(m_iValue); } void qtractorTimeSpinBox::stepBy ( int iSteps ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTimeSpinBox[%p]::stepBy(%d)", this, iSteps); #endif QLineEdit *pLineEdit = QAbstractSpinBox::lineEdit(); const int iCursorPos = pLineEdit->cursorPosition(); long iValue = long(value()); if (m_pTimeScale) { switch (m_displayFormat) { case qtractorTimeScale::BBT: { qtractorTimeScale::Cursor cursor(m_pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(iValue); unsigned long iFrame = pNode->frame; const QString& sText = pLineEdit->text(); const int iPos = sText.section('.', 0, 0).length() + 1; if (iCursorPos < iPos) iFrame = pNode->frameFromBar(pNode->bar + 1); else if (iCursorPos < iPos + sText.section('.', 1, 1).length() + 1) iFrame = pNode->frameFromBeat(pNode->beat + 1); else iFrame = pNode->frameFromTick(pNode->tick + 1); iSteps *= int(iFrame - pNode->frame); break; } case qtractorTimeScale::Time: { const QString& sText = pLineEdit->text(); const int iPos = sText.section(':', 0, 0).length() + 1; if (iCursorPos < iPos) iSteps *= int(3600 * m_pTimeScale->sampleRate()); else if (iCursorPos < iPos + sText.section(':', 1, 1).length() + 1) iSteps *= int(60 * m_pTimeScale->sampleRate()); else if (iCursorPos < sText.section('.', 0, 0).length() + 1) iSteps *= int(m_pTimeScale->sampleRate()); else iSteps *= int(m_pTimeScale->sampleRate() / 1000); break; } case qtractorTimeScale::Frames: default: break; } } iValue += iSteps; if (iValue < 0) iValue = 0; setValue(iValue); pLineEdit->setCursorPosition(iCursorPos); } QAbstractSpinBox::StepEnabled qtractorTimeSpinBox::stepEnabled (void) const { StepEnabled flags = StepUpEnabled; if (value() > 0) flags |= StepDownEnabled; return flags; } // Value/text format converters. unsigned long qtractorTimeSpinBox::valueFromText (void) const { return valueFromText(QAbstractSpinBox::text()); } unsigned long qtractorTimeSpinBox::valueFromText ( const QString& sText ) const { if (m_pTimeScale == nullptr) return sText.toULong(); return m_pTimeScale->frameFromTextEx( m_displayFormat, sText, m_bDeltaValue, m_iDeltaValue); } QString qtractorTimeSpinBox::textFromValue ( unsigned long iValue ) const { if (m_pTimeScale == nullptr) return QString::number(iValue); if (m_bDeltaValue) { return m_pTimeScale->textFromFrameEx( m_displayFormat, m_iDeltaValue, true, iValue); } else { return m_pTimeScale->textFromFrameEx( m_displayFormat, iValue); } } // Common value setler. bool qtractorTimeSpinBox::updateValue ( unsigned long iValue, bool bNotifyChange ) { if (iValue < m_iMinimumValue) iValue = m_iMinimumValue; if (iValue > m_iMaximumValue && m_iMaximumValue > m_iMinimumValue) iValue = m_iMaximumValue; if (m_iValue != iValue) { m_iValue = iValue; ++m_iValueChanged; } const int iValueChanged = m_iValueChanged; if (bNotifyChange && m_iValueChanged > 0) { emit valueChanged(m_iValue); m_iValueChanged = 0; } return (iValueChanged > 0); } void qtractorTimeSpinBox::updateText (void) { if (QAbstractSpinBox::isVisible()) { QLineEdit *pLineEdit = QAbstractSpinBox::lineEdit(); const bool bBlockSignals = pLineEdit->blockSignals(true); const int iCursorPos = pLineEdit->cursorPosition(); pLineEdit->setText(textFromValue(m_iValue)); // QAbstractSpinBox::interpretText(); pLineEdit->setCursorPosition(iCursorPos); pLineEdit->blockSignals(bBlockSignals); } } // Local context menu handler. void qtractorTimeSpinBox::contextMenuEvent ( QContextMenuEvent *pContextMenuEvent ) { // QAbstractSpinBox::contextMenuEvent(pContextMenuEvent); if (m_pTimeScale == nullptr) return; const bool bBlockSignals = QAbstractSpinBox::blockSignals(true); QMenu menu(this); QAction *pAction; pAction = menu.addAction(tr("&Frames")); pAction->setCheckable(true); pAction->setChecked(m_displayFormat == qtractorTimeScale::Frames); pAction->setData(int(qtractorTimeScale::Frames)); pAction = menu.addAction(tr("&Time")); pAction->setCheckable(true); pAction->setChecked(m_displayFormat == qtractorTimeScale::Time); pAction->setData(int(qtractorTimeScale::Time)); pAction = menu.addAction(tr("&BBT")); pAction->setCheckable(true); pAction->setChecked(m_displayFormat == qtractorTimeScale::BBT); pAction->setData(int(qtractorTimeScale::BBT)); pAction = menu.exec(pContextMenuEvent->globalPos()); QAbstractSpinBox::blockSignals(bBlockSignals); if (pAction == nullptr) return; const qtractorTimeScale::DisplayFormat displayFormat = qtractorTimeScale::DisplayFormat(pAction->data().toInt()); if (displayFormat != m_displayFormat) { setDisplayFormat(displayFormat); emit displayFormatChanged(int(displayFormat)); } } // Keyboard event handler. void qtractorTimeSpinBox::keyPressEvent ( QKeyEvent *pKeyEvent ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTimeSpinBox::keyPressEvent(%d)", pKeyEvent->key()); #endif if (pKeyEvent->key() == Qt::Key_Escape) { // Rephrase text display... if (m_iValueChanged > 0) { m_iValueChanged = 0; m_iValue = m_iDefaultValue; updateText(); } // Finish editing... //QAbstractSpinBox::clearFocus(); } QAbstractSpinBox::keyPressEvent(pKeyEvent); } // Pseudo-fixup slot. void qtractorTimeSpinBox::editingFinishedSlot (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTimeSpinBox[%p]::editingFinishedSlot()", this); #endif if (m_iValueChanged > 0) { // Kind of final fixup. if (updateValue(valueFromText(), true)) { // Rephrase text display... updateText(); } // Reset default value... m_iDefaultValue = m_iValue; } } // Textual value change notification. void qtractorTimeSpinBox::valueChangedSlot ( const QString& sText ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTimeSpinBox[%p]::valueChangedSlot(\"%s\")", this, sText.toUtf8().constData()); #endif // Kind of interim fixup. if (updateValue(valueFromText(sText), false)) { // Just forward this one... emit valueChanged(sText); } } //---------------------------------------------------------------------------- // qtractorTempoSpinBox -- A time-scale formatted spin-box widget. // Constructor. qtractorTempoSpinBox::qtractorTempoSpinBox ( QWidget *pParent ) : QAbstractSpinBox(pParent), m_fTempo(120.0f), m_iBeatsPerBar(4), m_iBeatDivisor(2), m_fDefaultTempo(120.0f), m_iDefaultBeatsPerBar(4), m_iDefaultBeatDivisor(2), m_iValueChanged(0) { #if 1//QTRACTOR_TEMPO_SPINBOX_LOCALE #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) m_decp = QLocale().decimalPoint().at(0); #else m_decp = QLocale().decimalPoint(); #endif #else m_decp = '.'; #endif QAbstractSpinBox::setAccelerated(true); QObject::connect(this, SIGNAL(editingFinished()), SLOT(editingFinishedSlot())); QObject::connect(QAbstractSpinBox::lineEdit(), SIGNAL(textChanged(const QString&)), SLOT(valueChangedSlot(const QString&))); } // Mark that we got actual value. void qtractorTempoSpinBox::showEvent ( QShowEvent */*pShowEvent*/ ) { QLineEdit *pLineEdit = QAbstractSpinBox::lineEdit(); const bool bBlockSignals = pLineEdit->blockSignals(true); pLineEdit->setText(textFromValue(m_fTempo, m_iBeatsPerBar, m_iBeatDivisor)); pLineEdit->blockSignals(bBlockSignals); // QAbstractSpinBox::interpretText(); } // Nominal tempo value (BPM) accessors. void qtractorTempoSpinBox::setTempo ( float fTempo, bool bNotifyChange ) { if (updateValue(fTempo, m_iBeatsPerBar, m_iBeatDivisor, bNotifyChange)) updateText(); m_fDefaultTempo = m_fTempo; } float qtractorTempoSpinBox::tempo (void) const { return m_fTempo; } // Nominal time-signature numerator (beats/bar) accessors. void qtractorTempoSpinBox::setBeatsPerBar ( unsigned short iBeatsPerBar, bool bNotifyChange ) { if (updateValue(m_fTempo, iBeatsPerBar, m_iBeatDivisor, bNotifyChange)) updateText(); m_iDefaultBeatsPerBar = m_iBeatsPerBar; } unsigned short qtractorTempoSpinBox::beatsPerBar (void) const { return m_iBeatsPerBar; } // Nominal time-signature denominator (beat-divisor) accessors. void qtractorTempoSpinBox::setBeatDivisor ( unsigned short iBeatDivisor, bool bNotifyChange ) { if (updateValue(m_fTempo, m_iBeatsPerBar, iBeatDivisor, bNotifyChange)) updateText(); m_iDefaultBeatDivisor = m_iBeatDivisor; } unsigned short qtractorTempoSpinBox::beatDivisor (void) const { return m_iBeatDivisor; } // Common value setler. bool qtractorTempoSpinBox::updateValue ( float fTempo, unsigned short iBeatsPerBar, unsigned short iBeatDivisor, bool bNotifyChange ) { if (fTempo < 1.0f) fTempo = 1.0f; if (fTempo > 1000.0f) fTempo = 1000.0f; if (iBeatsPerBar < 2) iBeatsPerBar = 2; if (iBeatsPerBar > 128) iBeatsPerBar = 128; if (iBeatDivisor < 1) iBeatDivisor = 1; if (iBeatDivisor > 8) iBeatDivisor = 8; if (qAbs(m_fTempo - fTempo) > 0.001f) { m_fTempo = 0.01f * ::roundf(100.0f * fTempo); ++m_iValueChanged; } if (m_iBeatsPerBar != iBeatsPerBar) { m_iBeatsPerBar = iBeatsPerBar; ++m_iValueChanged; } if (m_iBeatDivisor != iBeatDivisor) { m_iBeatDivisor = iBeatDivisor; ++m_iValueChanged; } const int iValueChanged = m_iValueChanged; if (bNotifyChange && m_iValueChanged > 0) { emit valueChanged(m_fTempo, m_iBeatsPerBar, m_iBeatDivisor); m_iValueChanged = 0; } return (iValueChanged > 0); } void qtractorTempoSpinBox::updateText (void) { if (QAbstractSpinBox::isVisible()) { QLineEdit *pLineEdit = QAbstractSpinBox::lineEdit(); const bool bBlockSignals = pLineEdit->blockSignals(true); const int iCursorPos = pLineEdit->cursorPosition(); pLineEdit->setText(textFromValue( m_fTempo, m_iBeatsPerBar, m_iBeatDivisor)); // QAbstractSpinBox::interpretText(); pLineEdit->setCursorPosition(iCursorPos); pLineEdit->blockSignals(bBlockSignals); } } // Inherited/override methods. QValidator::State qtractorTempoSpinBox::validate ( QString& sText, int& iPos ) const { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTempoSpinBox[%p]::validate(\"%s\",%d)", this, sText.toUtf8().constData(), iPos); #endif if (iPos == 0) return QValidator::Acceptable; const QChar& ch = sText.at(iPos - 1); if (ch == m_decp || ch == '/' || ch == ' ' || ch.isDigit()) return QValidator::Acceptable; else return QValidator::Invalid; } void qtractorTempoSpinBox::fixup ( QString& sText ) const { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTempoSpinBox[%p]::fixup(\"%s\")", this, sText.toUtf8().constData()); #endif sText = textFromValue(m_fTempo, m_iBeatsPerBar, m_iBeatDivisor); } void qtractorTempoSpinBox::stepBy ( int iSteps ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTempoSpinBox[%p]::stepBy(%d)", this, iSteps); #endif QLineEdit *pLineEdit = QAbstractSpinBox::lineEdit(); const int iCursorPos = pLineEdit->cursorPosition(); const QString& sText = pLineEdit->text(); const int iLength = sText.indexOf(' '); if (iCursorPos < iLength + 1) { float fStep = 1.0f; const int iDecimalPos = sText.indexOf(m_decp); if (iDecimalPos >= 0 && iDecimalPos < iCursorPos) { int i = iCursorPos - iDecimalPos; while (--i > 0) fStep *= 0.1f; } setTempo(tempo() + fStep * float(iSteps)); pLineEdit->setCursorPosition(iCursorPos + pLineEdit->text().indexOf(' ') - iLength); } else if (iCursorPos > sText.indexOf('/')) setBeatDivisor(int(beatDivisor()) + iSteps); else setBeatsPerBar(int(beatsPerBar()) + iSteps); } QAbstractSpinBox::StepEnabled qtractorTempoSpinBox::stepEnabled (void) const { StepEnabled flags = StepNone; #if 0 const float fTempo = tempo(); const unsigned short iBeatsPerBar = beatsPerBar(); const unsigned short iBeatDivisor = beatDivisor(); if (fTempo > 1.0f && iBeatsPerBar > 2 && iBeatDivisor > 1) flags |= StepDownEnabled; if (fTempo < 1000.0f && iBeatsPerBar < 128 && iBeatDivisor < 8) flags |= StepUpEnabled; #else QLineEdit *pLineEdit = QAbstractSpinBox::lineEdit(); const int iCursorPos = pLineEdit->cursorPosition(); const QString& sText = pLineEdit->text(); const int iLength = sText.indexOf(' '); if (iCursorPos < iLength + 1) { const float fTempo = tempo(); if (fTempo > 1.0f) flags |= StepDownEnabled; if (fTempo < 1000.0f) flags |= StepUpEnabled; } else if (iCursorPos > sText.indexOf('/')) { const unsigned short iBeatDivisor = beatDivisor(); if (iBeatDivisor > 1) flags |= StepDownEnabled; if (iBeatDivisor < 8) flags |= StepUpEnabled; } else { const unsigned short iBeatsPerBar = beatsPerBar(); if (iBeatsPerBar > 2) flags |= StepDownEnabled; if (iBeatsPerBar < 128) flags |= StepUpEnabled; } #endif return flags; } // Value/text format converters. float qtractorTempoSpinBox::tempoFromText ( const QString& sText ) const { #if 1//QTRACTOR_TEMPO_SPINBOX_LOCALE bool ok = false; const QString& sTempo = sText.section(' ', 0, 0); float fTempo = QLocale().toFloat(sTempo, &ok); if (!ok) fTempo = sText.toFloat(); #else const float fTempo = sText.section(' ', 0, 0).toFloat(); #endif return (fTempo >= 1.0f ? fTempo : m_fTempo); } unsigned short qtractorTempoSpinBox::beatsPerBarFromText ( const QString& sText) const { const unsigned short iBeatsPerBar = sText.section(' ', 1, 1).section('/', 0, 0).toUShort(); return (iBeatsPerBar >= 2 ? iBeatsPerBar : m_iBeatsPerBar); } unsigned short qtractorTempoSpinBox::beatDivisorFromText ( const QString& sText) const { unsigned short iBeatDivisor = 0; unsigned short i = sText.section(' ', 1, 1).section('/', 1, 1).toUShort(); while (i > 1) { ++iBeatDivisor; i >>= 1; } return (iBeatDivisor >= 1 ? iBeatDivisor : m_iBeatDivisor); } QString qtractorTempoSpinBox::textFromValue ( float fTempo, unsigned short iBeatsPerBar, unsigned short iBeatDivisor) const { return QString("%1 %2/%3") #if 1//QTRACTOR_TEMPO_SPINBOX_LOCALE .arg(QLocale().toString(fTempo)) #else .arg(fTempo) #endif .arg(iBeatsPerBar) .arg(1 << iBeatDivisor); } // Textual value change notification. void qtractorTempoSpinBox::valueChangedSlot ( const QString& sText ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTempoSpinBox[%p]::valueChangedSlot(\"%s\")", this, sText.toUtf8().constData()); #endif // Kind of interim fixup. if (updateValue(tempoFromText(sText), beatsPerBarFromText(sText), beatDivisorFromText(sText), false)) { // Just forward this one... emit valueChanged(sText); } } // Keyboard event handler. void qtractorTempoSpinBox::keyPressEvent ( QKeyEvent *pKeyEvent ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTempoSpinBox::keyPressEvent(%d)", pKeyEvent->key()); #endif if (pKeyEvent->key() == Qt::Key_Escape) { // Rephrase text display... if (m_iValueChanged > 0) { m_iValueChanged = 0; m_fTempo = m_fDefaultTempo; m_iBeatsPerBar = m_iDefaultBeatsPerBar; m_iBeatDivisor = m_iDefaultBeatDivisor; updateText(); } // Finish editing? //QAbstractSpinBox::clearFocus(); } QAbstractSpinBox::keyPressEvent(pKeyEvent); } // Final pseudo-fixup slot. void qtractorTempoSpinBox::editingFinishedSlot (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorTempoSpinBox[%p]::editingFinishedSlot()", this); #endif if (m_iValueChanged > 0) { // Kind of final fixup. const QString& sText = QAbstractSpinBox::text(); if (updateValue(tempoFromText(sText), beatsPerBarFromText(sText), beatDivisorFromText(sText), true)) { // Rephrase text display... updateText(); } } } // end of qtractorSpinBox.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMidiToolsForm.cpp0000644000000000000000000000013215101070305020235 xustar0030 mtime=1761898693.084267645 30 atime=1761898693.084267645 30 ctime=1761898693.084267645 qtractor-1.5.9/src/qtractorMidiToolsForm.cpp0000644000175000001440000016151615101070305020237 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.9/src/PaxHeaders/qtractorEditRangeForm.ui0000644000000000000000000000013215101070305020027 xustar0030 mtime=1761898693.070267601 30 atime=1761898693.070267601 30 ctime=1761898693.070267601 qtractor-1.5.9/src/qtractorEditRangeForm.ui0000644000175000001440000002364515101070305020031 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.9/src/PaxHeaders/qtractorMidiMeter.cpp0000644000000000000000000000013215101070305017365 xustar0030 mtime=1761898693.083267642 30 atime=1761898693.083267642 30 ctime=1761898693.083267642 qtractor-1.5.9/src/qtractorMidiMeter.cpp0000644000175000001440000004122315101070305017357 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.9/src/PaxHeaders/qtractorMidiTimer.cpp0000644000000000000000000000013215101070305017371 xustar0030 mtime=1761898693.084267645 30 atime=1761898693.084267645 30 ctime=1761898693.084267645 qtractor-1.5.9/src/qtractorMidiTimer.cpp0000644000175000001440000001072315101070305017364 0ustar00rncbcusers// qtractorMidiTimer.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 "qtractorMidiTimer.h" #include #include #include //---------------------------------------------------------------------- // class qtractorMidiTimer::Key -- ALSA sequencer timer key stuff. // // Setters. void qtractorMidiTimer::Key::setAlsaTimer ( int iAlsaTimer ) { if (iAlsaTimer == 0) iAlsaTimer = ((SND_TIMER_CLASS_GLOBAL & 0x7f) << 24); m_iAlsaTimer = iAlsaTimer; } void qtractorMidiTimer::Key::setAlsaTimer ( int iClass, int iCard, int iDevice, int iSubDev ) { if (iClass < 0 || iClass == SND_TIMER_CLASS_NONE) iClass = SND_TIMER_CLASS_GLOBAL; setAlsaTimer((unsigned long) ((iClass & 0x7f) << 24) | ((iCard & 0x7f) << 16) | ((iDevice & 0x7f) << 8) | (iSubDev & 0x7f)); } //---------------------------------------------------------------------- // class qtractorMidiTimer -- ALSA sequencer timer stuff (singleton). // // Constructor. qtractorMidiTimer::qtractorMidiTimer (void) { m_keys.clear(); m_names.clear(); m_keys.append(0); m_names.append(QObject::tr("(default)")); snd_timer_query_t *pTimerQuery = nullptr; if (snd_timer_query_open(&pTimerQuery, "hw", 0) >= 0) { snd_timer_id_t *pTimerID = nullptr; snd_timer_id_alloca(&pTimerID); snd_timer_id_set_class(pTimerID, SND_TIMER_CLASS_NONE); while (snd_timer_query_next_device(pTimerQuery, pTimerID) >= 0) { const int iClass = snd_timer_id_get_class(pTimerID); if (iClass < 0) break; int iSClass = snd_timer_id_get_sclass(pTimerID); if (iSClass < 0) iSClass = 0; int iCard = snd_timer_id_get_card(pTimerID); if (iCard < 0) iCard = 0; int iDevice = snd_timer_id_get_device(pTimerID); if (iDevice < 0) iDevice = 0; int iSubDev = snd_timer_id_get_subdevice(pTimerID); if (iSubDev < 0) iSubDev = 0; char szTimer[88]; snprintf(szTimer, sizeof(szTimer) - 1, "hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i", iClass, iSClass, iCard, iDevice, iSubDev); snd_timer_t *pTimer = nullptr; if (snd_timer_open(&pTimer, szTimer, SND_TIMER_OPEN_NONBLOCK) >= 0) { snd_timer_info_t *pTimerInfo; snd_timer_info_alloca(&pTimerInfo); if (snd_timer_info(pTimer, pTimerInfo) >= 0) { qtractorMidiTimer::Key key(iClass, iCard, iDevice, iSubDev); const long iResol = snd_timer_info_get_resolution(pTimerInfo); QString sTimerName = QString::fromUtf8( snd_timer_info_get_name(pTimerInfo)); QString sTimerText; if (!snd_timer_info_is_slave(pTimerInfo) && iResol > 0) sTimerText = QObject::tr("%1 Hz").arg(1000000000L / iResol); else sTimerText = QObject::tr("slave"); m_keys.append(key.alsaTimer()); m_names.append(QObject::tr("%1 (%2)") .arg(sTimerName).arg(sTimerText)); } snd_timer_close(pTimer); } } snd_timer_query_close(pTimerQuery); } } // Destructor. qtractorMidiTimer::~qtractorMidiTimer (void) { m_names.clear(); m_keys.clear(); } // Returns the number of available timers. int qtractorMidiTimer::count (void) const { return m_keys.count(); } // Index of the a timer key. int qtractorMidiTimer::indexOf ( int iKey ) const { int iIndex = m_keys.indexOf(iKey); if (iIndex < 0) iIndex = 0; return iIndex; } // Key from index. int qtractorMidiTimer::key ( int iIndex ) const { if (iIndex < 0 || iIndex >= m_names.count()) iIndex = 0; return m_keys.at(iIndex); } // Name from index. const QString& qtractorMidiTimer::name ( int iIndex ) const { if (iIndex < 0 || iIndex >= m_names.count()) iIndex = 0; return m_names.at(iIndex); } // end of qtractorMidiTimer.cpp qtractor-1.5.9/src/PaxHeaders/qtractorDssiPlugin.cpp0000644000000000000000000000013215101070305017567 xustar0030 mtime=1761898693.069267597 30 atime=1761898693.069267597 30 ctime=1761898693.069267597 qtractor-1.5.9/src/qtractorDssiPlugin.cpp0000644000175000001440000010520215101070305017557 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.9/src/PaxHeaders/qtractorAudioMadFile.h0000644000000000000000000000013215101070305017436 xustar0030 mtime=1761898693.064267582 30 atime=1761898693.064267582 30 ctime=1761898693.064267582 qtractor-1.5.9/src/qtractorAudioMadFile.h0000644000175000001440000000717615101070305017441 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.9/src/PaxHeaders/qtractorTakeRangeForm.ui0000644000000000000000000000013215101070305020026 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorTakeRangeForm.ui0000644000175000001440000001775315101070305020033 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. qtractorTakeRangeForm 0 0 360 260 Qt::StrongFocus Take Range :/images/qtractor.svg Range En&d: TakeEndSpinBox Selection range &Selection false Qt::Horizontal QSizePolicy::Fixed 14 14 Edit range &Edit false Custom range &Custom true Loop range &Loop false 120 0 Clip start 120 0 Clip offset St&art: TakeStartSpinBox Punch range &Punch false Select 0 0 100 100 Current take Format Time display format Frames Time BBT Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok qtractorTimeSpinBox QSpinBox
qtractorSpinBox.h
SelectionRangeRadioButton LoopRangeRadioButton PunchRangeRadioButton EditRangeRadioButton CustomRangeRadioButton TakeStartSpinBox TakeEndSpinBox CurrentTakeListBox FormatComboBox
qtractor-1.5.9/src/PaxHeaders/qtractorSessionForm.h0000644000000000000000000000013215101070305017422 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.088267658 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorSessionForm.h0000644000175000001440000000413115101070305017411 0ustar00rncbcusers// qtractorSessionForm.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 __qtractorSessionForm_h #define __qtractorSessionForm_h #include "ui_qtractorSessionForm.h" #include "qtractorSession.h" //---------------------------------------------------------------------------- // qtractorSessionForm -- UI wrapper form. class qtractorSessionForm : public QDialog { Q_OBJECT public: // Constructor. qtractorSessionForm(QWidget *pParent = nullptr); // Destructor. ~qtractorSessionForm(); void setSession(qtractorSession *pSession, bool bSessionDir); const qtractorSession::Properties& properties(); bool isSessionDirEnabled() const; protected slots: void accept(); void reject(); void changed(); void changeSessionName(const QString& sSessionName); void changeAutoSessionDir(bool bOn); void changeSessionDir(const QString& sSessionDir); void browseSessionDir(); protected: void stabilizeForm(); private: // The Qt-designer UI struct... Ui::qtractorSessionForm m_ui; // Instance variables... qtractorSession::Properties m_props; bool m_bNewSession; bool m_bSessionDir; QString m_sSessionDir; int m_iDirtyCount; }; #endif // __qtractorSessionForm_h // end of qtractorSessionForm.h qtractor-1.5.9/src/PaxHeaders/instruments0000644000000000000000000000013215101070305015540 xustar0030 mtime=1761898693.060267569 30 atime=1761898693.060267569 30 ctime=1761898693.060267569 qtractor-1.5.9/src/instruments/0000755000175000001440000000000015101070305015605 5ustar00rncbcusersqtractor-1.5.9/src/instruments/PaxHeaders/Standard1.ins0000644000000000000000000000013215101070305020151 xustar0030 mtime=1761898693.060267569 30 atime=1761898693.060267569 30 ctime=1761898693.060267569 qtractor-1.5.9/src/instruments/Standard1.ins0000644000175000001440000021150515101070305020145 0ustar00rncbcusers; ----------------------------------------------------------------------------- ; Filename: Standard1.ins ; ; This is a sample Cakewalk instrument file. ; ; It contains definitions for the General MIDI (GM), Roland GS ; and Yamaha XG MIDI instrument standards. ; ; 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. ; ; ----------------------------------------------------------------------------- .Patch Names [0..127] [1..128] [General MIDI] 0=Acoustic Grand Piano 1=Bright Acoustic Piano 2=Electric Grand Piano 3=Honky-tonk Piano 4=Rhodes Piano 5=Chorused Piano 6=Harpsichord 7=Clavinet 8=Celesta 9=Glockenspiel 10=Music Box 11=Vibraphone 12=Marimba 13=Xylophone 14=Tubular Bells 15=Dulcimer 16=Hammond Organ 17=Percussive Organ 18=Rock Organ 19=Church Organ 20=Reed Organ 21=Accordion 22=Harmonica 23=Tango Accordion 24=Acoustic Guitar (nylon) 25=Acoustic Guitar (steel) 26=Electric Guitar (jazz) 27=Electric Guitar (clean) 28=Electric Guitar (muted) 29=Overdriven Guitar 30=Distortion Guitar 31=Guitar Harmonics 32=Acoustic Bass 33=Electric Bass (finger) 34=Electric Bass (pick) 35=Fretless Bass 36=Slap Bass 1 37=Slap Bass 2 38=Synth Bass 1 39=Synth Bass 2 40=Violin 41=Viola 42=Cello 43=Contrabass 44=Tremolo Strings 45=Pizzicato Strings 46=Orchestral Harp 47=Timpani 48=String Ensemble 1 49=String Ensemble 2 50=SynthStrings 1 51=SynthStrings 2 52=Choir Aahs 53=Voice Oohs 54=Synth Voice 55=Orchestra Hit 56=Trumpet 57=Trombone 58=Tuba 59=Muted Trumpet 60=French Horn 61=Brass Section 62=Synth Brass 1 63=Synth Brass 2 64=Soprano Sax 65=Alto Sax 66=Tenor Sax 67=Baritone Sax 68=Oboe 69=English Horn 70=Bassoon 71=Clarinet 72=Piccolo 73=Flute 74=Recorder 75=Pan Flute 76=Bottle Blow 77=Shakuhachi 78=Whistle 79=Ocarina 80=Lead 1 (square) 81=Lead 2 (sawtooth) 82=Lead 3 (calliope lead) 83=Lead 4 (chiff lead) 84=Lead 5 (charang) 85=Lead 6 (voice) 86=Lead 7 (fifths) 87=Lead 8 (bass + lead) 88=Pad 1 (new age) 89=Pad 2 (warm) 90=Pad 3 (polysynth) 91=Pad 4 (choir) 92=Pad 5 (bowed) 93=Pad 6 (metallic) 94=Pad 7 (halo) 95=Pad 8 (sweep) 96=FX 1 (rain) 97=FX 2 (soundtrack) 98=FX 3 (crystal) 99=FX 4 (atmosphere) 100=FX 5 (brightness) 101=FX 6 (goblins) 102=FX 7 (echoes) 103=FX 8 (sci-fi) 104=Sitar 105=Banjo 106=Shamisen 107=Koto 108=Kalimba 109=Bagpipe 110=Fiddle 111=Shanai 112=Tinkle Bell 113=Agogo 114=Steel Drums 115=Woodblock 116=Taiko Drum 117=Melodic Tom 118=Synth Drum 119=Reverse Cymbal 120=Guitar Fret Noise 121=Breath Noise 122=Seashore 123=Bird Tweet 124=Telephone Ring 125=Helicopter 126=Applause 127=Gunshot [No Drums] [Roland GS Capital Tones] 0=Piano 1 1=Piano 2 2=Piano 3 3=Honky-tonk 4=E.Piano 1 5=E.Piano 2 6=Harpsichord 7=Clav. 8=Celesta 9=Glockenspiel 10=Music Box 11=Vibraphone 12=Marimba 13=Xylophone 14=Tubular-bell 15=Santur 16=Organ 1 17=Organ 2 18=Organ 3 19=Church Org.1 20=Reed Organ 21=Accordion Fr 22=Harmonica 23=Bandneon 24=Nylon-str.Gt 25=Steel-str.Gt 26=Jazz Gt. 27=Clean Gt. 28=Muted Gt. 29=Overdrive Gt 30=DistortionGt 31=Gt.Harmonics 32=Acoustic Bs. 33=Fingered Bs. 34=Picked Bass 35=Fretless Bs. 36=Slap Bass 1 37=Slap Bass 2 38=Synth Bass 1 39=Synth Bass 2 40=Violin 41=Viola 42=Cello 43=Contrabass 44=Tremolo Str 45=PizzicatoStr 46=Harp 47=Timpani 48=Strings 49=Slow Strings 50=Syn.Strings1 51=Syn.Strings2 52=Choir Aahs 53=Voice Oohs 54=SynVox 55=OrchestraHit 56=Trumpet 57=Trombone 58=Tuba 59=MutedTrumpet 60=French Horn 61=Brass 1 62=Synth Brass 1 63=Synth Brass 2 64=Soprano Sax 65=Alto Sax 66=Tenor Sax 67=Baritone Sax 68=Oboe 69=English Horn 70=Bassoon 71=Clarinet 72=Piccolo 73=Flute 74=Recorder 75=Pan Flute 76=Bottle Blow 77=Shakuhachi 78=Whistle 79=Ocarina 80=Square Wave 81=Saw Wave 82=Syn.Calliope 83=Chiffer Lead 84=Charang 85=Solo Vox 86=5th Saw Wave 87=Bass & Lead 88=Fantasia 89=Warm Pad 90=Polysynth 91=Space Voice 92=Bowed Glass 93=Metal Pad 94=Halo Pad 95=Sweep Pad 96=Ice Rain 97=Soundtrack 98=Crystal 99=Atmosphere 100=Brightness 101=Goblin 102=Echo Drops 103=Star Theme 104=Sitar 105=Banjo 106=Shamisen 107=Koto 108=Kalimba 109=Bag Pipe 110=Fiddle 111=Shanai 112=Tinkle Bell 113=Agogo 114=Steel Drums 115=Woodblock 116=Taiko 117=Melo. Tom 1 118=Synth Drum 119=Reverse Cym. 120=Gt.FretNoise 121=Breath Noise 122=Seashore 123=Bird 124=Telephone 1 125=Helicopter 126=Applause 127=Gun Shot [Roland GS Drumsets] 0=Standard 8=Room 16=Power 24=Electronic 25=TR-808 32=Jazz 40=Brush 48=Orchestra 56=SFX [Roland GS Var #01] 38=Synth Bass101 57=Trombone 2 60=French Horn2 80=Square 81=Saw 98=Syn Mallet 102=Echo Bell 104=Sitar 2 120=Gt.Cut Noise 121=Fl.Key Click 122=Rain 123=Dog 124=Telephone 2 125=Car-Engine 126=Laughing 127=Machine Gun [Roland GS Var #02] 102=Echo Pan 120=String Slap 122=Thunder 123=Horse-Gallop 124=DoorCreaking 125=Car-Stop 126=Screaming 127=Lasergun [Roland GS Var #03] 122=Wind 123=Bird 2 124=Door 125=Car-Pass 126=Punch 127=Explosion [Roland GS Var #04] 122=Stream 124=Scratch 125=Car-Crash 126=Heart Beat [Roland GS Var #05] 122=Bubble 124=Windchime 125=Siren 126=Footsteps [Roland GS Var #06] 125=Train [Roland GS Var #07] 125=Jetplane [Roland GS Var #08] 0=Piano 1w 1=Piano 2w 2=Piano 3w 3=Honky-tonk w 4=Detuned EP 1 5=Detuned EP 2 6=Coupled Hps. 11=Vib.w 12=Marimba w 14=Church Bell 16=Detuned Or.1 17=Detuned Or.2 19=Church Org.2 21=Accordion It 24=Ukulele 25=12-str.Gt 26=Hawaiian Gt. 27=Chorus Gt. 28=Funk Gt. 30=Feedback Gt. 31=Gt.Feedback 38=Synth Bass 3 39=Synth Bass 4 40=Slow Violine 48=Orchestra 50=Syn.Strings3 61=Brass 2 62=Synth Brass3 63=Synth Brass4 80=Sine Wave 81=Doctor Solo 107=Taisho Koto 115=Castanets 116=Concert BD 117=Melo. Tom 2 118=808 Tom 125=Starship [Roland GS Var #09] 14=Carillon 118=Elec Perc 125=Burst Noise [Roland GS Var #16] 0=Piano 1d 4=E.Piano 1w 5=E.Piano 2w 6=Harpsi.w 16=60's Organ 1 19=Church Org.3 24=Nylon Gt.o 25=Mandolin 28=Funk Gt.2 39=Rubber Bass 62=AnalogBrass1 63=AnalogBrass2 [Roland GS Var #24] 4=60's E.Piano 6=Harpsi.o [Roland GS Var #32] 16=Organ 4 17=Organ 5 24=Nylon Gt.2 52=Choir Aahs 2 [XG Bank 0] 0=01 GrandPno 1=02 BritePno 2=03 E.Grand 3=04 HnkyTonk 4=05 E.Piano1 5=06 E.Piano2 6=07 Harpsi. 7=08 Clavi. 8=09 Celesta 9=10 Glocken 10=11 MusicBox 11=12 Vibes 12=13 Marimba 13=14 Xylophon 14=15 TubulBel 15=16 Dulcimer 16=17 DrawOrgn 17=18 PercOrgn 18=19 RockOrgn 19=20 ChrchOrgn 20=21 ReedOrgn 21=22 Acordion 22=23 Harmnica 23=24 TangoAcd 24=25 NylonGtr 25=26 SteelGtr 26=27 Jazz Gtr 27=28 CleanGtr 28=29 Mute.Gtr 29=30 Ovrdrive 30=31 Dist.Gtr 31=32 GtrHarmo 32=33 Aco.Bass 33=34 FngrBass 34=35 PickBass 35=36 Fretless 36=37 SlapBas1 37=38 SlapBas2 38=39 SynBass1 39=40 SynBass2 40=41 Violin 41=42 Viola 42=43 Cello 43=44 Contrabs 44=45 Trem.Str 45=46 Pizz.Str 46=47 Harp 47=48 Timpani 48=49 Strings1 49=50 Strings2 50=51 Syn.Str1 51=52 Syn.Str2 52=53 ChiorAah 53=54 VoiceOoh 54=55 SynVoice 55=56 Orch.Hit 56=57 Trumpet 57=58 Trombone 58=59 Tuba 59=60 Mute.Trp 60=61 Fr.Horn 61=62 BrasSect 62=63 SynBras1 63=64 SynBras2 64=65 SprnoSax 65=66 Alto Sax 66=67 TenorSax 67=68 Bari.Sax 68=69 Oboe 69=70 Eng.Horn 70=71 Bassoon 71=72 Clarinet 72=73 Piccolo 73=74 Flute 74=75 Recorder 75=76 PanFlute 76=77 Bottle 77=78 Shakhchi 78=79 Whistle 79=80 Ocarina 80=81 SquareLd 81=82 Saw.Lead 82=83 CaliopLd 83=84 Chiff Ld 84=85 CharanLd 85=86 Voice Ld 86=87 Fifth Ld 87=88 Bass &Ld 88=89 NewAgePad 89=90 Warm Pad 90=91 PolySyPd 91=92 ChoirPad 92=93 BowedPad 93=94 MetalPad 94=95 Halo Pad 95=96 SweepPad 96=97 Rain 97=98 SoundTrk 98=99 Crystal 99=100 Atmosphr 100=101 Bright 101=102 Goblins 102=103 Echoes 103=104 Sci-Fi 104=105 Sitar 105=106 Banjo 106=107 Shamisen 107=108 Koto 108=109 Kalimba 109=110 Bagpipe 110=111 Fiddle 111=112 Shanai 112=113 TnklBell 113=114 Agogo 114=115 SteelDrm 115=116 WoodBlok 116=117 TaikoDrm 117=118 MelodTom 118=119 Syn.Drum 119=120 RevCymbl 120=121 FretNoiz 121=122 BrthNoiz 122=123 Seashore 123=124 Tweet 124=125 Telphone 125=126 Helicptr 126=127 Applause 127=128 Gunshot [XG Bank 1 (KSP)] 0=01 GrndPnoK 1=02 BritPnoK 2=03 ElGrPnoK 3=04 HnkyTnkK 4=05 El.Pno1K 5=06 El.Pno2K 6=07 Harpsi.K 7=08 Clavi. K 11=12 VibesK 12=13 MarimbaK [XG Bank 100] 112=113 Rama Cym 119=120 Rev Tom1 [XG Bank 101] 112=113 AsianBel 119=120 Rev Tom2 [XG Bank 12 (Fast Decay)] 30=31 DstRthmG 39=40 Seq Bass 62=63 QuackBr 98=99 SynDrCmp [XG Bank 14 (Double Attack)] 61=62 SfrzndBr 98=99 Popcorn 102=103 Echo Pan [XG Bank 16 (Bright)] 24=25 NylonGt2 25=26 SteelGt2 52=53 Ch.Aahs2 56=57 Trumpet2 58=59 Tuba 2 87=88 Big&Low 89=90 ThickPad [XG Bank 17] 56=57 BriteTrp 89=90 Soft Pad [XG Bank 18 (Dark)] 0=01 MelloGrP 4=05 MelloEP1 26=27 MelloGtr 33=34 FingrDrk 38=39 SynBa1Dk 39=40 ClkSynBa 57=58 Trmbone2 63=64 Soft Brs 80=81 Hollow 81=82 DynaSaw 89=90 SinePad 98=99 TinyBell 99=100 WarmAtms [XG Bank 19] 39=40 SynBa2Dk 80=81 Shmoog 81=82 DigiSaw 99=100 HollwRls [XG Bank 20 (Rsonant)] 38=39 FastResB 62=63 RezSynBr 81=82 Big Lead 95=96 Shwimmer [XG Bank 24 (Attack)] 17=18 70sPcOr1 30=31 DistGtr2 38=39 AcidBass 48=49 ArcoStr 62=63 PolyBrss 81=82 HeavySyn 85=86 SynthAah [XG Bank 25 (Release)] 6=7 Harpsi.2 24=25 NylonGt3 81=82 WaspySyn [XG Bank 27 (Rezo Sweep)] 7=8 ClaviWah 33=34 FlangeBa 36=37 ResoSlap 50=51 ResoStr 62=63 SynBras3 95=96 Converge 97=98 Prologue [XG Bank 28 (Muted)] 34=35 MutePkBa 105=106 MuteBnjo [XG Bank 3 (Stereo)] 48=49 S.Strngs 49=50 S.SlwStr 52=53 S.Choir [XG Bank 32 (Detune 1)] 2=03 Det.CP80 4=05 Chor.EP1 5=06 Chor.EP2 16=17 DetDrwOr 17=18 DetPrcOr 19=20 ChurOrg3 21=22 AccordIt 22=23 Harmo 2 26=27 JazzAmp 27=28 ChorusGt 35=36 Fretles2 36=37 PunchThm 39=40 SmthBa 2 52=53 MelChoir 56=57 WarmTrp 60=61 FrHorn2 62=63 JumpBrss 104=105 DetSitar [XG Bank 33 (Detune 2)] 5=06 DX Hard 16=17 60sDrOr1 17=18 LiteOrg 35=36 Fretles3 [XG Bank 34 (Detune 3)] 5=06 DX Legend 16=17 60sDrOr2 35=36 Fretles4 [XG Bank 35 (Octave 1)] 6=07 Harpsi.3 15=16 Dulcimr2 16=17 70sDrOr1 19=20 ChurOrg2 25=26 12StrGtr 30=31 DistGtr3 38=39 Clv Bass 48=49 60sStrng 50=51 Syn Str 3 55=56 OrchHit2 61=62 Tp&TbSec 86=87 Big Five 98=99 RndGlock 104=105 Sitar 2 [XG Bank 36 (Octave 2)] 16=17 DrawOrg2 30=31 PowerGt2 [XG Bank 37 (5th 1)] 16=17 60sDrOr3 17=18 PercOrg2 30=31 PowerGt1 60=61 HornOrch [XG Bank 38 (5th 2)] 16=17 EvenBar 30=31 Dst.5ths [XG Bank 39 (Bend)] 61=62 BrssFall [XG Bank 40 (Tutti)] 0=01 PianoStr 2=03 GrPno1 4=05 HardEl.P 5=06 DX Phase 16=17 16+2"2/3 19=20 NotreDam 20=21 Puff Org 25=26 Nyln&Stl 28=29 FunkGtr1 30=31 FeedbkGt 32=33 JazzRthm 33=34 Ba&DsEG 38=39 TeknoBa 39=40 ModulrBa 44=45 Susp Str 46=47 YangChin 48=49 Orchestr 49=50 Warm Str 52=53 ChoirStr 54=55 SynVox2 61=62 BrssSec2 63=64 SynBrss4 65=66 Sax Sect 66=67 BrthTnSx 81=82 PulseSaw 98=99 GlockChi 99=100 NylonEP [XG Bank 41] 0=01 Dream 2=03 ElGrPno2 5=06 DX+Analg 25=26 Stl&Body 28=29 MuteStlG 30=31 FeedbGt2 39=40 DX Bass 48=49 Orchstr2 49=50 Kingdom 54=55 Choral 61=62 HiBrass 63=64 ChoirBrs 66=67 SoftTenr 81=82 Dr. Lead 98=99 ClearBel [XG Bank 42] 5=06 DXKotoEP 48=49 TremOrch 61=62 MelloBrs 98=99 ChorBell [XG Bank 43 (Velo-Switch)] 24=25 VelGtHrm 28=29 FunkGtr2 29=30 Gt.Pinch 30=31 RkRythm2 33=34 FngrSlap 37=38 VeloSlap 65=66 HyprAlto [XG Bank 45 (Velo-Xfade)] 4=05 VX El.P1 5=06 VX El.P2 11=12 HardVibe 28=29 Jazz Man 30=31 RockRthm 32=33 VXUprght 33=34 FngBass2 48=49 VeloStr 62=63 AnaVelBr 63=64 VelBrss2 81=82 VeloLead 96=97 ClaviPad [XG Bank 6 (Single)] 39=40 MelloSB2 60=61 FrHrSolo 80=81 Square 2 81=82 Saw 2 [XG Bank 64 (other wave)] 4=05 60sEl.P 7=08 PulseClv 10=11 Orgel 12=13 SineMrmb 16=17 Organ Ba 18=19 RotaryOr 19=20 OrgFlute 23=24 TngoAcd2 27=28 CleanGt2 31=32 AcoHarmo 33=34 JazzBass 38=39 Oscar 39=40 X WireBa 49=50 70sStr 50=51 Syn Str4 52=53 StrngAah 53=54 VoiceDoo 54=55 AnaVoice 55=56 Impact 59=60 MuteTrp2 62=63 AnaBrss1 63=64 AnaBrss2 66=67 TnrSax 2 75=76 PanFlut2 80=81 Mellow 82=83 Vent Syn 83=84 Rubby 84=85 DistLead 85=86 VoxLead 87=88 Fat&Prky 88=89 Fantasy2 89=90 Horn Pad 90=91 PolyPd80 91=92 Heaven2 92=93 Glacier 93=94 Tine Pad 95=96 PolarPad 96=97 HrmoRain 97=98 Ancestrl 98=99 SynMalet 99=100 NylnHarp 100=101 FantaBel 101=102 GobSyn 102=103 EchoBell 103=104 Starz 108=109 BigKalim 111=112 Shanai2 117=118 Mel Tom2 118=119 Ana Tom 119=120 Rev Cym2 [XG Bank 65] 7=08 PierceCl 16=17 70sDrOr2 18=19 SloRotar 19=20 TrmOrgFl 31=32 GtFeedbk 33=34 ModAlem 38=39 SqrBass 49=50 Str Ens3 50=51 SS Str 52=53 Male Aah 55=56 BrssStab 80=81 SoloSine 82=83 Pure Pad 84=85 WireLead 87=88 SoftWurl 89=90 RotarStr 90=91 ClickPad 91=92 Lite Pad 92=93 GlassPad 93=94 Pan Pad 95=96 Sweepy 96=97 AfrcnWnd 97=98 Rave 98=99 SftCryst 99=100 Harp Vox 101=102 50sSciFi 102=103 Big Pan 103=104 Odyssey 117=118 Real Tom 118=119 ElecPerc [XG Bank 66] 16=17 CheezOrg 18=19 FstRotar 31=32 GtrHrmo2 38=39 RubberBa 55=56 DoublHit 80=81 SineLead 90=91 Ana Pad 91=92 Itopia 95=96 Celstial 96=97 Caribean 98=99 LoudGlok 99=100 AtmosPad 101=102 Ring Pad 102=103 SynPiano 117=118 Rock Tom [XG Bank 67] 16=17 DrawOrg3 55=56 BrStab80 90=91 SqarPad 91=92 CC Pad 98=99 XmasBell 99=100 Planet 101=102 Ritual 102=103 Creation [XG Bank 68] 98=99 VibeBell 101=102 ToHeaven 102=103 Stardust [XG Bank 69] 98=99 DigiBell 101=102 MilkyWay 102=103 Reso Pan [XG Bank 70] 98=99 AirBells 101=102 Night [XG Bank 71] 98=99 BellHarp 101=102 Glisten [XG Bank 72] 98=99 Gamelmba 101=102 Puffy [XG Bank 8 (Slow)] 40=41 Slow Vln 44=45 SlowTrStr 48=49 SlowStr 49=50 LegatoSt 80=81 LMSquare 81=82 ThickSaw 102=103 EchoPad2 [XG Bank 96] 12=13 Balafon 14=15 ChrchBel 15=16 Cimbalom 24=25 Ukulele 25=26 Mandolin 26=27 PdlSteel 28=29 Mu.DstGt 35=36 SynFret1 38=39 Hammer 53=54 VoiceHmn 56=57 FluglHrn 71=72 BassClar 75=76 Kawala 81=82 Seq Ana 100=101 Smokey 101=102 BelChoir 104=105 Tambra 105=106 Rabab 106=107 Tsugaru 107=108 T.Koto 111=112 Pungi 112=113 Bonang 113=114 Atrigane 114=115 Tablas 115=116 Castanet 116=117 Gr.Cassa 119=120 RevSnar1 [XG Bank 97] 12=13 Balafon2 14=15 Carillon 15=16 Santur 35=36 Smooth 104=105 Tamboura 105=106 Gopichnt 107=108 Kanoon 111=112 Hichriki 112=113 Gender 114=115 GlasPerc 119=120 RevSnar2 [XG Bank 98] 12=13 Log Drum 105=106 Oud 112=113 Gamelan 114=115 ThaiBell 119=120 RevKick1 [XG Bank 99] 112=113 S.Gamlan 119=120 RevConBD [XG Drum Kits] 0=1 Standard Kit 1=2 Standard Kit2 8=9 Room Kit 16=17 Rock Kit 24=25 Electro Kit 25=26 Analog Kit 32=33 Jazz Kit 40=41 Brush Kit 48=49 Classic Kit [XG SFX Bank] 0=1 CuttngNz 1=2 CuttngNz2 2=3 DstCutNz 3=4 Str Slap 4=5 B.Slide 5=6 P.Scrape 16=17 Fl.KClik 32=33 Rain 33=34 Thunder 34=35 Wind 35=36 Stream 36=37 Bubble 37=38 Feed 48=49 Dog 49=50 Horse 50=51 Bird 2 51=52 Kitty 52=53 Growl 53=54 Haunted 54=55 Ghost 55=56 Maou 64=65 Tel.Dial 65=66 DoorSqek 66=67 Door Slam 67=68 Scratch 68=69 Scratch 2 69=70 WindChm 70=71 Telphon2 80=81 CarEngin 81=82 Car Stop 82=83 Car Pass 83=84 CarCrash 84=85 Siren 85=86 Train 86=87 Jetplane 87=88 Starship 88=89 Burst 89=90 Coaster 90=91 SbMarine 96=97 Laughing 97=98 Scream 98=99 Punch 99=100 Heart 100=101 FootStep 101=102 Applaus2 112=113 MchinGun 113=114 LaserGun 114=115 Xplosion 115=116 FireWork [XG SFX Kits] 0=1 SFX 1 1=2 SFX 2 [XG Set channel to rhythm part] ; ----------------------------------------------------------------------------- .Note Names [0..127] [General MIDI Drums] 35=Acoustic Bass Drum 36=Bass Drum 1 37=Side Stick 38=Acoustic Snare 39=Hand Clap 40=Electric Snare 41=Low Floor Tom 42=Close Hi-Hat 43=High Floor Tom 44=Pedal Hi-Hat 45=Low Tom 46=Open Hi-Hat 47=Low-Mid Tom 48=Hi-Mid Tom 49=Crash Cymbal 1 50=High Tom 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Bell 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibraslap 59=Ride Cymbal 2 60=Hi Bongo 61=Low Bongo 62=Mute Hi Conga 63=Open Hi Conga 64=Low Conga 65=High Timbale 66=Low Timbale 67=High Agogo 68=Low Agogo 69=Cabasa 70=Maracas 71=Short Whistle 72=Long Whistle 73=Short Guiro 74=Long Guiro 75=Claves 76=Hi Wood Block 77=Low Wood Block 78=Mute Cuica 79=Open Cuica 80=Mute Triangle 81=Open Triangle [Roland GS Brush Set] BasedOn=Roland GS Standard Set 27=High Q 28=Slap 29=Scratch Push 30=Scratch Pull 31=Sticks 32=Square Click 33=Metronome Click 34=Metronome Bell 35=Jazz BD 2 36=Jazz BD 1 37=Side Stick 38=Brush Tap 39=Brush Slap 40=Brush Swirl 41=Low Tom 2 42=Closed Hi-Hat 43=Low Tom 1 44=Pedal Hi-Hat 45=Mid Tom 2 46=Open Hi-Hat 47=Mid Tom 1 48=High Tom 2 49=Crash Cymbal 1 50=High Tom 1 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Bell 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibra-slap 59=Ride Cymbal 2 60=High Bongo 61=Low Bongo 62=Mute High Conga 63=Open High Conga 64=Low Conga 65=High Timbale 66=Low Timbale 67=High Agogo 68=Low Agogo 69=Cabasa 70=Maracas 71=Short Hi Whistle 72=Long Low Whistle 73=Short Guiro 74=Long Guiro 75=Claves 76=High Wood Block 77=Low Wood Block 78=Mute Cuica 79=Open Cuica 80=Mute Triangle 81=Open Triangle 82=Shaker 83=Jingle Bell 84=Belltree 85=Castanets 86=Mute Surdo 87=Open Surdo [Roland GS Electronic Set] BasedOn=Roland GS Standard Set 27=High Q 28=Slap 29=Scratch Push 30=Scratch Pull 31=Sticks 32=Square Click 33=Metronome Click 34=Metronome Bell 35=Kick Drum 2 36=Elec BD 37=Side Stick 38=Elec SD 39=Hand Clap 40=Gated SD 41=Elec Low Tom 2 42=Closed Hi-Hat 43=Elec Low Tom 1 44=Pedal Hi-Hat 45=Elec Mid Tom 2 46=Open Hi-Hat 47=Elec Mid Tom 1 48=Elec Hi Tom 2 49=Crash Cymbal 1 50=Elec Hi Tom 1 51=Ride Cymbal 1 52=Reverse Cymbal 53=Ride Bell 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibra-slap 59=Ride Cymbal 2 60=High Bongo 61=Low Bongo 62=Mute High Conga 63=Open High Conga 64=Low Conga 65=High Timbale 66=Low Timbale 67=High Agogo 68=Low Agogo 69=Cabasa 70=Maracas 71=Short Hi Whistle 72=Long Low Whistle 73=Short Guiro 74=Long Guiro 75=Claves 76=High Wood Block 77=Low Wood Block 78=Mute Cuica 79=Open Cuica 80=Mute Triangle 81=Open Triangle 82=Shaker 83=Jingle Bell 84=Belltree 85=Castanets 86=Mute Surdo 87=Open Surdo [Roland GS Jazz Set] BasedOn=Roland GS Standard Set 27=High Q 28=Slap 29=Scratch Push 30=Scratch Pull 31=Sticks 32=Square Click 33=Metronome Click 34=Metronome Bell 35=Jazz BD 2 36=Jazz BD 1 37=Side Stick 38=Snare Drum 1 39=Hand Clap 40=Snare Drum 2 41=Low Tom 2 42=Closed Hi-Hat 43=Low Tom 1 44=Pedal Hi-Hat 45=Mid Tom 2 46=Open Hi-Hat 47=Mid Tom 1 48=High Tom 2 49=Crash Cymbal 1 50=High Tom 1 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Bell 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibra-slap 59=Ride Cymbal 2 60=High Bongo 61=Low Bongo 62=Mute High Conga 63=Open High Conga 64=Low Conga 65=High Timbale 66=Low Timbale 67=High Agogo 68=Low Agogo 69=Cabasa 70=Maracas 71=Short Hi Whistle 72=Long Low Whistle 73=Short Guiro 74=Long Guiro 75=Claves 76=High Wood Block 77=Low Wood Block 78=Mute Cuica 79=Open Cuica 80=Mute Triangle 81=Open Triangle 82=Shaker 83=Jingle Bell 84=Belltree 85=Castanets 86=Mute Surdo 87=Open Surdo [Roland GS Orchestra Set] BasedOn=Roland GS Standard Set 27=Closed Hi-Hat 28=Pedal Hi-Hat 29=Open Hi-Hat 30=Ride Cymbal 31=Sticks 32=Square Click 33=Metronome Click 34=Metronome Bell 35=Concert BD 2 36=Concert BD 1 37=Side Stick 38=Concert SD 39=Castanets 40=Concert SD 41=Timpani F 42=Timpani F# 43=Timpani G 44=Timpani G# 45=Timpani A 46=Timpani A# 47=Timpani B 48=Timpani c 49=Timpani c# 50=Timpani d 51=Timpani d# 52=Timpani e 53=Timpani f 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Concert Cymbal 2 58=Vibra-slap 59=Concert Cymbal 1 60=High Bongo 61=Low Bongo 62=Mute High Conga 63=Open High Conga 64=Low Conga 65=High Timbale 66=Low Timbale 67=High Agogo 68=Low Agogo 69=Cabasa 70=Maracas 71=Short Hi Whistle 72=Long Low Whistle 73=Short Guiro 74=Long Guiro 75=Claves 76=High Wood Block 77=Low Wood Block 78=Mute Cuica 79=Open Cuica 80=Mute Triangle 81=Open Triangle 82=Shaker 83=Jingle Bell 84=Belltree 85=Castanets 86=Mute Surdo 87=Open Surdo 88=Applause [Roland GS Power Set] BasedOn=Roland GS Standard Set 27=High Q 28=Slap 29=Scratch Push 30=Scratch Pull 31=Sticks 32=Square Click 33=Metronome Click 34=Metronome Bell 35=Kick Drum 2 36=MONDO Kick 37=Side Stick 38=Gated SD 39=Hand Clap 40=Snare Drum 2 41=Room Low Tom 2 42=Closed Hi-Hat 43=Room Low Tom 1 44=Pedal Hi-Hat 45=Room Mid Tom 2 46=Open Hi-Hat 47=Room Mid Tom 1 48=Room Hi Tom 2 49=Crash Cymbal 1 50=Room Hi Tom 1 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Bell 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibra-slap 59=Ride Cymbal 2 60=High Bongo 61=Low Bongo 62=Mute High Conga 63=Open High Conga 64=Low Conga 65=High Timbale 66=Low Timbale 67=High Agogo 68=Low Agogo 69=Cabasa 70=Maracas 71=Short Hi Whistle 72=Long Low Whistle 73=Short Guiro 74=Long Guiro 75=Claves 76=High Wood Block 77=Low Wood Block 78=Mute Cuica 79=Open Cuica 80=Mute Triangle 81=Open Triangle 82=Shaker 83=Jingle Bell 84=Belltree 85=Castanets 86=Mute Surdo 87=Open Surdo [Roland GS Room Set] BasedOn=Roland GS Standard Set 27=High Q 28=Slap 29=Scratch Push 30=Scratch Pull 31=Sticks 32=Square Click 33=Metronome Click 34=Metronome Bell 35=Kick Drum 2 36=Kick Drum 1 37=Side Stick 38=Snare Drum 1 39=Hand Clap 40=Snare Drum 2 41=Room Low Tom 2 42=Closed Hi-Hat 43=Room Low Tom 1 44=Pedal Hi-Hat 45=Room Mid Tom 2 46=Open Hi-Hat 47=Room Mid Tom 1 48=Room Hi Tom 2 49=Crash Cymbal 1 50=Room Hi Tom 1 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Bell 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibra-slap 59=Ride Cymbal 2 60=High Bongo 61=Low Bongo 62=Mute High Conga 63=Open High Conga 64=Low Conga 65=High Timbale 66=Low Timbale 67=High Agogo 68=Low Agogo 69=Cabasa 70=Maracas 71=Short Hi Whistle 72=Long Low Whistle 73=Short Guiro 74=Long Guiro 75=Claves 76=High Wood Block 77=Low Wood Block 78=Mute Cuica 79=Open Cuica 80=Mute Triangle 81=Open Triangle 82=Shaker 83=Jingle Bell 84=Belltree 85=Castanets 86=Mute Surdo 87=Open Surdo [Roland GS SFX Set] 39=High Q 40=Slap 41=Scratch Push 42=Scratch Pull 43=Sticks 44=Square Click 45=Metronome Click 46=Metronome Bell 47=Guitar Sliding Finger 48=Guitar Cutting Noise (Down) 49=Guitar Cutting Noise (Up) 50=String Slap of Double Bass 51=Flute Key click 52=Laughing 53=Screaming 54=Punch 55=Heart Beat 56=Footsteps 1 57=Footsteps 2 58=Applause 59=Door Creaking 60=Door 61=Scratch 62=Windchime 63=Car-Engine 64=Car-Stop 65=Car-Pass 66=Car-Crash 67=Siren 68=Train 69=Jetplane 70=Helicopter 71=Starship 72=Gunshot 73=Machine Gun 74=Lasergun 75=Explosion 76=Dog 77=Horse-Gallop 78=Birds 79=Rain 80=Thunder 81=Wind 82=Seashore 83=Stream 84=Bubble [Roland GS Standard Set] 27=High Q 28=Slap 29=Scratch Push 30=Scratch Pull 31=Sticks 32=Square Click 33=Metronome Click 34=Metronome Bell 35=Kick Drum 2 36=Kick Drum 1 37=Side Stick 38=Snare Drum 1 39=Hand Clap 40=Snare Drum 2 41=Low Tom 2 42=Closed Hi-Hat 43=Low Tom 1 44=Pedal Hi-Hat 45=Mid Tom 2 46=Open Hi-Hat 47=Mid Tom 1 48=High Tom 2 49=Crash Cymbal 1 50=High Tom 1 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Bell 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibra-slap 59=Ride Cymbal 2 60=High Bongo 61=Low Bongo 62=Mute High Conga 63=Open High Conga 64=Low Conga 65=High Timbale 66=Low Timbale 67=High Agogo 68=Low Agogo 69=Cabasa 70=Maracas 71=Short Hi Whistle 72=Long Low Whistle 73=Short Guiro 74=Long Guiro 75=Claves 76=High Wood Block 77=Low Wood Block 78=Mute Cuica 79=Open Cuica 80=Mute Triangle 81=Open Triangle 82=Shaker 83=Jingle Bell 84=Belltree 85=Castanets 86=Mute Surdo 87=Open Surdo [Roland GS TR-808 Set] BasedOn=Roland GS Standard Set 27=High Q 28=Slap 29=Scratch Push 30=Scratch Pull 31=Sticks 32=Square Click 33=Metronome Click 34=Metronome Bell 35=Kick Drum 2 36=808 Bass Drum 37=808 Rim Shot 38=808 Snare Drum 39=Hand Clap 40=Snare Drum 2 41=808 Low Tom 2 42=808 CHH 43=808 Low Tom 1 44=808 CHH 45=808 Mid Tom 2 46=808 OHH 47=808 Mid Tom 1 48=808 Hi Tom 2 49=808 Cymbal 50=808 Hi Tom 1 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Bell 54=Tambourine 55=Splash Cymbal 56=808 Cowbell 57=Crash Cymbal 2 58=Vibra-slap 59=Ride Cymbal 2 60=High Bongo 61=Low Bongo 62=808 High Conga 63=808 Mid Conga 64=808 Low Conga 65=High Timbale 66=Low Timbale 67=High Agogo 68=Low Agogo 69=Cabasa 70=808 Maracas 71=Short Hi Whistle 72=Long Low Whistle 73=Short Guiro 74=Long Guiro 75=808 Claves 76=High Wood Block 77=Low Wood Block 78=Mute Cuica 79=Open Cuica 80=Mute Triangle 81=Open Triangle 82=Shaker 83=Jingle Bell 84=Belltree 85=Castanets 86=Mute Surdo 87=Open Surdo [XG Analog Kit] BasedOn=XG Standard Kit 13=Surdo Mute 14=Surdo Open 15=Hi Q 16=Whip Slap 17=Scratch Push 18=Scratch Pull 19=Finger Snap 20=Click Noise 21=Metronome Click 22=Metronome Bell 23=Seq Click L 24=Seq Click H 25=Brush Tap 26=Brush Swirl L 27=Brush Slap 28=Reverse Cymbal 29=Snare Roll 30=Hi Q 31=SD Rock H 32=Sticks 33=Bass Drum M 34=Open Rim Shot 35=BD Analog L 36=BD Analog H 37=Analog Side Stick 38=Analog Snare L 39=Hand Clap 40=Analog Snare H 41=Analog Tom 1 42=Analog HH Closed 1 43=Analog Tom 2 44=Analog HH Closed 2 45=Analog Tom 3 46=Analog HH Open 47=Analog Tom 4 48=Analog Tom 5 49=Analog Cymbal 50=Analog Tom 6 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Cymbal Cup 54=Tambourine 55=Splash Cymbal 56=Analog Cowbell 57=Crash Cymbal 2 58=Vibraslap 59=Ride Cymbal 2 60=Bongo H 61=Bongo L 62=Analog Conga H 63=Analog Conga M 64=Analog Conga L 65=Timbale H 66=Timbale L 67=Agogo H 68=Agogo L 69=Cabasa 70=Analog Maracas 71=Samba Whistle H 72=Samba Whistle L 73=Guiro Short 74=Guiro Long 75=Analog Claves 76=Wood Block H 77=Wood Block L 78=Scratch Push 79=Scratch Pull 80=Triangle Mute 81=Triangle Open 82=Shaker 83=Jingle Bell 84=Bell Tree [XG Brush Kit] BasedOn=XG Standard Kit 13=Surdo Mute 14=Surdo Open 15=Hi Q 16=Whip Slap 17=Scratch Push 18=Scratch Pull 19=Finger Snap 20=Click Noise 21=Metronome Click 22=Metronome Bell 23=Seq Click L 24=Seq Click H 25=Brush Tap 26=Brush Swirl L 27=Brush Slap 28=Brush Swirl H 29=Snare Roll 30=Castanet 31=Brush Slap L 32=Sticks 33=Bass Drum L 34=Open Rim Shot 35=Bass Drum M 36=BD Soft 37=Side Stick 38=Brush Slap 39=Hand Clap 40=Brush Tap 41=Brush Tom 1 42=Hi-Hat Closed 43=Brush Tom 2 44=Hi-Hat Pedal 45=Brush Tom 3 46=Hi-Hat Open 47=Brush Tom 4 48=Brush Tom 5 49=Crash Cymbal 1 50=Brush Tom 6 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Cymbal Cup 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibraslap 59=Ride Cymbal 2 60=Bongo H 61=Bongo L 62=Conga H Mute 63=Conga H Open 64=Conga L 65=Timbale H 66=Timbale L 67=Agogo H 68=Agogo L 69=Cabasa 70=Maracas 71=Samba Whistle H 72=Samba Whistle L 73=Guiro Short 74=Guiro Long 75=Claves 76=Wood Block H 77=Wood Block L 78=Cuica Mute 79=Cuica Open 80=Triangle Mute 81=Triangle Open 82=Shaker 83=Jingle Bell 84=Bell Tree [XG Classic Kit] BasedOn=XG Standard Kit 13=Surdo Mute 14=Surdo Open 15=Hi Q 16=Whip Slap 17=Scratch Push 18=Scratch Pull 19=Finger Snap 20=Click Noise 21=Metronome Click 22=Metronome Bell 23=Seq Click L 24=Seq Click H 25=Brush Tap 26=Brush Swirl L 27=Brush Slap 28=Brush Swirl H 29=Snare Roll 30=Castanet 31=Snare L 32=Sticks 33=Bass Drum L 34=Open Rim Shot 35=Bass Drum M 36=Gran Casa 37=Side Stick 38=Snare M 39=Hand Clap 40=Snare H 41=Jazz Tom 1 42=Hi-Hat Closed 43=Jazz Tom 2 44=Hi-Hat Pedal 45=Jazz Tom 3 46=Hi-Hat Open 47=Jazz Tom 4 48=Jazz Tom 5 49=Hand Cym.Open L 50=Jazz Tom 6 51=Hand Cym.Closed L 52=Chinese Cymbal 53=Ride Cymbal Cup 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Hand Cym.Open H 58=Vibraslap 59=Hand Cym.Closed H 60=Bongo H 61=Bongo L 62=Conga H Mute 63=Conga H Open 64=Conga L 65=Timbale H 66=Timbale L 67=Agogo H 68=Agogo L 69=Cabasa 70=Maracas 71=Samba Whistle H 72=Samba Whistle L 73=Guiro Short 74=Guiro Long 75=Claves 76=Wood Block H 77=Wood Block L 78=Cuica Mute 79=Cuica Open 80=Triangle Mute 81=Triangle Open 82=Shaker 83=Jingle Bell 84=Bell Tree [XG Electro Kit] BasedOn=XG Standard Kit 13=Surdo Mute 14=Surdo Open 15=Hi Q 16=Whip Slap 17=Scratch Push 18=Scratch Pull 19=Finger Snap 20=Click Noise 21=Metronome Click 22=Metronome Bell 23=Seq Click L 24=Seq Click H 25=Brush Tap 26=Brush Swirl L 27=Brush Slap 28=Reverse Cymbal 29=Snare Roll 30=Hi Q 31=Snare M 32=Sticks 33=Bass Drum H 4 34=Open Rim Shot 35=BD Rock 36=BD Gate 37=Side Stick 38=SD Rock L 39=Hand Clap 40=SD Rock H 41=E Tom 1 42=Hi-Hat Closed 43=E Tom 2 44=Hi-Hat Pedal 45=E Tom 3 46=Hi-Hat Open 47=E Tom 4 48=E Tom 5 49=Crash Cymbal 1 50=E Tom 6 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Cymbal Cup 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibraslap 59=Ride Cymbal 2 60=Bongo H 61=Bongo L 62=Conga H Mute 63=Conga H Open 64=Conga L 65=Timbale H 66=Timbale L 67=Agogo H 68=Agogo L 69=Cabasa 70=Maracas 71=Samba Whistle H 72=Samba Whistle L 73=Guiro Short 74=Guiro Long 75=Claves 76=Wood Block H 77=Wood Block L 78=Scratch Push 79=Scratch Pull 80=Triangle Mute 81=Triangle Open 82=Shaker 83=Jingle Bell 84=Bell Tree [XG Jazz Kit] BasedOn=XG Standard Kit 13=Surdo Mute 14=Surdo Open 15=Hi Q 16=Whip Slap 17=Scratch Push 18=Scratch Pull 19=Finger Snap 20=Click Noise 21=Metronome Click 22=Metronome Bell 23=Seq Click L 24=Seq Click H 25=Brush Tap 26=Brush Swirl L 27=Brush Slap 28=Brush Swirl H 29=Snare Roll 30=Castanet 31=Snare L 32=Sticks 33=Bass Drum L 34=Open Rim Shot 35=Bass Drum M 36=BD Jazz 37=Side Stick 38=Snare M 39=Hand Clap 40=Snare H 41=Jazz Tom 1 42=Hi-Hat Closed 43=Jazz Tom 2 44=Hi-Hat Pedal 45=Jazz Tom 3 46=Hi-Hat Open 47=Jazz Tom 4 48=Jazz Tom 5 49=Crash Cymbal 1 50=Jazz Tom 6 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Cymbal Cup 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibraslap 59=Ride Cymbal 2 60=Bongo H 61=Bongo L 62=Conga H Mute 63=Conga H Open 64=Conga L 65=Timbale H 66=Timbale L 67=Agogo H 68=Agogo L 69=Cabasa 70=Maracas 71=Samba Whistle H 72=Samba Whistle L 73=Guiro Short 74=Guiro Long 75=Claves 76=Wood Block H 77=Wood Block L 78=Cuica Mute 79=Cuica Open 80=Triangle Mute 81=Triangle Open 82=Shaker 83=Jingle Bell 84=Bell Tree [XG Rock Kit] BasedOn=XG Standard Kit 13=Surdo Mute 14=Surdo Open 15=Hi Q 16=Whip Slap 17=Scratch Push 18=Scratch Pull 19=Finger Snap 20=Click Noise 21=Metronome Click 22=Metronome Bell 23=Seq Click L 24=Seq Click H 25=Brush Tap 26=Brush Swirl L 27=Brush Slap 28=Brush Swirl H 29=Snare Roll 30=Castanet 31=SD Rock M 32=Sticks 33=Bass Drum M 34=Open Rim Shot 35=Bass Drum H 3 36=BD Rock 37=Side Stick 38=SD Rock 39=Hand Clap 40=SD Rock Rim 41=Rock Tom 1 42=Hi-Hat Closed 43=Rock Tom 2 44=Hi-Hat Pedal 45=Rock Tom 3 46=Hi-Hat Open 47=Rock Tom 4 48=Rock Tom 5 49=Crash Cymbal 1 50=Rock Tom 6 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Cymbal Cup 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibraslap 59=Ride Cymbal 2 60=Bongo H 61=Bongo L 62=Conga H Mute 63=Conga H Open 64=Conga L 65=Timbale H 66=Timbale L 67=Agogo H 68=Agogo L 69=Cabasa 70=Maracas 71=Samba Whistle H 72=Samba Whistle L 73=Guiro Short 74=Guiro Long 75=Claves 76=Wood Block H 77=Wood Block L 78=Cuica Mute 79=Cuica Open 80=Triangle Mute 81=Triangle Open 82=Shaker 83=Jingle Bell 84=Bell Tree [XG Room Kit] BasedOn=XG Standard Kit 13=Surdo Mute 14=Surdo Open 15=Hi Q 16=Whip Slap 17=Scratch Push 18=Scratch Pull 19=Finger Snap 20=Click Noise 21=Metronome Click 22=Metronome Bell 23=Seq Click L 24=Seq Click H 25=Brush Tap 26=Brush Swirl L 27=Brush Slap 28=Brush Swirl H 29=Snare Roll 30=Castanet 31=Snare L 32=Sticks 33=Bass Drum L 34=Open Rim Shot 35=Bass Drum M 36=BD Room 37=Side Stick 38=Snare M 39=Hand Clap 40=Snare H 41=Room Tom 1 42=Hi-Hat Closed 43=Room Tom 2 44=Hi-Hat Pedal 45=Room Tom 3 46=Hi-Hat Open 47=Room Tom 4 48=Room Tom 5 49=Crash Cymbal 1 50=Room Tom 6 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Cymbal Cup 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibraslap 59=Ride Cymbal 2 60=Bongo H 61=Bongo L 62=Conga H Mute 63=Conga H Open 64=Conga L 65=Timbale H 66=Timbale L 67=Agogo H 68=Agogo L 69=Cabasa 70=Maracas 71=Samba Whistle H 72=Samba Whistle L 73=Guiro Short 74=Guiro Long 75=Claves 76=Wood Block H 77=Wood Block L 78=Cuica Mute 79=Cuica Open 80=Triangle Mute 81=Triangle Open 82=Shaker 83=Jingle Bell 84=Bell Tree [XG SFX 1] 36=Guitar Cutting Noise 37=Guitar Cutting Noise 2 38=Dist. Cut Noise 39=String Slap 40=Bass Slide 41=Pick Scrape 52=FL.Key Click 68=Rain 69=Thunder 70=Wind 71=Stream 72=Bubble 73=Feed 84=Dog 85=Horse Gallop 86=Bird 2 87=Kitty 88=Growl 89=Haunted 90=Ghost 91=Maou [XG SFX 2] 36=Dial Tone 37=Door Creaking 38=Door Slam 39=Scratch 40=Scratch 2 41=Windchime 42=Telephone Ring2 52=Engine Start 53=Tire Screech 54=Car Passing 55=Crash 56=Siren 57=Train 58=Jetplane 59=Starship 60=Burst Noise 61=Coaster 62=SbMarine [XG Standard Kit] 13=Surdo Mute 14=Surdo Open 15=Hi Q 16=Whip Slap 17=Scratch Push 18=Scratch Pull 19=Finger Snap 20=Click Noise 21=Metronome Click 22=Metronome Bell 23=Seq Click L 24=Seq Click H 25=Brush Tap 26=Brush Swirl L 27=Brush Slap 28=Brush Swirl H 29=Snare Roll 30=Castanet 31=Snare L 32=Sticks 33=Bass Drum L 34=Open Rim Shot 35=Bass Drum M 36=Bass Drum H 37=Side Stick 38=Snare M 39=Hand Clap 40=Snare H 41=Floor Tom L 42=Hi-Hat Closed 43=Floor Tom H 44=Hi-Hat Pedal 45=Low Tom 46=Hi-Hat Open 47=Mid Tom L 48=Mid Tom H 49=Crash Cymbal 1 50=High Tom 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Cymbal Cup 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibraslap 59=Ride Cymbal 2 60=Bongo H 61=Bongo L 62=Conga H Mute 63=Conga H Open 64=Conga L 65=Timbale H 66=Timbale L 67=Agogo H 68=Agogo L 69=Cabasa 70=Maracas 71=Samba Whistle H 72=Samba Whistle L 73=Guiro Short 74=Guiro Long 75=Claves 76=Wood Block H 77=Wood Block L 78=Cuica Mute 79=Cuica Open 80=Triangle Mute 81=Triangle Open 82=Shaker 83=Jingle Bell 84=Bell Tree [XG Standard2 Kit] BasedOn=XG Standard Kit 13=Surdo Mute 14=Surdo Open 15=Hi Q 16=Whip Slap 17=Scratch Push 18=Scratch Pull 19=Finger Snap 20=Click Noise 21=Metronome Click 22=Metronome Bell 23=Seq Click L 24=Seq Click H 25=Brush Tap 26=Brush Swirl L 27=Brush Slap 28=Brush Swirl H 29=Snare Roll 30=Castanet 31=Snare L 32=Sticks 33=Bass Drum L 34=Open Rim Shot 2 35=Bass Drum M 2 36=Bass Drum H 2 37=Side Stick 38=Snare M 2 39=Hand Clap 40=Snare H 2 41=Floor Tom L 42=Hi-Hat Closed 43=Floor Tom H 44=Hi-Hat Pedal 45=Low Tom 46=Hi-Hat Open 47=Mid Tom L 48=Mid Tom H 49=Crash Cymbal 1 50=High Tom 51=Ride Cymbal 1 52=Chinese Cymbal 53=Ride Cymbal Cup 54=Tambourine 55=Splash Cymbal 56=Cowbell 57=Crash Cymbal 2 58=Vibraslap 59=Ride Cymbal 2 60=Bongo H 61=Bongo L 62=Conga H Mute 63=Conga H Open 64=Conga L 65=Timbale H 66=Timbale L 67=Agogo H 68=Agogo L 69=Cabasa 70=Maracas 71=Samba Whistle H 72=Samba Whistle L 73=Guiro Short 74=Guiro Long 75=Claves 76=Wood Block H 77=Wood Block L 78=Cuica Mute 79=Cuica Open 80=Triangle Mute 81=Triangle Open 82=Shaker 83=Jingle Bell 84=Bell Tree ; ----------------------------------------------------------------------------- .Controller Names [0..127] [Roland GS Controllers] 1=1-Modulation 5=5-Portamento Time 6=6-Data Entry MSB 7=7-Volume 10=10-Pan 11=11-Expression 38=38-Data Entry LSB 64=64-Hold 1 65=65-Portamento 66=66-Sostenuto 67=67-Soft 84=84-Portamento Control 91=91-Effect1 (Reverb Send Level) 93=93-Effect3 (Chorus Send Level) 98=98-NRPN LSB 99=99-NRPN MSB 100=100-RPN LSB 101=101-RPN MSB [Standard] 1=1-Modulation 2=2-Breath 4=4-Foot controller 5=5-Portamento time 7=7-Volume 8=8-Balance 10=10-Pan 11=11-Expression 64=64-Pedal (sustain) 65=65-Portamento 66=66-Pedal (sostenuto) 67=67-Pedal (soft) 69=69-Hold 2 91=91-External Effects depth 92=92-Tremolo depth 93=93-Chorus depth 94=94-Celeste (detune) depth 95=95-Phaser depth [Yamaha XG Controllers] 0=0-Bank Select MSB 1=1-Modulation 5=5-Portamento Time 6=6-Data Entry MSB 7=7-Master Volume 10=10-Panpot 11=11-Expression 32=32-Bank Select LSB 38=38-Data Entry LSB 64=64-Sustain 65=65-Portamento 66=66-Sostenuto 67=67-Soft Pedal 71=71-Harmonic Content 72=72-Release Time 73=73-Attack Time 74=74-Brightness 84=84-Portamento Control 91=91-Effects Send Level 1 (reverb) 93=93-Effects Send Level 3 (chorus) 94=94-Effects Send Level 4 (variation) 96=96-RPN Increment 97=97-RPN Decrement 98=98-NRPN LSB 99=99-NRPN MSB 100=100-RPN LSB 101=101-RPN MSB 120=120-All Sound Off 121=121-Reset All Controllers 123=123-All Notes Off 124=124-OMNI Off 125=125-OMNI On 126=126-Mono 127=127-Poly ; ----------------------------------------------------------------------------- .RPN Names [0..16383] [Standard] 0=Pitch Bend Range 1=Fine Tuning 2=Coarse Tuning 3=Tuning Program Select 4=Tuning Bank Select ; ----------------------------------------------------------------------------- .NRPN Names [0..16383] [Roland GS NRPN] 136=Vibrato Rate 137=Vibrato Depth 138=Vibrato Delay 160=TVF Cutoff Frequency 161=TVF Resonance 227=TVF&TVA Envelope Attack Time 228=TVF&TVA Envelope Decay Time 230=TVF&TVA Envelope Release Time 3072=Drum Instrument Pitch Coarse #0 3073=Drum Instrument Pitch Coarse #1 3074=Drum Instrument Pitch Coarse #2 3075=Drum Instrument Pitch Coarse #3 3076=Drum Instrument Pitch Coarse #4 3077=Drum Instrument Pitch Coarse #5 3078=Drum Instrument Pitch Coarse #6 3079=Drum Instrument Pitch Coarse #7 3080=Drum Instrument Pitch Coarse #8 3081=Drum Instrument Pitch Coarse #9 3082=Drum Instrument Pitch Coarse #10 3083=Drum Instrument Pitch Coarse #11 3084=Drum Instrument Pitch Coarse #12 3085=Drum Instrument Pitch Coarse #13 3086=Drum Instrument Pitch Coarse #14 3087=Drum Instrument Pitch Coarse #15 3088=Drum Instrument Pitch Coarse #16 3089=Drum Instrument Pitch Coarse #17 3090=Drum Instrument Pitch Coarse #18 3091=Drum Instrument Pitch Coarse #19 3092=Drum Instrument Pitch Coarse #20 3093=Drum Instrument Pitch Coarse #21 3094=Drum Instrument Pitch Coarse #22 3095=Drum Instrument Pitch Coarse #23 3096=Drum Instrument Pitch Coarse #24 3097=Drum Instrument Pitch Coarse #25 3098=Drum Instrument Pitch Coarse #26 3099=Drum Instrument Pitch Coarse #27 3100=Drum Instrument Pitch Coarse #28 3101=Drum Instrument Pitch Coarse #29 3102=Drum Instrument Pitch Coarse #30 3103=Drum Instrument Pitch Coarse #31 3104=Drum Instrument Pitch Coarse #32 3105=Drum Instrument Pitch Coarse #33 3106=Drum Instrument Pitch Coarse #34 3107=Drum Instrument Pitch Coarse #35 3108=Drum Instrument Pitch Coarse #36 3109=Drum Instrument Pitch Coarse #37 3110=Drum Instrument Pitch Coarse #38 3111=Drum Instrument Pitch Coarse #39 3112=Drum Instrument Pitch Coarse #40 3113=Drum Instrument Pitch Coarse #41 3114=Drum Instrument Pitch Coarse #42 3115=Drum Instrument Pitch Coarse #43 3116=Drum Instrument Pitch Coarse #44 3117=Drum Instrument Pitch Coarse #45 3118=Drum Instrument Pitch Coarse #46 3119=Drum Instrument Pitch Coarse #47 3120=Drum Instrument Pitch Coarse #48 3121=Drum Instrument Pitch Coarse #49 3122=Drum Instrument Pitch Coarse #50 3123=Drum Instrument Pitch Coarse #51 3124=Drum Instrument Pitch Coarse #52 3125=Drum Instrument Pitch Coarse #53 3126=Drum Instrument Pitch Coarse #54 3127=Drum Instrument Pitch Coarse #55 3128=Drum Instrument Pitch Coarse #56 3129=Drum Instrument Pitch Coarse #57 3130=Drum Instrument Pitch Coarse #58 3131=Drum Instrument Pitch Coarse #59 3132=Drum Instrument Pitch Coarse #60 3133=Drum Instrument Pitch Coarse #61 3134=Drum Instrument Pitch Coarse #62 3135=Drum Instrument Pitch Coarse #63 3136=Drum Instrument Pitch Coarse #64 3137=Drum Instrument Pitch Coarse #65 3138=Drum Instrument Pitch Coarse #66 3139=Drum Instrument Pitch Coarse #67 3140=Drum Instrument Pitch Coarse #68 3141=Drum Instrument Pitch Coarse #69 3142=Drum Instrument Pitch Coarse #70 3143=Drum Instrument Pitch Coarse #71 3144=Drum Instrument Pitch Coarse #72 3145=Drum Instrument Pitch Coarse #73 3146=Drum Instrument Pitch Coarse #74 3147=Drum Instrument Pitch Coarse #75 3148=Drum Instrument Pitch Coarse #76 3149=Drum Instrument Pitch Coarse #77 3150=Drum Instrument Pitch Coarse #78 3151=Drum Instrument Pitch Coarse #79 3152=Drum Instrument Pitch Coarse #80 3153=Drum Instrument Pitch Coarse #81 3154=Drum Instrument Pitch Coarse #82 3155=Drum Instrument Pitch Coarse #83 3156=Drum Instrument Pitch Coarse #84 3157=Drum Instrument Pitch Coarse #85 3158=Drum Instrument Pitch Coarse #86 3159=Drum Instrument Pitch Coarse #87 3160=Drum Instrument Pitch Coarse #88 3161=Drum Instrument Pitch Coarse #89 3162=Drum Instrument Pitch Coarse #90 3163=Drum Instrument Pitch Coarse #91 3164=Drum Instrument Pitch Coarse #92 3165=Drum Instrument Pitch Coarse #93 3166=Drum Instrument Pitch Coarse #94 3167=Drum Instrument Pitch Coarse #95 3168=Drum Instrument Pitch Coarse #96 3169=Drum Instrument Pitch Coarse #97 3170=Drum Instrument Pitch Coarse #98 3171=Drum Instrument Pitch Coarse #99 3172=Drum Instrument Pitch Coarse #100 3173=Drum Instrument Pitch Coarse #101 3174=Drum Instrument Pitch Coarse #102 3175=Drum Instrument Pitch Coarse #103 3176=Drum Instrument Pitch Coarse #104 3177=Drum Instrument Pitch Coarse #105 3178=Drum Instrument Pitch Coarse #106 3179=Drum Instrument Pitch Coarse #107 3180=Drum Instrument Pitch Coarse #108 3181=Drum Instrument Pitch Coarse #109 3182=Drum Instrument Pitch Coarse #110 3183=Drum Instrument Pitch Coarse #111 3184=Drum Instrument Pitch Coarse #112 3185=Drum Instrument Pitch Coarse #113 3186=Drum Instrument Pitch Coarse #114 3187=Drum Instrument Pitch Coarse #115 3188=Drum Instrument Pitch Coarse #116 3189=Drum Instrument Pitch Coarse #117 3190=Drum Instrument Pitch Coarse #118 3191=Drum Instrument Pitch Coarse #119 3192=Drum Instrument Pitch Coarse #120 3193=Drum Instrument Pitch Coarse #121 3194=Drum Instrument Pitch Coarse #122 3195=Drum Instrument Pitch Coarse #123 3196=Drum Instrument Pitch Coarse #124 3197=Drum Instrument Pitch Coarse #125 3198=Drum Instrument Pitch Coarse #126 3199=Drum Instrument Pitch Coarse #127 3328=Drum Instrument TVA Level #0 3329=Drum Instrument TVA Level #1 3330=Drum Instrument TVA Level #2 3331=Drum Instrument TVA Level #3 3332=Drum Instrument TVA Level #4 3333=Drum Instrument TVA Level #5 3334=Drum Instrument TVA Level #6 3335=Drum Instrument TVA Level #7 3336=Drum Instrument TVA Level #8 3337=Drum Instrument TVA Level #9 3338=Drum Instrument TVA Level #10 3339=Drum Instrument TVA Level #11 3340=Drum Instrument TVA Level #12 3341=Drum Instrument TVA Level #13 3342=Drum Instrument TVA Level #14 3343=Drum Instrument TVA Level #15 3344=Drum Instrument TVA Level #16 3345=Drum Instrument TVA Level #17 3346=Drum Instrument TVA Level #18 3347=Drum Instrument TVA Level #19 3348=Drum Instrument TVA Level #20 3349=Drum Instrument TVA Level #21 3350=Drum Instrument TVA Level #22 3351=Drum Instrument TVA Level #23 3352=Drum Instrument TVA Level #24 3353=Drum Instrument TVA Level #25 3354=Drum Instrument TVA Level #26 3355=Drum Instrument TVA Level #27 3356=Drum Instrument TVA Level #28 3357=Drum Instrument TVA Level #29 3358=Drum Instrument TVA Level #30 3359=Drum Instrument TVA Level #31 3360=Drum Instrument TVA Level #32 3361=Drum Instrument TVA Level #33 3362=Drum Instrument TVA Level #34 3363=Drum Instrument TVA Level #35 3364=Drum Instrument TVA Level #36 3365=Drum Instrument TVA Level #37 3366=Drum Instrument TVA Level #38 3367=Drum Instrument TVA Level #39 3368=Drum Instrument TVA Level #40 3369=Drum Instrument TVA Level #41 3370=Drum Instrument TVA Level #42 3371=Drum Instrument TVA Level #43 3372=Drum Instrument TVA Level #44 3373=Drum Instrument TVA Level #45 3374=Drum Instrument TVA Level #46 3375=Drum Instrument TVA Level #47 3376=Drum Instrument TVA Level #48 3377=Drum Instrument TVA Level #49 3378=Drum Instrument TVA Level #50 3379=Drum Instrument TVA Level #51 3380=Drum Instrument TVA Level #52 3381=Drum Instrument TVA Level #53 3382=Drum Instrument TVA Level #54 3383=Drum Instrument TVA Level #55 3384=Drum Instrument TVA Level #56 3385=Drum Instrument TVA Level #57 3386=Drum Instrument TVA Level #58 3387=Drum Instrument TVA Level #59 3388=Drum Instrument TVA Level #60 3389=Drum Instrument TVA Level #61 3390=Drum Instrument TVA Level #62 3391=Drum Instrument TVA Level #63 3392=Drum Instrument TVA Level #64 3393=Drum Instrument TVA Level #65 3394=Drum Instrument TVA Level #66 3395=Drum Instrument TVA Level #67 3396=Drum Instrument TVA Level #68 3397=Drum Instrument TVA Level #69 3398=Drum Instrument TVA Level #70 3399=Drum Instrument TVA Level #71 3400=Drum Instrument TVA Level #72 3401=Drum Instrument TVA Level #73 3402=Drum Instrument TVA Level #74 3403=Drum Instrument TVA Level #75 3404=Drum Instrument TVA Level #76 3405=Drum Instrument TVA Level #77 3406=Drum Instrument TVA Level #78 3407=Drum Instrument TVA Level #79 3408=Drum Instrument TVA Level #80 3409=Drum Instrument TVA Level #81 3410=Drum Instrument TVA Level #82 3411=Drum Instrument TVA Level #83 3412=Drum Instrument TVA Level #84 3413=Drum Instrument TVA Level #85 3414=Drum Instrument TVA Level #86 3415=Drum Instrument TVA Level #87 3416=Drum Instrument TVA Level #88 3417=Drum Instrument TVA Level #89 3418=Drum Instrument TVA Level #90 3419=Drum Instrument TVA Level #91 3420=Drum Instrument TVA Level #92 3421=Drum Instrument TVA Level #93 3422=Drum Instrument TVA Level #94 3423=Drum Instrument TVA Level #95 3424=Drum Instrument TVA Level #96 3425=Drum Instrument TVA Level #97 3426=Drum Instrument TVA Level #98 3427=Drum Instrument TVA Level #99 3428=Drum Instrument TVA Level #100 3429=Drum Instrument TVA Level #101 3430=Drum Instrument TVA Level #102 3431=Drum Instrument TVA Level #103 3432=Drum Instrument TVA Level #104 3433=Drum Instrument TVA Level #105 3434=Drum Instrument TVA Level #106 3435=Drum Instrument TVA Level #107 3436=Drum Instrument TVA Level #108 3437=Drum Instrument TVA Level #109 3438=Drum Instrument TVA Level #110 3439=Drum Instrument TVA Level #111 3440=Drum Instrument TVA Level #112 3441=Drum Instrument TVA Level #113 3442=Drum Instrument TVA Level #114 3443=Drum Instrument TVA Level #115 3444=Drum Instrument TVA Level #116 3445=Drum Instrument TVA Level #117 3446=Drum Instrument TVA Level #118 3447=Drum Instrument TVA Level #119 3448=Drum Instrument TVA Level #120 3449=Drum Instrument TVA Level #121 3450=Drum Instrument TVA Level #122 3451=Drum Instrument TVA Level #123 3452=Drum Instrument TVA Level #124 3453=Drum Instrument TVA Level #125 3454=Drum Instrument TVA Level #126 3455=Drum Instrument TVA Level #127 3584=Drum Instrument Panpot #0 3585=Drum Instrument Panpot #1 3586=Drum Instrument Panpot #2 3587=Drum Instrument Panpot #3 3588=Drum Instrument Panpot #4 3589=Drum Instrument Panpot #5 3590=Drum Instrument Panpot #6 3591=Drum Instrument Panpot #7 3592=Drum Instrument Panpot #8 3593=Drum Instrument Panpot #9 3594=Drum Instrument Panpot #10 3595=Drum Instrument Panpot #11 3596=Drum Instrument Panpot #12 3597=Drum Instrument Panpot #13 3598=Drum Instrument Panpot #14 3599=Drum Instrument Panpot #15 3600=Drum Instrument Panpot #16 3601=Drum Instrument Panpot #17 3602=Drum Instrument Panpot #18 3603=Drum Instrument Panpot #19 3604=Drum Instrument Panpot #20 3605=Drum Instrument Panpot #21 3606=Drum Instrument Panpot #22 3607=Drum Instrument Panpot #23 3608=Drum Instrument Panpot #24 3609=Drum Instrument Panpot #25 3610=Drum Instrument Panpot #26 3611=Drum Instrument Panpot #27 3612=Drum Instrument Panpot #28 3613=Drum Instrument Panpot #29 3614=Drum Instrument Panpot #30 3615=Drum Instrument Panpot #31 3616=Drum Instrument Panpot #32 3617=Drum Instrument Panpot #33 3618=Drum Instrument Panpot #34 3619=Drum Instrument Panpot #35 3620=Drum Instrument Panpot #36 3621=Drum Instrument Panpot #37 3622=Drum Instrument Panpot #38 3623=Drum Instrument Panpot #39 3624=Drum Instrument Panpot #40 3625=Drum Instrument Panpot #41 3626=Drum Instrument Panpot #42 3627=Drum Instrument Panpot #43 3628=Drum Instrument Panpot #44 3629=Drum Instrument Panpot #45 3630=Drum Instrument Panpot #46 3631=Drum Instrument Panpot #47 3632=Drum Instrument Panpot #48 3633=Drum Instrument Panpot #49 3634=Drum Instrument Panpot #50 3635=Drum Instrument Panpot #51 3636=Drum Instrument Panpot #52 3637=Drum Instrument Panpot #53 3638=Drum Instrument Panpot #54 3639=Drum Instrument Panpot #55 3640=Drum Instrument Panpot #56 3641=Drum Instrument Panpot #57 3642=Drum Instrument Panpot #58 3643=Drum Instrument Panpot #59 3644=Drum Instrument Panpot #60 3645=Drum Instrument Panpot #61 3646=Drum Instrument Panpot #62 3647=Drum Instrument Panpot #63 3648=Drum Instrument Panpot #64 3649=Drum Instrument Panpot #65 3650=Drum Instrument Panpot #66 3651=Drum Instrument Panpot #67 3652=Drum Instrument Panpot #68 3653=Drum Instrument Panpot #69 3654=Drum Instrument Panpot #70 3655=Drum Instrument Panpot #71 3656=Drum Instrument Panpot #72 3657=Drum Instrument Panpot #73 3658=Drum Instrument Panpot #74 3659=Drum Instrument Panpot #75 3660=Drum Instrument Panpot #76 3661=Drum Instrument Panpot #77 3662=Drum Instrument Panpot #78 3663=Drum Instrument Panpot #79 3664=Drum Instrument Panpot #80 3665=Drum Instrument Panpot #81 3666=Drum Instrument Panpot #82 3667=Drum Instrument Panpot #83 3668=Drum Instrument Panpot #84 3669=Drum Instrument Panpot #85 3670=Drum Instrument Panpot #86 3671=Drum Instrument Panpot #87 3672=Drum Instrument Panpot #88 3673=Drum Instrument Panpot #89 3674=Drum Instrument Panpot #90 3675=Drum Instrument Panpot #91 3676=Drum Instrument Panpot #92 3677=Drum Instrument Panpot #93 3678=Drum Instrument Panpot #94 3679=Drum Instrument Panpot #95 3680=Drum Instrument Panpot #96 3681=Drum Instrument Panpot #97 3682=Drum Instrument Panpot #98 3683=Drum Instrument Panpot #99 3684=Drum Instrument Panpot #100 3685=Drum Instrument Panpot #101 3686=Drum Instrument Panpot #102 3687=Drum Instrument Panpot #103 3688=Drum Instrument Panpot #104 3689=Drum Instrument Panpot #105 3690=Drum Instrument Panpot #106 3691=Drum Instrument Panpot #107 3692=Drum Instrument Panpot #108 3693=Drum Instrument Panpot #109 3694=Drum Instrument Panpot #110 3695=Drum Instrument Panpot #111 3696=Drum Instrument Panpot #112 3697=Drum Instrument Panpot #113 3698=Drum Instrument Panpot #114 3699=Drum Instrument Panpot #115 3700=Drum Instrument Panpot #116 3701=Drum Instrument Panpot #117 3702=Drum Instrument Panpot #118 3703=Drum Instrument Panpot #119 3704=Drum Instrument Panpot #120 3705=Drum Instrument Panpot #121 3706=Drum Instrument Panpot #122 3707=Drum Instrument Panpot #123 3708=Drum Instrument Panpot #124 3709=Drum Instrument Panpot #125 3710=Drum Instrument Panpot #126 3711=Drum Instrument Panpot #127 3712=Drum Instrument Reverb Send Level #0 3713=Drum Instrument Reverb Send Level #1 3714=Drum Instrument Reverb Send Level #2 3715=Drum Instrument Reverb Send Level #3 3716=Drum Instrument Reverb Send Level #4 3717=Drum Instrument Reverb Send Level #5 3718=Drum Instrument Reverb Send Level #6 3719=Drum Instrument Reverb Send Level #7 3720=Drum Instrument Reverb Send Level #8 3721=Drum Instrument Reverb Send Level #9 3722=Drum Instrument Reverb Send Level #10 3723=Drum Instrument Reverb Send Level #11 3724=Drum Instrument Reverb Send Level #12 3725=Drum Instrument Reverb Send Level #13 3726=Drum Instrument Reverb Send Level #14 3727=Drum Instrument Reverb Send Level #15 3728=Drum Instrument Reverb Send Level #16 3729=Drum Instrument Reverb Send Level #17 3730=Drum Instrument Reverb Send Level #18 3731=Drum Instrument Reverb Send Level #19 3732=Drum Instrument Reverb Send Level #20 3733=Drum Instrument Reverb Send Level #21 3734=Drum Instrument Reverb Send Level #22 3735=Drum Instrument Reverb Send Level #23 3736=Drum Instrument Reverb Send Level #24 3737=Drum Instrument Reverb Send Level #25 3738=Drum Instrument Reverb Send Level #26 3739=Drum Instrument Reverb Send Level #27 3740=Drum Instrument Reverb Send Level #28 3741=Drum Instrument Reverb Send Level #29 3742=Drum Instrument Reverb Send Level #30 3743=Drum Instrument Reverb Send Level #31 3744=Drum Instrument Reverb Send Level #32 3745=Drum Instrument Reverb Send Level #33 3746=Drum Instrument Reverb Send Level #34 3747=Drum Instrument Reverb Send Level #35 3748=Drum Instrument Reverb Send Level #36 3749=Drum Instrument Reverb Send Level #37 3750=Drum Instrument Reverb Send Level #38 3751=Drum Instrument Reverb Send Level #39 3752=Drum Instrument Reverb Send Level #40 3753=Drum Instrument Reverb Send Level #41 3754=Drum Instrument Reverb Send Level #42 3755=Drum Instrument Reverb Send Level #43 3756=Drum Instrument Reverb Send Level #44 3757=Drum Instrument Reverb Send Level #45 3758=Drum Instrument Reverb Send Level #46 3759=Drum Instrument Reverb Send Level #47 3760=Drum Instrument Reverb Send Level #48 3761=Drum Instrument Reverb Send Level #49 3762=Drum Instrument Reverb Send Level #50 3763=Drum Instrument Reverb Send Level #51 3764=Drum Instrument Reverb Send Level #52 3765=Drum Instrument Reverb Send Level #53 3766=Drum Instrument Reverb Send Level #54 3767=Drum Instrument Reverb Send Level #55 3768=Drum Instrument Reverb Send Level #56 3769=Drum Instrument Reverb Send Level #57 3770=Drum Instrument Reverb Send Level #58 3771=Drum Instrument Reverb Send Level #59 3772=Drum Instrument Reverb Send Level #60 3773=Drum Instrument Reverb Send Level #61 3774=Drum Instrument Reverb Send Level #62 3775=Drum Instrument Reverb Send Level #63 3776=Drum Instrument Reverb Send Level #64 3777=Drum Instrument Reverb Send Level #65 3778=Drum Instrument Reverb Send Level #66 3779=Drum Instrument Reverb Send Level #67 3780=Drum Instrument Reverb Send Level #68 3781=Drum Instrument Reverb Send Level #69 3782=Drum Instrument Reverb Send Level #70 3783=Drum Instrument Reverb Send Level #71 3784=Drum Instrument Reverb Send Level #72 3785=Drum Instrument Reverb Send Level #73 3786=Drum Instrument Reverb Send Level #74 3787=Drum Instrument Reverb Send Level #75 3788=Drum Instrument Reverb Send Level #76 3789=Drum Instrument Reverb Send Level #77 3790=Drum Instrument Reverb Send Level #78 3791=Drum Instrument Reverb Send Level #79 3792=Drum Instrument Reverb Send Level #80 3793=Drum Instrument Reverb Send Level #81 3794=Drum Instrument Reverb Send Level #82 3795=Drum Instrument Reverb Send Level #83 3796=Drum Instrument Reverb Send Level #84 3797=Drum Instrument Reverb Send Level #85 3798=Drum Instrument Reverb Send Level #86 3799=Drum Instrument Reverb Send Level #87 3800=Drum Instrument Reverb Send Level #88 3801=Drum Instrument Reverb Send Level #89 3802=Drum Instrument Reverb Send Level #90 3803=Drum Instrument Reverb Send Level #91 3804=Drum Instrument Reverb Send Level #92 3805=Drum Instrument Reverb Send Level #93 3806=Drum Instrument Reverb Send Level #94 3807=Drum Instrument Reverb Send Level #95 3808=Drum Instrument Reverb Send Level #96 3809=Drum Instrument Reverb Send Level #97 3810=Drum Instrument Reverb Send Level #98 3811=Drum Instrument Reverb Send Level #99 3812=Drum Instrument Reverb Send Level #100 3813=Drum Instrument Reverb Send Level #101 3814=Drum Instrument Reverb Send Level #102 3815=Drum Instrument Reverb Send Level #103 3816=Drum Instrument Reverb Send Level #104 3817=Drum Instrument Reverb Send Level #105 3818=Drum Instrument Reverb Send Level #106 3819=Drum Instrument Reverb Send Level #107 3820=Drum Instrument Reverb Send Level #108 3821=Drum Instrument Reverb Send Level #109 3822=Drum Instrument Reverb Send Level #110 3823=Drum Instrument Reverb Send Level #111 3824=Drum Instrument Reverb Send Level #112 3825=Drum Instrument Reverb Send Level #113 3826=Drum Instrument Reverb Send Level #114 3827=Drum Instrument Reverb Send Level #115 3828=Drum Instrument Reverb Send Level #116 3829=Drum Instrument Reverb Send Level #117 3830=Drum Instrument Reverb Send Level #118 3831=Drum Instrument Reverb Send Level #119 3832=Drum Instrument Reverb Send Level #120 3833=Drum Instrument Reverb Send Level #121 3834=Drum Instrument Reverb Send Level #122 3835=Drum Instrument Reverb Send Level #123 3836=Drum Instrument Reverb Send Level #124 3837=Drum Instrument Reverb Send Level #125 3838=Drum Instrument Reverb Send Level #126 3839=Drum Instrument Reverb Send Level #127 3840=Drum Instrument Chorus Send Level #0 3841=Drum Instrument Chorus Send Level #1 3842=Drum Instrument Chorus Send Level #2 3843=Drum Instrument Chorus Send Level #3 3844=Drum Instrument Chorus Send Level #4 3845=Drum Instrument Chorus Send Level #5 3846=Drum Instrument Chorus Send Level #6 3847=Drum Instrument Chorus Send Level #7 3848=Drum Instrument Chorus Send Level #8 3849=Drum Instrument Chorus Send Level #9 3850=Drum Instrument Chorus Send Level #10 3851=Drum Instrument Chorus Send Level #11 3852=Drum Instrument Chorus Send Level #12 3853=Drum Instrument Chorus Send Level #13 3854=Drum Instrument Chorus Send Level #14 3855=Drum Instrument Chorus Send Level #15 3856=Drum Instrument Chorus Send Level #16 3857=Drum Instrument Chorus Send Level #17 3858=Drum Instrument Chorus Send Level #18 3859=Drum Instrument Chorus Send Level #19 3860=Drum Instrument Chorus Send Level #20 3861=Drum Instrument Chorus Send Level #21 3862=Drum Instrument Chorus Send Level #22 3863=Drum Instrument Chorus Send Level #23 3864=Drum Instrument Chorus Send Level #24 3865=Drum Instrument Chorus Send Level #25 3866=Drum Instrument Chorus Send Level #26 3867=Drum Instrument Chorus Send Level #27 3868=Drum Instrument Chorus Send Level #28 3869=Drum Instrument Chorus Send Level #29 3870=Drum Instrument Chorus Send Level #30 3871=Drum Instrument Chorus Send Level #31 3872=Drum Instrument Chorus Send Level #32 3873=Drum Instrument Chorus Send Level #33 3874=Drum Instrument Chorus Send Level #34 3875=Drum Instrument Chorus Send Level #35 3876=Drum Instrument Chorus Send Level #36 3877=Drum Instrument Chorus Send Level #37 3878=Drum Instrument Chorus Send Level #38 3879=Drum Instrument Chorus Send Level #39 3880=Drum Instrument Chorus Send Level #40 3881=Drum Instrument Chorus Send Level #41 3882=Drum Instrument Chorus Send Level #42 3883=Drum Instrument Chorus Send Level #43 3884=Drum Instrument Chorus Send Level #44 3885=Drum Instrument Chorus Send Level #45 3886=Drum Instrument Chorus Send Level #46 3887=Drum Instrument Chorus Send Level #47 3888=Drum Instrument Chorus Send Level #48 3889=Drum Instrument Chorus Send Level #49 3890=Drum Instrument Chorus Send Level #50 3891=Drum Instrument Chorus Send Level #51 3892=Drum Instrument Chorus Send Level #52 3893=Drum Instrument Chorus Send Level #53 3894=Drum Instrument Chorus Send Level #54 3895=Drum Instrument Chorus Send Level #55 3896=Drum Instrument Chorus Send Level #56 3897=Drum Instrument Chorus Send Level #57 3898=Drum Instrument Chorus Send Level #58 3899=Drum Instrument Chorus Send Level #59 3900=Drum Instrument Chorus Send Level #60 3901=Drum Instrument Chorus Send Level #61 3902=Drum Instrument Chorus Send Level #62 3903=Drum Instrument Chorus Send Level #63 3904=Drum Instrument Chorus Send Level #64 3905=Drum Instrument Chorus Send Level #65 3906=Drum Instrument Chorus Send Level #66 3907=Drum Instrument Chorus Send Level #67 3908=Drum Instrument Chorus Send Level #68 3909=Drum Instrument Chorus Send Level #69 3910=Drum Instrument Chorus Send Level #70 3911=Drum Instrument Chorus Send Level #71 3912=Drum Instrument Chorus Send Level #72 3913=Drum Instrument Chorus Send Level #73 3914=Drum Instrument Chorus Send Level #74 3915=Drum Instrument Chorus Send Level #75 3916=Drum Instrument Chorus Send Level #76 3917=Drum Instrument Chorus Send Level #77 3918=Drum Instrument Chorus Send Level #78 3919=Drum Instrument Chorus Send Level #79 3920=Drum Instrument Chorus Send Level #80 3921=Drum Instrument Chorus Send Level #81 3922=Drum Instrument Chorus Send Level #82 3923=Drum Instrument Chorus Send Level #83 3924=Drum Instrument Chorus Send Level #84 3925=Drum Instrument Chorus Send Level #85 3926=Drum Instrument Chorus Send Level #86 3927=Drum Instrument Chorus Send Level #87 3928=Drum Instrument Chorus Send Level #88 3929=Drum Instrument Chorus Send Level #89 3930=Drum Instrument Chorus Send Level #90 3931=Drum Instrument Chorus Send Level #91 3932=Drum Instrument Chorus Send Level #92 3933=Drum Instrument Chorus Send Level #93 3934=Drum Instrument Chorus Send Level #94 3935=Drum Instrument Chorus Send Level #95 3936=Drum Instrument Chorus Send Level #96 3937=Drum Instrument Chorus Send Level #97 3938=Drum Instrument Chorus Send Level #98 3939=Drum Instrument Chorus Send Level #99 3940=Drum Instrument Chorus Send Level #100 3941=Drum Instrument Chorus Send Level #101 3942=Drum Instrument Chorus Send Level #102 3943=Drum Instrument Chorus Send Level #103 3944=Drum Instrument Chorus Send Level #104 3945=Drum Instrument Chorus Send Level #105 3946=Drum Instrument Chorus Send Level #106 3947=Drum Instrument Chorus Send Level #107 3948=Drum Instrument Chorus Send Level #108 3949=Drum Instrument Chorus Send Level #109 3950=Drum Instrument Chorus Send Level #110 3951=Drum Instrument Chorus Send Level #111 3952=Drum Instrument Chorus Send Level #112 3953=Drum Instrument Chorus Send Level #113 3954=Drum Instrument Chorus Send Level #114 3955=Drum Instrument Chorus Send Level #115 3956=Drum Instrument Chorus Send Level #116 3957=Drum Instrument Chorus Send Level #117 3958=Drum Instrument Chorus Send Level #118 3959=Drum Instrument Chorus Send Level #119 3960=Drum Instrument Chorus Send Level #120 3961=Drum Instrument Chorus Send Level #121 3962=Drum Instrument Chorus Send Level #122 3963=Drum Instrument Chorus Send Level #123 3964=Drum Instrument Chorus Send Level #124 3965=Drum Instrument Chorus Send Level #125 3966=Drum Instrument Chorus Send Level #126 3967=Drum Instrument Chorus Send Level #127 3968=Drum Instrument Delay Send Level #0 3969=Drum Instrument Delay Send Level #1 3970=Drum Instrument Delay Send Level #2 3971=Drum Instrument Delay Send Level #3 3972=Drum Instrument Delay Send Level #4 3973=Drum Instrument Delay Send Level #5 3974=Drum Instrument Delay Send Level #6 3975=Drum Instrument Delay Send Level #7 3976=Drum Instrument Delay Send Level #8 3977=Drum Instrument Delay Send Level #9 3978=Drum Instrument Delay Send Level #10 3979=Drum Instrument Delay Send Level #11 3980=Drum Instrument Delay Send Level #12 3981=Drum Instrument Delay Send Level #13 3982=Drum Instrument Delay Send Level #14 3983=Drum Instrument Delay Send Level #15 3984=Drum Instrument Delay Send Level #16 3985=Drum Instrument Delay Send Level #17 3986=Drum Instrument Delay Send Level #18 3987=Drum Instrument Delay Send Level #19 3988=Drum Instrument Delay Send Level #20 3989=Drum Instrument Delay Send Level #21 3990=Drum Instrument Delay Send Level #22 3991=Drum Instrument Delay Send Level #23 3992=Drum Instrument Delay Send Level #24 3993=Drum Instrument Delay Send Level #25 3994=Drum Instrument Delay Send Level #26 3995=Drum Instrument Delay Send Level #27 3996=Drum Instrument Delay Send Level #28 3997=Drum Instrument Delay Send Level #29 3998=Drum Instrument Delay Send Level #30 3999=Drum Instrument Delay Send Level #31 4000=Drum Instrument Delay Send Level #32 4001=Drum Instrument Delay Send Level #33 4002=Drum Instrument Delay Send Level #34 4003=Drum Instrument Delay Send Level #35 4004=Drum Instrument Delay Send Level #36 4005=Drum Instrument Delay Send Level #37 4006=Drum Instrument Delay Send Level #38 4007=Drum Instrument Delay Send Level #39 4008=Drum Instrument Delay Send Level #40 4009=Drum Instrument Delay Send Level #41 4010=Drum Instrument Delay Send Level #42 4011=Drum Instrument Delay Send Level #43 4012=Drum Instrument Delay Send Level #44 4013=Drum Instrument Delay Send Level #45 4014=Drum Instrument Delay Send Level #46 4015=Drum Instrument Delay Send Level #47 4016=Drum Instrument Delay Send Level #48 4017=Drum Instrument Delay Send Level #49 4018=Drum Instrument Delay Send Level #50 4019=Drum Instrument Delay Send Level #51 4020=Drum Instrument Delay Send Level #52 4021=Drum Instrument Delay Send Level #53 4022=Drum Instrument Delay Send Level #54 4023=Drum Instrument Delay Send Level #55 4024=Drum Instrument Delay Send Level #56 4025=Drum Instrument Delay Send Level #57 4026=Drum Instrument Delay Send Level #58 4027=Drum Instrument Delay Send Level #59 4028=Drum Instrument Delay Send Level #60 4029=Drum Instrument Delay Send Level #61 4030=Drum Instrument Delay Send Level #62 4031=Drum Instrument Delay Send Level #63 4032=Drum Instrument Delay Send Level #64 4033=Drum Instrument Delay Send Level #65 4034=Drum Instrument Delay Send Level #66 4035=Drum Instrument Delay Send Level #67 4036=Drum Instrument Delay Send Level #68 4037=Drum Instrument Delay Send Level #69 4038=Drum Instrument Delay Send Level #70 4039=Drum Instrument Delay Send Level #71 4040=Drum Instrument Delay Send Level #72 4041=Drum Instrument Delay Send Level #73 4042=Drum Instrument Delay Send Level #74 4043=Drum Instrument Delay Send Level #75 4044=Drum Instrument Delay Send Level #76 4045=Drum Instrument Delay Send Level #77 4046=Drum Instrument Delay Send Level #78 4047=Drum Instrument Delay Send Level #79 4048=Drum Instrument Delay Send Level #80 4049=Drum Instrument Delay Send Level #81 4050=Drum Instrument Delay Send Level #82 4051=Drum Instrument Delay Send Level #83 4052=Drum Instrument Delay Send Level #84 4053=Drum Instrument Delay Send Level #85 4054=Drum Instrument Delay Send Level #86 4055=Drum Instrument Delay Send Level #87 4056=Drum Instrument Delay Send Level #88 4057=Drum Instrument Delay Send Level #89 4058=Drum Instrument Delay Send Level #90 4059=Drum Instrument Delay Send Level #91 4060=Drum Instrument Delay Send Level #92 4061=Drum Instrument Delay Send Level #93 4062=Drum Instrument Delay Send Level #94 4063=Drum Instrument Delay Send Level #95 4064=Drum Instrument Delay Send Level #96 4065=Drum Instrument Delay Send Level #97 4066=Drum Instrument Delay Send Level #98 4067=Drum Instrument Delay Send Level #99 4068=Drum Instrument Delay Send Level #100 4069=Drum Instrument Delay Send Level #101 4070=Drum Instrument Delay Send Level #102 4071=Drum Instrument Delay Send Level #103 4072=Drum Instrument Delay Send Level #104 4073=Drum Instrument Delay Send Level #105 4074=Drum Instrument Delay Send Level #106 4075=Drum Instrument Delay Send Level #107 4076=Drum Instrument Delay Send Level #108 4077=Drum Instrument Delay Send Level #109 4078=Drum Instrument Delay Send Level #110 4079=Drum Instrument Delay Send Level #111 4080=Drum Instrument Delay Send Level #112 4081=Drum Instrument Delay Send Level #113 4082=Drum Instrument Delay Send Level #114 4083=Drum Instrument Delay Send Level #115 4084=Drum Instrument Delay Send Level #116 4085=Drum Instrument Delay Send Level #117 4086=Drum Instrument Delay Send Level #118 4087=Drum Instrument Delay Send Level #119 4088=Drum Instrument Delay Send Level #120 4089=Drum Instrument Delay Send Level #121 4090=Drum Instrument Delay Send Level #122 4091=Drum Instrument Delay Send Level #123 4092=Drum Instrument Delay Send Level #124 4093=Drum Instrument Delay Send Level #125 4094=Drum Instrument Delay Send Level #126 4095=Drum Instrument Delay Send Level #127 ; ----------------------------------------------------------------------------- .Instrument Definitions [General MIDI] Patch[*]=General MIDI [General MIDI Drums] Patch[*]=1..128 Key[*,*]=General MIDI Drums Drum[*,*]=1 [Roland GS] Control=Roland GS Controllers NRPN=Roland GS NRPN Patch[*]=1..128 Patch[0]=Roland GS Capital Tones Patch[128]=Roland GS Var #01 Patch[256]=Roland GS Var #02 Patch[384]=Roland GS Var #03 Patch[512]=Roland GS Var #04 Patch[640]=Roland GS Var #05 Patch[768]=Roland GS Var #06 Patch[896]=Roland GS Var #07 Patch[1024]=Roland GS Var #08 Patch[1152]=Roland GS Var #09 Patch[2048]=Roland GS Var #16 Patch[3072]=Roland GS Var #24 Patch[4096]=Roland GS Var #32 [Roland GS Drumsets] BankSelMethod=1 Control=Roland GS Controllers NRPN=Roland GS NRPN Patch[*]=1..128 Patch[0]=Roland GS Drumsets Key[*,*]=0..127 Key[0,0]=Roland GS Standard Set Key[0,8]=Roland GS Room Set Key[0,16]=Roland GS Power Set Key[0,24]=Roland GS Electronic Set Key[0,25]=Roland GS TR-808 Set Key[0,32]=Roland GS Jazz Set Key[0,40]=Roland GS Brush Set Key[0,48]=Roland GS Orchestra Set Key[0,56]=Roland GS SFX Set Drum[*,*]=1 [Yamaha XG] Control=Yamaha XG Controllers Patch[*]=XG Bank 0 Patch[0]=XG Bank 0 Patch[1]=XG Bank 1 (KSP) Patch[3]=XG Bank 3 (Stereo) Patch[6]=XG Bank 6 (Single) Patch[8]=XG Bank 8 (Slow) Patch[12]=XG Bank 12 (Fast Decay) Patch[14]=XG Bank 14 (Double Attack) Patch[16]=XG Bank 16 (Bright) Patch[17]=XG Bank 17 Patch[18]=XG Bank 18 (Dark) Patch[19]=XG Bank 19 Patch[20]=XG Bank 20 (Rsonant) Patch[24]=XG Bank 24 (Attack) Patch[25]=XG Bank 25 (Release) Patch[27]=XG Bank 27 (Rezo Sweep) Patch[28]=XG Bank 28 (Muted) Patch[32]=XG Bank 32 (Detune 1) Patch[33]=XG Bank 33 (Detune 2) Patch[34]=XG Bank 34 (Detune 3) Patch[35]=XG Bank 35 (Octave 1) Patch[36]=XG Bank 36 (Octave 2) Patch[37]=XG Bank 37 (5th 1) Patch[38]=XG Bank 38 (5th 2) Patch[39]=XG Bank 39 (Bend) Patch[40]=XG Bank 40 (Tutti) Patch[41]=XG Bank 41 Patch[42]=XG Bank 42 Patch[43]=XG Bank 43 (Velo-Switch) Patch[45]=XG Bank 45 (Velo-Xfade) Patch[64]=XG Bank 64 (other wave) Patch[65]=XG Bank 65 Patch[66]=XG Bank 66 Patch[67]=XG Bank 67 Patch[68]=XG Bank 68 Patch[69]=XG Bank 69 Patch[70]=XG Bank 70 Patch[71]=XG Bank 71 Patch[72]=XG Bank 72 Patch[96]=XG Bank 96 Patch[97]=XG Bank 97 Patch[98]=XG Bank 98 Patch[99]=XG Bank 99 Patch[100]=XG Bank 100 Patch[101]=XG Bank 101 Patch[896]=XG Set channel to rhythm part Patch[8192]=XG SFX Bank Patch[16128]=XG SFX Kits Patch[16256]=XG Drum Kits Key[16128,0]=XG SFX 1 Key[16128,1]=XG SFX 2 Key[16256,0]=XG Standard Kit Key[16256,1]=XG Standard2 Kit Key[16256,8]=XG Room Kit Key[16256,16]=XG Rock Kit Key[16256,24]=XG Electro Kit Key[16256,25]=XG Analog Kit Key[16256,32]=XG Jazz Kit Key[16256,40]=XG Brush Kit Key[16256,48]=XG Classic Kit Drum[16256,*]=1 [Yamaha XG Drum Kits] Control=Yamaha XG Controllers Patch[*]=No Drums Patch[896]=XG Set channel to rhythm part Patch[16128]=XG SFX Kits Patch[16256]=XG Drum Kits Key[*,0]=XG Standard Kit Key[*,1]=XG Standard2 Kit Key[*,8]=XG Room Kit Key[*,16]=XG Rock Kit Key[*,24]=XG Electro Kit Key[*,25]=XG Analog Kit Key[*,32]=XG Jazz Kit Key[*,40]=XG Brush Kit Key[*,48]=XG Classic Kit Key[16128,0]=XG SFX 1 Key[16128,1]=XG SFX 2 Key[16256,0]=XG Standard Kit Key[16256,1]=XG Standard2 Kit Key[16256,8]=XG Room Kit Key[16256,16]=XG Rock Kit Key[16256,24]=XG Electro Kit Key[16256,25]=XG Analog Kit Key[16256,32]=XG Jazz Kit Key[16256,40]=XG Brush Kit Key[16256,48]=XG Classic Kit Drum[*,*]=1 Drum[*,0]=1 qtractor-1.5.9/src/PaxHeaders/qtractorMidiControlForm.h0000644000000000000000000000013215101070305020222 xustar0030 mtime=1761898693.078267626 30 atime=1761898693.077267623 30 ctime=1761898693.078267626 qtractor-1.5.9/src/qtractorMidiControlForm.h0000644000175000001440000000510015101070305020206 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.9/src/PaxHeaders/qtractorComboBox.h0000644000000000000000000000013215101070305016663 xustar0030 mtime=1761898693.067267591 30 atime=1761898693.067267591 30 ctime=1761898693.067267591 qtractor-1.5.9/src/qtractorComboBox.h0000644000175000001440000000450215101070305016654 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.9/src/PaxHeaders/qtractorMidiEditView.cpp0000644000000000000000000000013215101070305020031 xustar0030 mtime=1761898693.080267632 30 atime=1761898693.079267629 30 ctime=1761898693.080267632 qtractor-1.5.9/src/qtractorMidiEditView.cpp0000644000175000001440000006323015101070305020025 0ustar00rncbcusers// qtractorMidiEditView.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 "qtractorMidiEditView.h" #include "qtractorMidiEditor.h" #include "qtractorMidiEditList.h" #include "qtractorMidiEditTime.h" #include "qtractorMidiEditEvent.h" #include "qtractorMidiSequence.h" #include "qtractorMidiClip.h" #include "qtractorSession.h" #include "qtractorOptions.h" #include #include #include #include #include #include #include #ifdef CONFIG_GRADIENT #include #endif #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) #define horizontalAdvance width #endif //---------------------------------------------------------------------------- // qtractorMidiEditView -- MIDI sequence main view widget. // Constructor. qtractorMidiEditView::qtractorMidiEditView ( qtractorMidiEditor *pEditor, QWidget *pParent ) : qtractorScrollView(pParent) { m_pEditor = pEditor; m_eventType = qtractorMidiEvent::NOTEON; m_iNoteOn = -1; m_iNoteVel = -1; // Zoom tool widgets m_pVzoomIn = new QToolButton(this); m_pVzoomOut = new QToolButton(this); m_pVzoomReset = new QToolButton(this); m_pVzoomIn->setIcon(QIcon::fromTheme("viewZoomIn")); m_pVzoomOut->setIcon(QIcon::fromTheme("viewZoomOut")); m_pVzoomReset->setIcon(QIcon::fromTheme("viewZoomReset")); m_pVzoomIn->setAutoRepeat(true); m_pVzoomOut->setAutoRepeat(true); m_pVzoomIn->setToolTip(tr("Zoom in (vertical)")); m_pVzoomOut->setToolTip(tr("Zoom out (vertical)")); m_pVzoomReset->setToolTip(tr("Zoom reset (vertical)")); const int iScrollBarExtent = qtractorScrollView::style()->pixelMetric(QStyle::PM_ScrollBarExtent); m_pVzoomReset->setFixedHeight(iScrollBarExtent); m_pVzoomOut->setFixedHeight(iScrollBarExtent); m_pVzoomIn->setFixedHeight(iScrollBarExtent); qtractorScrollView::addScrollBarWidget(m_pVzoomReset, Qt::AlignBottom); qtractorScrollView::addScrollBarWidget(m_pVzoomOut, Qt::AlignBottom); qtractorScrollView::addScrollBarWidget(m_pVzoomIn, Qt::AlignBottom); QObject::connect(m_pVzoomIn, SIGNAL(clicked()), m_pEditor, SLOT(verticalZoomInSlot())); QObject::connect(m_pVzoomOut, SIGNAL(clicked()), m_pEditor, SLOT(verticalZoomOutSlot())); QObject::connect(m_pVzoomReset, SIGNAL(clicked()), m_pEditor, SLOT(verticalZoomResetSlot())); qtractorScrollView::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 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() - 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. qtractorMidiEditView::~qtractorMidiEditView (void) { } // Scrollbar/tools layout management. void qtractorMidiEditView::setVBarGeometry ( QScrollBar& vbar, int x, int y, int w, int h ) { vbar.setGeometry(x, y, w, h - w * 2); if (m_pVzoomIn) m_pVzoomIn->setGeometry(x, y + h - w * 2, w, w); if (m_pVzoomOut) m_pVzoomOut->setGeometry(x, y + h - w, w, w); } // Update track view content height. void qtractorMidiEditView::updateContentsHeight (void) { // Do the contents resize thing... qtractorScrollView::resizeContents( qtractorScrollView::contentsWidth(), m_pEditor->editList()->contentsHeight()); // updateContents(); } // Update track view content width. void qtractorMidiEditView::updateContentsWidth ( int iContentsWidth ) { // Do the contents resize thing... qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); if (pTimeScale) { qtractorMidiSequence *pSeq = m_pEditor->sequence(); if (pSeq) { const unsigned long t0 = pTimeScale->tickFromFrame(m_pEditor->offset()); const int x0 = pTimeScale->pixelFromFrame(m_pEditor->offset()); const int w0 = pTimeScale->pixelFromTick(t0 + pSeq->duration()) - x0; if (iContentsWidth < w0) iContentsWidth = w0; } iContentsWidth += pTimeScale->pixelFromBeat(pTimeScale->beatsPerBar()); if (iContentsWidth < qtractorScrollView::width()) iContentsWidth += qtractorScrollView::width(); } qtractorScrollView::resizeContents( iContentsWidth, qtractorScrollView::contentsHeight()); // Force an update on other views too... m_pEditor->editTime()->resizeContents( iContentsWidth + 100, m_pEditor->editTime()->viewport()->height()); // m_pEditor->editTime()->updateContents(); m_pEditor->editEvent()->resizeContents( iContentsWidth, m_pEditor->editEvent()->viewport()->height()); // m_pEditor->editEvent()->updateContents(); } // Local rectangular contents update. void qtractorMidiEditView::updateContents ( const QRect& rect ) { updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(rect); } // Overall contents update. void qtractorMidiEditView::updateContents (void) { updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(); } // Current event selection accessors. void qtractorMidiEditView::setEventType ( qtractorMidiEvent::EventType eventType ) { m_eventType = eventType; m_pEditor->updateContents(); } qtractorMidiEvent::EventType qtractorMidiEditView::eventType (void) const { return m_eventType; } // Single note-on handler. void qtractorMidiEditView::dragNoteOn ( int iNote, int iVelocity ) { // 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(); // Now for the sounding new one... if (iNote >= 0) { // This stands for the keyboard area... QWidget *pViewport = qtractorScrollView::viewport(); const int wk = pViewport->width(); const int hk = m_pEditor->editList()->itemHeight(); const int xk = qtractorScrollView::contentsX(); const int yk = ((127 - iNote) * hk) + 1; // This is the new note on... m_iNoteOn = iNote; m_iNoteVel = iVelocity; m_rectNote.setRect(xk, yk, wk, hk); // Otherwise, reset any pending note... qtractorScrollView::viewport()->update( QRect(contentsToViewport(m_rectNote.topLeft()), m_rectNote.size())); } } // Single note-on handler. void qtractorMidiEditView::dragNoteOff (void) { if (m_iNoteOn < 0) return; // Turn off old note... m_iNoteOn = m_iNoteVel = -1; qtractorScrollView::viewport()->update( QRect(contentsToViewport(m_rectNote.topLeft()), m_rectNote.size())); } // Resize event handler. void qtractorMidiEditView::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, 20); const QColor rgba2(0, 0, 0, 80); 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 // Scrollbar/tools layout management. const QSize& size = qtractorScrollView::size(); QScrollBar *pVScrollBar = qtractorScrollView::verticalScrollBar(); const int w = pVScrollBar->width(); updateContents(); m_pEditor->editEventScale()->setFixedWidth( m_pEditor->width() - size.width()); m_pEditor->editEventFrame()->setFixedWidth(w); } // (Re)create the complete track view pixmap. void qtractorMidiEditView::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 QColor& rgbBase = pal.base().color(); const QColor& rgbLine = pal.mid().color(); const QColor& rgbLight = pal.midlight().color(); const QColor& rgbDark = rgbBase.darker(110); const bool bDark = (rgbBase.value() < 128); m_pixmap = QPixmap(w, h); m_pixmap.fill(rgbBase); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); if (pTimeScale == nullptr) return; QPainter painter(&m_pixmap); // painter.initFrom(this); painter.setFont(qtractorScrollView::font()); // Show that we may have clip limits... if (m_pEditor->length() > 0) { int x1 = pTimeScale->pixelFromFrame(m_pEditor->length()) - cx; if (x1 < 0) x1 = 0; if (x1 < w) painter.fillRect(x1, 0, w - x1, h, rgbBase.darker(105)); } // Draw horizontal lines... painter.setPen(rgbLight); // p.setBrush(rgbDark); const int h1 = m_pEditor->editList()->itemHeight(); const int ch = qtractorScrollView::contentsHeight() - cy; const int q = (cy / h1); int n = 127 - q; int y = q * h1 - cy; while (y < h && y < ch) { const int k = (n % 12); if (k == 1 || k == 3 || k == 6 || k == 8 || k == 10) painter.fillRect(0, y + 1, w, h1, rgbDark); if (k == 11) { painter.setPen(rgbLine); painter.drawLine(0, y - 1, w, y - 1); painter.setPen(rgbLight); } painter.drawLine(0, y, w, y); y += h1; --n; } // Account for the editing offset: qtractorTimeScale::Cursor cursor(pTimeScale); const unsigned long f0 = m_pEditor->offset(); qtractorTimeScale::Node *pNode = cursor.seekFrame(f0); const unsigned long t0 = pNode->tickFromFrame(f0); const int x0 = pTimeScale->pixelFromFrame(f0); const int dx = x0 + cx; // Draw vertical grid lines... const QBrush zebra(QColor(0, 0, 0, 20)); pNode = cursor.seekPixel(dx); const unsigned short iSnapPerBeat = (m_pEditor->isSnapGrid() ? pTimeScale->snapPerBeat() : 0); #if 0 unsigned short iPixelsPerBeat = pNode->pixelsPerBeat(); unsigned int iBeat = pNode->beatFromPixel(dx); if (iBeat > 0) pNode = cursor.seekBeat(--iBeat); unsigned short iBar = (m_pEditor->isSnapZebra() ? pNode->barFromBeat(iBeat) : 0); int x = pNode->pixelFromBeat(iBeat) - dx; int x2 = x; while (x < w) { const bool bBeatIsBar = pNode->beatIsBar(iBeat); if (bBeatIsBar) { painter.setPen(rgbLine); painter.drawLine(x - 1, 0, x - 1, h); if (m_pEditor->isSnapZebra() && (x > x2) && (++iBar & 1)) painter.fillRect(QRect(x2, 0, x - x2 + 1, h), zebra); x2 = x; if (iBeat == pNode->beat) iPixelsPerBeat = pNode->pixelsPerBeat(); } if (bBeatIsBar || iPixelsPerBeat > 8) { painter.setPen(rgbLight); painter.drawLine(x, 0, x, h); } if (iSnapPerBeat > 1) { const int q = iPixelsPerBeat / iSnapPerBeat; if (q > 4) { painter.setPen(bDark ? rgbLight.darker(105) : rgbLight.lighter(120)); for (int i = 1; i < iSnapPerBeat; ++i) { x = pTimeScale->pixelSnap(x + dx + q) - dx - 1; painter.drawLine(x, 0, x, h); } } } pNode = cursor.seekBeat(++iBeat); x = pNode->pixelFromBeat(iBeat) - dx; } if (m_pEditor->isSnapZebra() && (x > x2) && (++iBar & 1)) painter.fillRect(QRect(x2, 0, x - x2 + 1, h), zebra); #else unsigned short iBar = pNode->barFromPixel(dx); if (iBar > 0) pNode = cursor.seekBar(--iBar); int x = pNode->pixelFromBar(iBar) - dx; while (x < w) { // Next bar... pNode = cursor.seekPixel(x + dx); const int x2 = pNode->pixelFromBar(++iBar) - dx; // Zebra lines... if (m_pEditor->isSnapZebra() && (iBar & 1)) painter.fillRect(QRect(x, 0, x2 - x + 1, h), zebra); // Beat lines... const unsigned short iBeatsPerBar2 = pNode->beatsPerBar2(); const float q2 = float(x2 - x) / float(iBeatsPerBar2); if (q2 > 8.0f) { float p2 = float(x); for (int i = 0; i < iBeatsPerBar2; ++i) { if (iSnapPerBeat > 1) { const float q1 = q2 / float(iSnapPerBeat); if (q1 > 4.0f) { painter.setPen(bDark ? rgbLight.darker(105) : rgbLight.lighter(120)); float p1 = p2; for (int j = 1; j < iSnapPerBeat; ++j) { const int x1 = int(p1 += q1); painter.drawLine(x1, 0, x1, h); } } } x = int(p2 += q2); if (x > w) break; if (i < iBeatsPerBar2 - 1) { painter.setPen(rgbLight); painter.drawLine(x, 0, x, h); } } } // Bar line... painter.setPen(rgbLine); painter.drawLine(x2 - 1, 0, x2 - 1, h); painter.setPen(rgbLight); painter.drawLine(x2, 0, x2, h); // Move forward... x = x2; } #endif if (y > ch) painter.fillRect(0, ch, w, h - ch, rgbLine); // Draw location marker lines... qtractorTimeScale::Marker *pMarker = pTimeScale->markers().seekPixel(dx); while (pMarker) { x = pTimeScale->pixelFromFrame(pMarker->frame) - dx; if (x > w) break; painter.setPen(pMarker->color); painter.drawLine(x, 0, x, h); pMarker = pMarker->next(); } // // Draw the clip(s) sequenece events... // qtractorMidiSequence *pSeq = m_pEditor->sequence(); if (pSeq == nullptr) return; pNode = cursor.seekPixel(x = dx); const unsigned long iTickStart = pNode->tickFromPixel(x); pNode = cursor.seekPixel(x += w); const unsigned long iTickEnd = pNode->tickFromPixel(x); const unsigned long f1 = f0 + m_pEditor->length(); pNode = cursor.seekFrame(f1); const unsigned long iTickEnd2 = pNode->tickFromFrame(f1); // Draw ghost-track events in dimmed transparecncy (alpha=55)... qtractorTrack *pGhostTrack = m_pEditor->ghostTrack(); if (pGhostTrack) { // Don't draw beyhond the right-most position (x = dx + w)... const unsigned long f2 = pTimeScale->frameFromPixel(x); const bool bDrumMode = pGhostTrack->isMidiDrums(); qtractorClip *pClip = pGhostTrack->clips().first(); while (pClip && pClip->clipStart() + pClip->clipLength() < f0) pClip = pClip->next(); while (pClip && pClip->clipStart() < f2) { qtractorMidiSequence *pGhostSeq = nullptr; qtractorMidiClip *pGhostClip = static_cast (pClip); if (pGhostClip && pGhostClip != m_pEditor->midiClip()) pGhostSeq = pGhostClip->sequence(); if (pGhostSeq) { m_pEditor->reset(false); // FIXME: reset cached cursor... const unsigned long iClipStart = pGhostClip->clipStart(); const unsigned long iClipEnd = iClipStart + pGhostClip->clipLength(); pNode = cursor.seekFrame(iClipStart); const unsigned long t1 = pNode->tickFromFrame(iClipStart); pNode = cursor.seekFrame(iClipEnd); const unsigned long t2 = pNode->tickFromFrame(iClipEnd); drawEvents(painter, dx, cy, pGhostSeq, t1, iTickStart, iTickEnd, t2, bDrumMode, pGhostTrack->foreground(), pGhostTrack->background(), 60); } pClip = pClip->next(); } // FIXME: reset cached cursor... m_pEditor->reset(false); } // Draw actual events in full brightness (alpha=255)... drawEvents(painter, dx, cy, pSeq, t0, iTickStart, iTickEnd, iTickEnd2, m_pEditor->isDrumMode(), m_pEditor->foreground(), m_pEditor->background()); // Draw loop boundaries, if applicable... if (pSession->isLooping()) { const QBrush shade(QColor(0, 0, 0, 60)); painter.setPen(Qt::darkCyan); x = pTimeScale->pixelFromFrame(pSession->loopStart()) - dx; 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()) - dx; 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); x = pTimeScale->pixelFromFrame(pSession->punchIn()) - dx; 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()) - dx; 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 the track view events. void qtractorMidiEditView::drawEvents ( QPainter& painter, int dx, int dy, qtractorMidiSequence *pSeq, unsigned long t0, unsigned long iTickStart, unsigned long iTickEnd, unsigned long iTickEnd2, bool bDrumMode, const QColor& fore, const QColor& back, int alpha ) { const int h = qtractorScrollView::viewport()->height(); const int h1 = m_pEditor->editList()->itemHeight(); const int ch = qtractorScrollView::contentsHeight() - dy; const QFontMetrics fm(qtractorScrollView::font()); const int hs = fm.ascent(); // fm.height() - 2; QVector diamond; if (bDrumMode) { const int h2 = (h1 >> 1) + 1; diamond.append(QPoint( 0, -1)); diamond.append(QPoint(-h2, h2)); diamond.append(QPoint( 0, h1 + 2)); diamond.append(QPoint( h2, h2)); painter.setRenderHint(QPainter::Antialiasing, true); } QColor rgbFore(fore); rgbFore.setAlpha(alpha); painter.setPen(rgbFore); QColor rgbNote(back); int hue, sat, val; rgbNote.getHsv(&hue, &sat, &val); sat = 86; rgbNote.setAlpha(alpha); int x, y; qtractorTimeScale::Cursor cursor(m_pEditor->timeScale()); qtractorTimeScale::Node *pNode; const qtractorMidiEvent::EventType eventType = m_eventType; const int wm = m_pEditor->minEventWidth(); qtractorMidiEvent *pEvent = m_pEditor->seekEvent(pSeq, iTickStart > t0 ? iTickStart - t0 : 0); while (pEvent) { const unsigned long t1 = t0 + pEvent->time(); if (t1 >= iTickEnd) break; unsigned long t2 = t1 + pEvent->duration(); if (t2 > iTickEnd2) t2 = iTickEnd2; // Filter event type!... if (pEvent->type() == eventType && t2 >= iTickStart) { y = ch - h1 * (pEvent->note() + 1); if (y + h1 >= 0 && y < h) { pNode = cursor.seekTick(t1); x = pNode->pixelFromTick(t1) - dx; pNode = cursor.seekTick(t2); int w1 = (t1 >= t2 && m_pEditor->isClipRecordEx() ? m_pEditor->playHeadX() : pNode->pixelFromTick(t2)) - dx - x; if (w1 < wm) w1 = wm; if (m_pEditor->isNoteColor()) { hue = (128 - int(pEvent->note())) << 4; if (m_pEditor->isValueColor()) sat = 64 + (int(pEvent->value()) >> 1); rgbNote.setHsv(hue, sat, val, alpha); } else if (m_pEditor->isValueColor()) { hue = (128 - int(pEvent->value())) << 1; rgbNote.setHsv(hue, sat, val, alpha); } if (bDrumMode) { painter.setBrush(rgbNote); const QPolygon& polyg = QPolygon(diamond).translated(x, y); if (h1 > 3) painter.drawPolygon(polyg.translated(1, 0)); // shadow painter.drawPolygon(polyg); // diamond } else { painter.fillRect(x, y, w1, h1, rgbFore); if (h1 > 3) { painter.fillRect(x + 1, y + 1, w1 - 4, h1 - 3, rgbNote); if (m_pEditor->isNoteNames() && hs < h1) { const QString& sNoteName = m_pEditor->noteName(pEvent->note()); painter.setPen(rgbFore.darker(160)); painter.drawText( QRect(x + 2, y + 1, w1 - 6, h1 - 4), Qt::AlignTop | Qt::AlignLeft, sNoteName); painter.setPen(rgbFore); } } } } } pEvent = pEvent->next(); } if (bDrumMode) painter.setRenderHint(QPainter::Antialiasing, false); } // Draw the track view. void qtractorMidiEditView::drawContents ( QPainter *pPainter, const QRect& rect ) { // Draw viewport canvas... pPainter->drawPixmap(rect, m_pixmap, rect); #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 m_pEditor->paintDragState(this, pPainter); // Are we sticking in some note? if (m_iNoteOn >= 0) { pPainter->fillRect(QRect( contentsToViewport(m_rectNote.topLeft()), m_rectNote.size()), m_iNoteVel > 0 ? QColor(255, 0, 120, 40) : QColor(120, 120, 255, 40)); } // Draw special play/edit-head/tail headers... const int cx = qtractorScrollView::contentsX(); int x = m_pEditor->editHeadX() - cx; if (x >= rect.left() && x <= rect.right()) { pPainter->setPen(Qt::blue); pPainter->drawLine(x, rect.top(), x, rect.bottom()); } x = m_pEditor->editTailX() - cx; if (x >= rect.left() && x <= rect.right()) { pPainter->setPen(Qt::blue); pPainter->drawLine(x, rect.top(), x, rect.bottom()); } x = m_pEditor->playHeadX() - cx; if (x >= rect.left() && x <= rect.right()) { pPainter->setPen(Qt::red); pPainter->drawLine(x, rect.top(), x, rect.bottom()); } if (m_pEditor->isStepInputHead()) { x = m_pEditor->stepInputHeadX() - cx; if (x >= rect.left() && x <= rect.right()) { pPainter->setPen(QColor(255, 0, 0, 120)); pPainter->drawLine(x, rect.top(), x, rect.bottom()); } } } // To have main view in h-sync with edit list. void qtractorMidiEditView::contentsXMovingSlot ( int cx, int /*cy*/ ) { if (qtractorScrollView::contentsX() != cx) qtractorScrollView::setContentsPos(cx, qtractorScrollView::contentsY()); } // To have main view in v-sync with edit list. void qtractorMidiEditView::contentsYMovingSlot ( int /*cx*/, int cy ) { if (qtractorScrollView::contentsY() != cy) qtractorScrollView::setContentsPos(qtractorScrollView::contentsX(), cy); } // Focus lost event. void qtractorMidiEditView::focusOutEvent ( QFocusEvent *pFocusEvent ) { m_pEditor->focusOut(this); qtractorScrollView::focusOutEvent(pFocusEvent); } // Keyboard event handler. void qtractorMidiEditView::keyPressEvent ( QKeyEvent *pKeyEvent ) { if (!m_pEditor->keyPress(this, pKeyEvent->key(), pKeyEvent->modifiers())) qtractorScrollView::keyPressEvent(pKeyEvent); } // Handle item selection/dragging -- mouse button press. void qtractorMidiEditView::mousePressEvent ( QMouseEvent *pMouseEvent ) { // Process mouse press... // qtractorScrollView::mousePressEvent(pMouseEvent); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // We'll need options somehow... qtractorOptions *pOptions = qtractorOptions::getInstance(); // Which mouse state? bool bModifier = (pMouseEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)); // Maybe start the drag-move-selection dance? const QPoint& pos = qtractorScrollView::viewportToContents(pMouseEvent->pos()); qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); const unsigned long iFrame = m_pEditor->frameSnap(m_pEditor->offset() + pTimeScale->frameFromPixel(pos.x() > 0 ? pos.x() : 0)); switch (pMouseEvent->button()) { case Qt::LeftButton: // Only the left-mouse-button was meaningful... break; case Qt::MiddleButton: // Mid-button direct positioning... m_pEditor->selectAll(this, false); if (pOptions && pOptions->bMidButtonModifier) bModifier = !bModifier; // Reverse mid-button role... // Which mouse state? if (bModifier) { // Play-head positioning commit... m_pEditor->setPlayHead(iFrame); pSession->setPlayHead(iFrame); } else { // Edit cursor (merge) positioning... m_pEditor->setEditHead(iFrame); m_pEditor->setEditTail(iFrame); } // Logical contents changed, just for visual feedback... m_pEditor->selectionChangeNotify(); // Fall thru... default: return; } // Remember what and where we'll be dragging/selecting... m_pEditor->dragMoveStart(this, pos, pMouseEvent->modifiers()); } // Handle item selection/dragging -- mouse pointer move. void qtractorMidiEditView::mouseMoveEvent ( QMouseEvent *pMouseEvent ) { // Process mouse move... // qtractorScrollView::mouseMoveEvent(pMouseEvent); // Are we already moving/dragging something? const QPoint& pos = qtractorScrollView::viewportToContents(pMouseEvent->pos()); m_pEditor->dragMoveUpdate(this, pos, pMouseEvent->modifiers()); } // Handle item selection/dragging -- mouse button release. void qtractorMidiEditView::mouseReleaseEvent ( QMouseEvent *pMouseEvent ) { // Process mouse release... // qtractorScrollView::mouseReleaseEvent(pMouseEvent); // Were we moving/dragging something? const QPoint& pos = qtractorScrollView::viewportToContents(pMouseEvent->pos()); m_pEditor->dragMoveCommit(this, pos, pMouseEvent->modifiers()); } // Handle zoom with mouse wheel. void qtractorMidiEditView::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); } // Trap for help/tool-tip and leave events. bool qtractorMidiEditView::eventFilter ( QObject *pObject, QEvent *pEvent ) { if (m_pEditor->dragMoveFilter(this, pObject, pEvent)) return true; return qtractorScrollView::eventFilter(pObject, pEvent); } // end of qtractorMidiEditView.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMainForm.ui0000644000000000000000000000013015101070305017047 xustar0029 mtime=1761898693.07626762 30 atime=1761898693.075267616 29 ctime=1761898693.07626762 qtractor-1.5.9/src/qtractorMainForm.ui0000644000175000001440000025605015101070305017051 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. qtractorMainForm true 0 0 1024 768 320 240 true :/images/qtractor.svg Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false Qt::Horizontal TopToolBarArea false 0 0 800 23 &File Open &Recent &Track &State &Navigate Mo&ve &Height Impor&t Tracks E&xport Tracks Instrum&ent A&utomation &Select M&ode &Clip T&ools Ta&ke &Select &Edit Select &Mode &Select I&nsert Remo&ve &View &Toolbars &Windows &Zoom S&nap T&ransport Mo&de St&ep &Help &New New New session New session file Ctrl+N &Open... Open Open session Open session from file Ctrl+O &Save Save Save session Save session to file Ctrl+S Save &As... Save As Save as Save current session with another file name &Properties... Session Properties Session properties Edit current session properties F2 E&xit Exit Exit Exit this application program &Undo Undo Undo Undo last action Ctrl+Z &Redo Redo Redo Redo last action Ctrl+Shift+Z Cu&t Cut Cut Cut selection to clipboard Ctrl+X &Copy Copy Copy Copy selection to clipboard Ctrl+C &Paste Paste Paste Paste clipboard contents Ctrl+V Past&e Repeat... Paste Repeat Paste repeat Paste/repeat clipboard contents Ctrl+Shift+V &Delete Delete Delete Delete selection Del true &Clip Clip Select clip Clip selection mode true &Range Range Select range Range selection mode true R&ectangle Rect Select rectangle Rectangular selection mode true &Automation Automation Automation Automation edit mode &All Select All Select all Mark all as selected Ctrl+A &None Select None Select none Mark all as unselected Ctrl+Shift+A &Invert Select Invert Select invert Invert selection Ctrl+I &Track Select Track Select track Mark track as selected Ctrl+T Trac&k Range Select Track Range Select track range Mark track range as selected Ctrl+Shift+R &Range Select Range Select range Mark range as selected Ctrl+R &Range... Remove Range Remove range Remove range as selected Ctrl+Del Trac&k Range Remove Track Range Remove track range Remove track range as selected Ctrl+Shift+Del &Range... Insert Range Insert range Insert range as selected Ctrl+Ins Trac&k Range Insert Track Range Insert track range Insert track range as selected Ctrl+Shift+Ins Sp&lit Split Selection Split selection Split current selection Ctrl+Y &Add Track... Add Track Add track Add a new track to session Shift+Ins &Remove Track Remove Track Remove track Remove current track from session Shift+Del &Duplicate Track Duplicate Track Duplicate track Duplicate current track Track &Properties... Track Properties Track properties Edit current track properties Shift+F2 &Inputs Track Inputs Track inputs Show current track input bus connections &Outputs Track Outputs Track outputs Show current track output bus connections true &Record Record Track Record track Arm current track for recording true &Mute Mute Track Mute track Mute current track true &Solo Solo Track Solo track Solo current track true M&onitor Monitor Track Monitor track Monitor current track &First First Track First track Make current the first track &Previous Previous Track Previous track Make current the previous track &Next Next Track Next track Make current the next track &Last Last Track Last track Make current the last track N&one None Track None track None current track &Top Move Top Move top Move current track to top &Up Move Up Move up Move current track up &Down Move Down Move down Move current track down &Bottom Move Bottom Move bottom Move current track to bottom &Increase Increase Height Increase height Increase track height Ctrl+Shift++ &Decrease Decrease Height Decrease height Decrease track height Ctrl+Shift+- &Minimize Minimize Height H Minimize height Minimize track height &Reset Height Reset Height reset Reset track height Ctrl+Shift+1 true Auto &Monitor Auto Monitor Auto monitor Auto-monitor current track F6 true Auto Dea&ctivate Auto Deactivate Auto-deactivate plugins Auto-deactivate plugins not producing sound Shift+F6 &Audio... Inport Audio File Import Audio file Import tracks from Audio file &MIDI... Import MIDI File Import MIDI file Import tracks from MIDI file &Audio... Export Audio File Export Audio file Export tracks to Audio file &MIDI... Export MIDI File Export MIDI file Export tracks to MIDI file true Log&arithmic Automation logarithmic Automation logarithmic Automation curve logarithmic scale C&olor... Automation color Automation color Automation curve color true &Lock Automation lock Automation lock Lock automation curve true &Play Automation playback Automation playback Playback automation curve true &Record Automation record Automation record Record automation curve &Clear Automation clear Automation clear Clear automation curve true Loc&k All Automation lock all Automation lock all Lock all automation curves true Play &All Automation playback all Automation playback all Playback all automation curves true Rec&ord All Automation record all Automation record all Record all automation curves C&lear All Automation clear all Automation clear all Clear all automation curves &New... New Clip New clip Create new clip &Edit... Edit Clip Edit clip Edit current clip F4 true &Mute Mute Clip Mute clip Mute current clip &Unlink Unlink Clip Unlink clip Unlink current clip true Recor&d Record Clip Record clip Record current clip (overdub) &Split Split Clip Split clip Split current clip at playhead &Merge... Merge Clips Merge clips Merge selected clips Normali&ze Normalize Clip Normalize clip Normalize current clip (gain/volume) &Quantize... Quantize Clip Quantize clip events Quantize current MIDI clip events &Transpose... Transpose Clip Transpose clip events Transpose current MIDI clip events &Normalize... Normalize Clip Normalize clip events Normalize current MIDI clip events &Randomize... Randomize Clip Randomize clip events Randomize current MIDI clip events Resi&ze... Resize Clip Resize clip events Resize current MIDI clip events Re&scale... Rescale Clip Rescale clip events Rescale current MIDI clip events T&imeshift... Timeshift Clip Timeshift clip events Timeshift current MIDI clip events T&empo ramp... Tempo ramp Clip Tempo ramp clip events Tempo ramp current MIDI clip events &Tempo Adjust... Tempo Adjust Tempo Adjust Adjust session tempo from current clip selection F7 &Cross Fade Clip Cross-fade Clip cross-fade Cross-fade current overlapped clips &Range Set Clip Range Clip range Set edit-range from current clip extents true &Loop Set Clip Loop Clip loop Set loop-range from current clip extents &Import... Import Clip Import clip Import clip from file(s) E&xport... Export Clip Export clip Export current clip to file &First First Take First take Select current clip first take &Previous Previous Take Previous take Select current clip previous take &Next Next Take Next take Select current clip next take Shift+T &Last Last Take Last take Select current clip last take &Reset Reset Takes Reset takes Reset (unfold) current clip takes R&ange... Take Range Take range Range (fold) current clip into takes true &Menubar Menubar Menubar Show/hide the main program window menubar Ctrl+M true &Statusbar Statusbar Statusbar Show/hide the main program window statusbar true &File File Toolbar File toolbar Show/hide main program window file toolbar true &Edit Edit Toolbar Edit toolbar Show/hide main program window edit toolbar true &Track Track Toolbar Track toolbar Show/hide main program window track toolbar true &View View Toolbar View toolbar Show/hide main program window view toolbar true &Options Options Toolbar Options toolbar Show/hide main program window options toolbar true T&ransport Transport Toolbar Transport toolbar Show/hide main program window transport toolbar true T&ime Time Toolbar Time toolbar Show/hide main program window time toolbar true Thum&b Thumb Toolbar Thumb toolbar Show/hide main program window thumb toolbar true File &System File System File system Show/hide the file system window true &Files Files Files Show/hide the files window true M&essages Messages Messages Show/hide the messages window true &Connections Connections Connections Show/hide the connections window F8 true Mi&xer Mixer Mixer Show/hide the mixer window F9 &In Zoom In Zoom in Zoom in Ctrl++ &Out Zoom Out Zoom out Zoom out Ctrl+- &Reset Zoom Reset Zoom reset Zoom reset Ctrl+1 true &Horizontal Horizontal Zoom Horizontal zoom Horizontal zoom mode true &Vertical Vertical Zoom Vertical zoom Vertical zoom mode true &All All Zoom All zoom All zoom mode true &Grid Grid Grid Snap grid view mode true &Zebra Zebra Zebra Bar zebra view mode true Too&l Tips Tool tips Tool tips Floating tool tips view mode &Refresh Refresh Refresh Refresh views F5 &Instruments... Instruments Instruments Change instrument definitions and files &Controllers... Controllers Controllers Change MIDI controllers configuration &Buses... Buses Buses Change session bus definitions Tempo M&ap / Markers... Tempo Map / Markers Tempo map / markers Change session tempo map / markers &Options... Options Options Change general application program options F12 &Backward Backward Backward Transport backward Backspace true Re&wind Rewind Rewind Transport rewind true F&ast Forward Fast Forward Fast forward Transport fast forward &Forward Forward Forward Transport forward true &Loop Loop Loop Transport loop Ctrl+Shift+L &Backward Step Backward Step backward Transport step backward &Forward Step Forward Step forward Transport step forward Loop &Set Loop Set Loop set Transport loop set Ctrl+L &Stop Stop Stop Transport stop true &Play Play Play Transport play/pause Space true &Record Record Record Transport record true &Punch Punch Punch in/out Transport punch in/out Ctrl+Shift+P Punch Se&t Punch Set Punch in/out set Transport punch in/out set Ctrl+P true &Count-in Count-in Count-in Count-in true &Metronome Metronome Metronome Metronome true F&ollow Playhead Follow Playhead Follow playhead Follow playhead true A&uto Backward Auto Backward Auto backward Auto backward true &Continue Past End Continue Past End Continue past end Continue past end true &None None Transport mode: None Transport mode set to None true &Slave Slave Transport mode: Slave Transport mode set to Slave true &Master Master Transport mode: Master Transport mode set to Master true &Full Full Transport mode: Full Transport mode set to Full Pa&nic Panic Panic All MIDI tracks shut off (panic) &Shortcuts... Shortcuts Shortcuts Keyboard shortcuts &About... About About Show information about this application program About &Qt... About Qt About Qt Show information about the Qt toolkit qtractor-1.5.9/src/PaxHeaders/palette0000644000000000000000000000013215101070305014603 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.061679615 30 ctime=1761898693.062554172 qtractor-1.5.9/src/palette/0000755000175000001440000000000015101070305014650 5ustar00rncbcusersqtractor-1.5.9/src/palette/PaxHeaders/Wonton Soup.conf0000644000000000000000000000013215101070305017722 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.061679615 30 ctime=1761898693.062554172 qtractor-1.5.9/src/palette/Wonton Soup.conf0000644000175000001440000000202615101070305017712 0ustar00rncbcusers[ColorThemes] Wonton%20Soup\AlternateBase=#434750, #434750, #3b3e46 Wonton%20Soup\Base=#3c4048, #3c4048, #34383f Wonton%20Soup\BrightText=#ffffff, #ffffff, #ffffff Wonton%20Soup\Button=#525863, #525863, #484d57 Wonton%20Soup\ButtonText=#d2def0, #d2def0, #6f7682 Wonton%20Soup\Dark=#282b31, #282b31, #23262b Wonton%20Soup\Highlight=#78889c, #515a67, #40444d Wonton%20Soup\HighlightedText=#d1e1f4, #b6c1d0, #616872 Wonton%20Soup\Light=#5f6572, #5f6572, #565c68 Wonton%20Soup\Link=#9cd4ff, #9cd4ff, #526677 Wonton%20Soup\LinkVisited=#4080ff, #4080ff, #364c77 Wonton%20Soup\Mid=#3f444c, #3f444c, #383b43 Wonton%20Soup\Midlight=#545a65, #545a65, #4b515b Wonton%20Soup\NoRole=#000000, #000000, #000000 Wonton%20Soup\PlaceholderText=#000000, #000000, #000000 Wonton%20Soup\Shadow=#1d1f23, #1d1f23, #191b1e Wonton%20Soup\Text=#d2def0, #d2def0, #636973 Wonton%20Soup\ToolTipBase=#b6c1d0, #b6c1d0, #b6c1d0 Wonton%20Soup\ToolTipText=#2a2c30, #2a2c30, #2a2c30 Wonton%20Soup\Window=#494e58, #494e58, #40444d Wonton%20Soup\WindowText=#b6c1d0, #b6c1d0, #616872 qtractor-1.5.9/src/palette/PaxHeaders/KXStudio.conf0000644000000000000000000000013215101070305017241 xustar0030 mtime=1761898693.061679615 30 atime=1761898693.061679615 30 ctime=1761898693.061679615 qtractor-1.5.9/src/palette/KXStudio.conf0000644000175000001440000000165515101070305017240 0ustar00rncbcusers[ColorThemes] KXStudio\AlternateBase=#0e0e0e, #0e0e0e, #0c0c0c KXStudio\Base=#070707, #070707, #060606 KXStudio\BrightText=#ffffff, #ffffff, #ffffff KXStudio\Button=#1c1c1c, #1c1c1c, #181818 KXStudio\ButtonText=#f0f0f0, #f0f0f0, #5a5a5a KXStudio\Dark=#818181, #818181, #818181 KXStudio\Highlight=#3c3c3c, #222222, #0e0e0e KXStudio\HighlightedText=#ffffff, #f0f0f0, #535353 KXStudio\Light=#bfbfbf, #bfbfbf, #bfbfbf KXStudio\Link=#6464e6, #6464e6, #22224a KXStudio\LinkVisited=#e664e6, #e664e6, #4a224a KXStudio\Mid=#5e5e5e, #5e5e5e, #5e5e5e KXStudio\Midlight=#9b9b9b, #9b9b9b, #9b9b9b KXStudio\NoRole=#000000, #000000, #000000 KXStudio\PlaceholderText=#000000, #000000, #000000 KXStudio\Shadow=#9b9b9b, #9b9b9b, #9b9b9b KXStudio\Text=#e6e6e6, #e6e6e6, #4a4a4a KXStudio\ToolTipBase=#040404, #040404, #040404 KXStudio\ToolTipText=#e6e6e6, #e6e6e6, #e6e6e6 KXStudio\Window=#111111, #111111, #0e0e0e KXStudio\WindowText=#f0f0f0, #f0f0f0, #535353 qtractor-1.5.9/src/PaxHeaders/qtractorNsmClient.cpp0000644000000000000000000000013215101070305017402 xustar0030 mtime=1761898693.085267648 30 atime=1761898693.085267648 30 ctime=1761898693.085267648 qtractor-1.5.9/src/qtractorNsmClient.cpp0000644000175000001440000002630315101070305017376 0ustar00rncbcusers// qtractorNsmClient.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 "qtractorNsmClient.h" #include #include #define NSM_API_VERSION_MAJOR 1 #define NSM_API_VERSION_MINOR 0 #ifdef CONFIG_LIBLO //--------------------------------------------------------------------------- // qtractorNsmClient - OSC (liblo) callback methods. static int osc_nsm_error ( const char */*path*/, const char */*types*/, lo_arg **argv, int /*argc*/, lo_message /*msg*/, void *user_data ) { qtractorNsmClient *pNsmClient = static_cast (user_data); if (pNsmClient == nullptr) return -1; if (strcmp(&argv[0]->s, "/nsm/server/announce")) return -1; pNsmClient->nsm_announce_error(&argv[2]->s); return 0; } static int osc_nsm_reply ( const char */*path*/, const char */*types*/, lo_arg **argv, int /*argc*/, lo_message /*msg*/, void *user_data ) { qtractorNsmClient *pNsmClient = static_cast (user_data); if (pNsmClient == nullptr) return -1; if (strcmp(&argv[0]->s, "/nsm/server/announce")) return -1; pNsmClient->nsm_announce_reply(&argv[1]->s, &argv[2]->s, &argv[3]->s); return 0; } static int osc_nsm_open ( const char */*path*/, const char */*types*/, lo_arg **argv, int /*argc*/, lo_message /*msg*/, void *user_data ) { qtractorNsmClient *pNsmClient = static_cast (user_data); if (pNsmClient == nullptr) return -1; pNsmClient->nsm_open(&argv[0]->s, &argv[1]->s, &argv[2]->s); return 0; } static int osc_nsm_save ( const char */*path*/, const char */*types*/, lo_arg **/*argv*/, int /*argc*/, lo_message /*msg*/, void *user_data ) { qtractorNsmClient *pNsmClient = static_cast (user_data); if (pNsmClient == nullptr) return -1; pNsmClient->nsm_save(); return 0; } static int osc_nsm_loaded ( const char */*path*/, const char */*types*/, lo_arg **/*argv*/, int /*argc*/, lo_message /*msg*/, void *user_data ) { qtractorNsmClient *pNsmClient = static_cast (user_data); if (pNsmClient == nullptr) return -1; pNsmClient->nsm_loaded(); return 0; } static int osc_nsm_show ( const char */*path*/, const char */*types*/, lo_arg **/*argv*/, int /*argc*/, lo_message /*msg*/, void *user_data ) { qtractorNsmClient *pNsmClient = static_cast (user_data); if (pNsmClient == nullptr) return -1; pNsmClient->nsm_show(); return 0; } static int osc_nsm_hide ( const char */*path*/, const char */*types*/, lo_arg **/*argv*/, int /*argc*/, lo_message /*msg*/, void *user_data ) { qtractorNsmClient *pNsmClient = static_cast (user_data); if (pNsmClient == nullptr) return -1; pNsmClient->nsm_hide(); return 0; } #endif // CONFIG_LIBLO //--------------------------------------------------------------------------- // qtractorNsmClient - NSM OSC client agent. // Constructor. qtractorNsmClient::qtractorNsmClient ( const QString& nsm_url, QObject *pParent ) : QObject(pParent), #ifdef CONFIG_LIBLO m_address(nullptr), m_thread(nullptr), m_server(nullptr), #endif m_active(false) { #ifdef CONFIG_LIBLO m_address = lo_address_new_from_url(nsm_url.toUtf8().constData()); const int proto = lo_address_get_protocol(m_address); m_thread = lo_server_thread_new_with_proto(nullptr, proto, nullptr); if (m_thread) { m_server = lo_server_thread_get_server(m_thread); lo_server_thread_add_method(m_thread, "/error", "sis", osc_nsm_error, this); lo_server_thread_add_method(m_thread, "/reply", "ssss", osc_nsm_reply, this); lo_server_thread_add_method(m_thread, "/nsm/client/open", "sss", osc_nsm_open, this); lo_server_thread_add_method(m_thread, "/nsm/client/save", "", osc_nsm_save, this); lo_server_thread_add_method(m_thread, "/nsm/client/session_is_loaded", "", osc_nsm_loaded, this); lo_server_thread_add_method(m_thread, "/nsm/client/show_optional_gui", "", osc_nsm_show, this); lo_server_thread_add_method(m_thread, "/nsm/client/hide_optional_gui", "", osc_nsm_hide, this); lo_server_thread_start(m_thread); } #endif } // Destructor. qtractorNsmClient::~qtractorNsmClient (void) { #ifdef CONFIG_LIBLO if (m_thread) { lo_server_thread_stop(m_thread); lo_server_thread_free(m_thread); } if (m_address) lo_address_free(m_address); #endif } // Session clieant methods. void qtractorNsmClient::announce ( const QString& app_name, const QString& capabilities ) { #ifdef CONFIG_LIBLO if (m_address && m_server) { const QFileInfo fi(QApplication::applicationFilePath()); lo_send_from(m_address, m_server, LO_TT_IMMEDIATE, "/nsm/server/announce", "sssiii", app_name.toUtf8().constData(), capabilities.toUtf8().constData(), fi.fileName().toUtf8().constData(), NSM_API_VERSION_MAJOR, NSM_API_VERSION_MINOR, int(QApplication::applicationPid())); } #endif } // Session activation accessor. bool qtractorNsmClient::is_active (void) const { return m_active; } // Session manager accessors. const QString& qtractorNsmClient::manager (void) const { return m_manager; } const QString& qtractorNsmClient::capabilities (void) const { return m_capabilities; } // Session client accessors. const QString& qtractorNsmClient::path_name (void) const { return m_path_name; } const QString& qtractorNsmClient::display_name (void) const { return m_display_name; } const QString& qtractorNsmClient::client_id (void) const { return m_client_id; } // Session client methods. void qtractorNsmClient::dirty ( bool is_dirty ) { #ifdef CONFIG_LIBLO if (m_address && m_server && m_active) { const char *path = is_dirty ? "/nsm/client/is_dirty" : "/nsm/client/is_clean"; lo_send_from(m_address, m_server, LO_TT_IMMEDIATE, path, ""); } #endif } void qtractorNsmClient::visible ( bool is_visible ) { #ifdef CONFIG_LIBLO if (m_address && m_server && m_active) { const char *path = is_visible ? "/nsm/client/gui_is_shown" : "/nsm/client/gui_is_hidden"; lo_send_from(m_address, m_server, LO_TT_IMMEDIATE, path, ""); } #endif } void qtractorNsmClient::progress ( float percent ) { #ifdef CONFIG_LIBLO if (m_address && m_server && m_active) { lo_send_from(m_address, m_server, LO_TT_IMMEDIATE, "/nsm/client/progress", "f", percent); } #endif } void qtractorNsmClient::message ( int priority, const QString& mesg ) { #ifdef CONFIG_LIBLO if (m_address && m_server && m_active) { lo_send_from(m_address, m_server, LO_TT_IMMEDIATE, "/nsm/client/message", "is", priority, mesg.toUtf8().constData()); } #endif } // Session client reply methods. void qtractorNsmClient::open_reply ( ReplyCode reply_code ) { reply("/nsm/client/open", reply_code); } void qtractorNsmClient::save_reply ( ReplyCode reply_code ) { reply("/nsm/client/save", reply_code); } void qtractorNsmClient::reply ( const QString& path, ReplyCode reply_code ) { const char *reply_mesg; switch (reply_code) { case ERR_OK: reply_mesg = "OK"; break; case ERR_GENERAL: reply_mesg = "ERR_GENERAL"; break; case ERR_INCOMPATIBLE_API: reply_mesg = "ERR_INCOMPATIBLE_API"; break; case ERR_BLACKLISTED: reply_mesg = "ERR_BLACKLISTED"; break; case ERR_LAUNCH_FAILED: reply_mesg = "ERR_LAUNCH_FAILED"; break; case ERR_NO_SUCH_FILE: reply_mesg = "ERR_NO_SUCH_FILE"; break; case ERR_NO_SESSION_OPEN: reply_mesg = "ERR_NO_SESSION_OPEN"; break; case ERR_UNSAVED_CHANGES: reply_mesg = "ERR_UNSAVED_CHANGES"; break; case ERR_NOT_NOW: reply_mesg = "ERR_NOT_NOW"; break; default: reply_mesg = "(UNKNOWN)"; break; } #ifdef CONFIG_LIBLO if (m_address && m_server) { if (reply_code == ERR_OK) { lo_send_from(m_address, m_server, LO_TT_IMMEDIATE, "/reply", "ss", path.toUtf8().constData(), reply_mesg); } else { lo_send_from(m_address, m_server, LO_TT_IMMEDIATE, "/error", "sis", path.toUtf8().constData(), int(reply_code), reply_mesg); } } #endif } // Server announce error. void qtractorNsmClient::nsm_announce_error ( const char *mesg ) { m_active = false; m_manager.clear(); m_capabilities.clear(); m_path_name.clear(); m_display_name.clear(); m_client_id.clear(); emit active(false); qWarning("NSM: Failed to register with server: %s.", mesg); } // Server announce reply. void qtractorNsmClient::nsm_announce_reply ( const char *mesg, const char *manager, const char *capabilities ) { m_active = true; m_manager = manager; m_capabilities = capabilities; emit active(true); qWarning("NSM: Successfully registered with server: %s.", mesg); } // Client open callback. void qtractorNsmClient::nsm_open ( const char *path_name, const char *display_name, const char *client_id ) { m_path_name = path_name; m_display_name = display_name; m_client_id = client_id; #ifdef CONFIG_DEBUG qDebug("qtractorNsmClient::nsm_open: " "path_name=\"%s\" display_name=\"%s\" client_id=\"%s\".", m_path_name.toUtf8().constData(), m_display_name.toUtf8().constData(), m_client_id.toUtf8().constData()); #endif emit open(); } // Client save callback. void qtractorNsmClient::nsm_save (void) { #ifdef CONFIG_DEBUG qDebug("qtractorNsmClient::nsm_save: " "path_name=\"%s\" display_name=\"%s\" client_id=\"%s\".", m_path_name.toUtf8().constData(), m_display_name.toUtf8().constData(), m_client_id.toUtf8().constData()); #endif emit save(); } // Client loaded callback. void qtractorNsmClient::nsm_loaded (void) { #ifdef CONFIG_DEBUG qDebug("qtractorNsmClient::nsm_loaded: " "path_name=\"%s\" display_name=\"%s\" client_id=\"%s\".", m_path_name.toUtf8().constData(), m_display_name.toUtf8().constData(), m_client_id.toUtf8().constData()); #endif emit loaded(); } // Client show optional GUI. void qtractorNsmClient::nsm_show (void) { #ifdef CONFIG_DEBUG qDebug("qtractorNsmClient::nsm_show: " "path_name=\"%s\" display_name=\"%s\" client_id=\"%s\".", m_path_name.toUtf8().constData(), m_display_name.toUtf8().constData(), m_client_id.toUtf8().constData()); #endif emit show(); } // Client hide optional GUI. void qtractorNsmClient::nsm_hide (void) { #ifdef CONFIG_DEBUG qDebug("qtractorNsmClient::nsm_hide: " "path_name=\"%s\" display_name=\"%s\" client_id=\"%s\".", m_path_name.toUtf8().constData(), m_display_name.toUtf8().constData(), m_client_id.toUtf8().constData()); #endif emit hide(); } // end of qtractorNsmClient.cpp qtractor-1.5.9/src/PaxHeaders/qtractorCommand.cpp0000644000000000000000000000013215101070305017064 xustar0030 mtime=1761898693.067267591 30 atime=1761898693.067267591 30 ctime=1761898693.067267591 qtractor-1.5.9/src/qtractorCommand.cpp0000644000175000001440000001110515101070305017052 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.9/src/PaxHeaders/qtractorPaletteForm.ui0000644000000000000000000000013215101070305017563 xustar0030 mtime=1761898693.086267651 30 atime=1761898693.086267651 30 ctime=1761898693.086267651 qtractor-1.5.9/src/qtractorPaletteForm.ui0000644000175000001440000001667315101070305017570 0ustar00rncbcusers 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.9/src/PaxHeaders/qtractorInstrumentForm.h0000644000000000000000000000013215101070305020147 xustar0030 mtime=1761898693.072267607 30 atime=1761898693.072267607 30 ctime=1761898693.072267607 qtractor-1.5.9/src/qtractorInstrumentForm.h0000644000175000001440000000521415101070305020141 0ustar00rncbcusers// qtractorInstrumentForm.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 __qtractorInstrumentForm_h #define __qtractorInstrumentForm_h #include "ui_qtractorInstrumentForm.h" // Forward declarations... class qtractorInstrumentData; class qtractorInstrumentDataList; class qtractorInstrumentList; //---------------------------------------------------------------------------- // qtractorInstrumentForm -- UI wrapper form. class qtractorInstrumentForm : public QDialog { Q_OBJECT public: // Constructor. qtractorInstrumentForm(QWidget *pParent = nullptr); // Destructor. ~qtractorInstrumentForm(); // Special group item types. enum { GroupItem = QTreeWidgetItem::UserType + 1 }; // Instrument list accessors. void setInstruments(qtractorInstrumentList *pInstruments); qtractorInstrumentList *instruments() const; protected slots: void accept(); void reject(); void importSlot(); void removeSlot(); void moveUpSlot(); void moveDownSlot(); void reloadSlot(); void exportSlot(); void stabilizeForm(); void refreshForm(); void itemCollapsed(QTreeWidgetItem*); void itemExpanded(QTreeWidgetItem*); protected: void reloadFiles(const QStringList& files); void listInstrumentData(QTreeWidgetItem *pParentItem, const qtractorInstrumentData& data); void listInstrumentDataList(QTreeWidgetItem *pParentItem, const qtractorInstrumentDataList& list, const QIcon& icon); QString bankSelMethod(int iBankSelMethod) const; private: // The Qt-designer UI struct... Ui::qtractorInstrumentForm m_ui; // Main editable data structure. qtractorInstrumentList *m_pInstruments; // Instance variables... QStringList m_files; int m_iDirtyCount; }; #endif // __qtractorInstrumentForm_h // end of qtractorInstrumentForm.h qtractor-1.5.9/src/PaxHeaders/qtractorPlugin.cpp0000644000000000000000000000013215101070305016744 xustar0030 mtime=1761898693.086267651 30 atime=1761898693.086267651 30 ctime=1761898693.086267651 qtractor-1.5.9/src/qtractorPlugin.cpp0000644000175000001440000022601015101070305016735 0ustar00rncbcusers// qtractorPlugin.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 "qtractorPlugin.h" #include "qtractorPluginFactory.h" #include "qtractorPluginListView.h" #include "qtractorPluginCommand.h" #include "qtractorPluginForm.h" #include "qtractorAudioEngine.h" #include "qtractorMidiManager.h" #include "qtractorInsertPlugin.h" #include "qtractorMainForm.h" #include "qtractorOptions.h" #include "qtractorSession.h" #include "qtractorCurveFile.h" #include "qtractorFileList.h" #include "qtractorMessageList.h" #include #include #include #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(4, 5, 0) namespace Qt { const WindowFlags WindowCloseButtonHint = WindowFlags(0x08000000); } #endif // Deprecated QTextStreamFunctions/Qt namespaces workaround. #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) #define endl Qt::endl #endif //---------------------------------------------------------------------------- // qtractorPluginFile -- Plugin file library instance. // // Executive methods. bool qtractorPluginFile::open (void) { // Check whether already open... if (m_module || ++m_iOpenCount > 1) return true; // Do the opening dance... if (m_module == nullptr) { const QByteArray aFilename = m_sFilename.toUtf8(); m_module = ::dlopen(aFilename.constData(), RTLD_LOCAL | RTLD_LAZY); } // Done alright. return (m_module != nullptr); } void qtractorPluginFile::close (void) { if (!m_module || --m_iOpenCount > 0) return; // ATTN: Might be really needed, as it would // otherwise pile up hosing all available RAM // until freed and unloaded on exit(); // nb. some VST might choke on auto-unload. if (m_bAutoUnload) { ::dlclose(m_module); m_module = nullptr; } } // Symbol resolver. void *qtractorPluginFile::resolve ( const char *symbol ) { return (m_module ? ::dlsym(m_module, symbol) : nullptr); } // Plugin file resgistry methods. qtractorPluginFile::Files qtractorPluginFile::g_files; qtractorPluginFile *qtractorPluginFile::addFile ( const QString& sFilename ) { qtractorPluginFile *pFile = g_files.value(sFilename, nullptr); if (pFile == nullptr && QLibrary::isLibrary(sFilename) #ifdef CONFIG_CLAP || QFileInfo(sFilename).suffix() == "clap" #endif ) { pFile = new qtractorPluginFile(sFilename); g_files.insert(pFile->filename(), pFile); } if (pFile && !pFile->open()) pFile = nullptr; if (pFile) pFile->addRef(); return pFile; } void qtractorPluginFile::removeFile ( qtractorPluginFile *pFile ) { if (pFile && pFile->removeRef()) { g_files.remove(pFile->filename()); delete pFile; } } //---------------------------------------------------------------------------- // qtractorPluginType -- Plugin type instance. // // Plugin filename accessor (default virtual). QString qtractorPluginType::filename (void) const { return (m_pFile ? m_pFile->filename() : QString()); } // Compute the number of instances needed // for the given input/output audio channels. unsigned short qtractorPluginType::instances ( unsigned short iChannels, bool bMidi ) const { unsigned short iInstances = 0; if (iChannels > 0) { if (bMidi && (m_iMidiIns > 0 || m_iMidiOuts > 0)) iInstances = 1; else if (m_iAudioOuts >= iChannels || m_iAudioIns >= iChannels) iInstances = 1; else if (m_iAudioOuts > 0) iInstances = (iChannels / m_iAudioOuts); } return iInstances; } // Plugin type(hint) textual helpers (static). qtractorPluginType::Hint qtractorPluginType::hintFromText ( const QString& sText ) { if (sText == "LADSPA") return Ladspa; else if (sText == "DSSI") return Dssi; else if (sText == "VST" || sText == "VST2") return Vst2; else if (sText == "VST3") return Vst3; else if (sText == "CLAP") return Clap; else if (sText == "LV2") return Lv2; else if (sText == "Insert") return Insert; else if (sText == "AuxSend") return AuxSend; else if (sText == "Control") return Control; else return Any; } QString qtractorPluginType::textFromHint ( qtractorPluginType::Hint typeHint ) { if (typeHint == Ladspa) return "LADSPA"; else if (typeHint == Dssi) return "DSSI"; else if (typeHint == Vst2) return "VST2"; else if (typeHint == Vst3) return "VST3"; else if (typeHint == Clap) return "CLAP"; else if (typeHint == Lv2) return "LV2"; else if (typeHint == Insert) return "Insert"; else if (typeHint == AuxSend) return "AuxSend"; else if (typeHint == Control) return "Control"; else return QObject::tr("(Any)"); } //---------------------------------------------------------------------------- // qtractorPlugin -- Plugin instance. // // Default preset name (global). QString qtractorPlugin::g_sDefPreset = QObject::tr("(default)"); // Constructors. qtractorPlugin::qtractorPlugin ( qtractorPluginList *pList, qtractorPluginType *pType ) : m_pList(pList), m_pType(pType), m_iUniqueID(0), m_iInstances(0), m_iActivated(0), m_bActivated(false), m_bAutoDeactivated(false), m_activateObserver(this), m_iActivateSubjectIndex(0), m_pLastUpdatedParam(nullptr), m_pLastUpdatedProperty(nullptr), m_pForm(nullptr), m_iEditorType(-1), m_iDirectAccessParamIndex(-1) { // Acquire a local unique id in chain... if (m_pList && m_pType) m_iUniqueID = m_pList->createUniqueID(m_pType); // Set default instance label... if (m_pType) m_sLabel = m_pType->label(); // Activate subject properties. m_activateSubject.setName(QObject::tr("Activate")); m_activateSubject.setToggled(true); } // Destructor. qtractorPlugin::~qtractorPlugin (void) { // Clear out all dependables... clearItems(); clearParams(); clearProperties(); // Rest of stuff goes cleaned too... if (m_pType) delete m_pType; } // Chain helper ones. unsigned short qtractorPlugin::channels (void) const { return (m_pList ? m_pList->channels() : 0); } // Set the internal instance count... void qtractorPlugin::setInstances ( unsigned short iInstances ) { // Some sanity required here... if (iInstances < 1) { // We're sorry but dialogs must also go now... closeEditor(); closeForm(true); } m_iInstances = iInstances; } // Activation methods. // immediate void qtractorPlugin::setActivated ( bool bActivated ) { updateActivated(bActivated); setActivatedEx(bActivated); } bool qtractorPlugin::isActivated (void) const { return m_bActivated; } // queued (GUI invocation) void qtractorPlugin::setActivatedEx ( bool bActivated ) { m_activateSubject.setValue(bActivated ? 1.0f : 0.0f); } bool qtractorPlugin::isActivatedEx (void) const { return (m_activateSubject.value() > 0.5f); } // auto-(de)activation bool qtractorPlugin::isAutoActivated (void) const { return m_bActivated && !m_bAutoDeactivated; } bool qtractorPlugin::isAutoDeactivated (void) const { return m_bActivated && m_bAutoDeactivated; } void qtractorPlugin::autoDeactivatePlugin ( bool bDeactivated ) { if (bDeactivated != m_bAutoDeactivated) { // deactivate? if (bDeactivated) { // was activated? if (m_bActivated) { m_iActivated = 0; deactivate(); if (m_pList) m_pList->updateActivated(false); } } else // reactivate? if (m_bActivated) { m_iActivated = 0; activate(); if (m_pList) m_pList->updateActivated(true); } m_bAutoDeactivated = bDeactivated; } } bool qtractorPlugin::canBeConnectedToOtherTracks (void) const { const qtractorPluginType::Hint hint = m_pType->typeHint(); return hint == qtractorPluginType::Insert || hint == qtractorPluginType::AuxSend || hint == qtractorPluginType::Control; } // Activation stabilizers. void qtractorPlugin::updateActivated ( bool bActivated ) { if (( bActivated && !m_bActivated) || (!bActivated && m_bActivated)) { // First time activate? if (bActivated && m_iActivated == 0) { activate(); ++m_iActivated; } // Let the change be... m_bActivated = bActivated; // Last time deactivate? if (!bActivated && m_iActivated == 0) deactivate(); // Auto-plugin-deactivation overrides standard-activation for plugins // without connections to other tracks (Inserts/AuxSends/Controllers) // otherwise user could (de)activate plugin without getting feedback const bool bIsConnectedToOtherTracks = canBeConnectedToOtherTracks(); if (!m_bAutoDeactivated || bIsConnectedToOtherTracks) { if (m_pList) m_pList->updateActivated(bActivated); } // Plugins connected to other tracks activation change // auto-plugin-deactivate for all tracks if (bIsConnectedToOtherTracks) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pSession->autoDeactivatePlugins(true); } } } void qtractorPlugin::updateActivatedEx ( bool bActivated ) { updateActivated(bActivated); // Get extra visual feedback as well, // iif. we're not exporting/freewheeling... // qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine == nullptr) return; if (pAudioEngine->isFreewheel()) return; QListIterator iter(m_items); while (iter.hasNext()) iter.next()->updateActivated(); updateFormActivated(); } // Internal activation methods. void qtractorPlugin::setChannelsActivated ( unsigned short iChannels, bool bActivated ) { if (iChannels > 0) { // First time activation?... if (!bActivated) ++m_iActivated; setActivated(bActivated); if (!bActivated) m_iActivated = 0; } else { m_iActivated = 0; updateActivated(bActivated); } } // Internal deactivation cleanup. void qtractorPlugin::cleanup (void) { m_bActivated = false; setChannels(0); } // Activate observer ctor. qtractorPlugin::ActivateObserver::ActivateObserver ( qtractorPlugin *pPlugin ) : qtractorMidiControlObserver(pPlugin->activateSubject()), m_pPlugin(pPlugin) { setCurveList(pPlugin->list()->curveList()); } // Activate observer updater. void qtractorPlugin::ActivateObserver::update ( bool bUpdate ) { qtractorMidiControlObserver::update(bUpdate); m_pPlugin->updateActivatedEx(m_pPlugin->isActivatedEx()); } // Plugin state serialization methods. void qtractorPlugin::setValueList ( const QStringList& vlist ) { // qSort(m_params); -- does not work with QHash... Params params; Params::ConstIterator param = m_params.constBegin(); const Params::ConstIterator& param_end = m_params.constEnd(); for ( ; param != param_end; ++param) params.insert(param.key(), param.value()); // Split it up... clearValues(); QStringListIterator val(vlist); QMapIterator iter(params); while (val.hasNext() && iter.hasNext()) m_values.index[iter.next().key()] = val.next().toFloat(); } QStringList qtractorPlugin::valueList (void) const { // qSort(m_params); -- does not work with QHash... Params params; Params::ConstIterator param = m_params.constBegin(); const Params::ConstIterator& param_end = m_params.constEnd(); for ( ; param != param_end; ++param) params.insert(param.key(), param.value()); // Join it up... QStringList vlist; QMapIterator iter(params); while (iter.hasNext()) vlist.append(QString::number(iter.next().value()->value())); return vlist; } // Reset-to-default method. void qtractorPlugin::reset (void) { Params::ConstIterator param = m_params.constBegin(); const Params::ConstIterator& param_end = m_params.constEnd(); for ( ; param != param_end; ++param) param.value()->reset(); } // Default copy/pass-through plugin processing procedure. (virtual) void qtractorPlugin::process ( float **ppIBuffer, float **ppOBuffer, unsigned int nframes ) { const unsigned short iChannels = channels(); for (unsigned short i = 0; i < iChannels; ++i) ::memcpy(ppOBuffer[i], ppIBuffer[i], nframes * sizeof(float)); } // Nominal plugin user-title (virtual). QString qtractorPlugin::title (void) const { QString sTitle = m_sAlias; if (sTitle.isEmpty() && m_pType) sTitle = m_pType->name(); return sTitle; } // Update editor title. void qtractorPlugin::updateEditorTitle (void) { QString sEditorTitle; if (m_pType && !alias().isEmpty()) sEditorTitle.append(QString("%1: ").arg(m_pType->name())); sEditorTitle.append(title()); if (m_pList && !m_pList->name().isEmpty()) sEditorTitle.append(QString(" - %1").arg(m_pList->name())); setEditorTitle(sEditorTitle); if (m_pForm) { sEditorTitle = editorTitle(); if (m_pType && m_pType->typeHint() == qtractorPluginType::AuxSend && alias().isEmpty()) sEditorTitle = QObject::tr("Aux Send: %1").arg(sEditorTitle); m_pForm->setWindowTitle(sEditorTitle); } } // List of observers management. void qtractorPlugin::addItem ( qtractorPluginListItem *pItem ) { m_items.append(pItem); } void qtractorPlugin::removeItem ( qtractorPluginListItem *pItem ) { int iItem = m_items.indexOf(pItem); if (iItem >= 0) m_items.removeAt(iItem); } void qtractorPlugin::clearItems (void) { qDeleteAll(m_items); m_items.clear(); } // Paremeters list accessors. void qtractorPlugin::addParam ( qtractorPlugin::Param *pParam ) { pParam->reset(); if (pParam->isLogarithmic()) pParam->observer()->setLogarithmic(true); m_params.insert(pParam->index(), pParam); m_paramNames.insert(pParam->name(), pParam); } void qtractorPlugin::removeParam ( qtractorPlugin::Param *pParam ) { m_paramNames.remove(pParam->name()); m_params.remove(pParam->index()); } void qtractorPlugin::clearParams (void) { qDeleteAll(m_params); m_params.clear(); m_paramNames.clear(); } // Properties registry accessor. void qtractorPlugin::addProperty ( qtractorPlugin::Property *pProp ) { m_properties.insert(pProp->index(), pProp); m_propertyKeys.insert(pProp->key(), pProp); } void qtractorPlugin::removeProperty ( qtractorPlugin::Property *pProp ) { m_propertyKeys.remove(pProp->name()); m_properties.remove(pProp->index()); } void qtractorPlugin::clearProperties (void) { qDeleteAll(m_properties); m_properties.clear(); m_propertyKeys.clear(); } // Special plugin form methods. void qtractorPlugin::openForm ( QWidget *pParent ) { // Take the change and create the form if it doesn't current exist. const bool bCreate = (m_pForm == nullptr); if (bCreate) { // Build up the plugin form... // What style do we create tool childs? Qt::WindowFlags wflags = Qt::Window; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && pOptions->bKeepToolsOnTop) { wflags |= Qt::Tool; // wflags |= Qt::WindowStaysOnTopHint; #if 0//QTRACTOR_PLUGIN_FORM_TOOL_PARENT // Make sure it has a parent... if (pParent == nullptr) pParent = qtractorMainForm::getInstance(); #endif } // Do it... m_pForm = new qtractorPluginForm(pParent, wflags); m_pForm->setPlugin(this); } // Activate form... if (!m_pForm->isVisible()) { m_pForm->toggleEditor(isEditorVisible()); m_pForm->show(); } m_pForm->raise(); m_pForm->activateWindow(); if (bCreate) moveWidgetPos(m_pForm, m_posForm); } void qtractorPlugin::closeForm ( bool bForce ) { if (m_pForm == nullptr) return; if (bForce) { m_pForm->close(); delete m_pForm; m_pForm = nullptr; } else if (m_pForm->isVisible()) m_pForm->hide(); } bool qtractorPlugin::isFormVisible (void) const { return (m_pForm && m_pForm->isVisible()); } void qtractorPlugin::toggleFormEditor ( bool bOn ) { if (m_pForm && m_pForm->isVisible()) m_pForm->toggleEditor(bOn); } void qtractorPlugin::updateFormDirtyCount (void) { if (m_pForm && m_pForm->isVisible()) m_pForm->updateDirtyCount(); } void qtractorPlugin::updateFormMidiControlAutoConnect (void) { if (m_pForm) m_pForm->updateMidiControlAutoConnect(); } void qtractorPlugin::updateFormAuxSendBusName (void) { if (m_pForm) m_pForm->updateAuxSendBusName(); } void qtractorPlugin::updateFormActivated (void) { if (m_pForm && m_pForm->isVisible()) m_pForm->updateActivated(); } void qtractorPlugin::refreshForm (void) { if (m_pForm && m_pForm->isVisible()) m_pForm->refresh(); } void qtractorPlugin::freezeFormPos (void) { if (m_pForm && m_pForm->isVisible()) m_posForm = m_pForm->pos(); } // Move widget to alleged parent center or else... void qtractorPlugin::moveWidgetPos ( QWidget *pWidget, const QPoint& pos ) const { QPoint wpos(pos); if (wpos.isNull() || wpos.x() < 0 || wpos.y() < 0) { QWidget *pParent = pWidget->parentWidget(); if (pParent == nullptr) pParent = qtractorMainForm::getInstance(); if (pParent) { QRect wrect(pWidget->geometry()); wrect.moveCenter(pParent->geometry().center()); wpos = wrect.topLeft(); } } if (!wpos.isNull() && wpos.x() >= 0 && wpos.y() >= 0) pWidget->move(wpos); } // Provisional preset accessors. QStringList qtractorPlugin::presetList (void) const { QStringList list; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { pOptions->settings().beginGroup(presetGroup()); list.append(pOptions->settings().childKeys()); pOptions->settings().endGroup(); } std::sort(list.begin(), list.end()); return list; } // Plugin default preset name accessor (informational) void qtractorPlugin::setPreset ( const QString& sPreset ) { m_sPreset = sPreset; if (m_pForm) m_pForm->setPreset(sPreset); } const QString& qtractorPlugin::preset (void) const { return m_sPreset; } // Plugin preset group - common identification group/prefix. QString qtractorPlugin::presetGroup (void) const { return "/Plugin/" + presetPrefix(); } // Normalize plugin identification prefix... QString qtractorPlugin::presetPrefix (void) const { return m_pType->label() + '_' + QString::number(m_pType->uniqueID()); } // Load plugin preset from xml file. bool qtractorPlugin::loadPresetFile ( const QString& sFilename ) { // Open file... QFile file(sFilename); if (!file.open(QIODevice::ReadOnly)) return false; // Parse it a-la-DOM :-) QDomDocument doc("qtractorPlugin"); if (!doc.setContent(&file)) { file.close(); return false; } file.close(); // Get root element. QDomElement ePreset = doc.documentElement(); if (ePreset.tagName() != "preset") return false; // Check if it's on the correct plugin preset... if (ePreset.attribute("type") != presetPrefix()) return false; // Reset any old configs. clearConfigs(); clearValues(); // Now parse for children... for (QDomNode nChild = ePreset.firstChild(); !nChild.isNull(); nChild = nChild.nextSibling()) { // Convert node to element, if any. QDomElement eChild = nChild.toElement(); if (eChild.isNull()) continue; // Check for preset item... if (eChild.tagName() == "configs" || eChild.tagName() == "configure") { // Parse for config entries... qtractorPlugin::loadConfigs(&eChild, m_configs, m_ctypes); } else if (eChild.tagName() == "params") { // Parse for param entries... qtractorPlugin::loadValues(&eChild, m_values); } } // Make it real. realizeConfigs(); realizeValues(); releaseConfigs(); releaseValues(); return true; } // Save plugin preset to xml file. bool qtractorPlugin::savePresetFile ( const QString& sFilename ) { freezeConfigs(); freezeValues(); QFileInfo fi(sFilename); QDomDocument doc("qtractorPlugin"); QDomElement ePreset = doc.createElement("preset"); ePreset.setAttribute("type", presetPrefix()); ePreset.setAttribute("name", fi.baseName()); ePreset.setAttribute("version", PROJECT_VERSION); // Save plugin configs... QDomElement eConfigs = doc.createElement("configs"); saveConfigs(&doc, &eConfigs); ePreset.appendChild(eConfigs); // Save plugin params... QDomElement eParams = doc.createElement("params"); saveValues(&doc, &eParams); ePreset.appendChild(eParams); doc.appendChild(ePreset); // Finally, we're ready to save to external file. QFile file(sFilename); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return false; QTextStream ts(&file); ts << doc.toString() << endl; file.close(); releaseConfigs(); return true; } // Load an existing preset by name. bool qtractorPlugin::loadPresetEx ( const QString& sPreset ) { // We'll need this, sure. qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return false; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return false; if (sPreset.isEmpty() || sPreset == g_sDefPreset) { // Reset to default... return pSession->execute(new qtractorResetPluginCommand(this)); } bool bResult = loadPreset(sPreset); if (bResult) { setPreset(sPreset); pMainForm->dirtyNotifySlot(); refreshForm(); } else { // An existing preset is about to be loaded... QSettings& settings = pOptions->settings(); // Should it be load from known file?... if (type()->isConfigure()) { settings.beginGroup(presetGroup()); bResult = loadPresetFileEx(settings.value(sPreset).toString()); settings.endGroup(); if (bResult) { setPreset(sPreset); pMainForm->dirtyNotifySlot(); refreshForm(); } } else { //...or make it as usual (parameter list only)... settings.beginGroup(presetGroup()); const QStringList& vlist = settings.value(sPreset).toStringList(); settings.endGroup(); if (!vlist.isEmpty()) { bResult = pSession->execute( new qtractorPresetPluginCommand(this, sPreset, vlist)); } } } return bResult; } // Load an existing preset from file. bool qtractorPlugin::loadPresetFileEx ( const QString& sFilename ) { const bool bActivated = isActivated(); setActivated(false); const bool bResult = loadPresetFile(sFilename); setActivated(bActivated); return bResult; } // Plugin parameter lookup. qtractorPlugin::Param *qtractorPlugin::paramFromName ( const QString& sName ) const { return m_paramNames.value(sName, nullptr); } // Direct access parameter qtractorPlugin::Param *qtractorPlugin::directAccessParam (void) const { if (isDirectAccessParam()) return findParam(m_iDirectAccessParamIndex); else return nullptr; } void qtractorPlugin::setDirectAccessParamIndex ( long iDirectAccessParamIndex ) { m_iDirectAccessParamIndex = iDirectAccessParamIndex; updateListViews(); } long qtractorPlugin::directAccessParamIndex (void) const { return m_iDirectAccessParamIndex; } bool qtractorPlugin::isDirectAccessParam (void) const { return (m_iDirectAccessParamIndex >= 0); } // Get all or some visual changes be announced.... void qtractorPlugin::updateListViews ( bool bRefresh ) { if (m_pList) { QListIterator iter(m_pList->views()); while (iter.hasNext()) { if (bRefresh) iter.next()->refresh(); else iter.next()->viewport()->update(); } } } // Plugin configuration/state snapshot. void qtractorPlugin::freezeConfigs (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorPlugin[%p]::freezeConfigs()", this); #endif // Do nothing... } void qtractorPlugin::releaseConfigs (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorPlugin[%p]::releaseConfigs()", this); #endif // Do nothing... } // Plugin parameter/state snapshot. void qtractorPlugin::freezeValues (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorPlugin[%p]::freezeValues()", this); #endif clearValues(); Params::ConstIterator param = m_params.constBegin(); const Params::ConstIterator& param_end = m_params.constEnd(); for ( ; param != param_end; ++param) { Param *pParam = param.value(); if (pParam->isValueEnabled()) { const unsigned long iIndex = pParam->index(); m_values.names[iIndex] = pParam->name(); m_values.index[iIndex] = pParam->value(); } } } void qtractorPlugin::releaseValues (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorPlugin[%p]::releaseValues()", this); #endif clearValues(); } // Plugin configure realization. void qtractorPlugin::realizeConfigs (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorPlugin[%p]::realizeConfigs()", this); #endif // Set configuration (CLOBs)... Configs::ConstIterator config = m_configs.constBegin(); const Configs::ConstIterator& config_end = m_configs.constEnd(); for ( ; config != config_end; ++config) configure(config.key(), config.value()); // Set proper bank/program selection... qtractorMidiManager *pMidiManager = nullptr; if (m_pList) pMidiManager = m_pList->midiManager(); if (pMidiManager) selectProgram(pMidiManager->currentBank(), pMidiManager->currentProg()); } // Plugin parameter realization. void qtractorPlugin::realizeValues (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorPlugin[%p]::realizeValues()", this); #endif // (Re)set parameter values (initial)... ValueIndex::ConstIterator param = m_values.index.constBegin(); const ValueIndex::ConstIterator& param_end = m_values.index.constEnd(); for ( ; param != param_end; ++param) { const unsigned long iIndex = param.key(); Param *pParam = findParam(iIndex); const QString& sName = m_values.names.value(iIndex); if (!sName.isEmpty() && !(pParam && sName == pParam->name())) { Param *pParamEx = m_paramNames.value(sName, nullptr); if (pParamEx) pParam = pParamEx; } if (pParam) pParam->setValue(param.value(), true); } } // Load plugin configuration stuff (CLOB). void qtractorPlugin::loadConfigs ( QDomElement *pElement, Configs& configs, ConfigTypes& ctypes ) { for (QDomNode nConfig = pElement->firstChild(); !nConfig.isNull(); nConfig = nConfig.nextSibling()) { // Convert config node to element... QDomElement eConfig = nConfig.toElement(); if (eConfig.isNull()) continue; if (eConfig.tagName() == "config") { const QString& sKey = eConfig.attribute("key"); if (!sKey.isEmpty()) { configs[sKey] = eConfig.text(); const QString& sType = eConfig.attribute("type"); if (!sType.isEmpty()) ctypes[sKey] = sType; } } } } // Load plugin parameter values. void qtractorPlugin::loadValues ( QDomElement *pElement, Values& values ) { for (QDomNode nParam = pElement->firstChild(); !nParam.isNull(); nParam = nParam.nextSibling()) { // Convert node to element, if any. QDomElement eParam = nParam.toElement(); if (eParam.isNull()) continue; // Check for config item... if (eParam.tagName() == "param") { const unsigned long iIndex = eParam.attribute("index").toULong(); const QString& sName = eParam.attribute("name"); if (!sName.isEmpty()) values.names.insert(iIndex, sName); values.index.insert(iIndex, eParam.text().toFloat()); } } } // Save plugin configuration stuff (CLOB)... void qtractorPlugin::saveConfigs ( QDomDocument *pDocument, QDomElement *pElement ) { // Save plugin configs... Configs::ConstIterator iter = m_configs.constBegin(); const Configs::ConstIterator& iter_end = m_configs.constEnd(); for ( ; iter != iter_end; ++iter) { QDomElement eConfig = pDocument->createElement("config"); eConfig.setAttribute("key", iter.key()); ConfigTypes::ConstIterator ctype = m_ctypes.find(iter.key()); if (ctype != m_ctypes.constEnd()) eConfig.setAttribute("type", ctype.value()); eConfig.appendChild( pDocument->createTextNode(iter.value())); pElement->appendChild(eConfig); } } // Save plugin parameter values. void qtractorPlugin::saveValues ( QDomDocument *pDocument, QDomElement *pElement ) { #if 0 Params::ConstIterator param = m_params.constBegin(); const Params::ConstIterator param_end = m_params.constEnd(); for ( ; param != param_end; ++param) { Param *pParam = param.value(); QDomElement eParam = pDocument->createElement("param"); eParam.setAttribute("name", pParam->name()); eParam.setAttribute("index", QString::number(pParam->index())); eParam.appendChild( pDocument->createTextNode(QString::number(pParam->value()))); pElement->appendChild(eParam); } #else ValueIndex::ConstIterator param = m_values.index.constBegin(); const ValueIndex::ConstIterator& param_end = m_values.index.constEnd(); for ( ; param != param_end; ++param) { const unsigned long iIndex = param.key(); const QString& sName = m_values.names.value(iIndex); QDomElement eParam = pDocument->createElement("param"); eParam.setAttribute("name", sName); eParam.setAttribute("index", QString::number(iIndex)); eParam.appendChild( pDocument->createTextNode(QString::number(param.value()))); pElement->appendChild(eParam); } #endif } // Parameter update executive. void qtractorPlugin::updateParamValue ( unsigned long iIndex, float fValue, bool bUpdate ) { Param *pParam = findParam(iIndex); if (pParam) pParam->updateValue(fValue, bUpdate); } // Load plugin parameter controllers (MIDI). void qtractorPlugin::loadControllers ( QDomElement *pElement, qtractorMidiControl::Controllers& controllers ) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; pMidiControl->loadControllers(pElement, controllers); } // Save plugin parameter controllers (MIDI). void qtractorPlugin::saveControllers ( qtractorDocument *pDocument, QDomElement *pElement ) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; qtractorMidiControl::Controllers controllers; unsigned long iActivateSubjectIndex = activateSubjectIndex(); Params::ConstIterator param = m_params.constBegin(); const Params::ConstIterator param_end = m_params.constEnd(); for ( ; param != param_end; ++param) { Param *pParam = param.value(); qtractorMidiControlObserver *pObserver = pParam->observer(); if (pMidiControl->isMidiObserverMapped(pObserver)) { qtractorMidiControl::Controller *pController = new qtractorMidiControl::Controller; pController->name = pParam->name(); pController->index = pParam->index(); pController->ctype = pObserver->type(); pController->channel = pObserver->channel(); pController->param = pObserver->param(); pController->logarithmic = pObserver->isLogarithmic(); pController->feedback = pObserver->isFeedback(); pController->invert = pObserver->isInvert(); pController->hook = pObserver->isHook(); pController->latch = pObserver->isLatch(); controllers.append(pController); } if (iActivateSubjectIndex < pParam->index()) iActivateSubjectIndex = pParam->index(); } Properties::ConstIterator prop = m_properties.constBegin(); const Properties::ConstIterator prop_end = m_properties.constEnd(); for ( ; prop != prop_end; ++prop) { Property *pProp = prop.value(); qtractorMidiControlObserver *pObserver = pProp->observer(); if (pMidiControl->isMidiObserverMapped(pObserver)) { qtractorMidiControl::Controller *pController = new qtractorMidiControl::Controller; pController->name = pProp->key(); pController->index = pProp->key_index(); pController->ctype = pObserver->type(); pController->channel = pObserver->channel(); pController->param = pObserver->param(); pController->logarithmic = pObserver->isLogarithmic(); pController->feedback = pObserver->isFeedback(); pController->invert = pObserver->isInvert(); pController->hook = pObserver->isHook(); pController->latch = pObserver->isLatch(); controllers.append(pController); } } qtractorMidiControlObserver *pActivateObserver = activateObserver(); if (pMidiControl->isMidiObserverMapped(pActivateObserver)) { setActivateSubjectIndex(iActivateSubjectIndex + 1); // hack up! qtractorMidiControl::Controller *pController = new qtractorMidiControl::Controller; pController->name = pActivateObserver->subject()->name(); pController->index = activateSubjectIndex(); pController->ctype = pActivateObserver->type(); pController->channel = pActivateObserver->channel(); pController->param = pActivateObserver->param(); pController->logarithmic = pActivateObserver->isLogarithmic(); pController->feedback = pActivateObserver->isFeedback(); pController->invert = pActivateObserver->isInvert(); pController->hook = pActivateObserver->isHook(); pController->latch = pActivateObserver->isLatch(); controllers.append(pController); } pMidiControl->saveControllers(pDocument, pElement, controllers); qDeleteAll(controllers); } // Map/realize plugin parameter controllers (MIDI). void qtractorPlugin::mapControllers ( const qtractorMidiControl::Controllers& controllers ) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; const unsigned long iActivateSubjectIndex = activateSubjectIndex(); QListIterator iter(controllers); while (iter.hasNext()) { qtractorMidiControl::Controller *pController = iter.next(); qtractorMidiControlObserver *pObserver = nullptr; if ((iActivateSubjectIndex > 0 && iActivateSubjectIndex == pController->index) || (activateSubject()->name() == pController->name)) { pObserver = activateObserver(); // setActivateSubjectIndex(0); // hack down! // iActivateSubjectIndex = 0; } else { Param *pParam = nullptr; Property *pProp = nullptr; if (!pController->name.isEmpty()) { pProp = m_propertyKeys.value(pController->name, nullptr); if (pProp && pController->index == pProp->key_index()) pObserver = pProp->observer(); else pParam = m_paramNames.value(pController->name, nullptr); } if (pParam == nullptr && pProp == nullptr) pParam = findParam(pController->index); if (pParam) pObserver = pParam->observer(); } 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); } } } // Load plugin automation curves (monitor, gain, pan, record, mute, solo). void qtractorPlugin::loadCurveFile ( QDomElement *pElement, qtractorCurveFile *pCurveFile ) { if (pCurveFile) pCurveFile->load(pElement); } // Save plugin automation curves (monitor, gain, pan, record, mute, solo). void qtractorPlugin::saveCurveFile ( qtractorDocument *pDocument, QDomElement *pElement, qtractorCurveFile *pCurveFile ) { if (pCurveFile == nullptr) return; qtractorCurveList *pCurveList = pCurveFile->list(); if (pCurveList == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; pCurveFile->clear(); pCurveFile->setBaseDir(pSession->sessionDir()); unsigned short iItem = 0; unsigned long iActivateSubjectIndex = activateSubjectIndex(); Params::ConstIterator param = m_params.constBegin(); const Params::ConstIterator param_end = m_params.constEnd(); for ( ; param != param_end; ++param) { Param *pParam = param.value(); qtractorCurve *pCurve = pParam->subject()->curve(); if (pCurve) { qtractorCurveFile::Item *pCurveItem = new qtractorCurveFile::Item; pCurveItem->name = pParam->name(); pCurveItem->index = pParam->index(); if (pParam->isToggled() || pParam->isInteger()) { const unsigned short controller = (iItem % 0x7f); if (controller == 0x00 || controller == 0x20) ++iItem; // Avoid bank-select controllers, please. pCurveItem->ctype = qtractorMidiEvent::CONTROLLER; pCurveItem->param = (iItem % 0x7f); } else { pCurveItem->ctype = qtractorMidiEvent::NONREGPARAM; pCurveItem->param = (iItem % 0x3fff); } pCurveItem->channel = ((iItem / 0x7f) % 16); 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); ++iItem; } if (iActivateSubjectIndex < pParam->index()) iActivateSubjectIndex = pParam->index(); } Properties::ConstIterator prop = m_properties.constBegin(); const Properties::ConstIterator prop_end = m_properties.constEnd(); for ( ; prop != prop_end; ++prop) { Property *pProp = prop.value(); if (!pProp->isAutomatable()) continue; qtractorCurve *pCurve = pProp->subject()->curve(); if (pCurve) { qtractorCurveFile::Item *pCurveItem = new qtractorCurveFile::Item; pCurveItem->name = pProp->key(); pCurveItem->index = pProp->key_index(); if (pProp->isToggled() || pProp->isInteger()) { const unsigned short controller = (iItem % 0x7f); if (controller == 0x00 || controller == 0x20) ++iItem; // Avoid bank-select controllers, please. pCurveItem->ctype = qtractorMidiEvent::CONTROLLER; pCurveItem->param = (iItem % 0x7f); } else { pCurveItem->ctype = qtractorMidiEvent::NONREGPARAM; pCurveItem->param = (iItem % 0x3fff); } pCurveItem->channel = ((iItem / 0x7f) % 16); 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); ++iItem; } } // Activate subject curve... qtractorCurve *pCurve = activateSubject()->curve(); if (pCurve) { setActivateSubjectIndex(iActivateSubjectIndex + 1); // hack up! qtractorCurveFile::Item *pCurveItem = new qtractorCurveFile::Item; pCurveItem->name = pCurve->subject()->name(); pCurveItem->index = activateSubjectIndex(); pCurveItem->ctype = qtractorMidiEvent::CONTROLLER; pCurveItem->channel = 0; const unsigned short controller = (iItem % 0x7f); if (controller == 0x00 || controller == 0x20) ++iItem; // Avoid bank-select controllers, please. pCurveItem->param = (iItem % 0x7f); 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; QString sBaseName(list()->name()); sBaseName += '_'; sBaseName += type()->label(); sBaseName += '_'; sBaseName += QString::number(uniqueID(), 16); sBaseName += "_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 plugin automation curves (monitor, gain, pan, record, mute, solo). void qtractorPlugin::applyCurveFile ( qtractorCurveFile *pCurveFile ) { 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; pCurveFile->setBaseDir(pSession->sessionDir()); qtractorSubject *pActivateSubject = activateSubject(); unsigned long iActivateSubjectIndex = activateSubjectIndex(); QListIterator iter(pCurveFile->items()); while (iter.hasNext()) { qtractorCurveFile::Item *pCurveItem = iter.next(); if ((iActivateSubjectIndex > 0 && iActivateSubjectIndex == pCurveItem->index) || (pActivateSubject->name() == pCurveItem->name)) { pCurveItem->subject = pActivateSubject; setActivateSubjectIndex(0); // hack down! iActivateSubjectIndex = 0; } else { Param *pParam = nullptr; Property *pProp = nullptr; if (!pCurveItem->name.isEmpty()) { pProp = m_propertyKeys.value(pCurveItem->name, nullptr); if (pProp && pCurveItem->index == pProp->key_index()) pCurveItem->subject = pProp->subject(); else pParam = m_paramNames.value(pCurveItem->name, nullptr); } if (pParam == nullptr && pProp == nullptr) pParam = findParam(pCurveItem->index); if (pParam) pCurveItem->subject = pParam->subject(); } } pCurveFile->apply(pSession->timeScale()); } // Save partial plugin state... bool qtractorPlugin::savePlugin ( qtractorDocument *pDocument, QDomElement *pElement ) { freezeConfigs(); freezeValues(); qtractorPluginType *pType = type(); pElement->setAttribute("type", qtractorPluginType::textFromHint(pType->typeHint())); // Pseudo-plugins don't have a file... const QString& sFilename = pType->filename(); if (!sFilename.isEmpty()) { pDocument->saveTextElement("filename", sFilename, pElement); } if (list()->isUniqueID(pType)) { pDocument->saveTextElement("unique-id", QString::number(uniqueID()), pElement); } pDocument->saveTextElement("index", QString::number(pType->index()), pElement); const QString& sLabel = label(); if (!sLabel.isEmpty()) pDocument->saveTextElement("label", sLabel, pElement); const QString& sAlias = alias(); if (!sAlias.isEmpty()) pDocument->saveTextElement("alias", sAlias, pElement); const QString& sPreset = preset(); if (!sPreset.isEmpty()) pDocument->saveTextElement("preset", sPreset, pElement); const long iDirectAccessParamIndex = directAccessParamIndex(); if (iDirectAccessParamIndex >= 0) { pDocument->saveTextElement("direct-access-param", QString::number(iDirectAccessParamIndex), pElement); } pDocument->saveTextElement("activated", qtractorDocument::textFromBool(isActivated()), pElement); // Plugin configuration stuff (CLOB)... QDomElement eConfigs = pDocument->document()->createElement("configs"); saveConfigs(pDocument->document(), &eConfigs); pElement->appendChild(eConfigs); // Plugin parameter values... QDomElement eParams = pDocument->document()->createElement("params"); saveValues(pDocument->document(), &eParams); pElement->appendChild(eParams); // May release plugin state... releaseConfigs(); releaseValues(); return true; } // Save complete plugin state... bool qtractorPlugin::savePluginEx ( qtractorDocument *pDocument, QDomElement *pElement ) { // Freeze form position, if currently visible... freezeFormPos(); // Save partial plugin state first... const bool bResult = savePlugin(pDocument, pElement); if (bResult) { // Plugin paramneter controllers... QDomElement eControllers = pDocument->document()->createElement("controllers"); saveControllers(pDocument, &eControllers); pElement->appendChild(eControllers); // Save plugin automation... qtractorCurveList *pCurveList = list()->curveList(); if (pCurveList && !pCurveList->isEmpty()) { qtractorCurveFile cfile(pCurveList); QDomElement eCurveFile = pDocument->document()->createElement("curve-file"); saveCurveFile(pDocument, &eCurveFile, &cfile); pElement->appendChild(eCurveFile); const unsigned long iActivateSubjectIndex = activateSubjectIndex(); if (iActivateSubjectIndex > 0) { pDocument->saveTextElement("activate-subject-index", QString::number(iActivateSubjectIndex), pElement); } } // Save editor position... const QPoint& posEditor = editorPos(); if (posEditor.x() >= 0 && posEditor.y() >= 0) { pDocument->saveTextElement("editor-pos", QString::number(posEditor.x()) + ',' + QString::number(posEditor.y()), pElement); } const QPoint& posForm = formPos(); if (posForm.x() >= 0 && posForm.y() >= 0) { pDocument->saveTextElement("form-pos", QString::number(posForm.x()) + ',' + QString::number(posForm.y()), pElement); } const int iEditorType = editorType(); if (iEditorType > 0) { pDocument->saveTextElement("editor-type", QString::number(iEditorType), pElement); } } return bResult; } //---------------------------------------------------------------------------- // qtractorPlugin::Param -- Plugin parameter (control input port) instance. // // Current port value. void qtractorPlugin::Param::setValue ( float fValue, bool bUpdate ) { // Decimals caching.... if (m_iDecimals < 0) { m_iDecimals = 0; if (!isInteger()) { const float fDecs = ::log10f(maxValue() - minValue()); if (fDecs < 0.0f) m_iDecimals = 6; else if (fDecs < 3.0f) m_iDecimals = 3; else if (fDecs < 6.0f) m_iDecimals = 1; #if 0 if (isLogarithmic()) ++m_iDecimals; #endif } // Make this permanent... m_subject.setToggled(isToggled()); m_subject.setInteger(isInteger()); } // Sanitize value... if (isBoundedAbove() && fValue > maxValue()) fValue = maxValue(); else if (isBoundedBelow() && fValue < minValue()) fValue = minValue(); m_observer.setValue(fValue); // Update specifics. if (bUpdate) m_pPlugin->updateParam(this, fValue, true); if (m_pPlugin->directAccessParamIndex() == long(m_iIndex)) m_pPlugin->updateListViews(); } // Parameter update executive method. void qtractorPlugin::Param::updateValue ( float fValue, bool bUpdate ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorPlugin::Param[%p]::updateValue(%g, %d)", this, fValue, int(bUpdate)); #endif // If immediately the same, make it directly dirty... if (m_pPlugin->isLastUpdatedParam(this)) { setValue(fValue, bUpdate); m_pPlugin->updateFormDirtyCount(); return; } // Make it a undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { pSession->execute( new qtractorPluginParamCommand(this, fValue, bUpdate)); // m_pPlugin->setLastUpdatedParam(this); } } // Virtual observer updater. void qtractorPlugin::Param::update ( float fValue, bool bUpdate ) { qtractorPlugin *pPlugin = plugin(); if (bUpdate && pPlugin->directAccessParamIndex() == long(index())) pPlugin->updateListViews(); pPlugin->updateParam(this, fValue, bUpdate); } // Constructor. qtractorPlugin::Param::Observer::Observer ( Param *pParam ) : qtractorMidiControlObserver(pParam->subject()), m_pParam(pParam) { setCurveList((pParam->plugin())->list()->curveList()); } // Virtual observer updater. void qtractorPlugin::Param::Observer::update ( bool bUpdate ) { m_pParam->update(qtractorMidiControlObserver::value(), bUpdate); qtractorMidiControlObserver::update(bUpdate); } //---------------------------------------------------------------------------- // qtractorPlugin::Property -- Plugin property (aka. parameter) instance. // // Current property value. void qtractorPlugin::Property::setVariant ( const QVariant& value, bool bUpdate ) { // Whether it's a scalar value... // if (isAutomatable() && !bUpdate) { // Sanitize value... float fValue = value.toFloat(); if (fValue > maxValue()) fValue = maxValue(); else if (fValue < minValue()) fValue = minValue(); setValue(fValue, false); } // Set main real value anyhow... m_value = value; } // Virtual observer updater. void qtractorPlugin::Property::update ( float fValue, bool bUpdate ) { setVariant(fValue, bUpdate); } //---------------------------------------------------------------------------- // qtractorPluginList -- Plugin chain list instance. // // Constructor. qtractorPluginList::qtractorPluginList ( unsigned short iChannels, unsigned int iFlags ) : m_iChannels(iChannels), m_iFlags(iFlags), m_iActivated(0), m_pMidiManager(nullptr), m_iMidiBank(-1), m_iMidiProg(-1), m_pMidiProgramSubject(nullptr), m_bAutoDeactivated(false), m_bAudioOutputMonitor(false), m_bLatency(false), m_iLatency(0) { setAutoDelete(true); m_pppBuffers[0] = nullptr; m_pppBuffers[1] = nullptr; m_pCurveList = new qtractorCurveList(); m_bAudioOutputBus = qtractorMidiManager::isDefaultAudioOutputBus(); m_bAudioOutputAutoConnect = qtractorMidiManager::isDefaultAudioOutputAutoConnect(); m_iAudioInsertActivated = 0; setChannels(iChannels, iFlags); } // Destructor. qtractorPluginList::~qtractorPluginList (void) { // Reset allocated channel buffers. setChannels(0, 0); // Clear out all dependables... m_views.clear(); delete m_pCurveList; } // The title to show up on plugin forms... void qtractorPluginList::setName ( const QString& sName ) { m_sName = sName; for (qtractorPlugin *pPlugin = first(); pPlugin; pPlugin = pPlugin->next()) { pPlugin->updateEditorTitle(); } if (m_pMidiManager) m_pMidiManager->resetAudioOutputBus(); } // Set all plugin chain number of channels. void qtractorPluginList::setChannels ( unsigned short iChannels, unsigned int iFlags ) { // Destroy any MIDI manager still there... if (m_pMidiManager) { m_bAudioOutputBus = m_pMidiManager->isAudioOutputBus(); m_bAudioOutputAutoConnect = m_pMidiManager->isAudioOutputAutoConnect(); m_sAudioOutputBusName = m_pMidiManager->audioOutputBusName(); m_bAudioOutputMonitor = m_pMidiManager->isAudioOutputMonitor(); m_pMidiManager->setAudioOutputMonitorEx(false); qtractorMidiManager::deleteMidiManager(m_pMidiManager); m_pMidiManager = nullptr; } if (m_pMidiProgramSubject) { delete m_pMidiProgramSubject; m_pMidiProgramSubject = nullptr; } // Go, go, go... m_iFlags = iFlags; // Allocate new MIDI manager, if applicable... if ((iChannels > 0) && (m_iFlags & Midi)) { m_pMidiProgramSubject = new MidiProgramSubject(m_iMidiBank, m_iMidiProg); m_pMidiManager = qtractorMidiManager::createMidiManager(this); qtractorAudioBus *pAudioOutputBus = m_pMidiManager->audioOutputBus(); if (pAudioOutputBus) { // Override number of channels from audio output bus... iChannels = pAudioOutputBus->channels(); // Restore it's connections if dedicated... if (m_pMidiManager->isAudioOutputBus()) pAudioOutputBus->outputs().copy(m_audioOutputs); } } // Allocate all new interim buffers... setChannelsEx(iChannels); // Reset all plugins number of channels... const bool bAudioOuts = resetChannels(iChannels, false); // FIXME: This should be better managed... if (m_pMidiManager) { m_pMidiManager->setAudioOutputMonitorEx(bAudioOuts); m_pMidiManager->updateInstruments(); } } void qtractorPluginList::setChannelsEx ( unsigned short iChannels ) { #if 0 // Maybe we don't need to change a thing here... if (iChannels == m_iChannels) return; #endif // Delete old interim buffer... if (m_pppBuffers[1]) { for (unsigned short i = 0; i < m_iChannels; ++i) delete [] m_pppBuffers[1][i]; delete [] m_pppBuffers[1]; m_pppBuffers[1] = nullptr; } // Go, go, go... m_iChannels = iChannels; // Allocate new interim buffers... if (m_iChannels > 0) { qtractorAudioEngine *pAudioEngine = nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pAudioEngine = pSession->audioEngine(); if (pAudioEngine) { const unsigned int iBufferSizeEx = pAudioEngine->bufferSizeEx(); m_pppBuffers[1] = new float * [m_iChannels]; for (unsigned short i = 0; i < m_iChannels; ++i) { m_pppBuffers[1][i] = new float [iBufferSizeEx]; ::memset(m_pppBuffers[1][i], 0, iBufferSizeEx * sizeof(float)); } } // Gone terribly wrong... else m_iChannels = 0; } } // Reset all plugin chain number of channels. bool qtractorPluginList::resetChannels ( unsigned short iChannels, bool bReset ) { // Whether to turn on/off any audio monitors/meters later... unsigned short iAudioOuts = 0; // Reset all plugin chain channels... for (qtractorPlugin *pPlugin = first(); pPlugin; pPlugin = pPlugin->next()) { if (bReset && iChannels > 0) { pPlugin->freezeConfigs(); pPlugin->freezeValues(); } pPlugin->setChannels(iChannels); if (bReset && iChannels > 0) { pPlugin->realizeConfigs(); pPlugin->realizeValues(); pPlugin->releaseConfigs(); pPlugin->releaseValues(); } iAudioOuts += pPlugin->audioOuts(); } // Turn on/off audio monitors/meters whether applicable... return (iAudioOuts > 0); } // Reset and (re)activate all plugin chain. void qtractorPluginList::resetBuffers (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine == nullptr) return; const unsigned int iBufferSizeEx = pAudioEngine->bufferSizeEx(); // Reset interim buffer, if any... if (m_pppBuffers[1]) { for (unsigned short i = 0; i < m_iChannels; ++i) ::memset(m_pppBuffers[1][i], 0, iBufferSizeEx * sizeof(float)); } } // Add-guarded plugin method. void qtractorPluginList::addPlugin ( qtractorPlugin *pPlugin ) { // Link the plugin into list... insertPlugin(pPlugin, pPlugin->next()); } // Insert-guarded plugin method. void qtractorPluginList::insertPlugin ( qtractorPlugin *pPlugin, qtractorPlugin *pNextPlugin ) { // We'll get prepared before plugging it in... pPlugin->setChannels(m_iChannels); if (pNextPlugin) insertBefore(pPlugin, pNextPlugin); else append(pPlugin); // Now update each observer list-view... QListIterator iter(m_views); while (iter.hasNext()) { qtractorPluginListView *pListView = iter.next(); int iNextItem = pListView->count(); if (pNextPlugin) iNextItem = pListView->pluginItem(pNextPlugin); qtractorPluginListItem *pNextItem = new qtractorPluginListItem(pPlugin); pListView->insertItem(iNextItem, pNextItem); pListView->setCurrentItem(pNextItem); } // Update plugins for auto-plugin-deactivation... autoDeactivatePlugins(m_bAutoDeactivated, true); } // Move-guarded plugin method. void qtractorPluginList::movePlugin ( qtractorPlugin *pPlugin, qtractorPlugin *pNextPlugin ) { // Source sanity... if (pPlugin == nullptr) return; qtractorPluginList *pPluginList = pPlugin->list(); if (pPluginList == nullptr) return; // Remove and insert back again... pPluginList->unlink(pPlugin); if (pNextPlugin) { insertBefore(pPlugin, pNextPlugin); } else { append(pPlugin); } // DANGER: Gasp, we might be not the same... if (pPluginList != this) { // Move all plugin automation curves... qtractorCurveList *pCurveList = pPluginList->curveList(); // Activation automation/curve... qtractorCurve *pCurve = pPlugin->activateSubject()->curve(); if (pCurve && pCurve->list() == pCurveList) { pCurveList->removeCurve(pCurve); m_pCurveList->addCurve(pCurve); } pPlugin->activateObserver()->setCurveList(m_pCurveList); // 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) { pCurveList->removeCurve(pCurve); m_pCurveList->addCurve(pCurve); } pParam->observer()->setCurveList(m_pCurveList); } // Now for the real thing... pPlugin->setPluginList(this); pPlugin->setChannels(channels()); if (pPlugin->isActivated()) { pPluginList->updateActivated(false); updateActivated(true); } } // Now update each observer list-view: // - take all items... QListIterator item(pPlugin->items()); while (item.hasNext()) delete item.next(); // - give them back into the right position... QListIterator view(m_views); while (view.hasNext()) { qtractorPluginListView *pListView = view.next(); int iNextItem = pListView->count(); if (pNextPlugin) iNextItem = pListView->pluginItem(pNextPlugin); qtractorPluginListItem *pNextItem = new qtractorPluginListItem(pPlugin); pListView->insertItem(iNextItem, pNextItem); pListView->setCurrentItem(pNextItem); } // update (both) lists for Auto-plugin-deactivation autoDeactivatePlugins(m_bAutoDeactivated, true); if (pPluginList != this) pPluginList->autoDeactivatePlugins(m_bAutoDeactivated, true); } // Remove-guarded plugin method. void qtractorPluginList::removePlugin ( qtractorPlugin *pPlugin ) { // Just unlink the plugin from the list... unlink(pPlugin); if (pPlugin->isActivated()) updateActivated(false); pPlugin->setChannels(0); pPlugin->clearItems(); // update Plugins for Auto-plugin-deactivation autoDeactivatePlugins(m_bAutoDeactivated, true); } // Clone/copy plugin method. qtractorPlugin *qtractorPluginList::copyPlugin ( qtractorPlugin *pPlugin ) { qtractorPluginType *pType = pPlugin->type(); if (pType == nullptr) return nullptr; // Clone the plugin instance... pPlugin->freezeConfigs(); pPlugin->freezeValues(); #if 0 // MIDI bank program whether necessary... int iBank = 0; int iProg = 0; if (m_pMidiManager && m_pMidiManager->currentBank() >= 0) iBank = m_pMidiManager->currentBank(); if (m_pMidiManager && m_pMidiManager->currentProg() >= 0) iProg = m_pMidiManager->currentProg(); #endif // Filename is empty for insert pseudo-plugins. const QString& sFilename = pType->filename(); qtractorPlugin *pNewPlugin = qtractorPluginFactory::createPlugin(this, sFilename, pType->index(), pType->typeHint()); if (pNewPlugin) { pNewPlugin->setAlias(pPlugin->alias()); pNewPlugin->setPreset(pPlugin->preset()); // Special case for audio Aux-sends copied into output buses... if ((flags() & qtractorPluginList::AudioOutBus) && (pType->typeHint() == qtractorPluginType::AuxSend) && (pType->index() > 0)) { // index == channels > 0 => Audio aux-send. qtractorAudioAuxSendPlugin *pAudioAuxSendPlugin = static_cast (pNewPlugin); if (pAudioAuxSendPlugin) { pAudioAuxSendPlugin->setAudioBusName(QString()); pAudioAuxSendPlugin->freezeConfigs(); } } else { // All other cases, proceed as usual... pNewPlugin->setConfigs(pPlugin->configs()); pNewPlugin->setConfigTypes(pPlugin->configTypes()); } pNewPlugin->setValues(pPlugin->values()); pNewPlugin->realizeConfigs(); pNewPlugin->realizeValues(); pNewPlugin->releaseConfigs(); pNewPlugin->releaseValues(); pNewPlugin->setActivated(pPlugin->isActivated()); pNewPlugin->setDirectAccessParamIndex( pPlugin->directAccessParamIndex()); } pPlugin->releaseConfigs(); pPlugin->releaseValues(); return pNewPlugin; } // List of views management. void qtractorPluginList::addView ( qtractorPluginListView *pView ) { m_views.append(pView); } void qtractorPluginList::removeView ( qtractorPluginListView *pView ) { const int iView = m_views.indexOf(pView); if (iView >= 0) m_views.removeAt(iView); } // The meta-main audio-processing plugin-chain procedure. void qtractorPluginList::process ( float **ppBuffer, unsigned int nframes ) { // Sanity checks... if (!isActivated()) return; if (ppBuffer == nullptr || *ppBuffer == nullptr || m_pppBuffers[1] == nullptr) return; // Start from first input buffer... m_pppBuffers[0] = ppBuffer; // Buffer binary iterator... unsigned short iBuffer = 0; // For each plugin in chain (in order, of course...) for (qtractorPlugin *pPlugin = first(); pPlugin; pPlugin = pPlugin->next()) { // Must be properly activated... if (!pPlugin->isActivated()) continue; // Set proper buffers for this plugin... float **ppIBuffer = m_pppBuffers[ iBuffer & 1]; float **ppOBuffer = m_pppBuffers[++iBuffer & 1]; // Time for the real thing... pPlugin->process(ppIBuffer, ppOBuffer, nframes); } // Now for the output buffer commitment... if (iBuffer & 1) { for (unsigned short i = 0; i < m_iChannels; ++i) { ::memcpy(ppBuffer[i], m_pppBuffers[1][i], nframes * sizeof(float)); } } } // Create/load plugin state. qtractorPlugin *qtractorPluginList::loadPlugin ( QDomElement *pElement ) { qtractorPlugin *pPlugin = nullptr; QString sFilename; QString sLabel; QString sAlias; QString sPreset; QStringList vlist; unsigned long iUniqueID = 0; unsigned long iIndex = 0; bool bActivated = false; unsigned long iActivateSubjectIndex = 0; long iDirectAccessParamIndex = -1; qtractorPlugin::Configs configs; qtractorPlugin::ConfigTypes ctypes; qtractorPlugin::Values values; qtractorMidiControl::Controllers controllers; qtractorCurveFile cfile(qtractorPluginList::curveList()); QPoint posEditor; QPoint posForm; int iEditorType = -1; const QString& sTypeHint = pElement->attribute("type"); qtractorPluginType::Hint typeHint = qtractorPluginType::hintFromText(sTypeHint); for (QDomNode nParam = pElement->firstChild(); !nParam.isNull(); nParam = nParam.nextSibling()) { // Convert buses list node to element... QDomElement eParam = nParam.toElement(); if (eParam.isNull()) continue; if (eParam.tagName() == "filename") sFilename = eParam.text(); else if (eParam.tagName() == "unique-id") iUniqueID = eParam.text().toULong(); else if (eParam.tagName() == "index") iIndex = eParam.text().toULong(); else if (eParam.tagName() == "label") sLabel = eParam.text(); else if (eParam.tagName() == "alias") sAlias = eParam.text(); else if (eParam.tagName() == "preset") sPreset = eParam.text(); else if (eParam.tagName() == "values") vlist = eParam.text().split(','); else if (eParam.tagName() == "activate-subject-index") iActivateSubjectIndex = eParam.text().toULong(); else if (eParam.tagName() == "activated") bActivated = qtractorDocument::boolFromText(eParam.text()); else if (eParam.tagName() == "configs") { // Load plugin configuration stuff (CLOB)... qtractorPlugin::loadConfigs(&eParam, configs, ctypes); } else if (eParam.tagName() == "params") { // Load plugin parameter values... qtractorPlugin::loadValues(&eParam, values); } else if (eParam.tagName() == "controllers") { // Load plugin parameter controllers... qtractorPlugin::loadControllers(&eParam, controllers); } else if (eParam.tagName() == "direct-access-param") iDirectAccessParamIndex = eParam.text().toLong(); else if (eParam.tagName() == "curve-file") { // Load plugin automation curves... qtractorPlugin::loadCurveFile(&eParam, &cfile); } else if (eParam.tagName() == "editor-pos") { const QStringList& sxy = eParam.text().split(','); posEditor.setX(sxy.at(0).toInt()); posEditor.setY(sxy.at(1).toInt()); } else if (eParam.tagName() == "form-pos") { const QStringList& sxy = eParam.text().split(','); posForm.setX(sxy.at(0).toInt()); posForm.setY(sxy.at(1).toInt()); } else if (eParam.tagName() == "editor-type") iEditorType = eParam.text().toInt(); } // Try to find some alternative, if it doesn't exist... if (checkPluginFile(sFilename, typeHint)) { pPlugin = qtractorPluginFactory::createPlugin(this, sFilename, iIndex, typeHint); } #if 0 if (!sFilename.isEmpty() && !sLabel.isEmpty() && ((pPlugin == nullptr) || (pPlugin->label() != sLabel))) { iIndex = 0; do { if (pPlugin) delete pPlugin; pPlugin = qtractorPluginFile::createPlugin(this, sFilename, iIndex++, typeHint); } while (pPlugin && (pPlugin->label() != sLabel)); } #endif if (pPlugin) { if (iUniqueID > 0) pPlugin->setUniqueID(iUniqueID); if (!sLabel.isEmpty()) pPlugin->setLabel(sLabel); if (!sAlias.isEmpty()) pPlugin->setAlias(sAlias); if (iActivateSubjectIndex > 0) pPlugin->setActivateSubjectIndex(iActivateSubjectIndex); pPlugin->setPreset(sPreset); pPlugin->setConfigs(configs); pPlugin->setConfigTypes(ctypes); if (!vlist.isEmpty()) pPlugin->setValueList(vlist); if (!values.index.isEmpty()) pPlugin->setValues(values); // append(pPlugin); pPlugin->mapControllers(controllers); pPlugin->applyCurveFile(&cfile); pPlugin->setDirectAccessParamIndex(iDirectAccessParamIndex); pPlugin->setActivated(bActivated); // Later's better! pPlugin->setEditorPos(posEditor); pPlugin->setFormPos(posForm); if (iEditorType >= 0) pPlugin->setEditorType(iEditorType); } else { qtractorMessageList::append( QObject::tr("%1(%2): %3 plugin not found.") .arg(sFilename).arg(iIndex).arg(sTypeHint)); } // Cleanup. qDeleteAll(controllers); controllers.clear(); return pPlugin; } // Document element methods. bool qtractorPluginList::loadElement ( qtractorDocument *pDocument, QDomElement *pElement ) { // Reset some MIDI manager elements... m_iMidiBank = -1; m_iMidiProg = -1; m_bAudioOutputBus = false; m_bAudioOutputAutoConnect = false; m_sAudioOutputBusName.clear(); m_audioOutputs.clear(); m_bLatency = false; m_iLatency = 0; // Load plugin-list children... for (QDomNode nPlugin = pElement->firstChild(); !nPlugin.isNull(); nPlugin = nPlugin.nextSibling()) { // Convert plugin node to element... QDomElement ePlugin = nPlugin.toElement(); if (ePlugin.isNull()) continue; if (ePlugin.tagName() == "bank") setMidiBank(ePlugin.text().toInt()); else if (ePlugin.tagName() == "program") setMidiProg(ePlugin.text().toInt()); else if (ePlugin.tagName() == "plugin") { qtractorPlugin *pPlugin = loadPlugin(&ePlugin); if (pPlugin) append(pPlugin); } else // Load audio output bus flag... if (ePlugin.tagName() == "audio-output-bus") { m_bAudioOutputBus = qtractorDocument::boolFromText(ePlugin.text()); } else // Load audio output bus name... if (ePlugin.tagName() == "audio-output-bus-name") { m_sAudioOutputBusName = ePlugin.text(); } else // Load audio output auto-connect flag... if (ePlugin.tagName() == "audio-output-auto-connect") { m_bAudioOutputAutoConnect = qtractorDocument::boolFromText(ePlugin.text()); } else // Load audio output connections... if (ePlugin.tagName() == "audio-outputs") { qtractorBus::loadConnects(m_audioOutputs, pDocument, &ePlugin); } // Make up audio output bus ... setAudioOutputBusName(m_sAudioOutputBusName); setAudioOutputAutoConnect(m_bAudioOutputAutoConnect); setAudioOutputBus(m_bAudioOutputBus); } return true; } bool qtractorPluginList::saveElement ( qtractorDocument *pDocument, QDomElement *pElement ) { // Save current MIDI bank/program setting... if (m_pMidiManager && m_pMidiManager->currentBank() >= 0) pDocument->saveTextElement("bank", QString::number(m_pMidiManager->currentBank()), pElement); if (m_pMidiManager && m_pMidiManager->currentProg() >= 0) pDocument->saveTextElement("program", QString::number(m_pMidiManager->currentProg()), pElement); // Save plugins... for (qtractorPlugin *pPlugin = qtractorPluginList::first(); pPlugin; pPlugin = pPlugin->next()) { // Create the new plugin element... QDomElement ePlugin = pDocument->document()->createElement("plugin"); pPlugin->savePluginEx(pDocument, &ePlugin); // Add this plugin... pElement->appendChild(ePlugin); } // Save audio output-bus connects... if (m_pMidiManager) { const bool bAudioOutputBus = m_pMidiManager->isAudioOutputBus(); pDocument->saveTextElement("audio-output-bus", qtractorDocument::textFromBool(bAudioOutputBus), pElement); const QString& sAudioOutputBusName = m_pMidiManager->audioOutputBusName(); if (!sAudioOutputBusName.isEmpty()) pDocument->saveTextElement("audio-output-bus-name", sAudioOutputBusName, pElement); pDocument->saveTextElement("audio-output-auto-connect", qtractorDocument::textFromBool( m_pMidiManager->isAudioOutputAutoConnect()), pElement); if (bAudioOutputBus) { qtractorAudioBus *pAudioBus = m_pMidiManager->audioOutputBus(); if (pAudioBus) { QDomElement eOutputs = pDocument->document()->createElement("audio-outputs"); qtractorBus::ConnectList outputs; pAudioBus->updateConnects(qtractorBus::Output, outputs); pAudioBus->saveConnects(outputs, pDocument, &eOutputs); pElement->appendChild(eOutputs); } } } return true; } // Acquire a unique plugin identifier in chain. unsigned long qtractorPluginList::createUniqueID ( qtractorPluginType *pType ) { const unsigned long k = pType->uniqueID(); const unsigned int i = m_uniqueIDs.value(k, 0); m_uniqueIDs.insert(k, i + 1); return k + i; } // Whether unique plugin identifiers are in chain. bool qtractorPluginList::isUniqueID ( qtractorPluginType *pType ) const { return (m_uniqueIDs.value(pType->uniqueID(), 0) > 1); } void qtractorPluginList::autoDeactivatePlugins ( bool bDeactivated, bool bForce ) { if (m_bAutoDeactivated != bDeactivated || bForce) { m_bAutoDeactivated = bDeactivated; unsigned short iAudioOuts = 0; if (bDeactivated) { bool bStopDeactivation = false; // Pass to all plugins bottom to top; // stop for any active plugins that are // possibly connected to other tracks... qtractorPlugin *pPlugin = last(); for ( ; pPlugin && !bStopDeactivation; pPlugin = pPlugin->prev()) { if (pPlugin->canBeConnectedToOtherTracks()) bStopDeactivation = pPlugin->isAutoActivated(); else pPlugin->autoDeactivatePlugin(bDeactivated); iAudioOuts += pPlugin->audioOuts(); } // (Re)activate all above stopper... for ( ; pPlugin; pPlugin = pPlugin->prev()) { if (bStopDeactivation) pPlugin->autoDeactivatePlugin(false); iAudioOuts += pPlugin->audioOuts(); } } else { // Pass to all plugins top to to bottom... for (qtractorPlugin *pPlugin = first(); pPlugin; pPlugin = pPlugin->next()) { pPlugin->autoDeactivatePlugin(bDeactivated); iAudioOuts += pPlugin->audioOuts(); } } // Take the chance to turn on/off automagically // the audio monitors/meters, when applicable... qtractorMidiManager *pMidiManager = midiManager(); if (pMidiManager) pMidiManager->setAudioOutputMonitorEx(iAudioOuts > 0); // Inform all views... QListIterator iter(m_views); while (iter.hasNext()) { qtractorPluginListView *pListView = iter.next(); pListView->refresh(); } } } bool qtractorPluginList::isAutoDeactivated (void) const { return m_bAutoDeactivated; } // Check/sanitize plugin file-path; bool qtractorPluginList::checkPluginFile ( QString& sFilename, qtractorPluginType::Hint typeHint ) const { // Care of internal pseudo-plugins... if (sFilename.isEmpty()) { return (typeHint == qtractorPluginType::Insert) || (typeHint == qtractorPluginType::AuxSend) || (typeHint == qtractorPluginType::Control); } // LV2 plug-ins are identified by URI... if (typeHint == qtractorPluginType::Lv2) return true; // Primary check for plugin pathname... QFileInfo fi(sFilename); if (fi.exists() && fi.isReadable()) return true; // Otherwise search for an alternative // under each respective search paths... qtractorPluginFactory *pPluginFactory = qtractorPluginFactory::getInstance(); if (pPluginFactory) { const QString fname = fi.fileName(); QStringListIterator iter(pPluginFactory->pluginPaths(typeHint)); while (iter.hasNext()) { fi.setFile(QDir(iter.next()), fname); if (fi.exists() && fi.isReadable()) { sFilename = fi.absoluteFilePath(); return true; } } } // No alternative has been found, sorry. return false; } // Recalculate plugin chain total latency (in frames)... unsigned long qtractorPluginList::currentLatency (void) const { unsigned long iLatency = 0; for (qtractorPlugin *pPlugin = first(); pPlugin; pPlugin = pPlugin->next()) { if (pPlugin->isActivated()) { // HACK: Dummy plugin processing for no single // frame, hopefully updating any output ports... if (m_iChannels > 0 && m_pppBuffers[1]) { float **ppIDummy = m_pppBuffers[1]; float **ppODummy = ppIDummy; pPlugin->process(ppIDummy, ppODummy, 0); } // Accumulate latency... iLatency += pPlugin->latency(); } } return iLatency; } // Plugin editors (GUI) visibility (auto-focus). void qtractorPluginList::setEditorVisibleAll ( bool bVisible ) { for (qtractorPlugin *pPlugin = first(); pPlugin; pPlugin = pPlugin->next()) { if (pPlugin->isEditorVisible()) pPlugin->setEditorVisible(bVisible); } } //------------------------------------------------------------------------- // qtractorPluginList::Document -- Plugins file import/export helper class. // // Constructor. qtractorPluginList::Document::Document ( QDomDocument *pDocument, qtractorPluginList *pPluginList ) : qtractorDocument(pDocument, "plugin-list"), m_pPluginList(pPluginList) { } // Default destructor. qtractorPluginList::Document::~Document (void) { } // Property accessors. qtractorPluginList *qtractorPluginList::Document::pluginList (void) const { return m_pPluginList; } //------------------------------------------------------------------------- // qtractorPluginList::Document -- loaders. // // External storage simple load method. bool qtractorPluginList::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() != "plugin-list") return false; return loadElement(&elem); } // Elemental loader... bool qtractorPluginList::Document::loadElement ( QDomElement *pElement ) { // Make it an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return false; qtractorImportPluginsCommand *pImportCommand = new qtractorImportPluginsCommand(); for (qtractorPlugin *pPlugin = m_pPluginList->first(); pPlugin; pPlugin = pPlugin->next()) { pImportCommand->removePlugin(pPlugin); } // Load plugin-list children... for (QDomNode nPlugin = pElement->firstChild(); !nPlugin.isNull(); nPlugin = nPlugin.nextSibling()) { // Convert plugin node to element... QDomElement ePlugin = nPlugin.toElement(); if (ePlugin.isNull()) continue; if (ePlugin.tagName() == "plugin") { qtractorPlugin *pPlugin = m_pPluginList->loadPlugin(&ePlugin); if (pPlugin) { pPlugin->realizeConfigs(); pPlugin->realizeValues(); pPlugin->releaseConfigs(); pPlugin->releaseValues(); pImportCommand->addPlugin(pPlugin); } } } pSession->execute(pImportCommand); return true; } //------------------------------------------------------------------------- // qtractorPluginList::Document -- savers. // // External storage simple save method. bool qtractorPluginList::Document::save ( const QString& sFilename ) { QFile file(sFilename); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) return false; QDomDocument *pDocument = document(); QDomElement elem = pDocument->createElement("plugin-list"); saveElement(&elem); pDocument->appendChild(elem); QTextStream ts(&file); ts << pDocument->toString() << endl; file.close(); return true; } // Elemental saver... bool qtractorPluginList::Document::saveElement ( QDomElement *pElement ) { // Save this program version (informational)... pElement->setAttribute("version", PROJECT_TITLE " " PROJECT_VERSION); // Save plugins... for (qtractorPlugin *pPlugin = m_pPluginList->first(); pPlugin; pPlugin = pPlugin->next()) { // Create the new plugin element... QDomElement ePlugin = document()->createElement("plugin"); pPlugin->savePlugin(this, &ePlugin); // Add this plugin... pElement->appendChild(ePlugin); } return true; } //------------------------------------------------------------------------- // class qtractorPliuginList::WaitCursor - A waiting (hour-glass) helper. // // Constructor. qtractorPluginList::WaitCursor::WaitCursor (void) { // Tell the world we'll (maybe) take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); } // Destructor. qtractorPluginList::WaitCursor::~WaitCursor (void) { // We're formerly done. QApplication::restoreOverrideCursor(); } // end of qtractorPlugin.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMidiEditor.cpp0000644000000000000000000000013215101070305017537 xustar0030 mtime=1761898693.080267632 30 atime=1761898693.080267632 30 ctime=1761898693.080267632 qtractor-1.5.9/src/qtractorMidiEditor.cpp0000644000175000001440000051612715101070305017543 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.9/src/PaxHeaders/qtractorTimeScaleForm.h0000644000000000000000000000013215101070305017645 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorTimeScaleForm.h0000644000175000001440000000577415101070305017652 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.9/src/PaxHeaders/qtractorAudioListView.h0000644000000000000000000000013215101070305017703 xustar0030 mtime=1761898693.063267578 30 atime=1761898693.063267578 30 ctime=1761898693.063267578 qtractor-1.5.9/src/qtractorAudioListView.h0000644000175000001440000000447615101070305017706 0ustar00rncbcusers// qtractorAudioListView.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 __qtractorAudioListView_h #define __qtractorAudioListView_h #include "qtractorFileListView.h" // Forward declarations. class qtractorAudioFile; class qtractorAudioListView; //---------------------------------------------------------------------- // class qtractorAudioFileItem -- MIDI file list view item. // class qtractorAudioFileItem : public qtractorFileListItem { public: // Constructor. qtractorAudioFileItem(const QString& sPath, qtractorAudioFile *pFile); protected: // Virtual tooltip renderer. QString toolTip() const; }; //---------------------------------------------------------------------------- // qtractorAudioListView -- Group/File list view, supporting drag-n-drop. // class qtractorAudioListView : public qtractorFileListView { Q_OBJECT public: // Constructor. qtractorAudioListView(QWidget *pParent = nullptr); // QListView::addColumn() ids. enum ItemColumn { Name = 0, Channels = 1, Frames = 2, Rate = 3, Time = 4, Path = 5, LastColumn = 6 }; 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 // __qtractorAudioListView_h // end of qtractorAudioListView.h qtractor-1.5.9/src/PaxHeaders/qtractorPluginSelectForm.h0000644000000000000000000000013215101070305020375 xustar0030 mtime=1761898693.087267654 30 atime=1761898693.087267654 30 ctime=1761898693.087267654 qtractor-1.5.9/src/qtractorPluginSelectForm.h0000644000175000001440000000412615101070305020370 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.9/src/PaxHeaders/audio0000644000000000000000000000013215101070305014246 xustar0030 mtime=1761898693.057007007 30 atime=1761898693.056842155 30 ctime=1761898693.057007007 qtractor-1.5.9/src/audio/0000755000175000001440000000000015101070305014313 5ustar00rncbcusersqtractor-1.5.9/src/audio/PaxHeaders/metro_beat.wav0000644000000000000000000000013215101070305017163 xustar0030 mtime=1761898693.057007007 30 atime=1761898693.056842155 30 ctime=1761898693.057007007 qtractor-1.5.9/src/audio/metro_beat.wav0000644000175000001440000002455415101070305017165 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.9/src/audio/PaxHeaders/metro_bar.wav0000644000000000000000000000013215101070305017014 xustar0030 mtime=1761898693.056842155 30 atime=1761898693.056842155 30 ctime=1761898693.056842155 qtractor-1.5.9/src/audio/metro_bar.wav0000644000175000001440000002455415101070305017016 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.9/src/PaxHeaders/qtractorFileSystem.h0000644000000000000000000000013215101070305017237 xustar0030 mtime=1761898693.071267604 30 atime=1761898693.071267604 30 ctime=1761898693.071267604 qtractor-1.5.9/src/qtractorFileSystem.h0000644000175000001440000000673415101070305017241 0ustar00rncbcusers// qtractorFileSystem.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 __qtractorFileSystem_h #define __qtractorFileSystem_h #include // forward decls. class QAction; class QToolButton; class QComboBox; class QTreeView; class QModelIndex; class QContextMenuEvent; //---------------------------------------------------------------------------- // qtractorFileSystem -- Custom composite widget. class qtractorFileSystem : public QDockWidget { Q_OBJECT public: // ctor. qtractorFileSystem(QWidget *pParent = nullptr); // dtor. ~qtractorFileSystem(); // Accessors. void setRootPath(const QString& sRootPath); QString rootPath() const; enum Flags { AllFiles = 1, SessionFiles = 2, AudioFiles = 4, MidiFiles = 8, HiddenFiles = 0x10 }; void setFlags(Flags flags, bool on = true); Flags flags() const; // Audition/pre-listening player methods. void setPlayState(bool bOn); bool isPlayState() const; // State saver/loader. QByteArray saveState() const; bool restoreState(const QByteArray& state); protected slots: // Chdir slots. void homeClicked(); void cdUpClicked(); void rootPathActivated(const QString& sRootPath); void treeViewActivated(const QModelIndex& index); // Filter slots. void filterChanged(); // Directory gathering thread doing sth... void restoreStateLoading(const QString& sPath); void restoreStateTimeout(); // Audition/pre-listening player slots. void playSlot(bool bOn); signals: // File entry activated. void activated(const QString& sFilename); protected: // Model factory method. void updateRootPath(const QString& sRootPath); // Model stabilizers. void updateRootPath(); void updateFilter(); // Just about to notify main-window that we're closing. void closeEvent(QCloseEvent *); // context-menu event handler. void contextMenuEvent(QContextMenuEvent *pContextMenuEvent); // Audition/pre-listening player method. void activateFile(const QString& sFilename); private: // Member actions... QAction *m_pHomeAction; QAction *m_pCdUpAction; QAction *m_pAllFilesAction; QAction *m_pSessionFilesAction; QAction *m_pAudioFilesAction; QAction *m_pMidiFilesAction; QAction *m_pHiddenFilesAction; QAction *m_pPlayAction; // Member widgets... QToolButton *m_pHomeToolButton; QToolButton *m_pCdUpToolButton; QComboBox *m_pRootPathComboBox; QTreeView *m_pFileSystemTreeView; class FileSystemModel; FileSystemModel *m_pFileSystemModel; // Restore state current-path. QString m_sRestoreStatePath; }; #endif // __qtractorFileSystem_h // end of qtractorFileSystem.h qtractor-1.5.9/src/PaxHeaders/qtractorPluginCommand.cpp0000644000000000000000000000013215101070305020243 xustar0030 mtime=1761898693.086267651 30 atime=1761898693.086267651 30 ctime=1761898693.086267651 qtractor-1.5.9/src/qtractorPluginCommand.cpp0000644000175000001440000006167715101070305020254 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(); // 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); // 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.9/src/PaxHeaders/qtractorAudioFile.cpp0000644000000000000000000000013215101070305017347 xustar0030 mtime=1761898693.063267578 30 atime=1761898693.063267578 30 ctime=1761898693.063267578 qtractor-1.5.9/src/qtractorAudioFile.cpp0000644000175000001440000001754315101070305017351 0ustar00rncbcusers// qtractorAudioFile.cpp // /**************************************************************************** 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. *****************************************************************************/ #include "qtractorAbout.h" #include "qtractorAudioFile.h" #include "qtractorAudioSndFile.h" #include "qtractorAudioVorbisFile.h" #include "qtractorAudioMadFile.h" #include #include //---------------------------------------------------------------------- // class qtractorAudioFileFactory -- Audio file factory (singleton). // // Initialize singleton instance pointer. qtractorAudioFileFactory *qtractorAudioFileFactory::g_pInstance = nullptr; // Singleton instance accessor. qtractorAudioFileFactory *qtractorAudioFileFactory::getInstance (void) { return g_pInstance; } // Constructor. qtractorAudioFileFactory::qtractorAudioFileFactory (void) { // Default file format/type (for capture/record) m_pDefaultFormat = nullptr; m_iDefaultFormat = 0; m_iDefaultQuality = 4; // Second for libsndfile stuff... FileFormat *pFormat; const QString sExtMask("*.%1"); const QString sFilterMask("%1 (%2)"); SF_FORMAT_INFO sffinfo; int iCount = 0; ::sf_command(nullptr, SFC_GET_FORMAT_MAJOR_COUNT, &iCount, sizeof(int)); for (int i = 0 ; i < iCount; ++i) { sffinfo.format = i; ::sf_command(nullptr, SFC_GET_FORMAT_MAJOR, &sffinfo, sizeof(sffinfo)); pFormat = new FileFormat; pFormat->type = SndFile; pFormat->name = QString(sffinfo.name) .replace('/', '-') // Replace some illegal characters. .replace('(', QString()) .replace(')', QString()); pFormat->ext = sffinfo.extension; pFormat->data = sffinfo.format; m_formats.append(pFormat); // Add for the extension map (should be unique)... QString sExt = pFormat->ext; QString sExts(sExtMask.arg(sExt)); if (!m_types.contains(sExt)) { m_types.insert(sExt, pFormat); // Take care of some old 8.3 convention, // specially regarding filename extensions... if (sExt.length() > 3) { sExt = sExt.left(3); if (!m_types.contains(sExt)) { sExts = sExtMask.arg(sExt) + ' ' + sExts; m_types.insert(sExt, pFormat); } } // Make a stance on the default format... if (sExt == "wav") m_pDefaultFormat = pFormat; } // What we see on dialog is some excerpt... m_filters.append( sFilterMask.arg(pFormat->name).arg(sExts)); } #ifdef CONFIG_LIBVORBIS // Add for libvorbis... pFormat = new FileFormat; pFormat->type = VorbisFile; pFormat->name = "OGG Vorbis"; pFormat->ext = "ogg"; pFormat->data = 0; m_formats.append(pFormat); m_types.insert(pFormat->ext, pFormat); m_filters.append( sFilterMask.arg(pFormat->name).arg(sExtMask.arg(pFormat->ext))); // Oh yeah, this will be the official default format... m_pDefaultFormat = pFormat; #endif #ifdef CONFIG_LIBMAD // Add for libmad (mp3 read-only)... pFormat = new FileFormat; pFormat->type = MadFile; pFormat->name = "MP3 MPEG-1 Audio Layer 3"; pFormat->ext = "mp3"; pFormat->data = 0; m_formats.append(pFormat); m_types.insert(pFormat->ext, pFormat); m_filters.append( sFilterMask.arg(pFormat->name).arg(sExtMask.arg(pFormat->ext))); #endif // Finally, simply build the all (most commonly) supported files entry. const QRegularExpression rx( "^(aif(|f)|fla(|c)|mp3|ogg|w(av|64))", QRegularExpression::CaseInsensitiveOption); QStringList exts; FileTypes::ConstIterator iter = m_types.constBegin(); const FileTypes::ConstIterator& iter_end = m_types.constEnd(); for ( ; iter != iter_end; ++iter) { const QString& sExt = iter.key(); if (rx.match(sExt).hasMatch()) exts.append(sExtMask.arg(sExt)); m_exts.append(sExt); } m_filters.prepend(QObject::tr("Audio files (%1)").arg(exts.join(" "))); m_filters.append(QObject::tr("All files (*.*)")); g_pInstance = this; } // Destructor. qtractorAudioFileFactory::~qtractorAudioFileFactory (void) { g_pInstance = nullptr; qDeleteAll(m_formats); m_formats.clear(); m_filters.clear(); m_types.clear(); m_exts.clear(); } // Factory method. (static) qtractorAudioFile *qtractorAudioFileFactory::createAudioFile ( const QString& sFilename, unsigned short iChannels, unsigned int iSampleRate, unsigned int iBufferSize, int iFormat ) { return g_pInstance->newAudioFile( sFilename, iChannels, iSampleRate, iBufferSize, iFormat); } // Internal factory method. qtractorAudioFile *qtractorAudioFileFactory::newAudioFile ( const QString& sFilename, unsigned short iChannels, unsigned int iSampleRate, unsigned int iBufferSize, int iFormat ) { const QString& sExt = QFileInfo(sFilename).suffix().toLower(); const FileFormat *pFormat = m_types.value(sExt, nullptr); if (pFormat == nullptr) return nullptr; switch (pFormat->type) { case SndFile: { if (iFormat < 0) iFormat = defaultFormat(); while (iFormat > 0 && // Retry down to PCM Signed 16-Bit... !qtractorAudioSndFile::isValidFormat(pFormat->data, iFormat)) --iFormat; return new qtractorAudioSndFile( iChannels, iSampleRate, iBufferSize, qtractorAudioSndFile::format(pFormat->data, iFormat)); } case VorbisFile: { if (iFormat < 0) iFormat = defaultQuality(); return new qtractorAudioVorbisFile( iChannels, iSampleRate, iBufferSize, qtractorAudioVorbisFile::quality(iFormat)); } case MadFile: return new qtractorAudioMadFile(iBufferSize); default: return nullptr; } } const qtractorAudioFileFactory::FileFormats& qtractorAudioFileFactory::formats (void) { return g_pInstance->m_formats; } const qtractorAudioFileFactory::FileTypes& qtractorAudioFileFactory::types (void) { return g_pInstance->m_types; } // The supported file types/names format lists. const QStringList& qtractorAudioFileFactory::filters (void) { return g_pInstance->m_filters; } const QStringList& qtractorAudioFileFactory::exts (void) { return g_pInstance->m_exts; } // Default audio file format accessors // (specific to capture/recording) void qtractorAudioFileFactory::setDefaultType ( const QString& sExt, int iType, int iFormat, int iQuality ) { // Reset for the obviusly trivial... g_pInstance->m_pDefaultFormat = nullptr; g_pInstance->m_iDefaultFormat = iFormat; g_pInstance->m_iDefaultQuality = iQuality; // Search for type-format... QListIterator iter(g_pInstance->m_formats); while (iter.hasNext()) { FileFormat *pFormat = iter.next(); if (sExt == pFormat->ext && (iType == 0 || iType == pFormat->data)) { g_pInstance->m_pDefaultFormat = pFormat; break; } } } QString qtractorAudioFileFactory::defaultExt (void) { FileFormat *pFormat = g_pInstance->m_pDefaultFormat; if (pFormat) return pFormat->ext; #ifdef CONFIG_LIBVORBIS_0 return "ogg"; #else return "wav"; #endif } int qtractorAudioFileFactory::defaultFormat (void) { return g_pInstance->m_iDefaultFormat; } int qtractorAudioFileFactory::defaultQuality (void) { return g_pInstance->m_iDefaultQuality; } // Check whether given file type/format is valid. (static) bool qtractorAudioFileFactory::isValidFormat ( const FileFormat *pFormat, int iFormat ) { if (pFormat == nullptr) return false; if (pFormat->type != SndFile) return true; return qtractorAudioSndFile::isValidFormat(pFormat->data, iFormat); } // end of qtractorAudioFile.cpp qtractor-1.5.9/src/PaxHeaders/qtractorClipForm.cpp0000644000000000000000000000013215101070305017221 xustar0030 mtime=1761898693.067267591 30 atime=1761898693.067267591 30 ctime=1761898693.067267591 qtractor-1.5.9/src/qtractorClipForm.cpp0000644000175000001440000006632015101070305017220 0ustar00rncbcusers// qtractorClipForm.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 "qtractorClipForm.h" #include "qtractorAbout.h" #include "qtractorClip.h" #include "qtractorClipCommand.h" #include "qtractorAudioClip.h" #include "qtractorMidiClip.h" #include "qtractorTimeStretcher.h" #include "qtractorSession.h" #include "qtractorOptions.h" #include "qtractorMainForm.h" #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); } //---------------------------------------------------------------------------- // Fade types curves. struct FadeTypeInfo { QString name; QIcon iconFadeIn; QIcon iconFadeOut; }; static QHash g_fadeTypes; void qtractorClipForm::initFadeTypes (void) { 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 }; if (g_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 = g_fadeTypes[i]; info.name = tr(s_aFadeTypeNames[i]); info.iconFadeIn = pmFadeIn.copy(i << 4, 0, 16, 16); info.iconFadeOut = pmFadeOut.copy(i << 4, 0, 16, 16); } } } //---------------------------------------------------------------------------- // qtractorClipForm -- UI wrapper form. // Constructor. qtractorClipForm::qtractorClipForm ( QWidget *pParent ) : QDialog(pParent) { // Setup UI struct... m_ui.setupUi(this); // Window modality (let plugin/tool windows rave around). QDialog::setWindowModality(Qt::WindowModal); // Initialize dirty control state. m_pClip = nullptr; m_bClipNew = false; m_pTimeScale = nullptr; m_iDirtyCount = 0; m_iDirtySetup = 0; initFadeTypes(); m_ui.FadeInTypeComboBox->clear(); m_ui.FadeOutTypeComboBox->clear(); for (int i = 0; i < g_fadeTypes.count(); ++i) { const FadeTypeInfo& info = g_fadeTypes.value(i); m_ui.FadeInTypeComboBox->addItem(info.iconFadeIn, info.name); m_ui.FadeOutTypeComboBox->addItem(info.iconFadeOut, info.name); } #if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) // Some conveniency cleaner helpers... m_ui.ClipNameLineEdit->setClearButtonEnabled(true); m_ui.FilenameComboBox->lineEdit()->setClearButtonEnabled(true); #endif // Try to set minimal window positioning. m_ui.TrackChannelTextLabel->hide(); m_ui.TrackChannelSpinBox->hide(); m_ui.AudioClipGroupBox->hide(); adjustSize(); // UI signal/slot connections... QObject::connect(m_ui.ClipNameLineEdit, SIGNAL(textChanged(const QString&)), SLOT(clipNameChanged(const QString&))); QObject::connect(m_ui.FilenameComboBox, SIGNAL(editTextChanged(const QString&)), SLOT(filenameChanged(const QString&))); QObject::connect(m_ui.FilenameToolButton, SIGNAL(clicked()), SLOT(browseFilename())); QObject::connect(m_ui.TrackChannelSpinBox, SIGNAL(valueChanged(int)), SLOT(trackChannelChanged(int))); QObject::connect(m_ui.ClipGainSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.ClipPanningSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.FormatComboBox, SIGNAL(activated(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.ClipStartSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(clipStartChanged(unsigned long))); QObject::connect(m_ui.ClipStartSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.ClipOffsetSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(changed())); QObject::connect(m_ui.ClipOffsetSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.ClipLengthSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(changed())); QObject::connect(m_ui.ClipOffsetSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.FadeInLengthSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(changed())); QObject::connect(m_ui.FadeInLengthSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.FadeInTypeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.FadeOutLengthSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(changed())); QObject::connect(m_ui.FadeOutLengthSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(formatChanged(int))); QObject::connect(m_ui.FadeOutTypeComboBox, SIGNAL(activated(int)), SLOT(changed())); QObject::connect(m_ui.TimeStretchSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.PitchShiftSpinBox, SIGNAL(valueChanged(double)), SLOT(changed())); QObject::connect(m_ui.WsolaTimeStretchCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.WsolaQuickSeekCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); #ifdef CONFIG_LIBRUBBERBAND QObject::connect(m_ui.RubberBandFormantCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); #ifdef CONFIG_LIBRUBBERBAND_R3 QObject::connect(m_ui.RubberBandFinerR3CheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); #endif #endif QObject::connect(m_ui.ClipMuteCheckBox, SIGNAL(stateChanged(int)), SLOT(changed())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(accepted()), SLOT(accept())); QObject::connect(m_ui.DialogButtonBox, SIGNAL(rejected()), SLOT(reject())); } // Destructor. qtractorClipForm::~qtractorClipForm (void) { // Don't forget to get rid of local time-scale instance... if (m_pTimeScale) delete m_pTimeScale; } // Populate (setup) dialog controls from settings descriptors. void qtractorClipForm::setClip ( qtractorClip *pClip ) { // Initialize conveniency options... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Mark that we're changing thing's here... ++m_iDirtySetup; QString sFilename; if (pClip) sFilename = pClip->filename(); // Clip properties cloning... m_pClip = pClip; m_bClipNew = sFilename.isEmpty(); // Why not change the dialog icon accordingly? if (m_bClipNew) QDialog::setWindowIcon(QIcon::fromTheme("clipNew")); // Copy from global time-scale instance... if (m_pTimeScale) delete m_pTimeScale; m_pTimeScale = new qtractorTimeScale(*pSession->timeScale()); m_ui.ClipStartSpinBox->setTimeScale(m_pTimeScale); m_ui.ClipOffsetSpinBox->setTimeScale(m_pTimeScale); m_ui.ClipLengthSpinBox->setTimeScale(m_pTimeScale); m_ui.FadeInLengthSpinBox->setTimeScale(m_pTimeScale); m_ui.FadeOutLengthSpinBox->setTimeScale(m_pTimeScale); // These have special delta formats... clipStartChanged(m_pClip->clipStart()); // Initialize dialog widgets... m_ui.ClipNameLineEdit->setText(m_pClip->clipName()); // Parameters... m_ui.ClipStartSpinBox->setValue(m_pClip->clipStart()); m_ui.ClipOffsetSpinBox->setValue(m_pClip->clipOffset()); m_ui.ClipLengthSpinBox->setValue(m_pClip->clipLength()); // Fade In/Out... m_ui.FadeInLengthSpinBox->setValue(m_pClip->fadeInLength()); m_ui.FadeInTypeComboBox->setCurrentIndex( indexFromFadeType(m_pClip->fadeInType())); m_ui.FadeOutLengthSpinBox->setValue(m_pClip->fadeOutLength()); m_ui.FadeOutTypeComboBox->setCurrentIndex( indexFromFadeType(m_pClip->fadeOutType())); // Set proper time scales display format... m_ui.FormatComboBox->setCurrentIndex( int(m_pTimeScale->displayFormat())); // Now those things specific on track type... const QString sSuffix = m_ui.FilenameComboBox->objectName(); switch (trackType()) { case qtractorTrack::Audio: { m_ui.FilenameComboBox->setObjectName("Audio" + sSuffix); m_ui.ClipGainTextLabel->setText(tr("&Gain:")); m_ui.ClipGainSpinBox->setSuffix(tr(" dB")); m_ui.ClipGainSpinBox->setRange(-60.0f, +24.0f); m_ui.ClipGainSpinBox->setValue(log10f2(m_pClip->clipGain())); m_ui.ClipPanningSpinBox->setRange(-1.0f, +1.0f); m_ui.ClipPanningSpinBox->setValue(pClip->clipPanning()); qtractorAudioClip *pAudioClip = static_cast (m_pClip); if (pAudioClip) { m_ui.TimeStretchSpinBox->setValue( 100.0f * pAudioClip->timeStretch()); m_ui.PitchShiftSpinBox->setValue( 12.0f * ::logf(pAudioClip->pitchShift()) / M_LN2); } m_ui.TrackChannelTextLabel->setVisible(false); m_ui.TrackChannelSpinBox->setVisible(false); m_ui.ClipPanningTextLabel->setVisible(true); m_ui.ClipPanningSpinBox->setVisible(true); m_ui.AudioClipGroupBox->setVisible(true); #ifdef CONFIG_LIBRUBBERBAND m_ui.WsolaTimeStretchCheckBox->setChecked( pAudioClip->isStretcherFlag(qtractorTimeStretcher::WsolaTimeStretch)); m_ui.RubberBandFormantCheckBox->setChecked( pAudioClip->isStretcherFlag(qtractorTimeStretcher::RubberBandFormant)); #ifdef CONFIG_LIBRUBBERBAND_R3 m_ui.RubberBandFinerR3CheckBox->setChecked( pAudioClip->isStretcherFlag(qtractorTimeStretcher::RubberBandFinerR3)); #else m_ui.RubberBandFinerR3CheckBox->hide(); #endif #else m_ui.PitchShiftTextLabel->setEnabled(false); m_ui.PitchShiftSpinBox->setEnabled(false); m_ui.WsolaTimeStretchCheckBox->setEnabled(false); m_ui.WsolaTimeStretchCheckBox->setChecked(true); m_ui.RubberBandFormantCheckBox->hide(); m_ui.RubberBandFinerR3CheckBox->hide(); #endif m_ui.WsolaQuickSeekCheckBox->setChecked( pAudioClip->isStretcherFlag(qtractorTimeStretcher::WsolaQuickSeek)); break; } case qtractorTrack::Midi: { m_ui.FilenameComboBox->setObjectName("Midi" + sSuffix); m_ui.ClipGainTextLabel->setText(tr("&Volume:")); m_ui.ClipGainSpinBox->setSuffix(tr(" %")); m_ui.ClipGainSpinBox->setRange(0.0f, 1200.0f); m_ui.ClipGainSpinBox->setValue(100.0f * m_pClip->clipGain()); qtractorMidiClip *pMidiClip = static_cast (m_pClip); if (pMidiClip) m_ui.TrackChannelSpinBox->setValue(pMidiClip->trackChannel()); m_ui.TrackChannelTextLabel->setVisible(true); m_ui.TrackChannelSpinBox->setVisible(true); m_ui.ClipPanningTextLabel->setVisible(false); m_ui.ClipPanningSpinBox->setVisible(false); m_ui.AudioClipGroupBox->setVisible(false); break; } case qtractorTrack::None: default: m_ui.TrackChannelTextLabel->setVisible(false); m_ui.TrackChannelSpinBox->setVisible(false); m_ui.AudioClipGroupBox->setVisible(false); break; } m_ui.ClipMuteCheckBox->setChecked(m_pClip->isClipMute()); qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) pOptions->loadComboBoxHistory(m_ui.FilenameComboBox); // Finally set clip filename... m_ui.FilenameComboBox->setEditText(sFilename); // Shake it a little bit first, but // make it as tight as possible... // resize(width() - 1, height() - 1); adjustSize(); // Backup clean. --m_iDirtySetup; m_iDirtyCount = 0; // Done. stabilizeForm(); } // Retrieve the accepted clip, if the case arises. qtractorClip *qtractorClipForm::clip (void) const { return m_pClip; } // Accept settings (OK button slot). void qtractorClipForm::accept (void) { // Sanity check... if (m_pClip == nullptr) return; if (!m_pClip->queryEditor()) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Save settings... if (m_iDirtyCount > 0) { // Cache the changed settings (if any)... qtractorClipCommand *pClipCommand = nullptr; qtractorTrack::TrackType clipType = trackType(); const QString& sFilename = m_ui.FilenameComboBox->currentText(); const unsigned short iTrackChannel = m_ui.TrackChannelSpinBox->value(); const QString& sClipName = m_ui.ClipNameLineEdit->text().trimmed(); float fClipGain = 1.0f; float fClipPanning = 0.0f; float fTimeStretch = 0.0f; float fPitchShift = 0.0f; unsigned int iStretcherFlags = 0; switch (clipType) { case qtractorTrack::Audio: fClipGain = pow10f2(m_ui.ClipGainSpinBox->value()); fClipPanning = m_ui.ClipPanningSpinBox->value(); fTimeStretch = 0.01f * m_ui.TimeStretchSpinBox->value(); fPitchShift = ::powf(2.0f, m_ui.PitchShiftSpinBox->value() / 12.0f); if (m_ui.WsolaTimeStretchCheckBox->isChecked()) iStretcherFlags |= qtractorTimeStretcher::WsolaTimeStretch; if (m_ui.WsolaQuickSeekCheckBox->isChecked()) iStretcherFlags |= qtractorTimeStretcher::WsolaQuickSeek; #ifdef CONFIG_LIBRUBBERBAND if (m_ui.RubberBandFormantCheckBox->isChecked()) iStretcherFlags |= qtractorTimeStretcher::RubberBandFormant; #ifdef CONFIG_LIBRUBBERBAND_R3 if (m_ui.RubberBandFinerR3CheckBox->isChecked()) iStretcherFlags |= qtractorTimeStretcher::RubberBandFinerR3; #endif #endif break; case qtractorTrack::Midi: fClipGain = 0.01f * m_ui.ClipGainSpinBox->value(); break; default: break; } const unsigned long iClipStart = m_ui.ClipStartSpinBox->value(); const unsigned long iClipOffset = m_ui.ClipOffsetSpinBox->value(); const unsigned long iClipLength = m_ui.ClipLengthSpinBox->value(); const unsigned long iFadeInLength = m_ui.FadeInLengthSpinBox->value(); qtractorClip::FadeType fadeInType = fadeTypeFromIndex(m_ui.FadeInTypeComboBox->currentIndex()); const unsigned long iFadeOutLength = m_ui.FadeOutLengthSpinBox->value(); qtractorClip::FadeType fadeOutType = fadeTypeFromIndex(m_ui.FadeOutTypeComboBox->currentIndex()); const bool bClipMute = m_ui.ClipMuteCheckBox->isChecked(); int iFileChange = 0; int iFlagsChange = 0; // It depends whether we're adding a new clip or not... if (m_bClipNew) { // Just set new clip properties... pClipCommand = new qtractorClipCommand(tr("new clip")); m_pClip->setClipName(sClipName); // Filename... m_pClip->setFilename(sFilename); // Track-channel and time-stretching... switch (clipType) { case qtractorTrack::Audio: { qtractorAudioClip *pAudioClip = static_cast (m_pClip); if (pAudioClip) { pAudioClip->setTimeStretch(fTimeStretch); pAudioClip->setPitchShift(fPitchShift); pAudioClip->setStretcherFlags(iStretcherFlags); ++iFileChange; } break; } case qtractorTrack::Midi: { qtractorMidiClip *pMidiClip = static_cast (m_pClip); if (pMidiClip) { pMidiClip->setTrackChannel(iTrackChannel); ++iFileChange; } break; } case qtractorTrack::None: default: break; } // Gain/panning... m_pClip->setClipGain(fClipGain); m_pClip->setClipPanning(fClipPanning); // Parameters... m_pClip->setClipStart(iClipStart); m_pClip->setClipOffset(iClipOffset); m_pClip->setClipLength(iClipLength); // Fade in... m_pClip->setFadeInLength(iFadeInLength); m_pClip->setFadeInType(fadeInType); // Fade out... m_pClip->setFadeOutLength(iFadeOutLength); m_pClip->setFadeOutType(fadeOutType); // Mute... m_pClip->setClipMute(bClipMute); // Ready it... pClipCommand->addClip(m_pClip, m_pClip->track()); // Ready new. } else { // Make changes (incrementally) undoable... pClipCommand = new qtractorClipCommand(tr("edit clip")); pClipCommand->renameClip(m_pClip, sClipName); // Filename changes... if (sFilename != m_pClip->filename()) ++iFileChange; // Track-channel and time-stretch issues... switch (clipType) { case qtractorTrack::Audio: { qtractorAudioClip *pAudioClip = static_cast (m_pClip); if (pAudioClip) { if (qAbs(fTimeStretch - pAudioClip->timeStretch()) < 0.001f) fTimeStretch = 0.0f; if (qAbs(fPitchShift - pAudioClip->pitchShift()) < 0.001f) fPitchShift = 0.0f; if (pAudioClip->stretcherFlags() != iStretcherFlags) ++iFlagsChange; } break; } case qtractorTrack::Midi: { qtractorMidiClip *pMidiClip = static_cast (m_pClip); if (pMidiClip) { if (iTrackChannel != pMidiClip->trackChannel()) ++iFileChange; } break; } case qtractorTrack::None: default: break; } // Filename and/or track-channel changes... if (iFileChange > 0) pClipCommand->fileClip(m_pClip, sFilename, iTrackChannel); // Gain/nning... if (qAbs(fClipGain - m_pClip->clipGain()) > 0.001f) pClipCommand->gainClip(m_pClip, fClipGain); if (qAbs(fClipPanning - m_pClip->clipPanning()) > 0.001f) pClipCommand->panningClip(m_pClip, fClipPanning); // Parameters and/or time-stretching changes... if (iClipStart != m_pClip->clipStart() || iClipOffset != m_pClip->clipOffset() || iClipLength != m_pClip->clipLength() || fTimeStretch > 0.0f || fPitchShift > 0.0f) { pClipCommand->resizeClip(m_pClip, iClipStart, iClipOffset, iClipLength, fTimeStretch, fPitchShift); } // Fade in changes... if (iFadeInLength != m_pClip->fadeInLength() || fadeInType != m_pClip->fadeInType()) pClipCommand->fadeInClip(m_pClip, iFadeInLength, fadeInType); // Fade out changes... if (iFadeOutLength != m_pClip->fadeOutLength() || fadeOutType != m_pClip->fadeOutType()) pClipCommand->fadeOutClip(m_pClip, iFadeOutLength, fadeOutType); // Audio clip time-stretch/pitch-shifting engine flags options... if (iFlagsChange > 0) pClipCommand->stretcherFlagsClip(m_pClip, iStretcherFlags); // Mute... if (( bClipMute && !m_pClip->isClipMute()) || (!bClipMute && m_pClip->isClipMute())) pClipCommand->muteClip(m_pClip, bClipMute); // Ready edit. } // Do it (by making it undoable)... if (pClipCommand) pSession->execute(pClipCommand); // Account for a new file in game... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (iFileChange > 0 && pMainForm) { switch (clipType) { case qtractorTrack::Audio: pMainForm->addAudioFile(sFilename); break; case qtractorTrack::Midi: pMainForm->addMidiFile(sFilename); break; case qtractorTrack::None: default: break; } // Save history conveniency options... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) pOptions->saveComboBoxHistory(m_ui.FilenameComboBox); } // Reset dirty flag. m_iDirtyCount = 0; } // Just go with dialog acceptance. QDialog::accept(); } // Reject settings (Cancel button slot). void qtractorClipForm::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 qtractorClipForm::changed (void) { ++m_iDirtyCount; stabilizeForm(); } // Display format has changed. void qtractorClipForm::formatChanged ( int iDisplayFormat ) { const bool bBlockSignals = m_ui.FormatComboBox->blockSignals(true); m_ui.FormatComboBox->setCurrentIndex(iDisplayFormat); qtractorTimeScale::DisplayFormat displayFormat = qtractorTimeScale::DisplayFormat(iDisplayFormat); m_ui.ClipStartSpinBox->setDisplayFormat(displayFormat); m_ui.ClipOffsetSpinBox->setDisplayFormat(displayFormat); m_ui.ClipLengthSpinBox->setDisplayFormat(displayFormat); m_ui.FadeInLengthSpinBox->setDisplayFormat(displayFormat); m_ui.FadeOutLengthSpinBox->setDisplayFormat(displayFormat); if (m_pTimeScale) m_pTimeScale->setDisplayFormat(displayFormat); m_ui.FormatComboBox->blockSignals(bBlockSignals); stabilizeForm(); } // Stabilize current form state. void qtractorClipForm::stabilizeForm (void) { const QString& sFilename = m_ui.FilenameComboBox->currentText(); const unsigned long iClipLength = m_ui.ClipLengthSpinBox->value(); m_ui.FadeInTypeComboBox->setEnabled( m_ui.FadeInLengthSpinBox->value() > 0); m_ui.FadeInLengthSpinBox->setMaximum(iClipLength); m_ui.FadeOutTypeComboBox->setEnabled( m_ui.FadeOutLengthSpinBox->value() > 0); m_ui.FadeOutLengthSpinBox->setMaximum(iClipLength); const bool bTimeStretch = (qAbs(float(m_ui.TimeStretchSpinBox->value()) - 100.0f) > 0.0f); const bool bWsolaTimeStretch = bTimeStretch && m_ui.WsolaTimeStretchCheckBox->isChecked(); m_ui.WsolaTimeStretchCheckBox->setEnabled(bTimeStretch); m_ui.WsolaQuickSeekCheckBox->setEnabled(bWsolaTimeStretch); const bool bPitchShift = (qAbs(float(m_ui.PitchShiftSpinBox->value())) > 0.0f); m_ui.RubberBandFormantCheckBox->setEnabled(bPitchShift); m_ui.RubberBandFinerR3CheckBox->setEnabled(bPitchShift || (bTimeStretch && !bWsolaTimeStretch)); bool bValid = (m_iDirtyCount > 0); bValid = bValid && !m_ui.ClipNameLineEdit->text().isEmpty(); bValid = bValid && !sFilename.isEmpty() && QFileInfo(sFilename).exists(); bValid = bValid && (iClipLength > 0); m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(bValid); } // Fade type index converters. qtractorClip::FadeType qtractorClipForm::fadeTypeFromIndex ( int iIndex ) const { return qtractorClip::FadeType(iIndex); } int qtractorClipForm::indexFromFadeType ( qtractorClip::FadeType fadeType ) const { return int(fadeType); } // Retrieve current clip/track type. qtractorTrack::TrackType qtractorClipForm::trackType (void) const { qtractorTrack *pTrack = nullptr; if (m_pClip) pTrack = m_pClip->track(); return (pTrack ? pTrack->trackType() : qtractorTrack::None); } // Browse for audio clip filename. void qtractorClipForm::browseFilename (void) { QString sType; QString sExt; QStringList filters; qtractorTrack::TrackType clipType = trackType(); switch (clipType) { case qtractorTrack::Audio: sType = tr("Audio"); sExt = qtractorAudioFileFactory::defaultExt(); filters = qtractorAudioFileFactory::filters(); break; case qtractorTrack::Midi: sType = tr("MIDI"); sExt = "mid"; filters.append(tr("MIDI files (*.%1 *.smf *.midi)").arg(sExt)); filters.append(tr("All files (*.*)")); break; case qtractorTrack::None: default: return; } const QString& sFilter = filters.join(";;"); // Browse for file... QString sFilename; const QString& sTitle = tr("%1 Clip File").arg(sType); 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) sFilename = QFileDialog::getOpenFileName(pParentWidget, sTitle, m_ui.FilenameComboBox->currentText(), sFilter, nullptr, options); #else QFileDialog fileDialog(pParentWidget, sTitle, m_ui.FilenameComboBox->currentText(), sFilter); // Set proper open-file modes... fileDialog.setAcceptMode(QFileDialog::AcceptOpen); fileDialog.setFileMode(QFileDialog::ExistingFile); fileDialog.setDefaultSuffix(sExt); // Stuff sidebar... if (pOptions) { QList urls(fileDialog.sidebarUrls()); urls.append(QUrl::fromLocalFile(pOptions->sSessionDir)); switch (clipType) { case qtractorTrack::Audio: urls.append(QUrl::fromLocalFile(pOptions->sAudioDir)); break; case qtractorTrack::Midi: urls.append(QUrl::fromLocalFile(pOptions->sMidiDir)); break; case qtractorTrack::None: default: break; } fileDialog.setSidebarUrls(urls); } fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sFilename = fileDialog.selectedFiles().first(); #endif if (!sFilename.isEmpty()) { m_ui.FilenameComboBox->setEditText(sFilename); m_ui.FilenameComboBox->setFocus(); // changed(); } } // Adjust clip length to (new) selected file. void qtractorClipForm::fileChanged ( const QString& sFilename, unsigned short iTrackChannel ) { if (m_iDirtySetup > 0) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Do nothing else if file is invalid... QFileInfo fi(sFilename); if (!fi.exists()) { changed(); return; } // Give as clip name hint if blank or new... if (m_ui.ClipNameLineEdit->text().isEmpty()) m_ui.ClipNameLineEdit->setText(fi.baseName()); // Depending on the clip/track type, // set clip length to the file length (in frames)... unsigned long iClipLength = 0; switch (trackType()) { case qtractorTrack::Midi: { qtractorMidiFile file; if (file.open(sFilename)) { const unsigned short p = pSession->ticksPerBeat(); const unsigned short q = file.ticksPerBeat(); const unsigned long iTrackDuration = file.readTrackDuration(iTrackChannel); if (iTrackDuration > 0) { const unsigned long duration = uint64_t(iTrackDuration * p) / q; iClipLength = pSession->frameFromTick(duration); } file.close(); } break; } case qtractorTrack::Audio: { qtractorAudioFile *pFile = qtractorAudioFileFactory::createAudioFile(sFilename); if (pFile) { if (pFile->open(sFilename)) { iClipLength = pFile->frames(); if (pFile->sampleRate() > 0 && pFile->sampleRate() != pSession->sampleRate()) { iClipLength = (unsigned long) (iClipLength * float(pSession->sampleRate()) / float(pFile->sampleRate())); } pFile->close(); } delete pFile; } break; } case qtractorTrack::None: default: break; } // Done. m_ui.ClipOffsetSpinBox->setValue(0); m_ui.ClipLengthSpinBox->setValue(iClipLength); changed(); } void qtractorClipForm::clipNameChanged ( const QString& sClipName ) { // Give as clip name hint if blank or new... if (sClipName.isEmpty()) { const QString& sFilename = m_ui.FilenameComboBox->currentText(); if (!sFilename.isEmpty()) m_ui.ClipNameLineEdit->setText(QFileInfo(sFilename).baseName()); } changed(); } void qtractorClipForm::filenameChanged ( const QString& sFilename ) { fileChanged(sFilename, m_ui.TrackChannelSpinBox->value()); } void qtractorClipForm::trackChannelChanged ( int iTrackChannel ) { fileChanged(m_ui.FilenameComboBox->currentText(), iTrackChannel); } // Adjust delta-value spin-boxes to new anchor frame. void qtractorClipForm::clipStartChanged ( unsigned long iClipStart ) { m_ui.ClipOffsetSpinBox->setDeltaValue(true, iClipStart); m_ui.ClipLengthSpinBox->setDeltaValue(true, iClipStart); m_ui.FadeInLengthSpinBox->setDeltaValue(true, iClipStart); m_ui.FadeOutLengthSpinBox->setDeltaValue(true, iClipStart); if (m_iDirtySetup == 0) changed(); } // end of qtractorClipForm.cpp qtractor-1.5.9/src/PaxHeaders/qtractorMidiControlObserverForm.cpp0000644000000000000000000000013215101070305022265 xustar0030 mtime=1761898693.078267626 30 atime=1761898693.078267626 30 ctime=1761898693.078267626 qtractor-1.5.9/src/qtractorMidiControlObserverForm.cpp0000644000175000001440000005023215101070305022257 0ustar00rncbcusers// qtractorMidiControlObserverForm.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 "qtractorMidiControlObserverForm.h" #include "qtractorMidiControlObserver.h" #include "qtractorMidiControlCommand.h" #include "qtractorMidiControlTypeGroup.h" #include "qtractorActionControl.h" #include "qtractorSession.h" #include "qtractorMainForm.h" #include "qtractorMidiEngine.h" #include "qtractorConnections.h" #include "qtractorCurve.h" #include "qtractorTracks.h" #include "qtractorTrackList.h" #include #include //---------------------------------------------------------------------------- // qtractorMidiControlObserverForm -- UI wrapper form. // Kind of singleton reference. qtractorMidiControlObserverForm * qtractorMidiControlObserverForm::g_pMidiObserverForm = nullptr; // Constructor. qtractorMidiControlObserverForm::qtractorMidiControlObserverForm ( QWidget *pParent, Qt::WindowFlags wflags ) : QDialog(pParent, wflags) { // Setup UI struct... m_ui.setupUi(this); // Make it auto-modeless dialog... QDialog::setAttribute(Qt::WA_DeleteOnClose); m_pControlTypeGroup = new qtractorMidiControlTypeGroup(nullptr, m_ui.ControlTypeComboBox, m_ui.ParamComboBox, m_ui.ParamTextLabel); // Target object. m_pMidiObserver = nullptr; // Target widget. m_pMidiObserverWidget = nullptr; // Proxy object. m_pMidiObserverAction = nullptr; // Start clean. m_iDirtyCount = 0; m_iDirtySetup = 0; // Populate param list. // activateControlType(m_ui.ControlTypeComboBox->currentIndex()); // Try to fix window geometry. adjustSize(); // UI signal/slot connections... QObject::connect(m_pControlTypeGroup, SIGNAL(controlTypeChanged(int)), SLOT(change())); QObject::connect(m_pControlTypeGroup, SIGNAL(controlParamChanged(int)), SLOT(change())); QObject::connect(m_ui.ChannelSpinBox, SIGNAL(valueChanged(int)), SLOT(change())); QObject::connect(m_ui.LogarithmicCheckBox, SIGNAL(toggled(bool)), SLOT(change())); QObject::connect(m_ui.FeedbackCheckBox, SIGNAL(toggled(bool)), SLOT(change())); QObject::connect(m_ui.InvertCheckBox, SIGNAL(toggled(bool)), SLOT(change())); QObject::connect(m_ui.HookCheckBox, SIGNAL(toggled(bool)), SLOT(change())); QObject::connect(m_ui.LatchCheckBox, SIGNAL(toggled(bool)), SLOT(change())); QObject::connect(m_ui.InputsPushButton, SIGNAL(clicked()), SLOT(inputs())); QObject::connect(m_ui.OutputsPushButton, SIGNAL(clicked()), SLOT(outputs())); 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())); // Pseudo-singleton reference setup. g_pMidiObserverForm = this; } // Destructor. qtractorMidiControlObserverForm::~qtractorMidiControlObserverForm (void) { delete m_pControlTypeGroup; } // Pseudo-singleton instance. qtractorMidiControlObserverForm * qtractorMidiControlObserverForm::getInstance (void) { return g_pMidiObserverForm; } // Pseudo-constructors. void qtractorMidiControlObserverForm::showInstance ( qtractorMidiControlObserver *pMidiObserver, QWidget *pMidiObserverWidget, QWidget *pParent, Qt::WindowFlags wflags ) { qtractorMidiControlObserverForm *pMidiObserverForm = qtractorMidiControlObserverForm::getInstance(); if (pMidiObserverForm) pMidiObserverForm->close(); if (pMidiObserver == nullptr) return; if (pMidiObserver->subject() == nullptr) return; pMidiObserverForm = new qtractorMidiControlObserverForm(pParent, wflags); pMidiObserverForm->setMidiObserver(pMidiObserver); pMidiObserverForm->setMidiObserverWidget(pMidiObserverWidget); pMidiObserverForm->show(); } void qtractorMidiControlObserverForm::showInstance ( QAction *pMidiObserverAction, QWidget *pParent, Qt::WindowFlags wflags ) { qtractorMidiControlObserverForm *pMidiObserverForm = qtractorMidiControlObserverForm::getInstance(); if (pMidiObserverForm) pMidiObserverForm->close(); pMidiObserverForm = new qtractorMidiControlObserverForm(pParent, wflags); pMidiObserverForm->setMidiObserverAction(pMidiObserverAction); pMidiObserverForm->show(); } // Observer accessors. void qtractorMidiControlObserverForm::setMidiObserver ( qtractorMidiControlObserver *pMidiObserver ) { ++m_iDirtySetup; m_pMidiObserver = pMidiObserver; QDialog::setWindowTitle( m_pMidiObserver->subject()->name() + " - " + tr("MIDI Controller")); m_pControlTypeGroup->setControlType(m_pMidiObserver->type()); m_pControlTypeGroup->setControlParam(m_pMidiObserver->param()); m_ui.ChannelSpinBox->setValue(m_pMidiObserver->channel() + 1); const bool bDecimal = m_pMidiObserver->isDecimal(); const bool bToggled = m_pMidiObserver->isToggled(); m_ui.LogarithmicCheckBox->setChecked(m_pMidiObserver->isLogarithmic()); m_ui.LogarithmicCheckBox->setEnabled(bDecimal); m_ui.FeedbackCheckBox->setChecked(m_pMidiObserver->isFeedback()); m_ui.FeedbackCheckBox->setEnabled(true); m_ui.InvertCheckBox->setChecked(m_pMidiObserver->isInvert()); m_ui.InvertCheckBox->setEnabled(true); m_ui.HookCheckBox->setChecked(m_pMidiObserver->isHook()); m_ui.HookCheckBox->setEnabled(bDecimal); m_ui.LatchCheckBox->setChecked(m_pMidiObserver->isLatch()); m_ui.LatchCheckBox->setEnabled(bToggled); qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); QPushButton *pResetButton = m_ui.DialogButtonBox->button(QDialogButtonBox::Reset); if (pResetButton && pMidiControl) pResetButton->setEnabled( pMidiControl->isMidiObserverMapped(m_pMidiObserver)); --m_iDirtySetup; m_iDirtyCount = 0; stabilizeForm(); } qtractorMidiControlObserver *qtractorMidiControlObserverForm::midiObserver (void) const { return m_pMidiObserver; } void qtractorMidiControlObserverForm::setMidiObserverWidget ( QWidget *pMidiObserverWidget ) { m_pMidiObserverWidget = pMidiObserverWidget; } QWidget *qtractorMidiControlObserverForm::midiObserverWidget (void) const { return m_pMidiObserverWidget; } // Action (control) observer accessors. void qtractorMidiControlObserverForm::setMidiObserverAction ( QAction *pMidiObserverAction ) { qtractorActionControl *pActionControl = qtractorActionControl::getInstance(); if (pActionControl == nullptr) return; qtractorActionControl::MidiObserver *pMidiObserver = pActionControl->addMidiObserver(pMidiObserverAction); if (pMidiObserver) { m_pMidiObserverAction = pMidiObserverAction; setMidiObserver(pMidiObserver); } } QAction *qtractorMidiControlObserverForm::midiObserverAction (void) const { return m_pMidiObserverAction; } // Pseudo-destructor. void qtractorMidiControlObserverForm::cleanup (void) { // Cleanup. m_pMidiObserverAction = nullptr; m_pMidiObserverWidget = nullptr; m_pMidiObserver = nullptr; // Aint't dirty no more... m_iDirtyCount = 0; // Pseudo-singleton reference cleanup. g_pMidiObserverForm = nullptr; } void qtractorMidiControlObserverForm::closeEvent ( QCloseEvent *pCloseEvent ) { cleanup(); // Sure acceptance and probable destruction (cf. WA_DeleteOnClose). QDialog::closeEvent(pCloseEvent); } // Process incoming controller event. void qtractorMidiControlObserverForm::processEvent ( const qtractorCtlEvent& ctle ) { m_pControlTypeGroup->setControlType(ctle.type()); m_pControlTypeGroup->setControlParam(ctle.param()); m_ui.ChannelSpinBox->setValue(ctle.channel() + 1); } // Change settings (anything else slot). void qtractorMidiControlObserverForm::change (void) { if (m_iDirtySetup > 0) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiControlObserverForm::change()"); #endif ++m_iDirtyCount; stabilizeForm(); } // Reset settings (action button slot). void qtractorMidiControlObserverForm::click ( QAbstractButton *pButton ) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiControlObserverForm::click(%p)", pButton); #endif QDialogButtonBox::ButtonRole role = m_ui.DialogButtonBox->buttonRole(pButton); if ((role & QDialogButtonBox::ResetRole) == QDialogButtonBox::ResetRole) reset(); } // Accept settings (OK button slot). void qtractorMidiControlObserverForm::accept (void) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl == nullptr) return; #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiControlObserverForm::accept()"); #endif // Get map settings... const qtractorMidiControl::ControlType ctype = m_pControlTypeGroup->controlType(); unsigned short iChannel = m_ui.ChannelSpinBox->value(); if (iChannel > 0) --iChannel; unsigned short iParam = 0; if (m_ui.ParamComboBox->isEnabled()) iParam = m_pControlTypeGroup->controlParam(); bool bLogarithmic = m_pMidiObserver->isLogarithmic(); if (m_ui.LogarithmicCheckBox->isEnabled()) bLogarithmic = m_ui.LogarithmicCheckBox->isChecked(); bool bFeedback = m_pMidiObserver->isFeedback(); if (m_ui.FeedbackCheckBox->isEnabled()) bFeedback = m_ui.FeedbackCheckBox->isChecked(); bool bInvert = m_pMidiObserver->isInvert(); if (m_ui.InvertCheckBox->isEnabled()) bInvert = m_ui.InvertCheckBox->isChecked(); bool bHook = m_pMidiObserver->isHook(); if (m_ui.HookCheckBox->isEnabled()) bHook = m_ui.HookCheckBox->isChecked(); bool bLatch = m_pMidiObserver->isLatch(); if (m_ui.LatchCheckBox->isEnabled()) bLatch = m_ui.LatchCheckBox->isChecked(); // Check whether already mapped... qtractorMidiControlObserver *pMidiObserver = pMidiControl->findMidiObserver(ctype, iChannel, iParam); if (pMidiObserver && pMidiObserver != m_pMidiObserver) { if (QMessageBox::warning(this, QDialog::windowTitle(), tr("MIDI controller is already assigned.\n\n" "Do you want to replace the mapping?"), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) return; } // Map the damn control.... pMidiControl->unmapMidiObserver(m_pMidiObserver, true); m_pMidiObserver->setType(ctype); m_pMidiObserver->setChannel(iChannel); m_pMidiObserver->setParam(iParam); m_pMidiObserver->setLogarithmic(bLogarithmic); m_pMidiObserver->setFeedback(bFeedback); m_pMidiObserver->setInvert(bInvert); m_pMidiObserver->setHook(bHook); m_pMidiObserver->setLatch(bLatch); if (m_pMidiObserverAction) { pMidiControl->mapMidiObserver(m_pMidiObserver); } else { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pSession->execute( new qtractorMidiControlObserverMapCommand( m_pMidiObserver, m_pMidiObserverWidget)); } cleanup(); // Just go with dialog acceptance... QDialog::accept(); } // Reject settings (Cancel button slot). void qtractorMidiControlObserverForm::reject (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiControlObserverForm::reject()"); #endif // Check if there's any pending changes... if (m_iDirtyCount > 0) { switch (QMessageBox::warning(this, QDialog::windowTitle(), tr("Some settings have been changed.\n\n" "Do you want to apply the changes?"), QMessageBox::Apply | QMessageBox::Discard | QMessageBox::Cancel)) { case QMessageBox::Discard: break; case QMessageBox::Apply: accept(); default: return; } } // Remove any (QAction) MIDI observer mapping... if (m_pMidiObserverAction) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl && !pMidiControl->isMidiObserverMapped(m_pMidiObserver)) { qtractorActionControl *pActionControl = qtractorActionControl::getInstance(); if (pActionControl) pActionControl->removeMidiObserver(m_pMidiObserverAction); } } cleanup(); // Just go with dialog rejection... QDialog::reject(); } // Reset settings (Reset button slot). void qtractorMidiControlObserverForm::reset (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiControlObserverForm::reset()"); #endif if (m_pMidiObserverAction) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); qtractorActionControl *pActionControl = qtractorActionControl::getInstance(); if (pMidiControl && pActionControl) { pMidiControl->unmapMidiObserver(m_pMidiObserver, true); pActionControl->removeMidiObserver(m_pMidiObserverAction); } } else { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pSession->execute( new qtractorMidiControlObserverUnmapCommand( m_pMidiObserver, m_pMidiObserverWidget)); } cleanup(); // Bail out... QDialog::accept(); } // Show control inputs (Custom button slot). void qtractorMidiControlObserverForm::inputs (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiControlObserverForm::inputs()"); #endif qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm && pMainForm->connections() && pMidiEngine->controlBus_in()) { (pMainForm->connections())->showBus( pMidiEngine->controlBus_in(), qtractorBus::Input); } } // Show control outputs (Custom button slot). void qtractorMidiControlObserverForm::outputs (void) { #ifdef CONFIG_DEBUG_0 qDebug("qtractorMidiControlObserverForm::outputs()"); #endif qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMidiEngine *pMidiEngine = pSession->midiEngine(); if (pMidiEngine == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm && pMainForm->connections() && pMidiEngine->controlBus_out()) { (pMainForm->connections())->showBus( pMidiEngine->controlBus_out(), qtractorBus::Output); } } // Update control widget state. void qtractorMidiControlObserverForm::stabilizeForm (void) { qtractorMidiEngine *pMidiEngine = nullptr; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pMidiEngine = pSession->midiEngine(); if (pMidiEngine) { const bool bFeedback = m_ui.FeedbackCheckBox->isChecked(); m_ui.InputsPushButton->setEnabled( pMidiEngine->controlBus_in() != nullptr); m_ui.OutputsPushButton->setEnabled( bFeedback && pMidiEngine->controlBus_out() != nullptr); } else { m_ui.InputsPushButton->setEnabled(false); m_ui.OutputsPushButton->setEnabled(false); } } // MIDI controller/observer attachment (context menu) activator. // Q_DECLARE_METATYPE(qtractorMidiControlObserver *); QAction *qtractorMidiControlObserverForm::addMidiControlAction ( QWidget *pParent, QWidget *pWidget, qtractorMidiControlObserver *pMidiObserver ) { qtractorMidiControl *pMidiControl = qtractorMidiControl::getInstance(); if (pMidiControl) pMidiControl->mapMidiObserverWidget(pMidiObserver, pWidget); QAction *pAction = new QAction( QIcon::fromTheme("itemControllers"), tr("&MIDI Controller..."), pWidget); pAction->setData(QVariant::fromValue(pMidiObserver)); QObject::connect( pAction, SIGNAL(triggered(bool)), pParent, SLOT(midiControlActionSlot())); pWidget->addAction(pAction); pWidget->setContextMenuPolicy(Qt::CustomContextMenu); QObject::connect( pWidget, SIGNAL(customContextMenuRequested(const QPoint&)), pParent, SLOT(midiControlMenuSlot(const QPoint&))); return pAction; } void qtractorMidiControlObserverForm::midiControlAction ( QWidget *pParent, QAction *pAction ) { if (pAction == nullptr) return; qtractorMidiControlObserver *pMidiObserver = pAction->data().value (); if (pMidiObserver) { QWidget *pWidget = qobject_cast (pAction->parent()); qtractorMidiControlObserverForm::showInstance( pMidiObserver, pWidget, pParent); } } void qtractorMidiControlObserverForm::midiControlMenu ( QWidget *pWidget, const QPoint& pos ) { if (pWidget == nullptr) return; QAction *pMidiControlAction = nullptr; qtractorMidiControlObserver *pMidiObserver = nullptr; QListIterator iter(pWidget->actions()); while (iter.hasNext()) { QAction *pAction = iter.next(); pMidiObserver = pAction->data().value (); if (pMidiObserver) { pMidiControlAction = pAction; break; } } if (pMidiControlAction == nullptr) return; if (pMidiObserver == nullptr) return; QMenu menu(pWidget); menu.addAction(pMidiControlAction); qtractorMidiControlObserverForm::addMidiControlMenu(&menu, pMidiObserver); menu.exec(pWidget->mapToGlobal(pos)); } // Add esquisite automation menu actions... void qtractorMidiControlObserverForm::addMidiControlMenu ( QMenu *pMenu, qtractorMidiControlObserver *pMidiObserver ) { qtractorCurveList *pCurveList = pMidiObserver->curveList(); if (pCurveList == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTrack *pTrack = pSession->findTrackCurveList(pCurveList); if (pTrack == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks == nullptr) return; qtractorCurve *pCurve = nullptr; qtractorSubject *pSubject = pMidiObserver->subject(); if (pSubject) pCurve = pSubject->curve(); if (pCurve && pCurveList->currentCurve() != pCurve) { pCurveList->setCurrentCurve(pCurve); pTracks->updateTrackView(); } pTracks->trackList()->setCurrentTrack(pTrack); QAction *pAction; pMenu->addSeparator(); pAction = pMenu->addAction(tr("&Automation")); pAction->setCheckable(true); pAction->setChecked(pCurve && pCurve == pCurveList->currentCurve()); pAction->setData(QVariant::fromValue(pMidiObserver)); QObject::connect( pAction, SIGNAL(triggered(bool)), pMainForm, SLOT(trackCurveSelect(bool))); pMenu->addSeparator(); QMenu *pTrackCurveModeMenu = pMainForm->trackCurveModeMenu(); pTrackCurveModeMenu->setEnabled(pCurve != nullptr); pMenu->addMenu(pTrackCurveModeMenu); pAction = pMenu->addAction(tr("&Lock")); pAction->setCheckable(true); pAction->setChecked(pCurve && pCurve->isLocked()); pAction->setEnabled(pCurve != nullptr); QObject::connect( pAction, SIGNAL(triggered(bool)), pMainForm, SLOT(trackCurveLocked(bool))); pMenu->addSeparator(); QIcon iconProcess; iconProcess.addPixmap( QIcon::fromTheme("trackCurveProcess").pixmap(16, 16), QIcon::Normal, QIcon::On); iconProcess.addPixmap( QIcon::fromTheme("trackCurveEnabled").pixmap(16, 16), QIcon::Normal, QIcon::Off); iconProcess.addPixmap( QIcon::fromTheme("trackCurveNone").pixmap(16, 16), QIcon::Disabled, QIcon::Off); pAction = pMenu->addAction(iconProcess, tr("&Play")); pAction->setCheckable(true); pAction->setChecked(pCurve && pCurve->isProcess()); pAction->setEnabled(pCurve != nullptr); QObject::connect( pAction, SIGNAL(triggered(bool)), pMainForm, SLOT(trackCurveProcess(bool))); QIcon iconCapture; iconCapture.addPixmap( QIcon::fromTheme("trackCurveCapture").pixmap(16, 16), QIcon::Normal, QIcon::On); iconCapture.addPixmap( QIcon::fromTheme("trackCurveEnabled").pixmap(16, 16), QIcon::Normal, QIcon::Off); iconProcess.addPixmap( QIcon::fromTheme("trackCurveNone").pixmap(16, 16), QIcon::Disabled, QIcon::Off); pAction = pMenu->addAction(iconCapture, tr("&Record")); pAction->setCheckable(true); pAction->setChecked(pCurve && pCurve->isCapture()); pAction->setEnabled(pCurve != nullptr); QObject::connect( pAction, SIGNAL(triggered(bool)), pMainForm, SLOT(trackCurveCapture(bool))); pMenu->addSeparator(); pAction = pMenu->addAction(tr("&Clear")); pAction->setEnabled(pCurve != nullptr); QObject::connect( pAction, SIGNAL(triggered(bool)), pMainForm, SLOT(trackCurveClear())); } // end of qtractorMidiControlObserverForm.cpp qtractor-1.5.9/src/PaxHeaders/qtractorShortcutForm.h0000644000000000000000000000013215101070305017612 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.088267658 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorShortcutForm.h0000644000175000001440000001241215101070305017602 0ustar00rncbcusers// qtractorShortcutForm.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 __qtractorShortcutForm_h #define __qtractorShortcutForm_h #include "ui_qtractorShortcutForm.h" #include "qtractorActionControl.h" #include #include #include // Forward decls. class QToolButton; //------------------------------------------------------------------------- // qtractorShortcutTableItemEdit class qtractorShortcutTableItemEdit : public QLineEdit { Q_OBJECT public: // Constructor. qtractorShortcutTableItemEdit(QWidget *pParent = nullptr) : QLineEdit(pParent) {} signals: // Custom cancel signal. void editingCanceled(); protected: // Shortcut key to text event translation. void keyPressEvent(QKeyEvent *pKeyEvent); }; //------------------------------------------------------------------------- // qtractorShortcutTableItemEditor class qtractorShortcutTableItemEditor : public QWidget { Q_OBJECT public: // Constructor. qtractorShortcutTableItemEditor(QWidget *pParent = nullptr); // Shortcut text accessors. void setText(const QString& sText); QString text() const; // Index model row accessors. void setIndex(const QModelIndex& index) { m_index = index; } const QModelIndex& index() const { return m_index; } // Default (initial) shortcut text accessors. void setDefaultText(const QString& sDefaultText); const QString& defaultText() const; signals: void editingFinished(); void editingCanceled(); public slots: void clear(); protected slots: void finish(); void cancel(); void changed(const QString&); private: // Instance variables. qtractorShortcutTableItemEdit *m_pItemEdit; QToolButton *m_pToolButton; QString m_sDefaultText; QModelIndex m_index; }; //------------------------------------------------------------------------- // qtractorShortcutTableItemDelegate class qtractorShortcutForm; class qtractorShortcutTableItemDelegate : public QItemDelegate { Q_OBJECT public: // Constructor. qtractorShortcutTableItemDelegate(qtractorShortcutForm *pShortcutForm); protected: void paint(QPainter *pPainter, 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; QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index) const; protected slots: void commitEditor(); private: qtractorShortcutForm *m_pShortcutForm; }; //------------------------------------------------------------------------- // qtractorShortcutForm class qtractorShortcutForm : public QDialog { Q_OBJECT public: // Constructor. qtractorShortcutForm(const QList& actions, QWidget *pParent = nullptr); // Destructor. ~qtractorShortcutForm(); // Action shortcut/control table widget accessor. QTreeWidget *tableWidget() const; // MIDI Controller manager accessor. void setActionControl(qtractorActionControl *pActionControl); qtractorActionControl *actionControl() const; // Action shortcut/control dirty-flag accessors. bool isDirtyActionShortcuts() const; bool isDirtyActionControls() const; // Shortcut action finder & settler. bool commitEditor(qtractorShortcutTableItemEditor *pItemEditor); protected slots: void actionShortcutActivated(QTreeWidgetItem *, int); void actionShortcutChanged(QTreeWidgetItem *, int); void actionControlMenuRequested(const QPoint&); void actionControlActivated(); void actionControlAccepted(); void actionControlRejected(); void refresh(); void accept(); void reject(); protected: QString actionControlText(QAction *pAction) const; void stabilizeForm(); private: // The Qt-designer UI struct... Ui::qtractorShortcutForm m_ui; qtractorActionControl *m_pActionControl; QList m_actions; QHash m_shortcuts; QHash m_item_actions; QHash m_action_items; QHash m_dirty_shortcuts; QHash m_dirty_controls; typedef qtractorActionControl::MidiObserver MidiObserver; QHash m_dirty_observers; QTreeWidgetItem *m_pActionControlItem; }; #endif // __qtractorShortcutForm_h // end of qtractorShortcutForm.h qtractor-1.5.9/src/PaxHeaders/qtractorTimeScaleForm.cpp0000644000000000000000000000013215101070305020200 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorTimeScaleForm.cpp0000644000175000001440000007545215101070305020205 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.9/src/PaxHeaders/qtractorActionControl.cpp0000644000000000000000000000013215101070305020264 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.062554172 30 ctime=1761898693.062554172 qtractor-1.5.9/src/qtractorActionControl.cpp0000644000175000001440000001254315101070305020261 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.9/src/PaxHeaders/qtractorAudioEngine.cpp0000644000000000000000000000013215101070305017675 xustar0030 mtime=1761898693.063267578 30 atime=1761898693.062554172 30 ctime=1761898693.063267578 qtractor-1.5.9/src/qtractorAudioEngine.cpp0000644000175000001440000027723115101070305017701 0ustar00rncbcusers// qtractorAudioEngine.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 "qtractorAudioEngine.h" #include "qtractorAudioMonitor.h" #include "qtractorAudioBuffer.h" #include "qtractorSession.h" #include "qtractorDocument.h" #include "qtractorMonitor.h" #include "qtractorSessionCursor.h" #include "qtractorMidiEngine.h" #include "qtractorMidiManager.h" #include "qtractorPlugin.h" #include "qtractorMainForm.h" #ifdef CONFIG_VST3 #include "qtractorVst3Plugin.h" #endif #ifdef CONFIG_CLAP #include "qtractorClapPlugin.h" #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_TIME #include "qtractorLv2Plugin.h" #endif #endif #include "qtractorInsertPlugin.h" #ifdef CONFIG_JACK_SESSION #include #endif #ifdef CONFIG_JACK_METADATA #include #endif #include #include #include // Sensible defaults. #define SAMPLE_RATE 44100 #define BUFFER_SIZE 1024 #define BLOCK_SIZE 64 #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 mix-down processor version. static inline void sse_buffer_add ( float **ppBuffer, float **ppFrames, unsigned int iFrames, unsigned short iBuffers, unsigned short iChannels, unsigned int iOffset ) { unsigned short j = 0; for (unsigned short i = 0; i < iChannels; ++i) { float *pBuffer = ppBuffer[j] + iOffset; float *pFrames = ppFrames[i] + iOffset; unsigned int nframes = iFrames; for (; (long(pBuffer) & 15) && (nframes > 0); --nframes) *pBuffer++ += *pFrames++; for (; nframes >= 4; nframes -= 4) { _mm_store_ps(pBuffer, _mm_add_ps( _mm_loadu_ps(pBuffer), _mm_loadu_ps(pFrames))); pFrames += 4; pBuffer += 4; } for (; nframes > 0; --nframes) *pBuffer++ += *pFrames++; if (++j >= iBuffers) j = 0; } } #endif // __SSE__ #if defined(__ARM_NEON__) #include "arm_neon.h" // NEON enabled mix-down processor version. static inline void neon_buffer_add ( float **ppBuffer, float **ppFrames, unsigned int iFrames, unsigned short iBuffers, unsigned short iChannels, unsigned int iOffset ) { unsigned short j = 0; for (unsigned short i = 0; i < iChannels; ++i) { float *pBuffer = ppBuffer[j] + iOffset; float *pFrames = ppFrames[i] + iOffset; unsigned int nframes = iFrames; for (; (long(pBuffer) & 15) && (nframes > 0); --nframes) *pBuffer++ += *pFrames++; for (; nframes >= 4; nframes -= 4) { vst1q_f32(pBuffer, vaddq_f32( vld1q_f32(pBuffer), vld1q_f32(pFrames))); pFrames += 4; pBuffer += 4; } for (; nframes > 0; --nframes) *pBuffer++ += *pFrames++; if (++j >= iBuffers) j = 0; } } #endif // __ARM_NEON__ // Standard mix-down processor version. static inline void std_buffer_add ( float **ppBuffer, float **ppFrames, unsigned int iFrames, unsigned short iBuffers, unsigned short iChannels, unsigned int iOffset ) { unsigned short j = 0; for (unsigned short i = 0; i < iChannels; ++i) { float *pBuffer = ppBuffer[j] + iOffset; float *pFrames = ppFrames[i] + iOffset; for (unsigned int n = 0; n < iFrames; ++n) *pBuffer++ += *pFrames++; if (++j >= iBuffers) j = 0; } } //---------------------------------------------------------------------- // qtractorAudioExportBuffer -- name tells all: audio export buffer. // class qtractorAudioExportBuffer { public: // Constructor qtractorAudioExportBuffer( unsigned short iChannels, unsigned int iBufferSize ) { m_iChannels = iChannels; m_iBufferSize = iBufferSize; m_ppBuffer = new float * [m_iChannels]; for (unsigned short i = 0; i < m_iChannels; ++i) m_ppBuffer[i] = new float [iBufferSize]; #if defined(__SSE__) if (sse_enabled()) m_pfnBufferAdd = sse_buffer_add; else #endif #if defined(__ARM_NEON__) m_pfnBufferAdd = neon_buffer_add; if (false) #endif m_pfnBufferAdd = std_buffer_add; } // Destructor. ~qtractorAudioExportBuffer() { for (unsigned short i = 0; i < m_iChannels; ++i) delete [] m_ppBuffer[i]; delete [] m_ppBuffer; } // Mix-down buffer accessors. unsigned short channels() const { return m_iChannels; } unsigned int bufferSize() const { return m_iBufferSize; } float **buffer() const { return m_ppBuffer; } // Prepare mix-down buffer. void process_prepare(unsigned int nframes) { for (unsigned short i = 0; i < m_iChannels; ++i) ::memset(m_ppBuffer[i], 0, nframes * sizeof(float)); } // Incremental mix-down buffer. void process_add (qtractorAudioBus *pAudioBus, unsigned int nframes, unsigned int offset = 0) { (*m_pfnBufferAdd)(m_ppBuffer, pAudioBus->out(), nframes, m_iChannels, pAudioBus->channels(), offset); } private: unsigned short m_iChannels; unsigned int m_iBufferSize; // Mix-down buffer. float **m_ppBuffer; // Mix-down buffer processor. void (*m_pfnBufferAdd)(float **, float **, unsigned int, unsigned short, unsigned short, unsigned int); }; //---------------------------------------------------------------------- // qtractorAudioEngine_process -- JACK client process callback. // static int qtractorAudioEngine_process ( jack_nframes_t nframes, void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); return pAudioEngine->process(nframes); } //---------------------------------------------------------------------- // qtractorAudioEngine_timebase -- JACK timebase master callback. // static void qtractorAudioEngine_timebase ( jack_transport_state_t, jack_nframes_t, jack_position_t *pPos, int iNewPos, void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); pAudioEngine->timebase(pPos, iNewPos); } //---------------------------------------------------------------------- // qtractorAudioEngine_shutdown -- JACK client shutdown callback. // static void qtractorAudioEngine_shutdown ( void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); pAudioEngine->notifyShutEvent(); } //---------------------------------------------------------------------- // qtractorAudioEngine_xrun -- JACK client XRUN callback. // static int qtractorAudioEngine_xrun ( void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); pAudioEngine->notifyXrunEvent(); return 0; } //---------------------------------------------------------------------- // qtractorAudioEngine_graph_order -- JACK graph change callback. // static int qtractorAudioEngine_graph_order ( void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); pAudioEngine->notifyPortEvent(); return 0; } //---------------------------------------------------------------------- // qtractorAudioEngine_client_registration -- JACK client reg. callback. // static void qtractorAudioEngine_client_registration ( const char *, int, void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); pAudioEngine->notifyPortEvent(); } //---------------------------------------------------------------------- // qtractorAudioEngine_port_registration -- JACK port reg. callback. // static void qtractorAudioEngine_port_registration ( jack_port_id_t, int, void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); pAudioEngine->notifyPortEvent(); } //---------------------------------------------------------------------- // qtractorAudioEngine_port_connect -- JACK port conn. callback. // static void qtractorAudioEngine_port_connect ( jack_port_id_t port_id1, jack_port_id_t port_id2, int, void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); jack_client_t *pJackClient = pAudioEngine->jackClient(); if (pJackClient) { jack_port_t *port1 = ::jack_port_by_id(pJackClient, port_id1); jack_port_t *port2 = ::jack_port_by_id(pJackClient, port_id2); if (::jack_port_is_mine(pJackClient, port1) && ::jack_port_is_mine(pJackClient, port2)) { pAudioEngine->notifySelfEvent(); } } pAudioEngine->notifyPortEvent(); } #ifdef CONFIG_JACK_PORT_RENAME //---------------------------------------------------------------------- // qtractorAudioEngine_port_rename -- JACK port rename callback. // static void qtractorAudioEngine_port_rename ( jack_port_id_t, const char *, const char *, void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); pAudioEngine->notifyPortEvent(); } #endif //---------------------------------------------------------------------- // qtractorAudioEngine_buffer_size -- JACK buffer-size change callback. // static int qtractorAudioEngine_buffer_size ( jack_nframes_t nframes, void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); pAudioEngine->notifyBuffEvent(nframes); return 0; } //---------------------------------------------------------------------- // qtractorAudioEngine_freewheel -- Audio export process callback. // static void qtractorAudioEngine_freewheel ( int iStarting, void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); pAudioEngine->setFreewheel(bool(iStarting)); } #ifdef CONFIG_JACK_SESSION //---------------------------------------------------------------------- // qtractorAudioEngine_session_event -- JACK session event callabck // static void qtractorAudioEngine_session_event ( jack_session_event_t *pSessionEvent, void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); pAudioEngine->notifySessEvent(pSessionEvent); } #endif //---------------------------------------------------------------------- // qtractorAudioEngine_sync -- JACK transport sync event callabck // static int qtractorAudioEngine_sync ( jack_transport_state_t state, jack_position_t *pos, void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); if (pAudioEngine->isFreewheel()) return 0; if (pAudioEngine->countIn() > 0) return 0; if (pAudioEngine->session()->midiEngine()->countIn() > 0) return 0; const bool bPlaying = pAudioEngine->isPlaying(); if ((state == JackTransportStopped && bPlaying) || (state == JackTransportStarting && !bPlaying) || (state == JackTransportRolling && !bPlaying)) { pAudioEngine->notifySyncEvent(pos->frame, !bPlaying); return 1; } const long iDeltaFrames = long(pos->frame) - long(pAudioEngine->sessionCursor()->frame()); if (state == JackTransportStarting && iDeltaFrames < 0) pAudioEngine->setTransportLatency(- iDeltaFrames); const long iBufferSize = long(pAudioEngine->bufferSize()); if (qAbs(iDeltaFrames) > iBufferSize) { unsigned long iPlayHead = pos->frame; if (bPlaying) iPlayHead += iBufferSize; pAudioEngine->notifySyncEvent(iPlayHead, bPlaying); } return 1; } #ifdef CONFIG_JACK_METADATA //---------------------------------------------------------------------- // qtractorAudioEngine_property_change -- JACK property change callabck // static void qtractorAudioEngine_property_change ( jack_uuid_t, const char *key, jack_property_change_t, void *pvArg ) { qtractorAudioEngine *pAudioEngine = static_cast (pvArg); // PRETTY_NAME is the only metadata we are currently interested in... if (key && (::strcmp(key, JACK_METADATA_PRETTY_NAME) == 0)) pAudioEngine->notifyPropEvent(); } #endif //---------------------------------------------------------------------- // class qtractorAudioEngine -- JACK client instance (singleton). // // Constructor. qtractorAudioEngine::qtractorAudioEngine ( qtractorSession *pSession ) : qtractorEngine(pSession, qtractorTrack::Audio) { m_pJackClient = nullptr; m_iSampleRate = SAMPLE_RATE; // A sensible default, always. m_iBufferSize = 0; m_iBufferSizeEx = BUFFER_SIZE; // Another sensible default, sometimes. m_iBufferOffset = 0; m_iBlockSize = BLOCK_SIZE; // Yet another sensible default, always. m_bMasterAutoConnect = true; // Audio-export freewheeling (internal) state. m_bFreewheel = false; // Common audio buffer sync thread. m_pSyncThread = nullptr; // Audio-export (in)active state. m_bExporting = false; m_pExportFile = nullptr; m_pExportBuses = nullptr; m_pExportBuffer = nullptr; m_iExportOffset = 0; m_iExportStart = 0; m_iExportEnd = 0; m_bExportDone = true; // Audio metronome stuff. m_bMetronome = false; m_bMetroBus = false; m_pMetroBus = nullptr; m_bMetroAutoConnect = true; m_pMetroBarBuff = nullptr; m_fMetroBarGain = 1.0f; m_pMetroBeatBuff = nullptr; m_fMetroBeatGain = 1.0f; m_iMetroOffset = 0; m_iMetroBeatStart = 0; m_iMetroBeat = 0; m_bMetroEnabled = false; // Audio metronome count-in stuff. m_bCountIn = false; m_countInMode = CountInNone; m_iCountInBeats = 0; m_iCountIn = 0; m_iCountInBeatStart = 0; m_iCountInBeat = 0; m_iCountInFrame = 0; m_iCountInFrameEnd = 0; // Audition/pre-listening player stuff. ATOMIC_SET(&m_playerLock, 0); m_bPlayerOpen = false; m_bPlayerBus = false; m_bPlayerAutoConnect = true; m_pPlayerBus = nullptr; m_pPlayerBuff = nullptr; m_iPlayerFrame = 0; // JACK transport mode. m_transportMode = qtractorBus::Duplex; // JACK transport latency. m_iTransportLatency = 0; // JACK timebase mode control. m_bTimebase = true; m_iTimebase = 0; // Time(base)/BBT time info. ::memset(&m_timeInfo, 0, sizeof(TimeInfo)); } // Special event notifier proxy object. const qtractorAudioEngineProxy *qtractorAudioEngine::proxy (void) const { return &m_proxy; } // Event notifications. void qtractorAudioEngine::notifyShutEvent (void) { m_proxy.notifyShutEvent(); } void qtractorAudioEngine::notifyXrunEvent (void) { m_proxy.notifyXrunEvent(); } void qtractorAudioEngine::notifyPortEvent (void) { m_proxy.notifyPortEvent(); } void qtractorAudioEngine::notifyBuffEvent ( unsigned int iBufferSize ) { if (m_iBufferSizeEx < iBufferSize) { m_proxy.notifyBuffEvent(iBufferSize); } else { m_iBufferSize = iBufferSize; if (m_iBlockSize > m_iBufferSize) m_iBlockSize = m_iBufferSize; } } void qtractorAudioEngine::notifySessEvent ( void *pvSessionArg ) { m_proxy.notifySessEvent(pvSessionArg); } void qtractorAudioEngine::notifySyncEvent ( unsigned long iPlayHead, bool bPlaying ) { m_iTimebase = 0; // HACK: Force reset timebase counter... m_proxy.notifySyncEvent(iPlayHead, bPlaying); } void qtractorAudioEngine::notifyPropEvent (void) { m_proxy.notifyPropEvent(); } void qtractorAudioEngine::notifySelfEvent (void) { m_proxy.notifySelfEvent(); } // JACK client descriptor accessor. jack_client_t *qtractorAudioEngine::jackClient (void) const { return m_pJackClient; } // Internal sample-rate accessor. unsigned int qtractorAudioEngine::sampleRate (void) const { return m_iSampleRate; } // Buffer size accessors. unsigned int qtractorAudioEngine::bufferSize (void) const { return m_iBufferSize; } unsigned int qtractorAudioEngine::bufferSizeEx (void) const { return m_iBufferSizeEx; } // Buffer offset accessor. unsigned int qtractorAudioEngine::bufferOffset (void) const { return m_iBufferOffset; } // Block-stride size (in frames) accessor. unsigned int qtractorAudioEngine::blockSize (void) const { return m_iBlockSize; } // Audio (Master) bus defaults accessors. void qtractorAudioEngine::setMasterAutoConnect ( bool bMasterAutoConnect ) { m_bMasterAutoConnect = bMasterAutoConnect; } bool qtractorAudioEngine::isMasterAutoConnect (void) const { return m_bMasterAutoConnect; } // Device engine initialization method. bool qtractorAudioEngine::init (void) { // There must a session reference... qtractorSession *pSession = session(); if (pSession == nullptr) return false; // Try open a new client... const QByteArray aClientName = pSession->clientName().toUtf8(); int opts = JackNullOption; #ifdef CONFIG_XUNIQUE opts |= JackUseExactName; #endif #ifdef CONFIG_JACK_SESSION if (!m_sSessionId.isEmpty()) { opts |= JackSessionID; const QByteArray aSessionId = m_sSessionId.toLocal8Bit(); m_pJackClient = jack_client_open( aClientName.constData(), jack_options_t(opts), nullptr, aSessionId.constData()); // Reset JACK session UUID. m_sSessionId.clear(); } else #endif m_pJackClient = jack_client_open( aClientName.constData(), jack_options_t(opts), nullptr); if (m_pJackClient == nullptr) return false; // ATTN: First thing to remember is initial sample-rate and buffer size. m_iSampleRate = jack_get_sample_rate(m_pJackClient); m_iBufferSize = jack_get_buffer_size(m_pJackClient); // Block-stride size changes... m_iBlockSize = BLOCK_SIZE; if (m_iBlockSize > m_iBufferSize) m_iBlockSize = m_iBufferSize; // Guard for buffer size changes... const unsigned int iBufferSizeEx = (m_iBufferSize << 1); if (m_iBufferSizeEx < iBufferSizeEx) m_iBufferSizeEx = iBufferSizeEx; if (m_iBufferSizeEx < m_iBlockSize) m_iBufferSizeEx = m_iBlockSize; // ATTN: Second is setting proper session client name. pSession->setClientName( QString::fromUtf8(jack_get_client_name(m_pJackClient))); // ATTN: Third is setting session sample rate. pSession->setSampleRate(m_iSampleRate); // Our dedicated audio buffer thread... m_pSyncThread = new qtractorAudioBufferThread(); m_pSyncThread->start(QThread::HighPriority); return true; } #ifdef CONFIG_JACK_SESSION #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #endif // Device engine activation method. bool qtractorAudioEngine::activate (void) { // There must a session reference... qtractorSession *pSession = session(); if (pSession == nullptr) return false; // Let remaining buses get a life... openPlayerBus(); openMetroBus(); // MIDI plugin managers output buses... qtractorMidiManager *pMidiManager = pSession->midiManagers().first(); while (pMidiManager) { qtractorAudioBus *pAudioBus = pMidiManager->audioOutputBus(); if (pAudioBus && pMidiManager->isAudioOutputBus()) pAudioBus->open(); pMidiManager = pMidiManager->next(); } // Ensure (not) freewheeling state... jack_set_freewheel(m_pJackClient, 0); // Set our main engine processor callbacks. jack_set_process_callback(m_pJackClient, qtractorAudioEngine_process, this); // Transport timebase callback... resetTimebase(); // And some other event callbacks... jack_set_xrun_callback(m_pJackClient, qtractorAudioEngine_xrun, this); jack_on_shutdown(m_pJackClient, qtractorAudioEngine_shutdown, this); jack_set_graph_order_callback(m_pJackClient, qtractorAudioEngine_graph_order, this); jack_set_client_registration_callback(m_pJackClient, qtractorAudioEngine_client_registration, this); jack_set_port_registration_callback(m_pJackClient, qtractorAudioEngine_port_registration, this); jack_set_port_connect_callback(m_pJackClient, qtractorAudioEngine_port_connect, this); #ifdef CONFIG_JACK_PORT_RENAME jack_set_port_rename_callback(m_pJackClient, qtractorAudioEngine_port_rename, this); #endif jack_set_buffer_size_callback(m_pJackClient, qtractorAudioEngine_buffer_size, this); // Set audio export processor callback. jack_set_freewheel_callback(m_pJackClient, qtractorAudioEngine_freewheel, this); #ifdef CONFIG_JACK_SESSION // Set JACK session event callback. jack_set_session_callback(m_pJackClient, qtractorAudioEngine_session_event, this); #endif // Set JACK transport sync callback. if (m_transportMode & qtractorBus::Input) { jack_set_sync_callback(m_pJackClient, qtractorAudioEngine_sync, this); } #ifdef CONFIG_JACK_METADATA // Set JACK property change callback. jack_set_property_change_callback(m_pJackClient, qtractorAudioEngine_property_change, this); #endif // Initialize time(base) information for sure... updateTimeInfo(0); // Reset all dependable monitoring... resetAllMonitors(); // Time to activate ourselves... jack_activate(m_pJackClient); // Now, do all auto-connection stuff (if applicable...) if (m_bPlayerBus && m_pPlayerBus) m_pPlayerBus->autoConnect(); if (m_bMetroBus && m_pMetroBus) m_pMetroBus->autoConnect(); for (qtractorBus *pBus = buses().first(); pBus; pBus = pBus->next()) { qtractorAudioBus *pAudioBus = static_cast(pBus); if (pAudioBus) pAudioBus->autoConnect(); } // MIDI plugin managers output buses... pMidiManager = session()->midiManagers().first(); while (pMidiManager) { qtractorAudioBus *pAudioBus = pMidiManager->audioOutputBus(); if (pAudioBus && pMidiManager->isAudioOutputBus()) pAudioBus->autoConnect(); pMidiManager = pMidiManager->next(); } // We're now ready and running... return true; } #ifdef CONFIG_JACK_SESSION #if defined(Q_CC_GNU) || defined(Q_CC_MINGW) #pragma GCC diagnostic pop #endif #endif // Device engine start method. bool qtractorAudioEngine::start (void) { if (!isActivated()) return false; // Reset all dependables... resetAllMonitors(); resetMetro(true); // Reset transport latency anyway... m_iTransportLatency = 0; // Start transport rolling... if (m_transportMode & qtractorBus::Output) jack_transport_start(m_pJackClient); // We're now ready and running... return true; } // Device engine stop method. void qtractorAudioEngine::stop (void) { if (!isActivated()) return; if (m_transportMode & qtractorBus::Output) { jack_transport_stop(m_pJackClient); transport_locate(sessionCursor()->frame()); } // MIDI plugin managers reset... qtractorMidiManager *pMidiManager = session()->midiManagers().first(); while (pMidiManager) { pMidiManager->reset(); pMidiManager = pMidiManager->next(); } } // Device engine deactivation method. void qtractorAudioEngine::deactivate (void) { // We're stopping now... // setPlaying(false); // Deactivate the JACK client first. if (m_pJackClient) jack_deactivate(m_pJackClient); } // Device engine cleanup method. void qtractorAudioEngine::clean (void) { // Audio master bus auto-connection option... if (!buses2().isEmpty()) { qtractorAudioBus *pMasterBus = static_cast (buses2().first()); if (pMasterBus) m_bMasterAutoConnect = pMasterBus->isAutoConnect(); } // Clean player/metronome buses... deletePlayerBus(); deleteMetroBus(); // Terminate common player/metro sync thread... 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; } // Audio-export stilll around? weird... if (m_pExportBuffer) { delete m_pExportBuffer; m_pExportBuffer = nullptr; } if (m_pExportBuses) { delete m_pExportBuses; m_pExportBuses = nullptr; } if (m_pExportFile) { delete m_pExportFile; m_pExportFile = nullptr; } // Close the JACK client, finally. if (m_pJackClient) { jack_client_close(m_pJackClient); m_pJackClient = nullptr; } // Null sample-rate/period. // m_iSampleRate = 0; // m_iBufferSize = 0; } // Whether we're in the audio/real-time thread... static thread_local bool g_bProcessing = false; bool qtractorAudioEngine::isProcessing (void) { return g_bProcessing; } // Process cycle executive. int qtractorAudioEngine::process ( unsigned int nframes ) { // Don't bother with a thing, if not running. if (!isActivated()) return 0; // Reset buffer offset. m_iBufferOffset = 0; // Are we actually freewheeling for export?... // notice that freewheeling has no RT requirements. if (m_bFreewheel) { process_export(nframes); return 0; } // Must have a valid session... qtractorSession *pSession = session(); if (pSession == nullptr) return 0; // Make sure we have an actual session cursor... qtractorSessionCursor *pAudioCursor = sessionCursor(); if (pAudioCursor == nullptr) return 0; // Session RT-safeness lock... if (!pSession->acquire()) return 0; // We're in the audio/real-time thread... g_bProcessing = true; // Track whether audio output buses // buses needs monitoring while idle... int iOutputBus = 0; qtractorBus *pBus; qtractorAudioBus *pAudioBus; // Prepare all current audio buses... for (pBus = buses().first(); pBus; pBus = pBus->next()) { pAudioBus = static_cast (pBus); if (pAudioBus) pAudioBus->process_prepare(nframes); } // Prepare all extra audio buses... for (pBus = busesEx().first(); pBus; pBus = pBus->next()) { pAudioBus = static_cast (pBus); if (pAudioBus) pAudioBus->process_prepare(nframes); } // Monitor all current audio buses... for (pBus = buses().first(); pBus; pBus = pBus->next()) { pAudioBus = static_cast (pBus); if (pAudioBus) pAudioBus->process_monitor(nframes); } // The owned buses too, if any... if (m_bMetroBus && m_pMetroBus) m_pMetroBus->process_prepare(nframes); if (m_bPlayerBus && m_pPlayerBus) m_pPlayerBus->process_prepare(nframes); // Process audition/pre-listening... if (m_bPlayerOpen && m_pPlayerBus && m_pPlayerBus->isEnabled() && ATOMIC_TAS(&m_playerLock)) { m_pPlayerBuff->readMix(m_pPlayerBus->out(), nframes, m_pPlayerBus->channels(), 0, 1.0f); m_bPlayerOpen = (m_iPlayerFrame < m_pPlayerBuff->length()); m_iPlayerFrame += nframes; if (m_bPlayerBus) m_pPlayerBus->process_commit(nframes); else ++iOutputBus; ATOMIC_SET(&m_playerLock, 0); } // This the legal process cycle frame range... unsigned long iFrameStart = pAudioCursor->frame(); unsigned long iFrameEnd = iFrameStart + nframes; // Don't go any further, if not playing. if (!isPlaying()) { // Update time(base) info... updateTimeInfo(iFrameStart); // MIDI managers plugin processing... qtractorMidiManager *pMidiManager = pSession->midiManagers().first(); while (pMidiManager) { pMidiManager->process(iFrameStart, iFrameEnd); if (!pMidiManager->isAudioOutputBus()) ++iOutputBus; pMidiManager = pMidiManager->next(); } // Do the idle processing... for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { // Audio-buffers needs some preparation... if (pTrack->trackType() == qtractorTrack::Audio) { qtractorAudioBus *pInputBus = static_cast (pTrack->inputBus()); qtractorAudioMonitor *pAudioMonitor = static_cast (pTrack->monitor()); // Pre-monitoring... if (pAudioMonitor == nullptr) continue; // Record non-passthru metering... if (pTrack->isRecord() && pInputBus) { pAudioMonitor->process_meter( pInputBus->in(), nframes, pInputBus->channels()); } // Monitor passthru/insert processing... qtractorPluginList *pPluginList = pTrack->pluginList(); if (pSession->isTrackMonitor(pTrack) || pPluginList->isAudioInsertActivated()) { // Plugin-chain processing... qtractorAudioBus *pOutputBus = static_cast (pTrack->outputBus()); if (pOutputBus) { pOutputBus->buffer_prepare(nframes, pInputBus); pPluginList->process(pOutputBus->buffer(), nframes); pAudioMonitor->process(pOutputBus->buffer(), nframes); pOutputBus->buffer_commit(nframes); ++iOutputBus; } } } } // Process audition/pre-listening bus... if (m_bPlayerBus && m_pPlayerBus) m_pPlayerBus->process_commit(nframes); // Pass-thru current audio buses... for (pBus = buses().first(); pBus; pBus = pBus->next()) { pAudioBus = static_cast (pBus); if (pAudioBus && (iOutputBus > 0 || pAudioBus->isMonitor())) pAudioBus->process_commit(nframes); } // Done as idle... g_bProcessing = false; pSession->release(); return 0; } // Bail out if the MIDI-metronome is under count-in... if (pSession->midiEngine()->countIn(nframes) > 0) { g_bProcessing = false; pSession->release(); return 0; } // Metronome/count-in stuff... if ((m_bMetronome || m_iCountIn > 0) && m_pMetroBus && m_pMetroBus->isEnabled()) { const unsigned long iMetroFrameStart = (m_iCountIn > 0 ? m_iCountInFrame : iFrameStart); const unsigned long iMetroFrameEnd = iMetroFrameStart + nframes; unsigned long iMetroBeatStart = (m_iCountIn > 0 ? m_iCountInBeatStart : m_iMetroBeatStart); unsigned int iMetroBeat = (m_iCountIn > 0 ? m_iCountInBeat : m_iMetroBeat); if (iMetroFrameEnd > iMetroBeatStart) { qtractorTimeScale::Cursor& cursor = pSession->timeScale()->cursor(); qtractorTimeScale::Node *pNode = cursor.seekFrame(iMetroFrameStart); qtractorAudioBuffer *pMetroBuff = nullptr; if (pNode->beatIsBar(iMetroBeat)) pMetroBuff = m_pMetroBarBuff; else pMetroBuff = m_pMetroBeatBuff; if (iMetroFrameStart < iMetroBeatStart) { pMetroBuff->readMix(m_pMetroBus->out(), iMetroFrameEnd - iMetroBeatStart, m_pMetroBus->channels(), iMetroBeatStart - iMetroFrameStart, 1.0f); } else if (iMetroFrameStart < iMetroBeatStart + pMetroBuff->length()) { pMetroBuff->readMix(m_pMetroBus->out(), nframes, m_pMetroBus->channels(), 0, 1.0f); } else { iMetroBeatStart = metro_offset(pNode->frameFromBeat(++iMetroBeat)); pMetroBuff->reset(false); if (m_iCountIn > 0) { m_iCountInBeatStart = iMetroBeatStart; m_iCountInBeat = iMetroBeat; // --m_iCountIn; // Not really useful? } else { m_iMetroBeatStart = iMetroBeatStart; m_iMetroBeat = iMetroBeat; } } if (m_bMetroBus || m_iCountIn > 0) m_pMetroBus->process_commit(nframes); } // Check whether count-in ends.... if (m_iCountIn > 0) { m_iCountInFrame += nframes; if (m_iCountInFrame < m_iCountInFrameEnd) { g_bProcessing = false; pSession->release(); return 0; } // Count-in ended. m_iCountIn = 0; // Reset MIDI queue time... pSession->midiEngine()->resetTime(); } } // MIDI managers plugin frame time... const unsigned long iFrameTimeStart = pAudioCursor->frameTime(); const unsigned long iFrameTimeEnd = iFrameTimeStart + nframes; unsigned int nframes2 = m_iBlockSize; if (nframes2 > nframes) nframes2 = nframes; unsigned long iFrameTimeStart2 = iFrameTimeStart; for (unsigned long iFrameStart2 = iFrameStart; iFrameStart2 < iFrameEnd; iFrameStart2 += nframes2) { // Update time(base) info... updateTimeInfo(iFrameStart2); // MIDI managers plugin processing... unsigned long iFrameTimeEnd2 = iFrameTimeStart2 + nframes2; if (iFrameTimeEnd2 > iFrameTimeEnd) iFrameTimeEnd2 = iFrameTimeEnd; qtractorMidiManager *pMidiManager = pSession->midiManagers().first(); while (pMidiManager) { pMidiManager->process(iFrameTimeStart2, iFrameTimeEnd2); if (!pMidiManager->isAudioOutputBus()) ++iOutputBus; pMidiManager = pMidiManager->next(); } iFrameTimeStart2 += nframes2; // Main processing... unsigned long iFrameEnd2 = iFrameStart2 + nframes2; if (iFrameEnd2 > iFrameEnd) iFrameEnd2 = iFrameEnd; // Split processing, in case we're looping... if (pSession->isLooping()) { const unsigned long iLoopEnd = pSession->loopEnd(); if (iFrameStart2 < iLoopEnd) { // Loop-length might be shorter than the buffer-period... while (iFrameEnd2 >= iLoopEnd + nframes2) { // Process the remaining until end-of-loop... pSession->process(pAudioCursor, iFrameStart2, iLoopEnd); m_iBufferOffset += (iLoopEnd - iFrameStart2); // Reset to start-of-loop... iFrameStart2 = pSession->loopStart(); iFrameEnd2 = iFrameStart2 + (iFrameEnd2 - iLoopEnd); // Set to new transport location... if (m_transportMode & qtractorBus::Output) transport_locate(iFrameStart2); pAudioCursor->seek(iFrameStart2); // Update time(base) info... updateTimeInfo(iFrameStart2); } } } // Regular range playback... pSession->process(pAudioCursor, iFrameStart2, iFrameEnd2); // Advance buffer for next stripe... m_iBufferOffset += (iFrameEnd2 - iFrameStart2); } // No more stripping... m_iBufferOffset = 0; // Commit current audio buses... for (pBus = buses().first(); pBus; pBus = pBus->next()) { pAudioBus = static_cast (pBus); if (pAudioBus) pAudioBus->process_commit(nframes); } // Regular range recording (if and when applicable)... if (pSession->isRecording()) pSession->process_record(iFrameStart, iFrameEnd); // Sync with loop boundaries (unlikely?) if (pSession->isLooping() && iFrameStart < pSession->loopEnd() && iFrameEnd >= pSession->loopEnd()) { iFrameEnd = pSession->loopStart() + (iFrameEnd - pSession->loopEnd()); // Set to new transport location... if (m_transportMode & qtractorBus::Output) transport_locate(iFrameEnd); // Take special care on metronome too... if (m_bMetronome) { m_iMetroBeat = pSession->beatFromFrame(iFrameEnd); m_iMetroBeatStart = metro_offset(pSession->frameFromBeat(m_iMetroBeat)); } } // Prepare advance for next cycle... pAudioCursor->seek(iFrameEnd); pAudioCursor->process(nframes); // Always sync to MIDI output thread... // (sure we have a MIDI engine, no?) pSession->midiEngine()->sync(); // Release RT-safeness lock... g_bProcessing = false; pSession->release(); // Process session stuff... return 0; } // Freewheeling process cycle executive (needed for export). void qtractorAudioEngine::process_export ( unsigned int nframes ) { if (m_bExportDone) return; if (m_pExportBuses == nullptr || m_pExportFile == nullptr || m_pExportBuffer == nullptr) return; qtractorSession *pSession = session(); if (pSession == nullptr) return; qtractorSessionCursor *pAudioCursor = sessionCursor(); if (pAudioCursor == nullptr) return; // Make sure we're in a valid state... QListIterator iter(*m_pExportBuses); // Prepare the output buses first... while (iter.hasNext()) iter.next()->process_prepare(nframes); // Prepare all extra audio buses... for (qtractorBus *pBusEx = busesEx().first(); pBusEx; pBusEx = pBusEx->next()) { qtractorAudioBus *pAudioBusEx = static_cast (pBusEx); if (pAudioBusEx) pAudioBusEx->process_prepare(nframes); } // This the legal process cycle frame range... const unsigned long iFrameStart = pAudioCursor->frame(); const unsigned long iFrameEnd = iFrameStart + nframes; unsigned int nframes2 = m_iBlockSize; if (nframes2 > nframes) nframes2 = nframes; // Write output bus buffers to export audio file... if (iFrameStart < m_iExportEnd) { // Prepare mix-down buffer... m_pExportBuffer->process_prepare(nframes); for (unsigned long iFrameStart2 = iFrameStart; iFrameStart2 < iFrameEnd; iFrameStart2 += nframes2) { // Update time(base) info... updateTimeInfo(iFrameStart2); // Main processing... unsigned long iFrameEnd2 = iFrameStart2 + nframes2; if (iFrameEnd2 > iFrameEnd) iFrameEnd2 = iFrameEnd; // MIDI managers plugin processing... qtractorMidiManager *pMidiManager = pSession->midiManagers().first(); while (pMidiManager) { pMidiManager->process(iFrameStart2, iFrameEnd2); pMidiManager = pMidiManager->next(); } // Perform all tracks processing... int iTrack = 0; for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { pTrack->process_export(pAudioCursor->clip(iTrack), iFrameStart2, iFrameEnd2); ++iTrack; } m_iBufferOffset += (iFrameEnd2 - iFrameStart2); } // Prepare advance for next cycle... pAudioCursor->seek(iFrameEnd); // Check end-of-export... if (iFrameEnd > m_iExportEnd) nframes -= (iFrameEnd - m_iExportEnd); // Commit the output buses... iter.toFront(); while (iter.hasNext()) { qtractorAudioBus *pExportBus = iter.next(); pExportBus->process_commit(nframes); m_pExportBuffer->process_add(pExportBus, nframes); } // Write to export file... m_pExportFile->write(m_pExportBuffer->buffer(), nframes); // HACK! Freewheeling observers update (non RT safe!)... qtractorSubject::flushQueue(false); } else { // Are we trough? m_bExportDone = true; // HACK! Reset all observers... qtractorSubject::resetQueue(); // HACK: Reset all MIDI plugin buffers... qtractorMidiManager *pMidiManager = pSession->midiManagers().first(); while (pMidiManager) { pMidiManager->reset(); pMidiManager = pMidiManager->next(); } // HACK: Shut-off (panic) all MIDI tracks... QHash channels; for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() != qtractorTrack::Midi) continue; qtractorMidiBus *pMidiBus = static_cast (pTrack->outputBus()); if (pMidiBus == nullptr) continue; const unsigned short iChannel = pTrack->midiChannel(); pMidiManager = (pTrack->pluginList())->midiManager(); if (pMidiManager) pMidiManager->shutOff(iChannel); const unsigned short iChannelMask = (1 << iChannel); const unsigned short iChannelFlags = channels.value(pMidiBus, 0); if ((iChannelFlags & iChannelMask) == 0) { pMidiManager = (pMidiBus->pluginList_out())->midiManager(); if (pMidiManager) pMidiManager->shutOff(iChannel); channels.insert(pMidiBus, iChannelFlags | iChannelMask); } } // HACK: Reset all audio monitors... resetAllMonitors(); } } // Update time(base)/BBT info. void qtractorAudioEngine::updateTimeInfo ( unsigned long iFrame ) { qtractorSession *pSession = session(); qtractorTimeScale *pTimeScale = pSession->timeScale(); qtractorTimeScale::Cursor& cursor = pTimeScale->cursor(); qtractorTimeScale::Node *pNode = cursor.seekFrame(iFrame); const unsigned int iTicksPerBeat = pTimeScale->ticksPerBeat(); m_timeInfo.frame = iFrame; m_timeInfo.playing = (isPlaying() || isFreewheel()); m_timeInfo.sampleRate = sampleRate(); m_timeInfo.tempo = pNode->tempo; m_timeInfo.beatsPerBar = pNode->beatsPerBar; m_timeInfo.ticksPerBeat = iTicksPerBeat; m_timeInfo.beatType = (1 << pNode->beatDivisor); const unsigned long ticks = pNode->tickFromFrame(iFrame) - pNode->tick; const float beats = float(ticks) / float(iTicksPerBeat); const unsigned short bars = (unsigned short) beats / m_timeInfo.beatsPerBar; m_timeInfo.bar = pNode->bar + bars; m_timeInfo.beat = (unsigned int) beats; m_timeInfo.tick = (unsigned int) ticks; if (m_timeInfo.tick >= (unsigned int) m_timeInfo.ticksPerBeat) m_timeInfo.tick -= (unsigned int) m_timeInfo.beat * m_timeInfo.ticksPerBeat; if (m_timeInfo.beat >= (unsigned int) m_timeInfo.beatsPerBar) m_timeInfo.beat -= (unsigned int) (bars * m_timeInfo.beatsPerBar); m_timeInfo.beats = float(pNode->beat) + beats; m_timeInfo.barBeats = ::truncf(m_timeInfo.beats) - float(m_timeInfo.beat); m_timeInfo.barTicks = pNode->tick + (unsigned long) ( bars * m_timeInfo.beatsPerBar * m_timeInfo.ticksPerBeat); ++m_timeInfo.bar; ++m_timeInfo.beat; #ifdef CONFIG_VST3 qtractorVst3Plugin::updateTime(this); #endif #ifdef CONFIG_CLAP qtractorClapPlugin::updateTime(this); #endif #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_TIME qtractorLv2Plugin::updateTime(this); #endif #endif } // JACK timebase master callback. void qtractorAudioEngine::timebase ( jack_position_t *pPos, int iNewPos ) { #if 0//QTRACTOR_TIMEBASE_OLD qtractorSession *pSession = session(); qtractorTimeScale::Cursor& cursor = pSession->timeScale()->cursor(); qtractorTimeScale::Node *pNode = cursor.seekFrame(pPos->frame); unsigned short bars = 0; unsigned int beats = 0; unsigned long ticks = pNode->tickFromFrame(pPos->frame) - 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); } // Time frame code in bars.beats.ticks ... pPos->valid = JackPositionBBT; pPos->bar = pNode->bar + bars + 1; pPos->beat = beats + 1; pPos->tick = ticks; // Keep current tempo (BPM)... pPos->beats_per_bar = pNode->beatsPerBar; pPos->ticks_per_beat = pNode->ticksPerBeat; pPos->beats_per_minute = pNode->tempo; pPos->beat_type = float(1 << pNode->beatDivisor); #else // Check for major gaps to last known JACK transport position... if (qAbs(long(pPos->frame) - long(m_timeInfo.frame)) > long(m_iBufferSize)) updateTimeInfo(pPos->frame); // Time frame code in bars.beats.ticks ... pPos->valid = JackPositionBBT; pPos->bar = m_timeInfo.bar; pPos->beat = m_timeInfo.beat; pPos->tick = m_timeInfo.tick; // Keep current tempo (BPM)... pPos->beats_per_bar = m_timeInfo.beatsPerBar; pPos->ticks_per_beat = m_timeInfo.ticksPerBeat; pPos->beats_per_minute = m_timeInfo.tempo; pPos->beat_type = m_timeInfo.beatType; pPos->bar_start_tick = m_timeInfo.barTicks; #endif // Tell that we've been here... if (iNewPos) ++m_iTimebase; } // Document element methods. bool qtractorAudioEngine::loadElement ( qtractorDocument *pDocument, QDomElement *pElement ) { qtractorEngine::clear(); createPlayerBus(); createMetroBus(); QStringList audio_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() == "audio-control") { for (QDomNode nProp = eChild.firstChild(); !nProp.isNull(); nProp = nProp.nextSibling()) { QDomElement eProp = nProp.toElement(); if (eProp.isNull()) continue; if (eProp.tagName() == "transport-mode") { qtractorAudioEngine::setTransportMode( qtractorBus::busModeFromText(eProp.text())); } else if (eProp.tagName() == "timebase") { qtractorAudioEngine::setTimebase( qtractorDocument::boolFromText(eProp.text())); } } } else if (eChild.tagName() == "audio-bus") { QString sBusName = eChild.attribute("name"); qtractorBus::BusMode busMode = qtractorBus::busModeFromText(eChild.attribute("mode")); qtractorAudioBus *pAudioBus = new qtractorAudioBus(this, sBusName, busMode); if (!pAudioBus->loadElement(pDocument, &eChild)) return false; qtractorEngine::addBus(pAudioBus); } else if (eChild.tagName() == "metronome-outputs") { if (m_bMetroBus && m_pMetroBus) { m_pMetroBus->loadConnects( m_pMetroBus->outputs(), pDocument, &eChild); } } else if (eChild.tagName() == "player-outputs") { if (m_bPlayerBus && m_pPlayerBus) { m_pPlayerBus->loadConnects( m_pPlayerBus->outputs(), pDocument, &eChild); } } else if (eChild.tagName() == "audio-buses2") { audio_buses2 = qtractorEngine::loadBuses2List( pDocument, &eChild, "audio-bus2"); } } qtractorEngine::setBuses2List(audio_buses2); return true; } bool qtractorAudioEngine::saveElement ( qtractorDocument *pDocument, QDomElement *pElement ) const { // Save transport/control modes... QDomElement eControl = pDocument->document()->createElement("audio-control"); pDocument->saveTextElement("transport-mode", qtractorBus::textFromBusMode( qtractorAudioEngine::transportMode()), &eControl); pDocument->saveTextElement("timebase", qtractorDocument::textFromBool( qtractorAudioEngine::isTimebase()), &eControl); pElement->appendChild(eControl); // Save audio buses... QListIterator iter(qtractorEngine::buses2()); while (iter.hasNext()) { qtractorAudioBus *pAudioBus = static_cast (iter.next()); if (pAudioBus) { // Create the new audio bus element... QDomElement eAudioBus = pDocument->document()->createElement("audio-bus"); pAudioBus->saveElement(pDocument, &eAudioBus); pElement->appendChild(eAudioBus); } } // 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); } // Audition/pre-listening player bus connects... if (m_bPlayerBus && m_pPlayerBus) { QDomElement eOutputs = pDocument->document()->createElement("player-outputs"); qtractorBus::ConnectList outputs; m_pPlayerBus->updateConnects(qtractorBus::Output, outputs); m_pPlayerBus->saveConnects(outputs, pDocument, &eOutputs); pElement->appendChild(eOutputs); } const QStringList& audio_buses2 = qtractorEngine::buses2List(); if (!audio_buses2.isEmpty()) { QDomElement eBuses2 = pDocument->document()->createElement("audio-buses2"); saveBuses2List(pDocument, &eBuses2, "audio-bus2", audio_buses2); pElement->appendChild(eBuses2); } return true; } // JACK Session UUID accessors. void qtractorAudioEngine::setSessionId ( const QString& sSessionId ) { m_sSessionId = sSessionId; } const QString& qtractorAudioEngine::sessionId (void) const { return m_sSessionId; } // Audio-exporting freewheel (internal) state accessors. void qtractorAudioEngine::setFreewheel ( bool bFreewheel ) { m_bFreewheel = bFreewheel; } bool qtractorAudioEngine::isFreewheel (void) const { return m_bFreewheel; } // Audio-exporting (freewheeling) state accessors. void qtractorAudioEngine::setExporting ( bool bExporting ) { m_bExporting = bExporting; } bool qtractorAudioEngine::isExporting (void) const { return m_bExporting; } // Last known export accessors. unsigned long qtractorAudioEngine::exportStart (void) const { return m_iExportStart; } unsigned long qtractorAudioEngine::exportOffset (void) const { return m_iExportOffset; } unsigned long qtractorAudioEngine::exportLength (void) const { return (m_iExportEnd > m_iExportStart ? m_iExportEnd - m_iExportStart : 0); } // Audio-export method. bool qtractorAudioEngine::fileExport ( const QString& sExportPath, const QList& exportBuses, unsigned long iExportStart, unsigned long iExportEnd, int iExportFormat ) { // No simultaneous or foul exports... if (!isActivated() || isPlaying() || isExporting()) return false; // Make sure we have an actual session cursor... qtractorSession *pSession = session(); if (pSession == nullptr) return false; qtractorSessionCursor *pAudioCursor = sessionCursor(); if (pAudioCursor == nullptr) return false; // About to show some progress bar... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return false; QProgressBar *pProgressBar = pMainForm->progressBar(); if (pProgressBar == nullptr) return false; // Cannot have exports longer than current session. if (iExportStart >= iExportEnd) iExportEnd = pSession->sessionEnd(); if (iExportStart >= iExportEnd) return false; // We'll grab the minimum number of channels around, as reference... unsigned short iChannels = 0; QListIterator iter(exportBuses); while (iter.hasNext()) { qtractorAudioBus *pAudioBus = iter.next(); if (pAudioBus->busMode() & qtractorBus::Output && iChannels < pAudioBus->channels()) { iChannels = pAudioBus->channels(); } } if (iChannels < 1) return false; // Get proper file type class... qtractorAudioFile *pExportFile = qtractorAudioFileFactory::createAudioFile(sExportPath, iChannels, sampleRate(), bufferSizeEx(), iExportFormat); // No file ready for export? if (pExportFile == nullptr) return false; // Go open it, for writing of course... if (!pExportFile->open(sExportPath, qtractorAudioFile::Write)) { delete pExportFile; return false; } // We'll be busy... pSession->lock(); // HACK! reset subject/observers queue... qtractorSubject::resetQueue(); // Start with fixing the export range... m_bExporting = true; m_pExportBuses = new QList (exportBuses); m_pExportFile = pExportFile; m_pExportBuffer = new qtractorAudioExportBuffer(iChannels, bufferSizeEx()); m_iExportStart = iExportStart; m_iExportEnd = iExportEnd; m_bExportDone = false; // Prepare and show some progress... pProgressBar->setRange(iExportStart, iExportEnd); pProgressBar->reset(); pProgressBar->show(); // We'll have to save some session parameters... const unsigned long iPlayHead = pSession->playHead(); const unsigned long iLoopStart = pSession->loopStart(); const unsigned long iLoopEnd = pSession->loopEnd(); QHash exportMonitors; QListIterator bus_iter(exportBuses); while (bus_iter.hasNext()) { qtractorAudioBus *pAudioBus = bus_iter.next(); exportMonitors.insert(pAudioBus, pAudioBus->isMonitor()); pAudioBus->setMonitor(false); } // Make sure all track latencies are reset and // maximum track latency is acquainted as offset... m_iExportOffset = 0; for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (!pTrack->isMute() && (!pSession->soloTracks() || pTrack->isSolo())) { qtractorPluginList *pPluginList = pTrack->pluginList(); if (pPluginList) { pPluginList->resetLatency(); const unsigned long iLatency = pPluginList->latency(); if (m_iExportOffset < iLatency) m_iExportOffset = iLatency; } } } if (m_iExportOffset > m_iExportStart) m_iExportOffset = m_iExportStart; // Because we'll have to set the export conditions... pSession->setLoop(0, 0); pSession->setPlayHead(m_iExportStart - m_iExportOffset); // Special initialization. m_iBufferOffset = 0; // Start export (freewheeling)... jack_set_freewheel(m_pJackClient, 1); // Wait for the export to end. struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = 20000000L; // 20msec. while (m_bExporting && !m_bExportDone) { qtractorSession::stabilize(200); #ifdef CONFIG_LV2 #ifdef CONFIG_LV2_TIME qtractorLv2Plugin::updateTimePost(); #endif #endif ::nanosleep(&ts, nullptr); // Ain't that enough? pProgressBar->setValue(pSession->playHead()); } // Stop export (freewheeling)... jack_set_freewheel(m_pJackClient, 0); // May close the file... m_pExportFile->close(); // Restore session at ease... pSession->setLoop(iLoopStart, iLoopEnd); pSession->setPlayHead(iPlayHead); bus_iter.toFront(); while (bus_iter.hasNext()) { qtractorAudioBus *pAudioBus = bus_iter.next(); pAudioBus->setMonitor(exportMonitors.value(pAudioBus, false)); } // Check user cancellation... const bool bResult = m_bExporting; // Free up things here. delete m_pExportBuffer; delete m_pExportBuses; delete m_pExportFile; // Made some progress... pProgressBar->hide(); m_bExporting = false; m_pExportBuses = nullptr; m_pExportFile = nullptr; m_pExportBuffer = nullptr; // m_iExportStart = 0; // m_iExportEnd = 0; m_bExportDone = true; // Back to business.. pSession->unlock(); // Done whether successfully. return bResult; } // Special track-immediate methods. void qtractorAudioEngine::trackMute ( qtractorTrack *pTrack, bool bMute ) { if (bMute) (pTrack->pluginList())->resetBuffers(); sessionCursor()->updateTrackClip(pTrack); } // Metronome switching. void qtractorAudioEngine::setMetronome ( bool bMetronome ) { m_bMetronome = bMetronome; if (isPlaying()) resetMetro(); } bool qtractorAudioEngine::isMetronome (void) const { return m_bMetronome; } // Metronome enabled accessors. void qtractorAudioEngine::setMetroEnabled ( bool bMetroEnabled ) { m_bMetroEnabled = bMetroEnabled; } bool qtractorAudioEngine::isMetroEnabled (void) const { return m_bMetroEnabled; } // Metronome bus mode accessors. void qtractorAudioEngine::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) { if (!outs.isEmpty()) m_pMetroBus->updateConnects(qtractorBus::Output, outs, true); else m_pMetroBus->autoConnect(); } } } bool qtractorAudioEngine::isMetroBus (void) const { return m_bMetroBus; } void qtractorAudioEngine::resetMetroBus (void) { if (m_bMetronome && m_bMetroBus && m_pMetroBus) return; createMetroBus(); if (isActivated()) openMetroBus(); } // Metronome bus defaults accessors. void qtractorAudioEngine::setMetroAutoConnect ( bool bMetroAutoConnect ) { m_bMetroAutoConnect = bMetroAutoConnect; } bool qtractorAudioEngine::isMetroAutoConnect (void) const { return m_bMetroAutoConnect; } // Metronome bar audio sample. void qtractorAudioEngine::setMetroBarFilename ( const QString& sFilename ) { m_sMetroBarFilename = sFilename; } const QString& qtractorAudioEngine::metroBarFilename (void) const { return m_sMetroBarFilename; } // Metronome bar audio sample gain. void qtractorAudioEngine::setMetroBarGain ( float fGain ) { m_fMetroBarGain = fGain; } float qtractorAudioEngine::metroBarGain (void) const { return m_fMetroBarGain; } // Metronome beat audio sample. void qtractorAudioEngine::setMetroBeatFilename ( const QString& sFilename ) { m_sMetroBeatFilename = sFilename; } const QString& qtractorAudioEngine::metroBeatFilename() const { return m_sMetroBeatFilename; } // Metronome beat audio sample gain. void qtractorAudioEngine::setMetroBeatGain ( float fGain ) { m_fMetroBeatGain = fGain; } float qtractorAudioEngine::metroBeatGain (void) const { return m_fMetroBeatGain; } // Metronome latency offset (in frames). void qtractorAudioEngine::setMetroOffset ( unsigned long iMetroOffset ) { m_iMetroOffset = iMetroOffset; } unsigned long qtractorAudioEngine::metroOffset (void) const { return m_iMetroOffset; } // Metronome latency offset compensation. unsigned long qtractorAudioEngine::metro_offset ( unsigned long iFrame ) const { const unsigned long iOffset = m_iMetroOffset; // + (m_pMetroBus ? m_pMetroBus->latency_out() : 0); return (iFrame > iOffset ? iFrame - iOffset : iFrame); } // Metronome count-in switching. void qtractorAudioEngine::setCountIn ( bool bCountIn ) { m_bCountIn = bCountIn; } bool qtractorAudioEngine::isCountIn (void) const { return m_bCountIn; } // Metronome count-in mode. void qtractorAudioEngine::setCountInMode ( CountInMode countInMode ) { m_countInMode = countInMode; } qtractorAudioEngine::CountInMode qtractorAudioEngine::countInMode (void) const { return m_countInMode; } // Metronome count-in number of beats. void qtractorAudioEngine::setCountInBeats ( unsigned short iCountInBeats ) { m_iCountInBeats = iCountInBeats; } unsigned short qtractorAudioEngine::countInBeats (void) const { return m_iCountInBeats; } // Metronome count-in status. unsigned short qtractorAudioEngine::countIn (void) const { return m_iCountIn; } // Create audio metronome stuff... void qtractorAudioEngine::createMetroBus (void) { deleteMetroBus(); if (!m_bMetroEnabled) return; // Whether metronome bus is here owned, or... if (m_bMetroBus) { m_pMetroBus = new qtractorAudioBus(this, "Metronome", qtractorBus::BusMode(qtractorBus::Output | qtractorBus::Ex)); m_pMetroBus->setAutoConnect(m_bMetroAutoConnect); } else { // Metronome bus gets to be the first available output bus... QListIterator iter(buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Output) { m_pMetroBus = static_cast (pBus); break; } } } } // Open audio metronome stuff... bool qtractorAudioEngine::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(); } // Enough number of channels?... const unsigned short iChannels = m_pMetroBus->channels(); if (iChannels < 1) { closeMetroBus(); return false; } // We got it... m_pMetroBarBuff = new qtractorAudioBuffer(m_pSyncThread, iChannels); m_pMetroBarBuff->setGain(m_fMetroBarGain); m_pMetroBarBuff->open(m_sMetroBarFilename); m_pMetroBeatBuff = new qtractorAudioBuffer(m_pSyncThread, iChannels); m_pMetroBeatBuff->setGain(m_fMetroBeatGain); m_pMetroBeatBuff->open(m_sMetroBeatFilename); return true; } // Close audio metronome stuff. void qtractorAudioEngine::closeMetroBus (void) { if (m_pMetroBeatBuff) { m_pMetroBeatBuff->close(); delete m_pMetroBeatBuff; m_pMetroBeatBuff = nullptr; } if (m_pMetroBarBuff) { m_pMetroBarBuff->close(); delete m_pMetroBarBuff; m_pMetroBarBuff = nullptr; } if (m_bMetroBus && m_pMetroBus) { m_pMetroBus->close(); removeBusEx(m_pMetroBus); } m_iMetroBeatStart = 0; m_iMetroBeat = 0; m_iCountIn = 0; m_iCountInBeatStart = 0; m_iCountInBeat = 0; m_iCountInFrame = 0; m_iCountInFrameEnd = 0; } // Destroy audio metronome stuff. void qtractorAudioEngine::deleteMetroBus (void) { closeMetroBus(); if (m_bMetroBus && m_pMetroBus) delete m_pMetroBus; m_pMetroBus = nullptr; } // Reset Audio metronome. void qtractorAudioEngine::resetMetro ( bool bCountIn ) { if (!m_bMetroEnabled) return; if (!m_bMetronome && m_iCountInBeats < 1) return; qtractorSessionCursor *pAudioCursor = sessionCursor(); if (pAudioCursor == nullptr) return; qtractorSession *pSession = session(); if (pSession == nullptr) return; // Reset to the next beat position... unsigned long iFrame = pAudioCursor->frame(); qtractorTimeScale::Cursor& cursor = pSession->timeScale()->cursor(); qtractorTimeScale::Node *pNode = cursor.seekFrame(iFrame); // FIXME: Each sample buffer must be bounded properly... unsigned long iMaxLength = 0; const unsigned short iMetroBeat = pNode->beatFromFrame(iFrame); if (iMetroBeat > 0) { m_iMetroBeat = iMetroBeat; m_iMetroBeatStart = metro_offset(pNode->frameFromBeat(m_iMetroBeat)); iMaxLength = (m_iMetroBeatStart / m_iMetroBeat); } else { m_iMetroBeat = 0; m_iMetroBeatStart = 0; iMaxLength = pNode->frameFromBeat(1); } if (m_pMetroBarBuff) { const unsigned long iMetroBarLength = m_pMetroBarBuff->frames(); m_pMetroBarBuff->setLength( iMetroBarLength > iMaxLength ? iMaxLength : iMetroBarLength); m_pMetroBarBuff->reset(false); } if (m_pMetroBeatBuff) { const unsigned long iMetroBeatLength = m_pMetroBeatBuff->frames(); m_pMetroBeatBuff->setLength( iMetroBeatLength > iMaxLength ? iMaxLength : iMetroBeatLength); m_pMetroBeatBuff->reset(false); } // Start count-in stuff... unsigned short iCountInBeats = 0; if (bCountIn && m_bCountIn && ( (m_countInMode == CountInPlayback) || (m_countInMode == CountInRecording && pSession->isRecording()))) iCountInBeats = m_iCountInBeats; if (iCountInBeats > 0) { m_iCountIn = iCountInBeats; m_iCountInBeat = m_iMetroBeat; m_iCountInBeatStart = m_iMetroBeatStart; m_iCountInFrame = iFrame; m_iCountInFrameEnd = m_iCountInFrame + metro_offset(pNode->frameFromBeat( m_iCountInBeat + iCountInBeats)) - m_iCountInBeatStart; ++m_iCountIn; // Give some slack to the end... } else { m_iCountIn = 0; m_iCountInBeat = 0; m_iCountInBeatStart = 0; m_iCountInFrame = 0; m_iCountInFrameEnd = 0; } } // Audition/pre-listening bus mode accessors. void qtractorAudioEngine::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) { if (!outs.isEmpty()) m_pPlayerBus->updateConnects(qtractorBus::Output, outs, true); else m_pPlayerBus->autoConnect(); } } } bool qtractorAudioEngine::isPlayerBus (void) const { return m_bPlayerBus; } void qtractorAudioEngine::resetPlayerBus (void) { if (m_bPlayerBus && m_pPlayerBus) return; createPlayerBus(); if (isActivated()) openPlayerBus(); } // Audition/pre-listening bus defaults accessors. void qtractorAudioEngine::setPlayerAutoConnect ( bool bPlayerAutoConnect ) { m_bPlayerAutoConnect = bPlayerAutoConnect; } bool qtractorAudioEngine::isPlayerAutoConnect (void) const { return m_bPlayerAutoConnect; } // Create audition/pre-listening stuff... void qtractorAudioEngine::createPlayerBus (void) { deletePlayerBus(); // Whether audition/pre-listening bus is here owned, or... if (m_bPlayerBus) { m_pPlayerBus = new qtractorAudioBus(this, "Player", qtractorBus::BusMode(qtractorBus::Output | qtractorBus::Ex)); m_pPlayerBus->setAutoConnect(m_bPlayerAutoConnect); } else { // Audition/pre-listening bus gets to be // the first available output bus... QListIterator iter(buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Output) { m_pPlayerBus = static_cast (pBus); break; } } } } // Open audition/pre-listening player stuff... bool qtractorAudioEngine::openPlayerBus (void) { closePlayerBus(); // Is there any? if (m_pPlayerBus == nullptr) createPlayerBus(); if (m_pPlayerBus == nullptr) return false; if (m_bPlayerBus) { addBusEx(m_pPlayerBus); m_pPlayerBus->open(); } // Enough number of channels?... const unsigned short iChannels = m_pPlayerBus->channels(); if (iChannels < 1) { closePlayerBus(); return false; } // We got it... m_pPlayerBuff = new qtractorAudioBuffer(m_pSyncThread, iChannels); return true; } // Close audition/pre-listening stuff... void qtractorAudioEngine::closePlayerBus (void) { if (m_pPlayerBuff) { m_pPlayerBuff->close(); delete m_pPlayerBuff; m_pPlayerBuff = nullptr; } if (m_bPlayerBus && m_pPlayerBus) { m_pPlayerBus->close(); removeBusEx(m_pPlayerBus); } } // Destroy audition/pre-listening stuff... void qtractorAudioEngine::deletePlayerBus (void) { closePlayerBus(); if (m_bPlayerBus && m_pPlayerBus) delete m_pPlayerBus; m_pPlayerBus = nullptr; } // Tell whether audition/pre-listening is active... bool qtractorAudioEngine::isPlayerOpen (void) const { return m_bPlayerOpen; } // Open and start audition/pre-listening... bool qtractorAudioEngine::openPlayer ( const QString& sFilename ) { // Must have a valid session... qtractorSession *pSession = session(); if (pSession == nullptr) return false; // Acquire proper locking... while (!ATOMIC_TAS(&m_playerLock)) pSession->stabilize(); // May close it logically... m_bPlayerOpen = false; // Is there any? if (m_pPlayerBuff) { m_pPlayerBuff->close(); m_pPlayerBuff->setLength(0); m_bPlayerOpen = m_pPlayerBuff->open(sFilename); } m_iPlayerFrame = 0; // Release player lock... ATOMIC_SET(&m_playerLock, 0); return m_bPlayerOpen; } // Stop and close audition/pre-listening... void qtractorAudioEngine::closePlayer (void) { // Must have a valid session... qtractorSession *pSession = session(); if (pSession == nullptr) return; // Acquire proper locking... while (!ATOMIC_TAS(&m_playerLock)) pSession->stabilize(); m_bPlayerOpen = false; if (m_pPlayerBuff) m_pPlayerBuff->close(); m_iPlayerFrame = 0; // Release player lock... ATOMIC_SET(&m_playerLock, 0); } // Retrieve/restore all connections, on all audio buses. // return the total number of effective (re)connection attempts... int qtractorAudioEngine::updateConnects (void) { // Do it as usual, on all standard owned dependable buses... return qtractorEngine::updateConnects(); } // JACK Transport mode accessors. void qtractorAudioEngine::setTransportMode ( qtractorBus::BusMode transportMode ) { const bool bActivated = isActivated(); if (bActivated && (m_transportMode & qtractorBus::Input)) { jack_set_sync_callback(m_pJackClient, nullptr, nullptr); } m_transportMode = transportMode; if (bActivated && (m_transportMode & qtractorBus::Input)) { jack_set_sync_callback(m_pJackClient, qtractorAudioEngine_sync, this); } } qtractorBus::BusMode qtractorAudioEngine::transportMode (void) const { return m_transportMode; } // JACK Transport latency accessors. void qtractorAudioEngine::setTransportLatency ( unsigned int iTransportLatency ) { m_iTransportLatency = iTransportLatency; } unsigned int qtractorAudioEngine::transportLatency (void) const { return m_iTransportLatency; } // JACK Timebase mode accessors. void qtractorAudioEngine::setTimebase ( bool bTimebase ) { m_bTimebase = bTimebase; } bool qtractorAudioEngine::isTimebase (void) const { return m_bTimebase; } bool qtractorAudioEngine::isTimebaseEx (void) const { return (!m_bTimebase || m_iTimebase > 0); } // JACK Timebase reset method. void qtractorAudioEngine::resetTimebase (void) { if (m_pJackClient == nullptr) return; if (m_iTimebase > 0) { // Release being a timebase master, if any... jack_release_timebase(m_pJackClient); m_iTimebase = 0; } if (m_bTimebase) { // Just force the timebase callback, maybe once again... jack_set_timebase_callback(m_pJackClient, 0 /* FIXME: un-conditional! */, qtractorAudioEngine_timebase, this); } } // Absolute number of frames elapsed since engine start. unsigned long qtractorAudioEngine::jackFrameTime (void) const { return (m_pJackClient ? jack_frame_time(m_pJackClient) : 0); } // Reset all audio monitoring... void qtractorAudioEngine::resetAllMonitors (void) { // There must a session reference... qtractorSession *pSession = session(); if (pSession == nullptr) return; // Reset all audio bus monitors... for (qtractorBus *pBus = buses().first(); pBus; pBus = pBus->next()) { qtractorAudioBus *pAudioBus = static_cast (pBus); if (pAudioBus) { // if (pAudioBus->audioMonitor_in()) // pAudioBus->audioMonitor_in()->reset(); if (pAudioBus->audioMonitor_out()) pAudioBus->audioMonitor_out()->reset(); } } // Reset all audio track channel monitors... for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() == qtractorTrack::Audio) { qtractorAudioMonitor *pAudioMonitor = static_cast (pTrack->monitor()); if (pAudioMonitor) pAudioMonitor->reset(); qtractorPluginList *pPluginList = pTrack->pluginList(); if (pPluginList) pPluginList->resetLatency(); } } } // Auxiliary audio output bus sorting method... // void qtractorAudioEngine::resetAudioOutBus ( qtractorAudioBus *pAudioBus, qtractorPluginList *pPluginList ) { if (pAudioBus == nullptr) return; if ((pAudioBus->busMode() & qtractorBus::Output) == 0) return; if (pPluginList == nullptr) return; if ((pPluginList->flags() & qtractorPluginList::AudioOutBus) == 0) return; qtractorAudioBus *pAfterBus = nullptr; for (qtractorBus *pBus = buses().first(); pBus; pBus = pBus->next()) { if ((pBus->busMode() & qtractorBus::Output) == 0) continue; if (pBus->pluginList_out() == pPluginList) { pAfterBus = static_cast (pBus); break; } } for (qtractorBus *pBus = buses().first(); pBus; pBus = pBus->next()) { if ((pBus->busMode() & qtractorBus::Output) == 0) continue; qtractorAudioBus *pAudioOutBus = static_cast (pBus); if (pAudioOutBus == pAfterBus) break; if (pAudioOutBus == pAudioBus) { qtractorEngine::moveBus(pAudioBus, pAfterBus); break; } } } QStringList qtractorAudioEngine::cyclicAudioOutBuses ( qtractorAudioBus *pAudioBus ) const { QStringList audioOutBuses; for (qtractorBus *pBus = buses().first(); pBus; pBus = pBus->next()) { if ((pBus->busMode() & qtractorBus::Output) == 0) continue; qtractorAudioBus *pAudioOutBus = static_cast (pBus); if (pAudioOutBus == nullptr) continue; if (pAudioOutBus == pAudioBus) continue; qtractorPluginList *pPluginList = pAudioOutBus->pluginList_out(); if (pPluginList == nullptr) continue; if (pPluginList == pAudioBus->pluginList_out()) 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 == channels > 0 => Audio aux-send. qtractorAudioAuxSendPlugin *pAudioAuxSendPlugin = static_cast (pPlugin); if (pAudioAuxSendPlugin && pAudioAuxSendPlugin->audioBus() == pAudioBus) { audioOutBuses.append(pAudioOutBus->busName()); audioOutBuses.append(cyclicAudioOutBuses(pAudioOutBus)); } } } } return audioOutBuses; } // Transport locate/reposition (timebase aware)... void qtractorAudioEngine::transport_locate ( unsigned long iFrame ) { if (m_bTimebase) { jack_position_t pos; pos.frame = iFrame; timebase(&pos, 1); jack_transport_reposition(m_pJackClient, &pos); } else { jack_transport_locate(m_pJackClient, iFrame); } } //---------------------------------------------------------------------- // class qtractorAudioBus -- Managed JACK port set // // Constructor. qtractorAudioBus::qtractorAudioBus ( qtractorAudioEngine *pAudioEngine, const QString& sBusName, BusMode busMode, bool bMonitor, unsigned short iChannels ) : qtractorBus(pAudioEngine, sBusName, busMode, bMonitor) { m_iChannels = iChannels; if ((busMode & qtractorBus::Input) && !(busMode & qtractorBus::Ex)) { m_pIAudioMonitor = new qtractorAudioMonitor(iChannels); m_pIPluginList = createPluginList(qtractorPluginList::AudioInBus); } else { m_pIAudioMonitor = nullptr; m_pIPluginList = nullptr; } if ((busMode & qtractorBus::Output) && !(busMode & qtractorBus::Ex)) { m_pOAudioMonitor = new qtractorAudioMonitor(iChannels); m_pOPluginList = createPluginList(qtractorPluginList::AudioOutBus); } else { m_pOAudioMonitor = nullptr; m_pOPluginList = nullptr; } m_bAutoConnect = false; m_ppIPorts = nullptr; m_ppOPorts = nullptr; m_ppIBuffer = nullptr; m_ppOBuffer = nullptr; m_ppXBuffer = nullptr; m_ppYBuffer = nullptr; m_bEnabled = false; #if defined(__SSE__) if (sse_enabled()) m_pfnBufferAdd = sse_buffer_add; else #endif #if defined(__ARM_NEON__) m_pfnBufferAdd = neon_buffer_add; if (false) #endif m_pfnBufferAdd = std_buffer_add; } // Destructor. qtractorAudioBus::~qtractorAudioBus (void) { close(); if (m_pIAudioMonitor) delete m_pIAudioMonitor; if (m_pOAudioMonitor) delete m_pOAudioMonitor; if (m_pIPluginList) delete m_pIPluginList; if (m_pOPluginList) delete m_pOPluginList; } // Channel number property accessor. void qtractorAudioBus::setChannels ( unsigned short iChannels ) { qtractorAudioEngine *pAudioEngine = static_cast (engine()); if (pAudioEngine == nullptr) return; m_iChannels = iChannels; if (m_pIAudioMonitor) m_pIAudioMonitor->setChannels(iChannels); if (m_pOAudioMonitor) m_pOAudioMonitor->setChannels(iChannels); #if 0 if (m_pIPluginList) updatePluginList(m_pIPluginList, qtractorPluginList::AudioInBus); if (m_pOPluginList) updatePluginList(m_pOPluginList, qtractorPluginList::AudioOutBus); #endif } unsigned short qtractorAudioBus::channels (void) const { return m_iChannels; } // Auto-connection predicate. void qtractorAudioBus::setAutoConnect ( bool bAutoConnect ) { m_bAutoConnect = bAutoConnect; } bool qtractorAudioBus::isAutoConnect (void) const { return m_bAutoConnect; } // Register and pre-allocate bus port buffers. bool qtractorAudioBus::open (void) { // close(); qtractorAudioEngine *pAudioEngine = static_cast (engine()); if (pAudioEngine == nullptr) return false; jack_client_t *pJackClient = pAudioEngine->jackClient(); if (pJackClient == nullptr) return false; const qtractorBus::BusMode busMode = qtractorAudioBus::busMode(); const unsigned int iBufferSizeEx = pAudioEngine->bufferSizeEx(); unsigned short i; unsigned short iDisabled = 0; if (busMode & qtractorBus::Input) { // Register and allocate input port buffers... m_ppIPorts = new jack_port_t * [m_iChannels]; m_ppIBuffer = new float * [m_iChannels]; const QString sIPortName(busName() + "/in_%1"); for (i = 0; i < m_iChannels; ++i) { m_ppIPorts[i] = jack_port_register(pJackClient, sIPortName.arg(i + 1).toUtf8().constData(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); m_ppIBuffer[i] = nullptr; if (m_ppIPorts[i] == nullptr) ++iDisabled; } } if (busMode & qtractorBus::Output) { // Register and allocate output port buffers... m_ppOPorts = new jack_port_t * [m_iChannels]; m_ppOBuffer = new float * [m_iChannels]; const QString sOPortName(busName() + "/out_%1"); for (i = 0; i < m_iChannels; ++i) { m_ppOPorts[i] = jack_port_register(pJackClient, sOPortName.arg(i + 1).toUtf8().constData(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); m_ppOBuffer[i] = nullptr; if (m_ppOPorts[i] == nullptr) ++iDisabled; } } // Allocate internal working bus buffers... m_ppXBuffer = new float * [m_iChannels]; m_ppYBuffer = new float * [m_iChannels]; for (i = 0; i < m_iChannels; ++i) { m_ppXBuffer[i] = new float [iBufferSizeEx]; m_ppYBuffer[i] = nullptr; } // Update monitor subject names... qtractorAudioBus::updateBusName(); // Plugin lists need some buffer (re)allocation too... if (m_pIPluginList) updatePluginList(m_pIPluginList, qtractorPluginList::AudioInBus); if (m_pOPluginList) updatePluginList(m_pOPluginList, qtractorPluginList::AudioOutBus); // Finally, open for biz... m_bEnabled = (iDisabled == 0); return true; } // Unregister and post-free bus port buffers. void qtractorAudioBus::close (void) { // Close for biz, immediate... m_bEnabled = false; qtractorAudioEngine *pAudioEngine = static_cast (engine()); if (pAudioEngine == nullptr) return; jack_client_t *pJackClient = pAudioEngine->jackClient(); const qtractorBus::BusMode busMode = qtractorAudioBus::busMode(); unsigned short i; if (busMode & qtractorBus::Input) { // Unregister and free input ports, // if we're not shutdown... if (m_ppIPorts && pJackClient) { for (i = 0; i < m_iChannels; ++i) { if (m_ppIPorts[i]) { jack_port_unregister(pJackClient, m_ppIPorts[i]); m_ppIPorts[i] = nullptr; } } } // Free input ports. if (m_ppIPorts) delete [] m_ppIPorts; m_ppIPorts = nullptr; // Free input buffers. if (m_ppIBuffer) delete [] m_ppIBuffer; m_ppIBuffer = nullptr; } if (busMode & qtractorBus::Output) { // Unregister and free output ports, // if we're not shutdown... if (m_ppOPorts && pJackClient) { for (i = 0; i < m_iChannels; ++i) { if (m_ppOPorts[i]) { jack_port_unregister(pJackClient, m_ppOPorts[i]); m_ppOPorts[i] = nullptr; } } } // Free output ports. if (m_ppOPorts) delete [] m_ppOPorts; m_ppOPorts = nullptr; // Free output buffers. if (m_ppOBuffer) delete [] m_ppOBuffer; m_ppOBuffer = nullptr; } // Free internal buffers. if (m_ppXBuffer) { for (i = 0; i < m_iChannels; ++i) delete [] m_ppXBuffer[i]; delete [] m_ppXBuffer; m_ppXBuffer = nullptr; } if (m_ppYBuffer) { delete [] m_ppYBuffer; m_ppYBuffer = nullptr; } } // Auto-connect to physical ports. void qtractorAudioBus::autoConnect (void) { if (!m_bAutoConnect) return; qtractorAudioEngine *pAudioEngine = static_cast (engine()); if (pAudioEngine == nullptr) return; jack_client_t *pJackClient = pAudioEngine->jackClient(); if (pJackClient == nullptr) return; const qtractorBus::BusMode busMode = qtractorAudioBus::busMode(); unsigned short i; if ((busMode & qtractorBus::Input) && inputs().isEmpty()) { const char **ppszOPorts = jack_get_ports(pJackClient, 0, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput | JackPortIsPhysical); if (ppszOPorts) { const QString sIPortName = pAudioEngine->clientName() + ':' + busName() + "/in_%1"; for (i = 0; i < m_iChannels && ppszOPorts[i]; ++i) { jack_connect(pJackClient, ppszOPorts[i], sIPortName.arg(i + 1).toUtf8().constData()); } ::free(ppszOPorts); } } if ((busMode & qtractorBus::Output) && outputs().isEmpty()) { const char **ppszIPorts = jack_get_ports(pJackClient, 0, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput | JackPortIsPhysical); if (ppszIPorts) { const QString sOPortName = pAudioEngine->clientName() + ':' + busName() + "/out_%1"; for (i = 0; i < m_iChannels && ppszIPorts[i]; ++i) { jack_connect(pJackClient, sOPortName.arg(i + 1).toUtf8().constData(), ppszIPorts[i]); } ::free(ppszIPorts); } } } // Bus mode change event. void qtractorAudioBus::updateBusMode (void) { const qtractorBus::BusMode busMode = qtractorAudioBus::busMode(); // Have a new/old input monitor? if ((busMode & qtractorBus::Input) && !(busMode & qtractorBus::Ex)) { if (m_pIAudioMonitor == nullptr) m_pIAudioMonitor = new qtractorAudioMonitor(m_iChannels); if (m_pIPluginList == nullptr) m_pIPluginList = createPluginList(qtractorPluginList::AudioInBus); } else { if (m_pIAudioMonitor) { delete m_pIAudioMonitor; m_pIAudioMonitor = 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_pOAudioMonitor == nullptr) m_pOAudioMonitor = new qtractorAudioMonitor(m_iChannels); if (m_pOPluginList == nullptr) m_pOPluginList = createPluginList(qtractorPluginList::AudioOutBus); } else { if (m_pOAudioMonitor) { delete m_pOAudioMonitor; m_pOAudioMonitor = nullptr; } if (m_pOPluginList) { delete m_pOPluginList; m_pOPluginList = nullptr; } } } // Bus name change event. void qtractorAudioBus::updateBusName (void) { const QString& sBusName = qtractorAudioBus::busName(); if (m_pIAudioMonitor) { const QString& sBusNameIn = QObject::tr("%1 In").arg(sBusName); m_pIAudioMonitor->gainSubject()->setName( QObject::tr("%1 Gain").arg(sBusNameIn)); m_pIAudioMonitor->panningSubject()->setName( QObject::tr("%1 Pan").arg(sBusNameIn)); } if (m_pOAudioMonitor) { const QString& sBusNameOut = QObject::tr("%1 Out").arg(sBusName); m_pOAudioMonitor->gainSubject()->setName( QObject::tr("%1 Gain").arg(sBusNameOut)); m_pOAudioMonitor->panningSubject()->setName( QObject::tr("%1 Pan").arg(sBusNameOut)); } qtractorBus::updateBusName(); } // Process cycle preparator. void qtractorAudioBus::process_prepare ( unsigned int nframes ) { if (!m_bEnabled) return; const qtractorBus::BusMode busMode = qtractorAudioBus::busMode(); unsigned short i; if (busMode & qtractorBus::Input) { for (i = 0; i < m_iChannels; ++i) { m_ppIBuffer[i] = static_cast (jack_port_get_buffer(m_ppIPorts[i], nframes)); } } if (busMode & qtractorBus::Output) { for (i = 0; i < m_iChannels; ++i) { m_ppOBuffer[i] = static_cast (jack_port_get_buffer(m_ppOPorts[i], nframes)); // Zero-out output buffer... ::memset(m_ppOBuffer[i], 0, nframes * sizeof(float)); } } } // Process cycle monitor. void qtractorAudioBus::process_monitor ( unsigned int nframes ) { if (!m_bEnabled) return; const qtractorBus::BusMode busMode = qtractorAudioBus::busMode(); if (busMode & qtractorBus::Input) { if (m_pIPluginList) m_pIPluginList->process(m_ppIBuffer, nframes); if (m_pIAudioMonitor) m_pIAudioMonitor->process(m_ppIBuffer, nframes); if (isMonitor() && (busMode & qtractorBus::Output)) { (*m_pfnBufferAdd)(m_ppOBuffer, m_ppIBuffer, nframes, m_iChannels, m_iChannels, 0); } } } // Process cycle commitment. void qtractorAudioBus::process_commit ( unsigned int nframes ) { if (!m_bEnabled) return; if (m_pOPluginList) m_pOPluginList->process(m_ppOBuffer, nframes); if (m_pOAudioMonitor) m_pOAudioMonitor->process(m_ppOBuffer, nframes); } // Bus-buffering methods. void qtractorAudioBus::buffer_prepare ( unsigned int nframes, qtractorAudioBus *pInputBus ) { if (!m_bEnabled) return; qtractorAudioEngine *pAudioEngine = static_cast (engine()); if (pAudioEngine == nullptr) return; const unsigned int offset = pAudioEngine->bufferOffset(); const unsigned int nbytes = nframes * sizeof(float); if (pInputBus == nullptr) { for (unsigned short i = 0; i < m_iChannels; ++i) { m_ppYBuffer[i] = m_ppXBuffer[i] + offset; ::memset(m_ppYBuffer[i], 0, nbytes); } return; } const unsigned short iBuffers = pInputBus->channels(); float **ppBuffer = pInputBus->in(); if (m_iChannels == iBuffers) { // Exact buffer copy... for (unsigned short i = 0; i < iBuffers; ++i) { m_ppYBuffer[i] = m_ppXBuffer[i] + offset; ::memcpy(m_ppYBuffer[i], ppBuffer[i] + offset, nbytes); } } else { // Buffer merge/multiplex... unsigned short i; for (i = 0; i < m_iChannels; ++i) { m_ppYBuffer[i] = m_ppXBuffer[i] + offset; ::memset(m_ppYBuffer[i], 0, nbytes); } if (m_iChannels > iBuffers) { unsigned short j = 0; for (i = 0; i < m_iChannels; ++i) { ::memcpy(m_ppYBuffer[i], ppBuffer[j] + offset, nbytes); if (++j >= iBuffers) j = 0; } } else { // (m_iChannels < iBuffers) (*m_pfnBufferAdd)(m_ppXBuffer, ppBuffer, nframes, m_iChannels, iBuffers, offset); } } } void qtractorAudioBus::buffer_commit ( unsigned int nframes ) { if (!m_bEnabled || (busMode() & qtractorBus::Output) == 0) return; qtractorAudioEngine *pAudioEngine = static_cast (engine()); if (pAudioEngine == nullptr) return; (*m_pfnBufferAdd)(m_ppOBuffer, m_ppXBuffer, nframes, m_iChannels, m_iChannels, pAudioEngine->bufferOffset()); } // Virtual I/O bus-monitor accessors. qtractorMonitor *qtractorAudioBus::monitor_in (void) const { return audioMonitor_in(); } qtractorMonitor *qtractorAudioBus::monitor_out (void) const { return audioMonitor_out(); } // Audio I/O bus-monitor accessors. qtractorAudioMonitor *qtractorAudioBus::audioMonitor_in (void) const { return m_pIAudioMonitor; } qtractorAudioMonitor *qtractorAudioBus::audioMonitor_out (void) const { return m_pOAudioMonitor; } // Plugin-chain accessors. qtractorPluginList *qtractorAudioBus::pluginList_in (void) const { return m_pIPluginList; } qtractorPluginList *qtractorAudioBus::pluginList_out (void) const { return m_pOPluginList; } // Audio I/O port latency accessors. unsigned int qtractorAudioBus::latency_in (void) const { if (m_ppIPorts == nullptr) return 0; qtractorAudioEngine *pAudioEngine = static_cast (engine()); if (pAudioEngine == nullptr) return 0; unsigned int iLatencyIn = 0; #ifdef CONFIG_JACK_LATENCY jack_nframes_t range_max = 0; jack_latency_range_t range; for (unsigned int i = 0; i < m_iChannels; ++i) { if (m_ppIPorts[i] == nullptr) continue; jack_port_get_latency_range(m_ppIPorts[i], JackCaptureLatency, &range); if (range_max > range.max || i == 0) range_max = range.max; } iLatencyIn += range_max; #else jack_nframes_t lat, lat_min = 0; for (unsigned int i = 0; i < m_iChannels; ++i) { if (m_ppIPorts[i] == nullptr) continue; lat = jack_port_get_latency(m_ppIPorts[i]); if (lat_max > lat || i == 0) lat_max = lat; } iLatencyIn += lat_max; #endif const unsigned int iBufferSize = pAudioEngine->bufferSize(); if (iLatencyIn < iBufferSize) iLatencyIn += iBufferSize; return iLatencyIn; } unsigned int qtractorAudioBus::latency_out (void) const { if (m_ppOPorts == nullptr) return 0; qtractorAudioEngine *pAudioEngine = static_cast (engine()); if (pAudioEngine == nullptr) return 0; unsigned int iLatencyOut = 0; #ifdef CONFIG_JACK_LATENCY jack_nframes_t range_max = 0; jack_latency_range_t range; for (unsigned int i = 0; i < m_iChannels; ++i) { if (m_ppOPorts[i] == nullptr) continue; jack_port_get_latency_range(m_ppOPorts[i], JackPlaybackLatency, &range); if (range_max > range.max || i == 0) range_max = range.max; } iLatencyOut += range_max; #else jack_nframes_t lat, lat_max = 0; for (unsigned int i = 0; i < m_iChannels; ++i) { if (m_ppOPorts[i] == nullptr) continue; lat = jack_port_get_latency(m_ppOPorts[i]); if (lat_max > lat || i == 0) lat_max = lat; } iLatencyOut += lat_max; #endif const unsigned int iBufferSize = pAudioEngine->bufferSize(); if (iLatencyOut < iBufferSize) iLatencyOut += iBufferSize; return iLatencyOut; } // Create plugin-list properly. qtractorPluginList *qtractorAudioBus::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 qtractorAudioBus::updatePluginListName ( qtractorPluginList *pPluginList, int iFlags ) const { pPluginList->setName((iFlags & qtractorPluginList::In ? QObject::tr("%1 In") : QObject::tr("%1 Out")).arg(busName())); } // Update plugin-list name/buffers properly. void qtractorAudioBus::updatePluginList ( qtractorPluginList *pPluginList, int iFlags ) { // Set plugin-list title name... updatePluginListName(pPluginList, iFlags); // Set plugin-list buffer alright... pPluginList->setChannels(m_iChannels, iFlags); } // Retrieve all current JACK connections for a given bus mode interface; // return the effective number of connection attempts... int qtractorAudioBus::updateConnects ( qtractorBus::BusMode busMode, ConnectList& connects, bool bConnect ) const { // Modes must match, at least... if ((busMode & qtractorAudioBus::busMode()) == 0) return 0; if (bConnect && connects.isEmpty()) return 0; qtractorAudioEngine *pAudioEngine = static_cast (engine()); if (pAudioEngine == nullptr) return 0; jack_client_t *pJackClient = pAudioEngine->jackClient(); if (pJackClient == nullptr) return 0; // Which kind of ports? jack_port_t **ppPorts = (busMode == qtractorBus::Input ? m_ppIPorts : m_ppOPorts); if (ppPorts == nullptr) return 0; // For each channel... ConnectItem item; for (item.index = 0; item.index < m_iChannels; ++item.index) { // Get port connections... const char **ppszClientPorts = jack_port_get_all_connections(pJackClient, ppPorts[item.index]); if (ppszClientPorts) { // Now, for each port... int iClientPort = 0; while (ppszClientPorts[iClientPort]) { // Check if already in list/connected... const QString sClientPort = QString::fromUtf8(ppszClientPorts[iClientPort]); item.clientName = sClientPort.section(':', 0, 0); item.portName = sClientPort.section(':', 1, 1); ConnectItem *pItem = connects.findItem(item); if (pItem && bConnect) { const int iItem = connects.indexOf(pItem); if (iItem >= 0) { connects.removeAt(iItem); delete pItem; } } else if (!bConnect) connects.append(new ConnectItem(item)); ++iClientPort; } ::free(ppszClientPorts); } } // Shall we proceed for actual connections? if (!bConnect) return 0; // Our client:port prefix template... QString sClientPort = pAudioEngine->clientName() + ':'; sClientPort += busName() + '/'; sClientPort += (busMode == qtractorBus::Input ? "in" : "out"); sClientPort += "_%1"; QString sOutputPort; QString sInputPort; // For each (remaining) connection, try... int iUpdate = 0; QListIterator iter(connects); while (iter.hasNext()) { ConnectItem *pItem = iter.next(); // Mangle which is output and input... if (busMode == qtractorBus::Input) { sOutputPort = pItem->clientName + ':' + pItem->portName; sInputPort = sClientPort.arg(pItem->index + 1); } else { sOutputPort = sClientPort.arg(pItem->index + 1); sInputPort = pItem->clientName + ':' + pItem->portName; } #ifdef CONFIG_DEBUG qDebug("qtractorAudioBus[%p]::updateConnects(%d): " "jack_connect: [%s] => [%s]", this, (int) busMode, sOutputPort.toUtf8().constData(), sInputPort.toUtf8().constData()); #endif // Do it... if (jack_connect(pJackClient, sOutputPort.toUtf8().constData(), sInputPort.toUtf8().constData()) == 0) { const int iItem = connects.indexOf(pItem); if (iItem >= 0) { connects.removeAt(iItem); delete pItem; ++iUpdate; } } } // Done. return iUpdate; } // Document element methods. bool qtractorAudioBus::loadElement ( qtractorDocument *pDocument, QDomElement *pElement ) { for (QDomNode nProp = pElement->firstChild(); !nProp.isNull(); nProp = nProp.nextSibling()) { // Convert audio-bus property to element... QDomElement eProp = nProp.toElement(); if (eProp.isNull()) continue; if (eProp.tagName() == "pass-through" || // Legacy compat. eProp.tagName() == "audio-thru" || eProp.tagName() == "monitor") { qtractorAudioBus::setMonitor( qtractorDocument::boolFromText(eProp.text())); } else if (eProp.tagName() == "channels") { qtractorAudioBus::setChannels(eProp.text().toUShort()); } else if (eProp.tagName() == "auto-connect") { qtractorAudioBus::setAutoConnect( qtractorDocument::boolFromText(eProp.text())); } else if (eProp.tagName() == "input-gain") { if (qtractorAudioBus::monitor_in()) qtractorAudioBus::monitor_in()->setGain( eProp.text().toFloat()); } else if (eProp.tagName() == "input-panning") { if (qtractorAudioBus::monitor_in()) qtractorAudioBus::monitor_in()->setPanning( eProp.text().toFloat()); } else if (eProp.tagName() == "input-controllers") { qtractorAudioBus::loadControllers(&eProp, qtractorBus::Input); } else if (eProp.tagName() == "input-plugins") { if (qtractorAudioBus::pluginList_in()) qtractorAudioBus::pluginList_in()->loadElement( pDocument, &eProp); } else if (eProp.tagName() == "input-connects") { qtractorAudioBus::loadConnects( qtractorAudioBus::inputs(), pDocument, &eProp); } else if (eProp.tagName() == "output-gain") { if (qtractorAudioBus::monitor_out()) qtractorAudioBus::monitor_out()->setGain( eProp.text().toFloat()); } else if (eProp.tagName() == "output-panning") { if (qtractorAudioBus::monitor_out()) qtractorAudioBus::monitor_out()->setPanning( eProp.text().toFloat()); } else if (eProp.tagName() == "output-controllers") { qtractorAudioBus::loadControllers(&eProp, qtractorBus::Output); } else if (eProp.tagName() == "output-plugins") { if (qtractorAudioBus::pluginList_out()) qtractorAudioBus::pluginList_out()->loadElement( pDocument, &eProp); } else if (eProp.tagName() == "output-connects") { qtractorAudioBus::loadConnects( qtractorAudioBus::outputs(), pDocument, &eProp); } } return true; } bool qtractorAudioBus::saveElement ( qtractorDocument *pDocument, QDomElement *pElement ) const { const qtractorBus::BusMode busMode = qtractorAudioBus::busMode(); pElement->setAttribute("name", qtractorAudioBus::busName()); pElement->setAttribute("mode", qtractorBus::textFromBusMode(busMode)); pDocument->saveTextElement("monitor", qtractorDocument::textFromBool( qtractorAudioBus::isMonitor()), pElement); pDocument->saveTextElement("channels", QString::number(qtractorAudioBus::channels()), pElement); pDocument->saveTextElement("auto-connect", qtractorDocument::textFromBool( qtractorAudioBus::isAutoConnect()), pElement); if (busMode & qtractorBus::Input) { if (qtractorAudioBus::monitor_in()) { pDocument->saveTextElement("input-gain", QString::number(qtractorAudioBus::monitor_in()->gain()), pElement); pDocument->saveTextElement("input-panning", QString::number(qtractorAudioBus::monitor_in()->panning()), pElement); } // Save input bus controllers... QDomElement eInputControllers = pDocument->document()->createElement("input-controllers"); qtractorAudioBus::saveControllers(pDocument, &eInputControllers, qtractorBus::Input); pElement->appendChild(eInputControllers); // Save input bus plugins... if (qtractorAudioBus::pluginList_in()) { QDomElement eInputPlugins = pDocument->document()->createElement("input-plugins"); qtractorAudioBus::pluginList_in()->saveElement( pDocument, &eInputPlugins); pElement->appendChild(eInputPlugins); } // Save input bus connections... QDomElement eAudioInputs = pDocument->document()->createElement("input-connects"); qtractorBus::ConnectList inputs; qtractorAudioBus::updateConnects(qtractorBus::Input, inputs); qtractorAudioBus::saveConnects(inputs, pDocument, &eAudioInputs); pElement->appendChild(eAudioInputs); } if (busMode & qtractorBus::Output) { if (qtractorAudioBus::monitor_out()) { pDocument->saveTextElement("output-gain", QString::number(qtractorAudioBus::monitor_out()->gain()), pElement); pDocument->saveTextElement("output-panning", QString::number(qtractorAudioBus::monitor_out()->panning()), pElement); } // Save output bus controllers... QDomElement eOutputControllers = pDocument->document()->createElement("output-controllers"); qtractorAudioBus::saveControllers(pDocument, &eOutputControllers, qtractorBus::Output); pElement->appendChild(eOutputControllers); // Save output bus plugins... if (qtractorAudioBus::pluginList_out()) { QDomElement eOutputPlugins = pDocument->document()->createElement("output-plugins"); qtractorAudioBus::pluginList_out()->saveElement( pDocument, &eOutputPlugins); pElement->appendChild(eOutputPlugins); } // Save output bus connections... QDomElement eAudioOutputs = pDocument->document()->createElement("output-connects"); qtractorBus::ConnectList outputs; qtractorAudioBus::updateConnects(qtractorBus::Output, outputs); qtractorAudioBus::saveConnects(outputs, pDocument, &eAudioOutputs); pElement->appendChild(eAudioOutputs); } return true; } // Update all aux-sends to this very bus... // void qtractorAudioBus::updateAudioAuxSends ( const QString& sAudioBusName ) { if ((busMode() & qtractorBus::Output) == 0) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); if (pAudioEngine == nullptr) return; // Make it to all audio output buses... for (qtractorBus *pBus = pAudioEngine->buses().first(); pBus; pBus = pBus->next()) { qtractorAudioBus *pAudioBus = static_cast (pBus); if (pAudioBus == this) continue; if ((pAudioBus->busMode() & qtractorBus::Output) == 0) continue; qtractorPluginList *pPluginList = pAudioBus->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 == channels > 0 => Audio aux-send. qtractorAudioAuxSendPlugin *pAudioAuxSendPlugin = static_cast (pPlugin); if (pAudioAuxSendPlugin && pAudioAuxSendPlugin->audioBus() == this) pAudioAuxSendPlugin->setAudioBusName(sAudioBusName); } } } // Make it to ALL audio tracks... for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { 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 == channels > 0 => Audio aux-send. qtractorAudioAuxSendPlugin *pAudioAuxSendPlugin = static_cast (pPlugin); if (pAudioAuxSendPlugin && pAudioAuxSendPlugin->audioBus() == this) pAudioAuxSendPlugin->setAudioBusName(sAudioBusName); } } } } // end of qtractorAudioEngine.cpp qtractor-1.5.9/src/PaxHeaders/qtractorShortcutForm.cpp0000644000000000000000000000013215101070305020145 xustar0030 mtime=1761898693.088267658 30 atime=1761898693.088267658 30 ctime=1761898693.088267658 qtractor-1.5.9/src/qtractorShortcutForm.cpp0000644000175000001440000005354515101070305020151 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.9/src/PaxHeaders/qtractorMeter.h0000644000000000000000000000012715101070305016233 xustar0029 mtime=1761898693.07626762 29 atime=1761898693.07626762 29 ctime=1761898693.07626762 qtractor-1.5.9/src/qtractorMeter.h0000644000175000001440000001420015101070305016214 0ustar00rncbcusers// qtractorMeter.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 __qtractorMeter_h #define __qtractorMeter_h #include // Forward declarations. class qtractorMeter; class qtractorMonitor; class qtractorSubject; class qtractorObserverSlider; class qtractorObserverSpinBox; class qtractorMidiControlObserver; class qtractorDocument; class QDomElement; class QHBoxLayout; class QVBoxLayout; class QPaintEvent; class QResizeEvent; //---------------------------------------------------------------------------- // qtractorMeterScale -- Meter bridge scale widget. class qtractorMeterScale : public QFrame { public: // Constructor. qtractorMeterScale(qtractorMeter *pMeter); // Meter accessor. qtractorMeter *meter() const; protected: // Specific event handlers. void paintEvent(QPaintEvent *); // Draw IEC scale line and label. void drawLineLabel(QPainter *p, int y, const QString& sLabel); // Actual scale drawing method. virtual void paintScale(QPainter *p) = 0; private: // Local instance variables. qtractorMeter *m_pMeter; // Running variables. int m_iLastY; }; //---------------------------------------------------------------------------- // qtractorMeterValue -- Meter bridge value widget. class qtractorMeterValue : public QWidget { public: // Constructor. qtractorMeterValue(qtractorMeter *pMeter); // Default destructor. virtual ~qtractorMeterValue(); // Meter bridge accessor. qtractorMeter *meter() const { return m_pMeter; } // Value refreshment. virtual void refresh(unsigned long iStamp) = 0; // Global refreshment/update. static void refreshAll(); static void updateAll(); private: // Local instance variables. qtractorMeter *m_pMeter; // List of meter-values (global obviously) static QList g_values; static unsigned long g_iStamp; }; //---------------------------------------------------------------------------- // qtractorMeter -- Meter bridge slot widget. class qtractorMeter : public QWidget { public: // Constructor. qtractorMeter(QWidget *pParent = nullptr); // Default destructor. virtual ~qtractorMeter(); // Dynamic layout accessors. QHBoxLayout *boxLayout() const { return m_pBoxLayout; } // Monitor accessors. virtual void setMonitor(qtractorMonitor *pMonitor) = 0; virtual qtractorMonitor *monitor() const = 0; // Meter reset. virtual void reset() = 0; // For faster scaling when drawing... int scale(float fValue) const { return int(m_fScale * fValue); } // Peak falloff mode setting. void setPeakFalloff(int iPeakFalloff) { m_iPeakFalloff = iPeakFalloff; } int peakFalloff() const { return m_iPeakFalloff; } protected: // Scale accessor. void setScale(float fScale) { m_fScale = fScale; } private: // Local instance variables. QHBoxLayout *m_pBoxLayout; // Meter width/height scale. float m_fScale; // Peak falloff mode setting (0=no peak falloff). int m_iPeakFalloff; }; //---------------------------------------------------------------------------- // qtractorMixerMeter -- Mixer-strip meter bridge widget. class qtractorMixerMeter : public QWidget { Q_OBJECT public: // Constructor. qtractorMixerMeter(QWidget *pParent = nullptr); // Default destructor. virtual ~qtractorMixerMeter(); // Dynamic layout accessors. QWidget *topWidget() const { return m_pTopWidget; } QHBoxLayout *topLayout() const { return m_pTopLayout; } QHBoxLayout *boxLayout() const { return m_pBoxLayout; } // Common slider/spin-box accessors. qtractorObserverSlider *panSlider() const { return m_pPanSlider; } qtractorObserverSpinBox *panSpinBox() const { return m_pPanSpinBox; } qtractorObserverSlider *gainSlider() const { return m_pGainSlider; } qtractorObserverSpinBox *gainSpinBox() const { return m_pGainSpinBox; } // Panning subject accessors. void setPanningSubject(qtractorSubject *pSubject); qtractorSubject *panningSubject() const; // Panning accessors. void setPanning(float fPanning); float panning() const; float prevPanning() const; // Gain subject accessors. void setGainSubject(qtractorSubject *pSubject); qtractorSubject *gainSubject() const; // Gain accessors. void setGain(float fGain); float gain() const; float prevGain() const; // Monitor accessors. virtual void setMonitor(qtractorMonitor *pMonitor) = 0; virtual qtractorMonitor *monitor() const = 0; // Local slider update methods. virtual void updatePanning() = 0; virtual void updateGain() = 0; // Meter reset. virtual void reset() = 0; // MIDI controller/observer attachment (context menu) activator. void addMidiControlAction( QWidget *pWidget, qtractorMidiControlObserver *pObserver); protected slots: // MIDI controller/observer attachment (context menu) slot. void midiControlActionSlot(); void midiControlMenuSlot(const QPoint& pos); private: // Local forward declarations. class PanSliderInterface; // Local instance variables. QWidget *m_pTopWidget; QHBoxLayout *m_pTopLayout; QHBoxLayout *m_pBoxLayout; qtractorObserverSlider *m_pPanSlider; qtractorObserverSpinBox *m_pPanSpinBox; qtractorObserverSlider *m_pGainSlider; qtractorObserverSpinBox *m_pGainSpinBox; class PanObserver; class GainObserver; PanObserver *m_pPanObserver; GainObserver *m_pGainObserver; }; #endif // __qtractorMeter_h // end of qtractorMeter.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiFile.h0000644000000000000000000000013215101070305016635 xustar0030 mtime=1761898693.082267639 30 atime=1761898693.082267639 30 ctime=1761898693.082267639 qtractor-1.5.9/src/qtractorMidiFile.h0000644000175000001440000000761615101070305016637 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.9/src/PaxHeaders/qtractor_plugin_scan.cpp0000644000000000000000000000013215101070305020147 xustar0030 mtime=1761898693.093267673 30 atime=1761898693.093267673 30 ctime=1761898693.093267673 qtractor-1.5.9/src/qtractor_plugin_scan.cpp0000644000175000001440000013120315101070305020137 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.9/src/PaxHeaders/qtractorCommand.h0000644000000000000000000000013215101070305016531 xustar0030 mtime=1761898693.067267591 30 atime=1761898693.067267591 30 ctime=1761898693.067267591 qtractor-1.5.9/src/qtractorCommand.h0000644000175000001440000000776215101070305016535 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.9/src/PaxHeaders/qtractorTempoAdjustForm.ui0000644000000000000000000000013215101070305020424 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorTempoAdjustForm.ui0000644000175000001440000002464415101070305020426 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. qtractorTempoAdjustForm 0 0 480 240 Qt::StrongFocus Tempo Adjust 8 8 Metronome 60 0 &Tempo: TempoSpinBox 100 10 Tempo/Time signature Qt::Horizontal QSizePolicy::Minimum 8 20 T&ap Qt::Horizontal QSizePolicy::Minimum 8 20 &Detect R&eset Qt::Vertical 20 0 Range 60 0 &Start: RangeStartSpinBox 120 0 Range start 60 0 &Length: RangeLengthSpinBox 120 0 Range length 60 0 &Beats: RangeLengthSpinBox Range beats 1 65536 A&djust Qt::Vertical 20 0 Format Time display format Frames Time BBT Qt::Vertical 20 20 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok qtractorTimeSpinBox QSpinBox
qtractorSpinBox.h
qtractorTempoSpinBox QSpinBox
qtractorSpinBox.h
TempoSpinBox TempoTapPushButton TempoDetectPushButton TempoResetPushButton RangeStartSpinBox RangeLengthSpinBox RangeBeatsSpinBox AdjustPushButton FormatComboBox DialogButtonBox
qtractor-1.5.9/src/PaxHeaders/qtractorMidiThumbView.h0000644000000000000000000000013215101070305017670 xustar0030 mtime=1761898693.084267645 30 atime=1761898693.084267645 30 ctime=1761898693.084267645 qtractor-1.5.9/src/qtractorMidiThumbView.h0000644000175000001440000000534015101070305017662 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.9/src/PaxHeaders/qtractorTrack.cpp0000644000000000000000000000013215101070305016552 xustar0030 mtime=1761898693.090267664 30 atime=1761898693.090267664 30 ctime=1761898693.090267664 qtractor-1.5.9/src/qtractorTrack.cpp0000644000175000001440000021447715101070305016561 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.9/src/PaxHeaders/qtractorAudioVorbisFile.cpp0000644000000000000000000000013215101070305020534 xustar0030 mtime=1761898693.064267582 30 atime=1761898693.064267582 30 ctime=1761898693.064267582 qtractor-1.5.9/src/qtractorAudioVorbisFile.cpp0000644000175000001440000002436415101070305020535 0ustar00rncbcusers// qtractorAudioVorbisFile.cpp // /**************************************************************************** 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. *****************************************************************************/ #include "qtractorAbout.h" #include "qtractorAudioVorbisFile.h" #ifdef CONFIG_LIBVORBIS // libvorbis encoder API. #include #endif #include #include #include //---------------------------------------------------------------------- // class qtractorAudioVorbisFile -- Buffered audio file implementation. // // Constructor. qtractorAudioVorbisFile::qtractorAudioVorbisFile ( unsigned short iChannels, unsigned int iSampleRate, unsigned int iBufferSize, int iQuality ) { // Initialize state variables. m_iMode = qtractorAudioVorbisFile::None; m_pFile = nullptr; // Estimate channel and sample-rate (for encoder purposes only). m_iChannels = iChannels; m_iSampleRate = iSampleRate; m_iFrames = 0; #ifdef CONFIG_LIBVORBIS m_ovinfo = nullptr; m_ovsect = 0; #endif // CONFIG_LIBVORBIS // Estimate buffer size. m_iBufferSize = (4096 << 1); // Adjust size the next nearest power-of-two. while (m_iBufferSize < iBufferSize) m_iBufferSize <<= 1; // Encoding quality (write-only). m_iQuality = iQuality; } // Destructor. qtractorAudioVorbisFile::~qtractorAudioVorbisFile (void) { close(); } // Open method. bool qtractorAudioVorbisFile::open ( const QString& sFilename, int iMode ) { #ifdef DEBUG_0 qDebug("qtractorAudioVorbisFile::open(\"%s\", %d)", sFilename.toUtf8().constData(), iMode); #endif close(); // As said, need a minimum of specification for write mode. if (iMode == Write && (m_iChannels == 0 || m_iSampleRate == 0)) return false; // Whether for Read or Write... const char *pszMode; switch (iMode) { case Read: pszMode = "rb"; break; case Write: pszMode = "wb"; break; default: return false; } // Now open it. QByteArray aFilename = sFilename.toUtf8(); m_pFile = ::fopen(aFilename.constData(), pszMode); if (m_pFile == nullptr) return false; #ifdef CONFIG_LIBVORBIS // Now's time for the vorbis stuff... switch (iMode) { case Read: { // Open the Ogg Vorbis file for decoding... if (::ov_open(m_pFile, &m_ovfile, nullptr, 0) < 0) { close(); return false; } // Grab the vorbis file info... m_ovinfo = ::ov_info(&m_ovfile, -1); m_ovsect = 0; break; } case Write: { // Init the Ogg Vorbis structs for encoding... m_ovinfo = new vorbis_info; vorbis_info_init(m_ovinfo); // Using a VBR quality mode: // Quality=0.1 (lowest quality, smallest file) // Quality=1.0 (highest quality, largest file). if (vorbis_encode_init_vbr(m_ovinfo, (long) m_iChannels, (long) m_iSampleRate, 0.1f * float(m_iQuality)) != 0) { close(); return false; } // Add a comments vorbis_comment_init(&m_ovcomment); vorbis_comment_add_tag(&m_ovcomment, (char *) "ENCODER", (char *) "qtractorAudioVorbisFile"); // Set up the analysis state and auxiliary encoding storage. vorbis_analysis_init(&m_ovdsp, m_ovinfo); vorbis_block_init(&m_ovdsp, &m_ovblock); // Set up our packet->stream encoder; // pick a random serial number; that way we can more // likely build chained streams just by concatenation... ::srand(::time(nullptr)); ogg_stream_init(&m_ovstate, ::rand()); // Vorbis streams begin with three headers; the initial header // (with most of the codec setup parameters) which is mandated // by the ogg bitstream spec; the second header holds any comment // fields; the third header holds the bitstream codebook. // We merely need to make the headers, then pass them to // libvorbis one at a time; libvorbis handles the additional // ogg bitstream constraints. ogg_packet ovhcodec; ogg_packet ovhcomment; ogg_packet ovhcodebook; vorbis_analysis_headerout(&m_ovdsp, &m_ovcomment, &ovhcodec, &ovhcomment, &ovhcodebook); // Automatically placed in its own page... ogg_stream_packetin(&m_ovstate, &ovhcodec); ogg_stream_packetin(&m_ovstate, &ovhcomment); ogg_stream_packetin(&m_ovstate, &ovhcodebook); // This ensures the actual audio data will // start on a new page, as per spec... ogg_page ovpage; while (ogg_stream_flush(&m_ovstate, &ovpage)) { fwrite(ovpage.header, 1, ovpage.header_len, m_pFile); fwrite(ovpage.body, 1, ovpage.body_len, m_pFile); } // Done on write/encoder overhead. break; } } // Set open mode (deterministically). m_iMode = iMode; return true; #else // CONFIG_LIBVORBIS return false; #endif } // Read method. int qtractorAudioVorbisFile::read ( float **ppFrames, unsigned int iFrames ) { #ifdef DEBUG_0 qDebug("qtractorAudioVorbisFile::read(%p, %d)", ppFrames, iFrames); #endif int nread = 0; #ifdef CONFIG_LIBVORBIS float **ppBuffer; nread = ::ov_read_float(&m_ovfile, &ppBuffer, iFrames, &m_ovsect); if (nread < 0) // HACK: Just in case things get go thru... nread = ::ov_read_float(&m_ovfile, &ppBuffer, iFrames, &m_ovsect); if (nread > 0) { unsigned short i; for (i = 0; i < (unsigned short) m_ovinfo->channels; ++i) { ::memcpy(ppFrames[i], ppBuffer[i], nread * sizeof(float)); } } #endif // CONFIG_LIBVORBIS return nread; } // Write method. int qtractorAudioVorbisFile::write ( float **ppFrames, unsigned int iFrames ) { #ifdef DEBUG_0 qDebug("qtractorAudioVorbisFile::write(%p, %d)", ppFrames, iFrames); #endif int nwrite = 0; #ifdef CONFIG_LIBVORBIS // Expose the buffer to submit data... float **ppBuffer = vorbis_analysis_buffer(&m_ovdsp, iFrames); // Uninterleaved samples... for (unsigned short i = 0; i < m_iChannels; ++i) ::memcpy(ppBuffer[i], ppFrames[i], iFrames * sizeof(float)); // Tell the library how much we actually submitted... vorbis_analysis_wrote(&m_ovdsp, iFrames); // Flush this block... flush(); // We've wrote it all, hopefully... m_iFrames += iFrames; nwrite = iFrames; #endif // CONFIG_LIBVORBIS return nwrite; } // Flush encoder buffers... void qtractorAudioVorbisFile::flush ( bool fEos ) { #ifdef CONFIG_LIBVORBIS // End of file: this can be done implicitly in the mainline, // but it's easier to see here in non-clever fashion; // tell the library we're at end of stream so that it can handle // the last frame and mark end of stream in the output properly... if (fEos) vorbis_analysis_wrote(&m_ovdsp, 0); // Vorbis does some data preanalysis, then divides up blocks // for more involved (potentially parallel) processing. // Get a single block for encoding now... ogg_packet ovpacket; // raw packet of data for decode. ogg_page ovpage; // ogg bitstream page; vorbis packets are inside. while (vorbis_analysis_blockout(&m_ovdsp, &m_ovblock) == 1) { // Analysis, assume we want to use bitrate management... vorbis_analysis(&m_ovblock, nullptr); vorbis_bitrate_addblock(&m_ovblock); // Probably we should do this also as left-over processing... while (vorbis_bitrate_flushpacket(&m_ovdsp, &ovpacket)) { // Weld the packet into the bitstream... ogg_stream_packetin(&m_ovstate, &ovpacket); // Write out pages (if any)... while (ogg_stream_pageout(&m_ovstate, &ovpage)) { fwrite(ovpage.header, 1, ovpage.header_len, m_pFile); fwrite(ovpage.body, 1, ovpage.body_len, m_pFile); if (ogg_page_eos(&ovpage)) break; } } } #endif // CONFIG_LIBVORBIS } // Seek method. bool qtractorAudioVorbisFile::seek ( unsigned long iOffset ) { #ifdef DEBUG_0 qDebug("qtractorAudioVorbisFile::seek(%d)", iOffset); #endif #ifdef CONFIG_LIBVORBIS return (::ov_pcm_seek(&m_ovfile, iOffset) == 0); #else return false; #endif } // Close method. void qtractorAudioVorbisFile::close (void) { #ifdef DEBUG_0 qDebug("qtractorAudioVorbisFile::close()"); #endif if (m_pFile) { #ifdef CONFIG_LIBVORBIS // Reinitialize libvorbis stuff... switch (m_iMode) { case Read: ::ov_clear(&m_ovfile); // ::fclose(m_pFile); -- already closed on ov_clear? break; case Write: flush(true); ogg_stream_clear(&m_ovstate); vorbis_block_clear(&m_ovblock); vorbis_dsp_clear(&m_ovdsp); vorbis_comment_clear(&m_ovcomment); vorbis_info_clear(m_ovinfo); delete m_ovinfo; ::fclose(m_pFile); break; } #else ::fclose(m_pFile); #endif m_pFile = nullptr; } // Reset all other state relevant variables. #ifdef CONFIG_LIBVORBIS m_ovsect = 0; ::memset(&m_ovfile, 0, sizeof(m_ovfile)); ::memset(&m_ovstate, 0, sizeof(m_ovstate)); ::memset(&m_ovblock, 0, sizeof(m_ovblock)); ::memset(&m_ovdsp, 0, sizeof(m_ovdsp)); ::memset(&m_ovcomment, 0, sizeof(m_ovcomment)); m_ovinfo = nullptr; #endif m_iMode = qtractorAudioVorbisFile::None; m_iFrames = 0; } // Open mode accessor. int qtractorAudioVorbisFile::mode (void) const { return m_iMode; } // Open channel(s) accessor. unsigned short qtractorAudioVorbisFile::channels (void) const { #ifdef CONFIG_LIBVORBIS return (m_ovinfo ? m_ovinfo->channels : 0); #else return 0; #endif } // Estimated number of frames specialty (aprox. 8secs). unsigned long qtractorAudioVorbisFile::frames (void) const { #ifdef CONFIG_LIBVORBIS if (m_iMode == Read) return ::ov_pcm_total((OggVorbis_File *) &m_ovfile, -1); #endif return m_iFrames; } // Sample rate specialty. unsigned int qtractorAudioVorbisFile::sampleRate (void) const { #ifdef CONFIG_LIBVORBIS return (m_ovinfo ? m_ovinfo->rate : 0); #else return 0; #endif } // Translate quality index into vorbis encoder specific. (static) int qtractorAudioVorbisFile::quality ( int iQuality ) { return (iQuality >= 0 && 10 >= iQuality ? iQuality : 4); } // end of qtractorAudioVorbisFile.cpp qtractor-1.5.9/src/PaxHeaders/qtractorTrackCommand.h0000644000000000000000000000013215101070305017516 xustar0030 mtime=1761898693.090267664 30 atime=1761898693.090267664 30 ctime=1761898693.090267664 qtractor-1.5.9/src/qtractorTrackCommand.h0000644000175000001440000002201315101070305017504 0ustar00rncbcusers// qtractorTrackCommand.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 __qtractorTrackCommand_h #define __qtractorTrackCommand_h #include "qtractorPropertyCommand.h" #include "qtractorSession.h" #include // Forward declarations. class qtractorClipCommand; class qtractorCurveList; class qtractorCurve; class qtractorSubject; //---------------------------------------------------------------------- // class qtractorTrackCommand - declaration. // class qtractorTrackCommand : public qtractorCommand { public: // Constructor. qtractorTrackCommand(const QString& sName, qtractorTrack *pTrack, qtractorTrack *pAfterTrack = nullptr); // Destructor. virtual ~qtractorTrackCommand(); // Track accessor. qtractorTrack *track() const { return m_pTrack; } // After/previous track accessor. qtractorTrack *afterTrack() const { return m_pAfterTrack; } protected: // Extra track list item. struct TrackItem { // Constructor. TrackItem(qtractorTrack *pTrack, bool bOn) : track(pTrack), on(bOn) {} // Item members. qtractorTrack *track; bool on; }; // Track command methods. bool addTrack(); bool removeTrack(); private: // Instance variables. qtractorTrack *m_pTrack; qtractorTrack *m_pAfterTrack; }; //---------------------------------------------------------------------- // class qtractorAddTrackCommand - declaration. // class qtractorAddTrackCommand : public qtractorTrackCommand { public: // Constructor. qtractorAddTrackCommand(qtractorTrack *pTrack, qtractorTrack *pAfterTrack = nullptr); // Track insertion command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorRemoveTrackCommand - declaration. // class qtractorRemoveTrackCommand : public qtractorTrackCommand { public: // Constructor. qtractorRemoveTrackCommand(qtractorTrack *pTrack); // Track-removal command methods. bool redo(); bool undo(); }; //---------------------------------------------------------------------- // class qtractorCopyTrackCommand - declaration. // class qtractorCopyTrackCommand : public qtractorTrackCommand { public: // Constructor. qtractorCopyTrackCommand(qtractorTrack *pTrack, qtractorTrack *pAfterTrack); // Track-removal command methods. bool redo(); bool undo(); protected: // Clone an existing automation/curve. qtractorCurve *cloneCurve(qtractorCurveList *pNewCurveList, qtractorSubject *pNewSubject, qtractorCurve *pCurve) const; private: // Instance variables. int m_iCopyCount; }; //---------------------------------------------------------------------- // class qtractorMoveTrackCommand - declaration. // class qtractorMoveTrackCommand : public qtractorTrackCommand { public: // Constructor. qtractorMoveTrackCommand(qtractorTrack *pTrack, qtractorTrack *pNextTrack); // Track-move command methods. bool redo(); bool undo(); private: // Instance variables. qtractorTrack *m_pNextTrack; }; //---------------------------------------------------------------------- // class qtractorResizeTrackCommand - declaration. // class qtractorResizeTrackCommand : public qtractorTrackCommand { public: // Constructor. qtractorResizeTrackCommand(qtractorTrack *pTrack, int iZoomHeight); // Track-move command methods. bool redo(); bool undo(); private: // Instance variables. int m_iZoomHeight; }; //---------------------------------------------------------------------- // class qtractorInportTracksCommand - declaration. // class qtractorImportTrackCommand : public qtractorCommand { public: // Constructor. qtractorImportTrackCommand(qtractorTrack *pAfterTrack); // Destructor. ~qtractorImportTrackCommand(); // Add track to command list. void addTrack(qtractorTrack *pTrack); // Track-import command methods. bool redo(); bool undo(); private: // Instance variables. qtractorTrack *m_pAfterTrack; QList m_trackCommands; // Session properties backup stuff. qtractorSession::Properties m_sessionProps; qtractorPropertyCommand *m_pSaveCommand; int m_iSaveCount; }; //---------------------------------------------------------------------- // class qtractorEditTrackCommand - declaration. // class qtractorEditTrackCommand : public qtractorPropertyCommand { public: // Constructor. qtractorEditTrackCommand(qtractorTrack *pTrack, const qtractorTrack::Properties& props); // Overridden track-edit command methods. bool redo(); bool undo(); private: // Instance variables. qtractorTrack *m_pTrack; bool m_bReopen; bool m_bLatency; }; //---------------------------------------------------------------------- // class qtractorTrackControlCommand - declaration. // class qtractorTrackControlCommand : public qtractorTrackCommand { public: // Constructor. qtractorTrackControlCommand(const QString& sName, qtractorTrack *pTrack, bool bMidiControl = false); protected: // Primitive control predicates. bool midiControlFeedback(); private: // Instance variables. bool m_bMidiControl; int m_iMidiControlFeedback; }; //---------------------------------------------------------------------- // class qtractorTrackStateCommand - declaration. // class qtractorTrackStateCommand : public qtractorTrackControlCommand { public: // Constructor. qtractorTrackStateCommand(qtractorTrack *pTrack, qtractorTrack::ToolType toolType, bool bOn, bool bMidiControl = false); // Destructor. ~qtractorTrackStateCommand(); // Track-button command methods. bool redo(); bool undo(); private: // Instance variables. qtractorTrack::ToolType m_toolType; bool m_bOn; // Extra track list. QList m_tracks; // Special sub-command needed to track recording clips. qtractorClipCommand *m_pClipCommand; int m_iRecordCount; }; //---------------------------------------------------------------------- // class qtractorTrackMonitorCommand - declaration. // class qtractorTrackMonitorCommand : public qtractorTrackControlCommand { public: // Constructor. qtractorTrackMonitorCommand(qtractorTrack *pTrack, bool bMonitor, bool bMidiControl = false); // Destructor. ~qtractorTrackMonitorCommand(); // Track-monitoring command methods. bool redo(); bool undo() { return redo(); } private: // Instance variables. bool m_bMonitor; // Extra track list. QList m_tracks; }; //---------------------------------------------------------------------- // class qtractorTrackGainCommand - declaration. // class qtractorTrackGainCommand : public qtractorTrackControlCommand { public: // Constructor. qtractorTrackGainCommand(qtractorTrack *pTrack, float fGain, bool bMidiControl = false); // Track-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 qtractorTrackPanningCommand - declaration. // class qtractorTrackPanningCommand : public qtractorTrackControlCommand { public: // Constructor. qtractorTrackPanningCommand(qtractorTrack *pTrack, float fPanning, bool bMidiControl = false); // Track-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; }; //---------------------------------------------------------------------- // class qtractorTrackInstrumentCommand - declaration. // class qtractorTrackInstrumentCommand : public qtractorTrackCommand { public: // Constructor. qtractorTrackInstrumentCommand(qtractorTrack *pTrack, const QString& sInstrumentName, int iBank, int iProg); // Track-instrument patch command methods. bool redo(); bool undo() { return redo(); } private: // Instance variables. QString m_sInstrumentName; int m_iBank; int m_iProg; }; #endif // __qtractorTrackCommand_h // end of qtractorTrackCommand.h qtractor-1.5.9/src/PaxHeaders/qtractorAudioIOMatrixForm.h0000644000000000000000000000013215101070305020455 xustar0030 mtime=1761898693.063267578 30 atime=1761898693.063267578 30 ctime=1761898693.063267578 qtractor-1.5.9/src/qtractorAudioIOMatrixForm.h0000644000175000001440000000521315101070305020446 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.9/src/PaxHeaders/qtractorMidiFileTempo.cpp0000644000000000000000000000013215101070305020175 xustar0030 mtime=1761898693.082267639 30 atime=1761898693.082267639 30 ctime=1761898693.082267639 qtractor-1.5.9/src/qtractorMidiFileTempo.cpp0000644000175000001440000002032215101070305020164 0ustar00rncbcusers// qtractorMidiFileTempo.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 "qtractorMidiFileTempo.h" #include "qtractorMidiFile.h" #include "qtractorTimeScale.h" //---------------------------------------------------------------------- // class qtractorMidiFileTempo -- MIDI tempo/time-signature map class. // // (Re)initializer method. void qtractorMidiFileTempo::clear (void) { m_nodes.setAutoDelete(true); m_markers.setAutoDelete(true); // Clear/reset tempo-map and location markers... m_nodes.clear(); m_markers.clear(); // There must always be one node, always. addNode(0); } // Update node coefficient divisor factors. void qtractorMidiFileTempo::Node::update ( qtractorMidiFile *pMidiFile ) { ticksPerBeat = pMidiFile->ticksPerBeat(); if (beatDivisor > 2) { ticksPerBeat >>= (beatDivisor - 2); } else if (beatDivisor < 2) { ticksPerBeat <<= (2 - beatDivisor); } } // Update tempo-map node position metrics. void qtractorMidiFileTempo::Node::reset ( qtractorMidiFileTempo::Node *pNode ) { if (bar > pNode->bar) tick = pNode->tickFromBar(bar); else bar = pNode->barFromTick(tick); } // Tempo-map node seeker (by tick). qtractorMidiFileTempo::Node *qtractorMidiFileTempo::seekNode ( unsigned long iTick ) const { Node *pNode = m_nodes.first(); // Seek tick forward... while (pNode && pNode->next() && iTick >= (pNode->next())->tick) pNode = pNode->next(); return pNode; } // Node list specifics. qtractorMidiFileTempo::Node *qtractorMidiFileTempo::addNode ( unsigned long iTick, float fTempo, unsigned short iBeatsPerBar, unsigned short iBeatDivisor ) { Node *pNode = 0; // Seek for the nearest preceding node... Node *pPrev = seekNode(iTick); // Snap to nearest bar... if (pPrev) { iTick = pPrev->tickSnapToBar(iTick); pPrev = seekNode(iTick); } // Either update existing node or add new one... Node *pNext = (pPrev ? pPrev->next() : 0); if (pPrev && pPrev->tick == iTick) { // Update exact matching node... pNode = pPrev; pNode->tempo = fTempo; pNode->beatsPerBar = iBeatsPerBar; pNode->beatDivisor = iBeatDivisor; } else if (pPrev && pPrev->tempo == fTempo && pPrev->beatsPerBar == iBeatsPerBar && pPrev->beatDivisor == iBeatDivisor) { // No need for a new node... return pPrev; } else if (pNext && pNext->tempo == fTempo && pNext->beatsPerBar == iBeatsPerBar && pNext->beatDivisor == iBeatDivisor) { // Update next exact matching node... pNode = pNext; pNode->tick = iTick; pNode->bar = 0; } else { // Add/insert a new node... pNode = new Node(iTick, fTempo, iBeatsPerBar, iBeatDivisor); if (pPrev) m_nodes.insertAfter(pNode, pPrev); else m_nodes.append(pNode); } // Update coefficients and positioning thereafter... updateNode(pNode); return pNode; } void qtractorMidiFileTempo::updateNode ( qtractorMidiFileTempo::Node *pNode ) { // Update coefficients... pNode->update(m_pMidiFile); // Update positioning on all nodes thereafter... Node *pPrev = pNode->prev(); while (pNode) { if (pPrev) pNode->reset(pPrev); pPrev = pNode; pNode = pNode->next(); } } void qtractorMidiFileTempo::removeNode ( qtractorMidiFileTempo::Node *pNode ) { // Don't ever remove the very first node... Node *pPrev = pNode->prev(); if (pPrev == 0) return; // Update positioning on all nodes thereafter... 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); } // Location marker seeker (by tick). qtractorMidiFileTempo::Marker *qtractorMidiFileTempo::seekMarker ( unsigned long iTick ) const { Marker *pMarker = m_markers.first(); // Seek tick forward... while (pMarker && pMarker->next() && iTick >= (pMarker->next())->tick) pMarker = pMarker->next(); return pMarker; } // Marker list specifics. qtractorMidiFileTempo::Marker *qtractorMidiFileTempo::addMarker ( unsigned long iTick, const QString& sText, int iAccidentals, int iMode ) { Marker *pMarker = 0; // Snap to nearest bar... Node *pNodePrev = seekNode(iTick); if (pNodePrev) iTick = pNodePrev->tickSnapToBar(iTick); // Seek for the nearest preceding node... Marker *pMarkerPrev = seekMarker(iTick); // Either update existing marker or add new one... if (pMarkerPrev && pMarkerPrev->tick == iTick) { // Update exact matching node... pMarker = pMarkerPrev; pMarker->text = sText; pMarker->accidentals = iAccidentals; pMarker->mode = iMode; } else { // Add/insert a new marker... pMarker = new Marker(iTick, sText, iAccidentals, iMode); if (pMarkerPrev) m_markers.insertAfter(pMarker, pMarkerPrev); else m_markers.append(pMarker); } return pMarker; } void qtractorMidiFileTempo::removeMarker ( qtractorMidiFileTempo::Marker *pMarker ) { // Actually remove/unlink the marker m_markers.remove(pMarker); } // Time-scale sync methods. void qtractorMidiFileTempo::fromTimeScale ( qtractorTimeScale *pTimeScale, unsigned long iTimeOffset ) { if (pTimeScale == nullptr) return; const unsigned short p = m_pMidiFile->ticksPerBeat(); const unsigned short q = pTimeScale->ticksPerBeat(); if (q < 1) return; // Copy tempo-map nodes... m_nodes.clear(); qtractorTimeScale::Cursor cursor(pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekTick(iTimeOffset); while (pNode) { unsigned long iTime = uint64_t(pNode->tick) * p / q; iTime = (iTime > iTimeOffset ? iTime - iTimeOffset : 0); addNode(iTime, pNode->tempo, pNode->beatsPerBar, pNode->beatDivisor); pNode = pNode->next(); } // Copy location markers... m_markers.clear(); qtractorTimeScale::Marker *pMarker = pTimeScale->markers().seekTick(iTimeOffset); while (pMarker) { const unsigned long iTick = pTimeScale->tickFromFrame(pMarker->frame); unsigned long iTime = uint64_t(iTick) * p / q; iTime = (iTime > iTimeOffset ? iTime - iTimeOffset : 0); addMarker(iTime, pMarker->text, pMarker->accidentals, pMarker->mode); pMarker = pMarker->next(); } } void qtractorMidiFileTempo::intoTimeScale ( qtractorTimeScale *pTimeScale, unsigned long iTimeOffset ) { if (pTimeScale == nullptr) return; const unsigned short p = pTimeScale->ticksPerBeat(); const unsigned short q = m_pMidiFile->ticksPerBeat(); if (q < 1) return; pTimeScale->reset(); // Copy tempo-map nodes... qtractorMidiFileTempo::Node *pNode = m_nodes.first(); // Very first node is kinda special... if (pNode) { pTimeScale->setTempo(pNode->tempo); pTimeScale->setBeatsPerBar(pNode->beatsPerBar); pTimeScale->setBeatDivisor(pNode->beatDivisor); pNode = pNode->next(); } // Now for all the rest... while (pNode) { const unsigned long iTime = uint64_t(pNode->tick) * p / q; pTimeScale->addNode( pTimeScale->frameFromTick(iTime + iTimeOffset), pNode->tempo, 2, pNode->beatsPerBar, pNode->beatDivisor); pNode = pNode->next(); } // Copy location markers... qtractorMidiFileTempo::Marker *pMarker = m_markers.first(); while (pMarker) { const unsigned long iTime = uint64_t(pMarker->tick) * p / q; if (!pMarker->text.isEmpty()) { pTimeScale->addMarker( pTimeScale->frameFromTick(iTime + iTimeOffset), pMarker->text); } if (qtractorTimeScale::isKeySignature(pMarker->accidentals, pMarker->mode)) { pTimeScale->addKeySignature( pTimeScale->frameFromTick(iTime + iTimeOffset), pMarker->accidentals, pMarker->mode); } pMarker = pMarker->next(); } } // end of qtractorMidiFileTempo.cpp qtractor-1.5.9/src/PaxHeaders/qtractorTimeScaleCommand.h0000644000000000000000000000013215101070305020320 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorTimeScaleCommand.h0000644000175000001440000002533615101070305020321 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.9/src/PaxHeaders/qtractorMidiClip.h0000644000000000000000000000013215101070305016645 xustar0030 mtime=1761898693.077267623 30 atime=1761898693.077267623 30 ctime=1761898693.077267623 qtractor-1.5.9/src/qtractorMidiClip.h0000644000175000001440000002742715101070305016651 0ustar00rncbcusers// qtractorMidiClip.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 __qtractorMidiClip_h #define __qtractorMidiClip_h #include "qtractorClip.h" #include "qtractorMidiCursor.h" #include "qtractorMidiFile.h" #include "qtractorCommand.h" #include #include // Forward declarations. class qtractorMidiEditorForm; class qtractorMidiEditCommand; //---------------------------------------------------------------------- // class qtractorMidiClip -- MIDI file/sequence clip. // class qtractorMidiClip : public qtractorClip { public: // Constructor. qtractorMidiClip(qtractorTrack *pTrack); // Copy constructor. qtractorMidiClip(const qtractorMidiClip& clip); // Destructor. ~qtractorMidiClip(); // Clip (re)open method. void open(); // Brand new clip contents new method. bool createMidiFile(const QString& sFilename, int iTrackChannel = 0); // The main use method. bool openMidiFile(const QString& sFilename, int iTrackChannel = 0, int iMode = qtractorMidiFile::Read); // MIDI file properties accessors. void setTrackChannel(unsigned short iTrackChannel) { m_iTrackChannel = iTrackChannel; } unsigned short trackChannel() const { return m_iTrackChannel; } unsigned short format() const { return (m_pData ? m_pData->format() : g_iDefaultFormat); } // (Meta)Session flag accessors. void setSessionFlag(bool bSessionFlag) { m_bSessionFlag = bSessionFlag; } bool isSessionFlag() const { return m_bSessionFlag; } // Revisionist accessors. void setRevision(unsigned short iRevision) { m_iRevision = iRevision; } unsigned short revision() const { return m_iRevision; } // Revisionist method. QString createFilePathRevision(bool bForce = false); // Sequence properties accessor. qtractorMidiSequence *sequence() const { return (m_pData ? m_pData->sequence() : nullptr); } unsigned short channel() const { return (m_pData ? m_pData->channel() : 0); } int bankSelMethod() const { return (m_pData ? m_pData->bankSelMethod() : -1); } int bank() const { return (m_pData ? m_pData->bank() : -1); } int prog() const { return (m_pData ? m_pData->prog() : -1); } // Local command-list (undo/redo) accessor. qtractorCommandList *commands() const { return (m_pData ? m_pData->commands() : nullptr); } // Intra-clip frame positioning. void seek(unsigned long iFrame); // Reset clip state. void reset(bool bLooping); // Loop positioning. void setLoop(unsigned long iLoopStart, unsigned long iLoopEnd); // Clip close-commit (record specific) void close(); // MIDI clip special process cycle executive. void process(unsigned long iFrameStart, unsigned long iFrameEnd); // MIDI clip freewheeling process cycle executive (needed for export). void process_export(unsigned long iFrameStart, unsigned long iFrameEnd); // Clip paint method. void draw(QPainter *pPainter, const QRect& clipRect, unsigned long iClipOffset); // Clip update method (rolling stats). void update(); // Clip editor methods. bool startEditor(QWidget *pParent = nullptr); void updateEditor(bool bSelectClear); void updateEditorContents(); void updateEditorTimeScale(); bool queryEditor(); // MIDI clip tool-tip. QString toolTip() const; // Auto-save to (possible) new file revision. bool saveCopyFile(const QString& sFilename, bool bUpdate); // MIDI clip export method. typedef void (*ClipExport)(qtractorMidiSequence *, void *); bool clipExport(ClipExport pfnClipExport, void *pvArg, unsigned long iOffset = 0, unsigned long iLength = 0) const; // Default MIDI file format accessors // (specific to capture/recording) static void setDefaultFormat(unsigned short iFormat); static unsigned short defaultFormat(); // MIDI file hash key. class FileKey; typedef QHash FileHash; // Most interesting key/data (ref-counted?)... class Key; class Data { public: // Constructor. Data(unsigned short iFormat) : m_iFormat(iFormat), m_pSeq(new qtractorMidiSequence()), m_pCommands(new qtractorCommandList()) {} // Destructor. ~Data() { clear(); delete m_pCommands; delete m_pSeq; } // Originial format accessor. unsigned short format() const { return m_iFormat; } // Sequence accessor. qtractorMidiSequence *sequence() const { return m_pSeq; } // Commands (undo/redo) accessor. qtractorCommandList *commands() const { return m_pCommands; } // Sequence properties accessors. unsigned short channel() const { return m_pSeq->channel(); } int bankSelMethod() const { return m_pSeq->bankSelMethod(); } int bank() const { return m_pSeq->bank(); } int prog() const { return m_pSeq->prog(); } // Ref-counting related methods. void attach(qtractorMidiClip *pMidiClip) { m_clips.append(pMidiClip); } void detach(qtractorMidiClip *pMidiClip) { m_clips.removeAll(pMidiClip); } unsigned short count() const { return m_clips.count(); } const QList& clips() const { return m_clips; } void clear() { m_clips.clear(); } private: // Interesting variables. unsigned short m_iFormat; qtractorMidiSequence *m_pSeq; qtractorCommandList *m_pCommands; // Ref-counting related stuff. QList m_clips; }; typedef QHash Hash; // Sync all ref-counted filenames. void setFilenameEx(const QString& sFilename, bool bUpdate); // Sync all ref-counted clip-lengths. void setClipLengthEx(unsigned long iClipLength); // Sync all ref-counted clip editors. void updateEditorEx(bool bSelectClear); // Sync all ref-counted clip-dirtyness. void setDirtyEx(bool bDirty); // Manage local hash key. void insertHashKey(); void updateHashKey(); void removeHashKey(); // Un/relink (de/clone) local hash data. void unlinkHashData(); void relinkHashData(); // Whether local hash is being shared. bool isHashLinked() const; // Check whether a MIDI clip is hash-linked to another. bool isLinkedClip (qtractorMidiClip *pMidiClip) const; // Get all hash-linked clips (including self). QList linkedClips() const; // Make sure the clip hash-table gets reset. static void clearHashTable(); // MIDI clip editor position/size accessors. void setEditorPos(const QPoint& pos) { m_posEditor = pos; } const QPoint& editorPos() const { return m_posEditor; } void setEditorSize(const QSize& size) { m_sizeEditor = size; } const QSize& editorSize() const { return m_sizeEditor; } // MIDI clip editor zoom ratio accessors. void setEditorHorizontalZoom(unsigned short iHorizontalZoom) { m_iEditorHorizontalZoom = iHorizontalZoom; } unsigned short editorHorizontalZoom() const { return m_iEditorHorizontalZoom; } void setEditorVerticalZoom(unsigned short iVerticalZoom) { m_iEditorVerticalZoom = iVerticalZoom; } unsigned short editorVerticalZoom() const { return m_iEditorVerticalZoom; } // MIDI clip editor splitter sizes accessors. void setEditorHorizontalSizes(const QList& sizes) { m_editorHorizontalSizes = sizes; } QList editorHorizontalSizes() const { return m_editorHorizontalSizes; } void setEditorVerticalSizes(const QList& sizes) { m_editorVerticalSizes = sizes; } QList editorVerticalSizes() const { return m_editorVerticalSizes; } // MIDI clip editor view drum-mode accessors (tree-state). void setEditorDrumMode(int iEditorDrumMode) { m_iEditorDrumMode = iEditorDrumMode; } int editorDrumMode() const { return m_iEditorDrumMode; } // Ghost track setting. void setGhostTrackName(const QString& sGhostTrackName) { m_sGhostTrackName = sGhostTrackName; } const QString& ghostTrackName() const { return m_sGhostTrackName; } // Secondary time signature (numerator) void setBeatsPerBar2(unsigned short iBeatsPerBar2) { m_iBeatsPerBar2 = iBeatsPerBar2; } unsigned short beatsPerBar2() const { return m_iBeatsPerBar2; } // Secondary time signature (denominator) void setBeatDivisor2(unsigned short iBeatDivisor2) { m_iBeatDivisor2 = iBeatDivisor2; } unsigned short beatDivisor2() const { return m_iBeatDivisor2; } // Step-input methods... void setStepInputHead(unsigned long iStepInputHead); unsigned long stepInputHead() const { return m_iStepInputHead; } unsigned long stepInputTail() const { return m_iStepInputTail; } unsigned long stepInputHeadTime() const { return m_iStepInputHeadTime; } unsigned long stepInputTailTime() const { return m_iStepInputTailTime; } void setStepInputLast(unsigned long iStepInputLast); unsigned long stepInputLast() const { return m_iStepInputLast; } // Step-input advance... void advanceStepInput(); // Step-input editor update... void updateStepInput(); // Check if an event is already in the sequence... qtractorMidiEvent *findStepInputEvent( qtractorMidiEvent *pStepInputEvent) const; // Step-input/overdub recording control methods... bool processInpEvents(const QList& events, bool bOverdub); void clearInpEvents(); protected: // Virtual document element methods. bool loadClipElement(qtractorDocument *pDocument, QDomElement *pElement); bool saveClipElement(qtractorDocument *pDocument, QDomElement *pElement); // Private cleanup. void closeMidiFile(); // MIDI clip freewheeling event enqueue method (needed for export). void enqueue_export(qtractorTrack *pTrack, qtractorMidiEvent *pEvent, unsigned long iTime, float fGain) const; private: // Instance variables. qtractorMidiFile *m_pFile; unsigned short m_iTrackChannel; bool m_bSessionFlag; // Revisionist count. unsigned short m_iRevision; // Most interesting key/data (ref-counted?)... Key *m_pKey; Data *m_pData; static Hash g_hashTable; // MIDI file hash key. static FileHash g_hashFiles; // To optimize and keep track of current playback // position, mostly like an sequence cursor/iterator. qtractorMidiCursor m_playCursor; qtractorMidiCursor m_drawCursor; // This clip editor form widget. qtractorMidiEditorForm *m_pMidiEditorForm; // And for geometry it was last seen... QPoint m_posEditor; QSize m_sizeEditor; // MIDI clip editor zoom ratio accessors. unsigned short m_iEditorHorizontalZoom; unsigned short m_iEditorVerticalZoom; // MIDI clip editor splitter sizes accessors. QList m_editorHorizontalSizes; QList m_editorVerticalSizes; // MIDI clip editor view drum-mode (tree-state). int m_iEditorDrumMode; // Ghost track setting. QString m_sGhostTrackName; // Secondary time signature (numerator/denuminator) unsigned short m_iBeatsPerBar2; unsigned short m_iBeatDivisor2; // Current step-input/capture range (in ticks) unsigned long m_iStepInputHead; unsigned long m_iStepInputTail; unsigned long m_iStepInputHeadTime; unsigned long m_iStepInputTailTime; unsigned long m_iStepInputLast; // Step-input/overdub recording control variables... qtractorMidiEditCommand *m_pInpEventsCommand; unsigned int m_iInpEventsCommand; bool m_bInpEventsOverdub; // Default MIDI file format (for capture/record) static unsigned short g_iDefaultFormat; }; #endif // __qtractorMidiClip_h // end of qtractorMidiClip.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiEditorForm.cpp0000644000000000000000000000013215101070305020363 xustar0030 mtime=1761898693.081267635 30 atime=1761898693.080267632 30 ctime=1761898693.081267635 qtractor-1.5.9/src/qtractorMidiEditorForm.cpp0000644000175000001440000026260115101070305020362 0ustar00rncbcusers// qtractorMidiEditorForm.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 "qtractorMidiEditorForm.h" #include "qtractorMidiEditView.h" #include "qtractorMidiEditList.h" #include "qtractorMidiEditEvent.h" #include "qtractorMidiControlTypeGroup.h" #include "qtractorMidiClip.h" #include "qtractorMidiEngine.h" #include "qtractorMidiMonitor.h" #include "qtractorOptions.h" #include "qtractorSession.h" #include "qtractorTracks.h" #include "qtractorTrackView.h" #include "qtractorConnections.h" #include "qtractorMainForm.h" #include "qtractorShortcutForm.h" #include "qtractorPasteRepeatForm.h" #include "qtractorClipForm.h" #include "qtractorTimeScale.h" #include "qtractorCommand.h" #include "qtractorMidiThumbView.h" #include "qtractorMidiEventList.h" #include "qtractorInstrumentMenu.h" #include "qtractorFileList.h" #include "qtractorClipCommand.h" #include "qtractorTimeScaleCommand.h" #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) #include #endif #include #include #include #include #include #include #include #include #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) #define horizontalAdvance width #endif //------------------------------------------------------------------------- // qtractorMidiEditorForm -- Main window form implementation. // Constructor. qtractorMidiEditorForm::qtractorMidiEditorForm ( QWidget *pParent, Qt::WindowFlags wflags ) : QMainWindow(pParent, wflags) { // Setup UI struct... m_ui.setupUi(this); #if QT_VERSION < QT_VERSION_CHECK(6, 1, 0) QMainWindow::setWindowIcon(QIcon(":/images/qtractor.png")); #endif m_iDirtyCount = 0; // Set our central widget. m_pMidiEditor = new qtractorMidiEditor(this); setCentralWidget(m_pMidiEditor); // Toolbar thumbnail view... m_ui.thumbViewToolbar->addWidget(m_pMidiEditor->thumbView()); m_ui.thumbViewToolbar->setAllowedAreas( Qt::TopToolBarArea | Qt::BottomToolBarArea); // Dockable widgets time. m_pMidiEventList = new qtractorMidiEventList(this); m_pMidiEventList->setEditor(m_pMidiEditor); m_pMidiEventList->hide(); // Initially hidden. // Custom track/instrument proxy menu. m_pInstrumentMenu = new qtractorInstrumentMenu(this); // Set edit-mode action group up... m_pEditModeActionGroup = new QActionGroup(this); m_pEditModeActionGroup->setExclusive(true); m_pEditModeActionGroup->addAction(m_ui.editModeOnAction); m_pEditModeActionGroup->addAction(m_ui.editModeOffAction); m_pEditModeActionGroup->addAction(m_ui.editModeDrawAction); // And the corresponding tool-button drop-down menu... m_pEditModeToolButton = new QToolButton(this); m_pEditModeToolButton->setPopupMode(QToolButton::InstantPopup); m_pEditModeToolButton->setMenu(m_ui.editModeMenu); // Add/insert this on its proper place in the edit-toobar... m_ui.editToolbar->addSeparator(); m_ui.editToolbar->addWidget(m_pEditModeToolButton); QObject::connect( m_pEditModeActionGroup, SIGNAL(triggered(QAction*)), m_pEditModeToolButton, SLOT(setDefaultAction(QAction*))); // Editable toolbar widgets special palette. QPalette pal; // Outrageous HACK: GTK+ ppl won't see green on black thing... #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #if !defined(QT_NO_STYLE_GTK) if (qobject_cast (style()) == nullptr) { #endif #endif // pal.setColor(QPalette::Window, Qt::black); pal.setColor(QPalette::Base, Qt::black); pal.setColor(QPalette::Text, Qt::green); // pal.setColor(QPalette::Button, Qt::darkGray); // pal.setColor(QPalette::ButtonText, Qt::green); #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) #if !defined(QT_NO_STYLE_GTK) } #endif #endif // Transport tempo/time-signature tracker. m_pTempoCursor = new qtractorTempoCursor(); const QSize pad(4, 0); const QFont& font0 = qtractorMidiEditorForm::font(); const QFont font(font0.family(), font0.pointSize() + 2); const QFontMetrics fm(font); const int d = fm.height() + fm.leading() + 8; // Transport time. const QString sTime("+99:99:99.999"); m_pTimeSpinBox = new qtractorTimeSpinBox(m_ui.timeToolbar); m_pTimeSpinBox->setTimeScale(m_pMidiEditor->timeScale()); m_pTimeSpinBox->setFont(font); m_pTimeSpinBox->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_pTimeSpinBox->setMinimumSize(QSize(fm.horizontalAdvance(sTime) + d, d) + pad); m_pTimeSpinBox->setPalette(pal); // m_pTimeSpinBox->setAutoFillBackground(true); m_pTimeSpinBox->setToolTip(tr("Current time (play-head)")); // m_pTimeSpinBox->setContextMenuPolicy(Qt::CustomContextMenu); m_ui.timeToolbar->addWidget(m_pTimeSpinBox); // m_ui.timeToolbar->addSeparator(); // Time-signature spin-box. const QString sTempo("+999 9/9"); m_pTempoSpinBox = new qtractorTempoSpinBox(m_ui.timeToolbar); // m_pTempoSpinBox->setFont(font); m_pTempoSpinBox->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); m_pTempoSpinBox->setMinimumSize(QSize(fm.horizontalAdvance(sTempo) + d, d) + pad); m_pTempoSpinBox->setPalette(pal); // m_pTempoSpinBox->setAutoFillBackground(true); m_pTempoSpinBox->setToolTip(tr("Current tempo (BPM)")); m_pTempoSpinBox->setContextMenuPolicy(Qt::CustomContextMenu); m_ui.timeToolbar->addWidget(m_pTempoSpinBox); // m_ui.timeToolbar->addSeparator(); m_pTimeSig2ResetButton = new QToolButton(m_ui.timeToolbar); const int h1 = m_pTempoSpinBox->sizeHint().height(); m_pTimeSig2ResetButton->setMaximumSize(h1, h1); m_pTimeSig2ResetButton->setIcon(QIcon::fromTheme("itemReset")); m_pTimeSig2ResetButton->setToolTip(tr("Reset time-sig.")); m_ui.timeToolbar->addWidget(m_pTimeSig2ResetButton); // Snap-per-beat combo-box. m_pSnapPerBeatComboBox = new QComboBox(m_ui.viewToolbar); m_pSnapPerBeatComboBox->setEditable(false); // Event type selection widgets... m_pViewTypeComboBox = new QComboBox(m_ui.editViewToolbar); m_pViewTypeComboBox->setEditable(false); m_pEventTypeComboBox = new QComboBox(m_ui.editEventToolbar); m_pEventTypeComboBox->setEditable(false); m_pEventParamComboBox = new QComboBox(m_ui.editEventToolbar); // m_pEventParamComboBox->setEditable(false); m_pEventParamComboBox->setMinimumWidth(220); m_pEventTypeGroup = new qtractorMidiControlTypeGroup( m_pMidiEditor, m_pEventTypeComboBox, m_pEventParamComboBox); // Snap-to-scale/quantize selection widgets... m_pSnapToScaleKeyComboBox = new QComboBox(m_ui.snapToScaleToolbar); m_pSnapToScaleKeyComboBox->setEditable(false); m_pSnapToScaleKeyComboBox->setMinimumWidth(86); m_pSnapToScaleTypeComboBox = new QComboBox(m_ui.snapToScaleToolbar); m_pSnapToScaleKeyComboBox->setEditable(false); // View/Snap-to-beat actions initialization... int iSnap = 0; const QIcon& snapIcon = QIcon::fromTheme("itemBeat"); const QString sSnapObjectName("viewSnapPerBeat%1"); const QString sSnapStatusTip(tr("Set current snap to %1")); const QStringList& snapItems = qtractorTimeScale::snapItems(); QStringListIterator snapIter(snapItems); while (snapIter.hasNext()) { const QString& sSnapText = snapIter.next(); QAction *pAction = new QAction(sSnapText, this); pAction->setObjectName(sSnapObjectName.arg(iSnap)); pAction->setStatusTip(sSnapStatusTip.arg(sSnapText)); pAction->setCheckable(true); // pAction->setIcon(snapIcon); pAction->setData(iSnap++); QObject::connect(pAction, SIGNAL(triggered(bool)), SLOT(viewSnap())); m_snapPerBeatActions.append(pAction); m_ui.viewSnapMenu->addAction(pAction); } // m_ui.viewSnapMenu->addSeparator(); m_ui.viewSnapMenu->addAction(m_ui.viewSnapZebraAction); m_ui.viewSnapMenu->addAction(m_ui.viewSnapGridAction); // Pre-fill the combo-boxes... m_pSnapPerBeatComboBox->setIconSize(QSize(8, 16)); snapIter.toFront(); if (snapIter.hasNext()) m_pSnapPerBeatComboBox->addItem( QIcon::fromTheme("itemNone"), snapIter.next()); while (snapIter.hasNext()) m_pSnapPerBeatComboBox->addItem(snapIcon, snapIter.next()); // m_pSnapPerBeatComboBox->insertItems(0, snapItems); const QIcon& icon = QIcon::fromTheme("itemProperty"); m_pViewTypeComboBox->addItem(icon, qtractorMidiControl::nameFromType(qtractorMidiEvent::NOTEON), int(qtractorMidiEvent::NOTEON)); m_pViewTypeComboBox->addItem(icon, qtractorMidiControl::nameFromType(qtractorMidiEvent::KEYPRESS), int(qtractorMidiEvent::KEYPRESS)); // Special control event names and stuff. m_pEventTypeComboBox->setItemText(0, tr("Note Velocity")); // NOTEON m_pEventTypeComboBox->removeItem(1); // NOTEOFF // Snap-to-scale/quantize selection widgets... QStringListIterator iter(qtractorMidiEditor::scaleKeyNames()); while (iter.hasNext()) m_pSnapToScaleKeyComboBox->addItem(icon, iter.next()); m_pSnapToScaleTypeComboBox->insertItems(0, qtractorMidiEditor::scaleTypeNames()); // updateInstrumentNames(); // Set combo-boxes tooltips... m_pSnapPerBeatComboBox->setToolTip(tr("Snap/beat")); m_pViewTypeComboBox->setToolTip(tr("Note type")); m_pEventTypeComboBox->setToolTip(tr("Value type")); m_pEventParamComboBox->setToolTip(tr("Parameter type")); m_pSnapToScaleKeyComboBox->setToolTip(tr("Scale key")); m_pSnapToScaleTypeComboBox->setToolTip(tr("Scale type")); // Late view/note type menu... const QString sViewTypeObjectName("viewNoteType%1"); const QString sViewTypeStatusTip("Set note type to %1"); const int iViewTypeCount = m_pViewTypeComboBox->count(); for (int iIndex = 0; iIndex < iViewTypeCount; ++iIndex) { const QString& sViewTypeText = m_pViewTypeComboBox->itemText(iIndex); QAction *pAction = new QAction(sViewTypeText, this); pAction->setObjectName(sViewTypeObjectName.arg(iIndex)); pAction->setStatusTip(sViewTypeStatusTip.arg(sViewTypeText)); pAction->setCheckable(true); pAction->setData(iIndex); QObject::connect(pAction, SIGNAL(triggered(bool)), SLOT(viewNoteType())); m_ui.viewNoteTypeMenu->addAction(pAction); } // Late event/type type menu... const QString sEventTypeObjectName("viewValueType%1"); const QString sEventTypeStatusTip("Set value type to %1"); const int iEventTypeCount = m_pEventTypeComboBox->count(); for (int iIndex = 0; iIndex < iEventTypeCount; ++iIndex) { const QString& sEventTypeText = m_pEventTypeComboBox->itemText(iIndex); QAction *pAction = new QAction(sEventTypeText, this); pAction->setObjectName(sEventTypeObjectName.arg(iIndex)); pAction->setStatusTip(sEventTypeStatusTip.arg(sEventTypeText)); pAction->setCheckable(true); pAction->setData(iIndex); QObject::connect(pAction, SIGNAL(triggered(bool)), SLOT(viewValueType())); m_ui.viewValueTypeMenu->addAction(pAction); } // Add combo-boxes to toolbars... m_ui.viewToolbar->addSeparator(); m_ui.viewToolbar->addWidget(m_pSnapPerBeatComboBox); m_ui.editViewToolbar->addWidget(m_pViewTypeComboBox); m_ui.editEventToolbar->addWidget(m_pEventTypeComboBox); m_ui.editEventToolbar->addSeparator(); m_ui.editEventToolbar->addWidget(m_pEventParamComboBox); m_ui.snapToScaleToolbar->addWidget(m_pSnapToScaleKeyComboBox); m_ui.snapToScaleToolbar->addSeparator(); m_ui.snapToScaleToolbar->addWidget(m_pSnapToScaleTypeComboBox); QStatusBar *pStatusBar = statusBar(); const QString spc(4, ' '); // Status clip/sequence name... m_pTrackNameLabel = new QLabel(spc); m_pTrackNameLabel->setAlignment(Qt::AlignLeft); m_pTrackNameLabel->setMinimumWidth(60); m_pTrackNameLabel->setToolTip(tr("MIDI clip name")); m_pTrackNameLabel->setAutoFillBackground(true); pStatusBar->addWidget(m_pTrackNameLabel, 1); // Status filename... m_pFileNameLabel = new QLabel(spc); m_pFileNameLabel->setAlignment(Qt::AlignLeft); m_pFileNameLabel->setMinimumWidth(240); m_pFileNameLabel->setToolTip(tr("MIDI file name")); m_pFileNameLabel->setAutoFillBackground(true); pStatusBar->addWidget(m_pFileNameLabel, 2); // Status track/channel number... m_pTrackChannelLabel = new QLabel(spc); m_pTrackChannelLabel->setAlignment(Qt::AlignHCenter); m_pTrackChannelLabel->setMinimumWidth(60); m_pTrackChannelLabel->setToolTip(tr("MIDI track/channel")); m_pTrackChannelLabel->setAutoFillBackground(true); pStatusBar->addWidget(m_pTrackChannelLabel); // Clip modification status. m_pStatusModLabel = new QLabel(tr("MOD")); m_pStatusModLabel->setAlignment(Qt::AlignHCenter); m_pStatusModLabel->setMinimumSize(m_pStatusModLabel->sizeHint() + pad); m_pStatusModLabel->setToolTip(tr("MIDI modification state")); m_pStatusModLabel->setAutoFillBackground(true); pStatusBar->addPermanentWidget(m_pStatusModLabel); // Clip recording status. m_pStatusRecLabel = new QLabel(tr("REC")); m_pStatusRecLabel->setAlignment(Qt::AlignHCenter); m_pStatusRecLabel->setMinimumSize(m_pStatusRecLabel->sizeHint() + pad); m_pStatusRecLabel->setToolTip(tr("MIDI clip record state")); m_pStatusRecLabel->setAutoFillBackground(true); pStatusBar->addPermanentWidget(m_pStatusRecLabel); // Clip mute(ness) status. m_pStatusMuteLabel = new QLabel(tr("MUTE")); m_pStatusMuteLabel->setAlignment(Qt::AlignHCenter); m_pStatusMuteLabel->setMinimumSize(m_pStatusMuteLabel->sizeHint() + pad); m_pStatusMuteLabel->setToolTip(tr("MIDI clip mute state")); m_pStatusMuteLabel->setAutoFillBackground(true); pStatusBar->addPermanentWidget(m_pStatusMuteLabel); // Sequence duration status. m_pDurationLabel = new QLabel(tr("00:00:00.000")); m_pDurationLabel->setAlignment(Qt::AlignHCenter); m_pDurationLabel->setMinimumSize(m_pDurationLabel->sizeHint() + pad); m_pDurationLabel->setToolTip(tr("MIDI clip duration")); m_pDurationLabel->setAutoFillBackground(true); pStatusBar->addPermanentWidget(m_pDurationLabel); m_pRedPalette = new QPalette(pStatusBar->palette()); m_pRedPalette->setColor(QPalette::WindowText, Qt::darkRed); m_pRedPalette->setColor(QPalette::Window, Qt::red); m_pYellowPalette = new QPalette(pStatusBar->palette()); m_pYellowPalette->setColor(QPalette::WindowText, Qt::darkYellow); m_pYellowPalette->setColor(QPalette::Window, Qt::yellow); // Some actions surely need those // shortcuts firmly attached... addAction(m_ui.viewMenubarAction); #if 0 // Special integration ones. addAction(m_ui.transportBackwardAction); addAction(m_ui.transportLoopAction); addAction(m_ui.transportLoopSetAction); addAction(m_ui.transportStopAction); addAction(m_ui.transportPlayAction); addAction(m_ui.transportPunchAction); addAction(m_ui.transportPunchSetAction); #endif // Make those primordially docked... addDockWidget(Qt::RightDockWidgetArea, m_pMidiEventList, Qt::Vertical); // Ah, make it stand right. setFocus(); // UI signal/slot connections... QObject::connect(m_ui.fileSaveAction, SIGNAL(triggered(bool)), SLOT(fileSave())); QObject::connect(m_ui.fileSaveAsAction, SIGNAL(triggered(bool)), SLOT(fileSaveAs())); QObject::connect(m_ui.fileMuteAction, SIGNAL(triggered(bool)), SLOT(fileMute())); QObject::connect(m_ui.fileUnlinkAction, SIGNAL(triggered(bool)), SLOT(fileUnlink())); QObject::connect(m_ui.fileRecordExAction, SIGNAL(triggered(bool)), SLOT(fileRecordEx(bool))); QObject::connect(m_ui.filePropertiesAction, SIGNAL(triggered(bool)), SLOT(fileProperties())); QObject::connect(m_ui.fileRangeSetAction, SIGNAL(triggered(bool)), SLOT(fileRangeSet())); QObject::connect(m_ui.fileLoopSetAction, SIGNAL(triggered(bool)), SLOT(fileLoopSet())); QObject::connect(m_ui.fileTrackInputsAction, SIGNAL(triggered(bool)), SLOT(fileTrackInputs())); QObject::connect(m_ui.fileTrackOutputsAction, SIGNAL(triggered(bool)), SLOT(fileTrackOutputs())); QObject::connect(m_ui.fileTrackPropertiesAction, SIGNAL(triggered(bool)), SLOT(fileTrackProperties())); QObject::connect(m_ui.fileCloseAction, SIGNAL(triggered(bool)), SLOT(fileClose())); QObject::connect(m_ui.editUndoAction, SIGNAL(triggered(bool)), SLOT(editUndo())); QObject::connect(m_ui.editRedoAction, SIGNAL(triggered(bool)), SLOT(editRedo())); QObject::connect(m_ui.editCutAction, SIGNAL(triggered(bool)), SLOT(editCut())); QObject::connect(m_ui.editCopyAction, SIGNAL(triggered(bool)), SLOT(editCopy())); QObject::connect(m_ui.editPasteRepeatAction, SIGNAL(triggered(bool)), SLOT(editPasteRepeat())); QObject::connect(m_ui.editPasteAction, SIGNAL(triggered(bool)), SLOT(editPaste())); QObject::connect(m_ui.editDeleteAction, SIGNAL(triggered(bool)), SLOT(editDelete())); QObject::connect(m_ui.editModeOnAction, SIGNAL(triggered(bool)), SLOT(editModeOn(bool))); QObject::connect(m_ui.editModeOffAction, SIGNAL(triggered(bool)), SLOT(editModeOff(bool))); QObject::connect(m_ui.editModeDrawAction, SIGNAL(triggered(bool)), SLOT(editModeDraw(bool))); QObject::connect(m_ui.editSelectAllAction, SIGNAL(triggered(bool)), SLOT(editSelectAll())); QObject::connect(m_ui.editSelectNoneAction, SIGNAL(triggered(bool)), SLOT(editSelectNone())); QObject::connect(m_ui.editSelectInvertAction, SIGNAL(triggered(bool)), SLOT(editSelectInvert())); QObject::connect(m_ui.editSelectRangeAction, SIGNAL(triggered(bool)), SLOT(editSelectRange())); QObject::connect(m_ui.editInsertRangeAction, SIGNAL(triggered(bool)), SLOT(editInsertRange())); QObject::connect(m_ui.editInsertStepAction, SIGNAL(triggered(bool)), SLOT(editInsertStep())); QObject::connect(m_ui.editRemoveRangeAction, SIGNAL(triggered(bool)), SLOT(editRemoveRange())); QObject::connect(m_ui.toolsQuantizeAction, SIGNAL(triggered(bool)), SLOT(toolsQuantize())); QObject::connect(m_ui.toolsTransposeAction, SIGNAL(triggered(bool)), SLOT(toolsTranspose())); QObject::connect(m_ui.toolsNormalizeAction, SIGNAL(triggered(bool)), SLOT(toolsNormalize())); QObject::connect(m_ui.toolsRandomizeAction, SIGNAL(triggered(bool)), SLOT(toolsRandomize())); QObject::connect(m_ui.toolsResizeAction, SIGNAL(triggered(bool)), SLOT(toolsResize())); QObject::connect(m_ui.toolsRescaleAction, SIGNAL(triggered(bool)), SLOT(toolsRescale())); QObject::connect(m_ui.toolsTimeshiftAction, SIGNAL(triggered(bool)), SLOT(toolsTimeshift())); QObject::connect(m_ui.toolsTemporampAction, SIGNAL(triggered(bool)), SLOT(toolsTemporamp())); QObject::connect(m_ui.viewMenubarAction, SIGNAL(triggered(bool)), SLOT(viewMenubar(bool))); QObject::connect(m_ui.viewStatusbarAction, SIGNAL(triggered(bool)), SLOT(viewStatusbar(bool))); QObject::connect(m_ui.viewToolbarFileAction, SIGNAL(triggered(bool)), SLOT(viewToolbarFile(bool))); QObject::connect(m_ui.viewToolbarEditAction, SIGNAL(triggered(bool)), SLOT(viewToolbarEdit(bool))); QObject::connect(m_ui.viewToolbarViewAction, SIGNAL(triggered(bool)), SLOT(viewToolbarView(bool))); QObject::connect(m_ui.viewToolbarTransportAction, SIGNAL(triggered(bool)), SLOT(viewToolbarTransport(bool))); QObject::connect(m_ui.viewToolbarTimeAction, SIGNAL(triggered(bool)), SLOT(viewToolbarTime(bool))); QObject::connect(m_ui.viewToolbarScaleAction, SIGNAL(triggered(bool)), SLOT(viewToolbarScale(bool))); QObject::connect(m_ui.viewToolbarThumbAction, SIGNAL(triggered(bool)), SLOT(viewToolbarThumb(bool))); QObject::connect(m_ui.viewEventsAction, SIGNAL(triggered(bool)), SLOT(viewEvents(bool))); QObject::connect(m_ui.viewNoteNamesAction, SIGNAL(triggered(bool)), SLOT(viewNoteNames(bool))); QObject::connect(m_ui.viewNoteDurationAction, SIGNAL(triggered(bool)), SLOT(viewNoteDuration(bool))); QObject::connect(m_ui.viewDrumModeAction, SIGNAL(triggered(bool)), SLOT(viewDrumMode(bool))); QObject::connect(m_ui.viewNoteColorAction, SIGNAL(triggered(bool)), SLOT(viewNoteColor(bool))); QObject::connect(m_ui.viewValueColorAction, SIGNAL(triggered(bool)), SLOT(viewValueColor(bool))); QObject::connect(m_ui.viewZoomInAction, SIGNAL(triggered(bool)), SLOT(viewZoomIn())); QObject::connect(m_ui.viewZoomOutAction, SIGNAL(triggered(bool)), SLOT(viewZoomOut())); QObject::connect(m_ui.viewZoomResetAction, SIGNAL(triggered(bool)), SLOT(viewZoomReset())); QObject::connect(m_ui.viewZoomHorizontalAction, SIGNAL(triggered(bool)), SLOT(viewZoomHorizontal())); QObject::connect(m_ui.viewZoomVerticalAction, SIGNAL(triggered(bool)), SLOT(viewZoomVertical())); QObject::connect(m_ui.viewZoomAllAction, SIGNAL(triggered(bool)), SLOT(viewZoomAll())); QObject::connect(m_ui.viewSnapZebraAction, SIGNAL(triggered(bool)), SLOT(viewSnapZebra(bool))); QObject::connect(m_ui.viewSnapGridAction, SIGNAL(triggered(bool)), SLOT(viewSnapGrid(bool))); QObject::connect(m_ui.viewToolTipsAction, SIGNAL(triggered(bool)), SLOT(viewToolTips(bool))); QObject::connect(m_ui.viewRefreshAction, SIGNAL(triggered(bool)), SLOT(viewRefresh())); QObject::connect(m_ui.viewPreviewAction, SIGNAL(triggered(bool)), SLOT(viewPreview(bool))); QObject::connect(m_ui.viewFollowAction, SIGNAL(triggered(bool)), SLOT(viewFollow(bool))); QObject::connect(m_ui.helpShortcutsAction, SIGNAL(triggered(bool)), SLOT(helpShortcuts())); QObject::connect(m_ui.helpAboutAction, SIGNAL(triggered(bool)), SLOT(helpAbout())); QObject::connect(m_ui.helpAboutQtAction, SIGNAL(triggered(bool)), SLOT(helpAboutQt())); QObject::connect(m_ui.fileTrackInstrumentMenu, SIGNAL(aboutToShow()), SLOT(updateTrackInstrumentMenu())); QObject::connect(m_ui.viewNoteTypeMenu, SIGNAL(aboutToShow()), SLOT(updateNoteTypeMenu())); QObject::connect(m_ui.viewValueTypeMenu, SIGNAL(aboutToShow()), SLOT(updateValueTypeMenu())); QObject::connect(m_ui.viewGhostTrackMenu, SIGNAL(aboutToShow()), SLOT(updateGhostTrackMenu())); QObject::connect(m_ui.viewZoomMenu, SIGNAL(aboutToShow()), SLOT(updateZoomMenu())); QObject::connect(m_ui.viewSnapMenu, SIGNAL(aboutToShow()), SLOT(updateSnapMenu())); QObject::connect(m_ui.viewScaleMenu, SIGNAL(aboutToShow()), SLOT(updateScaleMenu())); QObject::connect(m_pTimeSpinBox, SIGNAL(displayFormatChanged(int)), SLOT(transportTimeFormatChanged(int))); QObject::connect(m_pTimeSpinBox, SIGNAL(valueChanged(unsigned long)), SLOT(transportTimeChanged(unsigned long))); QObject::connect(m_pTimeSpinBox, SIGNAL(editingFinished()), SLOT(transportTimeFinished())); QObject::connect(m_pTempoSpinBox, SIGNAL(valueChanged(float, unsigned short, unsigned short)), SLOT(transportTempoChanged(float, unsigned short, unsigned short))); QObject::connect(m_pTempoSpinBox, SIGNAL(editingFinished()), SLOT(transportTempoFinished())); QObject::connect(m_pTempoSpinBox, SIGNAL(customContextMenuRequested(const QPoint&)), SLOT(transportTempoContextMenu(const QPoint&))); QObject::connect(m_pTimeSig2ResetButton, SIGNAL(clicked()), SLOT(timeSig2ResetClicked())); QObject::connect(m_pSnapPerBeatComboBox, SIGNAL(activated(int)), SLOT(snapPerBeatChanged(int))); // To handle event selection changes. QObject::connect(m_pViewTypeComboBox, SIGNAL(activated(int)), SLOT(viewTypeChanged(int))); QObject::connect(m_pEventTypeGroup, SIGNAL(controlTypeChanged(int)), SLOT(eventTypeChanged(int))); QObject::connect(m_pEventTypeGroup, SIGNAL(controlParamChanged(int)), SLOT(eventParamChanged(int))); QObject::connect(m_pSnapToScaleKeyComboBox, SIGNAL(activated(int)), SLOT(snapToScaleKeyChanged(int))); QObject::connect(m_pSnapToScaleTypeComboBox, SIGNAL(activated(int)), SLOT(snapToScaleTypeChanged(int))); QObject::connect(m_pMidiEditor, SIGNAL(selectNotifySignal(qtractorMidiEditor *)), SLOT(selectionChanged(qtractorMidiEditor *))); QObject::connect(m_pMidiEditor, SIGNAL(changeNotifySignal(qtractorMidiEditor *)), SLOT(contentsChanged(qtractorMidiEditor *))); QObject::connect(m_pMidiEventList->toggleViewAction(), SIGNAL(triggered(bool)), SLOT(stabilizeForm())); // Try to restore old editor state... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { // Initial decorations toggle state. m_ui.viewMenubarAction->setChecked(pOptions->bMidiMenubar); m_ui.viewStatusbarAction->setChecked(pOptions->bMidiStatusbar); m_ui.viewToolbarFileAction->setChecked(pOptions->bMidiFileToolbar); m_ui.viewToolbarEditAction->setChecked(pOptions->bMidiEditToolbar); m_ui.viewToolbarViewAction->setChecked(pOptions->bMidiViewToolbar); m_ui.viewToolbarTransportAction->setChecked(pOptions->bMidiTransportToolbar); m_ui.viewToolbarTimeAction->setChecked(pOptions->bMidiTimeToolbar); m_ui.viewToolbarScaleAction->setChecked(pOptions->bMidiScaleToolbar); m_ui.viewToolbarThumbAction->setChecked(pOptions->bMidiThumbToolbar); m_ui.viewNoteNamesAction->setChecked(pOptions->bMidiNoteNames); m_ui.viewNoteDurationAction->setChecked(pOptions->bMidiNoteDuration); m_ui.viewNoteColorAction->setChecked(pOptions->bMidiNoteColor); m_ui.viewValueColorAction->setChecked(pOptions->bMidiValueColor); m_ui.viewPreviewAction->setChecked(pOptions->bMidiPreview); m_ui.viewFollowAction->setChecked(pOptions->bMidiFollow); m_ui.viewSnapZebraAction->setChecked(pOptions->bMidiSnapZebra); m_ui.viewSnapGridAction->setChecked(pOptions->bMidiSnapGrid); m_ui.viewToolTipsAction->setChecked(pOptions->bMidiToolTips); if (!pOptions->bMidiEditMode) m_ui.editModeOffAction->setChecked(true); else if (!pOptions->bMidiEditModeDraw) m_ui.editModeOnAction->setChecked(true); else m_ui.editModeDrawAction->setChecked(true); // Set initial edit mode... m_pEditModeToolButton->setDefaultAction( m_pEditModeActionGroup->checkedAction()); // Initial decorations visibility state. viewMenubar(pOptions->bMidiMenubar); viewStatusbar(pOptions->bMidiStatusbar); viewToolbarFile(pOptions->bMidiFileToolbar); viewToolbarEdit(pOptions->bMidiEditToolbar); viewToolbarView(pOptions->bMidiViewToolbar); viewToolbarTransport(pOptions->bMidiTransportToolbar); viewToolbarTime(pOptions->bMidiTimeToolbar); viewToolbarScale(pOptions->bMidiScaleToolbar); viewToolbarThumb(pOptions->bMidiThumbToolbar); m_pMidiEditor->setZoomMode(pOptions->iMidiZoomMode); m_pMidiEditor->setHorizontalZoom(pOptions->iMidiHorizontalZoom); m_pMidiEditor->setVerticalZoom(pOptions->iMidiVerticalZoom); m_pMidiEditor->setSnapZebra(pOptions->bMidiSnapZebra); m_pMidiEditor->setSnapGrid(pOptions->bMidiSnapGrid); m_pMidiEditor->setToolTips(pOptions->bMidiToolTips); m_pMidiEditor->setEditMode(pOptions->bMidiEditMode); m_pMidiEditor->setEditModeDraw(pOptions->bMidiEditModeDraw); m_pMidiEditor->setNoteColor(pOptions->bMidiNoteColor); m_pMidiEditor->setValueColor(pOptions->bMidiValueColor); m_pMidiEditor->setNoteNames(pOptions->bMidiNoteNames); m_pMidiEditor->setNoteDuration(pOptions->bMidiNoteDuration); m_pMidiEditor->setSendNotes(pOptions->bMidiPreview); m_pMidiEditor->setSyncView(pOptions->bMidiFollow); m_pMidiEditor->setSnapToScaleKey(pOptions->iMidiSnapToScaleKey); m_pMidiEditor->setSnapToScaleType(pOptions->iMidiSnapToScaleType); // Initial transport display options... m_pMidiEditor->setSyncViewHold(pOptions->bSyncViewHold); // Default snap-per-beat setting... m_pSnapPerBeatComboBox->setCurrentIndex(pOptions->iMidiSnapPerBeat); // Default snap-to-scale settings... m_pSnapToScaleKeyComboBox->setCurrentIndex(pOptions->iMidiSnapToScaleKey); m_pSnapToScaleTypeComboBox->setCurrentIndex(pOptions->iMidiSnapToScaleType); // Restore whole dock windows state. QByteArray aDockables = pOptions->settings().value( "/MidiEditor/Layout/DockWindows").toByteArray(); if (aDockables.isEmpty()) { // Some windows are forced initially as is... insertToolBarBreak(m_ui.editViewToolbar); } else { // Make it as the last time. restoreState(aDockables); } // Try to restore old window positioning? // pOptions->loadWidgetGeometry(this, true); // Load (action) keyboard shortcuts... pOptions->loadActionShortcuts(this); } // Make last-but-not-least connections.... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { QObject::connect(m_ui.transportBackwardAction, SIGNAL(triggered(bool)), pMainForm, SLOT(transportBackward())); QObject::connect(m_ui.transportRewindAction, SIGNAL(triggered(bool)), pMainForm, SLOT(transportRewind())); QObject::connect(m_ui.transportFastForwardAction, SIGNAL(triggered(bool)), pMainForm, SLOT(transportFastForward())); QObject::connect(m_ui.transportForwardAction, SIGNAL(triggered(bool)), pMainForm, SLOT(transportForward())); QObject::connect(m_ui.transportStepBackwardAction, SIGNAL(triggered(bool)), SLOT(transportStepBackward())); QObject::connect(m_ui.transportStepForwardAction, SIGNAL(triggered(bool)), SLOT(transportStepForward())); QObject::connect(m_ui.transportStepNoteBackwardAction, SIGNAL(triggered(bool)), SLOT(transportStepNoteBackward())); QObject::connect(m_ui.transportStepNoteForwardAction, SIGNAL(triggered(bool)), SLOT(transportStepNoteForward())); QObject::connect(m_ui.transportLoopAction, SIGNAL(triggered(bool)), pMainForm, SLOT(transportLoop())); QObject::connect(m_ui.transportLoopSetAction, SIGNAL(triggered(bool)), pMainForm, SLOT(transportLoopSet())); QObject::connect(m_ui.transportStopAction, SIGNAL(triggered(bool)), pMainForm, SLOT(transportStop())); QObject::connect(m_ui.transportPlayAction, SIGNAL(triggered(bool)), pMainForm, SLOT(transportPlay())); QObject::connect(m_ui.transportRecordAction, SIGNAL(triggered(bool)), pMainForm, SLOT(transportRecord())); QObject::connect(m_ui.transportPunchAction, SIGNAL(triggered(bool)), pMainForm, SLOT(transportPunch())); QObject::connect(m_ui.transportPunchSetAction, SIGNAL(triggered(bool)), pMainForm, SLOT(transportPunchSet())); QObject::connect(m_ui.transportPanicAction, SIGNAL(triggered(bool)), pMainForm, SLOT(transportPanic())); // Add to main editors list... pMainForm->addEditorForm(this); } // Finally set initial editor type-params... if (pOptions) { m_pViewTypeComboBox->setCurrentIndex(pOptions->iMidiViewType); const qtractorMidiControl::ControlType ctype = m_pEventTypeGroup->controlTypeFromIndex(pOptions->iMidiEventType); m_pEventTypeGroup->setControlType(ctype); // eventTypeChanged(pOptions->iMidiEventType); m_pEventTypeGroup->setControlParam(pOptions->iMidiEventParam); viewTypeChanged(pOptions->iMidiViewType); } else { m_pEventTypeComboBox->setCurrentIndex(0); // eventTypeChanged(0); } // HACK: Some explicit focus immediately... m_pMidiEditor->editView()->setFocus(); } // Destructor. qtractorMidiEditorForm::~qtractorMidiEditorForm (void) { // Remove this one from main-form list... qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->removeEditorForm(this); // View/Snap-to-beat actions termination... qDeleteAll(m_snapPerBeatActions); m_snapPerBeatActions.clear(); // Drop any widgets around (not really necessary)... if (m_pMidiEventList) delete m_pMidiEventList; if (m_pMidiEditor) delete m_pMidiEditor; // Destroy custom track/instrument proxy menu. if (m_pInstrumentMenu) delete m_pInstrumentMenu; // Get edit-mode action group down. if (m_pEditModeActionGroup) delete m_pEditModeActionGroup; if (m_pEditModeToolButton) delete m_pEditModeToolButton; if (m_pEventTypeGroup) delete m_pEventTypeGroup; // Ditch rec-mode/red palette... if (m_pRedPalette) delete m_pRedPalette; // Custom time-signature cursor. if (m_pTempoCursor) delete m_pTempoCursor; } //------------------------------------------------------------------------- // qtractorMidiEditorForm -- Window close event handlers. // Pre-close event handlers. bool qtractorMidiEditorForm::queryClose (void) { bool bQueryClose = true; // Are we dirty enough to prompt it? if (m_iDirtyCount > 0) { if (isVisible()) { // Currently visible: save conditionally... switch (querySave(filename(), this)) { case QMessageBox::Save: bQueryClose = saveClipFile(false); // Fall thru.... case QMessageBox::Discard: break; default: // Cancel. bQueryClose = false; break; } } else { // Not currently visible: save unconditionally... bQueryClose = saveClipFile(false); } } return bQueryClose; } // Save(as) warning message box. int qtractorMidiEditorForm::querySave ( const QString& sFilename, QWidget *pParent ) { if (pParent == nullptr) pParent = qtractorMainForm::getInstance(); return (QMessageBox::warning(pParent, tr("Warning"), tr("The current MIDI clip has been changed:\n\n" "\"%1\"\n\n" "Do you want to save the changes?").arg(sFilename), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel)); } // On-close event handler. void qtractorMidiEditorForm::closeEvent ( QCloseEvent *pCloseEvent ) { // Try to save current editor view state... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions && isVisible()) { // Save decorations state. pOptions->bMidiMenubar = m_ui.menuBar->isVisible(); pOptions->bMidiStatusbar = statusBar()->isVisible(); pOptions->bMidiFileToolbar = m_ui.fileToolbar->isVisible(); pOptions->bMidiEditToolbar = m_ui.editToolbar->isVisible(); pOptions->bMidiViewToolbar = m_ui.viewToolbar->isVisible(); pOptions->bMidiTransportToolbar = m_ui.transportToolbar->isVisible(); pOptions->bMidiTimeToolbar = m_ui.timeToolbar->isVisible(); pOptions->bMidiScaleToolbar = m_ui.snapToScaleToolbar->isVisible(); pOptions->iMidiZoomMode = m_pMidiEditor->zoomMode(); pOptions->iMidiHorizontalZoom = m_pMidiEditor->horizontalZoom(); pOptions->iMidiVerticalZoom = m_pMidiEditor->verticalZoom(); pOptions->bMidiSnapZebra = m_pMidiEditor->isSnapZebra(); pOptions->bMidiSnapGrid = m_pMidiEditor->isSnapGrid(); pOptions->bMidiToolTips = m_pMidiEditor->isToolTips(); pOptions->bMidiEditMode = m_pMidiEditor->isEditMode(); pOptions->bMidiEditModeDraw = m_pMidiEditor->isEditModeDraw(); pOptions->iMidiDisplayFormat = (m_pMidiEditor->timeScale())->displayFormat(); pOptions->bMidiNoteNames = m_ui.viewNoteNamesAction->isChecked(); pOptions->bMidiNoteDuration = m_ui.viewNoteDurationAction->isChecked(); pOptions->bMidiNoteColor = m_ui.viewNoteColorAction->isChecked(); pOptions->bMidiValueColor = m_ui.viewValueColorAction->isChecked(); pOptions->bMidiPreview = m_ui.viewPreviewAction->isChecked(); pOptions->bMidiFollow = m_ui.viewFollowAction->isChecked(); // Save editor type-params... pOptions->iMidiViewType = m_pViewTypeComboBox->currentIndex(); pOptions->iMidiEventType = m_pEventTypeComboBox->currentIndex(); pOptions->iMidiEventParam = m_pEventTypeGroup->controlParam(); // Save snap-per-beat setting... pOptions->iMidiSnapPerBeat = m_pSnapPerBeatComboBox->currentIndex(); // Save snap-to-scale settings... pOptions->iMidiSnapToScaleKey = m_pSnapToScaleKeyComboBox->currentIndex(); pOptions->iMidiSnapToScaleType = m_pSnapToScaleTypeComboBox->currentIndex(); // Close floating dock windows... if (m_pMidiEventList->isFloating()) m_pMidiEventList->close(); // Save the dock windows state. pOptions->settings().setValue( "/MidiEditor/Layout/DockWindows", saveState()); // And this main windows state? // pOptions->saveWidgetGeometry(this, true); } // Close it good. pCloseEvent->accept(); } //------------------------------------------------------------------------- // qtractorMidiEditorForm -- Context menu event handlers. // Context menu request. void qtractorMidiEditorForm::contextMenuEvent ( QContextMenuEvent *pContextMenuEvent ) { stabilizeForm(); // Primordial edit menu should be available... m_ui.editMenu->exec(pContextMenuEvent->globalPos()); } // Edit menu accessor. QMenu *qtractorMidiEditorForm::editMenu (void) const { return m_ui.editMenu; } //------------------------------------------------------------------------- // qtractorMidiEditorForm -- Central widget redirect methods. // MIDI editor widget accessor. qtractorMidiEditor *qtractorMidiEditorForm::editor (void) const { return m_pMidiEditor; } // Local time-scale accessor. qtractorTimeScale *qtractorMidiEditorForm::timeScale (void) const { return m_pMidiEditor->timeScale(); } unsigned long qtractorMidiEditorForm::timeOffset (void) const { return m_pMidiEditor->timeOffset(); } // Editing MIDI clip accessors. qtractorMidiClip *qtractorMidiEditorForm::midiClip (void) const { return m_pMidiEditor->midiClip(); } // MIDI clip property accessors. const QString& qtractorMidiEditorForm::filename (void) const { return m_pMidiEditor->filename(); } unsigned short qtractorMidiEditorForm::trackChannel (void) const { return m_pMidiEditor->trackChannel(); } unsigned short qtractorMidiEditorForm::format (void) const { return m_pMidiEditor->format(); } qtractorMidiSequence *qtractorMidiEditorForm::sequence (void) const { return m_pMidiEditor->sequence(); } // Special executive setup method. void qtractorMidiEditorForm::setup ( qtractorMidiClip *pMidiClip ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks == nullptr) return; // Get those time-scales in sync, // while keeping zoom ratios persistant... const unsigned short iHorizontalZoom = m_pMidiEditor->horizontalZoom(); const unsigned short iVerticalZoom = m_pMidiEditor->verticalZoom(); qtractorTimeScale *pTimeScale = m_pMidiEditor->timeScale(); pTimeScale->copy(*pSession->timeScale()); m_pMidiEditor->setHorizontalZoom(iHorizontalZoom); m_pMidiEditor->setVerticalZoom(iVerticalZoom); // Fix some local options though... qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) { const qtractorTimeScale::DisplayFormat displayFormat = qtractorTimeScale::DisplayFormat(pOptions->iMidiDisplayFormat); pTimeScale->setDisplayFormat(displayFormat); m_pTimeSpinBox->setDisplayFormat(displayFormat); } // Reset custom time-sig cursor... m_pTempoCursor->clear(); // Default snap-per-beat setting... pTimeScale->setSnapPerBeat( qtractorTimeScale::snapFromIndex( m_pSnapPerBeatComboBox->currentIndex())); // Note that there's two modes for this method: // whether pMidiClip is given non-null wich means // form initialization first setup or else... if (pMidiClip) { // Set initial MIDI clip properties has seen fit... m_pMidiEditor->setMidiClip(pMidiClip); // Setup connections to main widget... QObject::connect(m_pMidiEditor, SIGNAL(changeNotifySignal(qtractorMidiEditor *)), pMainForm, SLOT(changeNotifySlot(qtractorMidiEditor *))); // This one's local but helps... QObject::connect(m_pMidiEditor, SIGNAL(sendNoteSignal(int, int, bool)), SLOT(sendNote(int, int, bool))); // Setup for last known top-level window position... QPoint wpos = pMidiClip->editorPos(); if (wpos.isNull() || wpos.x() < 0 || wpos.y() < 0) { QWidget *pParent = parentWidget(); if (pParent == nullptr) pParent = pMainForm; if (pParent) { QRect wrect(geometry()); wrect.moveCenter(pParent->geometry().center()); wpos = wrect.topLeft(); } } move(wpos); // Setup for last known top-level window size... const QSize& wsize = pMidiClip->editorSize(); if (!wsize.isNull() && wsize.isValid()) resize(wsize); #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) // Setup for top-level window geometry changes... QWindow *pWindow = windowHandle(); if (pWindow) { QObject::connect(pWindow, SIGNAL(xChanged(int)), SLOT(posChanged())); QObject::connect(pWindow, SIGNAL(yChanged(int)), SLOT(posChanged())); QObject::connect(pWindow, SIGNAL(widthChanged(int)), SLOT(sizeChanged())); QObject::connect(pWindow, SIGNAL(heightChanged(int)), SLOT(sizeChanged())); } #endif } // Whether we're a initial setup or a second coming... const bool bMidiClip = (pMidiClip == nullptr); if (bMidiClip) pMidiClip = midiClip(); // Setup for secondary time-signature, if any... if (pMidiClip) { pTimeScale->setBeatsPerBar2(pMidiClip->beatsPerBar2()); pTimeScale->setBeatDivisor2(pMidiClip->beatDivisor2()); } // Reset local dirty flag. resetDirtyCount(); // Get all those names right... updateInstrumentNames(); // Drum mode visuals.... updateDrumMode(); // Refresh and try to center (vertically) the edit-view... m_pMidiEditor->centerContents(); m_pMidiEventList->refresh(); // (Re)try to reposition the editor in the same relative // position in track-view, only if clip is not empty/new... qtractorTrack *pTrack = nullptr; if (pMidiClip) { qtractorMidiSequence *pSeq = pMidiClip->sequence(); if (bMidiClip || (pSeq && pSeq->events().count() > 0)) pTrack = pMidiClip->track(); } if (pTrack) { const int h1 = pTrack->zoomHeight(); if (h1 > 0) { // Try to recenter horizontally... qtractorTrackView *pTrackView = pTracks->trackView(); const QPoint& pos = pTrackView->mapFromGlobal(QCursor::pos()); unsigned long iFrame = pSession->frameFromPixel( pTrackView->contentsX() + pos.x()); if (iFrame > m_pMidiEditor->offset()) { iFrame -= m_pMidiEditor->offset(); } else { iFrame = 0; } qtractorMidiEditView *pEditView = m_pMidiEditor->editView(); const int w2 = (pEditView->width() >> 1); const int h2 = (pEditView->height() >> 1); const int x2 = pTimeScale->pixelFromFrame(iFrame); const int cx = pEditView->contentsX(); const int cy = pEditView->contentsY(); // Then try to recenter vertically... int y2 = cy; int y1 = 0; qtractorTrack *pTrackEx = pSession->tracks().first(); while (pTrackEx && pTrackEx != pTrack) { y1 += pTrackEx->zoomHeight(); pTrackEx = pTrackEx->next(); } y1 -= pTrackView->contentsY(); const int nmax = pTrack->midiNoteMax(); const int nmin = pTrack->midiNoteMin(); const int n1 = (pos.y() - y1) * (nmax - nmin) / h1; const int n2 = (128 - nmax) + n1; if (n2 > 0 && n2 < 128) y2 = n2 * (m_pMidiEditor->editList())->itemHeight(); // Need recentering?... if (x2 < cx || x2 > cx + w2 || y2 < cy || y2 > cy + h2) { pEditView->setContentsPos( (x2 > w2 ? x2 - w2 : 0), (y2 > h2 ? y2 - h2 : 0)); } } } // (Re)sync local play/edit-head/tail (avoid follow playhead)... m_pMidiEditor->setPlayHead(pSession->playHead(), false); m_pMidiEditor->setEditHead(pSession->editHead(), false); m_pMidiEditor->setEditTail(pSession->editTail(), false); // Finally update current time position display... updatePlayHead(pSession->playHead()); // Done. stabilizeForm(); } // Reset coomposite dirty flag. void qtractorMidiEditorForm::resetDirtyCount (void) { m_iDirtyCount = 0; // MIDI clip might be dirty already. qtractorMidiClip *pMidiClip = midiClip(); if (pMidiClip && pMidiClip->isDirty()) ++m_iDirtyCount; } //------------------------------------------------------------------------- // qtractorMidiEditorForm -- Clip Action methods. // Save current clip. bool qtractorMidiEditorForm::saveClipFile ( bool bPrompt ) { qtractorMidiClip *pMidiClip = m_pMidiEditor->midiClip(); if (pMidiClip == nullptr) return false; qtractorTrack *pTrack = pMidiClip->track(); if (pTrack == nullptr) return false; qtractorSession *pSession = pTrack->session(); if (pSession == nullptr) return false; // Suggest a brand new filename, if there's none... QString sFilename = pMidiClip->createFilePathRevision(bPrompt); // Ask for the file to save... if (sFilename.isEmpty()) bPrompt = true; if (bPrompt) { // If none is given, assume default directory. const QString sExt("mid"); const QString& sTitle = tr("Save MIDI Clip"); QStringList filters; filters.append(tr("MIDI files (*.%1 *.smf *.midi)").arg(sExt)); 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 filenames to open... sFilename = QFileDialog::getSaveFileName(pParentWidget, sTitle, sFilename, sFilter, nullptr, options); #else // Construct open-files 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... 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()) sFilename = fileDialog.selectedFiles().first(); else sFilename.clear(); #endif // Have we cancelled it? if (sFilename.isEmpty() || sFilename.at(0) == '.') return false; // Enforce .mid extension... if (QFileInfo(sFilename).suffix().isEmpty()) sFilename += '.' + sExt; } // Save it right away... const bool bResult = pMidiClip->saveCopyFile(sFilename, true); // Have we done it right? if (bResult) { // Aha, but we're not dirty no more. m_iDirtyCount = 0; } // Done. m_pMidiEventList->refresh(); stabilizeForm(); return bResult; } //------------------------------------------------------------------------- // qtractorMidiEditorForm -- File Action slots. // Save current clip. void qtractorMidiEditorForm::fileSave (void) { saveClipFile(false); } // Save current clip with another name. void qtractorMidiEditorForm::fileSaveAs (void) { saveClipFile(true); } // Mute current clip. void qtractorMidiEditorForm::fileMute (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks) pTracks->muteClip(m_pMidiEditor->midiClip()); } } // Unlink current clip. void qtractorMidiEditorForm::fileUnlink (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks) pTracks->unlinkClip(m_pMidiEditor->midiClip()); } } // Enter in clip record/ overdub mode. void qtractorMidiEditorForm::fileRecordEx ( bool bOn ) { qtractorMidiClip *pMidiClip = m_pMidiEditor->midiClip(); if (pMidiClip == nullptr) return; // Start record/overdub the current clip, if any... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) pSession->execute(new qtractorClipRecordExCommand(pMidiClip, bOn)); } // File properties dialog. void qtractorMidiEditorForm::fileProperties (void) { qtractorMidiClip *pMidiClip = m_pMidiEditor->midiClip(); if (pMidiClip == nullptr) return; qtractorClipForm clipForm(this); clipForm.setClip(pMidiClip); clipForm.exec(); } // Show current MIDI clip/track input bus connections. void qtractorMidiEditorForm::fileTrackInputs (void) { qtractorMidiClip *pMidiClip = m_pMidiEditor->midiClip(); if (pMidiClip == nullptr) return; qtractorTrack *pTrack = pMidiClip->track(); if (pTrack == nullptr) return; if (pTrack->inputBus() == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm && pMainForm->connections()) { (pMainForm->connections())->showBus( pTrack->inputBus(), qtractorBus::Input); } } // Show current MIDI clip/track output bus connections. void qtractorMidiEditorForm::fileTrackOutputs (void) { qtractorMidiClip *pMidiClip = m_pMidiEditor->midiClip(); if (pMidiClip == nullptr) return; qtractorTrack *pTrack = pMidiClip->track(); if (pTrack == nullptr) return; if (pTrack->outputBus() == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm && pMainForm->connections()) { (pMainForm->connections())->showBus( pTrack->outputBus(), qtractorBus::Output); } } // Edit current MIDI clip/track properties. void qtractorMidiEditorForm::fileTrackProperties (void) { qtractorMidiClip *pMidiClip = m_pMidiEditor->midiClip(); if (pMidiClip == nullptr) return; qtractorTrack *pTrack = pMidiClip->track(); if (pTrack == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks) pTracks->editTrack(pTrack); } } // Edit-range setting to clip extents. void qtractorMidiEditorForm::fileRangeSet (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks) pTracks->rangeClip(m_pMidiEditor->midiClip()); } } // Loop-range setting to clip extents. void qtractorMidiEditorForm::fileLoopSet (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) { qtractorTracks *pTracks = pMainForm->tracks(); if (pTracks) pTracks->loopClip(m_pMidiEditor->midiClip()); } } // Exit editing. void qtractorMidiEditorForm::fileClose (void) { // Go for close this thing. close(); } //------------------------------------------------------------------------- // qtractorMidiEditorForm -- Edit action slots. // Undo last edit command. void qtractorMidiEditorForm::editUndo (void) { m_pMidiEditor->undoCommand(); } // Redo last edit command. void qtractorMidiEditorForm::editRedo (void) { m_pMidiEditor->redoCommand(); } // Cut current selection to clipboard. void qtractorMidiEditorForm::editCut (void) { m_pMidiEditor->cutClipboard(); } // Copy current selection to clipboard. void qtractorMidiEditorForm::editCopy (void) { m_pMidiEditor->copyClipboard(); } // Paste from clipboard. void qtractorMidiEditorForm::editPaste (void) { m_pMidiEditor->pasteClipboard(); } // Paste/repeat from clipboard. void qtractorMidiEditorForm::editPasteRepeat (void) { qtractorPasteRepeatForm pasteForm(this); pasteForm.setRepeatPeriod(m_pMidiEditor->pastePeriod()); if (pasteForm.exec()) { m_pMidiEditor->pasteClipboard( pasteForm.repeatCount(), pasteForm.repeatPeriod() ); } } // Delete current selection. void qtractorMidiEditorForm::editDelete (void) { m_pMidiEditor->deleteSelect(); } // Toggle edit-mode on. void qtractorMidiEditorForm::editModeOn ( bool bOn ) { // m_pMidiEditor->setEditModeDraw(false); if (bOn && m_pMidiEditor->isEditMode() && !m_pMidiEditor->isEditModeDraw()) { m_ui.editModeOffAction->setChecked(true); m_pEditModeToolButton->setDefaultAction( m_pEditModeActionGroup->checkedAction()); m_pMidiEditor->setEditMode(false); } else { m_pMidiEditor->setEditModeDraw(!bOn); m_pMidiEditor->setEditMode(bOn); } m_pMidiEditor->updateContents(); stabilizeForm(); } // Toggle edit-mode off. void qtractorMidiEditorForm::editModeOff ( bool bOn ) { // m_pMidiEditor->setEditModeDraw(false); if (bOn && !m_pMidiEditor->isEditMode()) { if (m_pMidiEditor->isEditModeDraw()) m_ui.editModeDrawAction->setChecked(true); else m_ui.editModeOnAction->setChecked(true); m_pEditModeToolButton->setDefaultAction( m_pEditModeActionGroup->checkedAction()); m_pMidiEditor->setEditMode(true); } else { m_pMidiEditor->setEditMode(!bOn); } m_pMidiEditor->updateContents(); stabilizeForm(); } // Toggle draw-mode (notes) void qtractorMidiEditorForm::editModeDraw ( bool bOn ) { // m_pMidiEditor->setEditModeDraw(bOn); if (bOn && m_pMidiEditor->isEditMode() && m_pMidiEditor->isEditModeDraw()) { m_ui.editModeOnAction->setChecked(true); m_pEditModeToolButton->setDefaultAction( m_pEditModeActionGroup->checkedAction()); m_pMidiEditor->setEditModeDraw(false); } else { m_pMidiEditor->setEditModeDraw(bOn); m_pMidiEditor->setEditMode(bOn); } m_pMidiEditor->updateContents(); stabilizeForm(); } // Select none contents. void qtractorMidiEditorForm::editSelectNone (void) { qtractorScrollView *pScrollView = m_pMidiEditor->editView(); if (m_pMidiEditor->editEvent()->hasFocus()) pScrollView = m_pMidiEditor->editEvent(); m_pMidiEditor->selectAll(pScrollView, false, false); } // Select invert contents. void qtractorMidiEditorForm::editSelectInvert (void) { qtractorScrollView *pScrollView = m_pMidiEditor->editView(); if (m_pMidiEditor->editEvent()->hasFocus()) pScrollView = m_pMidiEditor->editEvent(); m_pMidiEditor->selectAll(pScrollView, true, true); } // Select all contents. void qtractorMidiEditorForm::editSelectAll (void) { qtractorScrollView *pScrollView = m_pMidiEditor->editView(); if (m_pMidiEditor->editEvent()->hasFocus()) pScrollView = m_pMidiEditor->editEvent(); m_pMidiEditor->selectAll(pScrollView, true, false); } // Select contents range. void qtractorMidiEditorForm::editSelectRange (void) { qtractorScrollView *pScrollView = m_pMidiEditor->editView(); if (m_pMidiEditor->editEvent()->hasFocus()) pScrollView = m_pMidiEditor->editEvent(); m_pMidiEditor->selectRange(pScrollView, true, true); } // Insert range. void qtractorMidiEditorForm::editInsertRange (void) { m_pMidiEditor->insertEditRange(); } // Insert step. void qtractorMidiEditorForm::editInsertStep (void) { qtractorMidiClip *pMidiClip = m_pMidiEditor->midiClip(); if (pMidiClip) { pMidiClip->advanceStepInput(); pMidiClip->updateStepInput(); } } // Remove range. void qtractorMidiEditorForm::editRemoveRange (void) { m_pMidiEditor->removeEditRange(); } // Quantize tool. void qtractorMidiEditorForm::toolsQuantize (void) { m_pMidiEditor->executeTool(qtractorMidiEditor::Quantize); } // Transpose tool. void qtractorMidiEditorForm::toolsTranspose (void) { m_pMidiEditor->executeTool(qtractorMidiEditor::Transpose); } // Normalize tool. void qtractorMidiEditorForm::toolsNormalize (void) { m_pMidiEditor->executeTool(qtractorMidiEditor::Normalize); } // Randomize tool. void qtractorMidiEditorForm::toolsRandomize (void) { m_pMidiEditor->executeTool(qtractorMidiEditor::Randomize); } // Resize tool. void qtractorMidiEditorForm::toolsResize (void) { m_pMidiEditor->executeTool(qtractorMidiEditor::Resize); } // Rescale tool. void qtractorMidiEditorForm::toolsRescale (void) { m_pMidiEditor->executeTool(qtractorMidiEditor::Rescale); } // Timeshift tool. void qtractorMidiEditorForm::toolsTimeshift (void) { m_pMidiEditor->executeTool(qtractorMidiEditor::Timeshift); } // Temporamp tool. void qtractorMidiEditorForm::toolsTemporamp (void) { m_pMidiEditor->executeTool(qtractorMidiEditor::Temporamp); } //------------------------------------------------------------------------- // qtractorMidiEditorForm -- View Action slots. // Show/hide the main program window menubar. void qtractorMidiEditorForm::viewMenubar ( bool bOn ) { m_ui.menuBar->setVisible(bOn); } // Show/hide the main program window statusbar. void qtractorMidiEditorForm::viewStatusbar ( bool bOn ) { statusBar()->setVisible(bOn); } // Show/hide the file-toolbar. void qtractorMidiEditorForm::viewToolbarFile ( bool bOn ) { m_ui.fileToolbar->setVisible(bOn); } // Show/hide the edit-toolbar. void qtractorMidiEditorForm::viewToolbarEdit ( bool bOn ) { m_ui.editToolbar->setVisible(bOn); } // Show/hide the view-toolbar. void qtractorMidiEditorForm::viewToolbarView ( bool bOn ) { m_ui.viewToolbar->setVisible(bOn); } // Show/hide the transport-toolbar. void qtractorMidiEditorForm::viewToolbarTransport ( bool bOn ) { m_ui.transportToolbar->setVisible(bOn); } // Show/hide the time-signature toolbar. void qtractorMidiEditorForm::viewToolbarTime( bool bOn ) { m_ui.timeToolbar->setVisible(bOn); } // Show/hide the snap-to-scale toolbar. void qtractorMidiEditorForm::viewToolbarScale ( bool bOn ) { m_ui.snapToScaleToolbar->setVisible(bOn); } // Show/hide the thumb-view toolbar. void qtractorMidiEditorForm::viewToolbarThumb ( bool bOn ) { m_ui.thumbViewToolbar->setVisible(bOn); } // Show/hide the events window view. void qtractorMidiEditorForm::viewEvents ( bool bOn ) { m_pMidiEventList->setVisible(bOn); } // View note (pitch) coloring. void qtractorMidiEditorForm::viewNoteColor ( bool bOn ) { m_pMidiEditor->setNoteColor(bOn); m_pMidiEditor->updateContents(); } // View note (velocity) coloring. void qtractorMidiEditorForm::viewValueColor ( bool bOn ) { m_pMidiEditor->setValueColor(bOn); m_pMidiEditor->updateContents(); } // View note names (in rectangles) void qtractorMidiEditorForm::viewNoteNames ( bool bOn ) { m_pMidiEditor->setNoteNames(bOn); m_pMidiEditor->updateContents(); } // View duration as widths of notes void qtractorMidiEditorForm::viewNoteDuration ( bool bOn ) { m_pMidiEditor->setNoteDuration(bOn); m_pMidiEditor->updateContents(); } // Change view/note type setting via menu. void qtractorMidiEditorForm::viewNoteType (void) { // Retrieve view/note type index from from action data... QAction *pAction = qobject_cast (sender()); if (pAction) { const int iIndex = pAction->data().toInt(); // Update the other toolbar control... m_pViewTypeComboBox->setCurrentIndex(iIndex); // Commit the change as usual... viewTypeChanged(iIndex); } } // View drum note of notes (diamods) void qtractorMidiEditorForm::viewDrumMode ( bool bOn ) { m_pSnapToScaleKeyComboBox->setEnabled(!bOn); m_pSnapToScaleTypeComboBox->setEnabled(!bOn); qtractorMidiClip *pMidiClip = m_pMidiEditor->midiClip(); if (pMidiClip) pMidiClip->setEditorDrumMode(bOn ? 1 : 0); m_pMidiEditor->setDrumMode(bOn); m_pMidiEditor->updateContents(); } // Change event/value type setting via menu. void qtractorMidiEditorForm::viewValueType (void) { // Retrieve event/value type index from from action data... QAction *pAction = qobject_cast (sender()); if (pAction) { const int iIndex = pAction->data().toInt(); // Update the other toolbar control... const qtractorMidiControl::ControlType ctype = m_pEventTypeGroup->controlTypeFromIndex(iIndex); m_pEventTypeGroup->setControlType(ctype); // Commit the change as usual... // eventTypeChanged(iIndex); } } // Change ghost-track setting via menu. void qtractorMidiEditorForm::viewGhostTrack (void) { // Retrieve ghost-track from from action data... QAction *pAction = qobject_cast (sender()); if (pAction) { // Commit the change as usual... qtractorTrack *pGhostTrack = static_cast ( pAction->data().value ()); m_pMidiEditor->setGhostTrack(pGhostTrack); m_pMidiEditor->updateContents(); } } // Horizontal and/or vertical zoom-in. void qtractorMidiEditorForm::viewZoomIn (void) { m_pMidiEditor->zoomIn(); } // Horizontal and/or vertical zoom-out. void qtractorMidiEditorForm::viewZoomOut (void) { m_pMidiEditor->zoomOut(); } // Reset zoom level to default. void qtractorMidiEditorForm::viewZoomReset (void) { m_pMidiEditor->zoomReset(); } // Set horizontal zoom mode void qtractorMidiEditorForm::viewZoomHorizontal (void) { m_pMidiEditor->setZoomMode(qtractorMidiEditor::ZoomHorizontal); } // Set vertical zoom mode void qtractorMidiEditorForm::viewZoomVertical (void) { m_pMidiEditor->setZoomMode(qtractorMidiEditor::ZoomVertical); } // Set all zoom mode void qtractorMidiEditorForm::viewZoomAll (void) { m_pMidiEditor->setZoomMode(qtractorMidiEditor::ZoomAll); } // Set zebra mode void qtractorMidiEditorForm::viewSnapZebra ( bool bOn ) { m_pMidiEditor->setSnapZebra(bOn); m_pMidiEditor->updateContents(); } // Set grid mode void qtractorMidiEditorForm::viewSnapGrid ( bool bOn ) { m_pMidiEditor->setSnapGrid(bOn); m_pMidiEditor->updateContents(); } // Set floating tool-tips view mode void qtractorMidiEditorForm::viewToolTips ( bool bOn ) { m_pMidiEditor->setToolTips(bOn); } // Change snap-per-beat setting via menu. void qtractorMidiEditorForm::viewSnap (void) { // Retrieve snap-per-beat index from from action data... QAction *pAction = qobject_cast (sender()); if (pAction) { // Commit the change as usual... snapPerBeatChanged(pAction->data().toInt()); // Update the other toolbar control... qtractorTimeScale *pTimeScale = m_pMidiEditor->timeScale(); if (pTimeScale) m_pSnapPerBeatComboBox->setCurrentIndex( qtractorTimeScale::indexFromSnap(pTimeScale->snapPerBeat())); } } // Change snap-to-scale key setting via menu. void qtractorMidiEditorForm::viewScaleKey (void) { // Retrieve snap-to-scale key index from from action data... QAction *pAction = qobject_cast (sender()); if (pAction) { // Commit the change as usual... snapToScaleKeyChanged(pAction->data().toInt()); // Update the other toolbar control... m_pSnapToScaleKeyComboBox->setCurrentIndex( m_pMidiEditor->snapToScaleKey()); } } // Change snap-to-scale type setting via menu. void qtractorMidiEditorForm::viewScaleType (void) { // Retrieve snap-to-scale type index from from action data... QAction *pAction = qobject_cast (sender()); if (pAction) { // Commit the change as usual... snapToScaleTypeChanged(pAction->data().toInt()); // Update the other toolbar control... m_pSnapToScaleTypeComboBox->setCurrentIndex( m_pMidiEditor->snapToScaleType()); } } // Refresh view display. void qtractorMidiEditorForm::viewRefresh (void) { m_pTempoCursor->clear(); m_pMidiEditor->updateContents(); m_pMidiEventList->refresh(); stabilizeForm(); } // View preview notes void qtractorMidiEditorForm::viewPreview ( bool bOn ) { m_pMidiEditor->setSendNotes(bOn); } // View follow playhead void qtractorMidiEditorForm::viewFollow ( bool bOn ) { m_pMidiEditor->setSyncView(bOn); } //------------------------------------------------------------------------- // qtractorMidiEditorForm -- Transport Action slots. // Transport step-backward (local) void qtractorMidiEditorForm::transportStepBackward (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = m_pMidiEditor->timeScale(); if (pTimeScale == nullptr) return; unsigned long iPlayHead = pSession->playHead(); const unsigned short iSnapPerBeat = pTimeScale->snapPerBeat(); if (iSnapPerBeat > 0) { // Step-backward a beat/fraction... const unsigned long t0 = pTimeScale->tickFromFrame(iPlayHead); const unsigned int iBeat = pTimeScale->beatFromTick(t0); const unsigned long t1 = pTimeScale->tickFromBeat(iBeat > 0 ? iBeat : iBeat + 1); const unsigned long t2 = pTimeScale->tickFromBeat(iBeat > 0 ? iBeat - 1 : iBeat); const unsigned long dt = (t1 - t2) / iSnapPerBeat; iPlayHead = pTimeScale->frameFromTick( pTimeScale->tickSnap(t0 > dt ? t0 - dt : 0)); } else { // Step-backward a bar... const unsigned short iBar = pTimeScale->barFromFrame(iPlayHead); iPlayHead = pTimeScale->frameFromBar(iBar > 0 ? iBar - 1 : iBar); } m_pMidiEditor->setSyncViewHoldOn(false); m_pMidiEditor->setPlayHead(iPlayHead); pSession->setPlayHead(iPlayHead); } // Transport step-forward (local) void qtractorMidiEditorForm::transportStepForward (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = m_pMidiEditor->timeScale(); if (pTimeScale == nullptr) return; unsigned long iPlayHead = pSession->playHead(); const unsigned short iSnapPerBeat = pTimeScale->snapPerBeat(); if (iSnapPerBeat > 0) { // Step-forward a beat/fraction... const unsigned long t0 = pTimeScale->tickFromFrame(iPlayHead); const unsigned int iBeat = pTimeScale->beatFromTick(t0); const unsigned long t1 = pTimeScale->tickFromBeat(iBeat); const unsigned long t2 = pTimeScale->tickFromBeat(iBeat + 1); const unsigned long dt = (t2 - t1) / iSnapPerBeat; iPlayHead = pTimeScale->frameFromTick( pTimeScale->tickSnap(t0 + dt)); } else { // Step-forward a bar... const unsigned short iBar = pTimeScale->barFromFrame(iPlayHead); iPlayHead = pTimeScale->frameFromBar(iBar + 1); } m_pMidiEditor->setSyncViewHoldOn(false); m_pMidiEditor->setPlayHead(iPlayHead); pSession->setPlayHead(iPlayHead); } // Transport note-backward (local) void qtractorMidiEditorForm::transportStepNoteBackward (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = m_pMidiEditor->timeScale(); if (pTimeScale == nullptr) return; const bool bSendNotes = m_pMidiEditor->isSendNotesEx(); unsigned long iPlayHead = pSession->playHead(); qtractorMidiSequence *pSeq = m_pMidiEditor->sequence(); if (pSeq) { // Step-backward a note event... qtractorTimeScale::Cursor cursor(pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(iPlayHead); const unsigned long t0 = pNode->tickFromFrame(m_pMidiEditor->offset()); const unsigned long t1 = pNode->tickFromFrame(iPlayHead); const unsigned long iTime = (t1 > t0 ? t1 - t0 : 0); qtractorMidiEditView *pEditView = m_pMidiEditor->editView(); const qtractorMidiEvent::EventType eventType = pEditView->eventType(); qtractorMidiEvent *pEvent = m_pMidiEditor->seekEvent(pSeq, iTime); while (pEvent && pEvent->time() < iTime) pEvent = pEvent->next(); if (pEvent == nullptr) pEvent = pSeq->events().last(); while (pEvent && pEvent->time() >= iTime) pEvent = pEvent->prev(); while (pEvent && pEvent->type() != eventType) pEvent = pEvent->prev(); if (pEvent && pEvent->type() == eventType) { const unsigned long iEventTime = pEvent->time(); const unsigned long t2 = t0 + iEventTime; pNode = cursor.seekTick(t2); iPlayHead = pNode->frameFromTick(t2); // Select all notes with same exact on-set time... m_pMidiEditor->selectAll(pEditView, false); while (pEvent && pEvent->time() == iEventTime) { if (pEvent->type() == eventType) { m_pMidiEditor->selectEvent(pEvent); if (bSendNotes && eventType == qtractorMidiEvent::NOTEON) { sendNoteEx(pEvent->note(), pEvent->velocity(), pEvent->duration()); } } pEvent = pEvent->prev(); } } } m_pMidiEditor->setSyncViewHoldOn(false); m_pMidiEditor->setPlayHead(iPlayHead); pSession->setPlayHead(iPlayHead); } // Transport note-forward (local) void qtractorMidiEditorForm::transportStepNoteForward (void) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = m_pMidiEditor->timeScale(); if (pTimeScale == nullptr) return; const bool bSendNotes = m_pMidiEditor->isSendNotesEx(); unsigned long iPlayHead = pSession->playHead(); qtractorMidiSequence *pSeq = m_pMidiEditor->sequence(); if (pSeq) { // Step-forward a note event... qtractorTimeScale::Cursor cursor(pTimeScale); qtractorTimeScale::Node *pNode = cursor.seekFrame(iPlayHead); const unsigned long t0 = pNode->tickFromFrame(m_pMidiEditor->offset()); const unsigned long t1 = pNode->tickFromFrame(iPlayHead); const unsigned long iTime = (t1 > t0 ? t1 - t0 : 0); qtractorMidiEditView *pEditView = m_pMidiEditor->editView(); const qtractorMidiEvent::EventType eventType = pEditView->eventType(); qtractorMidiEvent *pEvent = m_pMidiEditor->seekEvent(pSeq, iTime); if (pEvent && t1 >= t0 + pEvent->time()) while (pEvent && iTime >= pEvent->time()) pEvent = pEvent->next(); while (pEvent && pEvent->type() != eventType) pEvent = pEvent->next(); if (pEvent && pEvent->type() == eventType) { const unsigned long iEventTime = pEvent->time(); const unsigned long t2 = t0 + iEventTime; pNode = cursor.seekTick(t2); iPlayHead = pNode->frameFromTick(t2); // Select all notes with same exact on-set time... m_pMidiEditor->selectAll(pEditView, false); while (pEvent && pEvent->time() == iEventTime) { if (pEvent->type() == eventType) { m_pMidiEditor->selectEvent(pEvent); if (bSendNotes && eventType == qtractorMidiEvent::NOTEON) { sendNoteEx(pEvent->note(), pEvent->velocity(), pEvent->duration()); } } pEvent = pEvent->next(); } } } m_pMidiEditor->setSyncViewHoldOn(false); m_pMidiEditor->setPlayHead(iPlayHead); pSession->setPlayHead(iPlayHead); } //------------------------------------------------------------------------- // qtractorMidiEditorForm -- Help Action slots. // Show (and edit) keyboard shortcuts. void qtractorMidiEditorForm::helpShortcuts (void) { qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; const QList& actions = findChildren (QString(), Qt::FindDirectChildrenOnly); qtractorShortcutForm shortcutForm(actions, this); shortcutForm.setActionControl(nullptr); // Disable MIDI Controllers here! if (shortcutForm.exec() && shortcutForm.isDirtyActionShortcuts()) pOptions->saveActionShortcuts(this); } // Show information about application program. void qtractorMidiEditorForm::helpAbout (void) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->helpAbout(); } // Show information about the Qt toolkit. void qtractorMidiEditorForm::helpAboutQt (void) { QMessageBox::aboutQt(this); } //------------------------------------------------------------------------- // qtractorMidiEditorForm -- Utility methods. // Send note on/off to respective output bus. void qtractorMidiEditorForm::sendNote ( int iNote, int iVelocity, bool bForce ) { qtractorMidiClip *pMidiClip = m_pMidiEditor->midiClip(); if (pMidiClip == nullptr) return; qtractorTrack *pTrack = pMidiClip->track(); if (pTrack == nullptr) return; qtractorMidiBus *pMidiBus = static_cast (pTrack->outputBus()); if (pMidiBus == nullptr) return; pMidiBus->sendNote(pTrack, iNote, iVelocity, bForce); } // Schedule note to respective output bus. void qtractorMidiEditorForm::sendNoteEx ( int iNote, int iVelocity, unsigned long iDuration ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; const unsigned int srate = pSession->sampleRate(); if (srate > 0) { // Direct note-on... sendNote(iNote, iVelocity, false); // Schedule note-off... const unsigned long f1 = pSession->playHead(); qtractorTimeScale::Cursor cursor(pSession->timeScale()); qtractorTimeScale::Node *pNode = cursor.seekFrame(f1); const unsigned long iMaxDuration = pSession->ticksPerBeat() * pNode->beatsPerBar2(); const unsigned long t1 = pNode->tickFromFrame(f1); const unsigned long t2 = t1 + qMin(iDuration, iMaxDuration); pNode = cursor.seekTick(t2); const unsigned long f2 = pNode->frameFromTick(t2); const unsigned int msecs = (1000 * (f2 - f1)) / srate; QTimer::singleShot(msecs, [=]{ sendNote(iNote, 0, false); }); } } //------------------------------------------------------------------------- // qtractorMidiEditorForm -- Main form stabilization. void qtractorMidiEditorForm::stabilizeForm (void) { // Update the main menu state... qtractorTrack *pTrack = nullptr; qtractorMidiClip *pMidiClip = m_pMidiEditor->midiClip(); if (pMidiClip) pTrack = pMidiClip->track(); m_ui.fileSaveAction->setEnabled(m_iDirtyCount > 0); m_ui.fileMuteAction->setEnabled(pMidiClip != nullptr); m_ui.fileMuteAction->setChecked(pMidiClip && pMidiClip->isClipMute()); m_ui.fileUnlinkAction->setEnabled(pMidiClip && pMidiClip->isHashLinked()); const bool bClipRecordEx = (pTrack && pTrack->isClipRecordEx() && static_cast (pTrack->clipRecord()) == pMidiClip); m_ui.fileRecordExAction->setEnabled(pMidiClip != nullptr); m_ui.fileRecordExAction->setChecked(bClipRecordEx); m_ui.fileTrackInputsAction->setEnabled(pTrack && pTrack->inputBus() != nullptr); m_ui.fileTrackOutputsAction->setEnabled(pTrack && pTrack->outputBus() != nullptr); m_ui.fileTrackInstrumentMenu->setEnabled(pTrack != nullptr); m_ui.fileTrackPropertiesAction->setEnabled(pTrack != nullptr); m_ui.fileRangeSetAction->setEnabled(pTrack != nullptr); m_ui.fileLoopSetAction->setEnabled(pTrack != nullptr); // Update edit menu state... qtractorCommandList *pCommands = m_pMidiEditor->commands(); if (pCommands) { pCommands->updateAction(m_ui.editUndoAction, pCommands->lastCommand()); pCommands->updateAction(m_ui.editRedoAction, pCommands->nextCommand()); } const bool bSelected = m_pMidiEditor->isSelected(); const bool bSelectable = m_pMidiEditor->isSelectable(); const bool bClipboard = m_pMidiEditor->isClipboard(); m_ui.editCutAction->setEnabled(bSelected); m_ui.editCopyAction->setEnabled(bSelected); m_ui.editPasteAction->setEnabled(bClipboard); m_ui.editPasteRepeatAction->setEnabled(bClipboard); m_ui.editDeleteAction->setEnabled(bSelected); // m_ui.editModeDrawAction->setEnabled(m_pMidiEditor->isEditMode()); m_ui.editSelectNoneAction->setEnabled(bSelected); const bool bInsertable = m_pMidiEditor->isInsertable(); m_ui.editInsertRangeAction->setEnabled(bInsertable); m_ui.editRemoveRangeAction->setEnabled(bInsertable); #if 0 m_ui.toolsMenu->setEnabled(bSelected); #else m_ui.toolsQuantizeAction->setEnabled(bSelected); m_ui.toolsTransposeAction->setEnabled(bSelected); m_ui.toolsNormalizeAction->setEnabled(bSelected); m_ui.toolsRandomizeAction->setEnabled(bSelected); m_ui.toolsResizeAction->setEnabled(bSelected); m_ui.toolsRescaleAction->setEnabled(bSelected); m_ui.toolsTimeshiftAction->setEnabled(bSelected && bSelectable); m_ui.toolsTemporampAction->setEnabled(bSelected && bSelectable); #endif // Just having a non-null sequence will indicate // that we're editing a legal MIDI clip... qtractorMidiSequence *pSeq = sequence(); if (pSeq == nullptr) { setWindowTitle(tr("MIDI Editor")); m_pFileNameLabel->clear(); m_pTrackChannelLabel->clear(); m_pTrackNameLabel->clear(); m_pStatusModLabel->clear(); m_pStatusRecLabel->clear(); m_pStatusMuteLabel->clear(); m_pDurationLabel->clear(); return; } // Special display formatting... QString sTrackChannel; int k = 0; if (format() == 0) { sTrackChannel = tr("Channel %1"); ++k; } else { sTrackChannel = tr("Track %1"); } // Update the main window caption... QString sTitle = QFileInfo(filename()).fileName() + ' '; sTitle += '(' + sTrackChannel.arg(trackChannel() + k) + ')'; if (m_iDirtyCount > 0) sTitle += ' ' + tr("[modified]"); setWindowTitle(sTitle); m_ui.viewEventsAction->setChecked(m_pMidiEventList->isVisible()); m_pTrackNameLabel->setText(pTrack->trackName().simplified()); m_pFileNameLabel->setText(filename()); m_pTrackChannelLabel->setText(sTrackChannel.arg(trackChannel() + k)); if (m_iDirtyCount > 0) m_pStatusModLabel->setText(tr("MOD")); else m_pStatusModLabel->clear(); if (pTrack && pTrack->clipRecord() == pMidiClip) { m_pStatusRecLabel->setText(tr("REC")); m_pStatusRecLabel->setPalette(*m_pRedPalette); } else { m_pStatusRecLabel->clear(); m_pStatusRecLabel->setPalette(statusBar()->palette()); } if (pMidiClip && pMidiClip->isClipMute()) { m_pStatusMuteLabel->setText(tr("MUTE")); m_pStatusMuteLabel->setPalette(*m_pYellowPalette); } else { m_pStatusMuteLabel->clear(); m_pStatusMuteLabel->setPalette(statusBar()->palette()); } qtractorTimeScale *pTimeScale = m_pMidiEditor->timeScale(); m_pDurationLabel->setText(pTimeScale->textFromTick( pTimeScale->tickFromFrame(m_pMidiEditor->offset()), true, pSeq->duration())); qtractorSession *pSession = qtractorSession::getInstance(); qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pSession && pMainForm) { const unsigned long iPlayHead = pSession->playHead(); const bool bPlaying = pSession->isPlaying(); const bool bRecording = pSession->isRecording(); const bool bPunching = pSession->isPunching(); const bool bLooping = pSession->isLooping(); const bool bRolling = (bPlaying && bRecording); const bool bBumped = (!bRolling && (iPlayHead > 0 || bPlaying)); const int iRolling = pMainForm->rolling(); m_ui.editInsertStepAction->setEnabled(!bPlaying && bClipRecordEx); m_ui.transportBackwardAction->setEnabled(bBumped); m_ui.transportRewindAction->setEnabled(bBumped); m_ui.transportFastForwardAction->setEnabled(!bRolling); m_ui.transportForwardAction->setEnabled( !bRolling && (iPlayHead < pSession->sessionEnd() || iPlayHead < pSession->editHead() || iPlayHead < pSession->editTail())); m_ui.transportStepBackwardAction->setEnabled(bBumped); m_ui.transportStepForwardAction->setEnabled(!bRolling); m_ui.transportStepNoteBackwardAction->setEnabled(bBumped); m_ui.transportStepNoteForwardAction->setEnabled(!bRolling); m_ui.transportLoopAction->setEnabled( !bRolling && (bLooping || bSelectable)); m_ui.transportLoopSetAction->setEnabled( !bRolling && bSelectable); m_ui.transportStopAction->setEnabled(bPlaying); m_ui.transportRecordAction->setEnabled(pSession->recordTracks() > 0); m_ui.transportPunchAction->setEnabled(bPunching || bSelectable); m_ui.transportPunchSetAction->setEnabled(bSelectable); m_ui.transportRewindAction->setChecked(iRolling < 0); m_ui.transportFastForwardAction->setChecked(iRolling > 0); m_ui.transportLoopAction->setChecked(bLooping); m_ui.transportPlayAction->setChecked(bPlaying); m_ui.transportRecordAction->setChecked(bRecording); m_ui.transportPunchAction->setChecked(bPunching); // Special record mode settlement. m_pTimeSpinBox->setReadOnly(bRecording); m_pTempoSpinBox->setReadOnly(bRecording); // Check whether the clip is currently in loop-set... if (pMidiClip) { const unsigned long iClipStart = pMidiClip->clipStart(); const unsigned long iClipEnd = iClipStart + pMidiClip->clipLength(); m_ui.fileLoopSetAction->setChecked(bLooping && iClipStart == pSession->loopStart() && iClipEnd == pSession->loopEnd()); } } // Secondary rtime-signature status... m_pTimeSig2ResetButton->setEnabled(pMidiClip != nullptr && (pMidiClip->beatsPerBar2() > 0 || pMidiClip->beatDivisor2() > 0)); // Stabilize thumb-view... m_pMidiEditor->thumbView()->update(); m_pMidiEditor->thumbView()->updateThumb(); } // Update clip/track instrument names... void qtractorMidiEditorForm::updateInstrumentNames (void) { // Just in case... m_pEventTypeGroup->updateControlType(); const qtractorMidiEvent::EventType eventType = m_pEventTypeGroup->controlType(); m_pEventParamComboBox->setEnabled( eventType == qtractorMidiEvent::CONTROLLER || eventType == qtractorMidiEvent::REGPARAM || eventType == qtractorMidiEvent::NONREGPARAM || eventType == qtractorMidiEvent::CONTROL14); } // View/drum-mode update. void qtractorMidiEditorForm::updateDrumMode (void) { const bool bBlockSignals = m_ui.viewDrumModeAction->blockSignals(true); const bool bDrumMode = m_pMidiEditor->isDrumMode(); m_ui.viewDrumModeAction->setChecked(bDrumMode); m_pSnapToScaleKeyComboBox->setEnabled(!bDrumMode); m_pSnapToScaleTypeComboBox->setEnabled(!bDrumMode); m_ui.viewDrumModeAction->blockSignals(bBlockSignals); } // Update thumb-view play-head... void qtractorMidiEditorForm::updatePlayHead ( unsigned long iPlayHead ) { m_pTimeSpinBox->setValue(iPlayHead, false); m_pMidiEditor->thumbView()->updatePlayHead(iPlayHead); // Tricky stuff: node's non-null iif tempo changes... qtractorTimeScale *pTimeScale = m_pMidiEditor->timeScale(); qtractorTimeScale::Node *pNode = m_pTempoCursor->seek(pTimeScale, iPlayHead); if (pNode) { m_pTempoSpinBox->setTempo(pNode->tempo, false); unsigned short iBeatsPerBar = pTimeScale->beatsPerBar2(); if (iBeatsPerBar < 1) iBeatsPerBar = pNode->beatsPerBar; m_pTempoSpinBox->setBeatsPerBar(iBeatsPerBar, false); unsigned short iBeatDivisor = pTimeScale->beatDivisor2(); if (iBeatDivisor < 1) iBeatDivisor = pNode->beatDivisor; m_pTempoSpinBox->setBeatDivisor(iBeatDivisor, false); } } // Update local time-scale... void qtractorMidiEditorForm::updateTimeScale (void) { m_pTempoCursor->clear(); m_pMidiEditor->updateTimeScale(); m_pMidiEditor->updateContents(); } //------------------------------------------------------------------------- // qtractorMidiEditorForm -- Selection widget slots. // Note type view menu stabilizer. void qtractorMidiEditorForm::updateNoteTypeMenu (void) { const int iCurrentIndex = m_pViewTypeComboBox->currentIndex(); QListIterator iter(m_ui.viewNoteTypeMenu->actions()); while (iter.hasNext()) { QAction *pAction = iter.next(); pAction->setChecked(pAction->data().toInt() == iCurrentIndex); } } // Event type view menu stabilizer. void qtractorMidiEditorForm::updateValueTypeMenu (void) { const int iCurrentIndex = m_pEventTypeComboBox->currentIndex(); QListIterator iter(m_ui.viewValueTypeMenu->actions()); while (iter.hasNext()) { QAction *pAction = iter.next(); pAction->setChecked(pAction->data().toInt() == iCurrentIndex); } } // Ghost-track menu builder.. void qtractorMidiEditorForm::updateGhostTrackMenu (void) { m_ui.viewGhostTrackMenu->clear(); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorMidiClip *pMidiClip = m_pMidiEditor->midiClip(); if (pMidiClip == nullptr) return; qtractorTrack *pGhostTrack = m_pMidiEditor->ghostTrack(); QAction *pAction; QVariant data; for (qtractorTrack *pTrack = pSession->tracks().first(); pTrack; pTrack = pTrack->next()) { if (pTrack->trackType() == qtractorTrack::Midi) { pAction = m_ui.viewGhostTrackMenu->addAction( pTrack->shortTrackName(), this, SLOT(viewGhostTrack())); pAction->setCheckable(true); pAction->setChecked(pGhostTrack == pTrack); data.setValue(static_cast (pTrack)); pAction->setData(data); // pAction->setEnabled(pTrack != pMidiClip->track()); } } m_ui.viewGhostTrackMenu->addSeparator(); pAction = m_ui.viewGhostTrackMenu->addAction( tr("&None"), this, SLOT(viewGhostTrack())); pAction->setCheckable(true); pAction->setChecked(pGhostTrack == nullptr); data.setValue(0); pAction->setData(data); } // Zoom view menu stabilizer. void qtractorMidiEditorForm::updateZoomMenu (void) { const int iZoomMode = m_pMidiEditor->zoomMode(); m_ui.viewZoomHorizontalAction->setChecked( iZoomMode == qtractorMidiEditor::ZoomHorizontal); m_ui.viewZoomVerticalAction->setChecked( iZoomMode == qtractorMidiEditor::ZoomVertical); m_ui.viewZoomAllAction->setChecked( iZoomMode == qtractorMidiEditor::ZoomAll); } // Snap-per-beat view menu builder. void qtractorMidiEditorForm::updateSnapMenu (void) { m_ui.viewSnapMenu->clear(); qtractorTimeScale *pTimeScale = m_pMidiEditor->timeScale(); if (pTimeScale == nullptr) return; const int iSnapCurrent = qtractorTimeScale::indexFromSnap(pTimeScale->snapPerBeat()); int iSnap = 0; QListIterator iter(m_snapPerBeatActions); while (iter.hasNext()) { QAction *pAction = iter.next(); pAction->setChecked(iSnap == iSnapCurrent); m_ui.viewSnapMenu->addAction(pAction); ++iSnap; } m_ui.viewSnapMenu->addSeparator(); m_ui.viewSnapMenu->addAction(m_ui.viewSnapZebraAction); m_ui.viewSnapMenu->addAction(m_ui.viewSnapGridAction); } // Snap-to-scale view menu builder. void qtractorMidiEditorForm::updateScaleMenu (void) { m_ui.viewScaleMenu->clear(); const int iSnapToScaleKey = m_pMidiEditor->snapToScaleKey(); int iScaleKey = 0; QStringListIterator iter_key(qtractorMidiEditor::scaleKeyNames()); while (iter_key.hasNext()) { QAction *pAction = m_ui.viewScaleMenu->addAction( iter_key.next(), this, SLOT(viewScaleKey())); pAction->setCheckable(true); pAction->setChecked(iScaleKey == iSnapToScaleKey); pAction->setData(iScaleKey++); } m_ui.viewScaleMenu->addSeparator(); const int iSnapToScaleType = m_pMidiEditor->snapToScaleType(); int iScaleType = 0; QStringListIterator iter_type(qtractorMidiEditor::scaleTypeNames()); while (iter_type.hasNext()) { QAction *pAction = m_ui.viewScaleMenu->addAction( iter_type.next(), this, SLOT(viewScaleType())); pAction->setCheckable(true); pAction->setChecked(iScaleType == iSnapToScaleType); pAction->setData(iScaleType++); } } // Track/Instrument sub-menu stabilizers. void qtractorMidiEditorForm::updateTrackInstrumentMenu (void) { qtractorMidiClip *pMidiClip = m_pMidiEditor->midiClip(); if (pMidiClip) { m_pInstrumentMenu->updateTrackMenu( pMidiClip->track(), m_ui.fileTrackInstrumentMenu); } } // Time format custom context menu. void qtractorMidiEditorForm::transportTimeFormatChanged ( int iDisplayFormat ) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiEditorForm::transportTimeFormatChanged(%d)", iDisplayFormat); #endif const qtractorTimeScale::DisplayFormat displayFormat = qtractorTimeScale::DisplayFormat(iDisplayFormat); (m_pMidiEditor->timeScale())->setDisplayFormat(displayFormat); m_pTimeSpinBox->setDisplayFormat(displayFormat); m_pMidiEventList->refresh(); stabilizeForm(); } // Real thing: the playhead has been changed manually! void qtractorMidiEditorForm::transportTimeChanged ( unsigned long iPlayHead ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMidiEditorForm::transportTimeChanged(%lu)", iPlayHead); #endif m_pMidiEditor->setPlayHead(iPlayHead); pSession->setPlayHead(iPlayHead); } void qtractorMidiEditorForm::transportTimeFinished (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiEditorForm::transportTimeFinished()"); #endif const bool bBlockSignals = m_pTimeSpinBox->blockSignals(true); m_pTimeSpinBox->clearFocus(); m_pTimeSpinBox->blockSignals(bBlockSignals); } // Time-signature spin-box change slot. void qtractorMidiEditorForm::transportTempoChanged ( float fTempo, unsigned short iBeatsPerBar, unsigned short iBeatDivisor ) { qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; #ifdef CONFIG_DEBUG qDebug("qtractorMidiEditorForm::transportTempoChanged(%g, %u, %u)", fTempo, iBeatsPerBar, iBeatDivisor); #endif // Find appropriate node... qtractorTimeScale *pTimeScale = pSession->timeScale(); qtractorTimeScale::Cursor& cursor = pTimeScale->cursor(); qtractorTimeScale::Node *pNode = cursor.seekFrame(pSession->playHead()); // Check for local/secondary time-signature/meter changes... qtractorMidiClip *pMidiClip = midiClip(); if (pMidiClip) { if (iBeatsPerBar == pNode->beatsPerBar && iBeatDivisor == pNode->beatDivisor) { if (pMidiClip->beatsPerBar2() > 0 || pMidiClip->beatDivisor2() > 0) { resetTimeSig2(); return; } } else if (iBeatsPerBar != pMidiClip->beatsPerBar2() || iBeatDivisor != pMidiClip->beatDivisor2()) { resetTimeSig2(iBeatsPerBar, iBeatDivisor); return; } } // Now, express the change as an undoable command... m_pMidiEditor->execute( new qtractorTimeScaleUpdateNodeCommand( pTimeScale, pNode->frame, fTempo, 2, iBeatsPerBar, iBeatDivisor)); } void qtractorMidiEditorForm::transportTempoFinished (void) { #ifdef CONFIG_DEBUG qDebug("qtractorMidiEditorForm::transportTempoFinished()"); #endif const bool bBlockSignals = m_pTempoSpinBox->blockSignals(true); m_pTempoSpinBox->clearFocus(); m_pTempoSpinBox->blockSignals(bBlockSignals); } // Time-signature custom context menu. void qtractorMidiEditorForm::transportTempoContextMenu ( const QPoint& /*pos*/ ) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->viewTempoMap(); } // Secondary time-signature reset slot. void qtractorMidiEditorForm::timeSig2ResetClicked (void) { resetTimeSig2(); } // Secondary time-signature reset slot. void qtractorMidiEditorForm::resetTimeSig2 ( unsigned short iBeatsPerBar2, unsigned short iBeatDivisor2 ) { m_pMidiEditor->execute( new qtractorTimeScaleTimeSig2Command( timeScale(), midiClip(), iBeatsPerBar2, iBeatDivisor2)); } // Snap-per-beat spin-box change slot. void qtractorMidiEditorForm::snapPerBeatChanged ( int iSnap ) { qtractorTimeScale *pTimeScale = m_pMidiEditor->timeScale(); if (pTimeScale == nullptr) return; // Avoid bogus changes... const unsigned short iSnapPerBeat = qtractorTimeScale::snapFromIndex(iSnap); if (iSnapPerBeat == pTimeScale->snapPerBeat()) return; // No need to express the change as a undoable command? pTimeScale->setSnapPerBeat(iSnapPerBeat); // If showing grid, it changed a bit for sure... if (m_pMidiEditor->isSnapGrid() || m_pMidiEditor->isSnapZebra()) m_pMidiEditor->updateContents(); m_pMidiEditor->editView()->setFocus(); } // Snap-to-scale/quantize combo-box change slots. void qtractorMidiEditorForm::snapToScaleKeyChanged ( int iSnapToScaleKey ) { m_pMidiEditor->setSnapToScaleKey(iSnapToScaleKey); } void qtractorMidiEditorForm::snapToScaleTypeChanged ( int iSnapToScaleType ) { m_pMidiEditor->setSnapToScaleType(iSnapToScaleType); } // Tool selection handlers. void qtractorMidiEditorForm::viewTypeChanged ( int iIndex ) { const qtractorMidiEvent::EventType eventType = qtractorMidiEvent::EventType( m_pViewTypeComboBox->itemData(iIndex).toInt()); m_pMidiEditor->editView()->setEventType(eventType); m_pMidiEditor->updateContents(); m_pMidiEventList->refresh(); stabilizeForm(); } void qtractorMidiEditorForm::eventTypeChanged ( int iIndex ) { const qtractorMidiEvent::EventType eventType = qtractorMidiEvent::EventType( m_pEventTypeComboBox->itemData(iIndex).toInt()); m_pEventParamComboBox->setEnabled( eventType == qtractorMidiEvent::CONTROLLER || eventType == qtractorMidiEvent::REGPARAM || eventType == qtractorMidiEvent::NONREGPARAM || eventType == qtractorMidiEvent::CONTROL14); // updateInstrumentNames(); m_pMidiEditor->editEvent()->setEventType(eventType); m_pMidiEditor->updateContents(); m_pMidiEventList->refresh(); stabilizeForm(); } void qtractorMidiEditorForm::eventParamChanged ( int iParam ) { m_pMidiEditor->editEvent()->setEventParam(iParam); m_pMidiEditor->updateContents(); m_pMidiEventList->refresh(); stabilizeForm(); } void qtractorMidiEditorForm::selectionChanged ( qtractorMidiEditor *pMidiEditor ) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->selectionNotifySlot(pMidiEditor); stabilizeForm(); } void qtractorMidiEditorForm::contentsChanged ( qtractorMidiEditor *pMidiEditor ) { ++m_iDirtyCount; m_pTempoCursor->clear(); selectionChanged(pMidiEditor); } // Top-level window geometry related slots. void qtractorMidiEditorForm::posChanged (void) { if (isVisible()) { qtractorMidiClip *pMidiClip = midiClip(); if (pMidiClip) pMidiClip->setEditorPos(pos()); } } void qtractorMidiEditorForm::sizeChanged (void) { if (isVisible()) { qtractorMidiClip *pMidiClip = midiClip(); if (pMidiClip) pMidiClip->setEditorSize(size()); } } // end of qtractorMidiEditorForm.cpp qtractor-1.5.9/src/PaxHeaders/qtractorAbout.h0000644000000000000000000000013215101070305016225 xustar0030 mtime=1761898693.062554172 30 atime=1761898693.062554172 30 ctime=1761898693.062554172 qtractor-1.5.9/src/qtractorAbout.h0000644000175000001440000000246515101070305016224 0ustar00rncbcusers// qtractorAbout.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 __qtractorAbout_h #define __qtractorAbout_h #include "config.h" #define QTRACTOR_TITLE PROJECT_TITLE #define QTRACTOR_SUBTITLE PROJECT_DESCRIPTION #define QTRACTOR_WEBSITE PROJECT_HOMEPAGE_URL #define QTRACTOR_COPYRIGHT PROJECT_COPYRIGHT #define QTRACTOR_DOMAIN PROJECT_DOMAIN #endif // __qtractorAbout_h // end of qtractorAbout.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiCursor.cpp0000644000000000000000000000013215101070305017566 xustar0030 mtime=1761898693.079267629 30 atime=1761898693.078267626 30 ctime=1761898693.079267629 qtractor-1.5.9/src/qtractorMidiCursor.cpp0000644000175000001440000000544415101070305017565 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.9/src/PaxHeaders/qtractorTrackCommand.cpp0000644000000000000000000000013215101070305020051 xustar0030 mtime=1761898693.090267664 30 atime=1761898693.090267664 30 ctime=1761898693.090267664 qtractor-1.5.9/src/qtractorTrackCommand.cpp0000644000175000001440000011463315101070305020051 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.9/src/PaxHeaders/qtractorFileList.cpp0000644000000000000000000000013215101070305017221 xustar0030 mtime=1761898693.071267604 30 atime=1761898693.071267604 30 ctime=1761898693.071267604 qtractor-1.5.9/src/qtractorFileList.cpp0000644000175000001440000001275515101070305017223 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.9/src/PaxHeaders/qtractorMixer.cpp0000644000000000000000000000013215101070305016572 xustar0030 mtime=1761898693.084267645 30 atime=1761898693.084267645 30 ctime=1761898693.084267645 qtractor-1.5.9/src/qtractorMixer.cpp0000644000175000001440000014364415101070305016576 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.9/src/PaxHeaders/qtractorMidiManager.cpp0000644000000000000000000000013215101070305017663 xustar0030 mtime=1761898693.083267642 30 atime=1761898693.082267639 30 ctime=1761898693.083267642 qtractor-1.5.9/src/qtractorMidiManager.cpp0000644000175000001440000012771315101070305017666 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.9/src/PaxHeaders/qtractorAudioFile.h0000644000000000000000000000013215101070305017014 xustar0030 mtime=1761898693.063267578 30 atime=1761898693.063267578 30 ctime=1761898693.063267578 qtractor-1.5.9/src/qtractorAudioFile.h0000644000175000001440000001020115101070305016776 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.9/src/PaxHeaders/qtractorTrack.h0000644000000000000000000000013215101070305016217 xustar0030 mtime=1761898693.090267664 30 atime=1761898693.090267664 30 ctime=1761898693.090267664 qtractor-1.5.9/src/qtractorTrack.h0000644000175000001440000003231315101070305016211 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.9/src/PaxHeaders/qtractorMidiSequence.cpp0000644000000000000000000000013215101070305020061 xustar0030 mtime=1761898693.083267642 30 atime=1761898693.083267642 30 ctime=1761898693.083267642 qtractor-1.5.9/src/qtractorMidiSequence.cpp0000644000175000001440000001565215101070305020062 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.9/src/PaxHeaders/qtractorPluginListView.cpp0000644000000000000000000000013215101070305020433 xustar0030 mtime=1761898693.087267654 30 atime=1761898693.087267654 30 ctime=1761898693.087267654 qtractor-1.5.9/src/qtractorPluginListView.cpp0000644000175000001440000017330715101070305020436 0ustar00rncbcusers// qtractorPluginListView.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 "qtractorPluginListView.h" #include "qtractorPluginFactory.h" #include "qtractorPluginCommand.h" #include "qtractorPluginSelectForm.h" #include "qtractorPluginForm.h" #include "qtractorRubberBand.h" #include "qtractorSession.h" #include "qtractorOptions.h" #include "qtractorMainForm.h" #include "qtractorAudioEngine.h" #include "qtractorMidiEngine.h" #include "qtractorMidiManager.h" #include "qtractorConnections.h" #include "qtractorInsertPlugin.h" #include "qtractorMidiControlPlugin.h" #include "qtractorMixer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) #include #include #endif //---------------------------------------------------------------------------- // qtractorPluginListView::TinyScrollBarStyle -- Custom tiny scrollbar style. // #if QT_VERSION < QT_VERSION_CHECK(4, 6, 0) #include #else #include class QCDEStyle : public QProxyStyle {}; #endif class qtractorPluginListView::TinyScrollBarStyle : public QCDEStyle { protected: // Custom virtual override. int pixelMetric(PixelMetric pm, const QStyleOption *option = 0, const QWidget *pWidget = 0) const { if (pm == QStyle::PM_ScrollBarExtent) return 8; return QCDEStyle::pixelMetric(pm, option, pWidget); } }; //---------------------------------------------------------------------------- // qtractorPluginListItemDelegate -- Plugin list view item delgate. class qtractorPluginListItemDelegate : public QItemDelegate { public: // Constructor. qtractorPluginListItemDelegate(QListWidget *pListWidget) : QItemDelegate(pListWidget), m_pListWidget(pListWidget) {} protected: // Overridden paint method. void paint(QPainter *pPainter, const QStyleOptionViewItem& option, const QModelIndex& index) const { qtractorPluginListItem *pItem = static_cast ( m_pListWidget->item(index.row())); // nb. Unselectable items get special grayed out painting... if (pItem) { pPainter->save(); const QPalette& pal = option.palette; QColor rgbBack; QColor rgbFore; if (option.state & QStyle::State_HasFocus) { rgbBack = pal.highlight().color(); rgbFore = pal.highlightedText().color(); } else { rgbBack = pal.window().color(); rgbFore = pal.windowText().color(); } // Fill the background... pPainter->fillRect(option.rect, rgbBack); const QSize& iconSize = m_pListWidget->iconSize(); // Draw the direct access parameter value status... QPolygon polyg(3); qtractorPlugin *pPlugin = pItem->plugin(); qtractorPlugin::Param *pDirectAccessParam = nullptr; qtractorMidiControlObserver *pDirectAccessObserver = nullptr; if (pPlugin) pDirectAccessParam = pPlugin->directAccessParam(); if (pDirectAccessParam) pDirectAccessObserver = pDirectAccessParam->observer(); if (pDirectAccessObserver) { const float fScale = pDirectAccessObserver->scaleFromValue( pDirectAccessParam->value(), pDirectAccessParam->isLogarithmic()); QRect rectValue = option.rect .adjusted(iconSize.width(), 1, -2, -2); const int iDirectAccessWidth = rectValue.width(); pItem->setDirectAccessWidth(iDirectAccessWidth); rectValue.setWidth(int(fScale * float(iDirectAccessWidth))); pPainter->fillRect(rectValue, rgbBack.lighter(140)); polyg.setPoint(0, rectValue.right(), rectValue.top() + 1); polyg.setPoint(1, rectValue.right() - 2, rectValue.bottom() + 1); polyg.setPoint(2, rectValue.right() + 2, rectValue.bottom() + 1); } // Draw the icon... QRect rect = option.rect; pPainter->drawPixmap(rect.left() + 2, rect.top() + ((rect.height() - iconSize.height()) >> 1), pItem->icon().pixmap(iconSize)); // Draw the text... rect.setLeft(iconSize.width() + 4); pPainter->setPen(rgbFore); pPainter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, pItem->text()); // Draw frame lines... pPainter->setPen(rgbBack.lighter(150)); pPainter->drawLine( option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top()); pPainter->setPen(rgbBack.darker(150)); pPainter->drawLine( option.rect.left(), option.rect.bottom(), option.rect.right(), option.rect.bottom()); if (pDirectAccessObserver) { pPainter->setRenderHint(QPainter::Antialiasing, true); pPainter->setBrush(rgbBack.darker(160)); pPainter->drawPolygon(polyg); pPainter->setPen(rgbBack.lighter(180)); pPainter->drawLine(polyg.at(0), polyg.at(1)); pPainter->setRenderHint(QPainter::Antialiasing, false); } pPainter->restore(); // if (option.state & QStyle::State_HasFocus) // QItemDelegate::drawFocus(pPainter, option, option.rect); } else { // Others do as default... QItemDelegate::paint(pPainter, option, index); } } // Override height-hint. QSize sizeHint ( const QStyleOptionViewItem& option, const QModelIndex& index ) const { QSize size(QItemDelegate::sizeHint(option, index)); size.setHeight(qMax(size.height(), 16)); return size; } private: QListWidget *m_pListWidget; }; //---------------------------------------------------------------------------- // qtractorPluginListItem -- Plugins list item. // int qtractorPluginListItem::g_iIconsRefCount = 0; QIcon *qtractorPluginListItem::g_pIcons[3] = { nullptr, nullptr, nullptr }; // Constructors. qtractorPluginListItem::qtractorPluginListItem ( qtractorPlugin *pPlugin ) : QListWidgetItem(), m_pPlugin(pPlugin), m_iDirectAccessWidth(0) { if (++g_iIconsRefCount == 1) { g_pIcons[0] = new QIcon(QIcon::fromTheme("itemLedOff")); g_pIcons[1] = new QIcon(QIcon::fromTheme("itemLedOn")); g_pIcons[2] = new QIcon(QIcon::fromTheme("itemLedDim")); } m_pPlugin->addItem(this); QListWidgetItem::setText(m_pPlugin->title()); updateActivated(); } // Destructor. qtractorPluginListItem::~qtractorPluginListItem (void) { m_pPlugin->removeItem(this); if (--g_iIconsRefCount == 0) { for (int i = 0; i < 3; ++i) { delete g_pIcons[i]; g_pIcons[i] = nullptr; } } } // Plugin container accessor. qtractorPlugin *qtractorPluginListItem::plugin (void) const { return m_pPlugin; } // Activation methods. void qtractorPluginListItem::updateActivated (void) { int index = 0; if (m_pPlugin) { if (m_pPlugin->isAutoActivated()) index = 1; else if (m_pPlugin->isAutoDeactivated()) index = 2; } QListWidgetItem::setIcon(*g_pIcons[index]); } //---------------------------------------------------------------------------- // qtractorPluginListView -- Plugin chain list widget instance. // // Construcctor. qtractorPluginListView::qtractorPluginListView ( QWidget *pParent ) : QListWidget(pParent), m_pPluginList(nullptr), m_pClickedItem(nullptr) { // Drag-and-drop stuff. m_dragCursor = DragNone; m_dragState = DragNone; m_pDragItem = nullptr; m_pDropItem = nullptr; m_pRubberBand = nullptr; // Common tiny scrollbar style stuff. m_pTinyScrollBarStyle = nullptr; // To track the current item... m_pCurrentItem = nullptr; // QListWidget::setDragEnabled(true); QListWidget::setAcceptDrops(true); QListWidget::setDropIndicatorShown(true); QListWidget::setAutoScroll(true); QListWidget::setIconSize(QSize(8, 8)); QListWidget::setItemDelegate(new qtractorPluginListItemDelegate(this)); QListWidget::setSelectionMode(QAbstractItemView::SingleSelection); QListWidget::setMouseTracking(true); QListWidget::viewport()->setBackgroundRole(QPalette::Window); QListWidget::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // QListWidget::setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); QListWidget::setFrameShape(QFrame::Panel); QListWidget::setFrameShadow(QFrame::Sunken); // Trap for help/tool-tips events. QListWidget::viewport()->installEventFilter(this); // Double/simple-click handling... QObject::connect(this, SIGNAL(itemDoubleClicked(QListWidgetItem*)), SLOT(itemDoubleClickedSlot(QListWidgetItem*))); QObject::connect(this, SIGNAL(itemClicked(QListWidgetItem*)), SLOT(itemClickedSlot(QListWidgetItem*))); QObject::connect(this, SIGNAL(currentRowChanged(int)), SLOT(currentRowChangedSlot(int))); } // Destructor. qtractorPluginListView::~qtractorPluginListView (void) { // No need to delete child widgets, Qt does it all for us clear(); setPluginList(nullptr); if (m_pTinyScrollBarStyle) { delete m_pTinyScrollBarStyle; m_pTinyScrollBarStyle = nullptr; } } // Plugin list accessors. void qtractorPluginListView::setPluginList ( qtractorPluginList *pPluginList ) { if (m_pPluginList) m_pPluginList->removeView(this); m_pPluginList = pPluginList; if (m_pPluginList) m_pPluginList->addView(this); refresh(); } qtractorPluginList *qtractorPluginListView::pluginList (void) const { return m_pPluginList; } // Special scrollbar style accessors. void qtractorPluginListView::setTinyScrollBar ( bool bTinyScrollBar ) { if (bTinyScrollBar) { m_pTinyScrollBarStyle = new TinyScrollBarStyle(); QListWidget::verticalScrollBar()->setStyle(m_pTinyScrollBarStyle); QListWidget::horizontalScrollBar()->setStyle(m_pTinyScrollBarStyle); } else { QListWidget::verticalScrollBar()->setStyle(nullptr); QListWidget::horizontalScrollBar()->setStyle(nullptr); if (m_pTinyScrollBarStyle) { delete m_pTinyScrollBarStyle; m_pTinyScrollBarStyle = nullptr; } } } // Plugin list refreshner void qtractorPluginListView::refresh (void) { clear(); if (m_pPluginList) { QListWidget::setUpdatesEnabled(false); for (qtractorPlugin *pPlugin = m_pPluginList->first(); pPlugin; pPlugin = pPlugin->next()) { QListWidget::addItem(new qtractorPluginListItem(pPlugin)); pPlugin->updateFormActivated(); } QListWidget::setUpdatesEnabled(true); } } // Master clean-up. void qtractorPluginListView::clear (void) { m_pCurrentItem = nullptr; dragLeaveEvent(nullptr); QListWidget::clear(); } // Get an item index, given the plugin reference... int qtractorPluginListView::pluginItem ( qtractorPlugin *pPlugin ) { const int iItemCount = QListWidget::count(); for (int iItem = 0; iItem < iItemCount; ++iItem) { qtractorPluginListItem *pItem = static_cast (QListWidget::item(iItem)); if (pItem && pItem->plugin() == pPlugin) return iItem; } return -1; } // Move item on list. void qtractorPluginListView::moveItem ( qtractorPluginListItem *pItem, qtractorPluginListItem *pNextItem ) { if (pItem == nullptr) return; if (m_pPluginList == nullptr) return; // The plugin to be moved... qtractorPlugin *pPlugin = pItem->plugin(); if (pPlugin == nullptr) return; // To be after this one... qtractorPlugin *pNextPlugin = nullptr; if (pNextItem) pNextPlugin = pNextItem->plugin(); // Make it an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; pSession->execute( new qtractorMovePluginCommand(pPlugin, pNextPlugin, m_pPluginList)); emit contentsChanged(); } // Copy item on list. void qtractorPluginListView::copyItem ( qtractorPluginListItem *pItem, qtractorPluginListItem *pNextItem ) { if (pItem == nullptr) return; if (m_pPluginList == nullptr) return; // The plugin to be moved... qtractorPlugin *pPlugin = pItem->plugin(); // Clone/copy the new plugin here... if (pPlugin) pPlugin = m_pPluginList->copyPlugin(pPlugin); if (pPlugin == nullptr) return; // To be after this one... qtractorPlugin *pNextPlugin = nullptr; if (pNextItem) pNextPlugin = pNextItem->plugin(); // Make it an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; pSession->execute( new qtractorInsertPluginCommand( tr("copy plugin"), pPlugin, pNextPlugin)); emit contentsChanged(); } // Add a new plugin slot. void qtractorPluginListView::addPlugin (void) { if (m_pPluginList == nullptr) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->autoSaveAsap(); qtractorPluginSelectForm selectForm(this); selectForm.setPluginList(m_pPluginList); if (!selectForm.exec()) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; bool bOpenEditor = false; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) bOpenEditor = pOptions->bOpenEditor; // Tell the world we'll take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Make it a undoable command... qtractorAddPluginCommand *pAddPluginCommand = new qtractorAddPluginCommand(); for (int i = 0; i < selectForm.pluginCount(); ++i) { // Add an actual plugin item... qtractorPlugin *pPlugin = qtractorPluginFactory::createPlugin(m_pPluginList, selectForm.pluginFilename(i), selectForm.pluginIndex(i), selectForm.pluginTypeHint(i)); if (pPlugin) { pPlugin->setActivated(true); pAddPluginCommand->addPlugin(pPlugin); } } // Instantiate selected plugins... pSession->execute(pAddPluginCommand); // We're formerly done. QApplication::restoreOverrideCursor(); // Show plugin forms/editors right away... QListIterator iter(pAddPluginCommand->plugins()); while (iter.hasNext()) { qtractorPlugin *pPlugin = iter.next(); if (bOpenEditor && (pPlugin->type())->isEditor()) pPlugin->openEditor(); else pPlugin->openForm(); } emit contentsChanged(); } // Remove an existing plugin slot. void qtractorPluginListView::removePlugin (void) { if (m_pPluginList == nullptr) return; qtractorPluginListItem *pItem = static_cast (QListWidget::currentItem()); if (pItem == nullptr) return; qtractorPlugin *pPlugin = pItem->plugin(); if (pPlugin == nullptr) return; // Make it a undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; pSession->execute(new qtractorRemovePluginCommand(pPlugin)); if (m_pCurrentItem == pItem) m_pCurrentItem = nullptr; emit contentsChanged(); } // Activate existing plugin slot. void qtractorPluginListView::activatePlugin (void) { qtractorPluginListItem *pItem = static_cast (QListWidget::currentItem()); if (pItem == nullptr) return; qtractorPlugin *pPlugin = pItem->plugin(); if (pPlugin == nullptr) return; // Make it a undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; pSession->execute( new qtractorActivatePluginCommand(pPlugin, !pPlugin->isActivated())); emit contentsChanged(); } // Activate all plugins. void qtractorPluginListView::activateAllPlugins (void) { if (m_pPluginList == nullptr) return; // Check whether everyone is already activated... if (m_pPluginList->count() < 1 || m_pPluginList->isActivatedAll()) return; // Make it an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorActivatePluginCommand *pActivateAllCommand = new qtractorActivatePluginCommand(nullptr, true); pActivateAllCommand->setName(tr("activate all plugins")); for (qtractorPlugin *pPlugin = m_pPluginList->first(); pPlugin; pPlugin = pPlugin->next()) { if (!pPlugin->isActivated()) pActivateAllCommand->addPlugin(pPlugin); } pSession->execute(pActivateAllCommand); emit contentsChanged(); } // Dectivate all plugins. void qtractorPluginListView::deactivateAllPlugins (void) { if (m_pPluginList == nullptr) return; // Check whether everyone is already dectivated... if (!m_pPluginList->isActivated()) return; // Make it an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorActivatePluginCommand *pDeactivateAllCommand = new qtractorActivatePluginCommand(nullptr, false); pDeactivateAllCommand->setName(tr("deactivate all plugins")); for (qtractorPlugin *pPlugin = m_pPluginList->first(); pPlugin; pPlugin = pPlugin->next()) { if (pPlugin->isActivated()) pDeactivateAllCommand->addPlugin(pPlugin); } pSession->execute(pDeactivateAllCommand); emit contentsChanged(); } // Remove all plugins. void qtractorPluginListView::removeAllPlugins (void) { if (m_pPluginList == nullptr) return; // Check whether there's any... if (m_pPluginList->count() < 1) return; // Make it an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorRemovePluginCommand *pRemoveAllCommand = new qtractorRemovePluginCommand(); pRemoveAllCommand->setName(tr("remove all plugins")); for (qtractorPlugin *pPlugin = m_pPluginList->first(); pPlugin; pPlugin = pPlugin->next()) { pRemoveAllCommand->addPlugin(pPlugin); } pSession->execute(pRemoveAllCommand); emit contentsChanged(); } // Move an existing plugin upward slot. void qtractorPluginListView::moveUpPlugin (void) { if (m_pPluginList == nullptr) return; qtractorPluginListItem *pItem = static_cast (QListWidget::currentItem()); if (pItem == nullptr) return; const int iNextItem = QListWidget::row(pItem) - 1; if (iNextItem < 0) return; qtractorPluginListItem *pNextItem = static_cast ( QListWidget::item(iNextItem)); if (pNextItem == nullptr) return; moveItem(pItem, pNextItem); } // Move an existing plugin downward slot. void qtractorPluginListView::moveDownPlugin (void) { if (m_pPluginList == nullptr) return; qtractorPluginListItem *pItem = static_cast (QListWidget::currentItem()); if (pItem == nullptr) return; const int iNextItem = QListWidget::row(pItem) + 1; if (iNextItem >= QListWidget::count()) return; qtractorPluginListItem *pNextItem = static_cast ( QListWidget::item(iNextItem + 1)); moveItem(pItem, pNextItem); } // Load a plugin preset name. void qtractorPluginListView::loadPresetPlugin (void) { if (m_pPluginList == nullptr) return; qtractorPluginListItem *pItem = static_cast (QListWidget::currentItem()); if (pItem == nullptr) return; qtractorPlugin *pPlugin = pItem->plugin(); if (pPlugin == nullptr) return; // Retrieve preset-name from action text... QAction *pAction = qobject_cast (sender()); if (pAction == nullptr) return; const QString sPreset = pAction->text().remove('&'); if (sPreset.isEmpty()) return; pPlugin->loadPresetEx(sPreset); } // Select a direct access parameter index. void qtractorPluginListView::directAccessPlugin (void) { if (m_pPluginList == nullptr) return; qtractorPluginListItem *pItem = static_cast (QListWidget::currentItem()); if (pItem == nullptr) return; qtractorPlugin *pPlugin = pItem->plugin(); if (pPlugin == nullptr) return; // Retrieve direct access parameter index from action data... QAction *pAction = qobject_cast (sender()); if (pAction == nullptr) return; const int iDirectAccessParamIndex = pAction->data().toInt(); // Make it a undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; pSession->execute( new qtractorDirectAccessParamCommand(pPlugin, iDirectAccessParamIndex)); } // Show/hide an existing plugin form slot. void qtractorPluginListView::propertiesPlugin (void) { if (m_pPluginList == nullptr) return; qtractorPluginListItem *pItem = static_cast (QListWidget::currentItem()); if (pItem == nullptr) return; qtractorPlugin *pPlugin = pItem->plugin(); if (pPlugin == nullptr) return; if (pPlugin->isFormVisible()) pPlugin->closeForm(); else pPlugin->openForm(); } // Show/hide an existing plugin editor (GUI) slot. void qtractorPluginListView::editPlugin (void) { if (m_pPluginList == nullptr) return; qtractorPluginListItem *pItem = static_cast (QListWidget::currentItem()); if (pItem == nullptr) return; qtractorPlugin *pPlugin = pItem->plugin(); if (pPlugin == nullptr) return; if (pPlugin->isEditorVisible()) pPlugin->closeEditor(); else pPlugin->openEditor(); } // Import plugin-list slot. void qtractorPluginListView::importPlugins (void) { if (m_pPluginList == nullptr) return; // We'll need this, sure. qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; // Default file-name extension (suffix)... const QString sExt("xml"); // We'll assume that there's an external file... QString sFilename; // Construct the import-from-file dialog... const QString& sTitle = tr("Import Plugins"); QStringList filters; filters.append(tr("XML 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... sFilename = QFileDialog::getOpenFileName(pParentWidget, sTitle, pOptions->sPluginsDir, sFilter, nullptr, options); #else // Construct open-file dialog... QFileDialog fileDialog(pParentWidget, sTitle, pOptions->sPluginsDir, 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->sPluginsDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sFilename = fileDialog.selectedFiles().first(); #endif // Do we have any? if (sFilename.isEmpty()) return; // Save chosen directory as default... pOptions->sPluginsDir = QFileInfo(sFilename).absolutePath(); // Maybe ask whether we may actually reset the current list... if (m_pPluginList->count() > 0 && pOptions->bConfirmRemove) { if (QMessageBox::warning(this, tr("Warning"), tr("About to remove and import all plugins:\n\n" "\"%1\"\n\n" "Are you sure?") .arg(QFileInfo(sFilename).fileName()), QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Cancel) { return; } } // Tell the world we'll take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Import plugin-list state from XML document... // QDomDocument doc("qtractorPluginList"); qtractorPluginList::Document(&doc, m_pPluginList).load(sFilename); // We're formerly done. QApplication::restoreOverrideCursor(); emit contentsChanged(); } // Export plugin-list slot. void qtractorPluginListView::exportPlugins (void) { if (m_pPluginList == nullptr) return; // Check whether there's any... if (m_pPluginList->count() < 1) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // We'll need this, sure. qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions == nullptr) return; // The default file-name suffix/extension... const QString sExt("xml"); // Suggest a brand new filename ... QString sPluginsName = qtractorSession::sanitize(pSession->sessionName()); if (!sPluginsName.isEmpty()) sPluginsName += '-'; sPluginsName += qtractorSession::sanitize(m_pPluginList->name()); // If there are any existing, similar file-names, // add and increment a numeric version suffix... QFileInfo fi(pOptions->sPluginsDir, sPluginsName + '.' + sExt); int iFileNo = 0; while (fi.exists()) { fi.setFile(pOptions->sPluginsDir, sPluginsName + '-' + QString::number(++iFileNo) + '.' + sExt); } // Got that... QString sFilename = fi.absoluteFilePath(); // Construct the export-to-file dialog... const QString& sTitle = tr("Export Plugins"); QStringList filters; filters.append(tr("XML 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::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->sPluginsDir)); fileDialog.setSidebarUrls(urls); fileDialog.setOptions(options); // Show dialog... if (fileDialog.exec()) sFilename = fileDialog.selectedFiles().first(); else sFilename.clear(); #endif // Do we have any? if (sFilename.isEmpty()) return; // Save chosen directory as default... pOptions->sPluginsDir = QFileInfo(sFilename).absolutePath(); #ifdef CONFIG_DEBUG qDebug("qtractorPluginListView::exportPlugins(\"%s\")", sFilename.toUtf8().constData()); #endif // Tell the world we'll take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Export plugin-list state to XML document... // QDomDocument doc("qtractorPluginList"); qtractorPluginList::Document(&doc, m_pPluginList).save(sFilename); // We're formerly done. QApplication::restoreOverrideCursor(); } // Add an audio-insert pseudo-plugin slot. void qtractorPluginListView::addAudioInsertPlugin (void) { if (m_pPluginList == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Tell the world we'll take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Create our special pseudo-plugin type... qtractorPlugin *pPlugin = qtractorInsertPluginType::createPlugin( m_pPluginList, m_pPluginList->channels()); if (pPlugin) { // Make it a undoable command... pSession->execute(new qtractorAddInsertPluginCommand(pPlugin)); // Show the plugin form right away... pPlugin->openForm(); } // We're formerly done. QApplication::restoreOverrideCursor(); emit contentsChanged(); } // Add an audio-insert pseudo-plugin slot. void qtractorPluginListView::addAudioAuxSendPlugin (void) { if (m_pPluginList == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Tell the world we'll take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Create our special pseudo-plugin type... qtractorPlugin *pPlugin = qtractorAuxSendPluginType::createPlugin( m_pPluginList, m_pPluginList->channels()); if (pPlugin) { // Make it a undoable command... pSession->execute(new qtractorAddAuxSendPluginCommand(pPlugin)); // Show the plugin form right away... pPlugin->openForm(); } // We're formerly done. QApplication::restoreOverrideCursor(); emit contentsChanged(); } // Add a MIDI-insert pseudo-plugin slot. void qtractorPluginListView::addMidiInsertPlugin (void) { if (m_pPluginList == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Tell the world we'll take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Create our special pseudo-plugin type... qtractorPlugin *pPlugin = qtractorInsertPluginType::createPlugin(m_pPluginList, 0); // MIDI! if (pPlugin) { // Make it a undoable command... pSession->execute(new qtractorAddInsertPluginCommand(pPlugin)); // Show the plugin form right away... pPlugin->openForm(); } // We're formerly done. QApplication::restoreOverrideCursor(); emit contentsChanged(); } // Add a MIDI-insert pseudo-plugin slot. void qtractorPluginListView::addMidiAuxSendPlugin (void) { if (m_pPluginList == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Tell the world we'll take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Create our special pseudo-plugin type... qtractorPlugin *pPlugin = qtractorAuxSendPluginType::createPlugin(m_pPluginList, 0); // MIDI! if (pPlugin) { // Make it a undoable command... pSession->execute(new qtractorAddAuxSendPluginCommand(pPlugin)); // Show the plugin form right away... pPlugin->openForm(); } // We're formerly done. QApplication::restoreOverrideCursor(); emit contentsChanged(); } // Add a MIDI Controller pseudo-plugin slot. void qtractorPluginListView::addMidiControlPlugin (void) { if (m_pPluginList == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // Tell the world we'll take some time... QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); // Create our special pseudo-plugin type... qtractorPlugin *pPlugin = qtractorMidiControlPluginType::createPlugin(m_pPluginList); if (pPlugin) { // Make it a undoable command... pSession->execute(new qtractorAddMidiControlPluginCommand(pPlugin)); // Show the plugin form right away... pPlugin->openForm(); } // We're formerly done. QApplication::restoreOverrideCursor(); emit contentsChanged(); } // Send/return insert specific slots. void qtractorPluginListView::insertPluginOutputs (void) { insertPluginBus(qtractorBus::Output); } void qtractorPluginListView::insertPluginInputs (void) { insertPluginBus(qtractorBus::Input); } // Show insert pseudo-plugin audio bus connections. void qtractorPluginListView::insertPluginBus ( int iBusMode ) { qtractorPluginListItem *pItem = static_cast (QListWidget::currentItem()); if (pItem == nullptr) return; insertPluginBus(pItem->plugin(), iBusMode); } // Show insert pseudo-plugin audio bus connections (static) void qtractorPluginListView::insertPluginBus ( qtractorPlugin *pPlugin, int iBusMode ) { if (pPlugin == nullptr) return; qtractorPluginType *pType = pPlugin->type(); if (pType->typeHint() == qtractorPluginType::Insert) { // Might be either an audio or MIDI insert pseudo-plugin... qtractorBus *pInsertPluginBus = nullptr; if (pType->index() > 0) { qtractorAudioInsertPlugin *pAudioInsertPlugin = static_cast (pPlugin); if (pAudioInsertPlugin) pInsertPluginBus = pAudioInsertPlugin->audioBus(); } else { qtractorMidiInsertPlugin *pMidiInsertPlugin = static_cast (pPlugin); if (pMidiInsertPlugin) pInsertPluginBus = pMidiInsertPlugin->midiBus(); } // Show insert bus connections... if (pInsertPluginBus) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm && pMainForm->connections()) { (pMainForm->connections())->showBus( pInsertPluginBus, qtractorBus::BusMode(iBusMode)); } } } else if (pType->typeHint() == qtractorPluginType::Control) { // Should be a MIDI controller pseudo-plugin... qtractorBus *pMidiControlPluginBus = nullptr; qtractorMidiControlPlugin *pMidiControlPlugin = static_cast (pPlugin); if (pMidiControlPlugin) pMidiControlPluginBus = pMidiControlPlugin->midiBus(); // Show MIDI Controller send-bus connections... if (pMidiControlPluginBus) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm && pMainForm->connections()) { (pMainForm->connections())->showBus( pMidiControlPluginBus, qtractorBus::BusMode(iBusMode)); } } } } // Show selected Aux-Send bus on the mixer outputs pane. [static] void qtractorPluginListView::updateAuxSendPluginBus ( qtractorPlugin *pPlugin ) { if (pPlugin == nullptr) return; qtractorPluginType *pType = pPlugin->type(); if (pType == nullptr) return; if (pType->typeHint() != qtractorPluginType::AuxSend) return; qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm == nullptr) return; qtractorMixer *pMixer = pMainForm->mixer(); if (pMixer == nullptr) return; if (pType->index() > 0) { // index == channels > 0 => Audio aux-send. qtractorAudioAuxSendPlugin *pAudioAuxSendPlugin = static_cast (pPlugin); if (pAudioAuxSendPlugin) pMixer->setSelectedOutputBus(pAudioAuxSendPlugin->audioBus()); } else { qtractorMidiAuxSendPlugin *pMidiAuxSendPlugin = static_cast (pPlugin); if (pMidiAuxSendPlugin) pMixer->setSelectedOutputBus(pMidiAuxSendPlugin->midiBus()); } } // Audio specific slots. void qtractorPluginListView::audioOutputs (void) { qtractorMidiManager *pMidiManager = m_pPluginList->midiManager(); if (pMidiManager && pMidiManager->audioOutputBus()) { qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm && pMainForm->connections()) { (pMainForm->connections())->showBus( pMidiManager->audioOutputBus(), qtractorBus::Output); } } } void qtractorPluginListView::audioOutputBus (void) { qtractorMidiManager *pMidiManager = m_pPluginList->midiManager(); if (pMidiManager == nullptr) return; // Make it an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; pSession->execute( new qtractorAudioOutputBusCommand(pMidiManager, !pMidiManager->isAudioOutputBus(), // Toggle! pMidiManager->isAudioOutputAutoConnect(), pMidiManager->audioOutputBusName())); emit contentsChanged(); } void qtractorPluginListView::audioOutputBusName (void) { QAction *pAction = qobject_cast (sender()); if (pAction == nullptr) return; const QString sAudioOutputBusName = pAction->text().remove('&'); if (sAudioOutputBusName.isEmpty()) return; qtractorMidiManager *pMidiManager = m_pPluginList->midiManager(); if (pMidiManager == nullptr) return; // Make it an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; pSession->execute( new qtractorAudioOutputBusCommand(pMidiManager, false, // Turn dedicated audio bus off! pMidiManager->isAudioOutputAutoConnect(), sAudioOutputBusName)); emit contentsChanged(); } void qtractorPluginListView::audioOutputAutoConnect (void) { qtractorMidiManager *pMidiManager = m_pPluginList->midiManager(); if (pMidiManager == nullptr) return; // Make it an undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; pSession->execute( new qtractorAudioOutputBusCommand(pMidiManager, pMidiManager->isAudioOutputBus(), !pMidiManager->isAudioOutputAutoConnect(), // Toggle! pMidiManager->audioOutputBusName())); emit contentsChanged(); } void qtractorPluginListView::midiControlAutoConnect (void) { qtractorPluginListItem *pItem = static_cast (QListWidget::currentItem()); if (pItem == nullptr) return; qtractorPlugin *pPlugin = pItem->plugin(); if (pPlugin == nullptr) return; qtractorPluginType *pType = pPlugin->type(); if (pType == nullptr) return; if (pType->typeHint() == qtractorPluginType::Control && pType->index() == 0) { qtractorMidiControlPlugin *pMidiControlPlugin = static_cast (pPlugin); if (pMidiControlPlugin) { pMidiControlPlugin->setControlAutoConnect( !pMidiControlPlugin->isControlAutoConnect()); // Toggle! pMidiControlPlugin->updateFormMidiControlAutoConnect(); } } } // Double-click handler. void qtractorPluginListView::itemDoubleClickedSlot ( QListWidgetItem *item ) { if (m_pPluginList == nullptr) return; qtractorPluginListItem *pItem = static_cast (item); if (pItem == nullptr) return; qtractorPlugin *pPlugin = pItem->plugin(); if (pPlugin == nullptr) return; bool bOpenEditor = false; qtractorOptions *pOptions = qtractorOptions::getInstance(); if (pOptions) bOpenEditor = pOptions->bOpenEditor; if (QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) bOpenEditor = !bOpenEditor; if (bOpenEditor && (pPlugin->type())->isEditor()) pPlugin->openEditor(); else pPlugin->openForm(); } // Simple-click handler. void qtractorPluginListView::itemClickedSlot ( QListWidgetItem *item ) { if (m_pPluginList == nullptr) return; if (m_pPluginList->flags() & qtractorPluginList::Bus) return; qtractorPluginListItem *pItem = static_cast (item); if (pItem == nullptr) return; if (m_pCurrentItem == pItem) return; updateAuxSendPluginBus(pItem->plugin()); m_pCurrentItem = pItem; } // Row-change handler. void qtractorPluginListView::currentRowChangedSlot ( int iCurrentRow ) { itemClickedSlot(QListWidget::item(iCurrentRow)); } // Focus-in handler. void qtractorPluginListView::focusInEvent ( QFocusEvent *pFocusEvent ) { QListWidget::focusInEvent(pFocusEvent); m_pCurrentItem = nullptr; itemClickedSlot(QListWidget::currentItem()); } // Trap for help/tool-tip events. bool qtractorPluginListView::eventFilter ( QObject *pObject, QEvent *pEvent ) { QWidget *pViewport = QListWidget::viewport(); if (static_cast (pObject) == pViewport) { if (pEvent->type() == QEvent::ToolTip) { QHelpEvent *pHelpEvent = static_cast (pEvent); if (pHelpEvent) { qtractorPluginListItem *pItem = static_cast ( QListWidget::itemAt(pHelpEvent->pos())); qtractorPlugin *pPlugin = nullptr; if (pItem) pPlugin = pItem->plugin(); if (pPlugin) { QString sToolTip; qtractorPluginType *pType = pPlugin->type(); if (pType) { if (!pPlugin->alias().isEmpty()) sToolTip.append(QString("%1: ").arg(pType->name())); else if (pType->typeHint() == qtractorPluginType::AuxSend) sToolTip.append(tr("Aux Send: ")); } sToolTip.append(pItem->text()); if (pPlugin->isDirectAccessParam()) { qtractorPlugin::Param *pDirectAccessParam = pPlugin->directAccessParam(); if (pDirectAccessParam) { sToolTip.append(QString("\n(%1: %2)") .arg(pDirectAccessParam->name()) .arg(pDirectAccessParam->display())); } } QToolTip::showText(pHelpEvent->globalPos(), sToolTip, pViewport); return true; } } } else if (pEvent->type() == QEvent::Leave) { m_dragCursor = DragNone; QListWidget::unsetCursor(); return true; } } // Not handled here. return QListWidget::eventFilter(pObject, pEvent); } // trap the wheel event to change the value of the direcgAccessParameter void qtractorPluginListView::wheelEvent ( QWheelEvent *pWheelEvent ) { if (pWheelEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)) { #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) const QPoint& pos = pWheelEvent->position().toPoint(); #else const QPoint& pos = pWheelEvent->pos(); #endif qtractorPluginListItem *pItem = static_cast (QListWidget::itemAt(pos)); if (pItem) { qtractorPlugin *pPlugin = pItem->plugin(); qtractorPlugin::Param *pDirectAccessParam = nullptr; qtractorMidiControlObserver *pDirectAccessObserver = nullptr; if (pPlugin) pDirectAccessParam = pPlugin->directAccessParam(); if (pDirectAccessParam) pDirectAccessObserver = pDirectAccessParam->observer(); if (pDirectAccessObserver) { const bool bLogarithmic = pDirectAccessParam->isLogarithmic(); float fValue = pDirectAccessObserver->value(); const float fScale = pDirectAccessObserver->scaleFromValue( fValue, bLogarithmic); float fDelta = (pWheelEvent->angleDelta().y() < 0 ? -0.1f : +0.1f); if (!pDirectAccessParam->isInteger()) fDelta *= 0.5f; fValue = pDirectAccessObserver->valueFromScale( fScale + fDelta, bLogarithmic); pDirectAccessParam->updateValue(fValue, true); return; } } } // Not handled here. QListWidget::wheelEvent(pWheelEvent); // Do not propagate to parent(s)... pWheelEvent->accept(); } // To get precize clicking for in-place (de)activation; // handle mouse events for drag-and-drop stuff. void qtractorPluginListView::mousePressEvent ( QMouseEvent *pMouseEvent ) { dragLeaveEvent(nullptr); const QPoint& pos = pMouseEvent->pos(); qtractorPluginListItem *pItem = static_cast (QListWidget::itemAt(pos)); if (pItem && pos.x() > 0 && pos.x() < QListWidget::iconSize().width()) m_pClickedItem = pItem; if (pMouseEvent->button() == Qt::LeftButton) { m_posDrag = pos; m_pDragItem = pItem; dragDirectAccess(pos); } else // Reset to default value... if (pMouseEvent->button() == Qt::MiddleButton) { resetDirectAccess(pos); } QListWidget::mousePressEvent(pMouseEvent); } void qtractorPluginListView::mouseMoveEvent ( QMouseEvent *pMouseEvent ) { QListWidget::mouseMoveEvent(pMouseEvent); const QPoint& pos = pMouseEvent->pos(); if ((pMouseEvent->buttons() & Qt::LeftButton) && m_pDragItem) { if (m_dragCursor != DragNone) m_dragState = m_dragCursor; if (m_dragState != DragNone) { dragDirectAccess(pos); return; } if ((pos - m_posDrag).manhattanLength() >= QApplication::startDragDistance()) { // We'll start dragging something alright... QMimeData *pMimeData = new QMimeData(); encodeItem(pMimeData, m_pDragItem); QDrag *pDrag = new QDrag(this); pDrag->setMimeData(pMimeData); pDrag->setPixmap(m_pDragItem->icon().pixmap(16)); pDrag->setHotSpot(QPoint(-4, -12)); pDrag->exec(Qt::CopyAction | Qt::MoveAction); // We've dragged and maybe dropped it by now... m_pDragItem = nullptr; } } else if (m_dragState == DragNone) dragDirectAccess(pos); } void qtractorPluginListView::mouseReleaseEvent ( QMouseEvent *pMouseEvent ) { QListWidget::mouseReleaseEvent(pMouseEvent); const QPoint& pos = pMouseEvent->pos(); qtractorPluginListItem *pItem = static_cast (QListWidget::itemAt(pos)); if (pItem && pos.x() > 0 && pos.x() < QListWidget::iconSize().width() && m_pClickedItem == pItem) { qtractorPlugin *pPlugin = pItem->plugin(); if (pPlugin) { // Make it a undoable command... qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { pSession->execute( new qtractorActivatePluginCommand(pPlugin, !pPlugin->isActivated())); emit contentsChanged(); } } } dragLeaveEvent(nullptr); } // Drag-n-drop stuff; // track on the current drop item position. bool qtractorPluginListView::canDropEvent ( QDropEvent *pDropEvent ) { if (m_pPluginList == nullptr) return false; if (!canDecodeItem(pDropEvent->mimeData())) return false; if (m_pDragItem == nullptr) m_pDragItem = decodeItem(pDropEvent->mimeData()); if (m_pDragItem == nullptr) return false; qtractorPluginListItem *pDropItem = static_cast ( #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) QListWidget::itemAt(pDropEvent->position().toPoint())); #else QListWidget::itemAt(pDropEvent->pos())); #endif if (pDropItem && pDropItem != m_pDropItem) ensureVisibleItem(pDropItem); // Cannot drop onto itself or over the one below... qtractorPlugin *pPlugin = m_pDragItem->plugin(); if (pPlugin == nullptr) return false; if (pDropItem) { if (pDropItem->plugin() == pPlugin || (pDropItem->plugin())->prev() == pPlugin) return false; } else if (m_pPluginList->last() == pPlugin) return false; // All that to check whether it will get properly instantiated. const unsigned short iChannels = m_pPluginList->channels(); const bool bMidi = m_pPluginList->isMidi(); if ((pPlugin->type())->instances(iChannels, bMidi) < 1) return false; // This is the place we'll drop something // (append if null) m_pDropItem = pDropItem; return true; } bool qtractorPluginListView::canDropItem ( QDropEvent *pDropEvent ) { const bool bCanDropItem = canDropEvent(pDropEvent); if (bCanDropItem) moveRubberBand(m_pDropItem); else if (m_pRubberBand && m_pRubberBand->isVisible()) m_pRubberBand->hide(); return bCanDropItem; } // Ensure given item is brought to viewport visibility... void qtractorPluginListView::ensureVisibleItem ( qtractorPluginListItem *pItem ) { const int iItem = QListWidget::row(pItem); if (iItem > 0) { qtractorPluginListItem *pItemAbove = static_cast ( QListWidget::item(iItem - 1)); if (pItemAbove) QListWidget::scrollToItem(pItemAbove); } QListWidget::scrollToItem(pItem); } void qtractorPluginListView::dragEnterEvent ( QDragEnterEvent *pDragEnterEvent ) { #if 0 if (canDropItem(pDragEnterEvent)) { if (!pDragEnterEvent->isAccepted()) { pDragEnterEvent->setDropAction(Qt::MoveAction); pDragEnterEvent->accept(); } } else { pDragEnterEvent->ignore(); } #else // Always accept the drag-enter event, // so let we deal with it during move later... pDragEnterEvent->accept(); #endif } void qtractorPluginListView::dragMoveEvent ( QDragMoveEvent *pDragMoveEvent ) { if (canDropItem(pDragMoveEvent)) { if (!pDragMoveEvent->isAccepted()) { pDragMoveEvent->setDropAction(Qt::MoveAction); pDragMoveEvent->accept(); } } else { pDragMoveEvent->ignore(); } } void qtractorPluginListView::dragLeaveEvent ( QDragLeaveEvent * ) { if (m_pRubberBand) { delete m_pRubberBand; m_pRubberBand = nullptr; } // Should fallback mouse cursor... if (m_dragCursor != DragNone) QListWidget::unsetCursor(); m_dragCursor = DragNone; m_dragState = DragNone; m_pClickedItem = nullptr; m_pDragItem = nullptr; m_pDropItem = nullptr; } void qtractorPluginListView::dropEvent ( QDropEvent *pDropEvent ) { if (!canDropItem(pDropEvent)) { dragLeaveEvent(nullptr); return; } // If we aren't in the same list, // or care for keyboard modifiers... if (((m_pDragItem->plugin())->list() == m_pPluginList) || #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) (pDropEvent->modifiers() & Qt::ShiftModifier)) { #else (pDropEvent->keyboardModifiers() & Qt::ShiftModifier)) { #endif dropMove(); } else #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) if (pDropEvent->modifiers() & Qt::ControlModifier) { #else if (pDropEvent->keyboardModifiers() & Qt::ControlModifier) { #endif dropCopy(); } else { // We'll better have a drop menu... QMenu menu(this); menu.addAction(tr("&Move Here"), this, SLOT(dropMove())); menu.addAction(tr("&Copy Here"), this, SLOT(dropCopy())); menu.addSeparator(); menu.addAction(QIcon::fromTheme("formReject"), tr("C&ancel"), this, SLOT(dropCancel())); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) menu.exec(QListWidget::mapToGlobal(pDropEvent->position().toPoint())); #else menu.exec(QListWidget::mapToGlobal(pDropEvent->pos())); #endif } dragLeaveEvent(nullptr); } // Drop item slots. void qtractorPluginListView::dropMove (void) { moveItem(m_pDragItem, m_pDropItem); QListWidget::setFocus(); } void qtractorPluginListView::dropCopy (void) { copyItem(m_pDragItem, m_pDropItem); QListWidget::setFocus(); } void qtractorPluginListView::dropCancel (void) { // Do nothing... } // Draw a dragging separator line. void qtractorPluginListView::moveRubberBand ( qtractorPluginListItem *pDropItem ) { // Create the rubber-band if there's none... if (m_pRubberBand == nullptr) { m_pRubberBand = new qtractorRubberBand( QRubberBand::Line, QListWidget::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... QRect rect; if (pDropItem) { rect = QListWidget::visualItemRect(pDropItem); rect.setTop(rect.top() - 1); } else { pDropItem = static_cast ( QListWidget::item(QListWidget::count() - 1)); if (pDropItem) { rect = QListWidget::visualItemRect(pDropItem); rect.setTop(rect.bottom() - 1); } else { rect = QListWidget::viewport()->rect(); } } // Set always this height: rect.setHeight(3); m_pRubberBand->setGeometry(rect); // Ah, and make it visible, of course... if (!m_pRubberBand->isVisible()) m_pRubberBand->show(); } // Context menu event handler. void qtractorPluginListView::contextMenuEvent ( QContextMenuEvent *pContextMenuEvent ) { if (m_pPluginList == nullptr) return; qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; QMenu menu; QAction *pAction; const int iItemCount = QListWidget::count(); const bool bEnabled = (iItemCount > 0); int iItem = -1; qtractorPlugin *pPlugin = nullptr; qtractorPluginType *pType = nullptr; qtractorPluginListItem *pItem = static_cast (QListWidget::currentItem()); if (pItem) { iItem = QListWidget::row(pItem); pPlugin = pItem->plugin(); pType = pPlugin->type(); } pAction = menu.addAction( QIcon::fromTheme("formCreate"), tr("&Add Plugin..."), this, SLOT(addPlugin())); // pAction->setEnabled(true); QMenu *pInsertsMenu = menu.addMenu(tr("I&nserts")); QMenu *pAudioInsertsMenu = pInsertsMenu->addMenu( QIcon::fromTheme("trackAudio"), tr("&Audio")); pAudioInsertsMenu->setEnabled(m_pPluginList->channels() > 0); pAction = pAudioInsertsMenu->addAction( QIcon::fromTheme("formAdd"), tr("Add &Insert"), this, SLOT(addAudioInsertPlugin())); pAction = pAudioInsertsMenu->addAction( QIcon::fromTheme("formAdd"), tr("Add &Aux Send"), this, SLOT(addAudioAuxSendPlugin())); // pAction->setEnabled( // m_pPluginList->flags() != qtractorPluginList::AudioOutBus); pAudioInsertsMenu->addSeparator(); const bool bAudioInsertPlugin = (pType && pType->typeHint() == qtractorPluginType::Insert && pType->index() > 0); pAction = pAudioInsertsMenu->addAction( QIcon::fromTheme("itemAudioPortOut"), tr("&Sends"), this, SLOT(insertPluginOutputs())); pAction->setEnabled(bAudioInsertPlugin); pAction = pAudioInsertsMenu->addAction( QIcon::fromTheme("itemAudioPortIn"), tr("&Returns"), this, SLOT(insertPluginInputs())); pAction->setEnabled(bAudioInsertPlugin); const bool bMidiPluginList = m_pPluginList->isMidi(); const bool bMidiControlPlugin = (pType && pType->typeHint() == qtractorPluginType::Control && pType->index() == 0); QMenu *pMidiInsertsMenu = pInsertsMenu->addMenu( QIcon::fromTheme("trackMidi"), tr("&MIDI")); // pMidiInsertsMenu->setEnabled(bMidiPluginList); pAction = pMidiInsertsMenu->addAction( QIcon::fromTheme("formAdd"), tr("Add &Insert"), this, SLOT(addMidiInsertPlugin())); pAction->setEnabled(bMidiPluginList); pAction = pMidiInsertsMenu->addAction( QIcon::fromTheme("formAdd"), tr("Add &Aux Send"), this, SLOT(addMidiAuxSendPlugin())); pAction->setEnabled(bMidiPluginList); pMidiInsertsMenu->addSeparator(); pAction = pMidiInsertsMenu->addAction( QIcon::fromTheme("formAdd"), tr("Add &Controller"), this, SLOT(addMidiControlPlugin())); pMidiInsertsMenu->addSeparator(); const bool bMidiInsertPlugin = (pType && pType->typeHint() == qtractorPluginType::Insert && pType->index() == 0); pAction = pMidiInsertsMenu->addAction( QIcon::fromTheme("itemMidiPortOut"), tr("&Sends"), this, SLOT(insertPluginOutputs())); pAction->setEnabled(bMidiInsertPlugin || bMidiControlPlugin); pAction = pMidiInsertsMenu->addAction( QIcon::fromTheme("itemMidiPortIn"), tr("&Returns"), this, SLOT(insertPluginInputs())); pAction->setEnabled(bMidiInsertPlugin); pMidiInsertsMenu->addSeparator(); pAction = pMidiInsertsMenu->addAction( tr("&Auto-connect"), this, SLOT(midiControlAutoConnect())); pAction->setCheckable(true); pAction->setEnabled(bMidiControlPlugin); if (bMidiControlPlugin) { qtractorMidiControlPlugin *pMidiControlPlugin = static_cast (pPlugin); if (pMidiControlPlugin) pAction->setChecked(pMidiControlPlugin->isControlAutoConnect()); } menu.addSeparator(); const bool bAutoDeactivated = m_pPluginList->isAutoDeactivated(); pAction = menu.addAction( tr("Ac&tivate"), this, SLOT(activatePlugin())); pAction->setCheckable(true); pAction->setChecked(pPlugin && pPlugin->isActivated()); pAction->setEnabled(pPlugin && !bAutoDeactivated); const bool bActivatedAll = m_pPluginList->isActivatedAll(); pAction = menu.addAction( tr("Acti&vate All"), this, SLOT(activateAllPlugins())); pAction->setCheckable(true); pAction->setChecked(bActivatedAll); pAction->setEnabled(bEnabled && !bActivatedAll && !bAutoDeactivated); const bool bDeactivatedAll = !m_pPluginList->isActivated(); pAction = menu.addAction( tr("Deactivate Al&l"), this, SLOT(deactivateAllPlugins())); pAction->setCheckable(true); pAction->setChecked(bDeactivatedAll); pAction->setEnabled(bEnabled && !bDeactivatedAll && !bAutoDeactivated); menu.addSeparator(); pAction = menu.addAction( QIcon::fromTheme("formRemove"), tr("&Remove"), this, SLOT(removePlugin())); pAction->setEnabled(pPlugin != nullptr); pAction = menu.addAction( tr("Re&move All"), this, SLOT(removeAllPlugins())); pAction->setEnabled(bEnabled); menu.addSeparator(); pAction = menu.addAction( QIcon::fromTheme("formMoveUp"), tr("Move &Up"), this, SLOT(moveUpPlugin())); pAction->setEnabled(pItem && iItem > 0); pAction = menu.addAction( QIcon::fromTheme("formMoveDown"), tr("Move &Down"), this, SLOT(moveDownPlugin())); pAction->setEnabled(pItem && iItem < iItemCount - 1); menu.addSeparator(); QMenu *pPresetMenu = menu.addMenu(tr("Pre&set")); if (pPlugin) { const QStringList& presets = pPlugin->presetList(); QStringListIterator iter(presets); while (iter.hasNext()) { const QString& sPreset = iter.next(); pAction = pPresetMenu->addAction( sPreset, this, SLOT(loadPresetPlugin())); pAction->setCheckable(true); pAction->setChecked(sPreset == pPlugin->preset()); } if (presets.count() > 0) pPresetMenu->addSeparator(); pAction = pPresetMenu->addAction( qtractorPlugin::defPreset(), this, SLOT(loadPresetPlugin())); pAction->setCheckable(false); } pPresetMenu->setEnabled(pPlugin != nullptr); QMenu *pDirectAccessParamMenu = menu.addMenu(tr("Dire&ct Access")); if (pPlugin) { const int iDirectAccessParamIndex = pPlugin->directAccessParamIndex(); 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(); const int iParamIndex = int(param.key()); pAction = pDirectAccessParamMenu->addAction( pParam->name(), this, SLOT(directAccessPlugin())); pAction->setCheckable(true); pAction->setChecked(iDirectAccessParamIndex == iParamIndex); pAction->setData(iParamIndex); } const bool bParams = (params.count() > 0); if (bParams) { pDirectAccessParamMenu->addSeparator(); pAction = pDirectAccessParamMenu->addAction( tr("&None"), this, SLOT(directAccessPlugin())); pAction->setCheckable(true); pAction->setChecked(iDirectAccessParamIndex < 0); pAction->setData(int(-1)); } pDirectAccessParamMenu->setEnabled(bParams); } else pDirectAccessParamMenu->setEnabled(false); menu.addSeparator(); pAction = menu.addAction( QIcon::fromTheme("pluginProperties"), tr("&Properties..."), this, SLOT(propertiesPlugin())); pAction->setCheckable(true); pAction->setChecked(pPlugin && pPlugin->isFormVisible()); pAction->setEnabled(pItem != nullptr); pAction = menu.addAction( QIcon::fromTheme("pluginEdit"), tr("&Edit"), this, SLOT(editPlugin())); pAction->setCheckable(true); pAction->setChecked(pPlugin && pPlugin->isEditorVisible()); pAction->setEnabled(pType && pType->isEditor()); menu.addSeparator(); pAction = menu.addAction( tr("&Import..."), this, SLOT(importPlugins())); // pAction->setEnabled(true); pAction = menu.addAction( tr("E&xport..."), this, SLOT(exportPlugins())); pAction->setEnabled(bEnabled); qtractorAudioEngine *pAudioEngine = pSession->audioEngine(); qtractorMidiManager *pMidiManager = m_pPluginList->midiManager(); if (pAudioEngine && pMidiManager) { menu.addSeparator(); const QIcon& iconAudio = QIcon::fromTheme("trackAudio"); const bool bAudioOutputBus = pMidiManager->isAudioOutputBus(); qtractorAudioBus *pAudioOutputBus = pMidiManager->audioOutputBus(); QMenu *pAudioMenu = menu.addMenu(iconAudio, "Audi&o"); pAction = pAudioMenu->addAction( tr("&Outputs"), this, SLOT(audioOutputs())); pAction->setEnabled(pAudioOutputBus != nullptr); pAudioMenu->addSeparator(); QListIterator iter(pAudioEngine->buses2()); while (iter.hasNext()) { qtractorBus *pBus = iter.next(); if (pBus->busMode() & qtractorBus::Output) { const QString& sBusName = pBus->busName(); pAction = pAudioMenu->addAction(iconAudio, sBusName, this, SLOT(audioOutputBusName())); pAction->setCheckable(true); pAction->setChecked(pAudioOutputBus != nullptr && sBusName == pAudioOutputBus->busName()); pAction->setEnabled(!bAudioOutputBus); } } pAudioMenu->addSeparator(); pAction = pAudioMenu->addAction( tr("&Dedicated"), this, SLOT(audioOutputBus())); pAction->setCheckable(true); pAction->setChecked(bAudioOutputBus); pAction = pAudioMenu->addAction( tr("&Auto-connect"), this, SLOT(audioOutputAutoConnect())); pAction->setCheckable(true); pAction->setChecked(pMidiManager->isAudioOutputAutoConnect()); pAction->setEnabled(bAudioOutputBus); } menu.exec(pContextMenuEvent->globalPos()); } // Show insert pseudo-plugin audio bus connections. static const char *c_pszPluginListItemMimeType = "qtractor/plugin-item"; // Encoder method (static). void qtractorPluginListView::encodeItem ( QMimeData *pMimeData, qtractorPluginListItem *pItem ) { // Allocate the data array... pMimeData->setData(c_pszPluginListItemMimeType, QByteArray((const char *) &pItem, sizeof(qtractorPluginListItem *))); } // Decode trial method (static). bool qtractorPluginListView::canDecodeItem ( const QMimeData *pMimeData ) { return pMimeData->hasFormat(c_pszPluginListItemMimeType); } // Decode method (static). qtractorPluginListItem *qtractorPluginListView::decodeItem ( const QMimeData *pMimeData ) { qtractorPluginListItem *pItem = nullptr; QByteArray data = pMimeData->data(c_pszPluginListItemMimeType); if (data.size() == sizeof(qtractorPluginListItem *)) ::memcpy(&pItem, data.constData(), sizeof(qtractorPluginListItem *)); return pItem; } // Direct access parameter handle. void qtractorPluginListView::dragDirectAccess ( const QPoint& pos ) { qtractorPluginListItem *pItem = m_pDragItem; if (pItem == nullptr) pItem = static_cast (QListWidget::itemAt(pos)); if (pItem == nullptr) return; qtractorPlugin *pPlugin = pItem->plugin(); if (pPlugin == nullptr) return; qtractorPlugin::Param *pDirectAccessParam = pPlugin->directAccessParam(); if (pDirectAccessParam == nullptr) return; qtractorMidiControlObserver *pDirectAccessObserver = pDirectAccessParam->observer(); if (pDirectAccessObserver == nullptr) return; const bool bLogarithmic = pDirectAccessParam->isLogarithmic(); const int iDirectAccessWidth = pItem->directAccessWidth(); const QRect& rectItem = QListWidget::visualItemRect(pItem) .adjusted(QListWidget::iconSize().width(), 1, -2, -2); if (m_dragState == DragNone) { const float fValue = pDirectAccessParam->value(); const float fScale = pDirectAccessObserver->scaleFromValue(fValue, bLogarithmic); const int x = rectItem.x() + int(fScale * float(iDirectAccessWidth)); if (pos.x() > x - 5 && pos.x() < x + 5) { m_dragCursor = DragDirectAccess; QListWidget::setCursor(QCursor(Qt::PointingHandCursor)); } else { QListWidget::unsetCursor(); m_dragCursor = DragNone; } } else if (m_dragState == DragDirectAccess) { const float fScale = float(pos.x() - rectItem.x()) / float(iDirectAccessWidth); const float fValue = pDirectAccessObserver->valueFromScale(fScale, bLogarithmic); pDirectAccessParam->updateValue(fValue, true); QWidget *pViewport = QListWidget::viewport(); QToolTip::showText(pViewport->mapToGlobal(pos), QString("%1\n(%2: %3)") .arg(pItem->text()) // (pType->name(); .arg(pDirectAccessParam->name()) .arg(pDirectAccessParam->display()), pViewport); } } // Direct access parameter reset (to default value). void qtractorPluginListView::resetDirectAccess ( const QPoint& pos ) { qtractorPluginListItem *pItem = static_cast (QListWidget::itemAt(pos)); if (pItem == nullptr) return; qtractorPlugin *pPlugin = pItem->plugin(); if (pPlugin == nullptr) return; qtractorPlugin::Param *pDirectAccessParam = pPlugin->directAccessParam(); if (pDirectAccessParam == nullptr) return; qtractorMidiControlObserver *pDirectAccessObserver = pDirectAccessParam->observer(); if (pDirectAccessObserver == nullptr) return; const float fDefaultValue = pDirectAccessObserver->defaultValue(); pDirectAccessParam->updateValue(fDefaultValue, true); } // Initial size-policy hints. QSize qtractorPluginListView::sizeHint (void) const { const QFont& font = QListWidget::font(); const int iItemHeight = QFontMetrics(font).lineSpacing() + 4; return QSize(180, iItemHeight << 2); } // end of qtractorPluginListView.cpp qtractor-1.5.9/src/PaxHeaders/qtractorBusForm.cpp0000644000000000000000000000013215101070305017063 xustar0030 mtime=1761898693.065267585 30 atime=1761898693.064267582 30 ctime=1761898693.065267585 qtractor-1.5.9/src/qtractorBusForm.cpp0000644000175000001440000006751615101070305017072 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.9/src/PaxHeaders/qtractorTimeStretcher.h0000644000000000000000000000013215101070305017735 xustar0030 mtime=1761898693.090267664 30 atime=1761898693.090267664 30 ctime=1761898693.090267664 qtractor-1.5.9/src/qtractorTimeStretcher.h0000644000175000001440000000561715101070305017736 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.9/src/PaxHeaders/qtractorMidiEditEvent.cpp0000644000000000000000000000013215101070305020200 xustar0030 mtime=1761898693.079267629 30 atime=1761898693.079267629 30 ctime=1761898693.079267629 qtractor-1.5.9/src/qtractorMidiEditEvent.cpp0000644000175000001440000006151415101070305020177 0ustar00rncbcusers// qtractorMidiEditEvent.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 "qtractorMidiEditEvent.h" #include "qtractorMidiEditor.h" #include "qtractorMidiEditView.h" #include "qtractorMidiSequence.h" #include "qtractorMidiClip.h" #include "qtractorSession.h" #include "qtractorOptions.h" #include #include #include #include #include #include #include #ifdef CONFIG_GRADIENT #include #endif #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) #define horizontalAdvance width #endif //---------------------------------------------------------------------------- // qtractorMidiEditEventScale -- MIDI event scale widget. // Constructor. qtractorMidiEditEventScale::qtractorMidiEditEventScale ( qtractorMidiEditor *pEditor, QWidget *pParent ) : QWidget(pParent) { m_pEditor = pEditor; QWidget::setMinimumWidth(22); // QWidget::setBackgroundRole(QPalette::Mid); const QFont& font = QWidget::font(); QWidget::setFont(QFont(font.family(), font.pointSize() - 3)); } // Default destructor. qtractorMidiEditEventScale::~qtractorMidiEditEventScale (void) { } // Paint event handler. void qtractorMidiEditEventScale::paintEvent ( QPaintEvent * ) { QPainter painter(this); painter.setPen(Qt::darkGray); // Draw scale line labels... qtractorMidiEditEvent *pEditEvent = m_pEditor->editEvent(); const QFontMetrics& fm = painter.fontMetrics(); int h = (pEditEvent->viewport())->height(); int w = QWidget::width(); int h2 = (fm.height() >> 1); int dy = (h >> 3); // Account for event view widget frame... h += 4; int y = 2; int y0 = y + 1; int n, dn; const qtractorMidiEvent::EventType eventType = pEditEvent->eventType(); if (eventType == qtractorMidiEvent::PITCHBEND) { n = 8192; dn = (n >> 2); } else { if (eventType == qtractorMidiEvent::REGPARAM || eventType == qtractorMidiEvent::NONREGPARAM || eventType == qtractorMidiEvent::CONTROL14) n = 16384; else n = 128; dn = (n >> 3); } while (y < h) { const QString& sLabel = QString::number(n); if (fm.horizontalAdvance(sLabel) < w - 6) painter.drawLine(w - 4, y, w - 1, y); if (y > y0 + (h2 << 1) && y < h - (h2 << 1)) { painter.drawText(2, y - h2, w - 8, fm.height(), Qt::AlignRight | Qt::AlignVCenter, sLabel); y0 = y + 1; } y += dy; n -= dn; } } //---------------------------------------------------------------------------- // qtractorMidiEditEvent -- MIDI sequence event view widget. // Constructor. qtractorMidiEditEvent::qtractorMidiEditEvent ( qtractorMidiEditor *pEditor, QWidget *pParent ) : qtractorScrollView(pParent) { m_pEditor = pEditor; m_eventType = qtractorMidiEvent::NOTEON; m_eventParam = 0; // Zoom tool widgets m_pHzoomOut = new QToolButton(this); m_pHzoomIn = new QToolButton(this); m_pHzoomReset = new QToolButton(this); m_pHzoomOut->setIcon(QIcon::fromTheme("viewZoomOut")); m_pHzoomIn->setIcon(QIcon::fromTheme("viewZoomIn")); m_pHzoomReset->setIcon(QIcon::fromTheme("viewZoomReset")); const int iScrollBarExtent = qtractorScrollView::style()->pixelMetric(QStyle::PM_ScrollBarExtent); m_pHzoomReset->setFixedWidth(iScrollBarExtent); m_pHzoomIn->setFixedWidth(iScrollBarExtent); m_pHzoomOut->setFixedWidth(iScrollBarExtent); qtractorScrollView::addScrollBarWidget(m_pHzoomReset, Qt::AlignRight); qtractorScrollView::addScrollBarWidget(m_pHzoomIn, Qt::AlignRight); qtractorScrollView::addScrollBarWidget(m_pHzoomOut, Qt::AlignRight); m_pHzoomIn->setAutoRepeat(true); m_pHzoomOut->setAutoRepeat(true); m_pHzoomIn->setToolTip(tr("Zoom in (horizontal)")); m_pHzoomOut->setToolTip(tr("Zoom out (horizontal)")); m_pHzoomReset->setToolTip(tr("Zoom reset (horizontal)")); QObject::connect(m_pHzoomIn, SIGNAL(clicked()), m_pEditor, SLOT(horizontalZoomInSlot())); QObject::connect(m_pHzoomOut, SIGNAL(clicked()), m_pEditor, SLOT(horizontalZoomOutSlot())); QObject::connect(m_pHzoomReset, SIGNAL(clicked()), m_pEditor, SLOT(horizontalZoomResetSlot())); qtractorScrollView::setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); 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() - 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. qtractorMidiEditEvent::~qtractorMidiEditEvent (void) { } // Rectangular contents update. void qtractorMidiEditEvent::updateContents ( const QRect& rect ) { updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(rect); } // Overall contents update. void qtractorMidiEditEvent::updateContents (void) { updatePixmap( qtractorScrollView::contentsX(), qtractorScrollView::contentsY()); qtractorScrollView::updateContents(); } // Current event selection accessors. void qtractorMidiEditEvent::setEventType ( qtractorMidiEvent::EventType eventType ) { m_eventType = eventType; m_pEditor->selectAll(this, false); m_pEditor->editEventScale()->update(); // m_pEditor->updateContents(); } qtractorMidiEvent::EventType qtractorMidiEditEvent::eventType (void) const { return m_eventType; } void qtractorMidiEditEvent::setEventParam ( unsigned short param ) { m_eventParam = param; m_pEditor->selectAll(this, false); // m_pEditor->updateContents(); } unsigned short qtractorMidiEditEvent::eventParam (void) const { return m_eventParam; } // Resize event handler. void qtractorMidiEditEvent::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, 20); const QColor rgba2(0, 0, 0, 80); 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 // FIXME: Prevent any overlay selection during resizing this, // as the overlay rectangles will certainly be wrong... if (m_pEditor->isSelected()) { m_pEditor->updateContents(); } else { updateContents(); } } // (Re)create the complete view pixmap. void qtractorMidiEditEvent::updatePixmap ( int cx, int /*cy*/ ) { QWidget *pViewport = qtractorScrollView::viewport(); const int w = pViewport->width(); const int h = pViewport->height() & ~1; // always even. if (w < 1 || h < 1) return; const QPalette& pal = qtractorScrollView::palette(); const QColor& rgbBase = pal.base().color(); const QColor& rgbLine = pal.mid().color(); const QColor& rgbLight = pal.midlight().color(); m_pixmap = QPixmap(w, h); m_pixmap.fill(rgbBase); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); if (pTimeScale == nullptr) return; QPainter painter(&m_pixmap); // painter.initFrom(this); painter.setFont(qtractorScrollView::font()); // Show that we may have clip limits... if (m_pEditor->length() > 0) { int x1 = pTimeScale->pixelFromFrame(m_pEditor->length()) - cx; if (x1 < 0) x1 = 0; if (x1 < w) painter.fillRect(x1, 0, w - x1, h, rgbBase.darker(105)); } // Draw horizontal lines... painter.setPen(rgbLight); const int dy = (h >> 3); int y = 0; while (y < h) { painter.drawLine(0, y, w, y); y += dy; } // Account for the editing offset: qtractorTimeScale::Cursor cursor(pTimeScale); const unsigned long f0 = m_pEditor->offset(); qtractorTimeScale::Node *pNode = cursor.seekFrame(f0); const unsigned long t0 = pNode->tickFromFrame(f0); const int x0 = pTimeScale->pixelFromFrame(f0); const int dx = x0 + cx; // Draw vertical grid lines... const QBrush zebra(QColor(0, 0, 0, 20)); pNode = cursor.seekPixel(dx); const unsigned short iSnapPerBeat = (m_pEditor->isSnapGrid() ? pTimeScale->snapPerBeat() : 0); #if 0 unsigned short iPixelsPerBeat = pNode->pixelsPerBeat(); unsigned int iBeat = pNode->beatFromPixel(dx); if (iBeat > 0) pNode = cursor.seekBeat(--iBeat); unsigned short iBar = (m_pEditor->isSnapZebra() ? pNode->barFromBeat(iBeat) : 0); int x = pNode->pixelFromBeat(iBeat) - dx; int x2 = x; while (x < w) { const bool bBeatIsBar = pNode->beatIsBar(iBeat); if (bBeatIsBar) { painter.setPen(rgbLine); painter.drawLine(x - 1, 0, x - 1, h); if (m_pEditor->isSnapZebra() && (x > x2) && (++iBar & 1)) painter.fillRect(QRect(x2, 0, x - x2 + 1, h), zebra); x2 = x; if (iBeat == pNode->beat) iPixelsPerBeat = pNode->pixelsPerBeat(); } if (bBeatIsBar || iPixelsPerBeat > 8) { painter.setPen(rgbLight); painter.drawLine(x, 0, x, h); } if (iSnapPerBeat > 1) { const int q = iPixelsPerBeat / iSnapPerBeat; if (q > 4) { painter.setPen(rgbBase.value() < 0x7f ? rgbLight.darker(105) : rgbLight.lighter(120)); for (int i = 1; i < iSnapPerBeat; ++i) { x = pTimeScale->pixelSnap(x + dx + q) - dx - 1; painter.drawLine(x, 0, x, h); } } } pNode = cursor.seekBeat(++iBeat); x = pNode->pixelFromBeat(iBeat) - dx; } if (m_pEditor->isSnapZebra() && (x > x2) && (++iBar & 1)) painter.fillRect(QRect(x2, 0, x - x2 + 1, h), zebra); #else unsigned short iBar = pNode->barFromPixel(dx); if (iBar > 0) pNode = cursor.seekBar(--iBar); int x = pNode->pixelFromBar(iBar) - dx; while (x < w) { // Next bar... pNode = cursor.seekPixel(x + dx); const int x2 = pNode->pixelFromBar(++iBar) - dx; // Zebra lines... if (m_pEditor->isSnapZebra() && (iBar & 1)) painter.fillRect(QRect(x, 0, x2 - x + 1, h), zebra); // Beat lines... const unsigned short iBeatsPerBar2 = pNode->beatsPerBar2(); const float q2 = float(x2 - x) / float(iBeatsPerBar2); if (q2 > 8.0f) { float p2 = float(x); for (int i = 0; i < iBeatsPerBar2; ++i) { if (iSnapPerBeat > 1) { const float q1 = q2 / float(iSnapPerBeat); if (q1 > 4.0f) { painter.setPen(rgbBase.value() < 0x7f ? rgbLight.darker(105) : rgbLight.lighter(120)); float p1 = p2; for (int j = 1; j < iSnapPerBeat; ++j) { const int x1 = int(p1 += q1); painter.drawLine(x1, 0, x1, h); } } } x = int(p2 += q2); if (x > w) break; if (i < iBeatsPerBar2 - 1) { painter.setPen(rgbLight); painter.drawLine(x, 0, x, h); } } } // Bar line... painter.setPen(rgbLine); painter.drawLine(x2 - 1, 0, x2 - 1, h); painter.setPen(rgbLight); painter.drawLine(x2, 0, x2, h); // Move forward... x = x2; } #endif // Draw location marker lines... qtractorTimeScale::Marker *pMarker = pTimeScale->markers().seekPixel(dx); while (pMarker) { x = pTimeScale->pixelFromFrame(pMarker->frame) - dx; if (x > w) break; painter.setPen(pMarker->color); painter.drawLine(x, 0, x, h); pMarker = pMarker->next(); } // // Draw the clip(s) events... // qtractorMidiSequence *pSeq = m_pEditor->sequence(); if (pSeq == nullptr) return; pNode = cursor.seekPixel(x = dx); const unsigned long iTickStart = pNode->tickFromPixel(x); pNode = cursor.seekPixel(x += w); const unsigned long iTickEnd = pNode->tickFromPixel(x); const unsigned long f1 = f0 + m_pEditor->length(); pNode = cursor.seekFrame(f1); const unsigned long iTickEnd2 = pNode->tickFromFrame(f1); // This is the zero-line... const int y0 = (m_eventType == qtractorMidiEvent::PITCHBEND ? h >> 1 : h); painter.setPen(rgbLight); painter.drawLine(0, y0 - 1, w, y0 - 1); painter.setPen(rgbLine); painter.drawLine(0, y0, w, y0); painter.setRenderHint(QPainter::Antialiasing, true); // Draw ghost-track events in dimmed transparecncy (alpha=55)... qtractorTrack *pGhostTrack = m_pEditor->ghostTrack(); if (pGhostTrack) { // Don't draw beyhond the right-most position (x = dx + w)... const unsigned long f2 = pTimeScale->frameFromPixel(x); const bool bDrumMode = pGhostTrack->isMidiDrums(); qtractorClip *pClip = pGhostTrack->clips().first(); while (pClip && pClip->clipStart() + pClip->clipLength() < f0) pClip = pClip->next(); while (pClip && pClip->clipStart() < f2) { qtractorMidiSequence *pGhostSeq = nullptr; qtractorMidiClip *pGhostClip = static_cast (pClip); if (pGhostClip && pGhostClip != m_pEditor->midiClip()) pGhostSeq = pGhostClip->sequence(); if (pGhostSeq) { m_pEditor->reset(false); // FIXME: reset cached cursors... const unsigned long iClipStart = pGhostClip->clipStart(); const unsigned long iClipEnd = iClipStart + pGhostClip->clipLength(); pNode = cursor.seekFrame(iClipStart); const unsigned long t1 = pNode->tickFromFrame(iClipStart); pNode = cursor.seekFrame(iClipEnd); const unsigned long t2 = pNode->tickFromFrame(iClipEnd); drawEvents(painter, dx, y0, pGhostSeq, t1, iTickStart, iTickEnd, t2, bDrumMode, pGhostTrack->foreground(), pGhostTrack->background(), 60); } pClip = pClip->next(); } // FIXME: reset cached cursors... m_pEditor->reset(false); } // Draw actual events in full brightness (alpha=255)... drawEvents(painter, dx, y0, pSeq, t0, iTickStart, iTickEnd, iTickEnd2, m_pEditor->isDrumMode(), m_pEditor->foreground(), m_pEditor->background()); painter.setRenderHint(QPainter::Antialiasing, false); // Draw loop boundaries, if applicable... if (pSession->isLooping()) { const QBrush shade(QColor(0, 0, 0, 60)); painter.setPen(Qt::darkCyan); x = pTimeScale->pixelFromFrame(pSession->loopStart()) - dx; 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()) - dx; 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); x = pTimeScale->pixelFromFrame(pSession->punchIn()) - dx; 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()) - dx; 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 the track view events. void qtractorMidiEditEvent::drawEvents ( QPainter& painter, int dx, int dy, qtractorMidiSequence *pSeq, unsigned long t0, unsigned long iTickStart, unsigned long iTickEnd, unsigned long iTickEnd2, bool bDrumMode, const QColor& fore, const QColor& back, int alpha ) { const int y0 = dy; // former nomenclature. QColor rgbFore(fore); rgbFore.setAlpha(alpha); painter.setPen(rgbFore); QColor rgbValue(back); int hue, sat, val; rgbValue.getHsv(&hue, &sat, &val); sat = 86; rgbValue.setAlpha(alpha); int x, y; const QFontMetrics fm(qtractorScrollView::font()); const int hs = fm.ascent(); // fm.height() - 2; qtractorTimeScale::Cursor cursor(m_pEditor->timeScale()); qtractorTimeScale::Node *pNode; const qtractorMidiEvent::EventType eventType = m_eventType; const unsigned short eventParam = m_eventParam; const bool bEventParam = (eventType == qtractorMidiEvent::CONTROLLER || eventType == qtractorMidiEvent::REGPARAM || eventType == qtractorMidiEvent::NONREGPARAM || eventType == qtractorMidiEvent::CONTROL14); const int wm = m_pEditor->minEventWidth(); qtractorMidiEvent *pEvent = m_pEditor->seekEvent(pSeq, iTickStart > t0 ? iTickStart - t0 : 0); while (pEvent) { const unsigned long t1 = t0 + pEvent->time(); if (t1 >= iTickEnd) break; unsigned long t2 = t1 + pEvent->duration(); if (t2 > iTickEnd2) t2 = iTickEnd2; // Filter event type!... if (pEvent->type() == eventType && t2 >= iTickStart && (!bEventParam || pEvent->param() == eventParam)) { if (eventType == qtractorMidiEvent::REGPARAM || eventType == qtractorMidiEvent::NONREGPARAM || eventType == qtractorMidiEvent::CONTROL14) y = y0 - (y0 * pEvent->value()) / 16384; else if (eventType == qtractorMidiEvent::PITCHBEND) y = y0 - (y0 * pEvent->pitchBend()) / 8192; else if (eventType == qtractorMidiEvent::PGMCHANGE) y = y0 - (y0 * pEvent->param()) / 128; else y = y0 - (y0 * pEvent->value()) / 128; pNode = cursor.seekTick(t1); x = pNode->pixelFromTick(t1) - dx; pNode = cursor.seekTick(t2); int w1 = (t1 >= t2 && m_pEditor->isClipRecordEx() ? m_pEditor->playHeadX() : pNode->pixelFromTick(t2)) - dx - x; if (w1 < wm || !m_pEditor->isNoteDuration() || bDrumMode) w1 = wm; if (eventType == qtractorMidiEvent::NOTEON || eventType == qtractorMidiEvent::KEYPRESS) { if (m_pEditor->isNoteColor()) { hue = (128 - int(pEvent->note())) << 4; if (m_pEditor->isValueColor()) sat = 64 + (int(pEvent->velocity()) >> 1); rgbValue.setHsv(hue, sat, val, alpha); } else if (m_pEditor->isValueColor()) { hue = (128 - int(pEvent->velocity())) << 1; rgbValue.setHsv(hue, sat, val, alpha); } } // Lollipop candy shadow... const QRect rect(x, y, wm, wm); const int ym = (y > y0 - 5 ? -5 : 0); // negative pitch-bend adjust. painter.setPen(rgbFore); painter.setBrush(rgbFore); painter.drawEllipse(rect.adjusted(-1, ym - 1, +1, ym + 1)); // Lollipop stick... if (y < y0) { painter.fillRect(x, y, w1, y0 - y, rgbFore); painter.fillRect(x + 1, y + 1, w1 - 4, y0 - y - 2, rgbValue); } else if (y > y0) { painter.fillRect(x, y0, w1, y - y0, rgbFore); painter.fillRect(x + 1, y0 + 1, w1 - 4, y - y0 - 2, rgbValue); } else { painter.fillRect(x, y0 - 2, w1, 4, rgbFore); painter.fillRect(x + 1, y0 - 1, w1 - 4, 2, rgbValue); } // Lollipop candy hilite... painter.setPen(rgbValue); painter.setBrush(rgbValue); painter.drawEllipse(rect.adjusted(0, ym, -2, ym - 2)); // Note name... if (m_pEditor->isNoteNames() && m_pEditor->isNoteDuration() && hs < y0 - y && ( eventType == qtractorMidiEvent::NOTEON || eventType == qtractorMidiEvent::KEYPRESS)) { const QString& sNoteName = m_pEditor->noteName(pEvent->note()); painter.setPen(rgbFore.darker(160)); painter.drawText( QRect(x + 2, y + 1, w1 - 6, y0 - y), Qt::AlignTop | Qt::AlignLeft, sNoteName); painter.setPen(rgbFore); } } pEvent = pEvent->next(); } } // Draw the time scale. void qtractorMidiEditEvent::drawContents ( QPainter *pPainter, const QRect& rect ) { pPainter->drawPixmap(rect, m_pixmap, rect); #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 m_pEditor->paintDragState(this, pPainter); // Draw special play/edit-head/tail headers... const int cx = qtractorScrollView::contentsX(); int x = m_pEditor->editHeadX() - cx; if (x >= rect.left() && x <= rect.right()) { pPainter->setPen(Qt::blue); pPainter->drawLine(x, rect.top(), x, rect.bottom()); } x = m_pEditor->editTailX() - cx; if (x >= rect.left() && x <= rect.right()) { pPainter->setPen(Qt::blue); pPainter->drawLine(x, rect.top(), x, rect.bottom()); } x = m_pEditor->playHeadX() - cx; if (x >= rect.left() && x <= rect.right()) { pPainter->setPen(Qt::red); pPainter->drawLine(x, rect.top(), x, rect.bottom()); } if (m_pEditor->isStepInputHead()) { x = m_pEditor->stepInputHeadX() - cx; if (x >= rect.left() && x <= rect.right()) { pPainter->setPen(QColor(255, 0, 0, 120)); pPainter->drawLine(x, rect.top(), x, rect.bottom()); } } } // To have event view in h-sync with main view. void qtractorMidiEditEvent::contentsXMovingSlot ( int cx, int /*cy*/ ) { if (qtractorScrollView::contentsX() != cx) qtractorScrollView::setContentsPos(cx, qtractorScrollView::contentsY()); } // Focus lost event. void qtractorMidiEditEvent::focusOutEvent ( QFocusEvent *pFocusEvent ) { m_pEditor->focusOut(this); qtractorScrollView::focusOutEvent(pFocusEvent); } // Keyboard event handler. void qtractorMidiEditEvent::keyPressEvent ( QKeyEvent *pKeyEvent ) { if (!m_pEditor->keyPress(this, pKeyEvent->key(), pKeyEvent->modifiers())) qtractorScrollView::keyPressEvent(pKeyEvent); } // Handle item selection/dragging -- mouse button press. void qtractorMidiEditEvent::mousePressEvent ( QMouseEvent *pMouseEvent ) { // Process mouse press... // qtractorScrollView::mousePressEvent(pMouseEvent); qtractorSession *pSession = qtractorSession::getInstance(); if (pSession == nullptr) return; // We'll need options somehow... qtractorOptions *pOptions = qtractorOptions::getInstance(); // Which mouse state? bool bModifier = (pMouseEvent->modifiers() & (Qt::ShiftModifier | Qt::ControlModifier)); // Maybe start the drag-move-selection dance? const QPoint& pos = qtractorScrollView::viewportToContents(pMouseEvent->pos()); qtractorTimeScale *pTimeScale = m_pEditor->timeScale(); const unsigned long iFrame = m_pEditor->frameSnap(m_pEditor->offset() + pTimeScale->frameFromPixel(pos.x() > 0 ? pos.x() : 0)); switch (pMouseEvent->button()) { case Qt::LeftButton: // Only the left-mouse-button was meaningful... break; case Qt::MiddleButton: // Mid-button direct positioning... m_pEditor->selectAll(this, false); // Which mouse state? if (pOptions && pOptions->bMidButtonModifier) bModifier = !bModifier; // Reverse mid-button role... if (bModifier) { // Play-head positioning commit... m_pEditor->setPlayHead(iFrame); pSession->setPlayHead(iFrame); } else { // Edit cursor (merge) positioning... m_pEditor->setEditHead(iFrame); m_pEditor->setEditTail(iFrame); } // Logical contents changed, just for visual feedback... m_pEditor->selectionChangeNotify(); // Fall thru... default: return; } // Remember what and where we'll be dragging/selecting... m_pEditor->dragMoveStart(this, pos, pMouseEvent->modifiers()); } // Handle item selection/dragging -- mouse pointer move. void qtractorMidiEditEvent::mouseMoveEvent ( QMouseEvent *pMouseEvent ) { // Process mouse move... // qtractorScrollView::mouseMoveEvent(pMouseEvent); // Are we already moving/dragging something? const QPoint& pos = qtractorScrollView::viewportToContents(pMouseEvent->pos()); m_pEditor->dragMoveUpdate(this, pos, pMouseEvent->modifiers()); } // Handle item selection/dragging -- mouse button release. void qtractorMidiEditEvent::mouseReleaseEvent ( QMouseEvent *pMouseEvent ) { // Were we moving/dragging something? const QPoint& pos = qtractorScrollView::viewportToContents(pMouseEvent->pos()); m_pEditor->dragMoveCommit(this, pos, pMouseEvent->modifiers()); } // Handle zoom with mouse wheel. void qtractorMidiEditEvent::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); } // Trap for help/tool-tip and leave events. bool qtractorMidiEditEvent::eventFilter ( QObject *pObject, QEvent *pEvent ) { if (m_pEditor->dragMoveFilter(this, pObject, pEvent)) return true; // Not handled here. return qtractorScrollView::eventFilter(pObject, pEvent); } // end of qtractorMidiEditEvent.cpp qtractor-1.5.9/src/PaxHeaders/qtractorRubberBand.h0000644000000000000000000000013215101070305017161 xustar0030 mtime=1761898693.087267654 30 atime=1761898693.087267654 30 ctime=1761898693.087267654 qtractor-1.5.9/src/qtractorRubberBand.h0000644000175000001440000000414215101070305017152 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.9/src/PaxHeaders/qtractorThumbView.cpp0000644000000000000000000000013215101070305017420 xustar0030 mtime=1761898693.089267661 30 atime=1761898693.089267661 30 ctime=1761898693.089267661 qtractor-1.5.9/src/qtractorThumbView.cpp0000644000175000001440000003272415101070305017420 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.9/src/PaxHeaders/qtractorInstrument.h0000644000000000000000000000013215101070305017323 xustar0030 mtime=1761898693.072267607 30 atime=1761898693.072267607 30 ctime=1761898693.072267607 qtractor-1.5.9/src/qtractorInstrument.h0000644000175000001440000002600015101070305017311 0ustar00rncbcusers// qtractorInstrument.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 __qtractorInstrument_h #define __qtractorInstrument_h #include #include // Forward declarations. class QTextStream; class QFile; class QDomDocument; class QDomElement; //---------------------------------------------------------------------- // class qtractorInstrumentData -- instrument definition data classes. // class qtractorInstrumentData { public: typedef QMap DataMap; // Constructor. qtractorInstrumentData() : m_pData(new DataRef()) {} // Copy constructor. qtractorInstrumentData(const qtractorInstrumentData& data) { attach(data); } // Destructor. ~qtractorInstrumentData() { detach(); } // Assignment operator. qtractorInstrumentData& operator= (const qtractorInstrumentData& data) { if (m_pData != data.m_pData) { detach(); attach(data); } return *this; } // Accessor operator. QString& operator[] (int iIndex) const { return m_pData->map[iIndex]; } // Property accessors. void setName(const QString& sName) { m_pData->name = sName; } const QString& name() const { return m_pData->name; } void setBasedOn(const QString& sBasedOn) { m_pData->basedOn = sBasedOn; } const QString& basedOn() const { return m_pData->basedOn; } // Indirect iterator stuff. typedef DataMap::Iterator Iterator; Iterator begin() { return m_pData->map.begin(); } Iterator end() { return m_pData->map.end(); } typedef DataMap::ConstIterator ConstIterator; ConstIterator constBegin() const { return m_pData->map.constBegin(); } ConstIterator constEnd() const { return m_pData->map.constEnd(); } unsigned int count() const { return m_pData->map.count(); } bool contains(int iKey) const { return m_pData->map.contains(iKey); } protected: // Copy/clone method. void attach(const qtractorInstrumentData& data) { m_pData = data.m_pData; ++(m_pData->refCount); } // Destroy method. void detach() { if (--(m_pData->refCount) == 0) delete m_pData; } private: // The ref-counted data. struct DataRef { // Default payload constructor. DataRef() : refCount(1) {}; // Payload members. int refCount; QString name; QString basedOn; DataMap map; } *m_pData; }; class qtractorInstrumentDataList : public QMap {}; class qtractorInstrumentPatches : public QMap {}; class qtractorInstrumentNotes : public QMap {}; class qtractorInstrumentKeys : public QMap {}; class qtractorInstrumentDrumFlags : public QMap {}; class qtractorInstrumentDrums : public QMap {}; //---------------------------------------------------------------------- // class qtractorInstrument -- instrument definition instance class. // class qtractorInstrument { public: // Constructor. qtractorInstrument() : m_pData(new DataRef()) {} // Copy constructor. qtractorInstrument(const qtractorInstrument& instr) { attach(instr); } // Destructor. ~qtractorInstrument() { detach(); } // Assignment operator. qtractorInstrument& operator= (const qtractorInstrument& instr) { if (m_pData != instr.m_pData) { detach(); attach(instr); } return *this; } // Instrument title property accessors. void setInstrumentName(const QString& sInstrumentName) { m_pData->instrumentName = sInstrumentName; } const QString& instrumentName() const { return m_pData->instrumentName; } // BankSelMethod accessors. void setBankSelMethod(int iBankSelMethod) { m_pData->bankSelMethod = iBankSelMethod; } int bankSelMethod() const { return m_pData->bankSelMethod; } void setUsesNotesAsControllers(bool bUsesNotesAsControllers) { m_pData->usesNotesAsControllers = bUsesNotesAsControllers; } bool usesNotesAsControllers() const { return m_pData->usesNotesAsControllers; } // Patch banks accessors. const qtractorInstrumentPatches& patches() const { return m_pData->patches; } const qtractorInstrumentData& patch(int iBank) const; void setPatch(int iBank, const qtractorInstrumentData& patch) { m_pData->patches[iBank] = patch; } void setBankName(int iBank, const QString& sBankName) { m_pData->patches[iBank].setName(sBankName); } const QString& bankName(int iBank) const { return m_pData->patches[iBank].name(); } void setProgName(int iBank, int iProg, const QString& sProgName) { m_pData->patches[iBank][iProg] = sProgName; } const QString& progName(int iBank, int iProg) const { return m_pData->patches[iBank][iProg]; } // Control names accessors. void setControllersName(const QString& sControllersName) { m_pData->controllers.setName(sControllersName); } const QString& controllersName() const { return m_pData->controllers.name(); } void setControllers(const qtractorInstrumentData& controllers) { m_pData->controllers = controllers; } const qtractorInstrumentData& controllers() const { return m_pData->controllers; } // RPN names accessors. void setRpnsName(const QString& sRpnName) { m_pData->rpns.setName(sRpnName); } const QString& rpnsName() const { return m_pData->rpns.name(); } void setRpns(const qtractorInstrumentData& rpns) { m_pData->rpns = rpns; } const qtractorInstrumentData& rpns() const { return m_pData->rpns; } // NRPN names accessors. void setNrpnsName(const QString& sNrpnName) { m_pData->nrpns.setName(sNrpnName); } const QString& nrpnsName() const { return m_pData->nrpns.name(); } void setNrpns(const qtractorInstrumentData& nrpns) { m_pData->nrpns = nrpns; } const qtractorInstrumentData& nrpns() const { return m_pData->nrpns; } // Keys banks accessors. qtractorInstrumentData& notes(int iBank, int iProg) const; void setNotes(int iBank, int iProg, const qtractorInstrumentData& notes) { m_pData->keys[iBank][iProg] = notes; } const qtractorInstrumentKeys& keys() const { return m_pData->keys; } // Drumflags banks accessors. bool isDrum(int iBank, int iProg) const; void setDrum(int iBank, int iProg, bool bDrum) { m_pData->drums[iBank][iProg] = (int) bDrum; } const qtractorInstrumentDrums& drums() const { return m_pData->drums; } protected: // Copy/clone method. void attach(const qtractorInstrument& instr) { m_pData = instr.m_pData; ++(m_pData->refCount); } // Destroy method. void detach() { if (--(m_pData->refCount) == 0) delete m_pData; } private: // The ref-counted data. struct DataRef { // Default payload constructor. DataRef() : refCount(1), bankSelMethod(0), usesNotesAsControllers(false) {}; // Payload members. int refCount; int bankSelMethod; bool usesNotesAsControllers; QString instrumentName; qtractorInstrumentPatches patches; qtractorInstrumentData controllers; qtractorInstrumentData rpns; qtractorInstrumentData nrpns; qtractorInstrumentKeys keys; qtractorInstrumentDrums drums; } *m_pData; }; //---------------------------------------------------------------------- // class qtractorInstrumentList -- A Cakewalk .ins file container class. // class qtractorInstrumentList : public QMap { public: // Open file methods. bool load(const QString& sFilename); bool save(const QString& sFilename) const; // The official loaded file list. const QStringList& files() const; // Manage a file list (out of sync) void appendFile(const QString& sFilename) { m_files.append(sFilename); } void removeFile(const QString& sFilename) { const int iFile = m_files.indexOf(sFilename); if (iFile >= 0) m_files.removeAt(iFile); } // Patch Names definition accessors. const qtractorInstrumentDataList& patches() const { return m_patches; } const qtractorInstrumentData& patches(const QString& sName) { return m_patches[sName]; } // Note Names definition accessors. const qtractorInstrumentDataList& notes() const { return m_notes; } const qtractorInstrumentData& notes(const QString& sName) { return m_notes[sName]; } // Controller Names definition accessors. const qtractorInstrumentDataList& controllers() const { return m_controllers; } const qtractorInstrumentData& controllers(const QString& sName) { return m_controllers[sName]; } // RPN Names definition accessors. const qtractorInstrumentDataList& rpns() const { return m_rpns; } const qtractorInstrumentData& rpns(const QString& sName) { return m_rpns[sName]; } // NRPN Names definition accessors. const qtractorInstrumentDataList& nrpns() const { return m_nrpns; } const qtractorInstrumentData& nrpns(const QString& sName) { return m_nrpns[sName]; } // Clear all contents. void clearAll(); // Special instrument list merge method. void merge(const qtractorInstrumentList& instruments); // Special MIDINameDocument loader. bool loadMidiNameDocument(const QDomDocument& doc); protected: // Internal instrument data list save method helpers. void saveDataList(QTextStream& ts, const qtractorInstrumentDataList& list) const; void saveData(QTextStream& ts, const qtractorInstrumentData& data) const; // Special instrument data list merge method. void mergeDataList(qtractorInstrumentDataList& dst, const qtractorInstrumentDataList& src); // Special SoundFont loader. bool loadSoundFont(QFile *pFile); void loadSoundFontPresets(QFile *pFile, int iSize); // Special MIDINameDocument loaders. void loadMidiDeviceNames( QDomElement *pElement); void loadMidiChannelNameSet( QDomElement *pElement, qtractorInstrument& instr); void loadMidiPatchBank( QDomElement *pElement, qtractorInstrument& instr, const QString& sName); void loadMidiPatchNameList( QDomElement *pElement, qtractorInstrument& instr, const QString& sName); void loadMidiNoteNameList( QDomElement *pElement, const QString& sName); void loadMidiControlNameList( QDomElement *pElement, const QString& sName); private: // To hold the names definition lists. qtractorInstrumentDataList m_patches; qtractorInstrumentDataList m_notes; qtractorInstrumentDataList m_controllers; qtractorInstrumentDataList m_rpns; qtractorInstrumentDataList m_nrpns; // To old the official file list. QStringList m_files; }; #endif // __qtractorInstrument_h // end of qtractorInstrument.h qtractor-1.5.9/src/PaxHeaders/qtractorMidiToolsForm.ui0000644000000000000000000000013215101070305020070 xustar0030 mtime=1761898693.084267645 30 atime=1761898693.084267645 30 ctime=1761898693.084267645 qtractor-1.5.9/src/qtractorMidiToolsForm.ui0000644000175000001440000014650515101070305020073 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.9/src/PaxHeaders/qtractorFileSystem.cpp0000644000000000000000000000013215101070305017572 xustar0030 mtime=1761898693.071267604 30 atime=1761898693.071267604 30 ctime=1761898693.071267604 qtractor-1.5.9/src/qtractorFileSystem.cpp0000644000175000001440000004100715101070305017564 0ustar00rncbcusers// qtractorFileSystem.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 "qtractorFileSystem.h" #include "qtractorAudioFile.h" #include "qtractorDocument.h" #include "qtractorMainForm.h" #include #include #include #include #include #include #include #include //---------------------------------------------------------------------------- // qtractorFileSystem::FileSystemModel -- Custom file-system model. class qtractorFileSystem::FileSystemModel : public QFileSystemModel { public: // ctor. FileSystemModel(QObject *pParent = 0) : QFileSystemModel(pParent) {} protected: // flags override. Qt::ItemFlags flags(const QModelIndex& index) const { Qt::ItemFlags ret = QFileSystemModel::flags(index); if (QFileSystemModel::isDir(index)) ret &= ~Qt::ItemIsDragEnabled; return ret; } }; //---------------------------------------------------------------------------- // qdirview_wigdet -- Custom composite widget. // ctor. qtractorFileSystem::qtractorFileSystem ( QWidget *pParent ) : QDockWidget(pParent) { // Surely a name is crucial (e.g.for storing geometry settings) QDockWidget::setObjectName("qtractorFileSystem"); // Setup member widgets... m_pHomeAction = new QAction( QIcon::fromTheme("itemHome"), tr("&Home"), this); m_pCdUpAction = new QAction( QIcon::fromTheme("itemCdUp"), tr("&Up"), this); m_pAllFilesAction = new QAction(tr("Al&l Files"), this); m_pAllFilesAction->setCheckable(true); m_pSessionFilesAction = new QAction( QIcon::fromTheme("itemSessionFile"), tr("&Session"), this); m_pSessionFilesAction->setCheckable(true); m_pAudioFilesAction = new QAction( QIcon::fromTheme("itemAudioFile"), tr("&Audio"), this); m_pAudioFilesAction->setCheckable(true); m_pMidiFilesAction = new QAction( QIcon::fromTheme("itemMidiFile"), tr("&MIDI"), this); m_pMidiFilesAction->setCheckable(true); m_pHiddenFilesAction = new QAction(tr("H&idden"), this); m_pHiddenFilesAction->setCheckable(true); m_pPlayAction = new QAction( QIcon::fromTheme("transportPlay"), tr("&Play"), this); m_pPlayAction->setCheckable(true); // Setup member widgets... m_pHomeToolButton = new QToolButton(); m_pHomeToolButton->setDefaultAction(m_pHomeAction); m_pHomeToolButton->setFocusPolicy(Qt::NoFocus); m_pCdUpToolButton = new QToolButton(); m_pCdUpToolButton->setDefaultAction(m_pCdUpAction); m_pCdUpToolButton->setFocusPolicy(Qt::NoFocus); m_pRootPathComboBox = new QComboBox(); m_pRootPathComboBox->setEditable(true); // m_pRootPathComboBox->setInsertPolicy(QComboBox::NoInsert); m_pFileSystemTreeView = new QTreeView(); m_pFileSystemTreeView->setAnimated(true); m_pFileSystemTreeView->setIndentation(20); m_pFileSystemTreeView->setSortingEnabled(true); m_pFileSystemTreeView->sortByColumn(0, Qt::AscendingOrder); m_pFileSystemTreeView->setDragEnabled(true); m_pFileSystemTreeView->viewport()->setAcceptDrops(false); m_pFileSystemTreeView->setSelectionBehavior( QAbstractItemView::SelectRows); m_pFileSystemTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection); m_pFileSystemModel = nullptr; QHeaderView *pHeaderView = m_pFileSystemTreeView->header(); #if 0 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) pHeaderView->setResizeMode(QHeaderView::ResizeToContents); #else pHeaderView->setSectionResizeMode(QHeaderView::ResizeToContents); #endif #else pHeaderView->setStretchLastSection(true); pHeaderView->setDefaultSectionSize(120); pHeaderView->resizeSection(0, 240); // Name pHeaderView->resizeSection(1, 80); // Size pHeaderView->resizeSection(2, 60); // Type pHeaderView->resizeSection(3, 120); // Date #endif // Setup UI layout... QWidget *pGridWidget = new QWidget(); QGridLayout *pGridLayout = new QGridLayout(); pGridLayout->setContentsMargins(0, 0, 0, 0); pGridLayout->setSpacing(2); pGridLayout->addWidget(m_pHomeToolButton, 0, 0); pGridLayout->addWidget(m_pCdUpToolButton, 0, 1); pGridLayout->addWidget(m_pRootPathComboBox, 0, 2); pGridLayout->addWidget(m_pFileSystemTreeView, 1, 0, 1, 3); pGridLayout->setColumnStretch(2, 2); pGridLayout->setRowStretch(1, 4); pGridWidget->setLayout(pGridLayout); // Prepare the dockable window stuff. QDockWidget::setWidget(pGridWidget); // QDockWidget::setFeatures(QDockWidget::AllDockWidgetFeatures); QDockWidget::setAllowedAreas( Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); // Some specialties to this kind of dock window... QDockWidget::setMinimumWidth(160); // Finally set the default caption and tooltip. const QString& sCaption = tr("File System"); QDockWidget::setWindowTitle(sCaption); QDockWidget::setWindowIcon(QIcon::fromTheme("viewFileSystem")); QDockWidget::setToolTip(sCaption); // Setup signal/slot connections... QObject::connect(m_pHomeAction, SIGNAL(triggered(bool)), SLOT(homeClicked())); QObject::connect(m_pCdUpAction, SIGNAL(triggered(bool)), SLOT(cdUpClicked())); QObject::connect(m_pAllFilesAction, SIGNAL(triggered(bool)), SLOT(filterChanged())); QObject::connect(m_pSessionFilesAction, SIGNAL(triggered(bool)), SLOT(filterChanged())); QObject::connect(m_pAudioFilesAction, SIGNAL(triggered(bool)), SLOT(filterChanged())); QObject::connect(m_pMidiFilesAction, SIGNAL(triggered(bool)), SLOT(filterChanged())); QObject::connect(m_pHiddenFilesAction, SIGNAL(triggered(bool)), SLOT(filterChanged())); QObject::connect(m_pPlayAction, SIGNAL(triggered(bool)), SLOT(playSlot(bool))); QObject::connect(m_pRootPathComboBox, #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) SIGNAL(textActivated(const QString&)), #else SIGNAL(activated(const QString&)), #endif SLOT(rootPathActivated(const QString&))); QObject::connect(m_pFileSystemTreeView, SIGNAL(doubleClicked(const QModelIndex&)), SLOT(treeViewActivated(const QModelIndex&))); updateRootPath(QDir::homePath()); } // dtor. qtractorFileSystem::~qtractorFileSystem (void) { // Setup member widgets... m_pFileSystemTreeView->setModel(nullptr); if (m_pFileSystemModel) delete m_pFileSystemModel; delete m_pPlayAction; delete m_pHiddenFilesAction; delete m_pMidiFilesAction; delete m_pAudioFilesAction; delete m_pSessionFilesAction; delete m_pAllFilesAction; delete m_pCdUpAction; delete m_pHomeAction; } // Accessors. void qtractorFileSystem::setRootPath ( const QString& sRootPath ) { updateRootPath(sRootPath); } QString qtractorFileSystem::rootPath (void) const { QString sRootPath; FileSystemModel *pFileSystemModel = static_cast (m_pFileSystemTreeView->model()); if (pFileSystemModel) sRootPath = pFileSystemModel->rootPath(); return sRootPath; } void qtractorFileSystem::setFlags ( Flags flags, bool on ) { if (flags & AllFiles) m_pAllFilesAction->setChecked(on); else if (flags & (SessionFiles | AudioFiles | MidiFiles)) m_pAllFilesAction->setChecked(!on); if (flags & SessionFiles) m_pSessionFilesAction->setChecked(on); if (flags & AudioFiles) m_pAudioFilesAction->setChecked(on); if (flags & MidiFiles) m_pMidiFilesAction->setChecked(on); if (flags & HiddenFiles) m_pHiddenFilesAction->setChecked(on); } qtractorFileSystem::Flags qtractorFileSystem::flags (void) const { int flags = 0; if (m_pAllFilesAction->isChecked()) flags |= AllFiles; if (m_pSessionFilesAction->isChecked()) flags |= SessionFiles; if (m_pAudioFilesAction->isChecked()) flags |= AudioFiles; if (m_pMidiFilesAction->isChecked()) flags |= MidiFiles; if (m_pHiddenFilesAction->isChecked()) flags |= HiddenFiles; return Flags(flags); } // Model factory method. void qtractorFileSystem::updateRootPath ( const QString& sRootPath ) { QString sCurrentPath; QModelIndex index = m_pFileSystemTreeView->currentIndex(); if (index.isValid() && m_pFileSystemModel) sCurrentPath = m_pFileSystemModel->filePath(index); const QFileInfo info(sRootPath); if (info.isDir() && info.isReadable()) { FileSystemModel *pFileSystemModel = new FileSystemModel(this); QObject::connect(pFileSystemModel, SIGNAL(directoryLoaded(const QString&)), SLOT(restoreStateLoading(const QString&))); pFileSystemModel->setReadOnly(true); pFileSystemModel->setRootPath(sRootPath); m_pFileSystemTreeView->setModel(pFileSystemModel); if (m_pFileSystemModel) delete m_pFileSystemModel; m_pFileSystemModel = pFileSystemModel; index = m_pFileSystemModel->index(sRootPath); if (index.isValid()) { m_pFileSystemTreeView->setRootIndex(index); m_pFileSystemTreeView->setExpanded(index, true); } index = m_pFileSystemModel->index(sCurrentPath); if (index.isValid()) m_pFileSystemTreeView->setCurrentIndex(index); } updateRootPath(); } // Model stabilizers. void qtractorFileSystem::updateRootPath (void) { m_pRootPathComboBox->clear(); if (m_pFileSystemModel) { const QString& sRootPath = m_pFileSystemModel->rootPath(); m_pHomeAction->setEnabled(sRootPath != QDir::homePath()); m_pRootPathComboBox->addItem(sRootPath); QDir dir(sRootPath); while (dir.cdUp()) m_pRootPathComboBox->addItem(dir.absolutePath()); } m_pCdUpAction->setEnabled(m_pRootPathComboBox->count() > 1); updateFilter(); } void qtractorFileSystem::updateFilter (void) { if (m_pFileSystemModel == nullptr) return; QStringList filters; const QString sExtMask("*.%1"); if (m_pSessionFilesAction->isChecked()) { filters.append(sExtMask.arg("qtr")); filters.append(sExtMask.arg(qtractorDocument::defaultExt())); filters.append(sExtMask.arg(qtractorDocument::templateExt())); #ifdef CONFIG_LIBZ filters.append(sExtMask.arg(qtractorDocument::archiveExt())); #endif } if (m_pAudioFilesAction->isChecked()) { QStringListIterator iter(qtractorAudioFileFactory::exts()); while (iter.hasNext()) filters.append(sExtMask.arg(iter.next())); } if (m_pMidiFilesAction->isChecked()) { filters.append(sExtMask.arg("midi")); filters.append(sExtMask.arg("mid")); filters.append(sExtMask.arg("smf")); } m_pFileSystemModel->setNameFilters(filters); if (filters.isEmpty()) { const bool bBlockSignals = m_pAllFilesAction->blockSignals(true); m_pAllFilesAction->setChecked(true); m_pAllFilesAction->blockSignals(bBlockSignals); } m_pFileSystemModel->setNameFilterDisables( m_pAllFilesAction->isChecked()); QDir::Filters flags = m_pFileSystemModel->filter(); if (m_pHiddenFilesAction->isChecked()) flags |= QDir::Hidden; else flags &= ~QDir::Hidden; m_pFileSystemModel->setFilter(flags); } // Chdir slots. void qtractorFileSystem::cdUpClicked (void) { if (m_pFileSystemModel) { const QString sRootPath = m_pFileSystemModel->rootPath(); QDir dir(sRootPath); if (dir.cdUp()) updateRootPath(dir.absolutePath()); } } void qtractorFileSystem::homeClicked (void) { updateRootPath(QDir::homePath()); } void qtractorFileSystem::rootPathActivated ( const QString& sRootPath ) { updateRootPath(sRootPath); } void qtractorFileSystem::treeViewActivated ( const QModelIndex& index ) { if (m_pFileSystemModel) { const QString& sFilename = m_pFileSystemModel->filePath(index); if (m_pFileSystemModel->isDir(index)) updateRootPath(sFilename); else activateFile(sFilename); } } // Filter slots. void qtractorFileSystem::filterChanged (void) { updateFilter(); } // Just about to notify main-window that we're closing. void qtractorFileSystem::closeEvent ( QCloseEvent * /*pCloseEvent*/ ) { QDockWidget::hide(); qtractorMainForm *pMainForm = qtractorMainForm::getInstance(); if (pMainForm) pMainForm->stabilizeForm(); } // Context-menu event handler. void qtractorFileSystem::contextMenuEvent ( QContextMenuEvent *pContextMenuEvent ) { QMenu menu(this); menu.addAction(m_pHomeAction); menu.addAction(m_pCdUpAction); menu.addSeparator(); menu.addAction(m_pAllFilesAction); menu.addAction(m_pSessionFilesAction); menu.addAction(m_pAudioFilesAction); menu.addAction(m_pMidiFilesAction); menu.addAction(m_pHiddenFilesAction); menu.addSeparator(); menu.addAction(m_pPlayAction); menu.exec(pContextMenuEvent->globalPos()); } // Audition/pre-listening player methods. void qtractorFileSystem::setPlayState ( bool bOn ) { const bool bBlockSignals = m_pPlayAction->blockSignals(true); m_pPlayAction->setChecked(bOn); m_pPlayAction->blockSignals(bBlockSignals); } bool qtractorFileSystem::isPlayState (void) const { return m_pPlayAction->isChecked(); } // Audition/pre-listening player slot. void qtractorFileSystem::playSlot ( bool bOn ) { if (m_pFileSystemModel && bOn) { const QModelIndex& index = m_pFileSystemTreeView->currentIndex(); if (index.isValid()) activateFile(m_pFileSystemModel->filePath(index)); else setPlayState(false); } else setPlayState(false); } void qtractorFileSystem::activateFile ( const QString& sFilename ) { const QFileInfo info(sFilename); if (info.isFile() && info.isReadable()) { setPlayState(true); emit activated(info.absoluteFilePath()); } else { setPlayState(false); } } // State saver. QByteArray qtractorFileSystem::saveState (void) const { #if QT_VERSION < QT_VERSION_CHECK(5, 4, 0) QList list; #else QByteArrayList list; #endif // 1. header-view state... list.append(m_pFileSystemTreeView->header()->saveState()); // 2. file-filter flags... list.append(QByteArray::number(int(flags()))); if (m_pFileSystemModel) { // 3. current-path... QModelIndex index = m_pFileSystemTreeView->currentIndex(); list.append(m_pFileSystemModel->filePath(index).toUtf8()); // 4. root-path and expanded/open folders... const QString& sRootPath = m_pFileSystemModel->rootPath(); index = m_pFileSystemModel->index(sRootPath); while (index.isValid()) { if (m_pFileSystemTreeView->isExpanded(index)) list.append(m_pFileSystemModel->filePath(index).toUtf8()); index = m_pFileSystemTreeView->indexBelow(index); } } #if QT_VERSION < QT_VERSION_CHECK(5, 4, 0) QByteArray state; QListIterator iter(list); while (iter.hasNext()) { state.append(iter.next()); state.append(':'); } return state; #else return list.join(':'); #endif } // State loader. bool qtractorFileSystem::restoreState ( const QByteArray& state ) { int i = 0; QString sCurrentPath; m_sRestoreStatePath.clear(); #if QT_VERSION < QT_VERSION_CHECK(5, 4, 0) QListIterator iter(state.split(':')); #else QByteArrayListIterator iter(state.split(':')); #endif while (iter.hasNext()) { const QByteArray& data = iter.next(); switch (++i) { case 1: // header-view state... m_pFileSystemTreeView->header()->restoreState(data); break; case 2: // file-filter flags... setFlags(Flags(data.toInt())); break; case 3: // current-path... sCurrentPath = QString::fromUtf8(data); break; case 4: // root-path... updateRootPath(QString::fromUtf8(data)); break; default: // expanded/open folders... if (m_pFileSystemModel) { const QModelIndex& index = m_pFileSystemModel->index(QString::fromUtf8(data)); if (index.isValid()) m_pFileSystemTreeView->setExpanded(index, true); } break; } } if (m_pFileSystemModel && !sCurrentPath.isEmpty()) { const QModelIndex& index = m_pFileSystemModel->index(sCurrentPath); if (index.isValid()) { m_sRestoreStatePath = QFileInfo(sCurrentPath).absolutePath(); m_pFileSystemTreeView->setCurrentIndex(index); } } return (i > 0); } // Directory gathering thread doing sth... void qtractorFileSystem::restoreStateLoading ( const QString& sPath ) { if (m_sRestoreStatePath.isEmpty()) return; if (sPath == m_sRestoreStatePath) QTimer::singleShot(200, this, SLOT(restoreStateTimeout())); } void qtractorFileSystem::restoreStateTimeout (void) { if (m_sRestoreStatePath.isEmpty()) return; const QModelIndex& index = m_pFileSystemTreeView->currentIndex(); if (index.isValid()) m_pFileSystemTreeView->scrollTo(index); m_sRestoreStatePath.clear(); } // end of qtractorFileSystem.cpp qtractor-1.5.9/src/PaxHeaders/qtractorAudioIOMatrixForm.ui0000644000000000000000000000013215101070305020643 xustar0030 mtime=1761898693.063267578 30 atime=1761898693.063267578 30 ctime=1761898693.063267578 qtractor-1.5.9/src/qtractorAudioIOMatrixForm.ui0000644000175000001440000000447015101070305020640 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.9/src/PaxHeaders/qtractorPasteRepeatForm.ui0000644000000000000000000000013215101070305020402 xustar0030 mtime=1761898693.086267651 30 atime=1761898693.086267651 30 ctime=1761898693.086267651 qtractor-1.5.9/src/qtractorPasteRepeatForm.ui0000644000175000001440000001126015101070305020372 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. qtractorPasteRepeatForm 0 0 260 160 Qt::StrongFocus Paste Repeat Repeat &Count: RepeatCountSpinBox Repeat count 2 200 Qt::Horizontal 110 20 &Period: 120 0 Repeat period Repeat period format Frames Time BBT Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok qtractorTimeSpinBox QSpinBox
qtractorSpinBox.h
RepeatCountSpinBox RepeatPeriodCheckBox RepeatPeriodSpinBox RepeatFormatComboBox DialogButtonBox
qtractor-1.5.9/src/PaxHeaders/qtractorEngineCommand.h0000644000000000000000000000013215101070305017657 xustar0030 mtime=1761898693.070267601 30 atime=1761898693.070267601 30 ctime=1761898693.070267601 qtractor-1.5.9/src/qtractorEngineCommand.h0000644000175000001440000001417615101070305017660 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.9/src/PaxHeaders/qtractorDocument.h0000644000000000000000000000013215101070305016731 xustar0030 mtime=1761898693.069267597 30 atime=1761898693.069267597 30 ctime=1761898693.069267597 qtractor-1.5.9/src/qtractorDocument.h0000644000175000001440000000736715101070305016736 0ustar00rncbcusers// qtractorDocument.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 __qtractorDocument_h #define __qtractorDocument_h #include // Forward declartions. class QDomDocument; class QDomElement; class qtractorZipFile; //------------------------------------------------------------------------- // qtractorDocument -- Document file import/export abstract class. // class qtractorDocument { public: // Document flags. enum Flags { Default = 0, Template = 1, Archive = 2, SymLink = 4, Temporary = 8 }; // Constructor. qtractorDocument(QDomDocument *pDocument, const QString& sTagName = QString(), Flags = Default); // Default destructor. virtual ~qtractorDocument(); // Accessors. QDomDocument *document() const; const QString& tagName() const; // Regular text element factory method. void saveTextElement (const QString& sTagName, const QString& sText, QDomElement *pElement); // Document flags property accessors. bool isTemplate() const; bool isArchive() const; bool isTemporary() const; bool isSymLink() const; // Archive filename filter. QString addFile (const QString& sFilename); // External storage simple methods. bool load(const QString& sFilename, Flags flags = Default); bool save(const QString& sFilename, Flags flags = Default); // Helper methods. static bool boolFromText (const QString& sText); static QString textFromBool (bool bBool); // Filename extensions (suffix) accessors. static void setDefaultExt (const QString& sDefaultExt); static void setTemplateExt (const QString& sTemplateExt); static void setArchiveExt (const QString& sArchiveExt); static const QString& defaultExt(); static const QString& templateExt(); static const QString& archiveExt(); // Extracted archive paths simple management. static const QStringList& extractedArchives(); static void clearExtractedArchives(bool bRemove = false); // Extra-ordinary archive files management. static QString addFile(const QString& sDir, const QString& sFilename); protected: // Document flags property. void setFlags(Flags flags); Flags flags() const; // External storage element pure virtual methods. virtual bool loadElement (QDomElement *pElement) = 0; virtual bool saveElement (QDomElement *pElement) = 0; private: // Instance variables. QDomDocument *m_pDocument; QString m_sTagName; // Document flags Flags m_flags; // Document name (derived from filename). QString m_sName; // Archive stuff. qtractorZipFile *m_pZipFile; // Temporary files; QStringList m_tempFiles; // Filename extensions (file suffixes). static QString g_sDefaultExt; static QString g_sTemplateExt; static QString g_sArchiveExt; // Extracted archive paths. static QStringList g_extractedArchives; // Extra-ordinary archive files. static qtractorDocument *g_pDocument; }; #endif // __qtractorDocument_h // end of qtractorDocument.h qtractor-1.5.9/src/PaxHeaders/vst30000644000000000000000000000013215101070305014044 xustar0030 mtime=1761898693.108314407 30 atime=1761898693.107267718 30 ctime=1761898693.108314407 qtractor-1.5.9/src/vst3/0000755000175000001440000000000015101070305014111 5ustar00rncbcusersqtractor-1.5.9/src/vst3/PaxHeaders/pluginterfaces0000644000000000000000000000013215101070347017065 xustar0030 mtime=1761898727.395376451 30 atime=1761898693.107267718 30 ctime=1761898727.395376451 qtractor-1.5.9/src/vst3/pluginterfaces/0000755000175000001440000000000015101070347017132 5ustar00rncbcusersqtractor-1.5.9/src/vst3/pluginterfaces/PaxHeaders/gui0000644000000000000000000000013215101070323017643 xustar0030 mtime=1761898707.903396037 30 atime=1761898707.902314635 30 ctime=1761898707.903396037 qtractor-1.5.9/src/vst3/pluginterfaces/gui/0000755000175000001440000000000015101070323017710 5ustar00rncbcusersqtractor-1.5.9/src/vst3/pluginterfaces/gui/PaxHeaders/iplugview.h0000644000000000000000000000013215101070323022104 xustar0030 mtime=1761898707.902314635 30 atime=1761898707.902314635 30 ctime=1761898707.902314635 qtractor-1.5.9/src/vst3/pluginterfaces/gui/iplugview.h0000644000175000001440000002753415101070323022107 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) /*@}*/ //------------------------------------------------------------------------ //------------------------------------------------------------------------ /** 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.9/src/vst3/pluginterfaces/gui/PaxHeaders/iplugviewcontentscalesupport.h0000644000000000000000000000013215101070323026144 xustar0030 mtime=1761898707.903396037 30 atime=1761898707.902314635 30 ctime=1761898707.903396037 qtractor-1.5.9/src/vst3/pluginterfaces/gui/iplugviewcontentscalesupport.h0000644000175000001440000000665615101070323026151 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: //------------------------------------------------------------------------ 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.9/src/vst3/pluginterfaces/PaxHeaders/vst0000644000000000000000000000013215101070323017673 xustar0030 mtime=1761898707.904314641 30 atime=1761898707.903396037 30 ctime=1761898707.904314641 qtractor-1.5.9/src/vst3/pluginterfaces/vst/0000755000175000001440000000000015101070323017740 5ustar00rncbcusersqtractor-1.5.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstparameterchanges.h0000644000000000000000000000013215101070323024340 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstparameterchanges.h0000644000175000001440000001416215101070323024334 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstpluginterfacesupport.h0000644000000000000000000000013215101070323025314 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstpluginterfacesupport.h0000644000175000001440000000457315101070323025315 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstmidilearn.h0000644000000000000000000000013215101070323022773 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstmidilearn.h0000644000175000001440000000755115101070323022773 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 inform the host via 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstrepresentation.h0000644000000000000000000000013215101070323024071 xustar0030 mtime=1761898707.904314641 30 atime=1761898707.903554677 30 ctime=1761898707.904314641 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstrepresentation.h0000644000175000001440000003423715101070323024072 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstaudioprocessor.h0000644000000000000000000000013215101070323024070 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903396037 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstaudioprocessor.h0000644000175000001440000005710715101070323024072 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstmessage.h0000644000000000000000000000013215101070323022453 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstmessage.h0000644000175000001440000000730715101070323022452 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstcontextmenu.h0000644000000000000000000000013215101070323023400 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstcontextmenu.h0000644000175000001440000002053015101070323023370 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivsttestplugprovider.h0000644000000000000000000000013215101070323024451 xustar0030 mtime=1761898707.904314641 30 atime=1761898707.904314641 30 ctime=1761898707.904314641 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivsttestplugprovider.h0000644000175000001440000001115615101070323024445 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 // //----------------------------------------------------------------------------- // LICENSE // (c) 2022, Steinberg Media Technologies GmbH, 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 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 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. //----------------------------------------------------------------------------- #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.9/src/vst3/pluginterfaces/vst/PaxHeaders/vstpresetkeys.h0000644000000000000000000000013215101070323023054 xustar0030 mtime=1761898707.904314641 30 atime=1761898707.904314641 30 ctime=1761898707.904314641 qtractor-1.5.9/src/vst3/pluginterfaces/vst/vstpresetkeys.h0000644000175000001440000004501015101070323023044 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/vstpshpack4.h0000644000000000000000000000013215101070323022373 xustar0030 mtime=1761898707.904314641 30 atime=1761898707.904314641 30 ctime=1761898707.904314641 qtractor-1.5.9/src/vst3/pluginterfaces/vst/vstpshpack4.h0000644000175000001440000000227215101070323022366 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstplugview.h0000644000000000000000000000013215101070323022671 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstplugview.h0000644000175000001440000000521415101070323022663 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstattributes.h0000644000000000000000000000013215101070323023215 xustar0030 mtime=1761898707.903396037 30 atime=1761898707.903396037 30 ctime=1761898707.903396037 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstattributes.h0000644000175000001440000001301715101070323023207 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/vstspeaker.h0000644000000000000000000000013215101070323022310 xustar0030 mtime=1761898707.904314641 30 atime=1761898707.904314641 30 ctime=1761898707.904314641 qtractor-1.5.9/src/vst3/pluginterfaces/vst/vstspeaker.h0000644000175000001440000017073415101070323022314 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstparameterfunctionname.h0000644000000000000000000000013215101070323025416 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstparameterfunctionname.h0000644000175000001440000001463115101070323025413 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstnoteexpression.h0000644000000000000000000000013215101070323024114 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstnoteexpression.h0000644000175000001440000003122315101070323024105 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 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstphysicalui.h0000644000000000000000000000013215101070323023201 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstphysicalui.h0000644000175000001440000001417615101070323023202 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstprefetchablesupport.h0000644000000000000000000000013215101070323025110 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstprefetchablesupport.h0000644000175000001440000000755515101070323025114 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstautomationstate.h0000644000000000000000000000013215101070323024250 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstautomationstate.h0000644000175000001440000000472115101070323024244 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstremapparamid.h0000644000000000000000000000013215101070323023471 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstremapparamid.h0000644000175000001440000000630515101070323023465 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivsteditcontroller.h0000644000000000000000000000013215101070323024060 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivsteditcontroller.h0000644000175000001440000007457215101070323024067 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/vsttypes.h0000644000000000000000000000013215101070323022022 xustar0030 mtime=1761898707.904314641 30 atime=1761898707.904314641 30 ctime=1761898707.904314641 qtractor-1.5.9/src/vst3/pluginterfaces/vst/vsttypes.h0000644000175000001440000001720515101070323022017 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.7.14" ///< SDK version for PClassInfo2 #endif #define kVstVersionMajor 3 #define kVstVersionMinor 7 #define kVstVersionSub 14 #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_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_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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstmidicontrollers.h0000644000000000000000000000013215101070323024240 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstmidicontrollers.h0000644000175000001440000001137015101070323024232 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) }; //------------------------------------------------------------------------ } // namespace Vst } // namespace Steinberg qtractor-1.5.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstinterappaudio.h0000644000000000000000000000013215101070323023673 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstinterappaudio.h0000644000175000001440000001152615101070323023670 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstevents.h0000644000000000000000000000013215101070323022333 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstevents.h0000644000175000001440000002031015101070323022317 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 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 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstchannelcontextinfo.h0000644000000000000000000000013215101070323024720 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstchannelcontextinfo.h0000644000175000001440000002212515101070323024712 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstunits.h0000644000000000000000000000013215101070323022171 xustar0030 mtime=1761898707.904314641 30 atime=1761898707.904314641 30 ctime=1761898707.904314641 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstunits.h0000644000175000001440000003111715101070323022164 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivsthostapplication.h0000644000000000000000000000013215101070323024230 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivsthostapplication.h0000644000175000001440000001344715101070323024231 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstprocesscontext.h0000644000000000000000000000013215101070323024112 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstprocesscontext.h0000644000175000001440000001517215101070323024110 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstcomponent.h0000644000000000000000000000013215101070323023031 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstcomponent.h0000644000175000001440000002210315101070323023017 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.9/src/vst3/pluginterfaces/vst/PaxHeaders/ivstdataexchange.h0000644000000000000000000000013215101070323023443 xustar0030 mtime=1761898707.903554677 30 atime=1761898707.903554677 30 ctime=1761898707.903554677 qtractor-1.5.9/src/vst3/pluginterfaces/vst/ivstdataexchange.h0000644000175000001440000002262615101070323023443 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.9/src/vst3/pluginterfaces/PaxHeaders/CMakeLists.txt0000644000000000000000000000013215101070323021674 xustar0030 mtime=1761898707.901314632 30 atime=1761898707.901314632 30 ctime=1761898707.901314632 qtractor-1.5.9/src/vst3/pluginterfaces/CMakeLists.txt0000644000175000001440000000625015101070323021667 0ustar00rncbcusers # pluginterfaces should actually be a header-only library, # but it has some sources as well which need compilation. cmake_minimum_required(VERSION 3.25.0) project(smtg_pluginterfaces) # base interfaces add_library(pluginterfaces STATIC base/conststringtable.cpp base/conststringtable.h base/coreiids.cpp base/falignpop.h base/falignpush.h base/fplatform.h base/fstrdefs.h base/ftypes.h base/funknown.cpp base/funknown.h base/funknownimpl.h base/futils.h base/fvariant.h base/geoconstants.h base/ibstream.h base/icloneable.h base/ierrorcontext.h base/ipersistent.h base/ipluginbase.h base/istringresult.h base/iupdatehandler.h base/keycodes.h base/pluginbasefwd.h base/smartpointer.h base/typesizecheck.h base/ucolorspec.h base/ustring.cpp base/ustring.h ) # check for C11 atomic header include(CheckSourceCompiles) set(SMTG_CHECK_STDATOMIC_H_SRC "#include int main () { atomic_int_least32_t value = 0; atomic_fetch_add (&value, 1); return 0; }" ) #set(CMAKE_REQUIRED_QUIET 1) check_source_compiles(CXX "${SMTG_CHECK_STDATOMIC_H_SRC}" SMTG_USE_STDATOMIC_H) if(SMTG_USE_STDATOMIC_H) target_compile_definitions(pluginterfaces PRIVATE "SMTG_USE_STDATOMIC_H=${SMTG_USE_STDATOMIC_H}" ) endif(SMTG_USE_STDATOMIC_H) # vst3 interfaces if (VST_SDK) target_sources(pluginterfaces PRIVATE gui/iplugview.h gui/iplugviewcontentscalesupport.h vst/ivstattributes.h vst/ivstaudioprocessor.h vst/ivstautomationstate.h vst/ivstchannelcontextinfo.h vst/ivstcomponent.h vst/ivstcontextmenu.h vst/ivsteditcontroller.h vst/ivstevents.h vst/ivstdataexchange.h vst/ivsthostapplication.h vst/ivstinterappaudio.h vst/ivstmessage.h vst/ivstmidicontrollers.h vst/ivstmidilearn.h vst/ivstnoteexpression.h vst/ivstparameterchanges.h vst/ivstparameterfunctionname.h vst/ivstphysicalui.h vst/ivstpluginterfacesupport.h vst/ivstplugview.h vst/ivstprefetchablesupport.h vst/ivstprocesscontext.h vst/ivstremapparamid.h vst/ivstrepresentation.h vst/ivstunits.h vst/vstpresetkeys.h vst/vstpshpack4.h vst/vstspeaker.h vst/vsttypes.h ) endif() # ski interfaces if (SKI_SDK) target_sources(pluginterfaces PRIVATE host/paramids.cpp host/paramids.h ) endif() target_include_directories(pluginterfaces PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/.. ) target_compile_features(pluginterfaces PUBLIC cxx_std_17 ) smtg_target_setup_universal_binary(pluginterfaces) # iOS target if(SMTG_MAC AND XCODE AND SMTG_ENABLE_IOS_TARGETS) get_target_property(PLUGINTERFACES_SOURCES pluginterfaces SOURCES) add_library(pluginterfaces_ios STATIC ${PLUGINTERFACES_SOURCES}) smtg_target_set_platform_ios(pluginterfaces_ios) target_compile_features(pluginterfaces_ios PUBLIC cxx_std_17) endif() qtractor-1.5.9/src/vst3/pluginterfaces/PaxHeaders/LICENSE.txt0000644000000000000000000000013215101070323020757 xustar0030 mtime=1761898707.901314632 30 atime=1761898707.901314632 30 ctime=1761898707.901314632 qtractor-1.5.9/src/vst3/pluginterfaces/LICENSE.txt0000644000175000001440000000531115101070323020747 0ustar00rncbcusers//----------------------------------------------------------------------------- // LICENSE // (c) 2025, Steinberg Media Technologies GmbH, All Rights Reserved //----------------------------------------------------------------------------- This license applies only to files referencing this license, for other files of the Software Development Kit the respective embedded license text is applicable. The license can be found at: www.steinberg.net/sdklicenses_vst3 This Software Development Kit is licensed under the terms of the Steinberg VST3 License, or alternatively under the terms of the General Public License (GPL) Version 3. You may use the Software Development Kit according to either of these licenses as it is most appropriate for your project on a case-by-case basis (commercial or not). a) Proprietary Steinberg VST3 License The Software Development Kit may not be distributed in parts or its entirety without prior written agreement by Steinberg Media Technologies GmbH. The 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 GmbH nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. Before publishing a software under the proprietary license, you need to obtain a copy of the License Agreement signed by Steinberg Media Technologies GmbH. The Steinberg VST SDK License Agreement can be found at: www.steinberg.net/en/company/developers.html THE 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. b) General Public License (GPL) Version 3 Details of these licenses can be found at: www.gnu.org/licenses/gpl-3.0.html Please refer to the Steinberg VST usage guidelines for the use of VST, VST logo and VST compatible logos: https://steinbergmedia.github.io/vst3_dev_portal/pages/VST+3+Licensing/Usage+guidelines.html //---------------------------------------------------------------------------------- qtractor-1.5.9/src/vst3/pluginterfaces/PaxHeaders/test0000644000000000000000000000013215101070323020036 xustar0030 mtime=1761898707.903396037 30 atime=1761898707.903396037 30 ctime=1761898707.903396037 qtractor-1.5.9/src/vst3/pluginterfaces/test/0000755000175000001440000000000015101070323020103 5ustar00rncbcusersqtractor-1.5.9/src/vst3/pluginterfaces/test/PaxHeaders/itest.h0000644000000000000000000000013215101070323021414 xustar0030 mtime=1761898707.903396037 30 atime=1761898707.903396037 30 ctime=1761898707.903396037 qtractor-1.5.9/src/vst3/pluginterfaces/test/itest.h0000644000175000001440000001332015101070323021403 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.9/src/vst3/pluginterfaces/PaxHeaders/base0000644000000000000000000000013215101070323017771 xustar0030 mtime=1761898707.902314635 30 atime=1761898707.901314632 30 ctime=1761898707.902314635 qtractor-1.5.9/src/vst3/pluginterfaces/base/0000755000175000001440000000000015101070323020036 5ustar00rncbcusersqtractor-1.5.9/src/vst3/pluginterfaces/base/PaxHeaders/conststringtable.cpp0000644000000000000000000000013215101070323024137 xustar0030 mtime=1761898707.901314632 30 atime=1761898707.901314632 30 ctime=1761898707.901314632 qtractor-1.5.9/src/vst3/pluginterfaces/base/conststringtable.cpp0000644000175000001440000000615615101070323024137 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.9/src/vst3/pluginterfaces/base/PaxHeaders/smartpointer.h0000644000000000000000000000013215101070323022746 xustar0030 mtime=1761898707.902314635 30 atime=1761898707.902314635 30 ctime=1761898707.902314635 qtractor-1.5.9/src/vst3/pluginterfaces/base/smartpointer.h0000644000175000001440000002254415101070323022745 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