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