前言
最近同学有个作业,做音乐可视化播放器,为了学习PyQt,我就尝试做了做。该设计主要分为音乐播放器和可视化两部分。两部分单独做相对于结合在一起容易很多,结合的过程遇到了很多麻烦。
音乐播放器:
可视化:
参考博客:
1 python 将MP3格式转换为WAV格式(ffmpeg安装,使用pycharm安装包)
2 pyqt5+matplotlib+Funcanimation+scatter(qt5+动态散点图)
3 python 音频可视化
界面设计采用QtDesigner进行设计,如下图:
包含对象如下:
使用pyuic编译出.py代码。
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'gui.ui'
#
# Created by: PyQt5 UI code generator 5.14.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(449, 439)
MainWindow.setAnimated(True)
self.centralwidget = QtWidgets.QWidget(MainWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
self.centralwidget.setSizePolicy(sizePolicy)
self.centralwidget.setObjectName("centralwidget")
self.layoutWidget = QtWidgets.QWidget(self.centralwidget)
self.layoutWidget.setGeometry(QtCore.QRect(11, 11, 431, 30))
self.layoutWidget.setObjectName("layoutWidget")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.layoutWidget)
self.horizontalLayout_2.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.lab_name = QtWidgets.QLabel(self.layoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lab_name.sizePolicy().hasHeightForWidth())
self.lab_name.setSizePolicy(sizePolicy)
self.lab_name.setAlignment(QtCore.Qt.AlignCenter)
self.lab_name.setObjectName("lab_name")
self.horizontalLayout_2.addWidget(self.lab_name)
self.btn_openFile = QtWidgets.QPushButton(self.layoutWidget)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.btn_openFile.sizePolicy().hasHeightForWidth())
self.btn_openFile.setSizePolicy(sizePolicy)
self.btn_openFile.setObjectName("btn_openFile")
self.horizontalLayout_2.addWidget(self.btn_openFile)
self.layoutWidget1 = QtWidgets.QWidget(self.centralwidget)
self.layoutWidget1.setGeometry(QtCore.QRect(11, 400, 431, 30))
self.layoutWidget1.setObjectName("layoutWidget1")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.layoutWidget1)
self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.lab_time = QtWidgets.QLabel(self.layoutWidget1)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lab_time.sizePolicy().hasHeightForWidth())
self.lab_time.setSizePolicy(sizePolicy)
self.lab_time.setObjectName("lab_time")
self.horizontalLayout.addWidget(self.lab_time)
self.slider_time = QtWidgets.QSlider(self.layoutWidget1)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.slider_time.sizePolicy().hasHeightForWidth())
self.slider_time.setSizePolicy(sizePolicy)
self.slider_time.setOrientation(QtCore.Qt.Horizontal)
self.slider_time.setObjectName("slider_time")
self.horizontalLayout.addWidget(self.slider_time)
self.lab_duration = QtWidgets.QLabel(self.layoutWidget1)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.lab_duration.sizePolicy().hasHeightForWidth())
self.lab_duration.setSizePolicy(sizePolicy)
self.lab_duration.setObjectName("lab_duration")
self.horizontalLayout.addWidget(self.lab_duration)
self.btn_start = QtWidgets.QPushButton(self.layoutWidget1)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.btn_start.sizePolicy().hasHeightForWidth())
self.btn_start.setSizePolicy(sizePolicy)
self.btn_start.setObjectName("btn_start")
self.horizontalLayout.addWidget(self.btn_start)
self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 40, 431, 361))
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.container = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.container.setContentsMargins(0, 0, 0, 0)
self.container.setObjectName("container")
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "音乐播放器"))
self.lab_name.setText(_translate("MainWindow", "暂无歌曲导入"))
self.btn_openFile.setText(_translate("MainWindow", "打开文件"))
self.lab_time.setText(_translate("MainWindow", "00:00"))
self.lab_duration.setText(_translate("MainWindow", "00:00"))
self.btn_start.setText(_translate("MainWindow", "播放"))
输入音频格式为mp3
或wav
,如果输入mp3
会自动将其转换为wav
进行下一步处理,mp3
转wav
需要安装ffmpeg。
为什么要将 mp3 转成 wav 格式的文件?
因为 mp3
格式为了减小体积牺牲了音质,转成无损的 wav
格式之后,可以读取到更详细的信息。如果未安装ffmpeg,建议导入wav
文件。
视频播放链接:https://www.bilibili.com/video/av95442718/
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog
from PyQt5.QtMultimedia import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.animation import FuncAnimation
from gui.gui import Ui_MainWindow
from scipy.signal import detrend
from pydub import AudioSegment
import matplotlib.pyplot as plt
import numpy as np
import struct
import wave
import os
import sys
import time
chunk = 1024
# 将该路径文件从mp3转为wav
def change_format(path):
new_path = path.replace('mp3', 'wav')
# 将mp3文件转换成wav
sound = AudioSegment.from_mp3(path)
sound.export(new_path, format="wav")
# FigureCanvas 对象
class MyMplCanvas(FigureCanvas):
def __init__(self, parent=None, width=5, height=4, dpi=100):
fig = plt.figure(figsize=(width, height), dpi=dpi, facecolor='#f0f0f0') # facecolor 背景色
self.ax = fig.gca(projection='polar')
self.ax.set_axis_off()
self.ln, = self.ax.plot([], [])
FigureCanvas.__init__(self, fig)
class MyWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super(MyWindow, self).__init__()
self.setupUi(self)
self.initialize()
'''初始化'''
def initialize(self):
self.setWindowTitle("音乐播放器^_^")
self.setWindowIcon(QIcon('icon/音乐.png'))
self.fileName = ""
self.cur_song = ''
self.is_pause = True
self.y_temp = np.zeros(chunk)
self.playlist = QMediaPlaylist() # 播放列表
self.playlist.setPlaybackMode(QMediaPlaylist.Loop) # 列表循环
self.player = QMediaPlayer(self)
self.player.setPlaylist(self.playlist)
self.player.setVolume(50.0)
# 按键
self.btn_openFile.clicked.connect(lambda: self.btn_openFile_click())
self.btn_start.clicked.connect(lambda: self.btn_start_click())
# 进度条
self.slider_time.sliderMoved[int].connect(lambda: self.player.setPosition(self.slider_time.value()))
# 计时器:控制进度条和进度时间
self.timer = QTimer(self)
self.timer.start(1000)
self.timer.timeout.connect(self.player_timer)
# 音乐可视化
self.isualization()
# 坐标初始化
def init_draw(self):
self.canvas.ax.set_ylim(-0.1, 0.1)
self.canvas.ln.set_data(np.linspace(0, 2 * np.pi, chunk), np.zeros(chunk))
return self.canvas.ln,
# 坐标更新
def update_line(self, frame):
if self.is_pause is False:
data = self.wf.readframes(chunk)
data_int = struct.unpack(str(chunk * 4) + 'B', data)
y_detrend = detrend(data_int)
yft = np.abs(np.fft.fft(y_detrend))
y_vals = yft[:chunk] / (chunk * chunk * 4)
ind = np.where(y_vals > (np.max(y_vals) + np.min(y_vals)) / 2)
y_vals[ind[0]] *= 3
self.y_temp = y_vals
else:
y_vals = self.y_temp # 当暂停时,保存的是上一次的值
self.canvas.ln.set_ydata(y_vals)
return self.canvas.ln,
# 音乐可视化
def isualization(self):
self.canvas = MyMplCanvas(self.container, width=6, height=6, dpi=100)
self.container.addWidget(self.canvas) # 6
self.ani = FuncAnimation(self.canvas.figure, self.update_line, init_func=self.init_draw, interval=32, blit=True)
# 设置进度条和播放时间
def player_timer(self):
self.slider_time.setMinimum(0)
self.slider_time.setMaximum(self.player.duration())
self.slider_time.setValue(self.slider_time.value() + 1000)
self.lab_time.setText(time.strftime('%M:%S', time.localtime(self.player.position() / 1000)))
self.lab_duration.setText(time.strftime('%M:%S', time.localtime(self.player.duration() / 1000)))
# 进度条满了之后回零
if self.player.duration() == self.slider_time.value():
self.slider_time.setValue(0)
# 打开音乐文件,并添加至playlist
def btn_openFile_click(self):
self.playlist.clear() # 读取歌曲前,清空playlist
self.fileName, filetype = QFileDialog.getOpenFileName(self, '选择文件', '', '音频文件 (*.mp3; *.wav)')
if len(self.fileName) == 0:
print("取消选择")
return
else:
print('当前歌曲路径:' + self.fileName)
self.cur_song = os.path.basename(self.fileName)
self.lab_name.setText(self.cur_song)
# 如果是mp3格式,则将mp3转换wav,保存到同一目录下
if self.cur_song[-3:] == 'mp3':
change_format(self.fileName)
self.fileName = self.fileName.replace('mp3', 'wav')
print('new'+ self.fileName)
# 将音频文件添加到playlist
self.playlist.addMedia(QMediaContent(QUrl.fromLocalFile(self.fileName)))
# 可视化部分wave
self.wf = wave.open(self.fileName)
# 正在播放音乐时,中断播放
if self.is_pause is False:
self.player.pause()
self.btn_start.setText('播放')
def btn_start_click(self):
if self.is_pause:
self.is_pause = False
self.player.play()
self.btn_start.setText('暂停')
print('当前播放歌曲: ' + self.cur_song)
else:
self.is_pause = True
self.player.pause()
self.btn_start.setText('播放')
if __name__ == '__main__':
app = QApplication(sys.argv)
myWin = MyWindow()
myWin.show()
sys.exit(app.exec_())