reproducir play c# audio

play - naudio c#



¿Código de código abierto para presentar la forma de onda? (5)

¿Hay algún código de fuente abierta C # o biblioteca para presentar una forma de onda gráfica dada una matriz de bytes?


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


Esto es de código abierto:

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:

Esto toma una matriz de muestras en formato de coma 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 : dado que la pregunta técnicamente pidió algo para representar 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 for bucles, supongo.


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

El siguiente código de ejemplo representa una matriz 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(); }

La muestra anterior imita el estilo de un editor de audio al suprimir los ejes y cambiar los colores para producir lo siguiente:


Modifiqué la solución de MusiGenesis un poco. Esto me dio un resultado mucho mejor, especialmente con 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; }


con el código adaptado de robby y usando Graphics.Fill / DrawClosedCurve con antialiasing, obtengo un resultado bastante atractivo.

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