Whole document tree Chapter 6Processing Audio with ControlsPrevious chapters have discussed how to play or capture audio samples. The implicit goal has been to deliver samples as faithfully as possible, without modification (other than possibly mixing the samples with those from other audio lines). Sometimes, however, you want to be able to modify the signal. The user might want it to sound louder, quieter, fuller, more reverberant, higher or lower in pitch, and so on. This chapter discusses the JavaTM Sound API features that provide these kinds of signal processing. There are two ways to apply signal processing:
Introduction to Controls
A mixer can have various sorts of signal-processing controls on some or all of its lines. For example, a mixer used for audio capture might have an input port with a gain control, and target data lines with gain and pan controls. A mixer used for audio playback might have sample-rate controls on its source data lines. In each case, the controls are all accessed through methods of the
Because the Others of the mixer's own controls might affect a special line, neither a source nor a target, that the mixer uses internally for its processing. For example, a global reverb control might choose the sort of reverberation to apply to a mixture of the input signals, and this "wet" (reverberated) signal would get mixed back into the "dry" signal before delivery to the mixer's target lines. If the mixer or any of its lines have controls, you might wish to expose the controls via graphical objects in your program's user interface, so that the user can adjust the audio characteristics as desired. The controls are not themselves graphical; they just allow you to retrieve and change their settings. It's up to you to decide what sort of graphical representations (sliders, buttons, etc.), if any, to use in your program.
All controls are implemented as concrete subclasses of the abstract class
The Java Sound API specifies the following abstract subclasses of
Control above has methods appropriate for its underlying data type. Most of the classes include methods that set and get the control's current value(s), get the control's label(s), and so on.
Of course, each class has methods that are particular to it and the data model represented by the class. For example,
Each subclass of
The following table shows each
An implementation of the Java Sound API can provide any or all of these control types on its mixers and lines. It can also supply additional control types not defined in the Java Sound API. Such control types could be implemented via concrete subclasses of any of these four abstract subclasses, or via additional Getting a Line that Has the Desired Controls
In many cases, an application program will simply display whatever controls happen to be supported by the line in question. If the line doesn't have any controls, so be it. But what if it's important to find a line that has certain controls? In that case, you can use a For example, suppose you prefer an input port that lets the user set the volume of the sound input. The following code excerpt shows how one might query the default mixer to determine whether it has the desired port and control:
Getting the Controls from the Line
An application program that needs to expose controls in its user interface might simply query the available lines and controls, and then display an appropriate user-interface element for every control on every line of interest. In such a case, the program's only mission is to provide the user with "handles" on the control; not to know what those controls do to the audio signal. As long as the program knows how to map a line's controls into user-interface elements, the Java Sound API architecture of
For example, suppose your program plays back sound. You're using a Then, for each of the controls in this returned array, you then use the followingControl[] getControls() Control method to get the control's type:
Knowing the specificControl.Type getType() Control.Type instance, your program can display a corresponding user-interface element. Of course, choosing "a corresponding user-interface element" for a specific Control.Type depends on the approach taken by your program. On the one hand, you might use the same kind of element to represent all Control.Type instances of the same class. This would require you to query the class of the Control.Type instance using, for example, the Object.getClass method. Let's say the result matched BooleanControl.Type . In this case, your program might display a generic checkbox or toggle button, but if its class matched FloatControl.Type , then you might display a graphic slider.
On the other hand, your program might distinguish between different types of controls-even those of the same class-and use a different user-interface element for each one. This would require you to test the instance returned by Using a Control to Change the Audio Signal
Now that you know how to access a control and determine its type, this section will describe how to use
Control methods becomes a fairly straightforward matter.
The following subsections describe some of the methods that must be invoked to affect the changes to specific controls. Controlling a Line's Mute State
Controlling the mute state of any line is simply a matter of calling the following (Presumably, the program knows, by referring to its control-management data structures, that the mute is an instance of avoid setValue(boolean value) BooleanControl .) To mute the signal that's passing through the line, the program invokes the method above, specifying true as the value. To turn muting off, permitting the signal to flow through the line, the program invokes the method with the parameter set to false .
Changing a Line's Volume
Let's assume your program associates a particular graphic slider with a particular line's volume control. The value of a volume control (i.e., Detecting that the user moved the slider, the program gets the slider's current value and passes it, as the parametervoid setValue(float newValue) newValue , to the method above. This changes the volume of the signal flowing though the line that "owns" the control.
Selecting among Various Reverberation Presets
Let's suppose that our program has a mixer with a line that has a control of type on that control produces an array ofjava.lang.Objects[] getValues() ReverbType objects. If desired, the particular parameter settings of each of these objects can be accessed using the following ReverbType methods:
For example, if a program only wants a single reverb setting that sounds like a cavern, it can iterate over theint getDecayTime() int getEarlyReflectionDelay() float getEarlyReflectionIntensity() int getLateReflectionDelay() float getLateReflectionIntensity() ReverbType objects until it finds one for which getDecayTime returns a value greater than 2000. For a thorough explanation of these methods, including a table of representative return values, see the API reference documentation for javax.sound.sampled.ReverbType .
Typically, though, a program will create a user-interface element, for example, a radio button, for each of the wherevoid setValue(java.lang.Object value) value is set to the ReverbType that corresponds to the newly engaged button. The audio signal sent through the line that "owns" this EnumControl will then be reverberated according to the parameter settings that constitute the control's current ReverbType (i.e., the particular ReverbType specified in the value argument of the setValue method).
So, from our application program's perspective, enabling a user to move from one reverberation preset (i.e., ReverbType) to another is simply a matter of connecting each element of the array returned by Manipulating the Audio Data Directly
The
If you're processing incoming sound, you can read the bytes from a
To play back processed sound, you can place your manipulated array of bytes into a [Top] [Prev] [Next] [Bottom] Copyright © 2000, Sun Microsystems Inc. All rights reserved. |