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.
19 from gi
.repository
import Gtk
20 from gi
.repository
import Gdk
21 from gi
.repository
import GObject
25 from serialization
import SerializedObject
35 :not(button) > label {min-width: 100px;}
39 css_provider
= Gtk
.CssProvider()
40 css_provider
.load_from_data(css
)
41 context
= Gtk
.StyleContext()
42 screen
= Gdk
.Screen
.get_default()
43 context
.add_provider_for_screen(screen
, css_provider
, Gtk
.STYLE_PROVIDER_PRIORITY_APPLICATION
)
45 class Channel(Gtk
.VBox
, SerializedObject
):
46 '''Widget with slider and meter used as base class for more specific
50 def __init__(self
, app
, name
, stereo
):
51 Gtk
.VBox
.__init
__(self
)
53 self
.mixer
= app
.mixer
54 self
.gui_factory
= app
.gui_factory
55 self
._channel
_name
= name
57 self
.meter_scale
= self
.gui_factory
.get_default_meter_scale()
58 self
.slider_scale
= self
.gui_factory
.get_default_slider_scale()
59 self
.slider_adjustment
= slider
.AdjustmentdBFS(self
.slider_scale
, 0.0, 0.02)
60 self
.balance_adjustment
= Gtk
.Adjustment(0.0, -1.0, 1.0, 0.02)
61 self
.future_out_mute
= None
62 self
.future_volume_midi_cc
= None
63 self
.future_balance_midi_cc
= None
64 self
.future_mute_midi_cc
= None
65 self
.future_solo_midi_cc
= None
67 def get_channel_name(self
):
68 return self
._channel
_name
72 post_fader_output_channel
= None
73 def set_channel_name(self
, name
):
74 self
.app
.on_channel_rename(self
._channel
_name
, name
);
75 self
._channel
_name
= name
77 self
.label_name
.set_text(name
)
79 self
.channel
.name
= name
80 if self
.post_fader_output_channel
:
81 self
.post_fader_output_channel
.name
= "%s Out" % name
;
82 channel_name
= property(get_channel_name
, set_channel_name
)
85 #print "Realizing channel \"%s\"" % self.channel_name
86 if self
.future_out_mute
!= None:
87 self
.channel
.out_mute
= self
.future_out_mute
89 self
.slider_adjustment
.connect("volume-changed", self
.on_volume_changed
)
90 self
.balance_adjustment
.connect("value-changed", self
.on_balance_changed
)
93 self
.create_slider_widget()
96 self
.meter
= meter
.StereoMeterWidget(self
.meter_scale
)
98 self
.meter
= meter
.MonoMeterWidget(self
.meter_scale
)
99 self
.on_vumeter_color_changed(self
.gui_factory
)
101 self
.meter
.set_events(Gdk
.EventMask
.SCROLL_MASK
)
103 self
.gui_factory
.connect("default-meter-scale-changed", self
.on_default_meter_scale_changed
)
104 self
.gui_factory
.connect("default-slider-scale-changed", self
.on_default_slider_scale_changed
)
105 self
.gui_factory
.connect('vumeter-color-changed', self
.on_vumeter_color_changed
)
106 self
.gui_factory
.connect('vumeter-color-scheme-changed', self
.on_vumeter_color_changed
)
107 self
.gui_factory
.connect('use-custom-widgets-changed', self
.on_custom_widgets_changed
)
109 self
.abspeak
= abspeak
.AbspeakWidget()
110 self
.abspeak
.connect("reset", self
.on_abspeak_reset
)
111 self
.abspeak
.connect("volume-adjust", self
.on_abspeak_adjust
)
113 self
.volume_digits
= Gtk
.Entry()
114 self
.volume_digits
.set_property('xalign', 0.5)
115 self
.volume_digits
.connect("key-press-event", self
.on_volume_digits_key_pressed
)
116 self
.volume_digits
.connect("focus-out-event", self
.on_volume_digits_focus_out
)
118 self
.connect("key-press-event", self
.on_key_pressed
)
119 self
.connect("scroll-event", self
.on_scroll
)
122 #print "Unrealizing channel \"%s\"" % self.channel_name
125 def balance_preferred_width(self
):
128 def _preferred_height(self
):
131 def create_balance_widget(self
):
132 if self
.gui_factory
.use_custom_widgets
and phat
:
133 self
.balance
= phat
.HFanSlider()
134 self
.balance
.set_default_value(0)
135 self
.balance
.set_adjustment(self
.balance_adjustment
)
137 self
.balance
= Gtk
.Scale()
138 self
.balance
.get_preferred_width
= self
.balance_preferred_width
139 self
.balance
.get_preferred_height
= self
._preferred
_height
140 self
.balance
.set_orientation(Gtk
.Orientation
.HORIZONTAL
)
141 self
.balance
.set_adjustment(self
.balance_adjustment
)
142 self
.balance
.set_draw_value(False)
143 self
.pack_start(self
.balance
, False, True, 0)
144 if self
.monitor_button
:
145 self
.reorder_child(self
.monitor_button
, -1)
148 def create_slider_widget(self
):
151 parent
= self
.slider
.get_parent()
152 self
.slider
.destroy()
153 if self
.gui_factory
.use_custom_widgets
:
154 self
.slider
= slider
.CustomSliderWidget(self
.slider_adjustment
)
156 self
.slider
= slider
.GtkSlider(self
.slider_adjustment
)
158 parent
.pack_start(self
.slider
, True, True, 0)
159 parent
.reorder_child(self
.slider
, 0)
162 def on_default_meter_scale_changed(self
, gui_factory
, scale
):
163 #print "Default meter scale change detected."
164 self
.meter
.set_scale(scale
)
166 def on_default_slider_scale_changed(self
, gui_factory
, scale
):
167 #print "Default slider scale change detected."
168 self
.slider_scale
= scale
169 self
.slider_adjustment
.set_scale(scale
)
170 self
.channel
.midi_scale
= self
.slider_scale
.scale
172 def on_vumeter_color_changed(self
, gui_factory
, *args
):
173 color
= gui_factory
.get_vumeter_color()
174 color_scheme
= gui_factory
.get_vumeter_color_scheme()
175 if color_scheme
!= 'solid':
176 self
.meter
.set_color(None)
178 self
.meter
.set_color(Gdk
.color_parse(color
))
180 def on_custom_widgets_changed(self
, gui_factory
, value
):
181 self
.balance
.destroy()
182 self
.create_balance_widget()
183 self
.create_slider_widget()
185 def on_abspeak_adjust(self
, abspeak
, adjust
):
186 #print "abspeak adjust %f" % adjust
187 self
.slider_adjustment
.set_value_db(self
.slider_adjustment
.get_value_db() + adjust
)
188 self
.channel
.abspeak
= None
189 #self.update_volume(False) # We want to update gui even if actual decibels have not changed (scale wrap for example)
191 def on_abspeak_reset(self
, abspeak
):
192 #print "abspeak reset"
193 self
.channel
.abspeak
= None
195 def on_volume_digits_key_pressed(self
, widget
, event
):
196 if (event
.keyval
== Gdk
.KEY_Return
or event
.keyval
== Gdk
.KEY_KP_Enter
):
197 db_text
= self
.volume_digits
.get_text()
200 #print "Volume digits confirmation \"%f dBFS\"" % db
201 except (ValueError) as e
:
202 #print "Volume digits confirmation ignore, reset to current"
203 self
.update_volume(False)
205 self
.slider_adjustment
.set_value_db(db
)
207 #self.update_volume(False) # We want to update gui even if actual decibels have not changed (scale wrap for example)
209 def on_volume_digits_focus_out(self
, widget
, event
):
210 #print "volume digits focus out detected"
211 self
.update_volume(False)
213 def read_meter(self
):
217 meter_left
, meter_right
= self
.channel
.meter
218 self
.meter
.set_values(meter_left
, meter_right
)
220 self
.meter
.set_value(self
.channel
.meter
[0])
222 self
.abspeak
.set_peak(self
.channel
.abspeak
)
224 def on_scroll(self
, widget
, event
):
225 if event
.direction
== Gdk
.ScrollDirection
.DOWN
:
226 self
.slider_adjustment
.step_down()
227 elif event
.direction
== Gdk
.ScrollDirection
.UP
:
228 self
.slider_adjustment
.step_up()
231 def update_volume(self
, update_engine
):
232 db
= self
.slider_adjustment
.get_value_db()
234 db_text
= "%.2f" % db
235 self
.volume_digits
.set_text(db_text
)
238 self
.channel
.volume
= db
239 self
.app
.update_monitor(self
)
241 def on_volume_changed(self
, adjustment
):
242 self
.update_volume(True)
244 def on_balance_changed(self
, adjustment
):
245 balance
= self
.balance_adjustment
.get_value()
246 #print "%s balance: %f" % (self.channel_name, balance)
247 self
.channel
.balance
= balance
248 self
.app
.update_monitor(self
)
250 def on_key_pressed(self
, widget
, event
):
251 if (event
.keyval
== Gdk
.KEY_Up
):
252 #print self.channel_name + " Up"
253 self
.slider_adjustment
.step_up()
255 elif (event
.keyval
== Gdk
.KEY_Down
):
256 #print self.channel_name + " Down"
257 self
.slider_adjustment
.step_down()
262 def serialize(self
, object_backend
):
263 object_backend
.add_property("volume", "%f" % self
.slider_adjustment
.get_value_db())
264 object_backend
.add_property("balance", "%f" % self
.balance_adjustment
.get_value())
266 if hasattr(self
.channel
, 'out_mute'):
267 object_backend
.add_property('out_mute', str(self
.channel
.out_mute
))
268 if self
.channel
.volume_midi_cc
!= -1:
269 object_backend
.add_property('volume_midi_cc', str(self
.channel
.volume_midi_cc
))
270 if self
.channel
.balance_midi_cc
!= -1:
271 object_backend
.add_property('balance_midi_cc', str(self
.channel
.balance_midi_cc
))
272 if self
.channel
.mute_midi_cc
!= -1:
273 object_backend
.add_property('mute_midi_cc', str(self
.channel
.mute_midi_cc
))
274 if self
.channel
.solo_midi_cc
!= -1:
275 object_backend
.add_property('solo_midi_cc', str(self
.channel
.solo_midi_cc
))
278 def unserialize_property(self
, name
, value
):
280 self
.slider_adjustment
.set_value_db(float(value
))
282 if name
== "balance":
283 self
.balance_adjustment
.set_value(float(value
))
285 if name
== 'out_mute':
286 self
.future_out_mute
= (value
== 'True')
288 if name
== 'volume_midi_cc':
289 self
.future_volume_midi_cc
= int(value
)
291 if name
== 'balance_midi_cc':
292 self
.future_balance_midi_cc
= int(value
)
294 if name
== 'mute_midi_cc':
295 self
.future_mute_midi_cc
= int(value
)
297 if name
== 'solo_midi_cc':
298 self
.future_solo_midi_cc
= int(value
)
302 def on_midi_event_received(self
, *args
):
303 self
.slider_adjustment
.set_value_db(self
.channel
.volume
)
304 self
.balance_adjustment
.set_value(self
.channel
.balance
)
306 def on_monitor_button_toggled(self
, button
):
307 if button
.get_active():
308 for channel
in self
.app
.channels
+ self
.app
.output_channels
:
309 if channel
.monitor_button
.get_active() and channel
.monitor_button
is not button
:
310 channel
.monitor_button
.handler_block_by_func(
311 channel
.on_monitor_button_toggled
)
312 channel
.monitor_button
.set_active(False)
313 channel
.monitor_button
.handler_unblock_by_func(
314 channel
.on_monitor_button_toggled
)
315 self
.app
.set_monitored_channel(self
)
317 def set_monitored(self
):
319 self
.app
.set_monitored_channel(self
)
320 self
.monitor_button
.set_active(True)
322 class InputChannel(Channel
):
323 post_fader_output_channel
= None
325 def __init__(self
, app
, name
, stereo
):
326 Channel
.__init
__(self
, app
, name
, stereo
)
329 self
.channel
= self
.mixer
.add_channel(self
.channel_name
, self
.stereo
)
331 if self
.channel
== None:
332 raise Exception("Cannot create a channel")
333 Channel
.realize(self
)
334 if self
.future_volume_midi_cc
!= None:
335 self
.channel
.volume_midi_cc
= self
.future_volume_midi_cc
336 if self
.future_balance_midi_cc
!= None:
337 self
.channel
.balance_midi_cc
= self
.future_balance_midi_cc
338 if self
.future_mute_midi_cc
!= None:
339 self
.channel
.mute_midi_cc
= self
.future_mute_midi_cc
340 if self
.future_solo_midi_cc
!= None:
341 self
.channel
.solo_midi_cc
= self
.future_solo_midi_cc
342 if self
.app
._init
_solo
_channels
and self
.channel_name
in self
.app
._init
_solo
_channels
:
343 self
.channel
.solo
= True
345 self
.channel
.midi_scale
= self
.slider_scale
.scale
347 self
.on_volume_changed(self
.slider_adjustment
)
348 self
.on_balance_changed(self
.balance_adjustment
)
350 # vbox child at upper part
351 self
.vbox
= Gtk
.VBox()
352 self
.pack_start(self
.vbox
, False, True, 0)
353 self
.label_name
= Gtk
.Label()
354 self
.label_name
.set_text(self
.channel_name
)
355 self
.label_name
.set_width_chars(0)
356 self
.label_name_event_box
= Gtk
.EventBox()
357 self
.label_name_event_box
.connect("button-press-event", self
.on_label_mouse
)
358 self
.label_name_event_box
.add(self
.label_name
)
359 self
.vbox
.pack_start(self
.label_name_event_box
, True, True, 0)
360 # self.label_stereo = Gtk.Label()
362 # self.label_stereo.set_text("stereo")
364 # self.label_stereo.set_text("mono")
365 # self.label_stereo.set_size_request(0, -1)
366 # self.vbox.pack_start(self.label_stereo, True)
368 self
.hbox_mutesolo
= Gtk
.HBox()
369 vbox_mutesolo
= Gtk
.VBox()
370 vbox_mutesolo
.pack_start(self
.hbox_mutesolo
, True, True, button_padding
)
371 self
.vbox
.pack_start(vbox_mutesolo
, True, True, 0)
373 self
.mute
= Gtk
.ToggleButton()
374 self
.mute
.set_label("M")
375 self
.mute
.set_name("mute")
376 self
.mute
.set_active(self
.channel
.out_mute
)
377 self
.mute
.connect("toggled", self
.on_mute_toggled
)
378 self
.hbox_mutesolo
.pack_start(self
.mute
, True, True, button_padding
)
380 self
.solo
= Gtk
.ToggleButton()
381 self
.solo
.set_label("S")
382 self
.solo
.set_name("solo")
383 self
.solo
.set_active(self
.channel
.solo
)
384 self
.solo
.connect("toggled", self
.on_solo_toggled
)
385 self
.hbox_mutesolo
.pack_start(self
.solo
, True, True, button_padding
)
387 self
.vbox
.pack_start(self
.hbox_mutesolo
, True, True, 0)
390 frame
.set_shadow_type(Gtk
.ShadowType
.IN
)
391 frame
.add(self
.abspeak
);
392 self
.pack_start(frame
, False, True, 0)
394 # hbox child at lower part
395 self
.hbox
= Gtk
.HBox()
396 self
.hbox
.pack_start(self
.slider
, True, True, 0)
398 frame
.set_shadow_type(Gtk
.ShadowType
.IN
)
399 frame
.add(self
.meter
);
400 self
.hbox
.pack_start(frame
, True, True, 0)
402 frame
.set_shadow_type(Gtk
.ShadowType
.IN
)
403 frame
.add(self
.hbox
);
404 self
.pack_start(frame
, True, True, 0)
406 self
.volume_digits
.set_width_chars(6)
407 self
.pack_start(self
.volume_digits
, False, False, 0)
409 self
.create_balance_widget()
411 self
.monitor_button
= Gtk
.ToggleButton('MON')
412 self
.monitor_button
.connect('toggled', self
.on_monitor_button_toggled
)
413 self
.pack_start(self
.monitor_button
, False, False, 0)
415 def add_control_group(self
, channel
):
416 control_group
= ControlGroup(channel
, self
)
417 control_group
.show_all()
418 self
.vbox
.pack_start(control_group
, True, True, 0)
421 def remove_control_group(self
, channel
):
422 ctlgroup
= self
.get_control_group(channel
)
423 self
.vbox
.remove(ctlgroup
)
425 def update_control_group(self
, channel
):
426 for control_group
in self
.vbox
.get_children():
427 if isinstance(control_group
, ControlGroup
):
428 if control_group
.output_channel
is channel
:
429 control_group
.update()
431 def get_control_group(self
, channel
):
432 for control_group
in self
.vbox
.get_children():
433 if isinstance(control_group
, ControlGroup
):
434 if control_group
.output_channel
is channel
:
439 Channel
.unrealize(self
)
440 if self
.post_fader_output_channel
:
441 self
.post_fader_output_channel
.remove()
442 self
.post_fader_output_channel
= None
443 self
.channel
.remove()
446 channel_properties_dialog
= None
447 def on_channel_properties(self
):
448 if not self
.channel_properties_dialog
:
449 self
.channel_properties_dialog
= ChannelPropertiesDialog(self
, self
.app
)
450 self
.channel_properties_dialog
.show()
451 self
.channel_properties_dialog
.present()
453 def on_label_mouse(self
, widget
, event
):
454 if event
.type == Gdk
.EventType
._2BUTTON
_PRESS
:
455 if event
.button
== 1:
456 self
.on_channel_properties()
458 def on_mute_toggled(self
, button
):
459 self
.channel
.out_mute
= self
.mute
.get_active()
461 def on_solo_toggled(self
, button
):
462 self
.channel
.solo
= self
.solo
.get_active()
464 def midi_events_check(self
):
465 if hasattr(self
, 'channel') and self
.channel
.midi_in_got_events
:
466 self
.mute
.set_active(self
.channel
.out_mute
)
467 self
.solo
.set_active(self
.channel
.solo
)
468 Channel
.on_midi_event_received(self
)
470 def on_solo_button_pressed(self
, button
, event
, *args
):
471 if event
.button
== 3:
472 # right click on the solo button, act on all output channels
473 if button
.get_active(): # was soloed
474 button
.set_active(False)
475 if hasattr(button
, 'touched_channels'):
476 touched_channels
= button
.touched_channels
477 for chan
in touched_channels
:
478 ctlgroup
= self
.get_control_group(chan
)
479 ctlgroup
.solo
.set_active(False)
480 del button
.touched_channels
481 else: # was not soloed
482 button
.set_active(True)
483 touched_channels
= []
484 for chan
in self
.app
.output_channels
:
485 ctlgroup
= self
.get_control_group(chan
)
486 if not ctlgroup
.solo
.get_active():
487 ctlgroup
.solo
.set_active(True)
488 touched_channels
.append(chan
)
489 button
.touched_channels
= touched_channels
494 def serialization_name(cls
):
495 return 'input_channel'
497 def serialize(self
, object_backend
):
498 object_backend
.add_property("name", self
.channel_name
)
500 object_backend
.add_property("type", "stereo")
502 object_backend
.add_property("type", "mono")
503 Channel
.serialize(self
, object_backend
)
505 def unserialize_property(self
, name
, value
):
507 self
.channel_name
= str(value
)
510 if value
== "stereo":
516 return Channel
.unserialize_property(self
, name
, value
)
519 available_colours
= [
520 ('#648fcb', '#204c98', '#426cb8'),
521 ('#984a9a', '#542656', '#744676'),
522 ('#7f9abb', '#3f5677', '#5f7697'),
523 ('#bf8f9f', '#7b4d5b', '#9b6f7b'),
524 ('#ba6d89', '#762945', '#964965'),
525 ('#4c9196', '#0c5156', '#2c7176'),
526 ('#56a2c0', '#166280', '#3682a0'),
529 class OutputChannel(Channel
):
530 colours
= available_colours
[:]
531 _display_solo_buttons
= False
533 _init_muted_channels
= None
534 _init_solo_channels
= None
536 def __init__(self
, app
, name
, stereo
):
537 Channel
.__init
__(self
, app
, name
, stereo
)
539 def get_display_solo_buttons(self
):
540 return self
._display
_solo
_buttons
542 def set_display_solo_buttons(self
, value
):
543 self
._display
_solo
_buttons
= value
544 # notifying control groups
545 for inputchannel
in self
.app
.channels
:
546 inputchannel
.update_control_group(self
)
548 display_solo_buttons
= property(get_display_solo_buttons
, set_display_solo_buttons
)
551 self
.channel
= self
.mixer
.add_output_channel(self
.channel_name
, self
.stereo
)
552 if self
.channel
== None:
553 raise Exception("Cannot create a channel")
554 Channel
.realize(self
)
555 if self
.future_volume_midi_cc
!= None:
556 self
.channel
.volume_midi_cc
= self
.future_volume_midi_cc
557 if self
.future_balance_midi_cc
!= None:
558 self
.channel
.balance_midi_cc
= self
.future_balance_midi_cc
559 if self
.future_mute_midi_cc
!= None:
560 self
.channel
.mute_midi_cc
= self
.future_mute_midi_cc
561 self
.channel
.midi_scale
= self
.slider_scale
.scale
563 self
.on_volume_changed(self
.slider_adjustment
)
564 self
.on_balance_changed(self
.balance_adjustment
)
566 # vbox child at upper part
567 self
.vbox
= Gtk
.VBox()
568 self
.pack_start(self
.vbox
, False, True, 0)
569 self
.label_name
= Gtk
.Label()
570 self
.label_name
.set_text(self
.channel_name
)
571 self
.label_name
.set_width_chars(0)
572 self
.label_name_event_box
= Gtk
.EventBox()
573 self
.label_name_event_box
.connect('button-press-event', self
.on_label_mouse
)
574 self
.label_name_event_box
.add(self
.label_name
)
576 OutputChannel
.colours
= available_colours
[:]
577 for color
in self
.colours
:
578 self
.color_tuple
= [Gdk
.color_parse(color
[x
]) for x
in range(3)]
579 self
.colours
.remove(color
)
581 self
.label_name_event_box
.modify_bg(Gtk
.StateType
.NORMAL
, self
.color_tuple
[1])
582 self
.vbox
.pack_start(self
.label_name_event_box
, True, True, 0)
583 self
.mute
= Gtk
.ToggleButton()
584 self
.mute
.set_label("M")
585 self
.mute
.set_name("mute")
586 self
.mute
.set_active(self
.channel
.out_mute
)
587 self
.mute
.connect("toggled", self
.on_mute_toggled
)
589 hbox
.pack_start(self
.mute
, True, True, button_padding
)
590 self
.vbox
.pack_start(hbox
, True, True, button_padding
)
593 frame
.set_shadow_type(Gtk
.ShadowType
.IN
)
594 frame
.add(self
.abspeak
);
595 self
.vbox
.pack_start(frame
, False, True, 0)
597 # hbox child at lower part
598 self
.hbox
= Gtk
.HBox()
599 self
.hbox
.pack_start(self
.slider
, True, True, 0)
601 frame
.set_shadow_type(Gtk
.ShadowType
.IN
)
602 frame
.add(self
.meter
);
603 self
.hbox
.pack_start(frame
, True, True, 0)
605 frame
.set_shadow_type(Gtk
.ShadowType
.IN
)
606 frame
.add(self
.hbox
);
607 self
.pack_start(frame
, True, True, 0)
609 self
.volume_digits
.set_width_chars(6)
610 self
.pack_start(self
.volume_digits
, False, True, 0)
612 self
.create_balance_widget()
614 self
.monitor_button
= Gtk
.ToggleButton('MON')
615 self
.monitor_button
.connect('toggled', self
.on_monitor_button_toggled
)
616 self
.pack_start(self
.monitor_button
, False, False, 0)
618 # add control groups to the input channels, and initialize them
620 for input_channel
in self
.app
.channels
:
621 ctlgroup
= input_channel
.add_control_group(self
)
622 if self
._init
_muted
_channels
and input_channel
.channel
.name
in self
._init
_muted
_channels
:
623 ctlgroup
.mute
.set_active(True)
624 if self
._init
_solo
_channels
and input_channel
.channel
.name
in self
._init
_solo
_channels
:
625 ctlgroup
.solo
.set_active(True)
626 self
._init
_muted
_channels
= None
627 self
._init
_solo
_channels
= None
629 channel_properties_dialog
= None
630 def on_channel_properties(self
):
631 if not self
.channel_properties_dialog
:
632 self
.channel_properties_dialog
= OutputChannelPropertiesDialog(self
, self
.app
)
633 self
.channel_properties_dialog
.show()
634 self
.channel_properties_dialog
.present()
636 def on_label_mouse(self
, widget
, event
):
637 if event
.type == Gdk
.EventType
._2BUTTON
_PRESS
:
638 if event
.button
== 1:
639 self
.on_channel_properties()
641 def on_mute_toggled(self
, button
):
642 self
.channel
.out_mute
= self
.mute
.get_active()
644 def midi_events_check(self
):
645 if self
.channel
!= None and self
.channel
.midi_in_got_events
:
646 self
.mute
.set_active(self
.channel
.out_mute
)
647 Channel
.on_midi_event_received(self
)
650 # remove control groups from input channels
651 for input_channel
in self
.app
.channels
:
652 input_channel
.remove_control_group(self
)
654 Channel
.unrealize(self
)
655 self
.channel
.remove()
659 def serialization_name(cls
):
660 return 'output_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 if self
.display_solo_buttons
:
669 object_backend
.add_property("solo_buttons", "true")
672 for input_channel
in self
.app
.channels
:
673 if self
.channel
.is_muted(input_channel
.channel
):
674 muted_channels
.append(input_channel
)
675 if self
.channel
.is_solo(input_channel
.channel
):
676 solo_channels
.append(input_channel
)
678 object_backend
.add_property('muted_channels', '|'.join([x
.channel
.name
for x
in muted_channels
]))
680 object_backend
.add_property('solo_channels', '|'.join([x
.channel
.name
for x
in solo_channels
]))
681 Channel
.serialize(self
, object_backend
)
683 def unserialize_property(self
, name
, value
):
685 self
.channel_name
= str(value
)
688 if value
== "stereo":
694 if name
== "solo_buttons":
696 self
.display_solo_buttons
= True
698 if name
== 'muted_channels':
699 self
._init
_muted
_channels
= value
.split('|')
701 if name
== 'solo_channels':
702 self
._init
_solo
_channels
= value
.split('|')
704 return Channel
.unserialize_property(self
, name
, value
)
706 class ChannelPropertiesDialog(Gtk
.Dialog
):
709 def __init__(self
, parent
, app
):
710 self
.channel
= parent
712 self
.mixer
= self
.channel
.mixer
713 Gtk
.Dialog
.__init
__(self
, 'Channel "%s" Properties' % self
.channel
.channel_name
, app
.window
)
715 self
.add_button(Gtk
.STOCK_CANCEL
, Gtk
.ResponseType
.CANCEL
)
716 self
.ok_button
= self
.add_button(Gtk
.STOCK_APPLY
, Gtk
.ResponseType
.APPLY
)
717 self
.set_default_response(Gtk
.ResponseType
.APPLY
);
722 self
.connect('response', self
.on_response_cb
)
723 self
.connect('delete-event', self
.on_response_cb
)
725 def create_frame(self
, label
, child
):
728 frame
.set_border_width(3)
729 #frame.set_shadow_type(Gtk.ShadowType.NONE)
730 frame
.get_label_widget().set_markup('<b>%s</b>' % label
)
732 alignment
= Gtk
.Alignment
.new(0, 0, 1, 1)
733 alignment
.set_padding(0, 0, 12, 0)
743 table
= Gtk
.Table(2, 3, False)
744 vbox
.pack_start(self
.create_frame('Properties', table
), True, True, 0)
745 table
.set_row_spacings(5)
746 table
.set_col_spacings(5)
748 table
.attach(Gtk
.Label(label
='Name'), 0, 1, 0, 1)
749 self
.entry_name
= Gtk
.Entry()
750 self
.entry_name
.set_activates_default(True)
751 self
.entry_name
.connect('changed', self
.on_entry_name_changed
)
752 table
.attach(self
.entry_name
, 1, 2, 0, 1)
754 table
.attach(Gtk
.Label(label
='Mode'), 0, 1, 1, 2)
755 self
.mode_hbox
= Gtk
.HBox()
756 table
.attach(self
.mode_hbox
, 1, 2, 1, 2)
757 self
.mono
= Gtk
.RadioButton(label
='Mono')
758 self
.stereo
= Gtk
.RadioButton(label
='Stereo', group
=self
.mono
)
759 self
.mode_hbox
.pack_start(self
.mono
, True, True, 0)
760 self
.mode_hbox
.pack_start(self
.stereo
, True, True, 0)
762 table
= Gtk
.Table(2, 3, False)
763 vbox
.pack_start(self
.create_frame('MIDI Control Channels', table
), True, True, 0)
764 table
.set_row_spacings(5)
765 table
.set_col_spacings(5)
767 table
.attach(Gtk
.Label(label
='Volume'), 0, 1, 0, 1)
768 self
.entry_volume_cc
= Gtk
.Entry()
769 self
.entry_volume_cc
.set_activates_default(True)
770 self
.entry_volume_cc
.set_editable(False)
771 self
.entry_volume_cc
.set_width_chars(3)
772 table
.attach(self
.entry_volume_cc
, 1, 2, 0, 1)
773 self
.button_sense_midi_volume
= Gtk
.Button('Autoset')
774 self
.button_sense_midi_volume
.connect('clicked',
775 self
.on_sense_midi_volume_clicked
)
776 table
.attach(self
.button_sense_midi_volume
, 2, 3, 0, 1)
778 table
.attach(Gtk
.Label(label
='Balance'), 0, 1, 1, 2)
779 self
.entry_balance_cc
= Gtk
.Entry()
780 self
.entry_balance_cc
.set_activates_default(True)
781 self
.entry_balance_cc
.set_width_chars(3)
782 self
.entry_balance_cc
.set_editable(False)
783 table
.attach(self
.entry_balance_cc
, 1, 2, 1, 2)
784 self
.button_sense_midi_balance
= Gtk
.Button('Autoset')
785 self
.button_sense_midi_balance
.connect('clicked',
786 self
.on_sense_midi_balance_clicked
)
787 table
.attach(self
.button_sense_midi_balance
, 2, 3, 1, 2)
789 table
.attach(Gtk
.Label(label
='Mute'), 0, 1, 2, 3)
790 self
.entry_mute_cc
= Gtk
.Entry()
791 self
.entry_mute_cc
.set_activates_default(True)
792 self
.entry_mute_cc
.set_editable(False)
793 self
.entry_mute_cc
.set_width_chars(3)
794 table
.attach(self
.entry_mute_cc
, 1, 2, 2, 3)
795 self
.button_sense_midi_mute
= Gtk
.Button('Autoset')
796 self
.button_sense_midi_mute
.connect('clicked',
797 self
.on_sense_midi_mute_clicked
)
798 table
.attach(self
.button_sense_midi_mute
, 2, 3, 2, 3)
800 if (isinstance(self
, NewChannelDialog
) or (self
.channel
and
801 isinstance(self
.channel
, InputChannel
))):
802 table
.attach(Gtk
.Label(label
='Solo'), 0, 1, 3, 4)
803 self
.entry_solo_cc
= Gtk
.Entry()
804 self
.entry_solo_cc
.set_activates_default(True)
805 self
.entry_solo_cc
.set_editable(False)
806 self
.entry_solo_cc
.set_width_chars(3)
807 table
.attach(self
.entry_solo_cc
, 1, 2, 3, 4)
808 self
.button_sense_midi_solo
= Gtk
.Button('Autoset')
809 self
.button_sense_midi_solo
.connect('clicked',
810 self
.on_sense_midi_solo_clicked
)
811 table
.attach(self
.button_sense_midi_solo
, 2, 3, 3, 4)
816 self
.entry_name
.set_text(self
.channel
.channel_name
)
817 if self
.channel
.channel
.is_stereo
:
818 self
.stereo
.set_active(True)
820 self
.mono
.set_active(True)
821 self
.mode_hbox
.set_sensitive(False)
822 self
.entry_volume_cc
.set_text('%s' % self
.channel
.channel
.volume_midi_cc
)
823 self
.entry_balance_cc
.set_text('%s' % self
.channel
.channel
.balance_midi_cc
)
824 self
.entry_mute_cc
.set_text('%s' % self
.channel
.channel
.mute_midi_cc
)
825 if (self
.channel
and isinstance(self
.channel
, InputChannel
)):
826 self
.entry_solo_cc
.set_text('%s' % self
.channel
.channel
.solo_midi_cc
)
828 def sense_popup_dialog(self
, entry
):
829 window
= Gtk
.Window
.new(Gtk
.WindowType
.TOPLEVEL
)
830 window
.set_destroy_with_parent(True)
831 window
.set_transient_for(self
)
832 window
.set_decorated(False)
833 window
.set_modal(True)
834 window
.set_position(Gtk
.WindowPosition
.CENTER_ON_PARENT
)
835 window
.set_border_width(10)
840 vbox
.pack_start(Gtk
.Label(label
='Please move the MIDI control you want to use for this function.'), True, True, 0)
841 timeout_label
= Gtk
.Label(label
='This window will close in 5 seconds')
842 vbox
.pack_start(timeout_label
, True, True, 0)
843 def close_sense_timeout(window
, entry
):
845 timeout_label
.set_text('This window will close in %d seconds.' % window
.timeout
)
846 if window
.timeout
== 0:
848 entry
.set_text('%s' % self
.mixer
.last_midi_channel
)
852 GObject
.timeout_add_seconds(1, close_sense_timeout
, window
, entry
)
854 def on_sense_midi_volume_clicked(self
, *args
):
855 self
.mixer
.last_midi_channel
= int(self
.entry_volume_cc
.get_text())
856 self
.sense_popup_dialog(self
.entry_volume_cc
)
858 def on_sense_midi_balance_clicked(self
, *args
):
859 self
.mixer
.last_midi_channel
= int(self
.entry_balance_cc
.get_text())
860 self
.sense_popup_dialog(self
.entry_balance_cc
)
862 def on_sense_midi_mute_clicked(self
, *args
):
863 self
.mixer
.last_midi_channel
= int(self
.entry_mute_cc
.get_text())
864 self
.sense_popup_dialog(self
.entry_mute_cc
)
866 def on_sense_midi_solo_clicked(self
, *args
):
867 self
.mixer
.last_midi_channel
= int(self
.entry_solo_cc
.get_text())
868 self
.sense_popup_dialog(self
.entry_solo_cc
)
870 def on_response_cb(self
, dlg
, response_id
, *args
):
871 self
.channel
.channel_properties_dialog
= None
872 name
= self
.entry_name
.get_text()
873 if response_id
== Gtk
.ResponseType
.APPLY
:
874 self
.channel
.channel_name
= name
876 if self
.entry_volume_cc
.get_text() != '-1':
877 self
.channel
.channel
.volume_midi_cc
= int(self
.entry_volume_cc
.get_text())
881 if self
.entry_balance_cc
.get_text() != '-1':
882 self
.channel
.channel
.balance_midi_cc
= int(self
.entry_balance_cc
.get_text())
886 if self
.entry_mute_cc
.get_text() != '-1':
887 self
.channel
.channel
.mute_midi_cc
= int(self
.entry_mute_cc
.get_text())
891 if hasattr(self
, 'entry_solo_cc') and self
.entry_solo_cc
.get_text() != '-1':
892 self
.channel
.channel
.solo_midi_cc
= int(self
.entry_solo_cc
.get_text())
897 def on_entry_name_changed(self
, entry
):
899 if len(entry
.get_text()):
900 if self
.channel
and self
.channel
.channel
.name
== entry
.get_text():
902 elif entry
.get_text() not in [x
.channel
.name
for x
in self
.app
.channels
] + \
903 [x
.channel
.name
for x
in self
.app
.output_channels
] + ['MAIN']:
905 self
.ok_button
.set_sensitive(sensitive
)
908 class NewChannelDialog(ChannelPropertiesDialog
):
909 def __init__(self
, app
):
910 Gtk
.Dialog
.__init
__(self
, 'New Channel', app
.window
)
911 self
.mixer
= app
.mixer
916 self
.stereo
.set_active(True) # default to stereo
918 self
.add_button(Gtk
.STOCK_CANCEL
, Gtk
.ResponseType
.CANCEL
)
919 self
.ok_button
= self
.add_button(Gtk
.STOCK_ADD
, Gtk
.ResponseType
.OK
)
920 self
.ok_button
.set_sensitive(False)
921 self
.set_default_response(Gtk
.ResponseType
.OK
);
924 self
.entry_volume_cc
.set_text('-1')
925 self
.entry_balance_cc
.set_text('-1')
926 self
.entry_mute_cc
.set_text('-1')
927 self
.entry_solo_cc
.set_text('-1')
929 def get_result(self
):
930 return {'name': self
.entry_name
.get_text(),
931 'stereo': self
.stereo
.get_active(),
932 'volume_cc': self
.entry_volume_cc
.get_text(),
933 'balance_cc': self
.entry_balance_cc
.get_text(),
934 'mute_cc': self
.entry_mute_cc
.get_text(),
935 'solo_cc': self
.entry_solo_cc
.get_text()
938 class OutputChannelPropertiesDialog(ChannelPropertiesDialog
):
940 ChannelPropertiesDialog
.create_ui(self
)
943 self
.vbox
.pack_start(self
.create_frame('Input Channels', vbox
), True, True, 0)
945 self
.display_solo_buttons
= Gtk
.CheckButton('Display solo buttons')
946 vbox
.pack_start(self
.display_solo_buttons
, True, True, 0)
951 ChannelPropertiesDialog
.fill_ui(self
)
952 self
.display_solo_buttons
.set_active(self
.channel
.display_solo_buttons
)
954 def on_response_cb(self
, dlg
, response_id
, *args
):
955 if response_id
== Gtk
.ResponseType
.APPLY
:
956 self
.channel
.display_solo_buttons
= self
.display_solo_buttons
.get_active()
957 ChannelPropertiesDialog
.on_response_cb(self
, dlg
, response_id
, *args
)
960 class NewOutputChannelDialog(OutputChannelPropertiesDialog
):
961 def __init__(self
, app
):
962 Gtk
.Dialog
.__init
__(self
, 'New Output Channel', app
.window
)
963 self
.mixer
= app
.mixer
968 # TODO: disable mode for output channels as mono output channels may
969 # not be correctly handled yet.
970 self
.mode_hbox
.set_sensitive(False)
971 self
.stereo
.set_active(True) # default to stereo
973 self
.add_button(Gtk
.STOCK_CANCEL
, Gtk
.ResponseType
.CANCEL
)
974 self
.ok_button
= self
.add_button(Gtk
.STOCK_ADD
, Gtk
.ResponseType
.OK
)
975 self
.ok_button
.set_sensitive(False)
976 self
.set_default_response(Gtk
.ResponseType
.OK
);
979 self
.entry_volume_cc
.set_text('-1')
980 self
.entry_balance_cc
.set_text('-1')
981 self
.entry_mute_cc
.set_text('-1')
983 def get_result(self
):
984 return {'name': self
.entry_name
.get_text(),
985 'stereo': self
.stereo
.get_active(),
986 'volume_cc': self
.entry_volume_cc
.get_text(),
987 'balance_cc': self
.entry_balance_cc
.get_text(),
988 'mute_cc': self
.entry_mute_cc
.get_text(),
989 'display_solo_buttons': self
.display_solo_buttons
.get_active(),
992 class ControlGroup(Gtk
.Alignment
):
993 def __init__(self
, output_channel
, input_channel
):
994 GObject
.GObject
.__init
__(self
)
995 self
.set(0.5, 0.5, 1, 1)
996 self
.output_channel
= output_channel
997 self
.input_channel
= input_channel
998 self
.app
= input_channel
.app
1003 vbox
.pack_start(hbox
, True, True, button_padding
)
1006 vbox
.modify_bg(Gtk
.StateType
.NORMAL
, output_channel
.color_tuple
[1])
1007 mute_name
= "%s_mute" % output_channel
.channel
.name
1008 mute
= Gtk
.ToggleButton()
1010 mute
.set_name("mute")
1011 mute
.connect("toggled", self
.on_mute_toggled
)
1013 hbox
.pack_start(mute
, True, True, button_padding
)
1014 solo
= Gtk
.ToggleButton()
1015 solo
.set_name("solo")
1017 solo
.connect("toggled", self
.on_solo_toggled
)
1020 if self
.output_channel
.display_solo_buttons
:
1021 hbox
.pack_start(solo
, True, True, button_padding
)
1024 if self
.output_channel
.display_solo_buttons
:
1025 if not self
.solo
in self
.hbox
.get_children():
1026 self
.hbox
.pack_start(self
.solo
, True, True, 0)
1029 if self
.solo
in self
.hbox
.get_children():
1030 self
.hbox
.remove(self
.solo
)
1032 def on_mute_toggled(self
, button
):
1033 self
.output_channel
.channel
.set_muted(self
.input_channel
.channel
, button
.get_active())
1034 self
.app
.update_monitor(self
)
1036 def on_solo_toggled(self
, button
):
1037 self
.output_channel
.channel
.set_solo(self
.input_channel
.channel
, button
.get_active())
1038 self
.app
.update_monitor(self
)