Skip to main content

Multi-library audio effects registry with a unified chain-application API

Project description

MultiAFX

Unified registry of 85 audio effects across 7 libraries (audiomentations, sox, torchaudio, scipy, librosa, pyloudnorm, numpy), with a clean chain-application API inspired by pedalboard.

  • One call signature for every effect: (audio, sr, **params) -> np.ndarray
  • JSON / YAML / pickle serialization of effect chains
  • Pure data format: chains are lists of dicts, portable across languages
  • Parameter ranges attached to every effect for random chain generation

Install

pip install multiafx

Requires Python ≥ 3.10.


Quickstart

import multiafx
import soundfile as sf

# Build a chain inline
chain = multiafx.FXChain([
    {"effect": "sox_highpass", "params": {"frequency": 80.0, "width_q": 0.707}},
    {"effect": "sox_compand",  "params": {"attack_time": 0.005,
                                          "decay_time": 0.1,
                                          "soft_knee_db": 6.0}},
    {"effect": "sox_reverb",   "params": {"reverberance": 40.0,
                                          "high_freq_damping": 50.0,
                                          "room_scale": 60.0,
                                          "stereo_depth": 80.0,
                                          "pre_delay": 20.0,
                                          "wet_gain": -6.0}},
])

# Load audio as (channels, samples) float32
audio, sr = sf.read("input.wav", always_2d=True)
audio = audio.T.astype("float32")

# Apply — pedalboard style
processed = chain(audio, sr)

sf.write("output.wav", processed.T, sr)

Loading chains from files

chain = multiafx.FXChain.load("preset.json")     # auto-detect by extension
chain = multiafx.FXChain.load("preset.yaml")
chain = multiafx.FXChain.load("preset.pkl")

# or explicitly
chain = multiafx.FXChain.from_json("preset.json")
chain = multiafx.FXChain.from_yaml("preset.yaml")
chain = multiafx.FXChain.from_pickle("preset.pkl")

JSON format (preset.json):

[
  {"effect": "sox_highpass", "params": {"frequency": 80.0, "width_q": 0.707}},
  {"effect": "sox_compand",  "params": {"attack_time": 0.005,
                                         "decay_time": 0.1,
                                         "soft_knee_db": 6.0}}
]

YAML format (preset.yaml):

- effect: sox_highpass
  params: {frequency: 80.0, width_q: 0.707}
- effect: sox_compand
  params:
    attack_time: 0.005
    decay_time: 0.1
    soft_knee_db: 6.0

Saving is symmetric: chain.to_json(path), chain.to_yaml(path), chain.to_pickle(path).


Building chains programmatically

chain = multiafx.FXChain()
chain.add("sox_highpass", frequency=100.0, width_q=0.707)
chain.add("sox_compand", attack_time=0.005, decay_time=0.1, soft_knee_db=4.0)
chain.add("sox_reverb", reverberance=30.0, high_freq_damping=50.0, room_scale=40.0,
          stereo_depth=100.0, pre_delay=15.0, wet_gain=-8.0)

# List-like mutation
chain.append({"effect": "sox_gain", "params": {"gain_db": 2.0}})
chain.insert(0, {"effect": "sox_highpass", "params": {"frequency": 40.0, "width_q": 0.7}})
del chain[-1]
chain.pop()
len(chain)
for step in chain: ...

Default parameters

Every effect has sensible defaults, so you can omit params entirely or pass only the parameters you care about:

# All defaults — just effect names
chain = multiafx.FXChain([
    {"effect": "sox_highpass"},
    {"effect": "sox_compand"},
    {"effect": "sox_reverb"},
])

# .add() with no kwargs also works
chain = multiafx.FXChain()
chain.add("sox_highpass")
chain.add("sox_compand")

# Override only the parameters you want; the rest fall back to defaults
chain = multiafx.FXChain([
    {"effect": "sox_highpass", "params": {"frequency": 200.0}},  # width_q defaults
    {"effect": "sox_compand"},                                    # all defaults
])

Random chain generation

import multiafx

chain = multiafx.generate_random_chain(
    num_fx=8,                                   # or (1, 8) for a random range
    seed=42,
    exclude_categories=["PITCH", "TIME"],       # skip content-altering effects
    exclude_effects=["sox_reverb"],
    no_consecutive_same_category=True,          # no EQ → EQ back-to-back
)

Registry introspection

import multiafx

multiafx.registry.list_effects()                  # all 85 names
multiafx.registry.list_effects(library="sox")     # filter by library
multiafx.registry.list_effects(category="EQ")     # filter by macro category
multiafx.registry.libraries()                     # 7 libraries
multiafx.registry.categories()                    # 12 MacroCategory enums

eff = multiafx.registry.get("sox_compand")
eff.name                # "sox_compand"
eff.library             # "sox"
eff.macro_category      # <MacroCategory.DYNAMICS: 'DYNAMICS'>
eff.param_ranges        # {"attack_time": ParamRange(0.001, 0.1), ...}

Effect Categories

Category Count Example effects
EQ 37 sox_highpass, am_peaking_filter, ta_equalizer_biquad
DYNAMICS 11 sox_compand, sox_gain, am_normalize
DISTORTION 5 sox_overdrive, am_tanh_distortion, am_bit_crush
REVERB 2 sox_reverb, am_air_absorption
DELAY 2 sox_echo, sox_echos
MODULATION 4 sox_chorus, sox_flanger, sox_phaser, sox_tremolo
PITCH 3 sox_pitch, lib_pitch_shift, am_pitch_shift
TIME 4 sox_tempo, sox_speed, lib_time_stretch, am_time_stretch
SPECTRAL 5 sox_deemph, ta_riaa_biquad, lib_preemphasis
STEREO 4 sox_oops, sox_earwax, npy_lr_pan, npy_stereo_widener
NOISE 2 am_add_gaussian_noise, am_add_color_noise
OTHER 4 am_polarity_inversion, am_reverse, sox_dcshift, ta_dcshift

The full effect reference is in docs/effects.md.


Audio format

  • All effects take and return np.ndarray with shape (channels, samples) and dtype float32.
  • Sample rate is a second positional argument.
  • Applying an empty chain returns the input clipped to [-1, 1].

Development

git clone https://github.com/barry-mir/multiafx
cd multiafx
conda create -n multiafx python=3.10 -y
conda activate multiafx
pip install -e ".[dev]"
pytest tests/

The test suite runs every one of the 85 effects at min / mid / max of each parameter range. No test is expected to be skipped for any reason other than "effect has no parameters". If you add an effect, add nothing else — the parameterized tests pick it up automatically.


License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

multiafx-0.1.1.tar.gz (25.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

multiafx-0.1.1-py3-none-any.whl (23.8 kB view details)

Uploaded Python 3

File details

Details for the file multiafx-0.1.1.tar.gz.

File metadata

  • Download URL: multiafx-0.1.1.tar.gz
  • Upload date:
  • Size: 25.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.20

File hashes

Hashes for multiafx-0.1.1.tar.gz
Algorithm Hash digest
SHA256 6440a2de96f060abd69ae7030280a87af3021721d99fc08700086a617685a214
MD5 d3f4dd32bbb636bca1aaddea29631c73
BLAKE2b-256 932598633fb72a3b018757cbe0df57160ad5ded467092c561e3047564a2cb9db

See more details on using hashes here.

File details

Details for the file multiafx-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: multiafx-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 23.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.20

File hashes

Hashes for multiafx-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d9f3650616ced69d0c92fb0939cf06832835ad86d04b28b9b57467ec6ccb92db
MD5 3c739797a3d9d83d41807f4923283089
BLAKE2b-256 1d5df21b3405ffac7ef7f859a8445d1fd4fb184730bab2fe92b07f2eabc3fb6e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page