【问题标题】:Draw profile of the mic input画出麦克风输入的轮廓
【发布时间】:2017-09-13 09:11:08
【问题描述】:

我对声音数字化了解不多。我试图代表麦克风输入的即时配置文件。我知道如何从麦克风中获取信息,但我不知道如何将其解释为配置文件。谁能帮我填空?

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 位才能获得第一个通道。现在我需要知道每帧要读取多少个频率。然后我想我可以画出轮廓。有什么提示吗?

【问题讨论】:

    标签: java audio waveform


    【解决方案1】:

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

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

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

    【讨论】:

    • 感谢您提供非常完整的链接。我想实时从麦克风中提取每个频率的能量。您的链接很好,但是在将麦克风输入转换为位时完成了 STFT。问题更多是关于当我读取它们时数据在流中的排列方式,以及我应该如何对它们重新排序以获得我的表示。你明白我的意思吗?
    • 在答案中添加了一些关于流内容的评论。
    • 好的。我想我只有更多的信息,我应该如何解释一个负数?能量不能是负数,所以我猜-3000 意味着 3000 与 PI 相位?那么,我应该只考虑绝对值吗?如果我有一个大端无符号格式的字节数组byte[16],将其转换为数字的正确方法是什么?非常感谢您的帮助!
    • 您可以将 PCM 数据视为在给定时间点与正常气压的偏差。因此,正值意味着压力高于正常值,负值意味着压力较低。要将 16 字节数组转换为 Java 中的单个数字,请将其传递给 BigInteger 类的构造函数。但是,对于您的输入格式,16 个字节将构成 4 个帧。第一帧将在字节 0-3 中。该帧中第一个通道的样本值将在字节 0 和 1 中,样本的实际值将是 byte[0] * 256 + byte[1]。
    猜你喜欢
    • 1970-01-01
    • 2014-02-07
    • 2011-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-31
    相关资源
    最近更新 更多