24 May '13 - 23:38 *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
 
   Home   Help Search Login Register  
Pages: [1]
  Reply  |  Print  
Author Topic: Beat detection with various bands  (Read 497 times)
wedtaur
Posts: 2


« on: 14 Jan '12 - 01:23 »
Reply with quoteQuote

Hi, i'm trying to make my personal beats detector for a music game. I need to intercept beat from drums like as from guitar, or voice. I tried with BASS_FX_BPM_BeatDecodeGet and BASS_FX_BPM_BeatSetParameters but it seems not to respect the same beat (peak variations) from spectrum I captured too.

This is my code for beats recognition, where BEAT_RELEASE_TIME is 2, frequency 44100 and nBands 8.


float interval = (player_->getFrequency() / 2) / nBands;

float bandwidth = interval / 2;

float currentCenterFreq = bandwidth;

double length = (double) player_->getLength() / (double) 1000;

for (unsigned int i = 0; i < nBands; i++) {

BASS_FX_BPM_BeatSetParameters(player_->getDecodeStream(), bandwidth, currentCenterFreq, BEAT_RELEASE_TIME);

BASS_FX_BPM_BeatDecodeGet(player_->getDecodeStream(), 0, length, BASS_FX_FREESOURCE, (BPMBEATPROC*) processBeat, &data);

BASS_FX_BPM_BeatCallbackReset(player_->getDecodeStream());

currentCenterFreq += interval;

}

This is the code for spectrum detect:


float* AudioPlayer::getSpectrum(unsigned int nBands) {

float* BASS_Spectrum = new float[BASS_BANDS];

BASS_ChannelGetData(BASS_Stream_, BASS_Spectrum, BASS_DATA_FFT_Const(BASS_BANDS));

for (unsigned int i = 0; i < BASS_BANDS; i++) {

BASS_Spectrum[i] = (float) sqrt(BASS_Spectrum[i]) * 5;

}

float* spectrum = new float[nBands];

unsigned int currentBand = 0;

unsigned int frequencesPerBand = BASS_BANDS / nBands;

// From BASS_BANDS to nBands.

for (unsigned int i = 0; i < BASS_BANDS; i++) {

if (i >= ((currentBand + 1) * frequencesPerBand)) {

float fraction = ((currentBand + 1) * frequencesPerBand) - (i - 1);
float oneMFraction = 1 - fraction;

spectrum[currentBand] += fraction * BASS_Spectrum[i];

currentBand++;

spectrum[currentBand] += oneMFraction * BASS_Spectrum[i];

} else {

spectrum[currentBand] += BASS_Spectrum[i];

}

}

for (unsigned int i = 0; i < nBands; i++) {

spectrum[i] /= frequencesPerBand;

if (spectrum[i] > 1) {

spectrum[i] = 1;

}

}

delete [] BASS_Spectrum;

return spectrum;

}

Thanks in advice! Smiley
Logged
gnag
Posts: 160


« Reply #1 on: 14 Jan '12 - 21:18 »
Reply with quoteQuote

Info: advice != advance

Please explain a bit more, what you are trying to do or what your problem is, I just see a bunch of code.
I dont advice you to use the Beat Detection from BassFX, it is based on SoundTouch which is a lot of code but doesnt impress much, has not much accuracy.
I spent more than 2 weeks (days&nights) to try different aproaches of beat detection because I wasnt satisfied by the BASS_FX_BPM_BeatDecodeGet which doesnt even take any parameters or settings to change.
After some weeks I was able to make a beat detection which is way better than SoundTouch/BassFX even its its also way simpler.

So the SetParameters doesnt work here, only works for the live beat detector whic is really not good, I asked Jobnik (BassFX Developer) once if he could implement functions to change parameters of BeatDecodeGet but he could not because he had some things in his Real Life which are more important than coding.
Logged
wedtaur
Posts: 2


« Reply #2 on: 14 Jan '12 - 23:57 »
Reply with quoteQuote

Ok, sorry for my english, in advance Cheesy

Let me explain: i need to detect some informations from music to build game dynamics, like generating enemies.

I want to capture beats from each of 8 bands, in wich is divided the entire spectrum. I need the spectrum with few bands too for some graphic effects.

For spectrum, I take it with getdata, apply square scale and apply the average function in the code I posted. The result seems to be acceptable.

For beats detection I thought to use the beatdetect function using frequency division like mycode, but you told me setparameters doesn't work with Decode stream.

Have I to capture manually beats directly from captured spectrum (for the whole song), with a peak finder algorithm? Have you some idea?

Thanks for your help and I appreciate english corrections too Smiley
Logged
gnag
Posts: 160


« Reply #3 on: 15 Jan '12 - 11:19 »
Reply with quoteQuote

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);
        }


Logged
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.18 | SMF © 2013, Simple Machines