chiark / gitweb /
Update grapheme break algorithm to Unicode 5.1.0 (based on UAX #29)
[disorder] / lib / uaudio-oss.c
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
53 static int oss_fd = -1;
54 static int oss_mixer_fd = -1;
55 static int oss_mixer_channel;
56
57 static const char *const oss_options[] = {
58   "device",
59   "mixer-device",
60   "mixer-channel",
61   NULL
62 };
63
64 /** @brief Actually play sound via OSS */
65 static 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 */
74 static 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
108 static void oss_activate(void) {
109   oss_open();
110   uaudio_thread_activate();
111 }
112
113 static void oss_deactivate(void) {
114   uaudio_thread_deactivate();
115   close(oss_fd);
116   oss_fd = -1;
117 }
118   
119 static 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                       0);
133 #else
134   /* We could SNDCTL_DSP_GETBLKSIZE but only when the device is already open,
135    * which is kind of inconvenient.  We go with 1-4Kbyte for now. */
136   uaudio_thread_start(callback, userdata, oss_play, 
137                       32 / uaudio_sample_size,
138                       4096 / uaudio_sample_size,
139                       0);
140 #endif
141 }
142
143 static void oss_stop(void) {
144   uaudio_thread_stop();
145 }
146
147 /** @brief Channel names */
148 static const char *oss_channels[] = SOUND_DEVICE_NAMES;
149
150 static int oss_mixer_find_channel(const char *channel) {
151   if(!channel[strspn(channel, "0123456789")])
152     return atoi(channel);
153   else {
154     for(unsigned n = 0; n < sizeof oss_channels / sizeof *oss_channels; ++n)
155       if(!strcmp(oss_channels[n], channel))
156         return n;
157     return -1;
158   }
159 }  
160
161 static void oss_open_mixer(void) {
162   const char *mixer = uaudio_get("mixer-device", "/dev/mixer");
163   /* TODO infer mixer-device from device */
164   if((oss_mixer_fd = open(mixer, O_RDWR, 0)) < 0)
165     fatal(errno, "error opening %s", mixer);
166   const char *channel = uaudio_get("mixer-channel", "pcm");
167   oss_mixer_channel = oss_mixer_find_channel(channel);
168   if(oss_mixer_channel < 0)
169     fatal(0, "no such channel as '%s'", channel);
170 }
171
172 static void oss_close_mixer(void) {
173   close(oss_mixer_fd);
174   oss_mixer_fd = -1;
175 }
176
177 static void oss_get_volume(int *left, int *right) {
178   int r;
179
180   *left = *right = 0;
181   if(ioctl(oss_mixer_fd, SOUND_MIXER_READ(oss_mixer_channel), &r) < 0)
182     error(errno, "error getting volume");
183   else {
184     *left = r & 0xff;
185     *right = (r >> 8) & 0xff;
186   }
187 }
188
189 static void oss_set_volume(int *left, int *right) {
190   int r =  (*left & 0xff) + (*right & 0xff) * 256;
191   if(ioctl(oss_mixer_fd, SOUND_MIXER_WRITE(oss_mixer_channel), &r) == -1)
192     error(errno, "error setting volume");
193   else if(ioctl(oss_mixer_fd, SOUND_MIXER_READ(oss_mixer_channel), &r) < 0)
194     error(errno, "error getting volume");
195   else {
196     *left = r & 0xff;
197     *right = (r >> 8) & 0xff;
198   }
199 }
200
201 static void oss_configure(void) {
202   uaudio_set("device", config->device);
203   uaudio_set("mixer-device", config->mixer);
204   uaudio_set("mixer-channel", config->channel);
205 }
206
207 const struct uaudio uaudio_oss = {
208   .name = "oss",
209   .options = oss_options,
210   .start = oss_start,
211   .stop = oss_stop,
212   .activate = oss_activate,
213   .deactivate = oss_deactivate,
214   .open_mixer = oss_open_mixer,
215   .close_mixer = oss_close_mixer,
216   .get_volume = oss_get_volume,
217   .set_volume = oss_set_volume,
218   .configure = oss_configure,
219 };
220
221 #endif
222
223 /*
224 Local Variables:
225 c-basic-offset:2
226 comment-column:40
227 fill-column:79
228 indent-tabs-mode:nil
229 End:
230 */