5
votes

I'm working with phone raw phone sounds and recordings and I want to normalize them to a certain volume level in a .Net C# project.

The sound is a collection of raw audio bytes (mono unheadered 16-bit signed PCM audio 16000Hz).

The audio is split into blocks of 3200 bytes == 100ms.

Any suggestions how to increase the volume/amplitude so the sounds is louder?

I haven't got a clue if I need to add a constant or multiply values, or if I need to do it to every 1,2,3.... bytes? And maybe there is already a open source solution for this?

1
Every two bytes are a signed 16-bit value. Simply multiply then by certain fixed constant and avoid overflows. - Roman R.
@RomanR. thanks for the comment. When I convert the two bytes to Int16 and multiply them by 10 and convert the back to two byte I hear the sound but its really distorted. - Toine db
I wrote you should mind the overflows and multiplication by ten is likely to get you many. Try 1.1 for starters to witness the method works well. - Roman R.
@RomanR. Oh 2 or 3 as multiplier didn't get any hearable distortions. But the db increase wasn't noticeable. So I (maybe to quickly) was thinking it wasn't working. Ill give it a try again, thanks for the reply. - Toine db
@RomanR. Thanks for the info, I figured it out. - Toine db

1 Answers

5
votes

To answer my own question (for others).

The solution is to multiply every sample (when 16bit PCM that are 2 bytes) with a constant value.

Do avoid overflow\to much increase you can calculate the highest constant value you can use by looking for the highest sample value and calculate the multiply factor to get it to highest sample value possible, in 16bit PCM case thats 32676 or something.

Here is litle example:

    public byte[] IncreaseDecibel(byte[] audioBuffer, float multiplier) 
    {
        // Max range -32768 and 32767
        var highestValue = GetHighestAbsoluteSample(audioBuffer);
        var highestPosibleMultiplier = (float)Int16.MaxValue/highestValue; // Int16.MaxValue = 32767
        if (multiplier > highestPosibleMultiplier)
        {
            multiplier = highestPosibleMultiplier;
        }

        for (var i = 0; i < audioBuffer.Length; i = i + 2)
        {
            Int16 sample = BitConverter.ToInt16(audioBuffer, i);
            sample *= (Int16)(sample * multiplier);
            byte[] sampleBytes = GetLittleEndianBytesFromShort(sample);
            audioBuffer[i] = sampleBytes[sampleBytes.Length-2];
            audioBuffer[i+1] = sampleBytes[sampleBytes.Length-1];
        }

        return audioBuffer;
    }

// ADDED GetHighestAbsoluteSample, hopefully its still correct because code has changed over time

    /// <summary>
    /// Peak sample value
    /// </summary>
    /// <param name="audioBuffer">audio</param>
    /// <returns>0 - 32768</returns>
    public static short GetHighestAbsoluteSample(byte[] audioBuffer)
    {
        Int16 highestAbsoluteValue = 0;
        for (var i = 0; i < (audioBuffer.Length-1); i = i + 2)
        {
            Int16 sample = ByteConverter.GetShortFromLittleEndianBytes(audioBuffer, i);

            // prevent Math.Abs overflow exception
            if (sample == Int16.MinValue)
            {
                sample += 1;
            }
            var absoluteValue = Math.Abs(sample);

            if (absoluteValue > highestAbsoluteValue)
            {
                highestAbsoluteValue = absoluteValue;
            }
        }

        return (highestAbsoluteValue > LowestPossibleAmplitude) ?
                    highestAbsoluteValue : LowestPossibleAmplitude;
    }