From 65082478584f0259710adb9b8b0665c41fb47326 Mon Sep 17 00:00:00 2001 From: Samuel Aubertin Date: Wed, 27 Mar 2024 18:47:44 +0100 Subject: [PATCH] first --- .gitignore | 1 + Makefile | 8 +++ requirements.txt | 1 + skz-usbprep.py | 134 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 requirements.txt create mode 100755 skz-usbprep.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d17dae --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.venv diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a9fe980 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +all: .venv/bin/activate + source .venv/bin/activate && pip install -r requirements.txt > /dev/null && ./skz-usbprep.py + +.venv/bin/activate: + python -m venv --prompt SKZ-USBPREP .venv + +clean: + rm -rf .venv diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..cedebab --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +essentia diff --git a/skz-usbprep.py b/skz-usbprep.py new file mode 100755 index 0000000..577f287 --- /dev/null +++ b/skz-usbprep.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python3 +import argparse, os, shutil, subprocess +from multiprocessing import Pool, cpu_count + +#os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' +#import tensorflow as tf +#tf.get_logger().setLevel('FATAL') + +from essentia.standard import * +essentia.log.infoActive = False + +DIRS = ['_new', '_new-dubstep'] +RATE = 44100 + +parser = argparse.ArgumentParser() +parser.add_argument('dirs', nargs='*', default=DIRS, help='the list of directories') +args = parser.parse_args() + +def calculate_key(audio_path): + import essentia.streaming as ess + import essentia + + loader = ess.MonoLoader(filename=audio_path) + framecutter = ess.FrameCutter(frameSize=4096, hopSize=2048, silentFrames='noise') + windowing = ess.Windowing(type='blackmanharris62') + spectrum = ess.Spectrum() + spectralpeaks = ess.SpectralPeaks(orderBy='magnitude', + magnitudeThreshold=0.00001, + minFrequency=20, + maxFrequency=3500, + maxPeaks=60) + # Use default HPCP parameters for plots. + # However we will need higher resolution and custom parameters for better Key estimation. + hpcp = ess.HPCP() + hpcp_key = ess.HPCP(size=36, # We will need higher resolution for Key estimation. + referenceFrequency=440, # Assume tuning frequency is 44100. + bandPreset=False, + minFrequency=20, + maxFrequency=3500, + weightType='cosine', + nonLinear=False, + windowSize=1.) + + key = ess.Key(profileType='edmm', # Use profile for electronic music. + numHarmonics=4, + pcpSize=36, + slope=0.6, + usePolyphony=True, + useThreeChords=True) + + # Use pool to store data. + pool = essentia.Pool() + + # Connect streaming algorithms. + loader.audio >> framecutter.signal + framecutter.frame >> windowing.frame >> spectrum.frame + spectrum.spectrum >> spectralpeaks.spectrum + spectralpeaks.magnitudes >> hpcp.magnitudes + spectralpeaks.frequencies >> hpcp.frequencies + spectralpeaks.magnitudes >> hpcp_key.magnitudes + spectralpeaks.frequencies >> hpcp_key.frequencies + hpcp_key.hpcp >> key.pcp + hpcp.hpcp >> (pool, 'tonal.hpcp') + key.key >> (pool, 'tonal.key_key') + key.scale >> (pool, 'tonal.key_scale') + key.strength >> (pool, 'tonal.key_strength') + + # Run streaming network. + essentia.run(loader) + return pool['tonal.key_key'], pool['tonal.key_scale'] + +def calculate_bpm(audio_path): + audio44100 = MonoLoader(filename=audio_path, sampleRate=44100, resampleQuality=4)() + start44100 = int(len(audio44100)*0.2) + end44100 = int(len(audio44100)*0.8) + + rhythm_extractor = RhythmExtractor2013(method="multifeature") + bpm, _, _, _, _ = rhythm_extractor(audio44100[start44100:end44100]) + if bpm < 100: + bpm = bpm * 2 + return bpm + +def reencode(audio_path): + if '.wav' in audio_path[-4:]: + subprocess.run("ffmpeg -hide_banner -loglevel error -y -i \"" + audio_path + "\" -map 0:a -c:a pcm_s16le -ar " + str(RATE) + " -write_xing 0 -id3v2_version 0 -map_metadata -1 -bitexact \"" + audio_path + ".reenc.wav\"", shell=True) + shutil.move(audio_path + ".reenc.wav", audio_path) + return audio_path + elif '.flac' in audio_path[-5:]: + subprocess.run("ffmpeg -hide_banner -loglevel error -y -i \"" + audio_path + "\" -map 0:a -c:a pcm_s16le -ar " + str(RATE) + " -write_xing 0 -id3v2_version 0 -map_metadata -1 -bitexact \"" + audio_path.replace('.flac', '.wav') + "\"", shell=True) + os.remove(audio_path) + return audio_path.replace('.flac', '.wav') + elif '.mp3' in audio_path[-4:]: + subprocess.run("ffmpeg -hide_banner -loglevel error -y -i \"" + audio_path + "\" -map 0:a -c:a copy -write_xing 0 -id3v2_version 0 -map_metadata -1 \"" + audio_path + ".reenc.mp3\"", shell=True) + shutil.move(audio_path + ".reenc.mp3", audio_path) + return(audio_path) + else: + print("Can't reencode", audio_path) + return None + +def rmtags(audio_path): + if '.wav' in audio_path[-4:]: + subprocess.run("id3v2 -D \"" + audio_path + "\" > /dev/null", shell=True) + elif '.mp3' in audio_path[-4:]: + subprocess.run("id3v2 -D \"" + audio_path + "\" > /dev/null", shell=True) + subprocess.run("apetag -m erase -i \"" + audio_path + "\" > /dev/null", shell=True) + else: + print("Can't rmtags'", audio_path) + +def process(audio_path): + #key, scale = calculate_key(audio_path) + audio_path = reencode(audio_path) + if audio_path is not None: + bpm = "{:.2f}".format(calculate_bpm(audio_path)) + if bpm in audio_path: + name = audio_path + else: + rmtags(audio_path) + name = audio_path[:-4] + " - " + bpm + audio_path[-4:] + shutil.move(audio_path, name) + return name + +files = [] + +for dir in args.dirs: + for file in os.listdir(dir): + files.append(dir + "/" + file) + +NICENESS=15 + +with Pool(initializer=os.nice, initargs=(NICENESS,)) as pool: + for result in pool.imap_unordered(process, files): + print(result) + +print('done\n')