Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

About

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.

Resources

Stars

16 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages