2 * This file is part of DisOrder.
3 * Copyright (C) 2009 Richard Kettlewell
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.
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.
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/>.
18 /** @file lib/uaudio-oss.c
19 * @brief Support for OSS backend */
22 #if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
24 #if HAVE_SYS_SOUNDCARD_H
25 # include <sys/soundcard.h>
27 #include <sys/ioctl.h>
36 #include "configuration.h"
39 # if BYTE_ORDER == BIG_ENDIAN
40 # define AFMT_U16_NE AFMT_U16_BE
42 # define AFMT_U16_NE AFMT_U16_LE
46 /* documentation does not match implementation! */
47 #ifndef SOUND_MIXER_READ
48 # define SOUND_MIXER_READ(x) MIXER_READ(x)
50 #ifndef SOUND_MIXER_WRITE
51 # define SOUND_MIXER_WRITE(x) MIXER_WRITE(x)
54 static int oss_fd
= -1;
55 static int oss_mixer_fd
= -1;
56 static int oss_mixer_channel
;
58 static const char *const oss_options
[] = {
65 /** @brief Open the OSS sound device */
66 static void oss_open(void) {
67 const char *device
= uaudio_get("device", NULL
);
70 if(!device
|| !*device
|| !strcmp(device
, "default"))
71 device
= "/dev/audio";
73 if(!device
|| !*device
|| !strcmp(device
, "default")) {
74 if(access("/dev/dsp", W_OK
) == 0)
77 device
= "/dev/audio";
80 if((oss_fd
= open(device
, O_WRONLY
, 0)) < 0)
81 disorder_fatal(errno
, "error opening %s", device
);
83 int stereo
= (uaudio_channels
== 2), format
;
84 if(ioctl(oss_fd
, SNDCTL_DSP_STEREO
, &stereo
) < 0)
85 disorder_fatal(errno
, "error calling ioctl SNDCTL_DSP_STEREO %d", stereo
);
87 format
= uaudio_signed ? AFMT_S16_NE
: AFMT_U16_NE
;
89 format
= uaudio_signed ? AFMT_S8
: AFMT_U8
;
90 if(ioctl(oss_fd
, SNDCTL_DSP_SETFMT
, &format
) < 0)
91 disorder_fatal(errno
, "error calling ioctl SNDCTL_DSP_SETFMT %#x", format
);
92 int rate
= uaudio_rate
;
93 if(ioctl(oss_fd
, SNDCTL_DSP_SPEED
, &rate
) < 0)
94 disorder_fatal(errno
, "error calling ioctl SNDCTL_DSP_SPEED %d", rate
);
95 if(rate
!= uaudio_rate
)
96 disorder_error(0, "asked for %dHz, got %dHz", uaudio_rate
, rate
);
100 /** @brief Close the OSS sound device */
101 static void oss_close(void) {
108 /** @brief Actually play sound via OSS */
109 static size_t oss_play(void *buffer
, size_t samples
, unsigned flags
) {
110 /* cf uaudio-alsa.c:alsa-play() */
111 if(flags
& UAUDIO_PAUSED
) {
112 if(flags
& UAUDIO_PAUSE
)
116 const uint64_t ns
= ((uint64_t)samples
* 1000000000
117 / (uaudio_rate
* uaudio_channels
));
118 struct timespec ts
[1];
119 ts
->tv_sec
= ns
/ 1000000000;
120 ts
->tv_nsec
= ns
% 1000000000;
121 while(nanosleep(ts
, ts
) < 0 && errno
== EINTR
)
125 if(flags
& UAUDIO_RESUME
)
127 const size_t bytes
= samples
* uaudio_sample_size
;
128 int rc
= write(oss_fd
, buffer
, bytes
);
130 disorder_fatal(errno
, "error writing to sound device");
131 return rc
/ uaudio_sample_size
;
134 static void oss_start(uaudio_callback
*callback
,
136 if(uaudio_channels
!= 1 && uaudio_channels
!= 2)
137 disorder_fatal(0, "asked for %d channels but only support 1 or 2",
139 if(uaudio_bits
!= 8 && uaudio_bits
!= 16)
140 disorder_fatal(0, "asked for %d bits/channel but only support 8 or 16",
143 /* Very specific buffer size requirements here apparently */
144 uaudio_thread_start(callback
, userdata
, oss_play
,
145 4608 / uaudio_sample_size
,
146 4608 / uaudio_sample_size
,
149 /* We could SNDCTL_DSP_GETBLKSIZE but only when the device is already open,
150 * which is kind of inconvenient. We go with 1-4Kbyte for now. */
151 uaudio_thread_start(callback
, userdata
, oss_play
,
152 32 / uaudio_sample_size
,
153 4096 / uaudio_sample_size
,
158 static void oss_stop(void) {
159 uaudio_thread_stop();
160 oss_close(); /* might not have been paused */
163 /** @brief Channel names */
164 static const char *oss_channels
[] = SOUND_DEVICE_NAMES
;
166 static int oss_mixer_find_channel(const char *channel
) {
167 if(!channel
[strspn(channel
, "0123456789")])
168 return atoi(channel
);
170 for(unsigned n
= 0; n
< sizeof oss_channels
/ sizeof *oss_channels
; ++n
)
171 if(!strcmp(oss_channels
[n
], channel
))
177 static void oss_open_mixer(void) {
178 const char *mixer
= uaudio_get("mixer-device", "/dev/mixer");
179 /* TODO infer mixer-device from device */
180 if((oss_mixer_fd
= open(mixer
, O_RDWR
, 0)) < 0)
181 disorder_fatal(errno
, "error opening %s", mixer
);
182 const char *channel
= uaudio_get("mixer-channel", "pcm");
183 oss_mixer_channel
= oss_mixer_find_channel(channel
);
184 if(oss_mixer_channel
< 0)
185 disorder_fatal(0, "no such channel as '%s'", channel
);
188 static void oss_close_mixer(void) {
193 static void oss_get_volume(int *left
, int *right
) {
197 if(ioctl(oss_mixer_fd
, SOUND_MIXER_READ(oss_mixer_channel
), &r
) < 0)
198 disorder_error(errno
, "error getting volume");
201 *right
= (r
>> 8) & 0xff;
205 static void oss_set_volume(int *left
, int *right
) {
206 int r
= (*left
& 0xff) + (*right
& 0xff) * 256;
207 if(ioctl(oss_mixer_fd
, SOUND_MIXER_WRITE(oss_mixer_channel
), &r
) == -1)
208 disorder_error(errno
, "error setting volume");
209 else if(ioctl(oss_mixer_fd
, SOUND_MIXER_READ(oss_mixer_channel
), &r
) < 0)
210 disorder_error(errno
, "error getting volume");
213 *right
= (r
>> 8) & 0xff;
217 static void oss_configure(void) {
218 uaudio_set("device", config
->device
);
219 uaudio_set("mixer-device", config
->mixer
);
220 uaudio_set("mixer-channel", config
->channel
);
223 const struct uaudio uaudio_oss
= {
225 .options
= oss_options
,
228 .activate
= uaudio_thread_activate
,
229 .deactivate
= uaudio_thread_deactivate
,
230 .open_mixer
= oss_open_mixer
,
231 .close_mixer
= oss_close_mixer
,
232 .get_volume
= oss_get_volume
,
233 .set_volume
= oss_set_volume
,
234 .configure
= oss_configure
,