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