src/audio/SDL_audio.c
changeset 9393 ed79a66e57e5
parent 9392 92e23eff9b89
child 9394 bb28e5281770
     1.1 --- a/src/audio/SDL_audio.c	Wed Mar 18 10:09:39 2015 -0400
     1.2 +++ b/src/audio/SDL_audio.c	Mon Mar 16 02:11:39 2015 -0400
     1.3 @@ -333,6 +333,144 @@
     1.4  }
     1.5  #endif
     1.6  
     1.7 +/* device hotplug support... */
     1.8 +
     1.9 +/* this function expects its caller to hold current_audio.detection_lock */
    1.10 +static int
    1.11 +add_audio_device(const char *_name, char ***_devices, int *_devCount)
    1.12 +{
    1.13 +    char *name = SDL_strdup(_name);
    1.14 +    int retval = -1;
    1.15 +
    1.16 +    if (name != NULL) {
    1.17 +        char **devices = *_devices;
    1.18 +        int devCount = *_devCount;
    1.19 +        void *ptr = SDL_realloc(devices, (devCount+1) * sizeof(char*));
    1.20 +        if (ptr == NULL) {
    1.21 +            SDL_free(name);
    1.22 +        } else {
    1.23 +            retval = devCount;
    1.24 +            devices = (char **) ptr;
    1.25 +            devices[devCount++] = name;
    1.26 +            *_devices = devices;
    1.27 +            *_devCount = devCount;
    1.28 +        }
    1.29 +    }
    1.30 +
    1.31 +    return retval;
    1.32 +}
    1.33 +
    1.34 +static int
    1.35 +add_capture_device(const char *name)
    1.36 +{
    1.37 +    /* !!! FIXME: add this later. SDL_assert(current_audio.impl.HasCaptureSupport);*/
    1.38 +    return add_audio_device(name, &current_audio.inputDevices, &current_audio.inputDeviceCount);
    1.39 +}
    1.40 +
    1.41 +static int
    1.42 +add_output_device(const char *name)
    1.43 +{
    1.44 +    return add_audio_device(name, &current_audio.outputDevices, &current_audio.outputDeviceCount);
    1.45 +}
    1.46 +
    1.47 +static void
    1.48 +free_device_list(char ***devices, int *devCount)
    1.49 +{
    1.50 +    int i = *devCount;
    1.51 +    if ((i > 0) && (*devices != NULL)) {
    1.52 +        while (i--) {
    1.53 +            SDL_free((*devices)[i]);
    1.54 +        }
    1.55 +    }
    1.56 +
    1.57 +    SDL_free(*devices);
    1.58 +
    1.59 +    *devices = NULL;
    1.60 +    *devCount = 0;
    1.61 +}
    1.62 +
    1.63 +static void
    1.64 +perform_full_device_redetect(const int iscapture)
    1.65 +{
    1.66 +    SDL_LockMutex(current_audio.detection_lock);
    1.67 +
    1.68 +    if (iscapture) {
    1.69 +        if (!current_audio.impl.OnlyHasDefaultOutputDevice) {
    1.70 +            free_device_list(&current_audio.outputDevices, &current_audio.outputDeviceCount);
    1.71 +            current_audio.impl.DetectDevices(SDL_FALSE, add_output_device);
    1.72 +        }
    1.73 +    } else {
    1.74 +        if ((current_audio.impl.HasCaptureSupport) && (!current_audio.impl.OnlyHasDefaultInputDevice)) {
    1.75 +            free_device_list(&current_audio.inputDevices, &current_audio.inputDeviceCount);
    1.76 +            current_audio.impl.DetectDevices(SDL_TRUE, add_capture_device);
    1.77 +        }
    1.78 +    }
    1.79 +
    1.80 +    SDL_UnlockMutex(current_audio.detection_lock);
    1.81 +}
    1.82 +
    1.83 +/* The audio backends call this when a new device is plugged in. */
    1.84 +void
    1.85 +SDL_AudioDeviceConnected(const int iscapture, const char *name)
    1.86 +{
    1.87 +    int device_index = -1;
    1.88 +
    1.89 +    SDL_LockMutex(current_audio.detection_lock);
    1.90 +    if (iscapture) {
    1.91 +        device_index = add_capture_device(name);
    1.92 +    } else {
    1.93 +        device_index = add_output_device(name);
    1.94 +    }
    1.95 +    SDL_UnlockMutex(current_audio.detection_lock);
    1.96 +
    1.97 +    if (device_index != -1) {
    1.98 +        /* Post the event, if desired */
    1.99 +        if (SDL_GetEventState(SDL_AUDIODEVICEADDED) == SDL_ENABLE) {
   1.100 +            SDL_Event event;
   1.101 +            event.adevice.type = SDL_AUDIODEVICEADDED;
   1.102 +            event.adevice.which = device_index;
   1.103 +            event.adevice.iscapture = iscapture;
   1.104 +            SDL_PushEvent(&event);
   1.105 +        }
   1.106 +    }
   1.107 +}
   1.108 +
   1.109 +/* The audio backends call this when a device is unplugged. */
   1.110 +void
   1.111 +SDL_AudioDeviceDisconnected(const int iscapture, SDL_AudioDevice *device)
   1.112 +{
   1.113 +    /* device==NULL means an unopened device was lost; do the redetect only. */
   1.114 +    if (device != NULL) {
   1.115 +        SDL_assert(get_audio_device(device->id) == device);
   1.116 +        SDL_assert(device->enabled);  /* called more than once?! */
   1.117 +
   1.118 +        /* Ends the audio callback and mark the device as STOPPED, but the
   1.119 +           app still needs to close the device to free resources. */
   1.120 +        current_audio.impl.LockDevice(device);
   1.121 +        device->enabled = 0;
   1.122 +        current_audio.impl.UnlockDevice(device);
   1.123 +
   1.124 +        /* Post the event, if desired */
   1.125 +        if (SDL_GetEventState(SDL_AUDIODEVICEREMOVED) == SDL_ENABLE) {
   1.126 +            SDL_Event event;
   1.127 +            event.adevice.type = SDL_AUDIODEVICEREMOVED;
   1.128 +            event.adevice.which = device->id;
   1.129 +            event.adevice.iscapture = device->iscapture ? 1 : 0;
   1.130 +            SDL_PushEvent(&event);
   1.131 +        }
   1.132 +    }
   1.133 +
   1.134 +    /* we don't really know which name (if any) was associated with this
   1.135 +       device in the device list, so drop the entire list and rebuild it.
   1.136 +       (we should probably change the API in 2.1 to make this more clear?) */
   1.137 +    if (iscapture) {
   1.138 +        current_audio.need_capture_device_redetect = SDL_TRUE;
   1.139 +    } else {
   1.140 +        current_audio.need_output_device_redetect = SDL_TRUE;
   1.141 +    }
   1.142 +}
   1.143 +
   1.144 +
   1.145  
   1.146  /* buffer queueing support... */
   1.147  
   1.148 @@ -690,6 +828,13 @@
   1.149  
   1.150              /* !!! FIXME: this should be LockDevice. */
   1.151              SDL_LockMutex(device->mixer_lock);
   1.152 +
   1.153 +            /* Check again, in case device was removed while a lock was held. */
   1.154 +            if (!device->enabled) {
   1.155 +                SDL_UnlockMutex(device->mixer_lock);
   1.156 +                break;
   1.157 +            }
   1.158 +
   1.159              if (device->paused) {
   1.160                  SDL_memset(stream, silence, stream_len);
   1.161              } else {
   1.162 @@ -821,8 +966,34 @@
   1.163          return -1;            /* No driver was available, so fail. */
   1.164      }
   1.165  
   1.166 +    current_audio.detection_lock = SDL_CreateMutex();
   1.167 +
   1.168      finalize_audio_entry_points();
   1.169  
   1.170 +    /* Make sure we have a list of devices available at startup. */
   1.171 +    perform_full_device_redetect(SDL_TRUE);
   1.172 +    perform_full_device_redetect(SDL_FALSE);
   1.173 +
   1.174 +    /* Post an add event for each initial device, if desired */
   1.175 +    if (SDL_GetEventState(SDL_AUDIODEVICEADDED) == SDL_ENABLE) {
   1.176 +        SDL_Event event;
   1.177 +
   1.178 +        SDL_zero(event);
   1.179 +        event.adevice.type = SDL_AUDIODEVICEADDED;
   1.180 +
   1.181 +        event.adevice.iscapture = 0;
   1.182 +        for (i = 0; i < current_audio.outputDeviceCount; i++) {
   1.183 +            event.adevice.which = i;
   1.184 +            SDL_PushEvent(&event);
   1.185 +        }
   1.186 +
   1.187 +        event.adevice.iscapture = 1;
   1.188 +        for (i = 0; i < current_audio.inputDeviceCount; i++) {
   1.189 +            event.adevice.which = i;
   1.190 +            SDL_PushEvent(&event);
   1.191 +        }
   1.192 +    }
   1.193 +
   1.194      return 0;
   1.195  }
   1.196  
   1.197 @@ -835,53 +1006,6 @@
   1.198      return current_audio.name;
   1.199  }
   1.200  
   1.201 -static void
   1.202 -free_device_list(char ***devices, int *devCount)
   1.203 -{
   1.204 -    int i = *devCount;
   1.205 -    if ((i > 0) && (*devices != NULL)) {
   1.206 -        while (i--) {
   1.207 -            SDL_free((*devices)[i]);
   1.208 -        }
   1.209 -    }
   1.210 -
   1.211 -    SDL_free(*devices);
   1.212 -
   1.213 -    *devices = NULL;
   1.214 -    *devCount = 0;
   1.215 -}
   1.216 -
   1.217 -static
   1.218 -void SDL_AddCaptureAudioDevice(const char *_name)
   1.219 -{
   1.220 -    char *name = NULL;
   1.221 -    void *ptr = SDL_realloc(current_audio.inputDevices,
   1.222 -                          (current_audio.inputDeviceCount+1) * sizeof(char*));
   1.223 -    if (ptr == NULL) {
   1.224 -        return;  /* oh well. */
   1.225 -    }
   1.226 -
   1.227 -    current_audio.inputDevices = (char **) ptr;
   1.228 -    name = SDL_strdup(_name);  /* if this returns NULL, that's okay. */
   1.229 -    current_audio.inputDevices[current_audio.inputDeviceCount++] = name;
   1.230 -}
   1.231 -
   1.232 -static
   1.233 -void SDL_AddOutputAudioDevice(const char *_name)
   1.234 -{
   1.235 -    char *name = NULL;
   1.236 -    void *ptr = SDL_realloc(current_audio.outputDevices,
   1.237 -                          (current_audio.outputDeviceCount+1) * sizeof(char*));
   1.238 -    if (ptr == NULL) {
   1.239 -        return;  /* oh well. */
   1.240 -    }
   1.241 -
   1.242 -    current_audio.outputDevices = (char **) ptr;
   1.243 -    name = SDL_strdup(_name);  /* if this returns NULL, that's okay. */
   1.244 -    current_audio.outputDevices[current_audio.outputDeviceCount++] = name;
   1.245 -}
   1.246 -
   1.247 -
   1.248  int
   1.249  SDL_GetNumAudioDevices(int iscapture)
   1.250  {
   1.251 @@ -903,18 +1027,20 @@
   1.252          return 1;
   1.253      }
   1.254  
   1.255 -    if (iscapture) {
   1.256 -        free_device_list(&current_audio.inputDevices,
   1.257 -                         &current_audio.inputDeviceCount);
   1.258 -        current_audio.impl.DetectDevices(iscapture, SDL_AddCaptureAudioDevice);
   1.259 -        retval = current_audio.inputDeviceCount;
   1.260 -    } else {
   1.261 -        free_device_list(&current_audio.outputDevices,
   1.262 -                         &current_audio.outputDeviceCount);
   1.263 -        current_audio.impl.DetectDevices(iscapture, SDL_AddOutputAudioDevice);
   1.264 -        retval = current_audio.outputDeviceCount;
   1.265 +    if (current_audio.need_capture_device_redetect) {
   1.266 +        current_audio.need_capture_device_redetect = SDL_FALSE;
   1.267 +        perform_full_device_redetect(SDL_TRUE);
   1.268      }
   1.269  
   1.270 +    if (current_audio.need_output_device_redetect) {
   1.271 +        current_audio.need_output_device_redetect = SDL_FALSE;
   1.272 +        perform_full_device_redetect(SDL_FALSE);
   1.273 +    }
   1.274 +
   1.275 +    SDL_LockMutex(current_audio.detection_lock);
   1.276 +    retval = iscapture ? current_audio.inputDeviceCount : current_audio.outputDeviceCount;
   1.277 +    SDL_UnlockMutex(current_audio.detection_lock);
   1.278 +
   1.279      return retval;
   1.280  }
   1.281  
   1.282 @@ -922,6 +1048,8 @@
   1.283  const char *
   1.284  SDL_GetAudioDeviceName(int index, int iscapture)
   1.285  {
   1.286 +    const char *retval = NULL;
   1.287 +
   1.288      if (!SDL_WasInit(SDL_INIT_AUDIO)) {
   1.289          SDL_SetError("Audio subsystem is not initialized");
   1.290          return NULL;
   1.291 @@ -950,16 +1078,18 @@
   1.292          return DEFAULT_OUTPUT_DEVNAME;
   1.293      }
   1.294  
   1.295 -    if (iscapture) {
   1.296 -        if (index >= current_audio.inputDeviceCount) {
   1.297 -            goto no_such_device;
   1.298 -        }
   1.299 -        return current_audio.inputDevices[index];
   1.300 -    } else {
   1.301 -        if (index >= current_audio.outputDeviceCount) {
   1.302 -            goto no_such_device;
   1.303 -        }
   1.304 -        return current_audio.outputDevices[index];
   1.305 +    SDL_LockMutex(current_audio.detection_lock);
   1.306 +    if (iscapture && (index < current_audio.inputDeviceCount)) {
   1.307 +        retval = current_audio.inputDevices[index];
   1.308 +    } else if (!iscapture && (index < current_audio.outputDeviceCount)) {
   1.309 +        retval = current_audio.outputDevices[index];
   1.310 +    }
   1.311 +    SDL_UnlockMutex(current_audio.detection_lock);
   1.312 +
   1.313 +    /* !!! FIXME: a device could be removed after being returned here, freeing retval's pointer. */
   1.314 +
   1.315 +    if (retval != NULL) {
   1.316 +        return retval;
   1.317      }
   1.318  
   1.319  no_such_device:
   1.320 @@ -1077,6 +1207,18 @@
   1.321          return 0;
   1.322      }
   1.323  
   1.324 +    /* Find an available device ID... */
   1.325 +    for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) {
   1.326 +        if (open_devices[id] == NULL) {
   1.327 +            break;
   1.328 +        }
   1.329 +    }
   1.330 +
   1.331 +    if (id == SDL_arraysize(open_devices)) {
   1.332 +        SDL_SetError("Too many open audio devices");
   1.333 +        return 0;
   1.334 +    }
   1.335 +
   1.336      if (!obtained) {
   1.337          obtained = &_obtained;
   1.338      }
   1.339 @@ -1135,6 +1277,7 @@
   1.340          return 0;
   1.341      }
   1.342      SDL_zerop(device);
   1.343 +    device->id = id + 1;
   1.344      device->spec = *obtained;
   1.345      device->enabled = 1;
   1.346      device->paused = 1;
   1.347 @@ -1150,12 +1293,6 @@
   1.348          }
   1.349      }
   1.350  
   1.351 -    /* force a device detection if we haven't done one yet. */
   1.352 -    if ( ((iscapture) && (current_audio.inputDevices == NULL)) ||
   1.353 -         ((!iscapture) && (current_audio.outputDevices == NULL)) ) {
   1.354 -        SDL_GetNumAudioDevices(iscapture);
   1.355 -    }
   1.356 -
   1.357      if (current_audio.impl.OpenDevice(device, devname, iscapture) < 0) {
   1.358          close_audio_device(device);
   1.359          return 0;
   1.360 @@ -1247,25 +1384,14 @@
   1.361          device->spec.userdata = device;
   1.362      }
   1.363  
   1.364 -    /* Find an available device ID and store the structure... */
   1.365 -    for (id = min_id - 1; id < SDL_arraysize(open_devices); id++) {
   1.366 -        if (open_devices[id] == NULL) {
   1.367 -            open_devices[id] = device;
   1.368 -            break;
   1.369 -        }
   1.370 -    }
   1.371 -
   1.372 -    if (id == SDL_arraysize(open_devices)) {
   1.373 -        SDL_SetError("Too many open audio devices");
   1.374 -        close_audio_device(device);
   1.375 -        return 0;
   1.376 -    }
   1.377 +    /* add it to our list of open devices. */
   1.378 +    open_devices[id] = device;
   1.379  
   1.380      /* Start the audio thread if necessary */
   1.381      if (!current_audio.impl.ProvidesOwnCallbackThread) {
   1.382          /* Start the audio thread */
   1.383          char name[64];
   1.384 -        SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) (id + 1));
   1.385 +        SDL_snprintf(name, sizeof (name), "SDLAudioDev%d", (int) device->id);
   1.386  /* !!! FIXME: this is nasty. */
   1.387  #if defined(__WIN32__) && !defined(HAVE_LIBC)
   1.388  #undef SDL_CreateThread
   1.389 @@ -1278,13 +1404,13 @@
   1.390          device->thread = SDL_CreateThread(SDL_RunAudio, name, device);
   1.391  #endif
   1.392          if (device->thread == NULL) {
   1.393 -            SDL_CloseAudioDevice(id + 1);
   1.394 +            SDL_CloseAudioDevice(device->id);
   1.395              SDL_SetError("Couldn't create audio thread");
   1.396              return 0;
   1.397          }
   1.398      }
   1.399  
   1.400 -    return id + 1;
   1.401 +    return device->id;
   1.402  }
   1.403  
   1.404  
   1.405 @@ -1431,12 +1557,16 @@
   1.406  
   1.407      /* Free the driver data */
   1.408      current_audio.impl.Deinitialize();
   1.409 +
   1.410      free_device_list(&current_audio.outputDevices,
   1.411                       &current_audio.outputDeviceCount);
   1.412      free_device_list(&current_audio.inputDevices,
   1.413                       &current_audio.inputDeviceCount);
   1.414 -    SDL_memset(&current_audio, '\0', sizeof(current_audio));
   1.415 -    SDL_memset(open_devices, '\0', sizeof(open_devices));
   1.416 +
   1.417 +    SDL_DestroyMutex(current_audio.detection_lock);
   1.418 +
   1.419 +    SDL_zero(current_audio);
   1.420 +    SDL_zero(open_devices);
   1.421  }
   1.422  
   1.423  #define NUM_FORMATS 10