HamBook – Rev 59

Subversion Repositories:
Rev:
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.