idk whats going on man
This commit is contained in:
parent
8b4c1d0870
commit
87137bcd36
BIN
sines up.mp3
Normal file
BIN
sines up.mp3
Normal file
Binary file not shown.
@ -27,7 +27,7 @@ class FFTAnalyser(QtCore.QThread):
|
||||
# in this case, it takes 5% of the samples at some point in time
|
||||
self.sampling_window_length = 0.05
|
||||
self.visual_delta_threshold = 1000
|
||||
self.sensitivity = 10
|
||||
self.sensitivity = 1
|
||||
|
||||
def reset_media(self):
|
||||
"""Resets the media to the currently playing song."""
|
||||
@ -109,6 +109,7 @@ class FFTAnalyser(QtCore.QThread):
|
||||
# array (self.points) is so that we can fade out the previous amplitudes from
|
||||
# the past
|
||||
for n, amp in enumerate(point_samples):
|
||||
amp *= 2
|
||||
if self.player.state() in (
|
||||
self.player.PausedState,
|
||||
self.player.StoppedState,
|
||||
@ -121,12 +122,12 @@ class FFTAnalyser(QtCore.QThread):
|
||||
else:
|
||||
# Rise quickly to new peaks
|
||||
self.points[n] = amp
|
||||
|
||||
# print(f'amp > points[n] - {amp} > {self.points[n]}')
|
||||
# Set a lower threshold to properly reach zero
|
||||
if self.points[n] < 1e-2:
|
||||
self.points[n] = 0
|
||||
if self.points[n] < 1:
|
||||
self.points[n] = 1e-5
|
||||
|
||||
print(self.points)
|
||||
# print(self.points)
|
||||
# interpolate points
|
||||
rs = gaussian_filter1d(self.points, sigma=2)
|
||||
|
||||
|
||||
117
utils/fft_analyzer.py.ai
Normal file
117
utils/fft_analyzer.py.ai
Normal file
@ -0,0 +1,117 @@
|
||||
# Credit
|
||||
# https://github.com/ravenkls/MilkPlayer/blob/master/audio/fft_analyser.py
|
||||
|
||||
import time
|
||||
from PyQt5 import QtCore
|
||||
from pydub import AudioSegment
|
||||
import numpy as np
|
||||
from scipy.ndimage.filters import gaussian_filter1d
|
||||
from logging import debug, info
|
||||
|
||||
|
||||
class FFTAnalyser(QtCore.QThread):
|
||||
"""Analyses a song using FFTs."""
|
||||
|
||||
calculatedVisual = QtCore.pyqtSignal(np.ndarray)
|
||||
calculatedVisualRs = QtCore.pyqtSignal(np.ndarray)
|
||||
|
||||
def __init__(self, player, x_resolution): # noqa: F821
|
||||
super().__init__()
|
||||
self.player = player
|
||||
self.reset_media()
|
||||
self.player.currentMediaChanged.connect(self.reset_media)
|
||||
|
||||
self.resolution = x_resolution
|
||||
# this length is a number, in seconds, of how much audio is sampled to determine the frequencies
|
||||
# of the audio at a specific point in time
|
||||
# in this case, it takes 5% of the samples at some point in time
|
||||
self.sampling_window_length = 0.05
|
||||
self.visual_delta_threshold = 1000
|
||||
self.sensitivity = 0.2
|
||||
|
||||
def reset_media(self):
|
||||
"""Resets the media to the currently playing song."""
|
||||
audio_file = self.player.currentMedia().canonicalUrl().path()
|
||||
# if os.name == "nt" and audio_file.startswith("/"):
|
||||
# audio_file = audio_file[1:]
|
||||
if audio_file:
|
||||
try:
|
||||
self.song = AudioSegment.from_file(audio_file).set_channels(1)
|
||||
except PermissionError:
|
||||
self.start_animate = False
|
||||
else:
|
||||
self.samples = np.array(self.song.get_array_of_samples())
|
||||
|
||||
self.max_sample = self.samples.max()
|
||||
self.points = np.zeros(self.resolution)
|
||||
self.start_animate = True
|
||||
else:
|
||||
self.start_animate = False
|
||||
|
||||
def calculate_amps(self):
|
||||
"""Calculates the amplitudes used for visualising the media."""
|
||||
|
||||
sample_count = int(self.song.frame_rate * self.sampling_window_length)
|
||||
start_index = int((self.player.position() / 1000) * self.song.frame_rate)
|
||||
# samples to analyse
|
||||
v_sample = self.samples[start_index : start_index + sample_count]
|
||||
|
||||
# Use a window function to reduce spectral leakage
|
||||
window = np.hanning(len(v_sample))
|
||||
v_sample = v_sample * window
|
||||
|
||||
# use FFTs to analyse frequency and amplitudes
|
||||
fourier = np.fft.fft(v_sample)
|
||||
freq = np.fft.fftfreq(fourier.size, d=1/self.song.frame_rate)
|
||||
amps = np.abs(fourier)[:len(fourier)//2] # Only take positive frequencies
|
||||
freq = freq[:len(fourier)//2] # Match frequencies to amplitudes
|
||||
|
||||
# Define frequency bands (in Hz)
|
||||
bands = np.logspace(np.log10(10), np.log10(23000), self.resolution + 1)
|
||||
point_samples = np.zeros(self.resolution)
|
||||
|
||||
# Calculate average amplitude for each frequency band
|
||||
for i in range(len(bands) - 1):
|
||||
mask = (freq >= bands[i]) & (freq < bands[i+1])
|
||||
if np.any(mask):
|
||||
point_samples[i] = np.mean(amps[mask])
|
||||
|
||||
# Calculate RMS of the sample for dynamic sensitivity
|
||||
rms = np.sqrt(np.mean(np.square(v_sample)))
|
||||
rms_ratio = min(0.2, rms / (0.01 * self.max_sample)) # Smooth transition near silence
|
||||
|
||||
# Normalize and apply sensitivity with RMS-based scaling
|
||||
if np.max(point_samples) > 0:
|
||||
point_samples = point_samples / np.max(point_samples)
|
||||
point_samples = point_samples * self.sensitivity * rms_ratio
|
||||
else:
|
||||
point_samples = np.zeros(self.resolution)
|
||||
|
||||
# Update visualization points with decay
|
||||
for n in range(self.resolution):
|
||||
amp = point_samples[n]
|
||||
if self.player.state() in (self.player.PausedState, self.player.StoppedState):
|
||||
self.points[n] *= 0.95 # Fast decay when paused/stopped
|
||||
elif amp < self.points[n]:
|
||||
# More aggressive decay for very quiet signals
|
||||
decay_factor = 0.7 if rms_ratio < 0.1 else 0.9
|
||||
self.points[n] = max(amp, self.points[n] * decay_factor)
|
||||
else:
|
||||
self.points[n] = amp
|
||||
|
||||
# Apply Gaussian smoothing
|
||||
rs = gaussian_filter1d(self.points, sigma=1)
|
||||
|
||||
# Emit the smoothed data
|
||||
self.calculatedVisual.emit(rs)
|
||||
|
||||
def run(self):
|
||||
"""Runs the animate function depending on the song."""
|
||||
while True:
|
||||
if self.start_animate:
|
||||
try:
|
||||
self.calculate_amps()
|
||||
except ValueError:
|
||||
self.calculatedVisual.emit(np.zeros(self.resolution))
|
||||
self.start_animate = False
|
||||
time.sleep(0.033)
|
||||
Loading…
x
Reference in New Issue
Block a user