首先,介绍下今天使用的工具——Lame
LAME 是最好的MP3编码器,编码高品质MP3的最好也是唯一的选择。LAME本身是控制台程序,需要加外壳程序才比较容易使用,也可以在别的软件(比如EAC)中间调用。是一款出色的MP3压缩程序,它使用了独创的人体听音心理学模型和声学模型,改变了人们对MP3高音发哑、低音发破的音质的印象。——百度百科
通过前两篇博文,大家已经对C、NDK的使用有了一定的认识,现在就逐渐深入NDK的使用
录音功能在我们日常生活中使用十分频繁,通过此功能,我们免去了大量用于打字的时间,在APP中植入语音模块也变得尤为重要,但是,通常情况下,本地录音保存的音频文件都是wav类型(无损原音),但是,如果该文件用于本地保存,或者网络传输,相比较主流的mp3文件,在音质相近的情况下,我们需要消耗更多的本地内存和网络流量。所以,为了优化app,我们需要对音频格式进行转化。而Lame就提供了这一功能。
首先,我们登陆Lame 进行开发包的下载,在下载的压缩包中,有大量的c,cpp文件,这些都是用于实现音频转化的工具
现在,我们创建一个项目,暂且命名为DemoLame
先预览下项目的目录结构
因为我们在Demo中只实现wav mp3的转换,所以项目中就一个Activity 和一个Lame工具类
因为应用涉及文件的读取,我们在Manifest 中加入响应的权限
ndk {
moduleName "converMp3" //设置.so文件名称
ldLibs "log"
abiFilters "armeabi", "armeabi-v7a", "x86"
}
编写LameUtils
public class LameUtils {
static {
System.loadLibrary("converMp3");
}
public static native void conver(String sourWav,String sourMp3);
}
敲黑板!!重点来了
C/C++文件的添加
打开我们下载的开发包,打开libmp3lame文件夹,把文件夹下所有的文件添加到jni文件夹下,(其实重要复制C/C++文件就够了,有很多是图标文件,但是如果你懒,留在项目里影响也不大),接着回到根目录,把include文件夹下的lame.h也复制到jni文件夹下,现在,我们已经将所需要的文件添加到我们的项目中了。
我们来对一些C代码进行修改
1、因为我们刚才更改了lame.h的路径,所以把set_get.h中的#include改成#include "lame.h"
2、把utils.h中 extern ieee754_float32_t fast_log2(ieee754_float32_t x) 的ieee754_float32_t 改成浮点型float,这是Linux下的编写习惯
3、删除fft.c 中的#include "vector/lame_intrin.h",这是对CPU的优化,手持端暂时用不到
修改C代码后,我们编译下LameUtils,实现jni方法
#include "com_adrian_demolame_LameUtils.h"
#include "lame.h"
#include
#include
#define TAG "lamedemo" // LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // LOGF类型
JNIEXPORT void JNICALL Java_com_adrian_demolame_LameUtils_conver
(JNIEnv * env, jclass obj, jstring wavresoucre, jstring mp3resource){
//获取路径
char *cwav = (*env)->GetStringUTFChars(env, wavresoucre, 0);
char *cmp3 = (*env)->GetStringUTFChars(env, mp3resource, 0);
LOGE("源文件路径是%s", cwav);
LOGE("转化件路径是%s", cmp3);
//定义缓冲
short int wav_buffer[8192 * 2];
unsigned char mp3_buffer[8192];
//加载文件
FILE *fwav = fopen(cwav, "rb");//rb 二进制 读取
FILE *fmp3 = fopen(cmp3, "wb");
//初始化lame编码器
lame_t lame = lame_init();
//2. 设置lame mp3编码的采样率
lame_set_in_samplerate(lame, 44100);
lame_set_num_channels(lame, 2);
// 3. 设置MP3的编码方式
lame_set_VBR(lame, vbr_default);
lame_init_params(lame);
LOGE("Lame初始化完成");
int read;
int write;
int total = 0; // 当前读的wav文件的byte数目
//循环读取转化
do
{
read = fread(wav_buffer, sizeof(short int) * 2, 8192, fwav);
total += read * sizeof(short int) * 2;//read是实际读取的次数
LOGE("converting ....%d", total);
// 调用java代码 完成进度条的更新
if (read != 0)
{
write = lame_encode_buffer_interleaved(lame, wav_buffer, read, mp3_buffer, 8192);
//把转化后的mp3数据写到文件里
fwrite(mp3_buffer, sizeof(unsigned char), write, fmp3);
}
if (read == 0)
{
lame_encode_flush(lame, mp3_buffer, 8192);
}
} while (read != 0);
LOGE("convert finish");
lame_close(lame);
fclose(fwav);//关闭文件操作句柄
fclose(fmp3);
}
最后,在Activity中添加LameUtils的调用
public class MainActivity extends AppCompatActivity {
private Button mBtnConver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnConver = (Button)findViewById(R.id.btn_conver);
mBtnConver.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final File path = Environment.getExternalStorageDirectory();
File file = new File(path.getAbsolutePath() + "/test.wav");
new Thread() {
@Override
public void run() {
LameUtils.conver(path.getAbsolutePath()+"/test.wav",path.getAbsolutePath()+"/conver.mp3");
}
}.start();
}
});
}
}
Demo下载