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