Click here to Skip to main content
15,895,142 members
Articles / Programming Languages / C#

Visualizing Sound

Rate me:
Please Sign up or sign in to vote.
4.94/5 (24 votes)
6 Nov 2012CPOL7 min read 77.7K   7.7K   67  
Listen or playback sound and visualize the frequency spread.
// -----------------------------------------------------------------------
// <copyright file="Graph.cs" company="None.">
//  By Philip R. Braica (HoshiKata@aol.com, VeryMadSci@gmail.com)
//
//  Distributed under the The Code Project Open License (CPOL)
//  http://www.codeproject.com/info/cpol10.aspx
// </copyright>
// -----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace SoundFiltering
{
    /// <summary>
    /// Graph class.
    /// </summary>
    public partial class Graph : UserControl
    {
        /// <summary>
        /// Constructor.
        /// </summary>
        public Graph()
        {
            InitializeComponent();
            pictureBox1.Resize += new EventHandler(pictureBox1_Resize);
            ShowLegend = true;
            ShowAxis = true;
            ShowGrid = true;
        }

        /// <summary>
        /// Trigger redraw.
        /// </summary>
        public void TriggerRedraw()
        {
            timer1.Enabled = true;
        }

        /// <summary>
        /// On resize trigger redraw.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void pictureBox1_Resize(object sender, EventArgs e)
        {
            TriggerRedraw();
        }

        /// <summary>
        /// Timer to redraw
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer1_Tick(object sender, EventArgs e)
        {
        //    timer1.Enabled = false;
            doDraw();
        }

        /// <summary>
        /// Back buffer.
        /// </summary>
        protected System.Drawing.Bitmap m_back = null;

        /// <summary>
        /// Front buffer.
        /// </summary>
        protected System.Drawing.Bitmap m_front = null;

        /// <summary>
        /// Resize buffers as needed.
        /// </summary>
        protected void resizeBuffers()
        {
            int w = pictureBox1.Width;
            int h = pictureBox1.Height;
            w = w < 10 ? 10 : w;
            h = h < 10 ? 10 : h;
            System.Drawing.Bitmap old = m_back;
            if (old == null)
            {
                m_back = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            }
            else
            {
                if ((m_back.Width != w) || (m_back.Height != h))
                {
                    m_back = new Bitmap(w, h, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                }
            }
            if (m_back != old)
            {
                if (old != null)
                {
                    old.Dispose();
                }
            }
        }

        /// <summary>
        /// Swap buffers.
        /// </summary>
        protected void swapBuffers()
        {
            System.Drawing.Bitmap tmp = m_front;
            m_front = m_back;
            m_back = tmp;
        }
        
        /// <summary>
        /// Do the drawing.
        /// </summary>
        protected void doDraw()
        {
            resizeBuffers();
            using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(m_back))
            {
                paintGraph(g, m_back.Width, m_back.Height);
            }
            swapBuffers();
            pictureBox1.Image = m_front;
        }

        /// <summary>
        /// Paint the legend or not.
        /// </summary>
        public bool ShowLegend { get; set; }

        /// <summary>
        /// Paint the axis or not.
        /// </summary>
        public bool ShowAxis { get; set; }

        /// <summary>
        /// Paint the grid or not.
        /// </summary>
        public bool ShowGrid { get; set; }

        /// <summary>
        /// Paint.
        /// </summary>
        /// <param name="g"></param>
        /// <param name="w"></param>
        /// <param name="h"></param>
        protected void paintGraph(System.Drawing.Graphics g, int w, int h)
        {
            double ux = 0;
            double lx = 0;
            double uy = 0;
            double ly = 0;
            bool foundFirst = false;
            List<string> legend = new List<string>();
            List<Color> colors = new List<Color>();
            for (int i = 0; i < Lines.Count; i++)
            {
                if (Lines[i] == null) continue;
                if (Lines[i].Show == false) continue;
                if (!foundFirst)
                {
                    ux = Lines[i].MaxX;
                    lx = Lines[i].MinX;
                    uy = Lines[i].MaxY;
                    ly = Lines[i].MinY;
                    foundFirst = true;
                }
                legend.Add(Lines[i].Name);
                colors.Add(Lines[i].LineColor);
                ux = ux > Lines[i].MaxX ? ux : Lines[i].MaxX;
                lx = lx < Lines[i].MinX ? lx : Lines[i].MinX;
                uy = uy > Lines[i].MaxY ? uy : Lines[i].MaxY;
                ly = ly < Lines[i].MinY ? ly : Lines[i].MinY;
            }
            g.Clear(BackColor);

            Rectangle drawRectangle = new Rectangle(0, 0, w, h);
            if (ShowLegend)
            {
                int legendHeight = 5 + (int)g.MeasureString("AjtW", Font).Height;
                if (h - legendHeight > 10)
                {
                    drawRectangle = new Rectangle(0, 0, w, h - legendHeight);
                    Rectangle legendRectangle = new Rectangle(0, h - legendHeight + 2, w, legendHeight - 2);
                    drawLegend(g, legendRectangle, legend, colors);    
                }
            }
            if (ShowAxis)
            {
                // To do.

            }
            if (!foundFirst) return;
            for (int i = 0; i < Lines.Count; i++)
            {
                if (Lines[i] == null) continue;
                if (Lines[i].Show == false) continue;
                Lines[i].Draw(g, drawRectangle, lx, ux, ly, uy);
            }
        }

        /// <summary>
        /// Draw legend.
        /// </summary>
        /// <param name="g"></param>
        /// <param name="r"></param>
        /// <param name="legend"></param>
        /// <param name="colors"></param>
        protected void drawLegend(Graphics g, Rectangle r, List<string> legend, List<Color> colors)
        {
            int cnt = legend.Count;
            int sx = r.X;
            for (int i = 0; i < legend.Count; i++)
            {
                int ex = r.X  + (r.Width * (i + 1) / cnt);
                using (SolidBrush sb = new SolidBrush(colors[i]))
                {
                    g.FillRectangle(sb, sx, r.Y, ex, r.Height);
                }
                using (SolidBrush sb2 = new SolidBrush(colors[i].GetBrightness() > 0.5? Color.Black : Color.White))
                {
                    SizeF sf = g.MeasureString(legend[i], Font);
                    PointF pf = new PointF(
                        (float)(0.5* (ex + sx - sf.Width)), 
                        (float)( r.Y + 0.5 * (sf.Height - sf.Height)));
                    g.DrawString(legend[i], Font, sb2, pf);
                }
                sx = ex;
            }
        }

        /// <summary>
        /// Text color for markers.
        /// </summary>
        public Color TextColor = Color.Black;

        /// <summary>
        /// Lines to draw.
        /// </summary>
        public List<Line> Lines = new List<Line>();

        /// <summary>
        /// Line class.
        /// </summary>
        public class Line
        {
            /// <summary>
            /// Name.
            /// </summary>
            public string Name = string.Empty;
            
            /// <summary>
            /// Show it or not.
            /// </summary>
            public bool Show = true;

            /// <summary>
            /// X values if null, use index of Y.
            /// </summary>
            public double[] X = null;

            /// <summary>
            /// Y values.
            /// </summary>
            public double[] Y = null;

            /// <summary>
            /// Line color defaults to black.
            /// </summary>
            public Color LineColor = Color.Black;

            /// <summary>
            /// Line width.
            /// </summary>
            public float LineWidth = 1f;

            /// <summary>
            /// Minimum X.
            /// </summary>
            public double MinX
            {
                get
                {
                    double [] xt = X;
                    if (xt == null) return 0;
                    if (xt.Length < 1) return 0;
                    double min = xt[0];
                    for (int i = 0; i < xt.Length; i++)
                    {
                        min = min < xt[i] ? min : xt[i];
                    }
                    return min;
                }
            }

            /// <summary>
            /// Maximum X.
            /// </summary>
            public double MaxX
            {
                get
                {
                    double[] xt = X;
                    if (xt == null)
                    {
                        double[] yt = Y;
                        if (yt == null) return 0;
                        return yt.Length;
                    }
                    if (xt.Length < 1) return 0;
                    double max = xt[0];
                    for (int i = 0; i < xt.Length; i++)
                    {
                        max = max > xt[i] ? max : xt[i];
                    }
                    return max;
                }
            }

            /// <summary>
            /// Minimum Y.
            /// </summary>
            public double MinY
            {
                get
                {
                    double[] yt = Y;
                    if (yt == null) return 0;
                    if (yt.Length < 1) return 0;
                    double min = yt[0];
                    for (int i = 0; i < yt.Length; i++)
                    {
                        min = min < yt[i] ? min : yt[i];
                    }
                    return min;
                }
            }

            /// <summary>
            /// Maximum Y.
            /// </summary>
            public double MaxY
            {
                get
                {
                    double[] yt = Y;
                    if (yt == null) return 0;
                    if (yt.Length < 1) return 0;
                    double max = yt[0];
                    for (int i = 0; i < yt.Length; i++)
                    {
                        max = max > yt[i] ? max : yt[i];
                    }
                    return max;                
                }
            }

            /// <summary>
            /// Update
            /// </summary>
            /// <param name="y">Y value or null to use index.</param>
            public void Update(float[] y)
            {
                Update(null, y);
            }

            /// <summary>
            /// Update.
            /// </summary>
            /// <param name="x">X values or null to use index.</param>
            /// <param name="y">Y value or null to use index.</param>
            public void Update(float[] x, float[] y)
            {
                // Thread and null safe.
                if (x != null)
                {
                    bool resize = X == null ? true : X.Length == x.Length ? false : true;
                    double[] xn = resize ? new double[x.Length] : X;
                    for (int i = 0; i < x.Length; i++)
                    {
                        xn[i] = x[i];
                    }
                    X = xn;
                }
                else
                {
                    x = null;
                }
                if (y != null)
                {
                    bool resize = Y == null ? true : Y.Length == y.Length ? false : true;
                    double[] yn = resize ? new double[y.Length] : Y;
                    for (int i = 0; i < y.Length; i++)
                    {
                        yn[i] = y[i];
                    }
                    Y = yn;
                }
                else
                {
                    Y = null;
                }                
            }

            /// <summary>
            /// Update
            /// </summary>
            /// <param name="y">Y value or null to use index.</param>
            public void Update(double[] y)
            {
                Update(null, y);
            }

            /// <summary>
            /// Update
            /// </summary>
            /// <param name="x">X values or null to use index.</param>
            /// <param name="y">Y value or null to use index.</param>
            public void Update(double[] x, double[] y)
            {
                // Thread and null safe.
                if (x != null)
                {
                    bool resize = X == null ? true : X.Length == x.Length ? false : true;
                    double[] xn = resize ? new double[x.Length] : X;
                    for (int i = 0; i < x.Length; i++)
                    {
                        xn[i] = x[i];
                    }
                    X = xn;
                }
                else
                {
                    x = null;
                }
                if (y != null)
                {
                    bool resize = Y == null ? true : Y.Length == y.Length ? false : true;
                    double[] yn = resize ? new double[y.Length] : Y;
                    for (int i = 0; i < y.Length; i++)
                    {
                        yn[i] = y[i];
                    }
                    Y = yn;
                }
                else
                {
                    Y = null;
                }
            }

            /// <summary>
            /// Draw within r on g, assuming the lower / upper X values are lx, ux, and ly, uy for Y.
            /// </summary>
            /// <param name="g"></param>
            /// <param name="r"></param>
            /// <param name="lx"></param>
            /// <param name="ux"></param>
            /// <param name="ly"></param>
            /// <param name="uy"></param>
            public void Draw(Graphics g, Rectangle r, double lx, double ux, double ly, double uy)
            {
                double[] x = X;
                double[] y = Y;
                if (y == null) return;
                int n = x == null ? y.Length : y.Length > x.Length ? x.Length : y.Length;

                double ox = 0;
                double oy = 0;
                ux = ux == lx ? lx + 1 : ux;
                uy = uy == ly ? ly + 1 : uy;
                double xscale = r.Width / (ux - lx);
                double yscale = r.Height / (uy - ly);
                using (Pen p = new Pen(LineColor, LineWidth))
                {
                    for (int i = 0; i < n; i++)
                    {
                        double xi = x == null ? i : x[i];
                        double yi = y[i];
                        xi = r.X + ((xi - lx) * xscale);
                        yi = r.Y + r.Height - ((yi - ly) * yscale);
                        if (i != 0)
                        {
                            g.DrawLine(p, (float)xi, (float)yi, (float)ox, (float)oy);
                        }
                        ox = xi;
                        oy = yi;
                    }
                }
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead
United States United States
Phil is a Principal Software developer focusing on weird yet practical algorithms that run the gamut of embedded and desktop (PID loops, Kalman filters, FFTs, client-server SOAP bindings, ASIC design, communication protocols, game engines, robotics).

In his personal life he is a part time mad scientist, full time dad, and studies small circle jujitsu, plays guitar and piano.

Comments and Discussions