美文网首页视频音频播放iOS天生不是作曲家
AVAudioPlayer本地音乐播放、后台播放、歌词同步,都告

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

作者: 阿聪o | 来源:发表于2016-06-07 22:32 被阅读1769次

    后台播放配置

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

    相关文章

      网友评论

      本文标题:AVAudioPlayer本地音乐播放、后台播放、歌词同步,都告

      本文链接:https://www.haomeiwen.com/subject/knxddttx.html