×
Namespaces

Variants
Actions

Windows Phone音频降噪

From Nokia Developer Wiki
Jump to: navigation, search
WP Metro Icon Multimedia.png
SignpostIcon XAML 40.png
SignpostIcon WP7 70px.png
Article Metadata

代码示例
源文件: DSP.zip

兼容于
文章
翻译:
WS - OtomiiLu
最后由 kiran10182 在 09 Oct 2013 编辑

Windows Phone音频降噪 本文说明了运用Windows Phone快速傅里叶变换算法来进行音频降噪的一种方法。

Contents

快速傅里叶变换算法

本节提供了对快速傅里叶变换算法(以下简称FFT)的简单概述,这是理解降噪算法工作机制的最低需求。如想稍微深入地了解,请参看Sound pattern matching using Fast Fourier Transform in Windows Phone. FFT计算出DFT,并将一个功能从時域(Time domain)(物理信号)转换成频域(frequency domain)表示,例子中的短波谱图即为频域表示。快速傅里叶逆变换则做相反的工作,将频域表示重新变回物理信号。 FFT需要输入离散的函数。 此类输入是通过连续函数抽样建立的,比如一个人的声音、一首歌或环境噪音。该算法仅适用于2的n次方个数元素的信号,并返回一组复杂的数字, 即频谱(Frequency spectrum)。FFT元素个数等于时间样本的大小。 这些复杂数字的另一半对应负频率,并包含前一半正频率的复杂共轭,而不包含任何新信息。

Analog digital series.png

降噪机制是如何工作的

首先我们运用傅里叶分析来找到混杂在背景噪音中的纯人声波谱。对每个背景音频的窗口样本,我们采用FFT算法,并为每个波段制表统计----尤其是一排中至少n个样本达到的最大水平,n取不同值。该波谱被称为环境中静态背景噪音的“指纹”。 在录音时,我们取每个短音频样本的频谱与我们的“指纹”进行对比。样本中不如“指纹”响亮的纯人声可能是噪音,所以我们在波谱中降低他们的值(这项通用技术被称为波谱噪声过滤)。 当我们对样本降噪波谱进行快速傅里叶逆变换时,它被变回时域,并播放音频。变换的结果是得到原始声音,但是与背景噪音关联的频率被大大降低。 类似的技术被应用在高端降噪耳机中; 主要的区别是,这些通常用第二只麦克风实时地、动态地计算噪音。

在 Windows Phone中使用FFT

下载FFT.cs 并将它添加到你的项目中。 FFT.cs 提供了一个叫做 FFT的命名空间,以及一个包含一系列计算FFT的类FourierTransform 。 参阅How to access and manage the Microphone raw data in WP7来获得在WP7中管理麦克风的完整说明。 点击麦克风实例,里面有优秀的代码实例,本文所用即是。 别忘了把命名空间FTT包含到你的项目里。

using FFT;

Compute()

是命名空间中用来计算FTT的函数。以下是函数签名:

void Compute(UInt32 NumSamples, Double[] pRealIn, Double[] pImagIn, Double[] pRealOut, Double[] pImagOut, Boolean bInverseTransform);

NumSamples 样本数量 (必须是2的n次方) pRealIn 真正的原始数据样本输入 pImagIn 虚系数输入(选填,可为空), 当计算傅里叶逆变换时,须填写此参数 pRealOut 实系数输出 pImagOut 虚系数输出 bInverseTransform 当bInverseTransform为true时, 计算快速傅里叶逆变换。

切割频段

首先创建一个double类型的数组来存储噪音的“指纹”。

private double[] fingerprint;

我们需要一个 DispatcherTimer 来管理噪音“指纹”的监测。检测间隔是4秒,你可以随意改变此值,不过设成十秒以上不会返回好结果,反而会使“指纹”结果出错,因为比背景声音大的一些声音可能被卷进来。

DispatcherTimer dtFingerprint;
 
// Timer to detect fingerprint
dtFingerprint = new DispatcherTimer();
dtFingerprint.Interval = TimeSpan.FromMilliseconds(4000);
dtFingerprint.Tick += new EventHandler(stopFingerprintDetection);
 
private void stopFingerprintDetection(object sender, EventArgs e)
{
dtFingerprint.Stop();
microphone.Stop();
 
MessageBar.Text = "Noise fingerprint computed.";
 
SetButtonStates(false, false, true);
 
microphone.Start();
 
UserHelp.Text = "Record";
StatusImage.Source = microphoneImage;
}

DispacherTimer 被包含在 System.Windows.Threadin命名空间下。 在我的 .xaml 中,我加入了一个复选框,来开启/关闭录音降噪。

<CheckBox Content="Noise Reduction" Name="cb_noise_reduction" ... />
当录音按钮被按下时我们分配“指纹”数组,并开启监测计时器。
<code csharp>
private void recordButton_Click(object sender, EventArgs e)
{
// Get audio data in 1/2 second chunks
microphone.BufferDuration = TimeSpan.FromMilliseconds(100);
 
// Allocate memory to hold the audio data
buffer = new byte[microphone.GetSampleSizeInBytes(microphone.BufferDuration)];
 
// Allocate memory to hold the audio data
fingerprint = new double[ FFT.FourierTransform.NextPowerOfTwo((uint) microphone.GetSampleSizeInBytes(microphone.BufferDuration))];
 
// Set the stream back to zero in case there is already something in it
stream.SetLength(0);
 
WriteWavHeader(stream, microphone.SampleRate); // To save in .WAV format
 
if ((bool)cb_noise_reduction.IsChecked)
{
dtFingerprint.Start(); // Start the noise finger print detection
}
else
{
SetButtonStates(false, false, true);
UserHelp.Text = "Record";
StatusImage.Source = microphoneImage;
}
 
// Start recording
microphone.Start();
 
}

当 dtFingerprint 的时间到了,录音就会开始. 而在Microphone.BufferReady注册的事件如下:

private double cutoff = 0;
 
void microphone_BufferReady(object sender, EventArgs e)
{
// Retrieve audio data
microphone.GetData(buffer);
 
int index = 0;
 
double[] sampleBuffer = new double[FFT.FourierTransform.NextPowerOfTwo((uint)buffer.Length)];
 
for (int i = 0; i < buffer.Length; i += 2)
{
sampleBuffer[index] = Convert.ToDouble(BitConverter.ToInt16((byte[])buffer, i)); index++;
}
 
if (dtFingerprint.IsEnabled)
{
MessageBar.Text = "Computing noise fingerprint";
 
double[] xre = new double[sampleBuffer.Length]; // Real part
double[] xim = new double[sampleBuffer.Length]; // Immaginary part
 
FFT.FourierTransform.Compute((uint)sampleBuffer.Length, sampleBuffer, null, xre, xim, false);
 
double spectrum = 0;
 
for (int i = 0; i < xre.Length; i++)
{
spectrum = (float)(Math.Sqrt((xre[i] * xre[i]) + (xim[i] * xim[i]))); // Magnitude
if (spectrum > fingerprint[i])
{
fingerprint[i] = spectrum;
}
}
}
else
{
MessageBar.Text = "Recording....";
 
double cMagnitude = 0;
// double cPhase = 0;
 
double[] xre = new double[sampleBuffer.Length]; // Real part
double[] xim = new double[sampleBuffer.Length]; // Immaginary part
 
double[] ixre = new double[sampleBuffer.Length]; // Real part
double[] ixim = new double[sampleBuffer.Length]; // Immaginary part
 
double[] fftoutput = new double[sampleBuffer.Length];
byte[] output = new byte[buffer.Length];
 
FFT.FourierTransform.Compute((uint)sampleBuffer.Length, sampleBuffer, null, xre, xim, false);
 
for (int i = 0; i < xre.Length; i++)
{
cMagnitude = (float)(Math.Sqrt((xre[i] * xre[i]) + (xim[i] * xim[i]))); // Magnitude
 
if (cMagnitude < (fingerprint[i] ))
{
xre[i] *= cutoff; xre[(xre.Length - 1) - i] *= cutoff;
xim[i] *= cutoff; xim[(xre.Length - 1) - i] *= cutoff;
}
}
 
FFT.FourierTransform.Compute((uint)xre.Length, xre, xim, ixre, ixim, true);
 
index = 0;
short tmp = 0;
 
for (int i = 0; i < buffer.Length / 2; i++)
{
tmp = (short)ixre[i];
output[index] = (byte)((short)tmp & 255); output[index + 1] = (byte)((((short)tmp) >> 8) & 255);
index += 2;
}
 
// Store the audio data in a stream
//stream.Write(buffer, 0, buffer.Length);
stream.Write(output, 0, output.Length);
}
 
}

下载

Example Code

总结

本文主要展示了如何截取,并处理音频的例子。 本文翻译自Audio Noise Reduction in Windows Phone

This page was last modified on 9 October 2013, at 22:08.
163 page views in the last 30 days.
×