├── .gitignore ├── build.gradle ├── gradle.properties ├── gradlew ├── gradlew.bat ├── hplyricslibrary ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── zlm │ │ └── hp │ │ └── lyrics │ │ ├── LyricsReader.java │ │ ├── formats │ │ ├── LyricsFileReader.java │ │ ├── LyricsFileWriter.java │ │ ├── hrc │ │ │ ├── HrcLyricsFileReader.java │ │ │ └── HrcLyricsFileWriter.java │ │ ├── krc │ │ │ ├── KrcLyricsFileReader.java │ │ │ └── KrcLyricsFileWriter.java │ │ ├── ksc │ │ │ ├── KscLyricsFileReader.java │ │ │ └── KscLyricsFileWriter.java │ │ ├── lrc │ │ │ ├── LrcLyricsFileReader.java │ │ │ └── LrcLyricsFileWriter.java │ │ ├── lrcwy │ │ │ ├── WYLyricsFileReader.java │ │ │ └── WYLyricsFileWriter.java │ │ └── trc │ │ │ ├── TrcLyricsFileReader.java │ │ │ └── TrcLyricsFileWriter.java │ │ ├── model │ │ ├── LyricsInfo.java │ │ ├── LyricsLineInfo.java │ │ ├── LyricsTag.java │ │ ├── TranslateLrcLineInfo.java │ │ └── make │ │ │ ├── MakeExtraLrcLineInfo.java │ │ │ ├── MakeLrcInfo.java │ │ │ └── MakeLrcLineInfo.java │ │ ├── utils │ │ ├── CharUtils.java │ │ ├── ColorUtils.java │ │ ├── FileUtils.java │ │ ├── LyricsIOUtils.java │ │ ├── LyricsUtils.java │ │ ├── StringCompressUtils.java │ │ ├── StringUtils.java │ │ ├── TimeUtils.java │ │ └── UnicodeInputStream.java │ │ └── widget │ │ ├── AbstractLrcView.java │ │ ├── FloatLyricsView.java │ │ ├── ManyLyricsView.java │ │ └── make │ │ ├── MakeLrcPreView.java │ │ └── MakeLyricsView.java │ └── res │ └── values │ └── strings.xml ├── readme.md └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.0.0' 11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 12 | 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | } 17 | 18 | allprojects { 19 | repositories { 20 | google() 21 | jcenter() 22 | maven { url 'https://jitpack.io' } 23 | } 24 | } 25 | 26 | task clean(type: Delete) { 27 | delete rootProject.buildDir 28 | } 29 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /hplyricslibrary/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /hplyricslibrary/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | group = 'com.github.zhangliangming' 4 | android { 5 | compileSdkVersion 28 6 | defaultConfig { 7 | 8 | minSdkVersion 19 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | 15 | } 16 | buildTypes { 17 | release { 18 | zipAlignEnabled true 19 | minifyEnabled true 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | buildToolsVersion '28.0.2' 24 | } 25 | 26 | dependencies { 27 | implementation 'com.github.zhangliangming:Register:v1.1' 28 | } 29 | -------------------------------------------------------------------------------- /hplyricslibrary/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | -keep class com.zlm.hp.lyrics.** 24 | -keepclassmembers class com.zlm.hp.lyrics.** { 25 | public *; 26 | } 27 | -keep class com.zlm.libs.register.** { *; } -------------------------------------------------------------------------------- /hplyricslibrary/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/LyricsReader.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics; 2 | 3 | import android.util.Base64; 4 | 5 | import com.zlm.hp.lyrics.formats.LyricsFileReader; 6 | import com.zlm.hp.lyrics.model.LyricsInfo; 7 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 8 | import com.zlm.hp.lyrics.model.LyricsTag; 9 | import com.zlm.hp.lyrics.utils.LyricsIOUtils; 10 | import com.zlm.hp.lyrics.utils.LyricsUtils; 11 | 12 | import java.io.File; 13 | import java.util.List; 14 | import java.util.Map; 15 | import java.util.TreeMap; 16 | 17 | /** 18 | * 歌词读管理器 19 | * Created by zhangliangming on 2018-02-25. 20 | */ 21 | 22 | public class LyricsReader { 23 | 24 | /** 25 | * 时间补偿值,其单位是毫秒,正值表示整体提前,负值相反。这是用于总体调整显示快慢的。 26 | */ 27 | private long mDefOffset = 0; 28 | /** 29 | * 增量 30 | */ 31 | private long mOffset = 0; 32 | 33 | /** 34 | * 歌词类型 35 | */ 36 | private int mLyricsType = LyricsInfo.DYNAMIC; 37 | 38 | /** 39 | * 歌词文件路径 40 | */ 41 | private String mLrcFilePath; 42 | 43 | /** 44 | * 文件hash 45 | */ 46 | private String mHash; 47 | 48 | /** 49 | * 原始歌词列表 50 | */ 51 | private TreeMap mLrcLineInfos; 52 | /** 53 | * 原始翻译行歌词列表 54 | */ 55 | private List mTranslateLrcLineInfos; 56 | /** 57 | * 原始音译歌词行 58 | */ 59 | private List mTransliterationLrcLineInfos; 60 | 61 | private LyricsInfo mLyricsInfo; 62 | 63 | public LyricsReader() { 64 | 65 | } 66 | 67 | 68 | /** 69 | * 加载歌词数据 70 | * 71 | * @param lyricsFile 72 | */ 73 | public void loadLrc(File lyricsFile) throws Exception { 74 | 75 | this.mLrcFilePath = lyricsFile.getPath(); 76 | LyricsFileReader lyricsFileReader = LyricsIOUtils.getLyricsFileReader(lyricsFile); 77 | LyricsInfo lyricsInfo = lyricsFileReader.readFile(lyricsFile); 78 | parser(lyricsInfo); 79 | } 80 | 81 | /** 82 | * @param base64FileContentString 歌词base64文件 83 | * @param saveLrcFile 要保存的的lrc文件 84 | */ 85 | public void loadLrc(String base64FileContentString, File saveLrcFile) throws Exception { 86 | loadLrc(Base64.decode(base64FileContentString, Base64.NO_WRAP), saveLrcFile, saveLrcFile.getName()); 87 | } 88 | 89 | /** 90 | * @param base64FileContentString 歌词base64文件 91 | * @param saveLrcFile 要保存的的lrc文件 92 | * @param fileName 含后缀名的文件名称 93 | */ 94 | public void loadLrc(String base64FileContentString, File saveLrcFile, String fileName) throws Exception { 95 | loadLrc(Base64.decode(base64FileContentString, Base64.NO_WRAP), saveLrcFile, fileName); 96 | } 97 | 98 | 99 | /** 100 | * @param base64ByteArray 歌词base64数组 101 | * @param saveLrcFile 102 | */ 103 | public void loadLrc(byte[] base64ByteArray, File saveLrcFile) throws Exception { 104 | loadLrc(base64ByteArray, saveLrcFile, saveLrcFile.getName()); 105 | } 106 | 107 | 108 | /** 109 | * @param base64ByteArray 歌词base64数组 110 | * @param saveLrcFile 111 | * @param fileName 112 | */ 113 | public void loadLrc(byte[] base64ByteArray, File saveLrcFile, String fileName) throws Exception { 114 | if (saveLrcFile != null) 115 | mLrcFilePath = saveLrcFile.getPath(); 116 | LyricsFileReader lyricsFileReader = LyricsIOUtils.getLyricsFileReader(fileName); 117 | LyricsInfo lyricsInfo = lyricsFileReader.readLrcText(base64ByteArray, saveLrcFile); 118 | parser(lyricsInfo); 119 | 120 | } 121 | 122 | /** 123 | * @param lrcContent lrc歌词内容 124 | * @param saveLrcFile 歌词文件保存路径 125 | * @return 126 | * @throws Exception 127 | */ 128 | public void readLrcText(String lrcContent, File saveLrcFile) throws Exception { 129 | readLrcText(lrcContent, null, saveLrcFile, saveLrcFile.getName()); 130 | } 131 | 132 | /** 133 | * @param lrcContent lrc歌词内容 134 | * @param extraLrcContent 额外歌词内容(翻译歌词、音译歌词) 135 | * @param saveLrcFile 歌词文件保存路径 136 | * @return 137 | * @throws Exception 138 | */ 139 | public void readLrcText(String lrcContent, String extraLrcContent, File saveLrcFile) throws Exception { 140 | readLrcText(lrcContent, extraLrcContent, saveLrcFile, saveLrcFile.getName()); 141 | } 142 | 143 | /** 144 | * @param lrcContent lrc歌词内容 145 | * @param extraLrcContent 额外歌词内容(翻译歌词、音译歌词) 146 | * @param saveLrcFile 歌词文件保存路径 147 | * @return 148 | * @throws Exception 149 | */ 150 | public void readLrcText(String lrcContent, String extraLrcContent, File saveLrcFile, String fileName) throws Exception { 151 | readLrcText(null, lrcContent, extraLrcContent, saveLrcFile, fileName); 152 | } 153 | 154 | /** 155 | * @param dynamicContent 动感歌词内容 156 | * @param lrcContent lrc歌词内容 157 | * @param extraLrcContent 额外歌词内容(翻译歌词、音译歌词) 158 | * @param saveLrcFile 歌词文件保存路径 159 | * @return 160 | * @throws Exception 161 | */ 162 | public void readLrcText(String dynamicContent, String lrcContent, String extraLrcContent, File saveLrcFile) throws Exception { 163 | readLrcText(dynamicContent, lrcContent, extraLrcContent, saveLrcFile, saveLrcFile.getName()); 164 | } 165 | 166 | /** 167 | * @param dynamicContent 动感歌词内容 168 | * @param lrcContent lrc歌词内容 169 | * @param extraLrcContent 额外歌词内容(翻译歌词、音译歌词) 170 | * @param saveLrcFile 歌词文件保存路径 171 | * @return 172 | * @throws Exception 173 | */ 174 | public void readLrcText(String dynamicContent, String lrcContent, String extraLrcContent, File saveLrcFile, String fileName) throws Exception { 175 | if (saveLrcFile != null) 176 | mLrcFilePath = saveLrcFile.getPath(); 177 | LyricsFileReader lyricsFileReader = LyricsIOUtils.getLyricsFileReader(fileName); 178 | LyricsInfo lyricsInfo = lyricsFileReader.readLrcText(dynamicContent, lrcContent, extraLrcContent, saveLrcFile.getPath()); 179 | parser(lyricsInfo); 180 | } 181 | 182 | 183 | /** 184 | * 解析 185 | * 186 | * @param lyricsInfo 187 | */ 188 | private void parser(LyricsInfo lyricsInfo) { 189 | mLyricsInfo = lyricsInfo; 190 | mLyricsType = lyricsInfo.getLyricsType(); 191 | Map tags = lyricsInfo.getLyricsTags(); 192 | if (tags.containsKey(LyricsTag.TAG_OFFSET)) { 193 | mDefOffset = 0; 194 | try { 195 | mDefOffset = Long.parseLong((String) tags.get(LyricsTag.TAG_OFFSET)); 196 | } catch (Exception e) { 197 | e.printStackTrace(); 198 | } 199 | } else { 200 | mDefOffset = 0; 201 | } 202 | //默认歌词行 203 | mLrcLineInfos = lyricsInfo.getLyricsLineInfoTreeMap(); 204 | //翻译歌词集合 205 | if (lyricsInfo.getTranslateLrcLineInfos() != null && lyricsInfo.getTranslateLrcLineInfos().size() > 0) 206 | mTranslateLrcLineInfos = LyricsUtils.getTranslateLrc(mLyricsType, mLrcLineInfos, lyricsInfo.getTranslateLrcLineInfos()); 207 | //音译歌词集合 208 | if (lyricsInfo.getTransliterationLrcLineInfos() != null && lyricsInfo.getTransliterationLrcLineInfos().size() > 0) 209 | mTransliterationLrcLineInfos = LyricsUtils.getTransliterationLrc(mLyricsType, mLrcLineInfos, lyricsInfo.getTransliterationLrcLineInfos()); 210 | 211 | } 212 | 213 | 214 | //////////////////////////////////////////////////////////////////////////////// 215 | 216 | 217 | public int getLyricsType() { 218 | return mLyricsType; 219 | } 220 | 221 | public TreeMap getLrcLineInfos() { 222 | return mLrcLineInfos; 223 | } 224 | 225 | public List getTranslateLrcLineInfos() { 226 | return mTranslateLrcLineInfos; 227 | } 228 | 229 | public List getTransliterationLrcLineInfos() { 230 | return mTransliterationLrcLineInfos; 231 | } 232 | 233 | public String getHash() { 234 | return mHash; 235 | } 236 | 237 | public void setHash(String mHash) { 238 | this.mHash = mHash; 239 | } 240 | 241 | public String getLrcFilePath() { 242 | return mLrcFilePath; 243 | } 244 | 245 | public void setLrcFilePath(String mLrcFilePath) { 246 | this.mLrcFilePath = mLrcFilePath; 247 | } 248 | 249 | public long getOffset() { 250 | return mOffset; 251 | } 252 | 253 | public void setOffset(long offset) { 254 | this.mOffset = offset; 255 | } 256 | 257 | public LyricsInfo getLyricsInfo() { 258 | return mLyricsInfo; 259 | } 260 | 261 | public void setLyricsType(int mLyricsType) { 262 | this.mLyricsType = mLyricsType; 263 | } 264 | 265 | public void setLrcLineInfos(TreeMap mLrcLineInfos) { 266 | this.mLrcLineInfos = mLrcLineInfos; 267 | } 268 | 269 | public void setTranslateLrcLineInfos(List mTranslateLrcLineInfos) { 270 | this.mTranslateLrcLineInfos = mTranslateLrcLineInfos; 271 | } 272 | 273 | public void setTransliterationLrcLineInfos(List mTransliterationLrcLineInfos) { 274 | this.mTransliterationLrcLineInfos = mTransliterationLrcLineInfos; 275 | } 276 | 277 | public void setLyricsInfo(LyricsInfo lyricsInfo) { 278 | /** 279 | * 重新解析歌词 280 | */ 281 | parser(lyricsInfo); 282 | } 283 | 284 | //////////////////////////////////////////////////////////////////////////////// 285 | 286 | /** 287 | * 播放的时间补偿值 288 | * 289 | * @return 290 | */ 291 | public long getPlayOffset() { 292 | return mDefOffset + mOffset; 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/LyricsFileReader.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats; 2 | 3 | import android.util.Base64; 4 | 5 | import com.zlm.hp.lyrics.model.LyricsInfo; 6 | 7 | import java.io.BufferedInputStream; 8 | import java.io.ByteArrayInputStream; 9 | import java.io.File; 10 | import java.io.FileInputStream; 11 | import java.io.FileOutputStream; 12 | import java.io.IOException; 13 | import java.io.InputStream; 14 | import java.nio.charset.Charset; 15 | 16 | /** 17 | * @Description: 歌词文件读取器 18 | * @Author: zhangliangming 19 | * @Date: 2017/12/25 16:08 20 | * @Version: 21 | */ 22 | public abstract class LyricsFileReader { 23 | /** 24 | * 默认编码 25 | */ 26 | private Charset defaultCharset = Charset.forName("utf-8"); 27 | 28 | /** 29 | * 读取歌词文件 30 | * 31 | * @param file 32 | * @return 33 | */ 34 | public LyricsInfo readFile(File file) throws Exception { 35 | if (file != null) { 36 | return readInputStream(new FileInputStream(file)); 37 | } 38 | return null; 39 | } 40 | 41 | /** 42 | * 读取歌词文本内容 43 | * 44 | * @param base64FileContentString base64位文件内容 45 | * @param saveLrcFile 要保存的歌词文件 46 | * @return 47 | * @throws Exception 48 | */ 49 | public LyricsInfo readLrcText(String base64FileContentString, 50 | File saveLrcFile) throws Exception { 51 | byte[] fileContent = Base64.decode(base64FileContentString, Base64.NO_WRAP); 52 | 53 | if (saveLrcFile != null) { 54 | // 生成歌词文件 55 | FileOutputStream os = new FileOutputStream(saveLrcFile); 56 | os.write(fileContent); 57 | os.close(); 58 | 59 | os = null; 60 | } 61 | 62 | return readInputStream(new ByteArrayInputStream(fileContent)); 63 | } 64 | 65 | /** 66 | * 读取歌词文本内容 67 | * 68 | * @param base64ByteArray base64内容数组 69 | * @param saveLrcFile 70 | * @return 71 | * @throws Exception 72 | */ 73 | public LyricsInfo readLrcText(byte[] base64ByteArray, 74 | File saveLrcFile) throws Exception { 75 | if (saveLrcFile != null) { 76 | // 生成歌词文件 77 | FileOutputStream os = new FileOutputStream(saveLrcFile); 78 | os.write(base64ByteArray); 79 | os.close(); 80 | 81 | os = null; 82 | } 83 | 84 | return readInputStream(new ByteArrayInputStream(base64ByteArray)); 85 | } 86 | 87 | /** 88 | * @param dynamicContent 动感歌词内容 89 | * @param lrcContent lrc歌词内容 90 | * @param extraLrcContent 额外歌词内容(翻译歌词、音译歌词) 91 | * @param lyricsFilePath 歌词文件保存路径 92 | * @return 93 | * @throws Exception 94 | */ 95 | public abstract LyricsInfo readLrcText(String dynamicContent, String lrcContent, String extraLrcContent, String lyricsFilePath) throws Exception; 96 | 97 | 98 | /** 99 | * 读取歌词文件 100 | * 101 | * @param in 102 | * @return 103 | */ 104 | public abstract LyricsInfo readInputStream(InputStream in) throws Exception; 105 | 106 | /** 107 | * 支持文件格式 108 | * 109 | * @param ext 文件后缀名 110 | * @return 111 | */ 112 | public abstract boolean isFileSupported(String ext); 113 | 114 | /** 115 | * 获取支持的文件后缀名 116 | * 117 | * @return 118 | */ 119 | public abstract String getSupportFileExt(); 120 | 121 | public void setDefaultCharset(Charset charset) { 122 | defaultCharset = charset; 123 | } 124 | 125 | public Charset getDefaultCharset() { 126 | return defaultCharset; 127 | } 128 | 129 | /** 130 | * 判断文件的编码格式 131 | * 132 | * @param file 133 | * @return 134 | */ 135 | public String getCharsetName(File file) { 136 | String code = "GBK"; 137 | BufferedInputStream bin = null; 138 | try { 139 | bin = new BufferedInputStream(new FileInputStream(file)); 140 | int p = (bin.read() << 8) + bin.read(); 141 | 142 | switch (p) { 143 | case 0xefbb: 144 | code = "UTF-8"; 145 | break; 146 | case 0xfffe: 147 | code = "Unicode"; 148 | break; 149 | case 0xfeff: 150 | code = "UTF-16BE"; 151 | break; 152 | } 153 | 154 | } catch (Exception e) { 155 | e.printStackTrace(); 156 | } finally { 157 | if (bin != null) { 158 | try { 159 | bin.close(); 160 | } catch (IOException e) { 161 | e.printStackTrace(); 162 | } 163 | } 164 | } 165 | return code; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/LyricsFileWriter.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats; 2 | 3 | import com.zlm.hp.lyrics.model.LyricsInfo; 4 | 5 | import java.io.File; 6 | import java.io.FileOutputStream; 7 | import java.io.OutputStreamWriter; 8 | import java.io.PrintWriter; 9 | import java.nio.charset.Charset; 10 | 11 | /** 12 | * @Description: 歌词文件生成器 13 | * @Param: 14 | * @Return: 15 | * @Author: zhangliangming 16 | * @Date: 2017/12/25 16:42 17 | * @Throws: 18 | */ 19 | public abstract class LyricsFileWriter { 20 | /** 21 | * 默认编码 22 | */ 23 | private Charset defaultCharset = Charset.forName("utf-8"); 24 | 25 | /** 26 | * 保存歌词文件 27 | * 28 | * @param lyricsIfno 歌词数据 29 | * @param lyricsFilePath 歌词文件路径 30 | */ 31 | public abstract boolean writer(LyricsInfo lyricsIfno, String lyricsFilePath) 32 | throws Exception; 33 | 34 | 35 | /** 36 | * 保存歌词文件到本地 37 | * 38 | * @param lyricsContent 歌词文件内容文本 39 | * @param lyricsFilePath 歌词文件路径 40 | * @return 41 | * @throws Exception 42 | */ 43 | public boolean saveLyricsFile(String lyricsContent, String lyricsFilePath) throws Exception { 44 | 45 | File lyricsFile = new File(lyricsFilePath); 46 | if (lyricsFile != null) { 47 | // 48 | if (!lyricsFile.getParentFile().exists()) { 49 | lyricsFile.getParentFile().mkdirs(); 50 | } 51 | OutputStreamWriter outstream = new OutputStreamWriter( 52 | new FileOutputStream(lyricsFilePath), 53 | getDefaultCharset()); 54 | PrintWriter writer = new PrintWriter(outstream); 55 | writer.write(lyricsContent); 56 | writer.close(); 57 | 58 | outstream = null; 59 | writer = null; 60 | 61 | return true; 62 | } 63 | 64 | return false; 65 | } 66 | 67 | /** 68 | * 保存歌词文件到本地 69 | * 70 | * @param lyricsContent 歌词文件内容文本 71 | * @param lyricsFilePath 歌词文件路径 72 | * @return 73 | * @throws Exception 74 | */ 75 | public boolean saveLyricsFile(byte[] lyricsContent, String lyricsFilePath) throws Exception { 76 | 77 | File lyricsFile = new File(lyricsFilePath); 78 | if (lyricsFile != null) { 79 | // 80 | if (!lyricsFile.getParentFile().exists()) { 81 | lyricsFile.getParentFile().mkdirs(); 82 | } 83 | FileOutputStream os = new FileOutputStream(lyricsFile); 84 | os.write(lyricsContent); 85 | os.close(); 86 | 87 | os = null; 88 | 89 | return true; 90 | } 91 | return false; 92 | } 93 | 94 | /** 95 | * 获取歌词文件内容 96 | * 97 | * @param lyricsIfno 歌词内容类 98 | * @return 99 | * @throws Exception 100 | */ 101 | public abstract String getLyricsContent(LyricsInfo lyricsIfno) throws Exception; 102 | 103 | /** 104 | * 支持文件格式 105 | * 106 | * @param ext 文件后缀名 107 | * @return 108 | */ 109 | public abstract boolean isFileSupported(String ext); 110 | 111 | /** 112 | * 获取支持的文件后缀名 113 | * 114 | * @return 115 | */ 116 | public abstract String getSupportFileExt(); 117 | 118 | public void setDefaultCharset(Charset charset) { 119 | defaultCharset = charset; 120 | } 121 | 122 | public Charset getDefaultCharset() { 123 | return defaultCharset; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/hrc/HrcLyricsFileReader.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats.hrc; 2 | 3 | import android.util.Base64; 4 | 5 | import com.zlm.hp.lyrics.formats.LyricsFileReader; 6 | import com.zlm.hp.lyrics.model.LyricsInfo; 7 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 8 | import com.zlm.hp.lyrics.model.LyricsTag; 9 | import com.zlm.hp.lyrics.model.TranslateLrcLineInfo; 10 | import com.zlm.hp.lyrics.utils.StringCompressUtils; 11 | import com.zlm.hp.lyrics.utils.StringUtils; 12 | 13 | import org.json.JSONArray; 14 | import org.json.JSONObject; 15 | 16 | import java.io.InputStream; 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | import java.util.Iterator; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.SortedMap; 23 | import java.util.TreeMap; 24 | 25 | /** 26 | * @Description: hrc歌词解析,乐乐音乐的自定义歌词 27 | * @Param: 28 | * @Return: 29 | * @Author: zhangliangming 30 | * @Date: 2017/12/25 16:40 31 | * @Throws: 32 | */ 33 | public class HrcLyricsFileReader extends LyricsFileReader { 34 | /** 35 | * 歌曲名 字符串 36 | */ 37 | private final static String LEGAL_TITLE_PREFIX = "[ti:"; 38 | /** 39 | * 歌手名 字符串 40 | */ 41 | private final static String LEGAL_ARTIST_PREFIX = "[ar:"; 42 | /** 43 | * 时间补偿值 字符串 44 | */ 45 | private final static String LEGAL_OFFSET_PREFIX = "[offset:"; 46 | /** 47 | * 歌曲长度 48 | */ 49 | private final static String LEGAL_TOTAL_PREFIX = "[total:"; 50 | /** 51 | * 上传者 52 | */ 53 | private final static String LEGAL_BY_PREFIX = "[by:"; 54 | /** 55 | * Tag标签 56 | */ 57 | private final static String LEGAL_TAG_PREFIX = "haplayer.tag["; 58 | 59 | /** 60 | * 歌词 字符串 61 | */ 62 | public final static String LEGAL_LYRICS_LINE_PREFIX = "haplayer.lrc"; 63 | /** 64 | * 额外歌词 65 | */ 66 | private final static String LEGAL_EXTRA_LYRICS_PREFIX = "haplayer.extra.lrc"; 67 | 68 | public HrcLyricsFileReader() { 69 | } 70 | 71 | @Override 72 | public LyricsInfo readLrcText(String dynamicContent, String lrcContent, String extraLrcContent, String lyricsFilePath) throws Exception { 73 | return null; 74 | } 75 | 76 | @Override 77 | public LyricsInfo readInputStream(InputStream in) throws Exception { 78 | LyricsInfo lyricsIfno = new LyricsInfo(); 79 | lyricsIfno.setLyricsFileExt(getSupportFileExt()); 80 | if (in != null) { 81 | // 获取歌词文件里面的所有内容,并对文本内容进行解压 82 | String lyricsTextStr = StringCompressUtils.decompress(in, 83 | getDefaultCharset()); 84 | String[] lyricsTexts = lyricsTextStr.split("\n"); 85 | // 这里面key为该行歌词的开始时间,方便后面排序 86 | SortedMap lyricsLineInfosTemp = new TreeMap(); 87 | Map lyricsTags = new HashMap(); 88 | for (int i = 0; i < lyricsTexts.length; i++) { 89 | 90 | // 解析歌词 91 | parserLineInfos(lyricsIfno, lyricsLineInfosTemp, 92 | lyricsTags, lyricsTexts[i]); 93 | 94 | } 95 | in.close(); 96 | in = null; 97 | // 重新封装 98 | TreeMap lyricsLineInfos = new TreeMap(); 99 | int index = 0; 100 | Iterator it = lyricsLineInfosTemp.keySet().iterator(); 101 | while (it.hasNext()) { 102 | lyricsLineInfos 103 | .put(index++, lyricsLineInfosTemp.get(it.next())); 104 | } 105 | it = null; 106 | // 设置歌词的标签类 107 | lyricsIfno.setLyricsTags(lyricsTags); 108 | // 109 | lyricsIfno.setLyricsLineInfoTreeMap(lyricsLineInfos); 110 | } 111 | return lyricsIfno; 112 | } 113 | 114 | /** 115 | * 解析每行的歌词 116 | * 117 | * @param lyricsLineInfos 118 | * @param lyricsTags 119 | * @param lineInfo 120 | */ 121 | private void parserLineInfos(LyricsInfo lyricsIfno, 122 | SortedMap lyricsLineInfos, 123 | Map lyricsTags, String lineInfo) throws Exception { 124 | if (lineInfo.startsWith(LEGAL_TITLE_PREFIX)) { 125 | 126 | int start = LEGAL_TITLE_PREFIX.length(); 127 | int end = lineInfo.lastIndexOf("]"); 128 | String tagValue = lineInfo.substring(start, end); 129 | lyricsTags.put(LyricsTag.TAG_TITLE, tagValue); 130 | 131 | } else if (lineInfo.startsWith(LEGAL_ARTIST_PREFIX)) { 132 | 133 | int start = LEGAL_ARTIST_PREFIX.length(); 134 | int end = lineInfo.lastIndexOf("]"); 135 | String tagValue = lineInfo.substring(start, end); 136 | 137 | lyricsTags.put(LyricsTag.TAG_ARTIST, tagValue); 138 | 139 | } else if (lineInfo.startsWith(LEGAL_OFFSET_PREFIX)) { 140 | 141 | int start = LEGAL_OFFSET_PREFIX.length(); 142 | int end = lineInfo.lastIndexOf("]"); 143 | String tagValue = lineInfo.substring(start, end); 144 | lyricsTags.put(LyricsTag.TAG_OFFSET, tagValue); 145 | 146 | } else if (lineInfo.startsWith(LEGAL_BY_PREFIX)) { 147 | 148 | int start = LEGAL_BY_PREFIX.length(); 149 | int end = lineInfo.lastIndexOf("]"); 150 | String tagValue = lineInfo.substring(start, end); 151 | lyricsTags.put(LyricsTag.TAG_BY, tagValue); 152 | 153 | } else if (lineInfo.startsWith(LEGAL_TOTAL_PREFIX)) { 154 | 155 | int start = LEGAL_TOTAL_PREFIX.length(); 156 | int end = lineInfo.lastIndexOf("]"); 157 | String tagValue = lineInfo.substring(start, end); 158 | lyricsTags.put(LyricsTag.TAG_TOTAL, tagValue); 159 | 160 | } else if (lineInfo.startsWith(LEGAL_TAG_PREFIX)) { 161 | 162 | int start = LEGAL_TAG_PREFIX.length(); 163 | int end = lineInfo.lastIndexOf("]"); 164 | String tagValue = lineInfo.substring(start, end); 165 | String temp[] = tagValue.split(":"); 166 | lyricsTags.put(temp[0], temp.length == 1 ? "" : temp[1]); 167 | 168 | } else if (lineInfo.startsWith(LEGAL_EXTRA_LYRICS_PREFIX)) { 169 | int leftIndex = lineInfo.indexOf('\''); 170 | int rightIndex = lineInfo.lastIndexOf('\''); 171 | 172 | // 解析翻译歌词 173 | // 获取json base64字符串 174 | String translateJsonBase64String = lineInfo.substring(leftIndex + 1, 175 | rightIndex); 176 | if (!translateJsonBase64String.equals("")) { 177 | String translateJsonString = new String( 178 | Base64.decode(translateJsonBase64String, Base64.NO_WRAP)); 179 | parserOtherLrc(lyricsIfno, translateJsonString); 180 | } 181 | 182 | } else if (lineInfo.startsWith(LEGAL_LYRICS_LINE_PREFIX)) { 183 | int leftIndex = lineInfo.indexOf('\''); 184 | int rightIndex = lineInfo.lastIndexOf('\''); 185 | 186 | String[] lineComments = lineInfo.substring(leftIndex + 1, rightIndex) 187 | .split("'\\s*,\\s*'", -1); 188 | // 歌词 189 | String lineLyricsStr = lineComments[1]; 190 | 191 | // 歌词分隔 192 | String[] lyricsWords = getLyricsWords(lineLyricsStr); 193 | 194 | // 获取当行歌词 195 | String lineLyrics = getLineLyrics(lineLyricsStr); 196 | 197 | // 时间标签 198 | String timeText = lineComments[0]; 199 | int timeLeft = timeText.indexOf('<'); 200 | int timeRight = timeText.lastIndexOf('>'); 201 | timeText = timeText.substring(timeLeft + 1, timeRight); 202 | String[] timeTexts = timeText.split("><"); 203 | 204 | // 每个歌词的时间标签 205 | String wordsDisIntervalText = lineComments[2]; 206 | int wordsDisIntervalLeft = wordsDisIntervalText.indexOf('<'); 207 | int wordsDisIntervalRight = wordsDisIntervalText.lastIndexOf('>'); 208 | wordsDisIntervalText = wordsDisIntervalText.substring(wordsDisIntervalLeft + 1, wordsDisIntervalRight); 209 | String[] wordsDisIntervalTexts = wordsDisIntervalText.split("><"); 210 | 211 | parserLineInfos(lyricsLineInfos, lyricsWords, lineLyrics, 212 | timeTexts, wordsDisIntervalTexts); 213 | } 214 | 215 | } 216 | 217 | /** 218 | * 解析翻译和音译歌词 219 | * 220 | * @param lyricsIfno 221 | * @param translateJsonString 222 | */ 223 | private void parserOtherLrc(LyricsInfo lyricsIfno, 224 | String translateJsonString) throws Exception { 225 | 226 | JSONObject resultObj = new JSONObject(translateJsonString); 227 | JSONArray contentArrayObj = resultObj.getJSONArray("content"); 228 | for (int i = 0; i < contentArrayObj.length(); i++) { 229 | JSONObject dataObj = contentArrayObj.getJSONObject(i); 230 | JSONArray lyricContentArrayObj = dataObj 231 | .getJSONArray("lyricContent"); 232 | int type = dataObj.getInt("lyricType"); 233 | if (type == 1) { 234 | // 解析翻译歌词 235 | if (lyricsIfno.getTranslateLrcLineInfos() == null || lyricsIfno.getTranslateLrcLineInfos().size() == 0) 236 | parserTranslateLrc(lyricsIfno, lyricContentArrayObj); 237 | 238 | } else if (type == 0) { 239 | // 解析音译歌词 240 | if (lyricsIfno.getTransliterationLrcLineInfos() == null || lyricsIfno.getTransliterationLrcLineInfos().size() == 0) 241 | parserTransliterationLrc(lyricsIfno, 242 | lyricContentArrayObj); 243 | } 244 | } 245 | } 246 | 247 | /** 248 | * 解析音译歌词 249 | * 250 | * @param lyricsIfno 251 | * @param lyricContentArrayObj 252 | */ 253 | private void parserTransliterationLrc(LyricsInfo lyricsIfno, 254 | JSONArray lyricContentArrayObj) throws Exception { 255 | 256 | // 音译歌词集合 257 | List transliterationLrcLineInfos = new ArrayList(); 258 | // 获取歌词内容 259 | for (int j = 0; j < lyricContentArrayObj.length(); j++) { 260 | JSONArray lrcDataArrayObj = lyricContentArrayObj.getJSONArray(j); 261 | // 音译行歌词 262 | LyricsLineInfo transliterationLrcLineInfo = new LyricsLineInfo(); 263 | String[] lyricsWords = new String[lrcDataArrayObj.length()]; 264 | StringBuilder lineLyrics = new StringBuilder(); 265 | for (int k = 0; k < lrcDataArrayObj.length(); k++) { 266 | if (k == lrcDataArrayObj.length() - 1) { 267 | lyricsWords[k] = lrcDataArrayObj.getString(k).trim(); 268 | } else { 269 | lyricsWords[k] = lrcDataArrayObj.getString(k).trim() + " "; 270 | } 271 | lineLyrics.append(lyricsWords[k]); 272 | } 273 | transliterationLrcLineInfo.setLineLyrics(lineLyrics.toString()); 274 | transliterationLrcLineInfo.setLyricsWords(lyricsWords); 275 | 276 | transliterationLrcLineInfos.add(transliterationLrcLineInfo); 277 | } 278 | // 添加音译歌词 279 | if (transliterationLrcLineInfos.size() > 0) { 280 | lyricsIfno.setTransliterationLrcLineInfos(transliterationLrcLineInfos); 281 | } 282 | } 283 | 284 | /** 285 | * 解析翻译歌词 286 | * 287 | * @param lyricsIfno 288 | * @param lyricContentArrayObj 289 | */ 290 | private void parserTranslateLrc(LyricsInfo lyricsIfno, 291 | JSONArray lyricContentArrayObj) throws Exception { 292 | 293 | // 翻译歌词集合 294 | List translateLrcLineInfos = new ArrayList(); 295 | 296 | // 获取歌词内容 297 | for (int j = 0; j < lyricContentArrayObj.length(); j++) { 298 | JSONArray lrcDataArrayObj = lyricContentArrayObj.getJSONArray(j); 299 | String lrcComtext = lrcDataArrayObj.getString(0); 300 | 301 | // 翻译行歌词 302 | TranslateLrcLineInfo translateLrcLineInfo = new TranslateLrcLineInfo(); 303 | translateLrcLineInfo.setLineLyrics(lrcComtext); 304 | 305 | translateLrcLineInfos.add(translateLrcLineInfo); 306 | } 307 | // 添加翻译歌词 308 | if (translateLrcLineInfos.size() > 0) { 309 | lyricsIfno.setTranslateLrcLineInfos(translateLrcLineInfos); 310 | } 311 | } 312 | 313 | /** 314 | * 解析每行歌词的数据 315 | * 316 | * @param lyricsLineInfos 317 | * @param lyricsWords 歌词 318 | * @param lineLyrics 该行歌词 319 | * @param timeTexts 时间文本 320 | * @param wordsDisIntervalTexts 321 | */ 322 | private void parserLineInfos( 323 | SortedMap lyricsLineInfos, 324 | String[] lyricsWords, String lineLyrics, String[] timeTexts, 325 | String[] wordsDisIntervalTexts) throws Exception { 326 | if (timeTexts.length == wordsDisIntervalTexts.length) { 327 | for (int i = 0; i < wordsDisIntervalTexts.length; i++) { 328 | 329 | LyricsLineInfo lyricsLineInfo = new LyricsLineInfo(); 330 | 331 | // 每一行的开始时间和结束时间 332 | String timeTextStr = timeTexts[i]; 333 | String[] timeTextCom = timeTextStr.split(","); 334 | 335 | String startTimeStr = timeTextCom[0]; 336 | int startTime = Integer.parseInt(startTimeStr); 337 | 338 | String endTimeStr = timeTextCom[1]; 339 | int endTime = Integer.parseInt(endTimeStr); 340 | 341 | lyricsLineInfo.setEndTime(endTime); 342 | lyricsLineInfo.setStartTime(startTime); 343 | 344 | // 345 | lyricsLineInfo.setLineLyrics(lineLyrics); 346 | lyricsLineInfo.setLyricsWords(lyricsWords); 347 | 348 | // 每一行歌词的每个时间 349 | int wordsDisInterval[] = getWordsDisIntervalString(wordsDisIntervalTexts[i]); 350 | 351 | //验证 352 | if (lyricsWords.length != wordsDisInterval.length) { 353 | throw new Exception("字标签个数与字时间标签个数不相符"); 354 | } 355 | 356 | lyricsLineInfo.setWordsDisInterval(wordsDisInterval); 357 | // 358 | lyricsLineInfos.put(startTime, lyricsLineInfo); 359 | } 360 | } else { 361 | throw new Exception("开始与结束的标签个数与行字分配时间的标签个数不相等"); 362 | } 363 | } 364 | 365 | /** 366 | * 获取每个歌词的时间 367 | * 368 | * @param wordsDisIntervalString 369 | * @return 370 | */ 371 | private int[] getWordsDisIntervalString(String wordsDisIntervalString) throws Exception { 372 | String[] wordsDisIntervalStr = wordsDisIntervalString.split(","); 373 | int wordsDisInterval[] = new int[wordsDisIntervalStr.length]; 374 | for (int i = 0; i < wordsDisIntervalStr.length; i++) { 375 | String wordDisIntervalStr = wordsDisIntervalStr[i]; 376 | if (StringUtils.isNumeric(wordDisIntervalStr)) 377 | wordsDisInterval[i] = Integer.parseInt(wordDisIntervalStr); 378 | else throw new Exception("字时间标签不能含有非数字字符串"); 379 | } 380 | return wordsDisInterval; 381 | } 382 | 383 | /** 384 | * 获取当前行歌词,去掉中括号 385 | * 386 | * @param lineLyricsStr 387 | * @return 388 | */ 389 | private String getLineLyrics(String lineLyricsStr) throws Exception { 390 | StringBuilder temp = new StringBuilder(); 391 | for (int i = 0; i < lineLyricsStr.length(); i++) { 392 | char c = lineLyricsStr.charAt(i); 393 | switch (c) { 394 | case '<': 395 | break; 396 | case '>': 397 | break; 398 | default: 399 | temp.append(c); 400 | break; 401 | } 402 | } 403 | return temp.toString(); 404 | } 405 | 406 | /** 407 | * 分隔每个歌词 408 | * 409 | * @param lineLyricsStr 410 | * @return 411 | */ 412 | private String[] getLyricsWords(String lineLyricsStr) throws Exception { 413 | int startIndex = lineLyricsStr.indexOf("<"); 414 | int endIndex = lineLyricsStr.lastIndexOf('>'); 415 | lineLyricsStr = lineLyricsStr.substring(startIndex + 1, endIndex); 416 | String lineLyrics[] = lineLyricsStr.split("><"); 417 | return lineLyrics; 418 | } 419 | 420 | @Override 421 | public boolean isFileSupported(String ext) { 422 | return ext.equalsIgnoreCase("hrc"); 423 | } 424 | 425 | @Override 426 | public String getSupportFileExt() { 427 | return "hrc"; 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/hrc/HrcLyricsFileWriter.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats.hrc; 2 | 3 | import android.util.Base64; 4 | 5 | import com.zlm.hp.lyrics.formats.LyricsFileWriter; 6 | import com.zlm.hp.lyrics.model.LyricsInfo; 7 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 8 | import com.zlm.hp.lyrics.model.LyricsTag; 9 | import com.zlm.hp.lyrics.model.TranslateLrcLineInfo; 10 | import com.zlm.hp.lyrics.utils.StringCompressUtils; 11 | 12 | import org.json.JSONArray; 13 | import org.json.JSONObject; 14 | 15 | import java.util.ArrayList; 16 | import java.util.LinkedHashMap; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.TreeMap; 20 | 21 | /** 22 | * @Description: hrc歌词 23 | * @Param: 24 | * @Return: 25 | * @Author: zhangliangming 26 | * @Date: 2017/12/25 17:32 27 | * @Throws: 28 | */ 29 | 30 | public class HrcLyricsFileWriter extends LyricsFileWriter { 31 | 32 | /** 33 | * 歌曲名 字符串 34 | */ 35 | private final static String LEGAL_TITLE_PREFIX = "[ti:"; 36 | /** 37 | * 歌手名 字符串 38 | */ 39 | private final static String LEGAL_ARTIST_PREFIX = "[ar:"; 40 | /** 41 | * 时间补偿值 字符串 42 | */ 43 | private final static String LEGAL_OFFSET_PREFIX = "[offset:"; 44 | /** 45 | * 歌曲长度 46 | */ 47 | private final static String LEGAL_TOTAL_PREFIX = "[total:"; 48 | /** 49 | * 上传者 50 | */ 51 | private final static String LEGAL_BY_PREFIX = "[by:"; 52 | /** 53 | * Tag标签 54 | */ 55 | private final static String LEGAL_TAG_PREFIX = "haplayer.tag["; 56 | 57 | /** 58 | * 歌词 字符串 59 | */ 60 | public final static String LEGAL_LYRICS_LINE_PREFIX = "haplayer.lrc"; 61 | /** 62 | * 额外歌词 63 | */ 64 | private final static String LEGAL_EXTRA_LYRICS_PREFIX = "haplayer.extra.lrc"; 65 | 66 | public HrcLyricsFileWriter() { 67 | } 68 | 69 | @Override 70 | public boolean writer(LyricsInfo lyricsIfno, String lyricsFilePath) throws Exception { 71 | // 对字符串运行压缩 72 | byte[] lyricsContent = StringCompressUtils.compress( 73 | getLyricsContent(lyricsIfno), getDefaultCharset()); 74 | return saveLyricsFile(lyricsContent, lyricsFilePath); 75 | } 76 | 77 | @Override 78 | public String getLyricsContent(LyricsInfo lyricsIfno) throws Exception { 79 | StringBuilder lyricsCom = new StringBuilder(); 80 | // 先保存所有的标签数据 81 | Map tags = lyricsIfno.getLyricsTags(); 82 | for (Map.Entry entry : tags.entrySet()) { 83 | Object val = entry.getValue(); 84 | if (entry.getKey().equals(LyricsTag.TAG_TITLE)) { 85 | lyricsCom.append(LEGAL_TITLE_PREFIX); 86 | } else if (entry.getKey().equals(LyricsTag.TAG_ARTIST)) { 87 | lyricsCom.append(LEGAL_ARTIST_PREFIX); 88 | } else if (entry.getKey().equals(LyricsTag.TAG_OFFSET)) { 89 | lyricsCom.append(LEGAL_OFFSET_PREFIX); 90 | } else if (entry.getKey().equals(LyricsTag.TAG_BY)) { 91 | lyricsCom.append(LEGAL_BY_PREFIX); 92 | } else if (entry.getKey().equals(LyricsTag.TAG_TOTAL)) { 93 | lyricsCom.append(LEGAL_TOTAL_PREFIX); 94 | } else { 95 | lyricsCom.append(LEGAL_TAG_PREFIX + entry.getKey() + ":"); 96 | } 97 | lyricsCom.append(val + "];\n"); 98 | } 99 | 100 | // 获取额外歌词行(翻译歌词和音译歌词) 101 | JSONObject extraLyricsObj = new JSONObject(); 102 | JSONArray contentArray = new JSONArray(); 103 | // 判断是否有翻译歌词 104 | if (lyricsIfno.getTranslateLrcLineInfos() != null && lyricsIfno.getTranslateLrcLineInfos().size() != 0) { 105 | List translateLrcLineInfos = lyricsIfno.getTranslateLrcLineInfos(); 106 | if (translateLrcLineInfos != null 107 | && translateLrcLineInfos.size() > 0) { 108 | JSONObject lyricsObj = new JSONObject(); 109 | JSONArray lyricContentArray = new JSONArray(); 110 | lyricsObj.put("lyricType", 1); 111 | for (int i = 0; i < translateLrcLineInfos.size(); i++) { 112 | JSONArray lyricArray = new JSONArray(); 113 | TranslateLrcLineInfo translateLrcLineInfo = translateLrcLineInfos 114 | .get(i); 115 | lyricArray.put(translateLrcLineInfo.getLineLyrics()); 116 | lyricContentArray.put(lyricArray); 117 | } 118 | if (lyricContentArray.length() > 0) { 119 | lyricsObj.put("lyricContent", lyricContentArray); 120 | contentArray.put(lyricsObj); 121 | } 122 | 123 | } 124 | } 125 | 126 | // 判断是否有音译歌词 127 | if (lyricsIfno.getTransliterationLrcLineInfos() != null && lyricsIfno.getTransliterationLrcLineInfos().size() != 0) { 128 | List lyricsLineInfos = lyricsIfno.getTransliterationLrcLineInfos(); 129 | if (lyricsLineInfos != null && lyricsLineInfos.size() > 0) { 130 | JSONObject lyricsObj = new JSONObject(); 131 | JSONArray lyricContentArray = new JSONArray(); 132 | lyricsObj.put("lyricType", 0); 133 | for (int i = 0; i < lyricsLineInfos.size(); i++) { 134 | 135 | LyricsLineInfo lyricsLineInfo = lyricsLineInfos.get(i); 136 | String[] lyricsWords = lyricsLineInfo.getLyricsWords(); 137 | JSONArray lyricArray = new JSONArray(); 138 | for (int j = 0; j < lyricsWords.length; j++) { 139 | lyricArray.put(lyricsWords[j].trim()); 140 | } 141 | lyricContentArray.put(lyricArray); 142 | } 143 | if (lyricContentArray.length() > 0) { 144 | lyricsObj.put("lyricContent", lyricContentArray); 145 | contentArray.put(lyricsObj); 146 | } 147 | } 148 | } 149 | // 150 | extraLyricsObj.put("content", contentArray); 151 | // 添加翻译和音译歌词 152 | lyricsCom.append(LEGAL_EXTRA_LYRICS_PREFIX 153 | + "('" 154 | + Base64.encodeToString(extraLyricsObj.toString() 155 | .getBytes(), Base64.NO_WRAP) + "');\n"); 156 | 157 | // 每行歌词内容 158 | TreeMap lyricsLineInfos = lyricsIfno 159 | .getLyricsLineInfoTreeMap(); 160 | // 将每行歌词,放到有序的map,判断已重复的歌词 161 | LinkedHashMap> lyricsLineInfoMapResult = new LinkedHashMap>(); 162 | for (int i = 0; i < lyricsLineInfos.size(); i++) { 163 | LyricsLineInfo lyricsLineInfo = lyricsLineInfos.get(i); 164 | String saveLineLyrics = getSaveLineLyrics(lyricsLineInfo 165 | .getLyricsWords()); 166 | List indexs = null; 167 | // 如果已存在该行歌词,则往里面添加歌词行索引 168 | if (lyricsLineInfoMapResult.containsKey(saveLineLyrics)) { 169 | indexs = lyricsLineInfoMapResult.get(saveLineLyrics); 170 | } else { 171 | indexs = new ArrayList(); 172 | } 173 | indexs.add(i); 174 | lyricsLineInfoMapResult.put(saveLineLyrics, indexs); 175 | } 176 | // 遍历 177 | for (Map.Entry> entry : lyricsLineInfoMapResult 178 | .entrySet()) { 179 | lyricsCom.append(LEGAL_LYRICS_LINE_PREFIX + "('"); 180 | List indexs = entry.getValue(); 181 | // 当前行歌词文本 182 | String saveLineLyrics = entry.getKey(); 183 | StringBuilder timeText = new StringBuilder();// 时间标签内容 184 | StringBuilder wordsDisIntervalText = new StringBuilder();// 每个歌词时间 185 | 186 | for (int i = 0; i < indexs.size(); i++) { 187 | int key = indexs.get(i); 188 | LyricsLineInfo lyricsLineInfo = lyricsLineInfos.get(key); 189 | // 获取开始时间和结束时间 190 | timeText.append("<" + lyricsLineInfo.getStartTime() + ","); 191 | timeText.append(lyricsLineInfo.getEndTime() + ">"); 192 | 193 | // 获取每个歌词的时间 194 | StringBuilder wordsDisIntervalTextTemp = new StringBuilder(); 195 | int wordsDisInterval[] = lyricsLineInfo.getWordsDisInterval(); 196 | for (int j = 0; j < wordsDisInterval.length; j++) { 197 | if (j == 0) 198 | wordsDisIntervalTextTemp.append(wordsDisInterval[j] + ""); 199 | else 200 | wordsDisIntervalTextTemp.append("," + wordsDisInterval[j] 201 | + ""); 202 | } 203 | // 获取每个歌词时间的文本 204 | wordsDisIntervalText.append("<" + wordsDisIntervalTextTemp.toString() + ">"); 205 | } 206 | lyricsCom.append(timeText.toString() + "'"); 207 | lyricsCom.append(",'" + saveLineLyrics + "'"); 208 | lyricsCom.append(",'" + wordsDisIntervalText.toString() + "');\n"); 209 | } 210 | return lyricsCom.toString(); 211 | } 212 | 213 | /** 214 | * 获取要保存的行歌词内容 215 | * 216 | * @param lyricsWords 217 | * @return 218 | */ 219 | private String getSaveLineLyrics(String[] lyricsWords) { 220 | StringBuilder saveLineLyrics = new StringBuilder(); 221 | for (int i = 0; i < lyricsWords.length; i++) { 222 | saveLineLyrics.append("<" + lyricsWords[i] + ">"); 223 | } 224 | return saveLineLyrics.toString(); 225 | } 226 | 227 | @Override 228 | public boolean isFileSupported(String ext) { 229 | return ext.equalsIgnoreCase("hrc"); 230 | } 231 | 232 | @Override 233 | public String getSupportFileExt() { 234 | return "hrc"; 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/krc/KrcLyricsFileReader.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats.krc; 2 | 3 | import android.util.Base64; 4 | 5 | import com.zlm.hp.lyrics.formats.LyricsFileReader; 6 | import com.zlm.hp.lyrics.model.LyricsInfo; 7 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 8 | import com.zlm.hp.lyrics.model.LyricsTag; 9 | import com.zlm.hp.lyrics.model.TranslateLrcLineInfo; 10 | import com.zlm.hp.lyrics.utils.StringCompressUtils; 11 | 12 | import org.json.JSONArray; 13 | import org.json.JSONObject; 14 | 15 | import java.io.InputStream; 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.TreeMap; 21 | import java.util.regex.Matcher; 22 | import java.util.regex.Pattern; 23 | 24 | /** 25 | * @Description: krcs歌词读取器 26 | * @Param: 27 | * @Return: 28 | * @Author: zhangliangming 29 | * @Date: 2017/12/25 16:29 30 | * @Throws: 31 | */ 32 | 33 | public class KrcLyricsFileReader extends LyricsFileReader { 34 | 35 | /** 36 | * 歌曲名 字符串 37 | */ 38 | private final static String LEGAL_SONGNAME_PREFIX = "[ti:"; 39 | /** 40 | * 歌手名 字符串 41 | */ 42 | private final static String LEGAL_SINGERNAME_PREFIX = "[ar:"; 43 | /** 44 | * 时间补偿值 字符串 45 | */ 46 | private final static String LEGAL_OFFSET_PREFIX = "[offset:"; 47 | /** 48 | * 歌词上传者 49 | */ 50 | private final static String LEGAL_BY_PREFIX = "[by:"; 51 | private final static String LEGAL_HASH_PREFIX = "[hash:"; 52 | /** 53 | * 专辑 54 | */ 55 | private final static String LEGAL_AL_PREFIX = "[al:"; 56 | private final static String LEGAL_SIGN_PREFIX = "[sign:"; 57 | private final static String LEGAL_QQ_PREFIX = "[qq:"; 58 | private final static String LEGAL_TOTAL_PREFIX = "[total:"; 59 | private final static String LEGAL_LANGUAGE_PREFIX = "[language:"; 60 | /** 61 | * 解码参数 62 | */ 63 | private static final char[] key = {'@', 'G', 'a', 'w', '^', '2', 't', 'G', 64 | 'Q', '6', '1', '-', 'Î', 'Ò', 'n', 'i'}; 65 | 66 | public KrcLyricsFileReader() { 67 | } 68 | 69 | @Override 70 | public LyricsInfo readLrcText(String dynamicContent, String lrcContent, String extraLrcContent, String lyricsFilePath) throws Exception { 71 | return null; 72 | } 73 | 74 | @Override 75 | public LyricsInfo readInputStream(InputStream in) throws Exception { 76 | LyricsInfo lyricsIfno = new LyricsInfo(); 77 | lyricsIfno.setLyricsFileExt(getSupportFileExt()); 78 | if (in != null) { 79 | byte[] zip_byte = new byte[in.available()]; 80 | byte[] top = new byte[4]; 81 | in.read(top); 82 | in.read(zip_byte); 83 | int j = zip_byte.length; 84 | for (int k = 0; k < j; k++) { 85 | int l = k % 16; 86 | int tmp67_65 = k; 87 | byte[] tmp67_64 = zip_byte; 88 | tmp67_64[tmp67_65] = (byte) (tmp67_64[tmp67_65] ^ key[l]); 89 | } 90 | String lyricsTextStr = StringCompressUtils.decompress(zip_byte, 91 | getDefaultCharset()); 92 | String[] lyricsTexts = lyricsTextStr.split("\n"); 93 | TreeMap lyricsLineInfos = new TreeMap(); 94 | Map lyricsTags = new HashMap(); 95 | int index = 0; 96 | 97 | for (int i = 0; i < lyricsTexts.length; i++) { 98 | String lineInfo = lyricsTexts[i]; 99 | 100 | // 行读取,并解析每行歌词的内容 101 | LyricsLineInfo lyricsLineInfo = parserLineInfos(lyricsTags, 102 | lineInfo, lyricsIfno); 103 | if (lyricsLineInfo != null) { 104 | lyricsLineInfos.put(index, lyricsLineInfo); 105 | index++; 106 | } 107 | } 108 | in.close(); 109 | in = null; 110 | // 设置歌词的标签类 111 | lyricsIfno.setLyricsTags(lyricsTags); 112 | // 113 | lyricsIfno.setLyricsLineInfoTreeMap(lyricsLineInfos); 114 | } 115 | return lyricsIfno; 116 | } 117 | 118 | /** 119 | * 解析歌词 120 | * 121 | * @param lyricsTags 122 | * @param lineInfo 123 | * @param lyricsIfno 124 | * @return 125 | */ 126 | private LyricsLineInfo parserLineInfos(Map lyricsTags, 127 | String lineInfo, LyricsInfo lyricsIfno) throws Exception { 128 | LyricsLineInfo lyricsLineInfo = null; 129 | if (lineInfo.startsWith(LEGAL_SONGNAME_PREFIX)) { 130 | int startIndex = LEGAL_SONGNAME_PREFIX.length(); 131 | int endIndex = lineInfo.lastIndexOf("]"); 132 | // 133 | lyricsTags.put(LyricsTag.TAG_TITLE, 134 | lineInfo.substring(startIndex, endIndex)); 135 | } else if (lineInfo.startsWith(LEGAL_SINGERNAME_PREFIX)) { 136 | int startIndex = LEGAL_SINGERNAME_PREFIX.length(); 137 | int endIndex = lineInfo.lastIndexOf("]"); 138 | lyricsTags.put(LyricsTag.TAG_ARTIST, 139 | lineInfo.substring(startIndex, endIndex)); 140 | } else if (lineInfo.startsWith(LEGAL_OFFSET_PREFIX)) { 141 | int startIndex = LEGAL_OFFSET_PREFIX.length(); 142 | int endIndex = lineInfo.lastIndexOf("]"); 143 | lyricsTags.put(LyricsTag.TAG_OFFSET, 144 | lineInfo.substring(startIndex, endIndex)); 145 | } else if (lineInfo.startsWith(LEGAL_BY_PREFIX) 146 | || lineInfo.startsWith(LEGAL_HASH_PREFIX) 147 | || lineInfo.startsWith(LEGAL_SIGN_PREFIX) 148 | || lineInfo.startsWith(LEGAL_QQ_PREFIX) 149 | || lineInfo.startsWith(LEGAL_TOTAL_PREFIX) 150 | || lineInfo.startsWith(LEGAL_AL_PREFIX)) { 151 | 152 | int startIndex = lineInfo.indexOf("[") + 1; 153 | int endIndex = lineInfo.lastIndexOf("]"); 154 | String temp[] = lineInfo.substring(startIndex, endIndex).split(":"); 155 | lyricsTags.put(temp[0], temp.length == 1 ? "" : temp[1]); 156 | 157 | } else if (lineInfo.startsWith(LEGAL_LANGUAGE_PREFIX)) { 158 | int startIndex = lineInfo.indexOf("[") + 1; 159 | int endIndex = lineInfo.lastIndexOf("]"); 160 | String temp[] = lineInfo.substring(startIndex, endIndex).split(":"); 161 | // 解析翻译歌词 162 | // 获取json base64字符串 163 | String translateJsonBase64String = temp.length == 1 ? "" : temp[1]; 164 | if (!translateJsonBase64String.equals("")) { 165 | 166 | String translateJsonString = new String( 167 | Base64.decode(translateJsonBase64String, Base64.NO_WRAP)); 168 | parserOtherLrc(lyricsIfno, translateJsonString); 169 | } 170 | } else { 171 | // 匹配歌词行 172 | Pattern pattern = Pattern.compile("\\[\\d+,\\d+\\]"); 173 | Matcher matcher = pattern.matcher(lineInfo); 174 | if (matcher.find()) { 175 | lyricsLineInfo = new LyricsLineInfo(); 176 | // [此行开始时刻距0时刻的毫秒数,此行持续的毫秒数]<0,此字持续的毫秒数,0>歌<此字开始的时刻距此行开始时刻的毫秒数,此字持续的毫秒数,0>词<此字开始的时刻距此行开始时刻的毫秒数,此字持续的毫秒数,0>正<此字开始的时刻距此行开始时刻的毫秒数,此字持续的毫秒数,0>文 177 | // 获取行的出现时间和结束时间 178 | int mStartIndex = matcher.start(); 179 | int mEndIndex = matcher.end(); 180 | String lineTime[] = lineInfo.substring(mStartIndex + 1, 181 | mEndIndex - 1).split(","); 182 | // 183 | 184 | int startTime = Integer.parseInt(lineTime[0]); 185 | int endTime = startTime + Integer.parseInt(lineTime[1]); 186 | lyricsLineInfo.setEndTime(endTime); 187 | lyricsLineInfo.setStartTime(startTime); 188 | // 获取歌词信息 189 | String lineContent = lineInfo.substring(mEndIndex, 190 | lineInfo.length()); 191 | 192 | // 歌词匹配的正则表达式 193 | String regex = "\\<\\d+,\\d+,\\d+\\>"; 194 | Pattern lyricsWordsPattern = Pattern.compile(regex); 195 | Matcher lyricsWordsMatcher = lyricsWordsPattern 196 | .matcher(lineContent); 197 | 198 | if (lyricsWordsMatcher == null) { 199 | return null; 200 | } 201 | 202 | // 歌词分隔 203 | String lineLyricsTemp[] = lineContent.split(regex); 204 | String[] lyricsWords = getLyricsWords(lineLyricsTemp); 205 | lyricsLineInfo.setLyricsWords(lyricsWords); 206 | 207 | // 获取每个歌词的时间 208 | int wordsDisInterval[] = new int[lyricsWords.length]; 209 | int index = 0; 210 | while (lyricsWordsMatcher.find()) { 211 | 212 | //验证 213 | if (index >= wordsDisInterval.length) { 214 | throw new Exception("字标签个数与字时间标签个数不相符"); 215 | } 216 | 217 | // 218 | String wordsDisIntervalStr = lyricsWordsMatcher.group(); 219 | String wordsDisIntervalStrTemp = wordsDisIntervalStr 220 | .substring(wordsDisIntervalStr.indexOf('<') + 1, wordsDisIntervalStr.lastIndexOf('>')); 221 | String wordsDisIntervalTemp[] = wordsDisIntervalStrTemp 222 | .split(","); 223 | wordsDisInterval[index++] = Integer 224 | .parseInt(wordsDisIntervalTemp[1]); 225 | } 226 | lyricsLineInfo.setWordsDisInterval(wordsDisInterval); 227 | 228 | // 获取当行歌词 229 | String lineLyrics = lyricsWordsMatcher.replaceAll(""); 230 | lyricsLineInfo.setLineLyrics(lineLyrics); 231 | } 232 | 233 | } 234 | return lyricsLineInfo; 235 | } 236 | 237 | /** 238 | * 解析翻译和音译歌词 239 | * 240 | * @param lyricsIfno 241 | * @param translateJsonString 242 | */ 243 | private void parserOtherLrc(LyricsInfo lyricsIfno, 244 | String translateJsonString) throws Exception { 245 | 246 | JSONObject resultObj = new JSONObject(translateJsonString); 247 | JSONArray contentArrayObj = resultObj.getJSONArray("content"); 248 | for (int i = 0; i < contentArrayObj.length(); i++) { 249 | JSONObject dataObj = contentArrayObj.getJSONObject(i); 250 | JSONArray lyricContentArrayObj = dataObj 251 | .getJSONArray("lyricContent"); 252 | int type = dataObj.getInt("type"); 253 | if (type == 1) { 254 | // 解析翻译歌词 255 | if (lyricsIfno.getTranslateLrcLineInfos() == null || lyricsIfno.getTranslateLrcLineInfos().size() == 0) 256 | parserTranslateLrc(lyricsIfno, lyricContentArrayObj); 257 | 258 | } else if (type == 0) { 259 | // 解析音译歌词 260 | if (lyricsIfno.getTransliterationLrcLineInfos() == null || lyricsIfno.getTransliterationLrcLineInfos().size() == 0) 261 | parserTransliterationLrc(lyricsIfno, 262 | lyricContentArrayObj); 263 | } 264 | } 265 | } 266 | 267 | /** 268 | * 解析音译歌词 269 | * 270 | * @param lyricsIfno 271 | * @param lyricContentArrayObj 272 | */ 273 | private void parserTransliterationLrc(LyricsInfo lyricsIfno, 274 | JSONArray lyricContentArrayObj) throws Exception { 275 | 276 | // 音译歌词集合 277 | List transliterationLrcLineInfos = new ArrayList(); 278 | // 获取歌词内容 279 | for (int j = 0; j < lyricContentArrayObj.length(); j++) { 280 | JSONArray lrcDataArrayObj = lyricContentArrayObj.getJSONArray(j); 281 | // 音译行歌词 282 | LyricsLineInfo transliterationLrcLineInfo = new LyricsLineInfo(); 283 | String[] lyricsWords = new String[lrcDataArrayObj.length()]; 284 | StringBuilder lineLyrics = new StringBuilder(); 285 | for (int k = 0; k < lrcDataArrayObj.length(); k++) { 286 | if (k == lrcDataArrayObj.length() - 1) { 287 | lyricsWords[k] = lrcDataArrayObj.getString(k).trim(); 288 | } else { 289 | lyricsWords[k] = lrcDataArrayObj.getString(k).trim() + " "; 290 | } 291 | lineLyrics.append(lyricsWords[k]); 292 | } 293 | transliterationLrcLineInfo.setLineLyrics(lineLyrics.toString()); 294 | transliterationLrcLineInfo.setLyricsWords(lyricsWords); 295 | 296 | transliterationLrcLineInfos.add(transliterationLrcLineInfo); 297 | } 298 | // 添加音译歌词 299 | if (transliterationLrcLineInfos.size() > 0) { 300 | lyricsIfno 301 | .setTransliterationLrcLineInfos(transliterationLrcLineInfos); 302 | } 303 | } 304 | 305 | /** 306 | * 解析翻译歌词 307 | * 308 | * @param lyricsIfno 309 | * @param lyricContentArrayObj 310 | */ 311 | private void parserTranslateLrc(LyricsInfo lyricsIfno, 312 | JSONArray lyricContentArrayObj) throws Exception { 313 | 314 | // 翻译歌词集合 315 | List translateLrcLineInfos = new ArrayList(); 316 | 317 | // 获取歌词内容 318 | for (int j = 0; j < lyricContentArrayObj.length(); j++) { 319 | JSONArray lrcDataArrayObj = lyricContentArrayObj.getJSONArray(j); 320 | String lrcComtext = lrcDataArrayObj.getString(0); 321 | 322 | // 翻译行歌词 323 | TranslateLrcLineInfo translateLrcLineInfo = new TranslateLrcLineInfo(); 324 | translateLrcLineInfo.setLineLyrics(lrcComtext); 325 | 326 | translateLrcLineInfos.add(translateLrcLineInfo); 327 | } 328 | // 添加翻译歌词 329 | if (translateLrcLineInfos.size() > 0) { 330 | lyricsIfno.setTranslateLrcLineInfos(translateLrcLineInfos); 331 | } 332 | } 333 | 334 | /** 335 | * 分隔每个歌词 336 | * 337 | * @param lineLyricsTemp 338 | * @return 339 | */ 340 | private String[] getLyricsWords(String[] lineLyricsTemp) throws Exception { 341 | String temp[] = null; 342 | if (lineLyricsTemp.length < 2) { 343 | return new String[lineLyricsTemp.length]; 344 | } 345 | // 346 | temp = new String[lineLyricsTemp.length - 1]; 347 | for (int i = 1; i < lineLyricsTemp.length; i++) { 348 | temp[i - 1] = lineLyricsTemp[i]; 349 | } 350 | return temp; 351 | } 352 | 353 | @Override 354 | public boolean isFileSupported(String ext) { 355 | return ext.equalsIgnoreCase("krc"); 356 | } 357 | 358 | @Override 359 | public String getSupportFileExt() { 360 | return "krc"; 361 | } 362 | 363 | } 364 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/krc/KrcLyricsFileWriter.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats.krc; 2 | 3 | import android.util.Base64; 4 | 5 | import com.zlm.hp.lyrics.formats.LyricsFileWriter; 6 | import com.zlm.hp.lyrics.model.LyricsInfo; 7 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 8 | import com.zlm.hp.lyrics.model.LyricsTag; 9 | import com.zlm.hp.lyrics.model.TranslateLrcLineInfo; 10 | import com.zlm.hp.lyrics.utils.StringCompressUtils; 11 | 12 | import org.json.JSONArray; 13 | import org.json.JSONObject; 14 | 15 | import java.io.File; 16 | import java.io.FileOutputStream; 17 | import java.util.List; 18 | import java.util.Map; 19 | import java.util.TreeMap; 20 | 21 | /** 22 | * @Description: krc歌词生成器, 不是生成官方的歌词文件 23 | * @Param: 24 | * @Return: 25 | * @Author: zhangliangming 26 | * @Date: 2017/12/25 17:00 27 | * @Throws: 28 | */ 29 | 30 | public class KrcLyricsFileWriter extends LyricsFileWriter { 31 | /** 32 | * 歌曲名 字符串 33 | */ 34 | private final static String LEGAL_SONGNAME_PREFIX = "[ti:"; 35 | /** 36 | * 歌手名 字符串 37 | */ 38 | private final static String LEGAL_SINGERNAME_PREFIX = "[ar:"; 39 | /** 40 | * 时间补偿值 字符串 41 | */ 42 | private final static String LEGAL_OFFSET_PREFIX = "[offset:"; 43 | /** 44 | * 额外歌词字符串 45 | */ 46 | private final static String LEGAL_LANGUAGE_PREFIX = "[language:"; 47 | 48 | private final static String LEGAL_TOTAL_PREFIX = "[total:"; 49 | 50 | /** 51 | * 解码参数 52 | */ 53 | private static final char[] key = {'@', 'G', 'a', 'w', '^', '2', 't', 'G', 54 | 'Q', '6', '1', '-', 'Î', 'Ò', 'n', 'i'}; 55 | 56 | public KrcLyricsFileWriter() { 57 | } 58 | 59 | @Override 60 | public boolean writer(LyricsInfo lyricsIfno, String lyricsFilePath) throws Exception { 61 | File lyricsFile = new File(lyricsFilePath); 62 | if (lyricsFile != null) { 63 | // 64 | if (!lyricsFile.getParentFile().exists()) { 65 | lyricsFile.getParentFile().mkdirs(); 66 | } 67 | 68 | // 69 | // 对字符串运行压缩 70 | byte[] content = StringCompressUtils.compress( 71 | getLyricsContent(lyricsIfno), getDefaultCharset()); 72 | 73 | int j = content.length; 74 | for (int k = 0; k < j; k++) { 75 | int l = k % 16; 76 | int tmp67_65 = k; 77 | byte[] tmp67_64 = content; 78 | tmp67_64[tmp67_65] = (byte) (tmp67_64[tmp67_65] ^ key[l]); 79 | } 80 | String topText = "krc1"; 81 | byte[] top = new byte[4]; 82 | for (int i = 0; i < topText.length(); i++) { 83 | top[i] = (byte) topText.charAt(i); 84 | } 85 | 86 | // 生成歌词文件 87 | FileOutputStream os = new FileOutputStream(lyricsFile); 88 | os.write(top); 89 | os.write(content); 90 | os.close(); 91 | os = null; 92 | 93 | return true; 94 | } 95 | return false; 96 | } 97 | 98 | @Override 99 | public String getLyricsContent(LyricsInfo lyricsIfno) throws Exception { 100 | StringBuilder lyricsCom = new StringBuilder(); 101 | // 先保存所有的标签数据 102 | Map tags = lyricsIfno.getLyricsTags(); 103 | for (Map.Entry entry : tags.entrySet()) { 104 | Object val = entry.getValue(); 105 | if (entry.getKey().equals(LyricsTag.TAG_TITLE)) { 106 | lyricsCom.append(LEGAL_SONGNAME_PREFIX); 107 | } else if (entry.getKey().equals(LyricsTag.TAG_ARTIST)) { 108 | lyricsCom.append(LEGAL_SINGERNAME_PREFIX); 109 | } else if (entry.getKey().equals(LyricsTag.TAG_OFFSET)) { 110 | lyricsCom.append(LEGAL_OFFSET_PREFIX); 111 | } else if (entry.getKey().equals(LyricsTag.TAG_TOTAL)) { 112 | lyricsCom.append(LEGAL_TOTAL_PREFIX); 113 | }else { 114 | val = "[" + entry.getKey() + ":" + val; 115 | } 116 | lyricsCom.append(val + "]\n"); 117 | } 118 | 119 | JSONObject extraLyricsObj = new JSONObject(); 120 | JSONArray contentArray = new JSONArray(); 121 | // 判断是否有翻译歌词 122 | if (lyricsIfno.getTranslateLrcLineInfos() != null && lyricsIfno.getTranslateLrcLineInfos().size() != 0) { 123 | List translateLrcLineInfos = lyricsIfno.getTranslateLrcLineInfos(); 124 | if (translateLrcLineInfos != null 125 | && translateLrcLineInfos.size() > 0) { 126 | JSONObject lyricsObj = new JSONObject(); 127 | JSONArray lyricContentArray = new JSONArray(); 128 | lyricsObj.put("language", 0); 129 | lyricsObj.put("type", 1); 130 | for (int i = 0; i < translateLrcLineInfos.size(); i++) { 131 | JSONArray lyricArray = new JSONArray(); 132 | TranslateLrcLineInfo translateLrcLineInfo = translateLrcLineInfos 133 | .get(i); 134 | lyricArray.put(translateLrcLineInfo.getLineLyrics()); 135 | lyricContentArray.put(lyricArray); 136 | } 137 | if (lyricContentArray.length() > 0) { 138 | lyricsObj.put("lyricContent", lyricContentArray); 139 | contentArray.put(lyricsObj); 140 | } 141 | 142 | } 143 | } 144 | // 判断是否有音译歌词 145 | if (lyricsIfno.getTransliterationLrcLineInfos() != null && lyricsIfno.getTransliterationLrcLineInfos().size() != 0) { 146 | List lyricsLineInfos = lyricsIfno.getTransliterationLrcLineInfos(); 147 | if (lyricsLineInfos != null && lyricsLineInfos.size() > 0) { 148 | JSONObject lyricsObj = new JSONObject(); 149 | JSONArray lyricContentArray = new JSONArray(); 150 | lyricsObj.put("language", 0); 151 | lyricsObj.put("type", 0); 152 | for (int i = 0; i < lyricsLineInfos.size(); i++) { 153 | 154 | LyricsLineInfo lyricsLineInfo = lyricsLineInfos.get(i); 155 | String[] lyricsWords = lyricsLineInfo.getLyricsWords(); 156 | JSONArray lyricArray = new JSONArray(); 157 | for (int j = 0; j < lyricsWords.length; j++) { 158 | lyricArray.put(lyricsWords[j].trim()); 159 | } 160 | lyricContentArray.put(lyricArray); 161 | } 162 | if (lyricContentArray.length() > 0) { 163 | lyricsObj.put("lyricContent", lyricContentArray); 164 | contentArray.put(lyricsObj); 165 | } 166 | } 167 | } 168 | // 169 | extraLyricsObj.put("content", contentArray); 170 | // 添加翻译和音译歌词 171 | lyricsCom.append(LEGAL_LANGUAGE_PREFIX 172 | + Base64.encodeToString(extraLyricsObj.toString() 173 | .getBytes(), Base64.NO_WRAP) + "]\n"); 174 | 175 | // [1679,1550]<0,399,0>作<399,200,0>词<599,250,0>:<849,301,0>李<1150,400,0>健 176 | TreeMap lyricsLineInfos = lyricsIfno 177 | .getLyricsLineInfoTreeMap(); 178 | // 每行歌词内容 179 | for (int i = 0; i < lyricsLineInfos.size(); i++) { 180 | LyricsLineInfo lyricsLineInfo = lyricsLineInfos.get(i); 181 | // 182 | int startTime = lyricsLineInfo.getStartTime(); 183 | int endTime = lyricsLineInfo.getEndTime(); 184 | lyricsCom.append("[" + startTime + "," + (endTime - startTime) + "]"); 185 | // 186 | String[] lyricsWords = lyricsLineInfo.getLyricsWords(); 187 | int wordsDisInterval[] = lyricsLineInfo.getWordsDisInterval(); 188 | int lastTime = 0; 189 | for (int j = 0; j < wordsDisInterval.length; j++) { 190 | if (j == 0) { 191 | lyricsCom.append("<0," + wordsDisInterval[j] + ",0>" 192 | + lyricsWords[j]); 193 | } else { 194 | lyricsCom.append("<" + lastTime + "," + wordsDisInterval[j] 195 | + ",0>" + lyricsWords[j]); 196 | } 197 | lastTime = wordsDisInterval[j]; 198 | } 199 | lyricsCom.append("\n"); 200 | } 201 | 202 | return lyricsCom.toString(); 203 | } 204 | 205 | @Override 206 | public boolean isFileSupported(String ext) { 207 | return ext.equalsIgnoreCase("krc"); 208 | } 209 | 210 | @Override 211 | public String getSupportFileExt() { 212 | return "krc"; 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/ksc/KscLyricsFileReader.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats.ksc; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.zlm.hp.lyrics.formats.LyricsFileReader; 6 | import com.zlm.hp.lyrics.model.LyricsInfo; 7 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 8 | import com.zlm.hp.lyrics.model.LyricsTag; 9 | import com.zlm.hp.lyrics.utils.CharUtils; 10 | import com.zlm.hp.lyrics.utils.StringUtils; 11 | import com.zlm.hp.lyrics.utils.TimeUtils; 12 | import com.zlm.hp.lyrics.utils.UnicodeInputStream; 13 | 14 | import java.io.BufferedReader; 15 | import java.io.File; 16 | import java.io.FileInputStream; 17 | import java.io.InputStream; 18 | import java.io.InputStreamReader; 19 | import java.nio.charset.Charset; 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.TreeMap; 25 | 26 | /** 27 | * @Description: ksc歌词解析器 28 | * @Param: 29 | * @Return: 30 | * @Author: zhangliangming 31 | * @Date: 2017/12/25 16:26 32 | * @Throws: 33 | */ 34 | public class KscLyricsFileReader extends LyricsFileReader { 35 | /** 36 | * 歌曲名 字符串 37 | */ 38 | private final static String LEGAL_SONGNAME_PREFIX = "karaoke.songname"; 39 | /** 40 | * 歌手名 字符串 41 | */ 42 | private final static String LEGAL_SINGERNAME_PREFIX = "karaoke.singer"; 43 | /** 44 | * 时间补偿值 字符串 45 | */ 46 | private final static String LEGAL_OFFSET_PREFIX = "karaoke.offset"; 47 | /** 48 | * 歌词 字符串 49 | */ 50 | public final static String LEGAL_LYRICS_LINE_PREFIX = "karaoke.add"; 51 | 52 | /** 53 | * 歌词Tag 54 | */ 55 | public final static String LEGAL_TAG_PREFIX = "karaoke.tag"; 56 | 57 | /** 58 | * 读取歌词文件 59 | * 60 | * @param file 61 | * @return 62 | */ 63 | @Override 64 | public LyricsInfo readFile(File file) throws Exception { 65 | if (file != null) { 66 | String charsetName = getCharsetName(file); 67 | setDefaultCharset(Charset.forName(charsetName)); 68 | InputStream inputStream = new FileInputStream(file); 69 | if (charsetName.toLowerCase().equals("utf-8")) { 70 | inputStream = new UnicodeInputStream(inputStream, charsetName); 71 | } 72 | return readInputStream(inputStream); 73 | } 74 | return null; 75 | } 76 | 77 | @Override 78 | public LyricsInfo readInputStream(InputStream in) throws Exception { 79 | LyricsInfo lyricsIfno = new LyricsInfo(); 80 | lyricsIfno.setLyricsFileExt(getSupportFileExt()); 81 | if (in != null) { 82 | BufferedReader br = new BufferedReader(new InputStreamReader(in, 83 | getDefaultCharset())); 84 | 85 | TreeMap lyricsLineInfos = new TreeMap(); 86 | Map lyricsTags = new HashMap(); 87 | int index = 0; 88 | String lineInfo = ""; 89 | while ((lineInfo = br.readLine()) != null) { 90 | 91 | // 行读取,并解析每行歌词的内容 92 | LyricsLineInfo lyricsLineInfo = parserLineInfos(lyricsTags, 93 | lineInfo); 94 | if (lyricsLineInfo != null) { 95 | lyricsLineInfos.put(index, lyricsLineInfo); 96 | index++; 97 | } 98 | } 99 | in.close(); 100 | in = null; 101 | // 设置歌词的标签类 102 | lyricsIfno.setLyricsTags(lyricsTags); 103 | // 104 | lyricsIfno.setLyricsLineInfoTreeMap(lyricsLineInfos); 105 | } 106 | return lyricsIfno; 107 | } 108 | 109 | @Override 110 | public LyricsInfo readLrcText(String dynamicContent, String lrcContent, String extraLrcContent, String lyricsFilePath) throws Exception { 111 | LyricsInfo lyricsIfno = new LyricsInfo(); 112 | lyricsIfno.setLyricsFileExt(getSupportFileExt()); 113 | if (!TextUtils.isEmpty(lrcContent)) { 114 | 115 | TreeMap lyricsLineInfos = new TreeMap(); 116 | Map lyricsTags = new HashMap(); 117 | int index = 0; 118 | 119 | // 获取歌词内容 120 | String lrcContents[] = lrcContent.split("\n"); 121 | for (int i = 0; i < lrcContents.length; i++) { 122 | String lineInfo = lrcContents[i]; 123 | 124 | // 行读取,并解析每行歌词的内容 125 | LyricsLineInfo lyricsLineInfo = parserLineInfos(lyricsTags, 126 | lineInfo); 127 | if (lyricsLineInfo != null) { 128 | lyricsLineInfos.put(index, lyricsLineInfo); 129 | index++; 130 | } 131 | } 132 | 133 | // 设置歌词的标签类 134 | lyricsIfno.setLyricsTags(lyricsTags); 135 | lyricsIfno.setLyricsLineInfoTreeMap(lyricsLineInfos); 136 | } 137 | return lyricsIfno; 138 | } 139 | 140 | /** 141 | * 解析每行的歌词内容 142 | *

143 | * 歌词列表 144 | * 145 | * @param lyricsTags 歌词标签 146 | * @param lineInfo 行歌词内容 147 | * @return 148 | */ 149 | private LyricsLineInfo parserLineInfos(Map lyricsTags, 150 | String lineInfo) throws Exception { 151 | LyricsLineInfo lyricsLineInfo = null; 152 | if (lineInfo.startsWith(LEGAL_SONGNAME_PREFIX)) { 153 | String temp[] = lineInfo.split("\'"); 154 | if (temp.length > 1) { 155 | lyricsTags.put(LyricsTag.TAG_TITLE, temp[1]); 156 | } 157 | } else if (lineInfo.startsWith(LEGAL_SINGERNAME_PREFIX)) { 158 | String temp[] = lineInfo.split("\'"); 159 | if (temp.length > 1) { 160 | lyricsTags.put(LyricsTag.TAG_ARTIST, temp[1]); 161 | } 162 | } else if (lineInfo.startsWith(LEGAL_OFFSET_PREFIX)) { 163 | String temp[] = lineInfo.split("\'"); 164 | if (temp.length > 1) { 165 | lyricsTags.put(LyricsTag.TAG_OFFSET, temp[1]); 166 | } 167 | } else if (lineInfo.startsWith(LEGAL_TAG_PREFIX)) { 168 | // 自定义标签 169 | if (lineInfo.contains(":")) { 170 | String temp1[] = lineInfo.split("\'"); 171 | if (temp1.length > 1) { 172 | String temp2[] = temp1[1].split(":"); 173 | if (temp2.length > 1) { 174 | lyricsTags.put(temp2[0], temp2[1]); 175 | } 176 | } 177 | } 178 | } else if (lineInfo.startsWith(LEGAL_LYRICS_LINE_PREFIX)) { 179 | lyricsLineInfo = new LyricsLineInfo(); 180 | 181 | int leftIndex = lineInfo.indexOf('\''); 182 | int rightIndex = lineInfo.lastIndexOf('\''); 183 | 184 | String[] lineComments = lineInfo.substring(leftIndex + 1, rightIndex) 185 | .split("'\\s*,\\s*'", -1); 186 | // 开始时间 187 | String startTimeStr = lineComments[0]; 188 | int startTime = TimeUtils.parseInteger(startTimeStr); 189 | lyricsLineInfo.setStartTime(startTime); 190 | 191 | // 结束时间 192 | String endTimeStr = lineComments[1]; 193 | int endTime = TimeUtils.parseInteger(endTimeStr); 194 | lyricsLineInfo.setEndTime(endTime); 195 | 196 | // 歌词 197 | String lineLyricsStr = lineComments[2]; 198 | List lineLyricsList = getLyricsWords(lineLyricsStr); 199 | 200 | // 歌词分隔 201 | String[] lyricsWords = lineLyricsList 202 | .toArray(new String[lineLyricsList.size()]); 203 | lyricsLineInfo.setLyricsWords(lyricsWords); 204 | 205 | // 获取当行歌词 206 | String lineLyrics = getLineLyrics(lineLyricsStr); 207 | lyricsLineInfo.setLineLyrics(lineLyrics); 208 | 209 | // 获取每个歌词的时间 210 | int wordsDisInterval[] = getWordsDisIntervalString(lineComments[3]); 211 | lyricsLineInfo.setWordsDisInterval(wordsDisInterval); 212 | 213 | //验证 214 | if (lyricsWords.length != wordsDisInterval.length) { 215 | throw new Exception("字标签个数与字时间标签个数不相符"); 216 | } 217 | } 218 | return lyricsLineInfo; 219 | } 220 | 221 | /** 222 | * 获取每个歌词的时间 223 | * 224 | * @param wordsDisIntervalString 225 | * @return 226 | */ 227 | private int[] getWordsDisIntervalString(String wordsDisIntervalString) throws Exception { 228 | String[] wordsDisIntervalStr = wordsDisIntervalString.split(","); 229 | int wordsDisInterval[] = new int[wordsDisIntervalStr.length]; 230 | for (int i = 0; i < wordsDisIntervalStr.length; i++) { 231 | String wordDisIntervalStr = wordsDisIntervalStr[i]; 232 | if (StringUtils.isNumeric(wordDisIntervalStr)) 233 | wordsDisInterval[i] = Integer.parseInt(wordDisIntervalStr); 234 | else throw new Exception("字时间标签不能含有非数字字符串"); 235 | } 236 | return wordsDisInterval; 237 | } 238 | 239 | /** 240 | * 获取当前行歌词,去掉中括号 241 | * 242 | * @param lineLyricsStr 243 | * @return 244 | */ 245 | private String getLineLyrics(String lineLyricsStr) throws Exception { 246 | StringBuilder temp = new StringBuilder(); 247 | for (int i = 0; i < lineLyricsStr.length(); i++) { 248 | char c = lineLyricsStr.charAt(i); 249 | switch (c) { 250 | case '[': 251 | break; 252 | case ']': 253 | break; 254 | default: 255 | temp.append(c); 256 | break; 257 | } 258 | } 259 | return temp.toString(); 260 | } 261 | 262 | /** 263 | * 分隔每个歌词 264 | * 265 | * @param lineLyricsStr 266 | * @return 267 | */ 268 | private List getLyricsWords(String lineLyricsStr) throws Exception { 269 | List lineLyricsList = new ArrayList(); 270 | StringBuilder temp = new StringBuilder(); 271 | boolean isEnterFlag = false; 272 | for (int i = 0; i < lineLyricsStr.length(); i++) { 273 | char c = lineLyricsStr.charAt(i); 274 | if (CharUtils.isChinese(c) || CharUtils.isHangulSyllables(c) 275 | || CharUtils.isHiragana(c) 276 | || (!CharUtils.isWord(c) && c != '[' && c != ']')) { 277 | if (isEnterFlag) { 278 | temp.append(lineLyricsStr.charAt(i)); 279 | } else { 280 | lineLyricsList.add(String.valueOf(lineLyricsStr.charAt(i))); 281 | } 282 | } else if (c == '[') { 283 | isEnterFlag = true; 284 | } else if (c == ']') { 285 | isEnterFlag = false; 286 | lineLyricsList.add(temp.toString()); 287 | 288 | //清空 289 | temp.delete(0, temp.length()); 290 | } else { 291 | temp.append(lineLyricsStr.charAt(i)); 292 | } 293 | } 294 | return lineLyricsList; 295 | } 296 | 297 | @Override 298 | public boolean isFileSupported(String ext) { 299 | return ext.equalsIgnoreCase("ksc"); 300 | } 301 | 302 | @Override 303 | public String getSupportFileExt() { 304 | return "ksc"; 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/ksc/KscLyricsFileWriter.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats.ksc; 2 | 3 | import com.zlm.hp.lyrics.formats.LyricsFileWriter; 4 | import com.zlm.hp.lyrics.model.LyricsInfo; 5 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 6 | import com.zlm.hp.lyrics.model.LyricsTag; 7 | import com.zlm.hp.lyrics.utils.TimeUtils; 8 | 9 | import java.nio.charset.Charset; 10 | import java.util.Map; 11 | import java.util.TreeMap; 12 | 13 | /** 14 | * @Description: ksc歌词保存器 15 | * @Param: 16 | * @Return: 17 | * @Author: zhangliangming 18 | * @Date: 2017/12/25 16:45 19 | * @Throws: 20 | */ 21 | public class KscLyricsFileWriter extends LyricsFileWriter { 22 | 23 | /** 24 | * 歌曲名 字符串 25 | */ 26 | private final static String LEGAL_SONGNAME_PREFIX = "karaoke.songname"; 27 | /** 28 | * 歌手名 字符串 29 | */ 30 | private final static String LEGAL_SINGERNAME_PREFIX = "karaoke.singer"; 31 | /** 32 | * 时间补偿值 字符串 33 | */ 34 | private final static String LEGAL_OFFSET_PREFIX = "karaoke.offset"; 35 | /** 36 | * 歌词 字符串 37 | */ 38 | public final static String LEGAL_LYRICS_LINE_PREFIX = "karaoke.add"; 39 | 40 | /** 41 | * 歌词Tag 42 | */ 43 | public final static String LEGAL_TAG_PREFIX = "karaoke.tag"; 44 | 45 | public KscLyricsFileWriter() { 46 | // 设置编码 47 | setDefaultCharset(Charset.forName("GB2312")); 48 | } 49 | 50 | @Override 51 | public boolean isFileSupported(String ext) { 52 | return ext.equalsIgnoreCase("ksc"); 53 | } 54 | 55 | @Override 56 | public String getSupportFileExt() { 57 | return "ksc"; 58 | } 59 | 60 | @Override 61 | public boolean writer(LyricsInfo lyricsIfno, String lyricsFilePath) throws Exception { 62 | String lyricsContent = getLyricsContent(lyricsIfno); 63 | return saveLyricsFile(lyricsContent, lyricsFilePath); 64 | } 65 | 66 | @Override 67 | public String getLyricsContent(LyricsInfo lyricsIfno) throws Exception { 68 | StringBuilder lyricsCom = new StringBuilder(); 69 | // 先保存所有的标签数据 70 | Map tags = lyricsIfno.getLyricsTags(); 71 | for (Map.Entry entry : tags.entrySet()) { 72 | Object val = entry.getValue(); 73 | if (entry.getKey().equals(LyricsTag.TAG_TITLE)) { 74 | lyricsCom.append(LEGAL_SONGNAME_PREFIX); 75 | } else if (entry.getKey().equals(LyricsTag.TAG_ARTIST)) { 76 | lyricsCom.append(LEGAL_SINGERNAME_PREFIX); 77 | } else if (entry.getKey().equals(LyricsTag.TAG_OFFSET)) { 78 | lyricsCom.append(LEGAL_OFFSET_PREFIX); 79 | } else { 80 | lyricsCom.append(LEGAL_TAG_PREFIX); 81 | val = entry.getKey() + ":" + val; 82 | } 83 | lyricsCom.append(" := '" + val + "';\n"); 84 | } 85 | // 每行歌词内容 86 | TreeMap lyricsLineInfos = lyricsIfno 87 | .getLyricsLineInfoTreeMap(); 88 | for (int i = 0; i < lyricsLineInfos.size(); i++) { 89 | LyricsLineInfo lyricsLineInfo = lyricsLineInfos.get(i); 90 | 91 | lyricsCom.append(LEGAL_LYRICS_LINE_PREFIX + "('" 92 | + TimeUtils.parseMMSSFFFString(lyricsLineInfo.getStartTime()) 93 | + "',");// 添加开始时间 94 | lyricsCom.append("'" 95 | + TimeUtils.parseMMSSFFFString(lyricsLineInfo.getEndTime()) + "',");// 添加结束时间 96 | 97 | // 获取歌词文本行 98 | String lyricsText = getLineLyrics(lyricsLineInfo.getLyricsWords()); 99 | lyricsCom.append("'" + lyricsText + "',");// 解析文本歌词 100 | 101 | // 添加每个歌词的时间 102 | StringBuilder wordsDisIntervalText = new StringBuilder(); 103 | int wordsDisInterval[] = lyricsLineInfo.getWordsDisInterval(); 104 | for (int j = 0; j < wordsDisInterval.length; j++) { 105 | if (j == 0) 106 | wordsDisIntervalText.append(wordsDisInterval[j] + ""); 107 | else 108 | wordsDisIntervalText.append("," + wordsDisInterval[j] + ""); 109 | } 110 | lyricsCom.append("'" + wordsDisIntervalText.toString() + "');\n"); 111 | } 112 | return lyricsCom.toString(); 113 | } 114 | 115 | /** 116 | * 获取当行歌词(每个字添加[]是因为存在部分krc歌词转换成ksc歌词时,一个字时间标签对应几个歌词,这样子在ksc在解析时,会导致字时间标签与字标签的个数不对应,出错的问题,这是ksc歌词格式存在的问题) 117 | * 118 | * @param lyricsWords 119 | * @return 120 | */ 121 | private String getLineLyrics(String[] lyricsWords) throws Exception { 122 | StringBuilder lrcText = new StringBuilder(); 123 | for (int i = 0; i < lyricsWords.length; i++) { 124 | lrcText.append("[" + lyricsWords[i] + "]"); 125 | } 126 | return lrcText.toString(); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/lrc/LrcLyricsFileReader.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats.lrc; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.zlm.hp.lyrics.formats.LyricsFileReader; 6 | import com.zlm.hp.lyrics.model.LyricsInfo; 7 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 8 | import com.zlm.hp.lyrics.model.LyricsTag; 9 | import com.zlm.hp.lyrics.utils.TimeUtils; 10 | import com.zlm.hp.lyrics.utils.UnicodeInputStream; 11 | 12 | import java.io.BufferedReader; 13 | import java.io.File; 14 | import java.io.FileInputStream; 15 | import java.io.InputStream; 16 | import java.io.InputStreamReader; 17 | import java.nio.charset.Charset; 18 | import java.util.HashMap; 19 | import java.util.Iterator; 20 | import java.util.Map; 21 | import java.util.SortedMap; 22 | import java.util.TreeMap; 23 | import java.util.regex.Matcher; 24 | import java.util.regex.Pattern; 25 | 26 | /** 27 | * lrc歌词解析器 28 | * Created by zhangliangming on 2018-02-24. 29 | */ 30 | 31 | public class LrcLyricsFileReader extends LyricsFileReader { 32 | 33 | /** 34 | * 歌曲名 字符串 35 | */ 36 | private final static String LEGAL_SONGNAME_PREFIX = "[ti:"; 37 | /** 38 | * 歌手名 字符串 39 | */ 40 | private final static String LEGAL_SINGERNAME_PREFIX = "[ar:"; 41 | /** 42 | * 时间补偿值 字符串 43 | */ 44 | private final static String LEGAL_OFFSET_PREFIX = "[offset:"; 45 | /** 46 | * 歌词上传者 47 | */ 48 | private final static String LEGAL_BY_PREFIX = "[by:"; 49 | 50 | /** 51 | * 专辑 52 | */ 53 | private final static String LEGAL_AL_PREFIX = "[al:"; 54 | 55 | private final static String LEGAL_TOTAL_PREFIX = "[total:"; 56 | 57 | /** 58 | * 读取歌词文件 59 | * 60 | * @param file 61 | * @return 62 | */ 63 | @Override 64 | public LyricsInfo readFile(File file) throws Exception { 65 | if (file != null) { 66 | String charsetName = getCharsetName(file); 67 | setDefaultCharset(Charset.forName(charsetName)); 68 | InputStream inputStream = new FileInputStream(file); 69 | if (charsetName.toLowerCase().equals("utf-8")) { 70 | inputStream = new UnicodeInputStream(inputStream, charsetName); 71 | } 72 | return readInputStream(inputStream); 73 | } 74 | return null; 75 | } 76 | 77 | @Override 78 | public LyricsInfo readInputStream(InputStream in) throws Exception { 79 | LyricsInfo lyricsIfno = new LyricsInfo(); 80 | lyricsIfno.setLyricsFileExt(getSupportFileExt()); 81 | lyricsIfno.setLyricsType(LyricsInfo.LRC); 82 | 83 | if (in != null) { 84 | BufferedReader br = new BufferedReader(new InputStreamReader(in, 85 | getDefaultCharset())); 86 | 87 | // 这里面key为该行歌词的开始时间,方便后面排序 88 | SortedMap lyricsLineInfosTemp = new TreeMap(); 89 | Map lyricsTags = new HashMap(); 90 | String lineInfo = ""; 91 | while ((lineInfo = br.readLine()) != null) { 92 | 93 | // 解析歌词 94 | parserLineInfos(lyricsLineInfosTemp, 95 | lyricsTags, lineInfo); 96 | 97 | } 98 | in.close(); 99 | in = null; 100 | // 重新封装 101 | TreeMap lyricsLineInfos = new TreeMap(); 102 | int index = 0; 103 | Iterator it = lyricsLineInfosTemp.keySet().iterator(); 104 | while (it.hasNext()) { 105 | lyricsLineInfos 106 | .put(index++, lyricsLineInfosTemp.get(it.next())); 107 | } 108 | it = null; 109 | // 设置歌词的标签类 110 | lyricsIfno.setLyricsTags(lyricsTags); 111 | // 112 | lyricsIfno.setLyricsLineInfoTreeMap(lyricsLineInfos); 113 | } 114 | return lyricsIfno; 115 | } 116 | 117 | @Override 118 | public LyricsInfo readLrcText(String dynamicContent, String lrcContent, String extraLrcContent, String lyricsFilePath) throws Exception { 119 | LyricsInfo lyricsIfno = new LyricsInfo(); 120 | lyricsIfno.setLyricsFileExt(getSupportFileExt()); 121 | lyricsIfno.setLyricsType(LyricsInfo.LRC); 122 | 123 | if (!TextUtils.isEmpty(lrcContent)) { 124 | 125 | // 这里面key为该行歌词的开始时间,方便后面排序 126 | SortedMap lyricsLineInfosTemp = new TreeMap(); 127 | Map lyricsTags = new HashMap(); 128 | 129 | // 获取歌词内容 130 | String lrcContents[] = lrcContent.split("\n"); 131 | for (int i = 0; i < lrcContents.length; i++) { 132 | String lineInfo = lrcContents[i]; 133 | 134 | // 解析歌词 135 | parserLineInfos(lyricsLineInfosTemp, 136 | lyricsTags, lineInfo); 137 | } 138 | 139 | // 重新封装 140 | TreeMap lyricsLineInfos = new TreeMap(); 141 | int index = 0; 142 | Iterator it = lyricsLineInfosTemp.keySet().iterator(); 143 | while (it.hasNext()) { 144 | lyricsLineInfos 145 | .put(index++, lyricsLineInfosTemp.get(it.next())); 146 | } 147 | it = null; 148 | // 设置歌词的标签类 149 | lyricsIfno.setLyricsTags(lyricsTags); 150 | // 151 | lyricsIfno.setLyricsLineInfoTreeMap(lyricsLineInfos); 152 | } 153 | return lyricsIfno; 154 | 155 | } 156 | 157 | /** 158 | * 解析行歌词 159 | * 160 | * @param lyricsLineInfosTemp 排序集合 161 | * @param lyricsTags 歌曲标签 162 | * @param lineInfo 行歌词内容 163 | * @throws Exception 164 | */ 165 | private void parserLineInfos(SortedMap lyricsLineInfosTemp, Map lyricsTags, String lineInfo) throws Exception { 166 | LyricsLineInfo lyricsLineInfo = null; 167 | if (lineInfo.startsWith(LEGAL_SONGNAME_PREFIX)) { 168 | int startIndex = LEGAL_SONGNAME_PREFIX.length(); 169 | int endIndex = lineInfo.lastIndexOf("]"); 170 | // 171 | lyricsTags.put(LyricsTag.TAG_TITLE, 172 | lineInfo.substring(startIndex, endIndex)); 173 | } else if (lineInfo.startsWith(LEGAL_SINGERNAME_PREFIX)) { 174 | int startIndex = LEGAL_SINGERNAME_PREFIX.length(); 175 | int endIndex = lineInfo.lastIndexOf("]"); 176 | lyricsTags.put(LyricsTag.TAG_ARTIST, 177 | lineInfo.substring(startIndex, endIndex)); 178 | } else if (lineInfo.startsWith(LEGAL_OFFSET_PREFIX)) { 179 | int startIndex = LEGAL_OFFSET_PREFIX.length(); 180 | int endIndex = lineInfo.lastIndexOf("]"); 181 | lyricsTags.put(LyricsTag.TAG_OFFSET, 182 | lineInfo.substring(startIndex, endIndex)); 183 | } else if (lineInfo.startsWith(LEGAL_BY_PREFIX) 184 | || lineInfo.startsWith(LEGAL_TOTAL_PREFIX) 185 | || lineInfo.startsWith(LEGAL_AL_PREFIX)) { 186 | 187 | int startIndex = lineInfo.indexOf("[") + 1; 188 | int endIndex = lineInfo.lastIndexOf("]"); 189 | String temp[] = lineInfo.substring(startIndex, endIndex).split(":"); 190 | lyricsTags.put(temp[0], temp.length == 1 ? "" : temp[1]); 191 | 192 | } else { 193 | //时间标签 194 | String timeRegex = "\\[\\d+:\\d+.\\d+\\]"; 195 | String timeRegexs = "(" + timeRegex + ")+"; 196 | // 如果含有时间标签,则是歌词行 197 | Pattern pattern = Pattern.compile(timeRegexs); 198 | Matcher matcher = pattern.matcher(lineInfo); 199 | if (matcher.find()) { 200 | Pattern timePattern = Pattern.compile(timeRegex); 201 | Matcher timeMatcher = timePattern 202 | .matcher(matcher.group()); 203 | //遍历时间标签 204 | while (timeMatcher.find()) { 205 | lyricsLineInfo = new LyricsLineInfo(); 206 | //获取开始时间 207 | String startTimeString = timeMatcher.group().trim(); 208 | int startTime = TimeUtils.parseInteger(startTimeString.substring(startTimeString.indexOf('[') + 1, startTimeString.lastIndexOf(']'))); 209 | lyricsLineInfo.setStartTime(startTime); 210 | //获取歌词内容 211 | int timeEndIndex = matcher.end(); 212 | String lineLyrics = lineInfo.substring(timeEndIndex, 213 | lineInfo.length()).trim(); 214 | lyricsLineInfo.setLineLyrics(lineLyrics); 215 | lyricsLineInfosTemp.put(startTime, lyricsLineInfo); 216 | } 217 | } 218 | } 219 | } 220 | 221 | @Override 222 | public boolean isFileSupported(String ext) { 223 | return ext.equalsIgnoreCase("lrc"); 224 | } 225 | 226 | @Override 227 | public String getSupportFileExt() { 228 | return "lrc"; 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/lrc/LrcLyricsFileWriter.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats.lrc; 2 | 3 | import com.zlm.hp.lyrics.formats.LyricsFileWriter; 4 | import com.zlm.hp.lyrics.model.LyricsInfo; 5 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 6 | import com.zlm.hp.lyrics.model.LyricsTag; 7 | import com.zlm.hp.lyrics.utils.TimeUtils; 8 | 9 | import java.util.ArrayList; 10 | import java.util.LinkedHashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.TreeMap; 14 | 15 | /** 16 | * lrc歌词生成器 17 | * Created by zhangliangming on 2018-02-24. 18 | */ 19 | 20 | public class LrcLyricsFileWriter extends LyricsFileWriter { 21 | 22 | /** 23 | * 歌曲名 字符串 24 | */ 25 | private final static String LEGAL_SONGNAME_PREFIX = "[ti:"; 26 | /** 27 | * 歌手名 字符串 28 | */ 29 | private final static String LEGAL_SINGERNAME_PREFIX = "[ar:"; 30 | /** 31 | * 时间补偿值 字符串 32 | */ 33 | private final static String LEGAL_OFFSET_PREFIX = "[offset:"; 34 | 35 | /** 36 | * 歌曲长度 37 | */ 38 | private final static String LEGAL_TOTAL_PREFIX = "[total:"; 39 | 40 | @Override 41 | public boolean writer(LyricsInfo lyricsIfno, String lyricsFilePath) throws Exception { 42 | String lyricsContent = getLyricsContent(lyricsIfno); 43 | return saveLyricsFile(lyricsContent, lyricsFilePath); 44 | } 45 | 46 | @Override 47 | public String getLyricsContent(LyricsInfo lyricsIfno) throws Exception { 48 | StringBuilder lyricsCom = new StringBuilder(); 49 | // 先保存所有的标签数据 50 | Map tags = lyricsIfno.getLyricsTags(); 51 | for (Map.Entry entry : tags.entrySet()) { 52 | Object val = entry.getValue(); 53 | if (entry.getKey().equals(LyricsTag.TAG_TITLE)) { 54 | lyricsCom.append(LEGAL_SONGNAME_PREFIX); 55 | } else if (entry.getKey().equals(LyricsTag.TAG_ARTIST)) { 56 | lyricsCom.append(LEGAL_SINGERNAME_PREFIX); 57 | } else if (entry.getKey().equals(LyricsTag.TAG_OFFSET)) { 58 | lyricsCom.append(LEGAL_OFFSET_PREFIX); 59 | } else if (entry.getKey().equals(LyricsTag.TAG_TOTAL)) { 60 | lyricsCom.append(LEGAL_TOTAL_PREFIX); 61 | } else { 62 | val = "[" + entry.getKey() + ":" + val; 63 | } 64 | lyricsCom.append(val + "]\n"); 65 | } 66 | 67 | TreeMap lyricsLineInfos = lyricsIfno 68 | .getLyricsLineInfoTreeMap(); 69 | // 将每行歌词,放到有序的map,判断已重复的歌词 70 | LinkedHashMap> lyricsLineInfoMapResult = new LinkedHashMap>(); 71 | for (int i = 0; i < lyricsLineInfos.size(); i++) { 72 | LyricsLineInfo lyricsLineInfo = lyricsLineInfos.get(i); 73 | String saveLineLyrics = lyricsLineInfo.getLineLyrics(); 74 | List indexs = null; 75 | // 如果已存在该行歌词,则往里面添加歌词行索引 76 | if (lyricsLineInfoMapResult.containsKey(saveLineLyrics)) { 77 | indexs = lyricsLineInfoMapResult.get(saveLineLyrics); 78 | } else { 79 | indexs = new ArrayList(); 80 | } 81 | indexs.add(i); 82 | lyricsLineInfoMapResult.put(saveLineLyrics, indexs); 83 | } 84 | // 遍历 85 | for (Map.Entry> entry : lyricsLineInfoMapResult 86 | .entrySet()) { 87 | List indexs = entry.getValue(); 88 | // 当前行歌词文本 89 | String saveLineLyrics = entry.getKey(); 90 | StringBuilder timeText = new StringBuilder();// 时间标签内容 91 | 92 | for (int i = 0; i < indexs.size(); i++) { 93 | int key = indexs.get(i); 94 | LyricsLineInfo lyricsLineInfo = lyricsLineInfos.get(key); 95 | // 获取开始时间 96 | timeText.append("[" + TimeUtils.parseMMSSFFString(lyricsLineInfo.getStartTime()) + "]"); 97 | } 98 | lyricsCom.append(timeText.toString() + ""); 99 | lyricsCom.append("" + saveLineLyrics + "\n"); 100 | } 101 | return lyricsCom.toString(); 102 | } 103 | 104 | @Override 105 | public boolean isFileSupported(String ext) { 106 | return ext.equalsIgnoreCase("lrc"); 107 | } 108 | 109 | @Override 110 | public String getSupportFileExt() { 111 | return "lrc"; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/lrcwy/WYLyricsFileReader.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats.lrcwy; 2 | 3 | import android.text.TextUtils; 4 | import android.util.Base64; 5 | 6 | import com.zlm.hp.lyrics.formats.LyricsFileReader; 7 | import com.zlm.hp.lyrics.model.LyricsInfo; 8 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 9 | import com.zlm.hp.lyrics.model.LyricsTag; 10 | import com.zlm.hp.lyrics.model.TranslateLrcLineInfo; 11 | import com.zlm.hp.lyrics.utils.TimeUtils; 12 | import com.zlm.hp.lyrics.utils.UnicodeInputStream; 13 | 14 | import java.io.BufferedReader; 15 | import java.io.File; 16 | import java.io.FileInputStream; 17 | import java.io.InputStream; 18 | import java.io.InputStreamReader; 19 | import java.nio.charset.Charset; 20 | import java.util.ArrayList; 21 | import java.util.HashMap; 22 | import java.util.Iterator; 23 | import java.util.List; 24 | import java.util.Map; 25 | import java.util.SortedMap; 26 | import java.util.TreeMap; 27 | import java.util.regex.Matcher; 28 | import java.util.regex.Pattern; 29 | 30 | /** 31 | * @Description: 网易歌词读取器 32 | * @author: zhangliangming 33 | * @date: 2018-12-27 0:03 34 | **/ 35 | public class WYLyricsFileReader extends LyricsFileReader { 36 | /** 37 | * 歌曲名 字符串 38 | */ 39 | private final static String LEGAL_SONGNAME_PREFIX = "[ti:"; 40 | /** 41 | * 歌手名 字符串 42 | */ 43 | private final static String LEGAL_SINGERNAME_PREFIX = "[ar:"; 44 | /** 45 | * 时间补偿值 字符串 46 | */ 47 | private final static String LEGAL_OFFSET_PREFIX = "[offset:"; 48 | /** 49 | * 歌词上传者 50 | */ 51 | private final static String LEGAL_BY_PREFIX = "[by:"; 52 | 53 | /** 54 | * 专辑 55 | */ 56 | private final static String LEGAL_AL_PREFIX = "[al:"; 57 | 58 | private final static String LEGAL_TOTAL_PREFIX = "[total:"; 59 | 60 | /** 61 | * lrc歌词 字符串 62 | */ 63 | public final static String LEGAL_LYRICS_LINE_PREFIX = "wy.lrc"; 64 | 65 | /** 66 | * 动感歌词 字符串 67 | */ 68 | public final static String LEGAL_DLYRICS_LINE_PREFIX = "wy.dlrc"; 69 | 70 | /** 71 | * 额外歌词 72 | */ 73 | private final static String LEGAL_EXTRA_LYRICS_PREFIX = "wy.extra.lrc"; 74 | 75 | /** 76 | * 读取歌词文件 77 | * 78 | * @param file 79 | * @return 80 | */ 81 | @Override 82 | public LyricsInfo readFile(File file) throws Exception { 83 | if (file != null) { 84 | String charsetName = getCharsetName(file); 85 | setDefaultCharset(Charset.forName(charsetName)); 86 | InputStream inputStream = new FileInputStream(file); 87 | if (charsetName.toLowerCase().equals("utf-8")) { 88 | inputStream = new UnicodeInputStream(inputStream, charsetName); 89 | } 90 | return readInputStream(inputStream); 91 | } 92 | return null; 93 | } 94 | 95 | @Override 96 | public LyricsInfo readInputStream(InputStream in) throws Exception { 97 | LyricsInfo lyricsIfno = new LyricsInfo(); 98 | lyricsIfno.setLyricsFileExt(getSupportFileExt()); 99 | 100 | if (in != null) { 101 | BufferedReader br = new BufferedReader(new InputStreamReader(in, 102 | getDefaultCharset())); 103 | 104 | Map lyricsTags = new HashMap(); 105 | String lineInfo = ""; 106 | while ((lineInfo = br.readLine()) != null) { 107 | 108 | // 解析歌词 109 | parserLineInfos(lyricsIfno, 110 | lyricsTags, lineInfo); 111 | 112 | } 113 | in.close(); 114 | in = null; 115 | // 设置歌词的标签类 116 | lyricsIfno.setLyricsTags(lyricsTags); 117 | } 118 | return lyricsIfno; 119 | } 120 | 121 | /** 122 | * 解析歌词文件 123 | * 124 | * @param lyricsIfno 125 | * @param lyricsTags 126 | * @param lineInfo 127 | */ 128 | private void parserLineInfos(LyricsInfo lyricsIfno, Map lyricsTags, String lineInfo) throws Exception { 129 | if (lineInfo.startsWith(LEGAL_SONGNAME_PREFIX)) { 130 | int startIndex = LEGAL_SONGNAME_PREFIX.length(); 131 | int endIndex = lineInfo.lastIndexOf("]"); 132 | // 133 | lyricsTags.put(LyricsTag.TAG_TITLE, 134 | lineInfo.substring(startIndex, endIndex)); 135 | } else if (lineInfo.startsWith(LEGAL_SINGERNAME_PREFIX)) { 136 | int startIndex = LEGAL_SINGERNAME_PREFIX.length(); 137 | int endIndex = lineInfo.lastIndexOf("]"); 138 | lyricsTags.put(LyricsTag.TAG_ARTIST, 139 | lineInfo.substring(startIndex, endIndex)); 140 | } else if (lineInfo.startsWith(LEGAL_OFFSET_PREFIX)) { 141 | int startIndex = LEGAL_OFFSET_PREFIX.length(); 142 | int endIndex = lineInfo.lastIndexOf("]"); 143 | lyricsTags.put(LyricsTag.TAG_OFFSET, 144 | lineInfo.substring(startIndex, endIndex)); 145 | } else if (lineInfo.startsWith(LEGAL_BY_PREFIX) 146 | || lineInfo.startsWith(LEGAL_TOTAL_PREFIX) 147 | || lineInfo.startsWith(LEGAL_AL_PREFIX)) { 148 | 149 | int startIndex = lineInfo.indexOf("[") + 1; 150 | int endIndex = lineInfo.lastIndexOf("]"); 151 | String temp[] = lineInfo.substring(startIndex, endIndex).split(":"); 152 | lyricsTags.put(temp[0], temp.length == 1 ? "" : temp[1]); 153 | 154 | } else { 155 | if (lineInfo.startsWith(LEGAL_LYRICS_LINE_PREFIX)) { 156 | //lrc歌词 157 | lyricsIfno.setLyricsType(LyricsInfo.LRC); 158 | int startIndex = lineInfo.indexOf("("); 159 | int endIndex = lineInfo.lastIndexOf(")"); 160 | String lrcContent = lineInfo.substring(startIndex + 1, endIndex); 161 | lrcContent = new String(Base64.decode(lrcContent, Base64.NO_WRAP)); 162 | parserLrcCom(lyricsIfno, lrcContent); 163 | 164 | } else if (lineInfo.startsWith(LEGAL_DLYRICS_LINE_PREFIX)) { 165 | lyricsIfno.setLyricsType(LyricsInfo.DYNAMIC); 166 | //动感歌词 167 | int startIndex = lineInfo.indexOf("("); 168 | int endIndex = lineInfo.lastIndexOf(")"); 169 | String dynamicContent = lineInfo.substring(startIndex + 1, endIndex); 170 | dynamicContent = new String(Base64.decode(dynamicContent, Base64.NO_WRAP)); 171 | parseDynamicLrc(lyricsIfno, dynamicContent); 172 | 173 | } else if (lineInfo.startsWith(LEGAL_EXTRA_LYRICS_PREFIX)) { 174 | //额外歌词 175 | int startIndex = lineInfo.indexOf("("); 176 | int endIndex = lineInfo.lastIndexOf(")"); 177 | String extraLrcContent = lineInfo.substring(startIndex + 1, endIndex); 178 | extraLrcContent = new String(Base64.decode(extraLrcContent, Base64.NO_WRAP)); 179 | parserExtraLrc(lyricsIfno, extraLrcContent); 180 | } 181 | } 182 | 183 | } 184 | 185 | /** 186 | * 解析额外歌词 187 | * 188 | * @param lyricsIfno 189 | * @param extraLrcContent 190 | */ 191 | private void parserExtraLrc(LyricsInfo lyricsIfno, String extraLrcContent) { 192 | // 翻译歌词集合 193 | List translateLrcLineInfos = new ArrayList(); 194 | 195 | // 获取歌词内容 196 | String lrcContents[] = extraLrcContent.split("\n"); 197 | for (int i = 0; i < lrcContents.length; i++) { 198 | String lineInfo = lrcContents[i]; 199 | // 翻译行歌词 200 | TranslateLrcLineInfo translateLrcLineInfo = new TranslateLrcLineInfo(); 201 | translateLrcLineInfo.setLineLyrics(lineInfo.trim()); 202 | translateLrcLineInfos.add(translateLrcLineInfo); 203 | } 204 | // 添加翻译歌词 205 | if (translateLrcLineInfos.size() > 0) { 206 | lyricsIfno.setTranslateLrcLineInfos(translateLrcLineInfos); 207 | } 208 | } 209 | 210 | @Override 211 | public LyricsInfo readLrcText(String dynamicContent, String lrcContent, String extraLrcContent, String lyricsFilePath) throws Exception { 212 | return parserLrc(dynamicContent, lrcContent, extraLrcContent, lyricsFilePath); 213 | } 214 | 215 | 216 | /** 217 | * 解析歌词,其中动感歌词和lrc歌词只能2个选1个 218 | * 219 | * @param dynamicContent 动感歌词内容 220 | * @param lrcContent lrc歌词内容 221 | * @param extraLrcContent 额外歌词内容(翻译歌词、音译歌词) 222 | * @param lyricsFilePath 歌词文件保存路径 223 | * @return 224 | * @throws Exception 225 | */ 226 | private LyricsInfo parserLrc(String dynamicContent, String lrcContent, String extraLrcContent, String lyricsFilePath) throws Exception { 227 | LyricsInfo lyricsInfo = new LyricsInfo(); 228 | lyricsInfo.setLyricsFileExt(getSupportFileExt()); 229 | if (!TextUtils.isEmpty(dynamicContent)) { 230 | lyricsInfo.setLyricsType(LyricsInfo.DYNAMIC); 231 | //解析动感歌词 232 | parseDynamicLrc(lyricsInfo, dynamicContent); 233 | } else if (!TextUtils.isEmpty(lrcContent)) { 234 | lyricsInfo.setLyricsType(LyricsInfo.LRC); 235 | //解析lrc歌词 236 | parserLrcCom(lyricsInfo, lrcContent); 237 | } 238 | if (!TextUtils.isEmpty(extraLrcContent)) { 239 | //解析额外歌词 240 | parserTranslateLrc(lyricsInfo, extraLrcContent); 241 | } 242 | 243 | if (!TextUtils.isEmpty(lyricsFilePath)) { 244 | //保存歌词 245 | new WYLyricsFileWriter().writer(lyricsInfo, lyricsFilePath); 246 | } 247 | return lyricsInfo; 248 | } 249 | 250 | /** 251 | * 解析翻译歌词 252 | * 253 | * @param lyricsIfno 254 | * @param translateLrcContent 255 | */ 256 | private void parserTranslateLrc(LyricsInfo lyricsIfno, String translateLrcContent) { 257 | // 这里面key为该行歌词的开始时间,方便后面排序 258 | SortedMap translateLrcInfosTemp = new TreeMap(); 259 | Map lyricsTags = new HashMap(); 260 | String lrcContents[] = translateLrcContent.split("\n"); 261 | for (int i = 0; i < lrcContents.length; i++) { 262 | String lineInfo = lrcContents[i]; 263 | // 解析lrc歌词行 264 | parserLrcLineInfos(translateLrcInfosTemp, 265 | lyricsTags, lineInfo); 266 | 267 | } 268 | // 翻译歌词集合 269 | List translateLrcLineInfos = new ArrayList(); 270 | TreeMap lyricsLineInfos = lyricsIfno.getLyricsLineInfoTreeMap(); 271 | for (int i = 0; i < lyricsLineInfos.size(); i++) { 272 | TranslateLrcLineInfo translateLrcLineInfo = new TranslateLrcLineInfo(); 273 | LyricsLineInfo lyricsLineInfo = lyricsLineInfos.get(i); 274 | if (translateLrcInfosTemp.containsKey(lyricsLineInfo.getStartTime())) { 275 | LyricsLineInfo temp = translateLrcInfosTemp.get(lyricsLineInfo.getStartTime()); 276 | translateLrcLineInfo.setLineLyrics(temp.getLineLyrics()); 277 | }else{ 278 | translateLrcLineInfo.setLineLyrics(""); 279 | } 280 | translateLrcLineInfos.add(i, translateLrcLineInfo); 281 | } 282 | 283 | // 添加翻译歌词 284 | if (translateLrcLineInfos.size() > 0) { 285 | lyricsIfno.setTranslateLrcLineInfos(translateLrcLineInfos); 286 | } 287 | } 288 | 289 | /** 290 | * 解析动感歌词 291 | * 292 | * @param lyricsInfo 293 | * @param dynamicContent 294 | */ 295 | private void parseDynamicLrc(LyricsInfo lyricsInfo, String dynamicContent) throws Exception { 296 | TreeMap lyricsLineInfos = new TreeMap(); 297 | Map lyricsTags = new HashMap(); 298 | String lrcContents[] = dynamicContent.split("\n"); 299 | int index = 0; 300 | for (int i = 0; i < lrcContents.length; i++) { 301 | String lineInfo = lrcContents[i]; 302 | // 解析动感歌词行 303 | LyricsLineInfo lyricsLineInfo = parserDynamicLrcLineInfos(lyricsTags, 304 | lineInfo); 305 | if (lyricsLineInfo != null) { 306 | lyricsLineInfos.put(index, lyricsLineInfo); 307 | index++; 308 | } 309 | 310 | } 311 | 312 | // 设置歌词的标签类 313 | lyricsInfo.setLyricsTags(lyricsTags); 314 | // 315 | lyricsInfo.setLyricsLineInfoTreeMap(lyricsLineInfos); 316 | } 317 | 318 | /** 319 | * 解析动感歌词行 320 | * 321 | * @param lyricsTags 歌曲标签 322 | * @param lineInfo 行歌词内容 323 | */ 324 | private LyricsLineInfo parserDynamicLrcLineInfos(Map lyricsTags, String lineInfo) throws Exception { 325 | LyricsLineInfo lyricsLineInfo = null; 326 | if (lineInfo.startsWith(LEGAL_SONGNAME_PREFIX)) { 327 | int startIndex = LEGAL_SONGNAME_PREFIX.length(); 328 | int endIndex = lineInfo.lastIndexOf("]"); 329 | // 330 | lyricsTags.put(LyricsTag.TAG_TITLE, 331 | lineInfo.substring(startIndex, endIndex)); 332 | } else if (lineInfo.startsWith(LEGAL_SINGERNAME_PREFIX)) { 333 | int startIndex = LEGAL_SINGERNAME_PREFIX.length(); 334 | int endIndex = lineInfo.lastIndexOf("]"); 335 | lyricsTags.put(LyricsTag.TAG_ARTIST, 336 | lineInfo.substring(startIndex, endIndex)); 337 | } else if (lineInfo.startsWith(LEGAL_OFFSET_PREFIX)) { 338 | int startIndex = LEGAL_OFFSET_PREFIX.length(); 339 | int endIndex = lineInfo.lastIndexOf("]"); 340 | lyricsTags.put(LyricsTag.TAG_OFFSET, 341 | lineInfo.substring(startIndex, endIndex)); 342 | } else if (lineInfo.startsWith(LEGAL_BY_PREFIX) 343 | || lineInfo.startsWith(LEGAL_TOTAL_PREFIX) 344 | || lineInfo.startsWith(LEGAL_AL_PREFIX)) { 345 | 346 | int startIndex = lineInfo.indexOf("[") + 1; 347 | int endIndex = lineInfo.lastIndexOf("]"); 348 | String temp[] = lineInfo.substring(startIndex, endIndex).split(":"); 349 | lyricsTags.put(temp[0], temp.length == 1 ? "" : temp[1]); 350 | 351 | } else { 352 | //时间标签 353 | Pattern pattern = Pattern.compile("\\[\\d+,\\d+\\]"); 354 | Matcher matcher = pattern.matcher(lineInfo); 355 | if (matcher.find()) { 356 | lyricsLineInfo = new LyricsLineInfo(); 357 | // [此行开始时刻距0时刻的毫秒数,此行持续的毫秒数](0,此字持续的毫秒数)歌(0,此字持续的毫秒数)词(0,此字持续的毫秒数)正(0,此字持续的毫秒数)文 358 | // 获取行的出现时间和结束时间 359 | int mStartIndex = matcher.start(); 360 | int mEndIndex = matcher.end(); 361 | String lineTime[] = lineInfo.substring(mStartIndex + 1, 362 | mEndIndex - 1).split(","); 363 | // 364 | 365 | int startTime = Integer.parseInt(lineTime[0]); 366 | int endTime = startTime + Integer.parseInt(lineTime[1]); 367 | lyricsLineInfo.setEndTime(endTime); 368 | lyricsLineInfo.setStartTime(startTime); 369 | // 获取歌词信息 370 | String lineContent = lineInfo.substring(mEndIndex, 371 | lineInfo.length()); 372 | 373 | // 歌词匹配的正则表达式 374 | String regex = "\\(\\d+,\\d+\\)"; 375 | Pattern lyricsWordsPattern = Pattern.compile(regex); 376 | Matcher lyricsWordsMatcher = lyricsWordsPattern 377 | .matcher(lineContent); 378 | 379 | // 歌词分隔 380 | String lineLyricsTemp[] = lineContent.split(regex); 381 | String[] lyricsWords = getLyricsWords(lineLyricsTemp); 382 | lyricsLineInfo.setLyricsWords(lyricsWords); 383 | 384 | // 获取每个歌词的时间 385 | int wordsDisInterval[] = new int[lyricsWords.length]; 386 | int index = 0; 387 | while (lyricsWordsMatcher.find()) { 388 | 389 | //验证 390 | if (index >= wordsDisInterval.length) { 391 | throw new Exception("字标签个数与字时间标签个数不相符"); 392 | } 393 | 394 | // 395 | String wordsDisIntervalStr = lyricsWordsMatcher.group(); 396 | String wordsDisIntervalStrTemp = wordsDisIntervalStr 397 | .substring(wordsDisIntervalStr.indexOf('(') + 1, wordsDisIntervalStr.lastIndexOf(')')); 398 | String wordsDisIntervalTemp[] = wordsDisIntervalStrTemp 399 | .split(","); 400 | wordsDisInterval[index++] = Integer 401 | .parseInt(wordsDisIntervalTemp[1]); 402 | } 403 | lyricsLineInfo.setWordsDisInterval(wordsDisInterval); 404 | 405 | // 获取当行歌词 406 | String lineLyrics = lyricsWordsMatcher.replaceAll(""); 407 | lyricsLineInfo.setLineLyrics(lineLyrics); 408 | 409 | } 410 | } 411 | return lyricsLineInfo; 412 | } 413 | 414 | 415 | /** 416 | * 分隔每个歌词 417 | * 418 | * @param lineLyricsTemp 419 | * @return 420 | */ 421 | private String[] getLyricsWords(String[] lineLyricsTemp) { 422 | String temp[] = null; 423 | if (lineLyricsTemp.length < 2) { 424 | return new String[lineLyricsTemp.length]; 425 | } 426 | // 427 | temp = new String[lineLyricsTemp.length - 1]; 428 | for (int i = 1; i < lineLyricsTemp.length; i++) { 429 | temp[i - 1] = lineLyricsTemp[i]; 430 | } 431 | return temp; 432 | } 433 | 434 | 435 | /** 436 | * 解析lrc歌词 437 | * 438 | * @param lyricsInfo 439 | * @param lrcContent 440 | */ 441 | private void parserLrcCom(LyricsInfo lyricsInfo, String lrcContent) { 442 | // 这里面key为该行歌词的开始时间,方便后面排序 443 | SortedMap lyricsLineInfosTemp = new TreeMap(); 444 | Map lyricsTags = new HashMap(); 445 | String lrcContents[] = lrcContent.split("\n"); 446 | for (int i = 0; i < lrcContents.length; i++) { 447 | String lineInfo = lrcContents[i]; 448 | // 解析lrc歌词行 449 | parserLrcLineInfos(lyricsLineInfosTemp, 450 | lyricsTags, lineInfo); 451 | 452 | } 453 | // 重新封装 454 | TreeMap lyricsLineInfos = new TreeMap(); 455 | int index = 0; 456 | Iterator it = lyricsLineInfosTemp.keySet().iterator(); 457 | while (it.hasNext()) { 458 | lyricsLineInfos 459 | .put(index++, lyricsLineInfosTemp.get(it.next())); 460 | } 461 | it = null; 462 | // 设置歌词的标签类 463 | lyricsInfo.setLyricsTags(lyricsTags); 464 | // 465 | lyricsInfo.setLyricsLineInfoTreeMap(lyricsLineInfos); 466 | } 467 | 468 | /** 469 | * 解析行歌词 470 | * 471 | * @param lyricsLineInfosTemp 排序集合 472 | * @param lyricsTags 歌曲标签 473 | * @param lineInfo 行歌词内容 474 | * @throws Exception 475 | */ 476 | private void parserLrcLineInfos(SortedMap lyricsLineInfosTemp, Map lyricsTags, String lineInfo) { 477 | LyricsLineInfo lyricsLineInfo = null; 478 | if (lineInfo.startsWith(LEGAL_SONGNAME_PREFIX)) { 479 | int startIndex = LEGAL_SONGNAME_PREFIX.length(); 480 | int endIndex = lineInfo.lastIndexOf("]"); 481 | // 482 | lyricsTags.put(LyricsTag.TAG_TITLE, 483 | lineInfo.substring(startIndex, endIndex)); 484 | } else if (lineInfo.startsWith(LEGAL_SINGERNAME_PREFIX)) { 485 | int startIndex = LEGAL_SINGERNAME_PREFIX.length(); 486 | int endIndex = lineInfo.lastIndexOf("]"); 487 | lyricsTags.put(LyricsTag.TAG_ARTIST, 488 | lineInfo.substring(startIndex, endIndex)); 489 | } else if (lineInfo.startsWith(LEGAL_OFFSET_PREFIX)) { 490 | int startIndex = LEGAL_OFFSET_PREFIX.length(); 491 | int endIndex = lineInfo.lastIndexOf("]"); 492 | lyricsTags.put(LyricsTag.TAG_OFFSET, 493 | lineInfo.substring(startIndex, endIndex)); 494 | } else if (lineInfo.startsWith(LEGAL_BY_PREFIX) 495 | || lineInfo.startsWith(LEGAL_TOTAL_PREFIX) 496 | || lineInfo.startsWith(LEGAL_AL_PREFIX)) { 497 | 498 | int startIndex = lineInfo.indexOf("[") + 1; 499 | int endIndex = lineInfo.lastIndexOf("]"); 500 | String temp[] = lineInfo.substring(startIndex, endIndex).split(":"); 501 | lyricsTags.put(temp[0], temp.length == 1 ? "" : temp[1]); 502 | 503 | } else { 504 | //时间标签 505 | String timeRegex = "\\[\\d+:\\d+.\\d+\\]"; 506 | String timeRegexs = "(" + timeRegex + ")+"; 507 | // 如果含有时间标签,则是歌词行 508 | Pattern pattern = Pattern.compile(timeRegexs); 509 | Matcher matcher = pattern.matcher(lineInfo); 510 | if (matcher.find()) { 511 | Pattern timePattern = Pattern.compile(timeRegex); 512 | Matcher timeMatcher = timePattern 513 | .matcher(matcher.group()); 514 | //遍历时间标签 515 | while (timeMatcher.find()) { 516 | lyricsLineInfo = new LyricsLineInfo(); 517 | //获取开始时间 518 | String startTimeString = timeMatcher.group().trim(); 519 | int startTime = TimeUtils.parseInteger(startTimeString.substring(startTimeString.indexOf('[') + 1, startTimeString.lastIndexOf(']'))); 520 | lyricsLineInfo.setStartTime(startTime); 521 | //获取歌词内容 522 | int timeEndIndex = matcher.end(); 523 | String lineLyrics = lineInfo.substring(timeEndIndex, 524 | lineInfo.length()).trim(); 525 | lyricsLineInfo.setLineLyrics(lineLyrics); 526 | lyricsLineInfosTemp.put(startTime, lyricsLineInfo); 527 | } 528 | } 529 | } 530 | } 531 | 532 | @Override 533 | public boolean isFileSupported(String ext) { 534 | return ext.equalsIgnoreCase("lrcwy"); 535 | } 536 | 537 | @Override 538 | public String getSupportFileExt() { 539 | return "lrcwy"; 540 | } 541 | } 542 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/lrcwy/WYLyricsFileWriter.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats.lrcwy; 2 | 3 | import android.text.TextUtils; 4 | import android.util.Base64; 5 | 6 | import com.zlm.hp.lyrics.formats.LyricsFileWriter; 7 | import com.zlm.hp.lyrics.model.LyricsInfo; 8 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 9 | import com.zlm.hp.lyrics.model.LyricsTag; 10 | import com.zlm.hp.lyrics.model.TranslateLrcLineInfo; 11 | import com.zlm.hp.lyrics.utils.TimeUtils; 12 | 13 | import java.util.ArrayList; 14 | import java.util.LinkedHashMap; 15 | import java.util.List; 16 | import java.util.Map; 17 | import java.util.TreeMap; 18 | 19 | /** 20 | * @Description: 网易歌词生成器 21 | * @author: zhangliangming 22 | * @date: 2018-12-27 0:03 23 | **/ 24 | public class WYLyricsFileWriter extends LyricsFileWriter { 25 | /** 26 | * 歌曲名 字符串 27 | */ 28 | private final static String LEGAL_SONGNAME_PREFIX = "[ti:"; 29 | /** 30 | * 歌手名 字符串 31 | */ 32 | private final static String LEGAL_SINGERNAME_PREFIX = "[ar:"; 33 | /** 34 | * 时间补偿值 字符串 35 | */ 36 | private final static String LEGAL_OFFSET_PREFIX = "[offset:"; 37 | /** 38 | * 歌词上传者 39 | */ 40 | private final static String LEGAL_BY_PREFIX = "[by:"; 41 | 42 | /** 43 | * 专辑 44 | */ 45 | private final static String LEGAL_AL_PREFIX = "[al:"; 46 | 47 | private final static String LEGAL_TOTAL_PREFIX = "[total:"; 48 | 49 | /** 50 | * lrc歌词 字符串 51 | */ 52 | public final static String LEGAL_LYRICS_LINE_PREFIX = "wy.lrc"; 53 | 54 | /** 55 | * 动感歌词 字符串 56 | */ 57 | public final static String LEGAL_DLYRICS_LINE_PREFIX = "wy.dlrc"; 58 | 59 | /** 60 | * 额外歌词 61 | */ 62 | private final static String LEGAL_EXTRA_LYRICS_PREFIX = "wy.extra.lrc"; 63 | 64 | 65 | @Override 66 | public boolean writer(LyricsInfo lyricsIfno, String lyricsFilePath) throws Exception { 67 | String lyricsContent = getLyricsContent(lyricsIfno); 68 | return saveLyricsFile(lyricsContent, lyricsFilePath); 69 | } 70 | 71 | @Override 72 | public String getLyricsContent(LyricsInfo lyricsIfno) throws Exception { 73 | StringBuilder lyricsCom = new StringBuilder(); 74 | // 先保存所有的标签数据 75 | Map tags = lyricsIfno.getLyricsTags(); 76 | for (Map.Entry entry : tags.entrySet()) { 77 | Object val = entry.getValue(); 78 | if (entry.getKey().equals(LyricsTag.TAG_TITLE)) { 79 | lyricsCom.append(LEGAL_SONGNAME_PREFIX); 80 | } else if (entry.getKey().equals(LyricsTag.TAG_ARTIST)) { 81 | lyricsCom.append(LEGAL_SINGERNAME_PREFIX); 82 | } else if (entry.getKey().equals(LyricsTag.TAG_OFFSET)) { 83 | lyricsCom.append(LEGAL_OFFSET_PREFIX); 84 | } else if (entry.getKey().equals(LyricsTag.TAG_TOTAL)) { 85 | lyricsCom.append(LEGAL_TOTAL_PREFIX); 86 | } else { 87 | val = "[" + entry.getKey() + ":" + val; 88 | } 89 | lyricsCom.append(val + "]\n"); 90 | } 91 | //判断歌词类型 92 | if (lyricsIfno.getLyricsType() == LyricsInfo.DYNAMIC) { 93 | //保存动感歌词 94 | String dynamicContent = getDynamicContent(lyricsIfno); 95 | lyricsCom.append(dynamicContent); 96 | } else { 97 | //保存lrc歌词 98 | String lrcContent = getLrcContent(lyricsIfno); 99 | lyricsCom.append(lrcContent); 100 | } 101 | 102 | //保存额外歌词 103 | String extraLrcContent = getExtraLrcContent(lyricsIfno); 104 | if (!TextUtils.isEmpty(extraLrcContent)) { 105 | lyricsCom.append(extraLrcContent); 106 | } 107 | return lyricsCom.toString(); 108 | } 109 | 110 | /** 111 | * 获取额外歌词 112 | * 113 | * @param lyricsIfno 114 | * @return 115 | */ 116 | private String getExtraLrcContent(LyricsInfo lyricsIfno) { 117 | String result = ""; 118 | List translateLrcLineInfos = lyricsIfno.getTranslateLrcLineInfos(); 119 | if (translateLrcLineInfos != null && translateLrcLineInfos.size() > 0) { 120 | StringBuilder lyricsCom = new StringBuilder(); 121 | for (int i = 0; i < translateLrcLineInfos.size(); i++) { 122 | TranslateLrcLineInfo translateLrcLineInfo = translateLrcLineInfos.get(i); 123 | lyricsCom.append(translateLrcLineInfo.getLineLyrics() + "\n"); 124 | } 125 | result += LEGAL_EXTRA_LYRICS_PREFIX + "(" + Base64.encodeToString(lyricsCom.toString().getBytes(), Base64.NO_WRAP) + ")\n"; 126 | } 127 | return result; 128 | } 129 | 130 | /** 131 | * 获取lrc歌词 132 | * 133 | * @param lyricsIfno 134 | * @return 135 | */ 136 | private String getLrcContent(LyricsInfo lyricsIfno) { 137 | TreeMap lyricsLineInfos = lyricsIfno 138 | .getLyricsLineInfoTreeMap(); 139 | String result = ""; 140 | if (lyricsLineInfos != null && lyricsLineInfos.size() > 0) { 141 | StringBuilder lyricsCom = new StringBuilder(); 142 | // 将每行歌词,放到有序的map,判断已重复的歌词 143 | LinkedHashMap> lyricsLineInfoMapResult = new LinkedHashMap>(); 144 | 145 | for (int i = 0; i < lyricsLineInfos.size(); i++) { 146 | LyricsLineInfo lyricsLineInfo = lyricsLineInfos.get(i); 147 | String saveLineLyrics = lyricsLineInfo.getLineLyrics(); 148 | List indexs = null; 149 | // 如果已存在该行歌词,则往里面添加歌词行索引 150 | if (lyricsLineInfoMapResult.containsKey(saveLineLyrics)) { 151 | indexs = lyricsLineInfoMapResult.get(saveLineLyrics); 152 | } else { 153 | indexs = new ArrayList(); 154 | } 155 | indexs.add(i); 156 | lyricsLineInfoMapResult.put(saveLineLyrics, indexs); 157 | } 158 | // 遍历 159 | for (Map.Entry> entry : lyricsLineInfoMapResult 160 | .entrySet()) { 161 | List indexs = entry.getValue(); 162 | // 当前行歌词文本 163 | String saveLineLyrics = entry.getKey(); 164 | StringBuilder timeText = new StringBuilder();// 时间标签内容 165 | 166 | for (int i = 0; i < indexs.size(); i++) { 167 | int key = indexs.get(i); 168 | LyricsLineInfo lyricsLineInfo = lyricsLineInfos.get(key); 169 | // 获取开始时间 170 | timeText.append("[" + TimeUtils.parseMMSSFFString(lyricsLineInfo.getStartTime()) + "]"); 171 | } 172 | lyricsCom.append(timeText.toString() + ""); 173 | lyricsCom.append("" + saveLineLyrics + "\n"); 174 | } 175 | result += LEGAL_LYRICS_LINE_PREFIX + "(" + Base64.encodeToString(lyricsCom.toString().getBytes(), Base64.NO_WRAP) + ")\n"; 176 | } 177 | return result; 178 | } 179 | 180 | /** 181 | * 获取动感歌词 182 | * 183 | * @param lyricsIfno 184 | * @return 185 | */ 186 | private String getDynamicContent(LyricsInfo lyricsIfno) { 187 | String result = ""; 188 | TreeMap lyricsLineInfoTreeMap = lyricsIfno.getLyricsLineInfoTreeMap(); 189 | if (lyricsLineInfoTreeMap != null && lyricsLineInfoTreeMap.size() > 0) { 190 | StringBuilder lyricsCom = new StringBuilder(); 191 | for (int i = 0; i < lyricsLineInfoTreeMap.size(); i++) { 192 | LyricsLineInfo lyricsLineInfo = lyricsLineInfoTreeMap.get(i); 193 | lyricsCom.append("[" + lyricsLineInfo.getStartTime() + "," + (lyricsLineInfo.getEndTime() - lyricsLineInfo.getStartTime()) + "]"); 194 | String[] lyricsWords = lyricsLineInfo.getLyricsWords(); 195 | int[] wordsDisInterval = lyricsLineInfo.getWordsDisInterval(); 196 | for (int j = 0; j < lyricsWords.length; j++) { 197 | lyricsCom.append("(0" + "," + wordsDisInterval[j] + ")" + lyricsWords[j]); 198 | } 199 | lyricsCom.append("\n"); 200 | } 201 | result += LEGAL_DLYRICS_LINE_PREFIX + "(" + Base64.encodeToString(lyricsCom.toString().getBytes(), Base64.NO_WRAP) + ")\n"; 202 | } 203 | return result; 204 | } 205 | 206 | @Override 207 | public boolean isFileSupported(String ext) { 208 | return ext.equalsIgnoreCase("lrcwy"); 209 | } 210 | 211 | @Override 212 | public String getSupportFileExt() { 213 | return "lrcwy"; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/trc/TrcLyricsFileReader.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats.trc; 2 | 3 | import com.zlm.hp.lyrics.formats.LyricsFileReader; 4 | import com.zlm.hp.lyrics.model.LyricsInfo; 5 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 6 | import com.zlm.hp.lyrics.model.LyricsTag; 7 | import com.zlm.hp.lyrics.utils.TimeUtils; 8 | 9 | import java.io.BufferedReader; 10 | import java.io.InputStream; 11 | import java.io.InputStreamReader; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | import java.util.TreeMap; 15 | import java.util.regex.Matcher; 16 | import java.util.regex.Pattern; 17 | 18 | /** 19 | * @Description: trc歌词 20 | * @author: zhangliangming 21 | * @date: 2020-02-26 19:53 22 | **/ 23 | public class TrcLyricsFileReader extends LyricsFileReader { 24 | 25 | /** 26 | * 歌曲名 字符串 27 | */ 28 | private final static String LEGAL_SONGNAME_PREFIX = "[ti:"; 29 | /** 30 | * 歌手名 字符串 31 | */ 32 | private final static String LEGAL_SINGERNAME_PREFIX = "[ar:"; 33 | /** 34 | * 时间补偿值 字符串 35 | */ 36 | private final static String LEGAL_OFFSET_PREFIX = "[offset:"; 37 | /** 38 | * 歌词上传者 39 | */ 40 | private final static String LEGAL_BY_PREFIX = "[by:"; 41 | 42 | /** 43 | * 专辑 44 | */ 45 | private final static String LEGAL_AL_PREFIX = "[al:"; 46 | 47 | private final static String LEGAL_TOTAL_PREFIX = "[total:"; 48 | 49 | @Override 50 | public LyricsInfo readLrcText(String dynamicContent, String lrcContent, String extraLrcContent, String lyricsFilePath) throws Exception { 51 | return null; 52 | } 53 | 54 | @Override 55 | public LyricsInfo readInputStream(InputStream in) throws Exception { 56 | LyricsInfo lyricsIfno = new LyricsInfo(); 57 | lyricsIfno.setLyricsFileExt(getSupportFileExt()); 58 | if (in != null) { 59 | BufferedReader br = new BufferedReader(new InputStreamReader(in, 60 | getDefaultCharset())); 61 | 62 | TreeMap lyricsLineInfos = new TreeMap(); 63 | Map lyricsTags = new HashMap(); 64 | int index = 0; 65 | String lineInfo = ""; 66 | while ((lineInfo = br.readLine()) != null) { 67 | 68 | // 行读取,并解析每行歌词的内容 69 | LyricsLineInfo lyricsLineInfo = parserLineInfos(lyricsTags, 70 | lineInfo); 71 | if (lyricsLineInfo != null) { 72 | lyricsLineInfos.put(index, lyricsLineInfo); 73 | index++; 74 | } 75 | } 76 | in.close(); 77 | in = null; 78 | // 设置歌词的标签类 79 | lyricsIfno.setLyricsTags(lyricsTags); 80 | // 81 | lyricsIfno.setLyricsLineInfoTreeMap(lyricsLineInfos); 82 | } 83 | return lyricsIfno; 84 | } 85 | 86 | /** 87 | * 解析每行的歌词内容 88 | *

89 | * 歌词列表 90 | * 91 | * @param lyricsTags 歌词标签 92 | * @param lineInfo 行歌词内容 93 | * @return 94 | */ 95 | private LyricsLineInfo parserLineInfos(Map lyricsTags, 96 | String lineInfo) throws Exception { 97 | LyricsLineInfo lyricsLineInfo = null; 98 | if (lineInfo.startsWith(LEGAL_SONGNAME_PREFIX)) { 99 | int startIndex = LEGAL_SONGNAME_PREFIX.length(); 100 | int endIndex = lineInfo.lastIndexOf("]"); 101 | // 102 | lyricsTags.put(LyricsTag.TAG_TITLE, 103 | lineInfo.substring(startIndex, endIndex)); 104 | } else if (lineInfo.startsWith(LEGAL_SINGERNAME_PREFIX)) { 105 | int startIndex = LEGAL_SINGERNAME_PREFIX.length(); 106 | int endIndex = lineInfo.lastIndexOf("]"); 107 | lyricsTags.put(LyricsTag.TAG_ARTIST, 108 | lineInfo.substring(startIndex, endIndex)); 109 | } else if (lineInfo.startsWith(LEGAL_OFFSET_PREFIX)) { 110 | int startIndex = LEGAL_OFFSET_PREFIX.length(); 111 | int endIndex = lineInfo.lastIndexOf("]"); 112 | lyricsTags.put(LyricsTag.TAG_OFFSET, 113 | lineInfo.substring(startIndex, endIndex)); 114 | } else if (lineInfo.startsWith(LEGAL_BY_PREFIX) 115 | || lineInfo.startsWith(LEGAL_TOTAL_PREFIX) 116 | || lineInfo.startsWith(LEGAL_AL_PREFIX)) { 117 | 118 | int startIndex = lineInfo.indexOf("[") + 1; 119 | int endIndex = lineInfo.lastIndexOf("]"); 120 | String temp[] = lineInfo.substring(startIndex, endIndex).split(":"); 121 | lyricsTags.put(temp[0], temp.length == 1 ? "" : temp[1]); 122 | 123 | } else { 124 | //时间标签 125 | String timeRegex = "\\[\\d+:\\d+.\\d+\\]"; 126 | String timeRegexs = "(" + timeRegex + ")+"; 127 | // 如果含有时间标签,则是歌词行 128 | Pattern pattern = Pattern.compile(timeRegexs); 129 | Matcher matcher = pattern.matcher(lineInfo); 130 | if (matcher.find()) { 131 | lyricsLineInfo = new LyricsLineInfo(); 132 | // [此行开始时刻距0时刻的毫秒数]<此字持续的毫秒数>歌<此字持续的毫秒数>词<此字持续的毫秒数>正<此字持续的毫秒数>文 133 | // 获取行的出现时间和结束时间 134 | int startIndex = matcher.start(); 135 | int endIndex = matcher.end(); 136 | int startTime = TimeUtils.parseInteger(lineInfo.substring(startIndex + 1, 137 | endIndex - 1)); 138 | lyricsLineInfo.setStartTime(startTime); 139 | // 获取歌词信息 140 | String lineContent = lineInfo.substring(endIndex, 141 | lineInfo.length()); 142 | 143 | // 歌词匹配的正则表达式 144 | String regex = "\\<\\d+\\>"; 145 | Pattern lyricsWordsPattern = Pattern.compile(regex); 146 | Matcher lyricsWordsMatcher = lyricsWordsPattern 147 | .matcher(lineContent); 148 | 149 | if (lyricsWordsMatcher == null) { 150 | return null; 151 | } 152 | 153 | // 歌词分隔 154 | String lineLyricsTemp[] = lineContent.split(regex); 155 | String[] lyricsWords = getLyricsWords(lineLyricsTemp); 156 | lyricsLineInfo.setLyricsWords(lyricsWords); 157 | 158 | // 获取每个歌词的时间 159 | int wordsDisInterval[] = new int[lyricsWords.length]; 160 | int index = 0; 161 | int endTime = startTime; 162 | while (lyricsWordsMatcher.find()) { 163 | 164 | //验证 165 | if (index >= wordsDisInterval.length) { 166 | throw new Exception("字标签个数与字时间标签个数不相符"); 167 | } 168 | 169 | // 170 | String wordsDisIntervalStr = lyricsWordsMatcher.group(); 171 | String wordsDisIntervalStrTemp = wordsDisIntervalStr 172 | .substring(wordsDisIntervalStr.indexOf('<') + 1, wordsDisIntervalStr.lastIndexOf('>')); 173 | 174 | int wordsTime = Integer 175 | .parseInt(wordsDisIntervalStrTemp); 176 | wordsDisInterval[index++] = wordsTime; 177 | 178 | endTime += wordsTime; 179 | } 180 | lyricsLineInfo.setWordsDisInterval(wordsDisInterval); 181 | lyricsLineInfo.setEndTime(endTime); 182 | // 获取当行歌词 183 | String lineLyrics = lyricsWordsMatcher.replaceAll(""); 184 | lyricsLineInfo.setLineLyrics(lineLyrics); 185 | } 186 | 187 | } 188 | return lyricsLineInfo; 189 | } 190 | 191 | /** 192 | * 分隔每个歌词 193 | * 194 | * @param lineLyricsTemp 195 | * @return 196 | */ 197 | private String[] getLyricsWords(String[] lineLyricsTemp) throws Exception { 198 | String temp[] = null; 199 | if (lineLyricsTemp.length < 2) { 200 | return new String[lineLyricsTemp.length]; 201 | } 202 | // 203 | temp = new String[lineLyricsTemp.length - 1]; 204 | for (int i = 1; i < lineLyricsTemp.length; i++) { 205 | temp[i - 1] = lineLyricsTemp[i]; 206 | } 207 | return temp; 208 | } 209 | 210 | @Override 211 | public boolean isFileSupported(String ext) { 212 | return ext.equalsIgnoreCase("trc"); 213 | } 214 | 215 | @Override 216 | public String getSupportFileExt() { 217 | return "trc"; 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/formats/trc/TrcLyricsFileWriter.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.formats.trc; 2 | 3 | import com.zlm.hp.lyrics.formats.LyricsFileWriter; 4 | import com.zlm.hp.lyrics.model.LyricsInfo; 5 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 6 | import com.zlm.hp.lyrics.model.LyricsTag; 7 | import com.zlm.hp.lyrics.utils.TimeUtils; 8 | 9 | import java.util.Map; 10 | import java.util.TreeMap; 11 | 12 | /** 13 | * @Description: trc歌词生成器 14 | * @author: zhangliangming 15 | * @date: 2020-02-26 20:35 16 | **/ 17 | 18 | public class TrcLyricsFileWriter extends LyricsFileWriter { 19 | 20 | /** 21 | * 歌曲名 字符串 22 | */ 23 | private final static String LEGAL_SONGNAME_PREFIX = "[ti:"; 24 | /** 25 | * 歌手名 字符串 26 | */ 27 | private final static String LEGAL_SINGERNAME_PREFIX = "[ar:"; 28 | /** 29 | * 时间补偿值 字符串 30 | */ 31 | private final static String LEGAL_OFFSET_PREFIX = "[offset:"; 32 | 33 | /** 34 | * 歌曲长度 35 | */ 36 | private final static String LEGAL_TOTAL_PREFIX = "[total:"; 37 | 38 | @Override 39 | public boolean writer(LyricsInfo lyricsIfno, String lyricsFilePath) throws Exception { 40 | String lyricsContent = getLyricsContent(lyricsIfno); 41 | return saveLyricsFile(lyricsContent, lyricsFilePath); 42 | } 43 | 44 | @Override 45 | public String getLyricsContent(LyricsInfo lyricsIfno) throws Exception { 46 | StringBuilder lyricsCom = new StringBuilder(); 47 | // 先保存所有的标签数据 48 | Map tags = lyricsIfno.getLyricsTags(); 49 | for (Map.Entry entry : tags.entrySet()) { 50 | Object val = entry.getValue(); 51 | if (entry.getKey().equals(LyricsTag.TAG_TITLE)) { 52 | lyricsCom.append(LEGAL_SONGNAME_PREFIX); 53 | } else if (entry.getKey().equals(LyricsTag.TAG_ARTIST)) { 54 | lyricsCom.append(LEGAL_SINGERNAME_PREFIX); 55 | } else if (entry.getKey().equals(LyricsTag.TAG_OFFSET)) { 56 | lyricsCom.append(LEGAL_OFFSET_PREFIX); 57 | } else if (entry.getKey().equals(LyricsTag.TAG_TOTAL)) { 58 | lyricsCom.append(LEGAL_TOTAL_PREFIX); 59 | } else { 60 | val = "[" + entry.getKey() + ":" + val; 61 | } 62 | lyricsCom.append(val + "]\n"); 63 | } 64 | 65 | // 每行歌词内容 66 | TreeMap lyricsLineInfos = lyricsIfno 67 | .getLyricsLineInfoTreeMap(); 68 | for (int i = 0; i < lyricsLineInfos.size(); i++) { 69 | LyricsLineInfo lyricsLineInfo = lyricsLineInfos.get(i); 70 | 71 | lyricsCom.append("[" 72 | + TimeUtils.parseMMSSFFString(lyricsLineInfo.getStartTime()) 73 | + "]");// 添加开始时间 74 | 75 | String[] lyricsWords = lyricsLineInfo.getLyricsWords(); 76 | // 添加每个歌词的时间 77 | StringBuilder wordsText = new StringBuilder(); 78 | int wordsDisInterval[] = lyricsLineInfo.getWordsDisInterval(); 79 | for (int j = 0; j < wordsDisInterval.length; j++) { 80 | wordsText.append("<" + wordsDisInterval[j] + ">" + lyricsWords[j]); 81 | } 82 | lyricsCom.append(wordsText); 83 | lyricsCom.append("\n"); 84 | } 85 | return lyricsCom.toString(); 86 | } 87 | 88 | @Override 89 | public boolean isFileSupported(String ext) { 90 | return ext.equalsIgnoreCase("trc"); 91 | } 92 | 93 | @Override 94 | public String getSupportFileExt() { 95 | return "trc"; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/model/LyricsInfo.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.model; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | import java.util.TreeMap; 7 | 8 | /** 9 | * 歌词数据 10 | * 11 | * @author zhangliangming 12 | */ 13 | public class LyricsInfo { 14 | /** 15 | * lrc类型 16 | */ 17 | public static final int LRC = 0; 18 | /** 19 | * 动感歌词类型 20 | */ 21 | public static final int DYNAMIC = 1; 22 | /** 23 | * 默认歌词类型是:动感歌词类型 24 | */ 25 | private int mLyricsType = DYNAMIC; 26 | 27 | /** 28 | * 歌词格式 29 | */ 30 | private String mLyricsFileExt; 31 | /** 32 | * 所有的歌词行数据 33 | */ 34 | private TreeMap mLyricsLineInfoTreeMap; 35 | /** 36 | * 翻译行歌词 37 | */ 38 | private List mTranslateLrcLineInfos; 39 | /** 40 | * 音译歌词行 41 | */ 42 | private List mTransliterationLrcLineInfos; 43 | /** 44 | * 歌词标签 45 | */ 46 | private Map mLyricsTags = new HashMap(); 47 | 48 | public Map getLyricsTags() { 49 | return mLyricsTags; 50 | } 51 | 52 | public void setLyricsTags(Map lyricsTags) { 53 | this.mLyricsTags = lyricsTags; 54 | } 55 | 56 | public List getTranslateLrcLineInfos() { 57 | return mTranslateLrcLineInfos; 58 | } 59 | 60 | public void setTranslateLrcLineInfos(List translateLrcLineInfos) { 61 | this.mTranslateLrcLineInfos = translateLrcLineInfos; 62 | } 63 | 64 | public List getTransliterationLrcLineInfos() { 65 | return mTransliterationLrcLineInfos; 66 | } 67 | 68 | public void setTransliterationLrcLineInfos(List transliterationLrcLineInfos) { 69 | this.mTransliterationLrcLineInfos = transliterationLrcLineInfos; 70 | } 71 | 72 | public String getLyricsFileExt() { 73 | return mLyricsFileExt; 74 | } 75 | 76 | public void setLyricsFileExt(String lyricsFileExt) { 77 | this.mLyricsFileExt = lyricsFileExt; 78 | } 79 | 80 | public TreeMap getLyricsLineInfoTreeMap() { 81 | return mLyricsLineInfoTreeMap; 82 | } 83 | 84 | public void setLyricsLineInfoTreeMap( 85 | TreeMap lyricsLineInfoTreeMap) { 86 | this.mLyricsLineInfoTreeMap = lyricsLineInfoTreeMap; 87 | } 88 | 89 | public void setTitle(String title) { 90 | 91 | if (mLyricsTags == null) { 92 | mLyricsTags = new HashMap(); 93 | } 94 | mLyricsTags.put(LyricsTag.TAG_TITLE, title); 95 | 96 | } 97 | 98 | public String getTitle() { 99 | 100 | String title = ""; 101 | if (mLyricsTags != null && !mLyricsTags.isEmpty() 102 | && mLyricsTags.containsKey(LyricsTag.TAG_TITLE)) { 103 | title = (String) mLyricsTags.get(LyricsTag.TAG_TITLE); 104 | } 105 | return title; 106 | 107 | } 108 | 109 | public void setArtist(String artist) { 110 | if (mLyricsTags == null) { 111 | mLyricsTags = new HashMap(); 112 | } 113 | mLyricsTags.put(LyricsTag.TAG_ARTIST, artist); 114 | } 115 | 116 | public String getArtist() { 117 | 118 | String artist = ""; 119 | if (mLyricsTags != null && !mLyricsTags.isEmpty() 120 | && mLyricsTags.containsKey(LyricsTag.TAG_ARTIST)) { 121 | artist = (String) mLyricsTags.get(LyricsTag.TAG_ARTIST); 122 | } 123 | return artist; 124 | 125 | } 126 | 127 | public void setOffset(long offset) { 128 | if (mLyricsTags == null) { 129 | mLyricsTags = new HashMap(); 130 | } 131 | mLyricsTags.put(LyricsTag.TAG_OFFSET, offset); 132 | } 133 | 134 | public long getOffset() { 135 | 136 | long offset = 0; 137 | if (mLyricsTags != null && !mLyricsTags.isEmpty() 138 | && mLyricsTags.containsKey(LyricsTag.TAG_OFFSET)) { 139 | try { 140 | offset = Long.parseLong((String) mLyricsTags 141 | .get(LyricsTag.TAG_OFFSET)); 142 | } catch (Exception e) { 143 | e.printStackTrace(); 144 | } 145 | } 146 | return offset; 147 | 148 | } 149 | 150 | public void setBy(String by) { 151 | if (mLyricsTags == null) { 152 | mLyricsTags = new HashMap(); 153 | } 154 | mLyricsTags.put(LyricsTag.TAG_BY, by); 155 | } 156 | 157 | public String getBy() { 158 | 159 | String by = ""; 160 | if (mLyricsTags != null && !mLyricsTags.isEmpty() 161 | && mLyricsTags.containsKey(LyricsTag.TAG_BY)) { 162 | by = (String) mLyricsTags.get(LyricsTag.TAG_BY); 163 | } 164 | return by; 165 | 166 | } 167 | 168 | public void setTotal(long total) { 169 | if (mLyricsTags == null) { 170 | mLyricsTags = new HashMap(); 171 | } 172 | mLyricsTags.put(LyricsTag.TAG_TOTAL, total); 173 | } 174 | 175 | public long getTotal() { 176 | 177 | long total = 0; 178 | if (mLyricsTags != null && !mLyricsTags.isEmpty() 179 | && mLyricsTags.containsKey(LyricsTag.TAG_TOTAL)) { 180 | try { 181 | total = Long.parseLong((String) mLyricsTags 182 | .get(LyricsTag.TAG_TOTAL)); 183 | } catch (Exception e) { 184 | e.printStackTrace(); 185 | } 186 | } 187 | return total; 188 | 189 | } 190 | 191 | public void setLyricsType(int mLyricsType) { 192 | this.mLyricsType = mLyricsType; 193 | } 194 | 195 | public int getLyricsType() { 196 | return mLyricsType; 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/model/LyricsLineInfo.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.model; 2 | 3 | import android.text.TextUtils; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 歌词实体类 9 | * 10 | * @author zhangliangming 11 | */ 12 | public class LyricsLineInfo { 13 | 14 | /** 15 | * 歌词开始时间 16 | */ 17 | private int mStartTime; 18 | /** 19 | * 歌词结束时间 20 | */ 21 | private int mEndTime; 22 | /** 23 | * 该行歌词 24 | */ 25 | private String mLineLyrics = ""; 26 | 27 | /** 28 | * 歌词数组,用来分隔每个歌词 29 | */ 30 | private String[] mLyricsWords; 31 | /** 32 | * 数组,用来存放每个歌词的时间 33 | */ 34 | private int[] mWordsDisInterval; 35 | 36 | /** 37 | * 分割歌词行歌词 38 | */ 39 | private List mSplitDynamicLrcLineInfos; 40 | 41 | public List getSplitLyricsLineInfos() { 42 | return mSplitDynamicLrcLineInfos; 43 | } 44 | 45 | public void setSplitLyricsLineInfos( 46 | List splitDynamicLrcLineInfos) { 47 | this.mSplitDynamicLrcLineInfos = splitDynamicLrcLineInfos; 48 | } 49 | 50 | public String[] getLyricsWords() { 51 | return mLyricsWords; 52 | } 53 | 54 | public void setLyricsWords(String[] lyricsWords) { 55 | if (lyricsWords == null) return; 56 | String[] tempArray = new String[lyricsWords.length]; 57 | for (int i = 0; i < lyricsWords.length; i++) { 58 | String temp = lyricsWords[i]; 59 | if (TextUtils.isEmpty(temp)) { 60 | tempArray[i] = ""; 61 | } else { 62 | tempArray[i] = temp.replaceAll("\r|\n", ""); 63 | } 64 | } 65 | this.mLyricsWords = tempArray; 66 | } 67 | 68 | public int[] getWordsDisInterval() { 69 | return mWordsDisInterval; 70 | } 71 | 72 | public void setWordsDisInterval(int[] wordsDisInterval) { 73 | this.mWordsDisInterval = wordsDisInterval; 74 | } 75 | 76 | public int getStartTime() { 77 | return mStartTime; 78 | } 79 | 80 | public void setStartTime(int mStartTime) { 81 | this.mStartTime = mStartTime; 82 | } 83 | 84 | public int getEndTime() { 85 | return mEndTime; 86 | } 87 | 88 | public void setEndTime(int mEndTime) { 89 | this.mEndTime = mEndTime; 90 | } 91 | 92 | public String getLineLyrics() { 93 | return mLineLyrics; 94 | } 95 | 96 | public void setLineLyrics(String mLineLyrics) { 97 | if (!TextUtils.isEmpty(mLineLyrics)) { 98 | this.mLineLyrics = mLineLyrics.replaceAll("\r|\n", ""); 99 | } 100 | } 101 | 102 | /** 103 | * 复制 104 | * 105 | * @param dist 要复制的实体类 106 | * @param orig 原始实体类 107 | */ 108 | public void copy(LyricsLineInfo dist, LyricsLineInfo orig) { 109 | if (orig.getWordsDisInterval() != null) { 110 | dist.setWordsDisInterval(orig.getWordsDisInterval()); 111 | } 112 | dist.setStartTime(orig.getStartTime()); 113 | dist.setEndTime(orig.getEndTime()); 114 | 115 | if (orig.getLyricsWords() != null) { 116 | dist.setLyricsWords(orig.getLyricsWords()); 117 | } 118 | 119 | dist.setLineLyrics(orig.getLineLyrics()); 120 | 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/model/LyricsTag.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.model; 2 | 3 | /** 4 | * 歌词标签 5 | * 6 | * @author zhangliangming 7 | * 8 | */ 9 | public class LyricsTag { 10 | /** 11 | * 歌曲名称 12 | */ 13 | public static String TAG_TITLE = "lyrics.tag.title"; 14 | /** 15 | * 歌手 16 | */ 17 | public static String TAG_ARTIST = "lyrics.tag.artist"; 18 | /** 19 | * 时间补偿值 20 | */ 21 | public static String TAG_OFFSET = "lyrics.tag.offset"; 22 | /** 23 | * 上传者 24 | */ 25 | public static String TAG_BY = "lyrics.tag.by"; 26 | /** 27 | * 歌词总时长 28 | */ 29 | public static String TAG_TOTAL = "lyrics.tag.total"; 30 | } 31 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/model/TranslateLrcLineInfo.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.model; 2 | 3 | import android.text.TextUtils; 4 | 5 | /** 6 | * 翻译行歌词 7 | * Created by zhangliangming on 2017/9/11. 8 | */ 9 | 10 | public class TranslateLrcLineInfo { 11 | 12 | /** 13 | * 该行歌词 14 | */ 15 | private String mLineLyrics = ""; 16 | 17 | public String getLineLyrics() { 18 | return mLineLyrics; 19 | } 20 | 21 | public void setLineLyrics(String lineLyrics) { 22 | if (!TextUtils.isEmpty(lineLyrics)) 23 | this.mLineLyrics = lineLyrics.replaceAll("\r|\n", ""); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/model/make/MakeExtraLrcLineInfo.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.model.make; 2 | 3 | 4 | /** 5 | * 制作额外歌词行数据 6 | * Created by zhangliangming on 2018-03-29. 7 | */ 8 | 9 | public class MakeExtraLrcLineInfo extends MakeLrcInfo { 10 | /** 11 | * 该行额外歌词 12 | */ 13 | private String mExtraLineLyrics; 14 | 15 | public String getExtraLineLyrics() { 16 | return mExtraLineLyrics; 17 | } 18 | 19 | public void setExtraLineLyrics(String mExtraLineLyrics) { 20 | this.mExtraLineLyrics = mExtraLineLyrics; 21 | } 22 | 23 | @Override 24 | public void reset() { 25 | super.reset(); 26 | mExtraLineLyrics = ""; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/model/make/MakeLrcInfo.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.model.make; 2 | 3 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 4 | 5 | /** 6 | * 制作歌词信息 7 | * Created by zhangliangming on 2018-04-01. 8 | */ 9 | 10 | public class MakeLrcInfo { 11 | 12 | /** 13 | * 初始 14 | */ 15 | public static final int STATUS_NONE = 0; 16 | 17 | /** 18 | * 完成 19 | */ 20 | public static final int STATUS_FINISH = 1; 21 | /** 22 | * 状态 23 | */ 24 | private int status = STATUS_NONE; 25 | 26 | /** 27 | * 歌词索引,-1是未读,-2是已经完成 28 | */ 29 | private int lrcIndex = -1; 30 | /** 31 | * 行歌词 32 | */ 33 | private LyricsLineInfo lyricsLineInfo; 34 | 35 | 36 | /** 37 | * 重置 38 | */ 39 | public void reset() { 40 | status = STATUS_NONE; 41 | lrcIndex = -1; 42 | } 43 | 44 | public int getStatus() { 45 | return status; 46 | } 47 | 48 | public void setStatus(int status) { 49 | if (this.status != STATUS_FINISH) { 50 | this.status = status; 51 | } 52 | } 53 | 54 | public int getLrcIndex() { 55 | return lrcIndex; 56 | } 57 | 58 | public void setLrcIndex(int lrcIndex) { 59 | this.lrcIndex = lrcIndex; 60 | } 61 | 62 | public LyricsLineInfo getLyricsLineInfo() { 63 | return lyricsLineInfo; 64 | } 65 | 66 | public void setLyricsLineInfo(LyricsLineInfo lyricsLineInfo) { 67 | this.lyricsLineInfo = lyricsLineInfo; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/model/make/MakeLrcLineInfo.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.model.make; 2 | 3 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 4 | 5 | import java.util.TreeMap; 6 | 7 | /** 8 | * 制作歌词行数据 9 | * Created by zhangliangming on 2018-03-29. 10 | */ 11 | 12 | public class MakeLrcLineInfo extends MakeLrcInfo { 13 | 14 | /** 15 | * 每个字时间集合 16 | */ 17 | private TreeMap mWordDisIntervals = new TreeMap(); 18 | 19 | private byte[] lock = new byte[0]; 20 | 21 | /** 22 | * 设置当前歌曲索引 23 | * 24 | * @param curPlayingTime 25 | */ 26 | public boolean play(long curPlayingTime) { 27 | synchronized (lock) { 28 | 29 | int lrcIndex = getLrcIndex(); 30 | lrcIndex++; 31 | setLrcIndex(lrcIndex); 32 | int preLrcIndex = lrcIndex - 1; 33 | if (mWordDisIntervals.containsKey(preLrcIndex)) { 34 | //设置前一个字的结束时间 35 | WordDisInterval wordDisInterval = mWordDisIntervals.get(preLrcIndex); 36 | wordDisInterval.setEndTime((int) curPlayingTime); 37 | mWordDisIntervals.put(preLrcIndex, wordDisInterval); 38 | } 39 | 40 | LyricsLineInfo lyricsLineInfo = getLyricsLineInfo(); 41 | //判断是否完成 42 | if (lrcIndex == lyricsLineInfo.getLyricsWords().length) { 43 | 44 | lrcIndex = lyricsLineInfo.getLyricsWords().length - 1; 45 | 46 | //设置结束时间 47 | WordDisInterval wordDisInterval = mWordDisIntervals.get(lrcIndex); 48 | lyricsLineInfo.setEndTime(wordDisInterval.getEndTime()); 49 | 50 | setStatus(STATUS_FINISH); 51 | setLrcIndex(lrcIndex); 52 | 53 | return true; 54 | } 55 | 56 | //设置当前字的开始时间 57 | WordDisInterval wordDisInterval = new WordDisInterval(); 58 | wordDisInterval.setStartTime((int) curPlayingTime); 59 | mWordDisIntervals.put(lrcIndex, wordDisInterval); 60 | 61 | return false; 62 | } 63 | } 64 | 65 | 66 | /** 67 | * 行设置 68 | * 69 | * @param preLineEndTime 上一行结束时间 70 | * @param curPlayingTime 当前播放时间 71 | */ 72 | public void playLine(long preLineEndTime, long curPlayingTime) { 73 | synchronized (lock) { 74 | mWordDisIntervals.clear(); 75 | LyricsLineInfo lyricsLineInfo = getLyricsLineInfo(); 76 | lyricsLineInfo.setStartTime((int) preLineEndTime); 77 | lyricsLineInfo.setEndTime((int) curPlayingTime); 78 | long dTime = 0; 79 | if (preLineEndTime < curPlayingTime) { 80 | dTime = curPlayingTime - preLineEndTime; 81 | } 82 | String[] lyricsWords = lyricsLineInfo.getLyricsWords(); 83 | if (lyricsWords != null && lyricsWords.length > 0) { 84 | long aveTime = dTime / lyricsWords.length; 85 | for (int i = 0; i < lyricsWords.length; i++) { 86 | //设置当前字的开始时间/结束时间 87 | long startTime = lyricsLineInfo.getStartTime() + i * aveTime; 88 | WordDisInterval wordDisInterval = new WordDisInterval(); 89 | wordDisInterval.setStartTime((int) startTime); 90 | wordDisInterval.setEndTime((int) (startTime + aveTime)); 91 | mWordDisIntervals.put(i, wordDisInterval); 92 | } 93 | setStatus(STATUS_FINISH); 94 | setLrcIndex(lyricsWords.length - 1); 95 | } 96 | } 97 | } 98 | 99 | /** 100 | * 设置回滚 101 | */ 102 | public void back() { 103 | synchronized (lock) { 104 | 105 | int lrcIndex = getLrcIndex(); 106 | lrcIndex--; 107 | if (lrcIndex < -1) { 108 | lrcIndex = -1; 109 | } 110 | setLrcIndex(lrcIndex); 111 | //后退时,删除当前的歌词字时间 112 | int nextLrcIndex = lrcIndex + 1; 113 | if (mWordDisIntervals.containsKey(nextLrcIndex)) { 114 | mWordDisIntervals.remove(nextLrcIndex); 115 | } 116 | // 117 | if (mWordDisIntervals.containsKey(lrcIndex)) { 118 | WordDisInterval wordDisInterval = mWordDisIntervals.get(lrcIndex); 119 | wordDisInterval.setEndTime(0); 120 | mWordDisIntervals.put(lrcIndex, wordDisInterval); 121 | } 122 | } 123 | } 124 | 125 | @Override 126 | public void reset() { 127 | synchronized (lock) { 128 | super.reset(); 129 | mWordDisIntervals.clear(); 130 | } 131 | } 132 | 133 | /** 134 | * 获取该行完成后的歌词数据 135 | * 136 | * @return 137 | */ 138 | public LyricsLineInfo getFinishLrcLineInfo() { 139 | synchronized (lock) { 140 | if (getStatus() == STATUS_FINISH) { 141 | int startTime = 0; 142 | int endTime = 0; 143 | int[] wDisIntervals = new int[mWordDisIntervals.size()]; 144 | for (int j = 0; j < mWordDisIntervals.size(); j++) { 145 | WordDisInterval wordDisInterval = mWordDisIntervals.get(j); 146 | if (j == 0) { 147 | startTime = wordDisInterval.getStartTime(); 148 | } 149 | if (j == mWordDisIntervals.size() - 1) { 150 | endTime = wordDisInterval.getEndTime(); 151 | 152 | } 153 | int time = wordDisInterval.getEndTime() 154 | - wordDisInterval.getStartTime(); 155 | wDisIntervals[j] = time; 156 | } 157 | LyricsLineInfo lyricsLineInfo = getLyricsLineInfo(); 158 | lyricsLineInfo.setStartTime(startTime); 159 | lyricsLineInfo.setEndTime(endTime); 160 | lyricsLineInfo.setWordsDisInterval(wDisIntervals); 161 | return lyricsLineInfo; 162 | } 163 | return null; 164 | } 165 | } 166 | 167 | 168 | /** 169 | * 单个歌词字时间实体类 170 | * 171 | * @author zhangliangming 172 | */ 173 | class WordDisInterval { 174 | /** 175 | * 开始时间 176 | */ 177 | int startTime; 178 | /** 179 | * 结束时间 180 | */ 181 | int endTime; 182 | 183 | public int getStartTime() { 184 | return startTime; 185 | } 186 | 187 | public void setStartTime(int startTime) { 188 | this.startTime = startTime; 189 | } 190 | 191 | public int getEndTime() { 192 | return endTime; 193 | } 194 | 195 | public void setEndTime(int endTime) { 196 | this.endTime = endTime; 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/utils/CharUtils.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.utils; 2 | 3 | /** 4 | * 5 | * @author zhangliangming 6 | * 7 | */ 8 | public class CharUtils { 9 | /** 10 | * 判断字符是不是中文,中文字符标点都可以判断 11 | * 12 | * @param c 13 | * 字符 14 | * @return 15 | */ 16 | public static boolean isChinese(char c) { 17 | Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); 18 | if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS 19 | || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS 20 | || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A 21 | || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION 22 | || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION 23 | || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) { 24 | return true; 25 | } 26 | return false; 27 | } 28 | 29 | /** 30 | * 是否是日语平假名 31 | * 32 | * @param c 33 | * @return 34 | */ 35 | public static boolean isHiragana(char c) { 36 | Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); 37 | if (ub == Character.UnicodeBlock.HIRAGANA) { 38 | return true; 39 | } 40 | return false; 41 | } 42 | 43 | /** 44 | * 是否是韩语 45 | * 46 | * @return 47 | */ 48 | public static boolean isHangulSyllables(char c) { 49 | Character.UnicodeBlock ub = Character.UnicodeBlock.of(c); 50 | if (ub == Character.UnicodeBlock.HANGUL_SYLLABLES) { 51 | return true; 52 | } 53 | return false; 54 | } 55 | 56 | /** 57 | * 判断该歌词是不是字母 58 | * 59 | * @param c 60 | * @return 61 | */ 62 | public static boolean isWord(char c) { 63 | if (('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')) { 64 | return true; 65 | } 66 | return false; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/utils/ColorUtils.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.utils; 2 | 3 | import android.graphics.Color; 4 | 5 | /** 6 | * 颜色处理类 7 | * 8 | * @author zhangliangming 9 | */ 10 | public class ColorUtils { 11 | /** 12 | * 解析颜色 13 | * 14 | * @param colorStr #ffffff 颜色字符串 15 | * @param alpha 0-255 透明度 16 | * @return 17 | */ 18 | public static int parserColor(String colorStr, int alpha) { 19 | int color = Color.parseColor(colorStr); 20 | int red = (color & 0xff0000) >> 16; 21 | int green = (color & 0x00ff00) >> 8; 22 | int blue = (color & 0x0000ff); 23 | return Color.argb(alpha, red, green, blue); 24 | } 25 | 26 | /** 27 | * 解析颜色 28 | * 29 | * @param color Color.WHITE 30 | * @param alpha 0-255 透明度 31 | * @return 32 | */ 33 | public static int parserColor(int color, int alpha) { 34 | int red = (color & 0xff0000) >> 16; 35 | int green = (color & 0x00ff00) >> 8; 36 | int blue = (color & 0x0000ff); 37 | return Color.argb(alpha, red, green, blue); 38 | } 39 | 40 | public static int parserColor(int color) { 41 | int red = (color & 0xff0000) >> 16; 42 | int green = (color & 0x00ff00) >> 8; 43 | int blue = (color & 0x0000ff); 44 | return Color.argb(255, red, green, blue); 45 | } 46 | 47 | 48 | /** 49 | * 解析颜色 50 | * 51 | * @param colorStr #ffffff 颜色字符串 52 | * @return 53 | */ 54 | public static int parserColor(String colorStr) { 55 | return Color.parseColor(colorStr); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/utils/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.utils; 2 | 3 | import java.io.File; 4 | import java.text.DecimalFormat; 5 | 6 | /** 7 | * 8 | * @author zhangliangming 9 | * 10 | */ 11 | public class FileUtils { 12 | public static String getFileExt(File file) { 13 | return getFileExt(file.getName()); 14 | } 15 | 16 | public static String removeExt(String s) { 17 | int index = s.lastIndexOf("."); 18 | if (index == -1) 19 | index = s.length(); 20 | return s.substring(0, index); 21 | } 22 | 23 | public static String getFileExt(String fileName) { 24 | int pos = fileName.lastIndexOf("."); 25 | if (pos == -1) 26 | return ""; 27 | return fileName.substring(pos + 1).toLowerCase(); 28 | } 29 | 30 | /** 31 | * 计算文件的大小,返回相关的m字符串 32 | * 33 | * @param fileS 34 | * @return 35 | */ 36 | public static String getFileSize(long fileS) {// 转换文件大小 37 | DecimalFormat df = new DecimalFormat("#.00"); 38 | String fileSizeString = ""; 39 | if (fileS < 1024) { 40 | fileSizeString = df.format((double) fileS) + "B"; 41 | } else if (fileS < 1048576) { 42 | fileSizeString = df.format((double) fileS / 1024) + "K"; 43 | } else if (fileS < 1073741824) { 44 | fileSizeString = df.format((double) fileS / 1048576) + "M"; 45 | } else { 46 | fileSizeString = df.format((double) fileS / 1073741824) + "G"; 47 | } 48 | return fileSizeString; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/utils/LyricsIOUtils.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.utils; 2 | 3 | 4 | import com.zlm.hp.lyrics.formats.LyricsFileReader; 5 | import com.zlm.hp.lyrics.formats.LyricsFileWriter; 6 | import com.zlm.hp.lyrics.formats.hrc.HrcLyricsFileReader; 7 | import com.zlm.hp.lyrics.formats.hrc.HrcLyricsFileWriter; 8 | import com.zlm.hp.lyrics.formats.krc.KrcLyricsFileReader; 9 | import com.zlm.hp.lyrics.formats.krc.KrcLyricsFileWriter; 10 | import com.zlm.hp.lyrics.formats.ksc.KscLyricsFileReader; 11 | import com.zlm.hp.lyrics.formats.ksc.KscLyricsFileWriter; 12 | import com.zlm.hp.lyrics.formats.lrc.LrcLyricsFileReader; 13 | import com.zlm.hp.lyrics.formats.lrc.LrcLyricsFileWriter; 14 | import com.zlm.hp.lyrics.formats.lrcwy.WYLyricsFileReader; 15 | import com.zlm.hp.lyrics.formats.lrcwy.WYLyricsFileWriter; 16 | import com.zlm.hp.lyrics.formats.trc.TrcLyricsFileReader; 17 | import com.zlm.hp.lyrics.formats.trc.TrcLyricsFileWriter; 18 | 19 | import java.io.File; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | /** 24 | * 歌词io操作 25 | * @author zhangliangming 26 | * 27 | */ 28 | public class LyricsIOUtils { 29 | private static ArrayList readers; 30 | private static ArrayList writers; 31 | 32 | static { 33 | readers = new ArrayList(); 34 | readers.add(new HrcLyricsFileReader()); 35 | readers.add(new KscLyricsFileReader()); 36 | readers.add(new KrcLyricsFileReader()); 37 | readers.add(new TrcLyricsFileReader()); 38 | readers.add(new WYLyricsFileReader()); 39 | readers.add(new LrcLyricsFileReader()); 40 | // 41 | writers = new ArrayList(); 42 | writers.add(new HrcLyricsFileWriter()); 43 | writers.add(new KscLyricsFileWriter()); 44 | writers.add(new KrcLyricsFileWriter()); 45 | writers.add(new TrcLyricsFileWriter()); 46 | writers.add(new WYLyricsFileWriter()); 47 | writers.add(new LrcLyricsFileWriter()); 48 | } 49 | 50 | /** 51 | * 获取支持的歌词文件格式 52 | * 53 | * @return 54 | */ 55 | public static List getSupportLyricsExts() { 56 | List lrcExts = new ArrayList(); 57 | for (LyricsFileReader lyricsFileReader : readers) { 58 | lrcExts.add(lyricsFileReader.getSupportFileExt()); 59 | } 60 | return lrcExts; 61 | } 62 | 63 | /** 64 | * 获取歌词文件读取器 65 | * 66 | * @param file 67 | * @return 68 | */ 69 | public static LyricsFileReader getLyricsFileReader(File file) { 70 | return getLyricsFileReader(file.getName()); 71 | } 72 | 73 | /** 74 | * 获取歌词文件读取器 75 | * 76 | * @param fileName 77 | * @return 78 | */ 79 | public static LyricsFileReader getLyricsFileReader(String fileName) { 80 | String ext = FileUtils.getFileExt(fileName); 81 | for (LyricsFileReader lyricsFileReader : readers) { 82 | if (lyricsFileReader.isFileSupported(ext)) { 83 | return lyricsFileReader; 84 | } 85 | } 86 | return null; 87 | } 88 | 89 | /** 90 | * 获取歌词保存器 91 | * 92 | * @param file 93 | * @return 94 | */ 95 | public static LyricsFileWriter getLyricsFileWriter(File file) { 96 | return getLyricsFileWriter(file.getName()); 97 | } 98 | 99 | /** 100 | * 获取歌词保存器 101 | * 102 | * @param fileName 103 | * @return 104 | */ 105 | public static LyricsFileWriter getLyricsFileWriter(String fileName) { 106 | String ext = FileUtils.getFileExt(fileName); 107 | for (LyricsFileWriter lyricsFileWriter : writers) { 108 | if (lyricsFileWriter.isFileSupported(ext)) { 109 | return lyricsFileWriter; 110 | } 111 | } 112 | return null; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/utils/StringCompressUtils.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.utils; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.ByteArrayOutputStream; 5 | import java.io.InputStream; 6 | import java.io.OutputStream; 7 | import java.nio.charset.Charset; 8 | import java.util.zip.DeflaterOutputStream; 9 | import java.util.zip.InflaterInputStream; 10 | 11 | /** 12 | * 字符串解压和压缩 13 | * 14 | * @author zhangliangming 15 | */ 16 | public class StringCompressUtils { 17 | 18 | /** 19 | * 压缩 20 | * 21 | * @param text 22 | * @param charset 23 | * @return 24 | */ 25 | public static byte[] compress(String text, Charset charset) throws Exception { 26 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 27 | 28 | OutputStream out = new DeflaterOutputStream(baos); 29 | out.write(text.getBytes(charset)); 30 | out.close(); 31 | 32 | return baos.toByteArray(); 33 | } 34 | 35 | /** 36 | * @param input 37 | * @param charset 38 | * @return 39 | * @throws Exception 40 | */ 41 | public static String decompress(InputStream input, Charset charset) 42 | throws Exception { 43 | return decompress(toByteArray(input), charset); 44 | } 45 | 46 | /** 47 | * 解压 48 | * 49 | * @param bytes 50 | * @param charset 51 | * @return 52 | */ 53 | public static String decompress(byte[] bytes, Charset charset) throws Exception { 54 | InputStream in = new InflaterInputStream( 55 | new ByteArrayInputStream(bytes)); 56 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); 57 | 58 | byte[] buffer = new byte[8192]; 59 | int len; 60 | while ((len = in.read(buffer)) > 0) 61 | baos.write(buffer, 0, len); 62 | return new String(baos.toByteArray(), charset); 63 | 64 | } 65 | 66 | /** 67 | * @param input 68 | * @return 69 | */ 70 | private static byte[] toByteArray(InputStream input) throws Exception { 71 | ByteArrayOutputStream output = new ByteArrayOutputStream(); 72 | copy(input, output); 73 | return output.toByteArray(); 74 | } 75 | 76 | private static int copy(InputStream input, OutputStream output) 77 | throws Exception { 78 | long count = copyLarge(input, output); 79 | if (count > 2147483647L) { 80 | return -1; 81 | } 82 | return (int) count; 83 | } 84 | 85 | private static long copyLarge(InputStream input, OutputStream output) 86 | throws Exception { 87 | byte[] buffer = new byte[4096]; 88 | long count = 0L; 89 | int n = 0; 90 | while (-1 != (n = input.read(buffer))) { 91 | output.write(buffer, 0, n); 92 | count += n; 93 | } 94 | return count; 95 | } 96 | } -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/utils/StringUtils.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.utils; 2 | 3 | /** 4 | * Created by zhangliangming on 2018-02-23. 5 | */ 6 | 7 | public class StringUtils { 8 | 9 | 10 | /** 11 | *

12 | * Checks if a String is whitespace, empty ("") or null. 13 | *

14 | * 15 | *
 16 |      * StringUtils.isBlank(null)      = true
 17 |      * StringUtils.isBlank("")        = true
 18 |      * StringUtils.isBlank(" ")       = true
 19 |      * StringUtils.isBlank("bob")     = false
 20 |      * StringUtils.isBlank("  bob  ") = false
 21 |      * 
22 | * 23 | * @param str 24 | * the String to check, may be null 25 | * @return true if the String is null, empty or whitespace 26 | * @since 2.0 27 | */ 28 | public static boolean isBlank(String str) { 29 | int strLen; 30 | if (str == null || (strLen = str.length()) == 0) { 31 | return true; 32 | } 33 | for (int i = 0; i < strLen; i++) { 34 | if ((Character.isWhitespace(str.charAt(i)) == false)) { 35 | return false; 36 | } 37 | } 38 | return true; 39 | } 40 | 41 | /** 42 | *

43 | * Checks if a String is not empty (""), not null and not whitespace only. 44 | *

45 | * 46 | *
 47 |      * StringUtils.isNotBlank(null)      = false
 48 |      * StringUtils.isNotBlank("")        = false
 49 |      * StringUtils.isNotBlank(" ")       = false
 50 |      * StringUtils.isNotBlank("bob")     = true
 51 |      * StringUtils.isNotBlank("  bob  ") = true
 52 |      * 
53 | * 54 | * @param str 55 | * the String to check, may be null 56 | * @return true if the String is not empty and not null and not 57 | * whitespace 58 | * @since 2.0 59 | */ 60 | public static boolean isNotBlank(String str) { 61 | int strLen; 62 | if (str == null || (strLen = str.length()) == 0) { 63 | return false; 64 | } 65 | for (int i = 0; i < strLen; i++) { 66 | if ((Character.isWhitespace(str.charAt(i)) == false)) { 67 | return true; 68 | } 69 | } 70 | return false; 71 | } 72 | 73 | /** 74 | *

75 | * Checks if the String contains only unicode digits. A decimal point is not 76 | * a unicode digit and returns false. 77 | *

78 | * 79 | *

80 | * null will return false. An empty String ("") 81 | * will return true. 82 | *

83 | * 84 | *
 85 |      * StringUtils.isNumeric(null)   = false
 86 |      * StringUtils.isNumeric("")     = true
 87 |      * StringUtils.isNumeric("  ")   = false
 88 |      * StringUtils.isNumeric("123")  = true
 89 |      * StringUtils.isNumeric("12 3") = false
 90 |      * StringUtils.isNumeric("ab2c") = false
 91 |      * StringUtils.isNumeric("12-3") = false
 92 |      * StringUtils.isNumeric("12.3") = false
 93 |      * 
94 | * 95 | * @param str 96 | * the String to check, may be null 97 | * @return true if only contains digits, and is non-null 98 | */ 99 | public static boolean isNumeric(String str) { 100 | if (str == null) { 101 | return false; 102 | } 103 | int sz = str.length(); 104 | for (int i = 0; i < sz; i++) { 105 | if (Character.isDigit(str.charAt(i)) == false) { 106 | return false; 107 | } 108 | } 109 | return true; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/utils/TimeUtils.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.utils; 2 | 3 | /** 4 | * @author zhangliangming 5 | */ 6 | public class TimeUtils { 7 | /** 8 | * @param timeString 时间字符串 00:00.00/00:00.000 9 | * @return 10 | * @功能 将时间字符串转换成整数 11 | */ 12 | public static int parseInteger(String timeString) { 13 | timeString = timeString.replace(":", "."); 14 | timeString = timeString.replace(".", "@"); 15 | String timedata[] = timeString.split("@"); 16 | if (timedata.length == 3) { 17 | int m = Integer.parseInt(timedata[0]); // 分 18 | int s = Integer.parseInt(timedata[1]); // 秒 19 | int ms = 0; 20 | //部分lrc歌词,只精确到10倍毫秒 21 | if (timedata[2].length() == 3) { 22 | ms = Integer.parseInt(timedata[2]); // 毫秒 23 | } else { 24 | ms = Integer.parseInt(timedata[2]) * 10; // 毫秒 25 | } 26 | 27 | int currTime = (m * 60 + s) * 1000 + ms; 28 | return currTime; 29 | } else if (timedata.length == 2) { 30 | int m = Integer.parseInt(timedata[0]); // 分 31 | int s = Integer.parseInt(timedata[1]); // 秒 32 | int currTime = (m * 60 + s) * 1000; 33 | return currTime; 34 | } 35 | return 0; 36 | } 37 | 38 | /** 39 | * 毫秒转时间字符串 40 | * 41 | * @param msecTotal 42 | * @return 00:00.000 43 | */ 44 | public static String parseMMSSFFFString(int msecTotal) { 45 | int msec = msecTotal % 1000; 46 | msecTotal /= 1000; 47 | int minute = msecTotal / 60; 48 | int second = msecTotal % 60; 49 | minute %= 60; 50 | return String.format("%02d:%02d.%03d", minute, second, msec); 51 | } 52 | 53 | /** 54 | * 毫秒转时间字符串 55 | * 56 | * @param msecTotal 57 | * @return 00:00.00 58 | */ 59 | public static String parseMMSSFFString(int msecTotal) { 60 | int msec = msecTotal % 1000 / 10; 61 | msecTotal /= 1000; 62 | int minute = msecTotal / 60; 63 | int second = msecTotal % 60; 64 | minute %= 60; 65 | return String.format("%02d:%02d.%02d", minute, second, msec); 66 | } 67 | 68 | /** 69 | * 毫秒转时间字符串 70 | * 71 | * @param msecTotal 72 | * @return 00:00 73 | */ 74 | public static String parseMMSSString(int msecTotal) { 75 | msecTotal /= 1000; 76 | int minute = msecTotal / 60; 77 | int second = msecTotal % 60; 78 | minute %= 60; 79 | return String.format("%02d:%02d", minute, second); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/utils/UnicodeInputStream.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.utils; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * This inputstream will recognize unicode BOM marks and will skip bytes if 7 | * getEncoding() method is called before any of the read(...) methods. 8 | * 9 | * Usage pattern: String enc = "ISO-8859-1"; // or NULL to use systemdefault 10 | * FileInputStream fis = new FileInputStream(file); UnicodeInputStream uin = new 11 | * UnicodeInputStream(fis, enc); enc = uin.getEncoding(); // check and skip 12 | * possible BOM bytes InputStreamReader in; if (enc == null) in = new 13 | * InputStreamReader(uin); else in = new InputStreamReader(uin, enc); 14 | */ 15 | public class UnicodeInputStream extends InputStream { 16 | PushbackInputStream internalIn; 17 | boolean isInited = false; 18 | String defaultEnc; 19 | String encoding; 20 | 21 | private static final int BOM_SIZE = 4; 22 | 23 | public UnicodeInputStream(InputStream in, String defaultEnc) { 24 | internalIn = new PushbackInputStream(in, BOM_SIZE); 25 | this.defaultEnc = defaultEnc; 26 | } 27 | 28 | public String getDefaultEncoding() { 29 | return defaultEnc; 30 | } 31 | 32 | public String getEncoding() { 33 | if (!isInited) { 34 | try { 35 | init(); 36 | } catch (IOException ex) { 37 | IllegalStateException ise = new IllegalStateException( 38 | "Init method failed."); 39 | ise.initCause(ise); 40 | throw ise; 41 | } 42 | } 43 | return encoding; 44 | } 45 | 46 | /** 47 | * Read-ahead four bytes and check for BOM marks. Extra bytes are unread 48 | * back to the stream, only BOM bytes are skipped. 49 | */ 50 | protected void init() throws IOException { 51 | if (isInited) 52 | return; 53 | 54 | byte bom[] = new byte[BOM_SIZE]; 55 | int n, unread; 56 | n = internalIn.read(bom, 0, bom.length); 57 | 58 | if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) 59 | && (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) { 60 | encoding = "UTF-32BE"; 61 | unread = n - 4; 62 | } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) 63 | && (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) { 64 | encoding = "UTF-32LE"; 65 | unread = n - 4; 66 | } else if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) 67 | && (bom[2] == (byte) 0xBF)) { 68 | encoding = "UTF-8"; 69 | unread = n - 3; 70 | } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) { 71 | encoding = "UTF-16BE"; 72 | unread = n - 2; 73 | } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) { 74 | encoding = "UTF-16LE"; 75 | unread = n - 2; 76 | } else { 77 | // Unicode BOM mark not found, unread all bytes 78 | encoding = defaultEnc; 79 | unread = n; 80 | } 81 | // System.out.println("read=" + n + ", unread=" + unread); 82 | 83 | if (unread > 0) 84 | internalIn.unread(bom, (n - unread), unread); 85 | 86 | isInited = true; 87 | } 88 | 89 | public void close() throws IOException { 90 | // init(); 91 | isInited = true; 92 | internalIn.close(); 93 | } 94 | 95 | public int read() throws IOException { 96 | // init(); 97 | isInited = true; 98 | return internalIn.read(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/widget/FloatLyricsView.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.widget; 2 | 3 | 4 | import android.content.Context; 5 | import android.graphics.Canvas; 6 | import android.graphics.Paint; 7 | import android.graphics.Typeface; 8 | import android.util.AttributeSet; 9 | import android.util.DisplayMetrics; 10 | import android.view.Display; 11 | import android.view.WindowManager; 12 | 13 | import com.zlm.hp.lyrics.LyricsReader; 14 | import com.zlm.hp.lyrics.model.LyricsInfo; 15 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 16 | import com.zlm.hp.lyrics.utils.LyricsUtils; 17 | 18 | import java.util.List; 19 | import java.util.TreeMap; 20 | 21 | /** 22 | * @Description: 双行歌词,支持翻译(该歌词在这里只以动感歌词的形式显示)和音译歌词(注:不支持lrc歌词的显示) 23 | * @author: zhangliangming 24 | * @date: 2018-04-21 11:43 25 | **/ 26 | public class FloatLyricsView extends AbstractLrcView { 27 | 28 | /** 29 | * 歌词居左 30 | */ 31 | public static final int ORIENTATION_LEFT = 0; 32 | /** 33 | * 歌词居中 34 | */ 35 | public static final int ORIENTATION_CENTER = 1; 36 | /** 37 | * 38 | */ 39 | private int mOrientation = ORIENTATION_LEFT; 40 | 41 | public FloatLyricsView(Context context) { 42 | super(context); 43 | init(context); 44 | } 45 | 46 | public FloatLyricsView(Context context, AttributeSet attrs) { 47 | super(context, attrs); 48 | init(context); 49 | } 50 | 51 | /** 52 | * @throws 53 | * @Description: 初始 54 | * @param: 55 | * @return: 56 | * @author: zhangliangming 57 | * @date: 2018-04-21 9:08 58 | */ 59 | private void init(Context context) { 60 | 61 | //获取屏幕宽度 62 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 63 | Display display = wm.getDefaultDisplay(); 64 | DisplayMetrics displayMetrics = new DisplayMetrics(); 65 | display.getMetrics(displayMetrics); 66 | int screensWidth = displayMetrics.widthPixels; 67 | 68 | //设置歌词的最大宽度 69 | int textMaxWidth = screensWidth / 3 * 2; 70 | setTextMaxWidth(textMaxWidth); 71 | 72 | } 73 | 74 | @Override 75 | protected void onDrawLrcView(Canvas canvas) { 76 | drawFloatLrcView(canvas); 77 | } 78 | 79 | @Override 80 | protected void updateView(long playProgress) { 81 | updateFloatLrcView(playProgress); 82 | } 83 | 84 | 85 | /** 86 | * 绘画歌词 87 | * 88 | * @param canvas 89 | */ 90 | private void drawFloatLrcView(Canvas canvas) { 91 | int extraLrcStatus = getExtraLrcStatus(); 92 | //绘画歌词 93 | if (extraLrcStatus == AbstractLrcView.EXTRALRCSTATUS_NOSHOWEXTRALRC) { 94 | LyricsReader lyricsReader = getLyricsReader(); 95 | if (lyricsReader.getLyricsType() == LyricsInfo.LRC) { 96 | //绘画lrc歌词 97 | drawLyrics(canvas); 98 | } else { 99 | //只显示默认歌词 100 | drawDynamicLyrics(canvas); 101 | } 102 | } else { 103 | //显示翻译歌词 OR 音译歌词 104 | drawDynamiAndExtraLyrics(canvas); 105 | } 106 | 107 | } 108 | 109 | /** 110 | * 绘画lrc歌词 111 | * 112 | * @param canvas 113 | */ 114 | private void drawLyrics(Canvas canvas) { 115 | //获取数据 116 | TreeMap lrcLineInfos = getLrcLineInfos(); 117 | int lyricsLineNum = getLyricsLineNum(); 118 | float lyricsWordHLTime = getLyricsWordHLTime(); 119 | Paint paint = getPaint(); 120 | Paint paintHL = getPaintHL(); 121 | Paint paintOutline = getPaintOutline(); 122 | int[] paintColors = getPaintColors(); 123 | int[] paintHLColors = getPaintHLColors(); 124 | float spaceLineHeight = getSpaceLineHeight(); 125 | float paddingLeftOrRight = getPaddingLeftOrRight(); 126 | 127 | LyricsLineInfo lyricsLineInfo = lrcLineInfos.get(lyricsLineNum); 128 | // 当行歌词 129 | String curLyrics = lyricsLineInfo.getLineLyrics(); 130 | float curLrcTextWidth = LyricsUtils.getTextWidth(paint, curLyrics); 131 | // 当前歌词行的x坐标 132 | float textX = 0; 133 | // 当前歌词行的y坐标 134 | float textY = 0; 135 | 136 | float lineLyricsHLWidth = LyricsUtils.getLrcHLWidth(paint, lrcLineInfos, lyricsLineInfo, lyricsLineNum, lyricsWordHLTime); 137 | float topPadding = (getHeight() - spaceLineHeight - 2 * LyricsUtils.getTextHeight(paint)) / 2; 138 | if (lyricsLineNum % 2 == 0) { 139 | textY = topPadding + LyricsUtils.getTextHeight(paint); 140 | 141 | if (mOrientation == ORIENTATION_LEFT || curLrcTextWidth > getWidth()) { 142 | textX = LyricsUtils.getFristLrcMoveTextX(curLrcTextWidth, lineLyricsHLWidth, getWidth(), paddingLeftOrRight); 143 | } else { 144 | textX = LyricsUtils.getHLMoveTextX(curLrcTextWidth, lineLyricsHLWidth, getWidth(), paddingLeftOrRight); 145 | } 146 | 147 | 148 | if (lyricsLineNum + 1 < lrcLineInfos.size()) { 149 | float nextLrcTextY = textY + spaceLineHeight + LyricsUtils.getTextHeight(paint); 150 | 151 | String lrcRightText = lrcLineInfos.get( 152 | lyricsLineNum + 1).getLineLyrics(); 153 | float lrcRightTextWidth = LyricsUtils.getTextWidth(paint, lrcRightText); 154 | float textRightX = 0; 155 | 156 | if (mOrientation == ORIENTATION_LEFT || lrcRightTextWidth > getWidth()) { 157 | textRightX = getWidth() - lrcRightTextWidth - paddingLeftOrRight; 158 | } else { 159 | textRightX = (getWidth() - lrcRightTextWidth) / 2; 160 | } 161 | 162 | 163 | LyricsUtils.drawOutline(canvas, paintOutline, lrcRightText, textRightX, nextLrcTextY); 164 | LyricsUtils.drawText(canvas, paint, paintColors, lrcRightText, textRightX, 165 | nextLrcTextY); 166 | } 167 | 168 | } else { 169 | 170 | float preLrcTextY = topPadding + LyricsUtils.getTextHeight(paint); 171 | textY = preLrcTextY + spaceLineHeight + LyricsUtils.getTextHeight(paint); 172 | 173 | 174 | if (mOrientation == ORIENTATION_LEFT || curLrcTextWidth > getWidth()) { 175 | textX = LyricsUtils.getSecondLrcMoveTextX(curLrcTextWidth, lineLyricsHLWidth, getWidth(), paddingLeftOrRight); 176 | } else { 177 | textX = (getWidth() - curLrcTextWidth) / 2; 178 | } 179 | 180 | 181 | 182 | if (lyricsLineNum + 1 < lrcLineInfos.size()) { 183 | String lrcLeftText = lrcLineInfos.get( 184 | lyricsLineNum + 1).getLineLyrics(); 185 | float textLeftX = 0; 186 | float lrcLeftTextWidth = LyricsUtils.getTextWidth(paint, lrcLeftText); 187 | if (mOrientation == ORIENTATION_LEFT || lrcLeftTextWidth > getWidth()) { 188 | textLeftX = paddingLeftOrRight; 189 | } else { 190 | textLeftX = (getWidth() - lrcLeftTextWidth) / 2; 191 | } 192 | 193 | LyricsUtils.drawOutline(canvas, paintOutline, lrcLeftText, textLeftX, 194 | preLrcTextY); 195 | LyricsUtils.drawText(canvas, paint, paintColors, lrcLeftText, textLeftX, 196 | preLrcTextY); 197 | } 198 | } 199 | 200 | //画歌词 201 | LyricsUtils.drawOutline(canvas, paintOutline, curLyrics, textX, textY); 202 | LyricsUtils.drawText(canvas, paintHL, paintHLColors, curLyrics, textX, textY); 203 | } 204 | 205 | /** 206 | * 绘画歌词 207 | * 208 | * @param canvas 209 | */ 210 | private void drawDynamicLyrics(Canvas canvas) { 211 | //获取数据 212 | LyricsReader lyricsReader = getLyricsReader(); 213 | TreeMap lrcLineInfos = getLrcLineInfos(); 214 | int lyricsLineNum = getLyricsLineNum(); 215 | int splitLyricsLineNum = getSplitLyricsLineNum(); 216 | int splitLyricsWordIndex = getSplitLyricsWordIndex(); 217 | float lyricsWordHLTime = getLyricsWordHLTime(); 218 | Paint paint = getPaint(); 219 | Paint paintHL = getPaintHL(); 220 | Paint paintOutline = getPaintOutline(); 221 | int[] paintColors = getPaintColors(); 222 | int[] paintHLColors = getPaintHLColors(); 223 | float spaceLineHeight = getSpaceLineHeight(); 224 | float paddingLeftOrRight = getPaddingLeftOrRight(); 225 | 226 | 227 | // 先设置当前歌词,之后再根据索引判断是否放在左边还是右边 228 | List splitLyricsLineInfos = lrcLineInfos.get(lyricsLineNum).getSplitLyricsLineInfos(); 229 | LyricsLineInfo lyricsLineInfo = splitLyricsLineInfos.get(splitLyricsLineNum); 230 | //获取行歌词高亮宽度 231 | float lineLyricsHLWidth = LyricsUtils.getLineLyricsHLWidth(lyricsReader.getLyricsType(), paint, lyricsLineInfo, splitLyricsWordIndex, lyricsWordHLTime); 232 | // 当行歌词 233 | String curLyrics = lyricsLineInfo.getLineLyrics(); 234 | float curLrcTextWidth = LyricsUtils.getTextWidth(paint, curLyrics); 235 | // 当前歌词行的x坐标 236 | float textX = 0; 237 | // 当前歌词行的y坐标 238 | float textY = 0; 239 | int splitLyricsRealLineNum = LyricsUtils.getSplitLyricsRealLineNum(lrcLineInfos, lyricsLineNum, splitLyricsLineNum); 240 | float topPadding = (getHeight() - spaceLineHeight - 2 * LyricsUtils.getTextHeight(paint)) / 2; 241 | if (splitLyricsRealLineNum % 2 == 0) { 242 | if (mOrientation == ORIENTATION_LEFT || curLrcTextWidth > getWidth()) { 243 | textX = paddingLeftOrRight; 244 | } else { 245 | textX = (getWidth() - curLrcTextWidth) / 2; 246 | } 247 | textY = topPadding + LyricsUtils.getTextHeight(paint); 248 | float nextLrcTextY = textY + spaceLineHeight + LyricsUtils.getTextHeight(paint); 249 | 250 | // 画下一句的歌词,该下一句还在该行的分割集合里面 251 | if (splitLyricsLineNum + 1 < splitLyricsLineInfos.size()) { 252 | String lrcRightText = splitLyricsLineInfos.get( 253 | splitLyricsLineNum + 1).getLineLyrics(); 254 | float lrcRightTextWidth = LyricsUtils.getTextWidth(paint, lrcRightText); 255 | float textRightX = 0; 256 | 257 | if (mOrientation == ORIENTATION_LEFT || lrcRightTextWidth > getWidth()) { 258 | textRightX = getWidth() - lrcRightTextWidth - paddingLeftOrRight; 259 | } else { 260 | textRightX = (getWidth() - lrcRightTextWidth) / 2; 261 | } 262 | 263 | LyricsUtils.drawOutline(canvas, paintOutline, lrcRightText, textRightX, nextLrcTextY); 264 | 265 | LyricsUtils.drawText(canvas, paint, paintColors, lrcRightText, textRightX, 266 | nextLrcTextY); 267 | 268 | } else if (lyricsLineNum + 1 < lrcLineInfos.size()) { 269 | // 画下一句的歌词,该下一句不在该行分割歌词里面,需要从原始下一行的歌词里面找 270 | List nextSplitLyricsLineInfos = lrcLineInfos.get(lyricsLineNum + 1).getSplitLyricsLineInfos(); 271 | String lrcRightText = nextSplitLyricsLineInfos.get(0).getLineLyrics(); 272 | float lrcRightTextWidth = LyricsUtils.getTextWidth(paint, lrcRightText); 273 | float textRightX = 0; 274 | 275 | if (mOrientation == ORIENTATION_LEFT || lrcRightTextWidth > getWidth()) { 276 | textRightX = getWidth() - lrcRightTextWidth - paddingLeftOrRight; 277 | } else { 278 | textRightX = (getWidth() - lrcRightTextWidth) / 2; 279 | } 280 | 281 | LyricsUtils.drawOutline(canvas, paintOutline, lrcRightText, textRightX, 282 | nextLrcTextY); 283 | 284 | LyricsUtils.drawText(canvas, paint, paintColors, lrcRightText, textRightX, nextLrcTextY); 285 | } 286 | 287 | } else { 288 | if (mOrientation == ORIENTATION_LEFT || curLrcTextWidth > getWidth()) { 289 | textX = getWidth() - curLrcTextWidth - paddingLeftOrRight; 290 | } else { 291 | textX = (getWidth() - curLrcTextWidth) / 2; 292 | } 293 | float preLrcTextY = topPadding + LyricsUtils.getTextHeight(paint); 294 | textY = preLrcTextY + spaceLineHeight + LyricsUtils.getTextHeight(paint); 295 | 296 | // 画下一句的歌词,该下一句还在该行的分割集合里面 297 | if (splitLyricsLineNum + 1 < splitLyricsLineInfos.size()) { 298 | String lrcLeftText = splitLyricsLineInfos.get( 299 | splitLyricsLineNum + 1).getLineLyrics(); 300 | float lrcLeftTextWidth = LyricsUtils.getTextWidth(paint, lrcLeftText); 301 | 302 | float textLeftX = 0; 303 | if (mOrientation == ORIENTATION_LEFT || lrcLeftTextWidth > getWidth()) { 304 | textLeftX = paddingLeftOrRight; 305 | } else { 306 | textLeftX = (getWidth() - lrcLeftTextWidth) / 2; 307 | } 308 | 309 | LyricsUtils.drawOutline(canvas, paintOutline, lrcLeftText, textLeftX, 310 | preLrcTextY); 311 | LyricsUtils.drawText(canvas, paint, paintColors, lrcLeftText, textLeftX, 312 | preLrcTextY); 313 | 314 | } else if (lyricsLineNum + 1 < lrcLineInfos.size()) { 315 | // 画下一句的歌词,该下一句不在该行分割歌词里面,需要从原始下一行的歌词里面找 316 | List nextSplitLyricsLineInfos = lrcLineInfos.get(lyricsLineNum + 1).getSplitLyricsLineInfos(); 317 | String lrcLeftText = nextSplitLyricsLineInfos.get(0).getLineLyrics(); 318 | float lrcLeftTextWidth = LyricsUtils.getTextWidth(paint, lrcLeftText); 319 | 320 | float textLeftX = 0; 321 | if (mOrientation == ORIENTATION_LEFT || lrcLeftTextWidth > getWidth()) { 322 | textLeftX = paddingLeftOrRight; 323 | } else { 324 | textLeftX = (getWidth() - lrcLeftTextWidth) / 2; 325 | } 326 | 327 | LyricsUtils.drawOutline(canvas, paintOutline, lrcLeftText, textLeftX, 328 | preLrcTextY); 329 | LyricsUtils.drawText(canvas, paint, paintColors, lrcLeftText, textLeftX, 330 | preLrcTextY); 331 | } 332 | } 333 | //画歌词 334 | LyricsUtils.drawOutline(canvas, paintOutline, curLyrics, textX, textY); 335 | LyricsUtils.drawDynamicText(canvas, paint, paintHL, paintColors, paintHLColors, curLyrics, lineLyricsHLWidth, textX, textY); 336 | } 337 | 338 | 339 | /** 340 | * 绘画歌词和额外歌词 341 | * 342 | * @param canvas 343 | */ 344 | private void drawDynamiAndExtraLyrics(Canvas canvas) { 345 | //获取数据 346 | LyricsReader lyricsReader = getLyricsReader(); 347 | Paint paint = getPaint(); 348 | Paint paintHL = getPaintHL(); 349 | Paint paintOutline = getPaintOutline(); 350 | Paint extraLrcPaint = getExtraLrcPaint(); 351 | Paint extraLrcPaintHL = getExtraLrcPaintHL(); 352 | Paint extraLrcPaintOutline = getExtraLrcPaintOutline(); 353 | int[] paintColors = getPaintColors(); 354 | int[] paintHLColors = getPaintHLColors(); 355 | int extraLrcStatus = getExtraLrcStatus(); 356 | TreeMap lrcLineInfos = getLrcLineInfos(); 357 | int lyricsLineNum = getLyricsLineNum(); 358 | int lyricsWordIndex = getLyricsWordIndex(); 359 | int extraLyricsWordIndex = getExtraLyricsWordIndex(); 360 | float lyricsWordHLTime = getLyricsWordHLTime(); 361 | float translateLyricsWordHLTime = getTranslateLyricsWordHLTime(); 362 | float extraLrcSpaceLineHeight = getExtraLrcSpaceLineHeight(); 363 | float paddingLeftOrRight = getPaddingLeftOrRight(); 364 | int translateDrawType = getTranslateDrawType(); 365 | List translateLrcLineInfos = getTranslateLrcLineInfos(); 366 | List transliterationLrcLineInfos = getTransliterationLrcLineInfos(); 367 | 368 | // 369 | float topPadding = (getHeight() - extraLrcSpaceLineHeight - LyricsUtils.getTextHeight(paint) - LyricsUtils.getTextHeight(extraLrcPaint)) / 2; 370 | // 当前歌词行的y坐标 371 | float lrcTextY = topPadding + LyricsUtils.getTextHeight(paint); 372 | //额外歌词行的y坐标 373 | float extraLrcTextY = lrcTextY + extraLrcSpaceLineHeight + LyricsUtils.getTextHeight(extraLrcPaint); 374 | 375 | LyricsLineInfo lyricsLineInfo = lrcLineInfos.get(lyricsLineNum); 376 | //获取行歌词高亮宽度 377 | float lineLyricsHLWidth = LyricsUtils.getLineLyricsHLWidth(lyricsReader.getLyricsType(), paint, lyricsLineInfo, lyricsWordIndex, lyricsWordHLTime); 378 | //画默认歌词 379 | LyricsUtils.drawDynamiLyrics(canvas, lyricsReader.getLyricsType(), paint, paintHL, paintOutline, lyricsLineInfo, lineLyricsHLWidth, getWidth(), lyricsWordIndex, lyricsWordHLTime, lrcTextY, paddingLeftOrRight, paintColors, paintHLColors); 380 | 381 | //显示翻译歌词 382 | if (lyricsReader.getLyricsType() == LyricsInfo.DYNAMIC && extraLrcStatus == AbstractLrcView.EXTRALRCSTATUS_SHOWTRANSLATELRC && translateDrawType == AbstractLrcView.TRANSLATE_DRAW_TYPE_DYNAMIC) { 383 | 384 | LyricsLineInfo translateLyricsLineInfo = translateLrcLineInfos.get(lyricsLineNum); 385 | float extraLyricsLineHLWidth = LyricsUtils.getLineLyricsHLWidth(lyricsReader.getLyricsType(), extraLrcPaint, translateLyricsLineInfo, extraLyricsWordIndex, translateLyricsWordHLTime); 386 | //画翻译歌词 387 | LyricsUtils.drawDynamiLyrics(canvas, lyricsReader.getLyricsType(), extraLrcPaint, extraLrcPaintHL, extraLrcPaintOutline, translateLyricsLineInfo, extraLyricsLineHLWidth, getWidth(), extraLyricsWordIndex, translateLyricsWordHLTime, extraLrcTextY, paddingLeftOrRight, paintColors, paintHLColors); 388 | 389 | } else { 390 | LyricsLineInfo transliterationLineInfo = transliterationLrcLineInfos.get(lyricsLineNum); 391 | float extraLyricsLineHLWidth = LyricsUtils.getLineLyricsHLWidth(lyricsReader.getLyricsType(), extraLrcPaint, transliterationLineInfo, extraLyricsWordIndex, lyricsWordHLTime); 392 | //画音译歌词 393 | LyricsUtils.drawDynamiLyrics(canvas, lyricsReader.getLyricsType(), extraLrcPaint, extraLrcPaintHL, extraLrcPaintOutline, transliterationLineInfo, extraLyricsLineHLWidth, getWidth(), extraLyricsWordIndex, lyricsWordHLTime, extraLrcTextY, paddingLeftOrRight, paintColors, paintHLColors); 394 | 395 | } 396 | 397 | } 398 | 399 | 400 | /** 401 | * 更新歌词视图 402 | * 403 | * @param playProgress 404 | */ 405 | private void updateFloatLrcView(long playProgress) { 406 | 407 | LyricsReader lyricsReader = getLyricsReader(); 408 | TreeMap lrcLineInfos = getLrcLineInfos(); 409 | int lyricsLineNum = LyricsUtils.getLineNumber(lyricsReader.getLyricsType(), lrcLineInfos, playProgress, lyricsReader.getPlayOffset()); 410 | setLyricsLineNum(lyricsLineNum); 411 | if (lyricsReader.getLyricsType() == LyricsInfo.DYNAMIC) { 412 | updateSplitData(playProgress); 413 | } else { 414 | long lyricsWordHLTime = LyricsUtils.getLrcLenTime(lrcLineInfos, lyricsLineNum, playProgress, lyricsReader.getPlayOffset()); 415 | setLyricsWordHLTime(lyricsWordHLTime); 416 | } 417 | } 418 | 419 | 420 | /** 421 | * 设置默认颜色 422 | * 423 | * @param paintColor 424 | */ 425 | public void setPaintColor(int[] paintColor) { 426 | setPaintColor(paintColor, false); 427 | } 428 | 429 | 430 | /** 431 | * 设置高亮颜色 432 | * 433 | * @param paintHLColor 434 | */ 435 | public void setPaintHLColor(int[] paintHLColor) { 436 | setPaintHLColor(paintHLColor, false); 437 | } 438 | 439 | 440 | /** 441 | * 设置字体文件 442 | * 443 | * @param typeFace 444 | */ 445 | public void setTypeFace(Typeface typeFace) { 446 | setTypeFace(typeFace, false); 447 | } 448 | 449 | /** 450 | * 设置空行高度 451 | * 452 | * @param spaceLineHeight 453 | */ 454 | public void setSpaceLineHeight(float spaceLineHeight) { 455 | setSpaceLineHeight(spaceLineHeight, false); 456 | } 457 | 458 | 459 | /** 460 | * 设置额外空行高度 461 | * 462 | * @param extraLrcSpaceLineHeight 463 | */ 464 | public void setExtraLrcSpaceLineHeight(float extraLrcSpaceLineHeight) { 465 | setExtraLrcSpaceLineHeight(extraLrcSpaceLineHeight, false); 466 | } 467 | 468 | 469 | /** 470 | * 设置歌词解析器 471 | * 472 | * @param lyricsReader 473 | */ 474 | public void setLyricsReader(LyricsReader lyricsReader) { 475 | super.setLyricsReader(lyricsReader); 476 | if (lyricsReader != null) { 477 | if (lyricsReader.getLyricsType() == LyricsInfo.DYNAMIC) { 478 | int extraLrcType = getExtraLrcType(); 479 | //翻译歌词以动感歌词形式显示 480 | if (extraLrcType == AbstractLrcView.EXTRALRCTYPE_BOTH || extraLrcType == AbstractLrcView.EXTRALRCTYPE_TRANSLATELRC) { 481 | setTranslateDrawType(AbstractLrcView.TRANSLATE_DRAW_TYPE_DYNAMIC); 482 | } 483 | } 484 | } else { 485 | setLrcStatus(AbstractLrcView.LRCSTATUS_NONSUPPORT); 486 | } 487 | } 488 | 489 | /** 490 | * 设置字体大小 491 | * 492 | * @param fontSize 493 | * @param extraFontSize 额外歌词字体 494 | */ 495 | public void setSize(int fontSize, int extraFontSize) { 496 | setSize(fontSize, extraFontSize, false); 497 | } 498 | 499 | /** 500 | * 设置字体大小 501 | * 502 | * @param fontSize 503 | */ 504 | public void setFontSize(float fontSize) { 505 | setFontSize(fontSize, false); 506 | } 507 | 508 | 509 | /** 510 | * 设置额外字体大小 511 | * 512 | * @param extraLrcFontSize 513 | */ 514 | public void setExtraLrcFontSize(float extraLrcFontSize) { 515 | setExtraLrcFontSize(extraLrcFontSize, false); 516 | } 517 | 518 | public void setOrientation(int orientation) { 519 | this.mOrientation = orientation; 520 | } 521 | 522 | } 523 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/java/com/zlm/hp/lyrics/widget/make/MakeLrcPreView.java: -------------------------------------------------------------------------------- 1 | package com.zlm.hp.lyrics.widget.make; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Paint; 7 | import android.util.AttributeSet; 8 | import android.view.View; 9 | 10 | import com.zlm.hp.lyrics.model.LyricsLineInfo; 11 | import com.zlm.hp.lyrics.model.make.MakeLrcInfo; 12 | import com.zlm.hp.lyrics.utils.LyricsUtils; 13 | 14 | /** 15 | * 歌词制作预览视图 16 | * Created by zhangliangming on 2018-03-25. 17 | */ 18 | 19 | public class MakeLrcPreView extends View { 20 | /** 21 | * 默认歌词画笔 22 | */ 23 | private Paint mPaint; 24 | /** 25 | * 默认颜色 26 | */ 27 | private int mPaintColor = Color.BLACK; 28 | /** 29 | * 高亮歌词画笔 30 | */ 31 | private Paint mPaintHL; 32 | /** 33 | * 高亮画笔颜色 34 | */ 35 | private int mPaintHLColor = Color.BLUE; 36 | 37 | /** 38 | * 歌词字体大小 39 | */ 40 | private float mFontSize = 35; 41 | /** 42 | * 左右间隔距离 43 | */ 44 | private float mPaddingLeftOrRight = 15; 45 | /** 46 | * 制作歌词 47 | */ 48 | private MakeLrcInfo mMakeLrcInfo; 49 | 50 | public MakeLrcPreView(Context context) { 51 | super(context); 52 | init(context); 53 | } 54 | 55 | public MakeLrcPreView(Context context, AttributeSet attrs) { 56 | super(context, attrs); 57 | init(context); 58 | } 59 | 60 | /** 61 | * @param context 62 | */ 63 | protected void init(Context context) { 64 | //默认画笔 65 | mPaint = new Paint(); 66 | mPaint.setDither(true); 67 | mPaint.setAntiAlias(true); 68 | mPaint.setTextSize(mFontSize); 69 | setPaintColor(mPaintColor); 70 | 71 | //高亮画笔 72 | mPaintHL = new Paint(); 73 | mPaintHL.setDither(true); 74 | mPaintHL.setAntiAlias(true); 75 | mPaintHL.setTextSize(mFontSize); 76 | setPaintHLColor(mPaintHLColor); 77 | 78 | } 79 | 80 | 81 | @Override 82 | protected void onDraw(Canvas canvas) { 83 | if (mMakeLrcInfo == null) return; 84 | LyricsLineInfo lyricsLineInfo = mMakeLrcInfo.getLyricsLineInfo(); 85 | if (lyricsLineInfo == null) return; 86 | int viewHeight = getHeight(); 87 | int viewWidth = getWidth(); 88 | int textHeight = LyricsUtils.getTextHeight(mPaint); 89 | float textY = (viewHeight + textHeight) / 2; 90 | 91 | int lrcIndex = mMakeLrcInfo.getLrcIndex(); 92 | int status = mMakeLrcInfo.getStatus(); 93 | 94 | //歌词字集合 95 | String[] lyricsWords = lyricsLineInfo.getLyricsWords(); 96 | String lineLyrics = lyricsLineInfo.getLineLyrics(); 97 | float textWidth = LyricsUtils.getTextWidth(mPaint, lineLyrics); 98 | float textHLWidth = 0; 99 | //计算高亮宽度 100 | if (lrcIndex == -1) { 101 | //未读 102 | textHLWidth = 0; 103 | } else if (lrcIndex == -2) { 104 | textHLWidth = textWidth; 105 | } else { 106 | if (lrcIndex < lyricsWords.length) { 107 | StringBuilder temp = new StringBuilder(); 108 | for (int i = 0; i <= lrcIndex; i++) { 109 | temp.append(lyricsWords[i]); 110 | } 111 | textHLWidth = LyricsUtils.getTextWidth(mPaint, temp.toString()); 112 | 113 | } else { 114 | textHLWidth = textWidth; 115 | } 116 | } 117 | //画歌词 118 | float textX = LyricsUtils.getHLMoveTextX(textWidth, textHLWidth, viewWidth, mPaddingLeftOrRight); 119 | LyricsUtils.drawDynamicText(canvas, mPaint, mPaintHL, lineLyrics, textHLWidth, textX, textY); 120 | 121 | } 122 | 123 | public void setPaintColor(int mPaintColor) { 124 | this.mPaintColor = mPaintColor; 125 | mPaint.setColor(mPaintColor); 126 | } 127 | 128 | public void setPaintHLColor(int mPaintHLColor) { 129 | this.mPaintHLColor = mPaintHLColor; 130 | mPaintHL.setColor(mPaintHLColor); 131 | } 132 | 133 | public void setFontSize(float mFontSize) { 134 | this.mFontSize = mFontSize; 135 | mPaint.setTextSize(mFontSize); 136 | mPaintHL.setTextSize(mFontSize); 137 | } 138 | 139 | public void setMakeLrcInfo(MakeLrcInfo mMakeLrcInfo) { 140 | this.mMakeLrcInfo = mMakeLrcInfo; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /hplyricslibrary/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 |  2 | 乐乐歌词库 3 | 乐乐音乐 就是任性 4 | 正在获取歌词... 5 | 搜索歌词 6 | 加载歌词出错 7 | 不支持该格式歌词文件显示 8 | 9 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 简介 # 2 | 该开源依赖库是乐乐音乐播放器里的一个歌词模块功能,现在把该功能模块独立出来进行优化,并弄成了一个开源依赖库,其它音乐播放器项目只要引用该库并调用接口,便可轻松实现与乐乐音乐播放器一样的动感歌词显示效果,注:其默认歌词格式的编码都是utf-8,使用过程中请注意编码一致的问题,其项目地址如下:[乐乐音乐播放器](https://github.com/zhangliangming/HappyPlayer5.git)。 3 | 4 | 5 | # 使用注意 # 6 | 7 | - 1.x版本,只要是使用自定义view来实现,每次都使用handler去刷新view,但是如果handler队列中有很多任务执行,那就无法保证歌词每次都在100ms内刷新一次。 8 | - 2.x版本,主要是使用surfaceview来实现,每次刷新时间为40ms,歌词渐变相对会流畅。 9 | - 3.x版本,主要是使用TextureView来实现,每次刷新时间修改为50ms,TextureView支持view的相关动画属性 10 | 11 | # 2.x版本使用注意 # 12 | - 主题:我主要是使用Theme.AppCompat.Light.NoActionBar的主题,我试过其它的主题,会导致surfaceview背景为黑色,并且不能透明的问题。 13 | - surfaceview存在的问题,没有view相关的旋转,位移等动画和touch事件,所以我乐乐音乐的旋转界面会出现问题,如果有相关动画需求的,慎用。 14 | 15 | # 3.x版本使用注意 # 16 | - 设置硬件加速:android:hardwareAccelerated="true" 17 | - android4.0以上 18 | - 存在的问题,没有touch事件 19 | 20 | # 网易云API歌词调用方式 # 21 | 22 | ![](https://i.imgur.com/LIGQRmJ.png) 23 | 注:该歌词只适用于通过api获取歌词,文件保存格式为:lrcwy。其中动感歌词和lrc歌词只能选其中一种,支持翻译歌词, 24 | 25 | # 日志 # 26 | 27 | ## 2020-04-05 ## 28 | - 添加setRefreshTime接口来设置动感歌词行歌词中字刷新时间 29 | - 添加setDuration接口来设置多行歌词y轴的移动时间 30 | 31 | ## 2020-02-26 ## 32 | - 添加trc歌词支持 33 | 34 | ## 2019-01-18 ## 35 | - 添加读取歌词api接口 36 | 37 | ## 2018-12-30 ## 38 | - 修复制作歌词问题,添加多行歌词指示器回调接口 39 | 40 | ## 2018-12-29 ## 41 | - 添加制作歌词功能 42 | - 添加网易云API歌词支持 43 | - 修复网易云API歌词支持、翻译歌词支持高亮显示、修复歌词上滑动时有时不绘画的问题 44 | 45 | ## v3.2 ## 46 | - 2018-05-05 47 | - 添加混淆 48 | - 添加刷新时间 49 | 50 | ## v3.0 ## 51 | - 2018-04-22 52 | - surfaceview替换成TextureView 53 | 54 | ## v2.6 ## 55 | - 2018-04-22 56 | - 修复后台回到前台时,歌词视图内容为空的问题 57 | - 修复初始歌词数据时,OffsetY值没还原的问题 58 | 59 | ## v2.4 ## 60 | - 2018-04-21 61 | - 自定义view替换成surfaceview 62 | - 添加获取歌词参数方法 63 | 64 | ## v1.46 ## 65 | - 2018-10-02 66 | - 获取歌词最大的宽度默认为获取屏幕的大小的2/3。 67 | - 考虑到在设置歌词数据时,视图并没有显示,导致歌词的最大宽度获取为0,所以分隔歌词时出现了问题,最终出现竖直歌词的问题。 68 | 69 | ## v1.44 ## 70 | - 2018-08-11 71 | - 添加HandlerThread 72 | - 修复歌词类型切换 73 | 74 | ## v1.40 ## 75 | - 2018-06-02 76 | - minSdkVersion 修改为19 77 | 78 | ## v1.36 ## 79 | - 2018-05-12 80 | - 双行歌词的默认歌词添加居左显示和居中显示模式 81 | - 双行歌词不回手动设置字体大小标记 82 | 83 | ## v1.34 ## 84 | - 2018-05-07 85 | - 修复歌词快进点击按钮事件 86 | - 2018-05-06 87 | - 修复自定义view歌词 88 | 89 | ## v1.x ## 90 | 91 | - 修复制作歌词无法完成的问题 92 | - 修改音译歌词显示 93 | - 添加制作音译歌词实体 94 | - 修改制作翻译歌词实体 95 | - 添加制作翻译歌词实体 96 | - 添加修改绘画指示器颜色接口 97 | - 修复制作歌词问题 98 | - LyricsReader添加设置歌词数据 99 | - 添加制作歌词实体 100 | - 添加获取制作歌词状态接口 101 | - 添加获取制作后的歌词接口 102 | - 添加制作歌词预览视图 103 | - 添加额外歌词生成图片视图预览和生成额外歌词图片功能 104 | - 修复歌词生成图片问题 105 | - 修复歌词生成图片问题 106 | - 修复歌词生成图片视图的字体 107 | - 修改部分int变量的类型为long 108 | - 修改部分int变量的类型为float 109 | - 添加歌词生成图片文件接口 110 | - 添加歌词生成图片预览视图 111 | - 修复通过歌曲文件名获取歌词文件问题 112 | - 修复多行歌词未读时渐变的问题 113 | - 修复最后一个字渐变出错的问题 114 | - 修改歌词每次刷新的间隔最少为100ms 115 | - 修改歌词每次刷新的间隔最少为20ms 116 | - 修复未读到下一行歌词时,上一行歌词渐变宽度为0的问题 117 | - 修复设置歌词读取器的问题 118 | - 2018-03-04 119 | - 修复双行歌词加载歌词完成后,显示额外歌词渐变出错的问题 120 | - 修改了多行歌词,滑动时的指示器渐变颜色 121 | ## v1.2 ## 122 | 123 | - 添加歌词view获取歌词读取器方法 124 | ## v1.1 ## 125 | 126 | - 添加歌词读取器获取歌词实体类方法 127 | ## v1.0 ## 128 | 129 | - 实现lrc、ksc、krc和hrc歌词格式的显示 130 | - 实现双多行歌词的显示、字体大小、颜色、歌词换行 131 | - 多行歌词的快进、平滑移动、颜色渐变 132 | 133 | # 预览图 # 134 | 135 | ## 制作歌词界面 ## 136 | ![](https://i.imgur.com/7cU1njA.png) 137 | 138 | ## 主界面 ## 139 | 140 | ![](https://i.imgur.com/QJnz3sV.png) 141 | 142 | ## 歌词文件读取并预览 ## 143 | 144 | ![](https://i.imgur.com/8ZJYEni.png) 145 | 146 | ## 双行歌词-动感歌词 ## 147 | 148 | ![](https://i.imgur.com/rDsotfc.png) 149 | 150 | ## 双行歌词-音译歌词 ## 151 | 152 | ![](https://i.imgur.com/Q8AOiAB.png) 153 | 154 | ## 双行歌词-翻译歌词 ## 155 | 156 | ![](https://i.imgur.com/wlWCzSr.png) 157 | 158 | ## 多行歌词-lrc歌词 ## 159 | 160 | ![](https://i.imgur.com/VgFCIyG.png) 161 | 162 | ## 多行歌词-动感歌词 ## 163 | 164 | ![](https://i.imgur.com/XkNMk7l.png) 165 | 166 | ## 多行歌词-音译歌词 ## 167 | 168 | ![](https://i.imgur.com/7X6AtbZ.png) 169 | 170 | ## 多行歌词-翻译歌词 ## 171 | 172 | ![](https://i.imgur.com/g4oZvRw.png) 173 | 174 | ## 多行歌词-快进 ## 175 | 176 | ![](https://i.imgur.com/d2g7jc1.png) 177 | 178 | 179 | # Gradle # 180 | 1.root build.gradle 181 | 182 | `allprojects { 183 | repositories { 184 | ... 185 | maven { url 'https://jitpack.io' } 186 | } 187 | }` 188 | 189 | 2.app build.gradle 190 | 191 | `dependencies { 192 | compile 'com.github.zhangliangming:HPLyrics:v1.66' 193 | }` 194 | 195 | 196 | # 混淆注意 # 197 | -keep class com.zlm.hp.lyrics.** { *; } 198 | 199 | # 调用Demo # 200 | 201 | 链接: https://pan.baidu.com/s/1j-4wbtiNIfRhypb4uEnX6g 提取码: t8dj 202 | 203 | # 调用用法 # 204 | 205 | ![](https://i.imgur.com/eNPR7yy.png) 206 | 207 | ![](https://i.imgur.com/ITkxkjX.png) 208 | 209 | # 部分API # 210 | - setPaintColor:设置默认画笔颜色 211 | - setPaintHLColor:设置高亮画笔颜色 212 | - setExtraLyricsListener:设置额外歌词回调方法,多用于加载歌词完成后,根据额外歌词的状态来判断是否需要显示翻译、音译歌词按钮 213 | - setSearchLyricsListener:无歌词时,搜索歌词接口 214 | - setOnLrcClickListener:多行歌词中歌词快进时,点击播放按钮时,调用。 215 | - setFontSize:设置默认画笔的字体大小,可根据参数来设置是否要刷新view 216 | - setExtraLrcStatus:设置额外歌词状态 217 | - setLyricsReader:设置歌词读取器 218 | - play:设置歌词当前的播放进度(播放歌曲时调用一次即可) 219 | - pause:暂停歌词 220 | - seekto:快进歌词 221 | - resume:唤醒 222 | - initLrcData:初始化歌词内容 223 | - setTranslateDrawLrcColorType:设置翻译歌词绘画颜色类型 224 | - setTranslateDrawType:设置翻译歌词绘画类型 225 | 226 | # 声明 # 227 | 由于该项目涉及到酷狗的动感歌词的版权问题,所以该项目的代码和内容仅用于学习用途 228 | 229 | # 捐赠 # 230 | 如果该项目对您有所帮助,欢迎您的赞赏 231 | 232 | - 微信 233 | 234 | ![](https://i.imgur.com/hOs6tPn.png) 235 | 236 | - 支付宝 237 | 238 | ![](https://i.imgur.com/DGB9Lq0.png) 239 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':hplyricslibrary' 2 | --------------------------------------------------------------------------------