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!