2 # -*- coding: utf-8 -*-
4 # This file is part of jack_mixer
6 # Copyright (C) 2006-2009 Nedko Arnaudov <nedko@arnaudov.name>
7 # Copyright (C) 2009 Frederic Peters <fpeters@0d.be>
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; version 2 of the License
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22 from optparse
import OptionParser
34 print >> sys
.stderr
, "Cannot load LASH python bindings, you want them unless you enjoy manual jack plumbing each time you use this app"
36 # temporary change Python modules lookup path to look into installation
37 # directory ($prefix/share/jack_mixer/)
39 sys
.path
.insert(0, os
.path
.join(os
.path
.dirname(sys
.argv
[0]), '..', 'share', 'jack_mixer'))
49 from preferences
import PreferencesDialog
51 from serialization_xml
import XmlSerialization
52 from serialization
import SerializedObject
, Serializator
54 # restore Python modules lookup path
58 class TrayIcon(gtk
.StatusIcon
):
61 def __init__(self
, mixer
):
62 gtk
.StatusIcon
.__init
__(self
)
64 self
.set_from_icon_name( mixer
.window
.get_icon_name() )
65 self
.set_tooltip('Jack Mixer ('+mixer
.mixer
.client_name()+')')
66 self
.set_visible(True)
68 self
.menu
= menu
= gtk
.Menu()
70 window_item
= gtk
.MenuItem("Show Mixer")
71 window_item
.connect("activate", self
.show_window
, "Jack Mixer")
72 menu
.append(window_item
)
74 menu
.append(gtk
.SeparatorMenuItem())
76 quit_item
= gtk
.MenuItem("Quit")
77 quit_item
.connect("activate", self
.mixer
.on_quit_cb
, "quit")
78 menu
.append(quit_item
)
81 self
.connect("activate", self
.show_window
)
82 self
.connect('popup-menu', self
.icon_clicked
)
84 def show_window(self
, widget
, event
=None):
85 if self
.mixer
.window
.get_property("visible"):
86 self
.mixer
.window
.hide()
88 self
.mixer
.window
.present()
90 def icon_clicked(self
, status
, button
, time
):
91 self
.menu
.popup(None, None, None, button
, time
)
95 class JackMixer(SerializedObject
):
97 # scales suitable as meter scales
98 meter_scales
= [scale
.IEC268(), scale
.Linear70dB(), scale
.IEC268Minimalistic()]
100 # scales suitable as volume slider scales
101 slider_scales
= [scale
.Linear30dB(), scale
.Linear70dB()]
103 # name of settngs file that is currently open
104 current_filename
= None
106 def __init__(self
, name
, lash_client
):
107 self
.mixer
= jack_mixer_c
.Mixer(name
)
110 self
.monitor_channel
= self
.mixer
.add_output_channel("Monitor", True, True)
115 # Send our client name to server
116 lash_event
= lash
.lash_event_new_with_type(lash
.LASH_Client_Name
)
117 lash
.lash_event_set_string(lash_event
, name
)
118 lash
.lash_send_event(lash_client
, lash_event
)
120 lash
.lash_jack_client_name(lash_client
, name
)
122 self
.window
= gtk
.Window(gtk
.WINDOW_TOPLEVEL
)
123 if name
!= self
.mixer
.client_name():
124 self
.window
.set_title(name
+ " ("+ self
.mixer
.client_name()+")" )
126 self
.window
.set_title(name
)
128 self
.window
.set_icon_name('jack_mixer')
129 self
.gui_factory
= gui
.Factory(self
.window
, self
.meter_scales
, self
.slider_scales
)
131 self
.vbox_top
= gtk
.VBox()
132 self
.window
.add(self
.vbox_top
)
134 self
.menubar
= gtk
.MenuBar()
135 self
.vbox_top
.pack_start(self
.menubar
, False)
137 mixer_menu_item
= gtk
.MenuItem("_Mixer")
138 self
.menubar
.append(mixer_menu_item
)
139 edit_menu_item
= gtk
.MenuItem('_Edit')
140 self
.menubar
.append(edit_menu_item
)
141 help_menu_item
= gtk
.MenuItem('_Help')
142 self
.menubar
.append(help_menu_item
)
144 self
.window
.set_default_size(120, 300)
146 mixer_menu
= gtk
.Menu()
147 mixer_menu_item
.set_submenu(mixer_menu
)
149 add_input_channel
= gtk
.ImageMenuItem('New _Input Channel')
150 mixer_menu
.append(add_input_channel
)
151 add_input_channel
.connect("activate", self
.on_add_input_channel
)
153 add_output_channel
= gtk
.ImageMenuItem('New _Output Channel')
154 mixer_menu
.append(add_output_channel
)
155 add_output_channel
.connect("activate", self
.on_add_output_channel
)
157 mixer_menu
.append(gtk
.SeparatorMenuItem())
158 open = gtk
.ImageMenuItem(gtk
.STOCK_OPEN
)
159 mixer_menu
.append(open)
160 open.connect('activate', self
.on_open_cb
)
161 save
= gtk
.ImageMenuItem(gtk
.STOCK_SAVE
)
162 mixer_menu
.append(save
)
163 save
.connect('activate', self
.on_save_cb
)
164 save_as
= gtk
.ImageMenuItem(gtk
.STOCK_SAVE_AS
)
165 mixer_menu
.append(save_as
)
166 save_as
.connect('activate', self
.on_save_as_cb
)
168 mixer_menu
.append(gtk
.SeparatorMenuItem())
170 quit
= gtk
.ImageMenuItem(gtk
.STOCK_QUIT
)
171 mixer_menu
.append(quit
)
172 quit
.connect('activate', self
.on_quit_cb
)
174 edit_menu
= gtk
.Menu()
175 edit_menu_item
.set_submenu(edit_menu
)
177 self
.channel_edit_input_menu_item
= gtk
.MenuItem('_Edit Input Channel')
178 edit_menu
.append(self
.channel_edit_input_menu_item
)
179 self
.channel_edit_input_menu
= gtk
.Menu()
180 self
.channel_edit_input_menu_item
.set_submenu(self
.channel_edit_input_menu
)
182 self
.channel_edit_output_menu_item
= gtk
.MenuItem('Edit _Output Channel')
183 edit_menu
.append(self
.channel_edit_output_menu_item
)
184 self
.channel_edit_output_menu
= gtk
.Menu()
185 self
.channel_edit_output_menu_item
.set_submenu(self
.channel_edit_output_menu
)
187 self
.channel_remove_input_menu_item
= gtk
.MenuItem('Remove _Input Channel')
188 edit_menu
.append(self
.channel_remove_input_menu_item
)
189 self
.channel_remove_input_menu
= gtk
.Menu()
190 self
.channel_remove_input_menu_item
.set_submenu(self
.channel_remove_input_menu
)
192 self
.channel_remove_output_menu_item
= gtk
.MenuItem('_Remove Output Channel')
193 edit_menu
.append(self
.channel_remove_output_menu_item
)
194 self
.channel_remove_output_menu
= gtk
.Menu()
195 self
.channel_remove_output_menu_item
.set_submenu(self
.channel_remove_output_menu
)
197 channel_remove_all_menu_item
= gtk
.ImageMenuItem(gtk
.STOCK_CLEAR
)
198 edit_menu
.append(channel_remove_all_menu_item
)
199 channel_remove_all_menu_item
.connect("activate", self
.on_channels_clear
)
201 edit_menu
.append(gtk
.SeparatorMenuItem())
203 preferences
= gtk
.ImageMenuItem(gtk
.STOCK_PREFERENCES
)
204 preferences
.connect('activate', self
.on_preferences_cb
)
205 edit_menu
.append(preferences
)
207 help_menu
= gtk
.Menu()
208 help_menu_item
.set_submenu(help_menu
)
210 about
= gtk
.ImageMenuItem(gtk
.STOCK_ABOUT
)
211 help_menu
.append(about
)
212 about
.connect("activate", self
.on_about
)
214 self
.hbox_top
= gtk
.HBox()
215 self
.vbox_top
.pack_start(self
.hbox_top
, True)
217 self
.scrolled_window
= gtk
.ScrolledWindow()
218 self
.hbox_top
.pack_start(self
.scrolled_window
, True)
220 self
.hbox_inputs
= gtk
.HBox()
221 self
.hbox_inputs
.set_spacing(0)
222 self
.hbox_inputs
.set_border_width(0)
223 self
.hbox_top
.set_spacing(0)
224 self
.hbox_top
.set_border_width(0)
226 self
.output_channels
= []
228 self
.scrolled_window
.set_policy(gtk
.POLICY_AUTOMATIC
, gtk
.POLICY_AUTOMATIC
)
229 self
.scrolled_window
.add_with_viewport(self
.hbox_inputs
)
231 self
.main_mix
= MainMixChannel(self
)
232 self
.hbox_outputs
= gtk
.HBox()
233 self
.hbox_outputs
.set_spacing(0)
234 self
.hbox_outputs
.set_border_width(0)
236 frame
.add(self
.main_mix
)
237 self
.hbox_outputs
.pack_start(frame
, False)
238 self
.hbox_top
.pack_start(self
.hbox_outputs
, False)
240 self
.window
.connect("destroy", gtk
.main_quit
)
242 self
.trayicon
= TrayIcon(self
)
243 self
.window
.connect('delete-event', self
.on_delete_event
)
245 gobject
.timeout_add(80, self
.read_meters
)
246 self
.lash_client
= lash_client
248 gobject
.timeout_add(200, self
.lash_check_events
)
250 gobject
.timeout_add(50, self
.midi_events_check
)
253 def on_delete_event(self
, widget
, event
):
254 if self
.gui_factory
.get_minimize_to_tray():
262 def sighandler(self
, signum
, frame
):
263 #print "Signal %d received" % signum
264 if signum
== signal
.SIGUSR1
:
266 elif signum
== signal
.SIGTERM
:
268 elif signum
== signal
.SIGINT
:
271 print "Unknown signal %d received" % signum
274 print "Cleaning jack_mixer"
278 for channel
in self
.channels
:
283 def on_open_cb(self
, *args
):
284 dlg
= gtk
.FileChooserDialog(title
='Open', parent
=self
.window
,
285 action
=gtk
.FILE_CHOOSER_ACTION_OPEN
,
286 buttons
=(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
,
287 gtk
.STOCK_OPEN
, gtk
.RESPONSE_OK
))
288 dlg
.set_default_response(gtk
.RESPONSE_OK
)
289 if dlg
.run() == gtk
.RESPONSE_OK
:
290 filename
= dlg
.get_filename()
292 f
= file(filename
, 'r')
293 self
.load_from_xml(f
)
295 err
= gtk
.MessageDialog(self
.window
,
299 "Failed loading settings.")
303 self
.current_filename
= filename
308 def on_save_cb(self
, *args
):
309 if not self
.current_filename
:
310 return self
.on_save_as_cb()
311 f
= file(self
.current_filename
, 'w')
315 def on_save_as_cb(self
, *args
):
316 dlg
= gtk
.FileChooserDialog(title
='Save', parent
=self
.window
,
317 action
=gtk
.FILE_CHOOSER_ACTION_SAVE
,
318 buttons
=(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
,
319 gtk
.STOCK_SAVE
, gtk
.RESPONSE_OK
))
320 dlg
.set_default_response(gtk
.RESPONSE_OK
)
321 if dlg
.run() == gtk
.RESPONSE_OK
:
322 self
.current_filename
= dlg
.get_filename()
326 def on_quit_cb(self
, *args
):
329 preferences_dialog
= None
330 def on_preferences_cb(self
, widget
):
331 if not self
.preferences_dialog
:
332 self
.preferences_dialog
= PreferencesDialog(self
)
333 self
.preferences_dialog
.show()
334 self
.preferences_dialog
.present()
336 def on_add_input_channel(self
, widget
):
337 dialog
= NewChannelDialog(app
=self
)
338 dialog
.set_transient_for(self
.window
)
343 if ret
== gtk
.RESPONSE_OK
:
344 result
= dialog
.get_result()
345 channel
= self
.add_channel(**result
)
346 self
.window
.show_all()
348 def on_add_output_channel(self
, widget
):
349 dialog
= NewOutputChannelDialog(app
=self
)
350 dialog
.set_transient_for(self
.window
)
355 if ret
== gtk
.RESPONSE_OK
:
356 result
= dialog
.get_result()
357 channel
= self
.add_output_channel(**result
)
358 self
.window
.show_all()
360 def on_edit_input_channel(self
, widget
, channel
):
361 print 'Editing channel "%s"' % channel
.channel_name
362 channel
.on_channel_properties()
364 def remove_channel_edit_input_menuitem_by_label(self
, widget
, label
):
365 if (widget
.get_label() == label
):
366 self
.channel_edit_input_menu
.remove(widget
)
368 def on_remove_input_channel(self
, widget
, channel
):
369 print 'Removing channel "%s"' % channel
.channel_name
370 self
.channel_remove_input_menu
.remove(widget
)
371 self
.channel_edit_input_menu
.foreach(
372 self
.remove_channel_edit_input_menuitem_by_label
,
373 channel
.channel_name
);
374 if self
.monitored_channel
is channel
:
375 channel
.monitor_button
.set_active(False)
376 for i
in range(len(self
.channels
)):
377 if self
.channels
[i
] is channel
:
380 self
.hbox_inputs
.remove(channel
.parent
)
382 if len(self
.channels
) == 0:
383 self
.channel_remove_input_menu_item
.set_sensitive(False)
385 def on_edit_output_channel(self
, widget
, channel
):
386 print 'Editing channel "%s"' % channel
.channel_name
387 channel
.on_channel_properties()
389 def remove_channel_edit_output_menuitem_by_label(self
, widget
, label
):
390 if (widget
.get_label() == label
):
391 self
.channel_edit_output_menu
.remove(widget
)
393 def on_remove_output_channel(self
, widget
, channel
):
394 print 'Removing channel "%s"' % channel
.channel_name
395 self
.channel_remove_output_menu
.remove(widget
)
396 self
.channel_edit_output_menu
.foreach(
397 self
.remove_channel_edit_output_menuitem_by_label
,
398 channel
.channel_name
);
399 if self
.monitored_channel
is channel
:
400 channel
.monitor_button
.set_active(False)
401 for i
in range(len(self
.channels
)):
402 if self
.output_channels
[i
] is channel
:
404 del self
.output_channels
[i
]
405 self
.hbox_outputs
.remove(channel
.parent
)
407 if len(self
.output_channels
) == 0:
408 self
.channel_remove_output_menu_item
.set_sensitive(False)
410 def rename_channels(self
, container
, parameters
):
411 if (container
.get_label() == parameters
['oldname']):
412 container
.set_label(parameters
['newname'])
414 def on_channel_rename(self
, oldname
, newname
):
415 rename_parameters
= { 'oldname' : oldname
, 'newname' : newname
}
416 self
.channel_edit_input_menu
.foreach(self
.rename_channels
,
418 self
.channel_edit_output_menu
.foreach(self
.rename_channels
,
420 self
.channel_remove_input_menu
.foreach(self
.rename_channels
,
422 self
.channel_remove_output_menu
.foreach(self
.rename_channels
,
424 print "Renaming channel from %s to %s\n" % (oldname
, newname
)
427 def on_channels_clear(self
, widget
):
428 for channel
in self
.output_channels
:
430 self
.hbox_outputs
.remove(channel
.parent
)
431 for channel
in self
.channels
:
433 self
.hbox_inputs
.remove(channel
.parent
)
435 self
.output_channels
= []
436 self
.channel_edit_input_menu
= gtk
.Menu()
437 self
.channel_edit_input_menu_item
.set_submenu(self
.channel_edit_input_menu
)
438 self
.channel_edit_input_menu_item
.set_sensitive(False)
439 self
.channel_remove_input_menu
= gtk
.Menu()
440 self
.channel_remove_input_menu_item
.set_submenu(self
.channel_remove_input_menu
)
441 self
.channel_remove_input_menu_item
.set_sensitive(False)
442 self
.channel_edit_output_menu
= gtk
.Menu()
443 self
.channel_edit_output_menu_item
.set_submenu(self
.channel_edit_output_menu
)
444 self
.channel_edit_output_menu_item
.set_sensitive(False)
445 self
.channel_remove_output_menu
= gtk
.Menu()
446 self
.channel_remove_output_menu_item
.set_submenu(self
.channel_remove_output_menu
)
447 self
.channel_remove_output_menu_item
.set_sensitive(False)
449 def add_channel(self
, name
, stereo
, volume_cc
, balance_cc
):
451 channel
= InputChannel(self
, name
, stereo
)
452 self
.add_channel_precreated(channel
)
454 err
= gtk
.MessageDialog(self
.window
,
455 gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
,
458 "Channel creation failed")
463 channel
.channel
.volume_midi_cc
= int(volume_cc
)
465 channel
.channel
.balance_midi_cc
= int(balance_cc
)
466 if not (volume_cc
or balance_cc
):
467 channel
.channel
.autoset_midi_cc()
471 def add_channel_precreated(self
, channel
):
474 self
.hbox_inputs
.pack_start(frame
, False)
477 channel_edit_menu_item
= gtk
.MenuItem(channel
.channel_name
)
478 self
.channel_edit_input_menu
.append(channel_edit_menu_item
)
479 channel_edit_menu_item
.connect("activate", self
.on_edit_input_channel
, channel
)
480 self
.channel_edit_input_menu_item
.set_sensitive(True)
482 channel_remove_menu_item
= gtk
.MenuItem(channel
.channel_name
)
483 self
.channel_remove_input_menu
.append(channel_remove_menu_item
)
484 channel_remove_menu_item
.connect("activate", self
.on_remove_input_channel
, channel
)
485 self
.channel_remove_input_menu_item
.set_sensitive(True)
487 self
.channels
.append(channel
)
489 for outputchannel
in self
.output_channels
:
490 channel
.add_control_group(outputchannel
)
492 # create post fader output channel matching the input channel
493 channel
.post_fader_output_channel
= self
.mixer
.add_output_channel(
494 channel
.channel
.name
+ ' Out', channel
.channel
.is_stereo
, True)
495 channel
.post_fader_output_channel
.volume
= 0
496 channel
.post_fader_output_channel
.set_solo(channel
.channel
, True)
498 def read_meters(self
):
499 for channel
in self
.channels
:
501 self
.main_mix
.read_meter()
502 for channel
in self
.output_channels
:
506 def midi_events_check(self
):
507 for channel
in self
.channels
+ [self
.main_mix
] + self
.output_channels
:
508 channel
.midi_events_check()
511 def add_output_channel(self
, name
, stereo
, volume_cc
, balance_cc
, display_solo_buttons
):
513 channel
= OutputChannel(self
, name
, stereo
)
514 channel
.display_solo_buttons
= display_solo_buttons
515 self
.add_output_channel_precreated(channel
)
517 err
= gtk
.MessageDialog(self
.window
,
518 gtk
.DIALOG_MODAL | gtk
.DIALOG_DESTROY_WITH_PARENT
,
521 "Channel creation failed")
526 channel
.channel
.volume_midi_cc
= int(volume_cc
)
528 channel
.channel
.balance_midi_cc
= int(balance_cc
)
531 def add_output_channel_precreated(self
, channel
):
534 self
.hbox_outputs
.pack_start(frame
, False)
537 channel_edit_menu_item
= gtk
.MenuItem(channel
.channel_name
)
538 self
.channel_edit_output_menu
.append(channel_edit_menu_item
)
539 channel_edit_menu_item
.connect("activate", self
.on_edit_output_channel
, channel
)
540 self
.channel_edit_output_menu_item
.set_sensitive(True)
542 channel_remove_menu_item
= gtk
.MenuItem(channel
.channel_name
)
543 self
.channel_remove_output_menu
.append(channel_remove_menu_item
)
544 channel_remove_menu_item
.connect("activate", self
.on_remove_output_channel
, channel
)
545 self
.channel_remove_output_menu_item
.set_sensitive(True)
547 self
.output_channels
.append(channel
)
549 _monitored_channel
= None
550 def get_monitored_channel(self
):
551 return self
._monitored
_channel
553 def set_monitored_channel(self
, channel
):
554 if self
._monitored
_channel
:
555 if channel
.channel
.name
== self
._monitored
_channel
.channel
.name
:
557 self
._monitored
_channel
= channel
558 if type(channel
) is InputChannel
:
559 # reset all solo/mute settings
560 for in_channel
in self
.channels
:
561 self
.monitor_channel
.set_solo(in_channel
.channel
, False)
562 self
.monitor_channel
.set_muted(in_channel
.channel
, False)
563 self
.monitor_channel
.set_solo(channel
.channel
, True)
564 self
.monitor_channel
.prefader
= True
566 self
.monitor_channel
.prefader
= False
567 self
.update_monitor(channel
)
568 monitored_channel
= property(get_monitored_channel
, set_monitored_channel
)
570 def update_monitor(self
, channel
):
571 if self
.monitored_channel
is not channel
:
573 self
.monitor_channel
.volume
= channel
.channel
.volume
574 self
.monitor_channel
.balance
= channel
.channel
.balance
575 if type(self
.monitored_channel
) is OutputChannel
:
576 # sync solo/muted channels
577 for input_channel
in self
.channels
:
578 self
.monitor_channel
.set_solo(input_channel
.channel
,
579 channel
.channel
.is_solo(input_channel
.channel
))
580 self
.monitor_channel
.set_muted(input_channel
.channel
,
581 channel
.channel
.is_muted(input_channel
.channel
))
582 elif type(self
.monitored_channel
) is MainMixChannel
:
583 # sync solo/muted channels
584 for input_channel
in self
.channels
:
585 self
.monitor_channel
.set_solo(input_channel
.channel
,
586 input_channel
.channel
.solo
)
587 self
.monitor_channel
.set_muted(input_channel
.channel
,
588 input_channel
.channel
.mute
)
590 def get_input_channel_by_name(self
, name
):
591 for input_channel
in self
.channels
:
592 if input_channel
.channel
.name
== name
:
596 def on_about(self
, *args
):
597 about
= gtk
.AboutDialog()
598 about
.set_name('jack_mixer')
599 about
.set_copyright('Copyright © 2006-2010\nNedko Arnaudov, Frederic Peters, Arnout Engelen')
600 about
.set_license('''\
601 jack_mixer is free software; you can redistribute it and/or modify it
602 under the terms of the GNU General Public License as published by the
603 Free Software Foundation; either version 2 of the License, or (at your
604 option) any later version.
606 jack_mixer is distributed in the hope that it will be useful, but
607 WITHOUT ANY WARRANTY; without even the implied warranty of
608 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
609 General Public License for more details.
611 You should have received a copy of the GNU General Public License along
612 with jack_mixer; if not, write to the Free Software Foundation, Inc., 51
613 Franklin Street, Fifth Floor, Boston, MA 02110-130159 USA''')
614 about
.set_authors(['Nedko Arnaudov <nedko@arnaudov.name>',
615 'Frederic Peters <fpeters@0d.be>'])
616 about
.set_logo_icon_name('jack_mixer')
617 about
.set_website('http://home.gna.org/jackmixer/')
622 def lash_check_events(self
):
625 if self
.current_filename
:
626 print "saving on SIGUSR1 request"
630 print "not saving because filename is not known"
633 if not self
.lash_client
:
636 while lash
.lash_get_pending_event_count(self
.lash_client
):
637 event
= lash
.lash_get_event(self
.lash_client
)
641 event_type
= lash
.lash_event_get_type(event
)
642 if event_type
== lash
.LASH_Quit
:
643 print "jack_mixer: LASH ordered quit."
646 elif event_type
== lash
.LASH_Save_File
:
647 directory
= lash
.lash_event_get_string(event
)
648 print "jack_mixer: LASH ordered to save data in directory %s" % directory
649 filename
= directory
+ os
.sep
+ "jack_mixer.xml"
650 f
= file(filename
, "w")
653 lash
.lash_send_event(self
.lash_client
, event
) # we crash with double free
654 elif event_type
== lash
.LASH_Restore_File
:
655 directory
= lash
.lash_event_get_string(event
)
656 print "jack_mixer: LASH ordered to restore data from directory %s" % directory
657 filename
= directory
+ os
.sep
+ "jack_mixer.xml"
658 f
= file(filename
, "r")
659 self
.load_from_xml(f
, silence_errors
=True)
661 lash
.lash_send_event(self
.lash_client
, event
)
663 print "jack_mixer: Got unhandled LASH event, type " + str(event_type
)
666 #lash.lash_event_destroy(event)
670 def save_to_xml(self
, file):
671 #print "Saving to XML..."
672 b
= XmlSerialization()
677 def load_from_xml(self
, file, silence_errors
=False):
678 #print "Loading from XML..."
679 self
.on_channels_clear(None)
680 self
.unserialized_channels
= []
681 b
= XmlSerialization()
689 s
.unserialize(self
, b
)
690 for channel
in self
.unserialized_channels
:
691 if isinstance(channel
, InputChannel
):
692 self
.add_channel_precreated(channel
)
693 for channel
in self
.unserialized_channels
:
694 if isinstance(channel
, OutputChannel
):
695 self
.add_output_channel_precreated(channel
)
696 del self
.unserialized_channels
697 self
.window
.show_all()
699 def serialize(self
, object_backend
):
700 object_backend
.add_property('geometry',
701 '%sx%s' % (self
.window
.allocation
.width
, self
.window
.allocation
.height
))
703 def unserialize_property(self
, name
, value
):
704 if name
== 'geometry':
705 width
, height
= value
.split('x')
706 self
.window
.resize(int(width
), int(height
))
709 def unserialize_child(self
, name
):
710 if name
== MainMixChannel
.serialization_name():
713 if name
== InputChannel
.serialization_name():
714 channel
= InputChannel(self
, "", True)
715 self
.unserialized_channels
.append(channel
)
718 if name
== OutputChannel
.serialization_name():
719 channel
= OutputChannel(self
, "", True)
720 self
.unserialized_channels
.append(channel
)
723 def serialization_get_childs(self
):
724 '''Get child objects tha required and support serialization'''
725 childs
= self
.channels
[:] + self
.output_channels
[:]
726 childs
.append(self
.main_mix
)
729 def serialization_name(self
):
733 self
.main_mix
.realize()
734 self
.main_mix
.set_monitored()
739 self
.window
.show_all()
741 signal
.signal(signal
.SIGUSR1
, self
.sighandler
)
742 signal
.signal(signal
.SIGTERM
, self
.sighandler
)
743 signal
.signal(signal
.SIGINT
, self
.sighandler
)
744 signal
.signal(signal
.SIGHUP
, signal
.SIG_IGN
)
748 #f = file("/dev/stdout", "w")
753 print "Usage: %s [mixer_name]" % sys
.argv
[0]
756 # Connect to LASH if Python bindings are available, and the user did not
758 if lash
and not '--no-lash' in sys
.argv
:
759 # sys.argv is modified by this call
760 lash_client
= lash
.init(sys
.argv
, "jack_mixer", lash
.LASH_Config_File
)
764 parser
= OptionParser(usage
='usage: %prog [options] [jack_client_name]')
765 parser
.add_option('-c', '--config', dest
='config',
766 help='use a non default configuration file')
767 # --no-lash here is not acted upon, it is specified for completeness when
769 parser
.add_option('--no-lash', dest
='nolash', action
='store_true',
770 help='do not connect to LASH')
771 options
, args
= parser
.parse_args()
773 # Yeah , this sounds stupid, we connected earlier, but we dont want to show this if we got --help option
774 # This issue should be fixed in pylash, there is a reason for having two functions for initialization after all
776 server_name
= lash
.lash_get_server_name(lash_client
)
778 print "Successfully connected to LASH server at " + server_name
780 # getting the server name failed, probably not worth trying to do
781 # further things with as a lash client.
793 mixer
= JackMixer(name
, lash_client
)
795 err
= gtk
.MessageDialog(None,
799 "Mixer creation failed (%s)" % str(e
))
805 f
= file(options
.config
)
806 mixer
.current_filename
= options
.config
808 mixer
.load_from_xml(f
)
810 err
= gtk
.MessageDialog(mixer
.window
,
814 "Failed loading settings.")
817 mixer
.window
.set_default_size(60*(1+len(mixer
.channels
)+len(mixer
.output_channels
)), 300)
824 if __name__
== "__main__":