Merge with the 'oops' fix

This commit is contained in:
Thibaud 2024-06-03 11:43:07 +02:00
commit 1453e1d639
2 changed files with 289 additions and 169 deletions

272
debug.py Normal file
View File

@ -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)

View File

@ -15,6 +15,8 @@ from pathlib import Path
from time import sleep from time import sleep
from datetime import timedelta from datetime import timedelta
import debug
print("Starting...\n") print("Starting...\n")
def filter_n_percent_serial(song_name, offset, n_iter, step, threshold): def filter_n_percent_serial(song_name, offset, n_iter, step, threshold):
@ -283,11 +285,11 @@ def test_sample(timelist):
print("F") print("F")
sleep(timelist[i]-timelist[i-1]) 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): def get_songlen(filename):
""" """
@ -298,168 +300,6 @@ def get_songlen(filename):
return (len(global_data)/sample_rate) 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):
t = [j/1000+first_offset for j in range(len(new))]
scatter_t = [t[i] for i in range(len(new)) if new[i] != 0]
scatter_chosen_rhythm = [0.9 for i in range(len(new)) if new[i] != 0 ]
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.902
if(k % 4 == 0):
beats_1[int(1000*current_t)] = 0.91
if(k % 16 == 0):
beats_1[int(1000*current_t)] = 0.915
k += 1
current_t = first_offset + k*60/(bpm*div)
points = plt.scatter(scatter_t, scatter_chosen_rhythm, marker="o", label="Detected Rhythm")
div1_plot, = plt.plot(t, beats_4, "b-", label="1/4")
div2_plot, = plt.plot(t, beats_2, "r-", label="1/2")
div3_plot, = plt.plot(t, beats_1, "black", label="1/1")
plt.xlabel("Time (s)")
#plt.ylabel("Amplitude")
plt.legend(handles=[points, div1_plot, div2_plot, div3_plot])
plt.grid()
plt.show()
return new
def convert_to_wav(song_name:str, output_file="audio.wav") -> str: 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. Converts the song to .wav, only if it's not already in wave format.
@ -500,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" 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+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 = 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) 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) return convert_tuple(datares, frequencies)
'''
def main(): 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(data)
print("Program finished with return 0") print("Program finished with return 0")