1
0
mirror of https://github.com/DarthAffe/CUE.NET.git synced 2025-12-13 00:58:31 +00:00

177 lines
7.6 KiB
C#

/*
* ###################################################################################################################
* # Thanks to AterialDawn #
* # #
* # Source: https://github.com/AterialDawn/CUEAudioVisualizer/blob/master/CUEAudioVisualizer/SoundDataProcessor.cs #
* # Date: 23.10.2015 #
* # Commit: 9106ab777d70a23254872455c6f3cefbe9bef6ca #
* # #
* ###################################################################################################################
*
* > Note < This code is refactored and all parts not used in this tutorial are removed.
*/
using System;
using Un4seen.Bass;
using Un4seen.BassWasapi;
namespace Example_AudioAnalyzer_full.TakeAsIs
{
public class SoundDataProcessor
{
private const int BAR_COUNT = 1000;
public float[] BarValues = new float[BAR_COUNT];
public float VolumeScalar = 1f;
public float SongBeat = 0;
private float _averagedVolume = 0;
private int _wasapiDeviceIndex = -1;
private BASSData _maxFft = (BASSData.BASS_DATA_FFT8192);
private float[] _fftData = new float[4096];
private float[] _freqVolScalar = new float[BAR_COUNT];
private float[] _barValues = new float[BAR_COUNT];
private int _sampleFrequency = 48000;
private int _maximumFrequency = 16384;
private int _minimumFrequency = 32;
private int _maximumFrequencyIndex;
private int _minimumFrequencyIndex;
private int _deviceNumber;
private int[] _logIndex = new int[BAR_COUNT];
private bool _deviceInitialized = false;
private WASAPIPROC _wasapiProc;
public SoundDataProcessor(int deviceIndex)
{
this._wasapiDeviceIndex = deviceIndex;
BuildLookupTables();
_wasapiProc = IgnoreDataProc;
}
public void Process()
{
if (_deviceNumber != _wasapiDeviceIndex || !_deviceInitialized)
{
UpdateDevice();
_deviceNumber = BassWasapi.BASS_WASAPI_GetDevice();
}
if (!_deviceInitialized) return;
GetBarData();
float curVol = BassWasapi.BASS_WASAPI_GetDeviceLevel(_deviceNumber, -1) * VolumeScalar;
_averagedVolume = Utility.Clamp(Utility.LinearInterpolate(_averagedVolume, curVol, 0.02f), 0f, 1f);
}
private void UpdateDevice()
{
if (_wasapiDeviceIndex == -1) return;
if (_deviceInitialized)
{
Console.WriteLine("Deinitializing WASAPI device");
BassWasapi.BASS_WASAPI_Stop(true);
BassWasapi.BASS_WASAPI_Free();
_deviceInitialized = false;
}
BASS_WASAPI_DEVICEINFO devInfo = BassWasapi.BASS_WASAPI_GetDeviceInfo(_wasapiDeviceIndex);
if (devInfo == null)
throw new WASAPIInitializationException("Device " + _wasapiDeviceIndex + " is invalid!");
if (!BassWasapi.BASS_WASAPI_Init(_wasapiDeviceIndex, devInfo.mixfreq, 2, BASSWASAPIInit.BASS_WASAPI_AUTOFORMAT | BASSWASAPIInit.BASS_WASAPI_BUFFER, 0f, 0f, _wasapiProc, IntPtr.Zero))
{
BASSError error = Bass.BASS_ErrorGetCode();
throw new WASAPIInitializationException("Unable to initialize WASAPI device " + _wasapiDeviceIndex, error);
}
if (!BassWasapi.BASS_WASAPI_Start())
{
BASSError error = Bass.BASS_ErrorGetCode();
throw new WASAPIInitializationException("Unable to start WASAPI!", error);
}
Console.WriteLine("WASAPI device initialized");
_deviceNumber = _wasapiDeviceIndex;
_sampleFrequency = devInfo.mixfreq;
BuildLookupTables();
_deviceInitialized = true;
}
private void BuildLookupTables()
{
_maximumFrequencyIndex = Math.Min(Utils.FFTFrequency2Index(_maximumFrequency, 8192, _sampleFrequency) + 1, 4095);
_minimumFrequencyIndex = Math.Min(Utils.FFTFrequency2Index(_minimumFrequency, 8192, _sampleFrequency), 4095);
_freqVolScalar[0] = 1f;
for (int i = 1; i < BAR_COUNT; i++)
{
_logIndex[i] = (int)((Math.Log(BAR_COUNT, BAR_COUNT) - Math.Log(BAR_COUNT - i, BAR_COUNT)) * (_maximumFrequencyIndex - _minimumFrequencyIndex) + _minimumFrequencyIndex);
_freqVolScalar[i] = 1 + (float)Math.Sqrt((double)i / (double)BAR_COUNT) * 1.25f;
}
}
private void GetBarData()
{
//Get FFT data
BassWasapi.BASS_WASAPI_GetData(_fftData, (int)_maxFft);
int barIndex = 0;
//Calculate bar values by squaring fftData from log(x) fft bin, and multiply by a few magic values to end up with a somewhat reasonable graphical representation of the sound
for (barIndex = 0; barIndex < BAR_COUNT; barIndex++)
{
_barValues[barIndex] = ((float)Math.Sqrt(_fftData[_logIndex[barIndex]]) * 15f * VolumeScalar) * _freqVolScalar[barIndex];
}
barIndex = 0;
//This chunk of code is supposed to do a rolling average to smooth out the lower values to look cleaner for another visualizer i'm working on
float preScaled;
float preScaled1 = _barValues[barIndex];
preScaled1 += _barValues[barIndex + 1];
preScaled1 /= 2f;
BarValues[barIndex] = Utility.Clamp(preScaled1, 0f, 1f);
barIndex++;
preScaled1 = _barValues[barIndex - 1] * 0.75f;
preScaled1 += _barValues[barIndex];
preScaled1 += _barValues[barIndex + 1] * 0.75f;
preScaled1 /= 2.5f;
BarValues[barIndex] = Utility.Clamp(preScaled1, 0f, 1f);
for (barIndex = 2; barIndex < 50; barIndex++)
{
preScaled = _barValues[barIndex - 2] * 0.5f;
preScaled += _barValues[barIndex - 1] * 0.75f;
preScaled += _barValues[barIndex];
preScaled += _barValues[barIndex + 1] * 0.75f;
preScaled += _barValues[barIndex + 2] * 0.5f;
preScaled /= 3.5f;
BarValues[barIndex] = Utility.Clamp(preScaled, 0f, 1f);
}
for (barIndex = 50; barIndex < 999; barIndex++)
{
preScaled = _barValues[barIndex - 1] * 0.75f;
preScaled += _barValues[barIndex];
preScaled += _barValues[barIndex + 1] * 0.75f;
preScaled /= 2.5f;
BarValues[barIndex] = Utility.Clamp(preScaled, 0f, 1f);
}
preScaled = _barValues[barIndex - 1];
preScaled += _barValues[barIndex];
preScaled /= 2f;
BarValues[barIndex] = Utility.Clamp(preScaled, 0f, 1f);
//Calculate the song beat
float sum = 0f;
for (int i = 2; i < 28; i++)
sum += (float)Math.Sqrt(_barValues[i]); //Prettier scaling > Accurate scaling
SongBeat = (sum / 25f);
}
private static int IgnoreDataProc(IntPtr buffer, int length, IntPtr user)
{
return 1;
}
}
}