src/audio/nas/SDL_nasaudio.c

/* [<][>]
[^][v][top][bottom][index][help] */

FUNCTIONS

This source file includes following functions.
  1. Audio_Available
  2. Audio_DeleteDevice
  3. Audio_CreateDevice
  4. NAS_WaitAudio
  5. NAS_PlayAudio
  6. NAS_GetAudioBuf
  7. NAS_CloseAudio
  8. sdlformat_to_auformat
  9. event_handler
  10. find_device
  11. NAS_OpenAudio

   1 /*
   2     SDL - Simple DirectMedia Layer
   3     Copyright (C) 1997, 1998, 1999, 2000, 2001  Sam Lantinga
   4 
   5     This library is free software; you can redistribute it and/or
   6     modify it under the terms of the GNU Library General Public
   7     License as published by the Free Software Foundation; either
   8     version 2 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     Library General Public License for more details.
  14 
  15     You should have received a copy of the GNU Library General Public
  16     License along with this library; if not, write to the Free
  17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18 
  19     Sam Lantinga
  20     slouken@devolution.com
  21 
  22     This driver was written by:
  23     Erik Inge Bolsų
  24     knan@mo.himolde.no
  25 */
  26 
  27 #ifdef SAVE_RCSID
  28 static char rcsid =
  29  "@(#) $Id: SDL_nasaudio.c,v 1.1.2.2 2001/02/10 07:20:03 hercules Exp $";
  30 #endif
  31 
  32 /* Allow access to a raw mixing buffer */
  33 
  34 #include <stdlib.h>
  35 #include <stdio.h>
  36 #include <string.h>
  37 #include <errno.h>
  38 #include <signal.h>
  39 #include <unistd.h>
  40 
  41 #include "SDL_audio.h"
  42 #include "SDL_error.h"
  43 #include "SDL_audiomem.h"
  44 #include "SDL_audio_c.h"
  45 #include "SDL_timer.h"
  46 #include "SDL_audiodev_c.h"
  47 #include "SDL_nasaudio.h"
  48 
  49 /* The tag name used by artsc audio */
  50 #define NAS_DRIVER_NAME         "nas"
  51 
  52 static struct SDL_PrivateAudioData *this2 = NULL;
  53 
  54 /* Audio driver functions */
  55 static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec);
  56 static void NAS_WaitAudio(_THIS);
  57 static void NAS_PlayAudio(_THIS);
  58 static Uint8 *NAS_GetAudioBuf(_THIS);
  59 static void NAS_CloseAudio(_THIS);
  60 
  61 /* Audio driver bootstrap functions */
  62 
  63 static int Audio_Available(void)
     /* [<][>][^][v][top][bottom][index][help] */
  64 {
  65         AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
  66         if (!aud) return 0;
  67 
  68         AuCloseServer(aud);
  69         return 1;
  70 }
  71 
  72 static void Audio_DeleteDevice(SDL_AudioDevice *device)
     /* [<][>][^][v][top][bottom][index][help] */
  73 {
  74         free(device->hidden);
  75         free(device);
  76 }
  77 
  78 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
     /* [<][>][^][v][top][bottom][index][help] */
  79 {
  80         SDL_AudioDevice *this;
  81 
  82         /* Initialize all variables that we clean on shutdown */
  83         this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
  84         if ( this ) {
  85                 memset(this, 0, (sizeof *this));
  86                 this->hidden = (struct SDL_PrivateAudioData *)
  87                                 malloc((sizeof *this->hidden));
  88         }
  89         if ( (this == NULL) || (this->hidden == NULL) ) {
  90                 SDL_OutOfMemory();
  91                 if ( this ) {
  92                         free(this);
  93                 }
  94                 return(0);
  95         }
  96         memset(this->hidden, 0, (sizeof *this->hidden));
  97 
  98         /* Set the function pointers */
  99         this->OpenAudio = NAS_OpenAudio;
 100         this->WaitAudio = NAS_WaitAudio;
 101         this->PlayAudio = NAS_PlayAudio;
 102         this->GetAudioBuf = NAS_GetAudioBuf;
 103         this->CloseAudio = NAS_CloseAudio;
 104 
 105         this->free = Audio_DeleteDevice;
 106 
 107         return this;
 108 }
 109 
 110 AudioBootStrap NAS_bootstrap = {
 111         NAS_DRIVER_NAME, "Network Audio System",
 112         Audio_Available, Audio_CreateDevice
 113 };
 114 
 115 /* This function waits until it is possible to write a full sound buffer */
 116 static void NAS_WaitAudio(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 117 {
 118         while ( this->hidden->buf_free < this->hidden->mixlen ) {
 119                 AuEvent ev;
 120                 AuNextEvent(this->hidden->aud, AuTrue, &ev);
 121                 AuDispatchEvent(this->hidden->aud, &ev);
 122         }
 123 }
 124 
 125 static void NAS_PlayAudio(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 126 {
 127         while (this->hidden->mixlen > this->hidden->buf_free) { /* We think the buffer is full? Yikes! Ask the server for events,
 128                                     in the hope that some of them is LowWater events telling us more
 129                                     of the buffer is free now than what we think. */
 130                 AuEvent ev;
 131                 AuNextEvent(this->hidden->aud, AuTrue, &ev);
 132                 AuDispatchEvent(this->hidden->aud, &ev);
 133         }
 134         this->hidden->buf_free -= this->hidden->mixlen;
 135 
 136         /* Write the audio data */
 137         AuWriteElement(this->hidden->aud, this->hidden->flow, 0, this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
 138 
 139         this->hidden->written += this->hidden->mixlen;
 140         
 141 #ifdef DEBUG_AUDIO
 142         fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
 143 #endif
 144 }
 145 
 146 static Uint8 *NAS_GetAudioBuf(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 147 {
 148         return(this->hidden->mixbuf);
 149 }
 150 
 151 static void NAS_CloseAudio(_THIS)
     /* [<][>][^][v][top][bottom][index][help] */
 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 sdlformat_to_auformat(unsigned int fmt)
     /* [<][>][^][v][top][bottom][index][help] */
 164 {
 165   switch (fmt)
 166     {
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 185 {
 186         switch (ev->type) {
 187         case AuEventTypeElementNotify: {
 188                 AuElementNotifyEvent* event = (AuElementNotifyEvent *)ev;
 189 
 190                 switch (event->kind) {
 191                 case AuElementNotifyKindLowWater:
 192                         if (this2->buf_free >= 0) {
 193                                 this2->really += event->num_bytes;
 194                                 gettimeofday(&this2->last_tv, 0);
 195                                 this2->buf_free += event->num_bytes;
 196                         } else {
 197                                 this2->buf_free = event->num_bytes;
 198                         }
 199                         break;
 200                 case AuElementNotifyKindState:
 201                         switch (event->cur_state) {
 202                         case AuStatePause:
 203                                 if (event->reason != AuReasonUser) {
 204                                         if (this2->buf_free >= 0) {
 205                                                 this2->really += event->num_bytes;
 206                                                 gettimeofday(&this2->last_tv, 0);
 207                                                 this2->buf_free += event->num_bytes;
 208                                         } else {
 209                                                 this2->buf_free = event->num_bytes;
 210                                         }
 211                                 }
 212                                 break;
 213                         }
 214                 }
 215         }
 216         }
 217         return AuTrue;
 218 }
 219 
 220 static AuDeviceID
 221 find_device(_THIS, int nch)
     /* [<][>][^][v][top][bottom][index][help] */
 222 {
 223         int i;
 224         for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
 225                 if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
 226                                 AuComponentKindPhysicalOutput) &&
 227                         AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
 228                         return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
 229                 }
 230         }
 231         return AuNone;
 232 }
 233 
 234 static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec)
     /* [<][>][^][v][top][bottom][index][help] */
 235 {
 236         AuElement elms[3];
 237         int buffer_size;
 238         Uint16 test_format, format;
 239 
 240         this->hidden->mixbuf = NULL;
 241 
 242         /* Try for a closest match on audio format */
 243         format = 0;
 244         for ( test_format = SDL_FirstAudioFormat(spec->format);
 245                                                 ! format && test_format; ) {
 246                 format = sdlformat_to_auformat(test_format);
 247 
 248                 if (format == AuNone) {
 249                         test_format = SDL_NextAudioFormat();
 250                 }
 251         }
 252         if ( format == 0 ) {
 253                 SDL_SetError("Couldn't find any hardware audio formats");
 254                 return(-1);
 255         }
 256         spec->format = test_format;
 257 
 258         this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
 259         if (this->hidden->aud == 0)
 260         {
 261                 SDL_SetError("Couldn't open connection to NAS server");
 262                 return (-1);
 263         }
 264         
 265         this->hidden->dev = find_device(this, spec->channels);
 266         if ((this->hidden->dev == AuNone) || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) {
 267                 AuCloseServer(this->hidden->aud);
 268                 this->hidden->aud = 0;
 269                 SDL_SetError("Couldn't find a fitting playback device on NAS server");
 270                 return (-1);
 271         }
 272         
 273         buffer_size = spec->freq;
 274         if (buffer_size < 4096)
 275                 buffer_size = 4096; 
 276 
 277         if (buffer_size > 32768)
 278                 buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
 279 
 280         /* Calculate the final parameters for this audio specification */
 281         SDL_CalculateAudioSpec(spec);
 282 
 283         this2 = this->hidden;
 284 
 285         AuMakeElementImportClient(elms, spec->freq, format, spec->channels, AuTrue,
 286                                 buffer_size, buffer_size / 4, 0, NULL);
 287         AuMakeElementExportDevice(elms+1, 0, this->hidden->dev, spec->freq,
 288                                 AuUnlimitedSamples, 0, NULL);
 289         AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL);
 290         AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, this->hidden->flow,
 291                                 event_handler, (AuPointer) NULL);
 292 
 293         AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
 294 
 295         /* Allocate mixing buffer */
 296         this->hidden->mixlen = spec->size;
 297         this->hidden->mixbuf = (Uint8 *)SDL_AllocAudioMem(this->hidden->mixlen);
 298         if ( this->hidden->mixbuf == NULL ) {
 299                 return(-1);
 300         }
 301         memset(this->hidden->mixbuf, spec->silence, spec->size);
 302 
 303         /* Get the parent process id (we're the parent of the audio thread) */
 304         this->hidden->parent = getpid();
 305 
 306         /* We're ready to rock and roll. :-) */
 307         return(0);
 308 }

/* [<][>][^][v][top][bottom][index][help] */