jni:音频转成MP3格式的C库,将类名或者路径名改成自己项目路径,通过NDK生成so库。
mp3library:录音、格式转换的Library,包括so库和工具类。
步骤一:下载NDK,并配置(Mac)
1.下载地址:http://tools.android-studio.org/
2.解压后,配置环境变量
export NDK_HOME=/Users/liepin/Library/Android/ndk (改成自己的路径)
export PATH=$PATH:$NDK_HOME/
然后使用下列命令使其立即生效(否则重启后才生效):
source ~/.bash_profile
可以使用echo $NDK_HOME,检查此变量是否已正确设置。
步骤二:修改C代码相关路径,编译成so库
将C代码中路径改成自己库(so库所在的位置)的路径,cd进入jni目录下,运行ndk-build 命令生成so库,之后将编译成功的libs文件下面的so库拷贝到自己的项目中。
步骤三:应用层代码代码调用系统AudioRecord类开始录音
try {
String path = recAudioDir(recordFilePath).getAbsolutePath();
recFile = new File(path, System.currentTimeMillis() + ".mp3");
// 初始化录音工具类(mp3library库中),传入录音保存的路径
mRecorder = new MP3Recorder(recFile);
// 开始录音
mRecorder.start();
} catch (Exception e) {
e.printStackTrace();
}
}
AndioRecord类的主要功能是让各种JAVA应用能够管理音频资源,以便它们通过此类能够录制平台的声音输入硬件所收集的声音。此功能的实现就是通过”pulling同步”AudioRecord对象的声音数据来完成的。在录音过程中,应用所需要做的就是通过read方法去及时地获取AudioRecord对象的录音数据. AudioRecord类提供的三个获取声音数据的方法分别是read(byte[], int, int), read(short[], int, int), read(ByteBuffer, int). 无论选择使用那一个方法都必须事先设定方便用户的声音数据的存储格式。
开始录音的时候,一个AudioRecord需要初始化一个相关联的声音buffer, 这个buffer主要是用来保存新的声音数据。这个buffer的大小,我们可以在对象构造期间去指定。它表明一个AudioRecord对象还没有被读取(同步)声音数据前能录多长的音(即一次可以录制的声音容量)。一般情况下录音实现的简单流程如下:
1.创建一个数据流。
2.构造一个AudioRecord对象,其中需要的最小录音缓存buffer大小可以通过getMinBufferSize方法得到。如果buffer容量过小,将导致对象构造的失败。
3.初始化一个buffer,该buffer大于等于AudioRecord对象用于写声音数据的buffer大小。
4.开始录音。
5.从AudioRecord中读取声音数据到初始化buffer,将buffer中数据导入数据流。
6.停止录音。
7.关闭数据流。
public void start() throws IOException {
//以地址的方式获取frameCount的值。最小缓冲区的大小
mBufferSize = AudioRecord.getMinBufferSize(DEFAULT_SAMPLING_RATE,
DEFAULT_CHANNEL_CONFIG, DEFAULT_AUDIO_FORMAT.getAudioFormat());
/** 初始化 AudioRecord
* DEFAULT_AUDIO_SOURCE: 音源 MediaRecorder.AudioSource.MIC 本机的声音
* DEFAULT_SAMPLING_RATE: 采样率 44100,模拟器仅支持从麦克风输入8kHz采样率
* DEFAULT_CHANNEL_CONFIG:录音的声道数,当前设置的 16
* DEFAULT_AUDIO_FORMAT: 录音的比特数,当前设置的 PCMFormat.PCM_16BIT
* mBufferSize: 期望录音时系统为其提供的缓冲区大小
*/
mAudioRecord = new AudioRecord(DEFAULT_AUDIO_SOURCE,
DEFAULT_SAMPLING_RATE, DEFAULT_CHANNEL_CONFIG, DEFAULT_AUDIO_FORMAT.getAudioFormat(),mBufferSize);
mPCMBuffer = new short[mBufferSize];
mAudioRecord.startRecording();
new Thread() {
@Override
public void run() {
//设置线程权限
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
while (mIsRecording) {
int readSize = mAudioRecord.read(mPCMBuffer, 0, mBufferSize);
if (readSize > 0) {
mEncodeThread.addTask(mPCMBuffer, readSize);
calculateRealVolume(mPCMBuffer, readSize);
}
}
mAudioRecord.stop();
mAudioRecord.release();
mAudioRecord = null;
}
}
}
因为Android录音完成保存的音频格式默认为amr,这是个体积较小,播放效果较差的音频格式,所以我们需要将amr格式的音频转成mp3格式。这个时候就需要使用我们刚刚编译的so库了。
步骤四:在录音过程中实时转成MP3格式
/**
* 实现AudioRecord.OnRecordPositionUpdateListener
* 从缓冲区中读取并处理数据,使用lame编码MP3
* @return 从缓冲区中读取的数据的长度,缓冲区中没有数据时返回0
*/
@Override
public void onPeriodicNotification(AudioRecord recorder){
if (mTasks.size() > 0) {
Task task = mTasks.remove(0);
short[] buffer = task.getData();
int readSize = task.getReadSize();
// 调用so库转成mp3
int encodedSize = LameUtil.encode(buffer, buffer, readSize, mMp3Buffer);
if (encodedSize > 0){
try {
mFileOutputStream.write(mMp3Buffer, 0, encodedSize);
} catch (IOException e) {
e.printStackTrace();
}
}
return readSize;
}
return 0;
}
}
步骤五:停止录音
private void stopRecorder() {
try {
if (mRecorder != null) {
mRecorder.stop();
mRecorder = null;
}
} catch (Exception e) {
e.printStackTrace();
}
}
至此,录音转码完成!