This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
SDL_nasaudio.c
412 lines (355 loc) · 11.7 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2006 Sam Lantinga
4
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Lesser General Public
7
License as published by the Free Software Foundation; either
8
version 2.1 of the License, or (at your option) any later version.
9
10
11
12
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
Lesser General Public License for more details.
14
15
16
17
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
19
Sam Lantinga
20
slouken@libsdl.org
21
22
23
24
25
This driver was written by:
Erik Inge Bolsø
knan@mo.himolde.no
*/
26
#include "SDL_config.h"
27
28
29
30
31
32
/* Allow access to a raw mixing buffer */
#include <signal.h>
#include <unistd.h>
33
#include "SDL_timer.h"
34
#include "SDL_audio.h"
35
#include "SDL_loadso.h"
36
37
#include "../SDL_audiomem.h"
#include "../SDL_audio_c.h"
38
39
#include "SDL_nasaudio.h"
40
/* The tag name used by nas audio */
41
42
43
44
#define NAS_DRIVER_NAME "nas"
static struct SDL_PrivateAudioData *this2 = NULL;
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
static void (*NAS_AuCloseServer)(AuServer *);
static void (*NAS_AuNextEvent)(AuServer *, AuBool, AuEvent *);
static AuBool (*NAS_AuDispatchEvent)(AuServer *, AuEvent *);
static AuFlowID (*NAS_AuCreateFlow)(AuServer *, AuStatus *);
static void (*NAS_AuStartFlow)(AuServer *, AuFlowID, AuStatus *);
static void (*NAS_AuSetElements)
(AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
static void (*NAS_AuWriteElement)
(AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
static AuServer *(*NAS_AuOpenServer)
(_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
(AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer);
#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC;
static void *nas_handle = NULL;
65
66
static int
67
load_nas_sym(const char *fn, void **addr)
68
{
69
70
*addr = SDL_LoadFunction(nas_handle, fn);
if (*addr == NULL) {
71
return 0;
72
}
73
return 1;
74
75
}
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
#define SDL_NAS_SYM(x) \
if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1
#else
#define SDL_NAS_SYM(x) NAS_##x = x
#endif
static int load_nas_syms(void)
{
SDL_NAS_SYM(AuCloseServer);
SDL_NAS_SYM(AuNextEvent);
SDL_NAS_SYM(AuDispatchEvent);
SDL_NAS_SYM(AuCreateFlow);
SDL_NAS_SYM(AuStartFlow);
SDL_NAS_SYM(AuSetElements);
SDL_NAS_SYM(AuWriteElement);
SDL_NAS_SYM(AuOpenServer);
SDL_NAS_SYM(AuRegisterEventHandler);
return 0;
}
#undef SDL_NAS_SYM
#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
static int library_load_count = 0;
static void
UnloadNASLibrary(void)
{
if ((nas_handle != NULL) && (--library_load_count == 0)) {
SDL_UnloadObject(nas_handle);
nas_handle = NULL;
}
}
static int
LoadNASLibrary(void)
{
int retval = 0;
if (library_load_count++ == 0) {
nas_handle = SDL_LoadObject(nas_library);
if (nas_handle == NULL) {
/* Copy error string so we can use it in a new SDL_SetError(). */
char *origerr = SDL_GetError();
size_t len = SDL_strlen(origerr) + 1;
char *err = (char *) alloca(len);
SDL_strlcpy(err, origerr, len);
library_load_count--;
retval = -1;
SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n",
nas_library, err);
} else {
retval = load_nas_syms();
if (retval < 0) {
UnloadNASLibrary();
}
}
}
return retval;
}
#else
static void
UnloadNASLibrary(void)
{
}
static int
LoadNASLibrary(void)
{
load_nas_syms();
return 0;
}
#endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
static int
NAS_Available(void)
{
int available = 0;
if (LoadNASLibrary() >= 0) {
AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
if (aud != NULL) {
available = 1;
NAS_AuCloseServer(aud);
}
UnloadNASLibrary();
}
return available;
}
169
/* This function waits until it is possible to write a full sound buffer */
170
static void
171
NAS_WaitDevice(_THIS)
172
{
173
174
while (this->hidden->buf_free < this->hidden->mixlen) {
AuEvent ev;
175
176
NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
NAS_AuDispatchEvent(this->hidden->aud, &ev);
177
}
178
179
}
180
static void
181
NAS_PlayDevice(_THIS)
182
{
183
184
185
186
187
188
while (this->hidden->mixlen > this->hidden->buf_free) {
/*
* We think the buffer is full? Yikes! Ask the server for events,
* in the hope that some of them is LowWater events telling us more
* of the buffer is free now than what we think.
*/
189
AuEvent ev;
190
191
NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
NAS_AuDispatchEvent(this->hidden->aud, &ev);
192
193
194
195
}
this->hidden->buf_free -= this->hidden->mixlen;
/* Write the audio data */
196
NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
197
198
199
200
this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
this->hidden->written += this->hidden->mixlen;
201
#ifdef DEBUG_AUDIO
202
fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
203
204
205
#endif
}
206
static Uint8 *
207
NAS_GetDeviceBuf(_THIS)
208
{
209
return (this->hidden->mixbuf);
210
211
}
212
static void
213
NAS_CloseDevice(_THIS)
214
{
215
216
217
218
219
220
if (this->hidden != NULL) {
if (this->hidden->mixbuf != NULL) {
SDL_FreeAudioMem(this->hidden->mixbuf);
this->hidden->mixbuf = NULL;
}
if (this->hidden->aud) {
221
NAS_AuCloseServer(this->hidden->aud);
222
223
224
this->hidden->aud = 0;
}
SDL_free(this->hidden);
225
this2 = this->hidden = NULL;
226
UnloadNASLibrary();
227
}
228
229
}
230
231
static unsigned char
sdlformat_to_auformat(unsigned int fmt)
232
{
233
switch (fmt) {
234
case AUDIO_U8:
235
return AuFormatLinearUnsigned8;
236
case AUDIO_S8:
237
return AuFormatLinearSigned8;
238
case AUDIO_U16LSB:
239
return AuFormatLinearUnsigned16LSB;
240
case AUDIO_U16MSB:
241
return AuFormatLinearUnsigned16MSB;
242
case AUDIO_S16LSB:
243
return AuFormatLinearSigned16LSB;
244
case AUDIO_S16MSB:
245
return AuFormatLinearSigned16MSB;
246
}
247
return AuNone;
248
249
250
}
static AuBool
251
event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
252
{
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
switch (ev->type) {
case AuEventTypeElementNotify:
{
AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
switch (event->kind) {
case AuElementNotifyKindLowWater:
if (this2->buf_free >= 0) {
this2->really += event->num_bytes;
gettimeofday(&this2->last_tv, 0);
this2->buf_free += event->num_bytes;
} else {
this2->buf_free = event->num_bytes;
}
break;
case AuElementNotifyKindState:
switch (event->cur_state) {
case AuStatePause:
if (event->reason != AuReasonUser) {
if (this2->buf_free >= 0) {
this2->really += event->num_bytes;
gettimeofday(&this2->last_tv, 0);
this2->buf_free += event->num_bytes;
} else {
this2->buf_free = event->num_bytes;
}
}
break;
}
}
}
}
return AuTrue;
286
287
288
289
290
}
static AuDeviceID
find_device(_THIS, int nch)
{
291
/* These "Au" things are all macros, not functions... */
292
293
294
295
296
297
298
299
300
int i;
for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
AuComponentKindPhysicalOutput) &&
AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
}
}
return AuNone;
301
302
}
303
static int
304
NAS_OpenDevice(_THIS, const char *devname, int iscapture)
305
{
306
307
AuElement elms[3];
int buffer_size;
308
SDL_AudioFormat test_format, format;
309
310
311
312
313
314
315
316
317
/* Initialize all variables that we clean on shutdown */
this->hidden = (struct SDL_PrivateAudioData *)
SDL_malloc((sizeof *this->hidden));
if (this->hidden == NULL) {
SDL_OutOfMemory();
return 0;
}
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
318
319
320
321
322
323
if (LoadNASLibrary() < 0) {
NAS_CloseDevice(this);
return 0;
}
324
325
/* Try for a closest match on audio format */
format = 0;
326
for (test_format = SDL_FirstAudioFormat(this->spec.format);
327
328
329
330
331
332
333
!format && test_format;) {
format = sdlformat_to_auformat(test_format);
if (format == AuNone) {
test_format = SDL_NextAudioFormat();
}
}
if (format == 0) {
334
335
336
NAS_CloseDevice(this);
SDL_SetError("NAS: Couldn't find any hardware audio formats");
return 0;
337
}
338
this->spec.format = test_format;
339
340
this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
341
if (this->hidden->aud == 0) {
342
343
344
NAS_CloseDevice(this);
SDL_SetError("NAS: Couldn't open connection to NAS server");
return 0;
345
346
}
347
this->hidden->dev = find_device(this, this->spec.channels);
348
if ((this->hidden->dev == AuNone)
349
|| (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
350
351
352
NAS_CloseDevice(this);
SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
return 0;
353
354
}
355
buffer_size = this->spec.freq;
356
357
358
359
360
361
362
if (buffer_size < 4096)
buffer_size = 4096;
if (buffer_size > 32768)
buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
/* Calculate the final parameters for this audio specification */
363
SDL_CalculateAudioSpec(&this->spec);
364
365
366
this2 = this->hidden;
367
AuMakeElementImportClient(elms,this->spec.freq,format,this->spec.channels,
368
AuTrue, buffer_size, buffer_size / 4, 0, NULL);
369
AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
370
AuUnlimitedSamples, 0, NULL);
371
372
373
374
375
NAS_AuSetElements(this->hidden->aud, this->hidden->flow,
AuTrue, 2, elms, NULL);
NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
this->hidden->flow, event_handler,
(AuPointer) NULL);
376
377
NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
378
379
/* Allocate mixing buffer */
380
this->hidden->mixlen = this->spec.size;
381
382
this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
if (this->hidden->mixbuf == NULL) {
383
384
NAS_CloseDevice(this);
SDL_OutOfMemory();
385
return 0;
386
}
387
SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
388
389
/* We're ready to rock and roll. :-) */
390
return 1;
391
}
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
static int
NAS_Init(SDL_AudioDriverImpl *impl)
{
/* Set the function pointers */
impl->OpenDevice = NAS_OpenDevice;
impl->PlayDevice = NAS_PlayDevice;
impl->WaitDevice = NAS_WaitDevice;
impl->GetDeviceBuf = NAS_GetDeviceBuf;
impl->CloseDevice = NAS_CloseDevice;
impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: is this true? */
return 1;
}
AudioBootStrap NAS_bootstrap = {
NAS_DRIVER_NAME, "Network Audio System",
NAS_Available, NAS_Init, 0
};
412
/* vi: set ts=4 sw=4 expandtab: */