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


后台播放配置

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首歌配置了歌词)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值