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.util.Log; 20 import java.lang.ref.WeakReference; 21 import java.io.IOException; 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"); 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 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 * Sets the combination of measurement modes to be performed by this audio effect. 372 * @param mode a mask of the measurements to perform. The valid values are 373 * {@link #MEASUREMENT_MODE_NONE} (to cancel any measurement) 374 * or {@link #MEASUREMENT_MODE_PEAK_RMS}. 375 * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE} in case of failure. 376 * @throws IllegalStateException 377 */ 378 public int setMeasurementMode(int mode) 379 throws IllegalStateException { 380 synchronized (mStateLock) { 381 if (mState == STATE_UNINITIALIZED) { 382 throw(new IllegalStateException("setMeasurementMode() called in wrong state: " 383 + mState)); 384 } 385 return native_setMeasurementMode(mode); 386 } 387 } 388 389 /** 390 * Returns the current measurement modes performed by this audio effect 391 * @return the mask of the measurements, 392 * {@link #MEASUREMENT_MODE_NONE} (when no measurements are performed) 393 * or {@link #MEASUREMENT_MODE_PEAK_RMS}. 394 * @throws IllegalStateException 395 */ 396 public int getMeasurementMode() 397 throws IllegalStateException { 398 synchronized (mStateLock) { 399 if (mState == STATE_UNINITIALIZED) { 400 throw(new IllegalStateException("getMeasurementMode() called in wrong state: " 401 + mState)); 402 } 403 return native_getMeasurementMode(); 404 } 405 } 406 407 /** 408 * Returns the sampling rate of the captured audio. 409 * @return the sampling rate in milliHertz. 410 */ 411 public int getSamplingRate() 412 throws IllegalStateException { 413 synchronized (mStateLock) { 414 if (mState == STATE_UNINITIALIZED) { 415 throw(new IllegalStateException("getSamplingRate() called in wrong state: "+mState)); 416 } 417 return native_getSamplingRate(); 418 } 419 } 420 421 /** 422 * Returns a waveform capture of currently playing audio content. The capture consists in 423 * a number of consecutive 8-bit (unsigned) mono PCM samples equal to the capture size returned 424 * by {@link #getCaptureSize()}. 425 * <p>This method must be called when the Visualizer is enabled. 426 * @param waveform array of bytes where the waveform should be returned 427 * @return {@link #SUCCESS} in case of success, 428 * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} 429 * in case of failure. 430 * @throws IllegalStateException 431 */ 432 public int getWaveForm(byte[] waveform) 433 throws IllegalStateException { 434 synchronized (mStateLock) { 435 if (mState != STATE_ENABLED) { 436 throw(new IllegalStateException("getWaveForm() called in wrong state: "+mState)); 437 } 438 return native_getWaveForm(waveform); 439 } 440 } 441 /** 442 * Returns a frequency capture of currently playing audio content. 443 * <p>This method must be called when the Visualizer is enabled. 444 * <p>The capture is an 8-bit magnitude FFT, the frequency range covered being 0 (DC) to half of 445 * the sampling rate returned by {@link #getSamplingRate()}. The capture returns the real and 446 * imaginary parts of a number of frequency points equal to half of the capture size plus one. 447 * <p>Note: only the real part is returned for the first point (DC) and the last point 448 * (sampling frequency / 2). 449 * <p>The layout in the returned byte array is as follows: 450 * <ul> 451 * <li> n is the capture size returned by getCaptureSize()</li> 452 * <li> Rfk, Ifk are respectively the real and imaginary parts of the kth frequency 453 * component</li> 454 * <li> If Fs is the sampling frequency retuned by getSamplingRate() the kth frequency is: 455 * (k*Fs)/(n/2) </li> 456 * </ul> 457 * <table border="0" cellspacing="0" cellpadding="0"> 458 * <tr><td>Index </p></td> 459 * <td>0 </p></td> 460 * <td>1 </p></td> 461 * <td>2 </p></td> 462 * <td>3 </p></td> 463 * <td>4 </p></td> 464 * <td>5 </p></td> 465 * <td>... </p></td> 466 * <td>n - 2 </p></td> 467 * <td>n - 1 </p></td></tr> 468 * <tr><td>Data </p></td> 469 * <td>Rf0 </p></td> 470 * <td>Rf(n/2) </p></td> 471 * <td>Rf1 </p></td> 472 * <td>If1 </p></td> 473 * <td>Rf2 </p></td> 474 * <td>If2 </p></td> 475 * <td>... </p></td> 476 * <td>Rf(n-1)/2 </p></td> 477 * <td>If(n-1)/2 </p></td></tr> 478 * </table> 479 * @param fft array of bytes where the FFT should be returned 480 * @return {@link #SUCCESS} in case of success, 481 * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} 482 * in case of failure. 483 * @throws IllegalStateException 484 */ 485 public int getFft(byte[] fft) 486 throws IllegalStateException { 487 synchronized (mStateLock) { 488 if (mState != STATE_ENABLED) { 489 throw(new IllegalStateException("getFft() called in wrong state: "+mState)); 490 } 491 return native_getFft(fft); 492 } 493 } 494 495 /** 496 * A class to store peak and RMS values. 497 * Peak and RMS are expressed in mB, as described in the 498 * {@link Visualizer#MEASUREMENT_MODE_PEAK_RMS} measurement mode. 499 */ 500 public static final class MeasurementPeakRms { 501 /** 502 * The peak value in mB. 503 */ 504 public int mPeak; 505 /** 506 * The RMS value in mB. 507 */ 508 public int mRms; 509 } 510 511 /** 512 * Retrieves the latest peak and RMS measurement. 513 * Sets the peak and RMS fields of the supplied {@link Visualizer.MeasurementPeakRms} to the 514 * latest measured values. 515 * @param measurement a non-null {@link Visualizer.MeasurementPeakRms} instance to store 516 * the measurement values. 517 * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE}, 518 * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} 519 * in case of failure. 520 */ 521 public int getMeasurementPeakRms(MeasurementPeakRms measurement) { 522 if (measurement == null) { 523 Log.e(TAG, "Cannot store measurements in a null object"); 524 return ERROR_BAD_VALUE; 525 } 526 synchronized (mStateLock) { 527 if (mState != STATE_ENABLED) { 528 throw (new IllegalStateException("getMeasurementPeakRms() called in wrong state: " 529 + mState)); 530 } 531 return native_getPeakRms(measurement); 532 } 533 } 534 535 //--------------------------------------------------------- 536 // Interface definitions 537 //-------------------- 538 /** 539 * The OnDataCaptureListener interface defines methods called by the Visualizer to periodically 540 * update the audio visualization capture. 541 * The client application can implement this interface and register the listener with the 542 * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method. 543 */ 544 public interface OnDataCaptureListener { 545 /** 546 * Method called when a new waveform capture is available. 547 * <p>Data in the waveform buffer is valid only within the scope of the callback. 548 * Applications which needs access to the waveform data after returning from the callback 549 * should make a copy of the data instead of holding a reference. 550 * @param visualizer Visualizer object on which the listener is registered. 551 * @param waveform array of bytes containing the waveform representation. 552 * @param samplingRate sampling rate of the audio visualized. 553 */ 554 void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate); 555 556 /** 557 * Method called when a new frequency capture is available. 558 * <p>Data in the fft buffer is valid only within the scope of the callback. 559 * Applications which needs access to the fft data after returning from the callback 560 * should make a copy of the data instead of holding a reference. 561 * @param visualizer Visualizer object on which the listener is registered. 562 * @param fft array of bytes containing the frequency representation. 563 * @param samplingRate sampling rate of the audio visualized. 564 */ 565 void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate); 566 } 567 568 /** 569 * Registers an OnDataCaptureListener interface and specifies the rate at which the capture 570 * should be updated as well as the type of capture requested. 571 * <p>Call this method with a null listener to stop receiving the capture updates. 572 * @param listener OnDataCaptureListener registered 573 * @param rate rate in milliHertz at which the capture should be updated 574 * @param waveform true if a waveform capture is requested: the onWaveFormDataCapture() 575 * method will be called on the OnDataCaptureListener interface. 576 * @param fft true if a frequency capture is requested: the onFftDataCapture() method will be 577 * called on the OnDataCaptureListener interface. 578 * @return {@link #SUCCESS} in case of success, 579 * {@link #ERROR_NO_INIT} or {@link #ERROR_BAD_VALUE} in case of failure. 580 */ 581 public int setDataCaptureListener(OnDataCaptureListener listener, 582 int rate, boolean waveform, boolean fft) { 583 synchronized (mListenerLock) { 584 mCaptureListener = listener; 585 } 586 if (listener == null) { 587 // make sure capture callback is stopped in native code 588 waveform = false; 589 fft = false; 590 } 591 int status = native_setPeriodicCapture(rate, waveform, fft); 592 if (status == SUCCESS) { 593 if ((listener != null) && (mNativeEventHandler == null)) { 594 Looper looper; 595 if ((looper = Looper.myLooper()) != null) { 596 mNativeEventHandler = new NativeEventHandler(this, looper); 597 } else if ((looper = Looper.getMainLooper()) != null) { 598 mNativeEventHandler = new NativeEventHandler(this, looper); 599 } else { 600 mNativeEventHandler = null; 601 status = ERROR_NO_INIT; 602 } 603 } 604 } 605 return status; 606 } 607 608 /** 609 * @hide 610 * 611 * The OnServerDiedListener interface defines a method called by the Visualizer to indicate that 612 * the connection to the native media server has been broken and that the Visualizer object will 613 * need to be released and re-created. 614 * The client application can implement this interface and register the listener with the 615 * {@link #setServerDiedListener(OnServerDiedListener)} method. 616 */ 617 public interface OnServerDiedListener { 618 /** 619 * @hide 620 * 621 * Method called when the native media server has died. 622 * <p>If the native media server encounters a fatal error and needs to restart, the binder 623 * connection from the {@link #Visualizer} to the media server will be broken. Data capture 624 * callbacks will stop happening, and client initiated calls to the {@link #Visualizer} 625 * instance will fail with the error code {@link #DEAD_OBJECT}. To restore functionality, 626 * clients should {@link #release()} their old visualizer and create a new instance. 627 */ 628 void onServerDied(); 629 } 630 631 /** 632 * @hide 633 * 634 * Registers an OnServerDiedListener interface. 635 * <p>Call this method with a null listener to stop receiving server death notifications. 636 * @return {@link #SUCCESS} in case of success, 637 */ 638 public int setServerDiedListener(OnServerDiedListener listener) { 639 synchronized (mListenerLock) { 640 mServerDiedListener = listener; 641 } 642 return SUCCESS; 643 } 644 645 /** 646 * Helper class to handle the forwarding of native events to the appropriate listeners 647 */ 648 private class NativeEventHandler extends Handler 649 { 650 private Visualizer mVisualizer; 651 652 public NativeEventHandler(Visualizer v, Looper looper) { 653 super(looper); 654 mVisualizer = v; 655 } 656 657 private void handleCaptureMessage(Message msg) { 658 OnDataCaptureListener l = null; 659 synchronized (mListenerLock) { 660 l = mVisualizer.mCaptureListener; 661 } 662 663 if (l != null) { 664 byte[] data = (byte[])msg.obj; 665 int samplingRate = msg.arg1; 666 667 switch(msg.what) { 668 case NATIVE_EVENT_PCM_CAPTURE: 669 l.onWaveFormDataCapture(mVisualizer, data, samplingRate); 670 break; 671 case NATIVE_EVENT_FFT_CAPTURE: 672 l.onFftDataCapture(mVisualizer, data, samplingRate); 673 break; 674 default: 675 Log.e(TAG,"Unknown native event in handleCaptureMessge: "+msg.what); 676 break; 677 } 678 } 679 } 680 681 private void handleServerDiedMessage(Message msg) { 682 OnServerDiedListener l = null; 683 synchronized (mListenerLock) { 684 l = mVisualizer.mServerDiedListener; 685 } 686 687 if (l != null) 688 l.onServerDied(); 689 } 690 691 @Override 692 public void handleMessage(Message msg) { 693 if (mVisualizer == null) { 694 return; 695 } 696 697 switch(msg.what) { 698 case NATIVE_EVENT_PCM_CAPTURE: 699 case NATIVE_EVENT_FFT_CAPTURE: 700 handleCaptureMessage(msg); 701 break; 702 case NATIVE_EVENT_SERVER_DIED: 703 handleServerDiedMessage(msg); 704 break; 705 default: 706 Log.e(TAG,"Unknown native event: "+msg.what); 707 break; 708 } 709 } 710 } 711 712 //--------------------------------------------------------- 713 // Interface definitions 714 //-------------------- 715 716 private static native final void native_init(); 717 718 private native final int native_setup(Object audioeffect_this, 719 int audioSession, 720 int[] id); 721 722 private native final void native_finalize(); 723 724 private native final void native_release(); 725 726 private native final int native_setEnabled(boolean enabled); 727 728 private native final boolean native_getEnabled(); 729 730 private native final int native_setCaptureSize(int size); 731 732 private native final int native_getCaptureSize(); 733 734 private native final int native_setScalingMode(int mode); 735 736 private native final int native_getScalingMode(); 737 738 private native final int native_setMeasurementMode(int mode); 739 740 private native final int native_getMeasurementMode(); 741 742 private native final int native_getSamplingRate(); 743 744 private native final int native_getWaveForm(byte[] waveform); 745 746 private native final int native_getFft(byte[] fft); 747 748 private native final int native_getPeakRms(MeasurementPeakRms measurement); 749 750 private native final int native_setPeriodicCapture(int rate, boolean waveForm, boolean fft); 751 752 //--------------------------------------------------------- 753 // Java methods called from the native side 754 //-------------------- 755 @SuppressWarnings("unused") 756 private static void postEventFromNative(Object effect_ref, 757 int what, int arg1, int arg2, Object obj) { 758 Visualizer visu = (Visualizer)((WeakReference)effect_ref).get(); 759 if (visu == null) { 760 return; 761 } 762 763 if (visu.mNativeEventHandler != null) { 764 Message m = visu.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); 765 visu.mNativeEventHandler.sendMessage(m); 766 } 767 768 } 769 770 } 771 772