"""Define FX and FXParam classes."""
import reapy
from reapy import reascript_api as RPR
from reapy.core import ReapyObject, ReapyObjectList
from reapy.errors import DistError, UndefinedFXParamError
[docs]class FX(ReapyObject):
"""FX on a Track or a Take."""
_class_name = "FX"
functions = {
prefix: {
name.replace(prefix, ""): function
for name, function in RPR.__dict__.items()
if name.startswith(prefix)
}
for prefix in ("TrackFX_", "TakeFX_")
}
def __init__(self, parent=None, index=None, parent_id=None):
if parent_id is None:
message = (
"One of `parent` or `parent_id` must be specified."
)
assert parent is not None, message
parent_id = parent.id
self.parent_id = parent_id
self.index = index
self.functions = self._get_functions()
def _get_functions(self):
if isinstance(self.parent, reapy.Track):
type = "TrackFX_"
else:
type = "TakeFX_"
return self.functions[type]
@property
def _kwargs(self):
return {"parent_id": self.parent_id, "index": self.index}
[docs] def close_chain(self):
"""Close FX chain."""
self.functions["Show"](self.parent.id, self.index, 0)
[docs] def close_floating_window(self):
"""Close FX floating window."""
self.functions["Show"](self.parent.id, self.index, 2)
[docs] def close_ui(self):
"""Close user interface."""
self.is_ui_open = False
[docs] def copy_to_take(self, take, index=0):
"""
Copy FX to take.
Parameters
----------
take : Take
Destination take.
index : int
Index on destination take.
See also
--------
FX.move_to_take
"""
self.functions["CopyToTake"](
self.parent_id, self.index, take.id, index, False
)
[docs] def copy_to_track(self, track, index=0):
"""
Copy FX to track.
Parameters
----------
track : Track
Destination track.
index : int
Index on destination track.
See also
--------
FX.move_to_track
"""
self.functions["CopyToTrack"](
self.parent_id, self.index, track.id, index, False
)
[docs] def delete(self):
"""Delete FX."""
self.functions["Delete"](self.parent_id, self.index)
[docs] def disable(self):
"""Disable FX."""
self.is_enabled = False
[docs] def enable(self):
"""Enable FX."""
self.is_enabled = True
@property
def is_enabled(self):
"""
Whether FX is enabled.
:type: bool
"""
is_enabled = bool(
self.functions["GetEnabled"](self.parent_id, self.index)
)
return is_enabled
@is_enabled.setter
def is_enabled(self, enabled):
self.functions["SetEnabled"](self.parent_id, self.index, enabled)
@property
def is_online(self):
"""
Whether FX is online.
:type: bool
"""
is_online = not bool(
self.functions["GetOffline"](self.parent_id, self.index)
)
return is_online
@is_online.setter
def is_online(self, online):
offline = not online
self.functions["SetOffline"](self.parent_id, self.index, offline)
@property
def is_ui_open(self):
"""
Whether FX user interface is open.
:type: bool
"""
is_ui_open = bool(
self.functions["GetOpen"](self.parent_id, self.index)
)
return is_ui_open
@is_ui_open.setter
def is_ui_open(self, open):
self.functions["SetOpen"](self.parent_id, self.index, open)
[docs] def make_offline(self):
"""Make FX offline."""
self.is_online = False
[docs] def make_online(self):
"""Make FX online."""
self.is_online = True
[docs] def move_to_take(self, take, index=0):
"""
Move FX to take.
Parameters
----------
take : Take
Destination take.
index : int
Index on destination take.
See also
--------
FX.copy_to_take
"""
self.functions["CopyToTake"](
self.parent_id, self.index, take.id, index, True
)
[docs] def move_to_track(self, track, index=0):
"""
Move FX to track.
Parameters
----------
track : Track
Destination track.
index : int
Index on destination track.
See also
--------
FX.copy_to_track
"""
self.functions["CopyToTrack"](
self.parent_id, self.index, track.id, index, True
)
@property
def n_inputs(self):
"""
Number of inputs of FX.
:type: int
"""
return self.functions["GetIOSize"](
self.parent.id, self.index, 0, 0
)[3]
@property
def n_outputs(self):
"""
Number of outputs of FX.
:type: int
"""
return self.functions["GetIOSize"](
self.parent.id, self.index, 0, 0
)[4]
@property
def n_params(self):
"""
Number of parameters.
:type: int
"""
n_params = self.functions["GetNumParams"](self.parent_id, self.index)
return n_params
@property
def n_presets(self):
"""
Number of presets.
:type: int
"""
n_presets = self.functions["GetPresetIndex"](
self.parent_id, self.index, 0
)[-1]
return n_presets
@property
def name(self):
"""
FX name.
:type: str
"""
name = self.functions["GetFXName"](
self.parent_id, self.index, "", 2048
)[3]
return name
[docs] def open_chain(self):
"""Open FX chain with focus on FX."""
self.functions["Show"](self.parent.id, self.index, 1)
[docs] def open_floating_window(self):
"""Open FX floating window."""
self.functions["Show"](self.parent.id, self.index, 3)
[docs] def open_ui(self):
"""Open FX user interface."""
self.is_ui_open = True
@property
def params(self):
"""
List of parameters.
:type: FXParamsList
"""
params = reapy.FXParamsList(self)
return params
@property
def parent(self):
"""
FX parent.
:type: Track or Take
"""
if self.parent_id.startswith("(MediaTrack*)"):
return reapy.Track(self.parent_id)
return reapy.Take(self.parent_id)
@property
def preset(self):
"""
FX preset name.
:type: str
Attribute can be set by passing a str or int. In the first
case, the str can either be a preset name or the path to a
.vstpreset file. Otherwise, the int is the preset index.
"""
preset = self.functions["GetPreset"](
self.parent_id, self.index, "", 2048
)[3]
return preset
@preset.setter
def preset(self, preset):
"""
Set FX preset.
Parameters
----------
preset : str or int
If str, preset name or path to .vstpreset file. If int,
preset index. Set to -2 for factory preset, and -1 for user
default preset.
"""
if isinstance(preset, str):
self.functions["SetPreset"](
self.parent_id, self.index, preset
)
elif isinstance(preset, int):
self.functions["SetPresetByIndex"](
self.parent_id, self.index, preset
)
@property
def preset_index(self):
"""
FX preset index.
:type: int
"""
index = self.functions["GetPresetIndex"](
self.parent_id, self.index, 0
)[0]
return index
@property
def preset_file(self):
"""
Path to FX preset file.
:type: str
"""
file = self.functions["GetUserPresetFilename"](
self.parent_id, self.index, "", 2048
)[2]
return file
[docs] def use_previous_preset(self):
"""Use previous preset in the presets list."""
self.functions["NavigatePresets"](self.parent_id, self.index, -1)
[docs] def use_next_preset(self):
"""Use next preset in the presets list."""
self.functions["NavigatePresets"](self.parent_id, self.index, 1)
@property
def window(self):
"""
Floating window associated to FX, if it exists.
:type: Window or NoneType
"""
window = reapy.Window(
self.functions["GetFloatingWindow"](self.parent.id, self.index)
)
if not window._is_defined:
window = None
return window
[docs]class FXList(ReapyObjectList):
"""
Container class for a list of FXs.
FXs can be accessed by name or index.
Examples
--------
>>> fx_list = track.fxs
>>> fx_list[0]
FX(parent_id="(MediaTrack*)0x0000000006CDEBE0", index=0)
>>> len(fx_list)
1
>>> fx_list["VST: ReaComp (Cockos)"]
FX(parent_id="(MediaTrack*)0x0000000006CDEBE0", index=0)
"""
_class_name = "FXList"
def __init__(self, parent):
self.parent = parent
@reapy.inside_reaper()
def __delitem__(self, key):
fxs = self[key] if isinstance(key, slice) else [self[key]]
for fx in fxs:
fx.delete()
def __getitem__(self, i):
if isinstance(i, slice):
return self._get_items_from_slice(i)
with reapy.inside_reaper():
if isinstance(i, str):
i = self._get_fx_index(name=i)
n_fxs = self.parent.n_fxs
if i >= n_fxs:
raise IndexError("{} has only {} fxs".format(self.parent, n_fxs))
i = i % n_fxs # Allows for negative values
fx = FX(self.parent, i)
return fx
def __len__(self):
return self.parent.n_fxs
@reapy.inside_reaper()
def _get_items_from_slice(self, slice):
indices = range(*slice.indices(len(self)))
return [self[i] for i in indices]
def _get_fx_index(self, name):
name = name[name.find(': ') + 2:] # Remove FX type prefix
if isinstance(self.parent, reapy.Track):
prefix = "TrackFX_"
args = (self.parent.id, name, False, 0)
else:
prefix = "TakeFX_"
args = (self.parent.id, name, 0)
index = getattr(RPR, prefix + "AddByName")(*args)
if index == -1:
raise KeyError("No FX named {}".format(name))
return index
@property
def _args(self):
return self.parent,