zoukankan      html  css  js  c++  java
  • iOS开发拓展篇—音频处理(音乐播放器6)

    iOS开发拓展篇—音频处理(音乐播放器6)

    一、图片处理

    说明:

    Aspect表示按照原来的宽高比进行缩放。
    Aspectfit表示按照原来的宽高比缩放,要求看到全部图片,后果是不能完全覆盖窗口,会留有空白。
    Aspectfill表示按照原来的宽高比缩放,但只能看到部分图片。引发的问题:可能会有一部分超出屏幕。
    所以,如果选择了Aspectfill模式,那么需要剪切超出的图片,在storyboard中也可以进行设置。
    下面的两种设置是等效的。
    (1)在storyboard中进行设置
    (2)使用代码裁剪

     

    二、播放处理
    1.当前歌曲播放结束之后,继续播放后面的歌曲
      解决方案:成为播放器的代理。监听播放器的播放。
    2.播放中断处理
    #pragma mark-音乐播放器的代理
    //播放器播放完毕后就会调用该方法
    -(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
    {
        [self next];
    }
    //当播放器遇到中断的时候(如来电),调用该方法
    -(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
    {
        if (self.player.isPlaying) {
            //如果当前正在播放,那么就暂停
            [self playOrPause];
        }
    }
    //中断事件结束后调用下面的方法
    -(void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags
    {
        //可以什么都不做,让用户决定是继续播放还是暂停
    }
    3.开启后台任务
      说明:新的问题——当播放任务进入到后台运行的时候,音乐的播放会停止。
      解决方案:

      YYAppDelegate.m文件中的代码处理如下:

    争取更多的机会:

     

    告诉其是一个音乐播放任务

     

    4.最好是在音频播放工具类中也进行处理。

    #import "YYAudioTool.h"
    
    @implementation YYAudioTool
    +(void)initialize
    {
        //音频会话
        AVAudioSession *session =[AVAudioSession sharedInstance];
        //设置绘画类型(播放类型,播放模式,会自动停止其他音乐的播放)
        [session setCategory:AVAudioSessionCategorySoloAmbient error:nil];
        //激活会话
        [session setActive:YES error:nil];
    }
    //.......

    三、创建歌词控件

    创建歌词控件
    歌词的格式说明:
    播放效果:
    毛玻璃效果的实现
    (1)让美工提供一张半透明的图片
    (2)利用一大堆的图形算法生成毛玻璃样式的UIImage对象
    这里使用一个第三方框架来生成毛玻璃效果
      毛玻璃:英文blur
      第三方框架:DRNRealTimeBlur
    具体实现:
      新建一个类,让其继承自,就能够实现毛玻璃效果。

    在xib中添加一个歌词控件。

    注意歌词控件的层级关系,退出和词图两个按钮应该在歌词控件的上面,这样才能够点击切换。

    把该控件和新建的类进行关联。

     

    添加约束,并清空其背景颜色。默认不显示,(设置隐藏)

     

    毛玻璃效果如下:

     

     

    简单的代码处理如下:

    YYLrcView.m文件

    //
    //  YYLrcView.m
    //  24-音频处理(音乐播放器5)
    //
    //  Created by apple on 14-8-15.
    //  Copyright (c) 2014年 yangyong. All rights reserved.
    //
    
    #import "YYLrcView.h"
    
    @interface YYLrcView ()<UITableViewDataSource,UITableViewDelegate>
    @property(nonatomic,strong)UITableView *tableView;
    @end
    @implementation YYLrcView
    
    - (id)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            [self setup];
        }
        return self;
    }
    
    -(id)initWithCoder:(NSCoder *)aDecoder
    {
        self=[super initWithCoder:aDecoder];
        if (self) {
            [self setup];
        }
        return self;
    }
    
    -(void)setup
    {
        //添加表格控件
        UITableView *tableView=[[UITableView alloc]init];
        tableView.delegate=self;
        tableView.dataSource=self;
        [self addSubview:tableView];
        self.tableView=tableView;
    }
    
    #pragma mark-公共方法
    -(void)setLrcname:(NSString *)lrcname
    {
        _lrcname=[lrcname copy];
    }
    
    #pragma mark-数据源方法
    #warning TODO
    
    @end

    代码说明:

    注意:不要认为只有控制器才能作为tableView的数据源和代理。这也就是为什么代理和数据源属性的类型为id的原因,遵守其协议即可做其代理和数据源。

    -(id)initWithCoder:。从文件中读取一个对象的时候调用,为了程序的严谨性,建议在两个方法中调用初始化的代码。

    调用这个方法,说明对象是从文件中解析出来的。

    如果是通过代码allocinit创建的对象,那么调用-(id)initWithFrame:方法。

    说明:xib文件的本质是xml文件。

    四、主控制器的代码补充

    YYPlayingViewController.m文件

    //
    //  YYPlayingViewController.m
    //
    
    #import "YYPlayingViewController.h"
    #import "YYMusicTool.h"
    #import "YYMusicModel.h"
    #import "YYAudioTool.h"
    #import "YYLrcView.h"
    
    @interface YYPlayingViewController ()<AVAudioPlayerDelegate>
    - (IBAction)lyricOrPic:(UIButton *)sender;
    @property (weak, nonatomic) IBOutlet YYLrcView *lrcView;
    //显示拖拽进度
    @property (weak, nonatomic) IBOutlet UIButton *currentTimeView;
    //进度条
    @property (weak, nonatomic) IBOutlet UIView *progressView;
    //滑块
    @property (weak, nonatomic) IBOutlet UIButton *slider;
    @property (weak, nonatomic) IBOutlet UIImageView *iconView;
    @property (weak, nonatomic) IBOutlet UILabel *songLabel;
    @property (weak, nonatomic) IBOutlet UILabel *singerLabel;
    //当前播放的音乐的时长
    @property (weak, nonatomic) IBOutlet UILabel *durationLabel;
    //正在播放的音乐
    @property(nonatomic,strong)YYMusicModel *playingMusic;
    //音乐播放器对象
    @property(nonatomic,strong)AVAudioPlayer *player;
    //定时器
    @property(nonatomic,strong)NSTimer *CurrentTimeTimer;
    - (IBAction)exit;
    - (IBAction)tapProgressBg:(UITapGestureRecognizer *)sender;
    - (IBAction)panSlider:(UIPanGestureRecognizer *)sender;
    - (IBAction)previous;
    - (IBAction)playOrPause;
    - (IBAction)next;
    @property (weak, nonatomic) IBOutlet UIButton *playOrPauseButton;
    
    @end
    
    @implementation YYPlayingViewController
    
    -(void)viewDidLoad
    {
        [super viewDidLoad];
        
        //裁剪圆角
        self.currentTimeView.layer.cornerRadius=8;
        
    }
    #pragma mark-公共方法
    -(void)show
    {
        //1.禁用整个app的点击事件
        UIWindow *window=[UIApplication sharedApplication].keyWindow;
        window.userInteractionEnabled=NO;
        
        //2.添加播放界面
        //设置View的大小为覆盖整个窗口
        self.view.frame=window.bounds;
        //设置view显示
        self.view.hidden=NO;
        //把View添加到窗口上
        [window addSubview:self.view];
        
        //3.检测是否换了歌曲
        if (self.playingMusic!=[YYMusicTool playingMusic]) {
            [self resetPlayingMusic];
        }
        
        //4.使用动画让View显示
        self.view.y=self.view.height;
        [UIView animateWithDuration:0.25 animations:^{
            self.view.y=0;
        } completion:^(BOOL finished) {
            
            //设置音乐数据
            [self starPlayingMusic];
            window.userInteractionEnabled=YES;
        }];
    }
    
    
    #pragma mark-私有方法
    //重置正在播放的音乐
    -(void)resetPlayingMusic
    {
        //1.重置界面数据
        self.iconView.image=[UIImage imageNamed:@"play_cover_pic_bg"];
        self.songLabel.text=nil;
        self.singerLabel.text=nil;
        
        //2.停止播放
        [YYAudioTool stopMusic:self.playingMusic.filename];
        //把播放器进行清空
        self.player=nil;
        
        //3.停止定时器
        [self removeCurrentTime];
        
        //4.设置音乐播放按钮的状态
        self.playOrPauseButton.selected=NO;
    }
    //开始播放音乐数据
    -(void)starPlayingMusic
    {
        //1.设置界面数据
        
        //如果当前播放的音乐就是传入的音乐,那么就直接返回
        if (self.playingMusic==[YYMusicTool playingMusic])
        {
            //把定时器加进去
            [self addCurrentTimeTimer];
            return;
        }
        //存取音乐
        self.playingMusic=[YYMusicTool playingMusic];
        self.iconView.image=[UIImage imageNamed:self.playingMusic.icon];
        self.songLabel.text=self.playingMusic.name;
        self.singerLabel.text=self.playingMusic.singer;
        
        //2.开始播放
        self.player = [YYAudioTool playMusic:self.playingMusic.filename];
        self.player.delegate=self;
        
        //3.设置时长
        //self.player.duration;  播放器正在播放的音乐文件的时间长度
        self.durationLabel.text=[self strWithTime:self.player.duration];
        
        //4.添加定时器
        [self addCurrentTimeTimer];
        
        //5.设置音乐播放按钮的状态
        self.playOrPauseButton.selected=YES;
        
        //6.设置歌词
        self.lrcView.lrcname=self.playingMusic.lrcname;
    }
    
    /**
     *把时间长度-->时间字符串
     */
    -(NSString *)strWithTime:(NSTimeInterval)time
    {
        int minute=time / 60;
        int second=(int)time % 60;
        return [NSString stringWithFormat:@"%d:%d",minute,second];
    }
    
    #pragma mark-定时器控制
    /**
     *  添加一个定时器
     */
    -(void)addCurrentTimeTimer
    {
        //如果当前没有在播放,那么就直接返回
        if (self.player.isPlaying==NO) return;
        
        //在添加一个定时器之前,先把以前的定时器移除
        [self removeCurrentTime];
        
        //提前先调用一次进度更新,以保证定时器的工作时及时的
        [self updateCurrentTime];
        
        //创建一个定时器,每一秒钟调用一次
        self.CurrentTimeTimer=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateCurrentTime) userInfo:nil repeats:YES];
        //把定时器加入到运行时中
        [[NSRunLoop mainRunLoop]addTimer:self.CurrentTimeTimer forMode:NSRunLoopCommonModes];
    }
    /**
     *移除一个定时器
     */
    -(void)removeCurrentTime
    {
        [self.CurrentTimeTimer invalidate];
        
        //把定时器清空
        self.CurrentTimeTimer=nil;
    }
    
    /**
     *  更新播放进度
     */
    -(void)updateCurrentTime
    {
        //1.计算进度值
        double progress=self.player.currentTime/self.player.duration;
        
        //2.计算滑块的x值
        // 滑块的最大的x值
        CGFloat sliderMaxX=self.view.width-self.slider.width;
        self.slider.x=sliderMaxX*progress;
        //设置滑块上的当前播放时间
        [self.slider setTitle:[self strWithTime:self.player.currentTime] forState:UIControlStateNormal];
        
        //3.设置进度条的宽度
        self.progressView.width=self.slider.center.x;
        
    }
    
    #pragma mark-内部的按钮监听方法
    //返回按钮
    - (IBAction)exit {
        
        //0.移除定时器
        [self  removeCurrentTime];
        //1.禁用整个app的点击事件
        UIWindow *window=[UIApplication sharedApplication].keyWindow;
        window.userInteractionEnabled=NO;
        
        //2.动画隐藏View
        [UIView animateWithDuration:0.25 animations:^{
            self.view.y=window.height;
        } completion:^(BOOL finished) {
            window.userInteractionEnabled=YES;
            //设置view隐藏能够节省一些性能
            self.view.hidden=YES;
        }];
        
    }
    
    /**
     *点击了进度条
     */
    - (IBAction)tapProgressBg:(UITapGestureRecognizer *)sender {
        //获取当前单击的点
        CGPoint point=[sender locationInView:sender.view];
        //切换歌曲的当前播放时间
        self.player.currentTime=(point.x/sender.view.width)*self.player.duration;
        //更新播放进度
        [self updateCurrentTime];
    }
    /**
     *拖动滑块
     */
    - (IBAction)panSlider:(UIPanGestureRecognizer *)sender {
        
        //1.获得挪动的距离
        CGPoint t=[sender translationInView:sender.view];
        //把挪动清零
        [sender setTranslation:CGPointZero inView:sender.view];
        
        //2.控制滑块和进度条的frame
        CGFloat sliderMaxX=self.view.width-self.slider.width;
        self.slider.x+=t.x;
        //控制滑块的frame,不让其越界
        if(self.slider.x<0)
        {
            self.slider.x=0;
        }else if (self.slider.x>sliderMaxX)
        {
            self.slider.x=sliderMaxX;
        }
        //设置进度条的宽度
        self.progressView.width=self.slider.center.x;
        
        //3.设置时间值
        double progress=self.slider.x/sliderMaxX;
        //当前的时间值=音乐的时长*当前的进度值
        NSTimeInterval time=self.player.duration*progress;
        [self.slider setTitle:[self strWithTime:time] forState:UIControlStateNormal];
        
        //设置拖拽进度的X的值
        self.currentTimeView.x=self.slider.x;
        [self.currentTimeView setTitle:self.slider.currentTitle forState:UIControlStateNormal];
        
        //4.如果开始拖动,那么就停止定时器
        if (sender.state==UIGestureRecognizerStateBegan) {
            //停止定时器
            [self removeCurrentTime];
            
            //设置拖拽进度
            //显示
            self.currentTimeView.hidden=NO;
            self.currentTimeView.y=self.currentTimeView.superview.height-5-self.currentTimeView.height;
            
        }else if(sender.state==UIGestureRecognizerStateEnded)
        {
            //隐藏
            self.currentTimeView.hidden=YES;
            //设置播放器播放的时间
            self.player.currentTime=time;
    #warning 如果正在播放,才需要添加定时器
    //        if (self.player.isPlaying) {
            //开启定时器
            [self addCurrentTimeTimer];
    //        }
        }
    }
    
    //上一首
    - (IBAction)previous {
        //1.在开始播放之前,禁用一切的app点击事件
        UIWindow *window=[[UIApplication sharedApplication].windows lastObject];
        window.userInteractionEnabled=NO;
        
        //2.重置当前歌曲
        [self resetPlayingMusic];
        
        //3.获得上一首歌曲
        [YYMusicTool setPlayingMusic:[YYMusicTool previousMusic]];
        
        //4.播放上一首歌曲
        [self starPlayingMusic];
        
        //5.回复window的点击为可用
        window.userInteractionEnabled=YES;
    }
    //下一首
    - (IBAction)next {
        //1.在开始播放之前,禁用一切的app点击事件
        UIWindow *window=[[UIApplication sharedApplication].windows lastObject];
        window.userInteractionEnabled=NO;
        
        //2.重置当前歌曲
        [self resetPlayingMusic];
        
        //3.获得下一首歌曲
        [YYMusicTool setPlayingMusic:[YYMusicTool nextMusic]];
        
        //4.播放下一首歌曲
        [self starPlayingMusic];
        
        //5.回复window的点击为可用
        window.userInteractionEnabled=YES;
    }
    
    //继续或暂停播放
    - (IBAction)playOrPause {
        if (self.playOrPauseButton.isSelected) {//暂停
            self.playOrPauseButton.selected=NO;
            //暂停播放
            [YYAudioTool pauseMusic:self.playingMusic.filename];
            //停掉定时器
            [self removeCurrentTime];
        }else
        {
            self.playOrPauseButton.selected=YES;
            //继续播放
            [YYAudioTool playMusic:self.playingMusic.filename];
            //开启定时器
            [self addCurrentTimeTimer];
        }
    }
    
    #pragma mark-音乐播放器的代理
    //播放器播放完毕后就会调用该方法
    -(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
    {
        [self next];
    }
    //当播放器遇到中断的时候(如来电),调用该方法
    -(void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
    {
        if (self.player.isPlaying) {
            //如果当前正在播放,那么就暂停
            [self playOrPause];
        }
    }
    //中断事件结束后调用下面的方法
    -(void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags
    {
        //可以什么都不做,让用户决定是继续播放还是暂停
    }
    - (IBAction)lyricOrPic:(UIButton *)sender {
        if (self.lrcView.hidden) {
            //显示歌词
            self.lrcView.hidden=NO;
            sender.selected=YES;
        }else
        {
            //隐藏歌词,显示歌手图片
            self.lrcView.hidden=YES;
            sender.selected=NO;
        }
    }
    @end

     

  • 相关阅读:
    84. Largest Rectangle in Histogram (Solution 2)
    84. Largest Rectangle in Histogram (Solution 1)
    73. Set Matrix Zeroes
    【JavaScript】Symbol 静态方法
    【JavaScript】Date
    【JavaScript】Math
    725. Split Linked List in Parts把链表分成长度不超过1的若干部分
    791. Custom Sort String字符串保持字母一样,位置可以变
    508. Most Frequent Subtree Sum 最频繁的子树和
    762. Prime Number of Set Bits in Binary Representation二进制中有质数个1的数量
  • 原文地址:https://www.cnblogs.com/yipingios/p/5566851.html
Copyright © 2011-2022 走看看