WPF + NAudio 实现音波可视化

实现思路

NAudio是由 Mark Heath 编写的开源 .NET 音频库,提供丰富的音频操作功能,也提供了音频可视化的Demo。其基本思路如下:

  1. 计算样本和像素比例:
    • 计算每个音频样本的字节数。
    • 根据音频流的长度和样本字节数计算总样本数。
    • 计算每个像素点对应的样本数,以及步长(峰值像素加上间隔像素)。
  2. 绘制波形图:
    • 循环遍历整个宽度,使用 peakProvider 获取像素对应的音频的峰值。

      NAudio提供了四种高峰值计算策略:
      1. Max_Absolute_Value:最大绝对值,即从数据集中找出具有最大绝对值的数据点作为峰值。
      2. Max_Rms_Value:最大均方根值,即从数据集中找出具有最大均方根值的数据点作为峰值。
      3. Sampled_Peaks:采样峰值,可能是指在数据集中随机或按照某种规则选取一定数量的峰值进行计算。
      4. Average:平均值,即计算数据集中所有点的平均值作为峰值。

    • 根据峰值和设置的高度计算音波上半部分和下半部分的线条高度,

      • 创建 WaveformItem 对象,设置其宽度、位置、高度和颜色。
      • WaveformItem 对象添加到集合中。
    • 计算当前和下一个峰值的最大和最小值,绘制间隔条,同样将宽度、位置、高度和颜色记录到 WaveformItem

    • 最后将WaveformItem 的集合,绘制矩形到Canvas上,实现音波的可视化

代码Demo

  • 获取峰值信息
public ObservableCollection<WaveformItem> Render(WaveStream waveStream, IPeakProvider peakProvider, WaveFormRendererSettings settings)
{
    ObservableCollection<WaveformItem> waveformItems = new ObservableCollection<WaveformItem>();

    // 计算每个样本占据的字节数
    int bytesPerSample = (waveStream.WaveFormat.BitsPerSample / 8);

    // 计算总的音频样本数
    var samples = waveStream.Length / (bytesPerSample);

    // 计算每个像素点对应的音频样本数
    var samplesPerPixel = (int)(samples / settings.Width);

    // 计算步长,包括峰值像素和间隔像素
    var stepSize = settings.PixelsPerPeak + settings.SpacerPixels;

    // 初始化peakProvider,根据音频流和计算的样本数目
    peakProvider.Init(waveStream.ToSampleProvider(), samplesPerPixel * stepSize);

    // 调用重载的 Render 方法进行实际渲染
    waveformItems = Render(peakProvider, settings);

    return waveformItems;
}

private static ObservableCollection<WaveformItem> Render(IPeakProvider peakProvider, WaveFormRendererSettings settings)
{

    ObservableCollection<WaveformItem> waveformItems = new ObservableCollection<WaveformItem>();


    // 如果设置为使用分贝缩放,则创建 DecibelPeakProvider 来处理峰值
    if (settings.DecibelScale)
        peakProvider = new DecibelPeakProvider(peakProvider, 48);

    int x = 0;
    var currentPeak = peakProvider.GetNextPeak();
    var midPoint = settings.TopHeight;

    int i = 0;
    // 在整个宽度内循环绘制音波图
    while (x < settings.Width)
    {
        // 获取下一个峰值
        var nextPeak = peakProvider.GetNextPeak();


        // 绘制峰值条
        {
            // 计算并绘制顶底部峰值条
            var topLineHeight = settings.TopHeight * currentPeak.Max;

            // 计算并绘制底部峰值条
            var bottomLineHeight = settings.BottomHeight * currentPeak.Min;

            var waveHeight = Math.Abs(topLineHeight + bottomLineHeight);

            var waveformItem = new WaveformItem
            {
                Width = settings.PixelsPerPeak,
                Left = i * (settings.PixelsPerPeak + settings.SpacerPixels),
                Top =(settings.Height - waveHeight) /2,
                Height = waveHeight,
                Color = settings.WaveBrush
            };
            waveformItems.Add(waveformItem);
        }

        // 绘制间隔条
        {
            // 计算最大和最小峰值,并绘制顶部间隔条
            var max = Math.Min(currentPeak.Max, nextPeak.Max);
            var min = Math.Max(currentPeak.Min, nextPeak.Min);

            // 绘制峰值条
            // 计算并绘制顶底部峰值条
            var topLineHeight = settings.TopHeight * currentPeak.Max;

            // 计算并绘制底部峰值条
            var bottomLineHeight = settings.BottomHeight * currentPeak.Min;


            var waveHeight = Math.Abs(topLineHeight + bottomLineHeight);


            var waveformItem = new WaveformItem
            {
                Width = settings.SpacerPixels,
                Left = settings.PixelsPerPeak + i * (settings.PixelsPerPeak + settings.SpacerPixels),
                Top = (settings.Height - waveHeight) / 2,
                Height = waveHeight,
                Color = settings.SpacerBrush
            };
            waveformItems.Add(waveformItem);
        }

        x += settings.PixelsPerPeak + settings.SpacerPixels;
        i++;
        currentPeak = nextPeak;
    }

    return waveformItems;
    
}
  • 绘制矩形到Canvas
private void DrawRectangles()
{
    if (canvas == null
        || WaveSetting.ItemsSource == null
        || WaveSetting.ItemsSource.Count == 0)
    {
        return;
    }
    canvas.Children.Clear();

    canvas.Width = this.Width;

    foreach (var item in WaveSetting.ItemsSource)
    {
        var rect = new System.Windows.Shapes.Rectangle
        {
            Width = item.Width,
            Height = item.Height,
            Fill = item.Color,
            RadiusX = item.Width * 0.4, // 设置圆角的水平半径
            RadiusY = item.Width * 0.4  // 设置圆角的垂直半径
        };

        Canvas.SetLeft(rect, item.Left);
        Canvas.SetTop(rect, item.Top);

        canvas.Children.Add(rect);
    }
}

效果

在这里插入图片描述

项目参考

  1. naudio/NAudio.WaveFormRenderer:使用 System.Drawing 渲染各种样式的音频文件波形的简单实用程序 — naudio/NAudio.WaveFormRenderer: Simple utility to render waveforms of audio files in various styles using System.Drawing (github.com)

样式参考

  1. https://www.veed.io/

代码地址

NitasDemo/02WaveDemo at main · Nita121388/NitasDemo (github.com)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值