HamBook – Rev 53
?pathlinks?
using Configuration;
using FftSharp;
using HamBook.Utilities;
using NAudio.Utils;
using NAudio.Wave;
using Org.BouncyCastle.Math.EC.Multiplier;
using Spectrogram;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
namespace HamBook
{
public partial class SpectrogramForm : Form
{
#region Natives
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
private static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
private static readonly IntPtr HWND_TOP = new IntPtr(0);
private const UInt32 SWP_NOSIZE = 0x0001;
private const UInt32 SWP_NOMOVE = 0x0002;
private const UInt32 SWP_NOACTIVATE = 0x0010;
#endregion
private Configuration.Configuration Configuration { get; set; }
private CancellationToken _cancellationToken;
SpectrogramGenerator _spectrogramGenerator;
private WaveIn _waveIn;
private CancellationTokenSource _renderCancellationTokenSource;
private CancellationToken _renderCancellationToken;
private ScheduledContinuation _windowZOrderScheduledContinuation;
private volatile bool _mouseDown;
private Point _formLocation;
public SpectrogramForm()
{
InitializeComponent();
Utilities.WindowState.FormTracker.Track(this);
_renderCancellationTokenSource = new CancellationTokenSource();
_renderCancellationToken = _renderCancellationTokenSource.Token;
_windowZOrderScheduledContinuation = new ScheduledContinuation();
}
public SpectrogramForm(Configuration.Configuration configuration, CancellationToken cancellationToken) : this()
{
Configuration = configuration;
_cancellationToken = cancellationToken;
_spectrogramGenerator = new SpectrogramGenerator(Configuration.Visualisations.Spectrogram.SampleRate, fftSize: Configuration.Visualisations.Spectrogram.FFTSamples, stepSize: Configuration.Visualisations.Spectrogram.FFTSamples / Configuration.Visualisations.Spectrogram.AudioBufferTimespan);
_spectrogramGenerator.Colormap = Colormap.Viridis;
pictureBox2.Image = _spectrogramGenerator.GetVerticalScale(pictureBox2.Width);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
if(_waveIn != null)
{
if (_renderCancellationTokenSource != null)
{
_renderCancellationTokenSource.Cancel();
}
_waveIn.DataAvailable -= _waveIn_DataAvailable;
_waveIn.StopRecording();
_waveIn.Dispose();
_waveIn = null;
}
components.Dispose();
}
base.Dispose(disposing);
}
private void SpectrogramForm_Load(object sender, EventArgs e)
{
pinToDesktopToolStripMenuItem.Checked = Configuration.Visualisations.Spectrogram.PinToDesktop;
if (Configuration.Visualisations.Spectrogram.PinToDesktop)
{
_windowZOrderScheduledContinuation.Schedule(TimeSpan.FromSeconds(1), () =>
{
this.InvokeIfRequired(form =>
{
SetWindowPos(Handle, HWND_BOTTOM, 0, 0, Location.X, Location.Y, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
});
}, _renderCancellationToken);
}
foreach (var deviceNumber in Enumerable.Range(0, WaveIn.DeviceCount))
{
var capabilities = WaveIn.GetCapabilities(deviceNumber);
if(!string.Equals(capabilities.ProductName, Configuration.Audio.InputDeviceFriendlyName))
{
continue;
}
_waveIn = new WaveIn();
_waveIn.DeviceNumber = deviceNumber;
_waveIn.BufferMilliseconds = Configuration.Visualisations.Spectrogram.AudioBufferTimespan;
_waveIn.DataAvailable += _waveIn_DataAvailable;
_waveIn.StartRecording();
break;
}
}
private void _waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
int bytesPerSample = _waveIn.WaveFormat.BitsPerSample / 8;
int newSampleCount = e.BytesRecorded / bytesPerSample;
double[] buffer = new double[newSampleCount];
double peak = 0;
for (int i = 0; i < newSampleCount; i++)
{
buffer[i] = BitConverter.ToInt16(e.Buffer, i * bytesPerSample);
peak = Math.Max(peak, buffer[i]);
}
var AmplitudeFrac = peak / (1 << 15);
_spectrogramGenerator.Add(buffer, false);
}
private void timer1_Tick(object sender, EventArgs e)
{
if(_spectrogramGenerator.FftsToProcess == 0)
{
return;
}
_spectrogramGenerator.Process();
_spectrogramGenerator.SetFixedWidth(pictureBox1.Width);
var bmpSpec = new Bitmap(_spectrogramGenerator.Width, _spectrogramGenerator.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (var bmpSpecIndexed = _spectrogramGenerator.GetBitmap(Configuration.Visualisations.Spectrogram.SpectrumIntensity))
using (var gfx = Graphics.FromImage(bmpSpec))
using (var pen = new Pen(Color.White))
{
gfx.DrawImage(bmpSpecIndexed, 0, 0);
if (false)
{
//gfx.DrawLine(pen, spec.NextColumnIndex, 0, spec.NextColumnIndex, pbSpectrogram.Height);
}
}
pictureBox1.Image?.Dispose();
pictureBox1.Image = bmpSpec;
}
private void SpectrogramForm_MouseClick(object sender, MouseEventArgs e)
{
switch(e.Button)
{
case MouseButtons.Right:
var control = (Control)sender;
var screenPoint = control.PointToScreen(e.Location);
contextMenuStrip1.Show(screenPoint);
break;
case MouseButtons.Left:
break;
}
}
private void closeToolStripMenuItem_Click(object sender, EventArgs e)
{
Close();
}
private void SpectrogramForm_MouseDown(object sender, MouseEventArgs e)
{
if(e.Button != MouseButtons.Left)
{
return;
}
if(e.Clicks != 1)
{
return;
}
_mouseDown = true;
_formLocation = new Point(e.X, e.Y);
}
private void SpectrogramForm_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
{
return;
}
if (!_mouseDown)
{
return;
}
var control = (Control)sender;
var screenPoint = control.PointToScreen(e.Location);
Location = new Point(screenPoint.X - _formLocation.X, screenPoint.Y - _formLocation.Y);
}
private void SpectrogramForm_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
{
return;
}
_mouseDown = false;
}
private void SpectrogramForm_Activated(object sender, EventArgs e)
{
// Make form bottom most.
//SetWindowPos(Handle, HWND_BOTTOM, 0, 0, Location.X, Location.Y, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
private void SpectrogramForm_MouseEnter(object sender, EventArgs e)
{
if (Configuration.Visualisations.Spectrogram.PinToDesktop)
{
_windowZOrderScheduledContinuation.Schedule(TimeSpan.FromSeconds(1), () =>
{
this.InvokeIfRequired(form =>
{
SetWindowPos(Handle, HWND_TOP, 0, 0, Location.X, Location.Y, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
});
}, _renderCancellationToken);
}
}
private void SpectrogramForm_MouseLeave(object sender, EventArgs e)
{
if (Configuration.Visualisations.Spectrogram.PinToDesktop)
{
_windowZOrderScheduledContinuation.Schedule(TimeSpan.FromSeconds(1), () =>
{
this.InvokeIfRequired(form =>
{
SetWindowPos(form.Handle, HWND_BOTTOM, 0, 0, Location.X, Location.Y, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
});
}, _renderCancellationToken);
}
}
private void pinToDesktopToolStripMenuItem_CheckStateChanged(object sender, EventArgs e)
{
var toolStripMenuItem = (ToolStripMenuItem)sender;
switch(toolStripMenuItem.CheckState)
{
case CheckState.Checked:
Configuration.Visualisations.Spectrogram.PinToDesktop = true;
break;
case CheckState.Unchecked:
Configuration.Visualisations.Spectrogram.PinToDesktop = false;
break;
}
}
}
}