C# 应用程序:来自音频输出的示例音频 -> FFT 算法 -> 可视化

Posted

技术标签:

【中文标题】C# 应用程序:来自音频输出的示例音频 -> FFT 算法 -> 可视化【英文标题】:C# Application: Sample Audio from Audio Output -> FFT Algorithm -> Visualize 【发布时间】:2019-01-01 20:10:27 【问题描述】:

我是一名新手音频程序员,我是第一次使用 FFT。我想从我的音频输出中采样音频。之后,我想用 FFT 算法计算这些数据。我正在使用Naudio.dll

抽样要求:

音频输出示例 必须在 GUI 上调整采样频率 缓冲区大小必须可在 GUI 上调整 幅度值必须是原始的(无对数滤波器/无 sqrt() 滤波器...)

我该如何解决这个问题?使用哪个 dll?

我尝试使用 NAudio 的示例聚合器。但我不知道怎么做。

提前致谢

public class SampleAggregator : ISampleProvider

    public event EventHandler<MaxSampleEventArgs> MaximumCalculated;
    private float maxValue;
    private float minValue;
    public int NotificationCount  get; set; 
    int count;

    public event EventHandler<FftEventArgs> FftCalculated;
    public bool PerformFFT  get; set; 
    private readonly Complex[] fftBuffer;
    private readonly FftEventArgs fftArgs;
    private int fftPos;
    private readonly int fftLength;
    private readonly int m;
    private readonly ISampleProvider source;

    private readonly int channels;

    public SampleAggregator(ISampleProvider source, int fftLength = 1024)
    
        channels = source.WaveFormat.Channels;
        if (!IsPowerOfTwo(fftLength))
        
            throw new ArgumentException("FFT Length must be a power of two");
        
        m = (int)Math.Log(fftLength, 2.0);
        this.fftLength = fftLength;
        fftBuffer = new Complex[fftLength];
        fftArgs = new FftEventArgs(fftBuffer);
        this.source = source;
    

    static bool IsPowerOfTwo(int x)
    
        return (x & (x - 1)) == 0;
    


    public void Reset()
    
        count = 0;
        maxValue = minValue = 0;
    

    private void Add(float value)
    
        if (PerformFFT && FftCalculated != null)
        
            fftBuffer[fftPos].X = (float)(value * FastFourierTransform.HammingWindow(fftPos, fftLength));
            fftBuffer[fftPos].Y = 0;
            fftPos++;
            if (fftPos >= fftBuffer.Length)
            
                fftPos = 0;
                // 1024 = 2^10
                FastFourierTransform.FFT(true, m, fftBuffer);
                FftCalculated(this, fftArgs);
            
        

        maxValue = Math.Max(maxValue, value);
        minValue = Math.Min(minValue, value);
        count++;
        if (count >= NotificationCount && NotificationCount > 0)
        
            MaximumCalculated?.Invoke(this, new MaxSampleEventArgs(minValue, maxValue));
            Reset();
        
    

    public WaveFormat WaveFormat => source.WaveFormat;

    public int Read(float[] buffer, int offset, int count)
    
        var samplesRead = source.Read(buffer, offset, count);

        for (int n = 0; n < samplesRead; n+=channels)
        
            Add(buffer[n+offset]);
        
        return samplesRead;
    


public class MaxSampleEventArgs : EventArgs

    [DebuggerStepThrough]
    public MaxSampleEventArgs(float minValue, float maxValue)
    
        MaxSample = maxValue;
        MinSample = minValue;
    
    public float MaxSample  get; private set; 
    public float MinSample  get; private set; 


public class FftEventArgs : EventArgs

    [DebuggerStepThrough]
    public FftEventArgs(Complex[] result)
    
        Result = result;
    
    public Complex[] Result  get; private set; 

【问题讨论】:

【参考方案1】:

NAudio github repository 包含NAudioWpfDemo 项目,其中还包括频谱分析仪的实现。我试图解释下面最重要的部分。我将相关代码粘贴在此答案中,但您需要查看原始源代码才能完全理解它。

演示项目使用 WPF Polyline 元素(请参阅SpectrumAnalyser.xaml)来可视化 FFT 数据。

<UserControl x:Class="NAudioWpfDemo.SpectrumAnalyser">
    <Canvas Background="Black">
        <Polyline x:Name="polyline1" Stroke="Yellow" StrokeThickness="1"/>
    </Canvas>
</UserControl>

在SpectrumAnalyser.xaml.cs 中,您可以找到更新Polyline 元素的代码。方法Update(Complex[] fftResults)接收FFT数据,然后循环遍历FFT数据(fftResults数组)中的所有数据点...

for (int n = 0; n < fftResults.Length / 2; n+= binsPerPoint)

    // averaging out bins
    double yPos = 0;
    for (int b = 0; b < binsPerPoint; b++)
    
        yPos += GetYPosLog(fftResults[n+b]);
    
    AddResult(n / binsPerPoint, yPos / binsPerPoint);

...调用GetYPosLog(Complex c)计算每个FFT数据点的dB值...

double intensityDB = 10 * Math.Log10(Math.Sqrt(c.X * c.X + c.Y * c.Y));

...并将转换后的数据点添加到方法AddResult(int index, double power)中的polyline1元素

Point p = new Point(CalculateXPos(index), power);
polyline1.Points.Add(p);

【讨论】:

感谢您的回答。 github.com/tjscience/audion.cscore 对我来说看起来很有希望,但我也不知道如何使用它.. :-/ 是的,你也可以使用它。 readme 包含频谱分析仪的示例代码。尝试让该代码运行,然后对其进行更改以使其符合您的要求。如果您遇到困难,请针对您的具体问题发布一个新问题。

以上是关于C# 应用程序:来自音频输出的示例音频 -> FFT 算法 -> 可视化的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在 C# 中使用 NAudio 创建新的音频输入?

C# Android 暂停音频文件

如何监控设备的音频输出以判断声音是不是来自扬声器/耳机插孔?

C# 中的麦克风到扬声器音频流

使用 C# 测试音频文件

C#获取话筒主峰值(实时音频输出分贝量)