C# 验证代码以特定 SNR 播放 2 个音轨

C# Verify Code for playing 2 audio tracks at specific SNR

我有两个 WAV 音轨,我想分别以 +10、+8、+6、+4 和 0db 的 SNR 播放它们

SNR (db) 的公式 = 20 log(信号均方根 / 噪声均方根)

为此,我需要使用 C# 语言计算和设置音轨的信噪比。多亏了人,我终于想出了这个解决方案,想验证一下。

这是我所做的:

    public void play(int required_snr)
    {
        WaveFileReader signal = new WaveFileReader(@"E:\signal.wav"),
                            noise = new WaveFileReader(@"E:\noise.wav");

        int signal_length = (int)signal.Length, noise_length = (int)noise.Length, i;

        byte[] signal_sample = new byte[signal_length], noise_sample = new byte[noise_length];

        int signal_read = signal.Read(signal_sample, 0, signal_length) / 2, noise_read = noise.Read(noise_sample, 0, noise_length) / 2;

        float[] sample_arr = new float[signal_read], noise_arr = new float[noise_read];
        float sum = 0;

        for (i = 0; i < sample_arr.Length; i++)
        {
            sample_arr[i] = (float)BitConverter.ToInt16(signal_sample, i * 2) / 32768f;
            sum += (sample_arr[i] * sample_arr[i]);
        }

        float rms_signal = (float)Math.Sqrt((sum / sample_arr.Length));

        sum = 0;

        for (i = 0; i < noise_arr.Length; i++)
        {
            noise_arr[i] = (float)BitConverter.ToInt16(noise_sample, i * 2) / 32768f;
            sum += (noise_arr[i] * noise_arr[i]);
        }

        float rms_noise = (float)Math.Sqrt((sum / noise_arr.Length));

        float snr_db = (float)Math.Round(20 * Math.Log10(rms_signal / rms_noise), 1);

        float factor = (float)Math.Pow(10, (required_snr - snr_db) / 20);

        rms_noise = 0;

        for (i = 0; i < noise_arr.Length; i++)
        {
            noise_arr[i] = noise_arr[i] / factor;

            rms_noise += (noise_arr[i] * noise_arr[i]);
        }

        rms_noise = (float)Math.Sqrt((rms_noise / noise_arr.Length));
        snr_db = (float)Math.Round(20 * Math.Log10(rms_signal / rms_noise), 1);

        using (WaveFileWriter writer = new WaveFileWriter(@"E:\aw4.wav", noise.WaveFormat))
        {
            for (i = 0; i < noise_arr.Length; i++)
                writer.WriteSample(noise_arr[i]);
        }

        WaveFileReader reader = new WaveFileReader(@"E:\aw4.wav");
        WaveOut waveOut = new WaveOut();
        waveOut.Init(reader);
        waveOut.Play();

        WaveFileReader si = new WaveFileReader(@"E:\signal.wav");
        WaveOut o = new WaveOut();
        o.Init(si);
        o.Play();
    }

这段代码正确吗? 我是 DSP 的菜鸟,所以我不知道。 但是我确实听到并感觉到了噪声音量水平的变化,随着我将所需的 SNR 从 10 降低到 0,这种变化会增加。

您想生成具有定义的信噪比 (SNR) 的音频信号。这意味着你有两个音频信号,一个是signal另一个是noise.

以下所有操作都可以通过直接样本 (int) 操作、将一个样本乘以一个因子或将两个样本相加来完成。

首先,您必须测量两个信号的 RMS(均方根)电平。这是(顾名思义)所有平方样本均值的三次方根,表示为分贝值:

 db = 20*Log10(amplitude)

假设您有一个 30 dB 的信号和一个 20 dB 的噪声,您将获得 30-20 = 10dB 的 SNR。

如果您需要更好的信噪比,则必须放大信号或减弱噪声。

您通过将所有样本乘以常数因子来进行放大。 (小心不要过载,即产生的样本对于允许的整数范围来说太大)

factor = exp10(db/20);

最后将两个信号相加,得到具有所需 SNR 的组合信号。


编辑

信噪比可以通过两种方式计算:

snr1 = 20 * log10(rms_signal / rms_noise)

或使用对数(分贝)值:

snr2 = db_signal - db_noise

这两个是等价的。

这里是从两个音轨生成具有定义的 SNR 的混合信号的更详细步骤。

方法1线性计算

计算两个信号的线性均方根:

rms := sqrt ( sum(x*x) / num(x) )

带 x 的是音轨的所有样本(通常是 int16)。

6 dB 的 SNR 相当于 exp10(6/20) = 2.0 的线性比

因此,如果您发现两个信号的平均振幅 (rms) 为 160,则必须将 信号 乘以二或除以 噪声 两个。这将导致两个 rms 值 160 和 80,这为您提供 2.0(线性)或 6.0 dB 的预期 SNR。

方法2对数计算

首先如上计算线性rms(结果信号=160,噪声=160)

此均方根值的第二次计算分贝,导致信号=40 dB 噪声= 40dB

所以目前的信噪比是40-40 = 0dB,两部分的功率是一样的

要达到 6dB 的 SNR,您必须将噪声分量降低 6dB。

这样做所需的因子是 exp10(6.0/20.0) = 2.0

因此您必须将噪声信号除以 2.0。

这将为您提供噪声信号的线性均方根值 80。

所以最后你有一个 SNR = 20.0 * log10(160/80) = 6.0


编辑

修改噪声信号后,还得混合信号和噪声:

for(...)  mix[i] = signal[i] + noise[i];

这将导致具有所需 SNR 的噪声信号。