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 的噪声信号。
我有两个 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 的噪声信号。