205 lines
7.8 KiB
C#
205 lines
7.8 KiB
C#
using LibDynamics;
|
|
using FftComplex = FftSharp.Complex;
|
|
using FftTransform = FftSharp.Transform;
|
|
|
|
namespace LibAudioVisualizer {
|
|
public class Visualizer {
|
|
//private int _m;
|
|
private double[] _sampleData;
|
|
private DateTime _lastTime;
|
|
private SecondOrderDynamicsForArray _dynamics;
|
|
|
|
/// <summary>
|
|
/// 采样数据
|
|
/// </summary>
|
|
public double[] SampleData => _sampleData;
|
|
|
|
public Visualizer(int waveDataSize) {
|
|
if (!(Get2Flag(waveDataSize)))
|
|
throw new ArgumentException("长度必须是 2 的 n 次幂");
|
|
//_m = (int)Math.Log2(waveDataSize);
|
|
_lastTime = DateTime.Now;
|
|
_sampleData = new double[waveDataSize];
|
|
_dynamics = new SecondOrderDynamicsForArray(1, 1, 1, 0, waveDataSize / 2);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 判断是否是 2 的整数次幂
|
|
/// </summary>
|
|
/// <param name="num"></param>
|
|
/// <returns></returns>
|
|
private bool Get2Flag(int num) {
|
|
if (num < 1)
|
|
return false;
|
|
return (num & num - 1) == 0;
|
|
}
|
|
|
|
public void PushSampleData(double[] waveData) {
|
|
if (waveData.Length > _sampleData.Length) {
|
|
Array.Copy(waveData, waveData.Length - _sampleData.Length, _sampleData, 0, _sampleData.Length);
|
|
} else {
|
|
Array.Copy(_sampleData, waveData.Length, _sampleData, 0, _sampleData.Length - waveData.Length);
|
|
Array.Copy(waveData, 0, _sampleData, _sampleData.Length - waveData.Length, waveData.Length);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取频谱数据 (数据已经删去共轭部分)
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public double[] GetSpectrumData(String funcname) {
|
|
DateTime now = DateTime.Now;
|
|
double deltaTime = (now - _lastTime).TotalSeconds;
|
|
_lastTime = now;
|
|
|
|
int len = _sampleData.Length;
|
|
FftComplex[] data = new FftComplex[len];
|
|
|
|
for (int i = 0; i < len; i++)
|
|
data[i] = new FftComplex(_sampleData[i], 0);
|
|
|
|
FftTransform.FFT(data);
|
|
|
|
int halfLen = len / 2;
|
|
double[] spectrum = new double[halfLen]; // 傅里叶变换结果左右对称, 只需要取一半
|
|
for (int i = 0; i < halfLen; i++)
|
|
spectrum[i] = data[i].Magnitude / len;
|
|
|
|
//var window = new FftSharp.Windows.Hamming();
|
|
if (funcname == null) {
|
|
var window = new FftSharp.Windows.Bartlett();
|
|
window.Create(halfLen);
|
|
window.ApplyInPlace(spectrum, false);
|
|
} else if (funcname.Equals("RECTANGULAR")) {
|
|
var window = new FftSharp.Windows.Rectangular();
|
|
window.Create(halfLen);
|
|
window.ApplyInPlace(spectrum, false);
|
|
} else if (funcname.Equals("HANNING")) {
|
|
var window = new FftSharp.Windows.Hanning();
|
|
window.Create(halfLen);
|
|
window.ApplyInPlace(spectrum, false);
|
|
} else if (funcname.Equals("HAMMING")) {
|
|
var window = new FftSharp.Windows.Hamming();
|
|
window.Create(halfLen);
|
|
window.ApplyInPlace(spectrum, false);
|
|
} else if (funcname.Equals("BLACKMAN")) {
|
|
var window = new FftSharp.Windows.Blackman();
|
|
window.Create(halfLen);
|
|
window.ApplyInPlace(spectrum, false);
|
|
} else if (funcname.Equals("BARTLETT")) {
|
|
var window = new FftSharp.Windows.Bartlett();
|
|
window.Create(halfLen);
|
|
window.ApplyInPlace(spectrum, false);
|
|
} else {
|
|
var window = new FftSharp.Windows.Bartlett();
|
|
window.Create(halfLen);
|
|
window.ApplyInPlace(spectrum, false);
|
|
}
|
|
//return spectrum;
|
|
return _dynamics.Update(deltaTime, spectrum);
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// 取指定频率内的频谱数据
|
|
/// </summary>
|
|
/// <param name="spectrum">源频谱数据</param>
|
|
/// <param name="sampleRate">采样率</param>
|
|
/// <param name="frequency">目标频率</param>
|
|
/// <returns></returns>
|
|
public static double[] TakeSpectrumOfFrequency(double[] spectrum, double sampleRate, double frequency) {
|
|
double frequencyPerSampe = sampleRate / spectrum.Length;
|
|
|
|
int lengthInNeed = (int)(Math.Min(frequency / frequencyPerSampe, spectrum.Length));
|
|
double[] result = new double[lengthInNeed];
|
|
Array.Copy(spectrum, 0, result, 0, lengthInNeed);
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 简单的数据模糊
|
|
/// </summary>
|
|
/// <param name="data">数据</param>
|
|
/// <param name="radius">模糊半径</param>
|
|
/// <returns>结果</returns>
|
|
public static double[] MakeSmooth(double[] data, int radius) {
|
|
double[] GetWeights(int radius) {
|
|
double Gaussian(double x) => Math.Pow(Math.E, (-4 * x * x)); // 憨批高斯函数
|
|
|
|
int len = 1 + radius * 2; // 长度
|
|
int end = len - 1; // 最后的索引
|
|
double radiusF = (double)radius; // 半径浮点数
|
|
double[] weights = new double[len]; // 权重
|
|
|
|
for (int i = 0; i <= radius; i++) // 先把右边的权重算出来
|
|
weights[radius + i] = Gaussian(i / radiusF);
|
|
for (int i = 0; i < radius; i++) // 把右边的权重拷贝到左边
|
|
weights[i] = weights[end - i];
|
|
|
|
double total = weights.Sum();
|
|
for (int i = 0; i < len; i++) // 使权重合为 0
|
|
weights[i] = weights[i] / total;
|
|
|
|
return weights;
|
|
}
|
|
|
|
void ApplyWeights(double[] buffer, double[] weights) {
|
|
int len = buffer.Length;
|
|
for (int i = 0; i < len; i++)
|
|
buffer[i] = buffer[i] * weights[i];
|
|
}
|
|
|
|
|
|
double[] weights = GetWeights(radius);
|
|
double[] buffer = new double[1 + radius * 2];
|
|
|
|
double[] result = new double[data.Length];
|
|
if (data.Length < radius) {
|
|
Array.Fill(result, data.Average());
|
|
return result;
|
|
}
|
|
|
|
|
|
for (int i = 0; i < radius; i++) {
|
|
Array.Fill(buffer, data[i], 0, radius + 1); // 填充缺省
|
|
for (int j = 0; j < radius; j++) //
|
|
{
|
|
buffer[radius + 1 + j] = data[i + j];
|
|
}
|
|
|
|
ApplyWeights(buffer, weights);
|
|
result[i] = buffer.Sum();
|
|
}
|
|
|
|
for (int i = radius; i < data.Length - radius; i++) {
|
|
for (int j = 0; j < radius; j++) //
|
|
{
|
|
buffer[j] = data[i - j];
|
|
}
|
|
|
|
buffer[radius] = data[i];
|
|
|
|
for (int j = 0; j < radius; j++) //
|
|
{
|
|
buffer[radius + j + 1] = data[i + j];
|
|
}
|
|
|
|
ApplyWeights(buffer, weights);
|
|
result[i] = buffer.Sum();
|
|
}
|
|
|
|
for (int i = data.Length - radius; i < data.Length; i++) {
|
|
Array.Fill(buffer, data[i], 0, radius + 1); // 填充缺省
|
|
for (int j = 0; j < radius; j++) //
|
|
{
|
|
buffer[radius + 1 + j] = data[i - j];
|
|
}
|
|
|
|
ApplyWeights(buffer, weights);
|
|
result[i] = buffer.Sum();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
} |