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. Note that the size of the FFT 47 * is half of the specified capture size but both sides of the spectrum are returned yielding in a 48 * number of bytes equal to the capture size. The capture size must be a power of 2 in the range 49 * returned by {@link #getCaptureSizeRange()}. 50 * <p>In addition to the polling capture mode described above with {@link #getWaveForm(byte[])} and 51 * {@link #getFft(byte[])} methods, a callback mode is also available by installing a listener by 52 * use of the {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method. 53 * The rate at which the listener capture method is called as well as the type of data returned is 54 * specified. 55 * <p>Before capturing data, the Visualizer must be enabled by calling the 56 * {@link #setEnabled(boolean)} method. 57 * When data capture is not needed any more, the Visualizer should be disabled. 58 * <p>It is good practice to call the {@link #release()} method when the Visualizer is not used 59 * anymore to free up native resources associated to the Visualizer instance. 60 * <p>Creating a Visualizer on the output mix (audio session 0) requires permission 61 * {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS} 62 */ 63 64 public class Visualizer { 65 66 static { 67 System.loadLibrary("audioeffect_jni"); 68 native_init(); 69 } 70 71 private final static String TAG = "Visualizer-JAVA"; 72 73 /** 74 * State of a Visualizer object that was not successfully initialized upon creation 75 */ 76 public static final int STATE_UNINITIALIZED = 0; 77 /** 78 * State of a Visualizer object that is ready to be used. 79 */ 80 public static final int STATE_INITIALIZED = 1; 81 /** 82 * State of a Visualizer object that is active. 83 */ 84 public static final int STATE_ENABLED = 2; 85 86 // to keep in sync with frameworks/base/media/jni/audioeffect/android_media_Visualizer.cpp 87 private static final int NATIVE_EVENT_PCM_CAPTURE = 0; 88 private static final int NATIVE_EVENT_FFT_CAPTURE = 1; 89 90 // Error codes: 91 /** 92 * Successful operation. 93 */ 94 public static final int SUCCESS = 0; 95 /** 96 * Unspecified error. 97 */ 98 public static final int ERROR = -1; 99 /** 100 * Internal opreation status. Not returned by any method. 101 */ 102 public static final int ALREADY_EXISTS = -2; 103 /** 104 * Operation failed due to bad object initialization. 105 */ 106 public static final int ERROR_NO_INIT = -3; 107 /** 108 * Operation failed due to bad parameter value. 109 */ 110 public static final int ERROR_BAD_VALUE = -4; 111 /** 112 * Operation failed because it was requested in wrong state. 113 */ 114 public static final int ERROR_INVALID_OPERATION = -5; 115 /** 116 * Operation failed due to lack of memory. 117 */ 118 public static final int ERROR_NO_MEMORY = -6; 119 /** 120 * Operation failed due to dead remote object. 121 */ 122 public static final int ERROR_DEAD_OBJECT = -7; 123 124 //-------------------------------------------------------------------------- 125 // Member variables 126 //-------------------- 127 /** 128 * Indicates the state of the Visualizer instance 129 */ 130 private int mState = STATE_UNINITIALIZED; 131 /** 132 * Lock to synchronize access to mState 133 */ 134 private final Object mStateLock = new Object(); 135 /** 136 * System wide unique Identifier of the visualizer engine used by this Visualizer instance 137 */ 138 private int mId; 139 140 /** 141 * Lock to protect listeners updates against event notifications 142 */ 143 private final Object mListenerLock = new Object(); 144 /** 145 * Handler for events coming from the native code 146 */ 147 private NativeEventHandler mNativeEventHandler = null; 148 /** 149 * PCM and FFT capture listener registered by client 150 */ 151 private OnDataCaptureListener mCaptureListener = null; 152 153 // accessed by native methods 154 private int mNativeVisualizer; 155 private int mJniData; 156 157 //-------------------------------------------------------------------------- 158 // Constructor, Finalize 159 //-------------------- 160 /** 161 * Class constructor. 162 * @param audioSession system wide unique audio session identifier. If audioSession 163 * is not 0, the visualizer will be attached to the MediaPlayer or AudioTrack in the 164 * same audio session. Otherwise, the Visualizer will apply to the output mix. 165 * 166 * @throws java.lang.UnsupportedOperationException 167 * @throws java.lang.RuntimeException 168 */ 169 170 public Visualizer(int audioSession) 171 throws UnsupportedOperationException, RuntimeException { 172 int[] id = new int[1]; 173 174 synchronized (mStateLock) { 175 mState = STATE_UNINITIALIZED; 176 // native initialization 177 int result = native_setup(new WeakReference<Visualizer>(this), audioSession, id); 178 if (result != SUCCESS && result != ALREADY_EXISTS) { 179 Log.e(TAG, "Error code "+result+" when initializing Visualizer."); 180 switch (result) { 181 case ERROR_INVALID_OPERATION: 182 throw (new UnsupportedOperationException("Effect library not loaded")); 183 default: 184 throw (new RuntimeException("Cannot initialize Visualizer engine, error: " 185 +result)); 186 } 187 } 188 mId = id[0]; 189 if (native_getEnabled()) { 190 mState = STATE_ENABLED; 191 } else { 192 mState = STATE_INITIALIZED; 193 } 194 } 195 } 196 197 /** 198 * Releases the native Visualizer resources. It is a good practice to release the 199 * visualization engine when not in use. 200 */ 201 public void release() { 202 synchronized (mStateLock) { 203 native_release(); 204 mState = STATE_UNINITIALIZED; 205 } 206 } 207 208 @Override 209 protected void finalize() { 210 native_finalize(); 211 } 212 213 /** 214 * Enable or disable the visualization engine. 215 * @param enabled requested enable state 216 * @return {@link #SUCCESS} in case of success, 217 * {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} in case of failure. 218 * @throws IllegalStateException 219 */ 220 public int setEnabled(boolean enabled) 221 throws IllegalStateException { 222 synchronized (mStateLock) { 223 if (mState == STATE_UNINITIALIZED) { 224 throw(new IllegalStateException("setEnabled() called in wrong state: "+mState)); 225 } 226 int status = SUCCESS; 227 if ((enabled && (mState == STATE_INITIALIZED)) || 228 (!enabled && (mState == STATE_ENABLED))) { 229 status = native_setEnabled(enabled); 230 if (status == SUCCESS) { 231 mState = enabled ? STATE_ENABLED : STATE_INITIALIZED; 232 } 233 } 234 return status; 235 } 236 } 237 238 /** 239 * Get current activation state of the visualizer. 240 * @return true if the visualizer is active, false otherwise 241 */ 242 public boolean getEnabled() 243 { 244 synchronized (mStateLock) { 245 if (mState == STATE_UNINITIALIZED) { 246 throw(new IllegalStateException("getEnabled() called in wrong state: "+mState)); 247 } 248 return native_getEnabled(); 249 } 250 } 251 252 /** 253 * Returns the capture size range. 254 * @return the mininum capture size is returned in first array element and the maximum in second 255 * array element. 256 */ 257 public static native int[] getCaptureSizeRange(); 258 259 /** 260 * Returns the maximum capture rate for the callback capture method. This is the maximum value 261 * for the rate parameter of the 262 * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method. 263 * @return the maximum capture rate expressed in milliHertz 264 */ 265 public static native int getMaxCaptureRate(); 266 267 /** 268 * Sets the capture size, i.e. the number of bytes returned by {@link #getWaveForm(byte[])} and 269 * {@link #getFft(byte[])} methods. The capture size must be a power of 2 in the range returned 270 * by {@link #getCaptureSizeRange()}. 271 * This method must not be called when the Visualizer is enabled. 272 * @param size requested capture size 273 * @return {@link #SUCCESS} in case of success, 274 * {@link #ERROR_BAD_VALUE} in case of failure. 275 * @throws IllegalStateException 276 */ 277 public int setCaptureSize(int size) 278 throws IllegalStateException { 279 synchronized (mStateLock) { 280 if (mState != STATE_INITIALIZED) { 281 throw(new IllegalStateException("setCaptureSize() called in wrong state: "+mState)); 282 } 283 return native_setCaptureSize(size); 284 } 285 } 286 287 /** 288 * Returns current capture size. 289 * @return the capture size in bytes. 290 */ 291 public int getCaptureSize() 292 throws IllegalStateException { 293 synchronized (mStateLock) { 294 if (mState == STATE_UNINITIALIZED) { 295 throw(new IllegalStateException("getCaptureSize() called in wrong state: "+mState)); 296 } 297 return native_getCaptureSize(); 298 } 299 } 300 301 /** 302 * Returns the sampling rate of the captured audio. 303 * @return the sampling rate in milliHertz. 304 */ 305 public int getSamplingRate() 306 throws IllegalStateException { 307 synchronized (mStateLock) { 308 if (mState == STATE_UNINITIALIZED) { 309 throw(new IllegalStateException("getSamplingRate() called in wrong state: "+mState)); 310 } 311 return native_getSamplingRate(); 312 } 313 } 314 315 /** 316 * Returns a waveform capture of currently playing audio content. The capture consists in 317 * a number of consecutive 8-bit (unsigned) mono PCM samples equal to the capture size returned 318 * by {@link #getCaptureSize()}. 319 * <p>This method must be called when the Visualizer is enabled. 320 * @param waveform array of bytes where the waveform should be returned 321 * @return {@link #SUCCESS} in case of success, 322 * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} 323 * in case of failure. 324 * @throws IllegalStateException 325 */ 326 public int getWaveForm(byte[] waveform) 327 throws IllegalStateException { 328 synchronized (mStateLock) { 329 if (mState != STATE_ENABLED) { 330 throw(new IllegalStateException("getWaveForm() called in wrong state: "+mState)); 331 } 332 return native_getWaveForm(waveform); 333 } 334 } 335 /** 336 * Returns a frequency capture of currently playing audio content. The capture is a 8-bit 337 * magnitude FFT. Note that the size of the FFT is half of the specified capture size but both 338 * sides of the spectrum are returned yielding in a number of bytes equal to the capture size. 339 * {@see #getCaptureSize()}. 340 * <p>This method must be called when the Visualizer is enabled. 341 * @param fft array of bytes where the FFT should be returned 342 * @return {@link #SUCCESS} in case of success, 343 * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or {@link #ERROR_DEAD_OBJECT} 344 * in case of failure. 345 * @throws IllegalStateException 346 */ 347 public int getFft(byte[] fft) 348 throws IllegalStateException { 349 synchronized (mStateLock) { 350 if (mState != STATE_ENABLED) { 351 throw(new IllegalStateException("getFft() called in wrong state: "+mState)); 352 } 353 return native_getFft(fft); 354 } 355 } 356 357 //--------------------------------------------------------- 358 // Interface definitions 359 //-------------------- 360 /** 361 * The OnDataCaptureListener interface defines methods called by the Visualizer to periodically 362 * update the audio visualization capture. 363 * The client application can implement this interface and register the listener with the 364 * {@link #setDataCaptureListener(OnDataCaptureListener, int, boolean, boolean)} method. 365 */ 366 public interface OnDataCaptureListener { 367 /** 368 * Method called when a new waveform capture is available. 369 * @param visualizer Visualizer object on which the listener is registered. 370 * @param waveform array of bytes containing the waveform representation. 371 * @param samplingRate sampling rate of the audio visualized. 372 */ 373 void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate); 374 375 /** 376 * Method called when a new frequency capture is available. 377 * @param visualizer Visualizer object on which the listener is registered. 378 * @param fft array of bytes containing the frequency representation. 379 * @param samplingRate sampling rate of the audio visualized. 380 */ 381 void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate); 382 } 383 384 /** 385 * Registers an OnDataCaptureListener interface and specifies the rate at which the capture 386 * should be updated as well as the type of capture requested. 387 * <p>Call this method with a null listener to stop receiving the capture updates. 388 * @param listener OnDataCaptureListener registered 389 * @param rate rate in milliHertz at which the capture should be updated 390 * @param waveform true if a waveform capture is requested: the onWaveFormDataCapture() 391 * method will be called on the OnDataCaptureListener interface. 392 * @param fft true if a frequency capture is requested: the onFftDataCapture() method will be 393 * called on the OnDataCaptureListener interface. 394 * @return {@link #SUCCESS} in case of success, 395 * {@link #ERROR_NO_INIT} or {@link #ERROR_BAD_VALUE} in case of failure. 396 */ 397 public int setDataCaptureListener(OnDataCaptureListener listener, 398 int rate, boolean waveform, boolean fft) { 399 synchronized (mListenerLock) { 400 mCaptureListener = listener; 401 } 402 if (listener == null) { 403 // make sure capture callback is stopped in native code 404 waveform = false; 405 fft = false; 406 } 407 int status = native_setPeriodicCapture(rate, waveform, fft); 408 if (status == SUCCESS) { 409 if ((listener != null) && (mNativeEventHandler == null)) { 410 Looper looper; 411 if ((looper = Looper.myLooper()) != null) { 412 mNativeEventHandler = new NativeEventHandler(this, looper); 413 } else if ((looper = Looper.getMainLooper()) != null) { 414 mNativeEventHandler = new NativeEventHandler(this, looper); 415 } else { 416 mNativeEventHandler = null; 417 status = ERROR_NO_INIT; 418 } 419 } 420 } 421 return status; 422 } 423 424 /** 425 * Helper class to handle the forwarding of native events to the appropriate listeners 426 */ 427 private class NativeEventHandler extends Handler 428 { 429 private Visualizer mVisualizer; 430 431 public NativeEventHandler(Visualizer v, Looper looper) { 432 super(looper); 433 mVisualizer = v; 434 } 435 436 @Override 437 public void handleMessage(Message msg) { 438 if (mVisualizer == null) { 439 return; 440 } 441 OnDataCaptureListener l = null; 442 synchronized (mListenerLock) { 443 l = mVisualizer.mCaptureListener; 444 } 445 446 if (l != null) { 447 byte[] data = (byte[])msg.obj; 448 int samplingRate = msg.arg1; 449 switch(msg.what) { 450 case NATIVE_EVENT_PCM_CAPTURE: 451 l.onWaveFormDataCapture(mVisualizer, data, samplingRate); 452 break; 453 case NATIVE_EVENT_FFT_CAPTURE: 454 l.onFftDataCapture(mVisualizer, data, samplingRate); 455 break; 456 default: 457 Log.e(TAG,"Unknown native event: "+msg.what); 458 break; 459 } 460 } 461 } 462 } 463 464 //--------------------------------------------------------- 465 // Interface definitions 466 //-------------------- 467 468 private static native final void native_init(); 469 470 private native final int native_setup(Object audioeffect_this, 471 int audioSession, 472 int[] id); 473 474 private native final void native_finalize(); 475 476 private native final void native_release(); 477 478 private native final int native_setEnabled(boolean enabled); 479 480 private native final boolean native_getEnabled(); 481 482 private native final int native_setCaptureSize(int size); 483 484 private native final int native_getCaptureSize(); 485 486 private native final int native_getSamplingRate(); 487 488 private native final int native_getWaveForm(byte[] waveform); 489 490 private native final int native_getFft(byte[] fft); 491 492 private native final int native_setPeriodicCapture(int rate, boolean waveForm, boolean fft); 493 494 //--------------------------------------------------------- 495 // Java methods called from the native side 496 //-------------------- 497 @SuppressWarnings("unused") 498 private static void postEventFromNative(Object effect_ref, 499 int what, int arg1, int arg2, Object obj) { 500 Visualizer visu = (Visualizer)((WeakReference)effect_ref).get(); 501 if (visu == null) { 502 return; 503 } 504 505 if (visu.mNativeEventHandler != null) { 506 Message m = visu.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); 507 visu.mNativeEventHandler.sendMessage(m); 508 } 509 510 } 511 512 } 513 514