From e78b3222f395bc22bb89e1b61bb74ed7415f3938 Mon Sep 17 00:00:00 2001 From: Luis Scholl Date: Thu, 25 Sep 2025 21:29:26 +0200 Subject: [PATCH 1/3] Add beat callback parameter for streaming mode --- src/BeatNet/BeatNet.py | 4 ++-- src/BeatNet/particle_filtering_cascade.py | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/BeatNet/BeatNet.py b/src/BeatNet/BeatNet.py index a449fb9..038a7a9 100644 --- a/src/BeatNet/BeatNet.py +++ b/src/BeatNet/BeatNet.py @@ -48,7 +48,7 @@ class BeatNet: ''' - def __init__(self, model, mode='online', inference_model='PF', plot=[], thread=False, device='cpu'): + def __init__(self, model, mode='online', inference_model='PF', plot=[], thread=False, device='cpu', beat_callback=lambda is_downbeat: print("*beat!" if is_downbeat else "beat!")): self.model = model self.mode = mode self.inference_model = inference_model @@ -64,7 +64,7 @@ def __init__(self, model, mode='online', inference_model='PF', plot=[], thread=F self.proc = LOG_SPECT(sample_rate=self.log_spec_sample_rate, win_length=self.log_spec_win_length, hop_size=self.log_spec_hop_length, n_bands=[24], mode = self.mode) if self.inference_model == "PF": # instantiating a Particle Filter decoder - Is Chosen for online inference - self.estimator = particle_filter_cascade(beats_per_bar=[], fps=50, plot=self.plot, mode=self.mode) + self.estimator = particle_filter_cascade(beats_per_bar=[], fps=50, plot=self.plot, mode=self.mode, beat_callback=beat_callback) elif self.inference_model == "DBN": # instantiating an HMM decoder - Is chosen for offline inference self.estimator = DBNDownBeatTrackingProcessor(beats_per_bar=[2, 3, 4], fps=50) else: diff --git a/src/BeatNet/particle_filtering_cascade.py b/src/BeatNet/particle_filtering_cascade.py index e026100..f4504c6 100644 --- a/src/BeatNet/particle_filtering_cascade.py +++ b/src/BeatNet/particle_filtering_cascade.py @@ -125,7 +125,8 @@ def __init__(self, beats_per_bar=[], particle_size=PARTICLE_SIZE, down_particle_ min_bpm=MIN_BPM, max_bpm=MAX_BPM, num_tempi=NUM_TEMPI, min_beats_per_bar=MIN_BEAT_PER_BAR, max_beats_per_bar=MAX_BEAT_PER_BAR, offset=OFFSET, ig_threshold=IG_THRESHOLD, lambda_b=LAMBDA_B, lambda_d=LAMBDA_D, observation_lambda_b=OBSERVATION_LAMBDA_B, observation_lambda_d=OBSERVATION_LAMBDA_D, - fps=None, plot=False, mode=None, **kwargs): + fps=None, plot=False, mode=None, beat_callback=lambda is_downbeat: print("*beat!" if is_downbeat else "beat!"), + **kwargs): self.particle_size = particle_size self.down_particle_size = down_particle_size self.particle_filter = [] @@ -141,6 +142,7 @@ def __init__(self, beats_per_bar=[], particle_size=PARTICLE_SIZE, down_particle_ self.offset = offset self.ig_threshold = ig_threshold self.mode = mode + self.beat_callback = beat_callback # convert timing information to construct a beat state space min_interval = 60. * fps / max_bpm max_interval = 60. * fps / min_bpm @@ -284,11 +286,11 @@ def process(self, activations): if self.down_max in self.st2.first_states[0] and self.path[-1][1] !=1 and both_activations[i][1]>0.4: self.path = np.append(self.path, [[self.offset + self.counter * self.T, 1]], axis=0) if self.mode == 'stream' or self.mode == 'realtime': - print("*beat!") + self.beat_callback(True) elif (activations[i]>0.4) : self.path = np.append(self.path, [[self.offset + self.counter * self.T, 2]], axis=0) if self.mode == 'stream' or self.mode == 'realtime': - print("beat!") + self.beat_callback(False) #librosa.clicks(times=None, frames=None, sr=22050, hop_length=512, click_freq=440.0, click_duration=0.1, click=None, length=None) if 'downbeat_particles' in self.plot: self.downbeat_particles_plot() From 8faa532d0f53672d119cad38c4525b92b3dab4b6 Mon Sep 17 00:00:00 2001 From: Luis Scholl Date: Thu, 25 Sep 2025 21:36:06 +0200 Subject: [PATCH 2/3] lift depency versions; unpin numba --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index c0b244e..61cd93b 100644 --- a/setup.py +++ b/setup.py @@ -20,8 +20,8 @@ REQUIRED_PACKAGES = [ 'numpy', 'cython', - 'librosa>=0.8.0', - 'numba==0.54.1', # Manually specified here as librosa incorrectly states that it is compatible with the latest version of numba although 0.50.0 is not compatible. + 'librosa>=0.10.1', + 'numba>=0.58.1', # Manually specified here as librosa incorrectly states that it is compatible with the latest version of numba although 0.50.0 is not compatible. 'scipy', 'mido>=1.2.6', 'pytest', From 2f6af2fa295ace7ffa4027b340e8764256a08dc3 Mon Sep 17 00:00:00 2001 From: Luis Scholl Date: Thu, 25 Sep 2025 21:45:51 +0200 Subject: [PATCH 3/3] Document usage of beat_callback in README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b64ba5c..74187f0 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,8 @@ thread: To decide whether accomplish the inference at the main thread or another device: Type of device being used. Cuda or cpu (by default). +beat_callback: A function, which is called when a beat is detected. It is called with True for a downbeat and with False for a normal beat. Only available in streaming mode. Defaults to printing _*beat!_ for a downbeat and _beat!_ for a non-downbeat. + Installation command: ---------------------