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:
committed by
Bertrand Songis
parent
cb19949a82
commit
2eec091ac0
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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];
|
||||
|
||||
Reference in New Issue
Block a user