7 Stimmen

Sounddatei in PyQt abspielen

Ich habe eine Software in PyQt entwickelt, die sound.I'm mit Phonon Library, um den Ton zu spielen, aber es hat einige lag.So wie kann ich eine Sound-Datei in PyQt ohne Verwendung von Phonon Library spielen.

So verwende ich Phonon derzeit:

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

Jetzt ist die Verzögerung geringer. Aber das Problem ist, wenn ich zwei oder mehr Tasten in kurzer Zeit drücke, dass die neue Note übersteuert und die vorherige Note stoppt. Ich muss die vorherige Note spielen, bis sie endet.

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

16voto

jdi Punkte 86608

Ich habe diese Antwort umgeschrieben, da ich glaube, dass Ihre Frage zu divergieren beginnt

Zunächst zu den Codebeispielen

In Ihrem ersten PlayThread-Beispiel starten Sie jedes Mal, wenn Sie eine Taste abspielen wollen, einen neuen Thread, der dann einen Media Player vollständig einrichten, die Quelldatei öffnen und dann abspielen muss. Dies verursacht definitiv Overhead.

In Ihrem zweiten Beispiel geben Sie die run() Methode, die im Grunde dafür sorgt, dass der Faden direkt nach dem Start endet. Und dann rufen Sie direkt play() auf diesem QThread auf. Im Wesentlichen verwenden Sie den QThread wie eine grundlegende QObject-Klasse und rufen play innerhalb desselben Hauptthreads auf. Ich verstehe auch nicht, warum Sie eine MediaSource aus einer MediaSource erstellen (redundant?). Aber seine ersetzen den Ton jedes Mal, wenn Sie Play aufrufen, weshalb Sie hören, es neu starten.

Ich glaube nicht, dass Sie dafür QThreads benötigen.

QSound

Auf der höheren Ebene können Sie QSound verwenden. Um die mögliche Verzögerung zu reduzieren, sollten Sie nicht die statische Methode von play() um eine Datei im laufenden Betrieb zu starten. Stattdessen sollten Sie diese QSound-Objekte beim Starten Ihrer Anwendung vorab erstellen:

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

notes['c'].play()

Aufruf von play() werden nicht blockiert und Sie benötigen keinen separaten QThread, um diese auszuführen. Sie können das gleiche QSound-Objekt auch mehrmals abspielen lassen, aber das hat den Nachteil, dass Sie nicht in der Lage sind, alle mehreren Streams zu stoppen. Sie müssen sich abspielen. Wenn diese Methode zu einer akzeptablen Leistung führt, sind Sie fertig. Sie würden einfach die clicked Signal von der Klaviertaste zum play Schlitz des richtigen Schlüssels.

Phonon

Wenn QSound zu viel Verzögerung produziert, sollten Sie als nächstes Phonon ausprobieren. Auch hier müssen Sie die Medienobjekte im Voraus erstellen, um den Overhead durch Festplatten-IO und Objekterstellung zu reduzieren. Sie können ein einzelnes Medienobjekt nicht für die gleichzeitige Wiedergabe mehrerer Streams verwenden. Sie müssen sich also entscheiden, ob Sie versuchen wollen, für jeden Sound ein Medienobjekt zu erstellen, oder ob Sie eine Art Pool von Medienobjekten verwenden wollen. Um einen kleinen Pool von Medienobjekten zu erstellen, müssten Sie ein freies Objekt nehmen, seine Quelle auf das richtige Medienquellenobjekt setzen und dann abspielen. Nach Beendigung des Vorgangs müsste das Objekt wieder in den Pool zurückgegeben werden.

Die Verwendung von Phonon ist niedriger als die von QSound, so dass ein einzelnes Medienobjekt nicht denselben Sound mehrfach abspielen kann, wenn es die Wiedergabe aufruft. Es ignoriert nachfolgende Aufrufe von play wenn es sich bereits in einem Spielzustand befindet. Unabhängig davon könnte der grundlegende Ansatz darin bestehen, eine Schlüsselklasse zu erstellen, die dabei hilft, die Entität eines Spielers zu organisieren:

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

Damit wären Sie wieder fast so weit wie bei QSound, nur mit dem Unterschied, dass der Aufruf von play() mehr als einmal abspielt, wird der Ton wieder zurückgesetzt, anstatt sie übereinander abzuspielen:

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

notes['c'].play()

Phonon mit gleichzeitigen Streams aus derselben Quelle

Ich habe bereits erwähnt, dass Sie einen Pool von Medienobjekten haben, mit denen Sie mehrere Töne gleichzeitig abspielen können. Während ich nicht in diesem Bereich zu bekommen, kann ich einen einfachen Weg vorschlagen, um Ihre Tasten spielen gleichzeitig, dass vielleicht ein wenig weniger effizient, da Sie mehr Ressourcen auf einmal zu öffnen, aber einfacher zu bekommen läuft für jetzt.

Der einfache Ansatz besteht darin, einen kleinen vorbestimmten Pool von Medienobjekten pro Taste zu verwenden und diese bei jedem Aufruf von 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()

Was wir hier gemacht haben, ist eine deque , das die praktische Möglichkeit bietet, die Liste um n Beträge zu drehen. Im Init erstellen wir also 3 Medienobjekte aus derselben Quelle und legen sie in unserer Deque ab. Dann, jedes Mal, wenn du play aufrufst, rotieren wir die deque um eins und nehmen den ersten Index und spielen ihn ab. So erhalten Sie 3 gleichzeitige Streams.

Wenn die Verzögerung immer noch ein Problem ist, sollten Sie prüfen, ob Sie alle Audiodaten beim Start Ihrer Anwendung in QBuffer laden und diese dann aus dem Speicher in den Hörer übertragen können. Ich weiß nicht genug über die Phonon-Quelle, um zu wissen, ob sie die gesamte Datei bereits in den Speicher lädt, wenn Sie eine Quelle aus einer Datei erstellen, oder ob sie immer auf die Festplatte übertragen wird. Aber wenn es immer auf die Festplatte geht, wäre eine Reduzierung dieses IO der Weg, um die Verzögerung zu reduzieren.

Ich hoffe, dies beantwortet Ihre Frage vollständig!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X