src/audio/nas/SDL_nasaudio.c
author Sam Lantinga
Sun, 28 May 2006 13:04:16 +0000
branchSDL-1.3
changeset 1662 782fd950bd46
parent 1402 d910939febfa
child 1668 4da1ee79c9af
permissions -rw-r--r--
Revamp of the video system in progress - adding support for multiple displays, multiple windows, and a full video mode selection API.

WARNING: None of the video drivers have been updated for the new API yet! The API is still under design and very fluid.

The code is now run through a consistent indent format:
indent -i4 -nut -nsc -br -ce

The headers are being converted to automatically generate doxygen documentation.
     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,
   136                     NULL);
   137 
   138     this->hidden->written += this->hidden->mixlen;
   139 
   140 #ifdef DEBUG_AUDIO
   141     fprintf (stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
   142 #endif
   143 }
   144 
   145 static Uint8 *
   146 NAS_GetAudioBuf (_THIS)
   147 {
   148     return (this->hidden->mixbuf);
   149 }
   150 
   151 static void
   152 NAS_CloseAudio (_THIS)
   153 {
   154     if (this->hidden->mixbuf != NULL) {
   155         SDL_FreeAudioMem (this->hidden->mixbuf);
   156         this->hidden->mixbuf = NULL;
   157     }
   158     if (this->hidden->aud) {
   159         AuCloseServer (this->hidden->aud);
   160         this->hidden->aud = 0;
   161     }
   162 }
   163 
   164 static unsigned char
   165 sdlformat_to_auformat (unsigned int fmt)
   166 {
   167     switch (fmt) {
   168     case AUDIO_U8:
   169         return AuFormatLinearUnsigned8;
   170     case AUDIO_S8:
   171         return AuFormatLinearSigned8;
   172     case AUDIO_U16LSB:
   173         return AuFormatLinearUnsigned16LSB;
   174     case AUDIO_U16MSB:
   175         return AuFormatLinearUnsigned16MSB;
   176     case AUDIO_S16LSB:
   177         return AuFormatLinearSigned16LSB;
   178     case AUDIO_S16MSB:
   179         return AuFormatLinearSigned16MSB;
   180     }
   181     return AuNone;
   182 }
   183 
   184 static AuBool
   185 event_handler (AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
   186 {
   187     switch (ev->type) {
   188     case AuEventTypeElementNotify:
   189         {
   190             AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
   191 
   192             switch (event->kind) {
   193             case AuElementNotifyKindLowWater:
   194                 if (this2->buf_free >= 0) {
   195                     this2->really += event->num_bytes;
   196                     gettimeofday (&this2->last_tv, 0);
   197                     this2->buf_free += event->num_bytes;
   198                 } else {
   199                     this2->buf_free = event->num_bytes;
   200                 }
   201                 break;
   202             case AuElementNotifyKindState:
   203                 switch (event->cur_state) {
   204                 case AuStatePause:
   205                     if (event->reason != AuReasonUser) {
   206                         if (this2->buf_free >= 0) {
   207                             this2->really += event->num_bytes;
   208                             gettimeofday (&this2->last_tv, 0);
   209                             this2->buf_free += event->num_bytes;
   210                         } else {
   211                             this2->buf_free = event->num_bytes;
   212                         }
   213                     }
   214                     break;
   215                 }
   216             }
   217         }
   218     }
   219     return AuTrue;
   220 }
   221 
   222 static AuDeviceID
   223 find_device (_THIS, int nch)
   224 {
   225     int i;
   226     for (i = 0; i < AuServerNumDevices (this->hidden->aud); i++) {
   227         if ((AuDeviceKind (AuServerDevice (this->hidden->aud, i)) ==
   228              AuComponentKindPhysicalOutput) &&
   229             AuDeviceNumTracks (AuServerDevice (this->hidden->aud, i)) ==
   230             nch) {
   231             return AuDeviceIdentifier (AuServerDevice (this->hidden->aud, i));
   232         }
   233     }
   234     return AuNone;
   235 }
   236 
   237 static int
   238 NAS_OpenAudio (_THIS, SDL_AudioSpec * spec)
   239 {
   240     AuElement elms[3];
   241     int buffer_size;
   242     Uint16 test_format, format;
   243 
   244     this->hidden->mixbuf = NULL;
   245 
   246     /* Try for a closest match on audio format */
   247     format = 0;
   248     for (test_format = SDL_FirstAudioFormat (spec->format);
   249          !format && test_format;) {
   250         format = sdlformat_to_auformat (test_format);
   251 
   252         if (format == AuNone) {
   253             test_format = SDL_NextAudioFormat ();
   254         }
   255     }
   256     if (format == 0) {
   257         SDL_SetError ("Couldn't find any hardware audio formats");
   258         return (-1);
   259     }
   260     spec->format = test_format;
   261 
   262     this->hidden->aud = AuOpenServer ("", 0, NULL, 0, NULL, NULL);
   263     if (this->hidden->aud == 0) {
   264         SDL_SetError ("Couldn't open connection to NAS server");
   265         return (-1);
   266     }
   267 
   268     this->hidden->dev = find_device (this, spec->channels);
   269     if ((this->hidden->dev == AuNone)
   270         || (!(this->hidden->flow = AuCreateFlow (this->hidden->aud, NULL)))) {
   271         AuCloseServer (this->hidden->aud);
   272         this->hidden->aud = 0;
   273         SDL_SetError
   274             ("Couldn't find a fitting playback device on NAS server");
   275         return (-1);
   276     }
   277 
   278     buffer_size = spec->freq;
   279     if (buffer_size < 4096)
   280         buffer_size = 4096;
   281 
   282     if (buffer_size > 32768)
   283         buffer_size = 32768;    /* So that the buffer won't get unmanageably big. */
   284 
   285     /* Calculate the final parameters for this audio specification */
   286     SDL_CalculateAudioSpec (spec);
   287 
   288     this2 = this->hidden;
   289 
   290     AuMakeElementImportClient (elms, spec->freq, format, spec->channels,
   291                                AuTrue, buffer_size, buffer_size / 4, 0, NULL);
   292     AuMakeElementExportDevice (elms + 1, 0, this->hidden->dev, spec->freq,
   293                                AuUnlimitedSamples, 0, NULL);
   294     AuSetElements (this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
   295                    NULL);
   296     AuRegisterEventHandler (this->hidden->aud, AuEventHandlerIDMask, 0,
   297                             this->hidden->flow, event_handler,
   298                             (AuPointer) NULL);
   299 
   300     AuStartFlow (this->hidden->aud, this->hidden->flow, NULL);
   301 
   302     /* Allocate mixing buffer */
   303     this->hidden->mixlen = spec->size;
   304     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem (this->hidden->mixlen);
   305     if (this->hidden->mixbuf == NULL) {
   306         return (-1);
   307     }
   308     SDL_memset (this->hidden->mixbuf, spec->silence, spec->size);
   309 
   310     /* Get the parent process id (we're the parent of the audio thread) */
   311     this->hidden->parent = getpid ();
   312 
   313     /* We're ready to rock and roll. :-) */
   314     return (0);
   315 }
   316 
   317 /* vi: set ts=4 sw=4 expandtab: */