复读机,(原创)C# LRC复读机-1

最近想恶补下英语,找了很多MP3播放软件,但是没有哪个能按照句子播放时间来播放声音的,想想自己学习过的WinForm,为什么不自己写一个程序呢?一个想法出现在脑海中,说干就干,花了一天时间,先研究了一下LRC文件的结构,windows media player控件的使用,加上一些简单的控制,这个简陋的LRC复读机就诞生了!在网上得到很多朋友的帮助,现将全部源程序公开,以表谢意,您可以随意使用,修改.如果程序造成了死机及至电脑起火爆炸,很抱歉,不是我运行的!
代码很简单,思路那就更简单,有句英文怎么说呢:it’s ugly, it’s simple, but it works!image复读机,(原创)C# LRC复读机-1
这个复读机功能如下:
可以播放带有lrc文件的mp3文件,按照每个句子的的时间来进行声音片段的播放.
使用方法:
先点击选择文件,选择一个有配套LRC的mp3,注意文件名必须相同,不然程序会报错
imageimage复读机,(原创)C# LRC复读机-1
然后,程序就会自动载入LRC文件,并将文本内容读取到右边的listBox中,你可以在listBox中双击,键盘上下,回车什么的随便你,总之,想听哪句点哪句,读完就暂停了.或者你将英文显示关掉,只用上一句和下一句控制朗读,觉得一句一句听得不过瘾,就点全部播放,相信到这儿你看出来了,这个小软件就是用来练习听力的
要开发,先研究最基本的东西,这个小软件需要用到lrc,为什么用它呢?
先说说LRC文件的结构:
比如这个新概念的一个MP3的LRC文件内容
[al:新概念英语(二)]
[ar:MP3 同步字幕版(美音)]
[ti:A Private Conversation]
[by:更多学习内容,请到VeryCD.com搜索“露珠”]
[00:09.77]Why did the writer complain to the people behind him?
[00:15.91]Last week I went to the theatre.
[00:19.39]I had a very good seat.
[00:22.07]The play was very interesting.
[00:25.10]I did not enjoy it.
[00:27.43]A young man and a young woman were sitting behind me.
[00:31.96]They were talking loudly.
[00:34.59]I got very angry.
[00:37.41]I could not hear the actors.
[00:40.64]I turned round.
[00:42.80]I looked at the man and the woman angrily.
[00:47.27]They did not pay any attention.
[00:50.69]In the end, I could not bear it.
[00:54.98]I turned round again.
[00:57.75]'I can't hear a word!' I said angrily.
[01:03.45]'It's none of your business,'
[01:06.27]the young man said rudely.
[01:09.00]'This is a private conversation!'

前四行不用看,咱们关心的是下面,[]内部就是时间了,比如第一行[00:09.77]表示从09.77秒开始,在下面一句的15.91秒处结束;
[00:15.91]Last week I went to the theatre.这行从15.91秒处开始,在下面一句19.39秒处结束,下面的都一样,最后一句
[01:09.00]'This is a private conversation!'从1分9秒开始,结束时间?mp3播放的结束时间
现在的问题是:如何取得时间以及文本的内容呢?
先说时间,全部都是分钟在前,秒在后,格式都一样[??:??.??],这种非常规律的文本用正则表达式来取值是最简单不过了,如果你还不懂正则表达式,请先去学习了再继续往下看:
\d{2}:\d{2}.\d{2}就可以得到整个时间字符串,把全部的时间取出来,再做成时间对,比如
00:09.77 00:15.91
00:15.91 00:19.39
以开始时间和结束时间命名,这样就可以用一个struct来保存每一对:
struct 时间对 { public double 开始时间; public double 结束时间; //如果想在listBox中显示开始时间和结束时间,需要重载ToString方法 public override string ToString() { return string.Format("{0,8}-{1,8}", 开始时间, 结束时间); } }
有了时间对,咱们不就可以在WMP中按照某一段时间来播放了?在WMP的研究中发现要想在WMP中从09.77秒处播放,需要使用double数据类型!
imageimageimage复读机,(原创)C# LRC复读机-1
如上图所示Ctlcontrols.currentPosition只接受double,这个double和时间是个什么关系?网上没有找到相关的资料,只好自己做实验来研究了,弄了个mp3,给这个currentPosition赋值,发现其实很简单,就是个秒数,比如currentPosition=09.77,播放条一下就跑到09.77秒处了;那么1分9秒呢?1*60+9,OK,知道如何来控制播放的时间了,讲这些是因为需要将01:09.00这种字符串转换成double类型,需要单独取出分钟01和秒数09.00,奈何?
正则表达式可以从一个匹配字符串中一次取出二个,上面的\d{2}:\d{2}.\d{2},改成(\d{2}):(\d{2}.\d{2})就可以了,然后在程序中取出这二组,转换成double即可:
private static double 获取匹配时间(string 内容来源) { Regex 时间匹配 = new Regex("(\\d{2}):(\\d{2}.\\d{2})", RegexOptions.None); if (时间匹配.IsMatch(内容来源)) { Match ma = 时间匹配.Match(内容来源); double 分钟 = Convert.ToDouble(ma.Groups[1].Value); double 秒数 = Convert.ToDouble(ma.Groups[2].Value); return 分钟 * 60 + 秒数; } else { return -1; } }
有了能够取出一个字符串中的时间的方法,再使用一个File.ReadAllLines将lrc文件的每一行都读取进一个string[]中,弄一个循环就可以得到整个lrc文件的时间对:
public List<时间对> 得到时间对列表() { List<时间对> 时间对列表 = new List<时间对>(); 时间对 coupleTime = new 时间对(); //读取LRC文件的每一行并获取时间 for (int i = 0; i < LRC文件内容.Length - 1; i++) { if (获取匹配时间(LRC文件内容[i]) < 0)//没有找到匹配项,继续 { continue; } else { coupleTime.开始时间 = 获取匹配时间(LRC文件内容[i]); coupleTime.结束时间 = 获取匹配时间(LRC文件内容[i + 1]); 时间对列表.Add(coupleTime); } } //最后一个结束时间需要用MP3的总长度赋值,或者偷懒,double.MaxValue:) coupleTime.开始时间 = 获取匹配时间(LRC文件内容[LRC文件内容.Length - 1]); coupleTime.结束时间 = double.MaxValue; 时间对列表.Add(coupleTime); return 时间对列表; } OK,时间的问题解决掉;文本内容如何得到呢?其实也可以和时间一起获取,效率高,不过考虑到正则表达式不好写,再加上反正lrc文件小,现在计算机速度快,用简单清晰略带冗余的方式来处理好喽:
private static string 获取匹配英文短语(string 内容来源) { //注意,如果正则是[a-zA-Z\s\.\?'!,]+$,也就是没有两边的() //那么取的值就是空,因为并没有组存在! //要使用Groups来获取,必须在两边加上() //Regex 文字匹配 = new Regex(@"([a-zA-Z\d\s\.\?'!,_-]+$)", RegexOptions.None); string 纯英文LRC=@"([a-zA-Z\s\.\?'’!,,_\d-]+$)"; string 中英文LRC=@"";//留一个位置,以后还可以读取带中文翻译的LRC string regexString=纯英文LRC+中英文LRC; Regex 文字匹配 = new Regex(regexString, RegexOptions.Multiline); if (文字匹配.IsMatch(内容来源)) { Match ma = 文字匹配.Match(内容来源); return string.IsNullOrEmpty(ma.Groups[1].Value) ? ma.Groups[2].Value : ma.Groups[1].Value; } else { return null; } //这样也可以得到正确的值 //Regex 文字匹配 = new Regex(@"[a-zA-Z\s\.\?'!,]+$", RegexOptions.None); //if (文字匹配.IsMatch(内容来源)) //{ // Match ma = 文字匹配.Match(内容来源); // return ma.Value; //} //else //{ // return null; //} }
这个获取有一点儿小小问题,就是只能获取到仅仅含有英文的lrc文件,如果有中文,那么就只会得到null,因为正则表达式([a-zA-Z\d\s\.\?'!,_-]+$)最后是以$结束的.
比如这种lrc文件:
[00:00.440]--- lesson1 SCHOOLS AND EDUCATION 学校和教育
[00:06.160]--- Basic Patterns 基本句型
[00:09.160]What college should I attend in the fall?^秋季我上哪一所大学好呢?
[00:14.080]I’m majoring in history.^我学的是历史专业。
[00:17.880]Can you tell me where is the dormitory?^你能告诉我宿舍在哪吗?
[00:22.240]I’m taking extra classes this semester.^这个学期我额外选修了一些课程。
[00:26.720]Are you having problems with chemistry?^你在化学方面有困难吗?
[00:30.760]I’m not worried about this test.^我并不担心这次考试。
[00:34.520]The prices _disibledevent=>有非常懂正则表达式的还请赐教,如何从上面的字符串中一次性取得英文字符串和中文字符串呢?我自己研究了一下,这个英文标点和中文标点可真是让人头大,少写一个或者写错,什么也得不到!
待续...
Tags:  复读机软件 复读机的谜语 步步高复读机 复读机

延伸阅读

最新评论

发表评论