Click here to Skip to main content
12,451,446 members (54,682 online)
Click here to Skip to main content

Tagged as

Stats

37.5K views
5.4K downloads
58 bookmarked
Posted

Visualizing Sound

, 6 Nov 2012 CPOL
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)

Share

About the Author

HoshiKata
Software Developer (Senior) KMC Systems
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.

You may also be interested in...

Pro
Pro
| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.160826.1 | Last Updated 7 Nov 2012
Article Copyright 2012 by HoshiKata
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid