1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: http://www.musicpd.org
5 * OSS audio output (c) 2004, 2005, 2006, 2007 by Eric Wong <eric@petta-tech.com>
6 * and Warren Dukes <warren.dukes@gmail.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "../audioOutput.h"
33 #include <sys/types.h>
35 #include <sys/ioctl.h>
40 #if defined(__OpenBSD__) || defined(__NetBSD__)
41 # include <soundcard.h>
42 #else /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
43 # include <sys/soundcard.h>
44 #endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
46 #ifdef WORDS_BIGENDIAN
47 # define AFMT_S16_MPD AFMT_S16_BE
49 # define AFMT_S16_MPD AFMT_S16_LE
50 #endif /* WORDS_BIGENDIAN */
52 typedef struct _OssData
{
62 int numUnsupported
[3];
65 #define OSS_SUPPORTED 1
66 #define OSS_UNSUPPORTED 0
67 #define OSS_UNKNOWN -1
70 #define OSS_CHANNELS 1
73 static int getIndexForParam(int param
)
78 case SNDCTL_DSP_SPEED
:
81 case SNDCTL_DSP_CHANNELS
:
84 case SNDCTL_DSP_SAMPLESIZE
:
92 static int findSupportedParam(OssData
* od
, int param
, int val
)
95 int index
= getIndexForParam(param
);
97 for (i
= 0; i
< od
->numSupported
[index
]; i
++) {
98 if (od
->supported
[index
][i
] == val
)
105 static int canConvert(int index
, int val
)
121 static int getSupportedParam(OssData
* od
, int param
, int val
)
124 int index
= getIndexForParam(param
);
129 for (i
= 0; i
< od
->numSupported
[index
]; i
++) {
130 diff
= od
->supported
[index
][i
] - val
;
134 if (!canConvert(index
, od
->supported
[index
][i
])) {
138 ret
= od
->supported
[index
][i
];
145 static int findUnsupportedParam(OssData
* od
, int param
, int val
)
148 int index
= getIndexForParam(param
);
150 for (i
= 0; i
< od
->numUnsupported
[index
]; i
++) {
151 if (od
->unsupported
[index
][i
] == val
)
158 static void addSupportedParam(OssData
* od
, int param
, int val
)
160 int index
= getIndexForParam(param
);
162 od
->numSupported
[index
]++;
163 od
->supported
[index
] = xrealloc(od
->supported
[index
],
164 od
->numSupported
[index
] * sizeof(int));
165 od
->supported
[index
][od
->numSupported
[index
] - 1] = val
;
168 static void addUnsupportedParam(OssData
* od
, int param
, int val
)
170 int index
= getIndexForParam(param
);
172 od
->numUnsupported
[index
]++;
173 od
->unsupported
[index
] = xrealloc(od
->unsupported
[index
],
174 od
->numUnsupported
[index
] *
176 od
->unsupported
[index
][od
->numUnsupported
[index
] - 1] = val
;
179 static void removeSupportedParam(OssData
* od
, int param
, int val
)
183 int index
= getIndexForParam(param
);
185 for (i
= 0; i
< od
->numSupported
[index
] - 1; i
++) {
186 if (od
->supported
[index
][i
] == val
)
188 od
->supported
[index
][i
] = od
->supported
[index
][i
+ j
];
191 od
->numSupported
[index
]--;
192 od
->supported
[index
] = xrealloc(od
->supported
[index
],
193 od
->numSupported
[index
] * sizeof(int));
196 static void removeUnsupportedParam(OssData
* od
, int param
, int val
)
200 int index
= getIndexForParam(param
);
202 for (i
= 0; i
< od
->numUnsupported
[index
] - 1; i
++) {
203 if (od
->unsupported
[index
][i
] == val
)
205 od
->unsupported
[index
][i
] = od
->unsupported
[index
][i
+ j
];
208 od
->numUnsupported
[index
]--;
209 od
->unsupported
[index
] = xrealloc(od
->unsupported
[index
],
210 od
->numUnsupported
[index
] *
214 static int isSupportedParam(OssData
* od
, int param
, int val
)
216 if (findSupportedParam(od
, param
, val
))
217 return OSS_SUPPORTED
;
218 if (findUnsupportedParam(od
, param
, val
))
219 return OSS_UNSUPPORTED
;
223 static void supportParam(OssData
* od
, int param
, int val
)
225 int supported
= isSupportedParam(od
, param
, val
);
227 if (supported
== OSS_SUPPORTED
)
230 if (supported
== OSS_UNSUPPORTED
) {
231 removeUnsupportedParam(od
, param
, val
);
234 addSupportedParam(od
, param
, val
);
237 static void unsupportParam(OssData
* od
, int param
, int val
)
239 int supported
= isSupportedParam(od
, param
, val
);
241 if (supported
== OSS_UNSUPPORTED
)
244 if (supported
== OSS_SUPPORTED
) {
245 removeSupportedParam(od
, param
, val
);
248 addUnsupportedParam(od
, param
, val
);
251 static OssData
*newOssData(void)
253 OssData
*ret
= xmalloc(sizeof(OssData
));
258 ret
->supported
[OSS_RATE
] = NULL
;
259 ret
->supported
[OSS_CHANNELS
] = NULL
;
260 ret
->supported
[OSS_BITS
] = NULL
;
261 ret
->unsupported
[OSS_RATE
] = NULL
;
262 ret
->unsupported
[OSS_CHANNELS
] = NULL
;
263 ret
->unsupported
[OSS_BITS
] = NULL
;
265 ret
->numSupported
[OSS_RATE
] = 0;
266 ret
->numSupported
[OSS_CHANNELS
] = 0;
267 ret
->numSupported
[OSS_BITS
] = 0;
268 ret
->numUnsupported
[OSS_RATE
] = 0;
269 ret
->numUnsupported
[OSS_CHANNELS
] = 0;
270 ret
->numUnsupported
[OSS_BITS
] = 0;
272 supportParam(ret
, SNDCTL_DSP_SPEED
, 48000);
273 supportParam(ret
, SNDCTL_DSP_SPEED
, 44100);
274 supportParam(ret
, SNDCTL_DSP_CHANNELS
, 2);
275 supportParam(ret
, SNDCTL_DSP_SAMPLESIZE
, 16);
280 static void freeOssData(OssData
* od
)
282 if (od
->supported
[OSS_RATE
])
283 free(od
->supported
[OSS_RATE
]);
284 if (od
->supported
[OSS_CHANNELS
])
285 free(od
->supported
[OSS_CHANNELS
]);
286 if (od
->supported
[OSS_BITS
])
287 free(od
->supported
[OSS_BITS
]);
288 if (od
->unsupported
[OSS_RATE
])
289 free(od
->unsupported
[OSS_RATE
]);
290 if (od
->unsupported
[OSS_CHANNELS
])
291 free(od
->unsupported
[OSS_CHANNELS
]);
292 if (od
->unsupported
[OSS_BITS
])
293 free(od
->unsupported
[OSS_BITS
]);
298 #define OSS_STAT_NO_ERROR 0
299 #define OSS_STAT_NOT_CHAR_DEV -1
300 #define OSS_STAT_NO_PERMS -2
301 #define OSS_STAT_DOESN_T_EXIST -3
302 #define OSS_STAT_OTHER -4
304 static int oss_statDevice(const char *device
, int *stErrno
)
308 if (0 == stat(device
, &st
)) {
309 if (!S_ISCHR(st
.st_mode
)) {
310 return OSS_STAT_NOT_CHAR_DEV
;
318 return OSS_STAT_DOESN_T_EXIST
;
320 return OSS_STAT_NO_PERMS
;
322 return OSS_STAT_OTHER
;
329 static const char *default_devices
[] = { "/dev/sound/dsp", "/dev/dsp" };
331 static int oss_testDefault(void)
335 for (i
= ARRAY_SIZE(default_devices
); --i
>= 0; ) {
336 if ((fd
= open(default_devices
[i
], O_WRONLY
)) >= 0) {
340 WARNING("Error opening OSS device \"%s\": %s\n",
341 default_devices
[i
], strerror(errno
));
347 static int oss_open_default(AudioOutput
*ao
, ConfigParam
*param
, OssData
*od
)
350 int err
[ARRAY_SIZE(default_devices
)];
351 int ret
[ARRAY_SIZE(default_devices
)];
353 for (i
= ARRAY_SIZE(default_devices
); --i
>= 0; ) {
354 ret
[i
] = oss_statDevice(default_devices
[i
], &err
[i
]);
356 od
->device
= default_devices
[i
];
362 ERROR("error trying to open specified OSS device"
363 " at line %i\n", param
->line
);
365 ERROR("error trying to open default OSS device\n");
367 for (i
= ARRAY_SIZE(default_devices
); --i
>= 0; ) {
368 const char *dev
= default_devices
[i
];
370 case OSS_STAT_DOESN_T_EXIST
:
371 ERROR("%s not found\n", dev
);
373 case OSS_STAT_NOT_CHAR_DEV
:
374 ERROR("%s is not a character device\n", dev
);
376 case OSS_STAT_NO_PERMS
:
377 ERROR("%s: permission denied\n", dev
);
380 ERROR("Error accessing %s: %s", dev
, strerror(err
[i
]));
384 return 0; /* some compilers can be dumb... */
387 static int oss_initDriver(AudioOutput
* audioOutput
, ConfigParam
* param
)
389 OssData
*od
= newOssData();
390 audioOutput
->data
= od
;
392 BlockParam
*bp
= getBlockParam(param
, "device");
394 od
->device
= bp
->value
;
398 return oss_open_default(audioOutput
, param
, od
);
401 static void oss_finishDriver(AudioOutput
* audioOutput
)
403 OssData
*od
= audioOutput
->data
;
408 static int setParam(OssData
* od
, int param
, int *value
)
412 int supported
= isSupportedParam(od
, param
, val
);
415 if (supported
== OSS_UNSUPPORTED
) {
416 val
= getSupportedParam(od
, param
, val
);
421 if (ioctl(od
->fd
, param
, ©
)) {
422 unsupportParam(od
, param
, val
);
423 supported
= OSS_UNSUPPORTED
;
425 if (supported
== OSS_UNKNOWN
) {
426 supportParam(od
, param
, val
);
427 supported
= OSS_SUPPORTED
;
431 } while (supported
== OSS_UNSUPPORTED
);
438 static void oss_close(OssData
* od
)
441 while (close(od
->fd
) && errno
== EINTR
) ;
445 static int oss_open(AudioOutput
* audioOutput
)
448 OssData
*od
= audioOutput
->data
;
450 if ((od
->fd
= open(od
->device
, O_WRONLY
)) < 0) {
451 ERROR("Error opening OSS device \"%s\": %s\n", od
->device
,
456 if (setParam(od
, SNDCTL_DSP_CHANNELS
, &od
->channels
)) {
457 ERROR("OSS device \"%s\" does not support %i channels: %s\n",
458 od
->device
, od
->channels
, strerror(errno
));
462 if (setParam(od
, SNDCTL_DSP_SPEED
, &od
->sampleRate
)) {
463 ERROR("OSS device \"%s\" does not support %i Hz audio: %s\n",
464 od
->device
, od
->sampleRate
, strerror(errno
));
476 if (setParam(od
, SNDCTL_DSP_SAMPLESIZE
, &tmp
)) {
477 ERROR("OSS device \"%s\" does not support %i bit audio: %s\n",
478 od
->device
, tmp
, strerror(errno
));
482 audioOutput
->open
= 1;
488 audioOutput
->open
= 0;
492 static int oss_openDevice(AudioOutput
* audioOutput
)
495 OssData
*od
= audioOutput
->data
;
496 AudioFormat
*audioFormat
= &audioOutput
->outAudioFormat
;
498 od
->channels
= audioFormat
->channels
;
499 od
->sampleRate
= audioFormat
->sampleRate
;
500 od
->bits
= audioFormat
->bits
;
502 if ((ret
= oss_open(audioOutput
)) < 0)
505 audioFormat
->channels
= od
->channels
;
506 audioFormat
->sampleRate
= od
->sampleRate
;
507 audioFormat
->bits
= od
->bits
;
509 DEBUG("oss device \"%s\" will be playing %i bit %i channel audio at "
510 "%i Hz\n", od
->device
, od
->bits
, od
->channels
, od
->sampleRate
);
515 static void oss_closeDevice(AudioOutput
* audioOutput
)
517 OssData
*od
= audioOutput
->data
;
521 audioOutput
->open
= 0;
524 static void oss_dropBufferedAudio(AudioOutput
* audioOutput
)
526 OssData
*od
= audioOutput
->data
;
529 ioctl(od
->fd
, SNDCTL_DSP_RESET
, 0);
534 static int oss_playAudio(AudioOutput
* audioOutput
, char *playChunk
, int size
)
536 OssData
*od
= audioOutput
->data
;
539 /* reopen the device since it was closed by dropBufferedAudio */
540 if (od
->fd
< 0 && oss_open(audioOutput
) < 0)
544 ret
= write(od
->fd
, playChunk
, size
);
548 ERROR("closing oss device \"%s\" due to write error: "
549 "%s\n", od
->device
, strerror(errno
));
550 oss_closeDevice(audioOutput
);
560 AudioOutputPlugin ossPlugin
= {
567 oss_dropBufferedAudio
,
569 NULL
, /* sendMetadataFunc */
574 DISABLED_AUDIO_OUTPUT_PLUGIN(ossPlugin
)
575 #endif /* HAVE_OSS */