標籤:
iOS的音頻播放可以分為短音頻播放(例如:音效等點綴音頻)和長音頻播放(例:音樂等主音頻)。前者不需要對進度、迴圈等進行控制,而後者需要精確的控制。在iOS中播放這兩種音頻分別使用AudioToolbox.framework和AVFoundation.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方法即可,無需手動管理恢復等。
下面是主要代碼
iOS開發之音頻播放、錄音