Core Audio提供了數字音頻服務爲iOS與OS X, 它提供了一系列框架去處理音頻.git
Core Audio中包含咱們最經常使用的Audio Toolbox與Audio Unit框架.編程
Core Audio在iOS中針對移動平臺的計算資源做出了優化,同時,音頻服務必須嚴格由系統進行管理,特別是HAL與I/O Kit,然而Apple也提供了只在iOS平臺中才有的服務,如Audio Session Service將幫助咱們管理音頻上下文. 性能優化
PCM是最經常使用的無損壓縮數字音頻格式數據,根據採樣率以規則間隔測量模擬(真實世界)數字音頻信號並將每一個採集到的樣本轉換爲數值來建立PCM數據.如標準光盤(CD)音頻使用44.1 kHz的採樣率,16位整數描述每一個樣本 - 構成分辨率或位深度。bash
iOS中使用integer與fixed-point音頻數據,目的是在處理音頻數據時增長計算速度,減少電池能耗.iOS也提供了來自Audio Converter Services的Converter audio unit服務.cookie
iOS與OS X中,Core Audio提供了最經常使用的文件格式用於存儲域播放音頻數據.網絡
Apple針對移動平臺對iOS的Audio Unit做出了效率與性能優化,在開發中咱們必須將audio unit靜態編譯進APP,因此沒法使用別的APP中的Audio Unit.session
大多狀況下,咱們沒法直接與HAL進行交互,Apple提供了一個特別的audio unit,即OS X中的AUHAL, iOS中的AURemoteIO, 咱們能夠經過它們讓音頻與硬件交互.app
Core Audio接口中使用property管理對象的行爲與狀態.框架
Core Audio中經常使用回調函數以實現音頻數據通訊,回調函數常有一下功能less
爲了去使用回調函數,咱們須要作如下兩件事情
Note: 在OC中,回調函數是一個C語言形式的函數,咱們回調OC本類對象做爲對象傳入其中, 因此回調函數中不能直接引用
self.xxx
,須要藉助傳入的OC對象去實現本類的功能.
Core Audio封裝了音頻數據格式,咱們只須要對給定結構體賦正確的參數便可。
struct AudioStreamBasicDescription {
Float64 mSampleRate;
UInt32 mFormatID;
UInt32 mFormatFlags;
UInt32 mBytesPerPacket;
UInt32 mFramesPerPacket;
UInt32 mBytesPerFrame;
UInt32 mChannelsPerFrame;
UInt32 mBitsPerChannel;
UInt32 mReserved;
};
typedef struct AudioStreamBasicDescription AudioStreamBasicDescription;
struct AudioStreamPacketDescription {
SInt64 mStartOffset;
UInt32 mVariableFramesInPacket;
UInt32 mDataByteSize;
};
typedef struct AudioStreamPacketDescription AudioStreamPacketDescription;
複製代碼
注意,上面結構體中mReserved
是Apple的保留參數,必須爲0. 其餘一些參數在特定狀況下也需爲0,如:壓縮音頻格式每一個sample使用不一樣數量的bits。對於這些格式,mBitsPerChannel成員的值爲0。
你能夠手動爲ASBD的成員賦值,若是有些值是你不知道的,能夠賦0,Core Audio將自動選擇適當的值。
iOS: 線性PCM 16bit integer, Noninterleaved linear PCM 8.24bit 定點samples
struct AudioStreamBasicDescription {
mSampleRate = 44100.0;
mFormatID = kAudioFormatLinearPCM;
mFormatFlags = kAudioFormatFlagsAudioUnitCanonical;
mBitsPerChannel = 8 * sizeof (AudioUnitSampleType); // 32 bits
mChannelsPerFrame = 2;
mBytesPerFrame = mChannelsPerFrame * sizeof (AudioUnitSampleType); // 8 bytes
mFramesPerPacket = 1;
mBytesPerPacket = mFramesPerPacket * mBytesPerFrame; // 8 bytes
mReserved = 0;
};
複製代碼
在Core Audio中,magic cookie表示被附加到壓縮音頻數據(文件或流)中的元數據(metadata)。元數據爲解碼器提供了正確解碼文件或流所須要的詳細信息。Core Audio能夠複製,讀取,使用元數據包含的信息。
下面的例子展現瞭如何將一個文件中magic cookie拷貝提供給audio queue.
- (void) copyMagicCookieToQueue: (AudioQueueRef) queue fromFile: (AudioFileID) file {
UInt32 propertySize = sizeof (UInt32);
OSStatus result = AudioFileGetPropertyInfo (
file,
kAudioFilePropertyMagicCookieData,
&propertySize,
NULL
);
if (!result && propertySize) {
char *cookie = (char *) malloc (propertySize);
AudioFileGetProperty (
file,
kAudioFilePropertyMagicCookieData,
&propertySize,
cookie
);
AudioQueueSetProperty (
queue,
kAudioQueueProperty_MagicCookie,
cookie,
propertySize
);
free (cookie);
}
}
複製代碼
音頻數據包(packet)是一個或多個幀的集合,對於特定音頻格式,它是有意義的最小幀集合,所以它是最佳表示一段時間音頻數據的單位。
在CBR,VBR的格式中,對於給定的音頻文件或流,每秒鐘的包數是固定的,
使用audio converter能夠改變音頻採樣率,交錯或不交錯,以及壓縮與未壓縮數據格式相互轉換。
Core Audio中使用Audio File Service爲建立與訪問音頻文件及包含在其中元數據提供了一個強大的抽象。咱們不只可使用文件的ID,type,數據格式,還能夠添加標記,循環,回放等等功能。
AudioFileCreateWithURL (
audioFileURL,
kAudioFileCAFType,
&audioFormat,
kAudioFileFlags_EraseFile,
&audioFileID // the function provides the new file object here
);
複製代碼
AudioFileOpenURL
函數打開一個文件,提供URL,文件類型,訪問權限成功後返回一個文件ID,使用這個ID以及經常使用函數能夠檢索咱們須要的文件信息。下面列舉了一些經常使用函數kAudioFilePropertyFileFormat
kAudioFilePropertyDataFormat
kAudioFilePropertyMagicCookieData
kAudioFilePropertyChannelLayout
複製代碼
當一個VBR文件過大時,檢索信息速度會較慢,可使用kAudioFilePropertyPacketSizeUpperBound and kAudioFilePropertyEstimatedDuration.
這兩個函數快速獲取近似值。
讀寫文件
擴展 Core Audio提供了一個的API,稱爲擴展音頻文件服務。該接口包含音頻文件服務和音頻轉換器服務中的基本功能,提供與線性PCM之間的自動數據轉換
iPhone 支持的Audio file格式
Format name | Format filename extensions |
---|---|
AIFF | .aif,.aiff |
CAF | .caf |
MPEG-1,layer 3 | .mp3 |
MPEG-2 or MPEG-4 ADTS | .aac |
MPEG-4 | .m4a, .mp4 |
WAV | .wav |
AC-3 (Dolby Digital) | .ac3 |
Enhanced AC-3 (Dolby Digital Plus) | .ec3 |
iOS與OS X中原生音頻文件格式爲CAF(Core Audio Format),它能夠支持平臺中任意音頻數據格式。它沒有大小限制,能夠支持多種元數據,如聲道信息,文本註釋等
與音頻文件不一樣,咱們沒法肯定一個audio file stream(音頻流)的開始與結束點.由於咱們每每是經過網絡接受音頻流數據,開始與結束的時機取決於用戶的交互,而且,音頻流數據也沒法保證必定能夠獲取,由於網絡傳輸中可能會存儲在丟幀,暫停等等狀況.
Audio File Service能夠經過解析(parse)讓咱們使用音頻流.經過回調函數獲取parse到的一系列音頻數據.
在iOS中,有時咱們須要處理高優先級任務,如接電話,若是當前APP正在播放視頻,咱們必須作出符合用戶指望的事情以協調APP與系統電話.Audio Session對象充當了二者之間的一箇中介.每一個iPhone應用程序只有一個audio session,經過配置其屬性以使用.
開始以前,咱們要明確下面幾個問題
爲了解決上面的問題,咱們須要配置audio session使用以下特性
Audio Session feature | Description |
---|---|
Categories | 一個category標識着一組音頻行爲的鍵,經過設置分類,能夠代表音頻的行爲,如鎖屏時是否應該繼續播放音頻. |
Interruptions and route changes | 當音頻被中斷或音頻線路發生改變時,audio session將發送一個通知,經過接收通知以做出相應響應. |
Hardware characteristics | 經過audio session能夠查詢當前設備的一些硬件信息,如採樣率,聲道數,輸入源設備等 |
以上行爲是audio session默認分類(kAudioSessionCategory_SoloAmbientSound)的行爲
啓動時,默認的audio session是激活狀態,然而,若是有電話打進來(interruption),audio session立刻處於停用狀態且應用程序音頻中止.若是用戶選擇忽略當前電話,你的應用程序繼續運行,可是audio session還是未激活狀態,音頻沒法繼續工做.
若是應用程序中使用OpenAL, I/O unit, Audio Queue Services,咱們必須寫一個監聽中斷的回調函數,在中斷結束後從新激活audio session.
使用錄製功能的APP是否能錄製取決於當前選擇的硬件音頻輸入端,使用kAudioSessionProperty_AudioInputAvailable
能夠測試當前輸入端是否可用
UInt32 audioInputIsAvailable;
UInt32 propertySize = sizeof (audioInputIsAvailable);
AudioSessionGetProperty (
kAudioSessionProperty_AudioInputAvailable,
&propertySize,
&audioInputIsAvailable // A nonzero value on output means that
// audio input is available
);
複製代碼
應用程序僅有一個audio session分類在同一時間(此規則的一個例外是使用System Sound Services播放的音頻 - 用於警報和用戶界面聲音效果的API。此類音頻始終使用最低優先級的音頻會話類別),
若是你的應用程序不須要雙聲道,精確同步以及播放網絡流音頻,可使用AVAudioPlayer
類實現簡單的音頻播放.
如下使用範圍
NSString *soundFilePath =
[[NSBundle mainBundle] pathForResource: @"sound"
ofType: @"wav"];
NSURL *fileURL = [[NSURL alloc] initFileURLWithPath: soundFilePath];
AVAudioPlayer *newPlayer =
[[AVAudioPlayer alloc] initWithContentsOfURL: fileURL
error: nil];
[fileURL release];
self.player = newPlayer;
[newPlayer release];
[self.player prepareToPlay];
[self.player setDelegate: self];
- (void) audioPlayerDidFinishPlaying: (AVAudioPlayer *) player
successfully: (BOOL) flag {
if (flag == YES) {
[self.button setTitle: @"Play" forState: UIControlStateNormal];
}
}
- (IBAction) playOrPause: (id) sender {
// if already playing, then pause
if (self.player.playing) {
[self.button setTitle: @"Play" forState: UIControlStateHighlighted];
[self.button setTitle: @"Play" forState: UIControlStateNormal];
[self.player pause];
// if stopped or paused, start playing
} else {
[self.button setTitle: @"Pause" forState: UIControlStateHighlighted];
[self.button setTitle: @"Pause" forState: UIControlStateNormal];
[self.player play];
}
[self.player setVolume: 1.0]; // available range is 0.0 through 1.0
複製代碼
Audio Queue Services提供了一種低開銷,直接的方式去錄製和播放音頻,它使你的應用程序使用硬件(麥克風與揚聲器)錄製與播放而且無需瞭解硬件接口.它也讓咱們使用複雜的編解碼器而無需瞭解編解碼器的工做原理.
Audio Queue提供了更精確的定時播放以支持預約播放與同步,你可使用它去同步多個音頻播放隊列,同時播放聲音,獨立控制每一個隊裏的音量以及循環播放.
Audio Queue與AVAudioPlayer二者是在iPhone上播放音頻的惟一方式
經過屬性與回調函數讓咱們與audio queue對象間交互.對於錄製,咱們經過回調函數接收音頻數據.
對於播放回調,當你的音頻播放隊列須要播放一個音頻數據時它將被調用.你的回調函數將從磁盤讀取指定數量的音頻數據包而後將它們封裝在audio queue對象的buffer中.audio queue將按順序播放這些buffer.
實現一個播放隊列
a. 建立一個結構體管理audio queue須要的信息,如音頻格式,採樣率等等
b. 定義一個回調函數管理audio queue buffers,這個回調函數使用Audio File Services去讀取你想要播放的文件.
c. 初始化audio queue而且使用AudioQueueNewOutput建立對象.
static const int kNumberBuffers = 3;
// Create a data structure to manage information needed by the audio queue
struct myAQStruct {
AudioFileID mAudioFile;
CAStreamBasicDescription mDataFormat;
AudioQueueRef mQueue;
AudioQueueBufferRef mBuffers[kNumberBuffers];
SInt64 mCurrentPacket;
UInt32 mNumPacketsToRead;
AudioStreamPacketDescription *mPacketDescs;
bool mDone;
};
// Define a playback audio queue callback function
static void AQTestBufferCallback(
void *inUserData,
AudioQueueRef inAQ,
AudioQueueBufferRef inCompleteAQBuffer
) {
myAQStruct *myInfo = (myAQStruct *)inUserData;
if (myInfo->mDone) return;
UInt32 numBytes;
UInt32 nPackets = myInfo->mNumPacketsToRead;
AudioFileReadPackets (
myInfo->mAudioFile,
false,
&numBytes,
myInfo->mPacketDescs,
myInfo->mCurrentPacket,
&nPackets,
inCompleteAQBuffer->mAudioData
);
if (nPackets > 0) {
inCompleteAQBuffer->mAudioDataByteSize = numBytes;
AudioQueueEnqueueBuffer (
inAQ,
inCompleteAQBuffer,
(myInfo->mPacketDescs ? nPackets : 0),
myInfo->mPacketDescs
);
myInfo->mCurrentPacket += nPackets;
} else {
AudioQueueStop (
myInfo->mQueue,
false
);
myInfo->mDone = true;
}
}
// Instantiate an audio queue object
AudioQueueNewOutput (
&myInfo.mDataFormat,
AQTestBufferCallback,
&myInfo,
CFRunLoopGetCurrent(),
kCFRunLoopCommonModes,
0,
&myInfo.mQueue
);
複製代碼
Audio queue對象提供了兩種方式控制播放音量,一種是直接設置,以下,設置後能夠當即生效.
Float32 volume = 1;
AudioQueueSetParameter (
myAQstruct.audioQueueObject,
kAudioQueueParam_Volume,
volume
);
複製代碼
另外一種是使用AudioQueueEnqueueBufferWithParameters
,設置後在audio queue buffer開始播放時生效.
經過查詢audio queue對象的kAudioQueueProperty_CurrentLevelMeterDB
屬性能夠獲取當前播放的級別.
typedef struct AudioQueueLevelMeterState {
Float32 mAveragePower;
Float32 mPeakPower;
}; AudioQueueLevelMeterState;
複製代碼
爲了同時播放多個音頻,須要爲每一個音頻建立一個播放audio queue對象.對於每一個audio queue,使用AudioQueueEnqueueBufferWithParameters
函數安排第一個音頻buffer同時啓動。
同時播放多個音頻,音頻格式顯得相當重要,由於iOS中某些音頻格式使用了高效的硬件編解碼器,只能在設備上播放如下格式之一的單個實例.
a. AAC
b. ALAC
c. MP3
若是要播放高質量同步的音頻,須要使用線性PCM或IMA4格式.
a. 線性PCM和IMA / ADPCM(IMA4)音頻您能夠在iOS中同時播放多個線性PCM或IMA4格式聲音,而不會產生CPU資源問題。
b. AAC,MP3和Apple Lossless(ALAC)一次只能播放一首此類聲音
開源的OpenAL音頻API(可在OpenAL框架中使用,構建於Core Audio之上)針對播放期間的聲音定位進行了優化。使用OpenGL建模的界面,OpenAL能夠輕鬆播放,定位,混合和移動聲音,OpenAL和OpenGL共享一個通用座標系統,使您能夠同步音頻和視頻。OpenAL直接使用Core Audio的I / O audio unit),從而實現最低延遲播放。OpenAL是在iPhone和iPod touch上播放遊戲應用中的聲音效果的最佳選擇。
Audio Toolbox中的AudioServices.h提供了系統的聲音服務,當你僅僅想播放一個系統的短音頻時,它將是最好的選擇,iOS中播放系統聲音最不不能超過30秒.
在iOS中,調用AudioServicesPlaySystemSound
能夠當即播放,你也能夠調用AudioServicesPlayAlertSound
提示用戶是否播放.
調用AudioServicesPlaySystemSound
時使用kSystemSoundID_Vibrate
常量能夠顯式設置振動效果.
#include <AudioToolbox/AudioToolbox.h>
#include <CoreFoundation/CoreFoundation.h>
// Define a callback to be called when the sound is finished
// playing. Useful when you need to free memory after playing.
static void MyCompletionCallback (
SystemSoundID mySSID,
void * myURLRef
) {
AudioServicesDisposeSystemSoundID (mySSID);
CFRelease (myURLRef);
CFRunLoopStop (CFRunLoopGetCurrent());
}
int main (int argc, const char * argv[]) {
// Set up the pieces needed to play a sound.
SystemSoundID mySSID;
CFURLRef myURLRef;
myURLRef = CFURLCreateWithFileSystemPath (
kCFAllocatorDefault,
CFSTR ("../../ComedyHorns.aif"),
kCFURLPOSIXPathStyle,
FALSE
);
// create a system sound ID to represent the sound file
OSStatus error = AudioServicesCreateSystemSoundID (myURLRef, &mySSID);
// Register the sound completion callback.
// Again, useful when you need to free memory after playing.
AudioServicesAddSystemSoundCompletion (
mySSID,
NULL,
NULL,
MyCompletionCallback,
(void *) myURLRef
);
// Play the sound file.
AudioServicesPlaySystemSound (mySSID);
// Invoke a run loop on the current thread to keep the application
// running long enough for the sound to play; the sound completion
// callback later stops this run loop.
CFRunLoopRun ();
return 0;
}
複製代碼
在iOS中,Audio Unit爲應用程序提供了實現低延遲輸入和輸出的機制。它們還提供某些DSP功能.
iOS中Audio Unit輸入輸出使用8.24位定點線性PCM音頻數據.惟一例外的是如下狀況.
每一個Audio Unit的惟一標識符由類型,子類型,製造商代碼(type, subtype, and manufacturer code)肯定.每種子類型更加精確的描述了audio unit的用途.Audio Unit使用屬性配置音頻信息,如 Properties, Scopes, and Elements.每種audio unit須要一些指定屬性,
iOS中能夠用的錄製和播放編解碼器來平衡音頻質量,應用程序開發的靈活性,硬件功能和電池壽命。
AUGraph:定義了一組複雜的音頻執行任務.