2 mixer.py (an OSS sound mixer for the ROX Desktop)
4 Copyright 2004 Kenneth Hayber <khayber@socal.rr.com>
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
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
30 rox
.croak(_("You need python 2.3 for ossaudiodev support"))
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)
55 frame
.add_with_viewport(vbox
)
62 if controls
[x
].get_active():
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
)
77 box
.may_add_tip(frame
, node
)
79 OptionsBox
.widget_registry
['mixer_controls'] = build_mixer_controls
81 def build_hidden_value(box
, node
, label
, option
):
83 A custom Option widget to save/restore a value
84 in the Options system without any UI
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
)
91 OptionsBox
.widget_registry
['hidden_value'] = build_hidden_value
93 rox
.app_options
.notify()
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
126 class Mixer(rox
.Window
):
127 """A sound mixer class"""
129 rox
.Window
.__init
__(self
)
131 self
.thing
= gtk
.HBox()
134 # Update things when options change
135 rox
.app_options
.add_notify(self
.get_options
)
137 mixer
= ossaudiodev
.openmixer(MIXER_DEVICE
.value
)
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
)
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:
192 self
.menu
.popup(self
, event
)
195 def setting_toggled(self
, vol
, channel
, id, val
):
196 """Handle checkbox toggles"""
197 if id == volumecontrol
._MUTE
:
199 self
.mute_mask |
= (1<<channel
)
200 self
.set_volume((0, 0), channel
)
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
:
208 self
.lock_mask |
= (1<<channel
)
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
:
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
))
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
)):
258 (x
, y
) = self
.thing
.size_request()
261 def show_options(self
, button
=None):
262 """Options edit dialog"""
263 rox
.edit_options(APP_DIR
+'/Mixer.xml')
266 InfoWin
.infowin(APP_NAME
)
268 def quit(self
, ev
=None, e1
=None):
269 rox
.app_options
.save()