1 /* 2 * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.sound.sampled; 27 28 /** 29 * A {@code FloatControl} object provides control over a range of floating-point 30 * values. Float controls are often represented in graphical user interfaces by 31 * continuously adjustable objects such as sliders or rotary knobs. Concrete 32 * subclasses of {@code FloatControl} implement controls, such as gain and pan, 33 * that affect a line's audio signal in some way that an application can 34 * manipulate. The {@link FloatControl.Type} inner class provides static 35 * instances of types that are used to identify some common kinds of float 36 * control. 37 * <p> 38 * The {@code FloatControl} abstract class provides methods to set and get the 39 * control's current floating-point value. Other methods obtain the possible 40 * range of values and the control's resolution (the smallest increment between 41 * returned values). Some float controls allow ramping to a new value over a 42 * specified period of time. {@code FloatControl} also includes methods that 43 * return string labels for the minimum, maximum, and midpoint positions of the 44 * control. 45 * 46 * @author David Rivas 47 * @author Kara Kytle 48 * @see Line#getControls 49 * @see Line#isControlSupported 50 * @since 1.3 51 */ 52 public abstract class FloatControl extends Control { 53 54 /** 55 * The minimum supported value. 56 */ 57 private final float minimum; 58 59 /** 60 * The maximum supported value. 61 */ 62 private final float maximum; 63 64 /** 65 * The control's precision. 66 */ 67 private final float precision; 68 69 /** 70 * The smallest time increment in which a value change can be effected 71 * during a value shift, in microseconds. 72 */ 73 private final int updatePeriod; 74 75 /** 76 * A label for the units in which the control values are expressed, such as 77 * "dB" for decibels. 78 */ 79 private final String units; 80 81 /** 82 * A label for the minimum value, such as "Left". 83 */ 84 private final String minLabel; 85 86 /** 87 * A label for the maximum value, such as "Right". 88 */ 89 private final String maxLabel; 90 91 /** 92 * A label for the mid-point value, such as "Center". 93 */ 94 private final String midLabel; 95 96 /** 97 * The current value. 98 */ 99 private float value; 100 101 /** 102 * Constructs a new float control object with the given parameters. 103 * 104 * @param type the kind of control represented by this float control object 105 * @param minimum the smallest value permitted for the control 106 * @param maximum the largest value permitted for the control 107 * @param precision the resolution or granularity of the control. This is 108 * the size of the increment between discrete valid values. 109 * @param updatePeriod the smallest time interval, in microseconds, over 110 * which the control can change from one discrete value to the next 111 * during a {@link #shift(float,float,int) shift} 112 * @param initialValue the value that the control starts with when 113 * constructed 114 * @param units the label for the units in which the control's values are 115 * expressed, such as "dB" or "frames per second" 116 * @param minLabel the label for the minimum value, such as "Left" or "Off" 117 * @param midLabel the label for the midpoint value, such as "Center" or 118 * "Default" 119 * @param maxLabel the label for the maximum value, such as "Right" or 120 * "Full" 121 * @throws IllegalArgumentException if {@code minimum} is greater than 122 * {@code maximum} or {@code initialValue} does not fall within the 123 * allowable range 124 */ FloatControl(Type type, float minimum, float maximum, float precision, int updatePeriod, float initialValue, String units, String minLabel, String midLabel, String maxLabel)125 protected FloatControl(Type type, float minimum, float maximum, 126 float precision, int updatePeriod, float initialValue, 127 String units, String minLabel, String midLabel, String maxLabel) { 128 129 super(type); 130 131 if (minimum > maximum) { 132 throw new IllegalArgumentException("Minimum value " + minimum 133 + " exceeds maximum value " + maximum + "."); 134 } 135 if (initialValue < minimum) { 136 throw new IllegalArgumentException("Initial value " + initialValue 137 + " smaller than allowable minimum value " + minimum + "."); 138 } 139 if (initialValue > maximum) { 140 throw new IllegalArgumentException("Initial value " + initialValue 141 + " exceeds allowable maximum value " + maximum + "."); 142 } 143 144 145 this.minimum = minimum; 146 this.maximum = maximum; 147 148 this.precision = precision; 149 this.updatePeriod = updatePeriod; 150 this.value = initialValue; 151 152 this.units = units; 153 this.minLabel = ( (minLabel == null) ? "" : minLabel); 154 this.midLabel = ( (midLabel == null) ? "" : midLabel); 155 this.maxLabel = ( (maxLabel == null) ? "" : maxLabel); 156 } 157 158 /** 159 * Constructs a new float control object with the given parameters. The 160 * labels for the minimum, maximum, and mid-point values are set to 161 * zero-length strings. 162 * 163 * @param type the kind of control represented by this float control object 164 * @param minimum the smallest value permitted for the control 165 * @param maximum the largest value permitted for the control 166 * @param precision the resolution or granularity of the control. This is 167 * the size of the increment between discrete valid values. 168 * @param updatePeriod the smallest time interval, in microseconds, over 169 * which the control can change from one discrete value to the next 170 * during a {@link #shift(float,float,int) shift} 171 * @param initialValue the value that the control starts with when 172 * constructed 173 * @param units the label for the units in which the control's values are 174 * expressed, such as "dB" or "frames per second" 175 * @throws IllegalArgumentException if {@code minimum} is greater than 176 * {@code maximum} or {@code initialValue} does not fall within the 177 * allowable range 178 */ FloatControl(Type type, float minimum, float maximum, float precision, int updatePeriod, float initialValue, String units)179 protected FloatControl(Type type, float minimum, float maximum, 180 float precision, int updatePeriod, float initialValue, String units) { 181 this(type, minimum, maximum, precision, updatePeriod, 182 initialValue, units, "", "", ""); 183 } 184 185 /** 186 * Sets the current value for the control. The default implementation simply 187 * sets the value as indicated. If the value indicated is greater than the 188 * maximum value, or smaller than the minimum value, an 189 * {@code IllegalArgumentException} is thrown. Some controls require that 190 * their line be open before they can be affected by setting a value. 191 * 192 * @param newValue desired new value 193 * @throws IllegalArgumentException if the value indicated does not fall 194 * within the allowable range 195 */ setValue(float newValue)196 public void setValue(float newValue) { 197 198 if (newValue > maximum) { 199 throw new IllegalArgumentException("Requested value " + newValue + " exceeds allowable maximum value " + maximum + "."); 200 } 201 202 if (newValue < minimum) { 203 throw new IllegalArgumentException("Requested value " + newValue + " smaller than allowable minimum value " + minimum + "."); 204 } 205 206 value = newValue; 207 } 208 209 /** 210 * Obtains this control's current value. 211 * 212 * @return the current value 213 */ getValue()214 public float getValue() { 215 return value; 216 } 217 218 /** 219 * Obtains the maximum value permitted. 220 * 221 * @return the maximum allowable value 222 */ getMaximum()223 public float getMaximum() { 224 return maximum; 225 } 226 227 /** 228 * Obtains the minimum value permitted. 229 * 230 * @return the minimum allowable value 231 */ getMinimum()232 public float getMinimum() { 233 return minimum; 234 } 235 236 /** 237 * Obtains the label for the units in which the control's values are 238 * expressed, such as "dB" or "frames per second." 239 * 240 * @return the units label, or a zero-length string if no label 241 */ getUnits()242 public String getUnits() { 243 return units; 244 } 245 246 /** 247 * Obtains the label for the minimum value, such as "Left" or "Off". 248 * 249 * @return the minimum value label, or a zero-length string if no label has 250 * been set 251 */ getMinLabel()252 public String getMinLabel() { 253 return minLabel; 254 } 255 256 /** 257 * Obtains the label for the mid-point value, such as "Center" or "Default". 258 * 259 * @return the mid-point value label, or a zero-length string if no label 260 * has been set 261 */ getMidLabel()262 public String getMidLabel() { 263 return midLabel; 264 } 265 266 /** 267 * Obtains the label for the maximum value, such as "Right" or "Full". 268 * 269 * @return the maximum value label, or a zero-length string if no label has 270 * been set 271 */ getMaxLabel()272 public String getMaxLabel() { 273 return maxLabel; 274 } 275 276 /** 277 * Obtains the resolution or granularity of the control, in the units that 278 * the control measures. The precision is the size of the increment between 279 * discrete valid values for this control, over the set of supported 280 * floating-point values. 281 * 282 * @return the control's precision 283 */ getPrecision()284 public float getPrecision() { 285 return precision; 286 } 287 288 /** 289 * Obtains the smallest time interval, in microseconds, over which the 290 * control's value can change during a shift. The update period is the 291 * inverse of the frequency with which the control updates its value during 292 * a shift. If the implementation does not support value shifting over time, 293 * it should set the control's value to the final value immediately and 294 * return -1 from this method. 295 * 296 * @return update period in microseconds, or -1 if shifting over time is 297 * unsupported 298 * @see #shift 299 */ getUpdatePeriod()300 public int getUpdatePeriod() { 301 return updatePeriod; 302 } 303 304 /** 305 * Changes the control value from the initial value to the final value 306 * linearly over the specified time period, specified in microseconds. This 307 * method returns without blocking; it does not wait for the shift to 308 * complete. An implementation should complete the operation within the time 309 * specified. The default implementation simply changes the value to the 310 * final value immediately. 311 * 312 * @param from initial value at the beginning of the shift 313 * @param to final value after the shift 314 * @param microseconds maximum duration of the shift in microseconds 315 * @throws IllegalArgumentException if either {@code from} or {@code to} 316 * value does not fall within the allowable range 317 * @see #getUpdatePeriod 318 */ shift(float from, float to, int microseconds)319 public void shift(float from, float to, int microseconds) { 320 // test "from" value, "to" value will be tested by setValue() 321 if (from < minimum) { 322 throw new IllegalArgumentException("Requested value " + from 323 + " smaller than allowable minimum value " + minimum + "."); 324 } 325 if (from > maximum) { 326 throw new IllegalArgumentException("Requested value " + from 327 + " exceeds allowable maximum value " + maximum + "."); 328 } 329 setValue(to); 330 } 331 332 /** 333 * Returns a string representation of the float control. 334 * 335 * @return a string representation of the float control 336 */ 337 @Override toString()338 public String toString() { 339 return String.format("%s with current value: %s %s (range: %s - %s)", 340 super.toString(), getValue(), getUnits(), 341 getMinimum(), getMaximum()); 342 } 343 344 /** 345 * An instance of the {@code FloatControl.Type} inner class identifies one 346 * kind of float control. Static instances are provided for the common 347 * types. 348 * 349 * @author Kara Kytle 350 * @since 1.3 351 */ 352 public static class Type extends Control.Type { 353 354 /** 355 * Represents a control for the overall gain on a line. 356 * <p> 357 * Gain is a quantity in decibels (dB) that is added to the intrinsic 358 * decibel level of the audio signal--that is, the level of the signal 359 * before it is altered by the gain control. A positive gain amplifies 360 * (boosts) the signal's volume, and a negative gain attenuates(cuts)it. 361 * The gain setting defaults to a value of 0.0 dB, meaning the signal's 362 * loudness is unaffected. Note that gain measures dB, not amplitude. 363 * The relationship between a gain in decibels and the corresponding 364 * linear amplitude multiplier is: 365 * <p style="text-align:center"> 366 * {@code linearScalar = pow(10.0, gainDB/20.0)} 367 * <p> 368 * The {@code FloatControl} class has methods to impose a maximum and 369 * minimum allowable value for gain. However, because an audio signal 370 * might already be at a high amplitude, the maximum setting does not 371 * guarantee that the signal will be undistorted when the gain is 372 * applied to it (unless the maximum is zero or negative). To avoid 373 * numeric overflow from excessively large gain settings, a gain control 374 * can implement clipping, meaning that the signal's amplitude will be 375 * limited to the maximum value representable by its audio format, 376 * instead of wrapping around. 377 * <p> 378 * These comments apply to gain controls in general, not just master 379 * gain controls. A line can have more than one gain control. For 380 * example, a mixer (which is itself a line) might have a master gain 381 * control, an auxiliary return control, a reverb return control, and, 382 * on each of its source lines, an individual aux send and reverb send. 383 * 384 * @see #AUX_SEND 385 * @see #AUX_RETURN 386 * @see #REVERB_SEND 387 * @see #REVERB_RETURN 388 * @see #VOLUME 389 */ 390 public static final Type MASTER_GAIN = new Type("Master Gain"); 391 392 /** 393 * Represents a control for the auxiliary send gain on a line. 394 * 395 * @see #MASTER_GAIN 396 * @see #AUX_RETURN 397 */ 398 public static final Type AUX_SEND = new Type("AUX Send"); 399 400 /** 401 * Represents a control for the auxiliary return gain on a line. 402 * 403 * @see #MASTER_GAIN 404 * @see #AUX_SEND 405 */ 406 public static final Type AUX_RETURN = new Type("AUX Return"); 407 408 /** 409 * Represents a control for the pre-reverb gain on a line. This control 410 * may be used to affect how much of a line's signal is directed to a 411 * mixer's internal reverberation unit. 412 * 413 * @see #MASTER_GAIN 414 * @see #REVERB_RETURN 415 * @see EnumControl.Type#REVERB 416 */ 417 public static final Type REVERB_SEND = new Type("Reverb Send"); 418 419 /** 420 * Represents a control for the post-reverb gain on a line. This control 421 * may be used to control the relative amplitude of the signal returned 422 * from an internal reverberation unit. 423 * 424 * @see #MASTER_GAIN 425 * @see #REVERB_SEND 426 */ 427 public static final Type REVERB_RETURN = new Type("Reverb Return"); 428 429 /** 430 * Represents a control for the volume on a line. 431 */ 432 /* 433 * $$kk: 08.30.99: ISSUE: what units? linear or dB? 434 */ 435 public static final Type VOLUME = new Type("Volume"); 436 437 /** 438 * Represents a control for the relative pan (left-right positioning) of 439 * the signal. The signal may be mono; the pan setting affects how it is 440 * distributed by the mixer in a stereo mix. The valid range of values 441 * is -1.0 (left channel only) to 1.0 (right channel only). The default 442 * is 0.0 (centered). 443 * 444 * @see #BALANCE 445 */ 446 public static final Type PAN = new Type("Pan"); 447 448 /** 449 * Represents a control for the relative balance of a stereo signal 450 * between two stereo speakers. The valid range of values is -1.0 (left 451 * channel only) to 1.0 (right channel only). The default is 0.0 452 * (centered). 453 * 454 * @see #PAN 455 */ 456 public static final Type BALANCE = new Type("Balance"); 457 458 /** 459 * Represents a control that changes the sample rate of audio playback. 460 * The net effect of changing the sample rate depends on the 461 * relationship between the media's natural rate and the rate that is 462 * set via this control. The natural rate is the sample rate that is 463 * specified in the data line's {@code AudioFormat} object. For example, 464 * if the natural rate of the media is 11025 samples per second and the 465 * sample rate is set to 22050 samples per second, the media will play 466 * back at twice the normal speed. 467 * <p> 468 * Changing the sample rate with this control does not affect the data 469 * line's audio format. Also note that whenever you change a sound's 470 * sample rate, a change in the sound's pitch results. For example, 471 * doubling the sample rate has the effect of doubling the frequencies 472 * in the sound's spectrum, which raises the pitch by an octave. 473 */ 474 public static final Type SAMPLE_RATE = new Type("Sample Rate"); 475 476 /** 477 * Constructs a new float control type. 478 * 479 * @param name the name of the new float control type 480 */ Type(final String name)481 protected Type(final String name) { 482 super(name); 483 } 484 } 485 } 486