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 #include "../../SDL_internal.h"
22
23 #if SDL_AUDIO_DRIVER_BSD
24
25 /*
26 * Driver for native OpenBSD/NetBSD audio(4).
27 * vedge@vedge.com.ar.
28 */
29
30 #include <errno.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <sys/time.h>
34 #include <sys/ioctl.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <sys/audioio.h>
38
39 #include "SDL_timer.h"
40 #include "SDL_audio.h"
41 #include "../SDL_audio_c.h"
42 #include "../SDL_audiodev_c.h"
43 #include "SDL_bsdaudio.h"
44
45 /* Use timer for synchronization */
46 /* #define USE_TIMER_SYNC */
47
48 /* #define DEBUG_AUDIO */
49 /* #define DEBUG_AUDIO_STREAM */
50
51
52 static void
BSDAUDIO_DetectDevices(void)53 BSDAUDIO_DetectDevices(void)
54 {
55 SDL_EnumUnixAudioDevices(0, NULL);
56 }
57
58
59 static void
BSDAUDIO_Status(_THIS)60 BSDAUDIO_Status(_THIS)
61 {
62 #ifdef DEBUG_AUDIO
63 /* *INDENT-OFF* */
64 audio_info_t info;
65 const audio_prinfo *prinfo;
66
67 if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
68 fprintf(stderr, "AUDIO_GETINFO failed.\n");
69 return;
70 }
71
72 prinfo = this->iscapture ? &info.play : &info.record;
73
74 fprintf(stderr, "\n"
75 "[%s info]\n"
76 "buffer size : %d bytes\n"
77 "sample rate : %i Hz\n"
78 "channels : %i\n"
79 "precision : %i-bit\n"
80 "encoding : 0x%x\n"
81 "seek : %i\n"
82 "sample count : %i\n"
83 "EOF count : %i\n"
84 "paused : %s\n"
85 "error occured : %s\n"
86 "waiting : %s\n"
87 "active : %s\n"
88 "",
89 this->iscapture ? "record" : "play",
90 prinfo->buffer_size,
91 prinfo->sample_rate,
92 prinfo->channels,
93 prinfo->precision,
94 prinfo->encoding,
95 prinfo->seek,
96 prinfo->samples,
97 prinfo->eof,
98 prinfo->pause ? "yes" : "no",
99 prinfo->error ? "yes" : "no",
100 prinfo->waiting ? "yes" : "no",
101 prinfo->active ? "yes" : "no");
102
103 fprintf(stderr, "\n"
104 "[audio info]\n"
105 "monitor_gain : %i\n"
106 "hw block size : %d bytes\n"
107 "hi watermark : %i\n"
108 "lo watermark : %i\n"
109 "audio mode : %s\n"
110 "",
111 info.monitor_gain,
112 info.blocksize,
113 info.hiwat, info.lowat,
114 (info.mode == AUMODE_PLAY) ? "PLAY"
115 : (info.mode = AUMODE_RECORD) ? "RECORD"
116 : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" : "?"));
117 /* *INDENT-ON* */
118 #endif /* DEBUG_AUDIO */
119 }
120
121
122 /* This function waits until it is possible to write a full sound buffer */
123 static void
BSDAUDIO_WaitDevice(_THIS)124 BSDAUDIO_WaitDevice(_THIS)
125 {
126 #ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */
127 /* See if we need to use timed audio synchronization */
128 if (this->hidden->frame_ticks) {
129 /* Use timer for general audio synchronization */
130 Sint32 ticks;
131
132 ticks = ((Sint32) (this->hidden->next_frame - SDL_GetTicks())) - FUDGE_TICKS;
133 if (ticks > 0) {
134 SDL_Delay(ticks);
135 }
136 } else {
137 /* Use select() for audio synchronization */
138 fd_set fdset;
139 struct timeval timeout;
140
141 FD_ZERO(&fdset);
142 FD_SET(this->hidden->audio_fd, &fdset);
143 timeout.tv_sec = 10;
144 timeout.tv_usec = 0;
145 #ifdef DEBUG_AUDIO
146 fprintf(stderr, "Waiting for audio to get ready\n");
147 #endif
148 if (select(this->hidden->audio_fd + 1, NULL, &fdset, NULL, &timeout)
149 <= 0) {
150 const char *message =
151 "Audio timeout - buggy audio driver? (disabled)";
152 /* In general we should never print to the screen,
153 but in this case we have no other way of letting
154 the user know what happened.
155 */
156 fprintf(stderr, "SDL: %s\n", message);
157 SDL_OpenedAudioDeviceDisconnected(this);
158 /* Don't try to close - may hang */
159 this->hidden->audio_fd = -1;
160 #ifdef DEBUG_AUDIO
161 fprintf(stderr, "Done disabling audio\n");
162 #endif
163 }
164 #ifdef DEBUG_AUDIO
165 fprintf(stderr, "Ready!\n");
166 #endif
167 }
168 #endif /* !USE_BLOCKING_WRITES */
169 }
170
171 static void
BSDAUDIO_PlayDevice(_THIS)172 BSDAUDIO_PlayDevice(_THIS)
173 {
174 int written, p = 0;
175
176 /* Write the audio data, checking for EAGAIN on broken audio drivers */
177 do {
178 written = write(this->hidden->audio_fd,
179 &this->hidden->mixbuf[p], this->hidden->mixlen - p);
180
181 if (written > 0)
182 p += written;
183 if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
184 /* Non recoverable error has occurred. It should be reported!!! */
185 perror("audio");
186 break;
187 }
188
189 #ifdef DEBUG_AUDIO
190 fprintf(stderr, "Wrote %d bytes of audio data\n", written);
191 #endif
192
193 if (p < this->hidden->mixlen
194 || ((written < 0) && ((errno == 0) || (errno == EAGAIN)))) {
195 SDL_Delay(1); /* Let a little CPU time go by */
196 }
197 } while (p < this->hidden->mixlen);
198
199 /* If timer synchronization is enabled, set the next write frame */
200 if (this->hidden->frame_ticks) {
201 this->hidden->next_frame += this->hidden->frame_ticks;
202 }
203
204 /* If we couldn't write, assume fatal error for now */
205 if (written < 0) {
206 SDL_OpenedAudioDeviceDisconnected(this);
207 }
208 }
209
210 static Uint8 *
BSDAUDIO_GetDeviceBuf(_THIS)211 BSDAUDIO_GetDeviceBuf(_THIS)
212 {
213 return (this->hidden->mixbuf);
214 }
215
216
217 static int
BSDAUDIO_CaptureFromDevice(_THIS,void * _buffer,int buflen)218 BSDAUDIO_CaptureFromDevice(_THIS, void *_buffer, int buflen)
219 {
220 Uint8 *buffer = (Uint8 *) _buffer;
221 int br, p = 0;
222
223 /* Write the audio data, checking for EAGAIN on broken audio drivers */
224 do {
225 br = read(this->hidden->audio_fd, buffer + p, buflen - p);
226 if (br > 0)
227 p += br;
228 if (br == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
229 /* Non recoverable error has occurred. It should be reported!!! */
230 perror("audio");
231 return p ? p : -1;
232 }
233
234 #ifdef DEBUG_AUDIO
235 fprintf(stderr, "Captured %d bytes of audio data\n", br);
236 #endif
237
238 if (p < buflen
239 || ((br < 0) && ((errno == 0) || (errno == EAGAIN)))) {
240 SDL_Delay(1); /* Let a little CPU time go by */
241 }
242 } while (p < buflen);
243 }
244
245 static void
BSDAUDIO_FlushCapture(_THIS)246 BSDAUDIO_FlushCapture(_THIS)
247 {
248 audio_info_t info;
249 size_t remain;
250 Uint8 buf[512];
251
252 if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
253 return; /* oh well. */
254 }
255
256 remain = (size_t) (info.record.samples * (SDL_AUDIO_BITSIZE(this->spec.format) / 8));
257 while (remain > 0) {
258 const size_t len = SDL_min(sizeof (buf), remain);
259 const int br = read(this->hidden->audio_fd, buf, len);
260 if (br <= 0) {
261 return; /* oh well. */
262 }
263 remain -= br;
264 }
265 }
266
267 static void
BSDAUDIO_CloseDevice(_THIS)268 BSDAUDIO_CloseDevice(_THIS)
269 {
270 if (this->hidden->audio_fd >= 0) {
271 close(this->hidden->audio_fd);
272 }
273 SDL_free(this->hidden->mixbuf);
274 SDL_free(this->hidden);
275 }
276
277 static int
BSDAUDIO_OpenDevice(_THIS,void * handle,const char * devname,int iscapture)278 BSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
279 {
280 const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT;
281 SDL_AudioFormat format = 0;
282 audio_info_t info;
283 audio_prinfo *prinfo = iscapture ? &info.play : &info.record;
284
285 /* We don't care what the devname is...we'll try to open anything. */
286 /* ...but default to first name in the list... */
287 if (devname == NULL) {
288 devname = SDL_GetAudioDeviceName(0, iscapture);
289 if (devname == NULL) {
290 return SDL_SetError("No such audio device");
291 }
292 }
293
294 /* Initialize all variables that we clean on shutdown */
295 this->hidden = (struct SDL_PrivateAudioData *)
296 SDL_malloc((sizeof *this->hidden));
297 if (this->hidden == NULL) {
298 return SDL_OutOfMemory();
299 }
300 SDL_zerop(this->hidden);
301
302 /* Open the audio device */
303 this->hidden->audio_fd = open(devname, flags, 0);
304 if (this->hidden->audio_fd < 0) {
305 return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
306 }
307
308 AUDIO_INITINFO(&info);
309
310 /* Calculate the final parameters for this audio specification */
311 SDL_CalculateAudioSpec(&this->spec);
312
313 /* Set to play mode */
314 info.mode = iscapture ? AUMODE_RECORD : AUMODE_PLAY;
315 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
316 return SDL_SetError("Couldn't put device into play mode");
317 }
318
319 AUDIO_INITINFO(&info);
320 for (format = SDL_FirstAudioFormat(this->spec.format);
321 format; format = SDL_NextAudioFormat()) {
322 switch (format) {
323 case AUDIO_U8:
324 prinfo->encoding = AUDIO_ENCODING_ULINEAR;
325 prinfo->precision = 8;
326 break;
327 case AUDIO_S8:
328 prinfo->encoding = AUDIO_ENCODING_SLINEAR;
329 prinfo->precision = 8;
330 break;
331 case AUDIO_S16LSB:
332 prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE;
333 prinfo->precision = 16;
334 break;
335 case AUDIO_S16MSB:
336 prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE;
337 prinfo->precision = 16;
338 break;
339 case AUDIO_U16LSB:
340 prinfo->encoding = AUDIO_ENCODING_ULINEAR_LE;
341 prinfo->precision = 16;
342 break;
343 case AUDIO_U16MSB:
344 prinfo->encoding = AUDIO_ENCODING_ULINEAR_BE;
345 prinfo->precision = 16;
346 break;
347 default:
348 continue;
349 }
350
351 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == 0) {
352 break;
353 }
354 }
355
356 if (!format) {
357 return SDL_SetError("No supported encoding for 0x%x", this->spec.format);
358 }
359
360 this->spec.format = format;
361
362 AUDIO_INITINFO(&info);
363 prinfo->channels = this->spec.channels;
364 if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == -1) {
365 this->spec.channels = 1;
366 }
367 AUDIO_INITINFO(&info);
368 prinfo->sample_rate = this->spec.freq;
369 info.blocksize = this->spec.size;
370 info.hiwat = 5;
371 info.lowat = 3;
372 (void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info);
373 (void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info);
374 this->spec.freq = prinfo->sample_rate;
375
376 if (!iscapture) {
377 /* Allocate mixing buffer */
378 this->hidden->mixlen = this->spec.size;
379 this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
380 if (this->hidden->mixbuf == NULL) {
381 return SDL_OutOfMemory();
382 }
383 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
384 }
385
386 BSDAUDIO_Status(this);
387
388 /* We're ready to rock and roll. :-) */
389 return 0;
390 }
391
392 static int
BSDAUDIO_Init(SDL_AudioDriverImpl * impl)393 BSDAUDIO_Init(SDL_AudioDriverImpl * impl)
394 {
395 /* Set the function pointers */
396 impl->DetectDevices = BSDAUDIO_DetectDevices;
397 impl->OpenDevice = BSDAUDIO_OpenDevice;
398 impl->PlayDevice = BSDAUDIO_PlayDevice;
399 impl->WaitDevice = BSDAUDIO_WaitDevice;
400 impl->GetDeviceBuf = BSDAUDIO_GetDeviceBuf;
401 impl->CloseDevice = BSDAUDIO_CloseDevice;
402 impl->CaptureFromDevice = BSDAUDIO_CaptureFromDevice;
403 impl->FlushCapture = BSDAUDIO_FlushCapture;
404
405 impl->HasCaptureSupport = SDL_TRUE;
406 impl->AllowsArbitraryDeviceNames = 1;
407
408 return 1; /* this audio target is available. */
409 }
410
411
412 AudioBootStrap BSD_AUDIO_bootstrap = {
413 "bsd", "BSD audio", BSDAUDIO_Init, 0
414 };
415
416 #endif /* SDL_AUDIO_DRIVER_BSD */
417
418 /* vi: set ts=4 sw=4 expandtab: */
419