diff --git a/utils/fft_analyser.py b/utils/fft_analyser.py index 887d70a..a8359ca 100644 --- a/utils/fft_analyser.py +++ b/utils/fft_analyser.py @@ -74,15 +74,20 @@ class FFTAnalyser(QtCore.QThread): # np.argmax(fourier) = 2374 # freq[2374] * .05 * self.song.frame_rate = 520 :O omg! thats the hz value # x values = freq * self.song.frame_rate * self.sampling_window_length - # print(freq * self.song.frame_rate * .05) point_range = 1 / self.resolution # Logarithmic frequency scaling min_freq = np.min(freq[freq > 0]) # minimum positive frequency + # print( + # f"min freq: {min_freq * self.sampling_window_length * self.song.frame_rate}" + # ) # 20hz max_freq = np.max(freq) # maximum frequency - # 20khz + # print( + # f"max freq: {max_freq * self.sampling_window_length * self.song.frame_rate}" + # ) + # 23khz log_freqs = np.logspace(np.log10(min_freq), np.log10(max_freq), self.resolution) point_samples = [] diff --git a/utils/fft_analyser.py.dumb b/utils/fft_analyser.py.dumb deleted file mode 100644 index 95ca2ce..0000000 --- a/utils/fft_analyser.py.dumb +++ /dev/null @@ -1,168 +0,0 @@ -# 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.""" - - calculated_visual = 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.09 - self.visual_delta_threshold = 1000 - self.sensitivity = 10 - - 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) - v_sample = self.samples[ - start_index : start_index + sample_count - ] # samples to analyse - - - # use FFTs to analyse frequency and amplitudes - fourier = np.fft.fft(v_sample) - freq = np.fft.fftfreq(fourier.size, d=self.sampling_window_length) - amps = 2 / v_sample.size * np.abs(fourier) - data = np.array([freq, amps]).T - # print(freq * .05 * self.song.frame_rate) - - # NOTE: - # given 520 hz sine wave - # np.argmax(fourier) = 2374 - # freq[2374] * .05 * self.song.frame_rate = 520 :O omg! thats the hz value - # x values = freq * self.song.frame_rate * self.sampling_window_length - # print(freq * self.song.frame_rate * .05) - - freq_log_intervals = [ - (1,100), - (100,1000), - (1000, 10000), - (10000,22000) - ] - - all_log_freqs = [] - - for low, high in freq_log_intervals: - # generate logarithmic frequencies for each range - log_freqs = np.logspace(np.log10(low + 1e-6), np.log10(high), self.resolution) - all_log_freqs.extend(log_freqs) - - log_freqs = np.array(all_log_freqs) - - # point_range = 1 / self.resolution - - # Logarithmic frequency scaling - # min_freq = np.min(freq[freq > 0]) # minimum positive frequency - # 20hz - # print('min') - # print(min_freq * .05 * self.song.frame_rate) - # max_freq = np.max(freq) # maximum frequency - # 20khz - # print('max') - # print(max_freq * .05 * self.song.frame_rate) - # log_freqs = np.logspace(np.log10(min_freq), np.log10(max_freq), self.resolution) - - if len(self.points) != len(log_freqs): - self.points = np.zeros(len(log_freqs)) - - point_samples = [] - - if not data.size: - return - - #for i, freq in enumerate(np.arange(0, 1, point_range), start=1): - for i, log_freq in enumerate(log_freqs): - # get the amps which are in between the frequency range - #amps = data[(freq - point_range < data[:, 0]) & (data[:, 0] < freq)] - freq_range = log_freq * 0.01 - amps = data[(log_freq - freq_range < data[:, 0]) & (data[:, 0] < log_freq + freq_range)] - if not amps.size: - point_samples.append(0) - else: - point_samples.append( - amps.max() - * ( - (1 + self.sensitivity / 10 + (self.sensitivity - 1) / 10) - ** (i / 50) - ) - ) - - # Add the point_samples to the self.points array, the reason we have a separate - # array (self.bars) is so that we can fade out the previous amplitudes from - # the past - for n, amp in enumerate(point_samples): - amp *= 2 - - if ( - self.points[n] > 0 - and amp < self.points[n] - or self.player.state() - in (self.player.PausedState, self.player.StoppedState) - ): - self.points[n] -= self.points[n] / 10 # fade out - elif abs(self.points[n] - amp) > self.visual_delta_threshold: - self.points[n] = amp - if self.points[n] < 1: - self.points[n] = 1e-5 - - # interpolate points - rs = gaussian_filter1d(self.points, sigma=2) - - # Mirror the amplitudes, these are renamed to 'rs' because we are using them - # for polar plotting, which is plotted in terms of r and theta - # rs = np.concatenate((rs, np.flip(rs))) - # rs = np.concatenate((rs, np.flip(rs))) - - # they are divided by the highest sample in the song to normalise the - # amps in terms of decimals from 0 -> 1 - self.calculated_visual.emit(rs / self.max_sample) - # print(rs/self.max_sample) - - def run(self): - """Runs the animate function depending on the song.""" - while True: - if self.start_animate: - try: - self.calculate_amps() - except ValueError: - self.calculated_visual.emit(np.zeros(self.resolution)) - self.start_animate = False - time.sleep(0.025) diff --git a/utils/fft_analyzer.py.ai b/utils/fft_analyzer.ai.py similarity index 100% rename from utils/fft_analyzer.py.ai rename to utils/fft_analyzer.ai.py