Android录制mp3格式文件
发布时间 - 2026-01-11 02:45:06 点击率:次前言

最近做一个即时通信类的项目,由于要保证pc端,iOS端和Android端的通用性,最终统一为MP3格式,一直担心MP3格式会不会很大,但是实测还是可以接受的。下面来看看具体步骤:
工具
MP3格式是用一个开源项目转的,MP3lame,由于该项目用到了jni,所以需要大家配置好ndk环境,环境配置在此就不多说了,大家可以自行百度,最新的应该很好配置。
创建jni
拷贝文件
下载好后(我下载的是3.98.4版本)打开,找到libmp3lame文件,将里面的.h和.c拷贝下来,在自己的工程里创建jni文件夹,在jni文件夹下新建一个文件夹(我的命名为lame-3.98.4_libmp3lame,后面会用到),将刚才拷贝的文件复制进去,然后再把include文件夹里的lame.h也拷贝进去。
创建Android.mk
在jni中创建文件,Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LAME_LIBMP3_DIR := lame-3.98.4_libmp3lame LOCAL_MODULE := mp3lame LOCAL_SRC_FILES := $(LAME_LIBMP3_DIR)/bitstream.c $(LAME_LIBMP3_DIR)/fft.c $(LAME_LIBMP3_DIR)/id3tag.c $(LAME_LIBMP3_DIR)/mpglib_interface.c $(LAME_LIBMP3_DIR)/presets.c $(LAME_LIBMP3_DIR)/quantize.c $(LAME_LIBMP3_DIR)/reservoir.c $(LAME_LIBMP3_DIR)/tables.c $(LAME_LIBMP3_DIR)/util.c $(LAME_LIBMP3_DIR)/VbrTag.c $(LAME_LIBMP3_DIR)/encoder.c $(LAME_LIBMP3_DIR)/gain_analysis.c $(LAME_LIBMP3_DIR)/lame.c $(LAME_LIBMP3_DIR)/newmdct.c $(LAME_LIBMP3_DIR)/psymodel.c $(LAME_LIBMP3_DIR)/quantize_pvt.c $(LAME_LIBMP3_DIR)/set_get.c $(LAME_LIBMP3_DIR)/takehiro.c $(LAME_LIBMP3_DIR)/vbrquantize.c $(LAME_LIBMP3_DIR)/version.c com_maxi_mp3record_MP3Recorder.c include $(BUILD_SHARED_LIBRARY)
**注意:**LAME_LIBMP3_DIR := lame-3.98.4_libmp3lame 需要将其改为你的项目中的文件名,即上面说的jni下新建的文件夹。
大家应该看到了最后一句的com_maxi_mp3record_MP3Recorder.c
很明显这是我自己创建的.c文件。用来调用mp3lame中的接口的,对应着我java中的com.maxi.mp3record.MP3Recorder.java。咱们先创建java文件。
创建MP3Recorder.java
对应你的包名建一个MP3Recorder.java文件,该文件是java文件对应你的包名建立即可。
package cn.ctvonline.android.modules.project.widget;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Handler;
/**
* <b>类功能描述:</b><div style="margin-left:40px;margin-top:-10px">
* MP3实时录制功能,可暂停,注意因踩用Native开发,不能混淆
*/
public class MP3Recorder {
private String filePath;
private int sampleRate;
private boolean isRecording = false;
private boolean isPause = false;
private Handler handler;
/**
* 开始录音
*/
public static final int MSG_REC_STARTED = 1;
/**
* 结束录音
*/
public static final int MSG_REC_STOPPED = 2;
/**
* 暂停录音
*/
public static final int MSG_REC_PAUSE = 3;
/**
* 继续录音
*/
public static final int MSG_REC_RESTORE = 4;
/**
* 缓冲区挂了,采样率手机不支持
*/
public static final int MSG_ERROR_GET_MIN_BUFFERSIZE = -1;
/**
* 创建文件时扑街了
*/
public static final int MSG_ERROR_CREATE_FILE = -2;
/**
* 初始化录音器时扑街了
*/
public static final int MSG_ERROR_REC_START = -3;
/**
* 录紧音的时候出错
*/
public static final int MSG_ERROR_AUDIO_RECORD = -4;
/**
* 编码时挂了
*/
public static final int MSG_ERROR_AUDIO_ENCODE = -5;
/**
* 写文件时挂了
*/
public static final int MSG_ERROR_WRITE_FILE = -6;
/**
* 没法关闭文件流
*/
public static final int MSG_ERROR_CLOSE_FILE = -7;
public MP3Recorder(int sampleRate) {
this.sampleRate = sampleRate;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
/**
* 开片
*/
public void start() {
if (isRecording) {
return;
}
new Thread() {
@Override
public void run() {
android.os.Process
.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
// 根据定义好的几个配置,来获取合适的缓冲大小
final int minBufferSize = AudioRecord.getMinBufferSize(
sampleRate, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT);
if (minBufferSize < 0) {
if (handler != null) {
handler.sendEmptyMessage(MSG_ERROR_GET_MIN_BUFFERSIZE);
}
return;
}
AudioRecord audioRecord = new AudioRecord(
MediaRecorder.AudioSource.MIC, sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 2);
// 5秒的缓冲
short[] buffer = new short[sampleRate * (16 / 8) * 1 * 5];
byte[] mp3buffer = new byte[(int) (7200 + buffer.length * 2 * 1.25)];
FileOutputStream output = null;
try {
File file = createSDFile(filePath);
output = new FileOutputStream(file);
} catch (FileNotFoundException e) {
if (handler != null) {
handler.sendEmptyMessage(MSG_ERROR_CREATE_FILE);
}
return;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
MP3Recorder.init(sampleRate, 1, sampleRate, 32);
isRecording = true; // 录音状态
isPause = false; // 录音状态
try {
try {
audioRecord.startRecording(); // 开启录音获取音频数据
if (mListener != null) {
mListener.wellPrepared();
}
} catch (IllegalStateException e) {
// 不给录音...
if (handler != null) {
handler.sendEmptyMessage(MSG_ERROR_REC_START);
}
return;
}
try {
// 开始录音
if (handler != null) {
handler.sendEmptyMessage(MSG_REC_STARTED);
}
int readSize = 0;
boolean pause = false;
while (isRecording) {
/*--暂停--*/
if (isPause) {
if (!pause) {
handler.sendEmptyMessage(MSG_REC_PAUSE);
pause = true;
}
continue;
}
if (pause) {
handler.sendEmptyMessage(MSG_REC_RESTORE);
pause = false;
}
/*--End--*/
/*--实时录音写数据--*/
readSize = audioRecord.read(buffer, 0,
minBufferSize);
voiceLevel = getVoiceSize(readSize, buffer);
if (readSize < 0) {
if (handler != null) {
handler.sendEmptyMessage(MSG_ERROR_AUDIO_RECORD);
}
break;
} else if (readSize == 0) {
;
} else {
int encResult = MP3Recorder.encode(buffer,
buffer, readSize, mp3buffer);
if (encResult < 0) {
if (handler != null) {
handler.sendEmptyMessage(MSG_ERROR_AUDIO_ENCODE);
}
break;
}
if (encResult != 0) {
try {
output.write(mp3buffer, 0, encResult);
} catch (IOException e) {
if (handler != null) {
handler.sendEmptyMessage(MSG_ERROR_WRITE_FILE);
}
break;
}
}
}
/*--End--*/
}
/*--录音完--*/
int flushResult = MP3Recorder.flush(mp3buffer);
if (flushResult < 0) {
if (handler != null) {
handler.sendEmptyMessage(MSG_ERROR_AUDIO_ENCODE);
}
}
if (flushResult != 0) {
try {
output.write(mp3buffer, 0, flushResult);
} catch (IOException e) {
if (handler != null) {
handler.sendEmptyMessage(MSG_ERROR_WRITE_FILE);
}
}
}
try {
output.close();
} catch (IOException e) {
if (handler != null) {
handler.sendEmptyMessage(MSG_ERROR_CLOSE_FILE);
}
}
/*--End--*/
} finally {
audioRecord.stop();
audioRecord.release();
}
} finally {
MP3Recorder.close();
isRecording = false;
}
if (handler != null) {
handler.sendEmptyMessage(MSG_REC_STOPPED);
}
}
}.start();
}
public void stop() {
isRecording = false;
}
public void pause() {
isPause = true;
}
public void restore() {
isPause = false;
}
public boolean isRecording() {
return isRecording;
}
public boolean isPaus() {
if (!isRecording) {
return false;
}
return isPause;
}
// 获得声音的level
public int getVoiceSize(int r, short[] buffer) {
if (isRecording) {
try {
long v = 0;
// 将 buffer 内容取出,进行平方和运算
for (int i = 0; i < buffer.length; i++) {
v += buffer[i] * buffer[i];
}
// 平方和除以数据总长度,得到音量大小。
double mean = v / (double) r;
double volume = 10 * Math.log10(mean);
return (((int) volume / 10) - 1);
} catch (Exception e) {
// TODO Auto-generated catch block
}
}
return 1;
}
/**
* 在SD卡上创建文件
*
* @throws IOException
*/
public static File createSDFile(String fileName) throws IOException {
File file = new File(fileName);
if (!isFileExists(file))
if (file.isDirectory()) {
file.mkdirs();
} else {
file.createNewFile();
}
return file;
}
private int voiceLevel;
public int getVoiceLevel() {
return voiceLevel;
}
public interface AudioStageListener {
void wellPrepared();
}
public AudioStageListener mListener;
public void setOnAudioStageListener(AudioStageListener listener) {
mListener = listener;
}
/**
* 录音状态管理
*
* @see RecMicToMp3#MSG_REC_STARTED
* @see RecMicToMp3#MSG_REC_STOPPED
* @see RecMicToMp3#MSG_REC_PAUSE
* @see RecMicToMp3#MSG_REC_RESTORE
* @see RecMicToMp3#MSG_ERROR_GET_MIN_BUFFERSIZE
* @see RecMicToMp3#MSG_ERROR_CREATE_FILE
* @see RecMicToMp3#MSG_ERROR_REC_START
* @see RecMicToMp3#MSG_ERROR_AUDIO_RECORD
* @see RecMicToMp3#MSG_ERROR_AUDIO_ENCODE
* @see RecMicToMp3#MSG_ERROR_WRITE_FILE
* @see RecMicToMp3#MSG_ERROR_CLOSE_FILE
*/
public void setHandle(Handler handler) {
this.handler = handler;
}
/*--以下为Native部分--*/
static {
System.loadLibrary("mp3lame");
}
/**
* 初始化录制参数
*/
public static void init(int inSamplerate, int outChannel,
int outSamplerate, int outBitrate) {
init(inSamplerate, outChannel, outSamplerate, outBitrate, 7);
}
/**
* 初始化录制参数 quality:0=很好很慢 9=很差很快
*/
public native static void init(int inSamplerate, int outChannel,
int outSamplerate, int outBitrate, int quality);
/**
* 音频数据编码(PCM左进,PCM右进,MP3输出)
*/
public native static int encode(short[] buffer_l, short[] buffer_r,
int samples, byte[] mp3buf);
/**
* 据说录完之后要刷干净缓冲区
*/
public native static int flush(byte[] mp3buf);
/**
* 结束编码
*/
public native static void close();
}
创建c文件
在创建c文件,创建在jni下,命名就按你的java文件所在的包名命名”.”替换为“_”。例如:com_maxi_mp3record_MP3Recorder.c。当然还得有头文件:com_maxi_mp3record_MP3Recorder.h。
com_maxi_mp3record_MP3Recorder.c
#include "lame-3.98.4_libmp3lame/lame.h"
#include "com_maxi_mp3record_MP3Recorder.h"
static lame_global_flags *glf = NULL;
JNIEXPORT void JNICALL Java_com_maxi_mp3record_MP3Recorder_init(
JNIEnv *env, jclass cls, jint inSamplerate, jint outChannel,
jint outSamplerate, jint outBitrate, jint quality) {
if (glf != NULL) {
lame_close(glf);
glf = NULL;
}
glf = lame_init();
lame_set_in_samplerate(glf, inSamplerate);
lame_set_num_channels(glf, outChannel);
lame_set_out_samplerate(glf, outSamplerate);
lame_set_brate(glf, outBitrate);
lame_set_quality(glf, quality);
lame_init_params(glf);
}
JNIEXPORT jint JNICALL Java_com_maxi_mp3record_MP3Recorder_encode(
JNIEnv *env, jclass cls, jshortArray buffer_l, jshortArray buffer_r,
jint samples, jbyteArray mp3buf) {
jshort* j_buffer_l = (*env)->GetShortArrayElements(env, buffer_l, NULL);
jshort* j_buffer_r = (*env)->GetShortArrayElements(env, buffer_r, NULL);
const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf);
jbyte* j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL);
int result = lame_encode_buffer(glf, j_buffer_l, j_buffer_r,
samples, j_mp3buf, mp3buf_size);
(*env)->ReleaseShortArrayElements(env, buffer_l, j_buffer_l, 0);
(*env)->ReleaseShortArrayElements(env, buffer_r, j_buffer_r, 0);
(*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0);
return result;
}
JNIEXPORT jint JNICALL Java_com_maxi_mp3record_MP3Recorder_flush(
JNIEnv *env, jclass cls, jbyteArray mp3buf) {
const jsize mp3buf_size = (*env)->GetArrayLength(env, mp3buf);
jbyte* j_mp3buf = (*env)->GetByteArrayElements(env, mp3buf, NULL);
int result = lame_encode_flush(glf, j_mp3buf, mp3buf_size);
(*env)->ReleaseByteArrayElements(env, mp3buf, j_mp3buf, 0);
return result;
}
JNIEXPORT void JNICALL Java_com_maxi_mp3record_MP3Recorder_close(
JNIEnv *env, jclass cls) {
lame_close(glf);
glf = NULL;
}
com_maxi_mp3record_MP3Recorder.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_maxi_mp3record_MP3Recorder */
#ifndef _Included_com_maxi_mp3record_MP3Recorder
#define _Included_com_maxi_mp3record_MP3Recorder
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_maxi_mp3record_MP3Recorder
* Method: init
* Signature: (IIIII)V
*/
JNIEXPORT void JNICALL Java_com_maxi_mp3record_MP3Recorder_init
(JNIEnv *, jclass, jint, jint, jint, jint, jint);
/*
* Class: com_maxi_mp3record_MP3Recorder
* Method: encode
* Signature: ([S[SI[B)I
*/
JNIEXPORT jint JNICALL Java_com_maxi_mp3record_MP3Recorder_encode
(JNIEnv *, jclass, jshortArray, jshortArray, jint, jbyteArray);
/*
* Class: com_maxi_mp3record_MP3Recorder
* Method: flush
* Signature: ([B)I
*/
JNIEXPORT jint JNICALL Java_com_maxi_mp3record_MP3Recorder_flush
(JNIEnv *, jclass, jbyteArray);
/*
* Class: com_maxi_mp3record_MP3Recorder
* Method: close
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_maxi_mp3record_MP3Recorder_close
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
这俩文件别只复制不看内容啊,里面可都是连接java和c的接口,所以不能有差错。举个例子吧,#ifndef _Included_com_maxi_mp3record_MP3Recorder,这个就得替换成你对应的包名,里面所有都得替换成你自己的程序对应的包名。
编译ndk
创建Application.mk在你的项目文件的jni下,在里面写入:
APP_ABI := all
如果不加,NDK只会编译“armeabi”,然而安卓有很多不同类型的处理器,所以我们不止需要arm。相信你们搞到现在了肯定ndk都配置好了,然后打开终端,进入到你的项目(找到你的ndk目录下的ndk-bulid,最好把它添加到环境变量里,对于以后编译比较方便,在此默认你没添加环境变量),执行ndk-bulid。稍等片刻你会发现你的项目里多了一个obj文件夹,obj文件夹下会生成”arm64-v8a”、”armeabi”、”armeabi-v7a”、”mips”、”mips64”、”x86”、”x86_64”。打开它,各个文件夹下会有一个libmp3lame.so。ok,没错那就是你要的“滑板鞋”。将它放入你的libs文件下,没有自行创建,各个平台便都可以加载了。
使用方法
MP3Recorder recorder = new MP3Recorder(8000); recorder.setFilePath(voicePath);//录音保存目录 recorder.start();//开始录音 recorder.stop();//录音结束 recorder.getVoiceLevel()//这是我封装的获取音频振幅接口,大家可以用来录音的时候显示声音大小,数据自行调节。
总结
之前一直用MediaRecorder录音,发现录出来的只能是amr、acc等格式,用lame转MP3感觉是不可行的。我试了没能成功,不知道具体是什么原因,所以大家有时间可以研究研究,没时间就不要尝试了。
Mp3lame录制出来的声音还是挺靠谱的(不过据听说iOS就有些莎莎声),然后录制出来的大小还是可以接受的,五秒钟的音频大概在20k左右的样子吧。使用还是很方便的。如果有什么疑问或建议请留言哈。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# Android录制mp3格式
# Android录制mp3
# Android录制
# 利用libmp3lame实现在Android上录音MP3文件示例
# Android编程实现播放MP3功能示例
# 详解Android应用开发--MP3音乐播放器代码实现(一)
# Android实现使用流媒体播放远程mp3文件的方法
# 比较完整的android MP3 LRC歌词滚动高亮显示(附源码)
# Android递归方式删除某文件夹下的所有文件(.mp3文件等等)
# 平方和
# 自己的
# 挂了
# 很好
# 在此
# 这是我
# 的是
# 都是
# 替换成
# 有什么
# 几个
# 会有
# 好了
# 一句
# 你要
# 说了
# 不多
# 会不会
# 将其
# 把它
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
矢量图网站制作软件,用千图网的一张矢量图做公司app首页,该网站并未说明版权等问题,这样做算不算侵权?应该如何解决?
Laravel怎么配置.env环境变量_Laravel生产环境敏感数据保护与读取【方法】
制作网站软件推荐手机版,如何制作属于自己的手机网站app应用?
西安市网站制作公司,哪个相亲网站比较好?西安比较好的相亲网站?
怎么制作网站设计模板图片,有电商商品详情页面的免费模板素材网站推荐吗?
laravel怎么为API路由添加签名中间件保护_laravel API路由签名中间件保护方法
Java解压缩zip - 解压缩多个文件或文件夹实例
详解Nginx + Tomcat 反向代理 负载均衡 集群 部署指南
如何用西部建站助手快速创建专业网站?
用yum安装MySQLdb模块的步骤方法
个人摄影网站制作流程,摄影爱好者都去什么网站?
Laravel如何为API生成Swagger或OpenAPI文档
C++时间戳转换成日期时间的步骤和示例代码
Laravel的HTTP客户端怎么用_Laravel HTTP Client发起API请求教程
Android Socket接口实现即时通讯实例代码
如何用腾讯建站主机快速创建免费网站?
Laravel如何为API编写文档_Laravel API文档生成与维护方法
jQuery 常见小例汇总
Python制作简易注册登录系统
怎么用AI帮你为初创公司进行市场定位分析?
php 三元运算符实例详细介绍
Laravel如何创建自定义中间件?(Middleware代码示例)
Laravel如何使用Blade模板引擎?(完整语法和示例)
如何在搬瓦工VPS快速搭建网站?
zabbix利用python脚本发送报警邮件的方法
Laravel Octane如何提升性能_使用Laravel Octane加速你的应用
Win11搜索不到蓝牙耳机怎么办 Win11蓝牙驱动更新修复【详解】
详解一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)
魔方云NAT建站如何实现端口转发?
Laravel软删除怎么实现_Laravel Eloquent SoftDeletes功能使用教程
Laravel Eloquent模型如何创建_Laravel ORM基础之Model创建与使用教程
如何在阿里云高效完成企业建站全流程?
如何用AWS免费套餐快速搭建高效网站?
Python进程池调度策略_任务分发说明【指导】
,交易猫的商品怎么发布到网站上去?
node.js报错:Cannot find module 'ejs'的解决办法
HTML5空格在Angular项目里怎么处理_Angular中空格的渲染问题【详解】
成都网站制作公司哪家好,四川省职工服务网是做什么用?
如何快速重置建站主机并恢复默认配置?
利用python获取某年中每个月的第一天和最后一天
Laravel项目结构怎么组织_大型Laravel应用的最佳目录结构实践
深圳网站制作设计招聘,关于服装设计的流行趋势,哪里的资料比较全面?
Laravel PHP版本要求一览_Laravel各版本环境要求对照
Laravel如何实现文件上传和存储?(本地与S3配置)
高性能网站服务器部署指南:稳定运行与安全配置优化方案
晋江文学城电脑版官网 晋江文学城网页版直接进入
Laravel怎么调用外部API_Laravel Http Client客户端使用
香港服务器网站卡顿?如何解决网络延迟与负载问题?
简单实现jsp分页
Python文件流缓冲机制_IO性能解析【教程】

