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
33 class Channel(Gtk.VBox, SerializedObject):
34 '''Widget with slider and meter used as base class for more specific
38 def __init__(self, app, name, stereo):
39 Gtk.VBox.__init__(self)
41 self.mixer = app.mixer
42 self.gui_factory = app.gui_factory
43 self._channel_name = name
45 self.meter_scale = self.gui_factory.get_default_meter_scale()
46 self.slider_scale = self.gui_factory.get_default_slider_scale()
47 self.slider_adjustment = slider.AdjustmentdBFS(self.slider_scale, 0.0)
48 self.balance_adjustment = Gtk.Adjustment(0.0, -1.0, 1.0, 0.02)
49 self.future_out_mute = None
50 self.future_volume_midi_cc = None
51 self.future_balance_midi_cc = None
52 self.future_mute_midi_cc = None
53 self.future_solo_midi_cc = None
55 def get_channel_name(self):
56 return self._channel_name
60 post_fader_output_channel = None
61 def set_channel_name(self, name):
62 self.app.on_channel_rename(self._channel_name, name);
63 self._channel_name = name
65 self.label_name.set_text(name)
67 self.channel.name = name
68 if self.post_fader_output_channel:
69 self.post_fader_output_channel.name = "%s Out" % name;
70 channel_name = property(get_channel_name, set_channel_name)
73 #print "Realizing channel \"%s\"" % self.channel_name
74 if self.future_out_mute != None:
75 self.channel.out_mute = self.future_out_mute
77 self.slider_adjustment.connect("volume-changed", self.on_volume_changed)
78 self.balance_adjustment.connect("value-changed", self.on_balance_changed)
81 self.create_slider_widget()
84 self.meter = meter.StereoMeterWidget(self.meter_scale)
86 self.meter = meter.MonoMeterWidget(self.meter_scale)
87 self.on_vumeter_color_changed(self.gui_factory)
89 self.meter.set_events(Gdk.EventMask.SCROLL_MASK)
91 self.gui_factory.connect("default-meter-scale-changed", self.on_default_meter_scale_changed)
92 self.gui_factory.connect("default-slider-scale-changed", self.on_default_slider_scale_changed)
93 self.gui_factory.connect('vumeter-color-changed', self.on_vumeter_color_changed)
94 self.gui_factory.connect('vumeter-color-scheme-changed', self.on_vumeter_color_changed)
95 self.gui_factory.connect('use-custom-widgets-changed', self.on_custom_widgets_changed)
97 self.abspeak = abspeak.AbspeakWidget()
98 self.abspeak.connect("reset", self.on_abspeak_reset)
99 self.abspeak.connect("volume-adjust", self.on_abspeak_adjust)
101 self.volume_digits = Gtk.Entry()
102 self.volume_digits.connect("key-press-event", self.on_volume_digits_key_pressed)
103 self.volume_digits.connect("focus-out-event", self.on_volume_digits_focus_out)
105 self.connect("key-press-event", self.on_key_pressed)
106 self.connect("scroll-event", self.on_scroll)
109 #print "Unrealizing channel \"%s\"" % self.channel_name
112 def balance_preferred_width(self):
115 def _preferred_height(self):
118 def create_balance_widget(self):
119 if self.gui_factory.use_custom_widgets and phat:
120 self.balance = phat.HFanSlider()
121 self.balance.set_default_value(0)
122 self.balance.set_adjustment(self.balance_adjustment)
124 self.balance = Gtk.Scale()
125 self.balance.get_preferred_width = self.balance_preferred_width
126 self.balance.get_preferred_height = self._preferred_height
127 self.balance.set_orientation(Gtk.Orientation.HORIZONTAL)
128 self.balance.set_adjustment(self.balance_adjustment)
129 self.balance.set_draw_value(False)
130 self.pack_start(self.balance, False, True, 0)
131 if self.monitor_button:
132 self.reorder_child(self.monitor_button, -1)
135 def create_slider_widget(self):
138 parent = self.slider.get_parent()
139 self.slider.destroy()
140 if self.gui_factory.use_custom_widgets:
141 self.slider = slider.CustomSliderWidget(self.slider_adjustment)
143 self.slider = slider.GtkSlider(self.slider_adjustment)
145 parent.pack_start(self.slider, True, True, 0)
146 parent.reorder_child(self.slider, 0)
149 def on_default_meter_scale_changed(self, gui_factory, scale):
150 #print "Default meter scale change detected."
151 self.meter.set_scale(scale)
153 def on_default_slider_scale_changed(self, gui_factory, scale):
154 #print "Default slider scale change detected."
155 self.slider_scale = scale
156 self.slider_adjustment.set_scale(scale)
157 self.channel.midi_scale = self.slider_scale.scale
159 def on_vumeter_color_changed(self, gui_factory, *args):
160 color = gui_factory.get_vumeter_color()
161 color_scheme = gui_factory.get_vumeter_color_scheme()
162 if color_scheme != 'solid':
163 self.meter.set_color(None)
165 self.meter.set_color(Gdk.color_parse(color))
167 def on_custom_widgets_changed(self, gui_factory, value):
168 self.balance.destroy()
169 self.create_balance_widget()
170 self.create_slider_widget()
172 def on_abspeak_adjust(self, abspeak, adjust):
173 #print "abspeak adjust %f" % adjust
174 self.slider_adjustment.set_value_db(self.slider_adjustment.get_value_db() + adjust)
175 self.channel.abspeak = None
176 #self.update_volume(False) # We want to update gui even if actual decibels have not changed (scale wrap for example)
178 def on_abspeak_reset(self, abspeak):
179 #print "abspeak reset"
180 self.channel.abspeak = None
182 def on_volume_digits_key_pressed(self, widget, event):
183 if (event.keyval == Gdk.KEY_Return or event.keyval == Gdk.KEY_KP_Enter):
184 db_text = self.volume_digits.get_text()
187 #print "Volume digits confirmation \"%f dBFS\"" % db
188 except (ValueError), e:
189 #print "Volume digits confirmation ignore, reset to current"
190 self.update_volume(False)
192 self.slider_adjustment.set_value_db(db)
194 #self.update_volume(False) # We want to update gui even if actual decibels have not changed (scale wrap for example)
196 def on_volume_digits_focus_out(self, widget, event):
197 #print "volume digits focus out detected"
198 self.update_volume(False)
200 def read_meter(self):
204 meter_left, meter_right = self.channel.meter
205 self.meter.set_values(meter_left, meter_right)
207 self.meter.set_value(self.channel.meter[0])
209 self.abspeak.set_peak(self.channel.abspeak)
211 def on_scroll(self, widget, event):
212 if event.direction == Gdk.ScrollDirection.DOWN:
213 self.slider_adjustment.step_down()
214 elif event.direction == Gdk.ScrollDirection.UP:
215 self.slider_adjustment.step_up()
218 def update_volume(self, update_engine):
219 db = self.slider_adjustment.get_value_db()
221 db_text = "%.2f" % db
222 self.volume_digits.set_text(db_text)
225 self.channel.volume = db
226 self.app.update_monitor(self)
228 def on_volume_changed(self, adjustment):
229 self.update_volume(True)
231 def on_balance_changed(self, adjustment):
232 balance = self.balance_adjustment.get_value()
233 #print "%s balance: %f" % (self.channel_name, balance)
234 self.channel.balance = balance
235 self.app.update_monitor(self)
237 def on_key_pressed(self, widget, event):
238 if (event.keyval == Gdk.KEY_Up):
239 #print self.channel_name + " Up"
240 self.slider_adjustment.step_up()
242 elif (event.keyval == Gdk.KEY_Down):
243 #print self.channel_name + " Down"
244 self.slider_adjustment.step_down()
249 def serialize(self, object_backend):
250 object_backend.add_property("volume", "%f" % self.slider_adjustment.get_value_db())
251 object_backend.add_property("balance", "%f" % self.balance_adjustment.get_value())
253 if hasattr(self.channel, 'out_mute'):
254 object_backend.add_property('out_mute', str(self.channel.out_mute))
255 if self.channel.volume_midi_cc != -1:
256 object_backend.add_property('volume_midi_cc', str(self.channel.volume_midi_cc))
257 if self.channel.balance_midi_cc != -1:
258 object_backend.add_property('balance_midi_cc', str(self.channel.balance_midi_cc))
259 if self.channel.mute_midi_cc != -1:
260 object_backend.add_property('mute_midi_cc', str(self.channel.mute_midi_cc))
261 if self.channel.solo_midi_cc != -1:
262 object_backend.add_property('solo_midi_cc', str(self.channel.solo_midi_cc))
265 def unserialize_property(self, name, value):
267 self.slider_adjustment.set_value_db(float(value))
269 if name == "balance":
270 self.balance_adjustment.set_value(float(value))
272 if name == 'out_mute':
273 self.future_out_mute = (value == 'True')
275 if name == 'volume_midi_cc':
276 self.future_volume_midi_cc = int(value)
278 if name == 'balance_midi_cc':
279 self.future_balance_midi_cc = int(value)
281 if name == 'mute_midi_cc':
282 self.future_mute_midi_cc = int(value)
284 if name == 'solo_midi_cc':
285 self.future_solo_midi_cc = int(value)
289 def on_midi_event_received(self, *args):
290 self.slider_adjustment.set_value_db(self.channel.volume)
291 self.balance_adjustment.set_value(self.channel.balance)
293 def on_monitor_button_toggled(self, button):
294 if button.get_active():
295 for channel in self.app.channels + self.app.output_channels:
296 if channel.monitor_button.get_active() and channel.monitor_button is not button:
297 channel.monitor_button.handler_block_by_func(
298 channel.on_monitor_button_toggled)
299 channel.monitor_button.set_active(False)
300 channel.monitor_button.handler_unblock_by_func(
301 channel.on_monitor_button_toggled)
302 self.app.set_monitored_channel(self)
304 def set_monitored(self):
306 self.app.set_monitored_channel(self)
307 self.monitor_button.set_active(True)
309 class InputChannel(Channel):
310 post_fader_output_channel = None
312 def __init__(self, app, name, stereo):
313 Channel.__init__(self, app, name, stereo)
316 self.channel = self.mixer.add_channel(self.channel_name, self.stereo)
317 if self.channel == None:
318 raise Exception,"Cannot create a channel"
319 Channel.realize(self)
320 if self.future_volume_midi_cc != None:
321 self.channel.volume_midi_cc = self.future_volume_midi_cc
322 if self.future_balance_midi_cc != None:
323 self.channel.balance_midi_cc = self.future_balance_midi_cc
324 if self.future_mute_midi_cc != None:
325 self.channel.mute_midi_cc = self.future_mute_midi_cc
326 if self.future_solo_midi_cc != None:
327 self.channel.solo_midi_cc = self.future_solo_midi_cc
328 if self.app._init_solo_channels and self.channel_name in self.app._init_solo_channels:
329 self.channel.solo = True
331 self.channel.midi_scale = self.slider_scale.scale
333 self.on_volume_changed(self.slider_adjustment)
334 self.on_balance_changed(self.balance_adjustment)
336 # vbox child at upper part
337 self.vbox = Gtk.VBox()
338 self.pack_start(self.vbox, False, True, 0)
339 self.label_name = Gtk.Label()
340 self.label_name.set_text(self.channel_name)
341 self.label_name.set_width_chars(0)
342 self.label_name_event_box = Gtk.EventBox()
343 self.label_name_event_box.connect("button-press-event", self.on_label_mouse)
344 self.label_name_event_box.add(self.label_name)
345 self.vbox.pack_start(self.label_name_event_box, True, True, 0)
346 # self.label_stereo = Gtk.Label()
348 # self.label_stereo.set_text("stereo")
350 # self.label_stereo.set_text("mono")
351 # self.label_stereo.set_size_request(0, -1)
352 # self.vbox.pack_start(self.label_stereo, True)
354 self.hbox_mutesolo = Gtk.HBox()
356 self.mute = Gtk.ToggleButton()
357 self.mute.set_label("M")
358 self.mute.set_active(self.channel.out_mute)
359 self.mute.connect("toggled", self.on_mute_toggled)
360 self.hbox_mutesolo.pack_start(self.mute, True, True, 0)
362 self.solo = Gtk.ToggleButton()
363 self.solo.set_label("S")
364 self.solo.set_active(self.channel.solo)
365 self.solo.connect("toggled", self.on_solo_toggled)
366 self.hbox_mutesolo.pack_start(self.solo, True, True, 0)
368 self.vbox.pack_start(self.hbox_mutesolo, False, True, 0)
371 frame.set_shadow_type(Gtk.ShadowType.IN)
372 frame.add(self.abspeak);
373 self.pack_start(frame, False, True, 0)
375 # hbox child at lower part
376 self.hbox = Gtk.HBox()
377 self.hbox.pack_start(self.slider, True, True, 0)
379 frame.set_shadow_type(Gtk.ShadowType.IN)
380 frame.add(self.meter);
381 self.hbox.pack_start(frame, True, True, 0)
383 frame.set_shadow_type(Gtk.ShadowType.IN)
384 frame.add(self.hbox);
385 self.pack_start(frame, True, True, 0)
387 self.volume_digits.set_width_chars(0)
388 self.pack_start(self.volume_digits, False, False, 0)
390 self.create_balance_widget()
392 self.monitor_button = Gtk.ToggleButton('MON')
393 self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
394 self.pack_start(self.monitor_button, False, False, 0)
396 def add_control_group(self, channel):
397 control_group = ControlGroup(channel, self)
398 control_group.show_all()
399 self.vbox.pack_start(control_group, False, True, 0)
402 def remove_control_group(self, channel):
403 ctlgroup = self.get_control_group(channel)
404 self.vbox.remove(ctlgroup)
406 def update_control_group(self, channel):
407 for control_group in self.vbox.get_children():
408 if isinstance(control_group, ControlGroup):
409 if control_group.output_channel is channel:
410 control_group.update()
412 def get_control_group(self, channel):
413 for control_group in self.vbox.get_children():
414 if isinstance(control_group, ControlGroup):
415 if control_group.output_channel is channel:
420 Channel.unrealize(self)
421 if self.post_fader_output_channel:
422 self.post_fader_output_channel.remove()
423 self.post_fader_output_channel = None
424 self.channel.remove()
427 channel_properties_dialog = None
428 def on_channel_properties(self):
429 if not self.channel_properties_dialog:
430 self.channel_properties_dialog = ChannelPropertiesDialog(self, self.app)
431 self.channel_properties_dialog.show()
432 self.channel_properties_dialog.present()
434 def on_label_mouse(self, widget, event):
435 if event.type == Gdk.EventType._2BUTTON_PRESS:
436 if event.button == 1:
437 self.on_channel_properties()
439 def on_mute_toggled(self, button):
440 self.channel.out_mute = self.mute.get_active()
442 def on_solo_toggled(self, button):
443 self.channel.solo = self.solo.get_active()
445 def midi_events_check(self):
446 if hasattr(self, 'channel') and self.channel.midi_in_got_events:
447 self.mute.set_active(self.channel.out_mute)
448 self.solo.set_active(self.channel.solo)
449 Channel.on_midi_event_received(self)
451 def on_solo_button_pressed(self, button, event, *args):
452 if event.button == 3:
453 # right click on the solo button, act on all output channels
454 if button.get_active(): # was soloed
455 button.set_active(False)
456 if hasattr(button, 'touched_channels'):
457 touched_channels = button.touched_channels
458 for chan in touched_channels:
459 ctlgroup = self.get_control_group(chan)
460 ctlgroup.solo.set_active(False)
461 del button.touched_channels
462 else: # was not soloed
463 button.set_active(True)
464 touched_channels = []
465 for chan in self.app.output_channels:
466 ctlgroup = self.get_control_group(chan)
467 if not ctlgroup.solo.get_active():
468 ctlgroup.solo.set_active(True)
469 touched_channels.append(chan)
470 button.touched_channels = touched_channels
475 def serialization_name(cls):
476 return 'input_channel'
478 def serialize(self, object_backend):
479 object_backend.add_property("name", self.channel_name)
481 object_backend.add_property("type", "stereo")
483 object_backend.add_property("type", "mono")
484 Channel.serialize(self, object_backend)
486 def unserialize_property(self, name, value):
488 self.channel_name = str(value)
491 if value == "stereo":
497 return Channel.unserialize_property(self, name, value)
500 available_colours = [
501 ('#648fcb', '#204c98', '#426cb8'),
502 ('#984a9a', '#542656', '#744676'),
503 ('#7f9abb', '#3f5677', '#5f7697'),
504 ('#bf8f9f', '#7b4d5b', '#9b6f7b'),
505 ('#ba6d89', '#762945', '#964965'),
506 ('#4c9196', '#0c5156', '#2c7176'),
507 ('#56a2c0', '#166280', '#3682a0'),
510 class OutputChannel(Channel):
511 colours = available_colours[:]
512 _display_solo_buttons = False
514 _init_muted_channels = None
515 _init_solo_channels = None
517 def __init__(self, app, name, stereo):
518 Channel.__init__(self, app, name, stereo)
520 def get_display_solo_buttons(self):
521 return self._display_solo_buttons
523 def set_display_solo_buttons(self, value):
524 self._display_solo_buttons = value
525 # notifying control groups
526 for inputchannel in self.app.channels:
527 inputchannel.update_control_group(self)
529 display_solo_buttons = property(get_display_solo_buttons, set_display_solo_buttons)
532 self.channel = self.mixer.add_output_channel(self.channel_name, self.stereo)
533 if self.channel == None:
534 raise Exception,"Cannot create a channel"
535 Channel.realize(self)
536 if self.future_volume_midi_cc != None:
537 self.channel.volume_midi_cc = self.future_volume_midi_cc
538 if self.future_balance_midi_cc != None:
539 self.channel.balance_midi_cc = self.future_balance_midi_cc
540 if self.future_mute_midi_cc != None:
541 self.channel.mute_midi_cc = self.future_mute_midi_cc
542 self.channel.midi_scale = self.slider_scale.scale
544 self.on_volume_changed(self.slider_adjustment)
545 self.on_balance_changed(self.balance_adjustment)
547 # vbox child at upper part
548 self.vbox = Gtk.VBox()
549 self.pack_start(self.vbox, False, True, 0)
550 self.label_name = Gtk.Label()
551 self.label_name.set_text(self.channel_name)
552 self.label_name.set_width_chars(0)
553 self.label_name_event_box = Gtk.EventBox()
554 self.label_name_event_box.connect('button-press-event', self.on_label_mouse)
555 self.label_name_event_box.add(self.label_name)
557 OutputChannel.colours = available_colours[:]
558 for color in self.colours:
559 self.color_tuple = [Gdk.color_parse(color[x]) for x in range(3)]
560 self.colours.remove(color)
562 self.label_name_event_box.modify_bg(Gtk.StateType.NORMAL, self.color_tuple[1])
563 self.vbox.pack_start(self.label_name_event_box, True, True, 0)
564 self.mute = Gtk.ToggleButton()
565 self.mute.set_label("M")
566 self.mute.set_active(self.channel.out_mute)
567 self.mute.connect("toggled", self.on_mute_toggled)
568 self.vbox.pack_start(self.mute, False, True, 0)
571 frame.set_shadow_type(Gtk.ShadowType.IN)
572 frame.add(self.abspeak);
573 self.vbox.pack_start(frame, False, True, 0)
575 # hbox child at lower part
576 self.hbox = Gtk.HBox()
577 self.hbox.pack_start(self.slider, True, True, 0)
579 frame.set_shadow_type(Gtk.ShadowType.IN)
580 frame.add(self.meter);
581 self.hbox.pack_start(frame, True, True, 0)
583 frame.set_shadow_type(Gtk.ShadowType.IN)
584 frame.add(self.hbox);
585 self.pack_start(frame, True, True, 0)
587 self.volume_digits.set_width_chars(0)
588 self.pack_start(self.volume_digits, False, True, 0)
590 self.create_balance_widget()
592 self.monitor_button = Gtk.ToggleButton('MON')
593 self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
594 self.pack_start(self.monitor_button, False, False, 0)
596 # add control groups to the input channels, and initialize them
598 for input_channel in self.app.channels:
599 ctlgroup = input_channel.add_control_group(self)
600 if self._init_muted_channels and input_channel.channel.name in self._init_muted_channels:
601 ctlgroup.mute.set_active(True)
602 if self._init_solo_channels and input_channel.channel.name in self._init_solo_channels:
603 ctlgroup.solo.set_active(True)
604 self._init_muted_channels = None
605 self._init_solo_channels = None
607 channel_properties_dialog = None
608 def on_channel_properties(self):
609 if not self.channel_properties_dialog:
610 self.channel_properties_dialog = OutputChannelPropertiesDialog(self, self.app)
611 self.channel_properties_dialog.show()
612 self.channel_properties_dialog.present()
614 def on_label_mouse(self, widget, event):
615 if event.type == Gdk.EventType._2BUTTON_PRESS:
616 if event.button == 1:
617 self.on_channel_properties()
619 def on_mute_toggled(self, button):
620 self.channel.out_mute = self.mute.get_active()
622 def midi_events_check(self):
623 if self.channel != None and self.channel.midi_in_got_events:
624 self.mute.set_active(self.channel.out_mute)
625 Channel.on_midi_event_received(self)
628 # remove control groups from input channels
629 for input_channel in self.app.channels:
630 input_channel.remove_control_group(self)
632 Channel.unrealize(self)
633 self.channel.remove()
637 def serialization_name(cls):
638 return 'output_channel'
640 def serialize(self, object_backend):
641 object_backend.add_property("name", self.channel_name)
643 object_backend.add_property("type", "stereo")
645 object_backend.add_property("type", "mono")
646 if self.display_solo_buttons:
647 object_backend.add_property("solo_buttons", "true")
650 for input_channel in self.app.channels:
651 if self.channel.is_muted(input_channel.channel):
652 muted_channels.append(input_channel)
653 if self.channel.is_solo(input_channel.channel):
654 solo_channels.append(input_channel)
656 object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
658 object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
659 Channel.serialize(self, object_backend)
661 def unserialize_property(self, name, value):
663 self.channel_name = str(value)
666 if value == "stereo":
672 if name == "solo_buttons":
674 self.display_solo_buttons = True
676 if name == 'muted_channels':
677 self._init_muted_channels = value.split('|')
679 if name == 'solo_channels':
680 self._init_solo_channels = value.split('|')
682 return Channel.unserialize_property(self, name, value)
684 class ChannelPropertiesDialog(Gtk.Dialog):
687 def __init__(self, parent, app):
688 self.channel = parent
690 self.mixer = self.channel.mixer
691 GObject.GObject.__init__(self)
692 self.set_title('Channel "%s" Properties' % self.channel.channel_name)
694 self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
695 self.ok_button = self.add_button(Gtk.STOCK_APPLY, Gtk.ResponseType.APPLY)
696 self.set_default_response(Gtk.ResponseType.APPLY);
701 self.connect('response', self.on_response_cb)
702 self.connect('delete-event', self.on_response_cb)
704 def create_frame(self, label, child):
707 frame.set_border_width(3)
708 #frame.set_shadow_type(Gtk.ShadowType.NONE)
709 frame.get_label_widget().set_markup('<b>%s</b>' % label)
711 alignment = Gtk.Alignment.new(0, 0, 1, 1)
712 alignment.set_padding(0, 0, 12, 0)
722 table = Gtk.Table(2, 3, False)
723 vbox.pack_start(self.create_frame('Properties', table), True, True, 0)
724 table.set_row_spacings(5)
725 table.set_col_spacings(5)
727 table.attach(Gtk.Label(label='Name'), 0, 1, 0, 1)
728 self.entry_name = Gtk.Entry()
729 self.entry_name.set_activates_default(True)
730 self.entry_name.connect('changed', self.on_entry_name_changed)
731 table.attach(self.entry_name, 1, 2, 0, 1)
733 table.attach(Gtk.Label(label='Mode'), 0, 1, 1, 2)
734 self.mode_hbox = Gtk.HBox()
735 table.attach(self.mode_hbox, 1, 2, 1, 2)
736 self.mono = Gtk.RadioButton(label='Mono')
737 self.stereo = Gtk.RadioButton(label='Stereo', group=self.mono)
738 self.mode_hbox.pack_start(self.mono, True, True, 0)
739 self.mode_hbox.pack_start(self.stereo, True, True, 0)
741 table = Gtk.Table(2, 3, False)
742 vbox.pack_start(self.create_frame('MIDI Control Channels', table), True, True, 0)
743 table.set_row_spacings(5)
744 table.set_col_spacings(5)
746 table.attach(Gtk.Label(label='Volume'), 0, 1, 0, 1)
747 self.entry_volume_cc = Gtk.Entry()
748 self.entry_volume_cc.set_activates_default(True)
749 self.entry_volume_cc.set_editable(False)
750 self.entry_volume_cc.set_width_chars(3)
751 table.attach(self.entry_volume_cc, 1, 2, 0, 1)
752 self.button_sense_midi_volume = Gtk.Button('Autoset')
753 self.button_sense_midi_volume.connect('clicked',
754 self.on_sense_midi_volume_clicked)
755 table.attach(self.button_sense_midi_volume, 2, 3, 0, 1)
757 table.attach(Gtk.Label(label='Balance'), 0, 1, 1, 2)
758 self.entry_balance_cc = Gtk.Entry()
759 self.entry_balance_cc.set_activates_default(True)
760 self.entry_balance_cc.set_width_chars(3)
761 self.entry_balance_cc.set_editable(False)
762 table.attach(self.entry_balance_cc, 1, 2, 1, 2)
763 self.button_sense_midi_balance = Gtk.Button('Autoset')
764 self.button_sense_midi_balance.connect('clicked',
765 self.on_sense_midi_balance_clicked)
766 table.attach(self.button_sense_midi_balance, 2, 3, 1, 2)
768 table.attach(Gtk.Label(label='Mute'), 0, 1, 2, 3)
769 self.entry_mute_cc = Gtk.Entry()
770 self.entry_mute_cc.set_activates_default(True)
771 self.entry_mute_cc.set_editable(False)
772 self.entry_mute_cc.set_width_chars(3)
773 table.attach(self.entry_mute_cc, 1, 2, 2, 3)
774 self.button_sense_midi_mute = Gtk.Button('Autoset')
775 self.button_sense_midi_mute.connect('clicked',
776 self.on_sense_midi_mute_clicked)
777 table.attach(self.button_sense_midi_mute, 2, 3, 2, 3)
779 if (isinstance(self, NewChannelDialog) or (self.channel and
780 isinstance(self.channel, InputChannel))):
781 table.attach(Gtk.Label(label='Solo'), 0, 1, 3, 4)
782 self.entry_solo_cc = Gtk.Entry()
783 self.entry_solo_cc.set_activates_default(True)
784 self.entry_solo_cc.set_editable(False)
785 self.entry_solo_cc.set_width_chars(3)
786 table.attach(self.entry_solo_cc, 1, 2, 3, 4)
787 self.button_sense_midi_solo = Gtk.Button('Autoset')
788 self.button_sense_midi_solo.connect('clicked',
789 self.on_sense_midi_solo_clicked)
790 table.attach(self.button_sense_midi_solo, 2, 3, 3, 4)
795 self.entry_name.set_text(self.channel.channel_name)
796 if self.channel.channel.is_stereo:
797 self.stereo.set_active(True)
799 self.mono.set_active(True)
800 self.mode_hbox.set_sensitive(False)
801 self.entry_volume_cc.set_text('%s' % self.channel.channel.volume_midi_cc)
802 self.entry_balance_cc.set_text('%s' % self.channel.channel.balance_midi_cc)
803 self.entry_mute_cc.set_text('%s' % self.channel.channel.mute_midi_cc)
804 if (self.channel and isinstance(self.channel, InputChannel)):
805 self.entry_solo_cc.set_text('%s' % self.channel.channel.solo_midi_cc)
807 def sense_popup_dialog(self, entry):
808 window = Gtk.Window(Gtk.WindowType.TOPLEVEL)
809 window.set_destroy_with_parent(True)
810 window.set_transient_for(self)
811 window.set_decorated(False)
812 window.set_modal(True)
813 window.set_position(Gtk.WindowPosition.CENTER_ON_PARENT)
814 window.set_border_width(10)
819 vbox.pack_start(Gtk.Label('Please move the MIDI control you want to use for this function.', True, True, 0))
820 timeout_label = Gtk.Label(label='This window will close in 5 seconds')
821 vbox.pack_start(timeout_label, True, True, 0)
822 def close_sense_timeout(window, entry):
824 timeout_label.set_text('This window will close in %d seconds.' % window.timeout)
825 if window.timeout == 0:
827 entry.set_text('%s' % self.mixer.last_midi_channel)
831 GObject.timeout_add_seconds(1, close_sense_timeout, window, entry)
833 def on_sense_midi_volume_clicked(self, *args):
834 self.mixer.last_midi_channel = int(self.entry_volume_cc.get_text())
835 self.sense_popup_dialog(self.entry_volume_cc)
837 def on_sense_midi_balance_clicked(self, *args):
838 self.mixer.last_midi_channel = int(self.entry_balance_cc.get_text())
839 self.sense_popup_dialog(self.entry_balance_cc)
841 def on_sense_midi_mute_clicked(self, *args):
842 self.mixer.last_midi_channel = int(self.entry_mute_cc.get_text())
843 self.sense_popup_dialog(self.entry_mute_cc)
845 def on_sense_midi_solo_clicked(self, *args):
846 self.mixer.last_midi_channel = int(self.entry_solo_cc.get_text())
847 self.sense_popup_dialog(self.entry_solo_cc)
849 def on_response_cb(self, dlg, response_id, *args):
850 self.channel.channel_properties_dialog = None
851 name = self.entry_name.get_text()
852 if response_id == Gtk.ResponseType.APPLY:
853 self.channel.channel_name = name
855 if self.entry_volume_cc.get_text() != '-1':
856 self.channel.channel.volume_midi_cc = int(self.entry_volume_cc.get_text())
860 if self.entry_balance_cc.get_text() != '-1':
861 self.channel.channel.balance_midi_cc = int(self.entry_balance_cc.get_text())
865 if self.entry_mute_cc.get_text() != '-1':
866 self.channel.channel.mute_midi_cc = int(self.entry_mute_cc.get_text())
870 if hasattr(self, 'entry_solo_cc') and self.entry_solo_cc.get_text() != '-1':
871 self.channel.channel.solo_midi_cc = int(self.entry_solo_cc.get_text())
876 def on_entry_name_changed(self, entry):
878 if len(entry.get_text()):
879 if self.channel and self.channel.channel.name == entry.get_text():
881 elif entry.get_text() not in [x.channel.name for x in self.app.channels] + \
882 [x.channel.name for x in self.app.output_channels] + ['MAIN']:
884 self.ok_button.set_sensitive(sensitive)
887 class NewChannelDialog(ChannelPropertiesDialog):
888 def __init__(self, app):
889 Gtk.Dialog.__init__(self, 'New Channel', app.window)
890 self.mixer = app.mixer
895 self.stereo.set_active(True) # default to stereo
897 self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
898 self.ok_button = self.add_button(Gtk.STOCK_ADD, Gtk.ResponseType.OK)
899 self.ok_button.set_sensitive(False)
900 self.set_default_response(Gtk.ResponseType.OK);
903 self.entry_volume_cc.set_text('-1')
904 self.entry_balance_cc.set_text('-1')
905 self.entry_mute_cc.set_text('-1')
906 self.entry_solo_cc.set_text('-1')
908 def get_result(self):
909 return {'name': self.entry_name.get_text(),
910 'stereo': self.stereo.get_active(),
911 'volume_cc': self.entry_volume_cc.get_text(),
912 'balance_cc': self.entry_balance_cc.get_text(),
913 'mute_cc': self.entry_mute_cc.get_text(),
914 'solo_cc': self.entry_solo_cc.get_text()
917 class OutputChannelPropertiesDialog(ChannelPropertiesDialog):
919 ChannelPropertiesDialog.create_ui(self)
922 self.vbox.pack_start(self.create_frame('Input Channels', vbox), True, True, 0)
924 self.display_solo_buttons = Gtk.CheckButton('Display solo buttons')
925 vbox.pack_start(self.display_solo_buttons, True, True, 0)
930 ChannelPropertiesDialog.fill_ui(self)
931 self.display_solo_buttons.set_active(self.channel.display_solo_buttons)
933 def on_response_cb(self, dlg, response_id, *args):
934 if response_id == Gtk.ResponseType.APPLY:
935 self.channel.display_solo_buttons = self.display_solo_buttons.get_active()
936 ChannelPropertiesDialog.on_response_cb(self, dlg, response_id, *args)
939 class NewOutputChannelDialog(OutputChannelPropertiesDialog):
940 def __init__(self, app):
941 Gtk.Dialog.__init__(self, 'New Output Channel', app.window)
942 self.mixer = app.mixer
947 # TODO: disable mode for output channels as mono output channels may
948 # not be correctly handled yet.
949 self.mode_hbox.set_sensitive(False)
950 self.stereo.set_active(True) # default to stereo
952 self.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
953 self.ok_button = self.add_button(Gtk.STOCK_ADD, Gtk.ResponseType.OK)
954 self.ok_button.set_sensitive(False)
955 self.set_default_response(Gtk.ResponseType.OK);
958 self.entry_volume_cc.set_text('-1')
959 self.entry_balance_cc.set_text('-1')
960 self.entry_mute_cc.set_text('-1')
962 def get_result(self):
963 return {'name': self.entry_name.get_text(),
964 'stereo': self.stereo.get_active(),
965 'volume_cc': self.entry_volume_cc.get_text(),
966 'balance_cc': self.entry_balance_cc.get_text(),
967 'mute_cc': self.entry_mute_cc.get_text(),
968 'display_solo_buttons': self.display_solo_buttons.get_active(),
972 class ControlGroup(Gtk.Alignment):
973 def __init__(self, output_channel, input_channel):
974 GObject.GObject.__init__(self)
975 self.set(0.5, 0.5, 0, 0)
976 self.output_channel = output_channel
977 self.input_channel = input_channel
978 self.app = input_channel.app
984 vbox.pack_start(hbox, False, False, space)
987 vbox.modify_bg(Gtk.StateType.NORMAL, output_channel.color_tuple[1])
988 mute_name = "%s_mute" % output_channel.channel.name
989 mute = Gtk.ToggleButton()
990 mute.set_name(mute_name)
992 #%s { background-color: rgb(%d, %d, %d); }
993 #%s:hover { background-color: rgb(%d, %d, %d); }
994 #%s:checked { background-color: rgb(%d, %d, %d); }""" % (mute_name,
995 int(output_channel.color_tuple[1].red * 255/65535.),
996 int(output_channel.color_tuple[1].green * 255/65535.),
997 int(output_channel.color_tuple[1].blue * 255/65535.),
998 mute_name, int(output_channel.color_tuple[0].red *
999 255/65535.), int(output_channel.color_tuple[0].green
1001 int(output_channel.color_tuple[0].blue *
1002 255/65535.), mute_name,
1003 int(output_channel.color_tuple[2].red * 255/65535.),
1004 int(output_channel.color_tuple[2].green *
1005 255/65535.), int(output_channel.color_tuple[2].blue * 255/65535.))
1006 solo_name = "%s_solo" % output_channel.channel.name
1008 #%s { background-color: rgb(%d, %d, %d); }
1009 #%s:hover { background-color: rgb(%d, %d, %d); }
1010 #%s:checked { background-color: rgb(%d, %d, %d); }""" % (solo_name,
1011 int(output_channel.color_tuple[1].red * 255/65535.),
1012 int(output_channel.color_tuple[1].green * 255/65535.),
1013 int(output_channel.color_tuple[1].blue * 255/65535.),
1014 solo_name, int(output_channel.color_tuple[0].red *
1015 255/65535.), int(output_channel.color_tuple[0].green
1017 int(output_channel.color_tuple[0].blue *
1018 255/65535.), solo_name,
1019 int(output_channel.color_tuple[2].red * 255/65535.),
1020 int(output_channel.color_tuple[2].green *
1021 255/65535.), int(output_channel.color_tuple[2].blue * 255/65535.))
1022 css = mute_css + solo_css
1024 mute.connect("toggled", self.on_mute_toggled)
1026 hbox.pack_start(mute, False, False, space)
1027 solo = Gtk.ToggleButton()
1028 solo.set_name(solo_name)
1030 solo.connect("toggled", self.on_solo_toggled)
1032 if self.output_channel.display_solo_buttons:
1033 hbox.pack_start(solo, False, False, 10)
1034 css_provider = Gtk.CssProvider()
1035 #css_provider.load_from_data(mute_css)
1036 css_provider.load_from_data(css)
1037 context = Gtk.StyleContext()
1038 screen = Gdk.Screen.get_default()
1039 context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
1042 if self.output_channel.display_solo_buttons:
1043 if not self.solo in self.hbox.get_children():
1044 self.hbox.pack_start(self.solo, True, True, 0)
1047 if self.solo in self.hbox.get_children():
1048 self.hbox.remove(self.solo)
1050 def on_mute_toggled(self, button):
1051 self.output_channel.channel.set_muted(self.input_channel.channel, button.get_active())
1052 self.app.update_monitor(self)
1054 def on_solo_toggled(self, button):
1055 self.output_channel.channel.set_solo(self.input_channel.channel, button.get_active())
1056 self.app.update_monitor(self)