Linear PCM
在介紹Core Audio之前,先介紹一下最常用的非壓縮數(shù)字音頻格式Linear PCM(線性脈沖編碼調(diào)制)。在計(jì)算機(jī)錄音的過(guò)程中,他會(huì)每隔一定的時(shí)間會(huì)來(lái)抽樣獲取現(xiàn)實(shí)中的音頻信號(hào)的強(qiáng)度并且將獲取到的數(shù)據(jù)轉(zhuǎn)換成一個(gè)數(shù)值。CD音頻通常的抽樣的頻率是44.1kHz(sample rate),每個(gè)抽樣用一個(gè)16bit的整數(shù)來(lái)表示(bit depth)。
一個(gè)抽樣代表的是對(duì)一個(gè)音頻通道的數(shù)字量化值。
幀(frame)是一系列抽樣的集合。例如,一個(gè)立體聲的聲音文件中,每一幀會(huì)有2個(gè)抽樣,一個(gè)是左聲道的抽樣,一個(gè)是右聲道的。
Package是一系列幀的集合。在Linear PCM中,每個(gè)package總是包含一幀。在壓縮的音頻格式中,一個(gè)package可能有更多的幀。Package對(duì)已一個(gè)給定的音頻數(shù)據(jù)格式來(lái)說(shuō),是最小的有意義的集合。
在Linear PCM中,一個(gè)抽樣的大小和他所表示的信號(hào)的強(qiáng)度成線性關(guān)系。一個(gè)16位的標(biāo)準(zhǔn)CD音頻可以表示從靜音到最高的聲音總共65,536種可能的值。相鄰的兩個(gè)數(shù)值之間的信號(hào)的強(qiáng)度的差別總是相等的。
Core Audio在他的數(shù)據(jù)結(jié)構(gòu)中可以表示任何采樣率和位深的Linear PCM。
Core audio
Core audio是ios系統(tǒng)中用于處理和音頻相關(guān)的框架。下圖是Core audio的架構(gòu)。
Audio Queue Service主要負(fù)責(zé)聲音的錄制播放。
System sounds使用來(lái)播放系統(tǒng)的聲音和用戶界面的聲音效果
AVAudioPlayer提供了一個(gè)簡(jiǎn)單的OC接口的音頻播放器。
Audio File
Services主要負(fù)責(zé)來(lái)讀取和寫(xiě)入音頻數(shù)據(jù)和文件。
Audio Session
Services主要是用來(lái)管理你的app在移動(dòng)設(shè)備中的聲音的行為
Audio units包含一系列的底層的音頻處理插件,可以處理混音、均衡、實(shí)時(shí)的錄音和播放等
Audio File
Stream Services主要負(fù)責(zé)處理流媒體的音頻文件的播放
OpenAL主要是游戲開(kāi)發(fā)中使用的比較多,主要是根據(jù)標(biāo)準(zhǔn)實(shí)現(xiàn)了位置性的音源功能
Codecs主要是對(duì)音頻文件進(jìn)行編解碼,也就是對(duì)ios平臺(tái)支持的壓縮格式的編解碼
音頻的數(shù)據(jù)格式
系統(tǒng)使用AudioStreamBasicDescription和AudioStreamPacketDescription描述音頻的格式。他們的結(jié)構(gòu)如下所示:
struct AudioStreamBasicDescription {
Float64 mSampleRate;
UInt32mFormatID;
UInt32mFormatFlags;
UInt32mBytesPerPacket;
UInt32mFramesPerPacket;
UInt32mBytesPerFrame;
UInt32mChannelsPerFrame;
UInt32mBitsPerChannel;
UInt32mReserved;
};
typedef structAudioStreamBasicDescriptionAudioStreamBasicDescription;
structAudioStreamPacketDescription {
SInt64mStartOffset;
UInt32mVariableFramesInPacket;
UInt32mDataByteSize;
};
typedef struct AudioStreamPacketDescriptionAudioStreamPacketDescription;
獲取文件的數(shù)據(jù)格式
你可以手動(dòng)的填充音頻格式這個(gè)數(shù)據(jù)結(jié)構(gòu),也可以從系統(tǒng)中獲取。
- (void) openPlaybackFile: (CFURLRef)soundFile {
AudioFileOpenURL (
(CFURLRef) self.audioFileURL,
0x01,// readonly
kAudioFileCAFType,
&audioFileID
);
UInt32 sizeOfPlaybackFormatASBDStruct =sizeof ([self audioFormat]);
AudioFileGetProperty (
[self audioFileID],
kAudioFilePropertyDataFormat,
&sizeOfPlaybackFormatASBDStruct,
&audioFormat// thesound file's ASBD is returned here
);
}
標(biāo)準(zhǔn)音頻數(shù)據(jù)格式
對(duì)于不同的平臺(tái),Core
Audio有一兩個(gè)標(biāo)準(zhǔn)的音頻格式,這種格式會(huì):
1.在音頻格式轉(zhuǎn)化過(guò)程中,被指定為中間格式
2.在Core Audio中被特殊優(yōu)化過(guò)
3.當(dāng)你沒(méi)有設(shè)置音頻格式的時(shí)候,會(huì)作為默認(rèn)的格式
CoreAudio的標(biāo)準(zhǔn)格式是:
[if !supportLists]1.[endif]ios輸入輸出Linear PCM格式,并且位深是16bit
[if !supportLists]2.[endif]iOS audio units和其他的音頻處理使用linear PCM,并且使用8.24-bit定點(diǎn)數(shù)采樣
下面是一個(gè)雙聲道,標(biāo)準(zhǔn)的iPhone audio unit格式,采樣頻率是44.1 kHz的音頻格式結(jié)構(gòu)
structAudioStreamBasicDescription {
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;
};
Magic Cookies
在Core Audio的實(shí)現(xiàn)中,magic
cookie是對(duì)于壓縮音頻數(shù)據(jù)的一個(gè)不透明元數(shù)據(jù)。這個(gè)元數(shù)據(jù)給解碼器足夠的信息讓他來(lái)對(duì)音頻文件進(jìn)行解碼。你可以把magic
cookie當(dāng)作一個(gè)黑盒子,依靠Core Audio的函數(shù)來(lái)復(fù)制讀取和使用它。
下面是一個(gè)例子:
- (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);
}
}
音頻數(shù)據(jù)包
Package是一系列幀的集合。對(duì)于音頻數(shù)據(jù)來(lái)說(shuō),他是最小的有意義的單元。因此他是最好的表示一個(gè)音頻文件中某一個(gè)時(shí)間內(nèi)的數(shù)據(jù)集合。Core Audio中的同步就是通過(guò)數(shù)package來(lái)實(shí)現(xiàn)的。你可以使用package來(lái)計(jì)算音頻數(shù)據(jù)的緩存大小。
對(duì)于不同的數(shù)據(jù)類型有三種包:
[if !supportLists]1.[endif]CBR(固定比特率),例如linear PCM和IMA/ADPCM,所有的package的大小都一樣
[if !supportLists]2.[endif]VBR(浮動(dòng)比特率),例如AAC, Apple Lossless,和MP3,所有的package包含的frame的數(shù)量一樣,但是每個(gè)frame的大小可能不同
[if !supportLists]3.[endif]VFR(浮動(dòng)幀率),package有不同的數(shù)量的幀。這種類型少見(jiàn)
如果要使用VBR或者VFR你需要使用stream packet description結(jié)構(gòu)體,每一個(gè)結(jié)構(gòu)體表示一個(gè)package。如果要播放或者錄制,你需要一個(gè)這個(gè)結(jié)構(gòu)體的數(shù)字來(lái)表示其中的每一個(gè)package。
Audio File Services和Audio File
Stream會(huì)讓你使用package來(lái)進(jìn)行文件操作。AudioFileReadPackets從文件中讀取一個(gè)package到內(nèi)存中。同時(shí),他會(huì)給你一個(gè)AudioStreamPacketDescription的數(shù)組,來(lái)表示package。
對(duì)于CBR and VBR,每秒鐘讀取的package的數(shù)目是不一定的。下面的方法可以幫助你計(jì)算出音頻文件緩存的大小:
- (void) calculateSizesFor: (Float64) seconds {
UInt32 maxPacketSize;
UInt32 propertySize =sizeof (maxPacketSize);
AudioFileGetProperty (
audioFileID,
kAudioFilePropertyPacketSizeUpperBound,
&propertySize,
&maxPacketSize
);
static const intmaxBufferSize = 0x10000;// limitmaximum size to 64K
static const intminBufferSize = 0x4000;// limitminimum size to 16K
if (audioFormat.mFramesPerPacket){
Float64numPacketsForTime =
audioFormat.mSampleRate / audioFormat.mFramesPerPacket * seconds;
[selfsetBufferByteSize: numPacketsForTime * maxPacketSize];
} else {
// if frames perpacket is zero, then the codec doesn't know the
// relationshipbetween packets and time. Return a default buffer size
[selfsetBufferByteSize:
maxBufferSize >maxPacketSize ? maxBufferSize : maxPacketSize];
}
// clamp buffer size to ourspecified range
if (bufferByteSize >maxBufferSize && bufferByteSize > maxPacketSize) {
[selfsetBufferByteSize: maxBufferSize];
} else {
if (bufferByteSize< minBufferSize) {
[selfsetBufferByteSize: minBufferSize];
}
}
[self setNumPacketsToRead:self.bufferByteSize / maxPacketSize];
}
數(shù)據(jù)格式轉(zhuǎn)換
你可以使用格式轉(zhuǎn)換器來(lái)進(jìn)行數(shù)據(jù)的格式轉(zhuǎn)換。你可以進(jìn)行PCM的采樣率的轉(zhuǎn)換或者將壓縮格式和PCM直接任意轉(zhuǎn)換。一共有三種轉(zhuǎn)化:
[if !supportLists]1.[endif]將一個(gè)音頻格式解碼為linear PCM
[if !supportLists]2.[endif]將linear PCM轉(zhuǎn)換為另外一個(gè)音頻格式
[if !supportLists]3.[endif]linear PCM之間的轉(zhuǎn)化
當(dāng)你使用了AudioQueue Services,你會(huì)自動(dòng)的獲取到相應(yīng)的編碼器。
聲音文件
你可以使用Audio File Services來(lái)和app中的音頻文件交互。它對(duì)獲取音頻文件和創(chuàng)建音頻文件有很有用的抽象。
你可以使用Audio File Services來(lái)獲取系統(tǒng)參數(shù)。AudioFileGetGlobalInfoSize來(lái)讓你分配足夠的內(nèi)存空間來(lái)保存數(shù)據(jù),AudioFileGetGlobalInfo來(lái)獲取真正的數(shù)據(jù)。你可以在AudioFile.h文件中查看,你可以獲取到那些信息:
[if !supportLists]1.[endif]可讀的文件類型
[if !supportLists]2.[endif]可寫(xiě)的文件類型
[if !supportLists]3.[endif]對(duì)于每一種可寫(xiě)的類型,你可以寫(xiě)入的音頻數(shù)據(jù)的格式
另外還有其他的兩種技術(shù):
[if !supportLists]1.[endif]Audio
File Stream Services讓可以讀取硬盤(pán)或者流中的文件數(shù)據(jù)
[if !supportLists]2.[endif]Extended
Audio File Services他封裝了file和stream接口,讓你使用的更容易
創(chuàng)建一個(gè)新的聲音文件
為了創(chuàng)建一個(gè)新的聲音文件,你需要:
[if !supportLists]1.[endif]這個(gè)文件的系統(tǒng)路徑,以CFURL或者NSURL對(duì)象類型
[if !supportLists]2.[endif]你想要?jiǎng)?chuàng)建的文件的類型,例如CAF文件你需要設(shè)定為kAudioFileCAFType
[if !supportLists]3.[endif]你將要寫(xiě)入到文件中的音頻數(shù)據(jù)的audio stream basic description。在很多情況下,你可以設(shè)置一部分,然后讓系統(tǒng)幫你決定另一部分。
你將這三個(gè)參數(shù)給AudioFileCreateWithURL,它會(huì)創(chuàng)建一個(gè)文件,并返回AudioFileID對(duì)象。你可以使用它來(lái)作為后面進(jìn)一步交互的對(duì)象。
AudioFileCreateWithURL(
audioFileURL,
kAudioFileCAFType,
&audioFormat,
kAudioFileFlags_EraseFile,
&audioFileID// the function provides the new file objecthere
);
打開(kāi)一個(gè)音頻文件
你可以使用AudioFileOpenURL來(lái)打開(kāi)一個(gè)文件。你可以給他一個(gè)URL,一個(gè)文件類型的提示還有一個(gè)你想使用的文件權(quán)限,然后他就會(huì)返回一個(gè)ID。
你可以使用這個(gè)ID和AudioFileGetPropertyInfo和AudioFileGetProperty來(lái)獲取文件的各種信息。通常你需要知道的信息包括:kAudioFilePropertyFileFormat,kAudioFilePropertyDataFormat,kAudioFilePropertyMagicCookieData,kAudioFilePropertyChannelLayout。
對(duì)于一個(gè)VBR的長(zhǎng)文件,你讀取所有的package可能需要很長(zhǎng)時(shí)間,這兩個(gè)參數(shù)有幫助kAudioFilePropertyPacketSizeUpperBound和kAudioFilePropertyEstimatedDuration。他可以預(yù)估出package的大小和時(shí)長(zhǎng)。
讀寫(xiě)一個(gè)音頻文件
Ios中通常使用Audio File Services來(lái)讀和寫(xiě)文件。除非必要都使用package來(lái)讀和寫(xiě)。因?yàn)椋瑢?duì)于VBR你只能使用package來(lái)讀寫(xiě),并且使用基于package的讀寫(xiě)也更加簡(jiǎn)單。
iPhone可以支持的音頻文件格式
Format nameFormat 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
CAF文件
Ios和osx有自己的音頻文件格式叫做Core Audio Format (或者CAF)。他可以包含平臺(tái)支持的所有的音頻數(shù)據(jù)格式。
音頻流
Audio File Stream Services主要用來(lái)對(duì)音頻流進(jìn)行解析。你創(chuàng)建一個(gè)audio file stream的對(duì)象AudioFileStreamID。他作為你和音頻流相互的代理。因?yàn)閍udio file stream Services主要負(fù)責(zé)對(duì)音頻流進(jìn)行解析,那么你的app就需要解析后的各種信息和數(shù)據(jù)進(jìn)行交互和反饋。你通過(guò)兩個(gè)回調(diào)函數(shù)來(lái)對(duì)這些信息最反饋。
首先你需要有一個(gè)回調(diào)來(lái)對(duì)音頻流的屬性變化做反饋。至少你需要對(duì)屬性kAudioFileStreamProperty_ReadyToProducePackets的變化有反饋。通常使用這個(gè)屬性的場(chǎng)景如下:
[if !supportLists]1.[endif]用戶按下開(kāi)始播放按鈕,或者請(qǐng)求這個(gè)音頻流來(lái)播放
[if !supportLists]2.[endif]Audio
File Stream Services開(kāi)始解析音頻流
[if !supportLists]3.[endif]當(dāng)足夠多的package被解析并發(fā)送到你的app中,Audio File
Stream Services會(huì)將屬相kAudioFileStreamProperty_ReadyToProducePackets的值設(shè)置為1
[if !supportLists]4.[endif]Audio
File Stream Services會(huì)回調(diào)你的回調(diào)函數(shù),并且回調(diào)的屬性ID是kAudioFileStreamProperty_ReadyToProducePackets
[if !supportLists]5.[endif]你的回調(diào)開(kāi)始負(fù)責(zé)做后續(xù)的操作,例如建立一個(gè)audio queue對(duì)象來(lái)負(fù)責(zé)播放這個(gè)音頻流
其次,你需要一個(gè)回調(diào)來(lái)處理音頻數(shù)據(jù)。每當(dāng)Audio File Stream Services獲取到一系列完整的音頻package,他就會(huì)回調(diào)你的函數(shù)。你的回調(diào)需要處理收到的數(shù)據(jù)。通常,你需要立即將他們發(fā)送給Audio Queue Services來(lái)播放。
Audio Sessions:與Core Audio相互合作
audio
session是你的app和ios系統(tǒng)的中間人。每個(gè)app都只有一個(gè)audio session,你通過(guò)他來(lái)表達(dá)你的app的音頻的用處。你必須首先回答幾個(gè)關(guān)于你的app如何表現(xiàn)的問(wèn)題:
[if !supportLists]1.[endif]你的app如何對(duì)打斷作反應(yīng),例如一個(gè)電話?
[if !supportLists]2.[endif]你想要你的app的聲音和其他聲音混合播放嗎,或者你想要讓別的app靜音?
[if !supportLists]3.[endif]你的app對(duì)音頻通道的變化如何反應(yīng),當(dāng)用戶插入或者拔出他的二級(jí)
當(dāng)你想好了答案,你可以使用audio session的接口來(lái)設(shè)置你的接口和應(yīng)用。下面提供了三種特性,你可以通過(guò)變成來(lái)設(shè)置他們.
Audio session featureDescription
CategoriesA category is a key that? identifies a set of audio behaviors for your application. By setting a? category, you indicate your audio intentions to iOS, such as whether your? audio should continue when the screen locks.
Interruptions and route? changesYour audio session posts? notifications when your audio is interrupted, when an interruption ends, and? when the hardware audio route changes. These notifications let you respond to? changes in the larger audio environment—such as an interruption due to in an? incoming phone call—gracefully.
Hardware characteristicsYou can query the audio session? to discover characteristics of the device your application is running on,? such as hardware sample rate, number of hardware channels, and whether audio? input is available.
Audio Sessiond的默認(rèn)行為
[if !supportLists]1.[endif]當(dāng)用戶通過(guò)靜音按鈕來(lái)靜音,你的聲音將會(huì)被靜音
[if !supportLists]2.[endif]當(dāng)設(shè)備進(jìn)入鎖屏或者待機(jī)狀態(tài),你的聲音將被靜音。
[if !supportLists]3.[endif]當(dāng)你的聲音開(kāi)始,其他的聲音將會(huì)被停止
這些行為是由它的默認(rèn)類別kAudioSessionCategory_SoloAmbientSound所決定的。Ios提供了很多類別來(lái)滿足你的需求,你可以在應(yīng)用啟動(dòng)或者在你的app運(yùn)行的時(shí)候來(lái)設(shè)置
Audio session的默認(rèn)行為對(duì)于ios的音頻開(kāi)發(fā)足夠了。除非你有自己的特殊需求,下面會(huì)討論。
打斷:激活和非激活
一個(gè)audio session的默認(rèn)行為中缺少他在打斷結(jié)束后重新激活自己的能力。audio session主要有兩個(gè)狀態(tài):激活和非激活。你只有在激活狀態(tài)下,才能對(duì)你的音頻進(jìn)行操作。
在啟動(dòng)時(shí),你的默認(rèn)的audio
session就被激活。但是,如果電話來(lái)了,你的audio session會(huì)成為非激活狀態(tài),并且聲音被打斷。這叫做打斷,如果用戶選擇忽略這個(gè)電話,你的應(yīng)用會(huì)繼續(xù)運(yùn)行,但是你的audio session已經(jīng)處于非激活狀態(tài),他不能繼續(xù)播放了。
如果你的app中使用OpenAL,
I/O audio unit,或者Audio Queue Services,你必須寫(xiě)一個(gè)打斷的回調(diào)函數(shù),并且在打斷結(jié)束的時(shí)候,顯式的重新激活你的audio session。
如果你使用AVAudioPlayer類,他自己會(huì)幫你處理好。
判斷你的音頻輸入是否有效
在你錄音前,你必須首先知道你的ios音頻錄入設(shè)備是否有效,因?yàn)樵趇pod
touch(2代)中,他只有當(dāng)其他的輸入設(shè)備接入的時(shí)候,才能進(jìn)行語(yǔ)音的輸入。你可以使用kAudioSessionProperty_AudioInputAvailable屬性來(lái)判斷。下面是一個(gè)例子:
UInt32 audioInputIsAvailable;
UInt32 propertySize = sizeof (audioInputIsAvailable);
AudioSessionGetProperty (
kAudioSessionProperty_AudioInputAvailable,
&propertySize,
&audioInputIsAvailable // A nonzero value on output means that
// audio input isavailable
);
使用你的Audio Session
在同一時(shí)刻,你的app只能有一個(gè)audio
session的類別。你的所有的音頻特性都遵守這個(gè)類別的特性。(除了System Sound Services,她總是使用最低優(yōu)先級(jí)的audio
session列別)。
你必須在真機(jī)上測(cè)試你的audio session的行為。
使用AVAudioPlayer類播放聲音
AVAudioPlayer提供了一個(gè)簡(jiǎn)單的oc的類來(lái)播放音頻。如果你的app不需要立體聲或者準(zhǔn)確的同步,并且不是從網(wǎng)絡(luò)上獲取音頻來(lái)播放。蘋(píng)果推薦你使用這個(gè)類來(lái)播放音樂(lè)。
使用它來(lái)播放音樂(lè)你可以:
[if !supportLists]1.[endif]播放任何時(shí)長(zhǎng)的聲音
[if !supportLists]2.[endif]從文件或者內(nèi)存中播放聲音
[if !supportLists]3.[endif]循環(huán)播放
[if !supportLists]4.[endif]同時(shí)播放多個(gè)聲音
[if !supportLists]5.[endif]控制你播放的每個(gè)聲音的音量
[if !supportLists]6.[endif]獲取文件的某一個(gè)特定的點(diǎn),這個(gè)可以支持快進(jìn)或者快退
[if !supportLists]7.[endif]獲取聲音的音量數(shù)據(jù)
AVAudioPlayer不需要你使用Audio Session Services,但是他的默認(rèn)行為不符合你的要求,你可以使用默認(rèn)的audio session。
為了使用AVAudioPlayer,你首先給他一個(gè)音頻文件,準(zhǔn)備播放,并且設(shè)置一個(gè)代理,例子如下:
NSString*soundFilePath =
[[NSBundle mainBundle]pathForResource: @"sound"
ofType: @"wav"];
NSURL *fileURL =[[NSURL alloc] initFileURLWithPath: soundFilePath];
AVAudioPlayer*newPlayer =
[[AVAudioPlayer alloc]initWithContentsOfURL: fileURL
error: nil];
[fileURLrelease];
self.player =newPlayer;
[newPlayerrelease];
[self.playerprepareToPlay];
[self.playersetDelegate: self];
你使用一個(gè)代理來(lái)處理打斷和更新用戶界面當(dāng)聲音播放結(jié)束。下面實(shí)現(xiàn)了一個(gè)簡(jiǎn)單代理方法的實(shí)現(xiàn),這個(gè)嗲嗎更新了開(kāi)始和暫停按鈕的標(biāo)題,當(dāng)聲音完成播放。
- (void) audioPlayerDidFinishPlaying:(AVAudioPlayer *) player
successfully: (BOOL)flag {
if(flag == YES) {
[self.button setTitle: @"Play" forState:UIControlStateNormal];
}
}
為了播放、暫停或者停止一個(gè)AVAudioPlayer對(duì)象,調(diào)用他的播放控制方法。你可以使用playing屬性來(lái)獲取聲音是否在播放。下面是簡(jiǎn)單的一個(gè)實(shí)現(xiàn)了點(diǎn)擊播放暫停按鈕來(lái)更新按鈕的標(biāo)題:
- (IBAction) playOrPause: (id) sender {
// ifalready playing, then pause
if(self.player.playing) {
[self.button setTitle: @"Play" forState:UIControlStateHighlighted];
[self.button setTitle: @"Play" forState:UIControlStateNormal];
[self.player pause];
// ifstopped or paused, start playing
} else{
[self.button setTitle: @"Pause" forState: UIControlStateHighlighted];
[self.button setTitle: @"Pause" forState:UIControlStateNormal];
[self.player play];
}
}
AVAudioPlayer使用oc的方式聲明一個(gè)屬性并且管理聲音。你可以使用下面的方法來(lái)設(shè)置音頻的聲音:
[self.player setVolume: 1.0];// available range is 0.0 through 1.0
使用Audio Queue Services來(lái)錄制或者播放音頻
AudioQueue Services使用了一個(gè)很直接的并且足夠低層的方式來(lái)錄制或者播放聲音。他可以使你的app使用硬件來(lái)錄制或者播放聲音(例如麥克風(fēng)或者音像)而不用知道硬件的細(xì)節(jié)。他可以讓你使用復(fù)雜的編碼而不用知道編碼是如何工作的。
盡管他是一個(gè)高層的接口,Audio
Queue Services支持一些高級(jí)特性。他提供細(xì)粒度的時(shí)間控制來(lái)支持定時(shí)播放和同步。你可以使用它來(lái)同步多個(gè)audio queue的播放來(lái)使這些聲音同步播放,來(lái)單獨(dú)控制多個(gè)聲音的聲音大小,并且來(lái)重復(fù)播放。Audio Queue Services和AVAudioPlayer是唯一的兩種方式來(lái)播放壓縮的音頻。
通常你使用Audio Queue Services時(shí),需要和Audio File Services或者Audio File Stream Services相配合。
Audio Queue錄音和播放的回調(diào)函數(shù)
就和Audio File Stream Services一樣,你需要使用回調(diào)和屬性來(lái)和audio queue對(duì)象交互。錄音時(shí),你實(shí)現(xiàn)一個(gè)回調(diào)來(lái)接手音頻數(shù)據(jù)緩存并且寫(xiě)到硬盤(pán)中。你的audio queue會(huì)在每次新的緩存被寫(xiě)滿的時(shí)候會(huì)調(diào)用你的回調(diào)。下面是一個(gè)簡(jiǎn)單的示例:
對(duì)于播放,你的回調(diào)處于一個(gè)相反的過(guò)程。當(dāng)你的audio queue需要有一個(gè)新的內(nèi)存數(shù)據(jù)來(lái)播放的時(shí)候,會(huì)回調(diào)你。你的回調(diào)讀取一定數(shù)量的音頻package,并且將他們交給audio queue的緩存中。然后audioqueue開(kāi)始播放,如下圖示例:
創(chuàng)建一個(gè)Audio Queue對(duì)象
為了使用Audio Queue Services,你需要首先創(chuàng)建一個(gè)Audio Queue Services。對(duì)于播放和錄音,創(chuàng)建的方式不同:
[if !supportLists]1.[endif]使用AudioQueueNewInput來(lái)創(chuàng)建錄音Audio Queue
[if !supportLists]2.[endif]使用AudioQueueNewOutput來(lái)創(chuàng)建播放Audio Queue
為了創(chuàng)建一個(gè)播放的Audio Queue,你需要實(shí)現(xiàn)以下三步:
[if !supportLists]1.[endif]創(chuàng)建一個(gè)結(jié)構(gòu)體來(lái)管理Audio Queue需要的信息,例如你所播放的數(shù)據(jù)的音頻格式
[if !supportLists]2.[endif]定義一個(gè)回調(diào)來(lái)處理Audio Queue的緩存。回調(diào)使用Audio File Services來(lái)讀取文件播放
[if !supportLists]3.[endif]使用AudioQueueNewOutput來(lái)來(lái)初始化AudioQueue
示例如下:
static const int kNumberBuffers = 3;
// Create a data structure to manage informationneeded by the audio queue
struct myAQStruct {
AudioFileIDmAudioFile;
CAStreamBasicDescriptionmDataFormat;
AudioQueueRefmQueue;
AudioQueueBufferRefmBuffers[kNumberBuffers];
SInt64mCurrentPacket;
UInt32mNumPacketsToRead;
AudioStreamPacketDescription*mPacketDescs;
boolmDone;
};
// Define a playback audio queue callbackfunction
static void AQTestBufferCallback(
void*inUserData,
AudioQueueRefinAQ,
AudioQueueBufferRefinCompleteAQBuffer
) {
myAQStruct*myInfo = (myAQStruct *)inUserData;
if(myInfo->mDone) return;
UInt32numBytes;
UInt32nPackets = 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的播放音量
有兩種方式,一種是使用AudioQueueSetParameter直接設(shè)置kAudioQueueParam_Volume的參數(shù)。音量會(huì)立即生效:
Float32 volume = 1;
AudioQueueSetParameter (
myAQstruct.audioQueueObject,
kAudioQueueParam_Volume,
volume
);
你也可以使用AudioQueueEnqueueBufferWithParameters來(lái)控制音量。這個(gè)繪制在你將這個(gè)緩存插入到Audio
Queue中的時(shí)候生鮮。在這兩種情況下,直到你更改音量之前,audio
queue都會(huì)保持你這次設(shè)置的音量。
獲取Audio Queue播放音量
你可以使用kAudioQueueProperty_CurrentLevelMeterDB來(lái)獲取當(dāng)前播放的音量。這個(gè)返回是一個(gè)包含著AudioQueueLevelMeterState結(jié)構(gòu)體的數(shù)組,每一個(gè)代表一個(gè)聲道。如下圖所示:
typedef struct AudioQueueLevelMeterState {
Float32mAveragePower;
Float32mPeakPower;
};AudioQueueLevelMeterState
同時(shí)播放多個(gè)音樂(lè)
為了同時(shí)播放多個(gè)因?yàn)椋瑸槊恳粋€(gè)音樂(lè)創(chuàng)建一個(gè)audio queue。對(duì)于每一個(gè)audio queue,讓他們的音頻的第一個(gè)緩存同時(shí)播放,使用AudioQueueEnqueueBufferWithParameters。
音頻的格式對(duì)于同時(shí)播放是很重要的。有些壓縮的音頻格式十分的消耗硬件編碼。下面的格式同時(shí)只能播放一個(gè)實(shí)例:
[if !supportLists]·[endif]AAC
[if !supportLists]·[endif]ALAC (Apple Lossless)
[if !supportLists]·[endif]MP3
為了同時(shí)播放高音質(zhì)的音樂(lè),使用PCM or IMA4格式。
1.Linear PCM和IMA/ADPCM (IMA4)音頻。你可以同時(shí)播放多個(gè)PCM或者IMA4格式的音樂(lè)而不會(huì)產(chǎn)生CPU資源問(wèn)題.
2.AAC, MP3,和Apple Lossless
(ALAC)音頻播放AAC, MP3,和Apple Lossless (ALAC)會(huì)很消耗資源,你同時(shí)只能播放一個(gè)。
使用OpenAL來(lái)播放
OpenAL是最適合用來(lái)在游戲中播放音樂(lè)。
系統(tǒng)聲音:提醒和聲音效果
如果你播放的聲音不需要控制音量,位置,audio session或者其他的控制,使用System Sound Services。這個(gè)接口最適合播放一個(gè)短的簡(jiǎn)單的方式來(lái)播放聲音。在ios中,System Sound Services最多只能播放30秒鐘
可以使用AudioServicesPlaySystemSound來(lái)立即播放系統(tǒng)聲音。AudioServicesPlayAlertSound可以使用它來(lái)提醒用戶。每一個(gè)方法都會(huì)在用戶靜音的時(shí)候產(chǎn)生振動(dòng)。
你可以使用AudioServicesPlaySystemSound并且傳入kSystemSoundID_Vibrate參數(shù)來(lái)直接來(lái)觸發(fā)振動(dòng)。
使用AudioServicesPlaySystemSound來(lái)播放聲音,你首先通過(guò)創(chuàng)建一個(gè)sound ID來(lái)注冊(cè)這個(gè)聲音效果文件。然后你可以播放這個(gè)聲音。通常,會(huì)播放只播放一次或者重復(fù)的播放的情景,你需要持有這個(gè)sound ID直到你的app不再使用他。如果你只是使用一次,你可以在它播放完之后,立即釋放他。
下面是一個(gè)實(shí)例:
#include
#include
// Define a callback to be called when the soundis finished
// playing. Useful when you need to free memoryafter playing.
static void MyCompletionCallback (
SystemSoundIDmySSID,
void * myURLRef
) {
AudioServicesDisposeSystemSoundID (mySSID);
CFRelease (myURLRef);
CFRunLoopStop (CFRunLoopGetCurrent());
}
int main (int argc, const char * argv[]) {
// Setup the pieces needed to play a sound.
SystemSoundIDmySSID;
CFURLRefmyURLRef;
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
);
// Playthe 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 ();
return0;
}
Core Audio插件: Audio Units和Codecs
Core Audio有插件的機(jī)制來(lái)處理音頻數(shù)據(jù)。在ios中,系統(tǒng)提供這些插件。Osx中,有系統(tǒng)內(nèi)置的,你也可以創(chuàng)建你自己的。
Audio Units
在ios中,audio
units給app提供一個(gè)低延時(shí)的輸入輸出方案。他同樣提供一些DSP的特性。
iOSaudio units的輸入輸出使用8.24-bit固定小數(shù)的linear PCM音頻格式。Ios的audio units是:
[if !supportLists]1.[endif]3D混音
[if !supportLists]2.[endif]多通道混音器
[if !supportLists]3.[endif]轉(zhuǎn)換器
[if !supportLists]4.[endif]I/O器
[if !supportLists]5.[endif]iPod均衡器
?xV??h?