HamBook – Rev 59
?pathlinks?
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
using HamBook.Utilities;
using NAudio.Wave;
using Spectrogram;
namespace HamBook
{
public partial class SpectrogramForm : Form
{
private readonly CancellationToken _renderCancellationToken;
private readonly CancellationTokenSource _renderCancellationTokenSource;
private readonly SpectrogramGenerator _spectrogramGenerator;
private readonly ScheduledContinuation _windowZOrderScheduledContinuation;
private CancellationToken _cancellationToken;
private Point _formLocation;
private volatile bool _mouseDown;
private WaveIn _waveIn;
public SpectrogramForm()
{
InitializeComponent();
_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.Visualizations.Spectrogram.SampleRate,
Configuration.Visualizations.Spectrogram.FftSamples,
Configuration.Visualizations.Spectrogram.FftSamples /
Configuration.Visualizations.Spectrogram.AudioBufferTimespan);
_spectrogramGenerator.Colormap = Colormap.Viridis;
pictureBox2.Image = _spectrogramGenerator.GetVerticalScale(pictureBox2.Width);
}
private Configuration.Configuration Configuration { get; }
/// <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)
{
Utilities.WindowState.FormTracker.Track(this);
pinToDesktopToolStripMenuItem.Checked = Configuration.Visualizations.Spectrogram.PinToDesktop;
if (Configuration.Visualizations.Spectrogram.PinToDesktop)
_windowZOrderScheduledContinuation.Schedule(TimeSpan.FromSeconds(1),
() =>
{
this.InvokeIfRequired(form =>
{
SetWindowPos(Handle, HwndBottom, 0, 0, Location.X, Location.Y,
SwpNomove | SwpNosize | SwpNoactivate);
});
}, _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.Visualizations.Spectrogram.AudioBufferTimespan;
_waveIn.DataAvailable += _waveIn_DataAvailable;
_waveIn.StartRecording();
break;
}
}
private void _waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
var bytesPerSample = _waveIn.WaveFormat.BitsPerSample / 8;
var newSampleCount = e.BytesRecorded / bytesPerSample;
var buffer = new double[newSampleCount];
double peak = 0;
for (var 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,
PixelFormat.Format32bppPArgb);
using (var bmpSpecIndexed =
_spectrogramGenerator.GetBitmap(Configuration.Visualizations.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.Visualizations.Spectrogram.PinToDesktop)
_windowZOrderScheduledContinuation.Schedule(TimeSpan.FromSeconds(1),
() =>
{
this.InvokeIfRequired(form =>
{
SetWindowPos(Handle, HwndTop, 0, 0, Location.X, Location.Y,
SwpNomove | SwpNosize | SwpNoactivate);
});
}, _renderCancellationToken);
}
private void SpectrogramForm_MouseLeave(object sender, EventArgs e)
{
if (Configuration.Visualizations.Spectrogram.PinToDesktop)
_windowZOrderScheduledContinuation.Schedule(TimeSpan.FromSeconds(1),
() =>
{
this.InvokeIfRequired(form =>
{
SetWindowPos(form.Handle, HwndBottom, 0, 0, Location.X, Location.Y,
SwpNomove | SwpNosize | SwpNoactivate);
});
}, _renderCancellationToken);
}
private void pinToDesktopToolStripMenuItem_CheckStateChanged(object sender, EventArgs e)
{
var toolStripMenuItem = (ToolStripMenuItem)sender;
switch (toolStripMenuItem.CheckState)
{
case CheckState.Checked:
Configuration.Visualizations.Spectrogram.PinToDesktop = true;
break;
case CheckState.Unchecked:
Configuration.Visualizations.Spectrogram.PinToDesktop = false;
break;
}
}
#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 HwndBottom = new IntPtr(1);
private static readonly IntPtr HwndTop = new IntPtr(0);
private const uint SwpNosize = 0x0001;
private const uint SwpNomove = 0x0002;
private const uint SwpNoactivate = 0x0010;
#endregion
}
}
Generated by GNU Enscript 1.6.5.90.