代码之家  ›  专栏  ›  技术社区  ›  Ryan Eastabrook

计算分贝

  •  21
  • Ryan Eastabrook  · 技术社区  · 14 年前

    我正在使用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。

    我做错了吗?

    3 回复  |  直到 12 年前
        1
  •  31
  •   Han    12 年前

    测量声音信号的电平时,应根据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 ms段上的rms。 注意,计算的db值是相对于满刻度的。对于声音,db值应与20 upa相关,您需要校准信号以找到从数字值到压力值的正确转换。

        2
  •  5
  •   William Morrison    12 年前

    我很欣赏韩寒的文章,并写了一个程序,可以计算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;
            }
        }
    
        3
  •  3
  •   heavi5ide    14 年前

    我认为扬的意思是分贝是一个相对的刻度。如果你想测量实际的声压级或声压级,你需要校准。你测量的是dbfs(我想是全刻度分贝)。您正在测量信号比系统所能表示的最响亮的信号(即“满刻度”信号,或这些16位样本的32768)要低多少分贝。所以所有的值都是负数。