2017-10-07 95 views
1

我不想说我需要多长时间才能到达这一点,但我完全理解PyQt5以及它与我看到的C++代码有何关系在Qt网站上,但我认为......我明白了,或者......至少我以为我做到了,直到完全失败。我将从输出结果开始,告诉我我有一个真正存在的文件。我已经尝试过mp3和ogg版本,以防某些原因AudioDecoder无法解码MP3,即使QtMultimedia的其他部分已经能够播放它(我试图让音量更低,因此我可以将声像应用于音频和转移左/右平衡,并且一旦我想出来可能还有其他有趣的事情)。QtMultimedia - QAudioDecoder - Python - 状态的变化,但缓冲区从来没有变得可用

调试输出:

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_() 
+0

*我注意到一对夫妇失踪的事情在上面的代码中,没有什么东西会导致我得到的结果,但会导致意外行为的东西。例如,我忘了在阅读后忘记更新readamount,并且我注意到如果我在函数调用中放置状态,可以避免在连接的python插槽中调用decoder.state,但这并不会改变行为,即使它比以上。 –

+0

在修复代码中的很多小错误之后,我能够让它工作正常。我只在linux上测试,但是(它使用gstreamer后端)。您可能想尝试使用'wav'文件,因为该格式可以得到支持。 – ekhumoro

+0

以后我也尝试过用wav文件尝试一些调整(将它们放在下面)。你会介意在这里对你的代码做一些小的调整吗? –

回答

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 
+0

好吧,我将不得不稍微阅读一下,以了解你所做的一些区别,但我主要得到的是问题所在。一切都没有前进的原因似乎与我没有你的QtWidgets.QApplication对象和执行的事实有关,我猜是什么人可以称之为“应用程序循环”。我曾经见过一些需要它的python库,有些则没有,我不知道他们为什么要这么做,但我想我现在明白了。除非主应用程序循环对象正在运行,否则所有这些信号/插槽的东西不一定会发生。 –

+0

感谢您的回答,这非常有帮助。我知道我需要更多地关注具有所有这些不同库的整个应用程序循环。一旦我添加了我开始出现错误,并且其他修改完全无法解决的问题。如果我记得从其他地方读取,signal.signal行的目的是让我仍然可以用ctrl + C终止应用程序循环? –

+0

@MatthewSwaringen。啊 - 我确实想知道缺少一个应用程序对象,但只是假设你的例子是一个更完整脚本的一部分。信号槽框架与事件框架是分开的,所以它通常不需要运行事件循环。但是,一些重要的例外是定时器和线程,因为它们需要是异步的。我想多媒体类可能会广泛地使用定时器和线程。 – ekhumoro