Commit 2cc68064726856ecc1290bdb3191f7bc528556da

Sam Lantinga 2017-07-20T10:39:47

Fixed bug 3705 - Add capture support to the sndio backend kdrakehp The attached patch adds capture support to the sndio backend. The patch also allows the `OpenDevice' function to accept arbitrary device names.

diff --git a/src/audio/sndio/SDL_sndioaudio.c b/src/audio/sndio/SDL_sndioaudio.c
index cef058f..def7090 100644
--- a/src/audio/sndio/SDL_sndioaudio.c
+++ b/src/audio/sndio/SDL_sndioaudio.c
@@ -33,6 +33,7 @@
 #include <signal.h>
 #endif
 
+#include <poll.h>
 #include <unistd.h>
 
 #include "SDL_audio.h"
@@ -47,6 +48,9 @@ static struct sio_hdl * (*SNDIO_sio_open)(const char *, unsigned int, int);
 static void (*SNDIO_sio_close)(struct sio_hdl *);
 static int (*SNDIO_sio_setpar)(struct sio_hdl *, struct sio_par *);
 static int (*SNDIO_sio_getpar)(struct sio_hdl *, struct sio_par *);
+static int (*SNDIO_sio_pollfd)(struct sio_hdl *, struct pollfd *, int);
+static int (*SNDIO_sio_revents)(struct sio_hdl *, struct pollfd *);
+static int (*SNDIO_sio_nfds)(struct sio_hdl *);
 static int (*SNDIO_sio_start)(struct sio_hdl *);
 static int (*SNDIO_sio_stop)(struct sio_hdl *);
 static size_t (*SNDIO_sio_read)(struct sio_hdl *, void *, size_t);
@@ -83,6 +87,9 @@ load_sndio_syms(void)
     SDL_SNDIO_SYM(sio_close);
     SDL_SNDIO_SYM(sio_setpar);
     SDL_SNDIO_SYM(sio_getpar);
+    SDL_SNDIO_SYM(sio_pollfd);
+    SDL_SNDIO_SYM(sio_nfds);
+    SDL_SNDIO_SYM(sio_revents);
     SDL_SNDIO_SYM(sio_start);
     SDL_SNDIO_SYM(sio_stop);
     SDL_SNDIO_SYM(sio_read);
@@ -164,6 +171,44 @@ SNDIO_PlayDevice(_THIS)
 #endif
 }
 
+static int
+SNDIO_CaptureFromDevice(_THIS, void *buffer, int buflen)
+{
+    int revents;
+    int nfds;
+    int i;
+    int r;
+
+    /* Emulate a blocking read */
+    for (i = SNDIO_sio_read(this->hidden->dev, buffer, buflen); i < buflen;) {
+        if ((nfds = SNDIO_sio_pollfd(this->hidden->dev, this->hidden->pfd, POLLIN)) <= 0
+            || poll(this->hidden->pfd, nfds, INFTIM) <= 0) {
+            break;
+        }
+	revents = SNDIO_sio_revents(this->hidden->dev, this->hidden->pfd);
+        if (revents & POLLIN) {
+            if ((r = SNDIO_sio_read(this->hidden->dev, (char *)buffer + i, buflen - i)) == 0) {
+                break;
+            }
+            i += r;
+        }
+        if (revents & POLLHUP) {
+            break;
+        }
+    }
+    return i;
+}
+
+static void
+SNDIO_FlushCapture(_THIS)
+{
+    char buf[512];
+
+    while (SNDIO_sio_read(this->hidden->dev, buf, sizeof(buf)) != 0) {
+        /* do nothing */;
+    }
+}
+
 static Uint8 *
 SNDIO_GetDeviceBuf(_THIS)
 {
@@ -173,6 +218,9 @@ SNDIO_GetDeviceBuf(_THIS)
 static void
 SNDIO_CloseDevice(_THIS)
 {
+    if ( this->hidden->pfd != NULL ) {
+        SDL_free(this->hidden->pfd);
+    }
     if ( this->hidden->dev != NULL ) {
         SNDIO_sio_stop(this->hidden->dev);
         SNDIO_sio_close(this->hidden->dev);
@@ -197,11 +245,19 @@ SNDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 
     this->hidden->mixlen = this->spec.size;
 
-    /* !!! FIXME: SIO_DEVANY can be a specific device... */
-    if ((this->hidden->dev = SNDIO_sio_open(SIO_DEVANY, SIO_PLAY, 0)) == NULL) {
+    /* Capture devices must be non-blocking for SNDIO_FlushCapture */
+    if ((this->hidden->dev =
+        SNDIO_sio_open(devname != NULL ? SIO_DEVANY : devname,
+                       iscapture ? SIO_REC : SIO_PLAY, iscapture)) == NULL) {
         return SDL_SetError("sio_open() failed");
     }
 
+    /* Allocate the pollfd array for capture devices */
+    if (iscapture && (this->hidden->pfd =
+        SDL_malloc(sizeof(struct pollfd) * SNDIO_sio_nfds(this->hidden->dev))) == NULL) {
+        return SDL_OutOfMemory();
+    }
+
     SNDIO_sio_initpar(&par);
 
     par.rate = this->spec.freq;
@@ -300,8 +356,12 @@ SNDIO_Init(SDL_AudioDriverImpl * impl)
     impl->PlayDevice = SNDIO_PlayDevice;
     impl->GetDeviceBuf = SNDIO_GetDeviceBuf;
     impl->CloseDevice = SNDIO_CloseDevice;
+    impl->CaptureFromDevice = SNDIO_CaptureFromDevice;
+    impl->FlushCapture = SNDIO_FlushCapture;
     impl->Deinitialize = SNDIO_Deinitialize;
-    impl->OnlyHasDefaultOutputDevice = 1;  /* !!! FIXME: sndio can handle multiple devices. */
+
+    impl->AllowsArbitraryDeviceNames = 1;
+    impl->HasCaptureSupport = SDL_TRUE;
 
     return 1;   /* this audio target is available. */
 }
diff --git a/src/audio/sndio/SDL_sndioaudio.h b/src/audio/sndio/SDL_sndioaudio.h
index a534b0a..b20be19 100644
--- a/src/audio/sndio/SDL_sndioaudio.h
+++ b/src/audio/sndio/SDL_sndioaudio.h
@@ -23,6 +23,7 @@
 #ifndef _SDL_sndioaudio_h
 #define _SDL_sndioaudio_h
 
+#include <poll.h>
 #include <sndio.h>
 
 #include "../SDL_sysaudio.h"
@@ -38,6 +39,9 @@ struct SDL_PrivateAudioData
     /* Raw mixing buffer */
     Uint8 *mixbuf;
     int mixlen;
+
+    /* Polling structures for non-blocking sndio devices */
+    struct pollfd *pfd;
 };
 
 #endif /* _SDL_sndioaudio_h */