2009-07-31 15 views

risposta

44

Questo è come open source come ottiene:

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

Questa funzione produrrà qualcosa di simile:

enter image description here

Ciò richiede una serie di campioni in formato in virgola mobile (dove tutti i valori campione vanno da -1 a +1). Se i tuoi dati originali sono in realtà sotto forma di una matrice di byte [], dovrai fare un po 'di lavoro per convertirli in float []. Fammi sapere se ne hai bisogno anche tu.

Aggiornamento: dal momento che la questione tecnicamente chiesto qualcosa per rendere un array di byte, qui ci sono un paio di metodi helper:

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: Ho dimenticato che c'è un modo migliore per fare questo :

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

sono così innamorata di for loop, immagino.

+2

Fino a quando si licenza, e caricare il PO di $ 20 per l'uso :) – Martin

+5

@ Martin: mi carica che solo per guardare * * a questo. Dove sono i miei $ 20? :) – MusiGenesis

+0

Algoritmo freddo. Stavo solo calcolando il campione più vicino al mio pixel e mettendo un punto lì, ma facendo il massimo e il minimo dell'intervallo di quel pixel sembra molto meglio! – andrewrk

3

Sono stato un fan di ZedGraph per molti anni e l'ho usato per visualizzare tutti i tipi di dati in vari progetti.

Il seguente codice di esempio Grafici una serie di doppie variabile tra -1 e 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(); 
} 

L'esempio simula sopra lo stile di un editor audio sopprimendo gli assi e cambiando i colori per produrre il seguente:

ZedGraph audio wave

3

in NAudio, c'è il codice per disegnare le forme d'onda audio in entrambe le WinForms e WPF. Dai un'occhiata ai progetti demo per esempi su come usarlo.

10

Ho modificato un po 'la soluzione di MusiGenesis. Questo mi ha dato un risultato molto migliore, soprattutto con la musica 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 Sono completamente d'accordo! grazie per l'aggiunta. Questo metodo fornisce forme d'onda nello stile di SoundCloud.com. –

+0

Ben fatto! Grazie per aver postato questo. – Reinaldo

+0

@robyy Come chiamare la funzione Bitmap DrawNormalizedAudio (Elenco , Colore foreColor, Colore backColor, Dimensione imageSize)? – Illaya

5

con il codice adattato da Robby e utilizzando Graphics.Fill/DrawClosedCurve con antialiasing, ottengo un buon risultato cercando. enter image description here

Ecco il codice:

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

Questo sembra abbastanza buono. – MusiGenesis

Problemi correlati