src/audio/nas/SDL_nasaudio.c
author Ryan C. Gordon
Thu, 05 Oct 2006 01:13:47 +0000
branchSDL-ryan-multiple-audio-device
changeset 3809 7852b5b78af5
parent 3798 c8b3d3d13ed1
child 3821 18393b045759
permissions -rw-r--r--
Cleaning out SDL_audiodev.c and related references ...
     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 artsc audio */
    40 #define NAS_DRIVER_NAME         "nas"
    41 
    42 static struct SDL_PrivateAudioData *this2 = NULL;
    43 
    44 /* Audio driver functions */
    45 static int NAS_OpenAudio(_THIS, SDL_AudioSpec * spec);
    46 static void NAS_WaitAudio(_THIS);
    47 static void NAS_PlayAudio(_THIS);
    48 static Uint8 *NAS_GetAudioBuf(_THIS);
    49 static void NAS_CloseAudio(_THIS);
    50 
    51 /* Audio driver bootstrap functions */
    52 
    53 static int
    54 Audio_Available(void)
    55 {
    56     AuServer *aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
    57     if (!aud)
    58         return 0;
    59 
    60     AuCloseServer(aud);
    61     return 1;
    62 }
    63 
    64 static void
    65 Audio_DeleteDevice(SDL_AudioDevice * device)
    66 {
    67     SDL_free(device->hidden);
    68     SDL_free(device);
    69 }
    70 
    71 static SDL_AudioDevice *
    72 Audio_CreateDevice(int devindex)
    73 {
    74     SDL_AudioDevice *this;
    75 
    76     /* Initialize all variables that we clean on shutdown */
    77     this = (SDL_AudioDevice *) SDL_malloc(sizeof(SDL_AudioDevice));
    78     if (this) {
    79         SDL_memset(this, 0, (sizeof *this));
    80         this->hidden = (struct SDL_PrivateAudioData *)
    81             SDL_malloc((sizeof *this->hidden));
    82     }
    83     if ((this == NULL) || (this->hidden == NULL)) {
    84         SDL_OutOfMemory();
    85         if (this) {
    86             SDL_free(this);
    87         }
    88         return (0);
    89     }
    90     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    91 
    92     /* Set the function pointers */
    93     this->OpenAudio = NAS_OpenAudio;
    94     this->WaitAudio = NAS_WaitAudio;
    95     this->PlayAudio = NAS_PlayAudio;
    96     this->GetAudioBuf = NAS_GetAudioBuf;
    97     this->CloseAudio = NAS_CloseAudio;
    98 
    99     this->free = Audio_DeleteDevice;
   100 
   101     return this;
   102 }
   103 
   104 AudioBootStrap NAS_bootstrap = {
   105     NAS_DRIVER_NAME, "Network Audio System",
   106     Audio_Available, Audio_CreateDevice, 0
   107 };
   108 
   109 /* This function waits until it is possible to write a full sound buffer */
   110 static void
   111 NAS_WaitAudio(_THIS)
   112 {
   113     while (this->hidden->buf_free < this->hidden->mixlen) {
   114         AuEvent ev;
   115         AuNextEvent(this->hidden->aud, AuTrue, &ev);
   116         AuDispatchEvent(this->hidden->aud, &ev);
   117     }
   118 }
   119 
   120 static void
   121 NAS_PlayAudio(_THIS)
   122 {
   123     while (this->hidden->mixlen > this->hidden->buf_free) {     /* We think the buffer is full? Yikes! Ask the server for events,
   124                                                                    in the hope that some of them is LowWater events telling us more
   125                                                                    of the buffer is free now than what we think. */
   126         AuEvent ev;
   127         AuNextEvent(this->hidden->aud, AuTrue, &ev);
   128         AuDispatchEvent(this->hidden->aud, &ev);
   129     }
   130     this->hidden->buf_free -= this->hidden->mixlen;
   131 
   132     /* Write the audio data */
   133     AuWriteElement(this->hidden->aud, this->hidden->flow, 0,
   134                    this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
   135 
   136     this->hidden->written += this->hidden->mixlen;
   137 
   138 #ifdef DEBUG_AUDIO
   139     fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
   140 #endif
   141 }
   142 
   143 static Uint8 *
   144 NAS_GetAudioBuf(_THIS)
   145 {
   146     return (this->hidden->mixbuf);
   147 }
   148 
   149 static void
   150 NAS_CloseAudio(_THIS)
   151 {
   152     if (this->hidden->mixbuf != NULL) {
   153         SDL_FreeAudioMem(this->hidden->mixbuf);
   154         this->hidden->mixbuf = NULL;
   155     }
   156     if (this->hidden->aud) {
   157         AuCloseServer(this->hidden->aud);
   158         this->hidden->aud = 0;
   159     }
   160 }
   161 
   162 static unsigned char
   163 sdlformat_to_auformat(unsigned int fmt)
   164 {
   165     switch (fmt) {
   166     case AUDIO_U8:
   167         return AuFormatLinearUnsigned8;
   168     case AUDIO_S8:
   169         return AuFormatLinearSigned8;
   170     case AUDIO_U16LSB:
   171         return AuFormatLinearUnsigned16LSB;
   172     case AUDIO_U16MSB:
   173         return AuFormatLinearUnsigned16MSB;
   174     case AUDIO_S16LSB:
   175         return AuFormatLinearSigned16LSB;
   176     case AUDIO_S16MSB:
   177         return AuFormatLinearSigned16MSB;
   178     }
   179     return AuNone;
   180 }
   181 
   182 static AuBool
   183 event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
   184 {
   185     switch (ev->type) {
   186     case AuEventTypeElementNotify:
   187         {
   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)
   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
   235 NAS_OpenAudio(_THIS, SDL_AudioSpec * spec)
   236 {
   237     AuElement elms[3];
   238     int buffer_size;
   239     SDL_AudioFormat test_format, format;
   240 
   241     this->hidden->mixbuf = NULL;
   242 
   243     /* Try for a closest match on audio format */
   244     format = 0;
   245     for (test_format = SDL_FirstAudioFormat(spec->format);
   246          !format && test_format;) {
   247         format = sdlformat_to_auformat(test_format);
   248 
   249         if (format == AuNone) {
   250             test_format = SDL_NextAudioFormat();
   251         }
   252     }
   253     if (format == 0) {
   254         SDL_SetError("Couldn't find any hardware audio formats");
   255         return (-1);
   256     }
   257     spec->format = test_format;
   258 
   259     this->hidden->aud = AuOpenServer("", 0, NULL, 0, NULL, NULL);
   260     if (this->hidden->aud == 0) {
   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)
   267         || (!(this->hidden->flow = AuCreateFlow(this->hidden->aud, NULL)))) {
   268         AuCloseServer(this->hidden->aud);
   269         this->hidden->aud = 0;
   270         SDL_SetError("Couldn't find a fitting playback device on NAS server");
   271         return (-1);
   272     }
   273 
   274     buffer_size = spec->freq;
   275     if (buffer_size < 4096)
   276         buffer_size = 4096;
   277 
   278     if (buffer_size > 32768)
   279         buffer_size = 32768;    /* So that the buffer won't get unmanageably big. */
   280 
   281     /* Calculate the final parameters for this audio specification */
   282     SDL_CalculateAudioSpec(spec);
   283 
   284     this2 = this->hidden;
   285 
   286     AuMakeElementImportClient(elms, spec->freq, format, spec->channels,
   287                               AuTrue, buffer_size, buffer_size / 4, 0, NULL);
   288     AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, spec->freq,
   289                               AuUnlimitedSamples, 0, NULL);
   290     AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
   291                   NULL);
   292     AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
   293                            this->hidden->flow, event_handler,
   294                            (AuPointer) NULL);
   295 
   296     AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
   297 
   298     /* Allocate mixing buffer */
   299     this->hidden->mixlen = spec->size;
   300     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   301     if (this->hidden->mixbuf == NULL) {
   302         return (-1);
   303     }
   304     SDL_memset(this->hidden->mixbuf, spec->silence, spec->size);
   305 
   306     /* Get the parent process id (we're the parent of the audio thread) */
   307     this->hidden->parent = getpid();
   308 
   309     /* We're ready to rock and roll. :-) */
   310     return (0);
   311 }
   312 
   313 /* vi: set ts=4 sw=4 expandtab: */