[英]play sound file in PyQt
我在PyQt中開發了一個播放聲音的軟件。我正在使用Phonon Library播放聲音,但它有一些滯后。所以如何在不使用Phonon Library的情況下在PyQt中播放聲音文件。
這就是我目前使用Phonon的方式:
def Playnote(self,note_id):
global note
note = note_id
self.PlayThread = PlayThread()
self.PlayThread.start()
class PlayThread(QtCore.QThread):
def __init__(self):
QtCore.QThread.__init__(self)
def __del__(self):
self.wait()
def run(self):
global note
self.m_media = Phonon.MediaObject(self)
audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
Phonon.createPath(self.m_media, audioOutput)
self.m_media.setCurrentSource(Phonon.MediaSource(Phonon.MediaSource(note)))
self.m_media.play()
現在滯后減少了。 但問題是我在短時間內按下兩個或更多鍵,這是新音符開銷並停止前一個音符。 我需要播放前一個音符直到它結束。
class PlayThread(QtCore.QThread):
def __init__(self):
QtCore.QThread.__init__(self)
self.m_media = Phonon.MediaObject(self)
self.audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
Phonon.createPath(self.m_media, self.audioOutput)
def __del__(self):
self.wait()
def play(self, note):
self.m_media.setCurrentSource(Phonon.MediaSource(Phonon.MediaSource(note)))
self.m_media.play()
def run(self):pass
我已經重寫了這個答案,因為我認為你的問題已經開始出現分歧
首先,解決您的代碼示例
在您的第一個PlayThread示例中,您每次要播放一個鍵時都會啟動一個新線程,然后必須完全設置一個媒體播放器,然后打開源文件,然后播放。 這肯定會導致你的開銷。
在第二個示例中,您傳遞的是run()
方法,該方法基本上使線程在啟動后立即結束。 然后你直接在那個QThread上調用play()。 基本上你正在做的是使用QThread像一個基本的QObject類,並在同一主線程中調用play。 我也不明白你為什么要從MediaSource創建一個MediaSource(冗余?)。 但是每次你打電話都會取代聲音,這就是你重新開始播放的原因。
我不認為你真的需要QThreads。
QSound的
在更高級別,您可以使用QSound。 為了減少你可能引發的延遲,你不應該使用play()
的靜態方法來動態啟動文件。 相反,您應該在應用程序啟動時預先創建這些QSound對象:
notes = {
'c': QtGui.QSound("c.wav"),
'd': QtGui.QSound("d.wav"),
'e': QtGui.QSound("e.wav"),
}
notes['c'].play()
調用play()
不會阻塞,你不需要單獨運行QThread來運行它們。 您也可以在同一個QSound對象上多次調用play,但它的缺點是無法停止所有多個流。 他們將不得不發揮出來。 如果此方法產生的性能可接受性比您所做的要好。 您只需將鋼琴按鈕中的clicked
信號連接到正確鍵的play
槽即可。
聲子
如果QSound最終會產生太多延遲,那么下一步就是嘗試Phonon。 同樣,為了減少磁盤IO和對象創建的開銷,您需要預先創建這些媒體對象。 您不能使用單個媒體對象同時播放多個流。 因此,您必須選擇是否要嘗試為每個聲音創建一個媒體對象,或者使用一種媒體對象池。 要做一個小的媒體對象池,它需要你抓一個免費的,將其源設置為適當的媒體源對象,然后播放。 一旦完成,它將不得不返回池中。
使用Phonon的級別低於QSound,因此單個媒體對象在調用播放時不能多次播放相同的聲音。 它將忽略后續卡列斯play
在播放狀態,如果它已經。 無論如何,基本方法可能是創建一個Key類來幫助組織一個玩家的實體:
class Key(QtCore.QObject):
def __init__(self, soundFile, parent=None):
super(Key, self).__init__(parent)
self.soundFile = soundFile
self.mediaObject = Phonon.MediaObject(self)
self._audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
self._path = Phonon.createPath(self.mediaObject, self._audioOutput)
self.mediaSource = Phonon.MediaSource(soundFile)
self.mediaObject.setCurrentSource(self.mediaSource)
def play(self):
self.mediaObject.stop()
self.mediaObject.seek(0)
self.mediaObject.play()
這將再次讓你幾乎回到QSound的狀態,除了不同之處在於不止一次調用play()
會重新調整聲音,而不是將它們放在彼此之上:
notes = {
'c': Key("c.wav"),
'd': Key("d.wav"),
'e': Key("e.wav"),
}
notes['c'].play()
具有來自相同源的並發流的聲子
我提到了一個媒體對象池,你可以使用它來播放多個並發聲音。 雖然我不會進入該領域,但我可以提出一種簡單的方法來讓你的密鑰同時播放,因為你必須一次打開更多資源,但現在更容易運行,因此效率可能會低一些。
簡單的方法是每個鍵使用一個小的預定媒體對象池,並在每次調用play
時通過播放它們進行旋轉
from collections import deque
class Key(QtCore.QObject):
POOL_COUNT = 3
def __init__(self, soundFile, parent=None):
super(Key, self).__init__(parent)
self.soundFile = soundFile
self.resourcePool = deque()
mediaSource = Phonon.MediaSource(soundFile)
for i in xrange(self.POOL_COUNT):
mediaObject = Phonon.MediaObject(self)
audioOutput = Phonon.AudioOutput(Phonon.MusicCategory, self)
Phonon.createPath(mediaObject, audioOutput)
mediaObject.setCurrentSource(mediaSource)
self.resourcePool.append(mediaObject)
def play(self):
self.resourcePool.rotate(1)
m = self.resourcePool[0]
m.stop()
m.seek(0)
m.play()
我們在這里做的是創建了一個deque ,它具有非常方便的能力來旋轉列表n-amount。 所以在init中,我們從同一個源創建3個媒體對象並將它們放在我們的雙端隊列中。 然后,每次調用play時,我們將deque旋轉一次並取出第一個索引並播放它。 這將為您提供3個並發流。
此時,如果延遲仍然存在問題,那么您可能需要調查在應用程序啟動時將所有音頻加載到QBuffer中,然后將它們從內存中用於聲音。 我不太了解聲子源,知道當你從文件創建源時它是否已經將整個文件加載到內存中,或者它總是出現在磁盤上。 但如果它總是出現在磁盤上,減少這個IO將是再次減少延遲的方法。
希望這完全回答你的問題!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.