Imported Upstream version 2.0.14
[platform/upstream/SDL.git] / src / audio / nas / SDL_nasaudio.c
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 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 */
381     SDL_CalculateAudioSpec(&this->spec);
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
457 AudioBootStrap NAS_bootstrap = {
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: */