MCAudioSpectrumAnalyzerVS/MCAudioSpectrumAnalyzer/Analyzer.cs

179 lines
6.3 KiB
C#

using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Windows;
using System.Windows.Forms;
using Un4seen.Bass;
using Un4seen.BassWasapi;
namespace MCAudioSpectrumAnalyzer
{
internal class Analyzer
{
private bool _enable; //enabled status
private Timer _t; //timer that refreshes the display
private float[] _fft; //buffer for fft data
private ProgressBar _l, _r; //progressbars for left and right channel intensity
private WASAPIPROC _process; //callback function to obtain data
private int _lastlevel; //last output level
private int _hanctr; //last output level counter
private List<byte> _spectrumdata; //spectrum data buffer
//private Spectrum _spectrum; //spectrum dispay control
private ComboBox _devicelist; //device list
private bool _initialized; //initialized flag
private int devindex; //used device index
private int _lines = 16; // number of spectrum lines
//ctor
public Analyzer(ProgressBar left, ProgressBar right, ComboBox devicelist)
{
_fft = new float[1024];
_lastlevel = 0;
_hanctr = 0;
_t = new Timer();
_t.Tick += _t_Tick;
_t.Interval = 25; //40hz refresh rate
//_t.Interval = 50; //20 Hz
//_t.Interval = 400;
_t.Enabled = false;
_l = left;
_r = right;
_l.Minimum = 0;
_r.Minimum = 0;
_r.Maximum = ushort.MaxValue;
_l.Maximum = ushort.MaxValue;
_process = new WASAPIPROC(Process);
_spectrumdata = new List<byte>();
_devicelist = devicelist;
_initialized = false;
Init();
}
// Serial port for arduino output
public SerialPort Serial { get; set; }
// flag for display enable
public bool DisplayEnable { get; set; }
//flag for enabling and disabling program functionality
public bool Enable
{
get { return _enable; }
set
{
_enable = value;
if (value)
{
if (!_initialized)
{
var array = (_devicelist.Items[_devicelist.SelectedIndex] as string).Split(' ');
devindex = Convert.ToInt32(array[0]);
bool result = BassWasapi.BASS_WASAPI_Init(devindex, 0, 0, BASSWASAPIInit.BASS_WASAPI_BUFFER, 1f, 0.05f, _process, IntPtr.Zero);
if (!result)
{
var error = Bass.BASS_ErrorGetCode();
MessageBox.Show(error.ToString());
}
else
{
_initialized = true;
_devicelist.Enabled = false;
}
}
BassWasapi.BASS_WASAPI_Start();
}
else BassWasapi.BASS_WASAPI_Stop(true);
System.Threading.Thread.Sleep(500);
_t.Enabled = value;
}
}
// initialization
private void Init()
{
bool result = false;
for (int i = 0; i < BassWasapi.BASS_WASAPI_GetDeviceCount(); i++)
{
var device = BassWasapi.BASS_WASAPI_GetDeviceInfo(i);
if (device.IsEnabled && device.IsLoopback)
{
_devicelist.Items.Add(string.Format("{0} - {1}", i, device.name));
}
}
_devicelist.SelectedIndex = 0;
Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_UPDATETHREADS, false);
result = Bass.BASS_Init(0, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);
if (!result) throw new Exception("Init Error");
}
//timer
private void _t_Tick(object sender, EventArgs e)
{
int ret = BassWasapi.BASS_WASAPI_GetData(_fft, (int)BASSData.BASS_DATA_FFT2048); //get channel fft data
if (ret < -1) return;
int x, y;
int b0 = 0;
//computes the spectrum data, the code is taken from a bass_wasapi sample.
for (x=0; x<_lines; x++)
{
float peak = 0;
int b1 = (int)Math.Pow(2, x * 10.0 / (_lines - 1));
if (b1 > 1023) b1 = 1023;
if (b1 <= b0) b1 = b0 + 1;
for (;b0<b1;b0++)
{
if (peak < _fft[1 + b0]) peak = _fft[1 + b0];
}
y = (int)(Math.Sqrt(peak) * 3 * 255 - 4);
if (y > 255) y = 255;
if (y < 0) y = 0;
_spectrumdata.Add((byte)y);
//Console.Write("{0, 3} ", y);
}
Form1.Instance.Set(_spectrumdata);
if (Serial != null)
{
Serial.Write(_spectrumdata.ToArray(), 0, _spectrumdata.Count);
}
_spectrumdata.Clear();
int level = BassWasapi.BASS_WASAPI_GetLevel();
_l.Value = Utils.LowWord32(level);
_r.Value = Utils.HighWord32(level);
if (level == _lastlevel && level != 0) _hanctr++;
_lastlevel = level;
//Required, because some programs hang the output. If the output hangs for a 75ms
//this piece of code re initializes the output so it doesn't make a gliched sound for long.
if (_hanctr > 3)
{
_hanctr = 0;
_l.Value = 0;
_r.Value = 0;
Free();
Bass.BASS_Init(0, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);
_initialized = false;
Enable = true;
}
}
// WASAPI callback, required for continuous recording
private int Process(IntPtr buffer, int length, IntPtr user)
{
return length;
}
//cleanup
public void Free()
{
BassWasapi.BASS_WASAPI_Free();
Bass.BASS_Free();
}
}
}