GitHub地址
https://github.com/ar1st0crat/NWaves
CSDN下载地址
https://download.csdn.net/download/g313105910/16588600
安装
可在NuGet上获得。通过
PM> Install-Package NWaves
新版本0.9.4出局了!更快,更聪明,更有特色。在这里阅读有关更改的文章
主要特征
- 主要DSP变换(FFT、DCT、MDCT、STFT、FWT、Hilbert、Hartley、Mellin、倒谱、Goertzel)
- 信号建设者(正弦,白色/粉红色/红色/Perlin噪声,awgn,三角形,锯齿,方形,脉冲,斜坡,ADSR,可波)
- 基本LTI数字滤波器(移动平均,梳状,Savitzky-Golay,预/消重点,直流移除,Rasta)
- FIR/IIR滤波(离线和在线),零相位滤波
- 双四通滤波器(低通,高通,带通,缺口,全通,峰值,搁置)
- 1极滤波器(低通,高通)
- IIR滤波器(Bessel,Butterworth,Chebyshev I&II,Elliptic,Thiran)
- 基本运算(卷积、互相关、整流、放大)
- 块卷积(重叠-添加/重叠-脱机和在线保存)
- 基本滤波器设计和分析(群延迟、零点/极点、BP、BR、HP从Lp到LP、SOS、组合滤波器)
- LTI滤波器的状态空间表示
- FIR滤波器设计:频率采样,窗口-正弦,等波纹(Remez/Parks-McClellan)
- 非线性滤波器(中值滤波、过驱动和失真效应)
- 窗口功能(Hamming,Blackman,Hann,高斯,Kaiser,KBD,三角形,Lanczos,平顶,Bartlett-Hann)
- 心理声学滤波器组(MEL,BARK,临界波段,ERB,八度)和VTLN翘曲
- 可定制的特征提取(时域、光谱、MFCC、PNCC/SPNCC、LPC、LPCC、PLP、AMS)
- 预配置的MFCC萃取器:HTK(MFCC-FB24),Slaney(MFCC-FB40)
- LPC转换:LPC<->倒谱,LPC<->LSF
- 特征后处理(均值和方差归一化,添加增量)和csv序列化
- 光谱特征(质心,扩展,平整度,熵,滚转,对比度,峰顶,降低,噪声,mpeg 7)
- 谐波特征(谐波质心和传播,不谐波,三刺激,奇偶比)
- 时域特性(均方根、能量、过零率、熵)
- 基音跟踪(自相关,INE,ZCR+Schmitt触发器,HSS/HPS,倒谱)
- 时间尺度修正(相位声码器,具有相同相位锁定的PV,WSOLA,PaulStretch)
- 简单重采样,插值,抽取
- 带限重采样
- 小波:Haar,db,symlet,coiflet
- 多相滤波器
- 降噪(谱减法,科幻式维纳滤波)
- 信封跟随
- 声音效果(回声,颤音,瓦华,相位,合唱,颤音,侧边音,音高移动,变形,机器人化,耳语)
- 谐波/撞击分离
- Griffin-Lim算法
- Karplus-强合成
- PADSynth合成
- 自适应滤波(LMS,NLMS,LMF,SignLMS,RLS)
- 简单调制/解调(AM,环形,调频,PM)
- 简单的音频回放和录音
NWaves哲学
NWaves最初是为了研究、可视化和教授DSP和声音编程的基础知识。所有算法都是用C#编写的,尽可能简单,最初主要是为脱机处理而设计的(现在也有许多在线方法可用)。但这并不意味着库只能用于玩具项目;是的,它不是用C/C++或ASM编写的,但不是这样非常为了许多目的,也要慢慢来。
快速启动
与一维信号一起工作
// Create signal from samples repeated 100 times
float[] samples = new [] { 0.5f, 0.2f, -0.3f, 1.2f, 1.6f, -1.8f, 0.3f, -0.2f };
var s = new DiscreteSignal(8000, samples).Repeat(100);
var length = s.Length;
var duration = s.Duration;
var echoSignal = s + s.Delay(50);
var marginSignal = s.First(64).Concatenate(s.Last(64));
var repeatMiddle = s[400, 500].Repeat(10);
var mean = s.Samples.Average();
var sigma = s.Samples.Average(x => (x - mean) * (x - mean));
var normSignal = s - mean;
normSignal.Attenuate(sigma);
信号建设者
DiscreteSignal sinusoid =
new SineBuilder()
.SetParameter("frequency", 500.0/*Hz*/)
.SetParameter("phase", Math.PI / 6)
.OfLength(1000)
.SampledAt(44100/*Hz*/)
.Build();
DiscreteSignal noise =
new RedNoiseBuilder()
.SetParameter("min", -2.5)
.SetParameter("max", 2.5)
.OfLength(800)
.SampledAt(44100)
.DelayedBy(200)
.Build();
DiscreteSignal noisy =
new SineBuilder()
.SetParameter("min", -10.0)
.SetParameter("max", 10.0)
.SetParameter("freq", 1200.0/*Hz*/)
.OfLength(1000)
.SampledAt(44100)
.SuperimposedWith(noise)
.Build();
信号建设者也可以充当实时样本生成器:
SignalBuilder lfo =
new TriangleWaveBuilder()
.SetParameter("min", 100)
.SetParameter("max", 1500)
.SetParameter("frequency", 2.0/*Hz*/)
.SampledAt(16000/*Hz*/);
//while (...)
{
var sample = lfo.NextSample();
//...
}
信号和波形文件:
DiscreteSignal left, right;
// load
using (var stream = new FileStream("sample.wav", FileMode.Open))
{
var waveFile = new WaveFile(stream);
left = waveFile[Channels.Left];
right = waveFile[Channels.Right];
}
// save
using (var stream = new FileStream("saved_mono.wav", FileMode.Create))
{
var waveFile = new WaveFile(left);
waveFile.SaveTo(stream);
}
using (var stream = new FileStream("saved_stereo.wav", FileMode.Create))
{
var waveFile = new WaveFile(new [] { left, right });
waveFile.SaveTo(stream);
}
变换
对于每个转换,都有一个相应的转换器对象。每个转换器对象Direct()
和Inverse()
方法。
FFT
// Complex FFT transformer:
var fft = new Fft(1024);
// Real FFT transformer (faster):
var rfft = new RealFft(1024);
float[] real = signal.First(1024).Samples;
float[] imag = new float [1024];
// in-place FFT
fft.Direct(real, imag);
// ...do something with real and imaginary parts of the spectrum...
// in-place IFFT
fft.Inverse(real, imag);
// post-processed FFT:
var magnitudeSpectrum =
fft.MagnitudeSpectrum(signal[1000, 2024]);
var powerSpectrum =
fft.PowerSpectrum(signal.First(1024), normalize: false);
var logPowerSpectrum =
fft.PowerSpectrum(signal.Last(1024))
.Samples
.Select(s => Scale.ToDecibel(s))
.ToArray();
// rfft will run faster;
// real FFT transforms real-valued signal to complex-valued spectrum,
// hence prototypes of methods are same, except Direct/Inverse:
rfft.Direct(real, real, imag); // real -> (real, imag)
rfft.Inverse(real, imag, real); // (real, imag) -> real
var magnitudeSpectrum =
rfft.MagnitudeSpectrum(signal[1000, 2024]);
var powerSpectrum =
rfft.PowerSpectrum(signal.First(1024), normalize: false);
// ...
NWaves中的许多方法都重载了以输出缓冲区作为参数的版本。因此,只要有可能,就重用内存:
float[] spectrum = new float[1024];
for (var i = start; i < end; i += step)
{
rfft.MagnitudeSpectrum(signal[i, i + 1024], spectrum);
// ...
// do something with spectrum
}
STFT
// Short-Time Fourier Transform:
var stft = new Stft(1024, 256, WindowTypes.Hamming);
var timefreq = stft.Direct(signal);
var reconstructed = stft.Inverse(timefreq);
var spectrogram = stft.Spectrogram(signal);
倒谱变换
// Cepstral transformer:
var ct = new CepstralTransform(24, fftSize: 512);
// complex cepstrum
var cepstrum = ct.Direct(signal);
// or
ct.Direct(input, output);
// real cepstrum
ct.RealCepstrum(input, output);
小波
var fwt = new Fwt(192, new Wavelet("db5"));
// or
//var fwt = new Fwt(192, new Wavelet(WaveletFamily.Daubechies, 5));
var output = new float[192];
var reconstructed = new float[192];
fwt.Direct(input, output);
fwt.Inverse(output, reconstructed);
业务:
// convolution
var conv = Operation.Convolve(signal, kernel);
var xcorr = Operation.CrossCorrelate(signal1, signal2);
// block convolution
var filtered = Operation.BlockConvolve(signal, kernel, 4096, FilteringMethod.OverlapAdd);
// resampling
var resampled = Operation.Resample(signal, 22050);
var interpolated = Operation.Interpolate(signal, 3);
var decimated = Operation.Decimate(signal, 2);
var updown = Operation.ResampleUpDown(signal, 3, 2);
// time scale modification
var stretch = Operation.TimeStretch(signal, 0.7, TsmAlgorithm.Wsola);
var cool = Operation.TimeStretch(signal, 16, TsmAlgorithm.PaulStretch);
// envelope following
var envelope = Operation.Envelope(signal);
// rectification
var halfRect = Operation.HalfRectify(signal);
var fullRect = Operation.FullRectify(signal);
// spectral subtraction
var clean = Operation.SpectralSubtract(signal, noise);
过滤器和效果:
var maFilter = new MovingAverageFilter(7);
var smoothedSignal = maFilter.ApplyTo(signal);
var frequency = 800.0/*Hz*/;
var notchFilter = new BiQuad.NotchFilter(frequency / signal.SamplingRate);
var notchedSignal = notchFilter.ApplyTo(signal);
// filter analysis:
var transferFunction = new TransferFunction(new [] { 1, 0.5, 0.2 }, new [] { 1, -0.8, 0.3 });
var filter = new IirFilter(transferFunction);
// we can also write this:
// var filter = new IirFilter(new [] { 1, 0.5, 0.2 }, new [] { 1, -0.8, 0.3 });
// var transferFunction = filter.Tf;
// ...
// if we simply want to apply filter and don't care much about FDA precision:
// read more in tutorial
var impulseResponse = transferFunction.ImpulseResponse();
var magnitudeResponse = transferFunction.FrequencyResponse().Magnitude;
var phaseResponse = transferFunction.FrequencyResponse().Phase;
var b = transferFunction.Numerator;
var a = transferFunction.Denominator;
var zeros = transferFunction.Zeros;
var poles = transferFunction.Poles;
var gd = transferFunction.GroupDelay();
var pd = transferFunction.PhaseDelay();
// some examples of FIR filter design:
var kernel = DesignFilter.FirWinLp(345, 0.15);
var lpFilter = new FirFilter(kernel);
// HP filter can be obtained from LP with the same cutoff frequency:
var hpFilter = DesignFilter.FirLpToHp(lpFilter);
// design BP filter
var bpFilter = DesignFilter.FirWinBp(123, 0.05, 0.15);
// design equiripple HP filter
var bpFilter = DesignFilter.FirEquirippleHp(123, 0.34, 0.355, 0.05, 0.95);
// sequence of filters:
var cascade = filter * firFilter * notchFilter;
var filtered = cascade.ApplyTo(signal);
// equivalent to:
var filtered = filter.ApplyTo(signal);
filtered = firFilter.ApplyTo(filtered);
filtered = notchFilter.ApplyTo(filtered);
// parallel combination of filters:
var parallel = filter1 + filter2;
filtered = parallel.ApplyTo(signal);
// audio effects:
var flanger = new FlangerEffect(signal.SamplingRate);
var wahwah = new WahwahEffect(signal.SamplingRate, lfoFrequency: 2/*Hz*/);
var processed = wahwah.ApplyTo(flanger.ApplyTo(signal));
// this will create intermediate copy of the signal
// FilterChain is memory-efficient:
var filters = new FilterChain();
filters.Add(flanger);
filters.Add(wahwah);
processed = filters.ApplyTo(signal);
在线处理
对象的所有类都支持联机处理。IOnlineFilter
接口。目前,所有的LTI过滤器,FilterChain
类,块卷积器(OlaBlockConvolver
, OlsBlockConvolver
)和音频效果包含Process(sample)
和Process(bufferIn, bufferOut)
方法负责在线处理。
简单地处理一个接一个的数据样本:
var outputSample = filter.Process(sample);
或者准备必要的缓冲区(或者只是使用它们,如果它们来自您系统的另一部分):
float[] output;
...
void NewChunkAvailable(float[] chunk)
{
filter.Process(chunk, output);
}
// if input chunk shouldn't necessarily be preserved, it can be overwritten:
void NewChunkAvailable(float[] chunk)
{
filter.Process(chunk, chunk);
}
分块卷积器:
// Overlap-Add / Overlap-Save
FirFilter filter = new FirFilter(kernel);
var blockConvolver = OlaBlockConvolver.FromFilter(filter, 4096);
// processing loop:
// while new input sample is available
{
var outputSample = blockConvolver.Process(sample);
}
// or:
// while new input buffer is available
{
blockConvolver.Process(input, output);
}
特征提取器
高度可定制的特征提取器可用于离线和在线处理(MFCC系列、LPC、音调等)。
var mfccOptions = new MfccOptions
{
SamplingRate = signal.SamplingRate,
FeatureCount = 13,
FrameDuration = 0.032/*sec*/,
HopDuration = 0.015/*sec*/,
FilterBankSize = 26,
PreEmphasis = 0.97,
//...unspecified parameters will have default values
};
var mfccExtractor = new MfccExtractor(mfccOptions);
var mfccVectors = mfccExtractor.ComputeFrom(signal);
// serialize current config to JSON file:
using (var config = new FileStream("file.json", FileMode.Create))
{
config.SaveOptions(mfccOptions);
}
var lpcOptions = new LpcOptions
{
SamplingRate = signal.SamplingRate,
LpcOrder = 15
};
var lpcExtractor = new LpcExtractor(lpcOptions);
var lpcVectors = lpcExtractor.ParallelComputeFrom(signal);
var opts = new MultiFeatureOptions
{
SamplingRate = signal.SamplingRate,
FeatureList = "centroid, flatness, c1+c2+c3"
};
var spectralExtractor = new SpectralFeaturesExtractor(opts);
opts.FeatureList = "all";
var tdExtractor = new TimeDomainFeaturesExtractor(opts);
var vectors = FeaturePostProcessing.Join(
tdExtractor.ParallelComputeFrom(signal),
spectralExtractor.ParallelComputeFrom(signal));
// each vector will contain 1) all time-domain features (energy, rms, entropy, zcr)
// 2) specified spectral features
// open config from JSON file:
PnccOptions options;
using (var config = new FileStream("file.json", FileMode.Open))
{
options = config.LoadOptions<PnccOptions>();
}
var pnccExtractor = new PnccExtractor(pnccOptions);
var pnccVectors = pnccExtractor.ComputeFrom(signal, /*from*/1000, /*to*/60000 /*sample*/);
FeaturePostProcessing.NormalizeMean(pnccVectors);
// serialization
using (var csvFile = new FileStream("mfccs.csv", FileMode.Create))
{
var serializer = new CsvFeatureSerializer(mfccVectors);
await serializer.SerializeAsync(csvFile);
}
前处理
在语音处理中,在信号的主处理之前,通常会用到预强调滤波器.
有三个选项可以执行预强调过滤:
- 特征提取器构造函数中预强调系数的设置
- 在处理和处理滤波信号之前应用滤波器。
- 对信号进行就地滤波和处理。
第一个选项稍微慢一些,但是它不会分配额外的内存,也不会改变输入信号(因此,在默认情况下,它可能应该是选择)。如果不需要保留输入信号,那么第三种选择是最好的。如果输入信号必须被保留,并且没有额外的内存问题,那么第二种方法是首选的(它会更快)。
// option 1:
var opts = new MfccOptions
{
SamplingRate = signal.SamplingRate,
FeatureCount = 13,
PreEmphasis = 0.95
};
var mfccExtractor = new MfccExtractor(opts);
var mfccVectors = mfccExtractor.ComputeFrom(signal);
// option 2:
// ApplyTo() will create new signal (allocate new memory)
opts.PreEmphasis = 0;
mfccExtractor = new MfccExtractor(opts);
var pre = new PreEmphasisFilter(0.95);
var filtered = pre.ApplyTo(signal);
mfccVectors = mfccExtractor.ComputeFrom(filtered);
// option 3:
// process array or DiscreteSignal samples in-place:
for (var i = 0; i < signal.Length; i++)
{
signal[i] = pre.Process(signal[i]);
}
// or simply:
// pre.Process(signal.Samples, signal.Samples);
mfccVectors = mfccExtractor.ComputeFrom(signal);
播放录音
MciAudioPlayer和MciAudioRecorder只在Windows端工作,因为它们使用winmm.dll和mci命令。
IAudioPlayer player = new MciAudioPlayer();
// play entire file
await player.PlayAsync("temp.wav");
// play file from 16000th sample to 32000th sample
await player.PlayAsync("temp.wav", 16000, 32000);
// ...in some event handler
player.Pause();
// ...in some event handler
player.Resume();
// ...in some event handler
player.Stop();
// recording
IAudioRecorder recorder = new MciAudioRecorder();
// ...in some event handler
recorder.StartRecording(16000);
// ...in some event handler
recorder.StopRecording("temp.wav");