音頻處理
一.錄音
- 錄音應用場景
- 語音聊天
- 即時通訊軟件中,都包含語音發送功能
- 語音備忘錄
- 錄一段音頻,來記錄某件事情
- 語音聊天
- 錄音功能實現
- 導入AVFoundation框架
- 作用:一些多媒體的處理,基本上都是用這個框架
- #import <AVFoundation/AVFoundation.h>
- 使用AVAudioRecorder進行錄音
- 創建錄音文件存放路徑
- 設置錄音附加設置項
- #import <AVFoundation/AVAudioSettings.h>
- 錄音參數設置,保存到字典中,不需要掌握,固定的設置
- 設置編碼格式:AVFormatIDKey
- 采樣率:AVSampleRateKey
- 通道數:AVNumberOfChannelsKey
- 音頻質量,采樣質量:AVEncoderAudioQualityKey
- 根據路徑以及設置項,創建錄音對象
- _audioRecorder = [[AVAudioRecorder alloc] initWithURL:url settings:recordSettings error:nil];
- 准備錄音
- [self.audioRecorder prepareToRecord];
- 開始錄音
- [self.audioRecorder record];
- 停止錄音
- [self.audioRecorder stop];
- 導入AVFoundation框架
- 概念補充
- 編碼
- 概念:編碼是信息從一種形式轉換為另一種形式的過程
- 編碼格式:
- PCM:脈沖編碼調制,是一種非壓縮音頻數字化技術,是一種未壓縮的原音重現,數字模式下,音頻的廚師信號是PCM
- MP3
- AAC:AAC其實是"高級音頻編碼(advanced audio coding)"的縮寫,它是被設計用來取代MP3格式的
- HE-AAC:HE-AAC是AAC的一個超集,這個"HE"代表的是"high efficiency",HE-AAC是專門為低比特率所優化的一種音頻編碼格式
- AMR:AMR全稱是"adaptive multi-rate"的縮寫,它也是專門為低比特率所優化的一種音頻編碼格式
- ALAC:它全稱是"Apple Lossless",這是一種沒有任何質量損失的音頻編碼方式,也就是我們說的無損壓縮
- IMA4:這是一個在16bit音頻文件下按照4:1的壓縮比來進行壓縮的格式
- 文件格式(不同的文件格式,可保存不同的編碼格式編碼的文件)
- wav
- 特點:音質最好的格式,對應PCM編碼
- 適用:多媒體開發/保存音樂/音效素材
- mp3
- 特點:音質好,壓縮比比較高,被大量軟件和硬件支持
- 適用:適合用於比較高要求的音樂欣賞
- caf
- 特點:適用於幾乎iOS中所有的編碼格式
- wav
- 編碼
- 開發經驗
- caf文件格式,因為某些編碼設置,文件可能會很大,而且caf格式並不是很通用,所以在開發過程中,一般會進行壓縮轉碼為MP3格式
- lame靜態庫
- 簡介
- LAME是一個開源的MP3音頻壓縮軟件
- LAME是一個遞歸縮寫,來自LAME Ain't an MP3 Encoder(LAME不是MP3編碼器)
- 它自1998年以來由一個開源社區開發,目前是公認有損品質MP3中壓縮效果最好的編碼器
- 提升權限
- sudo -s
- 下載lame的最新版本並解壓
- 生成靜態庫
- 下載build的腳本
- 修改腳本權限(所有人有可執行權限)
- chmod a+x 腳本名稱
- 執行腳本
- ./腳本名稱
- 導入靜態庫到工程,開始使用
- 簡介
二.音效播放
1.音效概念簡介
- 音效和音樂的區別
- 其實並沒有嚴格意義上的限定,一般在開發中,將時間比較短,播放頻率比較高的,當做音效處理
- 將播放時間比較長,需要監聽播放進度,控制播放速率等操作的音頻當做音樂處理
2.導入AVFoundation框架
- 其實音效處理對應的框架是AudioToolbox,只不過AVFoundation框架包含了此框架
3.使用對應的API,開始播放音效
- 根據音效文件,生成SystemSoundID
- 獲取URL
- CFURLRef urlRef = (__bridge CFURLRef)([[NSBundle mainBundle] URLForResource:@"m_16.wav" withExtension:nil]);
- 創建保存soundID的變量
- SystemSoundID soundID;
- 通過URL和SoundID的地址,接收對應的音效SoundID
- AudioServicesCreateSystemSoundID(urlRef, &soundID);
- 獲取URL
- 播放音效
- AudioServicesPlaySystemSound(soundID);
- 效果:直接播放,沒有震動效果
- AudioServicesPlayAlertSound(soundID);
- 效果:直接播放,有震動效果
- AudioServicesPlaySystemSoundWithCompletion(soundID,block);
- 效果:帶有播放完成回調代碼塊
- AudioServicesPlaySystemSound(soundID);
- 根據SoundID釋放內存
- AudioServicesDisposeSystemSoundID(soundID);
- 代碼優化,播放工具類的封裝
- 優化SoundID的生成,不需要每次都創建一遍
- 封裝播放邏輯,供多處調用
三.音樂播放
1.音樂播放
- 導入AVFoundation框架
- 使用AVAudioPlayer類,進行播放音樂
- 根據音頻文件URL,創建AVAudioPlayer對象
- 獲取資源URL
- NSURL *url = [[NSBundle mainBundle] URLForResource:@"test.mp3" withExtension:nil];
- 根據資源URL,創建AVAudioPlayer對象
- AVAuioPlayer audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
- 獲取資源URL
- 准備播放
- [self.audioPlayer prepareToPlay];
- 開始播放
- [self.audioPlayer play];
- 根據音頻文件URL,創建AVAudioPlayer對象
- 附加設置
- 暫停
- [self.audioPlayer pause];
- 停止
- [self.audioPlayer stop];
- 停止某個音樂,下次再播放,會從當前位置開始播放
- self.audioPlayer.currentTime = 0;
- 重置當前播放時間
- [self.audioPlayer stop];
- 快進
- 系統已經對currentTime做了容錯處理,不用擔心事件為負數或者大於音樂總時長
- self.audioPlayer.currentTime += 5;
- 倍速播放
- 1.0為正常
- 設置允許調整播放速率,注意,此方法必須設置在准備播放之前(經測試,在播放前也可以)
- self.audioPlayer = nil;
- self.audioPlayer.enableRate = YES;
- self.audioPlayer.rate = 2.0;
- 音量調節
- 音量調節范圍:0.0~1.0
- self.audioPlayer.volume = slider.value;
- 監聽播放事件
- 設置代理
- 實現代理方法
- 暫停
- 后台播放
- 注意:模擬器測試不准確,以真機為准
- 勾選后台模式
- 激活音頻播放會話
- 在iOS中每個應用都有一個音頻會話,這個會話就可以通過AVAudioSessiion來表示
- AVAudioSession同樣存在於AVFoundation框架中,它是單例模式設計,通過sharedInstance進行訪問
- 音頻會話類型簡介
- AVAudioSessionCategoryAmbient
- 混音播放,可以與其他音頻應用同時播放
- AVAudioSessionCategorySoloAmbient
- 獨占播放
- AVAudioSessionCategoryPlayback
- 后台播放,也是獨占的
- AVAudioSessionCategoryRecord
- 錄音模式,用於錄音時使用
- AVAudioSessionCategoryPlayAndRecord
- 播放和錄音,此時可以錄音也可以播放
- AVAudioSessionCategoryProcessing
- 硬件解碼音頻,此時不能播放和錄制
- AVAudioSessionCategoryMultiRoute
- 多種輸入輸出,例如可以耳機/USB設備同時播放
- AVAudioSessionCategoryAmbient
- 代碼實現
- 獲取音頻會話
- AVAudioSession *session = [AVAudioSession sharedInstace];
- 設置會話分類
- [session setCategory: AVAudioSessionCategoryPlayback error:nil];
- 激活會話
- [session setActive:YES error:nil];
- 獲取音頻會話
- 代碼封裝重構(注意容錯處理)
- 結合QQ音樂案例進行封裝
- 使用須知
- 每一個AVPlayer對象對應一個音頻播放,如果想播放多個音頻,就需要創建多個AVPlayer
- 使用AVAudioPlayer類,只能播放本地資源,不能播放遠程音樂
- 測試環境
- 后台播放,需要使用真機進行測試,模擬器測試不准確
2.模仿QQ音樂
- 搭建項目結構
- 划分項目功能模塊,創建文件夾結構
- 音樂列表
- 主要負責展示音樂列表,當點擊某一個音樂時,就播放對應音樂,停止其他音樂播放
- 音樂詳情
- 主要負責展示音樂詳情,包含音樂名稱/歌手/專輯圖片/歌詞/進度以及控制邏輯
- 音樂列表
- 拖入必要的資源和工具類,以及第三方框架(可以使用時再拖入)
- 根據界面跳轉邏輯,搭建Storyboard,並創建好對應的控制器
- 導航控制器為初始控制器,其根控制器為UITableViewController(QQ音樂列表控制器)
- 當點擊QQ音樂列表控制器某一行時,跳轉到詳情控制愛UIViewController
- 划分項目功能模塊,創建文件夾結構
- 實現音樂列表功能
- 界面基本設置
- 背景圖片
- 隱藏導航欄
- 狀態欄設置為白色
- 加載QQ列表數據
- 經驗:千萬不要把獲取數據的實現邏輯寫在控制器中,不利於維護和重用,也不利於后期擴展
- 創建數據模型
- 根據音樂列表plist文件內容,創建對應的音樂數據模型xxxMusicModel
- 創建數據操作工具類
- 主要負責數據的獲取,和以后數據的操作
- 此處提供,供外界調用的獲取數據的接口
- 請使用block將數據傳遞出去,不要直接返回一個數組(因為后期如果改為從網絡獲取列表,因為網絡獲取數據是異步的,所以返回的結果可能為nil)
- 獲取所有音樂列表的接口如下:
- getMusicModelsWithResultBlock:block
- 在表格控制器內,調用數據操作類提供的接口,加載數據並顯示
- 音樂列表界面展示
- 使用自定義cell,以便后期擴充
- 預留好對接接口
- 經驗:知道到時候在哪里調用真正的外接播放接口/停止接口,為了統一管理
- 播放接口
- 停止播放接口
- 實現音樂播放功能
- 經驗:千萬不要把播放的業務實現邏輯直接寫在控制器里面,應當抽取一個工具類
- 高級經驗:針對於音樂播放功能,建議分為兩層
- 最底層負責單個音樂的播放/暫停/停止等操作
- 上層則負責播放的業務邏輯,比如上一首/下一首/隨機播放/順序播放等
- 這樣易於維護,重用和擴展
- 封裝單個音樂文件操作的工具類(xxxAudioTool)
- 接口1:根據音頻名稱播放音頻
- playAudioWithName:
- 接口2:暫停音頻
- pauseCurrentAudio
- 接口3:停止音頻
- stopCurrentAudio
- 接口1:根據音頻名稱播放音頻
- 封裝多個音樂文件操作的工具類(xxxMusicOperationTool)
- 接口1:根據音樂數據模型,播放一首音樂
- playMusicWithMusicModel:
- 接口2:暫停當前音樂
- pauseCurrentMusic
- 接口3:停止當前音樂
- stopCurrentMusic
- 接口1:根據音樂數據模型,播放一首音樂
- 重要建議:
- 將此工具類設計成為一個單例
- 因為會有很多界面使用,而且多個界面操作的數據一致
- 功能測試
- 在預留接口中,調用工具類的對應接口,然后測試
- 音樂列表功能開發結束
- 界面基本設置
- 詳情界面實現
- 音樂詳情界面搭建
- 分析界面結構,選擇合適控件搭建界面
- 注意將同一組子控件使用一個父控件進行包裝,方便添加約束布局
- 稍微不好構思的地方在於歌詞界面和專輯界面的切換,需要借助UIScrollView
- 關聯屬性和方法到對應的詳情控制器,方便后續的動畫和賦值操作
- 擴展音樂播放工具類接口,實現播放業務邏輯,並展示音樂詳情
- 擴展多個音樂操作的工具類(xxxMusicOperationTool)的上一首/下一首等接口
- 接口1:播放上一首音樂
- preMusic
- 接口2:播放下一首音樂
- nextMusic
- 接口1:播放上一首音樂
- 在控制器對應的關聯方法中,調用不同的播放接口,進行測試
- 將需要展示的數據按"刷新頻率"進行分類,分別提供"單次刷新"和"實時刷新"方法
- 需要根據不同的數據刷新頻率,采用不同的刷新策略
- 如果實時刷新,就可以使用NSTimer,使用定時任務不斷刷新,展示最新數據,比如播放進度,就需要不斷刷新
- 而歌曲圖片和作者,只需要刷新一次
- 匯總所有需要刷新的字段,根據字段,創建歌曲播放信息數據模型(此數據模型由多個音樂操作的工具類同一提供)
- 不要非常零散的單獨獲取,到處拼湊
- 之所以由工具類統一提供歌曲播放信息數據模型,主要原因
- 因為此功能,應該划分到此類的業務邏輯中
- 只有這個類,最了解當前音樂的播放信息
- 直接從控制器預留的"單次刷新"和"實時刷新"刷新方法中,從多個音樂操作的工具類中獲取最新的音樂播放數據
- 擴展多個音樂操作的工具類(xxxMusicOperationTool)的上一首/下一首等接口
- 實時更新歌詞,並實現進度展示
- 創建歌詞數據模型(xxxLrcModel)
- 屬性列表
- 每一句歌詞開始時間
- @property (nonatomic, assign) double beginTime;
- 每一句歌詞結束時間
- @property (nonatomic, assign) double endTime;
- 每一句歌詞的內容
- @property (nonatomic, copy) NSString *lrcText;
- 創建歌詞解析工具類(xxxLrcTool),負責解析不同歌曲對應的歌詞文件
- 根據歌詞文件名稱,解析歌詞
- getLrcModelsWithLrcName:
- 根據某個時間點,獲取歌詞模型數組中對應的歌詞模型
- getLrcModelInModels:withTime:
- 根據歌詞文件名稱,解析歌詞
- 使用UITableView展示歌詞
- 單獨抽離一個控制器,負責管理歌詞
- 根據當前播放進度,實時滾動切換歌詞
- 根據每句歌詞的播放進度,通過顏色展示單句歌詞進度
- 自定義集成自UILabel的子類
- 重寫drawRect:方法
- CGRect fillRect = CGRectMake(0,0,rect.size.width*self.progress,rect.size.height);
- [[UIColor greenColor] set];
- UIRectFillUsingBlendMode(fillRect,kCGBlendModeSourceIn);
- 創建歌詞數據模型(xxxLrcModel)
- 音樂詳情界面搭建
- 后台播放實現
- 勾選后台模式->音頻播放
- 激活音頻播放會話
- 獲取音頻會話
- 設置會話分類
- 激活會話
- 顯示鎖屏界面,並接收遠程事件
- 顯示鎖屏信息
- 獲取鎖屏信息中心
- MPNowPlayingInfoCenter *playInfoCenter = [MPNowPlayingInfoCenter defaultCenter];
- 創建鎖屏信息(信息從多音樂操作工具類中獲取,並保存到可變字典dic中)
- 設置鎖屏信息
- playInfoCenter.nowPlayingInfo = dic;
- 接收遠程控制事件
- [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
- 獲取鎖屏信息中心
- 接收遠程事件
- 可以監聽遠程事件的前提
- 必須啟動遠程事件接收
- 必須可以成為第一響應者
- 應用程序必須是該事件的控制者
- 在控制器中需要實現的方法
- remoteControlReceivedWithEvent:
- 補充:事件類型對應的含義
- UIEventSubtypeNone
- 不包含任何字事件類型
- UIEventSubtypeMotionShake
- 搖晃事件(從iOS3.0開始支持此事件)
- 遠程控制自事件類型(從iOS4.0開始支持遠程控制事件)
- UIEventSubtypeRemoteControlPlay
- 播放事件(操作:停滯狀態下,按二級線控中間按鈕一下)
- UIEventSubtypeRemoteControlPause
- 暫停事件
- UIEventSubtypeRemoteControlStop
- 停止事件
- UIEventSubtypeRemoteControlTogglePlayPause
- 播放或暫停切換(操作:播放或暫停狀態下,按耳機線控中間按鈕一下)
- UIEventSubtypeRemoteControlNextTrack
- 下一曲(操作:按耳機線控中間按鈕兩下)
- UIEventSubtypeRemoteControlPreviousTrack
- 上一曲(操作:按耳機線控中間按鈕三下)
- UIEventSubtypeRemoteControlBeginSeekingBackward
- 快退開始(操作:按耳機線控中間按鈕三下不要松開)
- UIEventSubtypeRemoteControlEndSeekingBackward
- 快退停止(操作:按耳機線控中間按鈕三下到了快退的位置松開)
- UIEventSubtypeRemoteControlBeginSeekingForward
- 快進開始(操作:按耳機線控中間按鈕兩下不要松開)
- UIEventSubtypeRemoteControlEndSeekingForward
- 快進停止(操作:按耳機線控中間按鈕兩下到了快進的位置松開)
- UIEventSubtypeNone
- 可以監聽遠程事件的前提
- 顯示鎖屏信息
- 鎖屏界面顯示歌詞
- 實現方案:利用鎖屏顯示圖片設置項,實時的將歌詞繪制到圖片上,組成一個新的圖片,設置為鎖屏的圖片
- 繪制步驟
- 開啟圖形上下文
- 繪制背景圖片
- 獲取歌詞信息,並繪制
- 從圖形上下文中獲取混合圖片
- 關閉圖形上下文
- 注意:效率優化
- 功能完善,細節處理
- 通過進度條拖拽,控制播放進度
- 通過點擊進度條某個位置,控制播放進度
- 自動播放下一首
- 列表歌詞頁的歌詞進度展示
- iOS9.0新推出的Storyboard Reference
3.播放遠程音樂
- 使用AVPlayer來播放遠程音樂
- 方案1:
- 根據URL,創建AVPlayer
- self.player = [[AVPlayer alloc] initWithURL:remoteURL];
- 播放
- [self.player play];
- 根據URL,創建AVPlayer
- 方案2:
- 根據AVPlayerItem,創建AVPlayer
- NSURL *remoteURL = [NSURL URLWithString:remoteURL];
- AVPlayerItem *playerItem = [AVPlayerItem playerItemWihtURL:remoteURL];
- self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
- 播放
- [self.player play];
- 根據AVPlayerItem,創建AVPlayer
- 方案對比
- 如果通過方案1播放某個遠程音頻,那么后面如果想要更改音樂,則需要重新創建AVPlayer對象
- 方案2就可以直接通過更改播放項來間接更換播放遠程音樂
