play sound file in PyQt

2024/10/6 12:33:24

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.

This is how I am currently using Phonon:

def Playnote(self,note_id):global note    note = note_idself.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 noteself.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
Answer

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. 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. And then you are directly calling play() on that QThread. Essentially what you are doing is using the QThread like a basic QObject class, and calling play within the same main thread. I also don't understand why you create a MediaSource from a MediaSource (redundant?). 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.

QSound

At the higher level, you can use 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. Instead you should pre-create these QSound objects at the startup of your application:

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. 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. 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.

Phonon

If QSound does end up producing too much lag, then your next step would be to try Phonon. Again, to reduce the overhead of disk IO and object creation, you would need to pre-create these media objects. 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. It will ignore subsequent calles to play if its already in a play state. Regardless, the basic approach might be to create a Key class to help organize the entity of a player:

class Key(QtCore.QObject):def __init__(self, soundFile, parent=None):super(Key, self).__init__(parent)self.soundFile = soundFileself.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:

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

from collections import dequeclass Key(QtCore.QObject):POOL_COUNT = 3def __init__(self, soundFile, parent=None):super(Key, self).__init__(parent)self.soundFile = soundFileself.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. So in the init, we create 3 media objects from the same source and place them in our deque. Then, every time you call play, we rotate the deque by one and take the first index and play it. This will give you 3 concurrent streams.

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. 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.

Hope this completely answers your question!

https://en.xdnf.cn/q/70372.html

Related Q&A

translating named list vectors from R into rpy2 in Python?

What is the equivalent of the following R code in Rpy2 in python?Var1 = c("navy", "darkgreen") names(Var1) = c("Class1", "Class2") ann_colors = list(Var1 = Var1…

Issue parsing multiline JSON file using Python

I am trying to parse a JSON multiline file using json library in Python 2.7. A simplified sample file is given below:{ "observations": {"notice": [{"copyright": "Copy…

timezone aware vs. timezone naive in python

I am working with datetime objects in python. I have a function that takes a time and finds the different between that time and now. def function(past_time):now = datetime.now()diff = now - past_timeWh…

How to return a value from Python script as a Bash variable?

This is a summary of my code:# import whateverdef createFolder():#someCodevar1=Gdrive.createFolder(name)return var1 def main():#someCodevar2=createFolder()return var2if __name__ == "__main__"…

How to align text to the right in ttk Treeview widget?

I am using a ttk.Treeview widget to display a list of Arabic books. Arabic is a right-to-left language, so the text should be aligned to the right. The justify option that is available for Label and o…

ImportError: cannot import name RemovedInDjango19Warning

Im on Django 1.8.7 and Ive just installed Django-Allauth by cloning the repo and running pip install in the apps directory in my webapp on the terminal. Now when I run manage.py migrate, I get this err…

How does Yahoo Finance calculate Adjusted Close stock prices?

Heres how Yahoo Finance apparently calculates Adjusted Close stock prices:https://help.yahoo.com/kb/adjusted-close-sln28256.htmlFrom this, I understand that a constant factor is applied to the unadjust…

Celery design help: how to prevent concurrently executing tasks

Im fairly new to Celery/AMQP and am trying to come up with a task/queue/worker design to meet the following requirements.I have multiple types of "per-user" tasks: e.g., TaskA, TaskB, TaskC. …

Google App Engine - Using Search API Python with list fields

Im using ndb.Model. The Search API has the following field classes:TextField : plain textHtmlField : HTML formatted textAtomField : a string which is treated as a single tokenNumberField : a numeric v…

Handling PyMySql exceptions - Best Practices

My question regards exception best practices. Ill present my question on a specific case with PyMySQL but it regards errors handling in general. I am using PyMySQL and out of the many possible exceptio…