Restructured files and added another snap function that does not relies on any bpm

This commit is contained in:
Alexandre 2024-06-02 16:45:25 +02:00
parent 31639c850e
commit 177a8658f0
2 changed files with 289 additions and 164 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 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")