Visualizer.java revision badca26cb218852d32862dada36ee52fce865ad2
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 17package android.media.audiofx; 18 19import android.util.Log; 20import java.lang.ref.WeakReference; 21import java.io.IOException; 22import android.os.Handler; 23import android.os.Looper; 24import 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 */ 61 62public class Visualizer { 63 64 static { 65 System.loadLibrary("audioeffect_jni"); 66 native_init(); 67 } 68 69 private final static String TAG = "Visualizer-JAVA"; 70 71 /** 72 * State of a Visualizer object that was not successfully initialized upon creation 73 */ 74 public static final int STATE_UNINITIALIZED = 0; 75 /** 76 * State of a Visualizer object that is ready to be used. 77 */ 78 public static final int STATE_INITIALIZED = 1; 79 /** 80 * State of a Visualizer object that is active. 81 */ 82 public static final int STATE_ENABLED = 2; 83 84 // to keep in sync with system/media/audio_effects/include/audio_effects/effect_visualizer.h 85 /** 86 * Defines a capture mode where amplification is applied based on the content of the captured 87 * data. This is the default Visualizer mode, and is suitable for music visualization. 88 */ 89 public static final int SCALING_MODE_NORMALIZED = 0; 90 /** 91 * Defines a capture mode where the playback volume will affect (scale) the range of the 92 * captured data. A low playback volume will lead to low sample and fft values, and vice-versa. 93 */ 94 public static final int SCALING_MODE_AS_PLAYED = 1; 95 96 /** 97 * @hide 98 * CANDIDATE FOR PUBLIC API 99 * Defines a measurement mode with no requested measurement. 100 */ 101 public static final int MEASUREMENT_MODE_NONE = 0; 102 103 /** 104 * @hide 105 * CANDIDATE FOR PUBLIC API 106 * Defines a measurement mode which computes the peak and RMS value in mB, where 0mB is the 107 * maximum sample value, and -9600mB is the minimum value. 108 * Values for peak and RMS can be retrieved with {@link #getIntMeasurements(int, int[])}, where 109 * the array holds the peak value at index {@link #MEASUREMENT_INDEX_PEAK} in the measurement 110 * array, and the RMS value at index {@link #MEASUREMENT_INDEX_RMS}. 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 int mNativeVisualizer; 188 private int 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 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 if (result != SUCCESS && result != ALREADY_EXISTS) { 212 Log.e(TAG, "Error code "+result+" when initializing Visualizer."); 213 switch (result) { 214 case ERROR_INVALID_OPERATION: 215 throw (new UnsupportedOperationException("Effect library not loaded")); 216 default: 217 throw (new RuntimeException("Cannot initialize Visualizer engine, error: " 218 +result)); 219 } 220 } 221 mId = id[0]; 222 if (native_getEnabled()) { 223 mState = STATE_ENABLED; 224 } else { 225 mState = STATE_INITIALIZED; 226 } 227 } 228 } 229 230 /** 231 * Releases the native Visualizer resources. It is a good practice to release the 232 * visualization engine when not in use. 233 */ 234 public void release() { 235 synchronized (mStateLock) { 236 native_release(); 237 mState = STATE_UNINITIALIZED; 238 } 239 } 240 241 @Override 242 protected void finalize() { 243 native_finalize(); 244 } 245 246 /** 247 * Enable or disable the visualization engine. 248 * @param enabled requested enable state 249 * @return {@link #SUCCESS} in case of success, 250 * {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} in case of failure. 251 * @throws IllegalStateException 252 */ 253 public int setEnabled(boolean enabled) 254 throws IllegalStateException { 255 synchronized (mStateLock) { 256 if (mState == STATE_UNINITIALIZED) { 257 throw(new IllegalStateException("setEnabled() called in wrong state: "+mState)); 258 } 259 int status = SUCCESS; 260 if ((enabled && (mState == STATE_INITIALIZED)) || 261 (!enabled && (mState == STATE_ENABLED))) { 262 status = native_setEnabled(enabled); 263 if (status == SUCCESS) { 264 mState = enabled ? STATE_ENABLED : STATE_INITIALIZED; 265 } 266 } 267 return status; 268 } 269 } 270 271 /** 272 * Get current activation state of the visualizer. 273 * @return true if the visualizer is active, false otherwise 274 */ 275 public boolean getEnabled() 276 { 277 synchronized (mStateLock) { 278 if (mState == STATE_UNINITIALIZED) { 279 throw(new IllegalStateException("getEnabled() called in wrong state: "+mState)); 280 } 281 return native_getEnabled(); 282 } 283 } 284 285 /** 286 * Returns the capture size range. 287 * @return the mininum capture size is returned in first array element and the maximum in second 288 * array element. 289 */ 290 public static native int[] getCaptureSizeRange(); 291 292 /** 293 * Returns the maximum capture rate for the callback capture method. This is the maximum value 294 * for the rate parameter of the 295 * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method. 296 * @return the maximum capture rate expressed in milliHertz 297 */ 298 public static native int getMaxCaptureRate(); 299 300 /** 301 * Sets the capture size, i.e. the number of bytes returned by {@link #getWaveForm(byte[])} and 302 * {@link #getFft(byte[])} methods. The capture size must be a power of 2 in the range returned 303 * by {@link #getCaptureSizeRange()}. 304 * This method must not be called when the Visualizer is enabled. 305 * @param size requested capture size 306 * @return {@link #SUCCESS} in case of success, 307 * {@link #ERROR_BAD_VALUE} in case of failure. 308 * @throws IllegalStateException 309 */ 310 public int setCaptureSize(int size) 311 throws IllegalStateException { 312 synchronized (mStateLock) { 313 if (mState != STATE_INITIALIZED) { 314 throw(new IllegalStateException("setCaptureSize() called in wrong state: "+mState)); 315 } 316 return native_setCaptureSize(size); 317 } 318 } 319 320 /** 321 * Returns current capture size. 322 * @return the capture size in bytes. 323 */ 324 public int getCaptureSize() 325 throws IllegalStateException { 326 synchronized (mStateLock) { 327 if (mState == STATE_UNINITIALIZED) { 328 throw(new IllegalStateException("getCaptureSize() called in wrong state: "+mState)); 329 } 330 return native_getCaptureSize(); 331 } 332 } 333 334 /** 335 * Set the type of scaling applied on the captured visualization data. 336 * @param mode see {@link #SCALING_MODE_NORMALIZED} 337 * and {@link #SCALING_MODE_AS_PLAYED} 338 * @return {@link #SUCCESS} in case of success, 339 * {@link #ERROR_BAD_VALUE} in case of failure. 340 * @throws IllegalStateException 341 */ 342 public int setScalingMode(int mode) 343 throws IllegalStateException { 344 synchronized (mStateLock) { 345 if (mState == STATE_UNINITIALIZED) { 346 throw(new IllegalStateException("setScalingMode() called in wrong state: " 347 + mState)); 348 } 349 return native_setScalingMode(mode); 350 } 351 } 352 353 /** 354 * Returns the current scaling mode on the captured visualization data. 355 * @return the scaling mode, see {@link #SCALING_MODE_NORMALIZED} 356 * and {@link #SCALING_MODE_AS_PLAYED}. 357 * @throws IllegalStateException 358 */ 359 public int getScalingMode() 360 throws IllegalStateException { 361 synchronized (mStateLock) { 362 if (mState == STATE_UNINITIALIZED) { 363 throw(new IllegalStateException("getScalingMode() called in wrong state: " 364 + mState)); 365 } 366 return native_getScalingMode(); 367 } 368 } 369 370 /** 371 * @hide 372 * CANDIDATE FOR PUBLIC API 373 * Sets the combination of measurement modes to be performed by this audio effect. 374 * @param mode a mask of the measurements to perform. The valid values are 375 * {@link #MEASUREMENT_MODE_NONE} (to cancel any measurement) 376 * or {@link #MEASUREMENT_MODE_PEAK_RMS}. 377 * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE} in case of failure. 378 * @throws IllegalStateException 379 */ 380 public int setMeasurementMode(int mode) 381 throws IllegalStateException { 382 synchronized (mStateLock) { 383 if (mState == STATE_UNINITIALIZED) { 384 throw(new IllegalStateException("setMeasurementMode() called in wrong state: " 385 + mState)); 386 } 387 return native_setMeasurementMode(mode); 388 } 389 } 390 391 /** 392 * @hide 393 * CANDIDATE FOR PUBLIC API 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 */ 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 */ 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 */ 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/2) </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-1)/2 </p></td> 481 * <td>If(n-1)/2 </p></td></tr> 482 * </table> 483 * @param fft array of bytes where the FFT should be returned 484 * @return {@link #SUCCESS} in case of success, 485 * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} 486 * in case of failure. 487 * @throws IllegalStateException 488 */ 489 public int getFft(byte[] fft) 490 throws IllegalStateException { 491 synchronized (mStateLock) { 492 if (mState != STATE_ENABLED) { 493 throw(new IllegalStateException("getFft() called in wrong state: "+mState)); 494 } 495 return native_getFft(fft); 496 } 497 } 498 499 /** 500 * @hide 501 * CANDIDATE FOR PUBLIC API 502 * A class to store peak and RMS values. 503 * Peak and RMS are expressed in mB, as described in the 504 * {@link Visualizer#MEASUREMENT_MODE_PEAK_RMS} measurement mode. 505 */ 506 public static final class MeasurementPeakRms { 507 /** 508 * @hide 509 * CANDIDATE FOR PUBLIC API 510 */ 511 public int mPeak; 512 /** 513 * @hide 514 * CANDIDATE FOR PUBLIC API 515 */ 516 public int mRms; 517 } 518 519 /** 520 * @hide 521 * Retrieves the latest peak and RMS measurement. 522 * Sets the peak and RMS fields of the {@link Visualizer.MeasurementPeakRms} to the latest 523 * measured values. 524 * @param measurement a non-null {@link Visualizer.MeasurementPeakRms} instance to store 525 * the measurement values. 526 * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE}, 527 * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} 528 * in case of failure. 529 */ 530 public int getMeasurementPeakRms(MeasurementPeakRms measurement) { 531 if (measurement == null) { 532 Log.e(TAG, "Cannot store measurements in a null object"); 533 return ERROR_BAD_VALUE; 534 } 535 synchronized (mStateLock) { 536 if (mState != STATE_ENABLED) { 537 throw (new IllegalStateException("getMeasurementPeakRms() called in wrong state: " 538 + mState)); 539 } 540 return native_getPeakRms(measurement); 541 } 542 } 543 544 //--------------------------------------------------------- 545 // Interface definitions 546 //-------------------- 547 /** 548 * The OnDataCaptureListener interface defines methods called by the Visualizer to periodically 549 * update the audio visualization capture. 550 * The client application can implement this interface and register the listener with the 551 * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method. 552 */ 553 public interface OnDataCaptureListener { 554 /** 555 * Method called when a new waveform capture is available. 556 * <p>Data in the waveform buffer is valid only within the scope of the callback. 557 * Applications which needs access to the waveform data after returning from the callback 558 * should make a copy of the data instead of holding a reference. 559 * @param visualizer Visualizer object on which the listener is registered. 560 * @param waveform array of bytes containing the waveform representation. 561 * @param samplingRate sampling rate of the audio visualized. 562 */ 563 void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate); 564 565 /** 566 * Method called when a new frequency capture is available. 567 * <p>Data in the fft buffer is valid only within the scope of the callback. 568 * Applications which needs access to the fft data after returning from the callback 569 * should make a copy of the data instead of holding a reference. 570 * @param visualizer Visualizer object on which the listener is registered. 571 * @param fft array of bytes containing the frequency representation. 572 * @param samplingRate sampling rate of the audio visualized. 573 */ 574 void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate); 575 } 576 577 /** 578 * Registers an OnDataCaptureListener interface and specifies the rate at which the capture 579 * should be updated as well as the type of capture requested. 580 * <p>Call this method with a null listener to stop receiving the capture updates. 581 * @param listener OnDataCaptureListener registered 582 * @param rate rate in milliHertz at which the capture should be updated 583 * @param waveform true if a waveform capture is requested: the onWaveFormDataCapture() 584 * method will be called on the OnDataCaptureListener interface. 585 * @param fft true if a frequency capture is requested: the onFftDataCapture() method will be 586 * called on the OnDataCaptureListener interface. 587 * @return {@link #SUCCESS} in case of success, 588 * {@link #ERROR_NO_INIT} or {@link #ERROR_BAD_VALUE} in case of failure. 589 */ 590 public int setDataCaptureListener(OnDataCaptureListener listener, 591 int rate, boolean waveform, boolean fft) { 592 synchronized (mListenerLock) { 593 mCaptureListener = listener; 594 } 595 if (listener == null) { 596 // make sure capture callback is stopped in native code 597 waveform = false; 598 fft = false; 599 } 600 int status = native_setPeriodicCapture(rate, waveform, fft); 601 if (status == SUCCESS) { 602 if ((listener != null) && (mNativeEventHandler == null)) { 603 Looper looper; 604 if ((looper = Looper.myLooper()) != null) { 605 mNativeEventHandler = new NativeEventHandler(this, looper); 606 } else if ((looper = Looper.getMainLooper()) != null) { 607 mNativeEventHandler = new NativeEventHandler(this, looper); 608 } else { 609 mNativeEventHandler = null; 610 status = ERROR_NO_INIT; 611 } 612 } 613 } 614 return status; 615 } 616 617 /** 618 * @hide 619 * 620 * The OnServerDiedListener interface defines a method called by the Visualizer to indicate that 621 * the connection to the native media server has been broken and that the Visualizer object will 622 * need to be released and re-created. 623 * The client application can implement this interface and register the listener with the 624 * {@link #setServerDiedListener(OnServerDiedListener)} method. 625 */ 626 public interface OnServerDiedListener { 627 /** 628 * @hide 629 * 630 * Method called when the native media server has died. 631 * <p>If the native media server encounters a fatal error and needs to restart, the binder 632 * connection from the {@link #Visualizer} to the media server will be broken. Data capture 633 * callbacks will stop happening, and client initiated calls to the {@link #Visualizer} 634 * instance will fail with the error code {@link #DEAD_OBJECT}. To restore functionality, 635 * clients should {@link #release()} their old visualizer and create a new instance. 636 */ 637 void onServerDied(); 638 } 639 640 /** 641 * @hide 642 * 643 * Registers an OnServerDiedListener interface. 644 * <p>Call this method with a null listener to stop receiving server death notifications. 645 * @return {@link #SUCCESS} in case of success, 646 */ 647 public int setServerDiedListener(OnServerDiedListener listener) { 648 synchronized (mListenerLock) { 649 mServerDiedListener = listener; 650 } 651 return SUCCESS; 652 } 653 654 /** 655 * Helper class to handle the forwarding of native events to the appropriate listeners 656 */ 657 private class NativeEventHandler extends Handler 658 { 659 private Visualizer mVisualizer; 660 661 public NativeEventHandler(Visualizer v, Looper looper) { 662 super(looper); 663 mVisualizer = v; 664 } 665 666 private void handleCaptureMessage(Message msg) { 667 OnDataCaptureListener l = null; 668 synchronized (mListenerLock) { 669 l = mVisualizer.mCaptureListener; 670 } 671 672 if (l != null) { 673 byte[] data = (byte[])msg.obj; 674 int samplingRate = msg.arg1; 675 676 switch(msg.what) { 677 case NATIVE_EVENT_PCM_CAPTURE: 678 l.onWaveFormDataCapture(mVisualizer, data, samplingRate); 679 break; 680 case NATIVE_EVENT_FFT_CAPTURE: 681 l.onFftDataCapture(mVisualizer, data, samplingRate); 682 break; 683 default: 684 Log.e(TAG,"Unknown native event in handleCaptureMessge: "+msg.what); 685 break; 686 } 687 } 688 } 689 690 private void handleServerDiedMessage(Message msg) { 691 OnServerDiedListener l = null; 692 synchronized (mListenerLock) { 693 l = mVisualizer.mServerDiedListener; 694 } 695 696 if (l != null) 697 l.onServerDied(); 698 } 699 700 @Override 701 public void handleMessage(Message msg) { 702 if (mVisualizer == null) { 703 return; 704 } 705 706 switch(msg.what) { 707 case NATIVE_EVENT_PCM_CAPTURE: 708 case NATIVE_EVENT_FFT_CAPTURE: 709 handleCaptureMessage(msg); 710 break; 711 case NATIVE_EVENT_SERVER_DIED: 712 handleServerDiedMessage(msg); 713 break; 714 default: 715 Log.e(TAG,"Unknown native event: "+msg.what); 716 break; 717 } 718 } 719 } 720 721 //--------------------------------------------------------- 722 // Interface definitions 723 //-------------------- 724 725 private static native final void native_init(); 726 727 private native final int native_setup(Object audioeffect_this, 728 int audioSession, 729 int[] id); 730 731 private native final void native_finalize(); 732 733 private native final void native_release(); 734 735 private native final int native_setEnabled(boolean enabled); 736 737 private native final boolean native_getEnabled(); 738 739 private native final int native_setCaptureSize(int size); 740 741 private native final int native_getCaptureSize(); 742 743 private native final int native_setScalingMode(int mode); 744 745 private native final int native_getScalingMode(); 746 747 private native final int native_setMeasurementMode(int mode); 748 749 private native final int native_getMeasurementMode(); 750 751 private native final int native_getSamplingRate(); 752 753 private native final int native_getWaveForm(byte[] waveform); 754 755 private native final int native_getFft(byte[] fft); 756 757 private native final int native_getPeakRms(MeasurementPeakRms measurement); 758 759 private native final int native_setPeriodicCapture(int rate, boolean waveForm, boolean fft); 760 761 //--------------------------------------------------------- 762 // Java methods called from the native side 763 //-------------------- 764 @SuppressWarnings("unused") 765 private static void postEventFromNative(Object effect_ref, 766 int what, int arg1, int arg2, Object obj) { 767 Visualizer visu = (Visualizer)((WeakReference)effect_ref).get(); 768 if (visu == null) { 769 return; 770 } 771 772 if (visu.mNativeEventHandler != null) { 773 Message m = visu.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); 774 visu.mNativeEventHandler.sendMessage(m); 775 } 776 777 } 778 779} 780 781