Compare commits

...

18 Commits

Author SHA1 Message Date
alexandre 95e06d3235 added rewrite of some core fcts (max_amps and max_freqs) 2024-09-23 17:48:44 +02:00
Thibaud 3ba8a29dd7 Break on Windows, Fix on UNIX 2024-09-16 17:13:02 +02:00
uwuw 0bafd0d899 New combo on new measure. Flow change on new combo. Still bad. 2024-08-22 18:58:16 +02:00
uwuw 568a467a27 Note 2D placement scaffold. Not very good. 2024-08-15 18:36:39 +02:00
uwuw 4ac58100fb add shell=true to fix running on windows powershell 2024-08-15 10:39:56 +02:00
uwuw 8834b4929a add slider to requirements 2024-08-15 10:22:31 +02:00
Alexandre 3587adbe2f rewrite of get_frequencies to match new data structure 2024-06-03 22:28:41 +02:00
Thibaud a567d0ef0d Update main.py to work with new code 2024-06-03 12:17:33 +02:00
Thibaud 1453e1d639 Merge with the 'oops' fix 2024-06-03 11:43:07 +02:00
Alexandre 177a8658f0 Restructured files and added another snap function that does not relies on any bpm 2024-06-02 16:45:25 +02:00
Thibaud 03156d2154 Oops 2024-06-01 16:30:18 +02:00
Thibaud ad5049d1ca Make new snapping plot more similar to osu editor 2024-06-01 12:23:07 +02:00
Alexandre 31639c850e Compat issues 2024-05-31 20:47:04 +02:00
Alexandre c6fda9449d Merge branch 'main' of mp2i-vms.fr:alexandre/osutipe 2024-05-31 20:37:15 +02:00
Alexandre f28e50e643 Added resnap function 2024-05-31 20:37:02 +02:00
Thibaud ac0e189b80 Merge branch 'freq-rhythm'
Add plotting to compare rhythm choice with original beatmap
2024-05-31 13:31:02 +02:00
Alexandre be71965289 Merge branch 'main' of mp2i-vms.fr:alexandre/osutipe 2024-05-30 21:49:23 +02:00
Alexandre b957068977 Tried fixing snap issues (although it's snapped, but not in the right way I guess) 2024-05-30 21:47:14 +02:00
7 changed files with 659 additions and 296 deletions

View File

@ -9,14 +9,14 @@ def compare_plot():
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)
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]
original_times = [x.time.total_seconds() for x in beatmap.hit_objects(spinners=False) if x.time.total_seconds() <= timings[len(timings) - 1]]
#bx.scatter(original_times, ys=10, zs=0)
plt.scatter(timings, amplitudes, marker="x", c="red")
plt.scatter(timings, [1 if amplitudes[i] > 1000 else 0 for i in range(len(amplitudes))], marker="x", c="red")
for x in original_times:
plt.axvline(x=x)

279
debug.py Normal file
View File

@ -0,0 +1,279 @@
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 avg(data, i, j):
res = 0
for e in range(i, min(len(data), j)):
res += data[e]
return (res/(min(len(data), j) - i))
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(avg(data_peaks, current_left, i))
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)
""" res_times is in seconds """

View File

@ -3,19 +3,21 @@ import slider as sl
from datetime import timedelta
import place
import sound_process
import os
import numpy as np
def main():
filename = fd.askopenfilename()
os.chdir(os.path.dirname(filename))
beatmap = sl.Beatmap.from_path(filename)
timing = beatmap.timing_points[0]
bpm = timing.bpm
offset = timing.offset.total_seconds() * 1000
print(beatmap.audio_filename)
data = sound_process.process_song(beatmap.audio_filename, int(bpm), offset0=offset, n_iter_2=48)
amplitudes, timings, frequencies = 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]
timings = [timedelta(seconds=x) for x in timings]
beatmap._hit_objects = place.greedy(bpm, offset, timings, amplitudes)
#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, [], [],)]

View File

@ -12,23 +12,36 @@ def beatify(bpm:float, offset:int, time:timedelta) -> float:
def debeatify(bpm:float, offset:int, beat:int) -> timedelta:
return timedelta(milliseconds=(beat*60000/bpm) + offset)
def f(intensity): return np.pi/2 - np.arctan(2*intensity - 5)
def f(intensity): return np.pi/2 - np.arctan(2*intensity) + np.pi/5
#def f(intensity): return np.pi/2
def greedy(bpm:int, offset:int, timings:list, amplitudes:list):
"""
input: takes Alexandre's note selection / intensity data
output: list of object type / position
"""
flow = 1
notes = [sl.HitObject(0, timedelta(milliseconds=0), 0)] * len(timings)
beats = [beatify(bpm, offset, timing) for timing in timings]
new_combo = True
flow = 0
notes = [sl.HitObject(sl.Position(260, 188), timedelta(milliseconds=0), 0)] * len(timings)
beats = [int(beatify(bpm, offset, timing)*4) for timing in timings]
last_position = (260, 188)
max_x, max_y, min_x, min_y = -np.inf, -np.inf, np.inf, np.inf
for (delta, note_beat, intensity, i) in zip(timings, beats, amplitudes, range(len(timings))):
try:
duration = note_beat - beats[i + 1]
if int(note_beat/32) % 2: #test si la mesure est impaire ou non
new_combo = not (flow == 1)
flow = 1
else:
new_combo = (flow == 1)
flow = -1
duration = abs(note_beat - beats[i + 1])
print(duration)
"""
if duration in (QUARTER, HALF):
notes[i] = sl.Circle(sl.Position(0, 0), delta, 0)
notes[i] = sl.Circle(sl.Position(0, 0), delta, 0)
"""
"""
elif duration % 2 == 0:
rhythms.insert(0, f"slider {duration}")
else:
@ -36,18 +49,37 @@ def greedy(bpm:int, offset:int, timings:list, amplitudes:list):
"""
except IndexError:
notes[i] = sl.Circle(sl.Position(0, 0), delta, 0)
# TODO mettre à jour flow
"""
if len(notes) > 2:
angle = flow * f(rhythm.intensite)
x1, y1 = notes[i-2].position
x2, y2 = notes[i-1].position
old_angle = np.arctan2((y1, y2), (x1, x2))
x3 = x2 + (intensity * np.cos(angle + old_angle))
y3 = y2 + (intensity * np.sin(angle + old_angle))
if duration == QUARTER:
x2, y2 = int(notes[i-1].position.x), int(notes[i-1].position.y)
notes[i] = sl.Circle(sl.Position(x2, y2), delta, 0)
else:
angle = flow * f(intensity/100)
x1, y1 = int(notes[i-2].position.x), int(notes[i-2].position.y)
x2, y2 = int(notes[i-1].position.x), int(notes[i-1].position.y)
old_angle = np.arctan2([y2-y1], [x2-x1])
x3 = x2 + ((duration + new_combo*4)* np.cos(angle + old_angle))
y3 = y2 + ((duration + new_combo*4) * np.sin(angle + old_angle))
notes[i] = sl.Circle(sl.Position(int(x3[0]), int(y3[0])), delta, 0)
else:
pass
"""
notes[i] = sl.Circle(sl.Position(260, 188), delta, 0)
if notes[i].position.x > max_x:
max_x = notes[i].position.x
elif notes[i].position.x < min_x:
min_x = notes[i].position.x
if notes[i].position.y > max_y:
max_y = notes[i].position.y
elif notes[i].position.y < min_y:
min_y = notes[i].position.y
notes[i].new_combo = new_combo
factor_x = 1/(max_x - min_x)
factor_y = 1/(max_y - min_y)
for note in notes:
note.position = sl.Position((note.position.x-min_x)*factor_x*512, (note.position.y-min_y)*factor_y*384)
return notes

View File

@ -1,3 +1,4 @@
slider
audioread==3.0.1
certifi==2024.2.2
cffi==1.16.0

145
script (1).py Normal file
View File

@ -0,0 +1,145 @@
import numpy as np
import scipy as scp
import heapq
def retrieve_dominant_freqs(song_name, offset, songlen, segsize):
# returns a list with peak frequencies alongside the sample rate
# /!\ song_name is specified to be a list, NOT a list of couples (aka song is mono)
# segsize is in seconds
# remove high_pitched/low-pitched frequencies
minfreq = 110
maxfreq = 440*8
# cutting the song to only keep the one we're interested in
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(songlen+offset), "-i", song_name, "crop.wav"], shell=False)
# extracting data from cropped song
sample_rate, song_data = wavfile.read("crop.wav")
blit = int(sample_rate*segsize) # Te
# remove the copy of the song
subprocess.run(["rm", "crop.wav"], shell=False)
# calculate the frequencies associated to the FFTs
pfreq = scipy.fft.rfftfreq(blit, 1/sample_rate)
# left boundary of segment to crop
current_time = offset
# list of FFTs
fft_list = []
# number of samples
k = 0
while(current_time <= songlen+offset):
# index corresponding to left boundary
left_id = int(current_time*sample_rate)
# index corresponding to right boundary
right_id = int((current_time+segsize)*sample_rate)
# calculate the fft, append it to fft_list
pff = scp.fft.rfft(global_data[left:right])
fft_list.append(pff)
# just to avoid what causes 0.1 + 0.1 == 0.2 to be False
k += 1
current_time = offset + k*segsize
# spacing between samples (time)
fe = segsize/sample_rate
# list that will contain the maximum frequencies/amplitudes for all FFTs
maxlist = []
maxamps = []
# find all maximums
for i in range(len(fft_list)):
current_max = -1
current_fmax = 0
for j in range(len(fft_list[i])):
if(pfreq[j] < maxfreq & pfreq[j] >= minfreq & np.abs(fft_list[i][j]) > current_max):
current_max = np.abs(fft_list[i][j])
current_fmax = pfreq[j]
maxlist.append(current_fmax)
maxamps.append(current_max)
# gg
# maxlist[i] corresponds to time (offset + i*segsize)
return (maxlist, maxamps, segsize)
def retrieve_dominant_amps(song_name, offset, songlen, segsize, percent):
# returns a list with the percent% peak amplitudes alongside the sample rate
# /!\ song_name is specified to be a list, NOT a list of couples (aka song is mono)
# segsize is in seconds
# cutting the song to only keep the one we're interested in
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(songlen+offset), "-i", song_name, "crop.wav"], shell=False)
# extracting data from cropped song
sample_rate, song_data = wavfile.read("crop.wav")
blit = int(sample_rate*segsize) # Te
# remove the copy of the song
subprocess.run(["rm", "crop.wav"], shell=False)
# which notes will be voided
is_locked = [False for i in range(len(song_data))]
x = int((len(song_data)*threshold)//100)
print("Retreiving the", int(x), "/", len(song_data), "highest values")
elements = heapq.nlargest(int(x), enumerate(song_data), key=lambda x: x[1])
#returns a list of couples [id, value]
for idx in range(len(elements)):
is_locked[elements[idx][0]] = True
for r in range(len(song_data)):
if(is_locked[r] == False):
song_data[r] = 0
# now we need to reduce song_data so that it matches the length of the previous function's return
res = []
k = 0
current_time = offset
while(current_time <= songlen+offset):
# index corresponding to left boundary
left_id = int(current_time*sample_rate)
# index corresponding to right boundary
right_id = int((current_time+segsize)*sample_rate)
# merge the segment into one value
cmax = 0
for i in range(left_id, right_id):
if(i < len(song_data) & cmax < song_data[i]):
cmax = song_data[i]
res.append(cmax)
k += 1
current_time = current_time + k*segsize
# gg
# res[i] corresponds to time (offset + i*segsize)
return res
print("done")

View File

@ -15,116 +15,10 @@ from pathlib import Path
from time import sleep
from datetime import timedelta
import debug
print("Starting...\n")
def find_bpm_2(sample_rate, data, threshold, maxbpm, show):
mx = np.max(data)
min_spacing = (60*sample_rate)/maxbpm
k = 0
while(k < len(data) and data[k]/mx < threshold):
k += 1
k += 1
spacing = []
current = 1
progress = 0
while(k < len(data)):
if(k%(len(data)/100) == 0):
print(progress, "%")
progress += 1
if(data[k]/mx >= threshold and current > min_spacing):
spacing.append(current)
current = 0
else:
current += 1
k += 1
for x in range(len(spacing)):
spacing[x] = 60/(spacing[x]/sample_rate)
digits = [i for i in range(len(spacing))]
if(show):
plt.plot(digits, spacing)
plt.xlabel("N")
plt.ylabel("BPM")
plt.grid()
plt.show()
beat = np.mean(spacing)
error = np.std(spacing)
return (np.round(beat, 3), np.round(error, 3))
def to_ms(song_data, sample_rate, offset):
# converts audio data to have exactly 1 sample per millisecond (aka set sample_rate to 1000)
new_data = []
spacing = int(sample_rate * 0.001)
mx = max(song_data)
i = 0
while(i < len(song_data)):
avg = 0
for k in range(spacing):
if(i+spacing < len(song_data)):
avg += song_data[i+spacing]
avg = avg / spacing
new_data.append(avg)
i += spacing
if(False): # pls dont kill me thx
t = [offset + j/1000 for j in range(len(new_data))]
plt.plot(t, new_data)
plt.xlabel("Time")
plt.ylabel("Amplitude")
plt.grid()
plt.show()
return (new_data, len(new_data))
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
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(length), "-i", song_name, "crop.wav"])
sample_rate, song_data = wavfile.read('crop.wav')
subprocess.run(["clear"])
subprocess.run(["rm", "crop.wav"])
if(reduce):
(song_data,e) = to_ms(song_data, 44100, 1)
sample_rate = 1000
mx = max(song_data)
is_locked = [False for i in range(len(song_data))]
x = int((len(song_data)*threshold)//100)
#print("X = ", x)
print("Retreiving the", int(x), "/", len(song_data), "highest values")
elements = heapq.nlargest(int(x), enumerate(song_data), key=lambda x: x[1])
print("Done")
for idx in range(len(elements)):
is_locked[elements[idx][0]] = True
for r in range(len(song_data)):
if(is_locked[r] == False):
song_data[r] = 0
if(show):
#print("EEEEE")
t = [offset + j/sample_rate for j in range(len(song_data))]
plt.plot(t, song_data)
plt.xlabel("Time")
plt.ylabel("Amplitude")
plt.grid()
plt.show()
return song_data
def filter_n_percent_serial(song_name, offset, n_iter, step, threshold):
"""
song_name : string
@ -136,16 +30,16 @@ 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
"""
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(offset+step*n_iter), "-i", song_name, "crop.wav"])
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(offset+step*n_iter), "-i", song_name, "crop.wav"], shell=False)
sample_rate, global_data = wavfile.read('crop.wav')
subprocess.run(["clear"])
subprocess.run(["rm", "crop.wav"])
subprocess.run(["clear"], shell=False)
subprocess.run(["rm", "crop.wav"], shell=False)
for i in range(n_iter):
print(i, "/", n_iter)
print(i * step)
#print(i * step)
song_data = global_data[int(i*step*sample_rate):int((i+1)*step*sample_rate)]
if(len(song_data) != 0):
@ -198,46 +92,38 @@ def round_t(id, sample_rate, bpm, div, offset, k0):
def compress(Zxx):
res = []
def get_freq(song_name, offset, step, songlen, data, display=False):
def get_freq(song_name, times, width=1000, display=False):
"""
for a given list of amplitudes, returns the corresponding peak frequencies
for a given list of times (in seconds), returns the corresponding peak frequencies
"""
fft_list = []
times = []
current_time = offset
k = 0
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(offset+songlen), "-i", song_name, "crop.wav"])
subprocess.run(["ffmpeg", "-ss", str(0), "-t", str(max(np.array(times))), "-i", song_name, "crop.wav"], shell=False)
sample_rate, global_data = wavfile.read("crop.wav")
#blit = int(len(global_data) / len(data))
blit = int(sample_rate*step)
sample_rate, global_data = wavfile.read(song_name)
#blit = int(sample_rate*step)
subprocess.run(["clear"])
subprocess.run(["rm", "crop.wav"])
subprocess.run(["clear"], shell=False)
subprocess.run(["rm", "crop.wav"], shell=False)
pfreq = scipy.fft.rfftfreq(blit, 1/sample_rate)
pfreq = scipy.fft.rfftfreq(2*width, 1/sample_rate)
print("len : ", len(global_data))
print("len : ", len(data))
frequencies = [0 for s in range(len(data))]
frequencies = [0 for s in range(len(times))]
print(len(pfreq))
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)))])
mx = max(np.abs(pff))
for id in range(len(pff)):
if frequencies[s] == 0 and np.abs(pff[id]) == mx:
frequencies[s] = pfreq[id]
for s in range(len(times)):
left = max(0, int(times[s]*44100)-width)
right = min(len(global_data), int(times[s]*44100)+width)
pff = scipy.fft.rfft(global_data[left:right])
elif s != 0:
frequencies[s] = 0
#print(len(pff), len(pfreq))
mx = max(np.abs(pff))
for id in range(len(pff)):
if frequencies[s] == 0 and np.abs(pff[id]) == mx:
frequencies[s] = pfreq[id]
if(display):
plt.plot([offset+t/1000 for t in range(len(data))], frequencies)
plt.plot(times, frequencies)
plt.grid()
plt.xlabel("Time (s)")
plt.ylabel("Dominant frequency (Hz)")
@ -280,15 +166,15 @@ def void_freq(song_name, offset, songlen, increment, minfreq, maxfreq, upperthr,
current_time = offset
k = 0
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(songlen+offset), "-i", song_name, "crop.wav"])
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(songlen+offset), "-i", song_name, "crop.wav"], shell=False)
sample_rate, raw_global_data = wavfile.read("crop.wav")
blit = int(sample_rate*increment)
global_data = [0 for i in range(len(raw_global_data))]
subprocess.run(["clear"])
subprocess.run(["rm", "crop.wav"])
#subprocess.run(["clear"])
subprocess.run(["rm", "crop.wav"], shell=False)
a = 0
@ -373,29 +259,11 @@ def void_freq(song_name, offset, songlen, increment, minfreq, maxfreq, upperthr,
print("Done")
def get_tpts(data, sample_rate, thr):
res = []
for i in range(len(data)):
if(data[i] > thr):
res.append(i/sample_rate)
for i in res:
print(i)
return res
def test_sample(timelist):
for i in range(1,len(timelist)):
#os.system('play -n synth %s sin %s' % (0.05, 440))
for k in range(random.randint(1, 10)):
print("E", end="")
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):
"""
@ -406,108 +274,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 snap2(data, sample_rate, bpm, first_offset=0, div=4, show=False):
"""
data : list(int)
sample_rate : int
bpm = float
"""
song_len = int(len(data)/sample_rate)
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)
y = int(current_t*1000)
if(y < len(new)):
new[y] = max(new[y], reduced[j])
print("Snap done")
if(show):
t = [j/1000+first_offset for j in range(len(new))]
beats = [0 for j 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[int(1000*current_t)] = 16384
k += 1
current_t = first_offset + k*60/(bpm*div)
plt.plot(t, beats)
plt.plot(t, new)
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.
@ -518,7 +284,7 @@ def convert_to_wav(song_name:str, output_file="audio.wav") -> str:
match extension:
case ".mp3" | ".ogg":
print("Converting to .wav...")
subprocess.run(["ffmpeg", "-y", "-i", song_name, output_file])
subprocess.run(["ffmpeg", "-y", "-i", song_name, output_file], shell=False)
return output_file
return song_name
@ -543,21 +309,33 @@ 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)-4
n_iter = int((song_len-offset/1000)/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, 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=4, show=True)
#snapped_data = amplitude
#times in ms
(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)
frequencies = get_freq(filtered_name, times, display=True)
Path(f"{filename}_trimmed.wav").unlink()
return snapped_data, times, frequencies
'''
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 = 32)
aa, bb, cc = process_song("tetris_4.wav", 160, n_iter_2=48)
#print(data)
print("Program finished with return 0")
@ -676,13 +454,13 @@ def fct(song_name, offset, increment, songlen, maxfreq, display):
current_time = offset
k = 0
while(current_time <= songlen):
subprocess.run(["ffmpeg", "-ss", str(current_time), "-t", str(increment), "-i", song_name, "crop.wav"])
subprocess.run(["ffmpeg", "-ss", str(current_time), "-t", str(increment), "-i", song_name, "crop.wav"], shell=False)
sample_rate, audio_data = wavfile.read('crop.wav')
size = audio_data.size
#subprocess.run(["clear"])
subprocess.run(["rm", "crop.wav"])
subprocess.run(["rm", "crop.wav"], shell=False)
# do stuff here
#f, t, Zxx = signal.stft(audio_data, sample_rate, nperseg=1000)
@ -825,12 +603,12 @@ def extract_peaks_v2(song_data, sample_rate, offset, display, threshold, seglen)
return (t, song_data)
def peaks(song_name, offset, length, display, thr):
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(length), "-i", song_name, "crop.wav"])
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(length), "-i", song_name, "crop.wav"], shell=False)
sample_rate, audio_data = wavfile.read('crop.wav')
subprocess.run(["clear"])
subprocess.run(["rm", "crop.wav"])
#subprocess.run(["clear"])
subprocess.run(["rm", "crop.wav"], shell=False)
#return extract_peaks(audio_data, sample_rate, offset, display, thr)
return extract_peaks_v2(audio_data, sample_rate, offset, display, thr, 44100*2)
@ -965,4 +743,130 @@ def void_freq(song_name, offset, songlen, increment, lthr, gthr):
wavfile.write('test.wav', sample_rate, np.array(audio_signal, dtype=np.int16))
print("Done")
def find_bpm_2(sample_rate, data, threshold, maxbpm, show):
mx = np.max(data)
min_spacing = (60*sample_rate)/maxbpm
k = 0
while(k < len(data) and data[k]/mx < threshold):
k += 1
k += 1
spacing = []
current = 1
progress = 0
while(k < len(data)):
if(k%(len(data)/100) == 0):
print(progress, "%")
progress += 1
if(data[k]/mx >= threshold and current > min_spacing):
spacing.append(current)
current = 0
else:
current += 1
k += 1
for x in range(len(spacing)):
spacing[x] = 60/(spacing[x]/sample_rate)
digits = [i for i in range(len(spacing))]
if(show):
plt.plot(digits, spacing)
plt.xlabel("N")
plt.ylabel("BPM")
plt.grid()
plt.show()
beat = np.mean(spacing)
error = np.std(spacing)
return (np.round(beat, 3), np.round(error, 3))
def to_ms(song_data, sample_rate, offset):
# converts audio data to have exactly 1 sample per millisecond (aka set sample_rate to 1000)
new_data = []
spacing = int(sample_rate * 0.001)
mx = max(song_data)
i = 0
while(i < len(song_data)):
avg = 0
for k in range(spacing):
if(i+spacing < len(song_data)):
avg += song_data[i+spacing]
avg = avg / spacing
new_data.append(avg)
i += spacing
if(False): # pls dont kill me thx
t = [offset + j/1000 for j in range(len(new_data))]
plt.plot(t, new_data)
plt.xlabel("Time")
plt.ylabel("Amplitude")
plt.grid()
plt.show()
return (new_data, len(new_data))
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
subprocess.run(["ffmpeg", "-ss", str(offset), "-t", str(length), "-i", song_name, "crop.wav"], shell=False)
sample_rate, song_data = wavfile.read('crop.wav')
subprocess.run(["clear"], shell=False)
subprocess.run(["rm", "crop.wav"], shell=False)
if(reduce):
(song_data,e) = to_ms(song_data, 44100, 1)
sample_rate = 1000
mx = max(song_data)
is_locked = [False for i in range(len(song_data))]
x = int((len(song_data)*threshold)//100)
#print("X = ", x)
print("Retreiving the", int(x), "/", len(song_data), "highest values")
elements = heapq.nlargest(int(x), enumerate(song_data), key=lambda x: x[1])
print("Done")
for idx in range(len(elements)):
is_locked[elements[idx][0]] = True
for r in range(len(song_data)):
if(is_locked[r] == False):
song_data[r] = 0
if(show):
#print("EEEEE")
t = [offset + j/sample_rate for j in range(len(song_data))]
plt.plot(t, song_data)
plt.xlabel("Time")
plt.ylabel("Amplitude")
plt.grid()
plt.show()
return song_data
def get_tpts(data, sample_rate, thr):
res = []
for i in range(len(data)):
if(data[i] > thr):
res.append(i/sample_rate)
for i in res:
print(i)
return res
def test_sample(timelist):
for i in range(1,len(timelist)):
#os.system('play -n synth %s sin %s' % (0.05, 440))
for k in range(random.randint(1, 10)):
print("E", end="")
print("F")
sleep(timelist[i]-timelist[i-1])
'''