src/audio/nas/SDL_nasaudio.c
author Ryan C. Gordon
Mon, 23 Jan 2017 12:06:10 -0500
changeset 10837 c2f241c2f6ad
parent 10737 3406a0f8b041
child 10945 1300a3135d61
permissions -rw-r--r--
audio: Fix same bug as last commit, but for _mm_bslli_si128 vs _mm_slli_si128.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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\n",
   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: */