2017-09-13 34 views
0

我对声音数字化知之甚少。我试图表示麦克风输入的即时配置文件。我知道如何从麦克风中获取音乐,但我不知道如何将其解释为配置文件。任何人都可以帮我填补空白吗?绘制麦克风输入配置文件

package test; 

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Container; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.RenderingHints; 
import java.awt.event.ActionEvent; 
import java.io.IOException; 
import java.io.OutputStream; 
import javax.sound.sampled.AudioFileFormat; 
import javax.sound.sampled.AudioFormat; 
import javax.sound.sampled.AudioInputStream; 
import javax.sound.sampled.AudioSystem; 
import javax.sound.sampled.DataLine; 
import javax.sound.sampled.LineUnavailableException; 
import javax.sound.sampled.TargetDataLine; 
import javax.swing.AbstractAction; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.SwingUtilities; 

/** 
* 
* @author François Billioud 
*/ 
public class SoundRecorder extends JFrame { 

    /** JFrame for the GUI **/ 
    public SoundRecorder() { 
     super("Sound Recorder"); 
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     Container pane = getContentPane(); 
     pane.setLayout(new BorderLayout()); 
     pane.add(wavePane = new WavePane(), BorderLayout.CENTER); 
     pane.add(new JButton(new AbstractAction("ok") { 
      @Override 
      public void actionPerformed(ActionEvent e) { 
       dispose(); 
      } 
     }), BorderLayout.SOUTH); 
     setSize(300,300); 
     setLocationRelativeTo(null); 
    } 

    /** Just displays the frame and starts listening **/ 
    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       SoundRecorder rec = new SoundRecorder(); 
       rec.setVisible(true); 
       rec.listenToMic(); 
      } 
     }); 
    } 

    /** Draws the sound read from the mic **/ 
    private static class WavePane extends JPanel { 
     private final int[] x = new int[0]; 
     private int[] y = new int[0]; 
     private WavePane() { 
      setOpaque(true); 
      setBackground(Color.WHITE); 
     } 
     /** updates the data to be displayed **/ 
     public void setData(int[] y) { 
      this.y = y; 
      int n = y.length; 
      this.x = new int[n]; 
      float pas = getWidth()/(float)(n-1); 
      float xCurrent = 0; 
      for(int i=0; i<n; i++) { 
       this.x[i] = Math.round(xCurrent); 
       xCurrent+=pas; 
      } 
      repaint(); 
     } 
     /** Draws a line that represent the mic profile **/ 
     @Override 
     public void paint(Graphics g) { 
      super.paint(g); 
      Graphics2D g2D = (Graphics2D) g; 
      g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); 
      g2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 
      g2D.drawPolyline(x, y, x.length); 
     } 
    } 

    /** Defines the audio format to be used. 
    * I know nothing about that and am open to suggestions if needed 
    */ 
    private static final AudioFormat format = new AudioFormat(
      16000, //Sample rate 
      16, //SampleSizeInBits 
      2, //Channels 
      true,//Signed 
      true //BigEndian 
    ); 

    /** Creates a thread that will read data from 
    * the mic and send it to the WavePane 
    * in order to be painted. 
    * We should be using a SwingWorker, but it will do 
    * for the sake of this demo. 
    **/ 
    private void listenToMic() { 
     new Thread(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        //Open the line and read 
        DataLine.Info info = new DataLine.Info(TargetDataLine.class, format); 

        //checks if system supports the data line 
        if (!AudioSystem.isLineSupported(info)) { 
         System.err.print("Line not supported"); 
        } 

        //starts listening 
        TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info); 
        line.open(format); 
        line.start(); 

        //sends the stream to the interpreter 
        AudioInputStream ais = new AudioInputStream(line); 
        AudioSystem.write(ais, AudioFileFormat.Type.AU, new Interpreter()); 
       } catch (LineUnavailableException | IOException ex) { 
        System.err.println(ex.getLocalizedMessage()); 
       } 
      } 
     }).start(); 
    } 

    private final WavePane wavePane; 
    private class Interpreter extends OutputStream { 
     private int[] y; 

     @Override 
     public void write(int b) throws IOException { 
      //TBD 

      //Fill y array 
     } 

     @Override 
     public void flush() throws IOException { 
      //Sends the values found to the panel for drawing 
      wavePane.setData(y); 
     } 

    } 

} 

我发现this link但它并没有帮助我......

编辑:好了,从我的理解,每个16位是一个频率的幅度。我有2个通道,所以我必须每32位读取16位才能获得第一个通道。现在我需要知道每帧要读取多少个频率。然后我想我可以画出轮廓。任何提示?

回答

0

如果您想绘制来自麦克风的信号的频谱(随时间变化的频率),那么您可能需要阅读this。也许你想知道更多,但它有你需要的数学。

如果你想画幅度(压力随时间),然后检查,例如,this

根据您对音频格式的规定,音频流的内容将成为PCM流。 PCM流意味着每一帧都是当时的声压值。每个帧将由四个字节组成,每个通道两个字节。前两个字节将是通道0,另外两个通道1.两个字节将采用大端格式(更重要的字节将在较低有效字节之前出现)。您指定为True的事实表示您应该将值解释为-32768至32767之间的范围。

+0

感谢您提供非常完整的链接。我想要从麦克风实时获取每个频率的能量。你的链接是好的,但STFT是在将麦克风输入转换成比特时完成的。问题更多的是关于数据如何在流中排列,以及如何重新排列它们以获得我的表示。你明白我的意思吗? – Sharcoux

+0

增加了关于流内容到答案的一些评论。 – astraujums

+0

好的。我想我只有更多的信息,那么我应该如何解读一个负数?能量不能是负面的,所以我猜想-3000意味着3000阶段的PI?那么,我应该只考虑绝对值吗?如果我有一个大字节无符号格式的字节数组byte [16],那么将其转换为数字的正确方法是什么?非常感谢您的帮助! – Sharcoux