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