Java – 从混音器录制

我有一个问题与我之前的问题有关。 我想从混音器(扬声器)录制音频,我正在使用javax.sound。 我必须设置audioFormat,我不知道在那里键入什么:/使用类ListMixer(我在这里找到 – > http://forums.oracle.com/forums/thread.jspa?threadID=2198477&tstart=2 ) ,我写这样的东西: http : //forums.oracle.com/forums/thread.jspa?threadID = 21848477&tstart = 2但我没有任何关于采样率的信息(未知采样率)。 程序抛出此exception:

java.lang.IllegalArgumentException:Line unsupported:interface TargetDataLine支持格式PCM_UNSIGNED 44100.0 Hz,8位,单声道,4字节/帧,

码:

package sound; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.io.*; import javax.sound.sampled.*; public class AudioCapture02 extends JFrame{ boolean stopCapture = false; ByteArrayOutputStream byteArrayOutputStream; AudioFormat audioFormat; TargetDataLine targetDataLine; AudioInputStream audioInputStream; SourceDataLine sourceDataLine; public AudioCapture02(){//constructor final JButton captureBtn = new JButton("Capture"); final JButton stopBtn = new JButton("Stop"); final JButton playBtn = new JButton("Playback"); captureBtn.setEnabled(true); stopBtn.setEnabled(false); playBtn.setEnabled(false); //Register anonymous listeners captureBtn.addActionListener( new ActionListener(){ public void actionPerformed( ActionEvent e){ captureBtn.setEnabled(false); stopBtn.setEnabled(true); playBtn.setEnabled(false); //Capture input data from the // microphone until the Stop button is // clicked. captureAudio(); }//end actionPerformed }//end ActionListener );//end addActionListener() getContentPane().add(captureBtn); stopBtn.addActionListener( new ActionListener(){ public void actionPerformed( ActionEvent e){ captureBtn.setEnabled(true); stopBtn.setEnabled(false); playBtn.setEnabled(true); //Terminate the capturing of input data // from the microphone. stopCapture = true; }//end actionPerformed }//end ActionListener );//end addActionListener() getContentPane().add(stopBtn); playBtn.addActionListener( new ActionListener(){ public void actionPerformed( ActionEvent e){ //Play back all of the data that was // saved during capture. playAudio(); }//end actionPerformed }//end ActionListener );//end addActionListener() getContentPane().add(playBtn); getContentPane().setLayout(new FlowLayout()); setTitle("Capture/Playback Demo"); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(250,70); setVisible(true); }//end constructor //This method captures audio input from a // microphone and saves it in a // ByteArrayOutputStream object. private void captureAudio(){ try{ //Get and display a list of // available mixers. Mixer.Info[] mixerInfo = AudioSystem.getMixerInfo(); System.out.println("Available mixers:"); for(int cnt = 0; cnt  "+mixerInfo[0].getName()); Mixer mixer = AudioSystem.getMixer(mixerInfo[0]); //Get a TargetDataLine on the selected // mixer. targetDataLine = (TargetDataLine) mixer.getLine(dataLineInfo); //Prepare the line for use. targetDataLine.open(audioFormat); targetDataLine.start(); //Create a thread to capture the microphone // data and start it running. It will run // until the Stop button is clicked. Thread captureThread = new CaptureThread(); captureThread.start(); } catch (Exception e) { System.out.println(e); System.exit(0); }//end catch }//end captureAudio method //This method plays back the audio data that // has been saved in the ByteArrayOutputStream private void playAudio() { try{ //Get everything set up for playback. //Get the previously-saved data into a byte // array object. byte audioData[] = byteArrayOutputStream. toByteArray(); //Get an input stream on the byte array // containing the data InputStream byteArrayInputStream = new ByteArrayInputStream(audioData); AudioFormat audioFormat = getAudioFormat(); audioInputStream = new AudioInputStream( byteArrayInputStream, audioFormat, audioData.length/audioFormat. getFrameSize()); DataLine.Info dataLineInfo = new DataLine.Info( SourceDataLine.class, audioFormat); sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo); sourceDataLine.open(audioFormat); sourceDataLine.start(); //Create a thread to play back the data and // start it running. It will run until // all the data has been played back. Thread playThread = new PlayThread(); playThread.start(); } catch (Exception e) { System.out.println(e); System.exit(0); }//end catch }//end playAudio //This method creates and returns an // AudioFormat object for a given set of format // parameters. If these parameters don't work // well for you, try some of the other // allowable parameter values, which are shown // in comments following the declartions. private AudioFormat getAudioFormat(){ float sampleRate = 8000.0F; //8000,11025,16000,22050,44100 int sampleSizeInBits = 16; //8,16 int channels = 1; //1,2 boolean signed = true; //true,false boolean bigEndian = false; //true,false return new AudioFormat( sampleRate, sampleSizeInBits, channels, signed, bigEndian); }//end getAudioFormat //=============================================// //Inner class to capture data from microphone class CaptureThread extends Thread{ //An arbitrary-size temporary holding buffer byte tempBuffer[] = new byte[10000]; public void run(){ byteArrayOutputStream = new ByteArrayOutputStream(); stopCapture = false; try{//Loop until stopCapture is set by // another thread that services the Stop // button. while(!stopCapture){ //Read data from the internal buffer of // the data line. int cnt = targetDataLine.read(tempBuffer, 0, tempBuffer.length); if(cnt > 0){ //Save data in output stream object. byteArrayOutputStream.write(tempBuffer, 0, cnt); }//end if }//end while byteArrayOutputStream.close(); }catch (Exception e) { System.out.println(e); System.exit(0); }//end catch }//end run }//end inner class CaptureThread //===================================// //Inner class to play back the data // that was saved. class PlayThread extends Thread{ byte tempBuffer[] = new byte[10000]; public void run(){ try{ int cnt; //Keep looping until the input read method // returns -1 for empty stream. while((cnt = audioInputStream.read( tempBuffer, 0, tempBuffer.length)) != -1){ if(cnt > 0){ //Write data to the internal buffer of // the data line where it will be // delivered to the speaker. sourceDataLine.write(tempBuffer,0,cnt); }//end if }//end while //Block and wait for internal buffer of the // data line to empty. sourceDataLine.drain(); sourceDataLine.close(); }catch (Exception e) { System.out.println(e); System.exit(0); }//end catch }//end run }//end inner class PlayThread //=============================================// class ListMixers { PrintWriter out; void listAll(final PrintWriter out) { this.out = out; Mixer.Info[] aInfos = AudioSystem.getMixerInfo(); for (int i = 0; i < aInfos.length; i++) { try { Mixer mixer = AudioSystem.getMixer(aInfos[i]); out.println(""+i+": "+aInfos[i].getName()+", " +aInfos[i].getVendor()+", " +aInfos[i].getVersion()+", " +aInfos[i].getDescription()); printLines(mixer, mixer.getSourceLineInfo()); printLines(mixer, mixer.getTargetLineInfo()); } catch (Exception e) { out.println("Exception: "+e); } out.println(); } if (aInfos.length == 0) { out.println("[No mixers available]"); } } void printLines(Mixer mixer, Line.Info[] infos) { for (int i = 0; i < infos.length; i++) { try { if (infos[i] instanceof Port.Info) { Port.Info info = (Port.Info) infos[i]; out.println(" Port " + info); } if (infos[i] instanceof DataLine.Info) { DataLine.Info info = (DataLine.Info) infos[i]; out.println(" Line " + info + " (max. " + mixer.getMaxLines(info) + " simultaneously): "); printFormats(info); } Line line = mixer.getLine(infos[i]); if (!(line instanceof Clip)) { try { line.open(); } catch (LineUnavailableException e) { out.println("LineUnavailableException when trying to open this line"); } } try { printControls(line.getControls()); } finally { if (!(line instanceof Clip)) { line.close(); } } } catch (Exception e) { out.println("Exception: " + e); } out.println(); } } void printFormats(DataLine.Info info) { AudioFormat[] formats = info.getFormats(); for (int i = 0; i < formats.length; i++) { out.println(" "+i+": "+formats[i] +" ("+formats[i].getChannels()+" channels, " +"frameSize="+formats[i].getFrameSize()+", " +(formats[i].isBigEndian()?"big endian":"little endian") +")"); } if (formats.length == 0) { out.println(" [no formats]"); } out.println(); } void printControls(Control[] controls) { for (int i = 0; i<controls.length; i++) { printControl(" ", "Controls["+i+"]: ", controls[i]); } if (controls.length == 0) { out.println(" [no controls]"); } out.println(); } void printControl(String indent, String id, Control control) { if (control instanceof BooleanControl) { BooleanControl ctrl = (BooleanControl) control; out.println(indent+id+"BooleanControl: "+ctrl); } else if (control instanceof CompoundControl) { CompoundControl ctrl = (CompoundControl) control; Control[] ctrls = ctrl.getMemberControls(); out.println(indent+id+"CompoundControl: "+control); for (int i=0; i<ctrls.length; i++) { printControl(indent+" ", "MemberControls["+i+"]: ", ctrls[i]); } } else if (control instanceof EnumControl) { EnumControl ctrl = (EnumControl) control; Object[] values = ctrl.getValues(); Object value = ctrl.getValue(); out.println(indent+id+"EnumControl: "+control); for (int i=0; i<values.length; i++) { if (values[i] instanceof Control) { printControl(indent+" ", "Values["+i+"]: "+((values[i]==value)?"*":""), (Control) values[i]); } else { out.println(indent+" Values["+i+"]: "+((values[i]==value)?"*":"")+values[i]); } } } else if (control instanceof FloatControl) { FloatControl ctrl = (FloatControl) control; out.println(indent+id+"FloatControl: "+ctrl); } else { out.println(indent+id+"Control: "+control); } } } }//end outer class AudioCapture02.java 

您正在使用您创建的AudioFormat获取TargetDataLine。 这不能保证有效。 您必须首先使用AudioSystem.isLineSupported(Info info)方法查询混音器以检查它是否支持您所需的AudioFormat。

就个人而言,我觉得这很麻烦。 您需要查询系统上的混音器以确定它们是否支持您想要的AudioFormat。

下面的函数将获得数据行类支持格式的Vector。 用它来称呼它

 Vector formats = getSupportedFormats(TargetDataLine.class); 

要么

 Vector formats = getSupportedFormats(SourceDataLine.class); 

这段代码可能需要一些调试; 我不得不删除一些特定于应用程序的东西以使其自包含…

 public Vector getSupportedFormats(Class dataLineClass) { /* * These define our criteria when searching for formats supported * by Mixers on the system. */ float sampleRates[] = { (float) 8000.0, (float) 16000.0, (float) 44100.0 }; int channels[] = { 1, 2 }; int bytesPerSample[] = { 2 }; AudioFormat format; DataLine.Info lineInfo; SystemAudioProfile profile = new SystemAudioProfile(); // Used for allocating MixerDetails below. Vector formats = new Vector(); for (Mixer.Info mixerInfo : AudioSystem.getMixerInfo()) { for (int a = 0; a < sampleRates.length; a++) { for (int b = 0; b < channels.length; b++) { for (int c = 0; c < bytesPerSample.length; c++) { format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, sampleRates[a], 8 * bytesPerSample[c], channels[b], bytesPerSample[c], sampleRates[a], false); lineInfo = new DataLine.Info(dataLineClass, format); if (AudioSystem.isLineSupported(lineInfo)) { /* * TODO: To perform an exhaustive search on supported lines, we should open * TODO: each Mixer and get the supported lines. Do this if this approach * TODO: doesn't give decent results. For the moment, we just work with whatever * TODO: the unopened mixers tell us. */ if (AudioSystem.getMixer(mixerInfo).isLineSupported(lineInfo)) { formats.add(format); } } } } } } return formats; } 

可以直接获取所有支持的Lines及其AudioFormats 。 我为系统上默认的MixerSourceDataLines做了这个,你可以轻松编辑代码以获得任何Mixer支持的任何类型的LinesAudioFormats

 Mixer mixer = AudioSystem.getMixer(null); // default mixer mixer.open(); System.out.printf("Supported SourceDataLines of default mixer (%s):\n\n", mixer.getMixerInfo().getName()); for(Line.Info info : mixer.getSourceLineInfo()) { if(SourceDataLine.class.isAssignableFrom(info.getLineClass())) { SourceDataLine.Info info2 = (SourceDataLine.Info) info; System.out.println(info2); System.out.printf(" max buffer size: \t%d\n", info2.getMaxBufferSize()); System.out.printf(" min buffer size: \t%d\n", info2.getMinBufferSize()); AudioFormat[] formats = info2.getFormats(); System.out.println(" Supported Audio formats: "); for(AudioFormat format : formats) { System.out.println(" "+format); // System.out.printf(" encoding: %s\n", format.getEncoding()); // System.out.printf(" channels: %d\n", format.getChannels()); // System.out.printf(format.getFrameRate()==-1?"":" frame rate [1/s]: %s\n", format.getFrameRate()); // System.out.printf(" frame size [bytes]: %d\n", format.getFrameSize()); // System.out.printf(format.getSampleRate()==-1?"":" sample rate [1/s]: %s\n", format.getSampleRate()); // System.out.printf(" sample size [bit]: %d\n", format.getSampleSizeInBits()); // System.out.printf(" big endian: %b\n", format.isBigEndian()); // // Map prop = format.properties(); // if(!prop.isEmpty()) { // System.out.println(" Properties: "); // for(Map.Entry entry : prop.entrySet()) { // System.out.printf(" %s: \t%s\n", entry.getKey(), entry.getValue()); // } // } } System.out.println(); } else { System.out.println(info.toString()); } System.out.println(); } mixer.close(); 

我得到这样的输出:

 interface SourceDataLine supporting 8 audio formats, and buffers of at least 32 bytes max buffer size: -1 min buffer size: 32 Supported Audio formats: PCM_UNSIGNED unknown sample rate, 8 bit, mono, 1 bytes/frame, PCM_SIGNED unknown sample rate, 8 bit, mono, 1 bytes/frame, PCM_SIGNED unknown sample rate, 16 bit, mono, 2 bytes/frame, little-endian PCM_SIGNED unknown sample rate, 16 bit, mono, 2 bytes/frame, big-endian PCM_UNSIGNED unknown sample rate, 8 bit, stereo, 2 bytes/frame, PCM_SIGNED unknown sample rate, 8 bit, stereo, 2 bytes/frame, PCM_SIGNED unknown sample rate, 16 bit, stereo, 4 bytes/frame, little-endian PCM_SIGNED unknown sample rate, 16 bit, stereo, 4 bytes/frame, big-endian interface Clip supporting 8 audio formats, and buffers of at least 32 bytes