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.IntDef; 20import android.media.AudioDeviceInfo; 21import android.media.AudioFormat; 22import android.media.audiofx.AudioEffect; 23import android.util.Log; 24 25import java.lang.annotation.Retention; 26import java.lang.annotation.RetentionPolicy; 27import java.nio.ByteBuffer; 28import java.nio.ByteOrder; 29import 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 51public 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 return VIRTUALIZATION_MODE_BINAURAL; 333 case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER: 334 case AudioDeviceInfo.TYPE_LINE_ANALOG: 335 case AudioDeviceInfo.TYPE_LINE_DIGITAL: 336 case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP: 337 case AudioDeviceInfo.TYPE_HDMI: 338 case AudioDeviceInfo.TYPE_HDMI_ARC: 339 case AudioDeviceInfo.TYPE_USB_DEVICE: 340 case AudioDeviceInfo.TYPE_USB_ACCESSORY: 341 case AudioDeviceInfo.TYPE_DOCK: 342 case AudioDeviceInfo.TYPE_FM: 343 case AudioDeviceInfo.TYPE_AUX_LINE: 344 return VIRTUALIZATION_MODE_TRANSAURAL; 345 case AudioDeviceInfo.TYPE_UNKNOWN: 346 default: 347 return VIRTUALIZATION_MODE_OFF; 348 } 349 } 350 351 /** 352 * Checks if the combination of a channel mask and virtualization mode is supported by this 353 * virtualizer. 354 * Some virtualizer implementations may only support binaural processing (i.e. only support 355 * headphone output, see {@link #VIRTUALIZATION_MODE_BINAURAL}), some may support transaural 356 * processing (i.e. for speaker output, see {@link #VIRTUALIZATION_MODE_TRANSAURAL}) for the 357 * built-in speakers. Use this method to query the virtualizer implementation capabilities. 358 * @param inputChannelMask the channel mask of the content to virtualize. 359 * @param virtualizationMode the mode for which virtualization processing is to be performed, 360 * one of {@link #VIRTUALIZATION_MODE_BINAURAL}, {@link #VIRTUALIZATION_MODE_TRANSAURAL}. 361 * @return true if the combination of channel mask and virtualization mode is supported, false 362 * otherwise. 363 * <br>An indication that a certain channel mask is not supported doesn't necessarily mean 364 * you cannot play content with that channel mask, it more likely implies the content will 365 * be downmixed before being virtualized. For instance a virtualizer that only supports a 366 * mask such as {@link AudioFormat#CHANNEL_OUT_STEREO} 367 * will still be able to process content with a mask of 368 * {@link AudioFormat#CHANNEL_OUT_5POINT1}, but will downmix the content to stereo first, and 369 * then will virtualize, as opposed to virtualizing each channel individually. 370 * @throws IllegalStateException 371 * @throws IllegalArgumentException 372 * @throws UnsupportedOperationException 373 */ 374 public boolean canVirtualize(int inputChannelMask, @VirtualizationMode int virtualizationMode) 375 throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { 376 return getAnglesInt(inputChannelMask, getDeviceForModeQuery(virtualizationMode), null); 377 } 378 379 /** 380 * Queries the virtual speaker angles (azimuth and elevation) for a combination of a channel 381 * mask and virtualization mode. 382 * If the virtualization configuration (mask and mode) is supported (see 383 * {@link #canVirtualize(int, int)}, the array angles will contain upon return the 384 * definition of each virtual speaker and its azimuth and elevation angles relative to the 385 * listener. 386 * <br>Note that in some virtualizer implementations, the angles may be strength-dependent. 387 * @param inputChannelMask the channel mask of the content to virtualize. 388 * @param virtualizationMode the mode for which virtualization processing is to be performed, 389 * one of {@link #VIRTUALIZATION_MODE_BINAURAL}, {@link #VIRTUALIZATION_MODE_TRANSAURAL}. 390 * @param angles a non-null array whose length is 3 times the number of channels in the channel 391 * mask. 392 * If the method indicates the configuration is supported, the array will contain upon return 393 * triplets of values: for each channel <code>i</code> among the channels of the mask: 394 * <ul> 395 * <li>the element at index <code>3*i</code> in the array contains the speaker 396 * identification (e.g. {@link AudioFormat#CHANNEL_OUT_FRONT_LEFT}),</li> 397 * <li>the element at index <code>3*i+1</code> contains its corresponding azimuth angle 398 * expressed in degrees, where 0 is the direction the listener faces, 180 is behind 399 * the listener, and -90 is to her/his left,</li> 400 * <li>the element at index <code>3*i+2</code> contains its corresponding elevation angle 401 * where +90 is directly above the listener, 0 is the horizontal plane, and -90 is 402 * directly below the listener.</li> 403 * @return true if the combination of channel mask and virtualization mode is supported, false 404 * otherwise. 405 * @throws IllegalStateException 406 * @throws IllegalArgumentException 407 * @throws UnsupportedOperationException 408 */ 409 public boolean getSpeakerAngles(int inputChannelMask, 410 @VirtualizationMode int virtualizationMode, int[] angles) 411 throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { 412 if (angles == null) { 413 throw (new IllegalArgumentException( 414 "Virtualizer: illegal null channel / angle array")); 415 } 416 417 return getAnglesInt(inputChannelMask, getDeviceForModeQuery(virtualizationMode), angles); 418 } 419 420 /** 421 * Forces the virtualizer effect to use the given processing mode. 422 * The effect must be enabled for the forced mode to be applied. 423 * @param virtualizationMode one of {@link #VIRTUALIZATION_MODE_BINAURAL}, 424 * {@link #VIRTUALIZATION_MODE_TRANSAURAL} to force a particular processing mode, or 425 * {@value #VIRTUALIZATION_MODE_AUTO} to stop forcing a mode. 426 * @return true if the processing mode is supported, and it is successfully set, or 427 * forcing was successfully disabled, false otherwise. 428 * @throws IllegalStateException 429 * @throws IllegalArgumentException 430 * @throws UnsupportedOperationException 431 */ 432 public boolean forceVirtualizationMode(@ForceVirtualizationMode int virtualizationMode) 433 throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { 434 // convert Java device type to internal representation 435 int deviceType = getDeviceForModeForce(virtualizationMode); 436 int internalDevice = AudioDeviceInfo.convertDeviceTypeToInternalDevice(deviceType); 437 438 int status = setParameter(PARAM_FORCE_VIRTUALIZATION_MODE, internalDevice); 439 440 if (status >= 0) { 441 return true; 442 } else if (status == AudioEffect.ERROR_BAD_VALUE) { 443 // a BAD_VALUE return from setParameter indicates the mode can't be forced 444 // don't throw an exception, just return false 445 return false; 446 } else { 447 // something wrong may have happened 448 checkStatus(status); 449 } 450 // unexpected virtualizer behavior 451 Log.e(TAG, "unexpected status code " + status 452 + " after setParameter(PARAM_FORCE_VIRTUALIZATION_MODE)"); 453 return false; 454 } 455 456 /** 457 * Return the virtualization mode being used, if any. 458 * @return the virtualization mode being used. 459 * If virtualization is not active, the virtualization mode will be 460 * {@link #VIRTUALIZATION_MODE_OFF}. Otherwise the value will be 461 * {@link #VIRTUALIZATION_MODE_BINAURAL} or {@link #VIRTUALIZATION_MODE_TRANSAURAL}. 462 * Virtualization may not be active either because the effect is not enabled or 463 * because the current output device is not compatible with this virtualization 464 * implementation. 465 * @throws IllegalStateException 466 * @throws UnsupportedOperationException 467 */ 468 public int getVirtualizationMode() 469 throws IllegalStateException, UnsupportedOperationException { 470 int[] value = new int[1]; 471 int status = getParameter(PARAM_VIRTUALIZATION_MODE, value); 472 if (status >= 0) { 473 return deviceToMode(AudioDeviceInfo.convertInternalDeviceToDeviceType(value[0])); 474 } else if (status == AudioEffect.ERROR_BAD_VALUE) { 475 return VIRTUALIZATION_MODE_OFF; 476 } else { 477 // something wrong may have happened 478 checkStatus(status); 479 } 480 // unexpected virtualizer behavior 481 Log.e(TAG, "unexpected status code " + status 482 + " after getParameter(PARAM_VIRTUALIZATION_MODE)"); 483 return VIRTUALIZATION_MODE_OFF; 484 } 485 486 /** 487 * The OnParameterChangeListener interface defines a method called by the Virtualizer when a 488 * parameter value has changed. 489 */ 490 public interface OnParameterChangeListener { 491 /** 492 * Method called when a parameter value has changed. The method is called only if the 493 * parameter was changed by another application having the control of the same 494 * Virtualizer engine. 495 * @param effect the Virtualizer on which the interface is registered. 496 * @param status status of the set parameter operation. 497 * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ... 498 * @param value the new parameter value. 499 */ 500 void onParameterChange(Virtualizer effect, int status, int param, short value); 501 } 502 503 /** 504 * Listener used internally to receive unformatted parameter change events from AudioEffect 505 * super class. 506 */ 507 private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { 508 private BaseParameterListener() { 509 510 } 511 public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { 512 OnParameterChangeListener l = null; 513 514 synchronized (mParamListenerLock) { 515 if (mParamListener != null) { 516 l = mParamListener; 517 } 518 } 519 if (l != null) { 520 int p = -1; 521 short v = -1; 522 523 if (param.length == 4) { 524 p = byteArrayToInt(param, 0); 525 } 526 if (value.length == 2) { 527 v = byteArrayToShort(value, 0); 528 } 529 if (p != -1 && v != -1) { 530 l.onParameterChange(Virtualizer.this, status, p, v); 531 } 532 } 533 } 534 } 535 536 /** 537 * Registers an OnParameterChangeListener interface. 538 * @param listener OnParameterChangeListener interface registered 539 */ 540 public void setParameterListener(OnParameterChangeListener listener) { 541 synchronized (mParamListenerLock) { 542 if (mParamListener == null) { 543 mParamListener = listener; 544 mBaseParamListener = new BaseParameterListener(); 545 super.setParameterListener(mBaseParamListener); 546 } 547 } 548 } 549 550 /** 551 * The Settings class regroups all virtualizer parameters. It is used in 552 * conjuntion with getProperties() and setProperties() methods to backup and restore 553 * all parameters in a single call. 554 */ 555 public static class Settings { 556 public short strength; 557 558 public Settings() { 559 } 560 561 /** 562 * Settings class constructor from a key=value; pairs formatted string. The string is 563 * typically returned by Settings.toString() method. 564 * @throws IllegalArgumentException if the string is not correctly formatted. 565 */ 566 public Settings(String settings) { 567 StringTokenizer st = new StringTokenizer(settings, "=;"); 568 int tokens = st.countTokens(); 569 if (st.countTokens() != 3) { 570 throw new IllegalArgumentException("settings: " + settings); 571 } 572 String key = st.nextToken(); 573 if (!key.equals("Virtualizer")) { 574 throw new IllegalArgumentException( 575 "invalid settings for Virtualizer: " + key); 576 } 577 try { 578 key = st.nextToken(); 579 if (!key.equals("strength")) { 580 throw new IllegalArgumentException("invalid key name: " + key); 581 } 582 strength = Short.parseShort(st.nextToken()); 583 } catch (NumberFormatException nfe) { 584 throw new IllegalArgumentException("invalid value for key: " + key); 585 } 586 } 587 588 @Override 589 public String toString() { 590 String str = new String ( 591 "Virtualizer"+ 592 ";strength="+Short.toString(strength) 593 ); 594 return str; 595 } 596 }; 597 598 599 /** 600 * Gets the virtualizer properties. This method is useful when a snapshot of current 601 * virtualizer settings must be saved by the application. 602 * @return a Virtualizer.Settings object containing all current parameters values 603 * @throws IllegalStateException 604 * @throws IllegalArgumentException 605 * @throws UnsupportedOperationException 606 */ 607 public Virtualizer.Settings getProperties() 608 throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { 609 Settings settings = new Settings(); 610 short[] value = new short[1]; 611 checkStatus(getParameter(PARAM_STRENGTH, value)); 612 settings.strength = value[0]; 613 return settings; 614 } 615 616 /** 617 * Sets the virtualizer properties. This method is useful when virtualizer settings have to 618 * be applied from a previous backup. 619 * @param settings a Virtualizer.Settings object containing the properties to apply 620 * @throws IllegalStateException 621 * @throws IllegalArgumentException 622 * @throws UnsupportedOperationException 623 */ 624 public void setProperties(Virtualizer.Settings settings) 625 throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { 626 checkStatus(setParameter(PARAM_STRENGTH, settings.strength)); 627 } 628} 629