From 177a8658f08b6cce6d782e1bc78201dba4d687ed Mon Sep 17 00:00:00 2001 From: Alexandre Date: Sun, 2 Jun 2024 16:45:25 +0200 Subject: [PATCH] Restructured files and added another snap function that does not relies on any bpm --- debug.py | 272 +++++++++++++++++++++++++++++++++++++++++++++++ sound_process.py | 181 +++---------------------------- 2 files changed, 289 insertions(+), 164 deletions(-) create mode 100644 debug.py diff --git a/debug.py b/debug.py new file mode 100644 index 0000000..9321052 --- /dev/null +++ b/debug.py @@ -0,0 +1,272 @@ +from math import * +import numpy as np +from scipy.io import wavfile +from scipy import signal +import matplotlib.pyplot as plt +import subprocess +import wave as wv +import struct +import librosa +import heapq +import scipy +import os +import random +from pathlib import Path +from time import sleep +from datetime import timedelta + +def adjust_timings(raw_data, snapped_data, indexes, thr=100): + """ + adjusts weirdly snapped notes + """ + current = 0 + + while(current < len(indexes)): + + if(current < len(indexes) - 3 and current % 2 == 1): # on a 1/4 beat + if(snapped_data[indexes[current]] > thr and snapped_data[indexes[current+1]] > thr and snapped_data[indexes[current+2]] > thr and snapped_data[indexes[current+3]] <= thr): + # -XXX_ + snapped_data[indexes[current+3]] = snapped_data[indexes[current+2]] + snapped_data[indexes[current+2]] = 0 + + if(current > 0 and current < len(indexes) - 1 and current % 2 == 1): + if(snapped_data[indexes[current]] > thr and (snapped_data[indexes[current+1]] < thr or snapped_data[indexes[current-1]] < thr)): + #_X_ + '''if(snapped_data[indexes[current-1]] < thr and raw_data[indexes[current-1]] > raw_data[indexes[current+1]]): + snapped_data[indexes[current-1]] = snapped_data[indexes[current]] + else: + snapped_data[indexes[current+1]] = snapped_data[indexes[current]]''' + + snapped_data[indexes[current]] = 0 + + current += 1 + + print("Resnap done") + return snapped_data + + + +def snap(data, sample_rate, bpm, divisor, show=False): + # adjust time amplitudes to match the given BPM + new = [0 for x in range(int(1000*len(data)/sample_rate))] # 1pt per millisecond + print("old =", len(data)) + print("len =", 1000*len(data)/sample_rate) + k = 0 + t = 0 + percent = 0 + for i in range(len(data)): + + while(t < i/sample_rate): + t = k/(bpm*divisor) + k += 60 + ''' + if(np.abs(i/sample_rate - k/(bpm*divisor)) > np.abs(i/sample_rate - (k-60)/(bpm*divisor))): + k -= 60 + t = k/(bpm*divisor)''' + + if(i%(len(data)//100) == 0): + print(percent, "%") + percent += 1 + + if(int(t*1000) < len(new)): + new[int(t*1000)] = max(data[i], new[int(t*1000)]) + else: + new[len(new)-1] = max(data[i], new[len(new)-1]) + + if(show): + t = [j/1000 for j in range(len(new))] + plt.plot(t, new) + plt.xlabel("Time (e)") + plt.ylabel("Amplitude") + plt.grid() + plt.show() + + return new + +def snap2(data, sample_rate, bpm, first_offset=0, div=4, show=False, adjust=False): + """ + data : list(int) + sample_rate : int + bpm = float + """ + + song_len = int(len(data)/sample_rate) + + indexes = [] + app = True + + reduced = [0 for i in range(song_len*1000)] + new = [0 for i in range(song_len*1000)] + + # build the reduced version + for i in range(len(data)): + x = int(i*1000/sample_rate) + if(x < len(reduced)): + reduced[x] = max(reduced[x], data[i]) + + + print("Build done") + # snap + k = 0 + current_t = first_offset + + while(current_t < 0): + k += 1 + current_t = first_offset + k*60/(bpm*div) + + for j in range(len(new)): + if(j/1000 > current_t): + k += 1 + current_t = first_offset + k*60/(bpm*div) + app = True + + y = int(current_t*1000) + if(y < len(new)): + new[y] = max(new[y], reduced[j]) + + if(app): + indexes.append(y) + app = False + + print("Snap done") + + if(adjust): + print("Len :", len(indexes)) + + new = adjust_timings(reduced, new, indexes) + + + if(show): + new2 = [0.9 if new[i] != 0 else 0 for i in range(len(new))] + + t = [j/1000+first_offset for j in range(len(new))] + beats_1 = [0 for j in range(len(new))] + beats_2 = [0 for k in range(len(new))] + beats_4 = [0 for l in range(len(new))] + + k = 0 + current_t = first_offset + + while(current_t < 0): + k += 1 + current_t = first_offset + k*60/(bpm*div) + + while(1000*current_t < len(new)): + beats_4[int(1000*current_t)] = 0.9 + + if(k % 2 == 0): + beats_2[int(1000*current_t)] = 0.92 + + if(k % 4 == 0): + beats_1[int(1000*current_t)] = 0.94 + + k += 1 + current_t = first_offset + k*60/(bpm*div) + + plt.plot(t, new2, "bo") + plt.plot(t, beats_4, "r-") + plt.plot(t, beats_2, "y-") + plt.plot(t, beats_1, "g-") + plt.xlabel("Time (s)") + plt.ylabel("Amplitude") + plt.grid() + plt.show() + + return new + +def filter_peaks(data, sample_rate=44100, thr=1000): + tdata = [] + times = [] + + for i in range(len(data)): + if data[i] > thr: + tdata.append(data[i]) + times.append(i/sample_rate) + + return (tdata, times) + ''' + times is in seconds + ''' + +def get_spacing(data, sample_rate=44100, show=False, retrieve=False): + tdata, times = filter_peaks(data, sample_rate=sample_rate) + + absc = [i for i in range(len(times))] + dt = [0] + + for i in range(1, len(times)): + dt.append(1000*(times[i]-times[i-1])) + + if(show): + plt.plot(absc, dt) + plt.xlabel("x") + plt.ylabel("T(peak x) - T(peak x-1) (ms)") + plt.grid() + plt.show() + + if(retrieve): + return dt + + ''' + post-condition : + - dt[i] = time(peak number i) - time(peak number i-1) + - dt is in ms + ''' + +def snap3(data, sample_rate=44100, mintime=10, initial_plot=False, after_plot=False): + ''' + explaination : + 1) get the time differences (cf get_spacing) + 2) for eack peak : 2 cases + - if it's farther than mintime (in ms) : + > calculate the weighted mean if all elements in temp_list + > place a note at that mean + > empty temp_list + > push the current peak to temp_list + - else : + > push the current peak to temp_list + ''' + data_peaks, peak_times = filter_peaks(data, sample_rate=sample_rate) + time_diff = get_spacing(data, show=initial_plot, retrieve=True) + + res_peaks = [] + res_times = [] + + segments = [] + seglen = [] + + current_left = 0 + + for i in range(len(peak_times)): + if(time_diff[i] > mintime): + segments.append([current_left, i]) + seglen.append(peak_times[i]-peak_times[current_left]) + res_peaks.append(500) + res_times.append(peak_times[i]) + current_left = i + + for i in range(len(segments)): + print(segments[i], ":", seglen[i]) + + if(after_plot): + peakplot = [] + diffplot = [] + for x in range(len(peak_times)): + peakplot.append(peak_times[x]-peak_times[x]/1000) + peakplot.append(peak_times[x]) + peakplot.append(peak_times[x]+peak_times[x]/1000) + diffplot.append(0) + diffplot.append(time_diff[x]) + diffplot.append(0) + + plt.plot(res_times, res_peaks, "ro", label="placed beats") + plt.plot(peakplot, diffplot, label="derivatine of time") + plt.xlabel("t (s)") + plt.ylabel(".") + plt.legend(loc="upper left") + plt.grid() + plt.show() + + return (res_peaks, res_times) + + \ No newline at end of file diff --git a/sound_process.py b/sound_process.py index cf937eb..853ac46 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 +import debug + print("Starting...\n") def filter_n_percent_serial(song_name, offset, n_iter, step, threshold): @@ -283,11 +285,11 @@ def test_sample(timelist): print("F") sleep(timelist[i]-timelist[i-1]) -def convert_tuple(datares, freq): +def convert_tuple(data, times): """ - Takes datares and converts it to a list of tuples (amplitude, datetimes) + Takes data and converts it to a list of tuples (amplitude, datetimes) """ - return [(timedelta(milliseconds=i), datares[i], freq[i]) for i in range(len(datares)) if datares[i] > 0] + return [(times[i], data[i]) for i in range(len(data))] def get_songlen(filename): """ @@ -298,163 +300,6 @@ def get_songlen(filename): return (len(global_data)/sample_rate) -def snap(data, sample_rate, bpm, divisor, show=False): - # adjust time amplitudes to match the given BPM - new = [0 for x in range(int(1000*len(data)/sample_rate))] # 1pt per millisecond - print("old =", len(data)) - print("len =", 1000*len(data)/sample_rate) - k = 0 - t = 0 - percent = 0 - for i in range(len(data)): - - while(t < i/sample_rate): - t = k/(bpm*divisor) - k += 60 - ''' - if(np.abs(i/sample_rate - k/(bpm*divisor)) > np.abs(i/sample_rate - (k-60)/(bpm*divisor))): - k -= 60 - t = k/(bpm*divisor)''' - - if(i%(len(data)//100) == 0): - print(percent, "%") - percent += 1 - - if(int(t*1000) < len(new)): - new[int(t*1000)] = max(data[i], new[int(t*1000)]) - else: - new[len(new)-1] = max(data[i], new[len(new)-1]) - - if(show): - t = [j/1000 for j in range(len(new))] - plt.plot(t, new) - plt.xlabel("Time (e)") - plt.ylabel("Amplitude") - plt.grid() - plt.show() - - return new - -def adjust_timings(raw_data, snapped_data, indexes, thr=100): - """ - adjusts weirdly snapped notes - """ - current = 0 - - while(current < len(indexes)): - - if(current < len(indexes) - 3 and current % 2 == 1): # on a 1/4 beat - if(snapped_data[indexes[current]] > thr and snapped_data[indexes[current+1]] > thr and snapped_data[indexes[current+2]] > thr and snapped_data[indexes[current+3]] <= thr): - # -XXX_ - snapped_data[indexes[current+3]] = snapped_data[indexes[current+2]] - snapped_data[indexes[current+2]] = 0 - - if(current > 0 and current < len(indexes) - 1 and current % 2 == 1): - if(snapped_data[indexes[current]] > thr and (snapped_data[indexes[current+1]] < thr or snapped_data[indexes[current-1]] < thr)): - #_X_ - '''if(snapped_data[indexes[current-1]] < thr and raw_data[indexes[current-1]] > raw_data[indexes[current+1]]): - snapped_data[indexes[current-1]] = snapped_data[indexes[current]] - else: - snapped_data[indexes[current+1]] = snapped_data[indexes[current]]''' - - snapped_data[indexes[current]] = 0 - - current += 1 - - print("Resnap done") - return snapped_data - -def snap2(data, sample_rate, bpm, first_offset=0, div=4, show=False, adjust=False): - """ - data : list(int) - sample_rate : int - bpm = float - """ - - song_len = int(len(data)/sample_rate) - - indexes = [] - app = True - - reduced = [0 for i in range(song_len*1000)] - new = [0 for i in range(song_len*1000)] - - # build the reduced version - for i in range(len(data)): - x = int(i*1000/sample_rate) - if(x < len(reduced)): - reduced[x] = max(reduced[x], data[i]) - - - print("Build done") - # snap - k = 0 - current_t = first_offset - - while(current_t < 0): - k += 1 - current_t = first_offset + k*60/(bpm*div) - - for j in range(len(new)): - if(j/1000 > current_t): - k += 1 - current_t = first_offset + k*60/(bpm*div) - app = True - - y = int(current_t*1000) - if(y < len(new)): - new[y] = max(new[y], reduced[j]) - - if(app): - indexes.append(y) - app = False - - print("Snap done") - - if(adjust): - print("Len :", len(indexes)) - - new = adjust_timings(reduced, new, indexes) - - - if(show): - new2 = [0.9 if new[i] != 0 else 0 for i in range(len(new))] - - t = [j/1000+first_offset for j in range(len(new))] - beats_1 = [0 for j in range(len(new))] - beats_2 = [0 for k in range(len(new))] - beats_4 = [0 for l in range(len(new))] - - k = 0 - current_t = first_offset - - while(current_t < 0): - k += 1 - current_t = first_offset + k*60/(bpm*div) - - while(1000*current_t < len(new)): - beats_4[int(1000*current_t)] = 0.9 - - if(k % 2 == 0): - beats_2[int(1000*current_t)] = 0.92 - - if(k % 4 == 0): - beats_1[int(1000*current_t)] = 0.94 - - k += 1 - current_t = first_offset + k*60/(bpm*div) - - plt.plot(t, new2, "bo") - plt.plot(t, beats_4, "r-") - plt.plot(t, beats_2, "y-") - plt.plot(t, beats_1, "g-") - plt.xlabel("Time (s)") - plt.ylabel("Amplitude") - plt.grid() - plt.show() - - return new - def convert_to_wav(song_name:str, output_file="audio.wav") -> str: """ Converts the song to .wav, only if it's not already in wave format. @@ -495,16 +340,24 @@ def process_song(filename, bpm, offset0=0, div_len_factor=1, n_iter_2=-1, thresh 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, 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) + + (snapped_data, times) = debug.snap3(datares, mintime=50, initial_plot=True, after_plot=True) + + #frequencies=get_freq(filtered_name, offset, div_len, div_len*n_iter, snapped_data, True) + Path(f"{filename}_trimmed.wav").unlink() + return convert_tuple(snapped_data, times) + + ''' + datares = debug.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) Path(f"{filename}_trimmed.wav").unlink() return convert_tuple(datares, frequencies) + ''' def main(): - data = process_song("tetris_4.wav", 160, n_iter_2=48, threshold=100) + data = process_song("tetris_4.wav", 160, n_iter_2=48) #print(data) print("Program finished with return 0")