AudioEffect.java revision fa5ecdc4ac6d7a8db2bb9e4a6a60a3189025df30
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.annotation.SdkConstant; 20import android.annotation.SdkConstant.SdkConstantType; 21import android.app.ActivityThread; 22import android.app.Application; 23import android.os.Handler; 24import android.os.Looper; 25import android.os.Message; 26import android.util.Log; 27import java.lang.ref.WeakReference; 28import java.nio.ByteOrder; 29import java.nio.ByteBuffer; 30import java.util.UUID; 31 32/** 33 * AudioEffect is the base class for controlling audio effects provided by the android audio 34 * framework. 35 * <p>Applications should not use the AudioEffect class directly but one of its derived classes to 36 * control specific effects: 37 * <ul> 38 * <li> {@link android.media.audiofx.Equalizer}</li> 39 * <li> {@link android.media.audiofx.Virtualizer}</li> 40 * <li> {@link android.media.audiofx.BassBoost}</li> 41 * <li> {@link android.media.audiofx.PresetReverb}</li> 42 * <li> {@link android.media.audiofx.EnvironmentalReverb}</li> 43 * </ul> 44 * <p>To apply the audio effect to a specific AudioTrack or MediaPlayer instance, 45 * the application must specify the audio session ID of that instance when creating the AudioEffect. 46 * (see {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions). 47 * <p>NOTE: attaching insert effects (equalizer, bass boost, virtualizer) to the global audio output 48 * mix by use of session 0 is deprecated. 49 * <p>Creating an AudioEffect object will create the corresponding effect engine in the audio 50 * framework if no instance of the same effect type exists in the specified audio session. 51 * If one exists, this instance will be used. 52 * <p>The application creating the AudioEffect object (or a derived class) will either receive 53 * control of the effect engine or not depending on the priority parameter. If priority is higher 54 * than the priority used by the current effect engine owner, the control will be transfered to the 55 * new object. Otherwise control will remain with the previous object. In this case, the new 56 * application will be notified of changes in effect engine state or control ownership by the 57 * appropiate listener. 58 */ 59 60public class AudioEffect { 61 static { 62 System.loadLibrary("audioeffect_jni"); 63 native_init(); 64 } 65 66 private final static String TAG = "AudioEffect-JAVA"; 67 68 // effect type UUIDs are taken from hardware/libhardware/include/hardware/audio_effect.h 69 70 /** 71 * The following UUIDs define effect types corresponding to standard audio 72 * effects whose implementation and interface conform to the OpenSL ES 73 * specification. The definitions match the corresponding interface IDs in 74 * OpenSLES_IID.h 75 */ 76 /** 77 * UUID for environmental reverberation effect 78 */ 79 public static final UUID EFFECT_TYPE_ENV_REVERB = UUID 80 .fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e"); 81 /** 82 * UUID for preset reverberation effect 83 */ 84 public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID 85 .fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b"); 86 /** 87 * UUID for equalizer effect 88 */ 89 public static final UUID EFFECT_TYPE_EQUALIZER = UUID 90 .fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b"); 91 /** 92 * UUID for bass boost effect 93 */ 94 public static final UUID EFFECT_TYPE_BASS_BOOST = UUID 95 .fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b"); 96 /** 97 * UUID for virtualizer effect 98 */ 99 public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID 100 .fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b"); 101 102 /** 103 * UUIDs for effect types not covered by OpenSL ES. 104 */ 105 /** 106 * UUID for Automatic Gain Control (AGC) 107 */ 108 public static final UUID EFFECT_TYPE_AGC = UUID 109 .fromString("0a8abfe0-654c-11e0-ba26-0002a5d5c51b"); 110 111 /** 112 * UUID for Acoustic Echo Canceler (AEC) 113 */ 114 public static final UUID EFFECT_TYPE_AEC = UUID 115 .fromString("7b491460-8d4d-11e0-bd61-0002a5d5c51b"); 116 117 /** 118 * UUID for Noise Suppressor (NS) 119 */ 120 public static final UUID EFFECT_TYPE_NS = UUID 121 .fromString("58b4b260-8e06-11e0-aa8e-0002a5d5c51b"); 122 123 /** 124 * UUID for Loudness Enhancer 125 */ 126 public static final UUID EFFECT_TYPE_LOUDNESS_ENHANCER = UUID 127 .fromString("fe3199be-aed0-413f-87bb-11260eb63cf1"); 128 129 /** 130 * Null effect UUID. Used when the UUID for effect type of 131 * @hide 132 */ 133 public static final UUID EFFECT_TYPE_NULL = UUID 134 .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210"); 135 136 /** 137 * State of an AudioEffect object that was not successfully initialized upon 138 * creation 139 * @hide 140 */ 141 public static final int STATE_UNINITIALIZED = 0; 142 /** 143 * State of an AudioEffect object that is ready to be used. 144 * @hide 145 */ 146 public static final int STATE_INITIALIZED = 1; 147 148 // to keep in sync with 149 // frameworks/base/include/media/AudioEffect.h 150 /** 151 * Event id for engine control ownership change notification. 152 * @hide 153 */ 154 public static final int NATIVE_EVENT_CONTROL_STATUS = 0; 155 /** 156 * Event id for engine state change notification. 157 * @hide 158 */ 159 public static final int NATIVE_EVENT_ENABLED_STATUS = 1; 160 /** 161 * Event id for engine parameter change notification. 162 * @hide 163 */ 164 public static final int NATIVE_EVENT_PARAMETER_CHANGED = 2; 165 166 /** 167 * Successful operation. 168 */ 169 public static final int SUCCESS = 0; 170 /** 171 * Unspecified error. 172 */ 173 public static final int ERROR = -1; 174 /** 175 * Internal operation status. Not returned by any method. 176 */ 177 public static final int ALREADY_EXISTS = -2; 178 /** 179 * Operation failed due to bad object initialization. 180 */ 181 public static final int ERROR_NO_INIT = -3; 182 /** 183 * Operation failed due to bad parameter value. 184 */ 185 public static final int ERROR_BAD_VALUE = -4; 186 /** 187 * Operation failed because it was requested in wrong state. 188 */ 189 public static final int ERROR_INVALID_OPERATION = -5; 190 /** 191 * Operation failed due to lack of memory. 192 */ 193 public static final int ERROR_NO_MEMORY = -6; 194 /** 195 * Operation failed due to dead remote object. 196 */ 197 public static final int ERROR_DEAD_OBJECT = -7; 198 199 /** 200 * The effect descriptor contains information on a particular effect implemented in the 201 * audio framework:<br> 202 * <ul> 203 * <li>type: UUID identifying the effect type. May be one of: 204 * {@link AudioEffect#EFFECT_TYPE_AEC}, {@link AudioEffect#EFFECT_TYPE_AGC}, 205 * {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB}, 206 * {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS}, 207 * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}, {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}. 208 * </li> 209 * <li>uuid: UUID for this particular implementation</li> 210 * <li>connectMode: {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}</li> 211 * <li>name: human readable effect name</li> 212 * <li>implementor: human readable effect implementor name</li> 213 * </ul> 214 * The method {@link #queryEffects()} returns an array of Descriptors to facilitate effects 215 * enumeration. 216 */ 217 public static class Descriptor { 218 219 public Descriptor() { 220 } 221 222 /** 223 * @param type UUID identifying the effect type. May be one of: 224 * {@link AudioEffect#EFFECT_TYPE_AEC}, {@link AudioEffect#EFFECT_TYPE_AGC}, 225 * {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB}, 226 * {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS}, 227 * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}, 228 * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}. 229 * @param uuid UUID for this particular implementation 230 * @param connectMode {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY} 231 * @param name human readable effect name 232 * @param implementor human readable effect implementor name 233 * 234 */ 235 public Descriptor(String type, String uuid, String connectMode, 236 String name, String implementor) { 237 this.type = UUID.fromString(type); 238 this.uuid = UUID.fromString(uuid); 239 this.connectMode = connectMode; 240 this.name = name; 241 this.implementor = implementor; 242 } 243 244 /** 245 * Indicates the generic type of the effect (Equalizer, Bass boost ...). 246 * One of {@link AudioEffect#EFFECT_TYPE_AEC}, 247 * {@link AudioEffect#EFFECT_TYPE_AGC}, {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, 248 * {@link AudioEffect#EFFECT_TYPE_ENV_REVERB}, {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, 249 * {@link AudioEffect#EFFECT_TYPE_NS}, {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB} 250 * or {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}.<br> 251 * For reverberation, bass boost, EQ and virtualizer, the UUID 252 * corresponds to the OpenSL ES Interface ID. 253 */ 254 public UUID type; 255 /** 256 * Indicates the particular implementation of the effect in that type. Several effects 257 * can have the same type but this uuid is unique to a given implementation. 258 */ 259 public UUID uuid; 260 /** 261 * Indicates if the effect is of insert category {@link #EFFECT_INSERT} or auxiliary 262 * category {@link #EFFECT_AUXILIARY}. 263 * Insert effects (typically an {@link Equalizer}) are applied 264 * to the entire audio source and usually not shared by several sources. Auxiliary effects 265 * (typically a reverberator) are applied to part of the signal (wet) and the effect output 266 * is added to the original signal (dry). 267 * Audio pre processing are applied to audio captured on a particular 268 * {@link android.media.AudioRecord}. 269 */ 270 public String connectMode; 271 /** 272 * Human readable effect name 273 */ 274 public String name; 275 /** 276 * Human readable effect implementor name 277 */ 278 public String implementor; 279 }; 280 281 /** 282 * Effect connection mode is insert. Specifying an audio session ID when creating the effect 283 * will insert this effect after all players in the same audio session. 284 */ 285 public static final String EFFECT_INSERT = "Insert"; 286 /** 287 * Effect connection mode is auxiliary. 288 * <p>Auxiliary effects must be created on session 0 (global output mix). In order for a 289 * MediaPlayer or AudioTrack to be fed into this effect, they must be explicitely attached to 290 * this effect and a send level must be specified. 291 * <p>Use the effect ID returned by {@link #getId()} to designate this particular effect when 292 * attaching it to the MediaPlayer or AudioTrack. 293 */ 294 public static final String EFFECT_AUXILIARY = "Auxiliary"; 295 /** 296 * Effect connection mode is pre processing. 297 * The audio pre processing effects are attached to an audio input (AudioRecord). 298 * @hide 299 */ 300 public static final String EFFECT_PRE_PROCESSING = "Pre Processing"; 301 302 // -------------------------------------------------------------------------- 303 // Member variables 304 // -------------------- 305 /** 306 * Indicates the state of the AudioEffect instance 307 */ 308 private int mState = STATE_UNINITIALIZED; 309 /** 310 * Lock to synchronize access to mState 311 */ 312 private final Object mStateLock = new Object(); 313 /** 314 * System wide unique effect ID 315 */ 316 private int mId; 317 318 // accessed by native methods 319 private long mNativeAudioEffect; 320 private long mJniData; 321 322 /** 323 * Effect descriptor 324 */ 325 private Descriptor mDescriptor; 326 327 /** 328 * Listener for effect engine state change notifications. 329 * 330 * @see #setEnableStatusListener(OnEnableStatusChangeListener) 331 */ 332 private OnEnableStatusChangeListener mEnableStatusChangeListener = null; 333 /** 334 * Listener for effect engine control ownership change notifications. 335 * 336 * @see #setControlStatusListener(OnControlStatusChangeListener) 337 */ 338 private OnControlStatusChangeListener mControlChangeStatusListener = null; 339 /** 340 * Listener for effect engine control ownership change notifications. 341 * 342 * @see #setParameterListener(OnParameterChangeListener) 343 */ 344 private OnParameterChangeListener mParameterChangeListener = null; 345 /** 346 * Lock to protect listeners updates against event notifications 347 * @hide 348 */ 349 public final Object mListenerLock = new Object(); 350 /** 351 * Handler for events coming from the native code 352 * @hide 353 */ 354 public NativeEventHandler mNativeEventHandler = null; 355 356 // -------------------------------------------------------------------------- 357 // Constructor, Finalize 358 // -------------------- 359 /** 360 * Class constructor. 361 * 362 * @param type type of effect engine created. See {@link #EFFECT_TYPE_ENV_REVERB}, 363 * {@link #EFFECT_TYPE_EQUALIZER} ... Types corresponding to 364 * built-in effects are defined by AudioEffect class. Other types 365 * can be specified provided they correspond an existing OpenSL 366 * ES interface ID and the corresponsing effect is available on 367 * the platform. If an unspecified effect type is requested, the 368 * constructor with throw the IllegalArgumentException. This 369 * parameter can be set to {@link #EFFECT_TYPE_NULL} in which 370 * case only the uuid will be used to select the effect. 371 * @param uuid unique identifier of a particular effect implementation. 372 * Must be specified if the caller wants to use a particular 373 * implementation of an effect type. This parameter can be set to 374 * {@link #EFFECT_TYPE_NULL} in which case only the type will 375 * be used to select the effect. 376 * @param priority the priority level requested by the application for 377 * controlling the effect engine. As the same effect engine can 378 * be shared by several applications, this parameter indicates 379 * how much the requesting application needs control of effect 380 * parameters. The normal priority is 0, above normal is a 381 * positive number, below normal a negative number. 382 * @param audioSession system wide unique audio session identifier. 383 * The effect will be attached to the MediaPlayer or AudioTrack in 384 * the same audio session. 385 * 386 * @throws java.lang.IllegalArgumentException 387 * @throws java.lang.UnsupportedOperationException 388 * @throws java.lang.RuntimeException 389 * @hide 390 */ 391 392 public AudioEffect(UUID type, UUID uuid, int priority, int audioSession) 393 throws IllegalArgumentException, UnsupportedOperationException, 394 RuntimeException { 395 int[] id = new int[1]; 396 Descriptor[] desc = new Descriptor[1]; 397 // native initialization 398 int initResult = native_setup(new WeakReference<AudioEffect>(this), 399 type.toString(), uuid.toString(), priority, audioSession, id, 400 desc, getMyOpPackageName()); 401 if (initResult != SUCCESS && initResult != ALREADY_EXISTS) { 402 Log.e(TAG, "Error code " + initResult 403 + " when initializing AudioEffect."); 404 switch (initResult) { 405 case ERROR_BAD_VALUE: 406 throw (new IllegalArgumentException("Effect type: " + type 407 + " not supported.")); 408 case ERROR_INVALID_OPERATION: 409 throw (new UnsupportedOperationException( 410 "Effect library not loaded")); 411 default: 412 throw (new RuntimeException( 413 "Cannot initialize effect engine for type: " + type 414 + " Error: " + initResult)); 415 } 416 } 417 mId = id[0]; 418 mDescriptor = desc[0]; 419 synchronized (mStateLock) { 420 mState = STATE_INITIALIZED; 421 } 422 } 423 424 /** 425 * Releases the native AudioEffect resources. It is a good practice to 426 * release the effect engine when not in use as control can be returned to 427 * other applications or the native resources released. 428 */ 429 public void release() { 430 synchronized (mStateLock) { 431 native_release(); 432 mState = STATE_UNINITIALIZED; 433 } 434 } 435 436 @Override 437 protected void finalize() { 438 native_finalize(); 439 } 440 441 /** 442 * Get the effect descriptor. 443 * 444 * @see android.media.audiofx.AudioEffect.Descriptor 445 * @throws IllegalStateException 446 */ 447 public Descriptor getDescriptor() throws IllegalStateException { 448 checkState("getDescriptor()"); 449 return mDescriptor; 450 } 451 452 // -------------------------------------------------------------------------- 453 // Effects Enumeration 454 // -------------------- 455 456 /** 457 * Query all effects available on the platform. Returns an array of 458 * {@link android.media.audiofx.AudioEffect.Descriptor} objects 459 * 460 * @throws IllegalStateException 461 */ 462 463 static public Descriptor[] queryEffects() { 464 return (Descriptor[]) native_query_effects(); 465 } 466 467 /** 468 * Query all audio pre-processing effects applied to the AudioRecord with the supplied 469 * audio session ID. Returns an array of {@link android.media.audiofx.AudioEffect.Descriptor} 470 * objects. 471 * @param audioSession system wide unique audio session identifier. 472 * @throws IllegalStateException 473 * @hide 474 */ 475 476 static public Descriptor[] queryPreProcessings(int audioSession) { 477 return (Descriptor[]) native_query_pre_processing(audioSession); 478 } 479 480 /** 481 * Checks if the device implements the specified effect type. 482 * @param type the requested effect type. 483 * @return true if the device implements the specified effect type, false otherwise. 484 * @hide 485 */ 486 public static boolean isEffectTypeAvailable(UUID type) { 487 AudioEffect.Descriptor[] desc = AudioEffect.queryEffects(); 488 if (desc == null) { 489 return false; 490 } 491 492 for (int i = 0; i < desc.length; i++) { 493 if (desc[i].type.equals(type)) { 494 return true; 495 } 496 } 497 return false; 498 } 499 500 // -------------------------------------------------------------------------- 501 // Control methods 502 // -------------------- 503 504 /** 505 * Enable or disable the effect. 506 * Creating an audio effect does not automatically apply this effect on the audio source. It 507 * creates the resources necessary to process this effect but the audio signal is still bypassed 508 * through the effect engine. Calling this method will make that the effect is actually applied 509 * or not to the audio content being played in the corresponding audio session. 510 * 511 * @param enabled the requested enable state 512 * @return {@link #SUCCESS} in case of success, {@link #ERROR_INVALID_OPERATION} 513 * or {@link #ERROR_DEAD_OBJECT} in case of failure. 514 * @throws IllegalStateException 515 */ 516 public int setEnabled(boolean enabled) throws IllegalStateException { 517 checkState("setEnabled()"); 518 return native_setEnabled(enabled); 519 } 520 521 /** 522 * Set effect parameter. The setParameter method is provided in several 523 * forms addressing most common parameter formats. This form is the most 524 * generic one where the parameter and its value are both specified as an 525 * array of bytes. The parameter and value type and length are therefore 526 * totally free. For standard effect defined by OpenSL ES, the parameter 527 * format and values must match the definitions in the corresponding OpenSL 528 * ES interface. 529 * 530 * @param param the identifier of the parameter to set 531 * @param value the new value for the specified parameter 532 * @return {@link #SUCCESS} in case of success, {@link #ERROR_BAD_VALUE}, 533 * {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} or 534 * {@link #ERROR_DEAD_OBJECT} in case of failure 535 * @throws IllegalStateException 536 * @hide 537 */ 538 public int setParameter(byte[] param, byte[] value) 539 throws IllegalStateException { 540 checkState("setParameter()"); 541 return native_setParameter(param.length, param, value.length, value); 542 } 543 544 /** 545 * Set effect parameter. The parameter and its value are integers. 546 * 547 * @see #setParameter(byte[], byte[]) 548 * @hide 549 */ 550 public int setParameter(int param, int value) throws IllegalStateException { 551 byte[] p = intToByteArray(param); 552 byte[] v = intToByteArray(value); 553 return setParameter(p, v); 554 } 555 556 /** 557 * Set effect parameter. The parameter is an integer and the value is a 558 * short integer. 559 * 560 * @see #setParameter(byte[], byte[]) 561 * @hide 562 */ 563 public int setParameter(int param, short value) 564 throws IllegalStateException { 565 byte[] p = intToByteArray(param); 566 byte[] v = shortToByteArray(value); 567 return setParameter(p, v); 568 } 569 570 /** 571 * Set effect parameter. The parameter is an integer and the value is an 572 * array of bytes. 573 * 574 * @see #setParameter(byte[], byte[]) 575 * @hide 576 */ 577 public int setParameter(int param, byte[] value) 578 throws IllegalStateException { 579 byte[] p = intToByteArray(param); 580 return setParameter(p, value); 581 } 582 583 /** 584 * Set effect parameter. The parameter is an array of 1 or 2 integers and 585 * the value is also an array of 1 or 2 integers 586 * 587 * @see #setParameter(byte[], byte[]) 588 * @hide 589 */ 590 public int setParameter(int[] param, int[] value) 591 throws IllegalStateException { 592 if (param.length > 2 || value.length > 2) { 593 return ERROR_BAD_VALUE; 594 } 595 byte[] p = intToByteArray(param[0]); 596 if (param.length > 1) { 597 byte[] p2 = intToByteArray(param[1]); 598 p = concatArrays(p, p2); 599 } 600 byte[] v = intToByteArray(value[0]); 601 if (value.length > 1) { 602 byte[] v2 = intToByteArray(value[1]); 603 v = concatArrays(v, v2); 604 } 605 return setParameter(p, v); 606 } 607 608 /** 609 * Set effect parameter. The parameter is an array of 1 or 2 integers and 610 * the value is an array of 1 or 2 short integers 611 * 612 * @see #setParameter(byte[], byte[]) 613 * @hide 614 */ 615 public int setParameter(int[] param, short[] value) 616 throws IllegalStateException { 617 if (param.length > 2 || value.length > 2) { 618 return ERROR_BAD_VALUE; 619 } 620 byte[] p = intToByteArray(param[0]); 621 if (param.length > 1) { 622 byte[] p2 = intToByteArray(param[1]); 623 p = concatArrays(p, p2); 624 } 625 626 byte[] v = shortToByteArray(value[0]); 627 if (value.length > 1) { 628 byte[] v2 = shortToByteArray(value[1]); 629 v = concatArrays(v, v2); 630 } 631 return setParameter(p, v); 632 } 633 634 /** 635 * Set effect parameter. The parameter is an array of 1 or 2 integers and 636 * the value is an array of bytes 637 * 638 * @see #setParameter(byte[], byte[]) 639 * @hide 640 */ 641 public int setParameter(int[] param, byte[] value) 642 throws IllegalStateException { 643 if (param.length > 2) { 644 return ERROR_BAD_VALUE; 645 } 646 byte[] p = intToByteArray(param[0]); 647 if (param.length > 1) { 648 byte[] p2 = intToByteArray(param[1]); 649 p = concatArrays(p, p2); 650 } 651 return setParameter(p, value); 652 } 653 654 /** 655 * Get effect parameter. The getParameter method is provided in several 656 * forms addressing most common parameter formats. This form is the most 657 * generic one where the parameter and its value are both specified as an 658 * array of bytes. The parameter and value type and length are therefore 659 * totally free. 660 * 661 * @param param the identifier of the parameter to set 662 * @param value the new value for the specified parameter 663 * @return the number of meaningful bytes in value array in case of success or 664 * {@link #ERROR_BAD_VALUE}, {@link #ERROR_NO_MEMORY}, {@link #ERROR_INVALID_OPERATION} 665 * or {@link #ERROR_DEAD_OBJECT} in case of failure. 666 * @throws IllegalStateException 667 * @hide 668 */ 669 public int getParameter(byte[] param, byte[] value) 670 throws IllegalStateException { 671 checkState("getParameter()"); 672 return native_getParameter(param.length, param, value.length, value); 673 } 674 675 /** 676 * Get effect parameter. The parameter is an integer and the value is an 677 * array of bytes. 678 * 679 * @see #getParameter(byte[], byte[]) 680 * @hide 681 */ 682 public int getParameter(int param, byte[] value) 683 throws IllegalStateException { 684 byte[] p = intToByteArray(param); 685 686 return getParameter(p, value); 687 } 688 689 /** 690 * Get effect parameter. The parameter is an integer and the value is an 691 * array of 1 or 2 integers 692 * 693 * @see #getParameter(byte[], byte[]) 694 * In case of success, returns the number of meaningful integers in value array. 695 * @hide 696 */ 697 public int getParameter(int param, int[] value) 698 throws IllegalStateException { 699 if (value.length > 2) { 700 return ERROR_BAD_VALUE; 701 } 702 byte[] p = intToByteArray(param); 703 704 byte[] v = new byte[value.length * 4]; 705 706 int status = getParameter(p, v); 707 708 if (status == 4 || status == 8) { 709 value[0] = byteArrayToInt(v); 710 if (status == 8) { 711 value[1] = byteArrayToInt(v, 4); 712 } 713 status /= 4; 714 } else { 715 status = ERROR; 716 } 717 return status; 718 } 719 720 /** 721 * Get effect parameter. The parameter is an integer and the value is an 722 * array of 1 or 2 short integers 723 * 724 * @see #getParameter(byte[], byte[]) 725 * In case of success, returns the number of meaningful short integers in value array. 726 * @hide 727 */ 728 public int getParameter(int param, short[] value) 729 throws IllegalStateException { 730 if (value.length > 2) { 731 return ERROR_BAD_VALUE; 732 } 733 byte[] p = intToByteArray(param); 734 735 byte[] v = new byte[value.length * 2]; 736 737 int status = getParameter(p, v); 738 739 if (status == 2 || status == 4) { 740 value[0] = byteArrayToShort(v); 741 if (status == 4) { 742 value[1] = byteArrayToShort(v, 2); 743 } 744 status /= 2; 745 } else { 746 status = ERROR; 747 } 748 return status; 749 } 750 751 /** 752 * Get effect parameter. The parameter is an array of 1 or 2 integers and 753 * the value is also an array of 1 or 2 integers 754 * 755 * @see #getParameter(byte[], byte[]) 756 * In case of success, the returns the number of meaningful integers in value array. 757 * @hide 758 */ 759 public int getParameter(int[] param, int[] value) 760 throws IllegalStateException { 761 if (param.length > 2 || value.length > 2) { 762 return ERROR_BAD_VALUE; 763 } 764 byte[] p = intToByteArray(param[0]); 765 if (param.length > 1) { 766 byte[] p2 = intToByteArray(param[1]); 767 p = concatArrays(p, p2); 768 } 769 byte[] v = new byte[value.length * 4]; 770 771 int status = getParameter(p, v); 772 773 if (status == 4 || status == 8) { 774 value[0] = byteArrayToInt(v); 775 if (status == 8) { 776 value[1] = byteArrayToInt(v, 4); 777 } 778 status /= 4; 779 } else { 780 status = ERROR; 781 } 782 return status; 783 } 784 785 /** 786 * Get effect parameter. The parameter is an array of 1 or 2 integers and 787 * the value is an array of 1 or 2 short integers 788 * 789 * @see #getParameter(byte[], byte[]) 790 * In case of success, returns the number of meaningful short integers in value array. 791 * @hide 792 */ 793 public int getParameter(int[] param, short[] value) 794 throws IllegalStateException { 795 if (param.length > 2 || value.length > 2) { 796 return ERROR_BAD_VALUE; 797 } 798 byte[] p = intToByteArray(param[0]); 799 if (param.length > 1) { 800 byte[] p2 = intToByteArray(param[1]); 801 p = concatArrays(p, p2); 802 } 803 byte[] v = new byte[value.length * 2]; 804 805 int status = getParameter(p, v); 806 807 if (status == 2 || status == 4) { 808 value[0] = byteArrayToShort(v); 809 if (status == 4) { 810 value[1] = byteArrayToShort(v, 2); 811 } 812 status /= 2; 813 } else { 814 status = ERROR; 815 } 816 return status; 817 } 818 819 /** 820 * Get effect parameter. The parameter is an array of 1 or 2 integers and 821 * the value is an array of bytes 822 * 823 * @see #getParameter(byte[], byte[]) 824 * @hide 825 */ 826 public int getParameter(int[] param, byte[] value) 827 throws IllegalStateException { 828 if (param.length > 2) { 829 return ERROR_BAD_VALUE; 830 } 831 byte[] p = intToByteArray(param[0]); 832 if (param.length > 1) { 833 byte[] p2 = intToByteArray(param[1]); 834 p = concatArrays(p, p2); 835 } 836 837 return getParameter(p, value); 838 } 839 840 /** 841 * Send a command to the effect engine. This method is intended to send 842 * proprietary commands to a particular effect implementation. 843 * In case of success, returns the number of meaningful bytes in reply array. 844 * In case of failure, the returned value is negative and implementation specific. 845 * @hide 846 */ 847 public int command(int cmdCode, byte[] command, byte[] reply) 848 throws IllegalStateException { 849 checkState("command()"); 850 return native_command(cmdCode, command.length, command, reply.length, reply); 851 } 852 853 // -------------------------------------------------------------------------- 854 // Getters 855 // -------------------- 856 857 /** 858 * Returns effect unique identifier. This system wide unique identifier can 859 * be used to attach this effect to a MediaPlayer or an AudioTrack when the 860 * effect is an auxiliary effect (Reverb) 861 * 862 * @return the effect identifier. 863 * @throws IllegalStateException 864 */ 865 public int getId() throws IllegalStateException { 866 checkState("getId()"); 867 return mId; 868 } 869 870 /** 871 * Returns effect enabled state 872 * 873 * @return true if the effect is enabled, false otherwise. 874 * @throws IllegalStateException 875 */ 876 public boolean getEnabled() throws IllegalStateException { 877 checkState("getEnabled()"); 878 return native_getEnabled(); 879 } 880 881 /** 882 * Checks if this AudioEffect object is controlling the effect engine. 883 * 884 * @return true if this instance has control of effect engine, false 885 * otherwise. 886 * @throws IllegalStateException 887 */ 888 public boolean hasControl() throws IllegalStateException { 889 checkState("hasControl()"); 890 return native_hasControl(); 891 } 892 893 // -------------------------------------------------------------------------- 894 // Initialization / configuration 895 // -------------------- 896 /** 897 * Sets the listener AudioEffect notifies when the effect engine is enabled 898 * or disabled. 899 * 900 * @param listener 901 */ 902 public void setEnableStatusListener(OnEnableStatusChangeListener listener) { 903 synchronized (mListenerLock) { 904 mEnableStatusChangeListener = listener; 905 } 906 if ((listener != null) && (mNativeEventHandler == null)) { 907 createNativeEventHandler(); 908 } 909 } 910 911 /** 912 * Sets the listener AudioEffect notifies when the effect engine control is 913 * taken or returned. 914 * 915 * @param listener 916 */ 917 public void setControlStatusListener(OnControlStatusChangeListener listener) { 918 synchronized (mListenerLock) { 919 mControlChangeStatusListener = listener; 920 } 921 if ((listener != null) && (mNativeEventHandler == null)) { 922 createNativeEventHandler(); 923 } 924 } 925 926 /** 927 * Sets the listener AudioEffect notifies when a parameter is changed. 928 * 929 * @param listener 930 * @hide 931 */ 932 public void setParameterListener(OnParameterChangeListener listener) { 933 synchronized (mListenerLock) { 934 mParameterChangeListener = listener; 935 } 936 if ((listener != null) && (mNativeEventHandler == null)) { 937 createNativeEventHandler(); 938 } 939 } 940 941 // Convenience method for the creation of the native event handler 942 // It is called only when a non-null event listener is set. 943 // precondition: 944 // mNativeEventHandler is null 945 private void createNativeEventHandler() { 946 Looper looper; 947 if ((looper = Looper.myLooper()) != null) { 948 mNativeEventHandler = new NativeEventHandler(this, looper); 949 } else if ((looper = Looper.getMainLooper()) != null) { 950 mNativeEventHandler = new NativeEventHandler(this, looper); 951 } else { 952 mNativeEventHandler = null; 953 } 954 } 955 956 // --------------------------------------------------------- 957 // Interface definitions 958 // -------------------- 959 /** 960 * The OnEnableStatusChangeListener interface defines a method called by the AudioEffect 961 * when a the enabled state of the effect engine was changed by the controlling application. 962 */ 963 public interface OnEnableStatusChangeListener { 964 /** 965 * Called on the listener to notify it that the effect engine has been 966 * enabled or disabled. 967 * @param effect the effect on which the interface is registered. 968 * @param enabled new effect state. 969 */ 970 void onEnableStatusChange(AudioEffect effect, boolean enabled); 971 } 972 973 /** 974 * The OnControlStatusChangeListener interface defines a method called by the AudioEffect 975 * when a the control of the effect engine is gained or lost by the application 976 */ 977 public interface OnControlStatusChangeListener { 978 /** 979 * Called on the listener to notify it that the effect engine control 980 * has been taken or returned. 981 * @param effect the effect on which the interface is registered. 982 * @param controlGranted true if the application has been granted control of the effect 983 * engine, false otherwise. 984 */ 985 void onControlStatusChange(AudioEffect effect, boolean controlGranted); 986 } 987 988 /** 989 * The OnParameterChangeListener interface defines a method called by the AudioEffect 990 * when a parameter is changed in the effect engine by the controlling application. 991 * @hide 992 */ 993 public interface OnParameterChangeListener { 994 /** 995 * Called on the listener to notify it that a parameter value has changed. 996 * @param effect the effect on which the interface is registered. 997 * @param status status of the set parameter operation. 998 * @param param ID of the modified parameter. 999 * @param value the new parameter value. 1000 */ 1001 void onParameterChange(AudioEffect effect, int status, byte[] param, 1002 byte[] value); 1003 } 1004 1005 1006 // ------------------------------------------------------------------------- 1007 // Audio Effect Control panel intents 1008 // ------------------------------------------------------------------------- 1009 1010 /** 1011 * Intent to launch an audio effect control panel UI. 1012 * <p>The goal of this intent is to enable separate implementations of music/media player 1013 * applications and audio effect control application or services. 1014 * This will allow platform vendors to offer more advanced control options for standard effects 1015 * or control for platform specific effects. 1016 * <p>The intent carries a number of extras used by the player application to communicate 1017 * necessary pieces of information to the control panel application. 1018 * <p>The calling application must use the 1019 * {@link android.app.Activity#startActivityForResult(Intent, int)} method to launch the 1020 * control panel so that its package name is indicated and used by the control panel 1021 * application to keep track of changes for this particular application. 1022 * <p>The {@link #EXTRA_AUDIO_SESSION} extra will indicate an audio session to which the 1023 * audio effects should be applied. If no audio session is specified, either one of the 1024 * follownig will happen: 1025 * <p>- If an audio session was previously opened by the calling application with 1026 * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intent, the effect changes will 1027 * be applied to that session. 1028 * <p>- If no audio session is opened, the changes will be stored in the package specific 1029 * storage area and applied whenever a new audio session is opened by this application. 1030 * <p>The {@link #EXTRA_CONTENT_TYPE} extra will help the control panel application 1031 * customize both the UI layout and the default audio effect settings if none are already 1032 * stored for the calling application. 1033 */ 1034 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) 1035 public static final String ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL = 1036 "android.media.action.DISPLAY_AUDIO_EFFECT_CONTROL_PANEL"; 1037 1038 /** 1039 * Intent to signal to the effect control application or service that a new audio session 1040 * is opened and requires audio effects to be applied. 1041 * <p>This is different from {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} in that no 1042 * UI should be displayed in this case. Music player applications can broadcast this intent 1043 * before starting playback to make sure that any audio effect settings previously selected 1044 * by the user are applied. 1045 * <p>The effect control application receiving this intent will look for previously stored 1046 * settings for the calling application, create all required audio effects and apply the 1047 * effect settings to the specified audio session. 1048 * <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the 1049 * audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory. 1050 * <p>If no stored settings are found for the calling application, default settings for the 1051 * content type indicated by {@link #EXTRA_CONTENT_TYPE} will be applied. The default settings 1052 * for a given content type are platform specific. 1053 */ 1054 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 1055 public static final String ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION = 1056 "android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION"; 1057 1058 /** 1059 * Intent to signal to the effect control application or service that an audio session 1060 * is closed and that effects should not be applied anymore. 1061 * <p>The effect control application receiving this intent will delete all effects on 1062 * this session and store current settings in package specific storage. 1063 * <p>The calling package name is indicated by the {@link #EXTRA_PACKAGE_NAME} extra and the 1064 * audio session ID by the {@link #EXTRA_AUDIO_SESSION} extra. Both extras are mandatory. 1065 * <p>It is good practice for applications to broadcast this intent when music playback stops 1066 * and/or when exiting to free system resources consumed by audio effect engines. 1067 */ 1068 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 1069 public static final String ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION = 1070 "android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION"; 1071 1072 /** 1073 * Contains the ID of the audio session the effects should be applied to. 1074 * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL}, 1075 * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and 1076 * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents. 1077 * <p>The extra value is of type int and is the audio session ID. 1078 * @see android.media.MediaPlayer#getAudioSessionId() for details on audio sessions. 1079 */ 1080 public static final String EXTRA_AUDIO_SESSION = "android.media.extra.AUDIO_SESSION"; 1081 1082 /** 1083 * Contains the package name of the calling application. 1084 * <p>This extra is for use with {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} and 1085 * {@link #ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION} intents. 1086 * <p>The extra value is a string containing the full package name. 1087 */ 1088 public static final String EXTRA_PACKAGE_NAME = "android.media.extra.PACKAGE_NAME"; 1089 1090 /** 1091 * Indicates which type of content is played by the application. 1092 * <p>This extra is for use with {@link #ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL} and 1093 * {@link #ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION} intents. 1094 * <p>This information is used by the effect control application to customize UI and select 1095 * appropriate default effect settings. The content type is one of the following: 1096 * <ul> 1097 * <li>{@link #CONTENT_TYPE_MUSIC}</li> 1098 * <li>{@link #CONTENT_TYPE_MOVIE}</li> 1099 * <li>{@link #CONTENT_TYPE_GAME}</li> 1100 * <li>{@link #CONTENT_TYPE_VOICE}</li> 1101 * </ul> 1102 * If omitted, the content type defaults to {@link #CONTENT_TYPE_MUSIC}. 1103 */ 1104 public static final String EXTRA_CONTENT_TYPE = "android.media.extra.CONTENT_TYPE"; 1105 1106 /** 1107 * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is music 1108 */ 1109 public static final int CONTENT_TYPE_MUSIC = 0; 1110 /** 1111 * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is video or movie 1112 */ 1113 public static final int CONTENT_TYPE_MOVIE = 1; 1114 /** 1115 * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is game audio 1116 */ 1117 public static final int CONTENT_TYPE_GAME = 2; 1118 /** 1119 * Value for {@link #EXTRA_CONTENT_TYPE} when the type of content played is voice audio 1120 */ 1121 public static final int CONTENT_TYPE_VOICE = 3; 1122 1123 1124 // --------------------------------------------------------- 1125 // Inner classes 1126 // -------------------- 1127 /** 1128 * Helper class to handle the forwarding of native events to the appropriate 1129 * listeners 1130 */ 1131 private class NativeEventHandler extends Handler { 1132 private AudioEffect mAudioEffect; 1133 1134 public NativeEventHandler(AudioEffect ae, Looper looper) { 1135 super(looper); 1136 mAudioEffect = ae; 1137 } 1138 1139 @Override 1140 public void handleMessage(Message msg) { 1141 if (mAudioEffect == null) { 1142 return; 1143 } 1144 switch (msg.what) { 1145 case NATIVE_EVENT_ENABLED_STATUS: 1146 OnEnableStatusChangeListener enableStatusChangeListener = null; 1147 synchronized (mListenerLock) { 1148 enableStatusChangeListener = mAudioEffect.mEnableStatusChangeListener; 1149 } 1150 if (enableStatusChangeListener != null) { 1151 enableStatusChangeListener.onEnableStatusChange( 1152 mAudioEffect, (boolean) (msg.arg1 != 0)); 1153 } 1154 break; 1155 case NATIVE_EVENT_CONTROL_STATUS: 1156 OnControlStatusChangeListener controlStatusChangeListener = null; 1157 synchronized (mListenerLock) { 1158 controlStatusChangeListener = mAudioEffect.mControlChangeStatusListener; 1159 } 1160 if (controlStatusChangeListener != null) { 1161 controlStatusChangeListener.onControlStatusChange( 1162 mAudioEffect, (boolean) (msg.arg1 != 0)); 1163 } 1164 break; 1165 case NATIVE_EVENT_PARAMETER_CHANGED: 1166 OnParameterChangeListener parameterChangeListener = null; 1167 synchronized (mListenerLock) { 1168 parameterChangeListener = mAudioEffect.mParameterChangeListener; 1169 } 1170 if (parameterChangeListener != null) { 1171 // arg1 contains offset of parameter value from start of 1172 // byte array 1173 int vOffset = msg.arg1; 1174 byte[] p = (byte[]) msg.obj; 1175 // See effect_param_t in EffectApi.h for psize and vsize 1176 // fields offsets 1177 int status = byteArrayToInt(p, 0); 1178 int psize = byteArrayToInt(p, 4); 1179 int vsize = byteArrayToInt(p, 8); 1180 byte[] param = new byte[psize]; 1181 byte[] value = new byte[vsize]; 1182 System.arraycopy(p, 12, param, 0, psize); 1183 System.arraycopy(p, vOffset, value, 0, vsize); 1184 1185 parameterChangeListener.onParameterChange(mAudioEffect, 1186 status, param, value); 1187 } 1188 break; 1189 1190 default: 1191 Log.e(TAG, "handleMessage() Unknown event type: " + msg.what); 1192 break; 1193 } 1194 } 1195 } 1196 1197 // --------------------------------------------------------- 1198 // Java methods called from the native side 1199 // -------------------- 1200 @SuppressWarnings("unused") 1201 private static void postEventFromNative(Object effect_ref, int what, 1202 int arg1, int arg2, Object obj) { 1203 AudioEffect effect = (AudioEffect) ((WeakReference) effect_ref).get(); 1204 if (effect == null) { 1205 return; 1206 } 1207 if (effect.mNativeEventHandler != null) { 1208 Message m = effect.mNativeEventHandler.obtainMessage(what, arg1, 1209 arg2, obj); 1210 effect.mNativeEventHandler.sendMessage(m); 1211 } 1212 1213 } 1214 1215 // --------------------------------------------------------- 1216 // Native methods called from the Java side 1217 // -------------------- 1218 1219 private static native final void native_init(); 1220 1221 private native final int native_setup(Object audioeffect_this, String type, 1222 String uuid, int priority, int audioSession, int[] id, Object[] desc, 1223 String opPackageName); 1224 1225 private native final void native_finalize(); 1226 1227 private native final void native_release(); 1228 1229 private native final int native_setEnabled(boolean enabled); 1230 1231 private native final boolean native_getEnabled(); 1232 1233 private native final boolean native_hasControl(); 1234 1235 private native final int native_setParameter(int psize, byte[] param, 1236 int vsize, byte[] value); 1237 1238 private native final int native_getParameter(int psize, byte[] param, 1239 int vsize, byte[] value); 1240 1241 private native final int native_command(int cmdCode, int cmdSize, 1242 byte[] cmdData, int repSize, byte[] repData); 1243 1244 private static native Object[] native_query_effects(); 1245 1246 private static native Object[] native_query_pre_processing(int audioSession); 1247 1248 // --------------------------------------------------------- 1249 // Utility methods 1250 // ------------------ 1251 1252 /** 1253 * @hide 1254 */ 1255 public void checkState(String methodName) throws IllegalStateException { 1256 synchronized (mStateLock) { 1257 if (mState != STATE_INITIALIZED) { 1258 throw (new IllegalStateException(methodName 1259 + " called on uninitialized AudioEffect.")); 1260 } 1261 } 1262 } 1263 1264 /** 1265 * @hide 1266 */ 1267 public void checkStatus(int status) { 1268 if (isError(status)) { 1269 switch (status) { 1270 case AudioEffect.ERROR_BAD_VALUE: 1271 throw (new IllegalArgumentException( 1272 "AudioEffect: bad parameter value")); 1273 case AudioEffect.ERROR_INVALID_OPERATION: 1274 throw (new UnsupportedOperationException( 1275 "AudioEffect: invalid parameter operation")); 1276 default: 1277 throw (new RuntimeException("AudioEffect: set/get parameter error")); 1278 } 1279 } 1280 } 1281 1282 /** 1283 * @hide 1284 */ 1285 public static boolean isError(int status) { 1286 return (status < 0); 1287 } 1288 1289 /** 1290 * @hide 1291 */ 1292 public static int byteArrayToInt(byte[] valueBuf) { 1293 return byteArrayToInt(valueBuf, 0); 1294 1295 } 1296 1297 /** 1298 * @hide 1299 */ 1300 public static int byteArrayToInt(byte[] valueBuf, int offset) { 1301 ByteBuffer converter = ByteBuffer.wrap(valueBuf); 1302 converter.order(ByteOrder.nativeOrder()); 1303 return converter.getInt(offset); 1304 1305 } 1306 1307 /** 1308 * @hide 1309 */ 1310 public static byte[] intToByteArray(int value) { 1311 ByteBuffer converter = ByteBuffer.allocate(4); 1312 converter.order(ByteOrder.nativeOrder()); 1313 converter.putInt(value); 1314 return converter.array(); 1315 } 1316 1317 /** 1318 * @hide 1319 */ 1320 public static short byteArrayToShort(byte[] valueBuf) { 1321 return byteArrayToShort(valueBuf, 0); 1322 } 1323 1324 /** 1325 * @hide 1326 */ 1327 public static short byteArrayToShort(byte[] valueBuf, int offset) { 1328 ByteBuffer converter = ByteBuffer.wrap(valueBuf); 1329 converter.order(ByteOrder.nativeOrder()); 1330 return converter.getShort(offset); 1331 1332 } 1333 1334 /** 1335 * @hide 1336 */ 1337 public static byte[] shortToByteArray(short value) { 1338 ByteBuffer converter = ByteBuffer.allocate(2); 1339 converter.order(ByteOrder.nativeOrder()); 1340 short sValue = (short) value; 1341 converter.putShort(sValue); 1342 return converter.array(); 1343 } 1344 1345 /** 1346 * @hide 1347 */ 1348 public static byte[] concatArrays(byte[]... arrays) { 1349 int len = 0; 1350 for (byte[] a : arrays) { 1351 len += a.length; 1352 } 1353 byte[] b = new byte[len]; 1354 1355 int offs = 0; 1356 for (byte[] a : arrays) { 1357 System.arraycopy(a, 0, b, offs, a.length); 1358 offs += a.length; 1359 } 1360 return b; 1361 } 1362 1363 private static String getMyOpPackageName() { 1364 ActivityThread activityThread = ActivityThread.currentActivityThread(); 1365 if (activityThread != null) { 1366 Application application = activityThread.getApplication(); 1367 if (application != null) { 1368 return application.getOpPackageName(); 1369 } 1370 } 1371 throw new IllegalStateException("Cannot create AudioEffect outside of an app"); 1372 } 1373} 1374