Android 之NDK实战系列(3)——Android Studio下使用lame 进行简单的转换

3 篇文章 0 订阅
1 篇文章 0 订阅

首先,介绍下今天使用的工具——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 中加入响应的权限

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

在gradle 中进行配置,这里就不多解释了,如果不懂就翻阅前两篇博文

 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);
}

静态代码块调用so链接库,native调用本地方法,还是那句话,不懂就翻前两篇博文


敲黑板!!重点来了

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 <android/log.h>
#include <string.h>

#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();

            }
        });
    }
}

由于这是一个耗时事件,我把转换过程放入到一个线程中,转换后在同级目录下会有一个conver.mp3文件,对了,我事先在根目录下存放了一个名为test.wav的音频文件,如果没有地址的传入容易报空指针。

Demo下载

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值