diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000..10785ef9a9d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/3rdparty/rpmalloc/rpmalloc"] + path = src/3rdparty/rpmalloc/rpmalloc + url = https://github.com/rampantpixels/rpmalloc.git diff --git a/.travis.yml b/.travis.yml index 76fc62b91f7..773aed79f6e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ matrix: - env: QT5=True TARGET_OS=win32 - env: QT5=True TARGET_OS=win64 - os: osx + osx_image: xcode8.2 env: QT5=True before_install: - . ${TRAVIS_BUILD_DIR}/.travis/${TRAVIS_OS_NAME}.${TARGET_OS}.before_install.sh diff --git a/.travis/linux..before_install.sh b/.travis/linux..before_install.sh index f5e3b435c08..91f86eef2af 100644 --- a/.travis/linux..before_install.sh +++ b/.travis/linux..before_install.sh @@ -1,5 +1,6 @@ #!/usr/bin/env bash +sudo add-apt-repository ppa:beineri/opt-qt592-trusty -y sudo add-apt-repository ppa:andrewrk/libgroove -y sudo sed -e "s/trusty/precise/" -i \ /etc/apt/sources.list.d/andrewrk-libgroove-trusty.list diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index f5988a83d70..41639980ea8 100644 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev libmp3lame-dev - libasound2-dev libjack-dev libsdl-dev libsamplerate0-dev libstk0-dev + libasound2-dev libjack-dev libsdl-dev libsamplerate0-dev libstk0-dev stk libfluidsynth-dev portaudio19-dev wine-dev g++-multilib libfltk1.3-dev libgig-dev libsoundio-dev" @@ -9,9 +9,15 @@ PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev libmp3lame-dev PACKAGES="$PACKAGES libjack0" if [ $QT5 ]; then - PACKAGES="$PACKAGES qtbase5-dev qttools5-dev-tools qttools5-dev" + PACKAGES="$PACKAGES qt59base qt59translations qt59tools" else PACKAGES="$PACKAGES libqt4-dev" fi sudo apt-get install -y $PACKAGES + +# kxstudio repo offers Carla; avoid package conflicts (wine, etc) by running last +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 diff --git a/.travis/linux..script.sh b/.travis/linux..script.sh index 895f5875ab8..d23c66ef92c 100644 --- a/.travis/linux..script.sh +++ b/.travis/linux..script.sh @@ -1,3 +1,7 @@ #!/usr/bin/env bash +if [ $QT5 ]; then + unset QTDIR QT_PLUGIN_PATH LD_LIBRARY_PATH + source /opt/qt59/bin/qt59-env.sh +fi cmake -DUSE_WERROR=ON $CMAKE_FLAGS .. diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e1861eba56..7969d6810dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ IF(COMMAND CMAKE_POLICY) CMAKE_POLICY(SET CMP0003 NEW) IF (CMAKE_MAJOR_VERSION GREATER 2) CMAKE_POLICY(SET CMP0026 OLD) + CMAKE_POLICY(SET CMP0045 NEW) CMAKE_POLICY(SET CMP0050 OLD) ENDIF() ENDIF(COMMAND CMAKE_POLICY) @@ -20,7 +21,7 @@ INCLUDE(FindPkgConfig) STRING(TOUPPER "${CMAKE_PROJECT_NAME}" PROJECT_NAME_UCASE) # Updated by maintenance tasks -SET(PROJECT_YEAR 2015) +SET(PROJECT_YEAR 2017) SET(PROJECT_AUTHOR "LMMS Developers") SET(PROJECT_URL "https://lmms.io") @@ -51,6 +52,7 @@ OPTION(WANT_CAPS "Include C* Audio Plugin Suite (LADSPA plugins)" ON) OPTION(WANT_CARLA "Include Carla plugin" ON) OPTION(WANT_CMT "Include Computer Music Toolkit LADSPA plugins" ON) OPTION(WANT_JACK "Include JACK (Jack Audio Connection Kit) support" ON) +OPTION(WANT_WEAKJACK "Loosely link JACK libraries" ON) OPTION(WANT_MP3LAME "Include MP3/Lame support" ON) OPTION(WANT_OGGVORBIS "Include OGG/Vorbis support" ON) OPTION(WANT_PULSEAUDIO "Include PulseAudio support" ON) @@ -84,12 +86,14 @@ IF(LMMS_BUILD_WIN32) SET(WANT_ALSA OFF) SET(WANT_JACK OFF) SET(WANT_PULSEAUDIO OFF) + SET(WANT_PORTAUDIO OFF) SET(WANT_SOUNDIO OFF) SET(WANT_WINMM ON) SET(LMMS_HAVE_WINMM TRUE) SET(STATUS_ALSA "") SET(STATUS_JACK "") SET(STATUS_PULSEAUDIO "") + SET(STATUS_PORTAUDIO "") SET(STATUS_SOUNDIO "") SET(STATUS_WINMM "OK") SET(STATUS_APPLEMIDI "") @@ -152,6 +156,9 @@ IF(WANT_QT5) Qt5::Xml ) + # Resolve Qt5::qmake to full path for use in packaging scripts + GET_TARGET_PROPERTY(QT_QMAKE_EXECUTABLE "${Qt5Core_QMAKE_EXECUTABLE}" IMPORTED_LOCATION) + FIND_PACKAGE(Qt5Test) SET(QT_QTTEST_LIBRARY Qt5::Test) ELSE() @@ -364,8 +371,16 @@ ENDIF(NOT LMMS_HAVE_ALSA) IF(WANT_JACK) PKG_CHECK_MODULES(JACK jack>=0.77) IF(JACK_FOUND) + IF(WANT_WEAKJACK) + SET(LMMS_HAVE_WEAKJACK TRUE) + SET(STATUS_JACK "OK (weak linking enabled)") + SET(JACK_INCLUDE_DIRS "") + # use dlsym instead + SET(JACK_LIBRARIES "dl") + ELSE() + SET(STATUS_JACK "OK") + ENDIF() SET(LMMS_HAVE_JACK TRUE) - SET(STATUS_JACK "OK") ELSE(JACK_FOUND) SET(STATUS_JACK "not found, please install libjack0.100.0-dev (or similar) " "if you require JACK support") @@ -424,7 +439,11 @@ IF(WANT_VST) FIND_PACKAGE(Wine) IF(WINE_FOUND) SET(LMMS_SUPPORT_VST TRUE) - SET(STATUS_VST "OK") + IF(WINE_LIBRARY_FIX) + SET(STATUS_VST "OK, with workaround linking ${WINE_LIBRARY_FIX}") + ELSE() + SET(STATUS_VST "OK") + ENDIF() ELSEIF(WANT_VST_NOWINE) SET(LMMS_SUPPORT_VST TRUE) SET(STATUS_VST "OK") diff --git a/cmake/linux/CMakeLists.txt b/cmake/linux/CMakeLists.txt index 78a7c541ab5..87f4194057b 100644 --- a/cmake/linux/CMakeLists.txt +++ b/cmake/linux/CMakeLists.txt @@ -1,4 +1,19 @@ -INSTALL(FILES lmms.png DESTINATION "${DATA_DIR}/pixmaps") -INSTALL(FILES lmms DESTINATION "${DATA_DIR}/menu") +INSTALL(FILES lmms.svg DESTINATION "${DATA_DIR}/icons/hicolor/scalable/apps") +INSTALL(FILES project.svg DESTINATION "${DATA_DIR}/icons/hicolor/scalable/mimetypes/" RENAME "application-x-lmms-project.svg") INSTALL(FILES lmms.desktop DESTINATION "${DATA_DIR}/applications") INSTALL(FILES lmms.xml DESTINATION "${DATA_DIR}/mime/packages") + +# AppImage creation target +SET(APPIMAGE_FILE "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}-${VERSION}-linux-${CMAKE_SYSTEM_PROCESSOR}.AppImage") + +CONFIGURE_FILE("package_linux.sh.in" "${CMAKE_BINARY_DIR}/package_linux.sh" @ONLY) + +FILE(REMOVE "${APPIMAGE_FILE}") +ADD_CUSTOM_TARGET(removeappimage + COMMAND rm -f "${APPIMAGE_FILE}" + COMMENT "Removing old AppImage") +ADD_CUSTOM_TARGET(appimage + COMMAND chmod +x "${CMAKE_BINARY_DIR}/package_linux.sh" + COMMAND "${CMAKE_BINARY_DIR}/package_linux.sh" + COMMENT "Generating AppImage") +ADD_DEPENDENCIES(appimage removeappimage) diff --git a/cmake/linux/lmms b/cmake/linux/lmms index 27488618506..884f0fed4b2 100644 --- a/cmake/linux/lmms +++ b/cmake/linux/lmms @@ -1,4 +1,4 @@ ?package(lmms):needs="X11" section="Apps/Sound" \ title="LMMS" hints="Audio" command="/usr/bin/lmms" \ longtitle="LMMS" \ - icon="/usr/share/pixmaps/lmms.png" + icon="/usr/share/icons/hicolor/scalable/apps/lmms.svg" diff --git a/cmake/linux/lmms.desktop b/cmake/linux/lmms.desktop index 6094ccfe119..67e9c2f1146 100644 --- a/cmake/linux/lmms.desktop +++ b/cmake/linux/lmms.desktop @@ -1,10 +1,10 @@ [Desktop Entry] Name=LMMS -GenericName=music production suite +GenericName=Music production suite GenericName[ca]=Programari de producció musical GenericName[de]=Software zur Musik-Produktion GenericName[fr]=Ensemble pour la production musicale -Comment=easy music production for everyone! +Comment=Music sequencer and synthesizer Comment[ca]=Producció fàcil de música per a tothom! Comment[fr]=Production facile de musique pour tout le monde ! Icon=lmms diff --git a/cmake/linux/lmms.svg b/cmake/linux/lmms.svg new file mode 100644 index 00000000000..ffe6976ce96 --- /dev/null +++ b/cmake/linux/lmms.svg @@ -0,0 +1,1540 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cmake/linux/lmms.xml b/cmake/linux/lmms.xml index 01675156053..579c3f12fdc 100644 --- a/cmake/linux/lmms.xml +++ b/cmake/linux/lmms.xml @@ -4,11 +4,7 @@ LMMS project Projecte LMMS - - - - - + diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in new file mode 100644 index 00000000000..8553be65ffc --- /dev/null +++ b/cmake/linux/package_linux.sh.in @@ -0,0 +1,198 @@ +#!/usr/bin/env bash +# Creates Linux ".AppImage" for @PROJECT_NAME_UCASE@ +# +# Depends: linuxdeployqt +# +# Notes: Will attempt to fetch linuxdeployqt automatically (x86_64 only) +# See Also: https://github.com/probonopd/linuxdeployqt/blob/master/BUILDING.md + +set -e + +USERBIN="$HOME/bin" +LINUXDEPLOYQT="$USERBIN/linuxdeployqt" +APPIMAGETOOL="$USERBIN/appimagetool" +VERBOSITY=2 # 3=debug +LOGFILE="@CMAKE_BINARY_DIR@/appimage.log" +APPDIR="@CMAKE_BINARY_DIR@/@PROJECT_NAME_UCASE@.AppDir/" +DESKTOPFILE="${APPDIR}usr/share/applications/lmms.desktop" +STRIP="" + +# Don't strip for Debug|RelWithDebInfo builds +if [[ "@CMAKE_BUILD_TYPE@" == *"Deb"* ]]; then + STRIP="-no-strip" +fi + +# Console colors +RED="\\x1B[1;31m" +GREEN="\\x1B[1;32m" +YELLOW="\\x1B[1;33m" +PLAIN="\\x1B[0m" + +function error { + echo -e " ${PLAIN}[${RED}error${PLAIN}] ${1}" + return 1 +} + +function success { + echo -e " ${PLAIN}[${GREEN}success${PLAIN}] ${1}" +} + +function skipped { + echo -e " ${PLAIN}[${YELLOW}skipped${PLAIN}] ${1}" +} + +# Blindly assume system arch is appimage arch +ARCH=$(arch) +export ARCH + +# Check for problematic install locations +INSTALL=$(echo "@CMAKE_INSTALL_PREFIX@" | sed 's/\/*$//g') +if [ "$INSTALL" == "/usr/local" ] || [ "$INSTALL" == "/usr" ] ; then + error "Incompatible CMAKE_INSTALL_PREFIX for creating AppImage: @CMAKE_INSTALL_PREFIX@" +fi + +echo -e "\nWriting verbose output to \"${LOGFILE}\"" + +# Ensure linuxdeployqt uses the same qmake version as cmake +export PATH="$(dirname "@QT_QMAKE_EXECUTABLE@")":$PATH + +# Fetch portable linuxdeployqt if cache is older than $DAYSOLD +echo -e "\nDownloading linuxdeployqt to ${LINUXDEPLOYQT}..." +mkdir -p "$HOME/bin" +DAYSOLD=2 +if env -i which linuxdeployqt > /dev/null 2>&1; then + skipped "System already provides this utility" +elif ! find "$LINUXDEPLOYQT" -mtime -$DAYSOLD 2>/dev/null|grep -q "." > /dev/null 2>&1; then + url="https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-$(uname -p).AppImage" + echo " [.......] Couldn't find linuxdeployqt newer than $DAYSOLD days old" + echo " [.......] Downloading ($(uname -p)): ${url}" + wget "$url" -O "$LINUXDEPLOYQT" -q || (rm "$LINUXDEPLOYQT" && false) + chmod +x "$LINUXDEPLOYQT" + touch "$LINUXDEPLOYQT" + success "Downloaded $LINUXDEPLOYQT" + "$LINUXDEPLOYQT" --appimage-extract > /dev/null 2>&1 + mv "squashfs-root/usr/bin/appimagetool" "$APPIMAGETOOL" + success "Extracted $APPIMAGETOOL" + mv "squashfs-root/usr/bin/mksquashfs" "$USERBIN/mksquashfs" + success "Extracted $USERBIN/mksquashfs" + rm -rf "squashfs-root/" + +else + skipped "$LINUXDEPLOYQT is less than $DAYSOLD days old" +fi + +# Make skeleton AppDir +echo -e "\nCreating ${APPDIR}..." +rm -rf "${APPDIR}" +mkdir -p "${APPDIR}usr" +success "Created ${APPDIR}" + +# Clone install to AppDir +echo -e "\nCopying @CMAKE_INSTALL_PREFIX@ to ${APPDIR}..." +cp -R "@CMAKE_INSTALL_PREFIX@/." "${APPDIR}usr" +rm -rf "${APPDIR}usr/include" +success "${APPDIR}" + +# Copy rawwaves directory for stk/mallets +mkdir -p "${APPDIR}usr/share/stk/" +cp -R /usr/share/stk/rawwaves/ "${APPDIR}usr/share/stk/" + +# Create a wrapper script which calls the lmms executable +mv "${APPDIR}usr/bin/lmms" "${APPDIR}usr/bin/lmms.real" +# shellcheck disable=SC1083 +cat >"${APPDIR}usr/bin/lmms" < /dev/null 2>&1; then + CARLAPATH="$(which carla)" + CARLAPREFIX="\${CARLAPATH%/bin*}" + echo "Carla appears to be installed on this system at \$CARLAPREFIX/lib[64]/carla so we'll use it." + export LD_LIBRARY_PATH=\$CARLAPREFIX/lib/carla:\$CARLAPREFIX/lib64/carla:\$LD_LIBRARY_PATH +else + echo "Carla does not appear to be installed. That's OK, please ignore any related library errors." +fi +export LD_LIBRARY_PATH=\$DIR/usr/lib/:\$DIR/usr/lib/lmms:\$LD_LIBRARY_PATH +# Prevent segfault on VirualBox +if lsmod |grep vboxguest > /dev/null 2>&1; then + echo "VirtualBox detected. Forcing libgl software rendering." + export LIBGL_ALWAYS_SOFTWARE=1; +fi +if ldconfig -p | grep libjack.so.0 > /dev/null 2>&1; then + echo "Jack appears to be installed on this system, so we'll use it." +else + echo "Jack does not appear to be installed. That's OK, we'll use a dummy version instead." + export LD_LIBRARY_PATH=\$DIR/usr/lib/lmms/optional:\$LD_LIBRARY_PATH +fi +QT_X11_NO_NATIVE_MENUBAR=1 \$DIR/usr/bin/lmms.real "\$@" +EOL + +chmod +x "${APPDIR}usr/bin/lmms" + +# Per https://github.com/probonopd/linuxdeployqt/issues/129 +unset LD_LIBRARY_PATH + +# Ensure linuxdeployqt can find shared objects +export LD_LIBRARY_PATH="${APPDIR}usr/lib/lmms/":$LD_LIBRARY_PATH + +# Handle wine linking +if [ -d "@WINE_LIBRARY_FIX@" ]; then + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@WINE_LIBRARY_FIX@:@WINE_LIBRARY_FIX@wine/ +fi + +# Move executables so linuxdeployqt can find them +ZYNLIB="${APPDIR}usr/lib/lmms/RemoteZynAddSubFx" +VSTLIB="${APPDIR}usr/lib/lmms/RemoteVstPlugin.exe.so" + +ZYNBIN="${APPDIR}usr/bin/RemoteZynAddSubFx" +VSTBIN="${APPDIR}usr/bin/RemoteVstPlugin.exe.so" + +mv "$ZYNLIB" "$ZYNBIN" +mv "$VSTLIB" "$VSTBIN" + +# Patch the desktop file +sed -i 's/.*Exec=.*/Exec=lmms.real/' "$DESKTOPFILE" + +# Fix linking for soft-linked plugins +for file in "${APPDIR}usr/lib/lmms/"*.so; do + thisfile="${APPDIR}usr/lib/lmms/${file##*/}" + executables="${executables} -executable=$thisfile" +done +executables="${executables} -executable=${ZYNBIN}" +executables="${executables} -executable=${VSTBIN}" +executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/imp_1199.so" +executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/imbeq_1197.so" +executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/pitch_scale_1193.so" +executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/pitch_scale_1194.so" + +# Bundle both qt and non-qt dependencies into appimage format +echo -e "\nBundling and relinking system dependencies..." +echo -e ">>>>> linuxdeployqt" > "$LOGFILE" +# shellcheck disable=SC2086 +"$LINUXDEPLOYQT" "$DESKTOPFILE" $executables -bundle-non-qt-libs -verbose=$VERBOSITY $STRIP >> "$LOGFILE" 2>&1 +success "Bundled and relinked dependencies" + +# Link to original location so lmms can find them +ln -sr "$ZYNBIN" "$ZYNLIB" +ln -sr "$VSTBIN" "$VSTLIB" + +# Remove wine library conflict +rm -f "${APPDIR}/usr/lib/libwine.so.1" + +# Use system-provided carla +rm -f "${APPDIR}usr/lib/"libcarla*.so + +# Remove problematic jack library, replace with weakjack +if [ -e "${APPDIR}/usr/lib/libjack.so.0" ]; then + rm -f "${APPDIR}/usr/lib/libjack.so.0" + mkdir -p "${APPDIR}usr/lib/lmms/optional/" + cp "@CMAKE_BINARY_DIR@/optional/weakjack.so" "${APPDIR}usr/lib/lmms/optional/weakjack.so" + ln -sr "${APPDIR}usr/lib/lmms/optional/weakjack.so" "${APPDIR}usr/lib/lmms/optional/libjack.so.0" +fi + +# Create AppImage +echo -e "\nFinishing the AppImage..." +echo -e "\n\n>>>>> appimagetool" >> "$LOGFILE" +"$APPIMAGETOOL" "${APPDIR}" "@APPIMAGE_FILE@" >> "$LOGFILE" 2>&1 +success "Created @APPIMAGE_FILE@" + +echo -e "\nFinished" diff --git a/data/application-x-lmms-project.svg b/cmake/linux/project.svg similarity index 100% rename from data/application-x-lmms-project.svg rename to cmake/linux/project.svg diff --git a/cmake/modules/FindWine.cmake b/cmake/modules/FindWine.cmake index da76f3ff338..47850dcd111 100644 --- a/cmake/modules/FindWine.cmake +++ b/cmake/modules/FindWine.cmake @@ -7,6 +7,8 @@ # WINE_DEFINITIONS - Compiler switches required for using wine # +LIST(APPEND CMAKE_PREFIX_PATH /opt/wine-stable /opt/wine-devel /opt/wine-staging) + FIND_PATH(WINE_INCLUDE_DIR windows/windows.h PATH_SUFFIXES wine) FIND_LIBRARY(WINE_LIBRARY NAMES wine PATH_SUFFIXES wine i386-linux-gnu/wine) FIND_PROGRAM(WINE_CXX NAMES wineg++ winegcc winegcc64 winegcc32) @@ -14,6 +16,26 @@ FIND_PROGRAM(WINE_CXX NAMES wineg++ winegcc winegcc64 winegcc32) 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) + +# Debian systems +IF("${WINEBUILD_OUTPUT}" MATCHES ".*x86_64-linux-gnu/wine/libwinecrt0.a.*") + SET(WINE_LIBRARY_FIX "/usr/lib/i386-linux-gnu/" ) +# Fedora systems +ELSEIF("${WINEBUILD_OUTPUT}" MATCHES "/usr/lib/lib64/wine/libwinecrt0.a.*") + SET(WINE_LIBRARY_FIX "/usr/lib/i386/") +# Wine stable +ELSEIF("${WINEBUILD_OUTPUT}" MATCHES "/opt/wine-stable/lib64/wine/libwinecrt0.a.*") + SET(WINE_LIBRARY_FIX "/opt/wine-stable/lib/") +# Wine development +ELSEIF("${WINEBUILD_OUTPUT}" MATCHES "/opt/wine-devel/lib64/wine/libwinecrt0.a.*") + SET(WINE_LIBRARY_FIX "/opt/wine-devel/lib/") +# Wine staging +ELSEIF("${WINEBUILD_OUTPUT}" MATCHES "/opt/wine-staging/lib64/wine/libwinecrt0.a.*") + SET(WINE_LIBRARY_FIX "/opt/wine-staging/lib/") +ENDIF() + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Wine DEFAULT_MSG WINE_LIBRARIES WINE_INCLUDE_DIRS) diff --git a/data/projects/templates/Empty.mpt b/data/projects/templates/Empty.mpt index cf5bfa150ac..4974213b76e 100644 --- a/data/projects/templates/Empty.mpt +++ b/data/projects/templates/Empty.mpt @@ -30,11 +30,7 @@ - - -

Put down your project notes here.

]]>
+ diff --git a/data/projects/templates/default.mpt b/data/projects/templates/default.mpt index 18fcdd92592..bebd41fe3bb 100644 --- a/data/projects/templates/default.mpt +++ b/data/projects/templates/default.mpt @@ -78,11 +78,7 @@ - - -

Enter project notes here

]]>
+ diff --git a/data/themes/default/lcd_19green.png b/data/themes/default/lcd_19green.png index 437ac8baf58..708987c3414 100644 Binary files a/data/themes/default/lcd_19green.png and b/data/themes/default/lcd_19green.png differ diff --git a/data/themes/default/lcd_19red.png b/data/themes/default/lcd_19red.png index 0bd8817de92..e4c4a1f720d 100644 Binary files a/data/themes/default/lcd_19red.png and b/data/themes/default/lcd_19red.png differ diff --git a/data/themes/default/lcd_21pink.png b/data/themes/default/lcd_21pink.png index 64ec75e33d9..c2009eedac6 100644 Binary files a/data/themes/default/lcd_21pink.png and b/data/themes/default/lcd_21pink.png differ diff --git a/include/AudioJack.h b/include/AudioJack.h index 7069a069178..60adfc9a56d 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -28,11 +28,16 @@ #include "lmmsconfig.h" #ifdef LMMS_HAVE_JACK +#ifndef LMMS_HAVE_WEAKJACK #include +#else +#include "AudioWeakJack.h" +#endif #include #include #include +#include #include "AudioDevice.h" #include "AudioDeviceSetupWidget.h" @@ -103,6 +108,7 @@ private slots: bool m_active; bool m_stopped; + QMutex m_processingMutex; MidiJack *m_midiClient; QVector m_outputPorts; diff --git a/include/AudioWeakJack.def b/include/AudioWeakJack.def new file mode 100644 index 00000000000..faa8fd10f18 --- /dev/null +++ b/include/AudioWeakJack.def @@ -0,0 +1,162 @@ +/* macro-absraction of the JACK API + * + * see weak_libjack.c for details, in general arguments are: + * + * [required], [return type], [name], [arguments], [code or return value] + * + * This file is included multiple times with different macro definitions + * do not add header guards. + * see https://en.wikibooks.org/wiki/C_Programming/Preprocessor#X-Macros + */ + +#ifdef USE_WEAK_JACK + +/* */ +JCFUN(1, int, client_close, 0) +JCFUN(1, char*, get_client_name, NULL) +JVFUN(0, on_shutdown, (jack_client_t *c, JackShutdownCallback s, void *a), (c,s,a),) +JVFUN(0, on_info_shutdown, (jack_client_t *c, JackInfoShutdownCallback s, void *a), (c,s,a),) + +JPFUN(1, int, set_process_callback, (jack_client_t *c, JackProcessCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_freewheel_callback, (jack_client_t *c, JackFreewheelCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_buffer_size_callback, (jack_client_t *c, JackBufferSizeCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_sample_rate_callback, (jack_client_t *c, JackSampleRateCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_port_registration_callback, (jack_client_t *c, JackPortRegistrationCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_port_connect_callback, (jack_client_t *c, JackPortConnectCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_graph_order_callback, (jack_client_t *c, JackGraphOrderCallback g, void *a), (c,g,a), -1) +JPFUN(1, int, set_xrun_callback, (jack_client_t *c, JackXRunCallback g, void *a), (c,g,a), -1) +JPFUN(1, int, set_latency_callback, (jack_client_t *c, JackLatencyCallback g, void *a), (c,g,a), -1) +JVFUN(1, set_error_function, (void (*f)(const char *)), (f),) +JVFUN(1, set_info_function, (void (*f)(const char *)), (f),) + +JCFUN(1, int, activate, -1) +JCFUN(1, int, deactivate, -1) + +JPFUN(1, int, client_name_size, (), (), 32) + +JCFUN(1, jack_nframes_t, get_sample_rate, 0) +JCFUN(1, jack_nframes_t, get_buffer_size, 0) +JPFUN(1, jack_nframes_t, frames_since_cycle_start, (const jack_client_t *c), (c), 0) +JPFUN(1, jack_nframes_t, frame_time, (const jack_client_t *c), (c), 0) +JPFUN(1, jack_nframes_t, last_frame_time, (const jack_client_t *c), (c), 0) +JPFUN(1, jack_time_t, get_time, (void), (), 0) +JCFUN(1, float, cpu_load, 0) +JCFUN(1, int, is_realtime, 0) + +JPFUN(1, int, set_freewheel, (jack_client_t *c, int o), (c,o), 0) +JPFUN(1, int, set_buffer_size, (jack_client_t *c, jack_nframes_t b), (c,b), 0) + +JCFUN(0, int, recompute_total_latencies, 0) +JPFUN(0, jack_nframes_t, port_get_total_latency, (jack_client_t *c, jack_port_t *p), (c,p), 0) +JVFUN(0, port_get_latency_range, (jack_port_t *p, jack_latency_callback_mode_t m, jack_latency_range_t *r), (p,m,r), if (r) {r->min = r->max = 0;}) +JVFUN(0, port_set_latency_range, (jack_port_t *p, jack_latency_callback_mode_t m, jack_latency_range_t *r), (p,m,r),) + +JPFUN(1, void*, port_get_buffer, (jack_port_t *p, jack_nframes_t n), (p,n), NULL) +JPFUN(1, int, port_request_monitor, (jack_port_t *p, int o), (p,o), 0) +JPFUN(1, int, port_ensure_monitor, (jack_port_t *p, int o), (p,o), 0) +JPFUN(1, int, port_monitoring_input, (jack_port_t *p), (p), 0) + +JPFUN(1, const char*, port_name, (const jack_port_t *p), (p), NULL) +JPFUN(1, const char*, port_short_name, (const jack_port_t *p), (p), NULL) +JPFUN(1, int, port_flags, (const jack_port_t *p), (p), 0) +JPFUN(1, const char**, get_ports,(jack_client_t *c, const char *p, const char *t, unsigned long f), (c,p,t,f), NULL) +JPFUN(1, int, port_name_size, (void), (), 0) +JPFUN(1, int, port_type_size, (void), (), 0) +JPFUN(1, size_t, port_type_get_buffer_size, (jack_client_t *c, const char *t), (c,t), 0) +JPFUN(1, jack_port_t*, port_by_name, (jack_client_t *c, const char *n), (c,n), NULL) +JPFUN(1, jack_port_t*, port_by_id, (jack_client_t *c, jack_port_id_t i), (c,i), NULL) +JPFUN(1, jack_port_t*, port_register, (jack_client_t *c, const char *n, const char *t, unsigned long f, unsigned long b), (c,n,t,f,b), NULL) +JPFUN(1, int, port_unregister, (jack_client_t *c, jack_port_t *p), (c,p), 0) +JPFUN(1, const char *, port_type, (const jack_port_t *p), (p), 0) +JPFUN(1, const char **, port_get_connections, (const jack_port_t *p), (p), 0) +JPFUN(1, const char **, port_get_all_connections, (const jack_client_t *c, const jack_port_t *p), (c,p), 0) +JPFUN(1, int, port_set_name, (jack_port_t *p, const char *n), (p,n), -1) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +JXFUN(0, int, port_rename, (jack_client_t *c, jack_port_t *p, const char *n), (c,p,n), return jack_port_set_name (p,n);) +#pragma GCC diagnostic pop +JPFUN(1, int, port_get_aliases, (const jack_port_t *port, char* const aliases[2]), (port,aliases), 0) +JPFUN(1, int, port_disconnect, (jack_client_t *c, jack_port_t *p), (c,p), 0) +JPFUN(1, int, connect, (jack_client_t *c, const char *s, const char *d), (c,s,d), -1) +JPFUN(1, int, disconnect, (jack_client_t *c, const char *s, const char *d), (c,s,d), -1) +JVFUN(0, free, (void *p), (p), free(p);) + +JCFUN(1, jack_nframes_t, cycle_wait, 0) +JVFUN(1, cycle_signal, (jack_client_t *c, int s), (c,s),) +JPFUN(1, int, set_process_thread, (jack_client_t *c, JackThreadCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_thread_init_callback, (jack_client_t *c, JackThreadInitCallback p, void *a), (c,p,a), -1) + +JPFUN(1, int, transport_locate, (jack_client_t *c, jack_nframes_t f), (c,f), 0) +JVFUN(1, transport_start, (jack_client_t *c), (c),) +JVFUN(1, transport_stop, (jack_client_t *c), (c),) +JPFUN(1, jack_nframes_t, get_current_transport_frame, (const jack_client_t *c), (c), 0) +JXFUN(1, jack_transport_state_t, transport_query, (const jack_client_t *c, jack_position_t *p), (c,p), memset(p, 0, sizeof(jack_position_t)); return JackTransportStopped;) +JPFUN(1, int, set_sync_callback, (jack_client_t *c, JackSyncCallback p, void *a), (c,p,a), -1) +JPFUN(1, int, set_timebase_callback, (jack_client_t *c, int l, JackTimebaseCallback p, void *a), (c,l,p,a), -1) +JCFUN(1, int, release_timebase, 0) + +/* */ +JPFUN(1, uint32_t, midi_get_event_count, (void* p), (p), 0) +JPFUN(1, int, midi_event_get, (jack_midi_event_t *e, void *p, uint32_t i), (e,p,i), -1) +JPFUN(1, int, midi_event_write, (void *b, jack_nframes_t t, const jack_midi_data_t *d, size_t s), (b,t,d,s), -1) +JVFUN(1, midi_clear_buffer, (void *b), (b),) + +/* */ +JPFUN(0, int, set_session_callback, (jack_client_t *c, JackSessionCallback s, void *a), (c,s,a), -1) +JPFUN(0, int, session_reply, (jack_client_t *c, jack_session_event_t *e), (c,e), -1) +JVFUN(0, session_event_free, (jack_session_event_t *e), (e), ) + +/* */ +JPFUN(1, jack_ringbuffer_t *, ringbuffer_create, (size_t s), (s), NULL) +JVFUN(1, ringbuffer_free, (jack_ringbuffer_t *rb), (rb), ) +JVFUN(1, ringbuffer_reset, (jack_ringbuffer_t *rb), (rb), ) +JVFUN(1, ringbuffer_read_advance, (jack_ringbuffer_t *rb, size_t c), (rb,c), ) +JVFUN(1, ringbuffer_write_advance, (jack_ringbuffer_t *rb, size_t c), (rb,c), ) +JPFUN(1, size_t, ringbuffer_read_space, (const jack_ringbuffer_t *rb), (rb), 0) +JPFUN(1, size_t, ringbuffer_write_space, (const jack_ringbuffer_t *rb), (rb), 0) +JPFUN(1, size_t, ringbuffer_read, (jack_ringbuffer_t *rb, char *d, size_t c), (rb,d,c), 0) +JPFUN(1, size_t, ringbuffer_write, (jack_ringbuffer_t *rb, const char *s, size_t c), (rb,s,c), 0) +JPFUN(0, int, ringbuffer_mlock, (jack_ringbuffer_t *rb), (rb), 0) +JVFUN(0, ringbuffer_get_read_vector, (const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *v), (rb,v), if (v) {v->buf=NULL; v->len=0;} ) +JVFUN(0, ringbuffer_get_write_vector, (const jack_ringbuffer_t *rb, jack_ringbuffer_data_t *v), (rb,v), if (v) {v->buf=NULL; v->len=0;} ) +JPFUN(0, size_t, ringbuffer_peek, (jack_ringbuffer_t *rb, char *d, size_t c), (rb,d,c), 0) + +/* */ +JCFUN(0, int, client_real_time_priority, 0) +JCFUN(0, int, client_max_real_time_priority, 0) +JPFUN(0, int, acquire_real_time_scheduling, (jack_native_thread_t t, int p), (t,p), 0) +JPFUN(0, int, drop_real_time_scheduling, (jack_native_thread_t t), (t), 0) +JPFUN(0, int, client_stop_thread, (jack_client_t* c, jack_native_thread_t t), (c,t), 0) +JPFUN(0, int, client_kill_thread, (jack_client_t* c, jack_native_thread_t t), (c,t), 0) +#ifndef _WIN32 +JVFUN(0, set_thread_creator, (jack_thread_creator_t c), (c),) +#endif +JPFUN(1, int, client_create_thread, \ + (jack_client_t* c, jack_native_thread_t *t, int p, int r, void *(*f)(void*), void *a), (c,t,p,r,f,a), 0) + +#ifndef NO_JACK_METADATA +/* - TODO*/ + +/* */ +JPFUN(0, char *, get_uuid_for_client_name, (jack_client_t* c, const char* n), (c,n), NULL) +JPFUN(0, char *, get_client_name_by_uuid, (jack_client_t* c, const char* u), (c,u), NULL) +JPFUN(0, jack_uuid_t, port_uuid, (const jack_port_t *p), (p), 0) + +/* */ +JPFUN(0, int, set_property, (jack_client_t* c, jack_uuid_t s, const char* k, const char* v, const char* t), (c,s,k,v,t), -1) +JXFUN(0, int, get_property, (jack_uuid_t s, const char* k, char** v, char** t), (s,k,v,t), if (v) *v=NULL; if (t) *t=NULL; return -1;) +JVFUN(0, free_description, (jack_description_t* d, int f), (d,f),) +JXFUN(0, int, get_properties, (jack_uuid_t s, jack_description_t* d), (s,d), if (d) {d->properties = NULL; d->property_cnt = 0;} return -1;) +JXFUN(0, int, get_all_properties, (jack_description_t** d), (d), if (d) *d=NULL; return -1;) +JPFUN(0, int, remove_property, (jack_client_t* c, jack_uuid_t s, const char* k), (c,s,k), -1) +JPFUN(0, int, remove_properties, (jack_client_t* c, jack_uuid_t s), (c,s), -1) +JPFUN(0, int, remove_all_properties, (jack_client_t* c), (c), -1) +JPFUN(0, int, set_property_change_callback, (jack_client_t *c, JackPropertyChangeCallback s, void *a), (c,s,a), -1) +#endif + +/* */ +JCFUN(1, float, get_max_delayed_usecs, 0.0) +JCFUN(1, float, get_xrun_delayed_usecs, 0.0) +JVFUN(0, reset_max_delayed_usecs, (jack_client_t *c), (c),) + +#endif // end USE_WEAK_JACK diff --git a/include/AudioWeakJack.h b/include/AudioWeakJack.h new file mode 100644 index 00000000000..e53fced8eaa --- /dev/null +++ b/include/AudioWeakJack.h @@ -0,0 +1,237 @@ +/* runtime/weak dynamic JACK linking + * + * (C) 2014 Robin Gareus + * + * The wrapped jack API itself is + * (C) 2001 Paul Davis + * (C) 2004 Jack O'Quin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef _WEAK_JACK_H +#define _WEAK_JACK_H + +// LMMS uses LMMS_HAVE_WEAKJACK instead +#ifndef USE_WEAK_JACK +#define USE_WEAK_JACK +#endif + +// LMMS doesn't use metadata.h +#ifndef NO_JACK_METADATA +#define NO_JACK_METADATA +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** check if libjack is available + * + * return 0 if libjack is dynamically linked of was + * successfully dl-opened. Otherwise: + * + * -1: library was not initialized + * -2: libjack was not found + * > 0 bitwise flags: + * 1: a required function was not found in libjack + * 2: jack_client_open was not found in libjack + */ +int have_libjack(void); + +#ifdef __cplusplus +} +#endif + +#ifdef USE_WEAK_JACK + +/* */ +#define jack_client_close WJACK_client_close +#define jack_get_client_name WJACK_get_client_name +#define jack_get_sample_rate WJACK_get_sample_rate +#define jack_get_buffer_size WJACK_get_buffer_size +#define jack_frames_since_cycle_start WJACK_frames_since_cycle_start +#define jack_frame_time WJACK_frame_time +#define jack_last_frame_time WJACK_last_frame_time +#define jack_get_time WJACK_get_time +#define jack_cpu_load WJACK_cpu_load +#define jack_is_realtime WJACK_is_realtime + +#define jack_client_name_size WJACK_client_name_size + +#define jack_set_freewheel WJACK_set_freewheel +#define jack_set_buffer_size WJACK_set_buffer_size + +#define jack_on_shutdown WJACK_on_shutdown +#define jack_on_info_shutdown WJACK_on_info_shutdown +#define jack_set_process_callback WJACK_set_process_callback +#define jack_set_freewheel_callback WJACK_set_freewheel_callback +#define jack_set_buffer_size_callback WJACK_set_buffer_size_callback +#define jack_set_sample_rate_callback WJACK_set_sample_rate_callback +#define jack_set_port_registration_callback WJACK_set_port_registration_callback +#define jack_set_port_connect_callback WJACK_set_port_connect_callback +#define jack_set_graph_order_callback WJACK_set_graph_order_callback +#define jack_set_xrun_callback WJACK_set_xrun_callback +#define jack_set_latency_callback WJACK_set_latency_callback +#define jack_set_error_function WJACK_set_error_function +#define jack_set_info_function WJACK_set_info_function + +#define jack_activate WJACK_activate +#define jack_deactivate WJACK_deactivate + +#define jack_recompute_total_latencies WJACK_recompute_total_latencies +#define jack_port_get_total_latency WJACK_port_get_total_latency +#define jack_port_get_latency_range WJACK_port_get_latency_range +#define jack_port_set_latency_range WJACK_port_set_latency_range +#define jack_port_get_buffer WJACK_port_get_buffer +#define jack_port_request_monitor WJACK_port_request_monitor +#define jack_port_ensure_monitor WJACK_port_ensure_monitor +#define jack_port_monitoring_input WJACK_port_monitoring_input + +#define jack_port_name WJACK_port_name +#define jack_port_short_name WJACK_port_short_name +#define jack_port_flags WJACK_port_flags +#define jack_get_ports WJACK_get_ports +#define jack_port_name_size WJACK_port_name_size +#define jack_port_type_size WJACK_port_type_size +#define jack_port_type_get_buffer_size WJACK_port_type_get_buffer_size +#define jack_port_by_name WJACK_port_by_name +#define jack_port_by_id WJACK_port_by_id +#define jack_port_set_name WJACK_port_set_name +#define jack_port_get_aliases WJACK_port_get_aliases +#define jack_port_rename WJACK_port_rename +#define jack_port_disconnect WJACK_port_disconnect +#define jack_port_register WJACK_port_register +#define jack_port_unregister WJACK_port_unregister +#define jack_port_type WJACK_port_type +#define jack_port_get_connections WJACK_port_get_connections +#define jack_port_get_all_connections WJACK_port_get_all_connections +#define jack_connect WJACK_connect +#define jack_disconnect WJACK_disconnect +#define jack_free WJACK_free + +#define jack_cycle_wait WJACK_cycle_wait +#define jack_cycle_signal WJACK_cycle_signal +#define jack_set_process_thread WJACK_set_process_thread +#define jack_set_thread_init_callback WJACK_set_thread_init_callback + +/* */ +#define jack_get_current_transport_frame WJACK_get_current_transport_frame +#define jack_transport_locate WJACK_transport_locate +#define jack_transport_start WJACK_transport_start +#define jack_transport_stop WJACK_transport_stop +#define jack_transport_query WJACK_transport_query +#define jack_set_sync_callback WJACK_set_sync_callback +#define jack_set_timebase_callback WJACK_set_timebase_callback +#define jack_release_timebase WJACK_release_timebase + +/* */ +#define jack_midi_get_event_count WJACK_midi_get_event_count +#define jack_midi_event_get WJACK_midi_event_get +#define jack_midi_event_write WJACK_midi_event_write +#define jack_midi_clear_buffer WJACK_midi_clear_buffer + +/* */ +#define jack_set_session_callback WJACK_set_session_callback +#define jack_session_reply WJACK_session_reply +#define jack_session_event_free WJACK_session_event_free + +/* */ +#define jack_ringbuffer_create WJACK_ringbuffer_create +#define jack_ringbuffer_free WJACK_ringbuffer_free +#define jack_ringbuffer_reset WJACK_ringbuffer_reset +#define jack_ringbuffer_read_advance WJACK_ringbuffer_read_advance +#define jack_ringbuffer_write_advance WJACK_ringbuffer_write_advance +#define jack_ringbuffer_read_space WJACK_ringbuffer_read_space +#define jack_ringbuffer_write_space WJACK_ringbuffer_write_space +#define jack_ringbuffer_read WJACK_ringbuffer_read +#define jack_ringbuffer_write WJACK_ringbuffer_write +#define jack_ringbuffer_mlock WJACK_ringbuffer_mlock +#define jack_ringbuffer_get_read_vector WJACK_ringbuffer_get_read_vector +#define jack_ringbuffer_get_write_vector WJACK_ringbuffer_get_write_vector +#define jack_ringbuffer_peek WJACK_ringbuffer_peek + +/* */ +#define jack_client_real_time_priority WJACK_client_real_time_priority +#define jack_client_max_real_time_priority WJACK_client_max_real_time_priority +#define jack_acquire_real_time_scheduling WJACK_acquire_real_time_scheduling +#define jack_client_create_thread WJACK_client_create_thread +#define jack_drop_real_time_scheduling WJACK_drop_real_time_scheduling +#define jack_client_stop_thread WJACK_client_stop_thread +#define jack_client_kill_thread WJACK_client_kill_thread +#define jack_set_thread_creator WJACK_set_thread_creator + +#define jack_client_open WJACK_client_client_openXXX + +#ifndef NO_JACK_METADATA +/* */ +#define jack_get_uuid_for_client_name WJACK_get_uuid_for_client_name +#define jack_get_client_name_by_uuid WJACK_get_client_name_by_uuid +#define jack_port_uuid WJACK_port_uuid + +#define jack_set_property WJACK_set_property +#define jack_get_property WJACK_get_property +#define jack_free_description WJACK_free_description +#define jack_get_properties WJACK_get_properties +#define jack_get_all_properties WJACK_get_all_properties +#define jack_remove_property WJACK_remove_property +#define jack_remove_properties WJACK_remove_properties +#define jack_remove_all_properties WJACK_remove_all_properties +#define jack_set_property_change_callback WJACK_set_property_change_callback +#endif + +/* */ +#define jack_get_max_delayed_usecs WJACK_get_max_delayed_usecs +#define jack_get_xrun_delayed_usecs WJACK_get_xrun_delayed_usecs +#define jack_reset_max_delayed_usecs WJACK_reset_max_delayed_usecs + +#endif // end USE_WEAK_JACK + +#include +#include +#include +#include +#include +#include + +#ifndef NO_JACK_METADATA +#include +#endif + +#ifdef USE_WEAK_JACK + +#undef jack_client_open + +/* var-args hack */ + +#ifdef __cplusplus +extern "C" { +#endif +void (* WJACK_get_client_open (void)) (void); +jack_client_t * WJACK_no_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...); +#ifdef __cplusplus +} +#endif + +#define jack_client_open(...) \ +( \ + (WJACK_get_client_open() != NULL) \ + ? ((jack_client_t* (*)(const char *, jack_options_t, jack_status_t *, ...))(WJACK_get_client_open()))(__VA_ARGS__) \ + : WJACK_no_client_open(__VA_ARGS__) \ +) + +#endif // end USE_WEAK_JACK + +#endif // _WEAK_JACK_H diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index dd9bd7680cf..cdfdffccf62 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -25,6 +25,7 @@ #ifndef AUTOMATABLE_MODEL_H #define AUTOMATABLE_MODEL_H +#include #include #include "JournallingObject.h" diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index 5fc3ca03ac4..9705c5efa6a 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -132,6 +132,7 @@ public slots: void getSelectedValues(timeMap & selected_values ); void drawLine( int x0, float y0, int x1, float y1 ); + void removePoints( int x0, int x1 ); protected slots: void play(); diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index 32b4bc28606..0af7ea4fa6c 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -80,7 +80,7 @@ class EXPORT AutomationPattern : public TrackContentObject MidiTime putValue( const MidiTime & time, const float value, const bool quantPos = true, - const bool ignoreSurroundingPoints = false ); + const bool ignoreSurroundingPoints = true ); void removeValue( const MidiTime & time ); diff --git a/include/ConfigManager.h b/include/ConfigManager.h index 4c0a73e056a..2510283c048 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -44,7 +44,7 @@ const QString TEMPLATE_PATH = "templates/"; const QString PRESETS_PATH = "presets/"; const QString SAMPLES_PATH = "samples/"; const QString GIG_PATH = "samples/gig/"; -const QString SF2_PATH = "samples/sf2/"; +const QString SF2_PATH = "samples/soundfonts/"; const QString LADSPA_PATH ="plugins/ladspa/"; const QString DEFAULT_THEME_PATH = "themes/default/"; const QString TRACK_ICON_PATH = "track_icons/"; diff --git a/include/MainApplication.h b/include/MainApplication.h index 8d5df9f8696..d3acb95cabf 100644 --- a/include/MainApplication.h +++ b/include/MainApplication.h @@ -31,15 +31,26 @@ #ifdef LMMS_BUILD_WIN32 #include +#if QT_VERSION >= 0x050000 +#include +#endif #endif +#if defined(LMMS_BUILD_WIN32) && QT_VERSION >= 0x050000 +class MainApplication : public QApplication, public QAbstractNativeEventFilter +#else class MainApplication : public QApplication +#endif { public: MainApplication(int& argc, char** argv); bool event(QEvent* event); #ifdef LMMS_BUILD_WIN32 bool winEventFilter(MSG* msg, long* result); +#if QT_VERSION >= 0x050000 + bool nativeEventFilter(const QByteArray& eventType, void* message, + long* result); +#endif #endif inline QString& queuedFile() { diff --git a/include/MemoryManager.h b/include/MemoryManager.h index ef6c0abbfc6..23561e4c0ac 100644 --- a/include/MemoryManager.h +++ b/include/MemoryManager.h @@ -1,6 +1,7 @@ /* - * MemoryManager.h - A lightweight, generic memory manager for LMMS + * MemoryManager.h * + * Copyright (c) 2017 Lukas W * Copyright (c) 2014 Vesa Kivimäki * Copyright (c) 2007-2014 Tobias Doerffel * @@ -26,81 +27,22 @@ #ifndef MEMORY_MANAGER_H #define MEMORY_MANAGER_H -#include -#include -#include -#include "MemoryHelper.h" -#include "export.h" - -class QReadWriteLock; - -const int MM_CHUNK_SIZE = 64; // granularity of managed memory -const int MM_INITIAL_CHUNKS = 1024 * 1024; // how many chunks to allocate at startup - TODO: make configurable -const int MM_INCREMENT_CHUNKS = 16 * 1024; // min. amount of chunks to increment at a time - -struct MemoryPool -{ - void * m_pool; - char * m_free; - size_t m_chunks; - QMutex m_mutex; - - MemoryPool() : - m_pool( NULL ), - m_free( NULL ), - m_chunks( 0 ) - {} - - MemoryPool( size_t chunks ) : - m_chunks( chunks ) - { - m_free = reinterpret_cast( MemoryHelper::alignedMalloc( chunks ) ); - memset( m_free, 1, chunks ); - } - - MemoryPool( const MemoryPool & mp ) : - m_pool( mp.m_pool ), - m_free( mp.m_free ), - m_chunks( mp.m_chunks ), - m_mutex() - {} - - MemoryPool & operator = ( const MemoryPool & mp ) - { - m_pool = mp.m_pool; - m_free = mp.m_free; - m_chunks = mp.m_chunks; - return *this; - } - - void * getChunks( int chunksNeeded ); - void releaseChunks( void * ptr, int chunks ); -}; +#include +#include -struct PtrInfo -{ - int chunks; - MemoryPool * memPool; -}; - -typedef QVector MemoryPoolVector; -typedef QHash PointerInfoMap; +#include "export.h" class EXPORT MemoryManager { public: - static bool init(); + struct ThreadGuard + { + ThreadGuard(); + ~ThreadGuard(); + }; + static void * alloc( size_t size ); static void free( void * ptr ); - static int extend( int chunks ); // returns index of created pool (for use by alloc) - static void cleanup(); - -private: - static MemoryPoolVector s_memoryPools; - static QReadWriteLock s_poolMutex; - - static PointerInfoMap s_pointerInfo; - static QMutex s_pointerMutex; }; template @@ -147,32 +89,4 @@ static void operator delete[] ( void * ptr ) \ // and just for symmetry... #define MM_FREE( ptr ) MemoryManager::free( ptr ) - - -// for debugging purposes - -#define MM_OPERATORS_DEBUG \ -public: \ -static void * operator new ( size_t size ) \ -{ \ - qDebug( "MM_OPERATORS_DEBUG: new called for %d bytes", size ); \ - return MemoryManager::alloc( size ); \ -} \ -static void * operator new[] ( size_t size ) \ -{ \ - qDebug( "MM_OPERATORS_DEBUG: new[] called for %d bytes", size ); \ - return MemoryManager::alloc( size ); \ -} \ -static void operator delete ( void * ptr ) \ -{ \ - qDebug( "MM_OPERATORS_DEBUG: delete called for %p", ptr ); \ - MemoryManager::free( ptr ); \ -} \ -static void operator delete[] ( void * ptr ) \ -{ \ - qDebug( "MM_OPERATORS_DEBUG: delete[] called for %p", ptr ); \ - MemoryManager::free( ptr ); \ -} - - #endif diff --git a/include/MidiJack.h b/include/MidiJack.h index 9929b57c2e2..b3634bc34a8 100644 --- a/include/MidiJack.h +++ b/include/MidiJack.h @@ -28,8 +28,12 @@ #include "lmmsconfig.h" #ifdef LMMS_HAVE_JACK +#ifndef LMMS_HAVE_WEAKJACK #include #include +#else +#include "AudioWeakJack.h" +#endif #include #include diff --git a/include/Mixer.h b/include/Mixer.h index 8368e843405..1c3780b1bee 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -155,6 +155,7 @@ class EXPORT Mixer : public QObject void initDevices(); void clear(); + void clearNewPlayHandles(); // audio-device-stuff diff --git a/include/Note.h b/include/Note.h index 2d448c6d459..c14f9d93109 100644 --- a/include/Note.h +++ b/include/Note.h @@ -113,11 +113,19 @@ class EXPORT Note : public SerializingObject void quantizeLength( const int qGrid ); void quantizePos( const int qGrid ); - static inline bool lessThan( Note * &lhs, Note * &rhs ) + static inline bool lessThan( const Note * lhs, const Note * rhs ) { // function to compare two notes - must be called explictly when // using qSort - return (bool) ((int) ( *lhs ).pos() < (int) ( *rhs ).pos()); + if( (int)( *lhs ).pos() < (int)( *rhs ).pos() ) + { + return true; + } + else if( (int)( *lhs ).pos() > (int)( *rhs ).pos() ) + { + return false; + } + return ( (int)( *lhs ).key() > (int)( *rhs ).key() ); } inline bool selected() const diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index 344980eb61e..768a74aa276 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -66,8 +66,7 @@ class EXPORT NotePlayHandle : public PlayHandle, public Note NotePlayHandle* parent = NULL, int midiEventChannel = -1, Origin origin = OriginPattern ); - virtual ~NotePlayHandle() {} - void done(); + virtual ~NotePlayHandle(); void * operator new ( size_t size, void * p ) { diff --git a/include/PluginFactory.h b/include/PluginFactory.h index b2d0131ad89..fb3e8ea9412 100644 --- a/include/PluginFactory.h +++ b/include/PluginFactory.h @@ -28,6 +28,7 @@ #include #include +#include #include #include "export.h" diff --git a/include/Song.h b/include/Song.h index b8879ad9b15..0ba8a3503c5 100644 --- a/include/Song.h +++ b/include/Song.h @@ -33,6 +33,7 @@ #include "TrackContainer.h" #include "Controller.h" #include "MeterModel.h" +#include "Mixer.h" #include "VstSyncController.h" @@ -229,6 +230,17 @@ class EXPORT Song : public TrackContainer return m_loadingProject; } + void loadingCancelled() + { + m_isCancelled = true; + Engine::mixer()->clearNewPlayHandles(); + } + + bool isCancelled() + { + return m_isCancelled; + } + bool isModified() const { return m_modified; @@ -358,6 +370,7 @@ private slots: volatile bool m_paused; bool m_loadingProject; + bool m_isCancelled; QStringList m_errors; diff --git a/include/lmms_math.h b/include/lmms_math.h index b1b01be277b..ccf5fbf62f2 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -229,11 +229,12 @@ static inline float logToLinearScale( float min, float max, float value ) static inline float linearToLogScale( float min, float max, float value ) { static const float EXP = 1.0f / F_E; - const float val = ( value - min ) / ( max - min ); + const float valueLimited = qBound( min, value, max); + const float val = ( valueLimited - min ) / ( max - min ); if( min < 0 ) { const float mmax = qMax( qAbs( min ), qAbs( max ) ); - float result = signedPowf( value / mmax, EXP ) * mmax; + float result = signedPowf( valueLimited / mmax, EXP ) * mmax; return isnan( result ) ? 0 : result; } float result = powf( val, EXP ) * ( max - min ) + min; diff --git a/plugins/Eq/EqCurve.cpp b/plugins/Eq/EqCurve.cpp index 24a560b1556..5d669b04ac4 100644 --- a/plugins/Eq/EqCurve.cpp +++ b/plugins/Eq/EqCurve.cpp @@ -32,8 +32,7 @@ EqHandle::EqHandle( int num, int x, int y ): m_width( x ), m_heigth( y ), m_mousePressed( false ), - m_active( false ), - m_handleMoved( false ) + m_active( false ) { setFlag( ItemIsMovable ); setFlag( ItemSendsGeometryChanges ); @@ -41,7 +40,6 @@ EqHandle::EqHandle( int num, int x, int y ): float totalHeight = 36; m_pixelsPerUnitHeight = ( m_heigth ) / ( totalHeight ); setMouseHover( false ); - connect( this, SIGNAL( positionChanged() ), this, SLOT( handleMoved() ) ); } @@ -93,14 +91,6 @@ float EqHandle::yPixelToGain(float y , int h, float pixelPerUnitHeight ) -void EqHandle::handleMoved() -{ - m_handleMoved = true; -} - - - - void EqHandle::paint( QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget ) { painter->setRenderHint( QPainter::Antialiasing, true ); @@ -189,6 +179,11 @@ void EqHandle::loadPixmap() m_circlePixmap = PLUGIN_NAME::getIconPixmap( fileName.toLatin1() ); } +bool EqHandle::mousePressed() const +{ + return m_mousePressed; +} + @@ -463,22 +458,6 @@ void EqHandle::setHandleActive( bool a ) -void EqHandle::setHandleMoved( bool a ) -{ - m_handleMoved = a; -} - - - - -bool EqHandle::getHandleMoved() -{ - return m_handleMoved; -} - - - - void EqHandle::sethp12() { m_hp12 = true; diff --git a/plugins/Eq/EqCurve.h b/plugins/Eq/EqCurve.h index 52e39a520bd..58dfa2ac92b 100644 --- a/plugins/Eq/EqCurve.h +++ b/plugins/Eq/EqCurve.h @@ -71,8 +71,7 @@ class EqHandle : public QGraphicsObject void setMouseHover( bool d ); bool isActiveHandle(); void setHandleActive( bool a ); - void setHandleMoved(bool a); - bool getHandleMoved(); + bool mousePressed() const; void sethp12(); void sethp24(); void sethp48(); @@ -95,7 +94,6 @@ class EqHandle : public QGraphicsObject private: double calculateGain( const double freq, const double a1, const double a2, const double b0, const double b1, const double b2 ); void loadPixmap(); - float m_pixelsPerUnitWidth; float m_pixelsPerUnitHeight; float m_scale; @@ -111,11 +109,7 @@ class EqHandle : public QGraphicsObject float m_resonance; bool m_mousePressed; bool m_active; - bool m_handleMoved; QPixmap m_circlePixmap; -private slots: - void handleMoved(); - }; diff --git a/plugins/Eq/EqParameterWidget.cpp b/plugins/Eq/EqParameterWidget.cpp index 4ccdfbb2fa3..be2174a2b36 100644 --- a/plugins/Eq/EqParameterWidget.cpp +++ b/plugins/Eq/EqParameterWidget.cpp @@ -104,7 +104,7 @@ void EqParameterWidget::updateHandle() m_eqcurve->setModelChanged( true ); for( int i = 0 ; i < bandCount(); i++ ) { - if ( m_handleList->at( i )->getHandleMoved() == false ) //prevents a short circuit between handle and data model + if ( !m_handleList->at( i )->mousePressed() ) //prevents a short circuit between handle and data model { //sets the band on active if a fader or a knob is moved bool hover = false; // prevents an action if handle is moved @@ -126,7 +126,6 @@ void EqParameterWidget::updateHandle() else { m_handleList->at( i )->setHandleActive( m_bands[i].active->value() ); - m_handleList->at( i )->setHandleMoved( false ); } } if ( m_bands[0].hp12->value() ) m_handleList->at( 0 )->sethp12(); diff --git a/plugins/LadspaEffect/CMakeLists.txt b/plugins/LadspaEffect/CMakeLists.txt index 8ab9685c74d..029cd9168b5 100644 --- a/plugins/LadspaEffect/CMakeLists.txt +++ b/plugins/LadspaEffect/CMakeLists.txt @@ -2,6 +2,10 @@ INCLUDE(BuildPlugin) # Disable C++11 REMOVE_DEFINITIONS(-std=c++0x) +# Enable C++11 for CXXFLAGS only and not for Windows +IF(NOT LMMS_BUILD_WIN32) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") +ENDIF() BUILD_PLUGIN(ladspaeffect LadspaEffect.cpp LadspaControls.cpp LadspaControlDialog.cpp LadspaSubPluginFeatures.cpp LadspaEffect.h LadspaControls.h LadspaControlDialog.h LadspaSubPluginFeatures.h MOCFILES LadspaEffect.h LadspaControls.h LadspaControlDialog.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/LadspaEffect/calf/src/audio_fx.cpp b/plugins/LadspaEffect/calf/src/audio_fx.cpp index f42ccc0a834..721a32ad571 100644 --- a/plugins/LadspaEffect/calf/src/audio_fx.cpp +++ b/plugins/LadspaEffect/calf/src/audio_fx.cpp @@ -731,6 +731,7 @@ void lookahead_limiter::process(float &left, float &right, float * multi_buffer) _peak = fabs(buffer[nextpos[j]]) > fabs(buffer[nextpos[j] + 1]) ? fabs(buffer[nextpos[j]]) : fabs(buffer[nextpos[j] + 1]); // calc a delta to use to reach our incoming peak from the // stored position + _peak = std::max( _peak, 0.000001f ); _delta = (_limit / peak - (limit * _multi_coeff * weight) / _peak) / (((buffer_size - nextpos[j] + pos) % buffer_size) / channels); if(_delta < nextdelta[j]) { // if the buffered delta is more important than the delta diff --git a/plugins/LadspaEffect/caps/SweepVF.cc b/plugins/LadspaEffect/caps/SweepVF.cc index f97c220cfb4..3dab97d7efc 100644 --- a/plugins/LadspaEffect/caps/SweepVF.cc +++ b/plugins/LadspaEffect/caps/SweepVF.cc @@ -28,10 +28,10 @@ 02111-1307, USA or point your web browser to http://www.gnu.org. */ -#include - #include "basics.h" +#include + #include "SweepVF.h" #include "Descriptor.h" diff --git a/plugins/LadspaEffect/swh/dyson_compress_1403.c b/plugins/LadspaEffect/swh/dyson_compress_1403.c index 25e0b7d64cc..27062f051a6 100644 --- a/plugins/LadspaEffect/swh/dyson_compress_1403.c +++ b/plugins/LadspaEffect/swh/dyson_compress_1403.c @@ -836,7 +836,7 @@ static void __attribute__((constructor)) swh_init() { D_("Release time (s)"); port_range_hints[DYSONCOMPRESS_RELEASE_TIME].HintDescriptor = LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_DEFAULT_LOW; - port_range_hints[DYSONCOMPRESS_RELEASE_TIME].LowerBound = 0; + port_range_hints[DYSONCOMPRESS_RELEASE_TIME].LowerBound = 0.0000001; port_range_hints[DYSONCOMPRESS_RELEASE_TIME].UpperBound = 1; /* Parameters for Fast compression ratio */ diff --git a/plugins/LadspaEffect/swh/hermes_filter_1200.c b/plugins/LadspaEffect/swh/hermes_filter_1200.c index 5cac646ba60..2f438648816 100644 --- a/plugins/LadspaEffect/swh/hermes_filter_1200.c +++ b/plugins/LadspaEffect/swh/hermes_filter_1200.c @@ -73,7 +73,7 @@ typedef struct { float *op; // pointer to output value } sv_filter; -inline float soft_clip(float sc_in) { +float soft_clip(float sc_in) { if ((sc_in < CLIP) && (sc_in > -CLIP)) { return sc_in; } else if (sc_in > 0.0f) { @@ -86,7 +86,7 @@ inline float soft_clip(float sc_in) { /* Store data in SVF struct, takes the sampling frequency, cutoff frequency and Q, and fills in the structure passed */ -inline void setup_svf(sv_filter *sv, float fs, float fc, float q, int t) { +void setup_svf(sv_filter *sv, float fs, float fc, float q, int t) { sv->f = 2.0f * sinf(M_PI * fc / (float)(fs * F_R)); sv->q = 2.0f * cosf(powf(q, 0.1f) * M_PI * 0.5f); sv->qnrm = sqrtf(sv->q*0.5f + 0.01f); @@ -111,7 +111,7 @@ inline void setup_svf(sv_filter *sv, float fs, float fc, float q, int t) { /* Change the frequency of a running SVF */ -inline void setup_f_svf(sv_filter *sv, const float fs, const float fc) { +void setup_f_svf(sv_filter *sv, const float fs, const float fc) { sv->f = 2.0f * sin(M_PI * fc / ((float)(fs * F_R))); } diff --git a/plugins/LadspaEffect/swh/shaper_1187.c b/plugins/LadspaEffect/swh/shaper_1187.c index c1a52906bf7..7adfb3abdf8 100644 --- a/plugins/LadspaEffect/swh/shaper_1187.c +++ b/plugins/LadspaEffect/swh/shaper_1187.c @@ -115,8 +115,8 @@ static void runShaper(LADSPA_Handle instance, unsigned long sample_count) { if (shapep < 1.0f && shapep > -1.0f) { shape = 1.0f; - } else if (shape < 0) { - shape = -1.0f / shape; + } else if (shapep < 0) { + shape = -1.0f / shapep; } else { shape = shapep; } @@ -160,8 +160,8 @@ static void runAddingShaper(LADSPA_Handle instance, unsigned long sample_count) if (shapep < 1.0f && shapep > -1.0f) { shape = 1.0f; - } else if (shape < 0) { - shape = -1.0f / shape; + } else if (shapep < 0) { + shape = -1.0f / shapep; } else { shape = shapep; } diff --git a/plugins/LadspaEffect/swh/sifter_1210.c b/plugins/LadspaEffect/swh/sifter_1210.c index 0e401e88710..f7d8b557b25 100644 --- a/plugins/LadspaEffect/swh/sifter_1210.c +++ b/plugins/LadspaEffect/swh/sifter_1210.c @@ -31,7 +31,7 @@ static void __attribute__((constructor)) swh_init(); // forward declaration #define MAX_BSIZE 1000 -inline int partition(LADSPA_Data array[], int left, int right); +int partition(LADSPA_Data array[], int left, int right); void q_sort(LADSPA_Data array[], int left, int right) { float pivot = partition(array, left, right); @@ -44,7 +44,7 @@ void q_sort(LADSPA_Data array[], int left, int right) { } } -inline int partition(LADSPA_Data array[], int left, int right) { +int partition(LADSPA_Data array[], int left, int right) { float pivot = array[left]; while (left < right) { diff --git a/plugins/LadspaEffect/swh/util/waveguide_nl.h b/plugins/LadspaEffect/swh/util/waveguide_nl.h index 398518eb0de..e882e77718c 100644 --- a/plugins/LadspaEffect/swh/util/waveguide_nl.h +++ b/plugins/LadspaEffect/swh/util/waveguide_nl.h @@ -35,7 +35,7 @@ waveguide_nl *waveguide_nl_new(int size, float fc, float da, float db) return wg; } -inline void waveguide_nl_reset(waveguide_nl *wg) +void waveguide_nl_reset(waveguide_nl *wg) { memset(wg->buffer[0], 0, wg->size * sizeof(float)); memset(wg->buffer[1], 0, wg->size * sizeof(float)); @@ -45,7 +45,7 @@ inline void waveguide_nl_reset(waveguide_nl *wg) wg->zm1[1] = 0.0f; } -inline void waveguide_nl_free(waveguide_nl *wg) +void waveguide_nl_free(waveguide_nl *wg) { if (!wg) { return; @@ -55,7 +55,7 @@ inline void waveguide_nl_free(waveguide_nl *wg) free(wg); } -inline void waveguide_nl_set_delay(waveguide_nl *wg, int delay) +void waveguide_nl_set_delay(waveguide_nl *wg, int delay) { if (delay > wg->size) { wg->delay = wg->size; @@ -66,18 +66,18 @@ inline void waveguide_nl_set_delay(waveguide_nl *wg, int delay) } } -inline void waveguide_nl_set_fc(waveguide_nl *wg, float fc) +void waveguide_nl_set_fc(waveguide_nl *wg, float fc) { wg->fc = fc; } -inline void waveguide_nl_set_ap(waveguide_nl *wg, float da, float db) +void waveguide_nl_set_ap(waveguide_nl *wg, float da, float db) { wg->a1a = (1.0f - da) / (1.0f + da); wg->a1b = (1.0f - db) / (1.0f + db); } -inline void waveguide_nl_process_lin(waveguide_nl *wg, float in0, float in1, float *out0, float *out1) +void waveguide_nl_process_lin(waveguide_nl *wg, float in0, float in1, float *out0, float *out1) { float tmp; @@ -103,7 +103,7 @@ inline void waveguide_nl_process_lin(waveguide_nl *wg, float in0, float in1, flo } } -inline void waveguide_nl_process(waveguide_nl *wg, float in0, float in1, float *out0, float *out1) +void waveguide_nl_process(waveguide_nl *wg, float in0, float in1, float *out0, float *out1) { float tmp; float a1; diff --git a/plugins/LadspaEffect/swh/vocoder_1337.c b/plugins/LadspaEffect/swh/vocoder_1337.c index d28975fe7e9..957a2f381e0 100644 --- a/plugins/LadspaEffect/swh/vocoder_1337.c +++ b/plugins/LadspaEffect/swh/vocoder_1337.c @@ -47,7 +47,7 @@ struct bandpasses LADSPA_Data y[MAX_BANDS]; }; -void inline doBandpasses(struct bandpasses *bands, LADSPA_Data sample, int num_bands); +void doBandpasses(struct bandpasses *bands, LADSPA_Data sample, int num_bands); struct bands_out{ LADSPA_Data decay[MAX_BANDS]; @@ -65,7 +65,7 @@ const LADSPA_Data decay_table[] = 1/250.0, 1/250.0, 1/250.0 }; -void inline doBandpasses(struct bandpasses *bands, LADSPA_Data sample, int num_bands) +void doBandpasses(struct bandpasses *bands, LADSPA_Data sample, int num_bands) { int i; for (i=0; i < num_bands; i++) diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index 0d550aae532..9221ad4f28e 100644 --- a/plugins/carlabase/carla.cpp +++ b/plugins/carlabase/carla.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -150,15 +151,22 @@ CarlaInstrument::CarlaInstrument(InstrumentTrack* const instrumentTrack, const D fHost.uiName = NULL; fHost.uiParentId = 0; - // figure out prefix from dll filename + // carla/resources contains PyQt scripts required for launch QString dllName(carla_get_library_filename()); - + QString resourcesPath; #if defined(CARLA_OS_LINUX) - fHost.resourceDir = strdup(QString(dllName.split("/lib/carla")[0] + "/share/carla/resources/").toUtf8().constData()); -#else - fHost.resourceDir = NULL; + // parse prefix from dll filename + QDir path = QFileInfo(dllName).dir(); + path.cdUp(); + path.cdUp(); + resourcesPath = path.absolutePath() + "/share/carla/resources"; +#elif defined(CARLA_OS_MAC) + // assume standard install location + resourcesPath = "/Applications/Carla.app/Contents/MacOS/resources"; +#elif defined(CARLA_OS_WIN32) || defined(CARLA_OS_WIN64) + // not yet supported #endif - + fHost.resourceDir = strdup(resourcesPath.toUtf8().constData()); fHost.get_buffer_size = host_get_buffer_size; fHost.get_sample_rate = host_get_sample_rate; fHost.is_offline = host_is_offline; @@ -252,21 +260,14 @@ intptr_t CarlaInstrument::handleDispatcher(const NativeHostDispatcherOpcode opco switch (opcode) { - case NATIVE_HOST_OPCODE_NULL: - break; - case NATIVE_HOST_OPCODE_UPDATE_PARAMETER: - case NATIVE_HOST_OPCODE_UPDATE_MIDI_PROGRAM: - case NATIVE_HOST_OPCODE_RELOAD_PARAMETERS: - case NATIVE_HOST_OPCODE_RELOAD_MIDI_PROGRAMS: - case NATIVE_HOST_OPCODE_RELOAD_ALL: - // nothing - break; case NATIVE_HOST_OPCODE_UI_UNAVAILABLE: handleUiClosed(); break; case NATIVE_HOST_OPCODE_HOST_IDLE: qApp->processEvents(); break; + default: + break; } return ret; diff --git a/plugins/carlabase/carla.h b/plugins/carlabase/carla.h index 59a34a76495..6431e5300de 100644 --- a/plugins/carlabase/carla.h +++ b/plugins/carlabase/carla.h @@ -25,6 +25,8 @@ #ifndef CARLA_H #define CARLA_H +#include + #include "CarlaNative.h" #include "Instrument.h" diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 81cd46a4702..ec21e8e8ba9 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -339,7 +339,6 @@ lb302Synth::lb302Synth( InstrumentTrack * _instrumentTrack ) : // Experimenting with a0 between original (0.5) and 1.0 vca_a0 = 0.5; - vca_a = 9; vca_mode = 3; vcfs[0] = new lb302FilterIIR2(&fs); diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp index 99a3be95b56..0f8ad5b968d 100644 --- a/plugins/monstro/Monstro.cpp +++ b/plugins/monstro/Monstro.cpp @@ -1692,6 +1692,8 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) makeknob( m_osc3SpoKnob, KNOBCOL4, O3ROW, tr( "Stereo phase offset" ), tr( " deg" ), "osc3Knob" ) makeknob( m_osc3SubKnob, KNOBCOL5, O3ROW, tr( "Sub-osc mix" ), "", "osc3Knob" ) + m_osc3VolKnob -> setVolumeKnob( true ); + m_osc3Wave1Box = new ComboBox( view ); m_osc3Wave1Box -> setGeometry( 160, O3ROW + 7, 42, 22 ); m_osc3Wave1Box->setFont( pointSize<8>( m_osc3Wave1Box->font() ) ); diff --git a/plugins/papu/CMakeLists.txt b/plugins/papu/CMakeLists.txt index b45fcf61472..20d9bbd2033 100644 --- a/plugins/papu/CMakeLists.txt +++ b/plugins/papu/CMakeLists.txt @@ -1,6 +1,3 @@ INCLUDE(BuildPlugin) -# Disable C++11 -REMOVE_DEFINITIONS(-std=c++0x) - BUILD_PLUGIN(papu papu_instrument.cpp papu_instrument.h Basic_Gb_Apu.cpp Basic_Gb_Apu.h gb_apu/Gb_Oscs.cpp gb_apu/Gb_Apu.h gb_apu/Blip_Buffer.cpp gb_apu/Gb_Apu.cpp gb_apu/Gb_Oscs.h gb_apu/blargg_common.h gb_apu/Blip_Buffer.h gb_apu/Multi_Buffer.cpp gb_apu/blargg_source.h gb_apu/Multi_Buffer.h MOCFILES papu_instrument.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/sfxr/sfxr.cpp b/plugins/sfxr/sfxr.cpp index b0222a0b12b..30310576176 100644 --- a/plugins/sfxr/sfxr.cpp +++ b/plugins/sfxr/sfxr.cpp @@ -464,6 +464,7 @@ void sfxrInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffe } else if( static_cast(_n->m_pluginData)->isPlaying() == false ) { + memset(_working_buffer + offset, 0, sizeof(sampleFrame) * frameNum); _n->noteOff(); return; } diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index a9a8088411b..a3f919adf31 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -33,30 +33,32 @@ IF(LMMS_BUILD_LINUX AND NOT WANT_VST_NOWINE) IF(LMMS_HOST_X86_64) SET(EXTRA_FLAGS -m32) - - # workaround for broken wineg++ in WINE 1.4 (shipped e.g. with Ubuntu Precise) - EXEC_PROGRAM( ${WINE_CXX} ARGS "-v -m32 /dev/zero" OUTPUT_VARIABLE WINEBUILD_OUTPUT) - IF("${WINEBUILD_OUTPUT}" MATCHES ".*x86_64-linux-gnu/wine/libwinecrt0.a.*") - SET(EXTRA_FLAGS ${EXTRA_FLAGS} -nodefaultlibs /usr/lib/i386-linux-gnu/wine/libwinecrt0.a -L/usr/lib/i386-linux-gnu/wine/ -luser32 -lkernel32 -lgdi32) - ENDIF() - #The following check works on Fedora systems - IF("${WINEBUILD_OUTPUT}" MATCHES ".*lib64/wine/libwinecrt0.a.*") - SET(EXTRA_FLAGS ${EXTRA_FLAGS} -nodefaultlibs /usr/lib/i386/wine/libwinecrt0.a -luser32 -lkernel32 -lgdi32) + IF(WINE_LIBRARY_FIX) + SET(EXTRA_FLAGS ${EXTRA_FLAGS} -nodefaultlibs ${WINE_LIBRARY_FIX}wine/libwinecrt0.a -L${WINE_LIBRARY_FIX}wine/ -luser32 -lkernel32 -lgdi32) ENDIF() ENDIF(LMMS_HOST_X86_64) SET(WINE_CXX_FLAGS "" CACHE STRING "Extra flags passed to wineg++") +STRING(REPLACE "include/wine" "include" WINE_INCLUDE_BASE_DIR ${WINE_INCLUDE_DIR}) +STRING(REPLACE "lib/libwine.so" "lib" WINE_LIBRARY_DIR ${WINE_LIBRARY}) STRING(REPLACE " " ";" WINE_BUILD_FLAGS ${CMAKE_CXX_FLAGS} " " ${CMAKE_EXE_LINKER_FLAGS} " " ${WINE_CXX_FLAGS}) ADD_CUSTOM_COMMAND( - SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp" - COMMAND ${WINE_CXX} - ARGS -I${CMAKE_BINARY_DIR} -I${CMAKE_SOURCE_DIR}/include -I${CMAKE_INSTALL_PREFIX}/include/wine/windows -I${CMAKE_INSTALL_PREFIX}/include -I/usr/include/wine/windows ${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp -ansi -mwindows -lpthread ${EXTRA_FLAGS} -fno-omit-frame-pointer ${WINE_BUILD_FLAGS} -o ../RemoteVstPlugin - # Ensure correct file extension - COMMAND sh -c "mv ../RemoteVstPlugin.exe ../RemoteVstPlugin || true" - TARGET vstbase - OUTPUTS ../RemoteVstPlugin - ) + SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp" + COMMAND ${WINE_CXX} + ARGS -I${CMAKE_BINARY_DIR} + -I${CMAKE_SOURCE_DIR}/include + -I${WINE_INCLUDE_BASE_DIR} + -L${WINE_LIBRARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/RemoteVstPlugin.cpp + -ansi -mwindows -lpthread ${EXTRA_FLAGS} -fno-omit-frame-pointer + ${WINE_BUILD_FLAGS} + -o ../RemoteVstPlugin + # Ensure correct file extension + COMMAND sh -c "mv ../RemoteVstPlugin.exe ../RemoteVstPlugin || true" + TARGET vstbase + OUTPUTS ../RemoteVstPlugin +) SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ../RemoteVstPlugin.exe.so) INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin" "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin.exe.so" DESTINATION "${PLUGIN_DIR}") diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index 47da5147c8f..8bba1f10f4a 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -445,8 +445,8 @@ void ZynAddSubFxInstrument::initPlugin() RemotePlugin::message( IdZasfPresetDirectory ). addString( QSTR_TO_STDSTR( - QString( ConfigManager::inst()->factoryPresetsDir() + - QDir::separator() + "ZynAddSubFX" ) ) ) ); + QDir( ConfigManager::inst()->factoryPresetsDir() + + "/ZynAddSubFX" ).absolutePath() ) ) ); m_remotePlugin->updateSampleRate( Engine::mixer()->processingSampleRate() ); diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt new file mode 100644 index 00000000000..4067413e3d5 --- /dev/null +++ b/src/3rdparty/CMakeLists.txt @@ -0,0 +1,4 @@ +set(CMAKE_C_FLAGS "") +set(CMAKE_CXX_FLAGS "") + +ADD_SUBDIRECTORY(rpmalloc) diff --git a/src/3rdparty/rpmalloc/CMakeLists.txt b/src/3rdparty/rpmalloc/CMakeLists.txt new file mode 100644 index 00000000000..23d1551c22b --- /dev/null +++ b/src/3rdparty/rpmalloc/CMakeLists.txt @@ -0,0 +1,37 @@ +set(CMAKE_C_FLAGS "-std=c11") + +add_library(rpmalloc STATIC + rpmalloc/rpmalloc/rpmalloc.c + rpmalloc/rpmalloc/rpmalloc.h +) + +target_include_directories(rpmalloc PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/rpmalloc/rpmalloc +) + +if (NOT LMMS_BUILD_WIN32) + target_compile_definitions(rpmalloc + PRIVATE -D_GNU_SOURCE + ) +endif() + +if (CMAKE_BUILD_TYPE STREQUAL "Debug") + # rpmalloc uses GCC builtin "__builtin_umull_overflow" with ENABLE_VALIDATE_ARGS, + # which is only available starting with GCC 5 + if (CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 5) + set(ENABLE_VALIDATE_ARGS OFF) + else () + set(ENABLE_VALIDATE_ARGS ON) + endif() + target_compile_definitions(rpmalloc + PRIVATE -DENABLE_ASSERTS=1 -DENABLE_VALIDATE_ARGS=${ENABLE_VALIDATE_ARGS} + ) +endif() + +option(LMMS_ENABLE_MALLOC_STATS "Enables statistics for rpmalloc" OFF) + +if (LMMS_ENABLE_MALLOC_STATS) + target_compile_definitions(rpmalloc + PRIVATE -DENABLE_STATISTICS=1 + ) +endif() diff --git a/src/3rdparty/rpmalloc/rpmalloc b/src/3rdparty/rpmalloc/rpmalloc new file mode 160000 index 00000000000..2e0479192b8 --- /dev/null +++ b/src/3rdparty/rpmalloc/rpmalloc @@ -0,0 +1 @@ +Subproject commit 2e0479192b8dfb15e0084969fdf06208cffbfd09 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 154ff4f85ae..c98f304e14a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,5 @@ +ADD_SUBDIRECTORY(3rdparty) + CONFIGURE_FILE("lmmsconfig.h.in" "${CMAKE_BINARY_DIR}/lmmsconfig.h") CONFIGURE_FILE("lmmsversion.h.in" "${CMAKE_BINARY_DIR}/lmmsversion.h") @@ -8,7 +10,7 @@ SET(CMAKE_AUTOMOC ON) SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) # Enable C++11 -ADD_DEFINITIONS(-std=c++0x) +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") IF(LMMS_BUILD_APPLE) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") @@ -147,7 +149,9 @@ SET(LMMS_REQUIRED_LIBS ${SAMPLERATE_LIBRARIES} ${SNDFILE_LIBRARIES} ${EXTRA_LIBRARIES} + rpmalloc ) + # Expose required libs for tests binary SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS} PARENT_SCOPE) @@ -155,6 +159,16 @@ TARGET_LINK_LIBRARIES(lmms ${LMMS_REQUIRED_LIBS} ) +FOREACH(LIB ${LMMS_REQUIRED_LIBS}) + IF(TARGET ${LIB}) + GET_TARGET_PROPERTY(INCLUDE_DIRS ${LIB} INTERFACE_INCLUDE_DIRECTORIES) + if (INCLUDE_DIRS) + TARGET_INCLUDE_DIRECTORIES(lmmsobjs PRIVATE ${INCLUDE_DIRS}) + ENDIF() + ENDIF() +ENDFOREACH() + + # Required libs for debug msys builds IF(LMMS_BUILD_MSYS AND CMAKE_BUILD_TYPE STREQUAL "Debug") TARGET_LINK_LIBRARIES(lmms QtCore4 QtGui4 QtXml4) diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index 0391061b65f..e2a088e04dd 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -42,8 +42,6 @@ AutomatableModel::AutomatableModel( DataType type, Model( parent, displayName, defaultConstructed ), m_dataType( type ), m_scaleType( Linear ), - m_value( val ), - m_initValue( val ), m_minValue( min ), m_maxValue( max ), m_step( step ), @@ -59,6 +57,7 @@ AutomatableModel::AutomatableModel( DataType type, m_hasSampleExactData( false ) { + m_value = fittedValue( val ); setInitValue( val ); } @@ -523,14 +522,8 @@ float AutomatableModel::controllerValue( int frameOffset ) const ValueBuffer * AutomatableModel::valueBuffer() { - // if we've already calculated the valuebuffer this period, return the cached buffer - if( m_lastUpdatedPeriod == s_periodCounter ) - { - return m_hasSampleExactData - ? &m_valueBuffer - : NULL; - } QMutexLocker m( &m_valueBufferMutex ); + // if we've already calculated the valuebuffer this period, return the cached buffer if( m_lastUpdatedPeriod == s_periodCounter ) { return m_hasSampleExactData @@ -626,6 +619,7 @@ void AutomatableModel::setInitValue( const float value ) m_initValue = fittedValue( value ); bool journalling = testAndSetJournalling( false ); setValue( value ); + m_oldValue = m_value; setJournalling( journalling ); emit initValueChanged( value ); } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5dcfa3c4209..30566e5ae2d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,3 +1,16 @@ +IF(LMMS_HAVE_WEAKJACK) + set(WEAKJACK core/audio/AudioWeakJack.c) + + # Build libjack.so.0 stub as weakjack.so for AppImages + IF(LMMS_BUILD_LINUX) + ADD_LIBRARY(weakjack MODULE ../../src/core/audio/AudioWeakJack.c) + INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include") + # We can't predict an AppImage build, so stash the build artifact for later + INSTALL(TARGETS weakjack LIBRARY DESTINATION "${CMAKE_BINARY_DIR}/optional") + SET_TARGET_PROPERTIES(weakjack PROPERTIES PREFIX "" SUFFIX ".so") + ENDIF() +ENDIF() + set(LMMS_SRCS ${LMMS_SRCS} core/AutomatableModel.cpp @@ -72,6 +85,7 @@ set(LMMS_SRCS core/audio/AudioFileMP3.cpp core/audio/AudioFileOgg.cpp core/audio/AudioFileWave.cpp + ${WEAKJACK} core/audio/AudioJack.cpp core/audio/AudioOss.cpp core/audio/AudioSndio.cpp diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 5ccb4d345d4..231475002f1 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -503,7 +503,16 @@ void ConfigManager::loadConfigFile( const QString & configFile ) #elif defined(LMMS_BUILD_APPLE) m_stkDir = qApp->applicationDirPath() + "/../share/stk/rawwaves/"; #else - m_stkDir = "/usr/share/stk/rawwaves/"; + if ( qApp->applicationDirPath().startsWith("/tmp/") ) + { + // Assume AppImage bundle + m_stkDir = qApp->applicationDirPath() + "/../share/stk/rawwaves/"; + } + else + { + // Fallback to system provided location + m_stkDir = "/usr/share/stk/rawwaves/"; + } #endif } #endif diff --git a/src/core/InstrumentSoundShaping.cpp b/src/core/InstrumentSoundShaping.cpp index 22327ae8eed..56011de04c5 100644 --- a/src/core/InstrumentSoundShaping.cpp +++ b/src/core/InstrumentSoundShaping.cpp @@ -299,13 +299,22 @@ f_cnt_t InstrumentSoundShaping::envFrames( const bool _only_vol ) const f_cnt_t InstrumentSoundShaping::releaseFrames() const { + if( !m_instrumentTrack->instrument() ) + { + return 0; + } + + f_cnt_t ret_val = m_instrumentTrack->instrument()->desiredReleaseFrames(); + + if( m_instrumentTrack->instrument()->flags().testFlag( Instrument::IsSingleStreamed ) ) + { + return ret_val; + } + if( m_envLfoParameters[Volume]->isUsed() ) { return m_envLfoParameters[Volume]->releaseFrames(); } - f_cnt_t ret_val = m_instrumentTrack->instrument() - ? m_instrumentTrack->instrument()->desiredReleaseFrames() - : 0; for( int i = Volume+1; i < NumTargets; ++i ) { diff --git a/src/core/MemoryManager.cpp b/src/core/MemoryManager.cpp index 4196d1aacd6..d0932e4f17b 100644 --- a/src/core/MemoryManager.cpp +++ b/src/core/MemoryManager.cpp @@ -1,8 +1,7 @@ /* - * MemoryManager.cpp - A lightweight, generic memory manager for LMMS + * MemoryManager.cpp * - * Copyright (c) 2014 Vesa Kivimäki - * Copyright (c) 2007-2014 Tobias Doerffel + * Copyright (c) 2017 Lukas W * * This file is part of LMMS - https://lmms.io * @@ -25,197 +24,54 @@ #include "MemoryManager.h" -#include +#include +#include "rpmalloc.h" -MemoryPoolVector MemoryManager::s_memoryPools; -QReadWriteLock MemoryManager::s_poolMutex; -PointerInfoMap MemoryManager::s_pointerInfo; -QMutex MemoryManager::s_pointerMutex; - - -bool MemoryManager::init() -{ - s_memoryPools.reserve( 64 ); - s_pointerInfo.reserve( 4096 ); - // construct first MemoryPool and allocate memory - MemoryPool m ( MM_INITIAL_CHUNKS ); - m.m_pool = MemoryHelper::alignedMalloc( MM_INITIAL_CHUNKS * MM_CHUNK_SIZE ); - s_memoryPools.append( m ); - return true; -} - - -void * MemoryManager::alloc( size_t size ) -{ - if( !size ) - { - return NULL; +/// Global static object handling rpmalloc intializing and finalizing +struct MemoryManagerGlobalGuard { + MemoryManagerGlobalGuard() { + rpmalloc_initialize(); } - - int requiredChunks = size / MM_CHUNK_SIZE + ( size % MM_CHUNK_SIZE > 0 ? 1 : 0 ); - - MemoryPool * mp = NULL; - void * ptr = NULL; - - MemoryPoolVector::iterator it = s_memoryPools.begin(); - - s_poolMutex.lockForRead(); - while( it != s_memoryPools.end() && !ptr ) - { - ptr = ( *it ).getChunks( requiredChunks ); - if( ptr ) - { - mp = &( *it ); - } - ++it; - } - s_poolMutex.unlock(); - - if( ptr ) - { - s_pointerMutex.lock(); - PtrInfo p; - p.chunks = requiredChunks; - p.memPool = mp; - s_pointerInfo[ptr] = p; - s_pointerMutex.unlock(); - return ptr; + ~MemoryManagerGlobalGuard() { + rpmalloc_finalize(); } +} static mm_global_guard; - // can't find enough chunks in existing pools, so - // create a new pool that is guaranteed to have enough chunks - int moreChunks = qMax( requiredChunks, MM_INCREMENT_CHUNKS ); - int i = MemoryManager::extend( moreChunks ); - mp = &s_memoryPools[i]; - ptr = s_memoryPools[i].getChunks( requiredChunks ); - if( ptr ) - { - s_pointerMutex.lock(); - PtrInfo p; - p.chunks = requiredChunks; - p.memPool = mp; - s_pointerInfo[ptr] = p; - s_pointerMutex.unlock(); - return ptr; - } - // still no luck? something is horribly wrong - qFatal( "MemoryManager.cpp: Couldn't allocate memory: %d chunks asked", requiredChunks ); - return NULL; +namespace { +static thread_local size_t thread_guard_depth; } - -void MemoryManager::free( void * ptr ) +MemoryManager::ThreadGuard::ThreadGuard() { - if( !ptr ) - { - return; // Null pointer deallocations are OK but do not need to be handled + if (thread_guard_depth++ == 0) { + rpmalloc_thread_initialize(); } - - // fetch info on the ptr and remove - s_pointerMutex.lock(); - if( ! s_pointerInfo.contains( ptr ) ) // if we have no info on ptr, fail loudly - { - qFatal( "MemoryManager: Couldn't find pointer info for pointer: %p", ptr ); - } - PtrInfo p = s_pointerInfo[ptr]; - s_pointerInfo.remove( ptr ); - s_pointerMutex.unlock(); - - p.memPool->releaseChunks( ptr, p.chunks ); -} - - -int MemoryManager::extend( int chunks ) -{ - MemoryPool m ( chunks ); - m.m_pool = MemoryHelper::alignedMalloc( chunks * MM_CHUNK_SIZE ); - - s_poolMutex.lockForWrite(); - s_memoryPools.append( m ); - int i = s_memoryPools.size() - 1; - s_poolMutex.unlock(); - - return i; } - -void MemoryManager::cleanup() +MemoryManager::ThreadGuard::~ThreadGuard() { - for( MemoryPoolVector::iterator it = s_memoryPools.begin(); it != s_memoryPools.end(); ++it ) - { - MemoryHelper::alignedFree( ( *it ).m_pool ); - MemoryHelper::alignedFree( ( *it ).m_free ); + if (--thread_guard_depth == 0) { + rpmalloc_thread_finalize(); } } +static thread_local MemoryManager::ThreadGuard local_mm_thread_guard{}; -void * MemoryPool::getChunks( int chunksNeeded ) +void* MemoryManager::alloc(size_t size) { - if( chunksNeeded > m_chunks ) // not enough chunks in this pool? - { - return NULL; - } - - m_mutex.lock(); - - // now find out if we have a long enough sequence of chunks in this pool - char last = 0; - intptr_t n = 0; - intptr_t index = -1; - bool found = false; - - for( int i = 0; i < m_chunks; ++i ) - { - if( m_free[i] ) - { - if( !last ) - { - index = i; - } - - ++n; - if( n >= chunksNeeded ) - { - found = true; - break; - } - } - else - { - n = 0; - } - - last = m_free[i]; - } - - if( found ) // if enough chunks found, return pointer to chunks - { - // set chunk flags to false so we know the chunks are in use - for( intptr_t i = 0; i < chunksNeeded; ++i ) - { - m_free[ index + i ] = 0; - } - m_mutex.unlock(); - return (char*)m_pool + ( index * MM_CHUNK_SIZE ); - } - m_mutex.unlock(); - return NULL; // out of stock, come again tomorrow! + // Reference local thread guard to ensure it is initialized. + // Compilers may optimize the instance away otherwise. + Q_UNUSED(&local_mm_thread_guard); + Q_ASSERT_X(rpmalloc_is_thread_initialized(), "MemoryManager::alloc", "Thread not initialized"); + return rpmalloc(size); } -void MemoryPool::releaseChunks( void * ptr, int chunks ) +void MemoryManager::free(void * ptr) { - m_mutex.lock(); - - intptr_t start = ( (intptr_t)ptr - (intptr_t)m_pool ) / MM_CHUNK_SIZE; - if( start < 0 ) - { - qFatal( "MemoryManager: error at releaseChunks() - corrupt pointer info?" ); - } - - memset( &m_free[ start ], 1, chunks ); - - m_mutex.unlock(); + Q_UNUSED(&local_mm_thread_guard); + Q_ASSERT_X(rpmalloc_is_thread_initialized(), "MemoryManager::free", "Thread not initialized"); + return rpfree(ptr); } diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 53cacbe6386..ed8d04a6ce8 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -36,6 +36,7 @@ #include "NotePlayHandle.h" #include "ConfigManager.h" #include "SamplePlayHandle.h" +#include "MemoryHelper.h" // platform-specific audio-interface-classes #include "AudioAlsa.h" @@ -500,6 +501,20 @@ void Mixer::clear() +void Mixer::clearNewPlayHandles() +{ + requestChangeInModel(); + for( LocklessListElement * e = m_newPlayHandles.popList(); e; ) + { + LocklessListElement * next = e->next; + m_newPlayHandles.free( e ); + e = next; + } + doneChangeInModel(); +} + + + // removes all play-handles. this is necessary, when the song is stopped -> // all remaining notes etc. would be played until their end void Mixer::clearInternal() diff --git a/src/core/MixerWorkerThread.cpp b/src/core/MixerWorkerThread.cpp index dfcc1ff6a51..e8c6bd61c08 100644 --- a/src/core/MixerWorkerThread.cpp +++ b/src/core/MixerWorkerThread.cpp @@ -153,6 +153,7 @@ void MixerWorkerThread::startAndWaitForJobs() void MixerWorkerThread::run() { + MemoryManager::ThreadGuard mmThreadGuard; Q_UNUSED(mmThreadGuard); disable_denormals(); QMutex m; diff --git a/src/core/NotePlayHandle.cpp b/src/core/NotePlayHandle.cpp index 84d888fee6b..11f71d8f19b 100644 --- a/src/core/NotePlayHandle.cpp +++ b/src/core/NotePlayHandle.cpp @@ -128,7 +128,7 @@ NotePlayHandle::NotePlayHandle( InstrumentTrack* instrumentTrack, } -void NotePlayHandle::done() +NotePlayHandle::~NotePlayHandle() { lock(); noteOff( 0 ); @@ -599,7 +599,7 @@ NotePlayHandle * NotePlayHandleManager::acquire( InstrumentTrack* instrumentTrac void NotePlayHandleManager::release( NotePlayHandle * nph ) { - nph->done(); + nph->NotePlayHandle::~NotePlayHandle(); s_mutex.lockForRead(); s_available[ s_availableIndex.fetchAndAddOrdered( 1 ) + 1 ] = nph; s_mutex.unlock(); diff --git a/src/core/PlayHandle.cpp b/src/core/PlayHandle.cpp index 5481ea3e2bb..9e92019a6b6 100644 --- a/src/core/PlayHandle.cpp +++ b/src/core/PlayHandle.cpp @@ -54,6 +54,7 @@ void PlayHandle::doProcessing() if( m_usesBuffer ) { m_bufferReleased = false; + BufferManager::clear(m_playHandleBuffer, Engine::mixer()->framesPerPeriod()); play( buffer() ); } else diff --git a/src/core/PresetPreviewPlayHandle.cpp b/src/core/PresetPreviewPlayHandle.cpp index dd57e9f9c8e..b85d02474a3 100644 --- a/src/core/PresetPreviewPlayHandle.cpp +++ b/src/core/PresetPreviewPlayHandle.cpp @@ -22,6 +22,7 @@ * */ +#include #include #include "PresetPreviewPlayHandle.h" @@ -66,12 +67,25 @@ class PreviewTrackContainer : public TrackContainer NotePlayHandle* previewNote() { + #if QT_VERSION >= 0x050000 + return m_previewNote.loadAcquire(); + #else return m_previewNote; + #endif } void setPreviewNote( NotePlayHandle * _note ) { + #if QT_VERSION >= 0x050000 + m_previewNote.storeRelease( _note ); + #else m_previewNote = _note; + #endif + } + + bool testAndSetPreviewNote( NotePlayHandle * expectedVal, NotePlayHandle * newVal ) + { + return m_previewNote.testAndSetOrdered( expectedVal, newVal ); } void lockData() @@ -97,7 +111,7 @@ class PreviewTrackContainer : public TrackContainer private: InstrumentTrack* m_previewInstrumentTrack; - NotePlayHandle* m_previewNote; + QAtomicPointer m_previewNote; QMutex m_dataMutex; friend class PresetPreviewPlayHandle; @@ -113,15 +127,14 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, PlayHandle( TypePresetPreviewHandle ), m_previewNote( NULL ) { - s_previewTC->lockData(); - setUsesBuffer( false ); - if( s_previewTC->previewNote() != NULL ) - { - s_previewTC->previewNote()->mute(); - } + s_previewTC->lockData(); + Engine::mixer()->requestChangeInModel(); + s_previewTC->setPreviewNote( nullptr ); + s_previewTC->previewInstrumentTrack()->silenceAllNotes(); + Engine::mixer()->doneChangeInModel(); const bool j = Engine::projectJournal()->isJournalling(); Engine::projectJournal()->setJournalling( false ); @@ -174,6 +187,7 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, s_previewTC->previewInstrumentTrack()-> midiPort()->setMode( MidiPort::Disabled ); + Engine::mixer()->requestChangeInModel(); // create note-play-handle for it m_previewNote = NotePlayHandleManager::acquire( s_previewTC->previewInstrumentTrack(), 0, @@ -186,6 +200,7 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, Engine::mixer()->addPlayHandle( m_previewNote ); + Engine::mixer()->doneChangeInModel(); s_previewTC->unlockData(); Engine::projectJournal()->setJournalling( j ); } @@ -195,15 +210,13 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, PresetPreviewPlayHandle::~PresetPreviewPlayHandle() { - s_previewTC->lockData(); + Engine::mixer()->requestChangeInModel(); // not muted by other preset-preview-handle? - if( !m_previewNote->isMuted() ) + if (s_previewTC->testAndSetPreviewNote(m_previewNote, nullptr)) { - // then set according state - s_previewTC->setPreviewNote( NULL ); + m_previewNote->noteOff(); } - m_previewNote->noteOff(); - s_previewTC->unlockData(); + Engine::mixer()->doneChangeInModel(); } @@ -228,7 +241,7 @@ bool PresetPreviewPlayHandle::isFinished() const bool PresetPreviewPlayHandle::isFromTrack( const Track * _track ) const { - return s_previewTC->previewInstrumentTrack() == _track; + return s_previewTC && s_previewTC->previewInstrumentTrack() == _track; } @@ -258,13 +271,11 @@ ConstNotePlayHandleList PresetPreviewPlayHandle::nphsOfInstrumentTrack( const InstrumentTrack * _it ) { ConstNotePlayHandleList cnphv; - s_previewTC->lockData(); if( s_previewTC->previewNote() != NULL && s_previewTC->previewNote()->instrumentTrack() == _it ) { cnphv.push_back( s_previewTC->previewNote() ); } - s_previewTC->unlockData(); return cnphv; } diff --git a/src/core/ProjectRenderer.cpp b/src/core/ProjectRenderer.cpp index 7588401c2da..56e9c01c0a9 100644 --- a/src/core/ProjectRenderer.cpp +++ b/src/core/ProjectRenderer.cpp @@ -163,6 +163,7 @@ void ProjectRenderer::startProcessing() void ProjectRenderer::run() { + MemoryManager::ThreadGuard mmThreadGuard; Q_UNUSED(mmThreadGuard); #if 0 #ifdef LMMS_BUILD_LINUX #ifdef LMMS_HAVE_SCHED_H diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 01071668c7a..5f8e7532070 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -86,6 +86,7 @@ Song::Song() : m_playing( false ), m_paused( false ), m_loadingProject( false ), + m_isCancelled( false ), m_playMode( Mode_None ), m_length( 0 ), m_patternToPlay( NULL ), @@ -1074,7 +1075,7 @@ void Song::loadProject( const QString & fileName ) } } - while( !node.isNull() ) + while( !node.isNull() && !isCancelled() ) { if( node.isElement() ) { @@ -1133,6 +1134,13 @@ void Song::loadProject( const QString & fileName ) emit projectLoaded(); + if( isCancelled() ) + { + m_isCancelled = false; + createNewProject(); + return; + } + if ( hasErrors()) { if ( gui ) @@ -1278,7 +1286,7 @@ void Song::saveControllerStates( QDomDocument & doc, QDomElement & element ) void Song::restoreControllerStates( const QDomElement & element ) { QDomNode node = element.firstChild(); - while( !node.isNull() ) + while( !node.isNull() && !isCancelled() ) { Controller * c = Controller::create( node.toElement(), this ); Q_ASSERT( c != NULL ); diff --git a/src/core/TrackContainer.cpp b/src/core/TrackContainer.cpp index 6e406b0a992..edea9aa14b7 100644 --- a/src/core/TrackContainer.cpp +++ b/src/core/TrackContainer.cpp @@ -33,12 +33,14 @@ #include "AutomationTrack.h" #include "BBTrack.h" #include "BBTrackContainer.h" +#include "embed.h" #include "TrackContainer.h" #include "InstrumentTrack.h" #include "Song.h" #include "GuiApplication.h" #include "MainWindow.h" +#include "TextFloat.h" TrackContainer::TrackContainer() : Model( NULL ), @@ -110,6 +112,14 @@ void TrackContainer::loadSettings( const QDomElement & _this ) QEventLoop::AllEvents, 100 ); if( pd->wasCanceled() ) { + if ( gui ) + { + TextFloat::displayMessage( tr( "Loading cancelled" ), + tr( "Project loading was cancelled." ), + embed::getIconPixmap( "project_file", 24, 24 ), + 2000 ); + } + Engine::getSong()->loadingCancelled(); break; } } @@ -286,6 +296,9 @@ AutomatedValueMap TrackContainer::automatedValuesFromTracks(const TrackList &tra continue; } MidiTime relTime = time - p->startPosition(); + if (! p->getAutoResize()) { + relTime = qMin(relTime, p->length()); + } float value = p->valueAt(relTime); for (AutomatableModel* model : p->objects()) diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index 24f7b2f4fb0..4d730eed412 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -71,6 +71,7 @@ AudioJack::AudioJack( bool & _success_ful, Mixer* _mixer ) : AudioJack::~AudioJack() { + stopProcessing(); #ifdef AUDIO_PORT_SUPPORT while( m_portMap.size() ) { @@ -200,6 +201,7 @@ bool AudioJack::initJackClient() void AudioJack::startProcessing() { + QMutexLocker m( &m_processingMutex ); m_stopped = false; if( m_active || m_client == NULL ) @@ -252,6 +254,8 @@ void AudioJack::startProcessing() void AudioJack::stopProcessing() { + QMutexLocker m( &m_processingMutex ); + m_stopped = true; } @@ -338,6 +342,8 @@ void AudioJack::renamePort( AudioPort * _port ) int AudioJack::processCallback( jack_nframes_t _nframes, void * _udata ) { + QMutexLocker m( &m_processingMutex ); + // do midi processing first so that midi input can // add to the following sound processing if( m_midiClient && _nframes > 0 ) @@ -397,15 +403,15 @@ int AudioJack::processCallback( jack_nframes_t _nframes, void * _udata ) if( m_framesDoneInCurBuf == m_framesToDoInCurBuf ) { m_framesToDoInCurBuf = getNextBuffer( m_outBuf ); + m_framesDoneInCurBuf = 0; if( !m_framesToDoInCurBuf ) { - m_stopped = true; + break; } - m_framesDoneInCurBuf = 0; } } - if( m_stopped == true ) + if( _nframes != done ) { for( int c = 0; c < channels(); ++c ) { diff --git a/src/core/audio/AudioWeakJack.c b/src/core/audio/AudioWeakJack.c new file mode 100644 index 00000000000..5cf93cdd839 --- /dev/null +++ b/src/core/audio/AudioWeakJack.c @@ -0,0 +1,273 @@ +/* runtime/weak dynamic JACK linking + * + * (C) 2014 Robin Gareus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "AudioWeakJack.h" + +#ifndef USE_WEAK_JACK + +int have_libjack (void) { + return 0; +} + +#else + +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#endif + +static void* lib_open(const char* const so) { +#ifdef _WIN32 + return (void*) LoadLibraryA(so); +#else + return dlopen(so, RTLD_NOW|RTLD_LOCAL); +#endif +} + +static void* lib_symbol(void* const lib, const char* const sym) { +#ifdef _WIN32 + return (void*) GetProcAddress((HMODULE)lib, sym); +#else + return dlsym(lib, sym); +#endif +} + +#if _MSC_VER && !__INTEL_COMPILER +typedef void * pvoid_t; +#define MAPSYM(SYM, FAIL) _j._ ## SYM = (func_t)lib_symbol(lib, "jack_" # SYM); \ + if (!_j._ ## SYM) err |= FAIL; +#elif defined NDEBUG +typedef void * __attribute__ ((__may_alias__)) pvoid_t; +#define MAPSYM(SYM, FAIL) *(pvoid_t *)(&_j._ ## SYM) = lib_symbol(lib, "jack_" # SYM); \ + if (!_j._ ## SYM) err |= FAIL; +#else +typedef void * __attribute__ ((__may_alias__)) pvoid_t; +#define MAPSYM(SYM, FAIL) *(pvoid_t *)(&_j._ ## SYM) = lib_symbol(lib, "jack_" # SYM); \ + if (!_j._ ## SYM) { \ + if (FAIL) { \ + fprintf(stderr, "*** WEAK-JACK: required symbol 'jack_%s' was not found\n", "" # SYM); \ + } \ + err |= FAIL; \ + } +#endif + +typedef void (* func_t) (void); + +/* function pointers to the real jack API */ +static struct WeakJack { + func_t _client_open; // special case due to varargs + +#define JCFUN(ERR, RTYPE, NAME, RVAL) func_t _ ## NAME ; +#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) func_t _ ## NAME ; +#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) func_t _ ## NAME ; +#define JVFUN(ERR, NAME, DEF, ARGS, CODE) func_t _ ## NAME ; + +#include "AudioWeakJack.def" + +#undef JCFUN +#undef JPFUN +#undef JXFUN +#undef JVFUN +} _j; + +static int _status = -1; + +__attribute__((constructor)) +static void init_weak_jack(void) +{ + void* lib; + int err = 0; +#ifndef NDEBUG + fprintf(stderr, "*** WEAK-JACK: initializing\n"); +#endif + + memset(&_j, 0, sizeof(_j)); + +#ifdef __APPLE__ + lib = lib_open("libjack.dylib"); + if (!lib) { + lib = lib_open("/usr/local/lib/libjack.dylib"); + } +#elif (defined _WIN32) +# ifdef __x86_64__ + lib = lib_open("libjack64.dll"); +# else + lib = lib_open("libjack.dll"); +# endif +#else + lib = lib_open("libjack.so.0"); +#endif + if (!lib) { +#ifndef NDEBUG + fprintf(stderr, "*** WEAK-JACK: libjack was not found\n"); +#endif + _status = -2; + return; + } + + /* found library, now lookup functions */ + MAPSYM(client_open, 2) + +#define JCFUN(ERR, RTYPE, NAME, RVAL) MAPSYM(NAME, ERR) +#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) MAPSYM(NAME, ERR) +#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) MAPSYM(NAME, ERR) +#define JVFUN(ERR, NAME, DEF, ARGS, CODE) MAPSYM(NAME, ERR) + +#include "AudioWeakJack.def" + +#undef JCFUN +#undef JPFUN +#undef JXFUN +#undef JVFUN + + /* if a required symbol is not found, disable JACK completly */ + if (err) { + _j._client_open = NULL; + } + _status = err; +#ifndef NDEBUG + fprintf(stderr, "*** WEAK-JACK: %s. (%d)\n", err ? "jack is not available" : "OK", _status); +#endif +} + +int have_libjack (void) { + if (_status == -1) { + init_weak_jack(); + } + return _status; +} + +/******************************************************************************* + * helper macros + */ + +#if defined(__GNUC__) && (__GNUC__ > 2) && !defined(NDEBUG) +#define likely(expr) (__builtin_expect (!!(expr), 1)) +#else +#define likely(expr) (expr) +#endif + +#ifndef NDEBUG +# define WJACK_WARNING(NAME) \ + fprintf(stderr, "*** WEAK-JACK: function 'jack_%s' ignored\n", "" # NAME); +#else +# define WJACK_WARNING(NAME) ; +#endif + +/****************************************************************************** + * JACK API wrapper functions. + * + * if a function pointer is set in the static struct WeakJack _j, + * the function is called directly. + * Otherwise a dummy NOOP implementation is provided. + * The latter is mainly for compile-time warnings. + * + * If libjack is not found, jack_client_open() will fail. + * In that case the application should not call any other libjack + * functions. Hence a real implementation is not needed. + * (jack ringbuffer may be an exception for some apps) + */ + +/* dedicated support for jack_client_open(,..) variable arg function macro */ +func_t WJACK_get_client_open(void) { + if (_status == -1) { + init_weak_jack(); + } + return _j._client_open; +} + +/* callback to set status */ +jack_client_t * WJACK_no_client_open (const char *client_name, jack_options_t options, jack_status_t *status, ...) { + WJACK_WARNING(client_open); + if (status) { *status = JackFailure; } + return NULL; +} + +/******************************************************************************* + * Macros to wrap jack API + */ + +/* abstraction for jack_client functions + * rtype jack_function_name (jack_client_t *client) { return rval; } + */ +#define JCFUN(ERR, RTYPE, NAME, RVAL) \ + RTYPE WJACK_ ## NAME (jack_client_t *client) { \ + if likely(_j._ ## NAME) { \ + return ((RTYPE (*)(jack_client_t *client)) _j._ ## NAME)(client); \ + } else { \ + WJACK_WARNING(NAME) \ + return RVAL; \ + } \ + } + +/* abstraction for NOOP functions with return value + * rtype jack_function_name (ARGS) { return rval; } + */ +#define JPFUN(ERR, RTYPE, NAME, DEF, ARGS, RVAL) \ + RTYPE WJACK_ ## NAME DEF { \ + if likely(_j._ ## NAME) { \ + return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \ + } else { \ + WJACK_WARNING(NAME) \ + return RVAL; \ + } \ + } + +/* abstraction for functions that need custom code. + * e.g. functions with return-value-pointer args, + * use CODE to initialize value + * + * rtype jack_function_name (ARGS) { CODE } + */ +#define JXFUN(ERR, RTYPE, NAME, DEF, ARGS, CODE) \ + RTYPE WJACK_ ## NAME DEF { \ + if likely(_j._ ## NAME) { \ + return ((RTYPE (*)DEF) _j._ ## NAME) ARGS; \ + } else { \ + WJACK_WARNING(NAME) \ + CODE \ + } \ + } + +/* abstraction for void functions with return-value-pointer args + * void jack_function_name (ARGS) { CODE } + */ +#define JVFUN(ERR, NAME, DEF, ARGS, CODE) \ + void WJACK_ ## NAME DEF { \ + if likely(_j._ ## NAME) { \ + ((void (*)DEF) _j._ ## NAME) ARGS; \ + } else { \ + WJACK_WARNING(NAME) \ + CODE \ + } \ + } + +#include "AudioWeakJack.def" + +#undef JCFUN +#undef JPFUN +#undef JXFUN +#undef JVFUN + +#endif // end USE_WEAK_JACK diff --git a/src/core/main.cpp b/src/core/main.cpp index b36ca0055c3..fc8dd5de2c8 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -57,7 +57,6 @@ #include #include "MainApplication.h" -#include "MemoryManager.h" #include "ConfigManager.h" #include "NotePlayHandle.h" #include "embed.h" @@ -203,7 +202,6 @@ void fileCheck( QString &file ) int main( int argc, char * * argv ) { // initialize memory managers - MemoryManager::init(); NotePlayHandleManager::init(); // intialize RNG @@ -930,9 +928,6 @@ int main( int argc, char * * argv ) Engine::destroy(); } - // cleanup memory managers - MemoryManager::cleanup(); - // ProjectRenderer::updateConsoleProgress() doesn't return line after render if( coreOnly ) { diff --git a/src/core/midi/MidiApple.cpp b/src/core/midi/MidiApple.cpp index 45f58e4bb1b..f4bc0d4dd97 100644 --- a/src/core/midi/MidiApple.cpp +++ b/src/core/midi/MidiApple.cpp @@ -615,7 +615,7 @@ char * MidiApple::getFullName(MIDIEndpointRef &endpoint_ref) char * deviceName = getName(device); char * endPointName = getName(endpoint_ref); qDebug("device name='%s' endpoint name='%s'",deviceName,endPointName); - char * fullName = (char *)malloc(strlen(deviceName) + strlen(endPointName)+1); + char * fullName = (char *)malloc(strlen(deviceName) + strlen(":") + strlen(endPointName)+1); sprintf(fullName, "%s:%s", deviceName,endPointName); return fullName; } diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index 68bdb4bf25f..448c233cb4c 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -319,6 +319,16 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) float *values = m_pat->valuesAfter( it.key() ); + float nextValue; + if( m_pat->progressionType() == AutomationPattern::DiscreteProgression ) + { + nextValue = it.value(); + } + else + { + nextValue = ( it + 1 ).value(); + } + QPainterPath path; QPointF origin = QPointF( x_base + it.key() * ppTick, 0.0f ); path.moveTo( origin ); @@ -332,7 +342,7 @@ void AutomationPatternView::paintEvent( QPaintEvent * ) path.lineTo( QPointF( x, value ) ); } - path.lineTo( x_base + ( ( it + 1 ).key() ) * ppTick, values[ ( it + 1 ).key() - 1 - it.key() ] ); + path.lineTo( x_base + ( ( it + 1 ).key() ) * ppTick, nextValue ); path.lineTo( x_base + ( ( it + 1 ).key() ) * ppTick, 0.0f ); path.lineTo( origin ); diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 0e828c32929..8da1cb4e0a5 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -382,6 +382,10 @@ void FxMixerView::deleteChannel(int index) // remember selected line int selLine = m_currentFxLine->channelIndex(); + // in case the deleted channel is soloed or the remaining + // channels will be left in a muted state + Engine::fxMixer()->clearChannel(index); + // delete the real channel Engine::fxMixer()->deleteChannel(index); diff --git a/src/gui/MainApplication.cpp b/src/gui/MainApplication.cpp index 767eaa8fef1..5210dd76ce6 100644 --- a/src/gui/MainApplication.cpp +++ b/src/gui/MainApplication.cpp @@ -33,7 +33,12 @@ MainApplication::MainApplication(int& argc, char** argv) : QApplication(argc, argv), - m_queuedFile() {} + m_queuedFile() +{ +#if defined(LMMS_BUILD_WIN32) && QT_VERSION >= 0x050000 + installNativeEventFilter(this); +#endif +} bool MainApplication::event(QEvent* event) { @@ -64,6 +69,7 @@ bool MainApplication::event(QEvent* event) } #ifdef LMMS_BUILD_WIN32 +// This can be moved into nativeEventFilter once Qt4 support has been dropped bool MainApplication::winEventFilter(MSG* msg, long* result) { switch(msg->message) @@ -85,4 +91,16 @@ bool MainApplication::winEventFilter(MSG* msg, long* result) return false; } } + +#if QT_VERSION >= 0x050000 +bool MainApplication::nativeEventFilter(const QByteArray& eventType, + void* message, long* result) +{ + if(eventType == "windows_generic_MSG") + { + return winEventFilter(static_cast(message), result); + } + return false; +} +#endif #endif diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index b91fe3ef2fa..32d16546b1b 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -64,6 +65,21 @@ #include "lmmsversion.h" +#if !defined(LMMS_BUILD_WIN32) && !defined(LMMS_BULID_APPLE) && !defined(LMMS_BUILD_HAIKU) +//Work around an issue on KDE5 as per https://bugs.kde.org/show_bug.cgi?id=337491#c21 +void disableAutoKeyAccelerators(QWidget* mainWindow) +{ + using DisablerFunc = void(*)(QWidget*); + QLibrary kf5WidgetsAddon("KF5WidgetsAddons", 5); + DisablerFunc setNoAccelerators = + reinterpret_cast(kf5WidgetsAddon.resolve("_ZN19KAcceleratorManager10setNoAccelEP7QWidget")); + if(setNoAccelerators) + { + setNoAccelerators(mainWindow); + } + kf5WidgetsAddon.unload(); +} +#endif MainWindow::MainWindow() : @@ -76,6 +92,9 @@ MainWindow::MainWindow() : m_metronomeToggle( 0 ), m_session( Normal ) { +#if !defined(LMMS_BUILD_WIN32) && !defined(LMMS_BULID_APPLE) && !defined(LMMS_BUILD_HAIKU) + disableAutoKeyAccelerators(this); +#endif setAttribute( Qt::WA_DeleteOnClose ); QWidget * main_widget = new QWidget( this ); @@ -836,8 +855,8 @@ void MainWindow::createNewProjectFromTemplate( QAction * _idx ) ConfigManager::inst()->factoryTemplatesDir() : ConfigManager::inst()->userTemplateDir(); - Engine::getSong()->createNewProjectFromTemplate( - dirBase + _idx->text() + ".mpt" ); + const QString f = dirBase + _idx->text().replace("&&", "&") + ".mpt"; + Engine::getSong()->createNewProjectFromTemplate(f); } } @@ -888,7 +907,7 @@ void MainWindow::updateRecentlyOpenedProjectsMenu() } m_recentlyOpenedProjectsMenu->addAction( - embed::getIconPixmap( "project_file" ), *it ); + embed::getIconPixmap( "project_file" ), it->replace("&", "&&") ); #ifdef LMMS_BUILD_APPLE m_recentlyOpenedProjectsMenu->actions().last()->setIconVisibleInMenu(false); // QTBUG-44565 workaround m_recentlyOpenedProjectsMenu->actions().last()->setIconVisibleInMenu(true); @@ -904,12 +923,11 @@ void MainWindow::updateRecentlyOpenedProjectsMenu() - void MainWindow::openRecentlyOpenedProject( QAction * _action ) { if ( mayChangeProject(true) ) { - const QString & f = _action->text(); + const QString f = _action->text().replace("&&", "&"); setCursor( Qt::WaitCursor ); Engine::getSong()->loadProject( f ); setCursor( Qt::ArrowCursor ); @@ -1500,7 +1518,7 @@ void MainWindow::fillTemplatesMenu() { m_templatesMenu->addAction( embed::getIconPixmap( "project_file" ), - ( *it ).left( ( *it ).length() - 4 ) ); + ( *it ).left( ( *it ).length() - 4 ).replace("&", "&&") ); #ifdef LMMS_BUILD_APPLE m_templatesMenu->actions().last()->setIconVisibleInMenu(false); // QTBUG-44565 workaround m_templatesMenu->actions().last()->setIconVisibleInMenu(true); diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 1e94c1b50e7..733cf281498 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -517,16 +517,13 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) // loop through whole time-map... while( it != time_map.end() ) { - MidiTime len = 4; - // and check whether the user clicked on an // existing value if( pos_ticks >= it.key() && - len > 0 && ( it+1==time_map.end() || pos_ticks <= (it+1).key() ) && ( pos_ticks<= it.key() + MidiTime::ticksPerTact() *4 / m_ppt ) && - level <= it.value() ) + ( level == it.value() || mouseEvent->button() == Qt::RightButton ) ) { break; } @@ -589,6 +586,7 @@ void AutomationEditor::mousePressEvent( QMouseEvent* mouseEvent ) m_editMode == DRAW ) || m_editMode == ERASE ) { + m_drawLastTick = pos_ticks; m_pattern->addJournalCheckPoint(); // erase single value if( it != time_map.end() ) @@ -680,6 +678,39 @@ void AutomationEditor::mouseReleaseEvent(QMouseEvent * mouseEvent ) + +void AutomationEditor::removePoints( int x0, int x1 ) +{ + int deltax = qAbs( x1 - x0 ); + int x = x0; + int xstep; + + if( deltax < 1 ) + { + return; + } + + if( x0 < x1 ) + { + xstep = 1; + } + else + { + xstep = -1; + } + + int i = 0; + while( i <= deltax ) + { + m_pattern->removeValue( MidiTime( x ) ); + x += xstep; + i += 1; + } +} + + + + void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) { QMutexLocker m( &m_patternMutex ); @@ -735,14 +766,13 @@ void AutomationEditor::mouseMoveEvent(QMouseEvent * mouseEvent ) ( mouseEvent->buttons() & Qt::LeftButton && m_editMode == ERASE ) ) { - // int resolution needed to improve the sensitivity of - // the erase manoeuvre with zoom levels < 100% - int zoom = m_zoomingXModel.value(); - int resolution = 1 + zoom * zoom; - for( int i = -resolution; i < resolution; ++i ) + // removing automation point + if( pos_ticks < 0 ) { - m_pattern->removeValue( MidiTime( pos_ticks + i ) ); + pos_ticks = 0; } + removePoints( m_drawLastTick, pos_ticks ); + Engine::getSong()->setModified(); } else if( mouseEvent->buttons() & Qt::NoButton && m_editMode == DRAW ) { @@ -1067,7 +1097,7 @@ inline void AutomationEditor::drawAutomationPoint( QPainter & p, timeMap::iterat { int x = xCoordOfTick( it.key() ); int y = yCoordOfLevel( it.value() ); - const int outerRadius = qBound( 2, ( m_ppt * AutomationPattern::quantization() ) / 576, 5 ); // man, getting this calculation right took forever + const int outerRadius = qBound( 3, ( m_ppt * AutomationPattern::quantization() ) / 576, 5 ); // man, getting this calculation right took forever p.setPen( QPen( vertexColor().lighter( 200 ) ) ); p.setBrush( QBrush( vertexColor() ) ); p.drawEllipse( x - outerRadius, y - outerRadius, outerRadius * 2, outerRadius * 2 ); @@ -1345,13 +1375,13 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) float *values = m_pattern->valuesAfter( it.key() ); float nextValue; - if ( m_pattern->valuesAfter( ( it + 1 ).key() ) != NULL ) + if( m_pattern->progressionType() == AutomationPattern::DiscreteProgression ) { - nextValue = *( m_pattern->valuesAfter( ( it + 1 ).key() ) ); + nextValue = it.value(); } else { - nextValue = values[ ( it + 1 ).key() - it.key() -1 ]; + nextValue = ( it + 1 ).value(); } p.setRenderHints( QPainter::Antialiasing, true ); @@ -2100,6 +2130,8 @@ void AutomationEditor::setQuantization() } quantization = DefaultTicksPerTact / quantization; AutomationPattern::setQuantization( quantization ); + + update(); } diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 9b65c684798..ae5c33c894a 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -963,6 +963,9 @@ void PianoRoll::shiftSemiTone( int amount ) // shift notes by amount semitones } } + m_pattern->rearrangeAllNotes(); + m_pattern->dataChanged(); + // we modified the song update(); gui->songEditor()->update(); diff --git a/src/lmmsconfig.h.in b/src/lmmsconfig.h.in index 421d0cc999c..aa4505dbbcc 100644 --- a/src/lmmsconfig.h.in +++ b/src/lmmsconfig.h.in @@ -11,6 +11,7 @@ #cmakedefine LMMS_HAVE_ALSA #cmakedefine LMMS_HAVE_FLUIDSYNTH #cmakedefine LMMS_HAVE_JACK +#cmakedefine LMMS_HAVE_WEAKJACK #cmakedefine LMMS_HAVE_MP3LAME #cmakedefine LMMS_HAVE_OGGVORBIS #cmakedefine LMMS_HAVE_OSS diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 6aaf3eb75e9..90dbf11a684 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -442,12 +442,15 @@ void InstrumentTrack::silenceAllNotes( bool removeIPH ) m_midiNotesMutex.unlock(); lock(); - // invalidate all NotePlayHandles linked to this track + // invalidate all NotePlayHandles and PresetPreviewHandles linked to this track m_processHandles.clear(); - Engine::mixer()->removePlayHandlesOfTypes( this, removeIPH - ? PlayHandle::TypeNotePlayHandle - | PlayHandle::TypeInstrumentPlayHandle - : PlayHandle::TypeNotePlayHandle ); + + quint8 flags = PlayHandle::TypeNotePlayHandle | PlayHandle::TypePresetPreviewHandle; + if( removeIPH ) + { + flags |= PlayHandle::TypeInstrumentPlayHandle; + } + Engine::mixer()->removePlayHandlesOfTypes( this, flags ); unlock(); } diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index 3fa01462b8d..e726ca2ee86 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -213,28 +213,7 @@ Note * Pattern::addNote( const Note & _new_note, const bool _quant_pos ) } instrumentTrack()->lock(); - if( m_notes.size() == 0 || m_notes.back()->pos() <= new_note->pos() ) - { - m_notes.push_back( new_note ); - } - else - { - // simple algorithm for inserting the note between two - // notes with smaller and greater position - // maybe it could be optimized by starting in the middle and - // going forward or backward but note-inserting isn't that - // time-critical since it is usually not done while playing... - long new_note_abs_time = new_note->pos(); - NoteVector::Iterator it = m_notes.begin(); - - while( it != m_notes.end() && - ( *it )->pos() < new_note_abs_time ) - { - ++it; - } - - m_notes.insert( it, new_note ); - } + m_notes.insert(std::upper_bound(m_notes.begin(), m_notes.end(), new_note, Note::lessThan), new_note); instrumentTrack()->unlock(); checkType(); @@ -292,7 +271,7 @@ Note * Pattern::noteAtStep( int _step ) void Pattern::rearrangeAllNotes() { // sort notes by start time - qSort(m_notes.begin(), m_notes.end(), Note::lessThan ); + std::sort(m_notes.begin(), m_notes.end(), Note::lessThan); } diff --git a/tests/src/tracks/AutomationTrackTest.cpp b/tests/src/tracks/AutomationTrackTest.cpp index 542815dccfc..bd5257253fd 100644 --- a/tests/src/tracks/AutomationTrackTest.cpp +++ b/tests/src/tracks/AutomationTrackTest.cpp @@ -30,6 +30,9 @@ #include "AutomationTrack.h" #include "BBTrack.h" #include "BBTrackContainer.h" +#include "DetuningHelper.h" +#include "InstrumentTrack.h" +#include "Pattern.h" #include "TrackContainer.h" #include "Engine.h" @@ -105,6 +108,55 @@ private slots: QCOMPARE(song->automatedValuesAt(150)[&model], 0.5f); } + void testLengthRespected() + { + FloatModel model; + + auto song = Engine::getSong(); + AutomationTrack track(song); + + AutomationPattern p(&track); + p.setProgressionType(AutomationPattern::LinearProgression); + p.addObject(&model); + + p.putValue(0, 0.0, false); + p.putValue(100, 1.0, false); + + p.changeLength(100); + QCOMPARE(song->automatedValuesAt( 0)[&model], 0.0f); + QCOMPARE(song->automatedValuesAt( 50)[&model], 0.5f); + QCOMPARE(song->automatedValuesAt(100)[&model], 1.0f); + + p.changeLength(50); + QCOMPARE(song->automatedValuesAt( 0)[&model], 0.0f); + QCOMPARE(song->automatedValuesAt( 50)[&model], 0.5f); + QCOMPARE(song->automatedValuesAt(100)[&model], 0.5f); + } + + void testInlineAutomation() + { + auto song = Engine::getSong(); + + InstrumentTrack* instrumentTrack = + dynamic_cast(Track::create(Track::InstrumentTrack, song)); + + Pattern* notePattern = dynamic_cast(instrumentTrack->createTCO(0)); + notePattern->changeLength(MidiTime(4, 0)); + Note* note = notePattern->addNote(Note(MidiTime(4, 0)), false); + note->createDetuning(); + + DetuningHelper* dh = note->detuning(); + auto pattern = dh->automationPattern(); + pattern->setProgressionType( AutomationPattern::LinearProgression ); + pattern->putValue(MidiTime(0, 0), 0.0); + pattern->putValue(MidiTime(4, 0), 1.0); + + QCOMPARE(pattern->valueAt(MidiTime(0, 0)), 0.0f); + QCOMPARE(pattern->valueAt(MidiTime(1, 0)), 0.25f); + QCOMPARE(pattern->valueAt(MidiTime(2, 0)), 0.5f); + QCOMPARE(pattern->valueAt(MidiTime(4, 0)), 1.0f); + } + void testBBTrack() { auto song = Engine::getSong();