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