Skip to content

Commit

Permalink
added tests for dynamic tempo estimation
Browse files Browse the repository at this point in the history
  • Loading branch information
bmcfee committed Dec 16, 2016
1 parent 479ae05 commit 4c197ed
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 1 deletion.
11 changes: 10 additions & 1 deletion librosa/beat.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ def estimate_tempo(onset_envelope, sr=22050, hop_length=512, start_bpm=120,

@cache(level=30)
def tempo(y=None, sr=22050, onset_envelope=None, hop_length=512, start_bpm=120,
std_bpm=1.0, ac_size=4.0, aggregate=np.mean):
std_bpm=1.0, ac_size=4.0, max_tempo=320.0, aggregate=np.mean):
"""Estimate the tempo (beats per minute)
Parameters
Expand All @@ -356,6 +356,9 @@ def tempo(y=None, sr=22050, onset_envelope=None, hop_length=512, start_bpm=120,
ac_size : float > 0 [scalar]
length (in seconds) of the auto-correlation window
max_tempo : float > 0 [scalar, optional]
If provided, only estimate tempo below this threshold
aggregate : callable [optional]
Aggregation function for estimating global tempo.
If `None`, then tempo is estimated independently for each frame.
Expand Down Expand Up @@ -422,6 +425,12 @@ def tempo(y=None, sr=22050, onset_envelope=None, hop_length=512, start_bpm=120,

# Weight the autocorrelation by a log-normal distribution
prior = np.exp(-0.5 * ((np.log2(bpms) - np.log2(start_bpm)) / std_bpm)**2)

# Kill everything above the max tempo
if max_tempo is not None:
max_idx = np.argmax(bpms < max_tempo)
prior[:max_idx] = 0

tg *= prior[:, np.newaxis]

# Really, instead of multiplying by the prior, we should set up a
Expand Down
28 changes: 28 additions & 0 deletions tests/test_beat.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,34 @@ def __test(infile):
yield (__test, infile)


def test_tempo():

def __test(tempo, sr, hop_length, ac_size, aggregate, y):

tempo_est = librosa.beat.tempo(y=y, sr=sr, hop_length=hop_length,
ac_size=ac_size,
aggregate=aggregate)

# Being within 10% for the stable frames is close enough
if aggregate is None:
win_size = int(ac_size * sr // hop_length)
assert np.all(np.abs(tempo_est[win_size:-win_size] - tempo) <= 0.10 * tempo), (tempo,
tempo_est[win_size:-win_size])
else:
assert np.abs(tempo_est - tempo) <= 0.10 * tempo, (tempo, tempo_est)

for sr in [22050, 44100]:
for tempo in [40, 60, 80, 110, 150, 160]:
# Make a pulse train at the target tempo
y = np.zeros(20 * sr)
delay = np.asscalar(librosa.time_to_samples(60./tempo, sr=sr))
y[::delay] = 1
for hop_length in [512, 1024]:
for ac_size in [4, 8]:
for aggregate in [None, np.mean]:
yield __test, tempo, sr, hop_length, ac_size, aggregate, y


@raises(librosa.ParameterError)
def test_beat_no_input():

Expand Down

0 comments on commit 4c197ed

Please sign in to comment.