Android集成LAME库,实现pcm转mp3

一、交叉编译LAME库

LAME是一种非常优秀的MP3编码引擎,在业界,转码成MP3格式的音频文件时,最常用的编码器就是LAME库。

1. 下载LAME库源码

https://sourceforge.net/projects/lame/files/lame/
进入LAME官网下载LAME源码,我选择最新版本:3.100

2. 配置交叉编译环境

在编译LAME之前,我们需要先配置交叉编译环境。

Android NDK附带了交叉工具链,具体参考这篇文章:https://developer.android.com/ndk/guides/other_build_systems?hl=zh-cn

我的NDK路径为:/home/lorien/Android/Sdk/ndk/22.1.7171670/toolchains/llvm/prebuilt/linux-x86_64/bin

3. 配置、编译、安装LAME

首先我们需要编译使用的一些环境变量:

#!/bin/bashexport TOOLCHAIN=/home/lorien/Android/Sdk/ndk/22.1.7171670/toolchains/llvm/prebuilt/linux-x86_64
export TARGET=aarch64-linux-android
export API=21
export AR=$TOOLCHAIN/bin/llvm-ar
export CC=$TOOLCHAIN/bin/$TARGET$API-clang
export AS=$CC
export CXX=$TOOLCHAIN/bin/$TARGET$API-clang++
export LD=$TOOLCHAIN/bin/ld
export RANLIB=$TOOLCHAIN/bin/llvm-ranlib
export STRIP=$TOOLCHAIN/bin/llvm-stripexport CFLAGS="-fPIC"

接下来解压的LAME源码:lame-3.100.tar.gz,解压后进入源码根目录:/lame-3.100

配置:

./configure --host=arm-linux --disable-shared --disable-frontend --enable-static --prefix=/Users/zhanghao43/Desktop/lame/arm64-v8a

编译:

make clean
make -j4

安装:

make install

安装完成后,生成的头文件和库文件,就会在prefix指定的路径下面,即:/Users/zhanghao43/Desktop/lame/arm64-v8a

在生成的文件中,接下来需要使用的文件是:

  • 头文件:/Users/zhanghao43/Desktop/lame/arm64-v8a/include/lame/lame.h
  • 库文件:/Users/zhanghao43/Desktop/lame/arm64-v8a/lib/lame/libmp3lame.a

至此,LAME库交叉编译完成。

二、创建Android Native项目使用LAME库

下面我们使用LAME库创建一个Android Demo项目,完成PCM音频的录制以及PCM文件转MP3的功能
我们需要创建Android Natvie项目。

1. 配置工程

我们先把编译LAME库生成的头文件和库文件放到项目中,路径如下图:
在这里插入图片描述
然后,我们修改下CMakeLists.txt,让CMake在编译、链接时,找到LAME头文件和库文件。CMakeLists.txt文件内容如下:

cmake_minimum_required(VERSION 3.10.2)# Declares and names the project.project("lame")include_directories(${CMAKE_SOURCE_DIR}/include/lame)add_library( # Sets the name of the library.native-lib# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).native-lib.cpp mp3_encoder.cpp)add_library(mp3lame STATIC IMPORTED)
set_target_properties(mp3lame PROPERTIES IMPORTED_LOCATION${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libmp3lame.a)find_library( # Sets the name of the path variable.log-lib# Specifies the name of the NDK library that# you want CMake to locate.log )target_link_libraries(native-libmp3lame)

2. C++代码

接下来,可以写代码了。首先看一下mp3_encoder.h, mp3_encoder.cpp

#ifndef LAME_MP3ENCODER_H
#define LAME_MP3ENCODER_H
#include <stdio.h>
#include "lame.h"class Mp3Encoder {
private:FILE* pcmFile;FILE* mp3File;lame_t lameClient;public:Mp3Encoder();~Mp3Encoder();int Init(const char* pcmFilePath, const char* mp3FilePath, int sampleRate, int channels, int bitRate);void Encode();void Destroy();
};#endif //LAME_MP3ENCODER_H
#include "mp3_encoder.h"
#include "lame.h"Mp3Encoder::Mp3Encoder() {
}int Mp3Encoder::Init(const char* pcmFilePath, const char* mp3FilePath, int sampleRate, int channels, int bitRate) {int ret = -1;pcmFile = fopen(pcmFilePath, "rb");if (pcmFile) {mp3File = fopen(mp3FilePath, "wb");if (mp3File) {lameClient = lame_init();// in 采样率lame_set_in_samplerate(lameClient, sampleRate);// out 采样率lame_set_out_samplerate(lameClient, sampleRate);lame_set_num_channels(lameClient, channels);lame_set_brate(lameClient, bitRate / 1000);lame_init_params(lameClient);ret = 0;}}return ret;
}void Mp3Encoder::Encode() {int bufferSize = 1024 * 256;short *buffer = new short[bufferSize / 2];short *leftBuffer = new short[bufferSize / 4];short *rightBuffer = new short[bufferSize / 4];unsigned char* mp3_buffer = new unsigned char[bufferSize];size_t readBufferSize = 0;while ((readBufferSize = fread(buffer, 2, bufferSize / 2, pcmFile)) > 0) {for (int i = 0; i < readBufferSize; i++) {if (i % 2 == 0) {leftBuffer[i / 2] = buffer[i];} else {rightBuffer[i / 2] = buffer[i];}}size_t wroteSize = lame_encode_buffer(lameClient, (short int *) leftBuffer, (short int *) rightBuffer, (int)(readBufferSize / 2), mp3_buffer, bufferSize);fwrite(mp3_buffer, 1, wroteSize, mp3File);}delete [] buffer;delete [] leftBuffer;delete [] rightBuffer;delete [] mp3_buffer;
}void Mp3Encoder::Destroy() {if (pcmFile) {fclose(pcmFile);}if (mp3File) {fclose(mp3File);lame_close(lameClient);}
}

然后我们看一下native-lib.cpp中的JNI方法:

Mp3Encoder *encoder;extern "C" JNIEXPORT jint JNICALL
Java_com_baidu_lame_MainActivity_pcmToMp3JNI(JNIEnv *env,jobject,jstring pcm_path,jstring mp3_path,jint sample_rate,jint channel,jint bit_rate) {const char *pcmPath = env->GetStringUTFChars(pcm_path, NULL);const char *mp3Path = env->GetStringUTFChars(mp3_path, NULL);encoder = new Mp3Encoder();encoder->Init(pcmPath, mp3Path, sample_rate, channel, bit_rate);encoder->Encode();env->ReleaseStringUTFChars(pcm_path, pcmPath);env->ReleaseStringUTFChars(mp3_path, mp3Path);return 0;
}

3. Java代码

先贴一下MainAcgtivity的XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="开始录制pcm"android:onClick="startRecordPcm"/><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="停止录制pcm"android:onClick="stopRecordPcm"/><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="pcm转mp3"android:onClick="pcm2mp3"/></LinearLayout>

MainActivity.kt

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)}external fun stringFromJNI(): Stringexternal fun pcmToMp3JNI(pcmPath: String, mp3Path: String,sampleRate: Int, channel: Int, bitRate: Int): Intcompanion object {init {System.loadLibrary("native-lib")}}private lateinit var audioRecord: AudioRecordprivate var pcmFilePath: String = ""private var buffersize = 1024private var isRecord = false/*** 调用Native代码完成PCM文件转成MP3*/fun pcm2mp3(view: View) {val pcmPath = getPCMFile().absolutePathval mp3Path = getMP3File().absolutePathval sampleRate = 44100val channel = 2val bitRate = 64000val ret = pcmToMp3JNI(pcmPath, mp3Path, sampleRate, channel, bitRate)Toast.makeText(this, "$ret", Toast.LENGTH_SHORT).show()}/*** 开始录制PCM音频文件*/@SuppressLint("MissingPermission")fun startRecordPcm(view: View) {val frequency = 44100val channelConfig = AudioFormat.CHANNEL_IN_STEREOval audioEncoding = AudioFormat.ENCODING_PCM_16BITbuffersize = AudioRecord.getMinBufferSize(frequency, channelConfig, audioEncoding)audioRecord = AudioRecord(MediaRecorder.AudioSource.MIC,frequency,channelConfig,audioEncoding,buffersize)pcmFilePath = getPCMFile().absolutePathisRecord = trueRecordThread().start()}/*** 结束录制PCM音频文件*/fun stopRecordPcm(view: View) {isRecord = false}private fun getPCMFile(): File {val root = getExternalFilesDir(null)val csvDir = File(root, "/audio/")if (!csvDir.exists()) {// 创建csv 目录csvDir.mkdir()}return File(csvDir, "sing.pcm")}private fun getMP3File(): File {val root = getExternalFilesDir(null)val csvDir = File(root, "/audio/")if (!csvDir.exists()) {// 创建csv 目录csvDir.mkdir()}return File(csvDir, "sing.mp3")}/*** 录制PCM音频线程*/inner class RecordThread : Thread() {override fun run() {audioRecord.startRecording()var fos: FileOutputStream? = nulltry {Log.d(TAG, "pcm文件:$pcmFilePath")fos = FileOutputStream(pcmFilePath)val bytes = ByteArray(buffersize)while (isRecord) {audioRecord.read(bytes, 0, bytes.size)fos.write(bytes, 0, bytes.size)fos.flush()}Log.d(TAG, "停止录制")audioRecord.stop()fos.flush()} catch (e: Exception) {Log.d(TAG, "exception: $e")} finally {if (fos != null) {try {fos.close()} catch (e: Exception) {}}}}}
}

最后,权限:

<!--录音-->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!--读取SD卡-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

代码中,没有动态申请权限,APP装上后,需要再到设置页面给APP开下对应权限才能录制音频。

OK,本文到这就结束了,感谢大家阅读。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/57782.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[opcv图像处理] C/C|++将图片转换为马赛克效果

这个程序将图片转换为马赛克效果。 算法原理&#xff1a;求出每个小方块内所有像素的颜色平均值&#xff0c;然后用来设置为该小方块的颜色。依次处理每个小方块&#xff0c;即可实现马赛克效果。 完整代码如下&#xff1a; / // 程序名称&#xff1a;将图片转换为马赛克效果…

从入门到入土:Python实现爬取网易云歌词|评论生成词云图

写在前面&#xff1a; 此博客仅用于记录个人学习进度&#xff0c;学识浅薄&#xff0c;若有错误观点欢迎评论区指出。欢迎各位前来交流。&#xff08;部分材料来源网络&#xff0c;若有侵权&#xff0c;立即删除&#xff09; Python实现爬取网易云歌词|评论生成词云图 免责声明…

用python写一个爬取周杰伦所有歌词的爬虫

写一个爬虫爬一下周董的所有歌词看看这么多年他为啥这么火 唱的都是什么主题的歌可以这么经久不衰&#xff0c;他凭啥被称为流行歌曲天王。废话不多说 直接上代码 今天比较晚了 之后再慢慢完善讲解。代码比较low因为是编自学边完成的&#xff0c;所以只是实现了基本的功能&…

buuoj 来首歌吧 writeup

题目&#xff08;二十三&#xff09;&#xff1a; 【题型】Misc 【题目】来首歌吧 【来源】&#xff08;buuoj&#xff09;https://buuoj.cn/challenges#%E6%9D%A5%E9%A6%96%E6%AD%8C%E5%90%A7 【思路】通过音频的节奏得出摩斯密码&#xff0c;得到flag。 【具体步骤】 Step1&a…

chatgpt赋能python:Python打折代码:为你的电商网站提供更便捷的价格管理工具

Python打折代码&#xff1a;为你的电商网站提供更便捷的价格管理工具 在当前这个竞争激烈的市场&#xff0c;随时提供大量的优惠促销活动是吸引消费者注意力和提高销售额的必要手段之一。而电商网站在进行促销活动时&#xff0c;一个鲜为人知的秘密是——打折代码。打折代码作…

利用Python实现有道翻译的功能

这是上学期在Python课堂上老师讲的利用Python实现有道翻译的功能。 流程如下&#xff1a;网址&#xff1a;有道翻译 输入翻译名称&#xff0c;按F12对网页进行分析&#xff0c;通过查询到translate开头的连接中我们找到了翻译的数据参数 首先将参数以urlencode编码的方式传入到…

中英文自动翻译(有道翻译、彩云小译)

一.有道翻译 1&#xff09;获取应用ID 和 应用密钥 https://ai.youdao.com/doc.s#guide 2&#xff09;遵循接口参数接入 具体参考接口文档&#xff1a;https://ai.youdao.com/DOCSIRMA/html/%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E7%BF%BB%E8%AF%91/API%E6%96%87%E6%A1%A3/…

Unity 接入有道智云AI - 文本翻译

接入接口前首先需要申请应用ID和应用秘钥&#xff0c;登录有道智云AI开放平台&#xff0c;创建应用&#xff0c;获取应用ID和秘钥。 定义接口响应类数据结构&#xff0c;接口实际返回内容和官方文档有点出入&#xff0c;大概是文档未更新吧。 以下是官方文档给出的说明&#x…

【Python爬虫】有道翻译新旧API接口

&#x1f308;据说&#xff0c;看我文章时 关注、点赞、收藏 的 帅哥美女们 心情都会不自觉的好起来。 前言&#xff1a; &#x1f9e1;作者简介&#xff1a;大家好我是 user_from_future &#xff0c;意思是 “ 来自未来的用户 ” &#xff0c;寓意着未来的自己一定很棒~ ✨个…

Translate插件的有道翻译

在plugins下载Translate插件 setting-> Tools->Translation 没有id和密钥就申请注册 登录后 创建应用 创建成功后输入id和密钥 点击鼠标右键即可使用 翻译效果 over

python利用有道词典翻译_Python利用有道词典接口制作即时翻译的工具

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理 以下文章来源于Python 实用宝典&#xff0c;作者Python 实用宝典 前言 在编程时经常会遇到需要将中文词汇翻译成英文的情况。 比如变量名的定义、取一个合…

如何用python“优雅的”调用有道翻译

文章目录 前言分析分析url分析参数01分析参数02加密分析 模拟请求注意点请求代码执行结果 结语 前言 其实在以前就盯上有道翻译了的&#xff0c;但是由于时间问题一直没有研究(我的骚操作还在后面&#xff0c;记得关注)&#xff0c;本文主要讲解如何用python调用有道翻译&…

Python 调用有道的翻译接口

最近为了熟悉一下 js 用有道翻译练了一下手&#xff0c;写一篇博客记录一下&#xff0c;也希望能对大家有所启迪&#xff0c;不过这些网站更新太快&#xff0c;可能大家尝试的时候会有所不同。 首先来看一下网页 post 过去的数据 大家不难发现&#xff0c;我们翻译的内容是…

使用python打造一个中英互译软件(基于有道翻译)

&#xff08;本博客简洁明了&#xff0c;适合小白入门&#xff09; 首先明确整体构架&#xff1a; 1.爬虫部分 2.界面部分 3.打包 涵盖的库&#xff1a; import urllib.request import urllib.parse import json import tkinter as tk import tkinter.messagebox 先确定爬…

ubuntu最好用的划词翻译词典:有道词典和GoldenDict

目录 1、安装有到词典 2、安装GoldenDict 3、GoldenDict的一些简单配置以及相关bug修改 用惯了Windows下的有道词典&#xff0c;其划词翻译功能用起来令人极其舒适&#xff5e;Ubuntu系统中也有有道词典以及一个类似的类似的软件GoldenDict&#xff0c;下面就分别介绍下这两…

有道翻译接口 破解

有道翻译 API 最近有些任务需要将中文翻译成英文&#xff0c;由于个人英文水平问题&#xff0c;每次都要打开好几个在线翻译网页&#xff0c;一句一句的丢进去&#xff0c;取最佳者为所用&#xff0c;甚是麻烦。 任务完成之后&#xff0c;就稍微研究了一下各个翻译接口&#…

对接有道翻译api中英翻译软件

中译英翻译软件对接了有道翻译API的翻译数据接口&#xff0c;通过数据接口&#xff0c;我们可以获得文本的批量翻译并对我们的译后文本进行内容自动编辑&#xff0c;通过调用有道翻译API数据接口&#xff0c;我们可以在我们的中译英翻译软件中更灵活地对我们的文本进行翻译处理…

百度、阿里、腾讯、有道各平台翻译API申请教程

文章目录 文章推荐 vscode插件 var-translate-en 中翻英转驼峰命名百度翻译申请腾讯翻译申请阿里翻译申请有道翻译申请 文章推荐 vscode插件 var-translate-en 中翻英转驼峰命名 快捷 一键转换为英文&#xff0c;并生成多种命名风格支持多平台翻译服务配置&#xff08;谷歌、腾…

塔望 · ​食界​人物|红牛饮料背后的两个企业家

关注行业&#xff0c;更要关注行业发展背后的人。关注企业&#xff0c;更要关注企业的人格化身——“企业家”。人类在任何领域的创新和进步&#xff0c;都离不开企业家精神。优秀的企业家和企业家精神&#xff0c;是经济社会发展的重要推动力。 本期塔望【​食界​人物】将带…

正则表达式爬取红牛分公司数据

正则表达式还是很好玩的,爬取红牛官网分公司信息 import requests import re import pandas as pd response requests.get(url"http://www.redbull.com.cn/about/branch") company re.findall(<h2>(.*?)</h2>, response.text) add re.findall("…