繁体   English   中英

在PyQt中播放声音文件

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM