【问题标题】:How to render audio waveform?如何渲染音频波形?
【发布时间】:2012-07-12 04:41:54
【问题描述】:
  1. 是否有任何与音频/编程相关的堆栈交换站点?
  2. 我正在尝试在 WinForms 中创建 wave form
  3. 我应该使用什么算法?

例如,如果我每个像素(垂直线)有 200 个样本,我应该从 200 个样本的那部分中提取最低和最高样本吗?或者我应该绘制低样本和高样本的平均值吗?也许两者颜色不同?

【问题讨论】:

  • 它背后没有太多的算法。你得到了数字,来自音频文件的样本。用线连接点。折线效果最好。颜色和缩放完全取决于您的口味。
  • 是的,但它仅在您使用非常大的缩放时才有效。如果每个像素有很多样本,则需要选择不同的解决方案。
  • 为什么不直接对样本进行采样 - 为每个像素选择一个?如果您缩小到足以让单个像素表示 200 个样本,我不确定知道该范围内的最小值和最大值有多大用处。这就是放大的目的。
  • 如果我使用最小/最大样本,我可以很容易地看到最高/最低值,这可能是模拟录音等错误。如果我使用样本作为样本,我将看不到任何有趣的东西。如果我使用平均值,所有曲目看起来都相似。
  • 这个问题以前被问过,在这里得到了回答。这是我给出的答案。 stackoverflow.com/questions/11091924/… 尝试搜索更多内容。

标签: c# audio visualization data-visualization waveform


【解决方案1】:

这将帮助您在 C# 中使用 nAudio 从音频文件生成波形...

using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class test : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
    string strPath = Server.MapPath("audio/060.mp3");
    string SongID = "2";
    byte[] bytes = File.ReadAllBytes(strPath);
    WriteToFile(SongID,strPath, bytes);
    Response.Redirect("Main.aspx");
    }

private void WriteToFile(string SongID, string strPath, byte[] Buffer)
{
    try
    {
        int samplesPerPixel = 128;
        long startPosition = 0;
        //FileStream newFile = new FileStream(GeneralUtils.Get_SongFilePath() + "/" + strPath, FileMode.Create);
        float[] data = FloatArrayFromByteArray(Buffer);

        Bitmap bmp = new Bitmap(1170, 200);

        int BORDER_WIDTH = 5;
        int width = bmp.Width - (2 * BORDER_WIDTH);
        int height = bmp.Height - (2 * BORDER_WIDTH);

        NAudio.Wave.Mp3FileReader reader = new NAudio.Wave.Mp3FileReader(strPath, wf => new NAudio.FileFormats.Mp3.DmoMp3FrameDecompressor(wf));
        NAudio.Wave.WaveChannel32 channelStream = new NAudio.Wave.WaveChannel32(reader);

        int bytesPerSample = (reader.WaveFormat.BitsPerSample / 8) * channelStream.WaveFormat.Channels;

        using (Graphics g = Graphics.FromImage(bmp))
        {

            g.Clear(Color.White);
            Pen pen1 = new Pen(Color.Gray);
            int size = data.Length;

            string hexValue1 = "#009adf";
            Color colour1 = System.Drawing.ColorTranslator.FromHtml(hexValue1);
            pen1.Color = colour1;

            Stream wavestream = new NAudio.Wave.Mp3FileReader(strPath, wf => new NAudio.FileFormats.Mp3.DmoMp3FrameDecompressor(wf));

            wavestream.Position = 0;
            int bytesRead1;
            byte[] waveData1 = new byte[samplesPerPixel * bytesPerSample];
            wavestream.Position = startPosition + (width * bytesPerSample * samplesPerPixel);

            for (float x = 0; x < width; x++)
            {
                short low = 0;
                short high = 0;
                bytesRead1 = wavestream.Read(waveData1, 0, samplesPerPixel * bytesPerSample);
                if (bytesRead1 == 0)
                    break;
                for (int n = 0; n < bytesRead1; n += 2)
                {
                    short sample = BitConverter.ToInt16(waveData1, n);
                    if (sample < low) low = sample;
                    if (sample > high) high = sample;
                }
                float lowPercent = ((((float)low) - short.MinValue) / ushort.MaxValue);
                float highPercent = ((((float)high) - short.MinValue) / ushort.MaxValue);
                float lowValue = height * lowPercent;
                float highValue = height * highPercent;
                g.DrawLine(pen1, x, lowValue, x, highValue);

            }
        }

        string filename = Server.MapPath("image/060.png");
        bmp.Save(filename);
        bmp.Dispose();

    }
catch (Exception e)
    {

    }
}
public float[] FloatArrayFromStream(System.IO.MemoryStream stream)
{
    return FloatArrayFromByteArray(stream.GetBuffer());
}

public float[] FloatArrayFromByteArray(byte[] input)
{
    float[] output = new float[input.Length / 4];
    for (int i = 0; i < output.Length; i++)
    {
        output[i] = BitConverter.ToSingle(input, i * 4);
    }
    return output;
}

}

【讨论】:

  • 谢谢,但这个问题有点老了 :) 我已经完成了我的应用程序。 i.imgur.com/DfAkGrv.png
  • 嘿@zgnilec 我需要你的帮助。我也在尝试与您开发的相同。你能帮我完成吗?我只完成了波形生成。我还需要在波形上做缩放功能和移动线。帮助我。
  • 然后通过电子邮件联系我。我稍后再回答,因为我现在在工作。
  • multiverse 在收件箱 dot com。发送您的所有问题。
【解决方案2】:
  1. 试试 dsp.stackexchange.com

  2. 每个像素有 200 个样本,您可以尝试多种方法。无论您做什么,通常最好在 0 上方和下方绘制每条垂直线,即。分别处理正负样本值。可能最简单的方法就是计算 RMS。在如此低的分辨率下,峰值可能会给您带来误导性的波形表示。

【讨论】:

    【解决方案3】:

    您可以使用代码项目中的AudioControl

    看看这个:Generating various audio waveforms in C#

    如果最初实现您的代码,这些项目可能对您有用:

    【讨论】:

    • 那个项目不完整,作者说它有时会导致错误。我需要构建更高级的东西。我试图将我的输出与 Audacity 波形进行比较,它看起来非常相似。但是我需要一些堆栈溢出的建议。
    【解决方案4】:

    以防有人遇到这种情况:

    您可以将每个像素的样本视为您的缩放级别,在更高级别(缩小更多)您可能出于性能原因需要对其进行二次采样。

    您很可能需要一个适合屏幕的固定宽度来绘制并使用虚拟滚动(因此您可能没有几百万像素的绘制区域)。

    您可以通过迭代音频数据来计算每个像素的值:skip(滚动位置 * 每个像素的样本数)+(像素 * 每个像素的样本数)每个像素的样本数。 这允许高性能无限缩放和滚动,因为您只读取和绘制最小量来填充视图。 滚动宽度是用音频数据长度/每像素样本数计算的。

    音频样本通常以两种方式之一显示,即样本范围的峰值或 rms 值。 rms 值是通过对样本范围内所有值的平方求和来计算的,将总和除以样本长度,如果是平方根,则 rms 值(rms 会比平均值高一点,是感知响度的一个很好的度量)

    您可以通过多种方式提高性能,例如增加子采样(导致细节丢失)、限制滚动以及在渲染前一个滚动之前触发新滚动时使绘制请求可取消。

    【讨论】:

      【解决方案5】:

      只是为了记录它,如果你想让音频文件填充输出图像的宽度

      samplesPerPixel = (reader.Length / bytesPerSample) / width ;
      

      【讨论】:

        猜你喜欢
        • 2012-08-27
        • 1970-01-01
        • 1970-01-01
        • 2014-12-14
        • 2016-12-09
        • 2017-07-08
        • 2010-10-28
        • 1970-01-01
        • 2012-02-01
        相关资源
        最近更新 更多