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