diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 301b601d76c3..a1ebaaa63741 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,6 +4,31 @@ name: Build on: workflow_call: + inputs: + publish: + type: boolean + default: false + secrets: + AZURE_CLIENT_ID: + required: false + AZURE_CLIENT_SECRET: + required: false + AZURE_TENANT_ID: + required: false + DOWNLOADS_HOSTGATOR_DOT_MIXXX_DOT_ORG_KEY: + required: false + DOWNLOADS_HOSTGATOR_DOT_MIXXX_DOT_ORG_KEY_PASSWORD: + required: false + MACOS_CODESIGN_CERTIFICATE_P12_BASE64: + required: false + MACOS_CODESIGN_CERTIFICATE_PASSWORD: + required: false + MACOS_NOTARIZATION_APP_SPECIFIC_PASSWORD: + required: false + NETLIFY_BUILD_HOOK: + required: false + RRYAN_AT_MIXXX_DOT_ORG_GPG_PRIVATE_KEY: + required: false permissions: contents: read # to fetch code (actions/checkout) @@ -49,7 +74,6 @@ jobs: -DQML=OFF -DWAVPACK=ON -DVCPKG_TARGET_TRIPLET=x64-osx-min1100-release - -DVCPKG_DEFAULT_HOST_TRIPLET=x64-osx-min1100-release # TODO: Fix this broken test on macOS ctest_args: --exclude-regex DirectoryDAOTest.relocateDirectory cpack_generator: DragNDrop @@ -74,7 +98,6 @@ jobs: -DQML=ON -DWAVPACK=ON -DVCPKG_TARGET_TRIPLET=arm64-osx-min1100-release - -DVCPKG_DEFAULT_HOST_TRIPLET=x64-osx-min1100-release # TODO: Fix this broken test on macOS crosscompile: true cpack_generator: DragNDrop @@ -103,7 +126,6 @@ jobs: -DQML=ON -DWAVPACK=ON -DVCPKG_TARGET_TRIPLET=x64-windows-release - -DVCPKG_DEFAULT_HOST_TRIPLET=x64-windows-release cc: cl cxx: cl # TODO: Fix these broken tests on Windows @@ -400,8 +422,8 @@ jobs: if: always() && steps.package.outcome == 'failure' && runner.os == 'windows' uses: actions/upload-artifact@v4 with: - name: logs-packages-wix - path: D:/a/mixxx/mixxx/build/_CPack_Packages/win64/WIX/wix.log + name: ${{ matrix.os }}-logs-packages-wix + path: ${{ github.workspace }}/build/_CPack_Packages/win64/WIX/wix.log - name: "[Ubuntu] Import PPA GPG key" if: startsWith(matrix.os, 'ubuntu') && env.RRYAN_AT_MIXXX_DOT_ORG_GPG_PRIVATE_KEY != null @@ -458,7 +480,7 @@ jobs: # also generates metadata for file artifact and write it to the job # output using the artifacts_slug value. id: prepare_deploy - if: github.event_name == 'push' && matrix.artifacts_path != null + if: inputs.publish && matrix.artifacts_path != null shell: bash run: > if [[ "${GITHUB_REF}" =~ ^refs/tags/.* ]]; @@ -479,7 +501,7 @@ jobs: - 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 + if: runner.os == 'Windows' && inputs.publish && env.SSH_PRIVATE_KEY != null run: | if (Test-Path "C:\msys64\usr\bin") { $msysPath="C:\msys64" @@ -514,7 +536,7 @@ jobs: 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 + if: inputs.publish && env.SSH_PRIVATE_KEY != null shell: bash env: SSH_AUTH_SOCK: /tmp/ssh_agent.sock @@ -529,7 +551,7 @@ jobs: - name: "[macOS/Windows] Upload build to downloads.mixxx.org" # skip deploying Ubuntu builds to downloads.mixxx.org because these are deployed to the PPA - if: runner.os != 'Linux' && github.event_name == 'push' && env.SSH_AUTH_SOCK != null + if: runner.os != 'Linux' && inputs.publish && env.SSH_AUTH_SOCK != null shell: bash --login -eo pipefail "{0}" run: rsync --verbose --recursive --checksum --times --delay-updates "deploy/" "${SSH_USER}@${SSH_HOST}:${DESTDIR}/" env: @@ -575,7 +597,7 @@ jobs: - name: "Collect Artifacts Metadata & Write Manifest" # Retrieve the metadata from the matrix job's outputs, merge them into a # single JSON document and then deploy to the server. - if: github.event_name == 'push' && env.SSH_PASSWORD != null + if: inputs.publish && env.SSH_PASSWORD != null run: > if [[ "${GITHUB_REF}" =~ ^refs/tags/.* ]]; then @@ -593,7 +615,7 @@ jobs: SSH_PASSWORD: ${{ secrets.DOWNLOADS_HOSTGATOR_DOT_MIXXX_DOT_ORG_KEY_PASSWORD }} - name: "Set up SSH Agent" - if: github.event_name == 'push' && env.SSH_PRIVATE_KEY != null && env.MANIFEST_DIRTY != null && env.MANIFEST_DIRTY != '0' + if: inputs.publish && env.SSH_PRIVATE_KEY != null && env.MANIFEST_DIRTY != null && env.MANIFEST_DIRTY != '0' shell: bash env: SSH_AUTH_SOCK: /tmp/ssh_agent.sock @@ -607,7 +629,7 @@ jobs: echo "SSH_AUTH_SOCK=${SSH_AUTH_SOCK}" >> "${GITHUB_ENV}" - name: "Deploy Manifest" - if: github.event_name == 'push' && env.SSH_AUTH_SOCK != null + if: inputs.publish && env.SSH_AUTH_SOCK != null shell: bash run: rsync --verbose --recursive --checksum --times --delay-updates "deploy/" "${SSH_USER}@${SSH_HOST}:${DESTDIR}/" env: diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml new file mode 100644 index 000000000000..46e617d65ba3 --- /dev/null +++ b/.github/workflows/develop.yml @@ -0,0 +1,88 @@ +name: Pull request or branch build + +on: + pull_request: + types: + - opened + - synchronize + - reopened + - edited + push: + branches: + - "*" + - "!main" + - "![0-9].[0-9]" + workflow_dispatch: + +permissions: + contents: read # to fetch code (actions/checkout) + checks: write # to create new checks (coverallsapp/github-action) + +jobs: + stop-build: + name: Check if build should be stopped + runs-on: ubuntu-latest + outputs: + result: ${{ github.event_name == 'push' && steps.stop-build.outputs.result || 'false' }} + steps: + - name: Check if there is an open PR for this branch + id: stop-build + if: ${{ github.event_name == 'push' }} + uses: actions/github-script@v7 + env: + ORGANIZATION: mixxxdj + REPOSITORY: mixxx + with: + script: | + try { + const branch = context.ref.replace('refs/heads/', ''); + const { data: pullRequests } = await github.rest.pulls.list({ + owner: process.env.ORGANIZATION, + repo: process.env.REPOSITORY, + head: `${context.repo.owner}:${branch}`, + state: 'open' + }); + console.log(`There is ${pullRequests.length} PR open upstream for branch '${context.repo.owner}:${branch}'`); + return pullRequests.length != 0; + } catch (error) { + console.log(`Didn't find a PR for branch '${context.repo.owner}:${branch}' on '${process.env.ORGANIZATION}/${process.env.REPOSITORY}'.`); + return false; + } + + pre-commit: + if: needs.stop-build.outputs.result == 'false' + needs: + - stop-build + uses: ./.github/workflows/pre-commit.yml + with: + pull_request: ${{ github.event_name == 'pull_request' }} + + checks: + if: needs.stop-build.outputs.result == 'false' + needs: + - stop-build + uses: ./.github/workflows/checks.yml + + git: + if: github.event_name == 'pull_request' + uses: ./.github/workflows/git.yml + + build: + if: needs.stop-build.outputs.result == 'false' + needs: + - stop-build + uses: ./.github/workflows/build.yml + + # This task is used as a probe for auto merge + # In the future, it could also be used to perform a status update (e.g once the whole CI is passing + is ready for review, add a specific label such as `need review`) + ready: + name: Ready to merge + needs: + - pre-commit + - checks + - git + - build + runs-on: ubuntu-latest + steps: + - name: Ready to go + run: "exit 0" diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index aa75be8787d8..97e9302ed689 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -4,6 +4,10 @@ name: pre-commit on: workflow_call: + inputs: + pull_request: + type: boolean + default: false permissions: contents: read # to fetch code (actions/checkout) @@ -36,26 +40,19 @@ jobs: git config --global --add safe.directory "${GITHUB_WORKSPACE}" git config --global --list - - name: "Detect code style issues (push)" + - name: "Detect code style issues" uses: pre-commit/action@v3.0.1 - if: github.event_name == 'push' - # There are too many files in the repo that have formatting issues. We'll - # disable these checks for now when pushing directly (but still run these - # on Pull Requests!). env: - SKIP: clang-format,eslint,no-commit-to-branch - - - name: "Detect code style issues (pull_request)" - uses: pre-commit/action@v3.0.1 - if: github.event_name == 'pull_request' - env: - SKIP: no-commit-to-branch + # There are too many files in the repo that have formatting issues. We'll + # disable these checks for now when pushing directly (but still run these + # on Pull Requests!). + SKIP: ${{ inputs.pull_request && 'no-commit-to-branch' || 'clang-format,eslint,no-commit-to-branch' }} # https://github.com/paleite/eslint-plugin-diff?tab=readme-ov-file#ci-setup - ESLINT_PLUGIN_DIFF_COMMIT: ${{ github.event.pull_request.base.ref }} + ESLINT_PLUGIN_DIFF_COMMIT: ${{ inputs.pull_request && github.event.pull_request.base.ref || '' }} with: # HEAD is the not yet integrated PR merge commit +refs/pull/xxxx/merge # HEAD^1 is the PR target branch and HEAD^2 is the HEAD of the source branch - extra_args: --from-ref HEAD^1 --to-ref HEAD + extra_args: ${{ inputs.pull_request && '--from-ref HEAD^1 --to-ref HEAD' || '' }} - name: "Generate patch file" if: failure() diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml deleted file mode 100644 index 3be26454ed91..000000000000 --- a/.github/workflows/pull-request.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Pull request - -on: - pull_request: - types: - - opened - - synchronize - - reopened - - edited - -jobs: - pre-commit: - uses: ./.github/workflows/pre-commit.yml - - checks: - uses: ./.github/workflows/checks.yml - - git: - uses: ./.github/workflows/git.yml - - build: - uses: ./.github/workflows/build.yml - - # This task is used as a probe for auto merge - # In the future, it could also be used to perform a status update (e.g once the whole CI is passing + is ready for review, add a specific label such as `need review`) - ready: - name: Ready to merge - needs: - - pre-commit - - checks - - git - - build - runs-on: ubuntu-latest - steps: - - name: Ready to go - run: "exit 0" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8d8a39c586c6..6a539cef4eb4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,12 @@ env: ACTIVE_VERSIONS: |- {"2.5": "2.6", "2.6": "main"} +# Global allowed scopes for all actions +permissions: + contents: write # to sync branches + pull-requests: write # to sync branches + checks: write # to create new checks (coverallsapp/github-action) + jobs: checks: uses: ./.github/workflows/checks.yml @@ -23,9 +29,22 @@ jobs: build: uses: ./.github/workflows/build.yml + with: + publish: true + secrets: + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + DOWNLOADS_HOSTGATOR_DOT_MIXXX_DOT_ORG_KEY: ${{ secrets.DOWNLOADS_HOSTGATOR_DOT_MIXXX_DOT_ORG_KEY }} + DOWNLOADS_HOSTGATOR_DOT_MIXXX_DOT_ORG_KEY_PASSWORD: ${{ secrets.DOWNLOADS_HOSTGATOR_DOT_MIXXX_DOT_ORG_KEY_PASSWORD }} + MACOS_CODESIGN_CERTIFICATE_P12_BASE64: ${{ secrets.MACOS_CODESIGN_CERTIFICATE_P12_BASE64 }} + MACOS_CODESIGN_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CODESIGN_CERTIFICATE_PASSWORD }} + MACOS_NOTARIZATION_APP_SPECIFIC_PASSWORD: ${{ secrets.MACOS_NOTARIZATION_APP_SPECIFIC_PASSWORD }} + NETLIFY_BUILD_HOOK: ${{ secrets.NETLIFY_BUILD_HOOK }} + RRYAN_AT_MIXXX_DOT_ORG_GPG_PRIVATE_KEY: ${{ secrets.RRYAN_AT_MIXXX_DOT_ORG_GPG_PRIVATE_KEY }} sync: - if: ${{ github.ref != 'refs/heads/main' }} + if: ${{ github.ref != 'refs/heads/main' }} && ${{ github.repository == 'mixxxdj/mixxx' }} uses: ./.github/workflows/sync_branches.yml secrets: - pat_token: ${{ secrets.MIXXX_BRANCH_SYNC_PAT }} + MIXXX_BRANCH_SYNC_PAT: ${{ secrets.MIXXX_BRANCH_SYNC_PAT }} diff --git a/.github/workflows/sync_branches.yml b/.github/workflows/sync_branches.yml index 46312712ac3f..0b2ad00bc828 100644 --- a/.github/workflows/sync_branches.yml +++ b/.github/workflows/sync_branches.yml @@ -4,7 +4,7 @@ on: workflow_call: secrets: # PAT setup with content:write and pull_request:write - pat_token: + MIXXX_BRANCH_SYNC_PAT: required: true permissions: {} @@ -13,7 +13,8 @@ env: SYNC_COMMITTER_EMAIL: bot@mixxx.org SYNC_COMMITTER_NAME: Mixxx Bot - # This variable stores the map of Mixxx branches that still being developed. The key is the branch receiving support and the value is the next version in line + # This variable stores the map of Mixxx branches that still being developed. + # The key is the branch receiving support and the value is the next version in line # NOTE: this must be valid JSON! ACTIVE_VERSIONS: |- {"2.5": "2.6", "2.6": "main"} @@ -41,7 +42,7 @@ jobs: - name: "Check out repository" uses: actions/checkout@v4.1.7 with: - token: ${{ secrets.pat_token }} + token: ${{ secrets.MIXXX_BRANCH_SYNC_PAT }} fetch-depth: 0 persist-credentials: true @@ -99,7 +100,7 @@ jobs: FROM_BRANCH: ${{ github.ref_name }} TO_BRANCH: ${{ fromJSON(env.ACTIVE_VERSIONS)[github.ref_name] }} SYNC_BRANCH: sync-branch-${{ github.ref_name }}-to-${{ fromJSON(env.ACTIVE_VERSIONS)[github.ref_name] }} - GITHUB_TOKEN: ${{ secrets.pat_token }} + GITHUB_TOKEN: ${{ secrets.MIXXX_BRANCH_SYNC_PAT }} PULL_REQUEST_TITLE: Merge changes from `${{ github.ref_name }}` into `${{ fromJSON(env.ACTIVE_VERSIONS)[github.ref_name] }}` PULL_REQUEST_BODY: | New content has landed in the `${{ github.ref_name }}` branch, so let's merge the changes into `${{ fromJSON(env.ACTIVE_VERSIONS)[github.ref_name] }}` diff --git a/CHANGELOG.md b/CHANGELOG.md index 60772cee1f4d..d4d620d83462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -333,6 +333,23 @@ * Fix: import proper QtQml.Models module instead of qmllabs [#14675](https://github.com/mixxxdj/mixxx/pull/14675) * qmlwaveform: Fix moc in Qt 6.9.0 [#14649](https://github.com/mixxxdj/mixxx/pull/14649) +## [2.5.3](https://github.com/mixxxdj/mixxx/milestone/50) (unreleased) + +### Controller Mappings + +* Traktor Kontrol S4 Mk3: tempo offset per deck [#14882](https://github.com/mixxxdj/mixxx/pull/14882) +* Traktor Kontrol S4 Mk3: don`t duplicate beatloop_activate behaviour [#14992](https://github.com/mixxxdj/mixxx/pull/14992) +* Traktor Kontrol S3: allow full library navigation [#14980](https://github.com/mixxxdj/mixxx/pull/14980) + +### Misc + +* Broadcast preferences: make setting string translatable [#15023](https://github.com/mixxxdj/mixxx/pull/15023) +* Sound Hardware preference: add (?) linking to Sound APIs in the manual [#14935](https://github.com/mixxxdj/mixxx/pull/14935) +* xwax: do not try to "correct" for drift in absolute mode. [#14960](https://github.com/mixxxdj/mixxx/pull/14960) +* Fix column header text assignment [#14944](https://github.com/mixxxdj/mixxx/pull/14944) +* Remove runtime assert to not risk crashes [#15000](https://github.com/mixxxdj/mixxx/pull/15000) +* Windows: Update build environment to Visual Studio 2022 [#15006](https://github.com/mixxxdj/mixxx/pull/15006) + ## [2.5.2](https://github.com/mixxxdj/mixxx/milestone/49) (2025-06-13) ### Library diff --git a/CMakeLists.txt b/CMakeLists.txt index 06690f416299..a9c5f6974c38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,6 +48,98 @@ if(POLICY CMP0135) cmake_policy(SET CMP0135 NEW) endif() +if(((APPLE AND NOT IOS) OR WIN32) AND NOT IS_DIRECTORY "${MIXXX_VCPKG_ROOT}") + if(NOT DEFINED BUILDENV_BASEPATH) + if(DEFINED ENV{BUILDENV_BASEPATH}) + set(BUILDENV_BASEPATH "$ENV{BUILDENV_BASEPATH}") + else() + set(BUILDENV_BASEPATH "${CMAKE_SOURCE_DIR}/buildenv") + endif() + endif() + + if(NOT DEFINED BUILDENV_URL) + if(DEFINED ENV{BUILDENV_URL}) + set(BUILDENV_URL "$ENV{BUILDENV_URL}") + elseif(NOT DEFINED BUILDENV_URL) + message(FATAL_ERROR "BUILDENV_URL not specified") + endif() + endif() + + # Extract the filename from the URL + get_filename_component(FILENAME_WITH_EXTENSION "${BUILDENV_URL}" NAME) + + # Remove the extension from the filename (note that our buildenv names contain two dots) + string( + REGEX REPLACE + "\\.[^.]*$" + "" + BUILDENV_NAME + "${FILENAME_WITH_EXTENSION}" + ) + + message(STATUS "BUILDENV_NAME is: ${BUILDENV_NAME}") + + if( + NOT EXISTS "${BUILDENV_BASEPATH}/${BUILDENV_NAME}" + OR NOT IS_DIRECTORY "${BUILDENV_BASEPATH}/${BUILDENV_NAME}" + ) + if(NOT DEFINED BUILDENV_SHA256) + if(DEFINED ENV{BUILDENV_SHA256}) + set(BUILDENV_SHA256 "$ENV{BUILDENV_SHA256}") + elseif(NOT DEFINED BUILDENV_SHA256) + message(STATUS "BUILDENV_SHA256 not specified") + endif() + endif() + + if(NOT EXISTS "${BUILDENV_BASEPATH}/${BUILDENV_NAME}.zip") + message( + STATUS + "Downloading file ${BUILDENV_URL} to ${BUILDENV_BASEPATH} ..." + ) + file( + DOWNLOAD ${BUILDENV_URL} "${BUILDENV_BASEPATH}/${BUILDENV_NAME}.zip" + SHOW_PROGRESS + TLS_VERIFY ON + ) + else() + # Reference to suppress intentionally unused variable warnings + set(_dummy "${BUILDENV_URL}") + endif() + + if(NOT ${BUILDENV_SHA256} STREQUAL "") + message( + STATUS + "Verify SHA256 of downloaded file ${BUILDENV_BASEPATH}/${BUILDENV_NAME}.zip ..." + ) + file(SHA256 "${BUILDENV_BASEPATH}/${BUILDENV_NAME}.zip" actual_sha256) + + if(NOT actual_sha256 STREQUAL ${BUILDENV_SHA256}) + message( + FATAL_ERROR + "SHA256 checksum mismatch:\nexpected: ${BUILDENV_SHA256}\n got: ${actual_sha256}" + ) + else() + message(STATUS "SHA256 ${BUILDENV_SHA256} is correct!") + endif() + endif() + + message( + STATUS + "Unpacking file ${BUILDENV_BASEPATH}/${BUILDENV_NAME}.zip ..." + ) + execute_process( + COMMAND ${CMAKE_COMMAND} -E tar xzf "${BUILDENV_NAME}.zip" + WORKING_DIRECTORY ${BUILDENV_BASEPATH} + ) + else() + # Reference to suppress intentionally unused variable warnings + set(_dummy "${BUILDENV_SHA256}") + endif() +else() + # Reference to suppress intentionally unused variable warnings + set(_dummy "${BUILDENV_URL} ${BUILDENV_BASEPATH} ${BUILDENV_SHA256}") +endif() + # Use this function to throw an error because the build environment is not set # up correctly. function(fatal_error_missing_env) diff --git a/res/controllers/Traktor Kontrol S4 MK3.hid.xml b/res/controllers/Traktor Kontrol S4 MK3.hid.xml index dceb9eb79b63..69d65c1037b3 100644 --- a/res/controllers/Traktor Kontrol S4 MK3.hid.xml +++ b/res/controllers/Traktor Kontrol S4 MK3.hid.xml @@ -183,17 +183,30 @@ Defines the center range in mm where the rate snaps to 0. - + + + + diff --git a/res/controllers/Traktor-Kontrol-S4-MK3.js b/res/controllers/Traktor-Kontrol-S4-MK3.js index 600dcf712874..e3623c870bc3 100644 --- a/res/controllers/Traktor-Kontrol-S4-MK3.js +++ b/res/controllers/Traktor-Kontrol-S4-MK3.js @@ -82,9 +82,12 @@ const TempoFaderTicksPerMm = 4096 / 77; // 53.1948.. const TempoCenterRangeTicks = TempoFaderTicksPerMm * TempoCenterRangeMm; // Value center may be off the labeled center. // Use this setting to compensate per device. -const TempoCenterValueOffset = engine.getSetting("tempoCenterOffsetMm") || 0.0; -const TempoCenterUpper = (4096 / 2) + (TempoCenterRangeTicks / 2) + TempoCenterValueOffset; -const TempoCenterLower = (4096 / 2) - (TempoCenterRangeTicks / 2) + TempoCenterValueOffset; +const TempoCenterValueOffsetLeft = (engine.getSetting("tempoCenterOffsetMmLeft") || 0.0) * TempoFaderTicksPerMm; +const TempoCenterValueOffsetRright = (engine.getSetting("tempoCenterOffsetMmRight") || 0.0) * TempoFaderTicksPerMm; +const TempoCenterUpperLeft = (4096 / 2) + (TempoCenterRangeTicks / 2) + TempoCenterValueOffsetLeft; +const TempoCenterLowerLeft = (4096 / 2) - (TempoCenterRangeTicks / 2) + TempoCenterValueOffsetLeft; +const TempoCenterUpperRight = (4096 / 2) + (TempoCenterRangeTicks / 2) + TempoCenterValueOffsetRright; +const TempoCenterLowerRight = (4096 / 2) - (TempoCenterRangeTicks / 2) + TempoCenterValueOffsetRright; // Define whether or not to keep LED that have only one color (reverse, flux, play, shift) dimmed if they are inactive. // 'true' will keep them dimmed, 'false' will turn them off. Default: true @@ -425,7 +428,7 @@ class ComponentContainer extends Component { /* eslint no-redeclare: "off" */ class Deck extends ComponentContainer { - constructor(decks, colors) { + constructor(decks, colors, settings) { super(); if (typeof decks === "number") { this.group = Deck.groupForNumber(decks); @@ -443,6 +446,7 @@ class Deck extends ComponentContainer { } this.color = colors[0]; } + this.settings = settings; this.secondDeckModes = null; } toggleDeck() { @@ -1569,8 +1573,8 @@ class S4Mk3EffectUnit extends ComponentContainer { } class S4Mk3Deck extends Deck { - constructor(decks, colors, effectUnit, mixer, inReports, outReport, io) { - super(decks, colors); + constructor(decks, colors, settings, effectUnit, mixer, inReports, outReport, io) { + super(decks, colors, settings); this.playButton = new PlayButton({ output: InactiveLightsAlwaysBacklit ? undefined : Button.prototype.uncoloredOutput @@ -1630,19 +1634,21 @@ class S4Mk3Deck extends Deck { } : undefined, }); this.tempoFader = new Pot({ + deck: this, inKey: "rate", outKey: "rate", appliedValue: null, - + tempoCenterUpper: this.settings.tempoCenterUpper, + tempoCenterLower: this.settings.tempoCenterLower, input: function(value) { const receivingFirstValue = this.appliedValue === null; - if (value < TempoCenterLower) { + if (value < this.tempoCenterLower) { // scale input for lower range - this.appliedValue = script.absoluteLin(value, -1, 0, 0, TempoCenterLower); - } else if (value > TempoCenterUpper) { + this.appliedValue = script.absoluteLin(value, -1, 0, 0, this.tempoCenterLower); + } else if (value > this.tempoCenterUpper) { // scale input for upper range - this.appliedValue = script.absoluteLin(value, 0, 1, TempoCenterUpper, 4096); + this.appliedValue = script.absoluteLin(value, 0, 1, this.tempoCenterUpper, 4096); } else { // reset rate in center region this.appliedValue = 0; @@ -2993,7 +2999,10 @@ class S4MK3 { // so every single components' IO needs to be specified individually // for both decks. this.leftDeck = new S4Mk3Deck( - [1, 3], [DeckColors[0], DeckColors[2]], this.effectUnit1, this.mixer, + [1, 3], [DeckColors[0], DeckColors[2]], { + tempoCenterLower: TempoCenterLowerLeft, + tempoCenterUpper: TempoCenterUpperLeft, + }, this.effectUnit1, this.mixer, this.inReports, this.outReports[128], { playButton: {inByte: 4, inBit: 0, outByte: 55}, @@ -3043,7 +3052,10 @@ class S4MK3 { ); this.rightDeck = new S4Mk3Deck( - [2, 4], [DeckColors[1], DeckColors[3]], this.effectUnit2, this.mixer, + [2, 4], [DeckColors[1], DeckColors[3]], { + tempoCenterLower: TempoCenterLowerRight, + tempoCenterUpper: TempoCenterUpperRight, + }, this.effectUnit2, this.mixer, this.inReports, this.outReports[128], { playButton: {inByte: 12, inBit: 0, outByte: 66}, diff --git a/res/linux/org.mixxx.Mixxx.metainfo.xml b/res/linux/org.mixxx.Mixxx.metainfo.xml index 83623db5c6b9..c9bc5b0239e4 100644 --- a/res/linux/org.mixxx.Mixxx.metainfo.xml +++ b/res/linux/org.mixxx.Mixxx.metainfo.xml @@ -96,11 +96,11 @@ Do not edit it manually. --> - + - +

STEM file support @@ -1080,6 +1080,56 @@ #14649 + + + + +

+ Controller Mappings +

+
    +
  • + Traktor Kontrol S4 Mk3: tempo offset per deck + #14882 +
  • +
  • + Traktor Kontrol S4 Mk3: don`t duplicate beatloop_activate behaviour + #14992 +
  • +
  • + Traktor Kontrol S3: allow full library navigation + #14980 +
  • +
+

+ Misc +

+
    +
  • + Broadcast preferences: make setting string translatable + #15023 +
  • +
  • + Sound Hardware preference: add (?) linking to Sound APIs in the manual + #14935 +
  • +
  • + xwax: do not try to "correct" for drift in absolute mode. + #14960 +
  • +
  • + Fix column header text assignment + #14944 +
  • +
  • + Remove runtime assert to not risk crashes + #15000 +
  • +
  • + Windows: Update build environment to Visual Studio 2022 + #15006 +
  • +
diff --git a/src/library/dlgtrackinfo.ui b/src/library/dlgtrackinfo.ui index d04e2e03eae4..63f6f25524a4 100644 --- a/src/library/dlgtrackinfo.ui +++ b/src/library/dlgtrackinfo.ui @@ -50,7 +50,7 @@ - + diff --git a/src/preferences/dialog/dlgprefbroadcast.cpp b/src/preferences/dialog/dlgprefbroadcast.cpp index e259a043f523..42633cbcbd03 100644 --- a/src/preferences/dialog/dlgprefbroadcast.cpp +++ b/src/preferences/dialog/dlgprefbroadcast.cpp @@ -18,7 +18,6 @@ #include "util/logger.h" namespace { -const char* kSettingsGroupHeader = "Settings for %1"; constexpr int kColumnEnabled = 0; constexpr int kColumnName = 1; const mixxx::Logger kLogger("DlgPrefBroadcast"); @@ -417,9 +416,8 @@ void DlgPrefBroadcast::getValuesFromProfile(BroadcastProfilePtr profile) { } // Set groupbox header - QString headerText = - QString(tr(kSettingsGroupHeader)) - .arg(profile->getProfileName()); + //: Settings for broadcast profile, %1 is the profile name placeholder + const QString headerText = tr("Settings for %1").arg(profile->getProfileName()); groupBoxProfileSettings->setTitle(headerText); rbPasswordCleartext->setChecked(!profile->secureCredentialStorage()); diff --git a/src/preferences/dialog/dlgprefwaveform.cpp b/src/preferences/dialog/dlgprefwaveform.cpp index 32d06bed9a82..d8c7bd139750 100644 --- a/src/preferences/dialog/dlgprefwaveform.cpp +++ b/src/preferences/dialog/dlgprefwaveform.cpp @@ -217,6 +217,18 @@ DlgPrefWaveform::DlgPrefWaveform( QOverload::of(&QComboBox::currentIndexChanged), this, &DlgPrefWaveform::slotSetUntilMarkTextHeightLimit); + connect(stemReorderLayerOnChangedCheckBox, + &QCheckBox::clicked, + this, + &DlgPrefWaveform::slotStemReorderOnChange); + connect(stemOpacitySpinBox, + &QDoubleSpinBox::valueChanged, + this, + &DlgPrefWaveform::slotStemOpacity); + connect(stemOutlineOpacitySpinBox, + &QDoubleSpinBox::valueChanged, + this, + &DlgPrefWaveform::slotStemOutlineOpacity); setScrollSafeGuardForAllInputWidgets(this); } @@ -306,6 +318,10 @@ void DlgPrefWaveform::slotUpdate() { WaveformWidgetFactory::toUntilMarkTextHeightLimitIndex( factory->getUntilMarkTextHeightLimit())); + stemReorderLayerOnChangedCheckBox->setChecked(factory->isStemReorderOnChange()); + stemOpacitySpinBox->setValue(factory->getStemOpacity()); + stemOutlineOpacitySpinBox->setValue(factory->getStemOutlineOpacity()); + mixxx::OverviewType cfgOverviewType = m_pConfig->getValue(kOverviewTypeCfgKey, mixxx::OverviewType::RGB); // Assumes the combobox index is in sync with the ControlPushButton @@ -674,6 +690,17 @@ void DlgPrefWaveform::slotSetUntilMarkTextHeightLimit(int index) { WaveformWidgetFactory::toUntilMarkTextHeightLimit(index)); } +void DlgPrefWaveform::slotStemOpacity(float value) { + WaveformWidgetFactory::instance()->setStemOpacity(value); +} +void DlgPrefWaveform::slotStemReorderOnChange(bool value) { + WaveformWidgetFactory::instance()->setStemReorderOnChange(value); +} + +void DlgPrefWaveform::slotStemOutlineOpacity(float value) { + WaveformWidgetFactory::instance()->setStemOutlineOpacity(value); +} + void DlgPrefWaveform::calculateCachedWaveformDiskUsage() { AnalysisDao analysisDao(m_pConfig); QSqlDatabase dbConnection = mixxx::DbConnectionPooled(m_pLibrary->dbConnectionPool()); diff --git a/src/preferences/dialog/dlgprefwaveform.h b/src/preferences/dialog/dlgprefwaveform.h index dbdfe2831d56..240f378c0c79 100644 --- a/src/preferences/dialog/dlgprefwaveform.h +++ b/src/preferences/dialog/dlgprefwaveform.h @@ -63,6 +63,9 @@ class DlgPrefWaveform : public DlgPreferencePage, public Ui::DlgPrefWaveformDlg void slotSetUntilMarkAlign(int index); void slotSetUntilMarkTextPointSize(int value); void slotSetUntilMarkTextHeightLimit(int index); + void slotStemOpacity(float value); + void slotStemReorderOnChange(bool value); + void slotStemOutlineOpacity(float value); private: void initWaveformControl(); diff --git a/src/preferences/dialog/dlgprefwaveformdlg.ui b/src/preferences/dialog/dlgprefwaveformdlg.ui index 01fc4e8ddef5..4d659eccb50b 100644 --- a/src/preferences/dialog/dlgprefwaveformdlg.ui +++ b/src/preferences/dialog/dlgprefwaveformdlg.ui @@ -589,6 +589,96 @@ Select from different types of displays for the waveform, which differ primarily + + + + Stem + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + stemOpacityMainLabel + + + + + + + + + Channel opacity + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + stemOpacityMainLabel + + + + + + + Channel opacity (outline) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + stemOpacityMainLabel + + + + + + + + Main stem opacity + + + 2 + + + 0.010000000000000 + + + 1.000000000000000 + + + 0.010000000000000 + + + + + + + Outline stem opacity + + + 2 + + + 0.010000000000000 + + + 1.000000000000000 + + + 0.010000000000000 + + + + + + + Move channel to foreground when volume is adjusted + + + + + + diff --git a/src/waveform/renderers/allshader/waveformrendererstem.cpp b/src/waveform/renderers/allshader/waveformrendererstem.cpp index fdf8be02e9bd..73ed176ed88d 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.cpp +++ b/src/waveform/renderers/allshader/waveformrendererstem.cpp @@ -4,6 +4,7 @@ #include #include +#include "control/controlproxy.h" #include "engine/channels/enginedeck.h" #include "engine/engine.h" #include "rendergraph/material/rgbamaterial.h" @@ -13,6 +14,7 @@ #include "util/math.h" #include "waveform/renderers/waveformwidgetrenderer.h" #include "waveform/waveform.h" +#include "waveform/waveformwidgetfactory.h" namespace { #ifdef __SCENEGRAPH__ @@ -35,7 +37,9 @@ WaveformRendererStem::WaveformRendererStem( ::WaveformRendererSignalBase::Options options) : WaveformRendererSignalBase(waveformWidget, options), m_isSlipRenderer(type == ::WaveformRendererAbstract::Slip), - m_splitStemTracks(false) { + m_splitStemTracks(false), + m_outlineOpacity(0.15f), + m_opacity(0.75f) { initForRectangles(0); setUsePreprocess(true); } @@ -52,12 +56,43 @@ bool WaveformRendererStem::init() { for (int stemIdx = 0; stemIdx < mixxx::kMaxSupportedStems; stemIdx++) { QString stemGroup = EngineDeck::getGroupForStem(m_waveformRenderer->getGroup(), stemIdx); m_pStemGain.emplace_back( - std::make_unique(stemGroup, + std::make_unique(stemGroup, QStringLiteral("volume"))); m_pStemMute.emplace_back( - std::make_unique(stemGroup, + std::make_unique(stemGroup, QStringLiteral("mute"))); + auto bringToForeground = [this, stemIdx](double) { + if (!m_reorderOnChange) { + return; + } + m_stackOrder.removeAll(stemIdx); + m_stackOrder.append(stemIdx); + }; + m_pStemGain.back()->connectValueChanged(this, bringToForeground); + m_pStemMute.back()->connectValueChanged(this, bringToForeground); } + + m_stackOrder.resize(mixxx::kMaxSupportedStems); + std::iota(m_stackOrder.begin(), m_stackOrder.end(), 0); + +#ifndef __SCENEGRAPH__ + auto* pWaveformWidgetFactory = WaveformWidgetFactory::instance(); + setReorderOnChange(pWaveformWidgetFactory->isStemReorderOnChange()); + connect(pWaveformWidgetFactory, + &WaveformWidgetFactory::stemReorderOnChangeChanged, + this, + &WaveformRendererStem::setReorderOnChange); + setOutlineOpacity(pWaveformWidgetFactory->getStemOutlineOpacity()); + connect(pWaveformWidgetFactory, + &WaveformWidgetFactory::stemOutlineOpacityChanged, + this, + &WaveformRendererStem::setOutlineOpacity); + setOpacity(pWaveformWidgetFactory->getStemOpacity()); + connect(pWaveformWidgetFactory, + &WaveformWidgetFactory::stemOpacityChanged, + this, + &WaveformRendererStem::setOpacity); +#endif return true; } @@ -158,7 +193,8 @@ bool WaveformRendererStem::preprocessInner() { const double maxSamplingRange = visualIncrementPerPixel / 2.0; for (int visualIdx = 0; visualIdx < stripLength; visualIdx++) { - for (int stemIdx = 0; stemIdx < mixxx::kMaxSupportedStems; stemIdx++) { + int stemLayer = 0; + for (int stemIdx : std::as_const(m_stackOrder)) { // Stem is drawn twice with different opacity level, this allow to // see the maximum signal by transparency for (int layerIdx = 0; layerIdx < 2; layerIdx++) { @@ -166,7 +202,7 @@ bool WaveformRendererStem::preprocessInner() { float color_r = stemColor.redF(), color_g = stemColor.greenF(), color_b = stemColor.blueF(), - color_a = stemColor.alphaF() * (layerIdx ? 0.75f : 0.15f); + color_a = stemColor.alphaF() * (layerIdx ? m_opacity : m_outlineOpacity); const int visualFrameStart = std::lround(xVisualFrame - maxSamplingRange); const int visualFrameStop = std::lround(xVisualFrame + maxSamplingRange); @@ -208,15 +244,16 @@ bool WaveformRendererStem::preprocessInner() { // shadow vertexUpdater.addRectangle( {fVisualIdx - halfStripSize, - stemIdx * stemBreadth + halfBreadth - + stemLayer * stemBreadth + halfBreadth - heightFactor * max}, {fVisualIdx + halfStripSize, m_isSlipRenderer - ? stemIdx * stemBreadth + halfBreadth - : stemIdx * stemBreadth + halfBreadth + + ? stemLayer * stemBreadth + halfBreadth + : stemLayer * stemBreadth + halfBreadth + heightFactor * max}, {color_r, color_g, color_b, color_a}); } + stemLayer++; } xVisualFrame += visualIncrementPerPixel; diff --git a/src/waveform/renderers/allshader/waveformrendererstem.h b/src/waveform/renderers/allshader/waveformrendererstem.h index d6c36517debf..bac42434a718 100644 --- a/src/waveform/renderers/allshader/waveformrendererstem.h +++ b/src/waveform/renderers/allshader/waveformrendererstem.h @@ -2,12 +2,12 @@ #include -#include "control/pollingcontrolproxy.h" #include "rendergraph/geometrynode.h" #include "util/class.h" #include "waveform/renderers/allshader/waveformrenderersignalbase.h" class QOpenGLTexture; +class ControlProxy; namespace allshader { class WaveformRendererStem; @@ -39,13 +39,32 @@ class allshader::WaveformRendererStem final void setSplitStemTracks(bool splitStemTracks) { m_splitStemTracks = splitStemTracks; } + void setReorderOnChange(bool value) { + m_reorderOnChange = value; + // Reset the stem layer stack to the natural order + std::iota(m_stackOrder.begin(), m_stackOrder.end(), 0); + } + void setOutlineOpacity(float value) { + m_outlineOpacity = value; + markDirtyMaterial(); + } + void setOpacity(float value) { + m_opacity = value; + markDirtyMaterial(); + } private: bool m_isSlipRenderer; bool m_splitStemTracks; - std::vector> m_pStemGain; - std::vector> m_pStemMute; + bool m_reorderOnChange; + float m_outlineOpacity; + float m_opacity; + + std::vector> m_pStemGain; + std::vector> m_pStemMute; + + QVarLengthArray m_stackOrder; bool preprocessInner(); diff --git a/src/waveform/renderers/allshader/waveformrenderertextured.cpp b/src/waveform/renderers/allshader/waveformrenderertextured.cpp index c561e72afd2c..f5ee356b958d 100644 --- a/src/waveform/renderers/allshader/waveformrenderertextured.cpp +++ b/src/waveform/renderers/allshader/waveformrenderertextured.cpp @@ -23,7 +23,7 @@ QString WaveformRendererTextured::fragShaderForType(::WaveformWidgetType::Type t default: break; } - assert(false); + DEBUG_ASSERT(!"unsupported WaveformWidgetType"); return QString(); } diff --git a/src/waveform/vsyncthread.cpp b/src/waveform/vsyncthread.cpp index 50709e43c5c0..175435a26167 100644 --- a/src/waveform/vsyncthread.cpp +++ b/src/waveform/vsyncthread.cpp @@ -64,13 +64,13 @@ void VSyncThread::run() { runTimer(); break; default: - assert(false); + DEBUG_ASSERT(!"unsupported sync mode"); break; } } void VSyncThread::runFree() { - assert(m_vSyncMode == ST_FREE); + DEBUG_ASSERT(m_vSyncMode == ST_FREE); while (m_bDoRendering) { // for benchmark only! @@ -87,7 +87,7 @@ void VSyncThread::runFree() { } void VSyncThread::runPLL() { - assert(m_vSyncMode == ST_PLL); + DEBUG_ASSERT(m_vSyncMode == ST_PLL); qint64 offsetAdjustedAt = 0; qint64 offset = 0; qint64 nextSwapMicros = 0; @@ -180,7 +180,7 @@ void VSyncThread::runPLL() { } void VSyncThread::runTimer() { - assert(m_vSyncMode == ST_TIMER); + DEBUG_ASSERT(m_vSyncMode == ST_TIMER); while (m_bDoRendering) { emit vsyncRender(); // renders the new waveform. diff --git a/src/waveform/waveformwidgetfactory.cpp b/src/waveform/waveformwidgetfactory.cpp index 95f2e40f15d4..6883a32c42b0 100644 --- a/src/waveform/waveformwidgetfactory.cpp +++ b/src/waveform/waveformwidgetfactory.cpp @@ -428,6 +428,12 @@ bool WaveformWidgetFactory::setConfig(UserSettingsPointer config) { setUntilMarkTextHeightLimit(toUntilMarkTextHeightLimit( m_config->getValue(ConfigKey("[Waveform]", "UntilMarkTextHeightLimit"), toUntilMarkTextHeightLimitIndex(m_untilMarkTextHeightLimit)))); + setStemReorderOnChange(m_config->getValue( + ConfigKey("[Waveform]", "stem_reorder_on_change"), true)); + setStemOpacity(static_cast( + m_config->getValue(ConfigKey("[Waveform]", "stem_opacity"), 0.75))); + setStemOutlineOpacity(static_cast(m_config->getValue( + ConfigKey("[Waveform]", "stem_outline_opacity"), 0.15))); return true; } @@ -1369,6 +1375,33 @@ void WaveformWidgetFactory::setUntilMarkTextHeightLimit(float value) { emit untilMarkTextHeightLimitChanged(value); } +void WaveformWidgetFactory::setStemReorderOnChange(bool value) { + m_stemReorderOnChange = value; + if (m_config) { + m_config->setValue(ConfigKey("[Waveform]", "stem_reorder_on_change"), + value); + } + emit stemReorderOnChangeChanged(value); +} + +void WaveformWidgetFactory::setStemOutlineOpacity(float value) { + m_stemOutlineOpacity = value; + if (m_config) { + m_config->setValue(ConfigKey("[Waveform]", "stem_outline_opacity"), + static_cast(value)); + } + emit stemOutlineOpacityChanged(value); +} + +void WaveformWidgetFactory::setStemOpacity(float value) { + m_stemOpacity = value; + if (m_config) { + m_config->setValue(ConfigKey("[Waveform]", "stem_opacity"), + static_cast(value)); + } + emit stemOpacityChanged(value); +} + // static Qt::Alignment WaveformWidgetFactory::toUntilMarkAlign(int index) { switch (index) { @@ -1379,7 +1412,7 @@ Qt::Alignment WaveformWidgetFactory::toUntilMarkAlign(int index) { case 2: return Qt::AlignBottom; } - assert(false); + DEBUG_ASSERT(!"unsupported align"); return Qt::AlignVCenter; } // static @@ -1394,7 +1427,7 @@ int WaveformWidgetFactory::toUntilMarkAlignIndex(Qt::Alignment align) { default: break; } - assert(false); + DEBUG_ASSERT(!"unsupported align index"); return 1; } // static @@ -1405,7 +1438,7 @@ float WaveformWidgetFactory::toUntilMarkTextHeightLimit(int index) { case 1: return 1.f; } - assert(false); + DEBUG_ASSERT(!"unsupported height limit"); return 0.33f; } // static @@ -1416,6 +1449,6 @@ int WaveformWidgetFactory::toUntilMarkTextHeightLimitIndex(float value) { if (value == 1.f) { return 1; } - assert(false); + DEBUG_ASSERT(!"unsupported height limit"); return 0; } diff --git a/src/waveform/waveformwidgetfactory.h b/src/waveform/waveformwidgetfactory.h index 3c698cbf62c0..73d7c06ea8b0 100644 --- a/src/waveform/waveformwidgetfactory.h +++ b/src/waveform/waveformwidgetfactory.h @@ -157,6 +157,10 @@ class WaveformWidgetFactory : public QObject, void setUntilMarkTextPointSize(int value); void setUntilMarkTextHeightLimit(float value); + void setStemReorderOnChange(bool value); + void setStemOutlineOpacity(float value); + void setStemOpacity(float value); + bool getUntilMarkShowBeats() const { return m_untilMarkShowBeats; } @@ -172,6 +176,15 @@ class WaveformWidgetFactory : public QObject, float getUntilMarkTextHeightLimit() const { return m_untilMarkTextHeightLimit; } + bool isStemReorderOnChange() const { + return m_stemReorderOnChange; + } + float getStemOutlineOpacity() const { + return m_stemOutlineOpacity; + } + float getStemOpacity() const { + return m_stemOpacity; + } static Qt::Alignment toUntilMarkAlign(int index); static int toUntilMarkAlignIndex(Qt::Alignment align); static float toUntilMarkTextHeightLimit(int index); @@ -233,6 +246,10 @@ class WaveformWidgetFactory : public QObject, void untilMarkTextPointSizeChanged(int value); void untilMarkTextHeightLimitChanged(float value); + void stemReorderOnChangeChanged(bool value); + void stemOutlineOpacityChanged(float value); + void stemOpacityChanged(float value); + public slots: void slotSkinLoaded(); @@ -295,6 +312,10 @@ class WaveformWidgetFactory : public QObject, int m_untilMarkTextPointSize; float m_untilMarkTextHeightLimit; + bool m_stemReorderOnChange; + float m_stemOutlineOpacity; + float m_stemOpacity; + bool m_openGlAvailable; bool m_openGlesAvailable; QString m_openGLVersion; diff --git a/tools/macos_buildenv.sh b/tools/macos_buildenv.sh index 35e667e11fa4..3847388928a4 100755 --- a/tools/macos_buildenv.sh +++ b/tools/macos_buildenv.sh @@ -66,6 +66,7 @@ else exit 1 fi +BUILDENV_URL="https://downloads.mixxx.org/dependencies/${BUILDENV_BRANCH}/macOS/${BUILDENV_NAME}.zip" MIXXX_ROOT="$(realpath "$(dirname "$THIS_SCRIPT_NAME")/..")" [ -z "$BUILDENV_BASEPATH" ] && BUILDENV_BASEPATH="${MIXXX_ROOT}/buildenv" @@ -81,44 +82,20 @@ case "$1" in setup) BUILDENV_PATH="${BUILDENV_BASEPATH}/${BUILDENV_NAME}" - mkdir -p "${BUILDENV_BASEPATH}" - if [ ! -d "${BUILDENV_PATH}" ]; then - if [ "$1" != "--profile" ]; then - echo "Build environment $BUILDENV_NAME not found in mixxx repository, downloading https://downloads.mixxx.org/dependencies/${BUILDENV_BRANCH}/macOS/${BUILDENV_NAME}.zip" - http_code=$(curl -sI -w "%{http_code}" "https://downloads.mixxx.org/dependencies/${BUILDENV_BRANCH}/macOS/${BUILDENV_NAME}.zip" -o /dev/null) - if [ "$http_code" -ne 200 ]; then - echo "Downloading failed with HTTP status code: $http_code" - exit 1 - fi - curl "https://downloads.mixxx.org/dependencies/${BUILDENV_BRANCH}/macOS/${BUILDENV_NAME}.zip" -o "${BUILDENV_PATH}.zip" - OBSERVED_SHA256=$(shasum -a 256 "${BUILDENV_PATH}.zip"|cut -f 1 -d' ') - if [[ "$OBSERVED_SHA256" == "$BUILDENV_SHA256" ]]; then - echo "Download matched expected SHA256 sum $BUILDENV_SHA256" - else - echo "ERROR: Download did not match expected SHA256 checksum!" - echo "Expected $BUILDENV_SHA256" - echo "Got $OBSERVED_SHA256" - exit 1 - fi - echo "" - echo "Extracting ${BUILDENV_NAME}.zip..." - unzip "${BUILDENV_PATH}.zip" -d "${BUILDENV_BASEPATH}" && \ - echo "Successfully extracted ${BUILDENV_NAME}.zip" && \ - rm "${BUILDENV_PATH}.zip" - else - echo "Build environment $BUILDENV_NAME not found in mixxx repository, run the command below to download it." - echo "source ${THIS_SCRIPT_NAME} setup" - return # exit would quit the shell being started - fi - elif [ "$1" != "--profile" ]; then - echo "Build environment found: ${BUILDENV_PATH}" - fi + export BUILDENV_NAME + export BUILDENV_BASEPATH + export BUILDENV_URL + export BUILDENV_SHA256 export MIXXX_VCPKG_ROOT="${BUILDENV_PATH}" export CMAKE_GENERATOR=Ninja export VCPKG_TARGET_TRIPLET="${VCPKG_TARGET_TRIPLET}" echo_exported_variables() { + echo "BUILDENV_NAME=${BUILDENV_NAME}" + echo "BUILDENV_BASEPATH=${BUILDENV_BASEPATH}" + echo "BUILDENV_URL=${BUILDENV_URL}" + echo "BUILDENV_SHA256=${BUILDENV_SHA256}" echo "MIXXX_VCPKG_ROOT=${MIXXX_VCPKG_ROOT}" echo "CMAKE_GENERATOR=${CMAKE_GENERATOR}" echo "VCPKG_TARGET_TRIPLET=${VCPKG_TARGET_TRIPLET}" @@ -140,6 +117,6 @@ case "$1" in echo "options:" echo " help Displays this help." echo " name Displays the name of the required build environment." - echo " setup Installs the build environment." + echo " setup Setup the build environment variables for download during CMake configuration." ;; esac diff --git a/tools/windows_buildenv.bat b/tools/windows_buildenv.bat index f82eae8c79dd..8715e6403d01 100644 --- a/tools/windows_buildenv.bat +++ b/tools/windows_buildenv.bat @@ -63,6 +63,7 @@ IF /I "%PLATFORM%"=="arm64" ( PAUSE EXIT /B 1 ) +SET BUILDENV_URL=https://downloads.mixxx.org/dependencies/!BUILDENV_BRANCH!/Windows/!BUILDENV_NAME!.zip IF "%~1"=="" ( REM In case of manual start by double click no arguments are specified: Default to COMMAND_setup @@ -73,7 +74,7 @@ IF "%~1"=="" ( ) REM Make These permanent, not local to the batch script. -ENDLOCAL & SET "MIXXX_VCPKG_ROOT=%MIXXX_VCPKG_ROOT%" & SET "VCPKG_DEFAULT_TRIPLET=%VCPKG_DEFAULT_TRIPLET%" & SET "X_VCPKG_APPLOCAL_DEPS_INSTALL=%X_VCPKG_APPLOCAL_DEPS_INSTALL%" & SET "CMAKE_GENERATOR=%CMAKE_GENERATOR%" +ENDLOCAL & SET "MIXXX_VCPKG_ROOT=%MIXXX_VCPKG_ROOT%" & SET "VCPKG_DEFAULT_TRIPLET=%VCPKG_DEFAULT_TRIPLET%" & SET "X_VCPKG_APPLOCAL_DEPS_INSTALL=%X_VCPKG_APPLOCAL_DEPS_INSTALL%" & SET "CMAKE_GENERATOR=%CMAKE_GENERATOR%" & SET "BUILDENV_BASEPATH=%BUILDENV_BASEPATH%" & SET "BUILDENV_URL=%BUILDENV_URL%" & SET "BUILDENV_SHA256=%BUILDENV_SHA256%" EXIT /B 0 @@ -85,47 +86,6 @@ EXIT /B 0 :COMMAND_setup SET BUILDENV_PATH=%BUILDENV_BASEPATH%\%BUILDENV_NAME% - - IF NOT EXIST "%BUILDENV_BASEPATH%" ( - ECHO ^Creating "buildenv" directory... - MD "%BUILDENV_BASEPATH%" - ) - - IF NOT EXIST "%BUILDENV_PATH%" ( - SET BUILDENV_URL=https://downloads.mixxx.org/dependencies/!BUILDENV_BRANCH!/Windows/!BUILDENV_NAME!.zip - IF NOT EXIST "!BUILDENV_PATH!.zip" ( - ECHO ^Download prebuilt build environment from "!BUILDENV_URL!" to "!BUILDENV_PATH!.zip"... - REM TODO: The /DYNAMIC parameter is required because our server does not yet support HTTP range headers - BITSADMIN /transfer buildenvjob /download /priority normal /DYNAMIC !BUILDENV_URL! "!BUILDENV_PATH!.zip" - ECHO ^Download complete. - certutil -hashfile "!BUILDENV_PATH!.zip" SHA256 | FIND /C "!BUILDENV_SHA256!" - IF errorlevel 1 ( - ECHO ^ERROR: Download did not match expected SHA256 checksum! - certutil -hashfile "!BUILDENV_PATH!.zip" SHA256 - echo ^Expected: "!BUILDENV_SHA256!" - EXIT /B 1 - ) - ) else ( - ECHO ^Using cached archive at "!BUILDENV_PATH!.zip". - ) - - CALL :DETECT_SEVENZIP - IF !RETVAL!=="" ( - ECHO ^Unpacking "!BUILDENV_PATH!.zip" using powershell... - CALL :UNZIP_POWERSHELL "!BUILDENV_PATH!.zip" "!BUILDENV_BASEPATH!" - ) ELSE ( - ECHO ^Unpacking "!BUILDENV_PATH!.zip" using 7z... - CALL :UNZIP_SEVENZIP !RETVAL! "!BUILDENV_PATH!.zip" "!BUILDENV_BASEPATH!" - ) - IF NOT EXIST "%BUILDENV_PATH%" ( - ECHO ^Error: Unpacking failed. The downloaded archive might be broken, consider removing "!BUILDENV_PATH!.zip" to force redownload. - EXIT /B 1 - ) - - ECHO ^Unpacking complete. - DEL /f /q "%BUILDENV_PATH%.zip" - ) - ECHO ^Build environment path: !BUILDENV_PATH! SET "MIXXX_VCPKG_ROOT=!BUILDENV_PATH!" @@ -135,11 +95,17 @@ EXIT /B 0 ECHO ^Environment Variables: ECHO ^- MIXXX_VCPKG_ROOT='!MIXXX_VCPKG_ROOT!' ECHO ^- CMAKE_GENERATOR='!CMAKE_GENERATOR!' + ECHO ^- BUILDENV_BASEPATH='!BUILDENV_BASEPATH!' + ECHO ^- BUILDENV_URL='!BUILDENV_URL!' + ECHO ^- BUILDENV_SHA256='!BUILDENV_SHA256!' IF DEFINED GITHUB_ENV ( ECHO MIXXX_VCPKG_ROOT=!MIXXX_VCPKG_ROOT!>>!GITHUB_ENV! ECHO CMAKE_GENERATOR=!CMAKE_GENERATOR!>>!GITHUB_ENV! ECHO VCPKG_TARGET_TRIPLET=!VCPKG_TARGET_TRIPLET!>>!GITHUB_ENV! + ECHO BUILDENV_BASEPATH=!BUILDENV_BASEPATH!>>!GITHUB_ENV! + ECHO BUILDENV_URL=!BUILDENV_URL!>>!GITHUB_ENV! + ECHO BUILDENV_SHA256=!BUILDENV_SHA256!>>!GITHUB_ENV! ) ELSE ( ECHO ^Generating "CMakeSettings.json"... CALL :GENERATE_CMakeSettings_JSON @@ -245,6 +211,9 @@ REM Generate CMakeSettings.json which is read by MS Visual Studio to determine t >>"%CMakeSettings%" echo "variables": [ SET variableElementTermination=, CALL :AddCMakeVar2CMakeSettings_JSON "MIXXX_VCPKG_ROOT" "STRING" "!MIXXX_VCPKG_ROOT:\=\\!" + CALL :AddCMakeVar2CMakeSettings_JSON "BUILDENV_BASEPATH" "STRING" "!BUILDENV_BASEPATH:\=\\!" + CALL :AddCMakeVar2CMakeSettings_JSON "BUILDENV_URL" "STRING" "!BUILDENV_URL!" + CALL :AddCMakeVar2CMakeSettings_JSON "BUILDENV_SHA256" "STRING" "!BUILDENV_SHA256:\=\\!" CALL :AddCMakeVar2CMakeSettings_JSON "BATTERY" "BOOL" "True" CALL :AddCMakeVar2CMakeSettings_JSON "BROADCAST" "BOOL" "True" CALL :AddCMakeVar2CMakeSettings_JSON "BULK" "BOOL" "True"