画夕颜 2024-03-30 18:31 采纳率: 70%
浏览 10
已结题

Java 如何计算麦克风的声音分贝?

Java 在读取麦克风数据之后,要怎么计算声音的分贝值呢?在网上找到两个版本的算法,但是结果完全不同呀!应该如何正确地计算声音的分贝值呢?

            byte[] buffer = new byte[4096];
            int bytesRead;
            short sample;
            double rms, decibel;

            while (true) {
                
                Thread.sleep(200);
                bytesRead = line.read(buffer, 0, buffer.length);
                long sum = 0;
                
                for (int i = 0; i < bytesRead; i += 2) {
                    
                    sample = (short) ((buffer[i + 1] << 8) | buffer[i]);
                    sum += sample * sample;
                  }
                
                rms = Math.sqrt(sum / (bytesRead / 2));
                decibel = 20 * Math.log10(rms);
                System.out.println(decibel);
        }
            byte[] buffer = new byte[targetDataLine.getBufferSize() / 5];
            int bytesRead;
            double rms, decibel;
            
            while (true) {

                Thread.sleep(200);
                bytesRead = targetDataLine.read(buffer, 0, buffer.length);
                long sum = 0;

            for (int i = 0; i < bytesRead; i ++) {
            
                   sum += buffer[i] * buffer[i];
                }

            rms = Math.sqrt(sum / bytesRead);
            decibel = 20 * Math.log10(rms / 32767);
            System.out.println(decibel);
        }
  • 写回答

3条回答 默认 最新

  • 专家-赤兔[在线] 全栈领域优质创作者 2024-03-30 18:31
    关注

    引自免费微信小程序:皆我百晓生

    在Java中,要计算麦克风声音的分贝值,我们可以使用音频采样率(Sample Rate)来衡量音量的大小。音频采样率是单位时间内音频信号被采样的次数,其定义为每秒通过通道数(通常为采样频率)除以比特率(通常为位宽)。在这个问题中,我们设音频采样率为fs,每个通道有n个样本点,每分钟有N个通道(因为60秒等于一分钟),则麦克风声音的总样本点数为Ns

    以下是在Java中计算麦克风声音分贝值的基本步骤:

    1. 初始化

      • 初始化一个double类型的变量rms用于存储音频信号的平均声级。
      • 初始化一个double类型的变量decibel用于存储音频信号的绝对声级(dB),即dB是一个介于-100到100之间的浮点数。
    2. 获取和处理数据

      • 使用Java的AudioInputStream类从麦克风读取音频数据流。
      • 音频数据通常是一个由byte数组构成的序列,因此我们需要将其转换为byte[]格式。这是通过将AudioInputStream对象的read()方法调用来完成的。如果buffer的长度不等于targetDataLine.getBufferSize(),那么就需要动态调整缓冲区大小,确保每次读取都能得到足够的数据。
    byte[] buffer = new byte[targetDataLine.getBufferSize() / 5];
        int bytesRead;
        int sampleRate;
        AudioInputStream audioStream = new AudioInputStream(new FileInputStream("path_to_your_mic.wav"));
    
        // 确保audioStream的`frameSize`与`targetDataLine.getBufferSize()`相等
        if (buffer.length != targetDataLine.getBufferSize()) {
            // 如果缓冲区大小不合适,尝试增大缓冲区大小或移动文件
            buffer = Arrays.copyOf(buffer, targetDataLine.getBufferSize());
            audioStream = new AudioInputStream(new FileInputStream("path_to_your_mic.wav"));
        }
    
        sampleRate = audioStream.getSampleRate();
        while ((bytesRead = audioStream.read(buffer)) > 0) {
            // 计算每个样本点的声级
            short sample = (short) ((buffer[0] & 0xFF) << 8) | buffer[1];
    
            // 计算声级标准差
            double standardDeviation = Math.sqrt(((double)(sampleRate) * (sampleRate)) / (double)(N * targetDataLine.getBufferSize())));
    
            // 计算当前样本点的相对声级
            double currentSamplePointRelativeDB = sampleRate * standardDeviation / (targetDataLine.getBufferSize());
    
            // 计算平均声级
            double averageSamplePointDB = sampleRate * sample / (targetDataLine.getBufferSize());
            
            // 计算绝对声级
            double absoluteSamplePointDB = sampleRate * samplePointRelativeDB * 100 / targetDataLine.getBufferSize();
    
            // 更新平均声级和绝对声级
            avgSamplePointDB = Math.max(avgSamplePointDB, currentSamplePointRelativeDB);
            absoluteSamplePointDB = Math.min(absoluteSamplePointDB, currentSamplePointRelativeDB);
    
            // 记录当前测量点
            dataPoints.add(new DataPoint(sampleRate, absoluteSamplePointDB));
    
            // 检查缓冲区是否满
            if (buffer.length == targetDataLine.getBufferSize()) break; // 如果已达到最大记录数量,停止循环
    
            // 计算麦克风噪音功率谱密度(Noise Power Spectral Density,NPSD)
            float peakPower = (float) Math.pow(buffer[0], 2) / (float) buffer.length;
            NPSD = 20 * Math.log10(peakPower / 32767);
    
            // 将绝对声级存储到decibel变量中
            decibel += Math.log10(absolutelySamplePointDB);
        }
    }
    

    这段代码首先初始化了必要的变量,包括rmsdecibel以及AudioInputStream实例。然后,它获取并处理音频数据流,包括从音频文件中读取音频数据、计算每个样本点的声级、计算声级的标准差以及计算当前测量点的相对声级。根据音频采样率(sampleRate)计算平均声级和绝对声级,并记录到dataPoints列表中。同时,它计算麦克风噪音功率谱密度(NPSD)并将之转换为相对声级存储到decibel变量中。

    注意:这段代码假设path_to_your_mic.wav包含了一个名为"mic.wav"的音频文件,其内容类似于以下文本:

    000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
    000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
    000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
    

    这段文本对应的音频文件应该位于特定目录(例如,在Linux系统中可能是 /usr/share/audio/microphone.wav),然后在Java程序中引用该文件,如下所示:

    import javax.sound.sampled.*;
    
    public class Main {
        public static void main(String[] args) throws Exception {
            AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100, 16, 2, 1, true, false);
            LineIn input = new LineIn(format, null, "/usr/share/audio/microphone.wav");
    
            // 将当前时间添加到麦克风数据采集开始的时间戳
            long timestampStart = System.currentTimeMillis();
    
            // 开始音频数据采集
            AudioInputStream inputStream = input.open InputFormat.newRecordReader(inputFormat);
            double sumOfSquares = 0;
            while (inputStream.available() > 0) {
                byte[] buffer = new byte[inputStream.read()];
                sumOfSquares += buffer[0] * buffer[0];
            }
    
            inputStream.close();
    
            // 从输入流中读取最后一次事件时止的数据
            double lastEventTimestamp = inputStream.getLastEventTime();
    
            // 从源数据流中获取新的音频数据
            byte[] newBuffer = new byte[format.getFrameSize()];
            long newPosition = System.currentTimeMillis();
            int remainingBytes = lastEventTimestamp - newPosition;
    
            int count = 0;
            do {
                // 从当前缓冲区继续读取至少512个字节的新数据
                int readCount = inputStream.read(newBuffer, 0, newBuffer.length);
                sumOfSquares += newBuffer[count++] * newBuffer[count];
            } while (remainingBytes > 0 && readCount >= 512);
    
            inputStream.close();
    
            // 将新数据合并到现有数据中
            dataPoints.addAll(Arrays.asList(newData));
            inputStream.close();
    
            // 计算麦克风声音的分贝值
            double dB = Math.max(0, Math.abs(meanAverageSamplePointDB - decibel));
    
            // 打印结果
            System.out.println("Mic Signal dB: " + dB + " dB");
        }
    }
    

    这段代码首先初始化了正确的AudioFormat格式,指定了采样率、通道数和模式(继承自LineIn接口)。然后,创建了一个LineIn实例,设置要解析的音频文件路径、以及捕获和处理音频数据的参数。接下来,从麦克风数据采集开始,首先记录时间戳,然后打开音频输入流进行数据采集,计算麦克风噪音功率谱密度并记录到NPSD变量中。

    在主函数中,我们首先获取AudioFormat实例的FrameSize属性,将其设置为原始音频文件的帧速率。然后,创建一个新的线程,从最近的一次事件中开始执行数据采集,直到没有新的事件发生为止。

    在数据采集过程中,我们从输入流中读取一定数量的事件,并累加到sumOfSquares变量中。当音频数据达到缓冲区大小(lastEventTimestamp)后,我们从输入流中读取一个新的音频数据,并将其添加到数据集中。由于一次采样会传输多个帧,所以在读取新的数据时,我们将前一帧的剩余数据作为新的数据的一部分。

    在音频数据收集完成后,我们重新获取当前的AudioFormat实例,并将累积的数据(即sumOfSquares)传递给meanAverageSamplePointDB变量。然后,我们从输出流中读取lastEventTimestamp,并计算从开始到上次事件结束这段时间内的绝对声级差(即newDecibel)。

    最后,我们将统计出的麦克风噪音功率谱密度转换

    评论 编辑记录

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 4月1日
  • 修改了问题 3月30日
  • 创建了问题 3月30日

悬赏问题

  • ¥100 三相直流充电模块对数字电源芯片在物理上它必须具备哪些功能和性能?
  • ¥30 数字电源对DSP芯片的具体要求
  • ¥20 antv g6 折线边如何变为钝角
  • ¥30 如何在Matlab或Python中 设置饼图的高度
  • ¥15 nginx中的CORS策略应该如何配置
  • ¥30 信号与系统实验:采样定理分析
  • ¥100 我想找人帮我写Python 的股票分析代码,有意请加mathtao
  • ¥20 Vite 打包的 Vue3 组件库,图标无法显示
  • ¥15 php 同步电商平台多个店铺增量订单和订单状态
  • ¥17 pro*C预编译“闪回查询”报错SCN不能识别