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