1. 程式人生 > >C# WPF 低仿網易雲音樂(PC)歌詞控制元件

C# WPF 低仿網易雲音樂(PC)歌詞控制元件

原文: C# WPF 低仿網易雲音樂(PC)歌詞控制元件

提醒:本篇部落格記錄了修改的過程,廢話比較多,需要專案原始碼和看演示效果的直接拉到文章最底部~

 

網易雲音樂獲取歌詞的api地址

http://music.163.com/api/song/media?id=歌曲ID

填寫歌曲的id即可獲取到json格式的資料(歌曲ID獲取的方法是:點選分享按鈕>其他分享>複製連結,就可以在連結中看到了):

{"songStatus":0,"lyricVersion":10,"lyric":"[by:Esida]\n[ti:起風了]\n[ar:買辣椒也用劵]\n[al:起風了]\n[by:九九Lrc歌詞網~www.99Lrc.net]\n\n\n[00:04.00]原曲: ヤキモチ\n\n\n\n[00:20.00]後期: 聖雨輕紗\n\n[00:24.00]海報:不 鹹\n\n\n\n[00:28.64]這一路上走走停停 順著少年漂流的痕跡\n\n[00:35.11]邁出車站的前一刻 竟有些猶豫\n\n[00:41.08]不禁笑這近鄉情怯 仍無可避免\n\n[00:46.49]而長野的天 依舊這麼暖 風吹起了從前\n\n[00:52.02]從前初識這世間 萬般流連 看著天邊似在眼前\n\n[00:59.50]也甘願赴湯蹈火去走它一遍\n\n[01:04.52]如今走過這世間 萬般流連 翻過歲月不同側臉\n\n[01:11.75]措不及防闖入你的笑顏\n\n[01:17.37]我曾難自拔於世界之大 也沉溺於其中夢話 不得真假 不做掙扎 不懼笑話\n\n[01:30.39]我曾將青春翻湧成她 也曾指尖彈出盛夏 心之所動 且就隨緣去吧\n\n[01:42.40]逆著光行走 任風吹雨打\n\n[01:49.89]-M-\n\n[01:59.14]短短的路走走停停 也有了幾分的距離\n\n[02:05.20]不知撫摸的是故事 還是段心情\n\n[02:11.22]也許期待的不過是 與時間為敵\n\n[02:16.94]再次看到你 微涼晨光裡 笑的很甜蜜\n\n[02:22.29]從前初識這世間 萬般流連 看著天邊似在眼前\n\n[02:29.58]也甘願赴湯蹈火去走它一遍\n\n[02:34.42]如今走過這世間 萬般流連 翻過歲月不同側臉\n\n[02:41.87]措不及防闖入你的笑顏\n\n[02:47.23]我曾難自拔於世界之大 也沉溺於其中夢話 不得真假 不做掙扎 不懼笑話\n\n[03:00.13]我曾將青春翻湧成她 也曾指尖彈出盛夏 心之所動 且就隨緣去吧\n\n[03:15.51]-=-\n\n[03:38.30]晚風吹起你鬢間的白髮 撫平回憶留下的疤 你的眼中 明暗交雜 一笑生花\n\n[03:50.53]暮色遮住你蹣跚的步伐 走進床頭藏起的畫 畫中的你 低著頭說話\n\n[04:03.05]我仍感嘆於世界之大 也沉醉於兒時情話 不剩真假 不做掙扎 無謂笑話\n\n[04:15.34]我終將青春還給了她 連同指尖彈出的盛夏 心之所動 就隨風去了\n\n[04:27.63]以愛之名 你還願意嗎\n\n[04:35.36]-E-
","code":200}

我們需要用到的資料只有lyric部分。

可以看到歌詞的結構很簡單:“[歌曲時間]歌詞部分\n”\n作為換行符。

看完歌詞結構思路已經有了,將時間和歌詞提取出來,播放音樂,獲取音樂播放進度,跟歌詞對應的時間作比對。

這裡我們要做的控制元件效果如網易雲音樂(以下簡稱網音)的效果差不多,先看下網音的效果動圖:

網音的效果如圖所示,播放到當前進度的歌詞字型顏色變成白色,並且會滾動檢視使焦點停在歌詞整體中間的位置。

 

新建專案命名為:網易雲音樂歌詞顯示控制元件,新建一個使用者控制元件命名為:LrcView。

LrcView.xaml:

<
UserControl x:Class="網易雲音樂歌詞顯示控制元件.Controls.LrcView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid> <ScrollViewer Name="c_scrollviewer"> <StackPanel Name="c_lrc_items"> </StackPanel> </ScrollViewer> </Grid> </UserControl>

應該很好理解,用一個scrollviewer包住stackpanel,把歌詞(textblock)新增到stackpanel裡,當前音樂所在進度的歌詞顏色只要修改相應的textblock的字型顏色即可,歌詞自動滾動則依靠scrollviewer。(小知識:*只要不設定固定高度stackpanel會隨著包含的內容高度的增大而增大,這時候套個scrollviewer就可以滾動stackpanel了。)

轉到控制元件後臺程式碼,為了方便修改歌詞顏色等一些操作,我們建立一個歌詞模型類:

   #region 歌詞模型
        public class LrcModel
        {
            /// <summary>
            /// 歌詞所在控制元件
            /// </summary>
            public TextBlock c_LrcTb { get; set; }

            /// <summary>
            /// 歌詞字串
            /// </summary>
            public string LrcText { get; set; }

            /// <summary>
            /// 時間(讀取格式參照網易雲音樂歌詞格式:xx:xx.xx,即分:秒.毫秒,秒小數點保留2位。如:00:28.64)
            /// </summary>
            public string Time { get; set; }
        }
        #endregion

新增一些需要用到的變數

 #region 變數
        //歌詞集合
        public Dictionary<string, LrcModel> Lrcs = new Dictionary<string, LrcModel>();
        //當前焦點所在歌詞集合位置
        public int FoucsLrcLocation { get; set; } = -1;
        //非焦點歌詞顏色
        public SolidColorBrush NoramlLrcColor = new SolidColorBrush(Colors.Black);
        //焦點歌詞顏色
        public SolidColorBrush FoucsLrcColor = new SolidColorBrush(Colors.OrangeRed);

        #endregion

接著是載入歌詞

將歌詞複製出來,用\n切割(split),再用正則表示式取出時間、歌詞,將獲取到的資料新增到集合內以及對textblock進行賦值即可。

 #region 載入歌詞
        public void LoadLrc(string lrcstr)
        {
            //迴圈以換行\n切割出歌詞
            foreach (string str in lrcstr.Split('\n'))
            {
                //過濾空行
                if (str.Length > 0)
                {
                    //歌詞時間
                    string time = GetTime(str);
                    //歌詞
                    string lrc = str.Replace("[" + time + "]", "");

                    //過濾空行
                    if (time.Length > 0)
                    {

                        //歌詞顯示textblock控制元件
                        TextBlock c_lrcbk = new TextBlock();
                        //賦值
                        c_lrcbk.Text = lrc;
                        if (c_lrc_items.Children.Count > 0)
                        {
                            //增加一些行間距,see起來不那麼擁擠~
                            c_lrcbk.Margin = new Thickness(0, 10, 0, 0);
                        }

                        //新增到集合,方便日後操作
                        Lrcs.Add(time, new LrcModel()
                        {
                            c_LrcTb = c_lrcbk,
                            LrcText = lrc,
                            Time = time

                        });

                        //將歌詞顯示textblock控制元件新增到介面中顯示
                        c_lrc_items.Children.Add(c_lrcbk);
                    }
                }
            }
        }

        //正則表示式提取時間
        public string GetTime(string str)
        {
            Regex reg = new Regex(@"\[(?<time>.*)\]", RegexOptions.IgnoreCase);
            return reg.Match(str).Groups["time"].Value;
        }

        #endregion

接下來是歌詞滾動以及判斷音樂進度對應的歌詞

輸入當前播放的音樂所在時間後,找到對應的歌詞,將其標亮,scrollviewer滾動到相對於歌詞整體中間的位置。

 #region 歌詞滾動

        public void LrcRoll(string nowtime)
        {
            nowtime = "00:" + nowtime;
            if (FoucsLrcLocation < 0)
            {
                //音樂開始時歌詞焦點到第一句
                FoucsLrcLocation = 0;
                Lrcs.Values.ToList()[FoucsLrcLocation].c_LrcTb.Foreground = FoucsLrcColor;
            }
            else
            {
                //迴圈獲取歌詞
                for (int i = FoucsLrcLocation + 1; i < Lrcs.Values.Count; i++)
                {
                    LrcModel lrc = Lrcs.Values.ToList()[i];

                    //調整格式方便計算
                    string lrctime = "00:" + lrc.Time;

                    //計算當前音樂播放時間與歌詞時間的差值,大於等於0時代表到達歌詞位置
                    double s = DateDiff(Convert.ToDateTime(nowtime), Convert.ToDateTime(lrctime));
                    if (s >= 0)
                    {
                        //取消當前焦點歌詞
                        Lrcs.Values.ToList()[FoucsLrcLocation].c_LrcTb.Foreground = NoramlLrcColor;
                        //給歌詞控制元件設定顏色突出顯示
                        lrc.c_LrcTb.Foreground = FoucsLrcColor;
                        //重新設定當前歌詞位置
                        FoucsLrcLocation = i;
                        ResetLrcviewScroll();
                        //Debug.WriteLine("nowtime:" + nowtime + ",lrctime:" + lrctime + ",s:" + s);
                        break;
                    }

                }
            }


        }

        //計算時間差
        public double DateDiff(DateTime DateTime1, DateTime DateTime2)

        {

            double dateDiff = 0;

            try

            {

                TimeSpan ts1 = new TimeSpan(DateTime1.Ticks);

                TimeSpan ts2 = new TimeSpan(DateTime2.Ticks);

                TimeSpan t = ts1 - ts2;

                TimeSpan ts = ts1.Subtract(ts2).Duration();

               

                dateDiff = t.Seconds + t.Milliseconds;
            }

            catch

            {

            }

            return dateDiff;

        }

        #endregion

        #region 調整歌詞控制元件滾動條位置
        public void ResetLrcviewScroll()
        {
            //獲得焦點歌詞位置
            GeneralTransform gf = Lrcs.Values.ToList()[FoucsLrcLocation].c_LrcTb.TransformToVisual(c_lrc_items);
            Point p = gf.Transform(new Point(0, 0));
            //滾動條當前位置
            Debug.WriteLine(c_scrollviewer.VerticalOffset + "/" + p.Y);

            //計算滾動位置(p.Y是焦點歌詞控制元件(c_LrcTb)相對於父級控制元件c_lrc_items(StackPanel)的位置)
            //拿焦點歌詞位置減去滾動區域控制元件高度除以2的值得到的【大概】就是歌詞焦點在滾動區域控制元件的位置
            double os = p.Y - (c_scrollviewer.ActualHeight / 2) + 10;
            //滾動
            c_scrollviewer.ScrollToVerticalOffset(os);

        }
        #endregion

控制元件部分寫完了,現在重新生成專案,我們呼叫試試效果。

開啟MainWindow.xaml,修改為:

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:網易雲音樂歌詞顯示控制元件"
        xmlns:Controls="clr-namespace:網易雲音樂歌詞顯示控制元件.Controls" x:Class="網易雲音樂歌詞顯示控制元件.MainWindow"
        mc:Ignorable="d"
        Title="網易雲音樂歌詞顯示控制元件" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="80"></RowDefinition>
            <RowDefinition Height="*"></RowDefinition>

        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <StackPanel Height="25" Orientation="Horizontal">
                <Button Name="pbtn" Width="100" Content="播放" Click="Button_Click"></Button>
                <Button Name="sbtn" Width="100" Content="暫停" Click="Button_Click_1"></Button>
                <TextBlock Name="metime" VerticalAlignment="Center" Text="00:00"></TextBlock>
                <MediaElement Name="me" LoadedBehavior="Manual"/>
            </StackPanel>
        </Grid>
        <Controls:LrcView x:Name="LrcView" Grid.Row="1" Margin="10"/>


    </Grid>
</Window>

後臺程式碼:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Media;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace 網易雲音樂歌詞顯示控制元件
{
    /// <summary>
    /// MainWindow.xaml 的互動邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        DispatcherTimer dt;
       
        public MainWindow()
        {
            InitializeComponent();

            string lrc = "[00:04.00]原曲: ヤキモチ\n\n\n\n[00:20.00]後期: 聖雨輕紗\n\n[00:24.00]海報:不 鹹\n\n\n\n[00:28.64]這一路上走走停停 順著少年漂流的痕跡\n\n[00:35.11]邁出車站的前一刻 竟有些猶豫\n\n[00:41.08]不禁笑這近鄉情怯 仍無可避免\n\n[00:46.49]而長野的天 依舊這麼暖 風吹起了從前\n\n[00:52.02]從前初識這世間 萬般流連 看著天邊似在眼前\n\n[00:59.50]也甘願赴湯蹈火去走它一遍\n\n[01:04.52]如今走過這世間 萬般流連 翻過歲月不同側臉\n\n[01:11.75]措不及防闖入你的笑顏\n\n[01:17.37]我曾難自拔於世界之大 也沉溺於其中夢話 不得真假 不做掙扎 不懼笑話\n\n[01:30.39]我曾將青春翻湧成她 也曾指尖彈出盛夏 心之所動 且就隨緣去吧\n\n[01:42.40]逆著光行走 任風吹雨打\n\n[01:49.89]-M-\n\n[01:59.14]短短的路走走停停 也有了幾分的距離\n\n[02:05.20]不知撫摸的是故事 還是段心情\n\n[02:11.22]也許期待的不過是 與時間為敵\n\n[02:16.94]再次看到你 微涼晨光裡 笑的很甜蜜\n\n[02:22.29]從前初識這世間 萬般流連 看著天邊似在眼前\n\n[02:29.58]也甘願赴湯蹈火去走它一遍\n\n[02:34.42]如今走過這世間 萬般流連 翻過歲月不同側臉\n\n[02:41.87]措不及防闖入你的笑顏\n\n[02:47.23]我曾難自拔於世界之大 也沉溺於其中夢話 不得真假 不做掙扎 不懼笑話\n\n[03:00.13]我曾將青春翻湧成她 也曾指尖彈出盛夏 心之所動 且就隨緣去吧\n\n[03:15.51]-=-\n\n[03:38.30]晚風吹起你鬢間的白髮 撫平回憶留下的疤 你的眼中 明暗交雜 一笑生花\n\n[03:50.53]暮色遮住你蹣跚的步伐 走進床頭藏起的畫 畫中的你 低著頭說話\n\n[04:03.05]我仍感嘆於世界之大 也沉醉於兒時情話 不剩真假 不做掙扎 無謂笑話\n\n[04:15.34]我終將青春還給了她 連同指尖彈出的盛夏 心之所動 就隨風去了\n\n[04:27.63]以愛之名 你還願意嗎\n\n[04:35.36]-E-";

            LrcView.LoadLrc(lrc);

            dt = new DispatcherTimer();
            dt.Interval = TimeSpan.FromSeconds(1);
            dt.Tick += (e, c) =>
            {
                string time = me.Position.ToString("mm") + ":" + me.Position.ToString("ss") + "." + me.Position.ToString("ff");

                metime.Text = me.Position.ToString("mm") + ":" + me.Position.ToString("ss");

                LrcView.LrcRoll(time);
            };

            Play(false);
            me.Source = new Uri(@"C:\CloudMusic\qfl.mp3", UriKind.RelativeOrAbsolute);
        }
        void Play(bool s)
        {
            if (s)
            {
                sbtn.IsEnabled = true;
                pbtn.IsEnabled = false;
            }
            else
            {
                sbtn.IsEnabled = false;
                pbtn.IsEnabled = true;
            }
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Play(true);

            dt.Start();
            me.Play();
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            Play(false);

            dt.Stop();
            me.Pause();
        }
    }
}

這裡用了一個計時器,在播放音樂開始時啟動,每隔1秒執行一次控制元件的LrcRoll方法實現歌詞標記和焦點滾動。

啟動程式看看效果吧~

效果看似實現了,但是發現了新的問題,如果當“使用者調整了音樂的進度”後,歌詞顯示將完全亂套。明天再修復吧

 - 2018年1月11日16:39:10

 

繼續昨天的優化(修復)

1,將歌詞模型的時間屬性資料型別修改為double。歌詞的位置計算將用總毫秒數去判斷,將音樂的播放進度轉換為毫秒數,載入歌詞時也將歌詞的對應時間轉為毫秒數,通過進度的毫秒數去查詢相應的歌詞;

2,修改變數部分程式碼:

//歌詞集合
        public Dictionary<double, LrcModel> Lrcs = new Dictionary<double, LrcModel>();

        //添加當前焦點歌詞變數
        public LrcModel foucslrc { get; set; }

3,修改核心部分程式碼:

  #region 載入歌詞
        public void LoadLrc(string lrcstr)
        {
            //迴圈以換行\n切割出歌詞
            foreach (string str in lrcstr.Split('\n'))
            {
                //過濾空行,判斷是否存在時間
                if (str.Length > 0 && str.IndexOf(":") != -1)
                {
                    //歌詞時間
                    TimeSpan time = GetTime(str);
                    //歌詞取]後面的就行了
                    string lrc = str.Split(']')[1];



                    //歌詞顯示textblock控制元件
                    TextBlock c_lrcbk = new TextBlock();
                    //賦值
                    c_lrcbk.Text = lrc;
                    if (c_lrc_items.Children.Count > 0)
                    {
                        //增加一些行間距,see起來不那麼擁擠~
                        c_lrcbk.Margin = new Thickness(0, 10, 0, 0);
                    }

                    //新增到集合,方便日後操作
                    Lrcs.Add(time.TotalMilliseconds, new LrcModel()
                    {
                        c_LrcTb = c_lrcbk,
                        LrcText = lrc,
                        Time = time.TotalMilliseconds

                    });

                    //將歌詞顯示textblock控制元件新增到介面中顯示
                    c_lrc_items.Children.Add(c_lrcbk);

                }
            }
        }

        //正則表示式提取時間

        public TimeSpan GetTime(string str)
        {
            Regex reg = new Regex(@"\[(?<time>.*)\]", RegexOptions.IgnoreCase);
            string timestr = reg.Match(str).Groups["time"].Value;

            //獲得分
            int m = Convert.ToInt32(timestr.Split(':')[0]);
            //判斷是否有小數點
            int s = 0, f = 0;
            if (timestr.Split(':')[1].IndexOf(".") != -1)
            {
                //
                s = Convert.ToInt32(timestr.Split(':')[1].Split('.')[0]);
                //獲得毫秒位
                f = Convert.ToInt32(timestr.Split(':')[1].Split('.')[1]);

            }
            else
            {
                //沒有
                s = Convert.ToInt32(timestr.Split(':')[1]);

            }
            Debug.WriteLine(m + "-" + s + "-" + f + "->" + new TimeSpan(0, 0, m, s, f).TotalMilliseconds);
            return new TimeSpan(0, 0, m, s, f);

        }

        #endregion

        #region 歌詞滾動
        /// <summary>
        /// 歌詞滾動、定位焦點
        /// </summary>
        /// <param name="nowtime"></param>
        public void LrcRoll(double nowtime)
        {
            if (foucslrc == null)
            {
                foucslrc = Lrcs.Values.First();
                foucslrc.c_LrcTb.Foreground = FoucsLrcColor;
            }
            else
            {


                //查詢焦點歌詞
                IEnumerable<KeyValuePair<double, LrcModel>> s = Lrcs.Where(m => nowtime>=m.Key);
                if (s.Count() > 0)
                {
                    LrcModel lm = s.Last().Value;
                    foucslrc.c_LrcTb.Foreground = NoramlLrcColor;

                    foucslrc = lm;
                    foucslrc.c_LrcTb.Foreground = FoucsLrcColor;
                    //定位歌詞在控制元件中間區域
                    ResetLrcviewScroll();
                }
            }

            //if (FoucsLrcLocation < 0)
            //{
            //    //音樂開始時歌詞焦點到第一句
            //    FoucsLrcLocation = 0;
            //    Lrcs.Values.ToList()[FoucsLrcLocation].c_LrcTb.Foreground = FoucsLrcColor;
            //}
            //else
            //{
            //    //迴圈獲取歌詞
            //    for (int i = FoucsLrcLocation + 1; i < Lrcs.Values.Count; i++)
            //    {
            //        LrcModel lrc = Lrcs.Values.ToList()[i];



            //        //計算當前音樂播放時間與歌詞時間的差值

            //        if ()
            //        {
            //            //取消當前焦點歌詞
            //            Lrcs.Values.ToList()[FoucsLrcLocation].c_LrcTb.Foreground = NoramlLrcColor;
            //            //給歌詞控制元件設定顏色突出顯示
            //            lrc.c_LrcTb.Foreground = FoucsLrcColor;
            //            //重新設定當前歌詞位置
            //            FoucsLrcLocation = i;
            //            ResetLrcviewScroll();
            //            //Debug.WriteLine("nowtime:" + nowtime + ",lrctime:" + lrctime + ",s:" + s);
            //            break;
            //        }

            //    }
            //}


        }



        #endregion

        #region 調整歌詞控制元件滾動條位置
        public void ResetLrcviewScroll()
        {
            //獲得焦點歌詞位置
            GeneralTransform gf = foucslrc.c_LrcTb.TransformToVisual(c_lrc_items);
            Point p = gf.Transform(new Point(0, 0));
            //滾動條當前位置
            Debug.WriteLine(c_scrollviewer.VerticalOffset + "/" + p.Y);

            //計算滾動位置(p.Y是焦點歌詞控制元件(c_LrcTb)相對於父級控制元件c_lrc_items(StackPanel)的位置)
            //拿焦點歌詞位置減去滾動區域控制元件高度除以2的值得到的【大概】就是歌詞焦點在滾動區域控制元件的位置
            double os = p.Y - (c_scrollviewer.ActualHeight / 2) + 10;
            //滾動
            c_scrollviewer.ScrollToVerticalOffset(os);

        }
        #endregion

為了能看到調整音樂進度時歌詞的顯示效果,我們需要在mainwindow.xaml加入一個slider控制音樂的進度並且修改一下後臺程式碼。

 <Slider IsSnapToTickEnabled="True" TickFrequency="1"  Name="sd" Height="20" VerticalAlignment="Top"  />

後臺程式碼:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Media;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace 網易雲音樂歌詞顯示控制元件
{
    /// <summary>
    /// MainWindow.xaml 的互動邏輯
    /// </summary>
    public partial class MainWindow : Window
    {
        DispatcherTimer dt;

        public MainWindow()
        {
            InitializeComponent();

            string lrc = "[00:04.00]原曲: ヤキモチ\n\n\n\n[00:20.00]後期: 聖雨輕紗\n\n[00:24.00]海報:不 鹹\n\n\n\n[00:28.64]這一路上走走停停 順著少年漂流的痕跡\n\n[00:35.11]邁出車站的前一刻 竟有些猶豫\n\n[00:41.08]不禁笑這近鄉情怯 仍無可避免\n\n[00:46.49]而長野的天 依舊這麼暖 風吹起了從前\n\n[00:52.02]從前初識這世間 萬般流連 看著天邊似在眼前\n\n[00:59.50]也甘願赴湯蹈火去走它一遍\n\n[01:04.52]如今走過這世間 萬般流連 翻過歲月不同側臉\n\n[01:11.75]措不及防闖入你的笑顏\n\n[01:17.37]我曾難自拔於世界之大 也沉溺於其中夢話 不得真假 不做掙扎 不懼笑話\n\n[01:30.39]我曾將青春翻湧成她 也曾指尖彈出盛夏 心之所動 且就隨緣去吧\n\n[01:42.40]逆著光行走 任風吹雨打\n\n[01:49.89]-M-\n\n[01:59.14]短短的路走走停停 也有了幾分的距離\n\n[02:05.20]不知撫摸的是故事 還是段心情\n\n[02:11.22]也許期待的不過是 與時間為敵\n\n[02:16.94]再次看到你 微涼晨光裡 笑的很甜蜜\n\n[02:22.29]從前初識這世間 萬般流連 看著天邊似在眼前\n\n[02:29.58]也甘願赴湯蹈火去走它一遍\n\n[02:34.42]如今走過這世間 萬般流連 翻過歲月不同側臉\n\n[02:41.87]措不及防闖入你的笑顏\n\n[02:47.23]我曾難自拔於世界之大 也沉溺於其中夢話 不得真假 不做掙扎 不懼笑話\n\n[03:00.13]我曾將青春翻湧成她 也曾指尖彈出盛夏 心之所動 且就隨緣去吧\n\n[03:15.51]-=-\n\n[03:38.30]晚風吹起你鬢間的白髮 撫平回憶留下的疤 你的眼中 明暗交雜 一笑生花\n\n[03:50.53]暮色遮住你蹣跚的步伐 走進床頭藏起的畫 畫中的你 低著頭說話\n\n[04:03.05]我仍感嘆於世界之大 也沉醉於兒時情話 不剩真假 不做掙扎 無謂笑話\n\n[04:15.34]我終將青春還給了她 連同指尖彈出的盛夏 心之所動 就隨風去了\n\n[04:27.63]以愛之名 你還願意嗎\n\n[04:35.36]-E-";
            //載入歌詞
            LrcView.LoadLrc(lrc);
            //初始化計時器
            dt = new DispatcherTimer();
            /*
             * 2018年1月12日10:04:48
             * 1秒有些延遲調整為0.5秒
             * 
             * */
            dt.Interval = TimeSpan.FromSeconds(0.5);

            dt.Tick += (e, c) =>
            {
                LrcRoll();
            };

            //設定按鈕狀態
            Play(false);
            me.Source = new Uri(@"C:\CloudMusic\qfl.mp3", UriKind.RelativeOrAbsolute);
            //設定為pause時才能達到啟動應用後馬上載入音樂以獲取總時長
            me.LoadedBehavior = MediaState.Pause;
            //載入音樂後獲取總時長
            me.MediaOpened += (e, c) =>
            {
                //將總時長轉換為秒單位
                sd.Maximum = (me.NaturalDuration.TimeSpan.Minutes * 60) + me.NaturalDuration.TimeSpan.Seconds;
                //重新設定為手動播放模式,否則無法播放音樂
                me.LoadedBehavior = MediaState.Manual;
            };


            //監聽slider的滑鼠釋放事件,即拖動時不調整音樂的進度,拖動後再調整
            sd.PreviewMouseLeftButtonUp += (e, c) =>
            {
                STime st = GetStime(sd.Value);
                me.Position = new TimeSpan(0, st.m, st.s);

                LrcRoll();

            };
            //對slider的值變化監聽,修改時可實時顯示調整的時間
            sd.ValueChanged += (e, c) =>
            {
                STime st = GetStime(sd.Value);
                TimeSpan ts = new TimeSpan(0, st.m, st.s);
                metime.Text = ts.ToString("mm") + ":" + ts.ToString("ss");
            };
        }
        void LrcRoll()
        {

            metime.Text = me.Position.ToString("mm") + ":" + me.Position.ToString("ss");

            LrcView.LrcRoll(me.Position.TotalMilliseconds);
        }
        void Play(bool s)
        {
            if (s)
            {
                sbtn.IsEnabled = true;
                pbtn.IsEnabled = false;
            }
            else
            {
                sbtn.IsEnabled = false;
                pbtn.IsEnabled = true;
            }
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Play(true);

            dt.Start();
            me.Play();
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            Play(false);

            dt.Stop();
            me.Pause();
        }


        public class STime
        {
            public int m { get; set; }
            public int s { get; set; }
        }
        public STime GetStime(double v)
        {
            var st = new STime();

            TimeSpan ts = new TimeSpan(0, 0, Convert.ToInt32(v));
            //得到多少分鐘
            st.m = ts.Minutes;
            st.s = ts.Seconds;
            return st;
        }


    }
}

OK,搞定,看下效果視訊

我發現寫個歌詞控制元件都快寫成一個完整的播放器了(逃

呼叫控制元件步驟

第一步,載入歌詞部分字串:

public void LoadLrc(string lrcstr);

將網音的歌詞介面資料的lyric部分複製下來填到lrcstr引數(這裡要注意的是這個控制元件並不完整,沒有對一些地方進行處理,如:專案中獲取到的歌詞開始的地方有幾個代表音樂製作人等一些資訊的引數,不處理的話會導致後面的時間獲取失敗)

第二步,播放音樂實時 / 調整進度的時候都要呼叫LRCROLL方法更新歌詞和焦點滾動:

 public void LrcRoll(double nowtime);

其中nowtime即音樂當前進度的總毫秒數

 

專案下載:

點我下載

 - 2018年1月12日10:19:11

 

關於如何做到酷狗那樣的歌詞逐字描色效果我的思路是這樣的,在歌詞結構中的每句歌詞都標記上時間段(即某個字到某個字所需要的時間),然後在程式中讀取時間段,根據時間段給時間段內的歌詞上色。至於怎麼一個字一個字地上色我暫時不知道。

 - 2018年1月13日08:53:26

 

實現了歌詞逐字描色,在這篇部落格C# WPF 歌詞逐字定位描色效果實現

 - 2018年4月23日