音視頻開(kāi)發(fā)之旅——音頻基礎(chǔ)概念西疤、交叉編譯原理和實(shí)踐(LAME的交叉編譯)(iOS)

本文主要講解的是音頻基礎(chǔ)概念烦粒、交叉編譯原理和實(shí)踐(LAME的交叉編譯),是基于iOS平臺(tái)代赁,示例代碼如下所示:

iOSAudioDemo

另外扰她,Android平臺(tái)也有相關(guān)的文章,如下所示:

音視頻開(kāi)發(fā)之旅——音頻基礎(chǔ)概念芭碍、交叉編譯原理和實(shí)踐(LAME的交叉編譯)(Android)

音頻基礎(chǔ)概念

在進(jìn)行音頻開(kāi)發(fā)的之前徒役,了解聲學(xué)的基礎(chǔ)還是很有必要的。

聲音的物理性質(zhì)

在初中物理的時(shí)候?qū)W過(guò)窖壕,聲音是由三要素組成:音調(diào)廉涕、響度音色

音調(diào)

聲音的高低叫做音調(diào)艇拍。物體振動(dòng)得越快,發(fā)出聲音的音調(diào)就越高宠纯;物體振動(dòng)得越慢卸夕,發(fā)出的音調(diào)越低。頻率過(guò)零率婆瓜,指信號(hào)的符號(hào)變化的比率)決定了音調(diào)快集,頻率越高,波長(zhǎng)越短廉白,聲音更容易繞過(guò)障礙物个初,也就是能量衰減越小,反之得到相反的結(jié)論猴蹂。

響度

聲音的強(qiáng)弱叫做響度院溺。我們可以一般用分貝(dB)來(lái)描述響度,分貝越大磅轻,聲音響度越大珍逸,反之得到相反的結(jié)論。

音色

聲音的品質(zhì)叫做音色聋溜,它反映了每個(gè)物體發(fā)出的聲音特有的品質(zhì)谆膳。例如在同樣的音調(diào)和響度下,吉他和鋼琴的聲音聽(tīng)起來(lái)是不同的撮躁,也就是音色是不同的漱病。波的形狀決定聲音的音色,吉他和鋼琴音色不同就是因?yàn)樗鼈兘橘|(zhì)產(chǎn)生的波形不同。

業(yè)界來(lái)說(shuō)杨帽,人耳能夠聽(tīng)到頻率范圍大約為20Hz~20kHz漓穿,對(duì)3kHz~4kHz頻率范圍內(nèi)的聲音比較敏感,對(duì)于較低或者較高頻率的聲音睦尽,人耳的敏感度會(huì)減弱器净;在分貝較低時(shí),聽(tīng)覺(jué)的頻率特性會(huì)很不均勻当凡,反之就會(huì)較為均勻山害。一個(gè)頻率范圍較寬的音樂(lè),最佳的分貝范圍為80dB~90dB沿量,超過(guò)90dB就會(huì)損害人耳浪慌,105dB是人耳的極限。

聲音在不同的介質(zhì)傳播的速度也會(huì)不一樣朴则,在空氣中的傳播速度為340m/s权纤,不過(guò)在真空是無(wú)法傳播的。

有時(shí)候我們?cè)诳諘绲牡胤交蛘吒呱酱蠛暗臅r(shí)候乌妒,會(huì)聽(tīng)到回聲(echo)汹想,產(chǎn)生回聲的原因是聲音在傳播的過(guò)程中遇到障礙物后反彈回來(lái)后再次讓我們聽(tīng)到,但是如果這兩種聲音傳回到我們耳朵的時(shí)差小于80毫秒的話撤蚊,我們就無(wú)法分辨這兩種聲音古掏。

音頻數(shù)字化

將聲音模擬信號(hào)轉(zhuǎn)換為數(shù)字信號(hào)的過(guò)程稱之為音頻數(shù)字化,這里需要經(jīng)過(guò)三個(gè)步驟:采樣侦啸、量化編碼槽唾。

采樣

首先對(duì)模擬信號(hào)進(jìn)行采樣,采樣是指在時(shí)間軸(橫軸)對(duì)信號(hào)進(jìn)行數(shù)字化光涂,根據(jù)奎斯特定理(采樣定理庞萍,我們要按比聲音最高音頻高兩倍以上的頻率對(duì)聲音進(jìn)行采樣,這個(gè)過(guò)程也稱為AD轉(zhuǎn)換忘闻。上面提過(guò)的人耳能夠聽(tīng)到的頻率為20Hz~20kHz钝计,所以一般采樣頻率為44.1kHz,也就是說(shuō)1秒會(huì)采樣44100次齐佳。

量化

上面提到的葵蒂,具體每個(gè)采樣需要怎樣處理呢?這就需要量化重虑,量化是指在幅度軸(縱軸)上對(duì)信號(hào)進(jìn)行數(shù)字化践付,要注意的是,和上面提到的采樣形成平面直角坐標(biāo)系缺厉,舉個(gè)例子:用16bit的二進(jìn)制信號(hào)表示這個(gè)聲音的一個(gè)采樣永高,16bit等于一個(gè)short隧土,表示范圍為[-32768, 32767],也就是說(shuō)有65536個(gè)可能取值命爬,所以在幅度上分為65536層曹傀。

編碼

最后一步就是要將采樣的數(shù)據(jù)進(jìn)行存儲(chǔ),也就是是需要進(jìn)行編碼饲宛,編碼就是按照一定的格式記錄采樣和量化后的數(shù)據(jù)數(shù)據(jù)皆愉,例如:順序存儲(chǔ)壓縮存儲(chǔ)等等。常用的格式為音頻的裸數(shù)據(jù)格式艇抠,也就是脈沖編碼調(diào)制(Pulse Code Modulation幕庐,簡(jiǎn)稱PCM)。描述一段PCM的數(shù)據(jù)需要這幾個(gè)概念:采樣率(sampleRate)家淤、量化格式(sampleFormat异剥,也稱為位深度)聲道數(shù)(channel)比特率用于衡量音頻數(shù)據(jù)單位時(shí)間內(nèi)的容量大小絮重,也就是一秒時(shí)間內(nèi)的比特?cái)?shù)目冤寿,我們以常見(jiàn)的CD格式和DVD-Audio格式為例子:

CD格式的采樣率為44100Hz,量化格式為16bit(2byte)青伤,聲道數(shù)為2督怜,那么它的比特率為:

44100 * 16 * 2 = 1411200bps

轉(zhuǎn)換可得1411200bps / 1024 = 1378.125Kibps

DVD-Audio格式的采樣率為96000Hz,量化格式為24bit(3byte)狠角,聲道數(shù)為6.那么它的比特率為:

96000 * 24 * 6 = 13824000bps

轉(zhuǎn)換可得13824000bps / 1024 = 13500Kibps亮蛔,再轉(zhuǎn)換可得13500Kibps / 1024 ≈ 13.18Mibps

一般來(lái)說(shuō)一首歌曲的時(shí)間大概在4分鐘左右,那我們算下CD格式和DVD-Audio格式會(huì)占用多大的存儲(chǔ)空間擎厢,如下所示:

CD格式:1411200bps * 4 * 60 = 338688000b,轉(zhuǎn)換可得338688000b / 8 / 1024 / 1024 ≈ 40.37MiB

DVD-Audio格式:13824000bps * 4 * 60 = 3317760000b辣吃,轉(zhuǎn)換可得3317760000b / 8 / 1024 / 1024 = 395.51MiB

由數(shù)據(jù)可得动遭,DVD-Audio格式一秒時(shí)間內(nèi)的比特?cái)?shù)目大于CD格式,因此它的音質(zhì)會(huì)更好神得,當(dāng)然所占的儲(chǔ)存空間也會(huì)相應(yīng)得大厘惦。

壓縮編碼

由上面可以看到一首歌如果僅僅是已CD格式去存儲(chǔ)的已經(jīng)占用了40.37MiB,如果只是存儲(chǔ)在存儲(chǔ)設(shè)備上(例如:硬盤或者光盤)那還可以接受哩簿,但是如果在網(wǎng)絡(luò)上實(shí)時(shí)在線傳輸?shù)脑捪叮@樣的大小實(shí)在是太大了,所以我們需要對(duì)其進(jìn)行壓縮編碼节榜,壓縮編碼里有個(gè)指標(biāo)叫做壓縮比羡玛,壓縮比是小于1,壓縮比越凶诓浴(越接近0)稼稿,丟失的信息就越多薄榛,反之得出相反的結(jié)論。壓縮算法有兩種:無(wú)損壓縮有損壓縮让歼。無(wú)損壓縮是指解壓后的數(shù)據(jù)能夠復(fù)原敞恋;有損壓縮是指解壓后的數(shù)據(jù)不能夠復(fù)原,壓縮導(dǎo)致的丟失得越多谋右,還原的失真就越大硬猫。

有如下常用的壓縮編碼格式:

WAV編碼

WAV(Waveform Audio File Format)是微軟專門為Windows開(kāi)發(fā)的一種編碼格式,它會(huì)在PCM數(shù)據(jù)格式的前面加上44字節(jié)改执,分別用來(lái)描述該P(yáng)CM數(shù)據(jù)的采樣率啸蜜、聲道數(shù)、量化格式天梧。

優(yōu)點(diǎn):音質(zhì)非常好盔性,有大量軟件支持。

缺點(diǎn):占用的存儲(chǔ)空間較大呢岗。

適用場(chǎng)合:多媒體開(kāi)發(fā)的中間文件冕香、音樂(lè)和音效素材。

MP3編碼

MP3(MPEG-1或者M(jìn)PEG-2 Audio Layer III)是一種有損壓縮的編碼格式后豫,它通過(guò)舍棄PCM數(shù)據(jù)人類聽(tīng)覺(jué)不重要的部分悉尾,已達(dá)到壓縮成較小文件的目的,對(duì)于大多數(shù)用戶來(lái)說(shuō)挫酿,它的音質(zhì)和不壓縮的音頻沒(méi)有明顯的下降构眯。我們常用LAME編碼MP3文件,下面會(huì)講解到早龟。

優(yōu)點(diǎn):音質(zhì)在高碼率(≥128Kbit/s)表現(xiàn)不錯(cuò)惫霸,同時(shí)壓縮比也比較高;有大量硬件和軟件支持葱弟,兼容性不錯(cuò)壹店。

適用場(chǎng)合:高碼率(≥128Kbit/s)的音頻。并且需要比較好的兼容性芝加。

AAC編碼

AAC(Advanced Audio Coding硅卢,高級(jí)音頻編碼)是一種高壓縮比的編碼格式,由于采用多聲道和使用低復(fù)雜性的描述方式藏杖,使其比幾乎所有的傳統(tǒng)編碼方式在同規(guī)格的情況下更勝一籌将塑。目前衍生出LC-AACHE-AAC v1蝌麸、HE-AAC v2三種主要的編碼格式点寥。LC-AAC是比較傳統(tǒng)的AAC,主要編碼中高碼率(≥80Kbit/s)的音頻来吩;HE-AAC v1是高效AAC开财,是對(duì)AAC的擴(kuò)展汉柒,它使用頻段復(fù)制(SBR)提高頻域的壓縮效率,適用于中低碼率(≤80Kbit/s)责鳍;HE-AAC v2結(jié)合使用了頻段復(fù)制(SBR)參數(shù)立體聲(PS)提高立體聲信號(hào)的壓縮效率碾褂,進(jìn)一步降低了對(duì)碼率的需要(接近于50%),主要編碼低碼率(≤48Kbit/s)的音質(zhì)历葛。大部分編碼器都設(shè)置為≤48Kbit/s自動(dòng)啟用PS正塌,>48Kbit/s就關(guān)閉PS,箱單與HE-AAC v1恤溶。

優(yōu)點(diǎn):音質(zhì)在中低碼率(<128Kbit/s)表現(xiàn)優(yōu)異乓诽,多用于視頻中音頻軌的編碼。

適用場(chǎng)合:中低碼率(<128Kbit/s)的音頻咒程,多用于視頻中音頻軌的編碼鸠天。

Ogg編碼

Ogg在各種碼率下都有優(yōu)秀的表現(xiàn),尤其在中低碼率的場(chǎng)景表現(xiàn)不錯(cuò)帐姻,同時(shí)它不收到軟件專利的限制稠集,完全免費(fèi)。Ogg有著非常出的的算法饥瓷,可以用更小的碼率編碼出更好的音質(zhì)剥纷,舉個(gè)例子:128Kbit/s的Ogg音質(zhì)甚至比192Kbit甚至更高的MP3還要好。

優(yōu)點(diǎn):可以用更小的碼率編碼出更好的音質(zhì)呢铆,在各種碼率下都變現(xiàn)優(yōu)異晦鞋。

缺點(diǎn):目前兼容性不夠好,流媒體特性不支持棺克。

適用場(chǎng)合:語(yǔ)音聊天的音頻消息悠垛。

iOS平臺(tái)增加C和C++支持

相比于Android,iOS對(duì)C和C++支持簡(jiǎn)單很多娜谊。如果我們使用Objective-C開(kāi)發(fā)iOS确买,由于Objective-C語(yǔ)法支持混編,所以我們只需要把引用C++的Objective-C類的后綴名改為.mm(Objective-C類的正常后綴名是.m)因俐,就可以和C++一起編譯。下面介紹一些基礎(chǔ)概念:

GNU周偎、GCC抹剩、gcc、g++

  • GNU:它是一個(gè)完全自由的操作系統(tǒng)蓉坎,起源于GNU計(jì)劃澳眷。

  • GCC:GNU Compiler Collection(GNU編譯器套件)的縮寫,它是一組GNU操作系統(tǒng)中的編譯器集合蛉艾,可以用于編譯C钳踊、C++衷敌、Java、Go等語(yǔ)言拓瞪。

  • gcc:GCC中的GNU C Compiler(C編譯器)缴罗。

  • g++:GCC中的GNU C++ Compiler(C++編譯器)。

對(duì)于.c文件和.cpp文件祭埂,gcc會(huì)分別當(dāng)作c文件和cpp文件編譯面氓,而g++會(huì)統(tǒng)一當(dāng)作cpp文件編譯。

編譯C/C++的四個(gè)步驟

接下來(lái)我們要了解一下使用gcc(GNU Compiler Collection蛆橡,GNU編譯器套件)生成可執(zhí)行二進(jìn)制文件的大概過(guò)程:

預(yù)處理(Preprocess)

預(yù)處理(Preprocess):預(yù)處理會(huì)處理一些編譯前的準(zhǔn)備工作舌界,把一些#define的宏定義完成文本替換,然后將#include里的文件復(fù)制到.cpp文件泰演,如果.h文件里還有.h文件呻拌,那么就會(huì)遞歸展開(kāi),要注意的是睦焕,在這一步中藐握,代碼注釋會(huì)被忽略。通過(guò)g++ -E命令將.c文件預(yù)處理為.i文件复亏,它是文本文件趾娃。

編譯(Compile)

編譯(Compile):編譯是把代碼轉(zhuǎn)換成匯編代碼,同時(shí)檢查詞法規(guī)則和語(yǔ)法規(guī)則缔御,如果沒(méi)有出現(xiàn)語(yǔ)法錯(cuò)誤抬闷,那么不管邏輯是否錯(cuò)誤都不會(huì)報(bào)錯(cuò)。通過(guò)g++ -S命令將.i文件轉(zhuǎn)換為.s文件耕突,它是文本文件笤成。

匯編(Assemble)

匯編(Assemble):匯編是把匯編代碼(.s文件)轉(zhuǎn)換為機(jī)器碼。通過(guò)g++ -c命令將.s文件轉(zhuǎn)換為.o文件(目標(biāo)文件)眷茁,它是二進(jìn)制格式炕泳。

鏈接(Link)

C/C++代碼經(jīng)過(guò)匯編后生成的.o文件(目標(biāo)文件),它是二進(jìn)制文件上祈,但是它不是最終可執(zhí)行的培遵,需要和系統(tǒng)組件(例如:標(biāo)準(zhǔn)庫(kù)、動(dòng)態(tài)鏈接庫(kù))鏈接起來(lái)才能得到可執(zhí)行的二進(jìn)制文件(Executable File)登刺,完成這個(gè)過(guò)程的組件叫做鏈接器(Linker)籽腕。鏈接分為靜態(tài)鏈接動(dòng)態(tài)鏈接,生成的文件叫做靜態(tài)庫(kù)動(dòng)態(tài)庫(kù)纸俭。

靜態(tài)庫(kù)

靜態(tài)庫(kù)在Linux下為.a文件皇耗,在Windows下為.lib文件。之所以稱之為靜態(tài)庫(kù)揍很,是因?yàn)樵阪溄与A段會(huì)將.o文件和引用到的庫(kù)一起鏈接打包到可執(zhí)行文件中郎楼,它有如下特點(diǎn):

  • 會(huì)在編譯時(shí)期完成靜態(tài)庫(kù)對(duì)函數(shù)庫(kù)的鏈接万伤。

  • 程序在運(yùn)行的時(shí)候與函數(shù)無(wú)關(guān),方便移植呜袁。

  • 會(huì)浪費(fèi)一定的空間和資源敌买,因?yàn)樗心繕?biāo)文件和涉及到的函數(shù)庫(kù)被鏈接合成一個(gè)可執(zhí)行文件。

動(dòng)態(tài)庫(kù)

動(dòng)態(tài)庫(kù)在Linux下為.so文件傅寡,在Windows下為.dll文件放妈。動(dòng)態(tài)庫(kù)在程序編譯時(shí)不會(huì)被鏈接到目標(biāo)文件,而是在程序運(yùn)行時(shí)才被載入荐操,它有如下特點(diǎn):

  • 把對(duì)一些庫(kù)函數(shù)的鏈接載入推遲到程序運(yùn)行的時(shí)期芜抒。

  • 不同的應(yīng)用程序如果調(diào)用相同的庫(kù),那么在內(nèi)存中只需要有一份該共享庫(kù)的實(shí)例托启,可以實(shí)現(xiàn)進(jìn)程之間的資源共享宅倒,節(jié)省了空間。

  • 由于動(dòng)態(tài)庫(kù)是在程序運(yùn)行的時(shí)候才載入屯耸,因此解決了靜態(tài)庫(kù)對(duì)程序的更新拐迁、部署和發(fā)布帶來(lái)的麻煩,只需要更新動(dòng)態(tài)庫(kù)就可以了疗绣,即增量更新线召。

  • 開(kāi)發(fā)者可以在程序代碼中控制鏈接載入,即顯示調(diào)用多矮。

Make

Make其實(shí)是一個(gè)批量處理的工具缓淹,它是通過(guò)調(diào)用makefile文件中開(kāi)發(fā)者指定的命令來(lái)進(jìn)行編譯鏈接,例如調(diào)用gcc或者其他編譯器的命令塔逃。

本機(jī)編譯

我們要在PC上運(yùn)行一個(gè)二進(jìn)制的程序(要注意的是讯壶,是以源碼的方式進(jìn)行編譯,而不是以包管理器的方式去安裝)會(huì)經(jīng)過(guò)如下步驟:

  1. 得到這段程序的源代碼湾盗,它可以是自己編寫的源代碼伏蚊,也可以是從第三方開(kāi)源網(wǎng)站上下載的源代碼。

  2. 在PC上編譯鏈接這些源代碼生成可執(zhí)行文件格粪。

  3. 在終端(Terminal)下執(zhí)行該可執(zhí)行文件躏吊。

總結(jié)就是使用本機(jī)器的編譯器和鏈接器,將源代碼編譯鏈接成一個(gè)可以在本機(jī)器運(yùn)行的程序帐萎,這個(gè)編譯過(guò)程叫做本機(jī)編譯比伏,它是正常的編譯過(guò)程。

交叉編譯

了解完本機(jī)編譯后吓肋,交叉編譯就好理解了凳怨,它就是一個(gè)平臺(tái)(例如:PC)上生成另外一個(gè)平臺(tái)(例如:Android瑰艘、iOS是鬼、其他嵌入式設(shè)備)可執(zhí)行的程序肤舞。這里的編譯機(jī)器是PC,所以編譯器是安裝在PC上均蜜,并且運(yùn)行在PC上的李剖,而這個(gè)編譯器叫做交叉工具編譯鏈。那其實(shí)為啥需要交叉編譯呢囤耳?因?yàn)檫\(yùn)行程序的目標(biāo)平臺(tái)運(yùn)算能力和存儲(chǔ)能力都是有限的篙顺,盡管現(xiàn)在iOS和Android設(shè)備的性能越來(lái)越強(qiáng)勁,但是和PC還是有一定的距離充择,而且ARM平臺(tái)下的編譯工具和整個(gè)編譯過(guò)程異常繁瑣德玫,所以PC是最佳選擇。目前大部分的嵌入式開(kāi)發(fā)平臺(tái)都提供本身平臺(tái)交叉編譯所需要運(yùn)行在PC上的交叉工具編譯鏈椎麦。

在所有的編譯器中宰僧,包括自行安裝在PC上的編譯器和嵌入式平臺(tái)的交叉工具編譯鏈,都包含以下這幾個(gè)工具:

  • CC:編譯器观挎,作用是對(duì)C或者C++源文件編譯成匯編文件琴儿。

  • AS:將匯編文件翻譯成機(jī)器碼鹅士,生成目標(biāo)文件曹鸠,匯編文件使用的是指令助記符。

  • AR:打包器榕莺,它可以從一個(gè)庫(kù)增加或者刪除目標(biāo)代碼模塊雄嚣。

  • LD:鏈接器晒屎,作用是為前面生成的目標(biāo)代碼分配地址空間,將多個(gè)目標(biāo)文件鏈接成一個(gè)庫(kù)或者可執(zhí)行文件现诀。

  • GDB:調(diào)試工具夷磕,它可以對(duì)正在運(yùn)行的程序進(jìn)行代碼調(diào)試。

  • STRIP:消除最終生成的庫(kù)文件或者可執(zhí)行文件其中的源碼仔沿。

  • NM:查看靜態(tài)庫(kù)文件中符號(hào)表坐桩。

  • Objdump:查看靜態(tài)庫(kù)或者動(dòng)態(tài)庫(kù)的方法簽名。

由于在安裝iOS的開(kāi)發(fā)環(huán)境Xcode的時(shí)候封锉,配套的編譯器已經(jīng)安裝好了绵跷,所以我們就不需要再單獨(dú)下載交叉工具編譯鏈。

LAME的交叉編譯

我們了解完交叉編譯后成福,以LAME庫(kù)為例進(jìn)行實(shí)踐碾局。

先介紹一下LAME庫(kù),它是目前最優(yōu)秀也是最常用的MP3編碼引擎奴艾。當(dāng)碼率達(dá)到320Kbit/s以上的時(shí)候净当,LAME編碼出來(lái)的音頻質(zhì)量幾乎可以和CD音質(zhì)媲美,并且還能保證其文件體積非常小,因此如果要在移動(dòng)端編碼MP3文件像啼,使用LAME是唯一選擇俘闯。

下面來(lái)講解下,在iOS平臺(tái)下如何交叉編譯LAME庫(kù)忽冻,并且打印LAME版本真朗。

下載LAME庫(kù)并解壓

然后在SourceForge下載最新版本的LAME庫(kù),目前為3.100僧诚,點(diǎn)擊下面文本即可下載:

lame-3.100.tar.gz

下載完成后遮婶,解壓文件得到lame-3.100文件夾。

編寫編譯腳本

由于蘋果在Xcode 14 Release Notes中聲明構(gòu)建iOS項(xiàng)目不再支持armv7湖笨、armv7si386指令集旗扑。

Building iOS projects with deployment targets for the armv7, armv7s, and i386 architectures is no longer supported. (92831716)

所以我們只需要編譯arm64x86_64指令集下的版本,其中x86_64的版本用于在模擬器上運(yùn)行慈省。下面來(lái)編寫相關(guān)的腳本肩豁,相關(guān)的代碼已經(jīng)push到示例代碼中,代碼如下所示:

arm64指令集下的編譯腳本

ios_lame_build_arm64.sh

./configure \
--disable-shared \
--disable-frontend \
--host=arm-apple-darwin \
--prefix="/Users/tanjiajun/lame-3.100/output/arm64" \
CC="xcrun -sdk iphoneos clang -arch arm64" \
CFLAGS="-arch arm64 -miphoneos-version-min=11.0" \
LDFLAGS="-arch arm64 -miphoneos-version-min=11.0"
make clean
make -j8
make install

x86_64指令集下的編譯腳本

ios_lame_build_x86_64.sh

./configure \
--disable-shared \
--disable-frontend \
--host=x86_64-apple-darwin \
--prefix="/Users/tanjiajun/lame-3.100/output/x86_64" \
CC="xcrun -sdk iphonesimulator clang -arch x86_64" \
CFLAGS="-arch x86_64 -mios-simulator-version-min=11.0" \
LDFLAGS="-arch x86_64 -mios-simulator-version-min=11.0"
make clean
make -j8
make install

下面解釋下這些命令和選項(xiàng)的含義:

configure是符合GNU標(biāo)準(zhǔn)的軟件包發(fā)布所必備的命令辫呻,所以這里通過(guò)configure的方式生成Makefile文件清钥,然后使用makemake install編譯和安裝整個(gè)庫(kù)。

  • make-clean:清除上次make命令產(chǎn)生的.o文件和可執(zhí)行文件放闺。

  • make -j8:根據(jù)Makefile文件執(zhí)行編譯四個(gè)步驟祟昭,也就是預(yù)處理編譯怖侦、匯編篡悟、鏈接,最后生成可執(zhí)行文件匾寝。make -j可以帶一個(gè)參數(shù)搬葬,用于進(jìn)行并行編譯j8是指讓make同時(shí)執(zhí)行最多八個(gè)命令艳悔。

  • make install:將編譯成功的可執(zhí)行文件安裝到系統(tǒng)目錄中急凰,一般為/usr/local/bin目錄。

我們可以使用configure -h命令查看configure的幫助文檔猜年,同時(shí)了解LAME的可選配置項(xiàng)抡锈。

  • --disable-shared:GNU標(biāo)準(zhǔn)中用于關(guān)閉動(dòng)態(tài)鏈接庫(kù),通常是在編譯出命令行工具的時(shí)候乔外,期望命令行工具可以單獨(dú)使用而不需要?jiǎng)討B(tài)鏈接庫(kù)的配置床三。

  • --disable-frontend:不編譯出LAME的可執(zhí)行文件。

  • --host:指定最終生成的庫(kù)要運(yùn)行的平臺(tái)杨幼,arm64指令集指定的是arm-apple-darwin撇簿,x86_64指令集指定的是x86_64-apple-darwin聂渊。

  • --prefix:指定最終生成的庫(kù)要放在哪個(gè)目錄下,通常來(lái)說(shuō)四瘫,這個(gè)是GNU大部分庫(kù)的標(biāo)準(zhǔn)配置歧沪。

  • CC:指定交叉工具編譯鏈的路徑,在這里就是指定gcc的路徑莲组。

  • CFLAGS:指定編譯時(shí)所帶的參數(shù)。-arch用于指定最終生成的庫(kù)運(yùn)行的目標(biāo)平臺(tái)暖夭,要注意的是锹杈,這個(gè)選項(xiàng)只在Darwin平臺(tái)有效,也就是只在Mac電腦有效迈着,如果不是該平臺(tái)可以使用-march竭望。另外,由于蘋果在Xcode 14 Release Notes中聲明棄用bitcode裕菠,在這之前是需要添加-fembed-bitcode選項(xiàng)咬清,它用于打開(kāi)bitcode,現(xiàn)在不需要了奴潘。同時(shí)指定了編譯出來(lái)這個(gè)庫(kù)所支持的最低iOS版本為11.0旧烧。

  • LDFLAGS:指定鏈接過(guò)程中的參數(shù)。參數(shù)含義和上面所述的CFLAGS一樣画髓,這里就不再贅述掘剪。

bitcode(蘋果已棄用)

以前是把所有的指令集的源碼編譯好,然后全部打包到一個(gè)APP奈虾,開(kāi)啟bitcode后夺谁,開(kāi)發(fā)者提交APP到App Store的時(shí)候,Xcode會(huì)將程序編譯為一個(gè)中間表現(xiàn)形式肉微,只需要上傳這個(gè)中間件(Intermediate Representation)匾鸥,而不是最終的可執(zhí)行文件,從而減少二進(jìn)制包的大小碉纳;在用戶下載APP之前勿负,App Store會(huì)根據(jù)用戶設(shè)備的指令集自動(dòng)編譯中間件,然后產(chǎn)生設(shè)備所需要的可執(zhí)行文件提供給用戶下載安裝劳曹;以后如果有新指令集的CPU笆环,就可以繼續(xù)從這份bitcode編譯出這個(gè)CPU上的可執(zhí)行文件提供給用戶下載安裝。

Starting with Xcode 14, bitcode is no longer required for watchOS and tvOS applications, and the App Store no longer accepts bitcode submissions from Xcode 14.

Xcode no longer builds bitcode by default and generates a warning message if a project explicitly enables bitcode: “Building with bitcode is deprecated. Please update your project and/or target settings to disable bitcode.” The capability to build with bitcode will be removed in a future Xcode release. IPAs that contain bitcode will have the bitcode stripped before being submitted to the App Store. Debug symbols can only be downloaded from App Store Connect / TestFlight for existing bitcode submissions and are no longer available for submissions made with Xcode 14. (86118779)

腳本編寫完成后厚者,存儲(chǔ)到lame-3.100文件夾下躁劣,然后通過(guò)下面這兩條命令修改這兩個(gè)腳本的文件權(quán)限為可執(zhí)行權(quán)限,命令如下所示:

chmod 777 /Users/tanjiajun/lame-3.100/ios_lame_build_arm64.sh
chmod 777 /Users/tanjiajun/lame-3.100/ios_lame_build_x86_64.sh

執(zhí)行完畢后運(yùn)行腳本库菲,命令如下所示:

/Users/tanjiajun/lame-3.100/ios_lame_build_arm64.sh
/Users/tanjiajun/lame-3.100/ios_lame_build_x86_64.sh

執(zhí)行完畢后账忘,可以在lame-3.100文件夾中的output文件下看到兩個(gè)文件夾分別為arm64x86_64,這兩個(gè)文件夾下會(huì)看到includelibshare文件夾鳖擒,由于在配置的時(shí)候裁剪了可執(zhí)行文件溉浙,所以不會(huì)產(chǎn)生bin文件夾,include文件夾為編譯過(guò)程中所需要引用的lame.h頭文件蒋荚,lib文件夾為鏈接過(guò)程中所需要鏈接的libmp3lame.a靜態(tài)庫(kù)文件戳稽。

至此我們編譯出arm64x86_64這兩個(gè)指令集下的頭文件靜態(tài)庫(kù)文件

合并靜態(tài)庫(kù)

我們需要將上面兩個(gè)指令集下的靜態(tài)庫(kù)文件合并到一個(gè)libmp3lame.a靜態(tài)庫(kù)文件中期升,可以通過(guò)如下命令實(shí)現(xiàn):

lipo -create /Users/tanjiajun/lame-3.100/output/arm64/lib/libmp3lame.a /Users/tanjiajun/lame-3.100/output/x86_64/lib/libmp3lame.a -output libmp3lame.a

執(zhí)行完畢后就可以在output文件夾下看到libmp3lame.a靜態(tài)庫(kù)文件惊奇,然后我們?cè)谠撐募A下執(zhí)行如下命令:

file libmp3lame.a

執(zhí)行完畢后,如果看到以下信息播赁,說(shuō)明編譯成功:

libmp3lame.a: Mach-O universal binary with 2 architectures: [x86_64:current ar archive random library] [arm64:current ar archive random library]
libmp3lame.a (for architecture x86_64): current ar archive random library
libmp3lame.a (for architecture arm64):  current ar archive random library

Xcode準(zhǔn)備工作

我們?cè)赬code新建一個(gè)SwiftUI的項(xiàng)目颂郎,執(zhí)行以下步驟:

  1. 新建一個(gè)lame文件夾,導(dǎo)入上面生成的lame.h頭文件和合并后的libmp3lame.a靜態(tài)庫(kù)文件容为。

  2. 因?yàn)槲覀冃枰?strong>Swift調(diào)用C++文件,所以要?jiǎng)?chuàng)建橋接文件iOSAudioDemo-Bridging-Header.h乓序,并且在工程中Build Settings中的Objective-C Bridging Header設(shè)置文件名為iOSAudioDemo-Bridging-Header.h

引入lame.h頭文件坎背,代碼如下所示:

//
//  iOSAudioDemo-Bridging-Header.h
//  iOSAudioDemo
//
//  Created by 譚嘉俊 on 2024/3/7.
//

#ifndef iOSAudioDemo_Bridging_Header_h
#define iOSAudioDemo_Bridging_Header_h

#import "iOSAudioDemo/lame/lame.h"

#endif /* iOSAudioDemo_Bridging_Header_h */

打印LAME庫(kù)的版本

我們用SwiftUI寫界面替劈,代碼如下所示:

//
//  iOSAudioDemoApp.swift
//  iOSAudioDemo
//
//  Created by 譚嘉俊 on 2024/3/5.
//

import SwiftUI

@main
struct iOSAudioDemoApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

調(diào)用get_lame_version函數(shù)就能獲取當(dāng)前LAME版本,ContentView代碼如下所示:

//
//  ContentView.swift
//  iOSAudioDemo
//
//  Created by 譚嘉俊 on 2024/3/5.
//

import SwiftUI

struct ContentView: View {
    
    // 調(diào)用get_lame_version函數(shù)就能獲取當(dāng)前LAME版本
    let lameVersion: String = String(cString: get_lame_version())
    
    var body: some View {
        NavigationView {
            Text(lameVersion)
                .navigationTitle("iOSAudioDemo")
        }
    }
}

#Preview {
    ContentView()
}

運(yùn)行后得滤,我們就可以看到界面有個(gè)居中的3.100文本抬纸,這就是目前編譯的LAME版本,代表我們編譯LAME庫(kù)成功耿戚。

我的GitHub:TanJiaJunBeyond

Android通用框架:Android通用框架

我的掘金:譚嘉俊

我的簡(jiǎn)書(shū):譚嘉俊

我的CSDN:譚嘉俊

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末湿故,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子膜蛔,更是在濱河造成了極大的恐慌坛猪,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件皂股,死亡現(xiàn)場(chǎng)離奇詭異墅茉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)呜呐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門就斤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人蘑辑,你說(shuō)我怎么就攤上這事洋机。” “怎么了洋魂?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵绷旗,是天一觀的道長(zhǎng)喜鼓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)衔肢,這世上最難降的妖魔是什么庄岖? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮角骤,結(jié)果婚禮上隅忿,老公的妹妹穿的比我還像新娘。我一直安慰自己邦尊,他們只是感情好背桐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著胳赌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪匙隔。 梳的紋絲不亂的頭發(fā)上疑苫,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音纷责,去河邊找鬼捍掺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛再膳,可吹牛的內(nèi)容都是我干的挺勿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼喂柒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼不瓶!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起灾杰,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蚊丐,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后艳吠,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體麦备,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年昭娩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凛篙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡栏渺,死狀恐怖呛梆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情磕诊,我是刑警寧澤削彬,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布全庸,位于F島的核電站,受9級(jí)特大地震影響融痛,放射性物質(zhì)發(fā)生泄漏壶笼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一雁刷、第九天 我趴在偏房一處隱蔽的房頂上張望覆劈。 院中可真熱鬧,春花似錦沛励、人聲如沸责语。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)坤候。三九已至,卻和暖如春企蹭,著一層夾襖步出監(jiān)牢的瞬間白筹,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工谅摄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留徒河,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓送漠,卻偏偏與公主長(zhǎng)得像顽照,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闽寡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容