uaudio OSS support now compiles
[disorder] / lib / uaudio-oss.c
CommitLineData
7a2c7068
RK
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"
b60e20be
RK
37#include "uaudio.h"
38#include "configuration.h"
7a2c7068
RK
39
40static int oss_fd = -1;
41static pthread_t oss_thread;
42static uaudio_callback *oss_callback;
43static int oss_started;
44static int oss_activated;
45static int oss_going;
46static pthread_mutex_t oss_lock = PTHREAD_MUTEX_INITIALIZER;
47static pthread_cond_t oss_cond = PTHREAD_COND_INITIALIZER;
b60e20be
RK
48
49/** @brief Buffer size in bytes */
7a2c7068
RK
50static int oss_bufsize;
51
52static const char *const oss_options[] = {
53 "device",
54 NULL
55};
56
b60e20be 57static void *oss_thread_fn(void *userdata) {
7a2c7068
RK
58 int16_t *buffer;
59 pthread_mutex_lock(&oss_lock);
b60e20be 60 buffer = xmalloc(oss_bufsize);
7a2c7068
RK
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) {
b60e20be
RK
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 }
7a2c7068
RK
82 }
83 oss_going = 0;
84 pthread_cond_signal(&oss_cond);
85 }
86 pthread_mutex_unlock(&oss_lock);
87 return NULL;
88}
89
7a2c7068
RK
90static void oss_activate(void) {
91 pthread_mutex_lock(&oss_lock);
92 if(!oss_activated) {
b60e20be 93 const char *device = uaudio_get("device");
7a2c7068
RK
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;
b60e20be
RK
115 if(ioctl(oss_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
116 fatal(errno, "error calling ioctl SNDCTL_DSP_STEREO %d", stereo);
7a2c7068
RK
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
146static 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}
b60e20be
RK
158
159static 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
170static 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}
7a2c7068
RK
180
181const 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/*
193Local Variables:
194c-basic-offset:2
195comment-column:40
196fill-column:79
197indent-tabs-mode:nil
198End:
199*/