diff --git a/.travis.yml b/.travis.yml index 091e2cb45a9..66563786fbe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ dist: trusty sudo: required cache: directories: - - apt_mingw_cache + - $HOME/apt_mingw_cache - $HOME/.ccache - $HOME/pbuilder-bases matrix: @@ -14,9 +14,15 @@ matrix: - env: TARGET_OS=win32 - env: TARGET_OS=win64 - env: TARGET_OS=debian-sid TARGET_DEPLOY=True + git: + depth: false - env: TARGET_OS=debian-sid TARGET_ARCH=i386 + git: + depth: false - compiler: clang env: TARGET_OS=debian-sid + git: + depth: false - os: osx osx_image: xcode8.3 install: ${TRAVIS_BUILD_DIR}/.travis/install.sh diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index ab4fba26395..2f1262d071b 100755 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -23,4 +23,4 @@ sudo apt-get install -y $PACKAGES sudo add-apt-repository -y ppa:kxstudio-debian/libs sudo add-apt-repository -y ppa:kxstudio-debian/apps sudo apt-get update -sudo apt-get install -y carla-git +sudo apt-get install -y carla diff --git a/.travis/linux.debian-sid.script.sh b/.travis/linux.debian-sid.script.sh index a75a9f8442c..9b8db416c4d 100755 --- a/.travis/linux.debian-sid.script.sh +++ b/.travis/linux.debian-sid.script.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -e : "${TARGET_ARCH:=amd64}" @@ -21,19 +21,50 @@ fi if [ ! -e "$BASETGZ.stamp" ] then mkdir -p "$HOME/pbuilder-bases" - # debootstrap fails to resolve dependencies which are virtual packages - # e.g. perl-openssl-abi-1.1 provided by perl-openssl-defaults, needed for building SWH - # See also: https://bugs.launchpad.net/ubuntu/+source/debootstrap/+bug/86536 sudo pbuilder --create --basetgz "$BASETGZ" --mirror $MIRROR \ --distribution sid --architecture $TARGET_ARCH \ --debootstrapopts --variant=buildd \ --debootstrapopts --keyring=$KEYRING \ - --debootstrapopts --include=perl,libxml2-utils,libxml-perl,liblist-moreutils-perl,perl-openssl-defaults + --debootstrapopts --include=perl touch "$BASETGZ.stamp" else sudo pbuilder --update --basetgz "$BASETGZ" fi +sync_version() { + local VERSION + local MMR + local STAGE + local EXTRA + + VERSION=$(git describe --tags --match v[0-9].[0-9].[0-9]*) + VERSION=${VERSION#v} + MMR=${VERSION%%-*} + case $VERSION in + *-*-*-*) + VERSION=${VERSION%-*} + STAGE=${VERSION#*-} + STAGE=${STAGE%-*} + EXTRA=${VERSION##*-} + VERSION=$MMR~$STAGE.$EXTRA + ;; + *-*-*) + VERSION=${VERSION%-*} + EXTRA=${VERSION##*-} + VERSION=$MMR.$EXTRA + ;; + *-*) + STAGE=${VERSION#*-} + VERSION=$MMR~$STAGE + ;; + esac + + sed "1 s/@VERSION@/$VERSION/" -i debian/changelog + echo "Set Debian version to $VERSION" +} + +sync_version + DIR="$PWD" cd .. dpkg-source -b "$DIR" diff --git a/.travis/linux.win.download.sh b/.travis/linux.win.download.sh index 215d4eb93f4..2f914f94ee7 100755 --- a/.travis/linux.win.download.sh +++ b/.travis/linux.win.download.sh @@ -2,7 +2,7 @@ set -e -CACHE_DIR=$TRAVIS_BUILD_DIR/apt_mingw_cache/$1 +CACHE_DIR=$HOME/apt_mingw_cache/$1 mkdir -p "$CACHE_DIR" pushd "$CACHE_DIR" diff --git a/CMakeLists.txt b/CMakeLists.txt index 77724526852..7a39d7d9fcf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,8 @@ OPTION(WANT_STK "Include Stk (Synthesis Toolkit) support" ON) OPTION(WANT_SWH "Include Steve Harris's LADSPA plugins" ON) OPTION(WANT_TAP "Include Tom's Audio Processing LADSPA plugins" ON) OPTION(WANT_VST "Include VST support" ON) -OPTION(WANT_VST_NOWINE "Include partial VST support (without wine)" OFF) +OPTION(WANT_VST_32 "Include 32-bit VST support" ON) +OPTION(WANT_VST_64 "Include 64-bit VST support" ON) OPTION(WANT_WINMM "Include WinMM MIDI support" OFF) OPTION(WANT_DEBUG_FPE "Debug floating point exceptions" OFF) @@ -75,6 +76,7 @@ OPTION(WANT_DEBUG_FPE "Debug floating point exceptions" OFF) IF(LMMS_BUILD_APPLE) # Fix linking on 10.14+. See issue #4762 on github LINK_DIRECTORIES(/usr/local/lib) + SET(WANT_SOUNDIO OFF) SET(WANT_ALSA OFF) SET(WANT_PULSEAUDIO OFF) SET(WANT_VST OFF) diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index 2cefe73c6ea..ba6c7279a06 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -101,6 +101,7 @@ mv "${APPDIR}usr/bin/lmms" "${APPDIR}usr/bin/lmms.real" cat >"${APPDIR}usr/bin/lmms" < /dev/null 2>&1; then CARLAPATH="\$(which carla)" CARLAPREFIX="\${CARLAPATH%/bin*}" @@ -134,15 +135,15 @@ export LD_LIBRARY_PATH="${APPDIR}usr/lib/lmms/":$LD_LIBRARY_PATH # Handle wine linking if [ -d "@WINE_32_LIBRARY_DIR@" ]; then - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LD_LIBRARY_PATH/wine/:@WINE_32_LIBRARY_DIR@:@WINE_32_LIBRARY_DIR@wine/ + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@WINE_32_LIBRARY_DIRS@ fi if [ -d "@WINE_64_LIBRARY_DIR@" ]; then - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LD_LIBRARY_PATH/wine/:@WINE_64_LIBRARY_DIR@:@WINE_64_LIBRARY_DIR@wine/ + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@WINE_64_LIBRARY_DIRS@ fi # Move executables so linuxdeployqt can find them ZYNLIB="${APPDIR}usr/lib/lmms/RemoteZynAddSubFx" -VSTLIB32="${APPDIR}usr/lib/lmms/RemoteVstPlugin32.exe.so" +VSTLIB32="${APPDIR}usr/lib/lmms/32/RemoteVstPlugin32.exe.so" VSTLIB64="${APPDIR}usr/lib/lmms/RemoteVstPlugin64.exe.so" ZYNBIN="${APPDIR}usr/bin/RemoteZynAddSubFx" @@ -172,8 +173,10 @@ executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/pitch_scale # Bundle both qt and non-qt dependencies into appimage format echo -e "\nBundling and relinking system dependencies..." echo -e ">>>>> linuxdeployqt" > "$LOGFILE" +# FIXME: -unsupported-allow-new-glibc may result in an AppImage which is unusable on old systems. + # shellcheck disable=SC2086 -"$LINUXDEPLOYQT" "$DESKTOPFILE" $executables -bundle-non-qt-libs -verbose=$VERBOSITY $STRIP >> "$LOGFILE" 2>&1 +"$LINUXDEPLOYQT" "$DESKTOPFILE" $executables -unsupported-allow-new-glibc -bundle-non-qt-libs -verbose=$VERBOSITY $STRIP >> "$LOGFILE" 2>&1 success "Bundled and relinked dependencies" # Link to original location so lmms can find them diff --git a/cmake/modules/CheckWineGcc.cmake b/cmake/modules/CheckWineGcc.cmake new file mode 100644 index 00000000000..2956198d894 --- /dev/null +++ b/cmake/modules/CheckWineGcc.cmake @@ -0,0 +1,27 @@ +INCLUDE(CheckCXXSourceCompiles) + +FUNCTION(CheckWineGcc BITNESS WINEGCC_EXECUTABLE RESULT) + FILE(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/winegcc_test.cxx" " + #include + #define USE_WS_PREFIX + #include + int main(int argc, const char* argv[]) { + return 0; + } + ") + EXECUTE_PROCESS(COMMAND ${WINEGCC_EXECUTABLE} "-m${BITNESS}" + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/winegcc_test.cxx" + "-o" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/winegcc_test" + OUTPUT_QUIET ERROR_QUIET + RESULT_VARIABLE WINEGCC_RESULT + ) + FILE(REMOVE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/winegcc_test.cxx" + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/winegcc_test" + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/winegcc_test.exe.so" + ) + IF(WINEGCC_RESULT EQUAL 0) + SET(${RESULT} True PARENT_SCOPE) + ELSE() + SET(${RESULT} False PARENT_SCOPE) + ENDIF() +ENDFUNCTION() diff --git a/cmake/modules/FindWine.cmake b/cmake/modules/FindWine.cmake index 225d6a824ba..50bf54edbc6 100644 --- a/cmake/modules/FindWine.cmake +++ b/cmake/modules/FindWine.cmake @@ -7,46 +7,89 @@ # WINE_DEFINITIONS - Compiler switches required for using wine # -LIST(APPEND CMAKE_PREFIX_PATH /opt/wine-stable /opt/wine-devel /opt/wine-staging /usr/lib/wine/) +MACRO(_findwine_find_flags output expression result) + STRING(REPLACE " " ";" WINEBUILD_FLAGS "${output}") + FOREACH(FLAG ${WINEBUILD_FLAGS}) + IF("${FLAG}" MATCHES "${expression}") + SET(${result} "${FLAG}") + ENDIF() + ENDFOREACH() +ENDMACRO() +LIST(APPEND CMAKE_PREFIX_PATH /opt/wine-stable /opt/wine-devel /opt/wine-staging /usr/lib/wine/) -FIND_PATH(WINE_INCLUDE_DIR wine/exception.h PATH_SUFFIXES wine) FIND_PROGRAM(WINE_CXX NAMES wineg++ winegcc winegcc64 winegcc32 winegcc-stable - PATHS /usr/lib/wine) + PATHS /usr/lib/wine +) FIND_PROGRAM(WINE_BUILD NAMES winebuild) +# Detect wine paths and handle linking problems +IF(WINE_CXX) + EXEC_PROGRAM(${WINE_CXX} ARGS "-m32 -v /dev/zero" OUTPUT_VARIABLE WINEBUILD_OUTPUT_32) + EXEC_PROGRAM(${WINE_CXX} ARGS "-m64 -v /dev/zero" OUTPUT_VARIABLE WINEBUILD_OUTPUT_64) + _findwine_find_flags("${WINEBUILD_OUTPUT_32}" "^-isystem/usr/include$" BUGGED_WINEGCC) + _findwine_find_flags("${WINEBUILD_OUTPUT_32}" "^-isystem" WINEGCC_INCLUDE_DIR) + _findwine_find_flags("${WINEBUILD_OUTPUT_32}" "libwinecrt0\\.a.*" WINECRT_32) + _findwine_find_flags("${WINEBUILD_OUTPUT_64}" "libwinecrt0\\.a.*" WINECRT_64) + STRING(REGEX REPLACE "^-isystem" "" WINE_INCLUDE_HINT "${WINEGCC_INCLUDE_DIR}") + STRING(REGEX REPLACE "/wine/windows$" "" WINE_INCLUDE_HINT "${WINE_INCLUDE_HINT}") + STRING(REGEX REPLACE "libwinecrt0\\.a.*" "" WINE_32_LIBRARY_DIR "${WINECRT_32}") + STRING(REGEX REPLACE "libwinecrt0\\.a.*" "" WINE_64_LIBRARY_DIR "${WINECRT_64}") + + IF(BUGGED_WINEGCC) + MESSAGE(WARNING "Your winegcc is unusable due to https://bugs.winehq.org/show_bug.cgi?id=46293,\n + Consider either upgrading or downgrading wine.") + RETURN() + ENDIF() + IF(WINE_32_LIBRARY_DIR STREQUAL WINE_64_LIBRARY_DIR) + MESSAGE(STATUS "Old winegcc detected, trying to use workaround linking") + # Fix library search directory according to the target bitness + IF(WINE_32_LIBRARY_DIR MATCHES "/lib/(x86_64|i386)-") + # Debian systems + STRING(REPLACE "/lib/x86_64-" "/lib/i386-" WINE_32_LIBRARY_DIR "${WINE_32_LIBRARY_DIR}") + STRING(REPLACE "/lib/i386-" "/lib/x86_64-" WINE_64_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + ELSEIF(WINE_32_LIBRARY_DIR MATCHES "/(lib|lib64)/wine/$") + # WineHQ (/opt/wine-stable, /opt/wine-devel, /opt/wine-staging) + STRING(REGEX REPLACE "/lib64/wine/$" "/lib/wine/" WINE_32_LIBRARY_DIR "${WINE_32_LIBRARY_DIR}") + STRING(REGEX REPLACE "/lib/wine/$" "/lib64/wine/" WINE_64_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + ELSEIF(WINE_32_LIBRARY_DIR MATCHES "/lib32/.*/wine/") + # Systems with old multilib layout + STRING(REPLACE "/lib32/" "/lib/" WINE_64_LIBRARY_DIR "${WINE_32_LIBRARY_DIR}") + ELSEIF(WINE_32_LIBRARY_DIR MATCHES "/lib64/.*/wine/") + # We need to test if the corresponding 64bit library directory is lib or lib32 + STRING(REPLACE "/lib64/" "/lib32/" WINE_32_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + IF(NOT EXISTS "${WINE_32_LIBRARY_DIR}") + STRING(REPLACE "/lib64/" "/lib/" WINE_32_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + ENDIF() + ELSEIF(WINE_32_LIBRARY_DIR MATCHES "/lib/.*/wine/") + # Test if this directory is for 32bit or 64bit + STRING(REPLACE "/lib/" "/lib32/" WINE_32_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + IF(NOT EXISTS "${WINE_32_LIBRARY_DIR}") + SET(WINE_32_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + STRING(REPLACE "/lib/" "/lib64/" WINE_64_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + ENDIF() + ELSE() + MESSAGE(WARNING "Can't detect wine installation layout. You may get some build errors.") + ENDIF() + SET(WINE_LIBRARY_FIX "${WINE_32_LIBRARY_DIR} and ${WINE_64_LIBRARY_DIR}") + ENDIF() +ENDIF() + +FIND_PATH(WINE_INCLUDE_DIR wine/exception.h + HINTS "${WINE_INCLUDE_HINT}" +) SET(_ARCHITECTURE ${CMAKE_LIBRARY_ARCHITECTURE}) -FIND_LIBRARY(WINE_LIBRARY NAMES wine PATH_SUFFIXES wine i386-linux-gnu/wine) +FIND_LIBRARY(WINE_LIBRARY NAMES wine + PATH_SUFFIXES wine i386-linux-gnu/wine + HINTS "${WINE_32_LIBRARY_DIR}" "${WINE_64_LIBRARY_DIR}" +) SET(CMAKE_LIBRARY_ARCHITECTURE ${_ARCHITECTURE}) SET(WINE_INCLUDE_DIRS ${WINE_INCLUDE_DIR} ) -SET(WINE_LIBRARIES ${WINE_LIBRARY} ) - -# Handle wine linking problems -EXEC_PROGRAM(${WINE_CXX} ARGS "-v -m32 /dev/zero" OUTPUT_VARIABLE WINEBUILD_OUTPUT) -STRING(REPLACE " " ";" WINEBUILD_FLAGS "${WINEBUILD_OUTPUT}") - -FOREACH(FLAG ${WINEBUILD_FLAGS}) - IF("${FLAG}" MATCHES "libwinecrt0.a.*") - STRING(REGEX REPLACE "/wine/libwinecrt0.a.*" "" FLAG "${FLAG}") - - SET(WINE_64_LIBRARY_DIR "${FLAG}/") - - # Debian systems - STRING(REPLACE "/lib/x86_64-" "/lib/i386-" FLAG "${FLAG}") - # Fedora systems - STRING(REPLACE "/lib/lib64" "/lib/i386" FLAG "${FLAG}") - # Gentoo systems - STRING(REPLACE "/lib/wine-" "/lib32/wine-" FLAG "${FLAG}") - # WineHQ (/opt/wine-stable, /opt/wine-devel, /opt/wine-staging) - STRING(REGEX REPLACE "/lib64$" "/lib" FLAG "${FLAG}") - - SET(WINE_32_LIBRARY_DIR "${FLAG}/") - ENDIF() -ENDFOREACH() +SET(WINE_LIBRARIES ${WINE_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Wine DEFAULT_MSG WINE_CXX WINE_LIBRARIES WINE_INCLUDE_DIRS) @@ -54,7 +97,23 @@ find_package_handle_standard_args(Wine DEFAULT_MSG WINE_CXX WINE_LIBRARIES WINE_ mark_as_advanced(WINE_INCLUDE_DIR WINE_LIBRARY WINE_CXX WINE_BUILD) IF(WINE_32_LIBRARY_DIR) - SET(WINE_32_FLAGS "-L${WINE_32_LIBRARY_DIR}wine/ -L${WINE_32_LIBRARY_DIR}") + IF(WINE_32_LIBRARY_DIR MATCHES "wine*/lib") + SET(WINE_32_FLAGS "-L${WINE_32_LIBRARY_DIR} -L${WINE_32_LIBRARY_DIR}../") + SET(WINE_32_LIBRARY_DIRS "${WINE_32_LIBRARY_DIR}:${WINE_32_LIBRARY_DIR}/..") + ELSE() + SET(WINE_32_FLAGS "-L${WINE_32_LIBRARY_DIR}") + SET(WINE_32_LIBRARY_DIRS "${WINE_32_LIBRARY_DIR}") + ENDIF() +ENDIF() + +IF(WINE_64_LIBRARY_DIR) + IF(WINE_64_LIBRARY_DIR MATCHES "wine*/lib") + SET(WINE_64_FLAGS "-L${WINE_64_LIBRARY_DIR} -L${WINE_64_LIBRARY_DIR}../") + SET(WINE_64_LIBRARY_DIRS "${WINE_64_LIBRARY_DIR}:${WINE_64_LIBRARY_DIR}/..") + ELSE() + SET(WINE_64_FLAGS "-L${WINE_64_LIBRARY_DIR}") + SET(WINE_64_LIBRARY_DIRS "${WINE_64_LIBRARY_DIR}") + ENDIF() ENDIF() # Create winegcc wrapper diff --git a/cmake/modules/winegcc_wrapper.in b/cmake/modules/winegcc_wrapper.in index d7d680be239..d32aec66432 100755 --- a/cmake/modules/winegcc_wrapper.in +++ b/cmake/modules/winegcc_wrapper.in @@ -22,6 +22,9 @@ while [ $# -gt 0 ]; do -m32) win32=true ;; + -m64) + win64=true + ;; *) ;; @@ -42,11 +45,19 @@ fi # by FindWine.cmake extra_args="-I@WINE_INCLUDE_DIR@ -I@WINE_INCLUDE_DIR@/wine/windows" +# Apply manually specified flags +extra_args="$extra_args @WINE_CXX_FLAGS@" + # Apply -m32 library fix if necessary if [ "$win32" = true ] && [ "$no_link" != true ]; then extra_args="$extra_args @WINE_32_FLAGS@" fi +# Apply -m64 library fix if necessary +if [ "$win64" = true ] && [ "$no_link" != true ]; then + extra_args="$extra_args @WINE_64_FLAGS@" +fi + # Run winegcc export WINEBUILD=@WINE_BUILD@ @WINE_CXX@ $extra_args $args diff --git a/debian/calf-ladspa.install b/debian/calf-ladspa.install deleted file mode 100644 index c25e49dbcb7..00000000000 --- a/debian/calf-ladspa.install +++ /dev/null @@ -1 +0,0 @@ -usr/lib/*/lmms/ladspa/calf.so usr/lib/ladspa diff --git a/debian/changelog b/debian/changelog index c44b1790c4d..86f03c427d0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -lmms (1.2.0~rc7.1) unstable; urgency=low +lmms (@VERSION@) unstable; urgency=low * Upstream integration. * Drop Debian menu entry (policy 9.6). diff --git a/debian/control b/debian/control index 0997676c953..463353df0ef 100644 --- a/debian/control +++ b/debian/control @@ -17,6 +17,7 @@ Build-Depends: libfluidsynth-dev, libgig-dev, libjack-jackd2-dev, + liblist-moreutils-perl, libmp3lame-dev, libpulse-dev, libqt5x11extras5-dev, @@ -29,10 +30,12 @@ Build-Depends: libvorbis-dev, libxcb-keysyms1-dev, libxcb-util0-dev, + libxml-perl, + libxml2-utils, portaudio19-dev, qtbase5-private-dev, qttools5-dev, - wine32-tools [i386] + wine64-tools [amd64] | wine32-tools [i386] Standards-Version: 4.2.1.4 Homepage: http://lmms.io/ Vcs-Browser: https://salsa.debian.org/debian-edu-pkg-team/lmms.git @@ -41,8 +44,9 @@ Package: lmms-bin Architecture: any Depends: lmms-common (>= ${source:Version}), ${shlibs:Depends}, ${misc:Depends}, stk -Recommends: calf-ladspa, tap-plugins, caps, - lmms-vst-server:i386 (>= ${source:Version}) +Recommends: tap-plugins, caps, + lmms-vst-server:i386 (>= ${source:Version}), + lmms-vst-server:amd64 (>= ${source:Version}) Suggests: fil-plugins, mcp-plugins, omins, freepats, fluid-soundfont-gm, ladspa-plugin Replaces: lmms-common (<< 1.0.0-1) @@ -63,7 +67,7 @@ Description: Linux Multimedia Studio - minimal installation Package: lmms Architecture: any -Depends: calf-ladspa, lmms-bin, ${misc:Depends} +Depends: lmms-bin, ${misc:Depends} Description: Linux Multimedia Studio LMMS aims to be a free alternative to popular (but commercial and closed- source) programs like FruityLoops, Cubase and Logic giving you the ability of @@ -96,23 +100,8 @@ Description: Linux Multimedia Studio - common files and some example projects. Package: lmms-vst-server -Architecture: i386 -# Order matters to avoid wine64 -Depends: wine32, wine, ${shlibs:Depends}, ${misc:Depends} +Architecture: amd64 i386 +Depends: wine64 [amd64] | wine64-development [amd64] | wine32 [i386] | wine32-development [i386], ${shlibs:Depends}, ${misc:Depends} Recommends: lmms-bin:any Description: Linux Multimedia Studio - VST server This package contains a helper application that loads VST plugins. - -Package: calf-ladspa -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} -Replaces: calf-plugins (<< 0.0.19) -Provides: ladspa-plugin -Description: Linux Multimedia Studio - Calf LADSPA plugins - Calf is a pack of audio plugins - effects and instruments. The goal is to - create a set of plugins using decent algorithms and parameter settings, - available in a form which is compatible with as many open source applications - as possible. - . - These plugins are distributed as part of Linux Multimedia Studio, but may be - used by other applications. diff --git a/debian/lmms-bin.install b/debian/lmms-bin.install index 5d19a3103ac..229fa02e315 100644 --- a/debian/lmms-bin.install +++ b/debian/lmms-bin.install @@ -1,7 +1,4 @@ usr/bin/lmms -usr/lib/*/lmms/ladspa/[a-b]* -usr/lib/*/lmms/ladspa/caps.so -usr/lib/*/lmms/ladspa/c[b-z]* -usr/lib/*/lmms/ladspa/[d-z]* +usr/lib/*/lmms/ladspa/* usr/lib/*/lmms/lib* usr/lib/*/lmms/RemoteZynAddSubFx diff --git a/debian/lmms-vst-server.install b/debian/lmms-vst-server.install index 1b520479d01..60efaed7b08 100644 --- a/debian/lmms-vst-server.install +++ b/debian/lmms-vst-server.install @@ -1 +1 @@ -usr/lib/*/lmms/RemoteVstPlugin* +usr/lib/*/lmms/{32/,}RemoteVstPlugin* diff --git a/debian/rules b/debian/rules index 5e8345845f9..aed094c2253 100755 --- a/debian/rules +++ b/debian/rules @@ -6,6 +6,7 @@ DH_CMAKE_BUILD_DIR=obj -${DEB_BUILD_GNU_TYPE} DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH) DEB_HOST_ARCH_OS ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_OS) +DEB_HOST_ARCH_BIT ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_BITS) CMAKE_OPTS= -DCONTRIBUTORS=$(CURDIR)/doc/CONTRIBUTORS -DFORCE_VERSION=internal \ -DWANT_QT5=1 @@ -13,13 +14,18 @@ ifneq ($(DEB_HOST_ARCH_OS),linux) CMAKE_OPTS+= -DWANT_ALSA=0 endif -ifeq ($(DEB_HOST_ARCH),i386) +ifeq ($(DEB_HOST_ARCH),amd64) export PATH := $(PATH):/usr/lib/wine WINE_PATH := /usr/lib/$(DEB_HOST_MULTIARCH)/wine -CMAKE_OPTS+= -DWINE_CXX_FLAGS=-Wl,--enable-new-dtags,-rpath=$(WINE_PATH) +CMAKE_OPTS+= -DWANT_VST_32=OFF -DREMOTE_VST_PLUGIN_FILEPATH_32=../../i386-linux-gnu/lmms/32/RemoteVstPlugin32 \ + -DWINE_CXX_FLAGS=-Wl,--enable-new-dtags,-rpath=$(WINE_PATH) +else ifeq ($(DEB_HOST_ARCH),i386) +export PATH := $(PATH):/usr/lib/wine +WINE_PATH := /usr/lib/$(DEB_HOST_MULTIARCH)/wine +CMAKE_OPTS+= -DWANT_VST_64=OFF -DREMOTE_VST_PLUGIN_FILEPATH_64=../../x86_64-linux-gnu/lmms/RemoteVstPlugin64 \ + -DWINE_CXX_FLAGS=-Wl,--enable-new-dtags,-rpath=$(WINE_PATH) else -CMAKE_OPTS+= -DWANT_VST_NOWINE=1 \ - -DREMOTE_VST_PLUGIN_FILEPATH=../../i386-linux-gnu/lmms/RemoteVstPlugin +CMAKE_OPTS+= -DWANT_VST=OFF endif # Define NDEBUG. This helps with reproducible builds. diff --git a/include/Editor.h b/include/Editor.h index ca4f7415e0b..26b70ec8791 100644 --- a/include/Editor.h +++ b/include/Editor.h @@ -47,6 +47,7 @@ class Editor : public QMainWindow DropToolBar * addDropToolBar(Qt::ToolBarArea whereToAdd, QString const & windowTitle); DropToolBar * addDropToolBar(QWidget * parent, Qt::ToolBarArea whereToAdd, QString const & windowTitle); + virtual void closeEvent( QCloseEvent * _ce ); protected slots: virtual void play() {} virtual void record() {} diff --git a/include/Effect.h b/include/Effect.h index d6aa04d9710..3d765fdc03c 100644 --- a/include/Effect.h +++ b/include/Effect.h @@ -148,11 +148,6 @@ class LMMS_EXPORT Effect : public Plugin m_noRun = _state; } - inline const Descriptor::SubPluginFeatures::Key & key() const - { - return m_key; - } - EffectChain * effectChain() const { return m_parent; @@ -201,8 +196,6 @@ class LMMS_EXPORT Effect : public Plugin sampleFrame * _dst_buf, sample_rate_t _dst_sr, const f_cnt_t _frames ); - Descriptor::SubPluginFeatures::Key m_key; - ch_cnt_t m_processors; bool m_okay; diff --git a/include/Engine.h b/include/Engine.h index fc25479969b..18960ec8f4c 100644 --- a/include/Engine.h +++ b/include/Engine.h @@ -111,6 +111,9 @@ class LMMS_EXPORT LmmsCore : public QObject return s_instanceOfMe; } + static void setDndPluginKey(void* newKey); + static void* pickDndPluginKey(); + signals: void initProgress(const QString &msg); @@ -137,6 +140,7 @@ class LMMS_EXPORT LmmsCore : public QObject static DummyTrackContainer * s_dummyTC; static Ladspa2LMMS * s_ladspaManager; + static void* s_dndPluginKey; // even though most methods are static, an instance is needed for Qt slots/signals static LmmsCore * s_instanceOfMe; diff --git a/include/FxLineLcdSpinBox.h b/include/FxLineLcdSpinBox.h new file mode 100644 index 00000000000..fa001b2bbe8 --- /dev/null +++ b/include/FxLineLcdSpinBox.h @@ -0,0 +1,53 @@ +/* + * FxLineLcdSpinBox.h - a specialization of LcdSpnBox for setting FX channels + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 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. + * + */ + +#ifndef FX_LINE_LCD_SPIN_BOX_H +#define FX_LINE_LCD_SPIN_BOX_H + +#include "LcdSpinBox.h" + +class TrackView; + + +class FxLineLcdSpinBox : public LcdSpinBox +{ + Q_OBJECT +public: + FxLineLcdSpinBox(int numDigits, QWidget * parent, const QString& name, TrackView * tv = NULL) : + LcdSpinBox(numDigits, parent, name), m_tv(tv) + {} + virtual ~FxLineLcdSpinBox() {} + + void setTrackView(TrackView * tv); + +protected: + virtual void mouseDoubleClickEvent(QMouseEvent* event); + virtual void contextMenuEvent(QContextMenuEvent* event); + +private: + TrackView * m_tv; + +}; + +#endif diff --git a/include/Instrument.h b/include/Instrument.h index a373ae4ac47..2179a1f721f 100644 --- a/include/Instrument.h +++ b/include/Instrument.h @@ -55,14 +55,17 @@ class LMMS_EXPORT Instrument : public Plugin Q_DECLARE_FLAGS(Flags, Flag); - Instrument( InstrumentTrack * _instrument_track, - const Descriptor * _descriptor ); + Instrument(InstrumentTrack * _instrument_track, + const Descriptor * _descriptor, + const Descriptor::SubPluginFeatures::Key * key = nullptr); virtual ~Instrument() = default; // -------------------------------------------------------------------- // functions that can/should be re-implemented: // -------------------------------------------------------------------- + virtual bool hasNoteInput() const { return true; } + // if the plugin doesn't play each note, it can create an instrument- // play-handle and re-implement this method, so that it mixes its // output buffer only once per mixer-period @@ -113,10 +116,12 @@ class LMMS_EXPORT Instrument : public Plugin // provided functions: // -------------------------------------------------------------------- - // instantiate instrument-plugin with given name or return NULL - // on failure - static Instrument * instantiate( const QString & _plugin_name, - InstrumentTrack * _instrument_track ); + //! instantiate instrument-plugin with given name or return NULL + //! on failure + static Instrument * instantiate(const QString & _plugin_name, + InstrumentTrack * _instrument_track, + const Plugin::Descriptor::SubPluginFeatures::Key* key, + bool keyFromDnd = false); virtual bool isFromTrack( const Track * _track ) const; diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index fb12e825a41..6a2cacb3ae2 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -36,6 +36,7 @@ #include "Piano.h" #include "PianoView.h" #include "Pitch.h" +#include "Plugin.h" #include "Track.h" @@ -52,6 +53,7 @@ class InstrumentTrackWindow; class InstrumentMidiIOView; class InstrumentMiscView; class Knob; +class FxLineLcdSpinBox; class LcdSpinBox; class LeftRightNav; class midiPortMenu; @@ -146,7 +148,9 @@ class LMMS_EXPORT InstrumentTrack : public Track, public MidiEventProcessor // load instrument whose name matches given one - Instrument * loadInstrument( const QString & _instrument_name ); + Instrument * loadInstrument(const QString & _instrument_name, + const Plugin::Descriptor::SubPluginFeatures::Key* key = nullptr, + bool keyFromDnd = false); AudioPort * audioPort() { @@ -440,7 +444,7 @@ protected slots: QLabel * m_pitchLabel; LcdSpinBox* m_pitchRangeSpinBox; QLabel * m_pitchRangeLabel; - LcdSpinBox * m_effectChannelNumber; + FxLineLcdSpinBox * m_effectChannelNumber; diff --git a/include/Knob.h b/include/Knob.h index 319b38184f9..245963ce2b3 100644 --- a/include/Knob.h +++ b/include/Knob.h @@ -74,6 +74,7 @@ class LMMS_EXPORT Knob : public QWidget, public FloatModelView public: Knob( knobTypes _knob_num, QWidget * _parent = NULL, const QString & _name = QString() ); Knob( QWidget * _parent = NULL, const QString & _name = QString() ); //!< default ctor + Knob( const Knob& other ) = delete; virtual ~Knob(); // TODO: remove diff --git a/include/Ladspa2LMMS.h b/include/Ladspa2LMMS.h index 14899487cf9..28fa25b894c 100644 --- a/include/Ladspa2LMMS.h +++ b/include/Ladspa2LMMS.h @@ -30,6 +30,8 @@ #include "LadspaManager.h" +//! Class responsible for sorting found plugins (by LadspaManager) +//! into categories class LMMS_EXPORT Ladspa2LMMS : public LadspaManager { public: diff --git a/include/Model.h b/include/Model.h index bc9f5c04646..b40c21029de 100644 --- a/include/Model.h +++ b/include/Model.h @@ -41,10 +41,6 @@ class LMMS_EXPORT Model : public QObject m_displayName( _display_name ), m_defaultConstructed( _default_constructed ) { -#if QT_VERSION < 0x050000 - connect( this, SIGNAL( dataChanged() ), this, - SLOT( thisDataChanged() ), Qt::DirectConnection ); -#endif } virtual ~Model() @@ -89,19 +85,6 @@ class LMMS_EXPORT Model : public QObject // emitted if properties of the model (e.g. ranges) have changed void propertiesChanged(); -#if QT_VERSION < 0x050000 - // emitted along with dataChanged(), but with this model as an argument - // workaround for when QObject::sender() and Qt5 are unavailable - void dataChanged( Model * ); - -private slots: - void thisDataChanged() - { - emit dataChanged( this ); - } - -signals: -#endif } ; diff --git a/include/Plugin.h b/include/Plugin.h index a2cc7d696ab..af42b0f1028 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -40,7 +40,27 @@ class PixmapLoader; class PluginView; class AutomatableModel; +/** + Abstract representation of a plugin + Such a plugin can be an Instrument, Effect, Tool plugin etc. + + Plugins have descriptors, containing meta info, which is used especially + by PluginFactory and friends. + + There are also Plugin keys (class Key, confusingly under + SubPluginFeatures), which contain pointers to the plugin descriptor. + + Some plugins have sub plugins, e.g. there is one CALF Plugin and for + each CALF effect, there is a CALF sub plugin. For those plugins, there + are keys for each sub plugin. These keys also link to the superior + Plugin::Descriptor. Additionally, they contain attributes that help the + superior Plugin saving them and recognizing them when loading. + + In case of sub plugins, the Descriptor has SubPluginFeatures. Those + are a bit like values to the sub plugins' keys (in terms of a key-value- + map). +*/ class LMMS_EXPORT Plugin : public Model, public JournallingObject { MM_OPERATORS @@ -59,9 +79,9 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject Undefined = 255 } ; - // descriptor holds information about a plugin - every external plugin - // has to instantiate such a descriptor in an extern "C"-section so that - // the plugin-loader is able to access information about the plugin + //! Descriptor holds information about a plugin - every external plugin + //! has to instantiate such a Descriptor in an extern "C"-section so that + //! the plugin-loader is able to access information about the plugin struct Descriptor { const char * name; @@ -71,23 +91,49 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject int version; PluginTypes type; const PixmapLoader * logo; - const char * supportedFileTypes; + const char * supportedFileTypes; //!< csv list of extensions inline bool supportsFileType( const QString& extension ) const { return QString( supportedFileTypes ).split( QChar( ',' ) ).contains( extension ); } + /** + Access to non-key-data of a sub plugin + + If you consider sub plugin keys as keys in a + key-value-map, this is the lookup for the corresponding + values. In order to have flexibility between different + plugin APIs, this is rather an array of fixed data, + but a bunch of virtual functions taking the key and + returning some values (or modifying objects of other + classes). + */ class LMMS_EXPORT SubPluginFeatures { public: + /** + Key reference a Plugin::Descriptor, and, + if the plugin has sub plugins, also reference + its sub plugin (using the attributes). + When keys are saved, those attributes are + written to XML in order to find the right sub + plugin when realoading. + + @note Any data that is not required to reference + the right Plugin or sub plugin should + not be here (but rather in + SubPluginFeatures, which are like values + in a key-value map). + */ struct Key { typedef QMap AttributeMap; inline Key( const Plugin::Descriptor * desc = NULL, - const QString & name = QString(), - const AttributeMap & am = AttributeMap() ) + const QString & name = QString(), + const AttributeMap & am = AttributeMap() + ) : desc( desc ), name( name ), @@ -101,12 +147,28 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject inline bool isValid() const { - return desc != NULL && name.isNull() == false; + return desc != nullptr; } + //! Key to subplugin: reference to parent descriptor + //! Key to plugin: reference to its descriptor const Plugin::Descriptor* desc; + //! Descriptive name like "Calf Phaser". + //! Not required for key lookup and not saved + //! only used sometimes to temporary store descriptive names + //! @todo This is a bug, there should be a function + //! in SubPluginFeatures (to get the name) instead QString name; + //! Attributes that make up the key and identify + //! the sub plugin. They are being loaded and saved AttributeMap attributes; + + // helper functions to retrieve data that is + // not part of the key, but mapped via desc->subPluginFeatures + QString additionalFileExtensions() const; + QString displayName() const; + QString description() const; + const PixmapLoader* logo() const; } ; typedef QList KeyList; @@ -125,11 +187,40 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject { } + //! While PluginFactory only collects the plugins, + //! this function is used by widgets like EffectSelectDialog + //! to find all possible sub plugins virtual void listSubPluginKeys( const Plugin::Descriptor *, KeyList & ) const { } + private: + // You can add values mapped by "Key" below + // The defaults are sane, i.e. redirect to sub plugin's + // supererior descriptor + + virtual QString additionalFileExtensions(const Key&) const + { + return QString(); + } + + virtual QString displayName(const Key& k) const + { + return k.isValid() ? k.name : QString(); + } + + virtual QString description(const Key& k) const + { + return k.isValid() ? k.desc->description : QString(); + } + + virtual const PixmapLoader* logo(const Key& k) const + { + Q_ASSERT(k.desc); + return k.desc->logo; + } + protected: const Plugin::PluginTypes m_type; } ; @@ -140,48 +231,66 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject // typedef a list so we can easily work with list of plugin descriptors typedef QList DescriptorList; - // contructor of a plugin - Plugin( const Descriptor * descriptor, Model * parent ); + //! Constructor of a plugin + //! @param key Sub plugins must pass a key here, optional otherwise. + //! See the key() function + Plugin(const Descriptor * descriptor, Model * parent, + const Descriptor::SubPluginFeatures::Key *key = nullptr); virtual ~Plugin(); - // returns display-name out of descriptor - virtual QString displayName() const - { - return Model::displayName().isEmpty() - ? m_descriptor->displayName - : Model::displayName(); - } + //! Return display-name out of sub plugin or descriptor + virtual QString displayName() const; + + //! Return logo out of sub plugin or descriptor + const PixmapLoader *logo() const; - // return plugin-type + //! Return plugin type inline PluginTypes type( void ) const { return m_descriptor->type; } - // return plugin-descriptor for further information + //! Return plugin Descriptor inline const Descriptor * descriptor() const { return m_descriptor; } - // can be called if a file matching supportedFileTypes should be - // loaded/processed with the help of this plugin + //! Return the key referencing this plugin. If the Plugin has no + //! sub plugin features, the key is pretty useless. If it has, + //! this key will also contain the sub plugin attributes, and will be + //! a key to those SubPluginFeatures. + inline const Descriptor::SubPluginFeatures::Key & key() const + { + return m_key; + } + + //! Can be called if a file matching supportedFileTypes should be + //! loaded/processed with the help of this plugin virtual void loadFile( const QString & file ); - // Called if external source needs to change something but we cannot - // reference the class header. Should return null if not key not found. + //! Called if external source needs to change something but we cannot + //! reference the class header. Should return null if not key not found. virtual AutomatableModel* childModel( const QString & modelName ); - // returns an instance of a plugin whose name matches to given one - // if specified plugin couldn't be loaded, it creates a dummy-plugin - static Plugin * instantiate( const QString& pluginName, Model * parent, void * data ); + //! Overload if the argument passed to the plugin is a subPluginKey + //! If you can not pass the key and are aware that it's stored in + //! Engine::pickDndPluginKey(), use this function, too + static Plugin * instantiateWithKey(const QString& pluginName, Model * parent, + const Descriptor::SubPluginFeatures::Key *key, + bool keyFromDnd = false); - // create a view for the model - PluginView * createView( QWidget * parent ); + //! Return an instance of a plugin whose name matches to given one + //! if specified plugin couldn't be loaded, it creates a dummy-plugin + //! @param data Anything the plugin expects. If this is a pointer to a sub plugin key, + //! use instantiateWithKey instead + static Plugin * instantiate(const QString& pluginName, Model * parent, void *data); + //! Create a view for the model + PluginView * createView( QWidget * parent ); protected: - // create a view for the model + //! Create a view for the model virtual PluginView* instantiateView( QWidget * ) = 0; void collectErrorForUI( QString errMsg ); @@ -189,6 +298,8 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject private: const Descriptor * m_descriptor; + Descriptor::SubPluginFeatures::Key m_key; + // pointer to instantiation-function in plugin typedef Plugin * ( * InstantiationHook )( Model * , void * ); diff --git a/include/PluginBrowser.h b/include/PluginBrowser.h index 75c7cd2919b..3cc54c6e47a 100644 --- a/include/PluginBrowser.h +++ b/include/PluginBrowser.h @@ -31,6 +31,10 @@ #include "SideBarWidget.h" #include "Plugin.h" +class QLineEdit; +class QTreeWidget; +class QTreeWidgetItem; + class PluginBrowser : public SideBarWidget { @@ -39,18 +43,18 @@ class PluginBrowser : public SideBarWidget PluginBrowser( QWidget * _parent ); virtual ~PluginBrowser() = default; -private: - QWidget * m_view; -}; - - +private slots: + void onFilterChanged( const QString & filter ); +private: + void addPlugins(); + void updateRootVisibility( int index ); + void updateRootVisibilities(); -class PluginDescList : public QWidget -{ - Q_OBJECT -public: - PluginDescList(QWidget* parent); + QWidget * m_view; + QTreeWidget * m_descTree; + QTreeWidgetItem * m_lmmsRoot; + QTreeWidgetItem * m_lv2Root; }; @@ -60,7 +64,9 @@ class PluginDescWidget : public QWidget { Q_OBJECT public: - PluginDescWidget( const Plugin::Descriptor & _pd, QWidget * _parent ); + typedef Plugin::Descriptor::SubPluginFeatures::Key PluginKey; + PluginDescWidget( const PluginKey & _pk, QWidget * _parent ); + QString name() const; protected: @@ -72,7 +78,7 @@ class PluginDescWidget : public QWidget private: constexpr static int DEFAULT_HEIGHT{24}; - const Plugin::Descriptor & m_pluginDescriptor; + PluginKey m_pluginKey; QPixmap m_logo; bool m_mouseOver; diff --git a/include/PluginFactory.h b/include/PluginFactory.h index 56d32c4e421..17b178108f0 100644 --- a/include/PluginFactory.h +++ b/include/PluginFactory.h @@ -26,10 +26,13 @@ #define PLUGINFACTORY_H #include +#include #include #include #include +#include +#include #include "lmms_export.h" #include "Plugin.h" @@ -41,12 +44,10 @@ class LMMS_EXPORT PluginFactory public: struct PluginInfo { - PluginInfo() : library(nullptr), descriptor(nullptr) {} - const QString name() const; QFileInfo file; - std::shared_ptr library; - Plugin::Descriptor* descriptor; + std::shared_ptr library = nullptr; + Plugin::Descriptor* descriptor = nullptr; bool isNull() const {return ! library;} }; @@ -56,6 +57,8 @@ class LMMS_EXPORT PluginFactory PluginFactory(); ~PluginFactory(); + static void setupSearchPaths(); + /// Returns the singleton instance of PluginFactory. You won't need to call /// this directly, use pluginFactory instead. static PluginFactory* instance(); @@ -64,10 +67,17 @@ class LMMS_EXPORT PluginFactory const Plugin::DescriptorList descriptors() const; const Plugin::DescriptorList descriptors(Plugin::PluginTypes type) const; + struct PluginInfoAndKey + { + PluginInfo info; + Plugin::Descriptor::SubPluginFeatures::Key key; + bool isNull() const { return info.isNull(); } + }; + /// Returns a list of all found plugins' PluginFactory::PluginInfo objects. const PluginInfoList& pluginInfos() const; /// Returns a plugin that support the given file extension - const PluginInfo pluginSupportingExtension(const QString& ext); + const PluginInfoAndKey pluginSupportingExtension(const QString& ext); /// Returns the PluginInfo object of the plugin with the given name. /// If the plugin is not found, an empty PluginInfo is returned (use @@ -84,7 +94,9 @@ public slots: private: DescriptorMap m_descriptors; PluginInfoList m_pluginInfos; - QMap m_pluginByExt; + + QMap m_pluginByExt; + QVector m_garbage; //!< cleaned up at destruction QHash m_errors; diff --git a/include/SampleTrack.h b/include/SampleTrack.h index decf52f3f15..ccb5a020e85 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -26,13 +26,19 @@ #define SAMPLE_TRACK_H #include +#include #include "AudioPort.h" +#include "FxMixer.h" +#include "FxLineLcdSpinBox.h" #include "Track.h" class EffectRackView; class Knob; class SampleBuffer; +class SampleTrackWindow; +class TrackLabelButton; +class QLineEdit; class SampleTCO : public TrackContentObject @@ -140,6 +146,11 @@ class SampleTrack : public Track QDomElement & _parent ); virtual void loadTrackSpecificSettings( const QDomElement & _this ); + inline IntModel * effectChannelModel() + { + return &m_effectChannelModel; + } + inline AudioPort * audioPort() { return &m_audioPort; @@ -153,15 +164,18 @@ class SampleTrack : public Track public slots: void updateTcos(); void setPlayingTcos( bool isPlaying ); + void updateEffectChannel(); private: FloatModel m_volumeModel; FloatModel m_panningModel; + IntModel m_effectChannelModel; AudioPort m_audioPort; friend class SampleTrackView; + friend class SampleTrackWindow; } ; @@ -174,6 +188,24 @@ class SampleTrackView : public TrackView SampleTrackView( SampleTrack* Track, TrackContainerView* tcv ); virtual ~SampleTrackView(); + SampleTrackWindow * getSampleTrackWindow() + { + return m_window; + } + + SampleTrack * model() + { + return castModel(); + } + + const SampleTrack * model() const + { + return castModel(); + } + + + virtual QMenu * createFxMenu( QString title, QString newFxLabel ); + public slots: void showEffects(); @@ -187,12 +219,77 @@ public slots: } +private slots: + void assignFxLine( int channelIndex ); + void createFxLine(); + + private: - EffectRackView * m_effectRack; - QWidget * m_effWindow; + SampleTrackWindow * m_window; Knob * m_volumeKnob; Knob * m_panningKnob; + TrackLabelButton * m_tlb; + + + friend class SampleTrackWindow; + +} ; + + + +class SampleTrackWindow : public QWidget, public ModelView, public SerializingObjectHook +{ + Q_OBJECT +public: + SampleTrackWindow(SampleTrackView * tv); + virtual ~SampleTrackWindow(); + + SampleTrack * model() + { + return castModel(); + } + + const SampleTrack * model() const + { + return castModel(); + } + + void setSampleTrackView(SampleTrackView * tv); + + SampleTrackView *sampleTrackView() + { + return m_stv; + } + + +public slots: + void textChanged(const QString & new_name); + void toggleVisibility(bool on); + void updateName(); + + +protected: + // capture close-events for toggling sample-track-button + virtual void closeEvent(QCloseEvent * ce); + + virtual void saveSettings(QDomDocument & doc, QDomElement & element); + virtual void loadSettings(const QDomElement & element); + +private: + virtual void modelChanged(); + + SampleTrack * m_track; + SampleTrackView * m_stv; + + // widgets on the top of an sample-track-window + QLineEdit * m_nameLineEdit; + Knob * m_volumeKnob; + Knob * m_panningKnob; + FxLineLcdSpinBox * m_effectChannelNumber; + + EffectRackView * m_effectRack; + } ; diff --git a/include/SetupDialog.h b/include/SetupDialog.h index d9665bbbc94..272ba7b093f 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -122,6 +122,9 @@ private slots: void toggleDisplayWaveform( bool en ); void toggleDisableAutoquit( bool en ); + void vstEmbedMethodChanged(); + void toggleVSTAlwaysOnTop( bool en ); + void setLanguage( int lang ); @@ -203,6 +206,8 @@ private slots: QComboBox* m_vstEmbedComboBox; QString m_vstEmbedMethod; + LedCheckBox * m_vstAlwaysOnTopCheckBox; + bool m_vstAlwaysOnTop; } ; diff --git a/include/Track.h b/include/Track.h index 302dcb5cc1d..1267d2ef742 100644 --- a/include/Track.h +++ b/include/Track.h @@ -352,7 +352,7 @@ class TrackContentWidget : public QWidget, public JournallingObject } } - bool canPasteSelection( MidiTime tcoPos, const QMimeData * mimeData ); + bool canPasteSelection( MidiTime tcoPos, const QDropEvent *de ); bool pasteSelection( MidiTime tcoPos, QDropEvent * de ); MidiTime endPosition( const MidiTime & posStart ); @@ -675,6 +675,10 @@ class TrackView : public QWidget, public ModelView, public JournallingObject virtual void update(); + // Create a menu for assigning/creating channels for this track + // Currently instrument track and sample track supports it + virtual QMenu * createFxMenu(QString title, QString newFxLabel); + public slots: virtual bool close(); diff --git a/include/VstSyncData.h b/include/VstSyncData.h index f9696252a42..6c2f1bbd2a6 100644 --- a/include/VstSyncData.h +++ b/include/VstSyncData.h @@ -41,10 +41,10 @@ struct VstSyncData { - bool isPlaying; double ppqPos; int timeSigNumer; int timeSigDenom; + bool isPlaying; bool isCycle; bool hasSHM; float cycleStart; diff --git a/plugins/Eq/EqSpectrumView.cpp b/plugins/Eq/EqSpectrumView.cpp index 959578499fb..e9e0043dea4 100644 --- a/plugins/Eq/EqSpectrumView.cpp +++ b/plugins/Eq/EqSpectrumView.cpp @@ -45,11 +45,11 @@ EqAnalyser::EqAnalyser() : const float a2 = 0.14128; const float a3 = 0.01168; - for(int i = 0; i < FFT_BUFFER_SIZE; i++) + for (int i = 0; i < FFT_BUFFER_SIZE; i++) { - m_fftWindow[i] = ( a0 - a1 * cosf( 2 * F_PI * i / (float)FFT_BUFFER_SIZE - 1 ) - + a2 * cosf( 4 * F_PI * i / (float)FFT_BUFFER_SIZE-1) - - a3 * cos( 6 * F_PI * i / (float)FFT_BUFFER_SIZE - 1.0 )); + m_fftWindow[i] = (a0 - a1 * cos(2 * F_PI * i / ((float)FFT_BUFFER_SIZE - 1.0)) + + a2 * cos(4 * F_PI * i / ((float)FFT_BUFFER_SIZE - 1.0)) + - a3 * cos(6 * F_PI * i / ((float)FFT_BUFFER_SIZE - 1.0))); } clear(); } diff --git a/plugins/FreeBoy/FreeBoy.cpp b/plugins/FreeBoy/FreeBoy.cpp index dd05444a69c..7e525234416 100644 --- a/plugins/FreeBoy/FreeBoy.cpp +++ b/plugins/FreeBoy/FreeBoy.cpp @@ -728,10 +728,10 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { return( new FreeBoyInstrument( - static_cast( _data ) ) ); + static_cast( m ) ) ); } diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index bf599025216..200439dae92 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -1390,9 +1390,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return new GigInstrument( static_cast( _data ) ); + return new GigInstrument( static_cast( m ) ); } } diff --git a/plugins/LadspaEffect/LadspaControls.cpp b/plugins/LadspaEffect/LadspaControls.cpp index 1a2f26a352b..8e65e0e9342 100644 --- a/plugins/LadspaEffect/LadspaControls.cpp +++ b/plugins/LadspaEffect/LadspaControls.cpp @@ -153,6 +153,9 @@ void LadspaControls::linkPort( int _port, bool _state ) { first->unlinkControls( m_controls[proc][_port] ); } + + // m_stereoLinkModel.setValue() will call updateLinkStatesFromGlobal() + // m_noLink will make sure that this will not unlink any other ports m_noLink = true; m_stereoLinkModel.setValue( false ); } diff --git a/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp b/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp index e9344e9438f..4cefa90b5f6 100644 --- a/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp +++ b/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp @@ -44,6 +44,16 @@ LadspaSubPluginFeatures::LadspaSubPluginFeatures( Plugin::PluginTypes _type ) : +QString LadspaSubPluginFeatures::displayName(const Plugin::Descriptor::SubPluginFeatures::Key &k) const +{ + const ladspa_key_t & lkey = subPluginKeyToLadspaKey(&k); + Ladspa2LMMS * lm = Engine::getLADSPAManager(); + return lm->getName(lkey); +} + + + + void LadspaSubPluginFeatures::fillDescriptionWidget( QWidget * _parent, const Key * _key ) const { diff --git a/plugins/LadspaEffect/LadspaSubPluginFeatures.h b/plugins/LadspaEffect/LadspaSubPluginFeatures.h index 904c8133bbb..3f47734f9c8 100644 --- a/plugins/LadspaEffect/LadspaSubPluginFeatures.h +++ b/plugins/LadspaEffect/LadspaSubPluginFeatures.h @@ -25,8 +25,8 @@ * */ -#ifndef _LADSPA_SUBPLUGIN_FEATURES_H -#define _LADSPA_SUBPLUGIN_FEATURES_H +#ifndef LADSPA_SUBPLUGIN_FEATURES_H +#define LADSPA_SUBPLUGIN_FEATURES_H #include "LadspaManager.h" #include "Plugin.h" @@ -37,11 +37,13 @@ class LadspaSubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures public: LadspaSubPluginFeatures( Plugin::PluginTypes _type ); - virtual void fillDescriptionWidget( QWidget * _parent, - const Key * _key ) const; + QString displayName(const Key& k) const override; + void fillDescriptionWidget( QWidget * _parent, + const Key * _key ) const override; virtual void listSubPluginKeys( const Plugin::Descriptor * _desc, - KeyList & _kl ) const; + KeyList & _kl ) const override; + static ladspa_key_t subPluginKeyToLadspaKey( const Key * _key ); diff --git a/plugins/OpulenZ/OpulenZ.cpp b/plugins/OpulenZ/OpulenZ.cpp index d8d5f3e263a..8e5b7f098ca 100644 --- a/plugins/OpulenZ/OpulenZ.cpp +++ b/plugins/OpulenZ/OpulenZ.cpp @@ -79,9 +79,9 @@ Plugin::Descriptor PLUGIN_EXPORT opulenz_plugin_descriptor = }; // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return( new OpulenzInstrument( static_cast( _data ) ) ); + return( new OpulenzInstrument( static_cast( m ) ) ); } } diff --git a/plugins/VstEffect/VstEffectControls.cpp b/plugins/VstEffect/VstEffectControls.cpp index d92717d3781..a0f97ce715e 100644 --- a/plugins/VstEffect/VstEffectControls.cpp +++ b/plugins/VstEffect/VstEffectControls.cpp @@ -90,13 +90,8 @@ void VstEffectControls::loadSettings( const QDomElement & _this ) knobFModel[ i ]->setInitValue(LocaleHelper::toFloat(s_dumpValues.at(2))); } -#if QT_VERSION < 0x050000 - connect( knobFModel[i], SIGNAL( dataChanged( Model * ) ), - this, SLOT( setParameter( Model * ) ), Qt::DirectConnection ); -#else connect( knobFModel[i], &FloatModel::dataChanged, this, [this, i]() { setParameter( knobFModel[i] ); }, Qt::DirectConnection); -#endif } } @@ -383,13 +378,8 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * } FloatModel * model = m_vi->knobFModel[i]; -#if QT_VERSION < 0x050000 - connect( model, SIGNAL( dataChanged( Model * ) ), this, - SLOT( setParameter( Model * ) ), Qt::DirectConnection ); -#else connect( model, &FloatModel::dataChanged, this, [this, model]() { setParameter( model ); }, Qt::DirectConnection); -#endif vstKnobs[ i ] ->setModel( model ); } diff --git a/plugins/Xpressive/Xpressive.cpp b/plugins/Xpressive/Xpressive.cpp index cb02937106e..f0154f75b02 100644 --- a/plugins/Xpressive/Xpressive.cpp +++ b/plugins/Xpressive/Xpressive.cpp @@ -877,8 +877,8 @@ void XpressiveView::helpClicked() { extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main(Model *, void * _data) { - return (new Xpressive(static_cast(_data))); +PLUGIN_EXPORT Plugin * lmms_plugin_main(Model *m, void *) { + return (new Xpressive(static_cast(m))); } } diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 6c080f7c9ac..c1cd725c10e 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -505,7 +505,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, "loop_pingpong_on" ) ); m_loopPingPongButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "loop_pingpong_off" ) ); - ToolTip::add( m_loopPingPongButton, tr( "Enable loop" ) ); + ToolTip::add( m_loopPingPongButton, tr( "Enable ping-pong loop" ) ); m_loopGroup = new automatableButtonGroup( this ); m_loopGroup->addButton( m_loopOffButton ); @@ -753,6 +753,7 @@ AudioFileProcessorWaveView::AudioFileProcessorWaveView( QWidget * _parent, int _ m_graph.fill( Qt::transparent ); update(); + updateCursor(); } @@ -769,7 +770,7 @@ void AudioFileProcessorWaveView::isPlaying( f_cnt_t _current_frame ) void AudioFileProcessorWaveView::enterEvent( QEvent * _e ) { - QApplication::setOverrideCursor( Qt::OpenHandCursor ); + updateCursor(); } @@ -777,10 +778,7 @@ void AudioFileProcessorWaveView::enterEvent( QEvent * _e ) void AudioFileProcessorWaveView::leaveEvent( QEvent * _e ) { - while( QApplication::overrideCursor() ) - { - QApplication::restoreOverrideCursor(); - } + updateCursor(); } @@ -808,7 +806,7 @@ void AudioFileProcessorWaveView::mousePressEvent( QMouseEvent * _me ) else { m_draggingType = wave; - QApplication::setOverrideCursor( Qt::ClosedHandCursor ); + updateCursor(_me); } } @@ -820,7 +818,7 @@ void AudioFileProcessorWaveView::mouseReleaseEvent( QMouseEvent * _me ) m_isDragging = false; if( m_draggingType == wave ) { - QApplication::restoreOverrideCursor(); + updateCursor(_me); } } @@ -831,22 +829,7 @@ void AudioFileProcessorWaveView::mouseMoveEvent( QMouseEvent * _me ) { if( ! m_isDragging ) { - const bool is_size_cursor = - QApplication::overrideCursor()->shape() == Qt::SizeHorCursor; - - if( isCloseTo( _me->x(), m_startFrameX ) || - isCloseTo( _me->x(), m_endFrameX ) || - isCloseTo( _me->x(), m_loopFrameX ) ) - { - if( ! is_size_cursor ) - { - QApplication::setOverrideCursor( Qt::SizeHorCursor ); - } - } - else if( is_size_cursor ) - { - QApplication::restoreOverrideCursor(); - } + updateCursor(_me); return; } @@ -1219,6 +1202,24 @@ void AudioFileProcessorWaveView::reverse() +void AudioFileProcessorWaveView::updateCursor( QMouseEvent * _me ) +{ + bool const waveIsDragged = m_isDragging && (m_draggingType == wave); + bool const pointerCloseToStartEndOrLoop = (_me != nullptr ) && + ( isCloseTo( _me->x(), m_startFrameX ) || + isCloseTo( _me->x(), m_endFrameX ) || + isCloseTo( _me->x(), m_loopFrameX ) ); + + if( !m_isDragging && pointerCloseToStartEndOrLoop) + setCursor(Qt::SizeHorCursor); + else if( waveIsDragged ) + setCursor(Qt::ClosedHandCursor); + else + setCursor(Qt::OpenHandCursor); +} + + + void AudioFileProcessorWaveView::knob::slideTo( double _v, bool _check_bound ) { @@ -1277,10 +1278,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main(Model * model, void *) { - return new audioFileProcessor( - static_cast( _data ) ); + return new audioFileProcessor(static_cast(model)); } diff --git a/plugins/audio_file_processor/audio_file_processor.h b/plugins/audio_file_processor/audio_file_processor.h index 150807686e9..d17be147c0c 100644 --- a/plugins/audio_file_processor/audio_file_processor.h +++ b/plugins/audio_file_processor/audio_file_processor.h @@ -211,7 +211,6 @@ class AudioFileProcessorWaveView : public QWidget private: bool checkBound( double _v ) const; - } ; @@ -276,6 +275,7 @@ public slots: void updateGraph(); void reverse(); + void updateCursor( QMouseEvent * _me = nullptr ); static bool isCloseTo( int _a, int _b ) { diff --git a/plugins/bit_invader/bit_invader.cpp b/plugins/bit_invader/bit_invader.cpp index 6680ed5d6fd..9b0b63635a7 100644 --- a/plugins/bit_invader/bit_invader.cpp +++ b/plugins/bit_invader/bit_invader.cpp @@ -566,9 +566,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return( new bitInvader( static_cast( _data ) ) ); + return( new bitInvader( static_cast( m ) ) ); } diff --git a/plugins/carlapatchbay/carlapatchbay.cpp b/plugins/carlapatchbay/carlapatchbay.cpp index 69c71ce68c8..ac00630d44d 100644 --- a/plugins/carlapatchbay/carlapatchbay.cpp +++ b/plugins/carlapatchbay/carlapatchbay.cpp @@ -25,6 +25,7 @@ #include "carla.h" #include "embed.h" +#include "InstrumentTrack.h" extern "C" { @@ -43,9 +44,9 @@ Plugin::Descriptor PLUGIN_EXPORT carlapatchbay_plugin_descriptor = NULL } ; -PLUGIN_EXPORT Plugin* lmms_plugin_main(Model*, void* data) +PLUGIN_EXPORT Plugin* lmms_plugin_main(Model* m, void*) { - return new CarlaInstrument(static_cast(data), &carlapatchbay_plugin_descriptor, true); + return new CarlaInstrument(static_cast(m), &carlapatchbay_plugin_descriptor, true); } } diff --git a/plugins/carlarack/carlarack.cpp b/plugins/carlarack/carlarack.cpp index 8bc7d372dd9..c0a39f9c258 100644 --- a/plugins/carlarack/carlarack.cpp +++ b/plugins/carlarack/carlarack.cpp @@ -25,6 +25,7 @@ #include "carla.h" #include "embed.h" +#include "InstrumentTrack.h" extern "C" { @@ -43,9 +44,9 @@ Plugin::Descriptor PLUGIN_EXPORT carlarack_plugin_descriptor = NULL } ; -PLUGIN_EXPORT Plugin* lmms_plugin_main(Model*, void* data) +PLUGIN_EXPORT Plugin* lmms_plugin_main(Model* m, void*) { - return new CarlaInstrument(static_cast(data), &carlarack_plugin_descriptor, false); + return new CarlaInstrument(static_cast(m), &carlarack_plugin_descriptor, false); } } diff --git a/plugins/kicker/kicker.cpp b/plugins/kicker/kicker.cpp index 2087cf88068..d204babab23 100644 --- a/plugins/kicker/kicker.cpp +++ b/plugins/kicker/kicker.cpp @@ -367,9 +367,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model * m, void * ) { - return new kickerInstrument( static_cast( _data ) ); + return new kickerInstrument( static_cast( m ) ); } diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index d32f14e8840..e556e42bf00 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -1029,11 +1029,11 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model * m, void * ) { return( new lb302Synth( - static_cast( _data ) ) ); + static_cast( m ) ) ); } diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp index baee242bb78..fb089aa0ce6 100644 --- a/plugins/monstro/Monstro.cpp +++ b/plugins/monstro/Monstro.cpp @@ -1828,9 +1828,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return new MonstroInstrument( static_cast( _data ) ); + return new MonstroInstrument( static_cast( m ) ); } diff --git a/plugins/monstro/exp.png b/plugins/monstro/exp.png index 9fe634881a8..acb7a55cbc2 100644 Binary files a/plugins/monstro/exp.png and b/plugins/monstro/exp.png differ diff --git a/plugins/monstro/moog.png b/plugins/monstro/moog.png index 6d9005966f4..f206bd0da04 100644 Binary files a/plugins/monstro/moog.png and b/plugins/monstro/moog.png differ diff --git a/plugins/monstro/noise.png b/plugins/monstro/noise.png index 6d3853f6073..dbdad683205 100644 Binary files a/plugins/monstro/noise.png and b/plugins/monstro/noise.png differ diff --git a/plugins/monstro/ramp.png b/plugins/monstro/ramp.png index dbd423efdf7..5061f6ed779 100644 Binary files a/plugins/monstro/ramp.png and b/plugins/monstro/ramp.png differ diff --git a/plugins/monstro/saw.png b/plugins/monstro/saw.png index 56c077f2c52..3122b082f8d 100644 Binary files a/plugins/monstro/saw.png and b/plugins/monstro/saw.png differ diff --git a/plugins/monstro/sin.png b/plugins/monstro/sin.png index acabd5a171a..3ac3c9afa23 100644 Binary files a/plugins/monstro/sin.png and b/plugins/monstro/sin.png differ diff --git a/plugins/monstro/sinabs.png b/plugins/monstro/sinabs.png index 2216224e69d..e28d1ec21d4 100644 Binary files a/plugins/monstro/sinabs.png and b/plugins/monstro/sinabs.png differ diff --git a/plugins/monstro/sqr.png b/plugins/monstro/sqr.png index 83d5e486903..d5d3c211c6e 100644 Binary files a/plugins/monstro/sqr.png and b/plugins/monstro/sqr.png differ diff --git a/plugins/monstro/sqrsoft.png b/plugins/monstro/sqrsoft.png index 2e5626d2053..037fcab12bf 100644 Binary files a/plugins/monstro/sqrsoft.png and b/plugins/monstro/sqrsoft.png differ diff --git a/plugins/monstro/tri.png b/plugins/monstro/tri.png index 9e1ab81080e..01119f8941e 100644 Binary files a/plugins/monstro/tri.png and b/plugins/monstro/tri.png differ diff --git a/plugins/nes/Nes.cpp b/plugins/nes/Nes.cpp index 5b34dcb1f60..ba64edb06d2 100644 --- a/plugins/nes/Nes.cpp +++ b/plugins/nes/Nes.cpp @@ -918,9 +918,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * _data ) { - return( new NesInstrument( static_cast( _data ) ) ); + return( new NesInstrument( static_cast( m ) ) ); } diff --git a/plugins/organic/organic.cpp b/plugins/organic/organic.cpp index b6d45c8d36c..63aee0dc694 100644 --- a/plugins/organic/organic.cpp +++ b/plugins/organic/organic.cpp @@ -636,9 +636,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return( new organicInstrument( static_cast( _data ) ) ); + return( new organicInstrument( static_cast( m ) ) ); } diff --git a/plugins/patman/patman.cpp b/plugins/patman/patman.cpp index 6ab7adce719..0ea9968dae7 100644 --- a/plugins/patman/patman.cpp +++ b/plugins/patman/patman.cpp @@ -66,9 +66,9 @@ Plugin::Descriptor PLUGIN_EXPORT patman_plugin_descriptor = // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return new patmanInstrument( static_cast( _data ) ); + return new patmanInstrument( static_cast( m ) ); } } diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index 4bf75777ce8..138e8165d17 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -1150,9 +1150,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return new sf2Instrument( static_cast( _data ) ); + return new sf2Instrument( static_cast( m ) ); } diff --git a/plugins/sfxr/sfxr.cpp b/plugins/sfxr/sfxr.cpp index c99f46f88cf..2718d66d70f 100644 --- a/plugins/sfxr/sfxr.cpp +++ b/plugins/sfxr/sfxr.cpp @@ -1122,9 +1122,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model*, void* data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model* m, void* ) { - return new sfxrInstrument( static_cast( data ) ); + return new sfxrInstrument( static_cast( m ) ); } diff --git a/plugins/sid/sid_instrument.cpp b/plugins/sid/sid_instrument.cpp index e671d4f0521..fcb0b82a790 100644 --- a/plugins/sid/sid_instrument.cpp +++ b/plugins/sid/sid_instrument.cpp @@ -794,10 +794,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return( new sidInstrument( - static_cast( _data ) ) ); + return( new sidInstrument( static_cast( m ) ) ); } diff --git a/plugins/stk/mallets/mallets.cpp b/plugins/stk/mallets/mallets.cpp index 7111bcdaa51..ee1e1fbc371 100644 --- a/plugins/stk/mallets/mallets.cpp +++ b/plugins/stk/mallets/mallets.cpp @@ -756,9 +756,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model * m, void * ) { - return new malletsInstrument( static_cast( _data ) ); + return new malletsInstrument( static_cast( m ) ); } diff --git a/plugins/triple_oscillator/TripleOscillator.cpp b/plugins/triple_oscillator/TripleOscillator.cpp index 6b644fd468b..a883f75fa32 100644 --- a/plugins/triple_oscillator/TripleOscillator.cpp +++ b/plugins/triple_oscillator/TripleOscillator.cpp @@ -723,9 +723,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model* model, void * ) { - return new TripleOscillator( static_cast( _data ) ); + return new TripleOscillator( static_cast( model ) ); } } diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index f4aabd265e0..1d54538b63e 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -186,7 +186,13 @@ vestigeInstrument::~vestigeInstrument() void vestigeInstrument::loadSettings( const QDomElement & _this ) { - loadFile( _this.attribute( "plugin" ) ); + QString plugin = _this.attribute( "plugin" ); + if( plugin.isEmpty() ) + { + return; + } + + loadFile( plugin ); m_pluginMutex.lock(); if( m_plugin != NULL ) { @@ -217,13 +223,8 @@ void vestigeInstrument::loadSettings( const QDomElement & _this ) knobFModel[ i ]->setInitValue(LocaleHelper::toFloat(s_dumpValues.at(2))); } -#if QT_VERSION < 0x050000 - connect( knobFModel[i], SIGNAL( dataChanged( Model * ) ), - this, SLOT( setParameter( Model * ) ), Qt::DirectConnection ); -#else connect( knobFModel[i], &FloatModel::dataChanged, this, [this, i]() { setParameter( knobFModel[i] ); }, Qt::DirectConnection); -#endif } } m_pluginMutex.unlock(); @@ -978,13 +979,8 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume } FloatModel * model = m_vi->knobFModel[i]; -#if QT_VERSION < 0x050000 - connect( model, SIGNAL( dataChanged( Model * ) ), this, - SLOT( setParameter( Model * ) ), Qt::DirectConnection ); -#else connect( model, &FloatModel::dataChanged, this, [this, model]() { setParameter( model ); }, Qt::DirectConnection); -#endif vstKnobs[i] ->setModel( model ); } @@ -1180,9 +1176,9 @@ extern "C" { // necessary for getting instance out of shared lib -Q_DECL_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +Q_DECL_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return new vestigeInstrument( static_cast( _data ) ); + return new vestigeInstrument( static_cast( m ) ); } diff --git a/plugins/vibed/vibed.cpp b/plugins/vibed/vibed.cpp index f33a9042853..bde5c8917a4 100644 --- a/plugins/vibed/vibed.cpp +++ b/plugins/vibed/vibed.cpp @@ -682,9 +682,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return( new vibed( static_cast( _data ) ) ); + return( new vibed( static_cast( m ) ) ); } diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index 314d5fc18e4..44ed0dcb3be 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -5,11 +5,11 @@ ENDIF() INCLUDE(BuildPlugin) INCLUDE(ExternalProject) -ADD_SUBDIRECTORY(vstbase) +# These variables are not meant to be used normally, except packaging +SET(REMOTE_VST_PLUGIN_FILEPATH_32 "32/RemoteVstPlugin32" CACHE STRING "Relative file path to RemoteVstPlugin32") +SET(REMOTE_VST_PLUGIN_FILEPATH_64 "RemoteVstPlugin64" CACHE STRING "Relative file path to RemoteVstPlugin64") -IF(LMMS_BUILD_LINUX AND WANT_VST_NOWINE) - RETURN() -ENDIF() +ADD_SUBDIRECTORY(vstbase) SET(LMMS_BINARY_DIR ${CMAKE_BINARY_DIR}) SET(LMMS_SOURCE_DIR ${CMAKE_SOURCE_DIR}) @@ -29,6 +29,10 @@ SET(EXTERNALPROJECT_CMAKE_ARGS ) # build 32 bit version of RemoteVstPlugin -INCLUDE("${CMAKE_CURRENT_LIST_DIR}/RemoteVstPlugin32.cmake") +IF(WANT_VST_32) + INCLUDE("${CMAKE_CURRENT_LIST_DIR}/RemoteVstPlugin32.cmake") +ENDIF() # build 64 bit version of RemoteVstPlugin -INCLUDE("${CMAKE_CURRENT_LIST_DIR}/RemoteVstPlugin64.cmake") +IF(WANT_VST_64) + INCLUDE("${CMAKE_CURRENT_LIST_DIR}/RemoteVstPlugin64.cmake") +ENDIF() diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index a9ed335f0c2..3526b27cc9d 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -791,10 +791,6 @@ void RemoteVstPlugin::initEditor() SWP_NOMOVE | SWP_NOZORDER ); pluginDispatch( effEditTop ); - if (! EMBED) { - showEditor(); - } - #ifdef LMMS_BUILD_LINUX m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" ); #else diff --git a/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt b/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt index 2a9f84076b0..59dd19a0aa6 100644 --- a/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt +++ b/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt @@ -18,6 +18,7 @@ if(IS_WIN64 OR CMAKE_SIZEOF_VOID_P EQUAL 8) set(BITNESS 64) else() set(BITNESS 32) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/32") endif() FOREACH( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) diff --git a/plugins/vst_base/RemoteVstPlugin32.cmake b/plugins/vst_base/RemoteVstPlugin32.cmake index 07bed93b61a..9a8f0452948 100644 --- a/plugins/vst_base/RemoteVstPlugin32.cmake +++ b/plugins/vst_base/RemoteVstPlugin32.cmake @@ -8,7 +8,7 @@ IF(LMMS_BUILD_WIN32 AND NOT LMMS_BUILD_WIN64) INSTALL(FILES "${MINGW_PREFIX}/bin/Qt5Core.dll" DESTINATION "${PLUGIN_DIR}/32") INSTALL(FILES "${MINGW_PREFIX}/bin/zlib1.dll" DESTINATION "${PLUGIN_DIR}/32") ENDIF(MSVC) - INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") + INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") ELSEIF(LMMS_BUILD_WIN64 AND MSVC) SET(MSVC_VER ${CMAKE_CXX_COMPILER_VERSION}) @@ -46,7 +46,7 @@ ELSEIF(LMMS_BUILD_WIN64 AND MSVC) "-DCMAKE_PREFIX_PATH=${QT_32_PREFIX}" ) - INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") + INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") #TODO: find a solution when not using vcpkg for qt SET(VCPKG_ROOT_32 "${CMAKE_FIND_ROOT_PATH}/../x86-windows") @@ -58,6 +58,12 @@ ELSEIF(LMMS_BUILD_WIN64 AND MSVC) ELSEIF(LMMS_BUILD_LINUX) # Use winegcc + INCLUDE(CheckWineGcc) + CheckWineGcc(32 "${WINEGCC}" WINEGCC_WORKING) + IF(NOT WINEGCC_WORKING) + MESSAGE(WARNING "winegcc fails to complie 32-bit binaries, please make sure you have 32-bit GCC libraries") + RETURN() + ENDIF() ExternalProject_Add(RemoteVstPlugin32 "${EXTERNALPROJECT_ARGS}" CMAKE_ARGS @@ -66,7 +72,7 @@ ELSEIF(LMMS_BUILD_LINUX) "-DCMAKE_CXX_FLAGS=-m32" ) - INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin32" "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin32.exe.so" DESTINATION "${PLUGIN_DIR}/32") + INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32" "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32.exe.so" DESTINATION "${PLUGIN_DIR}/32") ELSEIF(CMAKE_TOOLCHAIN_FILE_32) ExternalProject_Add(RemoteVstPlugin32 @@ -78,7 +84,7 @@ ELSEIF(CMAKE_TOOLCHAIN_FILE_32) ) INSTALL(FILES "${CMAKE_PREFIX_PATH_32}/bin/Qt5Core.dll" DESTINATION "${PLUGIN_DIR}/32") INSTALL(FILES "${CMAKE_PREFIX_PATH_32}/bin/zlib1.dll" DESTINATION "${PLUGIN_DIR}/32") - INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") + INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") ELSE() MESSAGE(WARNING "Can't build RemoteVstPlugin32, unknown environment. Please supply CMAKE_TOOLCHAIN_FILE_32 and optionally CMAKE_PREFIX_PATH_32") RETURN() diff --git a/plugins/vst_base/RemoteVstPlugin64.cmake b/plugins/vst_base/RemoteVstPlugin64.cmake index f802bc4b902..4b02bf8abf4 100644 --- a/plugins/vst_base/RemoteVstPlugin64.cmake +++ b/plugins/vst_base/RemoteVstPlugin64.cmake @@ -2,6 +2,12 @@ IF(LMMS_BUILD_WIN64) ADD_SUBDIRECTORY(RemoteVstPlugin) INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin64.exe" DESTINATION "${PLUGIN_DIR}") ELSEIF(LMMS_BUILD_LINUX) + INCLUDE(CheckWineGcc) + CheckWineGcc(64 "${WINEGCC}" WINEGCC_WORKING) + IF(NOT WINEGCC_WORKING) + MESSAGE(WARNING "winegcc fails to compile 64-bit binaries, please make sure you have 64-bit GCC libraries") + RETURN() + ENDIF() ExternalProject_Add(RemoteVstPlugin64 "${EXTERNALPROJECT_ARGS}" CMAKE_ARGS diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 2e69802b2c2..9f468087c78 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -149,10 +149,10 @@ VstPlugin::VstPlugin( const QString & _plugin ) : switch(machineType) { case PE::MachineType::amd64: - tryLoad( "RemoteVstPlugin64" ); + tryLoad( REMOTE_VST_PLUGIN_FILEPATH_64 ); // Default: RemoteVstPlugin64 break; case PE::MachineType::i386: - tryLoad( "32/RemoteVstPlugin32" ); + tryLoad( REMOTE_VST_PLUGIN_FILEPATH_32 ); // Default: 32/RemoteVstPlugin32 break; default: m_failed = true; @@ -386,7 +386,9 @@ bool VstPlugin::processMessage( const message & _m ) { case IdVstPluginWindowID: m_pluginWindowID = _m.getInt(); - if( m_embedMethod == "none" ) + if( m_embedMethod == "none" + && ConfigManager::inst()->value( + "ui", "vstalwaysontop" ).toInt() ) { #ifdef LMMS_BUILD_WIN32 // We're changing the owner, not the parent, diff --git a/plugins/vst_base/vstbase/CMakeLists.txt b/plugins/vst_base/vstbase/CMakeLists.txt index 28c09edb380..bd5f98b561e 100644 --- a/plugins/vst_base/vstbase/CMakeLists.txt +++ b/plugins/vst_base/vstbase/CMakeLists.txt @@ -1,3 +1,6 @@ +ADD_DEFINITIONS(-DREMOTE_VST_PLUGIN_FILEPATH_32="${REMOTE_VST_PLUGIN_FILEPATH_32}") +ADD_DEFINITIONS(-DREMOTE_VST_PLUGIN_FILEPATH_64="${REMOTE_VST_PLUGIN_FILEPATH_64}") + BUILD_PLUGIN(vstbase ../vst_base.cpp ../VstPlugin.cpp ../VstPlugin.h ../communication.h MOCFILES ../VstPlugin.h diff --git a/plugins/watsyn/Watsyn.cpp b/plugins/watsyn/Watsyn.cpp index 0122b293a23..2787e39b711 100644 --- a/plugins/watsyn/Watsyn.cpp +++ b/plugins/watsyn/Watsyn.cpp @@ -1279,9 +1279,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return( new WatsynInstrument( static_cast( _data ) ) ); + return( new WatsynInstrument( static_cast( m ) ) ); } diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index 85e5b0118b4..277c22596a6 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -290,6 +291,7 @@ void ZynAddSubFxInstrument::loadSettings( const QDomElement & _this ) emit settingsChanged(); } + emit instrumentTrack()->pitchModel()->dataChanged(); } @@ -659,10 +661,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main(Model * m, void *) { - - return new ZynAddSubFxInstrument( static_cast( _data ) ); + return new ZynAddSubFxInstrument(static_cast(m)); } diff --git a/plugins/zynaddsubfx/zynaddsubfx b/plugins/zynaddsubfx/zynaddsubfx index c22acd61eb5..ccac06336b3 160000 --- a/plugins/zynaddsubfx/zynaddsubfx +++ b/plugins/zynaddsubfx/zynaddsubfx @@ -1 +1 @@ -Subproject commit c22acd61eb5d074988acea5fc1b6930151345c42 +Subproject commit ccac06336b363b9afe7ff4aea02bfa2d48187e1a diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 37da8f414f9..4ac6bf1332f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,7 +45,7 @@ INCLUDE(GenQrc) ADD_GEN_QRC(LMMS_RCC_OUT lmms.qrc "${CMAKE_SOURCE_DIR}/doc/AUTHORS" "${CMAKE_SOURCE_DIR}/LICENSE.txt" - "${CMAKE_BINARY_DIR}/CONTRIBUTORS" + "${CONTRIBUTORS}" ) # Paths relative to lmms executable diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 9d6781f7ce7..d8c783dd235 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -511,8 +511,9 @@ void ConfigManager::loadConfigFile( const QString & configFile ) cfg_file.close(); } - + // Plugins are searched recursively, blacklist problematic locations if( m_vstDir.isEmpty() || m_vstDir == QDir::separator() || m_vstDir == "/" || + m_vstDir == ensureTrailingSlash( QDir::homePath() ) || !QDir( m_vstDir ).exists() ) { #ifdef LMMS_BUILD_WIN32 diff --git a/src/core/DrumSynth.cpp b/src/core/DrumSynth.cpp index cfa85ceb6c0..c41de1fd4d0 100644 --- a/src/core/DrumSynth.cpp +++ b/src/core/DrumSynth.cpp @@ -124,7 +124,10 @@ void DrumSynth::GetEnv(int env, const char *sec, const char *key, QString ini) char en[256], s[8]; int i=0, o=0, ep=0; GetPrivateProfileString(sec, key, "0,0 100,0", en, sizeof(en), ini); - en[255]=0; //be safe! + + //be safe! + en[255]=0; + s[0]=0; while(en[i]!=0) { diff --git a/src/core/Effect.cpp b/src/core/Effect.cpp index 8cdcc9c600a..c842977532f 100644 --- a/src/core/Effect.cpp +++ b/src/core/Effect.cpp @@ -36,9 +36,8 @@ Effect::Effect( const Plugin::Descriptor * _desc, Model * _parent, const Descriptor::SubPluginFeatures::Key * _key ) : - Plugin( _desc, _parent ), + Plugin( _desc, _parent, _key ), m_parent( NULL ), - m_key( _key ? *_key : Descriptor::SubPluginFeatures::Key() ), m_processors( 1 ), m_okay( true ), m_noRun( false ), @@ -117,7 +116,7 @@ Effect * Effect::instantiate( const QString& pluginName, Model * _parent, Descriptor::SubPluginFeatures::Key * _key ) { - Plugin * p = Plugin::instantiate( pluginName, _parent, _key ); + Plugin * p = Plugin::instantiateWithKey( pluginName, _parent, _key ); // check whether instantiated plugin is an effect if( dynamic_cast( p ) != NULL ) { diff --git a/src/core/Engine.cpp b/src/core/Engine.cpp index a53abbe5d45..50e25b0b416 100644 --- a/src/core/Engine.cpp +++ b/src/core/Engine.cpp @@ -29,6 +29,7 @@ #include "FxMixer.h" #include "Ladspa2LMMS.h" #include "Mixer.h" +#include "Plugin.h" #include "PresetPreviewPlayHandle.h" #include "ProjectJournal.h" #include "Song.h" @@ -41,6 +42,7 @@ BBTrackContainer * LmmsCore::s_bbTrackContainer = NULL; Song * LmmsCore::s_song = NULL; ProjectJournal * LmmsCore::s_projectJournal = NULL; Ladspa2LMMS * LmmsCore::s_ladspaManager = NULL; +void* LmmsCore::s_dndPluginKey = nullptr; DummyTrackContainer * LmmsCore::s_dummyTC = NULL; @@ -112,4 +114,24 @@ void LmmsCore::updateFramesPerTick() DefaultTicksPerTact / s_song->getTempo(); } + + + +void LmmsCore::setDndPluginKey(void *newKey) +{ + Q_ASSERT(static_cast(newKey)); + s_dndPluginKey = newKey; +} + + + + +void *LmmsCore::pickDndPluginKey() +{ + return s_dndPluginKey; +} + + + + LmmsCore * LmmsCore::s_instanceOfMe = NULL; diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 0e5f200d6d5..032090bf1d5 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -32,6 +32,7 @@ #include "Song.h" #include "InstrumentTrack.h" +#include "SampleTrack.h" #include "BBTrackContainer.h" FxRoute::FxRoute( FxChannel * from, FxChannel * to, float amount ) : @@ -305,6 +306,22 @@ void FxMixer::deleteChannel( int index ) inst->effectChannelModel()->setValue(val-1); } } + else if( t->type() == Track::SampleTrack ) + { + SampleTrack* strk = dynamic_cast( t ); + int val = strk->effectChannelModel()->value(0); + if( val == index ) + { + // we are deleting this track's fx send + // send to master + strk->effectChannelModel()->setValue(0); + } + else if( val > index ) + { + // subtract 1 to make up for the missing channel + strk->effectChannelModel()->setValue(val-1); + } + } } FxChannel * ch = m_fxChannels[index]; @@ -379,6 +396,19 @@ void FxMixer::moveChannelLeft( int index ) inst->effectChannelModel()->setValue(a); } } + else if( trackList[i]->type() == Track::SampleTrack ) + { + SampleTrack * strk = (SampleTrack *) trackList[i]; + int val = strk->effectChannelModel()->value(0); + if( val == a ) + { + strk->effectChannelModel()->setValue(b); + } + else if( val == b ) + { + strk->effectChannelModel()->setValue(a); + } + } } } @@ -780,4 +810,3 @@ void FxMixer::validateChannelName( int index, int oldIndex ) m_fxChannels[index]->m_name = tr( "FX %1" ).arg( index ); } } - diff --git a/src/core/Instrument.cpp b/src/core/Instrument.cpp index 534bb783a23..ba608da14a2 100644 --- a/src/core/Instrument.cpp +++ b/src/core/Instrument.cpp @@ -27,9 +27,10 @@ #include "DummyInstrument.h" -Instrument::Instrument( InstrumentTrack * _instrument_track, - const Descriptor * _descriptor ) : - Plugin( _descriptor, NULL/* _instrument_track*/ ), +Instrument::Instrument(InstrumentTrack * _instrument_track, + const Descriptor * _descriptor, + const Descriptor::SubPluginFeatures::Key *key) : + Plugin(_descriptor, NULL/* _instrument_track*/, key), m_instrumentTrack( _instrument_track ) { } @@ -56,19 +57,15 @@ f_cnt_t Instrument::beatLen( NotePlayHandle * ) const -Instrument * Instrument::instantiate( const QString & _plugin_name, - InstrumentTrack * _instrument_track ) +Instrument *Instrument::instantiate(const QString &_plugin_name, + InstrumentTrack *_instrument_track, const Descriptor::SubPluginFeatures::Key *key, bool keyFromDnd) { - Plugin * p = Plugin::instantiate( _plugin_name, _instrument_track, - _instrument_track ); - // check whether instantiated plugin is an instrument - if( dynamic_cast( p ) != NULL ) - { - // everything ok, so return pointer - return dynamic_cast( p ); - } - - // not quite... so delete plugin and return dummy instrument + if(keyFromDnd) + Q_ASSERT(!key); + // copy from above // TODO! common cleaner func + Plugin * p = Plugin::instantiateWithKey(_plugin_name, _instrument_track, key, keyFromDnd); + if(dynamic_cast(p)) + return dynamic_cast(p); delete p; return( new DummyInstrument( _instrument_track ) ); } diff --git a/src/core/LadspaManager.cpp b/src/core/LadspaManager.cpp index 4336e50a5d8..febbe5a9192 100644 --- a/src/core/LadspaManager.cpp +++ b/src/core/LadspaManager.cpp @@ -40,7 +40,7 @@ LadspaManager::LadspaManager() { // Make sure plugin search paths are set up - PluginFactory::instance(); + PluginFactory::setupSearchPaths(); QStringList ladspaDirectories = QString( getenv( "LADSPA_PATH" ) ). split( LADSPA_PATH_SEPERATOR ); diff --git a/src/core/MixHelpers.cpp b/src/core/MixHelpers.cpp index cdbf079d360..de1c5cfc3a8 100644 --- a/src/core/MixHelpers.cpp +++ b/src/core/MixHelpers.cpp @@ -23,6 +23,9 @@ */ #include "MixHelpers.h" + +#include + #include "lmms_math.h" #include "ValueBuffer.h" diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index e0ac414c25b..49cda8c34e2 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -186,8 +186,8 @@ Mixer::~Mixer() } delete m_fifo; - delete m_audioDev; delete m_midiClient; + delete m_audioDev; for( int i = 0; i < 3; i++ ) { diff --git a/src/core/Plugin.cpp b/src/core/Plugin.cpp index b479d7d8da6..411f6fe5899 100644 --- a/src/core/Plugin.cpp +++ b/src/core/Plugin.cpp @@ -22,12 +22,14 @@ * */ +#include "Plugin.h" + +#include #include #include #include #include -#include "Plugin.h" #include "embed.h" #include "Engine.h" #include "GuiApplication.h" @@ -53,10 +55,12 @@ static Plugin::Descriptor dummyPluginDescriptor = -Plugin::Plugin( const Descriptor * descriptor, Model * parent ) : - Model( parent ), +Plugin::Plugin(const Descriptor * descriptor, Model * parent, const + Descriptor::SubPluginFeatures::Key* key) : + Model(parent), JournallingObject(), - m_descriptor( descriptor ) + m_descriptor(descriptor), + m_key(key ? *key : Descriptor::SubPluginFeatures::Key(m_descriptor)) { if( m_descriptor == NULL ) { @@ -74,6 +78,97 @@ Plugin::~Plugin() +template +T use_this_or(T this_param, T or_param) +{ + return this_param ? this_param : or_param; +} + + + + +QString use_this_or(QString this_param, QString or_param) +{ + return this_param.isNull() ? or_param : this_param; +} + + + + +QString Plugin::displayName() const +{ + return Model::displayName().isEmpty() // currently always empty + ? (m_descriptor->subPluginFeatures && m_key.isValid()) + // get from sub plugin + ? m_key.displayName() + // get from plugin + : m_descriptor->displayName + : Model::displayName(); +} + + + + +const PixmapLoader* Plugin::logo() const +{ + return (m_descriptor->subPluginFeatures && m_key.isValid()) + ? m_key.logo() + : m_descriptor->logo; +} + + + + +QString Plugin::Descriptor::SubPluginFeatures::Key::additionalFileExtensions() const +{ + Q_ASSERT(isValid()); + return desc->subPluginFeatures + // get from sub plugin + ? desc->subPluginFeatures->additionalFileExtensions(*this) + // no sub plugin, so no *additional* file extensions + : QString(); +} + + + + +QString Plugin::Descriptor::SubPluginFeatures::Key::displayName() const +{ + Q_ASSERT(isValid()); + return desc->subPluginFeatures + // get from sub plugin + ? use_this_or(desc->subPluginFeatures->displayName(*this), + QString::fromUtf8(desc->displayName)) + // get from plugin + : desc->displayName; +} + + + + +const PixmapLoader* Plugin::Descriptor::SubPluginFeatures::Key::logo() const +{ + Q_ASSERT(isValid()); + return desc->subPluginFeatures + ? use_this_or(desc->subPluginFeatures->logo(*this), desc->logo) + : desc->logo; +} + + + + +QString Plugin::Descriptor::SubPluginFeatures::Key::description() const +{ + Q_ASSERT(isValid()); + return desc->subPluginFeatures + ? use_this_or(desc->subPluginFeatures->description(*this), + QString::fromUtf8(desc->description)) + : desc->description; +} + + + + void Plugin::loadFile( const QString & ) { } @@ -90,10 +185,38 @@ AutomatableModel * Plugin::childModel( const QString & ) #include "PluginFactory.h" -Plugin * Plugin::instantiate( const QString& pluginName, Model * parent, - void * data ) +Plugin * Plugin::instantiateWithKey(const QString& pluginName, Model * parent, + const Descriptor::SubPluginFeatures::Key *key, + bool keyFromDnd) { + if(keyFromDnd) + Q_ASSERT(!key); + const Descriptor::SubPluginFeatures::Key *keyPtr = keyFromDnd + ? static_cast(Engine::pickDndPluginKey()) + : key; const PluginFactory::PluginInfo& pi = pluginFactory->pluginInfo(pluginName.toUtf8()); + if(keyPtr) + { + // descriptor is not yet set when loading - set it now + Descriptor::SubPluginFeatures::Key keyCopy = *keyPtr; + keyCopy.desc = pi.descriptor; + return Plugin::instantiate(pluginName, parent, &keyCopy); + } + else + return Plugin::instantiate(pluginName, parent, + // the keys are never touched anywhere + const_cast(keyPtr)); +} + + + + +Plugin * Plugin::instantiate(const QString& pluginName, Model * parent, + void *data) +{ + const PluginFactory::PluginInfo& pi = pluginFactory->pluginInfo(pluginName.toUtf8()); + + Plugin* inst; if( pi.isNull() ) { if( gui ) @@ -104,23 +227,31 @@ Plugin * Plugin::instantiate( const QString& pluginName, Model * parent, arg( pluginName ).arg( pluginFactory->errorString(pluginName) ), QMessageBox::Ok | QMessageBox::Default ); } - return new DummyPlugin(); + inst = new DummyPlugin(); } - - InstantiationHook instantiationHook = ( InstantiationHook ) pi.library->resolve( "lmms_plugin_main" ); - if( instantiationHook == NULL ) + else { - if( gui ) + InstantiationHook instantiationHook; + if ((instantiationHook = ( InstantiationHook ) pi.library->resolve( "lmms_plugin_main" ))) { - QMessageBox::information( NULL, - tr( "Error while loading plugin" ), - tr( "Failed to load plugin \"%1\"!").arg( pluginName ), - QMessageBox::Ok | QMessageBox::Default ); + inst = instantiationHook(parent, data); + if(!inst) { + inst = new DummyPlugin(); + } + } + else + { + if( gui ) + { + QMessageBox::information( NULL, + tr( "Error while loading plugin" ), + tr( "Failed to load plugin \"%1\"!").arg( pluginName ), + QMessageBox::Ok | QMessageBox::Default ); + } + inst = new DummyPlugin(); } - return new DummyPlugin(); } - Plugin * inst = instantiationHook( parent, data ); return inst; } diff --git a/src/core/PluginFactory.cpp b/src/core/PluginFactory.cpp index a87e492702d..abf6421229e 100644 --- a/src/core/PluginFactory.cpp +++ b/src/core/PluginFactory.cpp @@ -28,8 +28,11 @@ #include #include #include +#include "lmmsconfig.h" #include "ConfigManager.h" +#include "Plugin.h" +#include "embed.h" #ifdef LMMS_BUILD_WIN32 QStringList nameFilters("*.dll"); @@ -45,6 +48,16 @@ qint64 qHash(const QFileInfo& fi) std::unique_ptr PluginFactory::s_instance; PluginFactory::PluginFactory() +{ + setupSearchPaths(); + discoverPlugins(); +} + +PluginFactory::~PluginFactory() +{ +} + +void PluginFactory::setupSearchPaths() { // Adds a search path relative to the main executable if the path exists. auto addRelativeIfExists = [](const QString & path) { @@ -76,12 +89,6 @@ PluginFactory::PluginFactory() QDir::addSearchPath("plugins", env_path); QDir::addSearchPath("plugins", ConfigManager::inst()->workingDir() + "plugins"); - - discoverPlugins(); -} - -PluginFactory::~PluginFactory() -{ } PluginFactory* PluginFactory::instance() @@ -107,9 +114,9 @@ const PluginFactory::PluginInfoList& PluginFactory::pluginInfos() const return m_pluginInfos; } -const PluginFactory::PluginInfo PluginFactory::pluginSupportingExtension(const QString& ext) +const PluginFactory::PluginInfoAndKey PluginFactory::pluginSupportingExtension(const QString& ext) { - return m_pluginByExt.value(ext, PluginInfo()); + return m_pluginByExt.value(ext, PluginInfoAndKey()); } const PluginFactory::PluginInfo PluginFactory::pluginInfo(const char* name) const @@ -150,42 +157,78 @@ void PluginFactory::discoverPlugins() for (const QFileInfo& file : files) { auto library = std::make_shared(file.absoluteFilePath()); - if (! library->load()) { m_errors[file.baseName()] = library->errorString(); qWarning("%s", library->errorString().toLocal8Bit().data()); continue; } - if (library->resolve("lmms_plugin_main") == nullptr) { - continue; - } - QString descriptorName = file.baseName() + "_plugin_descriptor"; - if( descriptorName.left(3) == "lib" ) + Plugin::Descriptor* pluginDescriptor = nullptr; + if (library->resolve("lmms_plugin_main")) { - descriptorName = descriptorName.mid(3); + QString descriptorName = file.baseName() + "_plugin_descriptor"; + if( descriptorName.left(3) == "lib" ) + { + descriptorName = descriptorName.mid(3); + } + + pluginDescriptor = reinterpret_cast(library->resolve(descriptorName.toUtf8().constData())); + if(pluginDescriptor == nullptr) + { + qWarning() << qApp->translate("PluginFactory", "LMMS plugin %1 does not have a plugin descriptor named %2!"). + arg(file.absoluteFilePath()).arg(descriptorName); + continue; + } } - Plugin::Descriptor* pluginDescriptor = reinterpret_cast(library->resolve(descriptorName.toUtf8().constData())); - if(pluginDescriptor == nullptr) + if(pluginDescriptor) { - qWarning() << qApp->translate("PluginFactory", "LMMS plugin %1 does not have a plugin descriptor named %2!"). - arg(file.absoluteFilePath()).arg(descriptorName); - continue; + PluginInfo info; + info.file = file; + info.library = library; + info.descriptor = pluginDescriptor; + pluginInfos << info; + + auto addSupportedFileTypes = + [this](QString supportedFileTypes, + const PluginInfo& info, + const Plugin::Descriptor::SubPluginFeatures::Key* key = nullptr) + { + if(!supportedFileTypes.isNull()) + { + for (const QString& ext : supportedFileTypes.split(',')) + { + //qDebug() << "Plugin " << info.name() + // << "supports" << ext; + PluginInfoAndKey infoAndKey; + infoAndKey.info = info; + infoAndKey.key = key + ? *key + : Plugin::Descriptor::SubPluginFeatures::Key(); + m_pluginByExt.insert(ext, infoAndKey); + } + } + }; + + if (info.descriptor->supportedFileTypes) + addSupportedFileTypes(QString(info.descriptor->supportedFileTypes), info); + + if (info.descriptor->subPluginFeatures) + { + Plugin::Descriptor::SubPluginFeatures::KeyList + subPluginKeys; + info.descriptor->subPluginFeatures->listSubPluginKeys( + info.descriptor, + subPluginKeys); + for(const Plugin::Descriptor::SubPluginFeatures::Key& key + : subPluginKeys) + { + addSupportedFileTypes(key.additionalFileExtensions(), info, &key); + } + } + + descriptors.insert(info.descriptor->type, info.descriptor); } - - PluginInfo info; - info.file = file; - info.library = library; - info.descriptor = pluginDescriptor; - pluginInfos << info; - - for (const QString& ext : QString(info.descriptor->supportedFileTypes).split(',')) - { - m_pluginByExt.insert(ext, info); - } - - descriptors.insert(info.descriptor->type, info.descriptor); } m_pluginInfos = pluginInfos; diff --git a/src/core/PresetPreviewPlayHandle.cpp b/src/core/PresetPreviewPlayHandle.cpp index dc36819b7d7..ca0e5219499 100644 --- a/src/core/PresetPreviewPlayHandle.cpp +++ b/src/core/PresetPreviewPlayHandle.cpp @@ -137,8 +137,10 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, suffix().toLower(); if( i == NULL || !i->descriptor()->supportsFileType( ext ) ) { + const PluginFactory::PluginInfoAndKey& infoAndKey = + pluginFactory->pluginSupportingExtension(ext); i = s_previewTC->previewInstrumentTrack()-> - loadInstrument(pluginFactory->pluginSupportingExtension(ext).name()); + loadInstrument(infoAndKey.info.name(), &infoAndKey.key); } if( i != NULL ) { diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 5e6758fdecb..d08144e4705 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -525,7 +525,7 @@ void TrackContentObjectView::dragEnterEvent( QDragEnterEvent * dee ) { TrackContentWidget * tcw = getTrackView()->getTrackContentWidget(); MidiTime tcoPos = MidiTime( m_tco->startPosition().getTact(), 0 ); - if( tcw->canPasteSelection( tcoPos, dee->mimeData() ) == false ) + if( tcw->canPasteSelection( tcoPos, dee ) == false ) { dee->ignore(); } @@ -630,9 +630,12 @@ DataFile TrackContentObjectView::createTCODataFiles( it != tcoViews.end(); ++it ) { // Insert into the dom under the "tcos" element - int trackIndex = tc->tracks().indexOf( ( *it )->m_trackView->getTrack() ); + Track* tcoTrack = ( *it )->m_trackView->getTrack(); + int trackIndex = tc->tracks().indexOf( tcoTrack ); QDomElement tcoElement = dataFile.createElement( "tco" ); tcoElement.setAttribute( "trackIndex", trackIndex ); + tcoElement.setAttribute( "trackType", tcoTrack->type() ); + tcoElement.setAttribute( "trackName", tcoTrack->name() ); ( *it )->m_tco->saveState( dataFile, tcoElement ); tcoParent.appendChild( tcoElement ); } @@ -649,6 +652,7 @@ DataFile TrackContentObjectView::createTCODataFiles( QDomElement metadata = dataFile.createElement( "copyMetadata" ); // initialTrackIndex is the index of the track that was touched metadata.setAttribute( "initialTrackIndex", initialTrackIndex ); + metadata.setAttribute( "trackContainerId", tc->id() ); // grabbedTCOPos is the pos of the tact containing the TCO we grabbed metadata.setAttribute( "grabbedTCOPos", m_tco->startPosition() ); @@ -1387,7 +1391,7 @@ MidiTime TrackContentWidget::getPosition( int mouseX ) void TrackContentWidget::dragEnterEvent( QDragEnterEvent * dee ) { MidiTime tcoPos = MidiTime( getPosition( dee->pos().x() ).getTact(), 0 ); - if( canPasteSelection( tcoPos, dee->mimeData() ) == false ) + if( canPasteSelection( tcoPos, dee ) == false ) { dee->ignore(); } @@ -1406,8 +1410,10 @@ void TrackContentWidget::dragEnterEvent( QDragEnterEvent * dee ) * \param tcoPos the position of the TCO slot being pasted on * \param de the DropEvent generated */ -bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData * mimeData ) +bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QDropEvent* de ) { + const QMimeData * mimeData = de->mimeData(); + Track * t = getTrack(); QString type = StringPairDrag::decodeMimeKey( mimeData ); QString value = StringPairDrag::decodeMimeValue( mimeData ); @@ -1437,7 +1443,9 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData * m const int currentTrackIndex = tracks.indexOf( t ); // Don't paste if we're on the same tact - if( tcoPos == grabbedTCOTact && currentTrackIndex == initialTrackIndex ) + auto sourceTrackContainerId = metadata.attributeNode( "trackContainerId" ).value().toUInt(); + if( de->source() && sourceTrackContainerId == t->trackContainer()->id() && + tcoPos == grabbedTCOTact && currentTrackIndex == initialTrackIndex ) { return false; } @@ -1460,9 +1468,9 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData * m } // Track must be of the same type - Track * startTrack = tracks.at( trackIndex ); + auto startTrackType = tcoElement.attributeNode("trackType").value().toInt(); Track * endTrack = tracks.at( finalTrackIndex ); - if( startTrack->type() != endTrack->type() ) + if( startTrackType != endTrack->type() ) { return false; } @@ -1478,7 +1486,7 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData * m */ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de ) { - if( canPasteSelection( tcoPos, de->mimeData() ) == false ) + if( canPasteSelection( tcoPos, de ) == false ) { return false; } @@ -1548,7 +1556,8 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de ) } //check tco name, if the same as source track name dont copy - if( tco->name() == tracks[trackIndex]->name() ) + QString sourceTrackName = outerTCOElement.attributeNode( "trackName" ).value(); + if( tco->name() == sourceTrackName ) { tco->setName( "" ); } @@ -1924,13 +1933,15 @@ void TrackOperationsWidget::updateMenu() { toMenu->addAction( tr( "Clear this track" ), this, SLOT( clearTrack() ) ); } - if( InstrumentTrackView * trackView = dynamic_cast( m_trackView ) ) + if (QMenu *fxMenu = m_trackView->createFxMenu(tr("FX %1: %2"), tr("Assign to new FX Channel"))) { - QMenu *fxMenu = trackView->createFxMenu( tr( "FX %1: %2" ), tr( "Assign to new FX Channel" )); toMenu->addMenu(fxMenu); + } + if (InstrumentTrackView * trackView = dynamic_cast(m_trackView)) + { toMenu->addSeparator(); - toMenu->addMenu( trackView->midiMenu() ); + toMenu->addMenu(trackView->midiMenu()); } if( dynamic_cast( m_trackView ) ) { @@ -2677,6 +2688,19 @@ void TrackView::update() +/*! \brief Create a menu for assigning/creating channels for this track. + * + */ +QMenu * TrackView::createFxMenu(QString title, QString newFxLabel) +{ + Q_UNUSED(title) + Q_UNUSED(newFxLabel) + return NULL; +} + + + + /*! \brief Close this track View. * */ diff --git a/src/core/midi/MidiJack.cpp b/src/core/midi/MidiJack.cpp index cea7f7381e7..568b6dae164 100644 --- a/src/core/midi/MidiJack.cpp +++ b/src/core/midi/MidiJack.cpp @@ -61,6 +61,7 @@ static void JackMidiShutdown(void *arg) MidiJack::MidiJack() : MidiClientRaw(), + m_jackClient( nullptr ), m_input_port( NULL ), m_output_port( NULL ), m_quit( false ) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index d5ff6461237..f17ef105fb5 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -56,6 +56,7 @@ SET(LMMS_SRCS gui/widgets/FadeButton.cpp gui/widgets/Fader.cpp gui/widgets/FxLine.cpp + gui/widgets/FxLineLcdSpinBox.cpp gui/widgets/Graph.cpp gui/widgets/GroupBox.cpp gui/widgets/InstrumentFunctionViews.cpp diff --git a/src/gui/EffectSelectDialog.cpp b/src/gui/EffectSelectDialog.cpp index 64b180d4817..79e40427b28 100644 --- a/src/gui/EffectSelectDialog.cpp +++ b/src/gui/EffectSelectDialog.cpp @@ -27,6 +27,7 @@ #include "ui_EffectSelectDialog.h" #include "gui_templates.h" +#include "DummyEffect.h" #include "embed.h" #include "PluginFactory.h" @@ -53,11 +54,6 @@ EffectSelectDialog::EffectSelectDialog( QWidget * _parent ) : if( desc->subPluginFeatures ) { desc->subPluginFeatures->listSubPluginKeys( - // as iterators are always stated to be not - // equal with pointers, we dereference the - // iterator and take the address of the item, - // so we're on the safe side and the compiler - // likely will reduce that to just "it" desc, subPluginEffectKeys ); } @@ -79,14 +75,14 @@ EffectSelectDialog::EffectSelectDialog( QWidget * _parent ) : { QString name; QString type; - if( ( *it ).desc->subPluginFeatures ) + if( it->desc->subPluginFeatures ) { - name = ( *it ).name; - type = ( *it ).desc->displayName; + name = it->displayName(); + type = it->desc->displayName; } else { - name = ( *it ).desc->displayName; + name = it->desc->displayName; type = "LMMS"; } m_sourceModel.setItem( row, 0, new QStandardItem( name ) ); @@ -147,12 +143,17 @@ EffectSelectDialog::~EffectSelectDialog() Effect * EffectSelectDialog::instantiateSelectedPlugin( EffectChain * _parent ) { - if( !m_currentSelection.name.isEmpty() && m_currentSelection.desc ) + Effect* result = nullptr; + if(!m_currentSelection.name.isEmpty() && m_currentSelection.desc) { - return Effect::instantiate( m_currentSelection.desc->name, - _parent, &m_currentSelection ); + result = Effect::instantiate(m_currentSelection.desc->name, + _parent, &m_currentSelection); } - return NULL; + if(!result) + { + result = new DummyEffect(_parent, QDomElement()); + } + return result; } @@ -184,62 +185,63 @@ void EffectSelectDialog::rowChanged( const QModelIndex & _idx, { m_currentSelection = m_effectKeys[m_model.mapToSource( _idx ).row()]; } - if( m_currentSelection.desc ) + if( m_currentSelection.desc ) { m_descriptionWidget = new QWidget; - QHBoxLayout *hbox = new QHBoxLayout( m_descriptionWidget ); - - Plugin::Descriptor const & descriptor = *( m_currentSelection.desc ); - - if ( descriptor.logo ) - { - QLabel *logoLabel = new QLabel( m_descriptionWidget ); - logoLabel->setPixmap( descriptor.logo->pixmap() ); - logoLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - - hbox->addWidget( logoLabel ); - hbox->setAlignment( logoLabel, Qt::AlignTop); - } - - QWidget *textualInfoWidget = new QWidget( m_descriptionWidget ); - - hbox->addWidget(textualInfoWidget); - - QVBoxLayout * textWidgetLayout = new QVBoxLayout( textualInfoWidget); - textWidgetLayout->setMargin( 4 ); - textWidgetLayout->setSpacing( 0 ); - - if ( m_currentSelection.desc->subPluginFeatures ) - { - QWidget *subWidget = new QWidget(textualInfoWidget); - QVBoxLayout * subLayout = new QVBoxLayout( subWidget ); - subLayout->setMargin( 4 ); - subLayout->setSpacing( 0 ); - m_currentSelection.desc->subPluginFeatures-> - fillDescriptionWidget( subWidget, &m_currentSelection ); - for( QWidget * w : subWidget->findChildren() ) - { - if( w->parent() == subWidget ) - { - subLayout->addWidget( w ); - } - } - - textWidgetLayout->addWidget(subWidget); - } - else - { - QLabel *label = new QLabel(m_descriptionWidget); - QString labelText = "

" + tr("Name") + ": " + QString::fromUtf8(descriptor.displayName) + "

"; - labelText += "

" + tr("Description") + ": " + qApp->translate( "pluginBrowser", descriptor.description ) + "

"; - labelText += "

" + tr("Author") + ": " + QString::fromUtf8(descriptor.author) + "

"; - - label->setText(labelText); - textWidgetLayout->addWidget(label); - } - - ui->scrollArea->setWidget( m_descriptionWidget ); + QHBoxLayout *hbox = new QHBoxLayout( m_descriptionWidget ); + + Plugin::Descriptor const & descriptor = *( m_currentSelection.desc ); + + const PixmapLoader* pixLoa = m_currentSelection.logo(); + if (pixLoa) + { + QLabel *logoLabel = new QLabel( m_descriptionWidget ); + logoLabel->setPixmap(pixLoa->pixmap()); + logoLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + + hbox->addWidget( logoLabel ); + hbox->setAlignment( logoLabel, Qt::AlignTop); + } + + QWidget *textualInfoWidget = new QWidget( m_descriptionWidget ); + + hbox->addWidget(textualInfoWidget); + + QVBoxLayout * textWidgetLayout = new QVBoxLayout( textualInfoWidget); + textWidgetLayout->setMargin( 4 ); + textWidgetLayout->setSpacing( 0 ); + + if ( m_currentSelection.desc->subPluginFeatures ) + { + QWidget *subWidget = new QWidget(textualInfoWidget); + QVBoxLayout * subLayout = new QVBoxLayout( subWidget ); + subLayout->setMargin( 4 ); + subLayout->setSpacing( 0 ); + m_currentSelection.desc->subPluginFeatures-> + fillDescriptionWidget( subWidget, &m_currentSelection ); + for( QWidget * w : subWidget->findChildren() ) + { + if( w->parent() == subWidget ) + { + subLayout->addWidget( w ); + } + } + + textWidgetLayout->addWidget(subWidget); + } + else + { + QLabel *label = new QLabel(m_descriptionWidget); + QString labelText = "

" + tr("Name") + ": " + QString::fromUtf8(descriptor.displayName) + "

"; + labelText += "

" + tr("Description") + ": " + qApp->translate( "pluginBrowser", descriptor.description ) + "

"; + labelText += "

" + tr("Author") + ": " + QString::fromUtf8(descriptor.author) + "

"; + + label->setText(labelText); + textWidgetLayout->addWidget(label); + } + + ui->scrollArea->setWidget( m_descriptionWidget ); m_descriptionWidget->show(); } } diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 05727d33295..4311e4e053b 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -180,7 +180,7 @@ void FileBrowser::reloadTree( void ) void FileBrowser::expandItems( QTreeWidgetItem * item ) { - int numChildren = item ? item->childCount() : m_fileBrowserTreeWidget->topLevelItemCount(); + int numChildren = item ? item->childCount() : m_fileBrowserTreeWidget->topLevelItemCount(); for( int i = 0; i < numChildren; ++i ) { QTreeWidgetItem * it = item ? item->child( i ) : m_fileBrowserTreeWidget->topLevelItem(i); @@ -241,7 +241,7 @@ void FileBrowser::addItems(const QString & path ) Directory *dd = new Directory( cur_file, path, m_filter ); m_fileBrowserTreeWidget->insertTopLevelItem( i,dd ); - dd->update(); + dd->update(); // add files to the directory orphan = false; break; } @@ -406,7 +406,7 @@ void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me ) delete tf; } else if( ( f->extension ()== "xiz" || f->extension() == "sf2" || f->extension() == "sf3" || f->extension() == "gig" || f->extension() == "pat" ) && - ! pluginFactory->pluginSupportingExtension(f->extension()).isNull() ) + ! pluginFactory->pluginSupportingExtension(f->extension()).info.isNull() ) { m_previewPlayHandle = new PresetPreviewPlayHandle( f->fullName(), f->handling() == FileItem::LoadByPlugin ); } @@ -549,8 +549,9 @@ void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it ) if( i == NULL || !i->descriptor()->supportsFileType( e ) ) { - i = it->loadInstrument( - pluginFactory->pluginSupportingExtension(e).name() ); + PluginFactory::PluginInfoAndKey piakn = + pluginFactory->pluginSupportingExtension(e); + i = it->loadInstrument(piakn.info.name(), &piakn.key); } i->loadFile( f->fullName() ); break; diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 440e37d1077..0a6a54866fe 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -47,6 +47,7 @@ #include "Mixer.h" #include "gui_templates.h" #include "InstrumentTrack.h" +#include "SampleTrack.h" #include "Song.h" #include "BBTrackContainer.h" @@ -251,6 +252,12 @@ void FxMixerView::updateMaxChannelSelector() inst->effectChannelModel()->setRange(0, m_fxChannelViews.size()-1,1); } + else if( trackList[i]->type() == Track::SampleTrack ) + { + SampleTrack * strk = (SampleTrack *) trackList[i]; + strk->effectChannelModel()->setRange(0, + m_fxChannelViews.size()-1,1); + } } } } diff --git a/src/gui/InstrumentView.cpp b/src/gui/InstrumentView.cpp index 9e8fc58c132..93e153f6f97 100644 --- a/src/gui/InstrumentView.cpp +++ b/src/gui/InstrumentView.cpp @@ -57,7 +57,7 @@ void InstrumentView::setModel( Model * _model, bool ) if( dynamic_cast( _model ) != NULL ) { ModelView::setModel( _model ); - instrumentTrackWindow()->setWindowIcon( model()->descriptor()->logo->pixmap() ); + instrumentTrackWindow()->setWindowIcon( model()->logo()->pixmap() ); connect( model(), SIGNAL( destroyed( QObject * ) ), this, SLOT( close() ) ); } } diff --git a/src/gui/PluginBrowser.cpp b/src/gui/PluginBrowser.cpp index dc0fc35e091..1e73b7cdcbf 100644 --- a/src/gui/PluginBrowser.cpp +++ b/src/gui/PluginBrowser.cpp @@ -24,13 +24,16 @@ #include "PluginBrowser.h" +#include #include -#include +#include #include -#include +#include #include +#include #include "embed.h" +#include "Engine.h" #include "templates.h" #include "gui_templates.h" #include "StringPairDrag.h" @@ -59,23 +62,91 @@ PluginBrowser::PluginBrowser( QWidget * _parent ) : m_view ); hint->setWordWrap( true ); - QScrollArea* scrollarea = new QScrollArea( m_view ); - PluginDescList* descList = new PluginDescList( m_view ); - scrollarea->setWidget(descList); - scrollarea->setWidgetResizable(true); + QLineEdit * searchBar = new QLineEdit( m_view ); + searchBar->setPlaceholderText( "Search" ); + searchBar->setMaxLength( 64 ); + searchBar->setClearButtonEnabled( true ); + + m_descTree = new QTreeWidget( m_view ); + m_descTree->setColumnCount( 1 ); + m_descTree->header()->setVisible( false ); + m_descTree->setIndentation( 10 ); + m_descTree->setSelectionMode( QAbstractItemView::NoSelection ); + + connect( searchBar, SIGNAL( textEdited( const QString & ) ), + this, SLOT( onFilterChanged( const QString & ) ) ); + + view_layout->addWidget( hint ); + view_layout->addWidget( searchBar ); + view_layout->addWidget( m_descTree ); - view_layout->addWidget(hint); - view_layout->addWidget(scrollarea); + // Add LMMS root to the tree + m_lmmsRoot = new QTreeWidgetItem(); + m_lmmsRoot->setText( 0, "LMMS" ); + m_descTree->insertTopLevelItem( 0, m_lmmsRoot ); + m_lmmsRoot->setExpanded( true ); + + // Add LV2 root to the tree + m_lv2Root = new QTreeWidgetItem(); + m_lv2Root->setText( 0, "LV2" ); + m_descTree->insertTopLevelItem( 1, m_lv2Root ); + + // Add plugins to the tree roots + addPlugins(); + + // Resize + m_descTree->header()->setSectionResizeMode( QHeaderView::ResizeToContents ); + + // Hide empty roots + updateRootVisibilities(); } +void PluginBrowser::updateRootVisibility( int rootIndex ) +{ + QTreeWidgetItem * root = m_descTree->topLevelItem( rootIndex ); + root->setHidden( !root->childCount() ); +} -PluginDescList::PluginDescList(QWidget *parent) : - QWidget(parent) +void PluginBrowser::updateRootVisibilities() { - QVBoxLayout* layout = new QVBoxLayout(this); + int rootCount = m_descTree->topLevelItemCount(); + for (int rootIndex = 0; rootIndex < rootCount; ++rootIndex) + { + updateRootVisibility( rootIndex ); + } +} + +void PluginBrowser::onFilterChanged( const QString & filter ) +{ + int rootCount = m_descTree->topLevelItemCount(); + for (int rootIndex = 0; rootIndex < rootCount; ++rootIndex) + { + QTreeWidgetItem * root = m_descTree->topLevelItem( rootIndex ); + + int itemCount = root->childCount(); + for (int itemIndex = 0; itemIndex < itemCount; ++itemIndex) + { + QTreeWidgetItem * item = root->child( itemIndex ); + PluginDescWidget * descWidget = static_cast + (m_descTree->itemWidget( item, 0)); + if (descWidget->name().contains(filter, Qt::CaseInsensitive)) + { + item->setHidden( false ); + } + else + { + item->setHidden( true ); + } + } + } +} + + +void PluginBrowser::addPlugins() +{ QList descs = pluginFactory->descriptors(Plugin::Instrument); std::sort( descs.begin(), @@ -85,37 +156,71 @@ PluginDescList::PluginDescList(QWidget *parent) : return qstricmp( d1->displayName, d2->displayName ) < 0 ? true : false; } ); - for (const Plugin::Descriptor* desc : descs) + + typedef Plugin::Descriptor::SubPluginFeatures::KeyList PluginKeyList; + typedef Plugin::Descriptor::SubPluginFeatures::Key PluginKey; + PluginKeyList subPluginKeys, pluginKeys; + + for (const Plugin::Descriptor* desc: descs) { - PluginDescWidget* p = new PluginDescWidget( *desc, this ); - p->show(); - layout->addWidget(p); + if ( desc->subPluginFeatures ) + { + desc->subPluginFeatures->listSubPluginKeys( + desc, + subPluginKeys ); + } + else + { + pluginKeys << PluginKey( desc, desc->name ); + } } - setLayout(layout); - layout->addStretch(); + pluginKeys += subPluginKeys; + + for (const PluginKey& key : pluginKeys) + { + QTreeWidgetItem * item = new QTreeWidgetItem(); + if ( key.desc->name == QStringLiteral("lv2instrument") ) + { + m_lv2Root->addChild( item ); + } + else + { + m_lmmsRoot->addChild( item ); + } + PluginDescWidget* p = new PluginDescWidget( key, m_descTree ); + m_descTree->setItemWidget( item, 0, p ); + } } -PluginDescWidget::PluginDescWidget( const Plugin::Descriptor & _pd, +PluginDescWidget::PluginDescWidget(const PluginKey &_pk, QWidget * _parent ) : QWidget( _parent ), - m_pluginDescriptor( _pd ), - m_logo( _pd.logo->pixmap() ), + m_pluginKey( _pk ), + m_logo( _pk.logo()->pixmap() ), m_mouseOver( false ) { setFixedHeight( DEFAULT_HEIGHT ); setMouseTracking( true ); setCursor( Qt::PointingHandCursor ); - setToolTip(_pd.description); + setToolTip(_pk.description()); +} + + + + +QString PluginDescWidget::name() const +{ + return m_pluginKey.displayName(); } -void PluginDescWidget::paintEvent( QPaintEvent * e ) +void PluginDescWidget::paintEvent( QPaintEvent * ) { QPainter p( this ); @@ -140,8 +245,7 @@ void PluginDescWidget::paintEvent( QPaintEvent * e ) } p.setFont( f ); - p.drawText( 10 + logo_size.width(), 15, - m_pluginDescriptor.displayName ); + p.drawText( 10 + logo_size.width(), 15, m_pluginKey.displayName()); } @@ -169,10 +273,11 @@ void PluginDescWidget::leaveEvent( QEvent * _e ) void PluginDescWidget::mousePressEvent( QMouseEvent * _me ) { - if( _me->button() == Qt::LeftButton ) + if ( _me->button() == Qt::LeftButton ) { - new StringPairDrag( "instrument", m_pluginDescriptor.name, - m_logo, this ); + Engine::setDndPluginKey(&m_pluginKey); + new StringPairDrag("instrument", + QString::fromUtf8(m_pluginKey.desc->name), m_logo, this); leaveEvent( _me ); } } diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 38db2287c02..5ab9630b2e1 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -139,7 +139,9 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : "displaywaveform").toInt() ), m_disableAutoQuit(ConfigManager::inst()->value( "ui", "disableautoquit", "1" ).toInt() ), - m_vstEmbedMethod( ConfigManager::inst()->vstEmbedMethod() ) + m_vstEmbedMethod( ConfigManager::inst()->vstEmbedMethod() ), + m_vstAlwaysOnTop( ConfigManager::inst()->value( "ui", + "vstalwaysontop" ).toInt() ) { setWindowIcon( embed::getIconPixmap( "setup_general" ) ); setWindowTitle( tr( "Setup LMMS" ) ); @@ -259,7 +261,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : } TabWidget* embed_tw = new TabWidget( tr( "PLUGIN EMBEDDING" ), general); - embed_tw->setFixedHeight( 48 ); + embed_tw->setFixedHeight( 66 ); m_vstEmbedComboBox = new QComboBox( embed_tw ); m_vstEmbedComboBox->move( XDelta, YDelta ); @@ -278,6 +280,17 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : m_vstEmbedComboBox->addItem( tr( "Embed using XEmbed protocol" ), "xembed" ); } m_vstEmbedComboBox->setCurrentIndex( m_vstEmbedComboBox->findData( m_vstEmbedMethod ) ); + connect( m_vstEmbedComboBox, SIGNAL( currentIndexChanged( int ) ), + this, SLOT( vstEmbedMethodChanged() ) ); + + m_vstAlwaysOnTopCheckBox = new LedCheckBox( + tr( "Keep plugin windows on top when not embedded" ), + embed_tw ); + m_vstAlwaysOnTopCheckBox->move( 20, 44 ); + m_vstAlwaysOnTopCheckBox->setChecked( m_vstAlwaysOnTop ); + m_vstAlwaysOnTopCheckBox->setVisible( m_vstEmbedMethod == "none" ); + connect( m_vstAlwaysOnTopCheckBox, SIGNAL( toggled( bool ) ), + this, SLOT( toggleVSTAlwaysOnTop( bool ) ) ); TabWidget * lang_tw = new TabWidget( tr( "LANGUAGE" ), general ); lang_tw->setFixedHeight( 48 ); @@ -427,7 +440,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : #endif addPathEntry("Themes directory", m_artworkDir, SLOT(setArtworkDir(const QString &)), - SLOT(openArtwortDir()), + SLOT(openArtworkDir()), m_adLineEdit, pathSelectors); pathSelectorLayout->addStretch(); addPathEntry("Background artwork", m_backgroundArtwork, @@ -854,7 +867,9 @@ void SetupDialog::accept() QString::number( m_disableAutoQuit ) ); ConfigManager::inst()->setValue( "app", "language", m_lang ); ConfigManager::inst()->setValue( "ui", "vstembedmethod", - m_vstEmbedComboBox->currentData().toString() ); + m_vstEmbedMethod ); + ConfigManager::inst()->setValue( "ui", "vstalwaysontop", + QString::number( m_vstAlwaysOnTop ) ); ConfigManager::inst()->setWorkingDir(QDir::fromNativeSeparators(m_workingDir)); @@ -1057,6 +1072,20 @@ void SetupDialog::toggleOneInstrumentTrackWindow( bool _enabled ) m_oneInstrumentTrackWindow = _enabled; } + +void SetupDialog::vstEmbedMethodChanged() +{ + m_vstEmbedMethod = m_vstEmbedComboBox->currentData().toString(); + m_vstAlwaysOnTopCheckBox->setVisible( m_vstEmbedMethod == "none" ); +} + + +void SetupDialog::toggleVSTAlwaysOnTop( bool en ) +{ + m_vstAlwaysOnTop = en; +} + + void SetupDialog::setLanguage( int lang ) { m_lang = m_languages[lang]; diff --git a/src/gui/TrackContainerView.cpp b/src/gui/TrackContainerView.cpp index 2772ef104d0..7c69d5eb824 100644 --- a/src/gui/TrackContainerView.cpp +++ b/src/gui/TrackContainerView.cpp @@ -384,8 +384,9 @@ void TrackContainerView::dropEvent( QDropEvent * _de ) InstrumentTrack * it = dynamic_cast( Track::create( Track::InstrumentTrack, m_tc ) ); - Instrument * i = it->loadInstrument( - pluginFactory->pluginSupportingExtension(FileItem::extension(value)).name()); + PluginFactory::PluginInfoAndKey piakn = + pluginFactory->pluginSupportingExtension(FileItem::extension(value)); + Instrument * i = it->loadInstrument(piakn.info.name(), &piakn.key); i->loadFile( value ); //it->toggledInstrumentTrackButton( true ); _de->accept(); @@ -529,7 +530,8 @@ InstrumentLoaderThread::InstrumentLoaderThread( QObject *parent, InstrumentTrack void InstrumentLoaderThread::run() { - Instrument *i = m_it->loadInstrument( m_name ); + Instrument *i = m_it->loadInstrument(m_name, nullptr, + true /*always DnD*/); QObject *parent = i->parent(); i->setParent( 0 ); i->moveToThread( m_containerThread ); diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index e00c107d1dc..7a371bcf7b7 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -405,6 +405,7 @@ void AutomationEditor::keyPressEvent(QKeyEvent * ke ) } break; + case Qt::Key_Backspace: case Qt::Key_Delete: deleteSelectedValues(); ke->accept(); @@ -2487,6 +2488,9 @@ void AutomationEditorWindow::dropEvent( QDropEvent *_de ) void AutomationEditorWindow::dragEnterEvent( QDragEnterEvent *_dee ) { + if (! m_editor->validPattern() ) { + return; + } StringPairDrag::processDragEnterEvent( _dee, "automatable_model" ); } diff --git a/src/gui/editors/Editor.cpp b/src/gui/editors/Editor.cpp index b82453acf03..c27eda4c06c 100644 --- a/src/gui/editors/Editor.cpp +++ b/src/gui/editors/Editor.cpp @@ -32,6 +32,7 @@ #include #include #include +#include void Editor::setPauseIcon(bool displayPauseIcon) @@ -128,8 +129,18 @@ QAction *Editor::playAction() const return m_playAction; } - - +void Editor::closeEvent( QCloseEvent * _ce ) +{ + if( parentWidget() ) + { + parentWidget()->hide(); + } + else + { + hide(); + } + _ce->ignore(); + } DropToolBar::DropToolBar(QWidget* parent) : QToolBar(parent) { @@ -145,3 +156,6 @@ void DropToolBar::dropEvent(QDropEvent* event) { dropped(event); } + + + diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index bf65444f290..76b2d04f153 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -1319,6 +1319,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke) clearSelectedNotes(); break; + case Qt::Key_Backspace: case Qt::Key_Delete: deleteSelectedNotes(); ke->accept(); diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 3e57ba026e1..b397434b102 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -304,12 +304,12 @@ void SongEditor::setEditModeSelect() void SongEditor::keyPressEvent( QKeyEvent * ke ) { if( ke->modifiers() & Qt::ShiftModifier && - ke->key() == Qt::Key_Insert ) + ( ke->key() == Qt::Key_Insert || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return ) ) { m_song->insertBar(); } else if( ke->modifiers() & Qt::ShiftModifier && - ke->key() == Qt::Key_Delete ) + ( ke->key() == Qt::Key_Delete || ke->key() == Qt::Key_Backspace ) ) { m_song->removeBar(); } diff --git a/src/gui/widgets/ComboBox.cpp b/src/gui/widgets/ComboBox.cpp index 0673ee32d4b..c796bfa74c8 100644 --- a/src/gui/widgets/ComboBox.cpp +++ b/src/gui/widgets/ComboBox.cpp @@ -42,7 +42,7 @@ QPixmap * ComboBox::s_background = NULL; QPixmap * ComboBox::s_arrow = NULL; QPixmap * ComboBox::s_arrowSelected = NULL; -const int CB_ARROW_BTN_WIDTH = 20; +const int CB_ARROW_BTN_WIDTH = 18; ComboBox::ComboBox( QWidget * _parent, const QString & _name ) : @@ -198,7 +198,7 @@ void ComboBox::paintEvent( QPaintEvent * _pe ) QPixmap * arrow = m_pressed ? s_arrowSelected : s_arrow; - p.drawPixmap( width() - CB_ARROW_BTN_WIDTH + 5, 4, *arrow ); + p.drawPixmap( width() - CB_ARROW_BTN_WIDTH + 3, 4, *arrow ); if( model() && model()->size() > 0 ) { @@ -251,4 +251,3 @@ void ComboBox::setItem( QAction* item ) - diff --git a/src/gui/widgets/FxLineLcdSpinBox.cpp b/src/gui/widgets/FxLineLcdSpinBox.cpp new file mode 100644 index 00000000000..bfe4a9637f9 --- /dev/null +++ b/src/gui/widgets/FxLineLcdSpinBox.cpp @@ -0,0 +1,66 @@ +/* + * FxLineLcdSpinBox.cpp - a specialization of LcdSpnBox for setting FX channels + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 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 "FxLineLcdSpinBox.h" + +#include "CaptionMenu.h" +#include "FxMixerView.h" +#include "GuiApplication.h" +#include "Track.h" + +void FxLineLcdSpinBox::setTrackView(TrackView * tv) +{ + m_tv = tv; +} + +void FxLineLcdSpinBox::mouseDoubleClickEvent(QMouseEvent* event) +{ + gui->fxMixerView()->setCurrentFxLine(model()->value()); + + gui->fxMixerView()->parentWidget()->show(); + gui->fxMixerView()->show();// show fxMixer window + gui->fxMixerView()->setFocus();// set focus to fxMixer window + //engine::getFxMixerView()->raise(); +} + +void FxLineLcdSpinBox::contextMenuEvent(QContextMenuEvent* event) +{ + // for the case, the user clicked right while pressing left mouse- + // button, the context-menu appears while mouse-cursor is still hidden + // and it isn't shown again until user does something which causes + // an QApplication::restoreOverrideCursor()-call... + mouseReleaseEvent(nullptr); + + QPointer contextMenu = new CaptionMenu(model()->displayName(), this); + + if (QMenu *fxMenu = m_tv->createFxMenu( + tr("Assign to:"), tr("New FX Channel"))) + { + contextMenu->addMenu(fxMenu); + + contextMenu->addSeparator(); + } + addDefaultActions(contextMenu); + contextMenu->exec(QCursor::pos()); +} diff --git a/src/gui/widgets/TrackLabelButton.cpp b/src/gui/widgets/TrackLabelButton.cpp index db310a05ec6..361db740e63 100644 --- a/src/gui/widgets/TrackLabelButton.cpp +++ b/src/gui/widgets/TrackLabelButton.cpp @@ -195,9 +195,15 @@ void TrackLabelButton::paintEvent( QPaintEvent * _pe ) InstrumentTrack * it = dynamic_cast( m_trackView->getTrack() ); const PixmapLoader * pl; + auto get_logo = [](InstrumentTrack* it) -> const PixmapLoader* + { + return it->instrument()->key().isValid() + ? it->instrument()->key().logo() + : it->instrument()->descriptor()->logo; + }; if( it && it->instrument() && it->instrument()->descriptor() && - ( pl = it->instrument()->descriptor()->logo ) ) + ( pl = get_logo(it) ) ) { if( pl->pixmapName() != m_iconName ) { diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index a927e8aadf1..0142952cfff 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -47,6 +47,7 @@ #include "EffectRackView.h" #include "embed.h" #include "FileBrowser.h" +#include "FxLineLcdSpinBox.h" #include "FxMixer.h" #include "FxMixerView.h" #include "GuiApplication.h" @@ -739,7 +740,10 @@ void InstrumentTrack::saveTrackSpecificSettings( QDomDocument& doc, QDomElement { QDomElement i = doc.createElement( "instrument" ); i.setAttribute( "name", m_instrument->descriptor()->name ); - m_instrument->saveState( doc, i ); + QDomElement ins = m_instrument->saveState( doc, i ); + if(m_instrument->key().isValid()) { + ins.appendChild( m_instrument->key().saveXML( doc ) ); + } thisElement.appendChild( i ); } m_soundShaping.saveState( doc, thisElement ); @@ -800,9 +804,13 @@ void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement } else if( node.nodeName() == "instrument" ) { + typedef Plugin::Descriptor::SubPluginFeatures::Key PluginKey; + PluginKey key( node.toElement().elementsByTagName( "key" ).item( 0 ).toElement() ); + delete m_instrument; m_instrument = NULL; - m_instrument = Instrument::instantiate( node.toElement().attribute( "name" ), this ); + m_instrument = Instrument::instantiate( + node.toElement().attribute( "name" ), this, &key); m_instrument->restoreState( node.firstChildElement() ); emit instrumentChanged(); @@ -816,7 +824,8 @@ void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement { delete m_instrument; m_instrument = NULL; - m_instrument = Instrument::instantiate( node.nodeName(), this ); + m_instrument = Instrument::instantiate( + node.nodeName(), this, nullptr, true); if( m_instrument->nodeName() == node.nodeName() ) { m_instrument->restoreState( node.toElement() ); @@ -841,15 +850,20 @@ void InstrumentTrack::setPreviewMode( const bool value ) -Instrument * InstrumentTrack::loadInstrument( const QString & _plugin_name ) +Instrument * InstrumentTrack::loadInstrument(const QString & _plugin_name, + const Plugin::Descriptor::SubPluginFeatures::Key *key, bool keyFromDnd) { + if(keyFromDnd) + Q_ASSERT(!key); + silenceAllNotes( true ); lock(); delete m_instrument; - m_instrument = Instrument::instantiate( _plugin_name, this ); + m_instrument = Instrument::instantiate(_plugin_name, this, + key, keyFromDnd); unlock(); - setName( m_instrument->displayName() ); + setName(m_instrument->displayName()); emit instrumentChanged(); @@ -1249,52 +1263,6 @@ QMenu * InstrumentTrackView::createFxMenu(QString title, QString newFxLabel) -class fxLineLcdSpinBox : public LcdSpinBox -{ - Q_OBJECT - public: - fxLineLcdSpinBox( int _num_digits, QWidget * _parent, - const QString & _name ) : - LcdSpinBox( _num_digits, _parent, _name ) {} - - protected: - virtual void mouseDoubleClickEvent ( QMouseEvent * _me ) - { - gui->fxMixerView()->setCurrentFxLine( model()->value() ); - - gui->fxMixerView()->parentWidget()->show(); - gui->fxMixerView()->show();// show fxMixer window - gui->fxMixerView()->setFocus();// set focus to fxMixer window - //engine::getFxMixerView()->raise(); - } - - virtual void contextMenuEvent( QContextMenuEvent* event ) - { - // for the case, the user clicked right while pressing left mouse- - // button, the context-menu appears while mouse-cursor is still hidden - // and it isn't shown again until user does something which causes - // an QApplication::restoreOverrideCursor()-call... - mouseReleaseEvent( NULL ); - - QPointer contextMenu = new CaptionMenu( model()->displayName(), this ); - - // This condition is here just as a safety check, fxLineLcdSpinBox is aways - // created inside a TabWidget inside an InstrumentTrackWindow - if ( InstrumentTrackWindow* window = dynamic_cast( (QWidget *)this->parent()->parent() ) ) - { - QMenu *fxMenu = window->instrumentTrackView()->createFxMenu( tr( "Assign to:" ), tr( "New FX channel" ) ); - contextMenu->addMenu( fxMenu ); - - contextMenu->addSeparator(); - } - addDefaultActions( contextMenu ); - contextMenu->exec( QCursor::pos() ); - } - -}; - - - // #### ITW: InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : QWidget(), @@ -1415,7 +1383,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : // setup spinbox for selecting FX-channel - m_effectChannelNumber = new fxLineLcdSpinBox( 2, NULL, tr( "FX channel" ) ); + m_effectChannelNumber = new FxLineLcdSpinBox( 2, NULL, tr( "FX channel" ), m_itv ); basicControlsLayout->addWidget( m_effectChannelNumber, 0, 6 ); basicControlsLayout->setAlignment( m_effectChannelNumber, widgetAlignment ); @@ -1536,6 +1504,7 @@ void InstrumentTrackWindow::setInstrumentTrackView( InstrumentTrackView* view ) } m_itv = view; + m_effectChannelNumber->setTrackView(m_itv); } @@ -1654,6 +1623,8 @@ void InstrumentTrackWindow::updateInstrumentView() modelChanged(); // Get the instrument window to refresh m_track->dataChanged(); // Get the text on the trackButton to change + + m_pianoView->setVisible(m_track->m_instrument->hasNoteInput()); } } @@ -1708,7 +1679,9 @@ void InstrumentTrackWindow::closeEvent( QCloseEvent* event ) void InstrumentTrackWindow::focusInEvent( QFocusEvent* ) { - m_pianoView->setFocus(); + if(m_pianoView->isVisible()) { + m_pianoView->setFocus(); + } } @@ -1737,7 +1710,7 @@ void InstrumentTrackWindow::dropEvent( QDropEvent* event ) if( type == "instrument" ) { - m_track->loadInstrument( value ); + m_track->loadInstrument( value, nullptr, true /* DnD */ ); Engine::getSong()->setModified(); @@ -1763,7 +1736,9 @@ void InstrumentTrackWindow::dropEvent( QDropEvent* event ) if( !i->descriptor()->supportsFileType( ext ) ) { - i = m_track->loadInstrument( pluginFactory->pluginSupportingExtension(ext).name() ); + PluginFactory::PluginInfoAndKey piakn = + pluginFactory->pluginSupportingExtension(ext); + i = m_track->loadInstrument(piakn.info.name(), &piakn.key); } i->loadFile( value ); @@ -1840,6 +1815,9 @@ void InstrumentTrackWindow::viewInstrumentInDirection(int d) // scroll the SongEditor/BB-editor to make sure the new trackview label is visible bringToFront->trackContainerView()->scrollToTrackView(bringToFront); + + // get the instrument window to refresh + modelChanged(); } bringToFront->getInstrumentTrackWindow()->setFocus(); } diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 8fa6fd50fdd..c965af140f2 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,8 @@ #include "MainWindow.h" #include "Mixer.h" #include "EffectRackView.h" +#include "FxMixerView.h" +#include "TabWidget.h" #include "TrackLabelButton.h" SampleTCO::SampleTCO( Track * _track ) : @@ -471,7 +474,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) bool muted = m_tco->getTrack()->isMuted() || m_tco->isMuted(); // state: selected, muted, normal - c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor() + c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor() : painter.background().color() ); lingrad.setColorAt( 1, c.darker( 300 ) ); @@ -515,7 +518,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) // inner border p.setPen( c.lighter( 160 ) ); - p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH, + p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH, rect().bottom() - TCO_BORDER_WIDTH ); // outer border @@ -531,7 +534,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) embed::getIconPixmap( "muted", size, size ) ); } - // recording sample tracks is not possible at the moment + // recording sample tracks is not possible at the moment /* if( m_tco->isRecord() ) { @@ -562,10 +565,14 @@ SampleTrack::SampleTrack( TrackContainer* tc ) : tr( "Volume" ) ), m_panningModel( DefaultPanning, PanningLeft, PanningRight, 0.1f, this, tr( "Panning" ) ), + m_effectChannelModel( 0, 0, 0, this, tr( "FX channel" ) ), m_audioPort( tr( "Sample track" ), true, &m_volumeModel, &m_panningModel, &m_mutedModel ) { setName( tr( "Sample track" ) ); m_panningModel.setCenterValue( DefaultPanning ); + m_effectChannelModel.setRange( 0, Engine::fxMixer()->numChannels()-1, 1); + + connect( &m_effectChannelModel, SIGNAL( dataChanged() ), this, SLOT( updateEffectChannel() ) ); } @@ -693,6 +700,7 @@ void SampleTrack::saveTrackSpecificSettings( QDomDocument & _doc, #endif m_volumeModel.saveSettings( _doc, _this, "vol" ); m_panningModel.saveSettings( _doc, _this, "pan" ); + m_effectChannelModel.saveSettings( _doc, _this, "fxch" ); } @@ -715,6 +723,8 @@ void SampleTrack::loadTrackSpecificSettings( const QDomElement & _this ) } m_volumeModel.loadSettings( _this, "vol" ); m_panningModel.loadSettings( _this, "pan" ); + m_effectChannelModel.setRange( 0, Engine::fxMixer()->numChannels() - 1 ); + m_effectChannelModel.loadSettings( _this, "fxch" ); } @@ -742,6 +752,14 @@ void SampleTrack::setPlayingTcos( bool isPlaying ) +void SampleTrack::updateEffectChannel() +{ + m_audioPort.setNextFxChannel( m_effectChannelModel.value() ); +} + + + + SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : @@ -749,13 +767,13 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : { setFixedHeight( 32 ); - TrackLabelButton * tlb = new TrackLabelButton( this, - getTrackSettingsWidget() ); - connect( tlb, SIGNAL( clicked( bool ) ), - this, SLOT( showEffects() ) ); - tlb->setIcon( embed::getIconPixmap( "sample_track" ) ); - tlb->move( 3, 1 ); - tlb->show(); + m_tlb = new TrackLabelButton(this, getTrackSettingsWidget()); + m_tlb->setCheckable(true); + connect(m_tlb, SIGNAL(clicked( bool )), + this, SLOT(showEffects())); + m_tlb->setIcon(embed::getIconPixmap("sample_track")); + m_tlb->move(3, 1); + m_tlb->show(); m_volumeKnob = new Knob( knobSmall_17, getTrackSettingsWidget(), tr( "Track volume" ) ); @@ -779,16 +797,10 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : m_panningKnob->setLabel( tr( "PAN" ) ); m_panningKnob->show(); - m_effectRack = new EffectRackView( _t->audioPort()->effects() ); - m_effectRack->setFixedSize( 240, 242 ); - - m_effWindow = gui->mainWindow()->addWindowedWidget( m_effectRack ); - m_effWindow->setAttribute( Qt::WA_DeleteOnClose, false ); - m_effWindow->layout()->setSizeConstraint( QLayout::SetFixedSize ); - m_effWindow->setWindowTitle( _t->name() ); - m_effWindow->hide(); - setModel( _t ); + + m_window = new SampleTrackWindow(this); + m_window->toggleVisibility(false); } @@ -796,24 +808,58 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : SampleTrackView::~SampleTrackView() { - m_effWindow->deleteLater(); + if(m_window != NULL) + { + m_window->setSampleTrackView(NULL); + m_window->parentWidget()->hide(); + } + m_window = NULL; } - -void SampleTrackView::showEffects() +QMenu * SampleTrackView::createFxMenu(QString title, QString newFxLabel) { - if( m_effWindow->isHidden() ) + int channelIndex = model()->effectChannelModel()->value(); + + FxChannel *fxChannel = Engine::fxMixer()->effectChannel(channelIndex); + + // If title allows interpolation, pass channel index and name + if (title.contains("%2")) { - m_effectRack->show(); - m_effWindow->show(); - m_effWindow->raise(); + title = title.arg(channelIndex).arg(fxChannel->m_name); } - else + + QMenu *fxMenu = new QMenu(title); + + QSignalMapper * fxMenuSignalMapper = new QSignalMapper(fxMenu); + + fxMenu->addAction(newFxLabel, this, SLOT(createFxLine())); + fxMenu->addSeparator(); + + for (int i = 0; i < Engine::fxMixer()->numChannels(); ++i) { - m_effWindow->hide(); + FxChannel * currentChannel = Engine::fxMixer()->effectChannel(i); + + if (currentChannel != fxChannel) + { + QString label = tr("FX %1: %2").arg(currentChannel->m_channelIndex).arg(currentChannel->m_name); + QAction * action = fxMenu->addAction(label, fxMenuSignalMapper, SLOT(map())); + fxMenuSignalMapper->setMapping(action, currentChannel->m_channelIndex); + } } + + connect(fxMenuSignalMapper, SIGNAL(mapped(int)), this, SLOT(assignFxLine(int))); + + return fxMenu; +} + + + + +void SampleTrackView::showEffects() +{ + m_window->toggleVisibility(m_window->parentWidget()->isHidden()); } @@ -821,7 +867,261 @@ void SampleTrackView::showEffects() void SampleTrackView::modelChanged() { SampleTrack * st = castModel(); - m_volumeKnob->setModel( &st->m_volumeModel ); + m_volumeKnob->setModel(&st->m_volumeModel); TrackView::modelChanged(); } + + + +SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) : + QWidget(), + ModelView(NULL, this), + m_track(tv->model()), + m_stv(tv) +{ + // init own layout + widgets + setFocusPolicy(Qt::StrongFocus); + QVBoxLayout * vlayout = new QVBoxLayout(this); + vlayout->setMargin(0); + vlayout->setSpacing(0); + + TabWidget* generalSettingsWidget = new TabWidget(tr("GENERAL SETTINGS"), this); + + QVBoxLayout* generalSettingsLayout = new QVBoxLayout(generalSettingsWidget); + + generalSettingsLayout->setContentsMargins(8, 18, 8, 8); + generalSettingsLayout->setSpacing(6); + + QWidget* nameWidget = new QWidget(generalSettingsWidget); + QHBoxLayout* nameLayout = new QHBoxLayout(nameWidget); + nameLayout->setContentsMargins(0, 0, 0, 0); + nameLayout->setSpacing(2); + + // setup line edit for changing sample track name + m_nameLineEdit = new QLineEdit; + m_nameLineEdit->setFont(pointSize<9>(m_nameLineEdit->font())); + connect(m_nameLineEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(textChanged(const QString &))); + + m_nameLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); + nameLayout->addWidget(m_nameLineEdit); + + + generalSettingsLayout->addWidget(nameWidget); + + + QGridLayout* basicControlsLayout = new QGridLayout; + basicControlsLayout->setHorizontalSpacing(3); + basicControlsLayout->setVerticalSpacing(0); + basicControlsLayout->setContentsMargins(0, 0, 0, 0); + + QString labelStyleSheet = "font-size: 6pt;"; + Qt::Alignment labelAlignment = Qt::AlignHCenter | Qt::AlignTop; + Qt::Alignment widgetAlignment = Qt::AlignHCenter | Qt::AlignCenter; + + // set up volume knob + m_volumeKnob = new Knob(knobBright_26, NULL, tr("Sample volume")); + m_volumeKnob->setVolumeKnob(true); + m_volumeKnob->setHintText(tr("Volume:"), "%"); + + basicControlsLayout->addWidget(m_volumeKnob, 0, 0); + basicControlsLayout->setAlignment(m_volumeKnob, widgetAlignment); + + QLabel *label = new QLabel(tr("VOL"), this); + label->setStyleSheet(labelStyleSheet); + basicControlsLayout->addWidget(label, 1, 0); + basicControlsLayout->setAlignment(label, labelAlignment); + + + // set up panning knob + m_panningKnob = new Knob(knobBright_26, NULL, tr("Panning")); + m_panningKnob->setHintText(tr("Panning:"), ""); + + basicControlsLayout->addWidget(m_panningKnob, 0, 1); + basicControlsLayout->setAlignment(m_panningKnob, widgetAlignment); + + label = new QLabel(tr("PAN"),this); + label->setStyleSheet(labelStyleSheet); + basicControlsLayout->addWidget(label, 1, 1); + basicControlsLayout->setAlignment(label, labelAlignment); + + + basicControlsLayout->setColumnStretch(2, 1); + + + // setup spinbox for selecting FX-channel + m_effectChannelNumber = new FxLineLcdSpinBox(2, NULL, tr("FX channel"), m_stv); + + basicControlsLayout->addWidget(m_effectChannelNumber, 0, 3); + basicControlsLayout->setAlignment(m_effectChannelNumber, widgetAlignment); + + label = new QLabel(tr("FX"), this); + label->setStyleSheet(labelStyleSheet); + basicControlsLayout->addWidget(label, 1, 3); + basicControlsLayout->setAlignment(label, labelAlignment); + + generalSettingsLayout->addLayout(basicControlsLayout); + + m_effectRack = new EffectRackView(tv->model()->audioPort()->effects()); + m_effectRack->setFixedSize(240, 242); + + vlayout->addWidget(generalSettingsWidget); + vlayout->addWidget(m_effectRack); + + + setModel(tv->model()); + + QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget(this); + Qt::WindowFlags flags = subWin->windowFlags(); + flags |= Qt::MSWindowsFixedSizeDialogHint; + flags &= ~Qt::WindowMaximizeButtonHint; + subWin->setWindowFlags(flags); + + // Hide the Size and Maximize options from the system menu + // since the dialog size is fixed. + QMenu * systemMenu = subWin->systemMenu(); + systemMenu->actions().at(2)->setVisible(false); // Size + systemMenu->actions().at(4)->setVisible(false); // Maximize + + subWin->setWindowIcon(embed::getIconPixmap("sample_track")); + subWin->setFixedSize(subWin->size()); + subWin->hide(); +} + + + +SampleTrackWindow::~SampleTrackWindow() +{ +} + + + +void SampleTrackWindow::setSampleTrackView(SampleTrackView* tv) +{ + if(m_stv && tv) + { + m_stv->m_tlb->setChecked(false); + } + + m_stv = tv; +} + + + +void SampleTrackWindow::modelChanged() +{ + m_track = castModel(); + + m_nameLineEdit->setText(m_track->name()); + + m_track->disconnect(SIGNAL(nameChanged()), this); + + connect(m_track, SIGNAL(nameChanged()), + this, SLOT(updateName())); + + m_volumeKnob->setModel(&m_track->m_volumeModel); + m_panningKnob->setModel(&m_track->m_panningModel); + m_effectChannelNumber->setModel(&m_track->m_effectChannelModel); + + updateName(); +} + + + +/*! \brief Create and assign a new FX Channel for this track */ +void SampleTrackView::createFxLine() +{ + int channelIndex = gui->fxMixerView()->addNewChannel(); + + Engine::fxMixer()->effectChannel(channelIndex)->m_name = getTrack()->name(); + + assignFxLine(channelIndex); +} + + + + +/*! \brief Assign a specific FX Channel for this track */ +void SampleTrackView::assignFxLine(int channelIndex) +{ + model()->effectChannelModel()->setValue(channelIndex); + + gui->fxMixerView()->setCurrentFxLine(channelIndex); +} + + + +void SampleTrackWindow::updateName() +{ + setWindowTitle(m_track->name().length() > 25 ? (m_track->name().left(24) + "...") : m_track->name()); + + if(m_nameLineEdit->text() != m_track->name()) + { + m_nameLineEdit->setText(m_track->name()); + } +} + + + +void SampleTrackWindow::textChanged(const QString& new_name) +{ + m_track->setName(new_name); + Engine::getSong()->setModified(); +} + + + +void SampleTrackWindow::toggleVisibility(bool on) +{ + if(on) + { + show(); + parentWidget()->show(); + parentWidget()->raise(); + } + else + { + parentWidget()->hide(); + } +} + + + + +void SampleTrackWindow::closeEvent(QCloseEvent* ce) +{ + ce->ignore(); + + if(gui->mainWindow()->workspace()) + { + parentWidget()->hide(); + } + else + { + hide(); + } + + m_stv->m_tlb->setFocus(); + m_stv->m_tlb->setChecked(false); +} + + + +void SampleTrackWindow::saveSettings(QDomDocument& doc, QDomElement & element) +{ + MainWindow::saveWidgetState(this, element); + Q_UNUSED(element) +} + + + +void SampleTrackWindow::loadSettings(const QDomElement& element) +{ + MainWindow::restoreWidgetState(this, element); + if(isVisible()) + { + m_stv->m_tlb->setChecked(true); + } +} +