broke everything changing layout

This commit is contained in:
billypom on debian 2025-03-30 22:53:10 -04:00
parent ba5a015495
commit 019bf5c8e7
8 changed files with 422 additions and 222 deletions

View File

@ -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)

View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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">

View File

@ -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
View 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)

View File

@ -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