Spaces:
Runtime error
Runtime error
| import numpy as np | |
| from . import utils | |
| def scale(beatmap:np.ndarray, scale:float, log = True, integer = True) -> np.ndarray: | |
| if isinstance(scale, str): scale = utils._safer_eval(scale) | |
| assert scale>0, f"scale should be > 0, your scale is {scale}" | |
| if scale == 1: return beatmap | |
| else: | |
| import math | |
| if log is True: print(f'scale={scale}; ') | |
| a = 0 | |
| b = np.array([], dtype=int) | |
| if scale%1==0: | |
| while a < len(beatmap): | |
| b = np.append(b, beatmap[int(a)]) | |
| a += scale | |
| else: | |
| if integer is True: | |
| while a + 1 < len(beatmap): | |
| b = np.append(b, int((1 - (a % 1)) * beatmap[math.floor(a)] + (a % 1) * beatmap[math.ceil(a)])) | |
| a += scale | |
| else: | |
| while a + 1 < len(beatmap): | |
| b = np.append(b, (1 - (a % 1)) * beatmap[math.floor(a)] + (a % 1) * beatmap[math.ceil(a)]) | |
| a += scale | |
| return b | |
| def shift(beatmap:np.ndarray, shift:float, log = True, mode = 1) -> np.ndarray: | |
| if isinstance(shift, str): shift = utils._safer_eval(shift) | |
| if shift == 0: return beatmap | |
| # positive shift | |
| elif shift > 0: | |
| # full value of beats is removed from the beginning | |
| if shift >= 1: beatmap = beatmap[int(shift//1):] | |
| # shift beatmap by the decimal value | |
| if shift%1 != 0: | |
| shift = shift%1 | |
| for i in range(len(beatmap) - int(shift) - 1): | |
| beatmap[i] = int(beatmap[i] + shift * (beatmap[i + 1] - beatmap[i])) | |
| # negative shift | |
| else: | |
| shift = -shift | |
| # full values are inserted in between first beats | |
| if shift >= 1: | |
| if mode == 1: | |
| step = int((beatmap[1] - beatmap[0]) / (int(shift//1) + 1)) | |
| beatmap = np.insert(arr = beatmap, obj = 1, values = np.linspace(start = beatmap[0] + step - 1, stop = 1 + beatmap[1] - step, num = int(shift//1))) | |
| elif mode == 2: | |
| for i in range(int(shift//1)): | |
| beatmap = np.insert(arr = beatmap, obj = (i*2)+1, values = int((beatmap[i*2] + beatmap[(i*2)+1])/2)) | |
| # shift beatmap by the decimal value | |
| if shift%1 != 0: | |
| shift = shift%1 | |
| for i in reversed(range(len(beatmap))): | |
| if i==0: continue | |
| beatmap[i] = int(beatmap[i] - shift * (beatmap[i] - beatmap[i-1])) | |
| return beatmap | |
| def generate(audio: np.ndarray, sr: int, lib='madmom.BeatDetectionProcessor', caching=True, filename: str = None, log = True, load_settings = True, split=None): | |
| """Creates beatmap attribute with a list of positions of beats in samples.""" | |
| if log is True: print(f'Analyzing beats using {lib}; ', end='') | |
| # load a beatmap if it is cached: | |
| if caching is True and filename is not None: | |
| audio_id=hex(len(audio[0])) | |
| import os | |
| if not os.path.exists('beat_manipulator/beatmaps'): | |
| os.mkdir('beat_manipulator/beatmaps') | |
| cacheDir="beat_manipulator/beatmaps/" + ''.join(filename.replace('\\', '/').split('/')[-1]) + "_"+lib+"_"+audio_id+'.txt' | |
| try: | |
| beatmap=np.loadtxt(cacheDir, dtype=int) | |
| if log is True: print('loaded cached beatmap.') | |
| except OSError: | |
| if log is True:print("beatmap hasn't been generated yet. Generating...") | |
| beatmap = None | |
| #generate the beatmap | |
| if beatmap is None: | |
| if 'madmom' in lib.lower(): | |
| from collections.abc import MutableMapping, MutableSequence | |
| import madmom | |
| assert len(audio[0])>sr*2, f'Audio file is too short, len={len(audio[0])} samples, or {len(audio[0])/sr} seconds. Minimum length is 2 seconds, audio below that breaks madmom processors.' | |
| if lib=='madmom.BeatTrackingProcessor': | |
| proc = madmom.features.beats.BeatTrackingProcessor(fps=100) | |
| act = madmom.features.beats.RNNBeatProcessor()(madmom.audio.signal.Signal(audio.T, sr)) | |
| beatmap= proc(act)*sr | |
| elif lib=='madmom.BeatTrackingProcessor.constant': | |
| proc = madmom.features.beats.BeatTrackingProcessor(fps=100, look_ahead=None) | |
| act = madmom.features.beats.RNNBeatProcessor()(madmom.audio.signal.Signal(audio.T, sr)) | |
| beatmap= proc(act)*sr | |
| elif lib=='madmom.BeatTrackingProcessor.consistent': | |
| proc = madmom.features.beats.BeatTrackingProcessor(fps=100, look_ahead=None, look_aside=0) | |
| act = madmom.features.beats.RNNBeatProcessor()(madmom.audio.signal.Signal(audio.T, sr)) | |
| beatmap= proc(act)*sr | |
| elif lib=='madmom.BeatDetectionProcessor': | |
| proc = madmom.features.beats.BeatDetectionProcessor(fps=100) | |
| act = madmom.features.beats.RNNBeatProcessor()(madmom.audio.signal.Signal(audio.T, sr)) | |
| beatmap= proc(act)*sr | |
| elif lib=='madmom.BeatDetectionProcessor.consistent': | |
| proc = madmom.features.beats.BeatDetectionProcessor(fps=100, look_aside=0) | |
| act = madmom.features.beats.RNNBeatProcessor()(madmom.audio.signal.Signal(audio.T, sr)) | |
| beatmap= proc(act)*sr | |
| elif lib=='madmom.CRFBeatDetectionProcessor': | |
| proc = madmom.features.beats.CRFBeatDetectionProcessor(fps=100) | |
| act = madmom.features.beats.RNNBeatProcessor()(madmom.audio.signal.Signal(audio.T, sr)) | |
| beatmap= proc(act)*sr | |
| elif lib=='madmom.CRFBeatDetectionProcessor.constant': | |
| proc = madmom.features.beats.CRFBeatDetectionProcessor(fps=100, use_factors=True, factors=[0.5, 1, 2]) | |
| act = madmom.features.beats.RNNBeatProcessor()(madmom.audio.signal.Signal(audio.T, sr)) | |
| beatmap= proc(act)*sr | |
| elif lib=='madmom.DBNBeatTrackingProcessor': | |
| proc = madmom.features.beats.DBNBeatTrackingProcessor(fps=100) | |
| act = madmom.features.beats.RNNBeatProcessor()(madmom.audio.signal.Signal(audio.T, sr)) | |
| beatmap= proc(act)*sr | |
| elif lib=='madmom.DBNBeatTrackingProcessor.1000': | |
| proc = madmom.features.beats.DBNBeatTrackingProcessor(fps=100, transition_lambda=1000) | |
| act = madmom.features.beats.RNNBeatProcessor()(madmom.audio.signal.Signal(audio.T, sr)) | |
| beatmap= proc(act)*sr | |
| elif lib=='madmom.DBNDownBeatTrackingProcessor': | |
| proc = madmom.features.downbeats.DBNDownBeatTrackingProcessor(beats_per_bar=[4], fps=100) | |
| act = madmom.features.downbeats.RNNDownBeatProcessor()(madmom.audio.signal.Signal(audio.T, sr)) | |
| beatmap= proc(act)*sr | |
| beatmap=beatmap[:,0] | |
| elif lib=='madmom.PatternTrackingProcessor': #broken | |
| from madmom.models import PATTERNS_BALLROOM | |
| proc = madmom.features.downbeats.PatternTrackingProcessor(PATTERNS_BALLROOM, fps=50) | |
| from madmom.audio.spectrogram import LogarithmicSpectrogramProcessor, SpectrogramDifferenceProcessor, MultiBandSpectrogramProcessor | |
| from madmom.processors import SequentialProcessor | |
| log = LogarithmicSpectrogramProcessor() | |
| diff = SpectrogramDifferenceProcessor(positive_diffs=True) | |
| mb = MultiBandSpectrogramProcessor(crossover_frequencies=[270]) | |
| pre_proc = SequentialProcessor([log, diff, mb]) | |
| act = pre_proc(madmom.audio.signal.Signal(audio.T, sr)) | |
| beatmap= proc(act)*sr | |
| beatmap=beatmap[:,0] | |
| elif lib=='madmom.DBNBarTrackingProcessor': #broken | |
| beats = generate(audio=audio, sr=sr, filename=filename, lib='madmom.DBNBeatTrackingProcessor', caching = caching) | |
| proc = madmom.features.downbeats.DBNBarTrackingProcessor(beats_per_bar=[4], fps=100) | |
| act = madmom.features.downbeats.RNNBarProcessor()(((madmom.audio.signal.Signal(audio.T, sr)), beats)) | |
| beatmap= proc(act)*sr | |
| elif lib=='librosa': #broken in 3.9, works in 3.8 | |
| import librosa | |
| beat_frames = librosa.beat.beat_track(y=audio[0], sr=sr, hop_length=512) | |
| beatmap = librosa.frames_to_samples(beat_frames[1]) | |
| # save the beatmap and return | |
| if caching is True: np.savetxt(cacheDir, beatmap.astype(int), fmt='%d') | |
| if not isinstance(beatmap, np.ndarray): beatmap=np.asarray(beatmap, dtype=int) | |
| else: beatmap=beatmap.astype(int) | |
| if load_settings is True: | |
| settingsDir="beat_manipulator/beatmaps/" + ''.join(filename.split('/')[-1]) + "_"+lib+"_"+audio_id+'_settings.txt' | |
| if os.path.exists(settingsDir): | |
| with open(settingsDir, 'r') as f: | |
| settings = f.read().split(',') | |
| if settings[0] != 'None': beatmap = scale(beatmap, settings[0], log = False) | |
| if settings[1] != 'None': beatmap = shift(beatmap, settings[1], log = False) | |
| if settings[2] != 'None': beatmap = np.sort(np.absolute(beatmap - int(settings[2]))) | |
| return beatmap | |
| def save_settings(audio: np.ndarray, filename: str = None, lib: str = 'madmom.BeatDetectionProcessor', scale: float = None, shift: float = None, adjust: int = None, normalized: str = None, log = True, overwrite = 'ask'): | |
| if isinstance(overwrite, str): overwrite = overwrite.lower() | |
| audio_id=hex(len(audio[0])) | |
| cacheDir="beat_manipulator/beatmaps/" + ''.join(filename.split('/')[-1]) + "_"+lib+"_"+audio_id+'.txt' | |
| import os | |
| assert os.path.exists(cacheDir), f"Beatmap `{cacheDir}` doesn't exist" | |
| settingsDir="beat_manipulator/beatmaps/" + ''.join(filename.split('/')[-1]) + "_"+lib+"_"+audio_id+'_settings.txt' | |
| try: | |
| a = utils._safer_eval_strict(scale) | |
| if a == 1: scale = None | |
| except Exception as e: assert scale is None, f'scale = `{scale}` - Not a valid scale, should be either a number, a math expression, or None: {e}' | |
| try: | |
| a = utils._safer_eval_strict(shift) | |
| if a == 0: shift = None | |
| except Exception as e: assert shift is None, f'shift = `{shift}` - Not a valid shift: {e}' | |
| assert isinstance(adjust, int) or adjust is None, f'adjust = `{adjust}` should be int, but it is `{type(adjust)}`' | |
| if adjust == 0: adjust = None | |
| if os.path.exists(settingsDir): | |
| if overwrite == 'ask' or overwrite =='a': | |
| what = input(f'`{settingsDir}` already exists. Overwrite (y/n)?: ') | |
| if not (what.lower() == 'y' or what.lower() == 'yes'): return | |
| elif not (overwrite == 'true' or overwrite =='y' or overwrite =='yes' or overwrite is True): return | |
| with open(settingsDir, 'w') as f: | |
| f.write(f'{scale},{shift},{adjust},{normalized}') | |
| if log is True: print(f"Saved scale = `{scale}`, shift = `{shift}`, adjust = `{adjust}` to `{settingsDir}`") | |