简体   繁体   English

在PyQt中播放声音文件

[英]play sound file in PyQt

I've developed a software in PyQt which plays sound.I'm using Phonon Library to play the sound but it has some lag.So how can I play a sound file in PyQt without using Phonon Library. 我在PyQt中开发了一个播放声音的软件。我正在使用Phonon Library播放声音,但它有一些滞后。所以如何在不使用Phonon Library的情况下在PyQt中播放声音文件。

This is how I am currently using Phonon: 这就是我目前使用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()

Now the lag is reduced. 现在滞后减少了。 But the problem is is I'm pressing two or more keys in short time that's the new note overheads and stops the previous note. 但问题是我在短时间内按下两个或更多键,这是新音符开销并停止前一个音符。 I need to play the previous note till it ends. 我需要播放前一个音符直到它结束。

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

I have rewritten this answer since I think your question has begun to diverge 我已经重写了这个答案,因为我认为你的问题已经开始出现分歧

First, addressing your code examples 首先,解决您的代码示例

In your first PlayThread example, you are starting a new thread every single time you want to play a key, which then has to completely set up a media player, and open the source file, and then play. 在您的第一个PlayThread示例中,您每次要播放一个键时都会启动一个新线程,然后必须完全设置一个媒体播放器,然后打开源文件,然后播放。 This is definitely causing you overhead. 这肯定会导致你的开销。

In your second example, you are passing on the run() method which basically makes the thread end right after you start it. 在第二个示例中,您传递的是run()方法,该方法基本上使线程在启动后立即结束。 And then you are directly calling play() on that QThread. 然后你直接在那个QThread上调用play()。 Essentially what you are doing is using the QThread like a basic QObject class, and calling play within the same main thread. 基本上你正在做的是使用QThread像一个基本的QObject类,并在同一主线程中调用play。 I also don't understand why you create a MediaSource from a MediaSource (redundant?). 我也不明白你为什么要从MediaSource创建一个MediaSource(冗余?)。 But its replacing the sound each time you call play, which is why you hear it restarting. 但是每次你打电话都会取代声音,这就是你重新开始播放的原因。

I do not think you actually need QThreads for this. 我不认为你真的需要QThreads。

QSound QSound的

At the higher level, you can use QSound. 在更高级别,您可以使用QSound。 To reduce the amount of lag you could potentially incur you should not use the static method of play() to start a file on the fly. 为了减少你可能引发的延迟,你不应该使用play()的静态方法来动态启动文件。 Instead you should pre-create these QSound objects at the startup of your application: 相反,您应该在应用程序启动时预先创建这些QSound对象:

notes = {
    'c': QtGui.QSound("c.wav"),
    'd': QtGui.QSound("d.wav"),
    'e': QtGui.QSound("e.wav"),
}

notes['c'].play()

Calling play() will not block and you do not need a QThread separately to run these. 调用play()不会阻塞,你不需要单独运行QThread来运行它们。 You can also call play multiple times on the same QSound object, but it has the downside of not being able to stop all of the multiple streams. 您也可以在同一个QSound对象上多次调用play,但它的缺点是无法停止所有多个流。 They will have to play out. 他们将不得不发挥出来。 If this method results in acceptable performance than you are done. 如果此方法产生的性能可接受性比您所做的要好。 You would simply just connect the clicked signal from the piano button to the play slot of the proper key. 您只需将钢琴按钮中的clicked信号连接到正确键的play槽即可。

Phonon 声子

If QSound does end up producing too much lag, then your next step would be to try Phonon. 如果QSound最终会产生太多延迟,那么下一步就是尝试Phonon。 Again, to reduce the overhead of disk IO and object creation, you would need to pre-create these media objects. 同样,为了减少磁盘IO和对象创建的开销,您需要预先创建这些媒体对象。 You cannot use a single media object to play multiple streams at the same time. 您不能使用单个媒体对象同时播放多个流。 So you have to choose whether you want to try and create one media object for every sound, or use a kind of pool of media objects. 因此,您必须选择是否要尝试为每个声音创建一个媒体对象,或者使用一种媒体对象池。 To do a small pool of media object, it would require that you grab a free one, set its source to the proper media source object, then play. 要做一个小的媒体对象池,它需要你抓一个免费的,将其源设置为适当的媒体源对象,然后播放。 Once it is finished it would have to be returned to the pool. 一旦完成,它将不得不返回池中。

Using Phonon is lower level than QSound, so a single media object cannot play the same sound multiple times when calling play. 使用Phonon的级别低于QSound,因此单个媒体对象在调用播放时不能多次播放相同的声音。 It will ignore subsequent calles to play if its already in a play state. 它将忽略后续卡列斯play在播放状态,如果它已经。 Regardless, the basic approach might be to create a Key class to help organize the entity of a player: 无论如何,基本方法可能是创建一个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()

This would again get you almost back to the point of being like QSound, except for the difference being that calling play() more than once would reset the sound over again instead of playing them on top of each other: 这将再次让你几乎回到QSound的状态,除了不同之处在于不止一次调用play()会重新调整声音,而不是将它们放在彼此之上:

notes = {
    'c': Key("c.wav"),
    'd': Key("d.wav"),
    'e': Key("e.wav"),
}

notes['c'].play()

Phonon with concurrent streams from same source 具有来自相同源的并发流的声子

I mentioned having a pool of media objects that you would use to play multiple concurrent sounds. 我提到了一个媒体对象池,你可以使用它来播放多个并发声音。 While I won't get into that area, I can suggest a simple way to get your keys playing concurrently that might be a little less efficient since you have to open more resources at once, but easier to get running for now. 虽然我不会进入该领域,但我可以提出一种简单的方法来让你的密钥同时播放,因为你必须一次打开更多资源,但现在更容易运行,因此效率可能会低一些。

The simple approach is to use a small predetermined pool of media objects per key, and rotate through playing them each time you call 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()

What we did here is created a deque , which comes with a really handy ability to rotate the list by n-amount. 我们在这里做的是创建了一个deque ,它具有非常方便的能力来旋转列表n-amount。 So in the init, we create 3 media objects from the same source and place them in our deque. 所以在init中,我们从同一个源创建3个媒体对象并将它们放在我们的双端队列中。 Then, every time you call play, we rotate the deque by one and take the first index and play it. 然后,每次调用play时,我们将deque旋转一次并取出第一个索引并播放它。 This will give you 3 concurrent streams. 这将为您提供3个并发流。

At this point, if the lag is still a problem then you might have to investigate loading all your audio into QBuffer's at the start of your app, and then use them from memory to phonon. 此时,如果延迟仍然存在问题,那么您可能需要调查在应用程序启动时将所有音频加载到QBuffer中,然后将它们从内存中用于声音。 I don't know enough about the phonon source to know if it loads the whole file in memory already when you create a source from file, or if its always going out to disk. 我不太了解声子源,知道当你从文件创建源时它是否已经将整个文件加载到内存中,或者它总是出现在磁盘上。 But if it is always going out to disk, reducing this IO would be the way to again reduce lag. 但如果它总是出现在磁盘上,减少这个IO将是再次减少延迟的方法。

Hope this completely answers your question! 希望这完全回答你的问题!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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