2009-07-31 10 views

Antwort

44

Dies ist als Open Source, wie es erhält:

Diese Funktion erzeugen in etwa so
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; 
} 

:

Diese enter image description here

ein Array von Proben in Gleitkommaformat nimmt (wo Alle Abtastwerte liegen zwischen -1 und +1. Wenn Ihre ursprünglichen Daten tatsächlich in Form eines byte [] -Arrays vorliegen, müssen Sie ein wenig arbeiten, um es in float [] umzuwandeln. Lass es mich wissen, wenn du das auch brauchst.

aktualisieren: Da die Frage technisch etwas gebeten, ein Byte-Array zu machen, hier sind ein paar Hilfsmethoden:

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

Update 2: Ich habe vergessen, es einen besseren Weg, dies zu tun ist :

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

ich mit for Schleifen nur so verliebt bin, denke ich.

+2

Bis Sie es lizenzieren, und das OP 20 pro Gebrauch $ laden :) – Martin

+5

@ Martin: Ich gebiete, dass nur für * suchen * auf sie. Wo sind meine 20 $? :) – MusiGenesis

+0

Cooler Algorithmus. Ich habe gerade das nächste Sample mit meinem Pixel berechnet und einen Punkt dorthin gesetzt, aber die Maximal- und Minimalwerte des Pixelbereichs zu sehen, sieht viel besser aus! – andrewrk

3

Ich bin seit vielen Jahren ein Fan von ZedGraph und habe es verwendet, um alle Arten von Daten in verschiedenen Projekten anzuzeigen.

Der folgende Beispielcode Graphen, die einen Array aus Doppel zwischen -1 und 1 variierende:

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

Die obige Probe ahmt den Stil eines Audio-Editor durch die Achsen unterdrückt wird und die Farben zu ändern, die folgenden zu erzeugen:

ZedGraph audio wave

3

in NAudio gibt es Code Audio-Wellenformen in beiden WinForms und WPF zu zeichnen. Sehen Sie sich die Demo-Projekte an, um Anwendungsbeispiele zu sehen.

10

Ich modifizierte MusiGenesis Lösung ein wenig. Diese gab mir ein viel besseres Ergebnis, vor allem mit Hausmusik :)

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

+1 stimme ich voll und ganz zu! Danke für die Zugabe. Diese Methode gibt Wellenformen im Stil von SoundCloud.com. –

+0

Schön gemacht! Danke für das Posten. – Reinaldo

+0

@robyy Wie Bitmap DrawNormalizedAudio (Liste Daten, Color ForeColor, Color BackColor, Größe imageSize) Funktion aufrufen? – Illaya

5

mit angepassten Code von Robby und mit Graphics.Fill/DrawClosedCurve mit Anti-Aliasing, erhalte ich ein ziemlich gut aussehendes Ergebnis. enter image description here

hier ist der Code:

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

Das sieht ziemlich gut aus. – MusiGenesis

Verwandte Themen