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
2 changes: 0 additions & 2 deletions .github/workflows/all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,6 @@ jobs:
CCACHE_IGNOREOPTIONS: "-fno-strict-overflow -fwrapv -W* -arch x86_64 arm64 -dynamic -fno-common -g -I/usr/local/opt/*"
CCACHE_LOGFILE: ${{ github.workspace }}/.ccache_log
USE_ASAN: "1"
USE_PORTABLE_SIMD: "1" # Use portable SIMD to avoid "Illegal instruction" on different runner CPUs
DISABLE_LTO: "1" # Speeds up un-cacheable link step which doesn't really increase performance in tests anyways
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}
Expand Down Expand Up @@ -798,7 +797,6 @@ jobs:
env:
DEBUG: "0"
USE_ASAN: "1"
USE_PORTABLE_SIMD: "1" # Use portable SIMD to avoid "Illegal instruction" on different runner CPUs
CCACHE_DIR: ${{ github.workspace }}/.ccache
CCACHE_BASEDIR: ${{ github.workspace }}
CCACHE_DEBUGDIR: ${{ github.workspace }}/.ccache_debug
Expand Down
97 changes: 97 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Pedalboard is a Python library (with C++ internals) for audio processing: reading, writing, rendering, and adding effects. Built by Spotify's Audio Intelligence Lab using pybind11 bindings over the JUCE framework.

## Build Commands

```bash
# Clone with submodules (required for vendors/JUCE)
git clone --recurse-submodules --shallow-submodules git@github.com:spotify/pedalboard.git

# Install build deps and build
pip install pybind11 scikit-build-core
pip install --no-build-isolation -ve .

# Fast debug build with ccache (macOS)
rm -rf build && CC="ccache clang" CXX="ccache clang++" DEBUG=1 python3 -m pip install -e .

# Fast debug build with ccache (Linux, GCC)
rm -rf build && CC="ccache gcc" CXX="scripts/ccache_g++" DEBUG=1 python3 setup.py build -j8 develop
```

Build environment variables: `DEBUG=1`, `DISABLE_LTO`, `USE_MARCH_NATIVE`, `USE_ASAN`, `USE_TSAN`, `USE_MSAN`.

## Testing

```bash
# Run all tests
pytest tests/

# Run a single test file
pytest tests/test_python_interface.py -v

# Run a single test
pytest tests/test_python_interface.py::test_no_transforms

# Via tox (runs coverage)
tox
```

Test dependencies are in `test-requirements.txt`. CI parallelizes tests via `NUM_TEST_WORKERS` and `TEST_WORKER_INDEX` env vars (see `tests/conftest.py`).

## Linting and Formatting

```bash
# Python lint
ruff check pedalboard

# Python format check
ruff format --check pedalboard --diff

# Type checking
pyright pedalboard tests/test_*.py

# C++ formatting (clang-format 14, LLVM style)
# Applied to pedalboard/ C++ files, excluding vendors/
```

Ruff config is in `pyproject.toml`: line-length 100, target Python 3.10+.

## Architecture

**Python layer** (`pedalboard/`):
- `__init__.py` — re-exports from `pedalboard_native` (the compiled C++ module)
- `_pedalboard.py` — `Pedalboard` class (effect chain container) and `load_plugin()` for VST3/AU
- `io/` — `AudioFile` (read/write WAV, FLAC, MP3, OGG, AIFF) and `AudioStream` (live audio)

**C++ native module** (`pedalboard_native`):
- `pedalboard/python_bindings.cpp` — main pybind11 entry point
- `pedalboard/plugins/*.h` — individual effect plugin implementations (Compressor, Reverb, Delay, Chorus, etc.), each wrapping a JUCE processor via `JucePlugin<T>` template
- `pedalboard/plugin_templates/` — reusable plugin wrappers (Resample, ForceMono, etc.)
- `pedalboard/juce_overrides/` — patched JUCE module inclusions

**Type stubs** (`pedalboard_native/*.pyi`):
- Generated via `pybind11-stubgen`, post-processed by `scripts/postprocess_type_hints`
- Update process documented in CONTRIBUTING.md

**Vendor libraries** (`vendors/`): FFTW3, RubberBand, LAME, libgsm, libFLAC — compiled as part of the build.

**Build system**: scikit-build-core (primary, via `pyproject.toml` + `CMakeLists.txt`) with legacy `setup.py` still functional. All `.cpp` and `.mm` files under `pedalboard/` are compiled automatically.

## Platform Notes

- **macOS**: Links Accelerate, AudioToolbox, CoreAudio, and other frameworks. Supports Audio Units.
- **Linux**: Requires libfreetype-dev, xorg-dev, ALSA. FFTW3 used for FFT (vendored).
- **Windows**: MSVC required. No AU support.
- C++17 required. SIMD: AVX on x86, NEON on ARM.

## Key Design Patterns

- Plugin architecture: `Plugin` base → `JucePlugin<juce::dsp::SomeProcessor>` template
- Container composition: `Pedalboard` extends `Chain` for serial/parallel plugin graphs
- GIL is released during C++ audio processing for thread safety
- External plugin hosting: VST3 cross-platform, Audio Units macOS-only
13 changes: 7 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,14 @@ elseif(UNIX AND NOT APPLE)
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|aarch64")
add_compile_definitions(HAVE_NEON=1)
else()
# Use -march=native for local builds to optimize for the current CPU,
# but use a portable baseline for CI builds to avoid "Illegal instruction" errors
# when ccache restores objects built on different runner hardware.
if(DEFINED ENV{USE_PORTABLE_SIMD})
add_compile_options(-mavx)
else()
# Use -mavx by default for portable binaries (AVX is baseline since
# Sandy Bridge, 2011). Set USE_MARCH_NATIVE=1 to optimize for the
# local CPU instead (useful for developer builds, but the resulting
# binaries may crash on other hardware).
if(DEFINED ENV{USE_MARCH_NATIVE})
add_compile_options(-march=native)
else()
add_compile_options(-mavx)
endif()
add_compile_definitions(HAVE_AVX)
endif()
Expand Down
13 changes: 7 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,14 @@ def ignore_files_matching(files, *matches):
else:
# And on x86, ignore the ARM-specific SIMD code (and KCVI; not GCC or Clang compatible).
fftw_paths = ignore_files_matching(fftw_paths, "neon")
# Use -march=native for local builds to optimize for the current CPU,
# but use a portable baseline for CI builds to avoid "Illegal instruction" errors
# when ccache restores objects built on different runner hardware.
if os.getenv("USE_PORTABLE_SIMD"):
ALL_CFLAGS.append("-mavx")
else:
# Use -mavx by default for portable binaries (AVX is baseline since
# Sandy Bridge, 2011). Set USE_MARCH_NATIVE=1 to optimize for the
# local CPU instead (useful for developer builds, but the resulting
# binaries may crash on other hardware).
if os.getenv("USE_MARCH_NATIVE"):
ALL_CFLAGS.append("-march=native")
else:
ALL_CFLAGS.append("-mavx")
# Enable SIMD instructions:
ALL_CFLAGS.extend(
[
Expand Down
Loading