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,
)
from pprint import pformat
from logging import debug
class DebugWindow(QDialog):
@ -16,6 +17,7 @@ class DebugWindow(QDialog):
layout = QVBoxLayout()
# Labels & input fields
# debug(pformat(self.text))
self.input_field = QPlainTextEdit(pformat(self.text))
layout.addWidget(self.input_field)

View File

@ -148,8 +148,8 @@ class MusicTable(QTableView):
# CONNECTIONS
self.clicked.connect(self.set_selected_song_filepath)
self.doubleClicked.connect(self.set_current_song_filepath)
self.enterKey.connect(self.set_current_song_filepath)
# self.doubleClicked.connect(self.set_current_song_filepath)
# self.enterKey.connect(self.set_current_song_filepath)
self.deleteKey.connect(self.delete_songs)
self.model2.dataChanged.connect(self.on_cell_data_changed) # editing cells
self.model2.layoutChanged.connect(self.restore_scroll_position)
@ -465,6 +465,7 @@ class MusicTable(QTableView):
)
if new_index.isValid():
self.setCurrentIndex(new_index)
super().keyPressEvent(e)
elif key == Qt.Key.Key_Down: # Arrow key navigation
current_index = self.currentIndex()
new_index = self.model2.index(
@ -472,6 +473,7 @@ class MusicTable(QTableView):
)
if new_index.isValid():
self.setCurrentIndex(new_index)
super().keyPressEvent(e)
elif key in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
if self.state() != QAbstractItemView.EditingState:
self.enterKey.emit() # Enter key detected
@ -737,7 +739,7 @@ class MusicTable(QTableView):
)
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:
# 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

26
main.py
View File

@ -12,6 +12,7 @@ from mutagen.id3._frames import APIC
from configparser import ConfigParser
from pathlib import Path
from appdirs import user_config_dir
from numpy import array as nparray
from logging import debug, error, warning, basicConfig, INFO, DEBUG
from ui import Ui_MainWindow
from PyQt5.QtWidgets import (
@ -148,6 +149,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
# UI
self.setupUi(self)
self.setWindowTitle("musicpom")
# self.vLayoutAlbumArt.SetFixedSize()
self.status_bar = QStatusBar()
self.permanent_status_label = QLabel("Status...")
self.status_bar.addPermanentWidget(self.permanent_status_label)
@ -160,7 +162,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
self.config.read(self.cfg_file)
self.player: QMediaPlayer = QMediaPlayer() # Audio player object
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.timer = QTimer(self) # Audio timing things
self.clipboard = clipboard
@ -186,14 +188,16 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
self.PlotWidget.setLogMode(False, False)
self.PlotWidget.setMouseEnabled(x=False, y=False)
# 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
# x = nparray([0, 31.25, 62.5, 125, 250, 500, 1000, 2000, 4000, 8000, 15000, 20000])
# 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
# self.PlotWidget.getAxis("left").setTicks([])
# self.PlotWidget.getAxis("left").setLabel("") # Remove y-axis label
self.PlotWidget.showGrid(x=True, y=True)
# Connections
self.playbackSlider.sliderReleased.connect(
@ -306,7 +310,10 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
self.permanent_status_label.setText(message)
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
self.current_song_metadata = self.tableView.get_current_song_metadata()
# read the file
@ -333,6 +340,9 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
)
title = self.current_song_metadata["TIT2"][0]
debug(artist)
debug(title)
debug(album)
self.artistLabel.setText(artist)
self.albumLabel.setText(album)
self.titleLabel.setText(title)

68
ui.py
View File

@ -35,29 +35,6 @@ class Ui_MainWindow(object):
self.hLayoutHead.addLayout(self.vlayoutAlbumArt)
self.vLayoutSongDetails = QtWidgets.QVBoxLayout()
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.vLayoutPlaybackVisuals = QtWidgets.QVBoxLayout()
self.vLayoutPlaybackVisuals.setObjectName("vLayoutPlaybackVisuals")
@ -118,7 +95,6 @@ class Ui_MainWindow(object):
self.vLayoutPlaybackVisuals.addWidget(self.PlotWidget)
self.hLayoutHead.addLayout(self.vLayoutPlaybackVisuals)
self.hLayoutHead.setStretch(0, 1)
self.hLayoutHead.setStretch(1, 4)
self.hLayoutHead.setStretch(2, 6)
self.verticalLayout.addLayout(self.hLayoutHead)
self.hLayoutMusicTable = QtWidgets.QHBoxLayout()
@ -134,9 +110,6 @@ class Ui_MainWindow(object):
self.hLayoutMusicTable.setStretch(0, 2)
self.hLayoutMusicTable.setStretch(1, 10)
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.setSpacing(6)
self.hLayoutControls.setObjectName("hLayoutControls")
@ -167,7 +140,7 @@ class Ui_MainWindow(object):
self.hLayoutControls.setStretch(2, 2)
self.hLayoutControls.setStretch(3, 2)
self.hLayoutControls.setStretch(4, 1)
self.verticalLayout_3.addLayout(self.hLayoutControls)
self.verticalLayout.addLayout(self.hLayoutControls)
self.hLayoutControls2 = QtWidgets.QHBoxLayout()
self.hLayoutControls2.setSpacing(6)
self.hLayoutControls2.setObjectName("hLayoutControls2")
@ -182,19 +155,46 @@ class Ui_MainWindow(object):
self.volumeLabel = QtWidgets.QLabel(self.centralwidget)
self.volumeLabel.setObjectName("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)
self.hLayoutControls2.addItem(spacerItem2)
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setObjectName("pushButton")
self.hLayoutControls2.addWidget(self.pushButton)
self.hLayoutControls2.setStretch(0, 1)
self.hLayoutControls2.setStretch(2, 4)
self.hLayoutControls2.setStretch(3, 1)
self.verticalLayout_3.addLayout(self.hLayoutControls2)
self.hLayoutControls2.setStretch(3, 4)
self.hLayoutControls2.setStretch(4, 1)
self.verticalLayout.addLayout(self.hLayoutControls2)
self.verticalLayout_3.addLayout(self.verticalLayout)
self.verticalLayout_3.setStretch(0, 20)
MainWindow.setCentralWidget(self.centralwidget)
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.menuFile = QtWidgets.QMenu(self.menubar)
self.menuFile.setObjectName("menuFile")
@ -243,8 +243,6 @@ class Ui_MainWindow(object):
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
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.slashLabel.setText(_translate("MainWindow", "/"))
self.endTimeLabel.setText(_translate("MainWindow", "00:00"))
@ -253,6 +251,8 @@ class Ui_MainWindow(object):
self.playButton.setText(_translate("MainWindow", "▶️"))
self.nextButton.setText(_translate("MainWindow", "⏭️"))
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.menuFile.setTitle(_translate("MainWindow", "File"))
self.menuEdit.setTitle(_translate("MainWindow", "Edit"))

323
ui.ui
View File

@ -17,9 +17,9 @@
<string/>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="20,0,0">
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="20">
<item>
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,2">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0">
<property name="spacing">
<number>6</number>
</property>
@ -27,7 +27,7 @@
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" name="hLayoutHead" stretch="1,4,6">
<layout class="QHBoxLayout" name="hLayoutHead" stretch="1,0,6">
<item>
<layout class="QVBoxLayout" name="vlayoutAlbumArt">
<property name="sizeConstraint">
@ -39,46 +39,7 @@
</layout>
</item>
<item>
<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>
<layout class="QVBoxLayout" name="vLayoutSongDetails"/>
</item>
<item>
<layout class="QVBoxLayout" name="vLayoutPlaybackVisuals">
@ -201,127 +162,169 @@
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hLayoutControls" stretch="1,2,2,2,1">
<property name="spacing">
<number>6</number>
</property>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<layout class="QHBoxLayout" name="hLayoutControls" stretch="1,2,2,2,1">
<property name="spacing">
<number>6</number>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="previousButton">
<property name="font">
<font>
<pointsize>28</pointsize>
</font>
</property>
<property name="text">
<string>⏮️</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="playButton">
<property name="font">
<font>
<pointsize>28</pointsize>
</font>
</property>
<property name="text">
<string>▶️</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="nextButton">
<property name="font">
<font>
<pointsize>28</pointsize>
</font>
</property>
<property name="text">
<string>⏭️</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QPushButton" name="previousButton">
<property name="font">
<font>
<pointsize>28</pointsize>
</font>
<layout class="QHBoxLayout" name="hLayoutControls2" stretch="1,0,0,4,1">
<property name="spacing">
<number>6</number>
</property>
<property name="text">
<string>⏮️</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="playButton">
<property name="font">
<font>
<pointsize>28</pointsize>
</font>
</property>
<property name="text">
<string>▶️</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="nextButton">
<property name="font">
<font>
<pointsize>28</pointsize>
</font>
</property>
<property name="text">
<string>⏭️</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="hLayoutControls2" stretch="1,0,4,1">
<property name="spacing">
<number>6</number>
</property>
<item>
<widget class="QSlider" name="volumeSlider">
<property name="minimum">
<number>-1</number>
</property>
<property name="maximum">
<number>101</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksAbove</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="volumeLabel">
<property name="text">
<string>50</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>nothing</string>
</property>
</widget>
<item>
<widget class="QSlider" name="volumeSlider">
<property name="minimum">
<number>-1</number>
</property>
<property name="maximum">
<number>101</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksAbove</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="volumeLabel">
<property name="text">
<string>50</string>
</property>
</widget>
</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>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>nothing</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
@ -333,7 +336,7 @@
<x>0</x>
<y>0</y>
<width>1152</width>
<height>41</height>
<height>24</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">

View File

@ -21,7 +21,10 @@ class FFTAnalyser(QtCore.QThread):
self.player.currentMediaChanged.connect(self.reset_media)
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.sensitivity = 10
@ -59,23 +62,38 @@ class FFTAnalyser(QtCore.QThread):
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)
# 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
# x values = freq * self.song.frame_rate * .05
# 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
# 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 = []
if not data.size:
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
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:
point_samples.append(0)
else:
@ -91,7 +109,7 @@ class FFTAnalyser(QtCore.QThread):
# 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
# amp *= 2
if (
self.points[n] > 0
@ -108,14 +126,11 @@ class FFTAnalyser(QtCore.QThread):
# 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
# divide 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)
# self.calculated_visual.emit(rs)
# print(rs)
# print(rs/self.max_sample)
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 logging
from logging import debug, error
from mutagen.id3 import ID3
from mutagen.id3._frames import TIT2
from mutagen.id3._util import ID3NoHeaderError
@ -7,7 +7,7 @@ from mutagen.id3._util import ID3NoHeaderError
def get_id3_tags(filename):
"""Get the ID3 tags for an audio file"""
logging.info(filename)
debug(filename)
try:
# Open the MP3 file and read its content
@ -34,6 +34,6 @@ def get_id3_tags(filename):
audio.save()
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