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

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

    说明:这篇文章主要介绍音频工具类和播放工具类的封装。

    一、控制器间数据传递

    1.两个控制器之间数据的传递
    第一种方法:self.parentViewController.music=self.music[indexPath.row];不能满足
    第二种做法:把整个数组传递给它
    第三种做法:设置一个数据源,设置播放控制器的数据源是这个控制器。self.parentViewController.dataSource=self;好处:没有耦合性,任何实现了协议的可以作为数据源。
    第四种做法:把整个项目会使用到的音频资源交给一个工具类去管理,这样就不用传递过去了。直接向工具类索要资源就可以。
     
    二、封装一个音频工具类
    新建一个音频工具类,用来管理音乐数据(音乐模型)
    工具类中的代码设计如下:
     YYMusicTool.h文件
    //
    //  YYMusicTool.h
    //
    
    #import <Foundation/Foundation.h>
    @class YYMusicModel;
    @interface YYMusicTool : NSObject
    /**
     *  返回所有的歌曲
     */
    + (NSArray *)musics;
    
    /**
     *  返回正在播放的歌曲
     */
    + (YYMusicModel *)playingMusic;
    + (void)setPlayingMusic:(YYMusicModel *)playingMusic;
    
    /**
     *  下一首歌曲
     */
    + (YYMusicModel *)nextMusic;
    
    /**
     *  上一首歌曲
     */
    + (YYMusicModel *)previousMusic;
    @end

    YYMusicTool.m文件

    //
    //  YYMusicTool.m
    //
    
    #import "YYMusicTool.h"
    #import "YYMusicModel.h"
    #import "MJExtension.h"
    
    @implementation YYMusicTool
    
    static NSArray *_musics;
    static  YYMusicModel *_playingMusic;
    
    /**
     *  @return 返回所有的歌曲
     */
    +(NSArray *)musics
    {
        if (_musics==nil) {
            _musics=[YYMusicModel objectArrayWithFilename:@"Musics.plist"];
        }
        return _musics;
    }
    
    +(void)setPlayingMusic:(YYMusicModel *)playingMusic
    {
        /*
         *如果没有传入需要播放的歌曲,或者是传入的歌曲名不在音乐库中,那么就直接返回
          如果需要播放的歌曲就是当前正在播放的歌曲,那么直接返回
         */
        if (!playingMusic || ![[self musics]containsObject:playingMusic]) return;
        if (_playingMusic == playingMusic) return;
        _playingMusic=playingMusic;
    }
    /**
     *  返回正在播放的歌曲
     */
    +(YYMusicModel *)playingMusic
    {
        return _playingMusic;
    }
    
    /**
     *  下一首歌曲
     */
    +(YYMusicModel *)nextMusic
    {
        //设定一个初值
        int nextIndex = 0;
        if (_playingMusic) {
            //获取当前播放音乐的索引
            int playingIndex = [[self musics] indexOfObject:_playingMusic];
            //设置下一首音乐的索引
            nextIndex = playingIndex+1;
            //检查数组越界,如果下一首音乐是最后一首,那么重置为0
            if (nextIndex>=[self musics].count) {
                nextIndex=0;
            }
        }
        return [self musics][nextIndex];
    }
    
    /**
     *  上一首歌曲
     */
    +(YYMusicModel *)previousMusic
    {
        //设定一个初值
        int previousIndex = 0;
        if (_playingMusic) {
            //获取当前播放音乐的索引
            int playingIndex = [[self musics] indexOfObject:_playingMusic];
            //设置下一首音乐的索引
            previousIndex = playingIndex-1;
            //检查数组越界,如果下一首音乐是最后一首,那么重置为0
            if (previousIndex<0) {
                previousIndex=[self musics].count-1;
            }
        }
        return [self musics][previousIndex];
    }
    @end

    三、封装一个音乐播放工具类

    该工具类中的代码设计如下:

    YYAudioTool.h文件

    //
    //  YYAudioTool.h
    //
    
    #import <Foundation/Foundation.h>
    #import <AVFoundation/AVFoundation.h>
    @interface YYAudioTool : NSObject
    /**
     *播放音乐文件
     */
    +(BOOL)playMusic:(NSString *)filename;
    /**
     *暂停播放
     */
    +(void)pauseMusic:(NSString *)filename;
    /**
     *播放音乐文件
     */
    +(void)stopMusic:(NSString *)filename;
    
    /**
     *播放音效文件
     */
    +(void)playSound:(NSString *)filename;
    /**
     *销毁音效
     */
    +(void)disposeSound:(NSString *)filename;
    @end

    YYAudioTool.m文件

    //
    //  YYAudioTool.m
    //
    
    #import "YYAudioTool.h"
    
    @implementation YYAudioTool
    /**
     *存放所有的音乐播放器
     */
    static NSMutableDictionary *_musicPlayers;
    +(NSMutableDictionary *)musicPlayers
    {
        if (_musicPlayers==nil) {
            _musicPlayers=[NSMutableDictionary dictionary];
        }
        return _musicPlayers;
    }
    
    /**
     *存放所有的音效ID
     */
    static NSMutableDictionary *_soundIDs;
    +(NSMutableDictionary *)soundIDs
    {
        if (_soundIDs==nil) {
            _soundIDs=[NSMutableDictionary dictionary];
        }
        return _soundIDs;
    }
    
    
    /**
     *播放音乐
     */
    +(BOOL)playMusic:(NSString *)filename
    {
        if (!filename) return NO;//如果没有传入文件名,那么直接返回
        //1.取出对应的播放器
        AVAudioPlayer *player=[self musicPlayers][filename];
        
        //2.如果播放器没有创建,那么就进行初始化
        if (!player) {
            //2.1音频文件的URL
            NSURL *url=[[NSBundle mainBundle]URLForResource:filename withExtension:nil];
            if (!url) return NO;//如果url为空,那么直接返回
            
            //2.2创建播放器
            player=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil];
            
            //2.3缓冲
            if (![player prepareToPlay]) return NO;//如果缓冲失败,那么就直接返回
            
            //2.4存入字典
            [self musicPlayers][filename]=player;
        }
        
        //3.播放
        if (![player isPlaying]) {
            //如果当前没处于播放状态,那么就播放
            return [player play];
        }
    
        return YES;//正在播放,那么就返回YES
    }
    
    +(void)pauseMusic:(NSString *)filename
    {
        if (!filename) return;//如果没有传入文件名,那么就直接返回
        
        //1.取出对应的播放器
        AVAudioPlayer *player=[self musicPlayers][filename];
        
        //2.暂停
        [player pause];//如果palyer为空,那相当于[nil pause],因此这里可以不用做处理
    
    }
    
    +(void)stopMusic:(NSString *)filename
    {
        if (!filename) return;//如果没有传入文件名,那么就直接返回
        
        //1.取出对应的播放器
        AVAudioPlayer *player=[self musicPlayers][filename];
        
        //2.停止
        [player stop];
        
        //3.将播放器从字典中移除
        [[self musicPlayers] removeObjectForKey:filename];
    }
    
    //播放音效
    +(void)playSound:(NSString *)filename
    {
        if (!filename) return;
        //1.取出对应的音效
        SystemSoundID soundID=[[self soundIDs][filename] unsignedIntegerValue];
        
        //2.播放音效
        //2.1如果音效ID不存在,那么就创建
        if (!soundID) {
            
            //音效文件的URL
            NSURL *url=[[NSBundle mainBundle]URLForResource:filename withExtension:nil];
            if (!url) return;//如果URL不存在,那么就直接返回
            
            OSStatus status = AudioServicesCreateSystemSoundID((__bridge CFURLRef)(url), &soundID);
            NSLog(@"%ld",status);
            //存入到字典中
            [self soundIDs][filename]=@(soundID);
        }
        
        //2.2有音效ID后,播放音效
        AudioServicesPlaySystemSound(soundID);
    }
    
    //销毁音效
    +(void)disposeSound:(NSString *)filename
    {
        //如果传入的文件名为空,那么就直接返回
        if (!filename) return;
        
        //1.取出对应的音效
        SystemSoundID soundID=[[self soundIDs][filename] unsignedIntegerValue];
        
        //2.销毁
        if (soundID) {
            AudioServicesDisposeSystemSoundID(soundID);
            
            //2.1销毁后,从字典中移除
            [[self soundIDs]removeObjectForKey:filename];
        }
    }
    @end

    四、在音乐播放控制器中的代码处理

    YYPlayingViewController.m文件

    //
    //  YYPlayingViewController.m
    //
    
    #import "YYPlayingViewController.h"
    #import "YYMusicTool.h"
    #import "YYMusicModel.h"
    #import "YYAudioTool.h"
    
    @interface YYPlayingViewController ()
    @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;
    - (IBAction)exit;
    
    @end
    
    @implementation YYPlayingViewController
    #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 RresetPlayingMusic];
        }
    
        //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)RresetPlayingMusic
    {
        //1.重置界面数据
        self.iconView.image=[UIImage imageNamed:@"play_cover_pic_bg"];
        self.songLabel.text=nil;
        self.singerLabel.text=nil;
        
        //2.停止播放
        [YYAudioTool stopMusic:self.playingMusic.filename];
    }
    //开始播放音乐数据
    -(void)starPlayingMusic
    {
        //1.设置界面数据
        
        //取出当前正在播放的音乐
    //    YYMusicModel *playingMusic=[YYMusicTool playingMusic];
        
        //如果当前播放的音乐就是传入的音乐,那么就直接返回
        if (self.playingMusic==[YYMusicTool playingMusic]) 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.开始播放
        [YYAudioTool playMusic:self.playingMusic.filename];
        
    }
    
    #pragma mark-内部的按钮监听方法
    //返回按钮
    - (IBAction)exit {
        //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;
        }];
    }
    @end

    注意:先让用户看到界面上的所有东西后,再开始播放歌曲。

    提示:一般的播放器需要做一个重置的操作。
      当从一首歌切换到另外一首时,应该先把上一首的信息删除,因此在show动画显示之前,应该检测是否换了歌曲,如果换了歌曲,则应该做一次重置操作。

     实现效果(能够顺利的切换和播放歌曲,下面是界面显示):

    五、补充代码

    YYMusicsViewController.m文件

    //
    //  YYMusicsViewController.m
    //
    
    #import "YYMusicsViewController.h"
    #import "YYMusicModel.h"
    #import "MJExtension.h"
    #import "YYMusicCell.h"
    #import "YYPlayingViewController.h"
    #import "YYMusicTool.h"
    
    @interface YYMusicsViewController ()
    
    @property(nonatomic,strong)YYPlayingViewController *playingViewController;
    @end
    
    @implementation YYMusicsViewController
    #pragma mark-懒加载
    
    -(YYPlayingViewController *)playingViewController
    {
        if (_playingViewController==nil) {
            _playingViewController=[[YYPlayingViewController alloc]init];
        }
        return _playingViewController;
    }
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    }
    
    #pragma mark - Table view data source
    /**
     *一共多少组
     */
    -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    {
        return 1;
    }
    /**
     *每组多少行
     */
    -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        return [YYMusicTool musics].count;
    }
    /**
     *每组每行的cell
     */
    -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        YYMusicCell *cell=[YYMusicCell cellWithTableView:tableView];
        cell.music=[YYMusicTool musics][indexPath.row];
        return cell;
    }
    /**
     *  设置每个cell的高度
     */
    -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        return 70;
    }
    
    /**
     *  cell的点击事件
     */
    -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        //1.取消选中被点击的这行
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
        
        //2.设置正在播放的歌曲
        [YYMusicTool setPlayingMusic:[YYMusicTool musics][indexPath.row]];
    
        //调用公共方法
        [self.playingViewController show];
        
    //    //执行segue跳转
    //    [self performSegueWithIdentifier:@"music2playing" sender:nil];
    }
    @end
  • 相关阅读:
    JS正则表达式
    TortoiseGit 中文汉化
    java之 Mybatis框架
    正向代理和反向代理
    Java基础之 多线程
    python库参考学习网址
    postman进行https接口测试所遇到的ssl证书问题,参考别人方法
    hbase单机搭建
    搭建单机版伪分布zookeeper集群
    搭建hadoop单机版
  • 原文地址:https://www.cnblogs.com/yipingios/p/5566737.html
Copyright © 2011-2022 走看看