Skip to content

Insert

Insert

Bases: _FLObject

Source code in pyflp/insert/insert.py
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
class Insert(_FLObject):
    class EQ(_FLObject):
        """Insert post EQ.

        [Manual](https://www.image-line.com/fl-studio-learning/fl-studio-online-manual/html/mixer_trackprops.htm)
        """

        _LEVEL_VALIDATOR = _IntValidator(-1800, 1800)
        _FREQ_Q_VALIDATOR = _UIntValidator(65536)

        low_level: int = _IntProperty(_LEVEL_VALIDATOR)
        """Gain of first band. Min: -1800, Max: 1800, Default: 0."""

        band_level: int = _IntProperty(_LEVEL_VALIDATOR)
        """Gain of second band. Min: -1800, Max: 1800, Default: 0."""

        high_level: int = _IntProperty(_LEVEL_VALIDATOR)
        """Gain of third band. Min: -1800, Max: 1800, Default: 0."""

        low_freq: int = _UIntProperty(_FREQ_Q_VALIDATOR)
        """Frequency of first band. Min: 0, Max: 65536, Default: 5777 (90 Hz)."""

        band_freq: int = _UIntProperty(_FREQ_Q_VALIDATOR)
        """Frequency of second band. Min: 0, Max: 65536, Default: 33145 (1500 Hz)."""

        high_freq: int = _UIntProperty(_FREQ_Q_VALIDATOR)
        """Frequency of third band. Min: 0, Max: 65536, Default: 55825 (8000 Hz)."""

        low_q: int = _UIntProperty(_FREQ_Q_VALIDATOR)
        """Resonance of first band. Min: 0, Max: 65536, Default: 17500."""

        band_q: int = _UIntProperty(_FREQ_Q_VALIDATOR)
        """Resonance of second band. Min: 0, Max: 65536, Default: 17500."""

        high_q: int = _UIntProperty(_FREQ_Q_VALIDATOR)
        """Resonance of thid band. Min: 0, Max: 65536, Default: 17500."""

    @enum.unique
    class EventID(enum.IntEnum):
        """Events used by `Insert`."""

        Parameters = DATA + 28
        """Stored in `Insert.locked` and `Insert.flags`."""

        Routing = DATA + 27
        """See `Insert.routing`."""

        Input = DWORD + 26
        """See `Insert.input`."""

        Output = DWORD + 19
        """See `Insert.output`."""

        Color = DWORD + 21
        """See `Insert.color`. Default event is not stored."""

        Icon = WORD + 31
        """See `Insert.icon`. Default event is not stored."""

        Name = TEXT + 12
        """See `Insert.name`. Default event doesn't exist."""

    # * Properties
    name: Optional[str] = _StrProperty()

    @property
    def routing(self) -> List[bool]:
        """An order collection of booleans, representing how `Insert` is routed. e.g.
        if it is [False, True, True, ...] then this insert is routed to insert 2, 3."""
        return getattr(self, "_routing", [])

    icon: Optional[int] = _IntProperty()

    input: Optional[int] = _IntProperty()
    """Hardware input, dk exactly."""

    output: Optional[int] = _IntProperty()
    """Hardware output, dk exactly."""

    color: Optional[colour.Color] = _ColorProperty()

    @property
    def flags(self) -> Optional[InsertFlags]:
        return self._parameters.flags

    @flags.setter
    def flags(self, value: InsertFlags) -> None:
        self._parameters.flags = value

    @property
    def slots(self) -> List[InsertSlot]:
        """Holds `InsertSlot` objects (empty and used, both)."""
        return getattr(self, "_slots", [])

    @property
    def enabled(self) -> Optional[bool]:
        """Whether `Insert` is in enabled state in the mixer."""
        flags = self._parameters.flags
        if flags:
            return InsertFlags.Enabled in flags

    @enabled.setter
    def enabled(self, value: bool) -> None:
        # https://stackoverflow.com/a/66667330
        if value:
            self._parameters.flags |= InsertFlags.Enabled
        else:
            self._parameters.flags &= ~InsertFlags.Enabled

    volume: Optional[int] = _UIntProperty(max_=16000)
    """Post volume fader. Min: 0 (0% / -INFdB / 0.00), Max: 16000 \
    (125% / 5.6dB / 1.90), Default: 12800 (100% / 0.0dB / 1.00)."""

    pan: Optional[int] = _IntProperty(min_=-6400, max_=6400)
    """Min: -6400 (100% left), Max: 6400 (100% right), Default: 0."""

    stereo_separation: Optional[int] = _IntProperty(min_=-64, max_=64)
    """Min: -64 (100% merged), Max: 64 (100% separated), Default: 0."""

    @property
    def eq(self) -> Optional[EQ]:
        """3-band post EQ. See `InsertEQ`."""
        return getattr(self, "_eq", None)

    @property
    def route_volumes(self) -> List[int]:
        """Ordered list of route volumes."""
        return getattr(self, "_route_volumes", [])

    @route_volumes.setter
    def route_volumes(self, value: List[int]):
        num_inserts = self._max_instances.inserts
        if len(value) != num_inserts:
            raise ValueError(f"Expected a list of size {num_inserts}")
        self._route_volumes = value

    @property
    def locked(self) -> Optional[bool]:
        """Whether `Insert` is in locked state in the mixer."""
        flags = self._parameters.flags
        if flags:
            return InsertFlags.Locked in flags

    @locked.setter
    def locked(self, value: bool) -> None:
        if value:
            self._parameters.flags |= InsertFlags.Locked
        else:
            self._parameters.flags &= ~InsertFlags.Locked

    # * Parsing logic
    def parse_event(self, e: EventType) -> None:
        if e.id_ == InsertSlot.EventID.Index:
            self._cur_slot.parse_event(e)
            self._slots.append(self._cur_slot)
            if len(self._slots) < self._max_instances.slots:
                self._cur_slot = InsertSlot()
        elif e.id_ in (
            InsertSlot.EventID.Color,
            InsertSlot.EventID.Icon,
            InsertSlot.EventID.PluginNew,
            InsertSlot.EventID.Plugin,
            InsertSlot.EventID.DefaultName,
            InsertSlot.EventID.Name,
        ):
            self._cur_slot.parse_event(e)
        else:
            return super().parse_event(e)

    def _parse_word_event(self, e: WordEventType) -> None:
        if e.id_ == Insert.EventID.Icon:
            self._parse_H(e, "icon")

    def _parse_dword_event(self, e: DWordEventType) -> None:
        if e.id_ == Insert.EventID.Input:
            self._parse_i(e, "input")
        elif e.id_ == Insert.EventID.Color:
            self._parse_color(e, "color")
        elif e.id_ == Insert.EventID.Output:
            self._parse_i(e, "output")

    def _parse_text_event(self, e: TextEventType) -> None:
        if e.id_ == Insert.EventID.Name:
            self._parse_s(e, "name")

    def _parse_data_event(self, e: DataEventType) -> None:
        if e.id_ == Insert.EventID.Parameters:
            self._events["parameters"] = e
            self._parameters = InsertParameters()
            self._parameters.parse_event(e)
        elif e.id_ == Insert.EventID.Routing:
            routing = []
            for byte in e.data:
                route = True if byte > 0 else False
                routing.append(route)
            self._parseprop(e, "routing", routing)

    def _save(self) -> List[EventType]:
        events = super()._save()
        for slot in self.slots:
            events.extend(slot._save())
        return events

    def __init__(self, project: "Project", max_instances: MaxInstances):
        super().__init__(project, max_instances)
        self._slots: List[InsertSlot] = []
        self._cur_slot = InsertSlot()
        self._eq = Insert.EQ()
        self._route_volumes: List[int] = []
        self.index = len(project.inserts) - 1

color: Optional[colour.Color] = _ColorProperty() class-attribute

icon: Optional[int] = _IntProperty() class-attribute

input: Optional[int] = _IntProperty() class-attribute

Hardware input, dk exactly.

name: Optional[str] = _StrProperty() class-attribute

output: Optional[int] = _IntProperty() class-attribute

Hardware output, dk exactly.

pan: Optional[int] = _IntProperty(min_=-6400, max_=6400) class-attribute

Min: -6400 (100% left), Max: 6400 (100% right), Default: 0.

stereo_separation: Optional[int] = _IntProperty(min_=-64, max_=64) class-attribute

Min: -64 (100% merged), Max: 64 (100% separated), Default: 0.

volume: Optional[int] = _UIntProperty(max_=16000) class-attribute

Post volume fader. Min: 0 (0% / -INFdB / 0.00), Max: 16000 (125% / 5.6dB / 1.90), Default: 12800 (100% / 0.0dB / 1.00).

EventID

Bases: enum.IntEnum

Events used by Insert.

Source code in pyflp/insert/insert.py
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
@enum.unique
class EventID(enum.IntEnum):
    """Events used by `Insert`."""

    Parameters = DATA + 28
    """Stored in `Insert.locked` and `Insert.flags`."""

    Routing = DATA + 27
    """See `Insert.routing`."""

    Input = DWORD + 26
    """See `Insert.input`."""

    Output = DWORD + 19
    """See `Insert.output`."""

    Color = DWORD + 21
    """See `Insert.color`. Default event is not stored."""

    Icon = WORD + 31
    """See `Insert.icon`. Default event is not stored."""

    Name = TEXT + 12
    """See `Insert.name`. Default event doesn't exist."""

Color = DWORD + 21 class-attribute

See Insert.color. Default event is not stored.

Icon = WORD + 31 class-attribute

See Insert.icon. Default event is not stored.

Input = DWORD + 26 class-attribute

See Insert.input.

Name = TEXT + 12 class-attribute

See Insert.name. Default event doesn't exist.

Output = DWORD + 19 class-attribute

See Insert.output.

Parameters = DATA + 28 class-attribute

Stored in Insert.locked and Insert.flags.

Routing = DATA + 27 class-attribute

See Insert.routing.

enabled() writable property

Whether Insert is in enabled state in the mixer.

Source code in pyflp/insert/insert.py
131
132
133
134
135
136
@property
def enabled(self) -> Optional[bool]:
    """Whether `Insert` is in enabled state in the mixer."""
    flags = self._parameters.flags
    if flags:
        return InsertFlags.Enabled in flags

eq() property

3-band post EQ. See InsertEQ.

Source code in pyflp/insert/insert.py
156
157
158
159
@property
def eq(self) -> Optional[EQ]:
    """3-band post EQ. See `InsertEQ`."""
    return getattr(self, "_eq", None)

flags() writable property

Source code in pyflp/insert/insert.py
118
119
120
@property
def flags(self) -> Optional[InsertFlags]:
    return self._parameters.flags

locked() writable property

Whether Insert is in locked state in the mixer.

Source code in pyflp/insert/insert.py
173
174
175
176
177
178
@property
def locked(self) -> Optional[bool]:
    """Whether `Insert` is in locked state in the mixer."""
    flags = self._parameters.flags
    if flags:
        return InsertFlags.Locked in flags

route_volumes() writable property

Ordered list of route volumes.

Source code in pyflp/insert/insert.py
161
162
163
164
@property
def route_volumes(self) -> List[int]:
    """Ordered list of route volumes."""
    return getattr(self, "_route_volumes", [])

routing() property

An order collection of booleans, representing how Insert is routed. e.g. if it is [False, True, True, ...] then this insert is routed to insert 2, 3.

Source code in pyflp/insert/insert.py
102
103
104
105
106
@property
def routing(self) -> List[bool]:
    """An order collection of booleans, representing how `Insert` is routed. e.g.
    if it is [False, True, True, ...] then this insert is routed to insert 2, 3."""
    return getattr(self, "_routing", [])

slots() property

Holds InsertSlot objects (empty and used, both).

Source code in pyflp/insert/insert.py
126
127
128
129
@property
def slots(self) -> List[InsertSlot]:
    """Holds `InsertSlot` objects (empty and used, both)."""
    return getattr(self, "_slots", [])

InsertSlot

Bases: _FLObject

Source code in pyflp/insert/slot.py
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
class InsertSlot(_FLObject):
    @enum.unique
    class EventID(enum.IntEnum):
        """Event IDs used by `InsertSlot`."""

        DefaultName = TEXT + 9
        """See `InsertSlot.default_name`."""

        Name = TEXT + 11
        """See `InsertSlot.name`. Not stored if slot is empty."""

        # Plugin wrapper data, windows pos of plugin etc, currently
        # selected plugin wrapper page; minimized, closed or not
        PluginNew = DATA + 4  # TODO
        """See `InsertSlot.new`. Not stored if slot is empty."""

        Icon = DWORD + 27
        """See `InsertSlot.icon`. Not stored if slot is empty."""

        Color = DWORD
        """See `InsertSlot.color`. Not stored if slot is empty."""

        Plugin = DATA + 5
        """See `InsertSlot.plugin`. Not stored if slot is empty."""

        Index = WORD + 34
        """See `InsertSlot.index`. New in FL 12.3."""

    # * Properties
    default_name: Optional[str] = _StrProperty()
    """'Fruity Wrapper' for VST/AU plugins. Factory name for native plugins."""

    icon: Optional[int] = _IntProperty()

    color: Optional[colour.Color] = _ColorProperty()

    index: Optional[int] = _UIntProperty()
    """Index (FL12.3+); occurs irrespective of whether slot is used or not."""

    enabled: Optional[bool] = _BoolProperty()
    """Enabled state of the insert slot. Obtained from `InsertParamsEvent`."""

    mix: Optional[int] = _UIntProperty(max_=12800)
    """Dry/Wet mix of the insert slot. Obtained from `InsertParamsEvent`.
    Min: 0 (0%), Max: 12800 (100%), Default: 12800 (100%)."""

    @property
    def plugin(self) -> Optional[_Plugin]:
        """Plugin parameters and preset data, this
        is what uses the most space typically."""
        return getattr(self, "_plugin", None)

    @plugin.setter
    def plugin(self, value: _Plugin) -> None:
        self._plugin = value

    @property
    def new(self) -> Optional[bytes]:
        return getattr(self, "_new", None)

    @new.setter
    def new(self, value: bytes) -> None:
        self._new = value

    name: Optional[str] = _StrProperty()
    """User-given/preset name for stock plugins. Factory name
    for VST/AU plugins, if a user-given name is not given."""

    # * Parsing logic
    def _parse_word_event(self, e: WordEventType) -> None:
        if e.id_ == InsertSlot.EventID.Index:
            self._parse_H(e, "index")

    def _parse_dword_event(self, e: DWordEventType) -> None:
        if e.id_ == InsertSlot.EventID.Color:
            self._parse_color(e, "color")
        elif e.id_ == InsertSlot.EventID.Icon:
            self._parse_I(e, "icon")

    def _parse_text_event(self, e: TextEventType) -> None:
        if e.id_ == InsertSlot.EventID.DefaultName:
            self._parse_s(e, "default_name")
        elif e.id_ == InsertSlot.EventID.Name:
            self._parse_s(e, "name")

    def _parse_data_event(self, e: DataEventType) -> None:
        if e.id_ == InsertSlot.EventID.PluginNew:
            self._events["new"] = e
            self._new = e.data
            # TODO: Parsing similar to ChannelEventID.New (same IDs)
        elif e.id_ == InsertSlot.EventID.Plugin:
            n = self.default_name
            if n == "Fruity Soft Clipper":
                plugin = FSoftClipper()
            elif n == "Fruity NoteBook 2":
                plugin = FNoteBook2()
            elif n == "Fruity Balance":
                plugin = FBalance()
            elif n == "Soundgoodizer":
                plugin = Soundgoodizer()
            elif n == "Fruity Stereo Enhancer":
                plugin = FStereoEnhancer()
            elif n == "Fruity Fast Dist":
                plugin = FFastDist()
            elif n == "Fruity Send":
                plugin = FSend()
            elif n == "Fruity Wrapper":
                plugin = VSTPlugin()
            else:
                plugin = _Plugin()
            self._parse_flobject(e, "plugin", plugin)

    def _save(self) -> List[EventType]:
        new_event = self._events.get("new")
        if new_event:
            new_event.dump(self._new)

        if self.plugin:
            self.plugin._save()

        return super()._save()

    # * Utility methods
    def is_used(self) -> bool:
        """Whether a slot is used or empty."""
        return True if self._events.get("new") else False

    def get_name(self) -> Optional[str]:
        """Returns the name that will be shown in FL."""
        return self.name or self.default_name

default_name: Optional[str] = _StrProperty() class-attribute

'Fruity Wrapper' for VST/AU plugins. Factory name for native plugins.

enabled: Optional[bool] = _BoolProperty() class-attribute

Enabled state of the insert slot. Obtained from InsertParamsEvent.

icon: Optional[int] = _IntProperty() class-attribute

index: Optional[int] = _UIntProperty() class-attribute

Index (FL12.3+); occurs irrespective of whether slot is used or not.

mix: Optional[int] = _UIntProperty(max_=12800) class-attribute

Dry/Wet mix of the insert slot. Obtained from InsertParamsEvent. Min: 0 (0%), Max: 12800 (100%), Default: 12800 (100%).

name: Optional[str] = _StrProperty() class-attribute

User-given/preset name for stock plugins. Factory name for VST/AU plugins, if a user-given name is not given.

EventID

Bases: enum.IntEnum

Event IDs used by InsertSlot.

Source code in pyflp/insert/slot.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
@enum.unique
class EventID(enum.IntEnum):
    """Event IDs used by `InsertSlot`."""

    DefaultName = TEXT + 9
    """See `InsertSlot.default_name`."""

    Name = TEXT + 11
    """See `InsertSlot.name`. Not stored if slot is empty."""

    # Plugin wrapper data, windows pos of plugin etc, currently
    # selected plugin wrapper page; minimized, closed or not
    PluginNew = DATA + 4  # TODO
    """See `InsertSlot.new`. Not stored if slot is empty."""

    Icon = DWORD + 27
    """See `InsertSlot.icon`. Not stored if slot is empty."""

    Color = DWORD
    """See `InsertSlot.color`. Not stored if slot is empty."""

    Plugin = DATA + 5
    """See `InsertSlot.plugin`. Not stored if slot is empty."""

    Index = WORD + 34
    """See `InsertSlot.index`. New in FL 12.3."""

Color = DWORD class-attribute

See InsertSlot.color. Not stored if slot is empty.

DefaultName = TEXT + 9 class-attribute

See InsertSlot.default_name.

Icon = DWORD + 27 class-attribute

See InsertSlot.icon. Not stored if slot is empty.

Index = WORD + 34 class-attribute

See InsertSlot.index. New in FL 12.3.

Name = TEXT + 11 class-attribute

See InsertSlot.name. Not stored if slot is empty.

Plugin = DATA + 5 class-attribute

See InsertSlot.plugin. Not stored if slot is empty.

PluginNew = DATA + 4 class-attribute

See InsertSlot.new. Not stored if slot is empty.

new() writable property

Source code in pyflp/insert/slot.py
102
103
104
@property
def new(self) -> Optional[bytes]:
    return getattr(self, "_new", None)

plugin() writable property

Plugin parameters and preset data, this is what uses the most space typically.

Source code in pyflp/insert/slot.py
92
93
94
95
96
@property
def plugin(self) -> Optional[_Plugin]:
    """Plugin parameters and preset data, this
    is what uses the most space typically."""
    return getattr(self, "_plugin", None)

is_used()

Whether a slot is used or empty.

Source code in pyflp/insert/slot.py
169
170
171
def is_used(self) -> bool:
    """Whether a slot is used or empty."""
    return True if self._events.get("new") else False