1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21
22 #include "../../SDL_internal.h"
23
24 #if SDL_AUDIO_DRIVER_SNDIO
25
26 /* OpenBSD sndio target */
27
28 #if HAVE_STDIO_H
29 #include <stdio.h>
30 #endif
31
32 #ifdef HAVE_SIGNAL_H
33 #include <signal.h>
34 #endif
35
36 #include <unistd.h>
37
38 #include "SDL_audio.h"
39 #include "../SDL_audio_c.h"
40 #include "SDL_sndioaudio.h"
41
42 #ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
43 #include "SDL_loadso.h"
44 #endif
45
46 static struct sio_hdl * (*SNDIO_sio_open)(const char *, unsigned int, int);
47 static void (*SNDIO_sio_close)(struct sio_hdl *);
48 static int (*SNDIO_sio_setpar)(struct sio_hdl *, struct sio_par *);
49 static int (*SNDIO_sio_getpar)(struct sio_hdl *, struct sio_par *);
50 static int (*SNDIO_sio_start)(struct sio_hdl *);
51 static int (*SNDIO_sio_stop)(struct sio_hdl *);
52 static size_t (*SNDIO_sio_read)(struct sio_hdl *, void *, size_t);
53 static size_t (*SNDIO_sio_write)(struct sio_hdl *, const void *, size_t);
54 static void (*SNDIO_sio_initpar)(struct sio_par *);
55
56 #ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
57 static const char *sndio_library = SDL_AUDIO_DRIVER_SNDIO_DYNAMIC;
58 static void *sndio_handle = NULL;
59
60 static int
load_sndio_sym(const char * fn,void ** addr)61 load_sndio_sym(const char *fn, void **addr)
62 {
63 *addr = SDL_LoadFunction(sndio_handle, fn);
64 if (*addr == NULL) {
65 /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
66 return 0;
67 }
68
69 return 1;
70 }
71
72 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
73 #define SDL_SNDIO_SYM(x) \
74 if (!load_sndio_sym(#x, (void **) (char *) &SNDIO_##x)) return -1
75 #else
76 #define SDL_SNDIO_SYM(x) SNDIO_##x = x
77 #endif
78
79 static int
load_sndio_syms(void)80 load_sndio_syms(void)
81 {
82 SDL_SNDIO_SYM(sio_open);
83 SDL_SNDIO_SYM(sio_close);
84 SDL_SNDIO_SYM(sio_setpar);
85 SDL_SNDIO_SYM(sio_getpar);
86 SDL_SNDIO_SYM(sio_start);
87 SDL_SNDIO_SYM(sio_stop);
88 SDL_SNDIO_SYM(sio_read);
89 SDL_SNDIO_SYM(sio_write);
90 SDL_SNDIO_SYM(sio_initpar);
91 return 0;
92 }
93
94 #undef SDL_SNDIO_SYM
95
96 #ifdef SDL_AUDIO_DRIVER_SNDIO_DYNAMIC
97
98 static void
UnloadSNDIOLibrary(void)99 UnloadSNDIOLibrary(void)
100 {
101 if (sndio_handle != NULL) {
102 SDL_UnloadObject(sndio_handle);
103 sndio_handle = NULL;
104 }
105 }
106
107 static int
LoadSNDIOLibrary(void)108 LoadSNDIOLibrary(void)
109 {
110 int retval = 0;
111 if (sndio_handle == NULL) {
112 sndio_handle = SDL_LoadObject(sndio_library);
113 if (sndio_handle == NULL) {
114 retval = -1;
115 /* Don't call SDL_SetError(): SDL_LoadObject already did. */
116 } else {
117 retval = load_sndio_syms();
118 if (retval < 0) {
119 UnloadSNDIOLibrary();
120 }
121 }
122 }
123 return retval;
124 }
125
126 #else
127
128 static void
UnloadSNDIOLibrary(void)129 UnloadSNDIOLibrary(void)
130 {
131 }
132
133 static int
LoadSNDIOLibrary(void)134 LoadSNDIOLibrary(void)
135 {
136 load_sndio_syms();
137 return 0;
138 }
139
140 #endif /* SDL_AUDIO_DRIVER_SNDIO_DYNAMIC */
141
142
143
144
145 static void
SNDIO_WaitDevice(_THIS)146 SNDIO_WaitDevice(_THIS)
147 {
148 /* no-op; SNDIO_sio_write() blocks if necessary. */
149 }
150
151 static void
SNDIO_PlayDevice(_THIS)152 SNDIO_PlayDevice(_THIS)
153 {
154 const int written = SNDIO_sio_write(this->hidden->dev,
155 this->hidden->mixbuf,
156 this->hidden->mixlen);
157
158 /* If we couldn't write, assume fatal error for now */
159 if ( written == 0 ) {
160 SDL_OpenedAudioDeviceDisconnected(this);
161 }
162 #ifdef DEBUG_AUDIO
163 fprintf(stderr, "Wrote %d bytes of audio data\n", written);
164 #endif
165 }
166
167 static Uint8 *
SNDIO_GetDeviceBuf(_THIS)168 SNDIO_GetDeviceBuf(_THIS)
169 {
170 return this->hidden->mixbuf;
171 }
172
173 static void
SNDIO_CloseDevice(_THIS)174 SNDIO_CloseDevice(_THIS)
175 {
176 if ( this->hidden->dev != NULL ) {
177 SNDIO_sio_stop(this->hidden->dev);
178 SNDIO_sio_close(this->hidden->dev);
179 }
180 SDL_free(this->hidden->mixbuf);
181 SDL_free(this->hidden);
182 }
183
184 static int
SNDIO_OpenDevice(_THIS,void * handle,const char * devname,int iscapture)185 SNDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
186 {
187 SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
188 struct sio_par par;
189 int status;
190
191 this->hidden = (struct SDL_PrivateAudioData *)
192 SDL_malloc(sizeof(*this->hidden));
193 if (this->hidden == NULL) {
194 return SDL_OutOfMemory();
195 }
196 SDL_zerop(this->hidden);
197
198 this->hidden->mixlen = this->spec.size;
199
200 /* !!! FIXME: SIO_DEVANY can be a specific device... */
201 if ((this->hidden->dev = SNDIO_sio_open(SIO_DEVANY, SIO_PLAY, 0)) == NULL) {
202 return SDL_SetError("sio_open() failed");
203 }
204
205 SNDIO_sio_initpar(&par);
206
207 par.rate = this->spec.freq;
208 par.pchan = this->spec.channels;
209 par.round = this->spec.samples;
210 par.appbufsz = par.round * 2;
211
212 /* Try for a closest match on audio format */
213 status = -1;
214 while (test_format && (status < 0)) {
215 if (!SDL_AUDIO_ISFLOAT(test_format)) {
216 par.le = SDL_AUDIO_ISLITTLEENDIAN(test_format) ? 1 : 0;
217 par.sig = SDL_AUDIO_ISSIGNED(test_format) ? 1 : 0;
218 par.bits = SDL_AUDIO_BITSIZE(test_format);
219
220 if (SNDIO_sio_setpar(this->hidden->dev, &par) == 0) {
221 continue;
222 }
223 if (SNDIO_sio_getpar(this->hidden->dev, &par) == 0) {
224 return SDL_SetError("sio_getpar() failed");
225 }
226 if (par.bps != SIO_BPS(par.bits)) {
227 continue;
228 }
229 if ((par.bits == 8 * par.bps) || (par.msb)) {
230 status = 0;
231 break;
232 }
233 }
234 test_format = SDL_NextAudioFormat();
235 }
236
237 if (status < 0) {
238 return SDL_SetError("sndio: Couldn't find any hardware audio formats");
239 }
240
241 if ((par.bps == 4) && (par.sig) && (par.le))
242 this->spec.format = AUDIO_S32LSB;
243 else if ((par.bps == 4) && (par.sig) && (!par.le))
244 this->spec.format = AUDIO_S32MSB;
245 else if ((par.bps == 2) && (par.sig) && (par.le))
246 this->spec.format = AUDIO_S16LSB;
247 else if ((par.bps == 2) && (par.sig) && (!par.le))
248 this->spec.format = AUDIO_S16MSB;
249 else if ((par.bps == 2) && (!par.sig) && (par.le))
250 this->spec.format = AUDIO_U16LSB;
251 else if ((par.bps == 2) && (!par.sig) && (!par.le))
252 this->spec.format = AUDIO_U16MSB;
253 else if ((par.bps == 1) && (par.sig))
254 this->spec.format = AUDIO_S8;
255 else if ((par.bps == 1) && (!par.sig))
256 this->spec.format = AUDIO_U8;
257 else {
258 return SDL_SetError("sndio: Got unsupported hardware audio format.");
259 }
260
261 this->spec.freq = par.rate;
262 this->spec.channels = par.pchan;
263 this->spec.samples = par.round;
264
265 /* Calculate the final parameters for this audio specification */
266 SDL_CalculateAudioSpec(&this->spec);
267
268 /* Allocate mixing buffer */
269 this->hidden->mixlen = this->spec.size;
270 this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
271 if (this->hidden->mixbuf == NULL) {
272 return SDL_OutOfMemory();
273 }
274 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
275
276 if (!SNDIO_sio_start(this->hidden->dev)) {
277 return SDL_SetError("sio_start() failed");
278 }
279
280 /* We're ready to rock and roll. :-) */
281 return 0;
282 }
283
284 static void
SNDIO_Deinitialize(void)285 SNDIO_Deinitialize(void)
286 {
287 UnloadSNDIOLibrary();
288 }
289
290 static int
SNDIO_Init(SDL_AudioDriverImpl * impl)291 SNDIO_Init(SDL_AudioDriverImpl * impl)
292 {
293 if (LoadSNDIOLibrary() < 0) {
294 return 0;
295 }
296
297 /* Set the function pointers */
298 impl->OpenDevice = SNDIO_OpenDevice;
299 impl->WaitDevice = SNDIO_WaitDevice;
300 impl->PlayDevice = SNDIO_PlayDevice;
301 impl->GetDeviceBuf = SNDIO_GetDeviceBuf;
302 impl->CloseDevice = SNDIO_CloseDevice;
303 impl->Deinitialize = SNDIO_Deinitialize;
304 impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: sndio can handle multiple devices. */
305
306 return 1; /* this audio target is available. */
307 }
308
309 AudioBootStrap SNDIO_bootstrap = {
310 "sndio", "OpenBSD sndio", SNDIO_Init, 0
311 };
312
313 #endif /* SDL_AUDIO_DRIVER_SNDIO */
314
315 /* vi: set ts=4 sw=4 expandtab: */
316