Compare commits

...

5 Commits

Author SHA1 Message Date
Onyx and Iris
3158ed87c7 fix iris_mic index 2026-01-02 09:37:44 +00:00
Onyx and Iris
426cd1be9f add intro banner 2026-01-02 06:31:44 +00:00
Onyx and Iris
5134c752ff upd reset bind 2026-01-02 06:15:57 +00:00
Onyx and Iris
30f06bb535 upd Bus7 label 2026-01-02 05:54:34 +00:00
Onyx and Iris
9cfba017ea make greater use of Enums to improve readability 2026-01-02 05:54:14 +00:00
5 changed files with 94 additions and 56 deletions

View File

@ -194,7 +194,7 @@ gain = 0.0
mode = "normal" mode = "normal"
[bus-7] [bus-7]
label = "Both Mics" label = "Stream Mics"
mono = false mono = false
eq.on = false eq.on = false
mute = false mute = false

View File

@ -4,7 +4,7 @@ import time
import vban_cmd import vban_cmd
from . import configuration from . import configuration
from .enums import Buttons, Strips from .enums import Buttons, VBANChannels, VMBuses, VMStrips, XAirBuses, XAirStrips
from .layer import ILayer from .layer import ILayer
from .states import AudioState from .states import AudioState
from .util import ensure_mixer_fadeout from .util import ensure_mixer_fadeout
@ -42,44 +42,42 @@ class Audio(ILayer):
def mute_mics(self): def mute_mics(self):
self.state.mute_mics = not self.state.mute_mics self.state.mute_mics = not self.state.mute_mics
if self.state.mute_mics: if self.state.mute_mics:
self.vm.strip[0].mute = True self.vm.strip[VMStrips.onyx_mic].mute = True
self.vm.strip[1].mute = True self.vm.strip[VMStrips.iris_mic].mute = True
self.vm.strip[4].mute = True
self.logger.info('Mics Muted') self.logger.info('Mics Muted')
else: else:
self.vm.strip[0].mute = False self.vm.strip[VMStrips.onyx_mic].mute = False
self.vm.strip[1].mute = False self.vm.strip[VMStrips.iris_mic].mute = False
self.vm.strip[4].mute = False
self.logger.info('Mics Unmuted') self.logger.info('Mics Unmuted')
self.vm.button[Buttons.mute_mics].stateonly = self.state.mute_mics self.vm.button[Buttons.mute_mics].stateonly = self.state.mute_mics
def only_discord(self): def only_discord(self):
self.state.only_discord = not self.state.only_discord self.state.only_discord = not self.state.only_discord
if self.state.only_discord: if self.state.only_discord:
self.mixer.dca[0].on = False self.vm.bus[VMBuses.both_mics].mute = True
self.vm.strip[4].mute = True self.mixer.strip[XAirStrips.comms].send[XAirBuses.stream_mix].level = -90
self.logger.info('Only Discord Enabled') self.logger.info('Only Discord Enabled')
else: else:
self.vm.strip[4].mute = False self.mixer.strip[XAirStrips.comms].send[XAirBuses.stream_mix].level = -24
self.mixer.dca[0].on = True self.vm.bus[VMBuses.both_mics].mute = False
self.logger.info('Only Discord Disabled') self.logger.info('Only Discord Disabled')
self.vm.button[Buttons.only_discord].stateonly = self.state.only_discord self.vm.button[Buttons.only_discord].stateonly = self.state.only_discord
def only_stream(self): def only_stream(self):
self.state.only_stream = not self.state.only_stream self.state.only_stream = not self.state.only_stream
if self.state.only_stream: if self.state.only_stream:
self.vm.bus[5].mute = True self.vm.bus[VMBuses.onyx_mic].mute = True
self.vm.bus[6].mute = True self.vm.bus[VMBuses.iris_mic].mute = True
self.vm.strip[2].gain = -3 self.vm.strip[VMStrips.onyx_pc].gain = -3
self.vm.strip[3].gain = -3 self.vm.strip[VMStrips.iris_pc].gain = -3
self.vm.strip[6].gain = -3 self.vm.strip[VMStrips.pretzel].gain = -3
self.logger.info('Only Stream Enabled') self.logger.info('Only Stream Enabled')
else: else:
self.vm.strip[2].gain = 0 self.vm.strip[VMStrips.onyx_pc].gain = 0
self.vm.strip[3].gain = 0 self.vm.strip[VMStrips.iris_pc].gain = 0
self.vm.strip[6].gain = 0 self.vm.strip[VMStrips.pretzel].gain = 0
self.vm.bus[5].mute = False self.vm.bus[VMBuses.onyx_mic].mute = False
self.vm.bus[6].mute = False self.vm.bus[VMBuses.iris_mic].mute = False
self.logger.info('Only Stream Disabled') self.logger.info('Only Stream Disabled')
self.vm.button[Buttons.only_stream].stateonly = self.state.only_stream self.vm.button[Buttons.only_stream].stateonly = self.state.only_stream
@ -113,54 +111,54 @@ class Audio(ILayer):
self.state.sound_test = not self.state.sound_test self.state.sound_test = not self.state.sound_test
if self.state.sound_test: if self.state.sound_test:
self.vm.strip[Strips.onyx_mic].apply({'A1': True, 'B1': False, 'B3': False, 'mute': False}) self.vm.strip[VMStrips.onyx_mic].apply({'A1': True, 'B1': False, 'B3': False, 'mute': False})
self.vm.strip[Strips.iris_mic].apply({'A1': True, 'B2': False, 'B3': False, 'mute': False}) self.vm.strip[VMStrips.iris_mic].apply({'A1': True, 'B2': False, 'B3': False, 'mute': False})
self.vm.vban.outstream[0].on = True self.vm.vban.outstream[VBANChannels.onyx_mic].on = True
self.vm.vban.outstream[1].on = True self.vm.vban.outstream[VBANChannels.iris_mic].on = True
self.vm.vban.outstream[0].route = 0 self.vm.vban.outstream[VBANChannels.onyx_mic].route = 0
self.vm.vban.outstream[1].route = 0 self.vm.vban.outstream[VBANChannels.iris_mic].route = 0
toggle_soundtest(ENABLE_SOUNDTEST) toggle_soundtest(ENABLE_SOUNDTEST)
self.logger.info('Sound Test Enabled') self.logger.info('Sound Test Enabled')
else: else:
toggle_soundtest(DISABLE_SOUNDTEST) toggle_soundtest(DISABLE_SOUNDTEST)
self.vm.vban.outstream[0].route = 5 self.vm.vban.outstream[VBANChannels.onyx_mic].route = 5
self.vm.vban.outstream[1].route = 6 self.vm.vban.outstream[VBANChannels.iris_mic].route = 6
self.vm.strip[Strips.onyx_mic].apply({'A1': False, 'B1': True, 'B3': True, 'mute': True}) self.vm.strip[VMStrips.onyx_mic].apply({'A1': False, 'B1': True, 'B3': True, 'mute': True})
self.vm.strip[Strips.iris_mic].apply({'A1': False, 'B2': True, 'B3': True, 'mute': True}) self.vm.strip[VMStrips.iris_mic].apply({'A1': False, 'B2': True, 'B3': True, 'mute': True})
self.logger.info('Sound Test Disabled') self.logger.info('Sound Test Disabled')
@ensure_mixer_fadeout @ensure_mixer_fadeout
def stage_onyx_mic(self): def stage_onyx_mic(self):
"""Gain stage SE Electronics DCM8 with phantom power""" """Gain stage SE Electronics DCM8 with phantom power"""
self.mixer.headamp[10].phantom = True self.mixer.headamp[XAirStrips.onyx_mic].phantom = True
for i in range(21): for i in range(21):
self.mixer.headamp[10].gain = i self.mixer.headamp[XAirStrips.onyx_mic].gain = i
time.sleep(0.1) time.sleep(0.1)
self.logger.info('Onyx Mic Staged with Phantom Power') self.logger.info('Onyx Mic Staged with Phantom Power')
@ensure_mixer_fadeout @ensure_mixer_fadeout
def stage_iris_mic(self): def stage_iris_mic(self):
"""Gain stage TLM102 with phantom power""" """Gain stage TLM102 with phantom power"""
self.mixer.headamp[11].phantom = True self.mixer.headamp[XAirStrips.iris_mic].phantom = True
for i in range(31): for i in range(31):
self.mixer.headamp[11].gain = i self.mixer.headamp[XAirStrips.iris_mic].gain = i
time.sleep(0.1) time.sleep(0.1)
self.logger.info('Iris Mic Staged with Phantom Power') self.logger.info('Iris Mic Staged with Phantom Power')
def unstage_onyx_mic(self): def unstage_onyx_mic(self):
"""Unstage SE Electronics DCM8 and disable phantom power""" """Unstage SE Electronics DCM8 and disable phantom power"""
for i in reversed(range(21)): for i in reversed(range(21)):
self.mixer.headamp[10].gain = i self.mixer.headamp[XAirStrips.onyx_mic].gain = i
time.sleep(0.1) time.sleep(0.1)
self.mixer.headamp[10].phantom = False self.mixer.headamp[XAirStrips.onyx_mic].phantom = False
self.logger.info('Onyx Mic Unstaged and Phantom Power Disabled') self.logger.info('Onyx Mic Unstaged and Phantom Power Disabled')
def unstage_iris_mic(self): def unstage_iris_mic(self):
"""Unstage TLM102 and disable phantom power""" """Unstage TLM102 and disable phantom power"""
for i in reversed(range(31)): for i in reversed(range(31)):
self.mixer.headamp[11].gain = i self.mixer.headamp[XAirStrips.iris_mic].gain = i
time.sleep(0.1) time.sleep(0.1)
self.mixer.headamp[11].phantom = False self.mixer.headamp[XAirStrips.iris_mic].phantom = False
self.logger.info('Iris Mic Unstaged and Phantom Power Disabled') self.logger.info('Iris Mic Unstaged and Phantom Power Disabled')
def solo_onyx(self): def solo_onyx(self):
@ -169,6 +167,8 @@ class Audio(ILayer):
def solo_iris(self): def solo_iris(self):
"""placeholder method""" """placeholder method"""
### Workstation and TV Audio Routing via VBAN ###
def _fade_mixer(self, target_fader, fade_in=True): def _fade_mixer(self, target_fader, fade_in=True):
"""Fade the mixer's fader to the target level.""" """Fade the mixer's fader to the target level."""
current_fader = self.mixer.lr.mix.fader current_fader = self.mixer.lr.mix.fader

View File

@ -2,5 +2,25 @@ from enum import IntEnum
Buttons = IntEnum('Buttons', 'mute_mics only_discord only_stream', start=0) Buttons = IntEnum('Buttons', 'mute_mics only_discord only_stream', start=0)
Strips = IntEnum('Strips', 'onyx_mic iris_mic onyx_pc iris_pc', start=0) # Voicemeeter Channels
Buses = IntEnum('Buses', 'MR18 ASIO[1,2] ASIO[3,4] ASIO[5,6] ASIO[7,8] onyx_mic iris_mic both_mics', start=5) VMStrips = IntEnum('Strips', 'onyx_mic iris_mic onyx_pc iris_pc st_input_5 system comms pretzel', start=0)
VMBuses = IntEnum('Buses', 'onyx_mic iris_mic both_mics', start=5)
# VBAN Channels
VBANChannels = IntEnum('VBANChannels', 'onyx_mic iris_mic comms workstation', start=0)
# XAir Channels
class XAirStrips(IntEnum):
system = 0
comms = 2
pretzel = 4
game_pcs = 6
onyx_mic = 10
iris_mic = 11
class XAirBuses(IntEnum):
onyx_mix = 0
iris_mix = 2
stream_mix = 4

View File

@ -9,6 +9,8 @@ from duckypad_twitch import configuration
logging.basicConfig(level=logging.INFO) logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def register_hotkeys(duckypad): def register_hotkeys(duckypad):
def audio_hotkeys(): def audio_hotkeys():
@ -40,7 +42,7 @@ def register_hotkeys(duckypad):
keyboard.add_hotkey('ctrl+alt+F14', duckypad.obsws.stop_stream) keyboard.add_hotkey('ctrl+alt+F14', duckypad.obsws.stop_stream)
def duckypad_hotkeys(): def duckypad_hotkeys():
keyboard.add_hotkey('ctrl+F21', duckypad.reset) keyboard.add_hotkey('ctrl+F24', duckypad.reset)
for step in ( for step in (
audio_hotkeys, audio_hotkeys,
@ -63,5 +65,21 @@ def run():
register_hotkeys(duckypad) register_hotkeys(duckypad)
banner_width = 80
logger.info(
'\n'.join(
(
'\n' + '#' * banner_width,
'Duckypad Twitch is running. ',
'Run sound test and gain stage mics to verify audio setup.',
'Then start the stream.',
"Don't forget Voicemeeter starts in Only Stream mode!",
'So first unmute mics, then give stream introduction, then disable Only Stream mode.',
'Now you are live with mics unmuted!',
'#' * banner_width,
)
)
)
print('press ctrl+shift+F24 to quit') print('press ctrl+shift+F24 to quit')
keyboard.wait('ctrl+shift+F24') keyboard.wait('ctrl+shift+F24')

View File

@ -1,6 +1,6 @@
import logging import logging
from .enums import Strips from .enums import VMStrips
from .layer import ILayer from .layer import ILayer
from .states import SceneState from .states import SceneState
@ -33,8 +33,8 @@ class Scene(ILayer):
self._state = SceneState() self._state = SceneState()
def start(self): def start(self):
self.vm.strip[Strips.onyx_pc].mute = True self.vm.strip[VMStrips.onyx_pc].mute = True
self.vm.strip[Strips.iris_pc].mute = True self.vm.strip[VMStrips.iris_pc].mute = True
self.obsws.switch_to_scene('START') self.obsws.switch_to_scene('START')
def dual_stream(self): def dual_stream(self):
@ -43,26 +43,26 @@ class Scene(ILayer):
'A5': True, 'A5': True,
'gain': 0, 'gain': 0,
} }
self.vm.strip[Strips.onyx_pc].apply(ENABLE_PC) self.vm.strip[VMStrips.onyx_pc].apply(ENABLE_PC)
self.vm.strip[Strips.iris_pc].apply(ENABLE_PC) self.vm.strip[VMStrips.iris_pc].apply(ENABLE_PC)
self.obsws.switch_to_scene('DUAL STREAM') self.obsws.switch_to_scene('DUAL STREAM')
def brb(self): def brb(self):
self.vm.strip[Strips.onyx_pc].mute = True self.vm.strip[VMStrips.onyx_pc].mute = True
self.vm.strip[Strips.iris_pc].mute = True self.vm.strip[VMStrips.iris_pc].mute = True
self.obsws.switch_to_scene('BRB') self.obsws.switch_to_scene('BRB')
def end(self): def end(self):
self.vm.strip[Strips.onyx_pc].mute = True self.vm.strip[VMStrips.onyx_pc].mute = True
self.vm.strip[Strips.iris_pc].mute = True self.vm.strip[VMStrips.iris_pc].mute = True
self.obsws.switch_to_scene('END') self.obsws.switch_to_scene('END')
def onyx_solo(self): def onyx_solo(self):
self.vm.strip[Strips.onyx_pc].mute = False self.vm.strip[VMStrips.onyx_pc].mute = False
self.vm.strip[Strips.iris_pc].mute = True self.vm.strip[VMStrips.iris_pc].mute = True
self.obsws.switch_to_scene('ONYX SOLO') self.obsws.switch_to_scene('ONYX SOLO')
def iris_solo(self): def iris_solo(self):
self.vm.strip[Strips.onyx_pc].mute = True self.vm.strip[VMStrips.onyx_pc].mute = True
self.vm.strip[Strips.iris_pc].mute = False self.vm.strip[VMStrips.iris_pc].mute = False
self.obsws.switch_to_scene('IRIS SOLO') self.obsws.switch_to_scene('IRIS SOLO')