在制作APP的時(shí)候很多時(shí)候都會(huì)用到聲音輸出,畢竟一款有聲音的APP才讓人感到有生命,有活力,沒有聲音的話,整個(gè)APP死氣沉沉的,所以在這里簡(jiǎn)單說一下iOS中的音頻播放。
在iOS設(shè)備中,聲音分為兩種,一種是音效輸出,一種是音頻輸出,音效,就是利用系統(tǒng)的聲音,用來播放比較短的音頻,主要用途是用來做系統(tǒng)的提示音,使用的框架是AudioToolBox,但是今天不重要介紹它,因?yàn)檫@種音效的播放方式和音頻的播放相同,同時(shí)功能也沒有音頻播放強(qiáng)大。
說一下簡(jiǎn)單的音效播放需要的幾個(gè)條件吧。
音效播放
1.聲明一個(gè)聲音ID(SoundID),靠這個(gè)聲音ID來區(qū)分哪個(gè)音頻(音效),類型是無符號(hào)長(zhǎng)整形的.
2.創(chuàng)建播放聲音的服務(wù)(告訴系統(tǒng) 有一個(gè)可以使用的SoundID) 聲音文件的路徑 SoundID。
3.播放聲音
音頻播放
播放音頻和音效都需要這三個(gè)步驟,音頻播放需要的框架也和音效的框架不同,音頻使用的框架式AVFoundation框架,這個(gè)框架中不僅有音頻播放的類,也有視頻播放的類,當(dāng)然今天主要還是說音頻類AVAudioPlayer(音頻播放器)。
在寫一個(gè)音頻播放器之前,有兩點(diǎn)需要我們注意。
1.音頻播放器必須寫為全局變量、屬性,音樂的播放對(duì)象才能播放。
2.在退出播放頁(yè)面的時(shí)候,一定要把播放對(duì)象置空,同時(shí)吧delegate置空。
3.導(dǎo)入#import <AVFoundation/AVFoundation.h>框架
音頻的播放和音效的也有一定區(qū)別
1.資源文件路徑
2.初始化播放器
3.設(shè)置播放器
4.預(yù)播放
5.播放
首先我們要生命一個(gè)全部變量的AVAudioPlayer對(duì)象。
@interface ViewController () <AVAudioPlayerDelegate>
{
AVAudioPlayer *audioPlayer;
}
@end
第一步,需要一個(gè)資源文件的路徑需要一個(gè)控制器來調(diào)用這個(gè)方法。
我們?yōu)榱俗尣シ盼募雍?jiǎn)便,寫了一個(gè)用來讀取文件路徑的方法
- (void)playMusicWithName:(NSString *)name
{
NSError *error;
//初始化并讀取文件路徑,讀取的音頻文件名我們用一個(gè)參數(shù)name來代替
audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:[[NSBundle mainBundle]URLForResource:name withExtension:nil] error:&error];
if (error) {
NSLog(@"%@",error);
}
// 預(yù)播放 在初始化播放器和路徑的同時(shí)給他設(shè)置預(yù)播放,不然后面播放其他音頻文件還需要再次預(yù)播放
[audioPlayer prepareToPlay];
// 播放
// [audioPlayer play];
}
然后接下來是我們?cè)赩iewDidLoad里面寫的方法,我寫了一個(gè)按鈕來使這個(gè)播放器播放音頻。
[self playMusicWithName:@"音頻文件的文件名(即傳過去的參數(shù)name)"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(100, 100, 100, 100);
button.backgroundColor = [UIColor brownColor];
[button setTitle:@"Play" forState:UIControlStateNormal];
[button setTitle:@"Pause" forState:UIControlStateSelected];
[button addTarget:self action:@selector(play:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
然后在觸發(fā)方法里面我們進(jìn)行播放
- (void)play:(UIButton *)sender{
// [audioPlayer playAtTime:30];
sender.selected = !sender.selected;
sender.selected!=YES?[audioPlayer pause]: [audioPlayer play];
// [audioPlayer play];開始播放
// [audioPlayer pause];暫停播放
// [audioPlayer stop];停止播放,停止之后不能再次打開播放器,需要再次初始化才可以
//然后是一些屬性
// duration 獲得播放音頻的時(shí)間
// volum 設(shè)置音量
// 設(shè)置速率必須先設(shè)置enableRate為YES
audioPlayer.enableRate = YES;
audioPlayer.rate = 1.5;
// 獲得峰值 必須設(shè)置Metersenable為YES
audioPlayer.meteringEnabled = YES;
// 更新峰值
[audioPlayer updateMeters];
// 獲得當(dāng)前峰值
NSLog(@"當(dāng)前峰值%f",[audioPlayer peakPowerForChannel:2]);
NSLog(@"平均峰值%f",[audioPlayer averagePowerForChannel:2]);
// 設(shè)置播放次數(shù)
// 負(fù)數(shù)是無限循環(huán) 0是一次 1是2次
audioPlayer.numberOfLoops = -1;
audioPlayer.delegate = self;
}
這樣我們就可以想要播放那個(gè)音頻文件的時(shí)候直接調(diào)用playMusicWithName:這個(gè)方法來得到路徑和預(yù)播放,然后開始播放和進(jìn)行的一些操作。
代理
接下來是一些常見的代理方法
代理的名字<AVAudioPlayerDelegate>
//播放完成的時(shí)候調(diào)用
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
//在制作音樂播放器時(shí)可以進(jìn)行一些單曲循環(huán)的使用等,也可以進(jìn)行下一曲操作,判斷循環(huán)按鈕的狀態(tài)來決定下一曲或者循環(huán)播放
}
//解碼出現(xiàn)錯(cuò)誤的時(shí)候調(diào)用
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError * __nullable)error
{
//如果播放文件出現(xiàn)問題的話可以在這里進(jìn)行提示用戶播放的文件有誤
}
//被打擾開始中斷的時(shí)候調(diào)用
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{
//因?yàn)橐恍╇娫捇蛘咂渌僮髟斐刹シ胖袛鄷?huì)調(diào)用這個(gè)方法,可以在這里進(jìn)行保存播放器等操作
}
//中斷結(jié)束調(diào)用
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags
{
//對(duì)應(yīng)可以在這里進(jìn)行恢復(fù)設(shè)置
}
然后還有一點(diǎn)需要大家注意的就是上面說的在退出頁(yè)面的時(shí)候一定 一定 一定 要釋放掉播放器和代理方法
重要的話說三遍
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
audioPlayer = nil;
audioPlayer.delegate = nil;
}
然后關(guān)于AVFoundation框架還有其錄音的功能
使用的類名也不同,當(dāng)然也需要是全局變量的對(duì)象。
{
AVAudioRecorder *audioRecorder;
}
接下來要說一下錄音的時(shí)候需要設(shè)置一些屬性
/*
AVAudioRecorder
<1>AVNumberOfChannelsKey 通道數(shù)
<2>AVSampleRateKey 采樣率44100
<3>AVLinearPCMBitDepthKey bit 16 32
<4>AVEncoderAudioQualityKey 質(zhì)量
<5>AVEncoderBitRateKey bit采樣率128000
*/
這點(diǎn)需要我們記下來
它需要我們給他一個(gè)URL,本地的一個(gè)URL,來確保錄制的音頻文件存儲(chǔ)的位置
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"文件的名字"];
我們知道我們?cè)谟肁PP錄音的時(shí)候,他們存儲(chǔ)之后的名字都是一些數(shù)字,這樣來保證他們不重名,那又怎么保證不創(chuàng)建重復(fù)名字的呢,我們知道,時(shí)間戳是一排數(shù)字,而且絕對(duì)不會(huì)有重復(fù)的數(shù)字,所以在這里我們用時(shí)間戳來設(shè)置他的名字。
NSString *name = [NSString stringWithFormat:@"%d.aiff",(int)[NSDate date].timeIntervalSince1970];
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:name];
這里我把它保存到了本地的文件夾中,也可以保存在Library等文件夾中,至于這些文件夾在本地中的區(qū)別,后面我會(huì)涉及到在這里更新出來。
接下來就要給錄音器對(duì)象來初始化并設(shè)置其屬性
NSError *error;
audioRecorder = [[AVAudioRecorder alloc]initWithURL:[NSURL URLWithString:path] settings:@{AVNumberOfChannelsKey:@2,AVSampleRateKey:@44100,AVLinearPCMBitDepthKey:@32,AVEncoderAudioQualityKey:@(AVAudioQualityMax),AVEncoderBitRateKey:@128000} error:&error];
audioRecorder.delegate = self;
//準(zhǔn)備錄制
[audioRecorder prepareToRecord];
//錄制
[audioRecorder record];
//我們可以打印一下路徑來查看我們錄制結(jié)束之后的文件
NSLog(@"%@",path);
在這里我同樣是使用了一個(gè)按鈕來開始,和停止錄制
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(100, 100, 100, 100);
[button setTitle:@"錄制" forState:UIControlStateNormal];
button.backgroundColor = [UIColor brownColor];
[button addTarget:self action:@selector(startAudioRecoder:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
- (void)startAudioRecoder:(UIButton *)sender
{
sender.selected =!sender.selected;
if (sender.selected!=YES) {
//[audioRecorder pause];暫停錄制
[audioRecorder stop];//停止錄制
return;
}
}
另外說一個(gè)代理方法,其實(shí)和音頻播放的都大致相同,這里就說一個(gè)錄制結(jié)束的方法
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
NSLog(@"錄音結(jié)束");
NSString *path = [ NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)lastObject];
// 文件操作類
NSFileManager *manager = [NSFileManager defaultManager];
// 獲得當(dāng)前文件的所有子文件subPathAtPath
NSArray *pathList = [manager subpathsAtPath:path];
NSLog(@"%@",pathList);
// 需要只獲得錄音文件
NSMutableArray *audioPathList = [NSMutableArray array];
// 便利所有這個(gè)文件夾下面的子文件
for (NSString *audioPath in pathList) {
// 通過對(duì)比文件的延展名,擴(kuò)展名來區(qū)分是不是錄音文件
if ([audioPath.pathExtension isEqualToString:@"aiff"]) {
NSLog(@"%@",audioPath);
// 把篩選出來的文件 放到數(shù)組中 得到所有的音頻文件
[audioPathList addObject:audioPath];
}
}
NSLog(@"%@",audioPathList);
}
這里我做了一個(gè)操作就是把錄制完成之后的文件挪到了我想要的地方,這樣我就可以在錄制結(jié)束的時(shí)候在桌面上找到我錄制成功的文件,當(dāng)然如果是在APP上面可以選擇存在其他的一些文件夾里,通過文件的閆占明來判斷是否是錄音文件等等。
在輸出獲得當(dāng)前文件的所有子文件的時(shí)候可以看到里面不只有我們錄音的文件也有其他文件,然后對(duì)比來篩選出來,也可以在桌面上直接command+G找到輸出的地址來找到文件
簡(jiǎn)單的音頻播放大致就這些,關(guān)于AVFoundation框架還有視頻播放的功能,http://www.lxweimin.com/p/f20342033622
在這里寫的是一些視頻播放的一些相關(guān)介紹。