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
25 gi.require_version('Gtk', '3.0')
26 from gi.repository import Gtk
27 from gi.repository import GObject
28 from gi.repository import GLib
37 print("Cannot load LASH python bindings, you want them unless you enjoy manual jack plumbing each time you use this app", file=sys.stderr)
39 # temporary change Python modules lookup path to look into installation
40 # directory ($prefix/share/jack_mixer/)
42 sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), '..', 'share', 'jack_mixer'))
52 from preferences import PreferencesDialog
54 from serialization_xml import XmlSerialization
55 from serialization import SerializedObject, Serializator
57 # restore Python modules lookup path
60 class JackMixer(SerializedObject):
62 # scales suitable as meter scales
63 meter_scales = [scale.IEC268(), scale.Linear70dB(), scale.IEC268Minimalistic()]
65 # scales suitable as volume slider scales
66 slider_scales = [scale.Linear30dB(), scale.Linear70dB()]
68 # name of settngs file that is currently open
69 current_filename = None
71 _init_solo_channels = None
73 def __init__(self, name, lash_client):
74 self.mixer = jack_mixer_c.Mixer(name)
77 self.monitor_channel = self.mixer.add_output_channel("Monitor", True, True)
82 # Send our client name to server
83 lash_event = lash.lash_event_new_with_type(lash.LASH_Client_Name)
84 lash.lash_event_set_string(lash_event, name)
85 lash.lash_send_event(lash_client, lash_event)
87 lash.lash_jack_client_name(lash_client, name)
89 self.window = Gtk.Window(type=Gtk.WindowType.TOPLEVEL)
90 if name != self.mixer.client_name():
91 self.window.set_title(name + " ("+ self.mixer.client_name()+")" )
93 self.window.set_title(name)
95 self.window.set_icon_name('jack_mixer')
96 self.gui_factory = gui.Factory(self.window, self.meter_scales, self.slider_scales)
98 self.vbox_top = Gtk.VBox()
99 self.window.add(self.vbox_top)
101 self.menubar = Gtk.MenuBar()
102 self.vbox_top.pack_start(self.menubar, False, True, 0)
104 mixer_menu_item = Gtk.MenuItem.new_with_mnemonic("_Mixer")
105 self.menubar.append(mixer_menu_item)
106 edit_menu_item = Gtk.MenuItem.new_with_mnemonic('_Edit')
107 self.menubar.append(edit_menu_item)
108 help_menu_item = Gtk.MenuItem.new_with_mnemonic('_Help')
109 self.menubar.append(help_menu_item)
111 self.window.set_default_size(120, 300)
113 mixer_menu = Gtk.Menu()
114 mixer_menu_item.set_submenu(mixer_menu)
116 add_input_channel = Gtk.MenuItem.new_with_mnemonic('New _Input Channel')
117 mixer_menu.append(add_input_channel)
118 add_input_channel.connect("activate", self.on_add_input_channel)
120 add_output_channel = Gtk.MenuItem.new_with_mnemonic('New _Output Channel')
121 mixer_menu.append(add_output_channel)
122 add_output_channel.connect("activate", self.on_add_output_channel)
124 mixer_menu.append(Gtk.SeparatorMenuItem())
125 open = Gtk.MenuItem.new_with_mnemonic('_Open')
126 mixer_menu.append(open)
127 open.connect('activate', self.on_open_cb)
128 save = Gtk.MenuItem.new_with_mnemonic('_Save')
129 mixer_menu.append(save)
130 save.connect('activate', self.on_save_cb)
131 save_as = Gtk.MenuItem.new_with_mnemonic('Save_As')
132 mixer_menu.append(save_as)
133 save_as.connect('activate', self.on_save_as_cb)
135 mixer_menu.append(Gtk.SeparatorMenuItem())
137 quit = Gtk.MenuItem.new_with_mnemonic('_Quit')
138 mixer_menu.append(quit)
139 quit.connect('activate', self.on_quit_cb)
141 edit_menu = Gtk.Menu()
142 edit_menu_item.set_submenu(edit_menu)
144 self.channel_edit_input_menu_item = Gtk.MenuItem.new_with_mnemonic('_Edit Input Channel')
145 edit_menu.append(self.channel_edit_input_menu_item)
146 self.channel_edit_input_menu = Gtk.Menu()
147 self.channel_edit_input_menu_item.set_submenu(self.channel_edit_input_menu)
149 self.channel_edit_output_menu_item = Gtk.MenuItem.new_with_mnemonic('Edit _Output Channel')
150 edit_menu.append(self.channel_edit_output_menu_item)
151 self.channel_edit_output_menu = Gtk.Menu()
152 self.channel_edit_output_menu_item.set_submenu(self.channel_edit_output_menu)
154 self.channel_remove_input_menu_item = Gtk.MenuItem.new_with_mnemonic('Remove _Input Channel')
155 edit_menu.append(self.channel_remove_input_menu_item)
156 self.channel_remove_input_menu = Gtk.Menu()
157 self.channel_remove_input_menu_item.set_submenu(self.channel_remove_input_menu)
159 self.channel_remove_output_menu_item = Gtk.MenuItem.new_with_mnemonic('_Remove Output Channel')
160 edit_menu.append(self.channel_remove_output_menu_item)
161 self.channel_remove_output_menu = Gtk.Menu()
162 self.channel_remove_output_menu_item.set_submenu(self.channel_remove_output_menu)
164 channel_remove_all_menu_item = Gtk.MenuItem.new_with_mnemonic('Clear')
165 edit_menu.append(channel_remove_all_menu_item)
166 channel_remove_all_menu_item.connect("activate", self.on_channels_clear)
168 edit_menu.append(Gtk.SeparatorMenuItem())
170 preferences = Gtk.MenuItem.new_with_mnemonic('_Preferences')
171 preferences.connect('activate', self.on_preferences_cb)
172 edit_menu.append(preferences)
174 help_menu = Gtk.Menu()
175 help_menu_item.set_submenu(help_menu)
177 about = Gtk.MenuItem.new_with_mnemonic('_About')
178 help_menu.append(about)
179 about.connect("activate", self.on_about)
181 self.hbox_top = Gtk.HBox()
182 self.vbox_top.pack_start(self.hbox_top, True, True, 0)
184 self.scrolled_window = Gtk.ScrolledWindow()
185 self.hbox_top.pack_start(self.scrolled_window, True, True, 0)
187 self.hbox_inputs = Gtk.HBox()
188 self.hbox_inputs.set_spacing(0)
189 self.hbox_inputs.set_border_width(0)
190 self.hbox_top.set_spacing(0)
191 self.hbox_top.set_border_width(0)
193 self.output_channels = []
195 self.scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
196 self.scrolled_window.add(self.hbox_inputs)
198 self.hbox_outputs = Gtk.HBox()
199 self.hbox_outputs.set_spacing(0)
200 self.hbox_outputs.set_border_width(0)
202 self.hbox_outputs.pack_start(frame, False, True, 0)
203 self.hbox_top.pack_start(self.hbox_outputs, False, True, 0)
205 self.window.connect("destroy", Gtk.main_quit)
207 self.window.connect('delete-event', self.on_delete_event)
209 GLib.timeout_add(80, self.read_meters)
210 self.lash_client = lash_client
212 GLib.timeout_add(200, self.lash_check_events)
214 GLib.timeout_add(50, self.midi_events_check)
217 def on_delete_event(self, widget, event):
218 if self.gui_factory.get_minimize_to_tray():
226 def sighandler(self, signum, frame):
227 #print "Signal %d received" % signum
228 if signum == signal.SIGUSR1:
230 elif signum == signal.SIGTERM:
232 elif signum == signal.SIGINT:
235 print("Unknown signal %d received" % signum)
238 print("Cleaning jack_mixer")
242 for channel in self.channels:
247 def on_open_cb(self, *args):
248 dlg = Gtk.FileChooserDialog(title='Open', parent=self.window,
249 action=Gtk.FileChooserAction.OPEN,
250 buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
251 Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
252 dlg.set_default_response(Gtk.ResponseType.OK)
253 if dlg.run() == Gtk.ResponseType.OK:
254 filename = dlg.get_filename()
256 f = file(filename, 'r')
257 self.load_from_xml(f)
259 err = Gtk.MessageDialog(self.window,
260 Gtk.DialogFlags.MODAL,
261 Gtk.MessageType.ERROR,
263 "Failed loading settings.")
267 self.current_filename = filename
272 def on_save_cb(self, *args):
273 if not self.current_filename:
274 return self.on_save_as_cb()
275 f = file(self.current_filename, 'w')
279 def on_save_as_cb(self, *args):
280 dlg = Gtk.FileChooserDialog(title='Save', parent=self.window,
281 action=Gtk.FileChooserAction.SAVE,
282 buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
283 Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
284 dlg.set_default_response(Gtk.ResponseType.OK)
285 if dlg.run() == Gtk.ResponseType.OK:
286 self.current_filename = dlg.get_filename()
290 def on_quit_cb(self, *args):
293 preferences_dialog = None
294 def on_preferences_cb(self, widget):
295 if not self.preferences_dialog:
296 self.preferences_dialog = PreferencesDialog(self)
297 self.preferences_dialog.show()
298 self.preferences_dialog.present()
300 def on_add_input_channel(self, widget):
301 dialog = NewChannelDialog(app=self)
302 dialog.set_transient_for(self.window)
307 if ret == Gtk.ResponseType.OK:
308 result = dialog.get_result()
309 channel = self.add_channel(**result)
310 self.window.show_all()
312 def on_add_output_channel(self, widget):
313 dialog = NewOutputChannelDialog(app=self)
314 dialog.set_transient_for(self.window)
319 if ret == Gtk.ResponseType.OK:
320 result = dialog.get_result()
321 channel = self.add_output_channel(**result)
322 self.window.show_all()
324 def on_edit_input_channel(self, widget, channel):
325 print('Editing channel "%s"' % channel.channel_name)
326 channel.on_channel_properties()
328 def remove_channel_edit_input_menuitem_by_label(self, widget, label):
329 if (widget.get_label() == label):
330 self.channel_edit_input_menu.remove(widget)
332 def on_remove_input_channel(self, widget, channel):
333 print('Removing channel "%s"' % channel.channel_name)
334 self.channel_remove_input_menu.remove(widget)
335 self.channel_edit_input_menu.foreach(
336 self.remove_channel_edit_input_menuitem_by_label,
337 channel.channel_name);
338 if self.monitored_channel is channel:
339 channel.monitor_button.set_active(False)
340 for i in range(len(self.channels)):
341 if self.channels[i] is channel:
344 self.hbox_inputs.remove(channel.get_parent())
346 if len(self.channels) == 0:
347 self.channel_remove_input_menu_item.set_sensitive(False)
349 def on_edit_output_channel(self, widget, channel):
350 print('Editing channel "%s"' % channel.channel_name)
351 channel.on_channel_properties()
353 def remove_channel_edit_output_menuitem_by_label(self, widget, label):
354 if (widget.get_label() == label):
355 self.channel_edit_output_menu.remove(widget)
357 def on_remove_output_channel(self, widget, channel):
358 print('Removing channel "%s"' % channel.channel_name)
359 self.channel_remove_output_menu.remove(widget)
360 self.channel_edit_output_menu.foreach(
361 self.remove_channel_edit_output_menuitem_by_label,
362 channel.channel_name);
363 if self.monitored_channel is channel:
364 channel.monitor_button.set_active(False)
365 for i in range(len(self.channels)):
366 if self.output_channels[i] is channel:
368 del self.output_channels[i]
369 self.hbox_outputs.remove(channel.get_parent())
371 if len(self.output_channels) == 0:
372 self.channel_remove_output_menu_item.set_sensitive(False)
374 def rename_channels(self, container, parameters):
375 if (container.get_label() == parameters['oldname']):
376 container.set_label(parameters['newname'])
378 def on_channel_rename(self, oldname, newname):
379 rename_parameters = { 'oldname' : oldname, 'newname' : newname }
380 self.channel_edit_input_menu.foreach(self.rename_channels,
382 self.channel_edit_output_menu.foreach(self.rename_channels,
384 self.channel_remove_input_menu.foreach(self.rename_channels,
386 self.channel_remove_output_menu.foreach(self.rename_channels,
388 print("Renaming channel from %s to %s\n" % (oldname, newname))
391 def on_channels_clear(self, widget):
392 for channel in self.output_channels:
394 self.hbox_outputs.remove(channel.get_parent())
395 for channel in self.channels:
397 self.hbox_inputs.remove(channel.get_parent())
399 self.output_channels = []
400 self.channel_edit_input_menu = Gtk.Menu()
401 self.channel_edit_input_menu_item.set_submenu(self.channel_edit_input_menu)
402 self.channel_edit_input_menu_item.set_sensitive(False)
403 self.channel_remove_input_menu = Gtk.Menu()
404 self.channel_remove_input_menu_item.set_submenu(self.channel_remove_input_menu)
405 self.channel_remove_input_menu_item.set_sensitive(False)
406 self.channel_edit_output_menu = Gtk.Menu()
407 self.channel_edit_output_menu_item.set_submenu(self.channel_edit_output_menu)
408 self.channel_edit_output_menu_item.set_sensitive(False)
409 self.channel_remove_output_menu = Gtk.Menu()
410 self.channel_remove_output_menu_item.set_submenu(self.channel_remove_output_menu)
411 self.channel_remove_output_menu_item.set_sensitive(False)
413 def add_channel(self, name, stereo, volume_cc, balance_cc, mute_cc, solo_cc):
415 channel = InputChannel(self, name, stereo)
416 self.add_channel_precreated(channel)
418 e = sys.exc_info()[0]
419 err = Gtk.MessageDialog(self.window,
420 Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
421 Gtk.MessageType.ERROR,
423 "Channel creation failed")
427 if volume_cc != '-1':
428 channel.channel.volume_midi_cc = int(volume_cc)
429 if balance_cc != '-1':
430 channel.channel.balance_midi_cc = int(balance_cc)
432 channel.channel.mute_midi_cc = int(mute_cc)
434 channel.channel.solo_midi_cc = int(solo_cc)
435 if (volume_cc == '-1' and balance_cc == '-1' and mute_cc == '-1' and solo_cc == '-1'):
436 channel.channel.autoset_midi_cc()
440 def add_channel_precreated(self, channel):
443 self.hbox_inputs.pack_start(frame, False, True, 0)
446 channel_edit_menu_item = Gtk.MenuItem(label=channel.channel_name)
447 self.channel_edit_input_menu.append(channel_edit_menu_item)
448 channel_edit_menu_item.connect("activate", self.on_edit_input_channel, channel)
449 self.channel_edit_input_menu_item.set_sensitive(True)
451 channel_remove_menu_item = Gtk.MenuItem(label=channel.channel_name)
452 self.channel_remove_input_menu.append(channel_remove_menu_item)
453 channel_remove_menu_item.connect("activate", self.on_remove_input_channel, channel)
454 self.channel_remove_input_menu_item.set_sensitive(True)
456 self.channels.append(channel)
458 for outputchannel in self.output_channels:
459 channel.add_control_group(outputchannel)
461 # create post fader output channel matching the input channel
462 channel.post_fader_output_channel = self.mixer.add_output_channel(
463 channel.channel.name + ' Out', channel.channel.is_stereo, True)
464 channel.post_fader_output_channel.volume = 0
465 channel.post_fader_output_channel.set_solo(channel.channel, True)
467 def read_meters(self):
468 for channel in self.channels:
470 for channel in self.output_channels:
474 def midi_events_check(self):
475 for channel in self.channels + self.output_channels:
476 channel.midi_events_check()
479 def add_output_channel(self, name, stereo, volume_cc, balance_cc, mute_cc, display_solo_buttons):
481 channel = OutputChannel(self, name, stereo)
482 channel.display_solo_buttons = display_solo_buttons
483 self.add_output_channel_precreated(channel)
485 err = Gtk.MessageDialog(self.window,
486 Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
487 Gtk.MessageType.ERROR,
489 "Channel creation failed")
493 if volume_cc != '-1':
494 channel.channel.volume_midi_cc = int(volume_cc)
495 if balance_cc != '-1':
496 channel.channel.balance_midi_cc = int(balance_cc)
498 channel.channel.mute_midi_cc = int(mute_cc)
501 def add_output_channel_precreated(self, channel):
504 self.hbox_outputs.pack_start(frame, False, True, 0)
507 channel_edit_menu_item = Gtk.MenuItem(label=channel.channel_name)
508 self.channel_edit_output_menu.append(channel_edit_menu_item)
509 channel_edit_menu_item.connect("activate", self.on_edit_output_channel, channel)
510 self.channel_edit_output_menu_item.set_sensitive(True)
512 channel_remove_menu_item = Gtk.MenuItem(label=channel.channel_name)
513 self.channel_remove_output_menu.append(channel_remove_menu_item)
514 channel_remove_menu_item.connect("activate", self.on_remove_output_channel, channel)
515 self.channel_remove_output_menu_item.set_sensitive(True)
517 self.output_channels.append(channel)
519 _monitored_channel = None
520 def get_monitored_channel(self):
521 return self._monitored_channel
523 def set_monitored_channel(self, channel):
524 if self._monitored_channel:
525 if channel.channel.name == self._monitored_channel.channel.name:
527 self._monitored_channel = channel
528 if type(channel) is InputChannel:
529 # reset all solo/mute settings
530 for in_channel in self.channels:
531 self.monitor_channel.set_solo(in_channel.channel, False)
532 self.monitor_channel.set_muted(in_channel.channel, False)
533 self.monitor_channel.set_solo(channel.channel, True)
534 self.monitor_channel.prefader = True
536 self.monitor_channel.prefader = False
537 self.update_monitor(channel)
538 monitored_channel = property(get_monitored_channel, set_monitored_channel)
540 def update_monitor(self, channel):
541 if self.monitored_channel is not channel:
543 self.monitor_channel.volume = channel.channel.volume
544 self.monitor_channel.balance = channel.channel.balance
545 self.monitor_channel.out_mute = channel.channel.out_mute
546 if type(self.monitored_channel) is OutputChannel:
547 # sync solo/muted channels
548 for input_channel in self.channels:
549 self.monitor_channel.set_solo(input_channel.channel,
550 channel.channel.is_solo(input_channel.channel))
551 self.monitor_channel.set_muted(input_channel.channel,
552 channel.channel.is_muted(input_channel.channel))
554 def get_input_channel_by_name(self, name):
555 for input_channel in self.channels:
556 if input_channel.channel.name == name:
560 def on_about(self, *args):
561 about = Gtk.AboutDialog()
562 about.set_name('jack_mixer')
563 about.set_copyright('Copyright © 2006-2020\nNedko Arnaudov, Frederic Peters, Arnout Engelen, Daniel Sheeler')
564 about.set_license('''\
565 jack_mixer is free software; you can redistribute it and/or modify it
566 under the terms of the GNU General Public License as published by the
567 Free Software Foundation; either version 2 of the License, or (at your
568 option) any later version.
570 jack_mixer is distributed in the hope that it will be useful, but
571 WITHOUT ANY WARRANTY; without even the implied warranty of
572 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
573 General Public License for more details.
575 You should have received a copy of the GNU General Public License along
576 with jack_mixer; if not, write to the Free Software Foundation, Inc., 51
577 Franklin Street, Fifth Floor, Boston, MA 02110-130159 USA''')
578 about.set_authors(['Nedko Arnaudov <nedko@arnaudov.name>',
579 'Frederic Peters <fpeters@0d.be>'])
580 about.set_logo_icon_name('jack_mixer')
581 about.set_website('http://home.gna.org/jackmixer/')
586 def lash_check_events(self):
589 if self.current_filename:
590 print("saving on SIGUSR1 request")
594 print("not saving because filename is not known")
597 if not self.lash_client:
600 while lash.lash_get_pending_event_count(self.lash_client):
601 event = lash.lash_get_event(self.lash_client)
605 event_type = lash.lash_event_get_type(event)
606 if event_type == lash.LASH_Quit:
607 print("jack_mixer: LASH ordered quit.")
610 elif event_type == lash.LASH_Save_File:
611 directory = lash.lash_event_get_string(event)
612 print("jack_mixer: LASH ordered to save data in directory %s" % directory)
613 filename = directory + os.sep + "jack_mixer.xml"
614 f = file(filename, "w")
617 lash.lash_send_event(self.lash_client, event) # we crash with double free
618 elif event_type == lash.LASH_Restore_File:
619 directory = lash.lash_event_get_string(event)
620 print("jack_mixer: LASH ordered to restore data from directory %s" % directory)
621 filename = directory + os.sep + "jack_mixer.xml"
622 f = file(filename, "r")
623 self.load_from_xml(f, silence_errors=True)
625 lash.lash_send_event(self.lash_client, event)
627 print("jack_mixer: Got unhandled LASH event, type " + str(event_type))
630 #lash.lash_event_destroy(event)
634 def save_to_xml(self, file):
635 #print "Saving to XML..."
636 b = XmlSerialization()
641 def load_from_xml(self, file, silence_errors=False):
642 #print "Loading from XML..."
643 self.on_channels_clear(None)
644 self.unserialized_channels = []
645 b = XmlSerialization()
653 s.unserialize(self, b)
654 for channel in self.unserialized_channels:
655 if isinstance(channel, InputChannel):
656 if self._init_solo_channels and channel.channel_name in self._init_solo_channels:
658 self.add_channel_precreated(channel)
659 self._init_solo_channels = None
660 for channel in self.unserialized_channels:
661 if isinstance(channel, OutputChannel):
662 self.add_output_channel_precreated(channel)
663 del self.unserialized_channels
664 self.window.show_all()
666 def serialize(self, object_backend):
667 width, height = self.window.get_size()
668 object_backend.add_property('geometry',
669 '%sx%s' % (width, height))
671 for input_channel in self.channels:
672 if input_channel.channel.solo:
673 solo_channels.append(input_channel)
675 object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
677 def unserialize_property(self, name, value):
678 if name == 'geometry':
679 width, height = value.split('x')
680 self.window.resize(int(width), int(height))
682 if name == 'solo_channels':
683 self._init_solo_channels = value.split('|')
686 def unserialize_child(self, name):
687 if name == InputChannel.serialization_name():
688 channel = InputChannel(self, "", True)
689 self.unserialized_channels.append(channel)
692 if name == OutputChannel.serialization_name():
693 channel = OutputChannel(self, "", True)
694 self.unserialized_channels.append(channel)
697 def serialization_get_childs(self):
698 '''Get child objects tha required and support serialization'''
699 childs = self.channels[:] + self.output_channels[:]
702 def serialization_name(self):
709 self.window.show_all()
711 signal.signal(signal.SIGUSR1, self.sighandler)
712 signal.signal(signal.SIGTERM, self.sighandler)
713 signal.signal(signal.SIGINT, self.sighandler)
714 signal.signal(signal.SIGHUP, signal.SIG_IGN)
718 #f = file("/dev/stdout", "w")
723 print("Usage: %s [mixer_name]" % sys.argv[0])
726 # Connect to LASH if Python bindings are available, and the user did not
728 if lash and not '--no-lash' in sys.argv:
729 # sys.argv is modified by this call
730 lash_client = lash.init(sys.argv, "jack_mixer", lash.LASH_Config_File)
734 parser = OptionParser(usage='usage: %prog [options] [jack_client_name]')
735 parser.add_option('-c', '--config', dest='config',
736 help='use a non default configuration file')
737 # --no-lash here is not acted upon, it is specified for completeness when
739 parser.add_option('--no-lash', dest='nolash', action='store_true',
740 help='do not connect to LASH')
741 options, args = parser.parse_args()
743 # Yeah , this sounds stupid, we connected earlier, but we dont want to show this if we got --help option
744 # This issue should be fixed in pylash, there is a reason for having two functions for initialization after all
746 server_name = lash.lash_get_server_name(lash_client)
748 print("Successfully connected to LASH server at " + server_name)
750 # getting the server name failed, probably not worth trying to do
751 # further things with as a lash client.
763 mixer = JackMixer(name, lash_client)
764 except Exception as e:
765 err = Gtk.MessageDialog(None,
766 Gtk.DialogFlags.MODAL,
767 Gtk.MessageType.ERROR,
769 "Mixer creation failed (%s)" % str(e))
775 f = open(options.config)
776 mixer.current_filename = options.config
778 mixer.load_from_xml(f)
780 err = Gtk.MessageDialog(mixer.window,
781 Gtk.DialogFlags.MODAL,
782 Gtk.MessageType.ERROR,
784 "Failed loading settings.")
787 mixer.window.set_default_size(60*(1+len(mixer.channels)+len(mixer.output_channels)), 300)
794 if __name__ == "__main__":