Home | History | Annotate | Download | only in audiofx
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.media.audiofx;
     18 
     19 import android.app.ActivityThread;
     20 import android.util.Log;
     21 import java.lang.ref.WeakReference;
     22 import android.os.Handler;
     23 import android.os.Looper;
     24 import android.os.Message;
     25 
     26 /**
     27  * The Visualizer class enables application to retrieve part of the currently playing audio for
     28  * visualization purpose. It is not an audio recording interface and only returns partial and low
     29  * quality audio content. However, to protect privacy of certain audio data (e.g voice mail) the use
     30  * of the visualizer requires the permission android.permission.RECORD_AUDIO.
     31  * <p>The audio session ID passed to the constructor indicates which audio content should be
     32  * visualized:<br>
     33  * <ul>
     34  *   <li>If the session is 0, the audio output mix is visualized</li>
     35  *   <li>If the session is not 0, the audio from a particular {@link android.media.MediaPlayer} or
     36  *   {@link android.media.AudioTrack}
     37  *   using this audio session is visualized </li>
     38  * </ul>
     39  * <p>Two types of representation of audio content can be captured: <br>
     40  * <ul>
     41  *   <li>Waveform data: consecutive 8-bit (unsigned) mono samples by using the
     42  *   {@link #getWaveForm(byte[])} method</li>
     43  *   <li>Frequency data: 8-bit magnitude FFT by using the {@link #getFft(byte[])} method</li>
     44  * </ul>
     45  * <p>The length of the capture can be retrieved or specified by calling respectively
     46  * {@link #getCaptureSize()} and {@link #setCaptureSize(int)} methods. The capture size must be a
     47  * power of 2 in the range returned by {@link #getCaptureSizeRange()}.
     48  * <p>In addition to the polling capture mode described above with {@link #getWaveForm(byte[])} and
     49  *  {@link #getFft(byte[])} methods, a callback mode is also available by installing a listener by
     50  *  use of the {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
     51  *  The rate at which the listener capture method is called as well as the type of data returned is
     52  *  specified.
     53  * <p>Before capturing data, the Visualizer must be enabled by calling the
     54  * {@link #setEnabled(boolean)} method.
     55  * When data capture is not needed any more, the Visualizer should be disabled.
     56  * <p>It is good practice to call the {@link #release()} method when the Visualizer is not used
     57  * anymore to free up native resources associated to the Visualizer instance.
     58  * <p>Creating a Visualizer on the output mix (audio session 0) requires permission
     59  * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}
     60  * <p>The Visualizer class can also be used to perform measurements on the audio being played back.
     61  * The measurements to perform are defined by setting a mask of the requested measurement modes with
     62  * {@link #setMeasurementMode(int)}. Supported values are {@link #MEASUREMENT_MODE_NONE} to cancel
     63  * any measurement, and {@link #MEASUREMENT_MODE_PEAK_RMS} for peak and RMS monitoring.
     64  * Measurements can be retrieved through {@link #getMeasurementPeakRms(MeasurementPeakRms)}.
     65  */
     66 
     67 public class Visualizer {
     68 
     69     static {
     70         System.loadLibrary("audioeffect_jni");
     71         native_init();
     72     }
     73 
     74     private final static String TAG = "Visualizer-JAVA";
     75 
     76     /**
     77      * State of a Visualizer object that was not successfully initialized upon creation
     78      */
     79     public static final int STATE_UNINITIALIZED = 0;
     80     /**
     81      * State of a Visualizer object that is ready to be used.
     82      */
     83     public static final int STATE_INITIALIZED   = 1;
     84     /**
     85      * State of a Visualizer object that is active.
     86      */
     87     public static final int STATE_ENABLED   = 2;
     88 
     89     // to keep in sync with system/media/audio_effects/include/audio_effects/effect_visualizer.h
     90     /**
     91      * Defines a capture mode where amplification is applied based on the content of the captured
     92      * data. This is the default Visualizer mode, and is suitable for music visualization.
     93      */
     94     public static final int SCALING_MODE_NORMALIZED = 0;
     95     /**
     96      * Defines a capture mode where the playback volume will affect (scale) the range of the
     97      * captured data. A low playback volume will lead to low sample and fft values, and vice-versa.
     98      */
     99     public static final int SCALING_MODE_AS_PLAYED = 1;
    100 
    101     /**
    102      * Defines a measurement mode in which no measurements are performed.
    103      */
    104     public static final int MEASUREMENT_MODE_NONE = 0;
    105 
    106     /**
    107      * Defines a measurement mode which computes the peak and RMS value in mB, where 0mB is the
    108      * maximum sample value, and -9600mB is the minimum value.
    109      * Values for peak and RMS can be retrieved with
    110      * {@link #getMeasurementPeakRms(MeasurementPeakRms)}.
    111      */
    112     public static final int MEASUREMENT_MODE_PEAK_RMS = 1 << 0;
    113 
    114     // to keep in sync with frameworks/base/media/jni/audioeffect/android_media_Visualizer.cpp
    115     private static final int NATIVE_EVENT_PCM_CAPTURE = 0;
    116     private static final int NATIVE_EVENT_FFT_CAPTURE = 1;
    117     private static final int NATIVE_EVENT_SERVER_DIED = 2;
    118 
    119     // Error codes:
    120     /**
    121      * Successful operation.
    122      */
    123     public  static final int SUCCESS              = 0;
    124     /**
    125      * Unspecified error.
    126      */
    127     public  static final int ERROR                = -1;
    128     /**
    129      * Internal operation status. Not returned by any method.
    130      */
    131     public  static final int ALREADY_EXISTS       = -2;
    132     /**
    133      * Operation failed due to bad object initialization.
    134      */
    135     public  static final int ERROR_NO_INIT              = -3;
    136     /**
    137      * Operation failed due to bad parameter value.
    138      */
    139     public  static final int ERROR_BAD_VALUE            = -4;
    140     /**
    141      * Operation failed because it was requested in wrong state.
    142      */
    143     public  static final int ERROR_INVALID_OPERATION    = -5;
    144     /**
    145      * Operation failed due to lack of memory.
    146      */
    147     public  static final int ERROR_NO_MEMORY            = -6;
    148     /**
    149      * Operation failed due to dead remote object.
    150      */
    151     public  static final int ERROR_DEAD_OBJECT          = -7;
    152 
    153     //--------------------------------------------------------------------------
    154     // Member variables
    155     //--------------------
    156     /**
    157      * Indicates the state of the Visualizer instance
    158      */
    159     private int mState = STATE_UNINITIALIZED;
    160     /**
    161      * Lock to synchronize access to mState
    162      */
    163     private final Object mStateLock = new Object();
    164     /**
    165      * System wide unique Identifier of the visualizer engine used by this Visualizer instance
    166      */
    167     private int mId;
    168 
    169     /**
    170      * Lock to protect listeners updates against event notifications
    171      */
    172     private final Object mListenerLock = new Object();
    173     /**
    174      * Handler for events coming from the native code
    175      */
    176     private NativeEventHandler mNativeEventHandler = null;
    177     /**
    178      *  PCM and FFT capture listener registered by client
    179      */
    180     private OnDataCaptureListener mCaptureListener = null;
    181     /**
    182      *  Server Died listener registered by client
    183      */
    184     private OnServerDiedListener mServerDiedListener = null;
    185 
    186     // accessed by native methods
    187     private long mNativeVisualizer;
    188     private long mJniData;
    189 
    190     //--------------------------------------------------------------------------
    191     // Constructor, Finalize
    192     //--------------------
    193     /**
    194      * Class constructor.
    195      * @param audioSession system wide unique audio session identifier. If audioSession
    196      *  is not 0, the visualizer will be attached to the MediaPlayer or AudioTrack in the
    197      *  same audio session. Otherwise, the Visualizer will apply to the output mix.
    198      *
    199      * @throws java.lang.UnsupportedOperationException
    200      * @throws java.lang.RuntimeException
    201      */
    202 
    203     public Visualizer(int audioSession)
    204     throws UnsupportedOperationException, RuntimeException {
    205         int[] id = new int[1];
    206 
    207         synchronized (mStateLock) {
    208             mState = STATE_UNINITIALIZED;
    209             // native initialization
    210             int result = native_setup(new WeakReference<Visualizer>(this), audioSession, id,
    211                     ActivityThread.currentOpPackageName());
    212             if (result != SUCCESS && result != ALREADY_EXISTS) {
    213                 Log.e(TAG, "Error code "+result+" when initializing Visualizer.");
    214                 switch (result) {
    215                 case ERROR_INVALID_OPERATION:
    216                     throw (new UnsupportedOperationException("Effect library not loaded"));
    217                 default:
    218                     throw (new RuntimeException("Cannot initialize Visualizer engine, error: "
    219                             +result));
    220                 }
    221             }
    222             mId = id[0];
    223             if (native_getEnabled()) {
    224                 mState = STATE_ENABLED;
    225             } else {
    226                 mState = STATE_INITIALIZED;
    227             }
    228         }
    229     }
    230 
    231     /**
    232      * Releases the native Visualizer resources. It is a good practice to release the
    233      * visualization engine when not in use.
    234      */
    235     public void release() {
    236         synchronized (mStateLock) {
    237             native_release();
    238             mState = STATE_UNINITIALIZED;
    239         }
    240     }
    241 
    242     @Override
    243     protected void finalize() {
    244         native_finalize();
    245     }
    246 
    247     /**
    248      * Enable or disable the visualization engine.
    249      * @param enabled requested enable state
    250      * @return {@link #SUCCESS} in case of success,
    251      * {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} in case of failure.
    252      * @throws IllegalStateException
    253      */
    254     public int setEnabled(boolean enabled)
    255     throws IllegalStateException {
    256         synchronized (mStateLock) {
    257             if (mState == STATE_UNINITIALIZED) {
    258                 throw(new IllegalStateException("setEnabled() called in wrong state: "+mState));
    259             }
    260             int status = SUCCESS;
    261             if ((enabled && (mState == STATE_INITIALIZED)) ||
    262                     (!enabled && (mState == STATE_ENABLED))) {
    263                 status = native_setEnabled(enabled);
    264                 if (status == SUCCESS) {
    265                     mState = enabled ? STATE_ENABLED : STATE_INITIALIZED;
    266                 }
    267             }
    268             return status;
    269         }
    270     }
    271 
    272     /**
    273      * Get current activation state of the visualizer.
    274      * @return true if the visualizer is active, false otherwise
    275      */
    276     public boolean getEnabled()
    277     {
    278         synchronized (mStateLock) {
    279             if (mState == STATE_UNINITIALIZED) {
    280                 throw(new IllegalStateException("getEnabled() called in wrong state: "+mState));
    281             }
    282             return native_getEnabled();
    283         }
    284     }
    285 
    286     /**
    287      * Returns the capture size range.
    288      * @return the mininum capture size is returned in first array element and the maximum in second
    289      * array element.
    290      */
    291     public static native int[] getCaptureSizeRange();
    292 
    293     /**
    294      * Returns the maximum capture rate for the callback capture method. This is the maximum value
    295      * for the rate parameter of the
    296      * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
    297      * @return the maximum capture rate expressed in milliHertz
    298      */
    299     public static native int getMaxCaptureRate();
    300 
    301     /**
    302      * Sets the capture size, i.e. the number of bytes returned by {@link #getWaveForm(byte[])} and
    303      * {@link #getFft(byte[])} methods. The capture size must be a power of 2 in the range returned
    304      * by {@link #getCaptureSizeRange()}.
    305      * This method must not be called when the Visualizer is enabled.
    306      * @param size requested capture size
    307      * @return {@link #SUCCESS} in case of success,
    308      * {@link #ERROR_BAD_VALUE} in case of failure.
    309      * @throws IllegalStateException
    310      */
    311     public int setCaptureSize(int size)
    312     throws IllegalStateException {
    313         synchronized (mStateLock) {
    314             if (mState != STATE_INITIALIZED) {
    315                 throw(new IllegalStateException("setCaptureSize() called in wrong state: "+mState));
    316             }
    317             return native_setCaptureSize(size);
    318         }
    319     }
    320 
    321     /**
    322      * Returns current capture size.
    323      * @return the capture size in bytes.
    324      */
    325     public int getCaptureSize()
    326     throws IllegalStateException {
    327         synchronized (mStateLock) {
    328             if (mState == STATE_UNINITIALIZED) {
    329                 throw(new IllegalStateException("getCaptureSize() called in wrong state: "+mState));
    330             }
    331             return native_getCaptureSize();
    332         }
    333     }
    334 
    335     /**
    336      * Set the type of scaling applied on the captured visualization data.
    337      * @param mode see {@link #SCALING_MODE_NORMALIZED}
    338      *     and {@link #SCALING_MODE_AS_PLAYED}
    339      * @return {@link #SUCCESS} in case of success,
    340      *     {@link #ERROR_BAD_VALUE} in case of failure.
    341      * @throws IllegalStateException
    342      */
    343     public int setScalingMode(int mode)
    344     throws IllegalStateException {
    345         synchronized (mStateLock) {
    346             if (mState == STATE_UNINITIALIZED) {
    347                 throw(new IllegalStateException("setScalingMode() called in wrong state: "
    348                         + mState));
    349             }
    350             return native_setScalingMode(mode);
    351         }
    352     }
    353 
    354     /**
    355      * Returns the current scaling mode on the captured visualization data.
    356      * @return the scaling mode, see {@link #SCALING_MODE_NORMALIZED}
    357      *     and {@link #SCALING_MODE_AS_PLAYED}.
    358      * @throws IllegalStateException
    359      */
    360     public int getScalingMode()
    361     throws IllegalStateException {
    362         synchronized (mStateLock) {
    363             if (mState == STATE_UNINITIALIZED) {
    364                 throw(new IllegalStateException("getScalingMode() called in wrong state: "
    365                         + mState));
    366             }
    367             return native_getScalingMode();
    368         }
    369     }
    370 
    371     /**
    372      * Sets the combination of measurement modes to be performed by this audio effect.
    373      * @param mode a mask of the measurements to perform. The valid values are
    374      *     {@link #MEASUREMENT_MODE_NONE} (to cancel any measurement)
    375      *     or {@link #MEASUREMENT_MODE_PEAK_RMS}.
    376      * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE} in case of failure.
    377      * @throws IllegalStateException
    378      */
    379     public int setMeasurementMode(int mode)
    380             throws IllegalStateException {
    381         synchronized (mStateLock) {
    382             if (mState == STATE_UNINITIALIZED) {
    383                 throw(new IllegalStateException("setMeasurementMode() called in wrong state: "
    384                         + mState));
    385             }
    386             return native_setMeasurementMode(mode);
    387         }
    388     }
    389 
    390     /**
    391      * Returns the current measurement modes performed by this audio effect
    392      * @return the mask of the measurements,
    393      *     {@link #MEASUREMENT_MODE_NONE} (when no measurements are performed)
    394      *     or {@link #MEASUREMENT_MODE_PEAK_RMS}.
    395      * @throws IllegalStateException
    396      */
    397     public int getMeasurementMode()
    398             throws IllegalStateException {
    399         synchronized (mStateLock) {
    400             if (mState == STATE_UNINITIALIZED) {
    401                 throw(new IllegalStateException("getMeasurementMode() called in wrong state: "
    402                         + mState));
    403             }
    404             return native_getMeasurementMode();
    405         }
    406     }
    407 
    408     /**
    409      * Returns the sampling rate of the captured audio.
    410      * @return the sampling rate in milliHertz.
    411      */
    412     public int getSamplingRate()
    413     throws IllegalStateException {
    414         synchronized (mStateLock) {
    415             if (mState == STATE_UNINITIALIZED) {
    416                 throw(new IllegalStateException("getSamplingRate() called in wrong state: "+mState));
    417             }
    418             return native_getSamplingRate();
    419         }
    420     }
    421 
    422     /**
    423      * Returns a waveform capture of currently playing audio content. The capture consists in
    424      * a number of consecutive 8-bit (unsigned) mono PCM samples equal to the capture size returned
    425      * by {@link #getCaptureSize()}.
    426      * <p>This method must be called when the Visualizer is enabled.
    427      * @param waveform array of bytes where the waveform should be returned
    428      * @return {@link #SUCCESS} in case of success,
    429      * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
    430      * in case of failure.
    431      * @throws IllegalStateException
    432      */
    433     public int getWaveForm(byte[] waveform)
    434     throws IllegalStateException {
    435         synchronized (mStateLock) {
    436             if (mState != STATE_ENABLED) {
    437                 throw(new IllegalStateException("getWaveForm() called in wrong state: "+mState));
    438             }
    439             return native_getWaveForm(waveform);
    440         }
    441     }
    442     /**
    443      * Returns a frequency capture of currently playing audio content.
    444      * <p>This method must be called when the Visualizer is enabled.
    445      * <p>The capture is an 8-bit magnitude FFT, the frequency range covered being 0 (DC) to half of
    446      * the sampling rate returned by {@link #getSamplingRate()}. The capture returns the real and
    447      * imaginary parts of a number of frequency points equal to half of the capture size plus one.
    448      * <p>Note: only the real part is returned for the first point (DC) and the last point
    449      * (sampling frequency / 2).
    450      * <p>The layout in the returned byte array is as follows:
    451      * <ul>
    452      *   <li> n is the capture size returned by getCaptureSize()</li>
    453      *   <li> Rfk, Ifk are respectively  the real and imaginary parts of the kth frequency
    454      *   component</li>
    455      *   <li> If Fs is the sampling frequency retuned by getSamplingRate() the kth frequency is:
    456      *   (k*Fs)/(n/2) </li>
    457      * </ul>
    458      * <table border="0" cellspacing="0" cellpadding="0">
    459      * <tr><td>Index </p></td>
    460      *     <td>0 </p></td>
    461      *     <td>1 </p></td>
    462      *     <td>2 </p></td>
    463      *     <td>3 </p></td>
    464      *     <td>4 </p></td>
    465      *     <td>5 </p></td>
    466      *     <td>... </p></td>
    467      *     <td>n - 2 </p></td>
    468      *     <td>n - 1 </p></td></tr>
    469      * <tr><td>Data </p></td>
    470      *     <td>Rf0 </p></td>
    471      *     <td>Rf(n/2) </p></td>
    472      *     <td>Rf1 </p></td>
    473      *     <td>If1 </p></td>
    474      *     <td>Rf2 </p></td>
    475      *     <td>If2 </p></td>
    476      *     <td>... </p></td>
    477      *     <td>Rf(n-1)/2 </p></td>
    478      *     <td>If(n-1)/2 </p></td></tr>
    479      * </table>
    480      * @param fft array of bytes where the FFT should be returned
    481      * @return {@link #SUCCESS} in case of success,
    482      * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
    483      * in case of failure.
    484      * @throws IllegalStateException
    485      */
    486     public int getFft(byte[] fft)
    487     throws IllegalStateException {
    488         synchronized (mStateLock) {
    489             if (mState != STATE_ENABLED) {
    490                 throw(new IllegalStateException("getFft() called in wrong state: "+mState));
    491             }
    492             return native_getFft(fft);
    493         }
    494     }
    495 
    496     /**
    497      * A class to store peak and RMS values.
    498      * Peak and RMS are expressed in mB, as described in the
    499      * {@link Visualizer#MEASUREMENT_MODE_PEAK_RMS} measurement mode.
    500      */
    501     public static final class MeasurementPeakRms {
    502         /**
    503          * The peak value in mB.
    504          */
    505         public int mPeak;
    506         /**
    507          * The RMS value in mB.
    508          */
    509         public int mRms;
    510     }
    511 
    512     /**
    513      * Retrieves the latest peak and RMS measurement.
    514      * Sets the peak and RMS fields of the supplied {@link Visualizer.MeasurementPeakRms} to the
    515      * latest measured values.
    516      * @param measurement a non-null {@link Visualizer.MeasurementPeakRms} instance to store
    517      *    the measurement values.
    518      * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE},
    519      *    {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
    520      *    in case of failure.
    521      */
    522     public int getMeasurementPeakRms(MeasurementPeakRms measurement) {
    523         if (measurement == null) {
    524             Log.e(TAG, "Cannot store measurements in a null object");
    525             return ERROR_BAD_VALUE;
    526         }
    527         synchronized (mStateLock) {
    528             if (mState != STATE_ENABLED) {
    529                 throw (new IllegalStateException("getMeasurementPeakRms() called in wrong state: "
    530                         + mState));
    531             }
    532             return native_getPeakRms(measurement);
    533         }
    534     }
    535 
    536     //---------------------------------------------------------
    537     // Interface definitions
    538     //--------------------
    539     /**
    540      * The OnDataCaptureListener interface defines methods called by the Visualizer to periodically
    541      * update the audio visualization capture.
    542      * The client application can implement this interface and register the listener with the
    543      * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
    544      */
    545     public interface OnDataCaptureListener  {
    546         /**
    547          * Method called when a new waveform capture is available.
    548          * <p>Data in the waveform buffer is valid only within the scope of the callback.
    549          * Applications which need access to the waveform data after returning from the callback
    550          * should make a copy of the data instead of holding a reference.
    551          * @param visualizer Visualizer object on which the listener is registered.
    552          * @param waveform array of bytes containing the waveform representation.
    553          * @param samplingRate sampling rate of the visualized audio.
    554          */
    555         void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate);
    556 
    557         /**
    558          * Method called when a new frequency capture is available.
    559          * <p>Data in the fft buffer is valid only within the scope of the callback.
    560          * Applications which need access to the fft data after returning from the callback
    561          * should make a copy of the data instead of holding a reference.
    562          *
    563          * <p>In order to obtain magnitude and phase values the following formulas can
    564          * be used:
    565          *    <pre class="prettyprint">
    566          *       for (int i = 0; i &lt; fft.size(); i += 2) {
    567          *           float magnitude = (float)Math.hypot(fft[i], fft[i + 1]);
    568          *           float phase = (float)Math.atan2(fft[i + 1], fft[i]);
    569          *       }</pre>
    570          * @param visualizer Visualizer object on which the listener is registered.
    571          * @param fft array of bytes containing the frequency representation.
    572          *    The fft array only contains the first half of the actual
    573          *    FFT spectrum (frequencies up to Nyquist frequency), exploiting
    574          *    the symmetry of the spectrum. For each frequencies bin <code>i</code>:
    575          *    <ul>
    576          *      <li>the element at index <code>2*i</code> in the array contains
    577          *          the real part of a complex number,</li>
    578          *      <li>the element at index <code>2*i+1</code> contains the imaginary
    579          *          part of the complex number.</li>
    580          *    </ul>
    581          * @param samplingRate sampling rate of the visualized audio.
    582          */
    583         void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate);
    584     }
    585 
    586     /**
    587      * Registers an OnDataCaptureListener interface and specifies the rate at which the capture
    588      * should be updated as well as the type of capture requested.
    589      * <p>Call this method with a null listener to stop receiving the capture updates.
    590      * @param listener OnDataCaptureListener registered
    591      * @param rate rate in milliHertz at which the capture should be updated
    592      * @param waveform true if a waveform capture is requested: the onWaveFormDataCapture()
    593      * method will be called on the OnDataCaptureListener interface.
    594      * @param fft true if a frequency capture is requested: the onFftDataCapture() method will be
    595      * called on the OnDataCaptureListener interface.
    596      * @return {@link #SUCCESS} in case of success,
    597      * {@link #ERROR_NO_INIT} or {@link #ERROR_BAD_VALUE} in case of failure.
    598      */
    599     public int setDataCaptureListener(OnDataCaptureListener listener,
    600             int rate, boolean waveform, boolean fft) {
    601         synchronized (mListenerLock) {
    602             mCaptureListener = listener;
    603         }
    604         if (listener == null) {
    605             // make sure capture callback is stopped in native code
    606             waveform = false;
    607             fft = false;
    608         }
    609         int status = native_setPeriodicCapture(rate, waveform, fft);
    610         if (status == SUCCESS) {
    611             if ((listener != null) && (mNativeEventHandler == null)) {
    612                 Looper looper;
    613                 if ((looper = Looper.myLooper()) != null) {
    614                     mNativeEventHandler = new NativeEventHandler(this, looper);
    615                 } else if ((looper = Looper.getMainLooper()) != null) {
    616                     mNativeEventHandler = new NativeEventHandler(this, looper);
    617                 } else {
    618                     mNativeEventHandler = null;
    619                     status = ERROR_NO_INIT;
    620                 }
    621             }
    622         }
    623         return status;
    624     }
    625 
    626     /**
    627      * @hide
    628      *
    629      * The OnServerDiedListener interface defines a method called by the Visualizer to indicate that
    630      * the connection to the native media server has been broken and that the Visualizer object will
    631      * need to be released and re-created.
    632      * The client application can implement this interface and register the listener with the
    633      * {@link #setServerDiedListener(OnServerDiedListener)} method.
    634      */
    635     public interface OnServerDiedListener  {
    636         /**
    637          * @hide
    638          *
    639          * Method called when the native media server has died.
    640          * <p>If the native media server encounters a fatal error and needs to restart, the binder
    641          * connection from the {@link #Visualizer} to the media server will be broken.  Data capture
    642          * callbacks will stop happening, and client initiated calls to the {@link #Visualizer}
    643          * instance will fail with the error code {@link #DEAD_OBJECT}.  To restore functionality,
    644          * clients should {@link #release()} their old visualizer and create a new instance.
    645          */
    646         void onServerDied();
    647     }
    648 
    649     /**
    650      * @hide
    651      *
    652      * Registers an OnServerDiedListener interface.
    653      * <p>Call this method with a null listener to stop receiving server death notifications.
    654      * @return {@link #SUCCESS} in case of success,
    655      */
    656     public int setServerDiedListener(OnServerDiedListener listener) {
    657         synchronized (mListenerLock) {
    658             mServerDiedListener = listener;
    659         }
    660         return SUCCESS;
    661     }
    662 
    663     /**
    664      * Helper class to handle the forwarding of native events to the appropriate listeners
    665      */
    666     private class NativeEventHandler extends Handler
    667     {
    668         private Visualizer mVisualizer;
    669 
    670         public NativeEventHandler(Visualizer v, Looper looper) {
    671             super(looper);
    672             mVisualizer = v;
    673         }
    674 
    675         private void handleCaptureMessage(Message msg) {
    676             OnDataCaptureListener l = null;
    677             synchronized (mListenerLock) {
    678                 l = mVisualizer.mCaptureListener;
    679             }
    680 
    681             if (l != null) {
    682                 byte[] data = (byte[])msg.obj;
    683                 int samplingRate = msg.arg1;
    684 
    685                 switch(msg.what) {
    686                 case NATIVE_EVENT_PCM_CAPTURE:
    687                     l.onWaveFormDataCapture(mVisualizer, data, samplingRate);
    688                     break;
    689                 case NATIVE_EVENT_FFT_CAPTURE:
    690                     l.onFftDataCapture(mVisualizer, data, samplingRate);
    691                     break;
    692                 default:
    693                     Log.e(TAG,"Unknown native event in handleCaptureMessge: "+msg.what);
    694                     break;
    695                 }
    696             }
    697         }
    698 
    699         private void handleServerDiedMessage(Message msg) {
    700             OnServerDiedListener l = null;
    701             synchronized (mListenerLock) {
    702                 l = mVisualizer.mServerDiedListener;
    703             }
    704 
    705             if (l != null)
    706                 l.onServerDied();
    707         }
    708 
    709         @Override
    710         public void handleMessage(Message msg) {
    711             if (mVisualizer == null) {
    712                 return;
    713             }
    714 
    715             switch(msg.what) {
    716             case NATIVE_EVENT_PCM_CAPTURE:
    717             case NATIVE_EVENT_FFT_CAPTURE:
    718                 handleCaptureMessage(msg);
    719                 break;
    720             case NATIVE_EVENT_SERVER_DIED:
    721                 handleServerDiedMessage(msg);
    722                 break;
    723             default:
    724                 Log.e(TAG,"Unknown native event: "+msg.what);
    725                 break;
    726             }
    727         }
    728     }
    729 
    730     //---------------------------------------------------------
    731     // Interface definitions
    732     //--------------------
    733 
    734     private static native final void native_init();
    735 
    736     private native final int native_setup(Object audioeffect_this,
    737                                           int audioSession,
    738                                           int[] id,
    739                                           String opPackageName);
    740 
    741     private native final void native_finalize();
    742 
    743     private native final void native_release();
    744 
    745     private native final int native_setEnabled(boolean enabled);
    746 
    747     private native final boolean native_getEnabled();
    748 
    749     private native final int native_setCaptureSize(int size);
    750 
    751     private native final int native_getCaptureSize();
    752 
    753     private native final int native_setScalingMode(int mode);
    754 
    755     private native final int native_getScalingMode();
    756 
    757     private native final int native_setMeasurementMode(int mode);
    758 
    759     private native final int native_getMeasurementMode();
    760 
    761     private native final int native_getSamplingRate();
    762 
    763     private native final int native_getWaveForm(byte[] waveform);
    764 
    765     private native final int native_getFft(byte[] fft);
    766 
    767     private native final int native_getPeakRms(MeasurementPeakRms measurement);
    768 
    769     private native final int native_setPeriodicCapture(int rate, boolean waveForm, boolean fft);
    770 
    771     //---------------------------------------------------------
    772     // Java methods called from the native side
    773     //--------------------
    774     @SuppressWarnings("unused")
    775     private static void postEventFromNative(Object effect_ref,
    776             int what, int arg1, int arg2, Object obj) {
    777         Visualizer visu = (Visualizer)((WeakReference)effect_ref).get();
    778         if (visu == null) {
    779             return;
    780         }
    781 
    782         if (visu.mNativeEventHandler != null) {
    783             Message m = visu.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj);
    784             visu.mNativeEventHandler.sendMessage(m);
    785         }
    786 
    787     }
    788 }
    789 
    790