×
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.
122 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×