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.
26 from serialization import SerializedObject
34 class Channel(gtk.VBox, SerializedObject):
35 '''Widget with slider and meter used as base class for more specific
39 def __init__(self, app, name, stereo):
40 gtk.VBox.__init__(self)
42 self.mixer = app.mixer
43 self.gui_factory = app.gui_factory
44 self._channel_name = name
46 self.meter_scale = self.gui_factory.get_default_meter_scale()
47 self.slider_scale = self.gui_factory.get_default_slider_scale()
48 self.slider_adjustment = slider.AdjustmentdBFS(self.slider_scale, 0.0)
49 self.balance_adjustment = gtk.Adjustment(0.0, -1.0, 1.0, 0.02)
50 self.future_volume_midi_cc = None
51 self.future_balance_midi_cc = None
53 def get_channel_name(self):
54 return self._channel_name
58 def set_channel_name(self, name):
59 self._channel_name = name
61 self.label_name.set_text(name)
63 self.channel.name = name
64 channel_name = property(get_channel_name, set_channel_name)
67 #print "Realizing channel \"%s\"" % self.channel_name
69 self.slider_adjustment.connect("volume-changed", self.on_volume_changed)
70 self.balance_adjustment.connect("value-changed", self.on_balance_changed)
71 self.connect('midi-event-received', self.on_midi_event_received)
74 self.create_slider_widget()
77 self.meter = meter.StereoMeterWidget(self.meter_scale)
79 self.meter = meter.MonoMeterWidget(self.meter_scale)
80 self.on_vumeter_color_changed(self.gui_factory)
82 self.meter.set_events(gtk.gdk.SCROLL_MASK)
84 self.gui_factory.connect("default-meter-scale-changed", self.on_default_meter_scale_changed)
85 self.gui_factory.connect("default-slider-scale-changed", self.on_default_slider_scale_changed)
86 self.gui_factory.connect('vumeter-color-changed', self.on_vumeter_color_changed)
87 self.gui_factory.connect('vumeter-color-scheme-changed', self.on_vumeter_color_changed)
88 self.gui_factory.connect('use-custom-widgets-changed', self.on_custom_widgets_changed)
90 self.abspeak = abspeak.AbspeakWidget()
91 self.abspeak.connect("reset", self.on_abspeak_reset)
92 self.abspeak.connect("volume-adjust", self.on_abspeak_adjust)
94 self.volume_digits = gtk.Entry()
95 self.volume_digits.connect("key-press-event", self.on_volume_digits_key_pressed)
96 self.volume_digits.connect("focus-out-event", self.on_volume_digits_focus_out)
98 self.connect("key-press-event", self.on_key_pressed)
99 self.connect("scroll-event", self.on_scroll)
102 #print "Unrealizing channel \"%s\"" % self.channel_name
105 def create_balance_widget(self):
106 if self.gui_factory.use_custom_widgets and phat:
107 self.balance = phat.HFanSlider()
108 self.balance.set_default_value(0)
109 self.balance.set_adjustment(self.balance_adjustment)
111 self.balance = gtk.HScale(self.balance_adjustment)
112 self.balance.set_draw_value(False)
113 self.pack_start(self.balance, False)
114 if self.monitor_button:
115 self.reorder_child(self.monitor_button, -1)
118 def create_slider_widget(self):
121 parent = self.slider.get_parent()
122 self.slider.destroy()
123 if self.gui_factory.use_custom_widgets:
124 self.slider = slider.CustomSliderWidget(self.slider_adjustment)
126 self.slider = slider.GtkSlider(self.slider_adjustment)
128 parent.pack_start(self.slider)
129 parent.reorder_child(self.slider, 0)
132 def on_default_meter_scale_changed(self, gui_factory, scale):
133 #print "Default meter scale change detected."
134 self.meter.set_scale(scale)
136 def on_default_slider_scale_changed(self, gui_factory, scale):
137 #print "Default slider scale change detected."
138 self.slider_scale = scale
139 self.slider_adjustment.set_scale(scale)
140 self.channel.midi_scale = self.slider_scale.scale
142 def on_vumeter_color_changed(self, gui_factory, *args):
143 color = gui_factory.get_vumeter_color()
144 color_scheme = gui_factory.get_vumeter_color_scheme()
145 if color_scheme != 'solid':
146 self.meter.set_color(None)
148 self.meter.set_color(gtk.gdk.color_parse(color))
150 def on_custom_widgets_changed(self, gui_factory, value):
151 self.balance.destroy()
152 self.create_balance_widget()
153 self.create_slider_widget()
155 def on_abspeak_adjust(self, abspeak, adjust):
156 #print "abspeak adjust %f" % adjust
157 self.slider_adjustment.set_value_db(self.slider_adjustment.get_value_db() + adjust)
158 self.channel.abspeak = None
159 #self.update_volume(False) # We want to update gui even if actual decibels have not changed (scale wrap for example)
161 def on_abspeak_reset(self, abspeak):
162 #print "abspeak reset"
163 self.channel.abspeak = None
165 def on_volume_digits_key_pressed(self, widget, event):
166 if (event.keyval == gtk.keysyms.Return or event.keyval == gtk.keysyms.KP_Enter):
167 db_text = self.volume_digits.get_text()
170 #print "Volume digits confirmation \"%f dBFS\"" % db
171 except (ValueError), e:
172 #print "Volume digits confirmation ignore, reset to current"
173 self.update_volume(False)
175 self.slider_adjustment.set_value_db(db)
177 #self.update_volume(False) # We want to update gui even if actual decibels have not changed (scale wrap for example)
179 def on_volume_digits_focus_out(self, widget, event):
180 #print "volume digits focus out detected"
181 self.update_volume(False)
183 def read_meter(self):
187 meter_left, meter_right = self.channel.meter
188 self.meter.set_values(meter_left, meter_right)
190 self.meter.set_value(self.channel.meter[0])
192 self.abspeak.set_peak(self.channel.abspeak)
194 def on_scroll(self, widget, event):
195 if event.direction == gtk.gdk.SCROLL_DOWN:
196 self.slider_adjustment.step_down()
197 elif event.direction == gtk.gdk.SCROLL_UP:
198 self.slider_adjustment.step_up()
201 def update_volume(self, update_engine):
202 db = self.slider_adjustment.get_value_db()
204 db_text = "%.2f" % db
205 self.volume_digits.set_text(db_text)
208 #print "Setting engine volume to " + db_text
209 self.channel.volume = db
210 self.app.update_monitor(self)
212 def on_volume_changed(self, adjustment):
213 self.update_volume(True)
215 def on_balance_changed(self, adjustment):
216 balance = self.balance_adjustment.get_value()
217 #print "%s balance: %f" % (self.channel_name, balance)
218 self.channel.balance = balance
219 self.app.update_monitor(self)
221 def on_key_pressed(self, widget, event):
222 if (event.keyval == gtk.keysyms.Up):
223 #print self.channel_name + " Up"
224 self.slider_adjustment.step_up()
226 elif (event.keyval == gtk.keysyms.Down):
227 #print self.channel_name + " Down"
228 self.slider_adjustment.step_down()
233 def serialize(self, object_backend):
234 object_backend.add_property("volume", "%f" % self.slider_adjustment.get_value_db())
235 object_backend.add_property("balance", "%f" % self.balance_adjustment.get_value())
237 if self.channel.volume_midi_cc:
238 object_backend.add_property('volume_midi_cc', str(self.channel.volume_midi_cc))
239 if self.channel.balance_midi_cc:
240 object_backend.add_property('balance_midi_cc', str(self.channel.balance_midi_cc))
242 def unserialize_property(self, name, value):
244 self.slider_adjustment.set_value_db(float(value))
246 if name == "balance":
247 self.balance_adjustment.set_value(float(value))
249 if name == 'volume_midi_cc':
250 self.future_volume_midi_cc = int(value)
252 if name == 'balance_midi_cc':
253 self.future_balance_midi_cc = int(value)
257 def on_midi_event_received(self, *args):
258 self.slider_adjustment.set_value_db(self.channel.volume)
259 self.balance_adjustment.set_value(self.channel.balance)
261 def midi_change_callback(self, *args):
262 # the changes are not applied directly to the widgets as they
263 # absolutely have to be done from the gtk thread.
264 self.emit('midi-event-received')
266 def on_monitor_button_toggled(self, button):
267 if not button.get_active():
268 self.app.main_mix.monitor_button.set_active(True)
270 for channel in self.app.channels + self.app.output_channels + [self.app.main_mix]:
271 if channel.monitor_button.get_active() and channel.monitor_button is not button:
272 channel.monitor_button.handler_block_by_func(
273 channel.on_monitor_button_toggled)
274 channel.monitor_button.set_active(False)
275 channel.monitor_button.handler_unblock_by_func(
276 channel.on_monitor_button_toggled)
277 self.app.set_monitored_channel(self)
279 def set_monitored(self):
281 self.app.set_monitored_channel(self)
282 self.monitor_button.set_active(True)
284 gobject.signal_new('midi-event-received', Channel,
285 gobject.SIGNAL_RUN_FIRST | gobject.SIGNAL_ACTION,
286 gobject.TYPE_NONE, ())
288 class InputChannel(Channel):
289 def __init__(self, app, name, stereo):
290 Channel.__init__(self, app, name, stereo)
293 self.channel = self.mixer.add_channel(self.channel_name, self.stereo)
294 if self.channel == None:
295 raise Exception,"Cannot create a channel"
296 Channel.realize(self)
297 if self.future_volume_midi_cc:
298 self.channel.volume_midi_cc = self.future_volume_midi_cc
299 if self.future_balance_midi_cc:
300 self.channel.balance_midi_cc = self.future_balance_midi_cc
301 self.channel.midi_scale = self.slider_scale.scale
302 self.channel.midi_change_callback = self.midi_change_callback
304 self.on_volume_changed(self.slider_adjustment)
305 self.on_balance_changed(self.balance_adjustment)
307 # vbox child at upper part
308 self.vbox = gtk.VBox()
309 self.pack_start(self.vbox, False)
310 self.label_name = gtk.Label()
311 self.label_name.set_text(self.channel_name)
312 self.label_name.set_size_request(0, -1)
313 self.label_name_event_box = gtk.EventBox()
314 self.label_name_event_box.connect("button-press-event", self.on_label_mouse)
315 self.label_name_event_box.add(self.label_name)
316 self.vbox.pack_start(self.label_name_event_box, True)
317 # self.label_stereo = gtk.Label()
319 # self.label_stereo.set_text("stereo")
321 # self.label_stereo.set_text("mono")
322 # self.label_stereo.set_size_request(0, -1)
323 # self.vbox.pack_start(self.label_stereo, True)
325 # hbox for mute and solo buttons
326 self.hbox_mutesolo = gtk.HBox()
328 self.mute = gtk.ToggleButton()
329 self.mute.set_label("M")
330 self.mute.set_active(self.channel.mute)
331 self.mute.connect("button-press-event", self.on_mute_button_pressed)
332 self.mute.connect("toggled", self.on_mute_toggled)
333 self.hbox_mutesolo.pack_start(self.mute, True)
335 self.solo = gtk.ToggleButton()
336 self.solo.set_label("S")
337 self.solo.set_active(self.channel.solo)
338 self.solo.connect("button-press-event", self.on_solo_button_pressed)
339 self.solo.connect("toggled", self.on_solo_toggled)
340 self.hbox_mutesolo.pack_start(self.solo, True)
342 self.vbox.pack_start(self.hbox_mutesolo, False)
345 frame.set_shadow_type(gtk.SHADOW_IN)
346 frame.add(self.abspeak);
347 self.pack_start(frame, False)
349 # hbox child at lower part
350 self.hbox = gtk.HBox()
351 self.hbox.pack_start(self.slider, True)
353 frame.set_shadow_type(gtk.SHADOW_IN)
354 frame.add(self.meter);
355 self.hbox.pack_start(frame, True)
357 frame.set_shadow_type(gtk.SHADOW_IN)
358 frame.add(self.hbox);
359 self.pack_start(frame, True)
361 self.volume_digits.set_size_request(0, -1)
362 self.pack_start(self.volume_digits, False)
364 self.create_balance_widget()
366 self.monitor_button = gtk.ToggleButton('MON')
367 self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
368 self.pack_start(self.monitor_button, False, False)
370 def add_control_group(self, channel):
371 control_group = ControlGroup(channel, self)
372 control_group.show_all()
373 self.vbox.pack_start(control_group, False)
376 def remove_control_group(self, channel):
377 ctlgroup = self.get_control_group(channel)
378 self.vbox.remove(ctlgroup)
380 def update_control_group(self, channel):
381 for control_group in self.vbox.get_children():
382 if isinstance(control_group, ControlGroup):
383 if control_group.output_channel is channel:
384 control_group.update()
386 def get_control_group(self, channel):
387 for control_group in self.vbox.get_children():
388 if isinstance(control_group, ControlGroup):
389 if control_group.output_channel is channel:
394 Channel.unrealize(self)
395 self.channel.remove()
398 channel_properties_dialog = None
399 def on_channel_properties(self):
400 if not self.channel_properties_dialog:
401 self.channel_properties_dialog = ChannelPropertiesDialog(self, self.app)
402 self.channel_properties_dialog.show()
403 self.channel_properties_dialog.present()
405 def on_label_mouse(self, widget, event):
406 if event.type == gtk.gdk._2BUTTON_PRESS:
407 if event.button == 1:
408 self.on_channel_properties()
410 def on_mute_toggled(self, button):
411 self.channel.mute = self.mute.get_active()
412 self.app.update_monitor(self.app.main_mix)
414 def on_mute_button_pressed(self, button, event, *args):
415 if event.button == 3:
416 # right click on the mute button, act on all output channels
417 if button.get_active(): # was muted
418 button.set_active(False)
419 if hasattr(button, 'touched_channels'):
420 touched_channels = button.touched_channels
421 for chan in touched_channels:
422 ctlgroup = self.get_control_group(chan)
423 ctlgroup.mute.set_active(False)
424 del button.touched_channels
425 else: # was not muted
426 button.set_active(True)
427 touched_channels = []
428 for chan in self.app.output_channels:
429 ctlgroup = self.get_control_group(chan)
430 if not ctlgroup.mute.get_active():
431 ctlgroup.mute.set_active(True)
432 touched_channels.append(chan)
433 button.touched_channels = touched_channels
437 def on_solo_toggled(self, button):
438 self.channel.solo = self.solo.get_active()
439 self.app.update_monitor(self.app.main_mix)
441 def on_solo_button_pressed(self, button, event, *args):
442 if event.button == 3:
443 # right click on the solo button, act on all output channels
444 if button.get_active(): # was soloed
445 button.set_active(False)
446 if hasattr(button, 'touched_channels'):
447 touched_channels = button.touched_channels
448 for chan in touched_channels:
449 ctlgroup = self.get_control_group(chan)
450 ctlgroup.solo.set_active(False)
451 del button.touched_channels
452 else: # was not soloed
453 button.set_active(True)
454 touched_channels = []
455 for chan in self.app.output_channels:
456 ctlgroup = self.get_control_group(chan)
457 if not ctlgroup.solo.get_active():
458 ctlgroup.solo.set_active(True)
459 touched_channels.append(chan)
460 button.touched_channels = touched_channels
464 def serialization_name(self):
465 return input_channel_serialization_name()
467 def serialize(self, object_backend):
468 object_backend.add_property("name", self.channel_name)
470 object_backend.add_property("type", "stereo")
472 object_backend.add_property("type", "mono")
473 Channel.serialize(self, object_backend)
475 def unserialize_property(self, name, value):
477 self.channel_name = str(value)
480 if value == "stereo":
486 return Channel.unserialize_property(self, name, value)
488 def input_channel_serialization_name():
489 return "input_channel"
492 available_colours = [
493 ('#ef2929', '#cc0000', '#840000'),
494 ('#729fcf', '#3465a4', '#204a67'),
495 ('#8aa234', '#73d216', '#4e7a06'),
496 ('#fce84f', '#edd400', '#c48000'),
497 ('#fcaf3e', '#f57900', '#ae5c00'),
498 ('#ad7fa8', '#75507b', '#4c3556'),
499 ('#e9b96e', '#c17d11', '#6f4902'),
502 class OutputChannel(Channel):
503 colours = available_colours[:]
504 _display_solo_buttons = False
506 _init_muted_channels = None
507 _init_solo_channels = None
509 def __init__(self, app, name, stereo):
510 Channel.__init__(self, app, name, stereo)
512 def get_display_solo_buttons(self):
513 return self._display_solo_buttons
515 def set_display_solo_buttons(self, value):
516 self._display_solo_buttons = value
517 # notifying control groups
518 for inputchannel in self.app.channels:
519 inputchannel.update_control_group(self)
521 display_solo_buttons = property(get_display_solo_buttons, set_display_solo_buttons)
524 Channel.realize(self)
525 self.channel = self.mixer.add_output_channel(self.channel_name, self.stereo)
526 if self.channel == None:
527 raise Exception,"Cannot create a channel"
528 Channel.realize(self)
530 self.channel.midi_scale = self.slider_scale.scale
531 self.channel.midi_change_callback = self.midi_change_callback
533 self.on_volume_changed(self.slider_adjustment)
534 self.on_balance_changed(self.balance_adjustment)
536 # vbox child at upper part
537 self.vbox = gtk.VBox()
538 self.pack_start(self.vbox, False)
539 self.label_name = gtk.Label()
540 self.label_name.set_text(self.channel_name)
541 self.label_name.set_size_request(0, -1)
542 self.label_name_event_box = gtk.EventBox()
543 self.label_name_event_box.connect('button-press-event', self.on_label_mouse)
544 self.label_name_event_box.add(self.label_name)
546 OutputChannel.colours = available_colours[:]
547 for color in self.colours:
548 self.color_tuple = [gtk.gdk.color_parse(color[x]) for x in range(3)]
549 self.colours.remove(color)
551 self.label_name_event_box.modify_bg(gtk.STATE_NORMAL, self.color_tuple[1])
552 self.vbox.pack_start(self.label_name_event_box, True)
554 frame.set_shadow_type(gtk.SHADOW_IN)
555 frame.add(self.abspeak);
556 self.vbox.pack_start(frame, False)
558 # hbox child at lower part
559 self.hbox = gtk.HBox()
560 self.hbox.pack_start(self.slider, True)
562 frame.set_shadow_type(gtk.SHADOW_IN)
563 frame.add(self.meter);
564 self.hbox.pack_start(frame, True)
566 frame.set_shadow_type(gtk.SHADOW_IN)
567 frame.add(self.hbox);
568 self.pack_start(frame, True)
570 self.volume_digits.set_size_request(0, -1)
571 self.pack_start(self.volume_digits, False)
573 self.create_balance_widget()
575 self.monitor_button = gtk.ToggleButton('MON')
576 self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
577 self.pack_start(self.monitor_button, False, False)
579 # add control groups to the input channels, and initialize them
581 for input_channel in self.app.channels:
582 ctlgroup = input_channel.add_control_group(self)
583 if self._init_muted_channels and input_channel.channel.name in self._init_muted_channels:
584 ctlgroup.mute.set_active(True)
585 if self._init_solo_channels and input_channel.channel.name in self._init_solo_channels:
586 ctlgroup.solo.set_active(True)
587 self._init_muted_channels = None
588 self._init_solo_channels = None
590 channel_properties_dialog = None
591 def on_channel_properties(self):
592 if not self.channel_properties_dialog:
593 self.channel_properties_dialog = OutputChannelPropertiesDialog(self, self.app)
594 self.channel_properties_dialog.show()
595 self.channel_properties_dialog.present()
597 def on_label_mouse(self, widget, event):
598 if event.type == gtk.gdk._2BUTTON_PRESS:
599 if event.button == 1:
600 self.on_channel_properties()
603 # remove control groups from input channels
604 for input_channel in self.app.channels:
605 input_channel.remove_control_group(self)
607 Channel.unrealize(self)
608 self.channel.remove()
611 def serialization_name(self):
612 return output_channel_serialization_name()
614 def serialize(self, object_backend):
615 object_backend.add_property("name", self.channel_name)
617 object_backend.add_property("type", "stereo")
619 object_backend.add_property("type", "mono")
620 if self.display_solo_buttons:
621 object_backend.add_property("solo_buttons", "true")
624 for input_channel in self.app.channels:
625 if self.channel.is_muted(input_channel.channel):
626 muted_channels.append(input_channel)
627 if self.channel.is_solo(input_channel.channel):
628 solo_channels.append(input_channel)
630 object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
632 object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
633 Channel.serialize(self, object_backend)
635 def unserialize_property(self, name, value):
637 self.channel_name = str(value)
640 if value == "stereo":
646 if name == "solo_buttons":
648 self.display_solo_buttons = True
650 if name == 'muted_channels':
651 self._init_muted_channels = value.split('|')
653 if name == 'solo_channels':
654 self._init_solo_channels = value.split('|')
656 return Channel.unserialize_property(self, name, value)
658 def output_channel_serialization_name():
659 return "output_channel"
661 class MainMixChannel(Channel):
662 _init_muted_channels = None
663 _init_solo_channels = None
665 def __init__(self, app):
666 Channel.__init__(self, app, "MAIN", True)
669 Channel.realize(self)
670 self.channel = self.mixer.main_mix_channel
671 self.channel.midi_scale = self.slider_scale.scale
672 self.channel.midi_change_callback = self.midi_change_callback
674 self.on_volume_changed(self.slider_adjustment)
675 self.on_balance_changed(self.balance_adjustment)
677 # vbox child at upper part
678 self.vbox = gtk.VBox()
679 self.pack_start(self.vbox, False)
680 self.label_name = gtk.Label()
681 self.label_name.set_text(self.channel_name)
682 self.label_name.set_size_request(0, -1)
683 self.vbox.pack_start(self.label_name, False)
685 frame.set_shadow_type(gtk.SHADOW_IN)
686 frame.add(self.abspeak);
687 self.vbox.pack_start(frame, False)
689 # hbox child at lower part
690 self.hbox = gtk.HBox()
691 self.hbox.pack_start(self.slider, True)
693 frame.set_shadow_type(gtk.SHADOW_IN)
694 frame.add(self.meter);
695 self.hbox.pack_start(frame, True)
697 frame.set_shadow_type(gtk.SHADOW_IN)
698 frame.add(self.hbox);
699 self.pack_start(frame, True)
701 self.volume_digits.set_size_request(0, -1)
702 self.pack_start(self.volume_digits, False)
704 self.create_balance_widget()
706 self.monitor_button = gtk.ToggleButton('MON')
707 self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
708 self.pack_start(self.monitor_button, False, False)
710 for input_channel in self.app.channels:
711 if self._init_muted_channels and input_channel.channel.name in self._init_muted_channels:
712 input_channel.mute.set_active(True)
713 if self._init_solo_channels and input_channel.channel.name in self._init_solo_channels:
714 input_channel.solo.set_active(True)
715 self._init_muted_channels = None
716 self._init_solo_channels = None
719 Channel.unrealize(self)
722 def serialization_name(self):
723 return main_mix_serialization_name()
725 def serialize(self, object_backend):
728 for input_channel in self.app.channels:
729 if input_channel.channel.mute:
730 muted_channels.append(input_channel)
731 if input_channel.channel.solo:
732 solo_channels.append(input_channel)
734 object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
736 object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
737 Channel.serialize(self, object_backend)
739 def unserialize_property(self, name, value):
740 if name == 'muted_channels':
741 self._init_muted_channels = value.split('|')
743 if name == 'solo_channels':
744 self._init_solo_channels = value.split('|')
746 return Channel.unserialize_property(self, name, value)
748 def main_mix_serialization_name():
749 return "main_mix_channel"
752 class ChannelPropertiesDialog(gtk.Dialog):
755 def __init__(self, parent, app):
756 self.channel = parent
758 self.mixer = self.channel.mixer
759 gtk.Dialog.__init__(self,
760 'Channel "%s" Properties' % self.channel.channel_name,
761 self.channel.gui_factory.topwindow)
763 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
764 self.ok_button = self.add_button(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY)
769 self.connect('response', self.on_response_cb)
770 self.connect('delete-event', self.on_response_cb)
772 def create_frame(self, label, child):
773 frame = gtk.Frame('')
774 frame.set_border_width(3)
775 #frame.set_shadow_type(gtk.SHADOW_NONE)
776 frame.get_label_widget().set_markup('<b>%s</b>' % label)
778 alignment = gtk.Alignment(0, 0, 1, 1)
779 alignment.set_padding(0, 0, 12, 0)
789 table = gtk.Table(2, 2, False)
790 vbox.pack_start(self.create_frame('Properties', table))
791 table.set_row_spacings(5)
792 table.set_col_spacings(5)
794 table.attach(gtk.Label('Name'), 0, 1, 0, 1)
795 self.entry_name = gtk.Entry()
796 self.entry_name.set_activates_default(True)
797 self.entry_name.connect('changed', self.on_entry_name_changed)
798 table.attach(self.entry_name, 1, 2, 0, 1)
800 table.attach(gtk.Label('Mode'), 0, 1, 1, 2)
801 self.mode_hbox = gtk.HBox()
802 table.attach(self.mode_hbox, 1, 2, 1, 2)
803 self.mono = gtk.RadioButton(label='Mono')
804 self.stereo = gtk.RadioButton(label='Stereo', group=self.mono)
805 self.mode_hbox.pack_start(self.mono)
806 self.mode_hbox.pack_start(self.stereo)
808 table = gtk.Table(2, 3, False)
809 vbox.pack_start(self.create_frame('MIDI Control Channels', table))
810 table.set_row_spacings(5)
811 table.set_col_spacings(5)
813 table.attach(gtk.Label('Volume'), 0, 1, 0, 1)
814 self.entry_volume_cc = gtk.Entry()
815 self.entry_volume_cc.set_activates_default(True)
816 self.entry_volume_cc.set_editable(False)
817 self.entry_volume_cc.set_width_chars(3)
818 table.attach(self.entry_volume_cc, 1, 2, 0, 1)
819 self.button_sense_midi_volume = gtk.Button('Autoset')
820 self.button_sense_midi_volume.connect('clicked',
821 self.on_sense_midi_volume_clicked)
822 table.attach(self.button_sense_midi_volume, 2, 3, 0, 1)
824 table.attach(gtk.Label('Balance'), 0, 1, 1, 2)
825 self.entry_balance_cc = gtk.Entry()
826 self.entry_balance_cc.set_activates_default(True)
827 self.entry_balance_cc.set_width_chars(3)
828 self.entry_balance_cc.set_editable(False)
829 table.attach(self.entry_balance_cc, 1, 2, 1, 2)
830 self.button_sense_midi_balance = gtk.Button('Autoset')
831 self.button_sense_midi_balance.connect('clicked',
832 self.on_sense_midi_balance_clicked)
833 table.attach(self.button_sense_midi_balance, 2, 3, 1, 2)
838 self.entry_name.set_text(self.channel.channel_name)
839 if self.channel.channel.is_stereo:
840 self.stereo.set_active(True)
842 self.mono.set_active(True)
843 self.mode_hbox.set_sensitive(False)
844 self.entry_volume_cc.set_text('%s' % self.channel.channel.volume_midi_cc)
845 self.entry_balance_cc.set_text('%s' % self.channel.channel.balance_midi_cc)
847 def sense_popup_dialog(self, entry):
848 window = gtk.Window(gtk.WINDOW_TOPLEVEL)
849 window.set_destroy_with_parent(True)
850 window.set_transient_for(self)
851 window.set_decorated(False)
852 window.set_modal(True)
853 window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
854 window.set_border_width(10)
859 vbox.pack_start(gtk.Label('Please move the MIDI control you want to use for this function.'))
860 timeout_label = gtk.Label('This window will close in 5 seconds')
861 vbox.pack_start(timeout_label)
862 def close_sense_timeout(window, entry):
864 timeout_label.set_text('This window will close in %d seconds.' % window.timeout)
865 if window.timeout == 0:
867 entry.set_text('%s' % self.mixer.last_midi_channel)
871 glib.timeout_add_seconds(1, close_sense_timeout, window, entry)
873 def on_sense_midi_volume_clicked(self, *args):
874 self.sense_popup_dialog(self.entry_volume_cc)
876 def on_sense_midi_balance_clicked(self, *args):
877 self.sense_popup_dialog(self.entry_balance_cc)
879 def on_response_cb(self, dlg, response_id, *args):
880 self.channel.channel_properties_dialog = None
882 if response_id == gtk.RESPONSE_APPLY:
883 name = self.entry_name.get_text()
884 self.channel.channel_name = name
885 self.channel.channel.volume_midi_cc = int(self.entry_volume_cc.get_text())
886 self.channel.channel.balance_midi_cc = int(self.entry_balance_cc.get_text())
888 def on_entry_name_changed(self, entry):
890 if len(entry.get_text()):
891 if self.channel and self.channel.channel.name == entry.get_text():
893 elif entry.get_text() not in [x.channel.name for x in self.app.channels] + \
894 [x.channel.name for x in self.app.output_channels] + ['MAIN']:
896 self.ok_button.set_sensitive(sensitive)
899 class NewChannelDialog(ChannelPropertiesDialog):
900 def __init__(self, app):
901 gtk.Dialog.__init__(self, 'New Channel', app.window)
902 self.mixer = app.mixer
906 self.stereo.set_active(True) # default to stereo
908 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
909 self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
910 self.ok_button.set_sensitive(False)
911 self.set_default_response(gtk.RESPONSE_OK);
913 def get_result(self):
914 return {'name': self.entry_name.get_text(),
915 'stereo': self.stereo.get_active(),
916 'volume_cc': self.entry_volume_cc.get_text(),
917 'balance_cc': self.entry_balance_cc.get_text()
920 class OutputChannelPropertiesDialog(ChannelPropertiesDialog):
922 ChannelPropertiesDialog.create_ui(self)
925 self.vbox.pack_start(self.create_frame('Input Channels', vbox))
927 self.display_solo_buttons = gtk.CheckButton('Display solo buttons')
928 vbox.pack_start(self.display_solo_buttons)
933 ChannelPropertiesDialog.fill_ui(self)
934 self.display_solo_buttons.set_active(self.channel.display_solo_buttons)
936 def on_response_cb(self, dlg, response_id, *args):
937 ChannelPropertiesDialog.on_response_cb(self, dlg, response_id, *args)
938 if response_id == gtk.RESPONSE_APPLY:
939 self.channel.display_solo_buttons = self.display_solo_buttons.get_active()
942 class NewOutputChannelDialog(OutputChannelPropertiesDialog):
943 def __init__(self, app):
944 gtk.Dialog.__init__(self, 'New Output Channel', app.window)
945 self.mixer = app.mixer
949 # TODO: disable mode for output channels as mono output channels may
950 # not be correctly handled yet.
951 self.mode_hbox.set_sensitive(False)
952 self.stereo.set_active(True) # default to stereo
954 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
955 self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
956 self.ok_button.set_sensitive(False)
957 self.set_default_response(gtk.RESPONSE_OK);
959 def get_result(self):
960 return {'name': self.entry_name.get_text(),
961 'stereo': self.stereo.get_active(),
962 'volume_cc': self.entry_volume_cc.get_text(),
963 'balance_cc': self.entry_balance_cc.get_text(),
964 'display_solo_buttons': self.display_solo_buttons.get_active(),
968 class ControlGroup(gtk.Alignment):
969 def __init__(self, output_channel, input_channel):
970 gtk.Alignment.__init__(self, 0.5, 0.5, 0, 0)
971 self.output_channel = output_channel
972 self.input_channel = input_channel
973 self.app = input_channel.app
979 mute = gtk.ToggleButton()
982 mute.connect("toggled", self.on_mute_toggled)
983 hbox.pack_start(mute, False)
985 solo = gtk.ToggleButton()
988 solo.connect("toggled", self.on_solo_toggled)
989 if self.output_channel.display_solo_buttons:
990 hbox.pack_start(solo, True)
992 mute.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
993 mute.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
994 mute.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
995 solo.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
996 solo.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
997 solo.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
1000 if self.output_channel.display_solo_buttons:
1001 if not self.solo in self.hbox.get_children():
1002 self.hbox.pack_start(self.solo, True)
1005 if self.solo in self.hbox.get_children():
1006 self.hbox.remove(self.solo)
1008 def on_mute_toggled(self, button):
1009 self.output_channel.channel.set_muted(self.input_channel.channel, button.get_active())
1010 self.app.update_monitor(self)
1012 def on_solo_toggled(self, button):
1013 self.output_channel.channel.set_solo(self.input_channel.channel, button.get_active())
1014 self.app.update_monitor(self)