绘制麦克风输入的配置文件

时间:2017-09-13 09:11:08

标签: java audio waveform

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

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位才能得到第一个通道。现在我需要知道我要为每帧读取多少频率。然后我想我可以画出个人资料。任何提示?

1 个答案:

答案 0 :(得分:0)

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

如果您想绘制幅度(压力随时间变化),请检查this

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