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.
24 from serialization import SerializedObject
32 class Channel(gtk.VBox, SerializedObject):
33 '''Widget with slider and meter used as base class for more specific
37 def __init__(self, app, name, stereo):
38 gtk.VBox.__init__(self)
40 self.mixer = app.mixer
41 self.gui_factory = app.gui_factory
42 self._channel_name = name
44 self.meter_scale = self.gui_factory.get_default_meter_scale()
45 self.slider_scale = self.gui_factory.get_default_slider_scale()
46 self.slider_adjustment = slider.AdjustmentdBFS(self.slider_scale, 0.0)
47 self.balance_adjustment = gtk.Adjustment(0.0, -1.0, 1.0, 0.02)
48 self.future_volume_midi_cc = None
49 self.future_balance_midi_cc = None
51 def get_channel_name(self):
52 return self._channel_name
56 def set_channel_name(self, name):
57 self.app.on_channel_rename(self._channel_name, name);
58 self._channel_name = name
60 self.label_name.set_text(name)
62 self.channel.name = name
63 if self.post_fader_output_channel:
64 self.post_fader_output_channel.name = "%s Out" % name;
65 channel_name = property(get_channel_name, set_channel_name)
68 #print "Realizing channel \"%s\"" % self.channel_name
70 self.slider_adjustment.connect("volume-changed", self.on_volume_changed)
71 self.balance_adjustment.connect("value-changed", self.on_balance_changed)
72 self.connect('midi-event-received', self.on_midi_event_received)
75 self.create_slider_widget()
78 self.meter = meter.StereoMeterWidget(self.meter_scale)
80 self.meter = meter.MonoMeterWidget(self.meter_scale)
81 self.on_vumeter_color_changed(self.gui_factory)
83 self.meter.set_events(gtk.gdk.SCROLL_MASK)
85 self.gui_factory.connect("default-meter-scale-changed", self.on_default_meter_scale_changed)
86 self.gui_factory.connect("default-slider-scale-changed", self.on_default_slider_scale_changed)
87 self.gui_factory.connect('vumeter-color-changed', self.on_vumeter_color_changed)
88 self.gui_factory.connect('vumeter-color-scheme-changed', self.on_vumeter_color_changed)
89 self.gui_factory.connect('use-custom-widgets-changed', self.on_custom_widgets_changed)
91 self.abspeak = abspeak.AbspeakWidget()
92 self.abspeak.connect("reset", self.on_abspeak_reset)
93 self.abspeak.connect("volume-adjust", self.on_abspeak_adjust)
95 self.volume_digits = gtk.Entry()
96 self.volume_digits.connect("key-press-event", self.on_volume_digits_key_pressed)
97 self.volume_digits.connect("focus-out-event", self.on_volume_digits_focus_out)
99 self.connect("key-press-event", self.on_key_pressed)
100 self.connect("scroll-event", self.on_scroll)
103 #print "Unrealizing channel \"%s\"" % self.channel_name
106 def create_balance_widget(self):
107 if self.gui_factory.use_custom_widgets and phat:
108 self.balance = phat.HFanSlider()
109 self.balance.set_default_value(0)
110 self.balance.set_adjustment(self.balance_adjustment)
112 self.balance = gtk.HScale(self.balance_adjustment)
113 self.balance.set_draw_value(False)
114 self.pack_start(self.balance, False)
115 if self.monitor_button:
116 self.reorder_child(self.monitor_button, -1)
119 def create_slider_widget(self):
122 parent = self.slider.get_parent()
123 self.slider.destroy()
124 if self.gui_factory.use_custom_widgets:
125 self.slider = slider.CustomSliderWidget(self.slider_adjustment)
127 self.slider = slider.GtkSlider(self.slider_adjustment)
129 parent.pack_start(self.slider)
130 parent.reorder_child(self.slider, 0)
133 def on_default_meter_scale_changed(self, gui_factory, scale):
134 #print "Default meter scale change detected."
135 self.meter.set_scale(scale)
137 def on_default_slider_scale_changed(self, gui_factory, scale):
138 #print "Default slider scale change detected."
139 self.slider_scale = scale
140 self.slider_adjustment.set_scale(scale)
141 self.channel.midi_scale = self.slider_scale.scale
143 def on_vumeter_color_changed(self, gui_factory, *args):
144 color = gui_factory.get_vumeter_color()
145 color_scheme = gui_factory.get_vumeter_color_scheme()
146 if color_scheme != 'solid':
147 self.meter.set_color(None)
149 self.meter.set_color(gtk.gdk.color_parse(color))
151 def on_custom_widgets_changed(self, gui_factory, value):
152 self.balance.destroy()
153 self.create_balance_widget()
154 self.create_slider_widget()
156 def on_abspeak_adjust(self, abspeak, adjust):
157 #print "abspeak adjust %f" % adjust
158 self.slider_adjustment.set_value_db(self.slider_adjustment.get_value_db() + adjust)
159 self.channel.abspeak = None
160 #self.update_volume(False) # We want to update gui even if actual decibels have not changed (scale wrap for example)
162 def on_abspeak_reset(self, abspeak):
163 #print "abspeak reset"
164 self.channel.abspeak = None
166 def on_volume_digits_key_pressed(self, widget, event):
167 if (event.keyval == gtk.keysyms.Return or event.keyval == gtk.keysyms.KP_Enter):
168 db_text = self.volume_digits.get_text()
171 #print "Volume digits confirmation \"%f dBFS\"" % db
172 except (ValueError), e:
173 #print "Volume digits confirmation ignore, reset to current"
174 self.update_volume(False)
176 self.slider_adjustment.set_value_db(db)
178 #self.update_volume(False) # We want to update gui even if actual decibels have not changed (scale wrap for example)
180 def on_volume_digits_focus_out(self, widget, event):
181 #print "volume digits focus out detected"
182 self.update_volume(False)
184 def read_meter(self):
188 meter_left, meter_right = self.channel.meter
189 self.meter.set_values(meter_left, meter_right)
191 self.meter.set_value(self.channel.meter[0])
193 self.abspeak.set_peak(self.channel.abspeak)
195 def on_scroll(self, widget, event):
196 if event.direction == gtk.gdk.SCROLL_DOWN:
197 self.slider_adjustment.step_down()
198 elif event.direction == gtk.gdk.SCROLL_UP:
199 self.slider_adjustment.step_up()
202 def update_volume(self, update_engine):
203 db = self.slider_adjustment.get_value_db()
205 db_text = "%.2f" % db
206 self.volume_digits.set_text(db_text)
209 #print "Setting engine volume to " + db_text
210 self.channel.volume = db
211 self.app.update_monitor(self)
213 def on_volume_changed(self, adjustment):
214 self.update_volume(True)
216 def on_balance_changed(self, adjustment):
217 balance = self.balance_adjustment.get_value()
218 #print "%s balance: %f" % (self.channel_name, balance)
219 self.channel.balance = balance
220 self.app.update_monitor(self)
222 def on_key_pressed(self, widget, event):
223 if (event.keyval == gtk.keysyms.Up):
224 #print self.channel_name + " Up"
225 self.slider_adjustment.step_up()
227 elif (event.keyval == gtk.keysyms.Down):
228 #print self.channel_name + " Down"
229 self.slider_adjustment.step_down()
234 def serialize(self, object_backend):
235 object_backend.add_property("volume", "%f" % self.slider_adjustment.get_value_db())
236 object_backend.add_property("balance", "%f" % self.balance_adjustment.get_value())
238 if self.channel.volume_midi_cc:
239 object_backend.add_property('volume_midi_cc', str(self.channel.volume_midi_cc))
240 if self.channel.balance_midi_cc:
241 object_backend.add_property('balance_midi_cc', str(self.channel.balance_midi_cc))
243 def unserialize_property(self, name, value):
245 self.slider_adjustment.set_value_db(float(value))
247 if name == "balance":
248 self.balance_adjustment.set_value(float(value))
250 if name == 'volume_midi_cc':
251 self.future_volume_midi_cc = int(value)
253 if name == 'balance_midi_cc':
254 self.future_balance_midi_cc = int(value)
258 def on_midi_event_received(self, *args):
259 self.slider_adjustment.set_value_db(self.channel.volume)
260 self.balance_adjustment.set_value(self.channel.balance)
262 def midi_change_callback(self, *args):
263 # the changes are not applied directly to the widgets as they
264 # absolutely have to be done from the gtk thread.
265 self.emit('midi-event-received')
267 def on_monitor_button_toggled(self, button):
268 if not button.get_active():
269 self.app.main_mix.monitor_button.set_active(True)
271 for channel in self.app.channels + self.app.output_channels + [self.app.main_mix]:
272 if channel.monitor_button.get_active() and channel.monitor_button is not button:
273 channel.monitor_button.handler_block_by_func(
274 channel.on_monitor_button_toggled)
275 channel.monitor_button.set_active(False)
276 channel.monitor_button.handler_unblock_by_func(
277 channel.on_monitor_button_toggled)
278 self.app.set_monitored_channel(self)
280 def set_monitored(self):
282 self.app.set_monitored_channel(self)
283 self.monitor_button.set_active(True)
285 gobject.signal_new('midi-event-received', Channel,
286 gobject.SIGNAL_RUN_FIRST | gobject.SIGNAL_ACTION,
287 gobject.TYPE_NONE, ())
289 class InputChannel(Channel):
290 post_fader_output_channel = None
292 def __init__(self, app, name, stereo):
293 Channel.__init__(self, app, name, stereo)
296 self.channel = self.mixer.add_channel(self.channel_name, self.stereo)
297 if self.channel == None:
298 raise Exception,"Cannot create a channel"
299 Channel.realize(self)
300 if self.future_volume_midi_cc:
301 self.channel.volume_midi_cc = self.future_volume_midi_cc
302 if self.future_balance_midi_cc:
303 self.channel.balance_midi_cc = self.future_balance_midi_cc
304 self.channel.midi_scale = self.slider_scale.scale
305 self.channel.midi_change_callback = self.midi_change_callback
307 self.on_volume_changed(self.slider_adjustment)
308 self.on_balance_changed(self.balance_adjustment)
310 # vbox child at upper part
311 self.vbox = gtk.VBox()
312 self.pack_start(self.vbox, False)
313 self.label_name = gtk.Label()
314 self.label_name.set_text(self.channel_name)
315 self.label_name.set_size_request(0, -1)
316 self.label_name_event_box = gtk.EventBox()
317 self.label_name_event_box.connect("button-press-event", self.on_label_mouse)
318 self.label_name_event_box.add(self.label_name)
319 self.vbox.pack_start(self.label_name_event_box, True)
320 # self.label_stereo = gtk.Label()
322 # self.label_stereo.set_text("stereo")
324 # self.label_stereo.set_text("mono")
325 # self.label_stereo.set_size_request(0, -1)
326 # self.vbox.pack_start(self.label_stereo, True)
328 # hbox for mute and solo buttons
329 self.hbox_mutesolo = gtk.HBox()
331 self.mute = gtk.ToggleButton()
332 self.mute.set_label("M")
333 self.mute.set_active(self.channel.mute)
334 self.mute.connect("button-press-event", self.on_mute_button_pressed)
335 self.mute.connect("toggled", self.on_mute_toggled)
336 self.hbox_mutesolo.pack_start(self.mute, True)
338 self.solo = gtk.ToggleButton()
339 self.solo.set_label("S")
340 self.solo.set_active(self.channel.solo)
341 self.solo.connect("button-press-event", self.on_solo_button_pressed)
342 self.solo.connect("toggled", self.on_solo_toggled)
343 self.hbox_mutesolo.pack_start(self.solo, True)
345 self.vbox.pack_start(self.hbox_mutesolo, False)
348 frame.set_shadow_type(gtk.SHADOW_IN)
349 frame.add(self.abspeak);
350 self.pack_start(frame, False)
352 # hbox child at lower part
353 self.hbox = gtk.HBox()
354 self.hbox.pack_start(self.slider, True)
356 frame.set_shadow_type(gtk.SHADOW_IN)
357 frame.add(self.meter);
358 self.hbox.pack_start(frame, True)
360 frame.set_shadow_type(gtk.SHADOW_IN)
361 frame.add(self.hbox);
362 self.pack_start(frame, True)
364 self.volume_digits.set_size_request(0, -1)
365 self.pack_start(self.volume_digits, False)
367 self.create_balance_widget()
369 self.monitor_button = gtk.ToggleButton('MON')
370 self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
371 self.pack_start(self.monitor_button, False, False)
373 def add_control_group(self, channel):
374 control_group = ControlGroup(channel, self)
375 control_group.show_all()
376 self.vbox.pack_start(control_group, False)
379 def remove_control_group(self, channel):
380 ctlgroup = self.get_control_group(channel)
381 self.vbox.remove(ctlgroup)
383 def update_control_group(self, channel):
384 for control_group in self.vbox.get_children():
385 if isinstance(control_group, ControlGroup):
386 if control_group.output_channel is channel:
387 control_group.update()
389 def get_control_group(self, channel):
390 for control_group in self.vbox.get_children():
391 if isinstance(control_group, ControlGroup):
392 if control_group.output_channel is channel:
397 Channel.unrealize(self)
398 if self.post_fader_output_channel:
399 self.post_fader_output_channel.remove()
400 self.post_fader_output_channel = None
401 self.channel.remove()
404 channel_properties_dialog = None
405 def on_channel_properties(self):
406 if not self.channel_properties_dialog:
407 self.channel_properties_dialog = ChannelPropertiesDialog(self, self.app)
408 self.channel_properties_dialog.show()
409 self.channel_properties_dialog.present()
411 def on_label_mouse(self, widget, event):
412 if event.type == gtk.gdk._2BUTTON_PRESS:
413 if event.button == 1:
414 self.on_channel_properties()
416 def on_mute_toggled(self, button):
417 self.channel.mute = self.mute.get_active()
418 self.app.update_monitor(self.app.main_mix)
420 def on_mute_button_pressed(self, button, event, *args):
421 if event.button == 3:
422 # right click on the mute button, act on all output channels
423 if button.get_active(): # was muted
424 button.set_active(False)
425 if hasattr(button, 'touched_channels'):
426 touched_channels = button.touched_channels
427 for chan in touched_channels:
428 ctlgroup = self.get_control_group(chan)
429 ctlgroup.mute.set_active(False)
430 del button.touched_channels
431 else: # was not muted
432 button.set_active(True)
433 touched_channels = []
434 for chan in self.app.output_channels:
435 ctlgroup = self.get_control_group(chan)
436 if not ctlgroup.mute.get_active():
437 ctlgroup.mute.set_active(True)
438 touched_channels.append(chan)
439 button.touched_channels = touched_channels
443 def on_solo_toggled(self, button):
444 self.channel.solo = self.solo.get_active()
445 self.app.update_monitor(self.app.main_mix)
447 def on_solo_button_pressed(self, button, event, *args):
448 if event.button == 3:
449 # right click on the solo button, act on all output channels
450 if button.get_active(): # was soloed
451 button.set_active(False)
452 if hasattr(button, 'touched_channels'):
453 touched_channels = button.touched_channels
454 for chan in touched_channels:
455 ctlgroup = self.get_control_group(chan)
456 ctlgroup.solo.set_active(False)
457 del button.touched_channels
458 else: # was not soloed
459 button.set_active(True)
460 touched_channels = []
461 for chan in self.app.output_channels:
462 ctlgroup = self.get_control_group(chan)
463 if not ctlgroup.solo.get_active():
464 ctlgroup.solo.set_active(True)
465 touched_channels.append(chan)
466 button.touched_channels = touched_channels
471 def serialization_name(cls):
472 return 'input_channel'
474 def serialize(self, object_backend):
475 object_backend.add_property("name", self.channel_name)
477 object_backend.add_property("type", "stereo")
479 object_backend.add_property("type", "mono")
480 Channel.serialize(self, object_backend)
482 def unserialize_property(self, name, value):
484 self.channel_name = str(value)
487 if value == "stereo":
493 return Channel.unserialize_property(self, name, value)
496 available_colours = [
497 ('#ef2929', '#cc0000', '#840000'),
498 ('#729fcf', '#3465a4', '#204a67'),
499 ('#8aa234', '#73d216', '#4e7a06'),
500 ('#fce84f', '#edd400', '#c48000'),
501 ('#fcaf3e', '#f57900', '#ae5c00'),
502 ('#ad7fa8', '#75507b', '#4c3556'),
503 ('#e9b96e', '#c17d11', '#6f4902'),
506 class OutputChannel(Channel):
507 colours = available_colours[:]
508 _display_solo_buttons = False
510 _init_muted_channels = None
511 _init_solo_channels = None
513 def __init__(self, app, name, stereo):
514 Channel.__init__(self, app, name, stereo)
516 def get_display_solo_buttons(self):
517 return self._display_solo_buttons
519 def set_display_solo_buttons(self, value):
520 self._display_solo_buttons = value
521 # notifying control groups
522 for inputchannel in self.app.channels:
523 inputchannel.update_control_group(self)
525 display_solo_buttons = property(get_display_solo_buttons, set_display_solo_buttons)
528 Channel.realize(self)
529 self.channel = self.mixer.add_output_channel(self.channel_name, self.stereo)
530 if self.channel == None:
531 raise Exception,"Cannot create a channel"
532 Channel.realize(self)
534 self.channel.midi_scale = self.slider_scale.scale
535 self.channel.midi_change_callback = self.midi_change_callback
537 self.on_volume_changed(self.slider_adjustment)
538 self.on_balance_changed(self.balance_adjustment)
540 # vbox child at upper part
541 self.vbox = gtk.VBox()
542 self.pack_start(self.vbox, False)
543 self.label_name = gtk.Label()
544 self.label_name.set_text(self.channel_name)
545 self.label_name.set_size_request(0, -1)
546 self.label_name_event_box = gtk.EventBox()
547 self.label_name_event_box.connect('button-press-event', self.on_label_mouse)
548 self.label_name_event_box.add(self.label_name)
550 OutputChannel.colours = available_colours[:]
551 for color in self.colours:
552 self.color_tuple = [gtk.gdk.color_parse(color[x]) for x in range(3)]
553 self.colours.remove(color)
555 self.label_name_event_box.modify_bg(gtk.STATE_NORMAL, self.color_tuple[1])
556 self.vbox.pack_start(self.label_name_event_box, True)
558 frame.set_shadow_type(gtk.SHADOW_IN)
559 frame.add(self.abspeak);
560 self.vbox.pack_start(frame, False)
562 # hbox child at lower part
563 self.hbox = gtk.HBox()
564 self.hbox.pack_start(self.slider, True)
566 frame.set_shadow_type(gtk.SHADOW_IN)
567 frame.add(self.meter);
568 self.hbox.pack_start(frame, True)
570 frame.set_shadow_type(gtk.SHADOW_IN)
571 frame.add(self.hbox);
572 self.pack_start(frame, True)
574 self.volume_digits.set_size_request(0, -1)
575 self.pack_start(self.volume_digits, False)
577 self.create_balance_widget()
579 self.monitor_button = gtk.ToggleButton('MON')
580 self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
581 self.pack_start(self.monitor_button, False, False)
583 # add control groups to the input channels, and initialize them
585 for input_channel in self.app.channels:
586 ctlgroup = input_channel.add_control_group(self)
587 if self._init_muted_channels and input_channel.channel.name in self._init_muted_channels:
588 ctlgroup.mute.set_active(True)
589 if self._init_solo_channels and input_channel.channel.name in self._init_solo_channels:
590 ctlgroup.solo.set_active(True)
591 self._init_muted_channels = None
592 self._init_solo_channels = None
594 channel_properties_dialog = None
595 def on_channel_properties(self):
596 if not self.channel_properties_dialog:
597 self.channel_properties_dialog = OutputChannelPropertiesDialog(self, self.app)
598 self.channel_properties_dialog.show()
599 self.channel_properties_dialog.present()
601 def on_label_mouse(self, widget, event):
602 if event.type == gtk.gdk._2BUTTON_PRESS:
603 if event.button == 1:
604 self.on_channel_properties()
607 # remove control groups from input channels
608 for input_channel in self.app.channels:
609 input_channel.remove_control_group(self)
611 Channel.unrealize(self)
612 self.channel.remove()
616 def serialization_name(cls):
617 return 'output_channel'
619 def serialize(self, object_backend):
620 object_backend.add_property("name", self.channel_name)
622 object_backend.add_property("type", "stereo")
624 object_backend.add_property("type", "mono")
625 if self.display_solo_buttons:
626 object_backend.add_property("solo_buttons", "true")
629 for input_channel in self.app.channels:
630 if self.channel.is_muted(input_channel.channel):
631 muted_channels.append(input_channel)
632 if self.channel.is_solo(input_channel.channel):
633 solo_channels.append(input_channel)
635 object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
637 object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
638 Channel.serialize(self, object_backend)
640 def unserialize_property(self, name, value):
642 self.channel_name = str(value)
645 if value == "stereo":
651 if name == "solo_buttons":
653 self.display_solo_buttons = True
655 if name == 'muted_channels':
656 self._init_muted_channels = value.split('|')
658 if name == 'solo_channels':
659 self._init_solo_channels = value.split('|')
661 return Channel.unserialize_property(self, name, value)
663 class MainMixChannel(Channel):
664 _init_muted_channels = None
665 _init_solo_channels = None
667 def __init__(self, app):
668 Channel.__init__(self, app, "MAIN", True)
671 Channel.realize(self)
672 self.channel = self.mixer.main_mix_channel
673 self.channel.midi_scale = self.slider_scale.scale
674 self.channel.midi_change_callback = self.midi_change_callback
676 self.on_volume_changed(self.slider_adjustment)
677 self.on_balance_changed(self.balance_adjustment)
679 # vbox child at upper part
680 self.vbox = gtk.VBox()
681 self.pack_start(self.vbox, False)
682 self.label_name = gtk.Label()
683 self.label_name.set_text(self.channel_name)
684 self.label_name.set_size_request(0, -1)
685 self.vbox.pack_start(self.label_name, False)
687 frame.set_shadow_type(gtk.SHADOW_IN)
688 frame.add(self.abspeak);
689 self.vbox.pack_start(frame, False)
691 # hbox child at lower part
692 self.hbox = gtk.HBox()
693 self.hbox.pack_start(self.slider, True)
695 frame.set_shadow_type(gtk.SHADOW_IN)
696 frame.add(self.meter);
697 self.hbox.pack_start(frame, True)
699 frame.set_shadow_type(gtk.SHADOW_IN)
700 frame.add(self.hbox);
701 self.pack_start(frame, True)
703 self.volume_digits.set_size_request(0, -1)
704 self.pack_start(self.volume_digits, False)
706 self.create_balance_widget()
708 self.monitor_button = gtk.ToggleButton('MON')
709 self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
710 self.pack_start(self.monitor_button, False, False)
712 for input_channel in self.app.channels:
713 if self._init_muted_channels and input_channel.channel.name in self._init_muted_channels:
714 input_channel.mute.set_active(True)
715 if self._init_solo_channels and input_channel.channel.name in self._init_solo_channels:
716 input_channel.solo.set_active(True)
717 self._init_muted_channels = None
718 self._init_solo_channels = None
721 Channel.unrealize(self)
725 def serialization_name(cls):
726 return 'main_mix_channel'
728 def serialize(self, object_backend):
731 for input_channel in self.app.channels:
732 if input_channel.channel.mute:
733 muted_channels.append(input_channel)
734 if input_channel.channel.solo:
735 solo_channels.append(input_channel)
737 object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
739 object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
740 Channel.serialize(self, object_backend)
742 def unserialize_property(self, name, value):
743 if name == 'muted_channels':
744 self._init_muted_channels = value.split('|')
746 if name == 'solo_channels':
747 self._init_solo_channels = value.split('|')
749 return Channel.unserialize_property(self, name, value)
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)
765 self.set_default_response(gtk.RESPONSE_APPLY);
770 self.connect('response', self.on_response_cb)
771 self.connect('delete-event', self.on_response_cb)
773 def create_frame(self, label, child):
774 frame = gtk.Frame('')
775 frame.set_border_width(3)
776 #frame.set_shadow_type(gtk.SHADOW_NONE)
777 frame.get_label_widget().set_markup('<b>%s</b>' % label)
779 alignment = gtk.Alignment(0, 0, 1, 1)
780 alignment.set_padding(0, 0, 12, 0)
790 table = gtk.Table(2, 2, False)
791 vbox.pack_start(self.create_frame('Properties', table))
792 table.set_row_spacings(5)
793 table.set_col_spacings(5)
795 table.attach(gtk.Label('Name'), 0, 1, 0, 1)
796 self.entry_name = gtk.Entry()
797 self.entry_name.set_activates_default(True)
798 self.entry_name.connect('changed', self.on_entry_name_changed)
799 table.attach(self.entry_name, 1, 2, 0, 1)
801 table.attach(gtk.Label('Mode'), 0, 1, 1, 2)
802 self.mode_hbox = gtk.HBox()
803 table.attach(self.mode_hbox, 1, 2, 1, 2)
804 self.mono = gtk.RadioButton(label='Mono')
805 self.stereo = gtk.RadioButton(label='Stereo', group=self.mono)
806 self.mode_hbox.pack_start(self.mono)
807 self.mode_hbox.pack_start(self.stereo)
809 table = gtk.Table(2, 3, False)
810 vbox.pack_start(self.create_frame('MIDI Control Channels', table))
811 table.set_row_spacings(5)
812 table.set_col_spacings(5)
814 table.attach(gtk.Label('Volume'), 0, 1, 0, 1)
815 self.entry_volume_cc = gtk.Entry()
816 self.entry_volume_cc.set_activates_default(True)
817 self.entry_volume_cc.set_editable(False)
818 self.entry_volume_cc.set_width_chars(3)
819 table.attach(self.entry_volume_cc, 1, 2, 0, 1)
820 self.button_sense_midi_volume = gtk.Button('Autoset')
821 self.button_sense_midi_volume.connect('clicked',
822 self.on_sense_midi_volume_clicked)
823 table.attach(self.button_sense_midi_volume, 2, 3, 0, 1)
825 table.attach(gtk.Label('Balance'), 0, 1, 1, 2)
826 self.entry_balance_cc = gtk.Entry()
827 self.entry_balance_cc.set_activates_default(True)
828 self.entry_balance_cc.set_width_chars(3)
829 self.entry_balance_cc.set_editable(False)
830 table.attach(self.entry_balance_cc, 1, 2, 1, 2)
831 self.button_sense_midi_balance = gtk.Button('Autoset')
832 self.button_sense_midi_balance.connect('clicked',
833 self.on_sense_midi_balance_clicked)
834 table.attach(self.button_sense_midi_balance, 2, 3, 1, 2)
839 self.entry_name.set_text(self.channel.channel_name)
840 if self.channel.channel.is_stereo:
841 self.stereo.set_active(True)
843 self.mono.set_active(True)
844 self.mode_hbox.set_sensitive(False)
845 self.entry_volume_cc.set_text('%s' % self.channel.channel.volume_midi_cc)
846 self.entry_balance_cc.set_text('%s' % self.channel.channel.balance_midi_cc)
848 def sense_popup_dialog(self, entry):
849 window = gtk.Window(gtk.WINDOW_TOPLEVEL)
850 window.set_destroy_with_parent(True)
851 window.set_transient_for(self)
852 window.set_decorated(False)
853 window.set_modal(True)
854 window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
855 window.set_border_width(10)
860 vbox.pack_start(gtk.Label('Please move the MIDI control you want to use for this function.'))
861 timeout_label = gtk.Label('This window will close in 5 seconds')
862 vbox.pack_start(timeout_label)
863 def close_sense_timeout(window, entry):
865 timeout_label.set_text('This window will close in %d seconds.' % window.timeout)
866 if window.timeout == 0:
868 entry.set_text('%s' % self.mixer.last_midi_channel)
872 glib.timeout_add_seconds(1, close_sense_timeout, window, entry)
874 def on_sense_midi_volume_clicked(self, *args):
875 self.sense_popup_dialog(self.entry_volume_cc)
877 def on_sense_midi_balance_clicked(self, *args):
878 self.sense_popup_dialog(self.entry_balance_cc)
880 def on_response_cb(self, dlg, response_id, *args):
881 self.channel.channel_properties_dialog = None
883 if response_id == gtk.RESPONSE_APPLY:
884 name = self.entry_name.get_text()
885 self.channel.channel_name = name
886 self.channel.channel.volume_midi_cc = int(self.entry_volume_cc.get_text())
887 self.channel.channel.balance_midi_cc = int(self.entry_balance_cc.get_text())
889 def on_entry_name_changed(self, entry):
891 if len(entry.get_text()):
892 if self.channel and self.channel.channel.name == entry.get_text():
894 elif entry.get_text() not in [x.channel.name for x in self.app.channels] + \
895 [x.channel.name for x in self.app.output_channels] + ['MAIN']:
897 self.ok_button.set_sensitive(sensitive)
900 class NewChannelDialog(ChannelPropertiesDialog):
901 def __init__(self, app):
902 gtk.Dialog.__init__(self, 'New Channel', app.window)
903 self.mixer = app.mixer
907 self.stereo.set_active(True) # default to stereo
909 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
910 self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
911 self.ok_button.set_sensitive(False)
912 self.set_default_response(gtk.RESPONSE_OK);
914 def get_result(self):
915 return {'name': self.entry_name.get_text(),
916 'stereo': self.stereo.get_active(),
917 'volume_cc': self.entry_volume_cc.get_text(),
918 'balance_cc': self.entry_balance_cc.get_text()
921 class OutputChannelPropertiesDialog(ChannelPropertiesDialog):
923 ChannelPropertiesDialog.create_ui(self)
926 self.vbox.pack_start(self.create_frame('Input Channels', vbox))
928 self.display_solo_buttons = gtk.CheckButton('Display solo buttons')
929 vbox.pack_start(self.display_solo_buttons)
934 ChannelPropertiesDialog.fill_ui(self)
935 self.display_solo_buttons.set_active(self.channel.display_solo_buttons)
937 def on_response_cb(self, dlg, response_id, *args):
938 ChannelPropertiesDialog.on_response_cb(self, dlg, response_id, *args)
939 if response_id == gtk.RESPONSE_APPLY:
940 self.channel.display_solo_buttons = self.display_solo_buttons.get_active()
943 class NewOutputChannelDialog(OutputChannelPropertiesDialog):
944 def __init__(self, app):
945 gtk.Dialog.__init__(self, 'New Output Channel', app.window)
946 self.mixer = app.mixer
950 # TODO: disable mode for output channels as mono output channels may
951 # not be correctly handled yet.
952 self.mode_hbox.set_sensitive(False)
953 self.stereo.set_active(True) # default to stereo
955 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
956 self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
957 self.ok_button.set_sensitive(False)
958 self.set_default_response(gtk.RESPONSE_OK);
960 def get_result(self):
961 return {'name': self.entry_name.get_text(),
962 'stereo': self.stereo.get_active(),
963 'volume_cc': self.entry_volume_cc.get_text(),
964 'balance_cc': self.entry_balance_cc.get_text(),
965 'display_solo_buttons': self.display_solo_buttons.get_active(),
969 class ControlGroup(gtk.Alignment):
970 def __init__(self, output_channel, input_channel):
971 gtk.Alignment.__init__(self, 0.5, 0.5, 0, 0)
972 self.output_channel = output_channel
973 self.input_channel = input_channel
974 self.app = input_channel.app
980 mute = gtk.ToggleButton()
983 mute.connect("toggled", self.on_mute_toggled)
984 hbox.pack_start(mute, False)
986 solo = gtk.ToggleButton()
989 solo.connect("toggled", self.on_solo_toggled)
990 if self.output_channel.display_solo_buttons:
991 hbox.pack_start(solo, True)
993 mute.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
994 mute.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
995 mute.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
996 solo.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
997 solo.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
998 solo.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
1001 if self.output_channel.display_solo_buttons:
1002 if not self.solo in self.hbox.get_children():
1003 self.hbox.pack_start(self.solo, True)
1006 if self.solo in self.hbox.get_children():
1007 self.hbox.remove(self.solo)
1009 def on_mute_toggled(self, button):
1010 self.output_channel.channel.set_muted(self.input_channel.channel, button.get_active())
1011 self.app.update_monitor(self)
1013 def on_solo_toggled(self, button):
1014 self.output_channel.channel.set_solo(self.input_channel.channel, button.get_active())
1015 self.app.update_monitor(self)