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