2

I want to play sounds with QtMultimedia.

In the case of QMediaPlayer, I could play mp3 file , it is made from gTTS.

(I think it is okay but I don't like the file remains unless I excute codes for deleting it.)

I make a mp3 file with gTTS module and I want to play sounds with the buffer directly.

It seems that I can make a valid object but QAudioOutput doesn't do anything.

I seriarize the mp3 file into a database and fetch it when I like.

What is short of my code?

Here is the excerpt of my original Code.

In my original code, the buffer data is in Qt.UserRole + x and I can take them whenever.

The playlist is constucted with QTableWidget and QTableWidgetItem.

from PySide2 import QtCore
from PySide2 import QtWidgets
from PySide2 import QtMultimedia
import os
import PySide2
import sys

dirname = os.path.dirname(PySide2.__file__)
plugin_path = os.path.join(dirname, 'plugins', 'platforms')
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path

import gtts

def main():
    if QtWidgets.QApplication.instance() is not None:
        app = QtWidgets.QApplication.instance()
    else:
        app = QtWidgets.QApplication([])
    widget = QtWidgets.QTableWidget()
    widget.setColumnCount(2)
    text = "hello"

    lang = "en"
    onsei = gtts.gTTS(text=text, lang=lang)
    buf = QtCore.QBuffer()


    buf.setOpenMode( QtCore.QIODevice.WriteOnly)      
    onsei.write_to_fp(buf)
    buf.close()


    if not widget.rowCount():
        widget.setRowCount(1)
    else:
        widget.insertRow(1)  
    nitem = QtWidgets.QTableWidgetItem()
    nitem.setText(str(widget.rowCount()))
    item = QtWidgets.QTableWidgetItem()

    item.setText("{0}_tts_lang_{1}.mp3".format(text, lang))
    item.setData(QtCore.Qt.UserRole+1, buf.data())

    variant = item.data(QtCore.Qt.UserRole+1)
    format = QtMultimedia.QAudioFormat()
    format.setSampleRate(8000)
    format.setChannelCount(1)
    format.setSampleSize(16)
    format.setCodec("audio/pcm")
    format.setByteOrder(QtMultimedia.QAudioFormat.LittleEndian)
    format.setSampleType(QtMultimedia.QAudioFormat.UnSignedInt)

    buf = QtCore.QBuffer()
    buf.setData(variant)        

    buf.open(QtCore.QIODevice.ReadOnly)
    buf.seek(0)

    audio = QtMultimedia.QAudioOutput(format, app)
    audio.setVolume(0.5)
    audio.setBufferSize(buf.size())

    audio.start(buf)
    buf.close()


    print(67)
#    sys.exit(QtWidgets.QApplication.exec_())
    sys.exit()
if __name__ == "__main__":
    main()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Haru
  • 1,884
  • 2
  • 12
  • 30

1 Answers1

1

Instead of using QAudioOutput you could use QMediaPlayer:

import sys
import threading
import uuid

from PySide2 import QtCore, QtGui, QtWidgets, QtMultimedia

import gtts

IdentifierRole = QtCore.Qt.UserRole
DataRole = QtCore.Qt.UserRole + 1
DownLoadRole = QtCore.Qt.UserRole + 2
ActiveRole = QtCore.Qt.UserRole + 3


class BackgroundColorDelegate(QtWidgets.QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super().initStyleOption(option, index)
        color = None
        if index.data(DownLoadRole):
            color = QtGui.QColor("green")
        if index.data(ActiveRole):
            color = QtGui.QColor("red")
        if color:
            option.backgroundBrush = color


class DownLoader(QtCore.QObject):
    downloaded = QtCore.Signal(str, QtCore.QByteArray)

    def start(self, identifier, text, lang):
        threading.Thread(
            target=self._execute, args=(identifier, text, lang), daemon=True
        ).start()

    def _execute(self, identifier, text, lang):
        tts = gtts.gTTS(text=text, lang=lang)
        buf = QtCore.QBuffer()
        buf.open(QtCore.QBuffer.ReadWrite)
        tts.write_to_fp(buf)
        self.downloaded.emit(identifier, buf.data())


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.player = QtMultimedia.QMediaPlayer()
        self.current_buff = QtCore.QBuffer()

        self.tablewidget = QtWidgets.QTableWidget(
            0,
            2,
            selectionBehavior=QtWidgets.QAbstractItemView.SelectRows,
            editTriggers=QtWidgets.QAbstractItemView.NoEditTriggers,
        )
        delegate = BackgroundColorDelegate(self.tablewidget)
        self.tablewidget.setItemDelegateForColumn(0, delegate)
        self.tablewidget.itemClicked.connect(self.on_item_clicked)
        self.setCentralWidget(self.tablewidget)

        self.add_row("hello", "en")
        self.add_row("world", "en")

    def add_row(self, text, lang):
        it = QtWidgets.QTableWidgetItem("{0}_tts_lang_{1}.mp3".format(text, lang))
        identifier = str(uuid.uuid4())
        it.setData(IdentifierRole, identifier)
        downloader = DownLoader(self)
        downloader.start(identifier, text, lang)
        downloader.downloaded.connect(self.on_downloaded)
        downloader.downloaded.connect(downloader.deleteLater)

        row = self.tablewidget.rowCount()
        self.tablewidget.insertRow(row)
        self.tablewidget.setItem(row, 0, it)

    @QtCore.Slot(str, QtCore.QByteArray)
    def on_downloaded(self, identifier, data):
        model = self.tablewidget.model()
        indexes = model.match(
            model.index(0, 0), IdentifierRole, identifier, flags=QtCore.Qt.MatchExactly
        )
        if indexes:
            item = self.tablewidget.itemFromIndex(indexes[0])
            item.setData(DataRole, data)
            item.setData(DownLoadRole, True)

    @QtCore.Slot("QTableWidgetItem*")
    def on_item_clicked(self, item):
        self.player.stop()
        self.current_buff.close()
        data = item.data(DataRole)
        if not data:
            return
        self.current_buff.setData(data)
        self.current_buff.open(QtCore.QIODevice.ReadOnly)
        self.player.setMedia(QtMultimedia.QMediaContent(), self.current_buff)
        self.player.play()

        for row in range(self.tablewidget.rowCount()):
            it = self.tablewidget.item(row, 0)
            it.setData(ActiveRole, it is item)


def main():
    app = QtWidgets.QApplication.instance()
    if app is None:
        app = QtWidgets.QApplication([])

    w = MainWindow()
    w.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
eyllanesc
  • 235,170
  • 19
  • 170
  • 241