Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AutoGain #1157

Open
alenet444 opened this issue May 17, 2024 · 0 comments
Open

AutoGain #1157

alenet444 opened this issue May 17, 2024 · 0 comments

Comments

@alenet444
Copy link

alenet444 commented May 17, 2024

I have developed a class in VB.NET that dynamically adjusts the gain of an audio signal to maintain a consistent volume level. It has proven to be very effective for my needs, so I decided to convert it to C#. My knowledge of C# is limited, so any improvements or feedback are welcome. I believe this class could be a valuable addition to the NAudio library.

using System;
using NAudio.Wave;

public class AutoGainSampleProvider : ISampleProvider
{
    private readonly ISampleProvider source;
    private readonly float gainFactor;
    private readonly float targetLevel;
    private readonly float maxGain;
    private readonly float adjustmentSpeed;
    private readonly float gateThreshold;
    private readonly float freezeThreshold;
    private readonly float attack;
    private readonly float release;
    private readonly float ratio;
    private float currentGain;
    private float lastRms;
    private bool _isEnabled;

    public AutoGainSampleProvider(ISampleProvider source, float gainFactor, float targetLevel, float maxGain, float adjustmentSpeed, float gateThreshold, float freezeThreshold, float attack, float release, float ratio, bool isEnabled = true)
    {
        this.source = source;
        this.gainFactor = gainFactor;
        this.targetLevel = targetLevel;
        this.maxGain = maxGain;
        this.adjustmentSpeed = adjustmentSpeed;
        this.gateThreshold = gateThreshold;
        this.freezeThreshold = freezeThreshold;
        this.attack = attack;
        this.release = release;
        this.ratio = ratio;
        this.currentGain = 1.0f;
        this.lastRms = 0.0f;
        this._isEnabled = isEnabled;
    }

    public WaveFormat WaveFormat => source.WaveFormat;

    public bool IsEnabled
    {
        get => _isEnabled;
        set => _isEnabled = value;
    }

    public int Read(float[] buffer, int offset, int count)
    {
        int samplesRead = source.Read(buffer, offset, count);
        AdjustGain(buffer, offset, samplesRead);
        if (_isEnabled)
        {
            ApplyGain(buffer, offset, samplesRead);
        }
        return samplesRead;
    }

    private void AdjustGain(float[] buffer, int offset, int count)
    {
        float rms = 0.0f;

        for (int i = 0; i < count; i++)
        {
            rms += buffer[offset + i] * buffer[offset + i];
        }

        rms = (float)Math.Sqrt(rms / count);

        // Apply gate threshold
        if (rms < gateThreshold)
        {
            currentGain = 1.0f;
            return;
        }

        // Freeze threshold
        if (rms < freezeThreshold && lastRms < freezeThreshold)
        {
            return;
        }

        lastRms = rms;

        // Compression ratio
        float desiredGain = targetLevel / (rms + 1.0E-10f);
        desiredGain = Math.Min(desiredGain, maxGain);
        desiredGain = 1.0f + ((desiredGain - 1.0f) / ratio);

        // Attack and release
        float gainDifference = desiredGain - currentGain;
        if (gainDifference > 0)
        {
            currentGain += gainDifference * attack;
        }
        else
        {
            currentGain += gainDifference * release;
        }
    }

    private void ApplyGain(float[] buffer, int offset, int count)
    {
        for (int i = 0; i < count; i++)
        {
            buffer[offset + i] *= currentGain * gainFactor;
        }
    }
}

A code example (generated by IA)

using System;
using NAudio.Wave;

namespace AudioPlaybackExample
{
    class Program
    {
        static void Main(string[] args)
        {
            // File path to audio
            string audioFilePath = "path_to_your_audio_file.wav";

            using (var audioFileReader = new AudioFileReader(audioFilePath))
            {
                // Convert audio to 32 bits (SampleChannel)
                var sampleChannel = new SampleChannel(audioFileReader, true);

                // Create instance for AutoGainSampleProvider with parameters
                var autoGain = new AutoGainSampleProvider(
                    sampleChannel,
                    gainFactor: 1.0f,
                    targetLevel: 0.1995f,
                    maxGain: 2.0f,
                    adjustmentSpeed: 0.00001f,
                    gateThreshold: 0.01f,
                    freezeThreshold: 0.05f,
                    attack: 0.001f,
                    release: 0.0005f,
                    ratio: 1.0f,
                    isEnabled: true
                );

                // Use WaveOutEvent for play
                using (var waveOut = new WaveOutEvent())
                {
                    waveOut.Init(autoGain);
                    waveOut.Play();

                    // Wait for end
                    while (waveOut.PlaybackState == PlaybackState.Playing)
                    {
                        System.Threading.Thread.Sleep(100);
                    }
                }
            }
        }
    }
}

I apologize if there are any errors in the code. I used AI to convert it from VB.NET to C#. If needed, I have the original code written in VB.NET.

Feel free to reach out if you have any questions or suggestions for improvement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant