@@ -323,6 +323,51 @@ def recursive(position):
323323 return np .array (positions )
324324
325325
326+ def detect_beats_with_ccf (activations , interval , look_aside = 0.2 ):
327+ """
328+ Detects the beats in the given activation function using a
329+ Cross-Correlated Pulse Train signal.
330+
331+ Parameters
332+ ----------
333+ activations : numpy array
334+ Beat activations.
335+ interval : int
336+ Look for the next beat each `interval` frames.
337+ look_aside : float
338+ Look this fraction of the `interval` to each side to detect the beats.
339+ Determines the width of each pulse.
340+
341+ Returns
342+ -------
343+ numpy array
344+ Beat positions [frames].
345+
346+ """
347+ # always look at least 1 frame to each side
348+ pulse_width = max (1 , int (interval * look_aside )) * 2
349+ # build pulse train with length of activations
350+ sig = (np .arange (len (activations )) % interval < pulse_width )
351+ # compute possible taus
352+ taus = range (interval ) + interval
353+ # cross correlate pulse train with activations
354+ bins = []
355+ for tau in taus :
356+ bins .append (np .sum (np .abs (activations [tau :] * sig [0 :- tau ])))
357+ # select best fitting offset for the pulse train
358+ offset = np .array (taus )[np .array (bins ).argmax (axis = 0 )]
359+ # correct starting position
360+ offset -= interval
361+ # predict beats over all activations by selecting the highest
362+ # activation within each pulse
363+ positions = []
364+ for beat in range (offset , len (activations ), interval ):
365+ act_max = activations [beat :beat + pulse_width ].argmax (axis = 0 )
366+ positions .append (beat + act_max )
367+ # return beat positions
368+ return np .array (positions )
369+
370+
326371# classes for detecting/tracking of beat inside a beat activation function
327372class BeatTrackingProcessor (OnlineProcessor ):
328373 """
0 commit comments