Files
2024-10-17 23:10:02 +08:00

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;
}
}
}