Calculate decibels

2019-01-16 12:01发布

问题:

I'm recording mic input using the XNA library (I don't think this is really technology specific, but it never hurts). Every time I get a sample I would like to calculate the decibels. I have done many searches on the internet and not found a rock solid example...

Here is my attempt at calculating decibels from a sample:

        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));

If I output the decibel value to the screen I can see the values get higher as I get louder and lower as I speak softer. However, it always hovers around -40 when I'm absolutely quiet...I would assume it would be -90. I must have a calculation wrong in the block above?? from what I have read on some sites -40 is equivalent to "soft talking"...however, it's totally quiet.

Also, If I mute my mic it goes straight to -90.

Am I doing it wrong?

回答1:

When measuring the level of a sound signal, you should calculate the dB from the RMS value. In your sample you are looking at the absolute peak level. A single (peak) sample value determines your dB value, even when all other samples are exactly 0.

try this:

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);

For 'instantaneous' dB levels you would normally calculate the RMS over a segment of 20-50 ms. Note that the calculated dB value is relative to full-scale. For sound the dB value should be related to 20 uPa, and you will need to calibrate your signal to find the proper conversion from digital values to pressure values.



回答2:

I appreciate Han's post, and wrote a routine that can calculate decibels on 8 and 16 bit audio formats, with multiple channels using his example.

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:

I think Yann means that Decibels are a relative scale. If you're trying to measure the actual Sound Pressure Level or SPL, you would need to calibrate. What you're measuring is dBFS (decibels full-scale, I think). You're measuring how many decibels quieter the signal is than the loudest possible signal the system can represent (the "full-scale" signal, or 32768 for these 16-bit samples). That's why all the values are negative.