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 'Daniel Sheeler <dsheeler@pobox.com>'])
574 about
.set_logo_icon_name('jack_mixer')
575 about
.set_website('http://home.gna.org/jackmixer/')
580 def lash_check_events(self
):
583 if self
.current_filename
:
584 print("saving on SIGUSR1 request")
588 print("not saving because filename is not known")
591 if not self
.lash_client
:
594 while lash
.lash_get_pending_event_count(self
.lash_client
):
595 event
= lash
.lash_get_event(self
.lash_client
)
599 event_type
= lash
.lash_event_get_type(event
)
600 if event_type
== lash
.LASH_Quit
:
601 print("jack_mixer: LASH ordered quit.")
604 elif event_type
== lash
.LASH_Save_File
:
605 directory
= lash
.lash_event_get_string(event
)
606 print("jack_mixer: LASH ordered to save data in directory %s" % directory
)
607 filename
= directory
+ os
.sep
+ "jack_mixer.xml"
608 f
= file(filename
, "w")
611 lash
.lash_send_event(self
.lash_client
, event
) # we crash with double free
612 elif event_type
== lash
.LASH_Restore_File
:
613 directory
= lash
.lash_event_get_string(event
)
614 print("jack_mixer: LASH ordered to restore data from directory %s" % directory
)
615 filename
= directory
+ os
.sep
+ "jack_mixer.xml"
616 f
= file(filename
, "r")
617 self
.load_from_xml(f
, silence_errors
=True)
619 lash
.lash_send_event(self
.lash_client
, event
)
621 print("jack_mixer: Got unhandled LASH event, type " + str(event_type
))
624 #lash.lash_event_destroy(event)
628 def save_to_xml(self
, file):
629 #print "Saving to XML..."
630 b
= XmlSerialization()
635 def load_from_xml(self
, file, silence_errors
=False):
636 #print "Loading from XML..."
637 self
.on_channels_clear(None)
638 self
.unserialized_channels
= []
639 b
= XmlSerialization()
647 s
.unserialize(self
, b
)
648 for channel
in self
.unserialized_channels
:
649 if isinstance(channel
, InputChannel
):
650 if self
._init
_solo
_channels
and channel
.channel_name
in self
._init
_solo
_channels
:
652 self
.add_channel_precreated(channel
)
653 self
._init
_solo
_channels
= None
654 for channel
in self
.unserialized_channels
:
655 if isinstance(channel
, OutputChannel
):
656 self
.add_output_channel_precreated(channel
)
657 del self
.unserialized_channels
658 self
.window
.show_all()
660 def serialize(self
, object_backend
):
661 width
, height
= self
.window
.get_size()
662 object_backend
.add_property('geometry',
663 '%sx%s' % (width
, height
))
665 for input_channel
in self
.channels
:
666 if input_channel
.channel
.solo
:
667 solo_channels
.append(input_channel
)
669 object_backend
.add_property('solo_channels', '|'.join([x
.channel
.name
for x
in solo_channels
]))
671 def unserialize_property(self
, name
, value
):
672 if name
== 'geometry':
673 width
, height
= value
.split('x')
674 self
.window
.resize(int(width
), int(height
))
676 if name
== 'solo_channels':
677 self
._init
_solo
_channels
= value
.split('|')
680 def unserialize_child(self
, name
):
681 if name
== InputChannel
.serialization_name():
682 channel
= InputChannel(self
, "", True)
683 self
.unserialized_channels
.append(channel
)
686 if name
== OutputChannel
.serialization_name():
687 channel
= OutputChannel(self
, "", True)
688 self
.unserialized_channels
.append(channel
)
691 def serialization_get_childs(self
):
692 '''Get child objects tha required and support serialization'''
693 childs
= self
.channels
[:] + self
.output_channels
[:]
696 def serialization_name(self
):
703 self
.window
.show_all()
705 signal
.signal(signal
.SIGUSR1
, self
.sighandler
)
706 signal
.signal(signal
.SIGTERM
, self
.sighandler
)
707 signal
.signal(signal
.SIGINT
, self
.sighandler
)
708 signal
.signal(signal
.SIGHUP
, signal
.SIG_IGN
)
712 #f = file("/dev/stdout", "w")
717 print("Usage: %s [mixer_name]" % sys
.argv
[0])
720 # Connect to LASH if Python bindings are available, and the user did not
722 if lash
and not '--no-lash' in sys
.argv
:
723 # sys.argv is modified by this call
724 lash_client
= lash
.init(sys
.argv
, "jack_mixer", lash
.LASH_Config_File
)
728 parser
= OptionParser(usage
='usage: %prog [options] [jack_client_name]')
729 parser
.add_option('-c', '--config', dest
='config',
730 help='use a non default configuration file')
731 # --no-lash here is not acted upon, it is specified for completeness when
733 parser
.add_option('--no-lash', dest
='nolash', action
='store_true',
734 help='do not connect to LASH')
735 options
, args
= parser
.parse_args()
737 # Yeah , this sounds stupid, we connected earlier, but we dont want to show this if we got --help option
738 # This issue should be fixed in pylash, there is a reason for having two functions for initialization after all
740 server_name
= lash
.lash_get_server_name(lash_client
)
742 print("Successfully connected to LASH server at " + server_name
)
744 # getting the server name failed, probably not worth trying to do
745 # further things with as a lash client.
757 mixer
= JackMixer(name
, lash_client
)
758 except Exception as e
:
759 err
= Gtk
.MessageDialog(None,
760 Gtk
.DialogFlags
.MODAL
,
761 Gtk
.MessageType
.ERROR
,
763 "Mixer creation failed (%s)" % str(e
))
769 f
= open(options
.config
)
770 mixer
.current_filename
= options
.config
772 mixer
.load_from_xml(f
)
774 err
= Gtk
.MessageDialog(mixer
.window
,
775 Gtk
.DialogFlags
.MODAL
,
776 Gtk
.MessageType
.ERROR
,
778 "Failed loading settings.")
781 mixer
.window
.set_default_size(60*(1+len(mixer
.channels
)+len(mixer
.output_channels
)), 300)
788 if __name__
== "__main__":