Skip to content

pickle: Support pickling and copying plugins and Pedalboards - #529

Open
Mohith26 wants to merge 1 commit into
spotify:masterfrom
Mohith26:pickle-support
Open

Mohith26 wants to merge 1 commit into
spotify:masterfrom
Mohith26:pickle-support

Conversation

@Mohith26

@Mohith26 Mohith26 commented Nov 4, 2026

Copy link
Copy Markdown

Closes #96, and makes the parallel processing case in #212 work with process pools.

Plugins and boards can't currently be pickled, so you can't hand a Pedalboard to a ProcessPoolExecutor, a PyTorch DataLoader with num_workers > 0 (spawn on macOS/Windows), Ray or Dask, save one alongside a dataset, or copy.deepcopy it.

This follows the approach @psobot sketched in the issue: Plugin.__reduce__ records the class, any constructor-only arguments, and every writable property. Unpickling constructs a fresh plugin and re-applies the properties, so new plugins whose parameters are properties are covered automatically.

  • Pedalboard, Chain and Mix pickle their plugins recursively (shared plugins stay shared).
  • Convolution pickles its IR by filename, or by value if it was passed as an array. Adds a read-only impulse_response_sample_rate property for that.
  • VST3Plugin / AudioUnitPlugin pickle their path and raw_state and reload from the path. Adds a read-only path_to_plugin_file property.
  • Internal test plugins raise TypeError rather than silently dropping arguments.
  • Only configuration is pickled, not DSP state, so an unpickled plugin behaves as if it was just reset. Documented in the FAQ, along with the external-file caveats and the usual "only unpickle trusted data" note.

Tests (tests/test_pickle.py): parameter and output-audio round trips for every built-in plugin with non-default settings, all pickle protocols, nested and empty containers, shared references, both Convolution modes, copy/deepcopy, unpickling in a fresh interpreter, and a guard that fails if any plugin constructor argument is neither a writable property nor handled explicitly.

How I tested (macOS arm64, Python 3.12): full pytest tests (27,130 passed; one timing-based GIL test in test_io.py failed under load and passes on rerun), ruff check / ruff format --check, clang-format 14, and pyright on the changed files (0 errors). The external plugin tests could not run locally because loading any VST3/AU aborts in my sandboxed environment (before reaching this code), so those two tests are relying on CI.

I used an AI assistant while writing this and reviewed and tested all of it myself.

Problem

Pedalboard plugins can't be pickled (spotify#96), so a Pedalboard can't be
passed to a process pool, a PyTorch DataLoader with workers, Ray or Dask
(spotify#212), saved alongside a dataset, or copied with copy.deepcopy.

Solution

Add a __reduce__ to Plugin that records the plugin's class, any
constructor-only arguments, and the value of every writable property.
Unpickling builds a fresh plugin and re-applies those properties.

- Pedalboard, Chain and Mix pickle their plugins recursively.
- Convolution pickles its impulse response (by filename, or by value
  if it was passed as an array). This adds a read-only
  impulse_response_sample_rate property.
- VST3Plugin and AudioUnitPlugin pickle their path and raw_state, and
  are reloaded from that path. This adds a read-only
  path_to_plugin_file property.
- Internal test plugins raise a TypeError instead of losing arguments.

Only configuration is pickled, not DSP state: an unpickled plugin
behaves as if it was just reset. This is documented in the FAQ.

Result

All built-in plugins round-trip through pickle with identical
parameters and identical output audio, including in a new process. A
test also checks that every plugin constructor argument is either a
writable property or handled explicitly, so new plugins can't silently
lose arguments when pickled.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pedalboard objects are not pickleable or serializable

1 participant