1 /*
   2  * Copyright (c) 1999, 2014, 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      */
 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      */
 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      */
 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      */
 214     public float getValue() {
 215         return value;
 216     }
 217 
 218     /**
 219      * Obtains the maximum value permitted.
 220      *
 221      * @return the maximum allowable value
 222      */
 223     public float getMaximum() {
 224         return maximum;
 225     }
 226 
 227     /**
 228      * Obtains the minimum value permitted.
 229      *
 230      * @return the minimum allowable value
 231      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      * Provides a string representation of the control.
 334      *
 335      * @return a string description
 336      */
 337     @Override
 338     public String toString() {
 339         return new String(getType() + " with current value: " + getValue() + " " + units +
 340                           " (range: " + minimum + " - " + maximum + ")");
 341     }
 342 
 343     /**
 344      * An instance of the {@code FloatControl.Type} inner class identifies one
 345      * kind of float control. Static instances are provided for the common
 346      * types.
 347      *
 348      * @author Kara Kytle
 349      * @since 1.3
 350      */
 351     public static class Type extends Control.Type {
 352 
 353         /**
 354          * Represents a control for the overall gain on a line.
 355          * <p>
 356          * Gain is a quantity in decibels (dB) that is added to the intrinsic
 357          * decibel level of the audio signal--that is, the level of the signal
 358          * before it is altered by the gain control. A positive gain amplifies
 359          * (boosts) the signal's volume, and a negative gain attenuates(cuts)it.
 360          * The gain setting defaults to a value of 0.0 dB, meaning the signal's
 361          * loudness is unaffected. Note that gain measures dB, not amplitude.
 362          * The relationship between a gain in decibels and the corresponding
 363          * linear amplitude multiplier is:
 364          *
 365          * <CENTER>{@code linearScalar = pow(10.0, gainDB/20.0)}</CENTER>
 366          * <p>
 367          * The {@code FloatControl} class has methods to impose a maximum and
 368          * minimum allowable value for gain. However, because an audio signal
 369          * might already be at a high amplitude, the maximum setting does not
 370          * guarantee that the signal will be undistorted when the gain is
 371          * applied to it (unless the maximum is zero or negative). To avoid
 372          * numeric overflow from excessively large gain settings, a gain control
 373          * can implement clipping, meaning that the signal's amplitude will be
 374          * limited to the maximum value representable by its audio format,
 375          * instead of wrapping around.
 376          * <p>
 377          * These comments apply to gain controls in general, not just master
 378          * gain controls. A line can have more than one gain control. For
 379          * example, a mixer (which is itself a line) might have a master gain
 380          * control, an auxiliary return control, a reverb return control, and,
 381          * on each of its source lines, an individual aux send and reverb send.
 382          *
 383          * @see #AUX_SEND
 384          * @see #AUX_RETURN
 385          * @see #REVERB_SEND
 386          * @see #REVERB_RETURN
 387          * @see #VOLUME
 388          */
 389         public static final Type MASTER_GAIN            = new Type("Master Gain");
 390 
 391         /**
 392          * Represents a control for the auxiliary send gain on a line.
 393          *
 394          * @see #MASTER_GAIN
 395          * @see #AUX_RETURN
 396          */
 397         public static final Type AUX_SEND                       = new Type("AUX Send");
 398 
 399         /**
 400          * Represents a control for the auxiliary return gain on a line.
 401          *
 402          * @see #MASTER_GAIN
 403          * @see #AUX_SEND
 404          */
 405         public static final Type AUX_RETURN                     = new Type("AUX Return");
 406 
 407         /**
 408          * Represents a control for the pre-reverb gain on a line. This control
 409          * may be used to affect how much of a line's signal is directed to a
 410          * mixer's internal reverberation unit.
 411          *
 412          * @see #MASTER_GAIN
 413          * @see #REVERB_RETURN
 414          * @see EnumControl.Type#REVERB
 415          */
 416         public static final Type REVERB_SEND            = new Type("Reverb Send");
 417 
 418         /**
 419          * Represents a control for the post-reverb gain on a line. This control
 420          * may be used to control the relative amplitude of the signal returned
 421          * from an internal reverberation unit.
 422          *
 423          * @see #MASTER_GAIN
 424          * @see #REVERB_SEND
 425          */
 426         public static final Type REVERB_RETURN          = new Type("Reverb Return");
 427 
 428         /**
 429          * Represents a control for the volume on a line.
 430          */
 431         /*
 432          * $$kk: 08.30.99: ISSUE: what units?  linear or dB?
 433          */
 434         public static final Type VOLUME                         = new Type("Volume");
 435 
 436         /**
 437          * Represents a control for the relative pan (left-right positioning) of
 438          * the signal. The signal may be mono; the pan setting affects how it is
 439          * distributed by the mixer in a stereo mix. The valid range of values
 440          * is -1.0 (left channel only) to 1.0 (right channel only). The default
 441          * is 0.0 (centered).
 442          *
 443          * @see #BALANCE
 444          */
 445         public static final Type PAN                            = new Type("Pan");
 446 
 447         /**
 448          * Represents a control for the relative balance of a stereo signal
 449          * between two stereo speakers. The valid range of values is -1.0 (left
 450          * channel only) to 1.0 (right channel only). The default is 0.0
 451          * (centered).
 452          *
 453          * @see #PAN
 454          */
 455         public static final Type BALANCE                        = new Type("Balance");
 456 
 457         /**
 458          * Represents a control that changes the sample rate of audio playback.
 459          * The net effect of changing the sample rate depends on the
 460          * relationship between the media's natural rate and the rate that is
 461          * set via this control. The natural rate is the sample rate that is
 462          * specified in the data line's {@code AudioFormat} object. For example,
 463          * if the natural rate of the media is 11025 samples per second and the
 464          * sample rate is set to 22050 samples per second, the media will play
 465          * back at twice the normal speed.
 466          * <p>
 467          * Changing the sample rate with this control does not affect the data
 468          * line's audio format. Also note that whenever you change a sound's
 469          * sample rate, a change in the sound's pitch results. For example,
 470          * doubling the sample rate has the effect of doubling the frequencies
 471          * in the sound's spectrum, which raises the pitch by an octave.
 472          */
 473         public static final Type SAMPLE_RATE            = new Type("Sample Rate");
 474 
 475         /**
 476          * Constructs a new float control type.
 477          *
 478          * @param name the name of the new float control type
 479          */
 480         protected Type(final String name) {
 481             super(name);
 482         }
 483     }
 484 }