【问题标题】:How to calculate the decibel of audio signal and record the audio in java?如何计算音频信号的分贝并在java中录制音频?
【发布时间】:2018-08-12 08:47:22
【问题描述】:
public TargetDataLine targetDataLine;
private static AudioFormat getAudioFormat() 
{
        return new AudioFormat(16000, 16, 2, true, false);
}
AudioFormat a = getAudioFormat();
DataLine.Info info = new DataLine.Info(TargetDataLine.class, a);
targetDataLine = (TargetDataLine) AudioSystem.getLine(info);
targetDataLine.open(a);
targetDataLine.start();
AudioInputStream ais = new AudioInputStream(targetDataLine); 
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File("record.wav"));
  1. 如何同时计算音频的分贝?
  2. 如何计算现有 wav 文件的分贝数?

【问题讨论】:

    标签: java audio wav decibel audioformat


    【解决方案1】:

    假设一:您希望在记录结束时处理并保存数据。

    • 使用TargetDataLine.read()将数据读入临时短缓冲区
    • 使用ByteArrayOutputStream.write() 将数据写入ByteArrayOutputStream
    • 将ByteArrayOutputStream 转换为byte[] 数组。
    • 将字节数据编码为样本
    • 计算 RMS、峰值等并将它们转换为分贝。
    • 使用 byte[] 数组构造 ByteArrayInputStream
    • 使用AudioSystem.write()制作您的音频文件

      int numBytesRead = 0;
      byte[] buffer = new byte[1024]; 
      ByteArrayOutputStream outStream = new ByteArrayOutputStream();
      boolean stopRecording = false;
      
      while (!stopRecording) {
          numBytesRead =  targetDataLine.read(buffer, 0, buffer.length);
          //short[] samples = encodeToSample(buffer, buffer.length);
          // process samples - calculate decibels
      
          if (numBytesRead > 0) {
              outStream.write(buffer, 0, numBytesRead);
          }
      }
      
      outStream.close();
      
      byte[] data = outStream.toByteArray();
      ByteArrayInputStream bais = new ByteArrayInputStream(data);
      AudioInputStream ais = new AudioInputStream(bais, a, data.length);
      
      AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File("record.wav"));
      

    假设二:您希望连续读取、处理和保存数据。

    您需要一个中间类才能将ByteArrayOutputStream 转换为ByteArrayInputStream。在后台线程捕获音频数据,并在另一个线程处理并在您所需的数据量可用时保存数据。

    编码:您可以对来自byte[] 数组的音频样本进行编码。在您的情况下,两个连续的字节产生一个样本。您将一个接一个地获得两个通道的样本。如果您有任何特定于通道的处理,那么您需要为每个通道分离样本。以下代码 sn -p 可以帮助您进行编码 -

    public static short[] encodeToSample(byte[] srcBuffer, int numBytes) {
        byte[] tempBuffer = new byte[2];
        int nSamples = numBytes / 2;        
        short[] samples = new short[nSamples];  // 16-bit signed value
    
        for (int i = 0; i < nSamples; i++) {
            tempBuffer[0] = srcBuffer[2 * i];
            tempBuffer[1] = srcBuffer[2 * i + 1];
            samples[i] = bytesToShort(tempBuffer);
        }
    
        return samples;
    }
    
    public static short bytesToShort(byte [] buffer) {
        ByteBuffer bb = ByteBuffer.allocate(2);
        bb.order(ByteOrder.BIG_ENDIAN);
        bb.put(buffer[0]);
        bb.put(buffer[1]);
        return bb.getShort(0);
    }
    

    分贝: Db 是给定电平与参考电平的对数比。例如,如果您想以 dBFS 为单位计算 RMS/Peak,下面的代码 sn-p 可能会对您有所帮助。

    public static void calculatePeakAndRms(short [] samples) {
        double sumOfSampleSq = 0.0;    // sum of square of normalized samples.
        double peakSample = 0.0;     // peak sample.
    
        for (short sample : samples) {
            double normSample = (double) sample / 32767;  // normalized the sample with maximum value.
            sumOfSampleSq += (normSample * normSample);
            if (Math.abs(sample) > peakSample) {
                peakSample = Math.abs(sample);
            }
        }
    
        double rms = 10*Math.log10(sumOfSampleSq / samples.length);
        double peak = 20*Math.log10(peakSample / 32767);
    }
    

    【讨论】:

    • 这个数字代表什么 32768
    • 您的样本大小(以位为单位)为 16。因此样本的存储范围为 -32768 到 +32767。通过除以数字 32768(最大可能值),我们将范围从 -1 标准化到 +1。
    • @IhabKhaledShhadat 这个答案有用吗?
    猜你喜欢
    • 1970-01-01
    • 2014-12-21
    • 1970-01-01
    • 1970-01-01
    • 2012-02-01
    • 1970-01-01
    • 2013-04-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多