计算分贝

Rya*_*ook 23 c# audio audio-recording

我正在使用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));
Run Code Online (Sandbox Code Playgroud)

如果我将分贝值输出到屏幕,我可以看到值越高越大,因为我说话更柔和.然而,当我绝对安静时,它总是在-40左右徘徊......我认为它会是-90.我必须在上面的块中计算错误?从我在某些网站上看到的内容-40相当于"软谈话"...然而,它完全是安静的.

此外,如果我将麦克风静音,它会直接转到-90.

我做错了吗?

Han*_*Han 32

测量声音信号的电平时,应根据RMS值计算dB.在您的样本中,您正在查看绝对峰值水平.即使所有其他样本都精确为0,单个(峰值)样本值也会确定您的dB值.

试试这个:

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);
Run Code Online (Sandbox Code Playgroud)

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

  • 98dB的校准器相对于20uPa,因此实际的rms压力水平为20*10E-6*10 ^(98/20)= 1.59帕斯卡.有时您可以找到以mV/Pa为单位的麦克风灵敏度.然后,您只需要知道ADC输入电压与ADC提供的数字值之间的关系.这将允许您使用已知的电压源(或电压表)来校准麦克风后面的电路,并使用麦克风灵敏度来获得校准比例因子. (3认同)

Wil*_*son 5

我很欣赏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;
        }
    }
Run Code Online (Sandbox Code Playgroud)