Initial revision 6759
[qball-mpd.git] / src / audioOutputs / .svn / text-base / audioOutput_oss.c.svn-base
blob01293cbd1567c8afd309c53760d0bc32a988e1a0
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
4  *
5  * OSS audio output (c) 2004, 2005, 2006, 2007 by Eric Wong <eric@petta-tech.com>
6  *                   and Warren Dukes <warren.dukes@gmail.com>
7  *
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.
12  *
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
20  */
22 #include "../audioOutput.h"
24 #include <stdlib.h>
26 #ifdef HAVE_OSS
28 #include "../conf.h"
29 #include "../log.h"
31 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/ioctl.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <errno.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
48 #else
49 # define        AFMT_S16_MPD     AFMT_S16_LE
50 #endif /* WORDS_BIGENDIAN */
52 typedef struct _OssData {
53         int fd;
54         const char *device;
55         int channels;
56         int sampleRate;
57         int bitFormat;
58         int bits;
59         int *supported[3];
60         int numSupported[3];
61         int *unsupported[3];
62         int numUnsupported[3];
63 } OssData;
65 #define OSS_SUPPORTED           1
66 #define OSS_UNSUPPORTED         0
67 #define OSS_UNKNOWN             -1
69 #define OSS_RATE                0
70 #define OSS_CHANNELS            1
71 #define OSS_BITS                2
73 static int getIndexForParam(int param)
75         int index = 0;
77         switch (param) {
78         case SNDCTL_DSP_SPEED:
79                 index = OSS_RATE;
80                 break;
81         case SNDCTL_DSP_CHANNELS:
82                 index = OSS_CHANNELS;
83                 break;
84         case SNDCTL_DSP_SAMPLESIZE:
85                 index = OSS_BITS;
86                 break;
87         }
89         return index;
92 static int findSupportedParam(OssData * od, int param, int val)
94         int i;
95         int index = getIndexForParam(param);
97         for (i = 0; i < od->numSupported[index]; i++) {
98                 if (od->supported[index][i] == val)
99                         return 1;
100         }
102         return 0;
105 static int canConvert(int index, int val)
107         switch (index) {
108         case OSS_BITS:
109                 if (val != 16)
110                         return 0;
111                 break;
112         case OSS_CHANNELS:
113                 if (val != 2)
114                         return 0;
115                 break;
116         }
118         return 1;
121 static int getSupportedParam(OssData * od, int param, int val)
123         int i;
124         int index = getIndexForParam(param);
125         int ret = -1;
126         int least = val;
127         int diff;
129         for (i = 0; i < od->numSupported[index]; i++) {
130                 diff = od->supported[index][i] - val;
131                 if (diff < 0)
132                         diff = -diff;
133                 if (diff < least) {
134                         if (!canConvert(index, od->supported[index][i])) {
135                                 continue;
136                         }
137                         least = diff;
138                         ret = od->supported[index][i];
139                 }
140         }
142         return ret;
145 static int findUnsupportedParam(OssData * od, int param, int val)
147         int i;
148         int index = getIndexForParam(param);
150         for (i = 0; i < od->numUnsupported[index]; i++) {
151                 if (od->unsupported[index][i] == val)
152                         return 1;
153         }
155         return 0;
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] *
175                                          sizeof(int));
176         od->unsupported[index][od->numUnsupported[index] - 1] = val;
179 static void removeSupportedParam(OssData * od, int param, int val)
181         int i = 0;
182         int j = 0;
183         int index = getIndexForParam(param);
185         for (i = 0; i < od->numSupported[index] - 1; i++) {
186                 if (od->supported[index][i] == val)
187                         j = 1;
188                 od->supported[index][i] = od->supported[index][i + j];
189         }
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)
198         int i = 0;
199         int j = 0;
200         int index = getIndexForParam(param);
202         for (i = 0; i < od->numUnsupported[index] - 1; i++) {
203                 if (od->unsupported[index][i] == val)
204                         j = 1;
205                 od->unsupported[index][i] = od->unsupported[index][i + j];
206         }
208         od->numUnsupported[index]--;
209         od->unsupported[index] = xrealloc(od->unsupported[index],
210                                          od->numUnsupported[index] *
211                                          sizeof(int));
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;
220         return OSS_UNKNOWN;
223 static void supportParam(OssData * od, int param, int val)
225         int supported = isSupportedParam(od, param, val);
227         if (supported == OSS_SUPPORTED)
228                 return;
230         if (supported == OSS_UNSUPPORTED) {
231                 removeUnsupportedParam(od, param, val);
232         }
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)
242                 return;
244         if (supported == OSS_SUPPORTED) {
245                 removeSupportedParam(od, param, val);
246         }
248         addUnsupportedParam(od, param, val);
251 static OssData *newOssData(void)
253         OssData *ret = xmalloc(sizeof(OssData));
255         ret->device = NULL;
256         ret->fd = -1;
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);
277         return ret;
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]);
295         free(od);
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)
306         struct stat st;
308         if (0 == stat(device, &st)) {
309                 if (!S_ISCHR(st.st_mode)) {
310                         return OSS_STAT_NOT_CHAR_DEV;
311                 }
312         } else {
313                 *stErrno = errno;
315                 switch (errno) {
316                 case ENOENT:
317                 case ENOTDIR:
318                         return OSS_STAT_DOESN_T_EXIST;
319                 case EACCES:
320                         return OSS_STAT_NO_PERMS;
321                 default:
322                         return OSS_STAT_OTHER;
323                 }
324         }
326         return 0;
329 static const char *default_devices[] = { "/dev/sound/dsp", "/dev/dsp" };
331 static int oss_testDefault(void)
333         int fd, i;
335         for (i = ARRAY_SIZE(default_devices); --i >= 0; ) {
336                 if ((fd = open(default_devices[i], O_WRONLY)) >= 0) {
337                         xclose(fd);
338                         return 0;
339                 }
340                 WARNING("Error opening OSS device \"%s\": %s\n",
341                         default_devices[i], strerror(errno));
342         }
344         return -1;
347 static int oss_open_default(AudioOutput *ao, ConfigParam *param, OssData *od)
349         int i;
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]);
355                 if (ret[i] == 0) {
356                         od->device = default_devices[i];
357                         return 0;
358                 }
359         }
361         if (param)
362                 ERROR("error trying to open specified OSS device"
363                       " at line %i\n", param->line);
364         else
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];
369                 switch(ret[i]) {
370                 case OSS_STAT_DOESN_T_EXIST:
371                         ERROR("%s not found\n", dev);
372                         break;
373                 case OSS_STAT_NOT_CHAR_DEV:
374                         ERROR("%s is not a character device\n", dev);
375                         break;
376                 case OSS_STAT_NO_PERMS:
377                         ERROR("%s: permission denied\n", dev);
378                         break;
379                 default:
380                         ERROR("Error accessing %s: %s", dev, strerror(err[i]));
381                 }
382         }
383         exit(EXIT_FAILURE);
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;
391         if (param) {
392                 BlockParam *bp = getBlockParam(param, "device");
393                 if (bp) {
394                         od->device = bp->value;
395                         return 0;
396                 }
397         }
398         return oss_open_default(audioOutput, param, od);
401 static void oss_finishDriver(AudioOutput * audioOutput)
403         OssData *od = audioOutput->data;
405         freeOssData(od);
408 static int setParam(OssData * od, int param, int *value)
410         int val = *value;
411         int copy;
412         int supported = isSupportedParam(od, param, val);
414         do {
415                 if (supported == OSS_UNSUPPORTED) {
416                         val = getSupportedParam(od, param, val);
417                         if (copy < 0)
418                                 return -1;
419                 }
420                 copy = val;
421                 if (ioctl(od->fd, param, &copy)) {
422                         unsupportParam(od, param, val);
423                         supported = OSS_UNSUPPORTED;
424                 } else {
425                         if (supported == OSS_UNKNOWN) {
426                                 supportParam(od, param, val);
427                                 supported = OSS_SUPPORTED;
428                         }
429                         val = copy;
430                 }
431         } while (supported == OSS_UNSUPPORTED);
433         *value = val;
435         return 0;
438 static void oss_close(OssData * od)
440         if (od->fd >= 0)
441                 while (close(od->fd) && errno == EINTR) ;
442         od->fd = -1;
445 static int oss_open(AudioOutput * audioOutput)
447         int tmp;
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,
452                       strerror(errno));
453                 goto fail;
454         }
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));
459                 goto fail;
460         }
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));
465                 goto fail;
466         }
468         switch (od->bits) {
469         case 8:
470                 tmp = AFMT_S8;
471                 break;
472         case 16:
473                 tmp = AFMT_S16_MPD;
474         }
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));
479                 goto fail;
480         }
482         audioOutput->open = 1;
484         return 0;
486 fail:
487         oss_close(od);
488         audioOutput->open = 0;
489         return -1;
492 static int oss_openDevice(AudioOutput * audioOutput)
494         int ret = -1;
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)
503                 return ret;
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);
512         return ret;
515 static void oss_closeDevice(AudioOutput * audioOutput)
517         OssData *od = audioOutput->data;
519         oss_close(od);
521         audioOutput->open = 0;
524 static void oss_dropBufferedAudio(AudioOutput * audioOutput)
526         OssData *od = audioOutput->data;
528         if (od->fd >= 0) {
529                 ioctl(od->fd, SNDCTL_DSP_RESET, 0);
530                 oss_close(od);
531         }
534 static int oss_playAudio(AudioOutput * audioOutput, char *playChunk, int size)
536         OssData *od = audioOutput->data;
537         int ret;
539         /* reopen the device since it was closed by dropBufferedAudio */
540         if (od->fd < 0 && oss_open(audioOutput) < 0)
541                 return -1;
543         while (size > 0) {
544                 ret = write(od->fd, playChunk, size);
545                 if (ret < 0) {
546                         if (errno == EINTR)
547                                 continue;
548                         ERROR("closing oss device \"%s\" due to write error: "
549                               "%s\n", od->device, strerror(errno));
550                         oss_closeDevice(audioOutput);
551                         return -1;
552                 }
553                 playChunk += ret;
554                 size -= ret;
555         }
557         return 0;
560 AudioOutputPlugin ossPlugin = {
561         "oss",
562         oss_testDefault,
563         oss_initDriver,
564         oss_finishDriver,
565         oss_openDevice,
566         oss_playAudio,
567         oss_dropBufferedAudio,
568         oss_closeDevice,
569         NULL,   /* sendMetadataFunc */
572 #else /* HAVE OSS */
574 DISABLED_AUDIO_OUTPUT_PLUGIN(ossPlugin)
575 #endif /* HAVE_OSS */