chiark / gitweb /
Uniform audio backend for Core Audio. There is an untested OSS one
[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"
37
38static int oss_fd = -1;
39static pthread_t oss_thread;
40static uaudio_callback *oss_callback;
41static int oss_started;
42static int oss_activated;
43static int oss_going;
44static pthread_mutex_t oss_lock = PTHREAD_MUTEX_INITIALIZER;
45static pthread_cond_t oss_cond = PTHREAD_COND_INITIALIZER;
46static int oss_bufsize;
47
48static const char *const oss_options[] = {
49 "device",
50 NULL
51};
52
53static 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
78static 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
89static 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
100static 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
158static 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
171const 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/*
183Local Variables:
184c-basic-offset:2
185comment-column:40
186fill-column:79
187indent-tabs-mode:nil
188End:
189*/