做網(wǎng)站是什么工作天津網(wǎng)絡(luò)關(guān)鍵詞排名
本文主要講解的是音頻基礎(chǔ)概念、交叉編譯原理和實(shí)踐(LAME的交叉編譯),是基于Android平臺(tái),示例代碼如下所示:
AndroidAudioDemo
音頻基礎(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)到頻率范圍大約為20Hz20kHz**,對(duì)**3kHz4kHz頻率范圍內(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ù)脑?#xff0c;這樣的大小實(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-AAC、HE-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ǔ)音聊天的音頻消息。
Android平臺(tái)增加C和C++支持
Android提供了一種編譯框架,叫做JNI(Java Native Interface),用于允許運(yùn)行于JVM的Java或者Kotlin代碼去調(diào)用本地代碼(C、C++、匯編語(yǔ)言)。大概步驟為將相關(guān)的C/C++代碼放在項(xiàng)目模塊的cpp目錄下,在構(gòu)建項(xiàng)目的時(shí)候,Gradle會(huì)將這些代碼和應(yīng)用的代碼一起打包到原生庫(kù),然后Java或者Kotlin代碼就可以通過(guò)JNI去調(diào)用原生庫(kù)中的函數(shù)。什么時(shí)候需要用到JNI呢?有以下幾種情況:
-
應(yīng)用程序需要一些平臺(tái)的特性支持,但是Java層沒(méi)有提供相應(yīng)的API支持,例如:OpenSL ES的使用)
-
調(diào)用一些已經(jīng)存在并且已是成熟方案的C/C++庫(kù),例如:使用LAME編碼MP3文件、使用FFmpeg處理音頻或者視頻、使用OpenGL ES處理視頻特效。
-
應(yīng)用程序?qū)Σ糠诌壿嫷倪\(yùn)行速度有較高的要求,那么這部分就可以用C/C++實(shí)現(xiàn),再通過(guò)JNI向Java層提供訪問(wèn)接口。
我們需要以下組件:
-
Android原生開(kāi)發(fā)套件(NDK):這是一套可以讓開(kāi)發(fā)者使用C/C++的工具。
-
ndk-build腳本或者CMake:用于構(gòu)建原生庫(kù)。
-
LLDB:Android Studio用于調(diào)試原生代碼的程序,默認(rèn)情況下,它會(huì)隨同Android Studio的安裝而安裝。
這里講解下ndk-build腳本和CMake的區(qū)別,在講解之前,我們要了解下下面的內(nèi)容:
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或者其他編譯器的命令。上面提的ndk-build腳本就是使用NDK基于Make來(lái)構(gòu)建項(xiàng)目。
CMake
如果是簡(jiǎn)單的工程makefile手寫起來(lái)還是比較輕松的,但是如果復(fù)雜的工程手寫起來(lái)就比較麻煩了,換平臺(tái)還要重新修改,所以就有上面提到的CMake。CMake是一個(gè)開(kāi)源的跨平臺(tái)自動(dòng)化建構(gòu)系統(tǒng),它可以根據(jù)CMakeList.txt文件自動(dòng)生成makefile文件來(lái)給上面的Make工具使用。它也是目前Android Studio編譯NDK默認(rèn)構(gòu)建工具,當(dāng)然也可以使用上面提到的ndk-build腳本,官方也是支持的。
本機(jī)編譯
我們要在PC上運(yùn)行一個(gè)二進(jìn)制的程序(要注意的是,是以源碼的方式進(jìn)行編譯,而不是以包管理器的方式去安裝)會(huì)經(jīng)過(guò)如下步驟:
-
得到這段程序的源代碼,它可以是自己編寫的源代碼,也可以是從第三方開(kāi)源網(wǎng)站上下載的源代碼。
-
在PC上編譯鏈接這些源代碼生成可執(zhí)行文件。
-
在終端(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ù)的方法簽名。
在Android的NDK提供的交叉工具編譯鏈就在開(kāi)發(fā)者用到的ndk下的prebuilt/darwin-x86_64/bin路徑中,它提供了上面這些工具。我的路徑如下所示:
/Users/tanjiajun/Library/Android/sdk/ndk/26.1.10909125/prebuilt/darwin-x86_64/bin
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)講解下,在Android平臺(tái)下如何交叉編譯LAME庫(kù),并且打印LAME版本。
Android Studio準(zhǔn)備工作
首先我們的Android Studio要下載好NDK,然后新建一個(gè)Android項(xiàng)目,右鍵模塊點(diǎn)擊“Add C++ to Module”,上面也提及過(guò),Android Studio編譯NDK的默認(rèn)構(gòu)建工具是CMake,所以我們會(huì)看到添加完畢后會(huì)有相關(guān)的代碼和文件生成,例如生成了cpp文件夾和里面相關(guān)的cpp文件和CMakeList.txt文件。
下載LAME庫(kù)并解壓,復(fù)制到項(xiàng)目中
然后在SourceForge下載最新版本的LAME庫(kù),目前為3.100,點(diǎn)擊下面文本即可下載:
lame-3.100.tar.gz
下載完成后,解壓文件得到lame-3.100文件夾,然后找到libmp3lame文件夾,把里面的.c和.h文件全部復(fù)制到Android Studio項(xiàng)目生成的cpp文件夾下,我這邊會(huì)新建一個(gè)lame的文件夾來(lái)存放這些文件,這樣看起來(lái)目錄會(huì)整潔點(diǎn),我這邊寫了個(gè)的Python腳本用于把文件夾里的.c和.h文件復(fù)制到指定的目錄,同時(shí)還會(huì)打印復(fù)制后的絕對(duì)路徑。我已經(jīng)把它push到示例代碼中,文件名為CopySpecifiedFiles.py,代碼如下所示:
import os
import shutiloldDir = '/Users/tanjiajun/lame-3.100/libmp3lame'
newDir = '/Users/tanjiajun/StudioProjects/AndroidAudioDemo/app/src/main/cpp/lame'
cExtension = '.c'
hExtension = '.h'if not os.path.exists(newDir):os.makedirs(newDir)
for file in os.listdir(oldDir):extension = os.path.splitext(file)[-1]if extension == cExtension or extension == hExtension:shutil.copy(os.path.join(oldDir, file), newDir)print(os.path.join(newDir, file))
然后再找到上面提到的lame-3.100文件夾下的include文件夾,把里面的lame.h文件同樣復(fù)制到Android Studio的lame文件夾。
編譯項(xiàng)目
接下來(lái),我們需要修改CMakeList.txt文件,利用上面腳本打印的絕對(duì)路徑,修改后代碼如下所示:
cmake_minimum_required(VERSION 3.22.1)project("audiodemo")add_library(${CMAKE_PROJECT_NAME}SHAREDaudiodemo.cpplame/reservoir.clame/mpglib_interface.clame/machine.hlame/fft.hlame/set_get.clame/quantize_pvt.hlame/psymodel.hlame/newmdct.clame/id3tag.hlame/lame-analysis.hlame/id3tag.clame/reservoir.hlame/lameerror.hlame/set_get.hlame/quantize.clame/fft.clame/l3side.hlame/newmdct.hlame/quantize.hlame/gain_analysis.clame/encoder.clame/lame.clame/bitstream.clame/quantize_pvt.clame/presets.clame/bitstream.hlame/encoder.hlame/gain_analysis.hlame/lame_global_flags.hlame/psymodel.clame/lame.hlame/tables.clame/tables.hlame/takehiro.clame/util.clame/util.hlame/vbrquantize.clame/vbrquantize.hlame/VbrTag.clame/VbrTag.hlame/version.clame/version.h
)target_link_libraries(${CMAKE_PROJECT_NAME}androidlog
)
然后我們make project,build完后以下文件會(huì)報(bào)錯(cuò),這里列出解決的辦法:
util.h
在570行,如圖所示:
解決辦法:
extern float fast_log2(float x);
fft.c
在47行,如圖所示:
解決辦法:刪除該行代碼。
set_get.h
在24行,如圖所示:
解決辦法:
#include "lame.h"
其他錯(cuò)誤
如圖所示:
解決辦法:修改app模塊中的build.gradle.kts文件,在android函數(shù)中增加如下代碼:
android {...defaultConfig {...externalNativeBuild {cmake {cFlags("-DSTDC_HEADERS")}}}...
}
修改后,再次make project后編譯成功,CMake會(huì)幫我們自動(dòng)生成libaudiodemo.so文件,過(guò)程上面也提過(guò)了,這里就不再贅述。
打印LAME庫(kù)的版本
新建MainActivity,這里我使用Compose寫界面,并且在AndroidManifest.xml添加相關(guān)的代碼,AndroidManifest.xml的代碼我就不貼了,詳細(xì)可查看該demo。調(diào)用getLameVersion函數(shù)就能獲取當(dāng)前LAME版本,要注意的是,需要調(diào)用**System.loadLibrary(“audiodemo”)**把生成的動(dòng)態(tài)庫(kù)加載進(jìn)來(lái)。代碼如下所示:
/*** Created by TanJiaJun on 2023/12/13.*/
class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {ContentView()}}@Composableprivate fun ContentView() {ConstraintLayout(modifier = Modifier.fillMaxSize()) {val (topBox, lameVersionText) = createRefs()Box(modifier = Modifier.fillMaxWidth().height(45.dp).background(Purple80).constrainAs(topBox) {start.linkTo(parent.start)top.linkTo(parent.top)end.linkTo(parent.end)}) {Text(modifier = Modifier.align(Alignment.Center),text = packageManager.getApplicationLabel(applicationInfo).toString(),fontSize = 18.sp,color = White)}Text(modifier = Modifier.constrainAs(lameVersionText) {start.linkTo(parent.start)top.linkTo(topBox.bottom)end.linkTo(parent.end)bottom.linkTo(parent.bottom)},text = getLameVersion(),fontSize = 16.sp,color = Black)}}/*** 獲取當(dāng)前LAME版本** @return 當(dāng)前LAME版本*/private external fun getLameVersion(): Stringprivate companion object {init {System.loadLibrary("androidaudiodemo")}}}
運(yùn)行后,我們就可以看到界面有個(gè)居中的3.100文本,這就是目前編譯的LAME版本,代表我們編譯LAME庫(kù)成功。
我的GitHub:TanJiaJunBeyond
Android通用框架:Android通用框架
我的掘金:譚嘉俊
我的簡(jiǎn)書:譚嘉俊
我的CSDN:譚嘉俊