From 50c54c709ebfb6732606a651a9cef4e5d653dad6 Mon Sep 17 00:00:00 2001 From: billypom on debian Date: Mon, 31 Mar 2025 00:19:18 -0400 Subject: [PATCH] fuck u sonnet --- components/AlbumArtGraphicsView.py | 4 ++ components/AudioVisualizer.py | 52 ++++++++++++++++++++++++ main.py | 64 ++++++++++++++++++------------ ui.py | 16 ++++++++ ui.ui | 45 ++++++++++++++++++++- utils/fft_analyser.py | 7 +++- 6 files changed, 159 insertions(+), 29 deletions(-) diff --git a/components/AlbumArtGraphicsView.py b/components/AlbumArtGraphicsView.py index 280ef82..120543a 100644 --- a/components/AlbumArtGraphicsView.py +++ b/components/AlbumArtGraphicsView.py @@ -1,6 +1,7 @@ import os import tempfile from logging import debug +from PyQt5 import QtWidgets from PyQt5.QtWidgets import ( QAbstractScrollArea, QGraphicsPixmapItem, @@ -32,6 +33,9 @@ class AlbumArtGraphicsView(QGraphicsView): self.setAcceptDrops(True) self.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) self.setMinimumSize(200, 200) + self.setMaximumSize(200, 200) # Also set maximum size to maintain square shape + # self.setSizePolicy(Qt.QSizePolicy.Fixed, Qt.QSizePolicy.Fixed) # Keep fixed size + self.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) self.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self.setSizeAdjustPolicy(QAbstractScrollArea.SizeAdjustPolicy.AdjustIgnored) diff --git a/components/AudioVisualizer.py b/components/AudioVisualizer.py index 5110ace..a874fd6 100644 --- a/components/AudioVisualizer.py +++ b/components/AudioVisualizer.py @@ -23,9 +23,61 @@ class AudioVisualizer(QtWidgets.QWidget): self.amps = np.array([]) self._plot_item = None self._x_data = np.arange(self.x_resolution) + self.use_decibels = True # Set to True to use decibel scale + + # Generate logarithmic frequency scale (20Hz - 20kHz) + self.min_freq = 20 + self.max_freq = 23000 + self.frequency_values = np.logspace(np.log10(self.min_freq), np.log10(self.max_freq), self.x_resolution) + + def get_frequency_ticks(self, num_ticks=10): + """Returns frequency ticks for x-axis display + + Args: + num_ticks (int): Approximate number of ticks to display + + Returns: + list: List of tuples with (position, label) for each tick + """ + # Standard frequency bands for audio visualization + standard_freqs = [20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000] + + # Map frequencies to x-axis positions + ticks = [] + for freq in standard_freqs: + # Find closest index to this frequency + idx = np.argmin(np.abs(self.frequency_values - freq)) + # Format labels: Hz for <1000, kHz for >=1000 + if freq < 1000: + label = f"{freq}Hz" + else: + label = f"{freq/1000:.0f}kHz" + ticks.append((idx, label)) + + return ticks + + def get_frequencies(self): + """Return the frequency values for x-axis""" + return self.frequency_values def get_amplitudes(self): return self.amps + def get_decibels(self): + """Convert amplitude values to decibel scale + + Formula: dB = 20 * log10(amplitude) + For normalized amplitude values, this gives a range of approx -96dB to 0dB + With a noise floor cutoff at around -96dB (for very small values) + """ + # Avoid log(0) by adding a small epsilon + epsilon = 1e-10 + amplitudes = np.maximum(self.amps, epsilon) + # Convert to decibels (20*log10 is the standard formula for amplitude to dB) + db_values = 20 * np.log10(amplitudes) + # Clip very low values to have a reasonable floor (e.g. -96dB) + db_values = np.maximum(db_values, -96) + return db_values + def set_amplitudes(self, amps): self.amps = np.array(amps) diff --git a/main.py b/main.py index 995a6a5..6856763 100644 --- a/main.py +++ b/main.py @@ -182,25 +182,32 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow): self.timer.start(100) self.timer.timeout.connect(self.move_slider) + # Set fixed size for album art + self.albumGraphicsView.setFixedSize(250, 250) + + # Make sure PlotWidget doesn't exceed album art height + self.PlotWidget.setFixedHeight(225) # Adjust to leave room for playback controls + # Graphics plot self.PlotWidget.setXRange(0, self.analyzer_x_resolution, padding=0) # x axis range - self.PlotWidget.setYRange(0, 1, padding=0) # y axis range - self.PlotWidget.setLogMode(False, False) + self.PlotWidget.setYRange(-96, 0, padding=0) # y axis range for decibels (-96dB to 0dB) + self.PlotWidget.setLogMode(x=False, y=False) # Logarithmic x-axis for frequency display self.PlotWidget.setMouseEnabled(x=False, y=False) # Performance optimizations self.PlotWidget.setAntialiasing(False) self.PlotWidget.setDownsampling(auto=True, mode='peak') self.PlotWidget.setClipToView(True) - # Remove x-axis ticks - # ticks = ['20', '31.25', '62.5', '125', '250', '500', '1000', '2000', '4000', '10000', '20000'] - # self.PlotWidget.getAxis("bottom").setTicks([]) - # self.PlotWidget.getAxis("bottom").setLabel("") # Remove x-axis label - # ticks = nparray([0, 31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000, 15000, 20000]) - # Remove y-axis labels and decorations - # self.PlotWidget.getAxis("left").setTicks([[(str(tick), tick) for tick in ticks]]) - # self.PlotWidget.getAxis("left").setTicks([]) - # self.PlotWidget.getAxis("left").setLabel("") # Remove y-axis label + # Add tick marks for common decibel values (expanded range) + y_ticks = [(-84, '-84dB'), (-60, '-60dB'), + (-36, '-36dB'), (-12, '-12dB'), (0, '0dB')] + self.PlotWidget.getAxis('left').setTicks([y_ticks]) + + # Add frequency ticks on x-axis + freq_ticks = self.audio_visualizer.get_frequency_ticks() + self.PlotWidget.getAxis('bottom').setTicks([freq_ticks]) + + # Display grid for better readability self.PlotWidget.showGrid(x=True, y=True) # Connections @@ -424,22 +431,29 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow): self.albumGraphicsView.load_album_art(album_art_data) def update_audio_visualization(self) -> None: - """Handles upading points on the pyqtgraph visual""" - y = self.audio_visualizer.get_amplitudes() + """Handles updating points on the pyqtgraph visual""" + if self.audio_visualizer.use_decibels: + # Use decibel values instead of raw amplitudes + y = self.audio_visualizer.get_decibels() + else: + y = self.audio_visualizer.get_amplitudes() + if len(y) == 0: return - - if self.audio_visualizer._plot_item is None: - self.PlotWidget.clear() - self.audio_visualizer._plot_item = self.PlotWidget.plot( - self.audio_visualizer._x_data, - y, - pen='b', # Use pen instead of fill for better performance - fillLevel=0, - fillBrush=mkBrush("b") - ) - else: - self.audio_visualizer._plot_item.setData(self.audio_visualizer._x_data, y) + + # if self.audio_visualizer._plot_item is None: + # thanks cursor sonnet whatever + self.PlotWidget.clear() + # Use the actual frequency values for x-axis + self.audio_visualizer._plot_item = self.PlotWidget.plot( + self.audio_visualizer._x_data, # We'll keep using indices for drawing + y, + pen='b', # Use pen instead of fill for better performance + fillLevel=-96 if self.audio_visualizer.use_decibels else 0, # Fill from -96dB for decibel scale + fillBrush=mkBrush("b") + ) + # else: + # self.audio_visualizer._plot_item.setData(self.audio_visualizer._x_data, y) def clear_audio_visualization(self) -> None: self.PlotWidget.clear() diff --git a/ui.py b/ui.py index 32e5f8d..935e09f 100644 --- a/ui.py +++ b/ui.py @@ -25,11 +25,19 @@ class Ui_MainWindow(object): self.verticalLayout.setSpacing(6) self.verticalLayout.setObjectName("verticalLayout") self.hLayoutHead = QtWidgets.QHBoxLayout() + self.hLayoutHead.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) self.hLayoutHead.setObjectName("hLayoutHead") self.vlayoutAlbumArt = QtWidgets.QVBoxLayout() self.vlayoutAlbumArt.setSizeConstraint(QtWidgets.QLayout.SetFixedSize) self.vlayoutAlbumArt.setObjectName("vlayoutAlbumArt") self.albumGraphicsView = AlbumArtGraphicsView(self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.albumGraphicsView.sizePolicy().hasHeightForWidth()) + self.albumGraphicsView.setSizePolicy(sizePolicy) + self.albumGraphicsView.setMinimumSize(QtCore.QSize(200, 200)) + self.albumGraphicsView.setMaximumSize(QtCore.QSize(200, 200)) self.albumGraphicsView.setObjectName("albumGraphicsView") self.vlayoutAlbumArt.addWidget(self.albumGraphicsView) self.hLayoutHead.addLayout(self.vlayoutAlbumArt) @@ -37,8 +45,10 @@ class Ui_MainWindow(object): self.vLayoutSongDetails.setObjectName("vLayoutSongDetails") self.hLayoutHead.addLayout(self.vLayoutSongDetails) self.vLayoutPlaybackVisuals = QtWidgets.QVBoxLayout() + self.vLayoutPlaybackVisuals.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize) self.vLayoutPlaybackVisuals.setObjectName("vLayoutPlaybackVisuals") self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize) self.horizontalLayout.setObjectName("horizontalLayout") self.playbackSlider = QtWidgets.QSlider(self.centralwidget) self.playbackSlider.setOrientation(QtCore.Qt.Horizontal) @@ -91,6 +101,12 @@ class Ui_MainWindow(object): self.horizontalLayout.setStretch(3, 1) self.vLayoutPlaybackVisuals.addLayout(self.horizontalLayout) self.PlotWidget = PlotWidget(self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.PlotWidget.sizePolicy().hasHeightForWidth()) + self.PlotWidget.setSizePolicy(sizePolicy) + self.PlotWidget.setMaximumSize(QtCore.QSize(16777215, 150)) self.PlotWidget.setObjectName("PlotWidget") self.vLayoutPlaybackVisuals.addWidget(self.PlotWidget) self.hLayoutHead.addLayout(self.vLayoutPlaybackVisuals) diff --git a/ui.ui b/ui.ui index 7c9c72a..e665c9c 100644 --- a/ui.ui +++ b/ui.ui @@ -28,13 +28,35 @@ + + QLayout::SetFixedSize + QLayout::SetFixedSize - + + + + 0 + 0 + + + + + 200 + 200 + + + + + 200 + 200 + + + @@ -43,8 +65,14 @@ + + QLayout::SetMaximumSize + + + QLayout::SetMaximumSize + @@ -137,7 +165,20 @@ - + + + + 0 + 0 + + + + + 16777215 + 150 + + + diff --git a/utils/fft_analyser.py b/utils/fft_analyser.py index 6d63a01..b8d8344 100644 --- a/utils/fft_analyser.py +++ b/utils/fft_analyser.py @@ -56,6 +56,9 @@ class FFTAnalyser(QtCore.QThread): start_index : start_index + sample_count ] # samples to analyse + # 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) @@ -117,11 +120,11 @@ class FFTAnalyser(QtCore.QThread): or self.player.state() in (self.player.PausedState, self.player.StoppedState) ): - self.points[n] -= self.points[n] / 10 # fade out + self.points[n] -= self.points[n] / 5 # 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 + self.points[n] = 1e-12 # interpolate points rs = gaussian_filter1d(self.points, sigma=1.5)