From d171849f17a60489624de31a853bc02ac5934a12 Mon Sep 17 00:00:00 2001 From: "Gordon Lam (SH) (from Dev Box)" Date: Tue, 1 Sep 2026 13:33:36 +0800 Subject: [PATCH 1/9] Add native Windows Arm64 audio readiness Add hosted and production CLANGARM64 lanes, fail-closed native-host/package validation, exact dependency locking, and ASIO-optional JUCE configuration while preserving existing architectures. AI assistance disclosure: implementation and review used GitHub Copilot CLI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/ci.yml | 45 +++ .gitlab-ci.yml | 40 +++ CMakeLists.txt | 2 + ci/test_validate_arm64_architecture.py | 155 +++++++++++ ci/validate_arm64_architecture.py | 372 +++++++++++++++++++++++++ ci/windows-arm64-packages.lock | 45 +++ 6 files changed, 659 insertions(+) create mode 100644 ci/test_validate_arm64_architecture.py create mode 100644 ci/validate_arm64_architecture.py create mode 100644 ci/windows-arm64-packages.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f950ca36..b2afa252 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,3 +56,48 @@ jobs: pushd build cmake --build . --target install -- VERBOSE=1 popd + + # Supplemental native Arm64 presubmit. Its output is never a release + # artifact; GitLab remains the production artifact chain. + build-arm64-presubmit: + runs-on: windows-11-arm + continue-on-error: true + steps: + - name: Preserve repository line endings + run: git config --global core.autocrlf input + + - uses: actions/checkout@v4 + + - uses: msys2/setup-msys2@v2 + with: + msystem: CLANGARM64 + # Intentionally refresh repository metadata. The subsequent exact + # lock verification fails on version drift until maintainers refresh + # the lock or provide the G1 signed snapshot. + update: true + + - name: Install exact CLANGARM64 package versions + shell: msys2 {0} + run: | + mapfile -t packages < <(sed -n '/^[^#[:space:]][^=]*=/s/,[^,]*$//p' ci/windows-arm64-packages.lock) + pacman --noconfirm -S --needed -- "${packages[@]}" + + - name: Build libopenshot-audio (CLANGARM64, presubmit only) + shell: msys2 {0} + run: | + cmake -B build -S . -G Ninja \ + -DCMAKE_C_COMPILER=clang \ + -DCMAKE_CXX_COMPILER=clang++ \ + -DCMAKE_INSTALL_PREFIX="$PWD/dist-arm64" \ + -DCMAKE_BUILD_TYPE=Debug \ + -DENABLE_AUDIO_DOCS=OFF + cmake --build build + ctest --test-dir build --output-on-failure -VV + cmake --install build + python -m unittest discover -s ci -p "test_*.py" -v + python ci/validate_arm64_architecture.py \ + --require-native-arm64 \ + --package-lock ci/windows-arm64-packages.lock \ + --payload-root dist-arm64 \ + --require-payload \ + --json-report build/arm64-presubmit-report.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2fac254e..d466550a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -79,6 +79,46 @@ windows-builder-x64: - tags tags: [windows] +# WINDOWS ARM64 (native, additive lane; existing x64/x86 jobs are unchanged). +# Contract identity: windows-arm64-clangarm64-v1 (design-spec.md). +# Verifies the shared ci/windows-arm64-packages.lock and validates every +# produced .exe/.dll/.pyd, plus the native-process oracle of the build host, +# using ci/validate_arm64_architecture.py (design-amendment-A1 semantics: +# native execution requires pProcessMachine == IMAGE_FILE_MACHINE_UNKNOWN +# and pNativeMachine == IMAGE_FILE_MACHINE_ARM64; every payload PE must be +# machine 0xAA64 independently). +windows-builder-arm64: + stage: build-libopenshot-audio + artifacts: + expire_in: 6 months + paths: + - build/install-arm64/ + - build\arm64-architecture-report.json + script: + - $env:Path = "C:\msys64\clangarm64\bin;C:\msys64\usr\bin;" + $env:Path; + - $env:MSYSTEM = "CLANGARM64" + - New-Item -ItemType Directory -Force -Path build + - cmake -B build -S . -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -D"CMAKE_INSTALL_PREFIX:PATH=build/install-arm64" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -G Ninja -D"CMAKE_BUILD_TYPE:STRING=Release" + - cmake --build build + - ctest --test-dir build --output-on-failure -VV + - cmake --install build + - python -m unittest discover -s ci -p "test_*.py" -v + - python ci\validate_arm64_architecture.py --require-native-arm64 --package-lock ci\windows-arm64-packages.lock --payload-root build\install-arm64 --require-payload --json-report build\arm64-architecture-report.json + - $PROJECT_VERSION = (Select-String -Path "CMakeLists.txt" -Pattern '^set\(PROJECT_VERSION_FULL "(.*)\"' | %{$_.Matches.Groups[1].value}) + - $PROJECT_SO = (Select-String -Path "CMakeLists.txt" -Pattern '^set\(PROJECT_SO_VERSION (.*)\)' | %{$_.Matches.Groups[1].value}) + - New-Item -path "build/install-arm64/share/" -Name "$CI_PROJECT_NAME.env" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID`nCI_PIPELINE_ID:$CI_PIPELINE_ID`nVERSION:$PROJECT_VERSION`nSO:$PROJECT_SO`nTARGET_TRIPLET:aarch64-w64-mingw32`nPE_MACHINE:0xAA64" -ItemType file -force + - $PREV_GIT_LABEL=(git describe --tags --abbrev=0 '@^') + - git log "$PREV_GIT_LABEL..@" --oneline --pretty=format:"- %C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "build/install-arm64/share/$CI_PROJECT_NAME.log" + rules: + - if: '$ENABLE_WINDOWS_ARM64 == "1" && $CI_COMMIT_TAG == null' + - when: never + tags: [windows-arm64] + # Requires release infrastructure's native/virtual Windows Arm64 GitLab + # runner tagged windows-arm64 (design-spec.md "Release-infrastructure + # surface"). Until that runner exists, this job cannot execute; it does + # not weaken or replace the existing windows-builder-x64/x86 jobs. + allow_failure: true + windows-builder-x86: stage: build-libopenshot-audio artifacts: diff --git a/CMakeLists.txt b/CMakeLists.txt index 400362d5..a43e2d45 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -215,6 +215,8 @@ if(WIN32) if(TARGET ASIO::SDK) target_link_libraries(openshot-audio PRIVATE ASIO::SDK) set(NEED_ASIO TRUE) + else() + target_compile_definitions(openshot-audio PUBLIC JUCE_ASIO=0) endif() # Order here can be important! # For example, winmm.lib must come before kernel32.lib (if linked) diff --git a/ci/test_validate_arm64_architecture.py b/ci/test_validate_arm64_architecture.py new file mode 100644 index 00000000..ace415d2 --- /dev/null +++ b/ci/test_validate_arm64_architecture.py @@ -0,0 +1,155 @@ +# SPDX-FileCopyrightText: 2026 OpenShot Studios, LLC +# SPDX-License-Identifier: LGPL-3.0-or-later + +import contextlib +import importlib.util +import io +import os +import struct +import sys +import tempfile +import unittest +from unittest import mock + +VALIDATOR_PATH = os.path.join(os.path.dirname(__file__), "validate_arm64_architecture.py") +SPEC = importlib.util.spec_from_file_location("validate_arm64_architecture", VALIDATOR_PATH) +if SPEC is None or SPEC.loader is None: + raise RuntimeError("Unable to load validator from %s" % VALIDATOR_PATH) +validator = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(validator) + + +def write_pe(path, machine): + data = bytearray(0x80) + data[:2] = b"MZ" + struct.pack_into(" Date: Fri, 16 Oct 2026 15:07:26 -0500 Subject: [PATCH 2/9] Use compatible GitLab syntax for optional ARM64 builds --- .gitlab-ci.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d466550a..013ec8cd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -109,9 +109,11 @@ windows-builder-arm64: - New-Item -path "build/install-arm64/share/" -Name "$CI_PROJECT_NAME.env" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID`nCI_PIPELINE_ID:$CI_PIPELINE_ID`nVERSION:$PROJECT_VERSION`nSO:$PROJECT_SO`nTARGET_TRIPLET:aarch64-w64-mingw32`nPE_MACHINE:0xAA64" -ItemType file -force - $PREV_GIT_LABEL=(git describe --tags --abbrev=0 '@^') - git log "$PREV_GIT_LABEL..@" --oneline --pretty=format:"- %C(auto,yellow)%h%C(auto,magenta)% %C(auto,blue)%>(12,trunc)%ad %C(auto,green)%<(25,trunc)%aN%C(auto,reset)%s%C(auto,red)% gD% D" --date=short > "build/install-arm64/share/$CI_PROJECT_NAME.log" - rules: - - if: '$ENABLE_WINDOWS_ARM64 == "1" && $CI_COMMIT_TAG == null' - - when: never + only: + variables: + - $ENABLE_WINDOWS_ARM64 == "1" + except: + - tags tags: [windows-arm64] # Requires release infrastructure's native/virtual Windows Arm64 GitLab # runner tagged windows-arm64 (design-spec.md "Release-infrastructure From ef7223dc290770f0654136a3ae1718a034a946fd Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 16 Oct 2026 15:08:03 -0500 Subject: [PATCH 3/9] Harden optional ARM64 validation and record rolling dependencies --- .github/workflows/ci.yml | 12 ++--- .gitlab-ci.yml | 2 +- ci/README-arm64.md | 22 +++++++++ ...validate_arm64_architecture.cpython-38.pyc | Bin 0 -> 9036 bytes ...validate_arm64_architecture.cpython-38.pyc | Bin 0 -> 11334 bytes ci/test_validate_arm64_architecture.py | 42 ++++++++++++++++++ ci/validate_arm64_architecture.py | 39 ++++++++++++---- ci/windows-arm64-packages.lock | 23 +++------- 8 files changed, 109 insertions(+), 31 deletions(-) create mode 100644 ci/README-arm64.md create mode 100644 ci/__pycache__/test_validate_arm64_architecture.cpython-38.pyc create mode 100644 ci/__pycache__/validate_arm64_architecture.cpython-38.pyc diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2afa252..a105b544 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,6 +60,7 @@ jobs: # Supplemental native Arm64 presubmit. Its output is never a release # artifact; GitLab remains the production artifact chain. build-arm64-presubmit: + if: ${{ vars.ENABLE_WINDOWS_ARM64 == '1' }} runs-on: windows-11-arm continue-on-error: true steps: @@ -71,15 +72,14 @@ jobs: - uses: msys2/setup-msys2@v2 with: msystem: CLANGARM64 - # Intentionally refresh repository metadata. The subsequent exact - # lock verification fails on version drift until maintainers refresh - # the lock or provide the G1 signed snapshot. + # Use available rolling packages; record versions against the baseline. + # An archived package snapshot is required for reproducible builds. update: true - - name: Install exact CLANGARM64 package versions + - name: Install CLANGARM64 packages shell: msys2 {0} run: | - mapfile -t packages < <(sed -n '/^[^#[:space:]][^=]*=/s/,[^,]*$//p' ci/windows-arm64-packages.lock) + mapfile -t packages < <(sed -n '/^[^#[:space:]][^=]*=/s/=.*//p' ci/windows-arm64-packages.lock) pacman --noconfirm -S --needed -- "${packages[@]}" - name: Build libopenshot-audio (CLANGARM64, presubmit only) @@ -97,7 +97,7 @@ jobs: python -m unittest discover -s ci -p "test_*.py" -v python ci/validate_arm64_architecture.py \ --require-native-arm64 \ - --package-lock ci/windows-arm64-packages.lock \ + --package-baseline ci/windows-arm64-packages.lock \ --payload-root dist-arm64 \ --require-payload \ --json-report build/arm64-presubmit-report.json diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 013ec8cd..2184d5aa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -103,7 +103,7 @@ windows-builder-arm64: - ctest --test-dir build --output-on-failure -VV - cmake --install build - python -m unittest discover -s ci -p "test_*.py" -v - - python ci\validate_arm64_architecture.py --require-native-arm64 --package-lock ci\windows-arm64-packages.lock --payload-root build\install-arm64 --require-payload --json-report build\arm64-architecture-report.json + - python ci\validate_arm64_architecture.py --require-native-arm64 --package-baseline ci\windows-arm64-packages.lock --payload-root build\install-arm64 --require-payload --json-report build\arm64-architecture-report.json - $PROJECT_VERSION = (Select-String -Path "CMakeLists.txt" -Pattern '^set\(PROJECT_VERSION_FULL "(.*)\"' | %{$_.Matches.Groups[1].value}) - $PROJECT_SO = (Select-String -Path "CMakeLists.txt" -Pattern '^set\(PROJECT_SO_VERSION (.*)\)' | %{$_.Matches.Groups[1].value}) - New-Item -path "build/install-arm64/share/" -Name "$CI_PROJECT_NAME.env" -Value "CI_PROJECT_NAME:$CI_PROJECT_NAME`nCI_COMMIT_REF_NAME:$CI_COMMIT_REF_NAME`nCI_COMMIT_SHA:$CI_COMMIT_SHA`nCI_JOB_ID:$CI_JOB_ID`nCI_PIPELINE_ID:$CI_PIPELINE_ID`nVERSION:$PROJECT_VERSION`nSO:$PROJECT_SO`nTARGET_TRIPLET:aarch64-w64-mingw32`nPE_MACHINE:0xAA64" -ItemType file -force diff --git a/ci/README-arm64.md b/ci/README-arm64.md new file mode 100644 index 00000000..50e71fb1 --- /dev/null +++ b/ci/README-arm64.md @@ -0,0 +1,22 @@ +# Experimental Windows ARM64 builds + +The `arm64-support` branch collects build support before integration into +`develop`. Existing Linux, Intel Mac, and Windows x86/x64 jobs remain enabled. + +Set `ENABLE_WINDOWS_ARM64=1` as a GitLab CI variable to enable the optional +job on a native Windows runner tagged `windows-arm64`, with MSYS2 CLANGARM64 +installed at `C:\msys64`. GitHub uses the repository Actions variable of the +same name to enable its supplemental `windows-11-arm` job. Neither job +publishes a release. + +`windows-arm64-packages.lock` is a reference package list. GitHub installs +currently available packages by name, and the validator records actual +versions and reports deviations using `--package-baseline`. GitLab expects +the packages to be provisioned on the runner. Reproducible builds require a +separately archived package snapshot; `--package-lock` retains strict version +checking for such environments. Archive checksums are not verified. + +Native validation requires both the Windows host API result and an ARM64 PE +header on the running Python interpreter. Every produced executable, DLL, +and Python extension is also checked independently. A Linux validator test +pass is not evidence of native Windows execution. diff --git a/ci/__pycache__/test_validate_arm64_architecture.cpython-38.pyc b/ci/__pycache__/test_validate_arm64_architecture.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c74f56a48a992efea7dd78d21b15a898c16623fc GIT binary patch literal 9036 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 literal 0 HcmV?d00001 diff --git a/ci/__pycache__/validate_arm64_architecture.cpython-38.pyc b/ci/__pycache__/validate_arm64_architecture.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..faa4944d25ba9129da22c598543622728c0660e6 GIT binary patch literal 11334 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�+p0xOdbbi)1;)S*jbtRVGcYgebYYlEis&2DyVcuVQs51(!T*jvZkT%_p(- z1!;rQgfiMklk`YkrgPHG_<~Y=v8|W|hMOF9*V&45Bv(zqVAP+LQYPM|`iQ_~9p#yY zsNr4wST>S2XyKZN!{F1}u_awNzgARND(Qj;4lF(_SKhBK{s9pE5QPStN>;Uq7}fZ4 zDm$UiB<{QcD@^towih-xpUKN{6sek~fqEW2`2T(0nj~wTjGfYtA{c;W|H{lzgsZGK z!7%>E&K2alEu&r0#_A11A^oGg4E>}5KLyo5&U#>Q{juJIa@fcq^Qn>Xew;x>O7B^i zR~WeqxQpOi~z&J53%*eXa^>3qtLS> zTl`lr7TH0OT4DaC9vd*{790p%G$TMUkog46U?MV+=R~MO>yuUCFH{mFL2$Bx*7iHT^``TW{s8^ zakRXew&49WxfXE4O8<)Y6NG1f{_6^3^&Y`3dVjK)$EY`=ylQ)*Z-3gh(K3^)Sd#GU zCx&RD^(46v$qEIuy^XeS%6AK}D$?DV#V!qh5M(zkpXJ|roca0DULiQLaWoo<3j7@ZHgVj* zam2`R%=w+795ACADE7qY{Dz+&oWp?nu#$P9x5a9lh^zyij<`EU=NYVaKrCkr`szeSN89K>};|;WndZ}?gL@?=(0)q9>8~S z*+;R9T-$<<1U=2~umeeaT02cBtBLYXEX^#dk2@;iSh>7` zWHok2S483Y+cGhU3t5Vqa4U-#7J-{e8X=fUb4!CbSWT>`!;{9cOfFk^PnndK?jBL15>65^6~qF0)ckHI9wfEnX5b- zQoG0u;PVRU*}cWmI6Z-l`_!PB_ms$Ou@z+f4)73lk;epfrR#f6>$}7mFW`QGzSAkI zTfRoiX|Jj3gVQx*{&Z;!NpG+mJ~atyBmK{AYDb=#Bd$GnpgG}p9v9N!n1r%+Bz==x ztS7sRC;H9m(im<~+L8wGfFn;+#sD-yy`^Vcdque-X?uFG^*$nisHUV4`!LejT;y)T^NhVczr1i09}uzI)tm2KNaQA+n4YKn zN28L{aAXQIKM8Rl zYI3)4?B8LEMjz>PkFmZZt`oc)R8VvPyJV3^+YZ7?|7)Ctky6IM#TRb;C8rHFh3D@q zEZ@5c!SRpJuW6ID; zVKGWm%AtjH=+gZ1vW&?}&&6~b)^UXI#>IKDCcjx(qE`{;>UDg@Qm@AahjV1cRA>Am zy=`^XhfxS0@bL?MfmB};UbhqHB`g~4%WEJJ5LGDwR>O6fJn%HxCW9KWL;Vk_ARjw6 zSMVWFZ7Lm_P*|!p8NbBY4)U#mOcvHg$#g+KkkXei6Y`35^J>zyNU2DHEbXLbrz{ei zgjj5;YccVFmhDopO9jOkwZ2YC8HKLsXn|TT(K3n!=V%5+00%=Dlfoff)ocELpjShz zk;D~WSXRH5eO9d>f#4=h_J4^xIKx6#(L_u_R~061+N=y>9NCsY*6gqHh;ry(jZACu z=I%eO3A(A9!vBOmX-ptfNx8}il#_;yHwze28;!y`2$QX^3dW$HvYT{WVi>p${L(-? zWKe8mSb#xzp#&81r`T(3*C7necD-KG@0N~=pAdO^R2-q=XH=Y_;*Y5KfQoNYF-gTS zDqcYG?5k7yk|uuc8JDW`agCs?mr5XigKBi^BbiPzRUbD>@qnLAq}hX^W0Oe(d53CW w-12z0 Date: Fri, 16 Oct 2026 15:08:09 -0500 Subject: [PATCH 4/9] Exclude generated validator bytecode --- .gitignore | 3 +++ ...t_validate_arm64_architecture.cpython-38.pyc | Bin 9036 -> 0 bytes .../validate_arm64_architecture.cpython-38.pyc | Bin 11334 -> 0 bytes 3 files changed, 3 insertions(+) delete mode 100644 ci/__pycache__/test_validate_arm64_architecture.cpython-38.pyc delete mode 100644 ci/__pycache__/validate_arm64_architecture.cpython-38.pyc diff --git a/.gitignore b/.gitignore index 927d8c34..3481775a 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ .settings .idea/* cmake-build-debug/* + +# Python validator bytecode +__pycache__/ diff --git a/ci/__pycache__/test_validate_arm64_architecture.cpython-38.pyc b/ci/__pycache__/test_validate_arm64_architecture.cpython-38.pyc deleted file mode 100644 index c74f56a48a992efea7dd78d21b15a898c16623fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9036 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 diff --git a/ci/__pycache__/validate_arm64_architecture.cpython-38.pyc b/ci/__pycache__/validate_arm64_architecture.cpython-38.pyc deleted file mode 100644 index faa4944d25ba9129da22c598543622728c0660e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11334 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�+p0xOdbbi)1;)S*jbtRVGcYgebYYlEis&2DyVcuVQs51(!T*jvZkT%_p(- z1!;rQgfiMklk`YkrgPHG_<~Y=v8|W|hMOF9*V&45Bv(zqVAP+LQYPM|`iQ_~9p#yY zsNr4wST>S2XyKZN!{F1}u_awNzgARND(Qj;4lF(_SKhBK{s9pE5QPStN>;Uq7}fZ4 zDm$UiB<{QcD@^towih-xpUKN{6sek~fqEW2`2T(0nj~wTjGfYtA{c;W|H{lzgsZGK z!7%>E&K2alEu&r0#_A11A^oGg4E>}5KLyo5&U#>Q{juJIa@fcq^Qn>Xew;x>O7B^i zR~WeqxQpOi~z&J53%*eXa^>3qtLS> zTl`lr7TH0OT4DaC9vd*{790p%G$TMUkog46U?MV+=R~MO>yuUCFH{mFL2$Bx*7iHT^``TW{s8^ zakRXew&49WxfXE4O8<)Y6NG1f{_6^3^&Y`3dVjK)$EY`=ylQ)*Z-3gh(K3^)Sd#GU zCx&RD^(46v$qEIuy^XeS%6AK}D$?DV#V!qh5M(zkpXJ|roca0DULiQLaWoo<3j7@ZHgVj* zam2`R%=w+795ACADE7qY{Dz+&oWp?nu#$P9x5a9lh^zyij<`EU=NYVaKrCkr`szeSN89K>};|;WndZ}?gL@?=(0)q9>8~S z*+;R9T-$<<1U=2~umeeaT02cBtBLYXEX^#dk2@;iSh>7` zWHok2S483Y+cGhU3t5Vqa4U-#7J-{e8X=fUb4!CbSWT>`!;{9cOfFk^PnndK?jBL15>65^6~qF0)ckHI9wfEnX5b- zQoG0u;PVRU*}cWmI6Z-l`_!PB_ms$Ou@z+f4)73lk;epfrR#f6>$}7mFW`QGzSAkI zTfRoiX|Jj3gVQx*{&Z;!NpG+mJ~atyBmK{AYDb=#Bd$GnpgG}p9v9N!n1r%+Bz==x ztS7sRC;H9m(im<~+L8wGfFn;+#sD-yy`^Vcdque-X?uFG^*$nisHUV4`!LejT;y)T^NhVczr1i09}uzI)tm2KNaQA+n4YKn zN28L{aAXQIKM8Rl zYI3)4?B8LEMjz>PkFmZZt`oc)R8VvPyJV3^+YZ7?|7)Ctky6IM#TRb;C8rHFh3D@q zEZ@5c!SRpJuW6ID; zVKGWm%AtjH=+gZ1vW&?}&&6~b)^UXI#>IKDCcjx(qE`{;>UDg@Qm@AahjV1cRA>Am zy=`^XhfxS0@bL?MfmB};UbhqHB`g~4%WEJJ5LGDwR>O6fJn%HxCW9KWL;Vk_ARjw6 zSMVWFZ7Lm_P*|!p8NbBY4)U#mOcvHg$#g+KkkXei6Y`35^J>zyNU2DHEbXLbrz{ei zgjj5;YccVFmhDopO9jOkwZ2YC8HKLsXn|TT(K3n!=V%5+00%=Dlfoff)ocELpjShz zk;D~WSXRH5eO9d>f#4=h_J4^xIKx6#(L_u_R~061+N=y>9NCsY*6gqHh;ry(jZACu z=I%eO3A(A9!vBOmX-ptfNx8}il#_;yHwze28;!y`2$QX^3dW$HvYT{WVi>p${L(-? zWKe8mSb#xzp#&81r`T(3*C7necD-KG@0N~=pAdO^R2-q=XH=Y_;*Y5KfQoNYF-gTS zDqcYG?5k7yk|uuc8JDW`agCs?mr5XigKBi^BbiPzRUbD>@qnLAq}hX^W0Oe(d53CW w-12z0 Date: Fri, 16 Oct 2026 15:30:22 -0500 Subject: [PATCH 5/9] Resolve ARM64 integration quality checks --- ci/README-arm64.md | 5 ++ ci/validate_arm64_architecture.py | 119 +++++++++++++----------------- 2 files changed, 57 insertions(+), 67 deletions(-) diff --git a/ci/README-arm64.md b/ci/README-arm64.md index 50e71fb1..50b02771 100644 --- a/ci/README-arm64.md +++ b/ci/README-arm64.md @@ -1,3 +1,8 @@ + + # Experimental Windows ARM64 builds The `arm64-support` branch collects build support before integration into diff --git a/ci/validate_arm64_architecture.py b/ci/validate_arm64_architecture.py index 89d34fc6..19edc83f 100644 --- a/ci/validate_arm64_architecture.py +++ b/ci/validate_arm64_architecture.py @@ -2,39 +2,11 @@ # SPDX-FileCopyrightText: 2026 OpenShot Studios, LLC # SPDX-License-Identifier: LGPL-3.0-or-later -""" -@file -@brief Reusable Windows Arm64 architecture validator (design-spec.md - G2/G3/G8/G11) implementing design-amendment-A1 native-process - semantics. - -This script is shared, byte-for-byte, across libopenshot-audio (PR A), -libopenshot (PR B), and openshot-qt (PR C). It performs two independent -checks, exactly as required by design-amendment-A1: - -1. Native-process oracle (host evidence): calls IsWow64Process2 via ctypes on - the *currently running* Python process and asserts - - pNativeMachine == IMAGE_FILE_MACHINE_ARM64 (0xAA64) - - pProcessMachine == IMAGE_FILE_MACHINE_UNKNOWN (0x0) - The running interpreter PE header must independently identify ARM64. - Any nonzero pProcessMachine is WOW/emulated execution and fails this - check. This check only passes evidence when actually run under a native - Arm64 Python interpreter; on any other host it reports the real, honest - values it observed and does not fabricate a pass. - -2. Recursive static PE architecture scan (payload evidence): recursively - scans a given root directory for every .exe, .dll, and .pyd, reads the - COFF file header "Machine" field directly (no external tool dependency), - and asserts every one of them is IMAGE_FILE_MACHINE_ARM64 (0xAA64). This - check is independent of the host architecture and can run on any Python - 3 interpreter, including this AMD64 documentation/implementation - workspace, against whatever candidate payload directory is supplied. - -The two checks are deliberately independent, per design-amendment-A1: a -native process oracle failure does not imply a payload architecture -failure, and vice versa. Callers (CI jobs) should run both and fail closed -if either reports a problem, unless the repository/job intentionally only -has one kind of evidence available (e.g. no payload directory yet). +"""Validate Windows ARM64 host, payload, and dependency architectures. + +The Windows host API and running interpreter PE header must both identify +native ARM64 execution. A separate recursive scan checks every payload PE. +Package manifests can enforce exact versions or report baseline drift. """ import argparse @@ -42,7 +14,7 @@ import json import os import re -import subprocess +import subprocess # nosec B404 - Query pacman with an argument list, never a shell. import struct import sys @@ -225,7 +197,7 @@ def verify_package_lock(path, require_exact_versions=True): % (package, expected_sha256) ) try: - completed = subprocess.run( + completed = subprocess.run( # nosec B603 B607 - Fixed pacman command and validated package name; no shell. ["pacman", "-Q", "--", package], capture_output=True, text=True, @@ -260,6 +232,48 @@ def verify_package_lock(path, require_exact_versions=True): return verified, failures +def print_native_report(oracle): + """Print the native-process validation result.""" + print("== Native Arm64 process oracle (design-amendment-A1) ==") + if oracle["checked"]: + print(" process_machine = %s" % machine_name(oracle["process_machine"])) + print(" native_machine = %s" % machine_name(oracle["native_machine"])) + print(" wow/emulated = %s" % oracle["is_wow_or_emulated"]) + print(" native ARM64 ok = %s" % oracle["native_arm64_ok"]) + else: + print(" NOT AVAILABLE: %s" % oracle["reason"]) + + +def print_payload_report(root, results, failures): + """Print the payload scan result.""" + print("== Recursive payload PE architecture scan ==") + if root: + print(" root = %s" % root) + print(" files scanned = %d" % len(results)) + for failure in failures: + print(" FAIL: %s" % failure) + else: + print(" SKIPPED: no --payload-root supplied") + + +def print_package_report(manifest, baseline, results, failures): + """Print package versions without claiming baseline drift is a lock match.""" + print("== MSYS2 package lock ==") + if manifest: + print(" manifest = %s" % manifest) + if baseline: + print(" BASELINE ONLY: exact package versions are not required") + for package in results: + if not package["version_matches"]: + print(" VERSION DRIFT: %s = %s (baseline %s)" % ( + package["package"], package["version"], package["expected_version"])) + print(" packages verified = %d" % len(results)) + for failure in failures: + print(" FAIL: %s" % failure) + else: + print(" SKIPPED: no package manifest supplied") + + def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( @@ -332,38 +346,9 @@ def main(): "package_failures": package_failures, } - print("== Native Arm64 process oracle (design-amendment-A1) ==") - if oracle["checked"]: - print(" process_machine = %s" % machine_name(oracle["process_machine"])) - print(" native_machine = %s" % machine_name(oracle["native_machine"])) - print(" wow/emulated = %s" % oracle["is_wow_or_emulated"]) - print(" native ARM64 ok = %s" % oracle["native_arm64_ok"]) - else: - print(" NOT AVAILABLE: %s" % oracle["reason"]) - - print("== Recursive payload PE architecture scan ==") - if args.payload_root: - print(" root = %s" % args.payload_root) - print(" files scanned = %d" % len(payload_results)) - for failure in payload_failures: - print(" FAIL: %s" % failure) - else: - print(" SKIPPED: no --payload-root supplied") - - print("== MSYS2 package lock ==") - if package_manifest: - print(" manifest = %s" % package_manifest) - if args.package_baseline: - print(" BASELINE ONLY: exact package versions are not required") - for package in package_results: - if not package["version_matches"]: - print(" VERSION DRIFT: %s = %s (baseline %s)" % ( - package["package"], package["version"], package["expected_version"])) - print(" packages verified = %d" % len(package_results)) - for failure in package_failures: - print(" FAIL: %s" % failure) - else: - print(" SKIPPED: no package manifest supplied") + print_native_report(oracle) + print_payload_report(args.payload_root, payload_results, payload_failures) + print_package_report(package_manifest, args.package_baseline, package_results, package_failures) if args.json_report: with open(args.json_report, "w", encoding="utf-8") as f: From fa0c69dafde7777dc0f96397a2ad84410a26362b Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 16 Oct 2026 15:32:01 -0500 Subject: [PATCH 6/9] Clarify ARM64 validator docstrings --- ci/validate_arm64_architecture.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/ci/validate_arm64_architecture.py b/ci/validate_arm64_architecture.py index 19edc83f..6ab9ccd1 100644 --- a/ci/validate_arm64_architecture.py +++ b/ci/validate_arm64_architecture.py @@ -2,12 +2,11 @@ # SPDX-FileCopyrightText: 2026 OpenShot Studios, LLC # SPDX-License-Identifier: LGPL-3.0-or-later -"""Validate Windows ARM64 host, payload, and dependency architectures. +"""Validate Windows ARM64 host, payload, and dependency architectures.""" -The Windows host API and running interpreter PE header must both identify -native ARM64 execution. A separate recursive scan checks every payload PE. -Package manifests can enforce exact versions or report baseline drift. -""" +# The Windows host API and interpreter PE header must both identify native +# ARM64 execution. A separate recursive scan checks every payload PE. +# Package manifests can enforce exact versions or report baseline drift. import argparse import ctypes @@ -36,12 +35,7 @@ def machine_name(value): def read_native_process_oracle(): - """ - Query IsWow64Process2 for the current process. Returns a dict with the - exact observed pProcessMachine / pNativeMachine values, the derived - WOW/emulation state, and a pass/fail verdict against - design-amendment-A1's native-execution requirement. - """ + """Report host API values, interpreter architecture, and native ARM64 status.""" result = { "checked": False, "process_machine": None, From ace200bf084c2b251ada3339659f7dfcd3c421e5 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Fri, 16 Oct 2026 15:33:26 -0500 Subject: [PATCH 7/9] Normalize remaining validator docstrings --- ci/validate_arm64_architecture.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/ci/validate_arm64_architecture.py b/ci/validate_arm64_architecture.py index 6ab9ccd1..f4685e4f 100644 --- a/ci/validate_arm64_architecture.py +++ b/ci/validate_arm64_architecture.py @@ -97,11 +97,7 @@ def read_native_process_oracle(): def read_pe_machine(path): - """ - Read the COFF "Machine" field directly from a PE file's file header, - without requiring llvm-readobj/dumpbin/objdump. Returns an int machine - value, or None if the file is not a valid PE (e.g. a script stub). - """ + """Read the COFF machine field directly, returning None for invalid PE files.""" with open(path, "rb") as f: dos_header = f.read(64) if len(dos_header) < 64 or dos_header[0:2] != b"MZ": @@ -119,12 +115,7 @@ def read_pe_machine(path): def scan_payload_architecture(root): - """ - Recursively scan `root` for .exe/.dll/.pyd files and read each PE - Machine field. Returns (results, failures) where results is a list of - {"path", "machine", "machine_name"} and failures is a list of - human-readable problem strings (missing PE header, wrong architecture). - """ + """Recursively return PE architecture results and failures for a payload.""" results = [] failures = [] if not os.path.isdir(root): From f1f08bb53c739eafa6f0a9865beb7f3412f280b2 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 17 Oct 2026 14:47:58 -0500 Subject: [PATCH 8/9] Run native Linux and Windows ARM64 build checks automatically --- .github/workflows/ci.yml | 41 +++++++++++++++++++++++++------- ci/README-arm64.md | 19 ++++++++++----- ci/audio-smoke/CMakeLists.txt | 15 ++++++++++++ ci/audio-smoke/main.cpp | 43 ++++++++++++++++++++++++++++++++++ ci/windows-arm64-packages.lock | 18 +------------- 5 files changed, 105 insertions(+), 31 deletions(-) create mode 100644 ci/audio-smoke/CMakeLists.txt create mode 100644 ci/audio-smoke/main.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a105b544..fa9ca202 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,12 +57,9 @@ jobs: cmake --build . --target install -- VERBOSE=1 popd - # Supplemental native Arm64 presubmit. Its output is never a release - # artifact; GitLab remains the production artifact chain. - build-arm64-presubmit: - if: ${{ vars.ENABLE_WINDOWS_ARM64 == '1' }} + windows-arm64: + name: Windows ARM64 build and tests runs-on: windows-11-arm - continue-on-error: true steps: - name: Preserve repository line endings run: git config --global core.autocrlf input @@ -82,7 +79,7 @@ jobs: mapfile -t packages < <(sed -n '/^[^#[:space:]][^=]*=/s/=.*//p' ci/windows-arm64-packages.lock) pacman --noconfirm -S --needed -- "${packages[@]}" - - name: Build libopenshot-audio (CLANGARM64, presubmit only) + - name: Build libopenshot-audio (CLANGARM64, ARM64) shell: msys2 {0} run: | cmake -B build -S . -G Ninja \ @@ -92,12 +89,40 @@ jobs: -DCMAKE_BUILD_TYPE=Debug \ -DENABLE_AUDIO_DOCS=OFF cmake --build build - ctest --test-dir build --output-on-failure -VV cmake --install build + cmake -S ci/audio-smoke -B smoke-build -G Ninja \ + -DCMAKE_PREFIX_PATH="$PWD/dist-arm64" -DREQUIRE_ARM64=ON + cmake --build smoke-build + PATH="$PWD/dist-arm64/bin:$PATH" ctest --test-dir smoke-build --output-on-failure --no-tests=error python -m unittest discover -s ci -p "test_*.py" -v python ci/validate_arm64_architecture.py \ --require-native-arm64 \ --package-baseline ci/windows-arm64-packages.lock \ --payload-root dist-arm64 \ --require-payload \ - --json-report build/arm64-presubmit-report.json + --json-report build/arm64-report.json + + linux-arm64: + name: Linux ARM64 build and tests + runs-on: ubuntu-24.04-arm + steps: + - uses: actions/checkout@v4 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y cmake ninja-build libasound2-dev python3 + - name: Build and install audio library + run: | + test "$(uname -m)" = aarch64 + cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="$PWD/install-arm64" -DENABLE_AUDIO_DOCS=OFF + cmake --build build --parallel 2 + cmake --install build + readelf -h install-arm64/lib/libopenshot-audio.so | grep -Eq 'Machine:.*AArch64' + - name: Test installed audio library and validator + run: | + cmake -S ci/audio-smoke -B smoke-build -G Ninja \ + -DCMAKE_PREFIX_PATH="$PWD/install-arm64" -DREQUIRE_ARM64=ON + cmake --build smoke-build + ctest --test-dir smoke-build --output-on-failure --no-tests=error + python3 -m unittest discover -s ci -p 'test_*.py' -v diff --git a/ci/README-arm64.md b/ci/README-arm64.md index 50b02771..5a8bee17 100644 --- a/ci/README-arm64.md +++ b/ci/README-arm64.md @@ -3,16 +3,19 @@ SPDX-FileCopyrightText: 2026 OpenShot Studios, LLC SPDX-License-Identifier: LGPL-3.0-or-later --> -# Experimental Windows ARM64 builds +# ARM64 build checks The `arm64-support` branch collects build support before integration into `develop`. Existing Linux, Intel Mac, and Windows x86/x64 jobs remain enabled. -Set `ENABLE_WINDOWS_ARM64=1` as a GitLab CI variable to enable the optional -job on a native Windows runner tagged `windows-arm64`, with MSYS2 CLANGARM64 -installed at `C:\msys64`. GitHub uses the repository Actions variable of the -same name to enable its supplemental `windows-11-arm` job. Neither job -publishes a release. +GitHub automatically runs **Linux ARM64 build and tests** on +`ubuntu-24.04-arm` and **Windows ARM64 build and tests** on `windows-11-arm` +for pushes and pull requests. No repository enable variables are needed, and +failures are reported normally. These checks do not publish releases. + +The production GitLab configuration is unchanged: its optional Windows ARM64 +job still requires `ENABLE_WINDOWS_ARM64=1`, a runner tagged `windows-arm64`, +and MSYS2 CLANGARM64 installed at `C:\msys64`. `windows-arm64-packages.lock` is a reference package list. GitHub installs currently available packages by name, and the validator records actual @@ -25,3 +28,7 @@ Native validation requires both the Windows host API result and an ARM64 PE header on the running Python interpreter. Every produced executable, DLL, and Python extension is also checked independently. A Linux validator test pass is not evidence of native Windows execution. + +The audio checks build a consumer of the installed library, apply gain to +stereo samples, and verify a WAV encode/decode round trip without requiring +an audio device. They also run the architecture-validator unit tests. diff --git a/ci/audio-smoke/CMakeLists.txt b/ci/audio-smoke/CMakeLists.txt new file mode 100644 index 00000000..734e4a72 --- /dev/null +++ b/ci/audio-smoke/CMakeLists.txt @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 OpenShot Studios, LLC +# SPDX-License-Identifier: LGPL-3.0-or-later + +cmake_minimum_required(VERSION 3.16) +project(AudioSmokeTest LANGUAGES CXX) +find_package(OpenShotAudio REQUIRED) +option(REQUIRE_ARM64 "Require a native ARM64 test executable" OFF) +add_executable(audio-smoke main.cpp) +target_compile_features(audio-smoke PRIVATE cxx_std_17) +target_link_libraries(audio-smoke PRIVATE OpenShot::Audio) +if(REQUIRE_ARM64) + target_compile_definitions(audio-smoke PRIVATE REQUIRE_ARM64=1) +endif() +enable_testing() +add_test(NAME audio-buffer-roundtrip COMMAND audio-smoke) diff --git a/ci/audio-smoke/main.cpp b/ci/audio-smoke/main.cpp new file mode 100644 index 00000000..f28cee68 --- /dev/null +++ b/ci/audio-smoke/main.cpp @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: 2026 OpenShot Studios, LLC +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include +#include +#include +#include "OpenShotAudio.h" + +#if defined(REQUIRE_ARM64) && !defined(__aarch64__) && !defined(_M_ARM64) +#error The ARM64 smoke test must be compiled for ARM64 +#endif + +int main() +{ + // Exercise the installed library without requiring an audio device. + juce::AudioBuffer samples(2, 128); + for (int i = 0; i < samples.getNumSamples(); ++i) { + samples.setSample(0, i, 0.5f); + samples.setSample(1, i, -0.25f); + } + samples.applyGain(0.5f); + juce::MemoryBlock encoded; + juce::WavAudioFormat format; + auto output = std::make_unique(encoded, false); + std::unique_ptr writer( + format.createWriterFor(output.get(), 48000, 2, 16, {}, 0)); + if (!writer) return 1; + output.release(); // The writer owns the stream after successful creation. + if (!writer->writeFromAudioSampleBuffer(samples, 0, samples.getNumSamples())) return 1; + writer.reset(); // Finalize the WAV header before reading it. + std::unique_ptr reader(format.createReaderFor( + new juce::MemoryInputStream(encoded, false), true)); + if (!reader || reader->numChannels != 2 || reader->sampleRate != 48000 + || reader->lengthInSamples != 128) return 1; + juce::AudioBuffer decoded(2, 128); + if (!reader->read(&decoded, 0, 128, 0, true, true)) return 1; + for (int i = 0; i < decoded.getNumSamples(); ++i) { + if (std::abs(decoded.getSample(0, i) - 0.25f) > 0.0001f + || std::abs(decoded.getSample(1, i) + 0.125f) > 0.0001f) return 1; + } + std::cout << "Installed audio library: buffer gain and WAV round trip passed\n"; + return 0; +} diff --git a/ci/windows-arm64-packages.lock b/ci/windows-arm64-packages.lock index 7301ecc7..b10da897 100644 --- a/ci/windows-arm64-packages.lock +++ b/ci/windows-arm64-packages.lock @@ -7,7 +7,7 @@ # reference. It still rejects missing packages. --package-lock instead # requires an exact version match, for a separately provisioned environment. # This file does not provide archived packages or verify archive checksums. -# Keep the audio and library copies in sync when refreshing the reference. +# This list contains the audio build and validation dependencies. # # name=version-release,sha256 mingw-w64-clang-aarch64-clang=22.1.8-2,UNVERIFIED-NO-SIGNED-SNAPSHOT @@ -17,20 +17,4 @@ mingw-w64-clang-aarch64-llvm-openmp=22.1.8-1,UNVERIFIED-NO-SIGNED-SNAPSHOT mingw-w64-clang-aarch64-libwinpthread=14.0.0.r302.gd7f3c5201-1,UNVERIFIED-NO-SIGNED-SNAPSHOT mingw-w64-clang-aarch64-cmake=4.4.2-2,UNVERIFIED-NO-SIGNED-SNAPSHOT mingw-w64-clang-aarch64-ninja=1.13.2-1,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-swig=4.5.0-1,UNVERIFIED-NO-SIGNED-SNAPSHOT mingw-w64-clang-aarch64-python=3.14.7-1,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-python-pyqt6=6.11.0-1,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-python-pyqt6-sip=13.12.0-1,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-qt6-base=6.11.2-2,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-qt6-svg=6.11.2-1,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-qt6-multimedia=6.11.2-1,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-qt6-imageformats=6.11.2-1,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-ffmpeg=9.0.1-3,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-opencv=5.0.0-3,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-protobuf=35.1-2,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-zeromq=4.3.5-5,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-cppzmq=4.11.0-1,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-babl=0.1.128-1,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-imagemagick=7.1.2.30-1,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-jsoncpp=1.9.8-1,UNVERIFIED-NO-SIGNED-SNAPSHOT -mingw-w64-clang-aarch64-zlib=1.3.2-2,UNVERIFIED-NO-SIGNED-SNAPSHOT From 02b5d664d2b0695a7948e594aad72e28ba29d621 Mon Sep 17 00:00:00 2001 From: Jonathan Thomas Date: Sat, 17 Oct 2026 14:52:28 -0500 Subject: [PATCH 9/9] Use built-in alignment queries with the Windows ARM toolchain --- .../modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h index ddff0df8..586416a0 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h @@ -1193,7 +1193,7 @@ class AudioBuffer jassert (size >= 0); auto channelListSize = (size_t) (numChannels + 1) * sizeof (Type*); - auto requiredSampleAlignment = std::alignment_of_v; + auto requiredSampleAlignment = alignof (Type); size_t alignmentOverflow = channelListSize % requiredSampleAlignment; if (alignmentOverflow != 0)