chiark / gitweb /
06ffb73e5cdc64fe58f079138d858b3a8ff8d627
[disorder] / lib / uaudio-oss.c
1 /*
2  * This file is part of DisOrder.
3  * Copyright (C) 2009 Richard Kettlewell
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 /** @file lib/uaudio-oss.c
19  * @brief Support for OSS backend */
20 #include "common.h"
21
22 #if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
23
24 #if HAVE_SYS_SOUNDCARD_H
25 # include <sys/soundcard.h>
26 #endif
27 #include <sys/ioctl.h>
28 #include <pthread.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <arpa/inet.h>
33
34 #include "mem.h"
35 #include "log.h"
36 #include "syscalls.h"
37
38 static int oss_fd = -1;
39 static pthread_t oss_thread;
40 static uaudio_callback *oss_callback;
41 static int oss_started;
42 static int oss_activated;
43 static int oss_going;
44 static pthread_mutex_t oss_lock = PTHREAD_MUTEX_INITIALIZER;
45 static pthread_cond_t oss_cond = PTHREAD_COND_INITIALIZER;
46 static int oss_bufsize;
47
48 static const char *const oss_options[] = {
49   "device",
50   NULL
51 };
52
53 static void *oss_thread(void *userdata) {
54   int16_t *buffer;
55   pthread_mutex_lock(&oss_lock);
56   buffer = xmalloc(XXX);
57   while(oss_started) {
58     while(oss_started && !oss_activated)
59       pthread_cond_wait(&oss_cond, &oss_lock);
60     if(!oss_started)
61       break;
62     /* We are definitely active now */
63     oss_going = 1;
64     pthread_cond_signal(&oss_cond);
65     while(oss_activated) {
66       const int nsamples = oss_callback(buffer, oss_bufsizeXXX, userdata);;
67       if(write(oss_fd, buffer, XXX) < 0)
68         fatal(errno, "error playing audio");
69       // TODO short writes!
70     }
71     oss_going = 0;
72     pthread_cond_signal(&oss_cond);
73   }
74   pthread_mutex_unlock(&oss_lock);
75   return NULL;
76 }
77
78 static void oss_start(uaudio_callback *callback,
79                       void *userdata) {
80   int e;
81   oss_callback = callback;
82   if((e = pthread_create(&oss_thread,
83                          NULL,
84                          oss_thread_fn,
85                          userdata)))
86     fatal(e, "pthread_create");
87 }
88
89 static void oss_stop(void) {
90   void *result;
91
92   oss_deactivate();
93   pthread_mutex_lock(&oss_lock);
94   oss_started = 0;
95   pthread_cond_signal(&oss_cond);
96   pthread_mutex_unlock(&oss_lock);
97   pthread_join(oss_thread, &result);
98 }
99
100 static void oss_activate(void) {
101   pthread_mutex_lock(&oss_lock);
102   if(!oss_activated) {
103     const char *device = uaudio_get(&uadiuo_oss, "device");
104
105 #if EMPEG_HOST
106     if(!device || !*device || !strcmp(device, "default")) {
107       device "/dev/audio";
108 #else
109     if(!device || !*device || !strcmp(device, "default")) {
110       if(access("/dev/dsp", W_OK) == 0)
111         device = "/dev/dsp";
112       else
113         device = "/dev/audio";
114     }
115 #endif
116     if((oss_fd = open(device, O_WRONLY, 0)) < 0)
117       fatal(errno, "error opening %s", device);
118 #if EMPEG_HOST
119     /* empeg audio driver only knows /dev/audio, only supports the equivalent
120      * of AFMT_S16_NE, has a fixed buffer size, and does not support the
121      * SNDCTL_ ioctls. */
122     oss_bufsize = 4608;
123 #else
124     int stereo = (config->sample_format.channels == 2), format, rate;
125     if(ioctl(ossfd, SNDCTL_DSP_STEREO, &stereo) < 0) {
126       error(errno, "error calling ioctl SNDCTL_DSP_STEREO %d", stereo);
127       goto failed;
128     }
129     /* TODO we need to think about where we decide this */
130     if(config->sample_format.bits == 8)
131       format = AFMT_U8;
132     else if(config->sample_format.bits == 16)
133       format = (config->sample_format.endian == ENDIAN_LITTLE
134                 ? AFMT_S16_LE : AFMT_S16_BE);
135     else
136       fatal(0, "unsupported sample_format for oss backend"); 
137     if(ioctl(oss_fd, SNDCTL_DSP_SETFMT, &format) < 0)
138       fatal(errno, "error calling ioctl SNDCTL_DSP_SETFMT %#x", format);
139     rate = config->sample_format.rate;
140     if(ioctl(oss_fd, SNDCTL_DSP_SPEED, &rate) < 0)
141       fatal(errno, "error calling ioctl SNDCTL_DSP_SPEED %d", rate);
142     if((unsigned)rate != config->sample_format.rate)
143       error(0, "asked for %luHz, got %dHz",
144             (unsigned long)config->sample_format.rate, rate);
145     if(ioctl(oss_fd, SNDCTL_DSP_GETBLKSIZE, &oss_bufsize) < 0) {
146       error(errno, "ioctl SNDCTL_DSP_GETBLKSIZE");
147       oss_bufsize = 2048;       /* guess */
148     }
149 #endif
150     oss_activated = 1;
151     pthread_cond_signal(&oss_cond);
152     while(!oss_going)
153       pthread_cond_wait(&oss_cond, &oss_lock);
154   }
155   pthread_mutex_unlock(&oss_lock);
156 }
157
158 static void oss_deactivate(void) {
159   pthread_mutex_lock(&oss_lock);
160   if(oss_activated) {
161     oss_activated = 0;
162     pthread_cond_signal(&oss_cond);
163     while(oss_going)
164       pthread_cond_wait(&oss_cond, &oss_lock);
165     close(oss_fd);
166     oss_fd = -1;
167   }
168   pthread_mutex_unlock(&oss_lock);
169 }
170
171 const struct uaudio uaudio_oss = {
172   .name = "oss",
173   .options = oss_options,
174   .start = oss_start,
175   .stop = oss_stop,
176   .activate = oss_activate,
177   .deactivate = oss_deactivate
178 };
179
180 #endif
181
182 /*
183 Local Variables:
184 c-basic-offset:2
185 comment-column:40
186 fill-column:79
187 indent-tabs-mode:nil
188 End:
189 */