From 31da6d8c62cef07925fee28f0a2e65118e9d47e3 Mon Sep 17 00:00:00 2001
From: Lyubomir Marinov <lyubomir.marinov@jitsi.org>
Date: Wed, 19 Dec 2012 00:23:37 +0000
Subject: [PATCH] Commits the C source code of a refining of the automatic
 audio device selection to pick up devices from the same hardware.

---
 src/native/build.xml                          |   1 +
 .../portaudio/AudioQualityImprovement.h       |   6 +-
 src/native/portaudio/ConditionVariable.h      |   7 +-
 src/native/portaudio/Mutex.h                  |   7 +-
 src/native/portaudio/WMME_DSound.c            | 297 ++++++++++++++++++
 src/native/portaudio/WMME_DSound.h            |  47 +++
 .../org_jitsi_impl_neomedia_portaudio_Pa.c    |  36 ++-
 .../impl/neomedia/device/AudioSystem.java     |  17 +-
 .../impl/neomedia/device/CaptureDevices.java  |  19 +-
 .../neomedia/device/DeviceConfiguration.java  |  14 +-
 .../jitsi/impl/neomedia/device/Devices.java   |  88 +++---
 .../impl/neomedia/device/PlaybackDevices.java |  19 +-
 .../impl/neomedia/device/PortAudioSystem.java |  40 ++-
 13 files changed, 510 insertions(+), 88 deletions(-)
 create mode 100644 src/native/portaudio/WMME_DSound.c
 create mode 100644 src/native/portaudio/WMME_DSound.h

diff --git a/src/native/build.xml b/src/native/build.xml
index d753fa7f..0530b117 100644
--- a/src/native/build.xml
+++ b/src/native/build.xml
@@ -437,6 +437,7 @@
       <linkerarg value="-lspeexdsp" location="end" if="is.running.windows" />
       <linkerarg value="-lwinmm" location="end" if="is.running.windows" />
       <linkerarg value="-lsetupapi" location="end" if="is.running.windows" />
+      <linkerarg value="-ldsound" location="end" if="is.running.windows" />
       <linkerarg value="-lm" location="end" if="is.running.windows" />
       <linkerarg value="-lstdc++" location="end" if="is.running.windows" />
       <linkerarg value="-lole32" location="end" if="is.running.windows" />
diff --git a/src/native/portaudio/AudioQualityImprovement.h b/src/native/portaudio/AudioQualityImprovement.h
index 6aa2f83b..45255d31 100644
--- a/src/native/portaudio/AudioQualityImprovement.h
+++ b/src/native/portaudio/AudioQualityImprovement.h
@@ -5,8 +5,8 @@
  * See terms of license at gnu.org.
  */
 
-#ifndef _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_PORTAUDIO_AUDIOQUALITYIMPROVEMENT_H_
-#define _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_PORTAUDIO_AUDIOQUALITYIMPROVEMENT_H_
+#ifndef _ORG_JITSI_IMPL_NEOMEDIA_PORTAUDIO_AUDIOQUALITYIMPROVEMENT_H_
+#define _ORG_JITSI_IMPL_NEOMEDIA_PORTAUDIO_AUDIOQUALITYIMPROVEMENT_H_
 
 #include <jni.h>
 
@@ -162,4 +162,4 @@ void AudioQualityImprovement_setSampleRate
 /** Unloads the <tt>AudioQualityImprovement</tt> class. */
 void AudioQualityImprovement_unload();
 
-#endif /* #ifndef _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_PORTAUDIO_AUDIOQUALITYIMPROVEMENT_H_ */
+#endif /* #ifndef _ORG_JITSI_IMPL_NEOMEDIA_PORTAUDIO_AUDIOQUALITYIMPROVEMENT_H_ */
diff --git a/src/native/portaudio/ConditionVariable.h b/src/native/portaudio/ConditionVariable.h
index 30a65502..de04b6db 100644
--- a/src/native/portaudio/ConditionVariable.h
+++ b/src/native/portaudio/ConditionVariable.h
@@ -5,13 +5,12 @@
  * See terms of license at gnu.org.
  */
 
-#ifndef _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_CONDITIONVARIABLE_H_
-#define _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_CONDITIONVARIABLE_H_
+#ifndef _ORG_JITSI_IMPL_NEOMEDIA_PORTAUDIO_CONDITIONVARIABLE_H_
+#define _ORG_JITSI_IMPL_NEOMEDIA_PORTAUDIO_CONDITIONVARIABLE_H_
 
 #include "Mutex.h"
 
 #ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
 typedef HANDLE ConditionVariable;
@@ -92,4 +91,4 @@ static inline int ConditionVariable_wait
 }
 #endif /* #ifdef _WIN32 */
 
-#endif /* _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_CONDITIONVARIABLE_H_ */
+#endif /* _ORG_JITSI_IMPL_NEOMEDIA_PORTAUDIO_CONDITIONVARIABLE_H_ */
diff --git a/src/native/portaudio/Mutex.h b/src/native/portaudio/Mutex.h
index 93ab3ec7..96b33edc 100644
--- a/src/native/portaudio/Mutex.h
+++ b/src/native/portaudio/Mutex.h
@@ -5,13 +5,12 @@
  * See terms of license at gnu.org.
  */
 
-#ifndef _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_PORTAUDIO_MUTEX_H_
-#define _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_PORTAUDIO_MUTEX_H_
+#ifndef _ORG_JITSI_IMPL_NEOMEDIA_PORTAUDIO_MUTEX_H_
+#define _ORG_JITSI_IMPL_NEOMEDIA_PORTAUDIO_MUTEX_H_
 
 #include <stdlib.h>
 
 #ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 
 typedef CRITICAL_SECTION Mutex;
@@ -79,4 +78,4 @@ static inline int Mutex_unlock(Mutex* mutex)
 }
 #endif /* #ifdef _WIN32 */
 
-#endif /* #ifndef _NET_JAVA_SIP_COMMUNICATOR_IMPL_NEOMEDIA_PORTAUDIO_MUTEX_H_ */
+#endif /* #ifndef _ORG_JITSI_IMPL_NEOMEDIA_PORTAUDIO_MUTEX_H_ */
diff --git a/src/native/portaudio/WMME_DSound.c b/src/native/portaudio/WMME_DSound.c
new file mode 100644
index 00000000..02967798
--- /dev/null
+++ b/src/native/portaudio/WMME_DSound.c
@@ -0,0 +1,297 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+#ifdef _WIN32
+
+#include "WMME_DSound.h"
+
+#include <dsound.h>
+#include <objbase.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * The length in characters including the C string terminating NULL character of
+ * a GUID represented as a C string of printable characters including enclosing
+ * braces.
+ */
+#define GUID_LENGTH 39
+
+/**
+ * Represents the information related to a DirectSound device reported by
+ * either the <tt>DirectSoundCaptureEnumerate</tt> function or the
+ * <tt>DirectSoundEnumerate</tt> function.
+ */
+typedef struct _WMMEDSoundDeviceInfo
+{
+    /**
+     * The textual description of the DirectSound device that this instance
+     * provides information about. Represents the name of the device which is
+     * to be displayed to the user.
+     */
+    char *description;
+    /**
+     * The GUID which (uniquely) identifies the DirectSound device that this
+     * instance provides information about.
+     */
+    char guid[GUID_LENGTH];
+    /**
+     * The name of the module of the DirectSound driver corresponding to the
+     * device this instance provides information about.
+     */
+    char *module;
+
+    /**
+     * The next <tt>WMMEDSoundDeviceInfo</tt> in the linked list of
+     * <tt>WMMEDSoundDeviceInfo</tt>s that this instance is an element of.
+     */
+    struct _WMMEDSoundDeviceInfo *next;
+}
+WMMEDSoundDeviceInfo;
+
+static BOOL CALLBACK WMME_DSound_dsEnumCallback(
+        LPGUID guid, LPCSTR description, LPCSTR module,
+        LPVOID context);
+static WMMEDSoundDeviceInfo *WMME_DSound_findDeviceInfoByDeviceUID(
+        WMMEDSoundDeviceInfo *deviceInfos,
+        const char *deviceUID);
+/**
+ * Frees (the memory allocated to) a specific linked list of
+ * <tt>WMMEDSoundDeviceInfo</tt>s.
+ *
+ * @param deviceInfos the linked list of <tt>WMMEDSoundDeviceInfo</tt>s to be
+ * freed
+ */
+static void WMME_DSound_freeDeviceInfos(WMMEDSoundDeviceInfo **deviceInfos);
+
+/**
+ * The linked list of DirectSound capture devices enumerated till now and cached
+ * so that it may be searched through for the purposes of, for example,
+ * retrieving the non-truncated name of a <tt>PaDeviceInfo</tt> detected by
+ * PortAudio's WMME backend.
+ */
+static WMMEDSoundDeviceInfo *WMME_DSound_captureDeviceInfos = NULL;
+/**
+ * The linked list of DirectSound playback devices enumerated till now and
+ * cached so that it may be searched through for the purposes of, for example,
+ * retrieving the non-truncated name of a <tt>PaDeviceInfo</tt> detected by
+ * PortAudio's WMME backend.
+ */
+static WMMEDSoundDeviceInfo *WMME_DSound_playbackDeviceInfos = NULL;
+
+/**
+ * Retrieve a human-readable name for a specific <tt>PaDeviceInfo</tt> by
+ * utilizing information from DirectSound. The implementation is provided in an
+ * attempt to overcome a limitation of the legacy API employed by PortAudio's
+ * WMME backend which limits the names of the devices to 32 characters.
+ *
+ * @param deviceInfo the <tt>PaDeviceInfo</tt> to retrieve a human-readable name
+ * for by utilizing information from DirectSound
+ * @return a human-readable name of the specified <tt>deviceInfo</tt> retrieved
+ * by utilizing information from DirectSound or <tt>NULL</tt> if no such
+ * information is available for the specified <tt>deviceInfo</tt>
+ */
+const char *
+WMME_DSound_DeviceInfo_getName(PaDeviceInfo *deviceInfo)
+{
+    const char *deviceUID = deviceInfo->deviceUID;
+    const char *name = NULL;
+
+    if (deviceUID)
+    {
+        if (deviceInfo->maxInputChannels)
+        {
+            if (!WMME_DSound_captureDeviceInfos)
+            {
+                HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+
+                if ((S_OK == hr) || (S_FALSE == hr))
+                {
+                    DirectSoundCaptureEnumerateA(
+                            WMME_DSound_dsEnumCallback,
+                            (LPVOID) &WMME_DSound_captureDeviceInfos);
+                    CoUninitialize();
+                }
+            }
+            if (WMME_DSound_captureDeviceInfos)
+            {
+                WMMEDSoundDeviceInfo *match
+                    = WMME_DSound_findDeviceInfoByDeviceUID(
+                            WMME_DSound_captureDeviceInfos,
+                            deviceUID);
+
+                if (match)
+                    name = match->description;
+            }
+        }
+        if (!name && deviceInfo->maxOutputChannels)
+        {
+            if (!WMME_DSound_playbackDeviceInfos)
+            {
+                HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+
+                if ((S_OK == hr) || (S_FALSE == hr))
+                {
+                    DirectSoundEnumerateA(
+                            WMME_DSound_dsEnumCallback,
+                            (LPVOID) &WMME_DSound_playbackDeviceInfos);
+                    CoUninitialize();
+                }
+            }
+            if (WMME_DSound_playbackDeviceInfos)
+            {
+                WMMEDSoundDeviceInfo *match
+                    = WMME_DSound_findDeviceInfoByDeviceUID(
+                            WMME_DSound_playbackDeviceInfos,
+                            deviceUID);
+
+                if (match)
+                    name = match->description;
+            }
+        }
+    }
+    return name;
+}
+
+/**
+ * Notifies the <tt>WMME_DSound</tt> module that PortAudio's
+ * <tt>PaUpdateAvailableDeviceList()<tt> function has been invoked.
+ * Frees/destroys the caches of the capture and playback DirectSound devices so
+ * that they may be rebuilt and, consequently, up-to-date upon next use.
+ */
+void
+WMME_DSound_didUpdateAvailableDeviceList()
+{
+    WMME_DSound_freeDeviceInfos(&WMME_DSound_captureDeviceInfos);
+    WMME_DSound_freeDeviceInfos(&WMME_DSound_playbackDeviceInfos);
+}
+
+static BOOL CALLBACK
+WMME_DSound_dsEnumCallback(
+        LPGUID guid, LPCSTR description, LPCSTR module,
+        LPVOID context)
+{
+    if (guid && description)
+    {
+        OLECHAR olestrGUID[GUID_LENGTH];
+
+        if (StringFromGUID2(guid, olestrGUID, GUID_LENGTH))
+        {
+            WMMEDSoundDeviceInfo *deviceInfo
+                = malloc(sizeof(WMMEDSoundDeviceInfo));
+
+            if (deviceInfo)
+            {
+                if (WideCharToMultiByte(
+                            CP_ACP,
+                            0,
+                            olestrGUID, -1,
+                            deviceInfo->guid, GUID_LENGTH,
+                            NULL,
+                            NULL)
+                        == GUID_LENGTH)
+                {
+                    deviceInfo->description = strdup(description);
+                    if (deviceInfo->description)
+                    {
+                        WMMEDSoundDeviceInfo **deviceInfos
+                            = (WMMEDSoundDeviceInfo **) context;
+
+                        deviceInfo->module = strdup(module);
+
+                        deviceInfo->next = *deviceInfos;
+                        *deviceInfos = deviceInfo;
+                    }
+                    else
+                        free(deviceInfo);
+                }
+                else
+                    free(deviceInfo);
+            }
+        }
+    }
+
+    return TRUE;
+}
+
+static WMMEDSoundDeviceInfo *
+WMME_DSound_findDeviceInfoByDeviceUID(
+        WMMEDSoundDeviceInfo *deviceInfos,
+        const char *deviceUID)
+{
+    while (deviceInfos)
+    {
+        if (strstr(deviceUID, deviceInfos->guid))
+            return deviceInfos;
+        else
+        {
+            const char *module = deviceInfos->module;
+
+            if (module && strlen(module) && strstr(deviceUID, module))
+                return deviceInfos;
+        }
+
+        deviceInfos = deviceInfos->next;
+    }
+
+    return NULL;
+}
+
+/**
+ * Frees (the memory allocated to) a specific linked list of
+ * <tt>WMMEDSoundDeviceInfo</tt>s.
+ *
+ * @param deviceInfos the linked list of <tt>WMMEDSoundDeviceInfo</tt>s to be
+ * freed
+ */
+static void
+WMME_DSound_freeDeviceInfos(WMMEDSoundDeviceInfo **deviceInfos)
+{
+    WMMEDSoundDeviceInfo *deviceInfo = *deviceInfos;
+
+    while (deviceInfo)
+    {
+        char *description = deviceInfo->description;
+        char *module = deviceInfo->module;
+        WMMEDSoundDeviceInfo *next = deviceInfo->next;
+
+        if (description)
+            free(description);
+        if (module)
+            free(module);
+        free(deviceInfo);
+        deviceInfo = next;
+    }
+    *deviceInfos = NULL;
+}
+
+/**
+ * Notifies the <tt>WMME_DSound</tt> module that the JNI library it is a part of
+ * is loading.
+ */
+void
+WMME_DSound_load()
+{
+    /* TODO Auto-generated method stub */
+}
+
+/**
+ * Notifies the <tt>WMME_DSound</tt> module that the JNI library it is a part of
+ * is unloading.
+ */
+void
+WMME_DSound_unload()
+{
+    /*
+     * WMME_DSound frees/destroys the cached DirectSound device information when
+     * PortAudio is told to update its list of available devices. The very same
+     * cleanup has to be performed when the JNI library is unloading.
+     */
+    WMME_DSound_didUpdateAvailableDeviceList();
+}
+
+#endif /* #ifdef _WIN32 */
diff --git a/src/native/portaudio/WMME_DSound.h b/src/native/portaudio/WMME_DSound.h
new file mode 100644
index 00000000..f8f75df8
--- /dev/null
+++ b/src/native/portaudio/WMME_DSound.h
@@ -0,0 +1,47 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+#ifndef _ORG_JITSI_IMPL_NEOMEDIA_PORTAUDIO_WMME_DSOUND_H_
+#define _ORG_JITSI_IMPL_NEOMEDIA_PORTAUDIO_WMME_DSOUND_H_
+
+#include <portaudio.h>
+
+/**
+ * Retrieve a human-readable name for a specific <tt>PaDeviceInfo</tt> by
+ * utilizing information from DirectSound. The implementation is provided in an
+ * attempt to overcome a limitation of the legacy API employed by PortAudio's
+ * WMME backend which limits the names of the devices to 32 characters.
+ *
+ * @param deviceInfo the <tt>PaDeviceInfo</tt> to retrieve a human-readable name
+ * for by utilizing information from DirectSound
+ * @return a human-readable name of the specified <tt>deviceInfo</tt> retrieved
+ * by utilizing information from DirectSound or <tt>NULL</tt> if no such
+ * information is available for the specified <tt>deviceInfo</tt>
+ */
+const char *WMME_DSound_DeviceInfo_getName(PaDeviceInfo *deviceInfo);
+
+/**
+ * Notifies the <tt>WMME_DSound</tt> module that PortAudio's
+ * <tt>PaUpdateAvailableDeviceList()<tt> function has been invoked.
+ * Frees/destroys the caches of the capture and playback DirectSound devices so
+ * that they may be rebuilt and, consequently, up-to-date upon next use.
+ */
+void WMME_DSound_didUpdateAvailableDeviceList();
+
+/**
+ * Notifies the <tt>WMME_DSound</tt> module that the JNI library it is a part of
+ * is loading.
+ */
+void WMME_DSound_load();
+
+/**
+ * Notifies the <tt>WMME_DSound</tt> module that the JNI library it is a part of
+ * is unloading.
+ */
+void WMME_DSound_unload();
+
+#endif /* #ifndef _ORG_JITSI_IMPL_NEOMEDIA_PORTAUDIO_WMME_DSOUND_H_ */
diff --git a/src/native/portaudio/org_jitsi_impl_neomedia_portaudio_Pa.c b/src/native/portaudio/org_jitsi_impl_neomedia_portaudio_Pa.c
index c2b22f54..0be49486 100644
--- a/src/native/portaudio/org_jitsi_impl_neomedia_portaudio_Pa.c
+++ b/src/native/portaudio/org_jitsi_impl_neomedia_portaudio_Pa.c
@@ -10,6 +10,7 @@
 #include "AudioQualityImprovement.h"
 #include "ConditionVariable.h"
 #include "Mutex.h"
+
 #include <portaudio.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -17,6 +18,10 @@
 #include <string.h>
 #include <sys/time.h>
 
+#ifdef _WIN32
+    #include "WMME_DSound.h"
+#endif /* #ifdef _WIN32 */
+
 typedef struct
 {
     AudioQualityImprovement *audioQualityImprovement;
@@ -250,10 +255,24 @@ JNIEXPORT jbyteArray JNICALL
 Java_org_jitsi_impl_neomedia_portaudio_Pa_DeviceInfo_1getNameBytes
     (JNIEnv *env, jclass clazz, jlong deviceInfo)
 {
-    return
-        PortAudio_getStrBytes(
-            env,
-            ((PaDeviceInfo *) (intptr_t) deviceInfo)->name);
+    PaDeviceInfo *di = (PaDeviceInfo *) (intptr_t) deviceInfo;
+    const char *name;
+
+#ifdef _WIN32
+    const PaHostApiInfo *hai = Pa_GetHostApiInfo(di->hostApi);
+
+    if (hai && (paMME == hai->type))
+    {
+        name = WMME_DSound_DeviceInfo_getName(di);
+        if (!name)
+            name = di->name;
+    }
+    else
+        name = di->name;
+#else /* #ifdef _WIN32 */
+    name = di->name;
+#endif /* #ifdef _WIN32 */
+    return PortAudio_getStrBytes(env, name);
 }
 
 JNIEXPORT jbyteArray JNICALL
@@ -876,6 +895,9 @@ Java_org_jitsi_impl_neomedia_portaudio_Pa_UpdateAvailableDeviceList
     (JNIEnv *env, jclass clazz)
 {
     Pa_UpdateAvailableDeviceList();
+#ifdef _WIN32
+    WMME_DSound_didUpdateAvailableDeviceList();
+#endif /* #ifdef _WIN32 */
 }
 
 JNIEXPORT void JNICALL
@@ -1005,6 +1027,9 @@ JNI_OnLoad(JavaVM *vm, void *reserved)
 {
     PortAudio_vm = vm;
     AudioQualityImprovement_load();
+#ifdef _WIN32
+    WMME_DSound_load();
+#endif /* #ifdef _WIN32 */
 
     return JNI_VERSION_1_4;
 }
@@ -1013,6 +1038,9 @@ JNIEXPORT void JNICALL
 JNI_OnUnload(JavaVM *vm, void *reserved)
 {
     AudioQualityImprovement_unload();
+#ifdef _WIN32
+    WMME_DSound_unload();
+#endif /* #ifdef _WIN32 */
     PortAudio_vm = NULL;
 }
 
diff --git a/src/org/jitsi/impl/neomedia/device/AudioSystem.java b/src/org/jitsi/impl/neomedia/device/AudioSystem.java
index 8a1ac516..6cb47b70 100644
--- a/src/org/jitsi/impl/neomedia/device/AudioSystem.java
+++ b/src/org/jitsi/impl/neomedia/device/AudioSystem.java
@@ -24,6 +24,7 @@
  * integrate the native PortAudio, PulseAudio libraries.
  *
  * @author Lyubomir Marinov
+ * @author Vincent Lucas
  */
 public abstract class AudioSystem
     extends DeviceSystem
@@ -293,15 +294,17 @@ protected void postInitializeSpecificDevices(int index)
         List<ExtendedCaptureDeviceInfo> activeDevices = getDevices(index);
         // Gets the default device.
         Devices devices = this.devices[index];
+        String locatorProtocol = getLocatorProtocol();
         ExtendedCaptureDeviceInfo selectedActiveDevice
-            = devices.getDevice(getLocatorProtocol(), activeDevices);
-        // Sets the default device as selected (this function will only fire a
-        // property change if the device has changed from previous
-        // configuration).
-        // This "set" part is important because only the fire property event
-        // provides a way to get hot plugged devices working during a call.
+            = devices.getDevice(locatorProtocol, activeDevices);
+
+        // Sets the default device as selected. The function will fire a
+        // property change only if the device has changed from a previous
+        // configuration. The "set" part is important because only the fired
+        // property event provides a way to get the hotplugged devices working
+        // during a call.
         devices.setDevice(
-                getLocatorProtocol(),
+                locatorProtocol,
                 selectedActiveDevice,
                 false,
                 activeDevices);
diff --git a/src/org/jitsi/impl/neomedia/device/CaptureDevices.java b/src/org/jitsi/impl/neomedia/device/CaptureDevices.java
index 6e58099e..9a296ea9 100644
--- a/src/org/jitsi/impl/neomedia/device/CaptureDevices.java
+++ b/src/org/jitsi/impl/neomedia/device/CaptureDevices.java
@@ -32,7 +32,7 @@ public class CaptureDevices
     /**
      * The list of active (actually plugged-in) capture devices.
      */
-    private List<ExtendedCaptureDeviceInfo> activeCaptureDevices = null;
+    private List<ExtendedCaptureDeviceInfo> activeCaptureDevices;
 
     /**
      * Initializes the capture device list management.
@@ -51,9 +51,11 @@ public CaptureDevices(AudioSystem audioSystem)
      */
     public List<ExtendedCaptureDeviceInfo> getDevices()
     {
-        List<ExtendedCaptureDeviceInfo> devices = null;
+        List<ExtendedCaptureDeviceInfo> devices;
 
-        if(activeCaptureDevices != null)
+        if(activeCaptureDevices == null)
+            devices = Collections.emptyList();
+        else
         {
             devices
                 = new ArrayList<ExtendedCaptureDeviceInfo>(
@@ -94,7 +96,9 @@ protected String getPropDevice()
      */
     public void setActiveDevices(List<ExtendedCaptureDeviceInfo> activeDevices)
     {
-        if(activeDevices != null)
+        if(activeDevices == null)
+            activeCaptureDevices = null;
+        else
         {
             boolean commit = false;
 
@@ -114,10 +118,9 @@ public void setActiveDevices(List<ExtendedCaptureDeviceInfo> activeDevices)
                     // Whatever.
                 }
             }
-        }
 
-        activeCaptureDevices = (activeDevices == null)
-                ? null
-                : new ArrayList<ExtendedCaptureDeviceInfo>(activeDevices);
+            activeCaptureDevices
+                = new ArrayList<ExtendedCaptureDeviceInfo>(activeDevices);
+        }
     }
 }
diff --git a/src/org/jitsi/impl/neomedia/device/DeviceConfiguration.java b/src/org/jitsi/impl/neomedia/device/DeviceConfiguration.java
index 9fc8bd83..90b34921 100644
--- a/src/org/jitsi/impl/neomedia/device/DeviceConfiguration.java
+++ b/src/org/jitsi/impl/neomedia/device/DeviceConfiguration.java
@@ -405,9 +405,10 @@ public ExtendedCaptureDeviceInfo getAudioCaptureDevice()
     {
         AudioSystem audioSystem = getAudioSystem();
 
-        return (audioSystem == null)
-            ? null
-            : audioSystem.getDevice(AudioSystem.CAPTURE_INDEX);
+        return
+            (audioSystem == null)
+                ? null
+                : audioSystem.getDevice(AudioSystem.CAPTURE_INDEX);
     }
 
     /**
@@ -668,9 +669,10 @@ public CaptureDeviceInfo getAudioNotifyDevice()
     {
         AudioSystem audioSystem = getAudioSystem();
 
-        return (audioSystem == null)
-            ? null
-            : audioSystem.getDevice(AudioSystem.NOTIFY_INDEX);
+        return
+            (audioSystem == null)
+                ? null
+                : audioSystem.getDevice(AudioSystem.NOTIFY_INDEX);
     }
 
     /**
diff --git a/src/org/jitsi/impl/neomedia/device/Devices.java b/src/org/jitsi/impl/neomedia/device/Devices.java
index 56ab17b8..a8f32f83 100644
--- a/src/org/jitsi/impl/neomedia/device/Devices.java
+++ b/src/org/jitsi/impl/neomedia/device/Devices.java
@@ -67,9 +67,11 @@ public ExtendedCaptureDeviceInfo getDevice(
             // Search if an active device is a new one (is not stored in the
             // preferences yet). If true, then active this device and set it as
             // default device (only for USB devices since the user has
-            // deliberately plugged-in the device).
-            for(ExtendedCaptureDeviceInfo activeDevice : activeDevices)
+            // deliberately plugged in the device).
+            for(int i = activeDevices.size() - 1; i >= 0; i--)
             {
+                ExtendedCaptureDeviceInfo activeDevice = activeDevices.get(i);
+
                 if(!devicePreferences.contains(activeDevice.getIdentifier()))
                 {
                     // Adds the device in the preference list (to the end of the
@@ -204,11 +206,10 @@ private void saveDevice(
             List<ExtendedCaptureDeviceInfo> activeDevices,
             boolean isSelected)
     {
-        String selectedDeviceIdentifier = NoneAudioSystem.LOCATOR_PROTOCOL;
-        if(device != null)
-        {
-            selectedDeviceIdentifier = device.getIdentifier();
-        }
+        String selectedDeviceIdentifier
+            = (device == null)
+                ? NoneAudioSystem.LOCATOR_PROTOCOL
+                : device.getIdentifier();
 
         // Sorts the user preferences to put the selected device on top.
         addToDevicePreferences(
@@ -288,24 +289,25 @@ private void addToDevicePreferences(
             String newDeviceIdentifier,
             boolean isSelected)
     {
-        int i;
-        int j;
-
         synchronized(devicePreferences)
         {
             devicePreferences.remove(newDeviceIdentifier);
             if(isSelected)
             {
-                // Searches for the first active device.
-                for(i = 0; i < devicePreferences.size(); ++i)
+                // Search for the first active device.
+                for(int i = 0, devicePreferenceCount = devicePreferences.size();
+                        i < devicePreferenceCount;
+                        i++)
                 {
-                    // Checks if this element is an active device.
-                    for(j = 0; j < activeDevices.size(); ++j)
+                    String devicePreference = devicePreferences.get(i);
+
+                    // Check if devicePreference is an active device.
+                    for(ExtendedCaptureDeviceInfo activeDevice : activeDevices)
                     {
-                        if(devicePreferences.get(i).equals(
-                                    activeDevices.get(j).getIdentifier())
-                                || devicePreferences.get(i).equals(
-                                    NoneAudioSystem.LOCATOR_PROTOCOL))
+                        if(devicePreference.equals(
+                                    activeDevice.getIdentifier())
+                                || devicePreference.equals(
+                                        NoneAudioSystem.LOCATOR_PROTOCOL))
                         {
                             // The first active device is found.
                             devicePreferences.add(i, newDeviceIdentifier);
@@ -315,7 +317,7 @@ private void addToDevicePreferences(
                     }
                 }
             }
-            // If there is no active devices or the device is not selected, then
+            // If there is no active device or the device is not selected, then
             // set the new device to the end of the device preference list.
             devicePreferences.add(newDeviceIdentifier);
         }
@@ -330,46 +332,40 @@ private void addToDevicePreferences(
     private void renameOldFashionedIdentifier(
             List<ExtendedCaptureDeviceInfo> activeDevices)
     {
-        String name;
-        String id;
-        int nameIndex;
-        int idIndex;
         // Renames the old fashioned device identifier for all active devices.
-        for(int i = 0; i < activeDevices.size(); ++i)
+        for(ExtendedCaptureDeviceInfo activeDevice : activeDevices)
         {
-            name = activeDevices.get(i).getName();
-            id = activeDevices.get(i).getIdentifier();
+            String name = activeDevice.getName();
+            String id = activeDevice.getIdentifier();
+
             // We can only switch to the new fashioned notation, only if the OS
-            // api give us a unique identifier (different from the device name).
+            // API gives us a unique identifier (different from the device
+            // name).
             if(!name.equals(id))
             {
                 synchronized(devicePreferences)
                 {
                     do
                     {
-                        nameIndex = devicePreferences.indexOf(
-                                activeDevices.get(i).getName());
-                        idIndex = devicePreferences.indexOf(
-                                activeDevices.get(i).getIdentifier());
+                        int nameIndex = devicePreferences.indexOf(name);
+
                         // If there is one old fashioned identifier.
-                        if(nameIndex != -1)
+                        if(nameIndex == -1)
+                            break;
+                        else
                         {
+                            int idIndex = devicePreferences.indexOf(id);
+
                             // If the corresponding new fashioned identifier
-                            // does not exists, then renames the old one into
+                            // does not exist, then renames the old one into
                             // the new one.
                             if(idIndex == -1)
-                            {
-                                devicePreferences.set(nameIndex,
-                                        activeDevices.get(i).getIdentifier());
-                            }
-                            // Else removes the duplicate.
-                            else
-                            {
+                                devicePreferences.set(nameIndex, id);
+                            else // Remove the duplicate.
                                 devicePreferences.remove(nameIndex);
-                            }
                         }
                     }
-                    while(nameIndex != -1);
+                    while(true);
                 }
             }
         }
@@ -397,13 +393,13 @@ private void writeDevicePreferences(String locator, String property)
 
             synchronized(devicePreferences)
             {
-                if(devicePreferences.size() > 0)
+                int devicePreferenceCount = devicePreferences.size();
+
+                if(devicePreferenceCount != 0)
                 {
                     value.append(devicePreferences.get(0));
-                    for(int i = 1; i < devicePreferences.size(); ++i)
-                    {
+                    for(int i = 1; i < devicePreferenceCount; i++)
                         value.append("\", \"").append(devicePreferences.get(i));
-                    }
                 }
             }
             value.append("\"]");
diff --git a/src/org/jitsi/impl/neomedia/device/PlaybackDevices.java b/src/org/jitsi/impl/neomedia/device/PlaybackDevices.java
index e7b64895..43d63b0c 100644
--- a/src/org/jitsi/impl/neomedia/device/PlaybackDevices.java
+++ b/src/org/jitsi/impl/neomedia/device/PlaybackDevices.java
@@ -26,7 +26,7 @@ public class PlaybackDevices
     /**
      * The list of active (actually plugged-in) playback devices.
      */
-    private List<ExtendedCaptureDeviceInfo> activePlaybackDevices = null;
+    private List<ExtendedCaptureDeviceInfo> activePlaybackDevices;
 
     /**
      * Initializes the playback device list management.
@@ -45,10 +45,18 @@ public PlaybackDevices(AudioSystem audioSystem)
      */
     public List<ExtendedCaptureDeviceInfo> getDevices()
     {
-        return (activePlaybackDevices == null)
-                ? null
-                : new ArrayList<ExtendedCaptureDeviceInfo>(
+        List<ExtendedCaptureDeviceInfo> devices;
+
+        if (activePlaybackDevices == null)
+            devices = Collections.emptyList();
+        else
+        {
+            devices
+                = new ArrayList<ExtendedCaptureDeviceInfo>(
                         activePlaybackDevices);
+        }
+
+        return devices;
     }
 
     /**
@@ -68,7 +76,8 @@ protected String getPropDevice()
      */
     public void setActiveDevices(List<ExtendedCaptureDeviceInfo> activeDevices)
     {
-        activePlaybackDevices = (activeDevices == null)
+        activePlaybackDevices
+            = (activeDevices == null)
                 ? null
                 : new ArrayList<ExtendedCaptureDeviceInfo>(activeDevices);
     }
diff --git a/src/org/jitsi/impl/neomedia/device/PortAudioSystem.java b/src/org/jitsi/impl/neomedia/device/PortAudioSystem.java
index 2af313dd..c46f6164 100644
--- a/src/org/jitsi/impl/neomedia/device/PortAudioSystem.java
+++ b/src/org/jitsi/impl/neomedia/device/PortAudioSystem.java
@@ -147,6 +147,41 @@ else if (l.equals(listener))
         }
     }
 
+    /**
+     * Sorts a specific list of <tt>ExtendedCaptureDeviceInfo</tt>s so that the
+     * ones representing USB devices appear at the beginning/top of the
+     * specified list.
+     *
+     * @param devices the list of <tt>ExtendedCaptureDeviceInfo</tt>s to be
+     * sorted so that the ones representing USB devices appear at the
+     * beginning/top of the list
+     */
+    private void bubbleUpUsbDevices(List<ExtendedCaptureDeviceInfo> devices)
+    {
+        if (!devices.isEmpty())
+        {
+            List<ExtendedCaptureDeviceInfo> nonUsbDevices
+                = new ArrayList<ExtendedCaptureDeviceInfo>(devices.size());
+
+            for (Iterator<ExtendedCaptureDeviceInfo> i = devices.iterator();
+                    i.hasNext();)
+            {
+                ExtendedCaptureDeviceInfo d = i.next();
+
+                if (!d.isSameTransportType("USB"))
+                {
+                    nonUsbDevices.add(d);
+                    i.remove();
+                }
+            }
+            if (!nonUsbDevices.isEmpty())
+            {
+                for (ExtendedCaptureDeviceInfo d : nonUsbDevices)
+                    devices.add(d);
+            }
+        }
+    }
+
     @Override
     public Renderer createRenderer(boolean playback)
     {
@@ -366,6 +401,8 @@ else if (maxOutputChannels > 0)
          * capture or playback (in order to achieve our goal to have automatic
          * selection pick up devices from one and the same hardware).
          */
+        bubbleUpUsbDevices(captureDevices);
+        bubbleUpUsbDevices(playbackDevices);
         if (!captureDevices.isEmpty() && !playbackDevices.isEmpty())
         {
             /*
@@ -382,6 +419,7 @@ else if (maxOutputChannels > 0)
          */
         if (!captureAndPlaybackDevices.isEmpty())
         {
+            bubbleUpUsbDevices(captureAndPlaybackDevices);
             for (int i = captureAndPlaybackDevices.size() - 1; i >= 0; i--)
             {
                 ExtendedCaptureDeviceInfo cdi
@@ -581,7 +619,7 @@ private void matchDevicesByName(
             = captureDevices.iterator();
         Pattern pattern
             = Pattern.compile(
-                    "microphone|speakers|\\p{Space}|\\(|\\)",
+                    "array|headphones|microphone|speakers|\\p{Space}|\\(|\\)",
                     Pattern.CASE_INSENSITIVE);
         LinkedList<ExtendedCaptureDeviceInfo> captureDevicesWithPlayback
             = new LinkedList<ExtendedCaptureDeviceInfo>();
-- 
GitLab