3 # This file is part of jack_mixer
5 # Copyright (C) 2006 Nedko Arnaudov <nedko@arnaudov.name>
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; version 2 of the License
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
29 from serialization import serialized_object
34 print "PHAT audio widgets not found, some features will not be available"
39 class channel(gtk.VBox, serialized_object):
40 '''Widget with slider and meter used as base class for more specific channel widgets'''
43 def __init__(self, app, name, stereo):
44 gtk.VBox.__init__(self)
46 self.mixer = app.mixer
47 self.gui_factory = app.gui_factory
48 self._channel_name = name
50 self.meter_scale = self.gui_factory.get_default_meter_scale()
51 self.slider_scale = self.gui_factory.get_default_slider_scale()
52 self.slider_adjustment = slider.adjustment_dBFS(self.slider_scale, 0.0)
53 self.balance_adjustment = gtk.Adjustment(0.0, -1.0, 1.0, 0.02)
54 self.future_volume_midi_cc = None
55 self.future_balance_midi_cc = None
57 def get_channel_name(self):
58 return self._channel_name
62 def set_channel_name(self, name):
63 self._channel_name = name
65 self.label_name.set_text(name)
67 self.channel.name = name
68 channel_name = property(get_channel_name, set_channel_name)
71 #print "Realizing channel \"%s\"" % self.channel_name
73 self.slider_adjustment.connect("volume-changed", self.on_volume_changed)
74 self.balance_adjustment.connect("value-changed", self.on_balance_changed)
75 self.connect('midi-event-received', self.on_midi_event_received)
78 self.create_slider_widget()
81 self.meter = meter.stereo(self.meter_scale)
83 self.meter = meter.mono(self.meter_scale)
84 self.on_vumeter_color_changed(self.gui_factory)
86 self.meter.set_events(gtk.gdk.SCROLL_MASK)
88 self.gui_factory.connect("default-meter-scale-changed", self.on_default_meter_scale_changed)
89 self.gui_factory.connect("default-slider-scale-changed", self.on_default_slider_scale_changed)
90 self.gui_factory.connect('vumeter-color-changed', self.on_vumeter_color_changed)
91 self.gui_factory.connect('vumeter-color-scheme-changed', self.on_vumeter_color_changed)
92 self.gui_factory.connect('use-custom-widgets-changed', self.on_custom_widgets_changed)
94 self.abspeak = abspeak.widget()
95 self.abspeak.connect("reset", self.on_abspeak_reset)
96 self.abspeak.connect("volume-adjust", self.on_abspeak_adjust)
98 self.volume_digits = gtk.Entry()
99 self.volume_digits.connect("key-press-event", self.on_volume_digits_key_pressed)
100 self.volume_digits.connect("focus-out-event", self.on_volume_digits_focus_out)
102 self.connect("key-press-event", self.on_key_pressed)
103 self.connect("scroll-event", self.on_scroll)
106 #print "Unrealizing channel \"%s\"" % self.channel_name
109 def create_balance_widget(self):
110 if self.gui_factory.use_custom_widgets and phat:
111 self.balance = phat.HFanSlider()
112 self.balance.set_default_value(0)
113 self.balance.set_adjustment(self.balance_adjustment)
115 self.balance = gtk.HScale(self.balance_adjustment)
116 self.balance.set_draw_value(False)
117 self.pack_start(self.balance, False)
118 if self.monitor_button:
119 self.reorder_child(self.monitor_button, -1)
122 def create_slider_widget(self):
125 parent = self.slider.get_parent()
126 self.slider.destroy()
127 if self.gui_factory.use_custom_widgets:
128 self.slider = slider.CustomSliderWidget(self.slider_adjustment)
130 self.slider = slider.GtkSlider(self.slider_adjustment)
132 parent.pack_start(self.slider)
133 parent.reorder_child(self.slider, 0)
136 def on_default_meter_scale_changed(self, gui_factory, scale):
137 #print "Default meter scale change detected."
138 self.meter.set_scale(scale)
140 def on_default_slider_scale_changed(self, gui_factory, scale):
141 #print "Default slider scale change detected."
142 self.slider_scale = scale
143 self.slider_adjustment.set_scale(scale)
144 self.channel.set_midi_scale(self.slider_scale.scale)
146 def on_vumeter_color_changed(self, gui_factory, *args):
147 color = gui_factory.get_vumeter_color()
148 color_scheme = gui_factory.get_vumeter_color_scheme()
149 if color_scheme != 'solid':
150 self.meter.set_color(None)
152 self.meter.set_color(gtk.gdk.color_parse(color))
154 def on_custom_widgets_changed(self, gui_factory, value):
155 self.balance.destroy()
156 self.create_balance_widget()
157 self.create_slider_widget()
159 def on_abspeak_adjust(self, abspeak, adjust):
160 #print "abspeak adjust %f" % adjust
161 self.slider_adjustment.set_value_db(self.slider_adjustment.get_value_db() + adjust)
162 self.channel.abspeak = None
163 #self.update_volume(False) # We want to update gui even if actual decibels have not changed (scale wrap for example)
165 def on_abspeak_reset(self, abspeak):
166 #print "abspeak reset"
167 self.channel.abspeak = None
169 def on_volume_digits_key_pressed(self, widget, event):
170 if (event.keyval == gtk.keysyms.Return or event.keyval == gtk.keysyms.KP_Enter):
171 db_text = self.volume_digits.get_text()
174 #print "Volume digits confirmation \"%f dBFS\"" % db
175 except (ValueError), e:
176 #print "Volume digits confirmation ignore, reset to current"
177 self.update_volume(False)
179 self.slider_adjustment.set_value_db(db)
181 #self.update_volume(False) # We want to update gui even if actual decibels have not changed (scale wrap for example)
183 def on_volume_digits_focus_out(self, widget, event):
184 #print "volume digits focus out detected"
185 self.update_volume(False)
187 def read_meter(self):
189 meter_left, meter_right = self.channel.meter
190 self.meter.set_values(meter_left, meter_right)
192 self.meter.set_value(self.channel.meter[0])
194 self.abspeak.set_peak(self.channel.abspeak)
196 def on_scroll(self, widget, event):
197 if event.direction == gtk.gdk.SCROLL_DOWN:
198 self.slider_adjustment.step_down()
199 elif event.direction == gtk.gdk.SCROLL_UP:
200 self.slider_adjustment.step_up()
203 def update_volume(self, update_engine):
204 db = self.slider_adjustment.get_value_db()
206 db_text = "%.2f" % db
207 self.volume_digits.set_text(db_text)
210 #print "Setting engine volume to " + db_text
211 self.channel.volume = db
212 self.app.update_monitor(self)
214 def on_volume_changed(self, adjustment):
215 self.update_volume(True)
217 def on_balance_changed(self, adjustment):
218 balance = self.balance_adjustment.get_value()
219 #print "%s balance: %f" % (self.channel_name, balance)
220 self.channel.balance = balance
221 self.app.update_monitor(self)
223 def on_key_pressed(self, widget, event):
224 if (event.keyval == gtk.keysyms.Up):
225 #print self.channel_name + " Up"
226 self.slider_adjustment.step_up()
228 elif (event.keyval == gtk.keysyms.Down):
229 #print self.channel_name + " Down"
230 self.slider_adjustment.step_down()
235 def serialize(self, object_backend):
236 object_backend.add_property("volume", "%f" % self.slider_adjustment.get_value_db())
237 object_backend.add_property("balance", "%f" % self.balance_adjustment.get_value())
239 if self.channel.volume_midi_cc:
240 object_backend.add_property('volume_midi_cc', str(self.channel.volume_midi_cc))
241 if self.channel.balance_midi_cc:
242 object_backend.add_property('balance_midi_cc', str(self.channel.balance_midi_cc))
244 def unserialize_property(self, name, value):
246 self.slider_adjustment.set_value_db(float(value))
248 if name == "balance":
249 self.balance_adjustment.set_value(float(value))
251 if name == 'volume_midi_cc':
252 self.future_volume_midi_cc = int(value)
254 if name == 'balance_midi_cc':
255 self.future_balance_midi_cc = int(value)
259 def on_midi_event_received(self, *args):
260 self.slider_adjustment.set_value_db(self.channel.volume)
261 self.balance_adjustment.set_value(self.channel.balance)
263 def midi_change_callback(self, *args):
264 # the changes are not applied directly to the widgets as they
265 # absolutely have to be done from the gtk thread.
266 self.emit('midi-event-received')
268 def on_monitor_button_toggled(self, button):
269 if not button.get_active():
270 self.app.main_mix.monitor_button.set_active(True)
272 for channel in self.app.channels + self.app.output_channels + [self.app.main_mix]:
273 if channel.monitor_button.get_active() and channel.monitor_button is not button:
274 channel.monitor_button.handler_block_by_func(
275 channel.on_monitor_button_toggled)
276 channel.monitor_button.set_active(False)
277 channel.monitor_button.handler_unblock_by_func(
278 channel.on_monitor_button_toggled)
279 self.app.set_monitored_channel(self)
281 def set_monitored(self):
283 self.app.set_monitored_channel(self)
284 self.monitor_button.set_active(True)
286 gobject.signal_new('midi-event-received', channel,
287 gobject.SIGNAL_RUN_FIRST | gobject.SIGNAL_ACTION,
288 gobject.TYPE_NONE, ())
290 class input_channel(channel):
291 def __init__(self, app, name, stereo):
292 channel.__init__(self, app, name, stereo)
295 self.channel = self.mixer.add_channel(self.channel_name, self.stereo)
296 if self.channel == None:
297 raise Exception,"Cannot create a channel"
298 channel.realize(self)
299 if self.future_volume_midi_cc:
300 self.channel.volume_midi_cc = self.future_volume_midi_cc
301 if self.future_balance_midi_cc:
302 self.channel.balance_midi_cc = self.future_balance_midi_cc
303 self.channel.midi_scale = self.slider_scale.scale
304 self.channel.midi_change_callback = self.midi_change_callback
306 self.on_volume_changed(self.slider_adjustment)
307 self.on_balance_changed(self.balance_adjustment)
309 # vbox child at upper part
310 self.vbox = gtk.VBox()
311 self.pack_start(self.vbox, False)
312 self.label_name = gtk.Label()
313 self.label_name.set_text(self.channel_name)
314 self.label_name.set_size_request(0, -1)
315 self.label_name_event_box = gtk.EventBox()
316 self.label_name_event_box.connect("button-press-event", self.on_label_mouse)
317 self.label_name_event_box.add(self.label_name)
318 self.vbox.pack_start(self.label_name_event_box, True)
319 # self.label_stereo = gtk.Label()
321 # self.label_stereo.set_text("stereo")
323 # self.label_stereo.set_text("mono")
324 # self.label_stereo.set_size_request(0, -1)
325 # self.vbox.pack_start(self.label_stereo, True)
327 # hbox for mute and solo buttons
328 self.hbox_mutesolo = gtk.HBox()
330 self.mute = gtk.ToggleButton()
331 self.mute.set_label("M")
332 self.mute.set_active(self.channel.mute)
333 self.mute.connect("button-press-event", self.on_mute_button_pressed)
334 self.mute.connect("toggled", self.on_mute_toggled)
335 self.hbox_mutesolo.pack_start(self.mute, True)
337 self.solo = gtk.ToggleButton()
338 self.solo.set_label("S")
339 self.solo.set_active(self.channel.solo)
340 self.solo.connect("button-press-event", self.on_solo_button_pressed)
341 self.solo.connect("toggled", self.on_solo_toggled)
342 self.hbox_mutesolo.pack_start(self.solo, True)
344 self.vbox.pack_start(self.hbox_mutesolo, False)
347 frame.set_shadow_type(gtk.SHADOW_IN)
348 frame.add(self.abspeak);
349 self.pack_start(frame, False)
351 # hbox child at lower part
352 self.hbox = gtk.HBox()
353 self.hbox.pack_start(self.slider, True)
355 frame.set_shadow_type(gtk.SHADOW_IN)
356 frame.add(self.meter);
357 self.hbox.pack_start(frame, True)
359 frame.set_shadow_type(gtk.SHADOW_IN)
360 frame.add(self.hbox);
361 self.pack_start(frame, True)
363 self.volume_digits.set_size_request(0, -1)
364 self.pack_start(self.volume_digits, False)
366 self.create_balance_widget()
368 self.monitor_button = gtk.ToggleButton('MON')
369 self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
370 self.pack_start(self.monitor_button, False, False)
372 def add_control_group(self, channel):
373 control_group = ControlGroup(channel, self)
374 control_group.show_all()
375 self.vbox.pack_start(control_group, False)
378 def update_control_group(self, channel):
379 for control_group in self.vbox.get_children():
380 if isinstance(control_group, ControlGroup):
381 if control_group.output_channel is channel:
382 control_group.update()
384 def get_control_group(self, channel):
385 for control_group in self.vbox.get_children():
386 if isinstance(control_group, ControlGroup):
387 if control_group.output_channel is channel:
392 channel.unrealize(self)
393 self.channel.remove()
396 channel_properties_dialog = None
397 def on_channel_properties(self):
398 if not self.channel_properties_dialog:
399 self.channel_properties_dialog = ChannelPropertiesDialog(self, self.app)
400 self.channel_properties_dialog.show()
401 self.channel_properties_dialog.present()
403 def on_label_mouse(self, widget, event):
404 if event.type == gtk.gdk._2BUTTON_PRESS:
405 if event.button == 1:
406 self.on_channel_properties()
408 def on_mute_toggled(self, button):
409 self.channel.mute = self.mute.get_active()
410 self.app.update_monitor(self.app.main_mix)
412 def on_mute_button_pressed(self, button, event, *args):
413 if event.button == 3:
414 # right click on the mute button, act on all output channels
415 if button.get_active(): # was muted
416 button.set_active(False)
417 if hasattr(button, 'touched_channels'):
418 touched_channels = button.touched_channels
419 for chan in touched_channels:
420 ctlgroup = self.get_control_group(chan)
421 ctlgroup.mute.set_active(False)
422 del button.touched_channels
423 else: # was not muted
424 button.set_active(True)
425 touched_channels = []
426 for chan in self.app.output_channels:
427 ctlgroup = self.get_control_group(chan)
428 if not ctlgroup.mute.get_active():
429 ctlgroup.mute.set_active(True)
430 touched_channels.append(chan)
431 button.touched_channels = touched_channels
435 def on_solo_toggled(self, button):
436 self.channel.solo = self.solo.get_active()
437 self.app.update_monitor(self.app.main_mix)
439 def on_solo_button_pressed(self, button, event, *args):
440 if event.button == 3:
441 # right click on the solo button, act on all output channels
442 if button.get_active(): # was soloed
443 button.set_active(False)
444 if hasattr(button, 'touched_channels'):
445 touched_channels = button.touched_channels
446 for chan in touched_channels:
447 ctlgroup = self.get_control_group(chan)
448 ctlgroup.solo.set_active(False)
449 del button.touched_channels
450 else: # was not soloed
451 button.set_active(True)
452 touched_channels = []
453 for chan in self.app.output_channels:
454 ctlgroup = self.get_control_group(chan)
455 if not ctlgroup.solo.get_active():
456 ctlgroup.solo.set_active(True)
457 touched_channels.append(chan)
458 button.touched_channels = touched_channels
462 def serialization_name(self):
463 return input_channel_serialization_name()
465 def serialize(self, object_backend):
466 object_backend.add_property("name", self.channel_name)
468 object_backend.add_property("type", "stereo")
470 object_backend.add_property("type", "mono")
471 channel.serialize(self, object_backend)
473 def unserialize_property(self, name, value):
475 self.channel_name = str(value)
478 if value == "stereo":
484 return channel.unserialize_property(self, name, value)
486 def input_channel_serialization_name():
487 return "input_channel"
490 available_colours = [
491 ('#ef2929', '#cc0000', '#a40000'),
492 ('#729fcf', '#3465a4', '#204a87'),
493 ('#8aa234', '#73d216', '#4e9a06'),
494 ('#fce84f', '#edd400', '#c4a000'),
495 ('#fcaf3e', '#f57900', '#ce5c00'),
496 ('#e9b96e', '#c17d11', '#8f5902'),
497 ('#ad7fa8', '#75507b', '#5c3566'),
500 class output_channel(channel):
501 colours = available_colours[:]
502 _display_solo_buttons = False
504 _init_muted_channels = None
505 _init_solo_channels = None
507 def __init__(self, app, name, stereo):
508 channel.__init__(self, app, name, stereo)
510 def get_display_solo_buttons(self):
511 return self._display_solo_buttons
513 def set_display_solo_buttons(self, value):
514 self._display_solo_buttons = value
515 # notifying control groups
516 for inputchannel in self.app.channels:
517 inputchannel.update_control_group(self)
519 display_solo_buttons = property(get_display_solo_buttons, set_display_solo_buttons)
522 channel.realize(self)
523 self.channel = self.mixer.add_output_channel(self.channel_name, self.stereo)
524 if self.channel == None:
525 raise Exception,"Cannot create a channel"
526 channel.realize(self)
528 self.channel.midi_scale = self.slider_scale.scale
529 self.channel.midi_change_callback = self.midi_change_callback
531 self.on_volume_changed(self.slider_adjustment)
532 self.on_balance_changed(self.balance_adjustment)
534 # vbox child at upper part
535 self.vbox = gtk.VBox()
536 self.pack_start(self.vbox, False)
537 self.label_name = gtk.Label()
538 self.label_name.set_text(self.channel_name)
539 self.label_name.set_size_request(0, -1)
540 self.label_name_event_box = gtk.EventBox()
541 self.label_name_event_box.connect('button-press-event', self.on_label_mouse)
542 self.label_name_event_box.add(self.label_name)
544 self.colours = available_colours[:]
545 for color in self.colours:
546 self.color_tuple = [gtk.gdk.color_parse(color[x]) for x in range(3)]
547 self.colours.remove(color)
549 self.label_name_event_box.modify_bg(gtk.STATE_NORMAL, self.color_tuple[1])
550 self.vbox.pack_start(self.label_name_event_box, True)
552 frame.set_shadow_type(gtk.SHADOW_IN)
553 frame.add(self.abspeak);
554 self.vbox.pack_start(frame, False)
556 # hbox child at lower part
557 self.hbox = gtk.HBox()
558 self.hbox.pack_start(self.slider, True)
560 frame.set_shadow_type(gtk.SHADOW_IN)
561 frame.add(self.meter);
562 self.hbox.pack_start(frame, True)
564 frame.set_shadow_type(gtk.SHADOW_IN)
565 frame.add(self.hbox);
566 self.pack_start(frame, True)
568 self.volume_digits.set_size_request(0, -1)
569 self.pack_start(self.volume_digits, False)
571 self.create_balance_widget()
573 self.monitor_button = gtk.ToggleButton('MON')
574 self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
575 self.pack_start(self.monitor_button, False, False)
577 # add control groups to the input channels, and initialize them
579 for input_channel in self.app.channels:
580 ctlgroup = input_channel.add_control_group(self)
581 if self._init_muted_channels and input_channel.channel.name in self._init_muted_channels:
582 ctlgroup.mute.set_active(True)
583 if self._init_solo_channels and input_channel.channel.name in self._init_solo_channels:
584 ctlgroup.solo.set_active(True)
585 self._init_muted_channels = None
586 self._init_solo_channels = None
588 channel_properties_dialog = None
589 def on_channel_properties(self):
590 if not self.channel_properties_dialog:
591 self.channel_properties_dialog = OutputChannelPropertiesDialog(self, self.app)
592 self.channel_properties_dialog.show()
593 self.channel_properties_dialog.present()
595 def on_label_mouse(self, widget, event):
596 if event.type == gtk.gdk._2BUTTON_PRESS:
597 if event.button == 1:
598 self.on_channel_properties()
601 channel.unrealize(self)
604 def serialization_name(self):
605 return output_channel_serialization_name()
607 def serialize(self, object_backend):
608 object_backend.add_property("name", self.channel_name)
610 object_backend.add_property("type", "stereo")
612 object_backend.add_property("type", "mono")
613 if self.display_solo_buttons:
614 object_backend.add_property("solo_buttons", "true")
617 for input_channel in self.app.channels:
618 if self.channel.is_muted(input_channel.channel):
619 muted_channels.append(input_channel)
620 if self.channel.is_solo(input_channel.channel):
621 solo_channels.append(input_channel)
623 object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
625 object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
626 channel.serialize(self, object_backend)
628 def unserialize_property(self, name, value):
630 self.channel_name = str(value)
633 if value == "stereo":
639 if name == "solo_buttons":
641 self.display_solo_buttons = True
643 if name == 'muted_channels':
644 self._init_muted_channels = value.split('|')
646 if name == 'solo_channels':
647 self._init_solo_channels = value.split('|')
649 return channel.unserialize_property(self, name, value)
651 def output_channel_serialization_name():
652 return "output_channel"
654 class main_mix(channel):
655 _init_muted_channels = None
656 _init_solo_channels = None
658 def __init__(self, app):
659 channel.__init__(self, app, "MAIN", True)
662 channel.realize(self)
663 self.channel = self.mixer.main_mix_channel
664 self.channel.midi_scale = self.slider_scale.scale
665 self.channel.midi_change_callback = self.midi_change_callback
667 self.on_volume_changed(self.slider_adjustment)
668 self.on_balance_changed(self.balance_adjustment)
670 # vbox child at upper part
671 self.vbox = gtk.VBox()
672 self.pack_start(self.vbox, False)
673 self.label_name = gtk.Label()
674 self.label_name.set_text(self.channel_name)
675 self.label_name.set_size_request(0, -1)
676 self.vbox.pack_start(self.label_name, False)
678 frame.set_shadow_type(gtk.SHADOW_IN)
679 frame.add(self.abspeak);
680 self.vbox.pack_start(frame, False)
682 # hbox child at lower part
683 self.hbox = gtk.HBox()
684 self.hbox.pack_start(self.slider, True)
686 frame.set_shadow_type(gtk.SHADOW_IN)
687 frame.add(self.meter);
688 self.hbox.pack_start(frame, True)
690 frame.set_shadow_type(gtk.SHADOW_IN)
691 frame.add(self.hbox);
692 self.pack_start(frame, True)
694 self.volume_digits.set_size_request(0, -1)
695 self.pack_start(self.volume_digits, False)
697 self.create_balance_widget()
699 self.monitor_button = gtk.ToggleButton('MON')
700 self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
701 self.pack_start(self.monitor_button, False, False)
703 for input_channel in self.app.channels:
704 if self._init_muted_channels and input_channel.channel.name in self._init_muted_channels:
705 input_channel.mute.set_active(True)
706 if self._init_solo_channels and input_channel.channel.name in self._init_solo_channels:
707 input_channel.solo.set_active(True)
708 self._init_muted_channels = None
709 self._init_solo_channels = None
712 channel.unrealize(self)
715 def serialization_name(self):
716 return main_mix_serialization_name()
718 def serialize(self, object_backend):
721 for input_channel in self.app.channels:
722 if input_channel.channel.mute:
723 muted_channels.append(input_channel)
724 if input_channel.channel.solo:
725 solo_channels.append(input_channel)
727 object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
729 object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
730 channel.serialize(self, object_backend)
732 def unserialize_property(self, name, value):
733 if name == 'muted_channels':
734 self._init_muted_channels = value.split('|')
736 if name == 'solo_channels':
737 self._init_solo_channels = value.split('|')
739 return channel.unserialize_property(self, name, value)
741 def main_mix_serialization_name():
742 return "main_mix_channel"
745 class ChannelPropertiesDialog(gtk.Dialog):
748 def __init__(self, parent, app):
749 self.channel = parent
751 self.mixer = self.channel.mixer
752 gtk.Dialog.__init__(self,
753 'Channel "%s" Properties' % self.channel.channel_name,
754 self.channel.gui_factory.topwindow)
756 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
757 self.ok_button = self.add_button(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY)
762 self.connect('response', self.on_response_cb)
763 self.connect('delete-event', self.on_response_cb)
765 def create_frame(self, label, child):
766 frame = gtk.Frame('')
767 frame.set_border_width(3)
768 #frame.set_shadow_type(gtk.SHADOW_NONE)
769 frame.get_label_widget().set_markup('<b>%s</b>' % label)
771 alignment = gtk.Alignment(0, 0, 1, 1)
772 alignment.set_padding(0, 0, 12, 0)
782 table = gtk.Table(2, 2, False)
783 vbox.pack_start(self.create_frame('Properties', table))
784 table.set_row_spacings(5)
785 table.set_col_spacings(5)
787 table.attach(gtk.Label('Name'), 0, 1, 0, 1)
788 self.entry_name = gtk.Entry()
789 self.entry_name.set_activates_default(True)
790 self.entry_name.connect('changed', self.on_entry_name_changed)
791 table.attach(self.entry_name, 1, 2, 0, 1)
793 table.attach(gtk.Label('Mode'), 0, 1, 1, 2)
794 self.mode_hbox = gtk.HBox()
795 table.attach(self.mode_hbox, 1, 2, 1, 2)
796 self.mono = gtk.RadioButton(label='Mono')
797 self.stereo = gtk.RadioButton(label='Stereo', group=self.mono)
798 self.mode_hbox.pack_start(self.mono)
799 self.mode_hbox.pack_start(self.stereo)
801 table = gtk.Table(2, 3, False)
802 vbox.pack_start(self.create_frame('MIDI Control Channels', table))
803 table.set_row_spacings(5)
804 table.set_col_spacings(5)
806 table.attach(gtk.Label('Volume'), 0, 1, 0, 1)
807 self.entry_volume_cc = gtk.Entry()
808 self.entry_volume_cc.set_activates_default(True)
809 self.entry_volume_cc.set_editable(False)
810 self.entry_volume_cc.set_width_chars(3)
811 table.attach(self.entry_volume_cc, 1, 2, 0, 1)
812 self.button_sense_midi_volume = gtk.Button('Autoset')
813 self.button_sense_midi_volume.connect('clicked',
814 self.on_sense_midi_volume_clicked)
815 table.attach(self.button_sense_midi_volume, 2, 3, 0, 1)
817 table.attach(gtk.Label('Balance'), 0, 1, 1, 2)
818 self.entry_balance_cc = gtk.Entry()
819 self.entry_balance_cc.set_activates_default(True)
820 self.entry_balance_cc.set_width_chars(3)
821 self.entry_balance_cc.set_editable(False)
822 table.attach(self.entry_balance_cc, 1, 2, 1, 2)
823 self.button_sense_midi_balance = gtk.Button('Autoset')
824 self.button_sense_midi_balance.connect('clicked',
825 self.on_sense_midi_balance_clicked)
826 table.attach(self.button_sense_midi_balance, 2, 3, 1, 2)
831 self.entry_name.set_text(self.channel.channel_name)
832 if self.channel.channel.is_stereo:
833 self.stereo.set_active(True)
835 self.mono.set_active(True)
836 self.mode_hbox.set_sensitive(False)
837 self.entry_volume_cc.set_text('%s' % self.channel.channel.volume_midi_cc)
838 self.entry_balance_cc.set_text('%s' % self.channel.channel.balance_midi_cc)
840 def sense_popup_dialog(self, entry):
841 window = gtk.Window(gtk.WINDOW_TOPLEVEL)
842 window.set_destroy_with_parent(True)
843 window.set_transient_for(self)
844 window.set_decorated(False)
845 window.set_modal(True)
846 window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
847 window.set_border_width(10)
852 vbox.pack_start(gtk.Label('Please move the MIDI control you want to use for this function.'))
853 timeout_label = gtk.Label('This window will close in 5 seconds')
854 vbox.pack_start(timeout_label)
855 def close_sense_timeout(window, entry):
857 timeout_label.set_text('This window will close in %d seconds.' % window.timeout)
858 if window.timeout == 0:
860 entry.set_text('%s' % self.mixer.last_midi_channel)
864 glib.timeout_add_seconds(1, close_sense_timeout, window, entry)
866 def on_sense_midi_volume_clicked(self, *args):
867 self.sense_popup_dialog(self.entry_volume_cc)
869 def on_sense_midi_balance_clicked(self, *args):
870 self.sense_popup_dialog(self.entry_balance_cc)
872 def on_response_cb(self, dlg, response_id, *args):
873 self.channel.channel_properties_dialog = None
875 if response_id == gtk.RESPONSE_APPLY:
876 name = self.entry_name.get_text()
877 self.channel.channel_name = name
878 self.channel.channel.volume_midi_cc = int(self.entry_volume_cc.get_text())
879 self.channel.channel.balance_midi_cc = int(self.entry_balance_cc.get_text())
881 def on_entry_name_changed(self, entry):
883 if len(entry.get_text()):
884 if self.channel and self.channel.channel.name == entry.get_text():
886 elif entry.get_text() not in [x.channel.name for x in self.app.channels] + \
887 [x.channel.name for x in self.app.output_channels] + ['MAIN']:
889 self.ok_button.set_sensitive(sensitive)
892 class NewChannelDialog(ChannelPropertiesDialog):
893 def __init__(self, app):
894 gtk.Dialog.__init__(self, 'New Channel', app.window)
895 self.mixer = app.mixer
899 self.stereo.set_active(True) # default to stereo
901 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
902 self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
903 self.ok_button.set_sensitive(False)
904 self.set_default_response(gtk.RESPONSE_OK);
906 def get_result(self):
907 return {'name': self.entry_name.get_text(),
908 'stereo': self.stereo.get_active(),
909 'volume_cc': self.entry_volume_cc.get_text(),
910 'balance_cc': self.entry_balance_cc.get_text()
913 class OutputChannelPropertiesDialog(ChannelPropertiesDialog):
915 ChannelPropertiesDialog.create_ui(self)
918 self.vbox.pack_start(self.create_frame('Input Channels', vbox))
920 self.display_solo_buttons = gtk.CheckButton('Display solo buttons')
921 vbox.pack_start(self.display_solo_buttons)
926 ChannelPropertiesDialog.fill_ui(self)
927 self.display_solo_buttons.set_active(self.channel.display_solo_buttons)
929 def on_response_cb(self, dlg, response_id, *args):
930 ChannelPropertiesDialog.on_response_cb(self, dlg, response_id, *args)
931 if response_id == gtk.RESPONSE_APPLY:
932 self.channel.display_solo_buttons = self.display_solo_buttons.get_active()
935 class NewOutputChannelDialog(OutputChannelPropertiesDialog):
936 def __init__(self, app):
937 gtk.Dialog.__init__(self, 'New Output Channel', app.window)
938 self.mixer = app.mixer
942 # TODO: disable mode for output channels as mono output channels may
943 # not be correctly handled yet.
944 self.mode_hbox.set_sensitive(False)
945 self.stereo.set_active(True) # default to stereo
947 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
948 self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
949 self.ok_button.set_sensitive(False)
950 self.set_default_response(gtk.RESPONSE_OK);
952 def get_result(self):
953 return {'name': self.entry_name.get_text(),
954 'stereo': self.stereo.get_active(),
955 'volume_cc': self.entry_volume_cc.get_text(),
956 'balance_cc': self.entry_balance_cc.get_text(),
957 'display_solo_buttons': self.display_solo_buttons.get_active(),
961 class ControlGroup(gtk.Alignment):
962 def __init__(self, output_channel, input_channel):
963 gtk.Alignment.__init__(self, 0.5, 0.5, 0, 0)
964 self.output_channel = output_channel
965 self.input_channel = input_channel
966 self.app = input_channel.app
972 mute = gtk.ToggleButton()
975 mute.connect("toggled", self.on_mute_toggled)
976 hbox.pack_start(mute, False)
978 solo = gtk.ToggleButton()
981 solo.connect("toggled", self.on_solo_toggled)
982 if self.output_channel.display_solo_buttons:
983 hbox.pack_start(solo, True)
985 mute.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
986 mute.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
987 mute.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
988 solo.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
989 solo.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
990 solo.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
993 if self.output_channel.display_solo_buttons:
994 if not self.solo in self.hbox.get_children():
995 self.hbox.pack_start(self.solo, True)
998 if self.solo in self.hbox.get_children():
999 self.hbox.remove(self.solo)
1001 def on_mute_toggled(self, button):
1002 self.output_channel.channel.set_muted(self.input_channel.channel, button.get_active())
1003 self.app.update_monitor(self)
1005 def on_solo_toggled(self, button):
1006 self.output_channel.channel.set_solo(self.input_channel.channel, button.get_active())
1007 self.app.update_monitor(self)