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.0.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.0-py3-none-any.whl (23.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: multiafx-0.1.0.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.0.tar.gz
Algorithm Hash digest
SHA256 b1801375d5b0e3395f521d8ac284e00b32513ade445dc98b12bf43a3b2542aa5
MD5 10510aee3dfc0c11557407636bd47616
BLAKE2b-256 f894e724f40b65a0027101323d296ceeb272ab3adc60b0d3e543abd40b3a0bac

See more details on using hashes here.

File details

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

File metadata

  • Download URL: multiafx-0.1.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 18711a1ae02735064017e762aaf747bfd24c1d78ae32fe4592adcffac7874008
MD5 3593f9ff86f7d75d62b4cb69fb53a27a
BLAKE2b-256 29458fd8b286a3b132f2ad5eabf60dadecb99598ca9fda5994ef358fd04a58e8

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