iOS开发手记-仿QQ音乐播放器动态歌词的实现

最近朋友想做个音乐App,让我帮忙参考下。其中歌词动态滚动的效果,正好我之前也没做过,顺便学习一下,先来个预览效果。

demo

实现思路

歌词常见的就是lrc歌词了,我们这里也是通过解析lrc歌词文件来获取其播放参数,以实现和播放器协同。下面是我从百度音乐获取的歌词文件示例:

```[ti:冰雨]`
[ar:刘德华]
[al:笨小孩]
[00:0.05]冰雨
[00:0.94]作词:刘德华、李密 作曲:潘协庆
[00:01.23]演唱:刘德华

[00:01.37]
[00:04.79](歌手独白)
[00:17.18]我是在等待 一个女孩
[00:25.18]还是在等待沉沦苦海
[00:32.91]一个人静静发呆 没有人去管花谢花开
[00:41.03]无法肯定的爱 左右摇摆
[00:45.43]只好把心酸往深心里塞
[00:49.61]我是在等待 你的回来
[00:57.73]难道只换回一句活该
[01:05.27]一个人静静发呆
[01:09.18]两个人却有不同无奈
[01:13.15]好好的一份爱 啊怎么会慢慢变坏
[01:18.43]
[01:20.44]冷冷的冰雨在脸上胡乱的拍
[01:24.31]暖暖的眼泪跟寒雨混成一块
[01:28.32]眼前的色彩忽然被掩盖
[01:32.28]你的影子无情在身边徘徊
[01:36.30]你就像一个刽子手把我出卖
[01:40.35]我的心彷佛被剌刀狠狠地宰
[01:44.36]在悬崖上的爱 谁会愿意接受最痛的意外
[01:51.09]
[02:26.59]我是在等待你的回来
[02:35.52]难道只换回一句活该
[02:42.99]一个人静静发呆
[02:46.99]两个人却有不同无奈
[02:51.08]好好的一份爱 啊怎么会慢慢变坏
[02:56.42]
[02:58.54]冷冷的冰雨在脸上胡乱的拍
[03:02.41]暖暖的眼泪跟寒雨混成一块
[03:06.39]眼前的色彩忽然被掩盖
[03:10.31]你的影子无情在身边徘徊
[03:14.23]你就像一个刽子手把我出卖
[03:18.34]我的心彷佛被剌刀狠狠地宰
[03:22.33]在悬崖上的爱 谁会愿意接受最痛的意外
[03:28.66]
[03:34.57]冷冷的冰雨在脸上胡乱的拍
[03:38.33]暖暖的眼泪跟寒雨混成一块
[03:42.31]眼前的色彩忽然被掩盖
[03:46.32]你的影子无情在身边徘徊
[03:50.27]你就像一个刽子手把我出卖
[03:54.34]我的心彷佛被剌刀狠狠地宰
[03:58.34]悬崖上的爱 谁会敢去采
[04:02.37]还是愿意接受最痛的意外 最爱的女孩
[04:08.85]
[04:19.72]悬崖上的爱 谁会敢去采
[04:31.84]还是愿意接受最痛的意外 最爱的女孩
[04:51.75]
[05:10.60]歌手独白
[06:16.76]```

解析lrc歌词

这可能是最常见的格式了,每行为一句歌词,[]括号内为歌词对应的时间区间,所以我们首先要做的事情就是将他们提取分离出来,分别作为时间参数数组和歌词内容数组。这里我参考了一些博客的方法,解析lrc文件的代码如下:

#import <Foundation/Foundation.h> @interface LrcParser : NSObject //时间 @property (nonatomic,strong) NSMutableArray *timerArray; //歌词 @property (nonatomic,strong) NSMutableArray *wordArray; //解析歌词 -(void) parseLrc; //解析歌词 -(void) parseLrc:(NSString*)lrc; @end

实现代码

@implementation LrcParser -(instancetype) init{ self=[super init]; if(self!=nil){ self.timerArray=[[NSMutableArray alloc] init]; self.wordArray=[[NSMutableArray alloc] init]; } return self; }

-(NSString *)getLrcFile:(NSString *)lrc{ NSString* filePath=[[NSBundle mainBundle] pathForResource:lrc ofType:@"lrc"]; return [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; }

//测试示例 -(void)parseLrc{ [self parseLrc:[self getLrcFile:@"冰雨"]]; }
-(void)parseLrc:(NSString *)lrc{ NSLog(@"%@",lrc); if(![lrc isEqual:nil]){ NSArray *sepArray=[lrc componentsSeparatedByString:@"["]; NSArray *lineArray=[[NSArray alloc] init]; for(int i=0;i<sepArray.count;i++){ if([sepArray[i] length]>0){ lineArray=[sepArray[i] componentsSeparatedByString:@"]"]; if(![lineArray[0] isEqualToString:@"\n"]){ [self.timerArray addObject:lineArray[0]];
[self.wordArray addObject:lineArray.count>1?lineArray[1]:@""]; } } } } } @end

经过测试,可以将歌词顺利解析出来,下面我们要将获得歌词数据应用于控制器。

实现动态歌词页面

看了QQ音乐的滚动歌词页面后,可以知道是借助UITableView或者UIScrollView来实现的,这里我们采用UITableView来实现动态歌词界面。

- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. //歌词TableView代理 self.lrcTable.delegate=self; self.lrcTable.dataSource=self; //解析歌词 self.lrcContent=[[LrcParser alloc] init]; [self.lrcContent parseLrc]; [self.lrcTable reloadData]; //初始化播放器 [self initPlayer]; //监听播放器状态 [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateTime) userInfo:nil repeats:YES]; //载入歌词背景 UIImage *img=[UIImage imageNamed:@"wall1.jpg"]; UIImageView *bgView=[[UIImageView alloc] initWithImage:[self getBlurredImage:img]]; self.lrcTable.backgroundView=bgView; } //cell委托 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell=[self.lrcTable dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; cell.textLabel.text=self.lrcContent.wordArray[indexPath.row]; if(indexPath.row==_currentRow) cell.textLabel.textColor = [UIColor redColor]; else cell.textLabel.textColor = [UIColor whiteColor]; cell.textLabel.textAlignment = NSTextAlignmentCenter; cell.textLabel.font = [UIFont systemFontOfSize:15]; cell.backgroundColor=[UIColor clearColor]; return cell; }

这里歌词列表的背景我采用了高斯模糊的图片,高斯模糊的方法如下:

//实现高斯模糊 -(UIImage *)getBlurredImage:(UIImage *)image{ CIContext *context = [CIContext contextWithOptions:nil]; CIImage *ciImage=[CIImage imageWithCGImage:image.CGImage]; CIFilter *filter=[CIFilter filterWithName:@"CIGaussianBlur"]; [filter setValue:ciImage forKey:kCIInputImageKey]; [filter setValue:@5.0f forKey:@"inputRadius"]; CIImage *result=[filter valueForKey:kCIOutputImageKey]; CGImageRef ref=[context createCGImage:result fromRect:[result extent]]; return [UIImage imageWithCGImage:ref]; }

播放器则采用AVPlayer,其定义和初始化设置如下:

@interface ViewController : UIViewController<UITableViewDataSource,UITableViewDelegate> @property (nonatomic,strong) AVAudioPlayer *player; @end

-(void) initPlayer{ AVAudioSession *session=[AVAudioSession sharedInstance]; [session setActive:YES error:nil]; [session setCategory:AVAudioSessionCategoryPlayback error:nil]; [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; self.player=[[AVAudioPlayer alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"冰雨" withExtension:@"mp3"] error:nil]; //单曲循环 self.player.numberOfLoops=10; [self.player prepareToPlay]; [self.player play]; }

这样就为应用定义了一个音乐播放器,下面要监听播放器的时间参数,来载入对应的歌词,如下:

[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateTime) userInfo:nil repeats:YES];

根据时间更新UI

-(void) updateTime{ CGFloat currentTime=self.player.currentTime; NSLog(@"%d:%d",(int)currentTime / 60, (int)currentTime % 60); for (int i=0; i<self.lrcContent.timerArray.count; i++) { NSArray *timeArray=[self.lrcContent.timerArray[i] componentsSeparatedByString:@":"]; float lrcTime=[timeArray[0] intValue]*60+[timeArray[1] floatValue]; if(currentTime>lrcTime){ _currentRow=i; }else break; } [self.lrcTable reloadData]; [self.lrcTable scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_currentRow inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES]; }

最后编译运行,就会发现一个滚动歌词播放器就实现啦。
完整Demo项目地址:https://github.com/ChangweiZhang/AudioPlayerDemo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容