Merge "Add audio level monitoring capabilities in Visualizer effect" into klp-dev
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 9197ed8..580a4f9 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -93,6 +93,24 @@
      */
     public static final int SCALING_MODE_AS_PLAYED = 1;
 
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * Defines a measurement mode with no requested measurement.
+     */
+    public static final int MEASUREMENT_MODE_NONE = 0;
+
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * Defines a measurement mode which computes the peak and RMS value in mB, where 0mB is the
+     * maximum sample value, and -9600mB is the minimum value.
+     * Values for peak and RMS can be retrieved with {@link #getIntMeasurements(int, int[])}, where
+     * the array holds the peak value at index {@link #MEASUREMENT_INDEX_PEAK} in the measurement
+     * array, and the RMS value at index {@link #MEASUREMENT_INDEX_RMS}.
+     */
+    public static final int MEASUREMENT_MODE_PEAK_RMS = 1 << 0;
+
     // to keep in sync with frameworks/base/media/jni/audioeffect/android_media_Visualizer.cpp
     private static final int NATIVE_EVENT_PCM_CAPTURE = 0;
     private static final int NATIVE_EVENT_FFT_CAPTURE = 1;
@@ -350,6 +368,47 @@
     }
 
     /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * Sets the combination of measurement modes to be performed by this audio effect.
+     * @param mode a mask of the measurements to perform. The valid values are
+     *     {@link #MEASUREMENT_MODE_NONE} (to cancel any measurement)
+     *     or {@link #MEASUREMENT_MODE_PEAK_RMS}.
+     * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE} in case of failure.
+     * @throws IllegalStateException
+     */
+    public int setMeasurementMode(int mode)
+            throws IllegalStateException {
+        synchronized (mStateLock) {
+            if (mState == STATE_UNINITIALIZED) {
+                throw(new IllegalStateException("setMeasurementMode() called in wrong state: "
+                        + mState));
+            }
+            return native_setMeasurementMode(mode);
+        }
+    }
+
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * Returns the current measurement modes performed by this audio effect
+     * @return the mask of the measurements,
+     *     {@link #MEASUREMENT_MODE_NONE} (when no measurements are performed)
+     *     or {@link #MEASUREMENT_MODE_PEAK_RMS}.
+     * @throws IllegalStateException
+     */
+    public int getMeasurementMode()
+            throws IllegalStateException {
+        synchronized (mStateLock) {
+            if (mState == STATE_UNINITIALIZED) {
+                throw(new IllegalStateException("getMeasurementMode() called in wrong state: "
+                        + mState));
+            }
+            return native_getMeasurementMode();
+        }
+    }
+
+    /**
      * Returns the sampling rate of the captured audio.
      * @return the sampling rate in milliHertz.
      */
@@ -437,6 +496,51 @@
         }
     }
 
+    /**
+     * @hide
+     * CANDIDATE FOR PUBLIC API
+     * A class to store peak and RMS values.
+     * Peak and RMS are expressed in mB, as described in the
+     * {@link Visualizer#MEASUREMENT_MODE_PEAK_RMS} measurement mode.
+     */
+    public static final class MeasurementPeakRms {
+        /**
+         * @hide
+         * CANDIDATE FOR PUBLIC API
+         */
+        public int mPeak;
+        /**
+         * @hide
+         * CANDIDATE FOR PUBLIC API
+         */
+        public int mRms;
+    }
+
+    /**
+     * @hide
+     * Retrieves the latest peak and RMS measurement.
+     * Sets the peak and RMS fields of the {@link Visualizer.MeasurementPeakRms} to the latest
+     * measured values.
+     * @param measurement a non-null {@link Visualizer.MeasurementPeakRms} instance to store
+     *    the measurement values.
+     * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE},
+     *    {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
+     *    in case of failure.
+     */
+    public int getMeasurementPeakRms(MeasurementPeakRms measurement) {
+        if (measurement == null) {
+            Log.e(TAG, "Cannot store measurements in a null object");
+            return ERROR_BAD_VALUE;
+        }
+        synchronized (mStateLock) {
+            if (mState != STATE_ENABLED) {
+                throw (new IllegalStateException("getMeasurementPeakRms() called in wrong state: "
+                        + mState));
+            }
+            return native_getPeakRms(measurement);
+        }
+    }
+
     //---------------------------------------------------------
     // Interface definitions
     //--------------------
@@ -640,12 +744,18 @@
 
     private native final int native_getScalingMode();
 
+    private native final int native_setMeasurementMode(int mode);
+
+    private native final int native_getMeasurementMode();
+
     private native final int native_getSamplingRate();
 
     private native final int native_getWaveForm(byte[] waveform);
 
     private native final int native_getFft(byte[] fft);
 
+    private native final int native_getPeakRms(MeasurementPeakRms measurement);
+
     private native final int native_setPeriodicCapture(int rate, boolean waveForm, boolean fft);
 
     //---------------------------------------------------------
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index 4d77cfd..40cd06b 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -43,6 +43,8 @@
 
 // ----------------------------------------------------------------------------
 static const char* const kClassPathName = "android/media/audiofx/Visualizer";
+static const char* const kClassPeakRmsPathName =
+        "android/media/audiofx/Visualizer$MeasurementPeakRms";
 
 struct fields_t {
     // these fields provide access from C++ to the...
@@ -50,6 +52,8 @@
     jmethodID midPostNativeEvent;   // event post callback method
     jfieldID  fidNativeVisualizer; // stores in Java the native Visualizer object
     jfieldID  fidJniData;           // stores in Java additional resources used by the native Visualizer
+    jfieldID  fidPeak; // to access Visualizer.MeasurementPeakRms.mPeak
+    jfieldID  fidRms;  // to access Visualizer.MeasurementPeakRms.mRms
 };
 static fields_t fields;
 
@@ -257,6 +261,14 @@
 
     fields.clazzEffect = (jclass)env->NewGlobalRef(clazz);
 
+    // Get the Visualizer.MeasurementPeakRms class
+    clazz = env->FindClass(kClassPeakRmsPathName);
+    if (clazz == NULL) {
+        ALOGE("Can't find %s", kClassPeakRmsPathName);
+        return;
+    }
+    jclass clazzMeasurementPeakRms = (jclass)env->NewGlobalRef(clazz);
+
     // Get the postEvent method
     fields.midPostNativeEvent = env->GetStaticMethodID(
             fields.clazzEffect,
@@ -283,7 +295,24 @@
         ALOGE("Can't find Visualizer.%s", "mJniData");
         return;
     }
+    //      fidPeak
+    fields.fidPeak = env->GetFieldID(
+            clazzMeasurementPeakRms,
+            "mPeak", "I");
+    if (fields.fidPeak == NULL) {
+        ALOGE("Can't find Visualizer.MeasurementPeakRms.%s", "mPeak");
+        return;
+    }
+    //      fidRms
+    fields.fidRms = env->GetFieldID(
+            clazzMeasurementPeakRms,
+            "mRms", "I");
+    if (fields.fidRms == NULL) {
+        ALOGE("Can't find Visualizer.MeasurementPeakRms.%s", "mPeak");
+        return;
+    }
 
+    env->DeleteGlobalRef(clazzMeasurementPeakRms);
 }
 
 static void android_media_visualizer_effect_callback(int32_t event,
@@ -513,6 +542,26 @@
 }
 
 static jint
+android_media_visualizer_native_setMeasurementMode(JNIEnv *env, jobject thiz, jint mode)
+{
+    Visualizer* lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == NULL) {
+        return VISUALIZER_ERROR_NO_INIT;
+    }
+    return translateError(lpVisualizer->setMeasurementMode(mode));
+}
+
+static jint
+android_media_visualizer_native_getMeasurementMode(JNIEnv *env, jobject thiz)
+{
+    Visualizer* lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == NULL) {
+        return MEASUREMENT_MODE_NONE;
+    }
+    return lpVisualizer->getMeasurementMode();
+}
+
+static jint
 android_media_visualizer_native_getSamplingRate(JNIEnv *env, jobject thiz)
 {
     Visualizer* lpVisualizer = getVisualizer(env, thiz);
@@ -560,6 +609,25 @@
 }
 
 static jint
+android_media_visualizer_native_getPeakRms(JNIEnv *env, jobject thiz, jobject jPeakRmsObj)
+{
+    Visualizer* lpVisualizer = getVisualizer(env, thiz);
+    if (lpVisualizer == NULL) {
+        return VISUALIZER_ERROR_NO_INIT;
+    }
+    int32_t measurements[2];
+    jint status = translateError(
+                lpVisualizer->getIntMeasurements(MEASUREMENT_MODE_PEAK_RMS,
+                        2, measurements));
+    if (status == VISUALIZER_SUCCESS) {
+        // measurement worked, write the values to the java object
+        env->SetIntField(jPeakRmsObj, fields.fidPeak, measurements[MEASUREMENT_IDX_PEAK]);
+        env->SetIntField(jPeakRmsObj, fields.fidRms, measurements[MEASUREMENT_IDX_RMS]);
+    }
+    return status;
+}
+
+static jint
 android_media_setPeriodicCapture(JNIEnv *env, jobject thiz, jint rate, jboolean jWaveform, jboolean jFft)
 {
     Visualizer* lpVisualizer = getVisualizer(env, thiz);
@@ -606,9 +674,13 @@
     {"native_getCaptureSize",    "()I",   (void *)android_media_visualizer_native_getCaptureSize},
     {"native_setScalingMode",    "(I)I",  (void *)android_media_visualizer_native_setScalingMode},
     {"native_getScalingMode",    "()I",   (void *)android_media_visualizer_native_getScalingMode},
+    {"native_setMeasurementMode","(I)I",  (void *)android_media_visualizer_native_setMeasurementMode},
+    {"native_getMeasurementMode","()I",   (void *)android_media_visualizer_native_getMeasurementMode},
     {"native_getSamplingRate",   "()I",   (void *)android_media_visualizer_native_getSamplingRate},
     {"native_getWaveForm",       "([B)I", (void *)android_media_visualizer_native_getWaveForm},
     {"native_getFft",            "([B)I", (void *)android_media_visualizer_native_getFft},
+    {"native_getPeakRms",      "(Landroid/media/audiofx/Visualizer$MeasurementPeakRms;)I",
+                                          (void *)android_media_visualizer_native_getPeakRms},
     {"native_setPeriodicCapture","(IZZ)I",(void *)android_media_setPeriodicCapture},
 };