From d499dee0deb4a53d296fec732aa115112baf4179 Mon Sep 17 00:00:00 2001 From: Thibaud Date: Sat, 1 Jun 2024 16:21:04 +0200 Subject: [PATCH] Experimental Changes --- compare_plot.py | 7 ++--- main.py | 6 ++-- sound_process.py | 81 ++++++++++++++++++++++++++++++++---------------- 3 files changed, 59 insertions(+), 35 deletions(-) diff --git a/compare_plot.py b/compare_plot.py index bea01c1..4687fe8 100644 --- a/compare_plot.py +++ b/compare_plot.py @@ -8,10 +8,9 @@ def compare_plot(): beatmap = sl.Beatmap.from_path(filename) timing = beatmap.timing_points[0] bpm = timing.bpm - offset = timing.offset.total_seconds() * 1000 - data = sound_process.process_song(beatmap.audio_filename, bpm, offset0=offset, n_iter_2=48, divisor=4) - - timings, amplitudes, freqs = [x[0].total_seconds() for x in data], [x[1] for x in data], [x[2] for x in data] + offset = timing.offset + timings, amplitudes = sound_process.process_song(beatmap.audio_filename, bpm, offset=offset, n_iter_2=-1) + timings = [x.total_seconds() for x in timings] original_times = [x.time.total_seconds() for x in beatmap.hit_objects(spinners=False) if x.time.total_seconds() <= timings[len(timings) - 1]] diff --git a/main.py b/main.py index 6d83827..7187afc 100644 --- a/main.py +++ b/main.py @@ -10,15 +10,13 @@ def main(): beatmap = sl.Beatmap.from_path(filename) timing = beatmap.timing_points[0] bpm = timing.bpm - offset = timing.offset.total_seconds() * 1000 + offset = timing.offset print(beatmap.audio_filename) - data = sound_process.process_song(beatmap.audio_filename, int(bpm), offset0=offset, n_iter_2=-1) + timings, amplitudes = sound_process.process_song(beatmap.audio_filename, bpm, offset0=offset, n_iter_2=-1) # NOTE : remove n_iter_2 to map the whole music - timings, amplitudes, freqs = [x[0] for x in data], [x[1] for x in data], [x[2] for x in data] beatmap._hit_objects = place.greedy(bpm, offset, timings, amplitudes) - beatmap.display_name = "TIPE's Extra" #beatmap._hit_objects = [sl.Slider(sl.Position(0, 0), timedelta(milliseconds=3), timedelta(milliseconds=130), 0, sl.curve.Linear([sl.Position(0, 0), sl.Position(100, 100)], 100), 100, 2, 1, 1, 1, timing.ms_per_beat, [], [],)] beatmap.write_path("rewrite.osu") diff --git a/sound_process.py b/sound_process.py index c832721..0ea12c5 100755 --- a/sound_process.py +++ b/sound_process.py @@ -15,6 +15,8 @@ from pathlib import Path from time import sleep from datetime import timedelta +WORKING_SAMPLE_RATE = 1000 + print("Starting...\n") def filter_n_percent_serial(song_name, offset, n_iter, step, threshold): @@ -28,6 +30,8 @@ def filter_n_percent_serial(song_name, offset, n_iter, step, threshold): filter data associated with song_name to keep only the highest threshold% values """ + offset = offset.total_seconds() + subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(offset+step*n_iter), "-i", song_name, "crop.wav"]) sample_rate, global_data = wavfile.read('crop.wav') @@ -94,6 +98,7 @@ def get_freq(song_name, offset, step, songlen, data, display=False): """ for a given list of amplitudes, returns the corresponding peak frequencies """ + offset = offset.total_seconds() fft_list = [] times = [] current_time = offset @@ -118,7 +123,7 @@ def get_freq(song_name, offset, step, songlen, data, display=False): for s in range(len(data)): if(data[s] != 0): - pff = scipy.fft.rfft(global_data[int(s*len(global_data)/len(data)):int(44100*step+int(s*len(global_data)/len(data)))]) + pff = scipy.fft.rfft(global_data[int(s*len(global_data)/len(data)):int(WORKING_SAMPLE_RATE*step+int(s*len(global_data)/len(data)))]) mx = max(np.abs(pff)) for id in range(len(pff)): @@ -167,6 +172,8 @@ def void_freq(song_name, offset, songlen, increment, minfreq, maxfreq, upperthr, write : bool (should be set to True) output_file : technical """ + offset = offset.total_seconds() + fft_list = [] times = [] current_time = offset @@ -257,7 +264,7 @@ def void_freq(song_name, offset, songlen, increment, minfreq, maxfreq, upperthr, res[i] = np.int16(32767*res[i]/mx) res = np.array(res) - wavfile.write(output_file, 44100, res) + wavfile.write(output_file, WORKING_SAMPLE_RATE, res) #plt.plot(np.abs(pfreq[:len(fft_list[0])]), np.abs(fft_list[0])) #plt.grid() @@ -460,21 +467,39 @@ def snap2(data, sample_rate, bpm, first_offset=0, div=4, show=False, adjust=Fals return new -def convert_to_wav(song_name:str, output_file="audio.wav") -> str: +def convert_song(song_name:str, output_file="audio.wav") -> str: """ - Converts the song to .wav, only if it's not already in wave format. - Currently relies on file extension. + Converts the song to .wav AND lower its sample rate to 1000. Returns: the song_name that should be used afterwards. """ - extension = Path(song_name).suffix - match extension: - case ".mp3" | ".ogg": - print("Converting to .wav...") - subprocess.run(["ffmpeg", "-y", "-i", song_name, output_file]) - return output_file - return song_name + subprocess.run(["ffmpeg", "-y", "-i", song_name, "-ar", "1000", output_file]) + return output_file -def process_song(filename, bpm, offset0=0, div_len_factor=1, n_iter_2=-1, threshold=0.5, divisor=4): +def quantify(time: timedelta, bpm, offset, snapping): + """ + Input: timedelta, bpm, offset, and snapping divisor (2 for 1/2, etc...) + Returns a timedelta that is properly timed to the map. + """ + offset_ms = offset.total_seconds() / 1000 + time_ms = time.total_seconds() * 1000 + time_spacing = (60000/bpm)/snapping + beats_away = round((time_ms - offset_ms)/time_spacing) + new_time = timedelta(milliseconds=time_spacing*beats_away + offset_ms) + return new_time + +def quantify_all(amplitudes_ugly, bpm, offset_ms, divisor): + n = len(amplitudes_ugly) + covered = [False] * n + times = [] + amplitudes = [] + for i in range(n): + if amplitudes_ugly[i] != 0 and not covered[i]: + times.append(quantify(timedelta(milliseconds=i), bpm, offset_ms, divisor)) + amplitudes.append(amplitudes_ugly[i]) + covered[i] = True + return times, amplitudes + +def process_song(filename, bpm, offset=timedelta(milliseconds=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) @@ -485,9 +510,7 @@ def process_song(filename, bpm, offset0=0, div_len_factor=1, n_iter_2=-1, thresh divisor : int [+] (beat divisor used to snap the notes, default is 4) """ - filename = convert_to_wav(filename) - - offset = offset0/1000 + filename = convert_song(filename) div_len = div_len_factor*60/bpm-0.01 @@ -495,18 +518,19 @@ def process_song(filename, bpm, offset0=0, div_len_factor=1, n_iter_2=-1, thresh song_len = get_songlen(filename) if(n_iter == -1): - n_iter = int((song_len-offset/1000)/div_len)-1 + n_iter = floor((song_len-offset.total_seconds())/div_len)-1 filtered_name = f"{filename}_trimmed.wav" - void_freq(filename, offset, min(song_len, offset+div_len*(n_iter+1)+0.01), 4*60/bpm, minfreq=0, maxfreq=220, upperthr=5000, ampthr=60, ampfreq = 1200, ampval = 5.0, leniency = 0.005, write=True, linear=False, output_file=filtered_name) + void_freq(filename, offset, min(song_len, offset.total_seconds()+div_len*(n_iter+1)+0.01), 4*60/bpm, minfreq=0, maxfreq=220, 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) - datares = snap2(datares, 44100, bpm, first_offset=offset, div=divisor, show=True, adjust=True) - frequencies = get_freq(filtered_name, offset, div_len, div_len*n_iter, datares, True) + amplitudes_ugly = filter_n_percent_serial(filtered_name, offset, n_iter, div_len, threshold) + #datares = snap(datares, WORKING_SAMPLE_RATE, bpm, 4, True) + times, amplitudes = quantify_all(amplitudes_ugly, bpm, offset, divisor) + #frequencies = get_freq(filtered_name, offset, div_len, div_len*n_iter, datares, True) Path(f"{filename}_trimmed.wav").unlink() - return convert_tuple(datares, frequencies) + #return convert_tuple(datares, frequencies) + return times, amplitudes def main(): data = process_song("tetris_4.wav", 160, n_iter_2=48, threshold=100) @@ -564,7 +588,7 @@ if(False): #t, f, Zxx = fct("deltamax.wav", 9.992, 0.032, 20, 3000, False) #t, f, Zxx = fct("da^9.wav", 8.463, 0.032, 20, 5000, False) t, f, Zxx = fct("13. Cosmic Mind.wav", 0, 0.032, 20, 5000, False) - #t, f, Zxx = fct("Furioso Melodia 44100.wav", 4, 0.032, 8, 3000, False) + #t, f, Zxx = fct("Furioso Melodia WORKING_SAMPLE_RATE.wav", 4, 0.032, 8, 3000, False) #t, f, Zxx = fct("changing.wav", 0, 0.05, 3.9, 5000, False) #fct("worlds_end_3.wav", 75, (60/178)/4, 75+2, 2500) @@ -575,7 +599,7 @@ if(False): (t, data) = peaks("worlds_end_3.wav", 74.582, 6, False, 0.9) #(t, data) = peaks("da^9.wav", 8.463, 301.924 - 8.463, False, 0.95) #(t, data) = peaks("deltamax.wav", 8.463, 30101.924 - 8.463, False, 0.92) - da = find_bpm(t, 44100, data, 100, 200, 1, 10) + da = find_bpm(t, WORKING_SAMPLE_RATE, data, 100, 200, 1, 10) print("BPM data is", da)''' #data = [-1 for i in range(int(x))] @@ -777,6 +801,7 @@ def extract_peaks_v2(song_data, sample_rate, offset, display, threshold, seglen) return (t, song_data) def peaks(song_name, offset, length, display, thr): + offset = offset.total_seconds() subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(length), "-i", song_name, "crop.wav"]) sample_rate, audio_data = wavfile.read('crop.wav') @@ -785,7 +810,7 @@ def peaks(song_name, offset, length, display, thr): subprocess.run(["rm", "crop.wav"]) #return extract_peaks(audio_data, sample_rate, offset, display, thr) - return extract_peaks_v2(audio_data, sample_rate, offset, display, thr, 44100*2) + return extract_peaks_v2(audio_data, sample_rate, offset, display, thr, WORKING_SAMPLE_RATE*2) def find_bpm(sample_rate, data, minbpm, maxbpm, step, width): optimal = minbpm @@ -987,6 +1012,8 @@ def filter_n_percent(song_name, offset, length, threshold, reduce, show): # threshold is in ]0, 100] # filter data associated with song_name to keep only the highest threshold% values + offset = offset.total_seconds() + subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(length), "-i", song_name, "crop.wav"]) sample_rate, song_data = wavfile.read('crop.wav') @@ -995,7 +1022,7 @@ def filter_n_percent(song_name, offset, length, threshold, reduce, show): subprocess.run(["rm", "crop.wav"]) if(reduce): - (song_data,e) = to_ms(song_data, 44100, 1) + (song_data,e) = to_ms(song_data, WORKING_SAMPLE_RATE, 1) sample_rate = 1000 mx = max(song_data)