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)