diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 999f3ce43fb..d38dceb1fa4 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -22,11 +22,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/labeler@main + # version between v4.0.3 and the one after which has not yet been tagged + # at the time of writing + - uses: actions/labeler@7012d51fe062f0b1e26e224a3c9fb52598ee3302 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" - - # This works around bug https://github.com/actions/labeler/issues/104. - # The workaround was proposed here: - # https://github.com/wesnoth/wesnoth/commit/958c82d0867568057caaf58356502ec8c87d8366 - sync-labels: "" + sync-labels: false diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 8751598d9cc..02487dd5196 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -72,6 +72,13 @@ jobs: name: ${{ env.UPLOAD_PATCH_FILE }} path: ${{ env.UPLOAD_PATCH_FILE }} + - name: "Upload pre-commit.log" + if: failure() && env.UPLOAD_PATCH_FILE == null + uses: actions/upload-artifact@v3.1.2 + with: + name: pre-commit.log + path: /github/home/.cache/pre-commit/pre-commit.log + # AppStream metadata has been generated/updated by a pre-commit hook - name: "Validate AppStream metadata" if: runner.os == 'Linux' diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2df5363e8e5..79734ad92d3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,7 +35,7 @@ repos: rev: v4.3.0 hooks: - id: fix-byte-order-marker - exclude: ^.*(\.cbproj|\.groupproj|\.props|\.sln|\.vcxproj|\.vcxproj.filters)$ + exclude: ^.*(\.cbproj|\.groupproj|\.props|\.sln|\.vcxproj|\.vcxproj.filters|UTF-8-BOM.txt)$ - id: check-case-conflict - id: check-json - id: check-merge-conflict @@ -43,6 +43,7 @@ repos: - id: check-yaml exclude: ^\.clang-format$ - id: end-of-file-fixer + exclude: ^.*UTF-8-BOM.txt$ - id: mixed-line-ending - id: trailing-whitespace exclude: \.(c|cc|cxx|cpp|frag|glsl|h|hpp|hxx|ih|ispc|ipp|java|js|m|mm|proto|vert)$ @@ -118,11 +119,10 @@ repos: hooks: - id: prettier types: [yaml] - # disabled until https://github.com/mixxxdj/mixxx/issues/11386 is fixed - #- repo: https://github.com/qarmin/qml_formatter.git - # rev: 0.2.0 - # hooks: - # - id: qml_formatter + - repo: https://github.com/qarmin/qml_formatter.git + rev: 0.2.0 + hooks: + - id: qml_formatter - repo: local hooks: - id: qsscheck diff --git a/.tx/config b/.tx/config index 553143ac744..4f74109f33c 100644 --- a/.tx/config +++ b/.tx/config @@ -1,9 +1,9 @@ [main] host = https://www.transifex.com -[mixxxdj.mixxx2-4] -file_filter = res/translations/mixxx_.ts -source_file = res/translations/mixxx.ts -source_lang = en +[o:mixxx-dj-software:p:mixxxdj:r:mixxx2-4] +file_filter = res/translations/mixxx_.ts +source_file = res/translations/mixxx.ts +source_lang = en +type = TS minimum_perc = 0 -type = QT diff --git a/CHANGELOG.md b/CHANGELOG.md index 638ce6e177a..2ea9281f69a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -845,6 +845,42 @@ [#8818](https://github.com/mixxxdj/mixxx/pull/8818) [#4907](https://github.com/mixxxdj/mixxx/pull/4907) +## [2.3.5](https://github.com/mixxxdj/mixxx/milestone/39) (unreleased) + +* Fix empty waveform overview after loading a track (Mixxx 2.3.4 regression) + Fixed by [#11333](https://github.com/mixxxdj/mixxx/pull/11333) + [#11359](https://github.com/mixxxdj/mixxx/pull/11359) + [#11344](https://github.com/mixxxdj/mixxx/issues/11344) +* Fullscreen: Fix a crash that occurs on Linux after enabling fullsceen and using menu + shortcuts e.g. Alt-F. + [#11328](https://github.com/mixxxdj/mixxx/pull/11328) + [#11320](https://github.com/mixxxdj/mixxx/issues/11320) +* Fullscreen: Rebuild & reconnect menu only on desktops with global menu + [#11350](https://github.com/mixxxdj/mixxx/pull/11350) +* macOS: Request Microphone and line-in access permission. + [#11367](https://github.com/mixxxdj/mixxx/pull/11367) + [#11365](https://github.com/mixxxdj/mixxx/issues/11365) +* JACK API: Allow to explicit select buffers of 2048 and 4096 frames/period. They are not + supported by the automatic buffer setting of the used PortAudio library. + [#11366](https://github.com/mixxxdj/mixxx/pull/11366) + [#11341](https://github.com/mixxxdj/mixxx/issues/11341) +* Pioneer DDJ-400: Make Beat FX section more intuitive + [#10912](https://github.com/mixxxdj/mixxx/pull/10912) +* Playlist export: Adopt new extension after changing the playlist type + [#11332](https://github.com/mixxxdj/mixxx/pull/11332) + [#11327](https://github.com/mixxxdj/mixxx/issues/11327) +* LateNight: brighter fx parameter buttons + [#11397](https://github.com/mixxxdj/mixxx/pull/11397) +* Fix drift in analyzis data after exporting metadata to MP3 files with ID3v1.1 tags + [#11168](https://github.com/mixxxdj/mixxx/pull/11168) + [#11159](https://github.com/mixxxdj/mixxx/issues/11159) +* Fix broadcasting using Opus encoding + [#11349](https://github.com/mixxxdj/mixxx/pull/11349) + [#10666](https://github.com/mixxxdj/mixxx/issues/10666) +* Tango: Remove VU peak indicators from stacked layout. This fixes a visual regression in Mixxx 2.3.4. + [#11430](https://github.com/mixxxdj/mixxx/pull/11430) + [#11362](https://github.com/mixxxdj/mixxx/issues/11362) + ## [2.3.4](https://launchpad.net/mixxx/+milestone/2.3.4) (2023-03-03) * Track Properties: Show 'date added' as local time [#4838](https://github.com/mixxxdj/mixxx/pull/4838) [#10776](https://github.com/mixxxdj/mixxx/issues/10776) @@ -889,7 +925,7 @@ * Allow search in external libraries [#11221](https://github.com/mixxxdj/mixxx/pull/11221) [#11216](https://github.com/mixxxdj/mixxx/issues/11216) * JACK buffer size fix [#11121](https://github.com/mixxxdj/mixxx/pull/11121) * Don't discard file tags with tuning information like "A#m +50" [#10992](https://github.com/mixxxdj/mixxx/pull/10992) -* Year search: Find also full date entries [#11251](https://github.com/mixxxdj/mixxx/pull/11251) [#11251](https://github.com/mixxxdj/mixxx/issues/11113) +* Year search: Find also full date entries [#11251](https://github.com/mixxxdj/mixxx/pull/11251) [#11113](https://github.com/mixxxdj/mixxx/issues/11113) * Fix visual alignment of beats and waveform in case of decoding issues [#11162](https://github.com/mixxxdj/mixxx/pull/11162) * Avoid "active key-value observers" messages during skin parsing on macOS [#11265](https://github.com/mixxxdj/mixxx/pull/11265) * Fullscreen on Linux: Fix issues caused by Ubuntu Unity workaround [#11295](https://github.com/mixxxdj/mixxx/pull/11295) [#11281](https://github.com/mixxxdj/mixxx/issues/11281) [#11294](https://github.com/mixxxdj/mixxx/issues/11294) diff --git a/CMakeLists.txt b/CMakeLists.txt index fbc74fca9d3..b8c8c70537c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -178,8 +178,13 @@ else() endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - # using regular Clang or AppleClang - set(LLVM_CLANG true) + if (CMAKE_CXX_SIMULATE_ID MATCHES "MSVC") + set(LLVM_CLANG false) + set(MSVC true) + else() + # using regular Clang or AppleClang + set(LLVM_CLANG true) + endif() else() set(LLVM_CLANG false) endif() @@ -194,6 +199,9 @@ set(CMAKE_CXX_STANDARD 20) if(MSVC) # Ensure MSVC populates __cplusplus correctly. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus") + # Remove unreferenced code and data + # Since c++11 they can safely be removed to speed up linking. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:inline") endif() # Speed up builds on HDDs and prevent wearing of SDDs @@ -297,6 +305,11 @@ if(MSVC) string(REPLACE "/RTC1" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") string(REPLACE "/RTC1" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") + # For some reasons cmake uses /Ob1 in RelWithDebInfo https://gitlab.kitware.com/cmake/cmake/-/issues/20812 + # /O2 is applied by CMake and this implies /Od2 + string(REPLACE "/Ob1" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + string(REPLACE "/Ob1" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") + # Reduce the size of the binary in RelWithDebInfo builds # Do not use /OPT:ICF because it has no effect. # https://github.com/mixxxdj/mixxx/pull/3660#pullrequestreview-600137258 @@ -363,8 +376,9 @@ if(MSVC) #Remove optimize flags set by cmake defaults string(REPLACE "/O2" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") string(REPLACE "/O2" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") - string(REPLACE "/Ob2" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") - string(REPLACE "/Ob2" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") + # For some reasons cmake uses /Ob1 in RelWithDebInfo https://gitlab.kitware.com/cmake/cmake/-/issues/20812 + string(REPLACE "/Ob1" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + string(REPLACE "/Ob1" "" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}") add_compile_options(/Od) # this implies /Ob0 add_compile_options(/RTC1) endif() @@ -757,7 +771,9 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL src/library/export/trackexportworker.cpp src/library/externaltrackcollection.cpp src/library/hiddentablemodel.cpp + src/library/itunes/itunesdao.cpp src/library/itunes/itunesfeature.cpp + src/library/itunes/itunesxmlimporter.cpp src/library/library_prefs.cpp src/library/library.cpp src/library/librarycontrol.cpp @@ -847,14 +863,12 @@ add_library(mixxx-lib STATIC EXCLUDE_FROM_ALL src/preferences/dialog/dlgprefbeatsdlg.ui src/preferences/dialog/dlgprefcolors.cpp src/preferences/dialog/dlgprefcolorsdlg.ui - src/preferences/dialog/dlgprefcrossfader.cpp - src/preferences/dialog/dlgprefcrossfaderdlg.ui + src/preferences/dialog/dlgprefmixer.cpp + src/preferences/dialog/dlgprefmixerdlg.ui src/preferences/dialog/dlgprefdeck.cpp src/preferences/dialog/dlgprefdeckdlg.ui src/preferences/dialog/dlgprefeffects.cpp src/preferences/dialog/dlgprefeffectsdlg.ui - src/preferences/dialog/dlgprefeq.cpp - src/preferences/dialog/dlgprefeqdlg.ui src/preferences/dialog/dlgpreferencepage.cpp src/preferences/dialog/dlgpreferences.cpp src/preferences/dialog/dlgpreferencesdlg.ui @@ -1166,6 +1180,23 @@ if(UNIX AND NOT APPLE) set(MIXXX_SETTINGS_PATH ".mixxx/") endif() +if(APPLE) + # Enable Automatic Reference Counting (ARC) when compiling Objective-C(++). + # This frees us from having to worry about memory management when interfacing + # with Apple frameworks (e.g. as in itunesmacosimporter.mm) since the compiler + # will automatically insert retain/release calls on Objective-C objects. + target_compile_options(mixxx-lib PUBLIC -fobjc-arc) + + option(MACOS_ITUNES_LIBRARY "Native macOS iTunes/Music.app library integration" ON) + if(MACOS_ITUNES_LIBRARY) + target_sources(mixxx-lib PRIVATE + src/library/itunes/itunesmacosimporter.mm + ) + target_link_libraries(mixxx-lib PRIVATE "-weak_framework iTunesLibrary") + target_compile_definitions(mixxx-lib PUBLIC __MACOS_ITUNES_LIBRARY__) + endif() +endif() + # QML Debugging if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_definitions(mixxx-lib PRIVATE QT_QML_DEBUG) @@ -1258,10 +1289,6 @@ target_compile_definitions(mixxx-lib PUBLIC $<$:MIXXX_BUILD_DEBUG> $<$:MIXXX_DEBUG_ASSERTIONS_ENABLED> $<$>:MIXXX_BUILD_RELEASE> - # Disable assert.h assertions in release mode. Some libraries use - # this as a signal for when to enable code that should be disabled - # in release mode. - $<$>:NDEBUG> ) # Mac-specific options @@ -1951,10 +1978,11 @@ endif() # Denon Engine Prime library export support (using libdjinterop) option(ENGINEPRIME "Support for library export to Denon Engine Prime" ON) if(ENGINEPRIME) - set(LIBDJINTEROP_VERSION 0.16.0) + set(LIBDJINTEROP_VERSION 0.16.1) # Look for an existing installation of libdjinterop and use that if available. # Otherwise, download and build from GitHub. - find_package(DjInterop ${LIBDJINTEROP_VERSION}) + # Note: Version 0.17.0 is not yet compatible + find_package(DjInterop ${LIBDJINTEROP_VERSION} EXACT) if(DjInterop_FOUND) # An existing installation of djinterop is available. message(STATUS "Using existing system installation of libdjinterop") @@ -1985,7 +2013,7 @@ if(ENGINEPRIME) # the configuration. ExternalProject_Add(libdjinterop URL "https://github.com/xsco/libdjinterop/archive/refs/tags/${LIBDJINTEROP_VERSION}.tar.gz" - URL_HASH SHA256=c998831fe4d3cc80c5c031491204244c9ed0c61575dd529260304b95d79db588 + URL_HASH SHA256=25461f5cc3ea80850d8400872f4fef08ad3730d9f2051719cccf2460f5ac15ad DOWNLOAD_DIR "${CMAKE_CURRENT_BINARY_DIR}/downloads" DOWNLOAD_NAME "libdjinterop-${LIBDJINTEROP_VERSION}.tar.gz" INSTALL_DIR ${DJINTEROP_INSTALL_DIR} @@ -2038,10 +2066,13 @@ add_library(fidlib STATIC EXCLUDE_FROM_ALL lib/fidlib/fidlib.c) if(MSVC) target_compile_definitions(fidlib PRIVATE T_MSVC) target_compile_definitions(fidlib PRIVATE _USE_MATH_DEFINES) + target_compile_options(fidlib PRIVATE /W3) elseif(MINGW) target_compile_definitions(fidlib PRIVATE T_MINGW) + target_compile_options(fidlib PRIVATE -fno-finite-math-only -Wall -Wextra -Wfloat-conversion -Werror=return-type) else() target_compile_definitions(fidlib PRIVATE T_LINUX) + target_compile_options(fidlib PRIVATE -fno-finite-math-only -Wall -Wextra -Wfloat-conversion -Werror=return-type) endif() target_include_directories(mixxx-lib SYSTEM PUBLIC lib/fidlib) target_link_libraries(mixxx-lib PRIVATE fidlib) @@ -2116,7 +2147,10 @@ target_link_libraries(mixxx-lib PRIVATE FLAC::FLAC) # inlining It is compiled without optimization and allows to use these function # from -ffast-math optimized objects. The MSVC option /fp:fast does not suffer this issue add_library(FpClassify STATIC EXCLUDE_FROM_ALL src/util/fpclassify.cpp) -if(GNU_GCC OR LLVM_CLANG) + +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_SIMULATE_ID MATCHES "MSVC") + target_compile_options(FpClassify PRIVATE /fp:precise) +elseif(GNU_GCC OR LLVM_CLANG) # The option `-ffp-contract=on` must precede `-fno-fast-math` # to silence a warning on Clang 14 target_compile_options(FpClassify PRIVATE -ffp-contract=on -fno-fast-math) @@ -3063,7 +3097,7 @@ if (NOT CPACK_DEBIAN_PACKAGE_RELEASE) set(CPACK_DEBIAN_PACKAGE_RELEASE 1) endif() -set(CPACK_DEBIAN_DISTRIBUTION_RELEASES jammy kinetic) +set(CPACK_DEBIAN_DISTRIBUTION_RELEASES jammy kinetic lunar) set(CPACK_DEBIAN_SOURCE_DIR ${CMAKE_SOURCE_DIR}) set(CPACK_DEBIAN_UPLOAD_PPA_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/packaging/CPackDebUploadPPA.cmake") set(CPACK_DEBIAN_INSTALL_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/packaging/CPackDebInstall.cmake") diff --git a/cmake/modules/BundleInstall.cmake.in b/cmake/modules/BundleInstall.cmake.in index c7a2c6a823d..6e3217255f7 100644 --- a/cmake/modules/BundleInstall.cmake.in +++ b/cmake/modules/BundleInstall.cmake.in @@ -9,7 +9,7 @@ include(BundleUtilities) #fixup_bundle tries to copy system libraries without this. Wtf? function(gp_resolved_file_type_override file type) - if(file MATCHES "^(/usr/lib)") + if(file MATCHES "^(/usr/lib)" OR file MATCHES "^(/Library/Frameworks)") set(type "system" PARENT_SCOPE) endif() endfunction() diff --git a/cmake/modules/FindDjInterop.cmake b/cmake/modules/FindDjInterop.cmake index dffb733d83a..c3c25ce76ea 100644 --- a/cmake/modules/FindDjInterop.cmake +++ b/cmake/modules/FindDjInterop.cmake @@ -61,12 +61,15 @@ find_library(DjInterop_LIBRARY ) mark_as_advanced(DjInterop_LIBRARY) +if(DEFINED PC_DjInterop_VERSION AND NOT PC_DjInterop_VERSION STREQUAL "") + set(DjInterop_VERSION "${PC_DjInterop_VERSION}") +endif() + include(FindPackageHandleStandardArgs) find_package_handle_standard_args( DjInterop - DEFAULT_MSG - DjInterop_LIBRARY - DjInterop_INCLUDE_DIR + REQUIRED_VARS DjInterop_LIBRARY DjInterop_INCLUDE_DIR + VERSION_VAR DjInterop_VERSION ) if(DjInterop_FOUND) diff --git a/lib/fidlib/fidlib.c b/lib/fidlib/fidlib.c index b6a451c144e..e93de8ca3d1 100644 --- a/lib/fidlib/fidlib.c +++ b/lib/fidlib/fidlib.c @@ -236,6 +236,10 @@ #endif #endif +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS // let me use standard functions +#endif + // // Includes // @@ -248,8 +252,6 @@ #include #include "fidlib.h" -extern FidFilter *mkfilter(char *, ...); - // // Target-specific fixes // @@ -279,6 +281,10 @@ extern FidFilter *mkfilter(char *, ...); #endif +#ifdef _MSC_VER +#define strdup _strdup // redirect to _strdup, strdup is deprecated with MSVC [warning C4996] +#endif + // // Support code // @@ -690,7 +696,7 @@ stack_filter(int order, int n_head, int n_val, ...) { order--; // Check length - len= ((char*)p)-((char*)q); + len= (int)(((char*)p)-((char*)q)); if (len != (int)FFCSIZE(n_head-1, n_val)) error("Internal error; bad call to stack_filter(); length mismatch (%d,%d)", len, FFCSIZE(n_head-1, n_val)); @@ -1559,7 +1565,7 @@ fid_design(const char *spec, double rate, double freq0, double freq1, int f_adj, // Generate a long description if required if (descp) { char *fmt= filter[sp.fi].txt; - int max= strlen(fmt) + 60 + sp.n_arg * 20; + int max= (int)strlen(fmt) + 60 + sp.n_arg * 20; char *desc= (char*)Alloc(max); char *p= desc; char ch; @@ -1783,26 +1789,27 @@ expand_spec(char *buf, char *bufend, char *str) { double fid_design_coef(double *coef, int n_coef, const char *spec, double rate, - double freq0, double freq1, int adj) { + double freq0, double freq1, int adj) { FidFilter *filt= fid_design(spec, rate, freq0, freq1, adj, 0); FidFilter *ff= filt; int a, len; int cnt= 0; double gain= 1.0; - double *iir, *fir, iir_adj; + double *iir, *fir; + double iir_adj= 1.0; static double const_one= 1; int n_iir, n_fir; int iir_cbm, fir_cbm; while (ff->typ) { if (ff->typ == 'F' && ff->len == 1) { - gain *= ff->val[0]; - ff= FFNEXT(ff); - continue; + gain *= ff->val[0]; + ff= FFNEXT(ff); + continue; } if (ff->typ != 'I' && ff->typ != 'F') - error("fid_design_coef can't handle FidFilter type: %c", ff->typ); + error("fid_design_coef can't handle FidFilter type: %c", ff->typ); // Initialise to safe defaults iir= fir= &const_one; @@ -1811,43 +1818,43 @@ fid_design_coef(double *coef, int n_coef, const char *spec, double rate, // See if we have an IIR filter if (ff->typ == 'I') { - iir= ff->val; - n_iir= ff->len; - iir_cbm= ff->cbm; - iir_adj= 1.0 / ff->val[0]; - ff= FFNEXT(ff); - gain *= iir_adj; + iir= ff->val; + n_iir= ff->len; + iir_cbm= ff->cbm; + iir_adj= 1.0 / ff->val[0]; + ff= FFNEXT(ff); + gain *= iir_adj; } // See if we have an FIR filter if (ff->typ == 'F') { - fir= ff->val; - n_fir= ff->len; - fir_cbm= ff->cbm; - ff= FFNEXT(ff); + fir= ff->val; + n_fir= ff->len; + fir_cbm= ff->cbm; + ff= FFNEXT(ff); } // Dump out all non-const coefficients in reverse order len= n_fir > n_iir ? n_fir : n_iir; for (a= len-1; a>=0; a--) { - // Output IIR if present and non-const - if (a < n_iir && a>0 && - !(iir_cbm & (1<<(a<15?a:15)))) { - if (cnt++ < n_coef) *coef++= iir_adj * iir[a]; - } - - // Output FIR if present and non-const - if (a < n_fir && - !(fir_cbm & (1<<(a<15?a:15)))) { - if (cnt++ < n_coef) *coef++= fir[a]; - } + // Output IIR if present and non-const + if (a < n_iir && a>0 && + !(iir_cbm & (1<<(a<15?a:15)))) { + if (cnt++ < n_coef) *coef++= iir_adj * iir[a]; + } + + // Output FIR if present and non-const + if (a < n_fir && + !(fir_cbm & (1<<(a<15?a:15)))) { + if (cnt++ < n_coef) *coef++= fir[a]; + } } } if (cnt != n_coef) error("fid_design_coef called with the wrong number of coefficients.\n" - " Given %d, expecting %d: (\"%s\",%g,%g,%g,%d)", - n_coef, cnt, spec, rate, freq0, freq1, adj); + " Given %d, expecting %d: (\"%s\",%g,%g,%g,%d)", + n_coef, cnt, spec, rate, freq0, freq1, adj); free(filt); return gain; @@ -2003,92 +2010,92 @@ parse_spec(Spec *sp) { if (!fmt) return strdupf("Spec-string \"%s\" matches no known format", sp->spec); while (*p && (ch= *fmt++)) { - if (ch != '#') { - if (ch == *p++) continue; - p= 0; break; - } - - if (isalpha(*p)) { p= 0; break; } - - // Handling a format character - switch (ch= *fmt++) { - default: - return strdupf("Internal error: Unknown format #%c in format: %s", - fmt[-1], filter[a].fmt); - case 'o': - case 'O': - sp->order= (int)strtol(p, &q, 10); - if (p == q) { - if (ch == 'O') goto bad; - sp->order= 1; - } - if (sp->order <= 0) - return strdupf("Bad order %d in spec-string \"%s\"", sp->order, sp->spec); - p= q; break; - case 'V': - sp->n_arg++; - *arg++= strtod(p, &q); - if (p == q) goto bad; - p= q; break; - case 'F': - sp->minlen= p-1-sp->spec; - sp->n_freq= 1; - sp->adj= (p[0] == '='); - if (sp->adj) p++; - sp->f0= strtod(p, &q); - sp->f1= 0; - if (p == q) goto bad; - p= q; break; - case 'R': - sp->minlen= p-1-sp->spec; - sp->n_freq= 2; - sp->adj= (p[0] == '='); - if (sp->adj) p++; - sp->f0= strtod(p, &q); - if (p == q) goto bad; - p= q; - if (*p++ != '-') goto bad; - sp->f1= strtod(p, &q); - if (p == q) goto bad; - if (sp->f0 > sp->f1) - return strdupf("Backwards frequency range in spec-string \"%s\"", sp->spec); - p= q; break; - } + if (ch != '#') { + if (ch == *p++) continue; + p= 0; break; + } + + if (isalpha(*p)) { p= 0; break; } + + // Handling a format character + switch (ch= *fmt++) { + default: + return strdupf("Internal error: Unknown format #%c in format: %s", + fmt[-1], filter[a].fmt); + case 'o': + case 'O': + sp->order= (int)strtol(p, &q, 10); + if (p == q) { + if (ch == 'O') goto bad; + sp->order= 1; + } + if (sp->order <= 0) + return strdupf("Bad order %d in spec-string \"%s\"", sp->order, sp->spec); + p= q; break; + case 'V': + sp->n_arg++; + *arg++= strtod(p, &q); + if (p == q) goto bad; + p= q; break; + case 'F': + sp->minlen= (int)(p-1-sp->spec); + sp->n_freq= 1; + sp->adj= (p[0] == '='); + if (sp->adj) p++; + sp->f0= strtod(p, &q); + sp->f1= 0; + if (p == q) goto bad; + p= q; break; + case 'R': + sp->minlen= (int)(p-1-sp->spec); + sp->n_freq= 2; + sp->adj= (p[0] == '='); + if (sp->adj) p++; + sp->f0= strtod(p, &q); + if (p == q) goto bad; + p= q; + if (*p++ != '-') goto bad; + sp->f1= strtod(p, &q); + if (p == q) goto bad; + if (sp->f0 > sp->f1) + return strdupf("Backwards frequency range in spec-string \"%s\"", sp->spec); + p= q; break; + } } if (p == 0) continue; if (fmt[0] == '/' && fmt[1] == '#' && fmt[2] == 'F') { - sp->minlen= p-sp->spec; - sp->n_freq= 1; - if (sp->in_f0 < 0.0) - return strdupf("Frequency omitted from filter-spec, and no default provided"); - sp->f0= sp->in_f0; - sp->f1= 0; - sp->adj= sp->in_adj; - fmt += 3; + sp->minlen= (int)(p-sp->spec); + sp->n_freq= 1; + if (sp->in_f0 < 0.0) + return strdupf("Frequency omitted from filter-spec, and no default provided"); + sp->f0= sp->in_f0; + sp->f1= 0; + sp->adj= sp->in_adj; + fmt += 3; } else if (fmt[0] == '/' && fmt[1] == '#' && fmt[2] == 'R') { - sp->minlen= p-sp->spec; - sp->n_freq= 2; - if (sp->in_f0 < 0.0 || sp->in_f1 < 0.0) - return strdupf("Frequency omitted from filter-spec, and no default provided"); - sp->f0= sp->in_f0; - sp->f1= sp->in_f1; - sp->adj= sp->in_adj; - fmt += 3; + sp->minlen= (int)(p-sp->spec); + sp->n_freq= 2; + if (sp->in_f0 < 0.0 || sp->in_f1 < 0.0) + return strdupf("Frequency omitted from filter-spec, and no default provided"); + sp->f0= sp->in_f0; + sp->f1= sp->in_f1; + sp->adj= sp->in_adj; + fmt += 3; } // Check for trailing unmatched format characters if (*fmt) { bad: - return strdupf("Bad match of spec-string \"%s\" to format \"%s\"", - sp->spec, filter[a].fmt); + return strdupf("Bad match of spec-string \"%s\" to format \"%s\"", + sp->spec, filter[a].fmt); } if (sp->n_arg > MAXARG) - return strdupf("Internal error -- maximum arguments exceeded"); + return strdupf("Internal error -- maximum arguments exceeded"); // Set the minlen to the whole string if unset - if (sp->minlen < 0) sp->minlen= p-sp->spec; + if (sp->minlen < 0) sp->minlen= (int)(p-sp->spec); // Save values, return sp->fi= a; @@ -2130,7 +2137,7 @@ fid_rewrite_spec(const char *spec, double freq0, double freq1, int adj, case 2: sprintf(buf, "/%s%.15g-%.15g", sp.adj ? "=" : "", sp.f0, sp.f1); break; default: buf[0]= 0; } - len= strlen(buf); + len= (int)strlen(buf); rv= (char*)Alloc(sp.minlen + len + 1); memcpy(rv, spec, sp.minlen); strcpy(rv+sp.minlen, buf); @@ -2171,16 +2178,16 @@ fid_cv_array(double *arr) { int n_val= 0; // Scan through for sizes - for (dp= arr; *dp; ) { + for (dp= arr; *dp != 0.0; ) { int len, typ; typ= (int)(*dp++); if (typ != 'F' && typ != 'I') - error("Bad type in array passed to fid_cv_array: %g", dp[-1]); + error("Bad type in array passed to fid_cv_array: %g", dp[-1]); len= (int)(*dp++); if (len < 1) - error("Bad length in array passed to fid_cv_array: %g", dp[-1]); + error("Bad length in array passed to fid_cv_array: %g", dp[-1]); n_head++; n_val += len; @@ -2190,7 +2197,7 @@ fid_cv_array(double *arr) { rv= ff= (FidFilter*)Alloc(FFCSIZE(n_head, n_val)); // Scan through to fill in FidFilter - for (dp= arr; *dp; ) { + for (dp= arr; *dp != 0.0; ) { int len, typ; typ= (int)(*dp++); len= (int)(*dp++); @@ -2228,8 +2235,8 @@ fid_cat(int freeme, ...) { va_start(ap, freeme); while ((ff0= va_arg(ap, FidFilter*))) { for (ff= ff0; ff->typ; ff= FFNEXT(ff)) - ; - len += ((char*)ff) - ((char*)ff0); + ; + len += (int)(((char*)ff) - ((char*)ff0)); } va_end(ap); @@ -2239,8 +2246,8 @@ fid_cat(int freeme, ...) { va_start(ap, freeme); while ((ff0= va_arg(ap, FidFilter*))) { for (ff= ff0; ff->typ; ff= FFNEXT(ff)) - ; - cnt= ((char*)ff) - ((char*)ff0); + ; + cnt= (int)(((char*)ff) - ((char*)ff0)); memcpy(dst, ff0, cnt); dst += cnt; if (freeme) free(ff0); @@ -2291,7 +2298,7 @@ grabWord(char **pp, char *buf, int buflen) { (*q != ',' && *q != ';' && *q != ')' && *q != ']' && *q != '}')) q++; } - len= q-p; + len= (int)(q-p); if (len >= buflen) return 0; memcpy(buf, p, len); @@ -2330,93 +2337,116 @@ fid_parse(double rate, char **pp, FidFilter **ffp) { double val; char dmy; -#define ERR(ptr, msg) { *pp= ptr; *ffp= 0; return msg; } -#define INCBUF { tmp= (char*)realloc(rv, (rvend-rv) * 2); if (!tmp) error("Out of memory"); \ - rvend= (rvend-rv) * 2 + tmp; rvp= (rvp-rv) + tmp; \ - curr= (FidFilter*)(((char*)curr) - rv + tmp); rv= tmp; } +#define ERR(ptr, msg) { \ + *pp= ptr; \ + *ffp= 0; \ + return msg; \ + } +#define INCBUF { \ + size_t new_size= (rvend - rv) * 2; \ + size_t rvp_offset= rvp - rv; \ + size_t curr_offset= (char*)curr - rv; \ + tmp= (char*)realloc(rv, new_size); \ + if (!tmp) { \ + error("Out of memory"); \ + } \ + rvend= tmp + new_size; \ + rvp= tmp + rvp_offset; \ + curr= (FidFilter*)(tmp + curr_offset); \ + rv= tmp; \ + } while (1) { rew= p; if (!grabWord(&p, buf, sizeof(buf))) { - if (*p) ERR(p, strdupf("Filter element unexpectedly long -- syntax error?")); - buf[0]= 0; + if (*p) ERR(p, strdupf("Filter element unexpectedly long -- syntax error?")); + buf[0]= 0; } - if (!buf[0] || !buf[1]) switch (buf[0]) { - default: - break; - case 0: - case ',': - case ';': - case ')': - case ']': - case '}': - // End of filter, return it - tmp= (char*)realloc(rv, (rvp-rv) + xtra); - if (!tmp) error("Out of memory"); - curr= (FidFilter*)((rvp-rv) + tmp); - curr->typ= 0; curr->cbm= 0; curr->len= 0; - *pp= buf[0] ? (p-1) : p; - *ffp= (FidFilter*)tmp; - return 0; - case '/': - if (typ > 0) ERR(rew, strdupf("Filter syntax error; unexpected '/'")); - typ= 'I'; - continue; - case 'x': - if (typ > 0) ERR(rew, strdupf("Filter syntax error; unexpected 'x'")); - typ= 'F'; - continue; + if (!buf[0] || !buf[1]) { + switch (buf[0]) { + default: + break; + case 0: + case ',': + case ';': + case ')': + case ']': + case '}': { + // End of filter, return it + size_t rvp_offset= rvp - rv; + size_t new_size= rvp_offset + xtra; + tmp= (char*)realloc(rv, new_size); + if (!tmp) { + error("Out of memory"); + } + curr= (FidFilter*)(tmp + rvp_offset); + curr->typ= 0; + curr->cbm= 0; + curr->len= 0; + *pp= buf[0] ? (p-1) : p; + *ffp= (FidFilter*)tmp; + return 0; + } + case '/': + if (typ > 0) ERR(rew, strdupf("Filter syntax error; unexpected '/'")); + typ= 'I'; + continue; + case 'x': + if (typ > 0) ERR(rew, strdupf("Filter syntax error; unexpected 'x'")); + typ= 'F'; + continue; + } } - if (typ < 0) typ= 'F'; // Assume 'x' if missing + if (typ < 0) typ= 'F'; // Assume 'x' if missing if (!typ) ERR(p, strdupf("Expecting a 'x' or '/' before this")); if (1 != sscanf(buf, "%lf %c", &val, &dmy)) { - // Must be a predefined filter - FidFilter *ff; - FidFilter *ff1; - Spec sp; - double f0, f1; - char *err; - int len; - - if (typ != 'F') ERR(rew, strdupf("Predefined filters cannot be used with '/'")); - - // Parse the filter-spec - memset(&sp, 0, sizeof(sp)); - sp.spec= buf; - sp.in_f0= sp.in_f1= -1; - if ((err= parse_spec(&sp))) ERR(rew, err); - f0= sp.f0; - f1= sp.f1; - - // Adjust frequencies to range 0-0.5, and check them - f0 /= rate; - if (f0 > 0.5) ERR(rew, strdupf("Frequency of %gHz out of range with " - "sampling rate of %gHz", f0*rate, rate)); - f1 /= rate; - if (f1 > 0.5) ERR(rew, strdupf("Frequency of %gHz out of range with " - "sampling rate of %gHz", f1*rate, rate)); - - // Okay we now have a successful spec-match to filter[sp.fi], and sp.n_arg - // args are now in sp.argarr[] - - // Generate the filter - if (!sp.adj) - ff= filter[sp.fi].rout(rate, f0, f1, sp.order, sp.n_arg, sp.argarr); - else if (strstr(filter[sp.fi].fmt, "#R")) - ff= auto_adjust_dual(&sp, rate, f0, f1); - else - ff= auto_adjust_single(&sp, rate, f0); - - // Append it to our FidFilter to return - for (ff1= ff; ff1->typ; ff1= FFNEXT(ff1)) ; - len= ((char*)ff1-(char*)ff); - while (rvp + len + xtra >= rvend) INCBUF; - memcpy(rvp, ff, len); rvp += len; - free(ff); - typ= 0; - continue; + // Must be a predefined filter + FidFilter *ff; + FidFilter *ff1; + Spec sp; + double f0, f1; + char *err; + int len; + + if (typ != 'F') ERR(rew, strdupf("Predefined filters cannot be used with '/'")); + + // Parse the filter-spec + memset(&sp, 0, sizeof(sp)); + sp.spec= buf; + sp.in_f0= sp.in_f1= -1; + if ((err= parse_spec(&sp))) ERR(rew, err); + f0= sp.f0; + f1= sp.f1; + + // Adjust frequencies to range 0-0.5, and check them + f0 /= rate; + if (f0 > 0.5) ERR(rew, strdupf("Frequency of %gHz out of range with " + "sampling rate of %gHz", f0*rate, rate)); + f1 /= rate; + if (f1 > 0.5) ERR(rew, strdupf("Frequency of %gHz out of range with " + "sampling rate of %gHz", f1*rate, rate)); + + // Okay we now have a successful spec-match to filter[sp.fi], and sp.n_arg + // args are now in sp.argarr[] + + // Generate the filter + if (!sp.adj) + ff= filter[sp.fi].rout(rate, f0, f1, sp.order, sp.n_arg, sp.argarr); + else if (strstr(filter[sp.fi].fmt, "#R")) + ff= auto_adjust_dual(&sp, rate, f0, f1); + else + ff= auto_adjust_single(&sp, rate, f0); + + // Append it to our FidFilter to return + for (ff1= ff; ff1->typ; ff1= FFNEXT(ff1)) ; + len= (int)((char*)ff1-(char*)ff); + while (rvp + len + xtra >= rvend) INCBUF; + memcpy(rvp, ff, len); rvp += len; + free(ff); + typ= 0; + continue; } // Must be a list of coefficients @@ -2431,19 +2461,19 @@ fid_parse(double rate, char **pp, FidFilter **ffp) { // See how many more coefficients we can pick up while (1) { - rew= p; - if (!grabWord(&p, buf, sizeof(buf))) { - if (*p) ERR(p, strdupf("Filter element unexpectedly long -- syntax error?")); - buf[0]= 0; - } - if (1 != sscanf(buf, "%lf %c", &val, &dmy)) { - p= rew; - break; - } - while (rvp + sizeof(double) >= rvend) INCBUF; - curr->len++; - *(double*)rvp= val; - rvp += sizeof(double); + rew= p; + if (!grabWord(&p, buf, sizeof(buf))) { + if (*p) ERR(p, strdupf("Filter element unexpectedly long -- syntax error?")); + buf[0]= 0; + } + if (1 != sscanf(buf, "%lf %c", &val, &dmy)) { + p= rew; + break; + } + while (rvp + sizeof(double) >= rvend) INCBUF; + curr->len++; + *(double*)rvp= val; + rvp += sizeof(double); } typ= 0; continue; diff --git a/lib/fidlib/fidrf_cmdlist.h b/lib/fidlib/fidrf_cmdlist.h index d0721d3c9f1..4f7605624d7 100644 --- a/lib/fidlib/fidrf_cmdlist.h +++ b/lib/fidlib/fidrf_cmdlist.h @@ -239,107 +239,107 @@ fid_run_new(FidFilter *filt, double (**funcpp)(void *,double)) { int n_iir = 0; int cnt; double *iir, *fir; - double adj; + double adj = 1.0; if (filt->typ == 'F' && filt->len == 1) { - gain *= filt->val[0]; - filt= FFNEXT(filt); - continue; + gain *= filt->val[0]; + filt= FFNEXT(filt); + continue; } if (filt->typ == 'F') { - iir= 0; - fir= filt->val; n_fir= filt->len; - filt= FFNEXT(filt); + iir= 0; + fir= filt->val; n_fir= filt->len; + filt= FFNEXT(filt); } else if (filt->typ == 'I') { - iir= filt->val; n_iir= filt->len; - fir= 0; - filt= FFNEXT(filt); - while (filt->typ == 'F' && filt->len == 1) { - gain *= filt->val[0]; - filt= FFNEXT(filt); - } - if (filt->typ == 'F') { - fir= filt->val; n_fir= filt->len; - filt= FFNEXT(filt); - } + iir= filt->val; n_iir= filt->len; + fir= 0; + filt= FFNEXT(filt); + while (filt->typ == 'F' && filt->len == 1) { + gain *= filt->val[0]; + filt= FFNEXT(filt); + } + if (filt->typ == 'F') { + fir= filt->val; n_fir= filt->len; + filt= FFNEXT(filt); + } } else - error("Internal error: fid_run_new can only handle IIR + FIR types"); + error("Internal error: fid_run_new can only handle IIR + FIR types"); // Okay, we now have an IIR/FIR pair to process, possibly with // n_iir or n_fir == 0 if one half is missing cnt= n_iir > n_fir ? n_iir : n_fir; buf_size += cnt-1; if (n_iir) { - adj= 1.0 / iir[0]; - gain *= adj; + adj= 1.0 / iir[0]; + gain *= adj; } if (n_fir == 3 && n_iir == 3) { - if (prev == 18) { cp[-1]= prev= 21; *cp++= 2; } - else if (prev == 21) { cp[-1]++; } - else *cp++= prev= 18; - *dp++= iir[2]*adj; *dp++= fir[2]; - *dp++= iir[1]*adj; *dp++= fir[1]; - *dp++= fir[0]; + if (prev == 18) { cp[-1]= prev= 21; *cp++= 2; } + else if (prev == 21) { cp[-1]++; } + else *cp++= prev= 18; + *dp++= iir[2]*adj; *dp++= fir[2]; + *dp++= iir[1]*adj; *dp++= fir[1]; + *dp++= fir[0]; } else if (n_fir == 3 && n_iir == 0) { - if (prev == 17) { cp[-1]= prev= 20; *cp++= 2; } - else if (prev == 20) { cp[-1]++; } - else *cp++= prev= 17; - *dp++= fir[2]; - *dp++= fir[1]; - *dp++= fir[0]; + if (prev == 17) { cp[-1]= prev= 20; *cp++= 2; } + else if (prev == 20) { cp[-1]++; } + else *cp++= prev= 17; + *dp++= fir[2]; + *dp++= fir[1]; + *dp++= fir[0]; } else if (n_fir == 0 && n_iir == 3) { - if (prev == 16) { cp[-1]= prev= 19; *cp++= 2; } - else if (prev == 19) { cp[-1]++; } - else *cp++= prev= 16; - *dp++= iir[2]*adj; - *dp++= iir[1]*adj; + if (prev == 16) { cp[-1]= prev= 19; *cp++= 2; } + else if (prev == 19) { cp[-1]++; } + else *cp++= prev= 16; + *dp++= iir[2]*adj; + *dp++= iir[1]*adj; } else { - prev= 0; // Just cancel 'prev' as we only use it for 16-18,19-21 - if (cnt > n_fir) { - a= 0; - while (cnt > n_fir && cnt > 2) { - *dp++= iir[--cnt] * adj; a++; - } - while (a >= 4) { - int nn= a/4; if (nn > 255) nn= 255; - *cp++= 4; *cp++= nn; a -= nn*4; - } - if (a) *cp++= a; - } - if (cnt > n_iir) { - a= 0; - while (cnt > n_iir && cnt > 2) { - *dp++= fir[--cnt]; a++; - } - while (a >= 4) { - int nn= a/4; if (nn > 255) nn= 255; - *cp++= 8; *cp++= nn; a -= nn*4; - } - if (a) *cp++= 4+a; - } - a= 0; - while (cnt > 2) { - cnt--; a++; - *dp++= iir[cnt]*adj; *dp++= fir[cnt]; - } - while (a >= 4) { - int nn= a/4; if (nn > 255) nn= 255; - *cp++= 12; *cp++= nn; a -= nn*4; - } - if (a) *cp++= 8+a; - - if (!n_fir) { - *cp++= 13; - *dp++= iir[1]; - } else if (!n_iir) { - *cp++= 14; - *dp++= fir[1]; - *dp++= fir[0]; - } else { - *cp++= 15; - *dp++= iir[1]; - *dp++= fir[1]; - *dp++= fir[0]; - } + prev= 0; // Just cancel 'prev' as we only use it for 16-18,19-21 + if (cnt > n_fir) { + a= 0; + while (cnt > n_fir && cnt > 2) { + *dp++= iir[--cnt] * adj; a++; + } + while (a >= 4) { + int nn= a/4; if (nn > 255) nn= 255; + *cp++= 4; *cp++= nn; a -= nn*4; + } + if (a) *cp++= a; + } + if (cnt > n_iir) { + a= 0; + while (cnt > n_iir && cnt > 2) { + *dp++= fir[--cnt]; a++; + } + while (a >= 4) { + int nn= a/4; if (nn > 255) nn= 255; + *cp++= 8; *cp++= nn; a -= nn*4; + } + if (a) *cp++= 4+a; + } + a= 0; + while (cnt > 2) { + cnt--; a++; + *dp++= iir[cnt]*adj; *dp++= fir[cnt]; + } + while (a >= 4) { + int nn= a/4; if (nn > 255) nn= 255; + *cp++= 12; *cp++= nn; a -= nn*4; + } + if (a) *cp++= 8+a; + + if (!n_fir) { + *cp++= 13; + *dp++= iir[1]; + } else if (!n_iir) { + *cp++= 14; + *dp++= fir[1]; + *dp++= fir[0]; + } else { + *cp++= 15; + *dp++= iir[1]; + *dp++= fir[1]; + *dp++= fir[0]; + } } } @@ -350,16 +350,16 @@ fid_run_new(FidFilter *filt, double (**funcpp)(void *,double)) { *cp++= 0; // Sanity checks - coef_cnt= dp-coef_tmp; - cmd_cnt= cp-cmd_tmp; + coef_cnt= (int)(dp-coef_tmp); + cmd_cnt= (int)(cp-cmd_tmp); if (coef_cnt > coef_max || cmd_cnt > cmd_max) error("fid_run_new internal error; arrays exceeded"); // Allocate the final Run structure to return rr= (Run*)Alloc(sizeof(Run) + - coef_cnt*sizeof(double) + - cmd_cnt*sizeof(char)); + coef_cnt*sizeof(double) + + cmd_cnt*sizeof(char)); rr->magic= 0x64966325; rr->buf_size= buf_size; rr->coef= (double*)(rr+1); diff --git a/res/controllers/Hercules P32 DJ.midi.xml b/res/controllers/Hercules P32 DJ.midi.xml index 0f528f3fe57..81d4debd550 100644 --- a/res/controllers/Hercules P32 DJ.midi.xml +++ b/res/controllers/Hercules P32 DJ.midi.xml @@ -512,7 +512,7 @@ [Channel1] - P32.leftDeck.effectUnit.dryWetKnob.input + P32.leftDeck.dryWetKnobOrPregain.input 0xB1 0x09 @@ -521,7 +521,7 @@ [Channel1] - P32.leftDeck.effectUnit.dryWetKnob.input + P32.leftDeck.dryWetKnobOrPregain.input 0xB4 0x09 @@ -1548,7 +1548,7 @@ [Channel1] - P32.rightDeck.effectUnit.dryWetKnob.input + P32.rightDeck.dryWetKnobOrPregain.input 0xB2 0x09 @@ -1557,7 +1557,7 @@ [Channel1] - P32.rightDeck.effectUnit.dryWetKnob.input + P32.rightDeck.dryWetKnobOrPregain.input 0xB5 0x09 diff --git a/res/controllers/Hercules-P32-scripts.js b/res/controllers/Hercules-P32-scripts.js index fbeb36f5210..203dd920e60 100644 --- a/res/controllers/Hercules-P32-scripts.js +++ b/res/controllers/Hercules-P32-scripts.js @@ -16,6 +16,8 @@ var loopEnabledDot = false; var samplerCrossfaderAssign = true; // Toggle effect units between 1 & 3 on left and 2 & 4 on right when toggling decks var toggleEffectUnitsWithDecks = false; +// Set the dry/wet knob as a pregain +var dryWetKnobAsPregain = false; /** * Hercules P32 DJ controller script for Mixxx 2.1 @@ -378,7 +380,16 @@ P32.Deck = function(deckNumbers, channel) { this.effectUnit.knobs[1].midi = [0xB0 + channel, 0x06]; this.effectUnit.knobs[2].midi = [0xB0 + channel, 0x07]; this.effectUnit.knobs[3].midi = [0xB0 + channel, 0x08]; - this.effectUnit.dryWetKnob.midi = [0xB0 + channel, 0x09]; + if (dryWetKnobAsPregain) { + this.dryWetKnobOrPregain = new components.Pot({ + midi: [0xB0 + channel, 0x09], + group: "[Channel" + channel + "]", + inKey: "pregain", + }); + } else { + this.effectUnit.dryWetKnob.midi = [0xB0 + channel, 0x09]; + this.dryWetKnobOrPregain = this.effectUnit.dryWetKnob; + } this.effectUnit.enableButtons[1].midi = [0x90 + channel, 0x03]; this.effectUnit.enableButtons[2].midi = [0x90 + channel, 0x04]; this.effectUnit.enableButtons[3].midi = [0x90 + channel, 0x05]; diff --git a/res/controllers/Pioneer-DDJ-400-script.js b/res/controllers/Pioneer-DDJ-400-script.js index 666ff7abaf2..c23bf12fab6 100644 --- a/res/controllers/Pioneer-DDJ-400-script.js +++ b/res/controllers/Pioneer-DDJ-400-script.js @@ -1,7 +1,7 @@ // Pioneer-DDJ-400-script.js // **************************************************************************** // * Mixxx mapping script file for the Pioneer DDJ-400. -// * Authors: Warker, nschloe, dj3730, jusko +// * Authors: Warker, nschloe, dj3730, jusko, tiesjan // * Reviewers: Be-ing, Holzhaus // * Manual: https://manual.mixxx.org/2.3/en/hardware/controllers/pioneer_ddj_400.html // **************************************************************************** @@ -19,14 +19,14 @@ // // Custom (Mixxx specific mappings): // * BeatFX: Assigned Effect Unit 1 -// < LEFT focus EFFECT1 -// > RIGHT focus EFFECT2 -// v FX_SELECT focus EFFECT3. +// < LEFT toggles focus between Effects 1, 2 and 3 leftward +// > RIGHT toggles focus between Effects 1, 2 and 3 rightward +// v DOWN loads next effect entry for focused Effect +// SHIFT + v UP loads previous effect entry for focused Effect +// LEVEL/DEPTH controls the Mix knob of the Effect Unit +// SHIFT + LEVEL/DEPTH controls the Meta knob of the focused Effect // ON/OFF toggles focused effect slot // SHIFT + ON/OFF disables all three effect slots. -// SHIFT + < loads previous effect -// SHIFT + > loads next effect -// // * 32 beat jump forward & back (Shift + CUE/LOOP CALL arrows) // * Toggle quantize (Shift + channel cue) // @@ -34,7 +34,6 @@ // * Loop Section: // * -4BEAT auto loop (hacky---prefer a clean way to set a 4 beat loop // from a previous position on long press) -// // * CUE/LOOP CALL - memory & delete (complex and not useful. Hot cues are sufficient) // // * Secondary pad modes (trial attempts complex and too experimental) @@ -173,7 +172,7 @@ PioneerDDJ400.init = function() { engine.softTakeover("[EffectRack1_EffectUnit1_Effect3]", "meta", true); engine.softTakeover("[EffectRack1_EffectUnit1]", "mix", true); - for (var i = 1; i <= 16; ++i) { + for (let i = 1; i <= 16; ++i) { engine.makeConnection("[Sampler" + i + "]", "play", PioneerDDJ400.samplerPlayOutputCallbackFunction); } @@ -190,7 +189,7 @@ PioneerDDJ400.init = function() { engine.makeConnection("[Channel1]", "loop_enabled", PioneerDDJ400.loopToggle); engine.makeConnection("[Channel2]", "loop_enabled", PioneerDDJ400.loopToggle); - for (i = 1; i <= 3; i++) { + for (let i = 1; i <= 3; i++) { engine.makeConnection("[EffectRack1_EffectUnit1_Effect" + i +"]", "enabled", PioneerDDJ400.toggleFxLight); } engine.makeConnection("[EffectRack1_EffectUnit1]", "focused_effect", PioneerDDJ400.toggleFxLight); @@ -204,7 +203,7 @@ PioneerDDJ400.init = function() { // PioneerDDJ400.vuMeterUpdate = function(value, group) { - var newVal = value * 150; + const newVal = value * 150; switch (group) { case "[Channel1]": @@ -222,14 +221,14 @@ PioneerDDJ400.vuMeterUpdate = function(value, group) { // PioneerDDJ400.toggleFxLight = function(_value, _group, _control) { - var enabled = engine.getValue(PioneerDDJ400.focusedFxGroup(), "enabled"); + const enabled = engine.getValue(PioneerDDJ400.focusedFxGroup(), "enabled"); PioneerDDJ400.toggleLight(PioneerDDJ400.lights.beatFx, enabled); PioneerDDJ400.toggleLight(PioneerDDJ400.lights.shiftBeatFx, enabled); }; PioneerDDJ400.focusedFxGroup = function() { - var focusedFx = engine.getValue("[EffectRack1_EffectUnit1]", "focused_effect"); + const focusedFx = engine.getValue("[EffectRack1_EffectUnit1]", "focused_effect"); return "[EffectRack1_EffectUnit1_Effect" + focusedFx + "]"; }; @@ -243,36 +242,50 @@ PioneerDDJ400.beatFxLevelDepthRotate = function(_channel, _control, value) { } }; -PioneerDDJ400.beatFxSelectPreviousEffect = function(_channel, _control, value) { - engine.setValue(PioneerDDJ400.focusedFxGroup(), "prev_effect", value); -}; +PioneerDDJ400.changeFocusedEffectBy = function(numberOfSteps) { + let focusedEffect = engine.getValue("[EffectRack1_EffectUnit1]", "focused_effect"); -PioneerDDJ400.beatFxSelectNextEffect = function(_channel, _control, value) { - engine.setValue(PioneerDDJ400.focusedFxGroup(), "next_effect", value); + // Convert to zero-based index + focusedEffect -= 1; + + // Standard Euclidean modulo by use of two plain modulos + const numberOfEffectsPerEffectUnit = 3; + focusedEffect = (((focusedEffect + numberOfSteps) % numberOfEffectsPerEffectUnit) + numberOfEffectsPerEffectUnit) % numberOfEffectsPerEffectUnit; + + // Convert back to one-based index + focusedEffect += 1; + + engine.setValue("[EffectRack1_EffectUnit1]", "focused_effect", focusedEffect); }; PioneerDDJ400.beatFxLeftPressed = function(_channel, _control, value) { if (value === 0) { return; } - engine.setValue("[EffectRack1_EffectUnit1]", "focused_effect", 1); + PioneerDDJ400.changeFocusedEffectBy(-1); }; PioneerDDJ400.beatFxRightPressed = function(_channel, _control, value) { if (value === 0) { return; } - engine.setValue("[EffectRack1_EffectUnit1]", "focused_effect", 2); + PioneerDDJ400.changeFocusedEffectBy(1); }; PioneerDDJ400.beatFxSelectPressed = function(_channel, _control, value) { if (value === 0) { return; } - engine.setValue("[EffectRack1_EffectUnit1]", "focused_effect", 3); + engine.setValue(PioneerDDJ400.focusedFxGroup(), "next_effect", value); +}; + +PioneerDDJ400.beatFxSelectShiftPressed = function(_channel, _control, value) { + if (value === 0) { return; } + + engine.setValue(PioneerDDJ400.focusedFxGroup(), "prev_effect", value); }; PioneerDDJ400.beatFxOnOffPressed = function(_channel, _control, value) { if (value === 0) { return; } - var toggleEnabled = !engine.getValue(PioneerDDJ400.focusedFxGroup(), "enabled"); + const toggleEnabled = !engine.getValue(PioneerDDJ400.focusedFxGroup(), "enabled"); engine.setValue(PioneerDDJ400.focusedFxGroup(), "enabled", toggleEnabled); }; @@ -282,7 +295,7 @@ PioneerDDJ400.beatFxOnOffShiftPressed = function(_channel, _control, value) { engine.setParameter("[EffectRack1_EffectUnit1]", "mix", 0); engine.softTakeoverIgnoreNextValue("[EffectRack1_EffectUnit1]", "mix"); - for (var i = 1; i <= 3; i++) { + for (let i = 1; i <= 3; i++) { engine.setValue("[EffectRack1_EffectUnit1_Effect" + i + "]", "enabled", 0); } PioneerDDJ400.toggleLight(PioneerDDJ400.lights.beatFx, false); @@ -292,7 +305,7 @@ PioneerDDJ400.beatFxOnOffShiftPressed = function(_channel, _control, value) { PioneerDDJ400.beatFxChannel = function(_channel, control, value, _status, group) { if (value === 0x00) { return; } - var enableChannel1 = control === 0x10 ? 1 : 0, + const enableChannel1 = control === 0x10 ? 1 : 0, enableChannel2 = control === 0x11 ? 1 : 0, enableMaster = control === 0x14 ? 1 : 0; @@ -335,7 +348,7 @@ PioneerDDJ400.setLoopButtonLights = function(status, value) { }; PioneerDDJ400.startLoopLightsBlink = function(channel, control, status, group) { - var blink = 0x7F; + let blink = 0x7F; PioneerDDJ400.stopLoopLightsBlink(group, control, status); @@ -374,7 +387,7 @@ PioneerDDJ400.stopLoopLightsBlink = function(group, control, status) { }; PioneerDDJ400.loopToggle = function(value, group, control) { - var status = group === "[Channel1]" ? 0x90 : 0x91, + const status = group === "[Channel1]" ? 0x90 : 0x91, channel = group === "[Channel1]" ? 0 : 1; PioneerDDJ400.setReloopLight(status, value ? 0x7F : 0x00); @@ -426,12 +439,12 @@ PioneerDDJ400.syncLongPressed = function(channel, control, value, status, group) }; PioneerDDJ400.cycleTempoRange = function(_channel, _control, value, _status, group) { - if (value === 0) return; // ignore release + if (value === 0) { return; } // ignore release - var currRange = engine.getValue(group, "rateRange"); - var idx = 0; + const currRange = engine.getValue(group, "rateRange"); + let idx = 0; - for (var i = 0; i < this.tempoRanges.length; i++) { + for (let i = 0; i < this.tempoRanges.length; i++) { if (currRange === this.tempoRanges[i]) { idx = (i + 1) % this.tempoRanges.length; break; @@ -445,12 +458,12 @@ PioneerDDJ400.cycleTempoRange = function(_channel, _control, value, _status, gro // PioneerDDJ400.jogTurn = function(channel, _control, value, _status, group) { - var deckNum = channel + 1; + const deckNum = channel + 1; // wheel center at 64; <64 rew >64 fwd - var newVal = value - 64; + let newVal = value - 64; // loop_in / out adjust - var loopEnabled = engine.getValue(group, "loop_enabled"); + const loopEnabled = engine.getValue(group, "loop_enabled"); if (loopEnabled > 0) { if (PioneerDDJ400.loopAdjustIn[channel]) { newVal = newVal * PioneerDDJ400.loopAdjustMultiply + engine.getValue(group, "loop_start_position"); @@ -473,12 +486,12 @@ PioneerDDJ400.jogTurn = function(channel, _control, value, _status, group) { PioneerDDJ400.jogSearch = function(_channel, _control, value, _status, group) { - var newVal = (value - 64) * PioneerDDJ400.fastSeekScale; + const newVal = (value - 64) * PioneerDDJ400.fastSeekScale; engine.setValue(group, "jog", newVal); }; PioneerDDJ400.jogTouch = function(channel, _control, value) { - var deckNum = channel + 1; + const deckNum = channel + 1; // skip while adjusting the loop points if (PioneerDDJ400.loopAdjustIn[channel] || PioneerDDJ400.loopAdjustOut[channel]) { @@ -514,7 +527,7 @@ PioneerDDJ400.tempoSliderMSB = function(channel, control, value, status, group) }; PioneerDDJ400.tempoSliderLSB = function(channel, control, value, status, group) { - var fullValue = (PioneerDDJ400.highResMSB[group].tempoSlider << 7) + value; + const fullValue = (PioneerDDJ400.highResMSB[group].tempoSlider << 7) + value; engine.setValue( group, @@ -565,7 +578,7 @@ PioneerDDJ400.decreaseBeatjumpSizes = function(_channel, control, value, _status PioneerDDJ400.samplerPlayOutputCallbackFunction = function(value, group, _control) { if (value === 1) { - var curPad = group.match(script.samplerRegEx)[1]; + const curPad = group.match(script.samplerRegEx)[1]; PioneerDDJ400.startSamplerBlink( 0x97 + (curPad > 8 ? 2 : 0), 0x30 + ((curPad > 8 ? curPad - 8 : curPad) - 1), @@ -590,7 +603,7 @@ PioneerDDJ400.samplerPadShiftPressed = function(_channel, _control, value, _stat }; PioneerDDJ400.startSamplerBlink = function(channel, control, group) { - var val = 0x7f; + let val = 0x7f; PioneerDDJ400.stopSamplerBlink(channel, control); PioneerDDJ400.timers[channel][control] = engine.beginTimer(250, function() { @@ -601,7 +614,7 @@ PioneerDDJ400.startSamplerBlink = function(channel, control, group) { // also blink the pad while SHIFT is pressed midi.sendShortMsg((channel+1), control, val); - var isPlaying = engine.getValue(group, "play") === 1; + const isPlaying = engine.getValue(group, "play") === 1; if (!isPlaying) { // kill timer @@ -656,14 +669,14 @@ PioneerDDJ400.shutdown = function() { // housekeeping // turn off all Sampler LEDs - for (var i = 0; i <= 7; ++i) { + for (let i = 0; i <= 7; ++i) { midi.sendShortMsg(0x97, 0x30 + i, 0x00); // Deck 1 pads midi.sendShortMsg(0x98, 0x30 + i, 0x00); // Deck 1 pads with SHIFT midi.sendShortMsg(0x99, 0x30 + i, 0x00); // Deck 2 pads midi.sendShortMsg(0x9A, 0x30 + i, 0x00); // Deck 2 pads with SHIFT } // turn off all Hotcue LEDs - for (i = 0; i <= 7; ++i) { + for (let i = 0; i <= 7; ++i) { midi.sendShortMsg(0x97, 0x00 + i, 0x00); // Deck 1 pads midi.sendShortMsg(0x98, 0x00 + i, 0x00); // Deck 1 pads with SHIFT midi.sendShortMsg(0x99, 0x00 + i, 0x00); // Deck 2 pads diff --git a/res/controllers/Pioneer-DDJ-400.midi.xml b/res/controllers/Pioneer-DDJ-400.midi.xml index d1abe4e38b1..018d8dc682d 100644 --- a/res/controllers/Pioneer-DDJ-400.midi.xml +++ b/res/controllers/Pioneer-DDJ-400.midi.xml @@ -2,7 +2,7 @@ Pioneer DDJ-400 - Warker/nschloe/dj3730/jusko + Warker, nschloe, dj3730, jusko, tiesjan Midi Mapping for the Pioneer DDJ-400 pioneer_ddj_400 https://mixxx.discourse.group/t/pioneer-ddj-400/17476 @@ -998,7 +998,7 @@ - BEAT LEFT - press - select effect unit 1 + BEAT LEFT - press - toggle focused Effect leftward [EffectRack1_EffectUnit1] PioneerDDJ400.beatFxLeftPressed 0x94 @@ -1007,19 +1007,9 @@ - - BEAT LEFT + shift - select previous effect for the current unit - [EffectRack1_EffectUnit1] - PioneerDDJ400.beatFxSelectPreviousEffect - 0x94 - 0x66 - - - - - BEAT RIGHT - press - select effect unit 2 + BEAT RIGHT - press - toggle focused Effect rightward [EffectRack1_EffectUnit1] PioneerDDJ400.beatFxRightPressed 0x94 @@ -1028,23 +1018,23 @@ + - BEAT RIGHT + shift - select next effect for the current unit + BEAT FX SELECT - press - load next effect entry for focused Effect [EffectRack1_EffectUnit1] - PioneerDDJ400.beatFxSelectNextEffect + PioneerDDJ400.beatFxSelectPressed 0x94 - 0x6B + 0x63 - - BEAT FX SELECT - press once - select effect unit 3 - press again - clear focus back to wet/dry mix + BEAT FX SELECT +SHIFT - press - load previous effect entry for focused Effect [EffectRack1_EffectUnit1] - PioneerDDJ400.beatFxSelectPressed + PioneerDDJ400.beatFxSelectShiftPressed 0x94 - 0x63 + 0x64 diff --git a/res/controllers/common-controller-scripts.js b/res/controllers/common-controller-scripts.js index d83fde54e35..c20a54908b6 100644 --- a/res/controllers/common-controller-scripts.js +++ b/res/controllers/common-controller-scripts.js @@ -45,23 +45,19 @@ var printObject = function(obj, maxdepth) { }; var stringifyObject = function(obj, maxdepth, checked, prefix) { - if (!maxdepth) - maxdepth = 2; + if (!maxdepth) { maxdepth = 2; } try { return JSON.stringify(obj, null, maxdepth); } catch (e) { - if (!checked) - checked = []; - if (!prefix) - prefix = ""; + if (!checked) { checked = []; } + if (!prefix) { prefix = ""; } if (maxdepth > 0 && typeof obj === "object" && obj !== null && Object.getPrototypeOf(obj) !== "" && !arrayContains(checked, obj)) { checked.push(obj); var output = "{\n"; for (var property in obj) { var value = obj[property]; - if (typeof value === "function") - continue; + if (typeof value === "function") { continue; } output += prefix + property + ": " + stringifyObject(value, maxdepth - 1, checked, prefix + " ") + "\n"; @@ -74,8 +70,7 @@ var stringifyObject = function(obj, maxdepth, checked, prefix) { var arrayContains = function(array, elem) { for (var i = 0; i < array.length; i++) { - if (array[i] === elem) - return true; + if (array[i] === elem) { return true; } } return false; }; @@ -98,8 +93,7 @@ var msecondstominutes = function(msecs) { var secs = (msecs / 1000) | 0; msecs %= 1000; msecs = Math.round(msecs * 100 / 1000); - if (msecs === 100) - msecs = 99; + if (msecs === 100) { msecs = 99; } return (m < 10 ? "0" + m : m) + ":" @@ -351,6 +345,17 @@ script.crossfaderCurve = function(value, min, max) { } }; +/* -------- ------------------------------------------------------ + script.posMod + Purpose: Computes the euclidean modulo of m % n. The result is always + in the range [0, m[ + Input: dividend `a` and divisor `m` for modulo (a % m) + Output: positive remainder + -------- ------------------------------------------------------ */ +script.posMod = function(a, m) { + return ((a % m) + m) % m; +}; + /* -------- ------------------------------------------------------ script.loopMove Purpose: Moves the current loop by the specified number of beats (default 1/2) @@ -361,7 +366,7 @@ script.crossfaderCurve = function(value, min, max) { Output: none -------- ------------------------------------------------------ */ script.loopMove = function(group, direction, numberOfBeats) { - if (!numberOfBeats || numberOfBeats === 0) numberOfBeats = 0.5; + if (!numberOfBeats || numberOfBeats === 0) { numberOfBeats = 0.5; } if (direction < 0) { engine.setValue(group, "loop_move", -numberOfBeats); @@ -495,7 +500,7 @@ bpm.tapButton = function(deck) { bpm.previousTapDelta = tapDelta; bpm.tap.push(60 / tapDelta); // Keep the last 8 samples for averaging - if (bpm.tap.length > 8) bpm.tap.shift(); + if (bpm.tap.length > 8) { bpm.tap.shift(); } var sum = 0; for (var i=0; i 1) { + if (this.packSizes[field.pack] > 1) { console.error("HIDPacket.pack - Packing multibyte bit vectors not yet supported"); return; } - for (const bit_id in bitVector.bits) { - const bit = bitVector.bits[bit_id]; - data[field.offset] = data[field.offset] | bit.value; + HIDController.fastForIn(bitVector.bits, (bit) => { + data[field.offset] |= bitVector.bits[bit].value; } + ); return; } @@ -415,17 +413,28 @@ class HIDPacket { return; } - for (let byte_index = 0; byte_index < bytes; byte_index++) { - const index = field.offset + byte_index; - if (signed) { - if (value >= 0) { - data[index] = (value >> (byte_index * 8)) & 255; - } else { - data[index] = 255 - ((-(value + 1) >> (byte_index * 8)) & 255); - } - } else { - data[index] = (value >> (byte_index * 8)) & 255; - } + const dataView = new DataView(data.buffer); + switch (field.pack) { + case "b": + dataView.setInt8(field.offset, value); + break; + case "B": + dataView.setUint8(field.offset, value); + break; + case "h": + dataView.setInt16(field.offset, value, true); + break; + case "H": + dataView.setUint16(field.offset, value, true); + break; + case "i": + dataView.setInt32(field.offset, value, true); + break; + case "I": + dataView.setUint32(field.offset, value, true); + break; + default: + // Impossible, because range checked at beginning of the function } } /** @@ -444,30 +453,24 @@ class HIDPacket { * @returns {number} Value for the field in data, represented according the fields packing type */ unpack(data, field) { - - if (!(field.pack in this.packSizes)) { + const dataView = new DataView(data.buffer); + switch (field.pack) { + case "b": + return dataView.getInt8(field.offset); + case "B": + return dataView.getUint8(field.offset); + case "h": + return dataView.getInt16(field.offset, true); + case "H": + return dataView.getUint16(field.offset, true); + case "i": + return dataView.getInt32(field.offset, true); + case "I": + return dataView.getUint32(field.offset, true); + default: console.error(`HIDPacket.unpack - Parsing packed value: invalid pack format ${field.pack}`); return undefined; } - const bytes = this.packSizes[field.pack]; - const signed = this.signedPackFormats.includes(field.pack); - - let value = 0; - for (let field_byte = 0; field_byte < bytes; field_byte++) { - if (data[field.offset + field_byte] === 255 && field_byte === 4) { - value += 0; - } else { - value += data[field.offset + field_byte] * Math.pow(2, (field_byte * 8)); - } - } - if (signed) { - const max_value = Math.pow(2, bytes * 8); - const split = max_value / 2 - 1; - if (value > split) { - value = value - max_value; - } - } - return value; } /** * Find HID packet group matching name. @@ -599,8 +602,12 @@ class HIDPacket { } const bitVector = /** @type {HIDBitVector} */ (field.value); const bit_id = `${group}.${name}`; - for (const bit_name in bitVector.bits) { - const bit = bitVector.bits[bit_name]; + + // Fast loop implementation over bitvector.bits object + const bitVectorKeyArr = Object.keys(bitVector.bits); + let bitVectorKeyIdx = bitVectorKeyArr.length; + while (bitVectorKeyIdx--) { + const bit = bitVector.bits[bitVectorKeyArr[bitVectorKeyIdx]]; if (bit.id === bit_id) { return bit; } @@ -782,7 +789,7 @@ class HIDPacket { * - H unsigned short (Uint16 Little-Endian) * - i signed integer (Int32 Little-Endian) * - I unsigned integer (Uint32 Little-Endian) - * @param {number} bitmask A bitwise mask of up to 32 bit. All bits set to'1' in this mask are + * @param {number} [bitmask=undefined] A bitwise mask of up to 32 bit. All bits set to'1' in this mask are * considered. * @param {fieldChangeCallback} [callback=undefined] Callback function for the control */ @@ -959,7 +966,8 @@ class HIDPacket { return undefined; } const bitVector = /** @type {HIDBitVector} */ (field.value); - for (const bit_id in bitVector.bits) { + + HIDController.fastForIn(bitVector.bits, (bit_id) => { const bit = bitVector.bits[bit_id]; const new_value = (bit.bitmask & value) >> bit.bit_offset; if (bit.value !== undefined && bit.value !== new_value) { @@ -967,6 +975,7 @@ class HIDPacket { } bit.value = new_value; } + ); return bits; } /** @@ -985,26 +994,37 @@ class HIDPacket { */ const field_changes = {}; - for (const group_name in this.groups) { - const group = this.groups[group_name]; - for (const field_id in group) { - const field = group[field_id]; + // Fast loop implementation over this.groups object + const groupKeyArr = Object.keys(this.groups); + let groupKeyIdx = groupKeyArr.length; + while (groupKeyIdx--) { + const group = this.groups[groupKeyArr[groupKeyIdx]]; + + // Fast loop implementation over group object + const fieldKeyArr = Object.keys(group); + let fieldKeyIdx = fieldKeyArr.length; + while (fieldKeyIdx--) { + const field = group[fieldKeyArr[fieldKeyIdx]]; + if (field === undefined) { continue; } const value = this.unpack(data, field); if (value === undefined) { - console.error(`HIDPacket.parse - Parsing packet field value for ${field_id}`); + console.error(`HIDPacket.parse - Parsing packet field value for ${group}.${field}`); return; } if (field.type === "bitvector") { // Bitvector deltas are checked in parseBitVector - const changed_bits = this.parseBitVector(field, value); - for (const bit_name in changed_bits) { - field_changes[bit_name] = changed_bits[bit_name]; + const changedBits = this.parseBitVector(field, value); + + + HIDController.fastForIn(changedBits, (changedBit) => { + field_changes[changedBit] = changedBits[changedBit]; } + ); } else if (field.type === "control") { if (field.value === value && field.mindelta !== undefined) { @@ -1057,12 +1077,17 @@ class HIDPacket { } } - for (const group_name in this.groups) { + HIDController.fastForIn(this.groups, (group_name) => { const group = this.groups[group_name]; - for (const field_name in group) { - this.pack(data, group[field_name]); + + HIDController.fastForIn(group, (field_name) => { + const field = group[field_name]; + + this.pack(data, field); } + ); } + ); if (debug) { let packet_string = ""; @@ -1117,6 +1142,14 @@ class HIDController { */ this.OutputPackets = {}; + /** + * A map to determine the output Bit or bytewise field by group and name, + * across all OutputPackets + * + * @type {Map} + */ + this.OutputFieldLookup = new Map(); + /** * Default input packet name: can be modified for controllers * which can swap modes (wiimote for example) @@ -1405,42 +1438,13 @@ class HIDController { /** * Find Output control matching give group and name * - * @todo The current implementation of this often called function is very slow and does not - * scale, due to several nested loops. * @param {string} m_group Mapped group, must be a valid Mixxx control group name e.g. "[Channel1]" * @param {string} m_name Name of mapped control, must be a valid Mixxx control name "VuMeter" * @returns {bitObject|packetField} Bit or bytewise field - Returns undefined if output field * can't be found. */ getOutputField(m_group, m_name) { - for (const packet_name in this.OutputPackets) { - const packet = this.OutputPackets[packet_name]; - for (const group_name in packet.groups) { - const group = packet.groups[group_name]; - for (const field_name in group) { - const field = group[field_name]; - if (field.type === "bitvector") { - for (const bit_id in field.value.bits) { - const bit = field.value.bits[bit_id]; - if (bit.mapped_group === m_group && bit.mapped_name === m_name) { - return bit; - } - if (bit.group === m_group && bit.name === m_name) { - return bit; - } - } - continue; - } - if (field.mapped_group === m_group && field.mapped_name === m_name) { - return field; - } - if (field.group === m_group && field.name === m_name) { - return field; - } - } - } - } - return undefined; + return this.OutputFieldLookup.get([m_group, m_name].toString()); } /** * Find input packet matching given name. @@ -1658,6 +1662,20 @@ class HIDController { for (const bit_id in field.value.bits) { const bit = field.value.bits[bit_id]; bit.packet = packet; + // Fill lookup map + if (bit.mapped_group && bit.mapped_name) { + this.OutputFieldLookup.set([bit.mapped_group, bit.mapped_name].toString(), bit); + } + if (bit.group && bit.name) { + this.OutputFieldLookup.set([bit.group, bit.name].toString(), bit); + } + } + } else { + if (field.mapped_group && field.mapped_name) { + this.OutputFieldLookup.set([field.mapped_group, field.mapped_name].toString(), field); + } + if (field.group && field.name) { + this.OutputFieldLookup.set([field.group, field.name].toString(), field); } } } @@ -1678,9 +1696,13 @@ class HIDController { if (this.InputPackets === undefined) { return; } - for (const name in this.InputPackets) { + + // Fast loop implementation over this.InputPackets object + const InputPacketsKeyArr = Object.keys(this.InputPackets); + let InputPacketsIdx = InputPacketsKeyArr.length; + while (InputPacketsIdx--) { /** @type {HIDPacket} */ - let packet = this.InputPackets[name]; + let packet = this.InputPackets[InputPacketsKeyArr[InputPacketsIdx]]; // When the device uses ReportIDs to enumerate the reports, hidapi // prepends the report ID to the data sent to Mixxx. If the device @@ -1748,16 +1770,16 @@ class HIDController { * @param {Object.} delta */ processIncomingPacket(packet, delta) { - /** @type {packetField} */ - for (const name in delta) { - // @ts-ignore ignoredControlChanges should be defined in the users mapping + + HIDController.fastForIn(delta, (field_name) => { + // @ts-ignore ignoredControlChanges should be defined in the users mapping // see EKS-Otus.js for an example if (this.ignoredControlChanges !== undefined && - // @ts-ignore - this.ignoredControlChanges.indexOf(name) !== -1) { - continue; + // @ts-ignore + this.ignoredControlChanges.indexOf(field_name) !== -1) { + return; // continue loop - by returning to fastForIn } - const field = delta[name]; + const field = delta[field_name]; if (field.type === "button") { // Button/Boolean field this.processButton(field); @@ -1768,6 +1790,7 @@ class HIDController { console.warn(`HIDController.processIncomingPacket - Unknown field ${field.name} type ${field.type}`); } } + ); } /** * Get active group for this field @@ -2298,6 +2321,23 @@ class HIDController { field.toggle = toggle_value << field.bit_offset; field.packet.send(); } + /** + * Fast loop implementation over object + * + * Don't use 'continue' and 'break' don't work as in normal loops, + * because body is a function + * 'return' statements in the body function behaves as 'continue' in normal loops + * + * @param {Object.} object + * @param {function (string):void } body + */ + static fastForIn(object, body) { + const objKeyArray = Object.keys(object); + let objKeyArrayIdx = objKeyArray.length; + while (objKeyArrayIdx--) { + body(objKeyArray[objKeyArrayIdx]); + } + } } // Add class HIDController to the Global JavaScript object // @ts-ignore Same identifier for class and instance needed for backward compatibility diff --git a/res/controllers/midi-components-0.0.js b/res/controllers/midi-components-0.0.js index 6bed8a8bf23..18ec1c48f57 100644 --- a/res/controllers/midi-components-0.0.js +++ b/res/controllers/midi-components-0.0.js @@ -786,6 +786,10 @@ engine.scratchDisable(this.deck); } }, + input: function(_channel, control, _value, status, _group) { + throw "Called wrong input handler for " + status + ": " + control + ".\n" + + "Please bind jogwheel-related messages to inputWheel and inputTouch!\n"; + } }); const EffectUnit = function(unitNumbers, allowFocusWhenParametersHidden, colors) { diff --git a/res/linux/org.mixxx.Mixxx.metainfo.xml b/res/linux/org.mixxx.Mixxx.metainfo.xml index d62f6dfd9da..be867e9fcbf 100644 --- a/res/linux/org.mixxx.Mixxx.metainfo.xml +++ b/res/linux/org.mixxx.Mixxx.metainfo.xml @@ -96,7 +96,7 @@ Do not edit it manually. --> - +

Cover Art @@ -1527,6 +1527,68 @@ #4907 + + + + +

    +
  • + Fix empty waveform overview after loading a track (Mixxx 2.3.4 regression) + Fixed by + #11333 + #11359 + #11344 +
  • +
  • + Fullscreen: Fix a crash that occurs on Linux after enabling fullsceen and using menu + shortcuts e.g. Alt-F. + #11328 + #11320 +
  • +
  • + Fullscreen: Rebuild & reconnect menu only on desktops with global menu + #11350 +
  • +
  • + macOS: Request Microphone and line-in access permission. + #11367 + #11365 +
  • +
  • + JACK API: Allow to explicit select buffers of 2048 and 4096 frames/period. They are not + supported by the automatic buffer setting of the used PortAudio library. + #11366 + #11341 +
  • +
  • + Pioneer DDJ-400: Make Beat FX section more intuitive + #10912 +
  • +
  • + Playlist export: Adopt new extension after changing the playlist type + #11332 + #11327 +
  • +
  • + LateNight: brighter fx parameter buttons + #11397 +
  • +
  • + Fix drift in analyzis data after exporting metadata to MP3 files with ID3v1.1 tags + #11168 + #11159 +
  • +
  • + Fix broadcasting using Opus encoding + #11349 + #10666 +
  • +
  • + Tango: Remove VU peak indicators from stacked layout. This fixes a visual regression in Mixxx 2.3.4. + #11430 + #11362 +
  • +
@@ -1700,7 +1762,7 @@
  • Year search: Find also full date entries #11251 - #11251 + #11113
  • Fix visual alignment of beats and waveform in case of decoding issues diff --git a/res/skins/LateNight/classic/style/splitter_handle_horizontal.png b/res/skins/LateNight/classic/style/splitter_handle_horizontal.png index df271fa6766..aaf6b944796 100644 Binary files a/res/skins/LateNight/classic/style/splitter_handle_horizontal.png and b/res/skins/LateNight/classic/style/splitter_handle_horizontal.png differ diff --git a/res/skins/LateNight/classic/style/splitter_handle_horizontal_pressed.png b/res/skins/LateNight/classic/style/splitter_handle_horizontal_pressed.png index 14060ce6afd..33b11e3e369 100644 Binary files a/res/skins/LateNight/classic/style/splitter_handle_horizontal_pressed.png and b/res/skins/LateNight/classic/style/splitter_handle_horizontal_pressed.png differ diff --git a/res/skins/LateNight/classic/style/splitter_handle_vertical_pressed.png b/res/skins/LateNight/classic/style/splitter_handle_vertical_pressed.png index 0c0af2be814..3873b055508 100644 Binary files a/res/skins/LateNight/classic/style/splitter_handle_vertical_pressed.png and b/res/skins/LateNight/classic/style/splitter_handle_vertical_pressed.png differ diff --git a/res/skins/LateNight/mixer/channel_4decks.xml b/res/skins/LateNight/mixer/channel_4decks.xml index 5fce1246270..361dafe5d41 100644 --- a/res/skins/LateNight/mixer/channel_4decks.xml +++ b/res/skins/LateNight/mixer/channel_4decks.xml @@ -14,10 +14,6 @@ min,min - - 0min,0me - - AlignRight horizontal @@ -34,7 +30,10 @@ - 1min,3f + + 1min,3me - 1min,3f + 1min,3me diff --git a/res/skins/LateNight/mixer/channel_left.xml b/res/skins/LateNight/mixer/channel_left.xml index a3e4666966f..4cc3d004bd2 100644 --- a/res/skins/LateNight/mixer/channel_left.xml +++ b/res/skins/LateNight/mixer/channel_left.xml @@ -23,6 +23,11 @@ vertical layout and a side-by-side layout for two-deck mode --> min,min + + 1min,0me + - 1min,2f + 1min,2me +