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