chiark / gitweb /
Build fixes for uaudio ALSA/OSS backends.
[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>
7a2c7068
RK
28#include <fcntl.h>
29#include <unistd.h>
30#include <errno.h>
7a2c7068
RK
31
32#include "mem.h"
33#include "log.h"
b60e20be 34#include "uaudio.h"
7a2c7068 35
4fd38868
RK
36#ifndef AFMT_U16_NE
37# if BYTE_ORDER == BIG_ENDIAN
38# define AFMT_U16_NE AFMT_U16_BE
39# else
40# define AFMT_U16_NE AFMT_U16_LE
41# endif
42#endif
b60e20be 43
b50cfb8a
RK
44/* documentation does not match implementation! */
45#ifndef SOUND_MIXER_READ
46# define SOUND_MIXER_READ(x) MIXER_READ(x)
47#endif
48#ifndef SOUND_MIXER_WRITE
49# define SOUND_MIXER_WRITE(x) MIXER_WRITE(x)
50#endif
51
4fd38868 52static int oss_fd = -1;
b50cfb8a
RK
53static int oss_mixer_fd = -1;
54static int oss_mixer_channel;
7a2c7068
RK
55
56static const char *const oss_options[] = {
57 "device",
b50cfb8a
RK
58 "mixer-device",
59 "mixer-channel",
7a2c7068
RK
60 NULL
61};
62
4fd38868
RK
63/** @brief Actually play sound via OSS */
64static size_t oss_play(void *buffer, size_t samples) {
65 const size_t bytes = samples * uaudio_sample_size;
66 int rc = write(oss_fd, buffer, bytes);
67 if(rc < 0)
68 fatal(errno, "error writing to sound device");
69 return rc / uaudio_sample_size;
7a2c7068
RK
70}
71
4fd38868
RK
72/** @brief Open the OSS sound device */
73static void oss_open(void) {
b50cfb8a 74 const char *device = uaudio_get("device", NULL);
7a2c7068
RK
75
76#if EMPEG_HOST
4fd38868
RK
77 if(!device || !*device || !strcmp(device, "default"))
78 device "/dev/audio";
7a2c7068 79#else
4fd38868
RK
80 if(!device || !*device || !strcmp(device, "default")) {
81 if(access("/dev/dsp", W_OK) == 0)
82 device = "/dev/dsp";
7a2c7068 83 else
4fd38868 84 device = "/dev/audio";
7a2c7068 85 }
4fd38868
RK
86#endif
87 if((oss_fd = open(device, O_WRONLY, 0)) < 0)
88 fatal(errno, "error opening %s", device);
89#if !EMPEG_HOST
90 int stereo = (uaudio_channels == 2), format;
91 if(ioctl(oss_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
92 fatal(errno, "error calling ioctl SNDCTL_DSP_STEREO %d", stereo);
93 if(uaudio_bits == 16)
94 format = uaudio_signed ? AFMT_S16_NE : AFMT_U16_NE;
95 else
96 format = uaudio_signed ? AFMT_S8 : AFMT_U8;
97 if(ioctl(oss_fd, SNDCTL_DSP_SETFMT, &format) < 0)
98 fatal(errno, "error calling ioctl SNDCTL_DSP_SETFMT %#x", format);
99 int rate = uaudio_rate;
100 if(ioctl(oss_fd, SNDCTL_DSP_SPEED, &rate) < 0)
101 fatal(errno, "error calling ioctl SNDCTL_DSP_SPEED %d", rate);
102 if(rate != uaudio_rate)
103 error(0, "asked for %dHz, got %dHz", uaudio_rate, rate);
104#endif
105}
106
107static void oss_activate(void) {
108 oss_open();
109 uaudio_thread_activate();
7a2c7068
RK
110}
111
112static void oss_deactivate(void) {
4fd38868
RK
113 uaudio_thread_deactivate();
114 close(oss_fd);
115 oss_fd = -1;
7a2c7068 116}
b60e20be
RK
117
118static void oss_start(uaudio_callback *callback,
119 void *userdata) {
4fd38868
RK
120 if(uaudio_channels != 1 && uaudio_channels != 2)
121 fatal(0, "asked for %d channels but only support 1 or 2",
122 uaudio_channels);
123 if(uaudio_bits != 8 && uaudio_bits != 16)
124 fatal(0, "asked for %d bits/channel but only support 8 or 16",
125 uaudio_bits);
126#if EMPEG_HOST
127 /* Very specific buffer size requirements here apparently */
128 uaudio_thread_start(callback, userdata, oss_play,
129 4608 / uaudio_sample_size,
130 4608 / uaudio_sample_size);
131#else
132 /* We could SNDCTL_DSP_GETBLKSIZE but only when the device is already open,
133 * which is kind of inconvenient. We go with 1-4Kbyte for now. */
134 uaudio_thread_start(callback, userdata, oss_play,
135 32 / uaudio_sample_size,
136 4096 / uaudio_sample_size);
137#endif
b60e20be
RK
138}
139
140static void oss_stop(void) {
4fd38868 141 uaudio_thread_stop();
b60e20be 142}
7a2c7068 143
b50cfb8a
RK
144/** @brief Channel names */
145static const char *oss_channels[] = SOUND_DEVICE_NAMES;
146
147static int oss_mixer_find_channel(const char *channel) {
42f738c2 148 if(!channel[strspn(channel, "0123456789")])
b50cfb8a
RK
149 return atoi(channel);
150 else {
42f738c2
RK
151 for(unsigned n = 0; n < sizeof oss_channels / sizeof *oss_channels; ++n)
152 if(!strcmp(oss_channels[n], channel))
b50cfb8a
RK
153 return n;
154 return -1;
155 }
156}
157
158static void oss_open_mixer(void) {
159 const char *mixer = uaudio_get("mixer-device", "/dev/mixer");
160 /* TODO infer mixer-device from device */
161 if((oss_mixer_fd = open(mixer, O_RDWR, 0)) < 0)
162 fatal(errno, "error opening %s", mixer);
163 const char *channel = uaudio_get("mixer-channel", "pcm");
164 oss_mixer_channel = oss_mixer_find_channel(channel);
165 if(oss_mixer_channel < 0)
166 fatal(0, "no such channel as '%s'", channel);
167}
168
169static void oss_close_mixer(void) {
170 close(oss_mixer_fd);
171 oss_mixer_fd = -1;
172}
173
174static void oss_get_volume(int *left, int *right) {
175 int r;
176
177 *left = *right = 0;
42f738c2 178 if(ioctl(oss_mixer_fd, SOUND_MIXER_READ(oss_mixer_channel), &r) < 0)
b50cfb8a
RK
179 error(errno, "error getting volume");
180 else {
181 *left = r & 0xff;
182 *right = (r >> 8) & 0xff;
183 }
184}
185
186static void oss_set_volume(int *left, int *right) {
187 int r = (*left & 0xff) + (*right & 0xff) * 256;
42f738c2 188 if(ioctl(oss_mixer_fd, SOUND_MIXER_WRITE(oss_mixer_channel), &r) == -1)
b50cfb8a 189 error(errno, "error setting volume");
42f738c2 190 else if(ioctl(oss_mixer_fd, SOUND_MIXER_READ(oss_mixer_channel), &r) < 0)
b50cfb8a
RK
191 error(errno, "error getting volume");
192 else {
193 *left = r & 0xff;
194 *right = (r >> 8) & 0xff;
195 }
196}
197
7a2c7068
RK
198const struct uaudio uaudio_oss = {
199 .name = "oss",
200 .options = oss_options,
201 .start = oss_start,
202 .stop = oss_stop,
203 .activate = oss_activate,
b50cfb8a
RK
204 .deactivate = oss_deactivate,
205 .open_mixer = oss_open_mixer,
206 .close_mixer = oss_close_mixer,
207 .get_volume = oss_get_volume,
208 .set_volume = oss_set_volume,
7a2c7068
RK
209};
210
211#endif
212
213/*
214Local Variables:
215c-basic-offset:2
216comment-column:40
217fill-column:79
218indent-tabs-mode:nil
219End:
220*/