首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

[C++实现mp3歌词同步程序]对载入.lrc歌词文件,大家看看还可以改进不?解决方案

2012-02-22 
[C++实现mp3歌词同步程序]对载入.lrc歌词文件,大家看看还可以改进不?近日在做mp3播放与歌词同步显示程序,

[C++实现mp3歌词同步程序]对载入.lrc歌词文件,大家看看还可以改进不?


近日在做mp3播放与歌词同步显示程序,现处在控制台下测试算法阶段。

打开、播放 mp3 : mciSendCommand 
载入.lrc歌词文件 : ifstream input

.lrc相信大家都不陌生,比如下面的例子:

[ti:依赖]
[ar:蔡健雅]
[al:MY SPACE]
[by:Walkmen]
[offset:0]
[00:00.50]蔡健雅 - 依赖
[00:07.94]词、曲:蔡健雅、陶晶莹 
[00:11.60]关了灯把房间整理好 
[01:07.84][01:58.23][02:25.11][02:33.15]你对爱 已不再 想依赖
.
.
.
[04:18.60]END

我来解释一下:

[ti:歌曲名]
[ar:歌手]
[al:专辑]
[by:lrc歌词制作者]
[offset:补偿时间]
[分:秒][分:秒]歌词



[分:秒] 分和秒都必须是正数,秒可以是精确到毫秒,不同时间出现同样的歌词的话,可以写在一行。

我的思路:

打开音乐 : mciSendCommand(MCI_OPEN)
载入歌词 : ifstream input(歌词路径)
取一行: getline(input,line)
分析这一行 : 

比如 " [ti:依赖] ",string line="[ti:依赖]",用':'为分隔符提取左边和右边,
string left="ti"
string right="依赖"
因为这些都是mp3信息所以我把它们都整合到一个 string info 
我用 map<size_t,string> lrc 来保存每一行歌词出现的时间和对应的歌词
if(left == "ti")
{
  info+="歌曲名:";
  info+=right;
}
else if(left == "ar")
{
  info+="歌手:";
  info+=right;
}
else if(left == "al")
{
  info+="专辑:";
  info+=right;
}
else if(left == "by")
{
  info+="lrc歌词制作者:";
  info+=right;
}
else if(left == "offset")
{
  ;什么也不做
}
else 
{
  分=atoi(left) 秒=atoi(right)
  时间=分*60+秒
  歌词=从line中找最后一个 ']'出现的位置后面的串
  lrc[时间]=歌词
  while( 这一行还有 "[分:秒]" )
  {
  lrc[新时间]=歌词
  }
}

播放音乐 : mciSendCommand(MCI_PLAY)
显示歌词 : 控制台下,我用 Sleep(1000) 模拟定时器消息,个人觉得1秒钟一次检查就可以了,已经很精确了

while(歌曲没放完)
{
  map<size_t,string>::iterator it=lrc.find(这时的时间);
  if( it != lrc.end() )
  {
  cout显示歌词
  }
  Sleep(1000);
}




基本思路就这样,有不少问题:

1、没有检查 [ ] 配对;
2、每提取一行都判断 left == "ti"或"ar"或"al"或"by"或"offset",其实在第五行后就没有这些了,
都是[分:秒],存在不需要的步骤 ;
3、几乎没有采纳 lrc 标准的毫秒建议,atoi(right)会舍弃小数位,当然,也没必要这么精确;
4、每隔一秒遍历一遍 map,查找有没有这个时间,有的话显示值,没有的话什么也不做,
个人觉得这个思路还好,因为歌词大多都几十行,所以建立的 map 也很小,查找很快,几乎不占 CPU时间,
有没有建议或是比这更好?


其他的就没有什么问题了,大家看看有什么可以改进的,载入歌词或建立 map 或 如何更好的实现歌词同步 ?



[解决办法]
1、如果要检查,在开始的时候做,发现有问题,直接提示,后面就不放了
2、可以把这几个放一个全局变量里,保存起来
3、还是用atof好一些吧,1秒有时候可以做很多事情
4、时间上应该是有先后顺序的吧,依次向后读不行吗,当然可以预读几行出来
  这个或者有其它的方法,就是读进来之后,把时间处理后,把顺序放入数组中,后面直接用,用map没有必要
[解决办法]
这个思路应该是可以的。
没有考虑播放时快进/退和拖放这种情况哦。

关于用map, 还可以用空间换时间, 比如mp3长度为480s, 则用一个string[480]来存放每一行歌词就可以了。不过你也说了建立map很小,查找很快,所以对于这个问题,用数组可以并不能看出明显的效率上的优势。
[解决办法]
感觉最后一步让歌与内容同步有问题;
map<size_t,string>::iterator it=lrc.find(这时的时间);
“这时的时间”得开始就有个计时器,两个的比较也不会完全匹配,1秒的间隔有点大

[解决办法]
将每行按string读入,然后读取括号中的时间,与歌曲的开始做时间偏移量比较,在精确的时刻显示这个字串,然后在快进和快退的时候也是取出偏移量
[解决办法]
你怎么知道哪一秒唱了哪一句啊,你用语音识别了?
[解决办法]
if(left == "ti")
{
info+="歌曲名:";
info+=right;
}


要是英文歌曲, 出现ti的地方很多吧, 类似的地方最好改成"[ti:"
------解决方案--------------------


马个克
[解决办法]
不错。。。
[解决办法]

探讨

你怎么知道哪一秒唱了哪一句啊,你用语音识别了?

[解决办法]
探讨
引用:

你怎么知道哪一秒唱了哪一句啊,你用语音识别了?

呵呵,你没做过山寨机吧。
这个其实不难。Audio Seek而已。

[解决办法]
呵呵 改进一下吧
row_A time1
row_B time2

播放row_A时 开启Timer1 定时 time2-time1 10ms误差 几乎不占cpu

再搞个外部定时 Timer2 每隔 5秒判断 已播放时间与当前显示的计时误差 纠正即可

热点排行