src/audio/nas/SDL_nasaudio.c
author Ryan C. Gordon
Sat, 07 Oct 2006 05:56:59 +0000
branchSDL-ryan-multiple-audio-device
changeset 3821 18393b045759
parent 3809 7852b5b78af5
child 3822 748707e2ddd1
permissions -rw-r--r--
Moved NAS audio driver to 1.3 API.
     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     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 
    22     This driver was written by:
    23     Erik Inge Bolsų
    24     knan@mo.himolde.no
    25 */
    26 #include "SDL_config.h"
    27 
    28 /* Allow access to a raw mixing buffer */
    29 
    30 #include <signal.h>
    31 #include <unistd.h>
    32 
    33 #include "SDL_timer.h"
    34 #include "SDL_audio.h"
    35 #include "../SDL_audiomem.h"
    36 #include "../SDL_audio_c.h"
    37 #include "SDL_nasaudio.h"
    38 
    39 /* The tag name used by nas audio */
    40 #define NAS_DRIVER_NAME         "nas"
    41 
    42 static struct SDL_PrivateAudioData *this2 = NULL;
    43 
    44 /* !!! FIXME: dynamic loading? */
    45 
    46 static int
    47 NAS_Available(void)
    48 {
    49     AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
    50     if (!aud)
    51         return 0;
    52 
    53     AuCloseServer(aud);
    54     return 1;
    55 }
    56 
    57 /* This function waits until it is possible to write a full sound buffer */
    58 static void
    59 NAS_WaitDevice(_THIS)
    60 {
    61     while (this->hidden->buf_free < this->hidden->mixlen) {
    62         AuEvent ev;
    63         AuNextEvent(this->hidden->aud, AuTrue, &ev);
    64         AuDispatchEvent(this->hidden->aud, &ev);
    65     }
    66 }
    67 
    68 static void
    69 NAS_PlayDevice(_THIS)
    70 {
    71     while (this->hidden->mixlen > this->hidden->buf_free) {
    72         /*
    73          * We think the buffer is full? Yikes! Ask the server for events,
    74          *  in the hope that some of them is LowWater events telling us more
    75          *  of the buffer is free now than what we think.
    76          */
    77         AuEvent ev;
    78         AuNextEvent(this->hidden->aud, AuTrue, &ev);
    79         AuDispatchEvent(this->hidden->aud, &ev);
    80     }
    81     this->hidden->buf_free -= this->hidden->mixlen;
    82 
    83     /* Write the audio data */
    84     AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
    85                    this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
    86 
    87     this->hidden->written += this->hidden->mixlen;
    88 
    89 #ifdef DEBUG_AUDIO
    90     fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
    91 #endif
    92 }
    93 
    94 static Uint8 *
    95 NAS_GetDeviceBuf(_THIS)
    96 {
    97     return (this->hidden->mixbuf);
    98 }
    99 
   100 static void
   101 NAS_CloseDevice(_THIS)
   102 {
   103     if (this->hidden != NULL) {
   104         if (this->hidden->mixbuf != NULL) {
   105             SDL_FreeAudioMem(this->hidden->mixbuf);
   106             this->hidden->mixbuf = NULL;
   107         }
   108         if (this->hidden->aud) {
   109             AuCloseServer(this->hidden->aud);
   110             this->hidden->aud = 0;
   111         }
   112         SDL_free(this->hidden);
   113         this->hidden = NULL;
   114     }
   115 }
   116 
   117 static unsigned char
   118 sdlformat_to_auformat(unsigned int fmt)
   119 {
   120     switch (fmt) {
   121     case AUDIO_U8:
   122         return AuFormatLinearUnsigned8;
   123     case AUDIO_S8:
   124         return AuFormatLinearSigned8;
   125     case AUDIO_U16LSB:
   126         return AuFormatLinearUnsigned16LSB;
   127     case AUDIO_U16MSB:
   128         return AuFormatLinearUnsigned16MSB;
   129     case AUDIO_S16LSB:
   130         return AuFormatLinearSigned16LSB;
   131     case AUDIO_S16MSB:
   132         return AuFormatLinearSigned16MSB;
   133     }
   134     return AuNone;
   135 }
   136 
   137 static AuBool
   138 event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
   139 {
   140     switch (ev->type) {
   141     case AuEventTypeElementNotify:
   142         {
   143             AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
   144 
   145             switch (event->kind) {
   146             case AuElementNotifyKindLowWater:
   147                 if (this2->buf_free >= 0) {
   148                     this2->really += event->num_bytes;
   149                     gettimeofday(&this2->last_tv, 0);
   150                     this2->buf_free += event->num_bytes;
   151                 } else {
   152                     this2->buf_free = event->num_bytes;
   153                 }
   154                 break;
   155             case AuElementNotifyKindState:
   156                 switch (event->cur_state) {
   157                 case AuStatePause:
   158                     if (event->reason != AuReasonUser) {
   159                         if (this2->buf_free >= 0) {
   160                             this2->really += event->num_bytes;
   161                             gettimeofday(&this2->last_tv, 0);
   162                             this2->buf_free += event->num_bytes;
   163                         } else {
   164                             this2->buf_free = event->num_bytes;
   165                         }
   166                     }
   167                     break;
   168                 }
   169             }
   170         }
   171     }
   172     return AuTrue;
   173 }
   174 
   175 static AuDeviceID
   176 find_device(_THIS, int nch)
   177 {
   178     int i;
   179     for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
   180         if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
   181              AuComponentKindPhysicalOutput) &&
   182             AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
   183             return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
   184         }
   185     }
   186     return AuNone;
   187 }
   188 
   189 static int
   190 NAS_OpenDevice(_THIS, const char *devname, int iscapture)
   191 {
   192     AuElement elms[3];
   193     int buffer_size;
   194     SDL_AudioFormat test_format, format;
   195 
   196     /* Initialize all variables that we clean on shutdown */
   197     this->hidden = (struct SDL_PrivateAudioData *)
   198                         SDL_malloc((sizeof *this->hidden));
   199     if (this->hidden == NULL) {
   200         SDL_OutOfMemory();
   201         return 0;
   202     }
   203     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   204 
   205     /* Try for a closest match on audio format */
   206     format = 0;
   207     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   208          !format && test_format;) {
   209         format = sdlformat_to_auformat(test_format);
   210         if (format == AuNone) {
   211             test_format = SDL_NextAudioFormat();
   212         }
   213     }
   214     if (format == 0) {
   215         NAS_CloseDevice(this);
   216         SDL_SetError("NAS: Couldn't find any hardware audio formats");
   217         return 0;
   218     }
   219     this->spec.format = test_format;
   220 
   221     this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
   222     if (this->hidden->aud == 0) {
   223         NAS_CloseDevice(this);
   224         SDL_SetError("NAS: Couldn't open connection to NAS server");
   225         return 0;
   226     }
   227 
   228     this->hidden->dev = find_device(this, this->spec.channels);
   229     if ((this->hidden->dev == AuNone)
   230         || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) {
   231         NAS_CloseDevice(this);
   232         SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
   233         return 0;
   234     }
   235 
   236     buffer_size = this->spec.freq;
   237     if (buffer_size < 4096)
   238         buffer_size = 4096;
   239 
   240     if (buffer_size > 32768)
   241         buffer_size = 32768;    /* So that the buffer won't get unmanageably big. */
   242 
   243     /* Calculate the final parameters for this audio specification */
   244     SDL_CalculateAudioSpec(&this->spec);
   245 
   246     this2 = this->hidden;
   247 
   248     AuMakeElementImportClient(elms,this->spec.freq,format,this->spec.channels,
   249                               AuTrue, buffer_size, buffer_size / 4, 0, NULL);
   250     AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
   251                               AuUnlimitedSamples, 0, NULL);
   252     AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL);
   253     AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
   254                            this->hidden->flow, event_handler,
   255                            (AuPointer) NULL);
   256 
   257     AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
   258 
   259     /* Allocate mixing buffer */
   260     this->hidden->mixlen = this->spec.size;
   261     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   262     if (this->hidden->mixbuf == NULL) {
   263         NAS_CloseDevice(this);
   264         SDL_OutOfMemory();
   265         return (-1);
   266     }
   267     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   268 
   269     /* We're ready to rock and roll. :-) */
   270     return 1;
   271 }
   272 
   273 static int
   274 NAS_Init(SDL_AudioDriverImpl *impl)
   275 {
   276     /* Set the function pointers */
   277     impl->OpenDevice = NAS_OpenDevice;
   278     impl->PlayDevice = NAS_PlayDevice;
   279     impl->WaitDevice = NAS_WaitDevice;
   280     impl->GetDeviceBuf = NAS_GetDeviceBuf;
   281     impl->CloseDevice = NAS_CloseDevice;
   282     impl->OnlyHasDefaultOutputDevice = 1;  /* !!! FIXME: is this true? */
   283 
   284     return 1;
   285 }
   286 
   287 AudioBootStrap NAS_bootstrap = {
   288     NAS_DRIVER_NAME, "Network Audio System",
   289     NAS_Available, NAS_Init, 0
   290 };
   291 
   292 /* vi: set ts=4 sw=4 expandtab: */