Added kwargs to effects and effects hooks
MODIFY
nussl/core/audio_signal.py
nussl/core/audio_signal.py
@@ -1922,7 +1922,7 @@ class AudioSignal(object):
self._effects_chain = []
return self
- def time_stretch(self, factor):
+ def time_stretch(self, factor, **kwargs):
"""
Adds a time stretch filter to the AudioSignal's effects chain.
A factor greater than one will shorten the signal, a factor less then one
@@ -1937,6 +1937,7 @@ class AudioSignal(object):
Args:
factor (float): Scaling factor for tempo change. Must be positive.
+ kwargs: Arugments passed to `sox.transform.tempo`
Returns:
self: Initial AudioSignal with updated effect chains
@@ -1945,10 +1946,10 @@ class AudioSignal(object):
* :func:`make_effect`: Syntactic sugar for adding an effect to the chain by name.
* :func:`reset_effects_chain`: Empties the effects chain without applying any effects.
"""
- self._effects_chain.append(effects.time_stretch(factor))
+ self._effects_chain.append(effects.time_stretch(factor, **kwargs))
return self
- def pitch_shift(self, n_semitones):
+ def pitch_shift(self, n_semitones, **kwargs):
"""
Add pitch shift effect to AudioSignal's effect chain.
A positive shift will change the pitch of the signal by `n_semitones`
@@ -1965,6 +1966,7 @@ class AudioSignal(object):
Args:
n_semitones (float): The number of semitones to shift the audio.
Positive values increases the frequency of the signal
+ kwargs: Arugments passed to `sox.transform.pitch`
Returns:
self: Initial AudioSignal with updated effect chains
@@ -1973,10 +1975,10 @@ class AudioSignal(object):
* :func:`make_effect`: Syntactic sugar for adding an effect to the chain by name.
* :func:`reset_effects_chain`: Empties the effects chain without applying any effects.
"""
- self._effects_chain.append(effects.pitch_shift(n_semitones))
+ self._effects_chain.append(effects.pitch_shift(n_semitones, **kwargs))
return self
- def low_pass(self, freq, poles=2, width_type="h", width=0.707):
+ def low_pass(self, freq, poles=2, width_type="h", width=0.707, **kwargs):
"""
Add low pass effect to AudioSignal's effect chain
@@ -1997,6 +1999,7 @@ class AudioSignal(object):
's': slope
'k': kHz
width (float): Band width in width_type units
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
self: Initial AudioSignal with updated effect chains
@@ -2007,10 +2010,10 @@ class AudioSignal(object):
"""
self._effects_chain.append(effects.low_pass(freq, poles=poles,
width_type=width_type,
- width=width))
+ width=width, **kwargs))
return self
- def high_pass(self, freq, poles=2, width_type="h", width=0.707):
+ def high_pass(self, freq, poles=2, width_type="h", width=0.707, **kwargs):
"""
Add high pass effect to AudioSignal's effect chain
@@ -2031,6 +2034,7 @@ class AudioSignal(object):
's': slope
'k': kHz
width (float): Band width in width_type units
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
self: Initial AudioSignal with updated effect chains
@@ -2041,10 +2045,10 @@ class AudioSignal(object):
"""
self._effects_chain.append(effects.high_pass(freq, poles=poles,
width_type=width_type,
- width=width))
+ width=width, **kwargs))
return self
- def tremolo(self, mod_freq, mod_depth):
+ def tremolo(self, mod_freq, mod_depth, **kwargs):
"""
Add tremolo effect to AudioSignal's effect chain
@@ -2058,6 +2062,7 @@ class AudioSignal(object):
Args:
mod_freq (float): Modulation frequency. Must be between .1 and 20000.
mod_depth (float): Modulation depth. Must be between 0 and 1.
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
self: Initial AudioSignal with updated effect chains
@@ -2066,10 +2071,10 @@ class AudioSignal(object):
* :func:`make_effect`: Syntactic sugar for adding an effect to the chain by name.
* :func:`reset_effects_chain`: Empties the effects chain without applying any effects.
"""
- self._effects_chain.append(effects.tremolo(mod_freq, mod_depth))
+ self._effects_chain.append(effects.tremolo(mod_freq, mod_depth, **kwargs))
return self
- def vibrato(self, mod_freq, mod_depth):
+ def vibrato(self, mod_freq, mod_depth, **kwargs):
"""
Add vibrato effect to AudioSignal's effect chain.
@@ -2083,6 +2088,7 @@ class AudioSignal(object):
Args:
mod_freq (float): Modulation frequency. Must be between .1 and 20000.
mod_depth (float): Modulation depth. Must be between 0 and 1.
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
self: Initial AudioSignal with updated effect chains
@@ -2091,10 +2097,11 @@ class AudioSignal(object):
* :func:`make_effect`: Syntactic sugar for adding an effect to the chain by name.
* :func:`reset_effects_chain`: Empties the effects chain without applying any effects.
"""
- self._effects_chain.append(effects.vibrato(mod_freq, mod_depth))
+ self._effects_chain.append(effects.vibrato(mod_freq, mod_depth, **kwargs))
return self
- def chorus(self, delays, decays, speeds, depths, in_gain=0.4, out_gain=0.4):
+ def chorus(self, delays, decays, speeds,
+ depths, in_gain=0.4, out_gain=0.4, **kwargs):
"""
Add chorus effect to AudioSignal's effect chain.
@@ -2112,6 +2119,7 @@ class AudioSignal(object):
depths (list of float): depths. Must be between 0 and 1
in_gain (float): Proportion of input gain. Must be between 0 and 1
out_gain (float): Proportion of output gain. Must be between 0 and 1
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
self: Initial AudioSignal with updated effect chains
@@ -2123,11 +2131,12 @@ class AudioSignal(object):
self._effects_chain.append(effects.chorus(delays, decays,
speeds, depths,
in_gain=in_gain,
- out_gain=out_gain))
+ out_gain=out_gain,
+ **kwargs))
return self
def phaser(self, in_gain=0.4, out_gain=0.74, delay=3, decay=0.4,
- speed=0.5, type_="triangular"):
+ speed=0.5, type_="triangular", **kwargs):
"""
Add phaser effect to AudioSignal's effect chain
@@ -2147,6 +2156,7 @@ class AudioSignal(object):
type_ (str): modulation type. Either Triangular or Sinusoidal
"triangular" or "t" for Triangular
"sinusoidal" of "s" for sinusoidal
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
self: Initial AudioSignal with updated effect chains
@@ -2156,12 +2166,12 @@ class AudioSignal(object):
* :func:`reset_effects_chain`: Empties the effects chain without applying any effects.
"""
fx = effects.phaser(in_gain=in_gain, out_gain=out_gain, delay=delay,
- decay=decay, speed=speed, type_=type_)
+ decay=decay, speed=speed, type_=type_, **kwargs)
self._effects_chain.append(fx)
return self
- def flanger(self, delay=0, depth=2, regen=0, width=71,
- speed=0.5, phase=25, shape="sinusoidal", interp="linear"):
+ def flanger(self, delay=0, depth=2, regen=0, width=71, speed=0.5,
+ phase=25, shape="sinusoidal", interp="linear", **kwargs):
"""
Add flanger effect to AudioSignal's effect chain
This is a FFmpeg effect. Please see
@@ -2182,6 +2192,7 @@ class AudioSignal(object):
shape (str): Swept wave shape, Must be "triangular" or "sinusoidal".
phase (float): swept wave percentage-shift for multi channel. Must be between 0 and 100.
interp (str): Delay Line interpolation. Must be "linear" or "quadratic".
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
self: Initial AudioSignal with updated effect chains
@@ -2191,11 +2202,12 @@ class AudioSignal(object):
* :func:`reset_effects_chain`: Empties the effects chain without applying any effects.
"""
fx = effects.flanger(delay=delay, depth=depth, regen=regen, width=width,
- speed=speed, phase=phase, shape=shape, interp=interp)
+ speed=speed, phase=phase, shape=shape, interp=interp,
+ **kwargs)
self._effects_chain.append(fx)
return self
- def emphasis(self, level_in, level_out, type_="col", mode='production'):
+ def emphasis(self, level_in, level_out, type_="col", mode='production', **kwargs):
"""
Add emphasis effect to AudioSignal's effect chain. An emphasis filter boosts
frequency ranges the most susceptible to noise in a medium. When restoring
@@ -2226,6 +2238,7 @@ class AudioSignal(object):
mode (str): Filter mode. Must be one of the following:
- "reproduction": Apply de-emphasis filter
- "production": Apply emphasis filter
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
self: Initial AudioSignal with updated effect chains
@@ -2235,12 +2248,13 @@ class AudioSignal(object):
* :func:`reset_effects_chain`: Empties the effects chain without applying any effects.
"""
self._effects_chain.append(effects.emphasis(level_in, level_out,
- type_=type_, mode=mode))
+ type_=type_, mode=mode,
+ **kwargs))
return self
def compressor(self, level_in, mode="downward", reduction_ratio=2,
attack=20, release=250, makeup=1, knee=2.8284, link="average",
- detection="rms", mix=1, threshold=0.125):
+ detection="rms", mix=1, threshold=0.125, **kwargs):
"""
Add compressor effect to AudioSignal's effect chain
@@ -2269,6 +2283,7 @@ class AudioSignal(object):
detection (str): Whether to process exact signal of the RMS of nearby signals.
Either "peak" for exact or "rms".
mix (float): Proportion of compressed signal in output.
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
self: Initial AudioSignal with updated effect chains
@@ -2279,11 +2294,11 @@ class AudioSignal(object):
"""
fx = effects.compressor(level_in, mode=mode, reduction_ratio=reduction_ratio,
attack=attack, release=release, makeup=makeup, knee=knee, link=link,
- detection=detection, mix=mix, threshold=threshold)
+ detection=detection, mix=mix, threshold=threshold, **kwargs)
self._effects_chain.append(fx)
return self
- def equalizer(self, bands):
+ def equalizer(self, bands, **kwargs):
"""
Add eqaulizer effect to AudioSignal's effect chain
@@ -2312,7 +2327,7 @@ class AudioSignal(object):
* :func:`make_effect`: Syntactic sugar for adding an effect to the chain by name.
* :func:`reset_effects_chain`: Empties the effects chain without applying any effects.
"""
- self._effects_chain.append(effects.equalizer(bands))
+ self._effects_chain.append(effects.equalizer(bands, **kwargs))
return self
##################################################

MODIFY
nussl/core/effects.py
nussl/core/effects.py
@@ -182,6 +182,7 @@ def time_stretch(factor, **kwargs):
for details.
Args:
factor (float): Scaling factor for tempo change. Must be positive.
+ kwargs: Arugments passed to `sox.transform.tempo`
Returns:
filter (SoXFilter): A SoXFilter object, to be called on an pysndfx stream
"""
@@ -206,6 +207,7 @@ def pitch_shift(n_semitones, **kwargs):
Args:
n_semitones (float): The number of semitones to shift the audio.
Positive values increases the frequency of the signal
+ kwargs: Arugments passed to `sox.transform.pitch`
Returns:
filter (SoxFilter): A SoXFilter object, to be called on an pysndfx stream
"""
@@ -223,7 +225,7 @@ def _pass_arg_check(freq, poles, width_type, width):
raise ValueError("width should be positive scalar")
-def low_pass(freq, poles=2, width_type="h", width=.707):
+def low_pass(freq, poles=2, width_type="h", width=.707, **kwargs):
"""
Creates an FFmpegFilter object, which when called on an ffmpeg stream,
applies a low pass filter to the audio signal.
@@ -243,19 +245,21 @@ def low_pass(freq, poles=2, width_type="h", width=.707):
's': slope
'k': kHz
width (float): Band width in width_type units
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
filter (FFmpegFilter): Resulting filter, to be called on a ffmpeg stream
"""
_pass_arg_check(freq, poles, width_type, width)
filter_ = FFmpegFilter("low_pass", ffmpeg_name="lowpass", f=freq, p=poles,
- t=width_type, w=width)
+ t=width_type, w=width, **kwargs)
filter_.params = {"freq": freq, "poles": poles,
- "width_type": width_type, "width": width}
+ "width_type": width_type, "width": width, **kwargs}
+
return filter_
-def high_pass(freq, poles=2, width_type="h", width=.707):
+def high_pass(freq, poles=2, width_type="h", width=.707, **kwargs):
"""
Creates a FFmpegFilter object, when called on an ffmpeg stream,
applies a high pass filter to the audio signal.
@@ -275,19 +279,20 @@ def high_pass(freq, poles=2, width_type="h", width=.707):
's': slope
'k': kHz
width (float): Band width in width_type units
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
filter (FFmpegFilter): Resulting filter, to be called on a ffmpeg stream
"""
_pass_arg_check(freq, poles, width_type, width)
filter_ = FFmpegFilter("high_pass", ffmpeg_name="highpass", f=freq, p=poles,
- t=width_type, w=width)
+ t=width_type, w=width, **kwargs)
filter_.params = {"freq": freq, "poles": poles,
- "width_type": width_type, "width": width}
+ "width_type": width_type, "width": width, **kwargs}
return filter_
-def tremolo(mod_freq, mod_depth):
+def tremolo(mod_freq, mod_depth, **kwargs):
"""
Creates a FFmpegFilter object, when called on an ffmpeg stream,
applies a tremolo filter to the audio signal
@@ -300,6 +305,7 @@ def tremolo(mod_freq, mod_depth):
Args:
mod_freq (float): Modulation frequency. Must be between .1 and 20000.
mod_depth (float): Modulation depth. Must be between 0 and 1.
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
filter (FFmpegFilter): Resulting filter, to be called on a ffmpeg stream
"""
@@ -309,12 +315,12 @@ def tremolo(mod_freq, mod_depth):
if not np.issubdtype(type(mod_depth), np.number) or mod_depth < 0 or mod_depth > 1:
raise ValueError("mod_depth should be positve scalar between 0 and 1.")
- filter_ = FFmpegFilter("tremolo", f=mod_freq, d=mod_depth)
- filter_.params = {"mod_freq": mod_freq, "mod_depth": mod_depth}
+ filter_ = FFmpegFilter("tremolo", f=mod_freq, d=mod_depth, **kwargs)
+ filter_.params = {"mod_freq": mod_freq, "mod_depth": mod_depth, **kwargs}
return filter_
-def vibrato(mod_freq, mod_depth):
+def vibrato(mod_freq, mod_depth, **kwargs):
"""
Creates a FFmpegFilter object, when called on an ffmpeg stream,
applies a vibrato filter to the audio signal.
@@ -327,6 +333,7 @@ def vibrato(mod_freq, mod_depth):
Args:
mod_freq (float): Modulation frequency. Must be between .1 and 20000.
mod_depth (float): Modulation depth. Must be between 0 and 1.
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
filter (FFmpegFilter): Resulting filter, to be called on a ffmpeg stream
"""
@@ -336,13 +343,13 @@ def vibrato(mod_freq, mod_depth):
if not np.issubdtype(type(mod_depth), np.number) or mod_depth < 0 or mod_depth > 1:
raise ValueError("mod_depth should be positve scalar between 0 and 1.")
- filter_ = FFmpegFilter("vibrato", f=mod_freq, d=mod_depth)
- filter_.params = {"mod_freq": mod_freq, "mod_depth": mod_depth}
+ filter_ = FFmpegFilter("vibrato", f=mod_freq, d=mod_depth, **kwargs)
+ filter_.params = {"mod_freq": mod_freq, "mod_depth": mod_depth, **kwargs}
return filter_
def chorus(delays, decays, speeds, depths,
- in_gain=.4, out_gain=.4):
+ in_gain=.4, out_gain=.4, **kwargs):
"""
Creates a FFmpegFilter object, when called on an ffmpeg stream,
applies a vibrato filter to the audio signal.
@@ -360,6 +367,7 @@ def chorus(delays, decays, speeds, depths,
depths (list of float): depths. Must be between 0 and 1
in_gain (float): Proportion of input gain. Must be between 0 and 1
out_gain (float): Proportion of output gain. Must be between 0 and 1
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
filter: Resulting filter, to be called on a ffmpeg stream
"""
@@ -383,17 +391,18 @@ def chorus(delays, decays, speeds, depths,
filter_ = FFmpegFilter("chorus", in_gain=in_gain,
out_gain=out_gain, delays=ffmpeg_delays,
speeds=ffmpeg_speeds, decays=ffmpeg_decays,
- depths=ffmpeg_depths)
+ depths=ffmpeg_depths, **kwargs)
filter_.params["delays"] = delays
filter_.params["speeds"] = speeds
filter_.params["decays"] = decays
filter_.params["depths"] = depths
+ filter_.params.update(kwargs)
return filter_
def phaser(in_gain=.4, out_gain=.74, delay=3,
- decay=.4, speed=.5, type_="triangular"):
+ decay=.4, speed=.5, type_="triangular", **kwargs):
"""
Creates a FFmpegFilter object, when called on an ffmpeg stream,
applies a phaser filter to the audio signal
@@ -412,6 +421,7 @@ def phaser(in_gain=.4, out_gain=.74, delay=3,
type_ (str): modulation type. Either Triangular or Sinusoidal
"triangular" or "t" for Triangular
"sinusoidal" of "s" for sinusoidal
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
filter (FFmpegFilter): Resulting filter, to be called on a ffmpeg stream
"""
@@ -427,16 +437,15 @@ def phaser(in_gain=.4, out_gain=.74, delay=3,
raise ValueError(f"_type must be one of the following:\n{allowed_mod_types}")
# type is reserved word in python, kwarg dict is necessary
- type_kwarg = {
- "type": type_
- }
+ type_kwarg = {"type": type_}
filter_ = FFmpegFilter("phaser", ffmpeg_name="aphaser", in_gain=in_gain, out_gain=out_gain,
- delay=delay, speed=speed, decay=decay, **type_kwarg)
+ delay=delay, speed=speed, decay=decay, **{**type_kwarg, **kwargs})
filter_.params = copy.deepcopy(filter_.params)
del filter_.params["type"]
filter_.params["type_"] = type_
+ filter_.params.update(kwargs)
return filter_
@@ -473,8 +482,8 @@ def _flanger_argcheck(delay, depth, regen, width,
raise ValueError(error_text)
-def flanger(delay=0, depth=2, regen=0, width=71,
- speed=.5, phase=25, shape="sinusoidal", interp="linear"):
+def flanger(delay=0, depth=2, regen=0, width=71, speed=.5,
+ phase=25, shape="sinusoidal", interp="linear", **kwargs):
"""
Creates a FFmpegFilter object, when called on an ffmpeg stream,
applies a flanger filter to the audio signal.
@@ -495,6 +504,7 @@ def flanger(delay=0, depth=2, regen=0, width=71,
shape (str): Swept wave shape, Must be "triangular" or "sinusoidal".
phase (float): swept wave percentage-shift for multi channel. Must be between 0 and 100.
interp (str): Delay Line interpolation. Must be "linear" or "quadratic".
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
filter (FFmpegFilter): Resulting filter, to be called on a ffmpeg stream
"""
@@ -502,10 +512,10 @@ def flanger(delay=0, depth=2, regen=0, width=71,
_flanger_argcheck(delay, depth, regen, width, speed, phase, shape, interp)
return FFmpegFilter("flanger", delay=delay, depth=depth, regen=regen, width=width,
- speed=speed, phase=phase, shape=shape, interp=interp)
+ speed=speed, phase=phase, shape=shape, interp=interp, **kwargs)
-def emphasis(level_in, level_out, type_="col", mode='production'):
+def emphasis(level_in, level_out, type_="col", mode='production', **kwargs):
"""
Creates a FFmpegFilter object, when called on an ffmpeg stream,
applies a emphasis filter to the audio signal. An emphasis filter boosts frequency ranges
@@ -533,6 +543,7 @@ def emphasis(level_in, level_out, type_="col", mode='production'):
mode (str): Filter mode. Must be one of the following:
- "reproduction": Apply de-emphasis filter
- "production": Apply emphasis filter
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
filter (FFmpeg Filter): Resulting filter, to be called on a ffmpeg stream
"""
@@ -548,16 +559,15 @@ def emphasis(level_in, level_out, type_="col", mode='production'):
raise ValueError(f"mode must be production or reproduction")
# type is a reserved word in python, so kwarg dict is necessary
- type_kwarg = {
- 'type': type_
- }
+ type_kwarg = {'type': type_}
filter_ = FFmpegFilter("emphasis", ffmpeg_name="aemphasis", level_in=level_in,
- level_out=level_out, mode=mode, **type_kwarg)
+ level_out=level_out, mode=mode, **{**type_kwarg, **kwargs})
filter_.params = copy.deepcopy(filter_.params)
del filter_.params["type"]
filter_.params["type_"] = type_
+ filter_.params.update(kwargs)
return filter_
@@ -607,7 +617,7 @@ def _compressor_argcheck(level_in, mode, reduction_ratio,
def compressor(level_in, mode="downward", reduction_ratio=2,
attack=20, release=250, makeup=1, knee=2.8284, link="average",
- detection="rms", mix=1, threshold=.125):
+ detection="rms", mix=1, threshold=.125, **kwargs):
"""
Creates a FFmpegFilter object, when called on an ffmpeg stream,
applies a compressor filter to the audio signal.
@@ -634,6 +644,7 @@ def compressor(level_in, mode="downward", reduction_ratio=2,
detection (str): Whether to process exact signal of the RMS of nearby signals.
Either "peak" for exact or "rms".
mix (float): Proportion of compressed signal in output.
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
filter (FFmpegFilter): Resulting filter, to be called on a ffmpeg stream
"""
@@ -642,14 +653,16 @@ def compressor(level_in, mode="downward", reduction_ratio=2,
filter_ = FFmpegFilter("compressor", ffmpeg_name="acompressor", level_in=level_in,
ratio=reduction_ratio, attack=attack, release=release, makeup=makeup,
- knee=knee, link=link, detection=detection, mix=mix, threshold=threshold)
+ knee=knee, link=link, detection=detection, mix=mix, threshold=threshold,
+ **kwargs)
filter_.params = copy.deepcopy(filter_.params)
del filter_.params["ratio"]
filter_.params["reduction_ratio"] = reduction_ratio
+ filter_.params.update(**kwargs)
return filter_
-def equalizer(bands):
+def equalizer(bands, **kwargs):
"""
Creates a FFmpegFilter object, when called on an ffmpeg stream,
applies a equalizer filter to the audio signal.
@@ -670,6 +683,7 @@ def equalizer(bands):
0, for Butterworth
1, for Chebyshev type 1
2, for Chebyshev type 2
+ kwargs: Arguments passed to `ffmpeg.filter`
Returns:
filter: Resulting filter, to be called on a ffmpeg stream
"""
@@ -694,6 +708,6 @@ def equalizer(bands):
for band in bands
])
- filter_ = FFmpegFilter("equalizer", ffmpeg_name="anequalizer", params=params)
- filter_.params = {"bands": bands}
+ filter_ = FFmpegFilter("equalizer", ffmpeg_name="anequalizer", params=params, **kwargs)
+ filter_.params = {"bands": bands, **kwargs}
return filter_

MODIFY
tests/core/test_effects.py
tests/core/test_effects.py
@@ -514,3 +514,10 @@ def test_order(mix_and_sources):
sox_ffmpeg_order = ["time_stretch", "pitch_shift", "tremolo", "vibrato"]
for effect, _filter in zip(signal_sf.effects_applied, sox_ffmpeg_order):
assert effect.filter == _filter
+
+def test_kwargs():
+ kwargs = {"a":1, "b":2, "c":3}
+ filter_func = effects.time_stretch(1.3, **kwargs)
+ for key, value in kwargs.items():
+ assert filter_func.params[key] == value
+ assert filter_func.params["factor"] == 1.3
\ No newline at end of file

Added kwargs to effects and effects hooks (#186)

Co-authored-by: Andreas Bugler