Multiple audio device code is now working for dsp and dma targets. SDL-ryan-multiple-audio-device
authorRyan C. Gordon <icculus@icculus.org>
Thu, 05 Oct 2006 04:47:13 +0000
branchSDL-ryan-multiple-audio-device
changeset 3810 2c5387c0a642
parent 3809 7852b5b78af5
child 3811 d980c4dcad0f
Multiple audio device code is now working for dsp and dma targets.
src/audio/SDL_audiodev.c
src/audio/SDL_audiodev_c.h
src/audio/dma/SDL_dmaaudio.c
src/audio/dsp/SDL_dspaudio.c
--- a/src/audio/SDL_audiodev.c	Thu Oct 05 01:13:47 2006 +0000
+++ b/src/audio/SDL_audiodev.c	Thu Oct 05 04:47:13 2006 +0000
@@ -46,14 +46,63 @@
 #define _PATH_DEV_AUDIO	"/dev/audio"
 #endif
 
+static inline void
+test_device(const char *fname, int flags, int (*test)(int fd),
+            char ***devices, int *devCount)
+{
+    struct stat sb;
+    if ( (stat(fname, &sb) == 0) && (S_ISCHR(sb.st_mode)) ) {
+        int audio_fd = open(fname, flags, 0);
+        if ( (audio_fd >= 0) && (test(audio_fd)) ) {
+            void *p = SDL_realloc(*devices, ((*devCount)+1) * sizeof (char *));
+            if (p != NULL) {
+                size_t len = strlen(fname) + 1;
+                char *str = (char *) SDL_malloc(len);
+                *devices = (char **) p;
+                if (str != NULL) {
+                    SDL_strlcpy(str, fname, len);
+                    (*devices)[(*devCount)++] = str;
+                }
+            }
+            close(audio_fd);
+        }
+    }
+}
 
-int
-SDL_OpenAudioPath(char *path, int maxlen, int flags, int classic)
+void
+SDL_FreeUnixAudioDevices(char ***devices, int *devCount)
+{
+    int i = *devCount;
+    if ((i > 0) && (*devices != NULL)) {
+        while (i--) {
+            SDL_free( (*devices)[*devCount] );
+        }
+    }
+
+    if (*devices != NULL) {
+        SDL_free(*devices);
+    }
+
+    *devices = NULL;
+    *devCount = 0;
+}
+
+static int
+test_stub(int fd)
+{
+    return 1;
+}
+
+void
+SDL_EnumUnixAudioDevices(int flags, int classic, int (*test)(int fd),
+                         char ***devices, int *devCount)
 {
     const char *audiodev;
-    int audio_fd;
     char audiopath[1024];
 
+    if (test == NULL)
+        test = test_stub;
+
     /* Figure out what our audio device is */
     if (((audiodev = SDL_getenv("SDL_PATH_DSP")) == NULL) &&
         ((audiodev = SDL_getenv("AUDIODEV")) == NULL)) {
@@ -72,31 +121,16 @@
             }
         }
     }
-    audio_fd = open(audiodev, flags, 0);
+    test_device(audiodev, flags, test, devices, devCount);
 
-    /* If the first open fails, look for other devices */
-    if ((audio_fd < 0) && (SDL_strlen(audiodev) < (sizeof(audiopath) - 3))) {
-        int exists, instance;
-        struct stat sb;
-
-        instance = 1;
-        do {                    /* Don't use errno ENOENT - it may not be thread-safe */
+    if (SDL_strlen(audiodev) < (sizeof(audiopath) - 3)) {
+        int instance = 0;
+        while (instance++ <= 64) {
             SDL_snprintf(audiopath, SDL_arraysize(audiopath),
-                         "%s%d", audiodev, instance++);
-            exists = 0;
-            if (stat(audiopath, &sb) == 0) {
-                exists = 1;
-                audio_fd = open(audiopath, flags, 0);
-            }
+                         "%s%d", audiodev, instance);
+            test_device(audiopath, flags, test, devices, devCount);
         }
-        while (exists && (audio_fd < 0));
-        audiodev = audiopath;
     }
-    if (path != NULL) {
-        SDL_strlcpy(path, audiodev, maxlen);
-        path[maxlen - 1] = '\0';
-    }
-    return (audio_fd);
 }
 
 #endif /* Audio driver selection */
--- a/src/audio/SDL_audiodev_c.h	Thu Oct 05 01:13:47 2006 +0000
+++ b/src/audio/SDL_audiodev_c.h	Thu Oct 05 04:47:13 2006 +0000
@@ -21,6 +21,8 @@
 */
 #include "SDL_config.h"
 
-/* Open the audio device, storing the pathname in 'path'  */
-extern int SDL_OpenAudioPath(char *path, int maxlen, int flags, int classic);
+void SDL_EnumUnixAudioDevices(int flags, int classic, int (*test)(int fd),
+                              char ***devs, int *count);
+void SDL_FreeUnixAudioDevices(char ***devices, int *devCount);
+
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/audio/dma/SDL_dmaaudio.c	Thu Oct 05 01:13:47 2006 +0000
+++ b/src/audio/dma/SDL_dmaaudio.c	Thu Oct 05 04:47:13 2006 +0000
@@ -59,7 +59,8 @@
 #define DMA_DRIVER_NAME         "dma"
 
 /* Open the audio device for playback, and don't block if busy */
-#define OPEN_FLAGS	(O_RDWR|O_NONBLOCK)
+#define OPEN_FLAGS_INPUT    (O_RDWR|O_NONBLOCK)
+#define OPEN_FLAGS_OUTPUT   (O_RDWR|O_NONBLOCK)
 
 /* Audio driver functions */
 static int DMA_DetectDevices(int iscapture);
@@ -69,34 +70,72 @@
 static void DMA_PlayDevice(_THIS);
 static Uint8 *DMA_GetDeviceBuf(_THIS);
 static void DMA_CloseDevice(_THIS);
+static void DMA_Deinitialize(void);
 
 /* Audio driver bootstrap functions */
 
+static char **outputDevices = NULL;
+static int outputDeviceCount = 0;
+static char **inputDevices = NULL;
+static int inputDeviceCount = 0;
+
+static int
+test_for_mmap(int fd)
+{
+    int caps = 0;
+    struct audio_buf_info info;
+    if ((ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) == 0) &&
+        (caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP) &&
+        (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) == 0))
+    {
+        size_t len = info.fragstotal * info.fragsize;
+        Uint8 *buf = (Uint8 *) mmap(NULL, len, PROT_WRITE, MAP_SHARED, fd, 0);
+        if (buf != MAP_FAILED) {
+            munmap(buf, len);
+            return 1;
+        }
+    }
+    return 0;
+}
+
+
+static inline void
+free_device_list(char ***devs, int *count)
+{
+    SDL_FreeUnixAudioDevices(devs, count);
+}
+
+static inline void
+build_device_list(int iscapture, char ***devs, int *count)
+{
+    const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
+    free_device_list(devs, count);
+    SDL_EnumUnixAudioDevices(flags, 0, test_for_mmap, devs, count);
+}
+
+static inline void
+build_device_lists(void)
+{
+    build_device_list(0, &outputDevices, &outputDeviceCount);
+    build_device_list(1, &inputDevices, &inputDeviceCount);
+}
+
+
+static inline void
+free_device_lists(void)
+{
+    free_device_list(&outputDevices, &outputDeviceCount);
+    free_device_list(&inputDevices, &inputDeviceCount);
+}
+
 static int
 DMA_Available(void)
 {
-    /*
-     * !!! FIXME: maybe change this to always available, and move this to
-     * !!! FIXME:  to device enumeration and opening?
-     */
-    int available;
-    int fd;
-
-    available = 0;
-
-    fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
-    if (fd >= 0) {
-        int caps;
-        struct audio_buf_info info;
-
-        if ((ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) == 0) &&
-            (caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP) &&
-            (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) == 0)) {
-            available = 1;
-        }
-        close(fd);
-    }
-    return (available);
+    int available = 0;
+    build_device_lists();
+    available = ((outputDeviceCount > 0) || (inputDeviceCount > 0));
+    free_device_lists();
+    return available;
 }
 
 
@@ -111,7 +150,9 @@
     impl->PlayDevice = DMA_PlayDevice;
     impl->GetDeviceBuf = DMA_GetDeviceBuf;
     impl->CloseDevice = DMA_CloseDevice;
+    impl->Deinitialize = DMA_Deinitialize;
 
+    build_device_lists();
     return 1;
 }
 
@@ -120,18 +161,36 @@
     DMA_Available, DMA_Init, 0
 };
 
+static void DMA_Deinitialize(void)
+{
+    free_device_lists();
+}
 
 static int
 DMA_DetectDevices(int iscapture)
 {
-    return -1;  /* !!! FIXME */
+    if (iscapture) {
+        build_device_list(1, &inputDevices, &inputDeviceCount);
+        return inputDeviceCount;
+    } else {
+        build_device_list(0, &outputDevices, &outputDeviceCount);
+        return outputDeviceCount;
+    }
+
+    return 0;  /* shouldn't ever hit this. */
 }
 
 
 static const char *
 DMA_GetDeviceName(int index, int iscapture)
 {
-    SDL_SetError("No such device");  /* !!! FIXME */
+    if ((iscapture) && (index < inputDeviceCount)) {
+        return inputDevices[index];
+    } else if ((!iscapture) && (index < outputDeviceCount)) {
+        return outputDevices[index];
+    }
+
+    SDL_SetError("No such device");
     return NULL;
 }
 
@@ -198,13 +257,24 @@
 static int
 open_device_internal(_THIS, const char *devname, int iscapture)
 {
-    char audiodev[1024];
+    const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
     int format;
     int stereo;
     int value;
     SDL_AudioFormat test_format;
     struct audio_buf_info info;
 
+    /* We don't care what the devname is...we'll try to open anything. */
+    /*  ...but default to first name in the list... */
+    if (devname == NULL) {
+        if ( ((iscapture) && (inputDeviceCount == 0)) ||
+             ((!iscapture) && (outputDeviceCount == 0)) ) {
+            SDL_SetError("No such audio device");
+            return 0;
+        }
+        devname = ((iscapture) ? inputDevices[0] : outputDevices[0]);
+    }
+
     /* Initialize all variables that we clean on shutdown */
     this->hidden = (struct SDL_PrivateAudioData *)
                         SDL_malloc((sizeof *this->hidden));
@@ -214,13 +284,11 @@
     }
     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
 
-    /* !!! FIXME: handle devname */
-    /* !!! FIXME: handle iscapture */
 
     /* Open the audio device */
-    audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
+    audio_fd = open(devname, flags, 0);
     if (audio_fd < 0) {
-        SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
+        SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
         return 0;
     }
     dma_buf = NULL;
@@ -304,7 +372,7 @@
        after setting the format, we must re-open the audio device
        once we know what format and channels are supported
      */
-    if (DMA_ReopenAudio(this, audiodev, format, stereo) < 0) {
+    if (DMA_ReopenAudio(this, devname, format, stereo) < 0) {
         /* Error is set by DMA_ReopenAudio() */
         return 0;
     }
--- a/src/audio/dsp/SDL_dspaudio.c	Thu Oct 05 01:13:47 2006 +0000
+++ b/src/audio/dsp/SDL_dspaudio.c	Thu Oct 05 04:47:13 2006 +0000
@@ -55,7 +55,8 @@
 #define DSP_DRIVER_NAME         "dsp"
 
 /* Open the audio device for playback, and don't block if busy */
-#define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK)
+#define OPEN_FLAGS_OUTPUT    (O_WRONLY|O_NONBLOCK)
+#define OPEN_FLAGS_INPUT    (O_RDONLY|O_NONBLOCK)
 
 /* Audio driver functions */
 static int DSP_DetectDevices(int iscapture);
@@ -65,23 +66,53 @@
 static void DSP_PlayDevice(_THIS);
 static Uint8 *DSP_GetDeviceBuf(_THIS);
 static void DSP_CloseDevice(_THIS);
+static void DSP_Deinitialize(void);
 
 /* Audio driver bootstrap functions */
 
+static char **outputDevices = NULL;
+static int outputDeviceCount = 0;
+static char **inputDevices = NULL;
+static int inputDeviceCount = 0;
+
+static inline void
+free_device_list(char ***devs, int *count)
+{
+    SDL_FreeUnixAudioDevices(devs, count);
+}
+
+static inline void
+build_device_list(int iscapture, char ***devs, int *count)
+{
+    const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
+    free_device_list(devs, count);
+    SDL_EnumUnixAudioDevices(flags, 0, NULL, devs, count);
+}
+
+static inline void
+build_device_lists(void)
+{
+    build_device_list(0, &outputDevices, &outputDeviceCount);
+    build_device_list(1, &inputDevices, &inputDeviceCount);
+}
+
+
+static inline void
+free_device_lists(void)
+{
+    free_device_list(&outputDevices, &outputDeviceCount);
+    free_device_list(&inputDevices, &inputDeviceCount);
+}
+
+
 static int
 DSP_Available(void)
 {
-    /*
-     * !!! FIXME: maybe change this to always available, and move this to
-     * !!! FIXME:  to device enumeration and opening?
-     */
-    int fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
     int available = 0;
-    if (fd >= 0) {
-        available = 1;
-        close(fd);
-    }
-    return (available);
+    build_device_lists();
+    available = ((outputDeviceCount > 0) || (inputDeviceCount > 0));
+    free_device_lists();
+    return available;
 }
 
 
@@ -92,11 +123,12 @@
     impl->DetectDevices = DSP_DetectDevices;
     impl->GetDeviceName = DSP_GetDeviceName;
     impl->OpenDevice = DSP_OpenDevice;
-    impl->WaitDevice = DSP_WaitDevice;
     impl->PlayDevice = DSP_PlayDevice;
     impl->GetDeviceBuf = DSP_GetDeviceBuf;
     impl->CloseDevice = DSP_CloseDevice;
+    impl->Deinitialize = DSP_Deinitialize;
 
+    build_device_lists();
     return 1;
 }
 
@@ -107,17 +139,36 @@
 };
 
 
+static void DSP_Deinitialize(void)
+{
+    free_device_lists();
+}
+
+
 static int
 DSP_DetectDevices(int iscapture)
 {
-    return -1;  /* !!! FIXME */
+    if (iscapture) {
+        build_device_list(1, &inputDevices, &inputDeviceCount);
+        return inputDeviceCount;
+    } else {
+        build_device_list(0, &outputDevices, &outputDeviceCount);
+        return outputDeviceCount;
+    }
+
+    return 0;  /* shouldn't ever hit this. */
 }
 
-
 static const char *
 DSP_GetDeviceName(int index, int iscapture)
 {
-    SDL_SetError("No such device");  /* !!! FIXME */
+    if ((iscapture) && (index < inputDeviceCount)) {
+        return inputDevices[index];
+    } else if ((!iscapture) && (index < outputDeviceCount)) {
+        return outputDevices[index];
+    }
+
+    SDL_SetError("No such device");
     return NULL;
 }
 
@@ -125,12 +176,23 @@
 static int
 DSP_OpenDevice(_THIS, const char *devname, int iscapture)
 {
-    char dev[1024];
+    const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT);
     int format;
     int value;
     int frag_spec;
     SDL_AudioFormat test_format;
 
+    /* We don't care what the devname is...we'll try to open anything. */
+    /*  ...but default to first name in the list... */
+    if (devname == NULL) {
+        if ( ((iscapture) && (inputDeviceCount == 0)) ||
+             ((!iscapture) && (outputDeviceCount == 0)) ) {
+            SDL_SetError("No such audio device");
+            return 0;
+        }
+        devname = ((iscapture) ? inputDevices[0] : outputDevices[0]);
+    }
+
     /* Initialize all variables that we clean on shutdown */
     this->hidden = (struct SDL_PrivateAudioData *)
                         SDL_malloc((sizeof *this->hidden));
@@ -141,13 +203,10 @@
     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
     this->hidden->audio_fd = -1;
 
-    /* !!! FIXME: handle devname */
-    /* !!! FIXME: handle iscapture */
-
     /* Open the audio device */
-    this->hidden->audio_fd = SDL_OpenAudioPath(dev, sizeof(dev), OPEN_FLAGS, 0);
+    this->hidden->audio_fd = open(devname, flags, 0);
     if (this->hidden->audio_fd < 0) {
-        SDL_SetError("Couldn't open %s: %s", dev, strerror(errno));
+        SDL_SetError("Couldn't open %s: %s", devname, strerror(errno));
         return 0;
     }
     this->hidden->mixbuf = NULL;
@@ -306,14 +365,6 @@
 }
 
 
-/* This function waits until it is possible to write a full sound buffer */
-static void
-DSP_WaitDevice(_THIS)
-{
-    /* Not needed at all since OSS handles waiting automagically */
-}
-
-
 static void
 DSP_PlayDevice(_THIS)
 {