2009-07-31 15 views

Respuesta

44

Esto es como código abierto como se pone:

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

Esta función producirá algo como esto:

enter image description here

Esto toma una serie de muestras en formato de punto flotante (donde todos los valores de muestra van de -1 a +1). Si sus datos originales están realmente en forma de una matriz de bytes [], tendrá que trabajar un poco para convertirlos en flotantes []. Avíseme si lo necesita también.

actualización: puesto que la pregunta hecha técnicamente algo para rendir una matriz de bytes, aquí hay un par de métodos de ayuda:

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

Actualización 2: Olvidé que hay una mejor manera de hacer esto :

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

estoy tan enamorado de for bucles, supongo.

+2

Hasta que autorizarla y cargar la OP $ 20 por el uso :) – Martin

+5

@ Martin: Me carga que sólo para mirar * * en ella. ¿Dónde están mis $ 20? :) – MusiGenesis

+0

algoritmo fresco. Solo estaba calculando la muestra más cercana a mi píxel y poniendo un punto allí, ¡pero haciendo el máximo y el mínimo del rango de ese píxel parece mucho mejor! – andrewrk

3

He sido fan de ZedGraph durante muchos años y lo he usado para mostrar todo tipo de datos en varios proyectos.

El código de ejemplo representa gráficamente una serie de dobles que varía entre -1 y 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(); 
} 

Los anteriores imita muestra el estilo de un editor de audio mediante la supresión de los ejes y el cambio de los colores para producir el siguiente:

ZedGraph audio wave

3

en NAudio, no hay código para dibujar las formas de onda de audio en WinForms y WPF. Echa un vistazo a los proyectos de demostración para ver ejemplos de cómo usarlo.

10

Modifiqué un poco la solución de MusiGenesis. Esto me dio un resultado mucho mejor, especialmente con la música house :)

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 Estoy completamente de acuerdo! thx para la adición. Este método proporciona formas de onda en el estilo de SoundCloud.com. –

+0

¡Bien hecho! Gracias por publicar esto. – Reinaldo

+0

@robyy Cómo llamar a Bitmap DrawNormalizedAudio (Lista data, Color foreColor, Color BackColor, Size image Size) ¿función? – Illaya

5

con código adaptado de Robby y el uso de Graphics.Fill/DrawClosedCurve con el antialiasing, aparece un resultado bastante bien. enter image description here

aquí está el código:

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

Esto se ve bastante bien. – MusiGenesis

Cuestiones relacionadas