]> git.0d.be Git - jack_mixer.git/blob - channel.py
Handle post fader outputs for input channels from Python code
[jack_mixer.git] / channel.py
1 # This file is part of jack_mixer
2 #
3 # Copyright (C) 2006 Nedko Arnaudov <nedko@arnaudov.name>
4 #
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
8 #
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.
13 #
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.
17
18 import gtk
19 import gobject
20 import glib
21 import slider
22 import meter
23 import abspeak
24 from serialization import SerializedObject
25
26 try:
27     import phat
28 except:
29     phat = None
30
31
32 class Channel(gtk.VBox, SerializedObject):
33     '''Widget with slider and meter used as base class for more specific
34        channel widgets'''
35     monitor_button = None
36
37     def __init__(self, app, name, stereo):
38         gtk.VBox.__init__(self)
39         self.app = app
40         self.mixer = app.mixer
41         self.gui_factory = app.gui_factory
42         self._channel_name = name
43         self.stereo = stereo
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
50
51     def get_channel_name(self):
52         return self._channel_name
53
54     label_name = None
55     channel = None
56     def set_channel_name(self, name):
57         self._channel_name = name
58         if self.label_name:
59             self.label_name.set_text(name)
60         if self.channel:
61             self.channel.name = name
62     channel_name = property(get_channel_name, set_channel_name)
63
64     def realize(self):
65         #print "Realizing channel \"%s\"" % self.channel_name
66
67         self.slider_adjustment.connect("volume-changed", self.on_volume_changed)
68         self.balance_adjustment.connect("value-changed", self.on_balance_changed)
69         self.connect('midi-event-received', self.on_midi_event_received)
70
71         self.slider = None
72         self.create_slider_widget()
73
74         if self.stereo:
75             self.meter = meter.StereoMeterWidget(self.meter_scale)
76         else:
77             self.meter = meter.MonoMeterWidget(self.meter_scale)
78         self.on_vumeter_color_changed(self.gui_factory)
79
80         self.meter.set_events(gtk.gdk.SCROLL_MASK)
81
82         self.gui_factory.connect("default-meter-scale-changed", self.on_default_meter_scale_changed)
83         self.gui_factory.connect("default-slider-scale-changed", self.on_default_slider_scale_changed)
84         self.gui_factory.connect('vumeter-color-changed', self.on_vumeter_color_changed)
85         self.gui_factory.connect('vumeter-color-scheme-changed', self.on_vumeter_color_changed)
86         self.gui_factory.connect('use-custom-widgets-changed', self.on_custom_widgets_changed)
87
88         self.abspeak = abspeak.AbspeakWidget()
89         self.abspeak.connect("reset", self.on_abspeak_reset)
90         self.abspeak.connect("volume-adjust", self.on_abspeak_adjust)
91
92         self.volume_digits = gtk.Entry()
93         self.volume_digits.connect("key-press-event", self.on_volume_digits_key_pressed)
94         self.volume_digits.connect("focus-out-event", self.on_volume_digits_focus_out)
95
96         self.connect("key-press-event", self.on_key_pressed)
97         self.connect("scroll-event", self.on_scroll)
98
99     def unrealize(self):
100         #print "Unrealizing channel \"%s\"" % self.channel_name
101         pass
102
103     def create_balance_widget(self):
104         if self.gui_factory.use_custom_widgets and phat:
105             self.balance = phat.HFanSlider()
106             self.balance.set_default_value(0)
107             self.balance.set_adjustment(self.balance_adjustment)
108         else:
109             self.balance = gtk.HScale(self.balance_adjustment)
110             self.balance.set_draw_value(False)
111         self.pack_start(self.balance, False)
112         if self.monitor_button:
113             self.reorder_child(self.monitor_button, -1)
114         self.balance.show()
115
116     def create_slider_widget(self):
117         parent = None
118         if self.slider:
119             parent = self.slider.get_parent()
120             self.slider.destroy()
121         if self.gui_factory.use_custom_widgets:
122             self.slider = slider.CustomSliderWidget(self.slider_adjustment)
123         else:
124             self.slider = slider.GtkSlider(self.slider_adjustment)
125         if parent:
126             parent.pack_start(self.slider)
127             parent.reorder_child(self.slider, 0)
128         self.slider.show()
129
130     def on_default_meter_scale_changed(self, gui_factory, scale):
131         #print "Default meter scale change detected."
132         self.meter.set_scale(scale)
133
134     def on_default_slider_scale_changed(self, gui_factory, scale):
135         #print "Default slider scale change detected."
136         self.slider_scale = scale
137         self.slider_adjustment.set_scale(scale)
138         self.channel.midi_scale = self.slider_scale.scale
139
140     def on_vumeter_color_changed(self, gui_factory, *args):
141         color = gui_factory.get_vumeter_color()
142         color_scheme = gui_factory.get_vumeter_color_scheme()
143         if color_scheme != 'solid':
144             self.meter.set_color(None)
145         else:
146             self.meter.set_color(gtk.gdk.color_parse(color))
147
148     def on_custom_widgets_changed(self, gui_factory, value):
149         self.balance.destroy()
150         self.create_balance_widget()
151         self.create_slider_widget()
152
153     def on_abspeak_adjust(self, abspeak, adjust):
154         #print "abspeak adjust %f" % adjust
155         self.slider_adjustment.set_value_db(self.slider_adjustment.get_value_db() + adjust)
156         self.channel.abspeak = None
157         #self.update_volume(False)   # We want to update gui even if actual decibels have not changed (scale wrap for example)
158
159     def on_abspeak_reset(self, abspeak):
160         #print "abspeak reset"
161         self.channel.abspeak = None
162
163     def on_volume_digits_key_pressed(self, widget, event):
164         if (event.keyval == gtk.keysyms.Return or event.keyval == gtk.keysyms.KP_Enter):
165             db_text = self.volume_digits.get_text()
166             try:
167                 db = float(db_text)
168                 #print "Volume digits confirmation \"%f dBFS\"" % db
169             except (ValueError), e:
170                 #print "Volume digits confirmation ignore, reset to current"
171                 self.update_volume(False)
172                 return
173             self.slider_adjustment.set_value_db(db)
174             #self.grab_focus()
175             #self.update_volume(False)   # We want to update gui even if actual decibels have not changed (scale wrap for example)
176
177     def on_volume_digits_focus_out(self, widget, event):
178         #print "volume digits focus out detected"
179         self.update_volume(False)
180
181     def read_meter(self):
182         if not self.channel:
183             return
184         if self.stereo:
185             meter_left, meter_right = self.channel.meter
186             self.meter.set_values(meter_left, meter_right)
187         else:
188             self.meter.set_value(self.channel.meter[0])
189
190         self.abspeak.set_peak(self.channel.abspeak)
191
192     def on_scroll(self, widget, event):
193         if event.direction == gtk.gdk.SCROLL_DOWN:
194             self.slider_adjustment.step_down()
195         elif event.direction == gtk.gdk.SCROLL_UP:
196             self.slider_adjustment.step_up()
197         return True
198
199     def update_volume(self, update_engine):
200         db = self.slider_adjustment.get_value_db()
201
202         db_text = "%.2f" % db
203         self.volume_digits.set_text(db_text)
204
205         if update_engine:
206             #print "Setting engine volume to " + db_text
207             self.channel.volume = db
208             self.app.update_monitor(self)
209
210     def on_volume_changed(self, adjustment):
211         self.update_volume(True)
212
213     def on_balance_changed(self, adjustment):
214         balance = self.balance_adjustment.get_value()
215         #print "%s balance: %f" % (self.channel_name, balance)
216         self.channel.balance = balance
217         self.app.update_monitor(self)
218
219     def on_key_pressed(self, widget, event):
220         if (event.keyval == gtk.keysyms.Up):
221             #print self.channel_name + " Up"
222             self.slider_adjustment.step_up()
223             return True
224         elif (event.keyval == gtk.keysyms.Down):
225             #print self.channel_name + " Down"
226             self.slider_adjustment.step_down()
227             return True
228
229         return False
230
231     def serialize(self, object_backend):
232         object_backend.add_property("volume", "%f" % self.slider_adjustment.get_value_db())
233         object_backend.add_property("balance", "%f" % self.balance_adjustment.get_value())
234
235         if self.channel.volume_midi_cc:
236             object_backend.add_property('volume_midi_cc', str(self.channel.volume_midi_cc))
237         if self.channel.balance_midi_cc:
238             object_backend.add_property('balance_midi_cc', str(self.channel.balance_midi_cc))
239
240     def unserialize_property(self, name, value):
241         if name == "volume":
242             self.slider_adjustment.set_value_db(float(value))
243             return True
244         if name == "balance":
245             self.balance_adjustment.set_value(float(value))
246             return True
247         if name == 'volume_midi_cc':
248             self.future_volume_midi_cc = int(value)
249             return True
250         if name == 'balance_midi_cc':
251             self.future_balance_midi_cc = int(value)
252             return True
253         return False
254
255     def on_midi_event_received(self, *args):
256         self.slider_adjustment.set_value_db(self.channel.volume)
257         self.balance_adjustment.set_value(self.channel.balance)
258
259     def midi_change_callback(self, *args):
260         # the changes are not applied directly to the widgets as they
261         # absolutely have to be done from the gtk thread.
262         self.emit('midi-event-received')
263
264     def on_monitor_button_toggled(self, button):
265         if not button.get_active():
266             self.app.main_mix.monitor_button.set_active(True)
267         else:
268             for channel in self.app.channels + self.app.output_channels + [self.app.main_mix]:
269                 if channel.monitor_button.get_active() and channel.monitor_button is not button:
270                     channel.monitor_button.handler_block_by_func(
271                                 channel.on_monitor_button_toggled)
272                     channel.monitor_button.set_active(False)
273                     channel.monitor_button.handler_unblock_by_func(
274                                 channel.on_monitor_button_toggled)
275             self.app.set_monitored_channel(self)
276
277     def set_monitored(self):
278         if self.channel:
279             self.app.set_monitored_channel(self)
280         self.monitor_button.set_active(True)
281
282 gobject.signal_new('midi-event-received', Channel,
283                 gobject.SIGNAL_RUN_FIRST | gobject.SIGNAL_ACTION,
284                 gobject.TYPE_NONE, ())
285
286 class InputChannel(Channel):
287     post_fader_output_channel = None
288
289     def __init__(self, app, name, stereo):
290         Channel.__init__(self, app, name, stereo)
291
292     def realize(self):
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
303
304         self.on_volume_changed(self.slider_adjustment)
305         self.on_balance_changed(self.balance_adjustment)
306
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()
318 #         if self.stereo:
319 #             self.label_stereo.set_text("stereo")
320 #         else:
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)
324
325         # hbox for mute and solo buttons
326         self.hbox_mutesolo = gtk.HBox()
327
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)
334
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)
341
342         self.vbox.pack_start(self.hbox_mutesolo, False)
343
344         frame = gtk.Frame()
345         frame.set_shadow_type(gtk.SHADOW_IN)
346         frame.add(self.abspeak);
347         self.pack_start(frame, False)
348
349         # hbox child at lower part
350         self.hbox = gtk.HBox()
351         self.hbox.pack_start(self.slider, True)
352         frame = gtk.Frame()
353         frame.set_shadow_type(gtk.SHADOW_IN)
354         frame.add(self.meter);
355         self.hbox.pack_start(frame, True)
356         frame = gtk.Frame()
357         frame.set_shadow_type(gtk.SHADOW_IN)
358         frame.add(self.hbox);
359         self.pack_start(frame, True)
360
361         self.volume_digits.set_size_request(0, -1)
362         self.pack_start(self.volume_digits, False)
363
364         self.create_balance_widget()
365
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)
369
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)
374         return control_group
375
376     def remove_control_group(self, channel):
377         ctlgroup = self.get_control_group(channel)
378         self.vbox.remove(ctlgroup)
379
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()
385
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:
390                     return control_group
391         return None
392
393     def unrealize(self):
394         Channel.unrealize(self)
395         if self.post_fader_output_channel:
396             self.post_fader_output_channel.remove()
397             self.post_fader_output_channel = None
398         self.channel.remove()
399         self.channel = None
400
401     channel_properties_dialog = None
402     def on_channel_properties(self):
403         if not self.channel_properties_dialog:
404             self.channel_properties_dialog = ChannelPropertiesDialog(self, self.app)
405         self.channel_properties_dialog.show()
406         self.channel_properties_dialog.present()
407
408     def on_label_mouse(self, widget, event):
409         if event.type == gtk.gdk._2BUTTON_PRESS:
410             if event.button == 1:
411                 self.on_channel_properties()
412
413     def on_mute_toggled(self, button):
414         self.channel.mute = self.mute.get_active()
415         self.app.update_monitor(self.app.main_mix)
416
417     def on_mute_button_pressed(self, button, event, *args):
418         if event.button == 3:
419             # right click on the mute button, act on all output channels
420             if button.get_active(): # was muted
421                 button.set_active(False)
422                 if hasattr(button, 'touched_channels'):
423                     touched_channels = button.touched_channels
424                     for chan in touched_channels:
425                         ctlgroup = self.get_control_group(chan)
426                         ctlgroup.mute.set_active(False)
427                     del button.touched_channels
428             else: # was not muted
429                 button.set_active(True)
430                 touched_channels = []
431                 for chan in self.app.output_channels:
432                     ctlgroup = self.get_control_group(chan)
433                     if not ctlgroup.mute.get_active():
434                         ctlgroup.mute.set_active(True)
435                         touched_channels.append(chan)
436                 button.touched_channels = touched_channels
437             return True
438         return False
439
440     def on_solo_toggled(self, button):
441         self.channel.solo = self.solo.get_active()
442         self.app.update_monitor(self.app.main_mix)
443
444     def on_solo_button_pressed(self, button, event, *args):
445         if event.button == 3:
446             # right click on the solo button, act on all output channels
447             if button.get_active(): # was soloed
448                 button.set_active(False)
449                 if hasattr(button, 'touched_channels'):
450                     touched_channels = button.touched_channels
451                     for chan in touched_channels:
452                         ctlgroup = self.get_control_group(chan)
453                         ctlgroup.solo.set_active(False)
454                     del button.touched_channels
455             else: # was not soloed
456                 button.set_active(True)
457                 touched_channels = []
458                 for chan in self.app.output_channels:
459                     ctlgroup = self.get_control_group(chan)
460                     if not ctlgroup.solo.get_active():
461                         ctlgroup.solo.set_active(True)
462                         touched_channels.append(chan)
463                 button.touched_channels = touched_channels
464             return True
465         return False
466
467     def serialization_name(self):
468         return input_channel_serialization_name()
469
470     def serialize(self, object_backend):
471         object_backend.add_property("name", self.channel_name)
472         if self.stereo:
473             object_backend.add_property("type", "stereo")
474         else:
475             object_backend.add_property("type", "mono")
476         Channel.serialize(self, object_backend)
477
478     def unserialize_property(self, name, value):
479         if name == "name":
480             self.channel_name = str(value)
481             return True
482         if name == "type":
483             if value == "stereo":
484                 self.stereo = True
485                 return True
486             if value == "mono":
487                 self.stereo = False
488                 return True
489         return Channel.unserialize_property(self, name, value)
490
491 def input_channel_serialization_name():
492     return "input_channel"
493
494
495 available_colours = [
496     ('#ef2929', '#cc0000', '#840000'),
497     ('#729fcf', '#3465a4', '#204a67'),
498     ('#8aa234', '#73d216', '#4e7a06'),
499     ('#fce84f', '#edd400', '#c48000'),
500     ('#fcaf3e', '#f57900', '#ae5c00'),
501     ('#ad7fa8', '#75507b', '#4c3556'),
502     ('#e9b96e', '#c17d11', '#6f4902'),
503 ]
504
505 class OutputChannel(Channel):
506     colours = available_colours[:]
507     _display_solo_buttons = False
508
509     _init_muted_channels = None
510     _init_solo_channels = None
511
512     def __init__(self, app, name, stereo):
513         Channel.__init__(self, app, name, stereo)
514
515     def get_display_solo_buttons(self):
516         return self._display_solo_buttons
517
518     def set_display_solo_buttons(self, value):
519         self._display_solo_buttons = value
520         # notifying control groups
521         for inputchannel in self.app.channels:
522             inputchannel.update_control_group(self)
523
524     display_solo_buttons = property(get_display_solo_buttons, set_display_solo_buttons)
525
526     def realize(self):
527         Channel.realize(self)
528         self.channel = self.mixer.add_output_channel(self.channel_name, self.stereo)
529         if self.channel == None:
530             raise Exception,"Cannot create a channel"
531         Channel.realize(self)
532
533         self.channel.midi_scale = self.slider_scale.scale
534         self.channel.midi_change_callback = self.midi_change_callback
535
536         self.on_volume_changed(self.slider_adjustment)
537         self.on_balance_changed(self.balance_adjustment)
538
539         # vbox child at upper part
540         self.vbox = gtk.VBox()
541         self.pack_start(self.vbox, False)
542         self.label_name = gtk.Label()
543         self.label_name.set_text(self.channel_name)
544         self.label_name.set_size_request(0, -1)
545         self.label_name_event_box = gtk.EventBox()
546         self.label_name_event_box.connect('button-press-event', self.on_label_mouse)
547         self.label_name_event_box.add(self.label_name)
548         if not self.colours:
549             OutputChannel.colours = available_colours[:]
550         for color in self.colours:
551             self.color_tuple = [gtk.gdk.color_parse(color[x]) for x in range(3)]
552             self.colours.remove(color)
553             break
554         self.label_name_event_box.modify_bg(gtk.STATE_NORMAL, self.color_tuple[1])
555         self.vbox.pack_start(self.label_name_event_box, True)
556         frame = gtk.Frame()
557         frame.set_shadow_type(gtk.SHADOW_IN)
558         frame.add(self.abspeak);
559         self.vbox.pack_start(frame, False)
560
561         # hbox child at lower part
562         self.hbox = gtk.HBox()
563         self.hbox.pack_start(self.slider, True)
564         frame = gtk.Frame()
565         frame.set_shadow_type(gtk.SHADOW_IN)
566         frame.add(self.meter);
567         self.hbox.pack_start(frame, True)
568         frame = gtk.Frame()
569         frame.set_shadow_type(gtk.SHADOW_IN)
570         frame.add(self.hbox);
571         self.pack_start(frame, True)
572
573         self.volume_digits.set_size_request(0, -1)
574         self.pack_start(self.volume_digits, False)
575
576         self.create_balance_widget()
577
578         self.monitor_button = gtk.ToggleButton('MON')
579         self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
580         self.pack_start(self.monitor_button, False, False)
581
582         # add control groups to the input channels, and initialize them
583         # appropriately
584         for input_channel in self.app.channels:
585             ctlgroup = input_channel.add_control_group(self)
586             if self._init_muted_channels and input_channel.channel.name in self._init_muted_channels:
587                 ctlgroup.mute.set_active(True)
588             if self._init_solo_channels and input_channel.channel.name in self._init_solo_channels:
589                 ctlgroup.solo.set_active(True)
590         self._init_muted_channels = None
591         self._init_solo_channels = None
592
593     channel_properties_dialog = None
594     def on_channel_properties(self):
595         if not self.channel_properties_dialog:
596             self.channel_properties_dialog = OutputChannelPropertiesDialog(self, self.app)
597         self.channel_properties_dialog.show()
598         self.channel_properties_dialog.present()
599
600     def on_label_mouse(self, widget, event):
601         if event.type == gtk.gdk._2BUTTON_PRESS:
602             if event.button == 1:
603                 self.on_channel_properties()
604
605     def unrealize(self):
606         # remove control groups from input channels
607         for input_channel in self.app.channels:
608             input_channel.remove_control_group(self)
609         # then remove itself
610         Channel.unrealize(self)
611         self.channel.remove()
612         self.channel = None
613
614     def serialization_name(self):
615         return output_channel_serialization_name()
616
617     def serialize(self, object_backend):
618         object_backend.add_property("name", self.channel_name)
619         if self.stereo:
620             object_backend.add_property("type", "stereo")
621         else:
622             object_backend.add_property("type", "mono")
623         if self.display_solo_buttons:
624             object_backend.add_property("solo_buttons", "true")
625         muted_channels = []
626         solo_channels = []
627         for input_channel in self.app.channels:
628             if self.channel.is_muted(input_channel.channel):
629                 muted_channels.append(input_channel)
630             if self.channel.is_solo(input_channel.channel):
631                 solo_channels.append(input_channel)
632         if muted_channels:
633             object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
634         if solo_channels:
635             object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
636         Channel.serialize(self, object_backend)
637
638     def unserialize_property(self, name, value):
639         if name == "name":
640             self.channel_name = str(value)
641             return True
642         if name == "type":
643             if value == "stereo":
644                 self.stereo = True
645                 return True
646             if value == "mono":
647                 self.stereo = False
648                 return True
649         if name == "solo_buttons":
650             if value == "true":
651                 self.display_solo_buttons = True
652                 return True
653         if name == 'muted_channels':
654             self._init_muted_channels = value.split('|')
655             return True
656         if name == 'solo_channels':
657             self._init_solo_channels = value.split('|')
658             return True
659         return Channel.unserialize_property(self, name, value)
660
661 def output_channel_serialization_name():
662     return "output_channel"
663
664 class MainMixChannel(Channel):
665     _init_muted_channels = None
666     _init_solo_channels = None
667
668     def __init__(self, app):
669         Channel.__init__(self, app, "MAIN", True)
670
671     def realize(self):
672         Channel.realize(self)
673         self.channel = self.mixer.main_mix_channel
674         self.channel.midi_scale = self.slider_scale.scale
675         self.channel.midi_change_callback = self.midi_change_callback
676
677         self.on_volume_changed(self.slider_adjustment)
678         self.on_balance_changed(self.balance_adjustment)
679
680         # vbox child at upper part
681         self.vbox = gtk.VBox()
682         self.pack_start(self.vbox, False)
683         self.label_name = gtk.Label()
684         self.label_name.set_text(self.channel_name)
685         self.label_name.set_size_request(0, -1)
686         self.vbox.pack_start(self.label_name, False)
687         frame = gtk.Frame()
688         frame.set_shadow_type(gtk.SHADOW_IN)
689         frame.add(self.abspeak);
690         self.vbox.pack_start(frame, False)
691
692         # hbox child at lower part
693         self.hbox = gtk.HBox()
694         self.hbox.pack_start(self.slider, True)
695         frame = gtk.Frame()
696         frame.set_shadow_type(gtk.SHADOW_IN)
697         frame.add(self.meter);
698         self.hbox.pack_start(frame, True)
699         frame = gtk.Frame()
700         frame.set_shadow_type(gtk.SHADOW_IN)
701         frame.add(self.hbox);
702         self.pack_start(frame, True)
703
704         self.volume_digits.set_size_request(0, -1)
705         self.pack_start(self.volume_digits, False)
706
707         self.create_balance_widget()
708
709         self.monitor_button = gtk.ToggleButton('MON')
710         self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
711         self.pack_start(self.monitor_button, False, False)
712
713         for input_channel in self.app.channels:
714             if self._init_muted_channels and input_channel.channel.name in self._init_muted_channels:
715                 input_channel.mute.set_active(True)
716             if self._init_solo_channels and input_channel.channel.name in self._init_solo_channels:
717                 input_channel.solo.set_active(True)
718         self._init_muted_channels = None
719         self._init_solo_channels = None
720
721     def unrealize(self):
722         Channel.unrealize(self)
723         self.channel = False
724
725     def serialization_name(self):
726         return main_mix_serialization_name()
727
728     def serialize(self, object_backend):
729         muted_channels = []
730         solo_channels = []
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)
736         if muted_channels:
737             object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
738         if solo_channels:
739             object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
740         Channel.serialize(self, object_backend)
741
742     def unserialize_property(self, name, value):
743         if name == 'muted_channels':
744             self._init_muted_channels = value.split('|')
745             return True
746         if name == 'solo_channels':
747             self._init_solo_channels = value.split('|')
748             return True
749         return Channel.unserialize_property(self, name, value)
750
751 def main_mix_serialization_name():
752     return "main_mix_channel"
753
754
755 class ChannelPropertiesDialog(gtk.Dialog):
756     channel = None
757
758     def __init__(self, parent, app):
759         self.channel = parent
760         self.app = app
761         self.mixer = self.channel.mixer
762         gtk.Dialog.__init__(self,
763                         'Channel "%s" Properties' % self.channel.channel_name,
764                         self.channel.gui_factory.topwindow)
765
766         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
767         self.ok_button = self.add_button(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY)
768
769         self.create_ui()
770         self.fill_ui()
771
772         self.connect('response', self.on_response_cb)
773         self.connect('delete-event', self.on_response_cb)
774
775     def create_frame(self, label, child):
776         frame = gtk.Frame('')
777         frame.set_border_width(3)
778         #frame.set_shadow_type(gtk.SHADOW_NONE)
779         frame.get_label_widget().set_markup('<b>%s</b>' % label)
780
781         alignment = gtk.Alignment(0, 0, 1, 1)
782         alignment.set_padding(0, 0, 12, 0)
783         frame.add(alignment)
784         alignment.add(child)
785
786         return frame
787
788     def create_ui(self):
789         vbox = gtk.VBox()
790         self.vbox.add(vbox)
791
792         table = gtk.Table(2, 2, False)
793         vbox.pack_start(self.create_frame('Properties', table))
794         table.set_row_spacings(5)
795         table.set_col_spacings(5)
796
797         table.attach(gtk.Label('Name'), 0, 1, 0, 1)
798         self.entry_name = gtk.Entry()
799         self.entry_name.set_activates_default(True)
800         self.entry_name.connect('changed', self.on_entry_name_changed)
801         table.attach(self.entry_name, 1, 2, 0, 1)
802
803         table.attach(gtk.Label('Mode'), 0, 1, 1, 2)
804         self.mode_hbox = gtk.HBox()
805         table.attach(self.mode_hbox, 1, 2, 1, 2)
806         self.mono = gtk.RadioButton(label='Mono')
807         self.stereo = gtk.RadioButton(label='Stereo', group=self.mono)
808         self.mode_hbox.pack_start(self.mono)
809         self.mode_hbox.pack_start(self.stereo)
810
811         table = gtk.Table(2, 3, False)
812         vbox.pack_start(self.create_frame('MIDI Control Channels', table))
813         table.set_row_spacings(5)
814         table.set_col_spacings(5)
815
816         table.attach(gtk.Label('Volume'), 0, 1, 0, 1)
817         self.entry_volume_cc = gtk.Entry()
818         self.entry_volume_cc.set_activates_default(True)
819         self.entry_volume_cc.set_editable(False)
820         self.entry_volume_cc.set_width_chars(3)
821         table.attach(self.entry_volume_cc, 1, 2, 0, 1)
822         self.button_sense_midi_volume = gtk.Button('Autoset')
823         self.button_sense_midi_volume.connect('clicked',
824                         self.on_sense_midi_volume_clicked)
825         table.attach(self.button_sense_midi_volume, 2, 3, 0, 1)
826
827         table.attach(gtk.Label('Balance'), 0, 1, 1, 2)
828         self.entry_balance_cc = gtk.Entry()
829         self.entry_balance_cc.set_activates_default(True)
830         self.entry_balance_cc.set_width_chars(3)
831         self.entry_balance_cc.set_editable(False)
832         table.attach(self.entry_balance_cc, 1, 2, 1, 2)
833         self.button_sense_midi_balance = gtk.Button('Autoset')
834         self.button_sense_midi_balance.connect('clicked',
835                         self.on_sense_midi_balance_clicked)
836         table.attach(self.button_sense_midi_balance, 2, 3, 1, 2)
837
838         self.vbox.show_all()
839
840     def fill_ui(self):
841         self.entry_name.set_text(self.channel.channel_name)
842         if self.channel.channel.is_stereo:
843             self.stereo.set_active(True)
844         else:
845             self.mono.set_active(True)
846         self.mode_hbox.set_sensitive(False)
847         self.entry_volume_cc.set_text('%s' % self.channel.channel.volume_midi_cc)
848         self.entry_balance_cc.set_text('%s' % self.channel.channel.balance_midi_cc)
849
850     def sense_popup_dialog(self, entry):
851         window = gtk.Window(gtk.WINDOW_TOPLEVEL)
852         window.set_destroy_with_parent(True)
853         window.set_transient_for(self)
854         window.set_decorated(False)
855         window.set_modal(True)
856         window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
857         window.set_border_width(10)
858
859         vbox = gtk.VBox(10)
860         window.add(vbox)
861         window.timeout = 5
862         vbox.pack_start(gtk.Label('Please move the MIDI control you want to use for this function.'))
863         timeout_label = gtk.Label('This window will close in 5 seconds')
864         vbox.pack_start(timeout_label)
865         def close_sense_timeout(window, entry):
866             window.timeout -= 1
867             timeout_label.set_text('This window will close in %d seconds.' % window.timeout)
868             if window.timeout == 0:
869                 window.destroy()
870                 entry.set_text('%s' % self.mixer.last_midi_channel)
871                 return False
872             return True
873         window.show_all()
874         glib.timeout_add_seconds(1, close_sense_timeout, window, entry)
875
876     def on_sense_midi_volume_clicked(self, *args):
877         self.sense_popup_dialog(self.entry_volume_cc)
878
879     def on_sense_midi_balance_clicked(self, *args):
880         self.sense_popup_dialog(self.entry_balance_cc)
881
882     def on_response_cb(self, dlg, response_id, *args):
883         self.channel.channel_properties_dialog = None
884         self.destroy()
885         if response_id == gtk.RESPONSE_APPLY:
886             name = self.entry_name.get_text()
887             self.channel.channel_name = name
888             self.channel.channel.volume_midi_cc = int(self.entry_volume_cc.get_text())
889             self.channel.channel.balance_midi_cc = int(self.entry_balance_cc.get_text())
890
891     def on_entry_name_changed(self, entry):
892         sensitive = False
893         if len(entry.get_text()):
894             if self.channel and self.channel.channel.name == entry.get_text():
895                 sensitive = True
896             elif entry.get_text() not in [x.channel.name for x in self.app.channels] + \
897                         [x.channel.name for x in self.app.output_channels] + ['MAIN']:
898                 sensitive = True
899         self.ok_button.set_sensitive(sensitive)
900
901
902 class NewChannelDialog(ChannelPropertiesDialog):
903     def __init__(self, app):
904         gtk.Dialog.__init__(self, 'New Channel', app.window)
905         self.mixer = app.mixer
906         self.app = app
907         self.create_ui()
908
909         self.stereo.set_active(True) # default to stereo
910
911         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
912         self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
913         self.ok_button.set_sensitive(False)
914         self.set_default_response(gtk.RESPONSE_OK);
915
916     def get_result(self):
917         return {'name': self.entry_name.get_text(),
918                 'stereo': self.stereo.get_active(),
919                 'volume_cc': self.entry_volume_cc.get_text(),
920                 'balance_cc': self.entry_balance_cc.get_text()
921                }
922
923 class OutputChannelPropertiesDialog(ChannelPropertiesDialog):
924     def create_ui(self):
925         ChannelPropertiesDialog.create_ui(self)
926
927         vbox = gtk.VBox()
928         self.vbox.pack_start(self.create_frame('Input Channels', vbox))
929
930         self.display_solo_buttons = gtk.CheckButton('Display solo buttons')
931         vbox.pack_start(self.display_solo_buttons)
932
933         self.vbox.show_all()
934
935     def fill_ui(self):
936         ChannelPropertiesDialog.fill_ui(self)
937         self.display_solo_buttons.set_active(self.channel.display_solo_buttons)
938
939     def on_response_cb(self, dlg, response_id, *args):
940         ChannelPropertiesDialog.on_response_cb(self, dlg, response_id, *args)
941         if response_id == gtk.RESPONSE_APPLY:
942             self.channel.display_solo_buttons = self.display_solo_buttons.get_active()
943
944
945 class NewOutputChannelDialog(OutputChannelPropertiesDialog):
946     def __init__(self, app):
947         gtk.Dialog.__init__(self, 'New Output Channel', app.window)
948         self.mixer = app.mixer
949         self.app = app
950         self.create_ui()
951
952         # TODO: disable mode for output channels as mono output channels may
953         # not be correctly handled yet.
954         self.mode_hbox.set_sensitive(False)
955         self.stereo.set_active(True) # default to stereo
956
957         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
958         self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
959         self.ok_button.set_sensitive(False)
960         self.set_default_response(gtk.RESPONSE_OK);
961
962     def get_result(self):
963         return {'name': self.entry_name.get_text(),
964                 'stereo': self.stereo.get_active(),
965                 'volume_cc': self.entry_volume_cc.get_text(),
966                 'balance_cc': self.entry_balance_cc.get_text(),
967                 'display_solo_buttons': self.display_solo_buttons.get_active(),
968                }
969
970
971 class ControlGroup(gtk.Alignment):
972     def __init__(self, output_channel, input_channel):
973         gtk.Alignment.__init__(self, 0.5, 0.5, 0, 0)
974         self.output_channel = output_channel
975         self.input_channel = input_channel
976         self.app = input_channel.app
977
978         hbox = gtk.HBox()
979         self.hbox = hbox
980         self.add(hbox)
981
982         mute = gtk.ToggleButton()
983         self.mute = mute
984         mute.set_label("M")
985         mute.connect("toggled", self.on_mute_toggled)
986         hbox.pack_start(mute, False)
987
988         solo = gtk.ToggleButton()
989         self.solo = solo
990         solo.set_label("S")
991         solo.connect("toggled", self.on_solo_toggled)
992         if self.output_channel.display_solo_buttons:
993             hbox.pack_start(solo, True)
994
995         mute.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
996         mute.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
997         mute.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
998         solo.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
999         solo.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
1000         solo.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
1001
1002     def update(self):
1003         if self.output_channel.display_solo_buttons:
1004             if not self.solo in self.hbox.get_children():
1005                 self.hbox.pack_start(self.solo, True)
1006                 self.solo.show()
1007         else:
1008             if self.solo in self.hbox.get_children():
1009                 self.hbox.remove(self.solo)
1010
1011     def on_mute_toggled(self, button):
1012         self.output_channel.channel.set_muted(self.input_channel.channel, button.get_active())
1013         self.app.update_monitor(self)
1014
1015     def on_solo_toggled(self, button):
1016         self.output_channel.channel.set_solo(self.input_channel.channel, button.get_active())
1017         self.app.update_monitor(self)
1018