重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
之前项目需要实现MP3的录音,于是使用上了Lame这个库。这次做一个demo,使用AndroidStudio+Cmake+NDK进行开发。利用Android SDK提供的AndroidRecorder进行录音,得到PCM数据,并使用jni调用Lame这个C库将PCM数据转换为MP3文件。并使用MediaPlayer对录音的MP3文件进行播放。另外此次的按键是仿微信的语音按键,按下录音,松开结束,若中途上滑松开即取消
创新互联坚持“要么做到,要么别承诺”的工作理念,服务领域包括:网站制作、成都网站设计、企业官网、英文网站、手机端网站、网站推广等服务,满足客户于互联网时代的涪城网站设计、移动媒体设计的需求,帮助企业找到有效的互联网解决方案。努力成为您成熟可靠的网络建设合作伙伴!效果如下:
项目地址: LameMp3ForAndroid_jb51.rar
一、主要类的介绍
二、录制的流程
三、主要的实现代码
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); } }