Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions .github/workflows/all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,42 @@ jobs:
cc: "msvc",
cxx: "msvc",
}
# Python 3.10 isn't available for Windows on ARM64.
- {
os: windows-11-arm,
python-version: "3.11",
compiler: "msvc",
cc: "msvc",
cxx: "msvc",
}
- {
os: windows-11-arm,
python-version: "3.12",
compiler: "msvc",
cc: "msvc",
cxx: "msvc",
}
- {
os: windows-11-arm,
python-version: "3.13",
compiler: "msvc",
cc: "msvc",
cxx: "msvc",
}
- {
os: windows-11-arm,
python-version: "3.14",
compiler: "msvc",
cc: "msvc",
cxx: "msvc",
}
- {
os: windows-11-arm,
python-version: "3.15",
compiler: "msvc",
cc: "msvc",
cxx: "msvc",
}
name: Test with Python ${{ matrix.python-version }} on ${{ matrix.os }}
steps:
- name: Set up Python ${{ matrix.python-version }}
Expand Down Expand Up @@ -752,6 +788,17 @@ jobs:
# Inline install here on purpose, as this is only required for test _setup_ and not running
pip install google-cloud-storage
python ./tests/download_test_plugins.py
# Most of pedalboard's external plugin code is only covered by tests that load plugins, so
# don't enforce the coverage minimum on ARM64 until ARM64 test plugins have been committed
# (by the "Build Windows Test Plugins" workflow):
- name: Relax coverage minimum if no ARM64 test plugins are available
if: matrix.os == 'windows-11-arm'
shell: bash
run: |
if ! compgen -G "tests/plugins/*/Windows/*.vst3/Contents/arm64-win/*.vst3" > /dev/null; then
echo "::warning::No Windows ARM64 test plugins found; external plugin tests will be skipped and coverage won't be enforced."
echo "MINIMUM_COVERAGE_PERCENTAGE=0" >> "$GITHUB_ENV"
fi
- name: Run tests
if: matrix.os != 'ubuntu-24.04' || matrix.python-version != '3.10'
run: pytest --maxfail=4 --cov-report term --cov-fail-under=${{ env.MINIMUM_COVERAGE_PERCENTAGE }} --cov=pedalboard --durations=100
Expand Down Expand Up @@ -890,6 +937,11 @@ jobs:
- { os: windows-latest, build: cp313-win_amd64 }
- { os: windows-latest, build: cp314-win_amd64 }
- { os: windows-latest, build: cp315-win_amd64 }
- { os: windows-11-arm, build: cp311-win_arm64 }
- { os: windows-11-arm, build: cp312-win_arm64 }
- { os: windows-11-arm, build: cp313-win_arm64 }
- { os: windows-11-arm, build: cp314-win_arm64 }
- { os: windows-11-arm, build: cp315-win_arm64 }
# - { os: windows-latest, build: cp312-win32 }
- { os: "ubuntu-24.04", build: cp310-manylinux_x86_64, python: "3.10" }
- { os: "ubuntu-24.04-arm", build: cp310-manylinux_aarch64 }
Expand Down
99 changes: 99 additions & 0 deletions .github/workflows/build-windows-test-plugins.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Build Windows Test Plugins

# Builds the VST3 plugins in tests/plugins/*/Windows from pinned source commits
# (see scripts/build_windows_test_plugins/build.ps1), as prebuilt plugin binaries
# can't be accepted in pull requests. The built plugins are always uploaded as
# workflow artifacts, and are optionally committed to the branch that this
# workflow was run on.
on:
workflow_dispatch:
inputs:
architectures:
description: Which architectures to build plugins for
type: choice
options:
- arm64
- x64
- arm64 and x64
default: arm64
commit:
description: Commit the built plugins to the branch this workflow is run on
type: boolean
default: false

permissions:
contents: read

jobs:
select-architectures:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.matrix.outputs.matrix }}
steps:
- id: matrix
env:
ARCHITECTURES: ${{ inputs.architectures }}
run: |
arm64='{"architecture": "ARM64", "bundle-folder": "arm64-win", "os": "windows-11-arm"}'
x64='{"architecture": "x64", "bundle-folder": "x86_64-win", "os": "windows-latest"}'
case "$ARCHITECTURES" in
arm64) include="$arm64" ;;
x64) include="$x64" ;;
*) include="$arm64, $x64" ;;
esac
echo "matrix={\"include\": [$include]}" >> "$GITHUB_OUTPUT"

build:
needs: select-architectures
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.select-architectures.outputs.matrix) }}
runs-on: ${{ matrix.os }}
name: Build ${{ matrix.architecture }} plugins
steps:
- uses: actions/checkout@v4
- name: Check out JUCE
run: git submodule update --init --depth 1 JUCE
- name: Build plugins
shell: pwsh
run: ./scripts/build_windows_test_plugins/build.ps1 -Architecture ${{ matrix.architecture }}
- uses: actions/upload-artifact@v4
with:
name: windows-test-plugins-${{ matrix.bundle-folder }}
path: tests/plugins/*/Windows/*.vst3/Contents/${{ matrix.bundle-folder }}/
if-no-files-found: error

commit:
needs: build
if: inputs.commit
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Ensure this workflow was run on a branch
if: github.ref_type != 'branch'
run: |
echo "::error::Plugins can only be committed when this workflow is run on a branch."
exit 1
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
pattern: windows-test-plugins-*
path: tests/plugins
merge-multiple: true
- name: Commit and push plugins
env:
ARCHITECTURES: ${{ inputs.architectures }}
BRANCH: ${{ github.ref_name }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add tests/plugins
if git diff --cached --quiet; then
echo "The built plugins are identical to the committed ones; nothing to commit."
exit 0
fi
git status --short
git commit -m "Update Windows test plugins ($ARCHITECTURES)" -m "Built by $RUN_URL"
git push origin "HEAD:refs/heads/$BRANCH"
30 changes: 24 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,15 @@ if(WIN32 AND NOT DEFINED Python_LIBRARY)
set(Python_VERSION_PATCH ${CMAKE_MATCH_3})

set(DYNAMIC_PYTHON_LIB_FILENAME "python${Python_VERSION_MAJOR}${Python_VERSION_MINOR}.lib")
set(EXPECTED_PYTHON_VERSION_DIR "C:/hostedtoolcache/windows/Python/${Python_VERSION}/x64/libs")
# Ask the interpreter where it lives rather than assuming an x64 hostedtoolcache layout,
# so that this also works for ARM64 and virtual environments.
execute_process(
COMMAND ${Python_EXECUTABLE} -c "import sys; print(sys.base_prefix)"
OUTPUT_VARIABLE PYTHON_BASE_PREFIX
OUTPUT_STRIP_TRAILING_WHITESPACE
)
file(TO_CMAKE_PATH "${PYTHON_BASE_PREFIX}" PYTHON_BASE_PREFIX)
set(EXPECTED_PYTHON_VERSION_DIR "${PYTHON_BASE_PREFIX}/libs")
set(Python_LIBRARY "${EXPECTED_PYTHON_VERSION_DIR}/${DYNAMIC_PYTHON_LIB_FILENAME}")

message(STATUS "DEBUG: On Windows, constructed DYNAMIC_PYTHON_LIB_FILENAME: ${DYNAMIC_PYTHON_LIB_FILENAME}")
Expand Down Expand Up @@ -142,6 +150,7 @@ if(APPLE)
find_library(FOUNDATION_FRAMEWORK Foundation REQUIRED)
find_library(IOKIT_FRAMEWORK IOKit REQUIRED)
find_library(QUARTZ_FRAMEWORK QuartzCore REQUIRED)
find_library(SECURITY_FRAMEWORK Security REQUIRED)
find_library(WEBKIT_FRAMEWORK WebKit REQUIRED)

set(MACOS_FRAMEWORKS
Expand All @@ -155,6 +164,7 @@ if(APPLE)
${FOUNDATION_FRAMEWORK}
${IOKIT_FRAMEWORK}
${QUARTZ_FRAMEWORK}
${SECURITY_FRAMEWORK}
${WEBKIT_FRAMEWORK}
)

Expand All @@ -169,6 +179,9 @@ elseif(WIN32)
# Windows specific flags
add_compile_definitions(WINDOWS=1 USE_BUILTIN_FFT NO_THREADING)
add_compile_definitions(JUCE_DLL_BUILD=1)
# JUCE 7's DLL allocators (injected via JUCE_LEAK_DETECTOR) cause ambiguous
# operator new/delete errors in classes with multiple JUCE base classes:
add_compile_definitions(JUCE_DISABLE_DLL_ALLOCATORS=1)

# python_bindings.cpp generates a very large number of COFF sections (from
# the pybind11 template instantiations); /bigobj raises the per-object
Expand Down Expand Up @@ -256,10 +269,11 @@ list(APPEND JUCE_COMPILE_DEFINITIONS
)

if(DEFINED ENV{CIBW_BUILD} AND "$ENV{CIBW_BUILD}" MATCHES "musllinux")
# For Alpine/musllinux compatibility:
# For Alpine/musllinux compatibility. musl doesn't define these glibc-only
# nl_langinfo() items, for which it returns an empty string:
add_compile_definitions(
_NL_IDENTIFICATION_LANGUAGE=0x42
_NL_IDENTIFICATION_TERRITORY=0x43
_NL_ADDRESS_LANG_AB=0x42
_NL_ADDRESS_COUNTRY_AB2=0x43
_LARGEFILE64_SOURCE
)
# musl doesn't provide execinfo.h; use our stub in vendors/
Expand All @@ -269,7 +283,6 @@ endif()
# Rubber Band library flags
list(APPEND RUBBERBAND_COMPILE_DEFINITIONS
USE_BQRESAMPLER=1
_HAS_STD_BYTE=0
NOMINMAX
ALREADY_CONFIGURED
)
Expand Down Expand Up @@ -356,9 +369,9 @@ target_compile_definitions(pedalboard_native PRIVATE ${RUBBERBAND_COMPILE_DEFINI

# LAME config
if(WIN32)
# lame_config.h defines HAVE_XMMINTRIN_H on x64 only, as SSE isn't available on ARM64.
target_compile_definitions(pedalboard_native PRIVATE
HAVE_MPGLIB
HAVE_XMMINTRIN_H
)
target_compile_options(pedalboard_native PRIVATE
"/FI${CMAKE_CURRENT_SOURCE_DIR}/vendors/lame_config.h"
Expand All @@ -375,6 +388,11 @@ endif()
# Link libraries
if(APPLE)
target_link_libraries(pedalboard_native PRIVATE ${MACOS_FRAMEWORKS})
# JUCE 7's juce_gui_basics weakly links against these:
target_link_options(pedalboard_native PRIVATE
"SHELL:-weak_framework Metal"
"SHELL:-weak_framework MetalKit"
)
elseif(WIN32)
target_link_libraries(pedalboard_native PRIVATE ${PLATFORM_LIBRARIES})
# Link Python libraries
Expand Down
2 changes: 1 addition & 1 deletion JUCE
Submodule JUCE updated 2782 files
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ If you are new to Python, follow [INSTALLATION.md](https://github.com/spotify/pe
- Compatible with a wide range of VSTs and Audio Units
- Windows
- Tested automatically on GitHub with VSTs
- Platform wheels available for `amd64` (x86-64, Intel/AMD)
- Platform wheels available for `amd64` (x86-64, Intel/AMD) and `arm64` (Windows on ARM, Python 3.11+)
- VST3 plugins must match the architecture of the Python interpreter (i.e.: ARM64 Python can't load x64-only plugins)

## Examples

Expand Down Expand Up @@ -310,7 +311,7 @@ To cite via BibTeX:

`pedalboard` is licensed under the [GNU General Public License v3](https://www.gnu.org/licenses/gpl-3.0.en.html). `pedalboard` includes a number of libraries that are statically compiled, and which carry the following licenses:

- The core audio processing code is pulled from [JUCE 6](https://juce.com/), which is [dual-licensed under a commercial license and the GPLv3](https://juce.com/juce-6-licence).
- The core audio processing code is pulled from [JUCE 7](https://juce.com/), which is [dual-licensed under a commercial license and the GPLv3](https://juce.com/juce-7-licence).
- The [VST3 SDK](https://github.com/steinbergmedia/vst3sdk), bundled with JUCE, is owned by [Steinberg® Media Technologies GmbH](https://www.steinberg.net/en/home.html) and licensed under the GPLv3.
- The `PitchShift` plugin and `time_stretch` functions use [the Rubber Band Library](https://github.com/breakfastquay/rubberband), which is [dual-licensed under a commercial license](https://breakfastquay.com/technology/license.html) and the GPLv2 (or newer). [FFTW](https://www.fftw.org/) is also included to speed up Rubber Band, and [is licensed under the GPLv2 (or newer)](https://www.fftw.org/doc/License-and-Copyright.html).
- The `MP3Compressor` plugin uses [libmp3lame from the LAME project](https://lame.sourceforge.io/), which is [licensed under the LGPLv2](https://github.com/lameproject/lame/blob/master/README) and [upgraded to the GPLv3 for inclusion in this project (as permitted by the LGPLv2)](https://www.gnu.org/licenses/gpl-faq.html#AllCompatibility).
Expand Down
12 changes: 6 additions & 6 deletions pedalboard/io/AudioStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,10 @@ class AudioStream : public std::enable_shared_from_this<AudioStream>
}

#ifdef JUCE_MODULE_AVAILABLE_juce_audio_devices
virtual void audioDeviceIOCallback(const float **inputChannelData,
int numInputChannels,
float **outputChannelData,
int numOutputChannels, int numSamples) {
void audioDeviceIOCallbackWithContext(
const float *const *inputChannelData, int numInputChannels,
float *const *outputChannelData, int numOutputChannels, int numSamples,
const juce::AudioIODeviceCallbackContext &) override {
// Live processing mode: run the input audio through a Pedalboard object.
if (playBufferFifo && recordBufferFifo) {
for (int i = 0; i < numOutputChannels; i++) {
Expand Down Expand Up @@ -390,7 +390,7 @@ class AudioStream : public std::enable_shared_from_this<AudioStream>
}
}

virtual void audioDeviceAboutToStart(juce::AudioIODevice *device) {
void audioDeviceAboutToStart(juce::AudioIODevice *device) override {
spec.sampleRate = deviceManager.getAudioDeviceSetup().sampleRate;
spec.maximumBlockSize = static_cast<juce::uint32>(
deviceManager.getAudioDeviceSetup().bufferSize);
Expand All @@ -403,7 +403,7 @@ class AudioStream : public std::enable_shared_from_this<AudioStream>
}
}

virtual void audioDeviceStopped() {
void audioDeviceStopped() override {
juce::SpinLock::ScopedLockType lock(livePedalboardMutex);

for (auto plugin : livePedalboard.getPlugins()) {
Expand Down
4 changes: 2 additions & 2 deletions pedalboard/io/LameMP3AudioFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ class LameMP3AudioFormat : public juce::AudioFormat {
flush();
};

virtual bool write(const int **samplesToWrite, int numSamples) {
bool write(const int **samplesToWrite, int numSamples) override {
std::vector<unsigned char> encodedMp3Buffer(1.25 * numSamples +
MAXIMUM_MP3_FRAME_SIZE);

Expand Down Expand Up @@ -229,7 +229,7 @@ class LameMP3AudioFormat : public juce::AudioFormat {
return true;
}

virtual bool flush() {
bool flush() override {
if (!output)
return false;
output->flush();
Expand Down
Loading