]> git.0d.be Git - jack_mixer.git/blob - channel.py
cleanup unused calc_channel_volumes funcs
[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 slider
21 import meter
22 import abspeak
23 from serialization import SerializedObject
24
25 try:
26     import phat
27 except:
28     phat = None
29
30
31 class Channel(gtk.VBox, SerializedObject):
32     '''Widget with slider and meter used as base class for more specific
33        channel widgets'''
34     monitor_button = None
35
36     def __init__(self, app, name, stereo):
37         gtk.VBox.__init__(self)
38         self.app = app
39         self.mixer = app.mixer
40         self.gui_factory = app.gui_factory
41         self._channel_name = name
42         self.stereo = stereo
43         self.meter_scale = self.gui_factory.get_default_meter_scale()
44         self.slider_scale = self.gui_factory.get_default_slider_scale()
45         self.slider_adjustment = slider.AdjustmentdBFS(self.slider_scale, 0.0)
46         self.balance_adjustment = gtk.Adjustment(0.0, -1.0, 1.0, 0.02)
47         self.future_volume_midi_cc = None
48         self.future_balance_midi_cc = None
49
50     def get_channel_name(self):
51         return self._channel_name
52
53     label_name = None
54     channel = None
55     post_fader_output_channel = None
56     def set_channel_name(self, name):
57         self.app.on_channel_rename(self._channel_name, name);
58         self._channel_name = name
59         if self.label_name:
60             self.label_name.set_text(name)
61         if self.channel:
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)
66
67     def realize(self):
68         #print "Realizing channel \"%s\"" % self.channel_name
69
70         self.slider_adjustment.connect("volume-changed", self.on_volume_changed)
71         self.balance_adjustment.connect("value-changed", self.on_balance_changed)
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             self.channel.volume = db
209             self.app.update_monitor(self)
210
211     def on_volume_changed(self, adjustment):
212         self.update_volume(True)
213
214     def on_balance_changed(self, adjustment):
215         balance = self.balance_adjustment.get_value()
216         #print "%s balance: %f" % (self.channel_name, balance)
217         self.channel.balance = balance
218         self.app.update_monitor(self)
219
220     def on_key_pressed(self, widget, event):
221         if (event.keyval == gtk.keysyms.Up):
222             #print self.channel_name + " Up"
223             self.slider_adjustment.step_up()
224             return True
225         elif (event.keyval == gtk.keysyms.Down):
226             #print self.channel_name + " Down"
227             self.slider_adjustment.step_down()
228             return True
229
230         return False
231
232     def serialize(self, object_backend):
233         object_backend.add_property("volume", "%f" % self.slider_adjustment.get_value_db())
234         object_backend.add_property("balance", "%f" % self.balance_adjustment.get_value())
235
236         if self.channel.volume_midi_cc:
237             object_backend.add_property('volume_midi_cc', str(self.channel.volume_midi_cc))
238         if self.channel.balance_midi_cc:
239             object_backend.add_property('balance_midi_cc', str(self.channel.balance_midi_cc))
240
241     def unserialize_property(self, name, value):
242         if name == "volume":
243             self.slider_adjustment.set_value_db(float(value))
244             return True
245         if name == "balance":
246             self.balance_adjustment.set_value(float(value))
247             return True
248         if name == 'volume_midi_cc':
249             self.future_volume_midi_cc = int(value)
250             return True
251         if name == 'balance_midi_cc':
252             self.future_balance_midi_cc = int(value)
253             return True
254         return False
255
256     def midi_events_check(self):
257         if self.channel.midi_in_got_events:
258             self.slider_adjustment.set_value_db(self.channel.volume)
259             self.balance_adjustment.set_value(self.channel.balance)
260
261     def on_midi_event_received(self, *args):
262         self.slider_adjustment.set_value_db(self.channel.volume)
263         self.balance_adjustment.set_value(self.channel.balance)
264
265     def on_monitor_button_toggled(self, button):
266         if not button.get_active():
267             self.app.main_mix.monitor_button.set_active(True)
268         else:
269             for channel in self.app.channels + self.app.output_channels + [self.app.main_mix]:
270                 if channel.monitor_button.get_active() and channel.monitor_button is not button:
271                     channel.monitor_button.handler_block_by_func(
272                                 channel.on_monitor_button_toggled)
273                     channel.monitor_button.set_active(False)
274                     channel.monitor_button.handler_unblock_by_func(
275                                 channel.on_monitor_button_toggled)
276             self.app.set_monitored_channel(self)
277
278     def set_monitored(self):
279         if self.channel:
280             self.app.set_monitored_channel(self)
281         self.monitor_button.set_active(True)
282
283 class InputChannel(Channel):
284     post_fader_output_channel = None
285
286     def __init__(self, app, name, stereo):
287         Channel.__init__(self, app, name, stereo)
288
289     def realize(self):
290         self.channel = self.mixer.add_channel(self.channel_name, self.stereo)
291         if self.channel == None:
292             raise Exception,"Cannot create a channel"
293         Channel.realize(self)
294         if self.future_volume_midi_cc:
295             self.channel.volume_midi_cc = self.future_volume_midi_cc
296         if self.future_balance_midi_cc:
297             self.channel.balance_midi_cc = self.future_balance_midi_cc
298         self.channel.midi_scale = self.slider_scale.scale
299
300         self.on_volume_changed(self.slider_adjustment)
301         self.on_balance_changed(self.balance_adjustment)
302
303         # vbox child at upper part
304         self.vbox = gtk.VBox()
305         self.pack_start(self.vbox, False)
306         self.label_name = gtk.Label()
307         self.label_name.set_text(self.channel_name)
308         self.label_name.set_size_request(0, -1)
309         self.label_name_event_box = gtk.EventBox()
310         self.label_name_event_box.connect("button-press-event", self.on_label_mouse)
311         self.label_name_event_box.add(self.label_name)
312         self.vbox.pack_start(self.label_name_event_box, True)
313 #         self.label_stereo = gtk.Label()
314 #         if self.stereo:
315 #             self.label_stereo.set_text("stereo")
316 #         else:
317 #             self.label_stereo.set_text("mono")
318 #         self.label_stereo.set_size_request(0, -1)
319 #         self.vbox.pack_start(self.label_stereo, True)
320
321         # hbox for mute and solo buttons
322         self.hbox_mutesolo = gtk.HBox()
323
324         self.mute = gtk.ToggleButton()
325         self.mute.set_label("M")
326         self.mute.set_active(self.channel.mute)
327         self.mute.connect("button-press-event", self.on_mute_button_pressed)
328         self.mute.connect("toggled", self.on_mute_toggled)
329         self.hbox_mutesolo.pack_start(self.mute, True)
330
331         self.solo = gtk.ToggleButton()
332         self.solo.set_label("S")
333         self.solo.set_active(self.channel.solo)
334         self.solo.connect("button-press-event", self.on_solo_button_pressed)
335         self.solo.connect("toggled", self.on_solo_toggled)
336         self.hbox_mutesolo.pack_start(self.solo, True)
337
338         self.vbox.pack_start(self.hbox_mutesolo, False)
339
340         frame = gtk.Frame()
341         frame.set_shadow_type(gtk.SHADOW_IN)
342         frame.add(self.abspeak);
343         self.pack_start(frame, False)
344
345         # hbox child at lower part
346         self.hbox = gtk.HBox()
347         self.hbox.pack_start(self.slider, True)
348         frame = gtk.Frame()
349         frame.set_shadow_type(gtk.SHADOW_IN)
350         frame.add(self.meter);
351         self.hbox.pack_start(frame, True)
352         frame = gtk.Frame()
353         frame.set_shadow_type(gtk.SHADOW_IN)
354         frame.add(self.hbox);
355         self.pack_start(frame, True)
356
357         self.volume_digits.set_size_request(0, -1)
358         self.pack_start(self.volume_digits, False)
359
360         self.create_balance_widget()
361
362         self.monitor_button = gtk.ToggleButton('MON')
363         self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
364         self.pack_start(self.monitor_button, False, False)
365
366     def add_control_group(self, channel):
367         control_group = ControlGroup(channel, self)
368         control_group.show_all()
369         self.vbox.pack_start(control_group, False)
370         return control_group
371
372     def remove_control_group(self, channel):
373         ctlgroup = self.get_control_group(channel)
374         self.vbox.remove(ctlgroup)
375
376     def update_control_group(self, channel):
377         for control_group in self.vbox.get_children():
378             if isinstance(control_group, ControlGroup):
379                 if control_group.output_channel is channel:
380                     control_group.update()
381
382     def get_control_group(self, channel):
383         for control_group in self.vbox.get_children():
384             if isinstance(control_group, ControlGroup):
385                 if control_group.output_channel is channel:
386                     return control_group
387         return None
388
389     def unrealize(self):
390         Channel.unrealize(self)
391         if self.post_fader_output_channel:
392             self.post_fader_output_channel.remove()
393             self.post_fader_output_channel = None
394         self.channel.remove()
395         self.channel = None
396
397     channel_properties_dialog = None
398     def on_channel_properties(self):
399         if not self.channel_properties_dialog:
400             self.channel_properties_dialog = ChannelPropertiesDialog(self, self.app)
401         self.channel_properties_dialog.show()
402         self.channel_properties_dialog.present()
403
404     def on_label_mouse(self, widget, event):
405         if event.type == gtk.gdk._2BUTTON_PRESS:
406             if event.button == 1:
407                 self.on_channel_properties()
408
409     def on_mute_toggled(self, button):
410         self.channel.mute = self.mute.get_active()
411         self.app.update_monitor(self.app.main_mix)
412
413     def on_mute_button_pressed(self, button, event, *args):
414         if event.button == 3:
415             # right click on the mute button, act on all output channels
416             if button.get_active(): # was muted
417                 button.set_active(False)
418                 if hasattr(button, 'touched_channels'):
419                     touched_channels = button.touched_channels
420                     for chan in touched_channels:
421                         ctlgroup = self.get_control_group(chan)
422                         ctlgroup.mute.set_active(False)
423                     del button.touched_channels
424             else: # was not muted
425                 button.set_active(True)
426                 touched_channels = []
427                 for chan in self.app.output_channels:
428                     ctlgroup = self.get_control_group(chan)
429                     if not ctlgroup.mute.get_active():
430                         ctlgroup.mute.set_active(True)
431                         touched_channels.append(chan)
432                 button.touched_channels = touched_channels
433             return True
434         return False
435
436     def on_solo_toggled(self, button):
437         self.channel.solo = self.solo.get_active()
438         self.app.update_monitor(self.app.main_mix)
439
440     def on_solo_button_pressed(self, button, event, *args):
441         if event.button == 3:
442             # right click on the solo button, act on all output channels
443             if button.get_active(): # was soloed
444                 button.set_active(False)
445                 if hasattr(button, 'touched_channels'):
446                     touched_channels = button.touched_channels
447                     for chan in touched_channels:
448                         ctlgroup = self.get_control_group(chan)
449                         ctlgroup.solo.set_active(False)
450                     del button.touched_channels
451             else: # was not soloed
452                 button.set_active(True)
453                 touched_channels = []
454                 for chan in self.app.output_channels:
455                     ctlgroup = self.get_control_group(chan)
456                     if not ctlgroup.solo.get_active():
457                         ctlgroup.solo.set_active(True)
458                         touched_channels.append(chan)
459                 button.touched_channels = touched_channels
460             return True
461         return False
462
463     @classmethod
464     def serialization_name(cls):
465         return 'input_channel'
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
489 available_colours = [
490     ('#ef2929', '#cc0000', '#840000'),
491     ('#729fcf', '#3465a4', '#204a67'),
492     ('#8aa234', '#73d216', '#4e7a06'),
493     ('#fce84f', '#edd400', '#c48000'),
494     ('#fcaf3e', '#f57900', '#ae5c00'),
495     ('#ad7fa8', '#75507b', '#4c3556'),
496     ('#e9b96e', '#c17d11', '#6f4902'),
497 ]
498
499 class OutputChannel(Channel):
500     colours = available_colours[:]
501     _display_solo_buttons = False
502
503     _init_muted_channels = None
504     _init_solo_channels = None
505
506     def __init__(self, app, name, stereo):
507         Channel.__init__(self, app, name, stereo)
508
509     def get_display_solo_buttons(self):
510         return self._display_solo_buttons
511
512     def set_display_solo_buttons(self, value):
513         self._display_solo_buttons = value
514         # notifying control groups
515         for inputchannel in self.app.channels:
516             inputchannel.update_control_group(self)
517
518     display_solo_buttons = property(get_display_solo_buttons, set_display_solo_buttons)
519
520     def realize(self):
521         Channel.realize(self)
522         self.channel = self.mixer.add_output_channel(self.channel_name, self.stereo)
523         if self.channel == None:
524             raise Exception,"Cannot create a channel"
525         Channel.realize(self)
526
527         self.channel.midi_scale = self.slider_scale.scale
528
529         self.on_volume_changed(self.slider_adjustment)
530         self.on_balance_changed(self.balance_adjustment)
531
532         # vbox child at upper part
533         self.vbox = gtk.VBox()
534         self.pack_start(self.vbox, False)
535         self.label_name = gtk.Label()
536         self.label_name.set_text(self.channel_name)
537         self.label_name.set_size_request(0, -1)
538         self.label_name_event_box = gtk.EventBox()
539         self.label_name_event_box.connect('button-press-event', self.on_label_mouse)
540         self.label_name_event_box.add(self.label_name)
541         if not self.colours:
542             OutputChannel.colours = available_colours[:]
543         for color in self.colours:
544             self.color_tuple = [gtk.gdk.color_parse(color[x]) for x in range(3)]
545             self.colours.remove(color)
546             break
547         self.label_name_event_box.modify_bg(gtk.STATE_NORMAL, self.color_tuple[1])
548         self.vbox.pack_start(self.label_name_event_box, True)
549         frame = gtk.Frame()
550         frame.set_shadow_type(gtk.SHADOW_IN)
551         frame.add(self.abspeak);
552         self.vbox.pack_start(frame, False)
553
554         # hbox child at lower part
555         self.hbox = gtk.HBox()
556         self.hbox.pack_start(self.slider, True)
557         frame = gtk.Frame()
558         frame.set_shadow_type(gtk.SHADOW_IN)
559         frame.add(self.meter);
560         self.hbox.pack_start(frame, True)
561         frame = gtk.Frame()
562         frame.set_shadow_type(gtk.SHADOW_IN)
563         frame.add(self.hbox);
564         self.pack_start(frame, True)
565
566         self.volume_digits.set_size_request(0, -1)
567         self.pack_start(self.volume_digits, False)
568
569         self.create_balance_widget()
570
571         self.monitor_button = gtk.ToggleButton('MON')
572         self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
573         self.pack_start(self.monitor_button, False, False)
574
575         # add control groups to the input channels, and initialize them
576         # appropriately
577         for input_channel in self.app.channels:
578             ctlgroup = input_channel.add_control_group(self)
579             if self._init_muted_channels and input_channel.channel.name in self._init_muted_channels:
580                 ctlgroup.mute.set_active(True)
581             if self._init_solo_channels and input_channel.channel.name in self._init_solo_channels:
582                 ctlgroup.solo.set_active(True)
583         self._init_muted_channels = None
584         self._init_solo_channels = None
585
586     channel_properties_dialog = None
587     def on_channel_properties(self):
588         if not self.channel_properties_dialog:
589             self.channel_properties_dialog = OutputChannelPropertiesDialog(self, self.app)
590         self.channel_properties_dialog.show()
591         self.channel_properties_dialog.present()
592
593     def on_label_mouse(self, widget, event):
594         if event.type == gtk.gdk._2BUTTON_PRESS:
595             if event.button == 1:
596                 self.on_channel_properties()
597
598     def unrealize(self):
599         # remove control groups from input channels
600         for input_channel in self.app.channels:
601             input_channel.remove_control_group(self)
602         # then remove itself
603         Channel.unrealize(self)
604         self.channel.remove()
605         self.channel = None
606
607     @classmethod
608     def serialization_name(cls):
609         return 'output_channel'
610
611     def serialize(self, object_backend):
612         object_backend.add_property("name", self.channel_name)
613         if self.stereo:
614             object_backend.add_property("type", "stereo")
615         else:
616             object_backend.add_property("type", "mono")
617         if self.display_solo_buttons:
618             object_backend.add_property("solo_buttons", "true")
619         muted_channels = []
620         solo_channels = []
621         for input_channel in self.app.channels:
622             if self.channel.is_muted(input_channel.channel):
623                 muted_channels.append(input_channel)
624             if self.channel.is_solo(input_channel.channel):
625                 solo_channels.append(input_channel)
626         if muted_channels:
627             object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
628         if solo_channels:
629             object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
630         Channel.serialize(self, object_backend)
631
632     def unserialize_property(self, name, value):
633         if name == "name":
634             self.channel_name = str(value)
635             return True
636         if name == "type":
637             if value == "stereo":
638                 self.stereo = True
639                 return True
640             if value == "mono":
641                 self.stereo = False
642                 return True
643         if name == "solo_buttons":
644             if value == "true":
645                 self.display_solo_buttons = True
646                 return True
647         if name == 'muted_channels':
648             self._init_muted_channels = value.split('|')
649             return True
650         if name == 'solo_channels':
651             self._init_solo_channels = value.split('|')
652             return True
653         return Channel.unserialize_property(self, name, value)
654
655 class MainMixChannel(Channel):
656     _init_muted_channels = None
657     _init_solo_channels = None
658
659     def __init__(self, app):
660         Channel.__init__(self, app, "MAIN", True)
661
662     def realize(self):
663         Channel.realize(self)
664         self.channel = self.mixer.main_mix_channel
665         self.channel.midi_scale = self.slider_scale.scale
666
667         self.on_volume_changed(self.slider_adjustment)
668         self.on_balance_changed(self.balance_adjustment)
669
670         # vbox child at upper part
671         self.vbox = gtk.VBox()
672         self.pack_start(self.vbox, False)
673         self.label_name = gtk.Label()
674         self.label_name.set_text(self.channel_name)
675         self.label_name.set_size_request(0, -1)
676         self.vbox.pack_start(self.label_name, False)
677         frame = gtk.Frame()
678         frame.set_shadow_type(gtk.SHADOW_IN)
679         frame.add(self.abspeak);
680         self.vbox.pack_start(frame, False)
681
682         # hbox child at lower part
683         self.hbox = gtk.HBox()
684         self.hbox.pack_start(self.slider, True)
685         frame = gtk.Frame()
686         frame.set_shadow_type(gtk.SHADOW_IN)
687         frame.add(self.meter);
688         self.hbox.pack_start(frame, True)
689         frame = gtk.Frame()
690         frame.set_shadow_type(gtk.SHADOW_IN)
691         frame.add(self.hbox);
692         self.pack_start(frame, True)
693
694         self.volume_digits.set_size_request(0, -1)
695         self.pack_start(self.volume_digits, False)
696
697         self.create_balance_widget()
698
699         self.monitor_button = gtk.ToggleButton('MON')
700         self.monitor_button.connect('toggled', self.on_monitor_button_toggled)
701         self.pack_start(self.monitor_button, False, False)
702
703         for input_channel in self.app.channels:
704             if self._init_muted_channels and input_channel.channel.name in self._init_muted_channels:
705                 input_channel.mute.set_active(True)
706             if self._init_solo_channels and input_channel.channel.name in self._init_solo_channels:
707                 input_channel.solo.set_active(True)
708         self._init_muted_channels = None
709         self._init_solo_channels = None
710
711     def unrealize(self):
712         Channel.unrealize(self)
713         self.channel = False
714
715     @classmethod
716     def serialization_name(cls):
717         return 'main_mix_channel'
718
719     def serialize(self, object_backend):
720         muted_channels = []
721         solo_channels = []
722         for input_channel in self.app.channels:
723             if input_channel.channel.mute:
724                 muted_channels.append(input_channel)
725             if input_channel.channel.solo:
726                 solo_channels.append(input_channel)
727         if muted_channels:
728             object_backend.add_property('muted_channels', '|'.join([x.channel.name for x in muted_channels]))
729         if solo_channels:
730             object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
731         Channel.serialize(self, object_backend)
732
733     def unserialize_property(self, name, value):
734         if name == 'muted_channels':
735             self._init_muted_channels = value.split('|')
736             return True
737         if name == 'solo_channels':
738             self._init_solo_channels = value.split('|')
739             return True
740         return Channel.unserialize_property(self, name, value)
741
742
743 class ChannelPropertiesDialog(gtk.Dialog):
744     channel = None
745
746     def __init__(self, parent, app):
747         self.channel = parent
748         self.app = app
749         self.mixer = self.channel.mixer
750         gtk.Dialog.__init__(self,
751                         'Channel "%s" Properties' % self.channel.channel_name,
752                         self.channel.gui_factory.topwindow)
753
754         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
755         self.ok_button = self.add_button(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY)
756         self.set_default_response(gtk.RESPONSE_APPLY);
757
758         self.create_ui()
759         self.fill_ui()
760
761         self.connect('response', self.on_response_cb)
762         self.connect('delete-event', self.on_response_cb)
763
764     def create_frame(self, label, child):
765         frame = gtk.Frame('')
766         frame.set_border_width(3)
767         #frame.set_shadow_type(gtk.SHADOW_NONE)
768         frame.get_label_widget().set_markup('<b>%s</b>' % label)
769
770         alignment = gtk.Alignment(0, 0, 1, 1)
771         alignment.set_padding(0, 0, 12, 0)
772         frame.add(alignment)
773         alignment.add(child)
774
775         return frame
776
777     def create_ui(self):
778         vbox = gtk.VBox()
779         self.vbox.add(vbox)
780
781         table = gtk.Table(2, 2, False)
782         vbox.pack_start(self.create_frame('Properties', table))
783         table.set_row_spacings(5)
784         table.set_col_spacings(5)
785
786         table.attach(gtk.Label('Name'), 0, 1, 0, 1)
787         self.entry_name = gtk.Entry()
788         self.entry_name.set_activates_default(True)
789         self.entry_name.connect('changed', self.on_entry_name_changed)
790         table.attach(self.entry_name, 1, 2, 0, 1)
791
792         table.attach(gtk.Label('Mode'), 0, 1, 1, 2)
793         self.mode_hbox = gtk.HBox()
794         table.attach(self.mode_hbox, 1, 2, 1, 2)
795         self.mono = gtk.RadioButton(label='Mono')
796         self.stereo = gtk.RadioButton(label='Stereo', group=self.mono)
797         self.mode_hbox.pack_start(self.mono)
798         self.mode_hbox.pack_start(self.stereo)
799
800         table = gtk.Table(2, 3, False)
801         vbox.pack_start(self.create_frame('MIDI Control Channels', table))
802         table.set_row_spacings(5)
803         table.set_col_spacings(5)
804
805         table.attach(gtk.Label('Volume'), 0, 1, 0, 1)
806         self.entry_volume_cc = gtk.Entry()
807         self.entry_volume_cc.set_activates_default(True)
808         self.entry_volume_cc.set_editable(False)
809         self.entry_volume_cc.set_width_chars(3)
810         table.attach(self.entry_volume_cc, 1, 2, 0, 1)
811         self.button_sense_midi_volume = gtk.Button('Autoset')
812         self.button_sense_midi_volume.connect('clicked',
813                         self.on_sense_midi_volume_clicked)
814         table.attach(self.button_sense_midi_volume, 2, 3, 0, 1)
815
816         table.attach(gtk.Label('Balance'), 0, 1, 1, 2)
817         self.entry_balance_cc = gtk.Entry()
818         self.entry_balance_cc.set_activates_default(True)
819         self.entry_balance_cc.set_width_chars(3)
820         self.entry_balance_cc.set_editable(False)
821         table.attach(self.entry_balance_cc, 1, 2, 1, 2)
822         self.button_sense_midi_balance = gtk.Button('Autoset')
823         self.button_sense_midi_balance.connect('clicked',
824                         self.on_sense_midi_balance_clicked)
825         table.attach(self.button_sense_midi_balance, 2, 3, 1, 2)
826
827         self.vbox.show_all()
828
829     def fill_ui(self):
830         self.entry_name.set_text(self.channel.channel_name)
831         if self.channel.channel.is_stereo:
832             self.stereo.set_active(True)
833         else:
834             self.mono.set_active(True)
835         self.mode_hbox.set_sensitive(False)
836         self.entry_volume_cc.set_text('%s' % self.channel.channel.volume_midi_cc)
837         self.entry_balance_cc.set_text('%s' % self.channel.channel.balance_midi_cc)
838
839     def sense_popup_dialog(self, entry):
840         window = gtk.Window(gtk.WINDOW_TOPLEVEL)
841         window.set_destroy_with_parent(True)
842         window.set_transient_for(self)
843         window.set_decorated(False)
844         window.set_modal(True)
845         window.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
846         window.set_border_width(10)
847
848         vbox = gtk.VBox(10)
849         window.add(vbox)
850         window.timeout = 5
851         vbox.pack_start(gtk.Label('Please move the MIDI control you want to use for this function.'))
852         timeout_label = gtk.Label('This window will close in 5 seconds')
853         vbox.pack_start(timeout_label)
854         def close_sense_timeout(window, entry):
855             window.timeout -= 1
856             timeout_label.set_text('This window will close in %d seconds.' % window.timeout)
857             if window.timeout == 0:
858                 window.destroy()
859                 entry.set_text('%s' % self.mixer.last_midi_channel)
860                 return False
861             return True
862         window.show_all()
863         gobject.timeout_add_seconds(1, close_sense_timeout, window, entry)
864
865     def on_sense_midi_volume_clicked(self, *args):
866         self.sense_popup_dialog(self.entry_volume_cc)
867
868     def on_sense_midi_balance_clicked(self, *args):
869         self.sense_popup_dialog(self.entry_balance_cc)
870
871     def on_response_cb(self, dlg, response_id, *args):
872         self.channel.channel_properties_dialog = None
873         name = self.entry_name.get_text()
874         if response_id == gtk.RESPONSE_APPLY:
875             self.channel.channel_name = name
876             try:
877                 self.channel.channel.volume_midi_cc = int(self.entry_volume_cc.get_text())
878             except ValueError:
879                 pass
880             try:
881                 self.channel.channel.balance_midi_cc = int(self.entry_balance_cc.get_text())
882             except ValueError:
883                 pass
884         self.destroy()
885
886     def on_entry_name_changed(self, entry):
887         sensitive = False
888         if len(entry.get_text()):
889             if self.channel and self.channel.channel.name == entry.get_text():
890                 sensitive = True
891             elif entry.get_text() not in [x.channel.name for x in self.app.channels] + \
892                         [x.channel.name for x in self.app.output_channels] + ['MAIN']:
893                 sensitive = True
894         self.ok_button.set_sensitive(sensitive)
895
896
897 class NewChannelDialog(ChannelPropertiesDialog):
898     def __init__(self, app):
899         gtk.Dialog.__init__(self, 'New Channel', app.window)
900         self.mixer = app.mixer
901         self.app = app
902         self.create_ui()
903
904         self.stereo.set_active(True) # default to stereo
905
906         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
907         self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
908         self.ok_button.set_sensitive(False)
909         self.set_default_response(gtk.RESPONSE_OK);
910
911     def get_result(self):
912         return {'name': self.entry_name.get_text(),
913                 'stereo': self.stereo.get_active(),
914                 'volume_cc': self.entry_volume_cc.get_text(),
915                 'balance_cc': self.entry_balance_cc.get_text()
916                }
917
918 class OutputChannelPropertiesDialog(ChannelPropertiesDialog):
919     def create_ui(self):
920         ChannelPropertiesDialog.create_ui(self)
921
922         vbox = gtk.VBox()
923         self.vbox.pack_start(self.create_frame('Input Channels', vbox))
924
925         self.display_solo_buttons = gtk.CheckButton('Display solo buttons')
926         vbox.pack_start(self.display_solo_buttons)
927
928         self.vbox.show_all()
929
930     def fill_ui(self):
931         ChannelPropertiesDialog.fill_ui(self)
932         self.display_solo_buttons.set_active(self.channel.display_solo_buttons)
933
934     def on_response_cb(self, dlg, response_id, *args):
935         if response_id == gtk.RESPONSE_APPLY:
936             self.channel.display_solo_buttons = self.display_solo_buttons.get_active()
937         ChannelPropertiesDialog.on_response_cb(self, dlg, response_id, *args)
938
939
940 class NewOutputChannelDialog(OutputChannelPropertiesDialog):
941     def __init__(self, app):
942         gtk.Dialog.__init__(self, 'New Output Channel', app.window)
943         self.mixer = app.mixer
944         self.app = app
945         self.create_ui()
946
947         # TODO: disable mode for output channels as mono output channels may
948         # not be correctly handled yet.
949         self.mode_hbox.set_sensitive(False)
950         self.stereo.set_active(True) # default to stereo
951
952         self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
953         self.ok_button = self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
954         self.ok_button.set_sensitive(False)
955         self.set_default_response(gtk.RESPONSE_OK);
956
957     def get_result(self):
958         return {'name': self.entry_name.get_text(),
959                 'stereo': self.stereo.get_active(),
960                 'volume_cc': self.entry_volume_cc.get_text(),
961                 'balance_cc': self.entry_balance_cc.get_text(),
962                 'display_solo_buttons': self.display_solo_buttons.get_active(),
963                }
964
965
966 class ControlGroup(gtk.Alignment):
967     def __init__(self, output_channel, input_channel):
968         gtk.Alignment.__init__(self, 0.5, 0.5, 0, 0)
969         self.output_channel = output_channel
970         self.input_channel = input_channel
971         self.app = input_channel.app
972
973         hbox = gtk.HBox()
974         self.hbox = hbox
975         self.add(hbox)
976
977         mute = gtk.ToggleButton()
978         self.mute = mute
979         mute.set_label("M")
980         mute.connect("toggled", self.on_mute_toggled)
981         hbox.pack_start(mute, False)
982
983         solo = gtk.ToggleButton()
984         self.solo = solo
985         solo.set_label("S")
986         solo.connect("toggled", self.on_solo_toggled)
987         if self.output_channel.display_solo_buttons:
988             hbox.pack_start(solo, True)
989
990         mute.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
991         mute.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
992         mute.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
993         solo.modify_bg(gtk.STATE_PRELIGHT, output_channel.color_tuple[0])
994         solo.modify_bg(gtk.STATE_NORMAL, output_channel.color_tuple[1])
995         solo.modify_bg(gtk.STATE_ACTIVE, output_channel.color_tuple[2])
996
997     def update(self):
998         if self.output_channel.display_solo_buttons:
999             if not self.solo in self.hbox.get_children():
1000                 self.hbox.pack_start(self.solo, True)
1001                 self.solo.show()
1002         else:
1003             if self.solo in self.hbox.get_children():
1004                 self.hbox.remove(self.solo)
1005
1006     def on_mute_toggled(self, button):
1007         self.output_channel.channel.set_muted(self.input_channel.channel, button.get_active())
1008         self.app.update_monitor(self)
1009
1010     def on_solo_toggled(self, button):
1011         self.output_channel.channel.set_solo(self.input_channel.channel, button.get_active())
1012         self.app.update_monitor(self)
1013