Improved the look of the Options dialog
[rox-volume.git] / mixer.py
blob7aaee2fb8611ea9c327f2bd42a901ed0da3c15e8
1 """
2 mixer.py (an OSS sound mixer for the ROX Desktop)
4 Copyright 2004 Kenneth Hayber <khayber@socal.rr.com>
5 All rights reserved.
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; either 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 """
21 import rox, sys
22 from rox import app_options, Menu, InfoWin, OptionsBox
23 from rox.options import Option
24 import gtk, gobject, volumecontrol
25 from volumecontrol import VolumeControl
27 try:
28 import ossaudiodev
29 except:
30 rox.croak(_("You need python 2.3 for ossaudiodev support"))
32 APP_NAME = 'Mixer'
33 APP_DIR = rox.app_dir
34 APP_SIZE = [20, 100]
36 #Options.xml processing
37 from rox import choices
38 choices.migrate('Volume', 'hayber.us')
39 rox.setup_app_options('Volume', 'Mixer.xml', site='hayber.us')
40 Menu.set_save_name('Volume', site='hayber.us')
42 MIXER_DEVICE = Option('mixer_device', '/dev/mixer')
43 SHOW_VALUES = Option('show_values', False)
44 SHOW_CONTROLS = Option('controls', -1)
46 MASK_LOCK = Option('lock_mask', -1)
47 MASK_MUTE = Option('mute_mask', 0)
49 def build_mixer_controls(box, node, label, option):
50 """Custom Option widget to allow hide/display of each mixer control"""
51 frame = gtk.ScrolledWindow()
52 frame.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS)
53 frame.set_size_request(150, 150)
54 vbox = gtk.VBox()
55 frame.add_with_viewport(vbox)
57 controls = {}
59 def get_values():
60 value = 0
61 for x in controls:
62 if controls[x].get_active():
63 value |= (1 << x)
64 return value
65 def set_values(): pass
66 box.handlers[option] = (get_values, set_values)
68 mixer = ossaudiodev.openmixer(MIXER_DEVICE.value)
69 for channel in OSS_CHANNELS:
70 if mixer.controls() & (1 << channel[0]):
71 checkbox = controls[channel[0]] = gtk.CheckButton(label=channel[1])
72 if option.int_value & (1 << channel[0]):
73 checkbox.set_active(True)
74 checkbox.connect('toggled', lambda e: box.check_widget(option))
75 vbox.pack_start(checkbox)
76 mixer.close()
77 box.may_add_tip(frame, node)
78 return [frame]
79 OptionsBox.widget_registry['mixer_controls'] = build_mixer_controls
81 def build_hidden_value(box, node, label, option):
82 """
83 A custom Option widget to save/restore a value
84 in the Options system without any UI
85 """
86 widget = gtk.HBox() #something unobtrusive
87 def get_values(): return option.int_value
88 def set_values(): pass
89 box.handlers[option] = (get_values, set_values)
90 return [widget]
91 OptionsBox.widget_registry['hidden_value'] = build_hidden_value
93 rox.app_options.notify()
96 OSS_CHANNELS = [
97 (ossaudiodev.SOUND_MIXER_VOLUME, _('Master')),
98 (ossaudiodev.SOUND_MIXER_BASS, _('Bass')),
99 (ossaudiodev.SOUND_MIXER_TREBLE, _('Treble')),
100 (ossaudiodev.SOUND_MIXER_SYNTH, _('Synth')),
101 (ossaudiodev.SOUND_MIXER_PCM, _('PCM')),
102 (ossaudiodev.SOUND_MIXER_SPEAKER, _('Speaker')),
103 (ossaudiodev.SOUND_MIXER_LINE, _('Line')),
104 (ossaudiodev.SOUND_MIXER_MIC, _('Mic')),
105 (ossaudiodev.SOUND_MIXER_CD, _('CD')),
106 (ossaudiodev.SOUND_MIXER_IMIX, _('iMix')), # Recording monitor
107 (ossaudiodev.SOUND_MIXER_ALTPCM, _('PCM2')),
108 (ossaudiodev.SOUND_MIXER_RECLEV, _('Rec Level')), # Recording level
109 (ossaudiodev.SOUND_MIXER_IGAIN, _('In Gain')), # Input gain
110 (ossaudiodev.SOUND_MIXER_OGAIN, _('Out Gain')), # Output gain
111 (14, _('Aux')),
112 (15, _('15')),
113 (16, _('16')),
114 (17, _('17')),
115 (18, _('18')),
116 (19, _('19')),
117 (20, _('ph In')),
118 (21, _('ph Out')),
119 (22, _('Video')),
120 (23, _('23')),
121 (24, _('24')),
122 (25, _('25')),
126 class Mixer(rox.Window):
127 """A sound mixer class"""
128 def __init__(self):
129 rox.Window.__init__(self)
131 self.thing = gtk.HBox()
132 self.add(self.thing)
134 # Update things when options change
135 rox.app_options.add_notify(self.get_options)
137 mixer = ossaudiodev.openmixer(MIXER_DEVICE.value)
138 self.mixer = mixer
140 self.lock_mask = MASK_LOCK.int_value
141 self.mute_mask = MASK_MUTE.int_value
142 self.rec_mask = mixer.get_recsrc()
144 for channel in OSS_CHANNELS:
145 #if the mixer supports a channel add it
146 if (mixer.controls() & (1 << channel[0])):
147 option_mask = option_value = 0
149 if mixer.stereocontrols() & (1 << channel[0]):
150 option_mask |= volumecontrol._STEREO
151 option_mask |= volumecontrol._LOCK
153 if self.lock_mask & (1 << channel[0]):
154 option_value |= volumecontrol._LOCK
156 if mixer.reccontrols() & (1 << channel[0]):
157 option_mask |= volumecontrol._REC
159 if self.rec_mask & (1 << channel[0]):
160 option_value |= volumecontrol._REC
162 option_mask |= volumecontrol._MUTE
163 if self.mute_mask & (1 << channel[0]):
164 option_value |= volumecontrol._MUTE
166 volume = VolumeControl(channel[0], option_mask, option_value,
167 SHOW_VALUES.int_value, channel[1])
168 volume.set_level(self.get_volume(channel[0]))
169 volume.connect("volume_changed", self.adjust_volume)
170 volume.connect("volume_setting_toggled", self.setting_toggled)
171 self.thing.pack_start(volume)
173 self.thing.show()
174 self.show_hide_controls()
176 self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
177 self.connect('button-press-event', self.button_press)
178 self.menu = Menu.Menu('main', [
179 Menu.Action(_('Options'), 'show_options', '', gtk.STOCK_PREFERENCES),
180 Menu.Action(_('Info'), 'get_info', '', gtk.STOCK_DIALOG_INFO),
181 Menu.Action(_('Close'), 'quit', '', gtk.STOCK_CLOSE),
183 self.menu.attach(self, self)
185 self.connect('delete_event', self.quit)
188 def button_press(self, text, event):
189 '''Popup menu handler'''
190 if event.button != 3:
191 return 0
192 self.menu.popup(self, event)
193 return 1
195 def setting_toggled(self, vol, channel, id, val):
196 """Handle checkbox toggles"""
197 if id == volumecontrol._MUTE:
198 if val: #mute on
199 self.mute_mask |= (1<<channel)
200 self.set_volume((0, 0), channel)
201 else:
202 self.mute_mask &= ~(1<<channel)
203 self.set_volume(vol.get_level(), channel)
204 MASK_MUTE._set(self.mute_mask)
206 if id == volumecontrol._LOCK:
207 if val:
208 self.lock_mask |= (1<<channel)
209 else:
210 self.lock_mask &= ~(1<<channel)
211 MASK_LOCK._set(self.lock_mask)
213 # use the OSS api to set/clear these bits/checkboxes.
214 if id == volumecontrol._REC:
215 if val:
216 #when one is checked, the others (typically) are cleared
217 #but it may be possible to have more than one checked (I think)
218 self.rec_mask = self.mixer.set_recsrc( (1<<channel) )
219 for x in self.thing.get_children():
220 if isinstance(x, VolumeControl):
221 x.set_recsrc(self.rec_mask & (1<<x.channel))
222 else:
223 self.rec_mask = self.mixer.set_recsrc( ~(1<<channel) )
226 def adjust_volume(self, vol, channel, volume1, volume2):
227 """Track changes to the volume controls"""
228 self.set_volume((volume1, volume2), channel)
230 def set_volume(self, volume, channel):
231 """Set the playback volume"""
232 if self.mixer != None:
233 self.mixer.set(channel, (volume[0], volume[1]))
235 def get_volume(self, channel):
236 """Get the current sound card setting for specified channel"""
237 if self.mixer != None:
238 vol = self.mixer.get(channel)
239 return (vol[0], vol[1])
241 def get_options(self):
242 """Used as the notify callback when options change"""
243 if SHOW_VALUES.has_changed:
244 controls = self.thing.get_children()
245 for control in controls:
246 control.show_values(bool(SHOW_VALUES.int_value))
248 if SHOW_CONTROLS.has_changed:
249 self.show_hide_controls()
251 def show_hide_controls(self):
252 controls = self.thing.get_children()
253 for control in controls:
254 if (SHOW_CONTROLS.int_value & (1 << control.channel)):
255 control.show()
256 else:
257 control.hide()
258 (x, y) = self.thing.size_request()
259 self.resize(x, y)
261 def show_options(self, button=None):
262 """Options edit dialog"""
263 rox.edit_options(APP_DIR+'/Mixer.xml')
265 def get_info(self):
266 InfoWin.infowin(APP_NAME)
268 def quit(self, ev=None, e1=None):
269 rox.app_options.save()
270 self.destroy()