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 < 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