重點 (二十四) : 音頻

簡介

簡單來說,音頻可以分為2種

音效

又稱“短音頻”,通常在程序中的播放時長為1~2秒

在應用程序中起到點綴效果,提升整體用戶體驗

音樂

比如游戲中的“背景音樂”,一般播放時間較長

播放音頻需要用到2個框架

AVFoundation.framework

AudioToolbox.framework

音效的播放

1.獲得音效文件的路徑

NSURL*url = [[NSBundle mainBundle] URLForResource:@"m_03.wav" withExtension:nil];

2.加載音效文件,得到對應的音效ID

SystemSoundIDsoundID = 0;

AudioServicesCreateSystemSoundID((__bridge CFURLRef)(url),
&soundID);

3.播放音效

AudioServicesPlaySystemSound(soundID);

音效文件只需要加載1次

音效的播放

音效播放常見函數總結

加載音效文件

AudioServicesCreateSystemSoundID(CFURLRef
inFileURL, SystemSoundID *outSystemSoundID)

釋放音效資源

AudioServicesDisposeSystemSoundID(SystemSoundID
inSystemSoundID)

播放音效

AudioServicesPlaySystemSound(SystemSoundID
inSystemSoundID)

播放音效帶點震動

AudioServicesPlayAlertSound(SystemSoundID inSystemSoundID)

聲音和音效小結——音頻轉換工具
? 轉換aiff格式
? afconvert -f AIFF -d I8 filename
? 轉換caf格式
? afconvert -f caff -d aac -b 32000 filename
? 批量轉換
? find . -name '*.mp3' -exec afconvert -f caff -d aac -b 32000 {} ;

音樂的播放
? 音樂播放用到一個叫做AVAudioPlayer的類
? AVAudioPlayer常用方法
? 加載音樂文件
? - (id)initWithContentsOfURL:(NSURL *)url error:(NSError **)outError;
? - (id)initWithData:(NSData *)data error:(NSError **)outError;
? 準備播放(緩沖,提高播放的流暢性)
? - (BOOL)prepareToPlay;
? 播放(異步播放)
? - (BOOL)play;
? 暫停
? - (void)pause;
? 停止
? - (void)stop;
? 是否正在播放
? @property(readonly, getter=isPlaying) BOOL playing;
? 時長
? @property(readonly) NSTimeInterval duration;
? 當前的播放位置
? @property NSTimeInterval currentTime;
? 播放次數(-1代表無限循環播放,其他代表播放numberOfLoops+1次)
? @property NSInteger numberOfLoops;
? 音量
? @property float volume;
? 是否允許更改速率
? @property BOOL enableRate;
? 播放速率(1是正常速率,0.5是一般速率,2是雙倍速率)
? @property float rate;
? 有多少個聲道
? @property(readonly) NSUInteger numberOfChannels;
? 聲道(-1是左聲道,1是右聲道,0是中間)
? @property float pan;
? 是否允許測量音量
? @property(getter=isMeteringEnabled) BOOL meteringEnabled;
? 更新測量值
? - (void)updateMeters;
? 獲得當前的平均音量
? - (float)averagePowerForChannel:(NSUInteger)channelNumber;

*********************筆記**********************


一. 錄音

  1. 應用場景
    大多數應用在即時通訊APP中, 語音發送

  2. 錄音步驟
    1.導入AVFoundation框架

import <AVFoundation/AVFoundation.h>

2.使用類AVAudioRecorder進行錄音

  1. 創建錄音文件存放路徑
    一般是沙盒路徑
    NSString *path = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"text.caf"];
    NSURL *url = [NSURL URLWithString:path];

  2. 設置錄音附加設置項(#import <AVFoundation/AVAudioSettings.h>)
    NSMutableDictionary *recordSettings = [[NSMutableDictionary alloc] init];
    設置編碼格式
    [recordSettings setValue :[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey: AVFormatIDKey];
    采樣率
    [recordSettings setValue :[NSNumber numberWithFloat:11025.0] forKey: AVSampleRateKey];44100.0
    通道數
    [recordSettings setValue :[NSNumber numberWithInt:2] forKey: AVNumberOfChannelsKey];
    音頻質量,采樣質量
    [recordSettings setValue:[NSNumber numberWithInt:AVAudioQualityMin] forKey:AVEncoderAudioQualityKey];

  3. 根據路徑以及設置項, 創建錄音對象
    _audioRecorder = [[AVAudioRecorder alloc] initWithURL:url settings:recordSettings error:nil];

  4. 準備錄音
    [self.audioRecorder prepareToRecord];

  5. 開始錄音
    [self.audioRecorder record];

  6. 停止錄音
    [self.audioRecorder stop];

  7. 封裝成工具類, 方便下次使用

3.概念補充

  1. 編碼格式

AAC: AAC其實是“高級音頻編碼(advanced audio coding)”的縮寫,它是被設計用來取代MP3格式的。
HE-AAC: HE-AAC是AAC的一個超集,這個“HE”代表的是“High efficiency”。 HE-AAC是專門為低比特率所優化的一種音頻編碼格式
AMR: AMR全稱是“Adaptive Multi-Rate”,它也是另一個專門為“說話(speech)”所優化的編碼格式,也是適合低比特率環境下采用
ALAC: 它全稱是“Apple Lossless”,這是一種沒有任何質量損失的音頻編碼方式,也就是我們說的無損壓縮
IMA4: 這是一個在16-bit音頻文件下按照4:1的壓縮比來進行壓縮的格式。
MP3
LPCM : 即線性脈沖編碼調制,是一種非壓縮音頻數字化技術,是一種未壓縮的原音重現;
參考資料地址
http://baike.baidu.com/link?url=z36Nw7UihAEnCC6BjCygft9rBpLr29Ru0of_9Blpl0aR6qzI1B9iWTD5h3uimPVix2SuuQYo7GVYOIsaaP2Eyq

  1. 文件格式(不同的文件格式, 可保存不同的編碼格式)

WAV :
特點: 音質最好的格式, 對應PCM編碼
適用: 多媒體開發、保存音樂和音效素材。
MP3 :
特點: 音質好,壓縮比比較高,被大量軟件和硬件支持
適用: 適合用于比較高要求的音樂欣賞。
caf :
特點: 適用于幾乎iOS中所有的編碼格式

4.開發經驗

  1. caf 文件格式, 因為某些編碼設置, 文件有可能會很大, 而且caf, 格式并不是很通用, 所以在開發過程中, 一般會進行壓縮轉碼, MP3;
    http://blog.csdn.net/ysy441088327/article/details/7392842

二. 音效播放

  1. 播放音效步驟
    1.音效和音樂?
    其實并沒有嚴格意義上的限定, 一般在開發中, 將時間比較短, 播放頻率比較高的, 當做音效處理;

2.導入AVFoundation框架

import <AVFoundation/AVFoundation.h>

3.使用對應的API, 開始播放音效

  1. 根據音效文件, 來生成SystemSoundID

  2. 獲取URL
    CFURLRef urlRef = (__bridge CFURLRef)([[NSBundle mainBundle] URLForResource:@"m_16.wav" withExtension:nil]);

  3. 創建保存soundID 的變量
    SystemSoundID soundID;

  4. 通過url, 和soundID的地址, 接收對應的音效soundID
    AudioServicesCreateSystemSoundID(urlRef, &soundID);

  5. 播放音效

AudioServicesPlaySystemSound(soundID);
或者
AudioServicesPlayAlertSound(soundID); 帶振動效果
或者
播放完成后, 回調代碼塊
AudioServicesPlaySystemSoundWithCompletion(soundID1, ^{
AudioServicesPlaySystemSound(soundID2);
});

4.代碼優化, 播放工具類的封裝

  1. 優化soundID的生成, 不需要每次都創建一遍
  2. 封裝播放邏輯, 供多處調用

三. 音頻播放
1.導入AVFoundation框架

import <AVFoundation/AVFoundation.h>

2.使用AVAudioPlayer類, 進行播放音頻

  1. 根據音頻文件URL, 創建AVAudioPlayer對象
    獲取資源URL
    NSURL *url = [[NSBundle mainBundle] URLForResource:@"簡單愛.mp3" withExtension:nil];
    根據資源URL, 創建 AVAudioPlayer 對象
    _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];

  2. 準備播放
    準備播放
    [_audioPlayer prepareToPlay];

  3. 開始播放
    [self.audioPlayer play];

3.附加設置

暫停
[self.audioPlayer pause];

停止
[self.audioPlayer stop]; 停止某個音樂, 下次再播放, 會從當前位置開始播放
self.audioPlayer.currentTime = 0; 重置當前播放時間

快進
系統已經對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;

監聽播放事件

  • 設置代理
  • 實現代理方法

4.后臺播放

  1. 勾選后臺模式
  2. 激活音頻播放會話
    ** 注意: 模擬器測試不準確, 以真機為準; **
  3. 代碼
  4. 獲取音頻會話
    AVAudioSession *session = [AVAudioSession sharedInstance];
  5. 設置會話分類
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
  6. 激活會話
    [session setActive:YES error:nil];

5.代碼封裝重構(實戰中, 注意容錯處理)
TODO: 結合QQ音樂案例進行封裝

6.使用須知

每一個AVPlayer對象對應一個音頻播放, 如果想播放多個音頻, 就需要創建多個AVPlayer;

7.測試環境

后臺播放,需要使用真機進行測試, 模擬器不準確

四. QQ音樂

  1. 搭建項目結構
  2. 劃分項目功能模塊, 創建文件夾結構
  3. 項目總共劃分為兩個大模塊

音樂列表

2.主要負責展示音樂列表, 當點擊某一個音樂時, 就播放對應音樂, 停止其他音樂播放

音樂詳情
主要負責展示音樂詳情, 包含音樂名稱, 歌手, 專輯圖片, 歌詞, 進度, 以及控制邏輯

  1. 文件夾結構創建如下
  • Classes

  • QQMusicList(QQ音樂列表)

  • Controller

  • Model

  • View

  • QQMusicDetail(QQ音樂詳情)

  • Controller

  • Model

  • View

  • Ohter(其他)

  • Lib

  • Tool

  • Category

  1. 拖入必要的資源文件和工具類,以及第三方框架(可以使用時再拖入)

  2. 拖入音樂文件, 專輯圖片文件, 歌詞文件等必備資源文件

  3. 一些第三方框架可以等使用時再拖入

  4. 根據界面跳轉邏輯, 搭建storyboard, 并創建好對應的控制器

  5. 導航控制器為初始控制器, 其根控制器為 UITableViewController(QQ音樂列表控制器)

  6. 當點擊QQ音樂列表控制器某一行時, 跳轉到詳情控制器 UIViewController;

  7. 實現QQ音樂列表功能

  8. 加載QQ列表數據
    經驗:千萬不要把獲取數據的實現邏輯在控制器中寫, 不利于維護和重用, 也不利于后期擴展

  9. 創建數據模型
    根據音樂列表的plist文件內容, 創建對應的音樂數據模型 XMGMusicModel

  10. 創建數據操作工具類

主要負責數據的獲取, 和以后數據的操作;
此處提供, 供外界調用的獲取數據的接口
請使用block將數據傳遞出去, 不要直接返回一個數組(因為后期如果改成從網絡獲取列表, 你就懵逼了, 網絡獲取數據是異步的)
代碼如下:
獲取所有音樂列表接口

  • (void)getMusicModelsWithResultBlock:(void(^)(NSArray <XMGMusicModel *> *musicModels))resultBlock;
  1. 在表格控制器內, 調用數據操作類提供的接口, 加載數據并展示

  2. 預留好對接接口
    經驗: 知道到時候在哪調用真正的外界播放接口, 停止接口. 為了統一管理

  3. 播放接口

  4. 停止播放接口

  5. 實現音樂播放功能
    經驗: 千萬不要把播放的業務實現邏輯直接寫在控制器里面, 大哥, 會死人; 應當抽取一個工具類
    高級經驗: 針對于音樂播放功能, 建議分為兩層; 最底層負責單個音樂的播放,暫停,停止等操作; 上層則負責播放的業務邏輯, 比如上一首, 下一首, 隨機播放, 順序播放等等; 易于維護,重用, 擴展!

  6. 封裝單個音樂文件操作的工具類 (XMGAudioTool)

接口如下:
根據音頻名稱播放音頻

  • (BOOL)playAudioWithName:(NSString *)audioName;

根據音頻名稱暫停音頻

  • (void)pauseAudioWithName:(NSString *)audioName;

根據音頻名稱停止音頻

  • (void)stopAudioWithName:(NSString *)audioName;

當前正在播放的播放器
@property (nonatomic, strong) AVAudioPlayer *currentPlayer;

  1. 封裝多個音樂文件操作的工具類 (XMGMusicOptionTool)

接口如下:
根據音樂數據模型, 播放一首音樂

  • (void)playMusicWithMusicM:(XMGMusicModel *)musicM;

根據音樂數據模型, 暫停一首音樂

  • (void)pauseMusicWithMusicM:(XMGMusicModel *)musicM;

根據音樂數據模型, 停止一首音樂

  • (void)stopMusicWithMusicM:(XMGMusicModel *)musicM;

播放當前音樂

  • (void)playCurrentMusic;

暫停當前音樂

  • (void)pauseCurrentMusic;

停止當前音樂

  • (void)stopCurrentMusic;

重要建議:

  • 將此工具類設計成為一個單例; 因為會有很多界面使用; 而且多個界面操作的數據一致
  1. 在預留接口中, 調用工具類的對應接口, 然后測試; QQ音樂列表功能結束;

  2. QQ音樂詳情界面實現

  3. QQ音樂詳情界面搭建

  4. 分析界面結構, 選擇合適控件搭建界面;

  5. 注意將同一組子控件使用一個父控件進行包裝, 方便添加約束布局;

  6. 稍微不好構思的地方在于歌詞界面和專輯界面的切換, 需要借助UIScrollView;

  7. 關聯屬性和方法到對應的詳情控制器, 方便后續的動畫和賦值操作

  8. 擴展音樂播放工具類接口, 實現播放業務邏輯, 并展示音樂詳情

  9. 擴展多個音樂操作的工具類 (XMGMusicOptionTool) 上一首, 下一首 等接口

接口如下:
播放上一首音樂

  • (void)preMusic;

播放下一首音樂

  • (void)nextMusic;
  1. 在控制器對應的關聯方法中, 調用不同的播放接口, 進行測試

  2. 將需要展示的數據按 "刷新頻率" 進行分類, 分別提供 "單次刷新" 和 "實時刷新" 方法

需要根據不同的數據刷新頻率, 采用不同的刷新策略
例如: 如果實時刷新, 就可以使用NSTimer, 使用定時任務不斷刷新, 展示最新數據;
比如播放進度, 就需要不斷刷新;

  1. 匯總所有需要刷新的字段, 根據字段, 創建歌曲播放信息數據模型; 此數據模型由 多個音樂操作的工具類 (XMGMusicOptionTool) 統一提供

不要非常零散的單獨獲取, 到處拼湊;
之所以由 XMGMusicOptionTool 統一提供歌曲播放信息數據模型, 主要原因有兩個: 第一是因為此功能, 應劃分到此類的業務邏輯中; 第二,只有這個類, 最了解當前音樂的播放信息;

  1. 直接從控制器預留的 "單次刷新" 和 "實時刷新" 刷新方法中, 從 XMGMusicOptionTool 中獲取最新的音樂播放數據;

  2. 實時更新歌詞, 并實現進度展示

  3. 創建歌詞數據模型 (XMGLrcModel)

屬性列表
每一句歌詞開始時間
@property (nonatomic, assign) double beginTime;
每一句歌詞結束時間
@property (nonatomic, assign) double endTime;
每一句歌詞內容
@property (nonatomic, copy) NSString *lrcText;

  1. 創建歌詞解析工具類 (XMGLrcTool), 負責解析不同歌曲對應的歌詞文件

接口如下:
根據歌詞文件名稱, 解析歌詞

  • (NSArray <XMGLrcModel *> *)getLrcModelsWithLrcName:(NSString *)lrcName;
    根據某個時間點, 獲取歌詞模型數組中對應的歌詞模型
  • (XMGLrcModel *)getLrcModleInModels:(NSArray <XMGLrcModel >)lrcModels WithTime:(double)time;
  1. 使用UITableView展示歌詞

單獨抽離一個控制器, 負責管理歌詞

  1. 根據當前播放進度, 實時滾動切換歌詞

  2. 根據每句歌詞的播放進度, 通過顏色展示單句歌詞進度

自定義集成自UILabel的子類
重寫 drawRect: 方法
關鍵代碼如下:

CGRect fillRect = CGRectMake(0, 0, rect.size.width * self.progress, rect.size.height);

[[UIColor greenColor] set];

UIRectFillUsingBlendMode(fillRect, kCGBlendModeSourceIn);

  1. QQ音樂后臺播放實現

  2. 勾選后臺模式 音頻播放

  3. 激活音頻播放會話

  4. 獲取音頻會話
    AVAudioSession *session = [AVAudioSession sharedInstance];

  5. 設置會話分類
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];

  6. 激活會話
    [session setActive:YES error:nil];

  7. QQ音樂顯示鎖屏界面, 并接收遠程事件

  8. 顯示鎖屏信息

  9. 獲取鎖屏信息中心
    MPNowPlayingInfoCenter *playInfoCenter = [MPNowPlayingInfoCenter defaultCenter];

  10. 創建鎖屏信息
    NSMutableDictionary *dic = [NSMutableDictionary dictionary];

dic[MPMediaItemPropertyAlbumTitle] = [XMGMusicOptionTool shareXMGMusicOptionTool].musicMessageM.musicM.name;

dic[MPMediaItemPropertyArtist] = [XMGMusicOptionTool shareXMGMusicOptionTool].musicMessageM.musicM.singer;

MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:[XMGMusicOptionTool shareXMGMusicOptionTool].musicMessageM.musicM.icon]];

dic[MPMediaItemPropertyArtwork] = artwork;

dic[MPMediaItemPropertyPlaybackDuration] = @([XMGMusicOptionTool shareXMGMusicOptionTool].musicMessageM.totalTime);

  1. 設置鎖屏信息
    playInfoCenter.nowPlayingInfo = dic;

  2. 接收遠程控制事件
    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

  3. 接收遠程事件
    可以監聽遠程事件的前提

  1. 啟動遠程事件接收
  2. 必須可以成為第一響應者
  3. 應用程序必須是該事件的控制者
    在控制器中實現以下方法

-(void)remoteControlReceivedWithEvent:(UIEvent *)event
{

switch (event.subtype) {
    case UIEventSubtypeRemoteControlPlay:
    {
        [[XMGMusicOptionTool shareXMGMusicOptionTool] playCurrentMusic];
        break;
    }
    case UIEventSubtypeRemoteControlPause:
    {
        [[XMGMusicOptionTool shareXMGMusicOptionTool] pauseCurrentMusic];
        break;
    }
    case UIEventSubtypeRemoteControlNextTrack:
    {
        [[XMGMusicOptionTool shareXMGMusicOptionTool] nextMusic];
        break;
    }
    case UIEventSubtypeRemoteControlPreviousTrack:
    {
        [[XMGMusicOptionTool shareXMGMusicOptionTool] preMusic];
        break;
    }
    default:
        break;
}

}
事件類型對應含義

不包含任何子事件類型
UIEventSubtypeNone = 0,
搖晃事件(從iOS3.0開始支持此事件)
UIEventSubtypeMotionShake = 1,
遠程控制子事件類型(從iOS4.0開始支持遠程控制事件)
播放事件【操作:停止狀態下,按耳機線控中間按鈕一下】
UIEventSubtypeRemoteControlPlay = 100,
暫停事件
UIEventSubtypeRemoteControlPause = 101,
停止事件
UIEventSubtypeRemoteControlStop = 102,
播放或暫停切換【操作:播放或暫停狀態下,按耳機線控中間按鈕一下】
UIEventSubtypeRemoteControlTogglePlayPause = 103,
下一曲【操作:按耳機線控中間按鈕兩下】
UIEventSubtypeRemoteControlNextTrack = 104,
上一曲【操作:按耳機線控中間按鈕三下】
UIEventSubtypeRemoteControlPreviousTrack = 105,
快退開始【操作:按耳機線控中間按鈕三下不要松開】
UIEventSubtypeRemoteControlBeginSeekingBackward = 106,
快退停止【操作:按耳機線控中間按鈕三下到了快退的位置松開】
UIEventSubtypeRemoteControlEndSeekingBackward = 107,
快進開始【操作:按耳機線控中間按鈕兩下不要松開】
UIEventSubtypeRemoteControlBeginSeekingForward = 108,
快進停止【操作:按耳機線控中間按鈕兩下到了快進的位置松開】
UIEventSubtypeRemoteControlEndSeekingForward = 109,

  1. QQ音樂鎖屏界面顯示歌詞
    實現方案: 利用鎖屏顯示圖片設置項; 實時的將歌詞繪制到圖片上, 組成一個新的圖片,設置為鎖屏的圖片
    繪制步驟
  2. 開啟圖形上下文
  3. 繪制背景圖片
  4. 獲取歌詞信息, 并繪制
  5. 從圖形上下文獲取混合圖片
  6. 關閉圖形上下文

五. 播放遠程音樂
使用AVPlayer 來播放遠程音樂
方案1:
1.根據URL, 創建AVPlayer
self.player = [[AVPlayer alloc] initWithURL:remoteURL];
2.播放
[self.player paly];

方案2:
1.根據URL, 創建AVPlayer
NSURL *remoteURL = [NSURL URLWithString:remoteURL];
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithURL:remoteURL];
self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
2.播放
[self.player paly];

方案對比:
如果通過方案1播放某個遠程音頻, 那么后面如果想要更改音樂, 則需要重新創建AVPlayer對象
方案2 就可以直接通過更改播放項來間接更換播放遠程音樂

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容