AVAudioPlayer本地音乐播放、后台播放、歌词同步,都告诉你

来源:互联网 发布:维氏瑞士军刀淘宝 编辑:程序博客网 时间:2024/05/01 14:40


后台播放配置

1、打开项目的Capabilities中的Background Models 勾选第一个选项


2727BDD5-6DB0-40D9-B4D3-BDF2509B7CFE.png

2、设置音乐后台播放的会话类型,开启远程事件(锁屏时使用)

    //设置音乐后台播放的会话类型    AVAudioSession *session = [AVAudioSession sharedInstance];    NSError *error = nil;    [session setCategory:AVAudioSessionCategoryPlayback error:&error];    if (error) {        NSLog(@"%s %@", __func__, error);    }    [session setActive:YES error:nil];    //开启远程事件(锁屏时使用)    [application beginReceivingRemoteControlEvents];

3、实现远程事件回调方法

#pragma mark 接收远程事件-(void)remoteControlReceivedWithEvent:(UIEvent *)event{    //判断是否为远程事件    if (event.type == UIEventTypeRemoteControl) {        //调用block        self.myRemoteEventBlock(event);    }}

4、处理回调block

    //锁屏后 事件    myAppDel.myRemoteEventBlock = ^(UIEvent *event)    {        switch (event.subtype) {            case UIEventSubtypeRemoteControlPlay:                [[MusicPlayTool sharedMusicPlayTool] play];                break;            case UIEventSubtypeRemoteControlPause:                [[MusicPlayTool sharedMusicPlayTool] pause];                break;            case UIEventSubtypeRemoteControlPreviousTrack:                [[MusicPlayTool sharedMusicPlayTool] prepareForPlayWithMusic:[[MusicManager sharedMusicManager] upMusicInfo]];                [[MusicPlayTool sharedMusicPlayTool] play];                break;            case UIEventSubtypeRemoteControlNextTrack:                [[MusicPlayTool sharedMusicPlayTool] prepareForPlayWithMusic:[[MusicManager sharedMusicManager] nextMusicInfo]];                [[MusicPlayTool sharedMusicPlayTool] play];                break;            default:                break;        }    };

经过上面四步,app能后台播放音乐,并且能捕捉上一首歌、下一首歌、暂停和播放事件。但是锁屏的时候并没有相关的音乐的信息。运行效果如下图。


ScreenShot_20160606170818.png

设置锁屏信息

    NSMutableDictionary *info = [NSMutableDictionary dictionary];    //设置专辑名称    info[MPMediaItemPropertyAlbumTitle] = @"网络热曲";    //设置歌曲名    info[MPMediaItemPropertyTitle] = music.musicName;    //设置歌手    info[MPMediaItemPropertyArtist] = music.songer;    //设置专辑图片    MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"song_400"]];    info[MPMediaItemPropertyArtwork] = artwork;    //设置歌曲时间    info[MPMediaItemPropertyPlaybackDuration] = @(self.player.duration);    [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = info;

运行效果


ScreenShot_20160606171219.png

歌词解析、歌词同步

目前的歌词都是lrc文件,格式如些

[ti:为情所伤][ar:庄心妍][al:][00:00.00]为情所伤[00:02.00]演唱:庄心妍[00:04.00]作词:镜喜 作曲:祁隆[00:06.00]编曲:颜小健[00:08.00]混音:王路遥[00:10.00]音乐总监:祁隆[00:12.00][00:20.02]你说不会在爱情里犯错[00:23.83]也说过会永远的爱我[00:27.60]才发现只剩下虚伪的承诺[00:31.79]用泪水把回忆包裹[00:35.41]我也曾经想把爱去闪躲[00:39.22]可摆脱不了那份寂寞[00:42.94]结果都是让我一错再错[00:47.06]被伤过的心再次漂泊[00:50.48]为什么爱留下难忍的伤[00:54.19]心里的酸楚要去遗忘[00:58.25]看见的是一道封闭的墙[01:02.05]什么时候能打开一扇窗[01:05.73]为什么爱留下难忍的伤[01:09.53]泪水无情拍打着海浪[01:13.38]谁能给我一双温暖的浆[01:17.57]不让我 再为情所伤[01:24.04][01:39.91]你说不会在爱情里犯错[01:43.90]也说过会永远的爱我[01:47.64]才发现只剩下虚伪的承诺[01:51.83]用泪水把回忆包裹[01:55.26]我也曾经想把爱去闪躲[01:59.23]可摆脱不了那份寂寞[02:02.98]结果都是让我一错再错[02:07.08]被伤过的心再次漂泊[02:10.50]为什么爱留下难忍的伤[02:14.17]心里的酸楚要去遗忘[02:18.11]看见的是一道封闭的墙[02:21.96]什么时候能打开一扇窗[02:25.69]为什么爱留下难忍的伤[02:29.43]泪水无情拍打着海浪[02:33.38]谁能给我一双温暖的浆[02:37.32]不让我 再为情所伤[02:40.89]为什么爱留下难忍的伤[02:44.68]心里的酸楚要去遗忘[02:48.65]看见的是一道封闭的墙[02:52.48]什么时候能打开一扇窗[02:56.19]为什么爱留下难忍的伤[03:00.01]泪水无情拍打着海浪[03:03.94]谁能给我一双温暖的浆[03:07.97]不让我 再为情所伤[03:14.57]

为了简单,不考虑上面的[ti:为情所伤]、[ar:庄心妍]、[al:]这个快内容。其余下面的部分都是采用时间+当前时间开始的歌词。那么解析思路就是把一句的时间(转换成秒)跟当前的歌词分离出来然后用字典保存,由于字典是无序的,所以再用一个数字一次保存当前的时间。代码如下:

//解析歌词    MusicInfo *currentModel = [MusicManager sharedMusicManager].currentMusic;    [self.lrcTimeArr removeAllObjects];    [self.lrcDic removeAllObjects];    if (currentModel.lrc.length>0) {        NSString *str = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:currentModel.lrc ofType:nil] encoding:NSUTF8StringEncoding error:nil];        NSArray *allRowArr = [str componentsSeparatedByString:@"\n"];        for (int i = 0; i < allRowArr.count; i++) {            NSString *linStr = [allRowArr objectAtIndex:i];            NSArray *lineArray = [linStr componentsSeparatedByString:@"]"];            if ([lineArray[0] length] > 4) {                NSString *str1 = [linStr substringWithRange:NSMakeRange(3, 1)];                NSString *str2 = [linStr substringWithRange:NSMakeRange(6, 1)];                NSString *str3 = [linStr substringWithRange:NSMakeRange(1, 1)];                if (([str1 isEqualToString:@":"] && [str2 isEqualToString:@"." ]) || ([str1 isEqualToString:@":"]&&[str2 isEqualToString:@"]"]&&[str3 isEqualToString:@"0"])) {                    NSString *lrcStr = [lineArray objectAtIndex:1];                    NSString *timeStr = [[lineArray objectAtIndex:0] substringWithRange:NSMakeRange(1, 5)];//分割区间求歌词时间                    NSArray *array = [timeStr componentsSeparatedByString:@":"];//把时间转换成秒                    NSUInteger currentTime = [array[0] intValue] * 60 + [array[1] intValue];                    NSString *str = [NSString stringWithFormat:@"%ld",currentTime];                    //把时间 和 歌词 加入词典                    [self.lrcDic setObject:lrcStr forKey:str];                    [self.lrcTimeArr addObject:str];//timeArray的count就是行数                }            }        }    }

当音乐播放时,不断刷新播放进度条的时候,根据当前的currentTime与保存时间的数字里面的时间进行对比选出当前正在播放的时间段,然后通过字典取出当前的歌词,刷新tableView,代码如下:

- (void)upDateUI{    MusicPlayTool *playTool = [MusicPlayTool sharedMusicPlayTool];    self.currentTime.text = [NSString stringWithFormat:@"%02d:%02d",(int)playTool.player.currentTime/60,(int)playTool.player.currentTime%60];    self.progressSlider.value = playTool.player.currentTime;    for (int i = 0; i < self.lrcTimeArr.count; i++) {        NSString *timeStr = self.lrcTimeArr[i];        if ([timeStr integerValue] == (NSInteger)playTool.player.currentTime) {            _currentIndex = i;            [self resetLockInfo];            [_lrcTableView reloadData];            [_lrcTableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:_currentIndex inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];            return;        }    }}

实际运行效果如下图:


Untitled.gif

锁屏歌词同步

在设置锁屏信息的时候有这么一句代码:

    //设置专辑图片    MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:@"song_400"]];    info[MPMediaItemPropertyArtwork] = artwork;

图片“song_400”就是锁屏的是锁屏时中间显示的大图,要显示歌词就要将歌词绘制到图片上面,然后合成生成新的图片显示,代码如下:

//设置专辑图片    //UIImage *image = [UIImage imageNamed:music.icon];    UIImage *image = [self createShareImage:[self.lrcDic objectForKey:self.lrcTimeArr[_currentIndex]] name:@"song_400" number:nil grade:nil];- ( UIImage *)createShareImage:( NSString *)str name:( NSString *)name number:( NSString *)number grade:( NSString *)grade{    UIImage *image = [UIImage imageNamed:name];    CGSize size= CGSizeMake (image.size.width, image.size.height); // 画布大小    UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);    [image drawAtPoint:CGPointMake(0, 0)];    // 获得一个位图图形上下文    CGContextRef context= UIGraphicsGetCurrentContext ();    CGContextDrawPath(context, kCGPathStroke );    CGSize needSize = CGSizeMake(1000,20);    NSDictionary *attribute = @{NSFontAttributeName:[UIFont systemFontOfSize:16], NSForegroundColorAttributeName:[UIColor grayColor]};    CGSize labelsize = [str boundingRectWithSize:needSize options: NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:attribute context:nil].size;    CGFloat width = MIN(labelsize.width, image.size.width);    [str drawInRect:CGRectMake((image.size.width-width)/2, image.size.height - 40, width, 20) withAttributes:attribute];    // 返回绘制的新图形    UIImage *newImage= UIGraphicsGetImageFromCurrentImageContext ();    UIGraphicsEndImageContext ();    return newImage;}

然后在刷新进度条的时候不停的合成生成新的图片进行显示。
最终运行效果如下:


Untitled1.gif

完整代码github连接(只有2首歌配置了歌词)

0 0