• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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