• 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.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_INVALID_OPERATION} if Visualizer effect enginer not enabled.
340      * @throws IllegalStateException if the effect is not in proper state.
341      * @throws IllegalArgumentException if the size parameter is invalid (out of supported range).
342      */
setCaptureSize(int size)343     public int setCaptureSize(int size)
344     throws IllegalStateException {
345         synchronized (mStateLock) {
346             if (mState != STATE_INITIALIZED) {
347                 throw(new IllegalStateException("setCaptureSize() called in wrong state: "+mState));
348             }
349 
350             int ret = native_setCaptureSize(size);
351             if (ret == ERROR_BAD_VALUE) {
352                 throw(new IllegalArgumentException("setCaptureSize to " + size + " failed"));
353             }
354 
355             return ret;
356         }
357     }
358 
359     /**
360      * Returns current capture size.
361      * @return the capture size in bytes.
362      */
getCaptureSize()363     public int getCaptureSize()
364     throws IllegalStateException {
365         synchronized (mStateLock) {
366             if (mState == STATE_UNINITIALIZED) {
367                 throw(new IllegalStateException("getCaptureSize() called in wrong state: "+mState));
368             }
369             return native_getCaptureSize();
370         }
371     }
372 
373     /**
374      * Set the type of scaling applied on the captured visualization data.
375      * @param mode see {@link #SCALING_MODE_NORMALIZED}
376      *     and {@link #SCALING_MODE_AS_PLAYED}
377      * @return {@link #SUCCESS} in case of success,
378      *     {@link #ERROR_BAD_VALUE} in case of failure.
379      * @throws IllegalStateException
380      */
setScalingMode(int mode)381     public int setScalingMode(int mode)
382     throws IllegalStateException {
383         synchronized (mStateLock) {
384             if (mState == STATE_UNINITIALIZED) {
385                 throw(new IllegalStateException("setScalingMode() called in wrong state: "
386                         + mState));
387             }
388             return native_setScalingMode(mode);
389         }
390     }
391 
392     /**
393      * Returns the current scaling mode on the captured visualization data.
394      * @return the scaling mode, see {@link #SCALING_MODE_NORMALIZED}
395      *     and {@link #SCALING_MODE_AS_PLAYED}.
396      * @throws IllegalStateException
397      */
getScalingMode()398     public int getScalingMode()
399     throws IllegalStateException {
400         synchronized (mStateLock) {
401             if (mState == STATE_UNINITIALIZED) {
402                 throw(new IllegalStateException("getScalingMode() called in wrong state: "
403                         + mState));
404             }
405             return native_getScalingMode();
406         }
407     }
408 
409     /**
410      * Sets the combination of measurement modes to be performed by this audio effect.
411      * @param mode a mask of the measurements to perform. The valid values are
412      *     {@link #MEASUREMENT_MODE_NONE} (to cancel any measurement)
413      *     or {@link #MEASUREMENT_MODE_PEAK_RMS}.
414      * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE} in case of failure.
415      * @throws IllegalStateException
416      */
setMeasurementMode(int mode)417     public int setMeasurementMode(int mode)
418             throws IllegalStateException {
419         synchronized (mStateLock) {
420             if (mState == STATE_UNINITIALIZED) {
421                 throw(new IllegalStateException("setMeasurementMode() called in wrong state: "
422                         + mState));
423             }
424             return native_setMeasurementMode(mode);
425         }
426     }
427 
428     /**
429      * Returns the current measurement modes performed by this audio effect
430      * @return the mask of the measurements,
431      *     {@link #MEASUREMENT_MODE_NONE} (when no measurements are performed)
432      *     or {@link #MEASUREMENT_MODE_PEAK_RMS}.
433      * @throws IllegalStateException
434      */
getMeasurementMode()435     public int getMeasurementMode()
436             throws IllegalStateException {
437         synchronized (mStateLock) {
438             if (mState == STATE_UNINITIALIZED) {
439                 throw(new IllegalStateException("getMeasurementMode() called in wrong state: "
440                         + mState));
441             }
442             return native_getMeasurementMode();
443         }
444     }
445 
446     /**
447      * Returns the sampling rate of the captured audio.
448      * @return the sampling rate in milliHertz.
449      */
getSamplingRate()450     public int getSamplingRate()
451     throws IllegalStateException {
452         synchronized (mStateLock) {
453             if (mState == STATE_UNINITIALIZED) {
454                 throw(new IllegalStateException("getSamplingRate() called in wrong state: "+mState));
455             }
456             return native_getSamplingRate();
457         }
458     }
459 
460     /**
461      * Returns a waveform capture of currently playing audio content. The capture consists in
462      * a number of consecutive 8-bit (unsigned) mono PCM samples equal to the capture size returned
463      * by {@link #getCaptureSize()}.
464      * <p>This method must be called when the Visualizer is enabled.
465      * @param waveform array of bytes where the waveform should be returned, array length must be
466      * at least equals to the capture size returned by {@link #getCaptureSize()}.
467      * @return {@link #SUCCESS} in case of success,
468      * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
469      * in case of failure.
470      * @throws IllegalStateException
471      * @throws IllegalArgumentException
472      */
getWaveForm(byte[] waveform)473     public int getWaveForm(byte[] waveform)
474     throws IllegalStateException {
475         synchronized (mStateLock) {
476             if (mState != STATE_ENABLED) {
477                 throw(new IllegalStateException("getWaveForm() called in wrong state: "+mState));
478             }
479             int captureSize = getCaptureSize();
480             if (captureSize > waveform.length) {
481                 throw(new IllegalArgumentException("getWaveForm() called with illegal size: "
482                                                    + waveform.length + " expecting at least "
483                                                    + captureSize + " bytes"));
484             }
485             return native_getWaveForm(waveform);
486         }
487     }
488     /**
489      * Returns a frequency capture of currently playing audio content.
490      * <p>This method must be called when the Visualizer is enabled.
491      * <p>The capture is an 8-bit magnitude FFT, the frequency range covered being 0 (DC) to half of
492      * the sampling rate returned by {@link #getSamplingRate()}. The capture returns the real and
493      * imaginary parts of a number of frequency points equal to half of the capture size plus one.
494      * <p>Note: only the real part is returned for the first point (DC) and the last point
495      * (sampling frequency / 2).
496      * <p>The layout in the returned byte array is as follows:
497      * <ul>
498      *   <li> n is the capture size returned by getCaptureSize()</li>
499      *   <li> Rfk, Ifk are respectively  the real and imaginary parts of the kth frequency
500      *   component</li>
501      *   <li> If Fs is the sampling frequency retuned by getSamplingRate() the kth frequency is:
502      *   k * Fs / n </li>
503      * </ul>
504      * <table border="0" cellspacing="0" cellpadding="0">
505      * <tr><td>Index </p></td>
506      *     <td>0 </p></td>
507      *     <td>1 </p></td>
508      *     <td>2 </p></td>
509      *     <td>3 </p></td>
510      *     <td>4 </p></td>
511      *     <td>5 </p></td>
512      *     <td>... </p></td>
513      *     <td>n - 2 </p></td>
514      *     <td>n - 1 </p></td></tr>
515      * <tr><td>Data </p></td>
516      *     <td>Rf0 </p></td>
517      *     <td>Rf(n/2) </p></td>
518      *     <td>Rf1 </p></td>
519      *     <td>If1 </p></td>
520      *     <td>Rf2 </p></td>
521      *     <td>If2 </p></td>
522      *     <td>... </p></td>
523      *     <td>Rf(n/2-1) </p></td>
524      *     <td>If(n/2-1) </p></td></tr>
525      * </table>
526      * <p>In order to obtain magnitude and phase values the following code can
527      * be used:
528      *    <pre class="prettyprint">
529      *       int n = fft.size();
530      *       float[] magnitudes = new float[n / 2 + 1];
531      *       float[] phases = new float[n / 2 + 1];
532      *       magnitudes[0] = (float)Math.abs(fft[0]);      // DC
533      *       magnitudes[n / 2] = (float)Math.abs(fft[1]);  // Nyquist
534      *       phases[0] = phases[n / 2] = 0;
535      *       for (int k = 1; k &lt; n / 2; k++) {
536      *           int i = k * 2;
537      *           magnitudes[k] = (float)Math.hypot(fft[i], fft[i + 1]);
538      *           phases[k] = (float)Math.atan2(fft[i + 1], fft[i]);
539      *       }</pre>
540      * @param fft array of bytes where the FFT should be returned
541      * @return {@link #SUCCESS} in case of success,
542      * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
543      * in case of failure.
544      * @throws IllegalStateException
545      */
getFft(byte[] fft)546     public int getFft(byte[] fft)
547     throws IllegalStateException {
548         synchronized (mStateLock) {
549             if (mState != STATE_ENABLED) {
550                 throw(new IllegalStateException("getFft() called in wrong state: "+mState));
551             }
552             return native_getFft(fft);
553         }
554     }
555 
556     /**
557      * A class to store peak and RMS values.
558      * Peak and RMS are expressed in mB, as described in the
559      * {@link Visualizer#MEASUREMENT_MODE_PEAK_RMS} measurement mode.
560      */
561     public static final class MeasurementPeakRms {
562         /**
563          * The peak value in mB.
564          */
565         public int mPeak;
566         /**
567          * The RMS value in mB.
568          */
569         public int mRms;
570     }
571 
572     /**
573      * Retrieves the latest peak and RMS measurement.
574      * Sets the peak and RMS fields of the supplied {@link Visualizer.MeasurementPeakRms} to the
575      * latest measured values.
576      * @param measurement a non-null {@link Visualizer.MeasurementPeakRms} instance to store
577      *    the measurement values.
578      * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE},
579      *    {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT}
580      *    in case of failure.
581      */
getMeasurementPeakRms(MeasurementPeakRms measurement)582     public int getMeasurementPeakRms(MeasurementPeakRms measurement) {
583         if (measurement == null) {
584             Log.e(TAG, "Cannot store measurements in a null object");
585             return ERROR_BAD_VALUE;
586         }
587         synchronized (mStateLock) {
588             if (mState != STATE_ENABLED) {
589                 throw (new IllegalStateException("getMeasurementPeakRms() called in wrong state: "
590                         + mState));
591             }
592             return native_getPeakRms(measurement);
593         }
594     }
595 
596     //---------------------------------------------------------
597     // Interface definitions
598     //--------------------
599     /**
600      * The OnDataCaptureListener interface defines methods called by the Visualizer to periodically
601      * update the audio visualization capture.
602      * The client application can implement this interface and register the listener with the
603      * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method.
604      */
605     public interface OnDataCaptureListener  {
606         /**
607          * Method called when a new waveform capture is available.
608          * <p>Data in the waveform buffer is valid only within the scope of the callback.
609          * Applications which need access to the waveform data after returning from the callback
610          * should make a copy of the data instead of holding a reference.
611          * @param visualizer Visualizer object on which the listener is registered.
612          * @param waveform array of bytes containing the waveform representation.
613          * @param samplingRate sampling rate of the visualized audio.
614          */
onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate)615         void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate);
616 
617         /**
618          * Method called when a new frequency capture is available.
619          * <p>Data in the fft buffer is valid only within the scope of the callback.
620          * Applications which need access to the fft data after returning from the callback
621          * should make a copy of the data instead of holding a reference.
622          * <p>For the explanation of the fft data array layout, and the example
623          * code for processing it, please see the documentation for {@link #getFft(byte[])} method.
624          *
625          * @param visualizer Visualizer object on which the listener is registered.
626          * @param fft array of bytes containing the frequency representation.
627          * @param samplingRate sampling rate of the visualized audio.
628          */
onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate)629         void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate);
630     }
631 
632     /**
633      * Registers an OnDataCaptureListener interface and specifies the rate at which the capture
634      * should be updated as well as the type of capture requested.
635      * <p>Call this method with a null listener to stop receiving the capture updates.
636      * @param listener OnDataCaptureListener registered
637      * @param rate rate in milliHertz at which the capture should be updated
638      * @param waveform true if a waveform capture is requested: the onWaveFormDataCapture()
639      * method will be called on the OnDataCaptureListener interface.
640      * @param fft true if a frequency capture is requested: the onFftDataCapture() method will be
641      * called on the OnDataCaptureListener interface.
642      * @return {@link #SUCCESS} in case of success,
643      * {@link #ERROR_NO_INIT} or {@link #ERROR_BAD_VALUE} in case of failure.
644      */
setDataCaptureListener(@ullable OnDataCaptureListener listener, int rate, boolean waveform, boolean fft)645     public int setDataCaptureListener(@Nullable OnDataCaptureListener listener,
646             int rate, boolean waveform, boolean fft) {
647         if (listener == null) {
648             // make sure capture callback is stopped in native code
649             waveform = false;
650             fft = false;
651         }
652         int status;
653         synchronized (mStateLock) {
654             status = native_setPeriodicCapture(rate, waveform, fft);
655         }
656         if (status == SUCCESS) {
657             synchronized (mListenerLock) {
658                 mCaptureListener = listener;
659                 if ((listener != null) && (mNativeEventHandler == null)) {
660                     Looper looper;
661                     if ((looper = Looper.myLooper()) != null) {
662                         mNativeEventHandler = new Handler(looper);
663                     } else if ((looper = Looper.getMainLooper()) != null) {
664                         mNativeEventHandler = new Handler(looper);
665                     } else {
666                         mNativeEventHandler = null;
667                         status = ERROR_NO_INIT;
668                     }
669                 }
670             }
671         }
672         return status;
673     }
674 
675     /**
676      * @hide
677      *
678      * The OnServerDiedListener interface defines a method called by the Visualizer to indicate that
679      * the connection to the native media server has been broken and that the Visualizer object will
680      * need to be released and re-created.
681      * The client application can implement this interface and register the listener with the
682      * {@link #setServerDiedListener(OnServerDiedListener)} method.
683      */
684     public interface OnServerDiedListener  {
685         /**
686          * @hide
687          *
688          * Method called when the native media server has died.
689          * <p>If the native media server encounters a fatal error and needs to restart, the binder
690          * connection from the {@link #Visualizer} to the media server will be broken.  Data capture
691          * callbacks will stop happening, and client initiated calls to the {@link #Visualizer}
692          * instance will fail with the error code {@link #DEAD_OBJECT}.  To restore functionality,
693          * clients should {@link #release()} their old visualizer and create a new instance.
694          */
onServerDied()695         void onServerDied();
696     }
697 
698     /**
699      * @hide
700      *
701      * Registers an OnServerDiedListener interface.
702      * <p>Call this method with a null listener to stop receiving server death notifications.
703      * @return {@link #SUCCESS} in case of success,
704      */
setServerDiedListener(@ullable OnServerDiedListener listener)705     public int setServerDiedListener(@Nullable OnServerDiedListener listener) {
706         synchronized (mListenerLock) {
707             mServerDiedListener = listener;
708         }
709         return SUCCESS;
710     }
711 
712     //---------------------------------------------------------
713     // Interface definitions
714     //--------------------
715 
native_init()716     private static native final void native_init();
717 
718     @GuardedBy("mStateLock")
native_setup(Object audioeffect_this, int audioSession, int[] id, @NonNull Parcel attributionSource)719     private native final int native_setup(Object audioeffect_this,
720                                           int audioSession,
721                                           int[] id,
722                                           @NonNull Parcel attributionSource);
723 
724     @GuardedBy("mStateLock")
native_finalize()725     private native final void native_finalize();
726 
727     @GuardedBy("mStateLock")
native_release()728     private native final void native_release();
729 
730     @GuardedBy("mStateLock")
native_setEnabled(boolean enabled)731     private native final int native_setEnabled(boolean enabled);
732 
733     @GuardedBy("mStateLock")
native_getEnabled()734     private native final boolean native_getEnabled();
735 
736     @GuardedBy("mStateLock")
native_setCaptureSize(int size)737     private native final int native_setCaptureSize(int size);
738 
739     @GuardedBy("mStateLock")
native_getCaptureSize()740     private native final int native_getCaptureSize();
741 
742     @GuardedBy("mStateLock")
native_setScalingMode(int mode)743     private native final int native_setScalingMode(int mode);
744 
745     @GuardedBy("mStateLock")
native_getScalingMode()746     private native final int native_getScalingMode();
747 
748     @GuardedBy("mStateLock")
native_setMeasurementMode(int mode)749     private native final int native_setMeasurementMode(int mode);
750 
751     @GuardedBy("mStateLock")
native_getMeasurementMode()752     private native final int native_getMeasurementMode();
753 
754     @GuardedBy("mStateLock")
native_getSamplingRate()755     private native final int native_getSamplingRate();
756 
757     @GuardedBy("mStateLock")
native_getWaveForm(byte[] waveform)758     private native final int native_getWaveForm(byte[] waveform);
759 
760     @GuardedBy("mStateLock")
native_getFft(byte[] fft)761     private native final int native_getFft(byte[] fft);
762 
763     @GuardedBy("mStateLock")
native_getPeakRms(MeasurementPeakRms measurement)764     private native final int native_getPeakRms(MeasurementPeakRms measurement);
765 
766     @GuardedBy("mStateLock")
native_setPeriodicCapture(int rate, boolean waveForm, boolean fft)767     private native final int native_setPeriodicCapture(int rate, boolean waveForm, boolean fft);
768 
769     //---------------------------------------------------------
770     // Java methods called from the native side
771     //--------------------
772     @SuppressWarnings("unused")
postEventFromNative(Object effect_ref, int what, int samplingRate, byte[] data)773     private static void postEventFromNative(Object effect_ref,
774             int what, int samplingRate, byte[] data) {
775         final Visualizer visualizer = (Visualizer) ((WeakReference) effect_ref).get();
776         if (visualizer == null) return;
777 
778         final Handler handler;
779         synchronized (visualizer.mListenerLock) {
780             handler = visualizer.mNativeEventHandler;
781         }
782         if (handler == null) return;
783 
784         switch (what) {
785             case NATIVE_EVENT_PCM_CAPTURE:
786             case NATIVE_EVENT_FFT_CAPTURE:
787                 handler.post(() -> {
788                     final OnDataCaptureListener l;
789                     synchronized (visualizer.mListenerLock) {
790                         l = visualizer.mCaptureListener;
791                     }
792                     if (l != null) {
793                         if (what == NATIVE_EVENT_PCM_CAPTURE) {
794                             l.onWaveFormDataCapture(visualizer, data, samplingRate);
795                         } else { // what == NATIVE_EVENT_FFT_CAPTURE
796                             l.onFftDataCapture(visualizer, data, samplingRate);
797                         }
798                     }
799                 });
800                 break;
801             case NATIVE_EVENT_SERVER_DIED:
802                 handler.post(() -> {
803                     final OnServerDiedListener l;
804                     synchronized (visualizer.mListenerLock) {
805                         l = visualizer.mServerDiedListener;
806                     }
807                     if (l != null) {
808                         l.onServerDied();
809                     }
810                 });
811                 break;
812             default:
813                 Log.e(TAG, "Unknown native event in postEventFromNative: " + what);
814                 break;
815         }
816     }
817 }
818