如何计算在Java中的音频信号的电平/幅度/ DB? [英] How to calculate the level/amplitude/db of audio signal in java?

查看:298
本文介绍了如何计算在Java中的音频信号的电平/幅度/ DB?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建Java中的音频电平表的麦克风检查输入是多么响亮。它应该像操作系统之一。我不要求有关GUI。这只是有关

计算的音频电平出产生的字节流

  N = targetDataLine.read(tempBuffer,0,tempBuffer.length);

所以,我已经有正在运行的东西,但它甚至没有接近我的操作系统(Windows),它stucks在中间的levelmeter。我有值0和100是好的,但与60围在中间音量它stucks输入的是无论多么响亮。

这是我现在该怎么计算呢:

 振幅= 0;
        对于(INT J = 0; J< tempBuffer.length;当J = J +2){
            如果(tempBuffer [J]> tempBuffer [J + 1])
                振幅=幅度+ tempBuffer [J] - tempBuffer [J + 1];
            否则幅度=振幅+ tempBuffer [J + 1] - tempBuffer [J]。
        }
        幅度=振幅/ tempBuffer.length * 2;

有没有更好/更precise的方法来计算的音频电平监视它?还是我也许做一个重大失误?

这是我的AudioFormat的:

 公共静态AudioFormat的getAudioFormat(){
    浮采样率= 20000.0F;
    // 8000,11025,16000,22050,44100
    INT sampleSizeInBits = 16;
    // 8,16
    INT频道= 1;
    // 1,2
    布尔签订= TRUE;
    //真假
    布尔大尾端= FALSE;
    //真假
    返回新AudioFormat的(采样率,sampleSizeInBits,渠道,签订大尾端);
    //返回新AudioFormat的(AudioFormat.Encoding.PCM_SIGNED,8000.0F,8,1,1,8000.0F,FALSE);
}


解决方案

主要是问题似乎是,你是不正确读取的音频数据。

具体来说,我真的不知道这是什么摘录的解释是:

 如果(tempBuffer [J]> tempBuffer [J + 1])
    ... tempBuffer [J] - tempBuffer [J + 1];
其他
    ... tempBuffer [J + 1] - tempBuffer [J]。

但无论如何,因为你正在录制的16位数据的字节数组中的字节不是对自己有意义的。每个字节仅重新presents每个样品中的比特的1/2。你需要解压他们整数,浮点,不管,之前你可以用他们什么。对于原始LPCM,并置字节,由他们转移和OR它们放在一起做。

下面是一个MCVE展示在Java中一个基本的电平表(包括RMS和简单的峰值保持)。

 进口javax.swing.SwingUtilities中;
进口javax.swing.JFrame中;
进口javax.swing.JPanel中;
进口javax.swing.JComponent中;进口java.awt.BorderLayout中;
进口java.awt.Graphics;
进口java.awt.Color中;
进口java.awt.Dimension中;
进口javax.swing.border.EmptyBorder中;进口javax.sound.sampled.AudioFormat中;
进口javax.sound.sampled.TargetDataLine;
进口javax.sound.sampled.AudioSystem;
进口javax.sound.sampled.LineUnavailableException;公共类LevelMeter扩展JComponent的{
    私人INT meterWidth = 10;    私人浮动放= 0F;
    私人持股量峰值= 0F;    公共无效setAmplitude(浮点AMP){
        this.amp = Math.abs(AMP);
        重绘();
    }    公共无效setPeak(浮点峰值){
        this.peak = Math.abs(峰值);
        重绘();
    }    公共无效setMeterWidth(INT meterWidth){
        this.meterWidth = meterWidth;
    }    @覆盖
    保护无效paintComponent(图形G){
        INT W = Math.min(meterWidth,的getWidth());
        INT H =的getHeight();
        INT X =的getWidth()/ 2 - W / 2;
        INT Y = 0;        g.setColor(Color.LIGHT_GRAY);
        g.fillRect(X,Y​​,W,H);        g.setColor(Color.BLACK);
        g.drawRect(X,Y​​,W - 1,H - 1);        int类型的= Math.round(AMP *(H - 2));
        g.setColor(Color.GREEN);
        g.fillRect(X + 1,Y + H - 1 - A,W - 2,一);        INT P = Math.round(峰值*(H - 2));
        g.setColor(Color.RED);
        g.drawLine(X + 1,Y + H - 1 - P,X + W -​​ 1,Y + H - 1 - P);
    }    @覆盖
    公共尺寸getMinimumSize的(){
        尺寸分钟= super.getMinimumSize();
        如果(min.width< meterWidth)
            min.width = meterWidth;
        如果(min.height< meterWidth)
            min.height = meterWidth;
        返回分钟;
    }    @覆盖
    公共尺寸的get preferredSize(){
        尺寸preF = super.get preferredSize();
        pref.width = meterWidth;
        返回preF;
    }    @覆盖
    公共无效集preferredSize(尺寸preF){
        super.set preferredSize(preF);
        setMeterWidth(pref.width);
    }    公共静态无效的主要(字串[] args){
        SwingUtilities.invokeLater(Runnable的新(){
            @覆盖
            公共无效的run(){
                JFrame的帧=新的JFrame(表);
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                内容的JPanel =新JPanel(新的BorderLayout());
                content.setBorder(新EmptyBorder(25,50,25,50));                LevelMeter米=新LevelMeter();
                meter.set preferredSize(新尺寸(9,100));
                content.add(米,BorderLayout.CENTER);                frame.setContentPane(内容);
                frame.pack();
                frame.setLocationRelativeTo(NULL);
                frame.setVisible(真);                新主题(新的记录(米))开始()。
            }
        });
    }    静态类录音机实现Runnable {
        最后LevelMeter米;        录音机(最终LevelMeter米){
            this.meter =计;
        }        @覆盖
        公共无效的run(){
            FMT AudioFormat的=新AudioFormat的(44100f,16,1,真,假);
            最终诠释bufferByteSize = 2048;            TargetDataLine的线;
            尝试{
                行= AudioSystem.getTargetDataLine(FMT);
                line.open(FMT,bufferByteSize);
            }赶上(LineUnavailableException E){
                通信System.err.println(E);
                返回;
            }            字节[] buf中=新的字节[bufferByteSize]
            浮动[] =样品新的浮动[bufferByteSize / 2];            浮lastPeak = 0F;            line.start();
            为(中间体B:(B = line.read(BUF,0,buf.length))-1个){                //字节转换为样本这里
                的for(int i = 0,S = 0; I< B;){
                    INT样本= 0;                    样本| = BUF [我++]放大器; 0xFF的; //(反向这两条线
                    样本| = BUF [我++<< 8; //如果格式是Big Endian)                    //正常化+/- 1.0f的范围
                    样本[S +] =采样/ 32768f;
                }                浮有效值= 0F;
                浮峰值= 0F;
                为(浮动样本:样本){                    浮ABS = Math.abs(样品);
                    如果(ABS>峰值){
                        峰值= ABS;
                    }                    RMS + =采样*样品;
                }                RMS =(浮动)的Math.sqrt(RMS / samples.length);                如果(lastPeak>峰值){
                    峰值= lastPeak * 0.875f;
                }                lastPeak =峰值;                setMeterOnEDT(RMS,峰值);
            }
        }        无效setMeterOnEDT(最终浮动有效值,最终浮动峰值){
            SwingUtilities.invokeLater(Runnable的新(){
                @覆盖
                公共无效的run(){
                    meter.setAmplitude(RMS);
                    meter.setPeak(峰);
                }
            });
        }
    }
}

请注意格式转换为硬codeD那里。

您还可以看到我如何使用从Java声音频采样数据吗?我如何从原始字节解压缩音频数据的详细说明。


相关阅读:

I want to create a audio level meter in java for the microphone to check how loud the input is. It should look like the one of the OS. I'm not asking about the gui. It is just about calculating the audio level out of the bytestream produced by

n = targetDataLine.read( tempBuffer , 0 , tempBuffer.length );

So I already have something that is running, but it is not even close to the levelmeter of my OS (windows) It stucks in the middle. I have values between 0 and 100 that is good but in the middle volume it stucks around 60 no matter how loud the input is.

This is how I calculate it now:

            amplitude = 0;
        for (int j = 0; j < tempBuffer.length; j = j +2 ){
            if (tempBuffer[j] > tempBuffer[j+1])
                amplitude = amplitude + tempBuffer[j] - tempBuffer[j+1];
            else amplitude = amplitude + tempBuffer[j + 1] - tempBuffer[j];
        }
        amplitude = amplitude / tempBuffer.length * 2;

Is there a better/more precise way to calculate the audio level to monitor it? Or did I maybe do a major mistake?

That is my Audioformat:

public static AudioFormat getAudioFormat(){
    float sampleRate = 20000.0F;
    //8000,11025,16000,22050,44100
    int sampleSizeInBits = 16;
    //8,16
    int channels = 1;
    //1,2
    boolean signed = true;
    //true,false
    boolean bigEndian = false;
    //true,false
    return new AudioFormat( sampleRate, sampleSizeInBits, channels, signed, bigEndian );
    //return new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 8000.0F, 8, 1, 1, 8000.0F, false);
}

解决方案

Principally the problem seems to be that you are reading the audio data incorrectly.

Specifically I'm not really sure what this excerpt is supposed to mean:

if (tempBuffer[j] > tempBuffer[j+1])
    ... tempBuffer[j] - tempBuffer[j+1];
else
    ... tempBuffer[j + 1] - tempBuffer[j];

But anyhow since you are recording 16-bit data the bytes in the byte array aren't meaningful on their own. Each byte only represents 1/2 of the bits in each sample. You need to 'unpack' them to int, float, whatever, before you can do anything with them. For raw LPCM, concatenating the bytes is done by shifting them and ORing them together.

Here is an MCVE to demonstrate a rudimentary level meter (both RMS and simple peak hold) in Java.

import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JComponent;

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.border.EmptyBorder;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.TargetDataLine;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;

public class LevelMeter extends JComponent {
    private int meterWidth = 10;

    private float amp = 0f;
    private float peak = 0f;

    public void setAmplitude(float amp) {
        this.amp = Math.abs(amp);
        repaint();
    }

    public void setPeak(float peak) {
        this.peak = Math.abs(peak);
        repaint();
    }

    public void setMeterWidth(int meterWidth) {
        this.meterWidth = meterWidth;
    }

    @Override
    protected void paintComponent(Graphics g) {
        int w = Math.min(meterWidth, getWidth());
        int h = getHeight();
        int x = getWidth() / 2 - w / 2;
        int y = 0;

        g.setColor(Color.LIGHT_GRAY);
        g.fillRect(x, y, w, h);

        g.setColor(Color.BLACK);
        g.drawRect(x, y, w - 1, h - 1);

        int a = Math.round(amp * (h - 2));
        g.setColor(Color.GREEN);
        g.fillRect(x + 1, y + h - 1 - a, w - 2, a);

        int p = Math.round(peak * (h - 2));
        g.setColor(Color.RED);
        g.drawLine(x + 1, y + h - 1 - p, x + w - 1, y + h - 1 - p);
    }

    @Override
    public Dimension getMinimumSize() {
        Dimension min = super.getMinimumSize();
        if(min.width < meterWidth)
            min.width = meterWidth;
        if(min.height < meterWidth)
            min.height = meterWidth;
        return min;
    }

    @Override
    public Dimension getPreferredSize() {
        Dimension pref = super.getPreferredSize();
        pref.width = meterWidth;
        return pref;
    }

    @Override
    public void setPreferredSize(Dimension pref) {
        super.setPreferredSize(pref);
        setMeterWidth(pref.width);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("Meter");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                JPanel content = new JPanel(new BorderLayout());
                content.setBorder(new EmptyBorder(25, 50, 25, 50));

                LevelMeter meter = new LevelMeter();
                meter.setPreferredSize(new Dimension(9, 100));
                content.add(meter, BorderLayout.CENTER);

                frame.setContentPane(content);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                new Thread(new Recorder(meter)).start();
            }
        });
    }

    static class Recorder implements Runnable {
        final LevelMeter meter;

        Recorder(final LevelMeter meter) {
            this.meter = meter;
        }

        @Override
        public void run() {
            AudioFormat fmt = new AudioFormat(44100f, 16, 1, true, false);
            final int bufferByteSize = 2048;

            TargetDataLine line;
            try {
                line = AudioSystem.getTargetDataLine(fmt);
                line.open(fmt, bufferByteSize);
            } catch(LineUnavailableException e) {
                System.err.println(e);
                return;
            }

            byte[] buf = new byte[bufferByteSize];
            float[] samples = new float[bufferByteSize / 2];

            float lastPeak = 0f;

            line.start();
            for(int b; (b = line.read(buf, 0, buf.length)) > -1;) {

                // convert bytes to samples here
                for(int i = 0, s = 0; i < b;) {
                    int sample = 0;

                    sample |= buf[i++] & 0xFF; // (reverse these two lines
                    sample |= buf[i++] << 8;   //  if the format is big endian)

                    // normalize to range of +/-1.0f
                    samples[s++] = sample / 32768f;
                }

                float rms = 0f;
                float peak = 0f;
                for(float sample : samples) {

                    float abs = Math.abs(sample);
                    if(abs > peak) {
                        peak = abs;
                    }

                    rms += sample * sample;
                }

                rms = (float)Math.sqrt(rms / samples.length);

                if(lastPeak > peak) {
                    peak = lastPeak * 0.875f;
                }

                lastPeak = peak;

                setMeterOnEDT(rms, peak);
            }
        }

        void setMeterOnEDT(final float rms, final float peak) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    meter.setAmplitude(rms);
                    meter.setPeak(peak);
                }
            });
        }
    }
}

Note the format conversion is hard-coded there.

You may also see "How do I use audio sample data from Java Sound?" for my detailed explanation of how to unpack audio data from the raw bytes.


Related:

这篇关于如何计算在Java中的音频信号的电平/幅度/ DB?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆