Я переписал этот ответ, так как думаю, что ваш вопрос начал расходиться
Сначала рассмотрим примеры кода
В вашем первом примере с PlayThread вы запускаете новый поток каждый раз, когда хотите воспроизвести клавишу, после чего необходимо полностью настроить медиаплеер, открыть исходный файл и затем воспроизвести. Это определенно вызывает у вас накладные расходы.
Во втором примере вы передаете метод run()
, который в основном приводит к завершению потока сразу после его запуска. И тогда вы напрямую вызываете play () для этого QThread. По сути, вы используете QThread как базовый класс QObject и вызываете play в том же основном потоке. Я также не понимаю, почему вы создаете MediaSource из MediaSource (избыточный?). Но он заменяет звук при каждом вызове play, поэтому вы слышите его перезапуск.
Я не думаю, что вам действительно нужны QThreads для этого.
QSound
На более высоком уровне вы можете использовать QSound. Чтобы уменьшить возможную задержку, не следует использовать статический метод play()
для запуска файла на лету. Вместо этого вы должны предварительно создать эти объекты QSound при запуске вашего приложения:
notes = {
'c': QtGui.QSound("c.wav"),
'd': QtGui.QSound("d.wav"),
'e': QtGui.QSound("e.wav"),
}
notes['c'].play()
Вызов play()
не заблокирует, и вам не нужен QThread отдельно для их запуска. Вы также можете вызывать play несколько раз для одного и того же объекта QSound, но его недостатком является невозможность остановить все несколько потоков. Им придется разыграть. Если этот метод приводит к приемлемой производительности, чем вы. Вы просто подключите сигнал clicked
от кнопки пианино к гнезду play
соответствующей клавиши.
Phonon
Если QSound в итоге выдает слишком много лагов, то следующим шагом будет попытка Phonon. Опять же, чтобы уменьшить накладные расходы на дисковый ввод-вывод и создание объектов, вам необходимо предварительно создать эти мультимедийные объекты. Вы не можете использовать один медиа-объект для одновременного воспроизведения нескольких потоков. Таким образом, вы должны выбрать, хотите ли вы создать один медиа-объект для каждого звука, или использовать своего рода пул медиа-объектов. Чтобы создать небольшой пул медиа-объекта, потребуется, чтобы вы взяли бесплатный объект, установили в качестве источника соответствующий объект медиа-источника, а затем воспроизвели. Как только это закончено, это должно было бы быть возвращено в бассейн.
Использование Phonon ниже уровня QSound, поэтому один медиа-объект не может воспроизводить один и тот же звук несколько раз при вызове play. Он будет игнорировать последующие вызовы до play
, если он уже находится в состоянии воспроизведения. Несмотря на это, основным подходом может быть создание класса 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()
Это снова вернет вас почти к тому, чтобы быть похожим на QSound, за исключением разницы в том, что при вызове play()
более одного раза звук будет сброшен снова вместо воспроизведения их друг над другом:
notes = {
'c': Key("c.wav"),
'd': Key("d.wav"),
'e': Key("e.wav"),
}
notes['c'].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()
Мы создали deque , который обладает очень удобной способностью поворачивать список на n-сумму. Итак, в init мы создаем 3 медиа-объекта из одного источника и помещаем их в нашу deque. Затем, каждый раз, когда вы вызываете игру, мы поворачиваем деку на единицу, берем первый индекс и воспроизводим его. Это даст вам 3 одновременных потока.
На этом этапе, если задержка все еще остается проблемой, вам, возможно, придется изучить загрузку всех ваших аудио в QBuffer в начале вашего приложения, а затем использовать их из памяти в фонон.Я не знаю достаточно об источнике фононов, чтобы знать, загружает ли он весь файл в памяти уже при создании источника из файла, или он всегда выходит на диск.Но если он всегда выходит на диск, уменьшение этого ввода-вывода будет способом снова уменьшить лаг.
Надеюсь, это полностью ответит на ваш вопрос!