SDL  2.0
SDL_bsdaudio.c
Go to the documentation of this file.
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
53 BSDAUDIO_DetectDevices(void)
54 {
56 }
57 
58 
59 static void
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
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);
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
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) {
207  }
208 }
209 
210 static Uint8 *
211 BSDAUDIO_GetDeviceBuf(_THIS)
212 {
213  return (this->hidden->mixbuf);
214 }
215 
216 
217 static int
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
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
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
278 BSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
279 {
280  const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT;
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 */
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
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 
413  "bsd", "BSD audio", BSDAUDIO_Init, 0
414 };
415 
416 #endif /* SDL_AUDIO_DRIVER_BSD */
417 
418 /* vi: set ts=4 sw=4 expandtab: */
#define OPEN_FLAGS_INPUT
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: SDL_opengl.h:1565
#define SDL_min(x, y)
Definition: SDL_stdinc.h:349
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1611
void(* DetectDevices)(void)
Definition: SDL_sysaudio.h:75
GLuint GLsizei const GLchar * message
#define AUDIO_U16LSB
Definition: SDL_audio.h:91
static int ticks
Definition: testtimer.c:24
GLfloat GLfloat p
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:79
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:78
void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
Definition: SDL_audio.c:381
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
#define SDL_zerop(x)
Definition: SDL_stdinc.h:360
AudioBootStrap BSD_AUDIO_bootstrap
GLenum GLsizei len
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1623
SDL_AudioSpec spec
Definition: loopwave.c:35
#define SDL_GetAudioDeviceName
unsigned int size_t
#define AUDIO_U8
Definition: SDL_audio.h:89
void SDL_EnumUnixAudioDevices(const int classic, int(*test)(int))
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
Uint8 channels
Definition: SDL_audio.h:172
#define OPEN_FLAGS_OUTPUT
#define _THIS
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:143
void SDL_free(void *mem)
#define SDL_AUDIO_BITSIZE(x)
Definition: SDL_audio.h:75
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1632
int32_t Sint32
A signed 32-bit integer type.
Definition: SDL_stdinc.h:155
GLenum GLuint GLenum GLsizei const GLchar * buf
#define FUDGE_TICKS
Definition: SDL_artsaudio.h:49
#define SDL_Delay
Uint32 size
Definition: SDL_audio.h:176
int(* OpenDevice)(_THIS, void *handle, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:76
#define NULL
Definition: begin_code.h:143
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
int(* CaptureFromDevice)(_THIS, void *buffer, int buflen)
Definition: SDL_sysaudio.h:82
#define SDL_SetError
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:85
#define AUDIO_S16MSB
Definition: SDL_audio.h:94
SDL_AudioFormat format
Definition: SDL_audio.h:171
void(* FlushCapture)(_THIS)
Definition: SDL_sysaudio.h:83
GLbitfield GLuint64 timeout
GLuint buffer
#define AUDIO_S16LSB
Definition: SDL_audio.h:92
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:81
GLbitfield flags
#define SDL_malloc
#define AUDIO_S8
Definition: SDL_audio.h:90
#define SDL_memset
#define AUDIO_U16MSB
Definition: SDL_audio.h:93