首页 > 代码库 > iOS开发拓展篇—音频处理(音乐播放器3)

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

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

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

一、控制器间数据传递

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

YYMusicTool.m文件

 1 // 2 //  YYMusicTool.m 3 // 4  5 #import "YYMusicTool.h" 6 #import "YYMusicModel.h" 7 #import "MJExtension.h" 8  9 @implementation YYMusicTool10 11 static NSArray *_musics;12 static  YYMusicModel *_playingMusic;13 14 /**15  *  @return 返回所有的歌曲16  */17 +(NSArray *)musics18 {19     if (_musics==nil) {20         _musics=[YYMusicModel objectArrayWithFilename:@"Musics.plist"];21     }22     return _musics;23 }24 25 +(void)setPlayingMusic:(YYMusicModel *)playingMusic26 {27     /*28      *如果没有传入需要播放的歌曲,或者是传入的歌曲名不在音乐库中,那么就直接返回29       如果需要播放的歌曲就是当前正在播放的歌曲,那么直接返回30      */31     if (!playingMusic || ![[self musics]containsObject:playingMusic]) return;32     if (_playingMusic == playingMusic) return;33     _playingMusic=playingMusic;34 }35 /**36  *  返回正在播放的歌曲37  */38 +(YYMusicModel *)playingMusic39 {40     return _playingMusic;41 }42 43 /**44  *  下一首歌曲45  */46 +(YYMusicModel *)nextMusic47 {48     //设定一个初值49     int nextIndex = 0;50     if (_playingMusic) {51         //获取当前播放音乐的索引52         int playingIndex = [[self musics] indexOfObject:_playingMusic];53         //设置下一首音乐的索引54         nextIndex = playingIndex+1;55         //检查数组越界,如果下一首音乐是最后一首,那么重置为056         if (nextIndex>=[self musics].count) {57             nextIndex=0;58         }59     }60     return [self musics][nextIndex];61 }62 63 /**64  *  上一首歌曲65  */66 +(YYMusicModel *)previousMusic67 {68     //设定一个初值69     int previousIndex = 0;70     if (_playingMusic) {71         //获取当前播放音乐的索引72         int playingIndex = [[self musics] indexOfObject:_playingMusic];73         //设置下一首音乐的索引74         previousIndex = playingIndex-1;75         //检查数组越界,如果下一首音乐是最后一首,那么重置为076         if (previousIndex<0) {77             previousIndex=[self musics].count-1;78         }79     }80     return [self musics][previousIndex];81 }82 @end

 

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

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

YYAudioTool.h文件

 1 // 2 //  YYAudioTool.h 3 // 4  5 #import <Foundation/Foundation.h> 6 #import <AVFoundation/AVFoundation.h> 7 @interface YYAudioTool : NSObject 8 /** 9  *播放音乐文件10  */11 +(BOOL)playMusic:(NSString *)filename;12 /**13  *暂停播放14  */15 +(void)pauseMusic:(NSString *)filename;16 /**17  *播放音乐文件18  */19 +(void)stopMusic:(NSString *)filename;20 21 /**22  *播放音效文件23  */24 +(void)playSound:(NSString *)filename;25 /**26  *销毁音效27  */28 +(void)disposeSound:(NSString *)filename;29 @end

YYAudioTool.m文件

  1 //  2 //  YYAudioTool.m  3 //  4   5 #import "YYAudioTool.h"  6   7 @implementation YYAudioTool  8 /**  9  *存放所有的音乐播放器 10  */ 11 static NSMutableDictionary *_musicPlayers; 12 +(NSMutableDictionary *)musicPlayers 13 { 14     if (_musicPlayers==nil) { 15         _musicPlayers=[NSMutableDictionary dictionary]; 16     } 17     return _musicPlayers; 18 } 19  20 /** 21  *存放所有的音效ID 22  */ 23 static NSMutableDictionary *_soundIDs; 24 +(NSMutableDictionary *)soundIDs 25 { 26     if (_soundIDs==nil) { 27         _soundIDs=[NSMutableDictionary dictionary]; 28     } 29     return _soundIDs; 30 } 31  32  33 /** 34  *播放音乐 35  */ 36 +(BOOL)playMusic:(NSString *)filename 37 { 38     if (!filename) return NO;//如果没有传入文件名,那么直接返回 39     //1.取出对应的播放器 40     AVAudioPlayer *player=[self musicPlayers][filename]; 41      42     //2.如果播放器没有创建,那么就进行初始化 43     if (!player) { 44         //2.1音频文件的URL 45         NSURL *url=[[NSBundle mainBundle]URLForResource:filename withExtension:nil]; 46         if (!url) return NO;//如果url为空,那么直接返回 47          48         //2.2创建播放器 49         player=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil]; 50          51         //2.3缓冲 52         if (![player prepareToPlay]) return NO;//如果缓冲失败,那么就直接返回 53          54         //2.4存入字典 55         [self musicPlayers][filename]=player; 56     } 57      58     //3.播放 59     if (![player isPlaying]) { 60         //如果当前没处于播放状态,那么就播放 61         return [player play]; 62     } 63  64     return YES;//正在播放,那么就返回YES 65 } 66  67 +(void)pauseMusic:(NSString *)filename 68 { 69     if (!filename) return;//如果没有传入文件名,那么就直接返回 70      71     //1.取出对应的播放器 72     AVAudioPlayer *player=[self musicPlayers][filename]; 73      74     //2.暂停 75     [player pause];//如果palyer为空,那相当于[nil pause],因此这里可以不用做处理 76  77 } 78  79 +(void)stopMusic:(NSString *)filename 80 { 81     if (!filename) return;//如果没有传入文件名,那么就直接返回 82      83     //1.取出对应的播放器 84     AVAudioPlayer *player=[self musicPlayers][filename]; 85      86     //2.停止 87     [player stop]; 88      89     //3.将播放器从字典中移除 90     [[self musicPlayers] removeObjectForKey:filename]; 91 } 92  93 //播放音效 94 +(void)playSound:(NSString *)filename 95 { 96     if (!filename) return; 97     //1.取出对应的音效 98     SystemSoundID soundID=[[self soundIDs][filename] unsignedIntegerValue]; 99     100     //2.播放音效101     //2.1如果音效ID不存在,那么就创建102     if (!soundID) {103         104         //音效文件的URL105         NSURL *url=[[NSBundle mainBundle]URLForResource:filename withExtension:nil];106         if (!url) return;//如果URL不存在,那么就直接返回107         108         OSStatus status = AudioServicesCreateSystemSoundID((__bridge CFURLRef)(url), &soundID);109         NSLog(@"%ld",status);110         //存入到字典中111         [self soundIDs][filename]=@(soundID);112     }113     114     //2.2有音效ID后,播放音效115     AudioServicesPlaySystemSound(soundID);116 }117 118 //销毁音效119 +(void)disposeSound:(NSString *)filename120 {121     //如果传入的文件名为空,那么就直接返回122     if (!filename) return;123     124     //1.取出对应的音效125     SystemSoundID soundID=[[self soundIDs][filename] unsignedIntegerValue];126     127     //2.销毁128     if (soundID) {129         AudioServicesDisposeSystemSoundID(soundID);130         131         //2.1销毁后,从字典中移除132         [[self soundIDs]removeObjectForKey:filename];133     }134 }135 @end

 

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

YYPlayingViewController.m文件

  1 //  2 //  YYPlayingViewController.m  3 //  4   5 #import "YYPlayingViewController.h"  6 #import "YYMusicTool.h"  7 #import "YYMusicModel.h"  8 #import "YYAudioTool.h"  9  10 @interface YYPlayingViewController () 11 @property (weak, nonatomic) IBOutlet UIImageView *iconView; 12 @property (weak, nonatomic) IBOutlet UILabel *songLabel; 13 @property (weak, nonatomic) IBOutlet UILabel *singerLabel; 14 @property (weak, nonatomic) IBOutlet UILabel *durationLabel; 15 @property(nonatomic,strong)YYMusicModel *playingMusic; 16 - (IBAction)exit; 17  18 @end 19  20 @implementation YYPlayingViewController 21 #pragma mark-公共方法 22 -(void)show 23 { 24     //1.禁用整个app的点击事件 25     UIWindow *window=[UIApplication sharedApplication].keyWindow; 26     window.userInteractionEnabled=NO; 27      28     //2.添加播放界面 29     //设置View的大小为覆盖整个窗口 30     self.view.frame=window.bounds; 31     //设置view显示 32     self.view.hidden=NO; 33     //把View添加到窗口上 34     [window addSubview:self.view]; 35      36     //3.检测是否换了歌曲 37     if (self.playingMusic!=[YYMusicTool playingMusic]) { 38         [self RresetPlayingMusic]; 39     } 40      41     //4.使用动画让View显示 42     self.view.y=self.view.height; 43     [UIView animateWithDuration:0.25 animations:^{ 44         self.view.y=0; 45     } completion:^(BOOL finished) { 46          47         //设置音乐数据 48         [self starPlayingMusic]; 49         window.userInteractionEnabled=YES; 50     }]; 51 } 52 #pragma mark-私有方法 53 //重置正在播放的音乐 54 -(void)RresetPlayingMusic 55 { 56     //1.重置界面数据 57     self.iconView.image=[UIImage imageNamed:@"play_cover_pic_bg"]; 58     self.songLabel.text=nil; 59     self.singerLabel.text=nil; 60      61     //2.停止播放 62     [YYAudioTool stopMusic:self.playingMusic.filename]; 63 } 64 //开始播放音乐数据 65 -(void)starPlayingMusic 66 { 67     //1.设置界面数据 68      69     //取出当前正在播放的音乐 70 //    YYMusicModel *playingMusic=[YYMusicTool playingMusic]; 71      72     //如果当前播放的音乐就是传入的音乐,那么就直接返回 73     if (self.playingMusic==[YYMusicTool playingMusic]) return; 74     //存取音乐 75     self.playingMusic=[YYMusicTool playingMusic]; 76     self.iconView.image=[UIImage imageNamed:self.playingMusic.icon]; 77     self.songLabel.text=self.playingMusic.name; 78     self.singerLabel.text=self.playingMusic.singer; 79      80     //2.开始播放 81     [YYAudioTool playMusic:self.playingMusic.filename]; 82      83 } 84  85 #pragma mark-内部的按钮监听方法 86 //返回按钮 87 - (IBAction)exit { 88     //1.禁用整个app的点击事件 89     UIWindow *window=[UIApplication sharedApplication].keyWindow; 90     window.userInteractionEnabled=NO; 91      92     //2.动画隐藏View 93     [UIView animateWithDuration:0.25 animations:^{ 94         self.view.y=window.height; 95     } completion:^(BOOL finished) { 96         window.userInteractionEnabled=YES; 97         //设置view隐藏能够节省一些性能 98         self.view.hidden=YES; 99     }];100 }101 @end

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

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

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

      

五、补充代码

YYMusicsViewController.m文件

 1 // 2 //  YYMusicsViewController.m 3 // 4  5 #import "YYMusicsViewController.h" 6 #import "YYMusicModel.h" 7 #import "MJExtension.h" 8 #import "YYMusicCell.h" 9 #import "YYPlayingViewController.h"10 #import "YYMusicTool.h"11 12 @interface YYMusicsViewController ()13 14 @property(nonatomic,strong)YYPlayingViewController *playingViewController;15 @end16 17 @implementation YYMusicsViewController18 #pragma mark-懒加载19 20 -(YYPlayingViewController *)playingViewController21 {22     if (_playingViewController==nil) {23         _playingViewController=[[YYPlayingViewController alloc]init];24     }25     return _playingViewController;26 }27 28 - (void)viewDidLoad29 {30     [super viewDidLoad];31 }32 33 #pragma mark - Table view data source34 /**35  *一共多少组36  */37 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView38 {39     return 1;40 }41 /**42  *每组多少行43  */44 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section45 {46     return [YYMusicTool musics].count;47 }48 /**49  *每组每行的cell50  */51 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath52 {53     YYMusicCell *cell=[YYMusicCell cellWithTableView:tableView];54     cell.music=[YYMusicTool musics][indexPath.row];55     return cell;56 }57 /**58  *  设置每个cell的高度59  */60 -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath61 {62     return 70;63 }64 65 /**66  *  cell的点击事件67  */68 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath69 {70     //1.取消选中被点击的这行71     [tableView deselectRowAtIndexPath:indexPath animated:YES];72     73     //2.设置正在播放的歌曲74     [YYMusicTool setPlayingMusic:[YYMusicTool musics][indexPath.row]];75 76     //调用公共方法77     [self.playingViewController show];78     79 //    //执行segue跳转80 //    [self performSegueWithIdentifier:@"music2playing" sender:nil];81 }82 @end