回 帖 发 新 帖 刷新版面

主题:[原创]在自制播放器中使用和制作 Lrc 歌词文件

在自制播放器中使用和制作 Lrc 歌词文件

  如果我们用 MMControl控件制作了一个播放器,在播放乐曲的同时,一定也想显示歌词。目前比较
流行的是.Lrc歌词文件,它实际上也是纯文本文件,但它在每句歌词前面都加上了时间参数,这样,利
用 MMContro l控件的 Position 属性,我们就可以实时显示相应的歌词。
  先来了解一下.Lrc歌词文件的基本结构,例如:

[ti:狼爱上羊]
[ar:一江秋水制作]
[al:2008年1月28日]
[00:00.01]《狼爱上羊》(歌手:汤潮)
[00:29.97]北风呼呼地刮
[00:33.19]雪花飘飘洒洒
……

  我们看到,前面三行(有些 Lrc文件可能有四、五行)是歌词文件的一些信息,无关紧要,所以我
们在使用自制的播放器播放或制作歌词文件时,就可以去掉这些内容,后面才是正式的歌词句,在每一
歌词句的前面,都用方括号表示了该句的显示时间,时间格式是分、秒、毫秒,都用两位数字表示。
  但是,我们从网上下载的 Lrc文件,常常与实际的歌曲衔接不上,这是因为 Lrc文件的制作者所使
用的歌曲版本与我们所使用的不一致,造成时间参数与歌曲进度不同步。例如上面那首《狼爱上羊》,
第一个歌词句的显示时间是29秒970毫秒,这是因为我从网上下载的歌曲前面有29秒多的过门,过后才是
歌手的声音,如果你下载的《狼爱上羊》前面只有15秒的过门,那么这个 Lrc文件就显然会与歌曲衔接
不上。所以,我们自制的播放器不但要能够显示 Lrc歌词文件,还要能够制作它。


一、显示 Lrc 歌词的方法

  在显示歌词的代码中,首先取出时间字串,转换成毫秒,再与Position属性比较,如果相差不多,
就将时间字串后面的歌词句字串显示出来。
  MMControl 控件的 Position属性的作用是:根据指定的 TimeFormat返回已打开的设备的位置,一
般用四字节表示,只读。在使用Position属性之前,要先设置属性 TimeFormat=0,这样Position属性
返回的时间单位就是毫秒了。
  歌词句是显示在屏幕下方的(如果你想显示在播放器窗体中,那也只要进行简单的修改就行了),
并且从右向左迅速移动,最终停止在屏幕左下方。
  好,我们就开始试验吧。
  
  新建一个工程,添加两个窗体Form1和Form3。Form1是播放器窗体,在上面要添加一个 MMControl
控件(将其名称改为MMC),一个 CommonDialog 控件(改其名称为 CD)、一个按纽控件,和其它你认
为需要的控件。Form3是显示歌词用的,在上面添加一个标签和一个计时器,属性设置为:

Form3窗体:
----------
BackColor:黑色
BorderStyle:0
Height:495


Label1标签:
-----------
Font:楷体_GB2312,小一号,粗体
ForeColor:白色
AutoSize:True
BackStyle:0
Height:495
Top:0


Timer1计时器:
------------
Inteval:20


模块的代码:
------------------------------------------------------------------------------------------
Declare Function SetLayeredWindowAttributes Lib "user32" (ByVal hWnd As Long, _
  ByVal crKey As Long, ByVal bAlpha As Byte, ByVal dwFlags As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd _
  As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Declare Function SetWindowPos Lib "user32" (ByVal hWnd As Long, ByVal _
  hWndInsertAfter As Long, ByVal X As Long, ByVal Y As Long, ByVal cx As Long, _
  ByVal cy As Long, ByVal wFlags As Long) As Long

Public ST As String       '歌词文本
Public BJForm3 As Boolean 'Form3调出标记
------------------------------------------------------------------------------------------


Form1的有关代码:
------------------------------------------------------------------------------------------
Option Explicit

Dim BJMode As Boolean  'True-显示歌词;False-制作歌词

Private Sub Command1_Click() '读 lrc 文件
On Error GoTo 100
Dim z As String
CD.Flags = &H1808
CD.DialogTitle = "打开 lrc 歌词文件"
CD.Filter = "Lrc 歌词文件(*.lrc)|*.lrc"
CD.ShowOpen
If Dir(CD.FileName) = "" Then Exit Sub
ST = ""
Open CD.FileName For Input As #1
Do Until EOF(1)
  Line Input #1, z
  If Len(z) > 0 Then ST = ST & z & "~" '用这个不常用的字符"~"作为歌词句的分隔符
Loop
BJMode = True
100
Close
End Sub

Private Sub 播放_Click() 
With MMC
  .FileName = "(MP3歌曲的全路径文件名)" '以播放MP3为例
  .Command = "Open"
  .TimeFormat = 0 '时间格式为毫秒
  .Notify = True '打开通知
  .Command = "play"
End With
If BJMode Then Form3.Show
End Sub

Private Sub MMC_Done(NotifyCode As Integer)
If NotifyCode = 1 Then  '如果播放完毕
  If BJForm3 Then Unload Form3 
  MMC.Command = "stop"
  MMC.Command = "close"
End If
End Sub

Private Sub Form_Unload(Cancel As Integer)
If BJForm3 Then Unload Form3
End Sub
------------------------------------------------------------------------------------------


Form3的代码:
------------------------------------------------------------------------------------------
Option Explicit

Dim LrcST As String '歌词句字串
Dim k1 As Long    '歌词句在文件中的起始位置
Dim k2 As Long    '歌词句的长度
Dim t As Long

Private Sub Form_Load()
SetWindowLong hWnd, -20, &H80000 
SetLayeredWindowAttributes hWnd, 0, 0, 1  '将窗体背景黑色设为透明
SetWindowPos hWnd, -1, 0, 0, 0, 0, 3&     '窗体总在最上
Move 0, Screen.Height - 540, Screen.Width '将窗体设置到屏幕下方,其宽度与屏幕宽度相同
BJForm3 = True: k1 = 1
End Sub

Private Sub Timer1_Timer()
Dim s As String
If t < Form1.MMC.Position Then
  Do 
    k2 = InStr(k1, ST, "~")
    LrcST = Mid$(ST, k1, k2 - k1)             '从歌词句中取出含有方括号的字串
    k1 = IIf(k2 + 10 > Len(ST), 1, k2 + 1)    '设置下一个歌词句在文件中的起始位置
    s = Mid$(LrcST, 2, InStr(LrcST, "]") - 2) '取出去掉了方括号的字串
  Loop Until IsNumeric(Left$(s, 2))           '如果是时间字串就退出Do循环
  t = Val(s) * 60000 + Val(Mid$(s, 4)) * 1000 '将时间字串转换为毫秒
End If
If Abs(Form1.MMC.Position - t) < 200 Then     '如果歌词与播放进度的时间差小于200毫秒
  t = 0
  Label1.Left = Width
  Label1.Caption = Mid$(LrcST, InStr(LrcST, "]") + 1) '取出歌词字串
  Do
    Label1.Left = Label1.Left - 150
    DoEvents
  Loop Until Label1.Left < 300
End If
End Sub

Private Sub Form_Unload(Cancel As Integer)
BJForm3 = False
End Sub
------------------------------------------------------------------------------------------

  在 Timer1_Timer 过程中,第一个 Do 循环的目的是不显示诸如[ti:****]、[ar:****]、
[al:****]、[by:****]这样的内容,如果 Lrc文件是自己制作的,没有这些东东,那么也可以
去掉 Do 和 Loop 这两行代码。第二个 Do 循环的作用是将 Label1 标签从屏幕右外方移至屏幕左。


二、制作 Lrc 歌词文件的方法

  要制作 Lrc 歌词文件,首先要有同名的 TXT 歌词文件,可以在网上下载,也可以自己编制。
  在 Form1 窗体上再添加一个 Label1 标签,和两个按纽控件,Command2 的作用是读入 TXT歌词文
件,Command3的作用是在歌词句的前面加上含方括号的时间参数,标签的作用是将制作好的含时间参数
的歌词句显示出来。标签的 AutoSize 属性设置为 True。


Form1的有关代码:
------------------------------------------------------------------------------------------
Option Explicit

Dim LrcST As String '歌词文本
Dim k1 As Long    '歌词句在TXT歌词文件中的起始位置

Sub Command2_Click()
On Error GoTo 100
CD.Flags = &H1808
CD.DialogTitle = "打开 txt 歌词文件"
CD.Filter = "txt 歌词文件(*.txt)|*.txt"
CD.ShowOpen
If Dir(CD.FileName) <> "" Then
  Dim z As String
  ST = ""
  Open CD.FileName For Input As #1
  Do Until EOF(1)
    Line Input #1, z
    If Len(z) > 0 Then ST = ST & z & "~"
  Loop
  k1 = InStr(ST, "~") + 1
  LrcST = "[00:00.01]" & Left$(ST, k1 - 2)
  Label1.Caption = LrcST
End If
BJMode = False
100
Close
End Sub

Private Sub Command3_Click()
If k1 > Len(ST) - 2 Then
  If MsgBox("已完成,是否保存 Lrc 歌词文件?", 4, "注意") = 6 Then 保存
  Exit Sub
End If
Dim k2 As Long, s As Long, z As String
s = MMC.Position
z = Format(s \ 60000, "00") & ":" & Format((s Mod 60000) \ 1000, "00") & "." & _
  Format((s Mod 60000 Mod 1000) \ 10, "00") '将播放时间位置转换为时间字串
k2 = InStr(k1, ST, "~")
Label1.Caption = "[" & z & "]" & Mid$(ST, k1, k2 - k1)
LrcST = LrcST & vbCrLf & Label1.Caption
k1 = k2 + 1
End Sub
------------------------------------------------------------------------------------------

   Command3_Click 过程中,“保存”Lrc 歌词文件的代码请自行编写(制作好的 Lrc 歌词文本在
字符型变量 LrcST 中)。
  使用方法:首先点击 Command2,打开有关的 TXT 歌词文件,然后点击【播放】,这时候,你就要
认真听,当听到歌手将要唱到下一句时,就用鼠标点一下Command3,这样边听边点击地操作,一直到整
首歌曲唱完,最后保存。

回复列表 (共5个回复)

沙发

顶..!

板凳

如此好帖,不可不顶。
前几天我正在想,是不是做一个东西,按照歌词里每行的时间,分行发送到UC的对话框里。
你这里就坐差不多了,嘿嘿。

3 楼

[quote]如此好帖,不可不顶[/quote]

4 楼

不错的代码,但是有问题来了,针对不同的歌词不能智能区分,因为有的歌是重复 就象这样 :
[02:19.17][00:44.15]原来只要几个人
[02:22.84][00:47.60]改变了角色和位子
[02:27.32][00:52.19]一份爱
[02:29.14][00:53.93]也会完全变质
相信一江秋水会明白我说的意思,

5 楼

非常抱歉,事隔一年才答复leonkerr的帖子。
因为最近回答davecheng的问题,被迫对这个问题进行了研究,才发现原来并不需要花费太多的时间。
解决方案如下:

1.在Command1_Click过程后面加一句:

....
Loop
ST = arrangeLRC(ST) '这一句是新加的
BJMode = True
....

2.增加一段函数代码:


Private Function arrangeLRC(dat As String) As String
Dim test As String, time As String
Dim i As Integer,j As Integer, k As Integer, n As Integer, L As Integer
Dim tim1() As String, tim2() As String
Dim x As Integer 'tim2下标记数

Do: i = InStr(i + 1, Data, "["): Loop Until IsNumeric(Mid(Data, i + 1, 2)) '如果是时间字串就跳出

Do
  k = InStr(i, dat, "~")          '查找"~"符
  If k = 0 Then Exit Do
  time = Mid(dat, i, k - i)          '取出一行
  j = InStrRev(time, "]")            '从time后面查找“]”位置
  test = Mid(time, j + 1, k - j - 1) '取出time后面的歌词
  time = Mid(Left(time, j), 2)
  tim1 = Split(time, "[")
  n = UBound(tim1)                   '测试time中有几个时间参数
  ReDim Preserve tim2(x + n)
  For L = 0 To n: tim2(x) = tim1(L) & test: x = x + 1: Next
  i = k + 1
Loop Until i > Len(dat) - 10

x = UBound(tim2)
For i = 0 To x - 1        '排序
  For j = i + 1 To x
    If StrComp(tim2(j), tim2(i)) < 0 Then time = tim2(j): tim2(j) = tim2(i): tim2(i) = time
  Next
Next

For i = 0 To x: tim2(i) = "[" & tim2(i): Next
time = Join(tim2, "~") '连接
time = time & "~"
arrangeLRC = time
End Function

我来回复

您尚未登录,请登录后再回复。点此登录或注册