You might find the following (improvised) Beat Detector useful which gives 150BPM on some Hardstyle Tracks I tested, 125BPM on "Last Friday Night".
It still gives out uncorrectly 150BPM on "E.T. by Katy Perry ft. Kayne West" which has some kind of strange double beats which are getting counted as 1 instead of 2, it should be detected as 76BPM.
Maybe in return for me working more than 2 weeks hardcore days&nights you can try to optimize the function to remove this failure. The following Code uses some Charts to make the BPM Results visible to see relations between the BPM Peaks etc, you dont need that code. You can get the needed RingBuffer Class
here.
Explanation: Collects 20ms of PCM Sample Data ("Sound Waves") , calculates RMS (Similar to Average), checks if the actual RMS is at least 1.3 times the Average of the RingBuffer (Has Space for 5 RMS Values, removes old one if new ones come in) and if yes then its a beat by high chance. Also my Code checks if the BPM is "reasonable" which meens between min and max BPM.
private float DoBPMAnalyseLoop3(int _playBackHandle, long len_stream, ScanResult sc_res)
{
//Test
chart_beat.Series[0].Points.Clear();
chart_tempo.Series[0].Points.Clear();
ChartArea chartArea1 = chart_beat.ChartAreas[0];
chartArea1.CursorX.IsUserEnabled = true;
chartArea1.CursorX.IsUserSelectionEnabled = true;
double nowpos_sec = 0;
//BPM Ranges (Filters strange BeatLenghts)
int max_bpm = 200;
int min_bpm = 60;
float duration_max_bpm = (float)60 / max_bpm;
float duration_min_bpm = (float)60 / min_bpm;
// Set ToolTips for Data Point Series
chart_beat.Series[0].ToolTip = "Second #VALX - AVG: #VALY";
List<BPM3_Beat> lst_beats = new List<BPM3_Beat>();
long bytesLeft = len_stream;
RingBuffer<float> rms_ringbuf = new RingBuffer<float>(5);
double oldpos=0;
while (bytesLeft > 0)
{
// Window in bytes to be filled with sample data
int length = (int)Bass.BASS_ChannelSeconds2Bytes(_playBackHandle, 0.02);
// first we need a mananged object where the sample data should be held
// only length/4 elements needed, since length is in byte and a float uses 4 bytes
long l4 = length / 4;
float[] sampleData = new float[l4];
// get the data
int ret = Bass.BASS_ChannelGetData(_playBackHandle, sampleData, length);
if (ret == -1) { MessageBox.Show("Error"); }
bytesLeft = bytesLeft - ret;
// the number of 32-bit floats received
// as less data might be returned by BASS_ChannelGetData as requested
l4 = ret / 4;
// RMS (Root Mean Square) = SQRT[(x1^2+x2^2+...)* (1/x_count) ]
float square_sums = new float();
for (int a = 0; a <l4; a++)
{
square_sums+= (float)Math.Pow(sampleData[a], 2);
}
float rms_now = (float)Math.Sqrt( (float)((square_sums / l4)));
//Fill Tempo Chart
nowpos_sec = Bass.BASS_ChannelBytes2Seconds(fileplay_handle, Bass.BASS_ChannelGetPosition(fileplay_handle));
//Draw Graph
chart_beat.Series[0].Points.AddXY(nowpos_sec, rms_now);
//Create Beat Object
BPM3_Beat beat = new BPM3_Beat();
beat.Distance = nowpos_sec - oldpos;
float rmsbf_avg = (rms_ringbuf.Count >= rms_ringbuf.Capacity) ? rms_ringbuf.Average() : 0;
if (rms_now >= (rmsbf_avg * 1.3) && beat.Distance >= duration_max_bpm && beat.Distance <= duration_min_bpm)
{
chart_beat.Series[0].Points.FindByValue(nowpos_sec, "X").Color = Color.DarkRed;
beat.TimeFound = nowpos_sec;
lst_beats.Add(beat);
oldpos = nowpos_sec;
}
if (beat.Distance >= duration_min_bpm )
{
oldpos = nowpos_sec;
}
rms_ringbuf.Add(rms_now);
if (nowpos_sec >= 60) { break; }
}
//Striplines clear
chart_beat.ChartAreas[0].AxisY.StripLines.Clear();
Application.DoEvents();
// Set ToolTips for Data Point Series
chart_beat.Series[1].ToolTip = "Sekunde #VALX";
// chart_beat.DataManipulator.FinancialFormula(FinancialFormula.MovingAverage, "5", "Low Freqs:Y", "Test:Y");
//Make Graph Points for BPM List
for (int i = min_bpm; i <= max_bpm; i++)
{
int count = lst_beats.Count(f => f.BPM == i);
chart_tempo.Series[0].Points.AddXY(i, count);
}
return (0);
}