zenilib  0.5.3.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
SDL_nasaudio.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2013 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_config.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_audiomem.h"
34 #include "../SDL_audio_c.h"
35 #include "SDL_nasaudio.h"
36 
37 static struct SDL_PrivateAudioData *this2 = NULL;
38 
39 
40 static void (*NAS_AuCloseServer) (AuServer *);
41 static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *);
42 static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *);
43 static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *);
44 static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *);
45 static void (*NAS_AuSetElements)
46  (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
47 static void (*NAS_AuWriteElement)
48  (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
49 static AuServer *(*NAS_AuOpenServer)
50  (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
51 static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
52  (AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer);
53 
54 
55 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
56 
57 static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC;
58 static void *nas_handle = NULL;
59 
60 static int
61 load_nas_sym(const char *fn, void **addr)
62 {
63  *addr = SDL_LoadFunction(nas_handle, fn);
64  if (*addr == NULL) {
65  return 0;
66  }
67  return 1;
68 }
69 
70 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
71 #define SDL_NAS_SYM(x) \
72  if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1
73 #else
74 #define SDL_NAS_SYM(x) NAS_##x = x
75 #endif
76 
77 static int
78 load_nas_syms(void)
79 {
80  SDL_NAS_SYM(AuCloseServer);
81  SDL_NAS_SYM(AuNextEvent);
82  SDL_NAS_SYM(AuDispatchEvent);
83  SDL_NAS_SYM(AuCreateFlow);
84  SDL_NAS_SYM(AuStartFlow);
85  SDL_NAS_SYM(AuSetElements);
86  SDL_NAS_SYM(AuWriteElement);
87  SDL_NAS_SYM(AuOpenServer);
88  SDL_NAS_SYM(AuRegisterEventHandler);
89  return 0;
90 }
91 
92 #undef SDL_NAS_SYM
93 
94 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
95 
96 static void
97 UnloadNASLibrary(void)
98 {
99  if (nas_handle != NULL) {
100  SDL_UnloadObject(nas_handle);
101  nas_handle = NULL;
102  }
103 }
104 
105 static int
106 LoadNASLibrary(void)
107 {
108  int retval = 0;
109  if (nas_handle == NULL) {
110  nas_handle = SDL_LoadObject(nas_library);
111  if (nas_handle == NULL) {
112  /* Copy error string so we can use it in a new SDL_SetError(). */
113  const char *origerr = SDL_GetError();
114  const size_t len = SDL_strlen(origerr) + 1;
115  char *err = (char *) alloca(len);
116  SDL_strlcpy(err, origerr, len);
117  retval = -1;
118  SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n",
119  nas_library, err);
120  } else {
121  retval = load_nas_syms();
122  if (retval < 0) {
123  UnloadNASLibrary();
124  }
125  }
126  }
127  return retval;
128 }
129 
130 #else
131 
132 static void
133 UnloadNASLibrary(void)
134 {
135 }
136 
137 static int
138 LoadNASLibrary(void)
139 {
140  load_nas_syms();
141  return 0;
142 }
143 
144 #endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
145 
146 /* This function waits until it is possible to write a full sound buffer */
147 static void
148 NAS_WaitDevice(_THIS)
149 {
150  while (this->hidden->buf_free < this->hidden->mixlen) {
151  AuEvent ev;
152  NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
153  NAS_AuDispatchEvent(this->hidden->aud, &ev);
154  }
155 }
156 
157 static void
158 NAS_PlayDevice(_THIS)
159 {
160  while (this->hidden->mixlen > this->hidden->buf_free) {
161  /*
162  * We think the buffer is full? Yikes! Ask the server for events,
163  * in the hope that some of them is LowWater events telling us more
164  * of the buffer is free now than what we think.
165  */
166  AuEvent ev;
167  NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
168  NAS_AuDispatchEvent(this->hidden->aud, &ev);
169  }
170  this->hidden->buf_free -= this->hidden->mixlen;
171 
172  /* Write the audio data */
173  NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
174  this->hidden->mixlen, this->hidden->mixbuf, AuFalse,
175  NULL);
176 
177  this->hidden->written += this->hidden->mixlen;
178 
179 #ifdef DEBUG_AUDIO
180  fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
181 #endif
182 }
183 
184 static Uint8 *
185 NAS_GetDeviceBuf(_THIS)
186 {
187  return (this->hidden->mixbuf);
188 }
189 
190 static void
191 NAS_CloseDevice(_THIS)
192 {
193  if (this->hidden != NULL) {
194  SDL_FreeAudioMem(this->hidden->mixbuf);
195  this->hidden->mixbuf = NULL;
196  if (this->hidden->aud) {
197  NAS_AuCloseServer(this->hidden->aud);
198  this->hidden->aud = 0;
199  }
200  SDL_free(this->hidden);
201  this2 = this->hidden = NULL;
202  }
203 }
204 
205 static unsigned char
206 sdlformat_to_auformat(unsigned int fmt)
207 {
208  switch (fmt) {
209  case AUDIO_U8:
210  return AuFormatLinearUnsigned8;
211  case AUDIO_S8:
212  return AuFormatLinearSigned8;
213  case AUDIO_U16LSB:
214  return AuFormatLinearUnsigned16LSB;
215  case AUDIO_U16MSB:
216  return AuFormatLinearUnsigned16MSB;
217  case AUDIO_S16LSB:
218  return AuFormatLinearSigned16LSB;
219  case AUDIO_S16MSB:
220  return AuFormatLinearSigned16MSB;
221  }
222  return AuNone;
223 }
224 
225 static AuBool
226 event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
227 {
228  switch (ev->type) {
229  case AuEventTypeElementNotify:
230  {
231  AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
232 
233  switch (event->kind) {
234  case AuElementNotifyKindLowWater:
235  if (this2->buf_free >= 0) {
236  this2->really += event->num_bytes;
237  gettimeofday(&this2->last_tv, 0);
238  this2->buf_free += event->num_bytes;
239  } else {
240  this2->buf_free = event->num_bytes;
241  }
242  break;
243  case AuElementNotifyKindState:
244  switch (event->cur_state) {
245  case AuStatePause:
246  if (event->reason != AuReasonUser) {
247  if (this2->buf_free >= 0) {
248  this2->really += event->num_bytes;
249  gettimeofday(&this2->last_tv, 0);
250  this2->buf_free += event->num_bytes;
251  } else {
252  this2->buf_free = event->num_bytes;
253  }
254  }
255  break;
256  }
257  }
258  }
259  }
260  return AuTrue;
261 }
262 
263 static AuDeviceID
264 find_device(_THIS, int nch)
265 {
266  /* These "Au" things are all macros, not functions... */
267  int i;
268  for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
269  if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
270  AuComponentKindPhysicalOutput) &&
271  AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
272  return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
273  }
274  }
275  return AuNone;
276 }
277 
278 static int
279 NAS_OpenDevice(_THIS, const char *devname, int iscapture)
280 {
281  AuElement elms[3];
282  int buffer_size;
283  SDL_AudioFormat test_format, format;
284 
285  /* Initialize all variables that we clean on shutdown */
286  this->hidden = (struct SDL_PrivateAudioData *)
287  SDL_malloc((sizeof *this->hidden));
288  if (this->hidden == NULL) {
289  return SDL_OutOfMemory();
290  }
291  SDL_memset(this->hidden, 0, (sizeof *this->hidden));
292 
293  /* Try for a closest match on audio format */
294  format = 0;
295  for (test_format = SDL_FirstAudioFormat(this->spec.format);
296  !format && test_format;) {
297  format = sdlformat_to_auformat(test_format);
298  if (format == AuNone) {
299  test_format = SDL_NextAudioFormat();
300  }
301  }
302  if (format == 0) {
303  NAS_CloseDevice(this);
304  return SDL_SetError("NAS: Couldn't find any hardware audio formats");
305  }
306  this->spec.format = test_format;
307 
308  this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
309  if (this->hidden->aud == 0) {
310  NAS_CloseDevice(this);
311  return SDL_SetError("NAS: Couldn't open connection to NAS server");
312  }
313 
314  this->hidden->dev = find_device(this, this->spec.channels);
315  if ((this->hidden->dev == AuNone)
316  || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
317  NAS_CloseDevice(this);
318  return SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
319  }
320 
321  buffer_size = this->spec.freq;
322  if (buffer_size < 4096)
323  buffer_size = 4096;
324 
325  if (buffer_size > 32768)
326  buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
327 
328  /* Calculate the final parameters for this audio specification */
329  SDL_CalculateAudioSpec(&this->spec);
330 
331  this2 = this->hidden;
332 
333  AuMakeElementImportClient(elms, this->spec.freq, format,
334  this->spec.channels, AuTrue, buffer_size,
335  buffer_size / 4, 0, NULL);
336  AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
337  AuUnlimitedSamples, 0, NULL);
338  NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
339  NULL);
340  NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
341  this->hidden->flow, event_handler,
342  (AuPointer) NULL);
343 
344  NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
345 
346  /* Allocate mixing buffer */
347  this->hidden->mixlen = this->spec.size;
348  this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
349  if (this->hidden->mixbuf == NULL) {
350  NAS_CloseDevice(this);
351  return SDL_OutOfMemory();
352  }
353  SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
354 
355  /* We're ready to rock and roll. :-) */
356  return 0;
357 }
358 
359 static void
360 NAS_Deinitialize(void)
361 {
362  UnloadNASLibrary();
363 }
364 
365 static int
366 NAS_Init(SDL_AudioDriverImpl * impl)
367 {
368  if (LoadNASLibrary() < 0) {
369  return 0;
370  } else {
371  AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
372  if (aud == NULL) {
373  SDL_SetError("NAS: AuOpenServer() failed (no audio server?)");
374  return 0;
375  }
376  NAS_AuCloseServer(aud);
377  }
378 
379  /* Set the function pointers */
380  impl->OpenDevice = NAS_OpenDevice;
381  impl->PlayDevice = NAS_PlayDevice;
382  impl->WaitDevice = NAS_WaitDevice;
383  impl->GetDeviceBuf = NAS_GetDeviceBuf;
384  impl->CloseDevice = NAS_CloseDevice;
385  impl->Deinitialize = NAS_Deinitialize;
386  impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: is this true? */
387 
388  return 1; /* this audio target is available. */
389 }
390 
392  "nas", "Network Audio System", NAS_Init, 0
393 };
394 
395 #endif /* SDL_AUDIO_DRIVER_NAS */
396 
397 /* vi: set ts=4 sw=4 expandtab: */
void(* CloseDevice)(_THIS)
Definition: SDL_sysaudio.h:45
SDL_AudioFormat SDL_FirstAudioFormat(SDL_AudioFormat format)
Definition: SDL_audio.c:1226
GLvoid **typedef void(GLAPIENTRY *PFNGLGETVERTEXATTRIBDVPROC)(GLuint
Definition: glew.h:1824
void(* WaitDevice)(_THIS)
Definition: SDL_sysaudio.h:41
#define NULL
Definition: ftobjs.h:61
#define AUDIO_U16LSB
Definition: SDL_audio.h:91
GLenum GLvoid * addr
Definition: glew.h:10667
Uint8 *(* GetDeviceBuf)(_THIS)
Definition: SDL_sysaudio.h:43
#define SDL_FreeAudioMem
Definition: SDL_audiomem.h:24
DECLSPEC void SDLCALL SDL_free(void *mem)
Uint16 SDL_AudioFormat
Audio format flags.
Definition: SDL_audio.h:64
GLenum GLsizei len
Definition: glew.h:7035
DECLSPEC const char *SDLCALL SDL_GetError(void)
Definition: SDL_error.c:204
int(* OpenDevice)(_THIS, const char *devname, int iscapture)
Definition: SDL_sysaudio.h:39
SDL_AudioFormat SDL_NextAudioFormat(void)
Definition: SDL_audio.c:1238
void(* PlayDevice)(_THIS)
Definition: SDL_sysaudio.h:42
#define AUDIO_U8
Definition: SDL_audio.h:89
int
Definition: SDL_systhread.c:37
#define _THIS
DECLSPEC void *SDLCALL SDL_LoadObject(const char *sofile)
DECLSPEC size_t SDLCALL SDL_strlcpy(char *dst, const char *src, size_t maxlen)
Definition: SDL_string.c:448
DECLSPEC void *SDLCALL SDL_memset(void *dst, int c, size_t len)
Definition: SDL_string.c:261
DECLSPEC void SDLCALL SDL_UnloadObject(void *handle)
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum format
Definition: gl2ext.h:845
DECLSPEC int SDLCALL SDL_SetError(const char *fmt,...)
Definition: SDL_error.c:53
DECLSPEC void *SDLCALL SDL_malloc(size_t size)
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
Definition: SDL_audio.c:1247
void(* Deinitialize)(void)
Definition: SDL_sysaudio.h:48
#define SDL_AllocAudioMem
Definition: SDL_audiomem.h:23
AudioBootStrap NAS_bootstrap
DECLSPEC size_t SDLCALL SDL_strlen(const char *str)
Definition: SDL_string.c:389
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
#define AUDIO_S16MSB
Definition: SDL_audio.h:94
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:129
#define AUDIO_S16LSB
Definition: SDL_audio.h:92
int i
Definition: pngrutil.c:1377
DECLSPEC void *SDLCALL SDL_LoadFunction(void *handle, const char *name)
#define AUDIO_S8
Definition: SDL_audio.h:90
struct timeval last_tv
Definition: SDL_nasaudio.h:51
cl_event event
Definition: glew.h:3556
#define AUDIO_U16MSB
Definition: SDL_audio.h:93