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

时间:2015-03-19 09:33:12

标签: c# audio naudio

我有两个WAV音轨我想分别在+ 10,+ 8,+ 6,+ 4和0db的SNR下播放

SNR(db)公式= 20 log(信号有效值/ rms噪声)

为此,我需要使用C#语言计算和设置音轨的SNR。我终于想出了这个解决方案,感谢人们,并希望验证它。

这是我做的:

    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而增加。

1 个答案:

答案 0 :(得分:0)

您希望生成具有定义的信噪比(SNR)的音频信号。这意味着你有两个音频信号,一个是信号,另一个是 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)

或使用log(分贝)值:

snr2 = db_signal - db_noise

这两个是等价的。

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

Method1线性计算

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

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

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

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

的线性比

因此,如果您发现两个信号的平均幅度(均方根)为160,则您需要将信号乘以2或除以噪声 by two。这将导致两个均方根值为160和80,这使您的预期信噪比为2.0(线性)或6.0 dB。

方法2对数计算

首先如上所述计算线性均方根(导致信号= 160,噪声= 160)

该rms值的第二个分贝分贝,导致信号= 40 dB噪声= 40dB

因此当前SNR为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的噪声信号。