QtMultimedia - QAudioDecoder - Python - изменения состояния, но буфер никогда не был доступен
Я не хочу говорить, сколько времени мне потребовалось, чтобы добраться до этого момента, но у меня были реальные проблемы с полным пониманием PyQt5 и как это связано с кодом C++, который я видел на веб-сайте Qt, но я думаю... я понимаю, или... по крайней мере, я так думал, пока это не сработало. Я начну с вывода, который я получаю, который говорит мне, что у меня есть файл, который действительно существует. Я попробовал 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_()
1 ответ
Вот моя (Linux) рабочая версия вашего оригинального скрипта:
from PyQt5 import QtWidgets
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)
fs = QFileInfo('test.wav')
self.decoder.setSourceFilename(fs.absoluteFilePath())
print('File exists:' + str(fs.exists()))
#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(self.decoder.bufferAvailable()))
def positionChanged(self):
print(str(self.decoder.position())+'/'+str(self.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()
count = buffer.byteCount()
print('Bytecount in buffer:' + str(count))
if self.readamount == 0:
self.ba = QByteArray()
self.ba.fromRawData(buffer.constData().asstring(count))
self.readamount = count
else:
self.ba.append(buffer.constData().asstring(count))
print('Bytearray size:' + str(self.ba.length()))
def play(self):
print('Decoding finished, ready to play')
app = QtWidgets.QApplication([''])
ad = AudioDecoder()
import signal
signal.signal(signal.SIGINT, signal.SIG_DFL)
# press Ctrl+C to exit
app.exec_()
Diff:
--- yours
+++ mine
@@ -1,3 +1,4 @@
+from PyQt5 import QtWidgets
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
@@ -14,9 +15,9 @@
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')))
+ fs = QFileInfo('test.wav')
+ self.decoder.setSourceFilename(fs.absoluteFilePath())
+ print('File exists:' + str(fs.exists()))
#self.connect(decoder,bufferReady(),None,readBuffer())
self.decoder.bufferReady.connect(self.readBuffer)
@@ -34,9 +35,9 @@
self.decoder.start()
print('Init finished, Decoder started?')
def bufferAvailableChanged(self):
- print(str(decoder.available))
+ print(str(self.decoder.bufferAvailable()))
def positionChanged(self):
- print(str(decoder.position())+'/'+str(decoder.duration))
+ print(str(self.decoder.position())+'/'+str(self.decoder.duration()))
def stateChanged(self):
#Confirm state is what we expect
print('Decoder state changed?')
@@ -48,14 +49,23 @@
def readBuffer(self):
print('Decoder ready for reading?')
buffer = self.decoder.read()
- print('Bytecount in buffer:' + str(buffer.byteCount))
+ count = buffer.byteCount()
+ print('Bytecount in buffer:' + str(count))
if self.readamount == 0:
self.ba = QByteArray()
- self.ba.fromRawData(buffer.data(),buffer.byteCount())
+ self.ba.fromRawData(buffer.constData().asstring(count))
+ self.readamount = count
else:
- self.ba.append(buffer.data(),buffer.byteCount())
+ self.ba.append(buffer.constData().asstring(count))
print('Bytearray size:' + str(self.ba.length()))
def play(self):
print('Decoding finished, ready to play')
+app = QtWidgets.QApplication([''])
ad = AudioDecoder()
+
+import signal
+signal.signal(signal.SIGINT, signal.SIG_DFL)
+# press Ctrl+C to exit
+
+app.exec_()
Выход:
File exists:True
Decoder stopped:True
Init finished, Decoder started?
Decoder state changed?
Decoder stopped?:False
Decoder decoding?:True
True
Decoder ready for reading?
0/196238
Bytecount in buffer:7680
Bytearray size:0
Decoder ready for reading?
40/196238
Bytecount in buffer:7680
Bytearray size:7680
Decoder ready for reading?
80/196238
Bytecount in buffer:7680
Bytearray size:15360
Decoder ready for reading?
120/196238
Bytecount in buffer:7680
Bytearray size:23040
Decoder ready for reading?
False
160/196238
Bytecount in buffer:7680
Bytearray size:30720
...
Bytecount in buffer:7680
Bytearray size:37662720
Decoder ready for reading?
False
196200/196238
Bytecount in buffer:7364
Bytearray size:37670084
Decoding finished, ready to play
Decoder state changed?
Decoder stopped?:True
Decoder decoding?:False