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