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