5

我想使用 C# 语言和 .NET Framework 创建一个完善的可视化系统。这可能看起来像在 Winamp 应用程序中。也许存在免费图书馆或一些描述如何做到这一点的有趣文章?示例: 替代文字 http://img44.imageshack.us/img44/9982/examplel.png

4

2 回答 2

16

你可以试试这些链接

OpenVP(是一个免费的开源平台,用于开发音乐可视化,用 C# 编写),请参阅 OpenVP 屏幕截图

C# 中的声音可视化器

使用 Managed Direct Sound 播放和可视化 WAV 文件

再见。

于 2009-08-30T22:16:53.757 回答
4

这是一个使用 WASAPI API 计算计算机上播放的任何声音的 FFT 的脚本。它使用 CSCore 及其WinformsVisualization示例:

using CSCore;
using CSCore.SoundIn;
using CSCore.Codecs.WAV;
using WinformsVisualization.Visualization;
using CSCore.DSP;
using CSCore.Streams;
using System;

public class SoundCapture
{

    public int numBars = 30;

    public int minFreq = 5;
    public int maxFreq = 4500;
    public int barSpacing = 0;
    public bool logScale = true;
    public bool isAverage = false;

    public float highScaleAverage = 2.0f;
    public float highScaleNotAverage = 3.0f;



    LineSpectrum lineSpectrum;

    WasapiCapture capture;
    WaveWriter writer;
    FftSize fftSize;
    float[] fftBuffer;

    SingleBlockNotificationStream notificationSource;

    BasicSpectrumProvider spectrumProvider;

    IWaveSource finalSource;

    public SoundCapture()
    {

        // This uses the wasapi api to get any sound data played by the computer
        capture = new WasapiLoopbackCapture();

        capture.Initialize();

        // Get our capture as a source
        IWaveSource source = new SoundInSource(capture);


        // From https://github.com/filoe/cscore/blob/master/Samples/WinformsVisualization/Form1.cs

        // This is the typical size, you can change this for higher detail as needed
        fftSize = FftSize.Fft4096;

        // Actual fft data
        fftBuffer = new float[(int)fftSize];

        // These are the actual classes that give you spectrum data
        // The specific vars of lineSpectrum here aren't that important because they can be changed by the user
        spectrumProvider = new BasicSpectrumProvider(capture.WaveFormat.Channels,
                    capture.WaveFormat.SampleRate, fftSize);

        lineSpectrum = new LineSpectrum(fftSize)
        {
            SpectrumProvider = spectrumProvider,
            UseAverage = true,
            BarCount = numBars,
            BarSpacing = 2,
            IsXLogScale = false,
            ScalingStrategy = ScalingStrategy.Linear
        };

        // Tells us when data is available to send to our spectrum
        var notificationSource = new SingleBlockNotificationStream(source.ToSampleSource());

        notificationSource.SingleBlockRead += NotificationSource_SingleBlockRead;

        // We use this to request data so it actualy flows through (figuring this out took forever...)
        finalSource = notificationSource.ToWaveSource();

        capture.DataAvailable += Capture_DataAvailable;
        capture.Start();
    }

    private void Capture_DataAvailable(object sender, DataAvailableEventArgs e)
    {
        finalSource.Read(e.Data, e.Offset, e.ByteCount);
    }

    private void NotificationSource_SingleBlockRead(object sender, SingleBlockReadEventArgs e)
    {
        spectrumProvider.Add(e.Left, e.Right);
    }

    ~SoundCapture()
    {
        capture.Stop();
        capture.Dispose();
    }

    public float[] barData = new float[20];

    public float[] GetFFtData()
    {
        lock (barData)
        {
            lineSpectrum.BarCount = numBars;
            if (numBars != barData.Length)
            {
                barData = new float[numBars];
            }
        }

        if (spectrumProvider.IsNewDataAvailable)
        {
            lineSpectrum.MinimumFrequency = minFreq;
            lineSpectrum.MaximumFrequency = maxFreq;
            lineSpectrum.IsXLogScale = logScale;
            lineSpectrum.BarSpacing = barSpacing;
            lineSpectrum.SpectrumProvider.GetFftData(fftBuffer, this);
            return lineSpectrum.GetSpectrumPoints(100.0f, fftBuffer);
        }
        else
        {
            return null;
        }
    }

    public void ComputeData()
    {


        float[] resData = GetFFtData();

        int numBars = barData.Length;

        if (resData == null)
        {
            return;
        }

        lock (barData)
        {
            for (int i = 0; i < numBars && i < resData.Length; i++)
            {
                // Make the data between 0.0 and 1.0
                barData[i] = resData[i] / 100.0f;
            }

            for (int i = 0; i < numBars && i < resData.Length; i++)
            {
                if (lineSpectrum.UseAverage)
                {
                    // Scale the data because for some reason bass is always loud and treble is soft
                    barData[i] = barData[i] + highScaleAverage * (float)Math.Sqrt(i / (numBars + 0.0f)) * barData[i];
                }
                else
                {
                    barData[i] = barData[i] + highScaleNotAverage * (float)Math.Sqrt(i / (numBars + 0.0f)) * barData[i];
                }
            }
        }

    }
}

然后,当从不同的脚本中检索 barData 时,建议先锁定它,因为这是在单独的线程上修改的。

我不确定我从哪里得到GetSpectrumPoints的,因为它似乎不在Github Repo中,但它就在这里。只需将其粘贴到该文件中,我的代码就可以工作了。

public float[] GetSpectrumPoints(float height, float[] fftBuffer)
{
    SpectrumPointData[] dats = CalculateSpectrumPoints(height, fftBuffer);
    float[] res = new float[dats.Length];
    for (int i = 0; i < dats.Length; i++)
    {
        res[i] = (float)dats[i].Value;
    }

    return res;
}
于 2016-08-14T16:35:33.353 回答