mirror of
https://github.com/onyx-and-iris/voicemeeter-compact.git
synced 2024-11-15 17:40:52 +00:00
fd30a5ef0d
merge subject classes. modify obsevers callback ensure sunvalley theme loaded only once. minor version bump
275 lines
8.6 KiB
Python
275 lines
8.6 KiB
Python
import tkinter as tk
|
|
from math import log
|
|
from tkinter import ttk
|
|
|
|
from . import builders
|
|
from .data import _base_values, _configuration
|
|
|
|
|
|
class GainLayer(ttk.LabelFrame):
|
|
"""Concrete class representing a single gainlayer"""
|
|
|
|
def __init__(self, parent, index, j):
|
|
super().__init__(parent)
|
|
self.parent = parent
|
|
self.index = index
|
|
self.j = j
|
|
self.styletable = self.parent.parent.styletable
|
|
if index <= parent.phys_in:
|
|
self.level_offset = index * 2
|
|
else:
|
|
self.level_offset = parent.phys_in * 2 + (index - parent.phys_in) * 8
|
|
|
|
self.builder = builders.ChannelLabelFrameBuilder(self, index, id="gainlayer")
|
|
self.builder.setup()
|
|
self.builder.add_progressbar()
|
|
self.builder.add_scale()
|
|
self.builder.add_gain_label()
|
|
self.builder.add_on_button()
|
|
self.sync()
|
|
self.grid_configure()
|
|
|
|
@property
|
|
def target(self):
|
|
"""returns the strip[i].gainlayer class in the current interface"""
|
|
|
|
_target = self.parent.target
|
|
return _target.strip[self.index].gainlayer[self.j]
|
|
|
|
@property
|
|
def identifier(self):
|
|
return "gainlayer"
|
|
|
|
def getter(self, param):
|
|
if hasattr(self.target, param):
|
|
return getattr(self.target, param)
|
|
|
|
def setter(self, param, value):
|
|
if hasattr(self.target, param):
|
|
setattr(self.target, param, value)
|
|
|
|
def reset_gain(self, *args):
|
|
self.setter("gain", 0)
|
|
self.gain.set(0)
|
|
self.gainlabel.set(0)
|
|
|
|
def scale_callback(self, *args):
|
|
"""callback function for scale widget"""
|
|
|
|
self.setter("gain", self.gain.get())
|
|
self.gainlabel.set(round(self.gain.get(), 1))
|
|
|
|
def scale_press(self, *args):
|
|
_base_values.in_scale_button_1 = True
|
|
|
|
def scale_release(self, *args):
|
|
_base_values.in_scale_button_1 = False
|
|
|
|
def _on_mousewheel(self, event):
|
|
self.gain.set(
|
|
self.gain.get()
|
|
+ (
|
|
_configuration.mwscroll_step
|
|
if event.delta > 0
|
|
else -_configuration.mwscroll_step
|
|
)
|
|
)
|
|
if self.gain.get() > 12:
|
|
self.gain.set(12)
|
|
elif self.gain.get() < -60:
|
|
self.gain.set(-60)
|
|
self.setter("gain", self.gain.get())
|
|
|
|
def set_on(self):
|
|
"""enables a gainlayer. sets its button colour"""
|
|
|
|
setattr(
|
|
self.parent.target.strip[self.index],
|
|
self.parent.buses[self.j],
|
|
self.on.get(),
|
|
)
|
|
if not _configuration.themes_enabled:
|
|
self.styletable.configure(
|
|
f"{self.identifier}On{self.index}.TButton",
|
|
background=f'{"green" if self.on.get() else "white"}',
|
|
)
|
|
|
|
def sync(self):
|
|
self.after(_base_values.pdelay, self.sync_params)
|
|
self.after(100, self.sync_labels)
|
|
|
|
def sync_params(self):
|
|
self.gain.set(self.getter("gain"))
|
|
self.gainlabel.set(round(self.gain.get(), 1))
|
|
self.on.set(
|
|
getattr(
|
|
self.parent.target.strip[self.index],
|
|
self.parent.buses[self.j],
|
|
)
|
|
)
|
|
if not _configuration.themes_enabled:
|
|
self.styletable.configure(
|
|
f"{self.identifier}On{self.index}.TButton",
|
|
background=f'{"green" if self.on.get() else "white"}',
|
|
)
|
|
|
|
def sync_labels(self):
|
|
"""sync params with voicemeeter"""
|
|
retval = self.parent.target.strip[self.index].label
|
|
if len(retval) > 10:
|
|
retval = f"{retval[:8]}.."
|
|
if not retval:
|
|
self.parent.columnconfigure(self.index, minsize=0)
|
|
self.parent.parent.subject.remove(self)
|
|
self.grid_remove()
|
|
else:
|
|
self.parent.parent.subject.add(self)
|
|
self.grid()
|
|
self.configure(text=retval)
|
|
|
|
def convert_level(self, val):
|
|
if _base_values.vban_connected:
|
|
return round(-val * 0.01, 1)
|
|
return round(20 * log(val, 10), 1) if val > 0 else -200.0
|
|
|
|
def upd_levels(self):
|
|
"""
|
|
Updates level values.
|
|
|
|
Checks offset against expected level array size to avoid a race condition
|
|
"""
|
|
if self.level_offset + 1 < len(self.parent.target.strip_levels):
|
|
if (
|
|
any(
|
|
self.parent.target._strip_comp[
|
|
self.level_offset : self.level_offset + 1
|
|
]
|
|
)
|
|
or self.level.get() > 0
|
|
):
|
|
val = self.convert_level(
|
|
max(
|
|
self.parent.target.strip_levels[
|
|
self.level_offset : self.level_offset + 1
|
|
]
|
|
)
|
|
)
|
|
self.level.set(
|
|
(
|
|
0
|
|
if self.parent.target.strip[self.index].mute
|
|
or not self.on.get()
|
|
else 100 + val - 18 + self.gain.get()
|
|
)
|
|
)
|
|
|
|
def on_update(self, subject):
|
|
"""update levels"""
|
|
if subject == "ldirty":
|
|
self.after(_base_values.ldelay, self.upd_levels)
|
|
|
|
def grid_configure(self):
|
|
[
|
|
child.grid_configure(padx=1, pady=1, sticky=(tk.N, tk.S, tk.W, tk.E))
|
|
for child in self.winfo_children()
|
|
if isinstance(child, ttk.Checkbutton)
|
|
]
|
|
[
|
|
child.grid_configure(padx=1, pady=1, sticky=(tk.N, tk.S))
|
|
for child in self.winfo_children()
|
|
if isinstance(child, ttk.Progressbar) or isinstance(child, ttk.Scale)
|
|
]
|
|
# pb and scale
|
|
self.columnconfigure(0, minsize=36)
|
|
self.columnconfigure(1, minsize=36)
|
|
# on button
|
|
if _configuration.themes_enabled:
|
|
self.rowconfigure(2, minsize=70)
|
|
else:
|
|
self.rowconfigure(2, minsize=55)
|
|
|
|
|
|
class SubMixFrame(ttk.Frame):
|
|
def __init__(self, parent):
|
|
super().__init__(parent)
|
|
self.parent = parent
|
|
self.phys_in, self.virt_in = parent.kind.ins
|
|
self.phys_out, self.virt_out = parent.kind.outs
|
|
self.buses = tuple(f"A{i+1}" for i in range(self.phys_out)) + tuple(
|
|
f"B{i+1}" for i in range(self.virt_out)
|
|
)
|
|
|
|
self.gainlayers = [
|
|
GainLayer(self, index, _configuration.submixes) for index in range(8)
|
|
]
|
|
for i, labelframe in enumerate(self.labelframes):
|
|
labelframe.grid(row=0, column=i)
|
|
if not self.target.strip[i].label:
|
|
self.columnconfigure(i, minsize=0)
|
|
labelframe.grid_remove()
|
|
|
|
if _configuration.extends_horizontal:
|
|
self.grid(row=0, column=2)
|
|
if parent.bus_frame:
|
|
parent.bus_frame.grid_remove()
|
|
else:
|
|
if parent.bus_frame:
|
|
self.grid(
|
|
row=parent.bus_frame.grid_info()["row"], column=0, sticky=(tk.W)
|
|
)
|
|
parent.bus_frame.grid_remove()
|
|
else:
|
|
self.grid(row=2, column=0, sticky=(tk.W))
|
|
|
|
# registers submixframe as pdirty observer
|
|
self.parent.subject.add(self)
|
|
|
|
self.grid_configure()
|
|
"""
|
|
Grids each labelframe, grid_removes any without a label
|
|
"""
|
|
for i, labelframe in enumerate(self.labelframes):
|
|
labelframe.grid(row=0, column=i)
|
|
if not self.target.strip[i].label:
|
|
self.columnconfigure(i, minsize=0)
|
|
labelframe.grid_remove()
|
|
|
|
@property
|
|
def target(self):
|
|
"""returns the current interface"""
|
|
|
|
return self.parent.target
|
|
|
|
@property
|
|
def labelframes(self):
|
|
"""returns a tuple of current gainlayer labelframe addresses"""
|
|
|
|
return tuple(
|
|
frame
|
|
for frame in self.winfo_children()
|
|
if isinstance(frame, ttk.LabelFrame)
|
|
)
|
|
|
|
def grid_configure(self):
|
|
[
|
|
self.columnconfigure(i, minsize=_configuration.level_width)
|
|
for i, _ in enumerate(self.labelframes)
|
|
]
|
|
[
|
|
self.rowconfigure(0, minsize=_configuration.level_height)
|
|
for i, _ in enumerate(self.labelframes)
|
|
]
|
|
|
|
def upd_labelframe(self, labelframe):
|
|
labelframe.sync()
|
|
|
|
def on_update(self, subject):
|
|
if subject == "pdirty":
|
|
for labelframe in self.labelframes:
|
|
self.after(1, self.upd_labelframe, labelframe)
|
|
|
|
def teardown(self):
|
|
# deregisters submixframe as pdirty observer
|
|
self.parent.subject.remove(self)
|
|
self.destroy()
|