2009-07-31 68 views

回答

44

这是作为开源,因为它得到:

public static void DrawNormalizedAudio(ref float[] data, PictureBox pb, 
    Color color) 
{ 
    Bitmap bmp; 
    if (pb.Image == null) 
    { 
     bmp = new Bitmap(pb.Width, pb.Height); 
    } 
    else 
    { 
     bmp = (Bitmap)pb.Image; 
    } 

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

    using (Graphics g = Graphics.FromImage(bmp)) 
    { 
     g.Clear(Color.Black); 
     Pen pen = new Pen(color); 
     int size = data.Length; 
     for (int iPixel = 0; iPixel < width; iPixel++) 
     { 
      // determine start and end points within WAV 
      int start = (int)((float)iPixel * ((float)size/(float)width)); 
      int end = (int)((float)(iPixel + 1) * ((float)size/(float)width)); 
      float min = float.MaxValue; 
      float max = float.MinValue; 
      for (int i = start; i < end; i++) 
      { 
       float val = data[i]; 
       min = val < min ? val : min; 
       max = val > max ? val : max; 
      } 
      int yMax = BORDER_WIDTH + height - (int)((max + 1) * .5 * height); 
      int yMin = BORDER_WIDTH + height - (int)((min + 1) * .5 * height); 
      g.DrawLine(pen, iPixel + BORDER_WIDTH, yMax, 
       iPixel + BORDER_WIDTH, yMin); 
     } 
    } 
    pb.Image = bmp; 
} 

这个函数将产生这样的事情:

enter image description here

这需要采样的阵列中的浮点格式(其中所有采样值范围从-1到+1)。如果您的原始数据实际上是以byte []数组的形式存在,那么您必须执行一些工作才能将其转换为float []。让我知道你是否也需要这个。

更新:由于技术上的要求使用一些问题来呈现一个字节数组,这里有一对夫妇的辅助方法:

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

更新2:我忘了还有一个更好的办法来做到这一点:

public float[] FloatArrayFromByteArray(byte[] input) 
{ 
    float[] output = new float[input.Length/4]; 
    Buffer.BlockCopy(input, 0, output, 0, input.Length); 
    return output; 
} 

我只是很喜欢for循环,我猜。

+2

除非你对其进行许可,并收取每OP使用:) – Martin 2009-10-01 18:56:41

+5

$ @马丁20:我负责的说只是看着* *它。我的20美元在哪里? :) – MusiGenesis 2009-10-02 00:58:36

+0

酷算法。我只是计算最接近我的像素的样本,并在那里放了一个点,但是做该像素范围的最大值和最小值看起来好多了! – andrewrk 2010-05-06 06:15:51

3

我一直是ZedGraph多年的粉丝,并用它来显示各种项目中的各种数据。

以下示例代码图表双打的-1和1之间变化的数组:

void DisplayWaveGraph(ZedGraphControl graphControl, double[] waveData) 
{ 
    var pane = graphControl.GraphPane; 
    pane.Chart.Border.IsVisible = false; 
    pane.Chart.Fill.IsVisible = false; 
    pane.Fill.Color = Color.Black; 
    pane.Margin.All = 0; 
    pane.Title.IsVisible = false; 
    pane.XAxis.IsVisible = false; 
    pane.XAxis.Scale.Max = waveData.Length - 1; 
    pane.XAxis.Scale.Min = 0; 
    pane.YAxis.IsVisible = false; 
    pane.YAxis.Scale.Max = 1; 
    pane.YAxis.Scale.Min = -1; 
    var timeData = Enumerable.Range(0, waveData.Length) 
          .Select(i => (double) i) 
          .ToArray(); 
    pane.AddCurve(null, timeData, waveData, Color.Lime, SymbolType.None); 
    graphControl.AxisChange(); 
} 

将上述样品模拟音频编辑器通过抑制轴以及改变颜色,以产生以下的式:

ZedGraph audio wave

3

NAudio,有代码来绘制在两个WinForms和WPF音频波形。查看演示项目,了解如何使用它的示例。

10

我修改了MusiGenesis的解决方案。 这给了我一个更好的结果,尤其是在音乐房子:)

public static Bitmap DrawNormalizedAudio(List<float> data, Color foreColor, Color backColor, Size imageSize) 
{ 
    Bitmap bmp = new Bitmap(imageSize.Width, imageSize.Height); 

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

    using (Graphics g = Graphics.FromImage(bmp)) 
    { 
     g.Clear(backColor); 
     Pen pen = new Pen(foreColor); 
     float size = data.Count; 
     for (float iPixel = 0; iPixel < width; iPixel += 1) 
     { 
      // determine start and end points within WAV 
      int start = (int)(iPixel * (size/width)); 
      int end = (int)((iPixel + 1) * (size/width)); 
      if (end > data.Count) 
       end = data.Count; 

      float posAvg, negAvg; 
      averages(data, start, end, out posAvg, out negAvg); 

      float yMax = BORDER_WIDTH + height - ((posAvg + 1) * .5f * height); 
      float yMin = BORDER_WIDTH + height - ((negAvg + 1) * .5f * height); 

      g.DrawLine(pen, iPixel + BORDER_WIDTH, yMax, iPixel + BORDER_WIDTH, yMin); 
     } 
    } 

    return bmp; 
} 


private static void averages(List<float> data, int startIndex, int endIndex, out float posAvg, out float negAvg) 
{ 
    posAvg = 0.0f; 
    negAvg = 0.0f; 

    int posCount = 0, negCount = 0; 

    for (int i = startIndex; i < endIndex; i++) 
    { 
     if (data[i] > 0) 
     { 
      posCount++; 
      posAvg += data[i]; 
     } 
     else 
     { 
      negCount++; 
      negAvg += data[i]; 
     } 
    } 

    posAvg /= posCount; 
    negAvg /= negCount; 
} 
5

与适应代码罗比和使用Graphics.Fill/DrawClosedCurve抗锯齿,我得到一个非常漂亮的结果。 enter image description here

下面的代码:

using System; 
using System.Drawing; 
using System.Drawing.Drawing2D; 

namespace Soundfingerprinting.Audio.Services 
{ 
    public static class AudioVisualizationService 
    { 
     public class WaveVisualizationConfiguration 
     { 
      public Nullable<Color> AreaColor { get; set; } 
      public Nullable<Color> EdgeColor { get; set; } 
      public int EdgeSize { get; set; } 
      public Nullable<Rectangle> Bounds { get; set; } 
      public double Overlap { get; set; } 
      public int Step { get; set; } 
     } 

     public static void DrawWave(float[] data, Bitmap bitmap, WaveVisualizationConfiguration config = null) 
     { 
      Color areaColor = Color.FromArgb(0x7F87CEFA);// Color.LightSkyBlue; semi transparent 
      Color edgeColor = Color.DarkSlateBlue; 
      int edgeSize = 2; 
      int step = 2; 
      double overlap = 0.10f; // would better use a windowing function 
      Rectangle bounds = Rectangle.FromLTRB(0, 0, bitmap.Width, bitmap.Height); 

      if (config != null) 
      { 
       edgeSize = config.EdgeSize; 
       if (config.AreaColor.HasValue) 
        areaColor = config.AreaColor.GetValueOrDefault(); 
       if (config.EdgeColor.HasValue) 
        edgeColor = config.EdgeColor.GetValueOrDefault(); 
       if (config.Bounds.HasValue) 
        bounds = config.Bounds.GetValueOrDefault(); 

       step = Math.Max(1, config.Step); 
       overlap = config.Overlap; 
      } 

      float width = bounds.Width; 
      float height = bounds.Height; 

      using (Graphics g = Graphics.FromImage(bitmap)) 
      { 
       Pen edgePen = new Pen(edgeColor); 
       edgePen.LineJoin = LineJoin.Round; 
       edgePen.Width = edgeSize; 
       Brush areaBrush = new SolidBrush(areaColor); 

       float size = data.Length; 
       PointF[] topCurve = new PointF[(int)width/step]; 
       PointF[] bottomCurve = new PointF[(int)width/step]; 
       int idx = 0; 
       for (float iPixel = 0; iPixel < width; iPixel += step) 
       { 
        // determine start and end points within WAV 
        int start = (int)(iPixel * (size/width)); 
        int end = (int)((iPixel + step) * (size/width)); 
        int window = end - start; 
        start -= (int)(overlap * window); 
        end += (int)(overlap * window); 
        if (start < 0) 
         start = 0; 
        if (end > data.Length) 
         end = data.Length; 

        float posAvg, negAvg; 
        averages(data, start, end, out posAvg, out negAvg); 

        float yMax = height - ((posAvg + 1) * .5f * height); 
        float yMin = height - ((negAvg + 1) * .5f * height); 
        float xPos = iPixel + bounds.Left; 
        if (idx >= topCurve.Length) 
         idx = topCurve.Length - 1; 
        topCurve[idx] = new PointF(xPos, yMax); 
        bottomCurve[bottomCurve.Length - idx - 1] = new PointF(xPos, yMin); 
        idx++; 
       } 

       PointF[] curve = new PointF[topCurve.Length * 2]; 
       Array.Copy(topCurve, curve, topCurve.Length); 
       Array.Copy(bottomCurve, 0, curve, topCurve.Length, bottomCurve.Length); 
       g.InterpolationMode = InterpolationMode.HighQualityBicubic; 
       g.SmoothingMode = SmoothingMode.AntiAlias; 
       g.FillClosedCurve(areaBrush, curve, FillMode.Winding, 0.15f); 
       if (edgeSize > 0) 
        g.DrawClosedCurve(edgePen, curve, 0.15f, FillMode.Winding); 
      } 

     } 

     private static void averages(float[] data, int startIndex, int endIndex, out float posAvg, out float negAvg) 
     { 
      posAvg = 0.0f; 
      negAvg = 0.0f; 

      int posCount = 0, negCount = 0; 

      for (int i = startIndex; i < endIndex; i++) 
      { 
       if (data[i] > 0) 
       { 
        posCount++; 
        posAvg += data[i]; 
       } 
       else 
       { 
        negCount++; 
        negAvg += data[i]; 
       } 
      } 

      if (posCount > 0) 
       posAvg /= posCount; 
      if (negCount > 0) 
       negAvg /= negCount; 
     } 
    } 
}