src/audio/nas/SDL_nasaudio.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 29 May 2006 04:04:35 +0000
branchSDL-1.3
changeset 1668 4da1ee79c9af
parent 1662 782fd950bd46
permissions -rw-r--r--
more tweaking indent options
     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_audiodev_c.h"
    38 #include "SDL_nasaudio.h"
    39 
    40 /* The tag name used by artsc audio */
    41 #define NAS_DRIVER_NAME         "nas"
    42 
    43 static struct SDL_PrivateAudioData *this2 = NULL;
    44 
    45 /* Audio driver functions */
    46 static int NAS_OpenAudio(_THIS, SDL_AudioSpec * spec);
    47 static void NAS_WaitAudio(_THIS);
    48 static void NAS_PlayAudio(_THIS);
    49 static Uint8 *NAS_GetAudioBuf(_THIS);
    50 static void NAS_CloseAudio(_THIS);
    51 
    52 /* Audio driver bootstrap functions */
    53 
    54 static int
    55 Audio_Available(void)
    56 {
    57     AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
    58     if (!aud)
    59         return 0;
    60 
    61     AuCloseServer(aud);
    62     return 1;
    63 }
    64 
    65 static void
    66 Audio_DeleteDevice(SDL_AudioDevice * device)
    67 {
    68     SDL_free(device->hidden);
    69     SDL_free(device);
    70 }
    71 
    72 static SDL_AudioDevice *
    73 Audio_CreateDevice(int devindex)
    74 {
    75     SDL_AudioDevice *this;
    76 
    77     /* Initialize all variables that we clean on shutdown */
    78     this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice));
    79     if (this) {
    80         SDL_memset(this, 0, (sizeof *this));
    81         this->hidden = (struct SDL_PrivateAudioData *)
    82             SDL_malloc((sizeof *this->hidden));
    83     }
    84     if ((this == NULL) || (this->hidden == NULL)) {
    85         SDL_OutOfMemory();
    86         if (this) {
    87             SDL_free(this);
    88         }
    89         return (0);
    90     }
    91     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    92 
    93     /* Set the function pointers */
    94     this->OpenAudio = NAS_OpenAudio;
    95     this->WaitAudio = NAS_WaitAudio;
    96     this->PlayAudio = NAS_PlayAudio;
    97     this->GetAudioBuf = NAS_GetAudioBuf;
    98     this->CloseAudio = NAS_CloseAudio;
    99 
   100     this->free = Audio_DeleteDevice;
   101 
   102     return this;
   103 }
   104 
   105 AudioBootStrap NAS_bootstrap = {
   106     NAS_DRIVER_NAME, "Network Audio System",
   107     Audio_Available, Audio_CreateDevice
   108 };
   109 
   110 /* This function waits until it is possible to write a full sound buffer */
   111 static void
   112 NAS_WaitAudio(_THIS)
   113 {
   114     while (this->hidden->buf_free < this->hidden->mixlen) {
   115         AuEvent ev;
   116         AuNextEvent(this->hidden->aud, AuTrue, &ev);
   117         AuDispatchEvent(this->hidden->aud, &ev);
   118     }
   119 }
   120 
   121 static void
   122 NAS_PlayAudio(_THIS)
   123 {
   124     while (this->hidden->mixlen > this->hidden->buf_free) {     /* We think the buffer is full? Yikes! Ask the server for events,
   125                                                                    in the hope that some of them is LowWater events telling us more
   126                                                                    of the buffer is free now than what we think. */
   127         AuEvent ev;
   128         AuNextEvent(this->hidden->aud, AuTrue, &ev);
   129         AuDispatchEvent(this->hidden->aud, &ev);
   130     }
   131     this->hidden->buf_free -= this->hidden->mixlen;
   132 
   133     /* Write the audio data */
   134     AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
   135                    this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
   136 
   137     this->hidden->written += this->hidden->mixlen;
   138 
   139 #ifdef DEBUG_AUDIO
   140     fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
   141 #endif
   142 }
   143 
   144 static Uint8 *
   145 NAS_GetAudioBuf(_THIS)
   146 {
   147     return (this->hidden->mixbuf);
   148 }
   149 
   150 static void
   151 NAS_CloseAudio(_THIS)
   152 {
   153     if (this->hidden->mixbuf != NULL) {
   154         SDL_FreeAudioMem(this->hidden->mixbuf);
   155         this->hidden->mixbuf = NULL;
   156     }
   157     if (this->hidden->aud) {
   158         AuCloseServer(this->hidden->aud);
   159         this->hidden->aud = 0;
   160     }
   161 }
   162 
   163 static unsigned char
   164 sdlformat_to_auformat(unsigned int fmt)
   165 {
   166     switch (fmt) {
   167     case AUDIO_U8:
   168         return AuFormatLinearUnsigned8;
   169     case AUDIO_S8:
   170         return AuFormatLinearSigned8;
   171     case AUDIO_U16LSB:
   172         return AuFormatLinearUnsigned16LSB;
   173     case AUDIO_U16MSB:
   174         return AuFormatLinearUnsigned16MSB;
   175     case AUDIO_S16LSB:
   176         return AuFormatLinearSigned16LSB;
   177     case AUDIO_S16MSB:
   178         return AuFormatLinearSigned16MSB;
   179     }
   180     return AuNone;
   181 }
   182 
   183 static AuBool
   184 event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
   185 {
   186     switch (ev->type) {
   187     case AuEventTypeElementNotify:
   188         {
   189             AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
   190 
   191             switch (event->kind) {
   192             case AuElementNotifyKindLowWater:
   193                 if (this2->buf_free >= 0) {
   194                     this2->really += event->num_bytes;
   195                     gettimeofday(&this2->last_tv, 0);
   196                     this2->buf_free += event->num_bytes;
   197                 } else {
   198                     this2->buf_free = event->num_bytes;
   199                 }
   200                 break;
   201             case AuElementNotifyKindState:
   202                 switch (event->cur_state) {
   203                 case AuStatePause:
   204                     if (event->reason != AuReasonUser) {
   205                         if (this2->buf_free >= 0) {
   206                             this2->really += event->num_bytes;
   207                             gettimeofday(&this2->last_tv, 0);
   208                             this2->buf_free += event->num_bytes;
   209                         } else {
   210                             this2->buf_free = event->num_bytes;
   211                         }
   212                     }
   213                     break;
   214                 }
   215             }
   216         }
   217     }
   218     return AuTrue;
   219 }
   220 
   221 static AuDeviceID
   222 find_device(_THIS, int nch)
   223 {
   224     int i;
   225     for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
   226         if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
   227              AuComponentKindPhysicalOutput) &&
   228             AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
   229             return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
   230         }
   231     }
   232     return AuNone;
   233 }
   234 
   235 static int
   236 NAS_OpenAudio(_THIS, SDL_AudioSpec * spec)
   237 {
   238     AuElement elms[3];
   239     int buffer_size;
   240     Uint16 test_format, format;
   241 
   242     this->hidden->mixbuf = NULL;
   243 
   244     /* Try for a closest match on audio format */
   245     format = 0;
   246     for (test_format = SDL_FirstAudioFormat(spec->format);
   247          !format && test_format;) {
   248         format = sdlformat_to_auformat(test_format);
   249 
   250         if (format == AuNone) {
   251             test_format = SDL_NextAudioFormat();
   252         }
   253     }
   254     if (format == 0) {
   255         SDL_SetError("Couldn't find any hardware audio formats");
   256         return (-1);
   257     }
   258     spec->format = test_format;
   259 
   260     this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
   261     if (this->hidden->aud == 0) {
   262         SDL_SetError("Couldn't open connection to NAS server");
   263         return (-1);
   264     }
   265 
   266     this->hidden->dev = find_device(this, spec->channels);
   267     if ((this->hidden->dev == AuNone)
   268         || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) {
   269         AuCloseServer(this->hidden->aud);
   270         this->hidden->aud = 0;
   271         SDL_SetError("Couldn't find a fitting playback device on NAS server");
   272         return (-1);
   273     }
   274 
   275     buffer_size = spec->freq;
   276     if (buffer_size < 4096)
   277         buffer_size = 4096;
   278 
   279     if (buffer_size > 32768)
   280         buffer_size = 32768;    /* So that the buffer won't get unmanageably big. */
   281 
   282     /* Calculate the final parameters for this audio specification */
   283     SDL_CalculateAudioSpec(spec);
   284 
   285     this2 = this->hidden;
   286 
   287     AuMakeElementImportClient(elms, spec->freq, format, spec->channels,
   288                               AuTrue, buffer_size, buffer_size / 4, 0, NULL);
   289     AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, spec->freq,
   290                               AuUnlimitedSamples, 0, NULL);
   291     AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
   292                   NULL);
   293     AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
   294                            this->hidden->flow, event_handler,
   295                            (AuPointer) NULL);
   296 
   297     AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
   298 
   299     /* Allocate mixing buffer */
   300     this->hidden->mixlen = spec->size;
   301     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   302     if (this->hidden->mixbuf == NULL) {
   303         return (-1);
   304     }
   305     SDL_memset(this->hidden->mixbuf, spec->silence, spec->size);
   306 
   307     /* Get the parent process id (we're the parent of the audio thread) */
   308     this->hidden->parent = getpid();
   309 
   310     /* We're ready to rock and roll. :-) */
   311     return (0);
   312 }
   313 
   314 /* vi: set ts=4 sw=4 expandtab: */