chiark / gitweb /
072188e7c6fdb7926598a1519500cb8d979404d4
[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 #include "uaudio.h"
38 #include "configuration.h"
39
40 static int oss_fd = -1;
41 static pthread_t oss_thread;
42 static uaudio_callback *oss_callback;
43 static int oss_started;
44 static int oss_activated;
45 static int oss_going;
46 static pthread_mutex_t oss_lock = PTHREAD_MUTEX_INITIALIZER;
47 static pthread_cond_t oss_cond = PTHREAD_COND_INITIALIZER;
48
49 /** @brief Buffer size in bytes */
50 static int oss_bufsize;
51
52 static const char *const oss_options[] = {
53   "device",
54   NULL
55 };
56
57 static void *oss_thread_fn(void *userdata) {
58   int16_t *buffer;
59   pthread_mutex_lock(&oss_lock);
60   buffer = xmalloc(oss_bufsize);
61   while(oss_started) {
62     while(oss_started && !oss_activated)
63       pthread_cond_wait(&oss_cond, &oss_lock);
64     if(!oss_started)
65       break;
66     /* We are definitely active now */
67     oss_going = 1;
68     pthread_cond_signal(&oss_cond);
69     while(oss_activated) {
70       const int nsamples = oss_callback(buffer,
71                                         oss_bufsize / sizeof(int16_t),
72                                         userdata);
73       const int nbytes = nsamples * sizeof(int16_t);
74       int written = 0;
75
76       while(written < nsamples) {
77         const int rc = write(oss_fd, buffer + written, nbytes - written);
78         if(rc < 0)
79           fatal(errno, "error playing audio");
80         written += rc;
81       }
82     }
83     oss_going = 0;
84     pthread_cond_signal(&oss_cond);
85   }
86   pthread_mutex_unlock(&oss_lock);
87   return NULL;
88 }
89
90 static void oss_activate(void) {
91   pthread_mutex_lock(&oss_lock);
92   if(!oss_activated) {
93     const char *device = uaudio_get("device");
94
95 #if EMPEG_HOST
96     if(!device || !*device || !strcmp(device, "default")) {
97       device "/dev/audio";
98 #else
99     if(!device || !*device || !strcmp(device, "default")) {
100       if(access("/dev/dsp", W_OK) == 0)
101         device = "/dev/dsp";
102       else
103         device = "/dev/audio";
104     }
105 #endif
106     if((oss_fd = open(device, O_WRONLY, 0)) < 0)
107       fatal(errno, "error opening %s", device);
108 #if EMPEG_HOST
109     /* empeg audio driver only knows /dev/audio, only supports the equivalent
110      * of AFMT_S16_NE, has a fixed buffer size, and does not support the
111      * SNDCTL_ ioctls. */
112     oss_bufsize = 4608;
113 #else
114     int stereo = (config->sample_format.channels == 2), format, rate;
115     if(ioctl(oss_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
116       fatal(errno, "error calling ioctl SNDCTL_DSP_STEREO %d", stereo);
117     /* TODO we need to think about where we decide this */
118     if(config->sample_format.bits == 8)
119       format = AFMT_U8;
120     else if(config->sample_format.bits == 16)
121       format = (config->sample_format.endian == ENDIAN_LITTLE
122                 ? AFMT_S16_LE : AFMT_S16_BE);
123     else
124       fatal(0, "unsupported sample_format for oss backend"); 
125     if(ioctl(oss_fd, SNDCTL_DSP_SETFMT, &format) < 0)
126       fatal(errno, "error calling ioctl SNDCTL_DSP_SETFMT %#x", format);
127     rate = config->sample_format.rate;
128     if(ioctl(oss_fd, SNDCTL_DSP_SPEED, &rate) < 0)
129       fatal(errno, "error calling ioctl SNDCTL_DSP_SPEED %d", rate);
130     if((unsigned)rate != config->sample_format.rate)
131       error(0, "asked for %luHz, got %dHz",
132             (unsigned long)config->sample_format.rate, rate);
133     if(ioctl(oss_fd, SNDCTL_DSP_GETBLKSIZE, &oss_bufsize) < 0) {
134       error(errno, "ioctl SNDCTL_DSP_GETBLKSIZE");
135       oss_bufsize = 2048;       /* guess */
136     }
137 #endif
138     oss_activated = 1;
139     pthread_cond_signal(&oss_cond);
140     while(!oss_going)
141       pthread_cond_wait(&oss_cond, &oss_lock);
142   }
143   pthread_mutex_unlock(&oss_lock);
144 }
145
146 static void oss_deactivate(void) {
147   pthread_mutex_lock(&oss_lock);
148   if(oss_activated) {
149     oss_activated = 0;
150     pthread_cond_signal(&oss_cond);
151     while(oss_going)
152       pthread_cond_wait(&oss_cond, &oss_lock);
153     close(oss_fd);
154     oss_fd = -1;
155   }
156   pthread_mutex_unlock(&oss_lock);
157 }
158   
159 static void oss_start(uaudio_callback *callback,
160                       void *userdata) {
161   int e;
162   oss_callback = callback;
163   if((e = pthread_create(&oss_thread,
164                          NULL,
165                          oss_thread_fn,
166                          userdata)))
167     fatal(e, "pthread_create");
168 }
169
170 static void oss_stop(void) {
171   void *result;
172
173   oss_deactivate();
174   pthread_mutex_lock(&oss_lock);
175   oss_started = 0;
176   pthread_cond_signal(&oss_cond);
177   pthread_mutex_unlock(&oss_lock);
178   pthread_join(oss_thread, &result);
179 }
180
181 const struct uaudio uaudio_oss = {
182   .name = "oss",
183   .options = oss_options,
184   .start = oss_start,
185   .stop = oss_stop,
186   .activate = oss_activate,
187   .deactivate = oss_deactivate
188 };
189
190 #endif
191
192 /*
193 Local Variables:
194 c-basic-offset:2
195 comment-column:40
196 fill-column:79
197 indent-tabs-mode:nil
198 End:
199 */