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