Fix AttributeError: remove redundant mapping_learned connection in main.py
TopBarWidget already connects mapping_learned to _on_mapping_learned internally in set_midi_engine, so the external connection in main.py was both redundant and referenced a non-existent method.
This commit is contained in:
+72
-15
@@ -143,6 +143,14 @@ class DeckState:
|
||||
return f"-{int(remaining//60):02d}:{int(remaining%60):02d}"
|
||||
|
||||
|
||||
class ExternalInputMonitor:
|
||||
def __init__(self, buffer_size=600):
|
||||
self.buffer = np.zeros(buffer_size, dtype=np.float32)
|
||||
self.write_pos = 0
|
||||
self.buffer_size = buffer_size
|
||||
self.monitoring = False
|
||||
|
||||
|
||||
class MixerChannel:
|
||||
def __init__(self, name: str, index: int):
|
||||
self.name = name
|
||||
@@ -150,14 +158,10 @@ class MixerChannel:
|
||||
self.volume = 1.0
|
||||
self.muted = False
|
||||
self.solo = False
|
||||
self.pfl = False
|
||||
self.vu_peak = 0.0
|
||||
self.in_port_L = None
|
||||
self.in_port_R = None
|
||||
self.out_port_L = None
|
||||
self.out_port_R = None
|
||||
|
||||
def reset_vu(self):
|
||||
self.vu_peak = 0.0
|
||||
|
||||
|
||||
class Compressor:
|
||||
@@ -169,7 +173,6 @@ class Compressor:
|
||||
self.release_ms = 100.0
|
||||
self.makeup_gain_db = 0.0
|
||||
self.bypassed = False
|
||||
|
||||
self._envelope = 0.0
|
||||
self._attack_coeff = self._time_constant(self.attack_ms)
|
||||
self._release_coeff = self._time_constant(self.release_ms)
|
||||
@@ -226,16 +229,13 @@ class MasterBus:
|
||||
self.vu_peak = 0.0
|
||||
self.lufs_current = -70.0
|
||||
|
||||
def reset_vu(self):
|
||||
self.vu_peak = 0.0
|
||||
|
||||
|
||||
class AudioEngine:
|
||||
def __init__(self):
|
||||
self.client = jack.Client("RadioPanel")
|
||||
self.sr = self.client.samplerate
|
||||
|
||||
# Output ports for decks (standalone, not mixed into master)
|
||||
# Output ports for digital decks (standalone, not mixed into master)
|
||||
self.ports = {
|
||||
'deck1': (
|
||||
self.client.outports.register("deck1_L"),
|
||||
@@ -251,6 +251,17 @@ class AudioEngine:
|
||||
),
|
||||
}
|
||||
|
||||
# External input monitors (deck3, deck4 — pass-through with waveform)
|
||||
self.ext_monitors = {}
|
||||
self.ext_ports = {}
|
||||
for ext_key, label in [('deck3', 'deck3'), ('deck4', 'deck4')]:
|
||||
in_L = self.client.inports.register(f"{label}_in_L")
|
||||
in_R = self.client.inports.register(f"{label}_in_R")
|
||||
out_L = self.client.outports.register(f"{label}_out_L")
|
||||
out_R = self.client.outports.register(f"{label}_out_R")
|
||||
self.ext_ports[ext_key] = (in_L, in_R, out_L, out_R)
|
||||
self.ext_monitors[ext_key] = ExternalInputMonitor()
|
||||
|
||||
# Mixer channels: 8 stereo input ports
|
||||
self.mixer = {
|
||||
'channels': [],
|
||||
@@ -268,7 +279,13 @@ class AudioEngine:
|
||||
self.client.outports.register("master_out_R"),
|
||||
)
|
||||
|
||||
# Deck states (unchanged)
|
||||
# PFL output ports
|
||||
self.pfl_out = (
|
||||
self.client.outports.register("pfl_out_L"),
|
||||
self.client.outports.register("pfl_out_R"),
|
||||
)
|
||||
|
||||
# Deck states (for digital decks and carts)
|
||||
self.decks = {
|
||||
'deck1': DeckState(),
|
||||
'deck2': DeckState(),
|
||||
@@ -300,6 +317,10 @@ class AudioEngine:
|
||||
if 0 <= index < NUM_MIXER_CHANNELS:
|
||||
self.mixer['channels'][index].solo = solo
|
||||
|
||||
def set_channel_pfl(self, index: int, pfl: bool):
|
||||
if 0 <= index < NUM_MIXER_CHANNELS:
|
||||
self.mixer['channels'][index].pfl = pfl
|
||||
|
||||
def _fill_port(self, port_L, port_R, deck: DeckState, frames: int):
|
||||
buf_L = port_L.get_array()
|
||||
buf_R = port_R.get_array()
|
||||
@@ -327,7 +348,7 @@ class AudioEngine:
|
||||
deck.position = track.num_samples - 1
|
||||
|
||||
def _process(self, frames: int):
|
||||
# ── Decks (unchanged) ──────────────────────────────────────────
|
||||
# ── Digital decks ───────────────────────────────────────────────
|
||||
self._fill_port(*self.ports['deck1'], self.decks['deck1'], frames)
|
||||
self._fill_port(*self.ports['deck2'], self.decks['deck2'], frames)
|
||||
|
||||
@@ -355,6 +376,34 @@ class AudioEngine:
|
||||
deck.playing = False
|
||||
deck.position = track.num_samples - 1
|
||||
|
||||
# ── External input monitors (deck3, deck4) ─────────────────────
|
||||
gate_linear = 10 ** (-46.0 / 20.0)
|
||||
for ext_key in ('deck3', 'deck4'):
|
||||
in_L, in_R, out_L, out_R = self.ext_ports[ext_key]
|
||||
in_buf_L = in_L.get_array()
|
||||
in_buf_R = in_R.get_array()
|
||||
out_buf_L = out_L.get_array()
|
||||
out_buf_R = out_R.get_array()
|
||||
|
||||
out_buf_L[:] = in_buf_L[:]
|
||||
out_buf_R[:] = in_buf_R[:]
|
||||
|
||||
monitor = self.ext_monitors[ext_key]
|
||||
if not monitor.monitoring:
|
||||
monitor.buffer[:] = 0.0
|
||||
continue
|
||||
|
||||
rms = np.sqrt(np.mean((in_buf_L ** 2 + in_buf_R ** 2) / 2.0))
|
||||
if rms > gate_linear:
|
||||
db = 20 * np.log10(max(rms, 1e-10))
|
||||
display = max(0.0, (db + 46.0) / 46.0)
|
||||
display = min(1.0, display)
|
||||
else:
|
||||
display = 0.0
|
||||
|
||||
monitor.buffer[monitor.write_pos] = display
|
||||
monitor.write_pos = (monitor.write_pos + 1) % monitor.buffer_size
|
||||
|
||||
# ── Mixer ───────────────────────────────────────────────────────
|
||||
master_buf_L = self.master_out[0].get_array()
|
||||
master_buf_R = self.master_out[1].get_array()
|
||||
@@ -363,12 +412,23 @@ class AudioEngine:
|
||||
|
||||
any_solo = any(ch.solo for ch in self.mixer['channels'])
|
||||
|
||||
# PFL bus
|
||||
pfl_buf_L = self.pfl_out[0].get_array()
|
||||
pfl_buf_R = self.pfl_out[1].get_array()
|
||||
pfl_buf_L.fill(0.0)
|
||||
pfl_buf_R.fill(0.0)
|
||||
|
||||
for ch in self.mixer['channels']:
|
||||
in_L = ch.in_port_L.get_array()
|
||||
in_R = ch.in_port_R.get_array()
|
||||
|
||||
mute = ch.muted or (any_solo and not ch.solo)
|
||||
|
||||
# PFL: pre-fader, pre-mute signal to PFL bus
|
||||
if ch.pfl:
|
||||
pfl_buf_L[:] += in_L[:]
|
||||
pfl_buf_R[:] += in_R[:]
|
||||
|
||||
if mute:
|
||||
ch.vu_peak = 0.0
|
||||
continue
|
||||
@@ -390,15 +450,12 @@ class AudioEngine:
|
||||
self.mixer['master'].compressor.process(master_audio)
|
||||
self.mixer['master'].limiter.process(master_audio)
|
||||
|
||||
# Copy processed audio back to port buffers
|
||||
master_buf_L[:] = master_audio[0]
|
||||
master_buf_R[:] = master_audio[1]
|
||||
|
||||
# Master VU
|
||||
master_peak = max(np.max(np.abs(master_audio[0])), np.max(np.abs(master_audio[1])))
|
||||
self.mixer['master'].vu_peak = master_peak
|
||||
|
||||
# LUFS calculation (every ~512 frames)
|
||||
if frames % 512 < frames:
|
||||
self._calculate_lufs(master_audio)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user