-
Notifications
You must be signed in to change notification settings - Fork 300
Microphone Selection
JARVIS uses the Java Sound API to record audio. By default, the Microphone Class (and, consequently, all subclasses) will use the Default System Microphone as determined by the JRE implementation. As such, in 99% of scenarios, selecting the microphone is unnecessary. If you ever do want to select a microphone (due to say a specific implementation on Linux or adding a front end feature for the end user), just use these easy steps to do so.
The a TargetDataLine is a stream of audioData from an audio input device. The AudioSystem provides the default TargetDataLine based off which microphone can support the specified audio format and the default system microphone. To generate a TargetDataLine from a specific audio source, one has to use a Line.Info to specify where the TargetDataLine should originate from. Here is some code to generate a TargetDataLine from that info.
public static TargetDataLine getTargetDataLine(Line.Info lineInfo){
try {
return (TargetDataLine) AudioSystem.getLine(lineInfo);
} catch (LineUnavailableException ex) {//If the line is unavailable, it returns
ex.printStackTrace();
return null;
}
}
To set the TargetDataLine for the Microphone class or any subclasses use. Microphone.setTargetDataLine(TargetDataLine);
The following section will cover how to determine what the LineInfo of the specified data is.
First, if you are selecting a specific microphone, you will need to know the properties of said microphone. Here is the basic code to list all the Microphones properties. Doing so well allow you to manually grab the data from the Microphone in a format the hardware supports. You cannot get a higher sampling rate than the Microphone allows for instance. Here is some code fragments that will print out the properties of all the lines available on your system.
//Enumerates all available microphones
Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();
for (Mixer.Info info: mixerInfos){
Mixer m = AudioSystem.getMixer(info);
Line.Info[] lineInfos = m.getTargetLineInfo();
if(lineInfos.length>=1 && lineInfos[0].getLineClass().equals(TargetDataLine.class)){//Only prints out info is it is a Microphone
System.out.println("Line Name: " + info.getName());//The name of the AudioDevice
System.out.println("Line Description: " + info.getDescription());//The type of audio device
for (Line.Info lineInfo:lineInfos){
System.out.println ("\t"+"---"+lineInfo);
Line line;
try {
line = m.getLine(lineInfo);
} catch (LineUnavailableException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return;
}
System.out.println("\t-----"+line);
}
}
}
The way most operating systems handle the Audio Device is by using it's name (specified by Mixer.getName()). So if you are looking for a specific microphone, that is the output you should pay attention to. In the event you want user interaction of this data, you will need to transform it in some way.
There are several manners to present this data to the user. I would recommend using a HashMap and then adding the KeySet() to a non-editable ComboBox. Then add a itemListener to change the TargetDataLine using Microphone.setTargetDataLine(TargetDataLine)
. Here is a method that generates the HashMap.
/**
* Generates a hashmap to simplify the microphone selection process.
* The keyset is the name of the audio device's Mixer
* The value is the first lineInfo from that Mixer.
* @author Aaron Gokaslan (Skylion)
* @return The generated hashmap
*/
public static HashMap<String, Line.Info> enumerateMicrophones(){
HashMap<String, Line.Info> out = new HashMap<String, Line.Info>();
Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();
for (Mixer.Info info: mixerInfos){
Mixer m = AudioSystem.getMixer(info);
Line.Info[] lineInfos = m.getTargetLineInfo();
if(lineInfos.length>=1 && lineInfos[0].getLineClass().equals(TargetDataLine.class)//Only adds to hashmap if it is audio input device
out.put(info.getName(), lineInfos[0]);//Please enjoy my pun
}
return out;
}
Such a hashmap simplifies the microphone selection process for the end user. Feel free to add any implementations of microphone selection processes to this wiki.
The following is an example of an extension of the JPanel which allows the user to choose a specific mixer. The TargetDataLine can then be obtained from the mixer. Instead of using a JCombobox, this JPanel uses radio buttons.
/**
* This is an input panel specifically designed to create a selection method for each microphone.
* In this case, a microphone is defined as mixer and not a specific target data line for sake of
* end user simplicity. This class extends JPanel and fires property change listeners when the
* user selects a new mixer.
* @author Aaron Gokaslan
* This class was inspired by TarsosDSP.
*/
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Line;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.TargetDataLine;
import javax.swing.ButtonGroup;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.border.TitledBorder;
public class MicrophonePanel extends JPanel {
/**
* Auto-generated Serial Long
*/
private static final long serialVersionUID = 1L;
Mixer mixer = null;
public MicrophonePanel(){
super(new BorderLayout());
this.setBorder(new TitledBorder("1. Choose a microphone input"));
JPanel buttonPanel = new JPanel(new GridLayout(0,1));
ButtonGroup group = new ButtonGroup();
Mixer.Info[] mixerInfos = AudioSystem.getMixerInfo();
for (Mixer.Info info: mixerInfos){
Mixer m = AudioSystem.getMixer(info);
Line.Info[] lineInfos = m.getTargetLineInfo();
if(lineInfos.length > 0 && lineInfos[0].getLineClass().equals(TargetDataLine.class)){
JRadioButton button = new JRadioButton();
button.setText(info.getName());
button.setActionCommand(info.toString());
button.addActionListener(setInput);
buttonPanel.add(button);
group.add(button);
}
}
this.add(new JScrollPane(buttonPanel,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER),BorderLayout.CENTER);
this.setMaximumSize(new Dimension(300,150));
this.setPreferredSize(new Dimension(300,150));
}
private ActionListener setInput = new ActionListener(){
@Override
public void actionPerformed(ActionEvent arg0) {
for(Mixer.Info info : AudioSystem.getMixerInfo()){
if(arg0.getActionCommand().equals(info.toString())){
Mixer newValue = AudioSystem.getMixer(info);
MicrophonePanel.this.firePropertyChange("mixer", mixer, newValue);
MicrophonePanel.this.mixer = newValue;
break;
}
}
}
};
//Example Method
public static void main(String[] args){
JFrame frame = new JFrame("Microphone Selection Test");
MicrophonePanel panel = new MicrophonePanel();
frame.getContentPane().add(panel);
frame.setVisible(true);;
frame.setLocationRelativeTo(null);
frame.pack();
}
}