From 94bcfef3050bb31ffba834549b23a550557e939b Mon Sep 17 00:00:00 2001
From: Vincent Lucas <chenzo@jitsi.org>
Date: Thu, 22 Nov 2012 16:49:58 +0000
Subject: [PATCH] Adds microphone hardware amplification for MacOSX via
 CoreAudio.

---
 lib/native/mac/libjncoreaudio.jnilib          | Bin 0 -> 35132 bytes
 src/native/build.xml                          |  33 ++
 ..._impl_neomedia_coreaudio_CoreAudioDevice.c | 111 +++++
 ..._impl_neomedia_coreaudio_CoreAudioDevice.h |  60 +++
 src/native/macosx/coreaudio/lib/device.c      | 459 ++++++++++++++++++
 src/native/macosx/coreaudio/lib/device.h      |  39 ++
 .../impl/neomedia/AbstractVolumeControl.java  | 130 +++--
 .../jitsi/impl/neomedia/MediaServiceImpl.java |  17 +-
 .../neomedia/coreaudio/CoreAudioDevice.java   |  51 ++
 .../coreaudio/CoreAudioVolumeControl.java     | 113 +++++
 src/org/jitsi/impl/neomedia/portaudio/Pa.java |  38 +-
 src/org/jitsi/util/StringUtils.java           |  31 ++
 12 files changed, 1008 insertions(+), 74 deletions(-)
 create mode 100755 lib/native/mac/libjncoreaudio.jnilib
 create mode 100644 src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice.c
 create mode 100644 src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice.h
 create mode 100644 src/native/macosx/coreaudio/lib/device.c
 create mode 100644 src/native/macosx/coreaudio/lib/device.h
 create mode 100644 src/org/jitsi/impl/neomedia/coreaudio/CoreAudioDevice.java
 create mode 100644 src/org/jitsi/impl/neomedia/coreaudio/CoreAudioVolumeControl.java

diff --git a/lib/native/mac/libjncoreaudio.jnilib b/lib/native/mac/libjncoreaudio.jnilib
new file mode 100755
index 0000000000000000000000000000000000000000..07697e3d57ede90963f041cf48ffebe2fb7e4ff0
GIT binary patch
literal 35132
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

literal 0
HcmV?d00001

diff --git a/src/native/build.xml b/src/native/build.xml
index 5071e585..b257e950 100644
--- a/src/native/build.xml
+++ b/src/native/build.xml
@@ -668,6 +668,38 @@
     </cc>
   </target>
 
+  <!-- compile jncoreaudio library for Mac OS X (32-bit/64-bit) -->
+  <target name="coreaudio" description="Build jncoreaudio shared library for Mac OS X" if="is.running.macos"
+    depends="init-native">
+    <cc outtype="shared" name="gcc" outfile="${native_install_dir}/jncoreaudio" objdir="${obj}">
+      <compilerarg value="-Wall" />
+      <compilerarg value="-O2" />
+      <compilerarg value="-arch" />
+      <compilerarg value="x86_64" />
+      <compilerarg value="-arch" />
+      <compilerarg value="i386" />
+      <compilerarg value="-I/System/Library/Frameworks/JavaVM.framework/Headers"
+      />
+
+      <linkerarg value="-o" location="end" />
+      <linkerarg value="libjncoreaudio.jnilib" location="end" />
+      <linkerarg value="-arch" />
+      <linkerarg value="x86_64" />
+      <linkerarg value="-arch" />
+      <linkerarg value="i386" />
+      <linkerarg value="-framework" />
+      <linkerarg value="Foundation" />
+      <linkerarg value="-framework" />
+      <linkerarg value="Coreaudio" />
+
+      <fileset dir="${src}/native/macosx/coreaudio/lib" includes="*.c"/>
+      <fileset dir="${src}/native/macosx/coreaudio/jni" includes="*.c"/>
+    </cc>
+
+    <delete dir="${obj}" failonerror="false" />
+    <delete file="${native_install_dir}/history.xml" failonerror="false" />
+  </target>
+
   <!-- compile jnquicktime library for Mac OS X (32-bit/64-bit/ppc) -->
   <target name="quicktime" description="Build jnquicktime shared library for Mac OS X" if="is.running.macos"
     depends="init-native">
@@ -767,6 +799,7 @@
     <echo message="'ant directshow (Windows only)' to compile jdirectshow shared library" />
     <echo message="'ant aegeturleventhandler (Mac OS X only)' to compile AEGetURLEventHandler shared library" />
     <echo message="'ant sparkle (Mac OS X only)' to compile sparkle shared library" />
+    <echo message="'ant coreaudio (Mac OS X only)' to compile jcoreaudio shared library" />
     <echo message="'ant quicktime (Mac OS X only)' to compile jquicktime shared library" />
     <echo message="" />
     <echo message="Options:" />
diff --git a/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice.c b/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice.c
new file mode 100644
index 00000000..d66c9aa4
--- /dev/null
+++ b/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice.c
@@ -0,0 +1,111 @@
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+#include "org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice.h"
+
+#include "../lib/device.h"
+
+/**
+ * JNI code for CoreAudioDevice.
+ *
+ * @author Vicnent Lucas
+ */
+
+// Private functions
+
+static jbyteArray getStrBytes(JNIEnv *env, const char *str);
+
+// Implementation
+
+JNIEXPORT jbyteArray JNICALL
+Java_org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice_getDeviceNameBytes
+  (JNIEnv *env, jclass clazz, jstring deviceUID)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    char * deviceName = getDeviceName(deviceUIDPtr);
+    jbyteArray deviceNameBytes = getStrBytes(env, deviceName);
+    // Free
+    free(deviceName);
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return deviceNameBytes;
+}
+
+JNIEXPORT jint JNICALL
+Java_org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice_setInputDeviceVolume
+  (JNIEnv *env, jclass clazz, jstring deviceUID, jfloat volume)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    jint err = setInputDeviceVolume(deviceUIDPtr, volume);
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return err;
+}
+
+JNIEXPORT jint JNICALL
+Java_org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice_setOutputDeviceVolume
+  (JNIEnv *env, jclass clazz, jstring deviceUID, jfloat volume)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    jint err = setOutputDeviceVolume(deviceUIDPtr, volume);
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return err;
+}
+
+JNIEXPORT jfloat JNICALL
+Java_org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice_getInputDeviceVolume
+  (JNIEnv *env, jclass clazz, jstring deviceUID)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    jfloat volume = getInputDeviceVolume(deviceUIDPtr);
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return volume;
+}
+
+JNIEXPORT jfloat JNICALL
+Java_org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice_getOutputDeviceVolume
+  (JNIEnv *env, jclass clazz, jstring deviceUID)
+{
+    const char * deviceUIDPtr = (*env)->GetStringUTFChars(env, deviceUID, 0);
+    jfloat volume = getOutputDeviceVolume(deviceUIDPtr);
+    // Free
+    (*env)->ReleaseStringUTFChars(env, deviceUID, deviceUIDPtr);
+
+    return volume;
+}
+
+/**
+ * Gets a new <tt>jbyteArray</tt> instance which is initialized with the bytes
+ * of a specific C string i.e. <tt>const char *</tt>.
+ *
+ * @param env
+ * @param str the bytes/C string to initialize the new <tt>jbyteArray</tt>
+ * instance with
+ * @return a new <tt>jbyteArray</tt> instance which is initialized with the
+ * bytes of the specified <tt>str</tt>
+ */
+static jbyteArray getStrBytes(JNIEnv *env, const char *str)
+{
+    jbyteArray bytes;
+
+    if (str)
+    {
+        size_t length = strlen(str);
+
+        bytes = (*env)->NewByteArray(env, length);
+        if (bytes && length)
+            (*env)->SetByteArrayRegion(env, bytes, 0, length, (jbyte *) str);
+    }
+    else
+        bytes = NULL;
+    return bytes;
+}
diff --git a/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice.h b/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice.h
new file mode 100644
index 00000000..5e7e83bb
--- /dev/null
+++ b/src/native/macosx/coreaudio/jni/org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice.h
@@ -0,0 +1,60 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice */
+
+#ifndef _Included_org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice
+#define _Included_org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class:     org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice
+ * Method:    getDeviceNameBytes
+ * Signature: (Ljava/lang/String;)[B
+ */
+JNIEXPORT jbyteArray JNICALL Java_org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice_getDeviceNameBytes
+  (JNIEnv *, jclass, jstring);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice
+ * Method:    setInputDeviceVolume
+ * Signature: (Ljava/lang/String;F)I
+ */
+JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice_setInputDeviceVolume
+  (JNIEnv *, jclass, jstring, jfloat);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice
+ * Method:    setOutputDeviceVolume
+ * Signature: (Ljava/lang/String;F)I
+ */
+JNIEXPORT jint JNICALL Java_org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice_setOutputDeviceVolume
+  (JNIEnv *, jclass, jstring, jfloat);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice
+ * Method:    getInputDeviceVolume
+ * Signature: (Ljava/lang/String;)F
+ */
+JNIEXPORT jfloat JNICALL Java_org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice_getInputDeviceVolume
+  (JNIEnv *, jclass, jstring);
+
+/*
+ * Class:     org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice
+ * Method:    getOutputDeviceVolume
+ * Signature: (Ljava/lang/String;)F
+ */
+JNIEXPORT jfloat JNICALL Java_org_jitsi_impl_neomedia_coreaudio_CoreAudioDevice_getOutputDeviceVolume
+  (JNIEnv *, jclass, jstring);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/native/macosx/coreaudio/lib/device.c b/src/native/macosx/coreaudio/lib/device.c
new file mode 100644
index 00000000..a6bfe348
--- /dev/null
+++ b/src/native/macosx/coreaudio/lib/device.c
@@ -0,0 +1,459 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+#include "device.h"
+
+#include <CoreAudio/CoreAudio.h>
+#include <CoreFoundation/CFString.h>
+#include <stdio.h>
+/**
+ * Functions to list, access and modifies audio devices via coreaudio.
+ *
+ * @author Vincent Lucas
+ */
+
+/**
+ * Private definition of functions,
+ */
+OSStatus setDeviceVolume(
+        const char * deviceUID,
+        Float32 volume,
+        UInt32 inputOutputScope);
+
+Float32 getDeviceVolume(
+        const char * deviceUID,
+        UInt32 inputOutputScope);
+
+OSStatus getChannelsForStereo(
+        const char * deviceUID,
+        UInt32 * channels);
+
+/**
+ * Returns the audio device corresponding to the UID given in parameter. Or
+ * kAudioObjectUnknown if the device is nonexistant or if anything as failed.
+ *
+ * @pqrqm deviceUID The device UID.
+ *
+ * @return The audio device corresponding to the UID given in parameter. Or
+ * kAudioObjectUnknown if the device is nonexistant or if anything as failed.
+ */
+AudioDeviceID getDevice(
+        const char * deviceUID)
+{
+    OSStatus err = noErr;
+    AudioObjectPropertyAddress address;
+    AudioDeviceID device = kAudioObjectUnknown;
+    UInt32 size;
+
+    // Converts the device UID into a ref.
+    CFStringRef deviceUIDRef;
+    if((deviceUIDRef = CFStringCreateWithCString(
+            kCFAllocatorDefault,
+            deviceUID,
+            kCFStringEncodingASCII)) == NULL)
+    {
+        fprintf(stderr,
+                "getDevice (coreaudio/device.c): \
+                    \n\tCFStringCreateWithCString\n");
+        return kAudioObjectUnknown;
+    }
+
+    // Gets the device corresponding to the given UID.
+    AudioValueTranslation translation;
+    translation.mInputData = &deviceUIDRef;
+    translation.mInputDataSize = sizeof(deviceUIDRef);
+    translation.mOutputData = &device;
+    translation.mOutputDataSize = sizeof(device);
+    size = sizeof(translation);
+    address.mSelector = kAudioHardwarePropertyDeviceForUID;
+    address.mScope = kAudioObjectPropertyScopeGlobal;
+    address.mElement = kAudioObjectPropertyElementMaster;
+
+    if((err = AudioObjectGetPropertyData(
+            kAudioObjectSystemObject,
+            &address,
+            0,
+            NULL,
+            &size,
+            &translation)) != noErr)
+    {
+        fprintf(stderr,
+                "getDevice (coreaudio/device.c): \
+                    \n\tAudioObjectGetPropertyData, err: %d\n",
+                ((int) err));
+        return kAudioObjectUnknown;
+    }
+
+    // Frees the allocated device UID ref.
+    CFRelease(deviceUIDRef);
+
+    return device;
+}
+
+/**
+ * Returns the device name for the given device. Or NULL, if not available. The
+ * returned string must be freed by the caller.
+ *
+ * @param device The device to get the name from.
+ *
+ * @return The device name for the given device. Or NULL, if not available. The
+ * returned string must be freed by the caller.
+ */
+char* getDeviceName(
+        const char * deviceUID)
+{
+    AudioDeviceID device;
+    OSStatus err = noErr;
+    AudioObjectPropertyAddress address;
+    UInt32 size;
+
+    // Gets the correspoding device
+    if((device = getDevice(deviceUID)) == kAudioObjectUnknown)
+    {
+        fprintf(stderr,
+                "getDeviceName (coreaudio/device.c): \
+                    \n\tgetDevice\n");
+        return NULL;
+    }
+
+    // Gets the device name
+    CFStringRef deviceName;
+    size = sizeof(deviceName);
+    address.mSelector = kAudioObjectPropertyName;
+    address.mScope = kAudioObjectPropertyScopeGlobal;
+    address.mElement = kAudioObjectPropertyElementMaster;
+
+    if((err = AudioObjectGetPropertyData(
+            device,
+            &address,
+            0,
+            NULL,
+            &size,
+            &deviceName)) != noErr)
+    {
+        fprintf(stderr,
+                "getDeviceName (coreaudio/device.c): \
+                    \n\tAudioObjectGetPropertyData, err: %d\n",
+                ((int) err));
+        return NULL;
+    }
+
+    // Converts the device name to ASCII.
+    CFIndex deviceNameLength = CFStringGetLength(deviceName) + 1;
+    char * deviceASCIIName;
+    // The caller of this function must free the string.
+    if((deviceASCIIName = (char *) malloc(deviceNameLength * sizeof(char)))
+            == NULL)
+    {
+        perror("getDeviceName (coreaudio/device.c): \
+                    \n\tmalloc\n");
+        return NULL;
+    }
+    if(CFStringGetCString(
+                deviceName,
+                deviceASCIIName,
+                deviceNameLength,
+                kCFStringEncodingASCII))
+    {
+        return deviceASCIIName;
+    }
+    return NULL;
+}
+
+/**
+ * Sets the input volume for a given device.
+ *
+ * @param device The device which volume must be changed.
+ * @param volume The new volume of the device. This is a scalar value between
+ * 0.0 and 1.0
+ *
+ * @return noErr if everything works well. Another value if an error has
+ * occured.  
+ */
+OSStatus setInputDeviceVolume(
+        const char * deviceUID,
+        Float32 volume)
+{
+    return setDeviceVolume(
+            deviceUID,
+            volume,
+            kAudioDevicePropertyScopeInput);
+}
+
+/**
+ * Sets the output volume for a given device.
+ *
+ * @param device The device which volume must be changed.
+ * @param volume The new volume of the device. This is a scalar value between
+ * 0.0 and 1.0
+ *
+ * @return noErr if everything works well. Another value if an error has
+ * occured.  
+ */
+OSStatus setOutputDeviceVolume(
+        const char * deviceUID,
+        Float32 volume)
+{
+    return setDeviceVolume(
+            deviceUID,
+            volume,
+            kAudioDevicePropertyScopeOutput);
+}
+
+/**
+ * Sets the input or output volume for a given device. This is an internal
+ * (private) function and must only be called by setInputDeviceVolume or
+ * setOutputDeviceVolume.
+ *
+ * @param device The device which volume must be changed.
+ * @param volume The new volume of the device. This is a scalar value between
+ * 0.0 and 1.0
+ * @param inputOutputScope The scope to tell if this is an output or an input
+ * device.
+ *
+ * @return noErr if everything works well. Another value if an error has
+ * occured.  
+ */
+OSStatus setDeviceVolume(
+        const char * deviceUID,
+        Float32 volume,
+        UInt32 inputOutputScope)
+{
+    AudioDeviceID device;
+    OSStatus err = noErr;
+    AudioObjectPropertyAddress address;
+    UInt32 size;
+    UInt32 channels[2];
+
+    // Gets the correspoding device
+    if((device = getDevice(deviceUID)) == kAudioObjectUnknown)
+    {
+        fprintf(stderr,
+                "setDeviceVolume (coreaudio/device.c): \
+                    \n\tgetDevice (unknown device for UID: %s)\n", deviceUID);
+        return -1;
+    }
+
+    // get the input device stereo channels
+    if((getChannelsForStereo(deviceUID, channels)) != noErr)
+    {
+        fprintf(stderr,
+                "setDeviceVolume (coreaudio/device.c): \
+                    \n\tgetChannelsForStereo, err: %d\n",
+                ((int) err));
+        return err;
+    }
+
+    // Sets the volume
+    size = sizeof(volume);
+    address.mSelector = kAudioDevicePropertyVolumeScalar;
+    address.mScope = inputOutputScope;
+    int i;
+    int elementsLength = 3;
+    UInt32 elements[] =
+    {
+        // The global volume.
+        kAudioObjectPropertyElementMaster,
+        // The left channel.
+        channels[0],
+        // The right channel.
+        channels[1]
+    };
+
+    // Applies the volume to the different elements of the device.
+    for(i = 0; i < elementsLength; ++i)
+    {
+        address.mElement = elements[i];
+        // Checks if this device volume can be set. If yes, then do so.
+        if(AudioObjectHasProperty(device, &address))
+        {
+            if((err = AudioObjectSetPropertyData(
+                    device,
+                    &address,
+                    0,
+                    NULL,
+                    size,
+                    &volume)) != noErr)
+            {
+                fprintf(stderr,
+                        "setDeviceVolume (coreaudio/device.c): \
+                            \n\tAudioObjectSetPropertyData, err: %d\n",
+                        ((int) err));
+                return err;
+            }
+        }
+    }
+
+    return err;
+}
+
+/**
+ * Gets the input volume for a given device.
+ *
+ * @param device The device to get volume from.
+ *
+ * @return The device volume as a scalar value between 0.0 and 1.0. Returns -1.0
+ * if an error occurs.
+ */
+Float32 getInputDeviceVolume(
+        const char * deviceUID)
+{
+    return getDeviceVolume(
+            deviceUID,
+            kAudioDevicePropertyScopeInput);
+}
+
+/**
+ * Gets the output volume for a given device.
+ *
+ * @param device The device to get volume from.
+ *
+ * @return The device volume as a scalar value between 0.0 and 1.0. Returns -1.0
+ * if an error occurs.
+ */
+Float32 getOutputDeviceVolume(
+        const char * deviceUID)
+{
+    return getDeviceVolume(
+            deviceUID,
+            kAudioDevicePropertyScopeOutput);
+}
+
+/**
+ * Gets the input or output volume for a given device. This is an internal
+ * (private) function and must only be called by getInputDeviceVolume or
+ * getOutputDeviceVolume.
+ *
+ * @param device The device to get volume from.
+ * @param inputOutputScope The scope to tell if this is an output or an input
+ * device.
+ *
+ * @return The device volume as a scalar value between 0.0 and 1.0. Returns -1.0
+ * if an error occurs.
+ */
+Float32 getDeviceVolume(
+        const char * deviceUID,
+        UInt32 inputOutputScope)
+{
+    AudioDeviceID device;
+    Float32 volume = -1.0;
+    OSStatus err = noErr;
+    AudioObjectPropertyAddress address;
+    UInt32 size;
+    UInt32 channels[2];
+
+    // Gets the correspoding device
+    if((device = getDevice(deviceUID)) == kAudioObjectUnknown)
+    {
+        fprintf(stderr,
+                "getDeviceVolume (coreaudio/device.c): \
+                    \n\tgetDevice\n");
+        return -1.0;
+    }
+
+    // get the input device stereo channels
+    if((getChannelsForStereo(deviceUID, channels)) != noErr)
+    {
+        fprintf(stderr,
+                "getDeviceVolume (coreaudio/device.c): \
+                    \n\tgetChannelsForStereo, err: %d\n",
+                ((int) err));
+        return -1.0;
+    }
+
+    // Sets the volume
+    size = sizeof(volume);
+    address.mSelector = kAudioDevicePropertyVolumeScalar;
+    address.mScope = inputOutputScope;
+    int i;
+    int elementsLength = 3;
+    UInt32 elements[] =
+    {
+        // The global volume.
+        kAudioObjectPropertyElementMaster,
+        // The left channel.
+        channels[0],
+        // The right channel.
+        channels[1]
+    };
+
+    // Applies the volume to the different elements of the device.
+    for(i = 0; i < elementsLength; ++i)
+    {
+        address.mElement = elements[i];
+        // Checks if this device volume can be set. If yes, then do so.
+        if(AudioObjectHasProperty(device, &address))
+        {
+            if((err = AudioObjectGetPropertyData(
+                    device,
+                    &address,
+                    0,
+                    NULL,
+                    &size,
+                    &volume)) != noErr)
+            {
+                fprintf(stderr,
+                        "getDeviceVolume (coreaudio/device.c): \
+                            \n\tAudioObjectSetPropertyData, err: %d\n",
+                        ((int) err));
+                return -1.0;
+            }
+        }
+    }
+
+    return volume;
+}
+
+/**
+ * Sets the channels for stereo of a given device.
+ *
+ * @param device The device to get the channels from.
+ * @param channels The channels to be filled in with the correct values. This
+ * must be a 2 item length array.
+ *
+ * @return An OSStatus set to noErr if everything works well. Any other vlaue
+ * otherwise.
+ */
+OSStatus getChannelsForStereo(
+        const char * deviceUID,
+        UInt32 * channels)
+{
+    AudioDeviceID device;
+    OSStatus err = noErr;
+    AudioObjectPropertyAddress address;
+    UInt32 size;
+
+    // Gets the correspoding device
+    if((device = getDevice(deviceUID)) == kAudioObjectUnknown)
+    {
+        fprintf(stderr,
+                "getChannelsForStereo (coreaudio/device.c): \
+                    \n\tgetDevice\n");
+        return -1;
+    }
+
+    // get the input device stereo channels
+    address.mSelector = kAudioDevicePropertyPreferredChannelsForStereo;
+    address.mScope = kAudioDevicePropertyScopeInput;
+    address.mElement = kAudioObjectPropertyElementWildcard;
+    size = sizeof(channels);
+    if((err = AudioObjectGetPropertyData(
+            device,
+            &address,
+            0,
+            NULL,
+            &size,
+            channels)) != noErr)
+    {
+        fprintf(stderr,
+                "getChannelsForStereo (coreaudio/device.c): \
+                    \n\tAudioObjectGetPropertyData, err: %d\n",
+                ((int) err));
+        return err;
+    }
+
+    return err;
+}
diff --git a/src/native/macosx/coreaudio/lib/device.h b/src/native/macosx/coreaudio/lib/device.h
new file mode 100644
index 00000000..ed5b12cb
--- /dev/null
+++ b/src/native/macosx/coreaudio/lib/device.h
@@ -0,0 +1,39 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+#ifndef device_h
+#define device_h
+
+#include <CoreAudio/CoreAudio.h>
+#include <CoreFoundation/CFString.h>
+#include <stdio.h>
+
+/**
+ * Functions to list, access and modifies audio devices via coreaudio.
+ * Look at correspondig ".c" file for documentation.
+ *
+ * @author Vincent Lucas
+ */
+AudioDeviceID getDevice(
+        const char * deviceUID);
+
+char* getDeviceName(
+        const char * deviceUID);
+
+OSStatus setInputDeviceVolume(
+        const char * deviceUID,
+        Float32 volume);
+
+OSStatus setOutputDeviceVolume(
+        const char * deviceUID,
+        Float32 volume);
+
+Float32 getInputDeviceVolume(
+        const char * deviceUID);
+
+Float32 getOutputDeviceVolume(
+        const char * deviceUID);
+#endif
diff --git a/src/org/jitsi/impl/neomedia/AbstractVolumeControl.java b/src/org/jitsi/impl/neomedia/AbstractVolumeControl.java
index 2c8a5234..bc1ff397 100644
--- a/src/org/jitsi/impl/neomedia/AbstractVolumeControl.java
+++ b/src/org/jitsi/impl/neomedia/AbstractVolumeControl.java
@@ -43,7 +43,7 @@ public class AbstractVolumeControl
     /**
      * The minimum volume level accepted by <tt>AbstractVolumeControl</tt>.
      */
-    private static final float MIN_VOLUME_LEVEL = 0.0F;
+    protected static final float MIN_VOLUME_LEVEL = 0.0F;
 
     /**
      * The minimum volume level expressed in percent accepted by
@@ -54,7 +54,7 @@ public class AbstractVolumeControl
     /**
      * The maximum volume level accepted by <tt>AbstractVolumeControl</tt>.
      */
-    private static final float MAX_VOLUME_LEVEL = 1.0F;
+    protected static final float MAX_VOLUME_LEVEL = 1.0F;
 
     /**
      * The maximum volume level expressed in percent accepted by
@@ -62,14 +62,6 @@ public class AbstractVolumeControl
      */
     public static final int MAX_VOLUME_PERCENT = 200;
 
-    /**
-     * The default volume level.
-     */
-    private static final float DEFAULT_VOLUME_LEVEL
-        = MIN_VOLUME_LEVEL
-            + (MAX_VOLUME_LEVEL - MIN_VOLUME_LEVEL)
-                / ((MAX_VOLUME_PERCENT - MIN_VOLUME_PERCENT) / 100);
-
     /**
      * The <tt>VolumeChangeListener</tt>s interested in volume change events
      * through the <tt>VolumeControl</tt> interface.
@@ -92,7 +84,13 @@ public class AbstractVolumeControl
     /**
      * The current volume level.
      */
-    private float volumeLevel = DEFAULT_VOLUME_LEVEL;
+    protected float volumeLevel;
+
+    /**
+     * The power level reference used to compute equivelents between the volume
+     * power level and the gain in decibels.
+     */
+    private float gainReferenceLevel;
 
     /**
      * Current mute state, by default we start unmuted.
@@ -104,11 +102,6 @@ public class AbstractVolumeControl
      */
     private float db;
 
-    /**
-     * The initial volume level, when this instance was created.
-     */
-    private final float initialVolumeLevel;
-
     /**
      * The name of the configuration property which specifies the value of the
      * volume level of this <tt>AbstractVolumeControl</tt>.
@@ -126,29 +119,29 @@ public class AbstractVolumeControl
     public AbstractVolumeControl(
         String volumeLevelConfigurationPropertyName)
     {
+        // Initializes default values.
+        this.volumeLevel = getDefaultVolumeLevel();
+        this.gainReferenceLevel = getGainReferenceLevel();
+
         this.volumeLevelConfigurationPropertyName
             = volumeLevelConfigurationPropertyName;
 
         // Read the initial volume level from the ConfigurationService.
-        float initialVolumeLevel = DEFAULT_VOLUME_LEVEL;
-
         try
         {
             ConfigurationService cfg = LibJitsi.getConfigurationService();
 
             if (cfg != null)
             {
-                String initialVolumeLevelString
+                String volumeLevelString
                     = cfg.getString(this.volumeLevelConfigurationPropertyName);
 
-                if (initialVolumeLevelString != null)
+                if (volumeLevelString != null)
                 {
-                    initialVolumeLevel
-                        = Float.parseFloat(initialVolumeLevelString);
+                    this.volumeLevel = Float.parseFloat(volumeLevelString);
                     if(logger.isDebugEnabled())
                     {
-                        logger.debug(
-                                "Restored volume: " + initialVolumeLevelString);
+                        logger.debug("Restored volume: " + volumeLevelString);
                     }
                 }
             }
@@ -157,9 +150,6 @@ public AbstractVolumeControl(
         {
             logger.warn("Error restoring volume", t);
         }
-
-        this.initialVolumeLevel = initialVolumeLevel;
-        this.volumeLevel = this.initialVolumeLevel;
     }
 
     /**
@@ -310,6 +300,7 @@ else if (value > MAX_VOLUME_LEVEL)
             return value;
 
         volumeLevel = value;
+        updateHardwareVolume();
         fireVolumeChange();
 
         /*
@@ -325,13 +316,7 @@ else if (value > MAX_VOLUME_LEVEL)
                     String.valueOf(volumeLevel));
         }
 
-        float f1 = value / initialVolumeLevel;
-
-        db
-            = (float)
-                ((Math.log(((double)f1 != 0.0D) ? f1 : 0.0001D)
-                        / Math.log(10D))
-                    * 20D);
+        db = getDbFromPowerRatio(value, this.gainReferenceLevel);
         fireGainEvents();
 
         return volumeLevel;
@@ -380,9 +365,7 @@ public float setDB(float gain)
         if(this.db != gain)
         {
             this.db = gain;
-
-            float f1 = (float)Math.pow(10D, (double)this.db / 20D);
-            float volumeLevel = f1 * this.initialVolumeLevel;
+            float volumeLevel = getPowerRatioFromDb(gain, gainReferenceLevel);
 
             setVolumeLevel(volumeLevel);
         }
@@ -533,4 +516,77 @@ public Component getControlComponent()
     {
         return null;
     }
+
+    /**
+     * Returns the decibel value for a ratio between a power level required and
+     * the reference power level.
+     *
+     * @param powerLevelRequired The power level wished for the signal
+     * (corresponds to the mesured power level).
+     * @param referencePowerLevel The reference power level.
+     *
+     * @return The gain in Db.
+     */
+    private static float getDbFromPowerRatio(
+            float powerLevelRequired,
+            float referencePowerLevel)
+    {
+        float powerRatio = powerLevelRequired / referencePowerLevel;
+
+        // Limits the lowest power ratio to be 0.0001.
+        float minPowerRatio = 0.0001F;
+        float flooredPowerRatio = Math.max(powerRatio, minPowerRatio);
+
+        return (float) (20.0 * Math.log10(flooredPowerRatio));
+    }
+
+    /**
+     * Returns the mesured power level corresponding to a gain in decibel and
+     * compared to the reference power level.
+     *
+     * @param gainInDb The gain in Db.
+     * @param referencePowerLevel The reference power level.
+     *
+     * @return The power level the signal, which corresponds to the mesured
+     * power level.
+     */
+    private static float getPowerRatioFromDb(
+            float gainInDb,
+            float referencePowerLevel)
+    {
+        return (float) Math.pow(10, (gainInDb / 20)) * referencePowerLevel;
+    }
+
+    /**
+     * Returns the default volume level.
+     *
+     * @return The default volume level.
+     */
+    protected static float getDefaultVolumeLevel()
+    {
+        return MIN_VOLUME_LEVEL
+            + (MAX_VOLUME_LEVEL - MIN_VOLUME_LEVEL)
+                / ((MAX_VOLUME_PERCENT - MIN_VOLUME_PERCENT) / 100);
+    }
+
+    /**
+     * Returns the reference volume level for computing the gain.
+     *
+     * @return The reference volume level for computing the gain.
+     */
+    protected static float getGainReferenceLevel()
+    {
+        return getDefaultVolumeLevel();
+    }
+
+    /**
+     * Modifies the hardware microphone sensibility (hardaware amplification).
+     * This is a void function for AbstractVolumeControl sincei it does not have
+     * any connection to hardware volume. But, this function must be redefined
+     * by any extending class.
+     */
+    protected void updateHardwareVolume()
+    {
+        // Nothing to do. This AbstractVolumeControl only modifies the gain.
+    }
 }
diff --git a/src/org/jitsi/impl/neomedia/MediaServiceImpl.java b/src/org/jitsi/impl/neomedia/MediaServiceImpl.java
index 0a859220..2055b523 100644
--- a/src/org/jitsi/impl/neomedia/MediaServiceImpl.java
+++ b/src/org/jitsi/impl/neomedia/MediaServiceImpl.java
@@ -21,6 +21,7 @@
 
 import org.jitsi.impl.neomedia.codec.*;
 import org.jitsi.impl.neomedia.codec.video.*;
+import org.jitsi.impl.neomedia.coreaudio.*;
 import org.jitsi.impl.neomedia.device.*;
 import org.jitsi.impl.neomedia.format.*;
 import org.jitsi.impl.neomedia.transform.sdes.*;
@@ -748,9 +749,19 @@ public VolumeControl getInputVolumeControl()
     {
         if (inputVolumeControl == null)
         {
-            inputVolumeControl
-                = new AbstractVolumeControl(
-                        VolumeControl.CAPTURE_VOLUME_LEVEL_PROPERTY_NAME);
+            if(OSUtils.IS_MAC)
+            {
+                inputVolumeControl
+                    = new CoreAudioVolumeControl(
+                            this,
+                            VolumeControl.CAPTURE_VOLUME_LEVEL_PROPERTY_NAME);
+            }
+            else
+            {
+                inputVolumeControl
+                    = new AbstractVolumeControl(
+                            VolumeControl.CAPTURE_VOLUME_LEVEL_PROPERTY_NAME);
+            }
         }
         return inputVolumeControl;
     }
diff --git a/src/org/jitsi/impl/neomedia/coreaudio/CoreAudioDevice.java b/src/org/jitsi/impl/neomedia/coreaudio/CoreAudioDevice.java
new file mode 100644
index 00000000..0ed93340
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/coreaudio/CoreAudioDevice.java
@@ -0,0 +1,51 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.impl.neomedia.coreaudio;
+
+import org.jitsi.util.*;
+
+/**
+ * JNI link to the CoreAudio library.
+ *
+ * @author Vincent Lucqs
+ */
+public class CoreAudioDevice
+{
+    static
+    {
+        System.loadLibrary("jncoreaudio");
+    }
+
+//    public static native AudioDeviceID getDevice(
+//            String deviceUID);
+
+    public static String getDeviceName(
+            String deviceUID)
+    {
+        byte[] deviceNameBytes = getDeviceNameBytes(deviceUID);
+        String deviceName = StringUtils.newString(deviceNameBytes);
+
+        return deviceName;
+    }
+
+    public static native byte[] getDeviceNameBytes(
+            String deviceUID);
+
+    public static native int setInputDeviceVolume(
+            String deviceUID,
+            float volume);
+
+    public static native int setOutputDeviceVolume(
+            String deviceUID,
+            float volume);
+
+    public static native float getInputDeviceVolume(
+            String deviceUID);
+
+    public static native float getOutputDeviceVolume(
+            String deviceUID);
+}
diff --git a/src/org/jitsi/impl/neomedia/coreaudio/CoreAudioVolumeControl.java b/src/org/jitsi/impl/neomedia/coreaudio/CoreAudioVolumeControl.java
new file mode 100644
index 00000000..60eb5da6
--- /dev/null
+++ b/src/org/jitsi/impl/neomedia/coreaudio/CoreAudioVolumeControl.java
@@ -0,0 +1,113 @@
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+package org.jitsi.impl.neomedia.coreaudio;
+
+import org.jitsi.impl.neomedia.*;
+import org.jitsi.impl.neomedia.device.*;
+import org.jitsi.service.neomedia.*;
+import org.jitsi.util.*;
+
+/**
+ * Implementation of VolumeControl which uses MacOSX sound architecture
+ * CoreAudio to change input/output hardware volume.
+ *
+ * @author Vincent Lucas
+ */
+public class CoreAudioVolumeControl
+    extends AbstractVolumeControl
+{
+    /**
+     * The <tt>Logger</tt> used by the <tt>CoreAudioVolumeControl</tt> class and
+     * its instances for logging output.
+     */
+    private static final Logger logger
+        = Logger.getLogger(CoreAudioVolumeControl.class);
+
+    /**
+     * The media service implementation.
+     */
+    MediaServiceImpl mediaServiceImpl = null;
+
+    /**
+     * The maximal power level used for hardware amplification. Over this value
+     * software amplification is used.
+     */
+    private static final float MAX_HARDWARE_POWER = 1.0F;
+
+    /**
+     * Creates volume control instance and initializes initial level value
+     * if stored in the configuration service.
+     *
+     * @param mediaServiceImpl The media service implementation.
+     * @param volumeLevelConfigurationPropertyName the name of the configuration
+     * property which specifies the value of the volume level of the new
+     * instance
+     */
+    public CoreAudioVolumeControl(
+        MediaServiceImpl mediaServiceImpl,
+        String volumeLevelConfigurationPropertyName)
+    {
+        super(volumeLevelConfigurationPropertyName);
+        this.mediaServiceImpl = mediaServiceImpl;
+        updateHardwareVolume();
+    }
+
+    /**
+     * Returns the default volume level.
+     *
+     * @return The default volume level.
+     */
+    protected static float getDefaultVolumeLevel()
+    {
+        // By default set the microphone at the middle of its hardware
+        // sensibility range.
+        return MAX_HARDWARE_POWER / 2;
+    }
+
+    /**
+     * Returns the reference volume level for computing the gain.
+     *
+     * @return The reference volume level for computing the gain.
+     */
+    protected static float getGainReferenceLevel()
+    {
+        // Starts to activate the gain (software amplification), only once the
+        // microphone sensibility is sets to its maximum (hardware
+        // amplification).
+        return MAX_HARDWARE_POWER;
+    }
+
+    /**
+     * Modifies the hardware microphone sensibility (hardware amplification).
+     */
+    protected void updateHardwareVolume()
+    {
+        // Gets the selected input dvice UID.
+        AudioSystem audioSystem
+            = mediaServiceImpl.getDeviceConfiguration().getAudioSystem();
+        ExtendedCaptureDeviceInfo captureDevice = (audioSystem == null)
+            ? null
+            : audioSystem.getDevice(AudioSystem.CAPTURE_INDEX);
+        String deviceUID = captureDevice.getUID();
+
+        // Computes the hardware volume.
+        float jitsiHarwareVolumeFactor = MAX_VOLUME_LEVEL / MAX_HARDWARE_POWER;
+        float hardwareVolumeLevel = this.volumeLevel * jitsiHarwareVolumeFactor;
+        if(hardwareVolumeLevel > 1.0F)
+        {
+            hardwareVolumeLevel = 1.0F;
+        }
+
+        // Changes the input volume of the capture device.
+        if(CoreAudioDevice.setInputDeviceVolume(
+                    deviceUID,
+                    hardwareVolumeLevel) != 0)
+        {
+            logger.debug("Could not change CoreAudio input device level");
+        }
+    }
+}
diff --git a/src/org/jitsi/impl/neomedia/portaudio/Pa.java b/src/org/jitsi/impl/neomedia/portaudio/Pa.java
index 703c89f3..46223bd8 100644
--- a/src/org/jitsi/impl/neomedia/portaudio/Pa.java
+++ b/src/org/jitsi/impl/neomedia/portaudio/Pa.java
@@ -8,7 +8,6 @@
 
 import java.io.*;
 import java.lang.reflect.*;
-import java.nio.charset.*;
 
 import org.jitsi.service.configuration.*;
 import org.jitsi.service.libjitsi.*;
@@ -247,7 +246,7 @@ public static native double DeviceInfo_getDefaultSampleRate(
      */
     public static String DeviceInfo_getDeviceUID(long deviceInfo)
     {
-        return newString(DeviceInfo_getDeviceUIDBytes(deviceInfo));
+        return StringUtils.newString(DeviceInfo_getDeviceUIDBytes(deviceInfo));
     }
 
     /**
@@ -293,7 +292,7 @@ public static String DeviceInfo_getDeviceUID(long deviceInfo)
      */
     public static String DeviceInfo_getName(long deviceInfo)
     {
-        return newString(DeviceInfo_getNameBytes(deviceInfo));
+        return StringUtils.newString(DeviceInfo_getNameBytes(deviceInfo));
     }
 
     /**
@@ -316,7 +315,8 @@ public static String DeviceInfo_getName(long deviceInfo)
      */
     public static String DeviceInfo_getTransportType(long deviceInfo)
     {
-        return newString(DeviceInfo_getTransportTypeBytes(deviceInfo));
+        return StringUtils.newString(
+                DeviceInfo_getTransportTypeBytes(deviceInfo));
     }
 
     /**
@@ -565,36 +565,6 @@ public static native boolean IsFormatSupported(
         long outputParameters,
         double sampleRate);
 
-    /**
-     * Initializes a new <tt>String</tt> instance by decoding a specified array
-     * of bytes.
-     *
-     * @param bytes the bytes to be decoded into characters/a new
-     * <tt>String</tt> instance
-     * @return a new <tt>String</tt> instance whose characters were decoded from
-     * the specified <tt>bytes</tt>
-     */
-    private static String newString(byte[] bytes)
-    {
-        if ((bytes == null) || (bytes.length == 0))
-            return null;
-        else
-        {
-            Charset defaultCharset = Charset.defaultCharset();
-            String charsetName
-                = (defaultCharset == null) ? "UTF-8" : defaultCharset.name();
-
-            try
-            {
-                return new String(bytes, charsetName);
-            }
-            catch (UnsupportedEncodingException ueex)
-            {
-                return new String(bytes);
-            }
-        }
-    }
-
     /**
      * Opens a stream for either input, output or both.
      *
diff --git a/src/org/jitsi/util/StringUtils.java b/src/org/jitsi/util/StringUtils.java
index c5dd5a55..e5e35898 100644
--- a/src/org/jitsi/util/StringUtils.java
+++ b/src/org/jitsi/util/StringUtils.java
@@ -7,6 +7,7 @@
 package org.jitsi.util;
 
 import java.io.*;
+import java.nio.charset.*;
 
 /**
  * Implements utility functions to facilitate work with <tt>String</tt>s.
@@ -233,4 +234,34 @@ public static String concatenateWords(String string)
         }
         return buff.toString();
     }
+
+    /**
+     * Initializes a new <tt>String</tt> instance by decoding a specified array
+     * of bytes (mostly used by JNI).
+     *
+     * @param bytes the bytes to be decoded into characters/a new
+     * <tt>String</tt> instance
+     * @return a new <tt>String</tt> instance whose characters were decoded from
+     * the specified <tt>bytes</tt>
+     */
+    public static String newString(byte[] bytes)
+    {
+        if ((bytes == null) || (bytes.length == 0))
+            return null;
+        else
+        {
+            Charset defaultCharset = Charset.defaultCharset();
+            String charsetName
+                = (defaultCharset == null) ? "UTF-8" : defaultCharset.name();
+
+            try
+            {
+                return new String(bytes, charsetName);
+            }
+            catch (UnsupportedEncodingException ueex)
+            {
+                return new String(bytes);
+            }
+        }
+    }
 }
-- 
GitLab