Android录制mp3格式文件

前言

最近做一个即时通信类的项目,由于要保证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格式文件 的全部内容, 来源链接: utcz.com/z/333522.html

回到顶部