Java - 从混音器录制

2022-09-03 06:46:28

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

java.lang.IllegalArgumentException: Line unsupport: 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.length;
                                          cnt++){
        System.out.println(mixerInfo[cnt].
                                      getName());
      }//end for loop

      //Get everything set up for capture
      audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_UNSIGNED, 44100.0F, 8, 1, 4, 44100.0F,
                        false);


      DataLine.Info dataLineInfo =
                            new DataLine.Info(
                            TargetDataLine.class,
                            audioFormat);
      ListMixers lm = new ListMixers();
      lm.listAll(new PrintWriter(System.out));

      System.out.println(" AKTUALNY => "+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

答案 1

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

就个人而言,我发现这很麻烦。您需要查询系统上的混音器,以确定它们是否支持所需的音频格式。

下面的函数将获得数据行类的受支持格式的 Vector。调用它使用

Vector<AudioFormat> formats = getSupportedFormats(TargetDataLine.class);

Vector<AudioFormat> formats = getSupportedFormats(SourceDataLine.class);

此代码可能需要一些调试;我不得不删除一些特定于应用程序的内容,以使其独立...

public Vector<AudioFormat> 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<AudioFormat> formats = new Vector<AudioFormat>();

    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;
}

答案 2

可以直接获得所有支持和它们。我为系统上的默认值执行此操作,您可以轻松编辑代码以获取任何类型的代码并得到任何.LinesAudioFormatsSourceDataLinesMixerLinesAudioFormatsMixer

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<String,Object> prop = format.properties();
//          if(!prop.isEmpty()) {
//              System.out.println("      Properties: ");
//              for(Map.Entry<String, Object> 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

推荐