parse lrc format of lyrics

dev
xufuji456 3 years ago
parent 03bbdcf160
commit 3c1c4e48e2
  1. 18
      app/src/main/java/com/frank/ffmpeg/model/LrcInfo.kt
  2. 2
      app/src/main/java/com/frank/ffmpeg/tool/LrcLineTool.kt
  3. 152
      app/src/main/java/com/frank/ffmpeg/tool/LrcParser.kt
  4. 85
      app/src/main/java/com/frank/ffmpeg/tool/UnicodeInputStream.kt
  5. 2
      app/src/main/java/com/frank/ffmpeg/util/TimeUtil.kt

@ -0,0 +1,18 @@
package com.frank.ffmpeg.model
import java.util.ArrayList
class LrcInfo {
var title: String? = null
var album: String? = null
var artist: String? = null
var author: String? = null
var creator: String? = null
var encoder: String? = null
var version: String? = null
var offset: Int = 0
var lrcLineList: ArrayList<LrcLine>? = null
}

@ -29,7 +29,7 @@ object LrcLineTool {
val mLrcLine = LrcLine()
mLrcLine.content = content
mLrcLine.timeString = temp
val startTime = TimeUtil.timeConvert(temp)
val startTime = TimeUtil.timeStrToLong(temp)
mLrcLine.startTime = startTime
listTimes.add(mLrcLine)
}

@ -0,0 +1,152 @@
package com.frank.ffmpeg.tool
import com.frank.ffmpeg.model.LrcInfo
import com.frank.ffmpeg.model.LrcLine
import com.frank.ffmpeg.util.TimeUtil
import java.io.BufferedInputStream
import java.io.BufferedReader
import java.io.FileInputStream
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.util.*
import java.util.regex.Pattern
/**
* Parse lrc format of lyrics
*/
class LrcParser {
private val lrcInfo = LrcInfo()
private val lrcLineList = ArrayList<LrcLine>()
fun readLrc(path: String): LrcInfo? {
var inputStream: InputStream? = null
var inputStreamReader: InputStreamReader? = null
try {
var charsetName: String? = getCharsetName(path)
inputStream = FileInputStream(path)
if (charsetName!!.toLowerCase(Locale.getDefault()) == "utf-8") {
inputStream = UnicodeInputStream(inputStream, charsetName)
charsetName = inputStream.getEncoding()
}
inputStreamReader = if (!charsetName.isNullOrEmpty()) {
InputStreamReader(inputStream, charsetName)
} else {
InputStreamReader(inputStream)
}
val reader = BufferedReader(inputStreamReader)
while (true) {
val str: String? = reader.readLine() ?: break
if (!str.isNullOrEmpty()) {
decodeLine(str)
}
}
lrcInfo.lrcLineList = lrcLineList
return lrcInfo
} catch (e: IOException) {
return null
} finally {
try {
inputStreamReader?.close()
inputStream?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
private fun getCharsetName(path: String): String {
var code = "GBK"
var bin: BufferedInputStream? = null
try {
bin = BufferedInputStream(FileInputStream(path))
when ((bin.read() shl 8) + bin.read()) {
0xefbb -> code = "UTF-8"
0xfffe -> code = "Unicode"
0xfeff -> code = "UTF-16BE"
else -> {
}
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
if (bin != null) {
try {
bin.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
return code
}
private fun match(line: String): Boolean {
return line.length > 4 && line.lastIndexOf("]") > 4
}
private fun decodeLine(str: String) {
if (str.startsWith("[ti:")) {
if (match(str))
lrcInfo.title = str.substring(4, str.lastIndexOf("]"))
} else if (str.startsWith("[ar:")) {
if (match(str))
lrcInfo.artist = str.substring(4, str.lastIndexOf("]"))
} else if (str.startsWith("[al:")) {
if (match(str))
lrcInfo.album = str.substring(4, str.lastIndexOf("]"))
} else if (str.startsWith("[au:")) {
if (match(str))
lrcInfo.author = str.substring(4, str.lastIndexOf("]"))
} else if (str.startsWith("[by:")) {
if (match(str))
lrcInfo.creator = str.substring(4, str.lastIndexOf("]"))
} else if (str.startsWith("[re:")) {
if (match(str))
lrcInfo.encoder = str.substring(4, str.lastIndexOf("]"))
} else if (str.startsWith("[ve:")) {
if (match(str))
lrcInfo.version = str.substring(4, str.lastIndexOf("]"))
} else if (str.startsWith("[offset:")) {
if (str.lastIndexOf("]") > 8) {
val offset = str.substring(8, str.lastIndexOf("]"))
try {
lrcInfo.offset = Integer.parseInt(offset)
} catch (e: NumberFormatException) {
e.printStackTrace()
}
}
} else {
val timeExpress = "\\[(\\d{1,2}:\\d{1,2}\\.\\d{1,2})]|\\[(\\d{1,2}:\\d{1,2})]"
val pattern = Pattern.compile(timeExpress)
val matcher = pattern.matcher(str)
var currentTime: Long = 0
while (matcher.find()) {
val groupCount = matcher.groupCount()
for (index in 0 until groupCount) {
val timeStr = matcher.group(index)
if (index == 0) {
currentTime = TimeUtil.timeStrToLong(timeStr.substring(1, timeStr.length - 1))
}
}
val content = pattern.split(str)
var currentContent: String? = ""
if (content.isNotEmpty()) {
currentContent = content[content.size - 1]
}
val lrcLine = LrcLine()
lrcLine.startTime = currentTime
lrcLine.endTime = currentTime + 10 * 1000
lrcLine.content = currentContent
lrcLineList.add(lrcLine)
}
}
}
}

@ -0,0 +1,85 @@
package com.frank.ffmpeg.tool
import java.io.IOException
import java.io.InputStream
import java.io.PushbackInputStream
class UnicodeInputStream(`in`: InputStream, private val defaultEnc: String) : InputStream() {
private var encoding: String? = null
private var isInitialed = false
private val internalIn: PushbackInputStream
init {
internalIn = PushbackInputStream(`in`, BOM_SIZE)
}
fun getEncoding(): String? {
if (!isInitialed) {
try {
init()
} catch (ex: IOException) {
throw IllegalStateException("getEncoding error=" + ex.message)
}
}
return encoding
}
/**
* Read-ahead four bytes and check for BOM marks. Extra bytes are unread
* back to the stream, only BOM bytes are skipped.
*/
@Throws(IOException::class)
private fun init() {
if (isInitialed)
return
val bom = ByteArray(BOM_SIZE)
val n: Int
val unread: Int
n = internalIn.read(bom, 0, bom.size)
if (bom[0] == 0x00.toByte() && bom[1] == 0x00.toByte()
&& bom[2] == 0xFE.toByte() && bom[3] == 0xFF.toByte()) {
encoding = "UTF-32BE"
unread = n - 4
} else if (bom[0] == 0xFF.toByte() && bom[1] == 0xFE.toByte()
&& bom[2] == 0x00.toByte() && bom[3] == 0x00.toByte()) {
encoding = "UTF-32LE"
unread = n - 4
} else if (bom[0] == 0xEF.toByte() && bom[1] == 0xBB.toByte()
&& bom[2] == 0xBF.toByte()) {
encoding = "UTF-8"
unread = n - 3
} else if (bom[0] == 0xFE.toByte() && bom[1] == 0xFF.toByte()) {
encoding = "UTF-16BE"
unread = n - 2
} else if (bom[0] == 0xFF.toByte() && bom[1] == 0xFE.toByte()) {
encoding = "UTF-16LE"
unread = n - 2
} else {
encoding = defaultEnc
unread = n
}
if (unread > 0)
internalIn.unread(bom, n - unread, unread)
isInitialed = true
}
@Throws(IOException::class)
override fun close() {
isInitialed = false
internalIn.close()
}
@Throws(IOException::class)
override fun read(): Int {
isInitialed = true
return internalIn.read()
}
companion object {
private const val BOM_SIZE = 4
}
}

@ -82,7 +82,7 @@ object TimeUtil {
/**
* string time to milliseconds
*/
fun timeConvert(timeStr: String): Long {
fun timeStrToLong(timeStr: String): Long {
var timeString = timeStr
timeString = timeString.replace('.', ':')
val times = timeString.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()

Loading…
Cancel
Save