nas: initial shot at audio capture support. Compiles, but not tested.
authorRyan C. Gordon <icculus@icculus.org>
Fri, 05 Aug 2016 04:23:32 -0400
changeset 10259457d9c7868ec
parent 10258 0ba7106e9a6d
child 10260 48c3f46395ae
nas: initial shot at audio capture support. Compiles, but not tested.
src/audio/nas/SDL_nasaudio.c
     1.1 --- a/src/audio/nas/SDL_nasaudio.c	Fri Aug 05 02:04:48 2016 -0400
     1.2 +++ b/src/audio/nas/SDL_nasaudio.c	Fri Aug 05 04:23:32 2016 -0400
     1.3 @@ -33,18 +33,18 @@
     1.4  #include "../SDL_audio_c.h"
     1.5  #include "SDL_nasaudio.h"
     1.6  
     1.7 -static struct SDL_PrivateAudioData *this2 = NULL;
     1.8 -
     1.9 -
    1.10  static void (*NAS_AuCloseServer) (AuServer *);
    1.11  static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *);
    1.12  static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *);
    1.13 +static void (*NAS_AuHandleEvents) (AuServer *);
    1.14  static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *);
    1.15  static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *);
    1.16  static void (*NAS_AuSetElements)
    1.17    (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
    1.18  static void (*NAS_AuWriteElement)
    1.19    (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
    1.20 +static AuUint32 (*NAS_AuReadElement)
    1.21 +  (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuStatus *);
    1.22  static AuServer *(*NAS_AuOpenServer)
    1.23    (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
    1.24  static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
    1.25 @@ -79,10 +79,12 @@
    1.26      SDL_NAS_SYM(AuCloseServer);
    1.27      SDL_NAS_SYM(AuNextEvent);
    1.28      SDL_NAS_SYM(AuDispatchEvent);
    1.29 +    SDL_NAS_SYM(AuHandleEvents);
    1.30      SDL_NAS_SYM(AuCreateFlow);
    1.31      SDL_NAS_SYM(AuStartFlow);
    1.32      SDL_NAS_SYM(AuSetElements);
    1.33      SDL_NAS_SYM(AuWriteElement);
    1.34 +    SDL_NAS_SYM(AuReadElement);
    1.35      SDL_NAS_SYM(AuOpenServer);
    1.36      SDL_NAS_SYM(AuRegisterEventHandler);
    1.37      return 0;
    1.38 @@ -186,6 +188,45 @@
    1.39      return (this->hidden->mixbuf);
    1.40  }
    1.41  
    1.42 +static int
    1.43 +NAS_CaptureFromDevice(_THIS, void *buffer, int buflen)
    1.44 +{
    1.45 +    struct SDL_PrivateAudioData *h = this->hidden;
    1.46 +    int retval;
    1.47 +
    1.48 +    while (SDL_TRUE) {
    1.49 +        /* just keep the event queue moving and the server chattering. */
    1.50 +        NAS_AuHandleEvents(h->aud);
    1.51 +    
    1.52 +        retval = (int) NAS_AuReadElement(h->aud, h->flow, 1, buflen, buffer, NULL);
    1.53 +        /*printf("read %d capture bytes\n", (int) retval);*/
    1.54 +        if (retval == 0) {
    1.55 +            SDL_Delay(10);  /* don't burn the CPU if we're waiting for data. */
    1.56 +        } else {
    1.57 +            break;
    1.58 +        }
    1.59 +    }
    1.60 +
    1.61 +    return retval;
    1.62 +}
    1.63 +
    1.64 +static void
    1.65 +NAS_FlushCapture(_THIS)
    1.66 +{
    1.67 +    struct SDL_PrivateAudioData *h = this->hidden;
    1.68 +    AuUint32 total = 0;
    1.69 +    AuUint32 br;
    1.70 +    Uint8 buf[512];
    1.71 +
    1.72 +    do {
    1.73 +        /* just keep the event queue moving and the server chattering. */
    1.74 +        NAS_AuHandleEvents(h->aud);
    1.75 +        br = NAS_AuReadElement(h->aud, h->flow, 1, sizeof (buf), buf, NULL);
    1.76 +        /*printf("flushed %d capture bytes\n", (int) br);*/
    1.77 +        total += br;
    1.78 +    } while ((br == sizeof (buf)) && (total < this->spec.size));
    1.79 +}
    1.80 +
    1.81  static void
    1.82  NAS_CloseDevice(_THIS)
    1.83  {
    1.84 @@ -219,6 +260,12 @@
    1.85  static AuBool
    1.86  event_handler(AuServer * aud, AuEvent * ev, AuEventHandlerRec * hnd)
    1.87  {
    1.88 +    SDL_AudioDevice *this = (SDL_AudioDevice *) hnd->data;
    1.89 +    struct SDL_PrivateAudioData *h = this->hidden;
    1.90 +    if (this->iscapture) {
    1.91 +        return AuTrue;  /* we don't (currently) care about any of this for capture devices */
    1.92 +    }
    1.93 +
    1.94      switch (ev->type) {
    1.95      case AuEventTypeElementNotify:
    1.96          {
    1.97 @@ -226,24 +273,24 @@
    1.98  
    1.99              switch (event->kind) {
   1.100              case AuElementNotifyKindLowWater:
   1.101 -                if (this2->buf_free >= 0) {
   1.102 -                    this2->really += event->num_bytes;
   1.103 -                    gettimeofday(&this2->last_tv, 0);
   1.104 -                    this2->buf_free += event->num_bytes;
   1.105 +                if (h->buf_free >= 0) {
   1.106 +                    h->really += event->num_bytes;
   1.107 +                    gettimeofday(&h->last_tv, 0);
   1.108 +                    h->buf_free += event->num_bytes;
   1.109                  } else {
   1.110 -                    this2->buf_free = event->num_bytes;
   1.111 +                    h->buf_free = event->num_bytes;
   1.112                  }
   1.113                  break;
   1.114              case AuElementNotifyKindState:
   1.115                  switch (event->cur_state) {
   1.116                  case AuStatePause:
   1.117                      if (event->reason != AuReasonUser) {
   1.118 -                        if (this2->buf_free >= 0) {
   1.119 -                            this2->really += event->num_bytes;
   1.120 -                            gettimeofday(&this2->last_tv, 0);
   1.121 -                            this2->buf_free += event->num_bytes;
   1.122 +                        if (h->buf_free >= 0) {
   1.123 +                            h->really += event->num_bytes;
   1.124 +                            gettimeofday(&h->last_tv, 0);
   1.125 +                            h->buf_free += event->num_bytes;
   1.126                          } else {
   1.127 -                            this2->buf_free = event->num_bytes;
   1.128 +                            h->buf_free = event->num_bytes;
   1.129                          }
   1.130                      }
   1.131                      break;
   1.132 @@ -255,15 +302,29 @@
   1.133  }
   1.134  
   1.135  static AuDeviceID
   1.136 -find_device(_THIS, int nch)
   1.137 +find_device(_THIS)
   1.138  {
   1.139      /* These "Au" things are all macros, not functions... */
   1.140 +    struct SDL_PrivateAudioData *h = this->hidden;
   1.141 +    const unsigned int devicekind = this->iscapture ? AuComponentKindPhysicalInput : AuComponentKindPhysicalOutput;
   1.142 +    const int numdevs = AuServerNumDevices(h->aud);
   1.143 +    const int nch = this->spec.channels;
   1.144      int i;
   1.145 -    for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
   1.146 -        if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
   1.147 -             AuComponentKindPhysicalOutput) &&
   1.148 -            AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
   1.149 -            return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
   1.150 +
   1.151 +    /* Try to find exact match on channels first... */
   1.152 +    for (i = 0; i < numdevs; i++) {
   1.153 +        const AuDeviceAttributes *dev = AuServerDevice(h->aud, i);
   1.154 +        if ((AuDeviceKind(dev) == devicekind) && (AuDeviceNumTracks(dev) == nch)) {
   1.155 +            return AuDeviceIdentifier(dev);
   1.156 +        }
   1.157 +    }
   1.158 +
   1.159 +    /* Take anything, then... */
   1.160 +    for (i = 0; i < numdevs; i++) {
   1.161 +        const AuDeviceAttributes *dev = AuServerDevice(h->aud, i);
   1.162 +        if (AuDeviceKind(dev) == devicekind) {
   1.163 +            this->spec.channels = AuDeviceNumTracks(dev);
   1.164 +            return AuDeviceIdentifier(dev);
   1.165          }
   1.166      }
   1.167      return AuNone;
   1.168 @@ -303,7 +364,7 @@
   1.169          return SDL_SetError("NAS: Couldn't open connection to NAS server");
   1.170      }
   1.171  
   1.172 -    this->hidden->dev = find_device(this, this->spec.channels);
   1.173 +    this->hidden->dev = find_device(this);
   1.174      if ((this->hidden->dev == AuNone)
   1.175          || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, 0)))) {
   1.176          return SDL_SetError("NAS: Couldn't find a fitting device on NAS server");
   1.177 @@ -319,28 +380,38 @@
   1.178      /* Calculate the final parameters for this audio specification */
   1.179      SDL_CalculateAudioSpec(&this->spec);
   1.180  
   1.181 -    this2 = this->hidden;
   1.182 +    if (iscapture) {
   1.183 +        AuMakeElementImportDevice(elms, this->spec.freq, this->hidden->dev,
   1.184 +                                  AuUnlimitedSamples, 0, NULL);
   1.185 +        AuMakeElementExportClient(elms + 1, 0, this->spec.freq, format,
   1.186 +                                  this->spec.channels, AuTrue, buffer_size,
   1.187 +                                  buffer_size, 0, NULL);
   1.188 +    } else {
   1.189 +        AuMakeElementImportClient(elms, this->spec.freq, format,
   1.190 +                                  this->spec.channels, AuTrue, buffer_size,
   1.191 +                                  buffer_size / 4, 0, NULL);
   1.192 +        AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
   1.193 +                                  AuUnlimitedSamples, 0, NULL);
   1.194 +    }
   1.195  
   1.196 -    AuMakeElementImportClient(elms, this->spec.freq, format,
   1.197 -                              this->spec.channels, AuTrue, buffer_size,
   1.198 -                              buffer_size / 4, 0, NULL);
   1.199 -    AuMakeElementExportDevice(elms + 1, 0, this->hidden->dev, this->spec.freq,
   1.200 -                              AuUnlimitedSamples, 0, NULL);
   1.201 -    NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms,
   1.202 -                      NULL);
   1.203 +    NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue,
   1.204 +                      2, elms, NULL);
   1.205 +
   1.206      NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0,
   1.207                                 this->hidden->flow, event_handler,
   1.208 -                               (AuPointer) NULL);
   1.209 +                               (AuPointer) this);
   1.210  
   1.211      NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
   1.212  
   1.213      /* Allocate mixing buffer */
   1.214 -    this->hidden->mixlen = this->spec.size;
   1.215 -    this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
   1.216 -    if (this->hidden->mixbuf == NULL) {
   1.217 -        return SDL_OutOfMemory();
   1.218 +    if (!iscapture) {
   1.219 +        this->hidden->mixlen = this->spec.size;
   1.220 +        this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
   1.221 +        if (this->hidden->mixbuf == NULL) {
   1.222 +            return SDL_OutOfMemory();
   1.223 +        }
   1.224 +        SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   1.225      }
   1.226 -    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   1.227  
   1.228      /* We're ready to rock and roll. :-) */
   1.229      return 0;
   1.230 @@ -371,9 +442,14 @@
   1.231      impl->PlayDevice = NAS_PlayDevice;
   1.232      impl->WaitDevice = NAS_WaitDevice;
   1.233      impl->GetDeviceBuf = NAS_GetDeviceBuf;
   1.234 +    impl->CaptureFromDevice = NAS_CaptureFromDevice;
   1.235 +    impl->FlushCapture = NAS_FlushCapture;
   1.236      impl->CloseDevice = NAS_CloseDevice;
   1.237      impl->Deinitialize = NAS_Deinitialize;
   1.238 -    impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: is this true? */
   1.239 +
   1.240 +    impl->OnlyHasDefaultOutputDevice = 1;
   1.241 +    impl->OnlyHasDefaultCaptureDevice = 1;
   1.242 +    impl->HasCaptureSupport = SDL_TRUE;
   1.243  
   1.244      return 1;   /* this audio target is available. */
   1.245  }