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