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.annotation.IntDef; 20 import android.media.AudioDeviceInfo; 21 import android.media.AudioFormat; 22 import android.media.audiofx.AudioEffect; 23 import android.util.Log; 24 25 import java.lang.annotation.Retention; 26 import java.lang.annotation.RetentionPolicy; 27 import java.nio.ByteBuffer; 28 import java.nio.ByteOrder; 29 import java.util.StringTokenizer; 30 31 32 /** 33 * An audio virtualizer is a general name for an effect to spatialize audio channels. The exact 34 * behavior of this effect is dependent on the number of audio input channels and the types and 35 * number of audio output channels of the device. For example, in the case of a stereo input and 36 * stereo headphone output, a stereo widening effect is used when this effect is turned on. 37 * <p>An application creates a Virtualizer object to instantiate and control a virtualizer engine 38 * in the audio framework. 39 * <p>The methods, parameter types and units exposed by the Virtualizer implementation are directly 40 * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/) 41 * for the SLVirtualizerItf interface. Please refer to this specification for more details. 42 * <p>To attach the Virtualizer to a particular AudioTrack or MediaPlayer, specify the audio session 43 * ID of this AudioTrack or MediaPlayer when constructing the Virtualizer. 44 * <p>NOTE: attaching a Virtualizer to the global audio output mix by use of session 0 is 45 * deprecated. 46 * <p>See {@link android.media.MediaPlayer#getAudioSessionId()} for details on audio sessions. 47 * <p>See {@link android.media.audiofx.AudioEffect} class for more details on controlling 48 * audio effects. 49 * 50 * @deprecated use the {@link android.media.Spatializer} class to query the capabilities of the 51 * platform with regards to spatialization, a different name for audio channel virtualization, 52 * and the {@link android.media.AudioAttributes.Builder#setSpatializationBehavior(int)} to 53 * characterize how you want your content to be played when spatialization is supported. 54 */ 55 56 public class Virtualizer extends AudioEffect { 57 58 private final static String TAG = "Virtualizer"; 59 private final static boolean DEBUG = false; 60 61 // These constants must be synchronized with those in 62 // system/media/audio_effects/include/audio_effects/effect_virtualizer.h 63 /** 64 * Is strength parameter supported by virtualizer engine. Parameter ID for getParameter(). 65 */ 66 public static final int PARAM_STRENGTH_SUPPORTED = 0; 67 /** 68 * Virtualizer effect strength. Parameter ID for 69 * {@link android.media.audiofx.Virtualizer.OnParameterChangeListener} 70 */ 71 public static final int PARAM_STRENGTH = 1; 72 /** 73 * @hide 74 * Parameter ID to query the virtual speaker angles for a channel mask / device configuration. 75 */ 76 public static final int PARAM_VIRTUAL_SPEAKER_ANGLES = 2; 77 /** 78 * @hide 79 * Parameter ID to force the virtualization mode to be that of a specific device 80 */ 81 public static final int PARAM_FORCE_VIRTUALIZATION_MODE = 3; 82 /** 83 * @hide 84 * Parameter ID to query the current virtualization mode. 85 */ 86 public static final int PARAM_VIRTUALIZATION_MODE = 4; 87 88 /** 89 * Indicates if strength parameter is supported by the virtualizer engine 90 */ 91 private boolean mStrengthSupported = false; 92 93 /** 94 * Registered listener for parameter changes. 95 */ 96 private OnParameterChangeListener mParamListener = null; 97 98 /** 99 * Listener used internally to to receive raw parameter change event from AudioEffect super class 100 */ 101 private BaseParameterListener mBaseParamListener = null; 102 103 /** 104 * Lock for access to mParamListener 105 */ 106 private final Object mParamListenerLock = new Object(); 107 108 /** 109 * Class constructor. 110 * @param priority the priority level requested by the application for controlling the Virtualizer 111 * engine. As the same engine can be shared by several applications, this parameter indicates 112 * how much the requesting application needs control of effect parameters. The normal priority 113 * is 0, above normal is a positive number, below normal a negative number. 114 * @param audioSession system wide unique audio session identifier. The Virtualizer will 115 * be attached to the MediaPlayer or AudioTrack in the same audio session. 116 * 117 * @throws java.lang.IllegalStateException 118 * @throws java.lang.IllegalArgumentException 119 * @throws java.lang.UnsupportedOperationException 120 * @throws java.lang.RuntimeException 121 */ Virtualizer(int priority, int audioSession)122 public Virtualizer(int priority, int audioSession) 123 throws IllegalStateException, IllegalArgumentException, 124 UnsupportedOperationException, RuntimeException { 125 super(EFFECT_TYPE_VIRTUALIZER, EFFECT_TYPE_NULL, priority, audioSession); 126 127 if (audioSession == 0) { 128 Log.w(TAG, "WARNING: attaching a Virtualizer to global output mix is deprecated!"); 129 } 130 131 int[] value = new int[1]; 132 checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value)); 133 mStrengthSupported = (value[0] != 0); 134 } 135 136 /** 137 * Indicates whether setting strength is supported. If this method returns false, only one 138 * strength is supported and the setStrength() method always rounds to that value. 139 * @return true is strength parameter is supported, false otherwise 140 */ getStrengthSupported()141 public boolean getStrengthSupported() { 142 return mStrengthSupported; 143 } 144 145 /** 146 * Sets the strength of the virtualizer effect. If the implementation does not support per mille 147 * accuracy for setting the strength, it is allowed to round the given strength to the nearest 148 * supported value. You can use the {@link #getRoundedStrength()} method to query the 149 * (possibly rounded) value that was actually set. 150 * @param strength strength of the effect. The valid range for strength strength is [0, 1000], 151 * where 0 per mille designates the mildest effect and 1000 per mille designates the strongest. 152 * @throws IllegalStateException 153 * @throws IllegalArgumentException 154 * @throws UnsupportedOperationException 155 */ setStrength(short strength)156 public void setStrength(short strength) 157 throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { 158 checkStatus(setParameter(PARAM_STRENGTH, strength)); 159 } 160 161 /** 162 * Gets the current strength of the effect. 163 * @return the strength of the effect. The valid range for strength is [0, 1000], where 0 per 164 * mille designates the mildest effect and 1000 per mille the strongest 165 * @throws IllegalStateException 166 * @throws IllegalArgumentException 167 * @throws UnsupportedOperationException 168 */ getRoundedStrength()169 public short getRoundedStrength() 170 throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { 171 short[] value = new short[1]; 172 checkStatus(getParameter(PARAM_STRENGTH, value)); 173 return value[0]; 174 } 175 176 /** 177 * Checks if a configuration is supported, and query the virtual speaker angles. 178 * @param inputChannelMask 179 * @param deviceType 180 * @param angles if non-null: array in which the angles will be written. If null, no angles 181 * are returned 182 * @return true if the combination of channel mask and output device type is supported, false 183 * otherwise 184 * @throws IllegalStateException 185 * @throws IllegalArgumentException 186 * @throws UnsupportedOperationException 187 */ getAnglesInt(int inputChannelMask, int deviceType, int[] angles)188 private boolean getAnglesInt(int inputChannelMask, int deviceType, int[] angles) 189 throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { 190 // parameter check 191 if (inputChannelMask == AudioFormat.CHANNEL_INVALID) { 192 throw (new IllegalArgumentException( 193 "Virtualizer: illegal CHANNEL_INVALID channel mask")); 194 } 195 int channelMask = inputChannelMask == AudioFormat.CHANNEL_OUT_DEFAULT ? 196 AudioFormat.CHANNEL_OUT_STEREO : inputChannelMask; 197 int nbChannels = AudioFormat.channelCountFromOutChannelMask(channelMask); 198 if ((angles != null) && (angles.length < (nbChannels * 3))) { 199 Log.e(TAG, "Size of array for angles cannot accomodate number of channels in mask (" 200 + nbChannels + ")"); 201 throw (new IllegalArgumentException( 202 "Virtualizer: array for channel / angle pairs is too small: is " + angles.length 203 + ", should be " + (nbChannels * 3))); 204 } 205 206 ByteBuffer paramsConverter = ByteBuffer.allocate(3 /* param + mask + device*/ * 4); 207 paramsConverter.order(ByteOrder.nativeOrder()); 208 paramsConverter.putInt(PARAM_VIRTUAL_SPEAKER_ANGLES); 209 // convert channel mask to internal native representation 210 paramsConverter.putInt(AudioFormat.convertChannelOutMaskToNativeMask(channelMask)); 211 // convert Java device type to internal representation 212 paramsConverter.putInt(AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType)); 213 // allocate an array to store the results 214 byte[] result = new byte[nbChannels * 4/*int to byte*/ * 3/*for mask, azimuth, elevation*/]; 215 216 // call into the effect framework 217 int status = getParameter(paramsConverter.array(), result); 218 if (DEBUG) { 219 Log.v(TAG, "getAngles(0x" + Integer.toHexString(inputChannelMask) + ", 0x" 220 + Integer.toHexString(deviceType) + ") returns " + status); 221 } 222 223 if (status >= 0) { 224 if (angles != null) { 225 // convert and copy the results 226 ByteBuffer resultConverter = ByteBuffer.wrap(result); 227 resultConverter.order(ByteOrder.nativeOrder()); 228 for (int i = 0 ; i < nbChannels ; i++) { 229 // write the channel mask 230 angles[3 * i] = AudioFormat.convertNativeChannelMaskToOutMask( 231 resultConverter.getInt((i * 4 * 3))); 232 // write the azimuth 233 angles[3 * i + 1] = resultConverter.getInt(i * 4 * 3 + 4); 234 // write the elevation 235 angles[3 * i + 2] = resultConverter.getInt(i * 4 * 3 + 8); 236 if (DEBUG) { 237 Log.v(TAG, "channel 0x" + Integer.toHexString(angles[3*i]).toUpperCase() 238 + " at az=" + angles[3*i+1] + "deg" 239 + " elev=" + angles[3*i+2] + "deg"); 240 } 241 } 242 } 243 return true; 244 } else if (status == AudioEffect.ERROR_BAD_VALUE) { 245 // a BAD_VALUE return from getParameter indicates the configuration is not supported 246 // don't throw an exception, just return false 247 return false; 248 } else { 249 // something wrong may have happened 250 checkStatus(status); 251 } 252 // unexpected virtualizer behavior 253 Log.e(TAG, "unexpected status code " + status 254 + " after getParameter(PARAM_VIRTUAL_SPEAKER_ANGLES)"); 255 return false; 256 } 257 258 /** 259 * A virtualization mode indicating virtualization processing is not active. 260 * See {@link #getVirtualizationMode()} as one of the possible return value. 261 */ 262 public static final int VIRTUALIZATION_MODE_OFF = 0; 263 264 /** 265 * A virtualization mode used to indicate the virtualizer effect must stop forcing the 266 * processing to a particular mode in {@link #forceVirtualizationMode(int)}. 267 */ 268 public static final int VIRTUALIZATION_MODE_AUTO = 1; 269 /** 270 * A virtualization mode typically used over headphones. 271 * Binaural virtualization describes an audio processing configuration for virtualization 272 * where the left and right channels are respectively reaching the left and right ear of the 273 * user, without also feeding the opposite ear (as is the case when listening over speakers). 274 * <p>Such a mode is therefore meant to be used when audio is playing over stereo wired 275 * headphones or headsets, but also stereo headphones through a wireless A2DP Bluetooth link. 276 * <p>See {@link #canVirtualize(int, int)} to verify this mode is supported by this Virtualizer. 277 */ 278 public final static int VIRTUALIZATION_MODE_BINAURAL = 2; 279 280 /** 281 * A virtualization mode typically used over speakers. 282 * Transaural virtualization describes an audio processing configuration that differs from 283 * binaural (as described in {@link #VIRTUALIZATION_MODE_BINAURAL} in that cross-talk is 284 * present, i.e. audio played from the left channel also reaches the right ear of the user, 285 * and vice-versa. 286 * <p>When supported, such a mode is therefore meant to be used when audio is playing over the 287 * built-in stereo speakers of a device, if they are featured. 288 * <p>See {@link #canVirtualize(int, int)} to verify this mode is supported by this Virtualizer. 289 */ 290 public final static int VIRTUALIZATION_MODE_TRANSAURAL = 3; 291 292 /** @hide */ 293 @IntDef( { 294 VIRTUALIZATION_MODE_BINAURAL, 295 VIRTUALIZATION_MODE_TRANSAURAL 296 }) 297 @Retention(RetentionPolicy.SOURCE) 298 public @interface VirtualizationMode {} 299 300 /** @hide */ 301 @IntDef( { 302 VIRTUALIZATION_MODE_AUTO, 303 VIRTUALIZATION_MODE_BINAURAL, 304 VIRTUALIZATION_MODE_TRANSAURAL 305 }) 306 @Retention(RetentionPolicy.SOURCE) 307 public @interface ForceVirtualizationMode {} 308 getDeviceForModeQuery(@irtualizationMode int virtualizationMode)309 private static int getDeviceForModeQuery(@VirtualizationMode int virtualizationMode) 310 throws IllegalArgumentException { 311 switch (virtualizationMode) { 312 case VIRTUALIZATION_MODE_BINAURAL: 313 return AudioDeviceInfo.TYPE_WIRED_HEADPHONES; 314 case VIRTUALIZATION_MODE_TRANSAURAL: 315 return AudioDeviceInfo.TYPE_BUILTIN_SPEAKER; 316 default: 317 throw (new IllegalArgumentException( 318 "Virtualizer: illegal virtualization mode " + virtualizationMode)); 319 } 320 } 321 getDeviceForModeForce(@orceVirtualizationMode int virtualizationMode)322 private static int getDeviceForModeForce(@ForceVirtualizationMode int virtualizationMode) 323 throws IllegalArgumentException { 324 if (virtualizationMode == VIRTUALIZATION_MODE_AUTO) { 325 return AudioDeviceInfo.TYPE_UNKNOWN; 326 } else { 327 return getDeviceForModeQuery(virtualizationMode); 328 } 329 } 330 deviceToMode(int deviceType)331 private static int deviceToMode(int deviceType) { 332 switch (deviceType) { 333 case AudioDeviceInfo.TYPE_WIRED_HEADSET: 334 case AudioDeviceInfo.TYPE_WIRED_HEADPHONES: 335 case AudioDeviceInfo.TYPE_BLUETOOTH_SCO: 336 case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE: 337 case AudioDeviceInfo.TYPE_USB_HEADSET: 338 return VIRTUALIZATION_MODE_BINAURAL; 339 case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER: 340 case AudioDeviceInfo.TYPE_LINE_ANALOG: 341 case AudioDeviceInfo.TYPE_LINE_DIGITAL: 342 case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP: 343 case AudioDeviceInfo.TYPE_HDMI: 344 case AudioDeviceInfo.TYPE_HDMI_ARC: 345 case AudioDeviceInfo.TYPE_USB_DEVICE: 346 case AudioDeviceInfo.TYPE_USB_ACCESSORY: 347 case AudioDeviceInfo.TYPE_DOCK: 348 case AudioDeviceInfo.TYPE_FM: 349 case AudioDeviceInfo.TYPE_AUX_LINE: 350 return VIRTUALIZATION_MODE_TRANSAURAL; 351 case AudioDeviceInfo.TYPE_UNKNOWN: 352 default: 353 return VIRTUALIZATION_MODE_OFF; 354 } 355 } 356 357 /** 358 * Checks if the combination of a channel mask and virtualization mode is supported by this 359 * virtualizer. 360 * Some virtualizer implementations may only support binaural processing (i.e. only support 361 * headphone output, see {@link #VIRTUALIZATION_MODE_BINAURAL}), some may support transaural 362 * processing (i.e. for speaker output, see {@link #VIRTUALIZATION_MODE_TRANSAURAL}) for the 363 * built-in speakers. Use this method to query the virtualizer implementation capabilities. 364 * @param inputChannelMask the channel mask of the content to virtualize. 365 * @param virtualizationMode the mode for which virtualization processing is to be performed, 366 * one of {@link #VIRTUALIZATION_MODE_BINAURAL}, {@link #VIRTUALIZATION_MODE_TRANSAURAL}. 367 * @return true if the combination of channel mask and virtualization mode is supported, false 368 * otherwise. 369 * <br>An indication that a certain channel mask is not supported doesn't necessarily mean 370 * you cannot play content with that channel mask, it more likely implies the content will 371 * be downmixed before being virtualized. For instance a virtualizer that only supports a 372 * mask such as {@link AudioFormat#CHANNEL_OUT_STEREO} 373 * will still be able to process content with a mask of 374 * {@link AudioFormat#CHANNEL_OUT_5POINT1}, but will downmix the content to stereo first, and 375 * then will virtualize, as opposed to virtualizing each channel individually. 376 * @throws IllegalStateException 377 * @throws IllegalArgumentException 378 * @throws UnsupportedOperationException 379 */ canVirtualize(int inputChannelMask, @VirtualizationMode int virtualizationMode)380 public boolean canVirtualize(int inputChannelMask, @VirtualizationMode int virtualizationMode) 381 throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { 382 return getAnglesInt(inputChannelMask, getDeviceForModeQuery(virtualizationMode), null); 383 } 384 385 /** 386 * Queries the virtual speaker angles (azimuth and elevation) for a combination of a channel 387 * mask and virtualization mode. 388 * If the virtualization configuration (mask and mode) is supported (see 389 * {@link #canVirtualize(int, int)}, the array angles will contain upon return the 390 * definition of each virtual speaker and its azimuth and elevation angles relative to the 391 * listener. 392 * <br>Note that in some virtualizer implementations, the angles may be strength-dependent. 393 * @param inputChannelMask the channel mask of the content to virtualize. 394 * @param virtualizationMode the mode for which virtualization processing is to be performed, 395 * one of {@link #VIRTUALIZATION_MODE_BINAURAL}, {@link #VIRTUALIZATION_MODE_TRANSAURAL}. 396 * @param angles a non-null array whose length is 3 times the number of channels in the channel 397 * mask. 398 * If the method indicates the configuration is supported, the array will contain upon return 399 * triplets of values: for each channel <code>i</code> among the channels of the mask: 400 * <ul> 401 * <li>the element at index <code>3*i</code> in the array contains the speaker 402 * identification (e.g. {@link AudioFormat#CHANNEL_OUT_FRONT_LEFT}),</li> 403 * <li>the element at index <code>3*i+1</code> contains its corresponding azimuth angle 404 * expressed in degrees, where 0 is the direction the listener faces, 180 is behind 405 * the listener, and -90 is to her/his left,</li> 406 * <li>the element at index <code>3*i+2</code> contains its corresponding elevation angle 407 * where +90 is directly above the listener, 0 is the horizontal plane, and -90 is 408 * directly below the listener.</li> 409 * @return true if the combination of channel mask and virtualization mode is supported, false 410 * otherwise. 411 * @throws IllegalStateException 412 * @throws IllegalArgumentException 413 * @throws UnsupportedOperationException 414 */ getSpeakerAngles(int inputChannelMask, @VirtualizationMode int virtualizationMode, int[] angles)415 public boolean getSpeakerAngles(int inputChannelMask, 416 @VirtualizationMode int virtualizationMode, int[] angles) 417 throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { 418 if (angles == null) { 419 throw (new IllegalArgumentException( 420 "Virtualizer: illegal null channel / angle array")); 421 } 422 423 return getAnglesInt(inputChannelMask, getDeviceForModeQuery(virtualizationMode), angles); 424 } 425 426 /** 427 * Forces the virtualizer effect to use the given processing mode. 428 * The effect must be enabled for the forced mode to be applied. 429 * @param virtualizationMode one of {@link #VIRTUALIZATION_MODE_BINAURAL}, 430 * {@link #VIRTUALIZATION_MODE_TRANSAURAL} to force a particular processing mode, or 431 * {@value #VIRTUALIZATION_MODE_AUTO} to stop forcing a mode. 432 * @return true if the processing mode is supported, and it is successfully set, or 433 * forcing was successfully disabled, false otherwise. 434 * @throws IllegalStateException 435 * @throws IllegalArgumentException 436 * @throws UnsupportedOperationException 437 */ forceVirtualizationMode(@orceVirtualizationMode int virtualizationMode)438 public boolean forceVirtualizationMode(@ForceVirtualizationMode int virtualizationMode) 439 throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { 440 // convert Java device type to internal representation 441 int deviceType = getDeviceForModeForce(virtualizationMode); 442 int internalDevice = AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType); 443 444 int status = setParameter(PARAM_FORCE_VIRTUALIZATION_MODE, internalDevice); 445 446 if (status >= 0) { 447 return true; 448 } else if (status == AudioEffect.ERROR_BAD_VALUE) { 449 // a BAD_VALUE return from setParameter indicates the mode can't be forced 450 // don't throw an exception, just return false 451 return false; 452 } else { 453 // something wrong may have happened 454 checkStatus(status); 455 } 456 // unexpected virtualizer behavior 457 Log.e(TAG, "unexpected status code " + status 458 + " after setParameter(PARAM_FORCE_VIRTUALIZATION_MODE)"); 459 return false; 460 } 461 462 /** 463 * Return the virtualization mode being used, if any. 464 * @return the virtualization mode being used. 465 * If virtualization is not active, the virtualization mode will be 466 * {@link #VIRTUALIZATION_MODE_OFF}. Otherwise the value will be 467 * {@link #VIRTUALIZATION_MODE_BINAURAL} or {@link #VIRTUALIZATION_MODE_TRANSAURAL}. 468 * Virtualization may not be active either because the effect is not enabled or 469 * because the current output device is not compatible with this virtualization 470 * implementation. 471 * @throws IllegalStateException 472 * @throws UnsupportedOperationException 473 */ getVirtualizationMode()474 public int getVirtualizationMode() 475 throws IllegalStateException, UnsupportedOperationException { 476 int[] value = new int[1]; 477 int status = getParameter(PARAM_VIRTUALIZATION_MODE, value); 478 if (status >= 0) { 479 return deviceToMode(AudioDeviceInfo.convertInternalDeviceToDeviceType(value[0])); 480 } else if (status == AudioEffect.ERROR_BAD_VALUE) { 481 return VIRTUALIZATION_MODE_OFF; 482 } else { 483 // something wrong may have happened 484 checkStatus(status); 485 } 486 // unexpected virtualizer behavior 487 Log.e(TAG, "unexpected status code " + status 488 + " after getParameter(PARAM_VIRTUALIZATION_MODE)"); 489 return VIRTUALIZATION_MODE_OFF; 490 } 491 492 /** 493 * The OnParameterChangeListener interface defines a method called by the Virtualizer when a 494 * parameter value has changed. 495 */ 496 public interface OnParameterChangeListener { 497 /** 498 * Method called when a parameter value has changed. The method is called only if the 499 * parameter was changed by another application having the control of the same 500 * Virtualizer engine. 501 * @param effect the Virtualizer on which the interface is registered. 502 * @param status status of the set parameter operation. 503 * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ... 504 * @param value the new parameter value. 505 */ onParameterChange(Virtualizer effect, int status, int param, short value)506 void onParameterChange(Virtualizer effect, int status, int param, short value); 507 } 508 509 /** 510 * Listener used internally to receive unformatted parameter change events from AudioEffect 511 * super class. 512 */ 513 private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { BaseParameterListener()514 private BaseParameterListener() { 515 516 } onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value)517 public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { 518 OnParameterChangeListener l = null; 519 520 synchronized (mParamListenerLock) { 521 if (mParamListener != null) { 522 l = mParamListener; 523 } 524 } 525 if (l != null) { 526 int p = -1; 527 short v = -1; 528 529 if (param.length == 4) { 530 p = byteArrayToInt(param, 0); 531 } 532 if (value.length == 2) { 533 v = byteArrayToShort(value, 0); 534 } 535 if (p != -1 && v != -1) { 536 l.onParameterChange(Virtualizer.this, status, p, v); 537 } 538 } 539 } 540 } 541 542 /** 543 * Registers an OnParameterChangeListener interface. 544 * @param listener OnParameterChangeListener interface registered 545 */ setParameterListener(OnParameterChangeListener listener)546 public void setParameterListener(OnParameterChangeListener listener) { 547 synchronized (mParamListenerLock) { 548 if (mParamListener == null) { 549 mParamListener = listener; 550 mBaseParamListener = new BaseParameterListener(); 551 super.setParameterListener(mBaseParamListener); 552 } 553 } 554 } 555 556 /** 557 * The Settings class regroups all virtualizer parameters. It is used in 558 * conjuntion with getProperties() and setProperties() methods to backup and restore 559 * all parameters in a single call. 560 */ 561 public static class Settings { 562 public short strength; 563 Settings()564 public Settings() { 565 } 566 567 /** 568 * Settings class constructor from a key=value; pairs formatted string. The string is 569 * typically returned by Settings.toString() method. 570 * @throws IllegalArgumentException if the string is not correctly formatted. 571 */ Settings(String settings)572 public Settings(String settings) { 573 StringTokenizer st = new StringTokenizer(settings, "=;"); 574 int tokens = st.countTokens(); 575 if (st.countTokens() != 3) { 576 throw new IllegalArgumentException("settings: " + settings); 577 } 578 String key = st.nextToken(); 579 if (!key.equals("Virtualizer")) { 580 throw new IllegalArgumentException( 581 "invalid settings for Virtualizer: " + key); 582 } 583 try { 584 key = st.nextToken(); 585 if (!key.equals("strength")) { 586 throw new IllegalArgumentException("invalid key name: " + key); 587 } 588 strength = Short.parseShort(st.nextToken()); 589 } catch (NumberFormatException nfe) { 590 throw new IllegalArgumentException("invalid value for key: " + key); 591 } 592 } 593 594 @Override toString()595 public String toString() { 596 String str = new String ( 597 "Virtualizer"+ 598 ";strength="+Short.toString(strength) 599 ); 600 return str; 601 } 602 }; 603 604 605 /** 606 * Gets the virtualizer properties. This method is useful when a snapshot of current 607 * virtualizer settings must be saved by the application. 608 * @return a Virtualizer.Settings object containing all current parameters values 609 * @throws IllegalStateException 610 * @throws IllegalArgumentException 611 * @throws UnsupportedOperationException 612 */ getProperties()613 public Virtualizer.Settings getProperties() 614 throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { 615 Settings settings = new Settings(); 616 short[] value = new short[1]; 617 checkStatus(getParameter(PARAM_STRENGTH, value)); 618 settings.strength = value[0]; 619 return settings; 620 } 621 622 /** 623 * Sets the virtualizer properties. This method is useful when virtualizer settings have to 624 * be applied from a previous backup. 625 * @param settings a Virtualizer.Settings object containing the properties to apply 626 * @throws IllegalStateException 627 * @throws IllegalArgumentException 628 * @throws UnsupportedOperationException 629 */ setProperties(Virtualizer.Settings settings)630 public void setProperties(Virtualizer.Settings settings) 631 throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { 632 checkStatus(setParameter(PARAM_STRENGTH, settings.strength)); 633 } 634 } 635