1 /* Sysdep Open Sound System sound dsp driver
2
3 Copyright 2000 Hans de Goede
4
5 This file and the acompanying files in this directory are free software;
6 you can redistribute them and/or modify them under the terms of the GNU
7 Library General Public License as published by the Free Software Foundation;
8 either version 2 of the License, or (at your option) any later version.
9
10 These files are distributed in the hope that they will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with these files; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20 /* Changelog
21 Version 0.1, January 2000
22 -initial release, based on the xmame driver done by Mike Oliphant
23 (oliphant@ling.ed.ac.uk), amongst others (Hans de Goede)
24 */
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/ioctl.h>
32 #if defined (__ARCH_openbsd)
33 #include <soundcard.h>
34 #else
35 #include <sys/soundcard.h>
36 #endif
37 #include "sysdep/sysdep_dsp.h"
38 #include "sysdep/sysdep_dsp_priv.h"
39 #include "sysdep/plugin_manager.h"
40
41 #ifdef __ARCH_openbsd
42 #define AUDIO_DEVICE "/dev/audio"
43 #else
44 #define AUDIO_DEVICE "/dev/dsp"
45 #endif
46
47 #if 1 /* QUASI88 */
48 extern int verbose_proc;
49 #define fprintf if (verbose_proc) fprintf
50 #endif /* QUASI88 */
51
52 /* our per instance private data struct */
53 struct oss_dsp_priv_data {
54 int fd;
55 };
56
57 /* public methods prototypes (static but exported through the sysdep_dsp or
58 plugin struct) */
59 static void *oss_dsp_create(const void *flags);
60 static void oss_dsp_destroy(struct sysdep_dsp_struct *dsp);
61 static int oss_dsp_get_freespace(struct sysdep_dsp_struct *dsp);
62 static int oss_dsp_write(struct sysdep_dsp_struct *dsp, unsigned char *data,
63 int count);
64
65 /* public variables */
66 const struct plugin_struct sysdep_dsp_oss = {
67 "oss",
68 "sysdep_dsp",
69 "Open Sound System DSP plugin",
70 NULL, /* no options */
71 NULL, /* no init */
72 NULL, /* no exit */
73 oss_dsp_create,
74 3 /* high priority */
75 };
76
77 /* private variables */
78 static int oss_dsp_bytes_per_sample[4] = SYSDEP_DSP_BYTES_PER_SAMPLE;
79
80 /* public methods (static but exported through the sysdep_dsp or plugin
81 struct) */
oss_dsp_create(const void * flags)82 static void *oss_dsp_create(const void *flags)
83 {
84 int i, j;
85 audio_buf_info info;
86 struct oss_dsp_priv_data *priv = NULL;
87 struct sysdep_dsp_struct *dsp = NULL;
88 const struct sysdep_dsp_create_params *params = flags;
89 const char *device = params->device;
90
91 /* allocate the dsp struct */
92 if (!(dsp = calloc(1, sizeof(struct sysdep_dsp_struct))))
93 {
94 fprintf(stderr,
95 "error malloc failed for struct sysdep_dsp_struct\n");
96 return NULL;
97 }
98
99 /* alloc private data */
100 if (!(priv = calloc(1, sizeof(struct oss_dsp_priv_data))))
101 {
102 fprintf(stderr,
103 "error malloc failed for struct oss_dsp_priv_data\n");
104 oss_dsp_destroy(dsp);
105 return NULL;
106 }
107
108 /* fill in the functions and some data */
109 priv->fd = -1;
110 dsp->_priv = priv;
111 dsp->get_freespace = oss_dsp_get_freespace;
112 dsp->write = oss_dsp_write;
113 dsp->destroy = oss_dsp_destroy;
114 dsp->hw_info.type = params->type;
115 dsp->hw_info.samplerate = params->samplerate;
116
117 /* open the sound device */
118 if (!device)
119 device = AUDIO_DEVICE;
120
121 /* always open in non-blocking mode, otherwise the open itself may
122 block, hanging the entire application */
123 if ((priv->fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) {
124 perror("error: " AUDIO_DEVICE);
125 oss_dsp_destroy(dsp);
126 return NULL;
127 }
128
129 /* set back to blocking mode, unless non-blocking mode was selected */
130 if (!(params->flags & SYSDEP_DSP_O_NONBLOCK))
131 {
132 if (fcntl(priv->fd, F_SETFL, O_WRONLY) < 0)
133 {
134 perror("OSS-driver, error: fnctl");
135 oss_dsp_destroy(dsp);
136 return NULL;
137 }
138 }
139
140 /* set the number of bits */
141 #ifdef LSB_FIRST
142 i = j = (dsp->hw_info.type & SYSDEP_DSP_16BIT) ? AFMT_S16_LE : AFMT_U8;
143 #else
144 i = j = (dsp->hw_info.type & SYSDEP_DSP_16BIT) ? AFMT_S16_BE : AFMT_U8;
145 #endif
146
147 if (ioctl(priv->fd, SNDCTL_DSP_SETFMT, &i) < 0)
148 {
149 perror("error: SNDCTL_DSP_SETFMT");
150 oss_dsp_destroy(dsp);
151 return NULL;
152 }
153
154 if (i != j)
155 {
156 if (dsp->hw_info.type & SYSDEP_DSP_16BIT)
157 {
158 fprintf(stderr, "warning: couldn't set sound to 16 bits,\n"
159 " trying again with 8 bits: ");
160 }
161 else
162 {
163 fprintf(stderr, "error: couldn't set sound to 8 bits,\n");
164 oss_dsp_destroy(dsp);
165 return NULL;
166 }
167
168 dsp->hw_info.type &= ~SYSDEP_DSP_16BIT;
169 i = AFMT_U8;
170 if (ioctl(priv->fd, SNDCTL_DSP_SETFMT, &i) < 0)
171 {
172 perror("error: SNDCTL_DSP_SETFMT");
173 oss_dsp_destroy(dsp);
174 return NULL;
175 }
176
177 if (i != AFMT_U8)
178 {
179 fprintf(stderr, "failed\n");
180 oss_dsp_destroy(dsp);
181 return NULL;
182 }
183 fprintf(stderr, "success\n");
184 }
185
186 /* set the number of channels */
187 i = (dsp->hw_info.type & SYSDEP_DSP_STEREO) ? 1 : 0;
188 if (ioctl(priv->fd, SNDCTL_DSP_STEREO, &i) < 0)
189 {
190 perror("error: SNDCTL_DSP_STEREO");
191 oss_dsp_destroy(dsp);
192 return NULL;
193 }
194
195 if (i)
196 dsp->hw_info.type |= SYSDEP_DSP_STEREO;
197 else
198 dsp->hw_info.type &= ~SYSDEP_DSP_STEREO;
199
200 /* set the samplerate */
201 if (ioctl(priv->fd, SNDCTL_DSP_SPEED, &dsp->hw_info.samplerate) < 0)
202 {
203 perror("error: SNDCTL_DSP_SPEED");
204 oss_dsp_destroy(dsp);
205 return NULL;
206 }
207
208 /* calculate and set the fragsize & number of frags */
209 /* fragsize (as power of 2) */
210 i = 7;
211 if (dsp->hw_info.type & SYSDEP_DSP_16BIT) i++;
212 if (dsp->hw_info.type & SYSDEP_DSP_STEREO) i++;
213 i += dsp->hw_info.samplerate / 22000;
214
215 /* number of frags */
216 j = ((dsp->hw_info.samplerate * oss_dsp_bytes_per_sample[dsp->hw_info.type] *
217 params->bufsize) / (0x01 << i)) + 1;
218
219 /* set the fraginfo */
220 i = j = i | (j << 16);
221 fprintf(stderr, "info: setting fragsize to %d, numfrags to %d\n",
222 1 << (i & 0x0000FFFF), i >> 16);
223 if (ioctl(priv->fd, SNDCTL_DSP_SETFRAGMENT, &i) < 0)
224 {
225 perror("error: SNDCTL_DSP_SETFRAGMENT");
226 oss_dsp_destroy(dsp);
227 return NULL;
228 }
229
230 if (ioctl(priv->fd, SNDCTL_DSP_GETOSPACE, &info) < 0)
231 {
232 perror("warning: SNDCTL_DSP_GETOSPACE");
233 fprintf(stderr, " falling back to timer based-audio\n");
234 dsp->get_freespace = NULL;
235 }
236 else
237 {
238 fprintf(stderr, "info: fragsize = %d, numfrags = %d\n", info.fragsize,
239 info.fragstotal);
240 /* i = requested fragsize, j = requested numfrags */
241 i = 1 << (j & 0x0000FFFF);
242 j = j >> 16;
243 if ((info.fragsize < (i / 2)) || (info.fragsize > (i * 2)) ||
244 (info.fragstotal < (j - 2)) || (info.fragstotal > (j + 2)))
245 {
246 fprintf(stderr, "warning: obtained fragsize/numfrags differs too much from requested\n"
247 " you may wish to adjust the bufsize setting in your xmamerc file, or try\n"
248 " timer-based audio by adding -timer to your command line\n");
249 }
250 else if (info.bytes > (info.fragsize * info.fragstotal))
251 {
252 fprintf(stderr, "warning: freespace > (fragsize * numfrags) assuming buggy FreeBSD PCM driver,\n"
253 " falling back to timer based-audio\n");
254 dsp->get_freespace = NULL;
255 }
256 else
257 /* info.fragstotal + 1 to work around yet more OSS bugs ;( */
258 dsp->hw_info.bufsize = (info.fragsize * (info.fragstotal + 1)) /
259 oss_dsp_bytes_per_sample[dsp->hw_info.type];
260 }
261
262 fprintf(stderr, "info: audiodevice %s set to %dbit linear %s %dHz\n",
263 device, (dsp->hw_info.type & SYSDEP_DSP_16BIT)? 16:8,
264 (dsp->hw_info.type & SYSDEP_DSP_STEREO)? "stereo":"mono",
265 dsp->hw_info.samplerate);
266
267 return dsp;
268 }
269
oss_dsp_destroy(struct sysdep_dsp_struct * dsp)270 static void oss_dsp_destroy(struct sysdep_dsp_struct *dsp)
271 {
272 struct oss_dsp_priv_data *priv = dsp->_priv;
273
274 if (priv)
275 {
276 if (priv->fd >= 0)
277 close(priv->fd);
278
279 free(priv);
280 }
281 free(dsp);
282 }
283
oss_dsp_get_freespace(struct sysdep_dsp_struct * dsp)284 static int oss_dsp_get_freespace(struct sysdep_dsp_struct *dsp)
285 {
286 audio_buf_info info;
287 struct oss_dsp_priv_data *priv = dsp->_priv;
288
289 if (ioctl(priv->fd, SNDCTL_DSP_GETOSPACE, &info) < 0)
290 {
291 perror("error: SNDCTL_DSP_GETOSPACE");
292 return -1;
293 }
294 return info.bytes / oss_dsp_bytes_per_sample[dsp->hw_info.type];
295 }
296
oss_dsp_write(struct sysdep_dsp_struct * dsp,unsigned char * data,int count)297 static int oss_dsp_write(struct sysdep_dsp_struct *dsp, unsigned char *data,
298 int count)
299 {
300 int result;
301 struct oss_dsp_priv_data *priv = dsp->_priv;
302
303 result = write(priv->fd, data, count *
304 oss_dsp_bytes_per_sample[dsp->hw_info.type]);
305
306 if (result < 0)
307 return -1;
308
309 return result / oss_dsp_bytes_per_sample[dsp->hw_info.type];
310 }
311