Add mic ON/OFF toggle, headphone and studio output busses
- Channels 0-1 are now dedicated Mic 1/Mic 2 with mic_on toggle - Mic audio only routes to main when mic_on=True, never to headphone - New headphone_out_L/R JACK ports (all channels except mics) - New studio_out_L/R JACK ports (copies main, mutes when any mic is on) - Mic ON/OFF toggle button in mixer UI (hidden on non-mic channels) - Session save/load includes mic_on state
This commit is contained in:
+67
-4
@@ -15,8 +15,8 @@ FFMPEG_FORMATS = {'.mp3', '.m4a', '.mp4', '.opus', '.aac', '.wma'}
|
||||
|
||||
NUM_MIXER_CHANNELS = 8
|
||||
DEFAULT_CHANNEL_NAMES = [
|
||||
"Turntable 1", "Turntable 2", "Mic", "Aux 1",
|
||||
"Aux 2", "Aux 3", "Aux 4", "Aux 5",
|
||||
"Mic 1", "Mic 2", "Aux 1",
|
||||
"Aux 2", "Aux 3", "Aux 4", "Aux 5", "Aux 6",
|
||||
]
|
||||
|
||||
|
||||
@@ -163,6 +163,7 @@ class MixerChannel:
|
||||
self.muted = False
|
||||
self.solo = False
|
||||
self.pfl = False
|
||||
self.mic_on = False
|
||||
self.vu_peak = 0.0
|
||||
self.in_port_L = None
|
||||
self.in_port_R = None
|
||||
@@ -285,6 +286,18 @@ class AudioEngine:
|
||||
self.client.outports.register("master_out_R"),
|
||||
)
|
||||
|
||||
# Headphone output ports (all channels except mics)
|
||||
self.headphone_out = (
|
||||
self.client.outports.register("headphone_out_L"),
|
||||
self.client.outports.register("headphone_out_R"),
|
||||
)
|
||||
|
||||
# Studio output ports (same as main, mutes when any mic is on)
|
||||
self.studio_out = (
|
||||
self.client.outports.register("studio_out_L"),
|
||||
self.client.outports.register("studio_out_R"),
|
||||
)
|
||||
|
||||
# PFL output ports
|
||||
self.pfl_out = (
|
||||
self.client.outports.register("pfl_out_L"),
|
||||
@@ -325,6 +338,15 @@ class AudioEngine:
|
||||
if 0 <= index < NUM_MIXER_CHANNELS:
|
||||
self.mixer['channels'][index].pfl = pfl
|
||||
|
||||
def set_channel_mic_on(self, index: int, on: bool):
|
||||
if 0 <= index < NUM_MIXER_CHANNELS:
|
||||
self.mixer['channels'][index].mic_on = on
|
||||
|
||||
def get_channel_mic_on(self, index: int) -> bool:
|
||||
if 0 <= index < NUM_MIXER_CHANNELS:
|
||||
return self.mixer['channels'][index].mic_on
|
||||
return False
|
||||
|
||||
def _fill_port(self, port_L, port_R, deck: DeckState, frames: int):
|
||||
buf_L = port_L.get_array()
|
||||
buf_R = port_R.get_array()
|
||||
@@ -423,7 +445,18 @@ class AudioEngine:
|
||||
master_buf_L.fill(0.0)
|
||||
master_buf_R.fill(0.0)
|
||||
|
||||
headphone_buf_L = self.headphone_out[0].get_array()
|
||||
headphone_buf_R = self.headphone_out[1].get_array()
|
||||
headphone_buf_L.fill(0.0)
|
||||
headphone_buf_R.fill(0.0)
|
||||
|
||||
studio_buf_L = self.studio_out[0].get_array()
|
||||
studio_buf_R = self.studio_out[1].get_array()
|
||||
studio_buf_L.fill(0.0)
|
||||
studio_buf_R.fill(0.0)
|
||||
|
||||
any_solo = any(ch.solo for ch in self.mixer['channels'])
|
||||
any_mic_on = any(ch.mic_on for ch in self.mixer['channels'][:2])
|
||||
|
||||
# PFL bus
|
||||
pfl_buf_L = self.pfl_out[0].get_array()
|
||||
@@ -436,6 +469,7 @@ class AudioEngine:
|
||||
in_R = ch.in_port_R.get_array()
|
||||
|
||||
mute = ch.muted or (any_solo and not ch.solo)
|
||||
is_mic = ch.index < 2
|
||||
|
||||
# PFL: pre-fader, pre-mute signal to PFL bus
|
||||
if ch.pfl:
|
||||
@@ -447,16 +481,31 @@ class AudioEngine:
|
||||
continue
|
||||
|
||||
vol = ch.volume
|
||||
goes_to_main = not is_mic or ch.mic_on
|
||||
goes_to_headphone = not is_mic
|
||||
|
||||
peak = 0.0
|
||||
for i in range(frames):
|
||||
s_L = in_L[i] * vol
|
||||
s_R = in_R[i] * vol
|
||||
master_buf_L[i] += s_L
|
||||
master_buf_R[i] += s_R
|
||||
if goes_to_main:
|
||||
master_buf_L[i] += s_L
|
||||
master_buf_R[i] += s_R
|
||||
if goes_to_headphone:
|
||||
headphone_buf_L[i] += s_L
|
||||
headphone_buf_R[i] += s_R
|
||||
peak = max(peak, abs(s_L), abs(s_R))
|
||||
|
||||
ch.vu_peak = peak
|
||||
|
||||
# ── Studio bus: same as main, muted when any mic is on ────────
|
||||
if any_mic_on:
|
||||
studio_buf_L.fill(0.0)
|
||||
studio_buf_R.fill(0.0)
|
||||
else:
|
||||
studio_buf_L[:] = master_buf_L[:]
|
||||
studio_buf_R[:] = master_buf_R[:]
|
||||
|
||||
# ── Master DSP ──────────────────────────────────────────────────
|
||||
master_audio = np.array([master_buf_L, master_buf_R])
|
||||
|
||||
@@ -466,6 +515,20 @@ class AudioEngine:
|
||||
master_buf_L[:] = master_audio[0]
|
||||
master_buf_R[:] = master_audio[1]
|
||||
|
||||
# Apply same DSP to headphone (without mics)
|
||||
hp_audio = np.array([headphone_buf_L, headphone_buf_R])
|
||||
self.mixer['master'].compressor.process(hp_audio)
|
||||
self.mixer['master'].limiter.process(hp_audio)
|
||||
headphone_buf_L[:] = hp_audio[0]
|
||||
headphone_buf_R[:] = hp_audio[1]
|
||||
|
||||
# Apply same DSP to studio
|
||||
studio_audio = np.array([studio_buf_L, studio_buf_R])
|
||||
self.mixer['master'].compressor.process(studio_audio)
|
||||
self.mixer['master'].limiter.process(studio_audio)
|
||||
studio_buf_L[:] = studio_audio[0]
|
||||
studio_buf_R[:] = studio_audio[1]
|
||||
|
||||
master_peak = max(np.max(np.abs(master_audio[0])), np.max(np.abs(master_audio[1])))
|
||||
self.mixer['master'].vu_peak = master_peak
|
||||
|
||||
|
||||
Reference in New Issue
Block a user