android开发语音录制,Android音频开发(6):Mp3的录制 - 使用Lame实时录制MP3格式音频...

Android 音频开发 目录

项目地址

前言

上一篇介绍了如何去编译so文件,这一篇主要介绍下如何实时将pcm数据转换为MP3数据。

一、实现过程:

AudioRecorder在开启录音后,通过read方法不断获取pcm的采样数据,每次获取到数据后交给lame去处理,处理完成后存入文件中。

这一篇相对之前代码,增加了两个类:Mp3Encoder.java 和 Mp3EncoderThread.java

Mp3Encoder: 通过Jni调用so文件的c代码,将pcm转换成mp3格式数据

Mp3EncodeThread: 将pcm转换成mp3时需要开启子线程进行统一管理,以及全部转码完成的回调

代码实现

Mp3Encoder.java

public class Mp3Encoder {

static {

System.loadLibrary("mp3lame");

}

public native static void close();

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 init(int inSampleRate, int outChannel, int outSampleRate, int outBitrate, int quality);

public static void init(int inSampleRate, int outChannel, int outSampleRate, int outBitrate) {

init(inSampleRate, outChannel, outSampleRate, outBitrate, 7);

}

}

Mp3EncodeThread.java

每次有新的pcm数据后将数据打包成ChangeBuffer 类型,通过addChangeBuffer()存放到线程队列当中,线程开启后会不断轮询队列内容,当有内容后开始转码,无内容时进入阻塞,直到数据全部处理完成后,关闭轮询。

public class Mp3EncodeThread extends Thread {

private static final String TAG = Mp3EncodeThread.class.getSimpleName();

/**

* mp3文件的码率 32kbit/s = 4kb/s

*/

private static final int OUT_BITRATE = 32;

private List cacheBufferList = Collections.synchronizedList(new LinkedList());

private File file;

private FileOutputStream os;

private byte[] mp3Buffer;

private EncordFinishListener encordFinishListener;

/**

* 是否已停止录音

*/

private volatile boolean isOver = false;

/**

* 是否继续轮询数据队列

*/

private volatile boolean start = true;

public Mp3EncodeThread(File file, int bufferSize) {

this.file = file;

mp3Buffer = new byte[(int) (7200 + (bufferSize * 2 * 1.25))];

RecordConfig currentConfig = RecordService.getCurrentConfig();

int sampleRate = currentConfig.getSampleRate();

Mp3Encoder.init(sampleRate, currentConfig.getChannelCount(), sampleRate, OUT_BITRATE);

}

@Override

public void run() {

try {

this.os = new FileOutputStream(file);

} catch (FileNotFoundException e) {

Logger.e(e, TAG, e.getMessage());

return;

}

while (start) {

ChangeBuffer next = next();

Logger.v(TAG, "处理数据:%s", next == null ? "null" : next.getReadSize());

lameData(next);

}

}

public void addChangeBuffer(ChangeBuffer changeBuffer) {

if (changeBuffer != null) {

cacheBufferList.add(changeBuffer);

synchronized (this) {

notify();

}

}

}

public void stopSafe(EncordFinishListener encordFinishListener) {

this.encordFinishListener = encordFinishListener;

isOver = true;

synchronized (this) {

notify();

}

}

private ChangeBuffer next() {

for (; ; ) {

if (cacheBufferList == null || cacheBufferList.size() == 0) {

try {

if (isOver) {

finish();

}

synchronized (this) {

wait();

}

} catch (Exception e) {

Logger.e(e, TAG, e.getMessage());

}

} else {

return cacheBufferList.remove(0);

}

}

}

private void lameData(ChangeBuffer changeBuffer) {

if (changeBuffer == null) {

return;

}

short[] buffer = changeBuffer.getData();

int readSize = changeBuffer.getReadSize();

if (readSize > 0) {

int encodedSize = Mp3Encoder.encode(buffer, buffer, readSize, mp3Buffer);

if (encodedSize < 0) {

Logger.e(TAG, "Lame encoded size: " + encodedSize);

}

try {

os.write(mp3Buffer, 0, encodedSize);

} catch (IOException e) {

Logger.e(e, TAG, "Unable to write to file");

}

}

}

private void finish() {

start = false;

final int flushResult = Mp3Encoder.flush(mp3Buffer);

if (flushResult > 0) {

try {

os.write(mp3Buffer, 0, flushResult);

os.close();

} catch (final IOException e) {

Logger.e(TAG, e.getMessage());

}

}

Logger.d(TAG, "转换结束 :%s", file.length());

if (encordFinishListener != null) {

encordFinishListener.onFinish();

}

}

public static class ChangeBuffer {

private short[] rawData;

private int readSize;

public ChangeBuffer(short[] rawData, int readSize) {

this.rawData = rawData.clone();

this.readSize = readSize;

}

short[] getData() {

return rawData;

}

int getReadSize() {

return readSize;

}

}

public interface EncordFinishListener {

/**

* 格式转换完毕

*/

void onFinish();

}

}

使用

private class AudioRecordThread extends Thread {

private AudioRecord audioRecord;

private int bufferSize;

AudioRecordThread() {

bufferSize = AudioRecord.getMinBufferSize(currentConfig.getSampleRate(),

currentConfig.getChannelConfig(), currentConfig.getEncodingConfig()) * RECORD_AUDIO_BUFFER_TIMES;

audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, currentConfig.getSampleRate(),

currentConfig.getChannelConfig(), currentConfig.getEncodingConfig(), bufferSize);

if (currentConfig.getFormat() == RecordConfig.RecordFormat.MP3 && mp3EncodeThread == null) {

initMp3EncoderThread(bufferSize);

}

}

@Override

public void run() {

super.run();

startMp3Recorder();

}

private void initMp3EncoderThread(int bufferSize) {

try {

mp3EncodeThread = new Mp3EncodeThread(resultFile, bufferSize);

mp3EncodeThread.start();

} catch (Exception e) {

Logger.e(e, TAG, e.getMessage());

}

}

private void startMp3Recorder() {

state = RecordState.RECORDING;

notifyState();

try {

audioRecord.startRecording();

short[] byteBuffer = new short[bufferSize];

while (state == RecordState.RECORDING) {

int end = audioRecord.read(byteBuffer, 0, byteBuffer.length);

if (mp3EncodeThread != null) {

mp3EncodeThread.addChangeBuffer(new Mp3EncodeThread.ChangeBuffer(byteBuffer, end));

}

notifyData(ByteUtils.toBytes(byteBuffer));

}

audioRecord.stop();

} catch (Exception e) {

Logger.e(e, TAG, e.getMessage());

notifyError("录音失败");

}

if (state != RecordState.PAUSE) {

state = RecordState.IDLE;

notifyState();

if (mp3EncodeThread != null) {

mp3EncodeThread.stopSafe(new Mp3EncodeThread.EncordFinishListener() {

@Override

public void onFinish() {

notifyFinish();

}

});

} else {

notifyFinish();

}

} else {

Logger.d(TAG, "暂停");

}

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值