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