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