src/audio/nas/SDL_nasaudio.c
author Ryan C. Gordon
Tue, 26 Jul 2011 14:18:00 -0700
changeset 5594 e741303e08a8
parent 5535 96594ac5fd1a
child 5880 db03ff7a7398
permissions -rw-r--r--
Removed needless macros in various audio targets.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2011 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 /* Allow access to a raw mixing buffer */
    24 
    25 #include <signal.h>
    26 #include <unistd.h>
    27 
    28 #include "SDL_timer.h"
    29 #include "SDL_audio.h"
    30 #include "SDL_loadso.h"
    31 #include "../SDL_audiomem.h"
    32 #include "../SDL_audio_c.h"
    33 #include "SDL_nasaudio.h"
    34 
    35 static struct SDL_PrivateAudioData *this2 = NULL;
    36 
    37 
    38 static void (*NAS_AuCloseServer) (AuServer *);
    39 static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *);
    40 static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *);
    41 static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *);
    42 static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *);
    43 static void (*NAS_AuSetElements)
    44   (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
    45 static void (*NAS_AuWriteElement)
    46   (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
    47 static AuServer *(*NAS_AuOpenServer)
    48   (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
    49 static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
    50   (AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer);
    51 
    52 
    53 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
    54 
    55 static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC;
    56 static void *nas_handle = NULL;
    57 
    58 static int
    59 load_nas_sym(const char *fn, void **addr)
    60 {
    61     *addr = SDL_LoadFunction(nas_handle, fn);
    62     if (*addr == NULL) {
    63         return 0;
    64     }
    65     return 1;
    66 }
    67 
    68 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
    69 #define SDL_NAS_SYM(x) \
    70     if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1
    71 #else
    72 #define SDL_NAS_SYM(x) NAS_##x = x
    73 #endif
    74 
    75 static int
    76 load_nas_syms(void)
    77 {
    78     SDL_NAS_SYM(AuCloseServer);
    79     SDL_NAS_SYM(AuNextEvent);
    80     SDL_NAS_SYM(AuDispatchEvent);
    81     SDL_NAS_SYM(AuCreateFlow);
    82     SDL_NAS_SYM(AuStartFlow);
    83     SDL_NAS_SYM(AuSetElements);
    84     SDL_NAS_SYM(AuWriteElement);
    85     SDL_NAS_SYM(AuOpenServer);
    86     SDL_NAS_SYM(AuRegisterEventHandler);
    87     return 0;
    88 }
    89 
    90 #undef SDL_NAS_SYM
    91 
    92 #ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
    93 
    94 static void
    95 UnloadNASLibrary(void)
    96 {
    97     if (nas_handle != NULL) {
    98         SDL_UnloadObject(nas_handle);
    99         nas_handle = NULL;
   100     }
   101 }
   102 
   103 static int
   104 LoadNASLibrary(void)
   105 {
   106     int retval = 0;
   107     if (nas_handle == NULL) {
   108         nas_handle = SDL_LoadObject(nas_library);
   109         if (nas_handle == NULL) {
   110             /* Copy error string so we can use it in a new SDL_SetError(). */
   111             char *origerr = SDL_GetError();
   112             size_t len = SDL_strlen(origerr) + 1;
   113             char *err = (char *) alloca(len);
   114             SDL_strlcpy(err, origerr, len);
   115             retval = -1;
   116             SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n",
   117                          nas_library, err);
   118         } else {
   119             retval = load_nas_syms();
   120             if (retval < 0) {
   121                 UnloadNASLibrary();
   122             }
   123         }
   124     }
   125     return retval;
   126 }
   127 
   128 #else
   129 
   130 static void
   131 UnloadNASLibrary(void)
   132 {
   133 }
   134 
   135 static int
   136 LoadNASLibrary(void)
   137 {
   138     load_nas_syms();
   139     return 0;
   140 }
   141 
   142 #endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
   143 
   144 /* This function waits until it is possible to write a full sound buffer */
   145 static void
   146 NAS_WaitDevice(_THIS)
   147 {
   148     while (this->hidden->buf_free < this->hidden->mixlen) {
   149         AuEvent ev;
   150         NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
   151         NAS_AuDispatchEvent(this->hidden->aud, &ev);
   152     }
   153 }
   154 
   155 static void
   156 NAS_PlayDevice(_THIS)
   157 {
   158     while (this->hidden->mixlen > this->hidden->buf_free) {
   159         /*
   160          * We think the buffer is full? Yikes! Ask the server for events,
   161          *  in the hope that some of them is LowWater events telling us more
   162          *  of the buffer is free now than what we think.
   163          */
   164         AuEvent ev;
   165         NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
   166         NAS_AuDispatchEvent(this->hidden->aud, &ev);
   167     }
   168     this->hidden->buf_free -= this->hidden->mixlen;
   169 
   170     /* Write the audio data */
   171     NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
   172                        this->hidden->mixlen, this->hidden->mixbuf, AuFalse,
   173                        NULL);
   174 
   175     this->hidden->written += this->hidden->mixlen;
   176 
   177 #ifdef DEBUG_AUDIO
   178     fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
   179 #endif
   180 }
   181 
   182 static Uint8 *
   183 NAS_GetDeviceBuf(_THIS)
   184 {
   185     return (this->hidden->mixbuf);
   186 }
   187 
   188 static void
   189 NAS_CloseDevice(_THIS)
   190 {
   191     if (this->hidden != NULL) {
   192         if (this->hidden->mixbuf != NULL) {
   193             SDL_FreeAudioMem(this->hidden->mixbuf);
   194             this->hidden->mixbuf = NULL;
   195         }
   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         SDL_OutOfMemory();
   290         return 0;
   291     }
   292     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   293 
   294     /* Try for a closest match on audio format */
   295     format = 0;
   296     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   297          !format && test_format;) {
   298         format = sdlformat_to_auformat(test_format);
   299         if (format == AuNone) {
   300             test_format = SDL_NextAudioFormat();
   301         }
   302     }
   303     if (format == 0) {
   304         NAS_CloseDevice(this);
   305         SDL_SetError("NAS: Couldn't find any hardware audio formats");
   306         return 0;
   307     }
   308     this->spec.format = test_format;
   309 
   310     this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
   311     if (this->hidden->aud == 0) {
   312         NAS_CloseDevice(this);
   313         SDL_SetError("NAS: Couldn't open connection to NAS server");
   314         return 0;
   315     }
   316 
   317     this->hidden->dev = find_device(this, this->spec.channels);
   318     if ((this->hidden->dev == AuNone)
   319         || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
   320         NAS_CloseDevice(this);
   321         SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
   322         return 0;
   323     }
   324 
   325     buffer_size = this->spec.freq;
   326     if (buffer_size < 4096)
   327         buffer_size = 4096;
   328 
   329     if (buffer_size > 32768)
   330         buffer_size = 32768;    /* So that the buffer won't get unmanageably big. */
   331 
   332     /* Calculate the final parameters for this audio specification */
   333     SDL_CalculateAudioSpec(&this->spec);
   334 
   335     this2 = this->hidden;
   336 
   337     AuMakeElementImportClient(elms, this->spec.freq, format,
   338                               this->spec.channels, AuTrue, buffer_size,
   339                               buffer_size / 4, 0, NULL);
   340     AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
   341                               AuUnlimitedSamples, 0, NULL);
   342     NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
   343                       NULL);
   344     NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
   345                                this->hidden->flow, event_handler,
   346                                (AuPointer) NULL);
   347 
   348     NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
   349 
   350     /* Allocate mixing buffer */
   351     this->hidden->mixlen = this->spec.size;
   352     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   353     if (this->hidden->mixbuf == NULL) {
   354         NAS_CloseDevice(this);
   355         SDL_OutOfMemory();
   356         return 0;
   357     }
   358     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   359 
   360     /* We're ready to rock and roll. :-) */
   361     return 1;
   362 }
   363 
   364 static void
   365 NAS_Deinitialize(void)
   366 {
   367     UnloadNASLibrary();
   368 }
   369 
   370 static int
   371 NAS_Init(SDL_AudioDriverImpl * impl)
   372 {
   373     if (LoadNASLibrary() < 0) {
   374         return 0;
   375     } else {
   376         AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
   377         if (aud == NULL) {
   378             SDL_SetError("NAS: AuOpenServer() failed (no audio server?)");
   379             return 0;
   380         }
   381         NAS_AuCloseServer(aud);
   382     }
   383 
   384     /* Set the function pointers */
   385     impl->OpenDevice = NAS_OpenDevice;
   386     impl->PlayDevice = NAS_PlayDevice;
   387     impl->WaitDevice = NAS_WaitDevice;
   388     impl->GetDeviceBuf = NAS_GetDeviceBuf;
   389     impl->CloseDevice = NAS_CloseDevice;
   390     impl->Deinitialize = NAS_Deinitialize;
   391     impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: is this true? */
   392 
   393     return 1;   /* this audio target is available. */
   394 }
   395 
   396 AudioBootStrap NAS_bootstrap = {
   397     "nas", "Network Audio System", NAS_Init, 0
   398 };
   399 
   400 /* vi: set ts=4 sw=4 expandtab: */