關(guān)于AudioUnit
Audio Unit 是iOS系統(tǒng)音頻架構(gòu)的最底層了,這一層架構(gòu)是最接近硬件層的妨猩,也是開發(fā)者目前能操作最的層的API架構(gòu)了。
這里先解釋一下DSP(digital signal processing)數(shù)字信號處理秽褒,音頻信號是需要通過設(shè)備采樣之后變成的數(shù)字信號壶硅,以方便數(shù)據(jù)的傳輸和記錄。目前最常用的是PCM格式的音頻數(shù)據(jù)信號销斟,因為這種高保真的信號方便后續(xù)的處理庐椒,還有就是它保留了數(shù)據(jù)的完整性。
關(guān)于音頻流參數(shù)
1蚂踊、采樣率
每秒鐘采得聲音樣本的次數(shù)约谈,聲音是一種能量波,有振幅和頻率犁钟,人的耳朵可以聽到的頻率在20-Hz~20kHz之間的聲波棱诱,所以采樣率越高,獲取的到的頻率信息就更為豐富涝动,由于人耳的分辨率很有限迈勋,太高的頻率并不能分辨出來。22050 的采樣頻率是常用的醋粟,44100已是CD音質(zhì)靡菇,超過48000或96000的采樣對人耳已經(jīng)沒有意義重归。
常用的采樣率:
8000 Hz - 電話所用采樣率
22050 Hz - 無線電廣播所用采樣率
32000 Hz - miniDV 數(shù)碼視頻 camcorder、DAT (LP mode)所用采樣率
44100 Hz - 音頻 CD, 也常用于 MPEG-1 音頻(VCD厦凤,SVCD鼻吮,MP3)所用采樣率
47250 Hz - 商用 PCM 錄音機所用采樣率
48000 Hz - miniDV、數(shù)字電視较鼓、DVD狈网、DAT、電影和專業(yè)音頻所用的數(shù)字聲音所用采樣率
50000 Hz - 商用數(shù)字錄音機所用采樣率
96000 Hz或者 192000 Hz - DVD-Audio笨腥、一些 LPCM DVD 音軌拓哺、BD-ROM(藍光盤)音軌、和 HD-DVD (高清晰度 DVD)音軌所用所用采樣率
2脖母、采樣位數(shù)
采樣位數(shù)士鸥,他是衡量聲音播到變化的一個參數(shù),它的數(shù)值越大谆级,分辨率就越高烤礁,錄制和回放的聲音就越接近真實。常見的聲卡主要有8位和16位兩種肥照,如今市面上所有的主流產(chǎn)品都是16位及以上的聲卡脚仔。
每個采樣數(shù)據(jù)記錄的是振幅, 采樣精度取決于采樣位數(shù)的大小:
1 字節(jié)(也就是8bit) 只能記錄 256 個數(shù), 也就是只能將振幅劃分成 256 個等級;
2 字節(jié)(也就是16bit) 可以細到 65536 個數(shù), 這已是 CD 標(biāo)準(zhǔn)了;
4 字節(jié)(也就是32bit) 能把振幅細分到 4294967296 個等級, 實在是沒必要了.
3、通道數(shù)
即聲音的通道的數(shù)目舆绎,目前使用較多的是單聲道和立體聲鲤脏,相當(dāng)于從多位置采集聲音。
4吕朵、比特率
每秒的傳輸速率(位速, 也叫比特率)猎醇。如705.6kbps 或 705600bps, 其中的 b 是 bit, ps 是每秒的意思,表示每秒705600bit的容量努溃。不同的音頻格式編碼硫嘶,對PCM都有一個壓縮比,所以比特率一般等于原始比特率/音頻壓縮比梧税。
5沦疾、幀長
幀長記錄了一個聲音單元字節(jié)為單位,其長度為:樣本長度 * 通道數(shù) = 幀長
6第队、幀數(shù)
每秒數(shù)據(jù)分為都少幀: 幀長 * 幀數(shù) * 8 = 比特率
Audio Unit 工作時的腦圖和流程圖
整體的一個流程圖如下:
使用流程
1哮塞、iOS 涉及音頻使用和會話都需要使用到 AudioSessionInitialize去初始化音頻會話對象。
// set audio session
NSError *error = nil;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
[audioSession setActive:YES error:&error];
2斥铺、配置音頻組件Audio Unit 并描述輸出的單元
//set audio component information
AudioComponentDescription audioDesc;
audioDesc.componentType = kAudioUnitType_Output;
audioDesc.componentSubType = kAudioUnitSubType_RemoteIO;
audioDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
audioDesc.componentFlags = 0;
audioDesc.componentFlagsMask = 0;
3彻桃、查找、創(chuàng)建對應(yīng)的音頻輸出單元組件
AudioUnit audioUnit;
//set audio component information
AudioComponentDescription audioDesc;
audioDesc.componentType = kAudioUnitType_Output;
audioDesc.componentSubType = kAudioUnitSubType_RemoteIO;
audioDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
audioDesc.componentFlags = 0;
audioDesc.componentFlagsMask = 0;
//Finds the next component that matches a specified AudioComponentDescription structure after a specified audio component.
AudioComponent inputComponent = AudioComponentFindNext(NULL, &audioDesc);
//create a new instance of an audio component
AudioComponentInstanceNew(inputComponent, &audioUnit);
//audio property
UInt32 flag = 1;
if (flag) {
status = AudioUnitSetProperty(audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
OUTPUT_BUS,
&flag,
sizeof(flag));
}
4晾蜘、配置對應(yīng)需要播放的音頻數(shù)據(jù)格式內(nèi)容
// format
AudioStreamBasicDescription outputFormat;
memset(&outputFormat, 0, sizeof(outputFormat));
outputFormat.mSampleRate = 44100; // 采樣率
outputFormat.mFormatID = kAudioFormatLinearPCM; // PCM格式
outputFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; // 整形
outputFormat.mFramesPerPacket = 1; // 每幀只有1個packet
outputFormat.mChannelsPerFrame = 1; // 聲道數(shù)
outputFormat.mBytesPerFrame = 2; // 每幀只有2個byte 聲道*位深*Packet數(shù)
outputFormat.mBytesPerPacket = 2; // 每個Packet只有2個byte
outputFormat.mBitsPerChannel = 16; // 位深
[self printAudioStreamBasicDescription:outputFormat];
status = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
OUTPUT_BUS,
&outputFormat,
sizeof(outputFormat));
if (status) {
NSLog(@"AudioUnitSetProperty eror with status:%d", status);
}
5邻眷、指定播放源的相關(guān)信息
// callback
AURenderCallbackStruct playCallback;
playCallback.inputProc = PlayCallback;
playCallback.inputProcRefCon = (__bridge void *)self;
AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
OUTPUT_BUS,
&playCallback,
sizeof(playCallback));
OSStatus result = AudioUnitInitialize(audioUnit);
6眠屎、給目標(biāo)播放組件輸入播放內(nèi)容
static OSStatus PlayCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData) {
yourPlayerClass *player = (__bridge yourPlayerClass *)inRefCon;//這里獲取之前初始化時配置給播放組件的類對象
// 這里是靜音數(shù)據(jù),需要播放更對內(nèi)容可以往ioData->mBuffers輸入數(shù)據(jù)
for (int iBuffer = 0; iBuffer < ioData->mNumberBuffers; ++iBuffer) {
memset(ioData->mBuffers[iBuffer].mData, 0, ioData->mBuffers[iBuffer].mDataByteSize);
}
return noErr;
}
}
7肆饶、這里想要補充說明一下
上面的配置和初始中改衩,多次用到了AudioUnitSetProperty,這是一個設(shè)置音頻單元屬性的函數(shù)驯镊,它的幾個參數(shù)如下:
上面用到了兩個宏定義 作用于設(shè)置AudioUnit的I/O口設(shè)置
#define INPUT_BUS 1
#define OUTPUT_BUS 0