在PyQ中播放声音文件

2024-05-15 08:20:54 发布

您现在位置:Python中文网/ 问答频道 /正文

我在PyQt中开发了一个可以播放声音的软件,我正在使用声子库来播放声音,但是它有一些延迟,所以我如何在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

Tags: self声音playinitdefmediapyqtnote
1条回答
网友
1楼 · 发布于 2024-05-15 08:20:54

我重写了这个答案,因为我认为你的问题已经开始出现分歧

首先,介绍代码示例

在第一个play thread示例中,每次要播放密钥时都要启动一个新线程,然后必须完全设置媒体播放器,打开源文件,然后播放。这肯定是你的开销。

在第二个示例中,您传递了run()方法,该方法基本上使线程在启动后立即结束。然后直接在那个QThread上调用play()。实际上,您所做的是像使用基本QObject类一样使用QThread,并在同一个主线程中调用play。我也不明白为什么要从MediaSource(冗余?)创建MediaSource。但每次你打电话给play时,它都会取代声音,这就是为什么你会听到它重新启动的原因。

我不认为你真的需要QThreads。

q声音

在更高的级别上,您可以使用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最终确实产生了太多的延迟,那么下一步就是尝试声子。同样,为了减少磁盘IO和对象创建的开销,您需要预先创建这些媒体对象。不能使用单个媒体对象同时播放多个流。因此,您必须选择是尝试为每个声音创建一个媒体对象,还是使用一种媒体对象池。要创建一个小的媒体对象池,需要获取一个空闲的媒体对象池,将其源设置为适当的媒体源对象,然后播放。一旦完成,就必须把它送回游泳池。

使用声子的级别低于QSound,因此单个媒体对象在调用play时不能多次播放同一声音。如果已经处于播放状态,它将忽略play的后续调用。无论如何,基本方法可能是创建一个键类来帮助组织播放器的实体:

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个数量。所以在init中,我们从同一个源创建3个媒体对象并将它们放在deque中。然后,每次你调用play,我们将deque旋转一次,取第一个索引并播放它。这将为您提供3个并发流。

在这一点上,如果延迟仍然是一个问题,那么您可能需要调查将所有音频加载到QBuffer的开始你的应用程序,然后使用他们从记忆到声子。我对声子源的了解还不够,不知道你从文件创建源时,它是否已经将整个文件加载到内存中,或者它是否总是输出到磁盘。但如果它总是输出到磁盘,那么减少这个IO将是再次减少延迟的方法。

希望这能完全回答你的问题!

相关问题 更多 >