C# 验证代码以在特定 SNR 下播放 2 个音轨
Posted
技术标签:
【中文标题】C# 验证代码以在特定 SNR 下播放 2 个音轨【英文标题】:C# Verify Code for playing 2 audio tracks at specific SNR 【发布时间】:2015-03-19 09:33:11 【问题描述】:我有两个 WAV 音轨,我想分别以 +10、+8、+6、+4 和 0db 的 SNR 播放它们
SNR 公式 (db) = 20 log(rms of signal / rms of noise)
为此,我需要使用 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,这种变化会增加。
【问题讨论】:
与其投票否决,你能告诉我解决方案吗? 我想设置 SNR..你发布的链接不相关 你在第二个循环之前错过了noise_rms = 0
。
谢谢,但这并没有改变任何东西...... :(
(byte)(noise_samples[i] / factor);
中的演员阵容是假的。样本是 not 字节。顺便说一句:WAV 文件的格式是什么(样本大小、采样率、通道数等)?
【参考方案1】:
您想要生成具有定义的信噪比 (SNR) 的音频信号。这意味着您有 两个 音频信号,一个是 signal 另一个是 noise。
以下所有操作都可以通过直接样本 (int) 操作、将样本乘以一个因子或将两个样本相加来完成。
首先,您必须测量两个信号的 RMS(均方根)电平。这是(顾名思义)所有平方样本均值的 Thre 根,表示为分贝值:
db = 20*Log10(amplitude)
假设您有一个 30 dB 的信号和一个 20 dB 的噪声,您将获得 30-20 = 10dB 的 SNR。
如果您需要更好的 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
,则您必须将 signal 乘以 2 或除以 noise由两个。这将导致两个 rms 值分别为 160 和 80,从而为您提供 2.0(线性)或 6.0 dB 的预期 SNR。
方法2对数计算
首先按上述方法计算线性均方根(结果信号 = 160,噪声 = 160)
此 rms 值的第二个计算分贝,导致信号 = 40 dB 噪声 = 40 dB
所以现在的SNR是40-40 = 0dB,两部分功率一样。
要达到 6dB 的 SNR,您必须将噪声分量降低 6dB。
这样做所需的因子是 exp10(6.0/20.0) = 2.0
因此,您必须将噪声信号除以 2.0。
这将为噪声信号提供 80 的线性 rms。
所以最后你有一个 SNR = 20.0 * log10(160/80) = 6.0
编辑
修改噪声信号后,你要混合信号和噪声:
for(...) mix[i] = signal[i] + noise[i];
这将产生具有所需 SNR 的噪声信号。
【讨论】:
这里的 factor = exp10(db/20) 公式中,db 是我需要的 db 吗? 如果您发现信噪比为 10dB 并且想要 6dB 之一,那么您必须将其中一个信号修改 4dB。那么这个公式的 db=4 我有以下代码获取 rms 和 rms db: WaveFileReader readertest = new WaveFileReader(@"E:\MARK\Audio Tracks\Volume.wav"); int bytesnumber = (int)readertest.Length;字节[] buf = 新字节[字节数]; readertest.Read(buf, 0, bytesnumber);双倍总和 = 0; foreach (int i in buf) sum += i * i;双均值 = sum / (buf.Length*1.0);双有效值 = Math.Sqrt(mean);双 rmsdb = 20 * Math.Log10(rms); 看起来不错。注意 sum 中的溢出(sum 变得如此之大,以至于 sum+i 不再有任何区别)。你得到合理的结果了吗? 我不确定 db 部分中的 rms 是否是 20*math.log10(rms) ?你写了 20*Mat.log10(amplitude)以上是关于C# 验证代码以在特定 SNR 下播放 2 个音轨的主要内容,如果未能解决你的问题,请参考以下文章
使用 SharpDx 或 IMSourceReader 从 mp4 文件中读取第二个音轨流