1

I have a QBuffer in RAM with a temporary wav file, and I want to let the user listen it from any point as many times as (s)he want. But, it only allows playing it once, and doesn't allow replaying it. If I play the audio from a file (QUrl.fromLocalFile), it can replay it. What's the difference? How to fix it?

1) To play the wav file from RAM I use the following code:

    data = b""
    with open(fname, "rb") as file:
        data = file.read()
    buf = QBuffer()
    buf.setData(data) #For debugging. Real buffer is filled differently.
    buf.open(QIODevice.ReadOnly);

    self.mediaPlayer=QMediaPlayer(self)
    self.mediaPlayer.setMedia(QMediaContent(),buf)

Then, if I call self.mediaplayer.play(), it will play the file to the end. But, all subsequent calls to self.mediaplayer.play() have no effect. This is not what I want.

2) If I init mediaplayer from a file, with:

self.mediaPlayer.setMedia(QMediaContent(QUrl.fromLocalFile("/home/me/myTestApp/test.wav")))

it works OK - if i call play() after the previous playback is over, QMediaPlayer just repeats the playback.

ekhumoro
  • 115,249
  • 20
  • 229
  • 336
al.zatv
  • 173
  • 1
  • 13

1 Answers1

2

A QBuffer is an io-device - once you've read it, you need to reset its position in order to read it again. So in your code you will need to do something like this:

    ...
    self._buffer = buf
    self.mediaPlayer=QMediaPlayer(self)
    self.mediaPlayer.setMedia(QMediaContent(), self._buffer)

def play(self):
    self._buffer.seek(0)
    self.mediaPlayer.play()

EDIT:

After some actual testing, I found that it is only necessary to keep a reference to the buffer in order to replay the audio. The script below is a complete example that works fine for me (on Linux, using the GStreamer backend):

import sys
from PyQt5 import QtCore, QtWidgets, QtMultimedia

class Window(QtWidgets.QWidget):
    def __init__(self):
        super(Window, self).__init__()
        self.buttonOpen = QtWidgets.QPushButton('Open', self)
        self.buttonOpen.clicked.connect(self.handleOpen)
        self.buttonPlay = QtWidgets.QPushButton('Play', self)
        self.buttonPlay.clicked.connect(self.handlePlay)
        layout = QtWidgets.QHBoxLayout(self)
        layout.addWidget(self.buttonOpen)
        layout.addWidget(self.buttonPlay)
        self.mediaPlayer = QtMultimedia.QMediaPlayer(self)
        self._buffer = QtCore.QBuffer()

    def handlePlay(self):
        if self.buttonPlay.text() == 'Play':
            self.buttonPlay.setText('Stop')
            # self._buffer.seek(0)
            self.mediaPlayer.play()
        else:
            self.buttonPlay.setText('Play')
            self.mediaPlayer.stop()

    def handleOpen(self):
        path, ok = QtWidgets.QFileDialog.getOpenFileName(
            self, filter='WAV Files (*.wav)')
        if ok:
            self._buffer.close()
            with open(path, 'rb') as stream:
                self._buffer.setData(stream.read())
            if self._buffer.open(QtCore.QIODevice.ReadOnly):
                self.mediaPlayer.setMedia(
                    QtMultimedia.QMediaContent(), self._buffer)

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 100, 200, 50)
    window.show()
    sys.exit(app.exec_())
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • No,it's not working. And,actually, I think your explanation is wrong because if you replaying a file, QMediaPlayer's implementation also must seek to start of the file. – al.zatv Oct 20 '17 at 08:05
  • @al.zatv. Yes, I admit I didn't test anything and it was mostly just a hunch. Anyway, I have added a fully working example to my answer. Can you please test it? – ekhumoro Oct 20 '17 at 14:27
  • Yes,it is working.I change yor solution a litle bit: I read it in constructor, and re-set it as a response to QMediaPlayer.stateChanged signal, if the state is set to 0 (stop state). It allow me to use the "play" button to continue both after pause in the middle of audio and after full stop. def onStateChanged(self): if self.mediaPlayer.state()==0: self.mediaPlayer.setMedia(QMediaContent(), self.wavBuf) There some problems with this solution: a) it feels like a hack; b)after moving to stop state, position is set to begining of the file, which is not convinient in my application. – al.zatv Oct 20 '17 at 15:02