Commit 9dd8477a21ec768d5dd7b3f9c2521d407b5ca6fc

Ryan C. Gordon 2016-08-03T01:57:41

bsdaudio: first shot at audio capture support! (untested, uncompiled...for now.)

diff --git a/src/audio/bsd/SDL_bsdaudio.c b/src/audio/bsd/SDL_bsdaudio.c
index 07fd7fa..d55a9d3 100644
--- a/src/audio/bsd/SDL_bsdaudio.c
+++ b/src/audio/bsd/SDL_bsdaudio.c
@@ -63,13 +63,17 @@ BSDAUDIO_Status(_THIS)
 #ifdef DEBUG_AUDIO
     /* *INDENT-OFF* */
     audio_info_t info;
+    const audio_prinfo *prinfo;
 
     if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
         fprintf(stderr, "AUDIO_GETINFO failed.\n");
         return;
     }
+
+    prinfo = this->iscapture ? &info.play : &info.record;
+
     fprintf(stderr, "\n"
-            "[play/record info]\n"
+            "[%s info]\n"
             "buffer size	:   %d bytes\n"
             "sample rate	:   %i Hz\n"
             "channels	:   %i\n"
@@ -83,18 +87,19 @@ BSDAUDIO_Status(_THIS)
             "waiting		:   %s\n"
             "active		:   %s\n"
             "",
-            info.play.buffer_size,
-            info.play.sample_rate,
-            info.play.channels,
-            info.play.precision,
-            info.play.encoding,
-            info.play.seek,
-            info.play.samples,
-            info.play.eof,
-            info.play.pause ? "yes" : "no",
-            info.play.error ? "yes" : "no",
-            info.play.waiting ? "yes" : "no",
-            info.play.active ? "yes" : "no");
+            this->iscapture ? "record" : "play",
+            prinfo->buffer_size,
+            prinfo->sample_rate,
+            prinfo->channels,
+            prinfo->precision,
+            prinfo->encoding,
+            prinfo->seek,
+            prinfo->samples,
+            prinfo->eof,
+            prinfo->pause ? "yes" : "no",
+            prinfo->error ? "yes" : "no",
+            prinfo->waiting ? "yes" : "no",
+            prinfo->active ? "yes" : "no");
 
     fprintf(stderr, "\n"
             "[audio info]\n"
@@ -209,6 +214,57 @@ BSDAUDIO_GetDeviceBuf(_THIS)
     return (this->hidden->mixbuf);
 }
 
+
+static int
+BSDAUDIO_CaptureFromDevice(_THIS, void *_buffer, int buflen)
+{
+    Uint8 *buffer = (Uint8 *) _buffer;
+    int br, p = 0;
+
+    /* Write the audio data, checking for EAGAIN on broken audio drivers */
+    do {
+        br = read(this->hidden->audio_fd, buffer + p, buflen - p);
+        if (br > 0)
+            p += br;
+        if (br == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) {
+            /* Non recoverable error has occurred. It should be reported!!! */
+            perror("audio");
+            return p ? p : -1;
+        }
+
+#ifdef DEBUG_AUDIO
+        fprintf(stderr, "Captured %d bytes of audio data\n", br);
+#endif
+
+        if (p < buflen
+            || ((br < 0) && ((errno == 0) || (errno == EAGAIN)))) {
+            SDL_Delay(1);       /* Let a little CPU time go by */
+        }
+    } while (p < buflen);
+}
+
+static void
+BSDAUDIO_FlushCapture(_THIS)
+{
+    audio_info_t info;
+    size_t remain;
+    Uint8 buf[512];
+
+    if (ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info) < 0) {
+        return;  /* oh well. */
+    }
+
+    remain = (size_t) (info.record.samples * (SDL_AUDIO_BITSIZE(this->spec.format) / 8));
+    while (remain > 0) {
+        const size_t len = SDL_min(sizeof (buf), remain);
+        const int br = read(this->hidden->audio_fd, buf, len);
+        if (br <= 0) {
+            return;  /* oh well. */
+        }
+        remain -= br;
+    }
+}
+
 static void
 BSDAUDIO_CloseDevice(_THIS)
 {
@@ -227,9 +283,10 @@ BSDAUDIO_CloseDevice(_THIS)
 static int
 BSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
 {
-    const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
+    const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT;
     SDL_AudioFormat format = 0;
     audio_info_t info;
+    audio_prinfo *prinfo = iscapture ? &info.play : &info.record;
 
     /* We don't care what the devname is...we'll try to open anything. */
     /*  ...but default to first name in the list... */
@@ -260,7 +317,7 @@ BSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     SDL_CalculateAudioSpec(&this->spec);
 
     /* Set to play mode */
-    info.mode = AUMODE_PLAY;
+    info.mode = iscapture ? AUMODE_RECORD : AUMODE_PLAY;
     if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) < 0) {
         BSDAUDIO_CloseDevice(this);
         return SDL_SetError("Couldn't put device into play mode");
@@ -271,28 +328,28 @@ BSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
          format; format = SDL_NextAudioFormat()) {
         switch (format) {
         case AUDIO_U8:
-            info.play.encoding = AUDIO_ENCODING_ULINEAR;
-            info.play.precision = 8;
+            prinfo->encoding = AUDIO_ENCODING_ULINEAR;
+            prinfo->precision = 8;
             break;
         case AUDIO_S8:
-            info.play.encoding = AUDIO_ENCODING_SLINEAR;
-            info.play.precision = 8;
+            prinfo->encoding = AUDIO_ENCODING_SLINEAR;
+            prinfo->precision = 8;
             break;
         case AUDIO_S16LSB:
-            info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
-            info.play.precision = 16;
+            prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE;
+            prinfo->precision = 16;
             break;
         case AUDIO_S16MSB:
-            info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
-            info.play.precision = 16;
+            prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE;
+            prinfo->precision = 16;
             break;
         case AUDIO_U16LSB:
-            info.play.encoding = AUDIO_ENCODING_ULINEAR_LE;
-            info.play.precision = 16;
+            prinfo->encoding = AUDIO_ENCODING_ULINEAR_LE;
+            prinfo->precision = 16;
             break;
         case AUDIO_U16MSB:
-            info.play.encoding = AUDIO_ENCODING_ULINEAR_BE;
-            info.play.precision = 16;
+            prinfo->encoding = AUDIO_ENCODING_ULINEAR_BE;
+            prinfo->precision = 16;
             break;
         default:
             continue;
@@ -311,26 +368,29 @@ BSDAUDIO_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
     this->spec.format = format;
 
     AUDIO_INITINFO(&info);
-    info.play.channels = this->spec.channels;
+    prinfo->channels = this->spec.channels;
     if (ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info) == -1) {
         this->spec.channels = 1;
     }
     AUDIO_INITINFO(&info);
-    info.play.sample_rate = this->spec.freq;
+    prinfo->sample_rate = this->spec.freq;
     info.blocksize = this->spec.size;
     info.hiwat = 5;
     info.lowat = 3;
     (void) ioctl(this->hidden->audio_fd, AUDIO_SETINFO, &info);
     (void) ioctl(this->hidden->audio_fd, AUDIO_GETINFO, &info);
-    this->spec.freq = info.play.sample_rate;
-    /* Allocate mixing buffer */
-    this->hidden->mixlen = this->spec.size;
-    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
-    if (this->hidden->mixbuf == NULL) {
-        BSDAUDIO_CloseDevice(this);
-        return SDL_OutOfMemory();
+    this->spec.freq = prinfo->sample_rate;
+
+    if (!iscapture) {
+        /* Allocate mixing buffer */
+        this->hidden->mixlen = this->spec.size;
+        this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
+        if (this->hidden->mixbuf == NULL) {
+            BSDAUDIO_CloseDevice(this);
+            return SDL_OutOfMemory();
+        }
+        SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
     }
-    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
 
     BSDAUDIO_Status(this);
 
@@ -348,7 +408,10 @@ BSDAUDIO_Init(SDL_AudioDriverImpl * impl)
     impl->WaitDevice = BSDAUDIO_WaitDevice;
     impl->GetDeviceBuf = BSDAUDIO_GetDeviceBuf;
     impl->CloseDevice = BSDAUDIO_CloseDevice;
+    impl->CaptureFromDevice = BSDAUDIO_CaptureFromDevice;
+    impl->FlushCapture = BSDAUDIO_FlushCapture;
 
+    impl->HasCaptureSupport = SDL_TRUE;
     impl->AllowsArbitraryDeviceNames = 1;
 
     return 1;   /* this audio target is available. */