我想捕获播放软件发送到声卡音频数据流,请问如何实现?

解决方案 »

  1.   

     在本章中,我们将逐步完成录制音频文件的过程。我们将使用类似于   IAudioPlayer   接口,称为   IAudioRecorder   的接口。   
      IAudioRecorder   接口非常象播放音频中演示的   IAudioPlayer,但它用于录制而不是回放。   
      与   IAudioPlayer   一样,IAudioRecorder   接口仅包含很通用的方法(例如,startRecording、stopRecording   和   save),   
      因此简化了使用并封装了特定于   Java   Sound   的功能。   
        
      虽然关于播放音频的一章与关于录制的本章之间有许多相似之处,但在实现方面却有许多很重要的不同之处。   
      首先,没有简单的对象能象   Clip   简单地播放音频那样来录制声音。   
      结果,我们不得不直接同   I/O   流、线路、字节数组和音频数据转换等打交道。   
      在   AudioPlayer   示例中,我们学习了如何从给定的声音文件中检索音频格式。   
      然而在   AudioRecorder(IAudioRecorder   的基本实现)中,我们控制用于录制实际音频的设置。   
      由于一旦录制了音频就难以更改音频格式,所以这需要更多地关注音频设置。   
      每当需要弄清音频术语或概念时,请您参考数字音频速成课程。   
        
        
      */   
        
        
      /*   
      接口   第   2   页(共12   页)     
        
        
        
        
      下面介绍   AudioRecorder   接口。AudioRecorder   接口与   AudioPlayer   之间有一点不同,   
      不同之处在于这个类需要考虑音频格式。对于   AudioPlayer,根据从读入的音频数据中检索的格式,   
      从   AudioSystem   类检索线路。   
      然而,在音频录制时,AudioRecorder   将控制录制音频所用的格式。   
        
      下面是   AudioRecorder   接口代码:   
      */   
        
        
        
        
      public   interface   IAudioRecorder     
      {   
        
      /*   start   recording   */     
      public   void   startRecording();   
        
      /*   stop   recording   */   
      public   void   stopRecording();   
        
      /*   reset   all   audio   buffers,   clear   recording   */   
      public   void   reset();   
        
      /*   set   AudioFormat   used   to   record   the   audio   */   
      public   void   setAudioFormat();   
        
      /*   retrieve   audio   format   used   to   record   the   audio   */   
      public   AudioFormat   getAudioFormat();   
        
      /*   set   the   default   audio   format,   non-compressed     
            CD-quality   settings   */   
      public   void   setDefaultAudioFormat();   
        
      /*   save   the   cached   recording   to   a   file   */   
      public   boolean     
          saveToFile(File   file,   AudioFileFormat.Type   fileType);   
      }   
      /*   
          
      标准音频设置   第   3   页(共12   页)     
        
        
        
        
      对于缺省   AudioFormat,我们将使用   CD   音频设置和   WAV   文件格式以使示例简单。   
        
      下面是缺省音频格式的规范:   
        
      编码:带正负号的   PCM   
        
        
      采样率:44.1   kHz(CD   音质)   
        
        
      采样大小:16   位或两个   8   位字节(CD   音质)   
        
        
      声道数:两个或立体声(CD   音质)   
        
        
      帧大小:32   位或   4   字节(16   位采样大小   *   2   声道   =   每字节   32   位/8   位每字节   =   4   字节)   
        
        
      帧速率:44.1   kHz(采样率)   
        
        
      大尾数法:否     
      接下来,我们将为   IAudioRecorder   接口中的缺省音频格式声明   public   static   final   变量。下面是格式:   
          
      */   
      public   static   final   AudioFormat   DEFAULT_AUDIO_FORMAT   =   new   AudioFormat   
      (   
      //Encoding   
      AudioFormat.Encoding.PCM_SIGNED,       
        
      //Sample   rate   
      44100f,       
        
      //Sample   size   in   bytes   
      2,       
        
      //Number   of   channels   
      2,     
        
      //Frame   size     
      4,     
        
      //Frame   rate   
      44100f,       
        
      //Big   endian   
      false     
      );   
      /*   
      除了我们必须实例化一个   AudioFormat   对象,而不是从   AudioInputStream   检索它之外,   
      检索输入   Line   的过程与检索   Clip   的过程相同。要检索一个输入   Line,请完成下列步骤:   
        
      实例化   AudioFormat   对象。   
        
        
      实例化   DataLine.Info   对象,该对象引用适当的   AudioFormat   并指定请求的   Line   类型。   
        
        
      从   AudioSystem   请求   Line。     
      与   AudioPlayer   示例相同,我们将请求一个   TargetDataLine   实例来接收音频输入而不是直接实例化   TargetDataLine。   
      在有了   TargetDataLine   实例之后,需要打开它以完成初始化。   
      与回放示例相同,AudioSystem   类返回一个缺少内容的容器。   
      另外,AudioRecorder   被设计成除非被覆盖,否则使用   DEFAULT_AUDIO_FORMAT。   
      下面是代码:   
        
      */   
      //Create   a   DataLine.Info   object   with   a   TargetDataLine   and   the   default   format   
        
      DataLine.Info   info   =   new   DataLine.Info(TargetDataLine.class,   format);   
      //Request   the   line   from   the   AudioSystem   
        
      line   =   (TargetDataLine)   AudioSystem.getLine(info);   
      //Open   the   line   to   complete   the   instantiation   
        
      line.open(format,   line.getBufferSize());   
      /*   
      TargetDataLine   中的读方法   第   5   页(共12   页)     
      

  2.   

        
      一旦提供了音频,我们就需要使用   TargetDataLine   接口中的   read   方法读取并储存音频数据。   
      让我们查看   read   方法以了解如何捕获音频输入:   
        
        
      public   int   read(byte[]   b,   int   offset,   int   length)   
        
        
      byte[]   b:音频数据输入被写入这个   byte[]。   
      状态   int   是由   read   方法返回的,因此必须传入   byte[]   以使它只具有单个返回类型。   
        
      int   offset:指定数组内的偏移量以开始写音频。   
      当在缓冲区中已有数据并且您想将新数据添加到已有数据的结尾时,可以使用偏移量。   
        
      对于我们简单的音频录制器用途,偏移量将始终为零。   
      这将新捕获的音频数据放在缓冲区的开始处。   
        
      int   length:指定从音频输入读取的字节数。在从输入读取   length   个字节以前,该方法会一直阻塞。   
      因此,read   方法应该在其自己的线程中执行;否则,将冻结该接口。   
      假设   AudioRecorder   被正确线程化(但我不会详细讨论线程化,因为它超出本教程的范围)。   
      检查示例源代码(可以在参考资料中获取)以了解更多信息。   
        
      */   
      /*   
                
      准备检索的字节数组   第   6   页(共12   页)     
        
        
        
        
      首先,我们需要计算传入   read   方法中的   byte   数组的大小。这是通过下列计算完成的:   
        
        
      //Frame   size   
      int   frameSizeInBytes   =   format.getFrameSize();     
        
      //The   number   of   frames   =   the   size   of   the   buffer   
      int   bufferLengthInFrames   =   line.getBufferSize()   /   8;     
        
      //Number   of   frames   *   frame   size   =   total   bytes   
      int   bufferLengthInBytes   =   bufferLengthInFrames   *   frameSizeInBytes;     
        
      //Make   a   new   byte[]   the   size   of   the   AudioBuffer   
      byte[]   data   =   new   byte[bufferLengthInBytes];   
        
        
      接下来,我们实例化一个输出流以将   byte   数组写入。   
      用这种方法,如果我们进行后续读取,则可以写入到输出流而不需要为数据管理编写更多代码。   
        
        
      ByteArrayOutputStream   out   =   new   ByteArrayOutputStream();   
        
          
      */   
      /*   
              
      读取音频数据并对其进行高速缓存   第   7   页(共12   页)     
        
        
        
        
      现在,我们已经准备好读入音频。可以通过调用   TargetDataLine   中的   read   方法读取音频数据。   
      read   方法在   while   循环中,因此我们可以连续地录制而不用创建一个非常大的   byte[]。   
      从   TargetDataLine   中读取的数据将被写到   ByteArrayOutputStream   进行存储。   
      如果   read   方法返回   -1(表示错误),则我们还需要执行检查以中断循环。   
      下面是代码:   
        
        
      int   numBytesRead;   //Cache   the   number   of   bytes   we   have   read   
      while   (recording)   
      {   
      //Read   in   some   data   from   the   line   
      //Read   will   return   -1   if   it   encounters   errors   
      if((numBytesRead   =   line.read(data,   0,   bufferLengthInBytes))   ==   -1)   
      {   
      break;   
      }   
        
      //Once   we   read   in   the   data,   we'll   write   it   to   an     
      //audio   output   stream   
      out.write(data,   0,   numBytesRead);   
      }   
      */   
      /*   
          
      将   ByteArrayOutputStream   转换成   AudioInputStream   第   8   页(共12   页)     
        
        
        
        
      接下来,我们将把   ByteArrayOutputStream   转换成   AudioInputStream。这样做有两个原因:   
        
      将音频保存到一个文件     
      将音频缓冲区保存到文件之前回放它     
      下面是用于转换的代码:   
        
        
      //Convert   the   output   stream   back   to   a   byte[]   
      byte[]   audioBytes   =   out.toByteArray();     
        
      //Make   a   ByteArrayInputStream   from   the   byte[]   
      ByteArrayInputStream   bais   =   new   ByteArrayInputStream(audioBytes);   
        
      //Request   an   AudioInputStream   from   the   AudioSystem     
      //passing   in   the   byte   stream,   audio   format,   and   length   
      audioInputStream   =     
          new   AudioInputStream(bais,   format,   audioBytes.length/frameSizeInBytes);   
        
          
        
          
      */   
      /*   
          
      将音频缓冲区保存到文件   第   9   页(共12   页)     
        
        
        
        
      既然我们已经有了   AudioInputStream,则我们可以调用   AudioSystem   中的   write   方法以将缓冲区保存到文件。   
        
      在保存该文件之前,我们需要指定   AudioFileFormat。AudioFileFormat   包含下列信息:   
        
      AudioFormat     
      AudioFileFormat.Type     
      AudioFileFormat.Type   是   AudioFileFormat   的一个内部类,它定义下列文件类型的全局常量:   
        
      AIFC     
      AIFF     
      AU     
      SND     
      WAV     
      我们需要提供输出文件和文件类型常量。   
      我们将假设调用方法将为我们指定   AudioFileFormat.Type   类.   
      下面是   save   方法的代码:   
        
        
      public   boolean   saveToFile(File   file,   AudioFileFormat.Type   fileType)     
          throws   Exception   
      {   
      //Reset   to   the   beginning   of   the   captured   data   
      audioInputStream.reset();     
        
      //Notify   if   an   error   has   occurred   
              return   (AudioSystem.write(audioInputStream,   fileType,   file)   ==   -1);     
      }   
        
        
          
      */   
      /*   
              
      支持的文件类型   第   10   页(共12   页)     
        
        
        
        
      AudioFileFormat.Type   常量对应的文件类型不是保证受支持的。   
      您可以通过使用   isFileTypeSupported(AudioFileFormat.Type   type)   方法询问   AudioSystem   类来确定受支持的文件类型。   
        
          
      */   
      /*   
      回放音频缓冲区   第   11   页(共12   页)     
      

  3.   

    谢谢sunyujia!
    这个资料谈及的是如何从输入音频流中捕获音频数据,我非常想从输出的音频流中捕获音频数据,也就是说当任意 一个播放软件在播放音乐的时候,我把它发往声卡的音频流截获。暂不结贴,期待更多信息。请大家共同探讨!
      

  4.   

    1.获得输出的途径
    SourceDataLine line=AudioSystem.getLine()2.constuct a sound format
    AudioFormat format = ...
    这个format许要和你采集是的format一致
    3.open line
    line.open(format)
    4.line.writeByte(byte[] b)具体的可以看sun的sound api 详情http://java.sun.com/products/java-media/sound/index.html这样采集的是输入吗?我没试过,以前想写这方面东西是找了很多资料
      

  5.   

    给你一段java播放音频的代码,以前在网上找到的,我自己改过之后可以正常播放音频文件,具体的数据流和声卡访问,自己看代码分析吧。import java.io.File;
    import java.io.IOException;
    import java.util.ArrayList;import javax.sound.sampled.AudioFormat;
    import javax.sound.sampled.AudioInputStream;
    import javax.sound.sampled.AudioSystem;
    import javax.sound.sampled.DataLine;
    import javax.sound.sampled.FloatControl;
    import javax.sound.sampled.LineUnavailableException;
    import javax.sound.sampled.SourceDataLine;
    import javax.sound.sampled.UnsupportedAudioFileException;public class SoundPlayer {
     
    private String filename;
     
    private Position curPosition;
     
    private final int EXTERNAL_BUFFER_SIZE = 524288; // 128Kb
     
    enum Position {
    LEFT, RIGHT, NORMAL
    };
     
    public SoundPlayer(String wavfile) {
    filename = wavfile;
    curPosition = Position.NORMAL;
    }
     
    public SoundPlayer(String wavfile, Position p) {
    filename = wavfile;
    curPosition = p;
    }
     
    public void run() {
     
    File soundFile = new File(filename);
    if (!soundFile.exists()) {
    System.err.println("Wave file not found: " + filename);
    return;
    }
     
    AudioInputStream audioInputStream = null;
    try {
    audioInputStream = AudioSystem.getAudioInputStream(soundFile);
    } catch (UnsupportedAudioFileException e1) {
    e1.printStackTrace();
    return;
    } catch (IOException e1) {
    e1.printStackTrace();
    return;
    }
     
    AudioFormat format = audioInputStream.getFormat();
    SourceDataLine auline = null;
    DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
     
    try {
    auline = (SourceDataLine) AudioSystem.getLine(info);
    auline.open(format);
    } catch (LineUnavailableException e) {
    e.printStackTrace();
    return;
    } catch (Exception e) {
    e.printStackTrace();
    return;
    }
     
    if (auline.isControlSupported(FloatControl.Type.PAN)) {
    FloatControl pan = (FloatControl) auline
    .getControl(FloatControl.Type.PAN);
    if (curPosition == Position.RIGHT)
    pan.setValue(1.0f);
    else if (curPosition == Position.LEFT)
    pan.setValue(-1.0f);

     
    auline.start();
    int nBytesRead = 0;
    byte[] abData = new byte[EXTERNAL_BUFFER_SIZE]; try {
    while (nBytesRead != -1) {
    nBytesRead = audioInputStream.read(abData, 0, abData.length);
    if (nBytesRead >= 0)
    auline.write(abData, 0, nBytesRead);
    }
    } catch (Exception e) {
    e.printStackTrace();
    return;
    } finally {
    auline.drain();
    auline.close();
    }
     
    }
    public static void main(String args[]){
    SoundPlayer a=new SoundPlayer(文件地址);
    a.run();
    }
    }
      

  6.   

    你好!!请问一下如何输出里面的数据流?PCM格式的~