iOS開發之音頻播放、錄音
來自: http://www.cnblogs.com/wuhongxing/p/5189465.html
iOS的音頻播放可以分為短音頻播放(例如:音效等點綴音頻)和長音頻播放(例:音樂等主音頻)。前者不需要對進度、循環等進行控制,而后者需要精確的控制。在iOS中播放這兩種音頻分別使用AudioToolbox.framewor k和AVFoundat ion.framework來完成。
短音頻音效
AudioToolbox.framework是一套基于C語言的框架,使用它來播放音效其本質是將短音頻注冊到系統聲音服務(System Sound Service)。System Sound Service是一種簡單、底層的聲音播放服務,但是它本身也存在著一些限制:
-
No longer than 30 seconds in duration(播放時間小于30s)
-
In linear PCM or IMA4 (IMA/ADPCM) format(數據必須是pcm或者ima4格式的)
-
Packaged in a .caf , .aif , or .wav file(音頻文件必須打包成.caf,.aif,.wav文件)
使用System Sound Service 播放音效的步驟如下(封裝成一個播放短音頻的工具類):
+ (void)initialize { // 加載所有音頻文件 // 1.遍歷所有的plane.bundle的所有的音頻文件 NSFileManager *manage = [NSFileManager defaultManager]; // 2.獲了plane.bundle的路徑 NSString *planePath = [[NSBundle mainBundle] pathForResource:@"plane.bundle" ofType:nil]; NSArray *contents = [manage contentsOfDirectoryAtPath:planePath error:nil]; // 3.遍歷里面的mp3文件,創建SystemSoundID; NSMutableDictionary *soundDictM = [NSMutableDictionary dictionary]; for (NSString *soundName in contents) { // 音頻的URL NSString *soundUrlPath = [planePath stringByAppendingPathComponent:soundName]; NSURL *soundUrl = [NSURL fileURLWithPath:soundUrlPath]; SystemSoundID soundID; // 創建聲音 AudioServicesCreateSystemSoundID((__bridge CFURLRef)soundUrl, &soundID); soundDictM[soundName] = @(soundID); } soundDict = soundDictM; } - (void)playShortSoundWithName:(NSString *)soundName { // 播放聲音 AudioServicesPlaySystemSound([soundDict[soundName] unsignedIntValue]); }
長音頻播放(使用AVFoundation.framework的AVAudioPlayer實現)
AVAudioPlayer的使用就比較簡單了:
- 初始化AVAudioPlayer對象,此時通常指定本地文件路徑。
- 設置播放器的屬性,例如重復次數、音量大小等。
- 調用play方法播放.
屬性 | 說明 |
@property(readonly, getter=isPlaying) BOOL playing | 是否正在播放,只讀 |
@property(readonly) NSUInteger numberOfChannels | 音頻聲道數,只讀 |
@property(readonly) NSTimeInterval duration | 音頻時長 |
@property(readonly) NSURL *url | 音頻文件路徑,只讀 |
@property(readonly) NSData *data | 音頻數據,只讀 |
@property float pan | 立體聲平衡,如果為-1.0則完全左聲道,如果0.0則左右聲道平衡,如果為1.0則完全為右聲道 |
@property float volume | 音量大小,范圍0-1.0 |
@property BOOL enableRate | 是否允許改變播放速率 |
@property float rate | 播放速率,范圍0.5-2.0,如果為1.0則正常播放,如果要修改播放速率則必須設置enableRate為YES |
@property NSTimeInterval currentTime | 當前播放時長 |
@property(readonly) NSTimeInterval deviceCurrentTime | 輸出設備播放音頻的時間,注意如果播放中被暫停此時間也會繼續累加 |
@property NSInteger numberOfLoops | 循環播放次數,如果為0則不循環,如果小于0則無限循環,大于0則表示循環次數 |
@property(readonly) NSDictionary *settings | 音頻播放設置信息,只讀 |
@property(getter=isMeteringEnabled) BOOL meteringEnabled | 是否啟用音頻測量,默認為NO,一旦啟用音頻測量可以通過updateMeters方法更新測量值 |
對象方法 | 說明 |
- (instancetype)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError | 使用文件URL初始化播放器,注意這個URL不能是HTTP URL,AVAudioPlayer不支持加載網絡媒體流,只能播放本地文件 |
- (instancetype)initWithData:(NSData *)data error:(NSError **)outError | 使用NSData初始化播放器,注意使用此方法時必須文件格式和文件后綴一致,否則出錯,所以相比此方法更推薦使用上述方法或- (instancetype)initWithData:(NSData *)data fileTypeHint:(NSString *)utiString error:(NSError **)outError方法進行初始化 |
- (BOOL)prepareToPlay; | 加載音頻文件到緩沖區,注意即使在播放之前音頻文件沒有加載到緩沖區程序也會隱式調用此方法。 |
- (BOOL)play; | 播放音頻文件 |
- (BOOL)playAtTime:(NSTimeInterval)time | 在指定的時間開始播放音頻 |
- (void)pause; | 暫停播放 |
- (void)stop; | 停止播放 |
- (void)updateMeters | 更新音頻測量值,注意如果要更新音頻測量值必須設置meteringEnabled為YES,通過音頻測量值可以即時獲得音頻分貝等信息 |
- (float)peakPowerForChannel:(NSUInteger)channelNumber; | 獲得指定聲道的分貝峰值,注意如果要獲得分貝峰值必須在此之前調用updateMeters方法 |
- (float)averagePowerForChannel:(NSUInteger)channelNumber | 獲得指定聲道的分貝平均值,注意如果要獲得分貝平均值必須在此之前調用updateMeters方法 |
@property(nonatomic, copy) NSArray *channelAssignments | 獲得或設置播放聲道 |
代理方法 | 說明 |
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag | 音頻播放完成 |
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error | 音頻解碼發生錯誤 |
下面就使用AVAudioPlayer實現一個簡單播放器,在這個播放器中實現了播放、暫停、顯示播放進度功能,當然例如調節音量、設置循環模式、甚至是聲波圖像(通過分析音頻分貝值)等功能都可以實現,這里就不再一一演示。界面效果如下:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. } - (IBAction)stop:(id)sender { /** * Stops playback and undoes the setup needed for playback. */ [_player stop]; // 自己指定播放的時間 _player.currentTime = 0; } - (IBAction)start:(id)sender { // 獲取mp3路徑 NSURL *mp3Url = [[NSBundle mainBundle] URLForResource:@"bbqne.mp3" withExtension:nil]; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _player = [[AVAudioPlayer alloc] initWithContentsOfURL:mp3Url error:nil]; /** * A Boolean value that specifies whether playback rate adjustment is enabled for an audio player. */ _player.enableRate = YES; /** * Prepares the audio player for playback by preloading its buffers. */ [_player prepareToPlay]; // 這里不能加全局斷點,不然會崩 /** * The number of times a sound will return to the beginning, upon reaching the end, to repeat playback. */ _player.numberOfLoops = MAXFLOAT; _player.delegate = self; _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(playMusic)]; [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; AVAudioSession *session = [AVAudioSession sharedInstance]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:session]; _timeSlider.maximumValue = _player.duration; }); /** * Plays a sound asynchronously. */ [_player play]; } /** * 改變進度條 */ - (void)playMusic { _timeSlider.value = _player.currentTime; int sec = _timeSlider.value; _timeLabel.text = [NSString stringWithFormat:@"%02d",sec]; [_player updateMeters]; double lowPassResults = pow(10, (0.05 * [self.player peakPowerForChannel:0])); float result = 10 * (float)lowPassResults; NSLog(@"%.2f",result); } - (void)handleInterruption:(NSNotification *)noti { // 處理中斷事件 NSLog(@"處理中斷事件"); } - (IBAction)pause:(id)sender { /** * Pauses playback; sound remains ready to resume playback from where it left off. */ [_player pause]; } /** * 指定當前播放的時間 */ - (IBAction)timeChanged:(UISlider *)sender { _player.currentTime = sender.value; } /** * 改變播放速度 */ - (IBAction)rateChange:(UISlider *)sender { _player.rate = sender.value; } /** * 改變播放音量 */ - (IBAction)volunmChanged:(UISlider *)sender { _player.volume = sender.value; }
錄音
在AVFoundation框架中還要一個AVAudioRecorder類專門處理錄音操作,它同樣支持 多種音頻格式 。與AVAudioPlayer類似,你完全可以將它看成是一個錄音機控制類,下面是常用的屬性和方法:
@property(readonly, getter=isRecording) BOOL recording; | 是否正在錄音,只讀 |
@property(readonly) NSURL *url | 錄音文件地址,只讀 |
@property(readonly) NSDictionary *settings | 錄音文件設置,只讀 |
@property(readonly) NSTimeInterval currentTime | 錄音時長,只讀,注意僅僅在錄音狀態可用 |
@property(readonly) NSTimeInterval deviceCurrentTime | 輸入設置的時間長度,只讀,注意此屬性一直可訪問 |
@property(getter=isMeteringEnabled) BOOL meteringEnabled; | 是否啟用錄音測量,如果啟用錄音測量可以獲得錄音分貝等數據信息 |
@property(nonatomic, copy) NSArray *channelAssignments | 當前錄音的通道 |
對象方法 | 說明 |
- (instancetype)initWithURL:(NSURL *)url settings:(NSDictionary *)settings error:(NSError **)outError | 錄音機對象初始化方法,注意其中的url必須是本地文件url,settings是錄音格式、編碼等設置 |
- (BOOL)prepareToRecord | 準備錄音,主要用于創建緩沖區,如果不手動調用,在調用record錄音時也會自動調用 |
- (BOOL)record | 開始錄音 |
- (BOOL)recordAtTime:(NSTimeInterval)time | 在指定的時間開始錄音,一般用于錄音暫停再恢復錄音 |
- (BOOL)recordForDuration:(NSTimeInterval) duration | 按指定的時長開始錄音 |
- (BOOL)recordAtTime:(NSTimeInterval)time forDuration:(NSTimeInterval) duration | 在指定的時間開始錄音,并指定錄音時長 |
- (void)pause; | 暫停錄音 |
- (void)stop; | 停止錄音 |
- (BOOL)deleteRecording; | 刪除錄音,注意要刪除錄音此時錄音機必須處于停止狀態 |
- (void)updateMeters; | 更新測量數據,注意只有meteringEnabled為YES此方法才可用 |
- (float)peakPowerForChannel:(NSUInteger)channelNumber; | 指定通道的測量峰值,注意只有調用完updateMeters才有值 |
- (float)averagePowerForChannel:(NSUInteger)channelNumber | 指定通道的測量平均值,注意只有調用完updateMeters才有值 |
代理方法 | 說明 |
- (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag | 完成錄音 |
- (void)audioRecorderEncodeErrorDidOccur:(AVAudioRecorder *)recorder error:(NSError *)error |
程序的構建主要分為以下幾步:
- 設置音頻會話類型為AVAudioSessionCategoryPlayAndRecord,因為程序中牽扯到錄音和播放操作。
- 創建錄音機AVAudioRecorder,指定錄音保存的路徑并且設置錄音屬性,注意對于一般的錄音文件要求的采樣率、位數并不高,需要適當設置以保證錄音文件的大小和效果。
- 設置錄音機代理以便在錄音完成后播放錄音,打開錄音測量保證能夠實時獲得錄音時的聲音強度。(注意聲音強度范圍-160到0,0代表最大輸入)
- 創建音頻播放器AVAudioPlayer,用于在錄音完成之后播放錄音。
- 創建一個定時器以便實時刷新錄音測量值并更新錄音強度到UIProgressView中顯示。
- 添加錄音、暫停、恢復、停止操作,需要注意錄音的恢復操作其實是有音頻會話管理的,恢復時只要再次調用record方法即可,無需手動管理恢復時間等。
下面是主要代碼