Spaces:
Runtime error
Runtime error
import librosa | |
from librosa import display | |
from librosa import feature | |
import numpy as np | |
from matplotlib import pyplot as plt | |
import scipy | |
import soundfile as sf | |
from numpy import typing as npt | |
import typing | |
def onsets_detection(y: npt.ArrayLike, sr: int, shift_array: npt.ArrayLike) -> tuple : | |
""" | |
計算音檔的onset frames | |
""" | |
o_env = librosa.onset.onset_strength(y=y, sr=sr) | |
times = librosa.times_like(o_env, sr=sr) | |
onset_frames = librosa.onset.onset_detect(onset_envelope=o_env, sr=sr) | |
D = np.abs(librosa.stft(y)) | |
fig, ax = plt.subplots() | |
librosa.display.specshow(librosa.amplitude_to_db(D, ref=np.max), | |
x_axis='time', y_axis='log', ax=ax, sr=sr) | |
ax.set_xticks(shift_array - shift_array[0], | |
shift_array) | |
ax.set_xlabel('Time (s)') | |
ax.autoscale() | |
ax.set(title='Power spectrogram') | |
return fig, ax, (o_env, times, onset_frames) | |
def onset_click_plot(o_env, times, onset_frames, y_len, sr, shift_time) -> tuple: | |
""" | |
重新繪製onset frames | |
""" | |
fig, ax = plt.subplots() | |
ax.plot(times + shift_time, o_env, label='Onset strength') | |
ax.vlines(times[onset_frames] + shift_time, 0, o_env.max(), color='r', alpha=0.9, | |
linestyles='--', label='Onsets') | |
ax.autoscale() | |
ax.legend() | |
ax.set_xlabel('Time (s)') | |
ax.set_ylabel('Strength') | |
y_onset_clicks = librosa.clicks(frames=onset_frames, sr=sr, length=y_len) | |
return fig, ax, y_onset_clicks | |
def plot_onset_strength(y: npt.ArrayLike, sr:int, standard: bool = True, custom_mel: bool = False, cqt: bool = False, shift_array: npt.ArrayLike = None) -> tuple: | |
D = np.abs(librosa.stft(y)) | |
times = librosa.times_like(D, sr) | |
fig, ax = plt.subplots(nrows=2, sharex=True) | |
librosa.display.specshow(librosa.amplitude_to_db(D, ref=np.max), | |
y_axis='log', x_axis='time', ax=ax[0], sr=sr) | |
ax[0].set(title='Power spectrogram') | |
ax[0].label_outer() | |
# Standard Onset Fuction | |
if standard : | |
onset_env_standard = librosa.onset.onset_strength(y=y, sr=sr) | |
ax[1].plot(times, 2 + onset_env_standard / onset_env_standard.max(), alpha=0.8, label='Mean (mel)') | |
if custom_mel : | |
onset_env_mel = librosa.onset.onset_strength(y=y, sr=sr, | |
aggregate=np.median, | |
fmax=8000, n_mels=256) | |
ax[1].plot(times, 1 + onset_env_mel / onset_env_mel.max(), alpha=0.8, label='Median (custom mel)') | |
if cqt : | |
C = np.abs(librosa.cqt(y=y, sr=sr)) | |
onset_env_cqt = librosa.onset.onset_strength(sr=sr, S=librosa.amplitude_to_db(C, ref=np.max)) | |
ax[1].plot(times, onset_env_cqt / onset_env_cqt.max(), alpha=0.8, label='Mean (CQT)') | |
ax[1].legend() | |
ax[1].set(ylabel='Normalized strength', yticks=[]) | |
ax[1].set_xticks(shift_array - shift_array[0], | |
shift_array) | |
ax[1].autoscale() | |
ax[1].set_xlabel('Time (s)') | |
return fig, ax | |
def beat_analysis(y: npt.ArrayLike, sr:int, spec_type: str = 'mel', spec_hop_length: int = 512, shift_array: npt.ArrayLike = None) : | |
fig, ax = plt.subplots() | |
onset_env = librosa.onset.onset_strength(y=y, sr=sr, aggregate=np.median) | |
tempo, beats = librosa.beat.beat_track(onset_envelope=onset_env, sr=sr) | |
times = librosa.times_like(onset_env, sr=sr, hop_length=spec_hop_length) | |
if spec_type == 'mel': | |
M = librosa.feature.melspectrogram(y=y, sr=sr, hop_length=spec_hop_length) | |
librosa.display.specshow(librosa.power_to_db(M, ref=np.max), | |
y_axis='mel', x_axis='time', hop_length=spec_hop_length, | |
ax=ax, sr=sr) | |
ax.set(title='Mel spectrogram') | |
if spec_type == 'stft': | |
S = np.abs(librosa.stft(y)) | |
img = librosa.display.specshow(librosa.amplitude_to_db(S, ref=np.max), | |
y_axis='log', x_axis='time', ax=ax, sr=sr) | |
ax.set_title('Power spectrogram') | |
# fig.colorbar(img, ax=ax[0], format="%+2.0f dB") | |
ax.set_xticks(shift_array - shift_array[0], | |
shift_array) | |
ax.autoscale() | |
ax.set_xlabel('Time (s)') | |
return fig, ax, (times, onset_env, tempo, beats) | |
def beat_plot(times, onset_env, tempo, beats, y_len, sr, shift_time): | |
""" | |
重新繪製beat | |
""" | |
fig, ax = plt.subplots() | |
ax.plot(times + shift_time, librosa.util.normalize(onset_env), label='Onset strength') | |
ax.vlines(times[beats] + shift_time, 0, 1, alpha=0.5, color='r', linestyle='--', label='Beats') | |
tempoString = 'Tempo = %.2f'% (tempo) | |
ax.plot([], [], ' ', label = tempoString) | |
ax.legend() | |
ax.set_xlabel('Time (s)') | |
ax.set_ylabel('Normalized strength') | |
y_beats = librosa.clicks(frames=beats, sr=sr, length=y_len) | |
return fig, ax, y_beats | |
def predominant_local_pulse(y: npt.ArrayLike, sr:int, shift_time:float=0) -> tuple : | |
onset_env = librosa.onset.onset_strength(y=y, sr=sr) | |
pulse = librosa.beat.plp(onset_envelope=onset_env, sr=sr) | |
beats_plp = np.flatnonzero(librosa.util.localmax(pulse)) | |
times = librosa.times_like(pulse, sr=sr) | |
fig, ax = plt.subplots() | |
ax.plot(times + shift_time, librosa.util.normalize(pulse),label='PLP') | |
ax.vlines(times[beats_plp] + shift_time, 0, 1, alpha=0.5, color='r', | |
linestyle='--', label='PLP Beats') | |
ax.legend() | |
ax.set(title="Predominant local pulse") | |
ax.set_xlabel('Time (s)') | |
ax.set_ylabel('Normalized strength') | |
return fig, ax | |
def static_tempo_estimation(y: npt.ArrayLike, sr: int, hop_length: int = 512) -> tuple: | |
''' | |
To visualize the result of static tempo estimation | |
y: input signal array | |
sr: sampling rate | |
''' | |
onset_env = librosa.onset.onset_strength(y=y, sr=sr) | |
tempo = librosa.beat.tempo(onset_envelope=onset_env, sr=sr) | |
# Static tempo estimation | |
prior = scipy.stats.uniform(30, 300) # uniform over 30-300 BPM | |
utempo = librosa.beat.tempo(onset_envelope=onset_env, sr=sr, prior=prior) | |
tempo = tempo.item() | |
utempo = utempo.item() | |
ac = librosa.autocorrelate(onset_env, max_size=2 * sr // hop_length) | |
freqs = librosa.tempo_frequencies(len(ac), sr=sr, | |
hop_length=hop_length) | |
fig, ax = plt.subplots() | |
ax.semilogx(freqs[1:], librosa.util.normalize(ac)[1:], | |
label='Onset autocorrelation', base=2) | |
ax.axvline(tempo, 0, 1, alpha=0.75, linestyle='--', color='r', | |
label='Tempo (default prior): {:.2f} BPM'.format(tempo)) | |
ax.axvline(utempo, 0, 1, alpha=0.75, linestyle=':', color='g', | |
label='Tempo (uniform prior): {:.2f} BPM'.format(utempo)) | |
ax.set(xlabel='Tempo (BPM)', title='Static tempo estimation') | |
ax.grid(True) | |
ax.legend() | |
return fig, ax | |
def plot_tempogram(y: npt.ArrayLike, sr: int, type: str = 'autocorr', hop_length: int = 512, shift_array: npt.ArrayLike = None) -> tuple : | |
oenv = librosa.onset.onset_strength(y=y, sr=sr, hop_length=hop_length) | |
tempogram = librosa.feature.fourier_tempogram(onset_envelope=oenv, sr=sr, hop_length=hop_length) | |
tempo = librosa.beat.tempo(onset_envelope=oenv, sr=sr, hop_length=hop_length)[0] | |
fig, ax = plt.subplots() | |
if type == 'fourier' : | |
# To determine which temp to show? | |
librosa.display.specshow(np.abs(tempogram), sr=sr, hop_length=hop_length, | |
x_axis='time', y_axis='fourier_tempo', cmap='magma') | |
ax.axhline(tempo, color='w', linestyle='--', alpha=1, label='Estimated tempo={:g}'.format(tempo)) | |
ax.legend(loc='upper right') | |
# ax.title('Fourier Tempogram') | |
if type == 'autocorr' : | |
ac_tempogram = librosa.feature.tempogram(onset_envelope=oenv, sr=sr, hop_length=hop_length, norm=None) | |
librosa.display.specshow(ac_tempogram, sr=sr, hop_length=hop_length, x_axis='time', y_axis='tempo', cmap='magma') | |
ax.axhline(tempo, color='w', linestyle='--', alpha=1, label='Estimated tempo={:g}'.format(tempo)) | |
ax.legend(loc='upper right') | |
# ax.title('Autocorrelation Tempogram') | |
ax.set_xticks(shift_array - shift_array[0], | |
shift_array) | |
ax.autoscale() | |
return fig, ax | |