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