Unity實現(xiàn)卡拉OK歌詞過渡效果
好長時間之前做過的一個項目 , 其中設(shè)計到用Unity模擬卡拉OK歌詞過渡的效果 , 如下圖所示 ↓ , 這里簡單把原理部分分享一下.
文章目錄
- 演示效果 ↓
- 歌詞效果類 ↓
- 配套資源下載
演示效果 ↓
- 實現(xiàn)歌詞動態(tài)調(diào)整功能
- 實現(xiàn)動態(tài)讀取歌詞文件功能
- 實現(xiàn)歌曲快進快退功能
- 實現(xiàn)歌曲單字時間匹配功能
- 實現(xiàn)可動態(tài)更換歌詞前景色背景色功能
注:
這里為實現(xiàn)精準過渡效果使用的是KSC歌詞文件, 并不是LRC文件哦 .
這其中我認為就是如何實現(xiàn)歌詞部分的前景色向后景色過渡的效果了, 開始的時候我想的也是很復(fù)雜 , 使用Shader的形式實現(xiàn) ,網(wǎng)上找了一些相關(guān)代碼 , 發(fā)現(xiàn)不是特別理想 , 最終還是自己嘗試著用Mask來實現(xiàn)的, 發(fā)現(xiàn)效果還不錯 !
因為今天下班就過年回家啦! 其他細節(jié)之后會完善的 , 今天把工程文件先上傳了 .
歌詞效果類 ↓
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using DG.Tweening;
using DG.Tweening.Core;
/// <summary>
/// 用于顯示歌詞過渡的效果
/// 1. 獲得路徑加載并解析歌詞文件信息
/// 2. 判斷當前歌曲是否播放( 歌曲暫停的時候歌詞效果也暫停 , 歌曲停止的時候歌詞效果消失 )
/// 3. 判斷歌曲快進或快退事件
/// </summary>
public class LayricPanelEffect : MonoSingleton<LayricPanelEffect>
{
#region *********************************************************************字段
//由外部傳入的聲音資源
[HideInInspector,SerializeField]
public AudioSource audioSource;
//歌詞前景顏色;歌詞后景顏色
[SerializeField]
public Color32 frontTextColor = Color.white, backTextColor = Color.black, outlineColor = Color.white;
//歌詞面板的前景部分和后景部分
public RectTransform rectFrontLyricText, rectBackLyricMask;
public Slider slider;
//歌詞文件路徑
[HideInInspector,SerializeField]
public string lyricFilePath;
//是否開始播放當前行歌詞內(nèi)容
public bool isStartLyricEffectTransition = true;
//歌詞調(diào)整進度 ( 糾錯 )
// [HideInInspector]
public float lyricAdjust = -5f;
//歌詞文本信息
// [HideInInspector]
[SerializeField,HideInInspector]
public Text _lyricText;
public Text _textContentLyric, _textLogMessage;
private Vector2 tempFrontSizeDelta, tempBackSizeDelta;
//用于訪問歌詞正文部分的內(nèi)容在KscWord類中
private KSC.KscWord kscword = new KSC.KscWord ();
private KSC.KscWord curKscword = new KSC.KscWord ();
//內(nèi)部定時器( 由外部傳入?yún)?shù)來控制 , 用來記錄歌曲播放的當前時間軸 )
private float _timer = 0.00f;
#endregion
/// <summary>
/// 初始化一些變量
/// </summary>
void InitSomething ()
{
//堅持對歌詞文件進行賦值操作
if (_lyricText == null || rectFrontLyricText.GetComponent <ContentSizeFitter> () == null) {
if (rectFrontLyricText.GetComponent <Text> () == null) {
_lyricText = rectFrontLyricText.gameObject.AddComponent <Text> ();
}
_lyricText = rectFrontLyricText.GetComponent <Text> ();
//保持歌詞實現(xiàn)自適應(yīng)
rectFrontLyricText.GetComponent <ContentSizeFitter> ().horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
rectFrontLyricText.GetComponent <ContentSizeFitter> ().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
}
rectBackLyricMask.GetComponentInChildren <Text> ().text = _lyricText.text;
//歌詞顏色的更改初始化
rectBackLyricMask.GetComponentInChildren <Text> ().color = backTextColor;
rectBackLyricMask.GetComponentInChildren <Outline> ().effectColor = outlineColor;
rectFrontLyricText.GetComponent <Text> ().color = frontTextColor;
//歌詞過渡的前景部分 ( 用于判斷過度遮罩的長度范圍 )
tempFrontSizeDelta = rectFrontLyricText.sizeDelta;
tempBackSizeDelta = rectBackLyricMask.sizeDelta;
//是否開始當前歌詞行播放標志位
isStartLyricEffectTransition = true;
}
void Awake ()
{
//初始化
InitSomething ();
}
/// <summary>
/// 控制歌詞面板的顯示
/// 1. 僅僅顯示歌詞面板信息 , 沒有過渡效果!
/// </summary>
/// <param name="row">歌詞正文部分行號.</param>
/// <param name="isPanelView">If set to <c>true</c> 顯示面板歌詞</param>
public void LyricPanelControllerView (KSC.KscWord curRowInfo, bool isPanelView)
{
// Debug.Log ("當前行是否開始=====>" + isPanelView.ToString ());
_textLogMessage.text = isStartLyricEffectTransition.ToString ();
rectBackLyricMask.sizeDelta = new Vector2 (0f, rectFrontLyricText.sizeDelta.y);
rectBackLyricMask.GetComponentInChildren <Text> ().text = _lyricText.text = "";
if (isPanelView) {
//根據(jù)時間得到當前播放的是第i行的歌詞
//處理歌詞面板信息 , 顯示歌詞
foreach (var item in curRowInfo.PerLineLyrics) {
_lyricText.text += item;
rectBackLyricMask.GetComponentInChildren<Text> ().text = _lyricText.text;
}
StartCoroutine (LyricPanelControllerEffect (curRowInfo, isPanelView));
} else {
StopAllCoroutines ();
rectBackLyricMask.sizeDelta = new Vector2 (0f, rectFrontLyricText.sizeDelta.y);
// StartCoroutine (LyricPanelControllerEffect (curRowInfo, isPanelView));
//當前歌詞結(jié)束以后將歌詞框初始化成0
rectBackLyricMask.GetComponentInChildren <Text> ().text = _lyricText.text = string.Empty;
}
}
/// <summary>
/// 開始實現(xiàn)歌此過渡效果, 僅僅效果實現(xiàn)
/// 1. 使用Dotween的doSizedata實現(xiàn)
/// 2. 動態(tài)調(diào)整蒙板的sizedata寬度
/// 3. 根據(jù)歌曲當前播放的時間進度進行調(diào)整
/// </summary>
/// <returns>The panel controller effect.</returns>
/// <param name="isPanelEffect">If set to <c>true</c> is panel effect.</param>
public IEnumerator LyricPanelControllerEffect (KSC.KscWord curRowInfo, bool isPanelEffect)
{
//當前時間歌詞播放進度的百分比比例
int curWordIndex = 0;
if (isPanelEffect) {
rectBackLyricMask.DORewind ();
yield return null;
rectBackLyricMask.sizeDelta = new Vector2 (0f, rectFrontLyricText.sizeDelta.y);
//開始效果過渡
if (audioSource.isPlaying) {
for (int i = 0; i < curKscword.PerLinePerLyricTime.Length; i++) {
rectBackLyricMask.DOSizeDelta (
new Vector2 (((float)(i + 1) / curKscword.PerLinePerLyricTime.Length) * rectFrontLyricText.sizeDelta.x, rectFrontLyricText.sizeDelta.y)
, curKscword.PerLinePerLyricTime [i] / 1000f
, false).SetEase (Ease.Linear);
// Debug.Log ("第" + i + "個歌詞時間");
yield return new WaitForSeconds (curKscword.PerLinePerLyricTime [i] / 1000f);
}
} else {
Debug.LogError ("歌曲沒有播放 !?。。?);
}
} else {
yield return null;
rectBackLyricMask.DOSizeDelta (new Vector2 (0f, rectFrontLyricText.sizeDelta.y), 0f, true);
}
}
/// <summary>
/// 開始播放音樂的時候調(diào)用
/// </summary>
/// <param name="lyricFilePath">歌詞文件路徑.</param>
/// <param name="audioSource">Audiosource用于判斷播放狀態(tài).</param>
/// <param name="frontColor">前景色.</param>
/// <param name="backColor">后景.</param>
/// <param name="isIgronLyricColor">如果設(shè)置為 <c>true</c> 則使用系統(tǒng)配置的默認顏色.</param>
public void StartPlayMusic (string lyricFilePath, AudioSource audioSource, Color frontColor, Color backColor, Color outlineColor, bool isIgronLyricColor)
{
_timer = 0f;
//初始化ksc文件
KSC.InitKsc (lyricFilePath);
this.lyricFilePath = lyricFilePath;
this.audioSource = audioSource;
_textContentLyric.text = string.Empty;
if (!isIgronLyricColor) {
//歌曲顏色信息
this.frontTextColor = frontColor;
this.backTextColor = backColor;
this.outlineColor = outlineColor;
}
#region ****************************************************輸出歌詞文件信息
//對初始化完成后的信息進行輸出
if (KSC.Instance.SongName != null) {
print ("歌名==========>" + KSC.Instance.SongName);
}
if (KSC.Instance.Singer != null) {
print ("歌手==========>" + KSC.Instance.Singer);
}
if (KSC.Instance.Pinyin != null) {
print ("拼音==========>" + KSC.Instance.Pinyin);
}
if (KSC.Instance.SongClass != null) {
print ("歌類==========>" + KSC.Instance.SongClass);
}
if (KSC.Instance.InternalNumber > 0) {
print ("歌曲編號=======>" + KSC.Instance.InternalNumber);
}
if (KSC.Instance.Mcolor != Color.clear) {
print ("男唱顏色=======>" + KSC.Instance.Mcolor);
}
if (KSC.Instance.Mcolor != Color.clear) {
print ("女唱顏色=======>" + KSC.Instance.Wcolor);
}
if (KSC.Instance.SongStyle != null) {
print ("風(fēng)格==========>" + KSC.Instance.SongStyle);
}
if (KSC.Instance.WordCount > 0) {
print ("歌名字數(shù)=======>" + KSC.Instance.WordCount);
}
if (KSC.Instance.LangClass != null) {
print ("語言種類=======>" + KSC.Instance.LangClass);
}
//一般是獨唱歌曲的時候使用全Tag標簽展現(xiàn)參數(shù)信息
foreach (var item in KSC.Instance.listTags) {
print (item);
}
#endregion
//顯示整個歌詞內(nèi)容
for (int i = 0; i < KSC.Instance.Add.Values.Count; i++) {
KSC.Instance.Add.TryGetValue (i, out kscword);
for (int j = 0; j < kscword.PerLineLyrics.Length; j++) {
_textContentLyric.text += kscword.PerLineLyrics [j];
}
_textContentLyric.text += "\n";
}
}
/// <summary>
/// 停止播放按鈕
/// </summary>
public void StopPlayMusic ()
{
Debug.Log ("停止播放按鈕");
}
/// <summary>
/// 主要用于歌詞部分的卡拉OK過渡效果
/// 1. 動態(tài)賦值歌詞框的長度
/// 2. 支持快進快退同步顯示
/// </summary>
int row = 0, tempRow = 0;
void FixedUpdate ()
{
#region *********************************************************播放過渡效果核心代碼
//如果是播放狀態(tài)并且沒有快進或快退 , 獲得當前播放時間 , 如果都下一句歌詞了 , 則切換到下一句歌詞進行過渡效果
//1. 是否是暫停;
//2. 是否開始播放
//3. 是否播放停止
if (audioSource != null && audioSource.isPlaying) {
//進度條
slider.value = _timer / audioSource.clip.length;
//快進快退快捷鍵
if (Input.GetKey (KeyCode.RightArrow)) {
audioSource.time = Mathf.Clamp ((audioSource.time + 1f), 0f, 4.35f * 60f);
} else if (Input.GetKey (KeyCode.LeftArrow)) {
audioSource.time = Mathf.Clamp ((audioSource.time - 1f), 0f, 4.35f * 60f);
// } else if (Input.GetKeyUp (KeyCode.LeftArrow)) {
isStartLyricEffectTransition = true;
rectBackLyricMask.GetComponentInChildren <Text> ().text = rectFrontLyricText.GetComponent <Text> ().text = string.Empty;
}
//實時計時
_timer = audioSource.time;
//歌曲開始播放的時間
_textLogMessage.text = _timer.ToString ("F2");
for (int i = 0; i < KSC.Instance.Add.Count; i++) {
KSC.Instance.Add.TryGetValue (i, out kscword);
//根據(jù)時間判斷當前播放的是哪一行的歌詞文件 ( 減去0.01可保證兩句歌詞銜接太快的時候的bug )
if ((_timer >= (kscword.PerLineLyricStartTimer + lyricAdjust + 0.1f) && _timer <= (kscword.PerLintLyricEndTimer + lyricAdjust - 0.1f)) && isStartLyricEffectTransition) {
tempRow = i;
KSC.Instance.Add.TryGetValue (tempRow, out curKscword);
isStartLyricEffectTransition = false;
Debug.Log ("當前播放====>" + i + "行");
//歌詞面板顯示當前播放內(nèi)容
LyricPanelControllerView (curKscword, !isStartLyricEffectTransition);
} else if ((_timer >= (curKscword.PerLintLyricEndTimer + lyricAdjust)) && !isStartLyricEffectTransition) {
isStartLyricEffectTransition = true;
//設(shè)置不顯示歌詞內(nèi)容
LyricPanelControllerView (curKscword, !isStartLyricEffectTransition);
}
}
// KSC.Instance.Add.TryGetValue (row, out kscword);
//
// //根據(jù)時間判斷當前播放的是哪一行的歌詞文件 ( 減去0.01可保證兩句歌詞銜接太快的時候的bug )
// if ((_timer >= (kscword.PerLineLyricStartTimer + lyricAdjust + 0.1f) && _timer <= (kscword.PerLintLyricEndTimer + lyricAdjust)) && isStartLyricEffectTransition) {
// tempRow = row;
// KSC.Instance.Add.TryGetValue (tempRow, out curKscword);
// isStartLyricEffectTransition = false;
// Debug.Log ("當前播放====>" + row + "行");
// //歌詞面板顯示當前播放內(nèi)容
// LyricPanelControllerView (curKscword, !isStartLyricEffectTransition);
// } else if ((_timer >= (curKscword.PerLintLyricEndTimer + lyricAdjust)) && !isStartLyricEffectTransition) {
// isStartLyricEffectTransition = true;
// //設(shè)置不顯示歌詞內(nèi)容
// LyricPanelControllerView (curKscword, !isStartLyricEffectTransition);
// row = (row + 1) % KSC.Instance.Add.Count;
// }
#endregion
}
}
}
###KSC文件解析類 ↓
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System.Text;
using UnityEngine.UI;
using System;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
/// <summary>
/// KSC歌詞文件解析屬性, 單例工具類 ( 解析解析解析解析解析解析解析解析解析!!!!!!重要的事情多說幾遍 )
/// 1. 歌詞部分標題信息用單例instance訪問
/// 2. 正文信息部分使用KSCWord對象訪問( 每句開始時間\結(jié)束時間\每句歌詞文字的數(shù)組\每句歌詞文件時間的數(shù)組 )
/// </summary>
public class KSC : Singleton<KSC>
{
/// <summary>
/// 歌曲 歌名
/// </summary>
public string SongName { get; set; }
/// <summary>
/// 歌名字數(shù) 歌名字數(shù)
/// </summary>
public int WordCount{ get; set; }
/// <summary>
/// 歌名字數(shù) 歌名的拼音聲母
/// </summary>
public string Pinyin{ get; set; }
/// <summary>
/// 歌名字數(shù) 歌曲語言種類
/// </summary>
public string LangClass{ get; set; }
/// <summary>
/// 歌類,如男女樂隊等
/// </summary>
public string SongClass{ get; set; }
/// <summary>
/// 藝術(shù)家 演唱者,對唱則用斜杠"/"分隔
/// </summary>
public string Singer { get; set; }
/// <summary>
/// 歌曲編號 歌曲編號
/// </summary>
public int InternalNumber{ get; set; }
/// <summary>
/// 歌曲風(fēng)格
/// </summary>
public string SongStyle{ get; set; }
/// <summary>
/// 視頻編號
/// </summary>
public string VideoFileName{ get; set; }
/// <summary>
/// 前景顏色
/// </summary>
public Color Mcolor{ get; set; }
/// <summary>
/// 后景顏色
/// </summary>
public Color Wcolor{ get; set; }
/// <summary>
/// 偏移量
/// </summary>
public string Offset { get; set; }
/// <summary>
/// 各類標簽
/// </summary>
public List<string> listTags = new List<string> ();
/// <summary>
/// 歌詞正文部分信息 ( key = 行號 value = 解析出來的歌詞正文部分的每句歌詞信息 )
/// </summary>
public Dictionary<int,KscWord> Add = new Dictionary<int, KscWord> ();
/// <summary>
/// 獲得歌詞信息
/// </summary>
/// <param name="LrcPath">歌詞路徑</param>
/// <returns>返回歌詞信息(Lrc實例)</returns>
public static KSC InitKsc (string LrcPath)
{
int row = 0;
//KscWord對象
//清除之前的歌曲歌詞, 保持當前
KSC.Instance.Add.Clear ();
using (FileStream fs = new FileStream (LrcPath, FileMode.Open, FileAccess.Read, FileShare.Read)) {
string line = string.Empty;
using (StreamReader sr = new StreamReader (fs, Encoding.Default)) {
while ((line = sr.ReadLine ()) != null) {
//每次循環(huán)新建一個對象用于存儲不同行數(shù)內(nèi)容
KSC.KscWord kscWord = new KSC.KscWord ();
#region ######################################合唱歌曲格式
if (line.StartsWith ("karaoke.songname := '")) {
Instance.SongName = SplitStrInfo (line);
} else if (line.StartsWith ("karaoke.internalnumber := ")) {
if (SplitIntInfo (line) != 0) {
Instance.InternalNumber = SplitIntInfo (line);
}
} else if (line.StartsWith ("karaoke.singer := '")) {
Instance.Singer = SplitStrInfo (line);
} else if (line.StartsWith ("karaoke.wordcount := ")) {
if (SplitIntInfo (line) != 0) {
Instance.WordCount = SplitIntInfo (line);
}
} else if (line.StartsWith ("karaoke.pinyin := '")) {
Instance.Pinyin = SplitStrInfo (line);
} else if (line.StartsWith ("karaoke.langclass := '")) {
Instance.LangClass = SplitStrInfo (line);
} else if (line.StartsWith ("karaoke.songclass := '")) {
Instance.SongClass = SplitStrInfo (line);
} else if (line.StartsWith ("karaoke.songstyle := '")) {
Instance.SongStyle = SplitStrInfo (line);
} else if (line.StartsWith ("karaoke.videofilename :='")) {
Instance.VideoFileName = SplitStrInfo (line);
} else if (line.StartsWith ("mcolor :=rgb(")) {
if (SplitColorInfo (line) != Color.clear) {
Instance.Mcolor = SplitColorInfo (line);
}
} else if (line.StartsWith ("wcolor :=rgb(")) {
if (SplitColorInfo (line) != Color.clear) {
Instance.Wcolor = SplitColorInfo (line);
}
#endregion
#region ##################################################獨唱歌曲風(fēng)格
} else if (line.StartsWith ("karaoke.tag('")) {
//獲取tag標簽的參數(shù)信息
KSC.Instance.listTags.Add (SplitTagInfo (line));
#endregion
#region ################################################歌詞正文部分解析
} else if (line.StartsWith (("karaoke.add"))) { //表示歌詞正文部分
if (SplitLyricStartTime (line) != null) {
//行號 ( 從0行開始 )
//獲取每句歌詞部分的開始時間
kscWord.PerLineLyricStartTimer = SplitLyricStartTime (line);
//獲取每句歌詞部分的結(jié)束時間
kscWord.PerLintLyricEndTimer = SplitLyricEndTime (line);
//獲取每行歌詞的內(nèi)容,并存儲到KSCWord中 ( 歌詞文字的數(shù)組信息 左 → 右 )
kscWord.PerLineLyrics = SplitPerLineLyrics (line);
//獲取每行中單個文字的過渡時間數(shù)組 ( 歌詞文字過渡時間數(shù)組 左 → 右 )
kscWord.PerLinePerLyricTime = SplitPerLinePerLyricTime (line);
KSC.Instance.Add.Add (row, kscWord);
row++;
}
} else {
//忽略ksc文件中的其他部分
if (line != "" && !line.Contains ("CreateKaraokeObject") && !line.Contains ("karaoke.rows") && !line.Contains ("karaoke.clear;") && !Regex.IsMatch (line, @"^\//")) {
Debug.LogWarning ("歌詞含有不能解析的部分 ===> " + line);
}
}
#endregion
}
}
}
Debug.Log ("LyricFileInitialized" + " Path : \n" + LrcPath);
return Instance;
}
#region ****************************************************************解析歌詞用的正則表達式部分 需更新
/// <summary>
/// 處理信息(私有方法)
/// </summary>
/// <param name="line"></param>
/// <returns>返回基礎(chǔ)信息</returns>
public static string SplitStrInfo (string line)
{
// char[] ch = new char[]{ '\0', '\0' };
// return line.Substring (line.IndexOf ("'") + 1).TrimEnd (ch);
string pattern = @"'\S{1,20}'"; //獲取歌曲標簽信息
Match match = Regex.Match (line, pattern);
//去除兩端的小分號
string resout = string.Empty;
resout = match.Value.Replace ("\'", string.Empty);
return resout;
}
/// <summary>
/// 處理參數(shù)是數(shù)字的情況
/// </summary>
/// <returns>The int info.</returns>
/// <param name="line">Line.</param>
public static int SplitIntInfo (string line)
{
string pattern = @"\d+"; //獲取歌曲標簽參數(shù)為數(shù)字的信息
Match match = Regex.Match (line, pattern);
//去除兩端的小分號
int result = 0;
result = Int32.Parse (match.Value);
return result;
}
/// <summary>
/// 處理參數(shù)顏色色值的情況 如: mcolor :=rgb(0, 0, 255);
/// </summary>
/// <returns>The color info.</returns>
/// <param name="line">Line.</param>
public static Color32 SplitColorInfo (string line)
{
string pattern = @"[r,R][g,G][b,G]?[\(](2[0-4][0-9])|25[0-5]|[01]?[0-9][0-9]?"; //獲取歌曲標簽參數(shù)為顏色值的信息
MatchCollection matches = Regex.Matches (line, pattern);
return new Color (float.Parse (matches [0].ToString ()), float.Parse (matches [1].ToString ()), float.Parse (matches [2].ToString ()));
}
/// <summary>
/// 獲取歌曲的標簽部分信息 如 : karaoke.tag('語種', '國語'); // 國語/粵語/臺語/外語
/// </summary>
/// <returns>The tag info.</returns>
public static string SplitTagInfo (string line)
{
string temp;
string pattern = @"\([\W|\w]+\)"; //獲取歌曲標簽參數(shù)為顏色值的信息
Match match = Regex.Match (line, pattern);
temp = match.Value.Replace ("(", string.Empty).Replace (")", string.Empty).Replace ("'", string.Empty).Replace (",", ":");
return temp;
}
/// <summary>
/// 獲取每句歌詞正文部分的開始時間 (單位 : 秒)
/// </summary>
/// <returns>The lyric start time.</returns>
/// <param name="line">Line.</param>
public static float SplitLyricStartTime (string line)
{
float time = 0f;
Regex regex = new Regex (@"\d{2}:\d{2}\.\d{2,3}", RegexOptions.IgnoreCase); //匹配單句歌詞時間 如: karaoke.add('00:29.412', '00:32.655'
MatchCollection mc = regex.Matches (line);
time = (float)TimeSpan.Parse ("00:" + mc [0].Value).TotalSeconds;
return time;
}
/// <summary>
/// 獲取每句歌詞正文部分的結(jié)束時間 (單位 : 秒)
/// </summary>
/// <returns>The lyric start time.</returns>
/// <param name="line">Line.</param>
public static float SplitLyricEndTime (string line)
{
Regex regex = new Regex (@"\d{2}:\d{2}\.\d{2,3}", RegexOptions.IgnoreCase); //匹配單句歌詞時間 如: karaoke.add('00:29.412', '00:32.655'
MatchCollection mc = regex.Matches (line);
float time = (float)TimeSpan.Parse ("00:" + mc [1].Value).TotalSeconds;
return time;
}
/// <summary>
/// 獲取每句歌詞部分的每個文字 和 PerLinePerLyricTime相匹配 (單位 : 秒)
/// </summary>
/// <returns>The line lyrics.</returns>
/// <param name="line">Line.</param>
public static string[] SplitPerLineLyrics (string line)
{
List<string> listStrResults = new List<string> ();
string pattern1 = @"\[[\w|\W]{1,}]{1,}"; //獲取歌曲正文每個單詞 如 : karaoke.add('00:25.183', '00:26.730', '[五][十][六][個][星][座]', '312,198,235,262,249,286');
string pattern2 = @"\'(\w){1,}\'"; //獲取歌曲正文每個單詞 如 : karaoke.add('00:28.420', '00:35.431', '夕陽底晚風(fēng)里', '322,1256,2820,217,1313,1083');
Match match = (line.Contains ("[") && line.Contains ("]")) ? Regex.Match (line, pattern1) : Regex.Match (line, pattern2);
//刪除掉 [ ] '
if (match.Value.Contains ("[") && match.Value.Contains ("]")) { //用于合唱類型的歌詞文件
string[] resultStr = match.Value.Replace ("][", "/").Replace ("[", string.Empty).Replace ("]", string.Empty).Split ('/');
foreach (var item in resultStr) {
listStrResults.Add (item);
}
} else if (match.Value.Contains ("'")) { //用于獨唱類型的歌詞文件 ( 尚未測試英文歌詞文件!!!!!!!!!!!!!!!!!!!!!!! )
char[] tempChar = match.Value.Replace ("'", string.Empty).ToCharArray ();
foreach (var item in tempChar) {
listStrResults.Add (item.ToString ());
}
}
return listStrResults.ToArray ();
}
/// <summary>
/// 獲取每句歌詞部分的每個文字需要的過渡時間 和 PerLineLyrics相匹配 (單位 : 秒)
/// </summary>
/// <returns>The line per lyric time.</returns>
/// <param name="line">Line.</param>
public static float[] SplitPerLinePerLyricTime (string line)
{
string pattern = @"\'((\d){0,}\,{0,1}){0,}\'"; //獲取歌曲正文每個單詞過渡時間 如 : karaoke.add('00:25.183', '00:26.730', '[五][十][六][個][星][座]', '312,198,235,262,249,286');
string str = null;
List<float> listfloat = new List<float> ();
//刪除掉 多余項
str = Regex.Match (line, pattern).Value.Replace ("'", string.Empty);
// Debug.Log (str);
foreach (var item in str.Split (',')) {
listfloat.Add (float.Parse (item));
}
return listfloat.ToArray ();
}
#endregion
#region ********************************************************************歌詞正文部分的時間與文字信息
/// <summary>
/// 用單獨的類來管理歌詞的正文部分 ( 在KSC類下 )主要用來存儲每句歌詞和每個歌詞的時間信息
/// 1. 每句歌詞的時間的 ( 開始 - 結(jié)束 )
/// 2. 每句歌詞中單個文字的時間信息 (集合的形式實現(xiàn))
/// </summary>
public class KscWord
{
/// <summary>
/// 每行歌詞部分開始的時間 (單位 : 秒) (key=行號,value=時間)
/// </summary>
public float PerLineLyricStartTimer { get; set; }
/// <summary>
/// 每行歌詞部分結(jié)束時間 (單位 : 秒) (key=行號,value=時間)
/// </summary>
public float PerLintLyricEndTimer { get; set; }
/// <summary>
/// 每行歌詞的單個文字集合
/// </summary>
public string[] PerLineLyrics{ get; set; }
/// <summary>
/// 每行歌詞中單個文字的速度過渡信息 (單位 : 毫秒)
/// </summary>
public float[] PerLinePerLyricTime{ get; set; }
}
#endregion
}
不敢說代碼如何清新脫俗 , 自己也在學(xué)習(xí)的路上 , 有程序愛好者想要交流的話歡迎交流啊 . 有心的朋友可以看看 , 年后看時間再完善啦
配套資源下載
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持我們。
上一篇:Unity3D使用鼠標旋轉(zhuǎn)縮放平移視角
欄 目:C#教程
下一篇:Unity調(diào)取移動端的麥克風(fēng)進行錄音并播放
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/4730.html
您可能感興趣的文章
- 01-10C#實現(xiàn)txt定位指定行完整實例
- 01-10WinForm實現(xiàn)仿視頻播放器左下角滾動新聞效果的方法
- 01-10C#實現(xiàn)清空回收站的方法
- 01-10C#實現(xiàn)讀取注冊表監(jiān)控當前操作系統(tǒng)已安裝軟件變化的方法
- 01-10C#實現(xiàn)多線程下載文件的方法
- 01-10C#實現(xiàn)Winform中打開網(wǎng)頁頁面的方法
- 01-10C#實現(xiàn)遠程關(guān)閉計算機或重啟計算機的方法
- 01-10C#自定義簽名章實現(xiàn)方法
- 01-10C#文件斷點續(xù)傳實現(xiàn)方法
- 01-10winform實現(xiàn)創(chuàng)建最前端窗體的方法


閱讀排行
本欄相關(guān)
- 01-10C#通過反射獲取當前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實現(xiàn)txt定位指定行完整實例
- 01-10WinForm實現(xiàn)仿視頻播放器左下角滾動新
- 01-10C#停止線程的方法
- 01-10C#實現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實現(xiàn)讀取注冊表監(jiān)控當前操作系統(tǒng)已
隨機閱讀
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-10C#中split用法實例總結(jié)
- 01-11ajax實現(xiàn)頁面的局部加載
- 01-10delphi制作wav文件的方法
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 04-02jquery與jsp,用jquery
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05DEDE織夢data目錄下的sessions文件夾有什


