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)
216 def on_delete_event(self, widget, event):
219 def sighandler(self, signum, frame):
220 #print "Signal %d received" % signum
221 if signum == signal.SIGUSR1:
223 elif signum == signal.SIGTERM:
225 elif signum == signal.SIGINT:
228 print("Unknown signal %d received" % signum)
231 print("Cleaning jack_mixer")
235 for channel in self.channels:
240 def on_open_cb(self, *args):
241 dlg = Gtk.FileChooserDialog(title='Open', parent=self.window,
242 action=Gtk.FileChooserAction.OPEN,
243 buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
244 Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
245 dlg.set_default_response(Gtk.ResponseType.OK)
246 if dlg.run() == Gtk.ResponseType.OK:
247 filename = dlg.get_filename()
249 f = file(filename, 'r')
250 self.load_from_xml(f)
252 err = Gtk.MessageDialog(self.window,
253 Gtk.DialogFlags.MODAL,
254 Gtk.MessageType.ERROR,
256 "Failed loading settings.")
260 self.current_filename = filename
265 def on_save_cb(self, *args):
266 if not self.current_filename:
267 return self.on_save_as_cb()
268 f = file(self.current_filename, 'w')
272 def on_save_as_cb(self, *args):
273 dlg = Gtk.FileChooserDialog(title='Save', parent=self.window,
274 action=Gtk.FileChooserAction.SAVE,
275 buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
276 Gtk.STOCK_SAVE, Gtk.ResponseType.OK))
277 dlg.set_default_response(Gtk.ResponseType.OK)
278 if dlg.run() == Gtk.ResponseType.OK:
279 self.current_filename = dlg.get_filename()
283 def on_quit_cb(self, *args):
286 preferences_dialog = None
287 def on_preferences_cb(self, widget):
288 if not self.preferences_dialog:
289 self.preferences_dialog = PreferencesDialog(self)
290 self.preferences_dialog.show()
291 self.preferences_dialog.present()
293 def on_add_input_channel(self, widget):
294 dialog = NewChannelDialog(app=self)
295 dialog.set_transient_for(self.window)
300 if ret == Gtk.ResponseType.OK:
301 result = dialog.get_result()
302 channel = self.add_channel(**result)
303 self.window.show_all()
305 def on_add_output_channel(self, widget):
306 dialog = NewOutputChannelDialog(app=self)
307 dialog.set_transient_for(self.window)
312 if ret == Gtk.ResponseType.OK:
313 result = dialog.get_result()
314 channel = self.add_output_channel(**result)
315 self.window.show_all()
317 def on_edit_input_channel(self, widget, channel):
318 print('Editing channel "%s"' % channel.channel_name)
319 channel.on_channel_properties()
321 def remove_channel_edit_input_menuitem_by_label(self, widget, label):
322 if (widget.get_label() == label):
323 self.channel_edit_input_menu.remove(widget)
325 def on_remove_input_channel(self, widget, channel):
326 print('Removing channel "%s"' % channel.channel_name)
327 self.channel_remove_input_menu.remove(widget)
328 self.channel_edit_input_menu.foreach(
329 self.remove_channel_edit_input_menuitem_by_label,
330 channel.channel_name);
331 if self.monitored_channel is channel:
332 channel.monitor_button.set_active(False)
333 for i in range(len(self.channels)):
334 if self.channels[i] is channel:
337 self.hbox_inputs.remove(channel.get_parent())
339 if len(self.channels) == 0:
340 self.channel_remove_input_menu_item.set_sensitive(False)
342 def on_edit_output_channel(self, widget, channel):
343 print('Editing channel "%s"' % channel.channel_name)
344 channel.on_channel_properties()
346 def remove_channel_edit_output_menuitem_by_label(self, widget, label):
347 if (widget.get_label() == label):
348 self.channel_edit_output_menu.remove(widget)
350 def on_remove_output_channel(self, widget, channel):
351 print('Removing channel "%s"' % channel.channel_name)
352 self.channel_remove_output_menu.remove(widget)
353 self.channel_edit_output_menu.foreach(
354 self.remove_channel_edit_output_menuitem_by_label,
355 channel.channel_name);
356 if self.monitored_channel is channel:
357 channel.monitor_button.set_active(False)
358 for i in range(len(self.channels)):
359 if self.output_channels[i] is channel:
361 del self.output_channels[i]
362 self.hbox_outputs.remove(channel.get_parent())
364 if len(self.output_channels) == 0:
365 self.channel_remove_output_menu_item.set_sensitive(False)
367 def rename_channels(self, container, parameters):
368 if (container.get_label() == parameters['oldname']):
369 container.set_label(parameters['newname'])
371 def on_channel_rename(self, oldname, newname):
372 rename_parameters = { 'oldname' : oldname, 'newname' : newname }
373 self.channel_edit_input_menu.foreach(self.rename_channels,
375 self.channel_edit_output_menu.foreach(self.rename_channels,
377 self.channel_remove_input_menu.foreach(self.rename_channels,
379 self.channel_remove_output_menu.foreach(self.rename_channels,
381 print("Renaming channel from %s to %s\n" % (oldname, newname))
384 def on_channels_clear(self, widget):
385 for channel in self.output_channels:
387 self.hbox_outputs.remove(channel.get_parent())
388 for channel in self.channels:
390 self.hbox_inputs.remove(channel.get_parent())
392 self.output_channels = []
393 self.channel_edit_input_menu = Gtk.Menu()
394 self.channel_edit_input_menu_item.set_submenu(self.channel_edit_input_menu)
395 self.channel_edit_input_menu_item.set_sensitive(False)
396 self.channel_remove_input_menu = Gtk.Menu()
397 self.channel_remove_input_menu_item.set_submenu(self.channel_remove_input_menu)
398 self.channel_remove_input_menu_item.set_sensitive(False)
399 self.channel_edit_output_menu = Gtk.Menu()
400 self.channel_edit_output_menu_item.set_submenu(self.channel_edit_output_menu)
401 self.channel_edit_output_menu_item.set_sensitive(False)
402 self.channel_remove_output_menu = Gtk.Menu()
403 self.channel_remove_output_menu_item.set_submenu(self.channel_remove_output_menu)
404 self.channel_remove_output_menu_item.set_sensitive(False)
406 def add_channel(self, name, stereo, volume_cc, balance_cc, mute_cc, solo_cc):
408 channel = InputChannel(self, name, stereo)
409 self.add_channel_precreated(channel)
411 e = sys.exc_info()[0]
412 err = Gtk.MessageDialog(self.window,
413 Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
414 Gtk.MessageType.ERROR,
416 "Channel creation failed")
420 if volume_cc != '-1':
421 channel.channel.volume_midi_cc = int(volume_cc)
422 if balance_cc != '-1':
423 channel.channel.balance_midi_cc = int(balance_cc)
425 channel.channel.mute_midi_cc = int(mute_cc)
427 channel.channel.solo_midi_cc = int(solo_cc)
428 if (volume_cc == '-1' and balance_cc == '-1' and mute_cc == '-1' and solo_cc == '-1'):
429 channel.channel.autoset_midi_cc()
433 def add_channel_precreated(self, channel):
436 self.hbox_inputs.pack_start(frame, False, True, 0)
439 channel_edit_menu_item = Gtk.MenuItem(label=channel.channel_name)
440 self.channel_edit_input_menu.append(channel_edit_menu_item)
441 channel_edit_menu_item.connect("activate", self.on_edit_input_channel, channel)
442 self.channel_edit_input_menu_item.set_sensitive(True)
444 channel_remove_menu_item = Gtk.MenuItem(label=channel.channel_name)
445 self.channel_remove_input_menu.append(channel_remove_menu_item)
446 channel_remove_menu_item.connect("activate", self.on_remove_input_channel, channel)
447 self.channel_remove_input_menu_item.set_sensitive(True)
449 self.channels.append(channel)
451 for outputchannel in self.output_channels:
452 channel.add_control_group(outputchannel)
454 # create post fader output channel matching the input channel
455 channel.post_fader_output_channel = self.mixer.add_output_channel(
456 channel.channel.name + ' Out', channel.channel.is_stereo, True)
457 channel.post_fader_output_channel.volume = 0
458 channel.post_fader_output_channel.set_solo(channel.channel, True)
460 def read_meters(self):
461 for channel in self.channels:
463 for channel in self.output_channels:
467 def midi_events_check(self):
468 for channel in self.channels + self.output_channels:
469 channel.midi_events_check()
472 def add_output_channel(self, name, stereo, volume_cc, balance_cc, mute_cc, display_solo_buttons):
474 channel = OutputChannel(self, name, stereo)
475 channel.display_solo_buttons = display_solo_buttons
476 self.add_output_channel_precreated(channel)
478 err = Gtk.MessageDialog(self.window,
479 Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
480 Gtk.MessageType.ERROR,
482 "Channel creation failed")
486 if volume_cc != '-1':
487 channel.channel.volume_midi_cc = int(volume_cc)
488 if balance_cc != '-1':
489 channel.channel.balance_midi_cc = int(balance_cc)
491 channel.channel.mute_midi_cc = int(mute_cc)
494 def add_output_channel_precreated(self, channel):
497 self.hbox_outputs.pack_start(frame, False, True, 0)
500 channel_edit_menu_item = Gtk.MenuItem(label=channel.channel_name)
501 self.channel_edit_output_menu.append(channel_edit_menu_item)
502 channel_edit_menu_item.connect("activate", self.on_edit_output_channel, channel)
503 self.channel_edit_output_menu_item.set_sensitive(True)
505 channel_remove_menu_item = Gtk.MenuItem(label=channel.channel_name)
506 self.channel_remove_output_menu.append(channel_remove_menu_item)
507 channel_remove_menu_item.connect("activate", self.on_remove_output_channel, channel)
508 self.channel_remove_output_menu_item.set_sensitive(True)
510 self.output_channels.append(channel)
512 _monitored_channel = None
513 def get_monitored_channel(self):
514 return self._monitored_channel
516 def set_monitored_channel(self, channel):
517 if self._monitored_channel:
518 if channel.channel.name == self._monitored_channel.channel.name:
520 self._monitored_channel = channel
521 if type(channel) is InputChannel:
522 # reset all solo/mute settings
523 for in_channel in self.channels:
524 self.monitor_channel.set_solo(in_channel.channel, False)
525 self.monitor_channel.set_muted(in_channel.channel, False)
526 self.monitor_channel.set_solo(channel.channel, True)
527 self.monitor_channel.prefader = True
529 self.monitor_channel.prefader = False
530 self.update_monitor(channel)
531 monitored_channel = property(get_monitored_channel, set_monitored_channel)
533 def update_monitor(self, channel):
534 if self.monitored_channel is not channel:
536 self.monitor_channel.volume = channel.channel.volume
537 self.monitor_channel.balance = channel.channel.balance
538 self.monitor_channel.out_mute = channel.channel.out_mute
539 if type(self.monitored_channel) is OutputChannel:
540 # sync solo/muted channels
541 for input_channel in self.channels:
542 self.monitor_channel.set_solo(input_channel.channel,
543 channel.channel.is_solo(input_channel.channel))
544 self.monitor_channel.set_muted(input_channel.channel,
545 channel.channel.is_muted(input_channel.channel))
547 def get_input_channel_by_name(self, name):
548 for input_channel in self.channels:
549 if input_channel.channel.name == name:
553 def on_about(self, *args):
554 about = Gtk.AboutDialog()
555 about.set_name('jack_mixer')
556 about.set_copyright('Copyright © 2006-2020\nNedko Arnaudov, Frederic Peters, Arnout Engelen, Daniel Sheeler')
557 about.set_license('''\
558 jack_mixer is free software; you can redistribute it and/or modify it
559 under the terms of the GNU General Public License as published by the
560 Free Software Foundation; either version 2 of the License, or (at your
561 option) any later version.
563 jack_mixer is distributed in the hope that it will be useful, but
564 WITHOUT ANY WARRANTY; without even the implied warranty of
565 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
566 General Public License for more details.
568 You should have received a copy of the GNU General Public License along
569 with jack_mixer; if not, write to the Free Software Foundation, Inc., 51
570 Franklin Street, Fifth Floor, Boston, MA 02110-130159 USA''')
571 about.set_authors(['Nedko Arnaudov <nedko@arnaudov.name>',
572 'Frederic Peters <fpeters@0d.be>'])
573 about.set_logo_icon_name('jack_mixer')
574 about.set_website('http://home.gna.org/jackmixer/')
579 def lash_check_events(self):
582 if self.current_filename:
583 print("saving on SIGUSR1 request")
587 print("not saving because filename is not known")
590 if not self.lash_client:
593 while lash.lash_get_pending_event_count(self.lash_client):
594 event = lash.lash_get_event(self.lash_client)
598 event_type = lash.lash_event_get_type(event)
599 if event_type == lash.LASH_Quit:
600 print("jack_mixer: LASH ordered quit.")
603 elif event_type == lash.LASH_Save_File:
604 directory = lash.lash_event_get_string(event)
605 print("jack_mixer: LASH ordered to save data in directory %s" % directory)
606 filename = directory + os.sep + "jack_mixer.xml"
607 f = file(filename, "w")
610 lash.lash_send_event(self.lash_client, event) # we crash with double free
611 elif event_type == lash.LASH_Restore_File:
612 directory = lash.lash_event_get_string(event)
613 print("jack_mixer: LASH ordered to restore data from directory %s" % directory)
614 filename = directory + os.sep + "jack_mixer.xml"
615 f = file(filename, "r")
616 self.load_from_xml(f, silence_errors=True)
618 lash.lash_send_event(self.lash_client, event)
620 print("jack_mixer: Got unhandled LASH event, type " + str(event_type))
623 #lash.lash_event_destroy(event)
627 def save_to_xml(self, file):
628 #print "Saving to XML..."
629 b = XmlSerialization()
634 def load_from_xml(self, file, silence_errors=False):
635 #print "Loading from XML..."
636 self.on_channels_clear(None)
637 self.unserialized_channels = []
638 b = XmlSerialization()
646 s.unserialize(self, b)
647 for channel in self.unserialized_channels:
648 if isinstance(channel, InputChannel):
649 if self._init_solo_channels and channel.channel_name in self._init_solo_channels:
651 self.add_channel_precreated(channel)
652 self._init_solo_channels = None
653 for channel in self.unserialized_channels:
654 if isinstance(channel, OutputChannel):
655 self.add_output_channel_precreated(channel)
656 del self.unserialized_channels
657 self.window.show_all()
659 def serialize(self, object_backend):
660 width, height = self.window.get_size()
661 object_backend.add_property('geometry',
662 '%sx%s' % (width, height))
664 for input_channel in self.channels:
665 if input_channel.channel.solo:
666 solo_channels.append(input_channel)
668 object_backend.add_property('solo_channels', '|'.join([x.channel.name for x in solo_channels]))
670 def unserialize_property(self, name, value):
671 if name == 'geometry':
672 width, height = value.split('x')
673 self.window.resize(int(width), int(height))
675 if name == 'solo_channels':
676 self._init_solo_channels = value.split('|')
679 def unserialize_child(self, name):
680 if name == InputChannel.serialization_name():
681 channel = InputChannel(self, "", True)
682 self.unserialized_channels.append(channel)
685 if name == OutputChannel.serialization_name():
686 channel = OutputChannel(self, "", True)
687 self.unserialized_channels.append(channel)
690 def serialization_get_childs(self):
691 '''Get child objects tha required and support serialization'''
692 childs = self.channels[:] + self.output_channels[:]
695 def serialization_name(self):
702 self.window.show_all()
704 signal.signal(signal.SIGUSR1, self.sighandler)
705 signal.signal(signal.SIGTERM, self.sighandler)
706 signal.signal(signal.SIGINT, self.sighandler)
707 signal.signal(signal.SIGHUP, signal.SIG_IGN)
711 #f = file("/dev/stdout", "w")
716 print("Usage: %s [mixer_name]" % sys.argv[0])
719 # Connect to LASH if Python bindings are available, and the user did not
721 if lash and not '--no-lash' in sys.argv:
722 # sys.argv is modified by this call
723 lash_client = lash.init(sys.argv, "jack_mixer", lash.LASH_Config_File)
727 parser = OptionParser(usage='usage: %prog [options] [jack_client_name]')
728 parser.add_option('-c', '--config', dest='config',
729 help='use a non default configuration file')
730 # --no-lash here is not acted upon, it is specified for completeness when
732 parser.add_option('--no-lash', dest='nolash', action='store_true',
733 help='do not connect to LASH')
734 options, args = parser.parse_args()
736 # Yeah , this sounds stupid, we connected earlier, but we dont want to show this if we got --help option
737 # This issue should be fixed in pylash, there is a reason for having two functions for initialization after all
739 server_name = lash.lash_get_server_name(lash_client)
741 print("Successfully connected to LASH server at " + server_name)
743 # getting the server name failed, probably not worth trying to do
744 # further things with as a lash client.
756 mixer = JackMixer(name, lash_client)
757 except Exception as e:
758 err = Gtk.MessageDialog(None,
759 Gtk.DialogFlags.MODAL,
760 Gtk.MessageType.ERROR,
762 "Mixer creation failed (%s)" % str(e))
768 f = open(options.config)
769 mixer.current_filename = options.config
771 mixer.load_from_xml(f)
773 err = Gtk.MessageDialog(mixer.window,
774 Gtk.DialogFlags.MODAL,
775 Gtk.MessageType.ERROR,
777 "Failed loading settings.")
780 mixer.window.set_default_size(60*(1+len(mixer.channels)+len(mixer.output_channels)), 300)
787 if __name__ == "__main__":