lx-music-desktop/src/renderer/utils/lyric-font-player/line-player.js

154 lines
4.2 KiB
JavaScript

const { getNow, TimeoutTools } = require('./utils')
const timeExp = /^\[([\d:.]*)\]{1}/g
const tagRegMap = {
title: 'ti',
artist: 'ar',
album: 'al',
offset: 'offset',
by: 'by',
}
const timeoutTools = new TimeoutTools()
module.exports = class LinePlayer {
constructor({ lyric = '', offset = 0, onPlay = function() { }, onSetLyric = function() { } } = {}) {
this.lyric = lyric
this.tags = {}
this.lines = null
this.onPlay = onPlay
this.onSetLyric = onSetLyric
this.isPlay = false
this.curLineNum = 0
this.maxLine = 0
this.offset = offset
this.isOffseted = false
this._performanceTime = 0
this._performanceOffsetTime = 0
this._init()
}
_init() {
if (this.lyric == null) this.lyric = ''
this._initTag()
this._initLines()
this.onSetLyric(this.lines)
}
_initTag() {
for (let tag in tagRegMap) {
const matches = this.lyric.match(new RegExp(`\\[${tagRegMap[tag]}:([^\\]]*)]`, 'i'))
this.tags[tag] = (matches && matches[1]) || ''
}
}
_initLines() {
this.lines = []
const lines = this.lyric.split('\n')
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim()
let result = timeExp.exec(line)
if (result) {
const text = line.replace(timeExp, '').trim()
if (text) {
const timeArr = RegExp.$1.split(':')
if (timeArr.length < 3) timeArr.unshift(0)
if (timeArr[2].indexOf('.') > -1) {
timeArr.push(...timeArr[2].split('.'))
timeArr.splice(2, 1)
}
this.lines.push({
time: parseInt(timeArr[0]) * 60 * 60 * 1000 + parseInt(timeArr[1]) * 60 * 1000 + parseInt(timeArr[2]) * 1000 + parseInt(timeArr[3] || 0),
text,
})
}
}
}
this.lines.sort((a, b) => {
return a.time - b.time
})
this.maxLine = this.lines.length - 1
}
_currentTime() {
return getNow() - this._performanceTime + this._performanceOffsetTime
}
_findCurLineNum(curTime) {
const length = this.lines.length
for (let index = 0; index < length; index++) if (curTime <= this.lines[index].time) return index === 0 ? 0 : index - 1
return length - 1
}
_handleMaxLine() {
this.onPlay(this.curLineNum, this.lines[this.curLineNum].text, this._currentTime())
this.pause()
}
_refresh() {
this.curLineNum++
// console.log('curLineNum time', this.lines[this.curLineNum].time)
let curLine = this.lines[this.curLineNum]
let nextLine = this.lines[this.curLineNum + 1]
const currentTime = this._currentTime()
const driftTime = currentTime - curLine.time
if (driftTime >= 0 || this.curLineNum === 0) {
if (this.curLineNum === this.maxLine) return this._handleMaxLine()
this.delay = nextLine.time - curLine.time - driftTime
if (this.delay > 0) {
if (!this.isOffseted && this.delay >= this.offset) {
this._performanceOffsetTime += this.offset
this.delay -= this.offset
this.isOffseted = true
}
timeoutTools.start(() => {
this._refresh()
}, this.delay)
this.onPlay(this.curLineNum, curLine.text, currentTime)
return
}
}
this.curLineNum = this._findCurLineNum(currentTime) - 1
this._refresh()
}
play(curTime = 0) {
if (!this.lines.length) return
this.pause()
this.isPlay = true
this._performanceOffsetTime = 0
this._performanceTime = getNow() - curTime
if (this._performanceTime < 0) {
this._performanceOffsetTime = -this._performanceTime
this._performanceTime = 0
}
this.curLineNum = this._findCurLineNum(curTime) - 1
this._refresh()
}
pause() {
if (!this.isPlay) return
this.isPlay = false
this.isOffseted = false
timeoutTools.clear()
if (this.curLineNum === this.maxLine) return
const currentTime = this._currentTime()
const curLineNum = this._findCurLineNum(currentTime)
if (this.curLineNum !== curLineNum) {
this.curLineNum = curLineNum
this.onPlay(curLineNum, this.lines[curLineNum].text, currentTime)
}
}
setLyric(lyric) {
if (this.isPlay) this.pause()
this.lyric = lyric
this._init()
}
}