]> git.0d.be Git - jack_mixer.git/blob - channel.py
Menu items to remove output channels individually
[jack_mixer.git] / channel.py
1 #!/usr/bin/env python
2 #
3 # This file is part of jack_mixer
4 #
5 # Copyright (C) 2006 Nedko Arnaudov <nedko@arnaudov.name>
6 #  
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
10 #
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.
15 #
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.
19
20 import gtk
21 import gobject
22 import glib
23 import slider
24 import meter
25 import abspeak
26 from serialization import SerializedObject
27
28 try:
29     import phat
30 except:
31     phat = None
32
33
34 class Channel(gtk.VBox, SerializedObject):
35     '''Widget with slider and meter used as base class for more specific
36        channel widgets'''
37     monitor_button = None
38
39     def __init__(self, app, name, stereo):
40         gtk.VBox.__init__(self)
41         self.app = app
42         self.mixer = app.mixer
43         self.gui_factory = app.gui_factory
44         self._channel_name = name
45         self.stereo = stereo
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
52
53     def get_channel_name(self):
54         return self._channel_name
55
56     label_name = None
57     channel = None
58     def set_channel_name(self, name):
59         self._channel_name = name
60         if self.label_name:
61             self.label_name.set_text(name)
62         if self.channel:
63             self.channel.name = name
64     channel_name = property(get_channel_name, set_channel_name)
65
66     def realize(self):
67         #print "Realizing channel \"%s\"" % self.channel_name
68
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)
72
73         self.slider = None
74         self.create_slider_widget()
75
76         if self.stereo:
77             self.meter = meter.StereoMeterWidget(self.meter_scale)
78         else:
79             self.meter = meter.MonoMeterWidget(self.meter_scale)
80         self.on_vumeter_color_changed(self.gui_factory)
81
82         self.meter.set_events(gtk.gdk.SCROLL_MASK)
83
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)
89
90         self.abspeak = abspeak.AbspeakWidget()
91         self.abspeak.connect("reset", self.on_abspeak_reset)
92         self.abspeak.connect("volume-adjust", self.on_abspeak_adjust)
93
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)
97
98         self.connect("key-press-event", self.on_key_pressed)
99         self.connect("scroll-event", self.on_scroll)
100
101     def unrealize(self):
102         #print "Unrealizing channel \"%s\"" % self.channel_name
103         pass
104
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)
110         else:
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)
116         self.balance.show()
117
118     def create_slider_widget(self):
119         parent = None
120         if self.slider:
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)
125         else:
126             self.slider = slider.GtkSlider(self.slider_adjustment)
127         if parent:
128             parent.pack_start(self.slider)
129             parent.reorder_child(self.slider, 0)
130         self.slider.show()
131
132     def on_default_meter_scale_changed(self, gui_factory, scale):
133         #print "Default meter scale change detected."
134         self.meter.set_scale(scale)
135
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
141
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)
147         else:
148             self.meter.set_color(gtk.gdk.color_parse(color))
149
150     def on_custom_widgets_changed(self, gui_factory, value):
151         self.balance.destroy()
152         self.create_balance_widget()
153         self.create_slider_widget()
154
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)
160
161     def on_abspeak_reset(self, abspeak):
162         #print "abspeak reset"
163         self.channel.abspeak = None
164
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()
168             try:
169                 db = float(db_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)
174                 return
175             self.slider_adjustment.set_value_db(db)
176             #self.grab_focus()
177             #self.update_volume(False)   # We want to update gui even if actual decibels have not changed (scale wrap for example)
178
179     def on_volume_digits_focus_out(self, widget, event):
180         #print "volume digits focus out detected"
181         self.update_volume(False)
182
183     def read_meter(self):
184         if not self.channel:
185             return
186         if self.stereo:
187             meter_left, meter_right = self.channel.meter
188             self.meter.set_values(meter_left, meter_right)
189         else:
190             self.meter.set_value(self.channel.meter[0])
191
192         self.abspeak.set_peak(self.channel.abspeak)
193
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()
199         return True
200
201     def update_volume(self, update_engine):
202         db = self.slider_adjustment.get_value_db()
203
204         db_text = "%.2f" % db
205         self.volume_digits.set_text(db_text)
206
207         if update_engine:
208             #print "Setting engine volume to " + db_text
209             self.channel.volume = db
210             self.app.update_monitor(self)
211
212     def on_volume_changed(self, adjustment):
213         self.update_volume(True)
214
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)
220
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()
225             return True
226         elif (event.keyval == gtk.keysyms.Down):
227             #print self.channel_name + " Down"
228             self.slider_adjustment.step_down()
229             return True
230
231         return False
232
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())
236
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))
241
242     def unserialize_property(self, name, value):
243         if name == "volume":
244             self.slider_adjustment.set_value_db(float(value))
245             return True
246         if name == "balance":
247             self.balance_adjustment.set_value(float(value))
248             return True
249         if name == 'volume_midi_cc':
250             self.future_volume_midi_cc = int(value)
251             return True
252         if name == 'balance_midi_cc':
253             self.future_balance_midi_cc = int(value)
254             return True
255         return False
256
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)
260
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')
265
266     def on_monitor_button_toggled(self, button):
267         if not button.get_active():
268             self.app.main_mix.monitor_button.set_active(True)
269         else:
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)
278
279     def set_monitored(self):
280         if self.channel:
281             self.app.set_monitored_channel(self)
282         self.monitor_button.set_active(True)
283
284 gobject.signal_new('midi-event-received', Channel,
285                 gobject.SIGNAL_RUN_FIRST | gobject.SIGNAL_ACTION,
286                 gobject.TYPE_NONE, ())
287
288 class InputChannel(Channel):
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         self.channel.remove()
396         self.channel = None
397
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()
404
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()
409
410     def on_mute_toggled(self, button):
411         self.channel.mute = self.mute.get_active()
412         self.app.update_monitor(self.app.main_mix)
413
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
434             return True
435         return False
436
437     def on_solo_toggled(self, button):
438         self.channel.solo = self.solo.get_active()
439         self.app.update_monitor(self.app.main_mix)
440
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
461             return True
462         return False
463
464     def serialization_name(self):
465         return input_channel_serialization_name()
466
467     def serialize(self, object_backend):
468         object_backend.add_property("name", self.channel_name)
469         if self.stereo:
470             object_backend.add_property("type", "stereo")
471         else:
472             object_backend.add_property("type", "mono")
473         Channel.serialize(self, object_backend)
474
475     def unserialize_property(self, name, value):
476         if name == "name":
477             self.channel_name = str(value)
478             return True
479         if name == "type":
480             if value == "stereo":
481                 self.stereo = True
482                 return True
483             if value == "mono":
484                 self.stereo = False
485                 return True
486         return Channel.unserialize_property(self, name, value)
487
488 def input_channel_serialization_name():
489     return "input_channel"
490
491
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'),
500 ]
501
502 class OutputChannel(Channel):
503     colours = available_colours[:]
504     _display_solo_buttons = False
505
506     _init_muted_channels = None
507     _init_solo_channels = None
508
509     def __init__(self, app, name, stereo):
510         Channel.__init__(self, app, name, stereo)
511
512     def get_display_solo_buttons(self):
513         return self._display_solo_buttons
514
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)
520
521     display_solo_buttons = property(get_display_solo_buttons, set_display_solo_buttons)
522
523     def realize(self):
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)
529
530         self.channel.midi_scale = self.slider_scale.scale
531         self.channel.midi_change_callback = self.midi_change_callback
532
533         self.on_volume_changed(self.slider_adjustment)
534         self.on_balance_changed(self.balance_adjustment)
535
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)
545         if not self.colours:
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)
550             break
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)
553         frame = gtk.Frame()
554         frame.set_shadow_type(gtk.SHADOW_IN)
555         frame.add(self.abspeak);
556         self.vbox.pack_start(frame, False)
557
558         # hbox child at lower part
559         self.hbox = gtk.HBox()
560         self.hbox.pack_start(self.slider, True)
561         frame = gtk.Frame()
562         frame.set_shadow_type(gtk.SHADOW_IN)
563         frame.add(self.meter);
564         self.hbox.pack_start(frame, True)
565         frame = gtk.Frame()
566         frame.set_shadow_type(gtk.SHADOW_IN)
567         frame.add(self.hbox);
568         self.pack_start(frame, True)
569
570         self.volume_digits.set_size_request(0, -1)
571         self.pack_start(self.volume_digits, False)
572
573         self.create_balance_widget()
574
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)
578
579         # add control groups to the input channels, and initialize them
580         # appropriately
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
589
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()
596
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()
601
602     def unrealize(self):
603         # remove control groups from input channels
604         for input_channel in self.app.channels:
605             input_channel.remove_control_group(self)
606         # then remove itself
607         Channel.unrealize(self)
608         self.channel.remove()
609         self.channel = None
610
611     def serialization_name(self):
612         return output_channel_serialization_name()
613
614     def serialize(self, object_backend):
615         object_backend.add_property("name", self.channel_name)
616         if self.stereo:
617             object_backend.add_property("type", "stereo")
618         else:
619             object_backend.add_property("type", "mono")
620         if self.display_solo_buttons:
621             object_backend.add_property("solo_buttons", "true")
622         muted_channels = []
623         solo_channels = []
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)
629         if muted_channels:
630             object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
631         if solo_channels:
632             object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
633         Channel.serialize(self, object_backend)
634
635     def unserialize_property(self, name, value):
636         if name == "name":
637             self.channel_name = str(value)
638             return True
639         if name == "type":
640             if value == "stereo":
641                 self.stereo = True
642                 return True
643             if value == "mono":
644                 self.stereo = False
645                 return True
646         if name == "solo_buttons":
647             if value == "true":
648                 self.display_solo_buttons = True
649                 return True
650         if name == 'muted_channels':
651             self._init_muted_channels = value.split('|')
652             return True
653         if name == 'solo_channels':
654             self._init_solo_channels = value.split('|')
655             return True
656         return Channel.unserialize_property(self, name, value)
657
658 def output_channel_serialization_name():
659     return "output_channel"
660
661 class MainMixChannel(Channel):
662     _init_muted_channels = None
663     _init_solo_channels = None
664
665     def __init__(self, app):
666         Channel.__init__(self, app, "MAIN", True)
667
668     def realize(self):
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
673
674         self.on_volume_changed(self.slider_adjustment)
675         self.on_balance_changed(self.balance_adjustment)
676
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)
684         frame = gtk.Frame()
685         frame.set_shadow_type(gtk.SHADOW_IN)
686         frame.add(self.abspeak);
687         self.vbox.pack_start(frame, False)
688
689         # hbox child at lower part
690         self.hbox = gtk.HBox()
691         self.hbox.pack_start(self.slider, True)
692         frame = gtk.Frame()
693         frame.set_shadow_type(gtk.SHADOW_IN)
694         frame.add(self.meter);
695         self.hbox.pack_start(frame, True)
696         frame = gtk.Frame()
697         frame.set_shadow_type(gtk.SHADOW_IN)
698         frame.add(self.hbox);
699         self.pack_start(frame, True)
700
701         self.volume_digits.set_size_request(0, -1)
702         self.pack_start(self.volume_digits, False)
703
704         self.create_balance_widget()
705
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)
709
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
717
718     def unrealize(self):
719         Channel.unrealize(self)
720         self.channel = False
721
722     def serialization_name(self):
723         return main_mix_serialization_name()
724
725     def serialize(self, object_backend):
726         muted_channels = []
727         solo_channels = []
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)
733         if muted_channels:
734             object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
735         if solo_channels:
736             object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
737         Channel.serialize(self, object_backend)
738
739     def unserialize_property(self, name, value):
740         if name == 'muted_channels':
741             self._init_muted_channels = value.split('|')
742             return True
743         if name == 'solo_channels':
744             self._init_solo_channels = value.split('|')
745             return True
746         return Channel.unserialize_property(self, name, value)
747
748 def main_mix_serialization_name():
749     return "main_mix_channel"
750
751
752 class ChannelPropertiesDialog(gtk.Dialog):
753     channel = None
754
755     def __init__(self, parent, app):
756         self.channel = parent
757         self.app = app
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)
762
763         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
764         self.ok_button = self.add_button(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY)
765
766         self.create_ui()
767         self.fill_ui()
768
769         self.connect('response', self.on_response_cb)
770         self.connect('delete-event', self.on_response_cb)
771
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)
777
778         alignment = gtk.Alignment(0, 0, 1, 1)
779         alignment.set_padding(0, 0, 12, 0)
780         frame.add(alignment)
781         alignment.add(child)
782
783         return frame
784
785     def create_ui(self):
786         vbox = gtk.VBox()
787         self.vbox.add(vbox)
788
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)
793
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)
799
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)
807
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)
812
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)
823
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)
834
835         self.vbox.show_all()
836
837     def fill_ui(self):
838         self.entry_name.set_text(self.channel.channel_name)
839         if self.channel.channel.is_stereo:
840             self.stereo.set_active(True)
841         else:
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)
846
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)
855
856         vbox = gtk.VBox(10)
857         window.add(vbox)
858         window.timeout = 5
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):
863             window.timeout -= 1
864             timeout_label.set_text('This window will close in %d seconds.' % window.timeout)
865             if window.timeout == 0:
866                 window.destroy()
867                 entry.set_text('%s' % self.mixer.last_midi_channel)
868                 return False
869             return True
870         window.show_all()
871         glib.timeout_add_seconds(1, close_sense_timeout, window, entry)
872
873     def on_sense_midi_volume_clicked(self, *args):
874         self.sense_popup_dialog(self.entry_volume_cc)
875
876     def on_sense_midi_balance_clicked(self, *args):
877         self.sense_popup_dialog(self.entry_balance_cc)
878
879     def on_response_cb(self, dlg, response_id, *args):
880         self.channel.channel_properties_dialog = None
881         self.destroy()
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())
887
888     def on_entry_name_changed(self, entry):
889         sensitive = False
890         if len(entry.get_text()):
891             if self.channel and self.channel.channel.name == entry.get_text():
892                 sensitive = True
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']:
895                 sensitive = True
896         self.ok_button.set_sensitive(sensitive)
897
898
899 class NewChannelDialog(ChannelPropertiesDialog):
900     def __init__(self, app):
901         gtk.Dialog.__init__(self, 'New Channel', app.window)
902         self.mixer = app.mixer
903         self.app = app
904         self.create_ui()
905
906         self.stereo.set_active(True) # default to stereo
907
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);
912
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()
918                }
919
920 class OutputChannelPropertiesDialog(ChannelPropertiesDialog):
921     def create_ui(self):
922         ChannelPropertiesDialog.create_ui(self)
923
924         vbox = gtk.VBox()
925         self.vbox.pack_start(self.create_frame('Input Channels', vbox))
926
927         self.display_solo_buttons = gtk.CheckButton('Display solo buttons')
928         vbox.pack_start(self.display_solo_buttons)
929
930         self.vbox.show_all()
931
932     def fill_ui(self):
933         ChannelPropertiesDialog.fill_ui(self)
934         self.display_solo_buttons.set_active(self.channel.display_solo_buttons)
935
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()
940
941
942 class NewOutputChannelDialog(OutputChannelPropertiesDialog):
943     def __init__(self, app):
944         gtk.Dialog.__init__(self, 'New Output Channel', app.window)
945         self.mixer = app.mixer
946         self.app = app
947         self.create_ui()
948
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
953
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);
958
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(),
965                }
966
967
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
974
975         hbox = gtk.HBox()
976         self.hbox = hbox
977         self.add(hbox)
978
979         mute = gtk.ToggleButton()
980         self.mute = mute
981         mute.set_label("M")
982         mute.connect("toggled", self.on_mute_toggled)
983         hbox.pack_start(mute, False)
984
985         solo = gtk.ToggleButton()
986         self.solo = solo
987         solo.set_label("S")
988         solo.connect("toggled", self.on_solo_toggled)
989         if self.output_channel.display_solo_buttons:
990             hbox.pack_start(solo, True)
991
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])
998
999     def update(self):
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)
1003                 self.solo.show()
1004         else:
1005             if self.solo in self.hbox.get_children():
1006                 self.hbox.remove(self.solo)
1007
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)
1011
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)
1015