【问题标题】:Calculate decibels计算分贝
【发布时间】:2011-05-08 07:43:53
【问题描述】:

我正在使用 XNA 库录制麦克风输入(我不认为这真的是特定于技术的,但它永远不会受到伤害)。每次我得到一个样本时,我都想计算分贝。我在互联网上进行了很多搜索,但没有找到可靠的例子......

这是我从样本中计算分贝的尝试:

        double peak = 0;

        for (var i = 0; i < _buffer.Length; i = i + 2)
        {
            var sample = BitConverter.ToInt16(_buffer, i);
            if (sample > peak)
                peak = sample;
            else if (sample < -peak)
                peak = -sample;
        }

        var decibel = (20 * Math.Log10(peak/32768));

如果我将分贝值输出到屏幕上,我可以看到分贝值会随着我的声音变得越来越大而随着我说话的声音越来越小而变得越来越低。但是,当我绝对安静时,它总是徘徊在 -40 左右……我认为它会是 -90。我在上面的块中一定有一个计算错误??从我在某些网站上读到的内容来看,-40 相当于“轻声细语”……但是,它完全安静。

另外,如果我将麦克风静音,它会直接变为 -90。

我做错了吗?

【问题讨论】:

  • 可能有背景噪音?
  • 对找到此页面的研究人员有用:float rms2db(float value) { return 10 * log10(value); }

标签: c# audio audio-recording


【解决方案1】:

我认为 Yann 的意思是分贝是一个相对规模。如果您尝试测量实际声压级或 SPL,则需要进行校准。您正在测量的是 dBFS(我认为是满量程分贝)。您正在测量信号比系统可以表示的最大可能信号(“满量程”信号,或这些 16 位样本的 32768)安静多少分贝。这就是为什么所有的值都是负数。

【讨论】:

    【解决方案2】:

    在测量声音信号的电平时,应根据 RMS 值计算 dB。在您的样本中,您正在查看绝对峰值电平。单个(峰值)样本值决定了您的 dB 值,即使所有其他样本正好为 0。

    试试这个:

    double sum = 0;
    for (var i = 0; i < _buffer.length; i = i + 2)
    {
        double sample = BitConverter.ToInt16(_buffer, i) / 32768.0;
        sum += (sample * sample);
    }
    double rms = Math.Sqrt(sum / (_buffer.length / 2));
    var decibel = 20 * Math.Log10(rms);
    

    对于“瞬时”dB 电平,您通常会在 20-50 毫秒的时间段内计算 RMS。 请注意,计算的 dB 值是相对于满量程的。对于声音,dB 值应与 20 uPa 相关,您需要校准信号以找到从数字值到压力值的正确转换。

    【讨论】:

    • 校准是指每个客户端都必须找到它们的零点...因为每个设备和环境都会有所不同?例如我的,似乎是-40,而一切都是沉默的……我可以将它校准到零吗?
    • 通常您会为此使用麦克风校准器。校准器提供具有非常精确的已知电平的信号,例如 98 dB。然后您测量/记录此信号并得出一个比例因子(与每个样本值相乘),这样您计算的分贝值为 98 dB。
    • 校准器的 98dB 相对于 20uPa,因此实际 rms 压力水平为 20*10E-6 * 10^(98/20) = 1.59 Pascal。有时您可以找到以 mV/Pa 为单位的麦克风灵敏度。那么您只需要知道 ADC 输入上的电压与 ADC 提供的数字值之间的关系。这将允许您使用已知电压源(或电压表)来校准麦克风后面的电路,并使用麦克风灵敏度来获得校准比例因子。
    • 虽然我并不完全理解你所描述的所有算法,但我相信你比我更了解这一点。 ;)
    • 不应该是:Math.Sqrt(sum / (_buffer.length/2));
    【解决方案3】:

    我很欣赏 Han 的帖子,并使用他的示例编写了一个可以计算 8 位和 16 位音频格式的分贝的例程,具有多个通道。

    public double MeasureDecibels(byte[] samples, int length, int bitsPerSample,
            int numChannels, params int[] channelsToMeasure)
        {
            if (samples == null || length == 0 || samples.Length == 0)
            {
                throw new ArgumentException("Missing samples to measure.");
            }
            //check bits are 8 or 16.
            if (bitsPerSample != 8 && bitsPerSample != 16)
            {
                throw new ArgumentException("Only 8 and 16 bit samples allowed.");
            }
            //check channels are valid
            if (channelsToMeasure == null || channelsToMeasure.Length == 0)
            {
                throw new ArgumentException("Must have target channels.");
            }
            //check each channel is in proper range.
            foreach (int channel in channelsToMeasure)
            {
                if (channel < 0 || channel >= numChannels)
                {
                    throw new ArgumentException("Invalid channel requested.");
                }
            }
    
            //ensure we have only full blocks. A half a block isn't considered valid.
            int sampleSizeInBytes = bitsPerSample / 8;
            int blockSizeInBytes = sampleSizeInBytes * numChannels;
            if (length % blockSizeInBytes != 0)
            {
                throw new ArgumentException("Non-integral number of bytes passed for given audio format.");
            }
    
            double sum = 0;
            for (var i = 0; i < length; i = i + blockSizeInBytes)
            {
                double sumOfChannels = 0;
                for (int j = 0; j < channelsToMeasure.Length; j++)
                {
                    int channelOffset = channelsToMeasure[j] * sampleSizeInBytes;
                    int channelIndex = i + channelOffset;
                    if (bitsPerSample == 8)
                    {
                        sumOfChannels = (127 - samples[channelIndex]) / byte.MaxValue;
                    }
                    else
                    {
                        double sampleValue = BitConverter.ToInt16(samples, channelIndex);
                        sumOfChannels += (sampleValue / short.MaxValue);
                    }
                }
                double averageOfChannels = sumOfChannels / channelsToMeasure.Length;
                sum += (averageOfChannels * averageOfChannels);
            }
            int numberSamples = length / blockSizeInBytes;
            double rootMeanSquared = Math.Sqrt(sum / numberSamples);
            if (rootMeanSquared == 0)
            {
                return 0;
            }
            else
            {
                double logvalue = Math.Log10(rootMeanSquared);
                double decibel = 20 * logvalue;
                return decibel;
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-18
      • 2011-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-05
      相关资源
      最近更新 更多