broke everything changing layout
This commit is contained in:
parent
ba5a015495
commit
019bf5c8e7
@ -4,6 +4,7 @@ from PyQt5.QtWidgets import (
|
|||||||
QVBoxLayout,
|
QVBoxLayout,
|
||||||
)
|
)
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
|
from logging import debug
|
||||||
|
|
||||||
|
|
||||||
class DebugWindow(QDialog):
|
class DebugWindow(QDialog):
|
||||||
@ -16,6 +17,7 @@ class DebugWindow(QDialog):
|
|||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
|
|
||||||
# Labels & input fields
|
# Labels & input fields
|
||||||
|
# debug(pformat(self.text))
|
||||||
self.input_field = QPlainTextEdit(pformat(self.text))
|
self.input_field = QPlainTextEdit(pformat(self.text))
|
||||||
layout.addWidget(self.input_field)
|
layout.addWidget(self.input_field)
|
||||||
|
|
||||||
|
|||||||
@ -148,8 +148,8 @@ class MusicTable(QTableView):
|
|||||||
|
|
||||||
# CONNECTIONS
|
# CONNECTIONS
|
||||||
self.clicked.connect(self.set_selected_song_filepath)
|
self.clicked.connect(self.set_selected_song_filepath)
|
||||||
self.doubleClicked.connect(self.set_current_song_filepath)
|
# self.doubleClicked.connect(self.set_current_song_filepath)
|
||||||
self.enterKey.connect(self.set_current_song_filepath)
|
# self.enterKey.connect(self.set_current_song_filepath)
|
||||||
self.deleteKey.connect(self.delete_songs)
|
self.deleteKey.connect(self.delete_songs)
|
||||||
self.model2.dataChanged.connect(self.on_cell_data_changed) # editing cells
|
self.model2.dataChanged.connect(self.on_cell_data_changed) # editing cells
|
||||||
self.model2.layoutChanged.connect(self.restore_scroll_position)
|
self.model2.layoutChanged.connect(self.restore_scroll_position)
|
||||||
@ -465,6 +465,7 @@ class MusicTable(QTableView):
|
|||||||
)
|
)
|
||||||
if new_index.isValid():
|
if new_index.isValid():
|
||||||
self.setCurrentIndex(new_index)
|
self.setCurrentIndex(new_index)
|
||||||
|
super().keyPressEvent(e)
|
||||||
elif key == Qt.Key.Key_Down: # Arrow key navigation
|
elif key == Qt.Key.Key_Down: # Arrow key navigation
|
||||||
current_index = self.currentIndex()
|
current_index = self.currentIndex()
|
||||||
new_index = self.model2.index(
|
new_index = self.model2.index(
|
||||||
@ -472,6 +473,7 @@ class MusicTable(QTableView):
|
|||||||
)
|
)
|
||||||
if new_index.isValid():
|
if new_index.isValid():
|
||||||
self.setCurrentIndex(new_index)
|
self.setCurrentIndex(new_index)
|
||||||
|
super().keyPressEvent(e)
|
||||||
elif key in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
|
elif key in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
|
||||||
if self.state() != QAbstractItemView.EditingState:
|
if self.state() != QAbstractItemView.EditingState:
|
||||||
self.enterKey.emit() # Enter key detected
|
self.enterKey.emit() # Enter key detected
|
||||||
@ -737,7 +739,7 @@ class MusicTable(QTableView):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def set_current_song_filepath(self) -> None:
|
def set_current_song_filepath(self) -> None:
|
||||||
"""Sets the filepath of the currently playing song"""
|
"""Sets the current song filepath to the value in column 'path' with current selected row index"""
|
||||||
# NOTE:
|
# NOTE:
|
||||||
# Setting the current song filepath automatically plays that song
|
# Setting the current song filepath automatically plays that song
|
||||||
# self.tableView listens to this function and plays the audio file located at self.current_song_filepath
|
# self.tableView listens to this function and plays the audio file located at self.current_song_filepath
|
||||||
|
|||||||
26
main.py
26
main.py
@ -12,6 +12,7 @@ from mutagen.id3._frames import APIC
|
|||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from appdirs import user_config_dir
|
from appdirs import user_config_dir
|
||||||
|
from numpy import array as nparray
|
||||||
from logging import debug, error, warning, basicConfig, INFO, DEBUG
|
from logging import debug, error, warning, basicConfig, INFO, DEBUG
|
||||||
from ui import Ui_MainWindow
|
from ui import Ui_MainWindow
|
||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
@ -148,6 +149,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
# UI
|
# UI
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.setWindowTitle("musicpom")
|
self.setWindowTitle("musicpom")
|
||||||
|
# self.vLayoutAlbumArt.SetFixedSize()
|
||||||
self.status_bar = QStatusBar()
|
self.status_bar = QStatusBar()
|
||||||
self.permanent_status_label = QLabel("Status...")
|
self.permanent_status_label = QLabel("Status...")
|
||||||
self.status_bar.addPermanentWidget(self.permanent_status_label)
|
self.status_bar.addPermanentWidget(self.permanent_status_label)
|
||||||
@ -160,7 +162,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.config.read(self.cfg_file)
|
self.config.read(self.cfg_file)
|
||||||
self.player: QMediaPlayer = QMediaPlayer() # Audio player object
|
self.player: QMediaPlayer = QMediaPlayer() # Audio player object
|
||||||
self.probe: QAudioProbe = QAudioProbe() # Gets audio data
|
self.probe: QAudioProbe = QAudioProbe() # Gets audio data
|
||||||
self.analyzer_x_resolution = 100
|
self.analyzer_x_resolution = 200
|
||||||
self.audio_visualizer: AudioVisualizer = AudioVisualizer(self.player, self.analyzer_x_resolution)
|
self.audio_visualizer: AudioVisualizer = AudioVisualizer(self.player, self.analyzer_x_resolution)
|
||||||
self.timer = QTimer(self) # Audio timing things
|
self.timer = QTimer(self) # Audio timing things
|
||||||
self.clipboard = clipboard
|
self.clipboard = clipboard
|
||||||
@ -186,14 +188,16 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.PlotWidget.setLogMode(False, False)
|
self.PlotWidget.setLogMode(False, False)
|
||||||
self.PlotWidget.setMouseEnabled(x=False, y=False)
|
self.PlotWidget.setMouseEnabled(x=False, y=False)
|
||||||
# Remove x-axis ticks
|
# Remove x-axis ticks
|
||||||
ticks = ['20', '31.25', '62.5', '125', '250', '500', '1000', '2000', '4000', '10000', '20000']
|
# ticks = ['20', '31.25', '62.5', '125', '250', '500', '1000', '2000', '4000', '10000', '20000']
|
||||||
self.PlotWidget.getAxis("bottom").setTicks([])
|
# self.PlotWidget.getAxis("bottom").setTicks([])
|
||||||
self.PlotWidget.getAxis("bottom").setLabel("") # Remove x-axis label
|
# self.PlotWidget.getAxis("bottom").setLabel("") # Remove x-axis label
|
||||||
# x = nparray([0, 31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000, 15000, 20000])
|
# ticks = nparray([0, 31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000, 15000, 20000])
|
||||||
|
|
||||||
# Remove y-axis labels and decorations
|
# Remove y-axis labels and decorations
|
||||||
# self.PlotWidget.getAxis("left").setTicks([[(str(tick), tick) for tick in ticks]])
|
# self.PlotWidget.getAxis("left").setTicks([[(str(tick), tick) for tick in ticks]])
|
||||||
self.PlotWidget.getAxis("left").setTicks([])
|
# self.PlotWidget.getAxis("left").setTicks([])
|
||||||
self.PlotWidget.getAxis("left").setLabel("") # Remove y-axis label
|
# self.PlotWidget.getAxis("left").setLabel("") # Remove y-axis label
|
||||||
|
self.PlotWidget.showGrid(x=True, y=True)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.playbackSlider.sliderReleased.connect(
|
self.playbackSlider.sliderReleased.connect(
|
||||||
@ -306,7 +310,10 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.permanent_status_label.setText(message)
|
self.permanent_status_label.setText(message)
|
||||||
|
|
||||||
def play_audio_file(self) -> None:
|
def play_audio_file(self) -> None:
|
||||||
"""Start playback of `tableView.current_song_filepath` & moves playback slider"""
|
"""
|
||||||
|
Start playback of `tableView.current_song_filepath` & moves playback slider
|
||||||
|
"""
|
||||||
|
self.tableView.set_current_song_filepath()
|
||||||
# get metadata
|
# get metadata
|
||||||
self.current_song_metadata = self.tableView.get_current_song_metadata()
|
self.current_song_metadata = self.tableView.get_current_song_metadata()
|
||||||
# read the file
|
# read the file
|
||||||
@ -333,6 +340,9 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
)
|
)
|
||||||
title = self.current_song_metadata["TIT2"][0]
|
title = self.current_song_metadata["TIT2"][0]
|
||||||
|
|
||||||
|
debug(artist)
|
||||||
|
debug(title)
|
||||||
|
debug(album)
|
||||||
self.artistLabel.setText(artist)
|
self.artistLabel.setText(artist)
|
||||||
self.albumLabel.setText(album)
|
self.albumLabel.setText(album)
|
||||||
self.titleLabel.setText(title)
|
self.titleLabel.setText(title)
|
||||||
|
|||||||
68
ui.py
68
ui.py
@ -35,29 +35,6 @@ class Ui_MainWindow(object):
|
|||||||
self.hLayoutHead.addLayout(self.vlayoutAlbumArt)
|
self.hLayoutHead.addLayout(self.vlayoutAlbumArt)
|
||||||
self.vLayoutSongDetails = QtWidgets.QVBoxLayout()
|
self.vLayoutSongDetails = QtWidgets.QVBoxLayout()
|
||||||
self.vLayoutSongDetails.setObjectName("vLayoutSongDetails")
|
self.vLayoutSongDetails.setObjectName("vLayoutSongDetails")
|
||||||
self.artistLabel = QtWidgets.QLabel(self.centralwidget)
|
|
||||||
font = QtGui.QFont()
|
|
||||||
font.setPointSize(24)
|
|
||||||
font.setBold(True)
|
|
||||||
font.setWeight(75)
|
|
||||||
self.artistLabel.setFont(font)
|
|
||||||
self.artistLabel.setObjectName("artistLabel")
|
|
||||||
self.vLayoutSongDetails.addWidget(self.artistLabel)
|
|
||||||
self.titleLabel = QtWidgets.QLabel(self.centralwidget)
|
|
||||||
font = QtGui.QFont()
|
|
||||||
font.setPointSize(18)
|
|
||||||
self.titleLabel.setFont(font)
|
|
||||||
self.titleLabel.setObjectName("titleLabel")
|
|
||||||
self.vLayoutSongDetails.addWidget(self.titleLabel)
|
|
||||||
self.albumLabel = QtWidgets.QLabel(self.centralwidget)
|
|
||||||
font = QtGui.QFont()
|
|
||||||
font.setPointSize(16)
|
|
||||||
font.setBold(False)
|
|
||||||
font.setItalic(True)
|
|
||||||
font.setWeight(50)
|
|
||||||
self.albumLabel.setFont(font)
|
|
||||||
self.albumLabel.setObjectName("albumLabel")
|
|
||||||
self.vLayoutSongDetails.addWidget(self.albumLabel)
|
|
||||||
self.hLayoutHead.addLayout(self.vLayoutSongDetails)
|
self.hLayoutHead.addLayout(self.vLayoutSongDetails)
|
||||||
self.vLayoutPlaybackVisuals = QtWidgets.QVBoxLayout()
|
self.vLayoutPlaybackVisuals = QtWidgets.QVBoxLayout()
|
||||||
self.vLayoutPlaybackVisuals.setObjectName("vLayoutPlaybackVisuals")
|
self.vLayoutPlaybackVisuals.setObjectName("vLayoutPlaybackVisuals")
|
||||||
@ -118,7 +95,6 @@ class Ui_MainWindow(object):
|
|||||||
self.vLayoutPlaybackVisuals.addWidget(self.PlotWidget)
|
self.vLayoutPlaybackVisuals.addWidget(self.PlotWidget)
|
||||||
self.hLayoutHead.addLayout(self.vLayoutPlaybackVisuals)
|
self.hLayoutHead.addLayout(self.vLayoutPlaybackVisuals)
|
||||||
self.hLayoutHead.setStretch(0, 1)
|
self.hLayoutHead.setStretch(0, 1)
|
||||||
self.hLayoutHead.setStretch(1, 4)
|
|
||||||
self.hLayoutHead.setStretch(2, 6)
|
self.hLayoutHead.setStretch(2, 6)
|
||||||
self.verticalLayout.addLayout(self.hLayoutHead)
|
self.verticalLayout.addLayout(self.hLayoutHead)
|
||||||
self.hLayoutMusicTable = QtWidgets.QHBoxLayout()
|
self.hLayoutMusicTable = QtWidgets.QHBoxLayout()
|
||||||
@ -134,9 +110,6 @@ class Ui_MainWindow(object):
|
|||||||
self.hLayoutMusicTable.setStretch(0, 2)
|
self.hLayoutMusicTable.setStretch(0, 2)
|
||||||
self.hLayoutMusicTable.setStretch(1, 10)
|
self.hLayoutMusicTable.setStretch(1, 10)
|
||||||
self.verticalLayout.addLayout(self.hLayoutMusicTable)
|
self.verticalLayout.addLayout(self.hLayoutMusicTable)
|
||||||
self.verticalLayout.setStretch(0, 1)
|
|
||||||
self.verticalLayout.setStretch(1, 2)
|
|
||||||
self.verticalLayout_3.addLayout(self.verticalLayout)
|
|
||||||
self.hLayoutControls = QtWidgets.QHBoxLayout()
|
self.hLayoutControls = QtWidgets.QHBoxLayout()
|
||||||
self.hLayoutControls.setSpacing(6)
|
self.hLayoutControls.setSpacing(6)
|
||||||
self.hLayoutControls.setObjectName("hLayoutControls")
|
self.hLayoutControls.setObjectName("hLayoutControls")
|
||||||
@ -167,7 +140,7 @@ class Ui_MainWindow(object):
|
|||||||
self.hLayoutControls.setStretch(2, 2)
|
self.hLayoutControls.setStretch(2, 2)
|
||||||
self.hLayoutControls.setStretch(3, 2)
|
self.hLayoutControls.setStretch(3, 2)
|
||||||
self.hLayoutControls.setStretch(4, 1)
|
self.hLayoutControls.setStretch(4, 1)
|
||||||
self.verticalLayout_3.addLayout(self.hLayoutControls)
|
self.verticalLayout.addLayout(self.hLayoutControls)
|
||||||
self.hLayoutControls2 = QtWidgets.QHBoxLayout()
|
self.hLayoutControls2 = QtWidgets.QHBoxLayout()
|
||||||
self.hLayoutControls2.setSpacing(6)
|
self.hLayoutControls2.setSpacing(6)
|
||||||
self.hLayoutControls2.setObjectName("hLayoutControls2")
|
self.hLayoutControls2.setObjectName("hLayoutControls2")
|
||||||
@ -182,19 +155,46 @@ class Ui_MainWindow(object):
|
|||||||
self.volumeLabel = QtWidgets.QLabel(self.centralwidget)
|
self.volumeLabel = QtWidgets.QLabel(self.centralwidget)
|
||||||
self.volumeLabel.setObjectName("volumeLabel")
|
self.volumeLabel.setObjectName("volumeLabel")
|
||||||
self.hLayoutControls2.addWidget(self.volumeLabel)
|
self.hLayoutControls2.addWidget(self.volumeLabel)
|
||||||
|
self.hLayoutSongDetails = QtWidgets.QHBoxLayout()
|
||||||
|
self.hLayoutSongDetails.setObjectName("hLayoutSongDetails")
|
||||||
|
self.titleLabel = QtWidgets.QLabel(self.centralwidget)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setPointSize(18)
|
||||||
|
self.titleLabel.setFont(font)
|
||||||
|
self.titleLabel.setObjectName("titleLabel")
|
||||||
|
self.hLayoutSongDetails.addWidget(self.titleLabel)
|
||||||
|
self.artistLabel = QtWidgets.QLabel(self.centralwidget)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setPointSize(24)
|
||||||
|
font.setBold(True)
|
||||||
|
font.setWeight(75)
|
||||||
|
self.artistLabel.setFont(font)
|
||||||
|
self.artistLabel.setObjectName("artistLabel")
|
||||||
|
self.hLayoutSongDetails.addWidget(self.artistLabel)
|
||||||
|
self.albumLabel = QtWidgets.QLabel(self.centralwidget)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setPointSize(16)
|
||||||
|
font.setBold(False)
|
||||||
|
font.setItalic(True)
|
||||||
|
font.setWeight(50)
|
||||||
|
self.albumLabel.setFont(font)
|
||||||
|
self.albumLabel.setObjectName("albumLabel")
|
||||||
|
self.hLayoutSongDetails.addWidget(self.albumLabel)
|
||||||
|
self.hLayoutControls2.addLayout(self.hLayoutSongDetails)
|
||||||
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||||
self.hLayoutControls2.addItem(spacerItem2)
|
self.hLayoutControls2.addItem(spacerItem2)
|
||||||
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
|
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
|
||||||
self.pushButton.setObjectName("pushButton")
|
self.pushButton.setObjectName("pushButton")
|
||||||
self.hLayoutControls2.addWidget(self.pushButton)
|
self.hLayoutControls2.addWidget(self.pushButton)
|
||||||
self.hLayoutControls2.setStretch(0, 1)
|
self.hLayoutControls2.setStretch(0, 1)
|
||||||
self.hLayoutControls2.setStretch(2, 4)
|
self.hLayoutControls2.setStretch(3, 4)
|
||||||
self.hLayoutControls2.setStretch(3, 1)
|
self.hLayoutControls2.setStretch(4, 1)
|
||||||
self.verticalLayout_3.addLayout(self.hLayoutControls2)
|
self.verticalLayout.addLayout(self.hLayoutControls2)
|
||||||
|
self.verticalLayout_3.addLayout(self.verticalLayout)
|
||||||
self.verticalLayout_3.setStretch(0, 20)
|
self.verticalLayout_3.setStretch(0, 20)
|
||||||
MainWindow.setCentralWidget(self.centralwidget)
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
self.menubar = QtWidgets.QMenuBar(MainWindow)
|
||||||
self.menubar.setGeometry(QtCore.QRect(0, 0, 1152, 41))
|
self.menubar.setGeometry(QtCore.QRect(0, 0, 1152, 24))
|
||||||
self.menubar.setObjectName("menubar")
|
self.menubar.setObjectName("menubar")
|
||||||
self.menuFile = QtWidgets.QMenu(self.menubar)
|
self.menuFile = QtWidgets.QMenu(self.menubar)
|
||||||
self.menuFile.setObjectName("menuFile")
|
self.menuFile.setObjectName("menuFile")
|
||||||
@ -243,8 +243,6 @@ class Ui_MainWindow(object):
|
|||||||
def retranslateUi(self, MainWindow):
|
def retranslateUi(self, MainWindow):
|
||||||
_translate = QtCore.QCoreApplication.translate
|
_translate = QtCore.QCoreApplication.translate
|
||||||
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
|
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
|
||||||
self.artistLabel.setText(_translate("MainWindow", "artist"))
|
|
||||||
self.albumLabel.setText(_translate("MainWindow", "album"))
|
|
||||||
self.startTimeLabel.setText(_translate("MainWindow", "00:00"))
|
self.startTimeLabel.setText(_translate("MainWindow", "00:00"))
|
||||||
self.slashLabel.setText(_translate("MainWindow", "/"))
|
self.slashLabel.setText(_translate("MainWindow", "/"))
|
||||||
self.endTimeLabel.setText(_translate("MainWindow", "00:00"))
|
self.endTimeLabel.setText(_translate("MainWindow", "00:00"))
|
||||||
@ -253,6 +251,8 @@ class Ui_MainWindow(object):
|
|||||||
self.playButton.setText(_translate("MainWindow", "▶️"))
|
self.playButton.setText(_translate("MainWindow", "▶️"))
|
||||||
self.nextButton.setText(_translate("MainWindow", "⏭️"))
|
self.nextButton.setText(_translate("MainWindow", "⏭️"))
|
||||||
self.volumeLabel.setText(_translate("MainWindow", "50"))
|
self.volumeLabel.setText(_translate("MainWindow", "50"))
|
||||||
|
self.artistLabel.setText(_translate("MainWindow", "artist"))
|
||||||
|
self.albumLabel.setText(_translate("MainWindow", "album"))
|
||||||
self.pushButton.setText(_translate("MainWindow", "nothing"))
|
self.pushButton.setText(_translate("MainWindow", "nothing"))
|
||||||
self.menuFile.setTitle(_translate("MainWindow", "File"))
|
self.menuFile.setTitle(_translate("MainWindow", "File"))
|
||||||
self.menuEdit.setTitle(_translate("MainWindow", "Edit"))
|
self.menuEdit.setTitle(_translate("MainWindow", "Edit"))
|
||||||
|
|||||||
97
ui.ui
97
ui.ui
@ -17,9 +17,9 @@
|
|||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="centralwidget">
|
<widget class="QWidget" name="centralwidget">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="20,0,0">
|
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="20">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,2">
|
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
@ -27,7 +27,7 @@
|
|||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="hLayoutHead" stretch="1,4,6">
|
<layout class="QHBoxLayout" name="hLayoutHead" stretch="1,0,6">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="vlayoutAlbumArt">
|
<layout class="QVBoxLayout" name="vlayoutAlbumArt">
|
||||||
<property name="sizeConstraint">
|
<property name="sizeConstraint">
|
||||||
@ -39,46 +39,7 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="vLayoutSongDetails">
|
<layout class="QVBoxLayout" name="vLayoutSongDetails"/>
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="artistLabel">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>24</pointsize>
|
|
||||||
<weight>75</weight>
|
|
||||||
<bold>true</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>artist</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="titleLabel">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>18</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="albumLabel">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>16</pointsize>
|
|
||||||
<weight>50</weight>
|
|
||||||
<italic>true</italic>
|
|
||||||
<bold>false</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>album</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QVBoxLayout" name="vLayoutPlaybackVisuals">
|
<layout class="QVBoxLayout" name="vLayoutPlaybackVisuals">
|
||||||
@ -201,8 +162,6 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="hLayoutControls" stretch="1,2,2,2,1">
|
<layout class="QHBoxLayout" name="hLayoutControls" stretch="1,2,2,2,1">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
@ -273,7 +232,7 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="hLayoutControls2" stretch="1,0,4,1">
|
<layout class="QHBoxLayout" name="hLayoutControls2" stretch="1,0,0,4,1">
|
||||||
<property name="spacing">
|
<property name="spacing">
|
||||||
<number>6</number>
|
<number>6</number>
|
||||||
</property>
|
</property>
|
||||||
@ -303,6 +262,48 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="hLayoutSongDetails">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="titleLabel">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>18</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="artistLabel">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>24</pointsize>
|
||||||
|
<weight>75</weight>
|
||||||
|
<bold>true</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>artist</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="albumLabel">
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>16</pointsize>
|
||||||
|
<weight>50</weight>
|
||||||
|
<italic>true</italic>
|
||||||
|
<bold>false</bold>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>album</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<spacer name="horizontalSpacer">
|
<spacer name="horizontalSpacer">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
@ -326,6 +327,8 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenuBar" name="menubar">
|
<widget class="QMenuBar" name="menubar">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
@ -333,7 +336,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1152</width>
|
<width>1152</width>
|
||||||
<height>41</height>
|
<height>24</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuFile">
|
<widget class="QMenu" name="menuFile">
|
||||||
|
|||||||
@ -21,7 +21,10 @@ class FFTAnalyser(QtCore.QThread):
|
|||||||
self.player.currentMediaChanged.connect(self.reset_media)
|
self.player.currentMediaChanged.connect(self.reset_media)
|
||||||
|
|
||||||
self.resolution = x_resolution
|
self.resolution = x_resolution
|
||||||
self.sampling_window_length = 0.05
|
# 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.visual_delta_threshold = 1000
|
||||||
self.sensitivity = 10
|
self.sensitivity = 10
|
||||||
|
|
||||||
@ -59,23 +62,38 @@ class FFTAnalyser(QtCore.QThread):
|
|||||||
freq = np.fft.fftfreq(fourier.size, d=self.sampling_window_length)
|
freq = np.fft.fftfreq(fourier.size, d=self.sampling_window_length)
|
||||||
amps = 2 / v_sample.size * np.abs(fourier)
|
amps = 2 / v_sample.size * np.abs(fourier)
|
||||||
data = np.array([freq, amps]).T
|
data = np.array([freq, amps]).T
|
||||||
print(freq * .05 * self.song.frame_rate)
|
# print(freq * .05 * self.song.frame_rate)
|
||||||
|
|
||||||
|
# NOTE:
|
||||||
# given 520 hz sine wave
|
# given 520 hz sine wave
|
||||||
# np.argmax(fourier) = 2374
|
# np.argmax(fourier) = 2374
|
||||||
# freq[2374] * .05 * self.song.frame_rate = 520
|
# 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
|
||||||
# x values = freq * self.song.frame_rate * .05
|
# print(freq * self.song.frame_rate * .05)
|
||||||
|
|
||||||
point_range = 1 / self.resolution
|
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)
|
||||||
|
|
||||||
point_samples = []
|
point_samples = []
|
||||||
|
|
||||||
if not data.size:
|
if not data.size:
|
||||||
return
|
return
|
||||||
|
|
||||||
for i, freq in enumerate(np.arange(0, 1, point_range), start=1):
|
#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
|
# get the amps which are in between the frequency range
|
||||||
amps = data[(freq - point_range < data[:, 0]) & (data[:, 0] < freq)]
|
#amps = data[(freq - point_range < data[:, 0]) & (data[:, 0] < freq)]
|
||||||
|
amps = data[(log_freq - point_range < data[:, 0]) & (data[:, 0] < log_freq)]
|
||||||
if not amps.size:
|
if not amps.size:
|
||||||
point_samples.append(0)
|
point_samples.append(0)
|
||||||
else:
|
else:
|
||||||
@ -91,7 +109,7 @@ class FFTAnalyser(QtCore.QThread):
|
|||||||
# array (self.bars) is so that we can fade out the previous amplitudes from
|
# array (self.bars) is so that we can fade out the previous amplitudes from
|
||||||
# the past
|
# the past
|
||||||
for n, amp in enumerate(point_samples):
|
for n, amp in enumerate(point_samples):
|
||||||
amp *= 2
|
# amp *= 2
|
||||||
|
|
||||||
if (
|
if (
|
||||||
self.points[n] > 0
|
self.points[n] > 0
|
||||||
@ -108,14 +126,11 @@ class FFTAnalyser(QtCore.QThread):
|
|||||||
# interpolate points
|
# interpolate points
|
||||||
rs = gaussian_filter1d(self.points, sigma=2)
|
rs = gaussian_filter1d(self.points, sigma=2)
|
||||||
|
|
||||||
# Mirror the amplitudes, these are renamed to 'rs' because we are using them
|
# divide by the highest sample in the song to normalise the
|
||||||
# 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
|
# amps in terms of decimals from 0 -> 1
|
||||||
self.calculated_visual.emit(rs / self.max_sample)
|
self.calculated_visual.emit(rs / self.max_sample)
|
||||||
|
# self.calculated_visual.emit(rs)
|
||||||
|
# print(rs)
|
||||||
# print(rs/self.max_sample)
|
# print(rs/self.max_sample)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|||||||
168
utils/fft_analyser.py.dumb
Normal file
168
utils/fft_analyser.py.dumb
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
# 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)
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
import logging
|
from logging import debug, error
|
||||||
from mutagen.id3 import ID3
|
from mutagen.id3 import ID3
|
||||||
from mutagen.id3._frames import TIT2
|
from mutagen.id3._frames import TIT2
|
||||||
from mutagen.id3._util import ID3NoHeaderError
|
from mutagen.id3._util import ID3NoHeaderError
|
||||||
@ -7,7 +7,7 @@ from mutagen.id3._util import ID3NoHeaderError
|
|||||||
|
|
||||||
def get_id3_tags(filename):
|
def get_id3_tags(filename):
|
||||||
"""Get the ID3 tags for an audio file"""
|
"""Get the ID3 tags for an audio file"""
|
||||||
logging.info(filename)
|
debug(filename)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Open the MP3 file and read its content
|
# Open the MP3 file and read its content
|
||||||
@ -34,6 +34,6 @@ def get_id3_tags(filename):
|
|||||||
audio.save()
|
audio.save()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Could not assign file ID3 tag: {e}")
|
error(f"Could not assign file ID3 tag: {e}")
|
||||||
|
|
||||||
return audio
|
return audio
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user