1 /*
2 * PortMixer
3 * Unix OSS Implementation
4 *
5 * Copyright (c) 2002
6 *
7 * Written by Dominic Mazzoni
8 *
9 * PortMixer is intended to work side-by-side with PortAudio,
10 * the Portable Real-Time Audio Library by Ross Bencina and
11 * Phil Burk.
12 *
13 * Permission is hereby granted, free of charge, to any person obtaining
14 * a copy of this software and associated documentation files
15 * (the "Software"), to deal in the Software without restriction,
16 * including without limitation the rights to use, copy, modify, merge,
17 * publish, distribute, sublicense, and/or sell copies of the Software,
18 * and to permit persons to whom the Software is furnished to do so,
19 * subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be
22 * included in all copies or substantial portions of the Software.
23 *
24 * Any person wishing to distribute modifications to the Software is
25 * requested to send the modifications to the original developer so that
26 * they can be incorporated into the canonical version.
27 *
28 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
31 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
32 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
33 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 *
36 */
37
38 #if defined(__linux__)
39 #include <linux/soundcard.h>
40 #elif defined(__FreeBSD__)
41 #include <sys/soundcard.h>
42 #else
43 #include <machine/soundcard.h> /* JH20010905 */
44 #endif
45
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <fcntl.h>
49 #include <unistd.h>
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <sys/ioctl.h>
53
54 #include "portaudio.h"
55 #include "portmixer.h"
56
57 typedef struct PxInfo
58 {
59 int index;
60 int fd;
61
62 int num_out;
63 int outs[SOUND_MIXER_NRDEVICES];
64 int num_rec;
65 int recs[SOUND_MIXER_NRDEVICES];
66 } PxInfo;
67
68 char PxDevice[20] = "/dev/mixerX";
69
70 int PxNumDevices = 0;
71 int PxDevices[10];
72
Px_GetNumMixers(void * pa_stream)73 int Px_GetNumMixers( void *pa_stream )
74 {
75 int i;
76 int fd;
77
78 PxNumDevices = 0;
79
80 for(i=0; i<11; i++) {
81 if (i==0)
82 PxDevice[10] = 0;
83 else
84 PxDevice[10] = '0'+(i-1);
85 fd = open(PxDevice, O_RDWR);
86 if (fd >= 0) {
87 PxDevices[PxNumDevices] = i;
88 PxNumDevices++;
89 close(fd);
90 }
91 }
92
93 return PxNumDevices;
94 }
95
Px_GetMixerName(void * pa_stream,int index)96 const char *Px_GetMixerName( void *pa_stream, int index )
97 {
98 if (PxNumDevices <= 0)
99 Px_GetNumMixers(pa_stream);
100
101 if (index < 0 || index >= PxNumDevices)
102 return NULL;
103
104 if (PxDevices[index]==0)
105 PxDevice[10] = 0;
106 else
107 PxDevice[10] = '0'+(PxDevices[index]-1);
108 return PxDevice;
109 }
110
Px_OpenMixer(void * pa_stream,int index)111 PxMixer *Px_OpenMixer( void *pa_stream, int index )
112 {
113 PxInfo *info;
114 int devmask, recmask, outmask;
115 int i;
116
117 if (PxNumDevices <= 0)
118 Px_GetNumMixers(pa_stream);
119
120 if (index < 0 || index >= PxNumDevices)
121 return NULL;
122
123 info = (PxInfo *)malloc(sizeof(PxInfo));
124 info->index = PxDevice[index];
125
126 if (PxDevices[index]==0)
127 PxDevice[10] = 0;
128 else
129 PxDevice[10] = '0'+(PxDevices[index]-1);
130 info->fd = open(PxDevice, O_RDWR);
131 if (info->fd < 0)
132 goto bad;
133
134 if (ioctl(info->fd, MIXER_READ(SOUND_MIXER_READ_DEVMASK),
135 &devmask) == -1)
136 goto bad;
137 if (ioctl(info->fd, MIXER_READ(SOUND_MIXER_READ_RECMASK),
138 &recmask) == -1)
139 goto bad;
140 outmask = devmask ^ recmask;
141
142 info->num_out = 0;
143 info->num_rec = 0;
144
145 for(i=0; i<SOUND_MIXER_NRDEVICES; i++)
146 if (recmask & (1<<i))
147 info->recs[info->num_rec++] = i;
148 else if (devmask & (1<<i))
149 info->outs[info->num_out++] = i;
150
151 return (PxMixer *)info;
152
153 bad:
154 free(info);
155 return NULL;
156 }
157
158 /*
159 Px_CloseMixer() closes a mixer opened using Px_OpenMixer and frees any
160 memory associated with it.
161 */
162
Px_CloseMixer(PxMixer * mixer)163 void Px_CloseMixer(PxMixer *mixer)
164 {
165 PxInfo *info = (PxInfo *)mixer;
166
167 close(info->fd);
168
169 free(info);
170 }
171
GetVolume(int fd,int channel)172 PxVolume GetVolume(int fd, int channel)
173 {
174 int vol;
175 int stereo;
176
177 if (ioctl(fd, SOUND_MIXER_READ_STEREODEVS, &stereo) == 0)
178 stereo = ((stereo & (1 << channel)) != 0);
179 else
180 stereo = 0;
181
182 if (ioctl(fd, MIXER_READ(channel), &vol) == -1)
183 return 0.0;
184
185 if (stereo)
186 return ((vol & 0xFF)/200.0) + (((vol>>8) & 0xFF)/200.0);
187 else
188 return (vol & 0xFF)/100.0;
189 }
190
191 /*
192 Master (output) volume
193 */
194
Px_GetMasterVolume(PxMixer * mixer)195 PxVolume Px_GetMasterVolume( PxMixer *mixer )
196 {
197 PxInfo *info = (PxInfo *)mixer;
198
199 return GetVolume(info->fd, SOUND_MIXER_VOLUME);
200 }
201
Px_SetMasterVolume(PxMixer * mixer,PxVolume volume)202 void Px_SetMasterVolume( PxMixer *mixer, PxVolume volume )
203 {
204 PxInfo *info = (PxInfo *)mixer;
205
206 int vol = (int)((volume * 100.0) + 0.5);
207 vol = (vol | (vol<<8));
208 ioctl(info->fd, MIXER_WRITE(SOUND_MIXER_VOLUME), &vol);
209 }
210
211 /*
212 PCM output volume
213 */
214
Px_SupportsPCMOutputVolume(PxMixer * mixer)215 int Px_SupportsPCMOutputVolume( PxMixer* mixer )
216 {
217 return 1 ;
218 }
219
Px_GetPCMOutputVolume(PxMixer * mixer)220 PxVolume Px_GetPCMOutputVolume( PxMixer *mixer )
221 {
222 PxInfo *info = (PxInfo *)mixer;
223
224 return GetVolume(info->fd, SOUND_MIXER_PCM);
225 }
226
Px_SetPCMOutputVolume(PxMixer * mixer,PxVolume volume)227 void Px_SetPCMOutputVolume( PxMixer *mixer, PxVolume volume )
228 {
229 PxInfo *info = (PxInfo *)mixer;
230
231 int vol = (int)((volume * 100.0) + 0.5);
232 vol = (vol | (vol<<8));
233 ioctl(info->fd, MIXER_WRITE(SOUND_MIXER_PCM), &vol);
234 }
235
236 /*
237 All output volumes
238 */
239
Px_GetNumOutputVolumes(PxMixer * mixer)240 int Px_GetNumOutputVolumes( PxMixer *mixer )
241 {
242 PxInfo *info = (PxInfo *)mixer;
243
244 return info->num_out;
245 }
246
Px_GetOutputVolumeName(PxMixer * mixer,int i)247 const char *Px_GetOutputVolumeName( PxMixer *mixer, int i )
248 {
249 PxInfo *info = (PxInfo *)mixer;
250 const char *labels[] = SOUND_DEVICE_LABELS;
251
252 return labels[info->outs[i]];
253 }
254
Px_GetOutputVolume(PxMixer * mixer,int i)255 PxVolume Px_GetOutputVolume( PxMixer *mixer, int i )
256 {
257 PxInfo *info = (PxInfo *)mixer;
258
259 return GetVolume(info->fd, info->outs[i]);
260 }
261
Px_SetOutputVolume(PxMixer * mixer,int i,PxVolume volume)262 void Px_SetOutputVolume( PxMixer *mixer, int i, PxVolume volume )
263 {
264 PxInfo *info = (PxInfo *)mixer;
265
266 int vol = (int)((volume * 100.0) + 0.5);
267 vol = (vol | (vol<<8));
268 ioctl(info->fd, MIXER_WRITE(info->outs[i]), &vol);
269 }
270
271 /*
272 Input sources
273 */
274
Px_GetNumInputSources(PxMixer * mixer)275 int Px_GetNumInputSources( PxMixer *mixer )
276 {
277 PxInfo *info = (PxInfo *)mixer;
278
279 return info->num_rec;
280 }
281
Px_GetInputSourceName(PxMixer * mixer,int i)282 const char *Px_GetInputSourceName( PxMixer *mixer, int i)
283 {
284 PxInfo *info = (PxInfo *)mixer;
285
286 const char *labels[] = SOUND_DEVICE_LABELS;
287 return labels[info->recs[i]];
288 }
289
Px_GetCurrentInputSource(PxMixer * mixer)290 int Px_GetCurrentInputSource( PxMixer *mixer )
291 {
292 PxInfo *info = (PxInfo *)mixer;
293 int recmask;
294 int i;
295
296 /* Note that there may be more than one in OSS; we pick
297 the first one */
298
299 if (ioctl(info->fd, MIXER_READ(SOUND_MIXER_READ_RECSRC),
300 &recmask) == -1)
301 return -1; /* none / error */
302
303 for(i=0; i<info->num_rec; i++)
304 if (recmask & (1 << (info->recs[i])))
305 return i;
306
307 return -1; /* none */
308 }
309
Px_SetCurrentInputSource(PxMixer * mixer,int i)310 void Px_SetCurrentInputSource( PxMixer *mixer, int i )
311 {
312 PxInfo *info = (PxInfo *)mixer;
313 int newrecsrcmask = (1 << (info->recs[i]));
314
315 ioctl(info->fd, MIXER_WRITE(SOUND_MIXER_READ_RECSRC),
316 &newrecsrcmask);
317 }
318
319 /*
320 Input volume
321 */
322
Px_GetInputVolume(PxMixer * mixer)323 PxVolume Px_GetInputVolume( PxMixer *mixer )
324 {
325 PxInfo *info = (PxInfo *)mixer;
326 int i;
327
328 i = Px_GetCurrentInputSource(mixer);
329 if (i < 0)
330 return 0.0;
331
332 return GetVolume(info->fd, info->recs[i]);
333 }
334
Px_SetInputVolume(PxMixer * mixer,PxVolume volume)335 void Px_SetInputVolume( PxMixer *mixer, PxVolume volume )
336 {
337 PxInfo *info = (PxInfo *)mixer;
338 int vol;
339 int i;
340
341 i = Px_GetCurrentInputSource(mixer);
342 if (i < 0)
343 return;
344
345 vol = (int)((volume * 100.0) + 0.5);
346 vol = (vol | (vol<<8));
347 ioctl(info->fd, MIXER_WRITE(info->recs[i]), &vol);
348 }
349
350 /*
351 Balance
352 */
353
Px_SupportsOutputBalance(PxMixer * mixer)354 int Px_SupportsOutputBalance( PxMixer *mixer )
355 {
356 return 0;
357 }
358
Px_GetOutputBalance(PxMixer * mixer)359 PxBalance Px_GetOutputBalance( PxMixer *mixer )
360 {
361 return 0.0;
362 }
363
Px_SetOutputBalance(PxMixer * mixer,PxBalance balance)364 void Px_SetOutputBalance( PxMixer *mixer, PxBalance balance )
365 {
366 }
367
368 /*
369 Playthrough
370 */
371
Px_SupportsPlaythrough(PxMixer * mixer)372 int Px_SupportsPlaythrough( PxMixer *mixer )
373 {
374 return 0;
375 }
376
Px_GetPlaythrough(PxMixer * mixer)377 PxVolume Px_GetPlaythrough( PxMixer *mixer )
378 {
379 return 0.0;
380 }
381
Px_SetPlaythrough(PxMixer * mixer,PxVolume volume)382 void Px_SetPlaythrough( PxMixer *mixer, PxVolume volume )
383 {
384 }
385
386
387 /*
388 unimplemented stubs
389 */
390
Px_SetMicrophoneBoost(PxMixer * mixer,int enable)391 int Px_SetMicrophoneBoost( PxMixer* mixer, int enable )
392 {
393 return 1 ;
394 }
395
Px_GetMicrophoneBoost(PxMixer * mixer)396 int Px_GetMicrophoneBoost( PxMixer* mixer )
397 {
398 return -1 ;
399 }
400
Px_SetCurrentInputSourceByName(PxMixer * mixer,const char * line_name)401 int Px_SetCurrentInputSourceByName( PxMixer* mixer, const char* line_name )
402 {
403 return 1 ;
404 }
405