【发布时间】:2017-10-07 06:19:33
【问题描述】:
我不想说我花了多长时间才达到这一点,但我很难完全理解 PyQt5 以及它与我在 Qt 网站上看到的 C++ 代码的关系,但我认为.. 我明白了,或者...至少我以为我做到了,直到这完全失败为止。我将从我得到的输出开始,它告诉我我有一个确实存在的文件。我已经尝试过 mp3 和 ogg 版本,以防由于某种原因 AudioDecoder 无法解码 MP3,即使 QtMultimedia 的其他部分能够播放它(我正在尝试降低级别,以便我可以将平移应用于音频和改变左/右平衡,一旦我弄清楚了,也许还有其他有趣的事情)。
调试输出:
MP3 exists:True
Decoder stopped:True <- expected at this point, just confirming state works
Decoder state changed? <- this means state change signal is being sent
Decoder stopped?:False <- ok, state did actually change, that's expected
Decoder decoding?:True <- expected, confirming there are only 2 states as documentation indicates
Init finished, Decoder started? <- after this, i expect to see position changes, buffer availability changes, or errors ... but I get nothing and it just exits the script.
代码:
from PyQt5 import QtCore, QtMultimedia
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QByteArray, QIODevice, QFileInfo
from PyQt5.QtMultimedia import QAudioDecoder, QAudioFormat, QMediaObject, QAudioBuffer, QAudioOutput, QAudio
class AudioDecoder(QObject):
def __init__(self):
super(AudioDecoder,self).__init__()
self.desiredFormat = QAudioFormat()
self.desiredFormat.setChannelCount(2)
self.desiredFormat.setCodec('audio/pcm')
self.desiredFormat.setSampleType(QAudioFormat.UnSignedInt)
self.desiredFormat.setSampleRate(48000)
self.desiredFormat.setSampleSize(16)
self.decoder = QAudioDecoder()
self.decoder.setAudioFormat(self.desiredFormat)
self.decoder.setSourceFilename('D:\\python\\sounds\\30.mp3')
fs = QFileInfo()
print('MP3 exists:' + str(fs.exists('D:\\python\\sounds\\30.mp3')))
#self.connect(decoder,bufferReady(),None,readBuffer())
self.decoder.bufferReady.connect(self.readBuffer)
self.decoder.finished.connect(self.play)
self.decoder.error.connect(self.error)
self.decoder.stateChanged.connect(self.stateChanged)
self.decoder.positionChanged.connect(self.positionChanged)
self.decoder.bufferAvailableChanged.connect(self.bufferAvailableChanged)
#using this to determine if we need to start byte array or append to it
self.readamount = 0
#Expect this to be true since we haven't started yet
print('Decoder stopped:' + str(self.decoder.state() == QAudioDecoder.StoppedState))
self.decoder.start()
print('Init finished, Decoder started?')
def bufferAvailableChanged(self):
print(str(decoder.available))
def positionChanged(self):
print(str(decoder.position())+'/'+str(decoder.duration))
def stateChanged(self):
#Confirm state is what we expect
print('Decoder state changed?')
print('Decoder stopped?:' + str(self.decoder.state() == QAudioDecoder.StoppedState))
print('Decoder decoding?:' + str(self.decoder.state() == QAudioDecoder.DecodingState))
def error(self):
print('Decoder error?')
print(self.decoder.errorString())
def readBuffer(self):
print('Decoder ready for reading?')
buffer = self.decoder.read()
print('Bytecount in buffer:' + str(buffer.byteCount))
if self.readamount == 0:
self.ba = QByteArray()
self.ba.fromRawData(buffer.data(),buffer.byteCount())
else:
self.ba.append(buffer.data(),buffer.byteCount())
print('Bytearray size:' + str(self.ba.length()))
def play(self):
print('Decoding finished, ready to play')
ad = AudioDecoder()
修改后的代码,尝试 WAV,但仍然无法正常工作:
from PyQt5 import QtCore, QtMultimedia
from PyQt5.QtTest import QSignalSpy
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QByteArray, QIODevice, QFileInfo
from PyQt5.QtMultimedia import QAudioDecoder, QAudioFormat, QMediaObject, QAudioBuffer, QAudioOutput, QAudio
class AudioDecoder(QObject):
def __init__(self):
super(AudioDecoder,self).__init__()
self.desiredFormat = QAudioFormat()
self.desiredFormat.setChannelCount(2)
self.desiredFormat.setCodec('audio/pcm')
self.desiredFormat.setSampleType(QAudioFormat.UnSignedInt)
self.desiredFormat.setSampleRate(48000)
self.desiredFormat.setSampleSize(16)
self.decoder = QAudioDecoder()
self.decoder.bufferReady.connect(self.readBuffer)
self.decoder.finished.connect(self.play)
self.decoder.error.connect(lambda: self.error(self.decoder.error()))
self.decoder.stateChanged.connect(lambda: self.stateChanged(self.decoder.state()))
self.decoder.positionChanged.connect(lambda: self.positionChanged(self.decoder.position(),self.decoder.duration()))
self.decoder.bufferAvailableChanged.connect(lambda: self.bufferAvailableChanged(self.decoder.available()))
self.decoder.setAudioFormat(self.desiredFormat)
self.decoder.setSourceFilename('D:\\python\\sounds\\piano2.wav')
fs = QFileInfo()
print('File exists:' + str(fs.exists('D:\\python\\sounds\\piano2.wav')))
#using this to determine if we need to start byte array or append to it
self.readamount = 0
#Expect this to be true since we haven't started yet
print('Decoder stopped?:' + str(self.decoder.state() == QAudioDecoder.StoppedState))
self.decoder.start()
print('Init finished, Decoder started on file:' + self.decoder.sourceFilename())
@pyqtSlot()
def bufferAvailableChanged(self,available):
print('Available:' + str(available))
@pyqtSlot()
def positionChanged(self,position,duration):
print('Position:' + str(position())+'/'+str(duration()))
@pyqtSlot()
def stateChanged(self,state):
#Confirm state is what we expect
print('Decoder state changed')
if state == QAudioDecoder.StoppedState:
print('Decoder stopped?:' + str(state == QAudioDecoder.StoppedState))
else:
print('Decoder decoding?:' + str(state == QAudioDecoder.DecodingState))
@pyqtSlot()
def error(self,err):
print('Decoder error')
print(self.decoder.errorString())
def readBuffer(self):
print('Decoder ready for reading?')
buffer = self.decoder.read()
print('Bytecount in buffer:' + str(buffer.byteCount))
if self.readamount == 0:
self.ba = QByteArray()
self.ba.fromRawData(buffer.data(),buffer.byteCount())
else:
self.ba.append(buffer.data(),buffer.byteCount())
self.readamount = self.readamount + 1
print('Bytearray size:' + str(self.ba.length()))
def play(self):
print('Decoding finished, ready to play')
ad = AudioDecoder()
我的更新代码从下面发布答案,它适用于 mp3 :)
from PyQt5 import QtCore, QtMultimedia, QtWidgets
from PyQt5.QtTest import QSignalSpy
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QByteArray, QIODevice, QFileInfo
from PyQt5.QtMultimedia import QAudioDecoder, QAudioFormat, QMediaObject, QAudioBuffer, QAudioOutput, QAudio
import signal
class AudioDecoder(QObject):
def __init__(self):
super(AudioDecoder,self).__init__()
self.desiredFormat = QAudioFormat()
self.desiredFormat.setChannelCount(2)
self.desiredFormat.setCodec('audio/pcm')
self.desiredFormat.setSampleType(QAudioFormat.UnSignedInt)
self.desiredFormat.setSampleRate(48000)
self.desiredFormat.setSampleSize(16)
self.decoder = QAudioDecoder()
self.decoder.bufferReady.connect(self.readBuffer)
self.decoder.finished.connect(self.play)
self.decoder.error.connect(self.error)
self.decoder.stateChanged.connect(self.stateChanged)
self.decoder.positionChanged.connect(self.positionChanged)
self.decoder.bufferAvailableChanged.connect(self.bufferAvailableChanged)
self.decoder.setAudioFormat(self.desiredFormat)
self.decoder.setSourceFilename('D:\\python\\sounds\\30.mp3')
fs = QFileInfo()
print('File exists:' + str(fs.exists('D:\\python\\sounds\\30.mp3')))
#using this to determine if we need to start byte array or append to it
self.readamount = 0
#Expect this to be true since we haven't started yet
print('Decoder stopped?:' + str(self.decoder.state() == QAudioDecoder.StoppedState))
self.decoder.start()
print('Init finished, Decoder started on file:' + self.decoder.sourceFilename())
def bufferAvailableChanged(self,available):
print('Available:' + str(available))
def positionChanged(self,position):
print('Position:' + str(position)+'/'+str(self.decoder.duration))
def stateChanged(self,state):
#Confirm state is what we expect
print('Decoder state changed')
if state == QAudioDecoder.StoppedState:
print('Decoder stopped?:' + str(state == QAudioDecoder.StoppedState))
else:
print('Decoder decoding?:' + str(state == QAudioDecoder.DecodingState))
def error(self,err):
print('Decoder error')
print(self.decoder.errorString())
def readBuffer(self):
print('Decoder ready for reading?')
buffer = self.decoder.read()
byteCount = buffer.byteCount()
print('Bytecount in buffer:' + str(byteCount))
if self.readamount == 0:
self.ba = QByteArray()
self.ba.fromRawData(buffer.constData().asstring(byteCount))
else:
self.ba.append(buffer.constData().asstring(byteCount))
self.readamount = self.readamount + 1
print('Bytearray size:' + str(self.ba.length()))
def play(self):
print('Decoding finished, ready to play')
app = QtWidgets.QApplication([''])
ad = AudioDecoder()
signal.signal(signal.SIGINT,signal.SIG_DFL)
app.exec_()
【问题讨论】:
-
*我注意到上面的代码中缺少一些东西,没有什么会导致我得到的结果,但是会导致意外行为的东西。例如,我在阅读后忘记更新 readamount,我注意到如果我将状态放入函数调用中,我可以避免在连接的 python 插槽中调用decoder.state,但这并没有改变行为,即使它比以上。
-
在修复了代码中的许多小错误之后,我能够让它正常工作。不过,我只在 linux 上进行了测试(它使用 gstreamer 后端)。您可能想尝试使用
wav文件,因为保证支持该格式。 -
后来我确实尝试过使用 wav 文件,也尝试了一些调整(将它们放在下面)。您是否介意通过您对代码所做的细微调整在这里给出答案?
-
我怀疑我错过了一些基本的东西,你看到我没有,因为否则这只是一个 linux 与 windows 的问题,但我不知道为什么会这样。我会看看我是否可以设置一个虚拟机来测试它。
-
我添加了一个答案。我怀疑它可能没有多大帮助,但你永远不知道......
标签: python-3.x qt5 pyqt5 qtmultimedia