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