3 # This file is part of jack_mixer
5 # Copyright (C) 2006 Nedko Arnaudov <nedko@arnaudov.name>
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; version 2 of the License
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
30 print "Cannot load LASH python bindings, you want LASH unless you enjoy manual jack plumbing each time you use this app"
34 sys
.path
.insert(0, os
.path
.dirname(sys
.argv
[0]) + os
.sep
+ ".." + os
.sep
+ "share"+ os
.sep
+ "jack_mixer")
40 # no need for serialization if there is no LASH available
42 from serialization_xml
import xml_serialization
43 from serialization
import serialized_object
, serializator
45 class jack_mixer(serialized_object
):
46 def __init__(self
, name
, lash_client
):
47 self
.mixer
= jack_mixer_c
.create(name
)
52 # Send our client name to server
53 lash_event
= lash
.lash_event_new_with_type(lash
.LASH_Client_Name
)
54 lash
.lash_event_set_string(lash_event
, name
)
55 lash
.lash_send_event(lash_client
, lash_event
)
57 lash
.lash_jack_client_name(lash_client
, name
)
59 self
.window
= gtk
.Window(gtk
.WINDOW_TOPLEVEL
)
60 self
.window
.set_title(name
)
62 self
.gui_factory
= gui
.factory(glade_xml
, self
.window
, meter_scales
, slider_scales
)
64 self
.vbox_top
= gtk
.VBox()
65 self
.window
.add(self
.vbox_top
)
67 self
.menubar
= gtk
.MenuBar()
68 self
.vbox_top
.pack_start(self
.menubar
, False)
70 self
.channels_menu_item
= gtk
.MenuItem("_Channel")
71 self
.menubar
.append(self
.channels_menu_item
)
73 self
.settings_menu_item
= gtk
.MenuItem("_Settings")
74 self
.menubar
.append(self
.settings_menu_item
)
76 self
.window
.set_size_request(120,300)
78 self
.channels_menu
= gtk
.Menu()
79 self
.channels_menu_item
.set_submenu(self
.channels_menu
)
81 self
.channel_add_menu_item
= gtk
.ImageMenuItem(gtk
.STOCK_ADD
)
82 self
.channels_menu
.append(self
.channel_add_menu_item
)
83 self
.channel_add_menu_item
.connect("activate", self
.on_add_channel
)
85 self
.channel_remove_menu_item
= gtk
.ImageMenuItem(gtk
.STOCK_REMOVE
)
86 self
.channels_menu
.append(self
.channel_remove_menu_item
)
88 self
.channel_remove_menu
= gtk
.Menu()
89 self
.channel_remove_menu_item
.set_submenu(self
.channel_remove_menu
)
91 self
.channel_remove_all_menu_item
= gtk
.ImageMenuItem(gtk
.STOCK_CLEAR
)
92 self
.channels_menu
.append(self
.channel_remove_all_menu_item
)
93 self
.channel_remove_all_menu_item
.connect("activate", self
.on_channels_clear
)
95 self
.settings_menu
= gtk
.Menu()
96 self
.settings_menu_item
.set_submenu(self
.settings_menu
)
98 self
.settings_choose_meter_scale_menu_item
= gtk
.MenuItem("_Meter scale")
99 self
.settings_choose_meter_scale_menu_item
.connect("activate", self
.on_choose_meter_scale
)
100 self
.settings_menu
.append(self
.settings_choose_meter_scale_menu_item
)
102 self
.settings_choose_slider_scale_menu_item
= gtk
.MenuItem("_Slider scale")
103 self
.settings_choose_slider_scale_menu_item
.connect("activate", self
.on_choose_slider_scale
)
104 self
.settings_menu
.append(self
.settings_choose_slider_scale_menu_item
)
106 self
.hbox_top
= gtk
.HBox()
107 self
.vbox_top
.pack_start(self
.hbox_top
, True)
109 self
.scrolled_window
= gtk
.ScrolledWindow()
110 self
.hbox_top
.pack_start(self
.scrolled_window
, True)
112 self
.hbox_inputs
= gtk
.HBox()
113 self
.hbox_inputs
.set_spacing(0)
114 self
.hbox_inputs
.set_border_width(0)
115 self
.hbox_top
.set_spacing(0)
116 self
.hbox_top
.set_border_width(0)
119 self
.scrolled_window
.set_policy(gtk
.POLICY_AUTOMATIC
, gtk
.POLICY_AUTOMATIC
)
120 self
.scrolled_window
.add_with_viewport(self
.hbox_inputs
)
122 self
.main_mix
= main_mix(self
.mixer
, self
.gui_factory
)
123 self
.main_mix
.realize()
125 frame
.add(self
.main_mix
)
126 self
.hbox_top
.pack_start(frame
, False)
128 self
.window
.connect("destroy", gtk
.main_quit
)
130 gobject
.timeout_add(80, self
.read_meters
)
131 self
.lash_client
= lash_client
134 gobject
.timeout_add(1000, self
.lash_check_events
)
136 gobject
.timeout_add(100, self
.midi_change_check
)
139 print "Cleaning jack_mixer"
143 for channel
in self
.channels
:
146 jack_mixer_c
.destroy(self
.mixer
)
148 def on_choose_meter_scale(self
, widget
):
149 self
.gui_factory
.run_dialog_choose_meter_scale()
151 def on_choose_slider_scale(self
, widget
):
152 self
.gui_factory
.run_dialog_choose_slider_scale()
154 def on_add_channel(self
, widget
):
155 result
= self
.gui_factory
.run_dialog_add_channel()
157 self
.add_channel(result
['name'], result
['stereo'])
158 self
.window
.show_all()
160 def on_remove_channel(self
, widget
, channel
, channel_remove_menu_item
):
161 print 'Removing channel "%s"' % channel
.channel_name
162 self
.channel_remove_menu
.remove(channel_remove_menu_item
)
163 for i
in range(len(self
.channels
)):
164 if self
.channels
[i
] is channel
:
167 self
.hbox_inputs
.remove(channel
.parent
)
169 if len(self
.channels
) == 0:
170 self
.channel_remove_menu_item
.set_sensitive(False)
172 def on_channels_clear(self
, widget
):
173 for channel
in self
.channels
:
175 self
.hbox_inputs
.remove(channel
.parent
)
177 self
.channel_remove_menu
= gtk
.Menu()
178 self
.channel_remove_menu_item
.set_submenu(self
.channel_remove_menu
)
179 self
.channel_remove_menu_item
.set_sensitive(False)
181 def add_channel(self
, name
, stereo
):
182 channel
= input_channel(self
.mixer
, self
.gui_factory
, name
, stereo
)
183 self
.add_channel_precreated(channel
)
185 def add_channel_precreated(self
, channel
):
186 self
.channels
.append(channel
)
189 self
.hbox_inputs
.pack_start(frame
, False)
191 channel_remove_menu_item
= gtk
.MenuItem(channel
.channel_name
)
192 self
.channel_remove_menu
.append(channel_remove_menu_item
)
193 channel_remove_menu_item
.connect("activate", self
.on_remove_channel
, channel
, channel_remove_menu_item
)
194 self
.channel_remove_menu_item
.set_sensitive(True)
196 def read_meters(self
):
197 for channel
in self
.channels
:
199 self
.main_mix
.read_meter()
202 def midi_change_check(self
):
203 for channel
in self
.channels
:
204 channel
.midi_change_check()
205 self
.main_mix
.midi_change_check()
208 def lash_check_events(self
):
209 while lash
.lash_get_pending_event_count(self
.lash_client
):
210 event
= lash
.lash_get_event(self
.lash_client
)
214 event_type
= lash
.lash_event_get_type(event
)
215 if event_type
== lash
.LASH_Quit
:
216 print "jack_mixer: LASH ordered quit."
219 elif event_type
== lash
.LASH_Save_File
:
220 directory
= lash
.lash_event_get_string(event
)
221 print "jack_mixer: LASH ordered to save data in directory %s" % directory
222 filename
= directory
+ os
.sep
+ "jack_mixer.xml"
223 f
= file(filename
, "w")
226 lash
.lash_send_event(self
.lash_client
, event
) # we crash with double free
227 elif event_type
== lash
.LASH_Restore_File
:
228 directory
= lash
.lash_event_get_string(event
)
229 print "jack_mixer: LASH ordered to restore data from directory %s" % directory
230 filename
= directory
+ os
.sep
+ "jack_mixer.xml"
231 f
= file(filename
, "r")
232 self
.load_from_xml(f
)
235 print "jack_mixer: Got unhandled LASH event, type " + str(event_type
)
238 #lash.lash_event_destroy(event)
242 def save_to_xml(self
, file):
243 #print "Saving to XML..."
244 b
= xml_serialization()
249 def load_from_xml(self
, file):
250 #print "Loading from XML..."
251 self
.on_channels_clear(None)
252 self
.unserialized_channels
= []
253 b
= xml_serialization()
256 s
.unserialize(self
, b
)
257 for channel
in self
.unserialized_channels
:
258 self
.add_channel_precreated(channel
)
259 del self
.unserialized_channels
260 self
.window
.show_all()
262 def serialize(self
, object_backend
):
265 def unserialize_property(self
, name
, value
):
268 def unserialize_child(self
, name
):
269 if name
== main_mix_serialization_name():
272 if name
== input_channel_serialization_name():
273 channel
= input_channel(self
.gui_factory
, "", True)
274 self
.unserialized_channels
.append(channel
)
277 def serialization_get_childs(self
):
278 '''Get child objects tha required and support serialization'''
279 childs
= self
.channels
[:]
280 childs
.append(self
.main_mix
)
283 def serialization_name(self
):
290 self
.window
.show_all()
294 #f = file("/dev/stdout", "w")
299 print "Usage: %s [mixer_name]" % sys
.argv
[0]
302 if lash
: # If LASH python bindings are available
303 # sys.argv is modified by this call
304 lash_client
= lash
.init(sys
.argv
, "jack_mixer", lash
.LASH_Config_File
)
311 for arg
in reversed(args
):
313 if len(arg
) != 0 and arg
[0] == '-':
318 print 'Unknown option "%s"' % args
[i
]
323 # print 'Non option argument "%s"' % args[i]
325 # Yeah , this sounds stupid, we connected earlier, but we dont want to show this if we got --help option
326 # This issue should be fixed in pylash, there is a reason for having two functions for initialization after all
328 print "Successfully connected to LASH server at " + lash
.lash_get_server_name(lash_client
)
336 name
= "jack_mixer-%u" % os
.getpid()
338 mixer
= jack_mixer(name
, lash_client
)
344 glade_dir
= os
.path
.dirname(sys
.argv
[0])
346 # since ppl tend to run "python jack_mixer.py", lets assume that it is in current directory
347 # "python ./jack_mixer.py" and "./jack_mixer.py" will work anyway.
351 glade_file
= glade_dir
+ os
.sep
+ "jack_mixer.glade"
353 if not os
.path
.isfile(glade_file
):
354 glade_file
= glade_dir
+ os
.sep
+ ".." + os
.sep
+ "share"+ os
.sep
+ "jack_mixer" + os
.sep
+ "jack_mixer.glade"
356 #print 'Loading glade from "%s"' % glade_file
358 glade_xml
= gtk
.glade
.XML(glade_file
)
360 # scales suitable as meter scales
361 meter_scales
= [scale
.iec_268(), scale
.linear_70dB(), scale
.iec_268_minimalistic()]
363 # scales suitable as volume slider scales
364 slider_scales
= [scale
.linear_30dB(), scale
.linear_70dB()]
366 if __name__
== "__main__":