Display.cs

using System;

using System.Collections.Generic;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Windows.Forms;

 

namespace DZouch

{

    public partial class Display : UserControl

    {

        private static readonly double FREQ_DO = 55.0 * Math.Pow(2.0, 3.0 / 12.0);

        private FFT.RealFourier fft;

        private double min = double.NaN;

        private double max = double.NaN;

        private double freq = double.NaN;

        private double sampleRate;

 

        public Display()

        {

            InitializeComponent();

        }

 

        public void SetFFT(FFT.RealFourier fft)

        {

            this.fft = fft;

            Invalidate();

        }

 

        public void SetFreq(double f, double rate)

        {

            freq = f;

            sampleRate = rate;

            if (double.IsNaN(min) || double.IsNaN(max) || (f < min) || (f > max))

            {

                double octave = Math.Floor(Math.Log(f / FREQ_DO, 2.0));

                min = FREQ_DO * Math.Pow(2.0, octave - 0.5 / 12.0);

                max = FREQ_DO * Math.Pow(2.0, octave + 1 - 0.5 / 12.0);

            }

            Invalidate();

        }

 

        protected override void OnPaint(PaintEventArgs e)

        {

            if (fft == null || double.IsNaN(min) || double.IsNaN(max) || double.IsNaN(freq))

            {

                base.OnPaint(e);

                return;

            }

 

            Graphics dc = e.Graphics;

            Rectangle rect = ClientRectangle;

            DrawScale(dc, rect);

            DrawSpectrum(dc, rect);

            DrawMarker(dc, rect);

        }

 

        private void DrawScale(Graphics dc, Rectangle rect)

        {

            string[] labels = { "C", "", "D", "", "E", "F", "", "G", "", "A", "", "B" };

            double fdo = FREQ_DO * Math.Pow(2.0, Math.Ceiling(Math.Log(min / FREQ_DO, 2.0)));

            for (int index = 0; index < 12; index++)

            {

                double f = fdo * Math.Pow(2.0, index / 12.0);

                DrawMarker(dc, rect, f, Color.Gray, 3f, DashStyle.Dash, labels[index]);

            }

        }

 

        private void DrawMarker(Graphics dc, Rectangle rect)

        {

            DrawMarker(dc, rect, freq, Color.Red, 1f, DashStyle.Solid, string.Empty);

        }

 

        private void DrawMarker(Graphics dc, Rectangle rect, double f, Color color, float width, DashStyle style, string label)

        {

            if (!string.IsNullOrEmpty(label))

            {

                Font font = new Font("Arial", 12f);

                Size sz = dc.MeasureString(label, font).ToSize();

                if (f > min && f < max)

                {

                    int x = ScaleFreq(rect, f) - sz.Width / 2;

                    using (Brush brush = new SolidBrush(Color.Navy))

                    {

                        dc.DrawString(label, font, brush, x, rect.Top);

                    }

 

                    rect.Y += sz.Height;

                    rect.Height -= sz.Height;

                }

            }

 

            using (Pen pen = new Pen(color, width))

            {

                pen.DashStyle = style;

                if (f > min && f < max)

                {

                    int x = ScaleFreq(rect, f);

                    dc.DrawLine(pen, x, rect.Top, x, rect.Bottom);

                }

            }

        }

 

        private void DrawSpectrum(Graphics dc, Rectangle rect)

        {

            List<Point> points = new List<Point>();

 

            using (Pen pen = new Pen(Color.Blue))

            {

                int maxCount = (fft.Count * 2) / 3;

                for (int index = 0; index < maxCount; index++)

                {

                    double f = fft.F(index, sampleRate);

                    if (f < min || f > max) continue;

                    int x = ScaleFreq(rect, f);

                    int y = ScalePower(rect, Math.Log10(fft.Power(index) + 1));

                    points.Add(new Point(x, y));

                }

 

                if (points.Count > 1)

                {

                    dc.DrawLines(pen, points.ToArray());

                }

            }

        }

 

        private int ScaleFreq(Rectangle rect, double f)

        {

            return rect.Left + (int)(rect.Width * Math.Log(f / min, 2.0) / Math.Log(max / min, 2.0));

        }

 

        private int ScalePower(Rectangle rect, double power)

        {

            return ScalePosition(rect, Math.Max(0, power), MaxPower);

        }

 

        private int ScalePosition(Rectangle rect, double value, double max)

        {

            int pos = (int)(rect.Height * (value / max));

            if (pos > rect.Height)

                pos = rect.Height;

            if (pos < 0)

                pos = 0;

 

            pos = rect.Top + rect.Height - pos;

            return pos;

        }

 

        public double MaxPower { get { return Math.Log10(32768.0 * 32768.0 * fft.Size + 1); } }

    }

}