diff --git a/voicemeeterlib/bus.py b/voicemeeterlib/bus.py index f79ea00..ecf4339 100644 --- a/voicemeeterlib/bus.py +++ b/voicemeeterlib/bus.py @@ -1,11 +1,19 @@ import time from abc import abstractmethod +from enum import IntEnum from math import log from typing import Union from .error import VMError from .iremote import IRemote -from .meta import bus_mode_prop +from .kinds import kinds_all +from .meta import bool_prop, bus_mode_prop, float_prop + +BusModes = IntEnum( + "BusModes", + "normal amix bmix repeat composite tvmix upmix21 upmix41 upmix61 centeronly lfeonly rearonly", + start=0, +) class Bus(IRemote): @@ -79,6 +87,14 @@ class Bus(IRemote): def gain(self, val: float): self.setter("gain", val) + @property + def monitor(self) -> bool: + return self.getter("monitor") == 1 + + @monitor.setter + def monitor(self, val: bool): + self.setter("monitor", 1 if val else 0) + def fadeto(self, target: float, time_: int): self.setter("FadeTo", f"({target}, {time_})") time.sleep(self._remote.DELAY) @@ -89,6 +105,19 @@ class Bus(IRemote): class PhysicalBus(Bus): + @classmethod + def make(cls, kind): + """ + Factory method for PhysicalBus. + + Returns a PhysicalBus class. + """ + kls = (cls,) + if kind.name == "potato": + EFFECTS_cls = _make_effects_mixin() + kls += (EFFECTS_cls,) + return type(f"PhysicalBus", kls, {}) + def __str__(self): return f"{type(self).__name__}{self.index}" @@ -102,6 +131,23 @@ class PhysicalBus(Bus): class VirtualBus(Bus): + @classmethod + def make(cls, kind): + """ + Factory method for VirtualBus. + + If basic kind subclass physical bus. + + Returns a VirtualBus class. + """ + kls = (cls,) + if kind.name == "basic": + kls += (PhysicalBus,) + elif kind.name == "potato": + EFFECTS_cls = _make_effects_mixin() + kls += (EFFECTS_cls,) + return type(f"VirtualBus", kls, {}) + def __str__(self): return f"{type(self).__name__}{self.index}" @@ -157,39 +203,61 @@ def _make_bus_mode_mixin(): def identifier(self) -> str: return f"Bus[{self.index}].mode" + def get(self) -> str: + time.sleep(0.01) + for i, val in enumerate( + [ + self.amix, + self.bmix, + self.repeat, + self.composite, + self.tvmix, + self.upmix21, + self.upmix41, + self.upmix61, + self.centeronly, + self.lfeonly, + self.rearonly, + ] + ): + if val: + return BusModes(i + 1).name + return "normal" + return type( "BusModeMixin", (IRemote,), { "identifier": property(identifier), + **{mode.name: bus_mode_prop(mode.name) for mode in BusModes}, + "get": get, + }, + ) + + +def _make_effects_mixin(): + """creates an fx mixin""" + return type( + f"FX", + (), + { **{ - mode: bus_mode_prop(mode) - for mode in [ - "normal", - "amix", - "bmix", - "repeat", - "composite", - "tvmix", - "upmix21", - "upmix41", - "upmix61", - "centeronly", - "lfeonly", - "rearonly", - ] + f"return{param}": float_prop(f"return{param}") + for param in ["reverb", "delay", "fx1", "fx2"] }, }, ) -def bus_factory(phys_bus, remote, i) -> Union[PhysicalBus, VirtualBus]: +def bus_factory(is_phys_bus, remote, i) -> Union[PhysicalBus, VirtualBus]: """ Factory method for buses Returns a physical or virtual bus subclass """ - BUS_cls = PhysicalBus if phys_bus else VirtualBus + BUS_cls = ( + PhysicalBus.make(remote.kind) if is_phys_bus else VirtualBus.make(remote.kind) + ) BUSMODEMIXIN_cls = _make_bus_mode_mixin() return type( f"{BUS_cls.__name__}{remote.kind}", diff --git a/voicemeeterlib/strip.py b/voicemeeterlib/strip.py index 1844c64..547e640 100644 --- a/voicemeeterlib/strip.py +++ b/voicemeeterlib/strip.py @@ -6,7 +6,7 @@ from typing import Union from .error import VMError from .iremote import IRemote from .kinds import kinds_all -from .meta import bool_prop +from .meta import bool_prop, float_prop class Strip(IRemote): @@ -82,6 +82,16 @@ class Strip(IRemote): class PhysicalStrip(Strip): + @classmethod + def make(cls, kind): + """ + Factory method for PhysicalStrip. + + Returns a PhysicalStrip class. + """ + EFFECTS_cls = _make_effects_mixins[kind.name] + return type(f"PhysicalStrip", (cls, EFFECTS_cls), {}) + def __str__(self): return f"{type(self).__name__}{self.index}" @@ -162,6 +172,8 @@ class VirtualStrip(Strip): def treble(self): return round(self.getter("EQGain3"), 1) + high = treble + @treble.setter def treble(self, val: float): self.setter("EQGain3", val) @@ -283,6 +295,52 @@ _make_channelout_mixins = { } +def _make_effects_mixin(kind): + """creates an effects mixin for a kind""" + XY_cls = type( + f"XY", + (), + { + param: float_prop(param) + for param in [ + "pan_x", + "pan_y", + "color_x", + "color_y", + "fx_x", + "fx_y", + ] + }, + ) + + FX_cls = type( + f"FX", + (), + { + **{ + param: float_prop(param) + for param in [ + "reverb", + "delay", + "fx1", + "fx2", + ] + }, + **{ + f"post{param}": bool_prop(f"post{param}") + for param in ["reverb", "delay", "fx1", "fx2"] + }, + }, + ) + + if kind.name == "potato": + return type(f"Effects{kind}", (XY_cls, FX_cls), {}) + return type(f"Effects{kind}", (XY_cls,), {}) + + +_make_effects_mixins = {kind.name: _make_effects_mixin(kind) for kind in kinds_all} + + def strip_factory(is_phys_strip, remote, i) -> Union[PhysicalStrip, VirtualStrip]: """ Factory method for strips @@ -291,7 +349,7 @@ def strip_factory(is_phys_strip, remote, i) -> Union[PhysicalStrip, VirtualStrip Returns a physical or virtual strip subclass """ - STRIP_cls = PhysicalStrip if is_phys_strip else VirtualStrip + STRIP_cls = PhysicalStrip.make(remote.kind) if is_phys_strip else VirtualStrip CHANNELOUTMIXIN_cls = _make_channelout_mixins[remote.kind.name] _kls = (STRIP_cls, CHANNELOUTMIXIN_cls)