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)