diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 8a8b03635cb..702d581fe54 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ +--- custom: https://lmms.io/get-involved/#donate diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 4cd1464d67b..fe3d60ff219 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -78,7 +78,8 @@ body: label: Screenshots / Minimum Reproducible Project description: | - Upload any screenshots showing the bug in action. - - If possible, also include a .mmp/.mmpz project containing the simplest possible setup needed to reproduce the bug. + - If possible, also include a .mmp/.mmpz project containing the simplest possible + setup needed to reproduce the bug. ***Note:** To upload a project file to GitHub, it will need to be placed in a .zip archive.* - type: checkboxes diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 2c51f276e3d..735942ffba8 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,5 @@ +--- contact_links: -- name: Get help on Discord - url: https://lmms.io/chat/ - about: Need help? Have a question? Reach out to other LMMS users on our Discord server! + - name: Get help on Discord + url: https://lmms.io/chat/ + about: Need help? Have a question? Reach out to other LMMS users on our Discord server! diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 1f11b4eb37c..cc233a6367a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -10,7 +10,8 @@ body: label: Enhancement Summary description: | - Briefly describe the enhancement. - - Explain why you believe the proposed enhancement to be a good idea, and (if applicable) how it helps overcome a limitation of LMMS you are currently facing. + - Explain why you believe the proposed enhancement to be a good idea, and (if applicable) how it helps + overcome a limitation of LMMS you are currently facing. validations: required: true - type: textarea diff --git a/.github/no-response.yml b/.github/no-response.yml index 4761654082e..f6c32953977 100644 --- a/.github/no-response.yml +++ b/.github/no-response.yml @@ -1,2 +1,3 @@ +--- # Label requiring a response responseRequiredLabel: "response required" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b08c3ba20a8..2c2e8dd73b4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: linux: name: linux runs-on: ubuntu-latest - container: lmmsci/linux.gcc:18.04 + container: ghcr.io/lmms/linux.gcc:20.04 env: CMAKE_OPTS: >- -DUSE_WERROR=ON @@ -18,12 +18,8 @@ jobs: CCACHE_NOCOMPRESS: 1 MAKEFLAGS: -j2 steps: - - name: Update and configure Git - run: | - add-apt-repository ppa:git-core/ppa - apt-get update - apt-get --yes install git - git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: Configure git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Check out uses: actions/checkout@v3 with: @@ -41,8 +37,10 @@ jobs: run: | ccache --zero-stats source /opt/qt5*/bin/qt5*-env.sh || true - mkdir build && cd build - cmake .. $CMAKE_OPTS -DCMAKE_INSTALL_PREFIX=./install + cmake -S . \ + -B build \ + -DCMAKE_INSTALL_PREFIX=./install \ + $CMAKE_OPTS - name: Build run: cmake --build build - name: Run tests @@ -54,7 +52,7 @@ jobs: cmake --build build --target install cmake --build build --target appimage - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: linux path: build/lmms-*.AppImage @@ -62,23 +60,35 @@ jobs: run: | ccache --cleanup echo "[ccache config]" - ccache --print-config + ccache --show-config echo "[ccache stats]" ccache --show-stats env: CCACHE_MAXSIZE: 500M macos: - name: macos - runs-on: macos-12 + strategy: + fail-fast: false + matrix: + arch: [ x86_64, arm64 ] + include: + - arch: x86_64 + os: macos-12 + xcode: "13.1" + - arch: arm64 + os: macos-14 + xcode: "14.3.1" + name: macos-${{ matrix.arch }} + runs-on: ${{ matrix.os }} env: CMAKE_OPTS: >- + -Werror=dev -DUSE_WERROR=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSE_COMPILE_CACHE=ON CCACHE_MAXSIZE: 0 CCACHE_NOCOMPRESS: 1 MAKEFLAGS: -j3 - DEVELOPER_DIR: /Applications/Xcode_13.1.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer steps: - name: Check out uses: actions/checkout@v3 @@ -88,19 +98,21 @@ jobs: - name: Clean up Homebrew download cache run: rm -rf ~/Library/Caches/Homebrew/downloads - name: Restore Homebrew download cache + id: cache-homebrew uses: actions/cache/restore@v3 with: key: n/a - only restore from restore-keys restore-keys: | - homebrew- + homebrew-${{ matrix.arch }}- path: ~/Library/Caches/Homebrew/downloads - name: Cache ccache data uses: actions/cache@v3 with: - key: ccache-${{ github.job }}-${{ github.ref }}-${{ github.run_id }} + key: "ccache-${{ github.job }}-${{ matrix.arch }}-${{ github.ref }}\ + -${{ github.run_id }}" restore-keys: | - ccache-${{ github.job }}-${{ github.ref }}- - ccache-${{ github.job }}- + ccache-${{ github.job }}-${{ matrix.arch }}-${{ github.ref }}- + ccache-${{ github.job }}-${{ matrix.arch }}- path: ~/Library/Caches/ccache - name: Install dependencies run: | @@ -118,7 +130,8 @@ jobs: cmake -S . \ -B build \ -DCMAKE_INSTALL_PREFIX="../target" \ - -DCMAKE_PREFIX_PATH="$(brew --prefix qt5)" \ + -DCMAKE_PREFIX_PATH="$(brew --prefix qt@5)" \ + -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }} \ $CMAKE_OPTS \ -DUSE_WERROR=OFF - name: Build @@ -132,9 +145,9 @@ jobs: cmake --build build --target install cmake --build build --target dmg - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: macos + name: macos-${{ matrix.arch }} path: build/lmms-*.dmg - name: Trim ccache and print statistics run: | @@ -146,10 +159,14 @@ jobs: env: CCACHE_MAXSIZE: 500MB - name: Save Homebrew download cache + if: ${{ steps.cache-homebrew.outputs.cache-matched-key != env.key }} uses: actions/cache/save@v3 with: - key: homebrew-${{ hashFiles('Brewfile.lock.json') }} + key: ${{ env.key }} path: ~/Library/Caches/Homebrew/downloads + env: + key: "homebrew-${{ matrix.arch }}\ + -${{ hashFiles('Brewfile.lock.json') }}" mingw: strategy: fail-fast: false @@ -157,9 +174,10 @@ jobs: arch: ['32', '64'] name: mingw${{ matrix.arch }} runs-on: ubuntu-latest - container: lmmsci/linux.mingw${{ matrix.arch }}:18.04 + container: ghcr.io/lmms/linux.mingw:20.04 env: CMAKE_OPTS: >- + -Werror=dev -DUSE_WERROR=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo -DUSE_COMPILE_CACHE=ON @@ -167,12 +185,14 @@ jobs: CCACHE_NOCOMPRESS: 1 MAKEFLAGS: -j2 steps: - - name: Update and configure Git + - name: Enable POSIX MinGW run: | - add-apt-repository ppa:git-core/ppa - apt-get update - apt-get --yes install git - git config --global --add safe.directory "$GITHUB_WORKSPACE" + update-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix + update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix + update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix + update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix + - name: Configure git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - name: Check out uses: actions/checkout@v3 with: @@ -190,14 +210,17 @@ jobs: - name: Configure run: | ccache --zero-stats - mkdir build && cd build - ../cmake/build_win${{ matrix.arch }}.sh + cmake -S . \ + -B build \ + -DCMAKE_INSTALL_PREFIX=./install \ + -DCMAKE_TOOLCHAIN_FILE="./cmake/toolchains/MinGW-W64-${{ matrix.arch }}.cmake" \ + $CMAKE_OPTS - name: Build run: cmake --build build - name: Package run: cmake --build build --target package - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: mingw${{ matrix.arch }} path: build/lmms-*.exe @@ -205,7 +228,7 @@ jobs: run: | ccache --cleanup echo "[ccache config]" - ccache --print-config + ccache --show-config echo "[ccache stats]" ccache --show-stats env: @@ -218,7 +241,6 @@ jobs: name: msvc-${{ matrix.arch }} runs-on: windows-2019 env: - qt-version: '5.15.2' CCACHE_MAXSIZE: 0 CCACHE_NOCOMPRESS: 1 steps: @@ -238,30 +260,31 @@ jobs: - name: Cache ccache data uses: actions/cache@v3 with: + # yamllint disable rule:line-length key: "ccache-${{ github.job }}-${{ matrix.arch }}-${{ github.ref }}\ -${{ github.run_id }}" restore-keys: | ccache-${{ github.job }}-${{ matrix.arch }}-${{ github.ref }}- ccache-${{ github.job }}-${{ matrix.arch }}- path: ~\AppData\Local\ccache + # yamllint enable rule:line-length - name: Install tools run: choco install ccache - - name: Install 64-bit Qt - if: matrix.arch == 'x64' - uses: jurplel/install-qt-action@b3ea5275e37b734d027040e2c7fe7a10ea2ef946 - with: - version: ${{ env.qt-version }} - arch: win64_msvc2019_64 - archives: qtbase qtsvg qttools - cache: true - - name: Install 32-bit Qt + - name: Install Qt uses: jurplel/install-qt-action@b3ea5275e37b734d027040e2c7fe7a10ea2ef946 with: - version: ${{ env.qt-version }} - arch: win32_msvc2019 + version: '5.15.2' + arch: |- + ${{ + fromJSON(' + { + "x86": "win32_msvc2019", + "x64": "win64_msvc2019_64" + } + ')[matrix.arch] + }} archives: qtbase qtsvg qttools cache: true - set-env: ${{ matrix.arch == 'x86' }} - name: Set up build environment uses: ilammy/msvc-dev-cmd@cec98b9d092141f74527d0afa6feb2af698cfe89 with: @@ -274,8 +297,10 @@ jobs: -B build ` -G Ninja ` --toolchain C:/vcpkg/scripts/buildsystems/vcpkg.cmake ` + -Werror=dev ` -DCMAKE_BUILD_TYPE=RelWithDebInfo ` -DUSE_COMPILE_CACHE=ON ` + -DUSE_WERROR=ON ` -DVCPKG_TARGET_TRIPLET="${{ matrix.arch }}-windows" ` -DVCPKG_HOST_TRIPLET="${{ matrix.arch }}-windows" ` -DVCPKG_MANIFEST_INSTALL="${{ env.should_install_manifest }}" @@ -291,7 +316,7 @@ jobs: - name: Package run: cmake --build build --target package - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: msvc-${{ matrix.arch }} path: build\lmms-*.exe diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 3f770067466..228f383c127 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -31,3 +31,10 @@ jobs: $(find "./cmake/" -type f -name '*.sh' -o -name "*.sh.in") \ doc/bash-completion/lmms \ buildtools/update_locales + yamllint: + runs-on: ubuntu-latest + steps: + - name: Check out + uses: actions/checkout@v3 + - name: Run yamllint + run: for i in $(git ls-files '*.yml'); do yamllint $i; done diff --git a/.gitignore b/.gitignore index 1b855f204cb..cc2823ba06a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -/build -/target +/build/ +/target/ .*.sw? .DS_Store *~ diff --git a/.gitmodules b/.gitmodules index 1c43fd1b339..58c4d8526d4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,9 +25,6 @@ [submodule "src/3rdparty/weakjack/weakjack"] path = src/3rdparty/weakjack/weakjack url = https://github.com/x42/weakjack.git -[submodule "src/3rdparty/mingw-std-threads"] - path = src/3rdparty/mingw-std-threads - url = https://github.com/meganz/mingw-std-threads.git [submodule "doc/wiki"] path = doc/wiki url = https://github.com/lmms/lmms.wiki.git diff --git a/.yamllint b/.yamllint new file mode 100644 index 00000000000..a7aa0ae1ed5 --- /dev/null +++ b/.yamllint @@ -0,0 +1,3 @@ +rules: + line-length: + max: 120 # be conforming to LMMS coding rules diff --git a/CMakeLists.txt b/CMakeLists.txt index e45db03e475..5bb4adc4aa8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.9) +CMAKE_MINIMUM_REQUIRED(VERSION 3.13) # Set the given policy to NEW. If it does not exist, it will not be set. If it # is already set to NEW (most likely due to predating the minimum required CMake @@ -15,8 +15,7 @@ function(enable_policy_if_exists id) endif() endfunction() -# Needed for the SWH Ladspa plugins. See below. -enable_policy_if_exists(CMP0074) # find_package() uses _ROOT variables. +enable_policy_if_exists(CMP0092) # MSVC warning flags are not in CMAKE__FLAGS by default. # Needed for ccache support with MSVC enable_policy_if_exists(CMP0141) # MSVC debug information format flags are selected by an abstraction. @@ -43,10 +42,11 @@ INCLUDE(AddFileDependencies) INCLUDE(CheckIncludeFiles) INCLUDE(FindPkgConfig) INCLUDE(GenerateExportHeader) +include(StaticDependencies) STRING(TOUPPER "${CMAKE_PROJECT_NAME}" PROJECT_NAME_UCASE) -SET(PROJECT_YEAR 2023) +SET(PROJECT_YEAR 2024) SET(PROJECT_AUTHOR "LMMS Developers") SET(PROJECT_URL "https://lmms.io") @@ -94,8 +94,8 @@ OPTION(WANT_STK "Include Stk (Synthesis Toolkit) support" ON) OPTION(WANT_SWH "Include Steve Harris's LADSPA plugins" ON) OPTION(WANT_TAP "Include Tom's Audio Processing LADSPA plugins" ON) OPTION(WANT_VST "Include VST support" ON) -OPTION(WANT_VST_32 "Include 32-bit VST support" ON) -OPTION(WANT_VST_64 "Include 64-bit VST support" ON) +OPTION(WANT_VST_32 "Include 32-bit Windows VST support" ON) +OPTION(WANT_VST_64 "Include 64-bit Windows VST support" ON) OPTION(WANT_WINMM "Include WinMM MIDI support" OFF) OPTION(WANT_DEBUG_FPE "Debug floating point exceptions" OFF) option(WANT_DEBUG_ASAN "Enable AddressSanitizer" OFF) @@ -131,6 +131,9 @@ IF(LMMS_BUILD_WIN32) SET(WANT_WINMM ON) SET(BUNDLE_QT_TRANSLATIONS ON) SET(LMMS_HAVE_WINMM TRUE) + if(NOT LMMS_BUILD_WIN64) + set(WANT_VST_64 OFF) + endif() SET(STATUS_ALSA "") SET(STATUS_OSS "") SET(STATUS_PULSEAUDIO "") @@ -142,16 +145,6 @@ ELSE(LMMS_BUILD_WIN32) SET(STATUS_WINMM "") ENDIF(LMMS_BUILD_WIN32) - -# TODO: Fix linking issues with msys debug builds -IF(LMMS_BUILD_MSYS AND CMAKE_BUILD_TYPE STREQUAL "Debug") - SET(WANT_GIG OFF) - SET(WANT_STK OFF) - SET(WANT_SWH OFF) - SET(STATUS_GIG "not built as requested") - SET(STATUS_STK "not built as requested") -ENDIF() - SET(CMAKE_CXX_STANDARD_REQUIRED ON) CHECK_INCLUDE_FILES(pthread.h LMMS_HAVE_PTHREAD_H) @@ -161,6 +154,7 @@ CHECK_INCLUDE_FILES(sys/types.h LMMS_HAVE_SYS_TYPES_H) CHECK_INCLUDE_FILES(sys/ipc.h LMMS_HAVE_SYS_IPC_H) CHECK_INCLUDE_FILES(sys/time.h LMMS_HAVE_SYS_TIME_H) CHECK_INCLUDE_FILES(sys/times.h LMMS_HAVE_SYS_TIMES_H) +CHECK_INCLUDE_FILES(sys/prctl.h LMMS_HAVE_SYS_PRCTL_H) CHECK_INCLUDE_FILES(sched.h LMMS_HAVE_SCHED_H) CHECK_INCLUDE_FILES(sys/soundcard.h LMMS_HAVE_SYS_SOUNDCARD_H) CHECK_INCLUDE_FILES(soundcard.h LMMS_HAVE_SOUNDCARD_H) @@ -176,10 +170,10 @@ check_library_exists(rt shm_open "" LMMS_HAVE_LIBRT) LIST(APPEND CMAKE_PREFIX_PATH "${CMAKE_INSTALL_PREFIX}") -FIND_PACKAGE(Qt5 5.6.0 COMPONENTS Core Gui Widgets Xml REQUIRED) +FIND_PACKAGE(Qt5 5.9.0 COMPONENTS Core Gui Widgets Xml REQUIRED) FIND_PACKAGE(Qt5 COMPONENTS LinguistTools QUIET) -INCLUDE_DIRECTORIES( +include_directories(SYSTEM ${Qt5Core_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} @@ -344,37 +338,14 @@ IF(WANT_SDL) FIND_PACKAGE(SDL2) IF(SDL2_FOUND) SET(LMMS_HAVE_SDL TRUE) - SET(LMMS_HAVE_SDL2 TRUE) - SET(STATUS_SDL "OK, using SDL2") + SET(STATUS_SDL "OK") SET(SDL2_LIBRARY "SDL2::SDL2") - SET(SDL_INCLUDE_DIR "") - SET(SDL_LIBRARY "") ELSE() + SET(STATUS_SDL "not found, please install libsdl2-dev (or similar) if you require SDL support") SET(SDL2_LIBRARY "") ENDIF() ENDIF() -# fallback to SDL1 -IF(WANT_SDL AND NOT LMMS_HAVE_SDL2) - # Fallback to SDL1.2 - SET(SDL_BUILDING_LIBRARY TRUE) - FIND_PACKAGE(SDL) - IF(SDL_FOUND) - SET(LMMS_HAVE_SDL TRUE) - SET(STATUS_SDL "OK, using SDL1.2") - # fix mingw since 53abd65 - IF(NOT SDL_INCLUDE_DIR) - SET(SDL_INCLUDE_DIR "${CMAKE_FIND_ROOT_PATH}/include") - ENDIF() - - ELSE() - SET(STATUS_SDL "not found, please install libsdl2-dev (or similar) " - "if you require SDL support") - SET(SDL_INCLUDE_DIR "") - SET(SDL_LIBRARY "") - ENDIF() -ENDIF() - # check for Sid if(WANT_SID) if(PERL_FOUND) @@ -417,7 +388,7 @@ IF(WANT_SOUNDIO) IF(SOUNDIO_FOUND) SET(LMMS_HAVE_SOUNDIO TRUE) SET(STATUS_SOUNDIO "OK") - INCLUDE_DIRECTORIES("${SOUNDIO_INCLUDE_DIR}") + include_directories(SYSTEM "${SOUNDIO_INCLUDE_DIR}") ELSE(SOUNDIO_FOUND) SET(SOUNDIO_INCLUDE_DIR "") SET(STATUS_SOUNDIO "not found, please install libsoundio if you require libsoundio support") @@ -498,11 +469,9 @@ ENDIF(NOT LMMS_HAVE_ALSA) IF(WANT_JACK) IF(WANT_WEAKJACK) SET(LMMS_HAVE_WEAKJACK TRUE) - SET(WEAKJACK_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/3rdparty/weakjack/weakjack) - SET(JACK_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/src/3rdparty/jack2/common) SET(STATUS_JACK "OK (weak linking enabled)") - # use dlsym instead - SET(JACK_LIBRARIES ${CMAKE_DL_LIBS}) + set(JACK_INCLUDE_DIRS "") + set(JACK_LIBRARIES weakjack) SET(LMMS_HAVE_JACK TRUE) SET(LMMS_HAVE_JACK_PRENAME TRUE) SET(JACK_FOUND TRUE) @@ -587,26 +556,42 @@ IF(WANT_SNDIO) ENDIF(WANT_SNDIO) # check for WINE -IF(WANT_VST) - FIND_PACKAGE(Wine) - IF(WINE_FOUND) - SET(LMMS_SUPPORT_VST TRUE) - 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") - ELSE(WINE_FOUND) - SET(STATUS_VST "not found, please install (lib)wine-dev (or similar) - 64 bit systems additionally need gcc-multilib and g++-multilib") - ENDIF(WINE_FOUND) -ENDIF(WANT_VST) -IF(LMMS_BUILD_WIN32) - SET(LMMS_SUPPORT_VST TRUE) - SET(STATUS_VST "OK") -ENDIF(LMMS_BUILD_WIN32) +if(WANT_VST) + if((WANT_VST_32 OR WANT_VST_64) AND NOT LMMS_BUILD_WIN32) + find_package(Wine) + include(CheckWineGcc) + endif() + macro(check_vst bits) + if(NOT WANT_VST_${bits}) + set(STATUS_VST_${bits} "Not built, as requested") + elseif(LMMS_BUILD_WIN32) + set(STATUS_VST_${bits} "OK") + set(LMMS_HAVE_VST_${bits} TRUE) + elseif(NOT WINE_FOUND) + set(STATUS_VST_${bits} "not found, please install (lib)wine-dev (or similar) - 64 bit systems additionally need gcc-multilib and g++-multilib") + else() + CheckWineGcc("${bits}" "${WINEGCC}" WINEGCC_WORKING) + if(WINEGCC_WORKING) + set(LMMS_HAVE_VST_${bits} TRUE) + if(WINE_LIBRARY_FIX) + set(STATUS_VST_${bits} "OK, with workaround linking ${WINE_LIBRARY_FIX}") + else() + set(STATUS_VST_${bits} "OK") + endif() + else() + set(STATUS_VST_${bits} "winegcc fails to compile ${bits}-bit binaries, please make sure you have ${bits}-bit GCC libraries") + endif() + endif() + endmacro() + check_vst(32) + check_vst(64) + if(LMMS_HAVE_VST_32 OR LMMS_HAVE_VST_64 OR LMMS_BUILD_LINUX) + set(LMMS_HAVE_VST TRUE) + set(STATUS_VST "OK") + else() + set(STATUS_VST "No hosts selected and available") + endif() +endif() IF(WANT_DEBUG_FPE) IF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE) @@ -622,28 +607,6 @@ ENDIF(WANT_DEBUG_FPE) # check for libsamplerate FIND_PACKAGE(Samplerate 0.1.8 MODULE REQUIRED) -# set compiler flags -IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - SET(WERROR_FLAGS "-Wall -Werror=unused-function -Wno-sign-compare -Wno-strict-overflow") - OPTION(USE_WERROR "Add -werror to the build flags. Stops the build on warnings" OFF) - IF(${USE_WERROR}) - SET(WERROR_FLAGS "${WERROR_FLAGS} -Werror") - ENDIF() - - # Due to a regression in gcc-4.8.X, we need to disable array-bounds check - IF (CMAKE_COMPILER_IS_GNUCXX AND ((CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL "4.8.0") OR (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "4.8.0") OR LMMS_BUILD_WIN32)) - SET(WERROR_FLAGS "${WERROR_FLAGS} -Wno-array-bounds -Wno-attributes") - ENDIF() -ELSEIF(MSVC) - # Remove any existing /W flags - STRING(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) - SET(WERROR_FLAGS "/W2") - IF(${USE_WERROR}) - SET(WERROR_FLAGS "${WERROR_FLAGS} /WX") - ENDIF() -ENDIF() - - IF(NOT CMAKE_BUILD_TYPE) message(STATUS "Setting build type to 'Release' as none was specified.") set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) @@ -652,8 +615,6 @@ IF(NOT CMAKE_BUILD_TYPE) "MinSizeRel" "RelWithDebInfo") ENDIF() -SET(CMAKE_C_FLAGS "${WERROR_FLAGS} ${CMAKE_C_FLAGS}") -SET(CMAKE_CXX_FLAGS "${WERROR_FLAGS} ${CMAKE_CXX_FLAGS}") SET(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DLMMS_DEBUG") SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DLMMS_DEBUG") @@ -695,12 +656,9 @@ ENDIF() # we somehow have to make LMMS-binary depend on MOC-files ADD_FILE_DEPENDENCIES("${CMAKE_BINARY_DIR}/lmmsconfig.h") -IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") - IF(WIN32) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-attributes") - ELSE(WIN32) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -DPIC") - ENDIF(WIN32) +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" AND NOT WIN32) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -DPIC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -DPIC") elseif(MSVC) # Use UTF-8 as the source and execution character set add_compile_options("/utf-8") @@ -730,6 +688,8 @@ add_sanitizer(memory "Clang" WANT_DEBUG_MSAN STATUS_DEBUG_MSAN -fno-omit-frame-p # not being found by PeakController add_sanitizer(undefined "GNU|Clang" WANT_DEBUG_UBSAN STATUS_DEBUG_UBSAN -fno-sanitize=vptr) +# Add warning and error flags +include(ErrorFlags) # use ccache include(CompileCache) @@ -852,8 +812,9 @@ MESSAGE( "* SoundFont2 player : ${STATUS_FLUIDSYNTH}\n" "* Sid instrument : ${STATUS_SID}\n" "* Stk Mallets : ${STATUS_STK}\n" -"* VST-instrument hoster : ${STATUS_VST}\n" -"* VST-effect hoster : ${STATUS_VST}\n" +"* VST plugin host : ${STATUS_VST}\n" +" * 32-bit Windows host : ${STATUS_VST_32}\n" +" * 64-bit Windows host : ${STATUS_VST_64}\n" "* CALF LADSPA plugins : ${STATUS_CALF}\n" "* CAPS LADSPA plugins : ${STATUS_CAPS}\n" "* CMT LADSPA plugins : ${STATUS_CMT}\n" diff --git a/cmake/apple/CMakeLists.txt b/cmake/apple/CMakeLists.txt index 0b66689e75d..3fd0a4da457 100644 --- a/cmake/apple/CMakeLists.txt +++ b/cmake/apple/CMakeLists.txt @@ -19,8 +19,19 @@ CONFIGURE_FILE("lmms.plist.in" "${CMAKE_BINARY_DIR}/Info.plist") CONFIGURE_FILE("install_apple.sh.in" "${CMAKE_BINARY_DIR}/install_apple.sh" @ONLY) CONFIGURE_FILE("package_apple.json.in" "${CMAKE_BINARY_DIR}/package_apple.json" @ONLY) +IF(CMAKE_OSX_ARCHITECTURES) + SET(DMG_ARCH "${CMAKE_OSX_ARCHITECTURES}") +ELSEIF(IS_ARM64) + # Target arch is host arch + SET(DMG_ARCH "arm64") +ELSE() + # Fallback to Intel + SET(DMG_ARCH "x86_64") +ENDIF() + # DMG creation target -SET(DMG_FILE "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}-${VERSION}-mac${APPLE_OS_VER}.dmg") +SET(DMG_FILE "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}-${VERSION}-mac${APPLE_OS_VER}-${DMG_ARCH}.dmg") + FILE(REMOVE "${DMG_FILE}") ADD_CUSTOM_TARGET(removedmg COMMAND touch "\"${DMG_FILE}\"" && rm "\"${DMG_FILE}\"" diff --git a/cmake/build_win32.sh b/cmake/build_win32.sh deleted file mode 100755 index 33cd8ecce17..00000000000 --- a/cmake/build_win32.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env bash -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -# Accomodate both linux windows mingw locations -if [ -z "$ARCH" ]; then - ARCH=32 -fi - -MINGW=/mingw$ARCH - -if [ -z "$MSYSCON" ]; then - MINGW=/opt$MINGW - - DISTRO=$(lsb_release -si) - DISTRO_VERSION=$(lsb_release -sr) - - if [ "$DISTRO" != "Ubuntu" ]; then - echo "This script only supports Ubuntu" - exit 1 - fi - - if [ "$DISTRO_VERSION" == "14.04" ]; then - TOOLCHAIN="$DIR/toolchains/Ubuntu-MinGW-X-Trusty-$ARCH.cmake" - else - TOOLCHAIN="$DIR/toolchains/Ubuntu-MinGW-W64-$ARCH.cmake" - fi -else - TOOLCHAIN="$DIR/toolchains/MSYS-$ARCH.cmake" -fi - -export PATH=$MINGW/bin:$PATH -export CXXFLAGS="$CFLAGS" -if [ "$ARCH" == "32" ]; then - export CFLAGS="-march=pentium3 -mtune=generic -mpreferred-stack-boundary=5 -mfpmath=sse" -fi - -CMAKE_OPTS="-DCMAKE_PREFIX_PATH=$MINGW $CMAKE_OPTS" - -# shellcheck disable=SC2086 -cmake "$DIR/.." -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN" $CMAKE_OPTS diff --git a/cmake/build_win64.sh b/cmake/build_win64.sh deleted file mode 100755 index 8dabe09f8f9..00000000000 --- a/cmake/build_win64.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -ARCH=64 "$DIR/build_win32.sh" diff --git a/cmake/install/excludelist-win b/cmake/install/excludelist-win index 17793a113d9..ac3c479019e 100644 --- a/cmake/install/excludelist-win +++ b/cmake/install/excludelist-win @@ -3,17 +3,21 @@ ADVAPI32.dll COMCTL32.dll comdlg32.dll +d3d11.dll dwmapi.dll +dxgi.dll GDI32.dll IMM32.dll KERNEL32.dll MPR.DLL msvcrt.dll +netapi32.dll ole32.dll OLEAUT32.dll OPENGL32.DLL SHELL32.dll USER32.dll +userenv.dll UxTheme.dll VERSION.dll WINMM.DLL diff --git a/cmake/modules/BuildPlugin.cmake b/cmake/modules/BuildPlugin.cmake index 70e518c93ce..69af41ecb12 100644 --- a/cmake/modules/BuildPlugin.cmake +++ b/cmake/modules/BuildPlugin.cmake @@ -1,12 +1,12 @@ # BuildPlugin.cmake - Copyright (c) 2008 Tobias Doerffel # # description: build LMMS-plugin -# usage: BUILD_PLUGIN( MOCFILES EMBEDDED_RESOURCES UICFILES LINK ) +# usage: BUILD_PLUGIN( MOCFILES EMBEDDED_RESOURCES LINK ) INCLUDE(GenQrc) MACRO(BUILD_PLUGIN PLUGIN_NAME) - CMAKE_PARSE_ARGUMENTS(PLUGIN "" "LINK;EXPORT_BASE_NAME" "MOCFILES;EMBEDDED_RESOURCES;UICFILES" ${ARGN}) + CMAKE_PARSE_ARGUMENTS(PLUGIN "" "LINK;EXPORT_BASE_NAME" "MOCFILES;EMBEDDED_RESOURCES" ${ARGN}) SET(PLUGIN_SOURCES ${PLUGIN_UNPARSED_ARGUMENTS}) INCLUDE_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_BINARY_DIR}" "${CMAKE_SOURCE_DIR}/include") @@ -31,10 +31,9 @@ MACRO(BUILD_PLUGIN PLUGIN_NAME) ENDIF(ER_LEN) QT5_WRAP_CPP(plugin_MOC_out ${PLUGIN_MOCFILES}) - QT5_WRAP_UI(plugin_UIC_out ${PLUGIN_UICFILES}) FOREACH(f ${PLUGIN_SOURCES}) - ADD_FILE_DEPENDENCIES(${f} ${RCC_OUT} ${plugin_UIC_out}) + ADD_FILE_DEPENDENCIES(${f} ${RCC_OUT}) ENDFOREACH(f) IF(LMMS_BUILD_APPLE) @@ -45,10 +44,6 @@ MACRO(BUILD_PLUGIN PLUGIN_NAME) LINK_DIRECTORIES("${CMAKE_BINARY_DIR}" "${CMAKE_SOURCE_DIR}") LINK_LIBRARIES(${QT_LIBRARIES}) ENDIF(LMMS_BUILD_WIN32) - IF(LMMS_BUILD_MSYS AND CMAKE_BUILD_TYPE STREQUAL "Debug") - # Override Qt debug libraries with release versions - SET(QT_LIBRARIES "${QT_OVERRIDE_LIBRARIES}") - ENDIF() IF (NOT PLUGIN_LINK) SET(PLUGIN_LINK "MODULE") @@ -65,7 +60,7 @@ MACRO(BUILD_PLUGIN PLUGIN_NAME) IF(LMMS_BUILD_APPLE) IF ("${PLUGIN_LINK}" STREQUAL "SHARED") - SET_TARGET_PROPERTIES(${PLUGIN_NAME} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup") + TARGET_LINK_OPTIONS(${PLUGIN_NAME} PRIVATE -undefined dynamic_lookup) ENDIF() ENDIF(LMMS_BUILD_APPLE) IF(LMMS_BUILD_WIN32) diff --git a/cmake/modules/DetectMachine.cmake b/cmake/modules/DetectMachine.cmake index 388efeb820c..b9aa4c8c6e5 100644 --- a/cmake/modules/DetectMachine.cmake +++ b/cmake/modules/DetectMachine.cmake @@ -12,11 +12,6 @@ ELSE() SET(LMMS_BUILD_LINUX 1) ENDIF(WIN32) -# LMMS_BUILD_MSYS also set in build_winXX.sh -IF(LMMS_BUILD_WIN32 AND CMAKE_COMPILER_IS_GNUCXX AND DEFINED ENV{MSYSCON}) - SET(LMMS_BUILD_MSYS TRUE) -ENDIF() - MESSAGE("PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR}") SET(LMMS_HOST_X86 FALSE) SET(LMMS_HOST_X86_64 FALSE) @@ -92,7 +87,7 @@ IF(WIN32) endif() ELSE() # Detect target architecture based on compiler target triple e.g. "x86_64-pc-linux" - EXEC_PROGRAM( ${CMAKE_C_COMPILER} ARGS "-dumpmachine ${CMAKE_C_FLAGS}" OUTPUT_VARIABLE Machine ) + execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpmachine ${CMAKE_C_FLAGS} OUTPUT_VARIABLE Machine) MESSAGE("Machine: ${Machine}") STRING(REGEX MATCH "i.86" IS_X86 "${Machine}") STRING(REGEX MATCH "86_64|amd64" IS_X86_64 "${Machine}") diff --git a/cmake/modules/ErrorFlags.cmake b/cmake/modules/ErrorFlags.cmake new file mode 100644 index 00000000000..57cc6ad49f8 --- /dev/null +++ b/cmake/modules/ErrorFlags.cmake @@ -0,0 +1,72 @@ +# Shim the SYSTEM property for older CMake versions - we rely on this property +# to determine which set of error flags to use. +if(CMAKE_VERSION VERSION_LESS "3.25") + define_property(TARGET + PROPERTY SYSTEM + INHERITED + BRIEF_DOCS "Shim of built-in SYSTEM property for CMake versions less than 3.25" + FULL_DOCS "Non-functional, but allows the property to be inherited properly." + "See the CMake documentation at https://cmake.org/cmake/help/latest/prop_tgt/SYSTEM.html." + ) +endif() + +# Allow the user to control whether to treat warnings as errors +option(USE_WERROR "Treat compiler warnings as errors" OFF) + +# Compute the appropriate flags for the current compiler and options +if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + set(COMPILE_ERROR_FLAGS + "-Wall" # Enable most warnings by default + ) + set(THIRD_PARTY_COMPILE_ERROR_FLAGS + "-w" # Disable all warnings + ) + + if(CMAKE_COMPILER_IS_GNUCXX) + list(APPEND COMPILE_ERROR_FLAGS + # The following warning generates false positives that are difficult + # to work around, in particular when inlining calls to standard + # algorithms performed on single-element arrays. See, for example, + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111273. + "-Wno-array-bounds" # Permit out-of-bounds array subscripts + ) + + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11") + list(APPEND COMPILE_ERROR_FLAGS + # This has the same problems described above for "array-bounds". + "-Wno-stringop-overread" # Permit string functions overreading the source + ) + endif() + endif() + + if(USE_WERROR) + list(APPEND COMPILE_ERROR_FLAGS + "-Werror" # Treat warnings as errors + ) + endif() +elseif(MSVC) + set(COMPILE_ERROR_FLAGS + "/W2" # Enable some warnings by default + "/external:W0" # Don't emit warnings for third-party code + "/external:anglebrackets" # Consider headers included with angle brackets to be third-party + "/external:templates-" # Still emit warnings from first-party instantiations of third-party templates + # Silence "class X needs to have DLL-interface to be used by clients of + # class Y" warnings. These aren't trivial to address, and don't pose a + # problem for us since we build all modules with the same compiler and + # options, and dynamically link the CRT. + "/wd4251" + ) + set(THIRD_PARTY_COMPILE_ERROR_FLAGS + "/W0" # Disable all warnings + ) + + if(USE_WERROR) + list(APPEND COMPILE_ERROR_FLAGS + "/WX" # Treat warnings as errors + ) + endif() +endif() + +# Add the flags to the whole directory tree. We use the third-party flags for +# targets whose SYSTEM property is true, and the normal flags otherwise. +add_compile_options("$>,${THIRD_PARTY_COMPILE_ERROR_FLAGS},${COMPILE_ERROR_FLAGS}>") diff --git a/cmake/modules/FindPulseAudio.cmake b/cmake/modules/FindPulseAudio.cmake index 8ea2616cf53..0023d1d6329 100644 --- a/cmake/modules/FindPulseAudio.cmake +++ b/cmake/modules/FindPulseAudio.cmake @@ -18,13 +18,15 @@ ENDIF (PULSEAUDIO_INCLUDE_DIR AND PULSEAUDIO_LIBRARIES) IF (NOT WIN32) # use pkg-config to get the directories and then use these values # in the FIND_PATH() and FIND_LIBRARY() calls - INCLUDE(FindPkgConfig) - pkg_check_modules(PA libpulse) - set(_PASIncDir ${PA_INCLUDE_DIRS}) - set(_PASLinkDir ${PA_LIBRARY_DIRS}) - set(_PASLinkFlags ${PA_LDFLAGS}) - set(_PASCflags ${PA_CFLAGS}) - SET(PULSEAUDIO_DEFINITIONS ${_PASCflags}) + find_package(PkgConfig QUIET) + if(PKG_CONFIG_FOUND) + pkg_check_modules(PA libpulse) + set(_PASIncDir ${PA_INCLUDE_DIRS}) + set(_PASLinkDir ${PA_LIBRARY_DIRS}) + set(_PASLinkFlags ${PA_LDFLAGS}) + set(_PASCflags ${PA_CFLAGS}) + set(PULSEAUDIO_DEFINITIONS ${_PASCflags}) + endif() ENDIF (NOT WIN32) FIND_PATH(PULSEAUDIO_INCLUDE_DIR pulse/pulseaudio.h diff --git a/cmake/modules/FindSDL2.cmake b/cmake/modules/FindSDL2.cmake index 3bad1002ead..6e07f7affc2 100644 --- a/cmake/modules/FindSDL2.cmake +++ b/cmake/modules/FindSDL2.cmake @@ -33,6 +33,18 @@ find_package(SDL2 CONFIG QUIET) if(TARGET SDL2::SDL2) + # SDL2::SDL2 under MinGW is an interface target for reasons, so we can't get + # the library location from it. Print minimal information and return early. + get_target_property(sdl2_target_type SDL2::SDL2 TYPE) + if(sdl2_target_type STREQUAL "INTERFACE_LIBRARY") + unset(sdl2_target_type) + if(NOT SDL2_FIND_QUIETLY) + message(STATUS "Found SDL2 (found version \"${SDL2_VERSION}\")") + endif() + return() + endif() + unset(sdl2_target_type) + # Extract details for find_package_handle_standard_args get_target_property(SDL2_LIBRARY SDL2::SDL2 LOCATION) get_target_property(SDL2_INCLUDE_DIR SDL2::SDL2 INTERFACE_INCLUDE_DIRECTORIES) diff --git a/cmake/modules/FindSndio.cmake b/cmake/modules/FindSndio.cmake index e993702c29e..a1b24d06c5b 100644 --- a/cmake/modules/FindSndio.cmake +++ b/cmake/modules/FindSndio.cmake @@ -24,7 +24,7 @@ check_library_exists(sndio sio_open "${SNDIO_LIBRARY_DIR}" HAVE_SIO_OPEN) find_path(SNDIO_INCLUDE_DIR sndio.h) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SNDIO DEFAULT_MSG SNDIO_LIBRARY SNDIO_INCLUDE_DIR HAVE_SIO_OPEN) +find_package_handle_standard_args(Sndio DEFAULT_MSG SNDIO_LIBRARY SNDIO_INCLUDE_DIR HAVE_SIO_OPEN) if(SNDIO_FOUND) set(SNDIO_INCLUDE_DIRS "${SNDIO_INCLUDE_DIR}") diff --git a/cmake/modules/FindSoundIo.cmake b/cmake/modules/FindSoundIo.cmake index 0d905ecf21c..f5948f06156 100644 --- a/cmake/modules/FindSoundIo.cmake +++ b/cmake/modules/FindSoundIo.cmake @@ -11,6 +11,6 @@ find_path(SOUNDIO_INCLUDE_DIR NAMES soundio/soundio.h) find_library(SOUNDIO_LIBRARY NAMES soundio) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SOUNDIO DEFAULT_MSG SOUNDIO_LIBRARY SOUNDIO_INCLUDE_DIR) +find_package_handle_standard_args(SoundIo DEFAULT_MSG SOUNDIO_LIBRARY SOUNDIO_INCLUDE_DIR) mark_as_advanced(SOUNDIO_INCLUDE_DIR SOUNDIO_LIBRARY) diff --git a/cmake/modules/FindWine.cmake b/cmake/modules/FindWine.cmake index 024dac1ea50..19a0cffbbef 100644 --- a/cmake/modules/FindWine.cmake +++ b/cmake/modules/FindWine.cmake @@ -2,8 +2,15 @@ # Once done this will define # # WINE_FOUND - System has wine -# WINE_INCLUDE_DIRS - The wine include directories -# WINE_DEFINITIONS - Compiler switches required for using wine +# +# WINE_INCLUDE_DIR - Wine include directory +# WINE_BUILD - Path to winebuild +# WINE_CXX - Path to wineg++ +# WINE_GCC - Path to winegcc +# WINE_32_LIBRARY_DIRS - Path(s) to 32-bit wine libs +# WINE_32_FLAGS - 32-bit linker flags +# WINE_64_LIBRARY_DIRS - Path(s) to 64-bit wine libs +# WINE_64_FLAGS - 64-bit linker flags # MACRO(_findwine_find_flags output expression result) @@ -23,17 +30,49 @@ MACRO(_regex_replace_foreach EXPRESSION REPLACEMENT RESULT INPUT) ENDFOREACH() ENDMACRO() -LIST(APPEND CMAKE_PREFIX_PATH /opt/wine-stable /opt/wine-devel /opt/wine-staging /usr/lib/wine/) +# Prefer newest wine first +list(APPEND WINE_LOCATIONS + /opt/wine-staging + /opt/wine-devel + /opt/wine-stable + /usr/lib/wine) + +# Prepare bin search +foreach(_loc ${WINE_LOCATIONS}) + if(_loc STREQUAL /usr/lib/wine) + # /usr/lib/wine doesn't have a "bin" + list(APPEND WINE_CXX_LOCATIONS "${_loc}") + else() + # expect "bin" + list(APPEND WINE_CXX_LOCATIONS "${_loc}/bin") + endif() +endforeach() +# Fallback +list(APPEND WINE_CXX_LOCATIONS "/usr/bin") + +# Prefer most-common to least common +FIND_PROGRAM(WINE_CXX NAMES + wineg++ + wineg++-stable + PATHS + ${WINE_CXX_LOCATIONS} + NO_DEFAULT_PATH +) -FIND_PROGRAM(WINE_CXX - NAMES wineg++ winegcc winegcc64 winegcc32 winegcc-stable - PATHS /usr/lib/wine +FIND_PROGRAM(WINE_GCC NAMES + winegcc + winegcc-stable + PATHS + ${WINE_CXX_LOCATIONS} + NO_DEFAULT_PATH ) -FIND_PROGRAM(WINE_BUILD NAMES winebuild) + +FIND_PROGRAM(WINE_BUILD NAMES winebuild PATHS ${WINE_CXX_LOCATIONS} NO_DEFAULT_PATH) # Detect wine paths and handle linking problems IF(WINE_CXX) - EXEC_PROGRAM(${WINE_CXX} ARGS "-m32 -v /dev/zero" OUTPUT_VARIABLE WINEBUILD_OUTPUT_32) - EXEC_PROGRAM(${WINE_CXX} ARGS "-m64 -v /dev/zero" OUTPUT_VARIABLE WINEBUILD_OUTPUT_64) + # call wineg++ to obtain implied includes and libs + execute_process(COMMAND ${WINE_CXX} -m32 -v /dev/zero OUTPUT_VARIABLE WINEBUILD_OUTPUT_32 ERROR_QUIET) + execute_process(COMMAND ${WINE_CXX} -m64 -v /dev/zero OUTPUT_VARIABLE WINEBUILD_OUTPUT_64 ERROR_QUIET) _findwine_find_flags("${WINEBUILD_OUTPUT_32}" "^-isystem/usr/include$" BUGGED_WINEGCC) _findwine_find_flags("${WINEBUILD_OUTPUT_32}" "^-isystem" WINEGCC_INCLUDE_DIR) _findwine_find_flags("${WINEBUILD_OUTPUT_32}" "libwinecrt0\\.a.*" WINECRT_32) @@ -42,6 +81,9 @@ IF(WINE_CXX) _regex_replace_foreach("/wine/windows$" "" WINE_INCLUDE_HINT "${WINE_INCLUDE_HINT}") STRING(REGEX REPLACE "wine/libwinecrt0\\.a.*" "" WINE_32_LIBRARY_DIR "${WINECRT_32}") STRING(REGEX REPLACE "wine/libwinecrt0\\.a.*" "" WINE_64_LIBRARY_DIR "${WINECRT_64}") + # Handle winehq + STRING(REGEX REPLACE "/libwinecrt0\\.a.*" "/" WINE_32_LIBRARY_DIR "${WINE_32_LIBRARY_DIR}") + STRING(REGEX REPLACE "/libwinecrt0\\.a.*" "/" WINE_64_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") IF(BUGGED_WINEGCC) MESSAGE(WARNING "Your winegcc is unusable due to https://bugs.winehq.org/show_bug.cgi?id=46293,\n @@ -98,7 +140,11 @@ find_package_handle_standard_args(Wine DEFAULT_MSG WINE_CXX WINE_INCLUDE_DIRS) mark_as_advanced(WINE_INCLUDE_DIR WINE_LIBRARY WINE_CXX WINE_BUILD) IF(WINE_32_LIBRARY_DIR) - IF(WINE_32_LIBRARY_DIR MATCHES "wine*/lib") + IF(WINE_32_LIBRARY_DIR MATCHES "^/opt/wine-.*") + # winehq uses a singular lib directory + SET(WINE_32_FLAGS "-L${WINE_32_LIBRARY_DIR}") + SET(WINE_32_LIBRARY_DIRS "${WINE_32_LIBRARY_DIR}") + ELSEIF(WINE_32_LIBRARY_DIR MATCHES "wine*/lib") SET(WINE_32_FLAGS "-L${WINE_32_LIBRARY_DIR} -L${WINE_32_LIBRARY_DIR}../") SET(WINE_32_LIBRARY_DIRS "${WINE_32_LIBRARY_DIR}:${WINE_32_LIBRARY_DIR}/..") ELSE() @@ -108,7 +154,11 @@ IF(WINE_32_LIBRARY_DIR) ENDIF() IF(WINE_64_LIBRARY_DIR) - IF(WINE_64_LIBRARY_DIR MATCHES "wine*/lib") + IF(WINE_32_LIBRARY_DIR MATCHES "^/opt/wine-.*") + # winehq uses a singular lib directory + SET(WINE_64_FLAGS "-L${WINE_64_LIBRARY_DIR}") + SET(WINE_64_LIBRARY_DIRS "${WINE_64_LIBRARY_DIR}") + ELSEIF(WINE_64_LIBRARY_DIR MATCHES "wine*/lib") SET(WINE_64_FLAGS "-L${WINE_64_LIBRARY_DIR} -L${WINE_64_LIBRARY_DIR}../") SET(WINE_64_LIBRARY_DIRS "${WINE_64_LIBRARY_DIR}:${WINE_64_LIBRARY_DIR}/..") ELSE() @@ -117,6 +167,12 @@ IF(WINE_64_LIBRARY_DIR) ENDIF() ENDIF() -# Create winegcc wrapper +message(STATUS " WINE_INCLUDE_DIR: ${WINE_INCLUDE_DIR}") +message(STATUS " WINE_CXX: ${WINE_CXX}") +message(STATUS " WINE_GCC: ${WINE_GCC}") +message(STATUS " WINE_32_FLAGS: ${WINE_32_FLAGS}") +message(STATUS " WINE_64_FLAGS: ${WINE_64_FLAGS}") + +# Create winegcc (technically, wineg++) wrapper configure_file(${CMAKE_CURRENT_LIST_DIR}/winegcc_wrapper.in winegcc_wrapper @ONLY) SET(WINEGCC "${CMAKE_CURRENT_BINARY_DIR}/winegcc_wrapper") diff --git a/cmake/modules/PluginList.cmake b/cmake/modules/PluginList.cmake index 6feb0cb1417..a4e8ac70d6d 100644 --- a/cmake/modules/PluginList.cmake +++ b/cmake/modules/PluginList.cmake @@ -38,6 +38,7 @@ SET(LMMS_PLUGIN_LIST DynamicsProcessor Eq Flanger + GranularPitchShifter HydrogenImport LadspaBrowser LadspaEffect @@ -101,12 +102,3 @@ IF(LIST_PLUGINS) UNSET(LIST_PLUGINS CACHE) LIST_ALL_PLUGINS() ENDIF() - -IF(MSVC) - SET(MSVC_INCOMPATIBLE_PLUGINS - LadspaEffect - ) - message(WARNING "Compiling with MSVC. The following plugins are not available: ${MSVC_INCOMPATIBLE_PLUGINS}") - LIST(REMOVE_ITEM PLUGIN_LIST ${MSVC_INCOMPATIBLE_PLUGINS}) -ENDIF() - diff --git a/cmake/modules/StaticDependencies.cmake b/cmake/modules/StaticDependencies.cmake new file mode 100644 index 00000000000..d12c47d12c8 --- /dev/null +++ b/cmake/modules/StaticDependencies.cmake @@ -0,0 +1,141 @@ +# StaticDependencies.cmake - adds features similar to interface properties that +# are only transitive over static dependencies. +# +# Copyright (c) 2024 Dominic Clark +# +# Redistribution and use is allowed according to the terms of the New BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +define_property(TARGET + PROPERTY STATIC_COMPILE_DEFINITIONS + BRIEF_DOCS "Compile definitions to be used by targets linking statically to this one" + FULL_DOCS "Behaves similarly to INTERFACE_COMPILE_DEFINITIONS, but only over static dependencies." + "Effectively becomes private once an executable module is reached." +) + +define_property(TARGET + PROPERTY STATIC_LINK_LIBRARIES + BRIEF_DOCS "Link libraries to be included in targets linking statically to this one" + FULL_DOCS "Behaves similarly to INTERFACE_LINK_LIBRARIES, but only over static dependencies." + "Effectively becomes private once an executable module is reached." +) + +# Link a target statically to a set of libraries. Forward the given arguments to +# `target_link_libraries`, but also perform two additional functions. Firstly, +# ensure that the given libraries will be linked into any module that also +# links the given target (allowing, for example, object libraries and private +# static libraries to be used transitively). Secondly, add any static compile +# definitions from the given libraries to the set of compile definitions used +# to build the given target. +# +# This function must be used in order for static requirements as defined in this +# module to be inherited transitively. Using `target_link_libraries` instead +# will break the chain for static link libraries and static compile definitions. +# +# Usage: +# target_static_libraries( +# # The target to which to add the libraries +# [] # Optionally, the scope to use for the following libraries +# ... # The libraries to which to link +# [ ...]... +# ) +function(target_static_libraries target) + # Target types that have a link step + set(linked_target_types "MODULE_LIBRARY" "SHARED_LIBRARY" "EXECUTABLE") + # Possible scopes for dependencies + set(scopes "PRIVATE" "PUBLIC" "INTERFACE") + + get_target_property(target_type "${target}" TYPE) + set(scope "") + + # Iterate over the dependencies (and possibly scopes) that we were given + foreach(dependency IN LISTS ARGN) + # If we have a scope, store it so we can apply it to upcoming libraries + if(dependency IN_LIST scopes) + set(scope "${dependency}") + continue() + endif() + + # Link the target to the current dependency. (Note: `${scope}` is + # unquoted so that the argument disappears if no scope was given.) + target_link_libraries("${target}" ${scope} "${dependency}") + + # Store the dependency so it can be linked in with this target later + set_property( + TARGET "${target}" + APPEND + PROPERTY STATIC_LINK_LIBRARIES + "${dependency}" + ) + + # If the dependency is a target, it may have some of our custom + # properties defined on it, so we have a bit more work to do + if(TARGET "${dependency}") + # Ensure it makes sense to link statically to this dependency + get_target_property(dependency_type "${dependency}" TYPE) + if(dependency_type IN_LIST linked_target_types) + message(SEND_ERROR "Cannot link statically to shared module ${dependency}") + endif() + + # Transitively include static definitions and libraries + set(defs "$>") + set(libs "$>") + set_property( + TARGET "${target}" + APPEND + PROPERTY STATIC_COMPILE_DEFINITIONS + "${defs}" + ) + set_property( + TARGET "${target}" + APPEND + PROPERTY STATIC_LINK_LIBRARIES + "${libs}" + ) + + # Add the dependency's transitive static compile definitions. + # (Note: definitions are private so dynamically linked dependents + # won't pick them up; a static dependent will have them set when + # this function is called for it.) + target_compile_definitions("${target}" PRIVATE "${defs}") + + # If the target has a link step, add the transitive static + # dependencies. (Note: we use `LINK_ONLY` so the caller can still + # control usage requirements through the normal use of scopes. Only + # transitive dependencies are needed here: the direct dependency was + # added earlier on. We have to append to `LINK_LIBRARIES` directly, + # rather than use `target_link_libraries(PRIVATE)`, in order to + # remain compatible with the scopeless signature of that command.) + if(target_type IN_LIST linked_target_types) + set_property( + TARGET "${target}" + APPEND + PROPERTY LINK_LIBRARIES + "$" + ) + endif() + endif() + endforeach() +endfunction() + +# Add compile definitions to a target only for use with dependents linking +# statically to it. +# +# Behaves like `target_compile_definitions(INTERFACE)`, except the definitions +# will not be inherited beyond any executable module into which the target is +# actually linked. The definitions are added to the `STATIC_COMPILE_DEFINITIONS` +# property on the target. +# +# Usage: +# target_static_definitions( +# # The target to which to add the definitions +# ... # The definitions to add to the target +# ) +function(target_static_definitions target) + set_property( + TARGET "${target}" + APPEND + PROPERTY STATIC_COMPILE_DEFINITIONS + "${ARGN}" + ) +endfunction() diff --git a/cmake/msys/extract_debs.sh b/cmake/msys/extract_debs.sh deleted file mode 100644 index 939912bb2c3..00000000000 --- a/cmake/msys/extract_debs.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -set -e - -ppa_dir=./ppa/ - -pushd $ppa_dir - -for f in *.deb; do - echo "Extracting $f..." - ar xv "$f" - rm debian-binary - rm control.tar.* - tar xf data.tar.* --exclude=*mingw*/bin/fluid - rm data.tar.* -done - -popd - -echo "Your extracted files should be located in $ppa_dir" diff --git a/cmake/msys/fetch_ppa.sh b/cmake/msys/fetch_ppa.sh deleted file mode 100644 index ba8697c817d..00000000000 --- a/cmake/msys/fetch_ppa.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/bash - -# Trusty=14.04, Precise=12.04 -PPA_DISTRO=trusty - -# Architecture=i386, amd64 -PPA_ARCH=amd64 - -# These shouldn't change -PPA_HOST=http://ppa.launchpad.net -PPA_USER=tobydox -PPA_PROJECT=mingw-x-trusty -PPA_ROOT=$PPA_HOST/$PPA_USER/$PPA_PROJECT/ubuntu - -PPA_URL=$PPA_ROOT/dists/$PPA_DISTRO/main/binary-$PPA_ARCH/Packages.gz - -ppa_dir=./ppa/ - -temp_file=/tmp/ppa_listing_$$ -temp_temp_file=/tmp/ppa_listing_temp_$$ - -skip_files="binutils openssl flac libgig libogg libvorbis x-bootstrap zlib" -skip_files="$skip_files x-runtime gcc qt_4 qt5 x-stk pkgconfig" -skip_files="$skip_files glib2 libpng" - -echo "Connecting to $PPA_URL to get list of packages..." -wget -qO- $PPA_URL | gzip -d -c | grep "Filename:" > $temp_file - -for j in $skip_files ; do - grep -v "$j" $temp_file > $temp_temp_file - mv $temp_temp_file $temp_file -done - -line_count=$(wc -l $temp_file |awk '{print $1}') - -echo "Found $line_count packages for download..." - -echo "Downloading packages. They will be saved to $ppa_dir" - -mkdir $ppa_dir - -while read -r j -do - echo "Downloading $j..." - echo "$PPA_ROOT/$j" - wget -qO "$ppa_dir$(basename "$j")" "$(echo "$PPA_ROOT/$j" | sed 's/\/Filename: /\//gi')" -done < $temp_file - - -echo "Cleaning up temporary files..." -rm -rf $temp_file - -echo "Packages have been saved to $ppa_dir. Please run extract_debs.sh" diff --git a/cmake/msys/msys_helper.sh b/cmake/msys/msys_helper.sh deleted file mode 100644 index a6a7e6aae38..00000000000 --- a/cmake/msys/msys_helper.sh +++ /dev/null @@ -1,226 +0,0 @@ -#!/bin/bash - -set -eu - -# Git repo information -fork="lmms" # i.e. "lmms" or "tobydox" -branch="master" # i.e. "master" or "stable-1.2" - -# Console colors -red="\\x1B[1;31m" -green="\\x1B[1;32m" -yellow="\\x1B[1;33m" -plain="\\x1B[0m" - -function info() { echo -e "\n${green}$1${plain}"; } -function warn() { echo -e "\n${yellow}$1${plain}"; } -function err() { echo -e "\n${red}$1${plain}"; exit 1;} - -info "Checking for mingw environment" -if ! env | grep MINGW; then - err " - Failed. Please relaunch using MinGW shell" -fi - -info "Preparing the git directory..." -mkdir "$HOME/.git" || true -touch "$HOME/.git/config" > /dev/null 2>&1 -git config --global http.sslverify false - -info "Cloning the repository..." -if [ -d ./lmms ]; then - warn " - Skipping, ./lmms already exists" -else - git clone -b $branch https://github.com/$fork/lmms.git -fi - -info "Fetching ppa using cmake/msys/fetch_ppas.sh..." -if [ -d "$HOME/ppa" ]; then - warn " - Skipping, $HOME/ppa already exists" -else - lmms/cmake/msys/fetch_ppa.sh -fi - -info "Extracting debs to $HOME/ppa/opt/, etc..." -if [ -d "$HOME/ppa/opt" ]; then - warn " - Skipping, $HOME/ppa/opt already exists" -else - lmms/cmake/msys/extract_debs.sh -fi - -info "Preparing library merge, making all qt headers writable..." -chmod u+w /mingw64/include/qt4 -R -chmod u+w /mingw32/include/qt4 -R - -info "Merging mingw headers and libraries from ppa over existing system libraries..." -if ! find /mingw64 | grep sndfile.h; then - command cp -r "$HOME/ppa/opt/mingw"* / -else - warn " - Skipping, sndfile.h has already been merged" -fi - -fltkver="1.3.3" -oggver="1.3.2" -vorbisver="1.3.5" -flacver="1.3.2" -gigver="4.0.0" -stkver="4.5.1" - -mingw_root="/$(echo "$MSYSTEM"|tr '[:upper:]' '[:lower:]')" - -info "Downloading and building fltk $fltkver" -if ! command -v fluid; then - wget http://fltk.org/pub/fltk/$fltkver/fltk-$fltkver-source.tar.gz -O "$HOME/fltk-source.tar.gz" - tar zxf "$HOME/fltk-source.tar.gz" -C "$HOME/" - pushd "$HOME/fltk-$fltkver" - - info " - Compiling fltk $fltkver..." - ./configure --prefix="$mingw_root" --enable-shared - make - - info " - Installing fltk..." - make install - -# ln -s $mingw_root/usr/local/bin/fluid.exe $mingw_root/bin/fluid.exe - popd -else - warn " - Skipping, fluid binary already exists" -fi - -info "Downloading and building libogg $oggver" -if [ ! -e "$mingw_root/lib/libogg.dll.a" ]; then - wget http://downloads.xiph.org/releases/ogg/libogg-$oggver.tar.xz -O "$HOME/libogg-source.tar.xz" - tar xf "$HOME/libogg-source.tar.xz" -C "$HOME/" - pushd "$HOME/libogg-$oggver" - - info " - Compiling libogg $oggver..." - ./configure --prefix="$mingw_root" - make - - info " - Installing libogg..." - make install - # for some reason libgig needs this - ./configure --prefix="/opt$mingw_root" - make - - info " - Installing libogg..." - make install - - popd -else - warn " - Skipping, libogg binary already exists" -fi - -info "Downloading and building libvorbis $vorbisver" -if [ ! -e "$mingw_root/lib/libvorbis.dll.a" ]; then - wget http://downloads.xiph.org/releases/vorbis/libvorbis-$vorbisver.tar.xz -O "$HOME/libvorbis-source.tar.xz" - tar xf "$HOME/libvorbis-source.tar.xz" -C "$HOME/" - pushd "$HOME/libvorbis-$vorbisver" - - info " - Compiling libvorbis $vorbisver..." - ./configure --prefix="$mingw_root" - make - - info " - Installing libvorbis..." - make install - - # for some reason libgig needs this - ./configure --prefix="/opt$mingw_root" - make - info " - Installing libvorbis..." - make install - - popd -else - warn " - Skipping, libvorbis binary already exists" -fi - -info "Downloading and building flac $flacver" - -if [ ! -e "$mingw_root/lib/libFLAC.dll.a" ]; then - - wget http://downloads.xiph.org/releases/flac/flac-$flacver.tar.xz -O "$HOME/flac-source.tar.xz" - tar xf "$HOME/flac-source.tar.xz" -C "$HOME/" - pushd "$HOME/flac-$flacver" - - info " - Compiling flac $flacver..." - ./configure --prefix="$mingw_root" - make - - info " - Installing flac..." - make install - - # for some reason libgig needs this - ./configure --prefix="/opt$mingw_root" - make - - info " - Installing flac..." - make install - - popd -else - warn " - Skipping, libvorbis flac already exists" -fi - -info "Downloading and building libgig $gigver" - -if [ ! -e "$mingw_root/lib/libgig/libgig.dll.a" ]; then - wget http://download.linuxsampler.org/packages/libgig-$gigver.tar.bz2 -O "$HOME/gig-source.tar.xz" - tar xf "$HOME/gig-source.tar.xz" -C "$HOME/" - pushd "$HOME/libgig-$gigver" - - info " - Compiling libgig $gigver..." - ./configure --prefix="$mingw_root" - make - - info " - Installing libgig..." - make install - - mv "$mingw_root/lib/bin/libakai-0.dll" "$mingw_root/bin" - mv "$mingw_root/lib/bin/libgig-7.dll" "$mingw_root/bin" - - popd -else - warn " - Skipping, libgig binary already exists" -fi - -info "Downloading and building stk $stkver" - -if [ ! -e "$mingw_root/lib/libstk.dll" ]; then - wget http://ccrma.stanford.edu/software/stk/release/stk-$stkver.tar.gz -O "$HOME/stk-source.tar.xz" - tar xf "$HOME/stk-source.tar.xz" -C "$HOME/" - pushd "$HOME/stk-$stkver" - - info " - Compiling stk $stkver..." - ./configure --prefix="$mingw_root" - make - - info " - Installing stk..." - make install - - mv "$mingw_root/lib/libstk.so" "$mingw_root/lib/libstk.dll" - mv "$mingw_root/lib/libstk-$stkver.so" "$mingw_root/lib/libstk-$stkver.dll" - - popd -else - warn " - Skipping, stk binary already exists" -fi - -# make a symlink to make cmake happy -if [ "$mingw_root" = "/mingw64" ]; then - if [ ! -e /opt/mingw64/bin/x86_64-w64-mingw32-pkg-config ]; then - ln -s /usr/bin/pkg-config /opt/mingw64/bin/x86_64-w64-mingw32-pkg-config - fi -elif [ "$mingw_root" = "/mingw32" ]; then - if [ ! -e /opt/mingw32/bin/i686-w64-mingw32-pkg-config ]; then - ln -s /usr/bin/pkg-config /opt/mingw32/bin/i686-w64-mingw32-pkg-config - fi -fi - -info "Cleaning up..." -rm -rf "$HOME/fltk-$fltkver" -rm -rf "$HOME/libogg-$oggver" -rm -rf "$HOME/libvorbis-$vorbisver" -rm -rf "$HOME/flac-$flacver" -rm -rf "$HOME/libgig-$gigver" -rm -rf "$HOME/stk-$stkver" -info "Done." diff --git a/cmake/nsis/CMakeLists.txt b/cmake/nsis/CMakeLists.txt index ee1bd45c305..e926e074d76 100644 --- a/cmake/nsis/CMakeLists.txt +++ b/cmake/nsis/CMakeLists.txt @@ -3,10 +3,9 @@ if(LMMS_MSVC_YEAR) SET(WIN_PLATFORM "msvc${LMMS_MSVC_YEAR}") endif() -SET(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/cmake/nsis/nsis_branding.bmp") -IF(MSVC) - STRING(REPLACE "/" "\\\\" CPACK_PACKAGE_ICON ${CPACK_PACKAGE_ICON}) -ENDIF(MSVC) +# the final slash needs to be flipped for CPACK_PACKAGE_ICON to work: +# https://cmake.org/pipermail/cmake/2008-June/022085.html +SET(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/cmake/nsis\\\\nsis_branding.bmp") SET(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/cmake/nsis/icon.ico") SET(CPACK_NSIS_INSTALLED_ICON_NAME "${CMAKE_PROJECT_NAME}.exe" PARENT_SCOPE) SET(CPACK_NSIS_DISPLAY_NAME "${PROJECT_NAME_UCASE} ${VERSION}" PARENT_SCOPE) @@ -44,25 +43,6 @@ IF(WIN64) ") ENDIF() -# Fix windows paths for msys -IF(LMMS_BUILD_MSYS) - STRING(REPLACE "/" "\\\\" CPACK_PACKAGE_ICON "${CPACK_PACKAGE_ICON}") - STRING(REPLACE "/" "\\\\" CPACK_NSIS_MUI_ICON "${CPACK_NSIS_MUI_ICON}") - STRING(REPLACE "/" "\\\\" CPACK_NSIS_DEFINES "${CPACK_NSIS_DEFINES}") - STRING(REPLACE "/" "\\\\" CMAKE_BINARY_DIR_FIX "${CMAKE_BINARY_DIR}") - - # FIXME: there's no easy way to fix $INST_DIR, so we'll redefine it manually - IF(WIN64) - SET(NSIS_ARCH "win64") - ELSE() - SET(NSIS_ARCH "win32") - ENDIF() - SET(CPACK_NSIS_DEFINES " - ${CPACK_NSIS_DEFINES} - !define /redef INST_DIR ${CMAKE_BINARY_DIR_FIX}\\\\_CPack_Packages\\\\${NSIS_ARCH}\\\\NSIS\\\\${CPACK_PACKAGE_FILE_NAME} - ") -ENDIF() - # Setup missing parent scopes SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_FILE_NAME}" PARENT_SCOPE) SET(CPACK_NSIS_DEFINES "${CPACK_NSIS_DEFINES}" PARENT_SCOPE) diff --git a/cmake/toolchains/MSYS-32.cmake b/cmake/toolchains/MSYS-32.cmake deleted file mode 100644 index 698dd543791..00000000000 --- a/cmake/toolchains/MSYS-32.cmake +++ /dev/null @@ -1,4 +0,0 @@ -INCLUDE(${CMAKE_CURRENT_LIST_DIR}/common/MSYS.cmake) -INCLUDE(${CMAKE_CURRENT_LIST_DIR}/common/Win32.cmake) - -SET(MINGW_PREFIX /mingw32) \ No newline at end of file diff --git a/cmake/toolchains/MSYS-64.cmake b/cmake/toolchains/MSYS-64.cmake deleted file mode 100644 index 8becd51b3d9..00000000000 --- a/cmake/toolchains/MSYS-64.cmake +++ /dev/null @@ -1,5 +0,0 @@ -INCLUDE(${CMAKE_CURRENT_LIST_DIR}/common/MSYS.cmake) -INCLUDE(${CMAKE_CURRENT_LIST_DIR}/common/Win64.cmake) - -SET(MINGW_PREFIX /mingw64) -SET(MINGW_PREFIX32 /mingw32) diff --git a/cmake/toolchains/MinGW-W64-32.cmake b/cmake/toolchains/MinGW-W64-32.cmake new file mode 100644 index 00000000000..acbdef8dd5a --- /dev/null +++ b/cmake/toolchains/MinGW-W64-32.cmake @@ -0,0 +1,6 @@ + +set(WIN64 FALSE) + +set(CMAKE_SYSTEM_PROCESSOR i686) + +include(${CMAKE_CURRENT_LIST_DIR}/common/MinGW-W64.cmake) diff --git a/cmake/toolchains/MinGW-W64-64.cmake b/cmake/toolchains/MinGW-W64-64.cmake new file mode 100644 index 00000000000..b3cf59b4775 --- /dev/null +++ b/cmake/toolchains/MinGW-W64-64.cmake @@ -0,0 +1,9 @@ + +set(WIN64 TRUE) + +set(CMAKE_SYSTEM_PROCESSOR x86_64) +set(CMAKE_SYSTEM_PROCESSOR32 i686) + +set(CMAKE_TOOLCHAIN_FILE_32 "${CMAKE_CURRENT_LIST_DIR}/MinGW-W64-32.cmake") + +include(${CMAKE_CURRENT_LIST_DIR}/common/MinGW-W64.cmake) diff --git a/cmake/toolchains/Ubuntu-MinGW-W64-32.cmake b/cmake/toolchains/Ubuntu-MinGW-W64-32.cmake deleted file mode 100644 index 6f7ec6f23d5..00000000000 --- a/cmake/toolchains/Ubuntu-MinGW-W64-32.cmake +++ /dev/null @@ -1,2 +0,0 @@ -INCLUDE(${CMAKE_CURRENT_LIST_DIR}/common/Win32.cmake) -INCLUDE(${CMAKE_CURRENT_LIST_DIR}/common/Ubuntu-MinGW-W64.cmake) diff --git a/cmake/toolchains/Ubuntu-MinGW-W64-64.cmake b/cmake/toolchains/Ubuntu-MinGW-W64-64.cmake deleted file mode 100644 index a1b33ec6795..00000000000 --- a/cmake/toolchains/Ubuntu-MinGW-W64-64.cmake +++ /dev/null @@ -1,4 +0,0 @@ -SET(CMAKE_TOOLCHAIN_FILE_32 "${CMAKE_CURRENT_LIST_DIR}/Ubuntu-MinGW-W64-32.cmake") - -INCLUDE(${CMAKE_CURRENT_LIST_DIR}/common/Win64.cmake) -INCLUDE(${CMAKE_CURRENT_LIST_DIR}/common/Ubuntu-MinGW-W64.cmake) diff --git a/cmake/toolchains/Ubuntu-MinGW-X-Trusty-32.cmake b/cmake/toolchains/Ubuntu-MinGW-X-Trusty-32.cmake deleted file mode 100644 index 0103d35e72b..00000000000 --- a/cmake/toolchains/Ubuntu-MinGW-X-Trusty-32.cmake +++ /dev/null @@ -1,3 +0,0 @@ -SET(MINGW_PREFIX /opt/mingw32) - -INCLUDE(${CMAKE_CURRENT_LIST_DIR}/common/Ubuntu-MinGW-X-Trusty.cmake) diff --git a/cmake/toolchains/Ubuntu-MinGW-X-Trusty-64.cmake b/cmake/toolchains/Ubuntu-MinGW-X-Trusty-64.cmake deleted file mode 100644 index 0f448fef527..00000000000 --- a/cmake/toolchains/Ubuntu-MinGW-X-Trusty-64.cmake +++ /dev/null @@ -1,9 +0,0 @@ -SET(MINGW_PREFIX /opt/mingw64) -SET(MINGW_PREFIX32 /opt/mingw32) - -SET(WIN64 TRUE) - -SET(CMAKE_TOOLCHAIN_FILE_32 "${CMAKE_CURRENT_LIST_DIR}/Ubuntu-MinGW-X-Trusty-32.cmake") -SET(CMAKE_PREFIX_PATH_32 "${MINGW_PREFIX32}") - -INCLUDE(${CMAKE_CURRENT_LIST_DIR}/common/Ubuntu-MinGW-X-Trusty.cmake) diff --git a/cmake/toolchains/common/MSYS.cmake b/cmake/toolchains/common/MSYS.cmake deleted file mode 100644 index 0b27e8d3246..00000000000 --- a/cmake/toolchains/common/MSYS.cmake +++ /dev/null @@ -1,22 +0,0 @@ -# The target environment -SET(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX}) -SET(CMAKE_INSTALL_PREFIX ${MINGW_PREFIX}) - -# Windows msys mingw ships with a mostly-suitable preconfigured environment -SET(STRIP ${MINGW_PREFIX}/bin/strip) -SET(CMAKE_RC_COMPILER ${MINGW_PREFIX}/bin/windres) -SET(CMAKE_C_COMPILER ${MINGW_PREFIX}/bin/gcc) -SET(CMAKE_CXX_COMPILER ${MINGW_PREFIX}/bin/g++) - -# For 32-bit vst support -IF(WIN64) - # Specify the 32-bit cross compiler - SET(CMAKE_C_COMPILER32 ${MINGW_PREFIX32}/bin/gcc) - SET(CMAKE_CXX_COMPILER32 ${MINGW_PREFIX32}/bin/g++) -ENDIF() - -# Msys compiler does not support @CMakeFiles/Include syntax -SET(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES OFF) -SET(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES OFF) - -SET(LMMS_BUILD_MSYS 1) diff --git a/cmake/toolchains/common/MinGW-W64.cmake b/cmake/toolchains/common/MinGW-W64.cmake new file mode 100644 index 00000000000..11b6a92d414 --- /dev/null +++ b/cmake/toolchains/common/MinGW-W64.cmake @@ -0,0 +1,24 @@ +# Toolchain for MinGW compiler + +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_VERSION 1) + +set(TOOLCHAIN_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32) +set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) +set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) + +set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) +set(ENV{PKG_CONFIG} /usr/bin/${TOOLCHAIN_PREFIX}-pkg-config) + +if(WIN64) + set(TOOLCHAIN_PREFIX32 ${CMAKE_SYSTEM_PROCESSOR32}-w64-mingw32) + set(CMAKE_C_COMPILER32 ${TOOLCHAIN_PREFIX32}-gcc) + set(CMAKE_CXX_COMPILER32 ${TOOLCHAIN_PREFIX32}-g++) +endif() + +# Search for programs in the build host directories +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +# Search for libraries and headers in the target directories +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/cmake/toolchains/common/Ubuntu-MinGW-W64.cmake b/cmake/toolchains/common/Ubuntu-MinGW-W64.cmake deleted file mode 100644 index 2f78a441e7a..00000000000 --- a/cmake/toolchains/common/Ubuntu-MinGW-W64.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# Toolchain for Ubuntu MinGw compiler shipped with the mingw-w64 and -# g++-mingw-w64 packages -SET(TOOLCHAIN_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32) -set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) -set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) -set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) - -set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) -SET(ENV{PKG_CONFIG} /usr/bin/${TOOLCHAIN_PREFIX}-pkg-config) - -IF(WIN64) - SET(TOOLCHAIN_PREFIX32 ${CMAKE_SYSTEM_PROCESSOR32}-w64-mingw32) - SET(CMAKE_C_COMPILER32 ${TOOLCHAIN_PREFIX32}-gcc) - SET(CMAKE_CXX_COMPILER32 ${TOOLCHAIN_PREFIX32}-g++) -ENDIF() - -INCLUDE(${CMAKE_CURRENT_LIST_DIR}/WinCrossCompile.cmake) diff --git a/cmake/toolchains/common/Ubuntu-MinGW-X-Trusty.cmake b/cmake/toolchains/common/Ubuntu-MinGW-X-Trusty.cmake deleted file mode 100644 index 686f4497f47..00000000000 --- a/cmake/toolchains/common/Ubuntu-MinGW-X-Trusty.cmake +++ /dev/null @@ -1,58 +0,0 @@ -IF(WIN64) - INCLUDE(${CMAKE_CURRENT_LIST_DIR}/Win64.cmake) -ELSE() - INCLUDE(${CMAKE_CURRENT_LIST_DIR}/Win32.cmake) -ENDIF() -INCLUDE(${CMAKE_CURRENT_LIST_DIR}/WinCrossCompile.cmake) - -# The target environment -SET(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX}) -SET(CMAKE_INSTALL_PREFIX ${MINGW_PREFIX}) - -# Linux mingw requires explicitly defined tools to prevent clash with native system tools -SET(MINGW_TOOL_PREFIX ${MINGW_PREFIX}/bin/${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-) - -# Specify the cross compiler -SET(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}gcc) -SET(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}g++) -SET(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres) - -# Mingw tools -SET(STRIP ${MINGW_TOOL_PREFIX}strip) -SET(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config) - -# For 32-bit vst support -IF(WIN64) - # Specify the 32-bit cross compiler - SET(MINGW_TOOL_PREFIX32 ${MINGW_PREFIX32}/bin/${CMAKE_SYSTEM_PROCESSOR32}-w64-mingw32-) - SET(CMAKE_C_COMPILER32 ${MINGW_TOOL_PREFIX32}gcc) - SET(CMAKE_CXX_COMPILER32 ${MINGW_TOOL_PREFIX32}g++) -ENDIF() - -INCLUDE_DIRECTORIES(${MINGW_PREFIX}/include) - -LINK_DIRECTORIES(${MINGW_PREFIX}/lib ${MINGW_PREFIX}/bin) - -# Qt tools -SET(QT_BINARY_DIR ${MINGW_PREFIX}/bin) -SET(QT_QMAKE_EXECUTABLE ${QT_BINARY_DIR}/qmake) - -# Echo modified cmake vars to screen for debugging purposes -IF(NOT DEFINED ENV{MINGW_DEBUG_INFO}) - MESSAGE("") - MESSAGE("Custom cmake vars: (blank = system default)") - MESSAGE("-----------------------------------------") - MESSAGE("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}") - MESSAGE("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}") - MESSAGE("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}") - MESSAGE("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}") - MESSAGE("* MINGW_TOOL_PREFIX32 : ${MINGW_TOOL_PREFIX32}") - MESSAGE("* CMAKE_C_COMPILER32 : ${CMAKE_C_COMPILER32}") - MESSAGE("* CMAKE_CXX_COMPILER32 : ${CMAKE_CXX_COMPILER32}") - MESSAGE("* STRIP : ${STRIP}") - MESSAGE("* QT_BINARY_DIR : ${QT_BINARY_DIR}") - MESSAGE("* QT_QMAKE_EXECUTABLE : ${QT_QMAKE_EXECUTABLE}") - MESSAGE("") - # So that the debug info only appears once - SET(ENV{MINGW_DEBUG_INFO} SHOWN) -ENDIF() diff --git a/cmake/toolchains/common/Win32.cmake b/cmake/toolchains/common/Win32.cmake deleted file mode 100644 index bc20775d6c0..00000000000 --- a/cmake/toolchains/common/Win32.cmake +++ /dev/null @@ -1,6 +0,0 @@ -SET(CMAKE_SYSTEM_NAME Windows) -SET(CMAKE_SYSTEM_VERSION 1) - -SET(CMAKE_SYSTEM_PROCESSOR i686) - -SET(WIN64 FALSE) diff --git a/cmake/toolchains/common/Win64.cmake b/cmake/toolchains/common/Win64.cmake deleted file mode 100644 index 155a658f42b..00000000000 --- a/cmake/toolchains/common/Win64.cmake +++ /dev/null @@ -1,7 +0,0 @@ -SET(CMAKE_SYSTEM_NAME Windows) -SET(CMAKE_SYSTEM_VERSION 1) - -SET(CMAKE_SYSTEM_PROCESSOR x86_64) -SET(CMAKE_SYSTEM_PROCESSOR32 i686) - -SET(WIN64 TRUE) diff --git a/cmake/toolchains/common/WinCrossCompile.cmake b/cmake/toolchains/common/WinCrossCompile.cmake deleted file mode 100644 index a2d6ff2e929..00000000000 --- a/cmake/toolchains/common/WinCrossCompile.cmake +++ /dev/null @@ -1,9 +0,0 @@ -# Required by cmake if `uname -s` is inadaquate -SET(CMAKE_SYSTEM_NAME Windows) -SET(CMAKE_SYSTEM_VERSION 1) - -# Search for programs in the build host directories -SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -# For libraries and headers in the target directories -SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) \ No newline at end of file diff --git a/data/projects/demos/unfa-Spoken.mmpz b/data/projects/demos/unfa-Spoken.mmpz index 66b7589d106..58d00d50d69 100644 Binary files a/data/projects/demos/unfa-Spoken.mmpz and b/data/projects/demos/unfa-Spoken.mmpz differ diff --git a/data/projects/shorties/Greshz-CoolSnip.mmpz b/data/projects/shorties/Greshz-CoolSnip.mmpz deleted file mode 100644 index dadeef1ba73..00000000000 Binary files a/data/projects/shorties/Greshz-CoolSnip.mmpz and /dev/null differ diff --git a/data/themes/classic/autoscroll_continuous_on.png b/data/themes/classic/autoscroll_continuous_on.png new file mode 100644 index 00000000000..0140d5cc517 Binary files /dev/null and b/data/themes/classic/autoscroll_continuous_on.png differ diff --git a/data/themes/classic/autoscroll_stepped_on.png b/data/themes/classic/autoscroll_stepped_on.png new file mode 100644 index 00000000000..e54489f7a1d Binary files /dev/null and b/data/themes/classic/autoscroll_stepped_on.png differ diff --git a/data/themes/classic/fader_background.png b/data/themes/classic/fader_background.png deleted file mode 100644 index 682ff4c9281..00000000000 Binary files a/data/themes/classic/fader_background.png and /dev/null differ diff --git a/data/themes/classic/fader_leds.png b/data/themes/classic/fader_leds.png deleted file mode 100644 index 6c673cf36d1..00000000000 Binary files a/data/themes/classic/fader_leds.png and /dev/null differ diff --git a/data/themes/classic/style.css b/data/themes/classic/style.css index f61d4ba580c..95737ed3afe 100644 --- a/data/themes/classic/style.css +++ b/data/themes/classic/style.css @@ -203,7 +203,9 @@ lmms--gui--GroupBox { lmms--gui--Oscilloscope { background: none; border: none; - qproperty-normalColor: rgb(71, 253, 133); + qproperty-leftChannelColor: rgb(71, 253, 133); + qproperty-rightChannelColor: rgb(238, 253, 71); + qproperty-otherChannelsColor: rgb(71, 235, 253); qproperty-clippingColor: rgb(255, 64, 64); } @@ -350,14 +352,24 @@ lmms--gui--TrackView > QWidget { /* autoscroll, loop, stop behaviour toggle buttons */ -/* track background colors */ +/* track background config */ lmms--gui--TrackContentWidget { - qproperty-darkerColor: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 rgb( 50, 50, 50 ), stop:0.33 rgb( 20, 20, 20 ), stop:1 rgb( 15, 15, 15 ) ); - qproperty-lighterColor: qlineargradient(x1:0, y1:0, x2:0, y2:1, - stop:0 rgb( 50, 50, 50 ), stop:0.33 rgb( 40, 40, 40 ), stop:1 rgb( 30, 30, 30 ) ); - qproperty-gridColor: rgba( 0, 0, 0, 160 ); - qproperty-embossColor: rgba( 140, 140, 140, 64 ); + /* colors */ + qproperty-darkerColor: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(50, 50, 50), stop:0.33 rgb(20, 20, 20), stop:1 rgb(15, 15, 15)); + qproperty-lighterColor: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgb(50, 50, 50), stop:0.33 rgb(40, 40, 40), stop:1 rgb(30, 30, 30)); + qproperty-coarseGridColor: rgba(0, 0, 0, 160); + qproperty-fineGridColor: rgba(0, 0, 0, 80); + qproperty-horizontalColor: rgba(0, 0, 0, 160); + qproperty-embossColor: rgba(140, 140, 140, 64); + + /* line widths */ + qproperty-coarseGridWidth: 2; + qproperty-fineGridWidth: 1; + qproperty-horizontalWidth: 1; + qproperty-embossWidth: 0; + + /* positive offset shifts emboss to the right */ + qproperty-embossOffset: 0; } @@ -649,11 +661,16 @@ lmms--gui--MixerChannelView QGraphicsView { border-style: none; } +lmms--gui--PeakIndicator { + background-color: #111811; + font-size: 7pt; +} + /* persistent peak markers for fx peak meters */ lmms--gui--Fader { - qproperty-peakGreen: rgb( 74, 253, 133); - qproperty-peakYellow: rgb(224, 222, 18); - qproperty-peakRed: rgb( 255, 100, 100); + qproperty-peakOk: rgb( 74, 253, 133); + qproperty-peakWarn: rgb(224, 222, 18); + qproperty-peakClip: rgb( 255, 100, 100); } lmms--gui--TimeLineWidget { @@ -996,6 +1013,7 @@ lmms--gui--CompressorControlDialog { qproperty-textColor: rgba(209, 216, 228, 50); qproperty-graphColor: rgba(209, 216, 228, 50); qproperty-resetColor: rgba(200, 100, 15, 200); + qproperty-backgroundColor: rgba(7, 8, 9, 255); } lmms--gui--CompressorControlDialog lmms--gui--Knob { diff --git a/data/themes/default/autoscroll_continuous_on.png b/data/themes/default/autoscroll_continuous_on.png new file mode 100644 index 00000000000..7f27a7f7dba Binary files /dev/null and b/data/themes/default/autoscroll_continuous_on.png differ diff --git a/data/themes/default/autoscroll_stepped_on.png b/data/themes/default/autoscroll_stepped_on.png new file mode 100644 index 00000000000..c58fa97b2cf Binary files /dev/null and b/data/themes/default/autoscroll_stepped_on.png differ diff --git a/data/themes/default/fader_background.png b/data/themes/default/fader_background.png deleted file mode 100644 index 7d6b59e35fe..00000000000 Binary files a/data/themes/default/fader_background.png and /dev/null differ diff --git a/data/themes/default/fader_leds.png b/data/themes/default/fader_leds.png deleted file mode 100644 index 707902e04c6..00000000000 Binary files a/data/themes/default/fader_leds.png and /dev/null differ diff --git a/data/themes/default/style.css b/data/themes/default/style.css index e1f0cf395b7..ef98c060999 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -234,7 +234,9 @@ lmms--gui--GroupBox { lmms--gui--Oscilloscope { background: none; border: none; - qproperty-normalColor: rgb(71, 253, 133); + qproperty-leftChannelColor: rgb(71, 253, 133); + qproperty-rightChannelColor: rgb(238, 253, 71); + qproperty-otherChannelsColor: rgb(71, 235, 253); qproperty-clippingColor: rgb(255, 64, 64); } @@ -276,33 +278,33 @@ QScrollBar::add-page:vertical:pressed, QScrollBar::sub-page:vertical:pressed { /* scrollbar: handles (sliders) */ QScrollBar::handle:horizontal { - background: #3f4750; + background: #5d6b77; border: none; border-radius: 4px; min-width: 24px; } QScrollBar::handle:horizontal:hover { - background: #525e69; + background: #b8c4d1; } QScrollBar::handle:horizontal:pressed { - background: rgba(11,213,86,100); + background: #ffffff; } QScrollBar::handle:vertical { - background: #3f4750; + background: #5d6b77; border: none; border-radius: 4px; min-height: 24px; } QScrollBar::handle:vertical:hover { - background: #525e69; + background: #b8c4d1; } QScrollBar::handle:vertical:pressed { - background: rgba(11,213,86,100); + background: #ffffff; } QScrollBar::handle:horizontal:disabled, QScrollBar::handle:vertical:disabled { @@ -386,12 +388,24 @@ lmms--gui--TrackView > QWidget { /* autoscroll, loop, stop behaviour toggle buttons */ -/* track background colors */ +/* track background config */ lmms--gui--TrackContentWidget { + /* colors */ qproperty-darkerColor: #0C0E0F; qproperty-lighterColor: #14151A; - qproperty-gridColor: #262B30; - qproperty-embossColor: rgba( 0, 0, 0, 0 ); + qproperty-coarseGridColor: #3C444C; + qproperty-fineGridColor: #262B30; + qproperty-horizontalColor: #3C444C; + qproperty-embossColor: rgba(0, 0, 0, 0); + + /* line widths */ + qproperty-coarseGridWidth: 2; + qproperty-fineGridWidth: 1; + qproperty-horizontalWidth: 1; + qproperty-embossWidth: 0; + + /* positive offset shifts emboss to the right */ + qproperty-embossOffset: 0; } @@ -537,6 +551,12 @@ QToolButton:hover { background: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:1, stop:0 #7c8799, stop:1 #343840) } +QToolButton:hover:checked { + border: 2px solid #343840; + border-radius: 2px; + background: qlineargradient(spread:reflect, x1:0, y1:0, x2:0, y2:1, stop:0 #292d33, stop:1 #22262c) +} + QToolButton:pressed { border-top: 1px solid #778394; border-bottom: 1px solid #1e2226; @@ -690,11 +710,16 @@ lmms--gui--MixerChannelView QGraphicsView { border-style: none; } +lmms--gui--PeakIndicator { + background-color: #111811; + font-size: 7pt; +} + /* persistent peak markers for fx peak meters */ lmms--gui--Fader { - qproperty-peakGreen: #0ad45c; - qproperty-peakYellow: #d6ec52; - qproperty-peakRed: #c12038; + qproperty-peakOk: #0ad45c; + qproperty-peakWarn: #d6ec52; + qproperty-peakClip: #c12038; } lmms--gui--TimeLineWidget { @@ -1038,6 +1063,7 @@ lmms--gui--CompressorControlDialog { qproperty-textColor: rgba(209, 216, 228, 50); qproperty-graphColor: rgba(209, 216, 228, 50); qproperty-resetColor: rgba(200, 100, 15, 200); + qproperty-backgroundColor: rgba(7, 8, 9, 255); } lmms--gui--CompressorControlDialog lmms--gui--Knob { diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index 86f03c427d0..00000000000 --- a/debian/changelog +++ /dev/null @@ -1,382 +0,0 @@ -lmms (@VERSION@) unstable; urgency=low - - * Upstream integration. - * Drop Debian menu entry (policy 9.6). - - -- Javier Serrano Polo Sat, 03 Nov 2018 02:43:42 +0100 - -lmms (1.1.3-8) unstable; urgency=low - - * Fix build (Closes: #897806). - * Allow recommendations (Closes: #891756). - - -- Javier Serrano Polo Sat, 13 Oct 2018 17:05:54 +0200 - -lmms (1.1.3-7.1) unstable; urgency=high - - * Non-maintainer upload. - - [ Javier Serrano Polo ] - * Fix build with Clang. - * Fix build with GCC 7 (Closes: #853527). - - [ Boyuan Yang ] - * Remove Patrick Winnertz from uploaders list. (Closes: #867759) - Thank you for your previous contributions! - - -- Boyuan Yang <073plan@gmail.com> Sat, 10 Mar 2018 11:32:05 +0800 - -lmms (1.1.3-7) unstable; urgency=low - - [ Javier Serrano Polo ] - * Separate flags for WINE_BUILD_FLAGS to fix build problem on i386. - - -- Petter Reinholdtsen Mon, 26 Dec 2016 07:36:54 +0000 - -lmms (1.1.3-6) unstable; urgency=low - - [ Javier Serrano Polo ] - * Build with install RUNPATH (reproducibility). - - -- Petter Reinholdtsen Sun, 25 Dec 2016 09:38:53 +0000 - -lmms (1.1.3-5) unstable; urgency=medium - - [ Javier Serrano Polo ] - * Add Wine stable path to PATH, otherwise wine package would be required. - * Sort plug-in embedded resources (reproducibility). - * Define NDEBUG instead of setting build type to honor optimization choice. - - -- Javier Serrano Polo Sat, 03 Sep 2016 17:31:05 +0200 - -lmms (1.1.3-4) unstable; urgency=medium - - [ Javier Serrano Polo ] - * Sort lists generated from globbing expressions (reproducibility). - * Fixed upgrade that installs recommended packages (Closes: #827039). - * Dropped unused build dependencies. - * Dropped workaround for #824715, wineg++ is fixed. - * Added contributors. - * Honor CONTRIBUTORS override. - * Replace __FILE__ (reproducibility). - * Use build type "Release" (reproducibility). - - -- Javier Serrano Polo Wed, 08 Jun 2016 01:25:37 +0200 - -lmms (1.1.3-3) unstable; urgency=medium - - [ Javier Serrano Polo ] - * Dropped Wine path from PATH. - * Fixed lmms-common upgrade (Closes: #825287). - * Hide vocoder plug-in, it is now in swh-plugins (Closes: #826110). - * Added workaround for #824715 (Closes: #825286). - - -- Petter Reinholdtsen Fri, 03 Jun 2016 08:51:31 +0000 - -lmms (1.1.3-2) unstable; urgency=medium - - [ Javier Serrano Polo ] - * Enabled vst plugin on i386 and amd64 (Closes: #763720). - * Made Calf and vocoder plugins available to other programs (Closes: #758888). - * Fixed version of metalish_dong01.ogg (Closes: #802588). - * Bumped Standards-Version to 3.9.7. Doxygen documentation is not very useful. - * Upgraded to Standards-Version 3.9.8. Ship icons in the default hicolor icon - theme directories. - * Use presets from zynaddsubfx-data. - * Fixed build problems with GCC 6 (Closes: #811697). - * Switch to PulseAudio back end on likely ALSA interception (Closes: #781479). - * Updated copyright information. - * Removed non-free projects. - - -- Petter Reinholdtsen Wed, 18 May 2016 09:09:23 +0000 - -lmms (1.1.3-1) unstable; urgency=low - - * New upstream version 1.1.3 (Closes: #788457). - - Drop well-defined-loop.patch, included upstream. - * Add config for git-buildpackage to use pristine-tar all the time. - * Change homepage url to new http://lmms.io/. - * Added fluid as build-depend. - * New patch find-fluid.patch to find the fluid binary in unstable. - * New patch gcc5.patch to get the code building with gcc 5 in - unstable (Closes: #777989). - * Added man-page-adjustment.patch to fix manpage formatting of AUTHOR - block. Discovered thanks to lintian. - - -- Petter Reinholdtsen Mon, 21 Sep 2015 13:54:02 +0200 - -lmms (1.0.3-5) unstable; urgency=low - - * Make lmms replace and break lmms-common (<< 1.0.0-1) to handle the - fact that /usr/share/menu/lmms moved from lmms-common to lmms in - this version (Closes: #765970). - - -- Petter Reinholdtsen Sun, 19 Oct 2014 23:43:08 +0200 - -lmms (1.0.3-4) unstable; urgency=low - - * Correct watch file to reflect '-src' part of upstream tarball. - * New desktop-argument.patch to let desktops know how to pass files to - lmms. Thanks to lintian for noticing the bug. - * Revert change to enable the vsl plugin in version 1.0.3-3, as it did - not work. Reopen bug #763720. - - -- Petter Reinholdtsen Wed, 08 Oct 2014 19:21:53 +0200 - -lmms (1.0.3-3) unstable; urgency=medium - - * Try to get vsl plugin working on i386 by build depending on libwine-dev - and wine32-dev-tools (Closes: #763720). Unable to get it working on - amd64. - - -- Petter Reinholdtsen Tue, 07 Oct 2014 12:13:16 +0200 - -lmms (1.0.3-2) unstable; urgency=medium - - * Change build rule to only enable ALSA support on Linux (Closes: #754718). - Patch from Steven Chamberlain with input from Guillem Jover. - * Drop libwine-dev build dependency to avoid build failure in the - vst plugin. It should be enabled when we figure out how to do it. - - -- Petter Reinholdtsen Thu, 02 Oct 2014 08:19:53 +0200 - -lmms (1.0.3-1) unstable; urgency=low - - * Move package into Debian Edu git repository and add myself as - co-maintainer. - * Update to upstream version 1.0.3. - * Correct build dependency on i386, use libwine-dev instead of the now - obsolete wine-dev (Closes: #748183). - * Update Standards-Version from 3.9.5 to 3.9.6. - * Add new well-defined-loop.patch to fix compile error triggered by - undefined loop behaviour (Closes: #753177). - - -- Petter Reinholdtsen Wed, 01 Oct 2014 23:00:19 +0200 - -lmms (1.0.0-1) unstable; urgency=low - - * New upstream version (Closes: #703900, #735764, #696271) - * Using a wraped-style control (Closes: #689347) - * Don't suggest vcf as it no longer exists (since a long time) - (Closes: #618350) - * Problems with jack backend were fixed long ago (Closes: #557421) - * No depends/recommends on wine anymore (Closes: #622215, #622080) - - -- Patrick Winnertz Thu, 24 Apr 2014 22:30:17 +0200 - -lmms (0.4.10-2.3) unstable; urgency=low - - * Non maintainer upload. - * Remove wine-related dependencies on amd64, thereby disabling building - VST plugin. (Closes: #676760) - - -- Hilko Bengen Wed, 27 Jun 2012 23:14:40 +0200 - -lmms (0.4.10-2.2) unstable; urgency=low - - * Non maintainer upload. - * Fix build failure with GCC 4.7. Closes: #667265. - * Turn on verbose build. - - -- Matthias Klose Tue, 17 Apr 2012 14:08:53 +0200 - -lmms (0.4.10-2.1) unstable; urgency=low - - * Non-maintainer upload. - * Fix FTBFS on GNU/kFreeBSD. Thanks Pino Toscano. (Closes: #641064) - - -- Robert Millan Fri, 27 Apr 2012 22:55:55 +0200 - -lmms (0.4.10-2) unstable; urgency=low - - * Removed build-dep on libestools2.0-dev (Closes: #614975) - - -- Patrick Winnertz Fri, 11 Mar 2011 09:37:43 +0100 - -lmms (0.4.10-1) unstable; urgency=low - - * Imported Upstream version 0.4.10 - * changed mode of patch - * Add source/format with 3.0 (quilt) and rearrange source a bit - * Bump standarts version to 3.9.1 - no further changes needed - - -- Patrick Winnertz Fri, 11 Feb 2011 20:03:06 +0100 - -lmms (0.4.7-2) unstable; urgency=low - - [ Reinhard Tartler ] - * Depend on wine only on i386 and amd64 (Closes: #590950) - - [ Patrick Winnertz ] - * Uploading patch from siretart, thanks for helping. - - -- Patrick Winnertz Mon, 02 Aug 2010 10:13:28 +0200 - -lmms (0.4.7-1) unstable; urgency=low - - * New upstream version - - -- Patrick Winnertz Thu, 29 Jul 2010 16:24:00 +0200 - -lmms (0.4.6-2) unstable; urgency=low - - * Build-depend on libestools2.0-dev (Closes: #589882) - - -- Patrick Winnertz Wed, 28 Jul 2010 11:55:16 +0200 - -lmms (0.4.6-1) unstable; urgency=low - - * New upstream version (Closes: #565733) - * Bumped standards version to 3.8.3, no further changes needed - - -- Patrick Winnertz Sun, 24 Jan 2010 16:40:39 +0100 - -lmms (0.4.5-1) unstable; urgency=low - - * New upstream version (Closes: #543645) - * Fix FTBFS on amd64 (Closes: #540671 #543017) - - -- Patrick Winnertz Fri, 11 Sep 2009 09:56:45 +0200 - -lmms (0.4.4-1) unstable; urgency=low - - * New upstream version (Closes: #511363) - * Provide menu file (Closes: #514905) - - The menufile is included in lmms-common and not in lmms - as it is not arch specific - * Bumped standards version to 3.8.2 no further changes needed - - -- Patrick Winnertz Wed, 05 Aug 2009 20:46:40 +0200 - -lmms (0.3.2-1) unstable; urgency=low - - * Decrease wine dependency to wine-bin as suggested. (Closes: #446163) - * Acknowlegded NMU from Pierre Habouzit in order to - fix FTBFS with gcc-4.3 which (Closes: #462202) - * Now including the singerbot plugin. (Closes: #443224) - * Add patch to prevent lmms from crashing. - - -- Patrick Winnertz Mon, 17 Mar 2008 10:56:12 +0100 - -lmms (0.3.1-1.1) unstable; urgency=low - - * Non-maintainer upload. - * Add g++-4.3.patch to fix g++-4.3 FTBFS (Closes: 462202). - - -- Pierre Habouzit Sun, 16 Mar 2008 23:21:56 +0000 - -lmms (0.3.1-1) unstable; urgency=low - - * Packaging new upstream release. - * Install Upstream Changelog (Closes: #441477) - Thanks to Felipe Sateler - * Lowered the caps dependency to Recommends (Closes: #446163) - * Added -i/-a to the build targets in rules in order to make sure that only - (in)dep packages are build. - * Changed my maintainer address - * Added patch to remove stereo_enhancer plugin which has a ftbfs with gcc - 4.2.3 - * Added imagemagick as build-dep since we have to convert a .png to a .xpm - via convert - * Doesn't install upstreams menu file, since it's outdated.. instead use - our own. - * Standard-Version bump to 3.7.3 - * Remove Homepage field from Description and create a own Header - * Added postinst and postrm for lmms-common to call update-menu if available - (this has to be done manually, since we doesn't use dh_installmenu to install - the menu file) - - -- Patrick Winnertz Thu, 06 Dec 2007 07:08:04 +0100 - -lmms (0.3.0-1) unstable; urgency=low - - [ Tobias Doerffel ] - * New upstream release. (Closes: #439301) - * Removed patch from Thomas Girard as upstream merged changes - - [ Patrick Winnertz ] - * Moved manpage into correct package (lmms) - * Removed manuall installation of Upstream Changelog, thanks to the power of - debhelper ;-) - * FTBFS with gcc 4.2 is fixed by upstream (Closes: #383295) - * lmms has now a proper menu entry (Closes: #383406) - * lmms depends now on the same version of lmms-common (Closes: #389037) - * fixed by upstream: arpeggio status is now saved (Closes: #433262) - * Added build-depends libqt3-i18n (Closes: #384406) - * Added watch file for lmms (Closes: #439302) - Thanks to Raphael Geissert - * Improved copyright file - * Doesn't build libsingerbot since it is experimental and doesn't work - correct yet - * Added several build-dependencys (libstk0-dev, libestools1.2-dev, ladspa-sdk, - libflac-dev, libwine-dev [i386], libqt3-i18n, libasound2-dev, - festival-dev, dpatch ) - * Take over this package from Florian Ragwitz, so set myself as maintainer - * Add a depends on a specific version of lmms-common - * Added several new dependencys to lmms - * Improved description of lmms and lmms-common - * Die on errors of clean, but don't die if the makefile doesn't exist - * Added watch file - - -- Patrick Winnertz Fri, 24 Aug 2007 08:23:34 +0200 - -lmms (0.2.1-1.1) unstable; urgency=high - - * Non-maintainer Upload - * Add Patch by Thomas Girard for segfault right after - lmms starting up (Closes: 382491) - - -- Michael Ablassmeier Tue, 5 Sep 2006 12:00:40 +0200 - -lmms (0.2.1-1) unstable; urgency=low - - * New upstream release. - * Bump up Standards-Version to 3.7.2 (no changes). - * Use DH_COMPAT 5. - - -- Florian Ragwitz Sun, 13 Aug 2006 14:40:13 +0200 - -lmms (0.1.4-1) unstable; urgency=low - - * New upstream release. - - -- Florian Ragwitz Sat, 4 Feb 2006 07:16:47 +0100 - -lmms (0.1.3-1) unstable; urgency=low - - * New upstream release. - * debian/lmms.1 was included by upstream. Removed it from debian/. - * Install lmms.1 in lmms-common instead of lmms to safe some mirror space. - * Added a lintian override for the above, as lintian is not smart enough to - check for manpages in other packages from the same source package on which - a package with a missing manpage depends. - - -- Florian Ragwitz Wed, 1 Feb 2006 18:28:42 +0100 - -lmms (0.1.2-1) unstable; urgency=low - - * New upstream release. - - -- Florian Ragwitz Thu, 22 Dec 2005 16:22:50 +0100 - -lmms (0.1.1-2) unstable; urgency=low - - * lmms-common doesn't depend on lmms anymore to remove a circular - dependencies (Closes: #339906). - - -- Florian Ragwitz Sun, 20 Nov 2005 12:27:08 +0100 - -lmms (0.1.1-1) unstable; urgency=low - - * New upstream release. - * Changed Maintainer address. - * Added libjack-dev to Build-Depends. - - -- Florian Ragwitz Mon, 31 Oct 2005 10:48:36 +0100 - -lmms (0.0.9+0.1.0rc1-1) unstable; urgency=low - - * Initial Release (Closes: #315976). - - -- Florian Ragwitz Fri, 22 Jul 2005 16:33:17 +0200 - diff --git a/debian/compat b/debian/compat deleted file mode 100644 index ec635144f60..00000000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -9 diff --git a/debian/control b/debian/control deleted file mode 100644 index 63eba44d2e1..00000000000 --- a/debian/control +++ /dev/null @@ -1,126 +0,0 @@ -Source: lmms -Section: sound -Priority: optional -Maintainer: Debian Edu Packaging Team -Uploaders: - Petter Reinholdtsen , - Israel Dahl , - Javier Serrano Polo , -Build-Depends: - cmake, - debhelper (>= 9.0.0), - fluid, - ladspa-sdk, - libasound2-dev [linux-any], - libfftw3-dev, - libfltk1.3-dev, - libfluidsynth-dev, - libgig-dev, - libjack-jackd2-dev, - liblist-moreutils-perl, - libmp3lame-dev, - libpulse-dev, - libqt5x11extras5-dev, - libsamplerate0-dev, - libsdl1.2-dev, - libsndfile1-dev, - libsndio-dev, - libsoundio-dev, - libstk0-dev, - libvorbis-dev, - libx11-xcb-dev, - libxcb-keysyms1-dev, - libxcb-util0-dev, - libxml-perl, - libxml2-utils, - portaudio19-dev, - qtbase5-private-dev, - qttools5-dev, - wine64-tools [amd64] | wine32-tools [i386] -Standards-Version: 4.2.1.4 -Homepage: https://lmms.io/ -Vcs-Browser: https://salsa.debian.org/debian-edu-pkg-team/lmms.git - -Package: lmms-bin -Architecture: any -Depends: - lmms-common (>= ${source:Version}), - ${shlibs:Depends}, - ${misc:Depends}, - stk, -Recommends: - caps, - lmms-vst-server:i386 (>= ${source:Version}), - lmms-vst-server:amd64 (>= ${source:Version}), - tap-plugins, -Suggests: - fil-plugins, - fluid-soundfont-gm, - freepats, - ladspa-plugin, - mcp-plugins, - omins, -Replaces: lmms-common (<< 1.0.0-1) -Breaks: lmms-common (<< 1.0.0-1) -Multi-Arch: allowed -Description: Linux Multimedia Studio - minimal installation - LMMS aims to be a free alternative to popular (but commercial and closed- - source) programs like FruityLoops, Cubase and Logic giving you the ability of - producing music with your computer by creating cool loops, synthesizing and - mixing sounds, arranging samples, having more fun with your MIDI-keyboard - and much more... - . - LMMS combines the features of a tracker-/sequencer-program (pattern-/channel-/ - sample-/song-/effect-management) and those of powerful synthesizers and - samplers in a modern, user-friendly and easy to use graphical user-interface. - . - This package provides the minimal installation. - -Package: lmms -Architecture: any -Depends: - lmms-bin, - ${misc:Depends}, -Description: Linux Multimedia Studio - LMMS aims to be a free alternative to popular (but commercial and closed- - source) programs like FruityLoops, Cubase and Logic giving you the ability of - producing music with your computer by creating cool loops, synthesizing and - mixing sounds, arranging samples, having more fun with your MIDI-keyboard - and much more... - . - LMMS combines the features of a tracker-/sequencer-program (pattern-/channel-/ - sample-/song-/effect-management) and those of powerful synthesizers and - samplers in a modern, user-friendly and easy to use graphical user-interface. - . - This package provides the recommended installation. - -Package: lmms-common -Architecture: all -Depends: - ${shlibs:Depends}, - ${misc:Depends}, - zynaddsubfx-data, -Pre-Depends: ${misc:Pre-Depends} -Description: Linux Multimedia Studio - common files - LMMS aims to be a free alternative to popular (but commercial and closed- - source) programs like FruityLoops, Cubase and Logic giving you the ability of - producing music with your computer by creating cool loops, synthesizing and - mixing sounds, arranging samples, having more fun with your MIDI-keyboard - and much more... - . - LMMS combines the features of a tracker-/sequencer-program (pattern-/channel-/ - sample-/song-/effect-management) and those of powerful synthesizers and - samplers in a modern, user-friendly and easy to use graphical user-interface. - . - This package contains the platform independent files such as samples, presets - and some example projects. - -Package: lmms-vst-server -Architecture: amd64 i386 -Depends: - wine64 [amd64] | wine64-development [amd64] | wine32 [i386] | wine32-development [i386], - ${shlibs:Depends}, - ${misc:Depends}, -Recommends: lmms-bin:any -Description: Linux Multimedia Studio - VST server - This package contains a helper application that loads VST plugins. diff --git a/debian/copyright b/debian/copyright deleted file mode 100644 index f4bc9d85c9d..00000000000 --- a/debian/copyright +++ /dev/null @@ -1,1373 +0,0 @@ -Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: LMMS -Upstream-Contact: https://github.com/LMMS/lmms -Source: https://github.com/LMMS/lmms/tags -Comment: - This package was debianized by Florian Ragwitz on - Thu, 14 Apr 2005 13:24:57 +0200. - -Files: * -Copyright: - 1998-2000 Paul Kellett (mda-vst.com) - 1999-2004 Dag Lem - 2002 Kjetil S. Matheussen - 2003-2007 Rui Nuno Capela - 2003-2005 Shay Green - 2004-2014 Tobias Doerffel - 2004 Paul Davis - 2004 Torben Hohn - 2005-2008 Danny McRae - 2006-2008 Andreas Brandmaier - 2006-2008 Javier Serrano Polo - 2007-2014 Vesa Kivimäki - 2007-2013 Paul Giblock - 2007 Keith Marshall - 2008-2009 Andrew Kelley - 2008 Attila Herman - 2008 Csaba Hruska - 2013-2014 Raine M. Ekman - 2013 Mike Choi - 2014 David French - 2014 Hannu Haahti - 2014 Lukas Wohlschläger - 2014 Rubén Ibarra Pastor - 2014 Simon Symeonidis - 2014 Wong Cho Ching - Chrissy McManus - Gabriel - Gurjot Singh - Johannes Lorenz - Jonathan Aquilina - Jorrit Rouwe - Juan Fabián Simón - LocoMatt - Oskar Wallgren - Peter Hanappe - Sebastian Tilsch - Tobiasz Karoń (unfa) - Uroš Maravić -License: GPL-2+ - -Files: data/projects/demos/Alf42red-* - data/projects/demos/CapDan/CapDan-TwilightArea-* -Copyright: - 2010-2011 Armin Heller - 2011 Der Daniel (CapDan) -License: CC-BY-SA-3 - -Files: data/projects/demos/AngryLlama-* - data/projects/demos/Ashore.* - data/projects/demos/DnB.* - data/projects/demos/Farbro-* - data/projects/demos/Greippi* - data/projects/demos/Namitryus-* - data/projects/demos/Popsip-* - data/projects/demos/Root84-* - data/projects/demos/Shovon-* - data/projects/demos/Skiessi/* - data/projects/demos/StrictProduction-* - data/projects/demos/Thaledric-* - data/projects/demos/TobyDox-* - data/projects/demos/unfa-* - data/projects/shorties/DirtyLove.* - data/projects/shorties/Root84-* - data/projects/shorties/Skiessi-* - data/projects/shorties/sv-* -Copyright: - LMMS contributors -License: non-free - -Files: data/projects/demos/CapDan/CapDan-ReggaeTry.* - data/projects/demos/CapDan/CapDan-ReggaetonTry.* - data/projects/demos/CapDan/CapDan-ZeroSumGame-* - data/projects/demos/EsoXLB-* - data/projects/demos/Impulslogik-* - data/projects/demos/Momo64-* - data/projects/demos/Oglsdl-* - data/projects/demos/Settel-* - data/projects/demos/Socceroos-* - data/projects/demos/TameAnderson-* - data/projects/demos/Thomasso-* - data/projects/shorties/Crunk* - data/projects/shorties/Greshz-* - data/projects/shorties/Surrender-* -Copyright: - 2009 Achim Settelmeier - 2009 Peter Asplund (Surrender) - 2009 Thomasso - 2010 E.SoX (lowbudget) - 2010 Impulslogik - 2011 Der Daniel (CapDan) - 2011 Sam (socceroos) - 2011 mauro (momo64) - 2011 tame anderson - 2011 Ümit (oglsdl) - Greshz -License: Artistic-2 - -Files: data/projects/demos/Jousboxx-* -Copyright: - Jousboxx -License: CC-BY-SA-4 - -Files: data/projects/demos/Saber-* -Copyright: - Saber Rastikerdar -License: BSD-2-clause - -Files: include/ladspa.h -Copyright: - 2000-2002 Paul Barton-Davis - 2000-2002 Richard W.E. Furse - 2000-2002 Stefan Westerfeld -License: LGPL-2.1+ - -Files: plugins/LadspaEffect/calf/veal/* -Copyright: - 2001-2010 Krzysztof Foltman - 2001-2010 Markus Schmidt - 2001-2010 Thor Harald Johansen - Alexandre Prokoudine - Carl Hetherington - Christian Holschuh - Damien Zammit - Dave Robillard - David Täht - Hans Baier - Hermann Meyer - Thorsten Wilms - Tom Szilagyi - Torben Hohn -License: LGPL-2+ -Comment: - COPYING is the GNU Lesser General Public License. Headers refer to version 2 of - this license instead of version 2.1. - -Files: plugins/LadspaEffect/calf/veal/src/calf/vumeter.h -Copyright: - 2007 Krzysztof Foltman -License: GPL-2+ - -Files: plugins/LadspaEffect/caps/* -Copyright: - 1998 Robert Bristow-Johnson - 2001-2011 Tim Goetze - 2003-2009 David Yeh - 2004-2005 Steve Harris -License: GPL-2+ - -Files: plugins/LadspaEffect/cmt/* -Copyright: - 1998 Andy Sloane - 1999-2001 David A. Bartold - 2000-2002 Richard W.E. Furse - 2000 Jezar - 2002 Nathaniel Virgo -License: GPL-2+ - -Files: plugins/LadspaEffect/swh/ladspa/* -Copyright: - 1999 Juhana Sadeharju - 2000-2003 Alexander Ehlert - 2000-2002 Steve Harris - Andy Wingo - Frank Neumann - Jesse Chappell - Joern Nettingsmeier - Marcus Andersson - Mark Knecht - Matthias Nagorni - Nathaniel Virgo - Pascal Haakmat - Patrick Shirkey - Paul Winkler -License: GPL-2+ - -Files: plugins/LadspaEffect/swh/ladspa/gsm/* -Copyright: - 1992-1994 Carsten Bormann - 1992-1994 Jutta Degener -License: Bormann-Degener - -Files: plugins/LadspaEffect/swh/ladspa/util/pitchscale.c -Copyright: - 1999 Stephan M. Sprenger -License: WOL - -Files: plugins/LadspaEffect/swh/ladspa/vocoder_1337.c -Copyright: - Achim Settelmeier - Hexasoft - Josh Green -License: GPL-2+ - -Files: plugins/LadspaEffect/tap/* -Copyright: - 2004 Tom Szilagyi - Alexander Koenig -License: GPL-2+ - -Files: plugins/MidiImport/portsmf/* -Copyright: - 1999-2000 Phil Burk - 1999-2000 Ross Bencina - 2001-2006 Roger B. Dannenberg -License: Expat -Comment: - The Expat license constitutes the entire Portsmf license; however, - the PortMusic community also makes the following non-binding requests: - . - Any person wishing to distribute modifications to the Software is - requested to send the modifications to the original developer so that - they can be incorporated into the canonical version. It is also - requested that these non-binding requests be included along with the - license above. - -Files: plugins/OpulenZ/adplug/src/fmopl.* - plugins/OpulenZ/adplug/src/mididata.h - plugins/OpulenZ/adplug/src/opl.h - plugins/OpulenZ/adplug/src/temuopl.* -Copyright: - 1999-2007 Simon Peter - 1999-2000 Tatsuyuki Satoh -License: LGPL-2.1+ - -Files: plugins/Sfxr/readme.* - plugins/Sfxr/Sfxr.* -Copyright: - 2007 Tomas Pettersson - 2014 Wong Cho Ching -License: Expat and GPL-2+ - -Files: plugins/ZynAddSubFx/zynaddsubfx/* -Copyright: - 2002-2009 Nasca Octavian Paul - 2009-2010 Mark McCurry - 2009 Alan Calvert - 2012 Jonathan Liles - Achim Settelmeier - Alexis Ballier - Andre Sklenar - Christopher Oliver - Damien Goutte-Gattat - Daniel Clemente - Emmanuel Saracco - Filipe Coelho - Gerald Folcher - Hans Petter Selasky - Harald Hvaal - Ilario Glasgo - James Morris - Johannes Lorenz - Jérémie Andréi - Lars Luthman - Lieven Moors - Olaf Schulz - Ryan Billing - Stephen Parry - Tobias Doerffel -License: GPL-2+ - -License: Artistic-2 - The Artistic License 2.0 - . - Copyright (c) 2000-2006, The Perl Foundation. - . - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - . - Preamble - . - This license establishes the terms under which a given free software - Package may be copied, modified, distributed, and/or redistributed. - The intent is that the Copyright Holder maintains some artistic - control over the development of that Package while still keeping the - Package available as open source and free software. - . - You are always permitted to make arrangements wholly outside of this - license directly with the Copyright Holder of a given Package. If the - terms of this license do not permit the full use that you propose to - make of the Package, you should contact the Copyright Holder and seek - a different licensing arrangement. - . - Definitions - . - "Copyright Holder" means the individual(s) or organization(s) - named in the copyright notice for the entire Package. - . - "Contributor" means any party that has contributed code or other - material to the Package, in accordance with the Copyright Holder's - procedures. - . - "You" and "your" means any person who would like to copy, - distribute, or modify the Package. - . - "Package" means the collection of files distributed by the - Copyright Holder, and derivatives of that collection and/or of - those files. A given Package may consist of either the Standard - Version, or a Modified Version. - . - "Distribute" means providing a copy of the Package or making it - accessible to anyone else, or in the case of a company or - organization, to others outside of your company or organization. - . - "Distributor Fee" means any fee that you charge for Distributing - this Package or providing support for this Package to another - party. It does not mean licensing fees. - . - "Standard Version" refers to the Package if it has not been - modified, or has been modified only in ways explicitly requested - by the Copyright Holder. - . - "Modified Version" means the Package, if it has been changed, and - such changes were not explicitly requested by the Copyright - Holder. - . - "Original License" means this Artistic License as Distributed with - the Standard Version of the Package, in its current version or as - it may be modified by The Perl Foundation in the future. - . - "Source" form means the source code, documentation source, and - configuration files for the Package. - . - "Compiled" form means the compiled bytecode, object code, binary, - or any other form resulting from mechanical transformation or - translation of the Source form. - . - Permission for Use and Modification Without Distribution - . - (1) You are permitted to use the Standard Version and create and use - Modified Versions for any purpose without restriction, provided that - you do not Distribute the Modified Version. - . - Permissions for Redistribution of the Standard Version - . - (2) You may Distribute verbatim copies of the Source form of the - Standard Version of this Package in any medium without restriction, - either gratis or for a Distributor Fee, provided that you duplicate - all of the original copyright notices and associated disclaimers. At - your discretion, such verbatim copies may or may not include a - Compiled form of the Package. - . - (3) You may apply any bug fixes, portability changes, and other - modifications made available from the Copyright Holder. The resulting - Package will still be considered the Standard Version, and as such - will be subject to the Original License. - . - Distribution of Modified Versions of the Package as Source - . - (4) You may Distribute your Modified Version as Source (either gratis - or for a Distributor Fee, and with or without a Compiled form of the - Modified Version) provided that you clearly document how it differs - from the Standard Version, including, but not limited to, documenting - any non-standard features, executables, or modules, and provided that - you do at least ONE of the following: - . - (a) make the Modified Version available to the Copyright Holder - of the Standard Version, under the Original License, so that the - Copyright Holder may include your modifications in the Standard - Version. - . - (b) ensure that installation of your Modified Version does not - prevent the user installing or running the Standard Version. In - addition, the Modified Version must bear a name that is different - from the name of the Standard Version. - . - (c) allow anyone who receives a copy of the Modified Version to - make the Source form of the Modified Version available to others - under - . - (i) the Original License or - . - (ii) a license that permits the licensee to freely copy, - modify and redistribute the Modified Version using the same - licensing terms that apply to the copy that the licensee - received, and requires that the Source form of the Modified - Version, and of any works derived from it, be made freely - available in that license fees are prohibited but Distributor - Fees are allowed. - . - Distribution of Compiled Forms of the Standard Version - or Modified Versions without the Source - . - (5) You may Distribute Compiled forms of the Standard Version without - the Source, provided that you include complete instructions on how to - get the Source of the Standard Version. Such instructions must be - valid at the time of your distribution. If these instructions, at any - time while you are carrying out such distribution, become invalid, you - must provide new instructions on demand or cease further distribution. - If you provide valid instructions or cease distribution within thirty - days after you become aware that the instructions are invalid, then - you do not forfeit any of your rights under this license. - . - (6) You may Distribute a Modified Version in Compiled form without - the Source, provided that you comply with Section 4 with respect to - the Source of the Modified Version. - . - Aggregating or Linking the Package - . - (7) You may aggregate the Package (either the Standard Version or - Modified Version) with other packages and Distribute the resulting - aggregation provided that you do not charge a licensing fee for the - Package. Distributor Fees are permitted, and licensing fees for other - components in the aggregation are permitted. The terms of this license - apply to the use and Distribution of the Standard or Modified Versions - as included in the aggregation. - . - (8) You are permitted to link Modified and Standard Versions with - other works, to embed the Package in a larger work of your own, or to - build stand-alone binary or bytecode versions of applications that - include the Package, and Distribute the result without restriction, - provided the result does not expose a direct interface to the Package. - . - Items That are Not Considered Part of a Modified Version - . - (9) Works (including, but not limited to, modules and scripts) that - merely extend or make use of the Package, do not, by themselves, cause - the Package to be a Modified Version. In addition, such works are not - considered parts of the Package itself, and are not subject to the - terms of this license. - . - General Provisions - . - (10) Any use, modification, and distribution of the Standard or - Modified Versions is governed by this Artistic License. By using, - modifying or distributing the Package, you accept this license. Do not - use, modify, or distribute the Package, if you do not accept this - license. - . - (11) If your Modified Version has been derived from a Modified - Version made by someone other than you, you are nevertheless required - to ensure that your Modified Version complies with the requirements of - this license. - . - (12) This license does not grant you the right to use any trademark, - service mark, tradename, or logo of the Copyright Holder. - . - (13) This license includes the non-exclusive, worldwide, - free-of-charge patent license to make, have made, use, offer to sell, - sell, import and otherwise transfer the Package with respect to any - patent claims licensable by the Copyright Holder that are necessarily - infringed by the Package. If you institute patent litigation - (including a cross-claim or counterclaim) against any party alleging - that the Package constitutes direct or contributory patent - infringement, then this Artistic License to you shall terminate on the - date that such litigation is filed. - . - (14) Disclaimer of Warranty: - THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS - IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES. THE IMPLIED - WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR - NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY YOUR LOCAL - LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR CONTRIBUTOR WILL - BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL - DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE, EVEN IF - ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -License: BSD-2-clause - All rights reserved. - . - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - . - 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - . - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - . - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -License: Bormann-Degener - Any use of this software is permitted provided that this notice is not - removed and that neither the authors nor the Technische Universitaet Berlin - are deemed to have made any representations as to the suitability of this - software for any purpose nor are held responsible for any defects of - this software. THERE IS ABSOLUTELY NO WARRANTY FOR THIS SOFTWARE. - . - As a matter of courtesy, the authors request to be informed about uses - this software has found, about bugs in this software, and about any - improvements that may be of general interest. - -License: CC-BY-SA-3 - Creative Commons Legal Code - . - Attribution-ShareAlike 3.0 Unported - . - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR - DAMAGES RESULTING FROM ITS USE. - . - License - . - THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE - COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY - COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS - AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. - . - BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE - TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY - BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS - CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND - CONDITIONS. - . - 1. Definitions - . - a. "Adaptation" means a work based upon the Work, or upon the Work and - other pre-existing works, such as a translation, adaptation, - derivative work, arrangement of music or other alterations of a - literary or artistic work, or phonogram or performance and includes - cinematographic adaptations or any other form in which the Work may be - recast, transformed, or adapted including in any form recognizably - derived from the original, except that a work that constitutes a - Collection will not be considered an Adaptation for the purpose of - this License. For the avoidance of doubt, where the Work is a musical - work, performance or phonogram, the synchronization of the Work in - timed-relation with a moving image ("synching") will be considered an - Adaptation for the purpose of this License. - b. "Collection" means a collection of literary or artistic works, such as - encyclopedias and anthologies, or performances, phonograms or - broadcasts, or other works or subject matter other than works listed - in Section 1(f) below, which, by reason of the selection and - arrangement of their contents, constitute intellectual creations, in - which the Work is included in its entirety in unmodified form along - with one or more other contributions, each constituting separate and - independent works in themselves, which together are assembled into a - collective whole. A work that constitutes a Collection will not be - considered an Adaptation (as defined below) for the purposes of this - License. - c. "Creative Commons Compatible License" means a license that is listed - at https://creativecommons.org/compatiblelicenses that has been - approved by Creative Commons as being essentially equivalent to this - License, including, at a minimum, because that license: (i) contains - terms that have the same purpose, meaning and effect as the License - Elements of this License; and, (ii) explicitly permits the relicensing - of adaptations of works made available under that license under this - License or a Creative Commons jurisdiction license with the same - License Elements as this License. - d. "Distribute" means to make available to the public the original and - copies of the Work or Adaptation, as appropriate, through sale or - other transfer of ownership. - e. "License Elements" means the following high-level license attributes - as selected by Licensor and indicated in the title of this License: - Attribution, ShareAlike. - f. "Licensor" means the individual, individuals, entity or entities that - offer(s) the Work under the terms of this License. - g. "Original Author" means, in the case of a literary or artistic work, - the individual, individuals, entity or entities who created the Work - or if no individual or entity can be identified, the publisher; and in - addition (i) in the case of a performance the actors, singers, - musicians, dancers, and other persons who act, sing, deliver, declaim, - play in, interpret or otherwise perform literary or artistic works or - expressions of folklore; (ii) in the case of a phonogram the producer - being the person or legal entity who first fixes the sounds of a - performance or other sounds; and, (iii) in the case of broadcasts, the - organization that transmits the broadcast. - h. "Work" means the literary and/or artistic work offered under the terms - of this License including without limitation any production in the - literary, scientific and artistic domain, whatever may be the mode or - form of its expression including digital form, such as a book, - pamphlet and other writing; a lecture, address, sermon or other work - of the same nature; a dramatic or dramatico-musical work; a - choreographic work or entertainment in dumb show; a musical - composition with or without words; a cinematographic work to which are - assimilated works expressed by a process analogous to cinematography; - a work of drawing, painting, architecture, sculpture, engraving or - lithography; a photographic work to which are assimilated works - expressed by a process analogous to photography; a work of applied - art; an illustration, map, plan, sketch or three-dimensional work - relative to geography, topography, architecture or science; a - performance; a broadcast; a phonogram; a compilation of data to the - extent it is protected as a copyrightable work; or a work performed by - a variety or circus performer to the extent it is not otherwise - considered a literary or artistic work. - i. "You" means an individual or entity exercising rights under this - License who has not previously violated the terms of this License with - respect to the Work, or who has received express permission from the - Licensor to exercise rights under this License despite a previous - violation. - j. "Publicly Perform" means to perform public recitations of the Work and - to communicate to the public those public recitations, by any means or - process, including by wire or wireless means or public digital - performances; to make available to the public Works in such a way that - members of the public may access these Works from a place and at a - place individually chosen by them; to perform the Work to the public - by any means or process and the communication to the public of the - performances of the Work, including by public digital performance; to - broadcast and rebroadcast the Work by any means including signs, - sounds or images. - k. "Reproduce" means to make copies of the Work by any means including - without limitation by sound or visual recordings and the right of - fixation and reproducing fixations of the Work, including storage of a - protected performance or phonogram in digital form or other electronic - medium. - . - 2. Fair Dealing Rights. Nothing in this License is intended to reduce, - limit, or restrict any uses free from copyright or rights arising from - limitations or exceptions that are provided for in connection with the - copyright protection under copyright law or other applicable laws. - . - 3. License Grant. Subject to the terms and conditions of this License, - Licensor hereby grants You a worldwide, royalty-free, non-exclusive, - perpetual (for the duration of the applicable copyright) license to - exercise the rights in the Work as stated below: - . - a. to Reproduce the Work, to incorporate the Work into one or more - Collections, and to Reproduce the Work as incorporated in the - Collections; - b. to create and Reproduce Adaptations provided that any such Adaptation, - including any translation in any medium, takes reasonable steps to - clearly label, demarcate or otherwise identify that changes were made - to the original Work. For example, a translation could be marked "The - original work was translated from English to Spanish," or a - modification could indicate "The original work has been modified."; - c. to Distribute and Publicly Perform the Work including as incorporated - in Collections; and, - d. to Distribute and Publicly Perform Adaptations. - e. For the avoidance of doubt: - . - i. Non-waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme cannot be waived, the Licensor - reserves the exclusive right to collect such royalties for any - exercise by You of the rights granted under this License; - ii. Waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme can be waived, the Licensor waives the - exclusive right to collect such royalties for any exercise by You - of the rights granted under this License; and, - iii. Voluntary License Schemes. The Licensor waives the right to - collect royalties, whether individually or, in the event that the - Licensor is a member of a collecting society that administers - voluntary licensing schemes, via that society, from any exercise - by You of the rights granted under this License. - . - The above rights may be exercised in all media and formats whether now - known or hereafter devised. The above rights include the right to make - such modifications as are technically necessary to exercise the rights in - other media and formats. Subject to Section 8(f), all rights not expressly - granted by Licensor are hereby reserved. - . - 4. Restrictions. The license granted in Section 3 above is expressly made - subject to and limited by the following restrictions: - . - a. You may Distribute or Publicly Perform the Work only under the terms - of this License. You must include a copy of, or the Uniform Resource - Identifier (URI) for, this License with every copy of the Work You - Distribute or Publicly Perform. You may not offer or impose any terms - on the Work that restrict the terms of this License or the ability of - the recipient of the Work to exercise the rights granted to that - recipient under the terms of the License. You may not sublicense the - Work. You must keep intact all notices that refer to this License and - to the disclaimer of warranties with every copy of the Work You - Distribute or Publicly Perform. When You Distribute or Publicly - Perform the Work, You may not impose any effective technological - measures on the Work that restrict the ability of a recipient of the - Work from You to exercise the rights granted to that recipient under - the terms of the License. This Section 4(a) applies to the Work as - incorporated in a Collection, but this does not require the Collection - apart from the Work itself to be made subject to the terms of this - License. If You create a Collection, upon notice from any Licensor You - must, to the extent practicable, remove from the Collection any credit - as required by Section 4(c), as requested. If You create an - Adaptation, upon notice from any Licensor You must, to the extent - practicable, remove from the Adaptation any credit as required by - Section 4(c), as requested. - b. You may Distribute or Publicly Perform an Adaptation only under the - terms of: (i) this License; (ii) a later version of this License with - the same License Elements as this License; (iii) a Creative Commons - jurisdiction license (either this or a later license version) that - contains the same License Elements as this License (e.g., - Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible - License. If you license the Adaptation under one of the licenses - mentioned in (iv), you must comply with the terms of that license. If - you license the Adaptation under the terms of any of the licenses - mentioned in (i), (ii) or (iii) (the "Applicable License"), you must - comply with the terms of the Applicable License generally and the - following provisions: (I) You must include a copy of, or the URI for, - the Applicable License with every copy of each Adaptation You - Distribute or Publicly Perform; (II) You may not offer or impose any - terms on the Adaptation that restrict the terms of the Applicable - License or the ability of the recipient of the Adaptation to exercise - the rights granted to that recipient under the terms of the Applicable - License; (III) You must keep intact all notices that refer to the - Applicable License and to the disclaimer of warranties with every copy - of the Work as included in the Adaptation You Distribute or Publicly - Perform; (IV) when You Distribute or Publicly Perform the Adaptation, - You may not impose any effective technological measures on the - Adaptation that restrict the ability of a recipient of the Adaptation - from You to exercise the rights granted to that recipient under the - terms of the Applicable License. This Section 4(b) applies to the - Adaptation as incorporated in a Collection, but this does not require - the Collection apart from the Adaptation itself to be made subject to - the terms of the Applicable License. - c. If You Distribute, or Publicly Perform the Work or any Adaptations or - Collections, You must, unless a request has been made pursuant to - Section 4(a), keep intact all copyright notices for the Work and - provide, reasonable to the medium or means You are utilizing: (i) the - name of the Original Author (or pseudonym, if applicable) if supplied, - and/or if the Original Author and/or Licensor designate another party - or parties (e.g., a sponsor institute, publishing entity, journal) for - attribution ("Attribution Parties") in Licensor's copyright notice, - terms of service or by other reasonable means, the name of such party - or parties; (ii) the title of the Work if supplied; (iii) to the - extent reasonably practicable, the URI, if any, that Licensor - specifies to be associated with the Work, unless such URI does not - refer to the copyright notice or licensing information for the Work; - and (iv) , consistent with Ssection 3(b), in the case of an - Adaptation, a credit identifying the use of the Work in the Adaptation - (e.g., "French translation of the Work by Original Author," or - "Screenplay based on original Work by Original Author"). The credit - required by this Section 4(c) may be implemented in any reasonable - manner; provided, however, that in the case of a Adaptation or - Collection, at a minimum such credit will appear, if a credit for all - contributing authors of the Adaptation or Collection appears, then as - part of these credits and in a manner at least as prominent as the - credits for the other contributing authors. For the avoidance of - doubt, You may only use the credit required by this Section for the - purpose of attribution in the manner set out above and, by exercising - Your rights under this License, You may not implicitly or explicitly - assert or imply any connection with, sponsorship or endorsement by the - Original Author, Licensor and/or Attribution Parties, as appropriate, - of You or Your use of the Work, without the separate, express prior - written permission of the Original Author, Licensor and/or Attribution - Parties. - d. Except as otherwise agreed in writing by the Licensor or as may be - otherwise permitted by applicable law, if You Reproduce, Distribute or - Publicly Perform the Work either by itself or as part of any - Adaptations or Collections, You must not distort, mutilate, modify or - take other derogatory action in relation to the Work which would be - prejudicial to the Original Author's honor or reputation. Licensor - agrees that in those jurisdictions (e.g. Japan), in which any exercise - of the right granted in Section 3(b) of this License (the right to - make Adaptations) would be deemed to be a distortion, mutilation, - modification or other derogatory action prejudicial to the Original - Author's honor and reputation, the Licensor will waive or not assert, - as appropriate, this Section, to the fullest extent permitted by the - applicable national law, to enable You to reasonably exercise Your - right under Section 3(b) of this License (right to make Adaptations) - but not otherwise. - . - 5. Representations, Warranties and Disclaimer - . - UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR - OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY - KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, - INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, - FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF - LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, - WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION - OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. - . - 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE - LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR - ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES - ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS - BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - . - 7. Termination - . - a. This License and the rights granted hereunder will terminate - automatically upon any breach by You of the terms of this License. - Individuals or entities who have received Adaptations or Collections - from You under this License, however, will not have their licenses - terminated provided such individuals or entities remain in full - compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will - survive any termination of this License. - b. Subject to the above terms and conditions, the license granted here is - perpetual (for the duration of the applicable copyright in the Work). - Notwithstanding the above, Licensor reserves the right to release the - Work under different license terms or to stop distributing the Work at - any time; provided, however that any such election will not serve to - withdraw this License (or any other license that has been, or is - required to be, granted under the terms of this License), and this - License will continue in full force and effect unless terminated as - stated above. - . - 8. Miscellaneous - . - a. Each time You Distribute or Publicly Perform the Work or a Collection, - the Licensor offers to the recipient a license to the Work on the same - terms and conditions as the license granted to You under this License. - b. Each time You Distribute or Publicly Perform an Adaptation, Licensor - offers to the recipient a license to the original Work on the same - terms and conditions as the license granted to You under this License. - c. If any provision of this License is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability of - the remainder of the terms of this License, and without further action - by the parties to this agreement, such provision shall be reformed to - the minimum extent necessary to make such provision valid and - enforceable. - d. No term or provision of this License shall be deemed waived and no - breach consented to unless such waiver or consent shall be in writing - and signed by the party to be charged with such waiver or consent. - e. This License constitutes the entire agreement between the parties with - respect to the Work licensed here. There are no understandings, - agreements or representations with respect to the Work not specified - here. Licensor shall not be bound by any additional provisions that - may appear in any communication from You. This License may not be - modified without the mutual written agreement of the Licensor and You. - f. The rights granted under, and the subject matter referenced, in this - License were drafted utilizing the terminology of the Berne Convention - for the Protection of Literary and Artistic Works (as amended on - September 28, 1979), the Rome Convention of 1961, the WIPO Copyright - Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 - and the Universal Copyright Convention (as revised on July 24, 1971). - These rights and subject matter take effect in the relevant - jurisdiction in which the License terms are sought to be enforced - according to the corresponding provisions of the implementation of - those treaty provisions in the applicable national law. If the - standard suite of rights granted under applicable copyright law - includes additional rights not granted under this License, such - additional rights are deemed to be included in the License; this - License is not intended to restrict the license of any rights under - applicable law. - . - Creative Commons Notice - . - Creative Commons is not a party to this License, and makes no warranty - whatsoever in connection with the Work. Creative Commons will not be - liable to You or any party on any legal theory for any damages - whatsoever, including without limitation any general, special, - incidental or consequential damages arising in connection to this - license. Notwithstanding the foregoing two (2) sentences, if Creative - Commons has expressly identified itself as the Licensor hereunder, it - shall have all rights and obligations of Licensor. - . - Except for the limited purpose of indicating to the public that the - Work is licensed under the CCPL, Creative Commons does not authorize - the use by either party of the trademark "Creative Commons" or any - related trademark or logo of Creative Commons without the prior - written consent of Creative Commons. Any permitted use will be in - compliance with Creative Commons' then-current trademark usage - guidelines, as may be published on its website or otherwise made - available upon request from time to time. For the avoidance of doubt, - this trademark restriction does not form part of the License. - . - Creative Commons may be contacted at https://creativecommons.org/. - -License: CC-BY-SA-4 - Attribution-ShareAlike 4.0 International - . - ======================================================================= - . - Creative Commons Corporation ("Creative Commons") is not a law firm and - does not provide legal services or legal advice. Distribution of - Creative Commons public licenses does not create a lawyer-client or - other relationship. Creative Commons makes its licenses and related - information available on an "as-is" basis. Creative Commons gives no - warranties regarding its licenses, any material licensed under their - terms and conditions, or any related information. Creative Commons - disclaims all liability for damages resulting from their use to the - fullest extent possible. - . - Using Creative Commons Public Licenses - . - Creative Commons public licenses provide a standard set of terms and - conditions that creators and other rights holders may use to share - original works of authorship and other material subject to copyright - and certain other rights specified in the public license below. The - following considerations are for informational purposes only, are not - exhaustive, and do not form part of our licenses. - . - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - . - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - . - ======================================================================= - . - Creative Commons Attribution-ShareAlike 4.0 International Public - License - . - By exercising the Licensed Rights (defined below), You accept and agree - to be bound by the terms and conditions of this Creative Commons - Attribution-ShareAlike 4.0 International Public License ("Public - License"). To the extent this Public License may be interpreted as a - contract, You are granted the Licensed Rights in consideration of Your - acceptance of these terms and conditions, and the Licensor grants You - such rights in consideration of benefits the Licensor receives from - making the Licensed Material available under these terms and - conditions. - . - . - Section 1 -- Definitions. - . - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - . - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - . - c. BY-SA Compatible License means a license listed at - creativecommons.org/compatiblelicenses, approved by Creative - Commons as essentially the equivalent of this Public License. - . - d. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - . - e. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - . - f. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - . - g. License Elements means the license attributes listed in the name - of a Creative Commons Public License. The License Elements of this - Public License are Attribution and ShareAlike. - . - h. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - . - i. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - . - j. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - . - k. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - . - l. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - . - m. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - . - . - Section 2 -- Scope. - . - a. License grant. - . - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - . - a. reproduce and Share the Licensed Material, in whole or - in part; and - . - b. produce, reproduce, and Share Adapted Material. - . - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - . - 3. Term. The term of this Public License is specified in Section - 6(a). - . - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - . - 5. Downstream recipients. - . - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - . - b. Additional offer from the Licensor -- Adapted Material. - Every recipient of Adapted Material from You - automatically receives an offer from the Licensor to - exercise the Licensed Rights in the Adapted Material - under the conditions of the Adapter's License You apply. - . - c. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - . - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - . - b. Other rights. - . - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - . - 2. Patent and trademark rights are not licensed under this - Public License. - . - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - . - . - Section 3 -- License Conditions. - . - Your exercise of the Licensed Rights is expressly made subject to the - following conditions. - . - a. Attribution. - . - 1. If You Share the Licensed Material (including in modified - form), You must: - . - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - . - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - . - ii. a copyright notice; - . - iii. a notice that refers to this Public License; - . - iv. a notice that refers to the disclaimer of - warranties; - . - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - . - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - . - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - . - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - . - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - . - b. ShareAlike. - . - In addition to the conditions in Section 3(a), if You Share - Adapted Material You produce, the following conditions also apply. - . - 1. The Adapter's License You apply must be a Creative Commons - license with the same License Elements, this version or - later, or a BY-SA Compatible License. - . - 2. You must include the text of, or the URI or hyperlink to, the - Adapter's License You apply. You may satisfy this condition - in any reasonable manner based on the medium, means, and - context in which You Share Adapted Material. - . - 3. You may not offer or impose any additional or different terms - or conditions on, or apply any Effective Technological - Measures to, Adapted Material that restrict exercise of the - rights granted under the Adapter's License You apply. - . - . - Section 4 -- Sui Generis Database Rights. - . - Where the Licensed Rights include Sui Generis Database Rights that - apply to Your use of the Licensed Material: - . - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - . - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material, - . - including for purposes of Section 3(b); and - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - . - For the avoidance of doubt, this Section 4 supplements and does not - replace Your obligations under this Public License where the Licensed - Rights include other Copyright and Similar Rights. - . - . - Section 5 -- Disclaimer of Warranties and Limitation of Liability. - . - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - . - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - . - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - . - . - Section 6 -- Term and Termination. - . - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - . - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - . - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - . - 2. upon express reinstatement by the Licensor. - . - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - . - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - . - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - . - . - Section 7 -- Other Terms and Conditions. - . - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - . - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - . - . - Section 8 -- Interpretation. - . - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - . - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - . - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - . - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - . - . - ======================================================================= - . - Creative Commons is not a party to its public - licenses. Notwithstanding, Creative Commons may elect to apply one of - its public licenses to material it publishes and in those instances - will be considered the “Licensor.” The text of the Creative Commons - public licenses is dedicated to the public domain under the CC0 Public - Domain Dedication. Except for the limited purpose of indicating that - material is shared under a Creative Commons public license or as - otherwise permitted by the Creative Commons policies published at - creativecommons.org/policies, Creative Commons does not authorize the - use of the trademark "Creative Commons" or any other trademark or logo - of Creative Commons without its prior written consent including, - without limitation, in connection with any unauthorized modifications - to any of its public licenses or any other arrangements, - understandings, or agreements concerning use of licensed material. For - the avoidance of doubt, this paragraph does not form part of the - public licenses. - . - Creative Commons may be contacted at creativecommons.org. - -License: Expat - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - . - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - . - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -License: GPL-2+ - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - . - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - . - On Debian systems, the complete text of the GNU General Public License - can be found in `/usr/share/common-licenses/GPL-2'. - -License: LGPL-2+ - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - . - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - . - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - . - On Debian systems, the complete text of the GNU Library General Public License - can be found in `/usr/share/common-licenses/LGPL-2'. - -License: LGPL-2.1+ - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - . - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - . - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - . - On Debian systems, the complete text of the GNU Lesser General Public License - can be found in `/usr/share/common-licenses/LGPL-2.1'. - -License: WOL - Permission to use, copy, modify, distribute and sell this software and its - documentation for any purpose is hereby granted without fee, provided that - the above copyright notice and this license appear in all source copies. - THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY OF - ANY KIND. See https://www.dspguru.com/wol.htm for more information. - -License: non-free - This license does not comply with Debian Free Software Guidelines. diff --git a/debian/gbp.conf b/debian/gbp.conf deleted file mode 100644 index cec628c7444..00000000000 --- a/debian/gbp.conf +++ /dev/null @@ -1,2 +0,0 @@ -[DEFAULT] -pristine-tar = True diff --git a/debian/lmms-bin.install b/debian/lmms-bin.install deleted file mode 100644 index 229fa02e315..00000000000 --- a/debian/lmms-bin.install +++ /dev/null @@ -1,4 +0,0 @@ -usr/bin/lmms -usr/lib/*/lmms/ladspa/* -usr/lib/*/lmms/lib* -usr/lib/*/lmms/RemoteZynAddSubFx diff --git a/debian/lmms-bin.lintian-overrides b/debian/lmms-bin.lintian-overrides deleted file mode 100644 index 45e07c39d30..00000000000 --- a/debian/lmms-bin.lintian-overrides +++ /dev/null @@ -1,9 +0,0 @@ -lmms: pkg-has-shlibs-control-file-but-no-actual-shared-libs -lmms: postinst-has-useless-call-to-ldconfig -lmms: postrm-has-useless-call-to-ldconfig - -# env is used to set the environment, then lmms is called. -lmms: desktop-command-not-in-package usr/share/applications/lmms.desktop env - -# Icon is in lmms-common. -lmms: menu-icon-missing usr/share/pixmaps/lmms.xpm diff --git a/debian/lmms-common.docs b/debian/lmms-common.docs deleted file mode 100644 index a7b624a0a7a..00000000000 --- a/debian/lmms-common.docs +++ /dev/null @@ -1 +0,0 @@ -plugins/LadspaEffect/caps/caps.html diff --git a/debian/lmms-common.install b/debian/lmms-common.install deleted file mode 100644 index 467ee8798d3..00000000000 --- a/debian/lmms-common.install +++ /dev/null @@ -1,25 +0,0 @@ -usr/share/applications -usr/share/bash-completion -usr/share/icons -usr/share/lmms/[a-o]* -usr/share/lmms/presets/[A-Y]* -usr/share/lmms/projects/demos/Alf42red-* -usr/share/lmms/projects/demos/CapDan -usr/share/lmms/projects/demos/EsoXLB-* -usr/share/lmms/projects/demos/Impulslogik-* -usr/share/lmms/projects/demos/Jousboxx-* -usr/share/lmms/projects/demos/Momo64-* -usr/share/lmms/projects/demos/Oglsdl-* -usr/share/lmms/projects/demos/Settel-* -usr/share/lmms/projects/demos/Socceroos-* -usr/share/lmms/projects/demos/TameAnderson-* -usr/share/lmms/projects/demos/Thomasso-* -usr/share/lmms/projects/shorties/Crunk* -usr/share/lmms/projects/shorties/Greshz-* -usr/share/lmms/projects/shorties/Surrender-* -usr/share/lmms/projects/templates -usr/share/lmms/projects/tutorials -usr/share/lmms/[q-z]* -usr/share/man -usr/share/mime -debian/lmms.xpm usr/share/pixmaps/ diff --git a/debian/lmms-common.links b/debian/lmms-common.links deleted file mode 100644 index 75596233c53..00000000000 --- a/debian/lmms-common.links +++ /dev/null @@ -1 +0,0 @@ -usr/share/zynaddsubfx/banks usr/share/lmms/presets/ZynAddSubFX diff --git a/debian/lmms-common.maintscript b/debian/lmms-common.maintscript deleted file mode 100644 index 15d67355689..00000000000 --- a/debian/lmms-common.maintscript +++ /dev/null @@ -1 +0,0 @@ -dir_to_symlink /usr/share/lmms/presets/ZynAddSubFX ../../zynaddsubfx/banks 1.1.3-2~ diff --git a/debian/lmms-vst-server.install b/debian/lmms-vst-server.install deleted file mode 100644 index 60efaed7b08..00000000000 --- a/debian/lmms-vst-server.install +++ /dev/null @@ -1 +0,0 @@ -usr/lib/*/lmms/{32/,}RemoteVstPlugin* diff --git a/debian/lmms.xpm b/debian/lmms.xpm deleted file mode 100644 index 425e3156db3..00000000000 --- a/debian/lmms.xpm +++ /dev/null @@ -1,103 +0,0 @@ -/* XPM */ -static char * lmms_xpm[] = { -"24 24 76 1", -" c None", -". c #208B4D", -"+ c #208C4D", -"@ c #229452", -"# c #239654", -"$ c #239754", -"% c #299557", -"& c #2A9658", -"* c #239B56", -"= c #249B56", -"- c #249C57", -"; c #249D57", -"> c #259E58", -", c #259F59", -"' c #25A059", -") c #25A15A", -"! c #25A25A", -"~ c #25A35A", -"{ c #26A35B", -"] c #26A45B", -"^ c #26A55C", -"/ c #26A65C", -"( c #26A65D", -"_ c #3E9E67", -": c #26A75D", -"< c #28A75E", -"[ c #28A75F", -"} c #27A85E", -"| c #27A95E", -"1 c #27AA5F", -"2 c #27AB5F", -"3 c #29AC60", -"4 c #2AAC61", -"5 c #2FAC64", -"6 c #54A275", -"7 c #33AE67", -"8 c #56A477", -"9 c #2AB365", -"0 c #5BA67B", -"a c #3CB36F", -"b c #41B572", -"c c #64B285", -"d c #50BA7D", -"e c #58B881", -"f c #5CBA84", -"g c #56BC82", -"h c #5FBB86", -"i c #34D07B", -"j c #34D17A", -"k c #34D17B", -"l c #68C28F", -"m c #77C899", -"n c #7DCD9F", -"o c #90C7A8", -"p c #8CD2AA", -"q c #9AD7B4", -"r c #A9DDBF", -"s c #B0E0C5", -"t c #BDDDCB", -"u c #C3DFCF", -"v c #BFE6CF", -"w c #CBE3D5", -"x c #C5E6D3", -"y c #CDE9D9", -"z c #D1EDDD", -"A c #D7EFE2", -"B c #E1F0E8", -"C c #E6F5EC", -"D c #EAF6EF", -"E c #F3FAF6", -"F c #F7FBF9", -"G c #F8FBFA", -"H c #F6FCF9", -"I c #FCFEFD", -"J c #FDFEFE", -"K c #FFFFFF", -" ", -" kiiiiiiiiiiiiiiiiiij ", -" j92222222222222222229i ", -" i22222222222222222222i ", -" i22222222asvb22222222i ", -" i2222223nEKKHp4222222i ", -" i11111dzKKKKKKAg11111i ", -" i|||5qIKKBccBKKJr7|||i ", -" i}}lCKKFo%))&oGKKDm}}i ", -" i::KKKt_*(::(*_tKKK::i ", -" i//KKK@~//////~@KKK//i ", -" i^^KKK^^^^^^^^^^KKK^^i ", -" i]]KKK]]]]]]]]]]KKK]]i ", -" i{{KKKf{{{{{{{{fKKK{{i ", -" i!!KKKKyh!!!!exKKKK!!i ", -" i))KKKKKK))))KKKKKK))i ", -" i''KKKKKK''''KKKKKK''i ", -" i,,KKKKKK,,,,KKKKKK,,i ", -" i>>6uKKw0>>>>6uKKw0>>i ", -" i;;$+88.$;;;;$+88.$;;i ", -" i----##--------##----i ", -" j<==================[i ", -" jiiiiiiiiiiiiiiiiiii ", -" "}; diff --git a/debian/patches/build-amd64-20181013.patch b/debian/patches/build-amd64-20181013.patch deleted file mode 100644 index a9bb61da4d4..00000000000 --- a/debian/patches/build-amd64-20181013.patch +++ /dev/null @@ -1,46 +0,0 @@ -Description: Fix build as of 2018-10-13 - Fix build errors, possibly introduced with GCC 8. -Author: Javier Serrano Polo -Bug-Debian: https://bugs.debian.org/897806 - -Index: lmms-1.1.3/plugins/LadspaEffect/caps/dsp/FPTruncateMode.h -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/caps/dsp/FPTruncateMode.h -+++ lmms-1.1.3/plugins/LadspaEffect/caps/dsp/FPTruncateMode.h -@@ -40,9 +40,11 @@ class FPTruncateMode - - FPTruncateMode() - { -+#ifdef __i386__ - fstcw (cw0); - cw1 = cw0 | 0xC00; - fldcw (cw1); -+#endif - } - - ~FPTruncateMode() -Index: lmms-1.1.3/plugins/ZynAddSubFx/zynaddsubfx/src/Synth/OscilGen.cpp -=================================================================== ---- lmms-1.1.3.orig/plugins/ZynAddSubFx/zynaddsubfx/src/Synth/OscilGen.cpp -+++ lmms-1.1.3/plugins/ZynAddSubFx/zynaddsubfx/src/Synth/OscilGen.cpp -@@ -33,7 +33,8 @@ - //operations on FFTfreqs - inline void clearAll(fft_t *freqs) - { -- memset(freqs, 0, synth->oscilsize / 2 * sizeof(fft_t)); -+ for(int i = 0; i < synth->oscilsize / 2; ++i) -+ freqs[i] = fft_t(0.0f, 0.0f); - } - - inline void clearDC(fft_t *freqs) -@@ -928,8 +929,8 @@ void OscilGen::getspectrum(int n, float - if(what == 0) { - for(int i = 0; i < n; ++i) - outoscilFFTfreqs[i] = fft_t(spc[i], spc[i]); -- memset(outoscilFFTfreqs + n, 0, -- (synth->oscilsize / 2 - n) * sizeof(fft_t)); -+ for(int i = n; i < synth->oscilsize / 2; ++i) -+ outoscilFFTfreqs[i] = fft_t(0.0f, 0.0f); - adaptiveharmonic(outoscilFFTfreqs, 0.0f); - adaptiveharmonicpostprocess(outoscilFFTfreqs, n - 1); - for(int i = 0; i < n; ++i) diff --git a/debian/patches/clang.patch b/debian/patches/clang.patch deleted file mode 100644 index 7bbd15cd8de..00000000000 --- a/debian/patches/clang.patch +++ /dev/null @@ -1,601 +0,0 @@ -Description: Fix build with Clang - Several issues are present: - - Unused private elements. - - Wrong use of delete. - - Unsupported compiler options. - - Shifting negative values. - - Possible truncations. - - Uninitialized variables. - - Unused code. - - Hiding overloaded virtual functions. - - Declarations outside namespace. - - Mismatched class tag. - . - Be careful editing this patch because allegrosmfwr.cpp has CRLF terminators. -Author: Javier Serrano Polo -Bug: https://github.com/LMMS/lmms/issues/3073 - -Index: lmms-1.1.3/include/AutomatableModel.h -=================================================================== ---- lmms-1.1.3.orig/include/AutomatableModel.h 2017-01-03 13:01:47.000000000 +0100 -+++ lmms-1.1.3/include/AutomatableModel.h 2017-01-03 13:11:25.000000000 +0100 -@@ -307,7 +307,6 @@ - - // most objects will need this temporarily (until sampleExact is - // standard) -- float m_oldValue; - int m_setValueDepth; - - AutoModelVector m_linkedModels; -Index: lmms-1.1.3/plugins/LadspaEffect/calf/CMakeLists.txt -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/calf/CMakeLists.txt 2017-01-03 16:03:14.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/calf/CMakeLists.txt 2017-01-03 16:14:28.000000000 +0100 -@@ -7,11 +7,22 @@ - "${CMAKE_CURRENT_SOURCE_DIR}/src") - INSTALL(TARGETS calf LIBRARY DESTINATION "${PLUGIN_DIR}/ladspa") - SET_TARGET_PROPERTIES(calf PROPERTIES PREFIX "") -+ - SET(INLINE_FLAGS "") --IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") --SET(INLINE_FLAGS "-finline-functions-called-once -finline-limit=80") -+ -+INCLUDE(CheckCXXCompilerFlag) -+CHECK_CXX_COMPILER_FLAG(-finline-functions CXX_HAVE_INLINE_FUNCTIONS) -+IF(${CXX_HAVE_INLINE_FUNCTIONS}) -+ SET(INLINE_FLAGS "${INLINE_FLAGS} -finline-functions") - ENDIF() --SET_TARGET_PROPERTIES(calf PROPERTIES COMPILE_FLAGS "-O2 -finline-functions ${INLINE_FLAGS}") -+CHECK_CXX_COMPILER_FLAG(-finline-functions-called-once -+ CXX_HAVE_INLINE_FUNCTIONS_CALLED_ONCE) -+IF(${CXX_HAVE_INLINE_FUNCTIONS_CALLED_ONCE}) -+ SET(INLINE_FLAGS "${INLINE_FLAGS} -finline-functions-called-once \ -+ -finline-limit=80") -+ENDIF() -+ -+SET_TARGET_PROPERTIES(calf PROPERTIES COMPILE_FLAGS "-O2 ${INLINE_FLAGS}") - - IF(LMMS_BUILD_WIN32) - ADD_CUSTOM_COMMAND(TARGET calf POST_BUILD COMMAND "${STRIP}" "\"${CMAKE_CURRENT_BINARY_DIR}/calf.dll\"") -Index: lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/metadata.h -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/calf/veal/src/calf/metadata.h 2017-01-03 17:41:17.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/metadata.h 2017-01-03 17:50:40.000000000 +0100 -@@ -51,7 +51,7 @@ - enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, rt_capable = true, require_midi = false, support_midi = false }; - PLUGIN_NAME_ID_LABEL("filter", "filter", "Filter") - /// do not export mode and inertia as CVs, as those are settings and not parameters -- bool is_cv(int param_no) { return param_no != par_mode && param_no != par_inertia; } -+ bool is_cv(int param_no) const { return param_no != par_mode && param_no != par_inertia; } - }; - - /// Filterclavier - metadata -@@ -61,7 +61,7 @@ - enum { in_count = 2, out_count = 2, ins_optional = 0, outs_optional = 0, rt_capable = true, require_midi = true, support_midi = true }; - PLUGIN_NAME_ID_LABEL("filterclavier", "filterclavier", "Filterclavier") - /// do not export mode and inertia as CVs, as those are settings and not parameters -- bool is_cv(int param_no) { return param_no != par_mode && param_no != par_inertia; } -+ bool is_cv(int param_no) const { return param_no != par_mode && param_no != par_inertia; } - }; - - struct reverb_metadata: public plugin_metadata -@@ -499,7 +499,7 @@ - PLUGIN_NAME_ID_LABEL("organ", "organ", "Organ") - - public: -- plugin_command_info *get_commands(); -+ plugin_command_info *get_commands() const; - const char *const *get_configure_vars() const; - }; - -Index: lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/modules.h -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/calf/veal/src/calf/modules.h 2017-01-03 19:02:59.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/modules.h 2017-01-03 19:30:35.000000000 +0100 -@@ -89,13 +89,14 @@ - using audio_module::ins; - using audio_module::outs; - using audio_module::params; -+ using FilterClass::calculate_filter; - - dsp::inertia inertia_cutoff, inertia_resonance, inertia_gain; - dsp::once_per_n timer; - bool is_active; - mutable volatile int last_generation, last_calculated_generation; - -- filter_module_with_inertia(float **ins, float **outs, float **params) -+ filter_module_with_inertia() - : inertia_cutoff(dsp::exponential_ramp(128), 20) - , inertia_resonance(dsp::exponential_ramp(128), 20) - , inertia_gain(dsp::exponential_ramp(128), 1.0) -@@ -193,7 +194,7 @@ - mutable float old_cutoff, old_resonance, old_mode; - public: - filter_audio_module() -- : filter_module_with_inertia(ins, outs, params) -+ : filter_module_with_inertia() - { - last_generation = 0; - old_mode = old_resonance = old_cutoff = -1; -Index: lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/modules_comp.h -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/calf/veal/src/calf/modules_comp.h 2017-01-03 19:35:53.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/modules_comp.h 2017-01-03 19:38:06.000000000 +0100 -@@ -39,10 +39,10 @@ - class gain_reduction_audio_module - { - private: -- float linSlope, detected, kneeSqrt, kneeStart, linKneeStart, kneeStop; -+ float linSlope, detected, kneeStart, linKneeStart, kneeStop; - float compressedKneeStop, adjKneeStart, thres; - float attack, release, threshold, ratio, knee, makeup, detection, stereo_link, bypass, mute, meter_out, meter_comp; -- mutable float old_threshold, old_ratio, old_knee, old_makeup, old_bypass, old_mute, old_detection, old_stereo_link; -+ mutable float old_threshold, old_ratio, old_knee, old_makeup, old_bypass, old_mute, old_detection; - mutable volatile int last_generation; - uint32_t srate; - bool is_active; -@@ -69,7 +69,7 @@ - /// Main gate routine by Damien called by various audio modules - class expander_audio_module { - private: -- float linSlope, peak, detected, kneeSqrt, kneeStart, linKneeStart, kneeStop, linKneeStop; -+ float linSlope, detected, kneeStart, linKneeStart, kneeStop, linKneeStop; - float compressedKneeStop, adjKneeStart, range, thres, attack_coeff, release_coeff; - float attack, release, threshold, ratio, knee, makeup, detection, stereo_link, bypass, mute, meter_out, meter_gate; - mutable float old_threshold, old_ratio, old_knee, old_makeup, old_bypass, old_range, old_trigger, old_mute, old_detection, old_stereo_link; -@@ -142,7 +142,7 @@ - mutable float f1_freq_old, f2_freq_old, f1_level_old, f2_level_old; - mutable float f1_freq_old1, f2_freq_old1, f1_level_old1, f2_level_old1; - CalfScModes sc_mode; -- mutable CalfScModes sc_mode_old, sc_mode_old1; -+ mutable CalfScModes sc_mode_old1; - float f1_active, f2_active; - stereo_in_out_metering meters; - gain_reduction_audio_module compressor; -Index: lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/modules_limit.h -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/calf/veal/src/calf/modules_limit.h 2017-01-03 19:39:00.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/modules_limit.h 2017-01-03 19:40:20.000000000 +0100 -@@ -37,7 +37,6 @@ - private: - typedef limiter_audio_module AM; - uint32_t clip_inL, clip_inR, clip_outL, clip_outR, asc_led; -- int mode, mode_old; - float meter_inL, meter_inR, meter_outL, meter_outR; - dsp::lookahead_limiter limiter; - public: -@@ -73,7 +72,6 @@ - unsigned int overall_buffer_size; - float *buffer; - int channels; -- float striprel[strips]; - float weight[strips]; - float weight_old[strips]; - float limit_old; -Index: lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/modules_mod.h -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/calf/veal/src/calf/modules_mod.h 2017-01-03 19:41:55.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/modules_mod.h 2017-01-03 19:42:19.000000000 +0100 -@@ -160,8 +160,6 @@ - typedef pulsator_audio_module AM; - uint32_t clip_inL, clip_inR, clip_outL, clip_outR; - float meter_inL, meter_inR, meter_outL, meter_outR; -- float offset_old; -- int mode_old; - bool clear_reset; - dsp::simple_lfo lfoL, lfoR; - public: -Index: lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/organ.h -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/calf/veal/src/calf/organ.h 2017-01-03 19:43:08.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/organ.h 2017-01-03 19:53:55.000000000 +0100 -@@ -318,6 +318,7 @@ - using drawbar_organ::note_on; - using drawbar_organ::note_off; - using drawbar_organ::control_change; -+ using drawbar_organ::pitch_bend; - enum { param_count = drawbar_organ::param_count}; - dsp::organ_parameters par_values; - uint32_t srate; -@@ -338,9 +339,9 @@ - void deactivate(); - uint32_t process(uint32_t offset, uint32_t nsamples, uint32_t inputs_mask, uint32_t outputs_mask); - /// No CV inputs for now -- bool is_cv(int param_no) { return false; } -+ bool is_cv(int param_no) const { return false; } - /// Practically all the stuff here is noisy -- bool is_noisy(int param_no) { return true; } -+ bool is_noisy(int param_no) const { return true; } - void execute(int cmd_no); - bool get_graph(int index, int subindex, float *data, int points, cairo_iface *context) const; - char *configure(const char *key, const char *value); -Index: lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/preset.h -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/calf/veal/src/calf/preset.h 2017-01-03 19:57:02.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/preset.h 2017-01-03 20:00:25.000000000 +0100 -@@ -27,7 +27,7 @@ - - namespace calf_plugins { - --class plugin_ctl_iface; -+struct plugin_ctl_iface; - - /// Contents of single preset - struct plugin_preset -Index: lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/primitives.h -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/calf/veal/src/calf/primitives.h 2017-01-03 17:36:12.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/calf/primitives.h 2017-01-03 16:22:16.000000000 +0100 -@@ -370,11 +370,6 @@ - next_task = (unsigned)-1; - eob = false; - } -- inline bool is_next_tick() { -- if (time < next_task) -- return true; -- do_tasks(); -- } - inline void next_tick() { - time++; - } -@@ -382,14 +377,6 @@ - timeline.insert(std::pair(time+pos, t)); - next_task = timeline.begin()->first; - } -- void do_tasks() { -- std::multimap::iterator i = timeline.begin(); -- while(i != timeline.end() && i->first == time) { -- i->second->execute(this); -- i->second->dispose(); -- timeline.erase(i); -- } -- } - bool is_eob() { - return eob; - } -Index: lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/metadata.cpp -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/calf/veal/src/metadata.cpp 2017-01-03 17:52:03.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/metadata.cpp 2017-01-03 18:49:18.000000000 +0100 -@@ -29,6 +29,8 @@ - - const char *calf_plugins::calf_copyright_info = "(C) 2001-2009 Krzysztof Foltman, Thor Harald Johanssen, Markus Schmidt and others; license: LGPL"; - -+namespace calf_plugins { -+ - //////////////////////////////////////////////////////////////////////////// - - CALF_PORT_NAMES(flanger) = {"In L", "In R", "Out L", "Out R"}; -@@ -1105,7 +1107,7 @@ - - CALF_PLUGIN_INFO(organ) = { 0x8481, "Organ", "Calf Organ", "Krzysztof Foltman", calf_plugins::calf_copyright_info, "SynthesizerPlugin" }; - --plugin_command_info *organ_metadata::get_commands() -+plugin_command_info *organ_metadata::get_commands() const - { - static plugin_command_info cmds[] = { - { "cmd_panic", "Panic!", "Stop all sounds and reset all controllers" }, -@@ -1439,6 +1441,8 @@ - - //////////////////////////////////////////////////////////////////////////// - -+}; // namespace calf_plugins -+ - calf_plugins::plugin_registry::plugin_registry() - { - #define PER_MODULE_ITEM(name, isSynth, jackname) plugins.push_back((new name##_metadata)); -Index: lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/modules.cpp -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/calf/veal/src/modules.cpp 2017-01-03 19:32:38.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/calf/veal/src/modules.cpp 2017-01-03 19:33:13.000000000 +0100 -@@ -339,7 +339,7 @@ - /////////////////////////////////////////////////////////////////////////////////////////////// - - filterclavier_audio_module::filterclavier_audio_module() --: filter_module_with_inertia(ins, outs, params) -+: filter_module_with_inertia() - , min_gain(1.0) - , max_gain(32.0) - , last_note(-1) -Index: lmms-1.1.3/plugins/LadspaEffect/swh/ladspa/flanger_1191.c -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/swh/ladspa/flanger_1191.c 2017-01-03 15:44:13.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/swh/ladspa/flanger_1191.c 2017-01-03 15:45:17.000000000 +0100 -@@ -266,7 +266,7 @@ - - // Calculate position in delay table - d_base = LIN_INTERP(frac, old_d_base, new_d_base); -- n_ph = (float)(law_p - abs(next_law_pos - count))/(float)law_p; -+ n_ph = (float)(law_p - labs(next_law_pos - count))/(float)law_p; - p_ph = n_ph + 0.5f; - while (p_ph > 1.0f) { - p_ph -= 1.0f; -@@ -392,7 +392,7 @@ - - // Calculate position in delay table - d_base = LIN_INTERP(frac, old_d_base, new_d_base); -- n_ph = (float)(law_p - abs(next_law_pos - count))/(float)law_p; -+ n_ph = (float)(law_p - labs(next_law_pos - count))/(float)law_p; - p_ph = n_ph + 0.5f; - while (p_ph > 1.0f) { - p_ph -= 1.0f; -Index: lmms-1.1.3/plugins/LadspaEffect/swh/ladspa/gsm/short_term.c -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/swh/ladspa/gsm/short_term.c 2017-01-03 15:35:13.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/swh/ladspa/gsm/short_term.c 2017-01-03 15:35:55.000000000 +0100 -@@ -53,7 +53,7 @@ - #undef STEP - #define STEP( B, MIC, INVA ) \ - temp1 = GSM_ADD( *LARc++, MIC ) << 10; \ -- temp1 = GSM_SUB( temp1, B << 1 ); \ -+ temp1 = GSM_SUB( temp1, B * 2 ); \ - temp1 = GSM_MULT_R( INVA, temp1 ); \ - *LARpp++ = GSM_ADD( temp1, temp1 ); - -Index: lmms-1.1.3/plugins/LadspaEffect/swh/ladspa/multivoice_chorus_1201.c -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/swh/ladspa/multivoice_chorus_1201.c 2017-01-03 15:47:51.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/swh/ladspa/multivoice_chorus_1201.c 2017-01-03 15:48:18.000000000 +0100 -@@ -345,7 +345,7 @@ - if (count % 16 < laws) { - unsigned int t = count % 16; - // Calculate sinus phases -- float n_ph = (float)(law_p - abs(next_peak_pos[t] - count))/law_p; -+ float n_ph = (float)(law_p - labs(next_peak_pos[t] - count))/law_p; - float p_ph = n_ph + 0.5f; - if (p_ph > 1.0f) { - p_ph -= 1.0f; -@@ -488,7 +488,7 @@ - if (count % 16 < laws) { - unsigned int t = count % 16; - // Calculate sinus phases -- float n_ph = (float)(law_p - abs(next_peak_pos[t] - count))/law_p; -+ float n_ph = (float)(law_p - labs(next_peak_pos[t] - count))/law_p; - float p_ph = n_ph + 0.5f; - if (p_ph > 1.0f) { - p_ph -= 1.0f; -Index: lmms-1.1.3/plugins/LadspaEffect/swh/ladspa/retro_flange_1208.c -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/swh/ladspa/retro_flange_1208.c 2017-01-03 15:46:35.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/swh/ladspa/retro_flange_1208.c 2017-01-03 15:47:02.000000000 +0100 -@@ -321,7 +321,7 @@ - prev_law_pos = count + law_p; - } - -- n_ph = (float)(law_p - abs(next_law_pos - count))/(float)law_p; -+ n_ph = (float)(law_p - labs(next_law_pos - count))/(float)law_p; - p_ph = n_ph + 0.5f; - if (p_ph > 1.0f) { - p_ph -= 1.0f; -@@ -446,7 +446,7 @@ - prev_law_pos = count + law_p; - } - -- n_ph = (float)(law_p - abs(next_law_pos - count))/(float)law_p; -+ n_ph = (float)(law_p - labs(next_law_pos - count))/(float)law_p; - p_ph = n_ph + 0.5f; - if (p_ph > 1.0f) { - p_ph -= 1.0f; -Index: lmms-1.1.3/plugins/LadspaEffect/swh/ladspa/vynil_1905.c -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/swh/ladspa/vynil_1905.c 2017-01-03 15:51:56.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/swh/ladspa/vynil_1905.c 2017-01-03 16:01:32.000000000 +0100 -@@ -243,6 +243,8 @@ - buffer_s = malloc(sizeof(LADSPA_Data) * buffer_size); - buffer_mask = buffer_size - 1; - buffer_pos = 0; -+ click_buffer_omega.all = 0; -+ click_buffer_pos.all = 0; - click_gain = 0; - phi = 0.0f; /* Angular phase */ - -Index: lmms-1.1.3/plugins/LadspaEffect/tap/CMakeLists.txt -=================================================================== ---- lmms-1.1.3.orig/plugins/LadspaEffect/tap/CMakeLists.txt 2017-01-03 13:52:28.000000000 +0100 -+++ lmms-1.1.3/plugins/LadspaEffect/tap/CMakeLists.txt 2017-01-03 15:12:14.000000000 +0100 -@@ -1,7 +1,15 @@ - INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include") - FILE(GLOB PLUGIN_SOURCES *.c) - LIST(SORT PLUGIN_SOURCES) --SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -Wno-write-strings -fomit-frame-pointer -fno-strict-aliasing -fstrength-reduce -funroll-loops -ffast-math") -+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -Wno-write-strings \ -+ -fomit-frame-pointer -fno-strict-aliasing -funroll-loops -ffast-math") -+ -+INCLUDE(CheckCCompilerFlag) -+CHECK_C_COMPILER_FLAG(-fstrength-reduce C_HAVE_STRENGTH_REDUCE) -+IF(${C_HAVE_STRENGTH_REDUCE}) -+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstrength-reduce") -+ENDIF() -+ - FOREACH(_item ${PLUGIN_SOURCES}) - GET_FILENAME_COMPONENT(_plugin "${_item}" NAME_WE) - ADD_LIBRARY("${_plugin}" MODULE "${_item}") -Index: lmms-1.1.3/plugins/MidiImport/portsmf/allegro.h -=================================================================== ---- lmms-1.1.3.orig/plugins/MidiImport/portsmf/allegro.h 2017-01-03 20:02:37.000000000 +0100 -+++ lmms-1.1.3/plugins/MidiImport/portsmf/allegro.h 2017-01-03 20:06:48.000000000 +0100 -@@ -842,6 +842,8 @@ - Alg_event_ptr write_track_name(std::ostream &file, int n, - Alg_events &events); - public: -+ using Alg_track::paste; -+ - int channel_offset_per_track; // used to encode track_num into channel - Alg_tracks track_list; // array of Alg_events - Alg_time_sigs time_sig; -Index: lmms-1.1.3/plugins/MidiImport/portsmf/allegrosmfwr.cpp -=================================================================== ---- lmms-1.1.3.orig/plugins/MidiImport/portsmf/allegrosmfwr.cpp 2017-01-03 20:07:50.000000000 +0100 -+++ lmms-1.1.3/plugins/MidiImport/portsmf/allegrosmfwr.cpp 2017-01-03 20:08:34.000000000 +0100 -@@ -57,13 +57,11 @@ - - Alg_seq_ptr seq; - -- int num_tracks; // number of tracks not counting tempo track - int division; // divisions per quarter note, default = 120 - int initial_tempo; - - int timesig_num; // numerator of time signature - int timesig_den; // denominator of time signature -- double timesig_when; // time of time signature - - int keysig; // number of sharps (+) or flats (-), -99 for undefined - char keysig_mode; // 'M' or 'm' for major/minor -Index: lmms-1.1.3/plugins/Delay/StereoDelay.cpp -=================================================================== ---- lmms-1.1.3.orig/plugins/Delay/StereoDelay.cpp 2017-01-03 13:40:27.000000000 +0100 -+++ lmms-1.1.3/plugins/Delay/StereoDelay.cpp 2017-01-03 13:42:16.000000000 +0100 -@@ -48,7 +48,7 @@ - { - if( m_buffer ) - { -- delete m_buffer; -+ delete[] m_buffer; - } - } - -@@ -84,7 +84,7 @@ - { - if( m_buffer ) - { -- delete m_buffer; -+ delete[] m_buffer; - } - - int bufferSize = ( int )( sampleRate * m_maxTime ); -Index: lmms-1.1.3/plugins/OpulenZ/adplug/src/fmopl.c -=================================================================== ---- lmms-1.1.3.orig/plugins/OpulenZ/adplug/src/fmopl.c 2017-01-03 20:11:03.000000000 +0100 -+++ lmms-1.1.3/plugins/OpulenZ/adplug/src/fmopl.c 2017-01-03 20:22:23.000000000 +0100 -@@ -70,7 +70,7 @@ - /* final output shift , limit minimum and maximum */ - #define OPL_OUTSB (TL_BITS+3-16) /* OPL output final shift 16bit */ - #define OPL_MAXOUT (0x7fff<nextBuffer(); + const SampleFrame* b = audioEngine()->nextBuffer(); if( !b ) { break; @@ -104,7 +104,7 @@ class AudioDummy : public QThread, public AudioDevice delete[] b; } - const int microseconds = static_cast( audioEngine()->framesPerPeriod() * 1000000.0f / audioEngine()->processingSampleRate() - timer.elapsed() ); + const int microseconds = static_cast( audioEngine()->framesPerPeriod() * 1000000.0f / audioEngine()->outputSampleRate() - timer.elapsed() ); if( microseconds > 0 ) { usleep( microseconds ); diff --git a/include/AudioEngine.h b/include/AudioEngine.h index 67c2edd867a..f80c860d819 100644 --- a/include/AudioEngine.h +++ b/include/AudioEngine.h @@ -25,18 +25,16 @@ #ifndef LMMS_AUDIO_ENGINE_H #define LMMS_AUDIO_ENGINE_H -#ifdef __MINGW32__ -#include -#else #include -#endif #include #include +#include #include #include "lmms_basics.h" +#include "SampleFrame.h" #include "LocklessList.h" #include "FifoBuffer.h" #include "AudioEngineProfiler.h" @@ -57,8 +55,7 @@ const fpp_t DEFAULT_BUFFER_SIZE = 256; const int BYTES_PER_SAMPLE = sizeof( sample_t ); const int BYTES_PER_INT_SAMPLE = sizeof( int_sample_t ); -const int BYTES_PER_FRAME = sizeof( sampleFrame ); -const int BYTES_PER_SURROUND_FRAME = sizeof( surroundSampleFrame ); +const int BYTES_PER_FRAME = sizeof( SampleFrame ); const float OUTPUT_SAMPLE_MULTIPLIER = 32767.0f; @@ -108,13 +105,6 @@ class LMMS_EXPORT AudioEngine : public QObject struct qualitySettings { - enum class Mode - { - Draft, - HighQuality, - FinalMix - } ; - enum class Interpolation { Linear, @@ -123,55 +113,13 @@ class LMMS_EXPORT AudioEngine : public QObject SincBest } ; - enum class Oversampling - { - None, - X2, - X4, - X8 - } ; - Interpolation interpolation; - Oversampling oversampling; - - qualitySettings(Mode m) - { - switch (m) - { - case Mode::Draft: - interpolation = Interpolation::Linear; - oversampling = Oversampling::None; - break; - case Mode::HighQuality: - interpolation = - Interpolation::SincFastest; - oversampling = Oversampling::X2; - break; - case Mode::FinalMix: - interpolation = Interpolation::SincBest; - oversampling = Oversampling::X8; - break; - } - } - qualitySettings(Interpolation i, Oversampling o) : - interpolation(i), - oversampling(o) + qualitySettings(Interpolation i) : + interpolation(i) { } - int sampleRateMultiplier() const - { - switch( oversampling ) - { - case Oversampling::None: return 1; - case Oversampling::X2: return 2; - case Oversampling::X4: return 4; - case Oversampling::X8: return 8; - } - return 1; - } - int libsrcInterpolation() const { switch( interpolation ) @@ -289,8 +237,6 @@ class LMMS_EXPORT AudioEngine : public QObject sample_rate_t baseSampleRate() const; sample_rate_t outputSampleRate() const; sample_rate_t inputSampleRate() const; - sample_rate_t processingSampleRate() const; - inline float masterGain() const { @@ -317,15 +263,6 @@ class LMMS_EXPORT AudioEngine : public QObject } - struct StereoSample - { - StereoSample(sample_t _left, sample_t _right) : left(_left), right(_right) {} - sample_t left; - sample_t right; - }; - StereoSample getPeakValues(sampleFrame * ab, const f_cnt_t _frames) const; - - bool criticalXRuns() const; inline bool hasFifoWriter() const @@ -333,9 +270,9 @@ class LMMS_EXPORT AudioEngine : public QObject return m_fifoWriter != nullptr; } - void pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames ); + void pushInputFrames( SampleFrame* _ab, const f_cnt_t _frames ); - inline const sampleFrame * inputBuffer() + inline const SampleFrame* inputBuffer() { return m_inputBuffer[ m_inputBufferRead ]; } @@ -345,7 +282,7 @@ class LMMS_EXPORT AudioEngine : public QObject return m_inputBufferFrames[ m_inputBufferRead ]; } - inline const surroundSampleFrame * nextBuffer() + inline const SampleFrame* nextBuffer() { return hasFifoWriter() ? m_fifo->read() : renderNextBuffer(); } @@ -371,11 +308,11 @@ class LMMS_EXPORT AudioEngine : public QObject signals: void qualitySettingsChanged(); void sampleRateChanged(); - void nextAudioBuffer( const lmms::surroundSampleFrame * buffer ); + void nextAudioBuffer(const lmms::SampleFrame* buffer); private: - using Fifo = FifoBuffer; + using Fifo = FifoBuffer; class fifoWriter : public QThread { @@ -392,7 +329,7 @@ class LMMS_EXPORT AudioEngine : public QObject void run() override; - void write( surroundSampleFrame * buffer ); + void write(SampleFrame* buffer); } ; @@ -411,7 +348,7 @@ class LMMS_EXPORT AudioEngine : public QObject void renderStageEffects(); void renderStageMix(); - const surroundSampleFrame * renderNextBuffer(); + const SampleFrame* renderNextBuffer(); void swapBuffers(); @@ -425,14 +362,14 @@ class LMMS_EXPORT AudioEngine : public QObject fpp_t m_framesPerPeriod; - sampleFrame * m_inputBuffer[2]; + SampleFrame* m_inputBuffer[2]; f_cnt_t m_inputBufferFrames[2]; f_cnt_t m_inputBufferSize[2]; int m_inputBufferRead; int m_inputBufferWrite; - surroundSampleFrame * m_outputBufferRead; - surroundSampleFrame * m_outputBufferWrite; + std::unique_ptr m_outputBufferRead; + std::unique_ptr m_outputBufferWrite; // worker thread stuff std::vector m_workers; @@ -469,7 +406,7 @@ class LMMS_EXPORT AudioEngine : public QObject bool m_clearSignal; - std::mutex m_changeMutex; + std::recursive_mutex m_changeMutex; friend class Engine; friend class AudioEngineWorkerThread; diff --git a/include/AudioEngineWorkerThread.h b/include/AudioEngineWorkerThread.h index b76235aa125..60a5ef8104d 100644 --- a/include/AudioEngineWorkerThread.h +++ b/include/AudioEngineWorkerThread.h @@ -71,8 +71,8 @@ class AudioEngineWorkerThread : public QThread private: std::atomic m_items[JOB_QUEUE_SIZE]; - std::atomic_int m_writeIndex; - std::atomic_int m_itemsDone; + std::atomic_size_t m_writeIndex; + std::atomic_size_t m_itemsDone; OperationMode m_opMode; } ; diff --git a/include/AudioFileFlac.h b/include/AudioFileFlac.h index 9432f423177..003450afa14 100644 --- a/include/AudioFileFlac.h +++ b/include/AudioFileFlac.h @@ -65,7 +65,7 @@ class AudioFileFlac : public AudioFileDevice SF_INFO m_sfinfo; SNDFILE* m_sf; - void writeBuffer(surroundSampleFrame const* _ab, fpp_t const frames) override; + void writeBuffer(const SampleFrame* _ab, fpp_t const frames) override; bool startEncoding(); void finishEncoding(); diff --git a/include/AudioFileMP3.h b/include/AudioFileMP3.h index 013c93a3e95..e56ac0ba332 100644 --- a/include/AudioFileMP3.h +++ b/include/AudioFileMP3.h @@ -58,7 +58,7 @@ class AudioFileMP3 : public AudioFileDevice } protected: - void writeBuffer(const surroundSampleFrame* /* _buf*/, const fpp_t /*_frames*/) override; + void writeBuffer(const SampleFrame* /* _buf*/, const fpp_t /*_frames*/) override; private: void flushRemainingBuffers(); diff --git a/include/AudioFileOgg.h b/include/AudioFileOgg.h index fc3ce25b4ee..2608da359c7 100644 --- a/include/AudioFileOgg.h +++ b/include/AudioFileOgg.h @@ -58,7 +58,7 @@ class AudioFileOgg : public AudioFileDevice private: - void writeBuffer(const surroundSampleFrame* _ab, const fpp_t _frames) override; + void writeBuffer(const SampleFrame* _ab, const fpp_t _frames) override; bool startEncoding(); void finishEncoding(); diff --git a/include/AudioFileWave.h b/include/AudioFileWave.h index 22b124f9331..040af95eac9 100644 --- a/include/AudioFileWave.h +++ b/include/AudioFileWave.h @@ -56,7 +56,7 @@ class AudioFileWave : public AudioFileDevice private: - void writeBuffer(const surroundSampleFrame* _ab, const fpp_t _frames) override; + void writeBuffer(const SampleFrame* _ab, const fpp_t _frames) override; bool startEncoding(); void finishEncoding(); diff --git a/include/AudioJack.h b/include/AudioJack.h index 6efb262ed40..234f6ebf234 100644 --- a/include/AudioJack.h +++ b/include/AudioJack.h @@ -31,7 +31,7 @@ #ifndef LMMS_HAVE_WEAKJACK #include #else -#include "weak_libjack.h" +#include #endif #include @@ -93,7 +93,6 @@ private slots: void startProcessing() override; void stopProcessing() override; - void applyQualitySettings() override; void registerPort(AudioPort* port) override; void unregisterPort(AudioPort* port) override; @@ -112,7 +111,7 @@ private slots: std::atomic m_midiClient; std::vector m_outputPorts; jack_default_audio_sample_t** m_tempOutBufs; - surroundSampleFrame* m_outBuf; + SampleFrame* m_outBuf; f_cnt_t m_framesDoneInCurBuf; f_cnt_t m_framesToDoInCurBuf; diff --git a/include/AudioOss.h b/include/AudioOss.h index 55f64de851e..91d45607344 100644 --- a/include/AudioOss.h +++ b/include/AudioOss.h @@ -79,7 +79,6 @@ class setupWidget : public gui::AudioDeviceSetupWidget private: void startProcessing() override; void stopProcessing() override; - void applyQualitySettings() override; void run() override; int m_audioFD; diff --git a/include/AudioPort.h b/include/AudioPort.h index 9e3ce2bd667..12a0ec7d818 100644 --- a/include/AudioPort.h +++ b/include/AudioPort.h @@ -46,7 +46,7 @@ class AudioPort : public ThreadableJob BoolModel * mutedModel = nullptr ); virtual ~AudioPort(); - inline sampleFrame * buffer() + inline SampleFrame* buffer() { return m_portBuffer; } @@ -112,7 +112,7 @@ class AudioPort : public ThreadableJob private: volatile bool m_bufferUsage; - sampleFrame * m_portBuffer; + SampleFrame* m_portBuffer; QMutex m_portBufferLock; bool m_extOutputEnabled; diff --git a/include/AudioPortAudio.h b/include/AudioPortAudio.h index 01b8f3fd7e8..fbfa9b60dd6 100644 --- a/include/AudioPortAudio.h +++ b/include/AudioPortAudio.h @@ -84,11 +84,7 @@ class AudioPortAudio : public AudioDevice return QT_TRANSLATE_NOOP( "AudioDeviceSetupWidget", "PortAudio" ); } - - int process_callback( const float *_inputBuffer, - float * _outputBuffer, - unsigned long _framesPerBuffer ); - + int process_callback(const float* _inputBuffer, float* _outputBuffer, f_cnt_t _framesPerBuffer); class setupWidget : public gui::AudioDeviceSetupWidget { @@ -109,7 +105,6 @@ class AudioPortAudio : public AudioDevice private: void startProcessing() override; void stopProcessing() override; - void applyQualitySettings() override; #ifdef PORTAUDIO_V19 static int _process_callback( const void *_inputBuffer, void * _outputBuffer, @@ -151,9 +146,9 @@ class AudioPortAudio : public AudioDevice bool m_wasPAInitError; - surroundSampleFrame * m_outBuf; - int m_outBufPos; - int m_outBufSize; + SampleFrame* m_outBuf; + std::size_t m_outBufPos; + fpp_t m_outBufSize; bool m_stopped; diff --git a/include/AudioPulseAudio.h b/include/AudioPulseAudio.h index b6a99827479..db3c566bf53 100644 --- a/include/AudioPulseAudio.h +++ b/include/AudioPulseAudio.h @@ -88,7 +88,6 @@ class AudioPulseAudio : public QThread, public AudioDevice private: void startProcessing() override; void stopProcessing() override; - void applyQualitySettings() override; void run() override; volatile bool m_quit; diff --git a/include/AudioResampler.h b/include/AudioResampler.h index 379146962a8..6dd6fcc6039 100644 --- a/include/AudioResampler.h +++ b/include/AudioResampler.h @@ -52,6 +52,7 @@ class LMMS_EXPORT AudioResampler auto resample(const float* in, long inputFrames, float* out, long outputFrames, double ratio) -> ProcessResult; auto interpolationMode() const -> int { return m_interpolationMode; } auto channels() const -> int { return m_channels; } + void setRatio(double ratio); private: int m_interpolationMode = -1; diff --git a/include/AudioSampleRecorder.h b/include/AudioSampleRecorder.h index a3e776881fd..691196be62c 100644 --- a/include/AudioSampleRecorder.h +++ b/include/AudioSampleRecorder.h @@ -48,9 +48,9 @@ class AudioSampleRecorder : public AudioDevice std::shared_ptr createSampleBuffer(); private: - void writeBuffer(const surroundSampleFrame* _ab, const fpp_t _frames) override; + void writeBuffer(const SampleFrame* _ab, const fpp_t _frames) override; - using BufferList = QList>; + using BufferList = QList>; BufferList m_buffers; } ; diff --git a/include/AudioSdl.h b/include/AudioSdl.h index 62db8b68ace..651ed96be57 100644 --- a/include/AudioSdl.h +++ b/include/AudioSdl.h @@ -29,17 +29,12 @@ #ifdef LMMS_HAVE_SDL -#ifdef LMMS_HAVE_SDL2 #include -#else -#include -#include -#endif #include "AudioDevice.h" #include "AudioDeviceSetupWidget.h" -class QLineEdit; +class QComboBox; namespace lmms { @@ -64,50 +59,42 @@ class AudioSdl : public AudioDevice ~setupWidget() override = default; void saveSettings() override; + + private: + void populatePlaybackDeviceComboBox(); + void populateInputDeviceComboBox(); private: - QLineEdit * m_device; + QComboBox* m_playbackDeviceComboBox = nullptr; + QComboBox* m_inputDeviceComboBox = nullptr; + static QString s_systemDefaultDevice; } ; private: void startProcessing() override; void stopProcessing() override; - void applyQualitySettings() override; static void sdlAudioCallback( void * _udata, Uint8 * _buf, int _len ); void sdlAudioCallback( Uint8 * _buf, int _len ); -#ifdef LMMS_HAVE_SDL2 static void sdlInputAudioCallback( void * _udata, Uint8 * _buf, int _len ); void sdlInputAudioCallback( Uint8 * _buf, int _len ); -#endif SDL_AudioSpec m_audioHandle; - surroundSampleFrame * m_outBuf; + SampleFrame* m_outBuf; -#ifdef LMMS_HAVE_SDL2 size_t m_currentBufferFramePos; size_t m_currentBufferFramesCount; -#else - Uint8 * m_convertedBuf; - int m_convertedBufPos; - int m_convertedBufSize; - bool m_outConvertEndian; -#endif - bool m_stopped; -#ifdef LMMS_HAVE_SDL2 SDL_AudioDeviceID m_outputDevice; SDL_AudioSpec m_inputAudioHandle; SDL_AudioDeviceID m_inputDevice; -#endif - } ; diff --git a/include/AudioSndio.h b/include/AudioSndio.h index 594ca94e72b..beb4913eb4b 100644 --- a/include/AudioSndio.h +++ b/include/AudioSndio.h @@ -75,7 +75,6 @@ class AudioSndio : public QThread, public AudioDevice private: void startProcessing() override; void stopProcessing() override; - void applyQualitySettings() override; void run() override; struct sio_hdl *m_hdl; diff --git a/include/AudioSoundIo.h b/include/AudioSoundIo.h index b327f7d8447..1260951bffc 100644 --- a/include/AudioSoundIo.h +++ b/include/AudioSoundIo.h @@ -110,7 +110,7 @@ class AudioSoundIo : public AudioDevice SoundIo *m_soundio; SoundIoOutStream *m_outstream; - surroundSampleFrame * m_outBuf; + SampleFrame* m_outBuf; int m_outBufSize; fpp_t m_outBufFramesTotal; fpp_t m_outBufFrameIndex; diff --git a/include/BandLimitedWave.h b/include/BandLimitedWave.h index 1f402aa6ea0..1c1a052cac5 100644 --- a/include/BandLimitedWave.h +++ b/include/BandLimitedWave.h @@ -107,7 +107,7 @@ class LMMS_EXPORT BandLimitedWave */ static inline float freqToLen( float f ) { - return freqToLen( f, Engine::audioEngine()->processingSampleRate() ); + return freqToLen( f, Engine::audioEngine()->outputSampleRate() ); } /*! \brief This method converts frequency to wavelength, but you can use any custom sample rate with it. diff --git a/include/BasicFilters.h b/include/BasicFilters.h index 215c32dfb49..8d21b3657ca 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -36,6 +36,7 @@ #endif #include +#include #include "lmms_basics.h" #include "lmms_constants.h" @@ -340,7 +341,7 @@ class BasicFilters inline sample_t update( sample_t _in0, ch_cnt_t _chnl ) { - sample_t out; + sample_t out = 0.0f; switch( m_type ) { case FilterType::Moog: @@ -375,7 +376,6 @@ class BasicFilters // input signal is linear-interpolated after oversampling, output signal is averaged from oversampled outputs case FilterType::Tripole: { - out = 0.0f; float ip = 0.0f; for( int i = 0; i < 4; ++i ) { @@ -431,7 +431,6 @@ class BasicFilters case FilterType::Highpass_SV: { float hp; - for( int i = 0; i < 2; ++i ) // 2x oversample { m_delay2[_chnl] = m_delay2[_chnl] + m_svf1 * m_delay1[_chnl]; @@ -444,8 +443,7 @@ class BasicFilters case FilterType::Notch_SV: { - float hp1, hp2; - + float hp1; for( int i = 0; i < 2; ++i ) // 2x oversample { m_delay2[_chnl] = m_delay2[_chnl] + m_svf1 * m_delay1[_chnl]; /* delay2/4 = lowpass output */ @@ -453,7 +451,7 @@ class BasicFilters m_delay1[_chnl] = m_svf1 * hp1 + m_delay1[_chnl]; /* delay1/3 = bandpass output */ m_delay4[_chnl] = m_delay4[_chnl] + m_svf2 * m_delay3[_chnl]; - hp2 = m_delay2[_chnl] - m_delay4[_chnl] - m_svq * m_delay3[_chnl]; + float hp2 = m_delay2[_chnl] - m_delay4[_chnl] - m_svq * m_delay3[_chnl]; m_delay3[_chnl] = m_svf2 * hp2 + m_delay3[_chnl]; } @@ -469,19 +467,19 @@ class BasicFilters case FilterType::Lowpass_RC12: { - sample_t lp, bp, hp, in; + sample_t lp = 0.0f; for( int n = 4; n != 0; --n ) { - in = _in0 + m_rcbp0[_chnl] * m_rcq; + sample_t in = _in0 + m_rcbp0[_chnl] * m_rcq; in = std::clamp(in, -1.0f, 1.0f); lp = in * m_rcb + m_rclp0[_chnl] * m_rca; lp = std::clamp(lp, -1.0f, 1.0f); - hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] ); + sample_t hp = m_rcc * (m_rchp0[_chnl] + in - m_rclast0[_chnl]); hp = std::clamp(hp, -1.0f, 1.0f); - bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca; + sample_t bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca; bp = std::clamp(bp, -1.0f, 1.0f); m_rclast0[_chnl] = in; @@ -494,10 +492,10 @@ class BasicFilters case FilterType::Highpass_RC12: case FilterType::Bandpass_RC12: { - sample_t hp, bp, in; + sample_t hp, bp; for( int n = 4; n != 0; --n ) { - in = _in0 + m_rcbp0[_chnl] * m_rcq; + sample_t in = _in0 + m_rcbp0[_chnl] * m_rcq; in = std::clamp(in, -1.0f, 1.0f); hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] ); @@ -515,20 +513,20 @@ class BasicFilters case FilterType::Lowpass_RC24: { - sample_t lp, bp, hp, in; + sample_t lp; for( int n = 4; n != 0; --n ) { // first stage is as for the 12dB case... - in = _in0 + m_rcbp0[_chnl] * m_rcq; + sample_t in = _in0 + m_rcbp0[_chnl] * m_rcq; in = std::clamp(in, -1.0f, 1.0f); lp = in * m_rcb + m_rclp0[_chnl] * m_rca; lp = std::clamp(lp, -1.0f, 1.0f); - hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] ); + sample_t hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] ); hp = std::clamp(hp, -1.0f, 1.0f); - bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca; + sample_t bp = hp * m_rcb + m_rcbp0[_chnl] * m_rca; bp = std::clamp(bp, -1.0f, 1.0f); m_rclast0[_chnl] = in; @@ -559,11 +557,11 @@ class BasicFilters case FilterType::Highpass_RC24: case FilterType::Bandpass_RC24: { - sample_t hp, bp, in; + sample_t hp, bp; for( int n = 4; n != 0; --n ) { // first stage is as for the 12dB case... - in = _in0 + m_rcbp0[_chnl] * m_rcq; + sample_t in = _in0 + m_rcbp0[_chnl] * m_rcq; in = std::clamp(in, -1.0f, 1.0f); hp = m_rcc * ( m_rchp0[_chnl] + in - m_rclast0[_chnl] ); @@ -600,20 +598,18 @@ class BasicFilters case FilterType::FastFormant: { if (std::abs(_in0) < 1.0e-10f && std::abs(m_vflast[0][_chnl]) < 1.0e-10f) { return 0.0f; } // performance hack - skip processing when the numbers get too small - sample_t hp, bp, in; - out = 0; const int os = m_type == FilterType::FastFormant ? 1 : 4; // no oversampling for fast formant for( int o = 0; o < os; ++o ) { // first formant - in = _in0 + m_vfbp[0][_chnl] * m_vfq; + sample_t in = _in0 + m_vfbp[0][_chnl] * m_vfq; in = std::clamp(in, -1.0f, 1.0f); - hp = m_vfc[0] * ( m_vfhp[0][_chnl] + in - m_vflast[0][_chnl] ); + sample_t hp = m_vfc[0] * ( m_vfhp[0][_chnl] + in - m_vflast[0][_chnl] ); hp = std::clamp(hp, -1.0f, 1.0f); - bp = hp * m_vfb[0] + m_vfbp[0][_chnl] * m_vfa[0]; + sample_t bp = hp * m_vfb[0] + m_vfbp[0][_chnl] * m_vfa[0]; bp = std::clamp(bp, -1.0f, 1.0f); m_vflast[0][_chnl] = in; diff --git a/include/BufferManager.h b/include/BufferManager.h index 712e420ff97..84602f1215d 100644 --- a/include/BufferManager.h +++ b/include/BufferManager.h @@ -32,20 +32,14 @@ namespace lmms { +class SampleFrame; class LMMS_EXPORT BufferManager { public: static void init( fpp_t fpp ); - static sampleFrame * acquire(); - // audio-buffer-mgm - static void clear( sampleFrame * ab, const f_cnt_t frames, - const f_cnt_t offset = 0 ); -#ifndef LMMS_DISABLE_SURROUND - static void clear( surroundSampleFrame * ab, const f_cnt_t frames, - const f_cnt_t offset = 0 ); -#endif - static void release( sampleFrame * buf ); + static SampleFrame* acquire(); + static void release( SampleFrame* buf ); private: static fpp_t s_framesPerPeriod; diff --git a/include/ColorHelper.h b/include/ColorHelper.h new file mode 100644 index 00000000000..78f99b9e2c1 --- /dev/null +++ b/include/ColorHelper.h @@ -0,0 +1,54 @@ +/* ColorHelper.h - Helper methods for color related algorithms, etc. + * + * Copyright (c) 2024- Michael Gregorius + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_GUI_COLOR_HELPER_H +#define LMMS_GUI_COLOR_HELPER_H + +#include + +namespace lmms::gui +{ + +class ColorHelper +{ +public: + static QColor interpolateInRgb(const QColor& a, const QColor& b, float t) + { + qreal ar, ag, ab, aa; + a.getRgbF(&ar, &ag, &ab, &aa); + + qreal br, bg, bb, ba; + b.getRgbF(&br, &bg, &bb, &ba); + + const float interH = lerp(ar, br, t); + const float interS = lerp(ag, bg, t); + const float interV = lerp(ab, bb, t); + const float interA = lerp(aa, ba, t); + + return QColor::fromRgbF(interH, interS, interV, interA); + } +}; + +} // namespace lmms::gui + +#endif // LMMS_GUI_COLOR_HELPER_H diff --git a/include/ComboBoxModel.h b/include/ComboBoxModel.h index e90d804e2df..7037495ea34 100644 --- a/include/ComboBoxModel.h +++ b/include/ComboBoxModel.h @@ -47,11 +47,6 @@ class LMMS_EXPORT ComboBoxModel : public IntModel { } - ~ComboBoxModel() override - { - clear(); - } - void addItem( QString item, std::unique_ptr loader = nullptr ); void replaceItem(std::size_t index, QString item, std::unique_ptr loader = nullptr); diff --git a/include/ConfigManager.h b/include/ConfigManager.h index f6239c29783..3cba834e198 100644 --- a/include/ConfigManager.h +++ b/include/ConfigManager.h @@ -230,6 +230,7 @@ class LMMS_EXPORT ConfigManager : public QObject QString defaultVersion() const; + static bool enableBlockedPlugins(); static QStringList availableVstEmbedMethods(); QString vstEmbedMethod() const; diff --git a/include/ControllerRackView.h b/include/ControllerRackView.h index 303cc2b401f..93d1e84380c 100644 --- a/include/ControllerRackView.h +++ b/include/ControllerRackView.h @@ -65,9 +65,11 @@ class ControllerRackView : public QWidget, public SerializingObject public slots: - void deleteController( lmms::gui::ControllerView * _view ); - void onControllerAdded( lmms::Controller * ); - void onControllerRemoved( lmms::Controller * ); + void deleteController(ControllerView* view); + void moveUp(ControllerView* view); + void moveDown(ControllerView* view); + void addController(Controller* controller); + void removeController(Controller* controller); protected: void closeEvent( QCloseEvent * _ce ) override; diff --git a/include/ControllerView.h b/include/ControllerView.h index d1ba533a1a0..9b442672d94 100644 --- a/include/ControllerView.h +++ b/include/ControllerView.h @@ -63,12 +63,16 @@ class ControllerView : public QFrame, public ModelView public slots: void editControls(); - void deleteController(); + void removeController(); void closeControls(); void renameController(); + void moveUp(); + void moveDown(); signals: - void deleteController( lmms::gui::ControllerView * _view ); + void movedUp(ControllerView* view); + void movedDown(ControllerView* view); + void removedController(ControllerView* view); protected: diff --git a/include/DataFile.h b/include/DataFile.h index ce5d4edf4c2..7f5f5b88824 100644 --- a/include/DataFile.h +++ b/include/DataFile.h @@ -101,6 +101,8 @@ class LMMS_EXPORT DataFile : public QDomDocument void cleanMetaNodes( QDomElement de ); + void mapSrcAttributeInElementsWithResources(const QMap& map); + // helper upgrade routines void upgrade_0_2_1_20070501(); void upgrade_0_2_1_20070508(); @@ -129,6 +131,9 @@ class LMMS_EXPORT DataFile : public QDomDocument void upgrade_midiCCIndexing(); void upgrade_loopsRename(); void upgrade_noteTypes(); + void upgrade_fixCMTDelays(); + void upgrade_fixBassLoopsTypo(); + void findProblematicLadspaPlugins(); // List of all upgrade methods static const std::vector UPGRADE_METHODS; diff --git a/include/DrumSynth.h b/include/DrumSynth.h index 750055ddba9..3b418abd644 100644 --- a/include/DrumSynth.h +++ b/include/DrumSynth.h @@ -27,33 +27,33 @@ #define LMMS_DRUM_SYNTH_H #include + #include "lmms_basics.h" class QString; -namespace lmms -{ - -class DrumSynth { - public: - DrumSynth() = default; - int GetDSFileSamples(QString dsfile, int16_t *&wave, int channels, sample_rate_t Fs); - - private: - float LoudestEnv(); - int LongestEnv(); - void UpdateEnv(int e, long t); - void GetEnv(int env, const char *sec, const char *key, QString ini); - - float waveform(float ph, int form); - - int GetPrivateProfileString(const char *sec, const char *key, const char *def, char *buffer, int size, QString file); - int GetPrivateProfileInt(const char *sec, const char *key, int def, QString file); - float GetPrivateProfileFloat(const char *sec, const char *key, float def, QString file); +namespace lmms { +class DrumSynth +{ +public: + DrumSynth() = default; + int GetDSFileSamples(QString dsfile, int16_t*& wave, int channels, sample_rate_t Fs); + +private: + float LoudestEnv(); + int LongestEnv(); + void UpdateEnv(int e, long t); + void GetEnv(int env, const char* sec, const char* key, QString ini); + + float waveform(float ph, int form); + + int GetPrivateProfileString( + const char* sec, const char* key, const char* def, char* buffer, int size, QString file); + int GetPrivateProfileInt(const char* sec, const char* key, int def, QString file); + float GetPrivateProfileFloat(const char* sec, const char* key, float def, QString file); }; - } // namespace lmms #endif // LMMS_DRUM_SYNTH_H diff --git a/include/DspEffectLibrary.h b/include/DspEffectLibrary.h index 14dbd7ede88..348c7076509 100644 --- a/include/DspEffectLibrary.h +++ b/include/DspEffectLibrary.h @@ -28,7 +28,7 @@ #include "lmms_math.h" #include "lmms_constants.h" #include "lmms_basics.h" - +#include "SampleFrame.h" namespace lmms::DspEffectLibrary { @@ -80,6 +80,17 @@ namespace lmms::DspEffectLibrary { } + void setGain(float gain) + { + leftFX().setGain(gain); + rightFX().setGain(gain); + } + + void nextSample(SampleFrame & in) + { + nextSample(in.left(), in.right()); + } + void nextSample( sample_t& inLeft, sample_t& inRight ) { inLeft = m_leftFX.nextSample( inLeft ); diff --git a/include/DummyEffect.h b/include/DummyEffect.h index 0a80b90a981..e2e649ab54d 100644 --- a/include/DummyEffect.h +++ b/include/DummyEffect.h @@ -107,7 +107,7 @@ class DummyEffect : public Effect return &m_controls; } - bool processAudioBuffer( sampleFrame *, const fpp_t ) override + bool processAudioBuffer( SampleFrame*, const fpp_t ) override { return false; } diff --git a/include/DummyInstrument.h b/include/DummyInstrument.h index aefa035db7b..0962dcd9e87 100644 --- a/include/DummyInstrument.h +++ b/include/DummyInstrument.h @@ -49,10 +49,9 @@ class DummyInstrument : public Instrument ~DummyInstrument() override = default; - void playNote( NotePlayHandle *, sampleFrame * buffer ) override + void playNote( NotePlayHandle*, SampleFrame* buffer ) override { - memset( buffer, 0, sizeof( sampleFrame ) * - Engine::audioEngine()->framesPerPeriod() ); + zeroSampleFrames(buffer, Engine::audioEngine()->framesPerPeriod()); } void saveSettings( QDomDocument &, QDomElement & ) override diff --git a/include/Effect.h b/include/Effect.h index 8b2ff81f0c4..34f8be00a07 100644 --- a/include/Effect.h +++ b/include/Effect.h @@ -64,7 +64,7 @@ class LMMS_EXPORT Effect : public Plugin } - virtual bool processAudioBuffer( sampleFrame * _buf, + virtual bool processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) = 0; inline ch_cnt_t processorCount() const @@ -111,7 +111,7 @@ class LMMS_EXPORT Effect : public Plugin inline f_cnt_t timeout() const { - const float samples = Engine::audioEngine()->processingSampleRate() * m_autoQuitModel.value() / 1000.0f; + const float samples = Engine::audioEngine()->outputSampleRate() * m_autoQuitModel.value() / 1000.0f; return 1 + ( static_cast( samples ) / Engine::audioEngine()->framesPerPeriod() ); } @@ -187,33 +187,35 @@ class LMMS_EXPORT Effect : public Plugin // some effects might not be capable of higher sample-rates so they can // sample it down before processing and back after processing - inline void sampleDown( const sampleFrame * _src_buf, - sampleFrame * _dst_buf, + inline void sampleDown( const SampleFrame* _src_buf, + SampleFrame* _dst_buf, sample_rate_t _dst_sr ) { resample( 0, _src_buf, - Engine::audioEngine()->processingSampleRate(), + Engine::audioEngine()->outputSampleRate(), _dst_buf, _dst_sr, Engine::audioEngine()->framesPerPeriod() ); } - inline void sampleBack( const sampleFrame * _src_buf, - sampleFrame * _dst_buf, + inline void sampleBack( const SampleFrame* _src_buf, + SampleFrame* _dst_buf, sample_rate_t _src_sr ) { resample( 1, _src_buf, _src_sr, _dst_buf, - Engine::audioEngine()->processingSampleRate(), + Engine::audioEngine()->outputSampleRate(), Engine::audioEngine()->framesPerPeriod() * _src_sr / - Engine::audioEngine()->processingSampleRate() ); + Engine::audioEngine()->outputSampleRate() ); } void reinitSRC(); + virtual void onEnabledChanged() {} + private: EffectChain * m_parent; - void resample( int _i, const sampleFrame * _src_buf, + void resample( int _i, const SampleFrame* _src_buf, sample_rate_t _src_sr, - sampleFrame * _dst_buf, sample_rate_t _dst_sr, + SampleFrame* _dst_buf, sample_rate_t _dst_sr, const f_cnt_t _frames ); ch_cnt_t m_processors; diff --git a/include/EffectChain.h b/include/EffectChain.h index f9482174ed5..0af23c4b82b 100644 --- a/include/EffectChain.h +++ b/include/EffectChain.h @@ -34,6 +34,7 @@ namespace lmms { class Effect; +class SampleFrame; namespace gui { @@ -62,7 +63,7 @@ class LMMS_EXPORT EffectChain : public Model, public SerializingObject void removeEffect( Effect * _effect ); void moveDown( Effect * _effect ); void moveUp( Effect * _effect ); - bool processAudioBuffer( sampleFrame * _buf, const fpp_t _frames, bool hasInputNoise ); + bool processAudioBuffer( SampleFrame* _buf, const fpp_t _frames, bool hasInputNoise ); void startRunning(); void clear(); diff --git a/include/EffectRackView.h b/include/EffectRackView.h index a1e21be0985..fec627a56f9 100644 --- a/include/EffectRackView.h +++ b/include/EffectRackView.h @@ -53,10 +53,9 @@ class EffectRackView : public QWidget, public ModelView public slots: void clearViews(); - void moveUp( lmms::gui::EffectView* view ); - void moveDown( lmms::gui::EffectView* view ); - void deletePlugin( lmms::gui::EffectView* view ); - + void moveUp(EffectView* view); + void moveDown(EffectView* view); + void deletePlugin(EffectView* view); private slots: virtual void update(); @@ -65,6 +64,8 @@ private slots: private: void modelChanged() override; + QSize sizeHint() const override; + QSize minimumSizeHint() const override { return sizeHint(); } inline EffectChain* fxChain() { diff --git a/include/EffectView.h b/include/EffectView.h index e90700952c7..805e4a4279c 100644 --- a/include/EffectView.h +++ b/include/EffectView.h @@ -77,10 +77,9 @@ public slots: signals: - void moveUp( lmms::gui::EffectView * _plugin ); - void moveDown( lmms::gui::EffectView * _plugin ); - void deletePlugin( lmms::gui::EffectView * _plugin ); - + void movedUp(EffectView* view); + void movedDown(EffectView* view); + void deletedPlugin(EffectView* view); protected: void contextMenuEvent( QContextMenuEvent * _me ) override; diff --git a/include/Engine.h b/include/Engine.h index ed4cbd93cbc..7e19e2e8483 100644 --- a/include/Engine.h +++ b/include/Engine.h @@ -81,8 +81,6 @@ class LMMS_EXPORT Engine : public QObject return s_projectJournal; } - static bool ignorePluginBlacklist(); - #ifdef LMMS_HAVE_LV2 static class Lv2Manager * getLv2Manager() { diff --git a/include/EnvelopeAndLfoParameters.h b/include/EnvelopeAndLfoParameters.h index 2a8d3a685e6..50bfdf78709 100644 --- a/include/EnvelopeAndLfoParameters.h +++ b/include/EnvelopeAndLfoParameters.h @@ -71,7 +71,18 @@ class LMMS_EXPORT EnvelopeAndLfoParameters : public Model, public JournallingObj using LfoList = QList; LfoList m_lfos; - } ; + }; + + enum class LfoShape + { + SineWave, + TriangleWave, + SawWave, + SquareWave, + UserDefinedWave, + RandomWave, + Count + }; EnvelopeAndLfoParameters( float _value_for_zero_amount, Model * _parent ); @@ -114,6 +125,28 @@ class LMMS_EXPORT EnvelopeAndLfoParameters : public Model, public JournallingObj return m_rFrames; } + // Envelope + const FloatModel& getPredelayModel() const { return m_predelayModel; } + const FloatModel& getAttackModel() const { return m_attackModel; } + const FloatModel& getHoldModel() const { return m_holdModel; } + const FloatModel& getDecayModel() const { return m_decayModel; } + const FloatModel& getSustainModel() const { return m_sustainModel; } + const FloatModel& getReleaseModel() const { return m_releaseModel; } + const FloatModel& getAmountModel() const { return m_amountModel; } + FloatModel& getAmountModel() { return m_amountModel; } + + + // LFO + inline f_cnt_t getLfoPredelayFrames() const { return m_lfoPredelayFrames; } + inline f_cnt_t getLfoAttackFrames() const { return m_lfoAttackFrames; } + inline f_cnt_t getLfoOscillationFrames() const { return m_lfoOscillationFrames; } + + const FloatModel& getLfoAmountModel() const { return m_lfoAmountModel; } + FloatModel& getLfoAmountModel() { return m_lfoAmountModel; } + const TempoSyncKnobModel& getLfoSpeedModel() const { return m_lfoSpeedModel; } + const BoolModel& getX100Model() const { return m_x100Model; } + const IntModel& getLfoWaveModel() const { return m_lfoWaveModel; } + std::shared_ptr getLfoUserWave() const { return m_userWave; } public slots: void updateSampleVars(); @@ -170,16 +203,6 @@ public slots: bool m_bad_lfoShapeData; std::shared_ptr m_userWave = SampleBuffer::emptyBuffer(); - enum class LfoShape - { - SineWave, - TriangleWave, - SawWave, - SquareWave, - UserDefinedWave, - RandomWave, - Count - } ; constexpr static auto NumLfoShapes = static_cast(LfoShape::Count); sample_t lfoShapeSample( fpp_t _frame_offset ); diff --git a/include/EnvelopeAndLfoView.h b/include/EnvelopeAndLfoView.h index d545aaa0687..0063dc78815 100644 --- a/include/EnvelopeAndLfoView.h +++ b/include/EnvelopeAndLfoView.h @@ -29,10 +29,6 @@ #include #include "ModelView.h" -#include "embed.h" - -class QPaintEvent; -class QPixmap; namespace lmms { @@ -47,6 +43,8 @@ class Knob; class LedCheckBox; class PixmapButton; class TempoSyncKnob; +class EnvelopeGraph; +class LfoGraph; @@ -63,8 +61,6 @@ class EnvelopeAndLfoView : public QWidget, public ModelView void dragEnterEvent( QDragEnterEvent * _dee ) override; void dropEvent( QDropEvent * _de ) override; - void mousePressEvent( QMouseEvent * _me ) override; - void paintEvent( QPaintEvent * _pe ) override; protected slots: @@ -72,13 +68,10 @@ protected slots: private: - QPixmap m_envGraph = embed::getIconPixmap("envelope_graph"); - QPixmap m_lfoGraph = embed::getIconPixmap("lfo_graph"); - EnvelopeAndLfoParameters * m_params; - // envelope stuff + EnvelopeGraph* m_envelopeGraph; Knob * m_predelayKnob; Knob * m_attackKnob; Knob * m_holdKnob; @@ -88,6 +81,7 @@ protected slots: Knob * m_amountKnob; // LFO stuff + LfoGraph* m_lfoGraph; Knob * m_lfoPredelayKnob; Knob * m_lfoAttackKnob; TempoSyncKnob * m_lfoSpeedKnob; @@ -97,8 +91,6 @@ protected slots: LedCheckBox * m_x100Cb; LedCheckBox * m_controlEnvAmountCb; - - float m_randomGraph; } ; } // namespace gui diff --git a/include/EnvelopeGraph.h b/include/EnvelopeGraph.h new file mode 100644 index 00000000000..4f8a5c38679 --- /dev/null +++ b/include/EnvelopeGraph.h @@ -0,0 +1,77 @@ +/* + * EnvelopeGraph.h - Displays envelope graphs + * + * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2024- Michael Gregorius + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_GUI_ENVELOPE_GRAPH_H +#define LMMS_GUI_ENVELOPE_GRAPH_H + +#include + +#include "ModelView.h" +#include "embed.h" + +namespace lmms +{ + +class EnvelopeAndLfoParameters; + +namespace gui +{ + +class EnvelopeGraph : public QWidget, public ModelView +{ +public: + enum class ScalingMode + { + Dynamic, + Absolute, + Relative + }; + +public: + EnvelopeGraph(QWidget* parent); + +protected: + void modelChanged() override; + + void mousePressEvent(QMouseEvent*) override; + void contextMenuEvent(QContextMenuEvent*) override; + void paintEvent(QPaintEvent*) override; + +private: + void toggleAmountModel(); + +private: + QPixmap m_envGraph = embed::getIconPixmap("envelope_graph"); + + EnvelopeAndLfoParameters* m_params = nullptr; + + ScalingMode m_scaling = ScalingMode::Dynamic; +}; + +} // namespace gui + +} // namespace lmms + +#endif // LMMS_GUI_ENVELOPE_GRAPH_H diff --git a/include/Fader.h b/include/Fader.h index 20132f71db2..53e353a3dc5 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -54,6 +54,7 @@ #include "AutomatableModelView.h" #include "embed.h" +#include "lmms_math.h" namespace lmms::gui @@ -66,21 +67,21 @@ class LMMS_EXPORT Fader : public QWidget, public FloatModelView { Q_OBJECT public: - Q_PROPERTY( QColor peakGreen READ peakGreen WRITE setPeakGreen ) - Q_PROPERTY( QColor peakRed READ peakRed WRITE setPeakRed ) - Q_PROPERTY( QColor peakYellow READ peakYellow WRITE setPeakYellow ) - Q_PROPERTY( bool levelsDisplayedInDBFS READ getLevelsDisplayedInDBFS WRITE setLevelsDisplayedInDBFS ) - - Fader( FloatModel * _model, const QString & _name, QWidget * _parent ); - Fader( FloatModel * _model, const QString & _name, QWidget * _parent, QPixmap * back, QPixmap * leds, QPixmap * knob ); + Q_PROPERTY(QColor peakOk MEMBER m_peakOk) + Q_PROPERTY(QColor peakClip MEMBER m_peakClip) + Q_PROPERTY(QColor peakWarn MEMBER m_peakWarn) + Q_PROPERTY(bool levelsDisplayedInDBFS MEMBER m_levelsDisplayedInDBFS) + Q_PROPERTY(bool renderUnityLine READ getRenderUnityLine WRITE setRenderUnityLine) + Q_PROPERTY(QColor unityMarker MEMBER m_unityMarker) + + Fader(FloatModel* model, const QString& name, QWidget* parent); + Fader(FloatModel* model, const QString& name, QWidget* parent, const QPixmap& knob); ~Fader() override = default; - void init(FloatModel * model, QString const & name); - - void setPeak_L( float fPeak ); + void setPeak_L(float fPeak); float getPeak_L() { return m_fPeakValue_L; } - void setPeak_R( float fPeak ); + void setPeak_R(float fPeak); float getPeak_R() { return m_fPeakValue_R; } inline float getMinPeak() const { return m_fMinPeak; } @@ -89,43 +90,34 @@ class LMMS_EXPORT Fader : public QWidget, public FloatModelView inline float getMaxPeak() const { return m_fMaxPeak; } inline void setMaxPeak(float maxPeak) { m_fMaxPeak = maxPeak; } - QColor const & peakGreen() const; - void setPeakGreen( const QColor & c ); - - QColor const & peakRed() const; - void setPeakRed( const QColor & c ); - - QColor const & peakYellow() const; - void setPeakYellow( const QColor & c ); + inline bool getRenderUnityLine() const { return m_renderUnityLine; } + inline void setRenderUnityLine(bool value = true) { m_renderUnityLine = value; } - inline bool getLevelsDisplayedInDBFS() const { return m_levelsDisplayedInDBFS; } - inline void setLevelsDisplayedInDBFS(bool value = true) { m_levelsDisplayedInDBFS = value; } - - void setDisplayConversion( bool b ) + void setDisplayConversion(bool b) { m_conversionFactor = b ? 100.0 : 1.0; } - inline void setHintText( const QString & _txt_before, - const QString & _txt_after ) + inline void setHintText(const QString& txt_before, + const QString& txt_after) { - setDescription( _txt_before ); - setUnit( _txt_after ); + setDescription(txt_before); + setUnit(txt_after); } -private: - void contextMenuEvent( QContextMenuEvent * _me ) override; - void mousePressEvent( QMouseEvent *ev ) override; - void mouseDoubleClickEvent( QMouseEvent* mouseEvent ) override; - void mouseMoveEvent( QMouseEvent *ev ) override; - void mouseReleaseEvent( QMouseEvent * _me ) override; - void wheelEvent( QWheelEvent *ev ) override; - void paintEvent( QPaintEvent *ev ) override; +signals: + void peakChanged(float peak); - inline bool clips(float const & value) const { return value >= 1.0f; } +private: + void contextMenuEvent(QContextMenuEvent* me) override; + void mousePressEvent(QMouseEvent* ev) override; + void mouseDoubleClickEvent(QMouseEvent* mouseEvent) override; + void mouseMoveEvent(QMouseEvent* ev) override; + void mouseReleaseEvent(QMouseEvent* me) override; + void wheelEvent(QWheelEvent* ev) override; + void paintEvent(QPaintEvent* ev) override; - void paintDBFSLevels(QPaintEvent *ev, QPainter & painter); - void paintLinearLevels(QPaintEvent *ev, QPainter & painter); + void paintLevels(QPaintEvent* ev, QPainter& painter, bool linear = false); int knobPosY() const { @@ -135,37 +127,37 @@ class LMMS_EXPORT Fader : public QWidget, public FloatModelView return height() - ((height() - m_knob.height()) * (realVal / fRange)); } - void setPeak( float fPeak, float &targetPeak, float &persistentPeak, QElapsedTimer &lastPeakTimer ); - int calculateDisplayPeak( float fPeak ); + void setPeak(float fPeak, float& targetPeak, float& persistentPeak, QElapsedTimer& lastPeakTimer); void updateTextFloat(); // Private members private: - float m_fPeakValue_L; - float m_fPeakValue_R; - float m_persistentPeak_L; - float m_persistentPeak_R; - float m_fMinPeak; - float m_fMaxPeak; + float m_fPeakValue_L {0.}; + float m_fPeakValue_R {0.}; + float m_persistentPeak_L {0.}; + float m_persistentPeak_R {0.}; + float m_fMinPeak {dbfsToAmp(-42)}; + float m_fMaxPeak {dbfsToAmp(9)}; QElapsedTimer m_lastPeakTimer_L; QElapsedTimer m_lastPeakTimer_R; - QPixmap m_back; - QPixmap m_leds; - QPixmap m_knob; + QPixmap m_knob {embed::getIconPixmap("fader_knob")}; + + bool m_levelsDisplayedInDBFS {true}; - bool m_levelsDisplayedInDBFS; + int m_moveStartPoint {-1}; + float m_startValue {0.}; - int m_moveStartPoint; - float m_startValue; + static SimpleTextFloat* s_textFloat; - static SimpleTextFloat * s_textFloat; + QColor m_peakOk {10, 212, 92}; + QColor m_peakClip {193, 32, 56}; + QColor m_peakWarn {214, 236, 82}; + QColor m_unityMarker {63, 63, 63, 255}; - QColor m_peakGreen; - QColor m_peakRed; - QColor m_peakYellow; + bool m_renderUnityLine {true}; } ; diff --git a/include/FileBrowser.h b/include/FileBrowser.h index b0c8a519992..9193da5e405 100644 --- a/include/FileBrowser.h +++ b/include/FileBrowser.h @@ -28,10 +28,11 @@ #include #include #include -#include "embed.h" - -#include "FileBrowserSearcher.h" #include +#include + +#include "FileSearch.h" +#include "embed.h" #if (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) #include @@ -76,9 +77,9 @@ class FileBrowser : public SideBarWidget ~FileBrowser() override = default; - static QStringList directoryBlacklist() + static QStringList excludedPaths() { - static auto s_blacklist = QStringList{ + static auto s_excludedPaths = QStringList{ #ifdef LMMS_BUILD_LINUX "/bin", "/boot", "/dev", "/etc", "/proc", "/run", "/sbin", "/sys" @@ -87,9 +88,9 @@ class FileBrowser : public SideBarWidget "C:\\Windows" #endif }; - return s_blacklist; + return s_excludedPaths; } - static QDir::Filters dirFilters() { return QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot; } + static QDir::Filters dirFilters() { return QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot | QDir::Hidden; } static QDir::SortFlags sortFlags() { return QDir::LocaleAware | QDir::DirsFirst | QDir::Name | QDir::IgnoreCase; } private slots: @@ -105,16 +106,19 @@ private slots: void saveDirectoriesStates(); void restoreDirectoriesStates(); - void buildSearchTree(); + void foundSearchMatch(FileSearch* search, const QString& match); + void searchCompleted(FileSearch* search); void onSearch(const QString& filter); - void toggleSearch(bool on); + void displaySearch(bool on); + + void addContentCheckBox(); FileBrowserTreeWidget * m_fileBrowserTreeWidget; FileBrowserTreeWidget * m_searchTreeWidget; QLineEdit * m_filterEdit; - std::shared_ptr m_currentSearch; + std::shared_ptr m_currentSearch; QProgressBar* m_searchIndicator = nullptr; QString m_directories; //!< Directories to search, split with '*' @@ -122,9 +126,13 @@ private slots: bool m_dirsAsItems; - void addContentCheckBox(); QCheckBox* m_showUserContent = nullptr; QCheckBox* m_showFactoryContent = nullptr; + QCheckBox* m_showHiddenContent = nullptr; + + QBoxLayout *filterWidgetLayout = nullptr; + QBoxLayout *hiddenWidgetLayout = nullptr; + QBoxLayout *outerLayout = nullptr; QString m_userDir; QString m_factoryDir; QList m_savedExpandedDirs; diff --git a/include/FileBrowserSearcher.h b/include/FileBrowserSearcher.h deleted file mode 100644 index 4f4d3ff1cba..00000000000 --- a/include/FileBrowserSearcher.h +++ /dev/null @@ -1,148 +0,0 @@ -/* - * FileBrowserSearcher.h - Batch processor for searching the filesystem - * - * Copyright (c) 2023 saker - * - * This file is part of LMMS - https://lmms.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program (see COPYING); if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - */ - -#ifndef LMMS_FILE_BROWSER_SEARCHER_H -#define LMMS_FILE_BROWSER_SEARCHER_H - -#include -#include -#include -#include -#include - -#ifdef __MINGW32__ -#include -#include -#include -#else -#include -#include -#include -#endif - -namespace lmms::gui { - -//! An active object that handles searching for files that match a certain filter across the file system. -class FileBrowserSearcher -{ -public: - //! Number of milliseconds to wait for before a match should be processed by the user. - static constexpr int MillisecondsPerMatch = 1; - - //! The future object for FileBrowserSearcher. It is used to track the current state of search operations, as - // well as retrieve matches. - class SearchFuture - { - public: - //! Possible state values of the future object. - enum class State - { - Idle, - Running, - Cancelled, - Completed - }; - - //! Constructs a future object using the specified filter, paths, and valid file extensions in the Idle state. - SearchFuture(const QString& filter, const QStringList& paths, const QStringList& extensions) - : m_filter(filter) - , m_paths(paths) - , m_extensions(extensions) - { - } - - //! Retrieves a match from the match list. - auto match() -> QString - { - const auto lock = std::lock_guard{m_matchesMutex}; - return m_matches.empty() ? QString{} : m_matches.takeFirst(); - } - - //! Returns the current state of this future object. - auto state() -> State { return m_state; } - - //! Returns the filter used. - auto filter() -> const QString& { return m_filter; } - - //! Returns the paths to filter. - auto paths() -> const QStringList& { return m_paths; } - - //! Returns the valid file extensions. - auto extensions() -> const QStringList& { return m_extensions; } - - private: - //! Adds a match to the match list. - auto addMatch(const QString& match) -> void - { - const auto lock = std::lock_guard{m_matchesMutex}; - m_matches.append(match); - } - - QString m_filter; - QStringList m_paths; - QStringList m_extensions; - - QStringList m_matches; - std::mutex m_matchesMutex; - - std::atomic m_state = State::Idle; - - friend FileBrowserSearcher; - }; - - ~FileBrowserSearcher(); - - //! Enqueues a search to be ran by the worker thread. - //! Returns a future that the caller can use to track state and results of the operation. - auto search(const QString& filter, const QStringList& paths, const QStringList& extensions) - -> std::shared_ptr; - - //! Sends a signal to cancel a running search. - auto cancel() -> void { m_cancelRunningSearch = true; } - - //! Returns the global instance of the searcher object. - static auto instance() -> FileBrowserSearcher* - { - static auto s_instance = FileBrowserSearcher{}; - return &s_instance; - } - -private: - //! Event loop for the worker thread. - auto run() -> void; - - //! Using Depth-first search (DFS), filters the specified path and adds any matches to the future list. - auto process(SearchFuture* searchFuture, const QString& path) -> bool; - - std::queue> m_searchQueue; - std::atomic m_cancelRunningSearch = false; - - bool m_workerStopped = false; - std::mutex m_workerMutex; - std::condition_variable m_workerCond; - std::thread m_worker{[this] { run(); }}; -}; -} // namespace lmms::gui - -#endif // LMMS_FILE_BROWSER_SEARCHER_H diff --git a/include/FileSearch.h b/include/FileSearch.h new file mode 100644 index 00000000000..fd311c9031e --- /dev/null +++ b/include/FileSearch.h @@ -0,0 +1,73 @@ +/* + * FileSearch.h - File system search task + * + * Copyright (c) 2024 saker + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_FILE_SEARCH_H +#define LMMS_FILE_SEARCH_H + +#include +#include +#include + +namespace lmms { +//! A Qt object that encapsulates the operation of searching the file system. +class FileSearch : public QObject +{ + Q_OBJECT +public: + //! Number of milliseconds the search waits before signaling a matching result. + static constexpr int MillisecondsBetweenResults = 1; + + //! Create a `FileSearch` object that uses the specified string filter `filter` and extension filters in + //! `extensions` to search within the given `paths`. + //! `excludedPaths`, `dirFilters`, and `sortFlags` can optionally be specified to exclude certain directories, filter + //! out certain types of entries, and sort the matches. + FileSearch(const QString& filter, const QStringList& paths, const QStringList& extensions, + const QStringList& excludedPaths = {}, QDir::Filters dirFilters = QDir::Filters{}, + QDir::SortFlags sortFlags = QDir::SortFlags{}); + + //! Execute the search, emitting the `foundResult` signal when matches are found. + void operator()(); + + //! Cancel the search. + void cancel(); + +signals: + //! Emitted when a result is found when searching the file system. + void foundMatch(FileSearch* search, const QString& match); + + //! Emitted when the search completes. + void searchCompleted(FileSearch* search); + +private: + static auto isPathExcluded(const QString& path) -> bool; + QString m_filter; + QStringList m_paths; + QStringList m_extensions; + QStringList m_excludedPaths; + QDir::Filters m_dirFilters; + QDir::SortFlags m_sortFlags; + std::atomic m_cancel = false; +}; +} // namespace lmms +#endif // LMMS_FILE_SEARCH_H diff --git a/include/Instrument.h b/include/Instrument.h index 243bdba61f6..3f701f12ef5 100644 --- a/include/Instrument.h +++ b/include/Instrument.h @@ -34,6 +34,9 @@ #include "Plugin.h" #include "TimePos.h" +#include + + namespace lmms { @@ -42,6 +45,7 @@ class InstrumentTrack; class MidiEvent; class NotePlayHandle; class Track; +class SampleFrame; class LMMS_EXPORT Instrument : public Plugin @@ -59,7 +63,8 @@ class LMMS_EXPORT Instrument : public Plugin Instrument(InstrumentTrack * _instrument_track, const Descriptor * _descriptor, - const Descriptor::SubPluginFeatures::Key * key = nullptr); + const Descriptor::SubPluginFeatures::Key * key = nullptr, + Flags flags = Flag::NoFlags); ~Instrument() override = default; // -------------------------------------------------------------------- @@ -71,11 +76,11 @@ class LMMS_EXPORT Instrument : public Plugin // if the plugin doesn't play each note, it can create an instrument- // play-handle and re-implement this method, so that it mixes its // output buffer only once per audio engine period - virtual void play( sampleFrame * _working_buffer ); + virtual void play( SampleFrame* _working_buffer ); // to be implemented by actual plugin virtual void playNote( NotePlayHandle * /* _note_to_play */, - sampleFrame * /* _working_buf */ ) + SampleFrame* /* _working_buf */ ) { } @@ -91,18 +96,39 @@ class LMMS_EXPORT Instrument : public Plugin virtual f_cnt_t beatLen( NotePlayHandle * _n ) const; - // some instruments need a certain number of release-frames even - // if no envelope is active - such instruments can re-implement this - // method for returning how many frames they at least like to have for - // release - virtual f_cnt_t desiredReleaseFrames() const + // This method can be overridden by instruments that need a certain + // release time even if no envelope is active. It returns the time + // in milliseconds that these instruments would like to have for + // their release stage. + virtual float desiredReleaseTimeMs() const + { + return 0.f; + } + + // Converts the desired release time in milliseconds to the corresponding + // number of frames depending on the sample rate. + f_cnt_t desiredReleaseFrames() const { - return 0; + const sample_rate_t sampleRate = getSampleRate(); + + return static_cast(std::ceil(desiredReleaseTimeMs() * sampleRate / 1000.f)); } - virtual Flags flags() const + sample_rate_t getSampleRate() const; + + bool isSingleStreamed() const { - return Flag::NoFlags; + return m_flags.testFlag(Instrument::Flag::IsSingleStreamed); + } + + bool isMidiBased() const + { + return m_flags.testFlag(Instrument::Flag::IsMidiBased); + } + + bool isBendable() const + { + return !m_flags.testFlag(Instrument::Flag::IsNotBendable); } // sub-classes can re-implement this for receiving all incoming @@ -135,18 +161,20 @@ class LMMS_EXPORT Instrument : public Plugin protected: // fade in to prevent clicks - void applyFadeIn(sampleFrame * buf, NotePlayHandle * n); + void applyFadeIn(SampleFrame* buf, NotePlayHandle * n); // instruments may use this to apply a soft fade out at the end of // notes - method does this only if really less or equal // desiredReleaseFrames() frames are left - void applyRelease( sampleFrame * buf, const NotePlayHandle * _n ); + void applyRelease( SampleFrame* buf, const NotePlayHandle * _n ); + + float computeReleaseTimeMsByFrameCount(f_cnt_t frames) const; private: InstrumentTrack * m_instrumentTrack; - -} ; + Flags m_flags; +}; LMMS_DECLARE_OPERATORS_FOR_FLAGS(Instrument::Flag) diff --git a/include/InstrumentFunctions.h b/include/InstrumentFunctions.h index 59c651a68c4..aa2e6de6862 100644 --- a/include/InstrumentFunctions.h +++ b/include/InstrumentFunctions.h @@ -25,11 +25,12 @@ #ifndef LMMS_INSTRUMENT_FUNCTIONS_H #define LMMS_INSTRUMENT_FUNCTIONS_H -#include "JournallingObject.h" -#include "lmms_basics.h" +#include + #include "AutomatableModel.h" -#include "TempoSyncKnobModel.h" #include "ComboBoxModel.h" +#include "JournallingObject.h" +#include "TempoSyncKnobModel.h" namespace lmms { diff --git a/include/InstrumentPlayHandle.h b/include/InstrumentPlayHandle.h index dc744b4ffdb..c3d9c13bca2 100644 --- a/include/InstrumentPlayHandle.h +++ b/include/InstrumentPlayHandle.h @@ -41,7 +41,7 @@ class LMMS_EXPORT InstrumentPlayHandle : public PlayHandle ~InstrumentPlayHandle() override = default; - void play(sampleFrame * working_buffer) override; + void play(SampleFrame* working_buffer) override; bool isFinished() const override { diff --git a/include/InstrumentSoundShaping.h b/include/InstrumentSoundShaping.h index fb5f1e8bd90..7dfeaf58bf3 100644 --- a/include/InstrumentSoundShaping.h +++ b/include/InstrumentSoundShaping.h @@ -34,6 +34,7 @@ namespace lmms class InstrumentTrack; class EnvelopeAndLfoParameters; class NotePlayHandle; +class SampleFrame; namespace gui { @@ -48,7 +49,7 @@ class InstrumentSoundShaping : public Model, public JournallingObject InstrumentSoundShaping( InstrumentTrack * _instrument_track ); ~InstrumentSoundShaping() override = default; - void processAudioBuffer( sampleFrame * _ab, const fpp_t _frames, + void processAudioBuffer( SampleFrame* _ab, const fpp_t _frames, NotePlayHandle * _n ); enum class Target diff --git a/include/InstrumentSoundShapingView.h b/include/InstrumentSoundShapingView.h index 8f671514a89..c9caea28c37 100644 --- a/include/InstrumentSoundShapingView.h +++ b/include/InstrumentSoundShapingView.h @@ -56,7 +56,7 @@ class InstrumentSoundShapingView : public QWidget, public ModelView void modelChanged() override; - InstrumentSoundShaping * m_ss; + InstrumentSoundShaping * m_ss = nullptr; TabWidget * m_targetsTabWidget; EnvelopeAndLfoView * m_envLfoViews[InstrumentSoundShaping::NumTargets]; diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 3d84df5979a..1e46fb0cb8c 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -26,6 +26,8 @@ #ifndef LMMS_INSTRUMENT_TRACK_H #define LMMS_INSTRUMENT_TRACK_H +#include + #include "AudioPort.h" #include "InstrumentFunctions.h" #include "InstrumentSoundShaping.h" @@ -66,7 +68,7 @@ class LMMS_EXPORT InstrumentTrack : public Track, public MidiEventProcessor ~InstrumentTrack() override; // used by instrument - void processAudioBuffer( sampleFrame * _buf, const fpp_t _frames, + void processAudioBuffer( SampleFrame* _buf, const fpp_t _frames, NotePlayHandle * _n ); MidiEvent applyMasterKey( const MidiEvent& event ); @@ -86,7 +88,7 @@ class LMMS_EXPORT InstrumentTrack : public Track, public MidiEventProcessor // for capturing note-play-events -> need that for arpeggio, // filter and so on - void playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ); + void playNote( NotePlayHandle * _n, SampleFrame* _working_buffer ); QString instrumentName() const; const Instrument *instrument() const diff --git a/include/InstrumentTrackView.h b/include/InstrumentTrackView.h index cfde89bde82..c7d524b36cf 100644 --- a/include/InstrumentTrackView.h +++ b/include/InstrumentTrackView.h @@ -71,8 +71,6 @@ class InstrumentTrackView : public TrackView // Create a menu for assigning/creating channels for this track QMenu * createMixerMenu( QString title, QString newMixerLabel ) override; - QPixmap determinePixmap(); - protected: void modelChanged() override; diff --git a/include/IoHelper.h b/include/IoHelper.h index 40c576b8354..3c453fa58ac 100644 --- a/include/IoHelper.h +++ b/include/IoHelper.h @@ -75,13 +75,12 @@ inline FILE* F_OPEN_UTF8(std::string const& fname, const char* mode){ inline int fileToDescriptor(FILE* f, bool closeFile = true) { - int fh; if (f == nullptr) {return -1;} #ifdef LMMS_BUILD_WIN32 - fh = _dup(_fileno(f)); + int fh = _dup(_fileno(f)); #else - fh = dup(fileno(f)); + int fh = dup(fileno(f)); #endif if (closeFile) {fclose(f);} diff --git a/include/LfoGraph.h b/include/LfoGraph.h new file mode 100644 index 00000000000..9d566770f09 --- /dev/null +++ b/include/LfoGraph.h @@ -0,0 +1,65 @@ +/* + * LfoGraph.h - Displays LFO graphs + * + * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2024- Michael Gregorius + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_GUI_LFO_GRAPH_H +#define LMMS_GUI_LFO_GRAPH_H + +#include + +#include "ModelView.h" +#include "embed.h" + +namespace lmms +{ + +class EnvelopeAndLfoParameters; + +namespace gui +{ + +class LfoGraph : public QWidget, public ModelView +{ +public: + LfoGraph(QWidget* parent); + +protected: + void mousePressEvent(QMouseEvent* me) override; + void paintEvent(QPaintEvent* pe) override; + +private: + void drawInfoText(const EnvelopeAndLfoParameters&); + void toggleAmountModel(); + +private: + QPixmap m_lfoGraph = embed::getIconPixmap("lfo_graph"); + + float m_randomGraph {0.}; +}; + +} // namespace gui + +} // namespace lmms + +#endif // LMMS_GUI_LFO_GRAPH_H diff --git a/include/LocaleHelper.h b/include/LocaleHelper.h index 9c829fcaa61..c1e1b473566 100644 --- a/include/LocaleHelper.h +++ b/include/LocaleHelper.h @@ -39,10 +39,9 @@ namespace lmms::LocaleHelper inline double toDouble(QString str, bool* ok = nullptr) { bool isOkay; - double value; QLocale c(QLocale::C); c.setNumberOptions(QLocale::RejectGroupSeparator); - value = c.toDouble(str, &isOkay); + double value = c.toDouble(str, &isOkay); if (!isOkay) { QLocale german(QLocale::German); diff --git a/include/LocklessAllocator.h b/include/LocklessAllocator.h index d44b9954374..1652ac71d66 100644 --- a/include/LocklessAllocator.h +++ b/include/LocklessAllocator.h @@ -50,8 +50,8 @@ class LocklessAllocator std::atomic_int * m_freeState; size_t m_freeStateSets; - std::atomic_int m_available; - std::atomic_int m_startIndex; + std::atomic_size_t m_available; + std::atomic_size_t m_startIndex; } ; diff --git a/include/LocklessRingBuffer.h b/include/LocklessRingBuffer.h index 2d65badfe58..ec3f4fc52de 100644 --- a/include/LocklessRingBuffer.h +++ b/include/LocklessRingBuffer.h @@ -28,9 +28,9 @@ #include #include -#include "lmms_basics.h" -#include "../src/3rdparty/ringbuffer/include/ringbuffer/ringbuffer.h" +#include +#include "lmms_basics.h" namespace lmms { diff --git a/include/Lv2Basics.h b/include/Lv2Basics.h index 53489e30d10..5b286586875 100644 --- a/include/Lv2Basics.h +++ b/include/Lv2Basics.h @@ -1,7 +1,7 @@ /* * Lv2Basics.h - basic Lv2 utils * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -37,6 +37,12 @@ namespace lmms { +template +struct LilvPtrDeleter +{ + void operator()(T* n) { lilv_free(static_cast(n)); } +}; + struct LilvNodeDeleter { void operator()(LilvNode* n) { lilv_node_free(n); } @@ -52,6 +58,8 @@ struct LilvScalePointsDeleter void operator()(LilvScalePoints* s) { lilv_scale_points_free(s); } }; +template +using AutoLilvPtr = std::unique_ptr>; using AutoLilvNode = std::unique_ptr; using AutoLilvNodes = std::unique_ptr; using AutoLilvScalePoints = std::unique_ptr; diff --git a/include/Lv2ControlBase.h b/include/Lv2ControlBase.h index 9bfb40f8798..8ee235ad898 100644 --- a/include/Lv2ControlBase.h +++ b/include/Lv2ControlBase.h @@ -43,6 +43,7 @@ namespace lmms class Lv2Proc; class PluginIssue; +class SampleFrame; /** Common base class for Lv2 plugins @@ -118,9 +119,9 @@ class LMMS_EXPORT Lv2ControlBase : public LinkedModelGroups void copyModelsToLmms() const; //! Copy buffer passed by LMMS into our ports - void copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames); + void copyBuffersFromLmms(const SampleFrame* buf, fpp_t frames); //! Copy our ports into buffers passed by LMMS - void copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const; + void copyBuffersToLmms(SampleFrame* buf, fpp_t frames) const; //! Run the Lv2 plugin instance for @param frames frames void run(fpp_t frames); diff --git a/include/Lv2Manager.h b/include/Lv2Manager.h index 58126a0a448..8a9f5168448 100644 --- a/include/Lv2Manager.h +++ b/include/Lv2Manager.h @@ -1,7 +1,7 @@ /* * Lv2Manager.h - Implementation of Lv2Manager class * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -131,14 +131,23 @@ class Lv2Manager AutoLilvNodes findNodes(const LilvNode *subject, const LilvNode *predicate, const LilvNode *object); - static const std::set& getPluginBlacklist() + static bool pluginIsUnstable(const char* pluginUri) { - return pluginBlacklist; + return unstablePlugins.find(pluginUri) != unstablePlugins.end(); } - static const std::set& getPluginBlacklistBuffersizeLessThan32() + static bool pluginIsOnlyUsefulWithUi(const char* pluginUri) { - return pluginBlacklistBuffersizeLessThan32; + return pluginsOnlyUsefulWithUi.find(pluginUri) != pluginsOnlyUsefulWithUi.end(); } + static bool pluginIsUnstableWithBuffersizeLessEqual32(const char* pluginUri) + { + return unstablePluginsBuffersizeLessEqual32.find(pluginUri) != + unstablePluginsBuffersizeLessEqual32.end(); + } + + //! Whether the user generally wants a UI (and we generally support that) + //! Since we do not generally support UI right now, this will always return false... + static bool wantUi(); private: // general data @@ -154,8 +163,9 @@ class Lv2Manager Lv2UridCache m_uridCache; // static - static const std::set - pluginBlacklist, pluginBlacklistBuffersizeLessThan32; + static const std::set unstablePlugins; + static const std::set pluginsOnlyUsefulWithUi; + static const std::set unstablePluginsBuffersizeLessEqual32; // functions bool isSubclassOf(const LilvPluginClass *clvss, const char *uriStr); diff --git a/include/Lv2Options.h b/include/Lv2Options.h index 603cdda43ac..69d294cbe1f 100644 --- a/include/Lv2Options.h +++ b/include/Lv2Options.h @@ -30,8 +30,8 @@ #ifdef LMMS_HAVE_LV2 #include -#include -#include +#include +#include #include #include #include diff --git a/include/Lv2Ports.h b/include/Lv2Ports.h index e4c896ff3e7..1b2986938a1 100644 --- a/include/Lv2Ports.h +++ b/include/Lv2Ports.h @@ -41,6 +41,7 @@ namespace lmms { +class SampleFrame; struct ConnectPortVisitor; using LV2_Evbuf = struct LV2_Evbuf_Impl; @@ -184,15 +185,15 @@ struct Audio : public VisitablePort //! Copy buffer passed by LMMS into our ports //! @param channel channel index into each sample frame - void copyBuffersFromCore(const sampleFrame *lmmsBuf, + void copyBuffersFromCore(const SampleFrame* lmmsBuf, unsigned channel, fpp_t frames); //! Add buffer passed by LMMS into our ports, and halve the result //! @param channel channel index into each sample frame - void averageWithBuffersFromCore(const sampleFrame *lmmsBuf, + void averageWithBuffersFromCore(const SampleFrame* lmmsBuf, unsigned channel, fpp_t frames); //! Copy our ports into buffers passed by LMMS //! @param channel channel index into each sample frame - void copyBuffersToCore(sampleFrame *lmmsBuf, + void copyBuffersToCore(SampleFrame* lmmsBuf, unsigned channel, fpp_t frames) const; bool isSideChain() const { return m_sidechain; } diff --git a/include/Lv2Proc.h b/include/Lv2Proc.h index 65bd90698a9..f315c5d7a89 100644 --- a/include/Lv2Proc.h +++ b/include/Lv2Proc.h @@ -33,6 +33,8 @@ #include #include +#include + #include "LinkedModelGroups.h" #include "LmmsSemaphore.h" #include "Lv2Basics.h" @@ -40,7 +42,6 @@ #include "Lv2Options.h" #include "Lv2Worker.h" #include "Plugin.h" -#include "../src/3rdparty/ringbuffer/include/ringbuffer/ringbuffer.h" #include "TimePos.h" @@ -48,6 +49,7 @@ namespace lmms { class PluginIssue; +class SampleFrame; // forward declare port structs/enums namespace Lv2Ports @@ -133,7 +135,7 @@ class Lv2Proc : public LinkedModelGroup * @param num Number of channels we must read from @param buf (starting at * @p offset) */ - void copyBuffersFromCore(const sampleFrame *buf, + void copyBuffersFromCore(const SampleFrame* buf, unsigned firstChan, unsigned num, fpp_t frames); /** * Copy our ports into buffers passed by the core @@ -146,7 +148,7 @@ class Lv2Proc : public LinkedModelGroup * @param num Number of channels we must write to @param buf (starting at * @p offset) */ - void copyBuffersToCore(sampleFrame *buf, unsigned firstChan, unsigned num, + void copyBuffersToCore(SampleFrame* buf, unsigned firstChan, unsigned num, fpp_t frames) const; //! Run the Lv2 plugin instance for @param frames frames void run(fpp_t frames); diff --git a/include/Lv2UridMap.h b/include/Lv2UridMap.h index 6c22aca3e40..23f8f758a44 100644 --- a/include/Lv2UridMap.h +++ b/include/Lv2UridMap.h @@ -29,7 +29,7 @@ #ifdef LMMS_HAVE_LV2 -#include +#include #include // TODO: use semaphore, even though this is not realtime critical #include #include diff --git a/include/Lv2Worker.h b/include/Lv2Worker.h index 90a3d9d4f8d..b15bd9026c4 100644 --- a/include/Lv2Worker.h +++ b/include/Lv2Worker.h @@ -30,7 +30,7 @@ #ifdef LMMS_HAVE_LV2 #include -#include +#include #include #include diff --git a/include/MidiEvent.h b/include/MidiEvent.h index 9a14e427c44..453f6541098 100644 --- a/include/MidiEvent.h +++ b/include/MidiEvent.h @@ -190,6 +190,8 @@ class MidiEvent setParam( 0, pitchBend ); } + auto sysExData() const -> const char* { return m_sysExData; } + Source source() const { return m_source; @@ -212,7 +214,7 @@ class MidiEvent int32_t m_sysExDataLen; // len of m_sysExData } m_data; - [[maybe_unused]] const char* m_sysExData; + const char* m_sysExData; const void* m_sourcePort; // Stores the source of the MidiEvent: Internal or External (hardware controllers). diff --git a/include/MidiJack.h b/include/MidiJack.h index e2b38e290b3..e61db26dda7 100644 --- a/include/MidiJack.h +++ b/include/MidiJack.h @@ -32,7 +32,7 @@ #include #include #else -#include "weak_libjack.h" +#include #endif #include diff --git a/include/MixHelpers.h b/include/MixHelpers.h index dde17dd0248..a55ad605861 100644 --- a/include/MixHelpers.h +++ b/include/MixHelpers.h @@ -31,52 +31,54 @@ namespace lmms { class ValueBuffer; +class SampleFrame; + namespace MixHelpers { -bool isSilent( const sampleFrame* src, int frames ); +bool isSilent( const SampleFrame* src, int frames ); bool useNaNHandler(); void setNaNHandler( bool use ); -bool sanitize( sampleFrame * src, int frames ); +bool sanitize( SampleFrame* src, int frames ); /*! \brief Add samples from src to dst */ -void add( sampleFrame* dst, const sampleFrame* src, int frames ); +void add( SampleFrame* dst, const SampleFrame* src, int frames ); /*! \brief Multiply samples from `dst` by `coeff` */ -void multiply(sampleFrame* dst, float coeff, int frames); +void multiply(SampleFrame* dst, float coeff, int frames); /*! \brief Add samples from src multiplied by coeffSrc to dst */ -void addMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames ); +void addMultiplied( SampleFrame* dst, const SampleFrame* src, float coeffSrc, int frames ); /*! \brief Add samples from src multiplied by coeffSrc to dst, swap inputs */ -void addSwappedMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames ); +void addSwappedMultiplied( SampleFrame* dst, const SampleFrame* src, float coeffSrc, int frames ); /*! \brief Add samples from src multiplied by coeffSrc and coeffSrcBuf to dst */ -void addMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames ); +void addMultipliedByBuffer( SampleFrame* dst, const SampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames ); /*! \brief Add samples from src multiplied by coeffSrc and coeffSrcBuf to dst */ -void addMultipliedByBuffers( sampleFrame* dst, const sampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames ); +void addMultipliedByBuffers( SampleFrame* dst, const SampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames ); /*! \brief Same as addMultiplied, but sanitize output (strip out infs/nans) */ -void addSanitizedMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames ); +void addSanitizedMultiplied( SampleFrame* dst, const SampleFrame* src, float coeffSrc, int frames ); /*! \brief Add samples from src multiplied by coeffSrc and coeffSrcBuf to dst - sanitized version */ -void addSanitizedMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames ); +void addSanitizedMultipliedByBuffer( SampleFrame* dst, const SampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames ); /*! \brief Add samples from src multiplied by coeffSrc and coeffSrcBuf to dst - sanitized version */ -void addSanitizedMultipliedByBuffers( sampleFrame* dst, const sampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames ); +void addSanitizedMultipliedByBuffers( SampleFrame* dst, const SampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames ); /*! \brief Add samples from src multiplied by coeffSrcLeft/coeffSrcRight to dst */ -void addMultipliedStereo( sampleFrame* dst, const sampleFrame* src, float coeffSrcLeft, float coeffSrcRight, int frames ); +void addMultipliedStereo( SampleFrame* dst, const SampleFrame* src, float coeffSrcLeft, float coeffSrcRight, int frames ); /*! \brief Multiply dst by coeffDst and add samples from src multiplied by coeffSrc */ -void multiplyAndAddMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffDst, float coeffSrc, int frames ); +void multiplyAndAddMultiplied( SampleFrame* dst, const SampleFrame* src, float coeffDst, float coeffSrc, int frames ); /*! \brief Multiply dst by coeffDst and add samples from srcLeft/srcRight multiplied by coeffSrc */ -void multiplyAndAddMultipliedJoined( sampleFrame* dst, const sample_t* srcLeft, const sample_t* srcRight, float coeffDst, float coeffSrc, int frames ); +void multiplyAndAddMultipliedJoined( SampleFrame* dst, const sample_t* srcLeft, const sample_t* srcRight, float coeffDst, float coeffSrc, int frames ); } // namespace MixHelpers diff --git a/include/Mixer.h b/include/Mixer.h index 302492cab94..d74f9c11c9b 100644 --- a/include/Mixer.h +++ b/include/Mixer.h @@ -56,7 +56,7 @@ class MixerChannel : public ThreadableJob float m_peakLeft; float m_peakRight; - sampleFrame * m_buffer; + SampleFrame* m_buffer; bool m_muteBeforeSolo; BoolModel m_muteModel; BoolModel m_soloModel; @@ -79,7 +79,7 @@ class MixerChannel : public ThreadableJob auto color() const -> const std::optional& { return m_color; } void setColor(const std::optional& color) { m_color = color; } - std::atomic_int m_dependenciesMet; + std::atomic_size_t m_dependenciesMet; void incrementDeps(); void processed(); @@ -137,10 +137,10 @@ class LMMS_EXPORT Mixer : public Model, public JournallingObject Mixer(); ~Mixer() override; - void mixToChannel( const sampleFrame * _buf, mix_ch_t _ch ); + void mixToChannel( const SampleFrame* _buf, mix_ch_t _ch ); void prepareMasterMix(); - void masterMix( sampleFrame * _buf ); + void masterMix( SampleFrame* _buf ); void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; void loadSettings( const QDomElement & _this ) override; diff --git a/include/MixerChannelLcdSpinBox.h b/include/MixerChannelLcdSpinBox.h index 0abd9f100f1..251cf7da259 100644 --- a/include/MixerChannelLcdSpinBox.h +++ b/include/MixerChannelLcdSpinBox.h @@ -50,6 +50,8 @@ class MixerChannelLcdSpinBox : public LcdSpinBox void contextMenuEvent(QContextMenuEvent* event) override; private: + void enterValue(); + TrackView * m_tv; }; diff --git a/include/MixerChannelView.h b/include/MixerChannelView.h index 1710623d780..7ccb8a24f23 100644 --- a/include/MixerChannelView.h +++ b/include/MixerChannelView.h @@ -45,6 +45,8 @@ namespace lmms namespace lmms::gui { + class PeakIndicator; + constexpr int MIXER_CHANNEL_INNER_BORDER_SIZE = 3; constexpr int MIXER_CHANNEL_OUTER_BORDER_SIZE = 1; @@ -90,6 +92,8 @@ namespace lmms::gui QColor strokeInnerInactive() const; void setStrokeInnerInactive(const QColor& c); + void reset(); + public slots: void renameChannel(); void resetColor(); @@ -104,6 +108,7 @@ namespace lmms::gui void moveChannelRight(); private: + bool confirmRemoval(int index); QString elideName(const QString& name); MixerChannel* mixerChannel() const; auto isMasterChannel() const -> bool { return m_channelIndex == 0; } @@ -118,6 +123,7 @@ namespace lmms::gui QLabel* m_receiveArrow; PixmapButton* m_muteButton; PixmapButton* m_soloButton; + PeakIndicator* m_peakIndicator = nullptr; Fader* m_fader; EffectRackView* m_effectRackView; MixerView* m_mixerView; diff --git a/include/MixerView.h b/include/MixerView.h index 81287bc547f..89315f93ab7 100644 --- a/include/MixerView.h +++ b/include/MixerView.h @@ -78,7 +78,6 @@ class LMMS_EXPORT MixerView : public QWidget, public ModelView, // notify the view that a mixer channel was deleted void deleteChannel(int index); - bool confirmRemoval(int index); // delete all unused channels void deleteUnusedChannels(); diff --git a/include/NotePlayHandle.h b/include/NotePlayHandle.h index f70268132f8..0c0acf6940c 100644 --- a/include/NotePlayHandle.h +++ b/include/NotePlayHandle.h @@ -110,7 +110,7 @@ class LMMS_EXPORT NotePlayHandle : public PlayHandle, public Note float currentDetuning() const { return m_baseDetuning->value(); } /*! Renders one chunk using the attached instrument into the buffer */ - void play( sampleFrame* buffer ) override; + void play( SampleFrame* buffer ) override; /*! Returns whether playback of note is finished and thus handle can be deleted */ bool isFinished() const override diff --git a/include/Oscillator.h b/include/Oscillator.h index a480bf52424..0a5e166dc9e 100644 --- a/include/Oscillator.h +++ b/include/Oscillator.h @@ -109,7 +109,7 @@ class LMMS_EXPORT Oscillator m_userAntiAliasWaveTable = waveform; } - void update(sampleFrame* ab, const fpp_t frames, const ch_cnt_t chnl, bool modulator = false); + void update(SampleFrame* ab, const fpp_t frames, const ch_cnt_t chnl, bool modulator = false); // now follow the wave-shape-routines... static inline sample_t sinSample( const float _sample ) @@ -163,23 +163,15 @@ class LMMS_EXPORT Oscillator static inline sample_t noiseSample( const float ) { - // Precise implementation -// return 1.0f - rand() * 2.0f / RAND_MAX; - - // Fast implementation - return 1.0f - fast_rand() * 2.0f / FAST_RAND_MAX; + return 1.0f - rand() * 2.0f / RAND_MAX; } static sample_t userWaveSample(const SampleBuffer* buffer, const float sample) { if (buffer == nullptr || buffer->size() == 0) { return 0; } const auto frames = buffer->size(); - const auto frame = sample * frames; - auto f1 = static_cast(frame) % frames; - if (f1 < 0) - { - f1 += frames; - } + const auto frame = absFraction(sample) * frames; + const auto f1 = static_cast(frame); return linearInterpolate(buffer->data()[f1][0], buffer->data()[(f1 + 1) % frames][0], fraction(frame)); } @@ -194,17 +186,13 @@ class LMMS_EXPORT Oscillator inline wtSampleControl getWtSampleControl(const float sample) const { wtSampleControl control; - control.frame = sample * OscillatorConstants::WAVETABLE_LENGTH; - control.f1 = static_cast(control.frame) % OscillatorConstants::WAVETABLE_LENGTH; - if (control.f1 < 0) - { - control.f1 += OscillatorConstants::WAVETABLE_LENGTH; - } + control.frame = absFraction(sample) * OscillatorConstants::WAVETABLE_LENGTH; + control.f1 = static_cast(control.frame); control.f2 = control.f1 < OscillatorConstants::WAVETABLE_LENGTH - 1 ? control.f1 + 1 : 0; control.band = waveTableBandFromFreq( - m_freq * m_detuning_div_samplerate * Engine::audioEngine()->processingSampleRate()); + m_freq * m_detuning_div_samplerate * Engine::audioEngine()->outputSampleRate()); return control; } @@ -286,40 +274,40 @@ class LMMS_EXPORT Oscillator /* End Multiband wavetable */ - void updateNoSub( sampleFrame * _ab, const fpp_t _frames, + void updateNoSub( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ); - void updatePM( sampleFrame * _ab, const fpp_t _frames, + void updatePM( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ); - void updateAM( sampleFrame * _ab, const fpp_t _frames, + void updateAM( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ); - void updateMix( sampleFrame * _ab, const fpp_t _frames, + void updateMix( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ); - void updateSync( sampleFrame * _ab, const fpp_t _frames, + void updateSync( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ); - void updateFM( sampleFrame * _ab, const fpp_t _frames, + void updateFM( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ); - float syncInit( sampleFrame * _ab, const fpp_t _frames, + float syncInit( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ); inline bool syncOk( float _osc_coeff ); template - void updateNoSub( sampleFrame * _ab, const fpp_t _frames, + void updateNoSub( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ); template - void updatePM( sampleFrame * _ab, const fpp_t _frames, + void updatePM( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ); template - void updateAM( sampleFrame * _ab, const fpp_t _frames, + void updateAM( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ); template - void updateMix( sampleFrame * _ab, const fpp_t _frames, + void updateMix( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ); template - void updateSync( sampleFrame * _ab, const fpp_t _frames, + void updateSync( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ); template - void updateFM( sampleFrame * _ab, const fpp_t _frames, + void updateFM( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ); template diff --git a/include/Oscilloscope.h b/include/Oscilloscope.h index 209370ce034..1e3c52b1eed 100644 --- a/include/Oscilloscope.h +++ b/include/Oscilloscope.h @@ -30,6 +30,13 @@ #include "lmms_basics.h" +namespace lmms +{ + +class SampleFrame; + +} + namespace lmms::gui { @@ -38,7 +45,9 @@ class Oscilloscope : public QWidget { Q_OBJECT public: - Q_PROPERTY( QColor normalColor READ normalColor WRITE setNormalColor ) + Q_PROPERTY( QColor leftChannelColor READ leftChannelColor WRITE setLeftChannelColor ) + Q_PROPERTY( QColor rightChannelColor READ rightChannelColor WRITE setRightChannelColor ) + Q_PROPERTY( QColor otherChannelsColor READ otherChannelsColor WRITE setOtherChannelsColor ) Q_PROPERTY( QColor clippingColor READ clippingColor WRITE setClippingColor ) Oscilloscope( QWidget * _parent ); @@ -46,8 +55,14 @@ class Oscilloscope : public QWidget void setActive( bool _active ); - QColor const & normalColor() const; - void setNormalColor(QColor const & normalColor); + QColor const & leftChannelColor() const; + void setLeftChannelColor(QColor const & leftChannelColor); + + QColor const & rightChannelColor() const; + void setRightChannelColor(QColor const & rightChannelColor); + + QColor const & otherChannelsColor() const; + void setOtherChannelsColor(QColor const & otherChannelsColor); QColor const & clippingColor() const; void setClippingColor(QColor const & clippingColor); @@ -59,19 +74,21 @@ class Oscilloscope : public QWidget protected slots: - void updateAudioBuffer( const lmms::surroundSampleFrame * buffer ); + void updateAudioBuffer(const lmms::SampleFrame* buffer); private: - QColor const & determineLineColor(float level) const; + bool clips(float level) const; private: QPixmap m_background; QPointF * m_points; - sampleFrame * m_buffer; + SampleFrame* m_buffer; bool m_active; - QColor m_normalColor; + QColor m_leftChannelColor; + QColor m_rightChannelColor; + QColor m_otherChannelsColor; QColor m_clippingColor; } ; diff --git a/include/MemoryHelper.h b/include/PeakIndicator.h similarity index 59% rename from include/MemoryHelper.h rename to include/PeakIndicator.h index e709ffc8aad..2bc91229274 100644 --- a/include/MemoryHelper.h +++ b/include/PeakIndicator.h @@ -1,7 +1,8 @@ /* - * Copyright (c) 2014 Simon Symeonidis - * Copyright (c) 2004-2014 Tobias Doerffel + * PeakIndicator.h - Peak indicator widget * + * Copyright (c) 2024- Michael Gregorius + * * This file is part of LMMS - https://lmms.io * * This program is free software; you can redistribute it and/or @@ -21,29 +22,39 @@ * */ -#ifndef LMMS_MEMORY_HELPER_H -#define LMMS_MEMORY_HELPER_H -#include +#ifndef LMMS_GUI_PEAKINDICATOR_H +#define LMMS_GUI_PEAKINDICATOR_H -namespace lmms -{ +#include "lmms_export.h" +#include -/** - * Helper class to alocate aligned memory and free it. - */ -class MemoryHelper { + +namespace lmms::gui +{ + +class LMMS_EXPORT PeakIndicator : public QLabel +{ + Q_OBJECT public: + PeakIndicator(QWidget* parent); - static void* alignedMalloc( size_t ); + void resetPeakToMinusInf(); - static void alignedFree( void* ); +public slots: + void updatePeak(float peak); + +protected: + void mousePressEvent(QMouseEvent* e) override; private: -}; + void updatePeakDisplay(); +private: + float m_peak; +} ; -} // namespace lmms +} // namespace lmms::gui -#endif // LMMS_MEMORY_HELPER_H +#endif // LMMS_GUI_PEAKINDICATOR_H diff --git a/include/Piano.h b/include/Piano.h index 698d9c8febc..08dfda4ddbc 100644 --- a/include/Piano.h +++ b/include/Piano.h @@ -25,8 +25,10 @@ #ifndef LMMS_PIANO_H #define LMMS_PIANO_H -#include "Note.h" +#include + #include "Model.h" +#include "Note.h" namespace lmms { diff --git a/include/PianoRoll.h b/include/PianoRoll.h index 881732be1e3..35550a5b32f 100644 --- a/include/PianoRoll.h +++ b/include/PianoRoll.h @@ -376,6 +376,7 @@ protected slots: TimePos m_currentPosition; bool m_recording; + bool m_doAutoQuantization{false}; QList m_recordingNotes; Note * m_currentNote; diff --git a/include/PlayHandle.h b/include/PlayHandle.h index 8f5d771ed7b..f2e87136ad0 100644 --- a/include/PlayHandle.h +++ b/include/PlayHandle.h @@ -41,6 +41,7 @@ namespace lmms class Track; class AudioPort; +class SampleFrame; class LMMS_EXPORT PlayHandle : public ThreadableJob { @@ -105,7 +106,7 @@ class LMMS_EXPORT PlayHandle : public ThreadableJob { return m_processingLock.tryLock(); } - virtual void play( sampleFrame* buffer ) = 0; + virtual void play( SampleFrame* buffer ) = 0; virtual bool isFinished() const = 0; // returns the frameoffset at the start of the playhandle, @@ -145,14 +146,14 @@ class LMMS_EXPORT PlayHandle : public ThreadableJob void releaseBuffer(); - sampleFrame * buffer(); + SampleFrame* buffer(); private: Type m_type; f_cnt_t m_offset; QThread* m_affinity; QMutex m_processingLock; - sampleFrame* m_playHandleBuffer; + SampleFrame* m_playHandleBuffer; bool m_bufferReleased; bool m_usesBuffer; AudioPort * m_audioPort; diff --git a/include/PluginIssue.h b/include/PluginIssue.h index 01a4268ecc9..b0b9b194612 100644 --- a/include/PluginIssue.h +++ b/include/PluginIssue.h @@ -57,7 +57,7 @@ enum class PluginIssueType FeatureNotSupported, //!< plugin requires functionality LMMS can't offer // misc BadPortType, //!< port type not supported - Blacklisted, + Blocked, NoIssue }; diff --git a/include/PresetPreviewPlayHandle.h b/include/PresetPreviewPlayHandle.h index b8199a71bd5..e464cf61590 100644 --- a/include/PresetPreviewPlayHandle.h +++ b/include/PresetPreviewPlayHandle.h @@ -47,7 +47,7 @@ class LMMS_EXPORT PresetPreviewPlayHandle : public PlayHandle return true; } - void play( sampleFrame* buffer ) override; + void play( SampleFrame* buffer ) override; bool isFinished() const override; bool isFromTrack( const Track * _track ) const override; diff --git a/include/RemotePlugin.h b/include/RemotePlugin.h index c5fcd7dd2c3..a2e2e08d62b 100644 --- a/include/RemotePlugin.h +++ b/include/RemotePlugin.h @@ -37,6 +37,7 @@ namespace lmms class RemotePlugin; +class SampleFrame; class ProcessWatcher : public QThread { @@ -96,7 +97,7 @@ class LMMS_EXPORT RemotePlugin : public QObject, public RemotePluginBase bool processMessage( const message & _m ) override; - bool process( const sampleFrame * _in_buf, sampleFrame * _out_buf ); + bool process( const SampleFrame* _in_buf, SampleFrame* _out_buf ); void processMidiEvent( const MidiEvent&, const f_cnt_t _offset ); @@ -188,13 +189,11 @@ private slots: void processErrored(QProcess::ProcessError err ); } ; - -LMMS_EXPORT inline std::string QSTR_TO_STDSTR(QString const& qstr) +inline std::string QSTR_TO_STDSTR(QString const& qstr) { return qstr.toStdString(); } - } // namespace lmms #endif // LMMS_REMOTE_PLUGIN_H diff --git a/include/RemotePluginBase.h b/include/RemotePluginBase.h index 357be1bea25..5214b6f9244 100644 --- a/include/RemotePluginBase.h +++ b/include/RemotePluginBase.h @@ -41,10 +41,6 @@ #ifdef LMMS_HAVE_PROCESS_H #include #endif - -#include -#include -#include #else // !(LMMS_HAVE_SYS_IPC_H && LMMS_HAVE_SEMAPHORE_H) #ifdef LMMS_HAVE_UNISTD_H #include @@ -75,6 +71,7 @@ #include #include #include +#include #ifndef SYNC_WITH_SHM_FIFO #include @@ -85,6 +82,7 @@ #ifdef SYNC_WITH_SHM_FIFO #include "SharedMemory.h" +#include "SystemSemaphore.h" #endif namespace lmms @@ -120,12 +118,11 @@ class shmFifo } ; public: +#ifndef BUILD_REMOTE_PLUGIN_CLIENT // constructor for master-side shmFifo() : m_invalid( false ), m_master( true ), - m_dataSem( QString() ), - m_messageSem( QString() ), m_lockDepth( 0 ) { m_data.create(QUuid::createUuid().toString().toStdString()); @@ -133,26 +130,21 @@ class shmFifo static int k = 0; m_data->dataSem.semKey = ( getpid()<<10 ) + ++k; m_data->messageSem.semKey = ( getpid()<<10 ) + ++k; - m_dataSem.setKey( QString::number( m_data->dataSem.semKey ), - 1, QSystemSemaphore::Create ); - m_messageSem.setKey( QString::number( - m_data->messageSem.semKey ), - 0, QSystemSemaphore::Create ); + m_dataSem = SystemSemaphore{std::to_string(m_data->dataSem.semKey), 1u}; + m_messageSem = SystemSemaphore{std::to_string(m_data->messageSem.semKey), 0u}; } +#endif // constructor for remote-/client-side - use _shm_key for making up // the connection to master shmFifo(const std::string& shmKey) : m_invalid( false ), m_master( false ), - m_dataSem( QString() ), - m_messageSem( QString() ), m_lockDepth( 0 ) { m_data.attach(shmKey); - m_dataSem.setKey( QString::number( m_data->dataSem.semKey ) ); - m_messageSem.setKey( QString::number( - m_data->messageSem.semKey ) ); + m_dataSem = SystemSemaphore{std::to_string(m_data->dataSem.semKey)}; + m_messageSem = SystemSemaphore{std::to_string(m_data->messageSem.semKey)}; } inline bool isInvalid() const @@ -336,11 +328,10 @@ class shmFifo volatile bool m_invalid; bool m_master; SharedMemory m_data; - QSystemSemaphore m_dataSem; - QSystemSemaphore m_messageSem; + SystemSemaphore m_dataSem; + SystemSemaphore m_messageSem; std::atomic_int m_lockDepth; - -} ; +}; #endif // SYNC_WITH_SHM_FIFO diff --git a/include/RemotePluginClient.h b/include/RemotePluginClient.h index 77eef68f035..22158f1b81b 100644 --- a/include/RemotePluginClient.h +++ b/include/RemotePluginClient.h @@ -44,6 +44,8 @@ namespace lmms { +class SampleFrame; + class RemotePluginClient : public RemotePluginBase { public: @@ -58,8 +60,8 @@ class RemotePluginClient : public RemotePluginBase bool processMessage( const message & _m ) override; - virtual void process( const sampleFrame * _in_buf, - sampleFrame * _out_buf ) = 0; + virtual void process( const SampleFrame* _in_buf, + SampleFrame* _out_buf ) = 0; virtual void processMidiEvent( const MidiEvent&, const f_cnt_t /* _offset */ ) { @@ -342,8 +344,8 @@ void RemotePluginClient::doProcessing() { if (m_audioBuffer) { - process( (sampleFrame *)( m_inputCount > 0 ? m_audioBuffer.get() : nullptr ), - (sampleFrame *)( m_audioBuffer.get() + + process( (SampleFrame*)( m_inputCount > 0 ? m_audioBuffer.get() : nullptr ), + (SampleFrame*)( m_audioBuffer.get() + ( m_inputCount*m_bufferSize ) ) ); } else diff --git a/include/RingBuffer.h b/include/RingBuffer.h index 98f72647593..41595be19ed 100644 --- a/include/RingBuffer.h +++ b/include/RingBuffer.h @@ -35,6 +35,7 @@ namespace lmms { +class SampleFrame; /** \brief A basic LMMS ring buffer for single-thread use. For thread and realtime safe alternative see LocklessRingBuffer. */ @@ -105,7 +106,7 @@ class LMMS_EXPORT RingBuffer : public QObject * to a specified destination, and advances the position by one period * \param dst Destination pointer */ - void pop( sampleFrame * dst ); + void pop( SampleFrame* dst ); // note: ringbuffer position is unaffected by all other read functions beside pop() @@ -113,27 +114,27 @@ class LMMS_EXPORT RingBuffer : public QObject * \param dst Destination pointer * \param offset Offset in frames against current position, may be negative */ - void read( sampleFrame * dst, f_cnt_t offset=0 ); + void read( SampleFrame* dst, f_cnt_t offset = 0 ); /** \brief Reads a period-sized buffer from the ringbuffer and writes it to a specified destination * \param dst Destination pointer * \param offset Offset in milliseconds against current position, may be negative */ - void read( sampleFrame * dst, float offset ); + void read( SampleFrame* dst, float offset ); /** \brief Reads a buffer of specified size from the ringbuffer and writes it to a specified destination * \param dst Destination pointer * \param offset Offset in frames against current position, may be negative * \param length Length in frames of the buffer to read - must not be higher than the size of the ringbuffer! */ - void read( sampleFrame * dst, f_cnt_t offset, f_cnt_t length ); + void read( SampleFrame* dst, f_cnt_t offset, f_cnt_t length ); /** \brief Reads a buffer of specified size from the ringbuffer and writes it to a specified destination * \param dst Destination pointer * \param offset Offset in milliseconds against current position, may be negative * \param length Length in frames of the buffer to read - must not be higher than the size of the ringbuffer! */ - void read( sampleFrame * dst, float offset, f_cnt_t length ); + void read( SampleFrame* dst, float offset, f_cnt_t length ); // write functions @@ -143,28 +144,28 @@ class LMMS_EXPORT RingBuffer : public QObject * \param offset Offset in frames against current position, may *NOT* be negative * \param length Length of the source buffer, if zero, period size is used - must not be higher than the size of the ringbuffer! */ - void write( sampleFrame * src, f_cnt_t offset=0, f_cnt_t length=0 ); + void write( SampleFrame* src, f_cnt_t offset=0, f_cnt_t length=0 ); /** \brief Writes a buffer of sampleframes to the ringbuffer at specified position * \param src Pointer to the source buffer * \param offset Offset in milliseconds against current position, may *NOT* be negative * \param length Length of the source buffer, if zero, period size is used - must not be higher than the size of the ringbuffer! */ - void write( sampleFrame * src, float offset, f_cnt_t length=0 ); + void write( SampleFrame* src, float offset, f_cnt_t length=0 ); /** \brief Mixes a buffer of sampleframes additively to the ringbuffer at specified position * \param src Pointer to the source buffer * \param offset Offset in frames against current position, may *NOT* be negative * \param length Length of the source buffer, if zero, period size is used - must not be higher than the size of the ringbuffer! */ - void writeAdding( sampleFrame * src, f_cnt_t offset=0, f_cnt_t length=0 ); + void writeAdding( SampleFrame* src, f_cnt_t offset=0, f_cnt_t length=0 ); /** \brief Mixes a buffer of sampleframes additively to the ringbuffer at specified position * \param src Pointer to the source buffer * \param offset Offset in milliseconds against current position, may *NOT* be negative * \param length Length of the source buffer, if zero, period size is used - must not be higher than the size of the ringbuffer! */ - void writeAdding( sampleFrame * src, float offset, f_cnt_t length=0 ); + void writeAdding( SampleFrame* src, float offset, f_cnt_t length=0 ); /** \brief Mixes a buffer of sampleframes additively to the ringbuffer at specified position, with * a specified multiplier applied to the frames @@ -173,7 +174,7 @@ class LMMS_EXPORT RingBuffer : public QObject * \param length Length of the source buffer, if zero, period size is used - must not be higher than the size of the ringbuffer! * \param level Multiplier applied to the frames before they're written to the ringbuffer */ - void writeAddingMultiplied( sampleFrame * src, f_cnt_t offset, f_cnt_t length, float level ); + void writeAddingMultiplied( SampleFrame* src, f_cnt_t offset, f_cnt_t length, float level ); /** \brief Mixes a buffer of sampleframes additively to the ringbuffer at specified position, with * a specified multiplier applied to the frames @@ -182,7 +183,7 @@ class LMMS_EXPORT RingBuffer : public QObject * \param length Length of the source buffer, if zero, period size is used * \param level Multiplier applied to the frames before they're written to the ringbuffer */ - void writeAddingMultiplied( sampleFrame * src, float offset, f_cnt_t length, float level ); + void writeAddingMultiplied( SampleFrame* src, float offset, f_cnt_t length, float level ); /** \brief Mixes a buffer of sampleframes additively to the ringbuffer at specified position, with * a specified multiplier applied to the frames, with swapped channels @@ -191,7 +192,7 @@ class LMMS_EXPORT RingBuffer : public QObject * \param length Length of the source buffer, if zero, period size is used - must not be higher than the size of the ringbuffer! * \param level Multiplier applied to the frames before they're written to the ringbuffer */ - void writeSwappedAddingMultiplied( sampleFrame * src, f_cnt_t offset, f_cnt_t length, float level ); + void writeSwappedAddingMultiplied( SampleFrame* src, f_cnt_t offset, f_cnt_t length, float level ); /** \brief Mixes a buffer of sampleframes additively to the ringbuffer at specified position, with * a specified multiplier applied to the frames, with swapped channels @@ -200,7 +201,7 @@ class LMMS_EXPORT RingBuffer : public QObject * \param length Length of the source buffer, if zero, period size is used * \param level Multiplier applied to the frames before they're written to the ringbuffer */ - void writeSwappedAddingMultiplied( sampleFrame * src, float offset, f_cnt_t length, float level ); + void writeSwappedAddingMultiplied( SampleFrame* src, float offset, f_cnt_t length, float level ); protected slots: @@ -215,7 +216,7 @@ protected slots: const fpp_t m_fpp; sample_rate_t m_samplerate; size_t m_size; - sampleFrame * m_buffer; + SampleFrame* m_buffer; volatile unsigned int m_position; }; diff --git a/include/RmsHelper.h b/include/RmsHelper.h index fd2c0f9bbd2..a50d5ff6d2b 100644 --- a/include/RmsHelper.h +++ b/include/RmsHelper.h @@ -36,7 +36,7 @@ namespace lmms class RmsHelper { public: - RmsHelper( int size ) : + RmsHelper(std::size_t size) : m_buffer( nullptr ) { setSize( size ); @@ -46,7 +46,7 @@ class RmsHelper if( m_buffer ) delete[] m_buffer; } - inline void setSize( int size ) + void setSize(std::size_t size) { if( m_buffer ) { @@ -90,8 +90,8 @@ class RmsHelper private: float * m_buffer; float m_sum; - unsigned int m_pos; - unsigned int m_size; + std::size_t m_pos; + std::size_t m_size; float m_sizef; }; diff --git a/include/Sample.h b/include/Sample.h index 86fba1ddc03..3fd5bc38ef1 100644 --- a/include/Sample.h +++ b/include/Sample.h @@ -77,8 +77,8 @@ class LMMS_EXPORT Sample }; Sample() = default; - Sample(const QByteArray& base64, int sampleRate = Engine::audioEngine()->processingSampleRate()); - Sample(const sampleFrame* data, size_t numFrames, int sampleRate = Engine::audioEngine()->processingSampleRate()); + Sample(const QByteArray& base64, int sampleRate = Engine::audioEngine()->outputSampleRate()); + Sample(const SampleFrame* data, size_t numFrames, int sampleRate = Engine::audioEngine()->outputSampleRate()); Sample(const Sample& other); Sample(Sample&& other); explicit Sample(const QString& audioFile); @@ -87,7 +87,7 @@ class LMMS_EXPORT Sample auto operator=(const Sample&) -> Sample&; auto operator=(Sample&&) -> Sample&; - auto play(sampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency = DefaultBaseFreq, + auto play(SampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency = DefaultBaseFreq, Loop loopMode = Loop::Off) const -> bool; auto sampleDuration() const -> std::chrono::milliseconds; @@ -97,7 +97,7 @@ class LMMS_EXPORT Sample auto toBase64() const -> QString { return m_buffer->toBase64(); } - auto data() const -> const sampleFrame* { return m_buffer->data(); } + auto data() const -> const SampleFrame* { return m_buffer->data(); } auto buffer() const -> std::shared_ptr { return m_buffer; } auto startFrame() const -> int { return m_startFrame.load(std::memory_order_relaxed); } auto endFrame() const -> int { return m_endFrame.load(std::memory_order_relaxed); } @@ -117,7 +117,7 @@ class LMMS_EXPORT Sample void setReversed(bool reversed) { m_reversed.store(reversed, std::memory_order_relaxed); } private: - void playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const; + void playRaw(SampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const; void advance(PlaybackState* state, size_t advanceAmount, Loop loopMode) const; private: diff --git a/include/SampleBuffer.h b/include/SampleBuffer.h index 4089eb44627..8ec6c58863c 100644 --- a/include/SampleBuffer.h +++ b/include/SampleBuffer.h @@ -41,22 +41,22 @@ namespace lmms { class LMMS_EXPORT SampleBuffer { public: - using value_type = sampleFrame; - using reference = sampleFrame&; - using const_reference = const sampleFrame&; - using iterator = std::vector::iterator; - using const_iterator = std::vector::const_iterator; - using difference_type = std::vector::difference_type; - using size_type = std::vector::size_type; - using reverse_iterator = std::vector::reverse_iterator; - using const_reverse_iterator = std::vector::const_reverse_iterator; + using value_type = SampleFrame; + using reference = SampleFrame&; + using const_reference = const SampleFrame&; + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; + using difference_type = std::vector::difference_type; + using size_type = std::vector::size_type; + using reverse_iterator = std::vector::reverse_iterator; + using const_reverse_iterator = std::vector::const_reverse_iterator; SampleBuffer() = default; explicit SampleBuffer(const QString& audioFile); SampleBuffer(const QString& base64, int sampleRate); - SampleBuffer(std::vector data, int sampleRate); + SampleBuffer(std::vector data, int sampleRate); SampleBuffer( - const sampleFrame* data, size_t numFrames, int sampleRate = Engine::audioEngine()->processingSampleRate()); + const SampleFrame* data, size_t numFrames, int sampleRate = Engine::audioEngine()->outputSampleRate()); friend void swap(SampleBuffer& first, SampleBuffer& second) noexcept; auto toBase64() const -> QString; @@ -82,16 +82,16 @@ class LMMS_EXPORT SampleBuffer auto crbegin() const -> const_reverse_iterator { return m_data.crbegin(); } auto crend() const -> const_reverse_iterator { return m_data.crend(); } - auto data() const -> const sampleFrame* { return m_data.data(); } + auto data() const -> const SampleFrame* { return m_data.data(); } auto size() const -> size_type { return m_data.size(); } auto empty() const -> bool { return m_data.empty(); } static auto emptyBuffer() -> std::shared_ptr; private: - std::vector m_data; + std::vector m_data; QString m_audioFile; - sample_rate_t m_sampleRate = Engine::audioEngine()->processingSampleRate(); + sample_rate_t m_sampleRate = Engine::audioEngine()->outputSampleRate(); }; } // namespace lmms diff --git a/include/SampleClip.h b/include/SampleClip.h index da11996b14c..3beca338bcd 100644 --- a/include/SampleClip.h +++ b/include/SampleClip.h @@ -55,7 +55,9 @@ class SampleClip : public Clip SampleClip& operator=( const SampleClip& that ) = delete; void changeLength( const TimePos & _length ) override; + void changeLengthToSampleLength(); const QString& sampleFile() const; + bool hasSampleFileLoaded(const QString & filename) const; void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; void loadSettings( const QDomElement & _this ) override; diff --git a/include/SampleDecoder.h b/include/SampleDecoder.h index d7ce076ddd3..2cfd0097717 100644 --- a/include/SampleDecoder.h +++ b/include/SampleDecoder.h @@ -32,6 +32,7 @@ #include #include "lmms_basics.h" +#include "SampleFrame.h" namespace lmms { class SampleDecoder @@ -39,7 +40,7 @@ class SampleDecoder public: struct Result { - std::vector data; + std::vector data; int sampleRate; }; diff --git a/include/SampleFrame.h b/include/SampleFrame.h new file mode 100644 index 00000000000..238a85dead9 --- /dev/null +++ b/include/SampleFrame.h @@ -0,0 +1,231 @@ +/* + * SampleFrame.h - Representation of a stereo sample + * + * Copyright (c) 2004-2009 Tobias Doerffel + * Copyright (c) 2024- Michael Gregorius + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_SAMPLEFRAME_H +#define LMMS_SAMPLEFRAME_H + +#include "lmms_basics.h" + +#include +#include +#include + + +namespace lmms +{ + +class SampleFrame +{ +public: + SampleFrame() : SampleFrame(0., 0.) + { + } + + explicit SampleFrame(sample_t value) : SampleFrame(value, value) + { + } + + SampleFrame(sample_t left, sample_t right) : + m_samples({ left, right }) + { + } + + sample_t* data() + { + return m_samples.data(); + } + + const sample_t* data() const + { + return m_samples.data(); + } + + sample_t& left() + { + return m_samples[0]; + } + + const sample_t& left() const + { + return m_samples[0]; + } + + void setLeft(const sample_t& value) + { + m_samples[0] = value; + } + + sample_t& right() + { + return m_samples[1]; + } + + const sample_t& right() const + { + return m_samples[1]; + } + + void setRight(const sample_t& value) + { + m_samples[1] = value; + } + + sample_t& operator[](size_t index) + { + return m_samples[index]; + } + + const sample_t& operator[](size_t index) const + { + return m_samples[index]; + } + + SampleFrame operator+(const SampleFrame& other) const + { + return SampleFrame(left() + other.left(), right() + other.right()); + } + + SampleFrame& operator+=(const SampleFrame& other) + { + auto & l = left(); + auto & r = right(); + + l += other.left(); + r += other.right(); + + return *this; + } + + SampleFrame operator*(float value) const + { + return SampleFrame(left() * value, right() * value); + } + + SampleFrame& operator*=(float value) + { + setLeft(left() * value); + setRight(right() * value); + + return *this; + } + + SampleFrame operator*(const SampleFrame& other) const + { + return SampleFrame(left() * other.left(), right() * other.right()); + } + + void operator*=(const SampleFrame& other) + { + left() *= other.left(); + right() *= other.right(); + } + + sample_t sumOfSquaredAmplitudes() const + { + return left() * left() + right() * right(); + } + + SampleFrame abs() const + { + return SampleFrame{std::abs(this->left()), std::abs(this->right())}; + } + + SampleFrame absMax(const SampleFrame& other) + { + const auto a = abs(); + const auto b = other.abs(); + + return SampleFrame(std::max(a.left(), b.left()), std::max(a.right(), b.right())); + } + + sample_t average() const + { + return (left() + right()) / 2; + } + + void clamp(sample_t low, sample_t high) + { + auto & l = left(); + l = std::clamp(l, low, high); + + auto & r = right(); + r = std::clamp(r, low, high); + } + + bool containsInf() const + { + return std::isinf(left()) || std::isinf(right()); + } + + bool containsNaN() const + { + return std::isnan(left()) || std::isnan(right()); + } + +private: + std::array m_samples; +}; + +inline void zeroSampleFrames(SampleFrame* buffer, size_t frames) +{ + // The equivalent of the following operation which yields compiler warnings + // memset(buffer, 0, sizeof(SampleFrame) * frames); + + std::fill(buffer, buffer + frames, SampleFrame()); +} + +inline SampleFrame getAbsPeakValues(SampleFrame* buffer, size_t frames) +{ + SampleFrame peaks; + + for (f_cnt_t i = 0; i < frames; ++i) + { + peaks = peaks.absMax(buffer[i]); + } + + return peaks; +} + +inline void copyToSampleFrames(SampleFrame* target, const float* source, size_t frames) +{ + for (size_t i = 0; i < frames; ++i) + { + target[i].setLeft(source[2*i]); + target[i].setRight(source[2*i + 1]); + } +} + +inline void copyFromSampleFrames(float* target, const SampleFrame* source, size_t frames) +{ + for (size_t i = 0; i < frames; ++i) + { + target[2*i] = source[i].left(); + target[2*i + 1] = source[i].right(); + } +} + +} // namespace lmms + +#endif // LMMS_SAMPLEFRAME_H diff --git a/include/SampleLoader.h b/include/SampleLoader.h index 7dbdbdc3312..fd8f1135725 100644 --- a/include/SampleLoader.h +++ b/include/SampleLoader.h @@ -39,7 +39,7 @@ class LMMS_EXPORT SampleLoader static QString openWaveformFile(const QString& previousFile = ""); static std::shared_ptr createBufferFromFile(const QString& filePath); static std::shared_ptr createBufferFromBase64( - const QString& base64, int sampleRate = Engine::audioEngine()->processingSampleRate()); + const QString& base64, int sampleRate = Engine::audioEngine()->outputSampleRate()); private: static void displayError(const QString& message); }; diff --git a/include/SamplePlayHandle.h b/include/SamplePlayHandle.h index 280010b066d..dde29b49b31 100644 --- a/include/SamplePlayHandle.h +++ b/include/SamplePlayHandle.h @@ -55,7 +55,7 @@ class LMMS_EXPORT SamplePlayHandle : public PlayHandle } - void play( sampleFrame * buffer ) override; + void play( SampleFrame* buffer ) override; bool isFinished() const override; bool isFromTrack( const Track * _track ) const override; diff --git a/include/SampleRecordHandle.h b/include/SampleRecordHandle.h index df2d7c772c3..b650c67609f 100644 --- a/include/SampleRecordHandle.h +++ b/include/SampleRecordHandle.h @@ -48,7 +48,7 @@ class SampleRecordHandle : public PlayHandle SampleRecordHandle( SampleClip* clip ); ~SampleRecordHandle() override; - void play( sampleFrame * _working_buffer ) override; + void play( SampleFrame* _working_buffer ) override; bool isFinished() const override; bool isFromTrack( const Track * _track ) const override; @@ -58,10 +58,10 @@ class SampleRecordHandle : public PlayHandle private: - virtual void writeBuffer( const sampleFrame * _ab, + virtual void writeBuffer( const SampleFrame* _ab, const f_cnt_t _frames ); - using bufferList = QList>; + using bufferList = QList>; bufferList m_buffers; f_cnt_t m_framesRecorded; TimePos m_minLength; diff --git a/include/SampleWaveform.h b/include/SampleWaveform.h index 0185e0e98f8..ccfc9fb609e 100644 --- a/include/SampleWaveform.h +++ b/include/SampleWaveform.h @@ -36,7 +36,7 @@ class LMMS_EXPORT SampleWaveform public: struct Parameters { - const sampleFrame* buffer; + const SampleFrame* buffer; size_t size; float amplification; bool reversed; diff --git a/include/SetupDialog.h b/include/SetupDialog.h index ce81bb47778..871a80bcd4b 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -102,13 +102,13 @@ private slots: // Audio settings widget. void audioInterfaceChanged(const QString & driver); - void toggleHQAudioDev(bool enabled); void updateBufferSizeWarning(int value); void setBufferSize(int value); void resetBufferSize(); // MIDI settings widget. void midiInterfaceChanged(const QString & driver); + void toggleMidiAutoQuantization(bool enabled); // Paths settings widget. void openWorkingDir(); @@ -179,7 +179,6 @@ private slots: AswMap m_audioIfaceSetupWidgets; trMap m_audioIfaceNames; bool m_NaNHandler; - bool m_hqAudioDev; int m_bufferSize; QSlider * m_bufferSizeSlider; QLabel * m_bufferSizeLbl; @@ -190,6 +189,7 @@ private slots: MswMap m_midiIfaceSetupWidgets; trMap m_midiIfaceNames; QComboBox * m_assignableMidiDevices; + bool m_midiAutoQuantize; // Paths settings widgets. QString m_workingDir; diff --git a/include/SongEditor.h b/include/SongEditor.h index ee9e83f44c3..98a9096fb26 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -97,8 +97,6 @@ public slots: void mouseReleaseEvent(QMouseEvent * me) override; private slots: - void setHighQuality( bool ); - void setMasterVolume( int new_val ); void showMasterVolumeFloat(); void updateMasterVolumeFloat( int new_val ); @@ -166,6 +164,7 @@ private slots: signals: void pixelsPerBarChanged(float); + void proportionalSnapChanged(); } ; diff --git a/include/SweepOscillator.h b/include/SweepOscillator.h index b5521da3bf1..c7c7938d1e0 100644 --- a/include/SweepOscillator.h +++ b/include/SweepOscillator.h @@ -43,7 +43,7 @@ class SweepOscillator virtual ~SweepOscillator() = default; - void update( sampleFrame* buf, const fpp_t frames, const float freq1, const float freq2, const float sampleRate ) + void update( SampleFrame* buf, const fpp_t frames, const float freq1, const float freq2, const float sampleRate ) { const float df = freq2 - freq1; for( fpp_t frame = 0; frame < frames; ++frame ) diff --git a/include/SystemSemaphore.h b/include/SystemSemaphore.h new file mode 100644 index 00000000000..931c472bb1f --- /dev/null +++ b/include/SystemSemaphore.h @@ -0,0 +1,61 @@ +/* + * SystemSemaphore.h + * + * Copyright (c) 2024 Dominic Clark + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#ifndef LMMS_SYSTEM_SEMAPHORE_H +#define LMMS_SYSTEM_SEMAPHORE_H + +#include +#include + +namespace lmms { + +namespace detail { + +class SystemSemaphoreImpl; + +} // namespace detail + +class SystemSemaphore +{ +public: + SystemSemaphore() noexcept; + SystemSemaphore(std::string key, unsigned int value); + explicit SystemSemaphore(std::string key); + ~SystemSemaphore(); + + SystemSemaphore(SystemSemaphore&& other) noexcept; + auto operator=(SystemSemaphore&& other) noexcept -> SystemSemaphore&; + + auto acquire() noexcept -> bool; + auto release() noexcept -> bool; + + auto key() const noexcept -> const std::string& { return m_key; } + +private: + std::string m_key; + std::unique_ptr m_impl; +}; + +} // namespace lmms + +#endif // LMMS_SYSTEM_SEMAPHORE_H diff --git a/include/ThreadPool.h b/include/ThreadPool.h new file mode 100644 index 00000000000..ad4d798505b --- /dev/null +++ b/include/ThreadPool.h @@ -0,0 +1,93 @@ +/* + * ThreadPool.h + * + * Copyright (c) 2024 saker + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_THREAD_POOL_H +#define LMMS_THREAD_POOL_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace lmms { +//! A thread pool that can be used for asynchronous processing. +class ThreadPool +{ +public: + //! Destroys the `ThreadPool` object. + //! This blocks until all workers have finished executing. + ~ThreadPool(); + + //! Enqueue function `fn` with arguments `args` to be ran asynchronously. + template + auto enqueue(Fn&& fn, Args&&... args) -> std::future> + { + using ReturnType = std::invoke_result_t; + + auto promise = std::make_shared>(); + auto task = [promise, fn = std::forward(fn), args = std::make_tuple(std::forward(args)...)] + { + if constexpr (!std::is_same_v) + { + promise->set_value(std::apply(fn, args)); + return; + } + std::apply(fn, args); + promise->set_value(); + }; + + { + const auto lock = std::unique_lock{m_runMutex}; + m_queue.push(std::move(task)); + } + + m_runCond.notify_one(); + return promise->get_future(); + } + + //! Return the number of worker threads used. + auto numWorkers() const -> size_t; + + //! Return the global `ThreadPool` instance. + static auto instance() -> ThreadPool&; + +private: + ThreadPool(size_t numWorkers); + void run(); + std::vector m_workers; + std::queue> m_queue; + std::atomic m_done = false; + std::condition_variable m_runCond; + std::mutex m_runMutex; + inline static size_t s_numWorkers = std::thread::hardware_concurrency(); +}; +} // namespace lmms + +#endif // LMMS_THREAD_POOL_H diff --git a/include/TimeLineWidget.h b/include/TimeLineWidget.h index 5c683cfd9f9..0eb33bad935 100644 --- a/include/TimeLineWidget.h +++ b/include/TimeLineWidget.h @@ -73,7 +73,8 @@ class TimeLineWidget : public QWidget enum class AutoScrollState { - Enabled, + Stepped, + Continuous, Disabled }; @@ -212,7 +213,7 @@ public slots: QCursor m_cursorSelectLeft = QCursor{embed::getIconPixmap("cursor_select_left"), 0, 16}; QCursor m_cursorSelectRight = QCursor{embed::getIconPixmap("cursor_select_right"), 32, 16}; - AutoScrollState m_autoScroll = AutoScrollState::Enabled; + AutoScrollState m_autoScroll = AutoScrollState::Stepped; // Width of the unused region on the widget's left (above track labels or piano) int m_xOffset; diff --git a/include/Track.h b/include/Track.h index b801bb1828b..db33900f5da 100644 --- a/include/Track.h +++ b/include/Track.h @@ -127,7 +127,7 @@ class LMMS_EXPORT Track : public Model, public JournallingObject void deleteClips(); int numOfClips(); - Clip * getClip( int clipNum ); + auto getClip(std::size_t clipNum) -> Clip*; int getClipNum(const Clip* clip ); const clipVector & getClips() const diff --git a/include/TrackContainerView.h b/include/TrackContainerView.h index 82d6f993b08..010cda60280 100644 --- a/include/TrackContainerView.h +++ b/include/TrackContainerView.h @@ -166,9 +166,7 @@ public slots: protected: - static const int DEFAULT_PIXELS_PER_BAR = 16; - - void resizeEvent( QResizeEvent * ) override; + static const int DEFAULT_PIXELS_PER_BAR = 128; TimePos m_currentPosition; diff --git a/include/TrackContentWidget.h b/include/TrackContentWidget.h index 7cf2363231d..f93b0a58d6f 100644 --- a/include/TrackContentWidget.h +++ b/include/TrackContentWidget.h @@ -49,18 +49,24 @@ class TrackContentWidget : public QWidget, public JournallingObject Q_OBJECT // qproperties for track background gradients - Q_PROPERTY( QBrush darkerColor READ darkerColor WRITE setDarkerColor ) - Q_PROPERTY( QBrush lighterColor READ lighterColor WRITE setLighterColor ) - Q_PROPERTY( QBrush gridColor READ gridColor WRITE setGridColor ) - Q_PROPERTY( QBrush embossColor READ embossColor WRITE setEmbossColor ) + Q_PROPERTY(QBrush darkerColor READ darkerColor WRITE setDarkerColor) + Q_PROPERTY(QBrush lighterColor READ lighterColor WRITE setLighterColor) + Q_PROPERTY(QBrush coarseGridColor READ coarseGridColor WRITE setCoarseGridColor) + Q_PROPERTY(QBrush fineGridColor READ fineGridColor WRITE setFineGridColor) + Q_PROPERTY(QBrush horizontalColor READ horizontalColor WRITE setHorizontalColor) + Q_PROPERTY(QBrush embossColor READ embossColor WRITE setEmbossColor) + + Q_PROPERTY(int coarseGridWidth READ coarseGridWidth WRITE setCoarseGridWidth) + Q_PROPERTY(int fineGridWidth READ fineGridWidth WRITE setFineGridWidth) + Q_PROPERTY(int horizontalWidth READ horizontalWidth WRITE setHorizontalWidth) + Q_PROPERTY(int embossWidth READ embossWidth WRITE setEmbossWidth) + + Q_PROPERTY(int embossOffset READ embossOffset WRITE setEmbossOffset) public: TrackContentWidget( TrackView * parent ); ~TrackContentWidget() override = default; - /*! \brief Updates the background tile pixmap. */ - void updateBackground(); - void addClipView( ClipView * clipv ); void removeClipView( ClipView * clipv ); void removeClipView( int clipNum ) @@ -82,17 +88,37 @@ class TrackContentWidget : public QWidget, public JournallingObject QBrush darkerColor() const; QBrush lighterColor() const; - QBrush gridColor() const; + QBrush coarseGridColor() const; + QBrush fineGridColor() const; + QBrush horizontalColor() const; QBrush embossColor() const; - void setDarkerColor( const QBrush & c ); - void setLighterColor( const QBrush & c ); - void setGridColor( const QBrush & c ); - void setEmbossColor( const QBrush & c); + int coarseGridWidth() const; + int fineGridWidth() const; + int horizontalWidth() const; + int embossWidth() const; + + int embossOffset() const; + + void setDarkerColor(const QBrush & c); + void setLighterColor(const QBrush & c); + void setCoarseGridColor(const QBrush & c); + void setFineGridColor(const QBrush & c); + void setHorizontalColor(const QBrush & c); + void setEmbossColor(const QBrush & c); + + void setCoarseGridWidth(int c); + void setFineGridWidth(int c); + void setHorizontalWidth(int c); + void setEmbossWidth(int c); + + void setEmbossOffset(int c); public slots: void update(); void changePosition( const lmms::TimePos & newPos = TimePos( -1 ) ); + /*! \brief Updates the background tile pixmap. */ + void updateBackground(); protected: enum class ContextMenuAction @@ -140,8 +166,17 @@ public slots: // qproperty fields QBrush m_darkerColor; QBrush m_lighterColor; - QBrush m_gridColor; + QBrush m_coarseGridColor; + QBrush m_fineGridColor; + QBrush m_horizontalColor; QBrush m_embossColor; + + int m_coarseGridWidth; + int m_fineGridWidth; + int m_horizontalWidth; + int m_embossWidth; + + int m_embossOffset; } ; diff --git a/include/TrackLabelButton.h b/include/TrackLabelButton.h index 1d3620d12f9..e2cba02a648 100644 --- a/include/TrackLabelButton.h +++ b/include/TrackLabelButton.h @@ -25,6 +25,8 @@ #ifndef LMMS_GUI_TRACK_LABEL_BUTTON_H #define LMMS_GUI_TRACK_LABEL_BUTTON_H +#include + #include namespace lmms::gui @@ -63,7 +65,7 @@ public slots: private: TrackView * m_trackView; - QString m_iconName; + std::string m_iconName; TrackRenameLineEdit * m_renameLineEdit; QRect m_buttonRect; QString elideName( const QString &name ); diff --git a/include/embed.h b/include/embed.h index 7d69f7c7d58..40a3622c6d8 100644 --- a/include/embed.h +++ b/include/embed.h @@ -25,117 +25,81 @@ #ifndef LMMS_EMBED_H #define LMMS_EMBED_H +#include +#include + #include #include #include "lmms_export.h" #include "lmms_basics.h" +namespace lmms { -namespace lmms -{ - -namespace embed -{ +namespace embed { /** * Return an image for the icon pixmap cache. * - * @param _name Identifier for the pixmap. If it is not in the icon pixmap + * @param name Identifier for the pixmap. If it is not in the icon pixmap * cache, it will be loaded from the artwork QDir search paths (exceptions are * compiled-in XPMs, you need to provide @p xpm for loading them). * @param xpm Must be XPM data if the source should be raw XPM data instead of * a file */ -QPixmap LMMS_EXPORT getIconPixmap( const QString& _name, - int _w = -1, int _h = -1 , const char** xpm = nullptr ); -QString LMMS_EXPORT getText( const char * _name ); - -} - - -#ifdef PLUGIN_NAME -namespace PLUGIN_NAME -{ - -inline QPixmap getIconPixmap( const QString& _name, - int _w = -1, int _h = -1, const char** xpm = nullptr ) -{ - return embed::getIconPixmap(QString("%1/%2").arg(LMMS_STRINGIFY(PLUGIN_NAME), _name), _w, _h, xpm); -} -//QString getText( const char * _name ); - -} // namespace PLUGIN_NAME - -#endif // PLUGIN_NAME +auto LMMS_EXPORT getIconPixmap(std::string_view name, + int width = -1, int height = -1, const char* const* xpm = nullptr) -> QPixmap; +auto LMMS_EXPORT getText(std::string_view name) -> QString; +} // namespace embed class PixmapLoader { public: - PixmapLoader( const PixmapLoader * _ref ) : - m_name( _ref != nullptr ? _ref->m_name : QString() ), - m_xpm( _ref->m_xpm ) - { - } + PixmapLoader() = default; - PixmapLoader( const QString & _name = QString(), - const char** xpm = nullptr ) : - m_name( _name ), - m_xpm(xpm) - { - } - - virtual QPixmap pixmap() const - { - if( !m_name.isEmpty() ) - { - return( embed::getIconPixmap( - m_name.toLatin1().constData(), -1, -1, m_xpm )); - } - return( QPixmap() ); - } + explicit PixmapLoader(std::string name, const char* const* xpm = nullptr) : + m_name{std::move(name)}, + m_xpm{xpm} + { } virtual ~PixmapLoader() = default; - virtual QString pixmapName() const + auto pixmap(int width = -1, int height = -1) const -> QPixmap { - return m_name; + return embed::getIconPixmap(m_name, width, height, m_xpm); } -protected: - QString m_name; - const char** m_xpm = nullptr; -} ; + auto pixmapName() const -> const std::string& { return m_name; } +private: + std::string m_name; + const char* const* m_xpm = nullptr; +}; #ifdef PLUGIN_NAME + class PluginPixmapLoader : public PixmapLoader { public: - PluginPixmapLoader( const QString & _name = QString() ) : - PixmapLoader( _name ) - { - } + PluginPixmapLoader() = default; - QPixmap pixmap() const override - { - if( !m_name.isEmpty() ) - { - return( PLUGIN_NAME::getIconPixmap( - m_name.toLatin1().constData() ) ); - } - return( QPixmap() ); - } + explicit PluginPixmapLoader(std::string name, const char* const* xpm = nullptr) : + PixmapLoader{LMMS_STRINGIFY(PLUGIN_NAME) "/" + name, xpm} + { } +}; - QString pixmapName() const override - { - return QString( LMMS_STRINGIFY(PLUGIN_NAME) ) + "::" + m_name; - } +namespace PLUGIN_NAME { -} ; -#endif // PLUGIN_NAME +inline auto getIconPixmap(std::string_view name, + int width = -1, int height = -1, const char* const* xpm = nullptr) -> QPixmap +{ + return PluginPixmapLoader{std::string{name}, xpm}.pixmap(width, height); +} + +} // namespace PLUGIN_NAME +#endif // PLUGIN_NAME } // namespace lmms diff --git a/include/fft_helpers.h b/include/fft_helpers.h index cd4e5f88db4..80c69a0a35a 100644 --- a/include/fft_helpers.h +++ b/include/fft_helpers.h @@ -37,7 +37,7 @@ namespace lmms // NOTE: FFT_BUFFER_SIZE should be considered deprecated! // It is used by Eq plugin and some older code here, but this should be a user // switchable parameter, not a constant. Use a value from FFT_BLOCK_SIZES -const unsigned int FFT_BUFFER_SIZE = 2048; +constexpr auto FFT_BUFFER_SIZE = std::size_t{2048}; // Allowed FFT block sizes. Ranging from barely useful to barely acceptable // because of performance and latency reasons. diff --git a/include/gui_templates.h b/include/gui_templates.h index c0afbdfc0bb..bbb5f80da67 100644 --- a/include/gui_templates.h +++ b/include/gui_templates.h @@ -25,47 +25,19 @@ #ifndef LMMS_GUI_TEMPLATES_H #define LMMS_GUI_TEMPLATES_H -#include "lmmsconfig.h" - #include #include -#include -namespace lmms +namespace lmms::gui { - -// return DPI-independent font-size - font with returned font-size has always -// the same size in pixels -template -inline QFont pointSize( QFont _f ) +// Convenience method to set the font size in pixels +inline QFont adjustedToPixelSize(QFont font, int size) { - static const float DPI = 96; -#ifdef LMMS_BUILD_WIN32 - _f.setPointSizeF( ((float) SIZE+0.5f) * DPI / - QApplication::desktop()->logicalDpiY() ); -#else - _f.setPointSizeF( (float) SIZE * DPI / - QApplication::desktop()->logicalDpiY() ); -#endif - return( _f ); + font.setPixelSize(size); + return font; } - -inline QFont pointSizeF( QFont _f, float SIZE ) -{ - static const float DPI = 96; -#ifdef LMMS_BUILD_WIN32 - _f.setPointSizeF( (SIZE+0.5f) * DPI / - QApplication::desktop()->logicalDpiY() ); -#else - _f.setPointSizeF( SIZE * DPI / - QApplication::desktop()->logicalDpiY() ); -#endif - return( _f ); -} - - -} // namespace lmms +} // namespace lmms::gui #endif // LMMS_GUI_TEMPLATES_H diff --git a/include/lmms_basics.h b/include/lmms_basics.h index b566fa781ed..63a2bf3adc2 100644 --- a/include/lmms_basics.h +++ b/include/lmms_basics.h @@ -26,12 +26,11 @@ #define LMMS_TYPES_H #include -#include #include "lmmsconfig.h" #include -#include + namespace lmms @@ -46,77 +45,18 @@ using sample_t = float; // standard sample-type using int_sample_t = int16_t; // 16-bit-int-sample using sample_rate_t = uint32_t; // sample-rate -using fpp_t = int16_t; // frames per period (0-16384) -using f_cnt_t = int32_t; // standard frame-count -using ch_cnt_t = uint8_t; // channel-count (0-SURROUND_CHANNELS) +using fpp_t = size_t; // frames per period (0-16384) +using f_cnt_t = size_t; // standard frame-count +using ch_cnt_t = uint8_t; // channel-count (0-DEFAULT_CHANNELS) using bpm_t = uint16_t; // tempo (MIN_BPM to MAX_BPM) using bitrate_t = uint16_t; // bitrate in kbps using mix_ch_t = uint16_t; // Mixer-channel (0 to MAX_CHANNEL) using jo_id_t = uint32_t; // (unique) ID of a journalling object -// windows headers define "min" and "max" macros, breaking the methods bwloe -#undef min -#undef max - -template -struct typeInfo -{ - static inline T min() - { - return std::numeric_limits::min(); - } - - static inline T max() - { - return std::numeric_limits::max(); - } - - static inline T minEps() - { - return 1; - } - - static inline bool isEqual( T x, T y ) - { - return x == y; - } - - static inline T absVal( T t ) - { - return t >= 0 ? t : -t; - } -} ; - - -template<> -inline float typeInfo::minEps() -{ - return 1.0e-10f; -} - -template<> -inline bool typeInfo::isEqual( float x, float y ) -{ - if( x == y ) - { - return true; - } - return absVal( x - y ) < minEps(); -} - - constexpr ch_cnt_t DEFAULT_CHANNELS = 2; -constexpr ch_cnt_t SURROUND_CHANNELS = -#define LMMS_DISABLE_SURROUND -#ifndef LMMS_DISABLE_SURROUND - 4; -#else - 2; -#endif - constexpr char LADSPA_PATH_SEPERATOR = #ifdef LMMS_BUILD_WIN32 ';'; @@ -126,11 +66,6 @@ constexpr char LADSPA_PATH_SEPERATOR = -using sampleFrame = std::array; -using surroundSampleFrame = std::array; -constexpr std::size_t LMMS_ALIGN_SIZE = 16; - - #define LMMS_STRINGIFY(s) LMMS_STR(s) #define LMMS_STR(PN) #PN diff --git a/include/lmms_constants.h b/include/lmms_constants.h index c6452d6c619..4390b81eaec 100644 --- a/include/lmms_constants.h +++ b/include/lmms_constants.h @@ -36,6 +36,7 @@ constexpr long double LD_PI_R = 1.0 / LD_PI; constexpr long double LD_PI_SQR = LD_PI * LD_PI; constexpr long double LD_E = 2.71828182845904523536028747135266249775724709369995; constexpr long double LD_E_R = 1.0 / LD_E; +constexpr long double LD_SQRT_2 = 1.41421356237309504880168872420969807856967187537695; constexpr double D_PI = (double) LD_PI; constexpr double D_2PI = (double) LD_2PI; @@ -44,6 +45,7 @@ constexpr double D_PI_R = (double) LD_PI_R; constexpr double D_PI_SQR = (double) LD_PI_SQR; constexpr double D_E = (double) LD_E; constexpr double D_E_R = (double) LD_E_R; +constexpr double D_SQRT_2 = (double) LD_SQRT_2; constexpr float F_PI = (float) LD_PI; constexpr float F_2PI = (float) LD_2PI; @@ -52,6 +54,9 @@ constexpr float F_PI_R = (float) LD_PI_R; constexpr float F_PI_SQR = (float) LD_PI_SQR; constexpr float F_E = (float) LD_E; constexpr float F_E_R = (float) LD_E_R; +constexpr float F_SQRT_2 = (float) LD_SQRT_2; + +constexpr float F_EPSILON = 1.0e-10f; // 10^-10 // Microtuner constexpr unsigned int MaxScaleCount = 10; //!< number of scales per project diff --git a/include/lmms_math.h b/include/lmms_math.h index f6455d6931f..369a89b6ea6 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -32,10 +32,16 @@ #include "lmms_constants.h" #include "lmmsconfig.h" +#include namespace lmms { +static inline bool approximatelyEqual(float x, float y) +{ + return x == y ? true : std::abs(x - y) < F_EPSILON; +} + #ifdef __INTEL_COMPILER static inline float absFraction( const float _x ) @@ -60,10 +66,9 @@ static inline float fraction( const float _x ) * If the result is interpreted as a phase of an oscillator, it makes that negative phases are * converted to positive phases. */ -static inline float absFraction( const float _x ) +static inline float absFraction(const float x) { - return( _x - ( _x >= 0.0f ? static_cast( _x ) : - static_cast( _x ) - 1 ) ); + return x - std::floor(x); } /*! @@ -269,18 +274,18 @@ static inline float safeDbfsToAmp( float dbfs ) //! @brief Converts linear amplitude (>0-1.0) to dBFS scale. //! @param amp Linear amplitude, where 1.0 = 0dBFS. ** Must be larger than zero! ** //! @return Amplitude in dBFS. -static inline float ampToDbfs( float amp ) +static inline float ampToDbfs(float amp) { - return log10f( amp ) * 20.0f; + return log10f(amp) * 20.0f; } //! @brief Converts dBFS-scale to linear amplitude with 0dBFS = 1.0 //! @param dbfs The dBFS value to convert. ** Must be a real number - not inf/nan! ** //! @return Linear amplitude -static inline float dbfsToAmp( float dbfs ) +static inline float dbfsToAmp(float dbfs) { - return std::pow(10.f, dbfs * 0.05f ); + return std::pow(10.f, dbfs * 0.05f); } @@ -326,6 +331,13 @@ static inline T absMin( T a, T b ) return std::abs(a) < std::abs(b) ? a : b; } +//! Returns the linear interpolation of the two values +template +constexpr T lerp(T a, T b, F t) +{ + return (1. - t) * a + t * b; +} + // @brief Calculate number of digits which LcdSpinBox would show for a given number // @note Once we upgrade to C++20, we could probably use std::formatted_size static inline int numDigitsAsInt(float f) @@ -352,6 +364,28 @@ static inline int numDigitsAsInt(float f) return digits; } +template +class LinearMap +{ +public: + LinearMap(T x1, T y1, T x2, T y2) + { + T const dx = x2 - x1; + assert (dx != T(0)); + + m_a = (y2 - y1) / dx; + m_b = y1 - m_a * x1; + } + + T map(T x) const + { + return m_a * x + m_b; + } + +private: + T m_a; + T m_b; +}; } // namespace lmms diff --git a/plugins/Amplifier/Amplifier.cpp b/plugins/Amplifier/Amplifier.cpp index ac5fdf23b7d..2f4e57f77d0 100644 --- a/plugins/Amplifier/Amplifier.cpp +++ b/plugins/Amplifier/Amplifier.cpp @@ -57,7 +57,7 @@ AmplifierEffect::AmplifierEffect(Model* parent, const Descriptor::SubPluginFeatu } -bool AmplifierEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames) +bool AmplifierEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames) { if (!isEnabled() || !isRunning()) { return false ; } @@ -80,14 +80,14 @@ bool AmplifierEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames) const float panLeft = std::min(1.0f, 1.0f - pan); const float panRight = std::min(1.0f, 1.0f + pan); - auto s = std::array{buf[f][0], buf[f][1]}; + auto& currentFrame = buf[f]; - s[0] *= volume * left * panLeft; - s[1] *= volume * right * panRight; + const auto s = currentFrame * SampleFrame(left * panLeft, right * panRight) * volume; - buf[f][0] = d * buf[f][0] + w * s[0]; - buf[f][1] = d * buf[f][1] + w * s[1]; - outSum += buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1]; + // Dry/wet mix + currentFrame = currentFrame * d + s * w; + + outSum += currentFrame.sumOfSquaredAmplitudes(); } checkGate(outSum / frames); diff --git a/plugins/Amplifier/Amplifier.h b/plugins/Amplifier/Amplifier.h index 8a39ffeb616..8be93800180 100644 --- a/plugins/Amplifier/Amplifier.h +++ b/plugins/Amplifier/Amplifier.h @@ -37,7 +37,7 @@ class AmplifierEffect : public Effect public: AmplifierEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key); ~AmplifierEffect() override = default; - bool processAudioBuffer(sampleFrame* buf, const fpp_t frames) override; + bool processAudioBuffer(SampleFrame* buf, const fpp_t frames) override; EffectControls* controls() override { diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.cpp b/plugins/AudioFileProcessor/AudioFileProcessor.cpp index 7cc5ee3fa6a..4cc14ba9cdb 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessor.cpp @@ -30,6 +30,7 @@ #include "SampleLoader.h" #include "Song.h" +#include "lmms_basics.h" #include "plugin_export.h" #include @@ -105,7 +106,7 @@ AudioFileProcessor::AudioFileProcessor( InstrumentTrack * _instrument_track ) : void AudioFileProcessor::playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) + SampleFrame* _working_buffer ) { const fpp_t frames = _n->framesLeftForCurrentPeriod(); const f_cnt_t offset = _n->noteOffset(); @@ -122,7 +123,7 @@ void AudioFileProcessor::playNote( NotePlayHandle * _n, if( !_n->m_pluginData ) { - if (m_stutterModel.value() == true && m_nextPlayStartPoint >= m_sample.endFrame()) + if (m_stutterModel.value() == true && m_nextPlayStartPoint >= static_cast(m_sample.endFrame())) { // Restart playing the note if in stutter mode, not in loop mode, // and we're at the end of the sample. @@ -165,7 +166,7 @@ void AudioFileProcessor::playNote( NotePlayHandle * _n, } else { - memset( _working_buffer, 0, ( frames + offset ) * sizeof( sampleFrame ) ); + zeroSampleFrames(_working_buffer, frames + offset); emit isPlaying( 0 ); } } @@ -276,7 +277,7 @@ QString AudioFileProcessor::nodeName() const -auto AudioFileProcessor::beatLen(NotePlayHandle* note) const -> int +auto AudioFileProcessor::beatLen(NotePlayHandle* note) const -> f_cnt_t { // If we can play indefinitely, use the default beat note duration if (static_cast(m_loopModel.value()) != Sample::Loop::Off) { return 0; } @@ -284,15 +285,15 @@ auto AudioFileProcessor::beatLen(NotePlayHandle* note) const -> int // Otherwise, use the remaining sample duration const auto baseFreq = instrumentTrack()->baseFreq(); const auto freqFactor = baseFreq / note->frequency() - * Engine::audioEngine()->processingSampleRate() + * Engine::audioEngine()->outputSampleRate() / Engine::audioEngine()->baseSampleRate(); - const auto startFrame = m_nextPlayStartPoint >= m_sample.endFrame() + const auto startFrame = m_nextPlayStartPoint >= static_cast(m_sample.endFrame()) ? m_sample.startFrame() : m_nextPlayStartPoint; const auto duration = m_sample.endFrame() - startFrame; - return static_cast(std::floor(duration * freqFactor)); + return static_cast(std::floor(duration * freqFactor)); } diff --git a/plugins/AudioFileProcessor/AudioFileProcessor.h b/plugins/AudioFileProcessor/AudioFileProcessor.h index 7ade1ec4fc3..acdbc45f722 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessor.h +++ b/plugins/AudioFileProcessor/AudioFileProcessor.h @@ -32,6 +32,7 @@ #include "Instrument.h" #include "Sample.h" +#include "lmms_basics.h" namespace lmms @@ -44,7 +45,7 @@ class AudioFileProcessor : public Instrument AudioFileProcessor( InstrumentTrack * _instrument_track ); void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; + SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; void saveSettings(QDomDocument& doc, QDomElement& elem) override; @@ -54,11 +55,11 @@ class AudioFileProcessor : public Instrument QString nodeName() const override; - auto beatLen(NotePlayHandle* note) const -> int override; + auto beatLen(NotePlayHandle* note) const -> f_cnt_t override; - f_cnt_t desiredReleaseFrames() const override + float desiredReleaseTimeMs() const override { - return 128; + return 3.f; } gui::PluginView* instantiateView( QWidget * _parent ) override; diff --git a/plugins/AudioFileProcessor/AudioFileProcessorView.cpp b/plugins/AudioFileProcessor/AudioFileProcessorView.cpp index d16b1d019a1..b7d5802dc44 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessorView.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessorView.cpp @@ -134,7 +134,6 @@ AudioFileProcessorView::AudioFileProcessorView(Instrument* instrument, // interpolation selector m_interpBox = new ComboBox(this); m_interpBox->setGeometry(142, 62, 82, ComboBox::DEFAULT_HEIGHT); - m_interpBox->setFont(pointSize<8>(m_interpBox->font())); // wavegraph m_waveView = 0; @@ -228,7 +227,7 @@ void AudioFileProcessorView::paintEvent(QPaintEvent*) int idx = a->sample().sampleFile().length(); - p.setFont(pointSize<8>(font())); + p.setFont(adjustedToPixelSize(font(), 8)); QFontMetrics fm(p.font()); diff --git a/plugins/AudioFileProcessor/AudioFileProcessorWaveView.cpp b/plugins/AudioFileProcessor/AudioFileProcessorWaveView.cpp index 507c4e7c0ee..2a07e5f77a1 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessorWaveView.cpp +++ b/plugins/AudioFileProcessor/AudioFileProcessorWaveView.cpp @@ -44,23 +44,23 @@ void AudioFileProcessorWaveView::updateSampleRange() { if (m_sample->sampleSize() > 1) { - const f_cnt_t marging = (m_sample->endFrame() - m_sample->startFrame()) * 0.1; + const auto marging = (m_sample->endFrame() - m_sample->startFrame()) * 0.1; setFrom(m_sample->startFrame() - marging); setTo(m_sample->endFrame() + marging); } } -void AudioFileProcessorWaveView::setTo(f_cnt_t to) +void AudioFileProcessorWaveView::setTo(int to) { - m_to = std::min(to, static_cast(m_sample->sampleSize())); + m_to = std::min(to, static_cast(m_sample->sampleSize())); } -void AudioFileProcessorWaveView::setFrom(f_cnt_t from) +void AudioFileProcessorWaveView::setFrom(int from) { m_from = std::max(from, 0); } -f_cnt_t AudioFileProcessorWaveView::range() const +int AudioFileProcessorWaveView::range() const { return m_to - m_from; } @@ -196,7 +196,7 @@ void AudioFileProcessorWaveView::paintEvent(QPaintEvent * pe) p.drawPixmap(s_padding, s_padding, m_graph); const QRect graph_rect(s_padding, s_padding, width() - 2 * s_padding, height() - 2 * s_padding); - const f_cnt_t frames = range(); + const auto frames = range(); m_startFrameX = graph_rect.x() + (m_sample->startFrame() - m_from) * double(graph_rect.width()) / frames; m_endFrameX = graph_rect.x() + (m_sample->endFrame() - m_from) * @@ -273,7 +273,7 @@ void AudioFileProcessorWaveView::paintEvent(QPaintEvent * pe) p.fillRect(s_padding, s_padding, m_graph.width(), 14, g); p.setPen(QColor(255, 255, 255)); - p.setFont(pointSize<8>(font())); + p.setFont(adjustedToPixelSize(font(), 8)); QString length_text; const int length = m_sample->sampleDuration().count(); @@ -341,61 +341,46 @@ void AudioFileProcessorWaveView::updateGraph() void AudioFileProcessorWaveView::zoom(const bool out) { - const f_cnt_t start = m_sample->startFrame(); - const f_cnt_t end = m_sample->endFrame(); - const f_cnt_t frames = m_sample->sampleSize(); - const f_cnt_t d_from = start - m_from; - const f_cnt_t d_to = m_to - end; + const auto start = m_sample->startFrame(); + const auto end = m_sample->endFrame(); + const auto frames = m_sample->sampleSize(); - const f_cnt_t step = qMax(1, qMax(d_from, d_to) / 10); - const f_cnt_t step_from = (out ? - step : step); - const f_cnt_t step_to = (out ? step : - step); + const auto dFrom = start - m_from; + const auto dTo = m_to - end; - const double comp_ratio = double(qMin(d_from, d_to)) - / qMax(1, qMax(d_from, d_to)); + const auto step = std::max(1.0, std::max(dFrom, dTo) / 10.0); + const auto stepFrom = out ? -step : step; + const auto stepTo = out ? step : -step; - f_cnt_t new_from; - f_cnt_t new_to; + const auto boundedFrom = std::clamp(m_from + stepFrom, 0.0, static_cast(start)); + const auto boundedTo = std::clamp(m_to + stepTo, static_cast(end), static_cast(frames)); - if ((out && d_from < d_to) || (! out && d_to < d_from)) - { - new_from = qBound(0, m_from + step_from, start); - new_to = qBound( - end, - m_to + f_cnt_t(step_to * (new_from == m_from ? 1 : comp_ratio)), - frames - ); - } - else - { - new_to = qBound(end, m_to + step_to, frames); - new_from = qBound( - 0, - m_from + f_cnt_t(step_from * (new_to == m_to ? 1 : comp_ratio)), - start - ); - } + const auto compRatio = std::min(dFrom, dTo) / static_cast(std::max(1, std::max(dFrom, dTo))); + const auto toStep = stepFrom * (boundedTo == m_to ? 1 : compRatio); + const auto newFrom = (out && dFrom < dTo) || (!out && dTo < dFrom) + ? boundedFrom + : std::clamp(m_from + toStep, 0.0, static_cast(start)); + + const auto fromStep = stepTo * (boundedFrom == m_from ? 1 : compRatio); + const auto newTo = (out && dFrom < dTo) || (!out && dTo < dFrom) + ? std::clamp(m_to + fromStep, static_cast(end), static_cast(frames)) + : boundedTo; - if (static_cast(new_to - new_from) / m_sample->sampleRate() > 0.05) + if ((newTo - newFrom) / m_sample->sampleRate() > 0.05) { - setFrom(new_from); - setTo(new_to); + setFrom(newFrom); + setTo(newTo); } } void AudioFileProcessorWaveView::slide(int px) { const double fact = qAbs(double(px) / width()); - f_cnt_t step = range() * fact; - if (px > 0) - { - step = -step; - } - - f_cnt_t step_from = qBound(0, m_from + step, m_sample->sampleSize()) - m_from; - f_cnt_t step_to = qBound(m_from + 1, m_to + step, m_sample->sampleSize()) - m_to; + auto step = range() * fact * (px > 0 ? -1 : 1); - step = qAbs(step_from) < qAbs(step_to) ? step_from : step_to; + const auto stepFrom = std::clamp(m_from + step, 0.0, static_cast(m_sample->sampleSize())) - m_from; + const auto stepTo = std::clamp(m_to + step, m_from + 1.0, static_cast(m_sample->sampleSize())) - m_to; + step = std::abs(stepFrom) < std::abs(stepTo) ? stepFrom : stepTo; setFrom(m_from + step); setTo(m_to + step); @@ -476,10 +461,8 @@ void AudioFileProcessorWaveView::reverse() - m_sample->startFrame() ); - const f_cnt_t from = m_from; setFrom(m_sample->sampleSize() - m_to); - setTo(m_sample->sampleSize() - from); - + setTo(m_sample->sampleSize() - m_from); m_reversed = ! m_reversed; } diff --git a/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h b/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h index f40b69d121f..8081d20ca63 100644 --- a/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h +++ b/plugins/AudioFileProcessor/AudioFileProcessorWaveView.h @@ -125,17 +125,17 @@ public slots: Sample const* m_sample; QPixmap m_graph; - f_cnt_t m_from; - f_cnt_t m_to; - f_cnt_t m_last_from; - f_cnt_t m_last_to; + int m_from; + int m_to; + int m_last_from; + int m_last_to; float m_last_amp; knob* m_startKnob; knob* m_endKnob; knob* m_loopKnob; - f_cnt_t m_startFrameX; - f_cnt_t m_endFrameX; - f_cnt_t m_loopFrameX; + int m_startFrameX; + int m_endFrameX; + int m_loopFrameX; bool m_isDragging; QPoint m_draggingLastPoint; DraggingType m_draggingType; @@ -152,9 +152,9 @@ public slots: void updateSampleRange(); private: - void setTo(f_cnt_t to); - void setFrom(f_cnt_t from); - f_cnt_t range() const; + void setTo(int to); + void setFrom(int from); + int range() const; void zoom(const bool out = false); void slide(int px); void slideSamplePointByPx(Point point, int px); diff --git a/plugins/BassBooster/BassBooster.cpp b/plugins/BassBooster/BassBooster.cpp index e6b25b0d141..f12fd6ace8f 100644 --- a/plugins/BassBooster/BassBooster.cpp +++ b/plugins/BassBooster/BassBooster.cpp @@ -69,7 +69,7 @@ BassBoosterEffect::BassBoosterEffect( Model* parent, const Descriptor::SubPlugin -bool BassBoosterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) +bool BassBoosterEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames ) { if( !isEnabled() || !isRunning () ) { @@ -91,23 +91,19 @@ bool BassBoosterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames const float d = dryLevel(); const float w = wetLevel(); - for( fpp_t f = 0; f < frames; ++f ) + for (fpp_t f = 0; f < frames; ++f) { - float gain = const_gain; - if (gainBuffer) { - //process period using sample exact data - gain = gainBuffer->value( f ); - } - //float gain = gainBuffer ? gainBuffer[f] : gain; - m_bbFX.leftFX().setGain( gain ); - m_bbFX.rightFX().setGain( gain); - - auto s = std::array{buf[f][0], buf[f][1]}; - m_bbFX.nextSample( s[0], s[1] ); - - buf[f][0] = d * buf[f][0] + w * s[0]; - buf[f][1] = d * buf[f][1] + w * s[1]; - outSum += buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1]; + auto& currentFrame = buf[f]; + + // Process copy of current sample frame + m_bbFX.setGain(gainBuffer ? gainBuffer->value(f) : const_gain); + auto s = currentFrame; + m_bbFX.nextSample(s); + + // Dry/wet mix + currentFrame = currentFrame * d + s * w; + + outSum += currentFrame.sumOfSquaredAmplitudes(); } checkGate( outSum / frames ); @@ -118,7 +114,7 @@ bool BassBoosterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames inline void BassBoosterEffect::changeFrequency() { - const sample_t fac = Engine::audioEngine()->processingSampleRate() / 44100.0f; + const sample_t fac = Engine::audioEngine()->outputSampleRate() / 44100.0f; m_bbFX.leftFX().setFrequency( m_bbControls.m_freqModel.value() * fac ); m_bbFX.rightFX().setFrequency( m_bbControls.m_freqModel.value() * fac ); diff --git a/plugins/BassBooster/BassBooster.h b/plugins/BassBooster/BassBooster.h index ea3ba2d986e..64c4e354d6b 100644 --- a/plugins/BassBooster/BassBooster.h +++ b/plugins/BassBooster/BassBooster.h @@ -38,7 +38,7 @@ class BassBoosterEffect : public Effect public: BassBoosterEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ); ~BassBoosterEffect() override = default; - bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override; + bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override; EffectControls* controls() override { diff --git a/plugins/BitInvader/BitInvader.cpp b/plugins/BitInvader/BitInvader.cpp index 4685478ff5b..8d9bef4d3c7 100644 --- a/plugins/BitInvader/BitInvader.cpp +++ b/plugins/BitInvader/BitInvader.cpp @@ -110,34 +110,18 @@ sample_t BSynth::nextStringSample( float sample_length ) sample_realindex -= sample_length; } - sample_t sample; - - if (interpolation) { + const auto currentRealIndex = sample_realindex; + const auto currentIndex = static_cast(sample_realindex); + sample_realindex += sample_step; - // find position in shape - int a = static_cast(sample_realindex); - int b; - if (a < (sample_length-1)) { - b = static_cast(sample_realindex+1); - } else { - b = 0; - } - - // Nachkommaanteil - const float frac = fraction( sample_realindex ); - - sample = linearInterpolate( sample_shape[a], sample_shape[b], frac ); - - } else { - // No interpolation - sample_index = static_cast(sample_realindex); - sample = sample_shape[sample_index]; + if (!interpolation) + { + sample_index = currentIndex; + return sample_shape[sample_index]; } - - // progress in shape - sample_realindex += sample_step; - return sample; + const auto nextIndex = currentIndex < sample_length - 1 ? currentIndex + 1 : 0; + return linearInterpolate(sample_shape[currentIndex], sample_shape[nextIndex], fraction(currentRealIndex)); } /*********************************************************************** @@ -272,25 +256,16 @@ QString BitInvader::nodeName() const void BitInvader::playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) + SampleFrame* _working_buffer ) { if (!_n->m_pluginData) { - float factor; - if( !m_normalize.value() ) - { - factor = defaultNormalizationFactor; - } - else - { - factor = m_normalizeFactor; - } - + float factor = !m_normalize.value() ? defaultNormalizationFactor : m_normalizeFactor; _n->m_pluginData = new BSynth( const_cast( m_graph.samples() ), _n, m_interpolation.value(), factor, - Engine::audioEngine()->processingSampleRate() ); + Engine::audioEngine()->outputSampleRate() ); } const fpp_t frames = _n->framesLeftForCurrentPeriod(); @@ -299,11 +274,7 @@ void BitInvader::playNote( NotePlayHandle * _n, auto ps = static_cast(_n->m_pluginData); for( fpp_t frame = offset; frame < frames + offset; ++frame ) { - const sample_t cur = ps->nextStringSample( m_graph.length() ); - for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) - { - _working_buffer[frame][chnl] = cur; - } + _working_buffer[frame] = SampleFrame(ps->nextStringSample(m_graph.length())); } applyRelease( _working_buffer, _n ); diff --git a/plugins/BitInvader/BitInvader.h b/plugins/BitInvader/BitInvader.h index 6dce9db8315..2ed2dde1bee 100644 --- a/plugins/BitInvader/BitInvader.h +++ b/plugins/BitInvader/BitInvader.h @@ -75,7 +75,7 @@ class BitInvader : public Instrument ~BitInvader() override = default; void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; + SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; @@ -85,9 +85,9 @@ class BitInvader : public Instrument QString nodeName() const override; - f_cnt_t desiredReleaseFrames() const override + float desiredReleaseTimeMs() const override { - return( 64 ); + return 1.5f; } gui::PluginView * instantiateView( QWidget * _parent ) override; diff --git a/plugins/Bitcrush/Bitcrush.cpp b/plugins/Bitcrush/Bitcrush.cpp index df4a8605dd8..ea1c43acba1 100644 --- a/plugins/Bitcrush/Bitcrush.cpp +++ b/plugins/Bitcrush/Bitcrush.cpp @@ -59,10 +59,10 @@ Plugin::Descriptor PLUGIN_EXPORT bitcrush_plugin_descriptor = BitcrushEffect::BitcrushEffect( Model * parent, const Descriptor::SubPluginFeatures::Key * key ) : Effect( &bitcrush_plugin_descriptor, parent, key ), m_controls( this ), - m_sampleRate( Engine::audioEngine()->processingSampleRate() ), + m_sampleRate( Engine::audioEngine()->outputSampleRate() ), m_filter( m_sampleRate ) { - m_buffer = new sampleFrame[Engine::audioEngine()->framesPerPeriod() * OS_RATE]; + m_buffer = new SampleFrame[Engine::audioEngine()->framesPerPeriod() * OS_RATE]; m_filter.setLowpass( m_sampleRate * ( CUTOFF_RATIO * OS_RATIO ) ); m_needsUpdate = true; @@ -83,7 +83,7 @@ BitcrushEffect::~BitcrushEffect() void BitcrushEffect::sampleRateChanged() { - m_sampleRate = Engine::audioEngine()->processingSampleRate(); + m_sampleRate = Engine::audioEngine()->outputSampleRate(); m_filter.setSampleRate( m_sampleRate ); m_filter.setLowpass( m_sampleRate * ( CUTOFF_RATIO * OS_RATIO ) ); m_needsUpdate = true; @@ -100,7 +100,7 @@ inline float BitcrushEffect::noise( float amt ) return fastRandf( amt * 2.0f ) - amt; } -bool BitcrushEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) +bool BitcrushEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames ) { if( !isEnabled() || !isRunning () ) { @@ -153,7 +153,7 @@ bool BitcrushEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) // read input buffer and write it to oversampled buffer if( m_rateEnabled ) // rate crushing enabled so do that { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { for( int o = 0; o < OS_RATE; ++o ) { @@ -180,7 +180,7 @@ bool BitcrushEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) } else // rate crushing disabled: simply oversample with zero-order hold { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { for( int o = 0; o < OS_RATE; ++o ) { @@ -196,7 +196,7 @@ bool BitcrushEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) // the oversampled buffer is now written, so filter it to reduce aliasing - for( int f = 0; f < frames * OS_RATE; ++f ) + for (auto f = std::size_t{0}; f < frames * OS_RATE; ++f) { if( qMax( qAbs( m_buffer[f][0] ), qAbs( m_buffer[f][1] ) ) >= 1.0e-10f ) { @@ -225,7 +225,7 @@ bool BitcrushEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) double outSum = 0.0; const float d = dryLevel(); const float w = wetLevel(); - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { float lsum = 0.0f; float rsum = 0.0f; diff --git a/plugins/Bitcrush/Bitcrush.h b/plugins/Bitcrush/Bitcrush.h index a6e5556a297..009c7c02dd0 100644 --- a/plugins/Bitcrush/Bitcrush.h +++ b/plugins/Bitcrush/Bitcrush.h @@ -41,7 +41,7 @@ class BitcrushEffect : public Effect public: BitcrushEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ); ~BitcrushEffect() override; - bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override; + bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override; EffectControls* controls() override { @@ -55,7 +55,7 @@ class BitcrushEffect : public Effect BitcrushControls m_controls; - sampleFrame * m_buffer; + SampleFrame* m_buffer; float m_sampleRate; StereoLinkwitzRiley m_filter; diff --git a/plugins/CarlaBase/CMakeLists.txt b/plugins/CarlaBase/CMakeLists.txt index 8f9e14dd101..6533055a4c3 100644 --- a/plugins/CarlaBase/CMakeLists.txt +++ b/plugins/CarlaBase/CMakeLists.txt @@ -15,7 +15,7 @@ if(LMMS_HAVE_WEAKCARLA) SET(CARLA_NATIVE_LIB carla_native-plugin) ADD_LIBRARY(${CARLA_NATIVE_LIB} SHARED DummyCarla.cpp) - TARGET_INCLUDE_DIRECTORIES(${CARLA_NATIVE_LIB} PUBLIC ${CARLA_INCLUDE_DIRS}) + target_include_directories(${CARLA_NATIVE_LIB} SYSTEM PUBLIC ${CARLA_INCLUDE_DIRS}) INSTALL(TARGETS ${CARLA_NATIVE_LIB} LIBRARY DESTINATION "${PLUGIN_DIR}/optional" RUNTIME DESTINATION "${PLUGIN_DIR}/optional" @@ -31,7 +31,7 @@ if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA) SET(CARLA_INCLUDE_DIRS ${CARLA_INCLUDE_DIRS} PARENT_SCOPE) INCLUDE(BuildPlugin) - INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS}) + include_directories(SYSTEM ${CARLA_INCLUDE_DIRS}) LINK_DIRECTORIES(${CARLA_LIBRARY_DIRS}) LINK_LIBRARIES(${CARLA_LIBRARIES}) BUILD_PLUGIN(carlabase Carla.cpp Carla.h diff --git a/plugins/CarlaBase/Carla.cpp b/plugins/CarlaBase/Carla.cpp index 819736e928b..e81e655501a 100644 --- a/plugins/CarlaBase/Carla.cpp +++ b/plugins/CarlaBase/Carla.cpp @@ -150,7 +150,7 @@ static const char* host_ui_save_file(NativeHostHandle, bool isDir, const char* t CarlaInstrument::CarlaInstrument(InstrumentTrack* const instrumentTrack, const Descriptor* const descriptor, const bool isPatchbay) - : Instrument(instrumentTrack, descriptor), + : Instrument(instrumentTrack, descriptor, nullptr, Flag::IsSingleStreamed | Flag::IsMidiBased | Flag::IsNotBendable), kIsPatchbay(isPatchbay), fHandle(nullptr), fDescriptor(isPatchbay ? carla_get_native_patchbay_plugin() : carla_get_native_rack_plugin()), @@ -205,9 +205,9 @@ CarlaInstrument::CarlaInstrument(InstrumentTrack* const instrumentTrack, const D m_paramsCompleter->setCompletionMode(QCompleter::PopupCompletion); // Add static amount of CarlaParamFloatModel's. - int paramCount = fDescriptor->get_parameter_count(fHandle); + const auto paramCount = fDescriptor->get_parameter_count(fHandle); m_paramModels.reserve(paramCount); - for (int i=0; i < paramCount; ++i) + for (auto i = std::size_t{0}; i < paramCount; ++i) { m_paramModels.push_back(new CarlaParamFloatModel(this)); connect(m_paramModels[i], &CarlaParamFloatModel::dataChanged, @@ -259,7 +259,7 @@ uint32_t CarlaInstrument::handleGetBufferSize() const double CarlaInstrument::handleGetSampleRate() const { - return Engine::audioEngine()->processingSampleRate(); + return Engine::audioEngine()->outputSampleRate(); } bool CarlaInstrument::handleIsOffline() const @@ -274,7 +274,7 @@ const NativeTimeInfo* CarlaInstrument::handleGetTimeInfo() const void CarlaInstrument::handleUiParameterChanged(const uint32_t index, const float value) const { - if (m_paramModels.count() > index) + if (m_paramModels.size() > index) { m_paramModels[index]->setValue(value); } @@ -343,11 +343,6 @@ intptr_t CarlaInstrument::handleDispatcher(const NativeHostDispatcherOpcode opco // ------------------------------------------------------------------- -Instrument::Flags CarlaInstrument::flags() const -{ - return Flag::IsSingleStreamed | Flag::IsMidiBased | Flag::IsNotBendable; -} - QString CarlaInstrument::nodeName() const { return descriptor()->name; @@ -374,7 +369,7 @@ void CarlaInstrument::saveSettings(QDomDocument& doc, QDomElement& parent) std::free(state); #if CARLA_VERSION_HEX >= CARLA_MIN_PARAM_VERSION - for (uint32_t index = 0; index < m_paramModels.count(); ++index) + for (uint32_t index = 0; index < m_paramModels.size(); ++index) { QString idStr = CARLA_SETTING_PREFIX + QString::number(index); m_paramModels[index]->saveSettings(doc, parent, idStr); @@ -444,7 +439,7 @@ void CarlaInstrument::refreshParams(bool init) void CarlaInstrument::clearParamModels() { //Delete the models, this also disconnects all connections (automation and controller connections) - for (uint32_t index=0; index < m_paramModels.count(); ++index) + for (uint32_t index = 0; index < m_paramModels.size(); ++index) { delete m_paramModels[index]; } @@ -500,11 +495,11 @@ void CarlaInstrument::loadSettings(const QDomElement& elem) #endif } -void CarlaInstrument::play(sampleFrame* workingBuffer) +void CarlaInstrument::play(SampleFrame* workingBuffer) { const uint bufsize = Engine::audioEngine()->framesPerPeriod(); - std::memset(workingBuffer, 0, sizeof(sample_t)*bufsize*DEFAULT_CHANNELS); + zeroSampleFrames(workingBuffer, bufsize); if (fHandle == nullptr) { @@ -632,7 +627,7 @@ CarlaInstrumentView::CarlaInstrumentView(CarlaInstrument* const instrument, QWid m_toggleUIButton->setCheckable( true ); m_toggleUIButton->setChecked( false ); m_toggleUIButton->setIcon( embed::getIconPixmap( "zoom" ) ); - m_toggleUIButton->setFont( pointSize<8>( m_toggleUIButton->font() ) ); + m_toggleUIButton->setFont(adjustedToPixelSize(m_toggleUIButton->font(), 8)); connect( m_toggleUIButton, SIGNAL( clicked(bool) ), this, SLOT( toggleUI( bool ) ) ); m_toggleUIButton->setToolTip( @@ -642,7 +637,7 @@ CarlaInstrumentView::CarlaInstrumentView(CarlaInstrument* const instrument, QWid m_toggleParamsWindowButton = new QPushButton(tr("Params"), this); m_toggleParamsWindowButton->setIcon(embed::getIconPixmap("controller")); m_toggleParamsWindowButton->setCheckable(true); - m_toggleParamsWindowButton->setFont(pointSize<8>(m_toggleParamsWindowButton->font())); + m_toggleParamsWindowButton->setFont(adjustedToPixelSize(m_toggleParamsWindowButton->font(), 8)); #if CARLA_VERSION_HEX < CARLA_MIN_PARAM_VERSION m_toggleParamsWindowButton->setEnabled(false); m_toggleParamsWindowButton->setToolTip(tr("Available from Carla version 2.1 and up.")); @@ -904,7 +899,7 @@ CarlaParamsView::~CarlaParamsView() m_carlaInstrumentView->m_paramsView = nullptr; // Clear models - if (m_carlaInstrument->m_paramModels.isEmpty() == false) + if (!m_carlaInstrument->m_paramModels.empty()) { m_carlaInstrument->clearParamModels(); } @@ -935,7 +930,7 @@ void CarlaParamsView::filterKnobs() m_maxColumns = m_inputScrollArea->width() / maxKnobWidth; QString text = m_paramsFilterLineEdit->text(); - for (uint32_t i=0; i < m_knobs.count(); ++i) + for (uint32_t i = 0; i < m_knobs.size(); ++i) { // Don't show disabled (unused) knobs. if (!m_carlaInstrument->m_paramModels[i]->enabled()) @@ -980,7 +975,7 @@ void CarlaParamsView::filterKnobs() void CarlaParamsView::refreshKnobs() { // Make sure all the knobs are deleted. - for (uint32_t i=0; i < m_knobs.count(); ++i) + for (uint32_t i = 0; i < m_knobs.size(); ++i) { delete m_knobs[i]; // Delete knob widgets itself. } @@ -1001,15 +996,15 @@ void CarlaParamsView::refreshKnobs() m_maxKnobWidthPerGroup[i] = 0; } - if (!m_carlaInstrument->m_paramModels.count()) { return; } + if (m_carlaInstrument->m_paramModels.empty()) { return; } // Make room in QList m_knobs - m_knobs.reserve(m_carlaInstrument->m_paramModels.count()); + m_knobs.reserve(m_carlaInstrument->m_paramModels.size()); QStringList groupNameList; groupNameList.reserve(m_carlaInstrument->m_paramGroupCount); - for (uint32_t i=0; i < m_carlaInstrument->m_paramModels.count(); ++i) + for (uint32_t i = 0; i < m_carlaInstrument->m_paramModels.size(); ++i) { bool enabled = m_carlaInstrument->m_paramModels[i]->enabled(); m_knobs.push_back(new Knob(KnobType::Dark28, m_inputScrollAreaWidgetContent)); @@ -1110,22 +1105,21 @@ void CarlaParamsView::addKnob(uint32_t index) void CarlaParamsView::clearKnobs() { // Remove knobs from layout. - for (uint16_t i=0; i < m_knobs.count(); ++i) + for (uint16_t i = 0; i < m_knobs.size(); ++i) { m_knobs[i]->close(); } // Remove spacers - QLayoutItem* item; for (int16_t i=m_inputScrollAreaLayout->count() - 1; i > 0; i--) { - item = m_inputScrollAreaLayout->takeAt(i); + auto item = m_inputScrollAreaLayout->takeAt(i); if (item->widget()) {continue;} delete item; } for (int16_t i=m_outputScrollAreaLayout->count() - 1; i > 0; i--) { - item = m_outputScrollAreaLayout->takeAt(i); + auto item = m_outputScrollAreaLayout->takeAt(i); if (item->widget()) {continue;} delete item; } diff --git a/plugins/CarlaBase/Carla.h b/plugins/CarlaBase/Carla.h index 3d0e424a277..f833c4668e6 100644 --- a/plugins/CarlaBase/Carla.h +++ b/plugins/CarlaBase/Carla.h @@ -29,6 +29,8 @@ #define CARLA_MIN_PARAM_VERSION 0x020090 #define CARLA_VERSION_HEX_3 0x30000 +#include + // qt #include #include @@ -37,13 +39,13 @@ // carla/source/includes #include "carlabase_export.h" -#include "CarlaDefines.h" +#include #if CARLA_VERSION_HEX >= 0x010911 - #include "CarlaNativePlugin.h" + #include #else - #include "CarlaBackend.h" - #include "CarlaNative.h" - #include "CarlaUtils.h" + #include + #include + #include CARLA_EXPORT const NativePluginDescriptor* carla_get_native_patchbay_plugin(); @@ -81,7 +83,7 @@ class CarlaParamFloatModel : public FloatModel { public: CarlaParamFloatModel(Model * parent): - FloatModel(0.0, 0.0, 1.0, 0.001, parent, "Unused"), + FloatModel(0.f, 0.f, 1.f, 0.001f, parent, "Unused"), m_isOutput(false), m_isEnabled(false) { @@ -190,11 +192,10 @@ class CARLABASE_EXPORT CarlaInstrument : public Instrument intptr_t handleDispatcher(const NativeHostDispatcherOpcode opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt); // LMMS functions - Flags flags() const override; QString nodeName() const override; void saveSettings(QDomDocument& doc, QDomElement& parent) override; void loadSettings(const QDomElement& elem) override; - void play(sampleFrame* workingBuffer) override; + void play(SampleFrame* workingBuffer) override; bool handleMidiEvent(const MidiEvent& event, const TimePos& time, f_cnt_t offset) override; gui::PluginView* instantiateView(QWidget* parent) override; @@ -224,7 +225,7 @@ private slots: QMutex fMutex; uint8_t m_paramGroupCount; - QList m_paramModels; + std::vector m_paramModels; QDomElement m_settingsElem; QCompleter* m_paramsCompleter; @@ -352,7 +353,7 @@ private slots: CarlaInstrument* const m_carlaInstrument; CarlaInstrumentView* const m_carlaInstrumentView; - QList m_knobs; + std::vector m_knobs; // Keep track of the biggest knob width per group QList m_maxKnobWidthPerGroup; diff --git a/plugins/CarlaBase/DummyCarla.cpp b/plugins/CarlaBase/DummyCarla.cpp index 572bdf4bc6f..9a5a137615e 100644 --- a/plugins/CarlaBase/DummyCarla.cpp +++ b/plugins/CarlaBase/DummyCarla.cpp @@ -1,6 +1,6 @@ // A dummy Carla interface #define BUILDING_CARLA -#include "CarlaNativePlugin.h" +#include #ifndef CARLA_PLUGIN_EXPORT #define CARLA_PLUGIN_EXPORT CARLA_EXPORT diff --git a/plugins/CarlaPatchbay/CMakeLists.txt b/plugins/CarlaPatchbay/CMakeLists.txt index 457e61e6a83..8f6efd5ea2c 100644 --- a/plugins/CarlaPatchbay/CMakeLists.txt +++ b/plugins/CarlaPatchbay/CMakeLists.txt @@ -1,7 +1,8 @@ if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA) ADD_DEFINITIONS(-DCARLA_PLUGIN_PATCHBAY -DCARLA_PLUGIN_SYNTH) INCLUDE(BuildPlugin) - INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/../CarlaBase") + include_directories(SYSTEM ${CARLA_INCLUDE_DIRS}) + include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../CarlaBase") LINK_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/../CarlaBase" ${CARLA_LIBRARY_DIRS}) LINK_LIBRARIES(carlabase) diff --git a/plugins/CarlaRack/CMakeLists.txt b/plugins/CarlaRack/CMakeLists.txt index 8373688c1ba..d5328562405 100644 --- a/plugins/CarlaRack/CMakeLists.txt +++ b/plugins/CarlaRack/CMakeLists.txt @@ -1,7 +1,8 @@ if(LMMS_HAVE_CARLA OR LMMS_HAVE_WEAKCARLA) ADD_DEFINITIONS(-DCARLA_PLUGIN_RACK -DCARLA_PLUGIN_SYNTH) INCLUDE(BuildPlugin) - INCLUDE_DIRECTORIES(${CARLA_INCLUDE_DIRS} "${CMAKE_CURRENT_SOURCE_DIR}/../CarlaBase") + include_directories(SYSTEM ${CARLA_INCLUDE_DIRS}) + include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../CarlaBase") LINK_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/../CarlaBase" ${CARLA_LIBRARY_DIRS}) LINK_LIBRARIES(carlabase) diff --git a/plugins/Compressor/Compressor.cpp b/plugins/Compressor/Compressor.cpp index e59562b0279..c04893361b1 100755 --- a/plugins/Compressor/Compressor.cpp +++ b/plugins/Compressor/Compressor.cpp @@ -56,7 +56,7 @@ CompressorEffect::CompressorEffect(Model* parent, const Descriptor::SubPluginFea Effect(&compressor_plugin_descriptor, parent, key), m_compressorControls(this) { - m_sampleRate = Engine::audioEngine()->processingSampleRate(); + m_sampleRate = Engine::audioEngine()->outputSampleRate(); m_yL[0] = m_yL[1] = COMP_NOISE_FLOOR; @@ -233,7 +233,7 @@ void CompressorEffect::calcMix() -bool CompressorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames) +bool CompressorEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames) { if (!isEnabled() || !isRunning()) { @@ -560,7 +560,7 @@ inline void CompressorEffect::calcTiltFilter(sample_t inputSample, sample_t &out void CompressorEffect::changeSampleRate() { - m_sampleRate = Engine::audioEngine()->processingSampleRate(); + m_sampleRate = Engine::audioEngine()->outputSampleRate(); m_coeffPrecalc = COMP_LOG / (m_sampleRate * 0.001f); @@ -590,6 +590,9 @@ void CompressorEffect::changeSampleRate() calcInGain(); calcTiltCoeffs(); calcMix(); + + calcAutoAttack(); + calcAutoRelease(); } diff --git a/plugins/Compressor/Compressor.h b/plugins/Compressor/Compressor.h index 3fc90b7523d..af322de9735 100755 --- a/plugins/Compressor/Compressor.h +++ b/plugins/Compressor/Compressor.h @@ -35,7 +35,7 @@ namespace lmms { -constexpr float COMP_LOG = -2.2; +constexpr float COMP_LOG = -2.2f; class CompressorEffect : public Effect { @@ -43,7 +43,7 @@ class CompressorEffect : public Effect public: CompressorEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key); ~CompressorEffect() override = default; - bool processAudioBuffer(sampleFrame* buf, const fpp_t frames) override; + bool processAudioBuffer(SampleFrame* buf, const fpp_t frames) override; EffectControls* controls() override { @@ -105,7 +105,7 @@ private slots: float m_coeffPrecalc; - sampleFrame m_maxLookaheadVal; + SampleFrame m_maxLookaheadVal; int m_maxLookaheadTimer[2] = {1, 1}; diff --git a/plugins/Compressor/CompressorControlDialog.cpp b/plugins/Compressor/CompressorControlDialog.cpp index ab81c84ec2d..d7350ba5966 100755 --- a/plugins/Compressor/CompressorControlDialog.cpp +++ b/plugins/Compressor/CompressorControlDialog.cpp @@ -45,26 +45,12 @@ namespace lmms::gui CompressorControlDialog::CompressorControlDialog(CompressorControls* controls) : EffectControlDialog(controls), - m_controls(controls), - m_inVolAreaColor(209, 216, 228, 17), - m_inVolColor(209, 216, 228, 100), - m_outVolAreaColor(209, 216, 228, 30), - m_outVolColor(209, 216, 228, 240), - m_gainReductionColor(180, 100, 100, 210), - m_kneeColor(39, 171, 95, 255), - m_kneeColor2(9, 171, 160, 255), - m_threshColor(39, 171, 95, 100), - m_textColor(209, 216, 228, 50), - m_graphColor(209, 216, 228, 50), - m_resetColor(200, 100, 15, 200) + m_controls(controls) { setAutoFillBackground(false); setAttribute(Qt::WA_OpaquePaintEvent, true); setAttribute(Qt::WA_NoSystemBackground, true); - QPalette pal; - pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork")); - setPalette(pal); setMinimumSize(MIN_COMP_SCREEN_X, MIN_COMP_SCREEN_Y); resize(COMP_SCREEN_X, COMP_SCREEN_Y); @@ -615,7 +601,7 @@ void CompressorControlDialog::paintEvent(QPaintEvent *event) m_p.begin(this); m_p.setCompositionMode(QPainter::CompositionMode_Source); - m_p.fillRect(0, 0, m_windowSizeX, m_windowSizeY, QColor("transparent")); + m_p.fillRect(0, 0, m_windowSizeX, m_windowSizeY, m_backgroundColor); m_p.setCompositionMode(QPainter::CompositionMode_SourceOver); m_p.drawPixmap(0, 0, m_graphPixmap); @@ -683,7 +669,7 @@ void CompressorControlDialog::drawGraph() // Redraw graph m_p.setPen(QPen(m_graphColor, 1)); - for (int i = 1; i < m_dbRange / COMP_GRID_SPACING + 1; ++i) + for (int i = 0; i < m_dbRange / COMP_GRID_SPACING + 1; ++i) { m_p.drawLine(0, dbfsToYPoint(-COMP_GRID_SPACING * i), m_windowSizeX, dbfsToYPoint(-COMP_GRID_SPACING * i)); m_p.drawLine(dbfsToXPoint(-COMP_GRID_SPACING * i), 0, dbfsToXPoint(-COMP_GRID_SPACING * i), m_kneeWindowSizeY); diff --git a/plugins/Compressor/CompressorControlDialog.h b/plugins/Compressor/CompressorControlDialog.h index a61482ad89b..108c421a06d 100755 --- a/plugins/Compressor/CompressorControlDialog.h +++ b/plugins/Compressor/CompressorControlDialog.h @@ -36,7 +36,7 @@ class QLabel; namespace lmms { -constexpr float COMP_NOISE_FLOOR = 0.000001;// -120 dbFs +constexpr float COMP_NOISE_FLOOR = 0.000001f;// -120 dbFs class CompressorControls; @@ -86,6 +86,7 @@ class CompressorControlDialog : public EffectControlDialog Q_PROPERTY(QColor textColor MEMBER m_textColor) Q_PROPERTY(QColor graphColor MEMBER m_graphColor) Q_PROPERTY(QColor resetColor MEMBER m_resetColor) + Q_PROPERTY(QColor backgroundColor MEMBER m_backgroundColor) protected: void resizeEvent(QResizeEvent *event) override; @@ -150,6 +151,7 @@ private slots: QColor m_textColor = QColor(209, 216, 228, 50); QColor m_graphColor = QColor(209, 216, 228, 50); QColor m_resetColor = QColor(200, 100, 15, 200); + QColor m_backgroundColor = QColor(7, 8, 9, 255); float m_peakAvg; float m_gainAvg; diff --git a/plugins/Compressor/artwork.png b/plugins/Compressor/artwork.png deleted file mode 100755 index 8f94e3d494b..00000000000 Binary files a/plugins/Compressor/artwork.png and /dev/null differ diff --git a/plugins/CrossoverEQ/CrossoverEQ.cpp b/plugins/CrossoverEQ/CrossoverEQ.cpp index 4dca94a4ccd..2142d040c76 100644 --- a/plugins/CrossoverEQ/CrossoverEQ.cpp +++ b/plugins/CrossoverEQ/CrossoverEQ.cpp @@ -55,7 +55,7 @@ Plugin::Descriptor PLUGIN_EXPORT crossovereq_plugin_descriptor = CrossoverEQEffect::CrossoverEQEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ) : Effect( &crossovereq_plugin_descriptor, parent, key ), m_controls( this ), - m_sampleRate( Engine::audioEngine()->processingSampleRate() ), + m_sampleRate( Engine::audioEngine()->outputSampleRate() ), m_lp1( m_sampleRate ), m_lp2( m_sampleRate ), m_lp3( m_sampleRate ), @@ -64,9 +64,9 @@ CrossoverEQEffect::CrossoverEQEffect( Model* parent, const Descriptor::SubPlugin m_hp4( m_sampleRate ), m_needsUpdate( true ) { - m_tmp2 = new sampleFrame[Engine::audioEngine()->framesPerPeriod()]; - m_tmp1 = new sampleFrame[Engine::audioEngine()->framesPerPeriod()]; - m_work = new sampleFrame[Engine::audioEngine()->framesPerPeriod()]; + m_tmp2 = new SampleFrame[Engine::audioEngine()->framesPerPeriod()]; + m_tmp1 = new SampleFrame[Engine::audioEngine()->framesPerPeriod()]; + m_work = new SampleFrame[Engine::audioEngine()->framesPerPeriod()]; } CrossoverEQEffect::~CrossoverEQEffect() @@ -78,7 +78,7 @@ CrossoverEQEffect::~CrossoverEQEffect() void CrossoverEQEffect::sampleRateChanged() { - m_sampleRate = Engine::audioEngine()->processingSampleRate(); + m_sampleRate = Engine::audioEngine()->outputSampleRate(); m_lp1.setSampleRate( m_sampleRate ); m_lp2.setSampleRate( m_sampleRate ); m_lp3.setSampleRate( m_sampleRate ); @@ -89,7 +89,7 @@ void CrossoverEQEffect::sampleRateChanged() } -bool CrossoverEQEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) +bool CrossoverEQEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames ) { if( !isEnabled() || !isRunning () ) { @@ -139,10 +139,10 @@ bool CrossoverEQEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames m_needsUpdate = false; - memset( m_work, 0, sizeof( sampleFrame ) * frames ); + zeroSampleFrames(m_work, frames); // run temp bands - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { m_tmp1[f][0] = m_lp2.update( buf[f][0], 0 ); m_tmp1[f][1] = m_lp2.update( buf[f][1], 1 ); @@ -153,7 +153,7 @@ bool CrossoverEQEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames // run band 1 if( mute1 ) { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { m_work[f][0] += m_lp1.update( m_tmp1[f][0], 0 ) * m_gain1; m_work[f][1] += m_lp1.update( m_tmp1[f][1], 1 ) * m_gain1; @@ -163,7 +163,7 @@ bool CrossoverEQEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames // run band 2 if( mute2 ) { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { m_work[f][0] += m_hp2.update( m_tmp1[f][0], 0 ) * m_gain2; m_work[f][1] += m_hp2.update( m_tmp1[f][1], 1 ) * m_gain2; @@ -173,7 +173,7 @@ bool CrossoverEQEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames // run band 3 if( mute3 ) { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { m_work[f][0] += m_lp3.update( m_tmp2[f][0], 0 ) * m_gain3; m_work[f][1] += m_lp3.update( m_tmp2[f][1], 1 ) * m_gain3; @@ -183,7 +183,7 @@ bool CrossoverEQEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames // run band 4 if( mute4 ) { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { m_work[f][0] += m_hp4.update( m_tmp2[f][0], 0 ) * m_gain4; m_work[f][1] += m_hp4.update( m_tmp2[f][1], 1 ) * m_gain4; @@ -193,7 +193,7 @@ bool CrossoverEQEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames const float d = dryLevel(); const float w = wetLevel(); double outSum = 0.0; - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { buf[f][0] = d * buf[f][0] + w * m_work[f][0]; buf[f][1] = d * buf[f][1] + w * m_work[f][1]; diff --git a/plugins/CrossoverEQ/CrossoverEQ.h b/plugins/CrossoverEQ/CrossoverEQ.h index fe9b4541bb0..078e51c2189 100644 --- a/plugins/CrossoverEQ/CrossoverEQ.h +++ b/plugins/CrossoverEQ/CrossoverEQ.h @@ -40,7 +40,7 @@ class CrossoverEQEffect : public Effect public: CrossoverEQEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ); ~CrossoverEQEffect() override; - bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override; + bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override; EffectControls* controls() override { @@ -69,9 +69,9 @@ class CrossoverEQEffect : public Effect StereoLinkwitzRiley m_hp3; StereoLinkwitzRiley m_hp4; - sampleFrame * m_tmp1; - sampleFrame * m_tmp2; - sampleFrame * m_work; + SampleFrame* m_tmp1; + SampleFrame* m_tmp2; + SampleFrame* m_work; bool m_needsUpdate; diff --git a/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp index 12b560b2370..a4f44f5d305 100644 --- a/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp +++ b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp @@ -32,6 +32,9 @@ #include "Knob.h" #include "Fader.h" +#include + + namespace lmms::gui { @@ -64,30 +67,32 @@ CrossoverEQControlDialog::CrossoverEQControlDialog( CrossoverEQControls * contro xover34->setLabel( "3/4" ); xover34->setHintText( tr( "Band 3/4 crossover:" ), " Hz" ); - m_fader_bg = QPixmap( PLUGIN_NAME::getIconPixmap( "fader_bg" ) ); - m_fader_empty = QPixmap( PLUGIN_NAME::getIconPixmap( "fader_empty" ) ); - m_fader_knob = QPixmap( PLUGIN_NAME::getIconPixmap( "fader_knob2" ) ); + QPixmap const fader_knob(PLUGIN_NAME::getIconPixmap("fader_knob2")); // faders - auto gain1 = new Fader(&controls->m_gain1, tr("Band 1 gain"), this, &m_fader_bg, &m_fader_empty, &m_fader_knob); + auto gain1 = new Fader(&controls->m_gain1, tr("Band 1 gain"), this, fader_knob); gain1->move( 7, 56 ); gain1->setDisplayConversion( false ); gain1->setHintText( tr( "Band 1 gain:" ), " dBFS" ); + gain1->setRenderUnityLine(false); - auto gain2 = new Fader(&controls->m_gain2, tr("Band 2 gain"), this, &m_fader_bg, &m_fader_empty, &m_fader_knob); + auto gain2 = new Fader(&controls->m_gain2, tr("Band 2 gain"), this, fader_knob); gain2->move( 47, 56 ); gain2->setDisplayConversion( false ); gain2->setHintText( tr( "Band 2 gain:" ), " dBFS" ); + gain2->setRenderUnityLine(false); - auto gain3 = new Fader(&controls->m_gain3, tr("Band 3 gain"), this, &m_fader_bg, &m_fader_empty, &m_fader_knob); + auto gain3 = new Fader(&controls->m_gain3, tr("Band 3 gain"), this, fader_knob); gain3->move( 87, 56 ); gain3->setDisplayConversion( false ); gain3->setHintText( tr( "Band 3 gain:" ), " dBFS" ); + gain3->setRenderUnityLine(false); - auto gain4 = new Fader(&controls->m_gain4, tr("Band 4 gain"), this, &m_fader_bg, &m_fader_empty, &m_fader_knob); + auto gain4 = new Fader(&controls->m_gain4, tr("Band 4 gain"), this, fader_knob); gain4->move( 127, 56 ); gain4->setDisplayConversion( false ); gain4->setHintText( tr( "Band 4 gain:" ), " dBFS" ); + gain4->setRenderUnityLine(false); // leds auto mute1 = new LedCheckBox("", this, tr("Band 1 mute"), LedCheckBox::LedColor::Green); diff --git a/plugins/CrossoverEQ/CrossoverEQControlDialog.h b/plugins/CrossoverEQ/CrossoverEQControlDialog.h index 9ddb5d9bfb2..0f25600f9f0 100644 --- a/plugins/CrossoverEQ/CrossoverEQControlDialog.h +++ b/plugins/CrossoverEQ/CrossoverEQControlDialog.h @@ -27,7 +27,6 @@ #ifndef CROSSOVEREQ_CONTROL_DIALOG_H #define CROSSOVEREQ_CONTROL_DIALOG_H -#include #include "EffectControlDialog.h" namespace lmms @@ -46,11 +45,6 @@ class CrossoverEQControlDialog : public EffectControlDialog public: CrossoverEQControlDialog( CrossoverEQControls * controls ); ~CrossoverEQControlDialog() override = default; - -private: - QPixmap m_fader_bg; - QPixmap m_fader_empty; - QPixmap m_fader_knob; }; diff --git a/plugins/CrossoverEQ/fader_bg.png b/plugins/CrossoverEQ/fader_bg.png deleted file mode 100644 index ca4eedafdc9..00000000000 Binary files a/plugins/CrossoverEQ/fader_bg.png and /dev/null differ diff --git a/plugins/CrossoverEQ/fader_empty.png b/plugins/CrossoverEQ/fader_empty.png deleted file mode 100644 index 797a0d3bc2d..00000000000 Binary files a/plugins/CrossoverEQ/fader_empty.png and /dev/null differ diff --git a/plugins/Delay/DelayControls.cpp b/plugins/Delay/DelayControls.cpp index b31488f721d..c1ad7315337 100644 --- a/plugins/Delay/DelayControls.cpp +++ b/plugins/Delay/DelayControls.cpp @@ -34,11 +34,11 @@ namespace lmms DelayControls::DelayControls( DelayEffect* effect ): EffectControls( effect ), m_effect ( effect ), - m_delayTimeModel( 0.5, 0.01, 5.0, 0.0001, 5000.0, this, tr( "Delay samples" )) , - m_feedbackModel(0.0f,0.0f,1.0f,0.01f,this,tr( "Feedback" ) ), - m_lfoTimeModel(2.0, 0.01, 5.0, 0.0001, 20000.0, this, tr( "LFO frequency" ) ), - m_lfoAmountModel(0.0, 0.0, 0.5, 0.0001, 2000.0, this, tr ( "LFO amount" ) ), - m_outGainModel( 0.0, -60.0, 20.0, 0.01, this, tr( "Output gain" ) ) + m_delayTimeModel(0.5f, 0.01f, 5.f, 0.0001f, 5000.f, this, tr("Delay samples")) , + m_feedbackModel(0.0f, 0.0f, 1.0f, 0.01f, this, tr("Feedback")), + m_lfoTimeModel(2.f, 0.01f, 5.f, 0.0001f, 20000.f, this, tr("LFO frequency")), + m_lfoAmountModel(0.f, 0.f, 0.5f, 0.0001f, 2000.f, this, tr("LFO amount")), + m_outGainModel(0.f, -60.f, 20.f, 0.01f, this, tr("Output gain")) { connect( Engine::audioEngine(), SIGNAL( sampleRateChanged() ), this, SLOT( changeSampleRate() ) ); m_outPeakL = 0.0; @@ -77,4 +77,4 @@ void DelayControls::changeSampleRate() } -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/plugins/Delay/DelayEffect.cpp b/plugins/Delay/DelayEffect.cpp index 05204f355ee..4e90c0fa899 100644 --- a/plugins/Delay/DelayEffect.cpp +++ b/plugins/Delay/DelayEffect.cpp @@ -58,8 +58,8 @@ DelayEffect::DelayEffect( Model* parent, const Plugin::Descriptor::SubPluginFeat m_delayControls( this ) { m_delay = 0; - m_delay = new StereoDelay( 20, Engine::audioEngine()->processingSampleRate() ); - m_lfo = new Lfo( Engine::audioEngine()->processingSampleRate() ); + m_delay = new StereoDelay( 20, Engine::audioEngine()->outputSampleRate() ); + m_lfo = new Lfo( Engine::audioEngine()->outputSampleRate() ); m_outGain = 1.0; } @@ -81,19 +81,18 @@ DelayEffect::~DelayEffect() -bool DelayEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) +bool DelayEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames ) { if( !isEnabled() || !isRunning () ) { return( false ); } double outSum = 0.0; - const float sr = Engine::audioEngine()->processingSampleRate(); + const float sr = Engine::audioEngine()->outputSampleRate(); const float d = dryLevel(); const float w = wetLevel(); - auto dryS = std::array{}; - float lPeak = 0.0; - float rPeak = 0.0; + + SampleFrame peak; float length = m_delayControls.m_delayTimeModel.value(); float amplitude = m_delayControls.m_lfoAmountModel.value() * sr; float lfoTime = 1.0 / m_delayControls.m_lfoTimeModel.value(); @@ -115,28 +114,29 @@ bool DelayEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) { m_outGain = dbfsToAmp( m_delayControls.m_outGainModel.value() ); } - int sampleLength; - for( fpp_t f = 0; f < frames; ++f ) + + for (fpp_t f = 0; f < frames; ++f) { - dryS[0] = buf[f][0]; - dryS[1] = buf[f][1]; + auto& currentFrame = buf[f]; + const auto dryS = currentFrame; + // Prepare delay for current sample m_delay->setFeedback( *feedbackPtr ); m_lfo->setFrequency( *lfoTimePtr ); - sampleLength = *lengthPtr * Engine::audioEngine()->processingSampleRate(); - m_currentLength = sampleLength; + m_currentLength = static_cast(*lengthPtr * Engine::audioEngine()->outputSampleRate()); m_delay->setLength( m_currentLength + ( *amplitudePtr * ( float )m_lfo->tick() ) ); - m_delay->tick( buf[f] ); - buf[f][0] *= m_outGain; - buf[f][1] *= m_outGain; + // Process the wet signal + m_delay->tick( currentFrame ); + currentFrame *= m_outGain; - lPeak = buf[f][0] > lPeak ? buf[f][0] : lPeak; - rPeak = buf[f][1] > rPeak ? buf[f][1] : rPeak; + // Calculate peak of wet signal + peak = peak.absMax(currentFrame); - buf[f][0] = ( d * dryS[0] ) + ( w * buf[f][0] ); - buf[f][1] = ( d * dryS[1] ) + ( w * buf[f][1] ); - outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; + // Dry/wet mix + currentFrame = dryS * d + currentFrame * w; + + outSum += currentFrame.sumOfSquaredAmplitudes(); lengthPtr += lengthInc; amplitudePtr += amplitudeInc; @@ -144,16 +144,16 @@ bool DelayEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) feedbackPtr += feedbackInc; } checkGate( outSum / frames ); - m_delayControls.m_outPeakL = lPeak; - m_delayControls.m_outPeakR = rPeak; + m_delayControls.m_outPeakL = peak.left(); + m_delayControls.m_outPeakR = peak.right(); return isRunning(); } void DelayEffect::changeSampleRate() { - m_lfo->setSampleRate( Engine::audioEngine()->processingSampleRate() ); - m_delay->setSampleRate( Engine::audioEngine()->processingSampleRate() ); + m_lfo->setSampleRate( Engine::audioEngine()->outputSampleRate() ); + m_delay->setSampleRate( Engine::audioEngine()->outputSampleRate() ); } diff --git a/plugins/Delay/DelayEffect.h b/plugins/Delay/DelayEffect.h index b6c090ff0da..b7e2cfef032 100644 --- a/plugins/Delay/DelayEffect.h +++ b/plugins/Delay/DelayEffect.h @@ -39,7 +39,7 @@ class DelayEffect : public Effect public: DelayEffect(Model* parent , const Descriptor::SubPluginFeatures::Key* key ); ~DelayEffect() override; - bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override; + bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override; EffectControls* controls() override { return &m_delayControls; diff --git a/plugins/Delay/StereoDelay.cpp b/plugins/Delay/StereoDelay.cpp index e4b8b5641d0..3187b3f2a6a 100644 --- a/plugins/Delay/StereoDelay.cpp +++ b/plugins/Delay/StereoDelay.cpp @@ -23,7 +23,9 @@ */ #include "StereoDelay.h" + #include "lmms_basics.h" +#include "SampleFrame.h" namespace lmms { @@ -32,9 +34,9 @@ namespace lmms StereoDelay::StereoDelay( int maxTime, int sampleRate ) { m_buffer = 0; - m_maxTime = maxTime; + m_maxTime = static_cast(maxTime); m_maxLength = maxTime * sampleRate; - m_length = m_maxLength; + m_length = static_cast(m_maxLength); m_writeIndex = 0; m_feedback = 0.0f; @@ -55,10 +57,10 @@ StereoDelay::~StereoDelay() -void StereoDelay::tick( sampleFrame& frame ) +void StereoDelay::tick( SampleFrame& frame ) { m_writeIndex = ( m_writeIndex + 1 ) % ( int )m_maxLength; - int readIndex = m_writeIndex - m_length; + int readIndex = m_writeIndex - static_cast(m_length); if (readIndex < 0 ) { readIndex += m_maxLength; } float lOut = m_buffer[ readIndex ][ 0 ]; float rOut = m_buffer[ readIndex ] [1 ]; @@ -81,7 +83,7 @@ void StereoDelay::setSampleRate( int sampleRate ) } int bufferSize = ( int )( sampleRate * m_maxTime ); - m_buffer = new sampleFrame[bufferSize]; + m_buffer = new SampleFrame[bufferSize]; for( int i = 0 ; i < bufferSize ; i++) { m_buffer[i][0] = 0.0; diff --git a/plugins/Delay/StereoDelay.h b/plugins/Delay/StereoDelay.h index 0ea0efeed06..ad0e020b5aa 100644 --- a/plugins/Delay/StereoDelay.h +++ b/plugins/Delay/StereoDelay.h @@ -31,6 +31,8 @@ namespace lmms { +class SampleFrame; + class StereoDelay { public: @@ -49,11 +51,11 @@ class StereoDelay m_feedback = feedback; } - void tick( sampleFrame& frame ); + void tick( SampleFrame& frame ); void setSampleRate( int sampleRate ); private: - sampleFrame* m_buffer; + SampleFrame* m_buffer; int m_maxLength; float m_length; int m_writeIndex; diff --git a/plugins/Dispersion/Dispersion.cpp b/plugins/Dispersion/Dispersion.cpp index fb28e1f47a3..a2fada615bb 100644 --- a/plugins/Dispersion/Dispersion.cpp +++ b/plugins/Dispersion/Dispersion.cpp @@ -52,13 +52,13 @@ Plugin::Descriptor PLUGIN_EXPORT dispersion_plugin_descriptor = DispersionEffect::DispersionEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key) : Effect(&dispersion_plugin_descriptor, parent, key), m_dispersionControls(this), - m_sampleRate(Engine::audioEngine()->processingSampleRate()), + m_sampleRate(Engine::audioEngine()->outputSampleRate()), m_amountVal(0) { } -bool DispersionEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames) +bool DispersionEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames) { if (!isEnabled() || !isRunning()) { diff --git a/plugins/Dispersion/Dispersion.h b/plugins/Dispersion/Dispersion.h index 9e2014baf8c..e3d5d4b5ca0 100644 --- a/plugins/Dispersion/Dispersion.h +++ b/plugins/Dispersion/Dispersion.h @@ -41,7 +41,7 @@ class DispersionEffect : public Effect public: DispersionEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key); ~DispersionEffect() override = default; - bool processAudioBuffer(sampleFrame* buf, const fpp_t frames) override; + bool processAudioBuffer(SampleFrame* buf, const fpp_t frames) override; EffectControls* controls() override { diff --git a/plugins/Dispersion/DispersionControls.cpp b/plugins/Dispersion/DispersionControls.cpp index 771ffb89dc9..f97fc0e37f5 100644 --- a/plugins/Dispersion/DispersionControls.cpp +++ b/plugins/Dispersion/DispersionControls.cpp @@ -35,9 +35,9 @@ DispersionControls::DispersionControls(DispersionEffect* effect) : EffectControls(effect), m_effect(effect), m_amountModel(0, 0, MAX_DISPERSION_FILTERS, this, tr("Amount")), - m_freqModel(200, 20, 20000, 0.001, this, tr("Frequency")), - m_resoModel(0.707, 0.01, 8, 0.0001, this, tr("Resonance")), - m_feedbackModel(0.f, -1.f, 1.f, 0.0001, this, tr("Feedback")), + m_freqModel(200, 20, 20000, 0.001f, this, tr("Frequency")), + m_resoModel(0.707f, 0.01f, 8, 0.0001f, this, tr("Resonance")), + m_feedbackModel(0.f, -1.f, 1.f, 0.0001f, this, tr("Feedback")), m_dcModel(false, this, tr("DC Offset Removal")) { m_freqModel.setScaleLogarithmic(true); diff --git a/plugins/DualFilter/DualFilter.cpp b/plugins/DualFilter/DualFilter.cpp index 4e66db988a1..b337e100301 100644 --- a/plugins/DualFilter/DualFilter.cpp +++ b/plugins/DualFilter/DualFilter.cpp @@ -57,8 +57,8 @@ DualFilterEffect::DualFilterEffect( Model* parent, const Descriptor::SubPluginFe Effect( &dualfilter_plugin_descriptor, parent, key ), m_dfControls( this ) { - m_filter1 = new BasicFilters<2>( Engine::audioEngine()->processingSampleRate() ); - m_filter2 = new BasicFilters<2>( Engine::audioEngine()->processingSampleRate() ); + m_filter1 = new BasicFilters<2>( Engine::audioEngine()->outputSampleRate() ); + m_filter2 = new BasicFilters<2>( Engine::audioEngine()->outputSampleRate() ); // ensure filters get updated m_filter1changed = true; @@ -77,7 +77,7 @@ DualFilterEffect::~DualFilterEffect() -bool DualFilterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) +bool DualFilterEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames ) { if( !isEnabled() || !isRunning () ) { @@ -218,6 +218,11 @@ bool DualFilterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames return isRunning(); } +void DualFilterEffect::onEnabledChanged() +{ + m_filter1->clearHistory(); + m_filter2->clearHistory(); +} diff --git a/plugins/DualFilter/DualFilter.h b/plugins/DualFilter/DualFilter.h index c179edbcca8..6c53f61ef27 100644 --- a/plugins/DualFilter/DualFilter.h +++ b/plugins/DualFilter/DualFilter.h @@ -40,13 +40,15 @@ class DualFilterEffect : public Effect public: DualFilterEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ); ~DualFilterEffect() override; - bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override; + bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override; EffectControls* controls() override { return &m_dfControls; } +protected: + void onEnabledChanged() override; private: DualFilterControls m_dfControls; diff --git a/plugins/DualFilter/DualFilterControlDialog.cpp b/plugins/DualFilter/DualFilterControlDialog.cpp index d316e337213..a674a4a42c2 100644 --- a/plugins/DualFilter/DualFilterControlDialog.cpp +++ b/plugins/DualFilter/DualFilterControlDialog.cpp @@ -29,7 +29,6 @@ #include "Knob.h" #include "LedCheckBox.h" #include "ComboBox.h" -#include "gui_templates.h" namespace lmms::gui { @@ -76,12 +75,10 @@ DualFilterControlDialog::DualFilterControlDialog( DualFilterControls* controls ) auto m_filter1ComboBox = new ComboBox(this); m_filter1ComboBox->setGeometry( 19, 70, 137, ComboBox::DEFAULT_HEIGHT ); - m_filter1ComboBox->setFont( pointSize<8>( m_filter1ComboBox->font() ) ); m_filter1ComboBox->setModel( &controls->m_filter1Model ); auto m_filter2ComboBox = new ComboBox(this); m_filter2ComboBox->setGeometry( 217, 70, 137, ComboBox::DEFAULT_HEIGHT ); - m_filter2ComboBox->setFont( pointSize<8>( m_filter2ComboBox->font() ) ); m_filter2ComboBox->setModel( &controls->m_filter2Model ); } diff --git a/plugins/DualFilter/DualFilterControls.cpp b/plugins/DualFilter/DualFilterControls.cpp index e862e6ae149..75478c9d7a9 100644 --- a/plugins/DualFilter/DualFilterControls.cpp +++ b/plugins/DualFilter/DualFilterControls.cpp @@ -43,7 +43,7 @@ DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : m_enabled1Model( true, this, tr( "Filter 1 enabled" ) ), m_filter1Model( this, tr( "Filter 1 type" ) ), m_cut1Model( 7000.0f, 1.0f, 20000.0f, 1.0f, this, tr( "Cutoff frequency 1" ) ), - m_res1Model( 0.5, BasicFilters<>::minQ(), 10.0, 0.01, this, tr( "Q/Resonance 1" ) ), + m_res1Model(0.5f, BasicFilters<>::minQ(), 10.f, 0.01f, this, tr("Q/Resonance 1")), m_gain1Model( 100.0f, 0.0f, 200.0f, 0.1f, this, tr( "Gain 1" ) ), m_mixModel( 0.0f, -1.0f, 1.0f, 0.01f, this, tr( "Mix" ) ), @@ -51,7 +51,7 @@ DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : m_enabled2Model( true, this, tr( "Filter 2 enabled" ) ), m_filter2Model( this, tr( "Filter 2 type" ) ), m_cut2Model( 7000.0f, 1.0f, 20000.0f, 1.0f, this, tr( "Cutoff frequency 2" ) ), - m_res2Model( 0.5, BasicFilters<>::minQ(), 10.0, 0.01, this, tr( "Q/Resonance 2" ) ), + m_res2Model(0.5f, BasicFilters<>::minQ(), 10.f, 0.01f, this, tr("Q/Resonance 2")), m_gain2Model( 100.0f, 0.0f, 200.0f, 0.1f, this, tr( "Gain 2" ) ) { m_filter1Model.addItem( tr( "Low-pass" ), std::make_unique( "filter_lp" ) ); @@ -111,8 +111,8 @@ void DualFilterControls::updateFilters() delete m_effect->m_filter1; delete m_effect->m_filter2; - m_effect->m_filter1 = new BasicFilters<2>( Engine::audioEngine()->processingSampleRate() ); - m_effect->m_filter2 = new BasicFilters<2>( Engine::audioEngine()->processingSampleRate() ); + m_effect->m_filter1 = new BasicFilters<2>( Engine::audioEngine()->outputSampleRate() ); + m_effect->m_filter2 = new BasicFilters<2>( Engine::audioEngine()->outputSampleRate() ); // flag filters as needing recalculation diff --git a/plugins/DynamicsProcessor/DynamicsProcessor.cpp b/plugins/DynamicsProcessor/DynamicsProcessor.cpp index 6bdf41eee4e..5b251a6f0ad 100644 --- a/plugins/DynamicsProcessor/DynamicsProcessor.cpp +++ b/plugins/DynamicsProcessor/DynamicsProcessor.cpp @@ -56,7 +56,7 @@ Plugin::Descriptor PLUGIN_EXPORT dynamicsprocessor_plugin_descriptor = } const float DYN_NOISE_FLOOR = 0.00001f; // -100dBFS noise floor -const double DNF_LOG = 5.0; +const double DNF_LOG = -1.0; DynProcEffect::DynProcEffect( Model * _parent, const Descriptor::SubPluginFeatures::Key * _key ) : @@ -64,8 +64,8 @@ DynProcEffect::DynProcEffect( Model * _parent, m_dpControls( this ) { m_currentPeak[0] = m_currentPeak[1] = DYN_NOISE_FLOOR; - m_rms[0] = new RmsHelper( 64 * Engine::audioEngine()->processingSampleRate() / 44100 ); - m_rms[1] = new RmsHelper( 64 * Engine::audioEngine()->processingSampleRate() / 44100 ); + m_rms[0] = new RmsHelper( 64 * Engine::audioEngine()->outputSampleRate() / 44100 ); + m_rms[1] = new RmsHelper( 64 * Engine::audioEngine()->outputSampleRate() / 44100 ); calcAttack(); calcRelease(); } @@ -82,16 +82,16 @@ DynProcEffect::~DynProcEffect() inline void DynProcEffect::calcAttack() { - m_attCoeff = std::pow(10.f, ( DNF_LOG / ( m_dpControls.m_attackModel.value() * 0.001 ) ) / Engine::audioEngine()->processingSampleRate() ); + m_attCoeff = std::exp((DNF_LOG / (m_dpControls.m_attackModel.value() * 0.001)) / Engine::audioEngine()->outputSampleRate()); } inline void DynProcEffect::calcRelease() { - m_relCoeff = std::pow(10.f, ( -DNF_LOG / ( m_dpControls.m_releaseModel.value() * 0.001 ) ) / Engine::audioEngine()->processingSampleRate() ); + m_relCoeff = std::exp((DNF_LOG / (m_dpControls.m_releaseModel.value() * 0.001)) / Engine::audioEngine()->outputSampleRate()); } -bool DynProcEffect::processAudioBuffer( sampleFrame * _buf, +bool DynProcEffect::processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) { if( !isEnabled() || !isRunning () ) @@ -106,7 +106,6 @@ bool DynProcEffect::processAudioBuffer( sampleFrame * _buf, int i = 0; auto sm_peak = std::array{0.0f, 0.0f}; - float gain; double out_sum = 0.0; const float d = dryLevel(); @@ -123,8 +122,8 @@ bool DynProcEffect::processAudioBuffer( sampleFrame * _buf, if( m_needsUpdate ) { - m_rms[0]->setSize( 64 * Engine::audioEngine()->processingSampleRate() / 44100 ); - m_rms[1]->setSize( 64 * Engine::audioEngine()->processingSampleRate() / 44100 ); + m_rms[0]->setSize( 64 * Engine::audioEngine()->outputSampleRate() / 44100 ); + m_rms[1]->setSize( 64 * Engine::audioEngine()->outputSampleRate() / 44100 ); calcAttack(); calcRelease(); m_needsUpdate = false; @@ -155,15 +154,15 @@ bool DynProcEffect::processAudioBuffer( sampleFrame * _buf, const double t = m_rms[i]->update( s[i] ); if( t > m_currentPeak[i] ) { - m_currentPeak[i] = qMin( m_currentPeak[i] * m_attCoeff, t ); + m_currentPeak[i] = m_currentPeak[i] * m_attCoeff + (1 - m_attCoeff) * t; } else if( t < m_currentPeak[i] ) { - m_currentPeak[i] = qMax( m_currentPeak[i] * m_relCoeff, t ); + m_currentPeak[i] = m_currentPeak[i] * m_relCoeff + (1 - m_relCoeff) * t; } - m_currentPeak[i] = qBound( DYN_NOISE_FLOOR, m_currentPeak[i], 10.0f ); + m_currentPeak[i] = std::max(DYN_NOISE_FLOOR, m_currentPeak[i]); } // account for stereo mode @@ -196,20 +195,10 @@ bool DynProcEffect::processAudioBuffer( sampleFrame * _buf, if( sm_peak[i] > DYN_NOISE_FLOOR ) { - if ( lookup < 1 ) - { - gain = frac * samples[0]; - } - else - if ( lookup < 200 ) - { - gain = linearInterpolate( samples[ lookup - 1 ], - samples[ lookup ], frac ); - } - else - { - gain = samples[199]; - }; + float gain; + if (lookup < 1) { gain = frac * samples[0]; } + else if (lookup < 200) { gain = linearInterpolate(samples[lookup - 1], samples[lookup], frac); } + else { gain = samples[199]; } s[i] *= gain; s[i] /= sm_peak[i]; diff --git a/plugins/DynamicsProcessor/DynamicsProcessor.h b/plugins/DynamicsProcessor/DynamicsProcessor.h index 576accdcc9b..970690d8d3d 100644 --- a/plugins/DynamicsProcessor/DynamicsProcessor.h +++ b/plugins/DynamicsProcessor/DynamicsProcessor.h @@ -42,7 +42,7 @@ class DynProcEffect : public Effect DynProcEffect( Model * _parent, const Descriptor::SubPluginFeatures::Key * _key ); ~DynProcEffect() override; - bool processAudioBuffer( sampleFrame * _buf, + bool processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) override; EffectControls * controls() override diff --git a/plugins/Eq/CMakeLists.txt b/plugins/Eq/CMakeLists.txt index 899dde5ec6d..68edb034b5c 100644 --- a/plugins/Eq/CMakeLists.txt +++ b/plugins/Eq/CMakeLists.txt @@ -1,5 +1,5 @@ INCLUDE(BuildPlugin) -INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) +include_directories(SYSTEM ${FFTW3F_INCLUDE_DIRS}) LINK_LIBRARIES(${FFTW3F_LIBRARIES}) BUILD_PLUGIN(eq EqEffect.cpp EqCurve.cpp EqCurve.h EqControls.cpp EqControlsDialog.cpp EqFilter.h EqParameterWidget.cpp EqFader.h EqSpectrumView.h EqSpectrumView.cpp MOCFILES EqControls.h EqControlsDialog.h EqCurve.h EqParameterWidget.h EqFader.h EqSpectrumView.h EMBEDDED_RESOURCES *.png) diff --git a/plugins/Eq/EqControls.cpp b/plugins/Eq/EqControls.cpp index 04fb9bd3a16..ea5b2947f7c 100644 --- a/plugins/Eq/EqControls.cpp +++ b/plugins/Eq/EqControls.cpp @@ -37,30 +37,30 @@ namespace lmms EqControls::EqControls( EqEffect *effect ) : EffectControls( effect ), m_effect( effect ), - m_inGainModel( 0.0, -60.0, 20.0, 0.01, this, tr( "Input gain") ), - m_outGainModel( -.0, -60.0, 20.0, 0.01, this, tr( "Output gain" ) ), - m_lowShelfGainModel( 0.0 , -18, 18, 0.001, this, tr( "Low-shelf gain" ) ), - m_para1GainModel( 0.0 , -18, 18, 0.001, this, tr( "Peak 1 gain" ) ), - m_para2GainModel( 0.0 , -18, 18, 0.001, this, tr( "Peak 2 gain" ) ), - m_para3GainModel( 0.0 , -18, 18, 0.001, this, tr( "Peak 3 gain" ) ), - m_para4GainModel( 0.0 , -18, 18, 0.001, this, tr( "Peak 4 gain" ) ), - m_highShelfGainModel( 0.0 , -18, 18, 0.001, this, tr( "High-shelf gain" ) ), - m_hpResModel( 0.707,0.003, 10.0 , 0.001, this, tr( "HP res" ) ), - m_lowShelfResModel( 0.707, 0.55, 10.0 , 0.001, this , tr( "Low-shelf res" ) ), - m_para1BwModel( 0.3, 0.1, 4 , 0.001, this , tr( "Peak 1 BW" ) ), - m_para2BwModel( 0.3, 0.1, 4 , 0.001, this , tr( "Peak 2 BW" ) ), - m_para3BwModel( 0.3, 0.1, 4 , 0.001, this , tr( "Peak 3 BW" ) ), - m_para4BwModel( 0.3, 0.1, 4 , 0.001, this , tr( "Peak 4 BW" ) ), - m_highShelfResModel( 0.707, 0.55, 10.0 , 0.001, this , tr( "High-shelf res" ) ), - m_lpResModel( 0.707,0.003, 10.0 , 0.001, this , tr( "LP res" ) ), - m_hpFeqModel( 31.0, 20.0, 20000, 0.001, this , tr( "HP freq" ) ), - m_lowShelfFreqModel( 80.0, 20.0, 20000, 0.001, this , tr( "Low-shelf freq" ) ), - m_para1FreqModel( 120.0, 20.0, 20000, 0.001, this , tr( "Peak 1 freq" ) ), - m_para2FreqModel( 250.0, 20.0, 20000, 0.001, this, tr( "Peak 2 freq" ) ), - m_para3FreqModel( 2000.0, 20.0, 20000, 0.001, this , tr( "Peak 3 freq" ) ), - m_para4FreqModel( 4000.0, 20.0, 20000, 0.001, this , tr( "Peak 4 freq" ) ), - m_highShelfFreqModel( 12000.0, 20.0, 20000, 0.001, this , tr( "High-shelf freq" ) ), - m_lpFreqModel( 18000.0, 20.0, 20000, 0.001, this , tr( "LP freq" ) ), + m_inGainModel(0.f, -60.f, 20.f, 0.01f, this, tr("Input gain")), + m_outGainModel(-0.f, -60.f, 20.f, 0.01f, this, tr("Output gain")), + m_lowShelfGainModel( 0.f, -18, 18, 0.001f, this, tr("Low-shelf gain")), + m_para1GainModel(0.f, -18, 18, 0.001f, this, tr("Peak 1 gain")), + m_para2GainModel(0.f, -18, 18, 0.001f, this, tr("Peak 2 gain")), + m_para3GainModel(0.f, -18, 18, 0.001f, this, tr("Peak 3 gain")), + m_para4GainModel(0.f, -18, 18, 0.001f, this, tr("Peak 4 gain")), + m_highShelfGainModel(0.f, -18, 18, 0.001f, this, tr("High-shelf gain")), + m_hpResModel(0.707f,0.003f, 10.f, 0.001f, this, tr("HP res")), + m_lowShelfResModel(0.707f, 0.55f, 10.f, 0.001f, this, tr("Low-shelf res")), + m_para1BwModel(0.3f, 0.1f, 4, 0.001f, this, tr("Peak 1 BW")), + m_para2BwModel(0.3f, 0.1f, 4, 0.001f, this, tr("Peak 2 BW")), + m_para3BwModel(0.3f, 0.1f, 4, 0.001f, this, tr("Peak 3 BW")), + m_para4BwModel(0.3f, 0.1f, 4, 0.001f, this, tr("Peak 4 BW")), + m_highShelfResModel(0.707f, 0.55f, 10.f, 0.001f, this, tr("High-shelf res")), + m_lpResModel(0.707f,0.003f, 10.f, 0.001f, this, tr("LP res")), + m_hpFeqModel(31.f, 20.f, 20000, 0.001f, this, tr("HP freq")), + m_lowShelfFreqModel(80.f, 20.f, 20000, 0.001f, this, tr("Low-shelf freq")), + m_para1FreqModel(120.f, 20.f, 20000, 0.001f, this, tr("Peak 1 freq")), + m_para2FreqModel(250.f, 20.f, 20000, 0.001f, this, tr("Peak 2 freq")), + m_para3FreqModel(2000.f, 20.f, 20000, 0.001f, this, tr("Peak 3 freq")), + m_para4FreqModel(4000.f, 20.f, 20000, 0.001f, this, tr("Peak 4 freq")), + m_highShelfFreqModel(12000.f, 20.f, 20000, 0.001f, this, tr("High-shelf freq")), + m_lpFreqModel(18000.f, 20.f, 20000, 0.001f, this, tr("LP freq")), m_hpActiveModel( false, this , tr( "HP active" ) ), m_lowShelfActiveModel( false, this , tr( "Low-shelf active" ) ), m_para1ActiveModel( false, this , tr( "Peak 1 active" ) ), diff --git a/plugins/Eq/EqControls.h b/plugins/Eq/EqControls.h index 6db82f3e33f..80680c7fb5a 100644 --- a/plugins/Eq/EqControls.h +++ b/plugins/Eq/EqControls.h @@ -66,6 +66,8 @@ class EqControls : public EffectControls float m_inPeakR; float m_outPeakL; float m_outPeakR; + + // The following are linear peaks float m_lowShelfPeakL, m_lowShelfPeakR; float m_para1PeakL, m_para1PeakR; float m_para2PeakL, m_para2PeakR; diff --git a/plugins/Eq/EqControlsDialog.cpp b/plugins/Eq/EqControlsDialog.cpp index 634bde846bc..8394569f668 100644 --- a/plugins/Eq/EqControlsDialog.cpp +++ b/plugins/Eq/EqControlsDialog.cpp @@ -72,18 +72,16 @@ EqControlsDialog::EqControlsDialog( EqControls *controls ) : setBand( 6, &controls->m_highShelfActiveModel, &controls->m_highShelfFreqModel, &controls->m_highShelfResModel, &controls->m_highShelfGainModel, QColor(255 ,255, 255), tr( "High-shelf" ), &controls->m_highShelfPeakL, &controls->m_highShelfPeakR,0,0,0,0,0,0 ); setBand( 7, &controls->m_lpActiveModel, &controls->m_lpFreqModel, &controls->m_lpResModel, 0, QColor(255 ,255, 255), tr( "LP" ) ,0,0,0,0,0, &controls->m_lp12Model, &controls->m_lp24Model, &controls->m_lp48Model); - static auto s_faderBg = PLUGIN_NAME::getIconPixmap("faderback"); - static auto s_faderLeds = PLUGIN_NAME::getIconPixmap("faderleds"); - static auto s_faderKnob = PLUGIN_NAME::getIconPixmap("faderknob"); + QSize const faderSize(23, 80); - auto GainFaderIn = new EqFader(&controls->m_inGainModel, tr("Input gain"), this, &s_faderBg, &s_faderLeds, &s_faderKnob, - &controls->m_inPeakL, &controls->m_inPeakR); + auto GainFaderIn = new EqFader(&controls->m_inGainModel, tr("Input gain"), this, &controls->m_inPeakL, &controls->m_inPeakR); + GainFaderIn->setFixedSize(faderSize); GainFaderIn->move( 23, 295 ); GainFaderIn->setDisplayConversion( false ); GainFaderIn->setHintText( tr( "Gain" ), "dBv"); - auto GainFaderOut = new EqFader(&controls->m_outGainModel, tr("Output gain"), this, &s_faderBg, &s_faderLeds, &s_faderKnob, - &controls->m_outPeakL, &controls->m_outPeakR); + auto GainFaderOut = new EqFader(&controls->m_outGainModel, tr("Output gain"), this, &controls->m_outPeakL, &controls->m_outPeakR); + GainFaderOut->setFixedSize(faderSize); GainFaderOut->move( 453, 295); GainFaderOut->setDisplayConversion( false ); GainFaderOut->setHintText( tr( "Gain" ), "dBv" ); @@ -92,8 +90,9 @@ EqControlsDialog::EqControlsDialog( EqControls *controls ) : int distance = 126; for( int i = 1; i < m_parameterWidget->bandCount() - 1; i++ ) { - auto gainFader = new EqFader(m_parameterWidget->getBandModels(i)->gain, tr(""), this, &s_faderBg, &s_faderLeds, - &s_faderKnob, m_parameterWidget->getBandModels(i)->peakL, m_parameterWidget->getBandModels(i)->peakR); + auto gainFader = new EqFader(m_parameterWidget->getBandModels(i)->gain, tr(""), this, + m_parameterWidget->getBandModels(i)->peakL, m_parameterWidget->getBandModels(i)->peakR); + gainFader->setFixedSize(faderSize); gainFader->move( distance, 295 ); distance += 44; gainFader->setMinimumHeight(80); @@ -124,10 +123,10 @@ EqControlsDialog::EqControlsDialog( EqControls *controls ) : activeButton->setCheckable(true); activeButton->setModel( m_parameterWidget->getBandModels( i )->active ); - QString iconActiveFileName = "bandLabel" + QString::number(i+1); - QString iconInactiveFileName = "bandLabel" + QString::number(i+1) + "off"; - activeButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( iconActiveFileName.toLatin1() ) ); - activeButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( iconInactiveFileName.toLatin1() ) ); + const auto iconActiveFileName = "bandLabel" + std::to_string(i + 1); + const auto iconInactiveFileName = iconActiveFileName + "off"; + activeButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap(iconActiveFileName)); + activeButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap(iconInactiveFileName)); activeButton->move( distance - 2, 276 ); activeButton->setModel( m_parameterWidget->getBandModels( i )->active ); diff --git a/plugins/Eq/EqCurve.cpp b/plugins/Eq/EqCurve.cpp index bb721a7e462..df17f71ffbf 100644 --- a/plugins/Eq/EqCurve.cpp +++ b/plugins/Eq/EqCurve.cpp @@ -31,6 +31,7 @@ #include "embed.h" #include "Engine.h" #include "lmms_constants.h" +#include "lmms_math.h" namespace lmms::gui @@ -65,7 +66,7 @@ QRectF EqHandle::boundingRect() const float EqHandle::freqToXPixel( float freq , int w ) { - if (typeInfo::isEqual(freq, 0.0f)) { return 0.0f; } + if (approximatelyEqual(freq, 0.0f)) { return 0.0f; } float min = log10f( 20 ); float max = log10f( 20000 ); float range = max - min; @@ -185,9 +186,9 @@ QPainterPath EqHandle::getCurvePath() void EqHandle::loadPixmap() { - QString fileName = "handle" + QString::number(m_numb+1); - if ( !isActiveHandle() ) { fileName = fileName + "inactive"; } - m_circlePixmap = PLUGIN_NAME::getIconPixmap( fileName.toLatin1() ); + auto fileName = "handle" + std::to_string(m_numb + 1); + if (!isActiveHandle()) { fileName += "inactive"; } + m_circlePixmap = PLUGIN_NAME::getIconPixmap(fileName); } bool EqHandle::mousePressed() const @@ -201,22 +202,21 @@ bool EqHandle::mousePressed() const float EqHandle::getPeakCurve( float x ) { double freqZ = xPixelToFreq( EqHandle::x(), m_width ); - const int SR = Engine::audioEngine()->processingSampleRate(); + const int SR = Engine::audioEngine()->outputSampleRate(); double w0 = 2 * LD_PI * freqZ / SR ; double c = cosf( w0 ); double s = sinf( w0 ); double Q = getResonance(); double A = pow( 10, yPixelToGain( EqHandle::y(), m_heigth, m_pixelsPerUnitHeight ) / 40 ); double alpha = s * sinh( log( 2 ) / 2 * Q * w0 / sinf( w0 ) ); - double a0, a1, a2, b0, b1, b2; // coeffs to calculate //calc coefficents - b0 = 1 + alpha * A; - b1 = -2 * c; - b2 = 1 - alpha * A; - a0 = 1 + alpha / A; - a1 = -2 * c; - a2 = 1 - alpha / A; + double b0 = 1 + alpha * A; + double b1 = -2 * c; + double b2 = 1 - alpha * A; + double a0 = 1 + alpha / A; + double a1 = -2 * c; + double a2 = 1 - alpha / A; //normalise b0 /= a0; @@ -239,21 +239,21 @@ float EqHandle::getPeakCurve( float x ) float EqHandle::getHighShelfCurve( float x ) { double freqZ = xPixelToFreq( EqHandle::x(), m_width ); - const int SR = Engine::audioEngine()->processingSampleRate(); + const int SR = Engine::audioEngine()->outputSampleRate(); double w0 = 2 * LD_PI * freqZ / SR; double c = cosf( w0 ); double s = sinf( w0 ); double A = pow( 10, yPixelToGain( EqHandle::y(), m_heigth, m_pixelsPerUnitHeight ) * 0.025 ); double beta = sqrt( A ) / m_resonance; - double a0, a1, a2, b0, b1, b2; // coeffs to calculate //calc coefficents - b0 = A * ( ( A + 1 ) + ( A - 1 ) * c + beta * s); - b1 = -2 * A * ( ( A - 1 ) + ( A + 1 ) * c ); - b2 = A * ( ( A + 1 ) + ( A - 1 ) * c - beta * s); - a0 = ( A + 1 ) - ( A - 1 ) * c + beta * s; - a1 = 2 * ( ( A - 1 ) - ( A + 1 ) * c ); - a2 = ( A + 1 ) - ( A - 1 ) * c - beta * s; + double b0 = A * ((A + 1) + (A - 1) * c + beta * s); + double b1 = -2 * A * ((A - 1) + (A + 1) * c); + double b2 = A * ((A + 1) + (A - 1) * c - beta * s); + double a0 = (A + 1) - (A - 1) * c + beta * s; + double a1 = 2 * ((A - 1) - (A + 1) * c); + double a2 = (A + 1) - (A - 1) * c - beta * s; + //normalise b0 /= a0; b1 /= a0; @@ -275,21 +275,20 @@ float EqHandle::getHighShelfCurve( float x ) float EqHandle::getLowShelfCurve( float x ) { double freqZ = xPixelToFreq( EqHandle::x(), m_width ); - const int SR = Engine::audioEngine()->processingSampleRate(); + const int SR = Engine::audioEngine()->outputSampleRate(); double w0 = 2 * LD_PI * freqZ / SR ; double c = cosf( w0 ); double s = sinf( w0 ); double A = pow( 10, yPixelToGain( EqHandle::y(), m_heigth, m_pixelsPerUnitHeight ) / 40 ); double beta = sqrt( A ) / m_resonance; - double a0, a1, a2, b0, b1, b2; // coeffs to calculate //calc coefficents - b0 = A * ( ( A + 1 ) - ( A - 1 ) * c + beta * s ); - b1 = 2 * A * ( ( A - 1 ) - ( A + 1 ) * c ) ; - b2 = A * ( ( A + 1 ) - ( A - 1 ) * c - beta * s); - a0 = ( A + 1 ) + ( A - 1 ) * c + beta * s; - a1 = -2 * ( ( A - 1 ) + ( A + 1 ) * c ); - a2 = ( A + 1 ) + ( A - 1) * c - beta * s; + double b0 = A * ((A + 1) - (A - 1) * c + beta * s); + double b1 = 2 * A * ((A - 1) - (A + 1) * c); + double b2 = A * ((A + 1) - (A - 1) * c - beta * s); + double a0 = (A + 1) + (A - 1) * c + beta * s; + double a1 = -2 * ((A - 1) + (A + 1) * c); + double a2 = (A + 1) + (A - 1) * c - beta * s; //normalise b0 /= a0; @@ -312,20 +311,20 @@ float EqHandle::getLowShelfCurve( float x ) float EqHandle::getLowCutCurve( float x ) { double freqZ = xPixelToFreq( EqHandle::x(), m_width ); - const int SR = Engine::audioEngine()->processingSampleRate(); + const int SR = Engine::audioEngine()->outputSampleRate(); double w0 = 2 * LD_PI * freqZ / SR ; double c = cosf( w0 ); double s = sinf( w0 ); double resonance = getResonance(); double alpha = s / (2 * resonance); - double a0, a1, a2, b0, b1, b2; // coeffs to calculate - - b0 = ( 1 + c ) * 0.5; - b1 = ( -( 1 + c ) ); - b2 = ( 1 + c ) * 0.5; - a0 = 1 + alpha; - a1 = ( -2 * c ); - a2 = 1 - alpha; + + double b0 = (1 + c) * 0.5; + double b1 = (-(1 + c)); + double b2 = (1 + c) * 0.5; + double a0 = 1 + alpha; + double a1 = (-2 * c); + double a2 = 1 - alpha; + //normalise b0 /= a0; b1 /= a0; @@ -355,20 +354,20 @@ float EqHandle::getLowCutCurve( float x ) float EqHandle::getHighCutCurve( float x ) { double freqZ = xPixelToFreq( EqHandle::x(), m_width ); - const int SR = Engine::audioEngine()->processingSampleRate(); + const int SR = Engine::audioEngine()->outputSampleRate(); double w0 = 2 * LD_PI * freqZ / SR ; double c = cosf( w0 ); double s = sinf( w0 ); double resonance = getResonance(); double alpha = s / (2 * resonance); - double a0, a1, a2, b0, b1, b2; // coeffs to calculate - - b0 = ( 1 - c ) * 0.5; - b1 = 1 - c; - b2 = ( 1 - c ) * 0.5; - a0 = 1 + alpha; - a1 = -2 * c; - a2 = 1 - alpha; + + double b0 = (1 - c) * 0.5; + double b1 = 1 - c; + double b2 = (1 - c) * 0.5; + double a0 = 1 + alpha; + double a1 = -2 * c; + double a2 = 1 - alpha; + //normalise b0 /= a0; b1 /= a0; @@ -529,7 +528,7 @@ void EqHandle::setlp48() double EqHandle::calculateGain(const double freq, const double a1, const double a2, const double b0, const double b1, const double b2 ) { - const int SR = Engine::audioEngine()->processingSampleRate(); + const int SR = Engine::audioEngine()->outputSampleRate(); const double w = 2 * LD_PI * freq / SR ; const double PHI = pow( sin( w / 2 ), 2 ) * 4; @@ -569,16 +568,7 @@ void EqHandle::mouseReleaseEvent( QGraphicsSceneMouseEvent *event ) void EqHandle::wheelEvent( QGraphicsSceneWheelEvent *wevent ) { - float highestBandwich; - if( m_type != EqHandleType::Para ) - { - highestBandwich = 10; - } - else - { - highestBandwich = 4; - } - + float highestBandwich = m_type != EqHandleType::Para ? 10 : 4; int numDegrees = wevent->delta() / 120; float numSteps = 0; if( wevent->modifiers() == Qt::ControlModifier ) @@ -592,17 +582,8 @@ void EqHandle::wheelEvent( QGraphicsSceneWheelEvent *wevent ) if( wevent->orientation() == Qt::Vertical ) { - m_resonance = m_resonance + ( numSteps ); - - if( m_resonance < 0.1 ) - { - m_resonance = 0.1; - } + m_resonance = std::clamp(m_resonance + numSteps, 0.1f, highestBandwich); - if( m_resonance > highestBandwich ) - { - m_resonance = highestBandwich; - } emit positionChanged(); } wevent->accept(); diff --git a/plugins/Eq/EqEffect.cpp b/plugins/Eq/EqEffect.cpp index 31be4d0f5b6..662b85a8ea5 100644 --- a/plugins/Eq/EqEffect.cpp +++ b/plugins/Eq/EqEffect.cpp @@ -64,9 +64,9 @@ EqEffect::EqEffect( Model *parent, const Plugin::Descriptor::SubPluginFeatures:: -bool EqEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) +bool EqEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames ) { - const int sampleRate = Engine::audioEngine()->processingSampleRate(); + const int sampleRate = Engine::audioEngine()->outputSampleRate(); //wet/dry controls const float dry = dryLevel(); @@ -157,7 +157,7 @@ bool EqEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) } const float outGain = m_outGain; - sampleFrame m_inPeak = { 0, 0 }; + SampleFrame m_inPeak = { 0, 0 }; if(m_eqControls.m_analyseInModel.value( true ) && outSum > 0 && m_eqControls.isViewVisible() ) { @@ -263,7 +263,7 @@ bool EqEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) } - sampleFrame outPeak = { 0, 0 }; + SampleFrame outPeak = { 0, 0 }; gain( buf, frames, outGain, &outPeak ); m_eqControls.m_outPeakL = m_eqControls.m_outPeakL < outPeak[0] ? outPeak[0] : m_eqControls.m_outPeakL; m_eqControls.m_outPeakR = m_eqControls.m_outPeakR < outPeak[1] ? outPeak[1] : m_eqControls.m_outPeakR; @@ -287,24 +287,23 @@ bool EqEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) -float EqEffect::peakBand( float minF, float maxF, EqAnalyser *fft, int sr ) +float EqEffect::linearPeakBand(float minF, float maxF, EqAnalyser* fft, int sr) { auto const fftEnergy = fft->getEnergy(); if (fftEnergy == 0.) { return 0.; } - float peak = -60; - float *b = fft->m_bands; - float h = 0; - for( int x = 0; x < MAX_BANDS; x++, b++ ) + + float peakLinear = 0.; + + for (int i = 0; i < MAX_BANDS; ++i) { - if( bandToFreq( x ,sr) >= minF && bandToFreq( x,sr ) <= maxF ) + if (bandToFreq(i, sr) >= minF && bandToFreq(i, sr) <= maxF) { - h = 20. * log10(*b / fftEnergy); - peak = h > peak ? h : peak; + peakLinear = std::max(peakLinear, fft->m_bands[i] / fftEnergy); } } - return ( peak + 60 ) / 100; + return peakLinear; } @@ -312,45 +311,34 @@ float EqEffect::peakBand( float minF, float maxF, EqAnalyser *fft, int sr ) void EqEffect::setBandPeaks( EqAnalyser *fft, int samplerate ) { + auto computePeakBand = [&](const FloatModel& freqModel, const FloatModel& bwModel) + { + float const freq = freqModel.value(); + float const bw = bwModel.value(); + + return linearPeakBand(freq * (1 - bw * 0.5), freq * (1 + bw * 0.5), fft, samplerate); + }; + m_eqControls.m_lowShelfPeakR = m_eqControls.m_lowShelfPeakL = - peakBand( m_eqControls.m_lowShelfFreqModel.value() - * ( 1 - m_eqControls.m_lowShelfResModel.value() * 0.5 ), - m_eqControls.m_lowShelfFreqModel.value(), - fft , samplerate ); + linearPeakBand(m_eqControls.m_lowShelfFreqModel.value() * (1 - m_eqControls.m_lowShelfResModel.value() * 0.5), + m_eqControls.m_lowShelfFreqModel.value(), fft , samplerate); m_eqControls.m_para1PeakL = m_eqControls.m_para1PeakR = - peakBand( m_eqControls.m_para1FreqModel.value() - * ( 1 - m_eqControls.m_para1BwModel.value() * 0.5 ), - m_eqControls.m_para1FreqModel.value() - * ( 1 + m_eqControls.m_para1BwModel.value() * 0.5 ), - fft , samplerate ); + computePeakBand(m_eqControls.m_para1FreqModel, m_eqControls.m_para1BwModel); m_eqControls.m_para2PeakL = m_eqControls.m_para2PeakR = - peakBand( m_eqControls.m_para2FreqModel.value() - * ( 1 - m_eqControls.m_para2BwModel.value() * 0.5 ), - m_eqControls.m_para2FreqModel.value() - * ( 1 + m_eqControls.m_para2BwModel.value() * 0.5 ), - fft , samplerate ); + computePeakBand(m_eqControls.m_para2FreqModel, m_eqControls.m_para2BwModel); m_eqControls.m_para3PeakL = m_eqControls.m_para3PeakR = - peakBand( m_eqControls.m_para3FreqModel.value() - * ( 1 - m_eqControls.m_para3BwModel.value() * 0.5 ), - m_eqControls.m_para3FreqModel.value() - * ( 1 + m_eqControls.m_para3BwModel.value() * 0.5 ), - fft , samplerate ); + computePeakBand(m_eqControls.m_para3FreqModel, m_eqControls.m_para3BwModel); m_eqControls.m_para4PeakL = m_eqControls.m_para4PeakR = - peakBand( m_eqControls.m_para4FreqModel.value() - * ( 1 - m_eqControls.m_para4BwModel.value() * 0.5 ), - m_eqControls.m_para4FreqModel.value() - * ( 1 + m_eqControls.m_para4BwModel.value() * 0.5 ), - fft , samplerate ); + computePeakBand(m_eqControls.m_para4FreqModel, m_eqControls.m_para4BwModel); m_eqControls.m_highShelfPeakL = m_eqControls.m_highShelfPeakR = - peakBand( m_eqControls.m_highShelfFreqModel.value(), - m_eqControls.m_highShelfFreqModel.value() - * ( 1 + m_eqControls.m_highShelfResModel.value() * 0.5 ), - fft, samplerate ); + linearPeakBand(m_eqControls.m_highShelfFreqModel.value(), + m_eqControls.m_highShelfFreqModel.value() * (1 + m_eqControls.m_highShelfResModel.value() * 0.5), + fft, samplerate); } extern "C" diff --git a/plugins/Eq/EqEffect.h b/plugins/Eq/EqEffect.h index 9b23b51b57e..ca0ebb1b9ad 100644 --- a/plugins/Eq/EqEffect.h +++ b/plugins/Eq/EqEffect.h @@ -40,12 +40,12 @@ class EqEffect : public Effect public: EqEffect( Model * parent , const Descriptor::SubPluginFeatures::Key * key ); ~EqEffect() override = default; - bool processAudioBuffer( sampleFrame * buf, const fpp_t frames ) override; + bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override; EffectControls * controls() override { return &m_eqControls; } - inline void gain( sampleFrame * buf, const fpp_t frames, float scale, sampleFrame * peak ) + inline void gain( SampleFrame* buf, const fpp_t frames, float scale, SampleFrame* peak ) { peak[0][0] = 0.0f; peak[0][1] = 0.0f; for( fpp_t f = 0; f < frames; ++f ) @@ -87,7 +87,7 @@ class EqEffect : public Effect float m_inGain; float m_outGain; - float peakBand( float minF, float maxF, EqAnalyser *, int ); + float linearPeakBand(float minF, float maxF, EqAnalyser*, int); inline float bandToFreq ( int index , int sampleRate ) { diff --git a/plugins/Eq/EqFader.h b/plugins/Eq/EqFader.h index 9db0fbe2d82..3185d08794b 100644 --- a/plugins/Eq/EqFader.h +++ b/plugins/Eq/EqFader.h @@ -42,20 +42,6 @@ class EqFader : public Fader public: Q_OBJECT public: - EqFader( FloatModel * model, const QString & name, QWidget * parent, QPixmap * backg, QPixmap * leds, QPixmap * knobpi, float* lPeak, float* rPeak ) : - Fader( model, name, parent, backg, leds, knobpi ) - { - setMinimumSize( 23, 80 ); - setMaximumSize( 23, 80 ); - resize( 23, 80 ); - m_lPeak = lPeak; - m_rPeak = rPeak; - connect( getGUI()->mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( updateVuMeters() ) ); - m_model = model; - setPeak_L( 0 ); - setPeak_R( 0 ); - } - EqFader( FloatModel * model, const QString & name, QWidget * parent, float* lPeak, float* rPeak ) : Fader( model, name, parent ) { @@ -81,7 +67,7 @@ private slots: { const float opl = getPeak_L(); const float opr = getPeak_R(); - const float fallOff = 1.07; + const float fallOff = 1.07f; if( *m_lPeak > opl ) { setPeak_L( *m_lPeak ); diff --git a/plugins/Eq/EqFilter.h b/plugins/Eq/EqFilter.h index c64f6d5c345..5408d131ce3 100644 --- a/plugins/Eq/EqFilter.h +++ b/plugins/Eq/EqFilter.h @@ -190,15 +190,13 @@ public : float s = sinf( w0 ); float alpha = s / ( 2 * m_res ); - float a0, a1, a2, b0, b1, b2; // coeffs to calculate - //calc coefficents - b0 = ( 1 + c ) * 0.5; - b1 = ( -( 1 + c ) ); - b2 = ( 1 + c ) * 0.5; - a0 = 1 + alpha; - a1 = ( -2 * c ); - a2 = 1 - alpha; + float b0 = (1 + c) * 0.5; + float b1 = (-(1 + c)); + float b2 = (1 + c) * 0.5; + float a0 = 1 + alpha; + float a1 = (-2 * c); + float a2 = 1 - alpha; //normalise b0 /= a0; @@ -235,15 +233,13 @@ public : float s = sinf( w0 ); float alpha = s / ( 2 * m_res ); - float a0, a1, a2, b0, b1, b2; // coeffs to calculate - //calc coefficents - b0 = ( 1 - c ) * 0.5; - b1 = 1 - c; - b2 = ( 1 - c ) * 0.5; - a0 = 1 + alpha; - a1 = -2 * c; - a2 = 1 - alpha; + float b0 = (1 - c) * 0.5; + float b1 = 1 - c; + float b2 = (1 - c) * 0.5; + float a0 = 1 + alpha; + float a1 = -2 * c; + float a2 = 1 - alpha; //normalise b0 /= a0; @@ -279,15 +275,13 @@ class EqPeakFilter : public EqFilter float A = pow( 10, m_gain * 0.025); float alpha = s * sinh( log( 2 ) / 2 * m_bw * w0 / sinf(w0) ); - float a0, a1, a2, b0, b1, b2; // coeffs to calculate - //calc coefficents - b0 = 1 + alpha*A; - b1 = -2*c; - b2 = 1 - alpha*A; - a0 = 1 + alpha/A; - a1 = -2*c; - a2 = 1 - alpha/A; + float b0 = 1 + alpha * A; + float b1 = -2 * c; + float b2 = 1 - alpha * A; + float a0 = 1 + alpha / A; + float a1 = -2 * c; + float a2 = 1 - alpha / A; //normalise b0 /= a0; @@ -345,15 +339,13 @@ public : // float alpha = s / ( 2 * m_res ); float beta = sqrt( A ) / m_res; - float a0, a1, a2, b0, b1, b2; // coeffs to calculate - //calc coefficents - b0 = A * ( ( A+1 ) - ( A-1 ) * c + beta * s ); - b1 = 2 * A * ( ( A - 1 ) - ( A + 1 ) * c) ; - b2 = A * ( ( A + 1 ) - ( A - 1 ) * c - beta * s); - a0 = ( A + 1 ) + ( A - 1 ) * c + beta * s; - a1 = -2 * ( ( A - 1 ) + ( A + 1 ) * c ); - a2 = ( A + 1 ) + ( A - 1) * c - beta * s; + float b0 = A * ((A + 1) - (A - 1) * c + beta * s); + float b1 = 2 * A * ((A - 1) - (A + 1) * c); + float b2 = A * ((A + 1) - (A - 1) * c - beta * s); + float a0 = (A + 1) + (A - 1) * c + beta * s; + float a1 = -2 * ((A - 1) + (A + 1) * c); + float a2 = (A + 1) + (A - 1) * c - beta * s; //normalise b0 /= a0; @@ -383,15 +375,14 @@ public : float A = pow( 10, m_gain * 0.025 ); float beta = sqrt( A ) / m_res; - float a0, a1, a2, b0, b1, b2; // coeffs to calculate - //calc coefficents - b0 = A *( ( A +1 ) + ( A - 1 ) * c + beta * s); - b1 = -2 * A * ( ( A - 1 ) + ( A + 1 ) * c ); - b2 = A * ( ( A + 1 ) + ( A - 1 ) * c - beta * s); - a0 = ( A + 1 ) - ( A - 1 ) * c + beta * s; - a1 = 2 * ( ( A - 1 ) - ( A + 1 ) * c ); - a2 = ( A + 1) - ( A - 1 ) * c - beta * s; + float b0 = A * ((A + 1) + (A - 1) * c + beta * s); + float b1 = -2 * A * ((A - 1) + (A + 1) * c); + float b2 = A * ((A + 1) + (A - 1) * c - beta * s); + float a0 = (A + 1) - (A - 1) * c + beta * s; + float a1 = 2 * ((A - 1) - (A + 1) * c); + float a2 = (A + 1) - (A - 1) * c - beta * s; + //normalise b0 /= a0; b1 /= a0; @@ -441,7 +432,7 @@ class EqLinkwitzRiley : public StereoLinkwitzRiley - virtual void processBuffer( sampleFrame* buf, const fpp_t frames ) + virtual void processBuffer( SampleFrame* buf, const fpp_t frames ) { for ( fpp_t f = 0 ; f < frames ; ++f) { diff --git a/plugins/Eq/EqSpectrumView.cpp b/plugins/Eq/EqSpectrumView.cpp index 540450883db..99df328efec 100644 --- a/plugins/Eq/EqSpectrumView.cpp +++ b/plugins/Eq/EqSpectrumView.cpp @@ -49,12 +49,12 @@ EqAnalyser::EqAnalyser() : //initialize Blackman-Harris window, constants taken from //https://en.wikipedia.org/wiki/Window_function#A_list_of_window_functions - const float a0 = 0.35875; - const float a1 = 0.48829; - const float a2 = 0.14128; - const float a3 = 0.01168; + const float a0 = 0.35875f; + const float a1 = 0.48829f; + const float a2 = 0.14128f; + const float a3 = 0.01168f; - for (int i = 0; i < FFT_BUFFER_SIZE; i++) + for (auto i = std::size_t{0}; i < FFT_BUFFER_SIZE; i++) { m_fftWindow[i] = (a0 - a1 * cos(2 * F_PI * i / ((float)FFT_BUFFER_SIZE - 1.0)) + a2 * cos(4 * F_PI * i / ((float)FFT_BUFFER_SIZE - 1.0)) @@ -75,7 +75,7 @@ EqAnalyser::~EqAnalyser() -void EqAnalyser::analyze( sampleFrame *buf, const fpp_t frames ) +void EqAnalyser::analyze( SampleFrame* buf, const fpp_t frames ) { //only analyse if the view is visible if ( m_active ) @@ -102,7 +102,7 @@ void EqAnalyser::analyze( sampleFrame *buf, const fpp_t frames ) return; } - m_sampleRate = Engine::audioEngine()->processingSampleRate(); + m_sampleRate = Engine::audioEngine()->outputSampleRate(); const int LOWEST_FREQ = 0; const int HIGHEST_FREQ = m_sampleRate / 2; @@ -228,13 +228,12 @@ void EqSpectrumView::paintEvent(QPaintEvent *event) //Now we calculate the path m_path = QPainterPath(); float *bands = m_analyser->m_bands; - float peak; m_path.moveTo( 0, height() ); m_peakSum = 0; - const float fallOff = 1.07; + const float fallOff = 1.07f; for( int x = 0; x < MAX_BANDS; ++x, ++bands ) { - peak = *bands != 0. ? (fh * 2.0 / 3.0 * (20. * log10(*bands / energy) - LOWER_Y) / (-LOWER_Y)) : 0.; + float peak = *bands != 0. ? (fh * 2.0 / 3.0 * (20. * log10(*bands / energy) - LOWER_Y) / (-LOWER_Y)) : 0.; if( peak < 0 ) { @@ -306,4 +305,4 @@ void EqSpectrumView::periodicalUpdate() } // namespace gui -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/plugins/Eq/EqSpectrumView.h b/plugins/Eq/EqSpectrumView.h index 947c5543470..448c0e3789f 100644 --- a/plugins/Eq/EqSpectrumView.h +++ b/plugins/Eq/EqSpectrumView.h @@ -32,6 +32,7 @@ namespace lmms { +class SampleFrame; const int MAX_BANDS = 2048; class EqAnalyser @@ -44,7 +45,7 @@ class EqAnalyser bool getInProgress(); void clear(); - void analyze( sampleFrame *buf, const fpp_t frames ); + void analyze( SampleFrame* buf, const fpp_t frames ); float getEnergy() const; int getSampleRate() const; diff --git a/plugins/Eq/faderback.png b/plugins/Eq/faderback.png deleted file mode 100644 index 2a03c3a5c1e..00000000000 Binary files a/plugins/Eq/faderback.png and /dev/null differ diff --git a/plugins/Eq/faderknob.png b/plugins/Eq/faderknob.png deleted file mode 100755 index 866cd863492..00000000000 Binary files a/plugins/Eq/faderknob.png and /dev/null differ diff --git a/plugins/Eq/faderleds.png b/plugins/Eq/faderleds.png deleted file mode 100644 index b99fac9fd5a..00000000000 Binary files a/plugins/Eq/faderleds.png and /dev/null differ diff --git a/plugins/Flanger/FlangerControls.cpp b/plugins/Flanger/FlangerControls.cpp index 5550cdfb75f..c5606380cdc 100644 --- a/plugins/Flanger/FlangerControls.cpp +++ b/plugins/Flanger/FlangerControls.cpp @@ -36,12 +36,12 @@ namespace lmms FlangerControls::FlangerControls( FlangerEffect *effect ) : EffectControls ( effect ), m_effect ( effect ), - m_delayTimeModel(0.001, 0.0001, 0.050, 0.0001, this, tr( "Delay samples" ) ), - m_lfoFrequencyModel( 0.25, 0.01, 60, 0.0001, 60000.0, this, tr( "LFO frequency" ) ), - m_lfoAmountModel( 0.0, 0.0, 0.0025, 0.0001, this, tr( "Amount" ) ), - m_lfoPhaseModel( 90.0, 0.0, 360.0, 0.0001, this, tr( "Stereo phase" ) ), - m_feedbackModel( 0.0, -1.0, 1.0, 0.0001, this, tr( "Feedback" ) ), - m_whiteNoiseAmountModel( 0.0, 0.0, 0.05, 0.0001, this, tr( "Noise" ) ), + m_delayTimeModel(0.001f, 0.0001f, 0.050f, 0.0001f, this, tr("Delay samples")), + m_lfoFrequencyModel(0.25f, 0.01f, 60, 0.0001f, 60000.f, this, tr("LFO frequency")), + m_lfoAmountModel(0.f, 0.f, 0.0025f, 0.0001f, this, tr("Amount")), + m_lfoPhaseModel(90.f, 0.f, 360.f, 0.0001f, this, tr("Stereo phase")), + m_feedbackModel(0.f, -1.f, 1.f, 0.0001f, this, tr("Feedback")), + m_whiteNoiseAmountModel(0.f, 0.f, 0.05f, 0.0001f, this, tr("Noise")), m_invertFeedbackModel ( false, this, tr( "Invert" ) ) { diff --git a/plugins/Flanger/FlangerEffect.cpp b/plugins/Flanger/FlangerEffect.cpp index 60b5df67b1f..b8bb9d692a0 100644 --- a/plugins/Flanger/FlangerEffect.cpp +++ b/plugins/Flanger/FlangerEffect.cpp @@ -58,9 +58,9 @@ FlangerEffect::FlangerEffect( Model *parent, const Plugin::Descriptor::SubPlugin Effect( &flanger_plugin_descriptor, parent, key ), m_flangerControls( this ) { - m_lfo = new QuadratureLfo( Engine::audioEngine()->processingSampleRate() ); - m_lDelay = new MonoDelay( 1, Engine::audioEngine()->processingSampleRate() ); - m_rDelay = new MonoDelay( 1, Engine::audioEngine()->processingSampleRate() ); + m_lfo = new QuadratureLfo( Engine::audioEngine()->outputSampleRate() ); + m_lDelay = new MonoDelay( 1, Engine::audioEngine()->outputSampleRate() ); + m_rDelay = new MonoDelay( 1, Engine::audioEngine()->outputSampleRate() ); m_noise = new Noise; } @@ -90,7 +90,7 @@ FlangerEffect::~FlangerEffect() -bool FlangerEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) +bool FlangerEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames ) { if( !isEnabled() || !isRunning () ) { @@ -99,19 +99,20 @@ bool FlangerEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) double outSum = 0.0; const float d = dryLevel(); const float w = wetLevel(); - const float length = m_flangerControls.m_delayTimeModel.value() * Engine::audioEngine()->processingSampleRate(); + const float length = m_flangerControls.m_delayTimeModel.value() * Engine::audioEngine()->outputSampleRate(); const float noise = m_flangerControls.m_whiteNoiseAmountModel.value(); - float amplitude = m_flangerControls.m_lfoAmountModel.value() * Engine::audioEngine()->processingSampleRate(); + float amplitude = m_flangerControls.m_lfoAmountModel.value() * Engine::audioEngine()->outputSampleRate(); bool invertFeedback = m_flangerControls.m_invertFeedbackModel.value(); m_lfo->setFrequency( 1.0/m_flangerControls.m_lfoFrequencyModel.value() ); m_lfo->setOffset( m_flangerControls.m_lfoPhaseModel.value() / 180 * D_PI ); m_lDelay->setFeedback( m_flangerControls.m_feedbackModel.value() ); m_rDelay->setFeedback( m_flangerControls.m_feedbackModel.value() ); auto dryS = std::array{}; - float leftLfo; - float rightLfo; for( fpp_t f = 0; f < frames; ++f ) { + float leftLfo; + float rightLfo; + buf[f][0] += m_noise->tick() * noise; buf[f][1] += m_noise->tick() * noise; dryS[0] = buf[f][0]; @@ -142,9 +143,9 @@ bool FlangerEffect::processAudioBuffer( sampleFrame *buf, const fpp_t frames ) void FlangerEffect::changeSampleRate() { - m_lfo->setSampleRate( Engine::audioEngine()->processingSampleRate() ); - m_lDelay->setSampleRate( Engine::audioEngine()->processingSampleRate() ); - m_rDelay->setSampleRate( Engine::audioEngine()->processingSampleRate() ); + m_lfo->setSampleRate( Engine::audioEngine()->outputSampleRate() ); + m_lDelay->setSampleRate( Engine::audioEngine()->outputSampleRate() ); + m_rDelay->setSampleRate( Engine::audioEngine()->outputSampleRate() ); } diff --git a/plugins/Flanger/FlangerEffect.h b/plugins/Flanger/FlangerEffect.h index 8428d89a304..c4afb8841be 100644 --- a/plugins/Flanger/FlangerEffect.h +++ b/plugins/Flanger/FlangerEffect.h @@ -42,7 +42,7 @@ class FlangerEffect : public Effect public: FlangerEffect( Model* parent , const Descriptor::SubPluginFeatures::Key* key ); ~FlangerEffect() override; - bool processAudioBuffer( sampleFrame *buf, const fpp_t frames ) override; + bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override; EffectControls* controls() override { return &m_flangerControls; diff --git a/plugins/Flanger/MonoDelay.cpp b/plugins/Flanger/MonoDelay.cpp index 3ec0289684f..269b77323f1 100644 --- a/plugins/Flanger/MonoDelay.cpp +++ b/plugins/Flanger/MonoDelay.cpp @@ -32,9 +32,9 @@ namespace lmms MonoDelay::MonoDelay( int maxTime , int sampleRate ) { m_buffer = 0; - m_maxTime = maxTime; + m_maxTime = static_cast(maxTime); m_maxLength = maxTime * sampleRate; - m_length = m_maxLength; + m_length = static_cast(m_maxLength); m_writeIndex = 0; m_feedback = 0.0f; @@ -57,7 +57,7 @@ MonoDelay::~MonoDelay() void MonoDelay::tick( sample_t* sample ) { m_writeIndex = ( m_writeIndex + 1 ) % ( int )m_maxLength; - int readIndex = m_writeIndex - m_length; + int readIndex = m_writeIndex - static_cast(m_length); if (readIndex < 0 ) { readIndex += m_maxLength; } float out = m_buffer[ readIndex ]; m_buffer[ m_writeIndex ] = *sample + ( out * m_feedback ); diff --git a/plugins/FreeBoy/CMakeLists.txt b/plugins/FreeBoy/CMakeLists.txt index 485ed3cc2e9..a08dfa439c6 100644 --- a/plugins/FreeBoy/CMakeLists.txt +++ b/plugins/FreeBoy/CMakeLists.txt @@ -1,21 +1,20 @@ -INCLUDE(BuildPlugin) -INCLUDE_DIRECTORIES(game-music-emu/gme) +include(BuildPlugin) -BUILD_PLUGIN(freeboy - FreeBoy.cpp - FreeBoy.h - GbApuWrapper.cpp - GbApuWrapper.h +add_library(gme STATIC game-music-emu/gme/Gb_Apu.cpp - game-music-emu/gme/Gb_Apu.h game-music-emu/gme/Gb_Oscs.cpp game-music-emu/gme/Blip_Buffer.cpp - game-music-emu/gme/Gb_Oscs.h - game-music-emu/gme/blargg_common.h - game-music-emu/gme/Blip_Buffer.h game-music-emu/gme/Multi_Buffer.cpp - game-music-emu/gme/blargg_source.h - game-music-emu/gme/Multi_Buffer.h +) +target_include_directories(gme PUBLIC game-music-emu/gme) +set_target_properties(gme PROPERTIES SYSTEM TRUE) + +build_plugin(freeboy + FreeBoy.cpp + FreeBoy.h + GbApuWrapper.cpp + GbApuWrapper.h MOCFILES FreeBoy.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png" ) +target_link_libraries(freeboy gme) diff --git a/plugins/FreeBoy/FreeBoy.cpp b/plugins/FreeBoy/FreeBoy.cpp index f2dc95699ed..e9acddeeb15 100644 --- a/plugins/FreeBoy/FreeBoy.cpp +++ b/plugins/FreeBoy/FreeBoy.cpp @@ -220,30 +220,18 @@ QString FreeBoyInstrument::nodeName() const -/*f_cnt_t FreeBoyInstrument::desiredReleaseFrames() const +float FreeBoyInstrument::desiredReleaseTimeMs() const { - const float samplerate = Engine::audioEngine()->processingSampleRate(); - int maxrel = 0; - for( int i = 0 ; i < 3 ; ++i ) - { - if( maxrel < m_voice[i]->m_releaseModel.value() ) - maxrel = m_voice[i]->m_releaseModel.value(); - } - - return f_cnt_t( float(relTime[maxrel])*samplerate/1000.0 ); -}*/ - -f_cnt_t FreeBoyInstrument::desiredReleaseFrames() const -{ - return f_cnt_t( 1000 ); + // Previous implementation was 1000 samples. At 44.1 kHz this is somewhat shy of 23. ms. + return 23.f; } -void FreeBoyInstrument::playNote(NotePlayHandle* nph, sampleFrame* workingBuffer) +void FreeBoyInstrument::playNote(NotePlayHandle* nph, SampleFrame* workingBuffer) { const f_cnt_t tfp = nph->totalFramesPlayed(); - const int samplerate = Engine::audioEngine()->processingSampleRate(); + const int samplerate = Engine::audioEngine()->outputSampleRate(); const fpp_t frames = nph->framesLeftForCurrentPeriod(); const f_cnt_t offset = nph->noteOffset(); @@ -392,9 +380,8 @@ void FreeBoyInstrument::playNote(NotePlayHandle* nph, sampleFrame* workingBuffer papu->writeRegister(0xff23, 128); } - constexpr int bufSize = 2048; - int framesLeft = frames; - int dataLen = 0; + constexpr auto bufSize = f_cnt_t{2048}; + auto framesLeft = frames; auto buf = std::array{}; while (framesLeft > 0) { @@ -404,12 +391,11 @@ void FreeBoyInstrument::playNote(NotePlayHandle* nph, sampleFrame* workingBuffer papu->endFrame(FRAME_LENGTH); avail = papu->samplesAvail(); } - dataLen = framesLeft > avail ? avail : framesLeft; - dataLen = dataLen > bufSize ? bufSize : dataLen; + const auto dataLen = std::min({static_cast(avail), framesLeft, bufSize}); - long count = papu->readSamples(buf.data(), dataLen * 2) / 2; + const auto count = static_cast(papu->readSamples(buf.data(), dataLen * 2) / 2); - for (fpp_t frame = 0; frame < count; ++frame) + for (auto frame = std::size_t{0}; frame < count; ++frame) { for (ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch) { diff --git a/plugins/FreeBoy/FreeBoy.h b/plugins/FreeBoy/FreeBoy.h index 747305414ba..3a178fdb1d0 100644 --- a/plugins/FreeBoy/FreeBoy.h +++ b/plugins/FreeBoy/FreeBoy.h @@ -26,8 +26,9 @@ #ifndef LMMS_FREEBOY_H #define LMMS_FREEBOY_H +#include + #include "AutomatableModel.h" -#include "Blip_Buffer.h" #include "Instrument.h" #include "InstrumentView.h" #include "Graph.h" @@ -54,7 +55,7 @@ class FreeBoyInstrument : public Instrument FreeBoyInstrument( InstrumentTrack * _instrument_track ); ~FreeBoyInstrument() override = default; - void playNote(NotePlayHandle* nph, sampleFrame* workingBuffer) override; + void playNote(NotePlayHandle* nph, SampleFrame* workingBuffer) override; void deleteNotePluginData(NotePlayHandle* nph) override; void saveSettings( QDomDocument & _doc, QDomElement & _parent ) override; @@ -62,7 +63,7 @@ class FreeBoyInstrument : public Instrument QString nodeName() const override; - f_cnt_t desiredReleaseFrames() const override; + float desiredReleaseTimeMs() const override; gui::PluginView* instantiateView( QWidget * _parent ) override; diff --git a/plugins/FreeBoy/GbApuWrapper.h b/plugins/FreeBoy/GbApuWrapper.h index 3b95869d579..88dc8261097 100644 --- a/plugins/FreeBoy/GbApuWrapper.h +++ b/plugins/FreeBoy/GbApuWrapper.h @@ -24,8 +24,8 @@ #ifndef LMMS_GB_APU_WRAPPER_H #define LMMS_GB_APU_WRAPPER_H -#include "Gb_Apu.h" -#include "Multi_Buffer.h" +#include +#include namespace lmms { diff --git a/plugins/FreeBoy/game-music-emu b/plugins/FreeBoy/game-music-emu index 6b676192d98..21a064ea66a 160000 --- a/plugins/FreeBoy/game-music-emu +++ b/plugins/FreeBoy/game-music-emu @@ -1 +1 @@ -Subproject commit 6b676192d98302e698ac78fe3c00833eae6a74e5 +Subproject commit 21a064ea66a5cdf71910e207c4756095c266814f diff --git a/plugins/GigPlayer/CMakeLists.txt b/plugins/GigPlayer/CMakeLists.txt index 7b634b605ce..ccec8c87336 100644 --- a/plugins/GigPlayer/CMakeLists.txt +++ b/plugins/GigPlayer/CMakeLists.txt @@ -1,23 +1,16 @@ if(LMMS_HAVE_GIG) INCLUDE(BuildPlugin) - INCLUDE_DIRECTORIES(${GIG_INCLUDE_DIRS}) + include_directories(SYSTEM ${GIG_INCLUDE_DIRS}) + SET(CMAKE_AUTOUIC ON) # Required for not crashing loading files with libgig - SET(GCC_COVERAGE_COMPILE_FLAGS "-fexceptions") - add_definitions(${GCC_COVERAGE_COMPILE_FLAGS}) - - # disable deprecated check for mingw-x-libgig - if(LMMS_BUILD_WIN32) - SET(GCC_GIG_COMPILE_FLAGS "-Wno-deprecated") - add_definitions(${GCC_GIG_COMPILE_FLAGS}) - endif(LMMS_BUILD_WIN32) + add_compile_options("-fexceptions") link_directories(${GIG_LIBRARY_DIRS}) link_libraries(${GIG_LIBRARIES}) build_plugin(gigplayer GigPlayer.cpp GigPlayer.h PatchesDialog.cpp PatchesDialog.h PatchesDialog.ui MOCFILES GigPlayer.h PatchesDialog.h - UICFILES PatchesDialog.ui EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png" ) target_link_libraries(gigplayer SampleRate::samplerate) diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index 2d67f0ddff3..b72e30b3335 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -81,7 +81,7 @@ Plugin::Descriptor PLUGIN_EXPORT gigplayer_plugin_descriptor = GigInstrument::GigInstrument( InstrumentTrack * _instrument_track ) : - Instrument( _instrument_track, &gigplayer_plugin_descriptor ), + Instrument(_instrument_track, &gigplayer_plugin_descriptor, nullptr, Flag::IsSingleStreamed | Flag::IsNotBendable), m_instance( nullptr ), m_instrument( nullptr ), m_filename( "" ), @@ -289,7 +289,7 @@ QString GigInstrument::getCurrentPatchName() // A key has been pressed -void GigInstrument::playNote( NotePlayHandle * _n, sampleFrame * ) +void GigInstrument::playNote( NotePlayHandle * _n, SampleFrame* ) { const float LOG440 = 2.643452676f; @@ -320,10 +320,10 @@ void GigInstrument::playNote( NotePlayHandle * _n, sampleFrame * ) // Process the notes and output a certain number of frames (e.g. 256, set in // the preferences) -void GigInstrument::play( sampleFrame * _working_buffer ) +void GigInstrument::play( SampleFrame* _working_buffer ) { const fpp_t frames = Engine::audioEngine()->framesPerPeriod(); - const int rate = Engine::audioEngine()->processingSampleRate(); + const auto rate = Engine::audioEngine()->outputSampleRate(); // Initialize to zeros std::memset( &_working_buffer[0][0], 0, DEFAULT_CHANNELS * frames * sizeof( float ) ); @@ -441,7 +441,7 @@ void GigInstrument::play( sampleFrame * _working_buffer ) } // Load this note's data - sampleFrame sampleData[samples]; + SampleFrame sampleData[samples]; loadSample(sample, sampleData, samples); // Apply ADSR using a copy so if we don't use these samples when @@ -458,7 +458,7 @@ void GigInstrument::play( sampleFrame * _working_buffer ) // Output the data resampling if needed if( resample == true ) { - sampleFrame convertBuf[frames]; + SampleFrame convertBuf[frames]; // Only output if resampling is successful (note that "used" is output) if (sample.convertSampleRate(*sampleData, *convertBuf, samples, frames, freq_factor, used)) @@ -499,7 +499,7 @@ void GigInstrument::play( sampleFrame * _working_buffer ) -void GigInstrument::loadSample( GigSample& sample, sampleFrame* sampleData, f_cnt_t samples ) +void GigInstrument::loadSample( GigSample& sample, SampleFrame* sampleData, f_cnt_t samples ) { if( sampleData == nullptr || samples < 1 ) { @@ -746,7 +746,7 @@ void GigInstrument::addSamples( GigNote & gignote, bool wantReleaseSample ) if( gignote.midiNote >= keyLow && gignote.midiNote <= keyHigh ) { float attenuation = pDimRegion->GetVelocityAttenuation( gignote.velocity ); - float length = (float) pSample->SamplesTotal / Engine::audioEngine()->processingSampleRate(); + float length = (float) pSample->SamplesTotal / Engine::audioEngine()->outputSampleRate(); // TODO: sample panning? crossfade different layers? @@ -1182,7 +1182,7 @@ void GigSample::updateSampleRate() -bool GigSample::convertSampleRate( sampleFrame & oldBuf, sampleFrame & newBuf, +bool GigSample::convertSampleRate( SampleFrame & oldBuf, SampleFrame & newBuf, f_cnt_t oldSize, f_cnt_t newSize, float freq_factor, f_cnt_t& used ) { if( srcState == nullptr ) @@ -1216,7 +1216,7 @@ bool GigSample::convertSampleRate( sampleFrame & oldBuf, sampleFrame & newBuf, return false; } - if( src_data.output_frames_gen > 0 && src_data.output_frames_gen < newSize ) + if (src_data.output_frames_gen > 0 && static_cast(src_data.output_frames_gen) < newSize) { qCritical() << "GigInstrument: not enough frames, wanted" << newSize << "generated" << src_data.output_frames_gen; diff --git a/plugins/GigPlayer/GigPlayer.h b/plugins/GigPlayer/GigPlayer.h index 986018654a7..685c7f5469a 100644 --- a/plugins/GigPlayer/GigPlayer.h +++ b/plugins/GigPlayer/GigPlayer.h @@ -157,7 +157,7 @@ class GigSample // Needed since libsamplerate stores data internally between calls void updateSampleRate(); - bool convertSampleRate( sampleFrame & oldBuf, sampleFrame & newBuf, + bool convertSampleRate( SampleFrame & oldBuf, SampleFrame & newBuf, f_cnt_t oldSize, f_cnt_t newSize, float freq_factor, f_cnt_t& used ); gig::Sample * sample; @@ -243,10 +243,10 @@ class GigInstrument : public Instrument GigInstrument( InstrumentTrack * _instrument_track ); ~GigInstrument() override; - void play( sampleFrame * _working_buffer ) override; + void play( SampleFrame* _working_buffer ) override; void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; + SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; @@ -259,16 +259,6 @@ class GigInstrument : public Instrument QString nodeName() const override; - f_cnt_t desiredReleaseFrames() const override - { - return 0; - } - - Flags flags() const override - { - return Flag::IsSingleStreamed | Flag::IsNotBendable; - } - gui::PluginView* instantiateView( QWidget * _parent ) override; QString getCurrentPatchName(); @@ -322,7 +312,7 @@ public slots: Dimension getDimensions( gig::Region * pRegion, int velocity, bool release ); // Load sample data from the Gig file, looping the sample where needed - void loadSample( GigSample& sample, sampleFrame* sampleData, f_cnt_t samples ); + void loadSample( GigSample& sample, SampleFrame* sampleData, f_cnt_t samples ); f_cnt_t getLoopedIndex( f_cnt_t index, f_cnt_t startf, f_cnt_t endf ) const; f_cnt_t getPingPongIndex( f_cnt_t index, f_cnt_t startf, f_cnt_t endf ) const; diff --git a/plugins/GranularPitchShifter/CMakeLists.txt b/plugins/GranularPitchShifter/CMakeLists.txt new file mode 100755 index 00000000000..c8f70fc7882 --- /dev/null +++ b/plugins/GranularPitchShifter/CMakeLists.txt @@ -0,0 +1,9 @@ +include(BuildPlugin) +build_plugin(granularpitchshifter + GranularPitchShifterEffect.cpp + GranularPitchShifterControls.cpp + GranularPitchShifterControlDialog.cpp + MOCFILES + GranularPitchShifterControls.h + GranularPitchShifterControlDialog.h + EMBEDDED_RESOURCES *.png) diff --git a/plugins/GranularPitchShifter/GranularPitchShifterControlDialog.cpp b/plugins/GranularPitchShifter/GranularPitchShifterControlDialog.cpp new file mode 100755 index 00000000000..71a8d15f716 --- /dev/null +++ b/plugins/GranularPitchShifter/GranularPitchShifterControlDialog.cpp @@ -0,0 +1,158 @@ +/* + * GranularPitchShifterControlDialog.cpp + * + * Copyright (c) 2024 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "GranularPitchShifterControlDialog.h" +#include "GranularPitchShifterControls.h" +#include "embed.h" +#include "LcdFloatSpinBox.h" +#include "Knob.h" +#include "GuiApplication.h" +#include "gui_templates.h" +#include "PixmapButton.h" + + +namespace lmms::gui +{ + +GranularPitchShifterControlDialog::GranularPitchShifterControlDialog(GranularPitchShifterControls* controls) : + EffectControlDialog(controls) +{ + setAutoFillBackground(true); + QPalette pal; + pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("artwork")); + setPalette(pal); + setFixedSize(305, 180); + + auto makeKnob = [this](KnobType style, int x, int y, const QString& hintText, const QString& unit, FloatModel* model) + { + Knob* newKnob = new Knob(style, this); + newKnob->move(x, y); + newKnob->setModel(model); + newKnob->setHintText(hintText, unit); + return newKnob; + }; + + makeKnob(KnobType::Bright26, 19, 78, tr("Grain Size:"), " Hz", &controls->m_sizeModel); + makeKnob(KnobType::Bright26, 116, 10, tr("Spray:"), " seconds", &controls->m_sprayModel); + makeKnob(KnobType::Bright26, 158, 10, tr("Jitter:"), " octaves", &controls->m_jitterModel); + makeKnob(KnobType::Bright26, 200, 10, tr("Twitch:"), " octaves", &controls->m_twitchModel); + makeKnob(KnobType::Bright26, 188, 60, tr("Spray Stereo Spread:"), "", &controls->m_spraySpreadModel); + makeKnob(KnobType::Bright26, 135, 110, tr("Grain Shape:"), "", &controls->m_shapeModel); + makeKnob(KnobType::Bright26, 188, 110, tr("Fade Length:"), "", &controls->m_fadeLengthModel); + makeKnob(KnobType::Bright26, 258, 45, tr("Feedback:"), "", &controls->m_feedbackModel); + makeKnob(KnobType::Bright26, 258, 92, tr("Minimum Allowed Latency:"), " seconds", &controls->m_minLatencyModel); + makeKnob(KnobType::Small17, 66, 157, tr("Density:"), "x", &controls->m_densityModel); + makeKnob(KnobType::Small17, 8, 157, tr("Glide:"), " seconds", &controls->m_glideModel); + + LcdFloatSpinBox* pitchBox = new LcdFloatSpinBox(3, 2, "11green", tr("Pitch"), this); + pitchBox->move(15, 41); + pitchBox->setModel(&controls->m_pitchModel); + pitchBox->setToolTip(tr("Pitch")); + pitchBox->setSeamless(true, true); + + LcdFloatSpinBox* pitchSpreadBox = new LcdFloatSpinBox(3, 2, "11green", tr("Pitch Stereo Spread"), this); + pitchSpreadBox->move(133, 66); + pitchSpreadBox->setModel(&controls->m_pitchSpreadModel); + pitchSpreadBox->setToolTip(tr("Pitch Stereo Spread")); + pitchSpreadBox->setSeamless(true, true); + + QPushButton button("Show Help", this); + connect(&button, &QPushButton::clicked, this, &GranularPitchShifterControlDialog::showHelpWindow); + + PixmapButton* m_helpBtn = new PixmapButton(this, nullptr); + m_helpBtn->move(278, 159); + m_helpBtn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("help_active")); + m_helpBtn->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("help_inactive")); + m_helpBtn->setToolTip(tr("Open help window")); + connect(m_helpBtn, SIGNAL(clicked()), this, SLOT(showHelpWindow())); + + PixmapButton* prefilterButton = new PixmapButton(this, tr("Prefilter")); + prefilterButton->move(8, 133); + prefilterButton->setActiveGraphic(PLUGIN_NAME::getIconPixmap("prefilter_active")); + prefilterButton->setInactiveGraphic(PLUGIN_NAME::getIconPixmap("prefilter_inactive")); + prefilterButton->setCheckable(true); + prefilterButton->setModel(&controls->m_prefilterModel); + prefilterButton->setToolTip(tr("Prefilter")); + + ComboBox* rangeBox = new ComboBox(this); + rangeBox->setGeometry(189, 155, 80, 22); + rangeBox->setModel(&controls->m_rangeModel); + controls->updateRange(); +} + +void GranularPitchShifterControlDialog::showHelpWindow() { + GranularPitchShifterHelpView::getInstance()->close(); + GranularPitchShifterHelpView::getInstance()->show(); +} + + +QString GranularPitchShifterHelpView::s_helpText= +"
" +"Granular Pitch Shifter

" +"Plugin by Lost Robot
" +"GUI by thismoon
" +"
" +"

Grain:

" +"Pitch - The amount of pitch shifting to perform, in 12EDO semitones.
" +"Size - The length of each grain, in Hz. By default, new grains will be created at double this rate.
In most cases, you'll want this to be set to higher frequencies when shifting the pitch upward, and vice-versa.
" +"

Random:

" +"Spray - The amount of randomization for the playback position of each grain, in seconds.
This does not change when the grain plays, but rather what audio the grain is pulling from.
For example, a value of 0.5 seconds will allow each grain to play back audio from up to half of a second ago.
It's oftentimes recommended to use at least a small amount of Spray, as this will break up the periodicity in the grains, which is usually the main artifact caused by a granular pitch shifter.
This will also make the grains uncorrelated with each other, guaranteeing that a grain Shape value of 2 will always be optimal.
" +"Jitter - The amount of randomization for the pitch of each grain, in octaves.
This does not impact how often grains are created.
" +"Twitch - The amount of randomization for how often new grains are created, in octaves.
Jitter and Twitch both use the same random numbers, so if they're at the same value, then the grain creation timings will be changed exactly proportionally to their change in pitch.
" +"

Stereo:

" +"Pitch - The total distance in pitch between both stereo channels, in 12EDO semitones.
Half of the amount of pitch shifting shown will be applied to the right channel, and the opposite to the left channel.
" +"Spray - The allowed distance between each channel's randomized position with the Spray feature in the Random category.
A value of 1 makes the Spray values in each channel entirely unlinked." +"

Shape:

" +"Shape - The shape of each grain's fades. In most cases, 2 is the optimal value, providing equal-power fades.
However, when the plugin is performing minimal pitch shifting and has most of its parameters at default, a value of 1 may be more optimal, providing equal-gain fades.
All fades are designed for 50% grain overlap.
" +"Fade - The length of the grain fades. A value of 1 provides the cleanest fades, causing those fades to reach across the entire grain.
Values below 1 make the fade artifacts more audible, but those fades will only apply to the outer edges of each grain.
A value of 0 will result in clicking sounds due to the fades no longer being present.
" +"

Delay:

" +"Feedback - The amount of feedback for the pitch shifter.
This feeds a portion of the pitch shifter output back into the input buffer. Large values can be dangerous.
" +"Latency - The minimum amount of latency the pitch shifter will have.
This granular pitch shifter dynamically changes its latency to be at the minimum possible amount depending on your settings.
If you'd like for this latency to be more predictable, you may increase the value of this parameter until the latency no longer changes.
This parameter may also be used to be set the minimum amount of delay for the feedback.
A larger latency amount can remove subtle fluttering artifacts that may result from automating the pitch shifting amount at high speeds." +"

Miscellaneous:

" +"Prefilter - Enables a 12 dB lowpass filter prior to the pitch shifting which automatically adjusts its cutoff to drastically reduce any resulting aliasing.
" +"Density - The multiplier for how often grains are spawned.
This will increase the grain overlap above 50%.
It will create painful piercing sounds if you don't make use of any of the knobs in the Random category.
Otherwise, you can get some interesting effects similar to unison or a stationary Paulstretch.
Note that this knob uses by far the most CPU out of any parameter in this plugin when increased.
" +"Glide - The length of interpolation for the amount of pitch shifting.
A small amount of glide is very effective for cleaning up many of the artifacts that may result from changing the pitch shift amount over time.
" +"Range - The length of the pitch shifter's internal ring buffer.
Changing this will change the minimum and maximum values for some of the other parameters, which are listed in each of the options.
Increase it if you need parameter values that aren't supported with the minimum buffer length. Otherwise, it's best to leave it at its minimum value.
" +; + +GranularPitchShifterHelpView::GranularPitchShifterHelpView():QTextEdit(s_helpText) +{ +#if (QT_VERSION < QT_VERSION_CHECK(5,12,0)) + // Bug workaround: https://codereview.qt-project.org/c/qt/qtbase/+/225348 + using ::operator|; +#endif + setWindowTitle("Granular Pitch Shifter Help"); + setTextInteractionFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse); + getGUI()->mainWindow()->addWindowedWidget(this); + parentWidget()->setAttribute(Qt::WA_DeleteOnClose, false); + parentWidget()->setWindowIcon(PLUGIN_NAME::getIconPixmap("logo")); + + // No maximize button + Qt::WindowFlags flags = parentWidget()->windowFlags(); + flags &= ~Qt::WindowMaximizeButtonHint; + parentWidget()->setWindowFlags(flags); +} + + +} // namespace lmms::gui diff --git a/plugins/GranularPitchShifter/GranularPitchShifterControlDialog.h b/plugins/GranularPitchShifter/GranularPitchShifterControlDialog.h new file mode 100755 index 00000000000..751106b2ce2 --- /dev/null +++ b/plugins/GranularPitchShifter/GranularPitchShifterControlDialog.h @@ -0,0 +1,75 @@ +/* + * GranularPitchShifterControlDialog.h + * + * Copyright (c) 2024 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_GRANULAR_PITCH_SHIFTER_CONTROLS_H +#define LMMS_GRANULAR_PITCH_SHIFTER_CONTROLS_H + +#include "EffectControlDialog.h" + +#include +#include "ComboBox.h" +#include "GuiApplication.h" +#include "MainWindow.h" + +namespace lmms +{ + +class GranularPitchShifterControls; +class FloatModel; + +namespace gui +{ + +class Knob; + +class GranularPitchShifterControlDialog : public EffectControlDialog +{ + Q_OBJECT +public: + GranularPitchShifterControlDialog(GranularPitchShifterControls* controls); + ~GranularPitchShifterControlDialog() override = default; +public slots: + void showHelpWindow(); +}; + +class GranularPitchShifterHelpView : public QTextEdit +{ + Q_OBJECT +public: + static GranularPitchShifterHelpView* getInstance() + { + static GranularPitchShifterHelpView instance; + return &instance; + } + +private: + GranularPitchShifterHelpView(); + static QString s_helpText; +}; + +} // namespace gui + +} // namespace lmms + +#endif // LMMS_GRANULAR_PITCH_SHIFTER_CONTROLS_H diff --git a/plugins/GranularPitchShifter/GranularPitchShifterControls.cpp b/plugins/GranularPitchShifter/GranularPitchShifterControls.cpp new file mode 100755 index 00000000000..86e9a0cfd7b --- /dev/null +++ b/plugins/GranularPitchShifter/GranularPitchShifterControls.cpp @@ -0,0 +1,149 @@ +/* + * GranularPitchShifterControls.cpp + * + * Copyright (c) 2024 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include + +#include "GranularPitchShifterControls.h" +#include "GranularPitchShifterEffect.h" + +namespace lmms +{ + +GranularPitchShifterControls::GranularPitchShifterControls(GranularPitchShifterEffect* effect) : + EffectControls(effect), + m_effect(effect), + m_pitchModel(1.f, -48.f, 24.f, 0.01f, this, tr("Pitch")), + m_sizeModel(10.f, 2.f, 1000.f, 0.001f, this, tr("Grain Size")), + m_sprayModel(0.005f, 0.f, 0.5f, 0.0001f, this, tr("Spray")), + m_jitterModel(0.f, 0.f, 1.f, 0.0001f, this, tr("Jitter")), + m_twitchModel(0.f, 0.f, 1.f, 0.0001f, this, tr("Twitch")), + m_pitchSpreadModel(0.f, -24.f, 24.f, 0.01f, this, tr("Pitch Stereo Spread")), + m_spraySpreadModel(0.f, 0.f, 1.f, 0.0001f, this, tr("Spray Stereo")), + m_shapeModel(2.f, 1.f, 2.f, 0.0001f, this, tr("Shape")), + m_fadeLengthModel(1.f, 0.001f, 1.f, 0.00001f, this, tr("Fade Length")), + m_feedbackModel(0.f, 0.f, 1.f, 0.00001f, this, tr("Feedback")), + m_minLatencyModel(0.01f, 0.f, 1.f, 0.00001f, this, tr("Minimum Allowed Latency")), + m_prefilterModel(true, this, tr("Prefilter")), + m_densityModel(1.f, 1.f, 16.f, 0.0001f, this, tr("Density")), + m_glideModel(0.01f, 0.f, 1.f, 0.0001f, this, tr("Glide")), + m_rangeModel(this, tr("Ring Buffer Length")) +{ + m_sizeModel.setScaleLogarithmic(true); + m_sprayModel.setScaleLogarithmic(true); + m_spraySpreadModel.setScaleLogarithmic(true); + m_minLatencyModel.setScaleLogarithmic(true); + m_densityModel.setScaleLogarithmic(true); + m_glideModel.setScaleLogarithmic(true); + + m_rangeModel.addItem(tr("5 Seconds")); + m_rangeModel.addItem(tr("10 Seconds (Size)")); + m_rangeModel.addItem(tr("40 Seconds (Size and Pitch)")); + m_rangeModel.addItem(tr("40 Seconds (Size and Spray and Jitter)")); + m_rangeModel.addItem(tr("120 Seconds (All of the above)")); + + connect(&m_rangeModel, &ComboBoxModel::dataChanged, this, &GranularPitchShifterControls::updateRange); +} + +void GranularPitchShifterControls::updateRange() +{ + switch (m_rangeModel.value()) + { + case 0:// 5 seconds + m_sizeModel.setRange(4.f, 1000.f, 0.001f); + m_pitchModel.setRange(-48.f, 24.f, 0.01f); + m_sprayModel.setRange(0.f, 0.5f, 0.0001f); + m_jitterModel.setRange(0.f, 1.f, 0.0001f); + break; + case 1:// 10 seconds (size) + m_sizeModel.setRange(2.f, 1000.f, 0.001f); + m_pitchModel.setRange(-48.f, 24.f, 0.01f); + m_sprayModel.setRange(0.f, 0.5f, 0.0001f); + m_jitterModel.setRange(0.f, 1.f, 0.0001f); + break; + case 2:// 40 seconds (size and pitch) + m_sizeModel.setRange(2.f, 1000.f, 0.001f); + m_pitchModel.setRange(-48.f, 48.f, 0.01f); + m_sprayModel.setRange(0.f, 0.5f, 0.0001f); + m_jitterModel.setRange(0.f, 1.f, 0.0001f); + break; + case 3:// 40 seconds (size and spray and jitter) + m_sizeModel.setRange(2.f, 1000.f, 0.001f); + m_pitchModel.setRange(-48.f, 24.f, 0.01f); + m_sprayModel.setRange(0.f, 20.f, 0.0001f); + m_jitterModel.setRange(0.f, 2.f, 0.0001f); + break; + case 4:// 120 seconds (all of the above) + m_sizeModel.setRange(2.f, 1000.f, 0.001f); + m_pitchModel.setRange(-48.f, 48.f, 0.01f); + m_sprayModel.setRange(0.f, 40.f, 0.0001f); + m_jitterModel.setRange(0.f, 2.f, 0.0001f); + break; + default: + break; + } + m_effect->sampleRateNeedsUpdate(); +} + +void GranularPitchShifterControls::loadSettings(const QDomElement& parent) +{ + // must be loaded first so the ranges are set properly + m_rangeModel.loadSettings(parent, "range"); + + m_pitchModel.loadSettings(parent, "pitch"); + m_sizeModel.loadSettings(parent, "size"); + m_sprayModel.loadSettings(parent, "spray"); + m_jitterModel.loadSettings(parent, "jitter"); + m_twitchModel.loadSettings(parent, "twitch"); + m_pitchSpreadModel.loadSettings(parent, "pitchSpread"); + m_spraySpreadModel.loadSettings(parent, "spraySpread"); + m_shapeModel.loadSettings(parent, "shape"); + m_fadeLengthModel.loadSettings(parent, "fadeLength"); + m_feedbackModel.loadSettings(parent, "feedback"); + m_minLatencyModel.loadSettings(parent, "minLatency"); + m_prefilterModel.loadSettings(parent, "prefilter"); + m_densityModel.loadSettings(parent, "density"); + m_glideModel.loadSettings(parent, "glide"); +} + +void GranularPitchShifterControls::saveSettings(QDomDocument& doc, QDomElement& parent) +{ + m_rangeModel.saveSettings(doc, parent, "range"); + m_pitchModel.saveSettings(doc, parent, "pitch"); + m_sizeModel.saveSettings(doc, parent, "size"); + m_sprayModel.saveSettings(doc, parent, "spray"); + m_jitterModel.saveSettings(doc, parent, "jitter"); + m_twitchModel.saveSettings(doc, parent, "twitch"); + m_pitchSpreadModel.saveSettings(doc, parent, "pitchSpread"); + m_spraySpreadModel.saveSettings(doc, parent, "spraySpread"); + m_shapeModel.saveSettings(doc, parent, "shape"); + m_fadeLengthModel.saveSettings(doc, parent, "fadeLength"); + m_feedbackModel.saveSettings(doc, parent, "feedback"); + m_minLatencyModel.saveSettings(doc, parent, "minLatency"); + m_prefilterModel.saveSettings(doc, parent, "prefilter"); + m_densityModel.saveSettings(doc, parent, "density"); + m_glideModel.saveSettings(doc, parent, "glide"); +} + + +} // namespace lmms diff --git a/plugins/GranularPitchShifter/GranularPitchShifterControls.h b/plugins/GranularPitchShifter/GranularPitchShifterControls.h new file mode 100755 index 00000000000..3ae8f881941 --- /dev/null +++ b/plugins/GranularPitchShifter/GranularPitchShifterControls.h @@ -0,0 +1,87 @@ +/* + * GranularPitchShifterControls.h + * + * Copyright (c) 2024 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_GRANULAR_PITCH_SHIFTER_CONTROL_DIALOG_H +#define LMMS_GRANULAR_PITCH_SHIFTER_CONTROL_DIALOG_H + +#include "EffectControls.h" +#include "GranularPitchShifterControlDialog.h" + +namespace lmms +{ + +class GranularPitchShifterEffect; + +namespace gui +{ +class GranularPitchShifterControlDialog; +} + +class GranularPitchShifterControls : public EffectControls +{ + Q_OBJECT +public: + GranularPitchShifterControls(GranularPitchShifterEffect* effect); + ~GranularPitchShifterControls() override = default; + + void saveSettings(QDomDocument& doc, QDomElement& parent) override; + void loadSettings(const QDomElement& parent) override; + inline QString nodeName() const override + { + return "GranularPitchShifterControls"; + } + gui::EffectControlDialog* createView() override + { + return new gui::GranularPitchShifterControlDialog(this); + } + int controlCount() override { return 4; } + +public slots: + void updateRange(); + +private: + GranularPitchShifterEffect* m_effect; + FloatModel m_pitchModel; + FloatModel m_sizeModel; + FloatModel m_sprayModel; + FloatModel m_jitterModel; + FloatModel m_twitchModel; + FloatModel m_pitchSpreadModel; + FloatModel m_spraySpreadModel; + FloatModel m_shapeModel; + FloatModel m_fadeLengthModel; + FloatModel m_feedbackModel; + FloatModel m_minLatencyModel; + BoolModel m_prefilterModel; + FloatModel m_densityModel; + FloatModel m_glideModel; + ComboBoxModel m_rangeModel; + + friend class gui::GranularPitchShifterControlDialog; + friend class GranularPitchShifterEffect; +}; + +} // namespace lmms + +#endif // LMMS_GRANULAR_PITCH_SHIFTER_CONTROL_DIALOG_H diff --git a/plugins/GranularPitchShifter/GranularPitchShifterEffect.cpp b/plugins/GranularPitchShifter/GranularPitchShifterEffect.cpp new file mode 100755 index 00000000000..992d05304ca --- /dev/null +++ b/plugins/GranularPitchShifter/GranularPitchShifterEffect.cpp @@ -0,0 +1,296 @@ +/* + * GranularPitchShifter.cpp + * + * Copyright (c) 2024 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "GranularPitchShifterEffect.h" + +#include +#include "embed.h" +#include "plugin_export.h" + + +namespace lmms +{ + +extern "C" +{ +Plugin::Descriptor PLUGIN_EXPORT granularpitchshifter_plugin_descriptor = +{ + LMMS_STRINGIFY(PLUGIN_NAME), + "Granular Pitch Shifter", + QT_TRANSLATE_NOOP("PluginBrowser", "Granular pitch shifter"), + "Lost Robot ", + 0x0100, + Plugin::Type::Effect, + new PluginPixmapLoader("logo"), + nullptr, + nullptr, +} ; +} + + +GranularPitchShifterEffect::GranularPitchShifterEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key) : + Effect(&granularpitchshifter_plugin_descriptor, parent, key), + m_granularpitchshifterControls(this), + m_prefilter({PrefilterLowpass(), PrefilterLowpass()}) +{ + autoQuitModel()->setValue(autoQuitModel()->maxValue()); + + changeSampleRate(); +} + + +bool GranularPitchShifterEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames) +{ + if (!isEnabled() || !isRunning()) { return false; } + + const float d = dryLevel(); + const float w = wetLevel(); + + const ValueBuffer* pitchBuf = m_granularpitchshifterControls.m_pitchModel.valueBuffer(); + const ValueBuffer* pitchSpreadBuf = m_granularpitchshifterControls.m_pitchSpreadModel.valueBuffer(); + + const float size = m_granularpitchshifterControls.m_sizeModel.value(); + const float shape = m_granularpitchshifterControls.m_shapeModel.value(); + const float jitter = m_granularpitchshifterControls.m_jitterModel.value(); + const float twitch = m_granularpitchshifterControls.m_twitchModel.value(); + const float spray = m_granularpitchshifterControls.m_sprayModel.value(); + const float spraySpread = m_granularpitchshifterControls.m_spraySpreadModel.value(); + const float density = m_granularpitchshifterControls.m_densityModel.value(); + const float glide = m_granularpitchshifterControls.m_glideModel.value(); + const int minLatency = m_granularpitchshifterControls.m_minLatencyModel.value() * m_sampleRate; + const float densityInvRoot = std::sqrt(1.f / density); + const float feedback = m_granularpitchshifterControls.m_feedbackModel.value(); + const float fadeLength = 1.f / m_granularpitchshifterControls.m_fadeLengthModel.value(); + const bool prefilter = m_granularpitchshifterControls.m_prefilterModel.value(); + + if (glide != m_oldGlide) + { + m_oldGlide = glide; + m_glideCoef = glide > 0 ? std::exp(-1 / (glide * m_sampleRate)) : 0; + } + + const float shapeK = cosWindowApproxK(shape); + const int sizeSamples = m_sampleRate / size; + const float waitMult = sizeSamples / (density * 2); + + for (fpp_t f = 0; f < frames; ++f) + { + const double pitch = (pitchBuf ? pitchBuf->value(f) : m_granularpitchshifterControls.m_pitchModel.value()) * (1. / 12.); + const double pitchSpread = (pitchSpreadBuf ? pitchSpreadBuf->value(f) : m_granularpitchshifterControls.m_pitchSpreadModel.value()) * (1. / 24.); + + // interpolate pitch depending on glide + for (int i = 0; i < 2; ++i) + { + double targetVal = pitch + pitchSpread * (i ? 1. : -1.); + + if (targetVal == m_truePitch[i]) { continue; } + m_updatePitches = true; + + m_truePitch[i] = m_glideCoef * m_truePitch[i] + (1. - m_glideCoef) * targetVal; + // we crudely lock the pitch to the target value once it gets close enough, so we can save on CPU + if (std::abs(targetVal - m_truePitch[i]) < GlideSnagRadius) { m_truePitch[i] = targetVal; } + } + + // this stuff is computationally expensive, so we should only do it when necessary + if (m_updatePitches) + { + m_updatePitches = false; + + std::array speed = { + std::exp2(m_truePitch[0]), + std::exp2(m_truePitch[1]) + }; + std::array ratio = { + speed[0] / m_speed[0], + speed[1] / m_speed[1] + }; + + for (int i = 0; i < m_grainCount; ++i) + { + for (int j = 0; j < 2; ++j) + { + m_grains[i].grainSpeed[j] *= ratio[j]; + + // we unfortunately need to do extra stuff to ensure these don't shoot past the write index... + if (m_grains[i].grainSpeed[j] > 1) + { + double distance = m_writePoint - m_grains[i].readPoint[j] - SafetyLatency; + if (distance <= 0) { distance += m_ringBufLength; } + double grainSpeedRequired = ((m_grains[i].grainSpeed[j] - 1.) / distance) * (1. - m_grains[i].phase); + m_grains[i].phaseSpeed[j] = std::max(m_grains[i].phaseSpeed[j], grainSpeedRequired); + } + } + } + m_speed[0] = speed[0]; + m_speed[1] = speed[1]; + + // prevent aliasing by lowpassing frequencies that the pitch shifting would push above nyquist + m_prefilter[0].setCoefs(m_sampleRate, std::min(m_nyquist / static_cast(speed[0]), m_nyquist) * PrefilterBandwidth); + m_prefilter[1].setCoefs(m_sampleRate, std::min(m_nyquist / static_cast(speed[1]), m_nyquist) * PrefilterBandwidth); + } + + std::array s = {0, 0}; + std::array filtered = {buf[f][0], buf[f][1]}; + + // spawn a new grain if it's time + if (++m_timeSinceLastGrain >= m_nextWaitRandomization * waitMult) + { + m_timeSinceLastGrain = 0; + double randThing = (fast_rand()/static_cast(FAST_RAND_MAX) * 2. - 1.); + m_nextWaitRandomization = std::exp2(randThing * twitch); + double grainSpeed = 1. / std::exp2(randThing * jitter); + + std::array sprayResult = {0, 0}; + if (spray > 0) + { + sprayResult[0] = (fast_rand() / static_cast(FAST_RAND_MAX)) * spray * m_sampleRate; + sprayResult[1] = linearInterpolate( + sprayResult[0], + (fast_rand() / static_cast(FAST_RAND_MAX)) * spray * m_sampleRate, + spraySpread); + } + + std::array readPoint; + int latency = std::max(static_cast(std::max(sizeSamples * (std::max(m_speed[0], m_speed[1]) * grainSpeed - 1.), 0.) + SafetyLatency), minLatency); + for (int i = 0; i < 2; ++i) + { + readPoint[i] = m_writePoint - latency - sprayResult[i]; + if (readPoint[i] < 0) { readPoint[i] += m_ringBufLength; } + } + const double phaseInc = 1. / sizeSamples; + m_grains.push_back(Grain(grainSpeed * m_speed[0], grainSpeed * m_speed[1], phaseInc, phaseInc, readPoint[0], readPoint[1])); + ++m_grainCount; + } + + for (int i = 0; i < m_grainCount; ++i) + { + m_grains[i].phase += std::max(m_grains[i].phaseSpeed[0], m_grains[i].phaseSpeed[1]); + if (m_grains[i].phase >= 1) + { + // grain is done, delete it + std::swap(m_grains[i], m_grains[m_grainCount-1]); + m_grains.pop_back(); + --i; + --m_grainCount; + continue; + } + + m_grains[i].readPoint[0] += m_grains[i].grainSpeed[0]; + m_grains[i].readPoint[1] += m_grains[i].grainSpeed[1]; + if (m_grains[i].readPoint[0] >= m_ringBufLength) { m_grains[i].readPoint[0] -= m_ringBufLength; } + if (m_grains[i].readPoint[1] >= m_ringBufLength) { m_grains[i].readPoint[1] -= m_ringBufLength; } + + const float fadePos = std::clamp((-std::abs(-2.f * static_cast(m_grains[i].phase) + 1.f) + 0.5f) * fadeLength + 0.5f, 0.f, 1.f); + const float windowVal = cosHalfWindowApprox(fadePos, shapeK); + s[0] += getHermiteSample(m_grains[i].readPoint[0], 0) * windowVal; + s[1] += getHermiteSample(m_grains[i].readPoint[1], 1) * windowVal; + } + + // note that adding two signals together, when uncorrelated, results in a signal power multiplication of sqrt(2), not 2 + s[0] *= densityInvRoot; + s[1] *= densityInvRoot; + + // 1-pole highpass for DC offset removal, to make feedback safer + s[0] -= (m_dcVal[0] = (1.f - m_dcCoeff) * s[0] + m_dcCoeff * m_dcVal[0]); + s[1] -= (m_dcVal[1] = (1.f - m_dcCoeff) * s[1] + m_dcCoeff * m_dcVal[1]); + + // cheap safety saturator to protect against infinite feedback + if (feedback > 0) + { + s[0] = safetySaturate(s[0]); + s[1] = safetySaturate(s[1]); + } + + if (++m_writePoint >= m_ringBufLength) + { + m_writePoint = 0; + } + if (prefilter) + { + filtered[0] = m_prefilter[0].process(filtered[0]); + filtered[1] = m_prefilter[1].process(filtered[1]); + } + + m_ringBuf[m_writePoint][0] = filtered[0] + s[0] * feedback; + m_ringBuf[m_writePoint][1] = filtered[1] + s[1] * feedback; + + buf[f][0] = d * buf[f][0] + w * s[0]; + buf[f][1] = d * buf[f][1] + w * s[1]; + } + + if (m_sampleRateNeedsUpdate) + { + m_sampleRateNeedsUpdate = false; + changeSampleRate(); + } + + return isRunning(); +} + +void GranularPitchShifterEffect::changeSampleRate() +{ + const int range = m_granularpitchshifterControls.m_rangeModel.value(); + const float ringBufLength = RangeSeconds[range]; + + m_sampleRate = Engine::audioEngine()->outputSampleRate(); + m_nyquist = m_sampleRate / 2; + + m_ringBufLength = m_sampleRate * ringBufLength; + m_ringBuf.resize(m_ringBufLength); + for (size_t i = 0; i < static_cast(m_ringBufLength); ++i) + { + m_ringBuf[i][0] = 0; + m_ringBuf[i][1] = 0; + } + m_writePoint = 0; + + m_oldGlide = -1; + + m_updatePitches = true; + + m_grains.clear(); + m_grainCount = 0; + m_grains.reserve(8);// arbitrary + + m_dcCoeff = std::exp(-2.0 * F_PI * DcRemovalHz / m_sampleRate); + + const double pitch = m_granularpitchshifterControls.m_pitchModel.value() * (1. / 12.); + const double pitchSpread = m_granularpitchshifterControls.m_pitchSpreadModel.value() * (1. / 24.); + m_truePitch[0] = pitch - pitchSpread; + m_truePitch[1] = pitch + pitchSpread; + m_speed[0] = std::exp2(m_truePitch[0]); + m_speed[1] = std::exp2(m_truePitch[1]); +} + + +extern "C" +{ +// necessary for getting instance out of shared lib +PLUGIN_EXPORT Plugin* lmms_plugin_main(Model* parent, void* data) +{ + return new GranularPitchShifterEffect(parent, static_cast(data)); +} +} + +} // namespace lmms diff --git a/plugins/GranularPitchShifter/GranularPitchShifterEffect.h b/plugins/GranularPitchShifter/GranularPitchShifterEffect.h new file mode 100755 index 00000000000..0f94168b7b6 --- /dev/null +++ b/plugins/GranularPitchShifter/GranularPitchShifterEffect.h @@ -0,0 +1,185 @@ +/* + * GranularPitchShifter.h + * + * Copyright (c) 2024 Lost Robot + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef LMMS_GRANULAR_PITCH_SHIFTER_EFFECT_H +#define LMMS_GRANULAR_PITCH_SHIFTER_EFFECT_H + +#include "Effect.h" +#include "GranularPitchShifterControls.h" + +#include "BasicFilters.h" +#include "interpolation.h" + +namespace lmms +{ + +constexpr float PrefilterBandwidth = 0.96f;// 96% of nyquist +constexpr double GlideSnagRadius = 0.001; +constexpr int SafetyLatency = 3; +constexpr float RangeSeconds[5] = {5, 10, 40, 40, 120}; +constexpr float DcRemovalHz = 7.f; +constexpr float SatuSafeVol = 16.f; +constexpr float SatuStrength = 0.001f; + + +class GranularPitchShifterEffect : public Effect +{ +public: + GranularPitchShifterEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key); + ~GranularPitchShifterEffect() override = default; + bool processAudioBuffer(SampleFrame* buf, const fpp_t frames) override; + + EffectControls* controls() override + { + return &m_granularpitchshifterControls; + } + + // double index and fraction are required for good quality + float getHermiteSample(double index, int ch) + { + const auto index_floor = static_cast(index); + const double fraction = index - index_floor; + + float v0, v1, v2, v3; + + if (index_floor == 0) { v0 = m_ringBuf[m_ringBuf.size() - 1][ch]; } + else { v0 = m_ringBuf[index_floor - 1][ch]; } + + v1 = m_ringBuf[index_floor][ch]; + + if(index_floor >= m_ringBuf.size() - 2) + { + v2 = m_ringBuf[(index_floor + 1) % m_ringBuf.size()][ch]; + v3 = m_ringBuf[(index_floor + 2) % m_ringBuf.size()][ch]; + } + else + { + v2 = m_ringBuf[index_floor + 1][ch]; + v3 = m_ringBuf[index_floor + 2][ch]; + } + + return hermiteInterpolate(v0, v1, v2, v3, static_cast(fraction)); + } + + // adapted from signalsmith's crossfade approximation: + // https://signalsmith-audio.co.uk/writing/2021/cheap-energy-crossfade + float cosHalfWindowApprox(float x, float k) + { + float A = x * (1 - x); + float B = A * (1 + k * A); + float C = (B + x); + return C * C; + } + // 1-2 fades between equal-gain and equal-power + float cosWindowApproxK(float p) + { + return -6.0026608f + p * (6.8773512f - 1.5838104f * p); + } + + // designed to use minimal CPU if the input isn't loud + float safetySaturate(float input) + { + float absInput = std::abs(input); + return absInput <= SatuSafeVol ? input : + std::copysign((absInput - SatuSafeVol) / (1 + (absInput - SatuSafeVol) * SatuStrength) + SatuSafeVol, input); + } + + void sampleRateNeedsUpdate() { m_sampleRateNeedsUpdate = true; } + + void changeSampleRate(); + +private: + struct PrefilterLowpass + { + float m_v0z = 0.f, m_v1 = 0.f, m_v2 = 0.f; + float m_g1, m_g2, m_g3, m_g4; + + void setCoefs(float sampleRate, float cutoff) + { + const float g = std::tan(F_PI * cutoff / sampleRate); + const float ginv = g / (1.f + g * (g + F_SQRT_2)); + m_g1 = ginv; + m_g2 = 2.f * (g + F_SQRT_2) * ginv; + m_g3 = g * ginv; + m_g4 = 2.f * ginv; + } + + float process(float input) + { + const float v1z = m_v1; + const float v3 = input + m_v0z - 2.f * m_v2; + m_v1 += m_g1 * v3 - m_g2 * v1z; + m_v2 += m_g3 * v3 + m_g4 * v1z; + m_v0z = input; + return m_v2; + } + }; + + struct Grain + { + Grain(double grainSpeedL, double grainSpeedR, double phaseSpeedL, double phaseSpeedR, double readPointL, double readPointR) : + readPoint{readPointL, readPointR}, + phaseSpeed{phaseSpeedL, phaseSpeedR}, + grainSpeed{grainSpeedL, grainSpeedR}, + phase{0} + {} + std::array readPoint; + std::array phaseSpeed; + std::array grainSpeed; + double phase; + }; + + GranularPitchShifterControls m_granularpitchshifterControls; + + std::vector> m_ringBuf; + std::vector m_grains; + + std::array m_prefilter; + std::array m_speed = {1, 1}; + std::array m_truePitch = {0, 0}; + std::array m_dcVal = {0, 0}; + + float m_sampleRate; + float m_nyquist; + float m_nextWaitRandomization = 1; + float m_dcCoeff; + + int m_ringBufLength = 0; + int m_writePoint = 0; + int m_grainCount = 0; + int m_timeSinceLastGrain = 999999999; + + double m_oldGlide = -1; + double m_glideCoef = 0; + + bool m_sampleRateNeedsUpdate = false; + bool m_updatePitches = true; + + friend class GranularPitchShifterControls; +}; + + +} // namespace lmms + +#endif // LMMS_GRANULAR_PITCH_SHIFTER_EFFECT_H diff --git a/plugins/GranularPitchShifter/artwork.png b/plugins/GranularPitchShifter/artwork.png new file mode 100755 index 00000000000..632750bcf5e Binary files /dev/null and b/plugins/GranularPitchShifter/artwork.png differ diff --git a/plugins/GranularPitchShifter/help_active.png b/plugins/GranularPitchShifter/help_active.png new file mode 100755 index 00000000000..d68707acb46 Binary files /dev/null and b/plugins/GranularPitchShifter/help_active.png differ diff --git a/plugins/GranularPitchShifter/help_inactive.png b/plugins/GranularPitchShifter/help_inactive.png new file mode 100644 index 00000000000..0113876bc59 Binary files /dev/null and b/plugins/GranularPitchShifter/help_inactive.png differ diff --git a/plugins/GranularPitchShifter/logo.png b/plugins/GranularPitchShifter/logo.png new file mode 100755 index 00000000000..9340da708dd Binary files /dev/null and b/plugins/GranularPitchShifter/logo.png differ diff --git a/plugins/GranularPitchShifter/prefilter_active.png b/plugins/GranularPitchShifter/prefilter_active.png new file mode 100755 index 00000000000..95d3ccd7099 Binary files /dev/null and b/plugins/GranularPitchShifter/prefilter_active.png differ diff --git a/plugins/GranularPitchShifter/prefilter_inactive.png b/plugins/GranularPitchShifter/prefilter_inactive.png new file mode 100755 index 00000000000..8efb1ff1d4a Binary files /dev/null and b/plugins/GranularPitchShifter/prefilter_inactive.png differ diff --git a/plugins/Kicker/Kicker.cpp b/plugins/Kicker/Kicker.cpp index e6418e2da5b..a71941fc6e8 100644 --- a/plugins/Kicker/Kicker.cpp +++ b/plugins/Kicker/Kicker.cpp @@ -64,7 +64,7 @@ Plugin::Descriptor PLUGIN_EXPORT kicker_plugin_descriptor = KickerInstrument::KickerInstrument( InstrumentTrack * _instrument_track ) : - Instrument( _instrument_track, &kicker_plugin_descriptor ), + Instrument(_instrument_track, &kicker_plugin_descriptor, nullptr, Flag::IsNotBendable), m_startFreqModel( 150.0f, 5.0f, 1000.0f, 1.0f, this, tr( "Start frequency" ) ), m_endFreqModel( 40.0f, 5.0f, 1000.0f, 1.0f, this, tr( "End frequency" ) ), m_decayModel( 440.0f, 5.0f, 5000.0f, 1.0f, 5000.0f, this, tr( "Length" ) ), @@ -156,11 +156,11 @@ using DistFX = DspEffectLibrary::Distortion; using SweepOsc = KickerOsc>; void KickerInstrument::playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) + SampleFrame* _working_buffer ) { const fpp_t frames = _n->framesLeftForCurrentPeriod(); const f_cnt_t offset = _n->noteOffset(); - const float decfr = m_decayModel.value() * Engine::audioEngine()->processingSampleRate() / 1000.0f; + const float decfr = m_decayModel.value() * Engine::audioEngine()->outputSampleRate() / 1000.0f; const f_cnt_t tfp = _n->totalFramesPlayed(); if (!_n->m_pluginData) @@ -184,17 +184,26 @@ void KickerInstrument::playNote( NotePlayHandle * _n, } auto so = static_cast(_n->m_pluginData); - so->update( _working_buffer + offset, frames, Engine::audioEngine()->processingSampleRate() ); + so->update( _working_buffer + offset, frames, Engine::audioEngine()->outputSampleRate() ); if( _n->isReleased() ) { - const float done = _n->releaseFramesDone(); + // We need this to check if the release has ended const float desired = desiredReleaseFrames(); - for( fpp_t f = 0; f < frames; ++f ) + + // This can be considered the current release frame in the "global" context of the release. + // We need it with the desired number of release frames to compute the linear decay. + fpp_t currentReleaseFrame = _n->releaseFramesDone(); + + // Start applying the release at the correct frame + const float framesBeforeRelease = _n->framesBeforeRelease(); + for (fpp_t f = framesBeforeRelease; f < frames; ++f, ++currentReleaseFrame) { - const float fac = ( done+f < desired ) ? ( 1.0f - ( ( done+f ) / desired ) ) : 0; - _working_buffer[f+offset][0] *= fac; - _working_buffer[f+offset][1] *= fac; + const bool releaseStillActive = currentReleaseFrame < desired; + const float attenuation = releaseStillActive ? (1.0f - (currentReleaseFrame / desired)) : 0.f; + + _working_buffer[f + offset][0] *= attenuation; + _working_buffer[f + offset][1] *= attenuation; } } } diff --git a/plugins/Kicker/Kicker.h b/plugins/Kicker/Kicker.h index b5d065598c5..2eadaa333a5 100644 --- a/plugins/Kicker/Kicker.h +++ b/plugins/Kicker/Kicker.h @@ -56,7 +56,7 @@ class KickerInstrument : public Instrument ~KickerInstrument() override = default; void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; + SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; void saveSettings(QDomDocument& doc, QDomElement& elem) override; @@ -64,14 +64,9 @@ class KickerInstrument : public Instrument QString nodeName() const override; - Flags flags() const override + float desiredReleaseTimeMs() const override { - return Flag::IsNotBendable; - } - - f_cnt_t desiredReleaseFrames() const override - { - return( 512 ); + return 12.f; } gui::PluginView* instantiateView( QWidget * _parent ) override; diff --git a/plugins/Kicker/KickerOsc.h b/plugins/Kicker/KickerOsc.h index 69436c5fc9d..420373512af 100644 --- a/plugins/Kicker/KickerOsc.h +++ b/plugins/Kicker/KickerOsc.h @@ -60,7 +60,7 @@ class KickerOsc virtual ~KickerOsc() = default; - void update( sampleFrame* buf, const fpp_t frames, const float sampleRate ) + void update( SampleFrame* buf, const fpp_t frames, const float sampleRate ) { for( fpp_t frame = 0; frame < frames; ++frame ) { diff --git a/plugins/LOMM/LOMM.cpp b/plugins/LOMM/LOMM.cpp index a0bd556efeb..7c4574cd1b8 100644 --- a/plugins/LOMM/LOMM.cpp +++ b/plugins/LOMM/LOMM.cpp @@ -50,33 +50,29 @@ extern "C" LOMMEffect::LOMMEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key) : Effect(&lomm_plugin_descriptor, parent, key), m_lommControls(this), - m_sampleRate(Engine::audioEngine()->processingSampleRate()), + m_sampleRate(Engine::audioEngine()->outputSampleRate()), m_lp1(m_sampleRate), m_lp2(m_sampleRate), m_hp1(m_sampleRate), m_hp2(m_sampleRate), m_ap(m_sampleRate), m_needsUpdate(true), - m_coeffPrecalc(-0.05), - m_crestTimeConst(0.999), + m_coeffPrecalc(-0.05f), + m_crestTimeConst(0.999f), m_lookWrite(0), m_lookBufLength(2) { autoQuitModel()->setValue(autoQuitModel()->maxValue()); - m_yL[0][0] = m_yL[0][1] = LOMM_MIN_FLOOR; - m_yL[1][0] = m_yL[1][1] = LOMM_MIN_FLOOR; - m_yL[2][0] = m_yL[2][1] = LOMM_MIN_FLOOR; - m_ap.setFilterType(BasicFilters<2>::FilterType::AllPass); connect(Engine::audioEngine(), SIGNAL(sampleRateChanged()), this, SLOT(changeSampleRate())); - emit changeSampleRate(); + changeSampleRate(); } void LOMMEffect::changeSampleRate() { - m_sampleRate = Engine::audioEngine()->processingSampleRate(); + m_sampleRate = Engine::audioEngine()->outputSampleRate(); m_lp1.setSampleRate(m_sampleRate); m_lp2.setSampleRate(m_sampleRate); m_hp1.setSampleRate(m_sampleRate); @@ -97,10 +93,15 @@ void LOMMEffect::changeSampleRate() m_scLookBuf[j][i].resize(m_lookBufLength, LOMM_MIN_FLOOR); } } + + std::fill(m_yL.begin(), m_yL.end(), std::array{LOMM_MIN_FLOOR, LOMM_MIN_FLOOR}); + m_rms = m_gainResult = m_displayIn = m_displayOut = m_prevOut = m_yL; + m_crestPeakVal[0] = m_crestPeakVal[1] = LOMM_MIN_FLOOR; + m_crestRmsVal = m_crestFactorVal = m_crestPeakVal; } -bool LOMMEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames) +bool LOMMEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames) { if (!isEnabled() || !isRunning()) { @@ -111,7 +112,7 @@ bool LOMMEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames) { m_lp1.setLowpass(m_lommControls.m_split1Model.value()); m_hp1.setHighpass(m_lommControls.m_split1Model.value()); - m_ap.calcFilterCoeffs(m_lommControls.m_split1Model.value(), 0.70710678118); + m_ap.calcFilterCoeffs(m_lommControls.m_split1Model.value(), 0.70710678118f); } if (m_needsUpdate || m_lommControls.m_split2Model.isValueChanged()) { diff --git a/plugins/LOMM/LOMM.h b/plugins/LOMM/LOMM.h index 196d0a09d14..783233c5f47 100644 --- a/plugins/LOMM/LOMM.h +++ b/plugins/LOMM/LOMM.h @@ -35,7 +35,7 @@ namespace lmms { -constexpr inline float LOMM_MIN_FLOOR = 0.00012589;// -72 dBFS +constexpr inline float LOMM_MIN_FLOOR = 0.00012589f;// -72 dBFS constexpr inline float LOMM_MAX_LOOKAHEAD = 20.f; constexpr inline float LOMM_AUTO_TIME_ADJUST = 5.f; @@ -45,7 +45,7 @@ class LOMMEffect : public Effect public: LOMMEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key); ~LOMMEffect() override = default; - bool processAudioBuffer(sampleFrame* buf, const fpp_t frames) override; + bool processAudioBuffer(SampleFrame* buf, const fpp_t frames) override; EffectControls* controls() override { diff --git a/plugins/LOMM/LOMMControlDialog.h b/plugins/LOMM/LOMMControlDialog.h index bf7e67c4ca8..3de38c984a2 100644 --- a/plugins/LOMM/LOMMControlDialog.h +++ b/plugins/LOMM/LOMMControlDialog.h @@ -86,7 +86,8 @@ class LOMMControlDialog : public EffectControlDialog return spinBox; } - PixmapButton* createPixmapButton(const QString& text, QWidget* parent, int x, int y, BoolModel* model, const QString& activeIcon, const QString& inactiveIcon, const QString& tooltip) + PixmapButton* createPixmapButton(const QString& text, QWidget* parent, int x, int y, BoolModel* model, + std::string_view activeIcon, std::string_view inactiveIcon, const QString& tooltip) { PixmapButton* button = new PixmapButton(parent, text); button->move(x, y); diff --git a/plugins/LOMM/LOMMControls.cpp b/plugins/LOMM/LOMMControls.cpp index d695cf483fd..3ede0ddf279 100644 --- a/plugins/LOMM/LOMMControls.cpp +++ b/plugins/LOMM/LOMMControls.cpp @@ -35,55 +35,55 @@ namespace lmms LOMMControls::LOMMControls(LOMMEffect* effect) : EffectControls(effect), m_effect(effect), - m_depthModel(0.4, 0, 1, 0.00001, this, tr("Depth")), - m_timeModel(1, 0, 10, 0.00001, this, tr("Time")), - m_inVolModel(0, -48, 48, 0.00001, this, tr("Input Volume")), - m_outVolModel(8, -48, 48, 0.00001, this, tr("Output Volume")), - m_upwardModel(1, 0, 2, 0.00001, this, tr("Upward Depth")), - m_downwardModel(1, 0, 2, 0.00001, this, tr("Downward Depth")), - m_split1Model(2500, 20, 20000, 0.01, this, tr("High/Mid Split")), - m_split2Model(88.3, 20, 20000, 0.01, this, tr("Mid/Low Split")), + m_depthModel(0.4f, 0, 1, 0.00001f, this, tr("Depth")), + m_timeModel(1, 0, 10, 0.00001f, this, tr("Time")), + m_inVolModel(0, -48, 48, 0.00001f, this, tr("Input Volume")), + m_outVolModel(8, -48, 48, 0.00001f, this, tr("Output Volume")), + m_upwardModel(1, 0, 2, 0.00001f, this, tr("Upward Depth")), + m_downwardModel(1, 0, 2, 0.00001f, this, tr("Downward Depth")), + m_split1Model(2500, 20, 20000, 0.01f, this, tr("High/Mid Split")), + m_split2Model(88.3f, 20, 20000, 0.01f, this, tr("Mid/Low Split")), m_split1EnabledModel(true, this, tr("Enable High/Mid Split")), m_split2EnabledModel(true, this, tr("Enable Mid/Low Split")), m_band1EnabledModel(true, this, tr("Enable High Band")), m_band2EnabledModel(true, this, tr("Enable Mid Band")), m_band3EnabledModel(true, this, tr("Enable Low Band")), - m_inHighModel(0, -48, 48, 0.00001, this, tr("High Input Volume")), - m_inMidModel(0, -48, 48, 0.00001, this, tr("Mid Input Volume")), - m_inLowModel(0, -48, 48, 0.00001, this, tr("Low Input Volume")), - m_outHighModel(4.6, -48, 48, 0.00001, this, tr("High Output Volume")), - m_outMidModel(0.0, -48, 48, 0.00001, this, tr("Mid Output Volume")), - m_outLowModel(4.6, -48, 48, 0.00001, this, tr("Low Output Volume")), - m_aThreshHModel(-30.3, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001, this, tr("Above Threshold High")), - m_aThreshMModel(-25.0, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001, this, tr("Above Threshold Mid")), - m_aThreshLModel(-28.6, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001, this, tr("Above Threshold Low")), - m_aRatioHModel(99.99, 1, 99.99, 0.01, this, tr("Above Ratio High")), - m_aRatioMModel(66.7, 1, 99.99, 0.01, this, tr("Above Ratio Mid")), - m_aRatioLModel(66.7, 1, 99.99, 0.01, this, tr("Above Ratio Low")), - m_bThreshHModel(-35.6, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001, this, tr("Below Threshold High")), - m_bThreshMModel(-36.6, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001, this, tr("Below Threshold Mid")), - m_bThreshLModel(-35.6, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001, this, tr("Below Threshold Low")), - m_bRatioHModel(4.17, 1, 99.99, 0.01, this, tr("Below Ratio High")), - m_bRatioMModel(4.17, 1, 99.99, 0.01, this, tr("Below Ratio Mid")), - m_bRatioLModel(4.17, 1, 99.99, 0.01, this, tr("Below Ratio Low")), - m_atkHModel(13.5, 0, 1000, 0.001, this, tr("Attack High")), - m_atkMModel(22.4, 0, 1000, 0.001, this, tr("Attack Mid")), - m_atkLModel(47.8, 0, 1000, 0.001, this, tr("Attack Low")), - m_relHModel(132, 0, 1000, 0.001, this, tr("Release High")), - m_relMModel(282, 0, 1000, 0.001, this, tr("Release Mid")), - m_relLModel(282, 0, 1000, 0.001, this, tr("Release Low")), - m_rmsTimeModel(10, 0, 500, 0.001, this, tr("RMS Time")), - m_kneeModel(6, 0, 36, 0.00001, this, tr("Knee")), - m_rangeModel(36, 0, 96, 0.00001, this, tr("Range")), - m_balanceModel(0, -18, 18, 0.00001, this, tr("Balance")), + m_inHighModel(0, -48, 48, 0.00001f, this, tr("High Input Volume")), + m_inMidModel(0, -48, 48, 0.00001f, this, tr("Mid Input Volume")), + m_inLowModel(0, -48, 48, 0.00001f, this, tr("Low Input Volume")), + m_outHighModel(4.6f, -48, 48, 0.00001f, this, tr("High Output Volume")), + m_outMidModel(0.f, -48, 48, 0.00001f, this, tr("Mid Output Volume")), + m_outLowModel(4.6f, -48, 48, 0.00001f, this, tr("Low Output Volume")), + m_aThreshHModel(-30.3f, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001f, this, tr("Above Threshold High")), + m_aThreshMModel(-25.f, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001f, this, tr("Above Threshold Mid")), + m_aThreshLModel(-28.6f, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001f, this, tr("Above Threshold Low")), + m_aRatioHModel(99.99f, 1, 99.99f, 0.01f, this, tr("Above Ratio High")), + m_aRatioMModel(66.7f, 1, 99.99f, 0.01f, this, tr("Above Ratio Mid")), + m_aRatioLModel(66.7f, 1, 99.99f, 0.01f, this, tr("Above Ratio Low")), + m_bThreshHModel(-35.6f, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001f, this, tr("Below Threshold High")), + m_bThreshMModel(-36.6f, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001f, this, tr("Below Threshold Mid")), + m_bThreshLModel(-35.6f, LOMM_DISPLAY_MIN, LOMM_DISPLAY_MAX, 0.001f, this, tr("Below Threshold Low")), + m_bRatioHModel(4.17f, 1, 99.99f, 0.01f, this, tr("Below Ratio High")), + m_bRatioMModel(4.17f, 1, 99.99f, 0.01f, this, tr("Below Ratio Mid")), + m_bRatioLModel(4.17f, 1, 99.99f, 0.01f, this, tr("Below Ratio Low")), + m_atkHModel(13.5f, 0, 1000, 0.001f, this, tr("Attack High")), + m_atkMModel(22.4f, 0, 1000, 0.001f, this, tr("Attack Mid")), + m_atkLModel(47.8f, 0, 1000, 0.001f, this, tr("Attack Low")), + m_relHModel(132, 0, 1000, 0.001f, this, tr("Release High")), + m_relMModel(282, 0, 1000, 0.001f, this, tr("Release Mid")), + m_relLModel(282, 0, 1000, 0.001f, this, tr("Release Low")), + m_rmsTimeModel(10, 0, 500, 0.001f, this, tr("RMS Time")), + m_kneeModel(6, 0, 36, 0.00001f, this, tr("Knee")), + m_rangeModel(36, 0, 96, 0.00001f, this, tr("Range")), + m_balanceModel(0, -18, 18, 0.00001f, this, tr("Balance")), m_depthScalingModel(true, this, tr("Scale output volume with Depth")), m_stereoLinkModel(false, this, tr("Stereo Link")), - m_autoTimeModel(0, 0, 1, 0.00001, this, tr("Auto Time")), - m_mixModel(1, 0, 1, 0.00001, this, tr("Mix")), + m_autoTimeModel(0, 0, 1, 0.00001f, this, tr("Auto Time")), + m_mixModel(1, 0, 1, 0.00001f, this, tr("Mix")), m_feedbackModel(false, this, tr("Feedback")), m_midsideModel(false, this, tr("Mid/Side")), m_lookaheadEnableModel(false, this, tr("Lookahead")), - m_lookaheadModel(0.f, 0.f, LOMM_MAX_LOOKAHEAD, 0.01, this, tr("Lookahead Length")), + m_lookaheadModel(0.f, 0.f, LOMM_MAX_LOOKAHEAD, 0.01f, this, tr("Lookahead Length")), m_lowSideUpwardSuppressModel(false, this, tr("Suppress upward compression for side band")) { auto models = {&m_timeModel, &m_inVolModel, &m_outVolModel, &m_inHighModel, &m_inMidModel, diff --git a/plugins/LadspaBrowser/LadspaBrowser.cpp b/plugins/LadspaBrowser/LadspaBrowser.cpp index 31be6405649..e6a31e15a3c 100644 --- a/plugins/LadspaBrowser/LadspaBrowser.cpp +++ b/plugins/LadspaBrowser/LadspaBrowser.cpp @@ -32,7 +32,6 @@ #include -#include "gui_templates.h" #include "LadspaDescription.h" #include "LadspaPortDialog.h" #include "TabBar.h" @@ -172,7 +171,6 @@ QWidget * LadspaBrowserView::createTab( QWidget * _parent, const QString & _txt, auto title = new QLabel(type + _txt, tab); QFont f = title->font(); f.setBold( true ); - title->setFont( pointSize<12>( f ) ); layout->addSpacing( 5 ); layout->addWidget( title ); diff --git a/plugins/LadspaBrowser/LadspaPortDialog.cpp b/plugins/LadspaBrowser/LadspaPortDialog.cpp index e256795114c..bf4d0038a4f 100644 --- a/plugins/LadspaBrowser/LadspaPortDialog.cpp +++ b/plugins/LadspaBrowser/LadspaPortDialog.cpp @@ -90,11 +90,11 @@ LadspaPortDialog::LadspaPortDialog( const ladspa_key_t & _key ) { if( min != NOHINT ) { - min *= Engine::audioEngine()->processingSampleRate(); + min *= Engine::audioEngine()->outputSampleRate(); } if( max != NOHINT ) { - max *= Engine::audioEngine()->processingSampleRate(); + max *= Engine::audioEngine()->outputSampleRate(); } } diff --git a/plugins/LadspaEffect/LadspaControlDialog.cpp b/plugins/LadspaEffect/LadspaControlDialog.cpp index 2a5437fb128..5189b0cde80 100644 --- a/plugins/LadspaEffect/LadspaControlDialog.cpp +++ b/plugins/LadspaEffect/LadspaControlDialog.cpp @@ -88,17 +88,9 @@ void LadspaControlDialog::updateEffectView( LadspaControls * _ctl ) int col = 0; BufferDataType last_port = BufferDataType::None; - QGroupBox * grouper; - if( _ctl->m_processors > 1 ) - { - grouper = new QGroupBox( tr( "Channel " ) + - QString::number( proc + 1 ), - this ); - } - else - { - grouper = new QGroupBox( this ); - } + auto grouper = _ctl->m_processors > 1 + ? new QGroupBox(tr("Channel ") + QString::number(proc + 1), this) + : new QGroupBox(this); auto gl = new QGridLayout(grouper); grouper->setLayout( gl ); diff --git a/plugins/LadspaEffect/LadspaEffect.cpp b/plugins/LadspaEffect/LadspaEffect.cpp index 837bd554c2b..75c79ad214b 100644 --- a/plugins/LadspaEffect/LadspaEffect.cpp +++ b/plugins/LadspaEffect/LadspaEffect.cpp @@ -129,7 +129,7 @@ void LadspaEffect::changeSampleRate() -bool LadspaEffect::processAudioBuffer( sampleFrame * _buf, +bool LadspaEffect::processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) { m_pluginMutex.lock(); @@ -139,17 +139,17 @@ bool LadspaEffect::processAudioBuffer( sampleFrame * _buf, return( false ); } - int frames = _frames; - sampleFrame * o_buf = nullptr; - QVarLengthArray sBuf(_frames * DEFAULT_CHANNELS); + auto frames = _frames; + SampleFrame* o_buf = nullptr; + QVarLengthArray sBuf(_frames); - if( m_maxSampleRate < Engine::audioEngine()->processingSampleRate() ) + if( m_maxSampleRate < Engine::audioEngine()->outputSampleRate() ) { o_buf = _buf; - _buf = reinterpret_cast(sBuf.data()); + _buf = sBuf.data(); sampleDown( o_buf, _buf, m_maxSampleRate ); frames = _frames * m_maxSampleRate / - Engine::audioEngine()->processingSampleRate(); + Engine::audioEngine()->outputSampleRate(); } // Copy the LMMS audio buffer to the LADSPA input buffer and initialize @@ -587,7 +587,7 @@ sample_rate_t LadspaEffect::maxSamplerate( const QString & _name ) { return( __buggy_plugins[_name] ); } - return( Engine::audioEngine()->processingSampleRate() ); + return( Engine::audioEngine()->outputSampleRate() ); } diff --git a/plugins/LadspaEffect/LadspaEffect.h b/plugins/LadspaEffect/LadspaEffect.h index 8ad4f141128..d5b93d4e24b 100644 --- a/plugins/LadspaEffect/LadspaEffect.h +++ b/plugins/LadspaEffect/LadspaEffect.h @@ -47,7 +47,7 @@ class LadspaEffect : public Effect const Descriptor::SubPluginFeatures::Key * _key ); ~LadspaEffect() override; - bool processAudioBuffer( sampleFrame * _buf, + bool processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) override; void setControl( int _control, LADSPA_Data _data ); diff --git a/plugins/LadspaEffect/calf/CMakeLists.txt b/plugins/LadspaEffect/calf/CMakeLists.txt index 0c9cd8fa96e..23f93da7ae0 100644 --- a/plugins/LadspaEffect/calf/CMakeLists.txt +++ b/plugins/LadspaEffect/calf/CMakeLists.txt @@ -1,6 +1,8 @@ # Note: # The last version of Calf that was LADSPA-capable is version 0.0.18.2 +set_directory_properties(PROPERTIES SYSTEM TRUE) + # Parse version info from autoconf FILE(READ veal/configure.ac VERSION_FILE) STRING(REPLACE "[" ";" VERSION_FILE ${VERSION_FILE} ) @@ -38,7 +40,14 @@ SET(INLINE_FLAGS "") IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") SET(INLINE_FLAGS -finline-functions-called-once -finline-limit=80) ENDIF() -target_compile_options(veal PRIVATE -fexceptions -O2 -finline-functions ${INLINE_FLAGS}) + +if(NOT MSVC) + target_compile_options(veal PRIVATE -fexceptions -O2 -finline-functions ${INLINE_FLAGS}) +endif() + +if(MSVC) + target_link_options(veal PRIVATE "/EXPORT:ladspa_descriptor") +endif() if(LMMS_BUILD_WIN32) add_custom_command( diff --git a/plugins/LadspaEffect/calf/veal b/plugins/LadspaEffect/calf/veal index fe628885b76..0162621fa75 160000 --- a/plugins/LadspaEffect/calf/veal +++ b/plugins/LadspaEffect/calf/veal @@ -1 +1 @@ -Subproject commit fe628885b761372b37136a3f2b7c3d56e179e3ba +Subproject commit 0162621fa75cf90c319c704d646c734e1ed21e14 diff --git a/plugins/LadspaEffect/caps/CMakeLists.txt b/plugins/LadspaEffect/caps/CMakeLists.txt index bdcf3a96af4..379413ae49a 100644 --- a/plugins/LadspaEffect/caps/CMakeLists.txt +++ b/plugins/LadspaEffect/caps/CMakeLists.txt @@ -1,3 +1,5 @@ +set_directory_properties(PROPERTIES SYSTEM TRUE) + INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include") FILE(GLOB SOURCES *.cc) LIST(SORT SOURCES) @@ -7,7 +9,14 @@ IF(LMMS_BUILD_WIN64) ADD_DEFINITIONS(-DLMMS_BUILD_WIN64) ENDIF(LMMS_BUILD_WIN64) SET_TARGET_PROPERTIES(caps PROPERTIES PREFIX "") -SET_TARGET_PROPERTIES(caps PROPERTIES COMPILE_FLAGS "-O2 -funroll-loops -Wno-write-strings") + +if(NOT MSVC) + target_compile_options(caps PRIVATE -O2 -funroll-loops) +endif() + +if(MSVC) + target_link_options(caps PRIVATE "/EXPORT:ladspa_descriptor") +endif() IF(LMMS_BUILD_WIN32) add_custom_command( @@ -18,11 +27,3 @@ IF(LMMS_BUILD_WIN32) COMMAND_EXPAND_LISTS ) ENDIF(LMMS_BUILD_WIN32) -IF(NOT LMMS_BUILD_APPLE AND NOT LMMS_BUILD_OPENBSD) - SET_TARGET_PROPERTIES(caps PROPERTIES LINK_FLAGS "${LINK_FLAGS} -shared -Wl,-no-undefined") -ENDIF(NOT LMMS_BUILD_APPLE AND NOT LMMS_BUILD_OPENBSD) - -IF(LMMS_BUILD_LINUX) - SET_TARGET_PROPERTIES(caps PROPERTIES LINK_FLAGS "${LINK_FLAGS}") -ENDIF(LMMS_BUILD_LINUX) - diff --git a/plugins/LadspaEffect/caps/basics.h b/plugins/LadspaEffect/caps/basics.h index df24e8c05ed..62eb778873b 100644 --- a/plugins/LadspaEffect/caps/basics.h +++ b/plugins/LadspaEffect/caps/basics.h @@ -41,6 +41,9 @@ #include #include +#ifndef _USE_MATH_DEFINES +#define _USE_MATH_DEFINES +#endif #include #include @@ -76,7 +79,7 @@ #define MIN_GAIN .000001 /* -120 dB */ -/* smallest non-denormal 32 bit IEEE float is 1.1810-38 */ +/* smallest non-denormal 32 bit IEEE float is 1.18×10^-38 */ #define NOISE_FLOOR .00000000000005 /* -266 dB */ typedef int8_t int8; diff --git a/plugins/LadspaEffect/caps/dsp/Eq.h b/plugins/LadspaEffect/caps/dsp/Eq.h index 92639e8a185..89c86dd18ff 100644 --- a/plugins/LadspaEffect/caps/dsp/Eq.h +++ b/plugins/LadspaEffect/caps/dsp/Eq.h @@ -62,11 +62,11 @@ class Eq { public: /* recursion coefficients, 3 per band */ - eq_sample __attribute__ ((aligned)) a[Bands], b[Bands], c[Bands]; + eq_sample a[Bands], b[Bands], c[Bands]; /* past outputs, 2 per band */ - eq_sample __attribute__ ((aligned)) y[2][Bands]; + eq_sample y[2][Bands]; /* current gain and recursion factor, each 1 per band = 2 */ - eq_sample __attribute__ ((aligned)) gain[Bands], gf[Bands]; + eq_sample gain[Bands], gf[Bands]; /* input history */ eq_sample x[2]; /* history index */ diff --git a/plugins/LadspaEffect/caps/interface.cc b/plugins/LadspaEffect/caps/interface.cc index 96e3d9806f6..4c7ca46b51c 100644 --- a/plugins/LadspaEffect/caps/interface.cc +++ b/plugins/LadspaEffect/caps/interface.cc @@ -29,7 +29,7 @@ (2541 - 2580 donated to artemio@kdemail.net) */ -#include +// #include #include "basics.h" @@ -69,7 +69,6 @@ seed() extern "C" { -__attribute__ ((constructor)) void caps_so_init() { DescriptorStub ** d = descriptors; @@ -125,7 +124,6 @@ void caps_so_init() //seed(); } -__attribute__ ((destructor)) void caps_so_fini() { for (ulong i = 0; i < N; ++i) @@ -142,4 +140,11 @@ ladspa_descriptor (unsigned long i) return 0; } +struct CapsSoInit +{ + CapsSoInit() { caps_so_init(); } + ~CapsSoInit() { caps_so_fini(); } +}; +static CapsSoInit capsSoInit; + }; /* extern "C" */ diff --git a/plugins/LadspaEffect/cmt/CMakeLists.txt b/plugins/LadspaEffect/cmt/CMakeLists.txt index 75dba319d6c..dbc3c9de630 100644 --- a/plugins/LadspaEffect/cmt/CMakeLists.txt +++ b/plugins/LadspaEffect/cmt/CMakeLists.txt @@ -1,3 +1,5 @@ +set_directory_properties(PROPERTIES SYSTEM TRUE) + INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include") FILE(GLOB_RECURSE SOURCES cmt/src/*.cpp) LIST(SORT SOURCES) @@ -5,7 +7,10 @@ ADD_LIBRARY(cmt MODULE ${SOURCES}) INSTALL(TARGETS cmt LIBRARY DESTINATION "${PLUGIN_DIR}/ladspa") SET_TARGET_PROPERTIES(cmt PROPERTIES PREFIX "") -target_compile_options(cmt PRIVATE -Wall -O3 -fno-strict-aliasing) + +if(NOT MSVC) + target_compile_options(cmt PRIVATE -O3 -fno-strict-aliasing) +endif() if(LMMS_BUILD_WIN32) add_custom_command( @@ -17,6 +22,10 @@ if(LMMS_BUILD_WIN32) ) endif() +if(MSVC) + target_link_options(cmt PRIVATE "/EXPORT:ladspa_descriptor") +endif() + if(NOT LMMS_BUILD_WIN32) target_compile_options(cmt PRIVATE -fPIC) endif() diff --git a/plugins/LadspaEffect/cmt/cmt b/plugins/LadspaEffect/cmt/cmt index 6e6e291fbad..24599fb45b9 160000 --- a/plugins/LadspaEffect/cmt/cmt +++ b/plugins/LadspaEffect/cmt/cmt @@ -1 +1 @@ -Subproject commit 6e6e291fbad1138c808860ba3f140a963b52fa58 +Subproject commit 24599fb45b99fff6302136f13adb3817e5833e7d diff --git a/plugins/LadspaEffect/swh/CMakeLists.txt b/plugins/LadspaEffect/swh/CMakeLists.txt index a8300117735..203c3168f77 100644 --- a/plugins/LadspaEffect/swh/CMakeLists.txt +++ b/plugins/LadspaEffect/swh/CMakeLists.txt @@ -1,3 +1,5 @@ +set_directory_properties(PROPERTIES SYSTEM TRUE) + # Create blank config.h FILE(WRITE ladspa/config.h "") @@ -5,13 +7,16 @@ FILE(WRITE ladspa/config.h "") IF(LMMS_BUILD_WIN32) SET(PIC_FLAGS "") ELSE() - SET(PIC_FLAGS "-fPIC -DPIC") + SET(PIC_FLAGS -fPIC -DPIC) ENDIF() # Additional compile flags -SET(COMPILE_FLAGS "${COMPILE_FLAGS} -O3 -Wall") -SET(COMPILE_FLAGS "${COMPILE_FLAGS} -fomit-frame-pointer -funroll-loops -ffast-math -c -fno-strict-aliasing") -SET(COMPILE_FLAGS "${COMPILE_FLAGS} ${PIC_FLAGS}") +if(NOT MSVC) + set(COMPILE_FLAGS ${COMPILE_FLAGS} -O3 -c + -fomit-frame-pointer -funroll-loops -ffast-math -fno-strict-aliasing + ${PIC_FLAGS} + ) +endif() # Loop over every XML file FILE(GLOB XML_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/ladspa/*.xml") @@ -34,6 +39,10 @@ FOREACH(_item ${XML_SOURCES}) # Add a library target for this C file, which depends on success of makestup.pl ADD_LIBRARY("${_plugin}" MODULE "${_out_file}") + if(MSVC) + target_link_options("${_plugin}" PRIVATE "/EXPORT:ladspa_descriptor") + endif() + # Vocoder does not use fftw IF(NOT ("${_plugin}" STREQUAL "vocoder_1337")) TARGET_LINK_LIBRARIES("${_plugin}" ${FFTW3F_LIBRARIES}) @@ -53,8 +62,7 @@ FOREACH(_item ${XML_SOURCES}) endif(LMMS_BUILD_WIN32) SET_TARGET_PROPERTIES("${_plugin}" PROPERTIES PREFIX "") - SET_TARGET_PROPERTIES("${_plugin}" PROPERTIES COMPILE_FLAGS "${COMPILE_FLAGS}") - SET_TARGET_PROPERTIES("${_plugin}" PROPERTIES LINK_FLAGS "${LINK_FLAGS}") + target_compile_options("${_plugin}" PRIVATE "${COMPILE_FLAGS}") INSTALL(TARGETS "${_plugin}" LIBRARY DESTINATION "${PLUGIN_DIR}/ladspa") ENDFOREACH() @@ -67,7 +75,7 @@ INCLUDE_DIRECTORIES( ) ADD_LIBRARY(iir STATIC ladspa/util/iir.c) -SET_TARGET_PROPERTIES(iir PROPERTIES COMPILE_FLAGS "${PIC_FLAGS}") +target_compile_options(iir PRIVATE "${PIC_FLAGS}") TARGET_LINK_LIBRARIES(bandpass_a_iir_1893 iir) TARGET_LINK_LIBRARIES(bandpass_iir_1892 iir) TARGET_LINK_LIBRARIES(butterworth_1902 iir) @@ -78,23 +86,23 @@ TARGET_LINK_LIBRARIES(notch_iir_1894 iir) FILE(GLOB GSM_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/ladspa/gsm/*.c") LIST(SORT GSM_SOURCES) ADD_LIBRARY(gsm STATIC ${GSM_SOURCES}) -SET_TARGET_PROPERTIES(gsm PROPERTIES COMPILE_FLAGS "${PIC_FLAGS}") +target_compile_options(gsm PRIVATE "${PIC_FLAGS}") TARGET_LINK_LIBRARIES(gsm_1215 gsm) ADD_LIBRARY(gverb STATIC ladspa/gverb/gverb.c ladspa/gverb/gverbdsp.c) -SET_TARGET_PROPERTIES(gverb PROPERTIES COMPILE_FLAGS "${PIC_FLAGS}") +target_compile_options(gverb PRIVATE "${PIC_FLAGS}") TARGET_LINK_LIBRARIES(gverb_1216 gverb) ADD_LIBRARY(blo STATIC ladspa/util/blo.c) -SET_TARGET_PROPERTIES(blo PROPERTIES COMPILE_FLAGS "${PIC_FLAGS}") +target_compile_options(blo PRIVATE "${PIC_FLAGS}") TARGET_LINK_LIBRARIES(analogue_osc_1416 blo) TARGET_LINK_LIBRARIES(fm_osc_1415 blo) TARGET_LINK_LIBRARIES(hermes_filter_1200 blo) ADD_LIBRARY(rms STATIC ladspa/util/rms.c) ADD_LIBRARY(db STATIC ladspa/util/db.c) -SET_TARGET_PROPERTIES(rms PROPERTIES COMPILE_FLAGS "${PIC_FLAGS}") -SET_TARGET_PROPERTIES(db PROPERTIES COMPILE_FLAGS "${PIC_FLAGS}") +target_compile_options(rms PRIVATE "${PIC_FLAGS}") +target_compile_options(db PRIVATE "${PIC_FLAGS}") TARGET_LINK_LIBRARIES(sc1_1425 rms db) TARGET_LINK_LIBRARIES(sc2_1426 rms db) TARGET_LINK_LIBRARIES(sc3_1427 rms db) @@ -103,7 +111,7 @@ TARGET_LINK_LIBRARIES(sc4m_1916 rms db) TARGET_LINK_LIBRARIES(se4_1883 rms db) ADD_LIBRARY(pitchscale STATIC ladspa/util/pitchscale.c) -SET_TARGET_PROPERTIES(pitchscale PROPERTIES COMPILE_FLAGS "${PIC_FLAGS}") -TARGET_LINK_LIBRARIES(pitchscale -lfftw3f) +target_compile_options(pitchscale PRIVATE "${PIC_FLAGS}") +TARGET_LINK_LIBRARIES(pitchscale ${FFTW3F_LIBRARIES}) TARGET_LINK_LIBRARIES(pitch_scale_1193 pitchscale) TARGET_LINK_LIBRARIES(pitch_scale_1194 pitchscale) diff --git a/plugins/LadspaEffect/swh/ladspa b/plugins/LadspaEffect/swh/ladspa index 02bda232041..0f54d2430fe 160000 --- a/plugins/LadspaEffect/swh/ladspa +++ b/plugins/LadspaEffect/swh/ladspa @@ -1 +1 @@ -Subproject commit 02bda232041380c2846414945798cbbfecb2f3f2 +Subproject commit 0f54d2430febb4d5f02d13132dd91d7345e080b5 diff --git a/plugins/LadspaEffect/tap/CMakeLists.txt b/plugins/LadspaEffect/tap/CMakeLists.txt index c8d0a4eb833..84c4694655f 100644 --- a/plugins/LadspaEffect/tap/CMakeLists.txt +++ b/plugins/LadspaEffect/tap/CMakeLists.txt @@ -1,10 +1,19 @@ +set_directory_properties(PROPERTIES SYSTEM TRUE) + INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include") FILE(GLOB PLUGIN_SOURCES tap-plugins/*.c) LIST(SORT PLUGIN_SOURCES) -SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -Wno-write-strings -fomit-frame-pointer -fno-strict-aliasing -funroll-loops -ffast-math") +if(MSVC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fp:fast") +else() + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -fomit-frame-pointer -fno-strict-aliasing -funroll-loops -ffast-math") +endif() FOREACH(_item ${PLUGIN_SOURCES}) GET_FILENAME_COMPONENT(_plugin "${_item}" NAME_WE) ADD_LIBRARY("${_plugin}" MODULE "${_item}") + if(MSVC) + target_link_options("${_plugin}" PRIVATE "/EXPORT:ladspa_descriptor") + endif() # TAP pinknoise will re-init srand(); use existing seed instead IF("${_plugin}" MATCHES "tap_pinknoise") TARGET_COMPILE_DEFINITIONS("${_plugin}" PRIVATE TAP_DISABLE_SRAND=1) @@ -24,4 +33,3 @@ FOREACH(_item ${PLUGIN_SOURCES}) TARGET_LINK_LIBRARIES("${_plugin}" m) ENDIF() ENDFOREACH() - diff --git a/plugins/Lb302/Lb302.cpp b/plugins/Lb302/Lb302.cpp index 44583cbc513..0041b0c5127 100644 --- a/plugins/Lb302/Lb302.cpp +++ b/plugins/Lb302/Lb302.cpp @@ -72,7 +72,7 @@ namespace lmms { -//#define engine::audioEngine()->processingSampleRate() 44100.0f +//#define engine::audioEngine()->outputSampleRate() 44100.0f const float sampleRateCutoff = 44100.0f; extern "C" @@ -111,8 +111,8 @@ void Lb302Filter::recalc() { vcf_e1 = exp(6.109 + 1.5876*(fs->envmod) + 2.1553*(fs->cutoff) - 1.2*(1.0-(fs->reso))); vcf_e0 = exp(5.613 - 0.8*(fs->envmod) + 2.1553*(fs->cutoff) - 0.7696*(1.0-(fs->reso))); - vcf_e0*=M_PI/Engine::audioEngine()->processingSampleRate(); - vcf_e1*=M_PI/Engine::audioEngine()->processingSampleRate(); + vcf_e0*=M_PI/Engine::audioEngine()->outputSampleRate(); + vcf_e1*=M_PI/Engine::audioEngine()->outputSampleRate(); vcf_e1 -= vcf_e0; vcf_rescoeff = exp(-1.20 + 3.455*(fs->reso)); @@ -166,12 +166,10 @@ void Lb302FilterIIR2::recalc() void Lb302FilterIIR2::envRecalc() { - float k, w; - Lb302Filter::envRecalc(); - w = vcf_e0 + vcf_c0; // e0 is adjusted for Hz and doesn't need ENVINC - k = exp(-w/vcf_rescoeff); // Does this mean c0 is inheritantly? + float w = vcf_e0 + vcf_c0; // e0 is adjusted for Hz and doesn't need ENVINC + float k = exp(-w/vcf_rescoeff); // Does this mean c0 is inheritantly? vcf_a = 2.0*cos(2.0*w) * k; vcf_b = -k*k; @@ -211,7 +209,7 @@ Lb302Filter3Pole::Lb302Filter3Pole(Lb302FilterKnobState *p_fs) : void Lb302Filter3Pole::recalc() { // DO NOT CALL BASE CLASS - vcf_e0 = 0.000001; + vcf_e0 = 0.000001f; vcf_e1 = 1.0; } @@ -219,18 +217,15 @@ void Lb302Filter3Pole::recalc() // TODO: Try using k instead of vcf_reso void Lb302Filter3Pole::envRecalc() { - float w,k; - float kfco; - Lb302Filter::envRecalc(); // e0 is adjusted for Hz and doesn't need ENVINC - w = vcf_e0 + vcf_c0; - k = (fs->cutoff > 0.975)?0.975:fs->cutoff; + float w = vcf_e0 + vcf_c0; + float k = (fs->cutoff > 0.975)?0.975:fs->cutoff; // sampleRateCutoff should not be changed to anything dynamic that is outside the // scope of LB302 (like e.g. the audio engine's sample rate) as this changes the filter's cutoff // behavior without any modification to its controls. - kfco = 50.f + (k)*((2300.f-1600.f*(fs->envmod))+(w) * + float kfco = 50.f + (k)*((2300.f-1600.f*(fs->envmod))+(w) * (700.f+1500.f*(k)+(1500.f+(k)*(sampleRateCutoff/2.f-6000.f)) * (fs->envmod)) ); //+iacc*(.3+.7*kfco*kenvmod)*kaccent*kaccurve*2000 @@ -238,7 +233,7 @@ void Lb302Filter3Pole::envRecalc() #ifdef LB_24_IGNORE_ENVELOPE // kfcn = fs->cutoff; - kfcn = 2.0 * kfco / Engine::audioEngine()->processingSampleRate(); + kfcn = 2.0 * kfco / Engine::audioEngine()->outputSampleRate(); #else kfcn = w; #endif @@ -273,8 +268,18 @@ float Lb302Filter3Pole::process(const float& samp) // LBSynth // +static float computeDecayFactor(float decayTimeInSeconds, float targetedAttenuation) +{ + // This is the number of samples that correspond to the decay time in seconds + auto samplesNeededForDecay = decayTimeInSeconds * Engine::audioEngine()->outputSampleRate(); + + // This computes the factor that's needed to make a signal with a value of 1 decay to the + // targeted attenuation over the time in number of samples. + return std::pow(targetedAttenuation, 1. / samplesNeededForDecay); +} + Lb302Synth::Lb302Synth( InstrumentTrack * _instrumentTrack ) : - Instrument( _instrumentTrack, &lb302_plugin_descriptor ), + Instrument(_instrumentTrack, &lb302_plugin_descriptor, nullptr, Flag::IsSingleStreamed), vcf_cut_knob( 0.75f, 0.0f, 1.5f, 0.005f, this, tr( "VCF Cutoff Frequency" ) ), vcf_res_knob( 0.75f, 0.0f, 1.25f, 0.005f, this, tr( "VCF Resonance" ) ), vcf_mod_knob( 0.1f, 0.0f, 1.0f, 0.005f, this, tr( "VCF Envelope Mod" ) ), @@ -286,8 +291,7 @@ Lb302Synth::Lb302Synth( InstrumentTrack * _instrumentTrack ) : accentToggle( false, this, tr( "Accent" ) ), deadToggle( false, this, tr( "Dead" ) ), db24Toggle( false, this, tr( "24dB/oct Filter" ) ), - vca_attack(1.0 - 0.96406088), - vca_decay(0.99897516), + vca_attack(1.f - 0.96406088f), vca_a0(0.5), vca_a(0.), vca_mode(VcaMode::NeverPlayed) @@ -410,7 +414,7 @@ void Lb302Synth::filterChanged() float d = 0.2 + (2.3*vcf_dec_knob.value()); - d *= Engine::audioEngine()->processingSampleRate(); // d *= smpl rate + d *= Engine::audioEngine()->outputSampleRate(); // d *= smpl rate fs.envdecay = pow(0.1, 1.0/d * ENVINC); // decay is 0.1 to the 1/d * ENVINC // vcf_envdecay is now adjusted for both // sampling rate and ENVINC @@ -444,7 +448,7 @@ void Lb302Synth::recalcFilter() // THIS IS OLD 3pole/24dB code, I may reintegrate it. Don't need it // right now. Should be toggled by LB_24_RES_TRICK at the moment. - /*kfcn = 2.0 * (((vcf_cutoff*3000))) / engine::audioEngine()->processingSampleRate(); + /*kfcn = 2.0 * (((vcf_cutoff*3000))) / engine::audioEngine()->outputSampleRate(); kp = ((-2.7528*kfcn + 3.0429)*kfcn + 1.718)*kfcn - 0.9984; kp1 = kp+1.0; kp1h = 0.5*kp1; @@ -455,14 +459,12 @@ void Lb302Synth::recalcFilter() } inline float GET_INC(float freq) { - return freq/Engine::audioEngine()->processingSampleRate(); // TODO: Use actual sampling rate. + return freq/Engine::audioEngine()->outputSampleRate(); // TODO: Use actual sampling rate. } -int Lb302Synth::process(sampleFrame *outbuf, const int size) +int Lb302Synth::process(SampleFrame* outbuf, const int size) { - const float sampleRatio = 44100.f / Engine::audioEngine()->processingSampleRate(); - float w; - float samp; + const float sampleRatio = 44100.f / Engine::audioEngine()->outputSampleRate(); // Hold on to the current VCF, and use it throughout this period Lb302Filter *filter = vcf.loadAcquire(); @@ -488,6 +490,14 @@ int Lb302Synth::process(sampleFrame *outbuf, const int size) // TODO: NORMAL RELEASE // vca_mode = 1; + // Note: this has to be computed during processing and cannot be initialized + // in the constructor because it's dependent on the sample rate and that might + // change during rendering! + // + // At 44.1 kHz this will compute something very close to the previously + // hard coded value of 0.99897516. + auto decay = computeDecayFactor(0.245260770975f, 1.f / 65536.f); + for( int i=0; i0.5) { - w = 2.0*(vco_k-0.5)-1.0; + float w = 2.0 * (vco_k - 0.5) - 1.0; vco_k = 0.5 - sqrtf(1.0-(w*w)); } vco_k *= 2.0; // MOOG wave gets filtered away @@ -610,7 +620,7 @@ int Lb302Synth::process(sampleFrame *outbuf, const int size) #ifdef LB_FILTERED //samp = vcf->process(vco_k)*2.0*vca_a; //samp = vcf->process(vco_k)*2.0; - samp = filter->process(vco_k) * vca_a; + float samp = filter->process(vco_k) * vca_a; //printf("%f %d\n", vco_c, sample_cnt); @@ -638,11 +648,11 @@ int Lb302Synth::process(sampleFrame *outbuf, const int size) // Handle Envelope if(vca_mode==VcaMode::Attack) { vca_a+=(vca_a0-vca_a)*vca_attack; - if(sample_cnt>=0.5*Engine::audioEngine()->processingSampleRate()) + if(sample_cnt>=0.5*Engine::audioEngine()->outputSampleRate()) vca_mode = VcaMode::Idle; } else if(vca_mode == VcaMode::Decay) { - vca_a *= vca_decay; + vca_a *= decay; // the following line actually speeds up processing if(vca_a < (1/65536.0)) { @@ -722,7 +732,7 @@ void Lb302Synth::initSlide() } -void Lb302Synth::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) +void Lb302Synth::playNote( NotePlayHandle * _n, SampleFrame* _working_buffer ) { if( _n->isMasterNote() || ( _n->hasParent() && _n->isReleased() ) ) { @@ -740,8 +750,8 @@ void Lb302Synth::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) m_notes.prepend( _n ); } m_notesMutex.unlock(); - - release_frame = qMax( release_frame, _n->framesLeft() + _n->offset() ); + + release_frame = std::max(release_frame, static_cast(_n->framesLeft()) + static_cast(_n->offset())); } @@ -781,7 +791,7 @@ void Lb302Synth::processNote( NotePlayHandle * _n ) -void Lb302Synth::play( sampleFrame * _working_buffer ) +void Lb302Synth::play( SampleFrame* _working_buffer ) { m_notesMutex.lock(); while( ! m_notes.isEmpty() ) diff --git a/plugins/Lb302/Lb302.h b/plugins/Lb302/Lb302.h index 237a3f3f8be..25a08592cd9 100644 --- a/plugins/Lb302/Lb302.h +++ b/plugins/Lb302/Lb302.h @@ -152,9 +152,9 @@ class Lb302Synth : public Instrument Lb302Synth( InstrumentTrack * _instrument_track ); ~Lb302Synth() override; - void play( sampleFrame * _working_buffer ) override; + void play( SampleFrame* _working_buffer ) override; void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; + SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; @@ -163,16 +163,6 @@ class Lb302Synth : public Instrument QString nodeName() const override; - Flags flags() const override - { - return Flag::IsSingleStreamed; - } - - f_cnt_t desiredReleaseFrames() const override - { - return 0; //4048; - } - gui::PluginView* instantiateView( QWidget * _parent ) override; private: @@ -230,7 +220,6 @@ public slots: int vcf_envpos; // Update counter. Updates when >= ENVINC float vca_attack, // Amp attack - vca_decay, // Amp decay vca_a0, // Initial amplifier coefficient vca_a; // Amplifier coefficient. @@ -257,7 +246,7 @@ public slots: void recalcFilter(); - int process(sampleFrame *outbuf, const int size); + int process(SampleFrame* outbuf, const int size); friend class gui::Lb302SynthView; diff --git a/plugins/Lv2Effect/CMakeLists.txt b/plugins/Lv2Effect/CMakeLists.txt index 915751797d1..e0427eaa30a 100644 --- a/plugins/Lv2Effect/CMakeLists.txt +++ b/plugins/Lv2Effect/CMakeLists.txt @@ -1,7 +1,7 @@ IF(LMMS_HAVE_LV2) - INCLUDE_DIRECTORIES(${LV2_INCLUDE_DIRS}) - INCLUDE_DIRECTORIES(${LILV_INCLUDE_DIRS}) - INCLUDE_DIRECTORIES(${SUIL_INCLUDE_DIRS}) + include_directories(SYSTEM ${LV2_INCLUDE_DIRS}) + include_directories(SYSTEM ${LILV_INCLUDE_DIRS}) + include_directories(SYSTEM ${SUIL_INCLUDE_DIRS}) INCLUDE(BuildPlugin) BUILD_PLUGIN(lv2effect Lv2Effect.cpp Lv2FxControls.cpp Lv2FxControlDialog.cpp Lv2Effect.h Lv2FxControls.h Lv2FxControlDialog.h MOCFILES Lv2Effect.h Lv2FxControls.h Lv2FxControlDialog.h diff --git a/plugins/Lv2Effect/Lv2Effect.cpp b/plugins/Lv2Effect/Lv2Effect.cpp index eef6305cc25..d6b89a2291b 100644 --- a/plugins/Lv2Effect/Lv2Effect.cpp +++ b/plugins/Lv2Effect/Lv2Effect.cpp @@ -68,7 +68,7 @@ Lv2Effect::Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key *ke -bool Lv2Effect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) +bool Lv2Effect::processAudioBuffer(SampleFrame* buf, const fpp_t frames) { if (!isEnabled() || !isRunning()) { return false; } Q_ASSERT(frames <= static_cast(m_tmpOutputSmps.size())); diff --git a/plugins/Lv2Effect/Lv2Effect.h b/plugins/Lv2Effect/Lv2Effect.h index a28182132da..bc81eb59098 100644 --- a/plugins/Lv2Effect/Lv2Effect.h +++ b/plugins/Lv2Effect/Lv2Effect.h @@ -42,7 +42,7 @@ class Lv2Effect : public Effect */ Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key* _key); - bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override; + bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override; EffectControls* controls() override { return &m_controls; } Lv2FxControls* lv2Controls() { return &m_controls; } @@ -50,7 +50,7 @@ class Lv2Effect : public Effect private: Lv2FxControls m_controls; - std::vector m_tmpOutputSmps; + std::vector m_tmpOutputSmps; }; diff --git a/plugins/Lv2Instrument/CMakeLists.txt b/plugins/Lv2Instrument/CMakeLists.txt index 290bd84e82e..e10eff69215 100644 --- a/plugins/Lv2Instrument/CMakeLists.txt +++ b/plugins/Lv2Instrument/CMakeLists.txt @@ -1,7 +1,7 @@ IF(LMMS_HAVE_LV2) - INCLUDE_DIRECTORIES(${LV2_INCLUDE_DIRS}) - INCLUDE_DIRECTORIES(${LILV_INCLUDE_DIRS}) - INCLUDE_DIRECTORIES(${SUIL_INCLUDE_DIRS}) + include_directories(SYSTEM ${LV2_INCLUDE_DIRS}) + include_directories(SYSTEM ${LILV_INCLUDE_DIRS}) + include_directories(SYSTEM ${SUIL_INCLUDE_DIRS}) INCLUDE(BuildPlugin) BUILD_PLUGIN(lv2instrument Lv2Instrument.cpp Lv2Instrument.h MOCFILES Lv2Instrument.h EMBEDDED_RESOURCES logo.png) ENDIF(LMMS_HAVE_LV2) diff --git a/plugins/Lv2Instrument/Lv2Instrument.cpp b/plugins/Lv2Instrument/Lv2Instrument.cpp index 31682932762..8da2d913ed3 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.cpp +++ b/plugins/Lv2Instrument/Lv2Instrument.cpp @@ -73,7 +73,13 @@ Plugin::Descriptor PLUGIN_EXPORT lv2instrument_plugin_descriptor = Lv2Instrument::Lv2Instrument(InstrumentTrack *instrumentTrackArg, Descriptor::SubPluginFeatures::Key *key) : - Instrument(instrumentTrackArg, &lv2instrument_plugin_descriptor, key), + Instrument(instrumentTrackArg, &lv2instrument_plugin_descriptor, key, +#ifdef LV2_INSTRUMENT_USE_MIDI + Flag::IsSingleStreamed | Flag::IsMidiBased +#else + Flag::IsSingleStreamed +#endif + ), Lv2ControlBase(this, key->attributes["uri"]) { clearRunningNotes(); @@ -171,7 +177,7 @@ bool Lv2Instrument::handleMidiEvent( // not yet working #ifndef LV2_INSTRUMENT_USE_MIDI -void Lv2Instrument::playNote(NotePlayHandle *nph, sampleFrame *) +void Lv2Instrument::playNote(NotePlayHandle *nph, SampleFrame*) { } #endif @@ -179,7 +185,7 @@ void Lv2Instrument::playNote(NotePlayHandle *nph, sampleFrame *) -void Lv2Instrument::play(sampleFrame *buf) +void Lv2Instrument::play(SampleFrame* buf) { copyModelsFromLmms(); diff --git a/plugins/Lv2Instrument/Lv2Instrument.h b/plugins/Lv2Instrument/Lv2Instrument.h index de41dc958e1..9fbc7b7f699 100644 --- a/plugins/Lv2Instrument/Lv2Instrument.h +++ b/plugins/Lv2Instrument/Lv2Instrument.h @@ -26,12 +26,13 @@ #define LV2_INSTRUMENT_H #include +#include #include "Instrument.h" #include "InstrumentView.h" -#include "Note.h" #include "Lv2ControlBase.h" #include "Lv2ViewBase.h" +#include "Note.h" // whether to use MIDI vs playHandle // currently only MIDI works @@ -77,21 +78,13 @@ class Lv2Instrument : public Instrument, public Lv2ControlBase bool handleMidiEvent(const MidiEvent &event, const TimePos &time = TimePos(), f_cnt_t offset = 0) override; #else - void playNote(NotePlayHandle *nph, sampleFrame *) override; + void playNote(NotePlayHandle *nph, SampleFrame*) override; #endif - void play(sampleFrame *buf) override; + void play(SampleFrame* buf) override; /* misc */ - Flags flags() const override - { -#ifdef LV2_INSTRUMENT_USE_MIDI - return Flag::IsSingleStreamed | Flag::IsMidiBased; -#else - return Flag::IsSingleStreamed; -#endif - } gui::PluginView* instantiateView(QWidget *parent) override; private slots: diff --git a/plugins/MidiExport/MidiExport.cpp b/plugins/MidiExport/MidiExport.cpp index 2600a40f2f9..b3b08a0e113 100644 --- a/plugins/MidiExport/MidiExport.cpp +++ b/plugins/MidiExport/MidiExport.cpp @@ -76,21 +76,18 @@ bool MidiExport::tryExport(const TrackContainer::TrackList &tracks, f.open(QIODevice::WriteOnly); QDataStream midiout(&f); - InstrumentTrack* instTrack; - PatternTrack* patternTrack; QDomElement element; int nTracks = 0; auto buffer = std::array{}; - uint32_t size; for (const Track* track : tracks) if (track->type() == Track::Type::Instrument) nTracks++; for (const Track* track : patternStoreTracks) if (track->type() == Track::Type::Instrument) nTracks++; // midi header MidiFile::MIDIHeader header(nTracks); - size = header.writeToBuffer(buffer.data()); + uint32_t size = header.writeToBuffer(buffer.data()); midiout.writeRawData((char *)buffer.data(), size); std::vector>> plists; @@ -108,7 +105,7 @@ bool MidiExport::tryExport(const TrackContainer::TrackList &tracks, //mtrack.addProgramChange(0, 0); mtrack.addTempo(tempo, 0); - instTrack = dynamic_cast(track); + auto instTrack = dynamic_cast(track); element = instTrack->saveState(dataFile, dataFile.content()); int base_pitch = 0; @@ -146,7 +143,7 @@ bool MidiExport::tryExport(const TrackContainer::TrackList &tracks, if (track->type() == Track::Type::Pattern) { - patternTrack = dynamic_cast(track); + auto patternTrack = dynamic_cast(track); element = patternTrack->saveState(dataFile, dataFile.content()); std::vector> plist; @@ -184,7 +181,7 @@ bool MidiExport::tryExport(const TrackContainer::TrackList &tracks, //mtrack.addProgramChange(0, 0); mtrack.addTempo(tempo, 0); - instTrack = dynamic_cast(track); + auto instTrack = dynamic_cast(track); element = instTrack->saveState(dataFile, dataFile.content()); int base_pitch = 0; diff --git a/plugins/MidiExport/MidiFile.hpp b/plugins/MidiExport/MidiFile.hpp index 79c8dcce2cd..26f203cd67c 100644 --- a/plugins/MidiExport/MidiFile.hpp +++ b/plugins/MidiExport/MidiFile.hpp @@ -25,6 +25,7 @@ #include #include #include +#include using std::string; using std::vector; @@ -47,8 +48,8 @@ int writeVarLength(uint32_t val, uint8_t *buffer) byte in question is the last in the stream */ int size = 0; - uint8_t result, little_endian[4]; - result = val & 0x7F; + uint8_t little_endian[4]; + uint8_t result = val & 0x7F; little_endian[size++] = result; val = val >> 7; while (val > 0) @@ -129,31 +130,37 @@ struct Event inline int writeToBuffer(uint8_t *buffer) const { - uint8_t code, fourbytes[4]; - int size=0; - switch (type) + int size = 0; + switch (type) { case NOTE_ON: - code = 0x9 << 4 | channel; + { + uint8_t code = 0x9 << 4 | channel; size += writeVarLength(time, buffer+size); buffer[size++] = code; buffer[size++] = pitch; buffer[size++] = volume; break; + } case NOTE_OFF: - code = 0x8 << 4 | channel; + { + uint8_t code = 0x8 << 4 | channel; size += writeVarLength(time, buffer+size); buffer[size++] = code; buffer[size++] = pitch; buffer[size++] = volume; break; + } case TEMPO: - code = 0xFF; + { + uint8_t code = 0xFF; size += writeVarLength(time, buffer+size); buffer[size++] = code; buffer[size++] = 0x51; buffer[size++] = 0x03; - writeBigEndian4(int(60000000.0 / tempo), fourbytes); + + std::array fourbytes; + writeBigEndian4(int(60000000.0 / tempo), fourbytes.data()); //printf("tempo of %x translates to ", tempo); /* @@ -164,23 +171,27 @@ struct Event buffer[size++] = fourbytes[2]; buffer[size++] = fourbytes[3]; break; + } case PROG_CHANGE: - code = 0xC << 4 | channel; + { + uint8_t code = 0xC << 4 | channel; size += writeVarLength(time, buffer+size); buffer[size++] = code; buffer[size++] = programNumber; break; + } case TRACK_NAME: + { size += writeVarLength(time, buffer+size); buffer[size++] = 0xFF; buffer[size++] = 0x03; size += writeVarLength(trackName.size(), buffer+size); trackName.copy((char *)(&buffer[size]), trackName.size()); size += trackName.size(); -// buffer[size++] = '\0'; -// buffer[size++] = '\0'; - break; + // buffer[size++] = '\0'; + // buffer[size++] = '\0'; + } } return size; } // writeEventsToBuffer @@ -275,7 +286,7 @@ class MIDITrack vector _events = events; std::sort(_events.begin(), _events.end()); vector::const_iterator it; - uint32_t time_last = 0, tmp; + uint32_t time_last = 0; for (it = _events.begin(); it!=_events.end(); ++it) { Event e = *it; @@ -283,7 +294,7 @@ class MIDITrack printf("error: e.time=%d time_last=%d\n", e.time, time_last); assert(false); } - tmp = e.time; + uint32_t tmp = e.time; e.time -= time_last; time_last = tmp; start += e.writeToBuffer(buffer+start); diff --git a/plugins/MidiImport/MidiImport.h b/plugins/MidiImport/MidiImport.h index 817d06be855..0870511b571 100644 --- a/plugins/MidiImport/MidiImport.h +++ b/plugins/MidiImport/MidiImport.h @@ -61,10 +61,10 @@ class MidiImport : public ImportFilter inline int readInt( int _bytes ) { - int c, value = 0; + int value = 0; do { - c = readByte(); + int c = readByte(); if( c == -1 ) { return( -1 ); diff --git a/plugins/Monstro/Monstro.cpp b/plugins/Monstro/Monstro.cpp index 9563f756d3c..874b5d8d13a 100644 --- a/plugins/Monstro/Monstro.cpp +++ b/plugins/Monstro/Monstro.cpp @@ -30,7 +30,6 @@ #include "ComboBox.h" #include "Engine.h" #include "InstrumentTrack.h" -#include "gui_templates.h" #include "lmms_math.h" #include "interpolation.h" @@ -111,7 +110,7 @@ MonstroSynth::MonstroSynth( MonstroInstrument * _i, NotePlayHandle * _nph ) : } -void MonstroSynth::renderOutput( fpp_t _frames, sampleFrame * _buf ) +void MonstroSynth::renderOutput( fpp_t _frames, SampleFrame* _buf ) { float modtmp; // temp variable for freq modulation // macros for modulating with env/lfos @@ -515,7 +514,7 @@ void MonstroSynth::renderOutput( fpp_t _frames, sampleFrame * _buf ) } sample_t O2R = 0.; - if (len_r != 0.) + if (pd_r != 0.) { len_r = BandLimitedWave::pdToLen(pd_r); if (m_counter2r > 0) @@ -686,7 +685,7 @@ void MonstroSynth::renderOutput( fpp_t _frames, sampleFrame * _buf ) } -inline void MonstroSynth::updateModulators( float * env1, float * env2, float * lfo1, float * lfo2, int frames ) +inline void MonstroSynth::updateModulators(float * env1, float * env2, float * lfo1, float * lfo2, f_cnt_t frames) { // frames played before const f_cnt_t tfp = m_nph->totalFramesPlayed(); @@ -791,7 +790,7 @@ inline void MonstroSynth::updateModulators( float * env1, float * env2, float * // attack for( f_cnt_t f = 0; f < frames; ++f ) { - if( tfp + f < m_lfoatt[i] ) lfo[i][f] *= ( static_cast( tfp ) / m_lfoatt[i] ); + if (tfp + f < static_cast(m_lfoatt[i])) { lfo[i][f] *= static_cast(tfp) / m_lfoatt[i]; } } @@ -866,31 +865,31 @@ inline sample_t MonstroSynth::calcSlope( int slope, sample_t s ) MonstroInstrument::MonstroInstrument( InstrumentTrack * _instrument_track ) : Instrument( _instrument_track, &monstro_plugin_descriptor ), - m_osc1Vol( 33.0, 0.0, 200.0, 0.1, this, tr( "Osc 1 volume" ) ), - m_osc1Pan( 0.0, -100.0, 100.0, 0.1, this, tr( "Osc 1 panning" ) ), + m_osc1Vol(33.f, 0.f, 200.f, 0.1f, this, tr("Osc 1 volume")), + m_osc1Pan(0.f, -100.f, 100.f, 0.1f, this, tr("Osc 1 panning")), m_osc1Crs( 0.0, -24.0, 24.0, 1.0, this, tr( "Osc 1 coarse detune" ) ), m_osc1Ftl( 0.0, -100.0, 100.0, 1.0, this, tr( "Osc 1 fine detune left" ) ), m_osc1Ftr( 0.0, -100.0, 100.0, 1.0, this, tr( "Osc 1 fine detune right" ) ), - m_osc1Spo( 0.0, -180.0, 180.0, 0.1, this, tr( "Osc 1 stereo phase offset" ) ), - m_osc1Pw( 50.0, PW_MIN, PW_MAX, 0.01, this, tr( "Osc 1 pulse width" ) ), + m_osc1Spo(0.f, -180.f, 180.f, 0.1f, this, tr("Osc 1 stereo phase offset")), + m_osc1Pw(50.f, PW_MIN, PW_MAX, 0.01f, this, tr("Osc 1 pulse width")), m_osc1SSR( false, this, tr( "Osc 1 sync send on rise" ) ), m_osc1SSF( false, this, tr( "Osc 1 sync send on fall" ) ), - m_osc2Vol( 33.0, 0.0, 200.0, 0.1, this, tr( "Osc 2 volume" ) ), - m_osc2Pan( 0.0, -100.0, 100.0, 0.1, this, tr( "Osc 2 panning" ) ), + m_osc2Vol(33.f, 0.f, 200.f, 0.1f, this, tr("Osc 2 volume")), + m_osc2Pan(0.f, -100.f, 100.f, 0.1f, this, tr("Osc 2 panning")), m_osc2Crs( 0.0, -24.0, 24.0, 1.0, this, tr( "Osc 2 coarse detune" ) ), m_osc2Ftl( 0.0, -100.0, 100.0, 1.0, this, tr( "Osc 2 fine detune left" ) ), m_osc2Ftr( 0.0, -100.0, 100.0, 1.0, this, tr( "Osc 2 fine detune right" ) ), - m_osc2Spo( 0.0, -180.0, 180.0, 0.1, this, tr( "Osc 2 stereo phase offset" ) ), + m_osc2Spo(0.f, -180.f, 180.f, 0.1f, this, tr("Osc 2 stereo phase offset")), m_osc2Wave( this, tr( "Osc 2 waveform" ) ), m_osc2SyncH( false, this, tr( "Osc 2 sync hard" ) ), m_osc2SyncR( false, this, tr( "Osc 2 sync reverse" ) ), - m_osc3Vol( 33.0, 0.0, 200.0, 0.1, this, tr( "Osc 3 volume" ) ), - m_osc3Pan( 0.0, -100.0, 100.0, 0.1, this, tr( "Osc 3 panning" ) ), + m_osc3Vol(33.f, 0.f, 200.f, 0.1f, this, tr("Osc 3 volume")), + m_osc3Pan(0.f, -100.f, 100.f, 0.1f, this, tr("Osc 3 panning")), m_osc3Crs( 0.0, -24.0, 24.0, 1.0, this, tr( "Osc 3 coarse detune" ) ), - m_osc3Spo( 0.0, -180.0, 180.0, 0.1, this, tr( "Osc 3 Stereo phase offset" ) ), - m_osc3Sub( 0.0, -100.0, 100.0, 0.1, this, tr( "Osc 3 sub-oscillator mix" ) ), + m_osc3Spo(0.f, -180.f, 180.f, 0.1f, this, tr("Osc 3 Stereo phase offset")), + m_osc3Sub(0.f, -100.f, 100.f, 0.1f, this, tr("Osc 3 sub-oscillator mix")), m_osc3Wave1( this, tr( "Osc 3 waveform 1" ) ), m_osc3Wave2( this, tr( "Osc 3 waveform 2" ) ), m_osc3SyncH( false, this, tr( "Osc 3 sync hard" ) ), @@ -898,13 +897,13 @@ MonstroInstrument::MonstroInstrument( InstrumentTrack * _instrument_track ) : m_lfo1Wave( this, tr( "LFO 1 waveform" ) ), m_lfo1Att( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "LFO 1 attack" ) ), - m_lfo1Rate( 1.0f, 0.1, 10000.0, 0.1, 10000.0f, this, tr( "LFO 1 rate" ) ), - m_lfo1Phs( 0.0, -180.0, 180.0, 0.1, this, tr( "LFO 1 phase" ) ), + m_lfo1Rate(1.0f, 0.1f, 10000.f, 0.1f, 10000.0f, this, tr("LFO 1 rate")), + m_lfo1Phs(0.f, -180.f, 180.f, 0.1f, this, tr("LFO 1 phase")), m_lfo2Wave( this, tr( "LFO 2 waveform" ) ), m_lfo2Att( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "LFO 2 attack" ) ), - m_lfo2Rate( 1.0f, 0.1, 10000.0, 0.1, 10000.0f, this, tr( "LFO 2 rate" ) ), - m_lfo2Phs( 0.0, -180.0, 180.0, 0.1, this, tr( "LFO 2 phase" ) ), + m_lfo2Rate(1.0f, 0.1f, 10000.f, 0.1f, 10000.0f, this, tr("LFO 2 rate")), + m_lfo2Phs(0.0, -180.f, 180.f, 0.1f, this, tr("LFO 2 phase")), m_env1Pre( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 1 pre-delay" ) ), m_env1Att( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 1 attack" ) ), @@ -1063,7 +1062,7 @@ MonstroInstrument::MonstroInstrument( InstrumentTrack * _instrument_track ) : void MonstroInstrument::playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) + SampleFrame* _working_buffer ) { const fpp_t frames = _n->framesLeftForCurrentPeriod(); const f_cnt_t offset = _n->noteOffset(); @@ -1327,13 +1326,12 @@ QString MonstroInstrument::nodeName() const return monstro_plugin_descriptor.name; } - -f_cnt_t MonstroInstrument::desiredReleaseFrames() const +float MonstroInstrument::desiredReleaseTimeMs() const { - return qMax( 64, qMax( m_env1_relF, m_env2_relF ) ); + const auto maxEnvelope = std::max(m_env1_rel, m_env2_rel); + return std::max(1.5f, maxEnvelope); } - gui::PluginView* MonstroInstrument::instantiateView( QWidget * _parent ) { return( new gui::MonstroView( this, _parent ) ); @@ -1449,7 +1447,7 @@ void MonstroInstrument::updateLFOAtts() void MonstroInstrument::updateSamplerate() { - m_samplerate = Engine::audioEngine()->processingSampleRate(); + m_samplerate = Engine::audioEngine()->outputSampleRate(); m_integrator = 0.5f - ( 0.5f - INTEGRATOR ) * 44100.0f / m_samplerate; m_fmCorrection = 44100.f / m_samplerate * FM_AMOUNT; @@ -1694,7 +1692,6 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) m_osc2WaveBox = new ComboBox( view ); m_osc2WaveBox -> setGeometry( 204, O2ROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); - m_osc2WaveBox->setFont( pointSize<8>( m_osc2WaveBox->font() ) ); maketinyled( m_osc2SyncHButton, 212, O2ROW - 3, tr( "Hard sync oscillator 2" ) ) maketinyled( m_osc2SyncRButton, 191, O2ROW - 3, tr( "Reverse sync oscillator 2" ) ) @@ -1709,18 +1706,15 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) m_osc3Wave1Box = new ComboBox( view ); m_osc3Wave1Box -> setGeometry( 160, O3ROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); - m_osc3Wave1Box->setFont( pointSize<8>( m_osc3Wave1Box->font() ) ); m_osc3Wave2Box = new ComboBox( view ); m_osc3Wave2Box -> setGeometry( 204, O3ROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); - m_osc3Wave2Box->setFont( pointSize<8>( m_osc3Wave2Box->font() ) ); maketinyled( m_osc3SyncHButton, 212, O3ROW - 3, tr( "Hard sync oscillator 3" ) ) maketinyled( m_osc3SyncRButton, 191, O3ROW - 3, tr( "Reverse sync oscillator 3" ) ) m_lfo1WaveBox = new ComboBox( view ); m_lfo1WaveBox -> setGeometry( 2, LFOROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); - m_lfo1WaveBox->setFont( pointSize<8>( m_lfo1WaveBox->font() ) ); maketsknob( m_lfo1AttKnob, LFOCOL1, LFOROW, tr( "Attack" ), " ms", "lfoKnob" ) maketsknob( m_lfo1RateKnob, LFOCOL2, LFOROW, tr( "Rate" ), " ms", "lfoKnob" ) @@ -1728,7 +1722,6 @@ QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) m_lfo2WaveBox = new ComboBox( view ); m_lfo2WaveBox -> setGeometry( 127, LFOROW + 7, 42, ComboBox::DEFAULT_HEIGHT ); - m_lfo2WaveBox->setFont( pointSize<8>( m_lfo2WaveBox->font() ) ); maketsknob(m_lfo2AttKnob, LFOCOL4, LFOROW, tr("Attack"), " ms", "lfoKnob") maketsknob(m_lfo2RateKnob, LFOCOL5, LFOROW, tr("Rate"), " ms", "lfoKnob") diff --git a/plugins/Monstro/Monstro.h b/plugins/Monstro/Monstro.h index 919409b2de5..a1ae72a381b 100644 --- a/plugins/Monstro/Monstro.h +++ b/plugins/Monstro/Monstro.h @@ -177,14 +177,14 @@ class MonstroSynth MonstroSynth( MonstroInstrument * _i, NotePlayHandle * _nph ); virtual ~MonstroSynth() = default; - void renderOutput( fpp_t _frames, sampleFrame * _buf ); + void renderOutput( fpp_t _frames, SampleFrame* _buf ); private: MonstroInstrument * m_parent; NotePlayHandle * m_nph; - inline void updateModulators( float * env1, float * env2, float * lfo1, float * lfo2, int frames ); + inline void updateModulators(float * env1, float * env2, float * lfo1, float * lfo2, f_cnt_t frames); // linear interpolation /* inline sample_t interpolate( sample_t s1, sample_t s2, float x ) @@ -357,7 +357,7 @@ class MonstroInstrument : public Instrument ~MonstroInstrument() override = default; void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; + SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; void saveSettings( QDomDocument & _doc, @@ -366,7 +366,7 @@ class MonstroInstrument : public Instrument QString nodeName() const override; - f_cnt_t desiredReleaseFrames() const override; + float desiredReleaseTimeMs() const override; gui::PluginView* instantiateView( QWidget * _parent ) override; diff --git a/plugins/MultitapEcho/MultitapEcho.cpp b/plugins/MultitapEcho/MultitapEcho.cpp index ff3ca828a27..ecc1d8f304e 100644 --- a/plugins/MultitapEcho/MultitapEcho.cpp +++ b/plugins/MultitapEcho/MultitapEcho.cpp @@ -25,6 +25,7 @@ #include "MultitapEcho.h" #include "embed.h" +#include "lmms_basics.h" #include "plugin_export.h" namespace lmms @@ -55,10 +56,10 @@ MultitapEchoEffect::MultitapEchoEffect( Model* parent, const Descriptor::SubPlug m_stages( 1 ), m_controls( this ), m_buffer( 16100.0f ), - m_sampleRate( Engine::audioEngine()->processingSampleRate() ), + m_sampleRate( Engine::audioEngine()->outputSampleRate() ), m_sampleRatio( 1.0f / m_sampleRate ) { - m_work = new sampleFrame[Engine::audioEngine()->framesPerPeriod()]; + m_work = new SampleFrame[Engine::audioEngine()->framesPerPeriod()]; m_buffer.reset(); m_stages = static_cast( m_controls.m_stages.value() ); updateFilters( 0, 19 ); @@ -83,9 +84,9 @@ void MultitapEchoEffect::updateFilters( int begin, int end ) } -void MultitapEchoEffect::runFilter( sampleFrame * dst, sampleFrame * src, StereoOnePole & filter, const fpp_t frames ) +void MultitapEchoEffect::runFilter( SampleFrame* dst, SampleFrame* src, StereoOnePole & filter, const fpp_t frames ) { - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { dst[f][0] = filter.update( src[f][0], 0 ); dst[f][1] = filter.update( src[f][1], 1 ); @@ -93,7 +94,7 @@ void MultitapEchoEffect::runFilter( sampleFrame * dst, sampleFrame * src, Stereo } -bool MultitapEchoEffect::processAudioBuffer( sampleFrame * buf, const fpp_t frames ) +bool MultitapEchoEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames ) { if( !isEnabled() || !isRunning () ) { @@ -118,8 +119,8 @@ bool MultitapEchoEffect::processAudioBuffer( sampleFrame * buf, const fpp_t fram } // add dry buffer - never swap inputs for dry - m_buffer.writeAddingMultiplied( buf, 0, frames, dryGain ); - + m_buffer.writeAddingMultiplied(buf, f_cnt_t{0}, frames, dryGain); + // swapped inputs? if( swapInputs ) { @@ -151,7 +152,7 @@ bool MultitapEchoEffect::processAudioBuffer( sampleFrame * buf, const fpp_t fram // pop the buffer and mix it into output m_buffer.pop( m_work ); - for( int f = 0; f < frames; ++f ) + for (auto f = std::size_t{0}; f < frames; ++f) { buf[f][0] = d * buf[f][0] + w * m_work[f][0]; buf[f][1] = d * buf[f][1] + w * m_work[f][1]; @@ -176,4 +177,4 @@ PLUGIN_EXPORT Plugin * lmms_plugin_main( Model* parent, void* data ) } -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/plugins/MultitapEcho/MultitapEcho.h b/plugins/MultitapEcho/MultitapEcho.h index 6145b9f861f..d6e981fbd58 100644 --- a/plugins/MultitapEcho/MultitapEcho.h +++ b/plugins/MultitapEcho/MultitapEcho.h @@ -40,7 +40,7 @@ class MultitapEchoEffect : public Effect public: MultitapEchoEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ); ~MultitapEchoEffect() override; - bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override; + bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override; EffectControls* controls() override { @@ -49,7 +49,7 @@ class MultitapEchoEffect : public Effect private: void updateFilters( int begin, int end ); - void runFilter( sampleFrame * dst, sampleFrame * src, StereoOnePole & filter, const fpp_t frames ); + void runFilter( SampleFrame* dst, SampleFrame* src, StereoOnePole & filter, const fpp_t frames ); inline void setFilterFreq( float fc, StereoOnePole & f ) { @@ -70,7 +70,7 @@ class MultitapEchoEffect : public Effect float m_sampleRate; float m_sampleRatio; - sampleFrame * m_work; + SampleFrame* m_work; friend class MultitapEchoControls; diff --git a/plugins/MultitapEcho/MultitapEchoControls.cpp b/plugins/MultitapEcho/MultitapEchoControls.cpp index 19564ba8adb..4df05afc688 100644 --- a/plugins/MultitapEcho/MultitapEchoControls.cpp +++ b/plugins/MultitapEcho/MultitapEchoControls.cpp @@ -172,7 +172,7 @@ void MultitapEchoControls::lengthChanged() void MultitapEchoControls::sampleRateChanged() { - m_effect->m_sampleRate = Engine::audioEngine()->processingSampleRate(); + m_effect->m_sampleRate = Engine::audioEngine()->outputSampleRate(); m_effect->m_sampleRatio = 1.0f / m_effect->m_sampleRate; m_effect->updateFilters( 0, 19 ); } diff --git a/plugins/Nes/Nes.cpp b/plugins/Nes/Nes.cpp index df88c4942ba..fb7c52459ca 100644 --- a/plugins/Nes/Nes.cpp +++ b/plugins/Nes/Nes.cpp @@ -103,7 +103,7 @@ NesObject::NesObject( NesInstrument * nes, const sample_rate_t samplerate, NoteP } -void NesObject::renderOutput( sampleFrame * buf, fpp_t frames ) +void NesObject::renderOutput( SampleFrame* buf, fpp_t frames ) { //////////////////////////////// // // @@ -545,14 +545,14 @@ NesInstrument::NesInstrument( InstrumentTrack * instrumentTrack ) : -void NesInstrument::playNote( NotePlayHandle * n, sampleFrame * workingBuffer ) +void NesInstrument::playNote( NotePlayHandle * n, SampleFrame* workingBuffer ) { const fpp_t frames = n->framesLeftForCurrentPeriod(); const f_cnt_t offset = n->noteOffset(); if (!n->m_pluginData) { - auto nes = new NesObject(this, Engine::audioEngine()->processingSampleRate(), n); + auto nes = new NesObject(this, Engine::audioEngine()->outputSampleRate(), n); n->m_pluginData = nes; } diff --git a/plugins/Nes/Nes.h b/plugins/Nes/Nes.h index a05b3a2f85c..207c22e837c 100644 --- a/plugins/Nes/Nes.h +++ b/plugins/Nes/Nes.h @@ -60,22 +60,22 @@ namespace lmms { -const float NES_SIMPLE_FILTER = 1.0 / 20.0; // simulate nes analog audio output +const float NES_SIMPLE_FILTER = 1.f / 20.f; // simulate nes analog audio output const float NFB = 895000.0f; const float NOISE_FREQS[16] = { NFB/5, NFB/9, NFB/17, NFB/33, NFB/65, NFB/97, NFB/129, NFB/161, NFB/193, NFB/255, NFB/381, NFB/509, NFB/763, NFB/1017, NFB/2035, NFB/4069 }; const uint16_t LFSR_INIT = 1; const float DUTY_CYCLE[4] = { 0.125, 0.25, 0.5, 0.75 }; -const float DITHER_AMP = 1.0 / 60.0; +const float DITHER_AMP = 1.f / 60.f; const float MIN_FREQ = 10.0; const int TRIANGLE_WAVETABLE[32] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }; const float NES_DIST = 0.9f; // simulate the slight nonlinear distortion in nes audio output -const float NES_MIXING_12 = 1.0 / 20.0; -const float NES_MIXING_34 = 1.0 / 12.0; -const float NES_MIXING_ALL = 1.0 / ( NES_MIXING_12 + NES_MIXING_34 ); // constants to simulate the hardwired mixing values for nes channels +const float NES_MIXING_12 = 1.f / 20.f; +const float NES_MIXING_34 = 1.f / 12.f; +const float NES_MIXING_ALL = 1.f / (NES_MIXING_12 + NES_MIXING_34); // constants to simulate the hardwired mixing values for nes channels const int MIN_WLEN = 4; @@ -95,7 +95,7 @@ class NesObject NesObject( NesInstrument * nes, const sample_rate_t samplerate, NotePlayHandle * nph ); virtual ~NesObject() = default; - void renderOutput( sampleFrame * buf, fpp_t frames ); + void renderOutput( SampleFrame* buf, fpp_t frames ); void updateVibrato( float * freq ); void updatePitch(); @@ -212,7 +212,7 @@ class NesInstrument : public Instrument ~NesInstrument() override = default; void playNote( NotePlayHandle * n, - sampleFrame * workingBuffer ) override; + SampleFrame* workingBuffer ) override; void deleteNotePluginData( NotePlayHandle * n ) override; @@ -222,9 +222,9 @@ class NesInstrument : public Instrument QString nodeName() const override; - f_cnt_t desiredReleaseFrames() const override + float desiredReleaseTimeMs() const override { - return( 8 ); + return 0.2f; } gui::PluginView* instantiateView( QWidget * parent ) override; diff --git a/plugins/OpulenZ/CMakeLists.txt b/plugins/OpulenZ/CMakeLists.txt index 58f661406a7..fcfce7819ad 100644 --- a/plugins/OpulenZ/CMakeLists.txt +++ b/plugins/OpulenZ/CMakeLists.txt @@ -1,20 +1,16 @@ -INCLUDE(BuildPlugin) +include(BuildPlugin) -# Avoid unused warnings for mididata.h -IF(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-variable") -ENDIF() - -INCLUDE_DIRECTORIES(adplug/src) +add_library(adplug STATIC + adplug/src/fmopl.c + adplug/src/temuopl.cpp +) +target_include_directories(adplug PUBLIC adplug/src) +set_target_properties(adplug PROPERTIES SYSTEM TRUE) -BUILD_PLUGIN(opulenz +build_plugin(opulenz OpulenZ.cpp OpulenZ.h - adplug/src/opl.h - adplug/src/fmopl.c - adplug/src/fmopl.h - adplug/src/temuopl.cpp - adplug/src/temuopl.h MOCFILES OpulenZ.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png" ) +target_link_libraries(opulenz adplug) diff --git a/plugins/OpulenZ/OpulenZ.cpp b/plugins/OpulenZ/OpulenZ.cpp index d90d5f343a4..5fd4bf58afa 100644 --- a/plugins/OpulenZ/OpulenZ.cpp +++ b/plugins/OpulenZ/OpulenZ.cpp @@ -46,9 +46,9 @@ #include #include -#include "opl.h" -#include "temuopl.h" -#include "mididata.h" +#include +#include +#include #include "embed.h" #include "debug.h" @@ -95,7 +95,7 @@ QMutex OpulenzInstrument::emulatorMutex; const auto adlib_opadd = std::array{0x00, 0x01, 0x02, 0x08, 0x09, 0x0A, 0x10, 0x11, 0x12}; OpulenzInstrument::OpulenzInstrument( InstrumentTrack * _instrument_track ) : - Instrument( _instrument_track, &opulenz_plugin_descriptor ), + Instrument(_instrument_track, &opulenz_plugin_descriptor, nullptr, Flag::IsSingleStreamed | Flag::IsMidiBased), m_patchModel( 0, 0, 127, this, tr( "Patch" ) ), op1_a_mdl(14.0, 0.0, 15.0, 1.0, this, tr( "Op 1 attack" ) ), op1_d_mdl(14.0, 0.0, 15.0, 1.0, this, tr( "Op 1 decay" ) ), @@ -140,7 +140,7 @@ OpulenzInstrument::OpulenzInstrument( InstrumentTrack * _instrument_track ) : // Create an emulator - samplerate, 16 bit, mono emulatorMutex.lock(); - theEmulator = new CTemuopl(Engine::audioEngine()->processingSampleRate(), true, false); + theEmulator = new CTemuopl(Engine::audioEngine()->outputSampleRate(), true, false); theEmulator->init(); // Enable waveform selection theEmulator->write(0x01,0x20); @@ -231,7 +231,7 @@ OpulenzInstrument::~OpulenzInstrument() { void OpulenzInstrument::reloadEmulator() { delete theEmulator; emulatorMutex.lock(); - theEmulator = new CTemuopl(Engine::audioEngine()->processingSampleRate(), true, false); + theEmulator = new CTemuopl(Engine::audioEngine()->outputSampleRate(), true, false); theEmulator->init(); theEmulator->write(0x01,0x20); emulatorMutex.unlock(); @@ -244,14 +244,12 @@ void OpulenzInstrument::reloadEmulator() { // This shall only be called from code protected by the holy Mutex! void OpulenzInstrument::setVoiceVelocity(int voice, int vel) { - int vel_adjusted; + int vel_adjusted = !fm_mdl.value() + ? 63 - (op1_lvl_mdl.value() * vel / 127.0) + : 63 - op1_lvl_mdl.value(); + // Velocity calculation, some kind of approximation // Only calculate for operator 1 if in adding mode, don't want to change timbre - if( fm_mdl.value() == false ) { - vel_adjusted = 63 - ( op1_lvl_mdl.value() * vel/127.0) ; - } else { - vel_adjusted = 63 - op1_lvl_mdl.value(); - } theEmulator->write(0x40+adlib_opadd[voice], ( (int)op1_scale_mdl.value() & 0x03 << 6) + ( vel_adjusted & 0x3f ) ); @@ -297,66 +295,60 @@ int OpulenzInstrument::pushVoice(int v) { bool OpulenzInstrument::handleMidiEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset ) { emulatorMutex.lock(); - int key, vel, voice, tmp_pb; - switch(event.type()) { - case MidiNoteOn: - key = event.key(); - vel = event.velocity(); - - voice = popVoice(); - if( voice != OPL2_NO_VOICE ) { + int key = event.key(); + int vel = event.velocity(); + switch (event.type()) + { + case MidiNoteOn: + if (int voice = popVoice(); voice != OPL2_NO_VOICE) + { // Turn voice on, NB! the frequencies are straight by voice number, // not by the adlib_opadd table! - theEmulator->write(0xA0+voice, fnums[key] & 0xff); - theEmulator->write(0xB0+voice, 32 + ((fnums[key] & 0x1f00) >> 8) ); + theEmulator->write(0xA0 + voice, fnums[key] & 0xff); + theEmulator->write(0xB0 + voice, 32 + ((fnums[key] & 0x1f00) >> 8)); setVoiceVelocity(voice, vel); voiceNote[voice] = key; velocities[key] = vel; } - break; - case MidiNoteOff: - key = event.key(); - for(voice=0; voicewrite(0xA0+voice, fnums[key] & 0xff); - theEmulator->write(0xB0+voice, (fnums[key] & 0x1f00) >> 8 ); - voiceNote[voice] |= OPL2_VOICE_FREE; + break; + case MidiNoteOff: + for (int voice = 0; voice < OPL2_VOICES; ++voice) + { + if (voiceNote[voice] == key) + { + theEmulator->write(0xA0 + voice, fnums[key] & 0xff); + theEmulator->write(0xB0 + voice, (fnums[key] & 0x1f00) >> 8); + voiceNote[voice] |= OPL2_VOICE_FREE; pushVoice(voice); - } - } - velocities[key] = 0; - break; - case MidiKeyPressure: - key = event.key(); - vel = event.velocity(); - if( velocities[key] != 0) { - velocities[key] = vel; - } - for(voice=0; voicewrite(0xA0+v, fnums[vn] & 0xff); - theEmulator->write(0xB0+v, (playing ? 32 : 0) + ((fnums[vn] & 0x1f00) >> 8) ); - } - break; + theEmulator->write(0xA0 + v, fnums[vn] & 0xff); + theEmulator->write(0xB0 + v, (playing ? 32 : 0) + ((fnums[vn] & 0x1f00) >> 8)); + } + break; case MidiControlChange: switch (event.controllerNumber()) { case MidiControllerRegisteredParameterNumberLSB: @@ -382,7 +374,7 @@ bool OpulenzInstrument::handleMidiEvent( const MidiEvent& event, const TimePos& printf("Midi event type %d\n",event.type()); #endif break; - } + } emulatorMutex.unlock(); return true; } @@ -398,7 +390,7 @@ gui::PluginView* OpulenzInstrument::instantiateView( QWidget * _parent ) } -void OpulenzInstrument::play( sampleFrame * _working_buffer ) +void OpulenzInstrument::play( SampleFrame* _working_buffer ) { emulatorMutex.lock(); theEmulator->update(renderbuffer, frameCount); @@ -504,9 +496,8 @@ void OpulenzInstrument::loadPatch(const unsigned char inst[14]) { } void OpulenzInstrument::tuneEqual(int center, float Hz) { - float tmp; for(int n=0; n<128; ++n) { - tmp = Hz*pow( 2.0, ( n - center ) * ( 1.0 / 12.0 ) + pitchbend * ( 1.0 / 1200.0 ) ); + float tmp = Hz * pow(2.0, (n - center) * (1.0 / 12.0) + pitchbend * (1.0 / 1200.0)); fnums[n] = Hz2fnum( tmp ); } } @@ -779,15 +770,15 @@ void OpulenzInstrumentView::updateKnobHints() // Envelope times in ms: t[0] = 0, t[n] = ( 1<{ - 0.0, 0.2, 0.4, 0.9, 1.8, 3.7, 7.4, - 15.0, 30.0, 60.0, 120.0, 240.0, 480.0, - 950.0, 1900.0, 3800.0 + 0.f, 0.2f, 0.4f, 0.9f, 1.8f, 3.7f, 7.4f, + 15.f, 30.f, 60.f, 120.f, 240.f, 480.f, + 950.f, 1900.f, 3800.f }; const auto dr_times = std::array{ - 0.0, 1.2, 2.5, 5.0, 10.0, 20.0, 40.0, - 80.0, 160.0, 320.0, 640.0, 1300.0, 2600.0, - 5200.0, 10000.0, 20000.0 + 0.f, 1.2f, 2.5f, 5.f, 10.f, 20.f, 40.f, + 80.f, 160.f, 320.f, 640.f, 1300.f, 2600.f, + 5200.f, 10000.f, 20000.f }; const auto fmultipliers = std::array{ diff --git a/plugins/OpulenZ/OpulenZ.h b/plugins/OpulenZ/OpulenZ.h index a3e11a6c0fc..bd51c9cf507 100644 --- a/plugins/OpulenZ/OpulenZ.h +++ b/plugins/OpulenZ/OpulenZ.h @@ -64,13 +64,8 @@ class OpulenzInstrument : public Instrument QString nodeName() const override; gui::PluginView* instantiateView( QWidget * _parent ) override; - Flags flags() const override - { - return Flag::IsSingleStreamed | Flag::IsMidiBased; - } - bool handleMidiEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset = 0 ) override; - void play( sampleFrame * _working_buffer ) override; + void play( SampleFrame* _working_buffer ) override; void saveSettings( QDomDocument & _doc, QDomElement & _this ) override; void loadSettings( const QDomElement & _this ) override; diff --git a/plugins/Organic/Organic.cpp b/plugins/Organic/Organic.cpp index 2dba6362931..c167bcec259 100644 --- a/plugins/Organic/Organic.cpp +++ b/plugins/Organic/Organic.cpp @@ -221,7 +221,7 @@ QString OrganicInstrument::nodeName() const void OrganicInstrument::playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) + SampleFrame* _working_buffer ) { const fpp_t frames = _n->framesLeftForCurrentPeriod(); const f_cnt_t offset = _n->noteOffset(); @@ -302,7 +302,7 @@ void OrganicInstrument::playNote( NotePlayHandle * _n, // fxKnob is [0;1] float t = m_fx1Model.value(); - for (int i=0 ; i < frames + offset ; i++) + for (auto i = std::size_t{0}; i < frames + offset; i++) { _working_buffer[i][0] = waveshape( _working_buffer[i][0], t ) * m_volModel.value() / 100.0f; @@ -605,10 +605,10 @@ void OscillatorObject::updateDetuning() { m_detuningLeft = powf( 2.0f, OrganicInstrument::s_harmonics[ static_cast( m_harmModel.value() ) ] + (float)m_detuneModel.value() * CENT ) / - Engine::audioEngine()->processingSampleRate(); + Engine::audioEngine()->outputSampleRate(); m_detuningRight = powf( 2.0f, OrganicInstrument::s_harmonics[ static_cast( m_harmModel.value() ) ] - (float)m_detuneModel.value() * CENT ) / - Engine::audioEngine()->processingSampleRate(); + Engine::audioEngine()->outputSampleRate(); } diff --git a/plugins/Organic/Organic.h b/plugins/Organic/Organic.h index e50550e5e2d..5a066af433b 100644 --- a/plugins/Organic/Organic.h +++ b/plugins/Organic/Organic.h @@ -125,7 +125,7 @@ class OrganicInstrument : public Instrument ~OrganicInstrument() override; void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; + SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; diff --git a/plugins/Patman/Patman.cpp b/plugins/Patman/Patman.cpp index e525498a5ee..6165bd53785 100644 --- a/plugins/Patman/Patman.cpp +++ b/plugins/Patman/Patman.cpp @@ -134,7 +134,7 @@ QString PatmanInstrument::nodeName() const void PatmanInstrument::playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) + SampleFrame* _working_buffer ) { if( m_patchFile == "" ) { @@ -160,7 +160,7 @@ void PatmanInstrument::playNote( NotePlayHandle * _n, } else { - memset( _working_buffer, 0, ( frames + offset ) * sizeof( sampleFrame ) ); + zeroSampleFrames(_working_buffer, frames + offset); } } @@ -298,17 +298,16 @@ PatmanInstrument::LoadError PatmanInstrument::loadPatch( SKIP_BYTES( 2 + 2 + 36 ); f_cnt_t frames; - sample_t * wave_samples; + std::unique_ptr wave_samples; if( modes & MODES_16BIT ) { frames = data_length >> 1; - wave_samples = new sample_t[frames]; + wave_samples = std::make_unique(frames); for( f_cnt_t frame = 0; frame < frames; ++frame ) { short sample; if ( fread( &sample, 2, 1, fd ) != 1 ) { - delete[] wave_samples; fclose( fd ); return( LoadError::IO ); } @@ -326,13 +325,12 @@ PatmanInstrument::LoadError PatmanInstrument::loadPatch( else { frames = data_length; - wave_samples = new sample_t[frames]; + wave_samples = std::make_unique(frames); for( f_cnt_t frame = 0; frame < frames; ++frame ) { char sample; if ( fread( &sample, 1, 1, fd ) != 1 ) { - delete[] wave_samples; fclose( fd ); return( LoadError::IO ); } @@ -344,7 +342,7 @@ PatmanInstrument::LoadError PatmanInstrument::loadPatch( } } - auto data = new sampleFrame[frames]; + auto data = new SampleFrame[frames]; for( f_cnt_t frame = 0; frame < frames; ++frame ) { @@ -366,7 +364,6 @@ PatmanInstrument::LoadError PatmanInstrument::loadPatch( m_patchSamples.push_back(psample); - delete[] wave_samples; delete[] data; } fclose( fd ); @@ -548,7 +545,7 @@ void PatmanView::updateFilename() m_displayFilename = ""; int idx = m_pi->m_patchFile.length(); - QFontMetrics fm( pointSize<8>( font() ) ); + QFontMetrics fm(adjustedToPixelSize(font(), 8)); // simple algorithm for creating a text from the filename that // matches in the white rectangle @@ -618,7 +615,7 @@ void PatmanView::paintEvent( QPaintEvent * ) { QPainter p( this ); - p.setFont( pointSize<8>( font() ) ); + p.setFont(adjustedToPixelSize(font() ,8)); p.drawText( 8, 116, 235, 16, Qt::AlignLeft | Qt::TextSingleLine | Qt::AlignVCenter, m_displayFilename ); diff --git a/plugins/Patman/Patman.h b/plugins/Patman/Patman.h index 486524522d4..a77c2607f2a 100644 --- a/plugins/Patman/Patman.h +++ b/plugins/Patman/Patman.h @@ -60,7 +60,7 @@ class PatmanInstrument : public Instrument ~PatmanInstrument() override; void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; + SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; @@ -71,9 +71,9 @@ class PatmanInstrument : public Instrument QString nodeName() const override; - f_cnt_t desiredReleaseFrames() const override + float desiredReleaseTimeMs() const override { - return( 128 ); + return 3.f; } gui::PluginView* instantiateView( QWidget * _parent ) override; diff --git a/plugins/PeakControllerEffect/PeakControllerEffect.cpp b/plugins/PeakControllerEffect/PeakControllerEffect.cpp index 7aff6f8033e..886036095f8 100644 --- a/plugins/PeakControllerEffect/PeakControllerEffect.cpp +++ b/plugins/PeakControllerEffect/PeakControllerEffect.cpp @@ -93,7 +93,7 @@ PeakControllerEffect::~PeakControllerEffect() } -bool PeakControllerEffect::processAudioBuffer( sampleFrame * _buf, +bool PeakControllerEffect::processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) { PeakControllerEffectControls & c = m_peakControls; @@ -110,7 +110,7 @@ bool PeakControllerEffect::processAudioBuffer( sampleFrame * _buf, if( c.m_absModel.value() ) { - for( int i = 0; i < _frames; ++i ) + for (auto i = std::size_t{0}; i < _frames; ++i) { // absolute value is achieved because the squares are > 0 sum += _buf[i][0]*_buf[i][0] + _buf[i][1]*_buf[i][1]; @@ -118,7 +118,7 @@ bool PeakControllerEffect::processAudioBuffer( sampleFrame * _buf, } else { - for( int i = 0; i < _frames; ++i ) + for (auto i = std::size_t{0}; i < _frames; ++i) { // the value is absolute because of squaring, // so we need to correct it @@ -131,7 +131,7 @@ bool PeakControllerEffect::processAudioBuffer( sampleFrame * _buf, // this will mute the output after the values were measured if( c.m_muteModel.value() ) { - for( int i = 0; i < _frames; ++i ) + for (auto i = std::size_t{0}; i < _frames; ++i) { _buf[i][0] = _buf[i][1] = 0.0f; } diff --git a/plugins/PeakControllerEffect/PeakControllerEffect.h b/plugins/PeakControllerEffect/PeakControllerEffect.h index f2bafeddaa5..dc6e507f351 100644 --- a/plugins/PeakControllerEffect/PeakControllerEffect.h +++ b/plugins/PeakControllerEffect/PeakControllerEffect.h @@ -41,7 +41,7 @@ class PeakControllerEffect : public Effect PeakControllerEffect( Model * parent, const Descriptor::SubPluginFeatures::Key * _key ); ~PeakControllerEffect() override; - bool processAudioBuffer( sampleFrame * _buf, + bool processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) override; EffectControls * controls() override diff --git a/plugins/PeakControllerEffect/PeakControllerEffectControls.cpp b/plugins/PeakControllerEffect/PeakControllerEffectControls.cpp index 070c9fbb8fd..0060b535b4b 100644 --- a/plugins/PeakControllerEffect/PeakControllerEffectControls.cpp +++ b/plugins/PeakControllerEffect/PeakControllerEffectControls.cpp @@ -38,14 +38,14 @@ PeakControllerEffectControls:: PeakControllerEffectControls( PeakControllerEffect * _eff ) : EffectControls( _eff ), m_effect( _eff ), - m_baseModel( 0.5, 0.0, 1.0, 0.001, this, tr( "Base value" ) ), - m_amountModel( 1.0, -1.0, 1.0, 0.005, this, tr( "Modulation amount" ) ), - m_attackModel( 0, 0, 0.999, 0.001, this, tr( "Attack" ) ), - m_decayModel( 0, 0, 0.999, 0.001, this, tr( "Release" ) ), - m_tresholdModel( 0, 0, 1.0, 0.001, this, tr( "Treshold" ) ), + m_baseModel(0.5f, 0.f, 1.f, 0.001f, this, tr("Base value")), + m_amountModel(1.f, -1.f, 1.f, 0.005f, this, tr("Modulation amount")), + m_attackModel(0, 0, 0.999f, 0.001f, this, tr("Attack")), + m_decayModel(0, 0, 0.999f, 0.001f, this, tr("Release")), + m_tresholdModel(0, 0, 1.f, 0.001f, this, tr("Treshold")), m_muteModel( false, this, tr( "Mute output" ) ), m_absModel( true, this, tr("Absolute value") ), - m_amountMultModel( 1.0, 0, 32, 0.2, this, tr("Amount multiplicator") ) + m_amountMultModel(1.f, 0, 32, 0.2f, this, tr("Amount multiplicator")) { } diff --git a/plugins/ReverbSC/ReverbSC.cpp b/plugins/ReverbSC/ReverbSC.cpp index 9006f8c9f21..2def88d1db9 100644 --- a/plugins/ReverbSC/ReverbSC.cpp +++ b/plugins/ReverbSC/ReverbSC.cpp @@ -55,7 +55,7 @@ ReverbSCEffect::ReverbSCEffect( Model* parent, const Descriptor::SubPluginFeatur m_reverbSCControls( this ) { sp_create(&sp); - sp->sr = Engine::audioEngine()->processingSampleRate(); + sp->sr = Engine::audioEngine()->outputSampleRate(); sp_revsc_create(&revsc); sp_revsc_init(sp, revsc); @@ -63,8 +63,8 @@ ReverbSCEffect::ReverbSCEffect( Model* parent, const Descriptor::SubPluginFeatur sp_dcblock_create(&dcblk[0]); sp_dcblock_create(&dcblk[1]); - sp_dcblock_init(sp, dcblk[0], Engine::audioEngine()->currentQualitySettings().sampleRateMultiplier() ); - sp_dcblock_init(sp, dcblk[1], Engine::audioEngine()->currentQualitySettings().sampleRateMultiplier() ); + sp_dcblock_init(sp, dcblk[0], 1); + sp_dcblock_init(sp, dcblk[1], 1); } ReverbSCEffect::~ReverbSCEffect() @@ -75,7 +75,7 @@ ReverbSCEffect::~ReverbSCEffect() sp_destroy(&sp); } -bool ReverbSCEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) +bool ReverbSCEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames ) { if( !isEnabled() || !isRunning () ) { @@ -132,7 +132,7 @@ bool ReverbSCEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) void ReverbSCEffect::changeSampleRate() { // Change sr variable in Soundpipe. does not need to be destroyed - sp->sr = Engine::audioEngine()->processingSampleRate(); + sp->sr = Engine::audioEngine()->outputSampleRate(); mutex.lock(); sp_revsc_destroy(&revsc); @@ -145,8 +145,8 @@ void ReverbSCEffect::changeSampleRate() sp_dcblock_create(&dcblk[0]); sp_dcblock_create(&dcblk[1]); - sp_dcblock_init(sp, dcblk[0], Engine::audioEngine()->currentQualitySettings().sampleRateMultiplier() ); - sp_dcblock_init(sp, dcblk[1], Engine::audioEngine()->currentQualitySettings().sampleRateMultiplier() ); + sp_dcblock_init(sp, dcblk[0], 1); + sp_dcblock_init(sp, dcblk[1], 1); mutex.unlock(); } diff --git a/plugins/ReverbSC/ReverbSC.h b/plugins/ReverbSC/ReverbSC.h index 2cb119bff23..f3c196f5bf8 100644 --- a/plugins/ReverbSC/ReverbSC.h +++ b/plugins/ReverbSC/ReverbSC.h @@ -45,7 +45,7 @@ class ReverbSCEffect : public Effect public: ReverbSCEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ); ~ReverbSCEffect() override; - bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ) override; + bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override; EffectControls* controls() override { diff --git a/plugins/ReverbSC/base.c b/plugins/ReverbSC/base.c index c8d1dff679e..d5c52b0bd1a 100644 --- a/plugins/ReverbSC/base.c +++ b/plugins/ReverbSC/base.c @@ -189,7 +189,7 @@ int sp_set(sp_param *p, SPFLOAT val) { int sp_out(sp_data *sp, uint32_t chan, SPFLOAT val) { - if(chan > sp->nchan - 1) { + if (chan >= (uint32_t) sp->nchan) { fprintf(stderr, "sp_out: Invalid channel\n"); return SP_NOT_OK; } diff --git a/plugins/ReverbSC/dcblock.c b/plugins/ReverbSC/dcblock.c index f38bb73dca5..2c8d86950fd 100644 --- a/plugins/ReverbSC/dcblock.c +++ b/plugins/ReverbSC/dcblock.c @@ -33,7 +33,7 @@ int sp_dcblock_init(sp_data *sp, sp_dcblock *p, int oversampling ) p->inputs = 0.0; p->gain = pow( 0.99, 1.0f / oversampling ); if (p->gain == 0.0 || p->gain>=1.0 || p->gain<=-1.0) - p->gain = 0.99; + p->gain = 0.99f; return SP_OK; } diff --git a/plugins/ReverbSC/revsc.c b/plugins/ReverbSC/revsc.c index d9808963f8e..54db9a4db8c 100644 --- a/plugins/ReverbSC/revsc.c +++ b/plugins/ReverbSC/revsc.c @@ -16,7 +16,7 @@ #include "base.h" #include "revsc.h" -#define DEFAULT_SRATE 44100.0 +#define DEFAULT_SRATE 44100.f #define MIN_SRATE 5000.0 #define MAX_SRATE 1000000.0 #define MAX_PITCHMOD 20.0 @@ -34,20 +34,20 @@ /* reverbParams[n][3] = random seed (0 - 32767) */ static const SPFLOAT reverbParams[8][4] = { - { (2473.0 / DEFAULT_SRATE), 0.0010, 3.100, 1966.0 }, - { (2767.0 / DEFAULT_SRATE), 0.0011, 3.500, 29491.0 }, - { (3217.0 / DEFAULT_SRATE), 0.0017, 1.110, 22937.0 }, - { (3557.0 / DEFAULT_SRATE), 0.0006, 3.973, 9830.0 }, - { (3907.0 / DEFAULT_SRATE), 0.0010, 2.341, 20643.0 }, - { (4127.0 / DEFAULT_SRATE), 0.0011, 1.897, 22937.0 }, - { (2143.0 / DEFAULT_SRATE), 0.0017, 0.891, 29491.0 }, - { (1933.0 / DEFAULT_SRATE), 0.0006, 3.221, 14417.0 } + { (2473.f / DEFAULT_SRATE), 0.0010f, 3.100f, 1966.f }, + { (2767.f / DEFAULT_SRATE), 0.0011f, 3.500f, 29491.f }, + { (3217.f / DEFAULT_SRATE), 0.0017f, 1.110f, 22937.f }, + { (3557.f / DEFAULT_SRATE), 0.0006f, 3.973f, 9830.f }, + { (3907.f / DEFAULT_SRATE), 0.0010f, 2.341f, 20643.f }, + { (4127.f / DEFAULT_SRATE), 0.0011f, 1.897f, 22937.f }, + { (2143.f / DEFAULT_SRATE), 0.0017f, 0.891f, 29491.f }, + { (1933.f / DEFAULT_SRATE), 0.0006f, 3.221f, 14417.f } }; static int delay_line_max_samples(SPFLOAT sr, SPFLOAT iPitchMod, int n); static int init_delay_line(sp_revsc *p, sp_revsc_dl *lp, int n); static int delay_line_bytes_alloc(SPFLOAT sr, SPFLOAT iPitchMod, int n); -static const SPFLOAT outputGain = 0.35; +static const SPFLOAT outputGain = 0.35f; static const SPFLOAT jpScale = 0.25; int sp_revsc_create(sp_revsc **p){ *p = malloc(sizeof(sp_revsc)); @@ -56,9 +56,9 @@ int sp_revsc_create(sp_revsc **p){ int sp_revsc_init(sp_data *sp, sp_revsc *p) { - p->iSampleRate = sp->sr; - p->sampleRate = sp->sr; - p->feedback = 0.97; + p->iSampleRate = (float) sp->sr; + p->sampleRate = (float) sp->sr; + p->feedback = 0.97f; p->lpfreq = 10000; p->iPitchMod = 1; p->iSkipInit = 0; @@ -67,14 +67,14 @@ int sp_revsc_init(sp_data *sp, sp_revsc *p) p->initDone = 1; int i, nBytes = 0; for(i = 0; i < 8; i++){ - nBytes += delay_line_bytes_alloc(sp->sr, 1, i); + nBytes += delay_line_bytes_alloc((float) sp->sr, 1, i); } sp_auxdata_alloc(&p->aux, nBytes); nBytes = 0; for (i = 0; i < 8; i++) { p->delayLines[i].buf = (SPFLOAT*) (((char*) p->aux.ptr) + nBytes); init_delay_line(p, &p->delayLines[i], i); - nBytes += delay_line_bytes_alloc(sp->sr, 1, i); + nBytes += delay_line_bytes_alloc((float) sp->sr, 1, i); } return SP_OK; @@ -217,7 +217,7 @@ int sp_revsc_compute(sp_data *sp, sp_revsc *p, SPFLOAT *in1, SPFLOAT *in2, SPFLO /* calculate interpolation coefficients */ - a2 = frac * frac; a2 -= 1.0; a2 *= (1.0 / 6.0); + a2 = frac * frac; a2 -= 1.0; a2 *= (1.f / 6.f); a1 = frac; a1 += 1.0; a1 *= 0.5; am1 = a1 - 1.0; a0 = 3.0 * a2; a1 -= a0; am1 -= a2; a0 -= frac; diff --git a/plugins/Sf2Player/CMakeLists.txt b/plugins/Sf2Player/CMakeLists.txt index 1d004a6c595..464b8bd3e4b 100644 --- a/plugins/Sf2Player/CMakeLists.txt +++ b/plugins/Sf2Player/CMakeLists.txt @@ -1,9 +1,9 @@ if(LMMS_HAVE_FLUIDSYNTH) + SET(CMAKE_AUTOUIC ON) include(BuildPlugin) build_plugin(sf2player Sf2Player.cpp Sf2Player.h PatchesDialog.cpp PatchesDialog.h PatchesDialog.ui MOCFILES Sf2Player.h PatchesDialog.h - UICFILES PatchesDialog.ui EMBEDDED_RESOURCES *.png ) target_link_libraries(sf2player fluidsynth SampleRate::samplerate) diff --git a/plugins/Sf2Player/Sf2Player.cpp b/plugins/Sf2Player/Sf2Player.cpp index 7795671c55e..13dab7b8abd 100644 --- a/plugins/Sf2Player/Sf2Player.cpp +++ b/plugins/Sf2Player/Sf2Player.cpp @@ -122,7 +122,7 @@ struct Sf2PluginData Sf2Instrument::Sf2Instrument( InstrumentTrack * _instrument_track ) : - Instrument( _instrument_track, &sf2player_plugin_descriptor ), + Instrument(_instrument_track, &sf2player_plugin_descriptor, nullptr, Flag::IsSingleStreamed), m_srcState( nullptr ), m_synth(nullptr), m_font( nullptr ), @@ -136,14 +136,14 @@ Sf2Instrument::Sf2Instrument( InstrumentTrack * _instrument_track ) : m_gain( 1.0f, 0.0f, 5.0f, 0.01f, this, tr( "Gain" ) ), m_reverbOn( false, this, tr( "Reverb" ) ), m_reverbRoomSize( FLUID_REVERB_DEFAULT_ROOMSIZE, 0, 1.0, 0.01f, this, tr( "Reverb room size" ) ), - m_reverbDamping( FLUID_REVERB_DEFAULT_DAMP, 0, 1.0, 0.01, this, tr( "Reverb damping" ) ), + m_reverbDamping(FLUID_REVERB_DEFAULT_DAMP, 0, 1.f, 0.01f, this, tr("Reverb damping")), m_reverbWidth( FLUID_REVERB_DEFAULT_WIDTH, 0, 1.0, 0.01f, this, tr( "Reverb width" ) ), m_reverbLevel( FLUID_REVERB_DEFAULT_LEVEL, 0, 1.0, 0.01f, this, tr( "Reverb level" ) ), m_chorusOn( false, this, tr( "Chorus" ) ), m_chorusNum( FLUID_CHORUS_DEFAULT_N, 0, 10.0, 1.0, this, tr( "Chorus voices" ) ), - m_chorusLevel( FLUID_CHORUS_DEFAULT_LEVEL, 0, 10.0, 0.01, this, tr( "Chorus level" ) ), - m_chorusSpeed( FLUID_CHORUS_DEFAULT_SPEED, 0.29, 5.0, 0.01, this, tr( "Chorus speed" ) ), - m_chorusDepth( FLUID_CHORUS_DEFAULT_DEPTH, 0, 46.0, 0.05, this, tr( "Chorus depth" ) ) + m_chorusLevel(FLUID_CHORUS_DEFAULT_LEVEL, 0, 10.f, 0.01f, this, tr("Chorus level")), + m_chorusSpeed(FLUID_CHORUS_DEFAULT_SPEED, 0.29f, 5.f, 0.01f, this, tr("Chorus speed")), + m_chorusDepth(FLUID_CHORUS_DEFAULT_DEPTH, 0, 46.f, 0.05f, this, tr("Chorus depth")) { @@ -499,44 +499,58 @@ void Sf2Instrument::updateGain() fluid_synth_set_gain( m_synth, m_gain.value() ); } - - +#define FLUIDSYNTH_VERSION_HEX ((FLUIDSYNTH_VERSION_MAJOR << 16) \ + | (FLUIDSYNTH_VERSION_MINOR << 8) \ + | FLUIDSYNTH_VERSION_MICRO) +#define USE_NEW_EFFECT_API (FLUIDSYNTH_VERSION_HEX >= 0x020200) void Sf2Instrument::updateReverbOn() { - fluid_synth_set_reverb_on( m_synth, m_reverbOn.value() ? 1 : 0 ); +#if USE_NEW_EFFECT_API + fluid_synth_reverb_on(m_synth, -1, m_reverbOn.value() ? 1 : 0); +#else + fluid_synth_set_reverb_on(m_synth, m_reverbOn.value() ? 1 : 0); +#endif } - - - void Sf2Instrument::updateReverb() { - fluid_synth_set_reverb( m_synth, m_reverbRoomSize.value(), +#if USE_NEW_EFFECT_API + fluid_synth_set_reverb_group_roomsize(m_synth, -1, m_reverbRoomSize.value()); + fluid_synth_set_reverb_group_damp(m_synth, -1, m_reverbDamping.value()); + fluid_synth_set_reverb_group_width(m_synth, -1, m_reverbWidth.value()); + fluid_synth_set_reverb_group_level(m_synth, -1, m_reverbLevel.value()); +#else + fluid_synth_set_reverb(m_synth, m_reverbRoomSize.value(), m_reverbDamping.value(), m_reverbWidth.value(), - m_reverbLevel.value() ); + m_reverbLevel.value()); +#endif } - - - -void Sf2Instrument::updateChorusOn() +void Sf2Instrument::updateChorusOn() { - fluid_synth_set_chorus_on( m_synth, m_chorusOn.value() ? 1 : 0 ); +#if USE_NEW_EFFECT_API + fluid_synth_chorus_on(m_synth, -1, m_chorusOn.value() ? 1 : 0); +#else + fluid_synth_set_chorus_on(m_synth, m_chorusOn.value() ? 1 : 0); +#endif } - - - -void Sf2Instrument::updateChorus() +void Sf2Instrument::updateChorus() { - fluid_synth_set_chorus( m_synth, static_cast( m_chorusNum.value() ), +#if USE_NEW_EFFECT_API + fluid_synth_set_chorus_group_nr(m_synth, -1, static_cast(m_chorusNum.value())); + fluid_synth_set_chorus_group_level(m_synth, -1, m_chorusLevel.value()); + fluid_synth_set_chorus_group_speed(m_synth, -1, m_chorusSpeed.value()); + fluid_synth_set_chorus_group_depth(m_synth, -1, m_chorusDepth.value()); + fluid_synth_set_chorus_group_type(m_synth, -1, FLUID_CHORUS_MOD_SINE); +#else + fluid_synth_set_chorus(m_synth, static_cast(m_chorusNum.value()), m_chorusLevel.value(), m_chorusSpeed.value(), - m_chorusDepth.value(), 0 ); + m_chorusDepth.value(), FLUID_CHORUS_MOD_SINE); +#endif } - - void Sf2Instrument::updateTuning() { if (instrumentTrack()->microtuner()->enabledModel()->value()) @@ -574,7 +588,7 @@ void Sf2Instrument::reloadSynth() double tempRate; // Set & get, returns the true sample rate - fluid_settings_setnum( m_settings, (char *) "synth.sample-rate", Engine::audioEngine()->processingSampleRate() ); + fluid_settings_setnum( m_settings, (char *) "synth.sample-rate", Engine::audioEngine()->outputSampleRate() ); fluid_settings_getnum( m_settings, (char *) "synth.sample-rate", &tempRate ); m_internalSampleRate = static_cast( tempRate ); @@ -616,7 +630,7 @@ void Sf2Instrument::reloadSynth() fluid_synth_set_interp_method( m_synth, -1, FLUID_INTERP_DEFAULT ); } m_synthMutex.unlock(); - if( m_internalSampleRate < Engine::audioEngine()->processingSampleRate() ) + if( m_internalSampleRate < Engine::audioEngine()->outputSampleRate() ) { m_synthMutex.lock(); if( m_srcState != nullptr ) @@ -647,7 +661,7 @@ void Sf2Instrument::reloadSynth() -void Sf2Instrument::playNote( NotePlayHandle * _n, sampleFrame * ) +void Sf2Instrument::playNote( NotePlayHandle * _n, SampleFrame* ) { if( _n->isMasterNote() || ( _n->hasParent() && _n->isReleased() ) ) { @@ -782,7 +796,7 @@ void Sf2Instrument::noteOff( Sf2PluginData * n ) } -void Sf2Instrument::play( sampleFrame * _working_buffer ) +void Sf2Instrument::play( SampleFrame* _working_buffer ) { const fpp_t frames = Engine::audioEngine()->framesPerPeriod(); @@ -868,18 +882,18 @@ void Sf2Instrument::play( sampleFrame * _working_buffer ) } -void Sf2Instrument::renderFrames( f_cnt_t frames, sampleFrame * buf ) +void Sf2Instrument::renderFrames( f_cnt_t frames, SampleFrame* buf ) { m_synthMutex.lock(); fluid_synth_get_gain(m_synth); // This flushes voice updates as a side effect - if( m_internalSampleRate < Engine::audioEngine()->processingSampleRate() && + if( m_internalSampleRate < Engine::audioEngine()->outputSampleRate() && m_srcState != nullptr ) { - const fpp_t f = frames * m_internalSampleRate / Engine::audioEngine()->processingSampleRate(); + const fpp_t f = frames * m_internalSampleRate / Engine::audioEngine()->outputSampleRate(); #ifdef __GNUC__ - sampleFrame tmp[f]; + SampleFrame tmp[f]; #else - sampleFrame * tmp = new sampleFrame[f]; + SampleFrame* tmp = new SampleFrame[f]; #endif fluid_synth_write_float( m_synth, f, tmp, 0, 2, tmp, 1, 2 ); @@ -898,9 +912,9 @@ void Sf2Instrument::renderFrames( f_cnt_t frames, sampleFrame * buf ) { qCritical( "Sf2Instrument: error while resampling: %s", src_strerror( error ) ); } - if( src_data.output_frames_gen > frames ) + if (static_cast(src_data.output_frames_gen) < frames) { - qCritical( "Sf2Instrument: not enough frames: %ld / %d", src_data.output_frames_gen, frames ); + qCritical("Sf2Instrument: not enough frames: %ld / %zu", src_data.output_frames_gen, frames); } } else diff --git a/plugins/Sf2Player/Sf2Player.h b/plugins/Sf2Player/Sf2Player.h index 1af370e05eb..b63dd48e988 100644 --- a/plugins/Sf2Player/Sf2Player.h +++ b/plugins/Sf2Player/Sf2Player.h @@ -27,6 +27,7 @@ #ifndef SF2_PLAYER_H #define SF2_PLAYER_H +#include #include #include #include @@ -64,10 +65,10 @@ class Sf2Instrument : public Instrument Sf2Instrument( InstrumentTrack * _instrument_track ); ~Sf2Instrument() override; - void play( sampleFrame * _working_buffer ) override; + void play( SampleFrame* _working_buffer ) override; void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; + SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; @@ -80,16 +81,6 @@ class Sf2Instrument : public Instrument QString nodeName() const override; - f_cnt_t desiredReleaseFrames() const override - { - return 0; - } - - Flags flags() const override - { - return Flag::IsSingleStreamed; - } - gui::PluginView* instantiateView( QWidget * _parent ) override; QString getCurrentPatchName(); @@ -160,7 +151,7 @@ public slots: void freeFont(); void noteOn( Sf2PluginData * n ); void noteOff( Sf2PluginData * n ); - void renderFrames( f_cnt_t frames, sampleFrame * buf ); + void renderFrames( f_cnt_t frames, SampleFrame* buf ); friend class gui::Sf2InstrumentView; diff --git a/plugins/Sfxr/Sfxr.cpp b/plugins/Sfxr/Sfxr.cpp index e79b8e2adbe..0279eb41a3b 100644 --- a/plugins/Sfxr/Sfxr.cpp +++ b/plugins/Sfxr/Sfxr.cpp @@ -157,7 +157,7 @@ void SfxrSynth::resetSample( bool restart ) -void SfxrSynth::update( sampleFrame * buffer, const int32_t frameNum ) +void SfxrSynth::update( SampleFrame* buffer, const int32_t frameNum ) { for(int i=0;iprocessingSampleRate(); + float currentSampleRate = Engine::audioEngine()->outputSampleRate(); fpp_t frameNum = _n->framesLeftForCurrentPeriod(); const f_cnt_t offset = _n->noteOffset(); @@ -454,7 +454,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); + zeroSampleFrames(_working_buffer + offset, frameNum); _n->noteOff(); return; } @@ -467,7 +467,7 @@ void SfxrInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffe // debug code // qDebug( "pFN %d", pitchedFrameNum ); - auto pitchedBuffer = new sampleFrame[pitchedFrameNum]; + auto pitchedBuffer = new SampleFrame[pitchedFrameNum]; static_cast(_n->m_pluginData)->update( pitchedBuffer, pitchedFrameNum ); for( fpp_t i=0; i + #include "AutomatableModel.h" #include "Instrument.h" #include "InstrumentView.h" @@ -82,7 +84,7 @@ class SfxrSynth virtual ~SfxrSynth() = default; void resetSample( bool restart ); - void update( sampleFrame * buffer, const int32_t frameNum ); + void update( SampleFrame* buffer, const int32_t frameNum ); bool isPlaying() const; @@ -135,7 +137,7 @@ class SfxrZeroToOneFloatModel : public FloatModel { public: SfxrZeroToOneFloatModel( float val, Model * parent, const QString& displayName ): - FloatModel( val, 0.0, 1.0, 0.001, parent, displayName ) + FloatModel(val, 0.f, 1.f, 0.001f, parent, displayName) { } /* purpose: prevent the initial value of the model from being changed */ @@ -156,7 +158,7 @@ class SfxrNegPosOneFloatModel : public FloatModel { public: SfxrNegPosOneFloatModel(float val, Model * parent, const QString& displayName ): - FloatModel( val, -1.0, 1.0, 0.001, parent, displayName ) + FloatModel(val, -1.f, 1.f, 0.001f, parent, displayName) { } /* purpose: prevent the initial value of the model from being changed */ @@ -177,7 +179,7 @@ class SfxrInstrument : public Instrument SfxrInstrument(InstrumentTrack * _instrument_track ); ~SfxrInstrument() override = default; - void playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) override; + void playNote( NotePlayHandle * _n, SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; void saveSettings( QDomDocument & _doc, diff --git a/plugins/Sid/SidInstrument.cpp b/plugins/Sid/SidInstrument.cpp index 7f9edf13f18..4d21abd4dea 100644 --- a/plugins/Sid/SidInstrument.cpp +++ b/plugins/Sid/SidInstrument.cpp @@ -29,7 +29,7 @@ #include #include -#include "sid.h" +#include #include "SidInstrument.h" #include "AudioEngine.h" @@ -221,44 +221,35 @@ QString SidInstrument::nodeName() const } - - -f_cnt_t SidInstrument::desiredReleaseFrames() const +float SidInstrument::desiredReleaseTimeMs() const { - const float samplerate = Engine::audioEngine()->processingSampleRate(); int maxrel = 0; for (const auto& voice : m_voice) { - if( maxrel < voice->m_releaseModel.value() ) - maxrel = (int)voice->m_releaseModel.value(); + maxrel = std::max(maxrel, static_cast(voice->m_releaseModel.value())); } - return f_cnt_t( float(relTime[maxrel])*samplerate/1000.0 ); + return computeReleaseTimeMsByFrameCount(relTime[maxrel]); } - - static int sid_fillbuffer(unsigned char* sidreg, reSID::SID *sid, int tdelta, short *ptr, int samples) { - int tdelta2; - int result; int total = 0; - int c; // customly added int residdelay = 0; int badline = rand() % NUMSIDREGS; - for (c = 0; c < NUMSIDREGS; c++) + for (int c = 0; c < NUMSIDREGS; c++) { unsigned char o = sidorder[c]; // Extra delay for loading the waveform (and mt_chngate,x) if ((o == 4) || (o == 11) || (o == 18)) { - tdelta2 = SIDWAVEDELAY; - result = sid->clock(tdelta2, ptr, samples); + int tdelta2 = SIDWAVEDELAY; + int result = sid->clock(tdelta2, ptr, samples); total += result; ptr += result; samples -= result; @@ -268,8 +259,8 @@ static int sid_fillbuffer(unsigned char* sidreg, reSID::SID *sid, int tdelta, sh // Possible random badline delay once per writing if ((badline == c) && (residdelay)) { - tdelta2 = residdelay; - result = sid->clock(tdelta2, ptr, samples); + int tdelta2 = residdelay; + int result = sid->clock(tdelta2, ptr, samples); total += result; ptr += result; samples -= result; @@ -278,14 +269,14 @@ static int sid_fillbuffer(unsigned char* sidreg, reSID::SID *sid, int tdelta, sh sid->write(o, sidreg[o]); - tdelta2 = SIDWRITEDELAY; - result = sid->clock(tdelta2, ptr, samples); + int tdelta2 = SIDWRITEDELAY; + int result = sid->clock(tdelta2, ptr, samples); total += result; ptr += result; samples -= result; tdelta -= SIDWRITEDELAY; } - result = sid->clock(tdelta, ptr, samples); + int result = sid->clock(tdelta, ptr, samples); total += result; return total; @@ -295,10 +286,10 @@ static int sid_fillbuffer(unsigned char* sidreg, reSID::SID *sid, int tdelta, sh void SidInstrument::playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) + SampleFrame* _working_buffer ) { const int clockrate = C64_PAL_CYCLES_PER_SEC; - const int samplerate = Engine::audioEngine()->processingSampleRate(); + const int samplerate = Engine::audioEngine()->outputSampleRate(); if (!_n->m_pluginData) { @@ -314,8 +305,11 @@ void SidInstrument::playNote( NotePlayHandle * _n, auto sid = static_cast(_n->m_pluginData); int delta_t = clockrate * frames / samplerate + 4; - // avoid variable length array for msvc compat - auto buf = reinterpret_cast(_working_buffer + offset); +#ifndef _MSC_VER + short buf[frames]; +#else + const auto buf = static_cast(_alloca(frames * sizeof(short))); +#endif auto sidreg = std::array{}; for (auto& reg : sidreg) @@ -416,12 +410,13 @@ void SidInstrument::playNote( NotePlayHandle * _n, sidreg[24] = data8&0x00FF; - int num = sid_fillbuffer(sidreg.data(), sid, delta_t, buf, frames); - if(num!=frames) + const auto num = static_cast(sid_fillbuffer(sidreg.data(), sid, delta_t, buf, frames)); + if (num != frames) { printf("!!!Not enough samples\n"); + } // loop backwards to avoid overwriting data in the short-to-float conversion - for( fpp_t frame = frames - 1; frame >= 0; frame-- ) + for (auto frame = std::size_t{0}; frame < frames; ++frame) { sample_t s = float(buf[frame])/32768.0; for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch ) diff --git a/plugins/Sid/SidInstrument.h b/plugins/Sid/SidInstrument.h index 1a133b58b1b..79ad7f32abf 100644 --- a/plugins/Sid/SidInstrument.h +++ b/plugins/Sid/SidInstrument.h @@ -102,7 +102,7 @@ class SidInstrument : public Instrument ~SidInstrument() override = default; void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; + SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; @@ -111,7 +111,7 @@ class SidInstrument : public Instrument QString nodeName() const override; - f_cnt_t desiredReleaseFrames() const override; + float desiredReleaseTimeMs() const override; gui::PluginView* instantiateView( QWidget * _parent ) override; diff --git a/plugins/Sid/resid/CMakeLists.txt b/plugins/Sid/resid/CMakeLists.txt index bb39e3d166f..e882679a88c 100644 --- a/plugins/Sid/resid/CMakeLists.txt +++ b/plugins/Sid/resid/CMakeLists.txt @@ -1,3 +1,5 @@ +set_directory_properties(PROPERTIES SYSTEM TRUE) + # These are the defaults set(RESID_INLINING 1) set(RESID_INLINE inline) diff --git a/plugins/SlicerT/CMakeLists.txt b/plugins/SlicerT/CMakeLists.txt index 49a80ca03aa..fa7ce3c11a1 100644 --- a/plugins/SlicerT/CMakeLists.txt +++ b/plugins/SlicerT/CMakeLists.txt @@ -1,10 +1,16 @@ -INCLUDE(BuildPlugin) +include(BuildPlugin) -INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) -LINK_LIBRARIES(${FFTW3F_LIBRARIES}) +include_directories(SYSTEM ${FFTW3F_INCLUDE_DIRS}) +link_libraries(${FFTW3F_LIBRARIES}) -INCLUDE_DIRECTORIES(${SAMPLERATE_INCLUDE_DIRS}) -LINK_DIRECTORIES(${SAMPLERATE_LIBRARY_DIRS}) -LINK_LIBRARIES(${SAMPLERATE_LIBRARIES}) - -BUILD_PLUGIN(slicert SlicerT.cpp SlicerT.h SlicerTView.cpp SlicerTView.h SlicerTWaveform.cpp SlicerTWaveform.h MOCFILES SlicerT.h SlicerTView.h SlicerTWaveform.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") \ No newline at end of file +build_plugin(slicert + SlicerT.cpp + SlicerT.h + SlicerTView.cpp + SlicerTView.h + SlicerTWaveform.cpp + SlicerTWaveform.h + MOCFILES SlicerT.h SlicerTView.h SlicerTWaveform.h + EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png" +) +target_link_libraries(slicert SampleRate::samplerate) diff --git a/plugins/SlicerT/SlicerT.cpp b/plugins/SlicerT/SlicerT.cpp index c9e75df601e..dcfbf6bc551 100644 --- a/plugins/SlicerT/SlicerT.cpp +++ b/plugins/SlicerT/SlicerT.cpp @@ -75,7 +75,7 @@ SlicerT::SlicerT(InstrumentTrack* instrumentTrack) m_sliceSnap.setValue(0); } -void SlicerT::playNote(NotePlayHandle* handle, sampleFrame* workingBuffer) +void SlicerT::playNote(NotePlayHandle* handle, SampleFrame* workingBuffer) { if (m_originalSample.sampleSize() <= 1) { return; } @@ -88,7 +88,7 @@ void SlicerT::playNote(NotePlayHandle* handle, sampleFrame* workingBuffer) float speedRatio = static_cast(m_originalBPM.value()) / bpm; if (!m_enableSync.value()) { speedRatio = 1; } speedRatio *= pitchRatio; - speedRatio *= Engine::audioEngine()->processingSampleRate() / static_cast(m_originalSample.sampleRate()); + speedRatio *= Engine::audioEngine()->outputSampleRate() / static_cast(m_originalSample.sampleRate()); float sliceStart, sliceEnd; if (noteIndex == 0) // full sample at base note @@ -96,7 +96,7 @@ void SlicerT::playNote(NotePlayHandle* handle, sampleFrame* workingBuffer) sliceStart = 0; sliceEnd = 1; } - else if (noteIndex > 0 && noteIndex < m_slicePoints.size()) + else if (noteIndex > 0 && static_cast(noteIndex) < m_slicePoints.size()) { noteIndex -= 1; sliceStart = m_slicePoints[noteIndex]; @@ -132,11 +132,11 @@ void SlicerT::playNote(NotePlayHandle* handle, sampleFrame* workingBuffer) playbackState->setNoteDone(nextNoteDone); // exponential fade out, applyRelease() not used since it extends the note length - int fadeOutFrames = m_fadeOutFrames.value() / 1000.0f * Engine::audioEngine()->processingSampleRate(); + int fadeOutFrames = m_fadeOutFrames.value() / 1000.0f * Engine::audioEngine()->outputSampleRate(); int noteFramesLeft = noteLeft * m_originalSample.sampleSize() * speedRatio; - for (int i = 0; i < frames; i++) + for (auto i = std::size_t{0}; i < frames; i++) { - float fadeValue = static_cast(noteFramesLeft - i) / fadeOutFrames; + float fadeValue = static_cast(noteFramesLeft - static_cast(i)) / fadeOutFrames; fadeValue = std::clamp(fadeValue, 0.0f, 1.0f); fadeValue = cosinusInterpolate(0, 1, fadeValue); @@ -144,8 +144,6 @@ void SlicerT::playNote(NotePlayHandle* handle, sampleFrame* workingBuffer) workingBuffer[i + offset][1] *= fadeValue; } - instrumentTrack()->processAudioBuffer(workingBuffer, frames + offset, handle); - emit isPlaying(noteDone, sliceStart, sliceEnd); } else { emit isPlaying(-1, 0, 0); } @@ -172,7 +170,7 @@ void SlicerT::findSlices() float maxMag = -1; std::vector singleChannel(m_originalSample.sampleSize(), 0); - for (int i = 0; i < m_originalSample.sampleSize(); i++) + for (auto i = std::size_t{0}; i < m_originalSample.sampleSize(); i++) { singleChannel[i] = (m_originalSample.data()[i][0] + m_originalSample.data()[i][1]) / 2; maxMag = std::max(maxMag, singleChannel[i]); @@ -181,7 +179,7 @@ void SlicerT::findSlices() // normalize and find 0 crossings std::vector zeroCrossings; float lastValue = 1; - for (int i = 0; i < singleChannel.size(); i++) + for (auto i = std::size_t{0}; i < singleChannel.size(); i++) { singleChannel[i] /= maxMag; if (sign(lastValue) != sign(singleChannel[i])) @@ -199,10 +197,9 @@ void SlicerT::findSlices() int lastPoint = -minDist - 1; // to always store 0 first float spectralFlux = 0; - float prevFlux = 1E-10; // small value, no divison by zero - float real, imag, magnitude, diff; + float prevFlux = 1E-10f; // small value, no divison by zero - for (int i = 0; i < singleChannel.size() - windowSize; i += windowSize) + for (int i = 0; i < static_cast(singleChannel.size()) - windowSize; i += windowSize) { // fft std::copy_n(singleChannel.data() + i, windowSize, fftIn.data()); @@ -211,12 +208,12 @@ void SlicerT::findSlices() // calculate spectral flux in regard to last window for (int j = 0; j < windowSize / 2; j++) // only use niquistic frequencies { - real = fftOut[j][0]; - imag = fftOut[j][1]; - magnitude = std::sqrt(real * real + imag * imag); + float real = fftOut[j][0]; + float imag = fftOut[j][1]; + float magnitude = std::sqrt(real * real + imag * imag); // using L2-norm (euclidean distance) - diff = std::sqrt(std::pow(magnitude - prevMags[j], 2)); + float diff = std::sqrt(std::pow(magnitude - prevMags[j], 2)); spectralFlux += diff; prevMags[j] = magnitude; @@ -230,15 +227,16 @@ void SlicerT::findSlices() } prevFlux = spectralFlux; - spectralFlux = 1E-10; // again for no divison by zero + spectralFlux = 1E-10f; // again for no divison by zero } m_slicePoints.push_back(m_originalSample.sampleSize()); for (float& sliceValue : m_slicePoints) { - int closestZeroCrossing = *std::lower_bound(zeroCrossings.begin(), zeroCrossings.end(), sliceValue); - if (std::abs(sliceValue - closestZeroCrossing) < windowSize) { sliceValue = closestZeroCrossing; } + auto closestZeroCrossing = std::lower_bound(zeroCrossings.begin(), zeroCrossings.end(), sliceValue); + if (closestZeroCrossing == zeroCrossings.end()) { continue; } + if (std::abs(sliceValue - *closestZeroCrossing) < windowSize) { sliceValue = *closestZeroCrossing; } } float beatsPerMin = m_originalBPM.value() / 60.0f; @@ -302,7 +300,7 @@ std::vector SlicerT::getMidi() float totalTicks = outFrames / framesPerTick; float lastEnd = 0; - for (int i = 0; i < m_slicePoints.size() - 1; i++) + for (auto i = std::size_t{0}; i < m_slicePoints.size() - 1; i++) { float sliceStart = lastEnd; float sliceEnd = totalTicks * m_slicePoints[i + 1]; @@ -344,7 +342,7 @@ void SlicerT::saveSettings(QDomDocument& document, QDomElement& element) } element.setAttribute("totalSlices", static_cast(m_slicePoints.size())); - for (int i = 0; i < m_slicePoints.size(); i++) + for (auto i = std::size_t{0}; i < m_slicePoints.size(); i++) { element.setAttribute(tr("slice_%1").arg(i), m_slicePoints[i]); } diff --git a/plugins/SlicerT/SlicerT.h b/plugins/SlicerT/SlicerT.h index 010985dfce1..06b55687b4e 100644 --- a/plugins/SlicerT/SlicerT.h +++ b/plugins/SlicerT/SlicerT.h @@ -75,7 +75,7 @@ public slots: public: SlicerT(InstrumentTrack* instrumentTrack); - void playNote(NotePlayHandle* handle, sampleFrame* workingBuffer) override; + void playNote(NotePlayHandle* handle, SampleFrame* workingBuffer) override; void deleteNotePluginData(NotePlayHandle* handle) override; void saveSettings(QDomDocument& document, QDomElement& element) override; diff --git a/plugins/SlicerT/SlicerTWaveform.cpp b/plugins/SlicerT/SlicerTWaveform.cpp index 3793ed2f159..808b81c399b 100644 --- a/plugins/SlicerT/SlicerTWaveform.cpp +++ b/plugins/SlicerT/SlicerTWaveform.cpp @@ -195,11 +195,11 @@ void SlicerTWaveform::drawEditor() brush.setPen(QPen(s_sliceColor, 2)); - for (int i = 0; i < m_slicerTParent->m_slicePoints.size(); i++) + for (auto i = std::size_t{0}; i < m_slicerTParent->m_slicePoints.size(); i++) { float xPos = (m_slicerTParent->m_slicePoints.at(i) - startFrame) / numFramesToDraw * m_editorWidth; - if (i == m_closestSlice) + if (i == static_cast(m_closestSlice)) { brush.setPen(QPen(s_sliceHighlightColor, 2)); brush.drawLine(xPos, 0, xPos, m_editorHeight); @@ -268,7 +268,7 @@ void SlicerTWaveform::updateClosest(QMouseEvent* me) m_closestSlice = -1; float startFrame = m_seekerStart; float endFrame = m_seekerEnd; - for (int i = 0; i < m_slicerTParent->m_slicePoints.size(); i++) + for (auto i = std::size_t{0}; i < m_slicerTParent->m_slicePoints.size(); i++) { float sliceIndex = m_slicerTParent->m_slicePoints.at(i); float xPos = (sliceIndex - startFrame) / (endFrame - startFrame); diff --git a/plugins/SpectrumAnalyzer/Analyzer.cpp b/plugins/SpectrumAnalyzer/Analyzer.cpp index 0bbada7db55..dc2108eb9e4 100644 --- a/plugins/SpectrumAnalyzer/Analyzer.cpp +++ b/plugins/SpectrumAnalyzer/Analyzer.cpp @@ -77,7 +77,7 @@ Analyzer::~Analyzer() } // Take audio data and pass them to the spectrum processor. -bool Analyzer::processAudioBuffer(sampleFrame *buffer, const fpp_t frame_count) +bool Analyzer::processAudioBuffer(SampleFrame* buffer, const fpp_t frame_count) { // Measure time spent in audio thread; both average and peak should be well under 1 ms. #ifdef SA_DEBUG diff --git a/plugins/SpectrumAnalyzer/Analyzer.h b/plugins/SpectrumAnalyzer/Analyzer.h index 5be1f9ba83c..da87ffd3527 100644 --- a/plugins/SpectrumAnalyzer/Analyzer.h +++ b/plugins/SpectrumAnalyzer/Analyzer.h @@ -45,7 +45,7 @@ class Analyzer : public Effect Analyzer(Model *parent, const Descriptor::SubPluginFeatures::Key *key); ~Analyzer() override; - bool processAudioBuffer(sampleFrame *buffer, const fpp_t frame_count) override; + bool processAudioBuffer(SampleFrame* buffer, const fpp_t frame_count) override; EffectControls *controls() override {return &m_controls;} SaProcessor *getProcessor() {return &m_processor;} @@ -63,7 +63,7 @@ class Analyzer : public Effect //m_processorThread = QThread::create([=]{m_processor.analyze(m_inputBuffer);}); DataprocLauncher m_processorThread; - LocklessRingBuffer m_inputBuffer; + LocklessRingBuffer m_inputBuffer; #ifdef SA_DEBUG int m_last_dump_time; diff --git a/plugins/SpectrumAnalyzer/CMakeLists.txt b/plugins/SpectrumAnalyzer/CMakeLists.txt index 488495a9e3d..4b3d2ed69bd 100644 --- a/plugins/SpectrumAnalyzer/CMakeLists.txt +++ b/plugins/SpectrumAnalyzer/CMakeLists.txt @@ -1,5 +1,5 @@ INCLUDE(BuildPlugin) -INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) +include_directories(SYSTEM ${FFTW3F_INCLUDE_DIRS}) LINK_LIBRARIES(${FFTW3F_LIBRARIES}) diff --git a/plugins/SpectrumAnalyzer/DataprocLauncher.h b/plugins/SpectrumAnalyzer/DataprocLauncher.h index 4325284c9cd..43bac6a438b 100644 --- a/plugins/SpectrumAnalyzer/DataprocLauncher.h +++ b/plugins/SpectrumAnalyzer/DataprocLauncher.h @@ -37,7 +37,7 @@ namespace lmms class DataprocLauncher : public QThread { public: - explicit DataprocLauncher(SaProcessor &proc, LocklessRingBuffer &buffer) + explicit DataprocLauncher(SaProcessor &proc, LocklessRingBuffer &buffer) : m_processor(&proc), m_inputBuffer(&buffer) { @@ -50,7 +50,7 @@ class DataprocLauncher : public QThread } SaProcessor *m_processor; - LocklessRingBuffer *m_inputBuffer; + LocklessRingBuffer *m_inputBuffer; }; diff --git a/plugins/SpectrumAnalyzer/SaProcessor.cpp b/plugins/SpectrumAnalyzer/SaProcessor.cpp index 38e8e9e92fb..d9e7ac8a494 100644 --- a/plugins/SpectrumAnalyzer/SaProcessor.cpp +++ b/plugins/SpectrumAnalyzer/SaProcessor.cpp @@ -53,7 +53,7 @@ SaProcessor::SaProcessor(const SaControls *controls) : m_terminate(false), m_inBlockSize(FFT_BLOCK_SIZES[0]), m_fftBlockSize(FFT_BLOCK_SIZES[0]), - m_sampleRate(Engine::audioEngine()->processingSampleRate()), + m_sampleRate(Engine::audioEngine()->outputSampleRate()), m_framesFilledUp(0), m_spectrumActive(false), m_waterfallActive(false), @@ -98,9 +98,9 @@ SaProcessor::~SaProcessor() // Load data from audio thread ringbuffer and run FFT analysis if buffer is full enough. -void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) +void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) { - LocklessRingBufferReader reader(ring_buffer); + LocklessRingBufferReader reader(ring_buffer); // Processing thread loop while (!m_terminate) @@ -166,7 +166,7 @@ void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) #endif // update sample rate - m_sampleRate = Engine::audioEngine()->processingSampleRate(); + m_sampleRate = Engine::audioEngine()->outputSampleRate(); // apply FFT window for (unsigned int i = 0; i < m_inBlockSize; i++) @@ -209,7 +209,6 @@ void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) memset(pixel, 0, waterfallWidth() * sizeof (QRgb)); // add newest result on top - int target; // pixel being constructed float accL = 0; // accumulators for merging multiple bins float accR = 0; for (unsigned int i = 0; i < binCount(); i++) @@ -233,7 +232,8 @@ void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) if (band_end - band_start > 1.0) { // band spans multiple pixels: draw all pixels it covers - for (target = std::max((int)band_start, 0); target < band_end && target < waterfallWidth(); target++) + for (auto target = static_cast(std::max(band_start, 0.f)); + target < band_end && target < waterfallWidth(); target++) { pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]); } @@ -245,7 +245,7 @@ void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) else { // sub-pixel drawing; add contribution of current band - target = (int)band_start; + int target = static_cast(band_start); if ((int)band_start == (int)band_end) { // band ends within current target pixel, accumulate @@ -259,7 +259,9 @@ void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) accL += ((int)band_end - band_start) * m_normSpectrumL[i]; accR += ((int)band_end - band_start) * m_normSpectrumR[i]; - if (target >= 0 && target < waterfallWidth()) {pixel[target] = makePixel(accL, accR);} + if (target >= 0 && static_cast(target) < waterfallWidth()) { + pixel[target] = makePixel(accL, accR); + } // save remaining portion of the band for the following band / pixel accL = (band_end - (int)band_end) * m_normSpectrumL[i]; @@ -270,7 +272,8 @@ void SaProcessor::analyze(LocklessRingBuffer &ring_buffer) else { // Linear: always draws one or more pixels per band - for (target = std::max((int)band_start, 0); target < band_end && target < waterfallWidth(); target++) + for (auto target = static_cast(std::max(band_start, 0.f)); + target < band_end && target < waterfallWidth(); target++) { pixel[target] = makePixel(m_normSpectrumL[i], m_normSpectrumR[i]); } @@ -361,30 +364,20 @@ void SaProcessor::setWaterfallActive(bool active) // Reallocate data buffers according to newly set block size. void SaProcessor::reallocateBuffers() { - unsigned int new_size_index = m_controls->m_blockSizeModel.value(); - unsigned int new_in_size, new_fft_size; - unsigned int new_bins; + m_zeroPadFactor = m_controls->m_zeroPaddingModel.value(); // get new block sizes and bin count based on selected index - if (new_size_index < FFT_BLOCK_SIZES.size()) - { - new_in_size = FFT_BLOCK_SIZES[new_size_index]; - } - else - { - new_in_size = FFT_BLOCK_SIZES.back(); - } - m_zeroPadFactor = m_controls->m_zeroPaddingModel.value(); - if (new_size_index + m_zeroPadFactor < FFT_BLOCK_SIZES.size()) - { - new_fft_size = FFT_BLOCK_SIZES[new_size_index + m_zeroPadFactor]; - } - else - { - new_fft_size = FFT_BLOCK_SIZES.back(); - } + const unsigned int new_size_index = m_controls->m_blockSizeModel.value(); + + const unsigned int new_in_size = new_size_index < FFT_BLOCK_SIZES.size() + ? FFT_BLOCK_SIZES[new_size_index] + : FFT_BLOCK_SIZES.back(); + + const unsigned int new_fft_size = (new_size_index + m_zeroPadFactor < FFT_BLOCK_SIZES.size()) + ? FFT_BLOCK_SIZES[new_size_index + m_zeroPadFactor] + : FFT_BLOCK_SIZES.back(); - new_bins = new_fft_size / 2 +1; + const unsigned int new_bins = new_fft_size / 2 + 1; // Use m_reallocating to tell analyze() to avoid asking for the lock. This // is needed because under heavy load the FFT thread requests data lock so diff --git a/plugins/SpectrumAnalyzer/SaProcessor.h b/plugins/SpectrumAnalyzer/SaProcessor.h index 66b79788d26..3903bf9d6a4 100644 --- a/plugins/SpectrumAnalyzer/SaProcessor.h +++ b/plugins/SpectrumAnalyzer/SaProcessor.h @@ -43,7 +43,7 @@ template class LocklessRingBuffer; class SaControls; - +class SampleFrame; //! Receives audio data, runs FFT analysis and stores the result. @@ -54,7 +54,7 @@ class SaProcessor virtual ~SaProcessor(); // analysis thread and a method to terminate it - void analyze(LocklessRingBuffer &ring_buffer); + void analyze(LocklessRingBuffer &ring_buffer); void terminate() {m_terminate = true;} // inform processor if any processing is actually required diff --git a/plugins/SpectrumAnalyzer/SaSpectrumView.cpp b/plugins/SpectrumAnalyzer/SaSpectrumView.cpp index 0d9c2af87a2..2d15da7f47b 100644 --- a/plugins/SpectrumAnalyzer/SaSpectrumView.cpp +++ b/plugins/SpectrumAnalyzer/SaSpectrumView.cpp @@ -302,7 +302,7 @@ void SaSpectrumView::refreshPaths() // part new, part old. At reasonable frame rate, such difference is invisible.. void SaSpectrumView::updateBuffers(const float *spectrum, float *displayBuffer, float *peakBuffer) { - for (int n = 0; n < m_processor->binCount(); n++) + for (auto n = std::size_t{0}; n < m_processor->binCount(); n++) { // Update the exponential average if enabled, or simply copy the value. if (!m_controls->m_pauseModel.value()) @@ -647,14 +647,13 @@ float SaSpectrumView::ampToYPixel(float amplitude, unsigned int height) std::vector> SaSpectrumView::makeLogFreqTics(int low, int high) { std::vector> result; - int i, j; auto a = std::array{10, 20, 50}; // sparse series multipliers auto b = std::array{14, 30, 70}; // additional (denser) series // generate main steps (powers of 10); use the series to specify smaller steps - for (i = 1; i <= high; i *= 10) + for (int i = 1; i <= high; i *= 10) { - for (j = 0; j < 3; j++) + for (int j = 0; j < 3; j++) { // insert a label from sparse series if it falls within bounds if (i * a[j] >= low && i * a[j] <= high) @@ -691,7 +690,7 @@ std::vector> SaSpectrumView::makeLogFreqTics(int low std::vector> SaSpectrumView::makeLinearFreqTics(int low, int high) { std::vector> result; - int i, increment; + int increment; // select a suitable increment based on zoom level if (high - low < 500) {increment = 50;} @@ -700,7 +699,7 @@ std::vector> SaSpectrumView::makeLinearFreqTics(int else {increment = 2000;} // generate steps based on increment, starting at 0 - for (i = 0; i <= high; i += increment) + for (int i = 0; i <= high; i += increment) { if (i >= low) { @@ -724,7 +723,6 @@ std::vector> SaSpectrumView::makeLinearFreqTics(int std::vector> SaSpectrumView::makeLogAmpTics(int low, int high) { std::vector> result; - float i; double increment; // Base zoom level on selected range and how close is the current height @@ -744,7 +742,7 @@ std::vector> SaSpectrumView::makeLogAmpTics(int lo // Generate n dB increments, start checking at -90 dB. Limits are tweaked // just a little bit to make sure float comparisons do not miss edges. - for (i = 0.000000001; 10 * log10(i) <= (high + 0.001); i *= increment) + for (float i = 0.000000001f; 10 * log10(i) <= (high + 0.001); i *= increment) { if (10 * log10(i) >= (low - 0.001)) { @@ -764,8 +762,6 @@ std::vector> SaSpectrumView::makeLogAmpTics(int lo std::vector> SaSpectrumView::makeLinearAmpTics(int low, int high) { std::vector> result; - double i, nearest; - // make about 5 labels when window is small, 10 if it is big float split = (float)height() / sizeHint().height() >= 1.5 ? 10.0 : 5.0; @@ -777,28 +773,28 @@ std::vector> SaSpectrumView::makeLinearAmpTics(int // multiples, just generate a few evenly spaced increments across the range, // paying attention only to the decimal places to keep labels short. // Limits are shifted a bit so that float comparisons do not miss edges. - for (i = 0; i <= (lin_high + 0.0001); i += (lin_high - lin_low) / split) + for (double i = 0; i <= (lin_high + 0.0001); i += (lin_high - lin_low) / split) { if (i >= (lin_low - 0.0001)) { if (i >= 9.99 && i < 99.9) { - nearest = std::round(i); + double nearest = std::round(i); result.emplace_back(nearest, std::to_string(nearest).substr(0, 2)); } else if (i >= 0.099) { // also covers numbers above 100 - nearest = std::round(i * 10) / 10; + double nearest = std::round(i * 10) / 10; result.emplace_back(nearest, std::to_string(nearest).substr(0, 3)); } else if (i >= 0.0099) { - nearest = std::round(i * 1000) / 1000; + double nearest = std::round(i * 1000) / 1000; result.emplace_back(nearest, std::to_string(nearest).substr(0, 4)); } else if (i >= 0.00099) { - nearest = std::round(i * 10000) / 10000; + double nearest = std::round(i * 10000) / 10000; result.emplace_back(nearest, std::to_string(nearest).substr(1, 4)); } else if (i > -0.01 && i < 0.01) diff --git a/plugins/SpectrumAnalyzer/SaWaterfallView.cpp b/plugins/SpectrumAnalyzer/SaWaterfallView.cpp index 598bad72551..5169a4b499f 100644 --- a/plugins/SpectrumAnalyzer/SaWaterfallView.cpp +++ b/plugins/SpectrumAnalyzer/SaWaterfallView.cpp @@ -213,17 +213,15 @@ float SaWaterfallView::yPixelToTime(float position, int height) std::vector> SaWaterfallView::makeTimeTics() { std::vector> result; - float i; // get time value of the last line float limit = yPixelToTime(m_displayBottom, m_displayHeight); // set increment to about 30 pixels (but min. 0.1 s) - float increment = std::round(10 * limit / (m_displayHeight / 30)) / 10; - if (increment < 0.1) {increment = 0.1;} + const float increment = std::max(std::round(10 * limit / (m_displayHeight / 30)) / 10, 0.1f); // NOTE: labels positions are rounded to match the (rounded) label value - for (i = 0; i <= limit; i += increment) + for (float i = 0; i <= limit; i += increment) { if (i > 99) { diff --git a/plugins/StereoEnhancer/StereoEnhancer.cpp b/plugins/StereoEnhancer/StereoEnhancer.cpp index a7937a2ec1d..261c897df2f 100644 --- a/plugins/StereoEnhancer/StereoEnhancer.cpp +++ b/plugins/StereoEnhancer/StereoEnhancer.cpp @@ -58,7 +58,7 @@ StereoEnhancerEffect::StereoEnhancerEffect( const Descriptor::SubPluginFeatures::Key * _key ) : Effect( &stereoenhancer_plugin_descriptor, _parent, _key ), m_seFX( DspEffectLibrary::StereoEnhancer( 0.0f ) ), - m_delayBuffer( new sampleFrame[DEFAULT_BUFFER_SIZE] ), + m_delayBuffer( new SampleFrame[DEFAULT_BUFFER_SIZE] ), m_currFrame( 0 ), m_bbControls( this ) { @@ -82,7 +82,7 @@ StereoEnhancerEffect::~StereoEnhancerEffect() -bool StereoEnhancerEffect::processAudioBuffer( sampleFrame * _buf, +bool StereoEnhancerEffect::processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) { @@ -90,10 +90,6 @@ bool StereoEnhancerEffect::processAudioBuffer( sampleFrame * _buf, // audio with this effect double out_sum = 0.0; - float width; - int frameIndex = 0; - - if( !isEnabled() || !isRunning() ) { return( false ); @@ -110,10 +106,10 @@ bool StereoEnhancerEffect::processAudioBuffer( sampleFrame * _buf, m_delayBuffer[m_currFrame][1] = _buf[f][1]; // Get the width knob value from the Stereo Enhancer effect - width = m_seFX.wideCoeff(); + float width = m_seFX.wideCoeff(); // Calculate the correct sample frame for processing - frameIndex = m_currFrame - width; + int frameIndex = m_currFrame - width; if( frameIndex < 0 ) { @@ -149,8 +145,7 @@ bool StereoEnhancerEffect::processAudioBuffer( sampleFrame * _buf, void StereoEnhancerEffect::clearMyBuffer() { - int i; - for (i = 0; i < DEFAULT_BUFFER_SIZE; i++) + for (auto i = std::size_t{0}; i < DEFAULT_BUFFER_SIZE; i++) { m_delayBuffer[i][0] = 0.0f; m_delayBuffer[i][1] = 0.0f; diff --git a/plugins/StereoEnhancer/StereoEnhancer.h b/plugins/StereoEnhancer/StereoEnhancer.h index 5872100d35d..861187f8f7e 100644 --- a/plugins/StereoEnhancer/StereoEnhancer.h +++ b/plugins/StereoEnhancer/StereoEnhancer.h @@ -40,7 +40,7 @@ class StereoEnhancerEffect : public Effect StereoEnhancerEffect( Model * parent, const Descriptor::SubPluginFeatures::Key * _key ); ~StereoEnhancerEffect() override; - bool processAudioBuffer( sampleFrame * _buf, + bool processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) override; EffectControls * controls() override @@ -54,7 +54,7 @@ class StereoEnhancerEffect : public Effect private: DspEffectLibrary::StereoEnhancer m_seFX; - sampleFrame * m_delayBuffer; + SampleFrame* m_delayBuffer; int m_currFrame; StereoEnhancerControls m_bbControls; diff --git a/plugins/StereoMatrix/StereoMatrix.cpp b/plugins/StereoMatrix/StereoMatrix.cpp index b96d2e10791..c4384fddd60 100644 --- a/plugins/StereoMatrix/StereoMatrix.cpp +++ b/plugins/StereoMatrix/StereoMatrix.cpp @@ -64,7 +64,7 @@ StereoMatrixEffect::StereoMatrixEffect( -bool StereoMatrixEffect::processAudioBuffer( sampleFrame * _buf, +bool StereoMatrixEffect::processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) { diff --git a/plugins/StereoMatrix/StereoMatrix.h b/plugins/StereoMatrix/StereoMatrix.h index ae7a59724d3..a254264f838 100644 --- a/plugins/StereoMatrix/StereoMatrix.h +++ b/plugins/StereoMatrix/StereoMatrix.h @@ -39,7 +39,7 @@ class StereoMatrixEffect : public Effect StereoMatrixEffect( Model * parent, const Descriptor::SubPluginFeatures::Key * _key ); ~StereoMatrixEffect() override = default; - bool processAudioBuffer( sampleFrame * _buf, + bool processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) override; EffectControls* controls() override diff --git a/plugins/Stk/Mallets/Mallets.cpp b/plugins/Stk/Mallets/Mallets.cpp index c67814b5f32..00ddbf422fb 100644 --- a/plugins/Stk/Mallets/Mallets.cpp +++ b/plugins/Stk/Mallets/Mallets.cpp @@ -37,7 +37,6 @@ #include "AudioEngine.h" #include "ConfigManager.h" #include "Engine.h" -#include "gui_templates.h" #include "GuiApplication.h" #include "InstrumentTrack.h" @@ -115,7 +114,7 @@ MalletsInstrument::MalletsInstrument( InstrumentTrack * _instrument_track ): // TubeBell m_presetsModel.addItem( tr( "Tubular bells" ) ); - m_scalers.append( 1.8 ); + m_scalers.append(1.8f); // BandedWG m_presetsModel.addItem( tr( "Uniform bar" ) ); @@ -279,7 +278,7 @@ QString MalletsInstrument::nodeName() const void MalletsInstrument::playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) + SampleFrame* _working_buffer ) { if( m_filesMissing ) { @@ -343,7 +342,7 @@ void MalletsInstrument::playNote( NotePlayHandle * _n, m_vibratoFreqModel.value(), p, (uint8_t) m_spreadModel.value(), - Engine::audioEngine()->processingSampleRate() ); + Engine::audioEngine()->outputSampleRate() ); } else if( p == 9 ) { @@ -356,7 +355,7 @@ void MalletsInstrument::playNote( NotePlayHandle * _n, m_lfoSpeedModel.value(), m_adsrModel.value(), (uint8_t) m_spreadModel.value(), - Engine::audioEngine()->processingSampleRate() ); + Engine::audioEngine()->outputSampleRate() ); } else { @@ -369,7 +368,7 @@ void MalletsInstrument::playNote( NotePlayHandle * _n, m_strikeModel.value() * 128.0, speed, (uint8_t) m_spreadModel.value(), - Engine::audioEngine()->processingSampleRate() ); + Engine::audioEngine()->outputSampleRate() ); } m.unlock(); static_cast(_n->m_pluginData)->setPresetIndex(p); @@ -450,7 +449,6 @@ MalletsInstrumentView::MalletsInstrumentView( MalletsInstrument * _instrument, m_presetsCombo = new ComboBox( this, tr( "Instrument" ) ); m_presetsCombo->setGeometry( 140, 50, 99, ComboBox::DEFAULT_HEIGHT ); - m_presetsCombo->setFont( pointSize<8>( m_presetsCombo->font() ) ); connect( &_instrument->m_presetsModel, SIGNAL( dataChanged() ), this, SLOT( changePreset() ) ); diff --git a/plugins/Stk/Mallets/Mallets.h b/plugins/Stk/Mallets/Mallets.h index 91e2dfce140..cf05ace6688 100644 --- a/plugins/Stk/Mallets/Mallets.h +++ b/plugins/Stk/Mallets/Mallets.h @@ -189,7 +189,7 @@ class MalletsInstrument : public Instrument ~MalletsInstrument() override = default; void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; + SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; diff --git a/plugins/TripleOscillator/TripleOscillator.cpp b/plugins/TripleOscillator/TripleOscillator.cpp index d5f2e905fd0..f04cee81835 100644 --- a/plugins/TripleOscillator/TripleOscillator.cpp +++ b/plugins/TripleOscillator/TripleOscillator.cpp @@ -177,7 +177,7 @@ void OscillatorObject::updateDetuningLeft() { m_detuningLeft = powf( 2.0f, ( (float)m_coarseModel.value() * 100.0f + (float)m_fineLeftModel.value() ) / 1200.0f ) - / Engine::audioEngine()->processingSampleRate(); + / Engine::audioEngine()->outputSampleRate(); } @@ -187,7 +187,7 @@ void OscillatorObject::updateDetuningRight() { m_detuningRight = powf( 2.0f, ( (float)m_coarseModel.value() * 100.0f + (float)m_fineRightModel.value() ) / 1200.0f ) - / Engine::audioEngine()->processingSampleRate(); + / Engine::audioEngine()->outputSampleRate(); } @@ -307,7 +307,7 @@ QString TripleOscillator::nodeName() const void TripleOscillator::playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) + SampleFrame* _working_buffer ) { if (!_n->m_pluginData) { diff --git a/plugins/TripleOscillator/TripleOscillator.h b/plugins/TripleOscillator/TripleOscillator.h index 4b6d97835e9..fd6fc85ee3f 100644 --- a/plugins/TripleOscillator/TripleOscillator.h +++ b/plugins/TripleOscillator/TripleOscillator.h @@ -112,7 +112,7 @@ class TripleOscillator : public Instrument ~TripleOscillator() override = default; void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; + SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; @@ -121,9 +121,9 @@ class TripleOscillator : public Instrument QString nodeName() const override; - f_cnt_t desiredReleaseFrames() const override + float desiredReleaseTimeMs() const override { - return( 128 ); + return 3.f; } gui::PluginView* instantiateView( QWidget * _parent ) override; diff --git a/plugins/Vectorscope/VectorView.cpp b/plugins/Vectorscope/VectorView.cpp index f856f6429e0..2077d12cd26 100644 --- a/plugins/Vectorscope/VectorView.cpp +++ b/plugins/Vectorscope/VectorView.cpp @@ -37,7 +37,7 @@ namespace lmms::gui { -VectorView::VectorView(VecControls *controls, LocklessRingBuffer *inputBuffer, unsigned short displaySize, QWidget *parent) : +VectorView::VectorView(VecControls *controls, LocklessRingBuffer *inputBuffer, unsigned short displaySize, QWidget *parent) : QWidget(parent), m_controls(controls), m_inputBuffer(inputBuffer), @@ -138,8 +138,6 @@ void VectorView::paintEvent(QPaintEvent *event) std::size_t frameCount = inBuffer.size(); // Draw new points on top - float left, right; - int x, y; const bool logScale = m_controls->m_logarithmicModel.value(); const unsigned short activeSize = hq ? m_displaySize : m_displaySize / 2; @@ -164,6 +162,8 @@ void VectorView::paintEvent(QPaintEvent *event) // The longer the line is, the dimmer, simulating real electron trace on luminescent screen. for (std::size_t frame = 0; frame < frameCount; frame++) { + float left = 0.0f; + float right = 0.0f; float inLeft = inBuffer[frame][0] * m_zoom; float inRight = inBuffer[frame][1] * m_zoom; // Scale left and right channel from (-1.0, 1.0) to display range @@ -185,8 +185,8 @@ void VectorView::paintEvent(QPaintEvent *event) } // Rotate display coordinates 45 degrees, flip Y axis and make sure the result stays within bounds - x = saturate(right - left + activeSize / 2.f); - y = saturate(activeSize - (right + left + activeSize / 2.f)); + int x = saturate(right - left + activeSize / 2.f); + int y = saturate(activeSize - (right + left + activeSize / 2.f)); // Estimate number of points needed to fill space between the old and new pixel. Cap at 100. unsigned char points = std::min((int)sqrt((m_oldX - x) * (m_oldX - x) + (m_oldY - y) * (m_oldY - y)), 100); @@ -222,6 +222,8 @@ void VectorView::paintEvent(QPaintEvent *event) // one full-color pixel per sample. for (std::size_t frame = 0; frame < frameCount; frame++) { + float left = 0.0f; + float right = 0.0f; float inLeft = inBuffer[frame][0] * m_zoom; float inRight = inBuffer[frame][1] * m_zoom; if (logScale) { @@ -235,8 +237,8 @@ void VectorView::paintEvent(QPaintEvent *event) left = inLeft * (activeSize - 1) / 4; right = inRight * (activeSize - 1) / 4; } - x = saturate(right - left + activeSize / 2.f); - y = saturate(activeSize - (right + left + activeSize / 2.f)); + int x = saturate(right - left + activeSize / 2.f); + int y = saturate(activeSize - (right + left + activeSize / 2.f)); ((QRgb*)m_displayBuffer.data())[x + y * activeSize] = m_controls->m_colorFG.rgb(); } } diff --git a/plugins/Vectorscope/VectorView.h b/plugins/Vectorscope/VectorView.h index 59ac99a7757..c828fd139e1 100644 --- a/plugins/Vectorscope/VectorView.h +++ b/plugins/Vectorscope/VectorView.h @@ -30,6 +30,7 @@ namespace lmms { class VecControls; +class SampleFrame; } //#define VEC_DEBUG @@ -43,7 +44,7 @@ class VectorView : public QWidget { Q_OBJECT public: - explicit VectorView(VecControls *controls, LocklessRingBuffer *inputBuffer, unsigned short displaySize, QWidget *parent = 0); + explicit VectorView(VecControls *controls, LocklessRingBuffer *inputBuffer, unsigned short displaySize, QWidget *parent = 0); ~VectorView() override = default; QSize sizeHint() const override {return QSize(300, 300);} @@ -59,8 +60,8 @@ private slots: private: VecControls *m_controls; - LocklessRingBuffer *m_inputBuffer; - LocklessRingBufferReader m_bufferReader; + LocklessRingBuffer *m_inputBuffer; + LocklessRingBufferReader m_bufferReader; std::vector m_displayBuffer; const unsigned short m_displaySize; diff --git a/plugins/Vectorscope/Vectorscope.cpp b/plugins/Vectorscope/Vectorscope.cpp index f843fc86d7b..c94eb5d2834 100644 --- a/plugins/Vectorscope/Vectorscope.cpp +++ b/plugins/Vectorscope/Vectorscope.cpp @@ -58,7 +58,7 @@ Vectorscope::Vectorscope(Model *parent, const Plugin::Descriptor::SubPluginFeatu // Take audio data and store them for processing and display in the GUI thread. -bool Vectorscope::processAudioBuffer(sampleFrame *buffer, const fpp_t frame_count) +bool Vectorscope::processAudioBuffer(SampleFrame* buffer, const fpp_t frame_count) { if (!isEnabled() || !isRunning ()) {return false;} diff --git a/plugins/Vectorscope/Vectorscope.h b/plugins/Vectorscope/Vectorscope.h index 54022ab9099..66d20e63903 100644 --- a/plugins/Vectorscope/Vectorscope.h +++ b/plugins/Vectorscope/Vectorscope.h @@ -39,16 +39,16 @@ class Vectorscope : public Effect Vectorscope(Model *parent, const Descriptor::SubPluginFeatures::Key *key); ~Vectorscope() override = default; - bool processAudioBuffer(sampleFrame *buffer, const fpp_t frame_count) override; + bool processAudioBuffer(SampleFrame* buffer, const fpp_t frame_count) override; EffectControls *controls() override {return &m_controls;} - LocklessRingBuffer *getBuffer() {return &m_inputBuffer;} + LocklessRingBuffer *getBuffer() {return &m_inputBuffer;} private: VecControls m_controls; // Maximum LMMS buffer size (hard coded, the actual constant is hard to get) const unsigned int m_maxBufferSize = 4096; - LocklessRingBuffer m_inputBuffer; + LocklessRingBuffer m_inputBuffer; }; diff --git a/plugins/Vestige/CMakeLists.txt b/plugins/Vestige/CMakeLists.txt index a51b051fd93..0a58478896a 100644 --- a/plugins/Vestige/CMakeLists.txt +++ b/plugins/Vestige/CMakeLists.txt @@ -1,14 +1,15 @@ -IF(LMMS_SUPPORT_VST) - INCLUDE(BuildPlugin) - LINK_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/..") - IF(LMMS_BUILD_LINUX) - LINK_LIBRARIES(-Wl,--enable-new-dtags) - SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) - SET(CMAKE_INSTALL_RPATH "$ORIGIN") - ELSE() - SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${PLUGIN_DIR}") - ENDIF() - BUILD_PLUGIN(vestige Vestige.cpp Vestige.h MOCFILES Vestige.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") - TARGET_LINK_LIBRARIES(vestige vstbase) -ENDIF(LMMS_SUPPORT_VST) +if(NOT LMMS_HAVE_VST) + return() +endif() +include(BuildPlugin) +link_directories("${CMAKE_CURRENT_BINARY_DIR}/..") +if(LMMS_BUILD_LINUX) + link_libraries(-Wl,--enable-new-dtags) + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + set(CMAKE_INSTALL_RPATH "$ORIGIN") +else() + set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${PLUGIN_DIR}") +endif() +build_plugin(vestige Vestige.cpp Vestige.h MOCFILES Vestige.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +target_link_libraries(vestige vstbase) diff --git a/plugins/Vestige/Vestige.cpp b/plugins/Vestige/Vestige.cpp index 583075c0cd3..ffed82af7e3 100644 --- a/plugins/Vestige/Vestige.cpp +++ b/plugins/Vestige/Vestige.cpp @@ -152,7 +152,7 @@ class VstInstrumentPlugin : public VstPlugin VestigeInstrument::VestigeInstrument( InstrumentTrack * _instrument_track ) : - Instrument( _instrument_track, &vestige_plugin_descriptor ), + Instrument(_instrument_track, &vestige_plugin_descriptor, nullptr, Flag::IsSingleStreamed | Flag::IsMidiBased), m_plugin( nullptr ), m_pluginMutex(), m_subWindow( nullptr ), @@ -395,7 +395,7 @@ void VestigeInstrument::loadFile( const QString & _file ) -void VestigeInstrument::play( sampleFrame * _buf ) +void VestigeInstrument::play( SampleFrame* _buf ) { if (!m_pluginMutex.tryLock(Engine::getSong()->isExporting() ? -1 : 0)) {return;} @@ -583,11 +583,12 @@ VestigeInstrumentView::VestigeInstrumentView( Instrument * _instrument, m_selPresetButton->setMenu(menu); + constexpr int buttonFontSize = 12; m_toggleGUIButton = new QPushButton( tr( "Show/hide GUI" ), this ); m_toggleGUIButton->setGeometry( 20, 130, 200, 24 ); m_toggleGUIButton->setIcon( embed::getIconPixmap( "zoom" ) ); - m_toggleGUIButton->setFont( pointSize<8>( m_toggleGUIButton->font() ) ); + m_toggleGUIButton->setFont(adjustedToPixelSize(m_toggleGUIButton->font(), buttonFontSize)); connect( m_toggleGUIButton, SIGNAL( clicked() ), this, SLOT( toggleGUI() ) ); @@ -596,7 +597,7 @@ VestigeInstrumentView::VestigeInstrumentView( Instrument * _instrument, this); note_off_all_btn->setGeometry( 20, 160, 200, 24 ); note_off_all_btn->setIcon( embed::getIconPixmap( "stop" ) ); - note_off_all_btn->setFont( pointSize<8>( note_off_all_btn->font() ) ); + note_off_all_btn->setFont(adjustedToPixelSize(note_off_all_btn->font(), buttonFontSize)); connect( note_off_all_btn, SIGNAL( clicked() ), this, SLOT( noteOffAll() ) ); @@ -881,7 +882,7 @@ void VestigeInstrumentView::paintEvent( QPaintEvent * ) tr( "No VST plugin loaded" ); QFont f = p.font(); f.setBold( true ); - p.setFont( pointSize<10>( f ) ); + p.setFont(adjustedToPixelSize(f, 10)); p.setPen( QColor( 255, 255, 255 ) ); p.drawText( 10, 100, plugin_name ); @@ -893,7 +894,7 @@ void VestigeInstrumentView::paintEvent( QPaintEvent * ) { p.setPen( QColor( 0, 0, 0 ) ); f.setBold( false ); - p.setFont( pointSize<8>( f ) ); + p.setFont(adjustedToPixelSize(f, 8)); p.drawText( 10, 114, tr( "by " ) + m_vi->m_plugin->vendorString() ); p.setPen( QColor( 255, 255, 255 ) ); @@ -1053,7 +1054,6 @@ void ManageVestigeInstrumentView::syncPlugin( void ) auto paramStr = std::array{}; QStringList s_dumpValues; const QMap & dump = m_vi->m_plugin->parameterDump(); - float f_value; for( int i = 0; i < m_vi->paramCount; i++ ) { @@ -1063,7 +1063,7 @@ void ManageVestigeInstrumentView::syncPlugin( void ) { sprintf(paramStr.data(), "param%d", i); s_dumpValues = dump[paramStr.data()].split(":"); - f_value = LocaleHelper::toFloat(s_dumpValues.at(2)); + float f_value = LocaleHelper::toFloat(s_dumpValues.at(2)); m_vi->knobFModel[ i ]->setAutomatedValue( f_value ); m_vi->knobFModel[ i ]->setInitValue( f_value ); } diff --git a/plugins/Vestige/Vestige.h b/plugins/Vestige/Vestige.h index 9ac66f74da6..6a308ad2c20 100644 --- a/plugins/Vestige/Vestige.h +++ b/plugins/Vestige/Vestige.h @@ -61,7 +61,7 @@ class VestigeInstrument : public Instrument VestigeInstrument( InstrumentTrack * _instrument_track ); virtual ~VestigeInstrument(); - virtual void play( sampleFrame * _working_buffer ); + virtual void play( SampleFrame* _working_buffer ); virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent ); virtual void loadSettings( const QDomElement & _this ); @@ -70,11 +70,6 @@ class VestigeInstrument : public Instrument virtual void loadFile( const QString & _file ); - virtual Flags flags() const - { - return Flag::IsSingleStreamed | Flag::IsMidiBased; - } - virtual bool handleMidiEvent( const MidiEvent& event, const TimePos& time, f_cnt_t offset = 0 ); virtual gui::PluginView* instantiateView( QWidget * _parent ); diff --git a/plugins/Vibed/Vibed.cpp b/plugins/Vibed/Vibed.cpp index ddf9097a527..19b8ab1c06b 100644 --- a/plugins/Vibed/Vibed.cpp +++ b/plugins/Vibed/Vibed.cpp @@ -72,11 +72,11 @@ class Vibed::StringContainer ~StringContainer() = default; - void addString(int harm, float pick, float pickup, const float* impulse, float randomize, + void addString(std::size_t harm, float pick, float pickup, const float* impulse, float randomize, float stringLoss, float detune, int oversample, bool state, int id) { constexpr auto octave = std::array{0.25f, 0.5f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f}; - assert(harm >= 0 && harm < octave.size()); + assert(harm < octave.size()); m_strings[id] = VibratingString{m_pitch * octave[harm], pick, pickup, impulse, m_bufferLength, m_sampleRate, oversample, randomize, stringLoss, detune, state}; @@ -97,7 +97,7 @@ class Vibed::StringContainer }; Vibed::Vibed(InstrumentTrack* instrumentTrack) : - Instrument(instrumentTrack, &vibedstrings_plugin_descriptor) + Instrument(instrumentTrack, &vibedstrings_plugin_descriptor, nullptr, Flag::IsNotBendable) { for (int harm = 0; harm < s_stringCount; ++harm) { @@ -201,12 +201,12 @@ QString Vibed::nodeName() const return vibedstrings_plugin_descriptor.name; } -void Vibed::playNote(NotePlayHandle* n, sampleFrame* workingBuffer) +void Vibed::playNote(NotePlayHandle* n, SampleFrame* workingBuffer) { if (!n->m_pluginData) { const auto newContainer = new StringContainer{n->frequency(), - Engine::audioEngine()->processingSampleRate(), s_sampleLength}; + Engine::audioEngine()->outputSampleRate(), s_sampleLength}; n->m_pluginData = newContainer; diff --git a/plugins/Vibed/Vibed.h b/plugins/Vibed/Vibed.h index 18d334c4d5a..f02f810de58 100644 --- a/plugins/Vibed/Vibed.h +++ b/plugins/Vibed/Vibed.h @@ -57,7 +57,7 @@ class Vibed : public Instrument Vibed(InstrumentTrack* instrumentTrack); ~Vibed() override = default; - void playNote(NotePlayHandle* n, sampleFrame* workingBuffer) override; + void playNote(NotePlayHandle* n, SampleFrame* workingBuffer) override; void deleteNotePluginData(NotePlayHandle* n) override; void saveSettings(QDomDocument& doc, QDomElement& elem) override; @@ -65,8 +65,6 @@ class Vibed : public Instrument QString nodeName() const override; - Flags flags() const override { return Flag::IsNotBendable; } - gui::PluginView* instantiateView(QWidget* parent) override; private: diff --git a/plugins/Vibed/VibratingString.cpp b/plugins/Vibed/VibratingString.cpp index 44ade3c3a5c..e4bb760e255 100644 --- a/plugins/Vibed/VibratingString.cpp +++ b/plugins/Vibed/VibratingString.cpp @@ -26,6 +26,7 @@ #include "interpolation.h" #include "AudioEngine.h" #include "Engine.h" +#include "lmms_basics.h" #include #include @@ -75,12 +76,10 @@ std::unique_ptr VibratingString::initDelayLine(int l if (len > 0) { dl->data = std::make_unique(len); - float r; - float offset = 0.0f; for (int i = 0; i < dl->length; ++i) { - r = static_cast(std::rand()) / RAND_MAX; - offset = (m_randomize / 2.0f - m_randomize) * r; + float r = static_cast(std::rand()) / RAND_MAX; + float offset = (m_randomize / 2.0f - m_randomize) * r; dl->data[i] = offset; } } @@ -101,7 +100,7 @@ void VibratingString::resample(const float* src, f_cnt_t srcFrames, f_cnt_t dstF { const float srcFrameFloat = frame * static_cast(srcFrames) / dstFrames; const float fracPos = srcFrameFloat - static_cast(srcFrameFloat); - const f_cnt_t srcFrame = std::clamp(static_cast(srcFrameFloat), 1, srcFrames - 3); + const f_cnt_t srcFrame = std::clamp(static_cast(srcFrameFloat), f_cnt_t{1}, srcFrames - 3); m_impulse[frame] = cubicInterpolate( src[srcFrame - 1], src[srcFrame + 0], diff --git a/plugins/Vibed/VibratingString.h b/plugins/Vibed/VibratingString.h index efdaec28256..0ad5a6e6e47 100644 --- a/plugins/Vibed/VibratingString.h +++ b/plugins/Vibed/VibratingString.h @@ -49,8 +49,6 @@ class VibratingString sample_t nextSample() { - sample_t ym0; - sample_t ypM; for (int i = 0; i < m_oversample; ++i) { // Output at pickup position @@ -58,9 +56,9 @@ class VibratingString m_outsamp[i] += toBridgeAccess(m_toBridge.get(), m_pickupLoc); // Sample traveling into "bridge" - ym0 = toBridgeAccess(m_toBridge.get(), 1); + sample_t ym0 = toBridgeAccess(m_toBridge.get(), 1); // Sample to "nut" - ypM = fromBridgeAccess(m_fromBridge.get(), m_fromBridge->length - 2); + sample_t ypM = fromBridgeAccess(m_fromBridge.get(), m_fromBridge->length - 2); // String state update @@ -105,21 +103,18 @@ class VibratingString */ void setDelayLine(DelayLine* dl, int pick, const float* values, int len, float scale, bool state) { - float r; - float offset; - if (!state) { for (int i = 0; i < pick; ++i) { - r = static_cast(std::rand()) / RAND_MAX; - offset = (m_randomize / 2.0f - m_randomize) * r; + float r = static_cast(std::rand()) / RAND_MAX; + float offset = (m_randomize / 2.0f - m_randomize) * r; dl->data[i] = scale * values[dl->length - i - 1] + offset; } for (int i = pick; i < dl->length; ++i) { - r = static_cast(std::rand()) / RAND_MAX; - offset = (m_randomize / 2.0f - m_randomize) * r; + float r = static_cast(std::rand()) / RAND_MAX; + float offset = (m_randomize / 2.0f - m_randomize) * r; dl->data[i] = scale * values[i - pick] + offset; } } @@ -129,8 +124,8 @@ class VibratingString { for (int i = pick; i < dl->length; ++i) { - r = static_cast(std::rand()) / RAND_MAX; - offset = (m_randomize / 2.0f - m_randomize) * r; + float r = static_cast(std::rand()) / RAND_MAX; + float offset = (m_randomize / 2.0f - m_randomize) * r; dl->data[i] = scale * values[i - pick] + offset; } } @@ -138,8 +133,8 @@ class VibratingString { for (int i = 0; i < len; ++i) { - r = static_cast(std::rand()) / RAND_MAX; - offset = (m_randomize / 2.0f - m_randomize) * r; + float r = static_cast(std::rand()) / RAND_MAX; + float offset = (m_randomize / 2.0f - m_randomize) * r; dl->data[i+pick] = scale * values[i] + offset; } } diff --git a/plugins/VstBase/CMakeLists.txt b/plugins/VstBase/CMakeLists.txt index 8d3262b1a09..15d47200c96 100644 --- a/plugins/VstBase/CMakeLists.txt +++ b/plugins/VstBase/CMakeLists.txt @@ -1,4 +1,4 @@ -IF(NOT LMMS_SUPPORT_VST) +if(NOT LMMS_HAVE_VST) RETURN() ENDIF() @@ -33,6 +33,7 @@ set(export_variables "LMMS_BUILD_WIN32" "PLUGIN_DIR" "LMMS_HAVE_LIBRT" + "USE_WERROR" ) SET(EXTERNALPROJECT_CMAKE_ARGS @@ -47,11 +48,11 @@ foreach(var ${export_variables}) endforeach() # build 32 bit version of RemoteVstPlugin -IF(WANT_VST_32) +if(LMMS_HAVE_VST_32) INCLUDE("${CMAKE_CURRENT_LIST_DIR}/RemoteVstPlugin32.cmake") ENDIF() # build 64 bit version of RemoteVstPlugin -IF(WANT_VST_64) +if(LMMS_HAVE_VST_64) INCLUDE("${CMAKE_CURRENT_LIST_DIR}/RemoteVstPlugin64.cmake") ENDIF() diff --git a/plugins/VstBase/NativeLinuxRemoteVstPlugin64.cmake b/plugins/VstBase/NativeLinuxRemoteVstPlugin64.cmake index dc582360429..d9ef2897d52 100644 --- a/plugins/VstBase/NativeLinuxRemoteVstPlugin64.cmake +++ b/plugins/VstBase/NativeLinuxRemoteVstPlugin64.cmake @@ -1,11 +1,13 @@ IF(LMMS_BUILD_LINUX) + if(LMMS_HOST_X86_64) + set(CXX_FLAGS -m64) + endif() ExternalProject_Add(NativeLinuxRemoteVstPlugin64 "${EXTERNALPROJECT_ARGS}" CMAKE_ARGS "${EXTERNALPROJECT_CMAKE_ARGS}" - "-DCMAKE_CXX_FLAGS=-m64 -DNATIVE_LINUX_VST" + "-DCMAKE_CXX_FLAGS=${CXX_FLAGS} -DNATIVE_LINUX_VST" "-DCMAKE_C_FLAGS=-DNATIVE_LINUX_VST" ) INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../NativeLinuxRemoteVstPlugin64" DESTINATION "${PLUGIN_DIR}") -ENDIF() - +ENDIF() \ No newline at end of file diff --git a/plugins/VstBase/RemoteVstPlugin.cpp b/plugins/VstBase/RemoteVstPlugin.cpp index 0ec60bea4fe..f8bf39a3755 100644 --- a/plugins/VstBase/RemoteVstPlugin.cpp +++ b/plugins/VstBase/RemoteVstPlugin.cpp @@ -87,11 +87,7 @@ #undef Unsorted #endif -#ifdef USE_MINGW_THREADS_REPLACEMENT -# include -#else -# include -#endif +#include #include #include @@ -191,7 +187,7 @@ class RemoteVstPlugin : public RemotePluginClient void hideEditor(); void destroyEditor(); - virtual void process( const sampleFrame * _in, sampleFrame * _out ); + virtual void process( const SampleFrame* _in, SampleFrame* _out ); virtual void processMidiEvent( const MidiEvent& event, const f_cnt_t offset ); @@ -269,7 +265,7 @@ class RemoteVstPlugin : public RemotePluginClient void saveChunkToFile( const std::string & _file ); // restore settings chunk of plugin from file - void loadChunkFromFile( const std::string & _file, int _len ); + void loadChunkFromFile(const std::string& _file, std::size_t _len); // restore settings chunk of plugin from file void loadPresetFile( const std::string & _file ); @@ -846,7 +842,6 @@ void RemoteVstPlugin::initEditor() #endif #else - XEvent e; Atom prop_atom, val_atom; if (m_display == nullptr) @@ -1027,7 +1022,7 @@ bool RemoteVstPlugin::load( const std::string & _plugin_file ) -void RemoteVstPlugin::process( const sampleFrame * _in, sampleFrame * _out ) +void RemoteVstPlugin::process( const SampleFrame* _in, SampleFrame* _out ) { // first we gonna post all MIDI-events we enqueued so far if( m_midiEvents.size() ) @@ -1307,7 +1302,7 @@ void RemoteVstPlugin::saveChunkToFile( const std::string & _file ) "Error opening file for saving chunk.\n" ); return; } - if ( fwrite( chunk, 1, len, fp ) != len ) + if (fwrite(chunk, 1, len, fp) != static_cast(len)) { fprintf( stderr, "Error saving chunk to file.\n" ); @@ -1545,7 +1540,7 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) unsigned int toUInt; float * pFloat; - if (m_plugin->uniqueID != pBank->fxID) { + if (static_cast(m_plugin->uniqueID) != pBank->fxID) { sendMessage( message( IdVstCurrentProgramName ). addString( "Error: Plugin UniqID not match" ) ); fclose( stream ); @@ -1581,7 +1576,7 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) else { auto toUIntArray = reinterpret_cast(chunk); - for (int i = 0; i < pBank->numPrograms; i++ ) + for (auto i = 0u; i < pBank->numPrograms; i++) { toUInt = endian_swap( toUIntArray[ i ] ); pFloat = ( float* ) &toUInt; @@ -1629,10 +1624,7 @@ void RemoteVstPlugin::loadPresetFile( const std::string & _file ) delete[] (char*)chunk; } - - - -void RemoteVstPlugin::loadChunkFromFile( const std::string & _file, int _len ) +void RemoteVstPlugin::loadChunkFromFile(const std::string& _file, std::size_t _len) { auto chunk = new char[_len]; @@ -2308,7 +2300,7 @@ void RemoteVstPlugin::guiEventLoop() { XNextEvent(m_display, &e); - if (e.type == ClientMessage && e.xclient.data.l[0] == m_wmDeleteMessage) + if (e.type == ClientMessage && static_cast(e.xclient.data.l[0]) == m_wmDeleteMessage) { hideEditor(); } diff --git a/plugins/VstBase/RemoteVstPlugin/CMakeLists.txt b/plugins/VstBase/RemoteVstPlugin/CMakeLists.txt index 3356ae5967d..3f861e2d566 100644 --- a/plugins/VstBase/RemoteVstPlugin/CMakeLists.txt +++ b/plugins/VstBase/RemoteVstPlugin/CMakeLists.txt @@ -1,4 +1,9 @@ -cmake_minimum_required(VERSION 3.8) +cmake_minimum_required(VERSION 3.13) + +if(POLICY "CMP0092") + cmake_policy(SET CMP0092 NEW) # MSVC warning flags are not in CMAKE__FLAGS by default. +endif() + project(RemoteVstPlugin LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) @@ -22,6 +27,12 @@ FOREACH( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) SET("CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG}" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") ENDFOREACH() +# Add warning and error flags +include(ErrorFlags) + +# Import of windows.h breaks min()/max() +add_definitions(-DNOMINMAX) + ADD_SUBDIRECTORY("${LMMS_SOURCE_DIR}/src/common" common) if(NOT IS_WIN) @@ -58,35 +69,11 @@ target_compile_definitions(${EXE_NAME} PRIVATE BUILD_REMOTE_PLUGIN_CLIENT) # Workaround for missing WinMain if(MSVC) - set_property(TARGET ${EXE_NAME} - APPEND - PROPERTY LINK_FLAGS "/entry:mainCRTStartup" -) -endif() - - -if(WIN32) - find_package(Qt5Core REQUIRED) - target_link_libraries(${EXE_NAME} Qt5::Core) + target_link_options(${EXE_NAME} PRIVATE "/entry:mainCRTStartup") endif() if(IS_MINGW) SET(CMAKE_REQUIRED_FLAGS "-std=c++17") - - CHECK_CXX_SOURCE_COMPILES(" - #include - int main(int argc, const char* argv[]) { - std::mutex m; - return 0; - } - " HAS_STD_MUTEX) - - if(NOT HAS_STD_MUTEX) - target_include_directories(${EXE_NAME} PRIVATE - "${LMMS_SOURCE_DIR}/src/3rdparty/mingw-std-threads") - target_compile_definitions(${EXE_NAME} PRIVATE - -DUSE_MINGW_THREADS_REPLACEMENT) - endif() endif() if(LMMS_BUILD_WIN32) @@ -96,7 +83,9 @@ if(LMMS_BUILD_WIN32) set(NOOP_COMMAND "${CMAKE_COMMAND}" "-E" "echo") endif() if(STRIP) - set(STRIP_COMMAND "$,${NOOP_COMMAND},${STRIP}>") + # TODO CMake 3.19: Now that CONFIG generator expressions support testing for + # multiple configurations, combine the OR into a single CONFIG expression. + set(STRIP_COMMAND "$,$>,${NOOP_COMMAND},${STRIP}>") else() set(STRIP_COMMAND "${NOOP_COMMAND}") endif() diff --git a/plugins/VstBase/RemoteVstPlugin32.cmake b/plugins/VstBase/RemoteVstPlugin32.cmake index f39bd93d06a..5afb7db8123 100644 --- a/plugins/VstBase/RemoteVstPlugin32.cmake +++ b/plugins/VstBase/RemoteVstPlugin32.cmake @@ -17,23 +17,6 @@ ENDMACRO() IF(LMMS_BUILD_WIN32 AND NOT LMMS_BUILD_WIN64) ADD_SUBDIRECTORY(RemoteVstPlugin) ELSEIF(LMMS_BUILD_WIN64 AND MSVC) - IF(NOT QT_32_PREFIX) - SET(LMMS_MSVC_YEAR_FOR_QT ${LMMS_MSVC_YEAR}) - - if(LMMS_MSVC_YEAR_FOR_QT EQUAL 2019 AND Qt5_VERSION VERSION_LESS "5.15") - SET(LMMS_MSVC_YEAR_FOR_QT 2017) # Qt only provides binaries for MSVC 2017, but 2019 is binary compatible - endif() - - GET_FILENAME_COMPONENT(QT_BIN_DIR ${QT_QMAKE_EXECUTABLE} DIRECTORY) - SET(QT_32_PREFIX "${QT_BIN_DIR}/../../msvc${LMMS_MSVC_YEAR_FOR_QT}") - ENDIF() - - #TODO: qt5 installed using vcpkg: I don't know how to detect if the user built the x86 version of qt5 from here. At least not cleanly. - #So for the moment, we'll allow the built. - IF(NOT (IS_DIRECTORY ${QT_32_PREFIX} AND EXISTS ${QT_32_PREFIX}/bin/qmake.exe)) - MESSAGE(WARNING "No Qt 32 bit installation found at ${QT_32_PREFIX}. If you're using VCPKG you can ignore this message if you've built x86-windows version of qt5") - ENDIF() - ExternalProject_Add(RemoteVstPlugin32 "${EXTERNALPROJECT_ARGS}" CMAKE_GENERATOR "${LMMS_MSVC_GENERATOR}" @@ -42,18 +25,10 @@ ELSEIF(LMMS_BUILD_WIN64 AND MSVC) CMAKE_ARGS "${EXTERNALPROJECT_CMAKE_ARGS}" "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" - "-DCMAKE_PREFIX_PATH=${QT_32_PREFIX}" ) INSTALL_EXTERNAL_PROJECT(RemoteVstPlugin32) ELSEIF(LMMS_BUILD_LINUX) - # Use winegcc - INCLUDE(CheckWineGcc) - CheckWineGcc(32 "${WINEGCC}" WINEGCC_WORKING) - IF(NOT WINEGCC_WORKING) - MESSAGE(WARNING "winegcc fails to compile 32-bit binaries, please make sure you have 32-bit GCC libraries") - RETURN() - ENDIF() ExternalProject_Add(RemoteVstPlugin32 "${EXTERNALPROJECT_ARGS}" CMAKE_ARGS @@ -63,7 +38,6 @@ ELSEIF(LMMS_BUILD_LINUX) ) INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32" "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32.exe.so" DESTINATION "${PLUGIN_DIR}/32") - ELSEIF(CMAKE_TOOLCHAIN_FILE_32) ExternalProject_Add(RemoteVstPlugin32 "${EXTERNALPROJECT_ARGS}" diff --git a/plugins/VstBase/RemoteVstPlugin64.cmake b/plugins/VstBase/RemoteVstPlugin64.cmake index 65b33a16251..2f4a745ac17 100644 --- a/plugins/VstBase/RemoteVstPlugin64.cmake +++ b/plugins/VstBase/RemoteVstPlugin64.cmake @@ -1,12 +1,6 @@ IF(LMMS_BUILD_WIN64) ADD_SUBDIRECTORY(RemoteVstPlugin) ELSEIF(LMMS_BUILD_LINUX) - INCLUDE(CheckWineGcc) - CheckWineGcc(64 "${WINEGCC}" WINEGCC_WORKING) - IF(NOT WINEGCC_WORKING) - MESSAGE(WARNING "winegcc fails to compile 64-bit binaries, please make sure you have 64-bit GCC libraries") - RETURN() - ENDIF() ExternalProject_Add(RemoteVstPlugin64 "${EXTERNALPROJECT_ARGS}" CMAKE_ARGS diff --git a/plugins/VstBase/VstPlugin.cpp b/plugins/VstBase/VstPlugin.cpp index b23ae39bf97..5dbe7a698ae 100644 --- a/plugins/VstBase/VstPlugin.cpp +++ b/plugins/VstBase/VstPlugin.cpp @@ -36,7 +36,7 @@ #ifdef LMMS_BUILD_LINUX # include -# include "X11EmbedContainer.h" +# include #endif #include @@ -338,7 +338,7 @@ void VstPlugin::updateSampleRate() { lock(); sendMessage( message( IdSampleRateInformation ). - addInt( Engine::audioEngine()->processingSampleRate() ) ); + addInt( Engine::audioEngine()->outputSampleRate() ) ); waitForMessage( IdInformationUpdated, true ); unlock(); } @@ -735,14 +735,12 @@ void VstPlugin::createUI( QWidget * parent ) QWidget* container = nullptr; -#if QT_VERSION >= 0x050100 if (m_embedMethod == "qt" ) { QWindow* vw = QWindow::fromWinId(m_pluginWindowID); container = QWidget::createWindowContainer(vw, parent ); container->installEventFilter(this); } else -#endif #ifdef LMMS_BUILD_WIN32 if (m_embedMethod == "win32" ) @@ -801,7 +799,6 @@ void VstPlugin::createUI( QWidget * parent ) bool VstPlugin::eventFilter(QObject *obj, QEvent *event) { -#if QT_VERSION >= 0x050100 if (embedMethod() == "qt" && obj == m_pluginWidget) { if (event->type() == QEvent::Show) { @@ -809,7 +806,6 @@ bool VstPlugin::eventFilter(QObject *obj, QEvent *event) } qDebug() << obj << event; } -#endif return false; } diff --git a/plugins/VstEffect/CMakeLists.txt b/plugins/VstEffect/CMakeLists.txt index 68ef141d984..3cbe8e8cc85 100644 --- a/plugins/VstEffect/CMakeLists.txt +++ b/plugins/VstEffect/CMakeLists.txt @@ -1,4 +1,7 @@ -IF(LMMS_SUPPORT_VST) +if(NOT LMMS_HAVE_VST) + return() +endif() + INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../vst_base") LINK_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/..") @@ -12,6 +15,3 @@ ENDIF() BUILD_PLUGIN(vsteffect VstEffect.cpp VstEffectControls.cpp VstEffectControlDialog.cpp VstSubPluginFeatures.cpp VstEffect.h VstEffectControls.h VstEffectControlDialog.h VstSubPluginFeatures.h MOCFILES VstEffectControlDialog.h VstEffectControls.h EMBEDDED_RESOURCES *.png) TARGET_LINK_LIBRARIES(vsteffect vstbase) - -ENDIF(LMMS_SUPPORT_VST) - diff --git a/plugins/VstEffect/VstEffect.cpp b/plugins/VstEffect/VstEffect.cpp index bdbdea8060c..ecb8240c867 100644 --- a/plugins/VstEffect/VstEffect.cpp +++ b/plugins/VstEffect/VstEffect.cpp @@ -76,7 +76,7 @@ VstEffect::VstEffect( Model * _parent, -bool VstEffect::processAudioBuffer( sampleFrame * _buf, const fpp_t _frames ) +bool VstEffect::processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) { if( !isEnabled() || !isRunning () ) { @@ -87,11 +87,11 @@ bool VstEffect::processAudioBuffer( sampleFrame * _buf, const fpp_t _frames ) { const float d = dryLevel(); #ifdef __GNUC__ - sampleFrame buf[_frames]; + SampleFrame buf[_frames]; #else - sampleFrame * buf = new sampleFrame[_frames]; + SampleFrame* buf = new SampleFrame[_frames]; #endif - memcpy( buf, _buf, sizeof( sampleFrame ) * _frames ); + memcpy( buf, _buf, sizeof( SampleFrame ) * _frames ); if (m_pluginMutex.tryLock(Engine::getSong()->isExporting() ? -1 : 0)) { m_plugin->process( buf, buf ); diff --git a/plugins/VstEffect/VstEffect.h b/plugins/VstEffect/VstEffect.h index 987b1ecc236..c3f6e8091ce 100644 --- a/plugins/VstEffect/VstEffect.h +++ b/plugins/VstEffect/VstEffect.h @@ -45,7 +45,7 @@ class VstEffect : public Effect const Descriptor::SubPluginFeatures::Key * _key ); ~VstEffect() override = default; - bool processAudioBuffer( sampleFrame * _buf, + bool processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) override; EffectControls * controls() override diff --git a/plugins/VstEffect/VstEffectControlDialog.cpp b/plugins/VstEffect/VstEffectControlDialog.cpp index 5bee94155d9..0fb4913a338 100644 --- a/plugins/VstEffect/VstEffectControlDialog.cpp +++ b/plugins/VstEffect/VstEffectControlDialog.cpp @@ -246,7 +246,7 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) : tb->addWidget(space1); tbLabel = new QLabel( tr( "Effect by: " ), this ); - tbLabel->setFont( pointSize<7>( f ) ); + tbLabel->setFont(adjustedToPixelSize(f, 7)); tbLabel->setTextFormat(Qt::RichText); tbLabel->setAlignment( Qt::AlignTop | Qt::AlignLeft ); tb->addWidget( tbLabel ); diff --git a/plugins/VstEffect/VstEffectControls.cpp b/plugins/VstEffect/VstEffectControls.cpp index af90e46464e..c9eb4923451 100644 --- a/plugins/VstEffect/VstEffectControls.cpp +++ b/plugins/VstEffect/VstEffectControls.cpp @@ -452,7 +452,6 @@ void ManageVSTEffectView::syncPlugin() auto paramStr = std::array{}; QStringList s_dumpValues; const QMap & dump = m_effect->m_plugin->parameterDump(); - float f_value; for( int i = 0; i < m_vi2->paramCount; i++ ) { @@ -463,7 +462,7 @@ void ManageVSTEffectView::syncPlugin() { sprintf(paramStr.data(), "param%d", i); s_dumpValues = dump[paramStr.data()].split(":"); - f_value = LocaleHelper::toFloat(s_dumpValues.at(2)); + float f_value = LocaleHelper::toFloat(s_dumpValues.at(2)); m_vi2->knobFModel[ i ]->setAutomatedValue( f_value ); m_vi2->knobFModel[ i ]->setInitValue( f_value ); } diff --git a/plugins/Watsyn/Watsyn.cpp b/plugins/Watsyn/Watsyn.cpp index 8e49942e16a..2749b2dafc9 100644 --- a/plugins/Watsyn/Watsyn.cpp +++ b/plugins/Watsyn/Watsyn.cpp @@ -74,8 +74,8 @@ WatsynObject::WatsynObject( float * _A1wave, float * _A2wave, m_fpp( _frames ), m_parent( _w ) { - m_abuf = new sampleFrame[_frames]; - m_bbuf = new sampleFrame[_frames]; + m_abuf = new SampleFrame[_frames]; + m_bbuf = new SampleFrame[_frames]; m_lphase[A1_OSC] = 0.0f; m_lphase[A2_OSC] = 0.0f; @@ -107,9 +107,9 @@ WatsynObject::~WatsynObject() void WatsynObject::renderOutput( fpp_t _frames ) { if( m_abuf == nullptr ) - m_abuf = new sampleFrame[m_fpp]; + m_abuf = new SampleFrame[m_fpp]; if( m_bbuf == nullptr ) - m_bbuf = new sampleFrame[m_fpp]; + m_bbuf = new SampleFrame[m_fpp]; for( fpp_t frame = 0; frame < _frames; frame++ ) { @@ -327,24 +327,24 @@ WatsynInstrument::WatsynInstrument( InstrumentTrack * _instrument_track ) : void WatsynInstrument::playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) + SampleFrame* _working_buffer ) { if (!_n->m_pluginData) { auto w = new WatsynObject(&A1_wave[0], &A2_wave[0], &B1_wave[0], &B2_wave[0], m_amod.value(), m_bmod.value(), - Engine::audioEngine()->processingSampleRate(), _n, Engine::audioEngine()->framesPerPeriod(), this); + Engine::audioEngine()->outputSampleRate(), _n, Engine::audioEngine()->framesPerPeriod(), this); _n->m_pluginData = w; } const fpp_t frames = _n->framesLeftForCurrentPeriod(); const f_cnt_t offset = _n->noteOffset(); - sampleFrame * buffer = _working_buffer + offset; + SampleFrame* buffer = _working_buffer + offset; auto w = static_cast(_n->m_pluginData); - sampleFrame * abuf = w->abuf(); - sampleFrame * bbuf = w->bbuf(); + SampleFrame* abuf = w->abuf(); + SampleFrame* bbuf = w->bbuf(); w-> renderOutput( frames ); diff --git a/plugins/Watsyn/Watsyn.h b/plugins/Watsyn/Watsyn.h index d238edbdea6..aebc74645f3 100644 --- a/plugins/Watsyn/Watsyn.h +++ b/plugins/Watsyn/Watsyn.h @@ -96,11 +96,11 @@ class WatsynObject void renderOutput( fpp_t _frames ); - inline sampleFrame * abuf() const + inline SampleFrame* abuf() const { return m_abuf; } - inline sampleFrame * bbuf() const + inline SampleFrame* bbuf() const { return m_bbuf; } @@ -120,8 +120,8 @@ class WatsynObject WatsynInstrument * m_parent; - sampleFrame * m_abuf; - sampleFrame * m_bbuf; + SampleFrame* m_abuf; + SampleFrame* m_bbuf; float m_lphase [NUM_OSCS]; float m_rphase [NUM_OSCS]; @@ -140,7 +140,7 @@ class WatsynInstrument : public Instrument ~WatsynInstrument() override = default; void playNote( NotePlayHandle * _n, - sampleFrame * _working_buffer ) override; + SampleFrame* _working_buffer ) override; void deleteNotePluginData( NotePlayHandle * _n ) override; @@ -150,9 +150,9 @@ class WatsynInstrument : public Instrument QString nodeName() const override; - f_cnt_t desiredReleaseFrames() const override + float desiredReleaseTimeMs() const override { - return( 64 ); + return 1.5f; } gui::PluginView* instantiateView( QWidget * _parent ) override; diff --git a/plugins/WaveShaper/WaveShaper.cpp b/plugins/WaveShaper/WaveShaper.cpp index acd5a933b07..37378540867 100644 --- a/plugins/WaveShaper/WaveShaper.cpp +++ b/plugins/WaveShaper/WaveShaper.cpp @@ -66,7 +66,7 @@ WaveShaperEffect::WaveShaperEffect( Model * _parent, -bool WaveShaperEffect::processAudioBuffer( sampleFrame * _buf, +bool WaveShaperEffect::processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) { if( !isEnabled() || !isRunning () ) diff --git a/plugins/WaveShaper/WaveShaper.h b/plugins/WaveShaper/WaveShaper.h index 4c63d5806c0..4c9d6e96222 100644 --- a/plugins/WaveShaper/WaveShaper.h +++ b/plugins/WaveShaper/WaveShaper.h @@ -40,7 +40,7 @@ class WaveShaperEffect : public Effect WaveShaperEffect( Model * _parent, const Descriptor::SubPluginFeatures::Key * _key ); ~WaveShaperEffect() override = default; - bool processAudioBuffer( sampleFrame * _buf, + bool processAudioBuffer( SampleFrame* _buf, const fpp_t _frames ) override; EffectControls * controls() override diff --git a/plugins/Xpressive/CMakeLists.txt b/plugins/Xpressive/CMakeLists.txt index 366381e6228..f5f1e71612d 100644 --- a/plugins/Xpressive/CMakeLists.txt +++ b/plugins/Xpressive/CMakeLists.txt @@ -1,6 +1,5 @@ INCLUDE(BuildPlugin) -INCLUDE_DIRECTORIES(exprtk) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_sc_andor") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_return_statement") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_break_continue") @@ -8,7 +7,7 @@ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_comments") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_string_capabilities") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_rtl_io_file") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Dexprtk_disable_rtl_vecops") -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WERROR_FLAGS} -fexceptions") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions") IF(LMMS_BUILD_WIN32 AND NOT MSVC) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wa,-mbig-obj -Dexprtk_disable_enhanced_features") @@ -16,13 +15,16 @@ ELSEIF(LMMS_BUILD_WIN32 AND MSVC) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") ENDIF() -BUILD_PLUGIN(xpressive +add_library(exprtk INTERFACE) +target_include_directories(exprtk INTERFACE exprtk) +set_target_properties(exprtk PROPERTIES SYSTEM TRUE) + +build_plugin(xpressive Xpressive.cpp ExprSynth.cpp Xpressive.h - exprtk/exprtk.hpp + ExprSynth.h MOCFILES Xpressive.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png" ) - - +target_link_libraries(xpressive exprtk) diff --git a/plugins/Xpressive/ExprSynth.cpp b/plugins/Xpressive/ExprSynth.cpp index 991e0d3e6b2..c48b94ec8dc 100644 --- a/plugins/Xpressive/ExprSynth.cpp +++ b/plugins/Xpressive/ExprSynth.cpp @@ -34,9 +34,10 @@ #include "interpolation.h" #include "lmms_math.h" #include "NotePlayHandle.h" +#include "SampleFrame.h" -#include "exprtk.hpp" +#include #define WARN_EXPRTK qWarning("ExprTk exception") @@ -146,13 +147,8 @@ struct LastSampleFunction : public exprtk::ifunction inline T operator()(const T& x) override { - if (!std::isnan(x) && !std::isinf(x)) - { - const int ix=(int)x; - if (ix>=1 && ix<=m_history_size) - { - return m_samples[(ix + m_pivot_last) % m_history_size]; - } + if (!std::isnan(x) && x >= 1 && x <= m_history_size) { + return m_samples[(static_cast(x) + m_pivot_last) % m_history_size]; } return 0; } @@ -322,14 +318,8 @@ struct RandomVectorSeedFunction : public exprtk::ifunction inline float operator()(const float& index,const float& seed) override { - int irseed; - if (seed < 0 || std::isnan(seed) || std::isinf(seed)) - { - irseed=0; - } - else - irseed=(int)seed; - return randv(index,irseed); + const int irseed = seed < 0 || std::isnan(seed) || std::isinf(seed) ? 0 : static_cast(seed); + return randv(index, irseed); } static const int data_size=sizeof(random_data)/sizeof(int); @@ -741,7 +731,7 @@ ExprSynth::~ExprSynth() } } -void ExprSynth::renderOutput(fpp_t frames, sampleFrame *buf) +void ExprSynth::renderOutput(fpp_t frames, SampleFrame* buf) { try { @@ -826,4 +816,4 @@ void ExprSynth::renderOutput(fpp_t frames, sampleFrame *buf) } -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/plugins/Xpressive/ExprSynth.h b/plugins/Xpressive/ExprSynth.h index 5d664c85e6d..3348ed9f46e 100644 --- a/plugins/Xpressive/ExprSynth.h +++ b/plugins/Xpressive/ExprSynth.h @@ -37,6 +37,7 @@ namespace lmms class ExprFrontData; class NotePlayHandle; +class SampleFrame; namespace gui { @@ -106,7 +107,7 @@ class ExprSynth const sample_rate_t sample_rate, const FloatModel* pan1, const FloatModel* pan2, float rel_trans); virtual ~ExprSynth(); - void renderOutput(fpp_t frames, sampleFrame* buf ); + void renderOutput(fpp_t frames, SampleFrame* buf ); private: diff --git a/plugins/Xpressive/Xpressive.cpp b/plugins/Xpressive/Xpressive.cpp index babc372317a..23a76b22820 100644 --- a/plugins/Xpressive/Xpressive.cpp +++ b/plugins/Xpressive/Xpressive.cpp @@ -196,7 +196,7 @@ QString Xpressive::nodeName() const { return (xpressive_plugin_descriptor.name); } -void Xpressive::playNote(NotePlayHandle* nph, sampleFrame* working_buffer) { +void Xpressive::playNote(NotePlayHandle* nph, SampleFrame* working_buffer) { m_A1=m_parameterA1.value(); m_A2=m_parameterA2.value(); m_A3=m_parameterA3.value(); @@ -204,14 +204,14 @@ void Xpressive::playNote(NotePlayHandle* nph, sampleFrame* working_buffer) { if (!nph->m_pluginData) { auto exprO1 = new ExprFront(m_outputExpression[0].constData(), - Engine::audioEngine()->processingSampleRate()); // give the "last" function a whole second - auto exprO2 = new ExprFront(m_outputExpression[1].constData(), Engine::audioEngine()->processingSampleRate()); + Engine::audioEngine()->outputSampleRate()); // give the "last" function a whole second + auto exprO2 = new ExprFront(m_outputExpression[1].constData(), Engine::audioEngine()->outputSampleRate()); auto init_expression_step1 = [this, nph](ExprFront* e) { //lambda function to init exprO1 and exprO2 //add the constants and the variables to the expression. e->add_constant("key", nph->key());//the key that was pressed. e->add_constant("bnote", nph->instrumentTrack()->baseNote()); // the base note - e->add_constant("srate", Engine::audioEngine()->processingSampleRate());// sample rate of the audio engine + e->add_constant("srate", Engine::audioEngine()->outputSampleRate());// sample rate of the audio engine e->add_constant("v", nph->getVolume() / 255.0); //volume of the note. e->add_constant("tempo", Engine::getSong()->getTempo());//tempo of the song. e->add_variable("A1", m_A1);//A1,A2,A3: general purpose input controls. @@ -225,7 +225,7 @@ void Xpressive::playNote(NotePlayHandle* nph, sampleFrame* working_buffer) { m_W2.setInterpolate(m_interpolateW2.value()); m_W3.setInterpolate(m_interpolateW3.value()); nph->m_pluginData = new ExprSynth(&m_W1, &m_W2, &m_W3, exprO1, exprO2, nph, - Engine::audioEngine()->processingSampleRate(), &m_panning1, &m_panning2, m_relTransition.value()); + Engine::audioEngine()->outputSampleRate(), &m_panning1, &m_panning2, m_relTransition.value()); } auto ps = static_cast(nph->m_pluginData); @@ -256,13 +256,12 @@ void Xpressive::smooth(float smoothness,const graphModel * in,graphModel * out) auto const guassian = new float[guass_size]; float sum = 0.0f; float temp = 0.0f; - int i; - for (i = 0; i < guass_size; i++ ) + for (int i = 0; i < guass_size; i++) { temp = (i - guass_center) / delta; sum += guassian[i] = a * powf(F_E, -0.5f * temp * temp); } - for (i = 0; i < guass_size; i++ ) + for (int i = 0; i < guass_size; i++) { guassian[i] = guassian[i] / sum; } @@ -336,13 +335,6 @@ XpressiveView::XpressiveView(Instrument * _instrument, QWidget * _parent) : pal.setBrush(backgroundRole(), PLUGIN_NAME::getIconPixmap("wavegraph")); m_graph->setPalette(pal); - PixmapButton * m_w1Btn; - PixmapButton * m_w2Btn; - PixmapButton * m_w3Btn; - PixmapButton * m_o1Btn; - PixmapButton * m_o2Btn; - PixmapButton * m_helpBtn; - m_w1Btn = new PixmapButton(this, nullptr); m_w1Btn->move(3, ROW_BTN); m_w1Btn->setActiveGraphic(PLUGIN_NAME::getIconPixmap("w1_active")); @@ -587,9 +579,9 @@ void XpressiveView::expressionChanged() { if (parse_ok) { e->exprValid().setValue(0); - const int length = m_raw_graph->length(); + const auto length = static_cast(m_raw_graph->length()); auto const samples = new float[length]; - for (i = 0; i < length; i++) { + for (auto i = std::size_t{0}; i < length; i++) { t = i / (float) length; samples[i] = expr.evaluate(); if (std::isinf(samples[i]) != 0 || std::isnan(samples[i]) != 0) diff --git a/plugins/Xpressive/Xpressive.h b/plugins/Xpressive/Xpressive.h index b91957ac480..7dd8dcfaa7a 100644 --- a/plugins/Xpressive/Xpressive.h +++ b/plugins/Xpressive/Xpressive.h @@ -68,7 +68,7 @@ class Xpressive : public Instrument Xpressive(InstrumentTrack* instrument_track ); void playNote(NotePlayHandle* nph, - sampleFrame* working_buffer ) override; + SampleFrame* working_buffer ) override; void deleteNotePluginData( NotePlayHandle* nph ) override; @@ -190,6 +190,7 @@ protected slots: PixmapButton *m_w3Btn; PixmapButton *m_o1Btn; PixmapButton *m_o2Btn; + PixmapButton *m_helpBtn; PixmapButton *m_sinWaveBtn; PixmapButton *m_triangleWaveBtn; PixmapButton *m_sqrWaveBtn; diff --git a/plugins/ZynAddSubFx/CMakeLists.txt b/plugins/ZynAddSubFx/CMakeLists.txt index 3369a793865..cbd7326fe6b 100644 --- a/plugins/ZynAddSubFx/CMakeLists.txt +++ b/plugins/ZynAddSubFx/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) # definitions for ZynAddSubFX IF(LMMS_BUILD_LINUX OR LMMS_BUILD_APPLE OR LMMS_BUILD_OPENBSD OR LMMS_BUILD_FREEBSD) FIND_PACKAGE(X11) - INCLUDE_DIRECTORIES(${X11_INCLUDE_DIR}) + include_directories(SYSTEM ${X11_INCLUDE_DIR}) ADD_DEFINITIONS(-DOS_LINUX) ELSE() ADD_DEFINITIONS(-DOS_WINDOWS) @@ -25,13 +25,9 @@ endif() # build ZynAddSubFX with full optimizations if(NOT MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wno-write-strings -Wno-deprecated-declarations -fpermissive") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -fpermissive") endif() -IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND NOT "${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "6.0.0") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-misleading-indentation") -ENDIF() - IF(MINGW_PREFIX) SET(FLTK_FLUID_EXECUTABLE "${MINGW_PREFIX}/bin/fluid") ENDIF() @@ -44,21 +40,21 @@ IF(NOT EXISTS ${FLTK_FLUID_EXECUTABLE}) ENDIF() ENDIF() -include_directories( +include_directories(SYSTEM "${FLTK_INCLUDE_DIR}" - "${CMAKE_CURRENT_SOURCE_DIR}" ${FFTW3F_INCLUDE_DIRS} +) +include_directories( + "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_BINARY_DIR}" - "${CMAKE_SOURCE_DIR}/src/3rdparty/mingw-std-threads" - "${CMAKE_CURRENT_SOURCE_DIR}/zynaddsubfx/src/UI" ) ADD_DEFINITIONS(-DPLUGINVERSION) # removes exit confirmation dialogs etc. in MasterUI.fl add_subdirectory(zynaddsubfx/src/Nio) add_subdirectory(zynaddsubfx/src/UI) -SET(zynaddsubfx_core_SRCS +add_library(zynaddsubfx_synth OBJECT zynaddsubfx/src/DSP/AnalogFilter.cpp zynaddsubfx/src/DSP/FFTwrapper.cpp zynaddsubfx/src/DSP/Filter.cpp @@ -107,13 +103,26 @@ SET(zynaddsubfx_core_SRCS zynaddsubfx/src/Synth/SUBnote.cpp ) -add_library(ZynAddSubFxCoreObjs OBJECT LocalZynAddSubFx.cpp ${zynaddsubfx_core_SRCS}) -add_library(ZynAddSubFxCore INTERFACE) -target_sources(ZynAddSubFxCore INTERFACE - $ - $ -) +set_target_properties(zynaddsubfx_nio PROPERTIES SYSTEM TRUE) +set_target_properties(zynaddsubfx_gui PROPERTIES SYSTEM TRUE) +set_target_properties(zynaddsubfx_synth PROPERTIES SYSTEM TRUE) + +# Relative include paths don't work automatically for the GUI, because the +# generated C++ files aren't in the source directory. Thus, add the expected +# source directory as an additional include directory. +target_include_directories(zynaddsubfx_gui PUBLIC zynaddsubfx/src/UI) +add_library(ZynAddSubFxCore STATIC + LocalZynAddSubFx.cpp +) +target_include_directories(ZynAddSubFxCore SYSTEM PUBLIC + zynaddsubfx/src + "${CMAKE_CURRENT_BINARY_DIR}/zynaddsubfx/src" +) +target_static_libraries(ZynAddSubFxCore PUBLIC + zynaddsubfx_nio + zynaddsubfx_synth +) target_link_libraries(ZynAddSubFxCore INTERFACE ${FFTW3F_LIBRARIES} ${QT_LIBRARIES} @@ -132,7 +141,7 @@ ELSE() SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${PLUGIN_DIR}") ENDIF() BUILD_PLUGIN(zynaddsubfx ZynAddSubFx.cpp ZynAddSubFx.h MOCFILES ZynAddSubFx.h EMBEDDED_RESOURCES artwork.png logo.png) -target_link_libraries(zynaddsubfx ZynAddSubFxCore) +target_static_libraries(zynaddsubfx ZynAddSubFxCore) if(MSVC) set(WINRC "${CMAKE_CURRENT_BINARY_DIR}/zynaddsubfx.rc") @@ -155,8 +164,8 @@ add_executable(RemoteZynAddSubFx RemoteZynAddSubFx.cpp ${LMMS_COMMON_SRCS} "${WINRC}" - $ ) +target_static_libraries(RemoteZynAddSubFx ZynAddSubFxCore zynaddsubfx_gui) INSTALL(TARGETS RemoteZynAddSubFx RUNTIME DESTINATION "${PLUGIN_DIR}") # Needed to deploy dependencies of RemoteZynAddSubFx SET_PROPERTY(GLOBAL APPEND PROPERTY PLUGINS_BUILT "RemoteZynAddSubFx") @@ -164,8 +173,8 @@ SET_PROPERTY(GLOBAL APPEND PROPERTY PLUGINS_BUILT "RemoteZynAddSubFx") TARGET_COMPILE_DEFINITIONS(RemoteZynAddSubFx PRIVATE BUILD_REMOTE_PLUGIN_CLIENT) IF(LMMS_BUILD_WIN32) - SET_TARGET_PROPERTIES(RemoteZynAddSubFx PROPERTIES LINK_FLAGS "${LINK_FLAGS} -mwindows") -ENDIF(LMMS_BUILD_WIN32) + target_link_options(RemoteZynAddSubFx PRIVATE "-mwindows") +ENDIF() # Remove useless dependencies from FLTK. Use fltk-config to avoid static library # in older environments @@ -176,7 +185,7 @@ IF(FLTK_CONFIG AND NOT (LMMS_BUILD_APPLE OR LMMS_BUILD_WIN32)) STRING(REPLACE " " ";" FLTK_FILTERED_LDFLAGS ${FLTK_FILTERED_LDFLAGS}) LIST(REMOVE_ITEM FLTK_FILTERED_LDFLAGS -lX11) ENDIF() -target_link_libraries(RemoteZynAddSubFx ZynAddSubFxCore ${FLTK_FILTERED_LDFLAGS}) +target_link_libraries(RemoteZynAddSubFx ${FLTK_FILTERED_LDFLAGS}) if(LMMS_HAVE_LIBRT) target_link_libraries(RemoteZynAddSubFx rt) diff --git a/plugins/ZynAddSubFx/LocalZynAddSubFx.cpp b/plugins/ZynAddSubFx/LocalZynAddSubFx.cpp index 83ef3e9a246..5dc8fb2052a 100644 --- a/plugins/ZynAddSubFx/LocalZynAddSubFx.cpp +++ b/plugins/ZynAddSubFx/LocalZynAddSubFx.cpp @@ -36,11 +36,12 @@ #endif #include "MidiEvent.h" +#include "SampleFrame.h" -#include "zynaddsubfx/src/Nio/NulEngine.h" -#include "zynaddsubfx/src/Misc/Master.h" -#include "zynaddsubfx/src/Misc/Part.h" -#include "zynaddsubfx/src/Misc/Util.h" +#include +#include +#include +#include // Global variable in zynaddsubfx/src/globals.h SYNTH_T* synth = nullptr; @@ -265,7 +266,7 @@ void LocalZynAddSubFx::processMidiEvent( const MidiEvent& event ) -void LocalZynAddSubFx::processAudio( sampleFrame * _out ) +void LocalZynAddSubFx::processAudio( SampleFrame* _out ) { #ifdef _MSC_VER const auto outputl = static_cast(_alloca(synth->buffersize * sizeof(float))); diff --git a/plugins/ZynAddSubFx/LocalZynAddSubFx.h b/plugins/ZynAddSubFx/LocalZynAddSubFx.h index f37a3e0f41f..5ef735f6472 100644 --- a/plugins/ZynAddSubFx/LocalZynAddSubFx.h +++ b/plugins/ZynAddSubFx/LocalZynAddSubFx.h @@ -25,6 +25,8 @@ #ifndef LOCAL_ZYNADDSUBFX_H #define LOCAL_ZYNADDSUBFX_H +#include + #include "Note.h" class Master; @@ -34,10 +36,12 @@ namespace lmms { class MidiEvent; +class SampleFrame; class LocalZynAddSubFx { + public: LocalZynAddSubFx(); ~LocalZynAddSubFx(); @@ -59,7 +63,7 @@ class LocalZynAddSubFx void processMidiEvent( const MidiEvent& event ); - void processAudio( sampleFrame * _out ); + void processAudio( SampleFrame* _out ); inline Master * master() { diff --git a/plugins/ZynAddSubFx/RemoteZynAddSubFx.cpp b/plugins/ZynAddSubFx/RemoteZynAddSubFx.cpp index c4d6b71a16b..d6b3b7b34c9 100644 --- a/plugins/ZynAddSubFx/RemoteZynAddSubFx.cpp +++ b/plugins/ZynAddSubFx/RemoteZynAddSubFx.cpp @@ -37,8 +37,8 @@ #include "RemotePluginClient.h" #include "LocalZynAddSubFx.h" -#include "zynaddsubfx/src/Nio/Nio.h" -#include "zynaddsubfx/src/UI/MasterUI.h" +#include +#include using namespace lmms; @@ -141,7 +141,7 @@ class RemoteZynAddSubFx : public RemotePluginClient, public LocalZynAddSubFx } - void process( const sampleFrame * _in, sampleFrame * _out ) override + void process( const SampleFrame* _in, SampleFrame* _out ) override { LocalZynAddSubFx::processAudio( _out ); } diff --git a/plugins/ZynAddSubFx/ThreadShims.h b/plugins/ZynAddSubFx/ThreadShims.h index 843b50fb58b..ff947b3d063 100644 --- a/plugins/ZynAddSubFx/ThreadShims.h +++ b/plugins/ZynAddSubFx/ThreadShims.h @@ -1,9 +1,3 @@ #include #include #include - -#if defined(__MINGW32__) && !defined(_GLIBCXX_HAS_GTHREADS) -# include -# include -# include -#endif diff --git a/plugins/ZynAddSubFx/ZynAddSubFx.cpp b/plugins/ZynAddSubFx/ZynAddSubFx.cpp index 01fa6400b79..51610d87786 100644 --- a/plugins/ZynAddSubFx/ZynAddSubFx.cpp +++ b/plugins/ZynAddSubFx/ZynAddSubFx.cpp @@ -40,7 +40,6 @@ #include "DataFile.h" #include "InstrumentPlayHandle.h" #include "InstrumentTrack.h" -#include "gui_templates.h" #include "Song.h" #include "StringPairDrag.h" #include "RemoteZynAddSubFx.h" @@ -104,12 +103,12 @@ bool ZynAddSubFxRemotePlugin::processMessage( const message & _m ) ZynAddSubFxInstrument::ZynAddSubFxInstrument( InstrumentTrack * _instrumentTrack ) : - Instrument( _instrumentTrack, &zynaddsubfx_plugin_descriptor ), + Instrument(_instrumentTrack, &zynaddsubfx_plugin_descriptor, nullptr, Flag::IsSingleStreamed | Flag::IsMidiBased), m_hasGUI( false ), m_plugin( nullptr ), m_remotePlugin( nullptr ), m_portamentoModel( 0, 0, 127, 1, this, tr( "Portamento" ) ), - m_filterFreqModel( 64, 0, 127, 1, this, tr( "Filter frequency" ) ), + m_filterFreqModel( 127, 0, 127, 1, this, tr( "Filter frequency" ) ), m_filterQModel( 64, 0, 127, 1, this, tr( "Filter resonance" ) ), m_bandwidthModel( 64, 0, 127, 1, this, tr( "Bandwidth" ) ), m_fmGainModel( 127, 0, 127, 1, this, tr( "FM gain" ) ), @@ -143,6 +142,11 @@ ZynAddSubFxInstrument::ZynAddSubFxInstrument( connect( instrumentTrack()->pitchRangeModel(), SIGNAL( dataChanged() ), this, SLOT( updatePitchRange() ), Qt::DirectConnection ); + + // ZynAddSubFX's internal value that LMMS's FREQ knob controls + // isn't set properly when the instrument is first loaded in, + // and doesn't update until the FREQ knob is moved + updateFilterFreq(); } @@ -329,7 +333,7 @@ QString ZynAddSubFxInstrument::nodeName() const -void ZynAddSubFxInstrument::play( sampleFrame * _buf ) +void ZynAddSubFxInstrument::play( SampleFrame* _buf ) { if (!m_pluginMutex.tryLock(Engine::getSong()->isExporting() ? -1 : 0)) {return;} if( m_remotePlugin ) @@ -452,7 +456,7 @@ void ZynAddSubFxInstrument::initPlugin() QDir( ConfigManager::inst()->factoryPresetsDir() + "/ZynAddSubFX" ).absolutePath() ) ) ); - m_remotePlugin->updateSampleRate( Engine::audioEngine()->processingSampleRate() ); + m_remotePlugin->updateSampleRate( Engine::audioEngine()->outputSampleRate() ); // temporary workaround until the VST synchronization feature gets stripped out of the RemotePluginClient class // causing not to send buffer size information requests @@ -464,7 +468,7 @@ void ZynAddSubFxInstrument::initPlugin() else { m_plugin = new LocalZynAddSubFx; - m_plugin->setSampleRate( Engine::audioEngine()->processingSampleRate() ); + m_plugin->setSampleRate( Engine::audioEngine()->outputSampleRate() ); m_plugin->setBufferSize( Engine::audioEngine()->framesPerPeriod() ); } @@ -541,7 +545,10 @@ ZynAddSubFxView::ZynAddSubFxView( Instrument * _instrument, QWidget * _parent ) m_toggleUIButton->setCheckable( true ); m_toggleUIButton->setChecked( false ); m_toggleUIButton->setIcon( embed::getIconPixmap( "zoom" ) ); - m_toggleUIButton->setFont( pointSize<8>( m_toggleUIButton->font() ) ); + QFont f = m_toggleUIButton->font(); + f.setPointSizeF(12); + m_toggleUIButton->setFont(f); + connect( m_toggleUIButton, SIGNAL( toggled( bool ) ), this, SLOT( toggleUI() ) ); diff --git a/plugins/ZynAddSubFx/ZynAddSubFx.h b/plugins/ZynAddSubFx/ZynAddSubFx.h index a391203f382..a35bc8f79ef 100644 --- a/plugins/ZynAddSubFx/ZynAddSubFx.h +++ b/plugins/ZynAddSubFx/ZynAddSubFx.h @@ -28,12 +28,12 @@ #include #include +#include + #include "AutomatableModel.h" #include "Instrument.h" #include "InstrumentView.h" #include "RemotePlugin.h" -#include "zynaddsubfx/src/globals.h" - class QPushButton; @@ -74,7 +74,7 @@ class ZynAddSubFxInstrument : public Instrument ZynAddSubFxInstrument( InstrumentTrack * _instrument_track ); ~ZynAddSubFxInstrument() override; - void play( sampleFrame * _working_buffer ) override; + void play( SampleFrame* _working_buffer ) override; bool handleMidiEvent( const MidiEvent& event, const TimePos& time = TimePos(), f_cnt_t offset = 0 ) override; @@ -86,11 +86,6 @@ class ZynAddSubFxInstrument : public Instrument QString nodeName() const override; - Flags flags() const override - { - return Flag::IsSingleStreamed | Flag::IsMidiBased; - } - gui::PluginView* instantiateView( QWidget * _parent ) override; diff --git a/plugins/ZynAddSubFx/zynaddsubfx b/plugins/ZynAddSubFx/zynaddsubfx index 7ad5663cbee..d958c3668cc 160000 --- a/plugins/ZynAddSubFx/zynaddsubfx +++ b/plugins/ZynAddSubFx/zynaddsubfx @@ -1 +1 @@ -Subproject commit 7ad5663cbeebc02d73fd3ad666e428c1287f2cda +Subproject commit d958c3668cc163805d581e97eb4d742168b6aad9 diff --git a/src/3rdparty/CMakeLists.txt b/src/3rdparty/CMakeLists.txt index f1578a9703e..e5cb6252756 100644 --- a/src/3rdparty/CMakeLists.txt +++ b/src/3rdparty/CMakeLists.txt @@ -1,24 +1,32 @@ -IF(LMMS_BUILD_LINUX AND WANT_VST) +set_directory_properties(PROPERTIES SYSTEM TRUE) + +if(LMMS_BUILD_LINUX AND LMMS_HAVE_VST) set(BUILD_SHARED_LIBS OFF) add_subdirectory(qt5-x11embed) ENDIF() +add_library(jack_headers INTERFACE) +target_include_directories(jack_headers INTERFACE jack2/common) + ADD_SUBDIRECTORY(hiir) ADD_SUBDIRECTORY(weakjack) -if(MINGW) - option(MINGW_STDTHREADS_GENERATE_STDHEADERS "" ON) - add_subdirectory(mingw-std-threads) - set(LMMS_USE_MINGW_STD_THREADS ON PARENT_SCOPE) -endif() - -# The lockless ring buffer library is compiled as part of the core -SET(RINGBUFFER_DIR "${CMAKE_SOURCE_DIR}/src/3rdparty/ringbuffer/") -SET(RINGBUFFER_DIR ${RINGBUFFER_DIR} PARENT_SCOPE) +# The lockless ring buffer library is linked as part of the core +add_library(ringbuffer OBJECT + ringbuffer/src/lib/ringbuffer.cpp +) +target_compile_features(ringbuffer PUBLIC cxx_std_17) +target_include_directories(ringbuffer PUBLIC + ringbuffer/include + "${CMAKE_CURRENT_BINARY_DIR}" +) # Create a dummy ringbuffer_export.h, since ringbuffer is not compiled as a library -FILE(WRITE ${CMAKE_BINARY_DIR}/src/ringbuffer_export.h - "#include \"${CMAKE_BINARY_DIR}/src/lmms_export.h\"\n - #define RINGBUFFER_EXPORT LMMS_EXPORT") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/ringbuffer_export.h.in" [[ + #include "${CMAKE_BINARY_DIR}/src/lmms_export.h" + #define RINGBUFFER_EXPORT LMMS_EXPORT +]]) +configure_file("${CMAKE_CURRENT_BINARY_DIR}/ringbuffer_export.h.in" ringbuffer_export.h) +target_compile_definitions(ringbuffer PRIVATE lmmsobjs_EXPORTS) # Enable MLOCK support for ringbuffer if available INCLUDE(CheckIncludeFiles) CHECK_INCLUDE_FILES(sys/mman.h HAVE_SYS_MMAN) @@ -28,5 +36,5 @@ ELSE() SET(USE_MLOCK OFF) ENDIF() # Generate ringbuffer configuration headers -CONFIGURE_FILE(${RINGBUFFER_DIR}/src/ringbuffer-config.h.in ${CMAKE_BINARY_DIR}/src/ringbuffer-config.h) -CONFIGURE_FILE(${RINGBUFFER_DIR}/src/ringbuffer-version.h.in ${CMAKE_BINARY_DIR}/src/ringbuffer-version.h) +configure_file(ringbuffer/src/ringbuffer-config.h.in ringbuffer-config.h) +configure_file(ringbuffer/src/ringbuffer-version.h.in ringbuffer-version.h) diff --git a/src/3rdparty/mingw-std-threads b/src/3rdparty/mingw-std-threads deleted file mode 160000 index 6c2061b7da4..00000000000 --- a/src/3rdparty/mingw-std-threads +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6c2061b7da41d6aa1b2162ff4383ec3ece864bc6 diff --git a/src/3rdparty/weakjack/CMakeLists.txt b/src/3rdparty/weakjack/CMakeLists.txt index 7600c3915ed..15c6d953e73 100644 --- a/src/3rdparty/weakjack/CMakeLists.txt +++ b/src/3rdparty/weakjack/CMakeLists.txt @@ -1,12 +1,14 @@ # Use weak jack library linking -IF(LMMS_HAVE_WEAKJACK) - SET(CMAKE_C_FLAGS "-std=c11") - +if(LMMS_HAVE_WEAKJACK) + add_library(weakjack STATIC + weakjack/weak_libjack.c + ) + target_include_directories(weakjack PUBLIC weakjack) + target_link_libraries(weakjack PUBLIC jack_headers ${CMAKE_DL_LIBS}) + target_compile_features(weakjack PRIVATE c_std_11) # Enable weakjack, disable metadata support - ADD_DEFINITIONS(-DUSE_WEAK_JACK=1 -DNO_JACK_METADATA=1) - - # Library stub for AppImages running on systems without jack - ADD_LIBRARY(weakjack MODULE weakjack/weak_libjack.c weakjack/weak_libjack.h) - TARGET_INCLUDE_DIRECTORIES(weakjack PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/weakjack ${JACK_INCLUDE_DIRS}) - INSTALL(TARGETS weakjack LIBRARY DESTINATION "${PLUGIN_DIR}/optional") -ENDIF() + target_compile_definitions(weakjack PUBLIC + USE_WEAK_JACK=1 + NO_JACK_METADATA=1 + ) +endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d71b34c5944..7645e49e34e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,9 +4,9 @@ CONFIGURE_FILE("lmmsconfig.h.in" "${CMAKE_BINARY_DIR}/lmmsconfig.h") CONFIGURE_FILE("lmmsversion.h.in" "${CMAKE_BINARY_DIR}/lmmsversion.h") SET(LMMS_SRCS "") -SET(LMMS_UIS "") SET(CMAKE_AUTOMOC ON) +SET(CMAKE_AUTOUIC ON) SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) # Enable C++17 @@ -23,14 +23,12 @@ ADD_SUBDIRECTORY(tracks) LIST(APPEND LMMS_SRCS ${LMMS_COMMON_SRCS}) -QT5_WRAP_UI(LMMS_UI_OUT ${LMMS_UIS}) INCLUDE_DIRECTORIES( "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_BINARY_DIR}" "${CMAKE_BINARY_DIR}/include" "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/include" - "${RINGBUFFER_DIR}/include" ) IF(WIN32 AND MSVC) @@ -56,39 +54,27 @@ ADD_GEN_QRC(LMMS_RCC_OUT lmms.qrc FILE(RELATIVE_PATH LIB_DIR_RELATIVE "/${BIN_DIR}" "/${LIB_DIR}") FILE(RELATIVE_PATH PLUGIN_DIR_RELATIVE "/${BIN_DIR}" "/${PLUGIN_DIR}") ADD_DEFINITIONS(-DLIB_DIR="${LIB_DIR_RELATIVE}" -DPLUGIN_DIR="${PLUGIN_DIR_RELATIVE}" ${PULSEAUDIO_DEFINITIONS}) -INCLUDE_DIRECTORIES( +include_directories(SYSTEM ${JACK_INCLUDE_DIRS} ${SNDIO_INCLUDE_DIRS} ${FFTW3F_INCLUDE_DIRS} ) -IF(NOT LMMS_HAVE_SDL2 AND NOT ("${SDL_INCLUDE_DIR}" STREQUAL "")) - INCLUDE_DIRECTORIES("${SDL_INCLUDE_DIR}") -ENDIF() - -IF(LMMS_HAVE_WEAKJACK) - LIST(APPEND LMMS_SRCS "${WEAKJACK_INCLUDE_DIRS}/weak_libjack.c") - LIST(APPEND LMMS_INCLUDES "${WEAKJACK_INCLUDE_DIRS}/weak_libjack.h") - INCLUDE_DIRECTORIES("${WEAKJACK_INCLUDE_DIRS}") - ADD_DEFINITIONS(-DUSE_WEAK_JACK=1 -DNO_JACK_METADATA=1) -ENDIF() - IF(NOT ("${PULSEAUDIO_INCLUDE_DIR}" STREQUAL "")) - INCLUDE_DIRECTORIES("${PULSEAUDIO_INCLUDE_DIR}") + include_directories(SYSTEM "${PULSEAUDIO_INCLUDE_DIR}") ENDIF() IF(NOT ("${LV2_INCLUDE_DIRS}" STREQUAL "")) - INCLUDE_DIRECTORIES(${LV2_INCLUDE_DIRS}) + include_directories(SYSTEM ${LV2_INCLUDE_DIRS}) ENDIF() IF(NOT ("${LILV_INCLUDE_DIRS}" STREQUAL "")) - INCLUDE_DIRECTORIES(${LILV_INCLUDE_DIRS}) + include_directories(SYSTEM ${LILV_INCLUDE_DIRS}) ENDIF() IF(NOT ("${SUIL_INCLUDE_DIRS}" STREQUAL "")) - INCLUDE_DIRECTORIES(${SUIL_INCLUDE_DIRS}) + include_directories(SYSTEM ${SUIL_INCLUDE_DIRS}) ENDIF() -LIST(APPEND LMMS_SRCS "${RINGBUFFER_DIR}/src/lib/ringbuffer.cpp") # Use libraries in non-standard directories (e.g., another version of Qt) IF(LMMS_BUILD_LINUX) @@ -100,7 +86,6 @@ SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) ADD_LIBRARY(lmmsobjs OBJECT ${LMMS_SRCS} ${LMMS_INCLUDES} - ${LMMS_UI_OUT} ${LMMS_RCC_OUT} ) @@ -110,23 +95,20 @@ GENERATE_EXPORT_HEADER(lmmsobjs ADD_EXECUTABLE(lmms core/main.cpp - $ "${WINRC}" ) TARGET_INCLUDE_DIRECTORIES(lmms PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ) +target_static_libraries(lmms PUBLIC lmmsobjs) # CMake doesn't define target_EXPORTS for OBJECT libraries. # See the documentation of DEFINE_SYMBOL for details. # Also add LMMS_STATIC_DEFINE for targets linking against it. TARGET_COMPILE_DEFINITIONS(lmmsobjs PRIVATE -Dlmmsobjs_EXPORTS - INTERFACE -DLMMS_STATIC_DEFINE -) -TARGET_COMPILE_DEFINITIONS(lmms - PRIVATE $ ) +target_static_definitions(lmmsobjs LMMS_STATIC_DEFINE) # Set Visual Studio startup project to lmms # https://stackoverflow.com/a/37994396/8166701 @@ -134,7 +116,7 @@ IF(NOT CMAKE_VERSION VERSION_LESS 3.6) SET_PROPERTY(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT lmms) ENDIF() -SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${LMMS_RCC_OUT} ${LMMS_UI_OUT} lmmsconfig.h lmms.1.gz") +SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${LMMS_RCC_OUT} lmmsconfig.h lmms.1.gz") IF(LMMS_BUILD_WIN32) SET(EXTRA_LIBRARIES "winmm") @@ -168,10 +150,6 @@ if(LMMS_HAVE_OGGVORBIS) list(APPEND EXTRA_LIBRARIES Vorbis::vorbisenc Vorbis::vorbisfile) endif() -if(LMMS_USE_MINGW_STD_THREADS) - list(APPEND EXTRA_LIBRARIES mingw_stdthreads) -endif() - SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS} ${CMAKE_THREAD_LIBS_INIT} ${QT_LIBRARIES} @@ -191,34 +169,19 @@ SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS} ${EXTRA_LIBRARIES} ) -# Expose required libs for tests binary -SET(LMMS_REQUIRED_LIBS ${LMMS_REQUIRED_LIBS} PARENT_SCOPE) - -TARGET_LINK_LIBRARIES(lmms +target_link_libraries(lmmsobjs ${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() +target_static_libraries(lmmsobjs ringbuffer) set_target_properties(lmms PROPERTIES ENABLE_EXPORTS ON WIN32_EXECUTABLE $> ) -IF(LMMS_BUILD_MSYS) - # ENABLE_EXPORTS property has no effect in some MSYS2 configurations. - # Add the linker flag manually to create liblmms.dll.a import library - SET_PROPERTY(TARGET lmms - APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--out-implib,liblmms.dll.a" - ) -ELSEIF(NOT WIN32) +set_target_properties(lmmsobjs PROPERTIES AUTOUIC_SEARCH_PATHS "gui/modals") + +IF(NOT WIN32) if(CMAKE_INSTALL_MANDIR) SET(INSTALL_MANDIR ${CMAKE_INSTALL_MANDIR}) ELSE(CMAKE_INSTALL_MANDIR) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 877bea3fcc8..9f349f09103 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -1,6 +1,7 @@ set(COMMON_SRCS RemotePluginBase.cpp SharedMemory.cpp + SystemSemaphore.cpp ) foreach(SRC ${COMMON_SRCS}) diff --git a/src/common/SharedMemory.cpp b/src/common/SharedMemory.cpp index 005e726ed34..19d3dba1bbe 100644 --- a/src/common/SharedMemory.cpp +++ b/src/common/SharedMemory.cpp @@ -23,45 +23,44 @@ #include "SharedMemory.h" +#include +#include + #include "lmmsconfig.h" +#include "RaiiHelpers.h" #ifdef LMMS_HAVE_UNISTD_H # include #endif -#if _POSIX_SHARED_MEMORY_OBJECTS > 0 -# include -# +#if _POSIX_SHARED_MEMORY_OBJECTS > 0 || defined(LMMS_BUILD_APPLE) # include # include # include -# -# include "RaiiHelpers.h" +#elif defined(LMMS_BUILD_WIN32) +# include #else -# include -# -# include -# include +# error "No shared memory implementation available" #endif - namespace lmms::detail { -#if _POSIX_SHARED_MEMORY_OBJECTS > 0 - +#if _POSIX_SHARED_MEMORY_OBJECTS > 0 || defined(LMMS_BUILD_APPLE) namespace { +[[noreturn]] void throwSystemError(const char* message) +{ + throw std::system_error{errno, std::generic_category(), message}; +} + template int retryWhileInterrupted(F&& function) noexcept(std::is_nothrow_invocable_v) { - int result; - do - { - result = function(); + while (true) { + const auto result = function(); + if (result != -1 || errno != EINTR) { return result; } } - while (result == -1 && errno == EINTR); - return result; } void deleteFileDescriptor(int fd) noexcept { retryWhileInterrupted([fd]() noexcept { return close(fd); }); } @@ -82,22 +81,15 @@ class SharedMemoryImpl const auto fd = FileDescriptor{ retryWhileInterrupted([&]() noexcept { return shm_open(m_key.c_str(), openFlags, 0); }) }; - if (!fd) - { - throw std::system_error{errno, std::generic_category(), "SharedMemoryImpl: shm_open() failed"}; - } + if (!fd) { throwSystemError("SharedMemoryImpl: shm_open() failed"); } + auto stat = (struct stat){}; - if (fstat(fd.get(), &stat) == -1) - { - throw std::system_error{errno, std::generic_category(), "SharedMemoryImpl: fstat() failed"}; - } + if (fstat(fd.get(), &stat) == -1) { throwSystemError("SharedMemoryImpl: fstat() failed"); } m_size = stat.st_size; + const auto mappingProtection = readOnly ? PROT_READ : PROT_READ | PROT_WRITE; m_mapping = mmap(nullptr, m_size, mappingProtection, MAP_SHARED, fd.get(), 0); - if (m_mapping == MAP_FAILED) - { - throw std::system_error{errno, std::generic_category(), "SharedMemoryImpl: mmap() failed"}; - } + if (m_mapping == MAP_FAILED) { throwSystemError("SharedMemoryImpl: mmap() failed"); } } SharedMemoryImpl(const std::string& key, std::size_t size, bool readOnly) : @@ -107,21 +99,16 @@ class SharedMemoryImpl const auto fd = FileDescriptor{ retryWhileInterrupted([&]() noexcept { return shm_open(m_key.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600); }) }; - if (fd.get() == -1) - { - throw std::system_error{errno, std::generic_category(), "SharedMemoryImpl: shm_open() failed"}; - } + if (fd.get() == -1) { throwSystemError("SharedMemoryImpl: shm_open() failed"); } m_object.reset(m_key.c_str()); - if (retryWhileInterrupted([&]() noexcept { return ftruncate(fd.get(), m_size); }) == -1) - { - throw std::system_error{errno, std::generic_category(), "SharedMemoryImpl: ftruncate() failed"}; + + if (retryWhileInterrupted([&]() noexcept { return ftruncate(fd.get(), m_size); }) == -1) { + throwSystemError("SharedMemoryImpl: ftruncate() failed"); } + const auto mappingProtection = readOnly ? PROT_READ : PROT_READ | PROT_WRITE; m_mapping = mmap(nullptr, m_size, mappingProtection, MAP_SHARED, fd.get(), 0); - if (m_mapping == MAP_FAILED) - { - throw std::system_error{errno, std::generic_category(), "SharedMemoryImpl: mmap() failed"}; - } + if (m_mapping == MAP_FAILED) { throwSystemError("SharedMemoryImpl: mmap() failed"); } } SharedMemoryImpl(const SharedMemoryImpl&) = delete; @@ -132,7 +119,7 @@ class SharedMemoryImpl munmap(m_mapping, m_size); } - void* get() { return m_mapping; } + auto get() const noexcept -> void* { return m_mapping; } private: std::string m_key; @@ -141,38 +128,68 @@ class SharedMemoryImpl ShmObject m_object; }; -#else +#elif defined(LMMS_BUILD_WIN32) + +namespace { + +template +auto sizeToHighAndLow(T size) -> std::pair +{ + if constexpr (sizeof(T) <= sizeof(DWORD)) { + return {0, size}; + } else { + return {static_cast(size >> 32), static_cast(size)}; + } +} + +[[noreturn]] void throwLastError(const char* message) +{ + throw std::system_error{static_cast(GetLastError()), std::system_category(), message}; +} + +using UniqueHandle = UniqueNullableResource; +using FileView = UniqueNullableResource; + +} // namespace class SharedMemoryImpl { public: - SharedMemoryImpl(const std::string& key, bool readOnly) : - m_shm{QString::fromStdString(key)} + SharedMemoryImpl(const std::string& key, bool readOnly) { - const auto mode = readOnly ? QSharedMemory::ReadOnly : QSharedMemory::ReadWrite; - if (!m_shm.attach(mode)) - { - throw std::runtime_error{"SharedMemoryImpl: QSharedMemory::attach() failed"}; - } + const auto access = readOnly ? FILE_MAP_READ : FILE_MAP_WRITE; + m_mapping.reset(OpenFileMappingA(access, false, key.c_str())); + if (!m_mapping) { throwLastError("SharedMemoryImpl: OpenFileMappingA() failed"); } + + m_view.reset(MapViewOfFile(m_mapping.get(), access, 0, 0, 0)); + if (!m_view) { throwLastError("SharedMemoryImpl: MapViewOfFile() failed"); } } - SharedMemoryImpl(const std::string& key, std::size_t size, bool readOnly) : - m_shm{QString::fromStdString(key)} + SharedMemoryImpl(const std::string& key, std::size_t size, bool readOnly) { - const auto mode = readOnly ? QSharedMemory::ReadOnly : QSharedMemory::ReadWrite; - if (!m_shm.create(size, mode)) - { - throw std::runtime_error{"SharedMemoryImpl: QSharedMemory::create() failed"}; + const auto [high, low] = sizeToHighAndLow(size); + m_mapping.reset(CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, high, low, key.c_str())); + // This constructor is supposed to create a new shared memory object, + // but passing the name of an existing object causes CreateFileMappingA + // to succeed and return a handle to that object. Thus we have to check + // GetLastError() too. + if (!m_mapping || GetLastError() == ERROR_ALREADY_EXISTS) { + throwLastError("SharedMemoryImpl: CreateFileMappingA() failed"); } + + const auto access = readOnly ? FILE_MAP_READ : FILE_MAP_WRITE; + m_view.reset(MapViewOfFile(m_mapping.get(), access, 0, 0, 0)); + if (!m_view) { throwLastError("SharedMemoryImpl: MapViewOfFile() failed"); } } SharedMemoryImpl(const SharedMemoryImpl&) = delete; - SharedMemoryImpl& operator=(const SharedMemoryImpl&) = delete; + auto operator=(const SharedMemoryImpl&) -> SharedMemoryImpl& = delete; - void* get() { return m_shm.data(); } + auto get() const noexcept -> void* { return m_view.get(); } private: - QSharedMemory m_shm; + UniqueHandle m_mapping; + FileView m_view; }; #endif @@ -196,7 +213,7 @@ SharedMemoryData::~SharedMemoryData() = default; SharedMemoryData::SharedMemoryData(SharedMemoryData&& other) noexcept : m_key{std::move(other.m_key)}, m_impl{std::move(other.m_impl)}, - m_ptr{other.m_ptr} + m_ptr{std::exchange(other.m_ptr, nullptr)} { } } // namespace lmms::detail diff --git a/src/common/SystemSemaphore.cpp b/src/common/SystemSemaphore.cpp new file mode 100644 index 00000000000..02c9a28887a --- /dev/null +++ b/src/common/SystemSemaphore.cpp @@ -0,0 +1,181 @@ +/* + * SystemSemaphore.cpp + * + * Copyright (c) 2024 Dominic Clark + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + */ + +#include "SystemSemaphore.h" + +#include +#include +#include +#include + +#include "lmmsconfig.h" +#include "RaiiHelpers.h" + +#ifdef LMMS_HAVE_UNISTD_H +# include +#endif + +#if (_POSIX_SEMAPHORES > 0 && !defined(__MINGW32__)) || defined(LMMS_BUILD_APPLE) +# include +# include +#elif defined(LMMS_BUILD_WIN32) +# include +#else +# error "No system semaphore implementation available" +#endif + +namespace lmms { + +namespace detail { + +#if (_POSIX_SEMAPHORES > 0 && !defined(__MINGW32__)) || defined(LMMS_BUILD_APPLE) + +namespace { + +[[noreturn]] void throwSystemError(const char* message) +{ + throw std::system_error{errno, std::generic_category(), message}; +} + +template +auto retryWhileInterrupted(F&& function, std::invoke_result_t error = -1) + noexcept(std::is_nothrow_invocable_v) -> auto +{ + while (true) + { + const auto result = function(); + if (result != error || errno != EINTR) { return result; } + } +} + +using UniqueSemaphore = UniqueNullableResource; + +} // namespace + +class SystemSemaphoreImpl +{ +public: + SystemSemaphoreImpl(const std::string& key, unsigned int value) : + m_key{"/" + key}, + m_sem{retryWhileInterrupted([&]() noexcept { + return sem_open(m_key.c_str(), O_CREAT | O_EXCL, 0600, value); + }, SEM_FAILED)} + { + if (m_sem == SEM_FAILED) { throwSystemError("SystemSemaphoreImpl: sem_open() failed"); } + m_ownedSemaphore.reset(m_key.c_str()); + } + + explicit SystemSemaphoreImpl(const std::string& key) : + m_key{"/" + key}, + m_sem{retryWhileInterrupted([&]() noexcept { + return sem_open(m_key.c_str(), 0); + }, SEM_FAILED)} + { + if (m_sem == SEM_FAILED) { throwSystemError("SystemSemaphoreImpl: sem_open() failed"); } + } + + ~SystemSemaphoreImpl() + { + // We can't use `UniqueNullableResource` for `m_sem`, as the null value + // (`SEM_FAILED`) is not a constant expression on macOS (it's defined as + // `(sem_t*) -1`), so can't be used as a template parameter. + sem_close(m_sem); + } + + auto acquire() noexcept -> bool + { + return retryWhileInterrupted([&]() noexcept { + return sem_wait(m_sem); + }) == 0; + } + + auto release() noexcept -> bool { return sem_post(m_sem) == 0; } + +private: + std::string m_key; + sem_t* m_sem; + UniqueSemaphore m_ownedSemaphore; +}; + +#elif defined(LMMS_BUILD_WIN32) + +namespace { + +[[noreturn]] void throwSystemError(const char* message) +{ + throw std::system_error{static_cast(GetLastError()), std::system_category(), message}; +} + +using UniqueHandle = UniqueNullableResource; + +} // namespace + +class SystemSemaphoreImpl +{ +public: + SystemSemaphoreImpl(const std::string& key, unsigned int value) : + m_sem{CreateSemaphoreA(nullptr, value, std::numeric_limits::max(), key.c_str())} + { + if (!m_sem || GetLastError() == ERROR_ALREADY_EXISTS) { + throwSystemError("SystemSemaphoreImpl: CreateSemaphoreA failed"); + } + } + + explicit SystemSemaphoreImpl(const std::string& key) : + m_sem{OpenSemaphoreA(SEMAPHORE_MODIFY_STATE | SYNCHRONIZE, false, key.c_str())} + { + if (!m_sem) { throwSystemError("SystemSemaphoreImpl: OpenSemaphoreA failed"); } + } + + auto acquire() noexcept -> bool { return WaitForSingleObject(m_sem.get(), INFINITE) == WAIT_OBJECT_0; } + auto release() noexcept -> bool { return ReleaseSemaphore(m_sem.get(), 1, nullptr); } + +private: + UniqueHandle m_sem; +}; + +#endif + +} // namespace detail + +SystemSemaphore::SystemSemaphore() noexcept = default; + +SystemSemaphore::SystemSemaphore(std::string key, unsigned int value) : + m_key{std::move(key)}, + m_impl{std::make_unique(m_key, value)} +{} + +SystemSemaphore::SystemSemaphore(std::string key) : + m_key{std::move(key)}, + m_impl{std::make_unique(m_key)} +{} + +SystemSemaphore::~SystemSemaphore() = default; + +SystemSemaphore::SystemSemaphore(SystemSemaphore&& other) noexcept = default; +auto SystemSemaphore::operator=(SystemSemaphore&& other) noexcept -> SystemSemaphore& = default; + +auto SystemSemaphore::acquire() noexcept -> bool { return m_impl->acquire(); } +auto SystemSemaphore::release() noexcept -> bool { return m_impl->release(); } + +} // namespace lmms diff --git a/src/core/AudioEngine.cpp b/src/core/AudioEngine.cpp index 04e3c7c7ce5..42e7079b016 100644 --- a/src/core/AudioEngine.cpp +++ b/src/core/AudioEngine.cpp @@ -37,7 +37,6 @@ #include "NotePlayHandle.h" #include "ConfigManager.h" #include "SamplePlayHandle.h" -#include "MemoryHelper.h" // platform-specific audio-interface-classes #include "AudioAlsa.h" @@ -67,8 +66,7 @@ namespace lmms using LocklessListElement = LocklessList::Element; -static thread_local bool s_renderingThread; -static thread_local bool s_runningChange; +static thread_local bool s_renderingThread = false; @@ -83,7 +81,7 @@ AudioEngine::AudioEngine( bool renderOnly ) : m_workers(), m_numWorkers( QThread::idealThreadCount()-1 ), m_newPlayHandles( PlayHandle::MaxNumber ), - m_qualitySettings( qualitySettings::Mode::Draft ), + m_qualitySettings(qualitySettings::Interpolation::Linear), m_masterGain( 1.0f ), m_audioDev( nullptr ), m_oldAudioDev( nullptr ), @@ -96,8 +94,8 @@ AudioEngine::AudioEngine( bool renderOnly ) : { m_inputBufferFrames[i] = 0; m_inputBufferSize[i] = DEFAULT_BUFFER_SIZE * 100; - m_inputBuffer[i] = new sampleFrame[ DEFAULT_BUFFER_SIZE * 100 ]; - BufferManager::clear( m_inputBuffer[i], m_inputBufferSize[i] ); + m_inputBuffer[i] = new SampleFrame[ DEFAULT_BUFFER_SIZE * 100 ]; + zeroSampleFrames(m_inputBuffer[i], m_inputBufferSize[i]); } // determine FIFO size and number of frames per period @@ -137,12 +135,9 @@ AudioEngine::AudioEngine( bool renderOnly ) : // now that framesPerPeriod is fixed initialize global BufferManager BufferManager::init( m_framesPerPeriod ); - int outputBufferSize = m_framesPerPeriod * sizeof(surroundSampleFrame); - m_outputBufferRead = static_cast(MemoryHelper::alignedMalloc(outputBufferSize)); - m_outputBufferWrite = static_cast(MemoryHelper::alignedMalloc(outputBufferSize)); + m_outputBufferRead = std::make_unique(m_framesPerPeriod); + m_outputBufferWrite = std::make_unique(m_framesPerPeriod); - BufferManager::clear(m_outputBufferRead, m_framesPerPeriod); - BufferManager::clear(m_outputBufferWrite, m_framesPerPeriod); for( int i = 0; i < m_numWorkers+1; ++i ) { @@ -181,8 +176,6 @@ AudioEngine::~AudioEngine() delete m_midiClient; delete m_audioDev; - MemoryHelper::alignedFree(m_outputBufferRead); - MemoryHelper::alignedFree(m_outputBufferWrite); for (const auto& input : m_inputBuffer) { @@ -277,17 +270,6 @@ sample_rate_t AudioEngine::inputSampleRate() const baseSampleRate(); } - - - -sample_rate_t AudioEngine::processingSampleRate() const -{ - return outputSampleRate() * m_qualitySettings.sampleRateMultiplier(); -} - - - - bool AudioEngine::criticalXRuns() const { return cpuLoad() >= 99 && Engine::getSong()->isExporting() == false; @@ -296,19 +278,19 @@ bool AudioEngine::criticalXRuns() const -void AudioEngine::pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames ) +void AudioEngine::pushInputFrames( SampleFrame* _ab, const f_cnt_t _frames ) { requestChangeInModel(); f_cnt_t frames = m_inputBufferFrames[ m_inputBufferWrite ]; - int size = m_inputBufferSize[ m_inputBufferWrite ]; - sampleFrame * buf = m_inputBuffer[ m_inputBufferWrite ]; + auto size = m_inputBufferSize[m_inputBufferWrite]; + SampleFrame* buf = m_inputBuffer[ m_inputBufferWrite ]; if( frames + _frames > size ) { size = std::max(size * 2, frames + _frames); - auto ab = new sampleFrame[size]; - memcpy( ab, buf, frames * sizeof( sampleFrame ) ); + auto ab = new SampleFrame[size]; + memcpy( ab, buf, frames * sizeof( SampleFrame ) ); delete [] buf; m_inputBufferSize[ m_inputBufferWrite ] = size; @@ -317,7 +299,7 @@ void AudioEngine::pushInputFrames( sampleFrame * _ab, const f_cnt_t _frames ) buf = ab; } - memcpy( &buf[ frames ], _ab, _frames * sizeof( sampleFrame ) ); + memcpy( &buf[ frames ], _ab, _frames * sizeof( SampleFrame ) ); m_inputBufferFrames[ m_inputBufferWrite ] += _frames; doneChangeInModel(); @@ -432,11 +414,11 @@ void AudioEngine::renderStageMix() AudioEngineProfiler::Probe profilerProbe(m_profiler, AudioEngineProfiler::DetailType::Mixing); Mixer *mixer = Engine::mixer(); - mixer->masterMix(m_outputBufferWrite); + mixer->masterMix(m_outputBufferWrite.get()); - MixHelpers::multiply(m_outputBufferWrite, m_masterGain, m_framesPerPeriod); + MixHelpers::multiply(m_outputBufferWrite.get(), m_masterGain, m_framesPerPeriod); - emit nextAudioBuffer(m_outputBufferRead); + emit nextAudioBuffer(m_outputBufferRead.get()); // and trigger LFOs EnvelopeAndLfoParameters::instances()->trigger(); @@ -446,7 +428,7 @@ void AudioEngine::renderStageMix() -const surroundSampleFrame *AudioEngine::renderNextBuffer() +const SampleFrame* AudioEngine::renderNextBuffer() { const auto lock = std::lock_guard{m_changeMutex}; @@ -459,9 +441,9 @@ const surroundSampleFrame *AudioEngine::renderNextBuffer() renderStageMix(); // STAGE 3: do master mix in mixer s_renderingThread = false; - m_profiler.finishPeriod(processingSampleRate(), m_framesPerPeriod); + m_profiler.finishPeriod(outputSampleRate(), m_framesPerPeriod); - return m_outputBufferRead; + return m_outputBufferRead.get(); } @@ -474,7 +456,7 @@ void AudioEngine::swapBuffers() m_inputBufferFrames[m_inputBufferWrite] = 0; std::swap(m_outputBufferRead, m_outputBufferWrite); - BufferManager::clear(m_outputBufferWrite, m_framesPerPeriod); + zeroSampleFrames(m_outputBufferWrite.get(), m_framesPerPeriod); } @@ -565,39 +547,12 @@ void AudioEngine::clearInternal() -AudioEngine::StereoSample AudioEngine::getPeakValues(sampleFrame * ab, const f_cnt_t frames) const -{ - sample_t peakLeft = 0.0f; - sample_t peakRight = 0.0f; - - for (f_cnt_t f = 0; f < frames; ++f) - { - float const absLeft = std::abs(ab[f][0]); - float const absRight = std::abs(ab[f][1]); - if (absLeft > peakLeft) - { - peakLeft = absLeft; - } - - if (absRight > peakRight) - { - peakRight = absRight; - } - } - - return StereoSample(peakLeft, peakRight); -} - - - - void AudioEngine::changeQuality(const struct qualitySettings & qs) { // don't delete the audio-device stopProcessing(); m_qualitySettings = qs; - m_audioDev->applyQualitySettings(); emit sampleRateChanged(); emit qualitySettingsChanged(); @@ -802,16 +757,14 @@ void AudioEngine::removePlayHandlesOfTypes(Track * track, PlayHandle::Types type void AudioEngine::requestChangeInModel() { - if (s_renderingThread || s_runningChange) { return; } + if (s_renderingThread) { return; } m_changeMutex.lock(); - s_runningChange = true; } void AudioEngine::doneChangeInModel() { - if (s_renderingThread || !s_runningChange) { return; } + if (s_renderingThread) { return; } m_changeMutex.unlock(); - s_runningChange = false; } bool AudioEngine::isAudioDevNameValid(QString name) @@ -1244,9 +1197,9 @@ void AudioEngine::fifoWriter::run() const fpp_t frames = m_audioEngine->framesPerPeriod(); while( m_writing ) { - auto buffer = new surroundSampleFrame[frames]; - const surroundSampleFrame * b = m_audioEngine->renderNextBuffer(); - memcpy( buffer, b, frames * sizeof( surroundSampleFrame ) ); + auto buffer = new SampleFrame[frames]; + const SampleFrame* b = m_audioEngine->renderNextBuffer(); + memcpy(buffer, b, frames * sizeof(SampleFrame)); m_fifo->write(buffer); } diff --git a/src/core/AudioEngineWorkerThread.cpp b/src/core/AudioEngineWorkerThread.cpp index ae459c5e484..70f7760419f 100644 --- a/src/core/AudioEngineWorkerThread.cpp +++ b/src/core/AudioEngineWorkerThread.cpp @@ -79,7 +79,7 @@ void AudioEngineWorkerThread::JobQueue::run() while (processedJob && m_itemsDone < m_writeIndex) { processedJob = false; - for( int i = 0; i < m_writeIndex && i < JOB_QUEUE_SIZE; ++i ) + for (auto i = std::size_t{0}; i < m_writeIndex && i < JOB_QUEUE_SIZE; ++i) { ThreadableJob * job = m_items[i].exchange(nullptr); if( job ) diff --git a/src/core/AudioResampler.cpp b/src/core/AudioResampler.cpp index 5f6b6a23955..8fb7d95a2aa 100644 --- a/src/core/AudioResampler.cpp +++ b/src/core/AudioResampler.cpp @@ -61,4 +61,9 @@ auto AudioResampler::resample(const float* in, long inputFrames, float* out, lon return {src_process(m_state, &data), data.input_frames_used, data.output_frames_gen}; } +void AudioResampler::setRatio(double ratio) +{ + src_set_ratio(m_state, ratio); +} + } // namespace lmms diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index c701f28e36d..5ce259dc2b0 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -354,8 +354,7 @@ float AutomatableModel::inverseScaledValue( float value ) const template void roundAt( T& value, const T& where, const T& step_size ) { - if (std::abs(value - where) - < typeInfo::minEps() * std::abs(step_size)) + if (std::abs(value - where) < F_EPSILON * std::abs(step_size)) { value = where; } @@ -583,7 +582,7 @@ float AutomatableModel::controllerValue( int frameOffset ) const "lacks implementation for a scale type"); break; } - if( typeInfo::isEqual( m_step, 1 ) && m_hasStrictStepSize ) + if (approximatelyEqual(m_step, 1) && m_hasStrictStepSize) { return std::round(v); } @@ -613,10 +612,9 @@ ValueBuffer * AutomatableModel::valueBuffer() float val = m_value; // make sure our m_value doesn't change midway - ValueBuffer * vb; if (m_controllerConnection && m_useControllerValue && m_controllerConnection->getController()->isSampleExact()) { - vb = m_controllerConnection->valueBuffer(); + auto vb = m_controllerConnection->valueBuffer(); if( vb ) { float * values = vb->values(); @@ -656,7 +654,7 @@ ValueBuffer * AutomatableModel::valueBuffer() if (lm && lm->controllerConnection() && lm->useControllerValue() && lm->controllerConnection()->getController()->isSampleExact()) { - vb = lm->valueBuffer(); + auto vb = lm->valueBuffer(); float * values = vb->values(); float * nvalues = m_valueBuffer.values(); for (int i = 0; i < vb->length(); i++) diff --git a/src/core/AutomationClip.cpp b/src/core/AutomationClip.cpp index 6035502704d..dc496fa044a 100644 --- a/src/core/AutomationClip.cpp +++ b/src/core/AutomationClip.cpp @@ -189,7 +189,7 @@ const AutomatableModel * AutomationClip::firstObject() const return model; } - static FloatModel fm(0, DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, 0.001); + static FloatModel fm(0, DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, 0.001f); return &fm; } @@ -1222,11 +1222,9 @@ void AutomationClip::generateTangents(timeMap::iterator it, int numToGenerate) // TODO: This behavior means that a very small difference between the inValue and outValue can // result in a big change in the curve. In the future, allowing the user to manually adjust // the tangents would be better. - float inTangent; - float outTangent; if (OFFSET(it) == 0) { - inTangent = (INVAL(it + 1) - OUTVAL(it - 1)) / (POS(it + 1) - POS(it - 1)); + float inTangent = (INVAL(it + 1) - OUTVAL(it - 1)) / (POS(it + 1) - POS(it - 1)); it.value().setInTangent(inTangent); // inTangent == outTangent in this case it.value().setOutTangent(inTangent); @@ -1234,9 +1232,9 @@ void AutomationClip::generateTangents(timeMap::iterator it, int numToGenerate) else { // Calculate the left side of the curve - inTangent = (INVAL(it) - OUTVAL(it - 1)) / (POS(it) - POS(it - 1)); + float inTangent = (INVAL(it) - OUTVAL(it - 1)) / (POS(it) - POS(it - 1)); // Calculate the right side of the curve - outTangent = (INVAL(it + 1) - OUTVAL(it)) / (POS(it + 1) - POS(it)); + float outTangent = (INVAL(it + 1) - OUTVAL(it)) / (POS(it + 1) - POS(it)); it.value().setInTangent(inTangent); it.value().setOutTangent(outTangent); } diff --git a/src/core/BandLimitedWave.cpp b/src/core/BandLimitedWave.cpp index cb09dc5b8f5..060ff510a43 100644 --- a/src/core/BandLimitedWave.cpp +++ b/src/core/BandLimitedWave.cpp @@ -49,11 +49,11 @@ QDataStream& operator<< ( QDataStream &out, WaveMipMap &waveMipMap ) QDataStream& operator>> ( QDataStream &in, WaveMipMap &waveMipMap ) { - sample_t sample; for( int tbl = 0; tbl <= MAXTBL; tbl++ ) { for( int i = 0; i < TLENS[tbl]; i++ ) { + sample_t sample; in >> sample; waveMipMap.setSampleAt( tbl, i, sample ); } @@ -67,9 +67,8 @@ void BandLimitedWave::generateWaves() // don't generate if they already exist if( s_wavesGenerated ) return; - int i; -// set wavetable directory + // set wavetable directory s_wavetableDir = "data:wavetables/"; // set wavetable files @@ -89,7 +88,7 @@ void BandLimitedWave::generateWaves() } else { - for( i = 0; i <= MAXTBL; i++ ) + for (int i = 0; i <= MAXTBL; i++) { const int len = TLENS[i]; //const double om = 1.0 / len; @@ -131,7 +130,7 @@ void BandLimitedWave::generateWaves() } else { - for( i = 0; i <= MAXTBL; i++ ) + for (int i = 0; i <= MAXTBL; i++) { const int len = TLENS[i]; //const double om = 1.0 / len; @@ -172,7 +171,7 @@ void BandLimitedWave::generateWaves() } else { - for( i = 0; i <= MAXTBL; i++ ) + for (int i = 0; i <= MAXTBL; i++) { const int len = TLENS[i]; //const double om = 1.0 / len; @@ -215,7 +214,7 @@ void BandLimitedWave::generateWaves() } else { - for( i = 0; i <= MAXTBL; i++ ) + for (int i = 0; i <= MAXTBL; i++) { const int len = TLENS[i]; diff --git a/src/core/BufferManager.cpp b/src/core/BufferManager.cpp index 2362be85a3d..47598c63325 100644 --- a/src/core/BufferManager.cpp +++ b/src/core/BufferManager.cpp @@ -26,6 +26,8 @@ #include "BufferManager.h" +#include "SampleFrame.h" + #include @@ -40,26 +42,14 @@ void BufferManager::init( fpp_t fpp ) } -sampleFrame * BufferManager::acquire() -{ - return new sampleFrame[s_framesPerPeriod]; -} - -void BufferManager::clear( sampleFrame *ab, const f_cnt_t frames, const f_cnt_t offset ) +SampleFrame* BufferManager::acquire() { - memset( ab + offset, 0, sizeof( *ab ) * frames ); + return new SampleFrame[s_framesPerPeriod]; } -#ifndef LMMS_DISABLE_SURROUND -void BufferManager::clear( surroundSampleFrame * ab, const f_cnt_t frames, - const f_cnt_t offset ) -{ - memset( ab + offset, 0, sizeof( *ab ) * frames ); -} -#endif -void BufferManager::release( sampleFrame * buf ) +void BufferManager::release( SampleFrame* buf ) { delete[] buf; } diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 1a4871fc72b..3608d28486a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -23,6 +23,7 @@ set(LMMS_SRCS core/Engine.cpp core/EnvelopeAndLfoParameters.cpp core/fft_helpers.cpp + core/FileSearch.cpp core/Mixer.cpp core/ImportFilter.cpp core/InlineAutomation.cpp @@ -38,7 +39,6 @@ set(LMMS_SRCS core/LfoController.cpp core/LinkedModelGroups.cpp core/LocklessAllocator.cpp - core/MemoryHelper.cpp core/MeterModel.cpp core/MicroTimer.cpp core/Microtuner.cpp @@ -76,6 +76,7 @@ set(LMMS_SRCS core/SerializingObject.cpp core/Song.cpp core/TempoSyncKnobModel.cpp + core/ThreadPool.cpp core/Timeline.cpp core/TimePos.cpp core/ToolPlugin.cpp diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 61d84770a13..d3c973020bf 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -188,13 +188,17 @@ QString ConfigManager::defaultVersion() const return LMMS_VERSION; } +bool ConfigManager::enableBlockedPlugins() +{ + const char* envVar = getenv("LMMS_ENABLE_BLOCKED_PLUGINS"); + return (envVar && *envVar); +} + QStringList ConfigManager::availableVstEmbedMethods() { QStringList methods; methods.append("none"); -#if QT_VERSION >= 0x050100 methods.append("qt"); -#endif #ifdef LMMS_BUILD_WIN32 methods.append("win32"); #endif @@ -514,7 +518,7 @@ void ConfigManager::loadConfigFile(const QString & configFile) cfg_file.close(); } - // Plugins are searched recursively, blacklist problematic locations + // Plugins are searched recursively, block problematic locations if( m_vstDir.isEmpty() || m_vstDir == QDir::separator() || m_vstDir == "/" || m_vstDir == ensureTrailingSlash( QDir::homePath() ) || !QDir( m_vstDir ).exists() ) diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp index 3e1b596b8ca..9218e3238b1 100644 --- a/src/core/Controller.cpp +++ b/src/core/Controller.cpp @@ -149,7 +149,7 @@ unsigned int Controller::runningFrames() // Get position in seconds float Controller::runningTime() { - return runningFrames() / Engine::audioEngine()->processingSampleRate(); + return runningFrames() / Engine::audioEngine()->outputSampleRate(); } @@ -220,24 +220,12 @@ Controller * Controller::create( ControllerType _ct, Model * _parent ) Controller * Controller::create( const QDomElement & _this, Model * _parent ) { - Controller * c; - if( static_cast(_this.attribute( "type" ).toInt()) == ControllerType::Peak ) - { - c = PeakController::getControllerBySetting( _this ); - } - else - { - c = create( - static_cast( _this.attribute( "type" ).toInt() ), - _parent ); - } - - if( c != nullptr ) - { - c->restoreState( _this ); - } - - return( c ); + const auto controllerType = static_cast(_this.attribute("type").toInt()); + auto controller = controllerType == ControllerType::Peak + ? PeakController::getControllerBySetting(_this) + : create(controllerType, _parent); + if (controller) { controller->restoreState(_this); } + return controller; } diff --git a/src/core/ControllerConnection.cpp b/src/core/ControllerConnection.cpp index fea907942af..934ccc3f8ca 100644 --- a/src/core/ControllerConnection.cpp +++ b/src/core/ControllerConnection.cpp @@ -159,11 +159,10 @@ inline void ControllerConnection::setTargetName( const QString & _name ) */ void ControllerConnection::finalizeConnections() { - for( int i = 0; i < s_connections.size(); ++i ) + for (auto i = std::size_t{0}; i < s_connections.size(); ++i) { ControllerConnection * c = s_connections[i]; - if ( !c->isFinalized() && c->m_controllerId < - Engine::getSong()->controllers().size() ) + if (!c->isFinalized() && static_cast(c->m_controllerId) < Engine::getSong()->controllers().size()) { c->setController( Engine::getSong()-> controllers().at( c->m_controllerId ) ); @@ -221,7 +220,7 @@ void ControllerConnection::loadSettings( const QDomElement & _this ) if (!Engine::getSong()->isLoadingProject() && m_controllerId != -1 - && m_controllerId < Engine::getSong()->controllers().size()) + && static_cast(m_controllerId) < Engine::getSong()->controllers().size()) { setController( Engine::getSong()-> controllers().at( m_controllerId ) ); diff --git a/src/core/DataFile.cpp b/src/core/DataFile.cpp index 3c6309db85f..3e7d6d8b691 100644 --- a/src/core/DataFile.cpp +++ b/src/core/DataFile.cpp @@ -83,7 +83,9 @@ const std::vector DataFile::UPGRADE_METHODS = { &DataFile::upgrade_defaultTripleOscillatorHQ, &DataFile::upgrade_mixerRename , &DataFile::upgrade_bbTcoRename, &DataFile::upgrade_sampleAndHold , &DataFile::upgrade_midiCCIndexing, - &DataFile::upgrade_loopsRename , &DataFile::upgrade_noteTypes + &DataFile::upgrade_loopsRename , &DataFile::upgrade_noteTypes, + &DataFile::upgrade_fixCMTDelays , &DataFile::upgrade_fixBassLoopsTypo, + &DataFile::findProblematicLadspaPlugins }; // Vector of all versions that have upgrade routines. @@ -639,6 +641,32 @@ void DataFile::cleanMetaNodes( QDomElement _de ) } } +void DataFile::mapSrcAttributeInElementsWithResources(const QMap& map) +{ + for (const auto& [elem, srcAttrs] : ELEMENTS_WITH_RESOURCES) + { + auto elements = elementsByTagName(elem); + + for (const auto& srcAttr : srcAttrs) + { + for (int i = 0; i < elements.length(); ++i) + { + auto item = elements.item(i).toElement(); + + if (item.isNull() || !item.hasAttribute(srcAttr)) { continue; } + + const QString srcVal = item.attribute(srcAttr); + + const auto it = map.constFind(srcVal); + if (it != map.constEnd()) + { + item.setAttribute(srcAttr, *it); + } + } + } + } +} + void DataFile::upgrade_0_2_1_20070501() { @@ -1045,7 +1073,6 @@ void DataFile::upgrade_0_4_0_rc2() } } - void DataFile::upgrade_1_0_99() { jo_id_t last_assigned_id = 0; @@ -1157,7 +1184,7 @@ static void upgradeElement_1_2_0_rc2_42( QDomElement & el ) int syncmode = el.attribute( "syncmode" ).toInt(); QStringList names; QDomNamedNodeMap atts = el.attributes(); - for( uint i = 0; i < atts.length(); i++ ) + for (auto i = 0; i < atts.length(); i++) { QString name = atts.item( i ).nodeName(); if( name.endsWith( "_numerator" ) ) @@ -1194,12 +1221,11 @@ void DataFile::upgrade_1_2_0_rc3() "pattern" ); for( int j = 0; !patterns.item( j ).isNull(); ++j ) { - int patternLength, steps; QDomElement el = patterns.item( j ).toElement(); if( el.attribute( "len" ) != "" ) { - patternLength = el.attribute( "len" ).toInt(); - steps = patternLength / 12; + int patternLength = el.attribute( "len" ).toInt(); + int steps = patternLength / 12; el.setAttribute( "steps", steps ); } } @@ -1456,7 +1482,7 @@ void DataFile::upgrade_1_3_0() if(num == 4) { // don't modify port 4, but some other ones: - int zoom_port; + int zoom_port = 0; if (plugin == "Equalizer5Band") zoom_port = 36; else if (plugin == "Equalizer8Band") @@ -1685,6 +1711,44 @@ void DataFile::upgrade_noteTypes() } } +void DataFile::upgrade_fixCMTDelays() +{ + static const QMap nameMap { + { "delay_0,01s", "delay_0.01s" }, + { "delay_0,1s", "delay_0.1s" }, + { "fbdelay_0,01s", "fbdelay_0.01s" }, + { "fbdelay_0,1s", "fbdelay_0.1s" } + }; + + const auto effects = elementsByTagName("effect"); + + for (int i = 0; i < effects.size(); ++i) + { + auto effect = effects.item(i).toElement(); + + // We are only interested in LADSPA plugins + if (effect.attribute("name") != "ladspaeffect") { continue; } + + // Fetch all attributes (LMMS) beneath the LADSPA effect so that we can check the value of the plugin attribute (XML) + auto attributes = effect.elementsByTagName("attribute"); + for (int j = 0; j < attributes.size(); ++j) + { + auto attribute = attributes.item(j).toElement(); + + if (attribute.attribute("name") == "plugin") + { + const auto attributeValue = attribute.attribute("value"); + + const auto it = nameMap.constFind(attributeValue); + if (it != nameMap.constEnd()) + { + attribute.setAttribute("value", *it); + } + } + } + } +} + /** \brief Note range has been extended to match MIDI specification * @@ -1833,10 +1897,10 @@ void DataFile::upgrade_sampleAndHold() } -// Change loops' filenames in s -void DataFile::upgrade_loopsRename() +static QMap buildReplacementMap() { static constexpr auto loopBPMs = std::array{ + std::pair{"bassloops/briff01", "140"}, std::pair{"bassloops/briff01", "140"}, std::pair{"bassloops/rave_bass01", "180"}, std::pair{"bassloops/rave_bass02", "180"}, @@ -1866,39 +1930,31 @@ void DataFile::upgrade_loopsRename() std::pair{"latin/latin_guitar03", "120"}, }; - const QString prefix = "factorysample:", - extension = ".ogg"; + QMap namesToNamesWithBPMsMap; - // Replace loop sample names - for (const auto& [elem, srcAttrs] : ELEMENTS_WITH_RESOURCES) + auto insertEntry = [&namesToNamesWithBPMsMap](const QString& originalName, const QString& bpm, const QString& prefix = "", const QString& extension = "ogg") { - auto elements = elementsByTagName(elem); + const QString original = prefix + originalName + "." + extension; + const QString replacement = prefix + originalName + " - " + bpm + " BPM." + extension; - for (const auto& srcAttr : srcAttrs) - { - for (int i = 0; i < elements.length(); ++i) - { - auto item = elements.item(i).toElement(); - - if (item.isNull() || !item.hasAttribute(srcAttr)) { continue; } - for (const auto& cur : loopBPMs) - { - QString x = cur.first, // loop name - y = cur.second, // BPM - srcVal = item.attribute(srcAttr), - pattern = prefix + x + extension; + namesToNamesWithBPMsMap.insert(original, replacement); + }; - if (srcVal == pattern) - { - // Add " - X BPM" to filename - item.setAttribute(srcAttr, - prefix + x + " - " + y + " BPM" + - extension); - } - } - } - } + for (const auto & loopBPM : loopBPMs) + { + insertEntry(loopBPM.first, loopBPM.second); + insertEntry(loopBPM.first, loopBPM.second, "factorysample:"); } + + return namesToNamesWithBPMsMap; +} + +// Change loops' filenames in s +void DataFile::upgrade_loopsRename() +{ + static const QMap namesToNamesWithBPMsMap = buildReplacementMap(); + + mapSrcAttributeInElementsWithResources(namesToNamesWithBPMsMap); } //! Update MIDI CC indexes, so that they are counted from 0. Older releases of LMMS @@ -1923,6 +1979,57 @@ void DataFile::upgrade_midiCCIndexing() } } +void DataFile::findProblematicLadspaPlugins() +{ + // This is not an upgrade but a check for potentially problematic LADSPA + // controls. See #5738 for more details. + + const QDomNodeList ladspacontrols = elementsByTagName("ladspacontrols"); + + uint numberOfProblematicPlugins = 0; + + for (int i = 0; i < ladspacontrols.size(); ++i) + { + const QDomElement ladspacontrol = ladspacontrols.item(i).toElement(); + + const auto attributes = ladspacontrol.attributes(); + for (int j = 0; j < attributes.length(); ++j) + { + const auto attribute = attributes.item(j); + const auto name = attribute.nodeName(); + if (name != "ports" && name.startsWith("port")) + { + ++numberOfProblematicPlugins; + break; + } + } + } + + if (numberOfProblematicPlugins > 0) + { + QMessageBox::warning(nullptr, QObject::tr("LADSPA plugins"), + QObject::tr("The project contains %1 LADSPA plugin(s) which might have not been restored correctly! Please check the project.").arg(numberOfProblematicPlugins)); + } +} + +void DataFile::upgrade_fixBassLoopsTypo() +{ + static const QMap replacementMap = { + { "bassloopes/briff01.ogg", "bassloops/briff01 - 140 BPM.ogg" }, + { "bassloopes/rave_bass01.ogg", "bassloops/rave_bass01 - 180 BPM.ogg" }, + { "bassloopes/rave_bass02.ogg", "bassloops/rave_bass02 - 180 BPM.ogg" }, + { "bassloopes/tb303_01.ogg","bassloops/tb303_01 - 123 BPM.ogg" }, + { "bassloopes/techno_bass01.ogg", "bassloops/techno_bass01 - 140 BPM.ogg" }, + { "bassloopes/techno_bass02.ogg", "bassloops/techno_bass02 - 140 BPM.ogg" }, + { "bassloopes/techno_synth01.ogg", "bassloops/techno_synth01 - 140 BPM.ogg" }, + { "bassloopes/techno_synth02.ogg", "bassloops/techno_synth02 - 140 BPM.ogg" }, + { "bassloopes/techno_synth03.ogg", "bassloops/techno_synth03 - 130 BPM.ogg" }, + { "bassloopes/techno_synth04.ogg", "bassloops/techno_synth04 - 140 BPM.ogg" } + }; + + mapSrcAttributeInElementsWithResources(replacementMap); +} + void DataFile::upgrade() { // Runs all necessary upgrade methods diff --git a/src/core/DrumSynth.cpp b/src/core/DrumSynth.cpp index decc6bfa26d..f855639cb72 100644 --- a/src/core/DrumSynth.cpp +++ b/src/core/DrumSynth.cpp @@ -23,727 +23,954 @@ * */ - #include "DrumSynth.h" -#include -#include - -#include //sin(), exp(), etc. - #include - +#include +#include +#include #ifdef _MSC_VER -//not #if LMMS_BUILD_WIN32 because we have strncasecmp in mingw +// not #if LMMS_BUILD_WIN32 because we have strncasecmp in mingw #define strcasecmp _stricmp #endif // _MSC_VER -namespace lmms -{ - +namespace lmms { using namespace std; // const int Fs = 44100; -const float TwoPi = 6.2831853f; -const int MAX = 0; -const int ENV = 1; -const int PNT = 2; -const int dENV = 3; -const int NEXTT = 4; +const float TwoPi = 6.2831853f; +const int MAX = 0; +const int ENV = 1; +const int PNT = 2; +const int dENV = 3; +const int NEXTT = 4; // Bah, I'll move these into the class once I sepearate DrumsynthFile from DrumSynth // llama -float envpts[8][3][32]; //envelope/time-level/point -float envData[8][6]; //envelope running status -int chkOn[8], sliLev[8]; //section on/off and level -float timestretch; //overall time scaling +float envpts[8][3][32]; // envelope/time-level/point +float envData[8][6]; // envelope running status +int chkOn[8], sliLev[8]; // section on/off and level +float timestretch; // overall time scaling short DD[1200], clippoint; float DF[1200]; float phi[1200]; -long wavewords, wavemode=0; -float mem_t=1.0f, mem_o=1.0f, mem_n=1.0f, mem_b=1.0f, mem_tune=1.0f, mem_time=1.0f; - +long wavewords, wavemode = 0; +float mem_t = 1.0f, mem_o = 1.0f, mem_n = 1.0f, mem_b = 1.0f, mem_tune = 1.0f, mem_time = 1.0f; int DrumSynth::LongestEnv() { - long e, eon, p; - float l=0.f; - - for(e=1; e<7; e++) //3 - { - eon = e - 1; if(eon>2) eon=eon-1; - p = 0; - while (envpts[e][0][p + 1] >= 0.f) p++; - envData[e][MAX] = envpts[e][0][p] * timestretch; - if(chkOn[eon]==1) if(envData[e][MAX]>l) l=envData[e][MAX]; - } - //l *= timestretch; - - return 2400 + (1200 * (int)(l / 1200)); + float l = 0.f; + + for (long e = 1; e < 7; e++) // 3 + { + long eon = e - 1; + if (eon > 2) + { + eon = eon - 1; + } + long p = 0; + while (envpts[e][0][p + 1] >= 0.f) + { + p++; + } + envData[e][MAX] = envpts[e][0][p] * timestretch; + if (chkOn[eon] == 1 && envData[e][MAX] > l) + { + l = envData[e][MAX]; + } + } + // l *= timestretch; + + return 2400 + (1200 * static_cast(l / 1200)); } - float DrumSynth::LoudestEnv() { - float loudest=0.f; - int i=0; - - while (i<5) //2 - { - if(chkOn[i]==1) if(sliLev[i]>loudest) loudest=(float)sliLev[i]; - i++; - } - return (loudest * loudest); + float loudest = 0.f; + int i = 0; + + while (i < 5) // 2 + { + if ((chkOn[i] == 1) && (sliLev[i] > loudest)) + { + loudest = static_cast(sliLev[i]); + } + i++; + } + return loudest * loudest; } - void DrumSynth::UpdateEnv(int e, long t) { - float endEnv, dT; - //0.2's added - envData[e][NEXTT] = envpts[e][0][(long)(envData[e][PNT] + 1.f)] * timestretch; //get next point - if(envData[e][NEXTT] < 0) envData[e][NEXTT] = 442000 * timestretch; //if end point, hold - envData[e][ENV] = envpts[e][1][(long)(envData[e][PNT] + 0.f)] * 0.01f; //this level - endEnv = envpts[e][1][(long)(envData[e][PNT] + 1.f)] * 0.01f; //next level - dT = envData[e][NEXTT] - (float)t; - if(dT < 1.0) dT = 1.0; - envData[e][dENV] = (endEnv - envData[e][ENV]) / dT; - envData[e][PNT] = envData[e][PNT] + 1.0f; + // 0.2's added + envData[e][NEXTT] = envpts[e][0][static_cast(envData[e][PNT] + 1.f)] * timestretch; // get next point + if (envData[e][NEXTT] < 0) + { + envData[e][NEXTT] = 442000 * timestretch; // if end point, hold + } + envData[e][ENV] = envpts[e][1][static_cast(envData[e][PNT] + 0.f)] * 0.01f; // this level + float endEnv = envpts[e][1][static_cast(envData[e][PNT] + 1.f)] * 0.01f; // next level + float dT = envData[e][NEXTT] - static_cast(t); + if (dT < 1.0) + { + dT = 1.0; + } + envData[e][dENV] = (endEnv - envData[e][ENV]) / dT; + envData[e][PNT] = envData[e][PNT] + 1.0f; } - -void DrumSynth::GetEnv(int env, const char *sec, const char *key, QString ini) +void DrumSynth::GetEnv(int env, const char* sec, const char* key, QString ini) { - char en[256], s[8]; - int i=0, o=0, ep=0; - GetPrivateProfileString(sec, key, "0,0 100,0", en, sizeof(en), ini); - - //be safe! - en[255]=0; - s[0]=0; - - while(en[i]!=0) - { - if(en[i] == ',') - { - if(sscanf(s, "%f", &envpts[env][0][ep])==0) envpts[env][0][ep] = 0.f; - o=0; - } - else if(en[i] == ' ') - { - if(sscanf(s, "%f", &envpts[env][1][ep])==0) envpts[env][1][ep] = 0.f; - o=0; ep++; - } - else { s[o]=en[i]; o++; s[o]=0; } - i++; - } - if(sscanf(s, "%f", &envpts[env][1][ep])==0) envpts[env][1][ep] = 0.f; - envpts[env][0][ep + 1] = -1; - - envData[env][MAX] = envpts[env][0][ep]; + char en[256], s[8]; + int i = 0, o = 0, ep = 0; + GetPrivateProfileString(sec, key, "0,0 100,0", en, sizeof(en), ini); + + // be safe! + en[255] = 0; + s[0] = 0; + + while (en[i] != 0) + { + if (en[i] == ',') + { + if (sscanf(s, "%f", &envpts[env][0][ep]) == 0) + { + envpts[env][0][ep] = 0.f; + } + o = 0; + } + else if (en[i] == ' ') + { + if (sscanf(s, "%f", &envpts[env][1][ep]) == 0) + { + envpts[env][1][ep] = 0.f; + } + o = 0; + ep++; + } + else + { + s[o] = en[i]; + o++; + s[o] = 0; + } + i++; + } + if (sscanf(s, "%f", &envpts[env][1][ep]) == 0) + { + envpts[env][1][ep] = 0.f; + } + envpts[env][0][ep + 1] = -1; + + envData[env][MAX] = envpts[env][0][ep]; } - float DrumSynth::waveform(float ph, int form) { - float w; - - switch (form) - { - case 0: w = (float)sin(fmod(ph,TwoPi)); break; //sine - case 1: w = (float)fabs(2.0f*(float)sin(fmod(0.5f*ph,TwoPi)))-1.f; break; //sine^2 - case 2: while(ph1.f) w=2.f-w; - break; - case 3: w = ph - TwoPi * (float)(int)(ph / TwoPi); //saw - w = (0.3183098f * w) - 1.f; break; - default: w = (sin(fmod(ph,TwoPi))>0.0)? 1.f: -1.f; break; //square - } - - return w; + float w; + + switch (form) + { + case 0: + w = static_cast(sin(fmod(ph, TwoPi))); + break; // sine + case 1: + w = static_cast(fabs(2.0f * static_cast(sin(fmod(0.5f * ph, TwoPi))) - 1.f)); + break; // sine^2 + case 2: + while (ph < TwoPi) + { + ph += TwoPi; + } + w = 0.6366197f * static_cast(fmod(ph, TwoPi) - 1.f); // tri + if (w > 1.f) + { + w = 2.f - w; + } + break; + case 3: + w = ph - TwoPi * static_cast(static_cast(ph / TwoPi)); // saw + w = (0.3183098f * w) - 1.f; + break; + default: + w = (sin(fmod(ph, TwoPi)) > 0.0) ? 1.f : -1.f; + break; // square + } + + return w; } - -int DrumSynth::GetPrivateProfileString(const char *sec, const char *key, const char *def, char *buffer, int size, QString file) +int DrumSynth::GetPrivateProfileString( + const char* sec, const char* key, const char* def, char* buffer, int size, QString file) { - stringstream is; - bool inSection = false; - char *line; - char *k, *b; - int len = 0; - - line = (char*)malloc(200); - - // Use QFile to handle unicode file name on Windows - // Previously we used ifstream directly - QFile f(file); - f.open(QIODevice::ReadOnly); - QByteArray dat = f.readAll().constData(); - is.str(string(dat.constData(), dat.size())); - - while (is.good()) { - if (!inSection) { - is.ignore( numeric_limits::max(), '['); - - if (!is.eof()) { - is.getline(line, 200, ']'); - if (strcasecmp(line, sec)==0) { - inSection = true; - } - } - } - else if (!is.eof()) { - is.getline(line, 200); - if (line[0] == '[') - break; - - k = strtok(line, " \t="); - b = strtok(nullptr, "\n\r\0"); - - if (k != 0 && strcasecmp(k, key)==0) { - if (b==0) { - len = 0; - buffer[0] = 0; - } - else { - k = (char *)(b + strlen(b)-1); - while ( (k>=b) && (*k==' ' || *k=='\t') ) - --k; - *(k+1) = '\0'; - - len = strlen(b); - if (len > size-1) len = size-1; - strncpy(buffer, b, len+1); - } - break; - } - } - } - - if (len == 0) { - len = strlen(def); - strncpy(buffer, def, size); - } - - free(line); - - return len; + stringstream is; + bool inSection = false; + int len = 0; + + char* line = static_cast(malloc(200)); + + // Use QFile to handle unicode file name on Windows + // Previously we used ifstream directly + QFile f(file); + f.open(QIODevice::ReadOnly); + QByteArray dat = f.readAll().constData(); + is.str(string(dat.constData(), dat.size())); + + while (is.good()) + { + if (!inSection) + { + is.ignore(numeric_limits::max(), '['); + + if (!is.eof()) + { + is.getline(line, 200, ']'); + if (strcasecmp(line, sec) == 0) + { + inSection = true; + } + } + } + else if (!is.eof()) + { + is.getline(line, 200); + if (line[0] == '[') + { + break; + } + + char* k = strtok(line, " \t="); + char* b = strtok(nullptr, "\n\r\0"); + + if (k != 0 && strcasecmp(k, key) == 0) + { + if (b == 0) + { + len = 0; + buffer[0] = 0; + } + else + { + k = static_cast(b + strlen(b) - 1); + while ((k >= b) && (*k == ' ' || *k == '\t')) + { + --k; + } + *(k + 1) = '\0'; + + len = strlen(b); + if (len > size - 1) + { + len = size - 1; + } + strncpy(buffer, b, len + 1); + } + break; + } + } + } + + if (len == 0) + { + len = strlen(def); + strncpy(buffer, def, size); + } + + free(line); + + return len; } -int DrumSynth::GetPrivateProfileInt(const char *sec, const char *key, int def, QString file) +int DrumSynth::GetPrivateProfileInt(const char* sec, const char* key, int def, QString file) { - char tmp[16]; - int i=0; + char tmp[16]; + int i = 0; - GetPrivateProfileString(sec, key, "", tmp, sizeof(tmp), file); - sscanf(tmp, "%d", &i); if(tmp[0]==0) i=def; + GetPrivateProfileString(sec, key, "", tmp, sizeof(tmp), file); + sscanf(tmp, "%d", &i); + if (tmp[0] == 0) + { + i = def; + } - return i; + return i; } -float DrumSynth::GetPrivateProfileFloat(const char *sec, const char *key, float def, QString file) +float DrumSynth::GetPrivateProfileFloat(const char* sec, const char* key, float def, QString file) { - char tmp[16]; - float f=0.f; + char tmp[16]; + float f = 0.f; - GetPrivateProfileString(sec, key, "", tmp, sizeof(tmp), file); - sscanf(tmp, "%f", &f); if(tmp[0]==0) f=def; + GetPrivateProfileString(sec, key, "", tmp, sizeof(tmp), file); + sscanf(tmp, "%f", &f); + if (tmp[0] == 0) + { + f = def; + } - return f; + return f; } - // Constantly opening and scanning each file for the setting really sucks. // But, the original did this, and we know it works. Will store file into // an associative array or something once we have a datastructure to load in to. // llama -int DrumSynth::GetDSFileSamples(QString dsfile, int16_t *&wave, int channels, sample_rate_t Fs) +int DrumSynth::GetDSFileSamples(QString dsfile, int16_t*& wave, int channels, sample_rate_t Fs) { - //input file - char sec[32]; - char ver[32]; - char comment[256]; - int commentLen=0; - - //generation - long Length, tpos=0, tplus, totmp, t, i, j; - float x[3] = {0.f, 0.f, 0.f}; - float MasterTune; - constexpr float randmax = 1.f / static_cast(RAND_MAX); - constexpr float randmax2 = 2.f / static_cast(RAND_MAX); - int MainFilter, HighPass; - - long NON, NT, TON, DiON, TDroop=0, DStep; - float a, b=0.f, c=0.f, d=0.f, g, TT=0.f, TL, NL, F1, F2; - float TphiStart=0.f, Tphi, TDroopRate, ddF, DAtten, DGain; - - long BON, BON2, BFStep, BFStep2, botmp; - float BdF=0.f, BdF2=0.f, BPhi, BPhi2, BF, BF2, BQ, BQ2, BL, BL2; - - long OON, OF1Sync=0, OF2Sync=0, OMode, OW1, OW2; - float Ophi1, Ophi2, OF1, OF2, OL, Ot=0 /*PG: init */, OBal1, OBal2, ODrive; - float Ocf1, Ocf2, OcF, OcQ, OcA, Oc[6][2]; //overtone cymbal mode - float Oc0=0.0f, Oc1=0.0f, Oc2=0.0f; - - float MFfb, MFtmp, MFres, MFin=0.f, MFout=0.f; - float DownAve; - long DownStart, DownEnd, jj; - - - if(wavemode==0) //semi-real-time adjustments if working in memory!! - { - mem_t = 1.0f; - mem_o = 1.0f; - mem_n = 1.0f; - mem_b = 1.0f; - mem_tune = 0.0f; - mem_time = 1.0f; - } - - //try to read version from input file - strcpy(sec, "General"); - GetPrivateProfileString(sec,"Version","",ver,sizeof(ver),dsfile); - ver[9]=0; - if(strcasecmp(ver, "DrumSynth") != 0) {return 0;} //input fail - if(ver[11] != '1' && ver[11] != '2') {return 0;} //version fail - - - //read master parameters - GetPrivateProfileString(sec,"Comment","",comment,sizeof(comment),dsfile); - while((comment[commentLen]!=0) && (commentLen<254)) commentLen++; - if(commentLen==0) { comment[0]=32; comment[1]=0; commentLen=1;} - comment[commentLen+1]=0; commentLen++; - if((commentLen % 2)==1) commentLen++; - - timestretch = .01f * mem_time * GetPrivateProfileFloat(sec,"Stretch",100.0,dsfile); - if(timestretch<0.2f) timestretch=0.2f; - if(timestretch>10.f) timestretch=10.f; - // the unit of envelope lengths is a sample in 44100Hz sample rate, so correct it - timestretch *= Fs / 44100.f; - - DGain = 1.0f; //leave this here! - DGain = static_cast(std::pow(10.0, 0.05 * GetPrivateProfileFloat(sec,"Level",0,dsfile))); - - MasterTune = GetPrivateProfileFloat(sec,"Tuning",0.0,dsfile); - MasterTune = static_cast(std::pow(1.0594631f, MasterTune + mem_tune)); - MainFilter = 2 * GetPrivateProfileInt(sec,"Filter",0,dsfile); - MFres = 0.0101f * GetPrivateProfileFloat(sec,"Resonance",0.0,dsfile); - MFres = static_cast(std::pow(MFres, 0.5f)); - - HighPass = GetPrivateProfileInt(sec,"HighPass",0,dsfile); - GetEnv(7, sec, "FilterEnv", dsfile); - - - //read noise parameters - strcpy(sec, "Noise"); - chkOn[1] = GetPrivateProfileInt(sec,"On",0,dsfile); - sliLev[1] = GetPrivateProfileInt(sec,"Level",0,dsfile); - NT = GetPrivateProfileInt(sec,"Slope",0,dsfile); - GetEnv(2, sec, "Envelope", dsfile); - NON = chkOn[1]; - NL = (float)(sliLev[1] * sliLev[1]) * mem_n; - if(NT<0) - { a = 1.f + (NT / 105.f); d = -NT / 105.f; - g = (1.f + 0.0005f * NT * NT) * NL; } - else - { a = 1.f; b = -NT / 50.f; c = (float)fabs((float)NT) / 100.f; g = NL; } - - //if(GetPrivateProfileInt(sec,"FixedSeq",0,dsfile)!=0) - //srand(1); //fixed random sequence - - //read tone parameters - strcpy(sec, "Tone"); - chkOn[0] = GetPrivateProfileInt(sec,"On",0,dsfile); TON = chkOn[0]; - sliLev[0] = GetPrivateProfileInt(sec,"Level",128,dsfile); - TL = (float)(sliLev[0] * sliLev[0]) * mem_t; - GetEnv(1, sec, "Envelope", dsfile); - F1 = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F1",200.0,dsfile) / Fs; - if(fabs(F1)<0.001f) F1=0.001f; //to prevent overtone ratio div0 - F2 = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F2",120.0,dsfile) / Fs; - TDroopRate = GetPrivateProfileFloat(sec,"Droop",0.f,dsfile); - if(TDroopRate>0.f) - { - TDroopRate = static_cast(std::pow(10.0f, (TDroopRate - 20.0f) / 30.0f)); - TDroopRate = TDroopRate * -4.f / envData[1][MAX]; - TDroop = 1; - F2 = F1+((F2-F1)/(1.f-(float)exp(TDroopRate * envData[1][MAX]))); - ddF = F1 - F2; - } - else ddF = F2-F1; - - Tphi = GetPrivateProfileFloat(sec,"Phase",90.f,dsfile) / 57.29578f; //degrees>radians - - //read overtone parameters - strcpy(sec, "Overtones"); - chkOn[2] = GetPrivateProfileInt(sec,"On",0,dsfile); OON = chkOn[2]; - sliLev[2] = GetPrivateProfileInt(sec,"Level",128,dsfile); - OL = (float)(sliLev[2] * sliLev[2]) * mem_o; - GetEnv(3, sec, "Envelope1", dsfile); - GetEnv(4, sec, "Envelope2", dsfile); - OMode = GetPrivateProfileInt(sec,"Method",2,dsfile); - OF1 = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F1",200.0,dsfile) / Fs; - OF2 = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F2",120.0,dsfile) / Fs; - OW1 = GetPrivateProfileInt(sec,"Wave1",0,dsfile); - OW2 = GetPrivateProfileInt(sec,"Wave2",0,dsfile); - OBal2 = (float)GetPrivateProfileInt(sec,"Param",50,dsfile); - ODrive = static_cast(std::pow(OBal2, 3.0f)) / std::pow(50.0f, 3.0f); - OBal2 *= 0.01f; - OBal1 = 1.f - OBal2; - Ophi1 = Tphi; - Ophi2 = Tphi; - if(MainFilter==0) - MainFilter = GetPrivateProfileInt(sec,"Filter",0,dsfile); - if((GetPrivateProfileInt(sec,"Track1",0,dsfile)==1) && (TON==1)) - { OF1Sync = 1; OF1 = OF1 / F1; } - if((GetPrivateProfileInt(sec,"Track2",0,dsfile)==1) && (TON==1)) - { OF2Sync = 1; OF2 = OF2 / F1; } - - OcA = 0.28f + OBal1 * OBal1; //overtone cymbal mode - OcQ = OcA * OcA; - OcF = (1.8f - 0.7f * OcQ) * 0.92f; //multiply by env 2 - OcA *= 1.0f + 4.0f * OBal1; //level is a compromise! - Ocf1 = TwoPi / OF1; - Ocf2 = TwoPi / OF2; - for(i=0; i<6; i++) Oc[i][0] = Oc[i][1] = Ocf1 + (Ocf2 - Ocf1) * 0.2f * (float)i; - - //read noise band parameters - strcpy(sec, "NoiseBand"); - chkOn[3] = GetPrivateProfileInt(sec,"On",0,dsfile); BON = chkOn[3]; - sliLev[3] = GetPrivateProfileInt(sec,"Level",128,dsfile); - BL = (float)(sliLev[3] * sliLev[3]) * mem_b; - BF = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F",1000.0,dsfile) / Fs; - BPhi = TwoPi / 8.f; - GetEnv(5, sec, "Envelope", dsfile); - BFStep = GetPrivateProfileInt(sec,"dF",50,dsfile); - BQ = (float)BFStep; - BQ = BQ * BQ / (10000.f-6600.f*((float)sqrt(BF)-0.19f)); - BFStep = 1 + (int)((40.f - (BFStep / 2.5f)) / (BQ + 1.f + (1.f * BF))); - - strcpy(sec, "NoiseBand2"); - chkOn[4] = GetPrivateProfileInt(sec,"On",0,dsfile); BON2 = chkOn[4]; - sliLev[4] = GetPrivateProfileInt(sec,"Level",128,dsfile); - BL2 = (float)(sliLev[4] * sliLev[4]) * mem_b; - BF2 = MasterTune * TwoPi * GetPrivateProfileFloat(sec,"F",1000.0,dsfile) / Fs; - BPhi2 = TwoPi / 8.f; - GetEnv(6, sec, "Envelope", dsfile); - BFStep2 = GetPrivateProfileInt(sec,"dF",50,dsfile); - BQ2 = (float)BFStep2; - BQ2 = BQ2 * BQ2 / (10000.f-6600.f*((float)sqrt(BF2)-0.19f)); - BFStep2 = 1 + (int)((40 - (BFStep2 / 2.5)) / (BQ2 + 1 + (1 * BF2))); - - //read distortion parameters - strcpy(sec, "Distortion"); - chkOn[5] = GetPrivateProfileInt(sec,"On",0,dsfile); DiON = chkOn[5]; - DStep = 1 + GetPrivateProfileInt(sec,"Rate",0,dsfile); - if(DStep==7) DStep=20; - if(DStep==6) DStep=10; - if(DStep==5) DStep=8; - - clippoint = 32700; - DAtten = 1.0f; - - if(DiON==1) - { - DAtten = DGain * (short)LoudestEnv(); - if(DAtten>32700) clippoint=32700; else clippoint=(short)DAtten; - DAtten = static_cast(std::pow(2.0, 2.0 * GetPrivateProfileInt(sec,"Bits",0,dsfile))); - DGain = DAtten * DGain * static_cast(std::pow(10.0, 0.05 * GetPrivateProfileInt(sec,"Clipping",0,dsfile))); - } - - //prepare envelopes - for (i=1;i<8;i++) { envData[i][NEXTT]=0; envData[i][PNT]=0; } - Length = LongestEnv(); - - //allocate the buffer - //if(wave!=NULL) free(wave); - //wave = new int16_t[channels * (Length + 1280)]; //wave memory buffer - wave = new int16_t[channels * Length]; //wave memory buffer - if(wave==nullptr) {return 0;} - wavewords = 0; - - /* - if(wavemode==0) - { - //open output file - fp = fopen(wavfile, "wb"); - if(!fp) {return 3;} //output fail - - //set up INFO chunk - WI.list = 0x5453494C; - WI.listLength = 36 + commentLen; - WI.info = 0x4F464E49; - WI.isft = 0x54465349; - WI.isftLength = 16; - strcpy(WI.software, "DrumSynth v2.0 "); WI.software[15]=0; - WI.icmt = 0x544D4349; - WI.icmtLength = commentLen; - - //write WAV header - WH.riff = 0x46464952; - WH.riffLength = 36 + (2 * Length) + 44 + commentLen; - WH.wave = 0x45564157; - WH.fmt = 0x20746D66; - WH.waveLength = 16; - WH.wFormatTag = WAVE_FORMAT_PCM; - WH.nChannels = 1; - WH.nSamplesPerSec = Fs; - WH.nAvgBytesPerSec = 2 * Fs; - WH.nBlockAlign = 2; - WH.wBitsPerSample = 16; - WH.data = 0x61746164; - WH.dataLength = 2 * Length; - fwrite(&WH, 1, 44, fp); - } - */ - - //generate - tpos = 0; - while(tpos=envData[2][MAX]) NON=0; - } - else { - for(j=0; j<1200; j++) DF[j]=0.f; - } - - if(TON==1) //tone - { - TphiStart = Tphi; - if(TDroop==1) - { - for(t=tpos; t<=tplus; t++) - phi[t - tpos] = F2 + (ddF * (float)exp(t * TDroopRate)); - } - else - { - for(t=tpos; t<=tplus; t++) - phi[t - tpos] = F1 + (t / envData[1][MAX]) * ddF; - } - for(t=tpos; t<=tplus; t++) - { - totmp = t - tpos; - if(t < envData[1][NEXTT]) - envData[1][ENV] = envData[1][ENV] + envData[1][dENV]; - else UpdateEnv(1, t); - Tphi = Tphi + phi[totmp]; - DF[totmp] += TL * envData[1][ENV] * (float)sin(fmod(Tphi,TwoPi));//overflow? - } - if(t>=envData[1][MAX]) TON=0; - } - else for(j=0; j<1200; j++) phi[j]=F2; //for overtone sync - - if(BON==1) //noise band 1 - { - for(t=tpos; t<=tplus; t++) - { - if(t < envData[5][NEXTT]) - envData[5][ENV] = envData[5][ENV] + envData[5][dENV]; - else UpdateEnv(5, t); - if((t % BFStep) == 0) BdF = randmax * (float)rand() - 0.5f; - BPhi = BPhi + BF + BQ * BdF; - botmp = t - tpos; - DF[botmp] = DF[botmp] + (float)cos(fmod(BPhi,TwoPi)) * envData[5][ENV] * BL; - } - if(t>=envData[5][MAX]) BON=0; - } - - if(BON2==1) //noise band 2 - { - for(t=tpos; t<=tplus; t++) - { - if(t < envData[6][NEXTT]) - envData[6][ENV] = envData[6][ENV] + envData[6][dENV]; - else UpdateEnv(6, t); - if((t % BFStep2) == 0) BdF2 = randmax * (float)rand() - 0.5f; - BPhi2 = BPhi2 + BF2 + BQ2 * BdF2; - botmp = t - tpos; - DF[botmp] = DF[botmp] + (float)cos(fmod(BPhi2,TwoPi)) * envData[6][ENV] * BL2; - } - if(t>=envData[6][MAX]) BON2=0; - } - - - for (t=tpos; t<=tplus; t++) - { - if(OON==1) //overtones - { - if(t=envData[3][MAX]) //wait for OT2 - { - envData[3][ENV] = 0; - envData[3][dENV] = 0; - envData[3][NEXTT] = 999999; - } - else UpdateEnv(3, t); - } - // - if(t=envData[4][MAX]) //wait for OT1 - { - envData[4][ENV] = 0; - envData[4][dENV] = 0; - envData[4][NEXTT] = 999999; - } - else UpdateEnv(4, t); - } - // - TphiStart = TphiStart + phi[t - tpos]; - if(OF1Sync==1) Ophi1 = TphiStart * OF1; else Ophi1 = Ophi1 + OF1; - if(OF2Sync==1) Ophi2 = TphiStart * OF2; else Ophi2 = Ophi2 + OF2; - Ot=0.0f; - switch (OMode) - { - case 0: //add - Ot = OBal1 * envData[3][ENV] * waveform(Ophi1, OW1); - Ot = OL * (Ot + OBal2 * envData[4][ENV] * waveform(Ophi2, OW2)); - break; - - case 1: //FM - Ot = ODrive * envData[4][ENV] * waveform(Ophi2, OW2); - Ot = OL * envData[3][ENV] * waveform(Ophi1 + Ot, OW1); - break; - - case 2: //RM - Ot = (1 - ODrive / 8) + (((ODrive / 8) * envData[4][ENV]) * waveform(Ophi2, OW2)); - Ot = OL * envData[3][ENV] * waveform(Ophi1, OW1) * Ot; - break; - - case 3: //808 Cymbal - for(j=0; j<6; j++) - { - Oc[j][0] += 1.0f; - - if(Oc[j][0]>Oc[j][1]) - { - Oc[j][0] -= Oc[j][1]; - Ot = OL * envData[3][ENV]; - } - } - Ocf1 = envData[4][ENV] * OcF; //filter freq - Oc0 += Ocf1 * Oc1; - Oc1 += Ocf1 * (Ot + Oc2 - OcQ * Oc1 - Oc0); //bpf - Oc2 = Ot; - Ot = Oc1; - break; - } - } - - if(MainFilter==1) //filter overtones - { - if(t0.2f) - MFfb = 1.001f - static_cast(std::pow(10.0f, MFtmp - 1)); - else - MFfb = 0.999f - 0.7824f * MFtmp; - - MFtmp = Ot + MFres * (1.f + (1.f/MFfb)) * (MFin - MFout); - MFin = MFfb * (MFin - MFtmp) + MFtmp; - MFout = MFfb * (MFout - MFin) + MFin; - - DF[t - tpos] = DF[t - tpos] + (MFout - (HighPass * Ot)); - } - else if(MainFilter==2) //filter all - { - if(t0.2f) - MFfb = 1.001f - static_cast(std::pow(10.0f, MFtmp - 1)); - else - MFfb = 0.999f - 0.7824f * MFtmp; - - MFtmp = DF[t - tpos] + Ot + MFres * (1.f + (1.f/MFfb)) * (MFin - MFout); - MFin = MFfb * (MFin - MFtmp) + MFtmp; - MFout = MFfb * (MFout - MFin) + MFin; - - DF[t - tpos] = MFout - (HighPass * (DF[t - tpos] + Ot)); - } - // PG: Ot is uninitialized - else DF[t - tpos] = DF[t - tpos] + Ot; //no filter - } - - if(DiON==1) //bit resolution - { - for(j=0; j<1200; j++) - DF[j] = DGain * (int)(DF[j] / DAtten); - - for(j=0; j<1200; j+=DStep) //downsampling - { - DownAve = 0; - DownStart = j; - DownEnd = j + DStep - 1; - for(jj = DownStart; jj<=DownEnd; jj++) - DownAve = DownAve + DF[jj]; - DownAve = DownAve / DStep; - for(jj = DownStart; jj<=DownEnd; jj++) - DF[jj] = DownAve; - } - } - else for(j=0; j<1200; j++) DF[j] *= DGain; - - for(j = 0; j<1200; j++) //clipping + output - { - if(DF[j] > clippoint) - wave[wavewords++] = clippoint; - else if(DF[j] < -clippoint) - wave[wavewords++] = -clippoint; - else - wave[wavewords++] = (short)DF[j]; - - for (int c = 1; c < channels; c++) { - wave[wavewords] = wave[wavewords-1]; - wavewords++; - } - } - - tpos = tpos + 1200; - } - - /* - if(wavemode==0) - { - fwrite(wave, 2, Length, fp); //write data - fwrite(&WI, 1, 44, fp); //write INFO chunk - fwrite(&comment, 1, commentLen, fp); - fclose(fp); - } - wavemode = 0; //force compatibility!! - */ - - - return Length; + // input file + char sec[32]; + char ver[32]; + char comment[256]; + int commentLen = 0; + + // generation + long Length, tpos = 0, tplus, totmp, t, i, j; + float x[3] = {0.f, 0.f, 0.f}; + float MasterTune; + constexpr float randmax = 1.f / static_cast(RAND_MAX); + constexpr float randmax2 = 2.f / static_cast(RAND_MAX); + int MainFilter, HighPass; + + long NON, NT, TON, DiON, TDroop = 0, DStep; + float a, b = 0.f, c = 0.f, d = 0.f, g, TT = 0.f, TL, NL, F1, F2; + float TphiStart = 0.f, Tphi, TDroopRate, ddF, DAtten, DGain; + + long BON, BON2, BFStep, BFStep2, botmp; + float BdF = 0.f, BdF2 = 0.f, BPhi, BPhi2, BF, BF2, BQ, BQ2, BL, BL2; + + long OON, OF1Sync = 0, OF2Sync = 0, OMode, OW1, OW2; + float Ophi1, Ophi2, OF1, OF2, OL, Ot = 0 /*PG: init */, OBal1, OBal2, ODrive; + float Ocf1, Ocf2, OcF, OcQ, OcA, Oc[6][2]; // overtone cymbal mode + float Oc0 = 0.0f, Oc1 = 0.0f, Oc2 = 0.0f; + + float MFfb, MFtmp, MFres, MFin = 0.f, MFout = 0.f; + float DownAve; + long DownStart, DownEnd, jj; + + if (wavemode == 0) // semi-real-time adjustments if working in memory!! + { + mem_t = 1.0f; + mem_o = 1.0f; + mem_n = 1.0f; + mem_b = 1.0f; + mem_tune = 0.0f; + mem_time = 1.0f; + } + + // try to read version from input file + strcpy(sec, "General"); + GetPrivateProfileString(sec, "Version", "", ver, sizeof(ver), dsfile); + ver[9] = 0; + if ((strcasecmp(ver, "DrumSynth") != 0) // input fail + || (ver[11] != '1' && ver[11] != '2')) // version fail + { + return 0; + } + + // read master parameters + GetPrivateProfileString(sec, "Comment", "", comment, sizeof(comment), dsfile); + while ((comment[commentLen] != 0) && (commentLen < 254)) + { + commentLen++; + } + if (commentLen == 0) + { + comment[0] = 32; + comment[1] = 0; + commentLen = 1; + } + comment[commentLen + 1] = 0; + commentLen++; + if ((commentLen % 2) == 1) + { + commentLen++; + } + + timestretch = .01f * mem_time * GetPrivateProfileFloat(sec, "Stretch", 100.0, dsfile); + if (timestretch < 0.2f) + { + timestretch = 0.2f; + } + if (timestretch > 10.f) + { + timestretch = 10.f; + } + // the unit of envelope lengths is a sample in 44100Hz sample rate, so correct it + timestretch *= Fs / 44100.f; + + DGain = 1.0f; // leave this here! + DGain = static_cast(std::pow(10.0, 0.05 * GetPrivateProfileFloat(sec, "Level", 0, dsfile))); + + MasterTune = GetPrivateProfileFloat(sec, "Tuning", 0.0, dsfile); + MasterTune = static_cast(std::pow(1.0594631f, MasterTune + mem_tune)); + MainFilter = 2 * GetPrivateProfileInt(sec, "Filter", 0, dsfile); + MFres = 0.0101f * GetPrivateProfileFloat(sec, "Resonance", 0.0, dsfile); + MFres = static_cast(std::pow(MFres, 0.5f)); + + HighPass = GetPrivateProfileInt(sec, "HighPass", 0, dsfile); + GetEnv(7, sec, "FilterEnv", dsfile); + + // read noise parameters + strcpy(sec, "Noise"); + chkOn[1] = GetPrivateProfileInt(sec, "On", 0, dsfile); + sliLev[1] = GetPrivateProfileInt(sec, "Level", 0, dsfile); + NT = GetPrivateProfileInt(sec, "Slope", 0, dsfile); + GetEnv(2, sec, "Envelope", dsfile); + NON = chkOn[1]; + NL = static_cast(sliLev[1] * sliLev[1]) * mem_n; + if (NT < 0) + { + a = 1.f + (NT / 105.f); + d = -NT / 105.f; + g = (1.f + 0.0005f * NT * NT) * NL; + } + else + { + a = 1.f; + b = -NT / 50.f; + c = static_cast(fabs(static_cast(NT))) / 100.f; + g = NL; + } + + // if(GetPrivateProfileInt(sec,"FixedSeq",0,dsfile)!=0) + // srand(1); //fixed random sequence + + // read tone parameters + strcpy(sec, "Tone"); + chkOn[0] = GetPrivateProfileInt(sec, "On", 0, dsfile); + TON = chkOn[0]; + sliLev[0] = GetPrivateProfileInt(sec, "Level", 128, dsfile); + TL = static_cast(sliLev[0] * sliLev[0]) * mem_t; + GetEnv(1, sec, "Envelope", dsfile); + F1 = MasterTune * TwoPi * GetPrivateProfileFloat(sec, "F1", 200.0, dsfile) / Fs; + if (fabs(F1) < 0.001f) + { + F1 = 0.001f; // to prevent overtone ratio div0 + } + F2 = MasterTune * TwoPi * GetPrivateProfileFloat(sec, "F2", 120.0, dsfile) / Fs; + TDroopRate = GetPrivateProfileFloat(sec, "Droop", 0.f, dsfile); + if (TDroopRate > 0.f) + { + TDroopRate = static_cast(std::pow(10.0f, (TDroopRate - 20.0f) / 30.0f)); + TDroopRate = TDroopRate * -4.f / envData[1][MAX]; + TDroop = 1; + F2 = F1 + ((F2 - F1) / (1.f - static_cast(exp(TDroopRate * envData[1][MAX])))); + ddF = F1 - F2; + } + else + { + ddF = F2 - F1; + } + + Tphi = GetPrivateProfileFloat(sec, "Phase", 90.f, dsfile) / 57.29578f; // degrees>radians + + // read overtone parameters + strcpy(sec, "Overtones"); + chkOn[2] = GetPrivateProfileInt(sec, "On", 0, dsfile); + OON = chkOn[2]; + sliLev[2] = GetPrivateProfileInt(sec, "Level", 128, dsfile); + OL = static_cast(sliLev[2] * sliLev[2]) * mem_o; + GetEnv(3, sec, "Envelope1", dsfile); + GetEnv(4, sec, "Envelope2", dsfile); + OMode = GetPrivateProfileInt(sec, "Method", 2, dsfile); + OF1 = MasterTune * TwoPi * GetPrivateProfileFloat(sec, "F1", 200.0, dsfile) / Fs; + OF2 = MasterTune * TwoPi * GetPrivateProfileFloat(sec, "F2", 120.0, dsfile) / Fs; + OW1 = GetPrivateProfileInt(sec, "Wave1", 0, dsfile); + OW2 = GetPrivateProfileInt(sec, "Wave2", 0, dsfile); + OBal2 = static_cast(GetPrivateProfileInt(sec, "Param", 50, dsfile)); + ODrive = static_cast(std::pow(OBal2, 3.0f)) / std::pow(50.0f, 3.0f); + OBal2 *= 0.01f; + OBal1 = 1.f - OBal2; + Ophi1 = Tphi; + Ophi2 = Tphi; + if (MainFilter == 0) + { + MainFilter = GetPrivateProfileInt(sec, "Filter", 0, dsfile); + } + if ((GetPrivateProfileInt(sec, "Track1", 0, dsfile) == 1) && (TON == 1)) + { + OF1Sync = 1; + OF1 = OF1 / F1; + } + if ((GetPrivateProfileInt(sec, "Track2", 0, dsfile) == 1) && (TON == 1)) + { + OF2Sync = 1; + OF2 = OF2 / F1; + } + + OcA = 0.28f + OBal1 * OBal1; // overtone cymbal mode + OcQ = OcA * OcA; + OcF = (1.8f - 0.7f * OcQ) * 0.92f; // multiply by env 2 + OcA *= 1.0f + 4.0f * OBal1; // level is a compromise! + Ocf1 = TwoPi / OF1; + Ocf2 = TwoPi / OF2; + for (i = 0; i < 6; i++) + { + Oc[i][0] = Oc[i][1] = Ocf1 + (Ocf2 - Ocf1) * 0.2f * static_cast(i); + } + + // read noise band parameters + strcpy(sec, "NoiseBand"); + chkOn[3] = GetPrivateProfileInt(sec, "On", 0, dsfile); + BON = chkOn[3]; + sliLev[3] = GetPrivateProfileInt(sec, "Level", 128, dsfile); + BL = static_cast(sliLev[3] * sliLev[3]) * mem_b; + BF = MasterTune * TwoPi * GetPrivateProfileFloat(sec, "F", 1000.0, dsfile) / Fs; + BPhi = TwoPi / 8.f; + GetEnv(5, sec, "Envelope", dsfile); + BFStep = GetPrivateProfileInt(sec, "dF", 50, dsfile); + BQ = static_cast(BFStep); + BQ = BQ * BQ / (10000.f - 6600.f * (static_cast(sqrt(BF)) - 0.19f)); + BFStep = 1 + static_cast((40.f - (BFStep / 2.5f)) / (BQ + 1.f + (1.f * BF))); + + strcpy(sec, "NoiseBand2"); + chkOn[4] = GetPrivateProfileInt(sec, "On", 0, dsfile); + BON2 = chkOn[4]; + sliLev[4] = GetPrivateProfileInt(sec, "Level", 128, dsfile); + BL2 = static_cast(sliLev[4] * sliLev[4]) * mem_b; + BF2 = MasterTune * TwoPi * GetPrivateProfileFloat(sec, "F", 1000.0, dsfile) / Fs; + BPhi2 = TwoPi / 8.f; + GetEnv(6, sec, "Envelope", dsfile); + BFStep2 = GetPrivateProfileInt(sec, "dF", 50, dsfile); + BQ2 = static_cast(BFStep2); + BQ2 = BQ2 * BQ2 / (10000.f - 6600.f * (static_cast(sqrt(BF2)) - 0.19f)); + BFStep2 = 1 + static_cast((40 - (BFStep2 / 2.5)) / (BQ2 + 1 + (1 * BF2))); + + // read distortion parameters + strcpy(sec, "Distortion"); + chkOn[5] = GetPrivateProfileInt(sec, "On", 0, dsfile); + DiON = chkOn[5]; + DStep = 1 + GetPrivateProfileInt(sec, "Rate", 0, dsfile); + if (DStep == 7) { DStep = 20; } + if (DStep == 6) { DStep = 10; } + if (DStep == 5) { DStep = 8; } + + clippoint = 32700; + DAtten = 1.0f; + + if (DiON == 1) + { + DAtten = DGain * static_cast(LoudestEnv()); + clippoint = DAtten > 32700 ? 32700 : static_cast(DAtten); + DAtten = static_cast(std::pow(2.0, 2.0 * GetPrivateProfileInt(sec, "Bits", 0, dsfile))); + DGain = DAtten * DGain + * static_cast(std::pow(10.0, 0.05 * GetPrivateProfileInt(sec, "Clipping", 0, dsfile))); + } + + // prepare envelopes + for (i = 1; i < 8; i++) + { + envData[i][NEXTT] = 0; + envData[i][PNT] = 0; + } + Length = LongestEnv(); + + // allocate the buffer + // if(wave!=NULL) free(wave); + // wave = new int16_t[channels * (Length + 1280)]; //wave memory buffer + wave = new int16_t[channels * Length]; // wave memory buffer + if (wave == nullptr) + { + return 0; + } + wavewords = 0; + + /* + if(wavemode==0) + { + //open output file + fp = fopen(wavfile, "wb"); + if(!fp) {return 3;} //output fail + + //set up INFO chunk + WI.list = 0x5453494C; + WI.listLength = 36 + commentLen; + WI.info = 0x4F464E49; + WI.isft = 0x54465349; + WI.isftLength = 16; + strcpy(WI.software, "DrumSynth v2.0 "); WI.software[15]=0; + WI.icmt = 0x544D4349; + WI.icmtLength = commentLen; + + //write WAV header + WH.riff = 0x46464952; + WH.riffLength = 36 + (2 * Length) + 44 + commentLen; + WH.wave = 0x45564157; + WH.fmt = 0x20746D66; + WH.waveLength = 16; + WH.wFormatTag = WAVE_FORMAT_PCM; + WH.nChannels = 1; + WH.nSamplesPerSec = Fs; + WH.nAvgBytesPerSec = 2 * Fs; + WH.nBlockAlign = 2; + WH.wBitsPerSample = 16; + WH.data = 0x61746164; + WH.dataLength = 2 * Length; + fwrite(&WH, 1, 44, fp); + } + */ + + // generate + tpos = 0; + while (tpos < Length) + { + tplus = tpos + 1199; + + if (NON == 1) // noise + { + for (t = tpos; t <= tplus; t++) + { + if (t < envData[2][NEXTT]) + { + envData[2][ENV] = envData[2][ENV] + envData[2][dENV]; + } + else + { + UpdateEnv(2, t); + } + x[2] = x[1]; + x[1] = x[0]; + x[0] = (randmax2 * static_cast(rand())) - 1.f; + TT = a * x[0] + b * x[1] + c * x[2] + d * TT; + DF[t - tpos] = TT * g * envData[2][ENV]; + } + if (t >= envData[2][MAX]) + { + NON = 0; + } + } + else + { + for (j = 0; j < 1200; j++) + { + DF[j] = 0.f; + } + } + + if (TON == 1) // tone + { + TphiStart = Tphi; + if (TDroop == 1) + { + for (t = tpos; t <= tplus; t++) + { + phi[t - tpos] = F2 + (ddF * static_cast(exp(t * TDroopRate))); + } + } + else + { + for (t = tpos; t <= tplus; t++) + { + phi[t - tpos] = F1 + (t / envData[1][MAX]) * ddF; + } + } + for (t = tpos; t <= tplus; t++) + { + totmp = t - tpos; + if (t < envData[1][NEXTT]) + { + envData[1][ENV] = envData[1][ENV] + envData[1][dENV]; + } + else + { + UpdateEnv(1, t); + } + Tphi = Tphi + phi[totmp]; + DF[totmp] += TL * envData[1][ENV] * static_cast(sin(fmod(Tphi, TwoPi))); // overflow? + } + if (t >= envData[1][MAX]) + { + TON = 0; + } + } + else + { + for (j = 0; j < 1200; j++) + { + phi[j] = F2; // for overtone sync + } + } + + if (BON == 1) // noise band 1 + { + for (t = tpos; t <= tplus; t++) + { + if (t < envData[5][NEXTT]) + { + envData[5][ENV] = envData[5][ENV] + envData[5][dENV]; + } + else + { + UpdateEnv(5, t); + } + if ((t % BFStep) == 0) + { + BdF = randmax * static_cast(rand()) - 0.5f; + } + BPhi = BPhi + BF + BQ * BdF; + botmp = t - tpos; + DF[botmp] = DF[botmp] + static_cast(cos(fmod(BPhi, TwoPi))) * envData[5][ENV] * BL; + } + if (t >= envData[5][MAX]) + { + BON = 0; + } + } + + if (BON2 == 1) // noise band 2 + { + for (t = tpos; t <= tplus; t++) + { + if (t < envData[6][NEXTT]) + { + envData[6][ENV] = envData[6][ENV] + envData[6][dENV]; + } + else + { + UpdateEnv(6, t); + } + if ((t % BFStep2) == 0) + { + BdF2 = randmax * static_cast(rand()) - 0.5f; + } + BPhi2 = BPhi2 + BF2 + BQ2 * BdF2; + botmp = t - tpos; + DF[botmp] = DF[botmp] + static_cast(cos(fmod(BPhi2, TwoPi))) * envData[6][ENV] * BL2; + } + if (t >= envData[6][MAX]) + { + BON2 = 0; + } + } + + for (t = tpos; t <= tplus; t++) + { + if (OON == 1) // overtones + { + if (t < envData[3][NEXTT]) + { + envData[3][ENV] = envData[3][ENV] + envData[3][dENV]; + } + else + { + if (t >= envData[3][MAX]) // wait for OT2 + { + envData[3][ENV] = 0; + envData[3][dENV] = 0; + envData[3][NEXTT] = 999999; + } + else + { + UpdateEnv(3, t); + } + } + // + if (t < envData[4][NEXTT]) + { + envData[4][ENV] = envData[4][ENV] + envData[4][dENV]; + } + else + { + if (t >= envData[4][MAX]) // wait for OT1 + { + envData[4][ENV] = 0; + envData[4][dENV] = 0; + envData[4][NEXTT] = 999999; + } + else + { + UpdateEnv(4, t); + } + } + // + TphiStart = TphiStart + phi[t - tpos]; + if (OF1Sync == 1) + { + Ophi1 = TphiStart * OF1; + } + else + { + Ophi1 = Ophi1 + OF1; + } + if (OF2Sync == 1) + { + Ophi2 = TphiStart * OF2; + } + else + { + Ophi2 = Ophi2 + OF2; + } + Ot = 0.0f; + switch (OMode) + { + case 0: // add + Ot = OBal1 * envData[3][ENV] * waveform(Ophi1, OW1); + Ot = OL * (Ot + OBal2 * envData[4][ENV] * waveform(Ophi2, OW2)); + break; + + case 1: // FM + Ot = ODrive * envData[4][ENV] * waveform(Ophi2, OW2); + Ot = OL * envData[3][ENV] * waveform(Ophi1 + Ot, OW1); + break; + + case 2: // RM + Ot = (1 - ODrive / 8) + (((ODrive / 8) * envData[4][ENV]) * waveform(Ophi2, OW2)); + Ot = OL * envData[3][ENV] * waveform(Ophi1, OW1) * Ot; + break; + + case 3: // 808 Cymbal + for (j = 0; j < 6; j++) + { + Oc[j][0] += 1.0f; + + if (Oc[j][0] > Oc[j][1]) + { + Oc[j][0] -= Oc[j][1]; + Ot = OL * envData[3][ENV]; + } + } + Ocf1 = envData[4][ENV] * OcF; // filter freq + Oc0 += Ocf1 * Oc1; + Oc1 += Ocf1 * (Ot + Oc2 - OcQ * Oc1 - Oc0); // bpf + Oc2 = Ot; + Ot = Oc1; + break; + } + } + + if (MainFilter == 1) // filter overtones + { + if (t < envData[7][NEXTT]) + { + envData[7][ENV] = envData[7][ENV] + envData[7][dENV]; + } + else + { + UpdateEnv(7, t); + } + + MFtmp = envData[7][ENV]; + if (MFtmp > 0.2f) + { + MFfb = 1.001f - static_cast(std::pow(10.0f, MFtmp - 1)); + } + else + { + MFfb = 0.999f - 0.7824f * MFtmp; + } + + MFtmp = Ot + MFres * (1.f + (1.f / MFfb)) * (MFin - MFout); + MFin = MFfb * (MFin - MFtmp) + MFtmp; + MFout = MFfb * (MFout - MFin) + MFin; + + DF[t - tpos] = DF[t - tpos] + (MFout - (HighPass * Ot)); + } + else if (MainFilter == 2) // filter all + { + if (t < envData[7][NEXTT]) + { + envData[7][ENV] = envData[7][ENV] + envData[7][dENV]; + } + else + { + UpdateEnv(7, t); + } + + MFtmp = envData[7][ENV]; + if (MFtmp > 0.2f) + { + MFfb = 1.001f - static_cast(std::pow(10.0f, MFtmp - 1)); + } + else + { + MFfb = 0.999f - 0.7824f * MFtmp; + } + + MFtmp = DF[t - tpos] + Ot + MFres * (1.f + (1.f / MFfb)) * (MFin - MFout); + MFin = MFfb * (MFin - MFtmp) + MFtmp; + MFout = MFfb * (MFout - MFin) + MFin; + + DF[t - tpos] = MFout - (HighPass * (DF[t - tpos] + Ot)); + } + // PG: Ot is uninitialized + else + { + DF[t - tpos] = DF[t - tpos] + Ot; // no filter + } + } + + if (DiON == 1) // bit resolution + { + for (j = 0; j < 1200; j++) + { + DF[j] = DGain * static_cast(DF[j] / DAtten); + } + + for (j = 0; j < 1200; j += DStep) // downsampling + { + DownAve = 0; + DownStart = j; + DownEnd = j + DStep - 1; + for (jj = DownStart; jj <= DownEnd; jj++) + { + DownAve = DownAve + DF[jj]; + } + DownAve = DownAve / DStep; + for (jj = DownStart; jj <= DownEnd; jj++) + { + DF[jj] = DownAve; + } + } + } + else + { + for (j = 0; j < 1200; j++) + { + DF[j] *= DGain; + } + } + + for (j = 0; j < 1200; j++) // clipping + output + { + if (DF[j] > clippoint) + { + wave[wavewords++] = clippoint; + } + else if (DF[j] < -clippoint) + { + wave[wavewords++] = -clippoint; + } + else + { + wave[wavewords++] = static_cast(DF[j]); + } + + for (int c = 1; c < channels; c++) + { + wave[wavewords] = wave[wavewords - 1]; + wavewords++; + } + } + + tpos = tpos + 1200; + } + + /* + if(wavemode==0) + { + fwrite(wave, 2, Length, fp); //write data + fwrite(&WI, 1, 44, fp); //write INFO chunk + fwrite(&comment, 1, commentLen, fp); + fclose(fp); + } + wavemode = 0; //force compatibility!! + */ + + return Length; } - } // namespace lmms diff --git a/src/core/Effect.cpp b/src/core/Effect.cpp index 51b701d9b54..b6b2840515a 100644 --- a/src/core/Effect.cpp +++ b/src/core/Effect.cpp @@ -31,6 +31,8 @@ #include "EffectView.h" #include "ConfigManager.h" +#include "SampleFrame.h" +#include "lmms_constants.h" namespace lmms { @@ -61,6 +63,10 @@ Effect::Effect( const Plugin::Descriptor * _desc, { m_autoQuitDisabled = true; } + + // Call the virtual method onEnabledChanged so that effects can react to changes, + // e.g. by resetting state. + connect(&m_enabledModel, &BoolModel::dataChanged, [this] { onEnabledChanged(); }); } @@ -149,7 +155,7 @@ void Effect::checkGate( double _out_sum ) // Check whether we need to continue processing input. Restart the // counter if the threshold has been exceeded. - if( _out_sum - gate() <= typeInfo::minEps() ) + if (_out_sum - gate() <= F_EPSILON) { incrementBufferCount(); if( bufferCount() > timeout() ) @@ -195,9 +201,9 @@ void Effect::reinitSRC() -void Effect::resample( int _i, const sampleFrame * _src_buf, +void Effect::resample( int _i, const SampleFrame* _src_buf, sample_rate_t _src_sr, - sampleFrame * _dst_buf, sample_rate_t _dst_sr, + SampleFrame* _dst_buf, sample_rate_t _dst_sr, f_cnt_t _frames ) { if( m_srcState[_i] == nullptr ) @@ -210,8 +216,8 @@ void Effect::resample( int _i, const sampleFrame * _src_buf, m_srcData[_i].data_out = _dst_buf[0].data (); m_srcData[_i].src_ratio = (double) _dst_sr / _src_sr; m_srcData[_i].end_of_input = 0; - int error; - if( ( error = src_process( m_srcState[_i], &m_srcData[_i] ) ) ) + + if (int error = src_process(m_srcState[_i], &m_srcData[_i])) { qFatal( "Effect::resample(): error while resampling: %s\n", src_strerror( error ) ); diff --git a/src/core/EffectChain.cpp b/src/core/EffectChain.cpp index 4da5c519787..0a6a496f355 100644 --- a/src/core/EffectChain.cpp +++ b/src/core/EffectChain.cpp @@ -184,7 +184,7 @@ void EffectChain::moveUp( Effect * _effect ) -bool EffectChain::processAudioBuffer( sampleFrame * _buf, const fpp_t _frames, bool hasInputNoise ) +bool EffectChain::processAudioBuffer( SampleFrame* _buf, const fpp_t _frames, bool hasInputNoise ) { if( m_enabledModel.value() == false ) { diff --git a/src/core/Engine.cpp b/src/core/Engine.cpp index 6c810472179..9435eb69c06 100644 --- a/src/core/Engine.cpp +++ b/src/core/Engine.cpp @@ -126,15 +126,6 @@ void Engine::destroy() -bool Engine::ignorePluginBlacklist() -{ - const char* envVar = getenv("LMMS_IGNORE_BLACKLIST"); - return (envVar && *envVar); -} - - - - float Engine::framesPerTick(sample_rate_t sampleRate) { return sampleRate * 60.0f * 4 / @@ -146,7 +137,7 @@ float Engine::framesPerTick(sample_rate_t sampleRate) void Engine::updateFramesPerTick() { - s_framesPerTick = s_audioEngine->processingSampleRate() * 60.0f * 4 / DefaultTicksPerBar / s_song->getTempo(); + s_framesPerTick = s_audioEngine->outputSampleRate() * 60.0f * 4 / DefaultTicksPerBar / s_song->getTempo(); } @@ -171,4 +162,4 @@ void *Engine::pickDndPluginKey() Engine * Engine::s_instanceOfMe = nullptr; -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/src/core/EnvelopeAndLfoParameters.cpp b/src/core/EnvelopeAndLfoParameters.cpp index 611700c5197..a3c3bcf9184 100644 --- a/src/core/EnvelopeAndLfoParameters.cpp +++ b/src/core/EnvelopeAndLfoParameters.cpp @@ -97,13 +97,13 @@ EnvelopeAndLfoParameters::EnvelopeAndLfoParameters( Model * _parent ) : Model( _parent ), m_used( false ), - m_predelayModel( 0.0, 0.0, 2.0, 0.001, this, tr( "Env pre-delay" ) ), - m_attackModel( 0.0, 0.0, 2.0, 0.001, this, tr( "Env attack" ) ), - m_holdModel( 0.5, 0.0, 2.0, 0.001, this, tr( "Env hold" ) ), - m_decayModel( 0.5, 0.0, 2.0, 0.001, this, tr( "Env decay" ) ), - m_sustainModel( 0.5, 0.0, 1.0, 0.001, this, tr( "Env sustain" ) ), - m_releaseModel( 0.1, 0.0, 2.0, 0.001, this, tr( "Env release" ) ), - m_amountModel( 0.0, -1.0, 1.0, 0.005, this, tr( "Env mod amount" ) ), + m_predelayModel(0.f, 0.f, 2.f, 0.001f, this, tr("Env pre-delay")), + m_attackModel(0.f, 0.f, 2.f, 0.001f, this, tr("Env attack")), + m_holdModel(0.5f, 0.f, 2.f, 0.001f, this, tr("Env hold")), + m_decayModel(0.5f, 0.f, 2.f, 0.001f, this, tr("Env decay")), + m_sustainModel(0.5f, 0.f, 1.f, 0.001f, this, tr("Env sustain")), + m_releaseModel(0.1f, 0.f, 2.f, 0.001f, this, tr("Env release")), + m_amountModel(0.f, -1.f, 1.f, 0.005f, this, tr("Env mod amount")), m_valueForZeroAmount( _value_for_zero_amount ), m_pahdFrames( 0 ), m_rFrames( 0 ), @@ -111,12 +111,12 @@ EnvelopeAndLfoParameters::EnvelopeAndLfoParameters( m_rEnv( nullptr ), m_pahdBufSize( 0 ), m_rBufSize( 0 ), - m_lfoPredelayModel( 0.0, 0.0, 1.0, 0.001, this, tr( "LFO pre-delay" ) ), - m_lfoAttackModel( 0.0, 0.0, 1.0, 0.001, this, tr( "LFO attack" ) ), - m_lfoSpeedModel( 0.1, 0.001, 1.0, 0.0001, - SECS_PER_LFO_OSCILLATION * 1000.0, this, - tr( "LFO frequency" ) ), - m_lfoAmountModel( 0.0, -1.0, 1.0, 0.005, this, tr( "LFO mod amount" ) ), + m_lfoPredelayModel(0.f, 0.f, 1.f, 0.001f, this, tr("LFO pre-delay")), + m_lfoAttackModel(0.f, 0.f, 1.f, 0.001f, this, tr("LFO attack")), + m_lfoSpeedModel(0.1f, 0.001f, 1.f, 0.0001f, + SECS_PER_LFO_OSCILLATION * 1000.f, this, + tr("LFO frequency")), + m_lfoAmountModel(0.f, -1.f, 1.f, 0.005f, this, tr("LFO mod amount")), m_lfoWaveModel( static_cast(LfoShape::SineWave), 0, NumLfoShapes, this, tr( "LFO wave shape" ) ), m_x100Model( false, this, tr( "LFO frequency x 100" ) ), m_controlEnvAmountModel( false, this, tr( "Modulate env amount" ) ), @@ -299,11 +299,6 @@ void EnvelopeAndLfoParameters::fillLevel( float * _buf, f_cnt_t _frame, { QMutexLocker m(&m_paramMutex); - if( _frame < 0 || _release_begin < 0 ) - { - return; - } - fillLfoLevel( _buf, _frame, _frames ); for( fpp_t offset = 0; offset < _frames; ++offset, ++_buf, ++_frame ) @@ -410,7 +405,7 @@ void EnvelopeAndLfoParameters::updateSampleVars() QMutexLocker m(&m_paramMutex); const float frames_per_env_seg = SECS_PER_ENV_SEGMENT * - Engine::audioEngine()->processingSampleRate(); + Engine::audioEngine()->outputSampleRate(); // TODO: Remove the expKnobVals, time should be linear const auto predelay_frames = static_cast(frames_per_env_seg * expKnobVal(m_predelayModel.value())); @@ -509,7 +504,7 @@ void EnvelopeAndLfoParameters::updateSampleVars() const float frames_per_lfo_oscillation = SECS_PER_LFO_OSCILLATION * - Engine::audioEngine()->processingSampleRate(); + Engine::audioEngine()->outputSampleRate(); m_lfoPredelayFrames = static_cast( frames_per_lfo_oscillation * expKnobVal( m_lfoPredelayModel.value() ) ); m_lfoAttackFrames = static_cast( frames_per_lfo_oscillation * diff --git a/src/core/FileSearch.cpp b/src/core/FileSearch.cpp new file mode 100644 index 00000000000..8a668360e2c --- /dev/null +++ b/src/core/FileSearch.cpp @@ -0,0 +1,92 @@ +/* + * FileSearch.cpp - File system search task + * + * Copyright (c) 2024 saker + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "FileSearch.h" + +#include +#include +#include + +#include + +namespace lmms { +FileSearch::FileSearch(const QString& filter, const QStringList& paths, const QStringList& extensions, + const QStringList& excludedPaths, QDir::Filters dirFilters, QDir::SortFlags sortFlags) + : m_filter(filter) + , m_paths(paths) + , m_extensions(extensions) + , m_excludedPaths(excludedPaths) + , m_dirFilters(dirFilters) + , m_sortFlags(sortFlags) +{ +} + +void FileSearch::operator()() +{ + auto stack = QFileInfoList{}; + for (const auto& path : m_paths) + { + if (m_excludedPaths.contains(path)) { continue; } + + auto dir = QDir{path}; + stack.append(dir.entryInfoList(m_dirFilters, m_sortFlags)); + + while (!stack.empty()) + { + if (m_cancel.load(std::memory_order_relaxed)) { return; } + + const auto info = stack.takeFirst(); + const auto entryPath = info.absoluteFilePath(); + if (m_excludedPaths.contains(entryPath)) { continue; } + + const auto name = info.fileName(); + const auto validFile = info.isFile() && m_extensions.contains(info.suffix(), Qt::CaseInsensitive); + const auto passesFilter = name.contains(m_filter, Qt::CaseInsensitive); + + if ((validFile || info.isDir()) && passesFilter) + { + std::this_thread::sleep_for(std::chrono::milliseconds{MillisecondsBetweenResults}); + emit foundMatch(this, entryPath); + } + + if (info.isDir()) + { + dir.setPath(entryPath); + const auto entries = dir.entryInfoList(m_dirFilters, m_sortFlags); + + // Reverse to maintain the sorting within this directory when popped + std::for_each(entries.rbegin(), entries.rend(), [&stack](const auto& entry) { stack.push_front(entry); }); + } + } + } + + emit searchCompleted(this); +} + +void FileSearch::cancel() +{ + m_cancel.store(true, std::memory_order_relaxed); +} + +} // namespace lmms diff --git a/src/core/Instrument.cpp b/src/core/Instrument.cpp index a7cfc467ba4..9237ad70dbe 100644 --- a/src/core/Instrument.cpp +++ b/src/core/Instrument.cpp @@ -28,6 +28,7 @@ #include "DummyInstrument.h" #include "InstrumentTrack.h" +#include "lmms_basics.h" #include "lmms_constants.h" @@ -37,13 +38,15 @@ namespace lmms Instrument::Instrument(InstrumentTrack * _instrument_track, const Descriptor * _descriptor, - const Descriptor::SubPluginFeatures::Key *key) : + const Descriptor::SubPluginFeatures::Key *key, + Flags flags) : Plugin(_descriptor, nullptr/* _instrument_track*/, key), - m_instrumentTrack( _instrument_track ) + m_instrumentTrack( _instrument_track ), + m_flags(flags) { } -void Instrument::play( sampleFrame * ) +void Instrument::play( SampleFrame* ) { } @@ -87,7 +90,7 @@ bool Instrument::isFromTrack( const Track * _track ) const } // helper function for Instrument::applyFadeIn -static int countZeroCrossings(sampleFrame *buf, fpp_t start, fpp_t frames) +static int countZeroCrossings(SampleFrame* buf, fpp_t start, fpp_t frames) { // zero point crossing counts of all channels auto zeroCrossings = std::array{}; @@ -126,7 +129,7 @@ fpp_t getFadeInLength(float maxLength, fpp_t frames, int zeroCrossings) } -void Instrument::applyFadeIn(sampleFrame * buf, NotePlayHandle * n) +void Instrument::applyFadeIn(SampleFrame* buf, NotePlayHandle * n) { const static float MAX_FADE_IN_LENGTH = 85.0; f_cnt_t total = n->totalFramesPlayed(); @@ -177,13 +180,13 @@ void Instrument::applyFadeIn(sampleFrame * buf, NotePlayHandle * n) } } -void Instrument::applyRelease( sampleFrame * buf, const NotePlayHandle * _n ) +void Instrument::applyRelease( SampleFrame* buf, const NotePlayHandle * _n ) { const auto fpp = Engine::audioEngine()->framesPerPeriod(); const auto releaseFrames = desiredReleaseFrames(); const auto endFrame = _n->framesLeft(); - const auto startFrame = std::max(0, endFrame - releaseFrames); + const auto startFrame = endFrame - std::min(endFrame, releaseFrames); for (auto f = startFrame; f < endFrame && f < fpp; f++) { @@ -195,7 +198,16 @@ void Instrument::applyRelease( sampleFrame * buf, const NotePlayHandle * _n ) } } +float Instrument::computeReleaseTimeMsByFrameCount(f_cnt_t frames) const +{ + return frames / getSampleRate() * 1000.; +} + +sample_rate_t Instrument::getSampleRate() const +{ + return Engine::audioEngine()->outputSampleRate(); +} QString Instrument::fullDisplayName() const diff --git a/src/core/InstrumentFunctions.cpp b/src/core/InstrumentFunctions.cpp index 976363d3d08..3687c0b7445 100644 --- a/src/core/InstrumentFunctions.cpp +++ b/src/core/InstrumentFunctions.cpp @@ -31,6 +31,9 @@ #include "InstrumentTrack.h" #include "PresetPreviewPlayHandle.h" +#include +#include + namespace lmms { @@ -348,10 +351,11 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) _n->setMasterNote(); const int selected_arp = m_arpModel.value(); + const auto arpMode = static_cast(m_arpModeModel.value()); - ConstNotePlayHandleList cnphv = NotePlayHandle::nphsOfInstrumentTrack( _n->instrumentTrack() ); + ConstNotePlayHandleList cnphv = NotePlayHandle::nphsOfInstrumentTrack(_n->instrumentTrack()); - if( static_cast(m_arpModeModel.value()) != ArpMode::Free && cnphv.size() == 0 ) + if(arpMode != ArpMode::Free && cnphv.size() == 0 ) { // maybe we're playing only a preset-preview-note? cnphv = PresetPreviewPlayHandle::nphsOfInstrumentTrack( _n->instrumentTrack() ); @@ -363,23 +367,38 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) } } + // avoid playing same key for all + // currently playing notes if sort mode is enabled + if (arpMode == ArpMode::Sort && _n != cnphv.first()) { return; } + const InstrumentFunctionNoteStacking::ChordTable & chord_table = InstrumentFunctionNoteStacking::ChordTable::getInstance(); const int cur_chord_size = chord_table.chords()[selected_arp].size(); - const int range = static_cast(cur_chord_size * m_arpRangeModel.value() * m_arpRepeatsModel.value()); - const int total_range = range * cnphv.size(); + const int total_chord_size = cur_chord_size * cnphv.size(); + // how many notes are in a single chord (multiplied by range) + const int singleNoteRange = static_cast(cur_chord_size * m_arpRangeModel.value() * m_arpRepeatsModel.value()); + // how many notes are in the final chord + const int range = arpMode == ArpMode::Sort ? singleNoteRange * cnphv.size() : singleNoteRange; + + if (arpMode == ArpMode::Sort) + { + std::sort(cnphv.begin(), cnphv.end(), [](const NotePlayHandle* a, const NotePlayHandle* b) + { + return a->key() < b->key(); + }); + } // number of frames that every note should be played - const auto arp_frames = (f_cnt_t)(m_arpTimeModel.value() / 1000.0f * Engine::audioEngine()->processingSampleRate()); + const auto arp_frames = (f_cnt_t)(m_arpTimeModel.value() / 1000.0f * Engine::audioEngine()->outputSampleRate()); const auto gated_frames = (f_cnt_t)(m_arpGateModel.value() * arp_frames / 100.0f); // used for calculating remaining frames for arp-note, we have to add // arp_frames-1, otherwise the first arp-note will not be setup // correctly... -> arp_frames frames silence at the start of every note! - int cur_frame = ( ( static_cast(m_arpModeModel.value()) != ArpMode::Free ) ? + int cur_frame = (arpMode != ArpMode::Free ? cnphv.first()->totalFramesPlayed() : - _n->totalFramesPlayed() ) + arp_frames - 1; + _n->totalFramesPlayed()) + arp_frames - 1; // used for loop - f_cnt_t frames_processed = ( static_cast(m_arpModeModel.value()) != ArpMode::Free ) ? cnphv.first()->noteOffset() : _n->noteOffset(); + f_cnt_t frames_processed = arpMode != ArpMode::Free ? cnphv.first()->noteOffset() : _n->noteOffset(); while( frames_processed < Engine::audioEngine()->framesPerPeriod() ) { @@ -395,17 +414,6 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) frames_processed += remaining_frames_for_cur_arp; - // in sorted mode: is it our turn or do we have to be quiet for - // now? - if( static_cast(m_arpModeModel.value()) == ArpMode::Sort && - ( ( cur_frame / arp_frames ) % total_range ) / range != (f_cnt_t) _n->index() ) - { - // update counters - frames_processed += arp_frames; - cur_frame += arp_frames; - continue; - } - // Skip notes randomly if( m_arpSkipModel.value() ) { @@ -433,46 +441,28 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) int cur_arp_idx = 0; // process according to arpeggio-direction... - if( dir == ArpDirection::Up ) + if (dir == ArpDirection::Up || dir == ArpDirection::Down) { - cur_arp_idx = ( cur_frame / arp_frames ) % range; + cur_arp_idx = (cur_frame / arp_frames) % range; } - else if( dir == ArpDirection::Down ) - { - cur_arp_idx = range - ( cur_frame / arp_frames ) % - range - 1; - } - else if( dir == ArpDirection::UpAndDown && range > 1 ) + else if ((dir == ArpDirection::UpAndDown || dir == ArpDirection::DownAndUp) && range > 1) { // imagine, we had to play the arp once up and then // once down -> makes 2 * range possible notes... // because we don't play the lower and upper notes // twice, we have to subtract 2 - cur_arp_idx = ( cur_frame / arp_frames ) % ( range * 2 - 2 ); + cur_arp_idx = (cur_frame / arp_frames) % (range * 2 - (2 * static_cast(m_arpRepeatsModel.value()))); // if greater than range, we have to play down... // looks like the code for arp_dir==DOWN... :) - if( cur_arp_idx >= range ) + if (cur_arp_idx >= range) { - cur_arp_idx = range - cur_arp_idx % ( range - 1 ) - 1; + cur_arp_idx = range - cur_arp_idx % (range - 1) - static_cast(m_arpRepeatsModel.value()); } } - else if( dir == ArpDirection::DownAndUp && range > 1 ) - { - // copied from ArpDirection::UpAndDown above - cur_arp_idx = ( cur_frame / arp_frames ) % ( range * 2 - 2 ); - // if greater than range, we have to play down... - // looks like the code for arp_dir==DOWN... :) - if( cur_arp_idx >= range ) - { - cur_arp_idx = range - cur_arp_idx % ( range - 1 ) - 1; - } - // inverts direction - cur_arp_idx = range - cur_arp_idx - 1; - } else if( dir == ArpDirection::Random ) { // just pick a random chord-index - cur_arp_idx = (int)( range * ( (float) rand() / (float) RAND_MAX ) ); + cur_arp_idx = static_cast(range * static_cast(rand()) / static_cast(RAND_MAX)); } // Divide cur_arp_idx with wanted repeats. The repeat feature will not affect random notes. @@ -482,12 +472,33 @@ void InstrumentFunctionArpeggio::processNote( NotePlayHandle * _n ) if( m_arpCycleModel.value() && dir != ArpDirection::Random ) { cur_arp_idx *= m_arpCycleModel.value() + 1; - cur_arp_idx %= static_cast( range / m_arpRepeatsModel.value() ); + cur_arp_idx %= static_cast(range / m_arpRepeatsModel.value()); + } + + // If ArpDirection::Down or ArpDirection::DownAndUp, invert the final range. + if (dir == ArpDirection::Down || dir == ArpDirection::DownAndUp) + { + cur_arp_idx = static_cast(range / m_arpRepeatsModel.value()) - cur_arp_idx - 1; } // now calculate final key for our arp-note - const int sub_note_key = base_note_key + (cur_arp_idx / cur_chord_size ) * + int sub_note_key = 0; + if (arpMode != ArpMode::Sort) + { + sub_note_key = base_note_key + (cur_arp_idx / cur_chord_size) * KeysPerOctave + chord_table.chords()[selected_arp][cur_arp_idx % cur_chord_size]; + } + else + { + const auto octaveDiv = std::div(cur_arp_idx, total_chord_size); + const int octave = octaveDiv.quot; + const auto arpDiv = std::div(octaveDiv.rem, cnphv.size()); + const int arpIndex = arpDiv.rem; + const int chordIndex = arpDiv.quot; + sub_note_key = cnphv[arpIndex]->key() + + chord_table.chords()[selected_arp][chordIndex] + + octave * KeysPerOctave; + } // range-checking if( sub_note_key >= NumKeys || diff --git a/src/core/InstrumentPlayHandle.cpp b/src/core/InstrumentPlayHandle.cpp index 097719ad83d..afae852a067 100644 --- a/src/core/InstrumentPlayHandle.cpp +++ b/src/core/InstrumentPlayHandle.cpp @@ -40,30 +40,28 @@ InstrumentPlayHandle::InstrumentPlayHandle(Instrument * instrument, InstrumentTr setAudioPort(instrumentTrack->audioPort()); } -void InstrumentPlayHandle::play(sampleFrame * working_buffer) +void InstrumentPlayHandle::play(SampleFrame* working_buffer) { InstrumentTrack * instrumentTrack = m_instrument->instrumentTrack(); // ensure that all our nph's have been processed first auto nphv = NotePlayHandle::nphsOfInstrumentTrack(instrumentTrack, true); - + bool nphsLeft; do { nphsLeft = false; - for (const NotePlayHandle * constNotePlayHandle : nphv) + for (const auto& handle : nphv) { - if (constNotePlayHandle->state() != ThreadableJob::ProcessingState::Done && - !constNotePlayHandle->isFinished()) + if (handle->state() != ThreadableJob::ProcessingState::Done && !handle->isFinished()) { nphsLeft = true; - NotePlayHandle * notePlayHandle = const_cast(constNotePlayHandle); - notePlayHandle->process(); + const_cast(handle)->process(); } } } while (nphsLeft); - + m_instrument->play(working_buffer); // Process the audio buffer that the instrument has just worked on... diff --git a/src/core/InstrumentSoundShaping.cpp b/src/core/InstrumentSoundShaping.cpp index 07c3bbf7c0f..eca06f9a2bb 100644 --- a/src/core/InstrumentSoundShaping.cpp +++ b/src/core/InstrumentSoundShaping.cpp @@ -64,9 +64,9 @@ InstrumentSoundShaping::InstrumentSoundShaping( m_filterEnabledModel( false, this ), m_filterModel( this, tr( "Filter type" ) ), m_filterCutModel( 14000.0, 1.0, 14000.0, 1.0, this, tr( "Cutoff frequency" ) ), - m_filterResModel( 0.5, BasicFilters<>::minQ(), 10.0, 0.01, this, tr( "Q/Resonance" ) ) + m_filterResModel(0.5f, BasicFilters<>::minQ(), 10.f, 0.01f, this, tr("Q/Resonance")) { - for( int i = 0; i < NumTargets; ++i ) + for (auto i = std::size_t{0}; i < NumTargets; ++i) { float value_for_zero_amount = 0.0; if( static_cast(i) == Target::Volume ) @@ -127,7 +127,7 @@ float InstrumentSoundShaping::volumeLevel( NotePlayHandle* n, const f_cnt_t fram -void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer, +void InstrumentSoundShaping::processAudioBuffer( SampleFrame* buffer, const fpp_t frames, NotePlayHandle* n ) { @@ -158,7 +158,7 @@ void InstrumentSoundShaping::processAudioBuffer( sampleFrame* buffer, if( n->m_filter == nullptr ) { - n->m_filter = std::make_unique>( Engine::audioEngine()->processingSampleRate() ); + n->m_filter = std::make_unique>( Engine::audioEngine()->outputSampleRate() ); } n->m_filter->setFilterType( static_cast::FilterType>(m_filterModel.value()) ); @@ -279,7 +279,7 @@ f_cnt_t InstrumentSoundShaping::envFrames( const bool _only_vol ) const if( _only_vol == false ) { - for( int i = static_cast(Target::Volume)+1; i < NumTargets; ++i ) + for (auto i = static_cast(Target::Volume) + 1; i < NumTargets; ++i) { if( m_envLfoParameters[i]->isUsed() && m_envLfoParameters[i]->PAHD_Frames() > ret_val ) @@ -303,7 +303,7 @@ f_cnt_t InstrumentSoundShaping::releaseFrames() const f_cnt_t ret_val = m_instrumentTrack->instrument()->desiredReleaseFrames(); - if( m_instrumentTrack->instrument()->flags().testFlag( Instrument::Flag::IsSingleStreamed ) ) + if (m_instrumentTrack->instrument()->isSingleStreamed()) { return ret_val; } @@ -313,7 +313,7 @@ f_cnt_t InstrumentSoundShaping::releaseFrames() const return m_envLfoParameters[static_cast(Target::Volume)]->releaseFrames(); } - for( int i = static_cast(Target::Volume)+1; i < NumTargets; ++i ) + for (auto i = static_cast(Target::Volume) + 1; i < NumTargets; ++i) { if( m_envLfoParameters[i]->isUsed() ) { @@ -333,7 +333,7 @@ void InstrumentSoundShaping::saveSettings( QDomDocument & _doc, QDomElement & _t m_filterResModel.saveSettings( _doc, _this, "fres" ); m_filterEnabledModel.saveSettings( _doc, _this, "fwet" ); - for( int i = 0; i < NumTargets; ++i ) + for (auto i = std::size_t{0}; i < NumTargets; ++i) { m_envLfoParameters[i]->saveState( _doc, _this ).setTagName( m_envLfoParameters[i]->nodeName() + @@ -356,7 +356,7 @@ void InstrumentSoundShaping::loadSettings( const QDomElement & _this ) { if( node.isElement() ) { - for( int i = 0; i < NumTargets; ++i ) + for (auto i = std::size_t{0}; i < NumTargets; ++i) { if( node.nodeName() == m_envLfoParameters[i]->nodeName() + diff --git a/src/core/LadspaManager.cpp b/src/core/LadspaManager.cpp index 064c928efd2..e4d472bd109 100644 --- a/src/core/LadspaManager.cpp +++ b/src/core/LadspaManager.cpp @@ -122,14 +122,8 @@ LadspaManager::~LadspaManager() LadspaManagerDescription * LadspaManager::getDescription( const ladspa_key_t & _plugin ) { - if( m_ladspaManagerMap.contains( _plugin ) ) - { - return( m_ladspaManagerMap[_plugin] ); - } - else - { - return( nullptr ); - } + auto const it = m_ladspaManagerMap.find(_plugin); + return it != m_ladspaManagerMap.end() ? *it : nullptr; } @@ -139,11 +133,7 @@ void LadspaManager::addPlugins( LADSPA_Descriptor_Function _descriptor_func, const QString & _file ) { - const LADSPA_Descriptor * descriptor; - - for( long pluginIndex = 0; - ( descriptor = _descriptor_func( pluginIndex ) ) != nullptr; - ++pluginIndex ) + for (long pluginIndex = 0; const auto descriptor = _descriptor_func(pluginIndex); ++pluginIndex) { ladspa_key_t key( _file, QString( descriptor->Label ) ); if( m_ladspaManagerMap.contains( key ) ) @@ -523,24 +513,16 @@ bool LadspaManager::isInteger( const ladspa_key_t & _plugin, bool LadspaManager::isEnum( const ladspa_key_t & _plugin, uint32_t _port ) { - if( m_ladspaManagerMap.contains( _plugin ) - && _port < getPortCount( _plugin ) ) + auto const * desc = getDescriptor(_plugin); + if (desc && _port < desc->PortCount) { - LADSPA_Descriptor_Function descriptorFunction = - m_ladspaManagerMap[_plugin]->descriptorFunction; - const LADSPA_Descriptor * descriptor = - descriptorFunction( - m_ladspaManagerMap[_plugin]->index ); LADSPA_PortRangeHintDescriptor hintDescriptor = - descriptor->PortRangeHints[_port].HintDescriptor; + desc->PortRangeHints[_port].HintDescriptor; // This is an LMMS extension to ladspa - return( LADSPA_IS_HINT_INTEGER( hintDescriptor ) && - LADSPA_IS_HINT_TOGGLED( hintDescriptor ) ); - } - else - { - return( false ); + return LADSPA_IS_HINT_INTEGER(hintDescriptor) && LADSPA_IS_HINT_TOGGLED(hintDescriptor); } + + return false; } @@ -566,22 +548,20 @@ const void * LadspaManager::getImplementationData( -const LADSPA_Descriptor * LadspaManager::getDescriptor( - const ladspa_key_t & _plugin ) +const LADSPA_Descriptor * LadspaManager::getDescriptor(const ladspa_key_t & _plugin) { - if( m_ladspaManagerMap.contains( _plugin ) ) - { - LADSPA_Descriptor_Function descriptorFunction = - m_ladspaManagerMap[_plugin]->descriptorFunction; - const LADSPA_Descriptor * descriptor = - descriptorFunction( - m_ladspaManagerMap[_plugin]->index ); - return( descriptor ); - } - else + auto const it = m_ladspaManagerMap.find(_plugin); + if (it != m_ladspaManagerMap.end()) { - return( nullptr ); + auto const plugin = *it; + + LADSPA_Descriptor_Function descriptorFunction = plugin->descriptorFunction; + const LADSPA_Descriptor* descriptor = descriptorFunction(plugin->index); + + return descriptor; } + + return nullptr; } diff --git a/src/core/LfoController.cpp b/src/core/LfoController.cpp index 152e0ad8b78..96ea71f7b50 100644 --- a/src/core/LfoController.cpp +++ b/src/core/LfoController.cpp @@ -39,9 +39,9 @@ namespace lmms LfoController::LfoController( Model * _parent ) : Controller( ControllerType::Lfo, _parent, tr( "LFO Controller" ) ), - m_baseModel( 0.5, 0.0, 1.0, 0.001, this, tr( "Base value" ) ), - m_speedModel( 2.0, 0.01, 20.0, 0.0001, 20000.0, this, tr( "Oscillator speed" ) ), - m_amountModel( 1.0, -1.0, 1.0, 0.005, this, tr( "Oscillator amount" ) ), + m_baseModel(0.5f, 0.f, 1.f, 0.001f, this, tr("Base value")), + m_speedModel(2.f, 0.01f, 20.f, 0.0001f, 20000.f, this, tr("Oscillator speed")), + m_amountModel(1.f, -1.f, 1.f, 0.005f, this, tr("Oscillator amount")), m_phaseModel( 0.0, 0.0, 360.0, 4.0, this, tr( "Oscillator phase" ) ), m_waveModel( static_cast(Oscillator::WaveShape::Sine), 0, Oscillator::NumWaveShapes, this, tr( "Oscillator waveform" ) ), @@ -155,7 +155,7 @@ void LfoController::updatePhase() void LfoController::updateDuration() { - float newDurationF = Engine::audioEngine()->processingSampleRate() * m_speedModel.value(); + float newDurationF = Engine::audioEngine()->outputSampleRate() * m_speedModel.value(); switch(m_multiplierModel.value() ) { diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp index 83bebc2a07e..c52bce43310 100644 --- a/src/core/LinkedModelGroups.cpp +++ b/src/core/LinkedModelGroups.cpp @@ -143,9 +143,7 @@ bool LinkedModelGroup::containsModel(const QString &name) const void LinkedModelGroups::linkAllModels() { LinkedModelGroup* first = getGroup(0); - LinkedModelGroup* cur; - - for (std::size_t i = 1; (cur = getGroup(i)); ++i) + for (size_t i = 1; auto cur = getGroup(i); ++i) { first->linkControls(cur); } @@ -172,8 +170,7 @@ void LinkedModelGroups::saveSettings(QDomDocument& doc, QDomElement& that) void LinkedModelGroups::loadSettings(const QDomElement& that) { QDomElement models = that.firstChildElement("models"); - LinkedModelGroup* grp0; - if (!models.isNull() && (grp0 = getGroup(0))) + if (auto grp0 = getGroup(0); !models.isNull() && grp0) { // only load the first group, the others are linked to the first grp0->loadValues(models); diff --git a/src/core/LocklessAllocator.cpp b/src/core/LocklessAllocator.cpp index 919093906c3..92eadd75128 100644 --- a/src/core/LocklessAllocator.cpp +++ b/src/core/LocklessAllocator.cpp @@ -71,8 +71,7 @@ LocklessAllocator::LocklessAllocator( size_t nmemb, size_t size ) LocklessAllocator::~LocklessAllocator() { - int available = m_available; - if( available != m_capacity ) + if (m_available != m_capacity) { fprintf( stderr, "LocklessAllocator: " "Destroying with elements still allocated\n" ); @@ -110,7 +109,7 @@ void * LocklessAllocator::alloc() // Some of these CAS loops could probably use relaxed atomics, as discussed // in http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange. // Let's use sequentially-consistent ops to be safe for now. - int available = m_available.load(); + auto available = m_available.load(); do { if( !available ) diff --git a/src/core/MemoryHelper.cpp b/src/core/MemoryHelper.cpp deleted file mode 100644 index de80ef7701d..00000000000 --- a/src/core/MemoryHelper.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2014 Simon Symeonidis - * Copyright (c) 2008-2014 Tobias Doerffel - * - * This file is part of LMMS - https://lmms.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program (see COPYING); if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - */ - -#include - -#include "lmms_basics.h" -#include "MemoryHelper.h" - -namespace lmms -{ - - -/** - * Allocate a number of bytes and return them. - * @param byteNum is the number of bytes - */ -void* MemoryHelper::alignedMalloc( size_t byteNum ) -{ - char *ptr, *ptr2, *aligned_ptr; - int align_mask = LMMS_ALIGN_SIZE - 1; - - ptr = static_cast( malloc( byteNum + LMMS_ALIGN_SIZE + sizeof( int ) ) ); - - if( ptr == nullptr ) return nullptr; - - ptr2 = ptr + sizeof( int ); - aligned_ptr = ptr2 + ( LMMS_ALIGN_SIZE - ( ( size_t ) ptr2 & align_mask ) ); - - ptr2 = aligned_ptr - sizeof( int ); - *( ( int* ) ptr2 ) = ( int )( aligned_ptr - ptr ); - - return aligned_ptr; -} - - -/** - * Free an aligned buffer - * @param _buffer is the buffer to free - */ -void MemoryHelper::alignedFree( void* _buffer ) -{ - if( _buffer ) - { - int *ptr2 = static_cast( _buffer ) - 1; - _buffer = static_cast( _buffer ) - *ptr2; - free( _buffer ); - } -} - - -} // namespace lmms diff --git a/src/core/Microtuner.cpp b/src/core/Microtuner.cpp index 9c5ab871bc8..46d83017fa2 100644 --- a/src/core/Microtuner.cpp +++ b/src/core/Microtuner.cpp @@ -126,14 +126,14 @@ int Microtuner::octaveSize() const */ void Microtuner::updateScaleList(int index) { - if (index >= 0 && index < MaxScaleCount) + if (index >= 0 && static_cast(index) < MaxScaleCount) { m_scaleModel.replaceItem(index, QString::number(index) + ": " + Engine::getSong()->getScale(index)->getDescription()); } else { - for (int i = 0; i < MaxScaleCount; i++) + for (auto i = std::size_t{0}; i < MaxScaleCount; i++) { m_scaleModel.replaceItem(i, QString::number(i) + ": " + Engine::getSong()->getScale(i)->getDescription()); @@ -147,14 +147,14 @@ void Microtuner::updateScaleList(int index) */ void Microtuner::updateKeymapList(int index) { - if (index >= 0 && index < MaxKeymapCount) + if (index >= 0 && static_cast(index) < MaxKeymapCount) { m_keymapModel.replaceItem(index, QString::number(index) + ": " + Engine::getSong()->getKeymap(index)->getDescription()); } else { - for (int i = 0; i < MaxKeymapCount; i++) + for (auto i = std::size_t{0}; i < MaxKeymapCount; i++) { m_keymapModel.replaceItem(i, QString::number(i) + ": " + Engine::getSong()->getKeymap(i)->getDescription()); diff --git a/src/core/MixHelpers.cpp b/src/core/MixHelpers.cpp index 209640b70b2..01ea0386e58 100644 --- a/src/core/MixHelpers.cpp +++ b/src/core/MixHelpers.cpp @@ -32,6 +32,7 @@ #include #include "ValueBuffer.h" +#include "SampleFrame.h" @@ -43,7 +44,7 @@ namespace lmms::MixHelpers /*! \brief Function for applying MIXOP on all sample frames */ template -static inline void run( sampleFrame* dst, const sampleFrame* src, int frames, const MIXOP& OP ) +static inline void run( SampleFrame* dst, const SampleFrame* src, int frames, const MIXOP& OP ) { for( int i = 0; i < frames; ++i ) { @@ -53,18 +54,18 @@ static inline void run( sampleFrame* dst, const sampleFrame* src, int frames, co /*! \brief Function for applying MIXOP on all sample frames - split source */ template -static inline void run( sampleFrame* dst, const sample_t* srcLeft, const sample_t* srcRight, int frames, const MIXOP& OP ) +static inline void run( SampleFrame* dst, const sample_t* srcLeft, const sample_t* srcRight, int frames, const MIXOP& OP ) { for( int i = 0; i < frames; ++i ) { - const sampleFrame src = { srcLeft[i], srcRight[i] }; + const SampleFrame src = { srcLeft[i], srcRight[i] }; OP( dst[i], src ); } } -bool isSilent( const sampleFrame* src, int frames ) +bool isSilent( const SampleFrame* src, int frames ) { const float silenceThreshold = 0.0000001f; @@ -90,55 +91,49 @@ void setNaNHandler( bool use ) } /*! \brief Function for sanitizing a buffer of infs/nans - returns true if those are found */ -bool sanitize( sampleFrame * src, int frames ) +bool sanitize( SampleFrame* src, int frames ) { if( !useNaNHandler() ) { return false; } - bool found = false; - for( int f = 0; f < frames; ++f ) + for (int f = 0; f < frames; ++f) { - for( int c = 0; c < 2; ++c ) + auto& currentFrame = src[f]; + + if (currentFrame.containsInf() || currentFrame.containsNaN()) { - if( std::isinf( src[f][c] ) || std::isnan( src[f][c] ) ) - { - #ifdef LMMS_DEBUG + #ifdef LMMS_DEBUG // TODO don't use printf here printf("Bad data, clearing buffer. frame: "); - printf("%d: value %f\n", f, src[f][c]); - #endif - for( int f = 0; f < frames; ++f ) - { - for( int c = 0; c < 2; ++c ) - { - src[f][c] = 0.0f; - } - } - found = true; - return found; - } - else - { - src[f][c] = std::clamp(src[f][c], -1000.0f, 1000.0f); - } + printf("%d: value %f, %f\n", f, currentFrame.left(), currentFrame.right()); + #endif + + // Clear the whole buffer if a problem is found + zeroSampleFrames(src, frames); + + return true; } - } - return found; + else + { + currentFrame.clamp(sample_t(-1000.0), sample_t(1000.0)); + } + }; + + return false; } struct AddOp { - void operator()( sampleFrame& dst, const sampleFrame& src ) const + void operator()( SampleFrame& dst, const SampleFrame& src ) const { - dst[0] += src[0]; - dst[1] += src[1]; + dst += src; } } ; -void add( sampleFrame* dst, const sampleFrame* src, int frames ) +void add( SampleFrame* dst, const SampleFrame* src, int frames ) { run<>( dst, src, frames, AddOp() ); } @@ -149,17 +144,16 @@ struct AddMultipliedOp { AddMultipliedOp( float coeff ) : m_coeff( coeff ) { } - void operator()( sampleFrame& dst, const sampleFrame& src ) const + void operator()( SampleFrame& dst, const SampleFrame& src ) const { - dst[0] += src[0] * m_coeff; - dst[1] += src[1] * m_coeff; + dst += src * m_coeff; } const float m_coeff; } ; -void addMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames ) +void addMultiplied( SampleFrame* dst, const SampleFrame* src, float coeffSrc, int frames ) { run<>( dst, src, frames, AddMultipliedOp(coeffSrc) ); } @@ -169,7 +163,7 @@ struct AddSwappedMultipliedOp { AddSwappedMultipliedOp( float coeff ) : m_coeff( coeff ) { } - void operator()( sampleFrame& dst, const sampleFrame& src ) const + void operator()( SampleFrame& dst, const SampleFrame& src ) const { dst[0] += src[1] * m_coeff; dst[1] += src[0] * m_coeff; @@ -178,22 +172,21 @@ struct AddSwappedMultipliedOp const float m_coeff; }; -void multiply(sampleFrame* dst, float coeff, int frames) +void multiply(SampleFrame* dst, float coeff, int frames) { for (int i = 0; i < frames; ++i) { - dst[i][0] *= coeff; - dst[i][1] *= coeff; + dst[i] *= coeff; } } -void addSwappedMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames ) +void addSwappedMultiplied( SampleFrame* dst, const SampleFrame* src, float coeffSrc, int frames ) { run<>( dst, src, frames, AddSwappedMultipliedOp(coeffSrc) ); } -void addMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames ) +void addMultipliedByBuffer( SampleFrame* dst, const SampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames ) { for( int f = 0; f < frames; ++f ) { @@ -202,7 +195,7 @@ void addMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, float coef } } -void addMultipliedByBuffers( sampleFrame* dst, const sampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames ) +void addMultipliedByBuffers( SampleFrame* dst, const SampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames ) { for( int f = 0; f < frames; ++f ) { @@ -212,7 +205,7 @@ void addMultipliedByBuffers( sampleFrame* dst, const sampleFrame* src, ValueBuff } -void addSanitizedMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames ) +void addSanitizedMultipliedByBuffer( SampleFrame* dst, const SampleFrame* src, float coeffSrc, ValueBuffer * coeffSrcBuf, int frames ) { if ( !useNaNHandler() ) { @@ -228,7 +221,7 @@ void addSanitizedMultipliedByBuffer( sampleFrame* dst, const sampleFrame* src, f } } -void addSanitizedMultipliedByBuffers( sampleFrame* dst, const sampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames ) +void addSanitizedMultipliedByBuffers( SampleFrame* dst, const SampleFrame* src, ValueBuffer * coeffSrcBuf1, ValueBuffer * coeffSrcBuf2, int frames ) { if ( !useNaNHandler() ) { @@ -254,7 +247,7 @@ struct AddSanitizedMultipliedOp { AddSanitizedMultipliedOp( float coeff ) : m_coeff( coeff ) { } - void operator()( sampleFrame& dst, const sampleFrame& src ) const + void operator()( SampleFrame& dst, const SampleFrame& src ) const { dst[0] += ( std::isinf( src[0] ) || std::isnan( src[0] ) ) ? 0.0f : src[0] * m_coeff; dst[1] += ( std::isinf( src[1] ) || std::isnan( src[1] ) ) ? 0.0f : src[1] * m_coeff; @@ -263,7 +256,7 @@ struct AddSanitizedMultipliedOp const float m_coeff; }; -void addSanitizedMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffSrc, int frames ) +void addSanitizedMultiplied( SampleFrame* dst, const SampleFrame* src, float coeffSrc, int frames ) { if ( !useNaNHandler() ) { @@ -284,7 +277,7 @@ struct AddMultipliedStereoOp m_coeffs[1] = coeffRight; } - void operator()( sampleFrame& dst, const sampleFrame& src ) const + void operator()( SampleFrame& dst, const SampleFrame& src ) const { dst[0] += src[0] * m_coeffs[0]; dst[1] += src[1] * m_coeffs[1]; @@ -294,7 +287,7 @@ struct AddMultipliedStereoOp } ; -void addMultipliedStereo( sampleFrame* dst, const sampleFrame* src, float coeffSrcLeft, float coeffSrcRight, int frames ) +void addMultipliedStereo( SampleFrame* dst, const SampleFrame* src, float coeffSrcLeft, float coeffSrcRight, int frames ) { run<>( dst, src, frames, AddMultipliedStereoOp(coeffSrcLeft, coeffSrcRight) ); @@ -312,7 +305,7 @@ struct MultiplyAndAddMultipliedOp m_coeffs[1] = coeffSrc; } - void operator()( sampleFrame& dst, const sampleFrame& src ) const + void operator()( SampleFrame& dst, const SampleFrame& src ) const { dst[0] = dst[0]*m_coeffs[0] + src[0]*m_coeffs[1]; dst[1] = dst[1]*m_coeffs[0] + src[1]*m_coeffs[1]; @@ -322,14 +315,14 @@ struct MultiplyAndAddMultipliedOp } ; -void multiplyAndAddMultiplied( sampleFrame* dst, const sampleFrame* src, float coeffDst, float coeffSrc, int frames ) +void multiplyAndAddMultiplied( SampleFrame* dst, const SampleFrame* src, float coeffDst, float coeffSrc, int frames ) { run<>( dst, src, frames, MultiplyAndAddMultipliedOp(coeffDst, coeffSrc) ); } -void multiplyAndAddMultipliedJoined( sampleFrame* dst, +void multiplyAndAddMultipliedJoined( SampleFrame* dst, const sample_t* srcLeft, const sample_t* srcRight, float coeffDst, float coeffSrc, int frames ) diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 6dd2e34510b..7e3fc1f60c5 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -43,8 +43,8 @@ namespace lmms MixerRoute::MixerRoute( MixerChannel * from, MixerChannel * to, float amount ) : m_from( from ), m_to( to ), - m_amount( amount, 0, 1, 0.001, nullptr, - tr( "Amount to send from channel %1 to channel %2" ).arg( m_from->m_channelIndex ).arg( m_to->m_channelIndex ) ) + m_amount(amount, 0, 1, 0.001f, nullptr, + tr("Amount to send from channel %1 to channel %2").arg(m_from->m_channelIndex).arg(m_to->m_channelIndex)) { //qDebug( "created: %d to %d", m_from->m_channelIndex, m_to->m_channelIndex ); // create send amount model @@ -64,17 +64,17 @@ MixerChannel::MixerChannel( int idx, Model * _parent ) : m_stillRunning( false ), m_peakLeft( 0.0f ), m_peakRight( 0.0f ), - m_buffer( new sampleFrame[Engine::audioEngine()->framesPerPeriod()] ), + m_buffer( new SampleFrame[Engine::audioEngine()->framesPerPeriod()] ), m_muteModel( false, _parent ), m_soloModel( false, _parent ), - m_volumeModel( 1.0, 0.0, 2.0, 0.001, _parent ), + m_volumeModel(1.f, 0.f, 2.f, 0.001f, _parent), m_name(), m_lock(), m_channelIndex( idx ), m_queued( false ), m_dependenciesMet(0) { - BufferManager::clear( m_buffer, Engine::audioEngine()->framesPerPeriod() ); + zeroSampleFrames(m_buffer, Engine::audioEngine()->framesPerPeriod()); } @@ -99,7 +99,7 @@ inline void MixerChannel::processed() void MixerChannel::incrementDeps() { - int i = m_dependenciesMet++ + 1; + const auto i = m_dependenciesMet++ + 1; if( i >= m_receives.size() && ! m_queued ) { m_queued = true; @@ -134,7 +134,7 @@ void MixerChannel::doProcessing() ValueBuffer * volBuf = sender->m_volumeModel.valueBuffer(); // mix it's output with this one's output - sampleFrame * ch_buf = sender->m_buffer; + SampleFrame* ch_buf = sender->m_buffer; // use sample-exact mixing if sample-exact values are available if( ! volBuf && ! sendBuf ) // neither volume nor send has sample-exact data... @@ -171,9 +171,9 @@ void MixerChannel::doProcessing() m_stillRunning = m_fxChain.processAudioBuffer( m_buffer, fpp, m_hasInput ); - AudioEngine::StereoSample peakSamples = Engine::audioEngine()->getPeakValues(m_buffer, fpp); - m_peakLeft = std::max(m_peakLeft, peakSamples.left * v); - m_peakRight = std::max(m_peakRight, peakSamples.right * v); + SampleFrame peakSamples = getAbsPeakValues(m_buffer, fpp); + m_peakLeft = std::max(m_peakLeft, peakSamples[0] * v); + m_peakRight = std::max(m_peakRight, peakSamples[1] * v); } else { @@ -235,7 +235,7 @@ int Mixer::createChannel() void Mixer::activateSolo() { - for (int i = 1; i < m_mixerChannels.size(); ++i) + for (auto i = std::size_t{1}; i < m_mixerChannels.size(); ++i) { m_mixerChannels[i]->m_muteBeforeSolo = m_mixerChannels[i]->m_muteModel.value(); m_mixerChannels[i]->m_muteModel.setValue( true ); @@ -244,7 +244,7 @@ void Mixer::activateSolo() void Mixer::deactivateSolo() { - for (int i = 1; i < m_mixerChannels.size(); ++i) + for (auto i = std::size_t{1}; i < m_mixerChannels.size(); ++i) { m_mixerChannels[i]->m_muteModel.setValue( m_mixerChannels[i]->m_muteBeforeSolo ); } @@ -260,7 +260,7 @@ void Mixer::toggledSolo() m_mixerChannels[m_lastSoloed]->m_soloModel.setValue( false ); } //determine the soloed channel - for (int i = 0; i < m_mixerChannels.size(); ++i) + for (auto i = std::size_t{0}; i < m_mixerChannels.size(); ++i) { if (m_mixerChannels[i]->m_soloModel.value() == true) soloedChan = i; @@ -355,7 +355,7 @@ void Mixer::deleteChannel( int index ) m_mixerChannels.erase(m_mixerChannels.begin() + index); delete ch; - for( int i = index; i < m_mixerChannels.size(); ++i ) + for (auto i = static_cast(index); i < m_mixerChannels.size(); ++i) { validateChannelName( i, i + 1 ); @@ -381,7 +381,7 @@ void Mixer::deleteChannel( int index ) void Mixer::moveChannelLeft( int index ) { // can't move master or first channel - if( index <= 1 || index >= m_mixerChannels.size() ) + if (index <= 1 || static_cast(index) >= m_mixerChannels.size()) { return; } @@ -596,7 +596,7 @@ FloatModel * Mixer::channelSendModel( mix_ch_t fromChannel, mix_ch_t toChannel ) -void Mixer::mixToChannel( const sampleFrame * _buf, mix_ch_t _ch ) +void Mixer::mixToChannel( const SampleFrame* _buf, mix_ch_t _ch ) { if( m_mixerChannels[_ch]->m_muteModel.value() == false ) { @@ -612,13 +612,12 @@ void Mixer::mixToChannel( const sampleFrame * _buf, mix_ch_t _ch ) void Mixer::prepareMasterMix() { - BufferManager::clear( m_mixerChannels[0]->m_buffer, - Engine::audioEngine()->framesPerPeriod() ); + zeroSampleFrames(m_mixerChannels[0]->m_buffer, Engine::audioEngine()->framesPerPeriod()); } -void Mixer::masterMix( sampleFrame * _buf ) +void Mixer::masterMix( SampleFrame* _buf ) { const int fpp = Engine::audioEngine()->framesPerPeriod(); @@ -685,8 +684,7 @@ void Mixer::masterMix( sampleFrame * _buf ) // reset channel process state for( int i = 0; i < numChannels(); ++i) { - BufferManager::clear( m_mixerChannels[i]->m_buffer, - Engine::audioEngine()->framesPerPeriod() ); + zeroSampleFrames(m_mixerChannels[i]->m_buffer, Engine::audioEngine()->framesPerPeriod()); m_mixerChannels[i]->reset(); m_mixerChannels[i]->m_queued = false; // also reset hasInput @@ -746,7 +744,7 @@ void Mixer::clearChannel(mix_ch_t index) void Mixer::saveSettings( QDomDocument & _doc, QDomElement & _this ) { // save channels - for( int i = 0; i < m_mixerChannels.size(); ++i ) + for (auto i = std::size_t{0}; i < m_mixerChannels.size(); ++i) { MixerChannel * ch = m_mixerChannels[i]; @@ -757,7 +755,7 @@ void Mixer::saveSettings( QDomDocument & _doc, QDomElement & _this ) ch->m_volumeModel.saveSettings( _doc, mixch, "volume" ); ch->m_muteModel.saveSettings( _doc, mixch, "muted" ); ch->m_soloModel.saveSettings( _doc, mixch, "soloed" ); - mixch.setAttribute( "num", i ); + mixch.setAttribute("num", static_cast(i)); mixch.setAttribute( "name", ch->m_name ); if (const auto& color = ch->color()) { mixch.setAttribute("color", color->name()); } @@ -776,7 +774,8 @@ void Mixer::saveSettings( QDomDocument & _doc, QDomElement & _this ) // make sure we have at least num channels void Mixer::allocateChannelsTo(int num) { - while( num > m_mixerChannels.size() - 1 ) + if (num <= 0) { return; } + while (static_cast(num) > m_mixerChannels.size() - 1) { createChannel(); @@ -815,7 +814,7 @@ void Mixer::loadSettings( const QDomElement & _this ) // mixer sends QDomNodeList chData = mixch.childNodes(); - for( unsigned int i=0; imidiNoteOn( *this ); } - if(m_instrumentTrack->instrument() && m_instrumentTrack->instrument()->flags() & Instrument::Flag::IsSingleStreamed ) + if (m_instrumentTrack->instrument() && m_instrumentTrack->instrument()->isSingleStreamed()) { setUsesBuffer( false ); } @@ -183,7 +183,7 @@ int NotePlayHandle::midiKey() const -void NotePlayHandle::play( sampleFrame * _working_buffer ) +void NotePlayHandle::play( SampleFrame* _working_buffer ) { if (m_muted) { @@ -568,7 +568,7 @@ void NotePlayHandle::processTimePos(const TimePos& time, float pitchValue, bool else { const float v = detuning()->automationClip()->valueAt(time - songGlobalParentOffset() - pos()); - if (!typeInfo::isEqual(v, m_baseDetuning->value())) + if (!approximatelyEqual(v, m_baseDetuning->value())) { m_baseDetuning->setValue(v); updateFrequency(); diff --git a/src/core/Oscillator.cpp b/src/core/Oscillator.cpp index 0330fad584a..a875cf2d4f4 100644 --- a/src/core/Oscillator.cpp +++ b/src/core/Oscillator.cpp @@ -77,11 +77,11 @@ Oscillator::Oscillator(const IntModel *wave_shape_model, -void Oscillator::update(sampleFrame* ab, const fpp_t frames, const ch_cnt_t chnl, bool modulator) +void Oscillator::update(SampleFrame* ab, const fpp_t frames, const ch_cnt_t chnl, bool modulator) { - if (m_freq >= Engine::audioEngine()->processingSampleRate() / 2) + if (m_freq >= Engine::audioEngine()->outputSampleRate() / 2) { - BufferManager::clear(ab, frames); + zeroSampleFrames(ab, frames); return; } // If this oscillator is used to PM or PF modulate another oscillator, take a note. @@ -316,7 +316,7 @@ void Oscillator::generateWaveTables() -void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames, +void Oscillator::updateNoSub( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { switch( static_cast(m_waveShapeModel->value()) ) @@ -352,7 +352,7 @@ void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames, -void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames, +void Oscillator::updatePM( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { switch( static_cast(m_waveShapeModel->value()) ) @@ -388,7 +388,7 @@ void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames, -void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames, +void Oscillator::updateAM( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { switch( static_cast(m_waveShapeModel->value()) ) @@ -424,7 +424,7 @@ void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames, -void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames, +void Oscillator::updateMix( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { switch( static_cast(m_waveShapeModel->value()) ) @@ -460,7 +460,7 @@ void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames, -void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames, +void Oscillator::updateSync( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { switch( static_cast(m_waveShapeModel->value()) ) @@ -496,7 +496,7 @@ void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames, -void Oscillator::updateFM( sampleFrame * _ab, const fpp_t _frames, +void Oscillator::updateFM( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { switch( static_cast(m_waveShapeModel->value()) ) @@ -535,7 +535,7 @@ void Oscillator::updateFM( sampleFrame * _ab, const fpp_t _frames, // should be called every time phase-offset is changed... inline void Oscillator::recalcPhase() { - if( !typeInfo::isEqual( m_phaseOffset, m_ext_phaseOffset ) ) + if (!approximatelyEqual(m_phaseOffset, m_ext_phaseOffset)) { m_phase -= m_phaseOffset; m_phaseOffset = m_ext_phaseOffset; @@ -558,7 +558,7 @@ inline bool Oscillator::syncOk( float _osc_coeff ) -float Oscillator::syncInit( sampleFrame * _ab, const fpp_t _frames, +float Oscillator::syncInit( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { if( m_subOsc != nullptr ) @@ -574,7 +574,7 @@ float Oscillator::syncInit( sampleFrame * _ab, const fpp_t _frames, // if we have no sub-osc, we can't do any modulation... just get our samples template -void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames, +void Oscillator::updateNoSub( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { recalcPhase(); @@ -592,7 +592,7 @@ void Oscillator::updateNoSub( sampleFrame * _ab, const fpp_t _frames, // do pm by using sub-osc as modulator template -void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames, +void Oscillator::updatePM( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { m_subOsc->update( _ab, _frames, _chnl, true ); @@ -613,7 +613,7 @@ void Oscillator::updatePM( sampleFrame * _ab, const fpp_t _frames, // do am by using sub-osc as modulator template -void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames, +void Oscillator::updateAM( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { m_subOsc->update( _ab, _frames, _chnl, false ); @@ -632,7 +632,7 @@ void Oscillator::updateAM( sampleFrame * _ab, const fpp_t _frames, // do mix by using sub-osc as mix-sample template -void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames, +void Oscillator::updateMix( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { m_subOsc->update( _ab, _frames, _chnl, false ); @@ -652,7 +652,7 @@ void Oscillator::updateMix( sampleFrame * _ab, const fpp_t _frames, // sync with sub-osc (every time sub-osc starts new period, we also start new // period) template -void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames, +void Oscillator::updateSync( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { const float sub_osc_coeff = m_subOsc->syncInit( _ab, _frames, _chnl ); @@ -675,13 +675,13 @@ void Oscillator::updateSync( sampleFrame * _ab, const fpp_t _frames, // do fm by using sub-osc as modulator template -void Oscillator::updateFM( sampleFrame * _ab, const fpp_t _frames, +void Oscillator::updateFM( SampleFrame* _ab, const fpp_t _frames, const ch_cnt_t _chnl ) { m_subOsc->update( _ab, _frames, _chnl, true ); recalcPhase(); const float osc_coeff = m_freq * m_detuning_div_samplerate; - const float sampleRateCorrection = 44100.0f / Engine::audioEngine()->processingSampleRate(); + const float sampleRateCorrection = 44100.0f / Engine::audioEngine()->outputSampleRate(); for( fpp_t frame = 0; frame < _frames; ++frame ) { @@ -697,7 +697,7 @@ void Oscillator::updateFM( sampleFrame * _ab, const fpp_t _frames, template<> inline sample_t Oscillator::getSample(const float sample) { - const float current_freq = m_freq * m_detuning_div_samplerate * Engine::audioEngine()->processingSampleRate(); + const float current_freq = m_freq * m_detuning_div_samplerate * Engine::audioEngine()->outputSampleRate(); if (!m_useWaveTable || current_freq < OscillatorConstants::MAX_FREQ) { diff --git a/src/core/PeakController.cpp b/src/core/PeakController.cpp index cfcd3765c43..1c38cf4cb33 100644 --- a/src/core/PeakController.cpp +++ b/src/core/PeakController.cpp @@ -80,7 +80,7 @@ void PeakController::updateValueBuffer() { if( m_coeffNeedsUpdate ) { - const float ratio = 44100.0f / Engine::audioEngine()->processingSampleRate(); + const float ratio = 44100.0f / Engine::audioEngine()->outputSampleRate(); m_attackCoeff = 1.0f - powf( 2.0f, -0.3f * ( 1.0f - m_peakEffect->attackModel()->value() ) * ratio ); m_decayCoeff = 1.0f - powf( 2.0f, -0.3f * ( 1.0f - m_peakEffect->decayModel()->value() ) * ratio ); m_coeffNeedsUpdate = false; diff --git a/src/core/PlayHandle.cpp b/src/core/PlayHandle.cpp index 168121e0f58..134fcd31100 100644 --- a/src/core/PlayHandle.cpp +++ b/src/core/PlayHandle.cpp @@ -55,7 +55,7 @@ void PlayHandle::doProcessing() if( m_usesBuffer ) { m_bufferReleased = false; - BufferManager::clear(m_playHandleBuffer, Engine::audioEngine()->framesPerPeriod()); + zeroSampleFrames(m_playHandleBuffer, Engine::audioEngine()->framesPerPeriod()); play( buffer() ); } else @@ -70,9 +70,9 @@ void PlayHandle::releaseBuffer() m_bufferReleased = true; } -sampleFrame* PlayHandle::buffer() +SampleFrame* PlayHandle::buffer() { - return m_bufferReleased ? nullptr : reinterpret_cast(m_playHandleBuffer); + return m_bufferReleased ? nullptr : m_playHandleBuffer; }; } // namespace lmms \ No newline at end of file diff --git a/src/core/Plugin.cpp b/src/core/Plugin.cpp index 97391450187..f165ddf7509 100644 --- a/src/core/Plugin.cpp +++ b/src/core/Plugin.cpp @@ -226,8 +226,8 @@ Plugin * Plugin::instantiate(const QString& pluginName, Model * parent, } else { - InstantiationHook instantiationHook; - if ((instantiationHook = ( InstantiationHook ) pi.library->resolve( "lmms_plugin_main" ))) + auto instantiationHook = reinterpret_cast(pi.library->resolve("lmms_plugin_main")); + if (instantiationHook) { inst = instantiationHook(parent, data); if(!inst) { diff --git a/src/core/PluginIssue.cpp b/src/core/PluginIssue.cpp index c9cf3400faa..b40c4723ba4 100644 --- a/src/core/PluginIssue.cpp +++ b/src/core/PluginIssue.cpp @@ -1,7 +1,7 @@ /* - * PluginIssue.h - PluginIssue class + * PluginIssue.cpp - PluginIssue class implementation * - * Copyright (c) 2019 Johannes Lorenz + * Copyright (c) 2019-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -68,8 +68,8 @@ const char *PluginIssue::msgFor(const PluginIssueType &it) return "required feature not supported"; case PluginIssueType::BadPortType: return "unsupported port type"; - case PluginIssueType::Blacklisted: - return "blacklisted plugin"; + case PluginIssueType::Blocked: + return "blocked plugin"; case PluginIssueType::NoIssue: return nullptr; } diff --git a/src/core/PresetPreviewPlayHandle.cpp b/src/core/PresetPreviewPlayHandle.cpp index 0930de0de22..e7e67185ed2 100644 --- a/src/core/PresetPreviewPlayHandle.cpp +++ b/src/core/PresetPreviewPlayHandle.cpp @@ -175,7 +175,7 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, // create note-play-handle for it m_previewNote = NotePlayHandleManager::acquire( s_previewTC->previewInstrumentTrack(), 0, - typeInfo::max() / 2, + std::numeric_limits::max() / 2, Note( 0, 0, DefaultKey, 100 ) ); setAudioPort( s_previewTC->previewInstrumentTrack()->audioPort() ); @@ -206,7 +206,7 @@ PresetPreviewPlayHandle::~PresetPreviewPlayHandle() -void PresetPreviewPlayHandle::play( sampleFrame * _working_buffer ) +void PresetPreviewPlayHandle::play( SampleFrame* _working_buffer ) { // Do nothing; the preview instrument is played by m_previewNote, which // has been added to the audio engine diff --git a/src/core/ProjectJournal.cpp b/src/core/ProjectJournal.cpp index fc77c98e65d..ae17b2aa8ab 100644 --- a/src/core/ProjectJournal.cpp +++ b/src/core/ProjectJournal.cpp @@ -23,11 +23,13 @@ */ #include +#include #include "ProjectJournal.h" #include "Engine.h" #include "JournallingObject.h" #include "Song.h" +#include "AutomationClip.h" namespace lmms { @@ -67,6 +69,12 @@ void ProjectJournal::undo() jo->restoreState( c.data.content().firstChildElement() ); setJournalling( prev ); Engine::getSong()->setModified(); + + // loading AutomationClip connections correctly + if (!c.data.content().elementsByTagName("automationclip").isEmpty()) + { + AutomationClip::resolveAllIDs(); + } break; } } diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index 088bc3cd868..dc26bf2b554 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -325,7 +325,7 @@ bool RemotePlugin::init(const QString &pluginExecutable, -bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf ) +bool RemotePlugin::process( const SampleFrame* _in_buf, SampleFrame* _out_buf ) { const fpp_t frames = Engine::audioEngine()->framesPerPeriod(); @@ -333,7 +333,7 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf { if( _out_buf != nullptr ) { - BufferManager::clear( _out_buf, frames ); + zeroSampleFrames(_out_buf, frames); } return false; } @@ -352,7 +352,7 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf } if( _out_buf != nullptr ) { - BufferManager::clear( _out_buf, frames ); + zeroSampleFrames(_out_buf, frames); } return false; } @@ -376,11 +376,12 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf } else if( inputs == DEFAULT_CHANNELS ) { - memcpy( m_audioBuffer.get(), _in_buf, frames * BYTES_PER_FRAME ); + auto target = m_audioBuffer.get(); + copyFromSampleFrames(target, _in_buf, frames); } else { - auto o = (sampleFrame*)m_audioBuffer.get(); + auto o = (SampleFrame*)m_audioBuffer.get(); for( ch_cnt_t ch = 0; ch < inputs; ++ch ) { for( fpp_t frame = 0; frame < frames; ++frame ) @@ -418,14 +419,14 @@ bool RemotePlugin::process( const sampleFrame * _in_buf, sampleFrame * _out_buf } else if( outputs == DEFAULT_CHANNELS ) { - memcpy( _out_buf, m_audioBuffer.get() + m_inputCount * frames, - frames * BYTES_PER_FRAME ); + auto source = m_audioBuffer.get() + m_inputCount * frames; + copyToSampleFrames(_out_buf, source, frames); } else { - auto o = (sampleFrame*)(m_audioBuffer.get() + m_inputCount * frames); + auto o = (SampleFrame*)(m_audioBuffer.get() + m_inputCount * frames); // clear buffer, if plugin didn't fill up both channels - BufferManager::clear( _out_buf, frames ); + zeroSampleFrames(_out_buf, frames); for (ch_cnt_t ch = 0; ch < std::min(DEFAULT_CHANNELS, outputs); ++ch) @@ -535,7 +536,7 @@ bool RemotePlugin::processMessage( const message & _m ) case IdSampleRateInformation: reply = true; - reply_message.addInt( Engine::audioEngine()->processingSampleRate() ); + reply_message.addInt( Engine::audioEngine()->outputSampleRate() ); break; case IdBufferSizeInformation: diff --git a/src/core/RingBuffer.cpp b/src/core/RingBuffer.cpp index 3f1ee7236c1..652cd43dcf9 100644 --- a/src/core/RingBuffer.cpp +++ b/src/core/RingBuffer.cpp @@ -34,22 +34,22 @@ namespace lmms RingBuffer::RingBuffer( f_cnt_t size ) : m_fpp( Engine::audioEngine()->framesPerPeriod() ), - m_samplerate( Engine::audioEngine()->processingSampleRate() ), + m_samplerate( Engine::audioEngine()->outputSampleRate() ), m_size( size + m_fpp ) { - m_buffer = new sampleFrame[ m_size ]; - memset( m_buffer, 0, m_size * sizeof( sampleFrame ) ); + m_buffer = new SampleFrame[ m_size ]; + zeroSampleFrames(m_buffer, m_size); m_position = 0; } RingBuffer::RingBuffer( float size ) : m_fpp( Engine::audioEngine()->framesPerPeriod() ), - m_samplerate( Engine::audioEngine()->processingSampleRate() ) + m_samplerate( Engine::audioEngine()->outputSampleRate() ) { m_size = msToFrames( size ) + m_fpp; - m_buffer = new sampleFrame[ m_size ]; - memset( m_buffer, 0, m_size * sizeof( sampleFrame ) ); + m_buffer = new SampleFrame[ m_size ]; + zeroSampleFrames(m_buffer, m_size); m_position = 0; setSamplerateAware( true ); //qDebug( "m_size %d, m_position %d", m_size, m_position ); @@ -64,7 +64,7 @@ RingBuffer::~RingBuffer() void RingBuffer::reset() { - memset( m_buffer, 0, m_size * sizeof( sampleFrame ) ); + zeroSampleFrames(m_buffer, m_size); m_position = 0; } @@ -72,10 +72,10 @@ void RingBuffer::reset() void RingBuffer::changeSize( f_cnt_t size ) { size += m_fpp; - sampleFrame * tmp = m_buffer; + SampleFrame* tmp = m_buffer; m_size = size; - m_buffer = new sampleFrame[ m_size ]; - memset( m_buffer, 0, m_size * sizeof( sampleFrame ) ); + m_buffer = new SampleFrame[ m_size ]; + zeroSampleFrames(m_buffer, m_size); m_position = 0; delete[] tmp; } @@ -118,111 +118,109 @@ void RingBuffer::movePosition( float amount ) } -void RingBuffer::pop( sampleFrame * dst ) +void RingBuffer::pop( SampleFrame* dst ) { if( m_position + m_fpp <= m_size ) // we won't go over the edge so we can just memcpy here { - memcpy( dst, & m_buffer [ m_position ], m_fpp * sizeof( sampleFrame ) ); - memset( & m_buffer[m_position], 0, m_fpp * sizeof( sampleFrame ) ); + memcpy( dst, & m_buffer [ m_position ], m_fpp * sizeof( SampleFrame ) ); + zeroSampleFrames(&m_buffer[m_position], m_fpp); } else { f_cnt_t first = m_size - m_position; f_cnt_t second = m_fpp - first; - memcpy( dst, & m_buffer [ m_position ], first * sizeof( sampleFrame ) ); - memset( & m_buffer [m_position], 0, first * sizeof( sampleFrame ) ); + memcpy( dst, & m_buffer [ m_position ], first * sizeof( SampleFrame ) ); + zeroSampleFrames(&m_buffer[m_position], first); - memcpy( & dst [first], m_buffer, second * sizeof( sampleFrame ) ); - memset( m_buffer, 0, second * sizeof( sampleFrame ) ); + memcpy( & dst [first], m_buffer, second * sizeof( SampleFrame ) ); + zeroSampleFrames(m_buffer, second); } m_position = ( m_position + m_fpp ) % m_size; } -void RingBuffer::read( sampleFrame * dst, f_cnt_t offset ) +void RingBuffer::read( SampleFrame* dst, f_cnt_t offset ) { f_cnt_t pos = ( m_position + offset ) % m_size; - if( pos < 0 ) { pos += m_size; } if( pos + m_fpp <= m_size ) // we won't go over the edge so we can just memcpy here { - memcpy( dst, & m_buffer [pos], m_fpp * sizeof( sampleFrame ) ); + memcpy( dst, & m_buffer [pos], m_fpp * sizeof( SampleFrame ) ); } else { f_cnt_t first = m_size - pos; f_cnt_t second = m_fpp - first; - memcpy( dst, & m_buffer [pos], first * sizeof( sampleFrame ) ); + memcpy( dst, & m_buffer [pos], first * sizeof( SampleFrame ) ); - memcpy( & dst [first], m_buffer, second * sizeof( sampleFrame ) ); + memcpy( & dst [first], m_buffer, second * sizeof( SampleFrame ) ); } } -void RingBuffer::read( sampleFrame * dst, float offset ) +void RingBuffer::read( SampleFrame* dst, float offset ) { read( dst, msToFrames( offset ) ); } -void RingBuffer::read( sampleFrame * dst, f_cnt_t offset, f_cnt_t length ) +void RingBuffer::read( SampleFrame* dst, f_cnt_t offset, f_cnt_t length ) { f_cnt_t pos = ( m_position + offset ) % m_size; - if( pos < 0 ) { pos += m_size; } if( pos + length <= m_size ) // we won't go over the edge so we can just memcpy here { - memcpy( dst, & m_buffer [pos], length * sizeof( sampleFrame ) ); + memcpy( dst, & m_buffer [pos], length * sizeof( SampleFrame ) ); } else { f_cnt_t first = m_size - pos; f_cnt_t second = length - first; - memcpy( dst, & m_buffer [pos], first * sizeof( sampleFrame ) ); + memcpy( dst, & m_buffer [pos], first * sizeof( SampleFrame ) ); - memcpy( & dst [first], m_buffer, second * sizeof( sampleFrame ) ); + memcpy( & dst [first], m_buffer, second * sizeof( SampleFrame ) ); } } -void RingBuffer::read( sampleFrame * dst, float offset, f_cnt_t length ) +void RingBuffer::read( SampleFrame* dst, float offset, f_cnt_t length ) { read( dst, msToFrames( offset ), length ); } -void RingBuffer::write( sampleFrame * src, f_cnt_t offset, f_cnt_t length ) +void RingBuffer::write( SampleFrame* src, f_cnt_t offset, f_cnt_t length ) { const f_cnt_t pos = ( m_position + offset ) % m_size; if( length == 0 ) { length = m_fpp; } if( pos + length <= m_size ) // we won't go over the edge so we can just memcpy here { - memcpy( & m_buffer [pos], src, length * sizeof( sampleFrame ) ); + memcpy( & m_buffer [pos], src, length * sizeof( SampleFrame ) ); } else { f_cnt_t first = m_size - pos; f_cnt_t second = length - first; - memcpy( & m_buffer [pos], src, first * sizeof( sampleFrame ) ); + memcpy( & m_buffer [pos], src, first * sizeof( SampleFrame ) ); - memcpy( m_buffer, & src [first], second * sizeof( sampleFrame ) ); + memcpy( m_buffer, & src [first], second * sizeof( SampleFrame ) ); } } -void RingBuffer::write( sampleFrame * src, float offset, f_cnt_t length ) +void RingBuffer::write( SampleFrame* src, float offset, f_cnt_t length ) { write( src, msToFrames( offset ), length ); } -void RingBuffer::writeAdding( sampleFrame * src, f_cnt_t offset, f_cnt_t length ) +void RingBuffer::writeAdding( SampleFrame* src, f_cnt_t offset, f_cnt_t length ) { const f_cnt_t pos = ( m_position + offset ) % m_size; if( length == 0 ) { length = m_fpp; } @@ -243,13 +241,13 @@ void RingBuffer::writeAdding( sampleFrame * src, f_cnt_t offset, f_cnt_t length } -void RingBuffer::writeAdding( sampleFrame * src, float offset, f_cnt_t length ) +void RingBuffer::writeAdding( SampleFrame* src, float offset, f_cnt_t length ) { writeAdding( src, msToFrames( offset ), length ); } -void RingBuffer::writeAddingMultiplied( sampleFrame * src, f_cnt_t offset, f_cnt_t length, float level ) +void RingBuffer::writeAddingMultiplied( SampleFrame* src, f_cnt_t offset, f_cnt_t length, float level ) { const f_cnt_t pos = ( m_position + offset ) % m_size; //qDebug( "pos %d m_pos %d ofs %d siz %d", pos, m_position, offset, m_size ); @@ -271,14 +269,14 @@ void RingBuffer::writeAddingMultiplied( sampleFrame * src, f_cnt_t offset, f_cnt } -void RingBuffer::writeAddingMultiplied( sampleFrame * src, float offset, f_cnt_t length, float level ) +void RingBuffer::writeAddingMultiplied( SampleFrame* src, float offset, f_cnt_t length, float level ) { f_cnt_t ofs = msToFrames( offset ); writeAddingMultiplied( src, ofs, length, level ); } -void RingBuffer::writeSwappedAddingMultiplied( sampleFrame * src, f_cnt_t offset, f_cnt_t length, float level ) +void RingBuffer::writeSwappedAddingMultiplied( SampleFrame* src, f_cnt_t offset, f_cnt_t length, float level ) { const f_cnt_t pos = ( m_position + offset ) % m_size; if( length == 0 ) { length = m_fpp; } @@ -299,7 +297,7 @@ void RingBuffer::writeSwappedAddingMultiplied( sampleFrame * src, f_cnt_t offset } -void RingBuffer::writeSwappedAddingMultiplied( sampleFrame * src, float offset, f_cnt_t length, float level ) +void RingBuffer::writeSwappedAddingMultiplied( SampleFrame* src, float offset, f_cnt_t length, float level ) { writeSwappedAddingMultiplied( src, msToFrames( offset ), length, level ); } @@ -307,12 +305,12 @@ void RingBuffer::writeSwappedAddingMultiplied( sampleFrame * src, float offset, void RingBuffer::updateSamplerate() { - float newsize = static_cast( ( m_size - m_fpp ) * Engine::audioEngine()->processingSampleRate() ) / m_samplerate; + float newsize = static_cast( ( m_size - m_fpp ) * Engine::audioEngine()->outputSampleRate() ) / m_samplerate; m_size = static_cast( ceilf( newsize ) ) + m_fpp; - m_samplerate = Engine::audioEngine()->processingSampleRate(); + m_samplerate = Engine::audioEngine()->outputSampleRate(); delete[] m_buffer; - m_buffer = new sampleFrame[ m_size ]; - memset( m_buffer, 0, m_size * sizeof( sampleFrame ) ); + m_buffer = new SampleFrame[ m_size ]; + zeroSampleFrames(m_buffer, m_size); m_position = 0; } diff --git a/src/core/Sample.cpp b/src/core/Sample.cpp index a07b100bf35..3a1dbfcb256 100644 --- a/src/core/Sample.cpp +++ b/src/core/Sample.cpp @@ -24,6 +24,8 @@ #include "Sample.h" +#include "lmms_math.h" + #include namespace lmms { @@ -46,7 +48,7 @@ Sample::Sample(const QByteArray& base64, int sampleRate) { } -Sample::Sample(const sampleFrame* data, size_t numFrames, int sampleRate) +Sample::Sample(const SampleFrame* data, size_t numFrames, int sampleRate) : m_buffer(std::make_shared(data, numFrames, sampleRate)) , m_startFrame(0) , m_endFrame(m_buffer->size()) @@ -116,7 +118,7 @@ auto Sample::operator=(Sample&& other) -> Sample& return *this; } -bool Sample::play(sampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency, Loop loopMode) const +bool Sample::play(SampleFrame* dst, PlaybackState* state, size_t numFrames, float desiredFrequency, Loop loopMode) const { assert(numFrames > 0); assert(desiredFrequency > 0); @@ -124,26 +126,28 @@ bool Sample::play(sampleFrame* dst, PlaybackState* state, size_t numFrames, floa const auto pastBounds = state->m_frameIndex >= m_endFrame || (state->m_frameIndex < 0 && state->m_backwards); if (loopMode == Loop::Off && pastBounds) { return false; } - const auto outputSampleRate = Engine::audioEngine()->processingSampleRate() * m_frequency / desiredFrequency; + const auto outputSampleRate = Engine::audioEngine()->outputSampleRate() * m_frequency / desiredFrequency; const auto inputSampleRate = m_buffer->sampleRate(); const auto resampleRatio = outputSampleRate / inputSampleRate; const auto marginSize = s_interpolationMargins[state->resampler().interpolationMode()]; state->m_frameIndex = std::max(m_startFrame, state->m_frameIndex); - auto playBuffer = std::vector(numFrames / resampleRatio + marginSize); + auto playBuffer = std::vector(numFrames / resampleRatio + marginSize); playRaw(playBuffer.data(), playBuffer.size(), state, loopMode); + state->resampler().setRatio(resampleRatio); + const auto resampleResult = state->resampler().resample(&playBuffer[0][0], playBuffer.size(), &dst[0][0], numFrames, resampleRatio); advance(state, resampleResult.inputFramesUsed, loopMode); - const auto outputFrames = resampleResult.outputFramesGenerated; - if (outputFrames < numFrames) { std::fill_n(dst + outputFrames, numFrames - outputFrames, sampleFrame{}); } + const auto outputFrames = static_cast(resampleResult.outputFramesGenerated); + if (outputFrames < numFrames) { std::fill_n(dst + outputFrames, numFrames - outputFrames, SampleFrame{}); } - if (!typeInfo::isEqual(m_amplification, 1.0f)) + if (!approximatelyEqual(m_amplification, 1.0f)) { - for (int i = 0; i < numFrames; ++i) + for (auto i = std::size_t{0}; i < numFrames; ++i) { dst[i][0] *= m_amplification; dst[i][1] *= m_amplification; @@ -168,8 +172,10 @@ void Sample::setAllPointFrames(int startFrame, int endFrame, int loopStartFrame, setLoopEndFrame(loopEndFrame); } -void Sample::playRaw(sampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const +void Sample::playRaw(SampleFrame* dst, size_t numFrames, const PlaybackState* state, Loop loopMode) const { + if (m_buffer->size() < 1) { return; } + auto index = state->m_frameIndex; auto backwards = state->m_backwards; diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 6483dd522e4..fda3f2f663e 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -31,7 +31,7 @@ namespace lmms { -SampleBuffer::SampleBuffer(const sampleFrame* data, size_t numFrames, int sampleRate) +SampleBuffer::SampleBuffer(const SampleFrame* data, size_t numFrames, int sampleRate) : m_data(data, data + numFrames) , m_sampleRate(sampleRate) { @@ -60,11 +60,11 @@ SampleBuffer::SampleBuffer(const QString& base64, int sampleRate) { // TODO: Replace with non-Qt equivalent const auto bytes = QByteArray::fromBase64(base64.toUtf8()); - m_data.resize(bytes.size() / sizeof(sampleFrame)); - std::memcpy(reinterpret_cast(m_data.data()), bytes, m_data.size() * sizeof(sampleFrame)); + m_data.resize(bytes.size() / sizeof(SampleFrame)); + std::memcpy(reinterpret_cast(m_data.data()), bytes, m_data.size() * sizeof(SampleFrame)); } -SampleBuffer::SampleBuffer(std::vector data, int sampleRate) +SampleBuffer::SampleBuffer(std::vector data, int sampleRate) : m_data(std::move(data)) , m_sampleRate(sampleRate) { @@ -82,7 +82,7 @@ QString SampleBuffer::toBase64() const { // TODO: Replace with non-Qt equivalent const auto data = reinterpret_cast(m_data.data()); - const auto size = static_cast(m_data.size() * sizeof(sampleFrame)); + const auto size = static_cast(m_data.size() * sizeof(SampleFrame)); const auto byteArray = QByteArray{data, size}; return byteArray.toBase64(); } diff --git a/src/core/SampleClip.cpp b/src/core/SampleClip.cpp index 42d4f644168..5ef001e20d1 100644 --- a/src/core/SampleClip.cpp +++ b/src/core/SampleClip.cpp @@ -112,6 +112,11 @@ void SampleClip::changeLength( const TimePos & _length ) Clip::changeLength(std::max(static_cast(_length), 1)); } +void SampleClip::changeLengthToSampleLength() +{ + int length = m_sample.sampleSize() / Engine::framesPerTick(); + changeLength(length); +} @@ -120,6 +125,11 @@ const QString& SampleClip::sampleFile() const return m_sample.sampleFile(); } +bool SampleClip::hasSampleFileLoaded(const QString & filename) const +{ + return m_sample.sampleFile() == filename; +} + void SampleClip::setSampleBuffer(std::shared_ptr sb) { { @@ -129,6 +139,8 @@ void SampleClip::setSampleBuffer(std::shared_ptr sb) updateLength(); emit sampleChanged(); + + Engine::getSong()->setModified(); } void SampleClip::setSampleFile(const QString& sf) @@ -210,6 +222,8 @@ void SampleClip::setIsPlaying(bool isPlaying) void SampleClip::updateLength() { emit sampleChanged(); + + Engine::getSong()->setModified(); } @@ -293,7 +307,7 @@ void SampleClip::loadSettings( const QDomElement & _this ) if( sampleFile().isEmpty() && _this.hasAttribute( "data" ) ) { auto sampleRate = _this.hasAttribute("sample_rate") ? _this.attribute("sample_rate").toInt() : - Engine::audioEngine()->processingSampleRate(); + Engine::audioEngine()->outputSampleRate(); auto buffer = gui::SampleLoader::createBufferFromBase64(_this.attribute("data"), sampleRate); m_sample = Sample(std::move(buffer)); diff --git a/src/core/SampleDecoder.cpp b/src/core/SampleDecoder.cpp index 94f8e387b07..d3ee091f42a 100644 --- a/src/core/SampleDecoder.cpp +++ b/src/core/SampleDecoder.cpp @@ -75,7 +75,7 @@ auto decodeSampleSF(const QString& audioFile) -> std::optional(sfInfo.frames); + auto result = std::vector(sfInfo.frames); for (int i = 0; i < static_cast(result.size()); ++i) { if (sfInfo.channels == 1) @@ -101,13 +101,13 @@ auto decodeSampleDS(const QString& audioFile) -> std::optionalprocessingSampleRate(); + const auto engineRate = Engine::audioEngine()->outputSampleRate(); const auto frames = ds.GetDSFileSamples(audioFile, dataPtr, DEFAULT_CHANNELS, engineRate); const auto data = std::unique_ptr{dataPtr}; // NOLINT, we have to use a C-style array here if (frames <= 0 || !data) { return std::nullopt; } - auto result = std::vector(frames); + auto result = std::vector(frames); src_short_to_float_array(data.get(), &result[0][0], frames * DEFAULT_CHANNELS); return SampleDecoder::Result{std::move(result), static_cast(engineRate)}; @@ -173,8 +173,8 @@ auto decodeSampleOggVorbis(const QString& audioFile) -> std::optional(totalSamplesRead / numChannels); - for (int i = 0; i < result.size(); ++i) + auto result = std::vector(totalSamplesRead / numChannels); + for (auto i = std::size_t{0}; i < result.size(); ++i) { if (numChannels == 1) { result[i] = {buffer[i], buffer[i]}; } else if (numChannels > 1) { result[i] = {buffer[i * numChannels], buffer[i * numChannels + 1]}; } diff --git a/src/core/SamplePlayHandle.cpp b/src/core/SamplePlayHandle.cpp index 61ded132aca..f2ddc2a4a04 100644 --- a/src/core/SamplePlayHandle.cpp +++ b/src/core/SamplePlayHandle.cpp @@ -85,23 +85,23 @@ SamplePlayHandle::~SamplePlayHandle() -void SamplePlayHandle::play( sampleFrame * buffer ) +void SamplePlayHandle::play( SampleFrame* buffer ) { const fpp_t fpp = Engine::audioEngine()->framesPerPeriod(); //play( 0, _try_parallelizing ); if( framesDone() >= totalFrames() ) { - memset( buffer, 0, sizeof( sampleFrame ) * fpp ); + zeroSampleFrames(buffer, fpp); return; } - sampleFrame * workingBuffer = buffer; + SampleFrame* workingBuffer = buffer; f_cnt_t frames = fpp; // apply offset for the first period if( framesDone() == 0 ) { - memset( buffer, 0, sizeof( sampleFrame ) * offset() ); + zeroSampleFrames(buffer, offset()); workingBuffer += offset(); frames -= offset(); } @@ -116,7 +116,7 @@ void SamplePlayHandle::play( sampleFrame * buffer ) // it is used only for previews, SampleTracks and the metronome. if (!m_sample->play(workingBuffer, &m_state, frames, DefaultBaseFreq)) { - memset(workingBuffer, 0, frames * sizeof(sampleFrame)); + zeroSampleFrames(workingBuffer, frames); } } @@ -145,7 +145,7 @@ bool SamplePlayHandle::isFromTrack( const Track * _track ) const f_cnt_t SamplePlayHandle::totalFrames() const { return (m_sample->endFrame() - m_sample->startFrame()) * - (static_cast(Engine::audioEngine()->processingSampleRate()) / m_sample->sampleRate()); + (static_cast(Engine::audioEngine()->outputSampleRate()) / m_sample->sampleRate()); } diff --git a/src/core/SampleRecordHandle.cpp b/src/core/SampleRecordHandle.cpp index 6857efa83d5..f7003f3beff 100644 --- a/src/core/SampleRecordHandle.cpp +++ b/src/core/SampleRecordHandle.cpp @@ -64,9 +64,9 @@ SampleRecordHandle::~SampleRecordHandle() -void SampleRecordHandle::play( sampleFrame * /*_working_buffer*/ ) +void SampleRecordHandle::play( SampleFrame* /*_working_buffer*/ ) { - const sampleFrame * recbuf = Engine::audioEngine()->inputBuffer(); + const SampleFrame* recbuf = Engine::audioEngine()->inputBuffer(); const f_cnt_t frames = Engine::audioEngine()->inputBufferFrames(); writeBuffer( recbuf, frames ); m_framesRecorded += frames; @@ -110,7 +110,7 @@ std::shared_ptr SampleRecordHandle::createSampleBuffer() { const f_cnt_t frames = framesRecorded(); // create buffer to store all recorded buffers in - auto bigBuffer = std::vector(frames); + auto bigBuffer = std::vector(frames); // now copy all buffers into big buffer auto framesCopied = 0; @@ -127,9 +127,9 @@ std::shared_ptr SampleRecordHandle::createSampleBuffer() -void SampleRecordHandle::writeBuffer( const sampleFrame * _ab, const f_cnt_t _frames ) +void SampleRecordHandle::writeBuffer( const SampleFrame* _ab, const f_cnt_t _frames ) { - auto buf = new sampleFrame[_frames]; + auto buf = new SampleFrame[_frames]; for( f_cnt_t frame = 0; frame < _frames; ++frame ) { for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) diff --git a/src/core/Song.cpp b/src/core/Song.cpp index ddc55707ca2..92cb2ba3051 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -562,6 +562,8 @@ void Song::playMidiClip( const MidiClip* midiClipToPlay, bool loop ) void Song::updateLength() { + if (m_loadingProject) { return; } + m_length = 0; m_tracksMutex.lockForRead(); for (auto track : tracks()) @@ -845,7 +847,7 @@ void Song::clearProject() stop(); } - for( int i = 0; i < PlayModeCount; i++ ) + for (auto i = std::size_t{0}; i < PlayModeCount; i++) { setPlayPos( 0, ( PlayMode )i ); } @@ -945,13 +947,12 @@ void Song::createNewProject() m_oldFileName = ""; setProjectFileName(""); - Track * t; - t = Track::create( Track::Type::Instrument, this ); - dynamic_cast( t )->loadInstrument( - "tripleoscillator" ); - t = Track::create(Track::Type::Instrument, Engine::patternStore()); - dynamic_cast( t )->loadInstrument( - "kicker" ); + auto tripleOscTrack = Track::create(Track::Type::Instrument, this); + dynamic_cast(tripleOscTrack)->loadInstrument("tripleoscillator"); + + auto kickerTrack = Track::create(Track::Type::Instrument, Engine::patternStore()); + dynamic_cast(kickerTrack)->loadInstrument("kicker"); + Track::create( Track::Type::Sample, this ); Track::create( Track::Type::Pattern, this ); Track::create( Track::Type::Automation, this ); @@ -964,7 +965,7 @@ void Song::createNewProject() QCoreApplication::instance()->processEvents(); m_loadingProject = false; - + updateLength(); Engine::patternStore()->updateAfterTrackAdd(); Engine::projectJournal()->setJournalling( true ); @@ -1207,6 +1208,7 @@ void Song::loadProject( const QString & fileName ) } m_loadingProject = false; + updateLength(); setModified(false); m_loadOnLaunch = false; } @@ -1347,7 +1349,7 @@ void Song::restoreScaleStates(const QDomElement &element) { QDomNode node = element.firstChild(); - for (int i = 0; i < MaxScaleCount && !node.isNull() && !isCancelled(); i++) + for (auto i = std::size_t{0}; i < MaxScaleCount && !node.isNull() && !isCancelled(); i++) { m_scales[i]->restoreState(node.toElement()); node = node.nextSibling(); @@ -1372,7 +1374,7 @@ void Song::restoreKeymapStates(const QDomElement &element) { QDomNode node = element.firstChild(); - for (int i = 0; i < MaxKeymapCount && !node.isNull() && !isCancelled(); i++) + for (auto i = std::size_t{0}; i < MaxKeymapCount && !node.isNull() && !isCancelled(); i++) { m_keymaps[i]->restoreState(node.toElement()); node = node.nextSibling(); diff --git a/src/core/ThreadPool.cpp b/src/core/ThreadPool.cpp new file mode 100644 index 00000000000..2e5f00df00b --- /dev/null +++ b/src/core/ThreadPool.cpp @@ -0,0 +1,85 @@ +/* + * ThreadPool.cpp + * + * Copyright (c) 2024 saker + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ +#include "ThreadPool.h" + +#include +#include +#include + +namespace lmms { +ThreadPool::ThreadPool(size_t numWorkers) +{ + assert(numWorkers > 0); + + m_workers.reserve(numWorkers); + for (size_t i = 0; i < numWorkers; ++i) + { + m_workers.emplace_back([this] { run(); }); + } +} + +ThreadPool::~ThreadPool() +{ + { + const auto lock = std::unique_lock{m_runMutex}; + m_done = true; + } + + m_runCond.notify_all(); + + for (auto& worker : m_workers) + { + if (worker.joinable()) { worker.join(); } + } +} + +auto ThreadPool::numWorkers() const -> size_t +{ + return m_workers.size(); +} + +void ThreadPool::run() +{ + while (!m_done) + { + std::function task; + { + auto lock = std::unique_lock{m_runMutex}; + m_runCond.wait(lock, [this] { return !m_queue.empty() || m_done; }); + + if (m_done) { break; } + task = m_queue.front(); + m_queue.pop(); + } + task(); + } +} + +auto ThreadPool::instance() -> ThreadPool& +{ + static auto s_pool = ThreadPool{s_numWorkers}; + return s_pool; +} + +} // namespace lmms diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 6c4ba465e4b..7ff73e71891 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -388,14 +388,14 @@ int Track::numOfClips() * \todo if we create a Clip here, should we somehow attach it to the * track? */ -Clip * Track::getClip( int clipNum ) +auto Track::getClip(std::size_t clipNum) -> Clip* { if( clipNum < m_clips.size() ) { return m_clips[clipNum]; } - printf( "called Track::getClip( %d ), " - "but Clip %d doesn't exist\n", clipNum, clipNum ); + printf( "called Track::getClip( %zu ), " + "but Clip %zu doesn't exist\n", clipNum, clipNum ); return createClip( clipNum * TimePos::ticksPerBar() ); } diff --git a/src/core/VstSyncController.cpp b/src/core/VstSyncController.cpp index c4b59eb6fb6..79344a5b5e2 100644 --- a/src/core/VstSyncController.cpp +++ b/src/core/VstSyncController.cpp @@ -155,7 +155,7 @@ void VstSyncController::updateSampleRate() { if (!m_syncData) { return; } - m_syncData->m_sampleRate = Engine::audioEngine()->processingSampleRate(); + m_syncData->m_sampleRate = Engine::audioEngine()->outputSampleRate(); #ifdef VST_SNC_LATENCY m_syncData->m_latency = m_syncData->m_bufferSize * m_syncData->m_bpm / ( (float) m_syncData->m_sampleRate * 60 ); diff --git a/src/core/audio/AudioAlsa.cpp b/src/core/audio/AudioAlsa.cpp index 201a967a381..c41703e2271 100644 --- a/src/core/audio/AudioAlsa.cpp +++ b/src/core/audio/AudioAlsa.cpp @@ -39,7 +39,7 @@ AudioAlsa::AudioAlsa( bool & _success_ful, AudioEngine* _audioEngine ) : AudioDevice(std::clamp( ConfigManager::inst()->value("audioalsa", "channels").toInt(), DEFAULT_CHANNELS, - SURROUND_CHANNELS), _audioEngine), + DEFAULT_CHANNELS), _audioEngine), m_handle( nullptr ), m_hwParams( nullptr ), m_swParams( nullptr ), @@ -53,12 +53,7 @@ AudioAlsa::AudioAlsa( bool & _success_ful, AudioEngine* _audioEngine ) : "Could not avoid possible interception by PulseAudio\n" ); } - int err; - - if( ( err = snd_pcm_open( &m_handle, - probeDevice().toLatin1().constData(), - SND_PCM_STREAM_PLAYBACK, - 0 ) ) < 0 ) + if (int err = snd_pcm_open(&m_handle, probeDevice().toLatin1().constData(), SND_PCM_STREAM_PLAYBACK, 0); err < 0) { printf( "Playback open error: %s\n", snd_strerror( err ) ); return; @@ -67,14 +62,13 @@ AudioAlsa::AudioAlsa( bool & _success_ful, AudioEngine* _audioEngine ) : snd_pcm_hw_params_malloc( &m_hwParams ); snd_pcm_sw_params_malloc( &m_swParams ); - if( ( err = setHWParams( channels(), - SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 ) + if (int err = setHWParams(channels(), SND_PCM_ACCESS_RW_INTERLEAVED); err < 0) { printf( "Setting of hwparams failed: %s\n", snd_strerror( err ) ); return; } - if( ( err = setSWParams() ) < 0 ) + if (int err = setSWParams(); err < 0) { printf( "Setting of swparams failed: %s\n", snd_strerror( err ) ); @@ -83,9 +77,8 @@ AudioAlsa::AudioAlsa( bool & _success_ful, AudioEngine* _audioEngine ) : // set FD_CLOEXEC flag for all file descriptors so forked processes // do not inherit them - struct pollfd * ufds; int count = snd_pcm_poll_descriptors_count( m_handle ); - ufds = new pollfd[count]; + auto ufds = new pollfd[count]; snd_pcm_poll_descriptors( m_handle, ufds, count ); for (int i = 0; i < std::max(3, count); ++i) { @@ -160,7 +153,7 @@ AudioAlsa::DeviceInfoCollection AudioAlsa::getAvailableDevices() { DeviceInfoCollection deviceInfos; - char **hints; + char** hints = nullptr; /* Enumerate sound devices */ int err = snd_device_name_hint(-1, "pcm", (void***)&hints); @@ -247,55 +240,9 @@ void AudioAlsa::stopProcessing() stopProcessingThread( this ); } - - - -void AudioAlsa::applyQualitySettings() -{ - if( hqAudio() ) - { - setSampleRate( Engine::audioEngine()->processingSampleRate() ); - - if( m_handle != nullptr ) - { - snd_pcm_close( m_handle ); - } - - int err; - if( ( err = snd_pcm_open( &m_handle, - probeDevice().toLatin1().constData(), - SND_PCM_STREAM_PLAYBACK, - 0 ) ) < 0 ) - { - printf( "Playback open error: %s\n", - snd_strerror( err ) ); - return; - } - - if( ( err = setHWParams( channels(), - SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 ) - { - printf( "Setting of hwparams failed: %s\n", - snd_strerror( err ) ); - return; - } - if( ( err = setSWParams() ) < 0 ) - { - printf( "Setting of swparams failed: %s\n", - snd_strerror( err ) ); - return; - } - } - - AudioDevice::applyQualitySettings(); -} - - - - void AudioAlsa::run() { - auto temp = new surroundSampleFrame[audioEngine()->framesPerPeriod()]; + auto temp = new SampleFrame[audioEngine()->framesPerPeriod()]; auto outbuf = new int_sample_t[audioEngine()->framesPerPeriod() * channels()]; auto pcmbuf = new int_sample_t[m_periodSize * channels()]; @@ -370,10 +317,8 @@ void AudioAlsa::run() int AudioAlsa::setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access ) { - int err, dir; - // choose all parameters - if( ( err = snd_pcm_hw_params_any( m_handle, m_hwParams ) ) < 0 ) + if (int err = snd_pcm_hw_params_any(m_handle, m_hwParams); err < 0) { printf( "Broken configuration for playback: no configurations " "available: %s\n", snd_strerror( err ) ); @@ -381,8 +326,7 @@ int AudioAlsa::setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access ) } // set the interleaved read/write format - if( ( err = snd_pcm_hw_params_set_access( m_handle, m_hwParams, - _access ) ) < 0 ) + if (int err = snd_pcm_hw_params_set_access(m_handle, m_hwParams, _access); err < 0) { printf( "Access type not available for playback: %s\n", snd_strerror( err ) ); @@ -390,11 +334,9 @@ int AudioAlsa::setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access ) } // set the sample format - if( ( snd_pcm_hw_params_set_format( m_handle, m_hwParams, - SND_PCM_FORMAT_S16_LE ) ) < 0 ) + if (int err = snd_pcm_hw_params_set_format(m_handle, m_hwParams, SND_PCM_FORMAT_S16_LE); err < 0) { - if( ( snd_pcm_hw_params_set_format( m_handle, m_hwParams, - SND_PCM_FORMAT_S16_BE ) ) < 0 ) + if (int err = snd_pcm_hw_params_set_format(m_handle, m_hwParams, SND_PCM_FORMAT_S16_BE); err < 0) { printf( "Neither little- nor big-endian available for " "playback: %s\n", snd_strerror( err ) ); @@ -408,8 +350,7 @@ int AudioAlsa::setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access ) } // set the count of channels - if( ( err = snd_pcm_hw_params_set_channels( m_handle, m_hwParams, - _channels ) ) < 0 ) + if (int err = snd_pcm_hw_params_set_channels(m_handle, m_hwParams, _channels); err < 0) { printf( "Channel count (%i) not available for playbacks: %s\n" "(Does your soundcard not support surround?)\n", @@ -418,11 +359,9 @@ int AudioAlsa::setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access ) } // set the sample rate - if( ( err = snd_pcm_hw_params_set_rate( m_handle, m_hwParams, - sampleRate(), 0 ) ) < 0 ) + if (int err = snd_pcm_hw_params_set_rate(m_handle, m_hwParams, sampleRate(), 0); err < 0) { - if( ( err = snd_pcm_hw_params_set_rate( m_handle, m_hwParams, - audioEngine()->baseSampleRate(), 0 ) ) < 0 ) + if (int err = snd_pcm_hw_params_set_rate(m_handle, m_hwParams, audioEngine()->baseSampleRate(), 0); err < 0) { printf( "Could not set sample rate: %s\n", snd_strerror( err ) ); @@ -432,36 +371,29 @@ int AudioAlsa::setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access ) m_periodSize = audioEngine()->framesPerPeriod(); m_bufferSize = m_periodSize * 8; - dir = 0; - err = snd_pcm_hw_params_set_period_size_near( m_handle, m_hwParams, - &m_periodSize, &dir ); - if( err < 0 ) + int dir; + if (int err = snd_pcm_hw_params_set_period_size_near(m_handle, m_hwParams, &m_periodSize, &dir); err < 0) { printf( "Unable to set period size %lu for playback: %s\n", m_periodSize, snd_strerror( err ) ); return err; } dir = 0; - err = snd_pcm_hw_params_get_period_size( m_hwParams, &m_periodSize, - &dir ); - if( err < 0 ) + if (int err = snd_pcm_hw_params_get_period_size(m_hwParams, &m_periodSize, &dir); err < 0) { printf( "Unable to get period size for playback: %s\n", snd_strerror( err ) ); } dir = 0; - err = snd_pcm_hw_params_set_buffer_size_near( m_handle, m_hwParams, - &m_bufferSize ); - if( err < 0 ) + if (int err = snd_pcm_hw_params_set_buffer_size_near(m_handle, m_hwParams, &m_bufferSize); err < 0) { printf( "Unable to set buffer size %lu for playback: %s\n", m_bufferSize, snd_strerror( err ) ); return ( err ); } - err = snd_pcm_hw_params_get_buffer_size( m_hwParams, &m_bufferSize ); - if( 2 * m_periodSize > m_bufferSize ) + if (int err = snd_pcm_hw_params_get_buffer_size(m_hwParams, &m_bufferSize); 2 * m_periodSize > m_bufferSize) { printf( "buffer to small, could not use\n" ); return ( err ); @@ -469,8 +401,7 @@ int AudioAlsa::setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access ) // write the parameters to device - err = snd_pcm_hw_params( m_handle, m_hwParams ); - if( err < 0 ) + if (int err = snd_pcm_hw_params(m_handle, m_hwParams); err < 0) { printf( "Unable to set hw params for playback: %s\n", snd_strerror( err ) ); @@ -485,10 +416,8 @@ int AudioAlsa::setHWParams( const ch_cnt_t _channels, snd_pcm_access_t _access ) int AudioAlsa::setSWParams() { - int err; - // get the current swparams - if( ( err = snd_pcm_sw_params_current( m_handle, m_swParams ) ) < 0 ) + if (int err = snd_pcm_sw_params_current(m_handle, m_swParams); err < 0) { printf( "Unable to determine current swparams for playback: %s" "\n", snd_strerror( err ) ); @@ -496,8 +425,7 @@ int AudioAlsa::setSWParams() } // start the transfer when a period is full - if( ( err = snd_pcm_sw_params_set_start_threshold( m_handle, - m_swParams, m_periodSize ) ) < 0 ) + if (int err = snd_pcm_sw_params_set_start_threshold(m_handle, m_swParams, m_periodSize); err < 0) { printf( "Unable to set start threshold mode for playback: %s\n", snd_strerror( err ) ); @@ -506,8 +434,7 @@ int AudioAlsa::setSWParams() // allow the transfer when at least m_periodSize samples can be // processed - if( ( err = snd_pcm_sw_params_set_avail_min( m_handle, m_swParams, - m_periodSize ) ) < 0 ) + if (int err = snd_pcm_sw_params_set_avail_min(m_handle, m_swParams, m_periodSize); err < 0) { printf( "Unable to set avail min for playback: %s\n", snd_strerror( err ) ); @@ -527,7 +454,7 @@ int AudioAlsa::setSWParams() #endif // write the parameters to the playback device - if( ( err = snd_pcm_sw_params( m_handle, m_swParams ) ) < 0 ) + if (int err = snd_pcm_sw_params(m_handle, m_swParams); err < 0) { printf( "Unable to set sw params for playback: %s\n", snd_strerror( err ) ); diff --git a/src/core/audio/AudioDevice.cpp b/src/core/audio/AudioDevice.cpp index 58ba3932e5b..2047fffe98b 100644 --- a/src/core/audio/AudioDevice.cpp +++ b/src/core/audio/AudioDevice.cpp @@ -34,18 +34,11 @@ namespace lmms AudioDevice::AudioDevice( const ch_cnt_t _channels, AudioEngine* _audioEngine ) : m_supportsCapture( false ), - m_sampleRate( _audioEngine->processingSampleRate() ), + m_sampleRate( _audioEngine->outputSampleRate() ), m_channels( _channels ), m_audioEngine( _audioEngine ), - m_buffer( new surroundSampleFrame[audioEngine()->framesPerPeriod()] ) + m_buffer(new SampleFrame[audioEngine()->framesPerPeriod()]) { - int error; - if( ( m_srcState = src_new( - audioEngine()->currentQualitySettings().libsrcInterpolation(), - SURROUND_CHANNELS, &error ) ) == nullptr ) - { - printf( "Error: src_new() failed in audio_device.cpp!\n" ); - } } @@ -53,9 +46,7 @@ AudioDevice::AudioDevice( const ch_cnt_t _channels, AudioEngine* _audioEngine ) AudioDevice::~AudioDevice() { - src_delete( m_srcState ); delete[] m_buffer; - m_devMutex.tryLock(); unlock(); } @@ -73,39 +64,16 @@ void AudioDevice::processNextBuffer() } } - - - -fpp_t AudioDevice::getNextBuffer( surroundSampleFrame * _ab ) +fpp_t AudioDevice::getNextBuffer(SampleFrame* _ab) { fpp_t frames = audioEngine()->framesPerPeriod(); - const surroundSampleFrame * b = audioEngine()->nextBuffer(); - if( !b ) - { - return 0; - } - - // make sure, no other thread is accessing device - lock(); - - // resample if necessary - if( audioEngine()->processingSampleRate() != m_sampleRate ) - { - frames = resample( b, frames, _ab, audioEngine()->processingSampleRate(), m_sampleRate ); - } - else - { - memcpy( _ab, b, frames * sizeof( surroundSampleFrame ) ); - } + const SampleFrame* b = audioEngine()->nextBuffer(); - // release lock - unlock(); + if (!b) { return 0; } - if( audioEngine()->hasFifoWriter() ) - { - delete[] b; - } + memcpy(_ab, b, frames * sizeof(SampleFrame)); + if (audioEngine()->hasFifoWriter()) { delete[] b; } return frames; } @@ -141,23 +109,6 @@ void AudioDevice::stopProcessingThread( QThread * thread ) - -void AudioDevice::applyQualitySettings() -{ - src_delete( m_srcState ); - - int error; - if( ( m_srcState = src_new( - audioEngine()->currentQualitySettings().libsrcInterpolation(), - SURROUND_CHANNELS, &error ) ) == nullptr ) - { - printf( "Error: src_new() failed in audio_device.cpp!\n" ); - } -} - - - - void AudioDevice::registerPort( AudioPort * ) { } @@ -176,49 +127,18 @@ void AudioDevice::renamePort( AudioPort * ) { } - - - -fpp_t AudioDevice::resample( const surroundSampleFrame * _src, - const fpp_t _frames, - surroundSampleFrame * _dst, - const sample_rate_t _src_sr, - const sample_rate_t _dst_sr ) -{ - if( m_srcState == nullptr ) - { - return _frames; - } - m_srcData.input_frames = _frames; - m_srcData.output_frames = _frames; - m_srcData.data_in = const_cast(_src[0].data()); - m_srcData.data_out = _dst[0].data (); - m_srcData.src_ratio = (double) _dst_sr / _src_sr; - m_srcData.end_of_input = 0; - int error; - if( ( error = src_process( m_srcState, &m_srcData ) ) ) - { - printf( "AudioDevice::resample(): error while resampling: %s\n", - src_strerror( error ) ); - } - return static_cast(m_srcData.output_frames_gen); -} - - - -int AudioDevice::convertToS16( const surroundSampleFrame * _ab, +int AudioDevice::convertToS16(const SampleFrame* _ab, const fpp_t _frames, int_sample_t * _output_buffer, const bool _convert_endian ) { if( _convert_endian ) { - int_sample_t temp; for( fpp_t frame = 0; frame < _frames; ++frame ) { for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl ) { - temp = static_cast(AudioEngine::clip(_ab[frame][chnl]) * OUTPUT_SAMPLE_MULTIPLIER); + auto temp = static_cast(AudioEngine::clip(_ab[frame][chnl]) * OUTPUT_SAMPLE_MULTIPLIER); ( _output_buffer + frame * channels() )[chnl] = ( temp & 0x00ff ) << 8 | @@ -252,13 +172,4 @@ void AudioDevice::clearS16Buffer( int_sample_t * _outbuf, const fpp_t _frames ) memset( _outbuf, 0, _frames * channels() * BYTES_PER_INT_SAMPLE ); } - - - -bool AudioDevice::hqAudio() const -{ - return ConfigManager::inst()->value( "audioengine", "hqaudio" ).toInt(); -} - - } // namespace lmms \ No newline at end of file diff --git a/src/core/audio/AudioFileFlac.cpp b/src/core/audio/AudioFileFlac.cpp index 097fbdd8957..dfb97a6beba 100644 --- a/src/core/audio/AudioFileFlac.cpp +++ b/src/core/audio/AudioFileFlac.cpp @@ -89,7 +89,7 @@ bool AudioFileFlac::startEncoding() return true; } -void AudioFileFlac::writeBuffer(surroundSampleFrame const* _ab, fpp_t const frames) +void AudioFileFlac::writeBuffer(const SampleFrame* _ab, fpp_t const frames) { OutputSettings::BitDepth depth = getOutputSettings().getBitDepth(); float clipvalue = std::nextafterf( -1.0f, 0.0f ); diff --git a/src/core/audio/AudioFileMP3.cpp b/src/core/audio/AudioFileMP3.cpp index 2141fabfc7e..4d1dbc02007 100644 --- a/src/core/audio/AudioFileMP3.cpp +++ b/src/core/audio/AudioFileMP3.cpp @@ -25,6 +25,8 @@ #include "AudioFileMP3.h" +#include "SampleFrame.h" + #ifdef LMMS_HAVE_MP3LAME @@ -53,7 +55,7 @@ AudioFileMP3::~AudioFileMP3() tearDownEncoder(); } -void AudioFileMP3::writeBuffer(const surroundSampleFrame* _buf, const fpp_t _frames) +void AudioFileMP3::writeBuffer(const SampleFrame* _buf, const fpp_t _frames) { if (_frames < 1) { diff --git a/src/core/audio/AudioFileOgg.cpp b/src/core/audio/AudioFileOgg.cpp index 9d5f0c809bb..59b796730bf 100644 --- a/src/core/audio/AudioFileOgg.cpp +++ b/src/core/audio/AudioFileOgg.cpp @@ -156,7 +156,6 @@ bool AudioFileOgg::startEncoding() ogg_packet header_main; ogg_packet header_comments; ogg_packet header_codebooks; - int result; // Build the packets vorbis_analysis_headerout( &m_vd, m_comments, &header_main, @@ -167,14 +166,9 @@ bool AudioFileOgg::startEncoding() ogg_stream_packetin( &m_os, &header_comments ); ogg_stream_packetin( &m_os, &header_codebooks ); - while( ( result = ogg_stream_flush( &m_os, &m_og ) ) ) + while (ogg_stream_flush(&m_os, &m_og)) { - if( !result ) - { - break; - } - int ret = writePage(); - if( ret != m_og.header_len + m_og.body_len ) + if (int ret = writePage(); ret != m_og.header_len + m_og.body_len) { // clean up finishEncoding(); @@ -185,7 +179,7 @@ bool AudioFileOgg::startEncoding() return true; } -void AudioFileOgg::writeBuffer(const surroundSampleFrame* _ab, const fpp_t _frames) +void AudioFileOgg::writeBuffer(const SampleFrame* _ab, const fpp_t _frames) { int eos = 0; diff --git a/src/core/audio/AudioFileWave.cpp b/src/core/audio/AudioFileWave.cpp index 612b9898277..4b4b928c5be 100644 --- a/src/core/audio/AudioFileWave.cpp +++ b/src/core/audio/AudioFileWave.cpp @@ -93,7 +93,7 @@ bool AudioFileWave::startEncoding() return true; } -void AudioFileWave::writeBuffer(const surroundSampleFrame* _ab, const fpp_t _frames) +void AudioFileWave::writeBuffer(const SampleFrame* _ab, const fpp_t _frames) { OutputSettings::BitDepth bitDepth = getOutputSettings().getBitDepth(); diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index 61d7814ed78..bd5b8e514de 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -37,7 +37,6 @@ #include "LcdSpinBox.h" #include "MainWindow.h" #include "MidiJack.h" -#include "gui_templates.h" namespace lmms { @@ -49,7 +48,7 @@ AudioJack::AudioJack(bool& successful, AudioEngine* audioEngineParam) std::clamp( ConfigManager::inst()->value("audiojack", "channels").toInt(), DEFAULT_CHANNELS, - SURROUND_CHANNELS + DEFAULT_CHANNELS ), // clang-format on audioEngineParam) @@ -57,7 +56,7 @@ AudioJack::AudioJack(bool& successful, AudioEngine* audioEngineParam) , m_active(false) , m_midiClient(nullptr) , m_tempOutBufs(new jack_default_audio_sample_t*[channels()]) - , m_outBuf(new surroundSampleFrame[audioEngine()->framesPerPeriod()]) + , m_outBuf(new SampleFrame[audioEngine()->framesPerPeriod()]) , m_framesDoneInCurBuf(0) , m_framesToDoInCurBuf(0) { @@ -230,24 +229,6 @@ void AudioJack::stopProcessing() m_stopped = true; } - - - -void AudioJack::applyQualitySettings() -{ - if (hqAudio()) - { - setSampleRate(Engine::audioEngine()->processingSampleRate()); - - if (jack_get_sample_rate(m_client) != sampleRate()) { setSampleRate(jack_get_sample_rate(m_client)); } - } - - AudioDevice::applyQualitySettings(); -} - - - - void AudioJack::registerPort(AudioPort* port) { #ifdef AUDIO_PORT_SUPPORT @@ -411,7 +392,7 @@ AudioJack::setupWidget::setupWidget(QWidget* parent) form->addRow(tr("Client name"), m_clientName); auto m = new gui::LcdSpinBoxModel(/* this */); - m->setRange(DEFAULT_CHANNELS, SURROUND_CHANNELS); + m->setRange(DEFAULT_CHANNELS, DEFAULT_CHANNELS); m->setStep(2); m->setValue(ConfigManager::inst()->value("audiojack", "channels").toInt()); diff --git a/src/core/audio/AudioOss.cpp b/src/core/audio/AudioOss.cpp index bd6d46c955f..bd427523520 100644 --- a/src/core/audio/AudioOss.cpp +++ b/src/core/audio/AudioOss.cpp @@ -34,7 +34,6 @@ #include "LcdSpinBox.h" #include "AudioEngine.h" #include "Engine.h" -#include "gui_templates.h" #ifdef LMMS_HAVE_UNISTD_H #include @@ -73,7 +72,7 @@ AudioOss::AudioOss( bool & _success_ful, AudioEngine* _audioEngine ) : AudioDevice(std::clamp( ConfigManager::inst()->value("audiooss", "channels").toInt(), DEFAULT_CHANNELS, - SURROUND_CHANNELS), _audioEngine), + DEFAULT_CHANNELS), _audioEngine), m_convertEndian( false ) { _success_ful = false; @@ -100,10 +99,9 @@ AudioOss::AudioOss( bool & _success_ful, AudioEngine* _audioEngine ) : fcntl( m_audioFD, F_SETFD, fcntl( m_audioFD, F_GETFD ) | FD_CLOEXEC ); int frag_spec; - for( frag_spec = 0; static_cast( 0x01 << frag_spec ) < - audioEngine()->framesPerPeriod() * channels() * - BYTES_PER_INT_SAMPLE; - ++frag_spec ) + for (frag_spec = 0; + 1u << frag_spec < audioEngine()->framesPerPeriod() * channels() * BYTES_PER_INT_SAMPLE; + ++frag_spec) { } @@ -255,44 +253,9 @@ void AudioOss::stopProcessing() stopProcessingThread( this ); } - - - -void AudioOss::applyQualitySettings() -{ - if( hqAudio() ) - { - setSampleRate( Engine::audioEngine()->processingSampleRate() ); - - unsigned int value = sampleRate(); - if ( ioctl( m_audioFD, SNDCTL_DSP_SPEED, &value ) < 0 ) - { - perror( "SNDCTL_DSP_SPEED" ); - printf( "Couldn't set audio frequency\n" ); - return; - } - if( value != sampleRate() ) - { - value = audioEngine()->baseSampleRate(); - if ( ioctl( m_audioFD, SNDCTL_DSP_SPEED, &value ) < 0 ) - { - perror( "SNDCTL_DSP_SPEED" ); - printf( "Couldn't set audio frequency\n" ); - return; - } - setSampleRate( value ); - } - } - - AudioDevice::applyQualitySettings(); -} - - - - void AudioOss::run() { - auto temp = new surroundSampleFrame[audioEngine()->framesPerPeriod()]; + auto temp = new SampleFrame[audioEngine()->framesPerPeriod()]; auto outbuf = new int_sample_t[audioEngine()->framesPerPeriod() * channels()]; while( true ) @@ -327,7 +290,7 @@ AudioOss::setupWidget::setupWidget( QWidget * _parent ) : form->addRow(tr("Device"), m_device); auto m = new gui::LcdSpinBoxModel(/* this */); - m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS ); + m->setRange(DEFAULT_CHANNELS, DEFAULT_CHANNELS); m->setStep( 2 ); m->setValue( ConfigManager::inst()->value( "audiooss", "channels" ).toInt() ); diff --git a/src/core/audio/AudioPort.cpp b/src/core/audio/AudioPort.cpp index 7bae3db1cf5..8efdd2c11a7 100644 --- a/src/core/audio/AudioPort.cpp +++ b/src/core/audio/AudioPort.cpp @@ -113,7 +113,7 @@ void AudioPort::doProcessing() const fpp_t fpp = Engine::audioEngine()->framesPerPeriod(); // clear the buffer - BufferManager::clear( m_portBuffer, fpp ); + zeroSampleFrames(m_portBuffer, fpp); //qDebug( "Playhandles: %d", m_playHandles.size() ); for( PlayHandle * ph : m_playHandles ) // now we mix all playhandle buffers into the audioport buffer diff --git a/src/core/audio/AudioPortAudio.cpp b/src/core/audio/AudioPortAudio.cpp index f303545d2ff..eb5058bc6b5 100644 --- a/src/core/audio/AudioPortAudio.cpp +++ b/src/core/audio/AudioPortAudio.cpp @@ -53,7 +53,6 @@ void AudioPortAudioSetupUtil::updateChannels() #include "Engine.h" #include "ConfigManager.h" -#include "gui_templates.h" #include "ComboBox.h" #include "AudioEngine.h" @@ -65,10 +64,10 @@ AudioPortAudio::AudioPortAudio( bool & _success_ful, AudioEngine * _audioEngine AudioDevice(std::clamp( ConfigManager::inst()->value("audioportaudio", "channels").toInt(), DEFAULT_CHANNELS, - SURROUND_CHANNELS), _audioEngine), + DEFAULT_CHANNELS), _audioEngine), m_paStream( nullptr ), m_wasPAInitError( false ), - m_outBuf( new surroundSampleFrame[audioEngine()->framesPerPeriod()] ), + m_outBuf(new SampleFrame[audioEngine()->framesPerPeriod()]), m_outBufPos( 0 ) { _success_ful = false; @@ -93,10 +92,9 @@ AudioPortAudio::AudioPortAudio( bool & _success_ful, AudioEngine * _audioEngine PaDeviceIndex inDevIdx = -1; PaDeviceIndex outDevIdx = -1; - const PaDeviceInfo * di; for( int i = 0; i < Pa_GetDeviceCount(); ++i ) { - di = Pa_GetDeviceInfo( i ); + const auto di = Pa_GetDeviceInfo(i); if( di->name == device && Pa_GetHostApiInfo( di->hostApi )->name == backend ) { @@ -231,46 +229,11 @@ void AudioPortAudio::stopProcessing() } - - -void AudioPortAudio::applyQualitySettings() -{ - if( hqAudio() ) - { - - setSampleRate( Engine::audioEngine()->processingSampleRate() ); - int samples = audioEngine()->framesPerPeriod(); - - PaError err = Pa_OpenStream( - &m_paStream, - supportsCapture() ? &m_inputParameters : nullptr, // The input parameter - &m_outputParameters, // The outputparameter - sampleRate(), - samples, - paNoFlag, // Don't use any flags - _process_callback, // our callback function - this ); - - if( err != paNoError ) - { - printf( "Couldn't open PortAudio: %s\n", Pa_GetErrorText( err ) ); - return; - } - } - - AudioDevice::applyQualitySettings(); -} - - - -int AudioPortAudio::process_callback( - const float *_inputBuffer, - float * _outputBuffer, - unsigned long _framesPerBuffer ) +int AudioPortAudio::process_callback(const float* _inputBuffer, float* _outputBuffer, f_cnt_t _framesPerBuffer) { if( supportsCapture() ) { - audioEngine()->pushInputFrames( (sampleFrame*)_inputBuffer, _framesPerBuffer ); + audioEngine()->pushInputFrames( (SampleFrame*)_inputBuffer, _framesPerBuffer ); } if( m_stopped ) @@ -295,8 +258,7 @@ int AudioPortAudio::process_callback( } m_outBufSize = frames; } - const int min_len = std::min(static_cast(_framesPerBuffer), - m_outBufSize - m_outBufPos); + const auto min_len = std::min(_framesPerBuffer, m_outBufSize - m_outBufPos); for( fpp_t frame = 0; frame < min_len; ++frame ) { @@ -344,10 +306,9 @@ void AudioPortAudioSetupUtil::updateBackends() return; } - const PaHostApiInfo * hi; for( int i = 0; i < Pa_GetHostApiCount(); ++i ) { - hi = Pa_GetHostApiInfo( i ); + const auto hi = Pa_GetHostApiInfo(i); m_backendModel.addItem( hi->name ); } @@ -368,10 +329,9 @@ void AudioPortAudioSetupUtil::updateDevices() // get active backend const QString& backend = m_backendModel.currentText(); int hostApi = 0; - const PaHostApiInfo * hi; for( int i = 0; i < Pa_GetHostApiCount(); ++i ) { - hi = Pa_GetHostApiInfo( i ); + const auto hi = Pa_GetHostApiInfo(i); if( backend == hi->name ) { hostApi = i; @@ -381,10 +341,9 @@ void AudioPortAudioSetupUtil::updateDevices() // get devices for selected backend m_deviceModel.clear(); - const PaDeviceInfo * di; for( int i = 0; i < Pa_GetDeviceCount(); ++i ) { - di = Pa_GetDeviceInfo( i ); + const auto di = Pa_GetDeviceInfo(i); if( di->hostApi == hostApi ) { m_deviceModel.addItem( di->name ); @@ -424,7 +383,7 @@ AudioPortAudio::setupWidget::setupWidget( QWidget * _parent ) : form->addRow(tr("Device"), m_device); /* LcdSpinBoxModel * m = new LcdSpinBoxModel( ); - m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS ); + m->setRange( DEFAULT_CHANNELS, DEFAULT_CHANNELS ); m->setStep( 2 ); m->setValue( ConfigManager::inst()->value( "audioportaudio", "channels" ).toInt() ); diff --git a/src/core/audio/AudioPulseAudio.cpp b/src/core/audio/AudioPulseAudio.cpp index a0c5ccaf9e1..aa434487154 100644 --- a/src/core/audio/AudioPulseAudio.cpp +++ b/src/core/audio/AudioPulseAudio.cpp @@ -32,7 +32,6 @@ #include "ConfigManager.h" #include "LcdSpinBox.h" #include "AudioEngine.h" -#include "gui_templates.h" #include "Engine.h" namespace lmms @@ -50,7 +49,7 @@ AudioPulseAudio::AudioPulseAudio( bool & _success_ful, AudioEngine* _audioEngin AudioDevice(std::clamp( ConfigManager::inst()->value("audiopa", "channels").toInt(), DEFAULT_CHANNELS, - SURROUND_CHANNELS), _audioEngine), + DEFAULT_CHANNELS), _audioEngine), m_s( nullptr ), m_quit( false ), m_convertEndian( false ) @@ -110,22 +109,6 @@ void AudioPulseAudio::stopProcessing() } - - -void AudioPulseAudio::applyQualitySettings() -{ - if( hqAudio() ) - { -// setSampleRate( engine::audioEngine()->processingSampleRate() ); - - } - - AudioDevice::applyQualitySettings(); -} - - - - /* This routine is called whenever the stream state changes */ static void stream_state_callback( pa_stream *s, void * userdata ) { @@ -247,7 +230,7 @@ void AudioPulseAudio::run() else { const fpp_t fpp = audioEngine()->framesPerPeriod(); - auto temp = new surroundSampleFrame[fpp]; + auto temp = new SampleFrame[fpp]; while( getNextBuffer( temp ) ) { } @@ -266,7 +249,7 @@ void AudioPulseAudio::run() void AudioPulseAudio::streamWriteCallback( pa_stream *s, size_t length ) { const fpp_t fpp = audioEngine()->framesPerPeriod(); - auto temp = new surroundSampleFrame[fpp]; + auto temp = new SampleFrame[fpp]; auto pcmbuf = (int_sample_t*)pa_xmalloc(fpp * channels() * sizeof(int_sample_t)); size_t fd = 0; @@ -315,7 +298,7 @@ AudioPulseAudio::setupWidget::setupWidget( QWidget * _parent ) : form->addRow(tr("Device"), m_device); auto m = new gui::LcdSpinBoxModel(); - m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS ); + m->setRange(DEFAULT_CHANNELS, DEFAULT_CHANNELS); m->setStep( 2 ); m->setValue( ConfigManager::inst()->value( "audiopa", "channels" ).toInt() ); diff --git a/src/core/audio/AudioSampleRecorder.cpp b/src/core/audio/AudioSampleRecorder.cpp index c9448b89ec9..0e51c9d2d6c 100644 --- a/src/core/audio/AudioSampleRecorder.cpp +++ b/src/core/audio/AudioSampleRecorder.cpp @@ -71,7 +71,7 @@ std::shared_ptr AudioSampleRecorder::createSampleBuffer() { const f_cnt_t frames = framesRecorded(); // create buffer to store all recorded buffers in - auto bigBuffer = std::vector(frames); + auto bigBuffer = std::vector(frames); // now copy all buffers into big buffer auto framesCopied = 0; @@ -85,9 +85,9 @@ std::shared_ptr AudioSampleRecorder::createSampleBuffer() return std::make_shared(std::move(bigBuffer), sampleRate()); } -void AudioSampleRecorder::writeBuffer(const surroundSampleFrame* _ab, const fpp_t _frames) +void AudioSampleRecorder::writeBuffer(const SampleFrame* _ab, const fpp_t _frames) { - auto buf = new sampleFrame[_frames]; + auto buf = new SampleFrame[_frames]; for( fpp_t frame = 0; frame < _frames; ++frame ) { for( ch_cnt_t chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) diff --git a/src/core/audio/AudioSdl.cpp b/src/core/audio/AudioSdl.cpp index 679912c50f5..8f533119c8a 100644 --- a/src/core/audio/AudioSdl.cpp +++ b/src/core/audio/AudioSdl.cpp @@ -23,35 +23,33 @@ */ #include "AudioSdl.h" +#include "lmms_basics.h" #ifdef LMMS_HAVE_SDL #include #include +#include #include #include "AudioEngine.h" #include "ConfigManager.h" -#include "gui_templates.h" namespace lmms { +constexpr auto SectionSDL = "audiosdl"; +constexpr auto PlaybackDeviceSDL = "device"; +constexpr auto InputDeviceSDL = "inputdevice"; + AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) : AudioDevice( DEFAULT_CHANNELS, _audioEngine ), - m_outBuf( new surroundSampleFrame[audioEngine()->framesPerPeriod()] ) + m_outBuf(new SampleFrame[audioEngine()->framesPerPeriod()]) { _success_ful = false; -#ifdef LMMS_HAVE_SDL2 m_currentBufferFramesCount = 0; m_currentBufferFramePos = 0; -#else - m_convertedBufSize = audioEngine()->framesPerPeriod() * channels() - * sizeof( int_sample_t ); - m_convertedBufPos = 0; - m_convertedBuf = new Uint8[m_convertedBufSize]; -#endif if( SDL_Init( SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE ) < 0 ) { @@ -60,67 +58,63 @@ AudioSdl::AudioSdl( bool & _success_ful, AudioEngine* _audioEngine ) : } m_audioHandle.freq = sampleRate(); -#ifdef LMMS_HAVE_SDL2 m_audioHandle.format = AUDIO_F32SYS; // we want it in byte-order // of system, so we don't have // to convert the buffers -#else - m_audioHandle.format = AUDIO_S16SYS; // we want it in byte-order - // of system, so we don't have - // to convert the buffers -#endif + m_audioHandle.channels = channels(); - m_audioHandle.samples = std::max(1024, audioEngine()->framesPerPeriod() * 2); + m_audioHandle.samples = std::max(f_cnt_t{1024}, audioEngine()->framesPerPeriod() * 2); m_audioHandle.callback = sdlAudioCallback; m_audioHandle.userdata = this; SDL_AudioSpec actual; -#ifdef LMMS_HAVE_SDL2 - m_outputDevice = SDL_OpenAudioDevice (nullptr, - 0, - &m_audioHandle, - &actual, - 0); - if (m_outputDevice == 0) { - qCritical( "Couldn't open SDL-audio: %s\n", SDL_GetError() ); - return; - } -#else - // open the audio device, forcing the desired format - if( SDL_OpenAudio( &m_audioHandle, &actual ) < 0 ) + const auto playbackDevice = ConfigManager::inst()->value(SectionSDL, PlaybackDeviceSDL).toStdString(); + const bool isDefaultPlayback = playbackDevice.empty(); + + // Try with the configured device + const auto playbackDeviceCStr = isDefaultPlayback ? nullptr : playbackDevice.c_str(); + m_outputDevice = SDL_OpenAudioDevice(playbackDeviceCStr, 0, &m_audioHandle, &actual, 0); + + // If we did not get a device ID try again with the default device if we did not try that before + if (m_outputDevice == 0 && !isDefaultPlayback) { + m_outputDevice = SDL_OpenAudioDevice(nullptr, 0, &m_audioHandle, &actual, 0); + } + + if (m_outputDevice == 0) { qCritical( "Couldn't open SDL-audio: %s\n", SDL_GetError() ); return; } - m_outConvertEndian = ( m_audioHandle.format != actual.format ); -#endif - - _success_ful = true; -#ifdef LMMS_HAVE_SDL2 // Workaround for a race condition that causes SDL to segfault SDL_Delay(50); m_inputAudioHandle = m_audioHandle; m_inputAudioHandle.callback = sdlInputAudioCallback; - m_inputDevice = SDL_OpenAudioDevice (nullptr, - 1, - &m_inputAudioHandle, - &actual, - 0); + const auto inputDevice = ConfigManager::inst()->value(SectionSDL, InputDeviceSDL).toStdString(); + const bool isDefaultInput = inputDevice.empty(); + + // Try with the configured device + const auto inputDeviceCStr = isDefaultInput ? nullptr : inputDevice.c_str(); + m_inputDevice = SDL_OpenAudioDevice (inputDeviceCStr, 1, &m_inputAudioHandle, &actual, 0); + + // If we did not get a device ID try again with the default device if we did not try that before + if (m_inputDevice == 0 && !isDefaultInput) + { + m_inputDevice = SDL_OpenAudioDevice(nullptr, 1, &m_inputAudioHandle, &actual, 0); + } + if (m_inputDevice != 0) { m_supportsCapture = true; } else { m_supportsCapture = false; qWarning ( "Couldn't open SDL capture device: %s\n", SDL_GetError ()); } - -#endif } @@ -130,15 +124,10 @@ AudioSdl::~AudioSdl() { stopProcessing(); -#ifdef LMMS_HAVE_SDL2 if (m_inputDevice != 0) SDL_CloseAudioDevice(m_inputDevice); if (m_outputDevice != 0) SDL_CloseAudioDevice(m_outputDevice); -#else - SDL_CloseAudio(); - delete[] m_convertedBuf; -#endif SDL_Quit(); @@ -152,12 +141,8 @@ void AudioSdl::startProcessing() { m_stopped = false; -#ifdef LMMS_HAVE_SDL2 SDL_PauseAudioDevice (m_outputDevice, 0); SDL_PauseAudioDevice (m_inputDevice, 0); -#else - SDL_PauseAudio( 0 ); -#endif } @@ -165,13 +150,8 @@ void AudioSdl::startProcessing() void AudioSdl::stopProcessing() { -#ifdef LMMS_HAVE_SDL2 if( SDL_GetAudioDeviceStatus(m_outputDevice) == SDL_AUDIO_PLAYING ) -#else - if( SDL_GetAudioStatus() == SDL_AUDIO_PLAYING ) -#endif { -#ifdef LMMS_HAVE_SDL2 SDL_LockAudioDevice (m_inputDevice); SDL_LockAudioDevice (m_outputDevice); @@ -182,47 +162,9 @@ void AudioSdl::stopProcessing() SDL_UnlockAudioDevice (m_inputDevice); SDL_UnlockAudioDevice (m_outputDevice); -#else - SDL_LockAudio(); - m_stopped = true; - SDL_PauseAudio( 1 ); - SDL_UnlockAudio(); -#endif - } } - - - -void AudioSdl::applyQualitySettings() -{ - // Better than if (0) -#if 0 - if( 0 )//hqAudio() ) - { - SDL_CloseAudio(); - - setSampleRate( Engine::audioEngine()->processingSampleRate() ); - - m_audioHandle.freq = sampleRate(); - - SDL_AudioSpec actual; - - // open the audio device, forcing the desired format - if( SDL_OpenAudio( &m_audioHandle, &actual ) < 0 ) - { - qCritical( "Couldn't open SDL-audio: %s\n", SDL_GetError() ); - } - } -#endif - - AudioDevice::applyQualitySettings(); -} - - - - void AudioSdl::sdlAudioCallback( void * _udata, Uint8 * _buf, int _len ) { auto _this = static_cast(_udata); @@ -242,7 +184,6 @@ void AudioSdl::sdlAudioCallback( Uint8 * _buf, int _len ) } // SDL2: process float samples -#ifdef LMMS_HAVE_SDL2 while( _len ) { if( m_currentBufferFramePos == 0 ) @@ -257,48 +198,19 @@ void AudioSdl::sdlAudioCallback( Uint8 * _buf, int _len ) m_currentBufferFramesCount = frames; } - const uint min_frames_count = std::min(_len/sizeof(sampleFrame), + const uint min_frames_count = std::min(_len/sizeof(SampleFrame), m_currentBufferFramesCount - m_currentBufferFramePos); - memcpy( _buf, m_outBuf + m_currentBufferFramePos, min_frames_count*sizeof(sampleFrame) ); - _buf += min_frames_count*sizeof(sampleFrame); - _len -= min_frames_count*sizeof(sampleFrame); + memcpy( _buf, m_outBuf + m_currentBufferFramePos, min_frames_count*sizeof(SampleFrame) ); + _buf += min_frames_count*sizeof(SampleFrame); + _len -= min_frames_count*sizeof(SampleFrame); m_currentBufferFramePos += min_frames_count; m_currentBufferFramePos %= m_currentBufferFramesCount; } -#else - while( _len ) - { - if( m_convertedBufPos == 0 ) - { - // frames depend on the sample rate - const fpp_t frames = getNextBuffer( m_outBuf ); - if( !frames ) - { - m_stopped = true; - memset( _buf, 0, _len ); - return; - } - m_convertedBufSize = frames * channels() - * sizeof( int_sample_t ); - - convertToS16(m_outBuf, frames, reinterpret_cast(m_convertedBuf), m_outConvertEndian); - } - const int min_len = std::min(_len, m_convertedBufSize - - m_convertedBufPos); - memcpy( _buf, m_convertedBuf + m_convertedBufPos, min_len ); - _buf += min_len; - _len -= min_len; - m_convertedBufPos += min_len; - m_convertedBufPos %= m_convertedBufSize; - } -#endif // LMMS_HAVE_SDL2 } -#ifdef LMMS_HAVE_SDL2 - void AudioSdl::sdlInputAudioCallback(void *_udata, Uint8 *_buf, int _len) { auto _this = static_cast(_udata); @@ -306,23 +218,31 @@ void AudioSdl::sdlInputAudioCallback(void *_udata, Uint8 *_buf, int _len) { } void AudioSdl::sdlInputAudioCallback(Uint8 *_buf, int _len) { - auto samples_buffer = (sampleFrame*)_buf; - fpp_t frames = _len / sizeof ( sampleFrame ); + auto samples_buffer = (SampleFrame*)_buf; + fpp_t frames = _len / sizeof ( SampleFrame ); audioEngine()->pushInputFrames (samples_buffer, frames); } -#endif +QString AudioSdl::setupWidget::s_systemDefaultDevice = AudioDeviceSetupWidget::tr("[System Default]"); AudioSdl::setupWidget::setupWidget( QWidget * _parent ) : AudioDeviceSetupWidget( AudioSdl::name(), _parent ) { QFormLayout * form = new QFormLayout(this); + form->setRowWrapPolicy(QFormLayout::WrapLongRows); + + m_playbackDeviceComboBox = new QComboBox(this); + + populatePlaybackDeviceComboBox(); - QString dev = ConfigManager::inst()->value( "audiosdl", "device" ); - m_device = new QLineEdit( dev, this ); + form->addRow(tr("Playback device"), m_playbackDeviceComboBox); - form->addRow(tr("Device"), m_device); + m_inputDeviceComboBox = new QComboBox(this); + + populateInputDeviceComboBox(); + + form->addRow(tr("Input device"), m_inputDeviceComboBox); } @@ -330,8 +250,68 @@ AudioSdl::setupWidget::setupWidget( QWidget * _parent ) : void AudioSdl::setupWidget::saveSettings() { - ConfigManager::inst()->setValue( "audiosdl", "device", - m_device->text() ); + const auto currentPlaybackDevice = m_playbackDeviceComboBox->currentText(); + if (currentPlaybackDevice == s_systemDefaultDevice) + { + // Represent the default playback device with an empty string + ConfigManager::inst()->setValue(SectionSDL, PlaybackDeviceSDL, ""); + } + else if (!currentPlaybackDevice.isEmpty()) + { + ConfigManager::inst()->setValue(SectionSDL, PlaybackDeviceSDL, currentPlaybackDevice); + } + + const auto currentInputDevice = m_inputDeviceComboBox->currentText(); + if (currentInputDevice == s_systemDefaultDevice) + { + // Represent the default input device with an empty string + ConfigManager::inst()->setValue(SectionSDL, InputDeviceSDL, ""); + } + else if (!currentInputDevice.isEmpty()) + { + ConfigManager::inst()->setValue(SectionSDL, InputDeviceSDL, currentInputDevice); + } +} + +void AudioSdl::setupWidget::populatePlaybackDeviceComboBox() +{ + m_playbackDeviceComboBox->addItem(s_systemDefaultDevice); + + QStringList playbackDevices; + const int numberOfPlaybackDevices = SDL_GetNumAudioDevices(0); + for (int i = 0; i < numberOfPlaybackDevices; ++i) + { + const QString deviceName = SDL_GetAudioDeviceName(i, 0); + playbackDevices.append(deviceName); + } + + playbackDevices.sort(); + + m_playbackDeviceComboBox->addItems(playbackDevices); + + const auto playbackDevice = ConfigManager::inst()->value(SectionSDL, PlaybackDeviceSDL); + m_playbackDeviceComboBox->setCurrentText(playbackDevice.isEmpty() ? s_systemDefaultDevice : playbackDevice); +} + +void AudioSdl::setupWidget::populateInputDeviceComboBox() +{ + m_inputDeviceComboBox->addItem(s_systemDefaultDevice); + + QStringList inputDevices; + const int numberOfInputDevices = SDL_GetNumAudioDevices(1); + for (int i = 0; i < numberOfInputDevices; ++i) + { + const QString deviceName = SDL_GetAudioDeviceName(i, 1); + inputDevices.append(deviceName); + } + + inputDevices.sort(); + + m_inputDeviceComboBox->addItems(inputDevices); + + // Set the current device to the one in the configuration + const auto inputDevice = ConfigManager::inst()->value(SectionSDL, InputDeviceSDL); + m_inputDeviceComboBox->setCurrentText(inputDevice.isEmpty() ? s_systemDefaultDevice : inputDevice); } diff --git a/src/core/audio/AudioSndio.cpp b/src/core/audio/AudioSndio.cpp index d934dfb9c8d..95fab473a79 100644 --- a/src/core/audio/AudioSndio.cpp +++ b/src/core/audio/AudioSndio.cpp @@ -35,7 +35,6 @@ #include "LcdSpinBox.h" #include "AudioEngine.h" #include "Engine.h" -#include "gui_templates.h" #include "ConfigManager.h" @@ -47,7 +46,7 @@ AudioSndio::AudioSndio(bool & _success_ful, AudioEngine * _audioEngine) : AudioDevice(std::clamp( ConfigManager::inst()->value("audiosndio", "channels").toInt(), DEFAULT_CHANNELS, - SURROUND_CHANNELS), _audioEngine), + DEFAULT_CHANNELS), _audioEngine), m_convertEndian ( false ) { _success_ful = false; @@ -140,23 +139,9 @@ void AudioSndio::stopProcessing() stopProcessingThread( this ); } - -void AudioSndio::applyQualitySettings() -{ - if( hqAudio() ) - { - setSampleRate( Engine::audioEngine()->processingSampleRate() ); - - /* change sample rate to sampleRate() */ - } - - AudioDevice::applyQualitySettings(); -} - - void AudioSndio::run() { - surroundSampleFrame * temp = new surroundSampleFrame[audioEngine()->framesPerPeriod()]; + SampleFrame* temp = new SampleFrame[audioEngine()->framesPerPeriod()]; int_sample_t * outbuf = new int_sample_t[audioEngine()->framesPerPeriod() * channels()]; while( true ) @@ -188,7 +173,7 @@ AudioSndio::setupWidget::setupWidget( QWidget * _parent ) : form->addRow(tr("Device"), m_device); gui::LcdSpinBoxModel * m = new gui::LcdSpinBoxModel( /* this */ ); - m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS ); + m->setRange(DEFAULT_CHANNELS, DEFAULT_CHANNELS); m->setStep( 2 ); m->setValue( ConfigManager::inst()->value( "audiosndio", "channels" ).toInt() ); diff --git a/src/core/audio/AudioSoundIo.cpp b/src/core/audio/AudioSoundIo.cpp index 6e8a03e384f..c7fa380e402 100644 --- a/src/core/audio/AudioSoundIo.cpp +++ b/src/core/audio/AudioSoundIo.cpp @@ -32,7 +32,6 @@ #include "Engine.h" #include "debug.h" #include "ConfigManager.h" -#include "gui_templates.h" #include "ComboBox.h" #include "AudioEngine.h" @@ -43,7 +42,7 @@ AudioSoundIo::AudioSoundIo( bool & outSuccessful, AudioEngine * _audioEngine ) : AudioDevice(std::clamp( ConfigManager::inst()->value("audiosoundio", "channels").toInt(), DEFAULT_CHANNELS, - SURROUND_CHANNELS), _audioEngine) + DEFAULT_CHANNELS), _audioEngine) { outSuccessful = false; m_soundio = nullptr; @@ -70,7 +69,6 @@ AudioSoundIo::AudioSoundIo( bool & outSuccessful, AudioEngine * _audioEngine ) : const QString& configDeviceId = ConfigManager::inst()->value( "audiosoundio", "out_device_id" ); const QString& configDeviceRaw = ConfigManager::inst()->value( "audiosoundio", "out_device_raw" ); - int err; int outDeviceCount = 0; int backendCount = soundio_backend_count(m_soundio); for (int i = 0; i < backendCount; i += 1) @@ -78,11 +76,7 @@ AudioSoundIo::AudioSoundIo( bool & outSuccessful, AudioEngine * _audioEngine ) : SoundIoBackend backend = soundio_get_backend(m_soundio, i); if (configBackend == soundio_backend_name(backend)) { - if ((err = soundio_connect_backend(m_soundio, backend))) - { - // error occurred, leave outDeviceCount 0 - } - else + if (!soundio_connect_backend(m_soundio, backend)) { soundio_flush_events(m_soundio); if (m_disconnectErr) @@ -99,7 +93,7 @@ AudioSoundIo::AudioSoundIo( bool & outSuccessful, AudioEngine * _audioEngine ) : if (outDeviceCount <= 0) { // try connecting to the default backend - if ((err = soundio_connect(m_soundio))) + if (int err = soundio_connect(m_soundio)) { fprintf(stderr, "Unable to initialize soundio: %s\n", soundio_strerror(err)); return; @@ -180,7 +174,7 @@ AudioSoundIo::AudioSoundIo( bool & outSuccessful, AudioEngine * _audioEngine ) : m_outstream->layout = *soundio_channel_layout_get_default(channels()); m_outstream->format = SoundIoFormatFloat32NE; - if ((err = soundio_outstream_open(m_outstream))) + if (int err = soundio_outstream_open(m_outstream)) { fprintf(stderr, "Unable to initialize soundio: %s\n", soundio_strerror(err)); return; @@ -215,17 +209,15 @@ AudioSoundIo::~AudioSoundIo() void AudioSoundIo::startProcessing() { - int err; - m_outBufFrameIndex = 0; m_outBufFramesTotal = 0; m_outBufSize = audioEngine()->framesPerPeriod(); - m_outBuf = new surroundSampleFrame[m_outBufSize]; + m_outBuf = new SampleFrame[m_outBufSize]; if (! m_outstreamStarted) { - if ((err = soundio_outstream_start(m_outstream))) + if (int err = soundio_outstream_start(m_outstream)) { fprintf(stderr, "AudioSoundIo::startProcessing() :: soundio unable to start stream: %s\n", @@ -237,7 +229,7 @@ void AudioSoundIo::startProcessing() m_stopped = false; - if ((err = soundio_outstream_pause(m_outstream, false))) + if (int err = soundio_outstream_pause(m_outstream, false)) { m_stopped = true; fprintf(stderr, @@ -248,12 +240,10 @@ void AudioSoundIo::startProcessing() void AudioSoundIo::stopProcessing() { - int err; - m_stopped = true; if (m_outstream) { - if ((err = soundio_outstream_pause(m_outstream, true))) + if (int err = soundio_outstream_pause(m_outstream, true)) { fprintf(stderr, "AudioSoundIo::stopProcessing() :: pausing result error: %s\n", @@ -282,16 +272,14 @@ void AudioSoundIo::writeCallback(int frameCountMin, int frameCountMax) { if (m_stopped) {return;} const struct SoundIoChannelLayout *layout = &m_outstream->layout; - SoundIoChannelArea *areas; + SoundIoChannelArea* areas; int bytesPerSample = m_outstream->bytes_per_sample; - int err; - int framesLeft = frameCountMax; while (framesLeft > 0) { int frameCount = framesLeft; - if ((err = soundio_outstream_begin_write(m_outstream, &areas, &frameCount))) + if (int err = soundio_outstream_begin_write(m_outstream, &areas, &frameCount)) { errorCallback(err); return; @@ -333,7 +321,7 @@ void AudioSoundIo::writeCallback(int frameCountMin, int frameCountMax) m_outBufFrameIndex += 1; } - if ((err = soundio_outstream_end_write(m_outstream))) + if (int err = soundio_outstream_end_write(m_outstream)) { errorCallback(err); return; @@ -373,11 +361,10 @@ void AudioSoundIo::setupWidget::reconnectSoundIo() soundio_disconnect(m_soundio); - int err; int backend_index = m_backendModel.findText(configBackend); if (backend_index < 0) { - if ((err = soundio_connect(m_soundio))) + if (int err = soundio_connect(m_soundio)) { fprintf(stderr, "soundio: unable to connect backend: %s\n", soundio_strerror(err)); return; @@ -388,11 +375,11 @@ void AudioSoundIo::setupWidget::reconnectSoundIo() else { SoundIoBackend backend = soundio_get_backend(m_soundio, backend_index); - if ((err = soundio_connect_backend(m_soundio, backend))) + if (int err = soundio_connect_backend(m_soundio, backend)) { fprintf(stderr, "soundio: unable to connect %s backend: %s\n", soundio_backend_name(backend), soundio_strerror(err)); - if ((err = soundio_connect(m_soundio))) + if (int err = soundio_connect(m_soundio)) { fprintf(stderr, "soundio: unable to connect backend: %s\n", soundio_strerror(err)); return; diff --git a/src/core/fft_helpers.cpp b/src/core/fft_helpers.cpp index 35906e8d3d1..c2431034170 100644 --- a/src/core/fft_helpers.cpp +++ b/src/core/fft_helpers.cpp @@ -122,13 +122,13 @@ int precomputeWindow(float *window, unsigned int length, FFTWindow type, bool no gain = 1; return 0; case FFTWindow::BlackmanHarris: - a0 = 0.35875; - a1 = 0.48829; - a2 = 0.14128; - a3 = 0.01168; + a0 = 0.35875f; + a1 = 0.48829f; + a2 = 0.14128f; + a3 = 0.01168f; break; case FFTWindow::Hamming: - a0 = 0.54; + a0 = 0.54f; a1 = 1.0 - a0; a2 = 0; a3 = 0; @@ -204,11 +204,11 @@ int compressbands(const float *absspec_buffer, float *compressedband, int num_ol float j_min = (i * ratio) + bottom; - if (j_min < 0) {j_min = bottom;} + if (j_min < 0) { j_min = static_cast(bottom); } float j_max = j_min + ratio; - for (float j = (int)j_min; j <= j_max; j++) + for (float j = std::floor(j_min); j <= j_max; j++) { compressedband[i] += absspec_buffer[(int)j]; } diff --git a/src/core/lv2/Lv2ControlBase.cpp b/src/core/lv2/Lv2ControlBase.cpp index 5741866e9fd..0147ebd6e6b 100644 --- a/src/core/lv2/Lv2ControlBase.cpp +++ b/src/core/lv2/Lv2ControlBase.cpp @@ -137,7 +137,7 @@ void Lv2ControlBase::copyModelsToLmms() const -void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) { +void Lv2ControlBase::copyBuffersFromLmms(const SampleFrame* buf, fpp_t frames) { unsigned firstChan = 0; // tell the procs which channels they shall read from for (const auto& c : m_procs) { @@ -149,7 +149,7 @@ void Lv2ControlBase::copyBuffersFromLmms(const sampleFrame *buf, fpp_t frames) { -void Lv2ControlBase::copyBuffersToLmms(sampleFrame *buf, fpp_t frames) const { +void Lv2ControlBase::copyBuffersToLmms(SampleFrame* buf, fpp_t frames) const { unsigned firstChan = 0; // tell the procs which channels they shall write to for (const auto& c : m_procs) { c->copyBuffersToCore(buf, firstChan, m_channelsPerProc, frames); diff --git a/src/core/lv2/Lv2Evbuf.cpp b/src/core/lv2/Lv2Evbuf.cpp index acfb9b8aa62..486910feed1 100644 --- a/src/core/lv2/Lv2Evbuf.cpp +++ b/src/core/lv2/Lv2Evbuf.cpp @@ -34,7 +34,7 @@ #include #include -#include +#include namespace lmms { @@ -129,12 +129,11 @@ lv2_evbuf_next(LV2_Evbuf_Iterator iter) LV2_Evbuf* evbuf = iter.evbuf; uint32_t offset = iter.offset; - uint32_t size; - size = ((LV2_Atom_Event*) - ((char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, &evbuf->buf.atom) - + offset))->body.size; - offset += lv2_evbuf_pad_size(sizeof(LV2_Atom_Event) + size); + const auto contents = static_cast(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, &evbuf->buf.atom)) + offset; + const uint32_t size = reinterpret_cast(contents)->body.size; + + offset += lv2_evbuf_pad_size(sizeof(LV2_Atom_Event) + size); LV2_Evbuf_Iterator next = { evbuf, offset }; return next; } diff --git a/src/core/lv2/Lv2Manager.cpp b/src/core/lv2/Lv2Manager.cpp index 6a1b2a8af20..9807379e44c 100644 --- a/src/core/lv2/Lv2Manager.cpp +++ b/src/core/lv2/Lv2Manager.cpp @@ -1,7 +1,7 @@ /* * Lv2Manager.cpp - Implementation of Lv2Manager class * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -29,13 +29,14 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include #include #include "AudioEngine.h" +#include "ConfigManager.h" #include "Engine.h" #include "Plugin.h" #include "Lv2ControlBase.h" @@ -47,7 +48,7 @@ namespace lmms { -const std::set Lv2Manager::pluginBlacklist = +const std::set Lv2Manager::unstablePlugins = { // github.com/calf-studio-gear/calf, #278 "http://calf.sourceforge.net/plugins/Analyzer", @@ -67,7 +68,13 @@ const std::set Lv2Manager::pluginBlacklist = "http://drobilla.net/plugins/blop/square", "http://drobilla.net/plugins/blop/triangle", - // Visualization, meters, and scopes etc., won't work until we have gui support + // unstable + "urn:juced:DrumSynth" +}; + +const std::set Lv2Manager::pluginsOnlyUsefulWithUi = +{ + // Visualization, meters, and scopes etc., won't work if UI is disabled "http://distrho.sf.net/plugins/ProM", "http://distrho.sf.net/plugins/glBars", "http://gareus.org/oss/lv2/meters#spectr30mono", @@ -132,13 +139,10 @@ const std::set Lv2Manager::pluginBlacklist = "urn:juce:TalFilter2", "urn:juce:Vex", "http://zynaddsubfx.sourceforge.net", - "http://geontime.com/geonkick/single", - - // unstable - "urn:juced:DrumSynth" + "http://geontime.com/geonkick/single" }; -const std::set Lv2Manager::pluginBlacklistBuffersizeLessThan32 = +const std::set Lv2Manager::unstablePluginsBuffersizeLessEqual32 = { "http://moddevices.com/plugins/mod-devel/2Voices", "http://moddevices.com/plugins/mod-devel/Capo", @@ -237,7 +241,7 @@ void Lv2Manager::initPlugins() QElapsedTimer timer; timer.start(); - unsigned blacklisted = 0; + unsigned blocked = 0; LILV_FOREACH(plugins, itr, plugins) { const LilvPlugin* curPlug = lilv_plugins_get(plugins, itr); @@ -266,9 +270,9 @@ void Lv2Manager::initPlugins() { if(std::any_of(issues.begin(), issues.end(), [](const PluginIssue& iss) { - return iss.type() == PluginIssueType::Blacklisted; })) + return iss.type() == PluginIssueType::Blocked; })) { - ++blacklisted; + ++blocked; } } ++pluginCount; @@ -295,19 +299,19 @@ void Lv2Manager::initPlugins() } // TODO: might be better in the LMMS core - if(Engine::ignorePluginBlacklist()) + if(ConfigManager::enableBlockedPlugins()) { qWarning() << - "WARNING! Plugin blacklist disabled! If you want to use the blacklist,\n" - " please set environment variable \"LMMS_IGNORE_BLACKLIST\" to empty or\n" + "WARNING! Blocked plugins enabled! If you want to disable them,\n" + " please set environment variable \"LMMS_ENABLE_BLOCKED_PLUGINS\" to empty or\n" " do not set it."; } - else if(blacklisted > 0) + else if(blocked > 0) { qDebug() << - "Lv2 Plugins blacklisted:" << blacklisted << "of" << pluginCount << "\n" - " If you want to ignore the blacklist (dangerous!), please set\n" - " environment variable \"LMMS_IGNORE_BLACKLIST\" to nonempty."; + "Blocked Lv2 Plugins:" << blocked << "of" << pluginCount << "\n" + " If you want to enable them (dangerous!), please set\n" + " environment variable \"LMMS_ENABLE_BLOCKED_PLUGINS\" to nonempty."; } } @@ -331,6 +335,14 @@ AutoLilvNodes Lv2Manager::findNodes(const LilvNode *subject, +bool Lv2Manager::wantUi() +{ + return false; +} + + + + // unused + untested yet bool Lv2Manager::isSubclassOf(const LilvPluginClass* clvss, const char* uriStr) { diff --git a/src/core/lv2/Lv2Ports.cpp b/src/core/lv2/Lv2Ports.cpp index a4625936e62..bdb0d4f006e 100644 --- a/src/core/lv2/Lv2Ports.cpp +++ b/src/core/lv2/Lv2Ports.cpp @@ -27,13 +27,14 @@ #ifdef LMMS_HAVE_LV2 -#include -#include +#include +#include #include "Engine.h" #include "Lv2Basics.h" #include "Lv2Manager.h" #include "Lv2Evbuf.h" +#include "SampleFrame.h" namespace lmms::Lv2Ports @@ -311,7 +312,7 @@ Audio::Audio(std::size_t bufferSize, bool isSidechain) -void Audio::copyBuffersFromCore(const sampleFrame *lmmsBuf, +void Audio::copyBuffersFromCore(const SampleFrame* lmmsBuf, unsigned channel, fpp_t frames) { for (std::size_t f = 0; f < static_cast(frames); ++f) @@ -323,7 +324,7 @@ void Audio::copyBuffersFromCore(const sampleFrame *lmmsBuf, -void Audio::averageWithBuffersFromCore(const sampleFrame *lmmsBuf, +void Audio::averageWithBuffersFromCore(const SampleFrame* lmmsBuf, unsigned channel, fpp_t frames) { for (std::size_t f = 0; f < static_cast(frames); ++f) @@ -335,7 +336,7 @@ void Audio::averageWithBuffersFromCore(const sampleFrame *lmmsBuf, -void Audio::copyBuffersToCore(sampleFrame *lmmsBuf, +void Audio::copyBuffersToCore(SampleFrame* lmmsBuf, unsigned channel, fpp_t frames) const { for (std::size_t f = 0; f < static_cast(frames); ++f) diff --git a/src/core/lv2/Lv2Proc.cpp b/src/core/lv2/Lv2Proc.cpp index 77177a1c0c0..7cd9d3a509b 100644 --- a/src/core/lv2/Lv2Proc.cpp +++ b/src/core/lv2/Lv2Proc.cpp @@ -1,7 +1,7 @@ /* * Lv2Proc.cpp - Lv2 processor class * - * Copyright (c) 2019-2022 Johannes Lorenz + * Copyright (c) 2019-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -27,10 +27,10 @@ #ifdef LMMS_HAVE_LV2 #include -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include @@ -38,6 +38,7 @@ #include "AudioEngine.h" #include "AutomatableModel.h" #include "ComboBoxModel.h" +#include "ConfigManager.h" #include "Engine.h" #include "Lv2Features.h" #include "Lv2Manager.h" @@ -74,21 +75,21 @@ Plugin::Type Lv2Proc::check(const LilvPlugin *plugin, const char* pluginUri = lilv_node_as_uri(lilv_plugin_get_uri(plugin)); //qDebug() << "Checking plugin" << pluginUri << "..."; - // TODO: manage a global blacklist outside of the code + // TODO: manage a global list of blocked plugins outside of the code // for now, this will help // this is only a fix for the meantime - if (!Engine::ignorePluginBlacklist()) + if (!ConfigManager::enableBlockedPlugins()) { - const auto& pluginBlacklist = Lv2Manager::getPluginBlacklist(); - const auto& pluginBlacklist32 = Lv2Manager::getPluginBlacklistBuffersizeLessThan32(); - if(pluginBlacklist.find(pluginUri) != pluginBlacklist.end()) + if( // plugin unstable? + Lv2Manager::pluginIsUnstable(pluginUri) || + // plugins only useful with UI? + (!Lv2Manager::wantUi() && + Lv2Manager::pluginIsOnlyUsefulWithUi(pluginUri)) || + // plugin unstable with 32 or less fpp? + (Engine::audioEngine()->framesPerPeriod() <= 32 && + Lv2Manager::pluginIsUnstableWithBuffersizeLessEqual32(pluginUri)) ) { - issues.emplace_back(PluginIssueType::Blacklisted); - } - else if(Engine::audioEngine()->framesPerPeriod() <= 32 && - pluginBlacklist32.find(pluginUri) != pluginBlacklist32.end()) - { - issues.emplace_back(PluginIssueType::Blacklisted); // currently no special blacklist category + issues.emplace_back(PluginIssueType::Blocked); } } @@ -324,7 +325,7 @@ void Lv2Proc::copyModelsToCore() -void Lv2Proc::copyBuffersFromCore(const sampleFrame *buf, +void Lv2Proc::copyBuffersFromCore(const SampleFrame* buf, unsigned firstChan, unsigned num, fpp_t frames) { @@ -349,7 +350,7 @@ void Lv2Proc::copyBuffersFromCore(const sampleFrame *buf, -void Lv2Proc::copyBuffersToCore(sampleFrame* buf, +void Lv2Proc::copyBuffersToCore(SampleFrame* buf, unsigned firstChan, unsigned num, fpp_t frames) const { @@ -425,12 +426,8 @@ void Lv2Proc::handleMidiInputEvent(const MidiEvent &event, const TimePos &time, AutomatableModel *Lv2Proc::modelAtPort(const QString &uri) { - // unused currently - AutomatableModel *mod; - auto itr = m_connectedModels.find(uri.toUtf8().data()); - if (itr != m_connectedModels.end()) { mod = itr->second; } - else { mod = nullptr; } - return mod; + const auto itr = m_connectedModels.find(uri.toUtf8().data()); + return itr != m_connectedModels.end() ? itr->second : nullptr; } @@ -443,14 +440,15 @@ void Lv2Proc::initPlugin() m_features.createFeatureVectors(); m_instance = lilv_plugin_instantiate(m_plugin, - Engine::audioEngine()->processingSampleRate(), + Engine::audioEngine()->outputSampleRate(), m_features.featurePointers()); if (m_instance) { const auto iface = static_cast( lilv_instance_get_extension_data(m_instance, LV2_WORKER__interface)); - if (iface) { + if (iface) + { m_worker->setHandle(lilv_instance_get_handle(m_instance)); m_worker->setInterface(iface); } @@ -511,7 +509,7 @@ void Lv2Proc::initMOptions() re-initialize, and this code section will be executed again, creating a new option vector. */ - float sampleRate = Engine::audioEngine()->processingSampleRate(); + float sampleRate = Engine::audioEngine()->outputSampleRate(); int32_t blockLength = Engine::audioEngine()->framesPerPeriod(); int32_t sequenceSize = defaultEvbufSize(); @@ -535,7 +533,8 @@ void Lv2Proc::initPluginSpecificFeatures() // worker (if plugin has worker extension) Lv2Manager* mgr = Engine::getLv2Manager(); - if (lilv_plugin_has_extension_data(m_plugin, mgr->uri(LV2_WORKER__interface).get())) { + if (lilv_plugin_has_extension_data(m_plugin, mgr->uri(LV2_WORKER__interface).get())) + { bool threaded = !Engine::audioEngine()->renderOnly(); m_worker.emplace(&m_workLock, threaded); m_features[LV2_WORKER__schedule] = m_worker->feature(); @@ -572,7 +571,7 @@ void Lv2Proc::createPort(std::size_t portNum) { AutoLilvNode node(lilv_port_get_name(m_plugin, lilvPort)); QString dispName = lilv_node_as_string(node.get()); - sample_rate_t sr = Engine::audioEngine()->processingSampleRate(); + sample_rate_t sr = Engine::audioEngine()->outputSampleRate(); if(meta.def() < meta.min(sr) || meta.def() > meta.max(sr)) { qWarning() << "Warning: Plugin" @@ -689,7 +688,8 @@ void Lv2Proc::createPort(std::size_t portNum) AutoLilvNode rszMinimumSize = mgr->uri(LV2_RESIZE_PORT__minimumSize); AutoLilvNodes minSizeV(lilv_port_get_value(m_plugin, lilvPort, rszMinimumSize.get())); LilvNode* minSize = minSizeV ? lilv_nodes_get_first(minSizeV.get()) : nullptr; - if (minSize && lilv_node_is_int(minSize)) { + if (minSize && lilv_node_is_int(minSize)) + { minimumSize = std::max(minimumSize, lilv_node_as_int(minSize)); } } @@ -851,7 +851,8 @@ void Lv2Proc::dumpPort(std::size_t num) { struct DumpPortDetail : public Lv2Ports::ConstVisitor { - void visit(const Lv2Ports::Control& ctrl) override { + void visit(const Lv2Ports::Control& ctrl) override + { qDebug() << " control port"; // output ports may be uninitialized yet, only print inputs if (ctrl.m_flow == Lv2Ports::Flow::Input) @@ -859,7 +860,8 @@ void Lv2Proc::dumpPort(std::size_t num) qDebug() << " value:" << ctrl.m_val; } } - void visit(const Lv2Ports::Audio& audio) override { + void visit(const Lv2Ports::Audio& audio) override + { qDebug() << (audio.isSideChain() ? " audio port (sidechain)" : " audio port"); qDebug() << " buffer size:" << audio.bufferSize(); @@ -875,7 +877,7 @@ void Lv2Proc::dumpPort(std::size_t num) qDebug() << " visualization: " << Lv2Ports::toStr(port.m_vis); if (port.m_type == Lv2Ports::Type::Control || port.m_type == Lv2Ports::Type::Cv) { - sample_rate_t sr = Engine::audioEngine()->processingSampleRate(); + sample_rate_t sr = Engine::audioEngine()->outputSampleRate(); qDebug() << " default:" << port.def(); qDebug() << " min:" << port.min(sr); qDebug() << " max:" << port.max(sr); diff --git a/src/core/lv2/Lv2SubPluginFeatures.cpp b/src/core/lv2/Lv2SubPluginFeatures.cpp index 135da3e2a10..66abe2f2e5a 100644 --- a/src/core/lv2/Lv2SubPluginFeatures.cpp +++ b/src/core/lv2/Lv2SubPluginFeatures.cpp @@ -3,7 +3,7 @@ * Plugin::Descriptor::SubPluginFeatures for * hosting LV2 plugins * - * Copyright (c) 2018-2023 Johannes Lorenz + * Copyright (c) 2018-2024 Johannes Lorenz * * This file is part of LMMS - https://lmms.io * @@ -28,8 +28,10 @@ #ifdef LMMS_HAVE_LV2 +#include #include #include +#include #include "Engine.h" #include "Lv2Basics.h" @@ -39,8 +41,8 @@ namespace lmms { -const LilvPlugin *Lv2SubPluginFeatures::getPlugin( - const Plugin::Descriptor::SubPluginFeatures::Key &k) +const LilvPlugin* Lv2SubPluginFeatures::getPlugin( + const Plugin::Descriptor::SubPluginFeatures::Key& k) { const LilvPlugin* result = Engine::getLv2Manager()-> getPlugin(k.attributes["uri"]); @@ -51,7 +53,7 @@ const LilvPlugin *Lv2SubPluginFeatures::getPlugin( -QString Lv2SubPluginFeatures::pluginName(const LilvPlugin *plug) +QString Lv2SubPluginFeatures::pluginName(const LilvPlugin* plug) { return qStringFromPluginNode(plug, lilv_plugin_get_name); } @@ -67,61 +69,132 @@ Lv2SubPluginFeatures::Lv2SubPluginFeatures(Plugin::Type type) : -void Lv2SubPluginFeatures::fillDescriptionWidget(QWidget *parent, - const Key *k) const +static void addHbox(QWidget* parent, QString left, QString right) { - const LilvPlugin *plug = getPlugin(*k); + if (right.isEmpty()) { return; } - auto label = new QLabel(parent); - label->setText(QWidget::tr("Name: ") + pluginName(plug)); - - auto label2 = new QLabel(parent); - label2->setText(QWidget::tr("URI: ") + - lilv_node_as_uri(lilv_plugin_get_uri(plug))); - - auto maker = new QWidget(parent); - auto l = new QHBoxLayout(maker); + auto container = new QWidget(parent); + auto l = new QHBoxLayout(container); l->setContentsMargins(0, 0, 0, 0); l->setSpacing(0); - auto maker_label = new QLabel(maker); - maker_label->setText(QWidget::tr("Maker: ")); - maker_label->setAlignment(Qt::AlignTop); + auto leftLabel = new QLabel(container); + leftLabel->setText(left); + leftLabel->setAlignment(Qt::AlignTop); - auto maker_content = new QLabel(maker); - maker_content->setText( - qStringFromPluginNode(plug, lilv_plugin_get_author_name)); - maker_content->setWordWrap(true); + auto rightLabel = new QLabel(container); + if (right.startsWith("http") && !right.contains(' ') && !right.contains('\n')) + { + right = QString("%1").arg(right); + rightLabel->setTextInteractionFlags(rightLabel->textInteractionFlags() + | Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); + rightLabel->setTextFormat(Qt::RichText); + rightLabel->setOpenExternalLinks(true); + } + rightLabel->setText(right); + rightLabel->setWordWrap(true); - l->addWidget(maker_label); - l->addWidget(maker_content, 1); + l->addWidget(leftLabel); + l->addWidget(rightLabel, 1); +} - auto copyright = new QWidget(parent); - l = new QHBoxLayout(copyright); - l->setContentsMargins(0, 0, 0, 0); - l->setSpacing(0); - copyright->setMinimumWidth(parent->minimumWidth()); - auto copyright_label = new QLabel(copyright); - copyright_label->setText(QWidget::tr("Copyright: ")); - copyright_label->setAlignment(Qt::AlignTop); - auto copyright_content = new QLabel(copyright); - copyright_content->setText(""); - copyright_content->setWordWrap(true); - l->addWidget(copyright_label); - l->addWidget(copyright_content, 1); - AutoLilvNodes extensions(lilv_plugin_get_extension_data(plug)); - (void)extensions; - // possibly TODO: version, project, plugin type, number of channels +static void addLabel(QWidget* parent, QString left, QString right) +{ + auto lbl = new QLabel(parent); + if (right.isEmpty()) { return; } + if (right.startsWith("http") && !right.contains(' ') && !right.contains('\n')) + { + right = QString("%1").arg(right); + lbl->setTextInteractionFlags(lbl->textInteractionFlags() + | Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); + lbl->setTextFormat(Qt::RichText); + lbl->setOpenExternalLinks(true); + } + lbl->setText(left + right); +} + + + + +AutoLilvNodes pluginGetValues(const LilvPlugin* plug, const char* valueUri) +{ + assert(plug); + AutoLilvNode valueUriNode{Engine::getLv2Manager()->uri(valueUri)}; + return AutoLilvNodes{lilv_plugin_get_value(plug, valueUriNode.get())}; +} + + + + +void Lv2SubPluginFeatures::fillDescriptionWidget(QWidget* parent, + const Key* k) const +{ + const LilvPlugin* plug = getPlugin(*k); + + QString pluginNameAndVersion = "" + pluginName(plug) + ""; + { + AutoLilvNodes minorVersions = pluginGetValues(plug, LILV_NS_LV2 "minorVersion"); + AutoLilvNodes microVersions = pluginGetValues(plug, LILV_NS_LV2 "microVersion"); + if (minorVersions && microVersions) + { + QString min = lilv_node_as_string(lilv_nodes_get_first(minorVersions.get())); + QString mic = lilv_node_as_string(lilv_nodes_get_first(microVersions.get())); + pluginNameAndVersion += QString(" v%1.%2").arg(min).arg(mic); + } + } + + (new QLabel(parent))->setText(pluginNameAndVersion); + { + AutoLilvNodes comments = pluginGetValues(plug, LILV_NS_RDFS "comment"); + if (comments) + { + QString description{lilv_node_as_string(lilv_nodes_get_first(comments.get()))}; + auto descLabel = new QLabel(parent); + descLabel->setText("" + description.trimmed() + ""); + descLabel->setWordWrap(true); + } + } + + addLabel(parent, QObject::tr("URI: "), lilv_node_as_uri(lilv_plugin_get_uri(plug))); + addHbox(parent, QObject::tr("Project: "), + qStringFromPluginNode(plug, lilv_plugin_get_project)); + addHbox(parent, QObject::tr("Maker: "), + qStringFromPluginNode(plug, lilv_plugin_get_author_name)); + { + AutoLilvNodes homepages = pluginGetValues(plug, LILV_NS_DOAP "homepage"); + if (homepages) + { + const char* homepage = lilv_node_as_uri(lilv_nodes_get_first(homepages.get())); + QString homepageStr{homepage}; + addLabel(parent, QObject::tr("Homepage: "), homepageStr); + } + } + { + AutoLilvNodes licenses = pluginGetValues(plug, LILV_NS_DOAP "license"); + addLabel(parent, QObject::tr("License: "), + licenses + ? lilv_node_as_uri(lilv_nodes_get_first(licenses.get())) + : ""); + } + { + const LilvNode* libraryUriNode = lilv_plugin_get_bundle_uri(plug); + const char* libraryUri = lilv_node_as_uri(libraryUriNode); + auto filename = AutoLilvPtr(lilv_file_uri_parse(libraryUri, nullptr)); + if (filename) + { + new QLabel(QObject::tr("File: %1").arg(filename.get()), parent); + } + } } QString Lv2SubPluginFeatures::additionalFileExtensions( - const Plugin::Descriptor::SubPluginFeatures::Key &k) const + const Plugin::Descriptor::SubPluginFeatures::Key& k) const { (void)k; // lv2 only loads .lv2 files @@ -133,7 +206,7 @@ QString Lv2SubPluginFeatures::additionalFileExtensions( QString Lv2SubPluginFeatures::displayName( - const Plugin::Descriptor::SubPluginFeatures::Key &k) const + const Plugin::Descriptor::SubPluginFeatures::Key& k) const { return pluginName(getPlugin(k)); } @@ -142,30 +215,44 @@ QString Lv2SubPluginFeatures::displayName( QString Lv2SubPluginFeatures::description( - const Plugin::Descriptor::SubPluginFeatures::Key &k) const + const Plugin::Descriptor::SubPluginFeatures::Key& k) const { - (void)k; - return QString::fromUtf8("description not implemented yet"); // TODO + auto mgr = Engine::getLv2Manager(); + const LilvPlugin* plug = mgr->getPlugin(k.attributes["uri"]); + if (plug) + { + QString result; + AutoLilvNode rdfs_comment{mgr->uri(LILV_NS_RDFS "comment")}; + AutoLilvNodes comments{lilv_plugin_get_value(plug, rdfs_comment.get())}; + if (comments) + { + result += lilv_node_as_string(lilv_nodes_get_first(comments.get())); + result += "\n\n"; + } + result += lilv_node_as_uri(lilv_plugin_get_uri(plug)); + return result.trimmed(); + } + return QObject::tr("failed to load description"); } -const PixmapLoader *Lv2SubPluginFeatures::logo( - const Plugin::Descriptor::SubPluginFeatures::Key &k) const +const PixmapLoader* Lv2SubPluginFeatures::logo( + const Plugin::Descriptor::SubPluginFeatures::Key& k) const { - (void)k; // TODO - return nullptr; + (void)k; + return nullptr; // Lv2 currently does not support this } -void Lv2SubPluginFeatures::listSubPluginKeys(const Plugin::Descriptor *desc, - KeyList &kl) const +void Lv2SubPluginFeatures::listSubPluginKeys(const Plugin::Descriptor* desc, + KeyList& kl) const { - Lv2Manager *lv2Mgr = Engine::getLv2Manager(); - for (const auto &uriInfoPair : *lv2Mgr) + Lv2Manager* lv2Mgr = Engine::getLv2Manager(); + for (const auto& uriInfoPair : *lv2Mgr) { if (uriInfoPair.second.type() == m_type && uriInfoPair.second.isValid()) { diff --git a/src/core/lv2/Lv2UridCache.cpp b/src/core/lv2/Lv2UridCache.cpp index 746878afb96..7d3a14c9349 100644 --- a/src/core/lv2/Lv2UridCache.cpp +++ b/src/core/lv2/Lv2UridCache.cpp @@ -26,10 +26,10 @@ #ifdef LMMS_HAVE_LV2 -#include -#include -#include -#include +#include +#include +#include +#include #include #include "Lv2UridMap.h" diff --git a/src/core/lv2/Lv2UridMap.cpp b/src/core/lv2/Lv2UridMap.cpp index ecc28693fef..4aa9d11b41f 100644 --- a/src/core/lv2/Lv2UridMap.cpp +++ b/src/core/lv2/Lv2UridMap.cpp @@ -76,12 +76,12 @@ LV2_URID UridMap::map(const char *uri) if (itr == m_map.end()) { // 1 is the first free URID - std::size_t index = 1u + m_unMap.size(); + const auto index = static_cast(1u + m_unMap.size()); auto pr = m_map.emplace(std::move(uriStr), index); if (pr.second) { m_unMap.emplace_back(pr.first->first.c_str()); - result = static_cast(index); + result = index; } } else { result = itr->second; } diff --git a/src/core/main.cpp b/src/core/main.cpp index 25a6ab9c53e..3e6c2c85f06 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -43,10 +43,6 @@ #include #endif -#ifdef LMMS_HAVE_SCHED_H -#include "sched.h" -#endif - #ifdef LMMS_HAVE_PROCESS_H #include #endif @@ -55,6 +51,10 @@ #include #endif +#ifdef LMMS_HAVE_SYS_PRCTL_H +#include +#endif + #include #include "MainApplication.h" @@ -210,7 +210,6 @@ void printHelp() " -p, --profile Dump profiling information to file \n" " -s, --samplerate Specify output samplerate in Hz\n" " Range: 44100 (default) to 192000\n" - " -x, --oversampling Specify oversampling\n" " Possible values: 1, 2, 4, 8\n" " Default: 2\n\n", LMMS_VERSION, LMMS_PROJECT_COPYRIGHT ); @@ -254,6 +253,57 @@ int main( int argc, char * * argv ) { using namespace lmms; + bool coreOnly = false; + bool fullscreen = true; + bool exitAfterImport = false; + bool allowRoot = false; + bool renderLoop = false; + bool renderTracks = false; + QString fileToLoad, fileToImport, renderOut, profilerOutputFile, configFile; + + // first of two command-line parsing stages + for (int i = 1; i < argc; ++i) + { + QString arg = argv[i]; + + if (arg == "--help" || arg == "-h") + { + printHelp(); + return EXIT_SUCCESS; + } + else if (arg == "--version" || arg == "-v") + { + printVersion(argv[0]); + return EXIT_SUCCESS; + } + else if (arg == "render" || arg == "--render" || arg == "-r" ) + { + coreOnly = true; + } + else if (arg == "rendertracks" || arg == "--rendertracks") + { + coreOnly = true; + renderTracks = true; + } + else if (arg == "--allowroot") + { + allowRoot = true; + } + else if (arg == "--geometry" || arg == "-geometry") + { + if (arg == "--geometry") + { + // Delete the first "-" so Qt recognize the option + strcpy(argv[i], "-geometry"); + } + // option -geometry is filtered by Qt later, + // so we need to check its presence now to + // determine, if the application should run in + // fullscreen mode (default, no -geometry given). + fullscreen = false; + } + } + #ifdef LMMS_DEBUG_FPE // Enable exceptions for certain floating point results // FE_UNDERFLOW is disabled for the time being @@ -294,6 +344,15 @@ int main( int argc, char * * argv ) qInstallMessageHandler(consoleMessageHandler); #endif +#if defined(LMMS_HAVE_SYS_PRCTL_H) && defined(PR_SET_CHILD_SUBREAPER) + // Set the "child subreaper" attribute so that plugin child processes remain as lmms' + // children even when some wrapper process exits, as it may happen with wine + if (prctl(PR_SET_CHILD_SUBREAPER, 1)) + { + perror("prctl(PR_SET_CHILD_SUBREAPER)"); + } +#endif + // initialize memory managers NotePlayHandleManager::init(); @@ -302,49 +361,6 @@ int main( int argc, char * * argv ) disable_denormals(); - bool coreOnly = false; - bool fullscreen = true; - bool exitAfterImport = false; - bool allowRoot = false; - bool renderLoop = false; - bool renderTracks = false; - QString fileToLoad, fileToImport, renderOut, profilerOutputFile, configFile; - - // first of two command-line parsing stages - for( int i = 1; i < argc; ++i ) - { - QString arg = argv[i]; - - if( arg == "--help" || arg == "-h" || - arg == "--version" || arg == "-v" || - arg == "render" || arg == "--render" || arg == "-r" ) - { - coreOnly = true; - } - else if( arg == "rendertracks" || arg == "--rendertracks" ) - { - coreOnly = true; - renderTracks = true; - } - else if( arg == "--allowroot" ) - { - allowRoot = true; - } - else if( arg == "--geometry" || arg == "-geometry") - { - if( arg == "--geometry" ) - { - // Delete the first "-" so Qt recognize the option - strcpy(argv[i], "-geometry"); - } - // option -geometry is filtered by Qt later, - // so we need to check its presence now to - // determine, if the application should run in - // fullscreen mode (default, no -geometry given). - fullscreen = false; - } - } - #if !defined(LMMS_BUILD_WIN32) && !defined(LMMS_BUILD_HAIKU) if ( ( getuid() == 0 || geteuid() == 0 ) && !allowRoot ) { @@ -356,14 +372,12 @@ int main( int argc, char * * argv ) // don't let OS steal the menu bar. FIXME: only effective on Qt4 QCoreApplication::setAttribute( Qt::AA_DontUseNativeMenuBar ); #endif -#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#endif QCoreApplication * app = coreOnly ? new QCoreApplication( argc, argv ) : new gui::MainApplication(argc, argv); - AudioEngine::qualitySettings qs( AudioEngine::qualitySettings::Mode::HighQuality ); + AudioEngine::qualitySettings qs(AudioEngine::qualitySettings::Interpolation::Linear); OutputSettings os( 44100, OutputSettings::BitRateSettings(160, false), OutputSettings::BitDepth::Depth16Bit, OutputSettings::StereoMode::JointStereo ); ProjectRenderer::ExportFileFormat eff = ProjectRenderer::ExportFileFormat::Wave; @@ -372,17 +386,7 @@ int main( int argc, char * * argv ) { QString arg = argv[i]; - if( arg == "--version" || arg == "-v" ) - { - printVersion( argv[0] ); - return EXIT_SUCCESS; - } - else if( arg == "--help" || arg == "-h" ) - { - printHelp(); - return EXIT_SUCCESS; - } - else if( arg == "upgrade" || arg == "--upgrade" || arg == "-u") + if (arg == "upgrade" || arg == "--upgrade" || arg == "-u") { ++i; @@ -648,36 +652,6 @@ int main( int argc, char * * argv ) return usageError( QString( "Invalid interpolation method %1" ).arg( argv[i] ) ); } } - else if( arg == "--oversampling" || arg == "-x" ) - { - ++i; - - if( i == argc ) - { - return usageError( "No oversampling specified" ); - } - - - int o = QString( argv[i] ).toUInt(); - - switch( o ) - { - case 1: - qs.oversampling = AudioEngine::qualitySettings::Oversampling::None; - break; - case 2: - qs.oversampling = AudioEngine::qualitySettings::Oversampling::X2; - break; - case 4: - qs.oversampling = AudioEngine::qualitySettings::Oversampling::X4; - break; - case 8: - qs.oversampling = AudioEngine::qualitySettings::Oversampling::X8; - break; - default: - return usageError( QString( "Invalid oversampling %1" ).arg( argv[i] ) ); - } - } else if( arg == "--import" ) { ++i; @@ -763,29 +737,6 @@ int main( int argc, char * * argv ) // override it with bundled/custom one, if exists loadTranslation(QString("qt_") + pos, ConfigManager::inst()->localeDir()); - - // try to set realtime priority -#if defined(LMMS_BUILD_LINUX) || defined(LMMS_BUILD_FREEBSD) -#ifdef LMMS_HAVE_SCHED_H -#ifndef __OpenBSD__ - struct sched_param sparam; - sparam.sched_priority = ( sched_get_priority_max( SCHED_FIFO ) + - sched_get_priority_min( SCHED_FIFO ) ) / 2; - if( sched_setscheduler( 0, SCHED_FIFO, &sparam ) == -1 ) - { - printf( "Notice: could not set realtime priority.\n" ); - } -#endif -#endif // LMMS_HAVE_SCHED_H -#endif - -#ifdef LMMS_BUILD_WIN32 - if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) ) - { - printf( "Notice: could not set high priority.\n" ); - } -#endif - #if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE struct sigaction sa; sa.sa_handler = SIG_IGN; @@ -906,19 +857,13 @@ int main( int argc, char * * argv ) mb.setWindowIcon( embed::getIconPixmap( "icon_small" ) ); mb.setWindowFlags( Qt::WindowCloseButtonHint ); - QPushButton * recover; - QPushButton * discard; - QPushButton * exit; - // setting all buttons to the same roles allows us // to have a custom layout - discard = mb.addButton( MainWindow::tr( "Discard" ), - QMessageBox::AcceptRole ); - recover = mb.addButton( MainWindow::tr( "Recover" ), - QMessageBox::AcceptRole ); + auto discard = mb.addButton(MainWindow::tr("Discard"), QMessageBox::AcceptRole); + auto recover = mb.addButton(MainWindow::tr("Recover"), QMessageBox::AcceptRole); // have a hidden exit button - exit = mb.addButton( "", QMessageBox::RejectRole); + auto exit = mb.addButton("", QMessageBox::RejectRole); exit->setVisible(false); // set icons diff --git a/src/core/midi/MidiAlsaRaw.cpp b/src/core/midi/MidiAlsaRaw.cpp index 23364fc01ef..f091b789fde 100644 --- a/src/core/midi/MidiAlsaRaw.cpp +++ b/src/core/midi/MidiAlsaRaw.cpp @@ -39,10 +39,7 @@ MidiAlsaRaw::MidiAlsaRaw() : m_outputp( &m_output ), m_quit( false ) { - int err; - if( ( err = snd_rawmidi_open( m_inputp, m_outputp, - probeDevice().toLatin1().constData(), - 0 ) ) < 0 ) + if (int err = snd_rawmidi_open(m_inputp, m_outputp, probeDevice().toLatin1().constData(), 0); err < 0) { printf( "cannot open MIDI-device: %s\n", snd_strerror( err ) ); return; @@ -111,29 +108,27 @@ void MidiAlsaRaw::run() { msleep( 5 ); // must do that, otherwise this thread takes // too much CPU-time, even with LowPriority... - int err = poll( m_pfds, m_npfds, 10000 ); - if( err < 0 && errno == EINTR ) + if (int err = poll(m_pfds, m_npfds, 10000); err < 0 && errno == EINTR) { printf( "MidiAlsaRaw::run(): Got EINTR while " "polling. Will stop polling MIDI-events from " "MIDI-port.\n" ); break; } - if( err < 0 ) + else if (err < 0) { printf( "poll failed: %s\nWill stop polling " "MIDI-events from MIDI-port.\n", strerror( errno ) ); break; } - if( err == 0 ) + else if (err == 0) { //printf( "there seems to be no active MIDI-device %d\n", ++cnt ); continue; } - unsigned short revents; - if( ( err = snd_rawmidi_poll_descriptors_revents( - m_input, m_pfds, m_npfds, &revents ) ) < 0 ) + unsigned short revents = 0; + if (int err = snd_rawmidi_poll_descriptors_revents(m_input, m_pfds, m_npfds, &revents); err < 0) { printf( "cannot get poll events: %s\nWill stop polling " "MIDI-events from MIDI-port.\n", @@ -149,25 +144,19 @@ void MidiAlsaRaw::run() { continue; } - err = snd_rawmidi_read(m_input, buf.data(), buf.size()); - if( err == -EAGAIN ) - { - continue; - } - if( err < 0 ) + + if (int err = snd_rawmidi_read(m_input, buf.data(), buf.size()); err == -EAGAIN) { continue; } + else if (err < 0) { printf( "cannot read from port \"%s\": %s\nWill stop " "polling MIDI-events from MIDI-port.\n", /*port_name*/"default", snd_strerror( err ) ); break; } - if( err == 0 ) - { - continue; - } - for( int i = 0; i < err; ++i ) + else if (err == 0) { continue; } + else { - parseData( buf[i] ); + for (int i = 0; i < err; ++i) { parseData(buf[i]); } } } diff --git a/src/core/midi/MidiAlsaSeq.cpp b/src/core/midi/MidiAlsaSeq.cpp index 0b3bab8195a..e0b8b486efc 100644 --- a/src/core/midi/MidiAlsaSeq.cpp +++ b/src/core/midi/MidiAlsaSeq.cpp @@ -78,10 +78,7 @@ MidiAlsaSeq::MidiAlsaSeq() : m_quit( false ), m_portListUpdateTimer( this ) { - int err; - if( ( err = snd_seq_open( &m_seqHandle, - probeDevice().toLatin1().constData(), - SND_SEQ_OPEN_DUPLEX, 0 ) ) < 0 ) + if (int err = snd_seq_open(&m_seqHandle, probeDevice().toLatin1().constData(), SND_SEQ_OPEN_DUPLEX, 0); err < 0) { fprintf( stderr, "cannot open sequencer: %s\n", snd_strerror( err ) ); diff --git a/src/core/midi/MidiClient.cpp b/src/core/midi/MidiClient.cpp index 030384c5e1e..a4e6ff097b2 100644 --- a/src/core/midi/MidiClient.cpp +++ b/src/core/midi/MidiClient.cpp @@ -24,8 +24,10 @@ */ #include "MidiClient.h" -#include "MidiPort.h" +#include + +#include "MidiPort.h" namespace lmms { @@ -309,4 +311,4 @@ int MidiClientRaw::eventLength( const unsigned char event ) return 1; } -} // namespace lmms \ No newline at end of file +} // namespace lmms diff --git a/src/core/midi/MidiController.cpp b/src/core/midi/MidiController.cpp index 0ae76d352c8..112d9d974b4 100644 --- a/src/core/midi/MidiController.cpp +++ b/src/core/midi/MidiController.cpp @@ -74,11 +74,11 @@ void MidiController::updateName() void MidiController::processInEvent(const MidiEvent& event, const TimePos& time, f_cnt_t offset) { - unsigned char controllerNum; switch(event.type()) { case MidiControlChange: - controllerNum = event.controllerNumber(); + { + unsigned char controllerNum = event.controllerNumber(); if (m_midiPort.inputController() == controllerNum && (m_midiPort.inputChannel() == event.channel() + 1 || m_midiPort.inputChannel() == 0)) @@ -89,7 +89,7 @@ void MidiController::processInEvent(const MidiEvent& event, const TimePos& time, emit valueChanged(); } break; - + } default: // Don't care - maybe add special cases for pitch and mod later break; diff --git a/src/core/midi/MidiJack.cpp b/src/core/midi/MidiJack.cpp index 145a72eccfe..29e7e27ec70 100644 --- a/src/core/midi/MidiJack.cpp +++ b/src/core/midi/MidiJack.cpp @@ -179,7 +179,6 @@ QString MidiJack::probeDevice() // we read data from jack void MidiJack::JackMidiRead(jack_nframes_t nframes) { - unsigned int i,b; void* port_buf = jack_port_get_buffer(m_input_port, nframes); jack_midi_event_t in_event; jack_nframes_t event_index = 0; @@ -188,13 +187,13 @@ void MidiJack::JackMidiRead(jack_nframes_t nframes) int rval = jack_midi_event_get(&in_event, port_buf, 0); if (rval == 0 /* 0 = success */) { - for(i=0; i 0; n--, p++) + for (char* p = buf; n > 0; n--, p++) { parseData( *p ); } diff --git a/src/gui/AudioAlsaSetupWidget.cpp b/src/gui/AudioAlsaSetupWidget.cpp index 7db822b4be8..43872a12fb8 100644 --- a/src/gui/AudioAlsaSetupWidget.cpp +++ b/src/gui/AudioAlsaSetupWidget.cpp @@ -31,7 +31,6 @@ #include "ConfigManager.h" #include "LcdSpinBox.h" -#include "gui_templates.h" namespace lmms::gui { @@ -71,7 +70,7 @@ AudioAlsaSetupWidget::AudioAlsaSetupWidget( QWidget * _parent ) : form->addRow(tr("Device"), m_deviceComboBox); auto m = new LcdSpinBoxModel(/* this */); - m->setRange( DEFAULT_CHANNELS, SURROUND_CHANNELS ); + m->setRange(DEFAULT_CHANNELS, DEFAULT_CHANNELS); m->setStep( 2 ); m->setValue( ConfigManager::inst()->value( "audioalsa", "channels" ).toInt() ); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index e2342b46548..4195ec58c1a 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -14,7 +14,6 @@ SET(LMMS_SRCS gui/EffectView.cpp gui/embed.cpp gui/FileBrowser.cpp - gui/FileBrowserSearcher.cpp gui/GuiApplication.cpp gui/LadspaControlView.cpp gui/LfoControllerDialog.cpp @@ -62,12 +61,14 @@ SET(LMMS_SRCS gui/editors/TrackContainerView.cpp gui/instrument/EnvelopeAndLfoView.cpp + gui/instrument/EnvelopeGraph.cpp gui/instrument/InstrumentFunctionViews.cpp gui/instrument/InstrumentMidiIOView.cpp gui/instrument/InstrumentTuningView.cpp gui/instrument/InstrumentSoundShapingView.cpp gui/instrument/InstrumentTrackWindow.cpp gui/instrument/InstrumentView.cpp + gui/instrument/LfoGraph.cpp gui/instrument/PianoView.cpp gui/menus/MidiPortMenu.cpp @@ -116,6 +117,7 @@ SET(LMMS_SRCS gui/widgets/MixerChannelLcdSpinBox.cpp gui/widgets/NStateButton.cpp gui/widgets/Oscilloscope.cpp + gui/widgets/PeakIndicator.cpp gui/widgets/PixmapButton.cpp gui/widgets/SimpleTextFloat.cpp gui/widgets/TabBar.cpp @@ -129,10 +131,3 @@ SET(LMMS_SRCS PARENT_SCOPE ) -set(LMMS_UIS - ${LMMS_UIS} - gui/modals/about_dialog.ui - gui/modals/export_project.ui - - PARENT_SCOPE -) diff --git a/src/gui/ControlLayout.cpp b/src/gui/ControlLayout.cpp index 5e9a211014b..75133c8e3d8 100644 --- a/src/gui/ControlLayout.cpp +++ b/src/gui/ControlLayout.cpp @@ -101,8 +101,7 @@ ControlLayout::ControlLayout(QWidget *parent, int margin, int hSpacing, int vSpa ControlLayout::~ControlLayout() { - QLayoutItem *item; - while ((item = takeAt(0))) { delete item; } + while (auto item = takeAt(0)) { delete item; } } void ControlLayout::onTextChanged(const QString&) diff --git a/src/gui/ControllerRackView.cpp b/src/gui/ControllerRackView.cpp index 54c325dc660..e7d2efebd06 100644 --- a/src/gui/ControllerRackView.cpp +++ b/src/gui/ControllerRackView.cpp @@ -24,6 +24,7 @@ */ #include +#include #include #include #include @@ -68,8 +69,8 @@ ControllerRackView::ControllerRackView() : this, SLOT(addController())); Song * song = Engine::getSong(); - connect( song, SIGNAL(controllerAdded(lmms::Controller*)), SLOT(onControllerAdded(lmms::Controller*))); - connect( song, SIGNAL(controllerRemoved(lmms::Controller*)), SLOT(onControllerRemoved(lmms::Controller*))); + connect(song, &Song::controllerAdded, this, qOverload(&ControllerRackView::addController)); + connect(song, &Song::controllerRemoved, this, &ControllerRackView::removeController); auto layout = new QVBoxLayout(); layout->addWidget( m_scrollArea ); @@ -132,17 +133,51 @@ void ControllerRackView::deleteController( ControllerView * _view ) song->removeController( c ); } +void ControllerRackView::moveUp(ControllerView* view) +{ + if (view == m_controllerViews.first()) { return; } + + const auto storedView = std::find(m_controllerViews.begin(), m_controllerViews.end(), view); + assert(storedView != m_controllerViews.end()); + + const auto index = std::distance(m_controllerViews.begin(), storedView); + + std::swap(m_controllerViews[index - 1], m_controllerViews[index]); + m_scrollAreaLayout->removeWidget(view); + m_scrollAreaLayout->insertWidget(index - 1, view); +} +void ControllerRackView::moveDown(ControllerView* view) +{ + if (view == m_controllerViews.last()) { return; } + const auto storedView = std::find(m_controllerViews.begin(), m_controllerViews.end(), view); + assert(storedView != m_controllerViews.end()); + moveUp(*std::next(storedView)); +} -void ControllerRackView::onControllerAdded( Controller * controller ) +void ControllerRackView::addController(Controller* controller) { QWidget * scrollAreaWidget = m_scrollArea->widget(); auto controllerView = new ControllerView(controller, scrollAreaWidget); - connect( controllerView, SIGNAL(deleteController(lmms::gui::ControllerView*)), - this, SLOT(deleteController(lmms::gui::ControllerView*)), Qt::QueuedConnection ); + connect(controllerView, &ControllerView::movedUp, this, &ControllerRackView::moveUp); + connect(controllerView, &ControllerView::movedDown, this, &ControllerRackView::moveDown); + connect(controllerView, &ControllerView::removedController, this, &ControllerRackView::deleteController, Qt::QueuedConnection); + + auto moveUpAction = new QAction(controllerView); + moveUpAction->setShortcut(Qt::Key_Up | Qt::AltModifier); + moveUpAction->setShortcutContext(Qt::WidgetShortcut); + connect(moveUpAction, &QAction::triggered, controllerView, &ControllerView::moveUp); + controllerView->addAction(moveUpAction); + + auto moveDownAction = new QAction(controllerView); + moveDownAction->setShortcut(Qt::Key_Down | Qt::AltModifier); + moveDownAction->setShortcutContext(Qt::WidgetShortcut); + connect(moveDownAction, &QAction::triggered, controllerView, &ControllerView::moveDown); + controllerView->addAction(moveDownAction); + m_controllerViews.append( controllerView ); m_scrollAreaLayout->insertWidget( m_nextIndex, controllerView ); @@ -153,7 +188,7 @@ void ControllerRackView::onControllerAdded( Controller * controller ) -void ControllerRackView::onControllerRemoved( Controller * removedController ) +void ControllerRackView::removeController(Controller* removedController) { ControllerView * viewOfRemovedController = 0; diff --git a/src/gui/ControllerView.cpp b/src/gui/ControllerView.cpp index d32e8d49c09..7f7c4729c67 100644 --- a/src/gui/ControllerView.cpp +++ b/src/gui/ControllerView.cpp @@ -53,6 +53,7 @@ ControllerView::ControllerView( Controller * _model, QWidget * _parent ) : { this->setFrameStyle( QFrame::StyledPanel ); this->setFrameShadow( QFrame::Raised ); + setFocusPolicy(Qt::StrongFocus); auto vBoxLayout = new QVBoxLayout(this); @@ -132,11 +133,11 @@ void ControllerView::closeControls() m_show = true; } +void ControllerView::moveUp() { emit movedUp(this); } -void ControllerView::deleteController() -{ - emit( deleteController( this ) ); -} +void ControllerView::moveDown() { emit movedDown(this); } + +void ControllerView::removeController() { emit removedController(this); } void ControllerView::renameController() { @@ -173,10 +174,13 @@ void ControllerView::modelChanged() void ControllerView::contextMenuEvent( QContextMenuEvent * ) { - QPointer contextMenu = new CaptionMenu( model()->displayName(), this ); - contextMenu->addAction( embed::getIconPixmap( "cancel" ), - tr( "&Remove this controller" ), - this, SLOT(deleteController())); + Controller* c = castModel(); + QPointer contextMenu = new CaptionMenu(c->name(), this); + contextMenu->addAction(embed::getIconPixmap("arp_up"), tr("Move &up"), this, &ControllerView::moveUp); + contextMenu->addAction(embed::getIconPixmap("arp_down"), tr("Move &down"), this, &ControllerView::moveDown); + contextMenu->addSeparator(); + contextMenu->addAction( + embed::getIconPixmap("cancel"), tr("&Remove this controller"), this, &ControllerView::removeController); contextMenu->addAction( tr("Re&name this controller"), this, SLOT(renameController())); contextMenu->addSeparator(); contextMenu->exec( QCursor::pos() ); diff --git a/src/gui/EffectRackView.cpp b/src/gui/EffectRackView.cpp index aa790d74dfc..eb8c6c43ea4 100644 --- a/src/gui/EffectRackView.cpp +++ b/src/gui/EffectRackView.cpp @@ -24,6 +24,7 @@ */ #include +#include #include #include #include @@ -170,13 +171,22 @@ void EffectRackView::update() if( i >= m_effectViews.size() ) { auto view = new EffectView(effect, w); - connect( view, SIGNAL(moveUp(lmms::gui::EffectView*)), - this, SLOT(moveUp(lmms::gui::EffectView*))); - connect( view, SIGNAL(moveDown(lmms::gui::EffectView*)), - this, SLOT(moveDown(lmms::gui::EffectView*))); - connect( view, SIGNAL(deletePlugin(lmms::gui::EffectView*)), - this, SLOT(deletePlugin(lmms::gui::EffectView*)), - Qt::QueuedConnection ); + connect(view, &EffectView::movedUp, this, &EffectRackView::moveUp); + connect(view, &EffectView::movedDown, this, &EffectRackView::moveDown); + connect(view, &EffectView::deletedPlugin, this, &EffectRackView::deletePlugin, Qt::QueuedConnection); + + QAction* moveUpAction = new QAction(view); + moveUpAction->setShortcut(Qt::Key_Up | Qt::AltModifier); + moveUpAction->setShortcutContext(Qt::WidgetShortcut); + connect(moveUpAction, &QAction::triggered, view, &EffectView::moveUp); + view->addAction(moveUpAction); + + QAction* moveDownAction = new QAction(view); + moveDownAction->setShortcut(Qt::Key_Down | Qt::AltModifier); + moveDownAction->setShortcutContext(Qt::WidgetShortcut); + connect(moveDownAction, &QAction::triggered, view, &EffectView::moveDown); + view->addAction(moveDownAction); + view->show(); m_effectViews.append( view ); if( i < view_map.size() ) @@ -263,4 +273,13 @@ void EffectRackView::modelChanged() +QSize EffectRackView::sizeHint() const +{ + // Use the formula from InstrumentTrackWindow.cpp + return QSize{EffectRackView::DEFAULT_WIDTH, 254 /* INSTRUMENT_HEIGHT */ - 4 - 1}; +} + + + + } // namespace lmms::gui diff --git a/src/gui/EffectView.cpp b/src/gui/EffectView.cpp index 7f7f9ee9df5..6f2b984c32a 100644 --- a/src/gui/EffectView.cpp +++ b/src/gui/EffectView.cpp @@ -53,6 +53,7 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : m_dragging(false) { setFixedSize(EffectView::DEFAULT_WIDTH, EffectView::DEFAULT_HEIGHT); + setFocusPolicy(Qt::StrongFocus); // Disable effects that are of type "DummyEffect" bool isEnabled = !dynamic_cast( effect() ); @@ -90,7 +91,7 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) : { auto ctls_btn = new QPushButton(tr("Controls"), this); QFont f = ctls_btn->font(); - ctls_btn->setFont( pointSize<8>( f ) ); + ctls_btn->setFont(adjustedToPixelSize(f, 10)); ctls_btn->setGeometry( 150, 14, 50, 20 ); connect( ctls_btn, SIGNAL(clicked()), this, SLOT(editControls())); @@ -162,7 +163,7 @@ void EffectView::editControls() void EffectView::moveUp() { - emit moveUp( this ); + emit movedUp(this); } @@ -170,14 +171,14 @@ void EffectView::moveUp() void EffectView::moveDown() { - emit moveDown( this ); + emit movedDown(this); } void EffectView::deletePlugin() { - emit deletePlugin( this ); + emit deletedPlugin(this); } @@ -257,7 +258,7 @@ void EffectView::paintEvent( QPaintEvent * ) QPainter p( this ); p.drawPixmap( 0, 0, m_bg ); - QFont f = pointSizeF( font(), 7.5f ); + QFont f = adjustedToPixelSize(font(), 10); f.setBold( true ); p.setFont( f ); diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 89201cb044e..e5168fac8bb 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -31,23 +31,22 @@ #include #include #include -#include -#include #include #include +#include #include +#include #include #include - +#include #include -#include "FileBrowser.h" -#include "FileBrowserSearcher.h" #include "AudioEngine.h" #include "ConfigManager.h" #include "DataFile.h" -#include "embed.h" #include "Engine.h" +#include "FileBrowser.h" +#include "FileSearch.h" #include "GuiApplication.h" #include "ImportFilter.h" #include "Instrument.h" @@ -65,6 +64,8 @@ #include "Song.h" #include "StringPairDrag.h" #include "TextFloat.h" +#include "ThreadPool.h" +#include "embed.h" namespace lmms::gui { @@ -77,31 +78,6 @@ enum TreeWidgetItemTypes } ; - -void FileBrowser::addContentCheckBox() -{ - auto filterWidget = new QWidget(contentParent()); - filterWidget->setFixedHeight(15); - auto filterWidgetLayout = new QHBoxLayout(filterWidget); - filterWidgetLayout->setContentsMargins(0, 0, 0, 0); - filterWidgetLayout->setSpacing(0); - - auto configCheckBox = [this, &filterWidgetLayout](QCheckBox* box) - { - box->setCheckState(Qt::Checked); - connect(box, SIGNAL(stateChanged(int)), this, SLOT(reloadTree())); - filterWidgetLayout->addWidget(box); - }; - - m_showUserContent = new QCheckBox(tr("User content")); - configCheckBox(m_showUserContent); - m_showFactoryContent = new QCheckBox(tr("Factory content")); - configCheckBox(m_showFactoryContent); - - addContentWidget(filterWidget); -}; - - FileBrowser::FileBrowser(const QString & directories, const QString & filter, const QString & title, const QPixmap & pm, QWidget * parent, bool dirs_as_items, @@ -116,10 +92,7 @@ FileBrowser::FileBrowser(const QString & directories, const QString & filter, { setWindowTitle( tr( "Browser" ) ); - if (!userDir.isEmpty() && !factoryDir.isEmpty()) - { - addContentCheckBox(); - } + addContentCheckBox(); auto searchWidget = new QWidget(contentParent()); searchWidget->setFixedHeight( 24 ); @@ -152,10 +125,6 @@ FileBrowser::FileBrowser(const QString & directories, const QString & filter, m_searchTreeWidget->hide(); addContentWidget(m_searchTreeWidget); - auto searchTimer = new QTimer(this); - connect(searchTimer, &QTimer::timeout, this, &FileBrowser::buildSearchTree); - searchTimer->start(FileBrowserSearcher::MillisecondsPerMatch); - m_searchIndicator = new QProgressBar(this); m_searchIndicator->setMinimum(0); m_searchIndicator->setMaximum(100); @@ -171,6 +140,54 @@ FileBrowser::FileBrowser(const QString & directories, const QString & filter, show(); } +void FileBrowser::addContentCheckBox() +{ + // user dir and factory dir checkboxes will display individually depending on whether they are empty. + const bool user_checkbox = !m_userDir.isEmpty(); + const bool factory = !m_factoryDir.isEmpty(); + + auto filterWidget = new QWidget(contentParent()); + + outerLayout = new QBoxLayout(QBoxLayout::Direction::TopToBottom, filterWidget); + outerLayout->setSpacing(0); + + if (user_checkbox || factory){ + filterWidgetLayout = new QBoxLayout(QBoxLayout::Direction::LeftToRight); + filterWidgetLayout->setContentsMargins(0, 0, 0, 0); + filterWidgetLayout->setSpacing(0); + + outerLayout->addLayout(filterWidgetLayout); + } + + hiddenWidgetLayout = new QBoxLayout(QBoxLayout::Direction::LeftToRight); + hiddenWidgetLayout->setContentsMargins(0, 0, 0, 0); + hiddenWidgetLayout->setSpacing(0); + + outerLayout->addLayout(hiddenWidgetLayout); + + auto configCheckBox = [this](QBoxLayout* boxLayout, QCheckBox* box, Qt::CheckState checkState) + { + box->setCheckState(checkState); + connect(box, SIGNAL(stateChanged(int)), this, SLOT(reloadTree())); + boxLayout->addWidget(box); + }; + + if (user_checkbox) { + m_showUserContent = new QCheckBox(tr("User content")); + configCheckBox(filterWidgetLayout, m_showUserContent, Qt::Checked); + } + + if (factory) { + m_showFactoryContent = new QCheckBox(tr("Factory content")); + configCheckBox(filterWidgetLayout, m_showFactoryContent, Qt::Checked); + } + + m_showHiddenContent = new QCheckBox(tr("Hidden content")); + configCheckBox(hiddenWidgetLayout, m_showHiddenContent, Qt::Unchecked); + + addContentWidget(filterWidget); +} + void FileBrowser::saveDirectoriesStates() { m_savedExpandedDirs = m_fileBrowserTreeWidget->expandedDirs(); @@ -181,20 +198,10 @@ void FileBrowser::restoreDirectoriesStates() expandItems(m_savedExpandedDirs); } -void FileBrowser::buildSearchTree() +void FileBrowser::foundSearchMatch(FileSearch* search, const QString& match) { - if (!m_currentSearch) { return; } - - const auto match = m_currentSearch->match(); - using State = FileBrowserSearcher::SearchFuture::State; - if ((m_currentSearch->state() == State::Completed && match.isEmpty()) - || m_currentSearch->state() == State::Cancelled) - { - m_currentSearch = nullptr; - m_searchIndicator->setMaximum(100); - return; - } - else if (match.isEmpty()) { return; } + assert(search != nullptr); + if (m_currentSearch.get() != search) { return; } auto basePath = QString{}; for (const auto& path : m_directories.split('*')) @@ -258,13 +265,22 @@ void FileBrowser::buildSearchTree() } } - +void FileBrowser::searchCompleted(FileSearch* search) +{ + assert(search != nullptr); + if (m_currentSearch.get() != search) { return; } + + m_currentSearch.reset(); + m_searchIndicator->setMaximum(100); +} + void FileBrowser::onSearch(const QString& filter) { + if (m_currentSearch) { m_currentSearch->cancel(); } + if (filter.isEmpty()) { - toggleSearch(false); - FileBrowserSearcher::instance()->cancel(); + displaySearch(false); return; } @@ -274,14 +290,21 @@ void FileBrowser::onSearch(const QString& filter) if (directories.isEmpty()) { return; } m_searchTreeWidget->clear(); - toggleSearch(true); + displaySearch(true); auto browserExtensions = m_filter; const auto searchExtensions = browserExtensions.remove("*.").split(' '); - m_currentSearch = FileBrowserSearcher::instance()->search(filter, directories, searchExtensions); + + auto search = std::make_shared( + filter, directories, searchExtensions, excludedPaths(), dirFilters(), sortFlags()); + connect(search.get(), &FileSearch::foundMatch, this, &FileBrowser::foundSearchMatch, Qt::QueuedConnection); + connect(search.get(), &FileSearch::searchCompleted, this, &FileBrowser::searchCompleted, Qt::QueuedConnection); + + m_currentSearch = search; + ThreadPool::instance().enqueue([search] { (*search)(); }); } -void FileBrowser::toggleSearch(bool on) +void FileBrowser::displaySearch(bool on) { if (on) { @@ -376,7 +399,7 @@ void FileBrowser::giveFocusToFilter() void FileBrowser::addItems(const QString & path ) { - if (FileBrowser::directoryBlacklist().contains(path)) { return; } + if (FileBrowser::excludedPaths().contains(path)) { return; } if( m_dirsAsItems ) { @@ -388,12 +411,15 @@ void FileBrowser::addItems(const QString & path ) QDir cdir(path); if (!cdir.isReadable()) { return; } QFileInfoList entries = cdir.entryInfoList( - m_filter.split(' '), dirFilters(), QDir::LocaleAware | QDir::DirsFirst | QDir::Name | QDir::IgnoreCase); + m_filter.split(' '), + dirFilters(), + QDir::LocaleAware | QDir::DirsFirst | QDir::Name | QDir::IgnoreCase); for (const auto& entry : entries) { - if (FileBrowser::directoryBlacklist().contains(entry.absoluteFilePath())) { continue; } + if (FileBrowser::excludedPaths().contains(entry.absoluteFilePath())) { continue; } QString fileName = entry.fileName(); + if (entry.isHidden() && m_showHiddenContent && !m_showHiddenContent->isChecked()) continue; if (entry.isDir()) { // Merge dir's together @@ -1066,7 +1092,7 @@ void Directory::update() bool Directory::addItems(const QString& path) { - if (FileBrowser::directoryBlacklist().contains(path)) { return false; } + if (FileBrowser::excludedPaths().contains(path)) { return false; } QDir thisDir(path); if (!thisDir.isReadable()) { return false; } @@ -1077,7 +1103,7 @@ bool Directory::addItems(const QString& path) = thisDir.entryInfoList(m_filter.split(' '), FileBrowser::dirFilters(), FileBrowser::sortFlags()); for (const auto& entry : entries) { - if (FileBrowser::directoryBlacklist().contains(entry.absoluteFilePath())) { continue; } + if (FileBrowser::excludedPaths().contains(entry.absoluteFilePath())) { continue; } QString fileName = entry.fileName(); if (entry.isDir()) diff --git a/src/gui/FileBrowserSearcher.cpp b/src/gui/FileBrowserSearcher.cpp deleted file mode 100644 index 80c2380580d..00000000000 --- a/src/gui/FileBrowserSearcher.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * FileBrowserSearcher.cpp - Batch processor for searching the filesystem - * - * Copyright (c) 2023 saker - * - * This file is part of LMMS - https://lmms.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program (see COPYING); if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - */ - -#include "FileBrowserSearcher.h" - -#include -#include - -#include "FileBrowser.h" - -namespace lmms::gui { - -FileBrowserSearcher::~FileBrowserSearcher() -{ - m_cancelRunningSearch = true; - - { - const auto lock = std::lock_guard{m_workerMutex}; - m_workerStopped = true; - } - - m_workerCond.notify_one(); - m_worker.join(); -} - -auto FileBrowserSearcher::search(const QString& filter, const QStringList& paths, const QStringList& extensions) - -> std::shared_ptr -{ - m_cancelRunningSearch = true; - auto future = std::make_shared(filter, paths, extensions); - - { - const auto lock = std::lock_guard{m_workerMutex}; - m_searchQueue.push(future); - m_cancelRunningSearch = false; - } - - m_workerCond.notify_one(); - return future; -} - -auto FileBrowserSearcher::run() -> void -{ - while (true) - { - auto lock = std::unique_lock{m_workerMutex}; - m_workerCond.wait(lock, [this] { return m_workerStopped || !m_searchQueue.empty(); }); - - if (m_workerStopped) { return; } - - const auto future = m_searchQueue.front(); - future->m_state = SearchFuture::State::Running; - m_searchQueue.pop(); - - auto cancelled = false; - for (const auto& path : future->m_paths) - { - if (FileBrowser::directoryBlacklist().contains(path)) { continue; } - - if (!process(future.get(), path)) - { - future->m_state = SearchFuture::State::Cancelled; - cancelled = true; - break; - } - } - - if (!cancelled) { future->m_state = SearchFuture::State::Completed; } - } -} - -auto FileBrowserSearcher::process(SearchFuture* searchFuture, const QString& path) -> bool -{ - auto stack = QFileInfoList{}; - - auto dir = QDir{path}; - stack.append(dir.entryInfoList(FileBrowser::dirFilters(), FileBrowser::sortFlags())); - - while (!stack.empty()) - { - if (m_cancelRunningSearch) - { - m_cancelRunningSearch = false; - return false; - } - - const auto info = stack.takeFirst(); - const auto path = info.absoluteFilePath(); - if (FileBrowser::directoryBlacklist().contains(path)) { continue; } - - const auto name = info.fileName(); - const auto validFile = info.isFile() && searchFuture->m_extensions.contains(info.suffix(), Qt::CaseInsensitive); - const auto passesFilter = name.contains(searchFuture->m_filter, Qt::CaseInsensitive); - - // Only when a directory doesn't pass the filter should we search further - if (info.isDir() && !passesFilter) - { - dir.setPath(path); - auto entries = dir.entryInfoList(FileBrowser::dirFilters(), FileBrowser::sortFlags()); - - // Reverse to maintain the sorting within this directory when popped - std::reverse(entries.begin(), entries.end()); - - for (const auto& entry : entries) - { - stack.push_front(entry); - } - } - else if ((validFile || info.isDir()) && passesFilter) { searchFuture->addMatch(path); } - } - return true; -} - -} // namespace lmms::gui diff --git a/src/gui/Lv2ViewBase.cpp b/src/gui/Lv2ViewBase.cpp index 77268bb9b75..fc025e26856 100644 --- a/src/gui/Lv2ViewBase.cpp +++ b/src/gui/Lv2ViewBase.cpp @@ -31,7 +31,7 @@ #include #include #include -#include +#include #include "AudioEngine.h" #include "Controls.h" @@ -74,7 +74,7 @@ Lv2ViewProc::Lv2ViewProc(QWidget* parent, Lv2Proc* proc, int colNum) : break; case PortVis::Integer: { - sample_rate_t sr = Engine::audioEngine()->processingSampleRate(); + sample_rate_t sr = Engine::audioEngine()->outputSampleRate(); auto pMin = port.min(sr); auto pMax = port.max(sr); int numDigits = std::max(numDigitsAsInt(pMin), numDigitsAsInt(pMax)); @@ -157,8 +157,7 @@ Lv2ViewBase::Lv2ViewBase(QWidget* meAsWidget, Lv2ControlBase *ctrlBase) : m_toggleUIButton->setCheckable(true); m_toggleUIButton->setChecked(false); m_toggleUIButton->setIcon(embed::getIconPixmap("zoom")); - m_toggleUIButton->setFont( - pointSize<8>(m_toggleUIButton->font())); + m_toggleUIButton->setFont(adjustedToPixelSize(m_toggleUIButton->font(), 8)); btnBox->addWidget(m_toggleUIButton, 0); } btnBox->addStretch(1); diff --git a/src/gui/MainApplication.cpp b/src/gui/MainApplication.cpp index d33ede4d260..48c400a24dd 100644 --- a/src/gui/MainApplication.cpp +++ b/src/gui/MainApplication.cpp @@ -92,7 +92,7 @@ bool MainApplication::winEventFilter(MSG* msg, long* result) switch(msg->message) { case WM_STYLECHANGING: - if(msg->wParam == GWL_EXSTYLE) + if (msg->wParam == static_cast(GWL_EXSTYLE)) { // Prevent plugins making the main window transparent STYLESTRUCT * style = reinterpret_cast(msg->lParam); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 0413e3bd656..d534be96f3f 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -135,7 +135,7 @@ MainWindow::MainWindow() : embed::getIconPixmap("home").transformed(QTransform().rotate(90)), splitter, false)); QStringList root_paths; - QString title = tr( "Root directory" ); + QString title = tr("Root Directory"); bool dirs_as_items = false; #ifdef LMMS_BUILD_APPLE @@ -324,10 +324,7 @@ void MainWindow::finalize() SLOT(onExportProjectMidi()), Qt::CTRL + Qt::Key_M ); -// Prevent dangling separator at end of menu per https://bugreports.qt.io/browse/QTBUG-40071 -#if !(defined(LMMS_BUILD_APPLE) && (QT_VERSION < 0x050600)) project_menu->addSeparator(); -#endif project_menu->addAction( embed::getIconPixmap( "exit" ), tr( "&Quit" ), qApp, SLOT(closeAllWindows()), Qt::CTRL + Qt::Key_Q ); @@ -400,10 +397,7 @@ void MainWindow::finalize() this, SLOT(help())); } -// Prevent dangling separator at end of menu per https://bugreports.qt.io/browse/QTBUG-40071 -#if !(defined(LMMS_BUILD_APPLE) && (QT_VERSION < 0x050600)) help_menu->addSeparator(); -#endif help_menu->addAction( embed::getIconPixmap( "icon_small" ), tr( "About" ), this, SLOT(aboutLMMS())); @@ -1108,8 +1102,7 @@ void MainWindow::updateViewMenu() // Here we should put all look&feel -stuff from configmanager // that is safe to change on the fly. There is probably some // more elegant way to do this. - QAction *qa; - qa = new QAction(tr( "Volume as dBFS" ), this); + auto qa = new QAction(tr("Volume as dBFS"), this); qa->setData("displaydbfs"); qa->setCheckable( true ); qa->setChecked( ConfigManager::inst()->value( "app", "displaydbfs" ).toInt() ); diff --git a/src/gui/MicrotunerConfig.cpp b/src/gui/MicrotunerConfig.cpp index 6bb8415bd45..20660df4110 100644 --- a/src/gui/MicrotunerConfig.cpp +++ b/src/gui/MicrotunerConfig.cpp @@ -224,14 +224,14 @@ MicrotunerConfig::MicrotunerConfig() : */ void MicrotunerConfig::updateScaleList(int index) { - if (index >= 0 && index < MaxScaleCount) + if (index >= 0 && static_cast(index) < MaxScaleCount) { m_scaleComboModel.replaceItem(index, QString::number(index) + ": " + Engine::getSong()->getScale(index)->getDescription()); } else { - for (int i = 0; i < MaxScaleCount; i++) + for (auto i = std::size_t{0}; i < MaxScaleCount; i++) { m_scaleComboModel.replaceItem(i, QString::number(i) + ": " + Engine::getSong()->getScale(i)->getDescription()); @@ -246,14 +246,14 @@ void MicrotunerConfig::updateScaleList(int index) */ void MicrotunerConfig::updateKeymapList(int index) { - if (index >= 0 && index < MaxKeymapCount) + if (index >= 0 && static_cast(index) < MaxKeymapCount) { m_keymapComboModel.replaceItem(index, QString::number(index) + ": " + Engine::getSong()->getKeymap(index)->getDescription()); } else { - for (int i = 0; i < MaxKeymapCount; i++) + for (auto i = std::size_t{0}; i < MaxKeymapCount; i++) { m_keymapComboModel.replaceItem(i, QString::number(i) + ": " + Engine::getSong()->getKeymap(i)->getDescription()); diff --git a/src/gui/MidiSetupWidget.cpp b/src/gui/MidiSetupWidget.cpp index 2385def02dd..0e6678727ce 100644 --- a/src/gui/MidiSetupWidget.cpp +++ b/src/gui/MidiSetupWidget.cpp @@ -28,7 +28,6 @@ #include #include "ConfigManager.h" -#include "gui_templates.h" namespace lmms::gui diff --git a/src/gui/MixerChannelView.cpp b/src/gui/MixerChannelView.cpp index 928255806ce..2a1fe09289e 100644 --- a/src/gui/MixerChannelView.cpp +++ b/src/gui/MixerChannelView.cpp @@ -28,7 +28,9 @@ #include "Mixer.h" #include "MixerChannelView.h" #include "MixerView.h" +#include "PeakIndicator.h" #include "Song.h" +#include "ConfigManager.h" #include "gui_templates.h" #include "lmms_math.h" @@ -38,6 +40,8 @@ #include #include #include +#include +#include #include @@ -48,8 +52,6 @@ namespace lmms::gui m_mixerView(mixerView), m_channelIndex(channelIndex) { - setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); - auto retainSizeWhenHidden = [](QWidget* widget) { auto sizePolicy = widget->sizePolicy(); @@ -73,7 +75,7 @@ namespace lmms::gui m_renameLineEdit = new QLineEdit{mixerName, nullptr}; m_renameLineEdit->setFixedWidth(65); - m_renameLineEdit->setFont(pointSizeF(font(), 7.5f)); + m_renameLineEdit->setFont(adjustedToPixelSize(font(), 12)); m_renameLineEdit->setReadOnly(true); m_renameLineEdit->installEventFilter(this); @@ -119,9 +121,9 @@ namespace lmms::gui soloMuteLayout->addWidget(m_muteButton, 0, Qt::AlignHCenter); m_fader = new Fader{&mixerChannel->m_volumeModel, tr("Fader %1").arg(channelIndex), this}; - m_fader->setLevelsDisplayedInDBFS(); - m_fader->setMinPeak(dbfsToAmp(-42)); - m_fader->setMaxPeak(dbfsToAmp(9)); + + m_peakIndicator = new PeakIndicator(this); + connect(m_fader, &Fader::peakChanged, m_peakIndicator, &PeakIndicator::updatePeak); m_effectRackView = new EffectRackView{&mixerChannel->m_fxChain, mixerView->m_racksWidget}; m_effectRackView->setFixedWidth(EffectRackView::DEFAULT_WIDTH); @@ -133,10 +135,10 @@ namespace lmms::gui mainLayout->addWidget(m_sendKnob, 0, Qt::AlignHCenter); mainLayout->addWidget(m_sendArrow, 0, Qt::AlignHCenter); mainLayout->addWidget(m_channelNumberLcd, 0, Qt::AlignHCenter); - mainLayout->addStretch(); mainLayout->addWidget(m_renameLineEditView, 0, Qt::AlignHCenter); mainLayout->addLayout(soloMuteLayout, 0); - mainLayout->addWidget(m_fader, 0, Qt::AlignHCenter); + mainLayout->addWidget(m_peakIndicator); + mainLayout->addWidget(m_fader, 1, Qt::AlignHCenter); connect(m_renameLineEdit, &QLineEdit::editingFinished, this, &MixerChannelView::renameFinished); } @@ -277,6 +279,7 @@ namespace lmms::gui m_muteButton->setModel(&mixerChannel->m_muteModel); m_soloButton->setModel(&mixerChannel->m_soloModel); m_effectRackView->setModel(&mixerChannel->m_fxChain); + m_channelNumberLcd->setValue(index); m_channelIndex = index; } @@ -342,6 +345,11 @@ namespace lmms::gui m_strokeInnerInactive = c; } + void MixerChannelView::reset() + { + m_peakIndicator->resetPeakToMinusInf(); + } + void MixerChannelView::renameChannel() { m_inRename = true; @@ -411,8 +419,44 @@ namespace lmms::gui update(); } + bool MixerChannelView::confirmRemoval(int index) + { + // if config variable is set to false, there is no need for user confirmation + bool needConfirm = ConfigManager::inst()->value("ui", "mixerchanneldeletionwarning", "1").toInt(); + if (!needConfirm) { return true; } + + // is the channel is not in use, there is no need for user confirmation + if (!getGUI()->mixerView()->getMixer()->isChannelInUse(index)) { return true; } + + QString messageRemoveTrack = tr("This Mixer Channel is being used.\n" + "Are you sure you want to remove this channel?\n\n" + "Warning: This operation can not be undone."); + + QString messageTitleRemoveTrack = tr("Confirm removal"); + QString askAgainText = tr("Don't ask again"); + auto askAgainCheckBox = new QCheckBox(askAgainText, nullptr); + connect(askAgainCheckBox, &QCheckBox::stateChanged, [](int state) { + // Invert button state, if it's checked we *shouldn't* ask again + ConfigManager::inst()->setValue("ui", "mixerchanneldeletionwarning", state ? "0" : "1"); + }); + + QMessageBox mb(this); + mb.setText(messageRemoveTrack); + mb.setWindowTitle(messageTitleRemoveTrack); + mb.setIcon(QMessageBox::Warning); + mb.addButton(QMessageBox::Cancel); + mb.addButton(QMessageBox::Ok); + mb.setCheckBox(askAgainCheckBox); + mb.setDefaultButton(QMessageBox::Cancel); + + int answer = mb.exec(); + + return answer == QMessageBox::Ok; + } + void MixerChannelView::removeChannel() { + if (!confirmRemoval(m_channelIndex)) { return; } auto mix = getGUI()->mixerView(); mix->deleteChannel(m_channelIndex); } diff --git a/src/gui/MixerView.cpp b/src/gui/MixerView.cpp index 8b2ecdc5672..8152509f86a 100644 --- a/src/gui/MixerView.cpp +++ b/src/gui/MixerView.cpp @@ -23,9 +23,7 @@ */ -#include #include -#include #include #include #include @@ -91,6 +89,7 @@ MixerView::MixerView(Mixer* mixer) : chLayout->setSizeConstraint(QLayout::SetMinimumSize); chLayout->setSpacing(0); chLayout->setContentsMargins(0, 0, 0, 0); + chLayout->setAlignment(Qt::AlignLeft); m_channelAreaWidget->setLayout(chLayout); // create rack layout before creating the first channel @@ -107,7 +106,7 @@ MixerView::MixerView(Mixer* mixer) : m_racksLayout->addWidget(m_mixerChannelViews[0]->m_effectRackView); - ml->addWidget(masterView, 0, Qt::AlignTop); + ml->addWidget(masterView, 0); auto mixerChannelSize = masterView->sizeHint(); @@ -139,18 +138,20 @@ MixerView::MixerView(Mixer* mixer) : channelArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); channelArea->setFrameStyle(QFrame::NoFrame); channelArea->setMinimumWidth(mixerChannelSize.width() * 6); + channelArea->setWidgetResizable(true); int const scrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent); - channelArea->setFixedHeight(mixerChannelSize.height() + scrollBarExtent); + channelArea->setMinimumHeight(mixerChannelSize.height() + scrollBarExtent); - ml->addWidget(channelArea, 1, Qt::AlignTop); + ml->addWidget(channelArea, 1); // show the add new mixer channel button auto newChannelBtn = new QPushButton(embed::getIconPixmap("new_channel"), QString(), this); newChannelBtn->setObjectName("newChannelBtn"); - newChannelBtn->setFixedSize(mixerChannelSize); + newChannelBtn->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding); + newChannelBtn->setFixedWidth(mixerChannelSize.width()); connect(newChannelBtn, SIGNAL(clicked()), this, SLOT(addNewChannel())); - ml->addWidget(newChannelBtn, 0, Qt::AlignTop); + ml->addWidget(newChannelBtn, 0); // add the stacked layout for the effect racks of mixer channels @@ -167,9 +168,6 @@ MixerView::MixerView(Mixer* mixer) : // add ourself to workspace QMdiSubWindow* subWin = mainWindow->addWindowedWidget(this); - Qt::WindowFlags flags = subWin->windowFlags(); - flags &= ~Qt::WindowMaximizeButtonHint; - subWin->setWindowFlags(flags); layout()->setSizeConstraint(QLayout::SetMinimumSize); subWin->layout()->setSizeConstraint(QLayout::SetMinAndMaxSize); @@ -377,12 +375,6 @@ void MixerView::deleteChannel(int index) // can't delete master if (index == 0) return; - // if there is no user confirmation, do nothing - if (!confirmRemoval(index)) - { - return; - } - // Disconnect from the solo/mute models of the channel we are about to delete disconnectFromSoloAndMute(index); @@ -421,47 +413,6 @@ void MixerView::deleteChannel(int index) updateMaxChannelSelector(); } -bool MixerView::confirmRemoval(int index) -{ - // if config variable is set to false, there is no need for user confirmation - bool needConfirm = ConfigManager::inst()->value("ui", "mixerchanneldeletionwarning", "1").toInt(); - if (!needConfirm) { return true; } - - Mixer* mix = getMixer(); - - if (!mix->isChannelInUse(index)) - { - // is the channel is not in use, there is no need for user confirmation - return true; - } - - QString messageRemoveTrack = tr("This Mixer Channel is being used.\n" - "Are you sure you want to remove this channel?\n\n" - "Warning: This operation can not be undone."); - - QString messageTitleRemoveTrack = tr("Confirm removal"); - QString askAgainText = tr("Don't ask again"); - auto askAgainCheckBox = new QCheckBox(askAgainText, nullptr); - connect(askAgainCheckBox, &QCheckBox::stateChanged, [](int state) { - // Invert button state, if it's checked we *shouldn't* ask again - ConfigManager::inst()->setValue("ui", "mixerchanneldeletionwarning", state ? "0" : "1"); - }); - - QMessageBox mb(this); - mb.setText(messageRemoveTrack); - mb.setWindowTitle(messageTitleRemoveTrack); - mb.setIcon(QMessageBox::Warning); - mb.addButton(QMessageBox::Cancel); - mb.addButton(QMessageBox::Ok); - mb.setCheckBox(askAgainCheckBox); - mb.setDefaultButton(QMessageBox::Cancel); - - int answer = mb.exec(); - - return answer == QMessageBox::Ok; -} - - void MixerView::deleteUnusedChannels() { Mixer* mix = getMixer(); @@ -593,6 +544,8 @@ void MixerView::clear() for (auto i = m_mixerChannelViews.size() - 1; i > 0; --i) { deleteChannel(i); } getMixer()->clearChannel(0); + m_mixerChannelViews[0]->reset(); + refreshDisplay(); } diff --git a/src/gui/PluginBrowser.cpp b/src/gui/PluginBrowser.cpp index 963609c431c..2594bdab374 100644 --- a/src/gui/PluginBrowser.cpp +++ b/src/gui/PluginBrowser.cpp @@ -62,7 +62,7 @@ PluginBrowser::PluginBrowser( QWidget * _parent ) : auto hint = new QLabel( tr( "Drag an instrument " "into either the Song Editor, the " - "Pattern Editor or into an " + "Pattern Editor or an " "existing instrument track." ), m_view ); hint->setWordWrap( true ); diff --git a/src/gui/ProjectNotes.cpp b/src/gui/ProjectNotes.cpp index f131a017c85..a71f146c66e 100644 --- a/src/gui/ProjectNotes.cpp +++ b/src/gui/ProjectNotes.cpp @@ -108,10 +108,8 @@ void ProjectNotes::setText( const QString & _text ) void ProjectNotes::setupActions() { QToolBar * tb = addToolBar( tr( "Edit Actions" ) ); - QAction * a; - a = new QAction( embed::getIconPixmap( "edit_undo" ), tr( "&Undo" ), - this ); + auto a = new QAction(embed::getIconPixmap("edit_undo"), tr("&Undo"), this); a->setShortcut( tr( "%1+Z" ).arg(UI_CTRL_KEY) ); connect( a, SIGNAL(triggered()), m_edit, SLOT(undo())); tb->addAction( a ); diff --git a/src/gui/SampleTrackWindow.cpp b/src/gui/SampleTrackWindow.cpp index c0dd8e04e6a..81a2ca89b60 100644 --- a/src/gui/SampleTrackWindow.cpp +++ b/src/gui/SampleTrackWindow.cpp @@ -33,7 +33,6 @@ #include "EffectRackView.h" #include "embed.h" -#include "gui_templates.h" #include "GuiApplication.h" #include "Knob.h" #include "MainWindow.h" @@ -41,7 +40,6 @@ #include "SampleTrackView.h" #include "Song.h" #include "SubWindow.h" -#include "TabWidget.h" #include "TrackLabelButton.h" namespace lmms::gui @@ -70,13 +68,9 @@ SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) : vlayout->setContentsMargins(0, 0, 0, 0); vlayout->setSpacing(0); - auto generalSettingsWidget = new TabWidget(tr("GENERAL SETTINGS"), this); - + auto generalSettingsWidget = new QWidget(this); auto generalSettingsLayout = new QVBoxLayout(generalSettingsWidget); - generalSettingsLayout->setContentsMargins(8, 18, 8, 8); - generalSettingsLayout->setSpacing(6); - auto nameWidget = new QWidget(generalSettingsWidget); auto nameLayout = new QHBoxLayout(nameWidget); nameLayout->setContentsMargins(0, 0, 0, 0); diff --git a/src/gui/SampleWaveform.cpp b/src/gui/SampleWaveform.cpp index 68b09b48271..ca356e72713 100644 --- a/src/gui/SampleWaveform.cpp +++ b/src/gui/SampleWaveform.cpp @@ -28,55 +28,66 @@ namespace lmms::gui { void SampleWaveform::visualize(Parameters parameters, QPainter& painter, const QRect& rect) { - const auto x = rect.x(); - const auto height = rect.height(); - const auto width = rect.width(); - const auto centerY = rect.center().y(); + const int x = rect.x(); + const int height = rect.height(); + const int width = rect.width(); + const int centerY = rect.center().y(); - const auto halfHeight = height / 2; + const int halfHeight = height / 2; const auto color = painter.pen().color(); const auto rmsColor = color.lighter(123); - const auto framesPerPixel = std::max(1, parameters.size / width); + const float framesPerPixel = std::max(1.0f, static_cast(parameters.size) / width); - constexpr auto maxFramesPerPixel = 512; - const auto resolution = std::max(1, framesPerPixel / maxFramesPerPixel); - const auto framesPerResolution = framesPerPixel / resolution; + constexpr float maxFramesPerPixel = 512.0f; + const float resolution = std::max(1.0f, framesPerPixel / maxFramesPerPixel); + const float framesPerResolution = framesPerPixel / resolution; - const auto numPixels = std::min(parameters.size, width); + const size_t numPixels = std::min(parameters.size, width); auto min = std::vector(numPixels, 1); auto max = std::vector(numPixels, -1); - auto squared = std::vector(numPixels); + auto squared = std::vector(numPixels, 0); - const auto maxFrames = numPixels * framesPerPixel; - for (int i = 0; i < maxFrames; i += resolution) + const size_t maxFrames = numPixels * static_cast(framesPerPixel); + + auto pixelIndex = std::size_t{0}; + + for (auto i = std::size_t{0}; i < maxFrames; i += static_cast(resolution)) { - const auto pixelIndex = i / framesPerPixel; + pixelIndex = i / framesPerPixel; const auto frameIndex = !parameters.reversed ? i : maxFrames - i; const auto& frame = parameters.buffer[frameIndex]; - const auto value = std::accumulate(frame.begin(), frame.end(), 0.0f) / frame.size(); + const auto value = frame.average(); if (value > max[pixelIndex]) { max[pixelIndex] = value; } if (value < min[pixelIndex]) { min[pixelIndex] = value; } squared[pixelIndex] += value * value; } + + while (pixelIndex < numPixels) + { + max[pixelIndex] = 0.0; + min[pixelIndex] = 0.0; + + pixelIndex++; + } - for (int i = 0; i < numPixels; i++) + for (auto i = std::size_t{0}; i < numPixels; i++) { - const auto lineY1 = centerY - max[i] * halfHeight * parameters.amplification; - const auto lineY2 = centerY - min[i] * halfHeight * parameters.amplification; - const auto lineX = i + x; + const int lineY1 = centerY - max[i] * halfHeight * parameters.amplification; + const int lineY2 = centerY - min[i] * halfHeight * parameters.amplification; + const int lineX = static_cast(i) + x; painter.drawLine(lineX, lineY1, lineX, lineY2); - const auto rms = std::sqrt(squared[i] / framesPerResolution); - const auto maxRMS = std::clamp(rms, min[i], max[i]); - const auto minRMS = std::clamp(-rms, min[i], max[i]); + const float rms = std::sqrt(squared[i] / framesPerResolution); + const float maxRMS = std::clamp(rms, min[i], max[i]); + const float minRMS = std::clamp(-rms, min[i], max[i]); - const auto rmsLineY1 = centerY - maxRMS * halfHeight * parameters.amplification; - const auto rmsLineY2 = centerY - minRMS * halfHeight * parameters.amplification; + const int rmsLineY1 = centerY - maxRMS * halfHeight * parameters.amplification; + const int rmsLineY2 = centerY - minRMS * halfHeight * parameters.amplification; painter.setPen(rmsColor); painter.drawLine(lineX, rmsLineY1, lineX, rmsLineY2); diff --git a/src/gui/clips/AutomationClipView.cpp b/src/gui/clips/AutomationClipView.cpp index 7ddb7015182..9b71bb74c74 100644 --- a/src/gui/clips/AutomationClipView.cpp +++ b/src/gui/clips/AutomationClipView.cpp @@ -314,24 +314,17 @@ void AutomationClipView::paintEvent( QPaintEvent * ) // the outValue of the current node). When we have nodes with linear or cubic progression // the value of the end of the shape between the two nodes will be the inValue of // the next node. - float nextValue; - if( m_clip->progressionType() == AutomationClip::ProgressionType::Discrete ) - { - nextValue = OUTVAL(it); - } - else - { - nextValue = INVAL(it + 1); - } + float nextValue = m_clip->progressionType() == AutomationClip::ProgressionType::Discrete + ? OUTVAL(it) + : INVAL(it + 1); QPainterPath path; QPointF origin = QPointF(POS(it) * ppTick, 0.0f); path.moveTo( origin ); path.moveTo(QPointF(POS(it) * ppTick,values[0])); - float x; for (int i = POS(it) + 1; i < POS(it + 1); i++) { - x = i * ppTick; + float x = i * ppTick; if( x > ( width() - BORDER_WIDTH ) ) break; float value = values[i - POS(it)]; path.lineTo( QPointF( x, value ) ); diff --git a/src/gui/clips/ClipView.cpp b/src/gui/clips/ClipView.cpp index 5c8a12b91e3..9eb6acb6b94 100644 --- a/src/gui/clips/ClipView.cpp +++ b/src/gui/clips/ClipView.cpp @@ -356,7 +356,7 @@ void ClipView::selectColor() // Get a color from the user const auto newColor = ColorChooser{this} .withPalette(ColorChooser::Palette::Track) - ->getColor(m_clip->color().value_or(palette().background().color())); + ->getColor(m_clip->color().value_or(palette().window().color())); if (newColor.isValid()) { setColor(newColor); } } @@ -534,8 +534,9 @@ DataFile ClipView::createClipDataFiles( { // Insert into the dom under the "clips" element Track* clipTrack = clipView->m_trackView->getTrack(); - int trackIndex = std::distance(tc->tracks().begin(), std::find(tc->tracks().begin(), tc->tracks().end(), clipTrack)); - assert(trackIndex != tc->tracks().size()); + const auto trackIt = std::find(tc->tracks().begin(), tc->tracks().end(), clipTrack); + assert(trackIt != tc->tracks().end()); + int trackIndex = std::distance(tc->tracks().begin(), trackIt); QDomElement clipElement = dataFile.createElement("clip"); clipElement.setAttribute( "trackIndex", trackIndex ); clipElement.setAttribute( "trackType", static_cast(clipTrack->type()) ); diff --git a/src/gui/clips/MidiClipView.cpp b/src/gui/clips/MidiClipView.cpp index 0a6fece3168..b735913e4d0 100644 --- a/src/gui/clips/MidiClipView.cpp +++ b/src/gui/clips/MidiClipView.cpp @@ -332,7 +332,8 @@ void MidiClipView::wheelEvent(QWheelEvent * we) } Note * n = m_clip->noteAtStep( step ); - if(!n && we->angleDelta().y() > 0) + const int direction = (we->angleDelta().y() > 0 ? 1 : -1) * (we->inverted() ? -1 : 1); + if(!n && direction > 0) { n = m_clip->addStepNote( step ); n->setVolume( 0 ); @@ -340,8 +341,7 @@ void MidiClipView::wheelEvent(QWheelEvent * we) if( n != nullptr ) { int vol = n->getVolume(); - - if(we->angleDelta().y() > 0) + if(direction > 0) { n->setVolume( qMin( 100, vol + 5 ) ); } diff --git a/src/gui/clips/SampleClipView.cpp b/src/gui/clips/SampleClipView.cpp index ef46325e4e6..30f09caa860 100644 --- a/src/gui/clips/SampleClipView.cpp +++ b/src/gui/clips/SampleClipView.cpp @@ -127,7 +127,6 @@ void SampleClipView::dropEvent( QDropEvent * _de ) m_clip->updateLength(); update(); _de->accept(); - Engine::getSong()->setModified(); } else { @@ -181,18 +180,21 @@ void SampleClipView::mouseReleaseEvent(QMouseEvent *_me) void SampleClipView::mouseDoubleClickEvent( QMouseEvent * ) { - QString af = SampleLoader::openAudioFile(); + const QString selectedAudioFile = SampleLoader::openAudioFile(); - if ( af.isEmpty() ) {} //Don't do anything if no file is loaded - else if (af == m_clip->m_sample.sampleFile()) - { //Instead of reloading the existing file, just reset the size - int length = static_cast(m_clip->m_sample.sampleSize() / Engine::framesPerTick()); - m_clip->changeLength(length); + if (selectedAudioFile.isEmpty()) { return; } + + if (m_clip->hasSampleFileLoaded(selectedAudioFile)) + { + m_clip->changeLengthToSampleLength(); } else - { //Otherwise load the new file as ususal - m_clip->setSampleFile( af ); - Engine::getSong()->setModified(); + { + auto sampleBuffer = SampleLoader::createBufferFromFile(selectedAudioFile); + if (sampleBuffer != SampleBuffer::emptyBuffer()) + { + m_clip->setSampleBuffer(sampleBuffer); + } } } diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 8fd892597df..a7ca0727972 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -120,7 +120,7 @@ AutomationEditor::AutomationEditor() : //keeps the direction of the widget, undepended on the locale setLayoutDirection( Qt::LeftToRight ); - m_tensionModel = new FloatModel(1.0, 0.0, 1.0, 0.01); + m_tensionModel = new FloatModel(1.f, 0.f, 1.f, 0.01f); connect( m_tensionModel, SIGNAL(dataChanged()), this, SLOT(setTension())); @@ -320,8 +320,6 @@ void AutomationEditor::drawLine( int x0In, float y0, int x1In, float y1 ) auto deltay = qAbs(y1 - y0); int x = x0; float y = y0; - int xstep; - int ystep; if( deltax < AutomationClip::quantization() ) { @@ -332,34 +330,14 @@ void AutomationEditor::drawLine( int x0In, float y0, int x1In, float y1 ) float yscale = deltay / ( deltax ); - if( x0 < x1 ) - { - xstep = AutomationClip::quantization(); - } - else - { - xstep = -( AutomationClip::quantization() ); - } - - float lineAdjust; - if( y0 < y1 ) - { - ystep = 1; - lineAdjust = yscale; - } - else - { - ystep = -1; - lineAdjust = -( yscale ); - } + int xstep = (x0 < x1 ? 1 : -1) * AutomationClip::quantization(); + int ystep = y0 < y1 ? 1 : -1; + float lineAdjust = ystep * yscale; - int i = 0; - while( i < deltax ) + for (int i = 0; i < deltax; ++i) { y = y0 + ( ystep * yscale * i ) + lineAdjust; - x += xstep; - i += 1; m_clip->removeNode(TimePos(x)); m_clip->putValue( TimePos( x ), y ); } @@ -979,7 +957,6 @@ inline void AutomationEditor::drawCross( QPainter & p ) inline void AutomationEditor::drawAutomationPoint(QPainter & p, timeMap::iterator it) { int x = xCoordOfTick(POS(it)); - int y; // Below (m_ppb * AutomationClip::quantization() / 576) is used because: // 1 bar equals to 192/quantization() notes. Hence, to calculate the number of pixels // per note we would have (m_ppb * 1 bar / (192/quantization()) notes per bar), or @@ -988,7 +965,7 @@ inline void AutomationEditor::drawAutomationPoint(QPainter & p, timeMap::iterato const int outerRadius = qBound(3, (m_ppb * AutomationClip::quantization()) / 576, 5); // Draw a circle for the outValue - y = yCoordOfLevel(OUTVAL(it)); + int y = yCoordOfLevel(OUTVAL(it)); p.setPen(QPen(m_nodeOutValueColor.lighter(200))); p.setBrush(QBrush(m_nodeOutValueColor)); p.drawEllipse(x - outerRadius, y - outerRadius, outerRadius * 2, outerRadius * 2); @@ -1006,7 +983,6 @@ inline void AutomationEditor::drawAutomationPoint(QPainter & p, timeMap::iterato inline void AutomationEditor::drawAutomationTangents(QPainter& p, timeMap::iterator it) { int x = xCoordOfTick(POS(it)); - int y, tx, ty; // The tangent value correlates the variation in the node value related to the increase // in ticks. So to have a proportionate drawing of the tangent line, we need to find the @@ -1020,9 +996,9 @@ inline void AutomationEditor::drawAutomationTangents(QPainter& p, timeMap::itera p.setPen(QPen(m_nodeTangentLineColor)); p.setBrush(QBrush(m_nodeTangentLineColor)); - y = yCoordOfLevel(INVAL(it)); - tx = x - 20; - ty = y + 20 * INTAN(it) * proportion; + int y = yCoordOfLevel(INVAL(it)); + int tx = x - 20; + int ty = y + 20 * INTAN(it) * proportion; p.drawLine(x, y, tx, ty); p.setBrush(QBrush(m_nodeTangentLineColor.darker(200))); p.drawEllipse(tx - 3, ty - 3, 6, 6); @@ -1064,8 +1040,7 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) QBrush bgColor = p.background(); p.fillRect( 0, 0, width(), height(), bgColor ); - // set font-size to 8 - p.setFont( pointSize<8>( p.font() ) ); + p.setFont(adjustedToPixelSize(p.font(), 10)); int grid_height = height() - TOP_MARGIN - SCROLLBAR_SIZE; @@ -1101,7 +1076,6 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) } else { - int y; int level = (int) m_bottomLevel; int printable = qMax( 1, 5 * DEFAULT_Y_DELTA / m_y_delta ); @@ -1116,7 +1090,7 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) { const QString & label = m_clip->firstObject() ->displayValue( level ); - y = yCoordOfLevel( level ); + int y = yCoordOfLevel(level); p.setPen( QApplication::palette().color( QPalette::Active, QPalette::Shadow ) ); p.drawText( 1, y - font_height + 1, @@ -1139,7 +1113,7 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) if( m_clip ) { - int tick, x, q; + int q; int x_line_end = (int)( m_y_auto || m_topLevel < m_maxLevel ? TOP_MARGIN : grid_bottom - ( m_topLevel - m_bottomLevel ) * m_y_delta ); @@ -1163,10 +1137,8 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) // 3 independent loops, because quantization might not divide evenly into // exotic denominators (e.g. 7/11 time), which are allowed ATM. // First quantization grid... - for( tick = m_currentPosition - m_currentPosition % q, - x = xCoordOfTick( tick ); - x<=width(); - tick += q, x = xCoordOfTick( tick ) ) + for (int tick = m_currentPosition - m_currentPosition % q, x = xCoordOfTick(tick); x <= width(); + tick += q, x = xCoordOfTick(tick)) { p.setPen(m_lineColor); p.drawLine( x, grid_bottom, x, x_line_end ); @@ -1187,10 +1159,9 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) } else { - float y; for( int level = (int)m_bottomLevel; level <= m_topLevel; level++) { - y = yCoordOfLevel( (float)level ); + float y = yCoordOfLevel(static_cast(level)); p.setPen(level % 10 == 0 ? m_beatLineColor : m_lineColor); @@ -1226,10 +1197,8 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) int ticksPerBeat = DefaultTicksPerBar / Engine::getSong()->getTimeSigModel().getDenominator(); - for( tick = m_currentPosition - m_currentPosition % ticksPerBeat, - x = xCoordOfTick( tick ); - x<=width(); - tick += ticksPerBeat, x = xCoordOfTick( tick ) ) + for (int tick = m_currentPosition - m_currentPosition % ticksPerBeat, x = xCoordOfTick(tick); x <= width(); + tick += ticksPerBeat, x = xCoordOfTick(tick)) { p.setPen(m_beatLineColor); p.drawLine( x, grid_bottom, x, x_line_end ); @@ -1316,10 +1285,8 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) } // and finally bars - for( tick = m_currentPosition - m_currentPosition % TimePos::ticksPerBar(), - x = xCoordOfTick( tick ); - x<=width(); - tick += TimePos::ticksPerBar(), x = xCoordOfTick( tick ) ) + for (int tick = m_currentPosition - m_currentPosition % TimePos::ticksPerBar(), x = xCoordOfTick(tick); + x <= width(); tick += TimePos::ticksPerBar(), x = xCoordOfTick(tick)) { p.setPen(m_barLineColor); p.drawLine( x, grid_bottom, x, x_line_end ); @@ -1365,15 +1332,9 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) // the outValue of the current node). When we have nodes with linear or cubic progression // the value of the end of the shape between the two nodes will be the inValue of // the next node. - float nextValue; - if( m_clip->progressionType() == AutomationClip::ProgressionType::Discrete ) - { - nextValue = OUTVAL(it); - } - else - { - nextValue = INVAL(it + 1); - } + float nextValue = m_clip->progressionType() == AutomationClip::ProgressionType::Discrete + ? OUTVAL(it) + : INVAL(it + 1); p.setRenderHints( QPainter::Antialiasing, true ); QPainterPath path; @@ -1421,9 +1382,9 @@ void AutomationEditor::paintEvent(QPaintEvent * pe ) } else { - QFont f = p.font(); + QFont f = font(); f.setBold( true ); - p.setFont( pointSize<14>( f ) ); + p.setFont(f); p.setPen( QApplication::palette().color( QPalette::Active, QPalette::BrightText ) ); p.drawText( VALUES_WIDTH + 20, TOP_MARGIN + 40, @@ -1523,25 +1484,11 @@ void AutomationEditor::drawLevelTick(QPainter & p, int tick, float value) || ( value > m_topLevel && m_topLevel >= 0 ) || ( value < m_bottomLevel && m_bottomLevel <= 0 ) ) { - int y_start = yCoordOfLevel( value ); - int rect_height; - - if( m_y_auto ) - { - int y_end = (int)( grid_bottom - + ( grid_bottom - TOP_MARGIN ) - * m_minLevel - / ( m_maxLevel - m_minLevel ) ); - - rect_height = y_end - y_start; - } - else - { - rect_height = (int)( value * m_y_delta ); - } + const int y_start = yCoordOfLevel(value); + const int y_end = grid_bottom + (grid_bottom - TOP_MARGIN) * m_minLevel / (m_maxLevel - m_minLevel); + const int rect_height = m_y_auto ? y_end - y_start : value * m_y_delta; QBrush currentColor = m_graphColor; - p.fillRect( x, y_start, rect_width, rect_height, currentColor ); } #ifdef LMMS_DEBUG @@ -1700,14 +1647,14 @@ float AutomationEditor::getLevel(int y ) { int level_line_y = height() - SCROLLBAR_SIZE - 1; // pressed level - float level = roundf( ( m_bottomLevel + ( m_y_auto ? + float level = std::roundf( ( m_bottomLevel + ( m_y_auto ? ( m_maxLevel - m_minLevel ) * ( level_line_y - y ) / (float)( level_line_y - ( TOP_MARGIN + 2 ) ) : ( level_line_y - y ) / (float)m_y_delta ) ) / m_step ) * m_step; // some range-checking-stuff - level = qBound( m_bottomLevel, level, m_topLevel ); + level = qBound(std::roundf(m_bottomLevel), level, std::roundf(m_topLevel)); - return( level ); + return level; } @@ -2160,7 +2107,7 @@ AutomationEditorWindow::AutomationEditorWindow() : for( float const & zoomLevel : m_editor->m_zoomXLevels ) { - m_editor->m_zoomingXModel.addItem( QString( "%1\%" ).arg( zoomLevel * 100 ) ); + m_editor->m_zoomingXModel.addItem(QString("%1%").arg(zoomLevel * 100)); } m_editor->m_zoomingXModel.setValue( m_editor->m_zoomingXModel.findText( "100%" ) ); diff --git a/src/gui/editors/PatternEditor.cpp b/src/gui/editors/PatternEditor.cpp index 5237690a7e7..2915786981d 100644 --- a/src/gui/editors/PatternEditor.cpp +++ b/src/gui/editors/PatternEditor.cpp @@ -135,10 +135,10 @@ void PatternEditor::dropEvent(QDropEvent* de) // Ensure pattern clips exist bool hasValidPatternClips = false; - if (t->getClips().size() == m_ps->numOfPatterns()) + if (t->getClips().size() == static_cast(m_ps->numOfPatterns())) { hasValidPatternClips = true; - for (int i = 0; i < t->getClips().size(); ++i) + for (auto i = std::size_t{0}; i < t->getClips().size(); ++i) { if (t->getClips()[i]->startPosition() != TimePos(i, 0)) { diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index ff88323253a..1c89f6a0b34 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -59,7 +59,6 @@ #include "DetuningHelper.h" #include "embed.h" #include "GuiApplication.h" -#include "gui_templates.h" #include "InstrumentTrack.h" #include "MainWindow.h" #include "MidiClip.h" @@ -161,6 +160,7 @@ PianoRoll::PianoRoll() : m_midiClip( nullptr ), m_currentPosition(), m_recording( false ), + m_doAutoQuantization(ConfigManager::inst()->value("midi", "autoquantize").toInt() != 0), m_currentNote( nullptr ), m_action( Action::None ), m_noteEditMode( NoteEditMode::Volume ), @@ -219,7 +219,7 @@ PianoRoll::PianoRoll() : m_noteEditMenu = new QMenu( this ); m_noteEditMenu->clear(); - for( int i = 0; i < m_nemStr.size(); ++i ) + for (auto i = std::size_t{0}; i < m_nemStr.size(); ++i) { auto act = new QAction(m_nemStr.at(i), this); connect( act, &QAction::triggered, [this, i](){ changeNoteEditMode(i); } ); @@ -241,6 +241,15 @@ PianoRoll::PianoRoll() : connect( markChordAction, &QAction::triggered, [this](){ markSemiTone(SemiToneMarkerAction::MarkCurrentChord); }); connect( unmarkAllAction, &QAction::triggered, [this](){ markSemiTone(SemiToneMarkerAction::UnmarkAll); }); connect( copyAllNotesAction, &QAction::triggered, [this](){ markSemiTone(SemiToneMarkerAction::CopyAllNotesOnKey); }); + connect(ConfigManager::inst(), &ConfigManager::valueChanged, + [this](QString const& cls, QString const& attribute, QString const& value) + { + if (!(cls == "midi" && attribute == "autoquantize")) + { + return; + } + this->m_doAutoQuantization = (value.toInt() != 0); + }); markScaleAction->setEnabled( false ); markChordAction->setEnabled( false ); @@ -305,7 +314,7 @@ PianoRoll::PianoRoll() : // setup zooming-stuff for( float const & zoomLevel : m_zoomLevels ) { - m_zoomingModel.addItem( QString( "%1\%" ).arg( zoomLevel * 100 ) ); + m_zoomingModel.addItem(QString("%1%").arg(zoomLevel * 100)); } m_zoomingModel.setValue( m_zoomingModel.findText( "100%" ) ); connect( &m_zoomingModel, SIGNAL(dataChanged()), @@ -314,7 +323,7 @@ PianoRoll::PianoRoll() : // zoom y for (float const & zoomLevel : m_zoomYLevels) { - m_zoomingYModel.addItem(QString( "%1\%" ).arg(zoomLevel * 100)); + m_zoomingYModel.addItem(QString("%1%").arg(zoomLevel * 100)); } m_zoomingYModel.setValue(m_zoomingYModel.findText("100%")); connect(&m_zoomingYModel, SIGNAL(dataChanged()), @@ -333,7 +342,7 @@ PianoRoll::PianoRoll() : // Set up note length model m_noteLenModel.addItem( tr( "Last note" ), std::make_unique( "edit_draw" ) ); - const auto pixmaps = std::array{"whole", "half", "quarter", "eighth", + const auto pixmaps = std::array{"whole", "half", "quarter", "eighth", "sixteenth", "thirtysecond", "triplethalf", "tripletquarter", "tripleteighth", "tripletsixteenth", "tripletthirtysecond"}; @@ -3030,10 +3039,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) if (hasValidMidiClip()) { - int pianoAreaHeight, partialKeyVisible, topKey, topNote; - pianoAreaHeight = keyAreaBottom() - keyAreaTop(); + int pianoAreaHeight = keyAreaBottom() - keyAreaTop(); m_pianoKeysVisible = pianoAreaHeight / m_keyLineHeight; - partialKeyVisible = pianoAreaHeight % m_keyLineHeight; + int partialKeyVisible = pianoAreaHeight % m_keyLineHeight; // check if we're below the minimum key area size if (m_pianoKeysVisible * m_keyLineHeight < KEY_AREA_MIN_HEIGHT) { @@ -3058,8 +3066,8 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) PR_TOP_MARGIN - PR_BOTTOM_MARGIN; partialKeyVisible = 0; } - topKey = qBound(0, m_startKey + m_pianoKeysVisible - 1, NumKeys - 1); - topNote = topKey % KeysPerOctave; + int topKey = std::clamp(m_startKey + m_pianoKeysVisible - 1, 0, NumKeys - 1); + int topNote = topKey % KeysPerOctave; // if not resizing the note edit area, we can change m_notesEditHeight if (m_action != Action::ResizeNoteEditArea && partialKeyVisible != 0) { @@ -3336,9 +3344,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) m_whiteKeyWidth, noteEditBottom() - keyAreaBottom()), bgColor); // display note editing info - //QFont f = p.font(); - f.setBold( false ); - p.setFont( pointSize<10>( f ) ); + f.setBold(false); + f.setPixelSize(10); + p.setFont(f); p.setPen(m_noteModeColor); p.drawText( QRect( 0, keyAreaBottom(), m_whiteKeyWidth, noteEditBottom() - keyAreaBottom()), @@ -3599,9 +3607,9 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) } else { - QFont f = p.font(); - f.setBold( true ); - p.setFont( pointSize<14>( f ) ); + QFont f = font(); + f.setBold(true); + p.setFont(f); p.setPen( QApplication::palette().color( QPalette::Active, QPalette::BrightText ) ); p.drawText(m_whiteKeyWidth + 20, PR_TOP_MARGIN + 40, @@ -3766,7 +3774,8 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) } if( nv.size() > 0 ) { - const int step = we->angleDelta().y() > 0 ? 1 : -1; + const int step = (we->angleDelta().y() > 0 ? 1 : -1) * (we->inverted() ? -1 : 1); + if( m_noteEditMode == NoteEditMode::Volume ) { for ( Note * n : nv ) @@ -4053,7 +4062,7 @@ void PianoRoll::stop() { Engine::getSong()->stop(); m_recording = false; - m_scrollBack = ( m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Enabled ); + m_scrollBack = m_timeLine->autoScroll() != TimeLineWidget::AutoScrollState::Disabled; } @@ -4110,8 +4119,13 @@ void PianoRoll::finishRecordNote(const Note & n ) Note n1(n.length(), it->pos(), it->key(), it->getVolume(), it->getPanning(), n.detuning()); - n1.quantizeLength( quantization() ); - m_midiClip->addNote( n1 ); + + if (m_doAutoQuantization) + { + n1.quantizeLength(quantization()); + n1.quantizePos(quantization()); + } + m_midiClip->addNote(n1, false); update(); m_recordingNotes.erase( it ); break; @@ -4449,30 +4463,36 @@ bool PianoRoll::deleteSelectedNotes() void PianoRoll::autoScroll( const TimePos & t ) { const int w = width() - m_whiteKeyWidth; - if( t > m_currentPosition + w * TimePos::ticksPerBar() / m_ppb ) + if (m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Stepped) { - m_leftRightScroll->setValue( t.getBar() * TimePos::ticksPerBar() ); + if (t > m_currentPosition + w * TimePos::ticksPerBar() / m_ppb) + { + m_leftRightScroll->setValue(t.getBar() * TimePos::ticksPerBar()); + } + else if (t < m_currentPosition) + { + TimePos t2 = std::max(t - w * TimePos::ticksPerBar() * + TimePos::ticksPerBar() / m_ppb, static_cast(0)); + m_leftRightScroll->setValue(t2.getBar() * TimePos::ticksPerBar()); + } } - else if( t < m_currentPosition ) + else if (m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Continuous) { - TimePos t2 = qMax( t - w * TimePos::ticksPerBar() * - TimePos::ticksPerBar() / m_ppb, (tick_t) 0 ); - m_leftRightScroll->setValue( t2.getBar() * TimePos::ticksPerBar() ); + m_leftRightScroll->setValue(std::max(t.getTicks() - w * TimePos::ticksPerBar() / m_ppb / 2, 0)); } m_scrollBack = false; } - -void PianoRoll::updatePosition( const TimePos & t ) +void PianoRoll::updatePosition(const TimePos & t) { - if( ( Engine::getSong()->isPlaying() + if ((Engine::getSong()->isPlaying() && Engine::getSong()->playMode() == Song::PlayMode::MidiClip - && m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Enabled - ) || m_scrollBack ) + && m_timeLine->autoScroll() != TimeLineWidget::AutoScrollState::Disabled + ) || m_scrollBack) { - autoScroll( t ); + autoScroll(t); } // ticks relative to m_currentPosition // < 0 = outside viewport left @@ -4881,7 +4901,7 @@ PianoRollWindow::PianoRollWindow() : m_quantizeComboBox = new ComboBox( m_toolBar ); m_quantizeComboBox->setModel( &m_editor->m_quantizeModel ); - m_quantizeComboBox->setFixedSize( 64, ComboBox::DEFAULT_HEIGHT ); + m_quantizeComboBox->setFixedSize(85, ComboBox::DEFAULT_HEIGHT); m_quantizeComboBox->setToolTip( tr( "Quantization") ); // setup note-len-stuff @@ -4905,7 +4925,7 @@ PianoRollWindow::PianoRollWindow() : m_scaleComboBox = new ComboBox( m_toolBar ); m_scaleComboBox->setModel( &m_editor->m_scaleModel ); - m_scaleComboBox->setFixedSize( 105, ComboBox::DEFAULT_HEIGHT ); + m_scaleComboBox->setFixedSize(155, ComboBox::DEFAULT_HEIGHT); m_scaleComboBox->setToolTip( tr( "Scale") ); // setup chord-stuff @@ -4914,7 +4934,7 @@ PianoRollWindow::PianoRollWindow() : m_chordComboBox = new ComboBox( m_toolBar ); m_chordComboBox->setModel( &m_editor->m_chordModel ); - m_chordComboBox->setFixedSize( 105, ComboBox::DEFAULT_HEIGHT ); + m_chordComboBox->setFixedSize(125, ComboBox::DEFAULT_HEIGHT); m_chordComboBox->setToolTip( tr( "Chord" ) ); // setup snap-stuff @@ -4923,7 +4943,7 @@ PianoRollWindow::PianoRollWindow() : m_snapComboBox = new ComboBox(m_toolBar); m_snapComboBox->setModel(&m_editor->m_snapModel); - m_snapComboBox->setFixedSize(105, ComboBox::DEFAULT_HEIGHT); + m_snapComboBox->setFixedSize(96, ComboBox::DEFAULT_HEIGHT); m_snapComboBox->setToolTip(tr("Snap mode")); // -- Clear ghost MIDI clip button diff --git a/src/gui/editors/PositionLine.cpp b/src/gui/editors/PositionLine.cpp index 8b938443d41..dda44e8f70a 100644 --- a/src/gui/editors/PositionLine.cpp +++ b/src/gui/editors/PositionLine.cpp @@ -93,7 +93,7 @@ void PositionLine::zoomChange(float zoom) { int playHeadPos = x() + width() - 1; - resize(8.0 * zoom, height()); + resize(std::max(8.0 * zoom, 1.0), height()); move(playHeadPos - width() + 1, y()); update(); diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index 400c03aa90c..f6e9fc83bf8 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -63,7 +63,7 @@ namespace lmms::gui namespace { -constexpr int MIN_PIXELS_PER_BAR = 2; +constexpr int MIN_PIXELS_PER_BAR = 4; constexpr int MAX_PIXELS_PER_BAR = 400; constexpr int ZOOM_STEPS = 200; @@ -142,17 +142,6 @@ SongEditor::SongEditor( Song * song ) : int tempoSpinBoxCol = getGUI()->mainWindow()->addWidgetToToolBar( m_tempoSpinBox, 0 ); -#if 0 - toolButton * hq_btn = new toolButton( embed::getIconPixmap( "hq_mode" ), - tr( "High quality mode" ), - nullptr, nullptr, tb ); - hq_btn->setCheckable( true ); - connect( hq_btn, SIGNAL(toggled(bool)), - this, SLOT(setHighQuality(bool))); - hq_btn->setFixedWidth( 42 ); - getGUI()->mainWindow()->addWidgetToToolBar( hq_btn, 1, col ); -#endif - getGUI()->mainWindow()->addWidgetToToolBar( new TimeDisplayWidget, 1, tempoSpinBoxCol ); getGUI()->mainWindow()->addSpacingToToolBar( 10 ); @@ -242,10 +231,10 @@ SongEditor::SongEditor( Song * song ) : static_cast( layout() )->insertWidget( 0, m_timeLine ); m_leftRightScroll = new QScrollBar( Qt::Horizontal, this ); - m_leftRightScroll->setMinimum( 0 ); - m_leftRightScroll->setMaximum( 0 ); - m_leftRightScroll->setSingleStep( 1 ); - m_leftRightScroll->setPageStep( 20 ); + m_leftRightScroll->setMinimum(0); + m_leftRightScroll->setMaximum(0); + m_leftRightScroll->setSingleStep(1); + m_leftRightScroll->setPageStep(20 * TimePos::ticksPerBar()); static_cast( layout() )->addWidget( m_leftRightScroll ); connect( m_leftRightScroll, SIGNAL(valueChanged(int)), this, SLOT(scrolled(int))); @@ -274,7 +263,7 @@ SongEditor::SongEditor( Song * song ) : m_snappingModel->addItem(QString("1/%1 Bar").arg(1 / bars)); } } - m_snappingModel->setInitValue( m_snappingModel->findText( "1 Bar" ) ); + m_snappingModel->setInitValue( m_snappingModel->findText( "1/4 Bar" ) ); setFocusPolicy( Qt::StrongFocus ); setFocus(); @@ -333,23 +322,10 @@ QString SongEditor::getSnapSizeString() const } } - - - -void SongEditor::setHighQuality( bool hq ) -{ - Engine::audioEngine()->changeQuality( AudioEngine::qualitySettings( - hq ? AudioEngine::qualitySettings::Mode::HighQuality : - AudioEngine::qualitySettings::Mode::Draft ) ); -} - - - - void SongEditor::scrolled( int new_pos ) { update(); - emit positionChanged( m_currentPosition = TimePos( new_pos, 0 ) ); + emit positionChanged(m_currentPosition = TimePos(new_pos)); } @@ -408,7 +384,7 @@ void SongEditor::updateRubberband() } //take care of the scrollbar position - int hs = (m_leftRightScroll->value() - m_scrollPos.x()) * pixelsPerBar(); + int hs = (m_leftRightScroll->value() - m_scrollPos.x()) * pixelsPerBar() / TimePos::ticksPerBar(); int vs = contentWidget()->verticalScrollBar()->value() - m_scrollPos.y(); //the adjusted origin point @@ -471,6 +447,8 @@ void SongEditor::toggleProportionalSnap() { m_proportionalSnap = !m_proportionalSnap; m_timeLine->setSnapSize(getSnapSize()); + + emit proportionalSnapChanged(); } @@ -544,8 +522,8 @@ void SongEditor::wheelEvent( QWheelEvent * we ) if ((we->modifiers() & Qt::ControlModifier) && (position(we).x() > m_trackHeadWidth)) { int x = position(we).x() - m_trackHeadWidth; - // bar based on the mouse x-position where the scroll wheel was used - int bar = x / pixelsPerBar(); + // tick based on the mouse x-position where the scroll wheel was used + int tick = x / pixelsPerBar() * TimePos::ticksPerBar(); // move zoom slider (pixelsPerBar will change automatically) int step = we->modifiers() & Qt::ShiftModifier ? 1 : 5; @@ -553,9 +531,9 @@ void SongEditor::wheelEvent( QWheelEvent * we ) int direction = (we->angleDelta().y() + we->angleDelta().x()) > 0 ? 1 : -1; m_zoomingModel->incValue(step * direction); - // scroll to zooming around cursor's bar - int newBar = static_cast(x / pixelsPerBar()); - m_leftRightScroll->setValue(m_leftRightScroll->value() + bar - newBar); + // scroll to zooming around cursor's tick + int newTick = static_cast(x / pixelsPerBar() * TimePos::ticksPerBar()); + m_leftRightScroll->setValue(m_leftRightScroll->value() + tick - newTick); // update timeline m_timeLine->setPixelsPerBar(pixelsPerBar()); @@ -564,15 +542,15 @@ void SongEditor::wheelEvent( QWheelEvent * we ) } // FIXME: Reconsider if determining orientation is necessary in Qt6. - else if(abs(we->angleDelta().x()) > abs(we->angleDelta().y())) // scrolling is horizontal + else if (std::abs(we->angleDelta().x()) > std::abs(we->angleDelta().y())) // scrolling is horizontal { - m_leftRightScroll->setValue(m_leftRightScroll->value() - - we->angleDelta().x() /30); + m_leftRightScroll->setValue(m_leftRightScroll->value() + - we->angleDelta().x()); } - else if(we->modifiers() & Qt::ShiftModifier) + else if (we->modifiers() & Qt::ShiftModifier) { - m_leftRightScroll->setValue(m_leftRightScroll->value() - - we->angleDelta().y() / 30); + m_leftRightScroll->setValue(m_leftRightScroll->value() + - we->angleDelta().y()); } else { @@ -733,9 +711,9 @@ void SongEditor::hideMasterPitchFloat( void ) -void SongEditor::updateScrollBar( int len ) +void SongEditor::updateScrollBar(int len) { - m_leftRightScroll->setMaximum( len ); + m_leftRightScroll->setMaximum(len * TimePos::ticksPerBar()); } @@ -774,34 +752,29 @@ static inline void animateScroll( QScrollBar *scrollBar, int newVal, bool smooth void SongEditor::updatePosition( const TimePos & t ) { - int widgetWidth, trackOpWidth; - if( ConfigManager::inst()->value( "ui", "compacttrackbuttons" ).toInt() ) - { - widgetWidth = DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT; - trackOpWidth = TRACK_OP_WIDTH_COMPACT; - } - else - { - widgetWidth = DEFAULT_SETTINGS_WIDGET_WIDTH; - trackOpWidth = TRACK_OP_WIDTH; - } + const bool compactTrackButtons = ConfigManager::inst()->value("ui", "compacttrackbuttons").toInt(); + const auto widgetWidth = compactTrackButtons ? DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT : DEFAULT_SETTINGS_WIDGET_WIDTH; + const auto trackOpWidth = compactTrackButtons ? TRACK_OP_WIDTH_COMPACT : TRACK_OP_WIDTH; - if( ( m_song->isPlaying() && m_song->m_playMode == Song::PlayMode::Song - && m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Enabled) || - m_scrollBack == true ) + if ((m_song->isPlaying() && m_song->m_playMode == Song::PlayMode::Song) + || m_scrollBack) { m_smoothScroll = ConfigManager::inst()->value( "ui", "smoothscroll" ).toInt(); const int w = width() - widgetWidth - trackOpWidth - contentWidget()->verticalScrollBar()->width(); // width of right scrollbar - if( t > m_currentPosition + w * TimePos::ticksPerBar() / - pixelsPerBar() ) + + if (m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Stepped) { - animateScroll( m_leftRightScroll, t.getBar(), m_smoothScroll ); + const auto nextPosition = m_currentPosition + w * TimePos::ticksPerBar() / pixelsPerBar(); + if (t > nextPosition || t < m_currentPosition) + { + animateScroll(m_leftRightScroll, t.getTicks(), m_smoothScroll); + } } - else if( t < m_currentPosition ) + else if (m_timeLine->autoScroll() == TimeLineWidget::AutoScrollState::Continuous) { - animateScroll( m_leftRightScroll, t.getBar(), m_smoothScroll ); + m_leftRightScroll->setValue(std::max(t.getTicks() - w * TimePos::ticksPerBar() / pixelsPerBar() / 2, 0.0f)); } m_scrollBack = false; } diff --git a/src/gui/editors/TimeLineWidget.cpp b/src/gui/editors/TimeLineWidget.cpp index 7657e2916cd..f75b1cabfac 100644 --- a/src/gui/editors/TimeLineWidget.cpp +++ b/src/gui/editors/TimeLineWidget.cpp @@ -88,9 +88,10 @@ void TimeLineWidget::setXOffset(const int x) void TimeLineWidget::addToolButtons( QToolBar * _tool_bar ) { auto autoScroll = new NStateButton(_tool_bar); - autoScroll->setGeneralToolTip( tr( "Auto scrolling" ) ); - autoScroll->addState( embed::getIconPixmap( "autoscroll_on" ) ); - autoScroll->addState( embed::getIconPixmap( "autoscroll_off" ) ); + autoScroll->setGeneralToolTip(tr("Auto scrolling")); + autoScroll->addState(embed::getIconPixmap("autoscroll_stepped_on"), tr("Stepped auto scrolling")); + autoScroll->addState(embed::getIconPixmap("autoscroll_continuous_on"), tr("Continuous auto scrolling")); + autoScroll->addState(embed::getIconPixmap("autoscroll_off"), tr("Auto scrolling disabled")); connect( autoScroll, SIGNAL(changedState(int)), this, SLOT(toggleAutoScroll(int))); diff --git a/src/gui/editors/TrackContainerView.cpp b/src/gui/editors/TrackContainerView.cpp index 60a46838041..39eb41f34db 100644 --- a/src/gui/editors/TrackContainerView.cpp +++ b/src/gui/editors/TrackContainerView.cpp @@ -89,11 +89,15 @@ TrackContainerView::TrackContainerView( TrackContainer * _tc ) : m_tc->setHook( this ); //keeps the direction of the widget, undepended on the locale setLayoutDirection( Qt::LeftToRight ); + + // The main layout - by default it only contains the scroll area, + // but SongEditor uses the layout to add a TimeLineWidget on top auto layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing( 0 ); layout->addWidget( m_scrollArea ); + // The widget that will contain all TrackViews auto scrollContent = new QWidget; m_scrollLayout = new QVBoxLayout( scrollContent ); m_scrollLayout->setContentsMargins(0, 0, 0, 0); @@ -101,6 +105,7 @@ TrackContainerView::TrackContainerView( TrackContainer * _tc ) : m_scrollLayout->setSizeConstraint( QLayout::SetMinAndMaxSize ); m_scrollArea->setWidget( scrollContent ); + m_scrollArea->setWidgetResizable(true); m_scrollArea->show(); m_rubberBand->hide(); @@ -254,10 +259,6 @@ void TrackContainerView::scrollToTrackView( TrackView * _tv ) void TrackContainerView::realignTracks() { - m_scrollArea->widget()->setFixedWidth(width()); - m_scrollArea->widget()->setFixedHeight( - m_scrollArea->widget()->minimumSizeHint().height()); - for (const auto& trackView : m_trackViews) { trackView->show(); @@ -447,15 +448,6 @@ void TrackContainerView::dropEvent( QDropEvent * _de ) -void TrackContainerView::resizeEvent( QResizeEvent * _re ) -{ - realignTracks(); - QWidget::resizeEvent( _re ); -} - - - - RubberBand *TrackContainerView::rubberBand() const { return m_rubberBand; diff --git a/src/gui/embed.cpp b/src/gui/embed.cpp index d934adcde64..e912fd6d4cd 100644 --- a/src/gui/embed.cpp +++ b/src/gui/embed.cpp @@ -22,68 +22,62 @@ * */ +#include "embed.h" + #include +#include #include #include #include -#include "embed.h" -namespace lmms::embed -{ +namespace lmms::embed { -QPixmap getIconPixmap(const QString& pixmapName, - int width, int height, const char** xpm ) +namespace { + +auto loadPixmap(const QString& name, int width, int height, const char* const* xpm) -> QPixmap { - QString cacheName; - if (width > 0 && height > 0) - { - cacheName = QString("%1_%2_%3").arg(pixmapName, width, height); - } - else - { - cacheName = pixmapName; - } + if (xpm) { return QPixmap{xpm}; } - // Return cached pixmap - QPixmap pixmap; - if( QPixmapCache::find(cacheName, &pixmap) ) - { - return pixmap; - } + const auto resourceName = QDir::isAbsolutePath(name) ? name : "artwork:" + name; + auto reader = QImageReader{resourceName}; + if (width > 0 && height > 0) { reader.setScaledSize(QSize{width, height}); } - if(xpm) - { - pixmap = QPixmap(xpm); + const auto pixmap = QPixmap::fromImageReader(&reader); + if (pixmap.isNull()) { + qWarning().nospace() << "Error loading icon pixmap " << name << ": " << reader.errorString(); + return QPixmap{1, 1}; } - else - { - QImageReader reader(QString("artwork:%1").arg(pixmapName)); + return pixmap; +} - if (width > 0 && height > 0) - { - reader.setScaledSize(QSize(width, height)); - } +} // namespace - pixmap = QPixmap::fromImageReader(&reader); +auto getIconPixmap(std::string_view name, int width, int height, const char* const* xpm) -> QPixmap +{ + if (name.empty()) { return QPixmap{}; } - if (pixmap.isNull()) - { - qWarning().nospace() << "Error loading icon pixmap " << pixmapName << ": " << - reader.errorString().toLocal8Bit().data(); - return QPixmap(1,1); - } - } + const auto pixmapName = QString::fromUtf8(name.data(), name.size()); + const auto cacheName = (width > 0 && height > 0) + ? QStringLiteral("%1_%2_%3").arg(pixmapName, width, height) + : pixmapName; - // Save to cache and return + // Return cached pixmap if it exists + if (auto pixmap = QPixmap{}; QPixmapCache::find(cacheName, &pixmap)) { return pixmap; } + + // Load the pixmap and cache it before returning + const auto pixmap = loadPixmap(pixmapName, width, height, xpm); QPixmapCache::insert(cacheName, pixmap); return pixmap; } - -QString getText( const char * name ) +auto getText(std::string_view name) -> QString { - return QString::fromUtf8( (const char*) QResource(QString(":/%1").arg(name)).data()); + const auto resource = QResource{":/" + QString::fromUtf8(name.data(), name.size())}; +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + return QString::fromUtf8(resource.uncompressedData()); +#else + return QString::fromUtf8(reinterpret_cast(resource.data()), resource.size()); +#endif } - } // namespace lmms::embed diff --git a/src/gui/instrument/EnvelopeAndLfoView.cpp b/src/gui/instrument/EnvelopeAndLfoView.cpp index c3bf53b39a4..1b639e6c326 100644 --- a/src/gui/instrument/EnvelopeAndLfoView.cpp +++ b/src/gui/instrument/EnvelopeAndLfoView.cpp @@ -23,20 +23,20 @@ * */ -#include -#include - #include "EnvelopeAndLfoView.h" + +#include + +#include + +#include "EnvelopeGraph.h" +#include "LfoGraph.h" #include "EnvelopeAndLfoParameters.h" #include "SampleLoader.h" -#include "embed.h" -#include "Engine.h" #include "gui_templates.h" #include "Knob.h" #include "LedCheckBox.h" -#include "AudioEngine.h" #include "DataFile.h" -#include "Oscillator.h" #include "PixmapButton.h" #include "StringPairDrag.h" #include "TempoSyncKnob.h" @@ -46,182 +46,148 @@ namespace lmms { -extern const float SECS_PER_ENV_SEGMENT; -extern const float SECS_PER_LFO_OSCILLATION; - - namespace gui { - -const int ENV_GRAPH_X = 6; -const int ENV_GRAPH_Y = 6; - -const int ENV_KNOBS_Y = 43; -const int ENV_KNOBS_LBL_Y = ENV_KNOBS_Y+35; -const int KNOB_X_SPACING = 32; -const int PREDELAY_KNOB_X = 6; -const int ATTACK_KNOB_X = PREDELAY_KNOB_X+KNOB_X_SPACING; -const int HOLD_KNOB_X = ATTACK_KNOB_X+KNOB_X_SPACING; -const int DECAY_KNOB_X = HOLD_KNOB_X+KNOB_X_SPACING; -const int SUSTAIN_KNOB_X = DECAY_KNOB_X+KNOB_X_SPACING; -const int RELEASE_KNOB_X = SUSTAIN_KNOB_X+KNOB_X_SPACING; -const int AMOUNT_KNOB_X = RELEASE_KNOB_X+KNOB_X_SPACING; - -const int TIME_UNIT_WIDTH = 40; - -const int LFO_GRAPH_X = 6; -const int LFO_GRAPH_Y = ENV_KNOBS_LBL_Y+14; -const int LFO_KNOB_Y = LFO_GRAPH_Y-2; -const int LFO_PREDELAY_KNOB_X = LFO_GRAPH_X + 100; -const int LFO_ATTACK_KNOB_X = LFO_PREDELAY_KNOB_X+KNOB_X_SPACING; -const int LFO_SPEED_KNOB_X = LFO_ATTACK_KNOB_X+KNOB_X_SPACING; -const int LFO_AMOUNT_KNOB_X = LFO_SPEED_KNOB_X+KNOB_X_SPACING; -const int LFO_SHAPES_X = LFO_GRAPH_X;//PREDELAY_KNOB_X; -const int LFO_SHAPES_Y = LFO_GRAPH_Y + 50; - -EnvelopeAndLfoView::EnvelopeAndLfoView( QWidget * _parent ) : - QWidget( _parent ), - ModelView( nullptr, this ), - m_params( nullptr ) +EnvelopeAndLfoView::EnvelopeAndLfoView(QWidget * parent) : + QWidget(parent), + ModelView(nullptr, this), + m_params(nullptr) { + // Helper lambdas for consistent repeated buiding of certain widgets + auto buildKnob = [&](const QString& label, const QString& hintText) + { + auto knob = new Knob(KnobType::Bright26, this); + knob->setLabel(label); + knob->setHintText(hintText, ""); + + return knob; + }; + + auto buildPixmapButton = [&](std::string_view activePixmap, std::string_view inactivePixmap) + { + auto button = new PixmapButton(this, nullptr); + button->setActiveGraphic(embed::getIconPixmap(activePixmap)); + button->setInactiveGraphic(embed::getIconPixmap(inactivePixmap)); - m_predelayKnob = new Knob( KnobType::Bright26, this ); - m_predelayKnob->setLabel( tr( "DEL" ) ); - m_predelayKnob->move( PREDELAY_KNOB_X, ENV_KNOBS_Y ); - m_predelayKnob->setHintText( tr( "Pre-delay:" ), "" ); + return button; + }; + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(5, 5, 5, 5); - m_attackKnob = new Knob( KnobType::Bright26, this ); - m_attackKnob->setLabel( tr( "ATT" ) ); - m_attackKnob->move( ATTACK_KNOB_X, ENV_KNOBS_Y ); - m_attackKnob->setHintText( tr( "Attack:" ), "" ); + // Envelope + QVBoxLayout* envelopeLayout = new QVBoxLayout(); + mainLayout->addLayout(envelopeLayout); - m_holdKnob = new Knob( KnobType::Bright26, this ); - m_holdKnob->setLabel( tr( "HOLD" ) ); - m_holdKnob->move( HOLD_KNOB_X, ENV_KNOBS_Y ); - m_holdKnob->setHintText( tr( "Hold:" ), "" ); + QHBoxLayout* graphAndAmountLayout = new QHBoxLayout(); + envelopeLayout->addLayout(graphAndAmountLayout); + m_envelopeGraph = new EnvelopeGraph(this); + graphAndAmountLayout->addWidget(m_envelopeGraph); - m_decayKnob = new Knob( KnobType::Bright26, this ); - m_decayKnob->setLabel( tr( "DEC" ) ); - m_decayKnob->move( DECAY_KNOB_X, ENV_KNOBS_Y ); - m_decayKnob->setHintText( tr( "Decay:" ), "" ); + m_amountKnob = buildKnob(tr("AMT"), tr("Modulation amount:")); + graphAndAmountLayout->addWidget(m_amountKnob, 0, Qt::AlignCenter); + QHBoxLayout* envKnobsLayout = new QHBoxLayout(); + envelopeLayout->addLayout(envKnobsLayout); - m_sustainKnob = new Knob( KnobType::Bright26, this ); - m_sustainKnob->setLabel( tr( "SUST" ) ); - m_sustainKnob->move( SUSTAIN_KNOB_X, ENV_KNOBS_Y ); - m_sustainKnob->setHintText( tr( "Sustain:" ), "" ); + m_predelayKnob = buildKnob(tr("DEL"), tr("Pre-delay:")); + envKnobsLayout->addWidget(m_predelayKnob); + m_attackKnob = buildKnob(tr("ATT"), tr("Attack:")); + envKnobsLayout->addWidget(m_attackKnob); - m_releaseKnob = new Knob( KnobType::Bright26, this ); - m_releaseKnob->setLabel( tr( "REL" ) ); - m_releaseKnob->move( RELEASE_KNOB_X, ENV_KNOBS_Y ); - m_releaseKnob->setHintText( tr( "Release:" ), "" ); + m_holdKnob = buildKnob(tr("HOLD"), tr("Hold:")); + envKnobsLayout->addWidget(m_holdKnob); + m_decayKnob = buildKnob(tr("DEC"), tr("Decay:")); + envKnobsLayout->addWidget(m_decayKnob); - m_amountKnob = new Knob( KnobType::Bright26, this ); - m_amountKnob->setLabel( tr( "AMT" ) ); - m_amountKnob->move( AMOUNT_KNOB_X, ENV_GRAPH_Y ); - m_amountKnob->setHintText( tr( "Modulation amount:" ), "" ); + m_sustainKnob = buildKnob(tr("SUST"), tr("Sustain:")); + envKnobsLayout->addWidget(m_sustainKnob); + m_releaseKnob = buildKnob(tr("REL"), tr("Release:")); + envKnobsLayout->addWidget(m_releaseKnob); + // Add some space between the envelope and LFO section + mainLayout->addSpacing(10); - m_lfoPredelayKnob = new Knob( KnobType::Bright26, this ); - m_lfoPredelayKnob->setLabel( tr( "DEL" ) ); - m_lfoPredelayKnob->move( LFO_PREDELAY_KNOB_X, LFO_KNOB_Y ); - m_lfoPredelayKnob->setHintText( tr( "Pre-delay:" ), "" ); + // LFO - m_lfoAttackKnob = new Knob( KnobType::Bright26, this ); - m_lfoAttackKnob->setLabel( tr( "ATT" ) ); - m_lfoAttackKnob->move( LFO_ATTACK_KNOB_X, LFO_KNOB_Y ); - m_lfoAttackKnob->setHintText( tr( "Attack:" ), "" ); + QHBoxLayout* lfoLayout = new QHBoxLayout(); + mainLayout->addLayout(lfoLayout); + QVBoxLayout* graphAndTypesLayout = new QVBoxLayout(); + lfoLayout->addLayout(graphAndTypesLayout); - m_lfoSpeedKnob = new TempoSyncKnob( KnobType::Bright26, this ); - m_lfoSpeedKnob->setLabel( tr( "SPD" ) ); - m_lfoSpeedKnob->move( LFO_SPEED_KNOB_X, LFO_KNOB_Y ); - m_lfoSpeedKnob->setHintText( tr( "Frequency:" ), "" ); + m_lfoGraph = new LfoGraph(this); + graphAndTypesLayout->addWidget(m_lfoGraph); + QHBoxLayout* typesLayout = new QHBoxLayout(); + graphAndTypesLayout->addLayout(typesLayout); + typesLayout->setContentsMargins(0, 0, 0, 0); + typesLayout->setSpacing(0); - m_lfoAmountKnob = new Knob( KnobType::Bright26, this ); - m_lfoAmountKnob->setLabel( tr( "AMT" ) ); - m_lfoAmountKnob->move( LFO_AMOUNT_KNOB_X, LFO_KNOB_Y ); - m_lfoAmountKnob->setHintText( tr( "Modulation amount:" ), "" ); + auto sin_lfo_btn = buildPixmapButton("sin_wave_active", "sin_wave_inactive"); + auto triangle_lfo_btn = buildPixmapButton("triangle_wave_active", "triangle_wave_inactive"); + auto saw_lfo_btn = buildPixmapButton("saw_wave_active", "saw_wave_inactive"); + auto sqr_lfo_btn = buildPixmapButton("square_wave_active","square_wave_inactive"); + auto random_lfo_btn = buildPixmapButton("random_wave_active", "random_wave_inactive"); + m_userLfoBtn = buildPixmapButton("usr_wave_active", "usr_wave_inactive"); - auto sin_lfo_btn = new PixmapButton(this, nullptr); - sin_lfo_btn->move( LFO_SHAPES_X, LFO_SHAPES_Y ); - sin_lfo_btn->setActiveGraphic( embed::getIconPixmap( - "sin_wave_active" ) ); - sin_lfo_btn->setInactiveGraphic( embed::getIconPixmap( - "sin_wave_inactive" ) ); + connect(m_userLfoBtn, SIGNAL(toggled(bool)), this, SLOT(lfoUserWaveChanged())); - auto triangle_lfo_btn = new PixmapButton(this, nullptr); - triangle_lfo_btn->move( LFO_SHAPES_X+15, LFO_SHAPES_Y ); - triangle_lfo_btn->setActiveGraphic( embed::getIconPixmap( - "triangle_wave_active" ) ); - triangle_lfo_btn->setInactiveGraphic( embed::getIconPixmap( - "triangle_wave_inactive" ) ); + typesLayout->addWidget(sin_lfo_btn); + typesLayout->addWidget(triangle_lfo_btn); + typesLayout->addWidget(saw_lfo_btn); + typesLayout->addWidget(sqr_lfo_btn); + typesLayout->addWidget(random_lfo_btn); + typesLayout->addWidget(m_userLfoBtn); - auto saw_lfo_btn = new PixmapButton(this, nullptr); - saw_lfo_btn->move( LFO_SHAPES_X+30, LFO_SHAPES_Y ); - saw_lfo_btn->setActiveGraphic( embed::getIconPixmap( - "saw_wave_active" ) ); - saw_lfo_btn->setInactiveGraphic( embed::getIconPixmap( - "saw_wave_inactive" ) ); + m_lfoWaveBtnGrp = new automatableButtonGroup(this); + m_lfoWaveBtnGrp->addButton(sin_lfo_btn); + m_lfoWaveBtnGrp->addButton(triangle_lfo_btn); + m_lfoWaveBtnGrp->addButton(saw_lfo_btn); + m_lfoWaveBtnGrp->addButton(sqr_lfo_btn); + m_lfoWaveBtnGrp->addButton(m_userLfoBtn); + m_lfoWaveBtnGrp->addButton(random_lfo_btn); - auto sqr_lfo_btn = new PixmapButton(this, nullptr); - sqr_lfo_btn->move( LFO_SHAPES_X+45, LFO_SHAPES_Y ); - sqr_lfo_btn->setActiveGraphic( embed::getIconPixmap( - "square_wave_active" ) ); - sqr_lfo_btn->setInactiveGraphic( embed::getIconPixmap( - "square_wave_inactive" ) ); + QVBoxLayout* knobsAndCheckBoxesLayout = new QVBoxLayout(); + lfoLayout->addLayout(knobsAndCheckBoxesLayout); - m_userLfoBtn = new PixmapButton( this, nullptr ); - m_userLfoBtn->move( LFO_SHAPES_X+75, LFO_SHAPES_Y ); - m_userLfoBtn->setActiveGraphic( embed::getIconPixmap( - "usr_wave_active" ) ); - m_userLfoBtn->setInactiveGraphic( embed::getIconPixmap( - "usr_wave_inactive" ) ); + QHBoxLayout* lfoKnobsLayout = new QHBoxLayout(); + knobsAndCheckBoxesLayout->addLayout(lfoKnobsLayout); - connect( m_userLfoBtn, SIGNAL(toggled(bool)), - this, SLOT(lfoUserWaveChanged())); + m_lfoPredelayKnob = buildKnob(tr("DEL"), tr("Pre-delay:")); + lfoKnobsLayout->addWidget(m_lfoPredelayKnob); - auto random_lfo_btn = new PixmapButton(this, nullptr); - random_lfo_btn->move( LFO_SHAPES_X+60, LFO_SHAPES_Y ); - random_lfo_btn->setActiveGraphic( embed::getIconPixmap( - "random_wave_active" ) ); - random_lfo_btn->setInactiveGraphic( embed::getIconPixmap( - "random_wave_inactive" ) ); + m_lfoAttackKnob = buildKnob(tr("ATT"), tr("Attack:")); + lfoKnobsLayout->addWidget(m_lfoAttackKnob); - m_lfoWaveBtnGrp = new automatableButtonGroup( this ); - m_lfoWaveBtnGrp->addButton( sin_lfo_btn ); - m_lfoWaveBtnGrp->addButton( triangle_lfo_btn ); - m_lfoWaveBtnGrp->addButton( saw_lfo_btn ); - m_lfoWaveBtnGrp->addButton( sqr_lfo_btn ); - m_lfoWaveBtnGrp->addButton( m_userLfoBtn ); - m_lfoWaveBtnGrp->addButton( random_lfo_btn ); - - m_x100Cb = new LedCheckBox( tr( "FREQ x 100" ), this ); - m_x100Cb->setFont( pointSizeF( m_x100Cb->font(), 6.5 ) ); - m_x100Cb->move( LFO_PREDELAY_KNOB_X, LFO_GRAPH_Y + 36 ); - m_x100Cb->setToolTip(tr("Multiply LFO frequency by 100")); + m_lfoSpeedKnob = new TempoSyncKnob(KnobType::Bright26, this); + m_lfoSpeedKnob->setLabel(tr("SPD")); + m_lfoSpeedKnob->setHintText(tr("Frequency:"), ""); + lfoKnobsLayout->addWidget(m_lfoSpeedKnob); + m_lfoAmountKnob = buildKnob(tr("AMT"), tr("Modulation amount:")); + lfoKnobsLayout->addWidget(m_lfoAmountKnob); - m_controlEnvAmountCb = new LedCheckBox( tr( "MODULATE ENV AMOUNT" ), - this ); - m_controlEnvAmountCb->move( LFO_PREDELAY_KNOB_X, LFO_GRAPH_Y + 54 ); - m_controlEnvAmountCb->setFont( pointSizeF( m_controlEnvAmountCb->font(), 6.5 ) ); - m_controlEnvAmountCb->setToolTip( - tr( "Control envelope amount by this LFO" ) ); + QVBoxLayout* checkBoxesLayout = new QVBoxLayout(); + knobsAndCheckBoxesLayout->addLayout(checkBoxesLayout); + m_x100Cb = new LedCheckBox(tr("FREQ x 100"), this); + m_x100Cb->setToolTip(tr("Multiply LFO frequency by 100")); + checkBoxesLayout->addWidget(m_x100Cb); - setAcceptDrops( true ); + m_controlEnvAmountCb = new LedCheckBox(tr("MOD ENV AMOUNT"), this); + m_controlEnvAmountCb->setToolTip(tr("Control envelope amount by this LFO")); + checkBoxesLayout->addWidget(m_controlEnvAmountCb); + setAcceptDrops(true); } @@ -238,6 +204,7 @@ EnvelopeAndLfoView::~EnvelopeAndLfoView() void EnvelopeAndLfoView::modelChanged() { m_params = castModel(); + m_envelopeGraph->setModel(m_params); m_predelayKnob->setModel( &m_params->m_predelayModel ); m_attackKnob->setModel( &m_params->m_attackModel ); m_holdKnob->setModel( &m_params->m_holdModel ); @@ -245,6 +212,8 @@ void EnvelopeAndLfoView::modelChanged() m_sustainKnob->setModel( &m_params->m_sustainModel ); m_releaseKnob->setModel( &m_params->m_releaseModel ); m_amountKnob->setModel( &m_params->m_amountModel ); + + m_lfoGraph->setModel(m_params); m_lfoPredelayKnob->setModel( &m_params->m_lfoPredelayModel ); m_lfoAttackKnob->setModel( &m_params->m_lfoAttackModel ); m_lfoSpeedKnob->setModel( &m_params->m_lfoSpeedModel ); @@ -257,40 +226,6 @@ void EnvelopeAndLfoView::modelChanged() -void EnvelopeAndLfoView::mousePressEvent( QMouseEvent * _me ) -{ - if( _me->button() != Qt::LeftButton ) - { - return; - } - - if (QRect(ENV_GRAPH_X, ENV_GRAPH_Y, m_envGraph.width(), m_envGraph.height()).contains(_me->pos())) - { - if( m_params->m_amountModel.value() < 1.0f ) - { - m_params->m_amountModel.setValue( 1.0f ); - } - else - { - m_params->m_amountModel.setValue( 0.0f ); - } - } - else if (QRect(LFO_GRAPH_X, LFO_GRAPH_Y, m_lfoGraph.width(), m_lfoGraph.height()).contains(_me->pos())) - { - if( m_params->m_lfoAmountModel.value() < 1.0f ) - { - m_params->m_lfoAmountModel.setValue( 1.0f ); - } - else - { - m_params->m_lfoAmountModel.setValue( 0.0f ); - } - } -} - - - - void EnvelopeAndLfoView::dragEnterEvent( QDragEnterEvent * _dee ) { StringPairDrag::processDragEnterEvent( _dee, @@ -330,167 +265,6 @@ void EnvelopeAndLfoView::dropEvent( QDropEvent * _de ) -void EnvelopeAndLfoView::paintEvent( QPaintEvent * ) -{ - QPainter p( this ); - p.setRenderHint( QPainter::Antialiasing ); - - // draw envelope-graph - p.drawPixmap(ENV_GRAPH_X, ENV_GRAPH_Y, m_envGraph); - // draw LFO-graph - p.drawPixmap(LFO_GRAPH_X, LFO_GRAPH_Y, m_lfoGraph); - - p.setFont( pointSize<8>( p.font() ) ); - - const float gray_amount = 1.0f - fabsf( m_amountKnob->value() ); - - p.setPen( QPen( QColor( static_cast( 96 * gray_amount ), - static_cast( 255 - 159 * gray_amount ), - static_cast( 128 - 32 * gray_amount ) ), - 2 ) ); - - const QColor end_points_color( 0x99, 0xAF, 0xFF ); - const QColor end_points_bg_color( 0, 0, 2 ); - - const int y_base = ENV_GRAPH_Y + m_envGraph.height() - 3; - const int avail_height = m_envGraph.height() - 6; - - int x1 = static_cast( m_predelayKnob->value() * TIME_UNIT_WIDTH ); - int x2 = x1 + static_cast( m_attackKnob->value() * TIME_UNIT_WIDTH ); - int x3 = x2 + static_cast( m_holdKnob->value() * TIME_UNIT_WIDTH ); - int x4 = x3 + static_cast( ( m_decayKnob->value() * - ( 1 - m_sustainKnob->value() ) ) * TIME_UNIT_WIDTH ); - int x5 = x4 + static_cast( m_releaseKnob->value() * TIME_UNIT_WIDTH ); - - if( x5 > 174 ) - { - x1 = ( x1 * 174 ) / x5; - x2 = ( x2 * 174 ) / x5; - x3 = ( x3 * 174 ) / x5; - x4 = ( x4 * 174 ) / x5; - x5 = ( x5 * 174 ) / x5; - } - x1 += ENV_GRAPH_X + 2; - x2 += ENV_GRAPH_X + 2; - x3 += ENV_GRAPH_X + 2; - x4 += ENV_GRAPH_X + 2; - x5 += ENV_GRAPH_X + 2; - - p.drawLine( x1, y_base, x2, y_base - avail_height ); - p.fillRect( x1 - 1, y_base - 2, 4, 4, end_points_bg_color ); - p.fillRect( x1, y_base - 1, 2, 2, end_points_color ); - - p.drawLine( x2, y_base - avail_height, x3, y_base - avail_height ); - p.fillRect( x2 - 1, y_base - 2 - avail_height, 4, 4, - end_points_bg_color ); - p.fillRect( x2, y_base - 1 - avail_height, 2, 2, end_points_color ); - - p.drawLine( x3, y_base-avail_height, x4, static_cast( y_base - - avail_height + - ( 1 - m_sustainKnob->value() ) * avail_height ) ); - p.fillRect( x3 - 1, y_base - 2 - avail_height, 4, 4, - end_points_bg_color ); - p.fillRect( x3, y_base - 1 - avail_height, 2, 2, end_points_color ); - - p.drawLine( x4, static_cast( y_base - avail_height + - ( 1 - m_sustainKnob->value() ) * - avail_height ), x5, y_base ); - p.fillRect( x4 - 1, static_cast( y_base - avail_height + - ( 1 - m_sustainKnob->value() ) * - avail_height ) - 2, 4, 4, - end_points_bg_color ); - p.fillRect( x4, static_cast( y_base - avail_height + - ( 1 - m_sustainKnob->value() ) * - avail_height ) - 1, 2, 2, - end_points_color ); - p.fillRect( x5 - 1, y_base - 2, 4, 4, end_points_bg_color ); - p.fillRect( x5, y_base - 1, 2, 2, end_points_color ); - - int LFO_GRAPH_W = m_lfoGraph.width() - 3; // subtract border - int LFO_GRAPH_H = m_lfoGraph.height() - 6; // subtract border - int graph_x_base = LFO_GRAPH_X + 2; - int graph_y_base = LFO_GRAPH_Y + 3 + LFO_GRAPH_H / 2; - - const float frames_for_graph = SECS_PER_LFO_OSCILLATION * - Engine::audioEngine()->baseSampleRate() / 10; - - const float lfo_gray_amount = 1.0f - fabsf( m_lfoAmountKnob->value() ); - p.setPen( QPen( QColor( static_cast( 96 * lfo_gray_amount ), - static_cast( 255 - 159 * lfo_gray_amount ), - static_cast( 128 - 32 * - lfo_gray_amount ) ), - 1.5 ) ); - - - float osc_frames = m_params->m_lfoOscillationFrames; - - if( m_params->m_x100Model.value() ) - { - osc_frames *= 100.0f; - } - - float old_y = 0; - for( int x = 0; x <= LFO_GRAPH_W; ++x ) - { - float val = 0.0; - float cur_sample = x * frames_for_graph / LFO_GRAPH_W; - if( static_cast( cur_sample ) > - m_params->m_lfoPredelayFrames ) - { - float phase = ( cur_sample -= - m_params->m_lfoPredelayFrames ) / - osc_frames; - switch( static_cast(m_params->m_lfoWaveModel.value()) ) - { - case EnvelopeAndLfoParameters::LfoShape::SineWave: - default: - val = Oscillator::sinSample( phase ); - break; - case EnvelopeAndLfoParameters::LfoShape::TriangleWave: - val = Oscillator::triangleSample( - phase ); - break; - case EnvelopeAndLfoParameters::LfoShape::SawWave: - val = Oscillator::sawSample( phase ); - break; - case EnvelopeAndLfoParameters::LfoShape::SquareWave: - val = Oscillator::squareSample( phase ); - break; - case EnvelopeAndLfoParameters::LfoShape::RandomWave: - if( x % (int)( 900 * m_lfoSpeedKnob->value() + 1 ) == 0 ) - { - m_randomGraph = Oscillator::noiseSample( 0.0f ); - } - val = m_randomGraph; - break; - case EnvelopeAndLfoParameters::LfoShape::UserDefinedWave: - val = Oscillator::userWaveSample(m_params->m_userWave.get(), phase); - break; - } - if( static_cast( cur_sample ) <= - m_params->m_lfoAttackFrames ) - { - val *= cur_sample / m_params->m_lfoAttackFrames; - } - } - float cur_y = -LFO_GRAPH_H / 2.0f * val; - p.drawLine( QLineF( graph_x_base + x - 1, graph_y_base + old_y, - graph_x_base + x, - graph_y_base + cur_y ) ); - old_y = cur_y; - } - - p.setPen( QColor( 201, 201, 225 ) ); - int ms_per_osc = static_cast( SECS_PER_LFO_OSCILLATION * - m_lfoSpeedKnob->value() * - 1000.0f ); - p.drawText(LFO_GRAPH_X + 4, LFO_GRAPH_Y + m_lfoGraph.height() - 6, tr("ms/LFO:")); - p.drawText(LFO_GRAPH_X + 52, LFO_GRAPH_Y + m_lfoGraph.height() - 6, QString::number(ms_per_osc)); -} - - - - void EnvelopeAndLfoView::lfoUserWaveChanged() { if( static_cast(m_params->m_lfoWaveModel.value()) == @@ -505,11 +279,6 @@ void EnvelopeAndLfoView::lfoUserWaveChanged() } } - - - - - } // namespace gui } // namespace lmms diff --git a/src/gui/instrument/EnvelopeGraph.cpp b/src/gui/instrument/EnvelopeGraph.cpp new file mode 100644 index 00000000000..4cf5da74bfa --- /dev/null +++ b/src/gui/instrument/EnvelopeGraph.cpp @@ -0,0 +1,261 @@ +/* + * EnvelopeGraph.cpp - Displays envelope graphs + * + * Copyright (c) 2004-2014 Tobias Doerffel + * Copyright (c) 2024- Michael Gregorius + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "EnvelopeGraph.h" + +#include +#include +#include + +#include "EnvelopeAndLfoParameters.h" +#include "lmms_math.h" +#include "ColorHelper.h" + +#include + + +namespace lmms +{ + +namespace gui +{ + +EnvelopeGraph::EnvelopeGraph(QWidget* parent) : + QWidget(parent), + ModelView(nullptr, this) +{ + setMinimumSize(m_envGraph.size()); +} + +void EnvelopeGraph::modelChanged() +{ + m_params = castModel(); +} + +void EnvelopeGraph::mousePressEvent(QMouseEvent* me) +{ + if (me->button() == Qt::LeftButton) { toggleAmountModel(); } +} + +void EnvelopeGraph::contextMenuEvent(QContextMenuEvent* event) +{ + QMenu menu(this); + QMenu* scalingMenu = menu.addMenu(tr("Scaling")); + scalingMenu->setToolTipsVisible(true); + + auto switchTo = [&](ScalingMode scaling) + { + if (m_scaling != scaling) + { + m_scaling = scaling; + update(); + } + }; + + auto addScalingEntry = [scalingMenu, &switchTo, this](const QString& text, const QString& toolTip, ScalingMode scaling) + { + QAction* action = scalingMenu->addAction(text, [&switchTo, scaling]() { switchTo(scaling); }); + action->setCheckable(true); + action->setChecked(m_scaling == scaling); + action->setToolTip(toolTip); + }; + + addScalingEntry( + tr("Dynamic"), + tr("Uses absolute spacings but switches to relative spacing if it's running out of space"), + ScalingMode::Dynamic); + addScalingEntry( + tr("Absolute"), + tr("Provides enough potential space for each segment but does not scale"), + ScalingMode::Absolute); + addScalingEntry( + tr("Relative"), + tr("Always uses all of the available space to display the envelope graph"), + ScalingMode::Relative); + + menu.exec(event->globalPos()); +} + +void EnvelopeGraph::paintEvent(QPaintEvent*) +{ + QPainter p{this}; + p.setRenderHint(QPainter::Antialiasing); + + // Draw the graph background + p.drawPixmap(rect(), m_envGraph); + + const auto* params = castModel(); + if (!params) { return; } + + // For the calculation of the percentages we will for now make use of the knowledge + // that the range goes from 0 to a positive max value, i.e. that it is in [0, max]. + const float amount = params->getAmountModel().value(); + + const float predelay = params->getPredelayModel().value(); + const float predelayPercentage = predelay / params->getPredelayModel().maxValue(); + + const float attack = params->getAttackModel().value(); + const float attackPercentage = attack / params->getAttackModel().maxValue(); + + const float hold = params->getHoldModel().value(); + const float holdPercentage = hold / params->getHoldModel().maxValue(); + + const float decay = params->getDecayModel().value(); + const float decayPercentage = decay / params->getDecayModel().maxValue(); + + const float sustain = params->getSustainModel().value(); + + const float release = params->getReleaseModel().value(); + const float releasePercentage = release / params->getReleaseModel().maxValue(); + + // The margin to the left and right so that we do not clip too much of the lines and markers + const float margin = 2.0; + const float availableWidth = width() - margin * 2; + + // Now determine the maximum width for one segment according to the scaling setting. + // The different scalings use different means to compute the maximum available width per segment. + const auto computeMaximumSegmentWidthAbsolute = [&]() -> float + { + return availableWidth / 5.; + }; + + const auto computeMaximumSegmentWidthRelative = [&]() -> float + { + const float sumOfSegments = predelayPercentage + attackPercentage + holdPercentage + decayPercentage + releasePercentage; + + return sumOfSegments != 0. + ? availableWidth / sumOfSegments + : computeMaximumSegmentWidthAbsolute(); + }; + + const auto computeMaximumSegmentWidthDynamic = [&]() -> float + { + const float sumOfSegments = predelayPercentage + attackPercentage + holdPercentage + decayPercentage + releasePercentage; + + float preliminarySegmentWidth = 80. / 182. * availableWidth; + + const float neededWidth = sumOfSegments * preliminarySegmentWidth; + + if (neededWidth > availableWidth && sumOfSegments != 0.) + { + return computeMaximumSegmentWidthRelative(); + } + + return preliminarySegmentWidth; + }; + + // This is the maximum width that each of the five segments (DAHDR) can occupy. + float maximumSegmentWidth; + + switch (m_scaling) + { + case ScalingMode::Absolute: + maximumSegmentWidth = computeMaximumSegmentWidthAbsolute(); + break; + case ScalingMode::Relative: + maximumSegmentWidth = computeMaximumSegmentWidthRelative(); + break; + case ScalingMode::Dynamic: + default: + maximumSegmentWidth = computeMaximumSegmentWidthDynamic(); + break; + } + + // Compute the actual widths that the segments occupy and add them to the + // previous x coordinates starting at the margin. + const float predelayX = margin + predelayPercentage * maximumSegmentWidth; + const float attackX = predelayX + attackPercentage * maximumSegmentWidth; + const float holdX = attackX + holdPercentage * maximumSegmentWidth; + const float decayX = holdX + (decayPercentage * (1 - sustain)) * maximumSegmentWidth; + const float releaseX = decayX + releasePercentage * maximumSegmentWidth; + + // Now compute the "full" points including y coordinates + const int yTop = 3; + const qreal yBase = height() - 3; + const int availableHeight = yBase - yTop; + + const QPointF predelayPoint{predelayX, yBase}; + const QPointF attackPoint{attackX, yTop}; + const QPointF holdPoint{holdX, yTop}; + const QPointF decayPoint{decayX, yTop + (1 - sustain) * availableHeight}; + const QPointF releasePoint{releaseX, yBase}; + + // Now that we have all points we can draw the lines + + // Compute the color of the lines based on the amount of the envelope + const float absAmount = std::abs(amount); + const QColor noAmountColor{96, 91, 96}; + const QColor fullAmountColor{0, 255, 128}; + const QColor lineColor{ColorHelper::interpolateInRgb(noAmountColor, fullAmountColor, absAmount)}; + + // Determine the line width so that it scales with the widget + // Use the minimum value of the current width and height to compute it. + const qreal lineWidth = std::min(width(), height()) / 20.; + const QPen linePen{lineColor, lineWidth}; + p.setPen(linePen); + + QPolygonF linePoly; + linePoly << predelayPoint << attackPoint << holdPoint << decayPoint << releasePoint; + p.drawPolyline(linePoly); + + // Now draw all marker on top of the lines + const QColor markerFillColor{153, 175, 255}; + const QColor markerOutlineColor{0, 0, 0}; + + QPen pen; + pen.setWidthF(lineWidth * 0.75); + pen.setBrush(markerOutlineColor); + p.setPen(pen); + p.setBrush(markerFillColor); + + // Compute the size of the circle we will draw based on the line width + const qreal baseRectSize = lineWidth * 3; + const QSizeF rectSize{baseRectSize, baseRectSize}; + + auto drawMarker = [&](const QPointF& point) + { + // Create a rectangle that has the given point at its center + QRectF bgRect{point + QPointF(-baseRectSize / 2, -baseRectSize / 2), rectSize}; + p.drawEllipse(bgRect); + }; + + drawMarker(predelayPoint); + drawMarker(attackPoint); + drawMarker(holdPoint); + drawMarker(decayPoint); + drawMarker(releasePoint); +} + +void EnvelopeGraph::toggleAmountModel() +{ + auto* params = castModel(); + auto& amountModel = params->getAmountModel(); + + amountModel.setValue(amountModel.value() < 1.0 ? 1.0 : 0.0); +} + +} // namespace gui + +} // namespace lmms diff --git a/src/gui/instrument/InstrumentFunctionViews.cpp b/src/gui/instrument/InstrumentFunctionViews.cpp index c9aa04272da..ad8abe73524 100644 --- a/src/gui/instrument/InstrumentFunctionViews.cpp +++ b/src/gui/instrument/InstrumentFunctionViews.cpp @@ -57,7 +57,7 @@ InstrumentFunctionNoteStackingView::InstrumentFunctionNoteStackingView( Instrume mainLayout->setVerticalSpacing( 1 ); auto chordLabel = new QLabel(tr("Chord:")); - chordLabel->setFont( pointSize<8>( chordLabel->font() ) ); + chordLabel->setFont(adjustedToPixelSize(chordLabel->font(), 10)); m_chordRangeKnob->setLabel( tr( "RANGE" ) ); m_chordRangeKnob->setHintText( tr( "Chord range:" ), " " + tr( "octave(s)" ) ); @@ -145,14 +145,15 @@ InstrumentFunctionArpeggioView::InstrumentFunctionArpeggioView( InstrumentFuncti m_arpGateKnob->setLabel( tr( "GATE" ) ); m_arpGateKnob->setHintText( tr( "Arpeggio gate:" ), tr( "%" ) ); + constexpr int labelFontSize = 10; auto arpChordLabel = new QLabel(tr("Chord:")); - arpChordLabel->setFont( pointSize<8>( arpChordLabel->font() ) ); + arpChordLabel->setFont(adjustedToPixelSize(arpChordLabel->font(), labelFontSize)); auto arpDirectionLabel = new QLabel(tr("Direction:")); - arpDirectionLabel->setFont( pointSize<8>( arpDirectionLabel->font() ) ); + arpDirectionLabel->setFont(adjustedToPixelSize(arpDirectionLabel->font(), labelFontSize)); auto arpModeLabel = new QLabel(tr("Mode:")); - arpModeLabel->setFont( pointSize<8>( arpModeLabel->font() ) ); + arpModeLabel->setFont(adjustedToPixelSize(arpModeLabel->font(), labelFontSize)); mainLayout->addWidget( arpChordLabel, 0, 0 ); mainLayout->addWidget( m_arpComboBox, 1, 0 ); diff --git a/src/gui/instrument/InstrumentMidiIOView.cpp b/src/gui/instrument/InstrumentMidiIOView.cpp index e321d061e98..e3f10bd1a32 100644 --- a/src/gui/instrument/InstrumentMidiIOView.cpp +++ b/src/gui/instrument/InstrumentMidiIOView.cpp @@ -144,7 +144,7 @@ InstrumentMidiIOView::InstrumentMidiIOView( QWidget* parent ) : midiOutputLayout->insertWidget( 0, m_wpBtn ); } - auto baseVelocityGroupBox = new GroupBox(tr("CUSTOM BASE VELOCITY")); + auto baseVelocityGroupBox = new GroupBox(tr("VELOCITY MAPPING")); baseVelocityGroupBox->setLedButtonShown(false); layout->addWidget( baseVelocityGroupBox ); @@ -152,15 +152,9 @@ InstrumentMidiIOView::InstrumentMidiIOView( QWidget* parent ) : baseVelocityLayout->setContentsMargins( 8, 18, 8, 8 ); baseVelocityLayout->setSpacing( 6 ); - auto baseVelocityHelp - = new QLabel(tr("Specify the velocity normalization base for MIDI-based instruments at 100% note velocity.")); - baseVelocityHelp->setWordWrap( true ); - baseVelocityHelp->setFont( pointSize<8>( baseVelocityHelp->font() ) ); - - baseVelocityLayout->addWidget( baseVelocityHelp ); - m_baseVelocitySpinBox = new LcdSpinBox( 3, baseVelocityGroupBox ); - m_baseVelocitySpinBox->setLabel( tr( "BASE VELOCITY" ) ); + m_baseVelocitySpinBox->setLabel(tr("MIDI VELOCITY")); + m_baseVelocitySpinBox->setToolTip(tr("MIDI notes at this velocity correspond to 100% note velocity.")); baseVelocityLayout->addWidget( m_baseVelocitySpinBox ); layout->addStretch(); diff --git a/src/gui/instrument/InstrumentSoundShapingView.cpp b/src/gui/instrument/InstrumentSoundShapingView.cpp index 1bfc166b342..7558c4c2637 100644 --- a/src/gui/instrument/InstrumentSoundShapingView.cpp +++ b/src/gui/instrument/InstrumentSoundShapingView.cpp @@ -22,9 +22,11 @@ * */ +#include "InstrumentSoundShapingView.h" + #include +#include -#include "InstrumentSoundShapingView.h" #include "EnvelopeAndLfoParameters.h" #include "EnvelopeAndLfoView.h" #include "ComboBox.h" @@ -37,69 +39,54 @@ namespace lmms::gui { -const int TARGETS_TABWIDGET_X = 4; -const int TARGETS_TABWIDGET_Y = 5; -const int TARGETS_TABWIDGET_WIDTH = 242; -const int TARGETS_TABWIDGET_HEIGTH = 175; - -const int FILTER_GROUPBOX_X = TARGETS_TABWIDGET_X; -const int FILTER_GROUPBOX_Y = TARGETS_TABWIDGET_Y+TARGETS_TABWIDGET_HEIGTH+5; -const int FILTER_GROUPBOX_WIDTH = TARGETS_TABWIDGET_WIDTH; -const int FILTER_GROUPBOX_HEIGHT = 245-FILTER_GROUPBOX_Y; - - - -InstrumentSoundShapingView::InstrumentSoundShapingView( QWidget * _parent ) : - QWidget( _parent ), - ModelView( nullptr, this ), - m_ss( nullptr ) +InstrumentSoundShapingView::InstrumentSoundShapingView(QWidget* parent) : + QWidget(parent), + ModelView(nullptr, this) { - m_targetsTabWidget = new TabWidget( tr( "TARGET" ), this ); - m_targetsTabWidget->setGeometry( TARGETS_TABWIDGET_X, - TARGETS_TABWIDGET_Y, - TARGETS_TABWIDGET_WIDTH, - TARGETS_TABWIDGET_HEIGTH ); + QVBoxLayout* mainLayout = new QVBoxLayout(this); + mainLayout->setContentsMargins(5, 5, 5, 5); - for( int i = 0; i < InstrumentSoundShaping::NumTargets; ++i ) + m_targetsTabWidget = new TabWidget(tr("TARGET"), this); + + for (auto i = std::size_t{0}; i < InstrumentSoundShaping::NumTargets; ++i) { - m_envLfoViews[i] = new EnvelopeAndLfoView( m_targetsTabWidget ); - m_targetsTabWidget->addTab( m_envLfoViews[i], - tr( InstrumentSoundShaping::targetNames[i][0] ), - nullptr ); + m_envLfoViews[i] = new EnvelopeAndLfoView(m_targetsTabWidget); + m_targetsTabWidget->addTab(m_envLfoViews[i], + tr(InstrumentSoundShaping::targetNames[i][0]), nullptr); } - - m_filterGroupBox = new GroupBox( tr( "FILTER" ), this ); - m_filterGroupBox->setGeometry( FILTER_GROUPBOX_X, FILTER_GROUPBOX_Y, - FILTER_GROUPBOX_WIDTH, - FILTER_GROUPBOX_HEIGHT ); + mainLayout->addWidget(m_targetsTabWidget, 1); - m_filterComboBox = new ComboBox( m_filterGroupBox ); - m_filterComboBox->setGeometry( 14, 22, 120, ComboBox::DEFAULT_HEIGHT ); - m_filterComboBox->setFont( pointSize<8>( m_filterComboBox->font() ) ); + m_filterGroupBox = new GroupBox(tr("FILTER"), this); + QHBoxLayout* filterLayout = new QHBoxLayout(m_filterGroupBox); + QMargins filterMargins = filterLayout->contentsMargins(); + filterMargins.setTop(18); + filterLayout->setContentsMargins(filterMargins); + m_filterComboBox = new ComboBox(m_filterGroupBox); + filterLayout->addWidget(m_filterComboBox); - m_filterCutKnob = new Knob( KnobType::Bright26, m_filterGroupBox ); - m_filterCutKnob->setLabel( tr( "FREQ" ) ); - m_filterCutKnob->move( 140, 18 ); - m_filterCutKnob->setHintText( tr( "Cutoff frequency:" ), " " + tr( "Hz" ) ); + m_filterCutKnob = new Knob(KnobType::Bright26, m_filterGroupBox); + m_filterCutKnob->setLabel(tr("FREQ")); + m_filterCutKnob->setHintText(tr("Cutoff frequency:"), " " + tr("Hz")); + filterLayout->addWidget(m_filterCutKnob); + m_filterResKnob = new Knob(KnobType::Bright26, m_filterGroupBox); + m_filterResKnob->setLabel(tr("Q/RESO")); + m_filterResKnob->setHintText(tr("Q/Resonance:"), ""); + filterLayout->addWidget(m_filterResKnob); - m_filterResKnob = new Knob( KnobType::Bright26, m_filterGroupBox ); - m_filterResKnob->setLabel( tr( "Q/RESO" ) ); - m_filterResKnob->move( 196, 18 ); - m_filterResKnob->setHintText( tr( "Q/Resonance:" ), "" ); + mainLayout->addWidget(m_filterGroupBox); - m_singleStreamInfoLabel = new QLabel( tr( "Envelopes, LFOs and filters are not supported by the current instrument." ), this ); - m_singleStreamInfoLabel->setWordWrap( true ); - m_singleStreamInfoLabel->setFont( pointSize<8>( m_singleStreamInfoLabel->font() ) ); + m_singleStreamInfoLabel = new QLabel(tr("Envelopes, LFOs and filters are not supported by the current instrument."), this); + m_singleStreamInfoLabel->setWordWrap(true); + // TODO Could also be rendered in system font size... + m_singleStreamInfoLabel->setFont(adjustedToPixelSize(m_singleStreamInfoLabel->font(), 10)); + m_singleStreamInfoLabel->setFixedWidth(242); - m_singleStreamInfoLabel->setGeometry( TARGETS_TABWIDGET_X, - TARGETS_TABWIDGET_Y, - TARGETS_TABWIDGET_WIDTH, - TARGETS_TABWIDGET_HEIGTH ); + mainLayout->addWidget(m_singleStreamInfoLabel, 0, Qt::AlignTop); } @@ -128,7 +115,7 @@ void InstrumentSoundShapingView::modelChanged() m_filterComboBox->setModel( &m_ss->m_filterModel ); m_filterCutKnob->setModel( &m_ss->m_filterCutModel ); m_filterResKnob->setModel( &m_ss->m_filterResModel ); - for( int i = 0; i < InstrumentSoundShaping::NumTargets; ++i ) + for (auto i = std::size_t{0}; i < InstrumentSoundShaping::NumTargets; ++i) { m_envLfoViews[i]->setModel( m_ss->m_envLfoParameters[i] ); } diff --git a/src/gui/instrument/InstrumentTrackWindow.cpp b/src/gui/instrument/InstrumentTrackWindow.cpp index a726dd5b989..1c9c93a09f9 100644 --- a/src/gui/instrument/InstrumentTrackWindow.cpp +++ b/src/gui/instrument/InstrumentTrackWindow.cpp @@ -44,7 +44,6 @@ #include "GroupBox.h" #include "MixerChannelLcdSpinBox.h" #include "GuiApplication.h" -#include "gui_templates.h" #include "Instrument.h" #include "InstrumentFunctions.h" #include "InstrumentFunctionViews.h" @@ -93,13 +92,9 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : vlayout->setContentsMargins(0, 0, 0, 0); vlayout->setSpacing( 0 ); - auto generalSettingsWidget = new TabWidget(tr("GENERAL SETTINGS"), this); - + auto generalSettingsWidget = new QWidget(this); auto generalSettingsLayout = new QVBoxLayout(generalSettingsWidget); - generalSettingsLayout->setContentsMargins( 8, 18, 8, 8 ); - generalSettingsLayout->setSpacing( 6 ); - auto nameAndChangeTrackWidget = new QWidget(generalSettingsWidget); auto nameAndChangeTrackLayout = new QHBoxLayout(nameAndChangeTrackWidget); nameAndChangeTrackLayout->setContentsMargins( 0, 0, 0, 0 ); @@ -107,7 +102,6 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : // setup line edit for changing instrument track name m_nameLineEdit = new QLineEdit; - m_nameLineEdit->setFont( pointSize<9>( m_nameLineEdit->font() ) ); connect( m_nameLineEdit, SIGNAL( textChanged( const QString& ) ), this, SLOT( textChanged( const QString& ) ) ); @@ -271,7 +265,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : m_tabWidget->addTab(m_tuningView, tr("Tuning and transposition"), "tuning_tab", 5); adjustTabSize(m_ssView); adjustTabSize(instrumentFunctions); - m_effectView->resize(EffectRackView::DEFAULT_WIDTH, INSTRUMENT_HEIGHT - 4 - 1); + // EffectRackView has sizeHint to be QSize(EffectRackView::DEFAULT_WIDTH, INSTRUMENT_HEIGHT - 4 - 1) adjustTabSize(m_midiView); adjustTabSize(m_tuningView); @@ -356,7 +350,7 @@ void InstrumentTrackWindow::modelChanged() m_mixerChannelNumber->setModel( &m_track->m_mixerChannelModel ); m_pianoView->setModel( &m_track->m_piano ); - if( m_track->instrument() && m_track->instrument()->flags().testFlag( Instrument::Flag::IsNotBendable ) == false ) + if (m_track->instrument() && m_track->instrument()->isBendable()) { m_pitchKnob->setModel( &m_track->m_pitchModel ); m_pitchRangeSpinBox->setModel( &m_track->m_pitchRangeModel ); @@ -374,7 +368,7 @@ void InstrumentTrackWindow::modelChanged() m_pitchRangeLabel->hide(); } - if (m_track->instrument() && m_track->instrument()->flags().testFlag(Instrument::Flag::IsMidiBased)) + if (m_track->instrument() && m_track->instrument()->isMidiBased()) { m_tuningView->microtunerNotSupportedLabel()->show(); m_tuningView->microtunerGroupBox()->hide(); @@ -468,7 +462,7 @@ void InstrumentTrackWindow::updateInstrumentView() m_tabWidget->addTab( m_instrumentView, tr( "Plugin" ), "plugin_tab", 0 ); m_tabWidget->setActiveTab( 0 ); - m_ssView->setFunctionsHidden( m_track->m_instrument->flags().testFlag( Instrument::Flag::IsSingleStreamed ) ); + m_ssView->setFunctionsHidden(m_track->m_instrument->isSingleStreamed()); modelChanged(); // Get the instrument window to refresh m_track->dataChanged(); // Get the text on the trackButton to change diff --git a/src/gui/instrument/InstrumentTuningView.cpp b/src/gui/instrument/InstrumentTuningView.cpp index 355d7d18c73..daa361aad6b 100644 --- a/src/gui/instrument/InstrumentTuningView.cpp +++ b/src/gui/instrument/InstrumentTuningView.cpp @@ -60,7 +60,7 @@ InstrumentTuningView::InstrumentTuningView(InstrumentTrack *it, QWidget *parent) auto tlabel = new QLabel(tr("Enables the use of global transposition")); tlabel->setWordWrap(true); - tlabel->setFont(pointSize<8>(tlabel->font())); + tlabel->setFont(adjustedToPixelSize(tlabel->font(), 10)); masterPitchLayout->addWidget(tlabel); // Microtuner settings diff --git a/src/gui/instrument/LfoGraph.cpp b/src/gui/instrument/LfoGraph.cpp new file mode 100644 index 00000000000..7edbacb09ea --- /dev/null +++ b/src/gui/instrument/LfoGraph.cpp @@ -0,0 +1,197 @@ +/* + * LfoGraph.cpp - Displays LFO graphs + * + * Copyright (c) 2004-2014 Tobias Doerffel + * Copyright (c) 2024- Michael Gregorius + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "LfoGraph.h" + +#include +#include + +#include "EnvelopeAndLfoParameters.h" +#include "Oscillator.h" +#include "ColorHelper.h" + +#include "gui_templates.h" + +namespace lmms +{ + +extern const float SECS_PER_LFO_OSCILLATION; + +namespace gui +{ + +LfoGraph::LfoGraph(QWidget* parent) : + QWidget(parent), + ModelView(nullptr, this) +{ + setMinimumSize(m_lfoGraph.size()); +} + +void LfoGraph::mousePressEvent(QMouseEvent* me) +{ + if (me->button() == Qt::LeftButton) + { + toggleAmountModel(); + } +} + +void LfoGraph::paintEvent(QPaintEvent*) +{ + QPainter p{this}; + p.setRenderHint(QPainter::Antialiasing); + + // Draw the graph background + p.drawPixmap(rect(), m_lfoGraph); + + const auto* params = castModel(); + if (!params) { return; } + + const float amount = params->getLfoAmountModel().value(); + const float lfoSpeed = params->getLfoSpeedModel().value(); + const f_cnt_t predelayFrames = params->getLfoPredelayFrames(); + const f_cnt_t attackFrames = params->getLfoAttackFrames(); + const f_cnt_t oscillationFrames = params->getLfoOscillationFrames(); + const bool x100 = params->getX100Model().value(); + const int lfoWaveModel = params->getLfoWaveModel().value(); + const auto * userWave = params->getLfoUserWave().get(); + + const int margin = 3; + const int lfoGraphWidth = width() - margin; // subtract margin + const int lfoGraphHeight = height() - 2 * margin; // subtract margin + int graphBaseX = 2; + int graphBaseY = margin + lfoGraphHeight / 2; + + const float framesForGraph = + SECS_PER_LFO_OSCILLATION * Engine::audioEngine()->baseSampleRate() / 10; + + float oscFrames = oscillationFrames * (x100 ? 100. : 1.); + + QPolygonF polyLine; + polyLine << QPointF(graphBaseX - 1, graphBaseY); + + // Collect the points for the poly line by sampling the LFO according to its shape + for (int x = 0; x <= lfoGraphWidth; ++x) + { + float value = 0.0; + float currentSample = x * framesForGraph / lfoGraphWidth; + const auto sampleAsFrameCount = static_cast(currentSample); + if (sampleAsFrameCount > predelayFrames) + { + currentSample -= predelayFrames; + const float phase = currentSample / oscFrames; + + const auto lfoShape = static_cast(lfoWaveModel); + switch (lfoShape) + { + case EnvelopeAndLfoParameters::LfoShape::SineWave: + default: + value = Oscillator::sinSample(phase); + break; + case EnvelopeAndLfoParameters::LfoShape::TriangleWave: + value = Oscillator::triangleSample(phase); + break; + case EnvelopeAndLfoParameters::LfoShape::SawWave: + value = Oscillator::sawSample(phase); + break; + case EnvelopeAndLfoParameters::LfoShape::SquareWave: + value = Oscillator::squareSample(phase); + break; + case EnvelopeAndLfoParameters::LfoShape::RandomWave: + if (x % (int)(900 * lfoSpeed + 1) == 0) + { + m_randomGraph = Oscillator::noiseSample(0.0); + } + value = m_randomGraph; + break; + case EnvelopeAndLfoParameters::LfoShape::UserDefinedWave: + value = Oscillator::userWaveSample(userWave, phase); + break; + } + + if (sampleAsFrameCount <= attackFrames) + { + value *= currentSample / attackFrames; + } + } + + const float currentY = -lfoGraphHeight / 2.0f * value; + + polyLine << QPointF(graphBaseX + x, graphBaseY + currentY); + } + + // Compute the color of the lines based on the amount of the LFO + const float absAmount = std::abs(amount); + const QColor noAmountColor{96, 91, 96}; + const QColor fullAmountColor{0, 255, 128}; + const QColor lineColor{ColorHelper::interpolateInRgb(noAmountColor, fullAmountColor, absAmount)}; + + p.setPen(QPen(lineColor, 1.5)); + + p.drawPolyline(polyLine); + + drawInfoText(*params); +} + +void LfoGraph::drawInfoText(const EnvelopeAndLfoParameters& params) +{ + QPainter p(this); + + const float lfoSpeed = params.getLfoSpeedModel().value(); + const bool x100 = params.getX100Model().value(); + + const float hertz = 1. / (SECS_PER_LFO_OSCILLATION * lfoSpeed) * (x100 ? 100. : 1.); + const auto infoText = tr("%1 Hz").arg(hertz, 0, 'f', 3); + + // First configure the font so that we get correct results for the font metrics used below + QFont f = p.font(); + f.setPixelSize(height() * 0.2); + p.setFont(f); + + // This is the position where the text and its rectangle will be rendered + const QPoint textPosition(4, height() - 6); + + // Draw a slightly transparent black rectangle underneath the text to keep it legible + const QFontMetrics fontMetrics(f); + // This gives the bounding rectangle if the text was rendered at the origin ... + const auto boundingRect = fontMetrics.boundingRect(infoText); + // ... so we translate it to the actual position where the text will be rendered. + p.fillRect(boundingRect.translated(textPosition), QColor{0, 0, 0, 192}); + + // Now draw the actual info text + p.setPen(QColor(201, 201, 225)); + p.drawText(textPosition, infoText); +} + +void LfoGraph::toggleAmountModel() +{ + auto* params = castModel(); + auto& lfoAmountModel = params->getLfoAmountModel(); + + lfoAmountModel.setValue(lfoAmountModel.value() < 1.0 ? 1.0 : 0.0); +} + +} // namespace gui + +} // namespace lmms diff --git a/src/gui/instrument/PianoView.cpp b/src/gui/instrument/PianoView.cpp index c8882898bc2..13628d97eea 100644 --- a/src/gui/instrument/PianoView.cpp +++ b/src/gui/instrument/PianoView.cpp @@ -807,7 +807,7 @@ void PianoView::paintEvent( QPaintEvent * ) QPainter p( this ); // set smaller font for printing number of every octave - p.setFont( pointSize( p.font() ) ); + p.setFont(adjustedToPixelSize(p.font(), LABEL_TEXT_SIZE)); // draw bar above the keyboard (there will be the labels diff --git a/src/gui/menus/MidiPortMenu.cpp b/src/gui/menus/MidiPortMenu.cpp index b99c3a0b72f..6c573fdf5c3 100644 --- a/src/gui/menus/MidiPortMenu.cpp +++ b/src/gui/menus/MidiPortMenu.cpp @@ -24,7 +24,6 @@ */ #include "MidiPortMenu.h" -#include "gui_templates.h" namespace lmms::gui { diff --git a/src/gui/modals/ControllerConnectionDialog.cpp b/src/gui/modals/ControllerConnectionDialog.cpp index 4d1090d5c88..c7bdd28b4c7 100644 --- a/src/gui/modals/ControllerConnectionDialog.cpp +++ b/src/gui/modals/ControllerConnectionDialog.cpp @@ -295,13 +295,12 @@ ControllerConnectionDialog::~ControllerConnectionDialog() void ControllerConnectionDialog::selectController() { // Midi - if( m_midiGroupBox->model()->value() > 0 ) + if (m_midiGroupBox->model()->value()) { if( m_midiControllerSpinBox->model()->value() > 0 ) { - MidiController * mc; - mc = m_midiController->copyToMidiController( Engine::getSong() ); - + auto mc = m_midiController->copyToMidiController(Engine::getSong()); + /* if( m_targetModel->getTrack() && !m_targetModel->getTrack()->displayName().isEmpty() ) @@ -322,8 +321,7 @@ void ControllerConnectionDialog::selectController() // User else { - if( m_userGroupBox->model()->value() > 0 && - Engine::getSong()->controllers().size() ) + if (m_userGroupBox->model()->value() && Engine::getSong()->controllers().size()) { m_controller = Engine::getSong()->controllers().at( m_userController->model()->value() ); diff --git a/src/gui/modals/ExportProjectDialog.cpp b/src/gui/modals/ExportProjectDialog.cpp index fe39082e450..1f989841f3b 100644 --- a/src/gui/modals/ExportProjectDialog.cpp +++ b/src/gui/modals/ExportProjectDialog.cpp @@ -56,7 +56,7 @@ ExportProjectDialog::ExportProjectDialog( const QString & _file_name, } int cbIndex = 0; - for( int i = 0; i < ProjectRenderer::NumFileFormats; ++i ) + for (auto i = std::size_t{0}; i < ProjectRenderer::NumFileFormats; ++i) { if( ProjectRenderer::fileEncodeDevices[i].isAvailable() ) { @@ -154,11 +154,8 @@ OutputSettings::StereoMode mapToStereoMode(int index) void ExportProjectDialog::startExport() { - AudioEngine::qualitySettings qs = - AudioEngine::qualitySettings( - static_cast(interpolationCB->currentIndex()), - static_cast(oversamplingCB->currentIndex()) ); - + auto qs = AudioEngine::qualitySettings( + static_cast(interpolationCB->currentIndex())); const auto samplerates = std::array{44100, 48000, 88200, 96000, 192000}; const auto bitrates = std::array{64, 128, 160, 192, 256, 320}; @@ -271,7 +268,7 @@ void ExportProjectDialog::startBtnClicked() } // Find proper file extension. - for( int i = 0; i < ProjectRenderer::NumFileFormats; ++i ) + for (auto i = std::size_t{0}; i < ProjectRenderer::NumFileFormats; ++i) { if (m_ft == ProjectRenderer::fileEncodeDevices[i].m_fileFormat) { diff --git a/src/gui/modals/SetupDialog.cpp b/src/gui/modals/SetupDialog.cpp index fffa94c82ba..6f05433b782 100644 --- a/src/gui/modals/SetupDialog.cpp +++ b/src/gui/modals/SetupDialog.cpp @@ -37,7 +37,6 @@ #include "embed.h" #include "Engine.h" #include "FileDialog.h" -#include "gui_templates.h" #include "MainWindow.h" #include "MidiSetupWidget.h" #include "ProjectJournal.h" @@ -143,10 +142,10 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : "ui", "disableautoquit", "1").toInt()), m_NaNHandler(ConfigManager::inst()->value( "app", "nanhandler", "1").toInt()), - m_hqAudioDev(ConfigManager::inst()->value( - "audioengine", "hqaudio").toInt()), m_bufferSize(ConfigManager::inst()->value( "audioengine", "framesperaudiobuffer").toInt()), + m_midiAutoQuantize(ConfigManager::inst()->value( + "midi", "autoquantize", "0").toInt() != 0), m_workingDir(QDir::toNativeSeparators(ConfigManager::inst()->workingDir())), m_vstDir(QDir::toNativeSeparators(ConfigManager::inst()->vstDir())), m_ladspaDir(QDir::toNativeSeparators(ConfigManager::inst()->ladspaDir())), @@ -160,8 +159,7 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : { setWindowIcon(embed::getIconPixmap("setup_general")); setWindowTitle(tr("Settings")); - // TODO: Equivalent to the new setWindowFlag(Qt::WindowContextHelpButtonHint, false) - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setWindowFlag(Qt::WindowContextHelpButtonHint, false); setModal(true); Engine::projectJournal()->setJournalling(false); @@ -560,10 +558,6 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : // audio_layout->addWidget(useNaNHandler); // useNaNHandler->setChecked(m_NaNHandler); - // HQ mode checkbox - auto hqaudio = addCheckBox(tr("HQ mode for output audio device"), audioInterfaceBox, nullptr, - m_hqAudioDev, SLOT(toggleHQAudioDev(bool)), false); - // Buffer size group QGroupBox * bufferSizeBox = new QGroupBox(tr("Buffer size"), audio_w); QVBoxLayout * bufferSizeLayout = new QVBoxLayout(bufferSizeBox); @@ -605,7 +599,6 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : // Audio layout ordering. audio_layout->addWidget(audioInterfaceBox); audio_layout->addWidget(as_w); - audio_layout->addWidget(hqaudio); audio_layout->addWidget(bufferSizeBox); audio_layout->addStretch(); @@ -717,10 +710,22 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) : m_assignableMidiDevices->setCurrentIndex(current); } + // MIDI Recording tab + auto* midiRecordingTab = new QGroupBox(tr("Behavior when recording"), midi_w); + auto* midiRecordingLayout = new QVBoxLayout(midiRecordingTab); + { + auto *box = addCheckBox(tr("Auto-quantize notes in Piano Roll"), + midiRecordingTab, midiRecordingLayout, + m_midiAutoQuantize, SLOT(toggleMidiAutoQuantization(bool)), + false); + box->setToolTip(tr("If enabled, notes will be automatically quantized when recording them from a MIDI controller. If disabled, they are always recorded at the highest possible resolution.")); + } + // MIDI layout ordering. midi_layout->addWidget(midiInterfaceBox); midi_layout->addWidget(ms_w); midi_layout->addWidget(midiAutoAssignBox); + midi_layout->addWidget(midiRecordingTab); midi_layout->addStretch(); @@ -958,14 +963,13 @@ void SetupDialog::accept() m_audioIfaceNames[m_audioInterfaces->currentText()]); ConfigManager::inst()->setValue("app", "nanhandler", QString::number(m_NaNHandler)); - ConfigManager::inst()->setValue("audioengine", "hqaudio", - QString::number(m_hqAudioDev)); ConfigManager::inst()->setValue("audioengine", "framesperaudiobuffer", QString::number(m_bufferSize)); ConfigManager::inst()->setValue("audioengine", "mididev", m_midiIfaceNames[m_midiInterfaces->currentText()]); ConfigManager::inst()->setValue("midi", "midiautoassign", m_assignableMidiDevices->currentText()); + ConfigManager::inst()->setValue("midi", "autoquantize", QString::number(m_midiAutoQuantize)); ConfigManager::inst()->setWorkingDir(QDir::fromNativeSeparators(m_workingDir)); @@ -1161,17 +1165,6 @@ void SetupDialog::toggleDisableAutoQuit(bool enabled) m_disableAutoQuit = enabled; } - - - -// Audio settings slots. - -void SetupDialog::toggleHQAudioDev(bool enabled) -{ - m_hqAudioDev = enabled; -} - - void SetupDialog::audioInterfaceChanged(const QString & iface) { for(AswMap::iterator it = m_audioIfaceSetupWidgets.begin(); @@ -1230,7 +1223,7 @@ void SetupDialog::setBufferSize(int value) m_bufferSize = value * BUFFERSIZE_RESOLUTION; m_bufferSizeLbl->setText(tr("Frames: %1\nLatency: %2 ms").arg(m_bufferSize).arg( - 1000.0f * m_bufferSize / Engine::audioEngine()->processingSampleRate(), 0, 'f', 1)); + 1000.0f * m_bufferSize / Engine::audioEngine()->outputSampleRate(), 0, 'f', 1)); updateBufferSizeWarning(m_bufferSize); } @@ -1254,6 +1247,11 @@ void SetupDialog::midiInterfaceChanged(const QString & iface) m_midiIfaceSetupWidgets[m_midiIfaceNames[iface]]->show(); } +void SetupDialog::toggleMidiAutoQuantization(bool enabled) +{ + m_midiAutoQuantize = enabled; +} + // Paths settings slots. diff --git a/src/gui/modals/export_project.ui b/src/gui/modals/export_project.ui index 6b175de78d6..797ae07901c 100644 --- a/src/gui/modals/export_project.ui +++ b/src/gui/modals/export_project.ui @@ -404,37 +404,6 @@
- - - - Oversampling: - - - - - - - - 1x (None) - - - - - 2x - - - - - 4x - - - - - 8x - - - - diff --git a/src/gui/tracks/InstrumentTrackView.cpp b/src/gui/tracks/InstrumentTrackView.cpp index c812999fd62..1d9991c3107 100644 --- a/src/gui/tracks/InstrumentTrackView.cpp +++ b/src/gui/tracks/InstrumentTrackView.cpp @@ -41,7 +41,6 @@ #include "MixerView.h" #include "GuiApplication.h" #include "Instrument.h" -#include "InstrumentTrack.h" #include "InstrumentTrackWindow.h" #include "MainWindow.h" #include "MidiClient.h" @@ -397,12 +396,6 @@ QMenu * InstrumentTrackView::createMixerMenu(QString title, QString newMixerLabe return mixerMenu; } -QPixmap InstrumentTrackView::determinePixmap() -{ - return determinePixmap(dynamic_cast(getTrack())); -} - - QPixmap InstrumentTrackView::determinePixmap(InstrumentTrack* instrumentTrack) { if (instrumentTrack) diff --git a/src/gui/tracks/PatternTrackView.cpp b/src/gui/tracks/PatternTrackView.cpp index be039ba79e5..ac0b42e2dcc 100644 --- a/src/gui/tracks/PatternTrackView.cpp +++ b/src/gui/tracks/PatternTrackView.cpp @@ -26,6 +26,7 @@ #include "Engine.h" #include "GuiApplication.h" +#include "MainWindow.h" #include "PatternEditor.h" #include "PatternStore.h" #include "PatternTrack.h" @@ -74,8 +75,7 @@ bool PatternTrackView::close() void PatternTrackView::clickedTrackLabel() { Engine::patternStore()->setCurrentPattern(m_patternTrack->patternIndex()); - getGUI()->patternEditor()->parentWidget()->show(); - getGUI()->patternEditor()->setFocus(Qt::ActiveWindowFocusReason); + getGUI()->mainWindow()->togglePatternEditorWin(true); } diff --git a/src/gui/tracks/TrackContentWidget.cpp b/src/gui/tracks/TrackContentWidget.cpp index 619eff8317b..1926ffeef92 100644 --- a/src/gui/tracks/TrackContentWidget.cpp +++ b/src/gui/tracks/TrackContentWidget.cpp @@ -43,14 +43,15 @@ #include "ClipView.h" #include "TrackView.h" - namespace lmms::gui { /*! Alternate between a darker and a lighter background color every 4 bars */ const int BARS_PER_GROUP = 4; - +/* Lines between bars will disappear if zoomed too far out (i.e + if there are less than 4 pixels between lines)*/ +const int MIN_PIXELS_BETWEEN_LINES = 4; /*! \brief Create a new trackContentWidget * @@ -65,8 +66,15 @@ TrackContentWidget::TrackContentWidget( TrackView * parent ) : m_trackView( parent ), m_darkerColor( Qt::SolidPattern ), m_lighterColor( Qt::SolidPattern ), - m_gridColor( Qt::SolidPattern ), - m_embossColor( Qt::SolidPattern ) + m_coarseGridColor( Qt::SolidPattern ), + m_fineGridColor( Qt::SolidPattern ), + m_horizontalColor( Qt::SolidPattern ), + m_embossColor( Qt::SolidPattern ), + m_coarseGridWidth(2), + m_fineGridWidth(1), + m_horizontalWidth(1), + m_embossWidth(0), + m_embossOffset(0) { setAcceptDrops( true ); @@ -74,6 +82,14 @@ TrackContentWidget::TrackContentWidget( TrackView * parent ) : SIGNAL( positionChanged( const lmms::TimePos& ) ), this, SLOT( changePosition( const lmms::TimePos& ) ) ); + // Update background if snap size changes + connect(getGUI()->songEditor()->m_editor->snappingModel(), &Model::dataChanged, + this, &TrackContentWidget::updateBackground); + + // Also update background if proportional snap is enabled/disabled + connect(getGUI()->songEditor()->m_editor, &SongEditor::proportionalSnapChanged, + this, &TrackContentWidget::updateBackground); + setStyle( QApplication::style() ); updateBackground(); @@ -82,16 +98,30 @@ TrackContentWidget::TrackContentWidget( TrackView * parent ) : - - - void TrackContentWidget::updateBackground() -{ +{ + // use snapSize to determine number of lines to draw + float snapSize = getGUI()->songEditor()->m_editor->getSnapSize(); + const TrackContainerView * tcv = m_trackView->trackContainerView(); // Assume even-pixels-per-bar. Makes sense, should be like this anyways int ppb = static_cast( tcv->pixelsPerBar() ); + // Coarse grid appears every bar (less frequently if quantization > 1 bar) + float coarseGridResolution = (snapSize >= 1) ? snapSize : 1; + // Fine grid appears within bars + float fineGridResolution = snapSize; + // Increase fine grid resolution (size between lines) if it results in less than + // 4 pixels between each line to avoid cluttering + float pixelsBetweenLines = ppb * snapSize; + if (pixelsBetweenLines < MIN_PIXELS_BETWEEN_LINES) { + // Scale fineGridResolution so that there are enough pixels between lines + // scaleFactor should be a power of 2 + int scaleFactor = 1 << static_cast( std::ceil( std::log2( MIN_PIXELS_BETWEEN_LINES / pixelsBetweenLines ) ) ); + fineGridResolution *= scaleFactor; + } + int w = ppb * BARS_PER_GROUP; int h = height(); m_background = QPixmap( w * 2, height() ); @@ -101,22 +131,29 @@ void TrackContentWidget::updateBackground() pmp.fillRect( w, 0, w , h, lighterColor() ); // draw lines - // vertical lines - pmp.setPen( QPen( gridColor(), 1 ) ); - for( float x = 0; x < w * 2; x += ppb ) + // draw fine grid + pmp.setPen( QPen( fineGridColor(), fineGridWidth() ) ); + for (float x = 0; x < w * 2; x += ppb * fineGridResolution) + { + pmp.drawLine( QLineF( x, 0.0, x, h ) ); + } + + // draw coarse grid + pmp.setPen( QPen( coarseGridColor(), coarseGridWidth() ) ); + for (float x = 0; x < w * 2; x += ppb * coarseGridResolution) { pmp.drawLine( QLineF( x, 0.0, x, h ) ); } - pmp.setPen( QPen( embossColor(), 1 ) ); - for( float x = 1.0; x < w * 2; x += ppb ) + pmp.setPen( QPen( embossColor(), embossWidth() ) ); + for (float x = (coarseGridWidth() + embossOffset()); x < w * 2; x += ppb * coarseGridResolution) { pmp.drawLine( QLineF( x, 0.0, x, h ) ); } - // horizontal line - pmp.setPen( QPen( gridColor(), 1 ) ); - pmp.drawLine( 0, h-1, w*2, h-1 ); + // draw horizontal line + pmp.setPen( QPen( horizontalColor(), horizontalWidth() ) ); + pmp.drawLine(0, h - (horizontalWidth() + 1) / 2, w * 2, h - (horizontalWidth() + 1) / 2); pmp.end(); @@ -255,6 +292,7 @@ void TrackContentWidget::changePosition( const TimePos & newPos ) setUpdatesEnabled( true ); // redraw background + updateBackground(); // update(); } @@ -376,7 +414,7 @@ bool TrackContentWidget::canPasteSelection( TimePos clipPos, const QMimeData* md int finalTrackIndex = trackIndex + currentTrackIndex - initialTrackIndex; // Track must be in TrackContainer's tracks - if( finalTrackIndex < 0 || finalTrackIndex >= tracks.size() ) + if (finalTrackIndex < 0 || static_cast(finalTrackIndex) >= tracks.size()) { return false; } @@ -591,8 +629,8 @@ void TrackContentWidget::paintEvent( QPaintEvent * pe ) // Don't draw background on Pattern Editor if (m_trackView->trackContainerView() != getGUI()->patternEditor()->m_editor) { - p.drawTiledPixmap( rect(), m_background, QPoint( - tcv->currentPosition().getBar() * ppb, 0 ) ); + p.drawTiledPixmap(rect(), m_background, QPoint( + tcv->currentPosition().getTicks() * ppb / TimePos::ticksPerBar(), 0)); } } @@ -690,13 +728,41 @@ QBrush TrackContentWidget::lighterColor() const { return m_lighterColor; } //! \brief CSS theming qproperty access method -QBrush TrackContentWidget::gridColor() const -{ return m_gridColor; } +QBrush TrackContentWidget::coarseGridColor() const +{ return m_coarseGridColor; } + +//! \brief CSS theming qproperty access method +QBrush TrackContentWidget::fineGridColor() const +{ return m_fineGridColor; } + +//! \brief CSS theming qproperty access method +QBrush TrackContentWidget::horizontalColor() const +{ return m_horizontalColor; } //! \brief CSS theming qproperty access method QBrush TrackContentWidget::embossColor() const { return m_embossColor; } +//! \brief CSS theming qproperty access method +int TrackContentWidget::coarseGridWidth() const +{ return m_coarseGridWidth; } + +//! \brief CSS theming qproperty access method +int TrackContentWidget::fineGridWidth() const +{ return m_fineGridWidth; } + +//! \brief CSS theming qproperty access method +int TrackContentWidget::horizontalWidth() const +{ return m_horizontalWidth; } + +//! \brief CSS theming qproperty access method +int TrackContentWidget::embossWidth() const +{ return m_embossWidth; } + +//! \brief CSS theming qproperty access method +int TrackContentWidget::embossOffset() const +{ return m_embossOffset; } + //! \brief CSS theming qproperty access method void TrackContentWidget::setDarkerColor( const QBrush & c ) { m_darkerColor = c; } @@ -706,12 +772,39 @@ void TrackContentWidget::setLighterColor( const QBrush & c ) { m_lighterColor = c; } //! \brief CSS theming qproperty access method -void TrackContentWidget::setGridColor( const QBrush & c ) -{ m_gridColor = c; } +void TrackContentWidget::setCoarseGridColor( const QBrush & c ) +{ m_coarseGridColor = c; } + +//! \brief CSS theming qproperty access method +void TrackContentWidget::setFineGridColor( const QBrush & c ) +{ m_fineGridColor = c; } + +//! \brief CSS theming qproperty access method +void TrackContentWidget::setHorizontalColor( const QBrush & c ) +{ m_horizontalColor = c; } //! \brief CSS theming qproperty access method void TrackContentWidget::setEmbossColor( const QBrush & c ) { m_embossColor = c; } +//! \brief CSS theming qproperty access method +void TrackContentWidget::setCoarseGridWidth(int c) +{ m_coarseGridWidth = c; } + +//! \brief CSS theming qproperty access method +void TrackContentWidget::setFineGridWidth(int c) +{ m_fineGridWidth = c; } + +//! \brief CSS theming qproperty access method +void TrackContentWidget::setHorizontalWidth(int c) +{ m_horizontalWidth = c; } + +//! \brief CSS theming qproperty access method +void TrackContentWidget::setEmbossWidth(int c) +{ m_embossWidth = c; } + +//! \brief CSS theming qproperty access method +void TrackContentWidget::setEmbossOffset(int c) +{ m_embossOffset = c; } } // namespace lmms::gui diff --git a/src/gui/tracks/TrackLabelButton.cpp b/src/gui/tracks/TrackLabelButton.cpp index c164b780e75..871d42316e5 100644 --- a/src/gui/tracks/TrackLabelButton.cpp +++ b/src/gui/tracks/TrackLabelButton.cpp @@ -31,6 +31,8 @@ #include "ConfigManager.h" #include "embed.h" #include "InstrumentTrackView.h" +#include "Instrument.h" +#include "InstrumentTrack.h" #include "RenameDialog.h" #include "TrackRenameLineEdit.h" #include "TrackView.h" @@ -181,12 +183,27 @@ void TrackLabelButton::mouseReleaseEvent( QMouseEvent *_me ) void TrackLabelButton::paintEvent(QPaintEvent* pe) { - InstrumentTrackView* instrumentTrackView = dynamic_cast(m_trackView); - if (instrumentTrackView) + if (m_trackView->getTrack()->type() == Track::Type::Instrument) { - setIcon(instrumentTrackView->determinePixmap()); + auto it = dynamic_cast(m_trackView->getTrack()); + const PixmapLoader* pl; + auto get_logo = [](InstrumentTrack* it) -> const PixmapLoader* + { + return it->instrument()->key().isValid() + ? it->instrument()->key().logo() + : it->instrument()->descriptor()->logo; + }; + if (it && it->instrument() && + it->instrument()->descriptor() && + (pl = get_logo(it))) + { + if (pl->pixmapName() != m_iconName) + { + m_iconName = pl->pixmapName(); + setIcon(pl->pixmap()); + } + } } - QToolButton::paintEvent(pe); } diff --git a/src/gui/tracks/TrackOperationsWidget.cpp b/src/gui/tracks/TrackOperationsWidget.cpp index e846370e69c..6cb74e2c5b7 100644 --- a/src/gui/tracks/TrackOperationsWidget.cpp +++ b/src/gui/tracks/TrackOperationsWidget.cpp @@ -38,7 +38,6 @@ #include "DataFile.h" #include "embed.h" #include "Engine.h" -#include "gui_templates.h" #include "InstrumentTrackView.h" #include "PixmapButton.h" #include "Song.h" @@ -237,6 +236,12 @@ void TrackOperationsWidget::cloneTrack() tcView->moveTrackView( newTrackView, i - 1 ); i--; } + + if (m_soloBtn->model()->value()) + { + // if this track was solo, make the new track the new solo + newTrack->toggleSolo(); + } } diff --git a/src/gui/widgets/BarModelEditor.cpp b/src/gui/widgets/BarModelEditor.cpp index 4b02c963461..ae224530d7a 100644 --- a/src/gui/widgets/BarModelEditor.cpp +++ b/src/gui/widgets/BarModelEditor.cpp @@ -86,7 +86,7 @@ void BarModelEditor::paintEvent(QPaintEvent *event) // Paint the bar // Compute the percentage as: // min + x * (max - min) = v <=> x = (v - min) / (max - min) - auto const percentage = range == 0 ? 1. : (mod->value() - minValue) / range; + auto const percentage = range == 0 ? 1. : (model()->inverseScaledValue(model()->value()) - minValue) / range; int const margin = 3; QMargins const margins(margin, margin, margin, margin); diff --git a/src/gui/widgets/ComboBox.cpp b/src/gui/widgets/ComboBox.cpp index ccc0c675b0d..0daae1b240b 100644 --- a/src/gui/widgets/ComboBox.cpp +++ b/src/gui/widgets/ComboBox.cpp @@ -26,16 +26,20 @@ #include "ComboBox.h" -#include -#include #include #include #include +#include #include "CaptionMenu.h" -#include "embed.h" #include "gui_templates.h" +#define QT_SUPPORTS_WIDGET_SCREEN (QT_VERSION >= QT_VERSION_CHECK(5,14,0)) +#if !QT_SUPPORTS_WIDGET_SCREEN +#include +#include +#endif + namespace lmms::gui { const int CB_ARROW_BTN_WIDTH = 18; @@ -49,7 +53,7 @@ ComboBox::ComboBox( QWidget * _parent, const QString & _name ) : { setFixedHeight( ComboBox::DEFAULT_HEIGHT ); - setFont( pointSize<9>( font() ) ); + setFont(adjustedToPixelSize(font(), 10)); connect( &m_menu, SIGNAL(triggered(QAction*)), this, SLOT(setItem(QAction*))); @@ -116,15 +120,23 @@ void ComboBox::mousePressEvent( QMouseEvent* event ) a->setData( i ); } - QPoint gpos = mapToGlobal( QPoint( 0, height() ) ); - if( gpos.y() + m_menu.sizeHint().height() < qApp->desktop()->height() ) + QPoint gpos = mapToGlobal(QPoint(0, height())); + + #if (QT_SUPPORTS_WIDGET_SCREEN) + bool const menuCanBeFullyShown = screen()->geometry().contains(QRect(gpos, m_menu.sizeHint())); + #else + bool const menuCanBeFullyShown = gpos.y() + m_menu.sizeHint().height() < qApp->desktop()->height(); + #endif + + if (menuCanBeFullyShown) { - m_menu.exec( gpos ); + m_menu.exec(gpos); } else { - m_menu.exec( mapToGlobal( QPoint( width(), 0 ) ) ); + m_menu.exec(mapToGlobal(QPoint(width(), 0))); } + m_pressed = false; update(); } @@ -208,7 +220,8 @@ void ComboBox::wheelEvent( QWheelEvent* event ) { if( model() ) { - model()->setInitValue(model()->value() + ((event->angleDelta().y() < 0) ? 1 : -1)); + const int direction = (event->angleDelta().y() < 0 ? 1 : -1) * (event->inverted() ? -1 : 1); + model()->setInitValue(model()->value() + direction); update(); event->accept(); } diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 6dbd9fbc337..9a0da4db483 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include "lmms_math.h" #include "embed.h" @@ -59,100 +60,60 @@ namespace lmms::gui { -SimpleTextFloat * Fader::s_textFloat = nullptr; - -Fader::Fader( FloatModel * _model, const QString & _name, QWidget * _parent ) : - QWidget( _parent ), - FloatModelView( _model, this ), - m_fPeakValue_L( 0.0 ), - m_fPeakValue_R( 0.0 ), - m_persistentPeak_L( 0.0 ), - m_persistentPeak_R( 0.0 ), - m_fMinPeak( 0.01f ), - m_fMaxPeak( 1.1 ), - m_back(embed::getIconPixmap("fader_background")), - m_leds(embed::getIconPixmap("fader_leds")), - m_knob(embed::getIconPixmap("fader_knob")), - m_levelsDisplayedInDBFS(false), - m_moveStartPoint( -1 ), - m_startValue( 0 ), - m_peakGreen( 0, 0, 0 ), - m_peakRed( 0, 0, 0 ), - m_peakYellow( 0, 0, 0 ) +SimpleTextFloat* Fader::s_textFloat = nullptr; + +Fader::Fader(FloatModel* model, const QString& name, QWidget* parent) : + QWidget(parent), + FloatModelView(model, this) { - if( s_textFloat == nullptr ) + if (s_textFloat == nullptr) { s_textFloat = new SimpleTextFloat; } - init(_model, _name); + setWindowTitle(name); + setAttribute(Qt::WA_OpaquePaintEvent, false); + // For now resize the widget to the size of the previous background image "fader_background.png" as it was found in the classic and default theme + constexpr QSize minimumSize(23, 116); + setMinimumSize(minimumSize); + resize(minimumSize); + setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + setModel(model); + setHintText("Volume:", "%"); m_conversionFactor = 100.0; } -Fader::Fader( FloatModel * model, const QString & name, QWidget * parent, QPixmap * back, QPixmap * leds, QPixmap * knob ) : - QWidget( parent ), - FloatModelView( model, this ), - m_fPeakValue_L( 0.0 ), - m_fPeakValue_R( 0.0 ), - m_persistentPeak_L( 0.0 ), - m_persistentPeak_R( 0.0 ), - m_fMinPeak( 0.01f ), - m_fMaxPeak( 1.1 ), - m_back(*back), - m_leds(*leds), - m_knob(*knob), - m_levelsDisplayedInDBFS(false), - m_moveStartPoint( -1 ), - m_startValue( 0 ), - m_peakGreen( 0, 0, 0 ), - m_peakRed( 0, 0, 0 ) +Fader::Fader(FloatModel* model, const QString& name, QWidget* parent, const QPixmap& knob) : + Fader(model, name, parent) { - if( s_textFloat == nullptr ) - { - s_textFloat = new SimpleTextFloat; - } - - init(model, name); + m_knob = knob; } -void Fader::init(FloatModel * model, QString const & name) -{ - setWindowTitle( name ); - setAttribute( Qt::WA_OpaquePaintEvent, false ); - QSize backgroundSize = m_back.size(); - setMinimumSize( backgroundSize ); - setMaximumSize( backgroundSize ); - resize( backgroundSize ); - setModel( model ); - setHintText( "Volume:","%"); -} - - -void Fader::contextMenuEvent( QContextMenuEvent * _ev ) +void Fader::contextMenuEvent(QContextMenuEvent* ev) { - CaptionMenu contextMenu( windowTitle() ); - addDefaultActions( &contextMenu ); - contextMenu.exec( QCursor::pos() ); - _ev->accept(); + CaptionMenu contextMenu(windowTitle()); + addDefaultActions(&contextMenu); + contextMenu.exec(QCursor::pos()); + ev->accept(); } -void Fader::mouseMoveEvent( QMouseEvent *mouseEvent ) +void Fader::mouseMoveEvent(QMouseEvent* mouseEvent) { - if( m_moveStartPoint >= 0 ) + if (m_moveStartPoint >= 0) { int dy = m_moveStartPoint - mouseEvent->globalY(); float delta = dy * (model()->maxValue() - model()->minValue()) / (float)(height() - (m_knob).height()); const auto step = model()->step(); - float newValue = static_cast( static_cast( ( m_startValue + delta ) / step + 0.5 ) ) * step; - model()->setValue( newValue ); + float newValue = static_cast(static_cast((m_startValue + delta) / step + 0.5)) * step; + model()->setValue(newValue); updateTextFloat(); } @@ -161,16 +122,16 @@ void Fader::mouseMoveEvent( QMouseEvent *mouseEvent ) -void Fader::mousePressEvent( QMouseEvent* mouseEvent ) +void Fader::mousePressEvent(QMouseEvent* mouseEvent) { - if( mouseEvent->button() == Qt::LeftButton && - ! ( mouseEvent->modifiers() & Qt::ControlModifier ) ) + if (mouseEvent->button() == Qt::LeftButton && + !(mouseEvent->modifiers() & Qt::ControlModifier)) { - AutomatableModel *thisModel = model(); - if( thisModel ) + AutomatableModel* thisModel = model(); + if (thisModel) { thisModel->addJournalCheckPoint(); - thisModel->saveJournallingState( false ); + thisModel->saveJournallingState(false); } if (mouseEvent->y() >= knobPosY() - (m_knob).height() && mouseEvent->y() < knobPosY()) @@ -190,39 +151,36 @@ void Fader::mousePressEvent( QMouseEvent* mouseEvent ) } else { - AutomatableModelView::mousePressEvent( mouseEvent ); + AutomatableModelView::mousePressEvent(mouseEvent); } } -void Fader::mouseDoubleClickEvent( QMouseEvent* mouseEvent ) +void Fader::mouseDoubleClickEvent(QMouseEvent* mouseEvent) { bool ok; - float newValue; - // TODO: dbV handling - newValue = QInputDialog::getDouble( this, tr( "Set value" ), - tr( "Please enter a new value between %1 and %2:" ). - arg( model()->minValue() * m_conversionFactor ). - arg( model()->maxValue() * m_conversionFactor ), - model()->getRoundedValue() * m_conversionFactor, - model()->minValue() * m_conversionFactor, - model()->maxValue() * m_conversionFactor, model()->getDigitCount(), &ok ) / m_conversionFactor; - - if( ok ) + // TODO: dbFS handling + auto minv = model()->minValue() * m_conversionFactor; + auto maxv = model()->maxValue() * m_conversionFactor; + float enteredValue = QInputDialog::getDouble(this, tr("Set value"), + tr("Please enter a new value between %1 and %2:").arg(minv).arg(maxv), + model()->getRoundedValue() * m_conversionFactor, minv, maxv, model()->getDigitCount(), &ok); + + if (ok) { - model()->setValue( newValue ); + model()->setValue(enteredValue / m_conversionFactor); } } -void Fader::mouseReleaseEvent( QMouseEvent * mouseEvent ) +void Fader::mouseReleaseEvent(QMouseEvent* mouseEvent) { - if( mouseEvent && mouseEvent->button() == Qt::LeftButton ) + if (mouseEvent && mouseEvent->button() == Qt::LeftButton) { - AutomatableModel *thisModel = model(); - if( thisModel ) + AutomatableModel* thisModel = model(); + if (thisModel) { thisModel->restoreJournallingState(); } @@ -232,20 +190,14 @@ void Fader::mouseReleaseEvent( QMouseEvent * mouseEvent ) } -void Fader::wheelEvent ( QWheelEvent *ev ) +void Fader::wheelEvent (QWheelEvent* ev) { ev->accept(); + const int direction = (ev->angleDelta().y() > 0 ? 1 : -1) * (ev->inverted() ? -1 : 1); - if (ev->angleDelta().y() > 0) - { - model()->incValue( 1 ); - } - else - { - model()->incValue( -1 ); - } + model()->incValue(direction); updateTextFloat(); - s_textFloat->setVisibilityTimeOut( 1000 ); + s_textFloat->setVisibilityTimeOut(1000); } @@ -253,47 +205,40 @@ void Fader::wheelEvent ( QWheelEvent *ev ) /// /// Set peak value (0.0 .. 1.0) /// -void Fader::setPeak( float fPeak, float &targetPeak, float &persistentPeak, QElapsedTimer &lastPeakTimer ) +void Fader::setPeak(float fPeak, float& targetPeak, float& persistentPeak, QElapsedTimer& lastPeakTimer) { - if( fPeak < m_fMinPeak ) - { - fPeak = m_fMinPeak; - } - else if( fPeak > m_fMaxPeak ) - { - fPeak = m_fMaxPeak; - } - - if( targetPeak != fPeak) + if (targetPeak != fPeak) { targetPeak = fPeak; - if( targetPeak >= persistentPeak ) + if (targetPeak >= persistentPeak) { persistentPeak = targetPeak; lastPeakTimer.restart(); + emit peakChanged(persistentPeak); } update(); } - if( persistentPeak > 0 && lastPeakTimer.elapsed() > 1500 ) + if (persistentPeak > 0 && lastPeakTimer.elapsed() > 1500) { - persistentPeak = qMax( 0, persistentPeak-0.05 ); + persistentPeak = qMax(0, persistentPeak-0.05); + emit peakChanged(persistentPeak); update(); } } -void Fader::setPeak_L( float fPeak ) +void Fader::setPeak_L(float fPeak) { - setPeak( fPeak, m_fPeakValue_L, m_persistentPeak_L, m_lastPeakTimer_L ); + setPeak(fPeak, m_fPeakValue_L, m_persistentPeak_L, m_lastPeakTimer_L); } -void Fader::setPeak_R( float fPeak ) +void Fader::setPeak_R(float fPeak) { - setPeak( fPeak, m_fPeakValue_R, m_persistentPeak_R, m_lastPeakTimer_R ); + setPeak(fPeak, m_fPeakValue_R, m_persistentPeak_R, m_lastPeakTimer_R); } @@ -301,169 +246,194 @@ void Fader::setPeak_R( float fPeak ) // update tooltip showing value and adjust position while changing fader value void Fader::updateTextFloat() { - if( ConfigManager::inst()->value( "app", "displaydbfs" ).toInt() && m_conversionFactor == 100.0 ) + if (ConfigManager::inst()->value("app", "displaydbfs").toInt() && m_conversionFactor == 100.0) { - s_textFloat->setText( QString("Volume: %1 dBFS"). - arg( ampToDbfs( model()->value() ), 3, 'f', 2 ) ); + QString label(tr("Volume: %1 dBFS")); + + auto const modelValue = model()->value(); + if (modelValue <= 0.) + { + s_textFloat->setText(label.arg("-∞")); + } + else + { + s_textFloat->setText(label.arg(ampToDbfs(modelValue), 3, 'f', 2)); + } } else { - s_textFloat->setText( m_description + " " + QString("%1 ").arg( model()->value() * m_conversionFactor ) + " " + m_unit ); + s_textFloat->setText(m_description + " " + QString("%1 ").arg(model()->value() * m_conversionFactor) + " " + m_unit); } s_textFloat->moveGlobal(this, QPoint(width() + 2, knobPosY() - s_textFloat->height() / 2)); } -inline int Fader::calculateDisplayPeak( float fPeak ) +void Fader::paintEvent(QPaintEvent* ev) { - int peak = static_cast(m_back.height() - (fPeak / (m_fMaxPeak - m_fMinPeak)) * m_back.height()); + QPainter painter(this); - return qMin(peak, m_back.height()); -} + // Draw the levels with peaks + paintLevels(ev, painter, !m_levelsDisplayedInDBFS); + // Draw the knob + painter.drawPixmap((width() - m_knob.width()) / 2, knobPosY() - m_knob.height(), m_knob); +} -void Fader::paintEvent( QPaintEvent * ev) +void Fader::paintLevels(QPaintEvent* ev, QPainter& painter, bool linear) { - QPainter painter(this); + std::function mapper = [this](float value) { return ampToDbfs(qMax(0.0001f, value)); }; - // Draw the background - painter.drawPixmap(ev->rect(), m_back, ev->rect()); - - // Draw the levels with peaks - if (getLevelsDisplayedInDBFS()) - { - paintDBFSLevels(ev, painter); - } - else + if (linear) { - paintLinearLevels(ev, painter); + mapper = [this](float value) { return value; }; } - // Draw the knob - painter.drawPixmap(0, knobPosY() - m_knob.height(), m_knob); -} + const float mappedMinPeak(mapper(m_fMinPeak)); + const float mappedMaxPeak(mapper(m_fMaxPeak)); + const float mappedPeakL(mapper(m_fPeakValue_L)); + const float mappedPeakR(mapper(m_fPeakValue_R)); + const float mappedPersistentPeakL(mapper(m_persistentPeak_L)); + const float mappedPersistentPeakR(mapper(m_persistentPeak_R)); + const float mappedUnity(mapper(1.f)); -void Fader::paintDBFSLevels(QPaintEvent * ev, QPainter & painter) -{ - int height = m_back.height(); - int width = m_back.width() / 2; - int center = m_back.width() - width; + painter.save(); - float const maxDB(ampToDbfs(m_fMaxPeak)); - float const minDB(ampToDbfs(m_fMinPeak)); + const QRect baseRect = rect(); - // We will need to divide by the span between min and max several times. It's more - // efficient to calculate the reciprocal once and then to multiply. - float const fullSpanReciprocal = 1 / (maxDB - minDB); + const int height = baseRect.height(); + const int margin = 1; + const int distanceBetweenMeters = 2; - // Draw left levels - float const leftSpan = ampToDbfs(qMax(0.0001, m_fPeakValue_L)) - minDB; - int peak_L = height * leftSpan * fullSpanReciprocal; - QRect drawRectL( 0, height - peak_L, width, peak_L ); // Source and target are identical - painter.drawPixmap(drawRectL, m_leds, drawRectL); - - float const persistentLeftPeakDBFS = ampToDbfs(qMax(0.0001, m_persistentPeak_L)); - int persistentPeak_L = height * (1 - (persistentLeftPeakDBFS - minDB) * fullSpanReciprocal); - // the LED's have a 4px padding and we don't want the peaks - // to draw on the fader background - if( persistentPeak_L <= 4 ) - { - persistentPeak_L = 4; - } - if( persistentLeftPeakDBFS > minDB ) - { - QColor const & peakColor = clips(m_persistentPeak_L) ? peakRed() : - persistentLeftPeakDBFS >= -6 ? peakYellow() : peakGreen(); - painter.fillRect( QRect( 2, persistentPeak_L, 7, 1 ), peakColor ); - } + const int numberOfMeters = 2; + // Compute the width of a single meter by removing the margins and the space between meters + const int leftAndRightMargin = 2 * margin; + const int pixelsBetweenAllMeters = distanceBetweenMeters * (numberOfMeters - 1); + const int remainingPixelsForMeters = baseRect.width() - leftAndRightMargin - pixelsBetweenAllMeters; + const int meterWidth = remainingPixelsForMeters / numberOfMeters; - // Draw right levels - float const rightSpan = ampToDbfs(qMax(0.0001, m_fPeakValue_R)) - minDB; - int peak_R = height * rightSpan * fullSpanReciprocal; - QRect const drawRectR( center, height - peak_R, width, peak_R ); // Source and target are identical - painter.drawPixmap(drawRectR, m_leds, drawRectR); - - float const persistentRightPeakDBFS = ampToDbfs(qMax(0.0001, m_persistentPeak_R)); - int persistentPeak_R = height * (1 - (persistentRightPeakDBFS - minDB) * fullSpanReciprocal); - // the LED's have a 4px padding and we don't want the peaks - // to draw on the fader background - if( persistentPeak_R <= 4 ) - { - persistentPeak_R = 4; - } - if( persistentRightPeakDBFS > minDB ) - { - QColor const & peakColor = clips(m_persistentPeak_R) ? peakRed() : - persistentRightPeakDBFS >= -6 ? peakYellow() : peakGreen(); - painter.fillRect( QRect( 14, persistentPeak_R, 7, 1 ), peakColor ); - } -} + QRect leftMeterOutlineRect(margin, margin, meterWidth, height - 2 * margin); + QRect rightMeterOutlineRect(baseRect.width() - margin - meterWidth, margin, meterWidth, height - 2 * margin); -void Fader::paintLinearLevels(QPaintEvent * ev, QPainter & painter) -{ - // peak leds - //float fRange = abs( m_fMaxPeak ) + abs( m_fMinPeak ); + QMargins removedMargins(1, 1, 1, 1); + QRect leftMeterRect = leftMeterOutlineRect.marginsRemoved(removedMargins); + QRect rightMeterRect = rightMeterOutlineRect.marginsRemoved(removedMargins); + + QPainterPath path; + qreal radius = 2; + path.addRoundedRect(leftMeterOutlineRect, radius, radius); + path.addRoundedRect(rightMeterOutlineRect, radius, radius); + painter.fillPath(path, Qt::black); - int height = m_back.height(); - int width = m_back.width() / 2; - int center = m_back.width() - width; + // Now clip everything to the paths of the meters + painter.setClipPath(path); - int peak_L = calculateDisplayPeak( m_fPeakValue_L - m_fMinPeak ); - int persistentPeak_L = qMax( 3, calculateDisplayPeak( m_persistentPeak_L - m_fMinPeak ) ); - painter.drawPixmap(QRect(0, peak_L, width, height - peak_L), m_leds, QRect(0, peak_L, width, height - peak_L)); + // This linear map performs the following mapping: + // Value (dbFS or linear) -> window coordinates of the widget + // It is for example used to determine the height of peaks, markers and to define the gradient for the levels + const LinearMap valuesToWindowCoordinates(mappedMaxPeak, leftMeterRect.y(), mappedMinPeak, leftMeterRect.y() + leftMeterRect.height()); - if( m_persistentPeak_L > 0.05 ) + // This lambda takes a value (in dbFS or linear) and a rectangle and computes a rectangle + // that represent the value within the rectangle. It is for example used to compute the unity indicators. + const auto computeLevelMarkerRect = [&valuesToWindowCoordinates](const QRect& rect, float peak) -> QRect { - painter.fillRect( QRect( 2, persistentPeak_L, 7, 1 ), ( m_persistentPeak_L < 1.0 ) - ? peakGreen() - : peakRed() ); - } + return QRect(rect.x(), valuesToWindowCoordinates.map(peak), rect.width(), 1); + }; - int peak_R = calculateDisplayPeak( m_fPeakValue_R - m_fMinPeak ); - int persistentPeak_R = qMax( 3, calculateDisplayPeak( m_persistentPeak_R - m_fMinPeak ) ); - painter.drawPixmap(QRect(center, peak_R, width, height - peak_R), m_leds, QRect(center, peak_R, width, height - peak_R)); + // This lambda takes a peak value (in dbFS or linear) and a rectangle and computes a rectangle + // that represent the peak value within the rectangle. It's used to compute the peak indicators + // which "dance" on top of the level meters. + const auto computePeakRect = [&valuesToWindowCoordinates](const QRect& rect, float peak) -> QRect + { + return QRect(rect.x(), valuesToWindowCoordinates.map(peak), rect.width(), 1); + }; - if( m_persistentPeak_R > 0.05 ) + // This lambda takes a peak value (in dbFS or linear) and a rectangle and returns an adjusted copy of the + // rectangle that represents the peak value. It is used to compute the level meters themselves. + const auto computeLevelRect = [&valuesToWindowCoordinates](const QRect& rect, float peak) -> QRect { - painter.fillRect( QRect( 14, persistentPeak_R, 7, 1 ), ( m_persistentPeak_R < 1.0 ) - ? peakGreen() - : peakRed() ); - } -} + QRect result(rect); + result.setTop(valuesToWindowCoordinates.map(peak)); + return result; + }; -QColor const & Fader::peakGreen() const -{ - return m_peakGreen; -} + // Draw left and right level markers for the unity lines (0 dbFS, 1.0 amplitude) + if (getRenderUnityLine()) + { + const auto unityRectL = computeLevelMarkerRect(leftMeterRect, mappedUnity); + painter.fillRect(unityRectL, m_unityMarker); -QColor const & Fader::peakRed() const -{ - return m_peakRed; -} + const auto unityRectR = computeLevelMarkerRect(rightMeterRect, mappedUnity); + painter.fillRect(unityRectR, m_unityMarker); + } -QColor const & Fader::peakYellow() const -{ - return m_peakYellow; -} + // These values define where the gradient changes values, i.e. the ranges + // for clipping, warning and ok. + // Please ensure that "clip starts" is the maximum value and that "ok ends" + // is the minimum value and that all other values lie inbetween. Otherwise + // there will be warnings when the gradient is defined. + const float mappedClipStarts(mapper(dbfsToAmp(0.f))); + const float mappedWarnEnd(mapper(dbfsToAmp(-0.01f))); + const float mappedWarnStart(mapper(dbfsToAmp(-6.f))); + const float mappedOkEnd(mapper(dbfsToAmp(-12.f))); + + // Prepare the gradient for the meters + // + // The idea is the following. We want to be able to render arbitrary ranges of min and max values. + // Therefore we first compute the start and end point of the gradient in window coordinates. + // The gradient is assumed to start with the clip color and to end with the ok color with warning values in between. + // We know the min and max peaks that map to a rectangle where we draw the levels. We can use the values of the min and max peaks + // as well as the Y-coordinates of the rectangle to compute a map which will give us the coordinates of the value where the clipping + // starts and where the ok area end. These coordinates are used to initialize the gradient. Please note that the gradient might thus + // extend the rectangle into which we paint. + float clipStartYCoord = valuesToWindowCoordinates.map(mappedClipStarts); + float okEndYCoord = valuesToWindowCoordinates.map(mappedOkEnd); + + QLinearGradient linearGrad(0, clipStartYCoord, 0, okEndYCoord); + + // We already know for the gradient that the clip color will be at 0 and that the ok color is at 1. + // What's left to do is to map the inbetween values into the interval [0,1]. + const LinearMap mapBetweenClipAndOk(mappedClipStarts, 0.f, mappedOkEnd, 1.f); + + linearGrad.setColorAt(0, m_peakClip); + linearGrad.setColorAt(mapBetweenClipAndOk.map(mappedWarnEnd), m_peakWarn); + linearGrad.setColorAt(mapBetweenClipAndOk.map(mappedWarnStart), m_peakWarn); + linearGrad.setColorAt(1, m_peakOk); -void Fader::setPeakGreen( const QColor & c ) -{ - m_peakGreen = c; -} + // Draw left levels + if (mappedPeakL > mappedMinPeak) + { + QPainterPath leftMeterPath; + leftMeterPath.addRoundedRect(computeLevelRect(leftMeterRect, mappedPeakL), radius, radius); + painter.fillPath(leftMeterPath, linearGrad); + } -void Fader::setPeakRed( const QColor & c ) -{ - m_peakRed = c; -} + // Draw left peaks + if (mappedPersistentPeakL > mappedMinPeak) + { + const auto peakRectL = computePeakRect(leftMeterRect, mappedPersistentPeakL); + painter.fillRect(peakRectL, linearGrad); + } -void Fader::setPeakYellow( const QColor & c ) -{ - m_peakYellow = c; -} + // Draw right levels + if (mappedPeakR > mappedMinPeak) + { + QPainterPath rightMeterPath; + rightMeterPath.addRoundedRect(computeLevelRect(rightMeterRect, mappedPeakR), radius, radius); + painter.fillPath(rightMeterPath, linearGrad); + } + // Draw right peaks + if (mappedPersistentPeakR > mappedMinPeak) + { + const auto peakRectR = computePeakRect(rightMeterRect, mappedPersistentPeakR); + painter.fillRect(peakRectR, linearGrad); + } + + painter.restore(); +} } // namespace lmms::gui diff --git a/src/gui/widgets/FloatModelEditorBase.cpp b/src/gui/widgets/FloatModelEditorBase.cpp index 7421908e2d2..ebd0d3d9dc7 100644 --- a/src/gui/widgets/FloatModelEditorBase.cpp +++ b/src/gui/widgets/FloatModelEditorBase.cpp @@ -326,6 +326,11 @@ void FloatModelEditorBase::wheelEvent(QWheelEvent * we) } } + // Handle "natural" scrolling, which is common on trackpads and touch devices + if (we->inverted()) { + direction = -direction; + } + // Compute the number of steps but make sure that we always do at least one step const float stepMult = std::max(range / numberOfStepsForFullSweep / step, 1.f); const int inc = direction * stepMult; @@ -388,12 +393,15 @@ void FloatModelEditorBase::enterValue() if (isVolumeKnob() && ConfigManager::inst()->value("app", "displaydbfs").toInt()) { + auto const initalValue = model()->getRoundedValue() / 100.0; + auto const initialDbValue = initalValue > 0. ? ampToDbfs(initalValue) : -96; + new_val = QInputDialog::getDouble( this, tr("Set value"), tr("Please enter a new value between " "-96.0 dBFS and 6.0 dBFS:"), - ampToDbfs(model()->getRoundedValue() / 100.0), - -96.0, 6.0, model()->getDigitCount(), &ok); + initialDbValue, -96.0, 6.0, model()->getDigitCount(), &ok); + if (new_val <= -96.0) { new_val = 0.0f; @@ -439,9 +447,12 @@ QString FloatModelEditorBase::displayValue() const if (isVolumeKnob() && ConfigManager::inst()->value("app", "displaydbfs").toInt()) { - return m_description.trimmed() + QString(" %1 dBFS"). - arg(ampToDbfs(model()->getRoundedValue() / volumeRatio()), - 3, 'f', 2); + auto const valueToVolumeRatio = model()->getRoundedValue() / volumeRatio(); + return m_description.trimmed() + ( + valueToVolumeRatio == 0. + ? QString(" -∞ dBFS") + : QString(" %1 dBFS").arg(ampToDbfs(valueToVolumeRatio), 3, 'f', 2) + ); } return m_description.trimmed() + QString(" %1"). diff --git a/src/gui/widgets/Graph.cpp b/src/gui/widgets/Graph.cpp index 922b98668fb..0781d4f1113 100644 --- a/src/gui/widgets/Graph.cpp +++ b/src/gui/widgets/Graph.cpp @@ -643,11 +643,10 @@ void graphModel::convolve(const float *convolution, // store values in temporary array QVector temp = m_samples; const int graphLength = length(); - float sum; // make a cyclic convolution for ( int i = 0; i < graphLength; i++ ) { - sum = 0; + float sum = 0.0f; for ( int j = 0; j < convolutionLength; j++ ) { sum += convolution[j] * temp[( i + j ) % graphLength]; diff --git a/src/gui/widgets/GroupBox.cpp b/src/gui/widgets/GroupBox.cpp index e3e71a8124e..e7d78acb9dd 100644 --- a/src/gui/widgets/GroupBox.cpp +++ b/src/gui/widgets/GroupBox.cpp @@ -111,7 +111,7 @@ void GroupBox::paintEvent( QPaintEvent * pe ) // draw text p.setPen( palette().color( QPalette::Active, QPalette::Text ) ); - p.setFont( pointSize<8>( font() ) ); + p.setFont(adjustedToPixelSize(font(), 10)); int const captionX = ledButtonShown() ? 22 : 6; p.drawText(captionX, m_titleBarHeight, m_caption); diff --git a/src/gui/widgets/Knob.cpp b/src/gui/widgets/Knob.cpp index 00a9363c87f..d282f72c20f 100644 --- a/src/gui/widgets/Knob.cpp +++ b/src/gui/widgets/Knob.cpp @@ -139,7 +139,7 @@ void Knob::setLabel( const QString & txt ) if( m_knobPixmap ) { setFixedSize(qMax( m_knobPixmap->width(), - horizontalAdvance(QFontMetrics(pointSizeF(font(), 6.5)), m_label)), + horizontalAdvance(QFontMetrics(adjustedToPixelSize(font(), 10)), m_label)), m_knobPixmap->height() + 10); } @@ -459,7 +459,7 @@ void Knob::paintEvent( QPaintEvent * _me ) { if (!m_isHtmlLabel) { - p.setFont(pointSizeF(p.font(), 6.5)); + p.setFont(adjustedToPixelSize(p.font(), 10)); p.setPen(textColor()); p.drawText(width() / 2 - horizontalAdvance(p.fontMetrics(), m_label) / 2, @@ -467,7 +467,8 @@ void Knob::paintEvent( QPaintEvent * _me ) } else { - m_tdRenderer->setDefaultFont(pointSizeF(p.font(), 6.5)); + // TODO setHtmlLabel is never called so this will never be executed. Remove functionality? + m_tdRenderer->setDefaultFont(adjustedToPixelSize(p.font(), 10)); p.translate((width() - m_tdRenderer->idealWidth()) / 2, (height() - m_tdRenderer->pageSize().height()) / 2); m_tdRenderer->drawContents(&p); } diff --git a/src/gui/widgets/LcdFloatSpinBox.cpp b/src/gui/widgets/LcdFloatSpinBox.cpp index c7e20467a5b..c71d665689e 100644 --- a/src/gui/widgets/LcdFloatSpinBox.cpp +++ b/src/gui/widgets/LcdFloatSpinBox.cpp @@ -245,7 +245,7 @@ void LcdFloatSpinBox::paintEvent(QPaintEvent*) // Label if (!m_label.isEmpty()) { - p.setFont(pointSizeF(p.font(), 6.5)); + p.setFont(adjustedToPixelSize(p.font(), 10)); p.setPen(m_wholeDisplay.textShadowColor()); p.drawText(width() / 2 - p.fontMetrics().boundingRect(m_label).width() / 2 + 1, height(), m_label); p.setPen(m_wholeDisplay.textColor()); diff --git a/src/gui/widgets/LcdSpinBox.cpp b/src/gui/widgets/LcdSpinBox.cpp index b53d7ddb5b0..3f12360ccdc 100644 --- a/src/gui/widgets/LcdSpinBox.cpp +++ b/src/gui/widgets/LcdSpinBox.cpp @@ -141,7 +141,9 @@ void LcdSpinBox::mouseReleaseEvent(QMouseEvent*) void LcdSpinBox::wheelEvent(QWheelEvent * we) { we->accept(); - model()->setValue(model()->value() + ((we->angleDelta().y() > 0) ? 1 : -1) * model()->step()); + const int direction = (we->angleDelta().y() > 0 ? 1 : -1) * (we->inverted() ? -1 : 1); + + model()->setValue(model()->value() + direction * model()->step()); emit manualChange(); } diff --git a/src/gui/widgets/LcdWidget.cpp b/src/gui/widgets/LcdWidget.cpp index a409fee8bf6..7370a939fe3 100644 --- a/src/gui/widgets/LcdWidget.cpp +++ b/src/gui/widgets/LcdWidget.cpp @@ -78,20 +78,26 @@ void LcdWidget::setValue(int value) } } - m_display = s; + if (m_display != s) + { + m_display = s; - update(); + update(); + } } void LcdWidget::setValue(float value) { - if (value < 0 && value > -1) + if (-1 < value && value < 0) { QString s = QString::number(static_cast(value)); s.prepend('-'); - m_display = s; - update(); + if (m_display != s) + { + m_display = s; + update(); + } } else { @@ -203,7 +209,7 @@ void LcdWidget::paintEvent( QPaintEvent* ) // Label if( !m_label.isEmpty() ) { - p.setFont( pointSizeF( p.font(), 6.5 ) ); + p.setFont(adjustedToPixelSize(p.font(), 10)); p.setPen( textShadowColor() ); p.drawText(width() / 2 - horizontalAdvance(p.fontMetrics(), m_label) / 2 + 1, @@ -255,7 +261,7 @@ void LcdWidget::updateSize() setFixedSize( qMax( m_cellWidth * m_numDigits + marginX1 + marginX2, - horizontalAdvance(QFontMetrics(pointSizeF(font(), 6.5)), m_label) + horizontalAdvance(QFontMetrics(adjustedToPixelSize(font(), 10)), m_label) ), m_cellHeight + (2 * marginY) + 9 ); diff --git a/src/gui/widgets/LedCheckBox.cpp b/src/gui/widgets/LedCheckBox.cpp index 1dbf650ed6e..c26e2103948 100644 --- a/src/gui/widgets/LedCheckBox.cpp +++ b/src/gui/widgets/LedCheckBox.cpp @@ -22,11 +22,12 @@ * */ +#include "LedCheckBox.h" #include #include +#include -#include "LedCheckBox.h" #include "DeprecationHelper.h" #include "embed.h" #include "gui_templates.h" @@ -92,10 +93,7 @@ void LedCheckBox::initUi( LedColor _color ) m_ledOnPixmap = embed::getIconPixmap(names[static_cast(_color)].toUtf8().constData()); m_ledOffPixmap = embed::getIconPixmap("led_off"); - if (m_legacyMode) - { - setFont( pointSize<7>( font() ) ); - } + if (m_legacyMode){ setFont(adjustedToPixelSize(font(), 10)); } setText( m_text ); } @@ -116,7 +114,7 @@ void LedCheckBox::onTextUpdated() void LedCheckBox::paintLegacy(QPaintEvent * pe) { QPainter p( this ); - p.setFont( pointSize<7>( font() ) ); + p.setFont(adjustedToPixelSize(font(), 10)); p.drawPixmap(0, 0, model()->value() ? m_ledOnPixmap : m_ledOffPixmap); diff --git a/src/gui/widgets/MeterDialog.cpp b/src/gui/widgets/MeterDialog.cpp index ced08382ef1..d01dca9a866 100644 --- a/src/gui/widgets/MeterDialog.cpp +++ b/src/gui/widgets/MeterDialog.cpp @@ -30,7 +30,6 @@ #include "MeterDialog.h" #include "MeterModel.h" -#include "gui_templates.h" #include "LcdSpinBox.h" namespace lmms::gui @@ -60,7 +59,6 @@ MeterDialog::MeterDialog( QWidget * _parent, bool _simple ) : { auto num_label = new QLabel(tr("Meter Numerator"), num); QFont f = num_label->font(); - num_label->setFont( pointSize<7>( f ) ); num_layout->addSpacing( 5 ); num_layout->addWidget( num_label ); } @@ -84,7 +82,6 @@ MeterDialog::MeterDialog( QWidget * _parent, bool _simple ) : { auto den_label = new QLabel(tr("Meter Denominator"), den); QFont f = den_label->font(); - den_label->setFont( pointSize<7>( f ) ); den_layout->addSpacing( 5 ); den_layout->addWidget( den_label ); } diff --git a/src/gui/widgets/MixerChannelLcdSpinBox.cpp b/src/gui/widgets/MixerChannelLcdSpinBox.cpp index 8a67394de91..73e21b479b4 100644 --- a/src/gui/widgets/MixerChannelLcdSpinBox.cpp +++ b/src/gui/widgets/MixerChannelLcdSpinBox.cpp @@ -24,6 +24,9 @@ #include "MixerChannelLcdSpinBox.h" +#include +#include + #include "CaptionMenu.h" #include "MixerView.h" #include "GuiApplication.h" @@ -40,6 +43,13 @@ void MixerChannelLcdSpinBox::setTrackView(TrackView * tv) void MixerChannelLcdSpinBox::mouseDoubleClickEvent(QMouseEvent* event) { + if (!(event->modifiers() & Qt::ShiftModifier) && + !(event->modifiers() & Qt::ControlModifier)) + { + enterValue(); + return; + } + getGUI()->mixerView()->setCurrentMixerChannel(model()->value()); getGUI()->mixerView()->parentWidget()->show(); @@ -69,5 +79,18 @@ void MixerChannelLcdSpinBox::contextMenuEvent(QContextMenuEvent* event) contextMenu->exec(QCursor::pos()); } +void MixerChannelLcdSpinBox::enterValue() +{ + const auto val = model()->value(); + const auto min = model()->minValue(); + const auto max = model()->maxValue(); + const auto step = model()->step(); + const auto label = tr("Please enter a new value between %1 and %2:").arg(min).arg(max); + + auto ok = false; + const auto newVal = QInputDialog::getInt(this, tr("Set value"), label, val, min, max, step, &ok); + + if (ok) { model()->setValue(newVal); } +} } // namespace lmms::gui diff --git a/src/gui/widgets/Oscilloscope.cpp b/src/gui/widgets/Oscilloscope.cpp index a689f53f367..775bd96a8b8 100644 --- a/src/gui/widgets/Oscilloscope.cpp +++ b/src/gui/widgets/Oscilloscope.cpp @@ -45,7 +45,9 @@ Oscilloscope::Oscilloscope( QWidget * _p ) : m_background( embed::getIconPixmap( "output_graph" ) ), m_points( new QPointF[Engine::audioEngine()->framesPerPeriod()] ), m_active( false ), - m_normalColor(71, 253, 133), + m_leftChannelColor(71, 253, 133), + m_rightChannelColor(71, 253, 133), + m_otherChannelsColor(71, 253, 133), m_clippingColor(255, 64, 64) { setFixedSize( m_background.width(), m_background.height() ); @@ -53,9 +55,9 @@ Oscilloscope::Oscilloscope( QWidget * _p ) : setActive( ConfigManager::inst()->value( "ui", "displaywaveform").toInt() ); const fpp_t frames = Engine::audioEngine()->framesPerPeriod(); - m_buffer = new sampleFrame[frames]; + m_buffer = new SampleFrame[frames]; - BufferManager::clear( m_buffer, frames ); + zeroSampleFrames(m_buffer, frames); setToolTip(tr("Oscilloscope")); @@ -73,12 +75,12 @@ Oscilloscope::~Oscilloscope() -void Oscilloscope::updateAudioBuffer( const surroundSampleFrame * buffer ) +void Oscilloscope::updateAudioBuffer(const SampleFrame* buffer) { if( !Engine::getSong()->isExporting() ) { const fpp_t fpp = Engine::audioEngine()->framesPerPeriod(); - memcpy( m_buffer, buffer, sizeof( surroundSampleFrame ) * fpp ); + memcpy(m_buffer, buffer, sizeof(SampleFrame) * fpp); } } @@ -94,8 +96,8 @@ void Oscilloscope::setActive( bool _active ) SIGNAL(periodicUpdate()), this, SLOT(update())); connect( Engine::audioEngine(), - SIGNAL(nextAudioBuffer(const lmms::surroundSampleFrame*)), - this, SLOT(updateAudioBuffer(const lmms::surroundSampleFrame*)) ); + SIGNAL(nextAudioBuffer(const lmms::SampleFrame*)), + this, SLOT(updateAudioBuffer(const lmms::SampleFrame*))); } else { @@ -103,8 +105,8 @@ void Oscilloscope::setActive( bool _active ) SIGNAL(periodicUpdate()), this, SLOT(update())); disconnect( Engine::audioEngine(), - SIGNAL( nextAudioBuffer( const lmms::surroundSampleFrame* ) ), - this, SLOT( updateAudioBuffer( const lmms::surroundSampleFrame* ) ) ); + SIGNAL(nextAudioBuffer(const lmms::SampleFrame*)), + this, SLOT(updateAudioBuffer(const lmms::SampleFrame*))); // we have to update (remove last waves), // because timer doesn't do that anymore update(); @@ -112,14 +114,34 @@ void Oscilloscope::setActive( bool _active ) } -QColor const & Oscilloscope::normalColor() const +QColor const & Oscilloscope::leftChannelColor() const { - return m_normalColor; + return m_leftChannelColor; } -void Oscilloscope::setNormalColor(QColor const & normalColor) +void Oscilloscope::setLeftChannelColor(QColor const & leftChannelColor) { - m_normalColor = normalColor; + m_leftChannelColor = leftChannelColor; +} + +QColor const & Oscilloscope::rightChannelColor() const +{ + return m_rightChannelColor; +} + +void Oscilloscope::setRightChannelColor(QColor const & rightChannelColor) +{ + m_rightChannelColor = rightChannelColor; +} + +QColor const & Oscilloscope::otherChannelsColor() const +{ + return m_otherChannelsColor; +} + +void Oscilloscope::setOtherChannelsColor(QColor const & otherChannelsColor) +{ + m_otherChannelsColor = otherChannelsColor; } QColor const & Oscilloscope::clippingColor() const @@ -146,12 +168,10 @@ void Oscilloscope::paintEvent( QPaintEvent * ) float masterOutput = audioEngine->masterGain(); const fpp_t frames = audioEngine->framesPerPeriod(); - AudioEngine::StereoSample peakValues = audioEngine->getPeakValues(m_buffer, frames); - const float max_level = qMax( peakValues.left, peakValues.right ); + SampleFrame peakValues = getAbsPeakValues(m_buffer, frames); - // Set the color of the line according to the maximum level - float const maxLevelWithAppliedMasterGain = max_level * masterOutput; - p.setPen(QPen(determineLineColor(maxLevelWithAppliedMasterGain), 0.7)); + auto const leftChannelClips = clips(peakValues.left() * masterOutput); + auto const rightChannelClips = clips(peakValues.right() * masterOutput); p.setRenderHint( QPainter::Antialiasing ); @@ -162,9 +182,15 @@ void Oscilloscope::paintEvent( QPaintEvent * ) int x_base = 2; const qreal y_base = height() / 2 - 0.5; + qreal const width = 0.7; for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch ) { - for( int frame = 0; frame < frames; ++frame ) + QColor color = ch == 0 ? (leftChannelClips ? clippingColor() : leftChannelColor()) : // Check left channel + ch == 1 ? (rightChannelClips ? clippingColor() : rightChannelColor()) : // Check right channel + otherChannelsColor(); // Any other channel + p.setPen(QPen(color, width)); + + for (auto frame = std::size_t{0}; frame < frames; ++frame) { sample_t const clippedSample = AudioEngine::clip(m_buffer[frame][ch]); m_points[frame] = QPointF( @@ -177,7 +203,7 @@ void Oscilloscope::paintEvent( QPaintEvent * ) else { p.setPen( QColor( 192, 192, 192 ) ); - p.setFont( pointSize<7>( p.font() ) ); + p.setFont(adjustedToPixelSize(p.font(), 10)); p.drawText( 6, height()-5, tr( "Click to enable" ) ); } } @@ -193,17 +219,9 @@ void Oscilloscope::mousePressEvent( QMouseEvent * _me ) } } - -QColor const & Oscilloscope::determineLineColor(float level) const +bool Oscilloscope::clips(float level) const { - if( level <= 1.0f ) - { - return normalColor(); - } - else - { - return clippingColor(); - } + return level > 1.0f; } diff --git a/src/gui/widgets/PeakIndicator.cpp b/src/gui/widgets/PeakIndicator.cpp new file mode 100644 index 00000000000..e1278b7ff2d --- /dev/null +++ b/src/gui/widgets/PeakIndicator.cpp @@ -0,0 +1,84 @@ +/* + * PeakIndicator.cpp - Peak indicator widget + * + * Copyright (c) 2024- Michael Gregorius + * + * This file is part of LMMS - https://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "PeakIndicator.h" + +#include "lmms_math.h" + +#include +#include + + +namespace lmms::gui +{ + +PeakIndicator::PeakIndicator(QWidget* parent) : + QLabel(parent), + m_peak(0.f) +{ + setAlignment(Qt::AlignCenter); + + updatePeakDisplay(); +} + +void PeakIndicator::resetPeakToMinusInf() +{ + m_peak = 0; + updatePeakDisplay(); +} + +void PeakIndicator::updatePeak(float peak) +{ + if (peak > m_peak) + { + m_peak = peak; + updatePeakDisplay(); + } +} + +void PeakIndicator::mousePressEvent(QMouseEvent* e) +{ + if (e->buttons() & Qt::LeftButton) + { + resetPeakToMinusInf(); + } +} + +void PeakIndicator::updatePeakDisplay() +{ + // Treat everything below -144 dbFS as -inf. Otherwise some residual signals show up + // in the form of very small dbFS values, e.g. -857.1 dbFS. + // TODO Make the threshold configurable in the settings? + if (m_peak <= dbfsToAmp(-144.)) + { + setText(tr("-inf")); + } + else + { + auto dbfs = ampToDbfs(m_peak); + setText(QString::number(dbfs, 'f', 1)); + } +} + +} // namespace lmms::gui diff --git a/src/gui/widgets/TabBar.cpp b/src/gui/widgets/TabBar.cpp index e2949455138..d77573a9218 100644 --- a/src/gui/widgets/TabBar.cpp +++ b/src/gui/widgets/TabBar.cpp @@ -25,7 +25,6 @@ #include "TabBar.h" #include "TabButton.h" -#include "gui_templates.h" namespace lmms::gui @@ -90,8 +89,6 @@ TabButton * TabBar::addTab( QWidget * _w, const QString & _text, int _id, _w->setFixedSize( _w->parentWidget()->size() ); } - b->setFont( pointSize<8>( b->font() ) ); - return( b ); } diff --git a/src/gui/widgets/TabWidget.cpp b/src/gui/widgets/TabWidget.cpp index 27671933eea..a370c1ea9fb 100644 --- a/src/gui/widgets/TabWidget.cpp +++ b/src/gui/widgets/TabWidget.cpp @@ -58,7 +58,7 @@ TabWidget::TabWidget(const QString& caption, QWidget* parent, bool usePixmap, m_tabheight = caption.isEmpty() ? m_tabbarHeight - 3 : m_tabbarHeight - 4; - setFont(pointSize<8>(font())); + setFont(adjustedToPixelSize(font(), 10)); setAutoFillBackground(true); QColor bg_color = QApplication::palette().color(QPalette::Active, QPalette::Window).darker(132); @@ -70,8 +70,6 @@ TabWidget::TabWidget(const QString& caption, QWidget* parent, bool usePixmap, void TabWidget::addTab(QWidget* w, const QString& name, const char* pixmap, int idx) { - setFont(pointSize<8>(font())); - // Append tab when position is not given if (idx < 0/* || m_widgets.contains(idx) == true*/) { @@ -216,7 +214,7 @@ void TabWidget::resizeEvent(QResizeEvent*) void TabWidget::paintEvent(QPaintEvent* pe) { QPainter p(this); - p.setFont(pointSize<7>(font())); + p.setFont(adjustedToPixelSize(font(), 10)); // Draw background QBrush bg_color = p.background(); @@ -232,7 +230,6 @@ void TabWidget::paintEvent(QPaintEvent* pe) // Draw title, if any if (!m_caption.isEmpty()) { - p.setFont(pointSize<8>(p.font())); p.setPen(tabTitleText()); p.drawText(5, 11, m_caption); } diff --git a/src/gui/widgets/TimeDisplayWidget.cpp b/src/gui/widgets/TimeDisplayWidget.cpp index 3dad6b1b022..92eaf1efe2a 100644 --- a/src/gui/widgets/TimeDisplayWidget.cpp +++ b/src/gui/widgets/TimeDisplayWidget.cpp @@ -91,24 +91,25 @@ void TimeDisplayWidget::updateTime() switch( m_displayMode ) { case DisplayMode::MinutesSeconds: - int msec; - msec = s->getMilliseconds(); + { + int msec = s->getMilliseconds(); m_majorLCD.setValue(msec / 60000); m_minorLCD.setValue((msec / 1000) % 60); m_milliSecondsLCD.setValue(msec % 1000); break; - + } case DisplayMode::BarsTicks: - int tick; - tick = s->getPlayPos().getTicks(); + { + int tick = s->getPlayPos().getTicks(); m_majorLCD.setValue((int)(tick / s->ticksPerBar()) + 1); m_minorLCD.setValue((tick % s->ticksPerBar()) / (s->ticksPerBar() / s->getTimeSigModel().getNumerator() ) +1); m_milliSecondsLCD.setValue((tick % s->ticksPerBar()) % (s->ticksPerBar() / s->getTimeSigModel().getNumerator())); break; - - default: break; + } + default: + break; } } diff --git a/src/lmmsconfig.h.in b/src/lmmsconfig.h.in index 89db21a7bfb..867a04a4535 100644 --- a/src/lmmsconfig.h.in +++ b/src/lmmsconfig.h.in @@ -31,7 +31,6 @@ #cmakedefine LMMS_HAVE_SOUNDIO #cmakedefine LMMS_HAVE_PULSEAUDIO #cmakedefine LMMS_HAVE_SDL -#cmakedefine LMMS_HAVE_SDL2 #cmakedefine LMMS_HAVE_STK #cmakedefine LMMS_HAVE_VST #cmakedefine LMMS_HAVE_SF_COMPLEVEL @@ -45,6 +44,7 @@ #cmakedefine LMMS_HAVE_SEMAPHORE_H #cmakedefine LMMS_HAVE_SYS_TIME_H #cmakedefine LMMS_HAVE_SYS_TIMES_H +#cmakedefine LMMS_HAVE_SYS_PRCTL_H #cmakedefine LMMS_HAVE_SCHED_H #cmakedefine LMMS_HAVE_SYS_SOUNDCARD_H #cmakedefine LMMS_HAVE_SOUNDCARD_H diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index cdd360e70e1..be18fc9e4ca 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -220,7 +220,7 @@ InstrumentTrack::~InstrumentTrack() -void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames, NotePlayHandle* n ) +void InstrumentTrack::processAudioBuffer( SampleFrame* buf, const fpp_t frames, NotePlayHandle* n ) { // we must not play the sound if this InstrumentTrack is muted... if( isMuted() || ( Engine::getSong()->playMode() != Song::PlayMode::MidiClip && @@ -233,8 +233,7 @@ void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames, // We could do that in all other cases as well but the overhead for silence test is bigger than // what we potentially save. While playing a note, a NotePlayHandle-driven instrument will produce sound in // 99 of 100 cases so that test would be a waste of time. - if( m_instrument->flags().testFlag( Instrument::Flag::IsSingleStreamed ) && - MixHelpers::isSilent( buf, frames ) ) + if (m_instrument->isSingleStreamed() && MixHelpers::isSilent(buf, frames)) { // at least pass one silent buffer to allow if( m_silentBuffersProcessed ) @@ -263,7 +262,7 @@ void InstrumentTrack::processAudioBuffer( sampleFrame* buf, const fpp_t frames, // instruments using instrument-play-handles will call this method // without any knowledge about notes, so they pass NULL for n, which // is no problem for us since we just bypass the envelopes+LFOs - if( m_instrument->flags().testFlag( Instrument::Flag::IsSingleStreamed ) == false && n != nullptr ) + if (!m_instrument->isSingleStreamed() && n != nullptr) { const f_cnt_t offset = n->noteOffset(); m_soundShaping.processAudioBuffer( buf + offset, frames - offset, n ); @@ -343,7 +342,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const TimePos& tim NotePlayHandle* nph = NotePlayHandleManager::acquire( this, offset, - typeInfo::max() / 2, + std::numeric_limits::max() / 2, Note(TimePos(), Engine::getSong()->getPlayPos(Engine::getSong()->playMode()), event.key(), event.volume(midiPort()->baseVelocity())), nullptr, event.channel(), @@ -568,7 +567,7 @@ f_cnt_t InstrumentTrack::beatLen( NotePlayHandle * _n ) const -void InstrumentTrack::playNote( NotePlayHandle* n, sampleFrame* workingBuffer ) +void InstrumentTrack::playNote( NotePlayHandle* n, SampleFrame* workingBuffer ) { // Note: under certain circumstances the working buffer is a nullptr. // These cases are triggered in PlayHandle::doProcessing when the play method is called with a nullptr. @@ -772,17 +771,17 @@ bool InstrumentTrack::play( const TimePos & _start, const fpp_t _frames, } } - Note * cur_note; - while( nit != notes.end() && - ( cur_note = *nit )->pos() == cur_start ) + while (nit != notes.end() && (*nit)->pos() == cur_start) { + const auto currentNote = *nit; + // If the note is a Step Note, frames will be 0 so the NotePlayHandle // plays for the whole length of the sample - const auto note_frames = cur_note->type() == Note::Type::Step + const auto noteFrames = currentNote->type() == Note::Type::Step ? 0 - : cur_note->length().frames(frames_per_tick); + : currentNote->length().frames(frames_per_tick); - NotePlayHandle* notePlayHandle = NotePlayHandleManager::acquire( this, _offset, note_frames, *cur_note ); + NotePlayHandle* notePlayHandle = NotePlayHandleManager::acquire(this, _offset, noteFrames, *currentNote); notePlayHandle->setPatternTrack(pattern_track); // are we playing global song? if( _clip_num < 0 ) diff --git a/src/tracks/MidiClip.cpp b/src/tracks/MidiClip.cpp index dfee9a5e610..409fb60aefe 100644 --- a/src/tracks/MidiClip.cpp +++ b/src/tracks/MidiClip.cpp @@ -473,9 +473,9 @@ MidiClip * MidiClip::nextMidiClip() const MidiClip * MidiClip::adjacentMidiClipByOffset(int offset) const { auto& clips = m_instrumentTrack->getClips(); - int clipNum = m_instrumentTrack->getClipNum(this); - if (clipNum < 0 || clipNum > clips.size() - 1) { return nullptr; } - return dynamic_cast(clips[clipNum + offset]); + int clipNum = m_instrumentTrack->getClipNum(this) + offset; + if (clipNum < 0 || static_cast(clipNum) >= clips.size()) { return nullptr; } + return dynamic_cast(clips[clipNum]); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9a609922c2c..625601a3ebb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,18 +16,17 @@ foreach(LMMS_TEST_SRC IN LISTS LMMS_TESTS) # TODO CMake 3.20: Use cmake_path get_filename_component(LMMS_TEST_NAME ${LMMS_TEST_SRC} NAME_WE) - add_executable(${LMMS_TEST_NAME} $ ${LMMS_TEST_SRC}) + add_executable(${LMMS_TEST_NAME} ${LMMS_TEST_SRC}) add_test(NAME ${LMMS_TEST_NAME} COMMAND ${LMMS_TEST_NAME}) # TODO CMake 3.12: Propagate usage requirements by linking to lmmsobjs target_include_directories(${LMMS_TEST_NAME} PRIVATE $) + target_static_libraries("${LMMS_TEST_NAME}" PRIVATE lmmsobjs) target_link_libraries(${LMMS_TEST_NAME} PRIVATE - ${LMMS_REQUIRED_LIBS} ${QT_LIBRARIES} ${QT_QTTEST_LIBRARY} ) target_compile_features(${LMMS_TEST_NAME} PRIVATE cxx_std_17) - target_compile_definitions(${LMMS_TEST_NAME} PRIVATE $) endforeach() diff --git a/tests/scripted/check-strings b/tests/scripted/check-strings index fe15d908d1c..24212c33ce2 100755 --- a/tests/scripted/check-strings +++ b/tests/scripted/check-strings @@ -141,26 +141,6 @@ for cur_file in sorted(Path('.').glob('*/patches/*.patch')): error(cur_file.as_posix(), f'Source file does not exist: {mpath.as_posix()}') -caption('debian docs (only one string)') - -# Checks for caps.html. This gets relevant when #4027 will be merged -for line in Path('debian/lmms-common.docs').read_text(errors='replace').splitlines(): - line = line.rstrip() - if 'caps.html' in line and not Path(line).is_file(): - error('debian/lmms-common.docs', f'Path does not exist: {line}') - - -caption('debian/copyright') - -pat = re.compile(r'^Files:\s*(\S+).*$', re.MULTILINE) -ladspa_swh = re.compile(r'(plugins/LadspaEffect/swh/ladspa/[^/.]+)\.c') -for mpath in pat.findall(Path('debian/copyright').read_text(errors='replace')): - # in case of LADSPA SWH effects, check that the XML exists, not the C file - # (because the C files are not generated until a build is done) - if res2 := ladspa_swh.match(mpath): - mpath = res2.group(1) + '.xml' - if not any(Path('.').glob(mpath)): - error('debian/copyright', f'Glob/Path does not exist: {mpath}') # summary diff --git a/tests/scripted/verify b/tests/scripted/verify index 57c33dc8ee1..46d37e3ec2e 100755 --- a/tests/scripted/verify +++ b/tests/scripted/verify @@ -37,8 +37,6 @@ class ScriptTest(): set_git_config() subprocess.run(['git', 'submodule', 'add', '../../carla', 'plugins/CarlaBase/carla'], check=True) create_file('src/core/classes.cpp', 'namespace lmms {\nclass TestClass\n}') - create_file('debian/lmms-common.docs', '') - create_file('debian/copyright', '') create_file('data/locale/de.ts', '\n' ' \n' @@ -131,24 +129,6 @@ with tempfile.TemporaryDirectory() as tmpdir: test.expect('Error: data/themes/classic/style.css: Class does not exist in source code: NonExistentClass') test.expect('1 errors') - with ScriptTest(check_strings) as test: - create_file('debian/patches/clang.patch', '/plugins/non-existent-file') - test.run() - test.expect('Error: debian/patches/clang.patch: Source file does not exist: plugins/non-existent-file') - test.expect('1 errors') - - with ScriptTest(check_strings) as test: - create_file('debian/lmms-common.docs', '/plugins/caps.html') - test.run() - test.expect('Error: debian/lmms-common.docs: Path does not exist: /plugins/caps.html') - test.expect('1 errors') - - with ScriptTest(check_strings) as test: - create_file('debian/copyright', 'Files: NonExistent') - test.run() - test.expect('Error: debian/copyright: Glob/Path does not exist: NonExistent') - test.expect('1 errors') - with ScriptTest(check_namespace) as test: # minimal working example test.run(0) # exitcode 0 - no errors expected diff --git a/tests/src/core/ArrayVectorTest.cpp b/tests/src/core/ArrayVectorTest.cpp index 1eba265e9fe..40ce4d00163 100644 --- a/tests/src/core/ArrayVectorTest.cpp +++ b/tests/src/core/ArrayVectorTest.cpp @@ -226,9 +226,19 @@ private slots: { { // Self-assignment should not change the contents + //// Please note the following: + //// https://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2468 + //// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81159 auto v = ArrayVector{1, 2, 3}; const auto oldValue = v; +#if __GNUC__ >= 13 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wself-move" +#endif v = std::move(v); +#if __GNUC__ >= 13 +# pragma GCC diagnostic pop +#endif QCOMPARE(v, oldValue); } {