2017-09-13 44 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