Reproducir archivo de sonido en PyQt

He desarrollado un software en PyQt que reproduce sonido. Estoy usando la Biblioteca de Phonon para reproducir el sonido, pero tiene cierto retraso. Entonces, ¿cómo puedo reproducir un archivo de sonido en PyQt sin usar la Biblioteca de Phonon?

Así es como actualmente estoy usando 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() 

Ahora el retraso se reduce. Pero el problema es que estoy presionando dos o más teclas en poco tiempo, lo que representa los gastos generales de la nueva nota y detiene la nota anterior. Necesito tocar la nota anterior hasta que termine.

 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 

He reescrito esta respuesta porque creo que su pregunta ha comenzado a divergir

Primero, direccionando sus ejemplos de código.

En su primer ejemplo de PlayThread, está comenzando una nueva secuencia cada vez que quiere tocar una tecla, que luego debe configurar completamente un reproductor multimedia, abrir el archivo de origen y luego reproducir. Esto definitivamente te está causando una sobrecarga.

En su segundo ejemplo, está pasando el método run() que básicamente hace que el hilo finalice justo después de iniciarlo. Y luego estás llamando directamente a play () en ese QThread. Esencialmente, lo que estás haciendo es usar el QThread como una clase básica de QObject y llamar al juego dentro del mismo hilo principal. Tampoco entiendo por qué creas un MediaSource a partir de un MediaSource (¿redundante?). Pero está reemplazando el sonido cada vez que llamas play, por eso lo escuchas reiniciar.

No creo que realmente necesites QThreads para esto.

QSonido

En el nivel superior, puedes usar QSound. Para reducir la cantidad de retraso en el que podría incurrir, no debería usar el método estático de play() para iniciar un archivo sobre la marcha. En su lugar, debe crear previamente estos objetos QSound al iniciar su aplicación:

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

Calling play() no se bloqueará y no necesitas un QThread por separado para ejecutar estos. También puede llamar al juego varias veces en el mismo objeto QSound, pero tiene el inconveniente de no poder detener todas las secuencias múltiples. Tendrán que jugar. Si este método da como resultado un rendimiento aceptable del que está hecho. Simplemente conecte la señal del botón del piano a la ranura de play de la tecla correcta.

Fonón

Si QSound termina produciendo demasiado retraso, entonces su próximo paso sería probar Phonon. Nuevamente, para reducir la sobrecarga de la E / S del disco y la creación de objetos, deberá crear previamente estos objetos multimedia. No puede usar un solo objeto multimedia para reproducir múltiples transmisiones al mismo tiempo. Por lo tanto, debe elegir si desea probar y crear un objeto multimedia para cada sonido, o usar un tipo de grupo de objetos multimedia. Para hacer un pequeño grupo de objetos de medios, sería necesario que tomes uno libre, configuras su fuente en el objeto de origen de medios adecuado, y luego juegas. Una vez terminado, habrá que devolverlo a la piscina.

El uso de Phonon es un nivel inferior al de QSound, por lo que un solo objeto multimedia no puede reproducir el mismo sonido varias veces al llamar a la reproducción. Ignorará las calles posteriores para play si ya está en un estado de juego. En cualquier caso, el enfoque básico podría ser crear una clase clave para ayudar a organizar la entidad de un jugador:

 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() 

Esto volverá a hacer que vuelvas al punto de ser como QSound, excepto que la diferencia es que llamar a play() más de una vez restablecerá el sonido otra vez en lugar de reproducirlos uno encima del otro:

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

Phonon con flujos concurrentes de la misma fuente.

Mencioné tener un grupo de objetos multimedia que utilizarías para reproducir múltiples sonidos simultáneos. Si bien no voy a entrar en esa área, puedo sugerir una forma sencilla de hacer que sus teclas se reproduzcan simultáneamente, lo que podría ser un poco menos eficiente, ya que tiene que abrir más recursos a la vez, pero más fácil de ejecutar por ahora.

El enfoque simple es utilizar un pequeño grupo predeterminado de objetos multimedia por tecla y rotarlos a través de su reproducción cada vez que 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() 

Lo que hicimos aquí fue crear un deque , que viene con una habilidad realmente útil para rotar la lista en n cantidades. Así que en el inicio, creamos 3 objetos de medios de la misma fuente y los colocamos en nuestro deque. Luego, cada vez que llamas a jugar, rotamos el deque en uno, tomamos el primer índice y lo reproducimos. Esto le dará 3 secuencias concurrentes.

En este punto, si el retraso sigue siendo un problema, es posible que tenga que investigar cómo cargar todo el audio en QBuffer al inicio de la aplicación y luego usarlos de la memoria al fonón. No sé lo suficiente acerca de la fuente de fonones para saber si ya carga todo el archivo en la memoria cuando crea una fuente a partir de un archivo, o si siempre sale al disco. Pero si siempre sale al disco, reducir este IO sería la forma de reducir nuevamente el retraso.

Espero que esto responda completamente a tu pregunta!