TTKMusicPlayer  4.2.0.0
TTKMusicPlayer imitates Kugou UI, the music player uses of qmmp core library based on Qt for windows and linux
musiclrcanalysis.cpp
Go to the documentation of this file.
1 #include "musiclrcanalysis.h"
2 #include "musiclrcparser.h"
3 #include "musicapplication.h"
4 #include "ttktime.h"
5 
6 #include <qmath.h>
7 
9  : QObject(parent),
10  m_lineMax(0),
11  m_currentLrcIndex(0)
12 {
13 
14 }
15 
17 {
18  QString data;
19  data.append(QString("[by: %1]\n[offset:0]\n").arg(TTK_APP_NAME));
20 
21  for(auto it = m_lrcContainer.constBegin(); it != m_lrcContainer.constEnd(); ++it)
22  {
23  data.append(TTKTime::toString(it.key(), "[mm:ss.zzz]"));
24  data.append(it.value() + TTK_LINEFEED);
25  }
26 
27  QFile file(m_currentFilePath);
28  if(!file.open(QIODevice::WriteOnly))
29  {
30  return;
31  }
32 
33  QTextStream outstream(&file);
34  outstream.setCodec("UTF-8");
35  outstream << data;
36  outstream << QtNamespace(endl);
37  file.close();
38 }
39 
41 {
42  clear();
43  if(data.isEmpty())
44  {
45  return State::Failed;
46  }
47 
48  m_lrcContainer = data;
49  return initialize();
50 }
51 
53 {
54  clear();
55  m_currentFilePath = path;
56 
57  QStringList data;
58  const QString &format = TTK_FILE_SUFFIX(QFileInfo(path));
59 
60  if(format == LRC_FILE_SUFFIX)
61  {
62  TTK_INFO_STREAM("Current in lrc parser mode");
63  MusicLrcFromPlain plain;
64  if(!plain.decode(path))
65  {
67  }
68 
69  data = QString::fromUtf8(plain.data()).split(TTK_LINEFEED);
70  }
71  else if(format == KRC_FILE_SUFFIX)
72  {
73  TTK_INFO_STREAM("Current in krc parser mode");
74  MusicLrcFromKrc krc;
75  if(!krc.decode(path))
76  {
78  }
79 
80  data = QString::fromUtf8(krc.data()).split(TTK_WLINEFEED);
81  }
82  else if(format == QRC_FILE_SUFFIX)
83  {
84  TTK_INFO_STREAM("Current in qrc parser mode");
85  MusicLrcFromQrc qrc;
86  if(!qrc.decode(path))
87  {
89  }
90 
91  data = QString::fromUtf8(qrc.data()).split(TTK_LINEFEED);
92  }
93  else if(format == TRC_FILE_SUFFIX)
94  {
95  TTK_INFO_STREAM("Current in trc parser mode");
96  MusicLrcFromTrc trc;
97  if(!trc.decode(path))
98  {
100  }
101 
102  data = QString::fromUtf8(trc.data()).split(TTK_LINEFEED);
103  }
104  else if(format == YRC_FILE_SUFFIX)
105  {
106  TTK_INFO_STREAM("Current in yrc parser mode");
107  MusicLrcFromYrc yrc;
108  if(!yrc.decode(path))
109  {
111  }
112 
113  data = QString::fromUtf8(yrc.data()).split(TTK_LINEFEED);
114  }
115  else
116  {
117  TTK_INFO_STREAM("Current in none parser mode");
119  }
120 
121  for(const QString &oneLine : qAsConst(data))
122  {
123  matchLrcLine(oneLine);
124  }
125 
126  return initialize();
127 }
128 
130 {
131  if(m_lrcContainer.isEmpty())
132  {
133  return State::Failed;
134  }
135 
136  for(int i = 0; i < lineMiddle(); ++i)
137  {
138  m_currentShowLrcContainer << QString();
139  }
140 
141  if(m_lrcContainer.find(0) == m_lrcContainer.end())
142  {
143  m_lrcContainer.insert(0, {});
144  }
145 
146  for(auto it = m_lrcContainer.constBegin(); it != m_lrcContainer.constEnd(); ++it)
147  {
148  m_currentShowLrcContainer << it.value();
149  }
150 
151  for(int i = 0; i < lineMiddle(); ++i)
152  {
153  m_currentShowLrcContainer << QString();
154  }
155 
156  return State::Success;
157 }
158 
159 void MusicLrcAnalysis::matchLrcLine(const QString &oneLine)
160 {
161  const QRegExp regx01("\\[\\d{2}:\\d{2}\\.\\d{3}\\]");
162  const QRegExp regx02("\\[\\d{2}:\\d{2}\\.\\d{2}\\]");
163  const QRegExp regx03("\\[\\d{2}:\\d{2}\\.\\d{1}\\]");
164  const QRegExp regx04("\\[\\d{2}:\\d{2}:\\d{3}\\]");
165  const QRegExp regx05("\\[\\d{2}:\\d{2}:\\d{2}\\]");
166  const QRegExp regx06("\\[\\d{2}:\\d{2}:\\d{1}\\]");
167  const QRegExp regx07("\\[\\d{2}:\\d{2}\\]");
168  const QRegExp regx08("\\[\\d{2}\\.\\d{2}\\.\\d{3}\\]");
169  const QRegExp regx09("\\[\\d{2}\\.\\d{2}\\.\\d{2}\\]");
170  const QRegExp regx10("\\[\\d{2}\\.\\d{2}\\.\\d{1}\\]");
171  const QRegExp regx11("\\[\\d{2}\\.\\d{2}:\\d{3}\\]");
172  const QRegExp regx12("\\[\\d{2}\\.\\d{2}:\\d{2}\\]");
173  const QRegExp regx13("\\[\\d{2}\\.\\d{2}:\\d{1}\\]");
174  const QRegExp regx14("\\[\\d{2}\\.\\d{2}\\]");
175 
176  Format type = Format::Type02;
177  QRegExp regx;
178  if(oneLine.contains(regx01))
179  {
180  type = Format::Type01;
181  regx = regx01;
182  }
183  else if(oneLine.contains(regx02))
184  {
185  type = Format::Type02;
186  regx = regx02;
187  }
188  else if(oneLine.contains(regx03))
189  {
190  type = Format::Type03;
191  regx = regx03;
192  }
193  else if(oneLine.contains(regx04))
194  {
195  type = Format::Type04;
196  regx = regx04;
197  }
198  else if(oneLine.contains(regx05))
199  {
200  type = Format::Type05;
201  regx = regx05;
202  }
203  else if(oneLine.contains(regx06))
204  {
205  type = Format::Type06;
206  regx = regx06;
207  }
208  else if(oneLine.contains(regx07))
209  {
210  type = Format::Type07;
211  regx = regx07;
212  }
213  else if(oneLine.contains(regx08))
214  {
215  type = Format::Type08;
216  regx = regx08;
217  }
218  else if(oneLine.contains(regx09))
219  {
220  type = Format::Type09;
221  regx = regx09;
222  }
223  else if(oneLine.contains(regx10))
224  {
225  type = Format::Type10;
226  regx = regx10;
227  }
228  else if(oneLine.contains(regx11))
229  {
230  type = Format::Type11;
231  regx = regx11;
232  }
233  else if(oneLine.contains(regx12))
234  {
235  type = Format::Type12;
236  regx = regx12;
237  }
238  else if(oneLine.contains(regx13))
239  {
240  type = Format::Type13;
241  regx = regx13;
242  }
243  else
244  {
245  type = Format::Type14;
246  regx = regx14;
247  }
248 
249  QString temp = oneLine;
250  temp.replace(regx, {});
251  int pos = regx.indexIn(oneLine);
252  while(pos != -1)
253  {
254  const QString &cap = regx.cap(0);
255  //Return zeroth expression matching the content
256  //The time tag into the time value, in milliseconds
257  switch(type)
258  {
259  case Format::Type01: matchLrcLine(temp, cap, "\\d{2}(?=:)", "\\d{2}(?=\\.)", "\\d{3}(?=\\])"); break;
260  case Format::Type02: matchLrcLine(temp, cap, "\\d{2}(?=:)", "\\d{2}(?=\\.)", "\\d{2}(?=\\])"); break;
261  case Format::Type03: matchLrcLine(temp, cap, "\\d{2}(?=:)", "\\d{2}(?=\\.)", "\\d{1}(?=\\])"); break;
262  case Format::Type04: matchLrcLine(temp, cap, ":"); break;
263  case Format::Type05: matchLrcLine(temp, cap, ":"); break;
264  case Format::Type06: matchLrcLine(temp, cap, ":"); break;
265  case Format::Type07: matchLrcLine(temp, cap, "\\d{2}(?=:)", "\\d{2}(?=\\])"); break;
266  case Format::Type08: matchLrcLine(temp, cap, "."); break;
267  case Format::Type09: matchLrcLine(temp, cap, "."); break;
268  case Format::Type10: matchLrcLine(temp, cap, "."); break;
269  case Format::Type11: matchLrcLine(temp, cap, "\\d{2}(?=\\.)", "\\d{2}(?=:)", "\\d{3}(?=\\])"); break;
270  case Format::Type12: matchLrcLine(temp, cap, "\\d{2}(?=\\.)", "\\d{2}(?=:)", "\\d{2}(?=\\])"); break;
271  case Format::Type13: matchLrcLine(temp, cap, "\\d{2}(?=\\.)", "\\d{2}(?=:)", "\\d{1}(?=\\])"); break;
272  case Format::Type14: matchLrcLine(temp, cap, "\\d{2}(?=\\.)", "\\d{2}(?=\\])"); break;
273  default: break;
274  }
275 
276  pos += regx.matchedLength();
277  pos = regx.indexIn(oneLine, pos); //Matching all
278  }
279 }
280 
281 void MusicLrcAnalysis::matchLrcLine(const QString &oneLine, const QString &cap, const QString &first, const QString &second, const QString &third)
282 {
283  QRegExp regx;
284  regx.setPattern(first);
285  regx.indexIn(cap);
286 
287  const int minutes = regx.cap(0).toInt();
288  regx.setPattern(second);
289  regx.indexIn(cap);
290 
291  const int seconds = regx.cap(0).toInt();
292  regx.setPattern(third);
293  regx.indexIn(cap);
294 
295  const int milliseconds = regx.cap(0).toInt();
296  const int length = QString::number(milliseconds).length();
297  const qint64 totalTime = minutes * TTK_DN_M2MS + seconds * TTK_DN_S2MS + milliseconds * pow(10, 3 - length);
298  m_lrcContainer.insert(totalTime, oneLine);
299 }
300 
301 void MusicLrcAnalysis::matchLrcLine(const QString &oneLine, const QString &cap, const QString &first, const QString &second)
302 {
303  QRegExp regx;
304  regx.setPattern(first);
305  regx.indexIn(cap);
306 
307  const int minutes = regx.cap(0).toInt();
308  regx.setPattern(second);
309  regx.indexIn(cap);
310 
311  const int seconds = regx.cap(0).toInt();
312  const qint64 totalTime = minutes * TTK_DN_M2MS + seconds * TTK_DN_S2MS;
313  m_lrcContainer.insert(totalTime, oneLine);
314 }
315 
316 void MusicLrcAnalysis::matchLrcLine(const QString &oneLine, QString cap, const QString &splite)
317 {
318  cap.chop(1);
319  cap.remove(0, 1);
320  const QStringList list(cap.split(splite));
321  if(list.count() != 3)
322  {
323  return;
324  }
325 
326  const int minutes = list[0].toInt();
327  const int seconds = list[1].toInt();
328  const int milliseconds = list[2].toInt();
329  const int length = QString::number(milliseconds).length();
330  const qint64 totalTime = minutes * TTK_DN_M2MS + seconds * TTK_DN_S2MS + milliseconds * pow(10, 3 - length);
331  m_lrcContainer.insert(totalTime, oneLine);
332 }
333 
335 {
336  const QList<qint64> keys(m_lrcContainer.keys());
337  qint64 beforeTime = 0;
338  int index = -1;
339 
340  if(!keys.isEmpty())
341  {
342  index = 0;
343  beforeTime = keys[0];
344  }
345 
346  for(int i = 1; i < keys.count(); ++i)
347  {
348  index = i;
349  const qint64 afterTime = keys[i];
350  if(beforeTime <= time && time <= afterTime)
351  {
352  time = afterTime;
353  break;
354  }
355  beforeTime = afterTime;
356  }
357 
358  if((m_currentLrcIndex = index - 1) < 0)
359  {
360  m_currentLrcIndex = 0;
361  }
362  return time;
363 }
364 
366 {
367  TTKIntStringMap copy;
368  for(auto it = m_lrcContainer.constBegin(); it != m_lrcContainer.constEnd(); ++it)
369  {
370  copy.insert(it.key() + pos, it.value());
371  }
372  m_lrcContainer = copy;
373 }
374 
375 void MusicLrcAnalysis::clear() noexcept
376 {
377  m_currentLrcIndex = 0;
378  m_lrcContainer.clear();
380 }
381 
383 {
384  return (!isEmpty()) && (m_currentLrcIndex + m_lineMax <= m_currentShowLrcContainer.count());
385 }
386 
388 {
389  return m_lrcContainer.isEmpty();
390 }
391 
393 {
394  return m_lrcContainer.count();
395 }
396 
397 QString MusicLrcAnalysis::text(int index) const
398 {
399  index = m_currentLrcIndex + index;
400  if(m_currentShowLrcContainer.count() <= index)
401  {
402  index = m_currentShowLrcContainer.count() - 1;
403  }
404 
405  if(index <0 || index >= m_currentShowLrcContainer.count())
406  {
407  return {};
408  }
409  return m_currentShowLrcContainer[index];
410 }
411 
412 bool MusicLrcAnalysis::findText(qint64 current, qint64 total, QString &pre, QString &last, qint64 &interval) const
413 {
414  if(isEmpty())
415  {
416  Q_UNUSED(current)
417  Q_UNUSED(total)
418  pre = last = QString();
419  return false;
420  }
421 
422  //After get the current time in the lyrics of the two time points
423  qint64 previous = 0;
424  qint64 later = 0;
425  //Keys () method returns a list of lrcContainer
426  for(const qint64 value : m_lrcContainer.keys())
427  {
428  if(current >= value)
429  {
430  previous = value;
431  }
432  else
433  {
434  later = value;
435  break;
436  }
437  }
438  //To the last line, set the later to song total time value
439  if(later == 0)
440  {
441  later = total;
442  }
443  //The lyrics content corresponds to obtain the current time
444  pre = m_lrcContainer.value(previous);
445  last = m_lrcContainer.value(later);
446  interval = later - previous;
447  return true;
448 }
449 
450 qint64 MusicLrcAnalysis::findTime(int index) const
451 {
452  if(index + m_lineMax < m_currentShowLrcContainer.count())
453  {
454  auto it = m_lrcContainer.constBegin();
455  for(int i = 0; i < index + 1; ++i)
456  {
457  if(it != m_lrcContainer.constEnd())
458  {
459  ++it;
460  }
461  }
462  return it.key();
463  }
464  else
465  {
466  return -1;
467  }
468 }
469 
470 qint64 MusicLrcAnalysis::findTime(const QStringList &ts) const
471 {
472  if(ts.isEmpty())
473  {
474  return -1;
475  }
476 
477  for(int i = 0; i < m_currentShowLrcContainer.count() - ts.count(); ++i)
478  {
479  if(m_currentShowLrcContainer.mid(i, ts.count()) == ts)
480  {
481  return findTime(i + lineMiddle());
482  }
483  }
484  return -1;
485 }
486 
488 {
489  QString v;
490  for(const QString &s : m_lrcContainer.values())
491  {
492  v.append(s + TTK_LINEFEED);
493  }
494  return v;
495 }
496 
497 QStringList MusicLrcAnalysis::dataList() const
498 {
499  return m_lrcContainer.values();
500 }
void revertTime(qint64 pos)
virtual bool decode(const QString &input) overridefinal
#define TRC_FILE_SUFFIX
Definition: musicobject.h:40
The input file format support.
Definition: format.h:29
const QByteArray & data() const noexcept
virtual bool decode(const QString &input) overridefinal
#define TTK_APP_NAME
Definition: ttkobject.h:25
QStringList dataList() const
bool isValid() const noexcept
#define TTK_DN_M2MS
Definition: ttkglobal.h:361
#define QRC_FILE_SUFFIX
Definition: musicobject.h:39
void clear() noexcept
#define qAsConst
Definition: ttkqtglobal.h:57
bool findText(qint64 current, qint64 total, QString &pre, QString &last, qint64 &interval) const
#define TTK_INFO_STREAM(msg)
Definition: ttklogger.h:74
QMap< qint64, QString > TTKIntStringMap
Definition: ttkqtglobal.h:204
QString text(int index) const
The class of the krc to lrc.
#define YRC_FILE_SUFFIX
Definition: musicobject.h:41
virtual bool decode(const QString &input) overridefinal
qint64 setSongTimeSpeed(qint64 time)
State setData(const TTKIntStringMap &data)
qint64 findTime(int index) const
int lineMiddle() const noexcept
TTK_MODULE_EXPORT QStringList split(const QString &value, const QString &key=TTK_DEFAULT_STR)
#define QtNamespace(p)
Qt use namespace.
Definition: ttkqtcompat.h:115
virtual bool decode(const QString &input) overridefinal
#define TTK_DN_S2MS
Definition: ttkglobal.h:355
virtual bool decode(const QString &input) overridefinal
#define KRC_FILE_SUFFIX
Definition: musicobject.h:38
#define TTK_WLINEFEED
Definition: ttkglobal.h:272
The class of the plain lrc.
TTKIntStringMap m_lrcContainer
MusicLrcAnalysis(QObject *parent=nullptr)
QString dataString() const
void matchLrcLine(const QString &oneLine)
#define TTK_LINEFEED
Definition: ttkglobal.h:271
State loadFromFile(const QString &path)
#define TTK_FILE_SUFFIX(fin)
Definition: ttkqtglobal.h:185
The class of the trc to lrc.
bool isEmpty() const noexcept
The class of the qrc to lrc.
static QString toString(qint64 time, const QString &format) noexcept
Definition: ttktime.cpp:72
The class of the yrc to lrc.
#define const
Definition: zconf.h:233
QStringList m_currentShowLrcContainer
#define LRC_FILE_SUFFIX
Definition: musicobject.h:37
int count() const noexcept