diff --git a/sound_process.py b/sound_process.py index da00863..c32e3d7 100755 --- a/sound_process.py +++ b/sound_process.py @@ -11,7 +11,6 @@ import heapq import scipy import os import random -from datetime import timedelta from pathlib import Path from time import sleep @@ -126,8 +125,15 @@ def filter_n_percent(song_name, offset, length, threshold, reduce, show): return song_data def filter_n_percent_serial(song_name, offset, n_iter, step, threshold): - # threshold is in ]0, 100] - # filter data associated with song_name to keep only the highest threshold% values + """ + song_name : string + offset : int + n_iter : int (number of turns) + step : int (length of each small segment) + threshold : int (is in ]0, 100]) + + filter data associated with song_name to keep only the highest threshold% values + """ subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(offset+step*n_iter), "-i", song_name, "crop.wav"]) @@ -273,7 +279,22 @@ def get_freq(song_name, offset, step, songlen, data, display=False): return frequencies -def void_freq(song_name, offset, songlen, increment, minfreq, maxfreq, upperthr, ampthr, ampfreq, ampval, leniency, write, output_file="trimmed.wav"): +def void_freq(song_name, offset, songlen, increment, minfreq, maxfreq, upperthr, ampthr, ampfreq, ampval, leniency, write, linear, output_file="trimmed.wav"): + """ + song_name : string + offset : int + songlen : int (length of the part that will be filtered, starting from offset) + increment : float (technical parameter) + minfreq and maxfreq : every frequency in [minfreq, maxfreq] will be voided + upperthr : every frequency above upperthr will be voided + ampthr : every frequency with amplitude under MAX/ampthr (aka amplitudes under (100/ampthr)% of the max will be voided + ampfreq, leniency (if linear is false), linear : technical parameters + ampval : int + - if linear is false, then this willbe the maximum amplification possible + - if linear is true, this is the multiplier (Amp <- Amp * (ampval * frequency + leniency)) + write : bool (should be set to True) + output_file : technical + """ fft_list = [] times = [] current_time = offset @@ -306,9 +327,14 @@ def void_freq(song_name, offset, songlen, increment, minfreq, maxfreq, upperthr, print("Finding global max...") - for i in range(len(fft_list)): - for j in range(len(fft_list[i])): - fft_list[i][j] *= (1 + ampval/max(1, np.abs(pfreq[j] - ampfreq))) + if(linear == False): + for i in range(len(fft_list)): + for j in range(len(fft_list[i])): + fft_list[i][j] *= (1 + ampval/max(1, np.abs(pfreq[j] - ampfreq))) + else: + for i in range(len(fft_list)): + for j in range(len(fft_list[i])): + fft_list[i][j] *= (ampval*pfreq[j] + leniency) print("Trimming...") @@ -369,40 +395,52 @@ def test_sample(timelist): print("F") sleep(timelist[i]-timelist[i-1]) -#Offset = 74.582 -#BPM = 178 -#Length = 48*60/BPM-0.01 - -#Offset = 0 -#BPM = 180 -#Length = 48*60/BPM-0.01 - -#Offset = 7 -#BPM = 140 -#Length = 32*60/BPM-0.01 - -def convert_tuple(datares, freq) -> tuple[timedelta, int, float]: +def convert_tuple(datares, freq): """ Takes datares and converts it to a list of tuples (amplitude, time in ms) """ - return [(timedelta(milliseconds=i), datares[i], freq[i]) for i in range(len(datares)) if datares[i] > 0] + return [(i, datares[i], freq[i]) for i in range(len(datares)) if datares[i] > 0] +def get_songlen(filename): + """ + retrieves the length of the song in seconds + """ + sample_rate, global_data = wavfile.read(filename) + return (len(global_data)/sample_rate) + +def process_song(filename, bpm, offset=0, div_len_factor=1, n_iter_2=-1, threshold=0.5, divisor=4): + """ + filename : string (name of the song) + offset : int [+] (song mapping will start from this time in seconds, default is 0) + bpm : int [+] + div_len_factor : float [+] (the length multiplier of each segment, default is 1) + n_iter : int [+*] (the number of iterations, default is -1 (maps the whole music)) + threshold : int [0, 100] (used by the filter function to only keep the largest threshold% of timing points, default is 0.5) + divisor : int [+] (beat divisor used to snap the notes, default is 4) + """ + + div_len = div_len_factor*60/bpm-0.01 + + n_iter = n_iter_2 + if(n_iter == -1): + song_len = get_songlen(filename) + n_iter = int(song_len/div_len)-1 -def process_song(filename, offset, bpm, div_len_factor=60, n_iter=48, threshold=0.5, divisor=4): - div_len = div_len_factor/bpm-0.01 filtered_name = f"{filename}_trimmed.wav" - void_freq(filename, offset, offset+div_len*(n_iter+1)+0.01, 4*60/bpm, minfreq=0, maxfreq=330, upperthr=5000, ampthr=60, ampfreq = 1200, ampval = 7.27, leniency = 0.005, write=True, output_file=filtered_name) + + void_freq(filename, offset, offset+div_len*(n_iter+1)+0.01, 4*60/bpm, minfreq=0, maxfreq=330, upperthr=5000, ampthr=60, ampfreq = 1200, ampval = 5.0, leniency = 0.005, write=True, linear=False, output_file=filtered_name) + #void_freq(filename, offset, offset+div_len*(n_iter+1)+0.01, 4*60/bpm, minfreq=0, maxfreq=330, upperthr=2500, ampthr=60, ampfreq = 1200, ampval = 1/2000, leniency = 0.0, write=True, linear=True, output_file=filtered_name) datares = filter_n_percent_serial(filtered_name, offset, n_iter, div_len, threshold) datares = snap(datares, 44100, bpm, 4, True) frequencies = get_freq(filtered_name, offset, div_len, div_len*n_iter, datares, True) - Path(f"{filename}_trimmed.wav").unlink() + #Path(f"{filename}_trimmed.wav").unlink() return convert_tuple(datares, frequencies) def main(): - data = process_song("tetris_4.wav", 0, 160) - print(data) + data = process_song("tetris_4.wav", 160, n_iter_2 = 32) + #print(data) print("Program finished with return 0") if __name__ == "__main__":