Companion custom functions sound playback fixes (#6000)

* [CompanionIcon] Can now add multiple image files to icon for representing different UI states (checked/active/etc).

* [Companion][CustomFunctionsPanel] Fix sound file locked after playback (fixes #5222, fixes #5997); Refactor play button (fixes #4929); Create new QMediaPlayer on each play and load in "LowLatency" mode instead of stream.
This commit is contained in:
Max Paperno
2018-06-29 08:04:45 +02:00
committed by Bertrand Songis
parent cb19949a82
commit 2eec091ac0
4 changed files with 78 additions and 74 deletions
+9 -4
View File
@@ -97,12 +97,17 @@ static QString iconThemeFolder(int theme_set)
}
CompanionIcon::CompanionIcon(const QString &baseimage)
{
addImage(baseimage);
}
void CompanionIcon::addImage(const QString & baseimage, Mode mode, State state)
{
const QString theme = iconThemeFolder(g.theme());
addFile(":/themes/"+theme+"/16/"+baseimage, QSize(16,16));
addFile(":/themes/"+theme+"/24/"+baseimage, QSize(24,24));
addFile(":/themes/"+theme+"/32/"+baseimage, QSize(32,32));
addFile(":/themes/"+theme+"/48/"+baseimage, QSize(48,48));
addFile(":/themes/"+theme+"/16/"+baseimage, QSize(16,16), mode, state);
addFile(":/themes/"+theme+"/24/"+baseimage, QSize(24,24), mode, state);
addFile(":/themes/"+theme+"/32/"+baseimage, QSize(32,32), mode, state);
addFile(":/themes/"+theme+"/48/"+baseimage, QSize(48,48), mode, state);
}
+1
View File
@@ -55,6 +55,7 @@ extern const QColor colors[CPN_MAX_CURVES];
class CompanionIcon: public QIcon {
public:
CompanionIcon(const QString &baseimage);
void addImage(const QString &baseimage, Mode mode = Normal, State state = Off);
};
class GVarGroup: public QObject {
+62 -67
View File
@@ -109,6 +109,7 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model,
s1.report("get scripts");
CompanionIcon playIcon("play.png");
playIcon.addImage("stop.png", QIcon::Normal, QIcon::On);
QStringList headerLabels;
headerLabels << "#" << tr("Switch") << tr("Action") << tr("Parameters") << tr("Enable");
@@ -209,12 +210,12 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model,
paramLayout->addWidget(fswtchBLcolor[i]);
connect(fswtchBLcolor[i], SIGNAL(sliderReleased()), this, SLOT(customFunctionEdited()));
playBT[i] = new QPushButton(this);
playBT[i]->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
playBT[i] = new QToolButton(this);
playBT[i]->setProperty("index", i);
playBT[i]->setIcon(playIcon);
playBT[i]->setCheckable(true);
paramLayout->addWidget(playBT[i]);
connect(playBT[i], SIGNAL(pressed()), this, SLOT(playMusic()));
connect(playBT[i], &QToolButton::clicked, this, &CustomFunctionsPanel::toggleSound);
QHBoxLayout * repeatLayout = new QHBoxLayout();
tableLayout->addLayout(i, 4, repeatLayout);
@@ -248,6 +249,8 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model,
CustomFunctionsPanel::~CustomFunctionsPanel()
{
if (mediaPlayer)
stopSound(mediaPlayerCurrent);
}
void CustomFunctionsPanel::setDataModels()
@@ -272,78 +275,70 @@ void CustomFunctionsPanel::setDataModels()
void CustomFunctionsPanel::onMediaPlayerStateChanged(QMediaPlayer::State state)
{
if (!lock) {
lock = true;
if (state==QMediaPlayer::StoppedState || state==QMediaPlayer::PausedState) {
mediaPlayer->stop();
if (mediaPlayerCurrent >= 0) {
playBT[mediaPlayerCurrent]->setIcon(CompanionIcon("play.png"));
mediaPlayerCurrent = -1;
}
}
lock = false;
}
if (state != QMediaPlayer::PlayingState)
stopSound(mediaPlayerCurrent);
}
void CustomFunctionsPanel::onMediaPlayerError(QMediaPlayer::Error error)
{
if (!lock) {
lock = true;
if (mediaPlayerCurrent >= 0) {
playBT[mediaPlayerCurrent]->setIcon(CompanionIcon("play.png"));
mediaPlayerCurrent = -1;
}
lock = false;
stopSound(mediaPlayerCurrent);
QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Error occurred while trying to play sound, possibly the file is already opened. (Err: %1 [%2])").arg(mediaPlayer ? mediaPlayer->errorString() : "").arg(error));
}
bool CustomFunctionsPanel::playSound(int index)
{
QString path = g.profile[g.id()].sdPath();
if (!QDir(path).exists())
return false; // unlikely
if (mediaPlayer)
stopSound(mediaPlayerCurrent);
if (firmware->getCapability(VoicesAsNumbers)) { // AVR
path.append(QString("/%1.wav").arg(int(fswtchParam[index]->value()), 4, 10, QChar('0')));
}
else {
QString lang(generalSettings.ttsLanguage);
if (lang.isEmpty())
lang = "en";
path.append(QString("/SOUNDS/%1/%2.wav").arg(lang).arg(fswtchParamArmT[index]->currentText()));
}
if (!QFileInfo::exists(path) || !QFileInfo(path).isReadable()) {
QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Unable to find or open sound file:\n%1").arg(path));
return false;
}
mediaPlayer = new QMediaPlayer(this, QMediaPlayer::LowLatency);
mediaPlayer->setMedia(QUrl::fromLocalFile(path));
connect(mediaPlayer, &QMediaPlayer::stateChanged, this, &CustomFunctionsPanel::onMediaPlayerStateChanged);
connect(mediaPlayer, static_cast<void(QMediaPlayer::*)(QMediaPlayer::Error)>(&QMediaPlayer::error), this, &CustomFunctionsPanel::onMediaPlayerError);
mediaPlayerCurrent = index;
mediaPlayer->play();
return true;
}
void CustomFunctionsPanel::stopSound(int index)
{
if (index > -1 && index < (int)DIM(playBT))
playBT[index]->setChecked(false);
mediaPlayerCurrent = -1;
if (mediaPlayer) {
disconnect(mediaPlayer, 0, this, 0);
mediaPlayer->stop();
mediaPlayer->deleteLater();
mediaPlayer = nullptr;
}
}
void CustomFunctionsPanel::playMusic()
void CustomFunctionsPanel::toggleSound(bool play)
{
if (!mediaPlayer) {
mediaPlayer = new QMediaPlayer(this);
connect(mediaPlayer, SIGNAL(stateChanged(QMediaPlayer::State)), this, SLOT(onMediaPlayerStateChanged(QMediaPlayer::State)));
connect(mediaPlayer, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(onMediaPlayerError(QMediaPlayer::Error)));
}
int index = sender()->property("index").toInt();
QString path = g.profile[g.id()].sdPath();
QDir qd(path);
QString track;
if (qd.exists()) {
if (firmware->getCapability(VoicesAsNumbers)) {
track = path + QString("/%1.wav").arg(int(fswtchParam[index]->value()), 4, 10, (const QChar)'0');
}
else {
path.append("/SOUNDS/");
QString lang = generalSettings.ttsLanguage;
if (lang.isEmpty())
lang = "en";
path.append(lang);
if (fswtchParamArmT[index]->currentText() != "----") {
track = path + "/" + fswtchParamArmT[index]->currentText() + ".wav";
}
}
QFile file(track);
if (!file.exists()) {
QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Unable to find sound file %1!").arg(track));
return;
}
if (mediaPlayerCurrent == index) {
mediaPlayer->stop();
playBT[index]->setIcon(CompanionIcon("play.png"));
mediaPlayerCurrent = -1;
}
else {
if (mediaPlayerCurrent >= 0) {
playBT[mediaPlayerCurrent]->setIcon(CompanionIcon("play.png"));
}
mediaPlayerCurrent = index;
mediaPlayer->setMedia(QUrl::fromLocalFile(track));
mediaPlayer->play();
playBT[index]->setIcon(CompanionIcon("stop.png"));
}
}
if (!sender() || !sender()->property("index").isValid())
return;
const int index = sender()->property("index").toInt();
if (play)
playBT[index]->setChecked(playSound(index));
else
stopSound(index);
}
#define CUSTOM_FUNCTION_NUMERIC_PARAM (1<<0)
+6 -3
View File
@@ -21,10 +21,11 @@
#ifndef _CUSTOMFUNCTIONS_H_
#define _CUSTOMFUNCTIONS_H_
#include <QMediaPlayer>
#include "modeledit.h"
#include "eeprominterface.h"
#include <QMediaPlayer>
class RawSwitchFilterItemModel;
class TimerEdit;
@@ -66,7 +67,9 @@ class CustomFunctionsPanel : public GenericPanel
void fsw_customContextMenuRequested(QPoint pos);
void refreshCustomFunction(int index, bool modified=false);
void onChildModified();
void playMusic();
bool playSound(int index);
void stopSound(int index);
void toggleSound(bool play);
void onMediaPlayerStateChanged(QMediaPlayer::State state);
void onMediaPlayerError(QMediaPlayer::Error error);
void fswDelete();
@@ -90,7 +93,7 @@ class CustomFunctionsPanel : public GenericPanel
QCheckBox * fswtchParamGV[CPN_MAX_SPECIAL_FUNCTIONS];
QDoubleSpinBox * fswtchParam[CPN_MAX_SPECIAL_FUNCTIONS];
TimerEdit * fswtchParamTime[CPN_MAX_SPECIAL_FUNCTIONS];
QPushButton * playBT[CPN_MAX_SPECIAL_FUNCTIONS];
QToolButton * playBT[CPN_MAX_SPECIAL_FUNCTIONS];
QComboBox * fswtchParamT[CPN_MAX_SPECIAL_FUNCTIONS];
QComboBox * fswtchParamArmT[CPN_MAX_SPECIAL_FUNCTIONS];
QCheckBox * fswtchEnable[CPN_MAX_SPECIAL_FUNCTIONS];