SDL  2.0
SDL_nasaudio.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_NAS
24 
25 /* Allow access to a raw mixing buffer */
26 
27 #include <signal.h>
28 #include <unistd.h>
29 
30 #include "SDL_timer.h"
31 #include "SDL_audio.h"
32 #include "SDL_loadso.h"
33 #include "../SDL_audio_c.h"
34 #include "SDL_nasaudio.h"
35 
36 static void (*NAS_AuCloseServer) (AuServer *);
37 static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *);
38 static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *);
39 static void (*NAS_AuHandleEvents) (AuServer *);
40 static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *);
41 static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *);
42 static void (*NAS_AuSetElements)
43  (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
44 static void (*NAS_AuWriteElement)
45  (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
46 static AuUint32 (*NAS_AuReadElement)
47  (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuStatus *);
48 static AuServer *(*NAS_AuOpenServer)
49  (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
50 static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
51  (AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer);
52 
53 
54 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
55 
56 static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC;
57 static void *nas_handle = NULL;
58 
59 static int
60 load_nas_sym(const char *fn, void **addr)
61 {
62  *addr = SDL_LoadFunction(nas_handle, fn);
63  if (*addr == NULL) {
64  return 0;
65  }
66  return 1;
67 }
68 
69 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
70 #define SDL_NAS_SYM(x) \
71  if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1
72 #else
73 #define SDL_NAS_SYM(x) NAS_##x = x
74 #endif
75 
76 static int
77 load_nas_syms(void)
78 {
79  SDL_NAS_SYM(AuCloseServer);
80  SDL_NAS_SYM(AuNextEvent);
81  SDL_NAS_SYM(AuDispatchEvent);
82  SDL_NAS_SYM(AuHandleEvents);
83  SDL_NAS_SYM(AuCreateFlow);
84  SDL_NAS_SYM(AuStartFlow);
85  SDL_NAS_SYM(AuSetElements);
86  SDL_NAS_SYM(AuWriteElement);
87  SDL_NAS_SYM(AuReadElement);
88  SDL_NAS_SYM(AuOpenServer);
89  SDL_NAS_SYM(AuRegisterEventHandler);
90  return 0;
91 }
92 
93 #undef SDL_NAS_SYM
94 
95 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
96 
97 static void
98 UnloadNASLibrary(void)
99 {
100  if (nas_handle != NULL) {
101  SDL_UnloadObject(nas_handle);
102  nas_handle = NULL;
103  }
104 }
105 
106 static int
107 LoadNASLibrary(void)
108 {
109  int retval = 0;
110  if (nas_handle == NULL) {
111  nas_handle = SDL_LoadObject(nas_library);
112  if (nas_handle == NULL) {
113  /* Copy error string so we can use it in a new SDL_SetError(). */
114  const char *origerr = SDL_GetError();
115  const size_t len = SDL_strlen(origerr) + 1;
116  char *err = (char *) alloca(len);
117  SDL_strlcpy(err, origerr, len);
118  retval = -1;
119  SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s",
120  nas_library, err);
121  } else {
122  retval = load_nas_syms();
123  if (retval < 0) {
124  UnloadNASLibrary();
125  }
126  }
127  }
128  return retval;
129 }
130 
131 #else
132 
133 static void
134 UnloadNASLibrary(void)
135 {
136 }
137 
138 static int
139 LoadNASLibrary(void)
140 {
141  load_nas_syms();
142  return 0;
143 }
144 
145 #endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
146 
147 /* This function waits until it is possible to write a full sound buffer */
148 static void
149 NAS_WaitDevice(_THIS)
150 {
151  while (this->hidden->buf_free < this->hidden->mixlen) {
152  AuEvent ev;
153  NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
154  NAS_AuDispatchEvent(this->hidden->aud, &ev);
155  }
156 }
157 
158 static void
159 NAS_PlayDevice(_THIS)
160 {
161  while (this->hidden->mixlen > this->hidden->buf_free) {
162  /*
163  * We think the buffer is full? Yikes! Ask the server for events,
164  * in the hope that some of them is LowWater events telling us more
165  * of the buffer is free now than what we think.
166  */
167  AuEvent ev;
168  NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
169  NAS_AuDispatchEvent(this->hidden->aud, &ev);
170  }
171  this->hidden->buf_free -= this->hidden->mixlen;
172 
173  /* Write the audio data */
174  NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
175  this->hidden->mixlen, this->hidden->mixbuf, AuFalse,
176  NULL);
177 
178  this->hidden->written += this->hidden->mixlen;
179 
180 #ifdef DEBUG_AUDIO
181  fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
182 #endif
183 }
184 
185 static Uint8 *
186 NAS_GetDeviceBuf(_THIS)
187 {
188  return (this->hidden->mixbuf);
189 }
190 
191 static int
192 NAS_CaptureFromDevice(_THIS, void *buffer, int buflen)
193 {
194  struct SDL_PrivateAudioData *h = this->hidden;
195  int retval;
196 
197  while (SDL_TRUE) {
198  /* just keep the event queue moving and the server chattering. */
199  NAS_AuHandleEvents(h->aud);
200 
201  retval = (int) NAS_AuReadElement(h->aud, h->flow, 1, buflen, buffer, NULL);
202  /*printf("read %d capture bytes\n", (int) retval);*/
203  if (retval == 0) {
204  SDL_Delay(10); /* don't burn the CPU if we're waiting for data. */
205  } else {
206  break;
207  }
208  }
209 
210  return retval;
211 }
212 
213 static void
214 NAS_FlushCapture(_THIS)
215 {
216  struct SDL_PrivateAudioData *h = this->hidden;
217  AuUint32 total = 0;
218  AuUint32 br;
219  Uint8 buf[512];
220 
221  do {
222  /* just keep the event queue moving and the server chattering. */
223  NAS_AuHandleEvents(h->aud);
224  br = NAS_AuReadElement(h->aud, h->flow, 1, sizeof (buf), buf, NULL);
225  /*printf("flushed %d capture bytes\n", (int) br);*/
226  total += br;
227  } while ((br == sizeof (buf)) && (total < this->spec.size));
228 }
229 
230 static void
231 NAS_CloseDevice(_THIS)
232 {
233  if (this->hidden->aud) {
234  NAS_AuCloseServer(this->hidden->aud);
235  }
236  SDL_free(this->hidden->mixbuf);
237  SDL_free(this->hidden);
238 }
239 
240 static unsigned char
241 sdlformat_to_auformat(unsigned int fmt)
242 {
243  switch (fmt) {
244  case AUDIO_U8:
245  return AuFormatLinearUnsigned8;
246  case AUDIO_S8:
247  return AuFormatLinearSigned8;
248  case AUDIO_U16LSB:
249  return AuFormatLinearUnsigned16LSB;
250  case AUDIO_U16MSB:
251  return AuFormatLinearUnsigned16MSB;
252  case AUDIO_S16LSB:
253  return AuFormatLinearSigned16LSB;
254  case AUDIO_S16MSB:
255  return AuFormatLinearSigned16MSB;
256  }
257  return AuNone;
258 }
259 
260 static AuBool
261 event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
262 {
263  SDL_AudioDevice *this = (SDL_AudioDevice *) hnd->data;
264  struct SDL_PrivateAudioData *h = this->hidden;
265  if (this->iscapture) {
266  return AuTrue; /* we don't (currently) care about any of this for capture devices */
267  }
268 
269  switch (ev->type) {
270  case AuEventTypeElementNotify:
271  {
272  AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
273 
274  switch (event->kind) {
275  case AuElementNotifyKindLowWater:
276  if (h->buf_free >= 0) {
277  h->really += event->num_bytes;
278  gettimeofday(&h->last_tv, 0);
279  h->buf_free += event->num_bytes;
280  } else {
281  h->buf_free = event->num_bytes;
282  }
283  break;
284  case AuElementNotifyKindState:
285  switch (event->cur_state) {
286  case AuStatePause:
287  if (event->reason != AuReasonUser) {
288  if (h->buf_free >= 0) {
289  h->really += event->num_bytes;
290  gettimeofday(&h->last_tv, 0);
291  h->buf_free += event->num_bytes;
292  } else {
293  h->buf_free = event->num_bytes;
294  }
295  }
296  break;
297  }
298  }
299  }
300  }
301  return AuTrue;
302 }
303 
304 static AuDeviceID
305 find_device(_THIS)
306 {
307  /* These "Au" things are all macros, not functions... */
308  struct SDL_PrivateAudioData *h = this->hidden;
309  const unsigned int devicekind = this->iscapture ? AuComponentKindPhysicalInput : AuComponentKindPhysicalOutput;
310  const int numdevs = AuServerNumDevices(h->aud);
311  const int nch = this->spec.channels;
312  int i;
313 
314  /* Try to find exact match on channels first... */
315  for (i = 0; i < numdevs; i++) {
316  const AuDeviceAttributes *dev = AuServerDevice(h->aud, i);
317  if ((AuDeviceKind(dev) == devicekind) && (AuDeviceNumTracks(dev) == nch)) {
318  return AuDeviceIdentifier(dev);
319  }
320  }
321 
322  /* Take anything, then... */
323  for (i = 0; i < numdevs; i++) {
324  const AuDeviceAttributes *dev = AuServerDevice(h->aud, i);
325  if (AuDeviceKind(dev) == devicekind) {
326  this->spec.channels = AuDeviceNumTracks(dev);
327  return AuDeviceIdentifier(dev);
328  }
329  }
330  return AuNone;
331 }
332 
333 static int
334 NAS_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
335 {
336  AuElement elms[3];
337  int buffer_size;
338  SDL_AudioFormat test_format, format;
339 
340  /* Initialize all variables that we clean on shutdown */
341  this->hidden = (struct SDL_PrivateAudioData *)
342  SDL_malloc((sizeof *this->hidden));
343  if (this->hidden == NULL) {
344  return SDL_OutOfMemory();
345  }
346  SDL_zerop(this->hidden);
347 
348  /* Try for a closest match on audio format */
349  format = 0;
350  for (test_format = SDL_FirstAudioFormat(this->spec.format);
351  !format && test_format;) {
352  format = sdlformat_to_auformat(test_format);
353  if (format == AuNone) {
354  test_format = SDL_NextAudioFormat();
355  }
356  }
357  if (format == 0) {
358  return SDL_SetError("NAS: Couldn't find any hardware audio formats");
359  }
360  this->spec.format = test_format;
361 
362  this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
363  if (this->hidden->aud == 0) {
364  return SDL_SetError("NAS: Couldn't open connection to NAS server");
365  }
366 
367  this->hidden->dev = find_device(this);
368  if ((this->hidden->dev == AuNone)
369  || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
370  return SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
371  }
372 
373  buffer_size = this->spec.freq;
374  if (buffer_size < 4096)
375  buffer_size = 4096;
376 
377  if (buffer_size > 32768)
378  buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
379 
380  /* Calculate the final parameters for this audio specification */
382 
383  if (iscapture) {
384  AuMakeElementImportDevice(elms, this->spec.freq, this->hidden->dev,
385  AuUnlimitedSamples, 0, NULL);
386  AuMakeElementExportClient(elms + 1, 0, this->spec.freq, format,
387  this->spec.channels, AuTrue, buffer_size,
388  buffer_size, 0, NULL);
389  } else {
390  AuMakeElementImportClient(elms, this->spec.freq, format,
391  this->spec.channels, AuTrue, buffer_size,
392  buffer_size / 4, 0, NULL);
393  AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
394  AuUnlimitedSamples, 0, NULL);
395  }
396 
397  NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue,
398  2, elms, NULL);
399 
400  NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
401  this->hidden->flow, event_handler,
402  (AuPointer) this);
403 
404  NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
405 
406  /* Allocate mixing buffer */
407  if (!iscapture) {
408  this->hidden->mixlen = this->spec.size;
409  this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
410  if (this->hidden->mixbuf == NULL) {
411  return SDL_OutOfMemory();
412  }
413  SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
414  }
415 
416  /* We're ready to rock and roll. :-) */
417  return 0;
418 }
419 
420 static void
421 NAS_Deinitialize(void)
422 {
423  UnloadNASLibrary();
424 }
425 
426 static int
427 NAS_Init(SDL_AudioDriverImpl * impl)
428 {
429  if (LoadNASLibrary() < 0) {
430  return 0;
431  } else {
432  AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
433  if (aud == NULL) {
434  SDL_SetError("NAS: AuOpenServer() failed (no audio server?)");
435  return 0;
436  }
437  NAS_AuCloseServer(aud);
438  }
439 
440  /* Set the function pointers */
441  impl->OpenDevice = NAS_OpenDevice;
442  impl->PlayDevice = NAS_PlayDevice;
443  impl->WaitDevice = NAS_WaitDevice;
444  impl->GetDeviceBuf = NAS_GetDeviceBuf;
445  impl->CaptureFromDevice = NAS_CaptureFromDevice;
446  impl->FlushCapture = NAS_FlushCapture;
447  impl->CloseDevice = NAS_CloseDevice;
448  impl->Deinitialize = NAS_Deinitialize;
449 
450  impl->OnlyHasDefaultOutputDevice = 1;
451  impl->OnlyHasDefaultCaptureDevice = 1;
452  impl->HasCaptureSupport = SDL_TRUE;
453 
454  return 1; /* this audio target is available. */
455 }
456 
458  "nas", "Network Audio System", NAS_Init, 0
459 };
460 
461 #endif /* SDL_AUDIO_DRIVER_NAS */
462 
463 /* vi: set ts=4 sw=4 expandtab: */
#define SDL_strlcpy
#define SDL_GetError
AudioBootStrap NAS_bootstrap
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1584
#define AUDIO_U16LSB
Definition: SDL_audio.h:91
GLfloat GLfloat GLfloat GLfloat h
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:73
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:72
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
#define SDL_LoadObject
#define SDL_UnloadObject
#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
SDL_bool retval
#define AUDIO_U8
Definition: SDL_audio.h:89
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:937
Uint8 channels
Definition: SDL_audio.h:181
#define _THIS
uint8_t Uint8
Definition: SDL_stdinc.h:157
#define SDL_free
struct _cl_event * event
GLenum const void * addr
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:83
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1605
GLenum GLuint GLenum GLsizei const GLchar * buf
#define SDL_Delay
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
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
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:79
#define AUDIO_S16MSB
Definition: SDL_audio.h:94
#define SDL_strlen
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
#define AUDIO_S16LSB
Definition: SDL_audio.h:92
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:75
#define SDL_malloc
void * SDL_LoadFunction(void *handle, const char *name)
#define AUDIO_S8
Definition: SDL_audio.h:90
struct timeval last_tv
Definition: SDL_nasaudio.h:51
#define SDL_memset
#define AUDIO_U16MSB
Definition: SDL_audio.h:93