1 # This file is part of jack_mixer
3 # Copyright (C) 2006 Nedko Arnaudov <nedko@arnaudov.name>
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; version 2 of the License
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 import gi
# noqa: F401
21 from gi
.repository
import Gtk
22 from gi
.repository
import Gdk
23 from gi
.repository
import GObject
24 from gi
.repository
import Pango
29 from serialization
import SerializedObject
30 from styling
import set_background_color
, random_color
33 log
= logging
.getLogger(__name__
)
37 class Channel(Gtk
.Box
, SerializedObject
):
38 """Widget with slider and meter used as base class for more specific
43 def __init__(self
, app
, name
, stereo
, value
=None):
44 super().__init
__(orientation
=Gtk
.Orientation
.VERTICAL
)
46 self
.mixer
= app
.mixer
48 self
.gui_factory
= app
.gui_factory
49 self
._channel
_name
= name
51 self
.initial_value
= value
52 self
.meter_scale
= self
.gui_factory
.get_default_meter_scale()
53 self
.slider_scale
= self
.gui_factory
.get_default_slider_scale()
54 self
.slider_adjustment
= slider
.AdjustmentdBFS(self
.slider_scale
, 0.0, 0.02)
55 self
.balance_adjustment
= slider
.BalanceAdjustment()
56 self
.post_fader_output_channel
= None
57 self
.future_out_mute
= None
58 self
.future_volume_midi_cc
= None
59 self
.future_balance_midi_cc
= None
60 self
.future_mute_midi_cc
= None
61 self
.future_solo_midi_cc
= None
62 self
.css_name
= "css_name_%d" % Channel
.num_instances
63 self
.label_name
= None
65 self
.label_chars_wide
= 12
66 self
.label_chars_narrow
= 7
67 self
.channel_properties_dialog
= None
68 self
.monitor_button
= None
69 Channel
.num_instances
+= 1
71 # ---------------------------------------------------------------------------------------------
75 def channel_name(self
):
76 return self
._channel
_name
79 def channel_name(self
, name
):
80 self
.app
.on_channel_rename(self
._channel
_name
, name
)
81 self
._channel
_name
= name
83 self
.label_name
.set_text(name
)
84 if len(name
) > (self
.label_chars_wide
if self
.wide
else self
.label_chars_narrow
):
85 self
.label_name
.set_tooltip_text(name
)
87 self
.channel
.name
= name
88 if self
.post_fader_output_channel
:
89 self
.post_fader_output_channel
.name
= "%s Out" % name
91 # ---------------------------------------------------------------------------------------------
92 # UI creation and (de-)initialization
94 def create_balance_widget(self
):
95 self
.balance
= slider
.BalanceSlider(self
.balance_adjustment
, (20, 20), (0, 100))
98 def create_buttons(self
):
99 # Mute, Solo and Monitor buttons
100 self
.hbox_mutesolo
= Gtk
.Box(False, 0, orientation
=Gtk
.Orientation
.HORIZONTAL
)
102 self
.mute
= Gtk
.ToggleButton()
103 self
.mute
.set_label("M")
104 self
.mute
.get_style_context().add_class("mute")
105 self
.mute
.set_active(self
.channel
.out_mute
)
106 self
.mute
.connect("toggled", self
.on_mute_toggled
)
107 self
.hbox_mutesolo
.pack_start(self
.mute
, True, True, 0)
109 self
.pack_start(self
.hbox_mutesolo
, False, False, 0)
111 self
.monitor_button
= Gtk
.ToggleButton("MON")
112 self
.monitor_button
.get_style_context().add_class("monitor")
113 self
.monitor_button
.connect("toggled", self
.on_monitor_button_toggled
)
114 self
.pack_start(self
.monitor_button
, False, False, 0)
116 def create_fader(self
):
117 # HBox for fader and meter
118 self
.vbox_fader
= Gtk
.Box(orientation
=Gtk
.Orientation
.VERTICAL
)
119 self
.vbox_fader
.get_style_context().add_class("vbox_fader")
121 self
.hbox_readouts
= Gtk
.Box(orientation
=Gtk
.Orientation
.HORIZONTAL
)
122 self
.hbox_readouts
.set_homogeneous(True)
123 self
.hbox_readouts
.pack_start(self
.volume_digits
, False, True, 0)
124 self
.hbox_readouts
.pack_start(self
.abspeak
, False, True, 0)
125 self
.vbox_fader
.pack_start(self
.hbox_readouts
, False, False, 0)
127 self
.hbox_fader
= Gtk
.Box(orientation
=Gtk
.Orientation
.HORIZONTAL
)
128 self
.hbox_fader
.pack_start(self
.slider
, True, True, 0)
129 self
.hbox_fader
.pack_start(self
.meter
, True, True, 0)
130 self
.vbox_fader
.pack_start(self
.hbox_fader
, True, True, 0)
131 self
.vbox_fader
.pack_start(self
.balance
, False, True, 0)
133 self
.pack_start(self
.vbox_fader
, True, True, 0)
135 def create_slider_widget(self
):
138 parent
= self
.slider
.get_parent()
139 self
.slider
.destroy()
141 if self
.gui_factory
.use_custom_widgets
:
142 self
.slider
= slider
.CustomSliderWidget(self
.slider_adjustment
)
144 self
.slider
= slider
.VolumeSlider(self
.slider_adjustment
)
147 parent
.pack_start(self
.slider
, True, True, 0)
148 parent
.reorder_child(self
.slider
, 0)
153 log
.debug('Realizing channel "%s".', self
.channel_name
)
154 if self
.future_out_mute
is not None:
155 self
.channel
.out_mute
= self
.future_out_mute
158 # Channel strip label
159 self
.vbox
= Gtk
.Box(orientation
=Gtk
.Orientation
.VERTICAL
)
160 self
.pack_start(self
.vbox
, False, True, 0)
161 self
.label_name
= Gtk
.Label()
162 self
.label_name
.get_style_context().add_class("top_label")
163 self
.label_name
.set_text(self
.channel_name
)
164 self
.label_name
.set_max_width_chars(
165 self
.label_chars_wide
if self
.wide
else self
.label_chars_narrow
167 self
.label_name
.set_ellipsize(Pango
.EllipsizeMode
.MIDDLE
)
168 self
.label_name_event_box
= Gtk
.EventBox()
169 self
.label_name_event_box
.connect("button-press-event", self
.on_label_mouse
)
170 self
.label_name_event_box
.add(self
.label_name
)
174 self
.create_slider_widget()
175 self
.create_balance_widget()
178 self
.volume_digits
= Gtk
.Entry()
179 self
.volume_digits
.set_has_frame(False)
180 self
.volume_digits
.set_width_chars(5)
181 self
.volume_digits
.set_property("xalign", 0.5)
182 self
.volume_digits
.connect("key-press-event", self
.on_volume_digits_key_pressed
)
183 self
.volume_digits
.connect("focus-out-event", self
.on_volume_digits_focus_out
)
184 self
.volume_digits
.get_style_context().add_class("readout")
187 self
.abspeak
= abspeak
.AbspeakWidget()
188 self
.abspeak
.connect("reset", self
.on_abspeak_reset
)
189 self
.abspeak
.connect("volume-adjust", self
.on_abspeak_adjust
)
190 self
.abspeak
.get_style_context().add_class("readout")
194 self
.meter
= meter
.StereoMeterWidget(self
.meter_scale
)
196 self
.meter
= meter
.MonoMeterWidget(self
.meter_scale
)
198 self
.meter
.set_events(Gdk
.EventMask
.SCROLL_MASK
)
199 self
.on_vumeter_color_changed(self
.gui_factory
)
201 if self
.initial_value
is not None:
202 if self
.initial_value
is True:
203 self
.slider_adjustment
.set_value(0)
205 self
.slider_adjustment
.set_value_db(0)
207 self
.slider_adjustment
.connect("volume-changed", self
.on_volume_changed
)
208 self
.slider_adjustment
.connect(
209 "volume-changed-from-midi", self
.on_volume_changed_from_midi
211 self
.balance_adjustment
.connect("balance-changed", self
.on_balance_changed
)
213 self
.gui_factory
.connect(
214 "default-meter-scale-changed", self
.on_default_meter_scale_changed
216 self
.gui_factory
.connect(
217 "default-slider-scale-changed", self
.on_default_slider_scale_changed
219 self
.gui_factory
.connect("vumeter-color-changed", self
.on_vumeter_color_changed
)
220 self
.gui_factory
.connect("vumeter-color-scheme-changed", self
.on_vumeter_color_changed
)
221 self
.gui_factory
.connect("use-custom-widgets-changed", self
.on_custom_widgets_changed
)
223 self
.connect("key-press-event", self
.on_key_pressed
)
224 self
.connect("scroll-event", self
.on_scroll
)
226 entries
= [Gtk
.TargetEntry
.new(self
.__class
__.__name
__, Gtk
.TargetFlags
.SAME_APP
, 0)]
227 self
.label_name_event_box
.drag_source_set(
228 Gdk
.ModifierType
.BUTTON1_MASK
, entries
, Gdk
.DragAction
.MOVE
230 self
.label_name_event_box
.connect("drag-data-get", self
.on_drag_data_get
)
231 self
.drag_dest_set(Gtk
.DestDefaults
.ALL
, entries
, Gdk
.DragAction
.MOVE
)
232 self
.connect_after("drag-data-received", self
.on_drag_data_received
)
234 self
.vbox
.pack_start(self
.label_name_event_box
, True, True, 0)
237 log
.debug('Unrealizing channel "%s".', self
.channel_name
)
239 # ---------------------------------------------------------------------------------------------
240 # Signal/event handlers
242 def on_label_mouse(self
, widget
, event
):
243 if event
.type == Gdk
.EventType
._2BUTTON
_PRESS
:
244 if event
.button
== 1:
245 self
.on_channel_properties()
248 event
.state
& Gdk
.ModifierType
.CONTROL_MASK
249 and event
.type == Gdk
.EventType
.BUTTON_PRESS
250 and event
.button
== 1
258 def on_channel_properties(self
):
259 if not self
.channel_properties_dialog
:
260 self
.channel_properties_dialog
= self
.properties_dialog_class(self
.app
, self
)
261 self
.channel_properties_dialog
.fill_and_show()
263 def on_default_meter_scale_changed(self
, gui_factory
, scale
):
264 log
.debug("Default meter scale change detected.")
265 self
.meter
.set_scale(scale
)
267 def on_default_slider_scale_changed(self
, gui_factory
, scale
):
268 log
.debug("Default slider scale change detected.")
269 self
.slider_scale
= scale
270 self
.slider_adjustment
.set_scale(scale
)
272 self
.channel
.midi_scale
= self
.slider_scale
.scale
274 def on_vumeter_color_changed(self
, gui_factory
, *args
):
275 color
= gui_factory
.get_vumeter_color()
276 color_scheme
= gui_factory
.get_vumeter_color_scheme()
277 if color_scheme
!= "solid":
278 self
.meter
.set_color(None)
280 self
.meter
.set_color(Gdk
.color_parse(color
))
282 def on_custom_widgets_changed(self
, gui_factory
, value
):
283 self
.balance
.destroy()
284 self
.create_balance_widget()
285 self
.create_slider_widget()
287 def on_abspeak_adjust(self
, abspeak
, adjust
):
288 log
.debug("abspeak adjust %f", adjust
)
289 self
.slider_adjustment
.set_value_db(self
.slider_adjustment
.get_value_db() + adjust
)
290 self
.channel
.abspeak
= None
291 # We want to update gui even if actual decibels have not changed (scale wrap for example)
292 # self.update_volume(False)
294 def on_abspeak_reset(self
, abspeak
):
295 log
.debug("abspeak reset")
296 self
.channel
.abspeak
= None
298 def on_volume_digits_key_pressed(self
, widget
, event
):
299 if event
.keyval
== Gdk
.KEY_Return
or event
.keyval
== Gdk
.KEY_KP_Enter
:
300 db_text
= self
.volume_digits
.get_text()
303 log
.debug('Volume digits confirmation "%f dBFS".', db
)
305 log
.debug("Volume digits confirmation ignore, reset to current.")
306 self
.update_volume(False)
308 self
.slider_adjustment
.set_value_db(db
)
310 # We want to update gui even if actual decibels have not changed
311 # (scale wrap for example)
312 # self.update_volume(False)
314 def on_volume_digits_focus_out(self
, widget
, event
):
315 log
.debug("Volume digits focus out detected.")
316 self
.update_volume(False)
318 def on_scroll(self
, widget
, event
):
319 if event
.direction
== Gdk
.ScrollDirection
.DOWN
:
320 self
.slider_adjustment
.step_down()
321 elif event
.direction
== Gdk
.ScrollDirection
.UP
:
322 self
.slider_adjustment
.step_up()
325 def on_volume_changed(self
, adjustment
):
326 self
.update_volume(True)
328 def on_volume_changed_from_midi(self
, adjustment
):
329 self
.update_volume(True, from_midi
=True)
331 def on_balance_changed(self
, adjustment
):
332 balance
= self
.balance_adjustment
.get_value()
333 log
.debug("%s balance: %f", self
.channel_name
, balance
)
334 self
.channel
.balance
= balance
335 self
.app
.update_monitor(self
)
337 def on_key_pressed(self
, widget
, event
):
338 if event
.keyval
== Gdk
.KEY_Up
:
339 log
.debug(self
.channel_name
+ " Up")
340 self
.slider_adjustment
.step_up()
342 elif event
.keyval
== Gdk
.KEY_Down
:
343 log
.debug(self
.channel_name
+ " Down")
344 self
.slider_adjustment
.step_down()
349 def on_drag_data_get(self
, widget
, drag_context
, data
, info
, time
):
350 channel
= widget
.get_parent().get_parent()
351 data
.set(data
.get_target(), 8, channel
._channel
_name
.encode("utf-8"))
353 def on_drag_data_received(self
, widget
, drag_context
, x
, y
, data
, info
, time
):
356 def on_midi_event_received(self
, *args
):
357 self
.slider_adjustment
.set_value_db(self
.channel
.volume
, from_midi
=True)
358 self
.balance_adjustment
.set_balance(self
.channel
.balance
, from_midi
=True)
360 def on_mute_toggled(self
, button
):
361 self
.channel
.out_mute
= self
.mute
.get_active()
363 def on_monitor_button_toggled(self
, button
):
364 if button
.get_active():
365 for channel
in self
.app
.channels
+ self
.app
.output_channels
:
366 if channel
.monitor_button
.get_active() and channel
.monitor_button
is not button
:
367 channel
.monitor_button
.handler_block_by_func(channel
.on_monitor_button_toggled
)
368 channel
.monitor_button
.set_active(False)
369 channel
.monitor_button
.handler_unblock_by_func(
370 channel
.on_monitor_button_toggled
372 self
.app
.set_monitored_channel(self
)
374 if self
.app
._monitored
_channel
.channel
.name
== self
.channel
.name
:
375 self
.monitor_button
.handler_block_by_func(self
.on_monitor_button_toggled
)
376 self
.monitor_button
.set_active(True)
377 self
.monitor_button
.handler_unblock_by_func(self
.on_monitor_button_toggled
)
379 def assign_midi_ccs(self
, volume_cc
, balance_cc
, mute_cc
, solo_cc
=None):
382 self
.channel
.volume_midi_cc
= volume_cc
384 volume_cc
= self
.channel
.autoset_volume_midi_cc()
386 log
.debug("Channel '%s' volume assigned to CC #%s.", self
.channel
.name
, volume_cc
)
387 except Exception as exc
:
388 log
.error("Channel '%s' volume CC assignment failed: %s", self
.channel
.name
, exc
)
392 self
.channel
.balance_midi_cc
= balance_cc
394 balance_cc
= self
.channel
.autoset_balance_midi_cc()
396 log
.debug("Channel '%s' balance assigned to CC #%s.", self
.channel
.name
, balance_cc
)
397 except Exception as exc
:
398 log
.error("Channel '%s' balance CC assignment failed: %s", self
.channel
.name
, exc
)
402 self
.channel
.mute_midi_cc
= mute_cc
404 mute_cc
= self
.channel
.autoset_mute_midi_cc()
406 log
.debug("Channel '%s' mute assigned to CC #%s.", self
.channel
.name
, mute_cc
)
407 except Exception as exc
:
408 log
.error("Channel '%s' mute CC assignment failed: %s", self
.channel
.name
, exc
)
410 if solo_cc
is not None:
413 self
.channel
.solo_midi_cc
= solo_cc
415 solo_cc
= self
.channel
.autoset_solo_midi_cc()
417 log
.debug("Channel '%s' solo assigned to CC #%s.", self
.channel
.name
, solo_cc
)
418 except Exception as exc
:
419 log
.error("Channel '%s' solo CC assignment failed: %s", self
.channel
.name
, exc
)
421 # ---------------------------------------------------------------------------------------------
424 def set_monitored(self
):
426 self
.app
.set_monitored_channel(self
)
427 self
.monitor_button
.set_active(True)
429 def set_color(self
, color
):
431 set_background_color(self
.label_name_event_box
, self
.css_name
, self
.color
)
433 def widen(self
, flag
=True):
435 ctx
= self
.label_name
.get_style_context()
438 ctx
.remove_class("narrow")
439 ctx
.add_class("wide")
441 ctx
.remove_class("wide")
442 ctx
.add_class("narrow")
444 label
= self
.label_name
.get_label()
445 label_width
= self
.label_chars_wide
if flag
else self
.label_chars_narrow
446 self
.label_name
.set_max_width_chars(label_width
)
448 if len(label
) > label_width
:
449 self
.label_name
.set_tooltip_text(label
)
451 self
.meter
.widen(flag
)
452 self
.hbox_readouts
.set_orientation(
453 Gtk
.Orientation
.HORIZONTAL
if flag
else Gtk
.Orientation
.VERTICAL
459 def read_meter(self
):
463 peak_left
, peak_right
, rms_left
, rms_right
= self
.channel
.kmeter
464 self
.meter
.set_values(peak_left
, peak_right
, rms_left
, rms_right
)
466 peak
, rms
= self
.channel
.kmeter
467 self
.meter
.set_values(peak
, rms
)
469 self
.abspeak
.set_peak(self
.channel
.abspeak
)
471 def update_volume(self
, update_engine
, from_midi
=False):
472 db
= self
.slider_adjustment
.get_value_db()
474 db_text
= "%.2f" % db
475 self
.volume_digits
.set_text(db_text
)
479 self
.channel
.volume
= db
480 self
.app
.update_monitor(self
)
482 # ---------------------------------------------------------------------------------------------
483 # Channel (de-)serialization
485 def serialize(self
, object_backend
):
486 object_backend
.add_property("volume", "%f" % self
.slider_adjustment
.get_value_db())
487 object_backend
.add_property("balance", "%f" % self
.balance_adjustment
.get_value())
488 object_backend
.add_property("wide", "%s" % str(self
.wide
))
490 if hasattr(self
.channel
, "out_mute"):
491 object_backend
.add_property("out_mute", str(self
.channel
.out_mute
))
492 if self
.channel
.volume_midi_cc
!= -1:
493 object_backend
.add_property("volume_midi_cc", str(self
.channel
.volume_midi_cc
))
494 if self
.channel
.balance_midi_cc
!= -1:
495 object_backend
.add_property("balance_midi_cc", str(self
.channel
.balance_midi_cc
))
496 if self
.channel
.mute_midi_cc
!= -1:
497 object_backend
.add_property("mute_midi_cc", str(self
.channel
.mute_midi_cc
))
498 if self
.channel
.solo_midi_cc
!= -1:
499 object_backend
.add_property("solo_midi_cc", str(self
.channel
.solo_midi_cc
))
501 def unserialize_property(self
, name
, value
):
503 self
.slider_adjustment
.set_value_db(float(value
))
505 if name
== "balance":
506 self
.balance_adjustment
.set_value(float(value
))
508 if name
== "out_mute":
509 self
.future_out_mute
= value
== "True"
511 if name
== "volume_midi_cc":
512 self
.future_volume_midi_cc
= int(value
)
514 if name
== "balance_midi_cc":
515 self
.future_balance_midi_cc
= int(value
)
517 if name
== "mute_midi_cc":
518 self
.future_mute_midi_cc
= int(value
)
520 if name
== "solo_midi_cc":
521 self
.future_solo_midi_cc
= int(value
)
524 self
.wide
= value
== "True"
529 class InputChannel(Channel
):
530 def __init__(self
, *args
, **kwargs
):
531 super().__init
__(*args
, **kwargs
)
532 self
.properties_dialog_class
= ChannelPropertiesDialog
534 def create_buttons(self
):
535 super().create_buttons()
536 self
.solo
= Gtk
.ToggleButton()
537 self
.solo
.set_label("S")
538 self
.solo
.get_style_context().add_class("solo")
539 self
.solo
.set_active(self
.channel
.solo
)
540 self
.solo
.connect("toggled", self
.on_solo_toggled
)
541 self
.hbox_mutesolo
.pack_start(self
.solo
, True, True, 0)
544 self
.channel
= self
.mixer
.add_channel(self
.channel_name
, self
.stereo
)
546 if self
.channel
is None:
547 raise Exception("Cannot create a channel")
551 if self
.future_volume_midi_cc
is not None:
552 self
.channel
.volume_midi_cc
= self
.future_volume_midi_cc
553 if self
.future_balance_midi_cc
is not None:
554 self
.channel
.balance_midi_cc
= self
.future_balance_midi_cc
555 if self
.future_mute_midi_cc
is not None:
556 self
.channel
.mute_midi_cc
= self
.future_mute_midi_cc
557 if self
.future_solo_midi_cc
is not None:
558 self
.channel
.solo_midi_cc
= self
.future_solo_midi_cc
559 if self
.app
._init
_solo
_channels
and self
.channel_name
in self
.app
._init
_solo
_channels
:
560 self
.channel
.solo
= True
562 self
.channel
.midi_scale
= self
.slider_scale
.scale
564 self
.on_volume_changed(self
.slider_adjustment
)
565 self
.on_balance_changed(self
.balance_adjustment
)
568 self
.create_buttons()
575 if self
.post_fader_output_channel
:
576 self
.post_fader_output_channel
.remove()
577 self
.post_fader_output_channel
= None
578 self
.channel
.remove()
583 for cg
in self
.get_control_groups():
586 def widen(self
, flag
=True):
588 for cg
in self
.get_control_groups():
591 def on_drag_data_received(self
, widget
, drag_context
, x
, y
, data
, info
, time
):
592 source_name
= data
.get_data().decode("utf-8")
593 if source_name
== self
._channel
_name
:
595 self
.emit("input-channel-order-changed", source_name
, self
._channel
_name
)
597 def add_control_group(self
, channel
):
598 control_group
= ControlGroup(channel
, self
)
599 control_group
.show_all()
600 self
.vbox
.pack_start(control_group
, True, True, 0)
603 def remove_control_group(self
, channel
):
604 ctlgroup
= self
.get_control_group(channel
)
605 self
.vbox
.remove(ctlgroup
)
607 def update_control_group(self
, channel
):
608 for control_group
in self
.vbox
.get_children():
609 if isinstance(control_group
, ControlGroup
):
610 if control_group
.output_channel
is channel
:
611 control_group
.update()
613 def get_control_group(self
, channel
):
614 for control_group
in self
.get_control_groups():
615 if control_group
.output_channel
is channel
:
619 def get_control_groups(self
):
621 for c
in self
.vbox
.get_children():
622 if isinstance(c
, ControlGroup
):
626 def midi_events_check(self
):
627 if self
.channel
is not None and self
.channel
.midi_in_got_events
:
628 self
.mute
.set_active(self
.channel
.out_mute
)
629 self
.solo
.set_active(self
.channel
.solo
)
630 super().on_midi_event_received()
632 def on_solo_toggled(self
, button
):
633 self
.channel
.solo
= self
.solo
.get_active()
635 def on_solo_button_pressed(self
, button
, event
, *args
):
636 if event
.button
== 3:
637 # right click on the solo button, act on all output channels
638 if button
.get_active(): # was soloed
639 button
.set_active(False)
640 if hasattr(button
, "touched_channels"):
641 touched_channels
= button
.touched_channels
642 for chan
in touched_channels
:
643 ctlgroup
= self
.get_control_group(chan
)
644 ctlgroup
.solo
.set_active(False)
645 del button
.touched_channels
646 else: # was not soloed
647 button
.set_active(True)
648 touched_channels
= []
649 for chan
in self
.app
.output_channels
:
650 ctlgroup
= self
.get_control_group(chan
)
651 if not ctlgroup
.solo
.get_active():
652 ctlgroup
.solo
.set_active(True)
653 touched_channels
.append(chan
)
654 button
.touched_channels
= touched_channels
659 def serialization_name(cls
):
660 return "input_channel"
662 def serialize(self
, object_backend
):
663 object_backend
.add_property("name", self
.channel_name
)
665 object_backend
.add_property("type", "stereo")
667 object_backend
.add_property("type", "mono")
668 super().serialize(object_backend
)
670 def unserialize_property(self
, name
, value
):
672 self
.channel_name
= str(value
)
675 if value
== "stereo":
681 return super().unserialize_property(name
, value
)
684 class OutputChannel(Channel
):
685 def __init__(self
, *args
, **kwargs
):
686 super().__init
__(*args
, **kwargs
)
687 self
.properties_dialog_class
= OutputChannelPropertiesDialog
688 self
._display
_solo
_buttons
= False
689 self
._init
_muted
_channels
= None
690 self
._init
_solo
_channels
= None
691 self
._init
_prefader
_channels
= None
694 def display_solo_buttons(self
):
695 return self
._display
_solo
_buttons
697 @display_solo_buttons.setter
698 def display_solo_buttons(self
, value
):
699 self
._display
_solo
_buttons
= value
700 # notifying control groups
701 for inputchannel
in self
.app
.channels
:
702 inputchannel
.update_control_group(self
)
705 self
.channel
= self
.mixer
.add_output_channel(self
.channel_name
, self
.stereo
)
707 if self
.channel
is None:
708 raise Exception("Cannot create a channel")
712 if self
.future_volume_midi_cc
is not None:
713 self
.channel
.volume_midi_cc
= self
.future_volume_midi_cc
714 if self
.future_balance_midi_cc
is not None:
715 self
.channel
.balance_midi_cc
= self
.future_balance_midi_cc
716 if self
.future_mute_midi_cc
is not None:
717 self
.channel
.mute_midi_cc
= self
.future_mute_midi_cc
718 self
.channel
.midi_scale
= self
.slider_scale
.scale
720 self
.on_volume_changed(self
.slider_adjustment
)
721 self
.on_balance_changed(self
.balance_adjustment
)
723 set_background_color(self
.label_name_event_box
, self
.css_name
, self
.color
)
726 self
.create_buttons()
728 # add control groups to the input channels, and initialize them
730 for input_channel
in self
.app
.channels
:
731 ctlgroup
= input_channel
.add_control_group(self
)
732 name
= input_channel
.channel
.name
733 if self
._init
_muted
_channels
and name
in self
._init
_muted
_channels
:
734 ctlgroup
.mute
.set_active(True)
735 if self
._init
_solo
_channels
and name
in self
._init
_solo
_channels
:
736 ctlgroup
.solo
.set_active(True)
737 if self
._init
_prefader
_channels
and name
in self
._init
_prefader
_channels
:
738 ctlgroup
.prefader
.set_active(True)
739 if not input_channel
.wide
:
742 self
._init
_muted
_channels
= None
743 self
._init
_solo
_channels
= None
744 self
._init
_prefader
_channels
= None
750 # remove control groups from input channels
751 for input_channel
in self
.app
.channels
:
752 input_channel
.remove_control_group(self
)
755 self
.channel
.remove()
758 def on_drag_data_received(self
, widget
, drag_context
, x
, y
, data
, info
, time
):
759 source_name
= data
.get_data().decode("utf-8")
760 if source_name
== self
._channel
_name
:
762 self
.emit("output-channel-order-changed", source_name
, self
._channel
_name
)
764 def midi_events_check(self
):
765 if self
.channel
is not None and self
.channel
.midi_in_got_events
:
766 self
.mute
.set_active(self
.channel
.out_mute
)
767 super().on_midi_event_received()
770 def serialization_name(cls
):
771 return "output_channel"
773 def serialize(self
, object_backend
):
774 object_backend
.add_property("name", self
.channel_name
)
776 object_backend
.add_property("type", "stereo")
778 object_backend
.add_property("type", "mono")
779 if self
.display_solo_buttons
:
780 object_backend
.add_property("solo_buttons", "true")
783 prefader_in_channels
= []
784 for input_channel
in self
.app
.channels
:
785 if self
.channel
.is_muted(input_channel
.channel
):
786 muted_channels
.append(input_channel
)
787 if self
.channel
.is_solo(input_channel
.channel
):
788 solo_channels
.append(input_channel
)
789 if self
.channel
.is_in_prefader(input_channel
.channel
):
790 prefader_in_channels
.append(input_channel
)
792 object_backend
.add_property(
793 "muted_channels", "|".join([x
.channel
.name
for x
in muted_channels
])
796 object_backend
.add_property(
797 "solo_channels", "|".join([x
.channel
.name
for x
in solo_channels
])
799 if prefader_in_channels
:
800 object_backend
.add_property(
801 "prefader_channels", "|".join([x
.channel
.name
for x
in prefader_in_channels
])
803 object_backend
.add_property("color", self
.color
.to_string())
804 super().serialize(object_backend
)
806 def unserialize_property(self
, name
, value
):
808 self
.channel_name
= str(value
)
811 if value
== "stereo":
817 if name
== "solo_buttons":
819 self
.display_solo_buttons
= True
821 if name
== "muted_channels":
822 self
._init
_muted
_channels
= value
.split("|")
824 if name
== "solo_channels":
825 self
._init
_solo
_channels
= value
.split("|")
827 if name
== "prefader_channels":
828 self
._init
_prefader
_channels
= value
.split("|")
835 return super().unserialize_property(name
, value
)
838 class ChannelPropertiesDialog(Gtk
.Dialog
):
839 def __init__(self
, app
, channel
=None, title
=None):
842 raise ValueError("Either 'title' or 'channel' must be passed.")
843 title
= 'Channel "%s" Properties' % channel
.channel_name
845 super().__init
__(title
, app
.window
)
846 self
.channel
= channel
848 self
.mixer
= app
.mixer
849 self
.set_default_size(365, -1)
853 def fill_and_show(self
):
857 def add_buttons(self
):
858 self
.add_button(Gtk
.STOCK_CANCEL
, Gtk
.ResponseType
.CANCEL
)
859 self
.ok_button
= self
.add_button(Gtk
.STOCK_APPLY
, Gtk
.ResponseType
.APPLY
)
860 self
.set_default_response(Gtk
.ResponseType
.APPLY
)
862 self
.connect("response", self
.on_response_cb
)
863 self
.connect("delete-event", self
.on_response_cb
)
865 def create_frame(self
, label
, child
, padding
=8):
866 # need to pass an empty label, otherwise no label widget is created
867 frame
= Gtk
.Frame(label
="")
868 frame
.get_label_widget().set_markup("<b>%s</b>" % label
)
869 frame
.set_border_width(3)
870 frame
.set_shadow_type(Gtk
.ShadowType
.NONE
)
872 alignment
= Gtk
.Alignment
.new(0.5, 0, 1, 1)
873 alignment
.set_padding(padding
, padding
, padding
, padding
)
881 vbox
= self
.get_content_area()
883 self
.properties_grid
= grid
= Gtk
.Grid()
884 vbox
.pack_start(self
.create_frame("Properties", grid
), True, True, 0)
885 grid
.set_row_spacing(8)
886 grid
.set_column_spacing(8)
887 grid
.set_column_homogeneous(True)
889 name_label
= Gtk
.Label
.new_with_mnemonic("_Name")
890 name_label
.set_halign(Gtk
.Align
.START
)
891 grid
.attach(name_label
, 0, 0, 1, 1)
892 self
.entry_name
= Gtk
.Entry()
893 self
.entry_name
.set_activates_default(True)
894 self
.entry_name
.connect("changed", self
.on_entry_name_changed
)
895 name_label
.set_mnemonic_widget(self
.entry_name
)
896 grid
.attach(self
.entry_name
, 1, 0, 2, 1)
898 grid
.attach(Gtk
.Label(label
="Mode", halign
=Gtk
.Align
.START
), 0, 1, 1, 1)
899 self
.mono
= Gtk
.RadioButton
.new_with_mnemonic(None, "_Mono")
900 self
.stereo
= Gtk
.RadioButton
.new_with_mnemonic_from_widget(self
.mono
, "_Stereo")
901 grid
.attach(self
.mono
, 1, 1, 1, 1)
902 grid
.attach(self
.stereo
, 2, 1, 1, 1)
905 vbox
.pack_start(self
.create_frame("MIDI Control Changes", grid
), True, True, 0)
906 grid
.set_row_spacing(8)
907 grid
.set_column_spacing(8)
908 grid
.set_column_homogeneous(True)
910 cc_tooltip
= "{} MIDI Control Change number (0-127, set to -1 to assign next free CC #)"
911 volume_label
= Gtk
.Label
.new_with_mnemonic("_Volume")
912 volume_label
.set_halign(Gtk
.Align
.START
)
913 grid
.attach(volume_label
, 0, 0, 1, 1)
914 self
.entry_volume_cc
= Gtk
.SpinButton
.new_with_range(-1, 127, 1)
915 self
.entry_volume_cc
.set_tooltip_text(cc_tooltip
.format("Volume"))
916 volume_label
.set_mnemonic_widget(self
.entry_volume_cc
)
917 grid
.attach(self
.entry_volume_cc
, 1, 0, 1, 1)
918 self
.button_sense_midi_volume
= Gtk
.Button("Learn")
919 self
.button_sense_midi_volume
.connect("clicked", self
.on_sense_midi_volume_clicked
)
920 grid
.attach(self
.button_sense_midi_volume
, 2, 0, 1, 1)
922 balance_label
= Gtk
.Label
.new_with_mnemonic("_Balance")
923 balance_label
.set_halign(Gtk
.Align
.START
)
924 grid
.attach(balance_label
, 0, 1, 1, 1)
925 self
.entry_balance_cc
= Gtk
.SpinButton
.new_with_range(-1, 127, 1)
926 self
.entry_balance_cc
.set_tooltip_text(cc_tooltip
.format("Balance"))
927 balance_label
.set_mnemonic_widget(self
.entry_balance_cc
)
928 grid
.attach(self
.entry_balance_cc
, 1, 1, 1, 1)
929 self
.button_sense_midi_balance
= Gtk
.Button("Learn")
930 self
.button_sense_midi_balance
.connect("clicked", self
.on_sense_midi_balance_clicked
)
931 grid
.attach(self
.button_sense_midi_balance
, 2, 1, 1, 1)
933 mute_label
= Gtk
.Label
.new_with_mnemonic("M_ute")
934 mute_label
.set_halign(Gtk
.Align
.START
)
935 grid
.attach(mute_label
, 0, 2, 1, 1)
936 self
.entry_mute_cc
= Gtk
.SpinButton
.new_with_range(-1, 127, 1)
937 self
.entry_mute_cc
.set_tooltip_text(cc_tooltip
.format("Mute"))
938 mute_label
.set_mnemonic_widget(self
.entry_mute_cc
)
939 grid
.attach(self
.entry_mute_cc
, 1, 2, 1, 1)
940 self
.button_sense_midi_mute
= Gtk
.Button("Learn")
941 self
.button_sense_midi_mute
.connect("clicked", self
.on_sense_midi_mute_clicked
)
942 grid
.attach(self
.button_sense_midi_mute
, 2, 2, 1, 1)
944 if isinstance(self
, NewChannelDialog
) or (
945 self
.channel
and isinstance(self
.channel
, InputChannel
)
947 solo_label
= Gtk
.Label
.new_with_mnemonic("S_olo")
948 solo_label
.set_halign(Gtk
.Align
.START
)
949 grid
.attach(solo_label
, 0, 3, 1, 1)
950 self
.entry_solo_cc
= Gtk
.SpinButton
.new_with_range(-1, 127, 1)
951 self
.entry_solo_cc
.set_tooltip_text(cc_tooltip
.format("Solo"))
952 solo_label
.set_mnemonic_widget(self
.entry_solo_cc
)
953 grid
.attach(self
.entry_solo_cc
, 1, 3, 1, 1)
954 self
.button_sense_midi_solo
= Gtk
.Button("Learn")
955 self
.button_sense_midi_solo
.connect("clicked", self
.on_sense_midi_solo_clicked
)
956 grid
.attach(self
.button_sense_midi_solo
, 2, 3, 1, 1)
961 self
.entry_name
.set_text(self
.channel
.channel_name
)
962 if self
.channel
.channel
.is_stereo
:
963 self
.stereo
.set_active(True)
965 self
.mono
.set_active(True)
966 self
.mono
.set_sensitive(False)
967 self
.stereo
.set_sensitive(False)
968 self
.entry_volume_cc
.set_value(self
.channel
.channel
.volume_midi_cc
)
969 self
.entry_balance_cc
.set_value(self
.channel
.channel
.balance_midi_cc
)
970 self
.entry_mute_cc
.set_value(self
.channel
.channel
.mute_midi_cc
)
971 if self
.channel
and isinstance(self
.channel
, InputChannel
):
972 self
.entry_solo_cc
.set_value(self
.channel
.channel
.solo_midi_cc
)
974 def sense_popup_dialog(self
, entry
):
975 window
= Gtk
.Window
.new(Gtk
.WindowType
.TOPLEVEL
)
976 window
.set_destroy_with_parent(True)
977 window
.set_transient_for(self
)
978 window
.set_decorated(False)
979 window
.set_modal(True)
980 window
.set_position(Gtk
.WindowPosition
.CENTER_ON_PARENT
)
981 window
.set_border_width(10)
983 vbox
= Gtk
.Box(10, orientation
=Gtk
.Orientation
.VERTICAL
)
986 label
= Gtk
.Label(label
="Please move the MIDI control you want to use for this function.")
987 vbox
.pack_start(label
, True, True, 0)
988 timeout_label
= Gtk
.Label(label
="This window will close in 5 seconds")
989 vbox
.pack_start(timeout_label
, True, True, 0)
991 def close_sense_timeout(window
, entry
):
993 timeout_label
.set_text("This window will close in %d seconds." % window
.timeout
)
994 if window
.timeout
== 0:
996 entry
.set_value(self
.mixer
.last_midi_cc
)
1001 GObject
.timeout_add_seconds(1, close_sense_timeout
, window
, entry
)
1003 def on_sense_midi_volume_clicked(self
, *args
):
1004 self
.mixer
.last_midi_cc
= int(self
.entry_volume_cc
.get_value())
1005 self
.sense_popup_dialog(self
.entry_volume_cc
)
1007 def on_sense_midi_balance_clicked(self
, *args
):
1008 self
.mixer
.last_midi_cc
= int(self
.entry_balance_cc
.get_value())
1009 self
.sense_popup_dialog(self
.entry_balance_cc
)
1011 def on_sense_midi_mute_clicked(self
, *args
):
1012 self
.mixer
.last_midi_cc
= int(self
.entry_mute_cc
.get_value())
1013 self
.sense_popup_dialog(self
.entry_mute_cc
)
1015 def on_sense_midi_solo_clicked(self
, *args
):
1016 self
.mixer
.last_midi_cc
= int(self
.entry_solo_cc
.get_value())
1017 self
.sense_popup_dialog(self
.entry_solo_cc
)
1019 def on_response_cb(self
, dlg
, response_id
, *args
):
1020 name
= self
.entry_name
.get_text()
1021 if response_id
== Gtk
.ResponseType
.APPLY
:
1022 if name
!= self
.channel
.channel_name
:
1023 self
.channel
.channel_name
= name
1024 for control
in ("volume", "balance", "mute", "solo"):
1025 widget
= getattr(self
, "entry_{}_cc".format(control
), None)
1026 if widget
is not None:
1027 value
= int(widget
.get_value())
1029 setattr(self
.channel
.channel
, "{}_midi_cc".format(control
), value
)
1034 def on_entry_name_changed(self
, entry
):
1036 if len(entry
.get_text()):
1037 if self
.channel
and self
.channel
.channel
.name
== entry
.get_text():
1039 elif entry
.get_text() not in [x
.channel
.name
for x
in self
.app
.channels
] + [
1040 x
.channel
.name
for x
in self
.app
.output_channels
1043 self
.ok_button
.set_sensitive(sensitive
)
1046 class NewChannelDialog(ChannelPropertiesDialog
):
1047 def create_ui(self
):
1049 self
.add_initial_value_radio()
1050 self
.vbox
.show_all()
1052 def add_buttons(self
):
1053 self
.add_button(Gtk
.STOCK_CANCEL
, Gtk
.ResponseType
.CANCEL
)
1054 self
.ok_button
= self
.add_button(Gtk
.STOCK_ADD
, Gtk
.ResponseType
.OK
)
1055 self
.ok_button
.set_sensitive(False)
1056 self
.set_default_response(Gtk
.ResponseType
.OK
)
1058 def add_initial_value_radio(self
):
1059 grid
= self
.properties_grid
1060 grid
.attach(Gtk
.Label(label
="Value", halign
=Gtk
.Align
.START
), 0, 2, 1, 1)
1061 self
.minus_inf
= Gtk
.RadioButton
.new_with_mnemonic(None, "-_Inf")
1062 self
.zero_dB
= Gtk
.RadioButton
.new_with_mnemonic_from_widget(self
.minus_inf
, "_0dB")
1063 grid
.attach(self
.minus_inf
, 1, 2, 1, 1)
1064 grid
.attach(self
.zero_dB
, 2, 2, 1, 1)
1067 class NewInputChannelDialog(NewChannelDialog
):
1068 def __init__(self
, app
, title
="New Input Channel"):
1069 super().__init
__(app
, title
=title
)
1071 def fill_ui(self
, **values
):
1072 self
.entry_name
.set_text(values
.get("name", ""))
1073 # don't set MIDI CCs to previously used values, because they
1074 # would overwrite existing mappings, if accepted.
1075 self
.entry_volume_cc
.set_value(-1)
1076 self
.entry_balance_cc
.set_value(-1)
1077 self
.entry_mute_cc
.set_value(-1)
1078 self
.entry_solo_cc
.set_value(-1)
1079 self
.stereo
.set_active(values
.get("stereo", True))
1080 self
.minus_inf
.set_active(values
.get("value", False))
1081 self
.entry_name
.grab_focus()
1083 def get_result(self
):
1085 "name": self
.entry_name
.get_text(),
1086 "stereo": self
.stereo
.get_active(),
1087 "volume_cc": int(self
.entry_volume_cc
.get_value()),
1088 "balance_cc": int(self
.entry_balance_cc
.get_value()),
1089 "mute_cc": int(self
.entry_mute_cc
.get_value()),
1090 "solo_cc": int(self
.entry_solo_cc
.get_value()),
1091 "value": self
.minus_inf
.get_active(),
1095 class OutputChannelPropertiesDialog(ChannelPropertiesDialog
):
1096 def create_ui(self
):
1099 grid
= self
.properties_grid
1100 color_label
= Gtk
.Label
.new_with_mnemonic("_Color")
1101 color_label
.set_halign(Gtk
.Align
.START
)
1102 grid
.attach(color_label
, 0, 3, 1, 1)
1103 self
.color_chooser_button
= Gtk
.ColorButton()
1104 self
.color_chooser_button
.set_use_alpha(True)
1105 color_label
.set_mnemonic_widget(self
.color_chooser_button
)
1106 grid
.attach(self
.color_chooser_button
, 1, 3, 2, 1)
1108 vbox
= Gtk
.Box(orientation
=Gtk
.Orientation
.VERTICAL
)
1109 self
.vbox
.pack_start(self
.create_frame("Input Channels", vbox
), True, True, 0)
1111 self
.display_solo_buttons
= Gtk
.CheckButton
.new_with_mnemonic("_Display solo buttons")
1112 vbox
.pack_start(self
.display_solo_buttons
, True, True, 0)
1114 self
.vbox
.show_all()
1118 self
.display_solo_buttons
.set_active(self
.channel
.display_solo_buttons
)
1119 self
.color_chooser_button
.set_rgba(self
.channel
.color
)
1121 def on_response_cb(self
, dlg
, response_id
, *args
):
1122 if response_id
== Gtk
.ResponseType
.APPLY
:
1123 self
.channel
.display_solo_buttons
= self
.display_solo_buttons
.get_active()
1124 self
.channel
.set_color(self
.color_chooser_button
.get_rgba())
1125 for inputchannel
in self
.app
.channels
:
1126 inputchannel
.update_control_group(self
.channel
)
1128 return super().on_response_cb(dlg
, response_id
, *args
)
1131 class NewOutputChannelDialog(NewChannelDialog
, OutputChannelPropertiesDialog
):
1132 def __init__(self
, app
, title
="New Output Channel"):
1133 super().__init
__(app
, title
=title
)
1135 def fill_ui(self
, **values
):
1136 self
.entry_name
.set_text(values
.get("name", ""))
1138 # TODO: disable mode for output channels as mono output channels may
1139 # not be correctly handled yet.
1140 self
.mono
.set_sensitive(False)
1141 self
.stereo
.set_sensitive(False)
1143 # don't set MIDI CCs to previously used values, because they
1144 # would overwrite existing mappings, if accepted.
1145 self
.entry_volume_cc
.set_value(-1)
1146 self
.entry_balance_cc
.set_value(-1)
1147 self
.entry_mute_cc
.set_value(-1)
1148 self
.stereo
.set_active(values
.get("stereo", True))
1149 self
.minus_inf
.set_active(values
.get("value", False))
1150 # choose a new random color for each new output channel
1151 self
.color_chooser_button
.set_rgba(random_color())
1152 self
.display_solo_buttons
.set_active(values
.get("display_solo_buttons", False))
1153 self
.entry_name
.grab_focus()
1155 def get_result(self
):
1157 "name": self
.entry_name
.get_text(),
1158 "stereo": self
.stereo
.get_active(),
1159 "volume_cc": int(self
.entry_volume_cc
.get_value()),
1160 "balance_cc": int(self
.entry_balance_cc
.get_value()),
1161 "mute_cc": int(self
.entry_mute_cc
.get_value()),
1162 "display_solo_buttons": self
.display_solo_buttons
.get_active(),
1163 "color": self
.color_chooser_button
.get_rgba(),
1164 "value": self
.minus_inf
.get_active(),
1168 class ControlGroup(Gtk
.Alignment
):
1169 def __init__(self
, output_channel
, input_channel
):
1171 self
.set(0.5, 0.5, 1, 1)
1172 self
.output_channel
= output_channel
1173 self
.input_channel
= input_channel
1174 self
.app
= input_channel
.app
1176 self
.hbox
= Gtk
.Box(orientation
=Gtk
.Orientation
.HORIZONTAL
)
1177 self
.vbox
= Gtk
.Box(orientation
=Gtk
.Orientation
.VERTICAL
)
1179 self
.buttons_box
= Gtk
.Box(False, BUTTON_PADDING
, orientation
=Gtk
.Orientation
.HORIZONTAL
)
1181 set_background_color(self
.vbox
, output_channel
.css_name
, output_channel
.color
)
1183 self
.vbox
.pack_start(self
.hbox
, True, True, BUTTON_PADDING
)
1184 hbox_context
= self
.hbox
.get_style_context()
1185 hbox_context
.add_class("control_group")
1187 name
= output_channel
.channel
.name
1188 self
.label
= Gtk
.Label(name
)
1189 self
.label
.get_style_context().add_class("label")
1190 self
.label
.set_max_width_chars(self
.input_channel
.label_chars_narrow
)
1191 self
.label
.set_ellipsize(Pango
.EllipsizeMode
.MIDDLE
)
1192 if len(name
) > self
.input_channel
.label_chars_narrow
:
1193 self
.label
.set_tooltip_text(name
)
1194 self
.hbox
.pack_start(self
.label
, False, False, BUTTON_PADDING
)
1195 self
.hbox
.pack_end(self
.buttons_box
, False, False, BUTTON_PADDING
)
1196 mute
= Gtk
.ToggleButton("M")
1197 mute
.get_style_context().add_class("mute")
1198 mute
.set_tooltip_text("Mute output channel send")
1199 mute
.connect("toggled", self
.on_mute_toggled
)
1201 solo
= Gtk
.ToggleButton("S")
1202 solo
.get_style_context().add_class("solo")
1203 solo
.set_tooltip_text("Solo output send")
1204 solo
.connect("toggled", self
.on_solo_toggled
)
1206 pre
= Gtk
.ToggleButton("P")
1207 pre
.get_style_context().add_class("prefader")
1208 pre
.set_tooltip_text("Pre (on) / Post (off) fader send")
1209 pre
.connect("toggled", self
.on_prefader_toggled
)
1211 self
.buttons_box
.pack_start(pre
, True, True, BUTTON_PADDING
)
1212 self
.buttons_box
.pack_start(mute
, True, True, BUTTON_PADDING
)
1213 if self
.output_channel
.display_solo_buttons
:
1214 self
.buttons_box
.pack_start(solo
, True, True, BUTTON_PADDING
)
1217 if self
.output_channel
.display_solo_buttons
:
1218 if self
.solo
not in self
.buttons_box
.get_children():
1219 self
.buttons_box
.pack_start(self
.solo
, True, True, BUTTON_PADDING
)
1222 if self
.solo
in self
.buttons_box
.get_children():
1223 self
.buttons_box
.remove(self
.solo
)
1225 name
= self
.output_channel
.channel
.name
1226 self
.label
.set_text(name
)
1227 if len(name
) > self
.input_channel
.label_chars_narrow
:
1228 self
.label
.set_tooltip_text(name
)
1230 set_background_color(self
.vbox
, self
.output_channel
.css_name
, self
.output_channel
.color
)
1232 def on_mute_toggled(self
, button
):
1233 self
.output_channel
.channel
.set_muted(self
.input_channel
.channel
, button
.get_active())
1234 self
.app
.update_monitor(self
)
1236 def on_solo_toggled(self
, button
):
1237 self
.output_channel
.channel
.set_solo(self
.input_channel
.channel
, button
.get_active())
1238 self
.app
.update_monitor(self
)
1240 def on_prefader_toggled(self
, button
):
1241 self
.output_channel
.channel
.set_in_prefader(
1242 self
.input_channel
.channel
, button
.get_active()
1246 self
.hbox
.remove(self
.label
)
1247 self
.hbox
.set_child_packing(self
.buttons_box
, True, True, BUTTON_PADDING
, Gtk
.PackType
.END
)
1250 self
.hbox
.pack_start(self
.label
, False, False, BUTTON_PADDING
)
1251 self
.hbox
.set_child_packing(
1252 self
.buttons_box
, False, False, BUTTON_PADDING
, Gtk
.PackType
.END
1257 "input-channel-order-changed",
1259 GObject
.SignalFlags
.RUN_FIRST | GObject
.SignalFlags
.ACTION
,
1261 [GObject
.TYPE_STRING
, GObject
.TYPE_STRING
],
1265 "output-channel-order-changed",
1267 GObject
.SignalFlags
.RUN_FIRST | GObject
.SignalFlags
.ACTION
,
1269 [GObject
.TYPE_STRING
, GObject
.TYPE_STRING
],