日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

java libmp3lame_利用libmp3lame实现在Android上录音MP3文件示例

發布時間:2023/12/16 Android 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java libmp3lame_利用libmp3lame实现在Android上录音MP3文件示例 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

之前項目需要實現MP3的錄音,于是使用上了Lame這個庫。這次做一個demo,使用AndroidStudio+Cmake+NDK進行開發。利用Android SDK提供的AndroidRecorder進行錄音,得到PCM數據,并使用jni調用Lame這個C庫將PCM數據轉換為MP3文件。并使用MediaPlayer對錄音的MP3文件進行播放。另外此次的按鍵是仿微信的語音按鍵,按下錄音,松開結束,若中途上滑松開即取消

效果如下:

一、主要類的介紹

Mp3Recorder—— 是負責調用AudioRecorder進行錄音的類

SimpleLame——是負責將MP3Recorder錄制出的PCM數據轉換成MP3文件

DataEncodeThread——是負責執行PCM轉MP3的線程

LameMp3Manager——是對Mp3Recorder的多一次封裝,增加了取消后刪除之前錄制的數據的邏輯

MediaPlayerUtil——是對系統的MediaPlayer進行簡單的封裝,使其只需要三步就可以播放音頻文件

MediaRecorderButton ——是一個仿微信錄音按鍵的控件,按下錄制,松開結束,錄制時上滑則取消錄制

二、錄制的流程

Mp3Recorder調用startRecording()開始錄制并初始化DataEncoderThread線程,并定期將錄制的PCM數據,傳入DataEncoderThread中。

在DataEncoderThread里,SimpleLame將Mp3Recorder傳入的PCM數據轉換成MP3格式并寫入文件,其中SimpleLame通過jni對Lame庫進行調用

Mp3Recorder調用stopRecording()停止錄制,并通知DataEncoderThread線程錄制結束,DataEncoderThread將剩余的數據轉換完畢。

三、主要的實現代碼

Mp3Recorder

public class Mp3Recorder {

static {

System.loadLibrary("lamemp3");

}

//默認采樣率

private static final int DEFAULT_SAMPLING_RATE = 44100;

//轉換周期,錄音每滿160幀,進行一次轉換

private static final int FRAME_COUNT = 160;

//輸出MP3的碼率

private static final int BIT_RATE = 32;

//根據資料假定的最大值。 實測時有時超過此值。

private static final int MAX_VOLUME = 2000;

private AudioRecord audioRecord = null;

private int bufferSize;

private File mp3File;

private int mVolume;

private short[] mPCMBuffer;

private FileOutputStream os = null;

private DataEncodeThread encodeThread;

private int samplingRate;

private int channelConfig;

private PCMFormat audioFormat;

private boolean isRecording = false;

private ExecutorService executor = Executors.newFixedThreadPool(1);

private OnFinishListener finishListener;

public interface OnFinishListener {

void onFinish(String mp3SavePath);

}

public Mp3Recorder(int samplingRate, int channelConfig, PCMFormat audioFormat) {

this.samplingRate = samplingRate;

this.channelConfig = channelConfig;

this.audioFormat = audioFormat;

}

public Mp3Recorder() {

this(DEFAULT_SAMPLING_RATE, AudioFormat.CHANNEL_IN_MONO, PCMFormat.PCM_16BIT);

}

public void startRecording(File mp3Save) throws IOException {

if (isRecording) return;

this.mp3File = mp3Save;

if (audioRecord == null) {

initAudioRecorder();

}

audioRecord.startRecording();

Runnable runnable = new Runnable() {

@Override

public void run() {

isRecording = true;

//循環的從AudioRecord獲取錄音的PCM數據

while (isRecording) {

int readSize = audioRecord.read(mPCMBuffer, 0, bufferSize);

if (readSize > 0) {

//待轉換的PCM數據放到轉換線程中

encodeThread.addChangeBuffer(mPCMBuffer,readSize);

calculateRealVolume(mPCMBuffer, readSize);

}

}

// 錄音完畢,釋放AudioRecord的資源

try {

audioRecord.stop();

audioRecord.release();

audioRecord = null;

// 錄音完畢,通知轉換線程停止,并等待直到其轉換完畢

Message msg = Message.obtain(encodeThread.getHandler(), DataEncodeThread.PROCESS_STOP);

msg.sendToTarget();

encodeThread.join();

//轉換完畢后回調監聽

if(finishListener != null) finishListener.onFinish(mp3File.getPath());

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

if (os != null) {

try {

os.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

};

executor.execute(runnable);

}

public void stopRecording() throws IOException {

isRecording = false;

}

//計算音量大小

private void calculateRealVolume(short[] buffer, int readSize) {

double sum = 0;

for (int i = 0; i < readSize; i++) {

sum += buffer[i] * buffer[i];

}

if (readSize > 0) {

double amplitude = sum / readSize;

mVolume = (int) Math.sqrt(amplitude);

}

}

public int getVolume(){

if (mVolume >= MAX_VOLUME) {

return MAX_VOLUME;

}

return mVolume;

}

public int getMaxVolume(){

return MAX_VOLUME;

}

public void setFinishListener(OnFinishListener listener){

this.finishListener = listener;

}

private void initAudioRecorder() throws IOException {

int bytesPerFrame = audioFormat.getBytesPerFrame();

//計算緩沖區的大小,使其是設置周期幀數的整數倍,方便循環

int frameSize = AudioRecord.getMinBufferSize(samplingRate, channelConfig, audioFormat.getAudioFormat()) / bytesPerFrame;

if (frameSize % FRAME_COUNT != 0) {

frameSize = frameSize + (FRAME_COUNT - frameSize % FRAME_COUNT);

}

bufferSize = frameSize * bytesPerFrame;

audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, samplingRate, channelConfig, audioFormat.getAudioFormat(), bufferSize);

mPCMBuffer = new short[bufferSize];

SimpleLame.init(samplingRate, 1, samplingRate, BIT_RATE);

os = new FileOutputStream(mp3File);

// 創建轉碼的線程

encodeThread = new DataEncodeThread(os, bufferSize);

encodeThread.start();

//給AudioRecord設置刷新監聽,待錄音幀數每次達到FRAME_COUNT,就通知轉換線程轉換一次數據

audioRecord.setRecordPositionUpdateListener(encodeThread, encodeThread.getHandler());

audioRecord.setPositionNotificationPeriod(FRAME_COUNT);

}

}

DataEncodeThread

public class DataEncodeThread extends Thread implements AudioRecord.OnRecordPositionUpdateListener {

public static final int PROCESS_STOP = 1;

private StopHandler handler;

private byte[] mp3Buffer;

//用于存取待轉換的PCM數據

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

private FileOutputStream os;

private CountDownLatch handlerInitLatch = new CountDownLatch(1);

private static class StopHandler extends Handler {

WeakReference encodeThread;

public StopHandler(DataEncodeThread encodeThread) {

this.encodeThread = new WeakReference<>(encodeThread);

}

@Override

public void handleMessage(Message msg) {

if (msg.what == PROCESS_STOP) {

DataEncodeThread threadRef = encodeThread.get();

//錄音停止后,將剩余的PCM數據轉換完畢

for (;threadRef.processData() > 0;);

removeCallbacksAndMessages(null);

threadRef.flushAndRelease();

getLooper().quit();

}

super.handleMessage(msg);

}

}

public DataEncodeThread(FileOutputStream os, int bufferSize) {

this.os = os;

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

}

@Override

public void run() {

Looper.prepare();

handler = new StopHandler(this);

handlerInitLatch.countDown();

Looper.loop();

}

public Handler getHandler() {

try {

handlerInitLatch.await();

} catch (InterruptedException e) {

e.printStackTrace();

Log.e(TAG, "Error when waiting handle to init");

}

return handler;

}

@Override

public void onMarkerReached(AudioRecord recorder) {

// Do nothing

}

@Override

public void onPeriodicNotification(AudioRecord recorder) {

//由AudioRecord進行回調,滿足幀數,通知數據轉換

processData();

}

//從緩存區ChangeBuffers里獲取待轉換的PCM數據,轉換為MP3數據,并寫入文件

private int processData() {

if(mChangeBuffers != null && mChangeBuffers.size() > 0) {

ChangeBuffer changeBuffer = mChangeBuffers.remove(0);

short[] buffer = changeBuffer.getData();

int readSize = changeBuffer.getReadSize();

Log.d(TAG, "Read size: " + readSize);

if (readSize > 0) {

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

if (encodedSize < 0) {

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

}

try {

os.write(mp3Buffer, 0, encodedSize);

} catch (IOException e) {

e.printStackTrace();

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

}

return readSize;

}

}

return 0;

}

private void flushAndRelease() {

final int flushResult = SimpleLame.flush(mp3Buffer);

if (flushResult > 0) {

try {

os.write(mp3Buffer, 0, flushResult);

} catch (final IOException e) {

e.printStackTrace();

}

}

}

public void addChangeBuffer(short[] rawData, int readSize){

mChangeBuffers.add(new ChangeBuffer(rawData, readSize));

}

private class ChangeBuffer{

private short[] rawData;

private int readSize;

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

this.rawData = rawData.clone();

this.readSize = readSize;

}

public short[] getData(){

return rawData;

}

public int getReadSize(){

return readSize;

}

}

}

SimpleLame 主要的邏輯是通過jni調用Lame庫

public class SimpleLame {

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);

}

}

#include

#include "SimpleLame.h"

#include "lamemp3/lame.h"

static lame_global_flags *glf = NULL;

void Java_com_clam314_lame_SimpleLame_close(JNIEnv *env, jclass type){

lame_close(glf);

glf = NULL;

}

jint Java_com_clam314_lame_SimpleLame_encode(JNIEnv *env, jclass type, jshortArray buffer_l_,

jshortArray buffer_r_, jint samples, jbyteArray mp3buf_) {

jshort *buffer_l = env->GetShortArrayElements(buffer_l_, NULL);

jshort *buffer_r = env->GetShortArrayElements(buffer_r_, NULL);

jbyte *mp3buf = env->GetByteArrayElements(mp3buf_, NULL);

const jsize mp3buf_size = env->GetArrayLength(mp3buf_);

int result =lame_encode_buffer(glf, buffer_l, buffer_r, samples, (u_char*)mp3buf, mp3buf_size);

env->ReleaseShortArrayElements(buffer_l_, buffer_l, 0);

env->ReleaseShortArrayElements(buffer_r_, buffer_r, 0);

env->ReleaseByteArrayElements(mp3buf_, mp3buf, 0);

return result;

}

jint Java_com_clam314_lame_SimpleLame_flush(JNIEnv *env, jclass type, jbyteArray mp3buf_) {

jbyte *mp3buf = env->GetByteArrayElements(mp3buf_, NULL);

const jsize mp3buf_size = env->GetArrayLength(mp3buf_);

int result = lame_encode_flush(glf, (u_char*)mp3buf, mp3buf_size);

env->ReleaseByteArrayElements(mp3buf_, mp3buf, 0);

return result;

}

void Java_com_clam314_lame_SimpleLame_init__IIIII(JNIEnv *env, jclass type, 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);

}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

總結

以上是生活随笔為你收集整理的java libmp3lame_利用libmp3lame实现在Android上录音MP3文件示例的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。