Java Synthesizer, Part 14 – Mixers


Ok, I sat down and spent half a day messing with the Mixer class.  Big disappointment.

If we go back to Dick Baldwin’s tutorials, the one on specifying a mixer does a pretty good job at laying out what we need to explicitly specify a mixer for use in the sound engine. Granted, his example uses a TargetDataLine for capturing data from a microphone and I need a SourceDataLine for sending to the speakers, but the premise is the same. The issue is just one of which tweaks are needed to change the code over.

Looking at the setup for the engine, the old code was:

private void startSend2SpeakersListener() {

try {

InputStream baStream       = new ByteArrayInputStream(audioData);
audioFormat                = new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);
audioInputStream           = new AudioInputStream(baStream, audioFormat, audioData.length/audioFormat.getFrameSize());
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);
speakerLine                = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
speakerLine.open(audioFormat);
speakerLine.start();
new sendToSpeakers().start();                                           // Start listener
}
catch(Exception ex) {
jTextArea1.append(“startPlayDataListener Exception: \n” + ex + “\n”);
}
}

And the new code is:

private void startSend2SpeakersListener() {
try {
Mixer.Info[] mixerInfo     = AudioSystem.getMixerInfo();                    // Get the list of all Mixers on the system.
Mixer        mixer         = AudioSystem.getMixer(mixerInfo[0]);            // On my system, only mixers 0 and 1 work, out of 6.
InputStream baStream       = new ByteArrayInputStream(audioData);
audioFormat                = new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);
audioInputStream           = new AudioInputStream(baStream, audioFormat, audioData.length/audioFormat.getFrameSize());
DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat);

speakerLine                = (SourceDataLine) mixer.getLine(dataLineInfo);  // Explicitly call mixer 0.
speakerLine.open(audioFormat);                                              // Open the source line using mixer 0
speakerLine.start();                                                        // Start the source line for receiving data
new sendToSpeakers().start();                                               // Start listener
}
catch(Exception ex) {
jTextArea1.append(“startPlayDataListener Exception: \n” + ex + “\n”);
}
}

First, I make a call to the AudioSystem to get the mixer info object. I have 6 mixers that show up when printing the names out from the array. 3 are targets (connections to the mike jack and the sound card input line), and one is some weird source driver that doesn’t support the audio format I’ve selected. Only mixers 0 and 1 work with my app, and they behave pretty much the same. I’m disguising it, but there are actually 2 source lines per source mixer – one dedicated to SourceDataLine objects, and one for SourceClip objects. If I was writing an MP3 player, I’d be using the SourceClip.class for obtaining the data line.  However, since I am using DataLine.Info, it’s making the choice for me as to how to grab the correct Line object, and the specification of SourceDataLine.class tells the constructor which of the two types of lines to get for me. (You can look at my test program data dump for the actual output.)

Next, I assign the speakerLine object from mixer.getLine() instead of AudioSystem.getLine(). Otherwise, the two versions are very similar. The reason for going through the extra effort is to get at the line controls. According to Baldwin, different mixers have controls for things like volume, balance, pan and reverb. I wanted to see if there was one for waveform smoothing, or whatever is introducing the clicks when the frequency changes too fast. And I wanted to see if changing mixers would make a difference. The way to get to the controls is by getting the mixer line, make sure it’s not for SourceClip, open it, and use line.getControls. When you’re done, be sure to close the line.

mixInfo = mixer.getSourceLineInfo();
Line line = mixer.getLine(mixInfo[i]);
if(! line.toString().toLowerCase().contains(“clip”)) {
line.open();
}
jTextArea1.append(”      Line name: ” + line + “\n”);
control = line.getControls();
jTextArea1.append(”         Number of Line Controls: ” + control.length + “\n”);
for(int j=0;  j <control .length; j++) {
jTextArea1.append(”         ” + (j + 1) + “: Control” + control[j].toString() + “\n”);
}
if(line.isOpen()) line.close();

To use the control –

if (line.isControlSupported(FloatControl.Type.MASTER_GAIN)) {
FloatControl volume = (FloatControl) dataLine.getControl(FloatControl.Type.MASTER_GAIN);
volume.setValue(6.0F);
}

And now we start really running into the limitations. On my PC, mixer 0 line 0 only supports controls for volume, master gain, balance and mute. However, I can’t access line 0 directly, I have to use the call to the dataline with (SourceDataLine) mixer.getLine(dataLineInfo); because Java demands to abstract it for me, and this strips out the controls for volume and balance, making only master gain and mute available to the line.getControls(); method. And, Java’s enum list for FloatControl.Type. doesn’t include a value for mute.  So, between Java and the  sound drivers on my PC, the only mixer control I can access is master gain.  Which has nothing to do with solving my problem of clicking in the waveform.

I would say that the entire exercise of trying to explicitly call a mixer was a waste of time and then rip all the new code out, except that it’s only 3 lines and I may buy a new PC in the future with a better sound card. Plus, the extra 3 lines of code have no effect on the behavior of the app, so I’ll leave them in place for now, and keep the old code as backup just in case.  Regardless, I’m running out of options for getting rid of that stupid clicking…

Raw formatted textfile.
Test Program Data Dump.

 

Advertisements
Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: