diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f7e4d99f757..73b25d53fb3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -94,35 +94,6 @@ jobs: artifact-windows-win64: ${{ steps.prepare_deploy.outputs.artifact-windows-win64 }} steps: - - name: "[Windows] Install rsync and openssh" - if: runner.os == 'Windows' - run: | - $Env:PATH="c:\msys64\usr\bin;$Env:PATH" - pacman -S --noconfirm coreutils bash rsync openssh - # Unfortunately, mixing executables from msys64 and mingw (i.e. Git - # Bash) does not work properly and leads to errors like these: - # - # 0 [main] python3 (5248) C:\msys64\usr\bin\python3.exe: *** fatal error - cygheap base mismatch detected - 0x180347408/0x180352408. - # - # Even when prepending the MSYS2 binary directory to %PATH%, GitHub - # Actions will still pick the Git Bash executable over the MSYS2 one - # when using `shell: bash`. Since it's not feasible to set `shell` to - # an absolute path in a cross-platform build workflow, we overwrite the - # git bash executable with the MSYS2 one. - # - # Also see related issue: - # https://github.com/actions/virtual-environments/issues/594 - Copy-Item -Path "C:\msys64\usr\bin\bash.exe" -Destination "C:\Program Files\Git\bin\bash.exe" -Force - # By default, MSYS2 uses an - # /etc/profile file that changes - # the current working directory - # when bash is started. We don't - # want this behavior,so we just - # delete it. - Remove-Item -Path "C:\msys64\etc\profile" - # Add MSYS2's tools to %PATH%. - Add-Content -Path "$Env:GITHUB_ENV" -Value "PATH=$Env:PATH" - # sccache's handling of the /fp:fast MSVC compiler option is broken, so use our fork with the fix. # https://github.com/mozilla/sccache/issues/950 - name: "[Windows] Set up cargo cache" @@ -362,6 +333,39 @@ jobs: --dest-url 'https://downloads.mixxx.org' ${{ matrix.artifacts_path }} + # Warning: do not move this step before restoring caches or it will break caching due to + # https://github.com/actions/cache/issues/531 + - name: "[Windows] Install rsync and openssh" + env: + SSH_PRIVATE_KEY: ${{ secrets.DOWNLOADS_HOSTGATOR_DOT_MIXXX_DOT_ORG_KEY }} + if: runner.os == 'Windows' && github.event_name == 'push' && env.SSH_PRIVATE_KEY != null + run: | + $Env:PATH="c:\msys64\usr\bin;$Env:PATH" + pacman -S --noconfirm coreutils bash rsync openssh + # Unfortunately, mixing executables from msys64 and mingw (i.e. Git + # Bash) does not work properly and leads to errors like these: + # + # 0 [main] python3 (5248) C:\msys64\usr\bin\python3.exe: *** fatal error - cygheap base mismatch detected - 0x180347408/0x180352408. + # + # Even when prepending the MSYS2 binary directory to %PATH%, GitHub + # Actions will still pick the Git Bash executable over the MSYS2 one + # when using `shell: bash`. Since it's not feasible to set `shell` to + # an absolute path in a cross-platform build workflow, we overwrite the + # git bash executable with the MSYS2 one. + # + # Also see related issue: + # https://github.com/actions/virtual-environments/issues/594 + Copy-Item -Path "C:\msys64\usr\bin\bash.exe" -Destination "C:\Program Files\Git\bin\bash.exe" -Force + # By default, MSYS2 uses an + # /etc/profile file that changes + # the current working directory + # when bash is started. We don't + # want this behavior,so we just + # delete it. + Remove-Item -Path "C:\msys64\etc\profile" + # Add MSYS2's tools to %PATH%. + Add-Content -Path "$Env:GITHUB_ENV" -Value "PATH=$Env:PATH" + - name: "Set up SSH Agent" if: github.event_name == 'push' && env.SSH_PRIVATE_KEY != null shell: bash diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e58e9603940..dfccf2ad70f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -137,3 +137,15 @@ repos: language: system types: [text] files: ^.*\.qml$ + - id: metainfo + name: metainfo + description: Update AppStream metainfo releases from CHANGELOG.md. + entry: ./tools/update_metainfo.py + pass_filenames: false + language: python + additional_dependencies: + - beautifulsoup4==4.9.3 + - lxml==4.6.3 + - Markdown==3.3.4 + types: [text] + files: ^(CHANGELOG\.md|res/linux/org\.mixxx\.Mixxx\.metainfo.xml)$ diff --git a/CHANGELOG.md b/CHANGELOG.md index 9af0201a977..99a791c5a08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,404 @@ ## [2.4.0](https://launchpad.net/mixxx/+milestone/2.4.0) (Unreleased) -* Cover art: Prevent wrong cover art display due to hash conflicts -* Cover art: Add background color for quick cover art preview -* Add Random Track Control to AutoDJ [#3076](https://github.com/mixxxdj/mixxx/pull/3076) -* Add support for saving loops as hotcues [#2194](https://github.com/mixxxdj/mixxx/pull/2194) [lp:1367159](https://bugs.launchpad.net/mixxx/+bug/1367159) +### Cover Art + +* Prevent wrong cover art display due to hash conflicts [#2524](https://github.com/mixxxdj/mixxx/pull/2524) +* Add background color for quick cover art preview [#2524](https://github.com/mixxxdj/mixxx/pull/2524) + +### Music Library + +* Ensure that tracks with an invalid BPM are re-analyzed [#2776](https://github.com/mixxxdj/mixxx/pull/2776) +* Add support for exporting crates, playlists and the library to Engine Prime and Denon standalone controllers + [#2753](https://github.com/mixxxdj/mixxx/pull/2753) + [#2932](https://github.com/mixxxdj/mixxx/pull/2932) + [#3102](https://github.com/mixxxdj/mixxx/pull/3102) + [#3155](https://github.com/mixxxdj/mixxx/pull/3155) + [#3621](https://github.com/mixxxdj/mixxx/pull/3621) + [#3776](https://github.com/mixxxdj/mixxx/pull/3776) + [#3787](https://github.com/mixxxdj/mixxx/pull/3787) + [#3797](https://github.com/mixxxdj/mixxx/pull/3797) + [#3798](https://github.com/mixxxdj/mixxx/pull/3798) + [#4025](https://github.com/mixxxdj/mixxx/pull/4025) + [#4087](https://github.com/mixxxdj/mixxx/pull/4087) + [#4102](https://github.com/mixxxdj/mixxx/pull/4102) + [#4143](https://github.com/mixxxdj/mixxx/pull/4143) +* Rekordbox: Save all loops and correct AAC timing offset for CoreAudio [#2779](https://github.com/mixxxdj/mixxx/pull/2779) +* Improve log messages during schema migration [#2979](https://github.com/mixxxdj/mixxx/pull/2979) +* Search related tracks in collection + [#3181](https://github.com/mixxxdj/mixxx/pull/3181) + [#3213](https://github.com/mixxxdj/mixxx/pull/3213) + [#2796](https://github.com/mixxxdj/mixxx/pull/2796) + [#4207](https://github.com/mixxxdj/mixxx/pull/4207) +* Add recent searches to a drop down menu of the search box + [#3171](https://github.com/mixxxdj/mixxx/pull/3171) + [#3262](https://github.com/mixxxdj/mixxx/pull/3262) +* Add new "[AutoDJ],add_random_track" to make this feature accessible from controllers [#3076](https://github.com/mixxxdj/mixxx/pull/3076) +* Add new library column that shows the last time a track was played + [#3140](https://github.com/mixxxdj/mixxx/pull/3140) + [#3457](https://github.com/mixxxdj/mixxx/pull/3457) + [#3494](https://github.com/mixxxdj/mixxx/pull/3494) + [#3596](https://github.com/mixxxdj/mixxx/pull/3596) + [#3740](https://github.com/mixxxdj/mixxx/pull/3740) +* Improve presentation of the history library tree [#2996](https://github.com/mixxxdj/mixxx/pull/2996) +* Don't store or update metadata of missing tracks in the Mixxx database to prevent inconsistencies with file tags [#3811](https://github.com/mixxxdj/mixxx/pull/3811) +* Code improvements and minor bug fixes when importing track metadata + [#3851](https://github.com/mixxxdj/mixxx/pull/3851) + [#3858](https://github.com/mixxxdj/mixxx/pull/3858) + [#3860](https://github.com/mixxxdj/mixxx/pull/3860) + [#3866](https://github.com/mixxxdj/mixxx/pull/3866) + [#3871](https://github.com/mixxxdj/mixxx/pull/3871) + [#3870](https://github.com/mixxxdj/mixxx/pull/3870) + [#3924](https://github.com/mixxxdj/mixxx/pull/3924) + [#3906](https://github.com/mixxxdj/mixxx/pull/3906) + [#3998](https://github.com/mixxxdj/mixxx/pull/3998) +* Update library schema to 37 for synchronizing file modified time with track source on metadata import/export + [#3978](https://github.com/mixxxdj/mixxx/pull/3978) + [#4012](https://github.com/mixxxdj/mixxx/pull/4012) +* Logging: Suppress expected and harmless schema migration errors [#4248](https://github.com/mixxxdj/mixxx/pull/4248) +* Only show the date in Date Added / Last Played columns. Move the time of day to tooltips [#3945](https://github.com/mixxxdj/mixxx/pull/3945) +* Fix handling of undefined BPM values + [#4062](https://github.com/mixxxdj/mixxx/pull/4062) + [#4063](https://github.com/mixxxdj/mixxx/pull/4063) + [#4100](https://github.com/mixxxdj/mixxx/pull/4100) + [#4154](https://github.com/mixxxdj/mixxx/pull/4154) + [#4165](https://github.com/mixxxdj/mixxx/pull/4165) + [#4168](https://github.com/mixxxdj/mixxx/pull/4168) +* Add support for m4v files [#4088](https://github.com/mixxxdj/mixxx/pull/4088) +* Adjust ReplayGain: Allow user to update the replaygain value based on a deck pregain value [#4031](https://github.com/mixxxdj/mixxx/pull/4031) +* Automatic analyze and optimize database [#4199](https://github.com/mixxxdj/mixxx/pull/4199) +* Fix playlists sidebar navigation/activation [#4193](https://github.com/mixxxdj/mixxx/pull/4193) [lp:1939082](https://bugs.launchpad.net/mixxx/+bug/1939082) +* Refactoring of library code + [#2756](https://github.com/mixxxdj/mixxx/pull/2756) + [#2717](https://github.com/mixxxdj/mixxx/pull/2717) + [#2715](https://github.com/mixxxdj/mixxx/pull/2715) + [#2810](https://github.com/mixxxdj/mixxx/pull/2756) + [#2900](https://github.com/mixxxdj/mixxx/pull/2900) + [#2906](https://github.com/mixxxdj/mixxx/pull/2906) + [#2925](https://github.com/mixxxdj/mixxx/pull/2925) + [#3017](https://github.com/mixxxdj/mixxx/pull/3017) + [#3475](https://github.com/mixxxdj/mixxx/pull/3475) + [#4164](https://github.com/mixxxdj/mixxx/pull/4164) + [#4152](https://github.com/mixxxdj/mixxx/pull/4152) + [#4162](https://github.com/mixxxdj/mixxx/pull/4162) + [#4101](https://github.com/mixxxdj/mixxx/pull/4101) + [#4214](https://github.com/mixxxdj/mixxx/pull/4214) + +### Sync + +* Add support for setting an explicit leader for sync lock + [#2768](https://github.com/mixxxdj/mixxx/pull/2768) + [#3099](https://github.com/mixxxdj/mixxx/pull/3099) + [#3695](https://github.com/mixxxdj/mixxx/pull/3695) + [#3734](https://github.com/mixxxdj/mixxx/pull/3734) + [#3698](https://github.com/mixxxdj/mixxx/pull/3698) + [#3864](https://github.com/mixxxdj/mixxx/pull/3864) + [#3867](https://github.com/mixxxdj/mixxx/pull/3867) + [#3921](https://github.com/mixxxdj/mixxx/pull/3921) + [#4119](https://github.com/mixxxdj/mixxx/pull/4119) + [#4135](https://github.com/mixxxdj/mixxx/pull/4135) + [#4149](https://github.com/mixxxdj/mixxx/pull/4149) +* Fix issue with half/double BPM calculation when using sync + [#3899](https://github.com/mixxxdj/mixxx/pull/3899) + [#3706](https://github.com/mixxxdj/mixxx/pull/3706) +* Sync Lock: Don't seek phase when disabling sync [#4169](https://github.com/mixxxdj/mixxx/pull/4169) + +### Audio Codecs + +* Fix recovering from FAAD2 decoding issues [#2850](https://github.com/mixxxdj/mixxx/pull/2850) + +### Audio Engine + +* Add support for Saved loops + [#2194](https://github.com/mixxxdj/mixxx/pull/2194) + [#3267](https://github.com/mixxxdj/mixxx/pull/3267) + [#3202](https://github.com/mixxxdj/mixxx/pull/3202) +* Fix an issue when pressing multiple cue buttons at the same time [#3382](https://github.com/mixxxdj/mixxx/pull/3382) +* Fix synchronization of main cue point/position + [#4137](https://github.com/mixxxdj/mixxx/pull/4137) + [lp1937074](https://bugs.launchpad.net/mixxx/+bug/1937074) + [#4153](https://github.com/mixxxdj/mixxx/pull/4153) +* Refactoring of beatgrid/beatmap code + [#4044](https://github.com/mixxxdj/mixxx/pull/4044) + [#4048](https://github.com/mixxxdj/mixxx/pull/4048) + [#4045](https://github.com/mixxxdj/mixxx/pull/4045) + [#4049](https://github.com/mixxxdj/mixxx/pull/4049) + [#4092](https://github.com/mixxxdj/mixxx/pull/4092) + [#4094](https://github.com/mixxxdj/mixxx/pull/4094) + [#4104](https://github.com/mixxxdj/mixxx/pull/4104) + [#4103](https://github.com/mixxxdj/mixxx/pull/4103) + [#4127](https://github.com/mixxxdj/mixxx/pull/4127) + [#4099](https://github.com/mixxxdj/mixxx/pull/4099) + [#4071](https://github.com/mixxxdj/mixxx/pull/4071) + [#4184](https://github.com/mixxxdj/mixxx/pull/4184) + [#4234](https://github.com/mixxxdj/mixxx/pull/4234) + [#4233](https://github.com/mixxxdj/mixxx/pull/4233) + [#4258](https://github.com/mixxxdj/mixxx/pull/4258) + [#4259](https://github.com/mixxxdj/mixxx/pull/4259) + [#4263](https://github.com/mixxxdj/mixxx/pull/4263) + [#4272](https://github.com/mixxxdj/mixxx/pull/4272) +* Refactoring of audio engine code + [#2762](https://github.com/mixxxdj/mixxx/pull/2762) + [#2801](https://github.com/mixxxdj/mixxx/pull/2801) + [#2885](https://github.com/mixxxdj/mixxx/pull/2885) + [#2997](https://github.com/mixxxdj/mixxx/pull/2997) + [#3266](https://github.com/mixxxdj/mixxx/pull/3266) + [#4064](https://github.com/mixxxdj/mixxx/pull/4064) + [#4065](https://github.com/mixxxdj/mixxx/pull/4065) + [#4066](https://github.com/mixxxdj/mixxx/pull/4066) + [#4069](https://github.com/mixxxdj/mixxx/pull/4069) + [#4074](https://github.com/mixxxdj/mixxx/pull/4074) + [#4075](https://github.com/mixxxdj/mixxx/pull/4075) + [#4076](https://github.com/mixxxdj/mixxx/pull/4076) + [#4078](https://github.com/mixxxdj/mixxx/pull/4078) + [#4082](https://github.com/mixxxdj/mixxx/pull/4082) + [#4077](https://github.com/mixxxdj/mixxx/pull/4077) + [#4080](https://github.com/mixxxdj/mixxx/pull/4080) + [#4086](https://github.com/mixxxdj/mixxx/pull/4086) + [#4089](https://github.com/mixxxdj/mixxx/pull/4089) + [#4090](https://github.com/mixxxdj/mixxx/pull/4090) + [#4079](https://github.com/mixxxdj/mixxx/pull/4079) + [#4091](https://github.com/mixxxdj/mixxx/pull/4091) + [#4083](https://github.com/mixxxdj/mixxx/pull/4083) + [#4095](https://github.com/mixxxdj/mixxx/pull/4095) + [#4081](https://github.com/mixxxdj/mixxx/pull/4081) + [#4061](https://github.com/mixxxdj/mixxx/pull/4061) + [#4105](https://github.com/mixxxdj/mixxx/pull/4105) + [#4183](https://github.com/mixxxdj/mixxx/pull/4183) + [#4186](https://github.com/mixxxdj/mixxx/pull/4186) + [#4189](https://github.com/mixxxdj/mixxx/pull/4189) + [#4216](https://github.com/mixxxdj/mixxx/pull/4216) + [#4221](https://github.com/mixxxdj/mixxx/pull/4221) + [#4219](https://github.com/mixxxdj/mixxx/pull/4219) + [#4191](https://github.com/mixxxdj/mixxx/pull/4191) + [#4232](https://github.com/mixxxdj/mixxx/pull/4232) + [#4231](https://github.com/mixxxdj/mixxx/pull/4231) + [#4229](https://github.com/mixxxdj/mixxx/pull/4229) + [#4257](https://github.com/mixxxdj/mixxx/pull/4257) + [#4266](https://github.com/mixxxdj/mixxx/pull/4266) + [#4217](https://github.com/mixxxdj/mixxx/pull/4217) + +### Controllers + +* Never raise a fatal error if a controller mapping tries access a non-existent control object [#2947](https://github.com/mixxxdj/mixxx/pull/2947) +* Update Novation Launchpad controller scripts [#2600](https://github.com/mixxxdj/mixxx/pull/2600) +* Add generic USB HID "Set Reports (Feature)" functionality [#3051](https://github.com/mixxxdj/mixxx/pull/3051) +* Add support for reading the status of an HID controller (like MIDI SYSEX) [#3317](https://github.com/mixxxdj/mixxx/pull/3317) +* Use hidapi's hidraw backend instead of libusb on Linux [#4054](https://github.com/mixxxdj/mixxx/pull/4054) +* Consistently use "mapping" instead of "preset" to refer to controller mappings [#3472](https://github.com/mixxxdj/mixxx/pull/3472) +* Introduce new control objects `[Master],indicator_250millis` and `[Master],indicator_500millis` [#4157](https://github.com/mixxxdj/mixxx/pull/4157) +* Don't automatically enable controller if it was disabled before [#4244](https://github.com/mixxxdj/mixxx/pull/4244) [lp:1941042](https://bugs.launchpad.net/mixxx/+bug/1941042) +* Roland DJ-505: Use new ControlIndicator COs for blinking lights [#4159](https://github.com/mixxxdj/mixxx/pull/4159) +* Prepare code for upcoming ES6 based controller mapping system with module support + [#2682](https://github.com/mixxxdj/mixxx/pull/2682) + [#2868](https://github.com/mixxxdj/mixxx/pull/2868) + [#2875](https://github.com/mixxxdj/mixxx/pull/2875) + [#2936](https://github.com/mixxxdj/mixxx/pull/2936) + [#2946](https://github.com/mixxxdj/mixxx/pull/2946) +* Other refactorings of controller code + [#2904](https://github.com/mixxxdj/mixxx/pull/2904) + [#3308](https://github.com/mixxxdj/mixxx/pull/3308) + [#3463](https://github.com/mixxxdj/mixxx/pull/3463) + [#3634](https://github.com/mixxxdj/mixxx/pull/3634) + [#3635](https://github.com/mixxxdj/mixxx/pull/3635) + [#3636](https://github.com/mixxxdj/mixxx/pull/3636) + [#3676](https://github.com/mixxxdj/mixxx/pull/3676) + [#3880](https://github.com/mixxxdj/mixxx/pull/3880) + [#4085](https://github.com/mixxxdj/mixxx/pull/4085) + +### Skins + +* Add experimental QML skin + [#3345](https://github.com/mixxxdj/mixxx/pull/3345) + [#3446](https://github.com/mixxxdj/mixxx/pull/3446) + [#3854](https://github.com/mixxxdj/mixxx/pull/3854) + [#3891](https://github.com/mixxxdj/mixxx/pull/3891) + [#2874](https://github.com/mixxxdj/mixxx/pull/2874) + [#3915](https://github.com/mixxxdj/mixxx/pull/3915) + [#3894](https://github.com/mixxxdj/mixxx/pull/3894) + [#3920](https://github.com/mixxxdj/mixxx/pull/3920) + [#3907](https://github.com/mixxxdj/mixxx/pull/3907) + [#3925](https://github.com/mixxxdj/mixxx/pull/3925) + [#3928](https://github.com/mixxxdj/mixxx/pull/3928) + [#3932](https://github.com/mixxxdj/mixxx/pull/3932) + [#3911](https://github.com/mixxxdj/mixxx/pull/3911) + [#3937](https://github.com/mixxxdj/mixxx/pull/3937) + [#3940](https://github.com/mixxxdj/mixxx/pull/3940) + [#3913](https://github.com/mixxxdj/mixxx/pull/3913) + [#3950](https://github.com/mixxxdj/mixxx/pull/3950) + [#3919](https://github.com/mixxxdj/mixxx/pull/3919) + [#3955](https://github.com/mixxxdj/mixxx/pull/3955) + [#3957](https://github.com/mixxxdj/mixxx/pull/3957) + [#3961](https://github.com/mixxxdj/mixxx/pull/3961) + [#3952](https://github.com/mixxxdj/mixxx/pull/3952) + [#3963](https://github.com/mixxxdj/mixxx/pull/3963) + [#3971](https://github.com/mixxxdj/mixxx/pull/3971) + [#3959](https://github.com/mixxxdj/mixxx/pull/3959) + [#3972](https://github.com/mixxxdj/mixxx/pull/3972) + [#3992](https://github.com/mixxxdj/mixxx/pull/3992) + [#4003](https://github.com/mixxxdj/mixxx/pull/4003) + [#4004](https://github.com/mixxxdj/mixxx/pull/4004) + [#3999](https://github.com/mixxxdj/mixxx/pull/3999) + [#4000](https://github.com/mixxxdj/mixxx/pull/4000) + [#4067](https://github.com/mixxxdj/mixxx/pull/4067) + [#4068](https://github.com/mixxxdj/mixxx/pull/4068) + [#4060](https://github.com/mixxxdj/mixxx/pull/4060) + [#4037](https://github.com/mixxxdj/mixxx/pull/4037) + [#3934](https://github.com/mixxxdj/mixxx/pull/3934) + [#4117](https://github.com/mixxxdj/mixxx/pull/4117) +* Add new "RGB Stacked" waveform [#3153](https://github.com/mixxxdj/mixxx/pull/3153) +* Add harmonic keywheel window + [#1695](https://github.com/mixxxdj/mixxx/pull/1695) + [#3622](https://github.com/mixxxdj/mixxx/pull/3622) + [#3624](https://github.com/mixxxdj/mixxx/pull/3624) +* Make beat indicator control behaviour more natural [#3608](https://github.com/mixxxdj/mixxx/pull/3608) +* Fix crash if no skin is available + [#3918](https://github.com/mixxxdj/mixxx/pull/3918) + [#3939](https://github.com/mixxxdj/mixxx/pull/3939) +* Inverted scroll wheel waveform zoom direction to mach other applications [#4195](https://github.com/mixxxdj/mixxx/pull/4195) +* Fix leaked controls [#4213](https://github.com/mixxxdj/mixxx/pull/4213) [lp:1912129](https://bugs.launchpad.net/mixxx/+bug/1912129) + +### Other + +* Improve/fix the build system + [#2796](https://github.com/mixxxdj/mixxx/pull/2796) + [#2937](https://github.com/mixxxdj/mixxx/pull/2937) + [#2943](https://github.com/mixxxdj/mixxx/pull/2943) + [#3041](https://github.com/mixxxdj/mixxx/pull/3041) + [#3046](https://github.com/mixxxdj/mixxx/pull/3046) + [#3114](https://github.com/mixxxdj/mixxx/pull/3114) + [#3182](https://github.com/mixxxdj/mixxx/pull/3182) + [#3274](https://github.com/mixxxdj/mixxx/pull/3274) + [#3300](https://github.com/mixxxdj/mixxx/pull/3300) + [#3471](https://github.com/mixxxdj/mixxx/pull/3471) + [#3514](https://github.com/mixxxdj/mixxx/pull/3514) + [#3765](https://github.com/mixxxdj/mixxx/pull/3765) + [#3849](https://github.com/mixxxdj/mixxx/pull/3849) + [#3876](https://github.com/mixxxdj/mixxx/pull/3876) + [#3861](https://github.com/mixxxdj/mixxx/pull/3861) + [#3923](https://github.com/mixxxdj/mixxx/pull/3923) + [#3948](https://github.com/mixxxdj/mixxx/pull/3948) + [#3929](https://github.com/mixxxdj/mixxx/pull/3929) + [#4007](https://github.com/mixxxdj/mixxx/pull/4007) + [#4070](https://github.com/mixxxdj/mixxx/pull/4070) + [#4084](https://github.com/mixxxdj/mixxx/pull/4084) + [#4098](https://github.com/mixxxdj/mixxx/pull/4098) + [#4113](https://github.com/mixxxdj/mixxx/pull/4113) + [#4163](https://github.com/mixxxdj/mixxx/pull/4163) + [#4166](https://github.com/mixxxdj/mixxx/pull/4166) + [#4185](https://github.com/mixxxdj/mixxx/pull/4185) + [#4187](https://github.com/mixxxdj/mixxx/pull/4187) + [#4192](https://github.com/mixxxdj/mixxx/pull/4192) + [#4226](https://github.com/mixxxdj/mixxx/pull/4226) + [#4203](https://github.com/mixxxdj/mixxx/pull/4203) + [#4250](https://github.com/mixxxdj/mixxx/pull/4250) + [#4274](https://github.com/mixxxdj/mixxx/pull/4274) +* Drop Ubuntu Bionic support, require Qt 5.12 + [#3687](https://github.com/mixxxdj/mixxx/pull/3687) + [#3735](https://github.com/mixxxdj/mixxx/pull/3735) + [#3736](https://github.com/mixxxdj/mixxx/pull/3736) + [#3985](https://github.com/mixxxdj/mixxx/pull/3985) +* Add NixOS support + [#2820](https://github.com/mixxxdj/mixxx/pull/2820) + [#2828](https://github.com/mixxxdj/mixxx/pull/2828) + [#2836](https://github.com/mixxxdj/mixxx/pull/2836) + [#2827](https://github.com/mixxxdj/mixxx/pull/2827) + [#2827](https://github.com/mixxxdj/mixxx/pull/2827) + [#2828](https://github.com/mixxxdj/mixxx/pull/2828) + [#3113](https://github.com/mixxxdj/mixxx/pull/3113) + [#3089](https://github.com/mixxxdj/mixxx/pull/3089) + [#3545](https://github.com/mixxxdj/mixxx/pull/3545) +* Add support for saving loops as hotcues + [#2194](https://github.com/mixxxdj/mixxx/pull/2194) + [#4265](https://github.com/mixxxdj/mixxx/pull/4265) + [lp:1367159](https://bugs.launchpad.net/mixxx/+bug/1367159) +* Make use of inclusive language + [#2894](https://github.com/mixxxdj/mixxx/pull/2894) + [#3868](https://github.com/mixxxdj/mixxx/pull/3868) +* Add Noise effect [#2921](https://github.com/mixxxdj/mixxx/pull/2921) +* Improve Unittests + [#2938](https://github.com/mixxxdj/mixxx/pull/2938) + [#2980](https://github.com/mixxxdj/mixxx/pull/2980) + [#3006](https://github.com/mixxxdj/mixxx/pull/3006) +* Logging: Add support for `QT_MESSAGE_PATTERN` environment variable + [#3204](https://github.com/mixxxdj/mixxx/pull/3204) + [#3518](https://github.com/mixxxdj/mixxx/pull/3518) +* Colored logging console output + [#3197](https://github.com/mixxxdj/mixxx/pull/3197) +* Improve command line argument parser + [#3640](https://github.com/mixxxdj/mixxx/pull/3640) + [#3962](https://github.com/mixxxdj/mixxx/pull/3962) + [#4022](https://github.com/mixxxdj/mixxx/pull/4022) + [#4036](https://github.com/mixxxdj/mixxx/pull/4036) + [#4170](https://github.com/mixxxdj/mixxx/pull/4170) + [#4057](https://github.com/mixxxdj/mixxx/pull/4057) +* Improve message when dealing with macOS sandbox [#4018](https://github.com/mixxxdj/mixxx/pull/4018) [lp:1921541](https://bugs.launchpad.net/mixxx/+bug/1921541) +* Move contribution guidelines into our git repository [#2699](https://github.com/mixxxdj/mixxx/pull/2699) +* Automize deployment of CHANGELOG to the manual + [#4180](https://github.com/mixxxdj/mixxx/pull/4180) + [#4256](https://github.com/mixxxdj/mixxx/pull/4256) + [#4208](https://github.com/mixxxdj/mixxx/pull/4208) + [#4228](https://github.com/mixxxdj/mixxx/pull/4228) + [#4222](https://github.com/mixxxdj/mixxx/pull/4222) +* Always show tooltips in preferences [#4198](https://github.com/mixxxdj/mixxx/pull/4198) [lp:1840493](https://bugs.launchpad.net/mixxx/+bug/1840493) +* Allow to build Mixxx on Linux without ALSA, working around a Pipewire bug [#4242](https://github.com/mixxxdj/mixxx/pull/4242) +* Fix possible crash with opus files with embedded cover arts and require TagLib 1.11 or newer + [#4251](https://github.com/mixxxdj/mixxx/pull/4251) + [#4252](https://github.com/mixxxdj/mixxx/pull/4252) + [lp:1940777](https://bugs.launchpad.net/mixxx/+bug/1940777) +* Misc. refactorings + [#3154](https://github.com/mixxxdj/mixxx/pull/3154) + [#2870](https://github.com/mixxxdj/mixxx/pull/2870) + [#2872](https://github.com/mixxxdj/mixxx/pull/2872) + [#2978](https://github.com/mixxxdj/mixxx/pull/2978) + [#2969](https://github.com/mixxxdj/mixxx/pull/2969) + [#3016](https://github.com/mixxxdj/mixxx/pull/3016) + [#3320](https://github.com/mixxxdj/mixxx/pull/3320) + [#3356](https://github.com/mixxxdj/mixxx/pull/3356) + [#3453](https://github.com/mixxxdj/mixxx/pull/3453) + [#3487](https://github.com/mixxxdj/mixxx/pull/3487) + [#3558](https://github.com/mixxxdj/mixxx/pull/3558) + [#3685](https://github.com/mixxxdj/mixxx/pull/3685) + [#3741](https://github.com/mixxxdj/mixxx/pull/3741) + [#3744](https://github.com/mixxxdj/mixxx/pull/3744) + [#3753](https://github.com/mixxxdj/mixxx/pull/3753) + [#3761](https://github.com/mixxxdj/mixxx/pull/3761) + [#3834](https://github.com/mixxxdj/mixxx/pull/3834) + [#3842](https://github.com/mixxxdj/mixxx/pull/3842) + [#3853](https://github.com/mixxxdj/mixxx/pull/3853) + [#3874](https://github.com/mixxxdj/mixxx/pull/3874) + [#3883](https://github.com/mixxxdj/mixxx/pull/3883) + [#3922](https://github.com/mixxxdj/mixxx/pull/3922) + [#3947](https://github.com/mixxxdj/mixxx/pull/3947) + [#3974](https://github.com/mixxxdj/mixxx/pull/3974) + [#4024](https://github.com/mixxxdj/mixxx/pull/4024) + [#4026](https://github.com/mixxxdj/mixxx/pull/4026) + [#4034](https://github.com/mixxxdj/mixxx/pull/4034) + [#4038](https://github.com/mixxxdj/mixxx/pull/4038) + [#4039](https://github.com/mixxxdj/mixxx/pull/4039) + [#4043](https://github.com/mixxxdj/mixxx/pull/4043) + [#4053](https://github.com/mixxxdj/mixxx/pull/4053) + [#4072](https://github.com/mixxxdj/mixxx/pull/4072) + [#4097](https://github.com/mixxxdj/mixxx/pull/4097) + [#4096](https://github.com/mixxxdj/mixxx/pull/4096) + [#4118](https://github.com/mixxxdj/mixxx/pull/4118) + [#4130](https://github.com/mixxxdj/mixxx/pull/4130) + [#4129](https://github.com/mixxxdj/mixxx/pull/4129) + [#4109](https://github.com/mixxxdj/mixxx/pull/4109) + [#4106](https://github.com/mixxxdj/mixxx/pull/4106) + [#4131](https://github.com/mixxxdj/mixxx/pull/4131) + [#4140](https://github.com/mixxxdj/mixxx/pull/4140) + [#3032](https://github.com/mixxxdj/mixxx/pull/3032) + [#4110](https://github.com/mixxxdj/mixxx/pull/4110) + [#4173](https://github.com/mixxxdj/mixxx/pull/4173) + [#4178](https://github.com/mixxxdj/mixxx/pull/4178) + [#4194](https://github.com/mixxxdj/mixxx/pull/4194) + [#4197](https://github.com/mixxxdj/mixxx/pull/4197) + [#4190](https://github.com/mixxxdj/mixxx/pull/4190) + [#4212](https://github.com/mixxxdj/mixxx/pull/4212) + [#4223](https://github.com/mixxxdj/mixxx/pull/4223) + [#4238](https://github.com/mixxxdj/mixxx/pull/4238) + [#4236](https://github.com/mixxxdj/mixxx/pull/4236) ## [2.3.1](https://launchpad.net/mixxx/+milestone/2.3.1) (Unreleased) @@ -169,7 +563,7 @@ * Enable CUE sheet recording by default [#3374](https://github.com/mixxxdj/mixxx/pull/3374) * Fix crash when double clicking GLSL waveforms with right mouse button [#3904](https://github.com/mixxxdj/mixxx/pull/3904) * Derive Mixxx version from `git describe` [#3824](https://github.com/mixxxdj/mixxx/pull/3824) [#3841](https://github.com/mixxxdj/mixxx/pull/3841) [#3848](https://github.com/mixxxdj/mixxx/pull/3848) -* Improve tapping the bpm of a deck [#3790](https://github.com/mixxxdj/mixxx/pull/3790) [lp:1882776](https://bugs.launchpad.net/mixxx/+bug/1882776) +* Improve tapping the BPM of a deck [#3790](https://github.com/mixxxdj/mixxx/pull/3790) [lp:1882776](https://bugs.launchpad.net/mixxx/+bug/1882776) * And countless other small fixes and improvements (too many to list them all!) ## [2.2.4](https://launchpad.net/mixxx/+milestone/2.2.4) (2020-06-27) @@ -223,7 +617,7 @@ * Fix playlist import. [lp:16878282](https://bugs.launchpad.net/mixxx/+bug/16878282) * Fix updating playlist labels. [lp:1837315](https://bugs.launchpad.net/mixxx/+bug/1837315) * Fix potential segfault on exit. [lp:1828360](https://bugs.launchpad.net/mixxx/+bug/1828360) -* Fix parsing of invalid bpm values in MP3 files. [lp:1832325](https://bugs.launchpad.net/mixxx/+bug/1832325) +* Fix parsing of invalid BPM values in MP3 files. [lp:1832325](https://bugs.launchpad.net/mixxx/+bug/1832325) * Fix crash when removing rows from empty model. [#2128](https://github.com/mixxxdj/mixxx/pull/2128) * Fix high DPI scaling of RGB overview waveforms. [#2090](https://github.com/mixxxdj/mixxx/pull/2090) * Fix for OpenGL SL detection on macOS. [lp:1828019](https://bugs.launchpad.net/mixxx/+bug/1828019) diff --git a/CMakeLists.txt b/CMakeLists.txt index f2c54d85bbd..113d12ae5ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1399,7 +1399,7 @@ if(UNIX AND NOT APPLE) # .metainfo.xml file for KDE/GNOME AppStream initiative install( FILES - "${CMAKE_CURRENT_SOURCE_DIR}/res/linux/mixxx.metainfo.xml" + "${CMAKE_CURRENT_SOURCE_DIR}/res/linux/org.mixxx.Mixxx.metainfo.xml" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/metainfo" ) diff --git a/res/linux/mixxx.metainfo.xml b/res/linux/mixxx.metainfo.xml deleted file mode 100644 index 859ec5753ef..00000000000 --- a/res/linux/mixxx.metainfo.xml +++ /dev/null @@ -1,95 +0,0 @@ - - - - org.mixxx.Mixxx - org.mixxx.Mixxx.desktop - CC-BY-SA-3.0 - GPL-2.0 - Mixxx DJ Software - Everything you need to perform live DJ mixes - -

- Mixxx is free DJ software that gives you everything you need to perform - live DJ mixes. Blend songs together with automatic BPM matching and - remix on-the-fly with looping and hot cues. - Whether you're a pro DJ or just getting started, Mixxx has you covered. -

-

- Mixxx works with ALSA, JACK, OSS and supports many popular DJ - controllers. Key features include: -

- -
- - - usb:v22F0p0008d* - usb:v1C75p* - usb:v1397p* - usb:v154Ep* - usb:v1157p* - usb:v23C7p* - usb:v06F8p* - usb:v15E4p* - usb:v0944p* - usb:v17CCp* - usb:v057Ep* - usb:v08E4p* - usb:v2B73p* - usb:v13E5p* - usb:v200Cp* - usb:v0582p* - usb:v054Cp* - - - - https://mixxx.org/theme/images/2.3/screenshots/latenight-palemoon-3840x2160.png - Mixxx with the default LateNight PaleMoon skin - - - https://mixxx.org/theme/images/2.3/screenshots/latenight-classic-3840x2160.png - Mixxx with the LateNight Classic skin - - - https://mixxx.org/theme/images/2.3/screenshots/deere-3840x2160.png - Mixxx with the Deere skin - - - https://mixxx.org/theme/images/2.3/screenshots/shade-classic-3840x2160.png - Mixxx with the Shade Classic skin - - - https://mixxx.org/theme/images/2.3/screenshots/shade-dark-3840x2160.png - Mixxx with the Shade Dark skin - - - https://mixxx.org/theme/images/2.3/screenshots/shade-summersunset-3840x2160.png - Mixxx with the Shade Summer Sunset skin - - - https://mixxx.org/theme/images/2.3/screenshots/tango-3840x2160.png - Mixxx with the Tango skin - - - https://mixxx.org - https://bugs.launchpad.net/mixxx - https://mixxx.org/donate - https://www.mixxx.org/support - https://www.transifex.com/mixxx-dj-software/public - mixxx-devel@lists.sourceforge.net -
diff --git a/res/linux/org.mixxx.Mixxx.metainfo.xml b/res/linux/org.mixxx.Mixxx.metainfo.xml new file mode 100644 index 00000000000..dc4bf34a1b2 --- /dev/null +++ b/res/linux/org.mixxx.Mixxx.metainfo.xml @@ -0,0 +1,2372 @@ + + + org.mixxx.Mixxx + org.mixxx.Mixxx.desktop + CC-BY-SA-3.0 + GPL-2.0 + Mixxx DJ Software + Everything you need to perform live DJ mixes + +

+ Mixxx is free DJ software that gives you everything you need to perform + live DJ mixes. Blend songs together with automatic BPM matching and + remix on-the-fly with looping and hot cues. + Whether you're a pro DJ or just getting started, Mixxx has you covered. +

+

+ Mixxx works with ALSA, JACK, OSS and supports many popular DJ + controllers. Key features include: +

+ +
+ + + usb:v22F0p0008d* + usb:v1C75p* + usb:v1397p* + usb:v154Ep* + usb:v1157p* + usb:v23C7p* + usb:v06F8p* + usb:v15E4p* + usb:v0944p* + usb:v17CCp* + usb:v057Ep* + usb:v08E4p* + usb:v2B73p* + usb:v13E5p* + usb:v200Cp* + usb:v0582p* + usb:v054Cp* + + + + https://mixxx.org/theme/images/2.3/screenshots/latenight-palemoon-3840x2160.png + Mixxx with the default LateNight PaleMoon skin + + + https://mixxx.org/theme/images/2.3/screenshots/latenight-classic-3840x2160.png + Mixxx with the LateNight Classic skin + + + https://mixxx.org/theme/images/2.3/screenshots/deere-3840x2160.png + Mixxx with the Deere skin + + + https://mixxx.org/theme/images/2.3/screenshots/shade-classic-3840x2160.png + Mixxx with the Shade Classic skin + + + https://mixxx.org/theme/images/2.3/screenshots/shade-dark-3840x2160.png + Mixxx with the Shade Dark skin + + + https://mixxx.org/theme/images/2.3/screenshots/shade-summersunset-3840x2160.png + Mixxx with the Shade Summer Sunset skin + + + https://mixxx.org/theme/images/2.3/screenshots/tango-3840x2160.png + Mixxx with the Tango skin + + + https://mixxx.org + https://bugs.launchpad.net/mixxx + https://mixxx.org/donate + https://www.mixxx.org/support + https://www.transifex.com/mixxx-dj-software/public + https://mixxx.zulipchat.com + + + + +

+ Cover Art +

+
    +
  • + Prevent wrong cover art display due to hash conflicts + #2524 +
  • +
  • + Add background color for quick cover art preview + #2524 +
  • +
+

+ Music Library +

+
    +
  • + Ensure that tracks with an invalid BPM are re-analyzed + #2776 +
  • +
  • + Add support for exporting crates, playlists and the library to Engine Prime and Denon standalone controllers + #2753 + #2932 + #3102 + #3155 + #3621 + #3776 + #3787 + #3797 + #3798 + #4025 + #4087 + #4102 + #4143 +
  • +
  • + Rekordbox: Save all loops and correct AAC timing offset for CoreAudio + #2779 +
  • +
  • + Improve log messages during schema migration + #2979 +
  • +
  • + Search related tracks in collection + #3181 + #3213 + #2796 + #4207 +
  • +
  • + Add recent searches to a drop down menu of the search box + #3171 + #3262 +
  • +
  • + Add new "[AutoDJ],add_random_track" to make this feature accessible from controllers + #3076 +
  • +
  • + Add new library column that shows the last time a track was played + #3140 + #3457 + #3494 + #3596 + #3740 +
  • +
  • + Improve presentation of the history library tree + #2996 +
  • +
  • + Don't store or update metadata of missing tracks in the Mixxx database to prevent inconsistencies with file tags + #3811 +
  • +
  • + Code improvements and minor bug fixes when importing track metadata + #3851 + #3858 + #3860 + #3866 + #3871 + #3870 + #3924 + #3906 + #3998 +
  • +
  • + Update library schema to 37 for synchronizing file modified time with track source on metadata import/export + #3978 + #4012 +
  • +
  • + Logging: Suppress expected and harmless schema migration errors + #4248 +
  • +
  • + Only show the date in Date Added / Last Played columns. Move the time of day to tooltips + #3945 +
  • +
  • + Fix handling of undefined BPM values + #4062 + #4063 + #4100 + #4154 + #4165 + #4168 +
  • +
  • + Add support for m4v files + #4088 +
  • +
  • + Adjust ReplayGain: Allow user to update the replaygain value based on a deck pregain value + #4031 +
  • +
  • + Automatic analyze and optimize database + #4199 +
  • +
  • + Fix playlists sidebar navigation/activation + #4193 + lp:1939082 +
  • +
  • + Refactoring of library code + #2756 + #2717 + #2715 + #2810 + #2900 + #2906 + #2925 + #3017 + #3475 + #4164 + #4152 + #4162 + #4101 + #4214 +
  • +
+

+ Sync +

+
    +
  • + Add support for setting an explicit leader for sync lock + #2768 + #3099 + #3695 + #3734 + #3698 + #3864 + #3867 + #3921 + #4119 + #4135 + #4149 +
  • +
  • + Fix issue with half/double BPM calculation when using sync + #3899 + #3706 +
  • +
  • + Sync Lock: Don't seek phase when disabling sync + #4169 +
  • +
+

+ Audio Codecs +

+
    +
  • + Fix recovering from FAAD2 decoding issues + #2850 +
  • +
+

+ Audio Engine +

+
    +
  • + Add support for Saved loops + #2194 + #3267 + #3202 +
  • +
  • + Fix an issue when pressing multiple cue buttons at the same time + #3382 +
  • +
  • + Fix synchronization of main cue point/position + #4137 + lp1937074 + #4153 +
  • +
  • + Refactoring of beatgrid/beatmap code + #4044 + #4048 + #4045 + #4049 + #4092 + #4094 + #4104 + #4103 + #4127 + #4099 + #4071 + #4184 + #4234 + #4233 + #4258 + #4259 + #4263 + #4272 +
  • +
  • + Refactoring of audio engine code + #2762 + #2801 + #2885 + #2997 + #3266 + #4064 + #4065 + #4066 + #4069 + #4074 + #4075 + #4076 + #4078 + #4082 + #4077 + #4080 + #4086 + #4089 + #4090 + #4079 + #4091 + #4083 + #4095 + #4081 + #4061 + #4105 + #4183 + #4186 + #4189 + #4216 + #4221 + #4219 + #4191 + #4232 + #4231 + #4229 + #4257 + #4266 + #4217 +
  • +
+

+ Controllers +

+
    +
  • + Never raise a fatal error if a controller mapping tries access a non-existent control object + #2947 +
  • +
  • + Update Novation Launchpad controller scripts + #2600 +
  • +
  • + Add generic USB HID "Set Reports (Feature)" functionality + #3051 +
  • +
  • + Add support for reading the status of an HID controller (like MIDI SYSEX) + #3317 +
  • +
  • + Use hidapi's hidraw backend instead of libusb on Linux + #4054 +
  • +
  • + Consistently use "mapping" instead of "preset" to refer to controller mappings + #3472 +
  • +
  • + Introduce new control objects + + [Master],indicator_250millis + + and + + [Master],indicator_500millis + + #4157 +
  • +
  • + Don't automatically enable controller if it was disabled before + #4244 + lp:1941042 +
  • +
  • + Roland DJ-505: Use new ControlIndicator COs for blinking lights + #4159 +
  • +
  • + Prepare code for upcoming ES6 based controller mapping system with module support + #2682 + #2868 + #2875 + #2936 + #2946 +
  • +
  • + Other refactorings of controller code + #2904 + #3308 + #3463 + #3634 + #3635 + #3636 + #3676 + #3880 + #4085 +
  • +
+

+ Skins +

+
    +
  • + Add experimental QML skin + #3345 + #3446 + #3854 + #3891 + #2874 + #3915 + #3894 + #3920 + #3907 + #3925 + #3928 + #3932 + #3911 + #3937 + #3940 + #3913 + #3950 + #3919 + #3955 + #3957 + #3961 + #3952 + #3963 + #3971 + #3959 + #3972 + #3992 + #4003 + #4004 + #3999 + #4000 + #4067 + #4068 + #4060 + #4037 + #3934 + #4117 +
  • +
  • + Add new "RGB Stacked" waveform + #3153 +
  • +
  • + Add harmonic keywheel window + #1695 + #3622 + #3624 +
  • +
  • + Make beat indicator control behaviour more natural + #3608 +
  • +
  • + Fix crash if no skin is available + #3918 + #3939 +
  • +
  • + Inverted scroll wheel waveform zoom direction to mach other applications + #4195 +
  • +
  • + Fix leaked controls + #4213 + lp:1912129 +
  • +
+

+ Other +

+
    +
  • + Improve/fix the build system + #2796 + #2937 + #2943 + #3041 + #3046 + #3114 + #3182 + #3274 + #3300 + #3471 + #3514 + #3765 + #3849 + #3876 + #3861 + #3923 + #3948 + #3929 + #4007 + #4070 + #4084 + #4098 + #4113 + #4163 + #4166 + #4185 + #4187 + #4192 + #4226 + #4203 + #4250 + #4274 +
  • +
  • + Drop Ubuntu Bionic support, require Qt 5.12 + #3687 + #3735 + #3736 + #3985 +
  • +
  • + Add NixOS support + #2820 + #2828 + #2836 + #2827 + #2827 + #2828 + #3113 + #3089 + #3545 +
  • +
  • + Add support for saving loops as hotcues + #2194 + #4265 + lp:1367159 +
  • +
  • + Make use of inclusive language + #2894 + #3868 +
  • +
  • + Add Noise effect + #2921 +
  • +
  • + Improve Unittests + #2938 + #2980 + #3006 +
  • +
  • + Logging: Add support for + + QT_MESSAGE_PATTERN + + environment variable + #3204 + #3518 +
  • +
  • + Colored logging console output + #3197 +
  • +
  • + Improve command line argument parser + #3640 + #3962 + #4022 + #4036 + #4170 + #4057 +
  • +
  • + Improve message when dealing with macOS sandbox + #4018 + lp:1921541 +
  • +
  • + Move contribution guidelines into our git repository + #2699 +
  • +
  • + Automize deployment of CHANGELOG to the manual + #4180 + #4256 + #4208 + #4228 + #4222 +
  • +
  • + Always show tooltips in preferences + #4198 + lp:1840493 +
  • +
  • + Allow to build Mixxx on Linux without ALSA, working around a Pipewire bug + #4242 +
  • +
  • + Fix possible crash with opus files with embedded cover arts and require TagLib 1.11 or newer + #4251 + #4252 + lp:1940777 +
  • +
  • + Misc. refactorings + #3154 + #2870 + #2872 + #2978 + #2969 + #3016 + #3320 + #3356 + #3453 + #3487 + #3558 + #3685 + #3741 + #3744 + #3753 + #3761 + #3834 + #3842 + #3853 + #3874 + #3883 + #3922 + #3947 + #3974 + #4024 + #4026 + #4034 + #4038 + #4039 + #4043 + #4053 + #4072 + #4097 + #4096 + #4118 + #4130 + #4129 + #4109 + #4106 + #4131 + #4140 + #3032 + #4110 + #4173 + #4178 + #4194 + #4197 + #4190 + #4212 + #4223 + #4238 + #4236 +
  • +
+
+
+ + +
    +
  • + Added mapping for the Numark DJ2GO Touch controller + #4108 +
  • +
  • + Added mapping for the Numark Mixtrack Pro FX controller + #4160 +
  • +
  • + Disabled detection of keyboards and mice as HID controllers + #4243 +
  • +
  • + Add support for HiDPI scale factors of 125% and 175% (only with Qt 5.14+) + lp1938102 + #4161 +
  • +
  • + Fix unhandled exception when parsing corrupt Rekordbox PDB files + lp1933853 + #4040 +
  • +
  • + Fix Echo effect adding left channel samples to right channel + #4141 +
  • +
  • + Fix bad phase seek when starting from preroll + lp1930143 + #4093 +
  • +
  • + Fix bad phase seek when a channel's audible status changes + #4156 +
  • +
  • + Tango skin: Show crossfader assign buttons by default + #4046 +
  • +
  • + Fix keyfinder library in arm64 builds + #4047 +
  • +
  • + Fix wrong track being recorded in History + lp1933991 + #4041 + #4059 + #4107 +
  • +
  • + Fix support for relative paths in the skin system which caused missing images in third-party skins + #4151 +
  • +
  • + Fix relocation of directories with special/reserved characters in path name + #4146 +
  • +
  • + Update keyboard shortcuts sheet + #4042 +
  • +
  • + Library: resize the Played checkbox and BPM lock with the library font + #4050 +
  • +
  • + Don't allow Input focus on waveforms + #4134 +
  • +
  • + Fix performance issue on AArch64 by enabling flush-to-zero for floating-point arithmetic + #4144 +
  • +
  • + Fix custom key notation not restored correctly after restart + #4136 +
  • +
  • + Traktor S3: Disable scratch when switching decks to prevent locked scratch issue + #4073 +
  • +
  • + FFmpeg: Ignore inaudible samples before start of stream + #4245 +
  • +
+

+ Packaging +

+
    +
  • + It is no longer necessary to manually copy the udev rule file in packaging scripts. Now pkg-config is used to determine the udevdir used to install the rules file in the CMake install step when CMAKE_INSTALL_PREFIX is + + / + + or + + /usr + + . + #4126 +
  • +
  • + Various build issues on FreeBSD are fixed + #4122 + #4123 + #4124 +
  • +
  • + .desktop file has be renamed to org.mixxx.Mixxx.desktop according to Freedesktop standards + #4206 +
  • +
  • + Uses system provided hidapi library if version >= 0.10.1 + #4215 +
  • +
  • + Please update PortAudio to + 19.7 + if you have not done so already. This is required for Mixxx to work with PipeWire via the JACK API for many devices. +
  • +
+
+
+ + +

+ Hotcues +

+
    +
  • + Add hotcue colors and custom labels by right clicking hotcue buttons or right clicking hotcues on overview waveforms + #2016 + #2520 + #2238 + #2560 + #2557 + #2362 +
  • +
  • + Mouse hover cues on overview waveform to show time remaining until the cue + #2238 +
  • +
+

+ Hotcue & Track Colors +

+
    +
  • + Add configurable color per track + #2470 + #2539 + #2545 + #2630 + lp:1100882 +
  • +
  • + Add customizable color palettes for hotcue and track colors + #2530 + #2589 + #3749 + #2902 +
  • +
  • + Add hotcue color find-and-replace tool + #2547 +
  • +
+

+ Importing From Other DJ Software +

+
    +
  • + Import cue points, track colors, and playlists from Serato file tags & database + #2480 + #2526 + #2499 + #2495 + #2673 + #3885 +
  • +
  • + Note: Mixxx does not yet support multiple loops per track. We are + working on this for Mixxx 2.4 + . In Mixxx 2.3, if you import a track with multiple loops from Serato, Mixxx will use the first loop cue as the single loop Mixxx currently supports. The imported loops are still stored in Mixxx's database and are treated as hotcues in Mixxx 2.3. If you do not delete these hotcues, they will be usable as loops in Mixxx 2.4. Serato keeps loops and hotcues in separate lists, but Mixxx does not, so loops from Serato are imported starting as hotcue 9. +
  • +
  • + Import cue points, track colors, and playlists from Rekordbox USB drives + #2119 + #2555 + #2543 + #2779 +
  • +
  • + Note: The first Rekordbox memory cue is imported for the main cue button in Mixxx and the remaining Rekordbox memory cues are imported as Mixxx hotcues, starting with the next hotcue number after the last hotcue from Rekordbox. +
  • +
  • + Note: Mixxx does not yet support multiple loops per track. Imported loops from Rekordbox are treated like imported loops from Serato, so refer to the note above for details. +
  • +
+

+ Intro & Outro Cues +

+
    +
  • + Add intro & outro range cues with automatic silence detection + #1242 +
  • +
  • + Show duration of intro & outro ranges on overview waveform + #2089 +
  • +
  • + Use intro & outro cues in AutoDJ transitions + #2103 +
  • +
+

+ Deck cloning +

+
    +
  • + Add deck cloning (also known as "instant doubles" in other DJ software) by dragging and dropping between decks + #1892 + and samplers + #3200 +
  • +
  • + Clone decks by double pressing the load button on a controller (with option to disable this) + #2024 + #2042 +
  • +
+

+ Skins & GUI +

+
    +
  • + Aesthetically revamped LateNight skin + #2298 + #2342 +
  • +
  • + Right click overview waveform to show time remaining until that point + #2238 +
  • +
  • + Show track info dialog when double clicking track labels in decks + #2990 +
  • +
  • + Show track context menu when right clicking text in decks + #2612 + #2675 + #2684 + #2696 +
  • +
  • + Add laptop battery widget to skins + #2283 + #2277 + #2250 + #2228 + #2221 + #2163 + #2160 + #2147 + #2281 + #2319 + #2287 +
  • +
  • + Show when passthrough mode is active on overview waveforms + #2575 + #2616 +
  • +
  • + Changed format of currently playing track in window title from "artist, title" to "artist - title" + #2807 +
  • +
  • + Workaround Linux skin change crash + #3144 + lp:1885009 +
  • +
  • + Fix touch control + lp:1895431 +
  • +
  • + Fix broken knob interaction on touchscreens + #3512 +
  • +
  • + AutoDJ: Make "enable" shortcut work after startup + #3242 +
  • +
  • + Add rate range indicator + #3693 +
  • +
  • + Allow menubar to be styled + #3372 + #3788 +
  • +
  • + Add Donate button to About dialog + #3838 + #3846 +
  • +
  • + Add Scrollable Skin Widget + #3890 +
  • +
  • + Fix minor visual issues in Skins + #3958 + #3954 + #3941 + #3938 + #3936 + #3886 + #3927 + #3844 + #3933 + #3835 + #3902 + #3931 +
  • +
+

+ Music Feature Analysis +

+
    +
  • + Multithreaded analysis for much faster batch analysis on multicore CPUs + #1624 + #2142 + lp:1641153 +
  • +
  • + Fix bugs affecting key detection accuracy + #2137 + #2152 + #2112 + #2136 +
  • +
  • + Note: Users who have not manually corrected keys are advised to clear all keys in their library by pressing Ctrl + A in the library, right clicking, going to Reset -> Key, then reanalyzing their library. This will freeze the GUI while Mixxx clears the keys; this is a known problem that we will not be able to fix for 2.3. Wait until it is finished and you will be able to reanalyze tracks for better key detection results. +
  • +
  • + Remove VAMP plugin support and use Queen Mary DSP library directly. vamp-plugin-sdk and vamp-hostsdk are no longer required dependencies. + #926 +
  • +
  • + Improvements BPM detection on non-const beatgrids + #3626 +
  • +
  • + Fix const beatgrid placement + #3965 + #3973 +
  • +
+

+ Music Library +

+
    +
  • + Add support for searching for empty fields (for example + + crate:"" + + ) + lp:1788086 +
  • +
  • + Improve synchronization of track metadata and file tags + #2406 +
  • +
  • + Library Scanner: Improve hashing of directory contents + #2497 +
  • +
  • + Rework of Cover Image Hashing + lp:1607097 + #2507 + #2508 +
  • +
  • + MusicBrainz: Handle 301 status response + #2510 +
  • +
  • + MusicBrainz: Add extended metadata support + lp:1581256 + #2522 +
  • +
  • + TagLib: Fix detection of empty or missing file tags + lp:1865957 + #2535 +
  • +
  • + Fix caching of duplicate tracks that reference the same file + #3027 +
  • +
  • + Use 6 instead of only 4 compatible musical keys (major/minor) + #3205 +
  • +
  • + Fix possible crash when trying to refocus the tracks table while another Mixxx window has focus + #3201 +
  • +
  • + Don't create new tags in file when exporting metadata to it + #3898 +
  • +
  • + Fix playlist files beginning with non-english characters not being loaded + #3916 +
  • +
  • + Enable sorting in "Hidden Tracks" and "Missing Tracks" views + #3828 + lp:1828555 + lp:1924616 +
  • +
  • + Fix track table being empty after start + #3935 + lp:1930546 + lp:1924843 +
  • +
+

+ Audio Codecs +

+
    +
  • + Add FFmpeg audio decoder, bringing support for ALAC files + #1356 +
  • +
  • + Include LAME MP3 encoder with Mixxx now that the MP3 patent has expired + lp:1294128 + buildserver:#37 + buildserver:9e8bcee +
  • +
  • + Add Opus streaming and recording support. + lp:1338413 +
  • +
  • + Remove support for SoundSource plugins because the code was not well-maintained and could lead to crashes + lp:1792747 +
  • +
  • + Add HE-AAC encoding capabilities for recording and broadcasting + #3615 +
  • +
+

+ Audio Engine +

+
    +
  • + Fix loss of precision when dealing with floating-point sample positions while setting loop out position and seeking using vinyl control + #3126 + #3127 +
  • +
  • + Prevent moving a loop beyond track end + #3117 + lp:1799574 +
  • +
  • + Fix possible memory corruption using JACK on Linux + #3160 +
  • +
  • + Fix changing of vinyl lead-in time + lp:1915483 + #3781 +
  • +
  • + Fix tempo change of non-const beatgrid track on audible deck when cueing another track + #3772 +
  • +
  • + Fix crash when changing effect unit routing + #3882 + lp:1775497 +
  • +
  • + Make microphone ducking use strength knob the same way in automatic & manual mode + #2750 +
  • +
+

+ Controllers +

+
    +
  • + Improve workflow for configuring controller mappings and editing mappings + #2569 + #3278 + #3667 +
  • +
  • + Improve error reporting from controller scripts + #2588 +
  • +
  • + Make hotcue and track colors mappable on controllers + #2030 + #2541 + #2665 + #2520 +
  • +
  • + Add way to change library table sorting from controllers + #2118 +
  • +
  • + Add support for velocity sensitive sampler buttons in Components JS library + #2032 +
  • +
  • + Add logging when script ControlObject callback is disconnected successfully + #2054 +
  • +
  • + Add controller mapping for Roland DJ-505 + #2111 +
  • +
  • + Add controller mapping for Numark iDJ Live II + #2818 +
  • +
  • + Add controller mapping for Hercules DJControl Inpulse 200 + #2542 +
  • +
  • + Add controller mapping for Hercules DJControl Jogvision + #2370 +
  • +
  • + Add controller mapping for Pioneer DDJ-200 + #3185 + #3193 + #3742 + #3793 + #3949 +
  • +
  • + Add controller mapping for Pioneer DDJ-400 + #3479 +
  • +
  • + Add controller mapping for ION Discover DJ Pro + #2893 +
  • +
  • + Add controller mapping for Native Instrument Traktor Kontrol S3 + #3031 +
  • +
  • + Add controller mapping for Behringer BCR2000 + #3342 + #3943 +
  • +
  • + Add controller mapping for Behringer DDM4000 + #3542 +
  • +
  • + Update controller mapping for Allen & Heath Xone K2 to add intro/outro cues + #2236 +
  • +
  • + Update controller mapping for Hercules P32 for more accurate headmix control + #3537 +
  • +
  • + Update controller mapping for Native Instruments Traktor Kontrol S4MK2 to add auto-slip mode and pitch fader range + #3331 +
  • +
  • + Fix Pioneer DDJ-SB2 controller mapping auto tempo going to infinity bug + #2559 + lp:1846403 +
  • +
  • + Fix Numark Mixtrack Pro 3 controller mapping inverted FX on/off control + #3758 +
  • +
  • + Gracefully handle MIDI overflow + #825 +
  • +
+

+ Other +

+
    +
  • + Add CMake build system with + + ccache + + and + + sccache + + support for faster compilation times and remove SCons + #2280 + #3618 +
  • +
  • + Make Mixxx compile even though + + QT_NO_OPENGL + + or + + QT_OPENGL_ES_2 + + is defined (fixes build on Raspberry Pi) + lp:1863440 + #2504 +
  • +
  • + Fix ARM build issues + #3602 +
  • +
  • + Fix missing manual in DEB package + lp:1889776 + #2985 +
  • +
  • + Add macOS codesigning and notarization to fix startup warnings + #3281 +
  • +
  • + Don't trash user configuration if an error occurs when writing + #3192 +
  • +
  • + Enable CUE sheet recording by default + #3374 +
  • +
  • + Fix crash when double clicking GLSL waveforms with right mouse button + #3904 +
  • +
  • + Derive Mixxx version from + + git describe + + #3824 + #3841 + #3848 +
  • +
  • + Improve tapping the BPM of a deck + #3790 + lp:1882776 +
  • +
  • + And countless other small fixes and improvements (too many to list them all!) +
  • +
+
+
+ + +
    +
  • + Store default recording format after "Restore Defaults" + lp:1857806 + #2414 +
  • +
  • + Prevent infinite loop when decoding corrupt MP3 files + #2417 +
  • +
  • + Add workaround for broken libshout versions + #2040 + #2438 +
  • +
  • + Speed up purging of tracks + lp:1845837 + #2393 +
  • +
  • + Don't stop playback if vinyl passthrough input is configured and PASS button is pressed + #2474 +
  • +
  • + Fix debug assertion for invalid crate names + lp:1861431 + #2477 +
  • +
  • + Fix crashes when executing actions on tracks that already disappeared from the DB + #2527 +
  • +
  • + AutoDJ: Skip next track when both deck are playing + lp:1399974 + #2531 +
  • +
  • + Tweak scratch parameters for Mixtrack Platinum + #2028 +
  • +
  • + Fix auto tempo going to infinity on Pioneer DDJ-SB2 + #2559 +
  • +
  • + Fix bpm.tapButton logic and reject missed & double taps + #2594 +
  • +
  • + Add controller mapping for Native Instruments Traktor Kontrol S2 MK3 + #2348 +
  • +
  • + Add controller mapping for Soundless joyMIDI + #2425 +
  • +
  • + Add controller mapping for Hercules DJControl Inpulse 300 + #2465 +
  • +
  • + Add controller mapping for Denon MC7000 + #2546 +
  • +
  • + Add controller mapping for Stanton DJC.4 + #2607 +
  • +
  • + Fix broadcasting via broadcast/recording input + lp:1876222 + #2743 +
  • +
  • + Only apply ducking gain in manual ducking mode when talkover is enabed + lp:1394968 + lp:1737113 + lp:1662536 + #2759 +
  • +
  • + Ignore MIDI Clock Messages (0xF8) because they are not usable in Mixxx and inhibited the screensaver + #2786 +
  • +
+
+
+ + +
    +
  • + Don't make users reconfigure sound hardware when it has not changed + #2253 +
  • +
  • + Fix MusicBrainz metadata lookup + lp:1848887 + #2328 +
  • +
  • + Fix high DPI scaling of cover art + #2247 +
  • +
  • + Fix high DPI scaling of cue point labels on scrolling waveforms + #2331 +
  • +
  • + Fix high DPI scaling of sliders in Tango skin + #2318 +
  • +
  • + Fix sound dropping out during recording + lp:1842679 + #2265 + #2305 + #2308 + #2309 +
  • +
  • + Fix rare crash on application shutdown + #2293 +
  • +
  • + Workaround various rare bugs caused by database inconsistencies + lp:1846971 + #2321 +
  • +
  • + Improve handling of corrupt FLAC files + #2315 +
  • +
  • + Don't immediately jump to loop start when loop_out is pressed in quantized mode + lp:1837077 + #2269 +
  • +
  • + Preserve order of tracks when dragging and dropping from AutoDJ to playlist + lp:1829601 + #2237 +
  • +
  • + Explicitly use X11 Qt platform plugin instead of Wayland in .desktop launcher + lp:1850729 + #2340 +
  • +
  • + Pioneer DDJ-SX: fix delayed sending of MIDI messages with low audio buffer sizes + #2326 +
  • +
  • + Enable modplug support on Linux by default + lp:1840537 + #2244 + #2272 +
  • +
  • + Fix keyboard shortcut for View > Skin Preferences + lp:1851993 + #2358 + #2372 +
  • +
  • + Reloop Terminal Mix: Fix mapping of sampler buttons 5-8 + lp:1846966 + #2330 +
  • +
+
+
+ + +
    +
  • + Fix battery widget with upower <= 0.99.7. + #2221 +
  • +
  • + Fix BPM adjust in BpmControl. + lp:1836480 +
  • +
  • + Disable track metadata export for .ogg files and TagLib 1.11.1. + lp:1833190 +
  • +
  • + Fix interaction of hot cue buttons and looping. + lp:1778246 +
  • +
  • + Fix detection of moved tracks. + #2197 +
  • +
  • + Fix playlist import. + lp:16878282 +
  • +
  • + Fix updating playlist labels. + lp:1837315 +
  • +
  • + Fix potential segfault on exit. + lp:1828360 +
  • +
  • + Fix parsing of invalid BPM values in MP3 files. + lp:1832325 +
  • +
  • + Fix crash when removing rows from empty model. + #2128 +
  • +
  • + Fix high DPI scaling of RGB overview waveforms. + #2090 +
  • +
  • + Fix for OpenGL SL detection on macOS. + lp:1828019 +
  • +
  • + Fix OpenGL ES detection. + lp:1825461 +
  • +
  • + Fix FX1/2 buttons missing Mic unit in Deere (64 samplers). + lp:1837716 +
  • +
  • + Tango64: Re-enable 64 samplers. + #2223 +
  • +
  • + Numark DJ2Go re-enable note-off for deck A cue button. + #2087 +
  • +
  • + Replace Flanger with QuickEffect in keyboard mapping. + #2233 +
  • +
+
+
+ + +
    +
  • + Include all fixes from Mixxx 2.1.7 and 2.1.8 +
  • +
  • + Fix high CPU usage on MAC due to preview column + lp:1812763 +
  • +
  • + Fix HID controller output on Windows with common-hid-packet-parser.js +
  • +
  • + Fix rendering slow down by not using QStylePainter in WSpinny + lp:1530720 +
  • +
  • + Fix broken Mic mute button + lp:1782568 +
  • +
  • + added quick effect enable button to the control picker menu +
  • +
  • + Fix Cover Window close issue with empty cover arts +
  • +
  • + Fix Numark Mixtrack 3 mapping. + #2057 +
  • +
+
+
+ + +

+ General +

+
    +
  • + Update from Qt4 to Qt5. +
  • +
  • + Use Qt5's automatic high DPI scaling (and remove the old + scaling option from the preferences). +
  • +
  • + Vectorize remaining raster graphics for better HiDPI support. +
  • +
+

+ Effects +

+
    +
  • + Add mix mode switch (Dry/Wet vs Dry+Wet) for effect units. +
  • +
  • + Add support for LV2 effects plugins (currently no way to show plugin GUIs). +
  • +
  • + Add preference option for selecting which effects are shown in the + list of available effects in the main window (all LV2 effects are + hidden by default and must be explicitly enabled by users). +
  • +
+

+ Skins +

+
    +
  • + Add 8 sampler and small sampler options to LateNight. +
  • +
  • + Add key / BPM expansion indicators to Deere decks. +
  • +
  • + Add skin settings menu to LateNight. +
  • +
+

+ Controllers +

+
    +
  • + Add controller mapping for Numark Mixtrack Platinum. +
  • +
  • + Update controller mapping for Numark N4. +
  • +
  • + Add spinback and break for Vestax VCI-400 mapping. +
  • +
+

+ Miscellaneous +

+
    +
  • + Add preference option to adjust the play position marker of + scrolling waveforms. +
  • +
  • + Add preference option to adjust opacity of beatgrid markers on + scrolling waveforms. +
  • +
  • + Support IRC/AIM/ICQ broadcast metadata. +
  • +
+
+
+ + +
    +
  • + Fix a rare chance for a corrupt track file while writing metadata in out of disk situations. + lp:1815305 +
  • +
  • + Fix export of BPM track file metadata. + lp:1816490 +
  • +
  • + Fix sending of broadcast metadata with TLS enabled libshout 2.4.1. + lp:1817395 +
  • +
  • + Fix resdicovering purged tracks in all cases. + lp:1821514 +
  • +
  • + Fix dropping track from OSX Finder. + lp:1822424 +
  • +
+
+
+ + +
    +
  • + Fix syncing to doublespeed + lp:1808697 +
  • +
  • + Fix issues when changing beats of a synced track + lp:1808698 +
  • +
  • + Fix direction of pitch bend buttons when inverting rate slider + lp:1770745 +
  • +
  • + Use first loaded deck if no playing deck is found + lp:1784185 +
  • +
  • + Encode file names correctly on macOS + lp:1776949 +
  • +
+
+
+ + +
    +
  • + Fix crash when loading a Qt5 Soundsource / Vamp Plug-In. + lp:1774639 +
  • +
  • + Validate effect parameter range. + lp:1795234 +
  • +
  • + Fix crash using the bpm_tap button without a track loaded. + lp:1801844 +
  • +
  • + Fix possible crash after ejecting a track. + lp:1801874 +
  • +
  • + Fix wrong bitrate reported for faulty mp3 files. + lp:1782912 +
  • +
  • + Fix Echo effect syncing + lp:1793232 +
  • +
  • + Fix iTunes context menu + lp:1799932 +
  • +
  • + Fix loading the wrong track after delete search and scroll. + lp:1803148 +
  • +
  • + Improve search bar timing. + lp:1635087 +
  • +
  • + Fix quoted search sentence. + lp:1784141 +
  • +
  • + Fix loading a track formerly not existing. + lp:1800395 +
  • +
  • + Fix importing m3u files with blank lines. + lp:1806271 +
  • +
  • + Fix position in sampler overview waveforms. + lp:1744170 +
  • +
  • + Don't reset rate slider, syncing a track without a beatgrid. + lp:1783020 +
  • +
  • + Clean up iTunes track context menu. + lp:1800335 +
  • +
  • + Collapsed sampler are not analyzed on startup. + lp:1801126 +
  • +
  • + search for decoration characters like "Ëš". + lp:1802730 +
  • +
  • + Fix cue button blinking after pressing eject on an empty deck. + lp:1808222 +
  • +
+
+
+ + +
    +
  • + Code signing for Windows builds. + lp:1517823 +
  • +
  • + Fix crash on exit when preferences is open. + lp:1793185 +
  • +
  • + Fix crash when analyzing corrupt MP3s. + lp:1793387 +
  • +
  • + Fix crash when importing metadata from MusicBrainz. + lp:1794993 +
  • +
  • + Library search fixes when single quotes are used. + lp:1784090 + lp:1789728 +
  • +
  • + Fix scrolling waveform on Windows with WDM-KS sound API. + lp:1729345 +
  • +
  • + Fix right clicking on beatgrid alignment button in Tango and LateNight skins. + lp:1798237 +
  • +
  • + Improve speed of importing iTunes library. + lp:1785545 +
  • +
  • + Add 2 deck mapping for DJTechTools MIDI Fighter Twister. +
  • +
+
+
+ + +

+ Fix track selection not getting shown in the track +table on Windows. There are no changes to the +source code, but the Jenkins build configuration +was changed to delete the Jenkins workspace before +each build. + lp:1751482 +

+
+
+ + +

+ Fix a severe performance regression on Windows: + Mixxx 2.1.2 running much slower than 2.1.1 +

+
+
+ + +

+ Yet another bugfix release of Mixxx 2.1. +Here is a quick summary of what is new in Mixxx 2.1.2: +

+
    +
  • + Allow maximum deck speed of 4x normal. +
  • +
  • + Don't always quantize hotcues, a 2.1.1 regression. + lp:1777429 +
  • +
  • + Fix artifacts using more than 32 samplers. + lp:1779559 +
  • +
  • + store No EQ and Filter persistently. + lp:1780479 +
  • +
  • + Pad unreadable samples with silence on cache miss. + lp:1777480 +
  • +
  • + Fixing painting of preview column for Qt5 builds. + lp:1776555 +
  • +
  • + LateNight: Fix play button right click. + lp:1781829 +
  • +
  • + LateNight: Added missing sort up/down buttons. +
  • +
  • + Fix sampler play button tooltips. + lp:1779468 +
  • +
  • + Shade: remove superfluid margins and padding in sampler.xml. + lp:1773588 +
  • +
  • + Deere: Fix background-color code. +
  • +
  • + ITunes: Don't stop import in case of duplicated Playlists. + lp:1783493 +
  • +
+
+
+ + +

+ After two months it is time to do a bugfix release of Mixxx 2.1. +Here is a quick summary of what is new in Mixxx 2.1.1: +

+
    +
  • + Require Soundtouch 2.0 to avoid segfault. + lp:1577042 +
  • +
  • + Improved skins including library view fix. + lp:1773709 + lp:1772202 + lp:1763953 +
  • +
  • + Fix crash when importing ID3v2 APIC frames. + lp:1774790 +
  • +
  • + Synchronize execution of Vamp analyzers. + lp:1743256 +
  • +
  • + DlgTrackInfo: Mismatching signal/slot connection. +
  • +
  • + Detect M4A decoding errors on Windows. + lp:1766834 +
  • +
  • + Fix spinback inertia effect. +
  • +
  • + Fix decoding fixes and upgrade DB schema. + lp:1766042 + lp:1769717 +
  • +
  • + Fix integration of external track libraries. + lp:1766360 +
  • +
  • + Fix memory leak when loading cover art. + lp:1767068 +
  • +
  • + Fix clearing of ReplayGain gain/ratio in file tags. + lp:1766094 +
  • +
  • + Fix crash when removing a quick link. + lp:1510068 +
  • +
  • + Fidlib: Thread-safe and reentrant generation of filters. + lp:1765210 +
  • +
  • + Fix unresponsive scrolling through crates & playlists using encoder. + lp:1719474 +
  • +
  • + Swap default values for temp/perm rate changes. + lp:1764254 +
  • +
+
+
+ + +

+ After two years of hard work, we are pleased to announce Mixxx 2.1. We +have overhauled the effects system, redesigned the skins, added and improved +lots of controller mappings, rewrote the audio file decoders twice, and of +course fixed a bunch of bugs. Download it! +

+

+ Here is a quick summary of what is new in Mixxx 2.1.0: +

+
    +
  • + Graphical interface scales for high resolution screens +
  • +
  • + Overhauled Deere and LateNight skins +
  • +
  • + New Tango skin +
  • +
  • + Effects are synchronized to the tempo +
  • +
  • + Effects are processed post-fader and post-crossfader and can be previewed in headphones +
  • +
  • + One metaknob per effect with customizable parameter control for intuitive use of effect chains +
  • +
  • + Nine new effects: Autopan, Biquad Equalizer, Biquad Full Kill Equalizer, Loudness Contour, Metronome, Parametric Equalizer, Phaser, Stereo Balance, Tremolo +
  • +
  • + Loaded effects and their parameters are saved and restored when Mixxx restarts +
  • +
  • + More transparent sounding equalizers (Biquad Equalizer and Biquad Full Kill Equalizer) +
  • +
  • + Improved scratching sounds with jog wheels, vinyl control, and dragging waveforms with the mouse +
  • +
  • + Simplified looping and beatjump controls +
  • +
  • + Configurable rows of 8 samplers with up to 8 rows available for a total of 64 samplers +
  • +
  • + Files loaded to samplers are reloaded when Mixxx restarts +
  • +
  • + Improved volume normalization algorithm (EBU-R 128) +
  • +
  • + Filter library table by crates +
  • +
  • + Sort musical keys in library table by circle of fifths +
  • +
  • + Write metadata tags back to audio files +
  • +
  • + New JavaScript library for controller mapping +
  • +
  • + Configure multiple Internet broadcasting stations and use multiple stations at the same time +
  • +
  • + Broadcast and record microphones with direct monitoring and latency compensation +
  • +
  • + Broadcast and record from an external mixer +
  • +
  • + Booth output with independent gain knob for using sound cards with 6 output channels without an external mixer +
  • +
  • + Prevent screensaver from starting while Mixxx is running +
  • +
  • + CUP (Cue And Play) cue button mode +
  • +
  • + Time remaining and time elapsed now take into account the tempo fader +
  • +
  • + Clicking cover art now shows it full size in a separate window +
  • +
  • + and of course, lots and lots of bug fixes. +
  • +
+

+ Here are controllers with mappings that have been added or updated since the 2.0 +release. Mappings marked with an asterisk (*) have been updated for the new +effects interface: +

+
    +
  • + American Audio VMS2 +
  • +
  • + American Audio VMS4 +
  • +
  • + Allen & Heath Xone K2/K1* +
  • +
  • + Behringer CMD Micro +
  • +
  • + Behringer CMD MM1* +
  • +
  • + Behringer CMD Studio 4a +
  • +
  • + Denon MC4000* +
  • +
  • + Denon MC6000 Mk2* +
  • +
  • + FaderFox DJ2 +
  • +
  • + Hercules DJ Console 4-Mx* +
  • +
  • + Hercules DJ Control MP3 LE / Glow +
  • +
  • + Hercules DJ Control Compact +
  • +
  • + Hercules P32* +
  • +
  • + Ion Discover DJ +
  • +
  • + Korg Nanokontrol 2 +
  • +
  • + Korg KAOSS DJ +
  • +
  • + M-Audio Xponent +
  • +
  • + Native Instruments Traktor Kontrol S4 Mk2* +
  • +
  • + Novation Launchpad Mk1 & Mk2 +
  • +
  • + Novation Twitch +
  • +
  • + Numark Mixtrack Pro 3 & Numark Mixtrack 3* +
  • +
  • + Pioneer DDJ-SB2* +
  • +
  • + Pioneer DDJ-SX* +
  • +
  • + Reloop Beatmix 2 +
  • +
  • + Reloop Beatmix 4 +
  • +
  • + Reloop Digital Jockey 3 ME +
  • +
  • + Reloop Terminal Mix 2 +
  • +
  • + Reloop Terminal Mix 4 +
  • +
  • + Vestax VCI-100 Mk2 +
  • +
  • + Vestax Typhoon +
  • +
+

+ For users upgrading from older versions of Mixxx, we have a few important +announcements. First, if you are using Windows, you will have to uninstall any +old versions of Mixxx before you can install 2.1. How to uninstall Mixxx +varies on different versions of Windows: +

+
    +
  • + Windows Vista, 7, and 8: + Start > Control Panel > Programs > Uninstall a + Program +
  • +
  • + Windows 10: + Start > Control Panel > Programs > Programs And Features > + look for Mixxx > Uninstall +
  • +
+

+ If you are upgrading from an older version of Mixxx and have MP3 files in +your library, we have another important announcement. The good news is that we +fixed a bug where the waveforms and audio playback of MP3 files were +misaligned. The bad news is that we have no way of knowing which MP3 files were +affected or how much the offset was. That means that waveforms, beatgrids, +cues, and loops from older versions of Mixxx may be offset by an unknown amount +for any MP3 file. Only MP3 files were affected by this bug; other audio file +types are unaffected. You can either correct your beatgrids and cue points +manually for each track, or you can clear this information for all MP3s and +start fresh. Regardless, we recommend clearing the waveforms for all MP3 +files. To clear these, type "location:mp3" into the library search bar, press +Control + A to select all tracks, right click, and select the information you +want to clear from the menu. +

+

+ In the works for Mixxx 2.2, we have a big redesign of the library GUI. Along +with that will come saving & restoring search queries plus nested crates. +We are also planning on adding support for saving and loading custom effect +chain presets with the ability to import and export them to share online. +

+

+ Want to help make Mixxx even more awesome? The biggest thing we need is more +people. You do not need to be a programmer to help out. Giving feedback on the +design of new features as they are being made is very valuable. Refer to the +Testing page on the wiki for more information on how to get involved with that. +Reporting bugs and telling us your ideas on the Launchpad bug tracker is a big +help too! We cannot fix problems we do not know about, so please let us know if +you find any issues with Mixxx. If you would like to help translate Mixxx into +another language, refer to the Internationalization wiki page. Of course, more +programmers could always help. Read the Developer Documentation on the wiki for +tips on getting started contributing code to Mixxx. +

+

+ We hope you have as much fun with Mixxx as we do! +

+

+ For a full list of new features and bugfixes, check out the + 2.1.0 milestone on Launchpad + . +

+
+
+ + +
    +
  • + 4 Decks with Master Sync +
  • +
  • + New Effects Framework with 4 Effect Units and 5 Built-in Effects: +
  • +
  • + Flanger, Bit Crusher, Reverb, Echo, Filter +
  • +
  • + More to come! +
  • +
  • + Configurable, Resizeable User Interface with 3 Brand New Skins +
  • +
  • + Cover Art Display +
  • +
  • + Music Key Detection and Shifting +
  • +
  • + Vinyl Audio Pass-Through +
  • +
  • + 4 Microphone inputs and 4 Auxiliary inputs +
  • +
  • + MIDI Mapping GUI and Improved Learning Wizard +
  • +
  • + MusicBrainz metadata fetching +
  • +
  • + RGB Musical Waveforms +
  • +
  • + Hundreds of Bug Fixes and Improvements +
  • +
  • + New Pitch-Independent Algorithm for Better-Sounding Key-lock +
  • +
+

+ For a full list of new features and bugfixes, check out the + 2.0.0 milestone on Launchpad + . +

+
+
+
+
diff --git a/src/analyzer/analyzerbeats.cpp b/src/analyzer/analyzerbeats.cpp index 5684b1ebf87..4d53dc98b2f 100644 --- a/src/analyzer/analyzerbeats.cpp +++ b/src/analyzer/analyzerbeats.cpp @@ -10,8 +10,6 @@ #include "analyzer/plugins/analyzersoundtouchbeats.h" #include "library/rekordbox/rekordboxconstants.h" #include "track/beatfactory.h" -#include "track/beatgrid.h" -#include "track/beatmap.h" #include "track/beatutils.h" #include "track/track.h" @@ -235,7 +233,7 @@ void AnalyzerBeats::storeResults(TrackPointer pTrack) { } else { mixxx::Bpm bpm = m_pPlugin->getBpm(); qDebug() << "AnalyzerBeats plugin detected constant BPM: " << bpm; - pBeats = mixxx::BeatGrid::makeBeatGrid(m_sampleRate, bpm, mixxx::audio::kStartFramePos); + pBeats = mixxx::Beats::fromConstTempo(m_sampleRate, mixxx::audio::kStartFramePos, bpm); } pTrack->trySetBeats(pBeats); diff --git a/src/controllers/hid/hiddevice.h b/src/controllers/hid/hiddevice.h index 38281c8cd81..7fd820601be 100644 --- a/src/controllers/hid/hiddevice.h +++ b/src/controllers/hid/hiddevice.h @@ -16,7 +16,9 @@ constexpr unsigned short kGenericDesktopUsagePage = 0x01; constexpr unsigned short kGenericDesktopMouseUsage = 0x02; constexpr unsigned short kGenericDesktopKeyboardUsage = 0x06; +// Apple has two two different vendor IDs which are used for different devices. constexpr unsigned short kAppleVendorId = 0x5ac; +constexpr unsigned short kAppleIncVendorId = 0x004c; /// Detached copy of `struct hid_device_info`. /// diff --git a/src/controllers/hid/hidenumerator.cpp b/src/controllers/hid/hidenumerator.cpp index e6f1a9bdc41..d22c96f3023 100644 --- a/src/controllers/hid/hidenumerator.cpp +++ b/src/controllers/hid/hidenumerator.cpp @@ -24,7 +24,8 @@ bool recognizeDevice(const hid_device_info& device_info) { // these devices in future computers and none of these devices are DJ controllers, // so skip all Apple HID devices rather than maintaining a list of specific devices // to skip. - if (device_info.vendor_id == mixxx::hid::kAppleVendorId) { + if (device_info.vendor_id == mixxx::hid::kAppleVendorId + || device_info.vendor_id == mixxx::hid::kAppleIncVendorId) { return false; } diff --git a/src/database/schemamanager.cpp b/src/database/schemamanager.cpp index 8fffd33000e..a17dc47e9a3 100644 --- a/src/database/schemamanager.cpp +++ b/src/database/schemamanager.cpp @@ -238,9 +238,7 @@ SchemaManager::Result SchemaManager::upgradeToSchemaVersion( FwdSqlQuery query(m_settingsDao.database(), statement); result = query.isPrepared() && query.execPrepared(); if (!result && - query.hasError() && - query.lastError().databaseText().startsWith( - QStringLiteral("duplicate column name: "))) { + query.hasDuplicateColumnNameError()) { // New columns may have already been added during a previous // migration to a different (= preceding) schema version. This // is a very common situation during development when switching @@ -249,10 +247,10 @@ SchemaManager::Result SchemaManager::upgradeToSchemaVersion( // handle those errors here after they occurred. // If the remaining migration finishes without other errors this // is probably ok. - kLogger.warning() - << "Ignoring failed statement" + kLogger.info() + << "Safely ignoring failed statement" << statement - << "and continuing with schema migration"; + << "while re-applying a schema migration"; result = true; } } diff --git a/src/engine/controls/bpmcontrol.cpp b/src/engine/controls/bpmcontrol.cpp index 2a6160637ef..7c161341f6d 100644 --- a/src/engine/controls/bpmcontrol.cpp +++ b/src/engine/controls/bpmcontrol.cpp @@ -723,8 +723,7 @@ mixxx::audio::FramePos BpmControl::getNearestPositionInPhase( return thisPosition; } - const auto otherPosition = mixxx::audio::FramePos::fromEngineSamplePos( - pOtherEngineBuffer->getExactPlayPos()); + const auto otherPosition = pOtherEngineBuffer->getExactPlayPos(); if (!BpmControl::getBeatContext(otherBeats, otherPosition, nullptr, @@ -909,8 +908,7 @@ mixxx::audio::FramePos BpmControl::getBeatMatchPosition( return thisPosition; } - const auto otherPosition = mixxx::audio::FramePos::fromEngineSamplePos( - pOtherEngineBuffer->getExactPlayPos()); + const auto otherPosition = pOtherEngineBuffer->getExactPlayPos(); const mixxx::audio::SampleRate thisSampleRate = m_pBeats->getSampleRate(); // Seek our next beat to the other next beat near our beat. diff --git a/src/engine/controls/enginecontrol.cpp b/src/engine/controls/enginecontrol.cpp index 26d3c11fd4e..21c8635f25a 100644 --- a/src/engine/controls/enginecontrol.cpp +++ b/src/engine/controls/enginecontrol.cpp @@ -85,13 +85,13 @@ void EngineControl::setLoop(mixxx::audio::FramePos startPosition, void EngineControl::seekAbs(mixxx::audio::FramePos position) { if (m_pEngineBuffer) { - m_pEngineBuffer->slotControlSeekAbs(position.toEngineSamplePos()); + m_pEngineBuffer->seekAbs(position); } } void EngineControl::seekExact(mixxx::audio::FramePos position) { if (m_pEngineBuffer) { - m_pEngineBuffer->slotControlSeekExact(position.toEngineSamplePos()); + m_pEngineBuffer->seekExact(position); } } diff --git a/src/engine/enginebuffer.cpp b/src/engine/enginebuffer.cpp index 786369ce5ed..1f584a54cdf 100644 --- a/src/engine/enginebuffer.cpp +++ b/src/engine/enginebuffer.cpp @@ -45,6 +45,13 @@ namespace { const mixxx::Logger kLogger("EngineBuffer"); +// This value is used to make sure the initial seek after loading a track is +// not omitted. Therefore this value must be different for 0.0 or any likely +// value for the main cue +constexpr auto kInitialPlayPosition = + mixxx::audio::FramePos::fromEngineSamplePos( + std::numeric_limits::lowest()); + constexpr double kLinearScalerElipsis = 1.00058; // 2^(0.01/12): changes < 1 cent allows a linear scaler @@ -69,7 +76,7 @@ EngineBuffer::EngineBuffer(const QString& group, m_pKeyControl(nullptr), m_pReadAheadManager(nullptr), m_pReader(nullptr), - m_filepos_play(kInitalSamplePosition), + m_playPosition(kInitialPlayPosition), m_speed_old(0), m_tempo_ratio_old(1.), m_scratching_old(false), @@ -77,8 +84,8 @@ EngineBuffer::EngineBuffer(const QString& group, m_pitch_old(0), m_baserate_old(0), m_rate_old(0.), - m_trackSamplesOld(0), - m_dSlipPosition(0.), + m_trackEndPositionOld(mixxx::audio::kInvalidFramePos), + m_slipPosition(mixxx::audio::kStartFramePos), m_dSlipRate(1.0), m_bSlipEnabledProcessing(false), m_pRepeat(nullptr), @@ -93,6 +100,9 @@ EngineBuffer::EngineBuffer(const QString& group, m_pCrossfadeBuffer(SampleUtil::alloc(MAX_BUFFER_LEN)), m_bCrossfadeReady(false), m_iLastBufferSize(0) { + // This should be a static assertion, but isValid() is not constexpr. + DEBUG_ASSERT(kInitialPlayPosition.isValid()); + m_queuedSeek.setValue(kNoQueuedSeek); // zero out crossfade buffer @@ -452,32 +462,30 @@ void EngineBuffer::readToCrossfadeBuffer(const int iBufferSize) { // (Must be called only once per callback) m_pScale->scaleBuffer(m_pCrossfadeBuffer, iBufferSize); // Restore the original position that was lost due to scaleBuffer() above - m_pReadAheadManager->notifySeek(m_filepos_play); + m_pReadAheadManager->notifySeek(m_playPosition); m_bCrossfadeReady = true; } } void EngineBuffer::seekCloneBuffer(EngineBuffer* pOtherBuffer) { - const auto position = mixxx::audio::FramePos::fromEngineSamplePos( - pOtherBuffer->getExactPlayPos()); - doSeekPlayPos(position, SEEK_EXACT); + doSeekPlayPos(pOtherBuffer->getExactPlayPos(), SEEK_EXACT); } // WARNING: This method is not thread safe and must not be called from outside // the engine callback! -void EngineBuffer::setNewPlaypos(double newpos) { +void EngineBuffer::setNewPlaypos(mixxx::audio::FramePos position) { if (kLogger.traceEnabled()) { - kLogger.trace() << m_group << "EngineBuffer::setNewPlaypos" << newpos; + kLogger.trace() << m_group << "EngineBuffer::setNewPlaypos" << position; } - m_filepos_play = newpos; + m_playPosition = position; if (m_rate_old != 0.0) { // Before seeking, read extra buffer for crossfading // this also sets m_pReadAheadManager to newpos readToCrossfadeBuffer(m_iLastBufferSize); } else { - m_pReadAheadManager->notifySeek(m_filepos_play); + m_pReadAheadManager->notifySeek(m_playPosition); } m_pScale->clear(); @@ -485,9 +493,8 @@ void EngineBuffer::setNewPlaypos(double newpos) { m_iSamplesSinceLastIndicatorUpdate = 1000000; // Must hold the engineLock while using m_engineControls - const auto playPosition = mixxx::audio::FramePos::fromEngineSamplePos(m_filepos_play); for (const auto& pControl: qAsConst(m_engineControls)) { - pControl->notifySeek(playPosition); + pControl->notifySeek(m_playPosition); } verifyPlay(); // verify or update play button and indicator @@ -520,7 +527,7 @@ void EngineBuffer::slotTrackLoading() { // Set play here, to signal the user that the play command is adopted m_playButton->set((double)m_bPlayAfterLoading); - m_pTrackSamples->set(0); // Stop renderer + setTrackEndPosition(mixxx::audio::kInvalidFramePos); // Stop renderer } void EngineBuffer::loadFakeTrack(TrackPointer pTrack, bool bPlay) { @@ -546,7 +553,7 @@ void EngineBuffer::slotTrackLoaded(TrackPointer pTrack, m_pause.lock(); m_visualPlayPos->setInvalid(); - m_filepos_play = kInitalSamplePosition; // for execute seeks to 0.0 + m_playPosition = kInitialPlayPosition; // for execute seeks to 0.0 m_pCurrentTrack = pTrack; m_pTrackSamples->set(iTrackNumSamples); m_pTrackSampleRate->set(iTrackSampleRate); @@ -555,7 +562,7 @@ void EngineBuffer::slotTrackLoaded(TrackPointer pTrack, // Reset slip mode m_pSlipButton->set(0); m_bSlipEnabledProcessing = false; - m_dSlipPosition = 0.; + m_slipPosition = mixxx::audio::kStartFramePos; m_dSlipRate = 0; m_queuedSeek.setValue(kNoQueuedSeek); @@ -591,7 +598,7 @@ void EngineBuffer::ejectTrack() { doSeekPlayPos(mixxx::audio::kStartFramePos, SEEK_EXACT); m_pCurrentTrack.reset(); - m_pTrackSamples->set(0); + setTrackEndPosition(mixxx::audio::kInvalidFramePos); m_pTrackSampleRate->set(0); m_pTrackLoaded->forceSet(0); @@ -624,14 +631,11 @@ void EngineBuffer::notifyTrackLoaded( // First inform engineControls directly // Note: we are still in a worker thread. - const auto currentPosition = mixxx::audio::FramePos::fromEngineSamplePos(m_filepos_play); - const auto trackEndPosition = - mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid( - m_pTrackSamples->get()); + const auto trackEndPosition = getTrackEndPosition(); const auto sampleRate = mixxx::audio::SampleRate::fromDouble(m_pTrackSampleRate->get()); for (const auto& pControl : qAsConst(m_engineControls)) { pControl->trackLoaded(pNewTrack); - pControl->setFrameInfo(currentPosition, trackEndPosition, sampleRate); + pControl->setFrameInfo(m_playPosition, trackEndPosition, sampleRate); } if (pNewTrack) { @@ -659,27 +663,25 @@ void EngineBuffer::slotControlSeek(double fractionalPos) { doSeekFractional(fractionalPos, SEEK_STANDARD); } -// WARNING: This method runs from SyncWorker and Engine Worker -void EngineBuffer::slotControlSeekAbs(double playPosition) { - // TODO: Check if we can assert a valid play position here - const auto position = mixxx::audio::FramePos::fromEngineSamplePos(playPosition); +// WARNING: This method is called by EngineControl and runs in the engine thread +void EngineBuffer::seekAbs(mixxx::audio::FramePos position) { + DEBUG_ASSERT(position.isValid()); doSeekPlayPos(position, SEEK_STANDARD); } -// WARNING: This method runs from SyncWorker and Engine Worker -void EngineBuffer::slotControlSeekExact(double playPosition) { - // TODO: Check if we can assert a valid play position here - const auto position = mixxx::audio::FramePos::fromEngineSamplePos(playPosition); +// WARNING: This method is called by EngineControl and runs in the engine thread +void EngineBuffer::seekExact(mixxx::audio::FramePos position) { + DEBUG_ASSERT(position.isValid()); doSeekPlayPos(position, SEEK_EXACT); } -double EngineBuffer::fractionalPlayposFromAbsolute(double absolutePlaypos) { - double fFractionalPlaypos = 0.0; - if (m_trackSamplesOld != 0) { - fFractionalPlaypos = math_min(absolutePlaypos, m_trackSamplesOld); - fFractionalPlaypos /= m_trackSamplesOld; +double EngineBuffer::fractionalPlayposFromAbsolute(mixxx::audio::FramePos absolutePlaypos) { + if (!m_trackEndPositionOld.isValid()) { + return 0.0; } - return fFractionalPlaypos; + + const auto position = std::min(absolutePlaypos, m_trackEndPositionOld); + return position.value() / m_trackEndPositionOld.value(); } void EngineBuffer::doSeekFractional(double fractionalPos, enum SeekRequest seekType) { @@ -689,8 +691,7 @@ void EngineBuffer::doSeekFractional(double fractionalPos, enum SeekRequest seekT } // FIXME: Use maybe invalid here - const auto trackEndPosition = - mixxx::audio::FramePos::fromEngineSamplePos(m_pTrackSamples->get()); + const mixxx::audio::FramePos trackEndPosition = getTrackEndPosition(); VERIFY_OR_DEBUG_ASSERT(trackEndPosition.isValid()) { return; } @@ -718,7 +719,7 @@ bool EngineBuffer::updateIndicatorsAndModifyPlay(bool newPlay, bool oldPlay) { const QueuedSeek queuedSeek = m_queuedSeek.getValue(); if ((!m_pCurrentTrack && atomicLoadRelaxed(m_iTrackLoading) == 0) || (m_pCurrentTrack && atomicLoadRelaxed(m_iTrackLoading) == 0 && - m_filepos_play >= m_pTrackSamples->get() && + m_playPosition >= getTrackEndPosition() && queuedSeek.seekType == SEEK_NONE) || m_pPassthroughEnabled->toBool()) { // play not possible @@ -810,7 +811,7 @@ void EngineBuffer::processTrackLocked( ScopedTimer t("EngineBuffer::process_pauselock"); m_trackSampleRateOld = mixxx::audio::SampleRate::fromDouble(m_pTrackSampleRate->get()); - m_trackSamplesOld = m_pTrackSamples->get(); + m_trackEndPositionOld = getTrackEndPosition(); double baserate = 0.0; if (sampleRate.isValid()) { @@ -836,7 +837,7 @@ void EngineBuffer::processTrackLocked( // Update the slipped position and seek if it was disabled. processSlip(iBufferSize); - // Note: This may effects the m_filepos_play, play, scaler and crossfade buffer + // Note: This may effects the m_playPosition, play, scaler and crossfade buffer processSeek(paused); // speed is the ratio between track-time and real-time @@ -1016,11 +1017,12 @@ void EngineBuffer::processTrackLocked( rate = m_rate_old; } - bool at_end = m_filepos_play >= m_trackSamplesOld; + const mixxx::audio::FramePos trackEndPosition = getTrackEndPosition(); + bool atEnd = m_playPosition >= trackEndPosition; bool backwards = rate < 0; bool bCurBufferPaused = false; - if (at_end && !backwards) { + if (atEnd && !backwards) { // do not play past end bCurBufferPaused = true; } else if (rate == 0 && !is_scratching) { @@ -1035,28 +1037,25 @@ void EngineBuffer::processTrackLocked( // If the buffer is not paused, then scale the audio. if (!bCurBufferPaused) { // Perform scaling of Reader buffer into buffer. - double framesRead = - m_pScale->scaleBuffer(pOutput, iBufferSize); + const auto framesRead = m_pScale->scaleBuffer(pOutput, iBufferSize); // TODO(XXX): The result framesRead might not be an integer value. // Converting to samples here does not make sense. All positional // calculations should be done in frames instead of samples! Otherwise // rounding errors might occur when converting from samples back to // frames later. - double samplesRead = framesRead * kSamplesPerFrame; if (m_bScalerOverride) { // If testing, we don't have a real log so we fake the position. - m_filepos_play += samplesRead; + m_playPosition += framesRead; } else { // Adjust filepos_play by the amount we processed. - m_filepos_play = - m_pReadAheadManager->getFilePlaypositionFromLog( - m_filepos_play, samplesRead); + m_playPosition = + m_pReadAheadManager->getFilePlaypositionFromLog(m_playPosition, framesRead); } // Note: The last buffer of a track is padded with silence. // This silence is played together with the last samples in the last - // callback and the m_filepos_play is advanced behind the end of the track. + // callback and the m_playPosition is advanced behind the end of the track. if (m_bCrossfadeReady) { // Bring pOutput with the new parameters in and fade out the old one, @@ -1081,29 +1080,27 @@ void EngineBuffer::processTrackLocked( } } - const auto currentPosition = mixxx::audio::FramePos::fromEngineSamplePos(m_filepos_play); - const auto trackEndPosition = mixxx::audio::FramePos::fromEngineSamplePos(m_trackSamplesOld); for (const auto& pControl: qAsConst(m_engineControls)) { - pControl->setFrameInfo(currentPosition, trackEndPosition, m_trackSampleRateOld); - pControl->process(rate, currentPosition, iBufferSize); + pControl->setFrameInfo(m_playPosition, trackEndPosition, m_trackSampleRateOld); + pControl->process(rate, m_playPosition, iBufferSize); } m_scratching_old = is_scratching; // Handle repeat mode - bool at_start = m_filepos_play <= 0; - at_end = m_filepos_play >= m_trackSamplesOld; + const bool atStart = m_playPosition <= mixxx::audio::kStartFramePos; + atEnd = m_playPosition >= trackEndPosition; bool repeat_enabled = m_pRepeat->toBool(); bool end_of_track = //(at_start && backwards) || - (at_end && !backwards); + (atEnd && !backwards); // If playbutton is pressed, check if we are at start or end of track if ((m_playButton->toBool() || (m_fwdButton->toBool() || m_backButton->toBool())) && end_of_track) { if (repeat_enabled) { - double fractionalPos = at_start ? 1.0 : 0; + double fractionalPos = atStart ? 1.0 : 0; doSeekFractional(fractionalPos, SEEK_STANDARD); } else { m_playButton->set(0.); @@ -1191,20 +1188,27 @@ void EngineBuffer::processSlip(int iBufferSize) { if (enabled != m_bSlipEnabledProcessing) { m_bSlipEnabledProcessing = enabled; if (enabled) { - m_dSlipPosition = m_filepos_play; + m_slipPosition = m_playPosition; m_dSlipRate = m_rate_old; } else { // TODO(owen) assuming that looping will get canceled properly - double newPlayFrame = m_dSlipPosition / kSamplesPerFrame; - double roundedSlip = round(newPlayFrame) * kSamplesPerFrame; - slotControlSeekExact(roundedSlip); - m_dSlipPosition = 0; + seekExact(m_slipPosition.toNearestFrameBoundary()); + m_slipPosition = mixxx::audio::kStartFramePos; } } // Increment slip position even if it was just toggled -- this ensures the position is correct. if (enabled) { - m_dSlipPosition += static_cast(iBufferSize) * m_dSlipRate; + // `iBufferSize` originates from `SoundManager::onDeviceOutputCallback` + // and is always a multiple of 2, so we can safely use integer division + // to find the number of frames per buffer here. + // + // TODO: Check if we can replace `iBufferSize` with the number of + // frames per buffer in most engine method signatures to avoid this + // back and forth calculations. + const int bufferFrameCount = iBufferSize / mixxx::kEngineChannelCount; + DEBUG_ASSERT(bufferFrameCount * mixxx::kEngineChannelCount == iBufferSize); + m_slipPosition += static_cast(bufferFrameCount) * m_dSlipRate; } } @@ -1258,7 +1262,7 @@ void EngineBuffer::processSeek(bool paused) { return; case SEEK_PHASE: // only adjust phase - position = mixxx::audio::FramePos::fromEngineSamplePos(m_filepos_play); + position = m_playPosition; break; case SEEK_STANDARD: if (m_pQuantize->toBool()) { @@ -1281,12 +1285,8 @@ void EngineBuffer::processSeek(bool paused) { return; } - const auto trackEndPosition = mixxx::audio::FramePos::fromEngineSamplePos(m_trackSamplesOld); - // Don't allow the playposition to go past the end. - if (position > trackEndPosition) { - position = trackEndPosition; - } + position = std::min(position, m_trackEndPositionOld); if (!paused && (seekType & SEEK_PHASE)) { if (kLogger.traceEnabled()) { @@ -1297,15 +1297,15 @@ void EngineBuffer::processSeek(bool paused) { position = m_pLoopingControl->getSyncPositionInsideLoop(position, syncPosition); if (kLogger.traceEnabled()) { kLogger.trace() - << "EngineBuffer::processSeek" << getGroup() << "seek info:" << m_filepos_play + << "EngineBuffer::processSeek" << getGroup() << "seek info:" << m_playPosition << "->" << position; } } - if (position.toEngineSamplePos() != m_filepos_play) { + if (position != m_playPosition) { if (kLogger.traceEnabled()) { kLogger.trace() << "EngineBuffer::processSeek" << getGroup() << "Seek to" << position; } - setNewPlaypos(position.toEngineSamplePos()); + setNewPlaypos(position); m_previousBufferSeek = true; } // Reset the m_queuedSeek value after it has been processed in @@ -1363,10 +1363,10 @@ void EngineBuffer::updateIndicators(double speed, int iBufferSize) { // Increase samplesCalculated by the buffer size m_iSamplesSinceLastIndicatorUpdate += iBufferSize; - const double fFractionalPlaypos = fractionalPlayposFromAbsolute(m_filepos_play); + const double fFractionalPlaypos = fractionalPlayposFromAbsolute(m_playPosition); - const double tempoTrackSeconds = m_trackSamplesOld / kSamplesPerFrame - / m_trackSampleRateOld / m_tempo_ratio_old; + const double tempoTrackSeconds = m_trackEndPositionOld.value() / + m_trackSampleRateOld / m_tempo_ratio_old; if(speed > 0 && fFractionalPlaypos == 1.0) { // At Track end speed = 0; @@ -1389,18 +1389,19 @@ void EngineBuffer::updateIndicators(double speed, int iBufferSize) { // Update visual control object, this needs to be done more often than the // playpos slider - m_visualPlayPos->set(fFractionalPlaypos, speed * m_baserate_old, - (double)iBufferSize / m_trackSamplesOld, - fractionalPlayposFromAbsolute(m_dSlipPosition), + m_visualPlayPos->set(fFractionalPlaypos, + speed * m_baserate_old, + static_cast(iBufferSize) / + m_trackEndPositionOld.toEngineSamplePos(), + fractionalPlayposFromAbsolute(m_slipPosition), tempoTrackSeconds); // TODO: Especially with long audio buffers, jitter is visible. This can be fixed by moving the // ClockControl::updateIndicators into the waveform update loop which is synced with the display refresh rate. // Via the visual play position it's possible to access to the sample that is currently played, // and not the one that have been processed as in the current solution. - const auto currentPosition = mixxx::audio::FramePos::fromEngineSamplePos(m_filepos_play); const auto sampleRate = mixxx::audio::SampleRate::fromDouble(m_pSampleRate->get()); - m_pClockControl->updateIndicators(speed * m_baserate_old, currentPosition, sampleRate); + m_pClockControl->updateIndicators(speed * m_baserate_old, m_playPosition, sampleRate); } void EngineBuffer::hintReader(const double dRate) { @@ -1410,7 +1411,7 @@ void EngineBuffer::hintReader(const double dRate) { //if slipping, hint about virtual position so we're ready for it if (m_bSlipEnabledProcessing) { Hint hint; - hint.frame = SampleUtil::floorPlayPosToFrame(m_dSlipPosition); + hint.frame = static_cast(m_slipPosition.toLowerFrameBoundary().value()); hint.priority = 1; if (m_dSlipRate >= 0) { hint.frameCount = Hint::kFrameCountForward; @@ -1467,19 +1468,23 @@ void EngineBuffer::slotEjectTrack(double v) { } } -double EngineBuffer::getExactPlayPos() const { +mixxx::audio::FramePos EngineBuffer::getExactPlayPos() const { if (!m_visualPlayPos->isValid()) { - return 0.0; + return mixxx::audio::kStartFramePos; } - return m_visualPlayPos->getEnginePlayPos() * getTrackSamples(); + return getTrackEndPosition() * m_visualPlayPos->getEnginePlayPos(); } double EngineBuffer::getVisualPlayPos() const { return m_visualPlayPos->getEnginePlayPos(); } -double EngineBuffer::getTrackSamples() const { - return m_pTrackSamples->get(); +mixxx::audio::FramePos EngineBuffer::getTrackEndPosition() const { + return mixxx::audio::FramePos::fromEngineSamplePosMaybeInvalid(m_pTrackSamples->get()); +} + +void EngineBuffer::setTrackEndPosition(mixxx::audio::FramePos position) { + m_pTrackSamples->set(position.toEngineSamplePosMaybeInvalid()); } double EngineBuffer::getUserOffset() const { diff --git a/src/engine/enginebuffer.h b/src/engine/enginebuffer.h index 7f40d44220b..9683a7d3b75 100644 --- a/src/engine/enginebuffer.h +++ b/src/engine/enginebuffer.h @@ -80,11 +80,6 @@ class EngineBuffer : public EngineObject { KEYLOCK_ENGINE_COUNT, }; - // This value is used to make sure the initial seek after loading a track is - // not omitted. Therefore this value must be different for 0.0 or any likely - // value for the main cue - static constexpr double kInitalSamplePosition = -DBL_MAX; - EngineBuffer(const QString& group, UserSettingsPointer pConfig, EngineChannel* pChannel, EngineMaster* pMixingEngine); virtual ~EngineBuffer(); @@ -128,9 +123,10 @@ class EngineBuffer : public EngineObject { bool isTrackLoaded() const; TrackPointer getLoadedTrack() const; - double getExactPlayPos() const; + mixxx::audio::FramePos getExactPlayPos() const; double getVisualPlayPos() const; - double getTrackSamples() const; + mixxx::audio::FramePos getTrackEndPosition() const; + void setTrackEndPosition(mixxx::audio::FramePos position); double getUserOffset() const; double getRateRatio() const; @@ -165,6 +161,9 @@ class EngineBuffer : public EngineObject { m_channelIndex = channelIndex; } + void seekAbs(mixxx::audio::FramePos); + void seekExact(mixxx::audio::FramePos); + public slots: void slotControlPlayRequest(double); void slotControlPlayFromStart(double); @@ -173,8 +172,6 @@ class EngineBuffer : public EngineObject { void slotControlStart(double); void slotControlEnd(double); void slotControlSeek(double); - void slotControlSeekAbs(double); - void slotControlSeekExact(double); void slotKeylockEngineChanged(double); void slotEjectTrack(double); @@ -212,7 +209,7 @@ class EngineBuffer : public EngineObject { void ejectTrack(); - double fractionalPlayposFromAbsolute(double absolutePlaypos); + double fractionalPlayposFromAbsolute(mixxx::audio::FramePos position); void doSeekFractional(double fractionalPos, enum SeekRequest seekType); void doSeekPlayPos(mixxx::audio::FramePos position, enum SeekRequest seekType); @@ -226,7 +223,7 @@ class EngineBuffer : public EngineObject { void seekCloneBuffer(EngineBuffer* pOtherBuffer); // Reset buffer playpos and set file playpos. - void setNewPlaypos(double playpos); + void setNewPlaypos(mixxx::audio::FramePos playpos); void processSyncRequests(); void processSeek(bool paused); @@ -283,7 +280,7 @@ class EngineBuffer : public EngineObject { HintVector m_hintList; // The current sample to play in the file. - double m_filepos_play; + mixxx::audio::FramePos m_playPosition; // The previous callback's speed. Used to check if the scaler parameters // need updating. @@ -310,7 +307,7 @@ class EngineBuffer : public EngineObject { double m_rate_old; // Copy of length of file - double m_trackSamplesOld; + mixxx::audio::FramePos m_trackEndPositionOld; // Copy of file sample rate mixxx::audio::SampleRate m_trackSampleRateOld; @@ -322,7 +319,7 @@ class EngineBuffer : public EngineObject { int m_iSamplesSinceLastIndicatorUpdate; // The location where the track would have been had slip not been engaged - double m_dSlipPosition; + mixxx::audio::FramePos m_slipPosition; // Saved value of rate for slip mode double m_dSlipRate; // m_bSlipEnabledProcessing is only used by the engine processing thread. diff --git a/src/engine/readaheadmanager.cpp b/src/engine/readaheadmanager.cpp index 2462bded14b..8958ea44ac8 100644 --- a/src/engine/readaheadmanager.cpp +++ b/src/engine/readaheadmanager.cpp @@ -279,3 +279,12 @@ double ReadAheadManager::getFilePlaypositionFromLog( return filePlayposition; } + +mixxx::audio::FramePos ReadAheadManager::getFilePlaypositionFromLog( + mixxx::audio::FramePos currentPosition, + mixxx::audio::FrameDiff_t numConsumedFrames) { + const double positionSamples = + getFilePlaypositionFromLog(currentPosition.toEngineSamplePos(), + numConsumedFrames * mixxx::kEngineChannelCount); + return mixxx::audio::FramePos::fromEngineSamplePos(positionSamples); +} diff --git a/src/engine/readaheadmanager.h b/src/engine/readaheadmanager.h index 49f5b98c64f..fa673dcca85 100644 --- a/src/engine/readaheadmanager.h +++ b/src/engine/readaheadmanager.h @@ -4,6 +4,7 @@ #include #include +#include "audio/frame.h" #include "engine/cachingreader/cachingreader.h" #include "util/math.h" #include "util/types.h" @@ -46,6 +47,9 @@ class ReadAheadManager { } virtual void notifySeek(double seekPosition); + virtual void notifySeek(mixxx::audio::FramePos position) { + notifySeek(position.toEngineSamplePos()); + } /// hintReader allows the ReadAheadManager to provide hints to the reader to /// indicate that the given portion of a song is about to be read. @@ -54,6 +58,9 @@ class ReadAheadManager { virtual double getFilePlaypositionFromLog( double currentFilePlayposition, double numConsumedSamples); + mixxx::audio::FramePos getFilePlaypositionFromLog( + mixxx::audio::FramePos currentPosition, + mixxx::audio::FrameDiff_t numConsumedFrames); private: /// An entry in the read log indicates the virtual playposition the read diff --git a/src/library/autodj/autodjfeature.cpp b/src/library/autodj/autodjfeature.cpp index c34e0849292..eb18b36296d 100644 --- a/src/library/autodj/autodjfeature.cpp +++ b/src/library/autodj/autodjfeature.cpp @@ -62,10 +62,15 @@ AutoDJFeature::AutoDJFeature(Library* pLibrary, pPlayerManager, pLibrary->trackCollectionManager(), m_iAutoDJPlaylistId); + + // Connect loadTrackToPlayer signal as a queued connection to make sure all callbacks of a + // previous load attempt have been called (lp1941743) connect(m_pAutoDJProcessor, &AutoDJProcessor::loadTrackToPlayer, this, - &AutoDJFeature::loadTrackToPlayer); + &LibraryFeature::loadTrackToPlayer, + Qt::QueuedConnection); + m_playlistDao.setAutoDJProcessor(m_pAutoDJProcessor); // Create the "Crates" tree-item under the root item. @@ -127,7 +132,7 @@ void AutoDJFeature::bindLibraryWidget( connect(m_pAutoDJView, &DlgAutoDJ::loadTrackToPlayer, this, - &AutoDJFeature::loadTrackToPlayer); + &LibraryFeature::loadTrackToPlayer); connect(m_pAutoDJView, &DlgAutoDJ::trackSelected, diff --git a/src/library/dao/trackdao.cpp b/src/library/dao/trackdao.cpp index 026542b40af..95f796f051f 100644 --- a/src/library/dao/trackdao.cpp +++ b/src/library/dao/trackdao.cpp @@ -24,7 +24,6 @@ #include "library/trackset/crate/cratestorage.h" #include "moc_trackdao.cpp" #include "sources/soundsourceproxy.h" -#include "track/beatgrid.h" #include "track/beats.h" #include "track/globaltrackcache.h" #include "track/keyfactory.h" @@ -1245,8 +1244,8 @@ bool setTrackBeats(const QSqlRecord& record, const int column, Track* pTrack) { } } else if (bpm.isValid()) { // Load a temporary beat grid without offset that will be replaced by the analyzer. - const auto pBeats = mixxx::BeatGrid::makeBeatGrid( - pTrack->getSampleRate(), bpm, mixxx::audio::kStartFramePos); + const auto pBeats = mixxx::Beats::fromConstTempo( + pTrack->getSampleRate(), mixxx::audio::kStartFramePos, bpm); pTrack->trySetBeats(pBeats); } else { pTrack->trySetBeats(nullptr); diff --git a/src/library/dlgtrackinfo.cpp b/src/library/dlgtrackinfo.cpp index 5e2cc7c4f80..fb8333190b9 100644 --- a/src/library/dlgtrackinfo.cpp +++ b/src/library/dlgtrackinfo.cpp @@ -14,7 +14,6 @@ #include "moc_dlgtrackinfo.cpp" #include "preferences/colorpalettesettings.h" #include "sources/soundsourceproxy.h" -#include "track/beatgrid.h" #include "track/beatutils.h" #include "track/keyfactory.h" #include "track/keyutils.h" @@ -573,10 +572,10 @@ void DlgTrackInfo::slotBpmConstChanged(int state) { // The cue point should be set on a beat, so this seems // to be a good alternative const mixxx::audio::FramePos cuePosition = m_pLoadedTrack->getMainCuePosition(); - m_pBeatsClone = mixxx::BeatGrid::makeBeatGrid( + m_pBeatsClone = mixxx::Beats::fromConstTempo( m_pLoadedTrack->getSampleRate(), - bpm, - cuePosition); + cuePosition, + bpm); } else { m_pBeatsClone.reset(); } @@ -612,10 +611,10 @@ void DlgTrackInfo::slotSpinBpmValueChanged(double value) { if (!m_pBeatsClone) { const mixxx::audio::FramePos cuePosition = m_pLoadedTrack->getMainCuePosition(); - m_pBeatsClone = mixxx::BeatGrid::makeBeatGrid( + m_pBeatsClone = mixxx::Beats::fromConstTempo( m_pLoadedTrack->getSampleRate(), - bpm, - cuePosition); + cuePosition, + bpm); } const mixxx::Bpm oldValue = m_pBeatsClone->getBpm(); diff --git a/src/library/previewbuttondelegate.cpp b/src/library/previewbuttondelegate.cpp index f97f0508924..5c818d4c4fe 100644 --- a/src/library/previewbuttondelegate.cpp +++ b/src/library/previewbuttondelegate.cpp @@ -167,25 +167,31 @@ QSize PreviewButtonDelegate::sizeHint(const QStyleOptionViewItem& option, } void PreviewButtonDelegate::cellEntered(const QModelIndex& index) { - DEBUG_ASSERT(index.isValid()); + VERIFY_OR_DEBUG_ASSERT(index.isValid()) { + return; + } VERIFY_OR_DEBUG_ASSERT(parentTableView()) { return; } - // this slot is called if the mouse pointer enters ANY cell on - // the QTableView but the code should only be executed on a button - if (index.column() == m_column) { - DEBUG_ASSERT(index != m_currentEditedCellIndex); - if (m_currentEditedCellIndex.isValid()) { - // Close the editor in the other cell - parentTableView()->closePersistentEditor(m_currentEditedCellIndex); - } - parentTableView()->openPersistentEditor(index); - m_currentEditedCellIndex = index; - } else if (m_currentEditedCellIndex.isValid()) { - // Close editor if the mouse leaves the button + // Ignore signal if the edited cell index didn't change. + // Receiving this signal for the same cell again could happen + // if no other cell has been entered between those events. + // https://bugs.launchpad.net/mixxx/+bug/1929141 + if (index == m_currentEditedCellIndex) { + return; + } + // Close the editor when leaving the currently edited cell + if (m_currentEditedCellIndex.isValid()) { parentTableView()->closePersistentEditor(m_currentEditedCellIndex); m_currentEditedCellIndex = QModelIndex(); } + // Only open a new editor for preview column cells, but not any + // other cells + if (index.column() != m_column) { + return; + } + parentTableView()->openPersistentEditor(index); + m_currentEditedCellIndex = index; } void PreviewButtonDelegate::buttonClicked() { diff --git a/src/library/rekordbox/rekordboxfeature.cpp b/src/library/rekordbox/rekordboxfeature.cpp index 586c9b51385..6c85c37de60 100644 --- a/src/library/rekordbox/rekordboxfeature.cpp +++ b/src/library/rekordbox/rekordboxfeature.cpp @@ -19,7 +19,7 @@ #include "library/trackcollectionmanager.h" #include "library/treeitem.h" #include "moc_rekordboxfeature.cpp" -#include "track/beatmap.h" +#include "track/beats.h" #include "track/cue.h" #include "track/keyfactory.h" #include "track/track.h" @@ -938,10 +938,10 @@ void readAnalyze(TrackPointer track, beats << mixxx::audio::FramePos(sampleRateKhz * static_cast(time)); } - const auto pBeats = mixxx::BeatMap::makeBeatMap( + const auto pBeats = mixxx::Beats::fromBeatPositions( sampleRate, - mixxx::rekordboxconstants::beatsSubversion, - beats); + beats, + mixxx::rekordboxconstants::beatsSubversion); track->trySetBeats(pBeats); } break; case rekordbox_anlz_t::SECTION_TAGS_CUES: { diff --git a/src/mixer/basetrackplayer.cpp b/src/mixer/basetrackplayer.cpp index bf0ee457588..5cd14e5e088 100644 --- a/src/mixer/basetrackplayer.cpp +++ b/src/mixer/basetrackplayer.cpp @@ -393,7 +393,7 @@ void BaseTrackPlayerImpl::disconnectLoadedTrack() { } void BaseTrackPlayerImpl::slotLoadTrack(TrackPointer pNewTrack, bool bPlay) { - //qDebug() << "BaseTrackPlayerImpl::slotLoadTrack" << getGroup(); + //qDebug() << "BaseTrackPlayerImpl::slotLoadTrack" << getGroup() << pNewTrack.get(); // Before loading the track, ensure we have access. This uses lazy // evaluation to make sure track isn't NULL before we dereference it. if (pNewTrack) { @@ -408,12 +408,14 @@ void BaseTrackPlayerImpl::slotLoadTrack(TrackPointer pNewTrack, bool bPlay) { loadTrack(pNewTrack); + // await slotTrackLoaded()/slotLoadFailed() + // emit this before pEngineBuffer->loadTrack() to avoid receiving + // unexpected slotTrackLoaded() before, in case the track is still cached (lp1941743) + emit loadingTrack(pNewTrack, pOldTrack); + // Request a new track from EngineBuffer EngineBuffer* pEngineBuffer = m_pChannel->getEngineBuffer(); pEngineBuffer->loadTrack(pNewTrack, bPlay); - - // await slotTrackLoaded()/slotLoadFailed() - emit loadingTrack(pNewTrack, pOldTrack); } void BaseTrackPlayerImpl::slotLoadFailed(TrackPointer pTrack, const QString& reason) { @@ -437,7 +439,7 @@ void BaseTrackPlayerImpl::slotLoadFailed(TrackPointer pTrack, const QString& rea void BaseTrackPlayerImpl::slotTrackLoaded(TrackPointer pNewTrack, TrackPointer pOldTrack) { - //qDebug() << "BaseTrackPlayerImpl::slotTrackLoaded"; + //qDebug() << "BaseTrackPlayerImpl::slotTrackLoaded" << pNewTrack.get() << pOldTrack.get(); if (!pNewTrack && pOldTrack && pOldTrack == m_pLoadedTrack) { diff --git a/src/test/beatgridtest.cpp b/src/test/beatgridtest.cpp index f8af05b1be9..4b00338d64e 100644 --- a/src/test/beatgridtest.cpp +++ b/src/test/beatgridtest.cpp @@ -2,7 +2,7 @@ #include -#include "track/beatgrid.h" +#include "track/beats.h" #include "track/track.h" #include "util/memory.h" @@ -29,9 +29,9 @@ TEST(BeatGridTest, Scale) { constexpr mixxx::Bpm bpm(60.0); pTrack->trySetBpm(bpm.value()); - auto pGrid = BeatGrid::makeBeatGrid(pTrack->getSampleRate(), - mixxx::Bpm(bpm), - mixxx::audio::kStartFramePos); + auto pGrid = Beats::fromConstTempo(pTrack->getSampleRate(), + mixxx::audio::kStartFramePos, + mixxx::Bpm(bpm)); EXPECT_DOUBLE_EQ(bpm.value(), pGrid->getBpm().value()); pGrid = pGrid->scale(Beats::BpmScale::Double); @@ -61,9 +61,9 @@ TEST(BeatGridTest, TestNthBeatWhenOnBeat) { pTrack->trySetBpm(bpm); constexpr mixxx::audio::FrameDiff_t beatLengthFrames = 60.0 * sampleRate / bpm; - auto pGrid = BeatGrid::makeBeatGrid(pTrack->getSampleRate(), - mixxx::Bpm(bpm), - mixxx::audio::kStartFramePos); + auto pGrid = Beats::fromConstTempo(pTrack->getSampleRate(), + mixxx::audio::kStartFramePos, + mixxx::Bpm(bpm)); // Pretend we're on the 20th beat; constexpr mixxx::audio::FramePos position(beatLengthFrames * 20); @@ -105,9 +105,9 @@ TEST(BeatGridTest, TestNthBeatWhenNotOnBeat) { pTrack->trySetBpm(bpm.value()); const mixxx::audio::FrameDiff_t beatLengthFrames = 60.0 * sampleRate / bpm.value(); - auto pGrid = BeatGrid::makeBeatGrid(pTrack->getSampleRate(), - bpm, - mixxx::audio::kStartFramePos); + auto pGrid = Beats::fromConstTempo(pTrack->getSampleRate(), + mixxx::audio::kStartFramePos, + bpm); // Pretend we're half way between the 20th and 21st beat const mixxx::audio::FramePos previousBeat(beatLengthFrames * 20.0); diff --git a/src/test/beatmaptest.cpp b/src/test/beatmaptest.cpp index a09bb79b931..585cf146758 100644 --- a/src/test/beatmaptest.cpp +++ b/src/test/beatmaptest.cpp @@ -3,7 +3,7 @@ #include -#include "track/beatmap.h" +#include "track/beats.h" #include "track/track.h" using namespace mixxx; @@ -60,7 +60,7 @@ TEST_F(BeatMapTest, Scale) { // Note beats must be in frames, not samples. QVector beats = createBeatVector(startOffsetFrames, numBeats, beatLengthFrames); - auto pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats); + auto pMap = Beats::fromBeatPositions(m_pTrack->getSampleRate(), beats); EXPECT_DOUBLE_EQ(bpm.value(), pMap->getBpm().value()); pMap = pMap->scale(Beats::BpmScale::Double); @@ -91,7 +91,7 @@ TEST_F(BeatMapTest, TestNthBeat) { // Note beats must be in frames, not samples. QVector beats = createBeatVector(startOffsetFrames, numBeats, beatLengthFrames); - auto pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats); + auto pMap = Beats::fromBeatPositions(m_pTrack->getSampleRate(), beats); // Check edge cases const mixxx::audio::FramePos firstBeat = startOffsetFrames + beatLengthFrames * 0; @@ -137,7 +137,7 @@ TEST_F(BeatMapTest, TestNthBeatWhenOnBeat) { // Note beats must be in frames, not samples. QVector beats = createBeatVector(startOffsetFrames, numBeats, beatLengthFrames); - auto pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats); + auto pMap = Beats::fromBeatPositions(m_pTrack->getSampleRate(), beats); // Pretend we're on the 20th beat; const int curBeat = 20; @@ -180,7 +180,7 @@ TEST_F(BeatMapTest, TestNthBeatWhenNotOnBeat) { // Note beats must be in frames, not samples. QVector beats = createBeatVector(startOffsetFrames, numBeats, beatLengthFrames); - auto pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats); + auto pMap = Beats::fromBeatPositions(m_pTrack->getSampleRate(), beats); // Pretend we're half way between the 20th and 21st beat const mixxx::audio::FramePos previousBeat = startOffsetFrames + beatLengthFrames * 20.0; @@ -225,7 +225,7 @@ TEST_F(BeatMapTest, TestBpmAround) { beat_pos += beat_length; } - auto pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats); + auto pMap = Beats::fromBeatPositions(m_pTrack->getSampleRate(), beats); // The average of the first 8 beats should be different than the average // of the last 8 beats. @@ -251,7 +251,7 @@ TEST_F(BeatMapTest, TestBpmAround) { // Try a really, really short track constexpr auto startFramePos = mixxx::audio::FramePos(10); beats = createBeatVector(startFramePos, 3, approx_beat_length); - pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats); + pMap = Beats::fromBeatPositions(m_pTrack->getSampleRate(), beats); EXPECT_DOUBLE_EQ(filebpm.value(), pMap->getBpmAroundPosition( mixxx::audio::kStartFramePos + 1 * approx_beat_length, @@ -271,7 +271,7 @@ TEST_F(BeatMapTest, FindBeatsWithFractionalPos) { for (; beatPos <= lastBeatPos; beatPos += beatLengthFrames) { beats.append(beatPos); } - const auto pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats); + const auto pMap = Beats::fromBeatPositions(m_pTrack->getSampleRate(), beats); // All beats are in range auto it = pMap->findBeats(mixxx::audio::kStartFramePos, lastBeatPos); @@ -318,7 +318,7 @@ TEST_F(BeatMapTest, HasBeatInRangeWithFractionalPos) { for (; beatPos <= lastBeatPos; beatPos += beatLengthFrames) { beats.append(beatPos); } - const auto pMap = BeatMap::makeBeatMap(m_pTrack->getSampleRate(), QString(), beats); + const auto pMap = Beats::fromBeatPositions(m_pTrack->getSampleRate(), beats); const mixxx::audio::FrameDiff_t halfBeatLengthFrames = beatLengthFrames / 2; EXPECT_TRUE(pMap->hasBeatInRange(mixxx::audio::kStartFramePos, diff --git a/src/test/beatstranslatetest.cpp b/src/test/beatstranslatetest.cpp index 464bb60b131..bf7b7052201 100644 --- a/src/test/beatstranslatetest.cpp +++ b/src/test/beatstranslatetest.cpp @@ -1,5 +1,5 @@ #include "test/mockedenginebackendtest.h" -#include "track/beatgrid.h" +#include "track/beats.h" #include "util/memory.h" class BeatsTranslateTest : public MockedEngineBackendTest { @@ -9,21 +9,21 @@ TEST_F(BeatsTranslateTest, SimpleTranslateMatch) { // Set up BeatGrids for decks 1 and 2. const auto bpm = mixxx::Bpm(60.0); constexpr auto firstBeat = mixxx::audio::kStartFramePos; - auto grid1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), bpm, firstBeat); + auto grid1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), firstBeat, bpm); m_pTrack1->trySetBeats(grid1); ASSERT_DOUBLE_EQ(firstBeat.value(), grid1->findClosestBeat(mixxx::audio::kStartFramePos).value()); - auto grid2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), bpm, firstBeat); + auto grid2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), firstBeat, bpm); m_pTrack2->trySetBeats(grid2); ASSERT_DOUBLE_EQ(firstBeat.value(), grid2->findClosestBeat(mixxx::audio::kStartFramePos).value()); // Seek deck 1 forward a bit. - const double deltaFrames = 1111.0; - m_pChannel1->getEngineBuffer()->slotControlSeekAbs(deltaFrames * 2.0); + const auto seekPosition = mixxx::audio::FramePos{1111.0}; + m_pChannel1->getEngineBuffer()->seekAbs(seekPosition); ProcessBuffer(); EXPECT_TRUE(m_pChannel1->getEngineBuffer()->getVisualPlayPos() > 0); @@ -45,9 +45,9 @@ TEST_F(BeatsTranslateTest, SimpleTranslateMatch) { pTranslateMatchAlignment->set(1.0); ProcessBuffer(); - // Deck 1 is +deltaFrames away from its closest beat (which is at 0). - // Deck 2 was left at 0. We translated grid 2 so that it is also +deltaFrames - // away from its closest beat, so that beat should be at -deltaFrames. - mixxx::BeatsPointer beats = m_pTrack2->getBeats(); - ASSERT_DOUBLE_EQ(-deltaFrames, beats->findClosestBeat(mixxx::audio::kStartFramePos).value()); + // Deck 1 is +seekPosition away from its closest beat (which is at 0). + // Deck 2 was left at 0. We translated grid 2 so that it is also +seekPosition + // away from its closest beat, so that beat should be at -seekPosition. + mixxx::BeatsPointer pBeats = m_pTrack2->getBeats(); + EXPECT_FRAMEPOS_EQ(seekPosition * -1, pBeats->findClosestBeat(mixxx::audio::kStartFramePos)); } diff --git a/src/test/bpmcontrol_test.cpp b/src/test/bpmcontrol_test.cpp index f2c621ed2c0..9baa95b0049 100644 --- a/src/test/bpmcontrol_test.cpp +++ b/src/test/bpmcontrol_test.cpp @@ -1,14 +1,14 @@ +#include "engine/controls/bpmcontrol.h" + #include -#include #include +#include -#include "mixxxtest.h" #include "control/controlobject.h" #include "control/controlpushbutton.h" -#include "engine/controls/bpmcontrol.h" -#include "track/beatgrid.h" -#include "track/beatmap.h" +#include "mixxxtest.h" +#include "track/beats.h" #include "track/track.h" class BpmControlTest : public MixxxTest { @@ -35,8 +35,8 @@ TEST_F(BpmControlTest, BeatContext_BeatGrid) { const auto bpm = mixxx::Bpm(60.0); const mixxx::audio::FrameDiff_t expectedBeatLengthFrames = (60.0 * sampleRate / bpm.value()); - const mixxx::BeatsPointer pBeats = mixxx::BeatGrid::makeBeatGrid( - pTrack->getSampleRate(), bpm, mixxx::audio::kStartFramePos); + const mixxx::BeatsPointer pBeats = mixxx::Beats::fromConstTempo( + pTrack->getSampleRate(), mixxx::audio::kStartFramePos, bpm); // On a beat. mixxx::audio::FramePos prevBeatPosition; diff --git a/src/test/enginesynctest.cpp b/src/test/enginesynctest.cpp index d6a50081a07..d2744f6166c 100644 --- a/src/test/enginesynctest.cpp +++ b/src/test/enginesynctest.cpp @@ -10,8 +10,7 @@ #include "preferences/usersettings.h" #include "test/mixxxtest.h" #include "test/mockedenginebackendtest.h" -#include "track/beatgrid.h" -#include "track/beatmap.h" +#include "track/beats.h" #include "util/memory.h" namespace { @@ -244,11 +243,11 @@ TEST_F(EngineSyncTest, SetLeaderSuccess) { TEST_F(EngineSyncTest, ExplicitLeaderPersists) { // If we set an explicit leader, enabling sync or pressing play on other decks // doesn't cause the leader to move around. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(120), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(120)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(124), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(124)); m_pTrack2->trySetBeats(pBeats2); auto pButtonLeaderSync1 = @@ -278,14 +277,14 @@ TEST_F(EngineSyncTest, ExplicitLeaderPersists) { TEST_F(EngineSyncTest, SetLeaderWhilePlaying) { // Make sure we don't get two leader lights if we change leaders while playing. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(120), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(120)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(124), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(124)); m_pTrack2->trySetBeats(pBeats2); - mixxx::BeatsPointer pBeats3 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack3->getSampleRate(), mixxx::Bpm(128), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats3 = mixxx::Beats::fromConstTempo( + m_pTrack3->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(128)); m_pTrack3->trySetBeats(pBeats3); auto pButtonLeaderSync1 = @@ -315,8 +314,8 @@ TEST_F(EngineSyncTest, SetLeaderWhilePlaying) { TEST_F(EngineSyncTest, SetEnabledBecomesLeader) { // If we set the first channel with a valid tempo to follower, it should be leader. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(80), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(80)); m_pTrack1->trySetBeats(pBeats1); auto pButtonLeaderSync1 = std::make_unique(m_sGroup1, "sync_mode"); @@ -342,12 +341,12 @@ TEST_F(EngineSyncTest, DisableInternalLeaderWhilePlaying) { EXPECT_TRUE(isExplicitLeader(m_sInternalClockGroup)); // Make sure both decks are playing. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(80), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(80)); m_pTrack1->trySetBeats(pBeats1); ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(80), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(80)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0); ProcessBuffer(); @@ -363,15 +362,15 @@ TEST_F(EngineSyncTest, DisableInternalLeaderWhilePlaying) { TEST_F(EngineSyncTest, DisableSyncOnLeader) { // Channel 1 follower, channel 2 leader. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack1->trySetBeats(pBeats1); auto pButtonSyncMode1 = std::make_unique(m_sGroup1, "sync_mode"); pButtonSyncMode1->set(static_cast(SyncMode::Follower)); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack2->trySetBeats(pBeats2); // Set deck two to explicit leader. auto pButtonSyncLeader2 = @@ -409,8 +408,8 @@ TEST_F(EngineSyncTest, InternalLeaderSetFollowerSliderMoves) { pButtonLeaderSyncInternal->set(1); // Set the file bpm of channel 1 to 80 bpm. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(80), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(80)); m_pTrack1->trySetBeats(pBeats1); auto pButtonLeaderSync1 = @@ -428,8 +427,8 @@ TEST_F(EngineSyncTest, AnySyncDeckSliderStays) { // If there exists a sync deck, even if it's not playing, don't change the // leader BPM if a new deck enables sync. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(80), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(80)); m_pTrack1->trySetBeats(pBeats1); auto pButtonSyncEnabled1 = std::make_unique(m_sGroup1, "sync_enabled"); @@ -440,8 +439,8 @@ TEST_F(EngineSyncTest, AnySyncDeckSliderStays) { ControlObject::getControl(ConfigKey(m_sInternalClockGroup, "bpm")) ->get()); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(100), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(100)); m_pTrack2->trySetBeats(pBeats2); auto pButtonSyncEnabled2 = std::make_unique(m_sGroup2, "sync_enabled"); @@ -465,13 +464,13 @@ TEST_F(EngineSyncTest, InternalClockFollowsFirstPlayingDeck) { std::make_unique(m_sGroup2, "sync_enabled"); // Set up decks so they can be playing, and start deck 1. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(100), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(100)); m_pTrack1->trySetBeats(pBeats1); ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.0)); ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack2->trySetBeats(pBeats2); ControlObject::set(ConfigKey(m_sGroup2, "rate"), getRateSliderValue(1.0)); ControlObject::set(ConfigKey(m_sGroup2, "play"), 0.0); @@ -537,12 +536,12 @@ TEST_F(EngineSyncTest, SetExplicitLeaderByLights) { std::make_unique(m_sGroup2, "sync_leader"); // Set the file bpm of channel 1 to 160bpm. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(160), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(160)); m_pTrack1->trySetBeats(pBeats1); // Set the file bpm of channel 2 to 150bpm. - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(150), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(150)); m_pTrack2->trySetBeats(pBeats2); ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0); @@ -620,8 +619,8 @@ TEST_F(EngineSyncTest, RateChangeTest) { ProcessBuffer(); // Set the file bpm of channel 1 to 160bpm. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(160), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(160)); m_pTrack1->trySetBeats(pBeats1); EXPECT_DOUBLE_EQ( 160.0, ControlObject::get(ConfigKey(m_sGroup1, "file_bpm"))); @@ -640,8 +639,8 @@ TEST_F(EngineSyncTest, RateChangeTest) { 192.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm"))); // Set the file bpm of channel 2 to 120bpm. - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(120), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(120)); m_pTrack2->trySetBeats(pBeats2); EXPECT_DOUBLE_EQ( 120.0, ControlObject::get(ConfigKey(m_sGroup2, "file_bpm"))); @@ -663,15 +662,15 @@ TEST_F(EngineSyncTest, RateChangeTestWeirdOrder) { ProcessBuffer(); // Set the file bpm of channel 1 to 160bpm. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(160), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(160)); m_pTrack1->trySetBeats(pBeats1); EXPECT_DOUBLE_EQ( 160.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm"))); // Set the file bpm of channel 2 to 120bpm. - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(120), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(120)); m_pTrack2->trySetBeats(pBeats2); // Set the rate slider of channel 1 to 1.2. @@ -689,15 +688,15 @@ TEST_F(EngineSyncTest, RateChangeTestWeirdOrder) { TEST_F(EngineSyncTest, RateChangeTestOrder3) { // Set the file bpm of channel 1 to 160bpm. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(160), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(160)); m_pTrack1->trySetBeats(pBeats1); EXPECT_DOUBLE_EQ( 160.0, ControlObject::get(ConfigKey(m_sGroup1, "file_bpm"))); // Set the file bpm of channel 2 to 120bpm. - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(120), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(120)); m_pTrack2->trySetBeats(pBeats2); EXPECT_DOUBLE_EQ( 120.0, ControlObject::get(ConfigKey(m_sGroup2, "file_bpm"))); @@ -737,13 +736,13 @@ TEST_F(EngineSyncTest, FollowerRateChange) { ProcessBuffer(); // Set the file bpm of channel 1 to 160bpm. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(160), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(160)); m_pTrack1->trySetBeats(pBeats1); // Set the file bpm of channel 2 to 120bpm. - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(120), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(120)); m_pTrack2->trySetBeats(pBeats2); // Set the rate slider of channel 1 to 1.2. @@ -785,15 +784,15 @@ TEST_F(EngineSyncTest, InternalRateChangeTest) { EXPECT_TRUE(isFollower(m_sGroup2)); // Set the file bpm of channel 1 to 160bpm. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(160), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(160)); m_pTrack1->trySetBeats(pBeats1); EXPECT_DOUBLE_EQ(160.0, ControlObject::getControl(ConfigKey(m_sGroup1, "file_bpm"))->get()); // Set the file bpm of channel 2 to 120bpm. - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(120), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(120)); m_pTrack2->trySetBeats(pBeats2); EXPECT_DOUBLE_EQ(120.0, ControlObject::getControl(ConfigKey(m_sGroup2, "file_bpm"))->get()); @@ -839,11 +838,11 @@ TEST_F(EngineSyncTest, InternalRateChangeTest) { TEST_F(EngineSyncTest, LeaderStopSliderCheck) { // If the leader is playing, and stop is pushed, the sliders should stay the same. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(120), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(120)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(128), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(128)); m_pTrack2->trySetBeats(pBeats2); auto pButtonLeaderSync1 = @@ -885,8 +884,8 @@ TEST_F(EngineSyncTest, EnableOneDeckInitsLeader) { ProcessBuffer(); // Set up the deck to play. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack1->trySetBeats(pBeats1); ControlObject::getControl(ConfigKey(m_sGroup1, "rate")) ->set(getRateSliderValue(1.0)); @@ -913,8 +912,8 @@ TEST_F(EngineSyncTest, EnableOneDeckInitsLeader) { ConfigKey(m_sInternalClockGroup, "beat_distance"))); // Enable second deck, bpm and beat distance should still match original setting. - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(140), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(140)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup2, "rate")) ->set(getRateSliderValue(1.0)); @@ -942,11 +941,11 @@ TEST_F(EngineSyncTest, MomentarySyncAlgorithmTwo) { m_pConfig->set(ConfigKey("[BPM]", "sync_lock_algorithm"), ConfigValue(EngineSync::PREFER_LOCK_BPM)); - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(120), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(120)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(128), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(128)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup2, "play"))->set(1.0); @@ -964,8 +963,8 @@ TEST_F(EngineSyncTest, MomentarySyncAlgorithmTwo) { TEST_F(EngineSyncTest, EnableOneDeckInitializesLeader) { // Enabling sync on a deck causes it to be leader, and sets bpm and clock. // Set the deck to play. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack1->trySetBeats(pBeats1); ControlObject::getControl(ConfigKey(m_sGroup1, "rate")) ->set(getRateSliderValue(1.0)); @@ -1155,8 +1154,8 @@ TEST_F(EngineSyncTest, EnableOneDeckSliderUpdates) { auto pButtonSyncEnabled1 = std::make_unique(m_sGroup1, "sync_enabled"); - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack1->trySetBeats(pBeats1); ControlObject::getControl(ConfigKey(m_sGroup1, "rate")) ->set(getRateSliderValue(1.0)); @@ -1183,13 +1182,13 @@ TEST_F(EngineSyncTest, SyncToNonSyncDeck) { auto pButtonSyncEnabled2 = std::make_unique(m_sGroup2, "sync_enabled"); - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack1->trySetBeats(pBeats1); ProcessBuffer(); ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.0)); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(100), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(100)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup2, "rate")) ->set(getRateSliderValue(1.0)); @@ -1271,13 +1270,13 @@ TEST_F(EngineSyncTest, MomentarySyncDependsOnPlayingStates) { std::make_unique(m_sGroup2, "sync_enabled"); // Set up decks so they can be playing, and start deck 1. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(100), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(100)); m_pTrack1->trySetBeats(pBeats1); ControlObject::set(ConfigKey(m_sGroup1, "rate"), getRateSliderValue(1.0)); ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack2->trySetBeats(pBeats2); ControlObject::set(ConfigKey(m_sGroup2, "rate"), getRateSliderValue(1.0)); ControlObject::set(ConfigKey(m_sGroup2, "play"), 1.0); @@ -1347,8 +1346,8 @@ TEST_F(EngineSyncTest, EjectTrackSyncRemains) { std::make_unique(m_sGroup2, "sync_enabled"); auto pButtonEject1 = std::make_unique(m_sGroup1, "eject"); - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(120), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(120)); m_pTrack1->trySetBeats(pBeats1); pButtonSyncEnabled1->set(1.0); ProcessBuffer(); @@ -1373,8 +1372,8 @@ TEST_F(EngineSyncTest, EjectTrackSyncRemains) { m_pMixerDeck1->loadFakeTrack(false, 128.0); EXPECT_DOUBLE_EQ(128.0, ControlObject::getControl(ConfigKey(m_sGroup1, "bpm"))->get()); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(135), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(135)); m_pTrack2->trySetBeats(pBeats2); pButtonSyncEnabled2->set(1.0); ProcessBuffer(); @@ -1394,16 +1393,16 @@ TEST_F(EngineSyncTest, EjectTrackSyncRemains) { TEST_F(EngineSyncTest, FileBpmChangesDontAffectLeader) { // If filebpm changes, don't treat it like a rate change unless it's the leader. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(100), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(100)); m_pTrack1->trySetBeats(pBeats1); auto pButtonSyncEnabled1 = std::make_unique(m_sGroup1, "sync_enabled"); pButtonSyncEnabled1->set(1.0); ProcessBuffer(); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(120), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(120)); m_pTrack2->trySetBeats(pBeats2); auto pButtonSyncEnabled2 = std::make_unique(m_sGroup2, "sync_enabled"); @@ -1414,8 +1413,8 @@ TEST_F(EngineSyncTest, FileBpmChangesDontAffectLeader) { EXPECT_TRUE(isFollower(m_sGroup2)); // Update the leader's beats -- update the internal clock - pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(160), mixxx::audio::kStartFramePos); + pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(160)); m_pTrack1->trySetBeats(pBeats1); EXPECT_DOUBLE_EQ( 160.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm"))); @@ -1423,8 +1422,8 @@ TEST_F(EngineSyncTest, FileBpmChangesDontAffectLeader) { EXPECT_TRUE(isSoftLeader(m_sGroup1)); // Update follower beats -- don't update internal clock. - pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(140), mixxx::audio::kStartFramePos); + pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(140)); m_pTrack2->trySetBeats(pBeats2); EXPECT_DOUBLE_EQ( 160.0, ControlObject::get(ConfigKey(m_sInternalClockGroup, "bpm"))); @@ -1436,8 +1435,8 @@ TEST_F(EngineSyncTest, ExplicitLeaderPostProcessed) { auto pButtonLeaderSync1 = std::make_unique(m_sGroup1, "sync_mode"); pButtonLeaderSync1->set(static_cast(SyncMode::LeaderExplicit)); - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(160), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(160)); m_pTrack1->trySetBeats(pBeats1); ProcessBuffer(); ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0); @@ -1459,8 +1458,8 @@ TEST_F(EngineSyncTest, ZeroBPMRateAdjustIgnored) { ->set(getRateSliderValue(1.0)); ProcessBuffer(); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(120), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(120)); m_pTrack2->trySetBeats(pBeats2); auto pButtonSyncEnabled2 = std::make_unique(m_sGroup2, "sync_enabled"); @@ -1498,8 +1497,8 @@ TEST_F(EngineSyncTest, BeatDistanceBeforeStart) { // If the start position is before zero, we should still initialize the beat distance // correctly. Unfortunately, this currently doesn't work. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(128), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(128)); m_pTrack1->trySetBeats(pBeats1); ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(1.0); ControlObject::set(ConfigKey(m_sGroup1, "playposition"), -.05); @@ -1516,11 +1515,11 @@ TEST_F(EngineSyncTest, BeatDistanceBeforeStart) { TEST_F(EngineSyncTest, ZeroLatencyRateChangeNoQuant) { // Confirm that a rate change in an explicit leader is instantly communicated // to followers. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(128), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(128)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(160), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(160)); m_pTrack2->trySetBeats(pBeats2); // Make Channel2 leader to weed out any channel ordering issues. @@ -1566,11 +1565,11 @@ TEST_F(EngineSyncTest, ZeroLatencyRateChangeNoQuant) { TEST_F(EngineSyncTest, ZeroLatencyRateChangeQuant) { // Confirm that a rate change in an explicit leader is instantly communicated // to followers. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(128), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(128)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(160), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(160)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(1.0); @@ -1621,11 +1620,11 @@ TEST_F(EngineSyncTest, ZeroLatencyRateChangeQuant) { TEST_F(EngineSyncTest, ZeroLatencyRateDiffQuant) { // Confirm that a rate change in an explicit leader is instantly communicated // to followers. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(128), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(128)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(160), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(160)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup2, "quantize"))->set(1.0); @@ -1677,11 +1676,11 @@ TEST_F(EngineSyncTest, ZeroLatencyRateDiffQuant) { // need to check. The Sync feature is unfortunately brittle. // This test exercises https://bugs.launchpad.net/mixxx/+bug/1884324 TEST_F(EngineSyncTest, ActivatingSyncDoesNotCauseDrifting) { - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(150), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(150)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(150), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(150)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(0.0); @@ -1723,11 +1722,11 @@ TEST_F(EngineSyncTest, ActivatingSyncDoesNotCauseDrifting) { } TEST_F(EngineSyncTest, HalfDoubleBpmTest) { - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(70), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(70)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(140), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(140)); m_pTrack2->trySetBeats(pBeats2); // Mixxx will choose the first playing deck to be leader. Let's start deck 2 first. @@ -1822,11 +1821,11 @@ TEST_F(EngineSyncTest, HalfDoubleBpmTest) { TEST_F(EngineSyncTest, HalfDoubleThenPlay) { // If a deck plays that had its multiplier set, we need to reset the // internal clock. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(80), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(80)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(175), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(175)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup1, "rate")) ->set(getRateSliderValue(1.0)); @@ -1947,11 +1946,11 @@ TEST_F(EngineSyncTest, HalfDoubleThenPlay) { TEST_F(EngineSyncTest, HalfDoubleInternalClockTest) { // If we set the file_bpm CO's directly, the correct signals aren't fired. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(70), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(70)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(140), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(140)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(1.0); @@ -1989,13 +1988,13 @@ TEST_F(EngineSyncTest, HalfDoubleConsistency) { const int numBeats = 100; QVector beats1 = createBeatVector(startOffsetFrames, numBeats, beatLengthFrames); - auto pBeats1 = mixxx::BeatMap::makeBeatMap(m_pTrack1->getSampleRate(), QString(), beats1); + auto pBeats1 = mixxx::Beats::fromBeatPositions(m_pTrack1->getSampleRate(), beats1); m_pTrack1->trySetBeats(pBeats1); beatLengthFrames = 60.0 * 44100 / 145.0; QVector beats2 = createBeatVector(startOffsetFrames, numBeats, beatLengthFrames); - auto pBeats2 = mixxx::BeatMap::makeBeatMap(m_pTrack2->getSampleRate(), QString(), beats2); + auto pBeats2 = mixxx::Beats::fromBeatPositions(m_pTrack2->getSampleRate(), beats2); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup1, "play"))->set(1.0); @@ -2024,11 +2023,11 @@ TEST_F(EngineSyncTest, HalfDoubleEachOther) { // Confirm that repeated sync with both decks leads to the same // Half/Double decision. // This test demonstrates https://bugs.launchpad.net/mixxx/+bug/1921962 - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(144), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(144)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(105), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(105)); m_pTrack2->trySetBeats(pBeats2); // Threshold 1.414 sqrt(2); @@ -2053,8 +2052,8 @@ TEST_F(EngineSyncTest, HalfDoubleEachOther) { // 105 / 75 = 1.40 // expect 75 BPM - mixxx::BeatsPointer pBeats1b = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(150), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1b = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(150)); m_pTrack1->trySetBeats(pBeats1b); EXPECT_DOUBLE_EQ(150.0, @@ -2075,8 +2074,8 @@ TEST_F(EngineSyncTest, HalfDoubleEachOther) { TEST_F(EngineSyncTest, SetFileBpmUpdatesLocalBpm) { ControlObject::getControl(ConfigKey(m_sGroup1, "beat_distance"))->set(0.2); - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack1->trySetBeats(pBeats1); EXPECT_EQ( mixxx::Bpm(130.0), m_pEngineSync->getSyncableForGroup(m_sGroup1)->getBaseBpm()); @@ -2088,16 +2087,16 @@ TEST_F(EngineSyncTest, SyncPhaseToPlayingNonSyncDeck) { auto pButtonSyncEnabled1 = std::make_unique(m_sGroup1, "sync_enabled"); - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack1->trySetBeats(pBeats1); ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(1.0); auto pButtonSyncEnabled2 = std::make_unique(m_sGroup2, "sync_enabled"); ControlObject::set(ConfigKey(m_sGroup2, "rate_ratio"), 1.0); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(100), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(100)); m_pTrack2->trySetBeats(pBeats2); // Set the sync deck playing with nothing else active. @@ -2180,8 +2179,8 @@ TEST_F(EngineSyncTest, SyncPhaseToPlayingNonSyncDeck) { std::make_unique(m_sGroup3, "sync_enabled"); ControlObject::set(ConfigKey(m_sGroup3, "beat_distance"), 0.6); ControlObject::set(ConfigKey(m_sGroup2, "rate_ratio"), 1.0); - mixxx::BeatsPointer pBeats3 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack3->getSampleRate(), mixxx::Bpm(140), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats3 = mixxx::Beats::fromConstTempo( + m_pTrack3->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(140)); m_pTrack3->trySetBeats(pBeats3); // This will sync to the first deck here and not the second (lp1784185) pButtonSyncEnabled3->set(1.0); @@ -2226,11 +2225,11 @@ TEST_F(EngineSyncTest, UserTweakBeatDistance) { // If a deck has a user tweak, and another deck stops such that the first // is used to reseed the leader beat distance, make sure the user offset // is reset. - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(128), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(128)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(128), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(128)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup1, "quantize"))->set(1.0); @@ -2279,11 +2278,11 @@ TEST_F(EngineSyncTest, UserTweakPreservedInSeek) { // This is about 128 bpm, but results in nice round numbers of samples. const double kDivisibleBpm = 44100.0 / 344.0; - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(kDivisibleBpm), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(kDivisibleBpm)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup2, "sync_enabled"))->set(1); @@ -2360,11 +2359,11 @@ TEST_F(EngineSyncTest, FollowerUserTweakPreservedInLeaderChange) { // This is about 128 bpm, but results in nice round numbers of samples. const double kDivisibleBpm = 44100.0 / 344.0; - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(kDivisibleBpm), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(kDivisibleBpm)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup1, "sync_leader"))->set(1); @@ -2411,11 +2410,11 @@ TEST_F(EngineSyncTest, FollowerUserTweakPreservedInSyncDisable) { // Ensure that when a follower disables sync, a phase seek is not performed. // This is about 128 bpm, but results in nice round numbers of samples. const double kDivisibleBpm = 44100.0 / 344.0; - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(kDivisibleBpm), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(kDivisibleBpm)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup1, "sync_leader"))->set(1); @@ -2447,11 +2446,11 @@ TEST_F(EngineSyncTest, LeaderUserTweakPreservedInLeaderChange) { // This is about 128 bpm, but results in nice round numbers of samples. const double kDivisibleBpm = 44100.0 / 344.0; - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(kDivisibleBpm), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(kDivisibleBpm)); m_pTrack1->trySetBeats(pBeats1); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack2->trySetBeats(pBeats2); ControlObject::getControl(ConfigKey(m_sGroup1, "sync_leader"))->set(1); @@ -2496,8 +2495,8 @@ TEST_F(EngineSyncTest, LeaderUserTweakPreservedInLeaderChange) { } TEST_F(EngineSyncTest, LeaderBpmNeverZero) { - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(128), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(128)); m_pTrack1->trySetBeats(pBeats1); auto pButtonSyncEnabled1 = std::make_unique(m_sGroup1, "sync_enabled"); @@ -2530,13 +2529,13 @@ TEST_F(EngineSyncTest, QuantizeImpliesSyncPhase) { auto pButtonBeatsync1 = std::make_unique(m_sGroup1, "beatsync"); auto pButtonBeatsyncPhase1 = std::make_unique(m_sGroup1, "beatsync_phase"); - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack1->trySetBeats(pBeats1); ControlObject::set(ConfigKey(m_sGroup2, "rate"), getRateSliderValue(1.0)); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(100), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(100)); m_pTrack2->trySetBeats(pBeats2); ProcessBuffer(); @@ -2628,8 +2627,8 @@ TEST_F(EngineSyncTest, QuantizeImpliesSyncPhase) { TEST_F(EngineSyncTest, SeekStayInPhase) { ControlObject::set(ConfigKey(m_sGroup1, "quantize"), 1.0); - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack1->trySetBeats(pBeats1); ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0); @@ -2651,8 +2650,8 @@ TEST_F(EngineSyncTest, SeekStayInPhase) { ControlObject::set(ConfigKey(m_sGroup1, "play"), 0.0); ProcessBuffer(); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack2->trySetBeats(pBeats2); ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0); @@ -2674,8 +2673,8 @@ TEST_F(EngineSyncTest, SeekStayInPhase) { TEST_F(EngineSyncTest, SyncWithoutBeatgrid) { // this tests bug lp1783020, notresetting rate when other deck has no beatgrid - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(128), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(128)); m_pTrack1->trySetBeats(pBeats1); m_pTrack2->trySetBeats(mixxx::BeatsPointer()); @@ -2693,13 +2692,13 @@ TEST_F(EngineSyncTest, SyncWithoutBeatgrid) { } TEST_F(EngineSyncTest, QuantizeHotCueActivate) { - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack1->trySetBeats(pBeats1); auto pHotCue2Activate = std::make_unique(m_sGroup2, "hotcue_1_activate"); - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(100), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(100)); m_pTrack2->trySetBeats(pBeats2); @@ -2760,8 +2759,8 @@ TEST_F(EngineSyncTest, ChangeBeatGrid) { auto pButtonSyncEnabled2 = std::make_unique(m_sGroup2, "sync_enabled"); // set beatgrid for deck 1 - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(130), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(130)); m_pTrack1->trySetBeats(pBeats1); pButtonSyncEnabled1->set(1.0); ControlObject::set(ConfigKey(m_sGroup1, "play"), 1.0); @@ -2794,8 +2793,8 @@ TEST_F(EngineSyncTest, ChangeBeatGrid) { EXPECT_TRUE(isFollower(m_sGroup2)); // Load a new beatgrid during playing, this happens when the analyser is finished. - mixxx::BeatsPointer pBeats2 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(140), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2 = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(140)); m_pTrack2->trySetBeats(pBeats2); ProcessBuffer(); @@ -2819,8 +2818,8 @@ TEST_F(EngineSyncTest, ChangeBeatGrid) { EXPECT_TRUE(isSoftLeader(m_sGroup2)); // Load a new beatgrid again, this happens when the user adjusts the beatgrid - mixxx::BeatsPointer pBeats2n = mixxx::BeatGrid::makeBeatGrid( - m_pTrack2->getSampleRate(), mixxx::Bpm(75), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats2n = mixxx::Beats::fromConstTempo( + m_pTrack2->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(75)); m_pTrack2->trySetBeats(pBeats2n); ProcessBuffer(); @@ -2835,14 +2834,13 @@ TEST_F(EngineSyncTest, ChangeBeatGrid) { TEST_F(EngineSyncTest, BeatMapQuantizePlay) { // This test demonstates https://bugs.launchpad.net/mixxx/+bug/1874918 - mixxx::BeatsPointer pBeats1 = mixxx::BeatGrid::makeBeatGrid( - m_pTrack1->getSampleRate(), mixxx::Bpm(120), mixxx::audio::kStartFramePos); + mixxx::BeatsPointer pBeats1 = mixxx::Beats::fromConstTempo( + m_pTrack1->getSampleRate(), mixxx::audio::kStartFramePos, mixxx::Bpm(120)); m_pTrack1->trySetBeats(pBeats1); constexpr auto kSampleRate = mixxx::audio::SampleRate(44100); - auto pBeats2 = mixxx::BeatMap::makeBeatMap(kSampleRate, - QString(), + auto pBeats2 = mixxx::Beats::fromBeatPositions(kSampleRate, // Add two beats at 120 Bpm QVector( {mixxx::audio::FramePos( diff --git a/src/test/seratobeatgridtest.cpp b/src/test/seratobeatgridtest.cpp index 3ce050bb7e2..41b985293c9 100644 --- a/src/test/seratobeatgridtest.cpp +++ b/src/test/seratobeatgridtest.cpp @@ -5,8 +5,7 @@ #include #include -#include "track/beatgrid.h" -#include "track/beatmap.h" +#include "track/beats.h" #include "track/serato/beatgrid.h" #include "track/serato/beatsimporter.h" #include "util/memory.h" @@ -116,8 +115,8 @@ TEST_F(SeratoBeatGridTest, SerializeBeatgrid) { constexpr mixxx::Bpm bpm(120.0); const auto sampleRate = mixxx::audio::SampleRate(44100); EXPECT_EQ(sampleRate.isValid(), true); - const auto pBeats = mixxx::BeatGrid::makeBeatGrid( - sampleRate, bpm, mixxx::audio::kStartFramePos); + const auto pBeats = mixxx::Beats::fromConstTempo( + sampleRate, mixxx::audio::kStartFramePos, bpm); const auto signalInfo = mixxx::audio::SignalInfo(mixxx::audio::ChannelCount(2), sampleRate); const auto duration = mixxx::Duration::fromSeconds(300); @@ -153,8 +152,7 @@ TEST_F(SeratoBeatGridTest, SerializeBeatMap) { // Check the const beatmap { - const auto pBeats = mixxx::BeatMap::makeBeatMap( - sampleRate, QString("Test"), beatPositionsFrames); + const auto pBeats = mixxx::Beats::fromBeatPositions(sampleRate, beatPositionsFrames); // Check that the first section's BPM is 100 EXPECT_EQ(pBeats->getBpmAroundPosition( mixxx::audio::FramePos(initialFrameOffset + @@ -191,8 +189,7 @@ TEST_F(SeratoBeatGridTest, SerializeBeatMap) { ASSERT_EQ(beatPositionsFrames.size(), kNumBeats120BPM + kNumBeats60BPM); { - const auto pBeats = mixxx::BeatMap::makeBeatMap( - sampleRate, QString("Test"), beatPositionsFrames); + const auto pBeats = mixxx::Beats::fromBeatPositions(sampleRate, beatPositionsFrames); // Check that the first section'd BPM is 100 EXPECT_EQ(pBeats->getBpmAroundPosition( mixxx::audio::FramePos(initialFrameOffset + @@ -242,8 +239,7 @@ TEST_F(SeratoBeatGridTest, SerializeBeatMap) { beatPositionsFrames.append(beatPositionFrames); { - const auto pBeats = mixxx::BeatMap::makeBeatMap( - sampleRate, QString("Test"), beatPositionsFrames); + const auto pBeats = mixxx::Beats::fromBeatPositions(sampleRate, beatPositionsFrames); // Check that the first section's BPM is 100 EXPECT_EQ(pBeats->getBpmAroundPosition( mixxx::audio::FramePos(initialFrameOffset + diff --git a/src/track/beatfactory.cpp b/src/track/beatfactory.cpp index 110dc2c4d29..8d4b4ca0c11 100644 --- a/src/track/beatfactory.cpp +++ b/src/track/beatfactory.cpp @@ -83,15 +83,15 @@ mixxx::BeatsPointer BeatFactory::makePreferredBeats( constantRegions, sampleRate, &firstBeat); if (firstBeat.isValid()) { firstBeat = BeatUtils::adjustPhase(firstBeat, constBPM, sampleRate, beats); - auto pGrid = mixxx::BeatGrid::makeBeatGrid( - sampleRate, constBPM, firstBeat.toNearestFrameBoundary(), subVersion); + auto pGrid = mixxx::Beats::fromConstTempo( + sampleRate, firstBeat.toNearestFrameBoundary(), constBPM, subVersion); return pGrid; } else { qWarning() << "Failed to create beat grid: Invalid first beat"; } } else if (version == BEAT_MAP_VERSION) { QVector ironedBeats = BeatUtils::getBeats(constantRegions); - auto pBeatMap = mixxx::BeatMap::makeBeatMap(sampleRate, subVersion, ironedBeats); + auto pBeatMap = mixxx::Beats::fromBeatPositions(sampleRate, ironedBeats, subVersion); return pBeatMap; } else { qDebug() << "ERROR: Could not determine what type of beatgrid to create."; diff --git a/src/track/beatmap.cpp b/src/track/beatmap.cpp index b09f103fb9a..e82929abbec 100644 --- a/src/track/beatmap.cpp +++ b/src/track/beatmap.cpp @@ -12,7 +12,6 @@ #include #include -#include "track/beatgrid.h" #include "track/beatutils.h" #include "track/track.h" #include "util/math.h" @@ -605,7 +604,7 @@ BeatsPointer BeatMap::setBpm(mixxx::Bpm bpm) { const auto firstBeatPosition = mixxx::audio::FramePos(m_beats.first().frame_position()); DEBUG_ASSERT(firstBeatPosition.isValid()); - return mixxx::BeatGrid::makeBeatGrid(m_sampleRate, bpm, firstBeatPosition); + return fromConstTempo(m_sampleRate, firstBeatPosition, bpm); } } // namespace mixxx diff --git a/src/track/beats.cpp b/src/track/beats.cpp index 459c35b2fd9..3a8b870d49d 100644 --- a/src/track/beats.cpp +++ b/src/track/beats.cpp @@ -32,6 +32,23 @@ mixxx::BeatsPointer Beats::fromByteArray( return pBeats; } +// static +BeatsPointer Beats::fromConstTempo( + audio::SampleRate sampleRate, + audio::FramePos position, + Bpm bpm, + const QString& subVersion) { + return BeatGrid::makeBeatGrid(sampleRate, bpm, position, subVersion); +} + +// static +BeatsPointer Beats::fromBeatPositions( + audio::SampleRate sampleRate, + const QVector& beatPositions, + const QString& subVersion) { + return BeatMap::makeBeatMap(sampleRate, subVersion, beatPositions); +} + int Beats::numBeatsInRange(audio::FramePos startPosition, audio::FramePos endPosition) const { audio::FramePos lastPosition = audio::kStartFramePos; int i = 1; diff --git a/src/track/beats.h b/src/track/beats.h index f255c48beab..54afb8fb98b 100644 --- a/src/track/beats.h +++ b/src/track/beats.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "audio/frame.h" @@ -36,6 +37,17 @@ class Beats { const QString& beatsSubVersion, const QByteArray& beatsSerialized); + static mixxx::BeatsPointer fromConstTempo( + audio::SampleRate sampleRate, + audio::FramePos position, + Bpm bpm, + const QString& subVersion = QString()); + + static mixxx::BeatsPointer fromBeatPositions( + audio::SampleRate sampleRate, + const QVector& beatPositions, + const QString& subVersion = QString()); + enum class BpmScale { Double, Halve, diff --git a/src/track/track.cpp b/src/track/track.cpp index e3125bffb6c..d99c36f35ae 100644 --- a/src/track/track.cpp +++ b/src/track/track.cpp @@ -7,8 +7,6 @@ #include "library/library_prefs.h" #include "moc_track.cpp" #include "sources/metadatasource.h" -#include "track/beatgrid.h" -#include "track/beatmap.h" #include "track/trackref.h" #include "util/assert.h" #include "util/color/color.h" @@ -355,9 +353,9 @@ bool Track::trySetBpmWhileLocked(mixxx::Bpm bpm) { if (!cuePosition.isValid()) { cuePosition = mixxx::audio::kStartFramePos; } - auto pBeats = mixxx::BeatGrid::makeBeatGrid(getSampleRate(), - bpm, - cuePosition); + auto pBeats = mixxx::Beats::fromConstTempo(getSampleRate(), + cuePosition, + bpm); return trySetBeatsWhileLocked(std::move(pBeats)); } else if (m_pBeats->getBpm() != bpm) { // Continue with the regular cases @@ -1083,9 +1081,8 @@ bool Track::importPendingBeatsWhileLocked() { // The sample rate is supposed to be consistent DEBUG_ASSERT(m_record.getStreamInfoFromSource()->getSignalInfo().getSampleRate() == m_record.getMetadata().getStreamInfo().getSignalInfo().getSampleRate()); - const auto pBeats = mixxx::BeatMap::makeBeatMap( + const auto pBeats = mixxx::Beats::fromBeatPositions( m_record.getStreamInfoFromSource()->getSignalInfo().getSampleRate(), - QString(), m_pBeatsImporterPending->importBeatsAndApplyTimingOffset( getLocation(), *m_record.getStreamInfoFromSource())); DEBUG_ASSERT(m_pBeatsImporterPending->isEmpty()); diff --git a/src/util/db/fwdsqlquery.cpp b/src/util/db/fwdsqlquery.cpp index 7c8e5ea8838..82e5ddee442 100644 --- a/src/util/db/fwdsqlquery.cpp +++ b/src/util/db/fwdsqlquery.cpp @@ -39,14 +39,31 @@ FwdSqlQuery::FwdSqlQuery( m_prepared(prepareQuery(*this, statement)) { if (!m_prepared) { DEBUG_ASSERT(!database.isOpen() || hasError()); - kLogger.critical() - << "Failed to prepare" - << statement - << ":" - << lastError(); + if (hasDuplicateColumnNameError()) { + // Special case: Errors caused by re-adding a column when + // re-appliying a schema migration are expected and we + // should not report them as an error. + kLogger.debug() + << "Failed to prepare schema migration statement" + << statement + << ":" + << lastError(); + } else { + kLogger.critical() + << "Failed to prepare statement" + << statement + << ":" + << lastError(); + } } } +bool FwdSqlQuery::hasDuplicateColumnNameError() const { + return hasError() && + lastError().databaseText().startsWith( + QStringLiteral("duplicate column name: ")); +} + bool FwdSqlQuery::execPrepared() { DEBUG_ASSERT(isPrepared()); DEBUG_ASSERT(!hasError()); diff --git a/src/util/db/fwdsqlquery.h b/src/util/db/fwdsqlquery.h index c03aef77967..5a08a04ba33 100644 --- a/src/util/db/fwdsqlquery.h +++ b/src/util/db/fwdsqlquery.h @@ -49,6 +49,7 @@ class FwdSqlQuery : protected QSqlQuery { return lastError().isValid() && (lastError().type() != QSqlError::NoError); } + bool hasDuplicateColumnNameError() const; QSqlError lastError() const { return QSqlQuery::lastError(); } diff --git a/src/widget/woverview.cpp b/src/widget/woverview.cpp index 5c84af93e7b..8754c8961fa 100644 --- a/src/widget/woverview.cpp +++ b/src/widget/woverview.cpp @@ -304,6 +304,7 @@ void WOverview::onTrackAnalyzerProgress(TrackId trackId, AnalyzerProgress analyz void WOverview::slotTrackLoaded(TrackPointer pTrack) { Q_UNUSED(pTrack); // only used in DEBUG_ASSERT + //qDebug() << "WOverview::slotTrackLoaded()" << m_pCurrentTrack.get() << pTrack.get(); DEBUG_ASSERT(m_pCurrentTrack == pTrack); m_trackLoaded = true; if (m_pCurrentTrack) { diff --git a/tools/update_metainfo.py b/tools/update_metainfo.py new file mode 100755 index 00000000000..694c6244c1c --- /dev/null +++ b/tools/update_metainfo.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +import datetime +import os +import re + +import markdown +import bs4 +from lxml import etree + + +def parse_changelog(content): + for section in re.split("^## ", content.strip(), flags=re.MULTILINE)[1:]: + version, sep, description_md = section.partition("\n") + matchobj = re.match( + r"\[(?P\d\.\d\.\d)\]\([^\)]+\)" + r"(?P(?: \((?i:Unreleased|\d{4}-\d{2}-\d{2})\))?)", + version, + ) + if not matchobj: + continue + + attrib = { + "version": matchobj.group("number"), + } + try: + attrib["date"] = datetime.datetime.strptime( + matchobj.group("date"), " (%Y-%m-%d)" + ).strftime("%Y-%m-%d") + attrib["type"] = "stable" + except ValueError: + attrib["type"] = "development" + + soup = bs4.BeautifulSoup( + markdown.markdown(description_md), "html.parser" + ) + + # The description tag contains HTML-like markup, but only supports a + # few select tags. Hence we need to strip some tags that are not + # supported. + # + # See the specification for details: + # https://freedesktop.org/software/appstream/docs/chap-Metadata.html#tag-description + for tag in soup.find_all("a"): + tag.replace_with(tag.get_text()) + for tag in soup.find_all("h3"): + new_tag = soup.new_tag("p") + new_tag.string = tag.get_text() + tag.replace_with(new_tag) + desc = soup.new_tag("description") + desc.extend(soup.contents) + + release = etree.Element("release", attrib=attrib) + release.append(etree.fromstring(desc.prettify())) + yield release + + +def main(argv=None): + rootpath = os.path.join(os.path.dirname(__file__), "..") + metainfo_path = os.path.join( + rootpath, "res/linux/org.mixxx.Mixxx.metainfo.xml" + ) + parser = etree.XMLParser(remove_blank_text=True, remove_comments=False) + tree = etree.parse(metainfo_path, parser) + root = tree.getroot() + releases = root.find("releases") + releases.clear() + with open(os.path.join(rootpath, "CHANGELOG.md"), mode="r") as fp: + for release in parse_changelog(fp.read()): + releases.append(release) + tree.write( + metainfo_path, + encoding="UTF-8", + xml_declaration=True, + pretty_print=True, + ) + + +if __name__ == "__main__": + main()