TTKMusicPlayer  4.1.3.0
TTKMusicPlayer imitates Kugou UI, the music player uses of qmmp core library based on Qt for windows and linux
musicreplaygainwidget.cpp
Go to the documentation of this file.
2 #include "ui_musicreplaygainwidget.h"
3 #include "musicfileutils.h"
4 #include "musicwidgetheaders.h"
5 #include "musicqmmputils.h"
6 #include "musictoastlabel.h"
7 #include "ttksemaphoreloop.h"
8 
9 #include <QProcess>
10 #include <QPluginLoader>
11 
12 #include <qmmp/lightfactory.h>
13 
14 static constexpr int GAIN_DEFAULT = 89;
15 static constexpr const char *GAIN_TRACKDB = "Recommended \"Track\" dB change:";
16 static constexpr const char *GAIN_ALBUMDB = "Recommended \"Album\" dB change for all files:";
17 
19  : MusicAbstractTableWidget(parent)
20 {
21  setColumnCount(5);
22 
23  QHeaderView *headerView = horizontalHeader();
24  headerView->resizeSection(0, 332);
25  headerView->resizeSection(1, 60);
26  headerView->resizeSection(2, 60);
27  headerView->resizeSection(3, 60);
28  headerView->resizeSection(4, 60);
29 }
30 
31 
32 
34  : MusicAbstractMoveWidget(parent),
35  m_ui(new Ui::MusicReplayGainWidget),
36  m_replayGainWidget(nullptr),
37  m_currentIndex(-1)
38 {
39  m_ui->setupUi(this);
40  setFixedSize(size());
41  setAttribute(Qt::WA_DeleteOnClose);
42  setBackgroundLabel(m_ui->background);
43 
44  m_ui->topTitleCloseButton->setIcon(QIcon(":/functions/btn_close_hover"));
45  m_ui->topTitleCloseButton->setStyleSheet(TTK::UI::ToolButtonStyle04);
46  m_ui->topTitleCloseButton->setCursor(QCursor(Qt::PointingHandCursor));
47  m_ui->topTitleCloseButton->setToolTip(tr("Close"));
48  connect(m_ui->topTitleCloseButton, SIGNAL(clicked()), SLOT(close()));
49 
50  m_ui->mainViewWidget->setStyleSheet(TTK::UI::TabWidgetStyle01);
51 
52  m_ui->addFileButton->setIcon(QIcon(":/toolSets/btn_gain_add_file"));
53  m_ui->addFileButton->setIconSize(QSize(40, 40));
54  m_ui->addFileButton->setStyleSheet(TTK::UI::ToolButtonStyle04);
55  m_ui->addFileButton->setToolTip(tr("Add File"));
56  m_ui->addFileButton->setCursor(QCursor(Qt::PointingHandCursor));
57 
58  m_ui->addFilesButton->setIcon(QIcon(":/toolSets/btn_gain_add_files"));
59  m_ui->addFilesButton->setIconSize(QSize(40, 40));
60  m_ui->addFilesButton->setStyleSheet(TTK::UI::ToolButtonStyle04);
61  m_ui->addFilesButton->setToolTip(tr("Add Files"));
62  m_ui->addFilesButton->setCursor(QCursor(Qt::PointingHandCursor));
63 
64  m_ui->rmFileButton->setIcon(QIcon(":/toolSets/btn_gain_rm_file"));
65  m_ui->rmFileButton->setIconSize(QSize(40, 40));
66  m_ui->rmFileButton->setStyleSheet(TTK::UI::ToolButtonStyle04);
67  m_ui->rmFileButton->setToolTip(tr("Remove File"));
68  m_ui->rmFileButton->setCursor(QCursor(Qt::PointingHandCursor));
69 
70  m_ui->rmFilesButton->setIcon(QIcon(":/toolSets/btn_gain_rm_files"));
71  m_ui->rmFilesButton->setIconSize(QSize(40, 40));
72  m_ui->rmFilesButton->setStyleSheet(TTK::UI::ToolButtonStyle04);
73  m_ui->rmFilesButton->setToolTip(tr("Remove Files"));
74  m_ui->rmFilesButton->setCursor(QCursor(Qt::PointingHandCursor));
75 
76  m_ui->analysisButton->setIcon(QIcon(":/toolSets/btn_analysis"));
77  m_ui->analysisButton->setIconSize(QSize(40, 40));
78  m_ui->analysisButton->setStyleSheet(TTK::UI::ToolButtonStyle04);
79  m_ui->analysisButton->setToolTip(tr("Analysis"));
80  m_ui->analysisButton->setCursor(QCursor(Qt::PointingHandCursor));
81 
82  m_ui->applyButton->setIcon(QIcon(":/toolSets/btn_analysis_apply"));
83  m_ui->applyButton->setIconSize(QSize(50, 50));
84  m_ui->applyButton->setStyleSheet(TTK::UI::ToolButtonStyle04);
85  m_ui->applyButton->setToolTip(tr("Apply"));
86  m_ui->applyButton->setCursor(QCursor(Qt::PointingHandCursor));
87 
88  m_ui->volumeLineEdit->setStyleSheet(TTK::UI::LineEditStyle01);
89  m_ui->volumeLineEdit->setValidator(new QRegExpValidator(QRegExp("-?[0-9]+$"), this));
90 
91  m_ui->progressBar->setStyleSheet(TTK::UI::ProgressBar01);
92  m_ui->progressBarAll->setStyleSheet(TTK::UI::ProgressBar01);
93 
94  m_process = new QProcess(this);
95  m_process->setProcessChannelMode(QProcess::MergedChannels);
96 
97  initialize();
98 
99  connect(m_process, SIGNAL(readyReadStandardOutput()), SLOT(analysisOutput()));
100  connect(m_ui->addFileButton, SIGNAL(clicked()), SLOT(addFileButtonClicked()));
101  connect(m_ui->addFilesButton, SIGNAL(clicked()), SLOT(addFilesButtonClicked()));
102  connect(m_ui->rmFileButton, SIGNAL(clicked()), SLOT(rmFileButtonClicked()));
103  connect(m_ui->rmFilesButton, SIGNAL(clicked()), SLOT(rmFilesButtonClicked()));
104  connect(m_ui->analysisButton, SIGNAL(clicked()), SLOT(analysisButtonClicked()));
105  connect(m_ui->applyButton, SIGNAL(clicked()), SLOT(applyButtonClicked()));
106  connect(m_ui->volumeLineEdit, SIGNAL(textChanged(QString)), SLOT(lineTextChanged(QString)));
107  connect(m_ui->selectedAreaWidget, SIGNAL(confirmChanged()), SLOT(confirmDataChanged()));
108 }
109 
111 {
112  TTKRemoveSingleWidget(className());
113  delete m_process;
114  delete m_ui;
115 }
116 
118 {
119  QPluginLoader loader;
120  loader.setFileName(TTK::TTKQmmp::pluginPath("Light", "lightreplaygain"));
121  const QObject *obj = loader.instance();
122 
123  LightFactory *factory = nullptr;
124  if(obj && (factory = TTKObjectCast(LightFactory*, obj)))
125  {
126  m_replayGainWidget = factory->create(this);
130  m_ui->replayGainLayout->addWidget(m_replayGainWidget);
131  }
132  else
133  {
134  MusicToastLabel::popup(tr("ReplayGain init error"));
135  }
136 }
137 
138 void MusicReplayGainWidget::addItemFinished(const QString &track, const QString &album)
139 {
140  if(m_currentIndex >= m_paths.count())
141  {
142  return;
143  }
144 
145  const int row = m_ui->tableWidget->rowCount();
146  m_ui->tableWidget->setRowCount(row + 1);
147  QHeaderView *headerView = m_ui->tableWidget->horizontalHeader();
148 
149  QTableWidgetItem *item = new QTableWidgetItem;
150  item->setToolTip(m_paths[m_currentIndex]);
151  item->setText(TTK::Widget::elidedText(font(), item->toolTip(), Qt::ElideRight, headerView->sectionSize(0) - 15));
152  QtItemSetTextAlignment(item, Qt::AlignLeft | Qt::AlignVCenter);
153  m_ui->tableWidget->setItem(row, 0, item);
154 
155  item = new QTableWidgetItem;
156  item->setText(QString::number(GAIN_DEFAULT - track.toDouble()));
157  QtItemSetTextAlignment(item, Qt::AlignLeft | Qt::AlignVCenter);
158  m_ui->tableWidget->setItem(row, 1, item);
159 
160  item = new QTableWidgetItem;
161  item->setText(QString::number(m_ui->volumeLineEdit->text().toDouble() - GAIN_DEFAULT + track.toDouble()));
162  QtItemSetTextAlignment(item, Qt::AlignLeft | Qt::AlignVCenter);
163  m_ui->tableWidget->setItem(row, 2, item);
164 
165  item = new QTableWidgetItem;
166  item->setText(QString::number(GAIN_DEFAULT - album.toDouble()));
167  QtItemSetTextAlignment(item, Qt::AlignLeft | Qt::AlignVCenter);
168  m_ui->tableWidget->setItem(row, 3, item);
169 
170  item = new QTableWidgetItem;
171  item->setText(QString::number(m_ui->volumeLineEdit->text().toDouble() - GAIN_DEFAULT + album.toDouble()));
172  QtItemSetTextAlignment(item, Qt::AlignLeft | Qt::AlignVCenter);
173  m_ui->tableWidget->setItem(row, 4, item);
174 }
175 
177 {
178  m_ui->addFileButton->setEnabled(enabled);
179  m_ui->addFilesButton->setEnabled(enabled);
180  m_ui->rmFileButton->setEnabled(enabled);
181  m_ui->rmFilesButton->setEnabled(enabled);
182  m_ui->analysisButton->setEnabled(enabled);
183  m_ui->applyButton->setEnabled(enabled);
184 }
185 
187 {
188  const QStringList &files = TTK::File::getOpenFileNames(this, "MP3 File(*.mp3)");
189  if(!files.isEmpty())
190  {
191  setControlEnabled(false);
192  const int orcount = m_paths.count();
193 
194  for(const QString &path : qAsConst(files))
195  {
196  if(!m_paths.contains(path))
197  {
198  m_paths << path;
199  }
200  }
201 
202  for(int i = orcount; i < m_paths.count(); ++i)
203  {
204  m_currentIndex = i;
205  TTKSemaphoreLoop loop;
206  connect(m_process, SIGNAL(finished(int)), &loop, SLOT(quit()));
207  QtProcessVoidConnect(m_process, &loop, quit, TTK_SLOT);
208  m_process->start(MAKE_GAIN_PATH_FULL, {m_paths[i]});
209  loop.exec();
210  }
211  setControlEnabled(true);
212  }
213 }
214 
216 {
217  const QString &path = TTK::File::getExistingDirectory(this);
218  if(!path.isEmpty())
219  {
220  setControlEnabled(false);
221  for(const QFileInfo &fin : TTK::File::fileInfoListByPath(path))
222  {
223  if(TTK_FILE_SUFFIX(fin) == MP3_FILE_SUFFIX && !m_paths.contains(fin.absoluteFilePath()))
224  {
225  m_currentIndex = m_paths.count();
226  m_paths << fin.absoluteFilePath();
227 
228  TTKSemaphoreLoop loop;
229  connect(m_process, SIGNAL(finished(int)), &loop, SLOT(quit()));
230  QtProcessVoidConnect(m_process, &loop, quit, TTK_SLOT);
231  m_process->start(MAKE_GAIN_PATH_FULL, {m_paths.back()});
232  loop.exec();
233  }
234  }
235  setControlEnabled(true);
236  }
237 }
238 
240 {
241  const int row = m_ui->tableWidget->currentRow();
242  if(row < 0)
243  {
244  MusicToastLabel::popup(tr("Please select one item first"));
245  return;
246  }
247  m_ui->tableWidget->removeRow(row);
248 }
249 
251 {
252  m_paths.clear();
253  m_ui->tableWidget->removeItems();
254  m_currentIndex = -1;
255 }
256 
258 {
260 }
261 
263 {
264  if(m_paths.isEmpty())
265  {
266  MusicToastLabel::popup(tr("Music gain list is empty"));
267  return;
268  }
269 
270  disconnect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(analysisOutput()));
271  connect(m_process, SIGNAL(readyReadStandardOutput()), SLOT(applyOutput()));
272 
273  setControlEnabled(false);
274  m_ui->progressBarAll->setRange(0, m_ui->tableWidget->rowCount());
275 
276  for(int i = 0; i < m_ui->tableWidget->rowCount(); ++i)
277  {
278  TTKSemaphoreLoop loop;
279  connect(m_process, SIGNAL(finished(int)), &loop, SLOT(quit()));
280  QtProcessVoidConnect(m_process, &loop, quit, TTK_SLOT);
281  m_process->start(MAKE_GAIN_PATH_FULL, {"-g", m_ui->tableWidget->item(i, 2)->text(), m_paths[i]});
282  m_ui->progressBarAll->setValue(i + 1);
283  loop.exec();
284  }
285 
286  setControlEnabled(true);
288 
289  disconnect(m_process, SIGNAL(readyReadStandardOutput()), this, SLOT(applyOutput()));
290  connect(m_process, SIGNAL(readyReadStandardOutput()), SLOT(analysisOutput()));
291 
292  MusicToastLabel::popup(tr("Music gain finished"));
293 }
294 
295 void MusicReplayGainWidget::lineTextChanged(const QString &text)
296 {
297  const double d = text.toDouble();
298  for(int i = 0; i < m_ui->tableWidget->rowCount(); ++i)
299  {
300  QString v = m_ui->tableWidget->item(i, 1)->text();
301  m_ui->tableWidget->item(i, 2)->setText(QString::number(d - v.toDouble()));
302  v = m_ui->tableWidget->item(i, 3)->text();
303  m_ui->tableWidget->item(i, 4)->setText(QString::number(d - v.toDouble()));
304  }
305 }
306 
308 {
309  QString track, album;
310  while(m_process->canReadLine())
311  {
312  QByteArray data = m_process->readLine();
313  if(data.contains(GAIN_TRACKDB))
314  {
315  data.replace(GAIN_TRACKDB, {});
316  data.replace(TTK_WLINEFEED, {});
317  track = QString(data.trimmed());
318  }
319 
320  if(data.contains(GAIN_ALBUMDB))
321  {
322  data.replace(GAIN_ALBUMDB, {});
323  data.replace(TTK_WLINEFEED, {});
324  album = QString(data.trimmed());
325  }
326  }
327 
328  if(!track.isEmpty() || !album.isEmpty())
329  {
330  addItemFinished(track, album);
331  }
332 }
333 
335 {
336  while(m_process->canReadLine())
337  {
338  const QByteArray &data = m_process->readLine();
339  if(data.contains("Applying gain change"))
340  {
341  m_ui->progressBar->setValue(40);
342  }
343  else if(data.contains("done"))
344  {
345  m_ui->progressBar->setValue(100);
346  }
347  else
348  {
349  m_ui->progressBar->setValue(80);
350  }
351  }
352 }
353 
355 {
356  const MusicSongList songs(m_ui->selectedAreaWidget->selectedSongItems());
357  for(const MusicSong &song : qAsConst(songs))
358  {
360  {
361  m_replayGainWidget->open(song.path());
362  }
363  }
364 }
365 
367 {
368  if(!QFile::exists(MAKE_GAIN_PATH_FULL))
369  {
370  MusicToastLabel::popup(tr("Lack of plugin file"));
371  return;
372  }
373 
375 }
static constexpr const char * GAIN_ALBUMDB
TTK_MODULE_EXPORT QString getExistingDirectory(QWidget *parent)
void setBackgroundLabel(QLabel *label)
MusicReplayGainTableWidget(QWidget *parent=nullptr)
Light plugin interface (Light factory).
Definition: lightfactory.h:76
MusicReplayGainWidget(QWidget *parent=nullptr)
The class of the table widget base.
static const QString CheckBoxStyle01
CheckBox.
voidpf void uLong size
Definition: ioapi.h:136
The class of the replay gain widget.
Ui::MusicReplayGainWidget * m_ui
int exec(ProcessEventsFlags flags=AllEvents)
#define QtItemSetTextAlignment(p, a)
Item text alignment.
Definition: ttkqtcompat.h:45
static const QString ProgressBar01
ProgressBar.
static constexpr int GAIN_DEFAULT
static const QString HeaderView01
HeadView.
#define MP3_FILE_SUFFIX
Definition: musicobject.h:51
#define QtProcessVoidConnect(p, q, f, s)
Process error void connect.
Definition: ttkqtcompat.h:94
static constexpr const char * GAIN_TRACKDB
virtual void open(const QString &path)=0
virtual Light * create(QWidget *parent)=0
static void popup(const QString &text)
void addItemFinished(const QString &track, const QString &album)
static const QString ScrollBarStyle03
#define qAsConst
Definition: ttkqtglobal.h:51
#define TTKRemoveSingleWidget(name)
static const QString PushButtonStyle04
TTK_MODULE_EXPORT QString elidedText(const QFont &font, const QString &text, Qt::TextElideMode mode, int width)
TTK_MODULE_EXPORT QFileInfoList fileInfoListByPath(const QString &dpath, const QStringList &filter={}, bool recursively=true)
#define TTK_SLOT
Definition: ttkqtglobal.h:181
#define MAKE_GAIN_PATH_FULL
Definition: musicobject.h:152
void lineTextChanged(const QString &text)
void setControlEnabled(bool enabled)
static const QString LineEditStyle01
LineEdit.
#define TTK_WLINEFEED
Definition: ttkglobal.h:199
TTK_MODULE_EXPORT QString pluginPath(const QString &module, const QString &format)
static const QString TabWidgetStyle01
TabWidget.
static const QString ToolButtonStyle04
TTK_MODULE_EXPORT QStringList getOpenFileNames(QWidget *parent, const QString &filter="Image Files (*.png *.bmp *.jpg)")
#define TTK_FILE_SUFFIX(fin)
Definition: ttkqtglobal.h:189
The class of the music song info.
Definition: musicsong.h:28
The class of the semaphore event loop.
The class of the moving widget base.
#define TTKObjectCast(x, y)
Definition: ttkqtglobal.h:76