src/audio/SDL_audio.c
changeset 9394 bb28e5281770
parent 9393 ed79a66e57e5
child 9396 69c501ed36f3
     1.1 --- a/src/audio/SDL_audio.c	Mon Mar 16 02:11:39 2015 -0400
     1.2 +++ b/src/audio/SDL_audio.c	Wed Mar 18 02:01:17 2015 -0400
     1.3 @@ -161,8 +161,16 @@
     1.4  
     1.5  /* stubs for audio drivers that don't need a specific entry point... */
     1.6  static void
     1.7 -SDL_AudioDetectDevices_Default(int iscapture, SDL_AddAudioDevice addfn)
     1.8 -{                               /* no-op. */
     1.9 +SDL_AudioDetectDevices_Default(void)
    1.10 +{
    1.11 +    /* you have to write your own implementation if these assertions fail. */
    1.12 +    SDL_assert(current_audio.impl.OnlyHasDefaultOutputDevice);
    1.13 +    SDL_assert(current_audio.impl.OnlyHasDefaultInputDevice || !current_audio.impl.HasCaptureSupport);
    1.14 +
    1.15 +    SDL_AddAudioDevice(SDL_FALSE, DEFAULT_OUTPUT_DEVNAME, (void *) ((size_t) 0x1));
    1.16 +    if (current_audio.impl.HasCaptureSupport) {
    1.17 +        SDL_AddAudioDevice(SDL_TRUE, DEFAULT_INPUT_DEVNAME, (void *) ((size_t) 0x2));
    1.18 +    }
    1.19  }
    1.20  
    1.21  static void
    1.22 @@ -207,10 +215,16 @@
    1.23  {                               /* no-op. */
    1.24  }
    1.25  
    1.26 +static void
    1.27 +SDL_AudioFreeDeviceHandle_Default(void *handle)
    1.28 +{                               /* no-op. */
    1.29 +}
    1.30 +
    1.31 +
    1.32  static int
    1.33 -SDL_AudioOpenDevice_Default(_THIS, const char *devname, int iscapture)
    1.34 +SDL_AudioOpenDevice_Default(_THIS, void *handle, const char *devname, int iscapture)
    1.35  {
    1.36 -    return -1;
    1.37 +    return SDL_Unsupported();
    1.38  }
    1.39  
    1.40  static SDL_INLINE SDL_bool
    1.41 @@ -267,6 +281,7 @@
    1.42      FILL_STUB(CloseDevice);
    1.43      FILL_STUB(LockDevice);
    1.44      FILL_STUB(UnlockDevice);
    1.45 +    FILL_STUB(FreeDeviceHandle);
    1.46      FILL_STUB(Deinitialize);
    1.47  #undef FILL_STUB
    1.48  }
    1.49 @@ -335,94 +350,64 @@
    1.50  
    1.51  /* device hotplug support... */
    1.52  
    1.53 -/* this function expects its caller to hold current_audio.detection_lock */
    1.54  static int
    1.55 -add_audio_device(const char *_name, char ***_devices, int *_devCount)
    1.56 +add_audio_device(const char *name, void *handle, SDL_AudioDeviceItem **devices, int *devCount)
    1.57  {
    1.58 -    char *name = SDL_strdup(_name);
    1.59      int retval = -1;
    1.60 +    const size_t size = sizeof (SDL_AudioDeviceItem) + SDL_strlen(name) + 1;
    1.61 +    SDL_AudioDeviceItem *item = (SDL_AudioDeviceItem *) SDL_malloc(size);
    1.62 +    if (item == NULL) {
    1.63 +        return -1;
    1.64 +    }
    1.65  
    1.66 -    if (name != NULL) {
    1.67 -        char **devices = *_devices;
    1.68 -        int devCount = *_devCount;
    1.69 -        void *ptr = SDL_realloc(devices, (devCount+1) * sizeof(char*));
    1.70 -        if (ptr == NULL) {
    1.71 -            SDL_free(name);
    1.72 -        } else {
    1.73 -            retval = devCount;
    1.74 -            devices = (char **) ptr;
    1.75 -            devices[devCount++] = name;
    1.76 -            *_devices = devices;
    1.77 -            *_devCount = devCount;
    1.78 -        }
    1.79 -    }
    1.80 +    SDL_assert(handle != NULL);
    1.81 +
    1.82 +    item->handle = handle;
    1.83 +    SDL_strlcpy(item->name, name, size - sizeof (SDL_AudioDeviceItem));
    1.84 +
    1.85 +    SDL_LockMutex(current_audio.detectionLock);
    1.86 +    item->next = *devices;
    1.87 +    *devices = item;
    1.88 +    retval = (*devCount)++;
    1.89 +    SDL_UnlockMutex(current_audio.detectionLock);
    1.90  
    1.91      return retval;
    1.92  }
    1.93  
    1.94 -static int
    1.95 -add_capture_device(const char *name)
    1.96 +static SDL_INLINE int
    1.97 +add_capture_device(const char *name, void *handle)
    1.98  {
    1.99      /* !!! FIXME: add this later. SDL_assert(current_audio.impl.HasCaptureSupport);*/
   1.100 -    return add_audio_device(name, &current_audio.inputDevices, &current_audio.inputDeviceCount);
   1.101 +    return add_audio_device(name, handle, &current_audio.inputDevices, &current_audio.inputDeviceCount);
   1.102  }
   1.103  
   1.104 -static int
   1.105 -add_output_device(const char *name)
   1.106 +static SDL_INLINE int
   1.107 +add_output_device(const char *name, void *handle)
   1.108  {
   1.109 -    return add_audio_device(name, &current_audio.outputDevices, &current_audio.outputDeviceCount);
   1.110 +    return add_audio_device(name, handle, &current_audio.outputDevices, &current_audio.outputDeviceCount);
   1.111  }
   1.112  
   1.113  static void
   1.114 -free_device_list(char ***devices, int *devCount)
   1.115 +free_device_list(SDL_AudioDeviceItem **devices, int *devCount)
   1.116  {
   1.117 -    int i = *devCount;
   1.118 -    if ((i > 0) && (*devices != NULL)) {
   1.119 -        while (i--) {
   1.120 -            SDL_free((*devices)[i]);
   1.121 +    SDL_AudioDeviceItem *item, *next;
   1.122 +    for (item = *devices; item != NULL; item = next) {
   1.123 +        next = item->next;
   1.124 +        if (item->handle != NULL) {
   1.125 +            current_audio.impl.FreeDeviceHandle(item->handle);
   1.126          }
   1.127 +        SDL_free(item);
   1.128      }
   1.129 -
   1.130 -    SDL_free(*devices);
   1.131 -
   1.132      *devices = NULL;
   1.133      *devCount = 0;
   1.134  }
   1.135  
   1.136 -static void
   1.137 -perform_full_device_redetect(const int iscapture)
   1.138 -{
   1.139 -    SDL_LockMutex(current_audio.detection_lock);
   1.140 -
   1.141 -    if (iscapture) {
   1.142 -        if (!current_audio.impl.OnlyHasDefaultOutputDevice) {
   1.143 -            free_device_list(&current_audio.outputDevices, &current_audio.outputDeviceCount);
   1.144 -            current_audio.impl.DetectDevices(SDL_FALSE, add_output_device);
   1.145 -        }
   1.146 -    } else {
   1.147 -        if ((current_audio.impl.HasCaptureSupport) && (!current_audio.impl.OnlyHasDefaultInputDevice)) {
   1.148 -            free_device_list(&current_audio.inputDevices, &current_audio.inputDeviceCount);
   1.149 -            current_audio.impl.DetectDevices(SDL_TRUE, add_capture_device);
   1.150 -        }
   1.151 -    }
   1.152 -
   1.153 -    SDL_UnlockMutex(current_audio.detection_lock);
   1.154 -}
   1.155  
   1.156  /* The audio backends call this when a new device is plugged in. */
   1.157  void
   1.158 -SDL_AudioDeviceConnected(const int iscapture, const char *name)
   1.159 +SDL_AddAudioDevice(const int iscapture, const char *name, void *handle)
   1.160  {
   1.161 -    int device_index = -1;
   1.162 -
   1.163 -    SDL_LockMutex(current_audio.detection_lock);
   1.164 -    if (iscapture) {
   1.165 -        device_index = add_capture_device(name);
   1.166 -    } else {
   1.167 -        device_index = add_output_device(name);
   1.168 -    }
   1.169 -    SDL_UnlockMutex(current_audio.detection_lock);
   1.170 -
   1.171 +    const int device_index = iscapture ? add_capture_device(name, handle) : add_output_device(name, handle);
   1.172      if (device_index != -1) {
   1.173          /* Post the event, if desired */
   1.174          if (SDL_GetEventState(SDL_AUDIODEVICEADDED) == SDL_ENABLE) {
   1.175 @@ -435,39 +420,51 @@
   1.176      }
   1.177  }
   1.178  
   1.179 -/* The audio backends call this when a device is unplugged. */
   1.180 -void
   1.181 -SDL_AudioDeviceDisconnected(const int iscapture, SDL_AudioDevice *device)
   1.182 +/* The audio backends call this when a currently-opened device is lost. */
   1.183 +void SDL_OpenedAudioDeviceDisconnected(SDL_AudioDevice *device)
   1.184  {
   1.185 -    /* device==NULL means an unopened device was lost; do the redetect only. */
   1.186 -    if (device != NULL) {
   1.187 -        SDL_assert(get_audio_device(device->id) == device);
   1.188 -        SDL_assert(device->enabled);  /* called more than once?! */
   1.189 +    SDL_assert(get_audio_device(device->id) == device);
   1.190 +    SDL_assert(device->enabled);  /* called more than once?! */
   1.191 +
   1.192 +    /* Ends the audio callback and mark the device as STOPPED, but the
   1.193 +       app still needs to close the device to free resources. */
   1.194 +    current_audio.impl.LockDevice(device);
   1.195 +    device->enabled = 0;
   1.196 +    current_audio.impl.UnlockDevice(device);
   1.197  
   1.198 -        /* Ends the audio callback and mark the device as STOPPED, but the
   1.199 -           app still needs to close the device to free resources. */
   1.200 -        current_audio.impl.LockDevice(device);
   1.201 -        device->enabled = 0;
   1.202 -        current_audio.impl.UnlockDevice(device);
   1.203 +    /* Post the event, if desired */
   1.204 +    if (SDL_GetEventState(SDL_AUDIODEVICEREMOVED) == SDL_ENABLE) {
   1.205 +        SDL_Event event;
   1.206 +        event.adevice.type = SDL_AUDIODEVICEREMOVED;
   1.207 +        event.adevice.which = device->id;
   1.208 +        event.adevice.iscapture = device->iscapture ? 1 : 0;
   1.209 +        SDL_PushEvent(&event);
   1.210 +    }
   1.211 +}
   1.212  
   1.213 -        /* Post the event, if desired */
   1.214 -        if (SDL_GetEventState(SDL_AUDIODEVICEREMOVED) == SDL_ENABLE) {
   1.215 -            SDL_Event event;
   1.216 -            event.adevice.type = SDL_AUDIODEVICEREMOVED;
   1.217 -            event.adevice.which = device->id;
   1.218 -            event.adevice.iscapture = device->iscapture ? 1 : 0;
   1.219 -            SDL_PushEvent(&event);
   1.220 +static void
   1.221 +mark_device_removed(void *handle, SDL_AudioDeviceItem *devices, SDL_bool *removedFlag)
   1.222 +{
   1.223 +    SDL_AudioDeviceItem *item;
   1.224 +    SDL_assert(handle != NULL);
   1.225 +    for (item = devices; item != NULL; item = item->next) {
   1.226 +        if (item->handle == handle) {
   1.227 +            item->handle = NULL;
   1.228 +            *removedFlag = SDL_TRUE;
   1.229 +            return;
   1.230          }
   1.231      }
   1.232 +}
   1.233  
   1.234 -    /* we don't really know which name (if any) was associated with this
   1.235 -       device in the device list, so drop the entire list and rebuild it.
   1.236 -       (we should probably change the API in 2.1 to make this more clear?) */
   1.237 -    if (iscapture) {
   1.238 -        current_audio.need_capture_device_redetect = SDL_TRUE;
   1.239 -    } else {
   1.240 -        current_audio.need_output_device_redetect = SDL_TRUE;
   1.241 -    }
   1.242 +/* The audio backends call this when a device is removed from the system. */
   1.243 +void
   1.244 +SDL_RemoveAudioDevice(void *handle)
   1.245 +{
   1.246 +    SDL_LockMutex(current_audio.detectionLock);
   1.247 +    mark_device_removed(handle, current_audio.inputDevices, &current_audio.captureDevicesRemoved);
   1.248 +    mark_device_removed(handle, current_audio.outputDevices, &current_audio.outputDevicesRemoved);
   1.249 +    SDL_UnlockMutex(current_audio.detectionLock);
   1.250 +    current_audio.impl.FreeDeviceHandle(handle);
   1.251  }
   1.252  
   1.253  
   1.254 @@ -966,33 +963,12 @@
   1.255          return -1;            /* No driver was available, so fail. */
   1.256      }
   1.257  
   1.258 -    current_audio.detection_lock = SDL_CreateMutex();
   1.259 +    current_audio.detectionLock = SDL_CreateMutex();
   1.260  
   1.261      finalize_audio_entry_points();
   1.262  
   1.263      /* Make sure we have a list of devices available at startup. */
   1.264 -    perform_full_device_redetect(SDL_TRUE);
   1.265 -    perform_full_device_redetect(SDL_FALSE);
   1.266 -
   1.267 -    /* Post an add event for each initial device, if desired */
   1.268 -    if (SDL_GetEventState(SDL_AUDIODEVICEADDED) == SDL_ENABLE) {
   1.269 -        SDL_Event event;
   1.270 -
   1.271 -        SDL_zero(event);
   1.272 -        event.adevice.type = SDL_AUDIODEVICEADDED;
   1.273 -
   1.274 -        event.adevice.iscapture = 0;
   1.275 -        for (i = 0; i < current_audio.outputDeviceCount; i++) {
   1.276 -            event.adevice.which = i;
   1.277 -            SDL_PushEvent(&event);
   1.278 -        }
   1.279 -
   1.280 -        event.adevice.iscapture = 1;
   1.281 -        for (i = 0; i < current_audio.inputDeviceCount; i++) {
   1.282 -            event.adevice.which = i;
   1.283 -            SDL_PushEvent(&event);
   1.284 -        }
   1.285 -    }
   1.286 +    current_audio.impl.DetectDevices();
   1.287  
   1.288      return 0;
   1.289  }
   1.290 @@ -1006,6 +982,35 @@
   1.291      return current_audio.name;
   1.292  }
   1.293  
   1.294 +/* Clean out devices that we've removed but had to keep around for stability. */
   1.295 +static void
   1.296 +clean_out_device_list(SDL_AudioDeviceItem **devices, int *devCount, SDL_bool *removedFlag)
   1.297 +{
   1.298 +    SDL_AudioDeviceItem *item = *devices;
   1.299 +    SDL_AudioDeviceItem *prev = NULL;
   1.300 +    int total = 0;
   1.301 +
   1.302 +    while (item) {
   1.303 +        SDL_AudioDeviceItem *next = item->next;
   1.304 +        if (item->handle != NULL) {
   1.305 +            total++;
   1.306 +            prev = item;
   1.307 +        } else {
   1.308 +            if (prev) {
   1.309 +                prev->next = next;
   1.310 +            } else {
   1.311 +                *devices = next;
   1.312 +            }
   1.313 +            SDL_free(item);
   1.314 +        }
   1.315 +        item = next;
   1.316 +    }
   1.317 +
   1.318 +    *devCount = total;
   1.319 +    *removedFlag = SDL_FALSE;
   1.320 +}
   1.321 +
   1.322 +
   1.323  int
   1.324  SDL_GetNumAudioDevices(int iscapture)
   1.325  {
   1.326 @@ -1015,31 +1020,18 @@
   1.327          return -1;
   1.328      }
   1.329  
   1.330 -    if ((iscapture) && (!current_audio.impl.HasCaptureSupport)) {
   1.331 -        return 0;
   1.332 -    }
   1.333 -
   1.334 -    if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
   1.335 -        return 1;
   1.336 -    }
   1.337 -
   1.338 -    if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
   1.339 -        return 1;
   1.340 +    SDL_LockMutex(current_audio.detectionLock);
   1.341 +    if (iscapture && current_audio.captureDevicesRemoved) {
   1.342 +        clean_out_device_list(&current_audio.inputDevices, &current_audio.inputDeviceCount, &current_audio.captureDevicesRemoved);
   1.343      }
   1.344  
   1.345 -    if (current_audio.need_capture_device_redetect) {
   1.346 -        current_audio.need_capture_device_redetect = SDL_FALSE;
   1.347 -        perform_full_device_redetect(SDL_TRUE);
   1.348 +    if (!iscapture && current_audio.outputDevicesRemoved) {
   1.349 +        clean_out_device_list(&current_audio.outputDevices, &current_audio.outputDeviceCount, &current_audio.outputDevicesRemoved);
   1.350 +        current_audio.outputDevicesRemoved = SDL_FALSE;
   1.351      }
   1.352  
   1.353 -    if (current_audio.need_output_device_redetect) {
   1.354 -        current_audio.need_output_device_redetect = SDL_FALSE;
   1.355 -        perform_full_device_redetect(SDL_FALSE);
   1.356 -    }
   1.357 -
   1.358 -    SDL_LockMutex(current_audio.detection_lock);
   1.359      retval = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount;
   1.360 -    SDL_UnlockMutex(current_audio.detection_lock);
   1.361 +    SDL_UnlockMutex(current_audio.detectionLock);
   1.362  
   1.363      return retval;
   1.364  }
   1.365 @@ -1060,41 +1052,28 @@
   1.366          return NULL;
   1.367      }
   1.368  
   1.369 -    if (index < 0) {
   1.370 -        goto no_such_device;
   1.371 -    }
   1.372 +    if (index >= 0) {
   1.373 +        SDL_AudioDeviceItem *item;
   1.374 +        int i;
   1.375  
   1.376 -    if ((iscapture) && (current_audio.impl.OnlyHasDefaultInputDevice)) {
   1.377 -        if (index > 0) {
   1.378 -            goto no_such_device;
   1.379 +        SDL_LockMutex(current_audio.detectionLock);
   1.380 +        item = iscapture ? current_audio.inputDevices : current_audio.outputDevices;
   1.381 +        i = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount;
   1.382 +        if (index < i) {
   1.383 +            for (i--; i > index; i--, item = item->next) {
   1.384 +                SDL_assert(item != NULL);
   1.385 +            }
   1.386 +            SDL_assert(item != NULL);
   1.387 +            retval = item->name;
   1.388          }
   1.389 -        return DEFAULT_INPUT_DEVNAME;
   1.390 -    }
   1.391 -
   1.392 -    if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
   1.393 -        if (index > 0) {
   1.394 -            goto no_such_device;
   1.395 -        }
   1.396 -        return DEFAULT_OUTPUT_DEVNAME;
   1.397 +        SDL_UnlockMutex(current_audio.detectionLock);
   1.398      }
   1.399  
   1.400 -    SDL_LockMutex(current_audio.detection_lock);
   1.401 -    if (iscapture && (index < current_audio.inputDeviceCount)) {
   1.402 -        retval = current_audio.inputDevices[index];
   1.403 -    } else if (!iscapture && (index < current_audio.outputDeviceCount)) {
   1.404 -        retval = current_audio.outputDevices[index];
   1.405 -    }
   1.406 -    SDL_UnlockMutex(current_audio.detection_lock);
   1.407 -
   1.408 -    /* !!! FIXME: a device could be removed after being returned here, freeing retval's pointer. */
   1.409 -
   1.410 -    if (retval != NULL) {
   1.411 -        return retval;
   1.412 +    if (retval == NULL) {
   1.413 +        SDL_SetError("No such device");
   1.414      }
   1.415  
   1.416 -no_such_device:
   1.417 -    SDL_SetError("No such device");
   1.418 -    return NULL;
   1.419 +    return retval;
   1.420  }
   1.421  
   1.422  
   1.423 @@ -1195,6 +1174,7 @@
   1.424      SDL_AudioSpec _obtained;
   1.425      SDL_AudioDevice *device;
   1.426      SDL_bool build_cvt;
   1.427 +    void *handle = NULL;
   1.428      int i = 0;
   1.429  
   1.430      if (!SDL_WasInit(SDL_INIT_AUDIO)) {
   1.431 @@ -1254,9 +1234,7 @@
   1.432                  return 0;
   1.433              }
   1.434          }
   1.435 -    }
   1.436 -
   1.437 -    if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
   1.438 +    } else if ((!iscapture) && (current_audio.impl.OnlyHasDefaultOutputDevice)) {
   1.439          if ((devname) && (SDL_strcmp(devname, DEFAULT_OUTPUT_DEVNAME) != 0)) {
   1.440              SDL_SetError("No such device");
   1.441              return 0;
   1.442 @@ -1269,6 +1247,30 @@
   1.443                  return 0;
   1.444              }
   1.445          }
   1.446 +    } else if (devname != NULL) {
   1.447 +        /* if the app specifies an exact string, we can pass the backend
   1.448 +           an actual device handle thingey, which saves them the effort of
   1.449 +           figuring out what device this was (such as, reenumerating
   1.450 +           everything again to find the matching human-readable name).
   1.451 +           It might still need to open a device based on the string for,
   1.452 +           say, a network audio server, but this optimizes some cases. */
   1.453 +        SDL_AudioDeviceItem *item;
   1.454 +        SDL_LockMutex(current_audio.detectionLock);
   1.455 +        for (item = iscapture ? current_audio.inputDevices : current_audio.outputDevices; item; item = item->next) {
   1.456 +            if ((item->handle != NULL) && (SDL_strcmp(item->name, devname) == 0)) {
   1.457 +                handle = item->handle;
   1.458 +                break;
   1.459 +            }
   1.460 +        }
   1.461 +        SDL_UnlockMutex(current_audio.detectionLock);
   1.462 +    }
   1.463 +
   1.464 +    if (!current_audio.impl.AllowsArbitraryDeviceNames) {
   1.465 +        /* has to be in our device list, or the default device. */
   1.466 +        if ((handle == NULL) && (devname != NULL)) {
   1.467 +            SDL_SetError("No such device.");
   1.468 +            return 0;
   1.469 +        }
   1.470      }
   1.471  
   1.472      device = (SDL_AudioDevice *) SDL_AllocAudioMem(sizeof(SDL_AudioDevice));
   1.473 @@ -1293,7 +1295,7 @@
   1.474          }
   1.475      }
   1.476  
   1.477 -    if (current_audio.impl.OpenDevice(device, devname, iscapture) < 0) {
   1.478 +    if (current_audio.impl.OpenDevice(device, handle, devname, iscapture) < 0) {
   1.479          close_audio_device(device);
   1.480          return 0;
   1.481      }
   1.482 @@ -1555,15 +1557,13 @@
   1.483          }
   1.484      }
   1.485  
   1.486 +    free_device_list(&current_audio.outputDevices, &current_audio.outputDeviceCount);
   1.487 +    free_device_list(&current_audio.inputDevices, &current_audio.inputDeviceCount);
   1.488 +
   1.489      /* Free the driver data */
   1.490      current_audio.impl.Deinitialize();
   1.491  
   1.492 -    free_device_list(&current_audio.outputDevices,
   1.493 -                     &current_audio.outputDeviceCount);
   1.494 -    free_device_list(&current_audio.inputDevices,
   1.495 -                     &current_audio.inputDeviceCount);
   1.496 -
   1.497 -    SDL_DestroyMutex(current_audio.detection_lock);
   1.498 +    SDL_DestroyMutex(current_audio.detectionLock);
   1.499  
   1.500      SDL_zero(current_audio);
   1.501      SDL_zero(open_devices);