src/audio/nas/SDL_nasaudio.c
author Sam Lantinga
Sat, 06 Oct 2012 12:16:32 -0700
changeset 6566 dd7e57847ea9
parent 6138 4c64952a58fb
child 6885 700f1b25f77f
permissions -rw-r--r--
Add flags to the vidmode debug output
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 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         if (this->hidden->mixbuf != NULL) {
   195             SDL_FreeAudioMem(this->hidden->mixbuf);
   196             this->hidden->mixbuf = NULL;
   197         }
   198         if (this->hidden->aud) {
   199             NAS_AuCloseServer(this->hidden->aud);
   200             this->hidden->aud = 0;
   201         }
   202         SDL_free(this->hidden);
   203         this2 = this->hidden = NULL;
   204     }
   205 }
   206 
   207 static unsigned char
   208 sdlformat_to_auformat(unsigned int fmt)
   209 {
   210     switch (fmt) {
   211     case AUDIO_U8:
   212         return AuFormatLinearUnsigned8;
   213     case AUDIO_S8:
   214         return AuFormatLinearSigned8;
   215     case AUDIO_U16LSB:
   216         return AuFormatLinearUnsigned16LSB;
   217     case AUDIO_U16MSB:
   218         return AuFormatLinearUnsigned16MSB;
   219     case AUDIO_S16LSB:
   220         return AuFormatLinearSigned16LSB;
   221     case AUDIO_S16MSB:
   222         return AuFormatLinearSigned16MSB;
   223     }
   224     return AuNone;
   225 }
   226 
   227 static AuBool
   228 event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
   229 {
   230     switch (ev->type) {
   231     case AuEventTypeElementNotify:
   232         {
   233             AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
   234 
   235             switch (event->kind) {
   236             case AuElementNotifyKindLowWater:
   237                 if (this2->buf_free >= 0) {
   238                     this2->really += event->num_bytes;
   239                     gettimeofday(&this2->last_tv, 0);
   240                     this2->buf_free += event->num_bytes;
   241                 } else {
   242                     this2->buf_free = event->num_bytes;
   243                 }
   244                 break;
   245             case AuElementNotifyKindState:
   246                 switch (event->cur_state) {
   247                 case AuStatePause:
   248                     if (event->reason != AuReasonUser) {
   249                         if (this2->buf_free >= 0) {
   250                             this2->really += event->num_bytes;
   251                             gettimeofday(&this2->last_tv, 0);
   252                             this2->buf_free += event->num_bytes;
   253                         } else {
   254                             this2->buf_free = event->num_bytes;
   255                         }
   256                     }
   257                     break;
   258                 }
   259             }
   260         }
   261     }
   262     return AuTrue;
   263 }
   264 
   265 static AuDeviceID
   266 find_device(_THIS, int nch)
   267 {
   268     /* These "Au" things are all macros, not functions... */
   269     int i;
   270     for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
   271         if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
   272              AuComponentKindPhysicalOutput) &&
   273             AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
   274             return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
   275         }
   276     }
   277     return AuNone;
   278 }
   279 
   280 static int
   281 NAS_OpenDevice(_THIS, const char *devname, int iscapture)
   282 {
   283     AuElement elms[3];
   284     int buffer_size;
   285     SDL_AudioFormat test_format, format;
   286 
   287     /* Initialize all variables that we clean on shutdown */
   288     this->hidden = (struct SDL_PrivateAudioData *)
   289         SDL_malloc((sizeof *this->hidden));
   290     if (this->hidden == NULL) {
   291         SDL_OutOfMemory();
   292         return 0;
   293     }
   294     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   295 
   296     /* Try for a closest match on audio format */
   297     format = 0;
   298     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   299          !format && test_format;) {
   300         format = sdlformat_to_auformat(test_format);
   301         if (format == AuNone) {
   302             test_format = SDL_NextAudioFormat();
   303         }
   304     }
   305     if (format == 0) {
   306         NAS_CloseDevice(this);
   307         SDL_SetError("NAS: Couldn't find any hardware audio formats");
   308         return 0;
   309     }
   310     this->spec.format = test_format;
   311 
   312     this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
   313     if (this->hidden->aud == 0) {
   314         NAS_CloseDevice(this);
   315         SDL_SetError("NAS: Couldn't open connection to NAS server");
   316         return 0;
   317     }
   318 
   319     this->hidden->dev = find_device(this, this->spec.channels);
   320     if ((this->hidden->dev == AuNone)
   321         || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
   322         NAS_CloseDevice(this);
   323         SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
   324         return 0;
   325     }
   326 
   327     buffer_size = this->spec.freq;
   328     if (buffer_size < 4096)
   329         buffer_size = 4096;
   330 
   331     if (buffer_size > 32768)
   332         buffer_size = 32768;    /* So that the buffer won't get unmanageably big. */
   333 
   334     /* Calculate the final parameters for this audio specification */
   335     SDL_CalculateAudioSpec(&this->spec);
   336 
   337     this2 = this->hidden;
   338 
   339     AuMakeElementImportClient(elms, this->spec.freq, format,
   340                               this->spec.channels, AuTrue, buffer_size,
   341                               buffer_size / 4, 0, NULL);
   342     AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
   343                               AuUnlimitedSamples, 0, NULL);
   344     NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
   345                       NULL);
   346     NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
   347                                this->hidden->flow, event_handler,
   348                                (AuPointer) NULL);
   349 
   350     NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
   351 
   352     /* Allocate mixing buffer */
   353     this->hidden->mixlen = this->spec.size;
   354     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   355     if (this->hidden->mixbuf == NULL) {
   356         NAS_CloseDevice(this);
   357         SDL_OutOfMemory();
   358         return 0;
   359     }
   360     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   361 
   362     /* We're ready to rock and roll. :-) */
   363     return 1;
   364 }
   365 
   366 static void
   367 NAS_Deinitialize(void)
   368 {
   369     UnloadNASLibrary();
   370 }
   371 
   372 static int
   373 NAS_Init(SDL_AudioDriverImpl * impl)
   374 {
   375     if (LoadNASLibrary() < 0) {
   376         return 0;
   377     } else {
   378         AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
   379         if (aud == NULL) {
   380             SDL_SetError("NAS: AuOpenServer() failed (no audio server?)");
   381             return 0;
   382         }
   383         NAS_AuCloseServer(aud);
   384     }
   385 
   386     /* Set the function pointers */
   387     impl->OpenDevice = NAS_OpenDevice;
   388     impl->PlayDevice = NAS_PlayDevice;
   389     impl->WaitDevice = NAS_WaitDevice;
   390     impl->GetDeviceBuf = NAS_GetDeviceBuf;
   391     impl->CloseDevice = NAS_CloseDevice;
   392     impl->Deinitialize = NAS_Deinitialize;
   393     impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: is this true? */
   394 
   395     return 1;   /* this audio target is available. */
   396 }
   397 
   398 AudioBootStrap NAS_bootstrap = {
   399     "nas", "Network Audio System", NAS_Init, 0
   400 };
   401 
   402 #endif /* SDL_AUDIO_DRIVER_NAS */
   403 
   404 /* vi: set ts=4 sw=4 expandtab: */