美文网首页Android开发Android开发Android技术知识
Android音频开发(3):使用AudioRecord实现录音

Android音频开发(3):使用AudioRecord实现录音

作者: android_赵乐玮 | 来源:发表于2018-08-09 17:25 被阅读12次

    Android 音频开发 目录

    1. Android音频开发(1):音频相关知识
    2. Android音频开发(2):使用AudioRecord录制pcm格式音频
    3. Android音频开发(3):使用AudioRecord实现录音的暂停和恢复
    4. Android音频开发(4):PCM转WAV格式音频
    5. Android音频开发(5):Mp3的录制 - 编译Lame源码
    6. Android音频开发(6):Mp3的录制 - 使用Lame实时录制MP3格式音频

    项目地址

    https://github.com/zhaolewei/ZlwAudioRecorder


    上一篇主要写了AudioRecord实现音频录制的开始和停止,AudioRecord并没有暂停和恢复播放功能的API,所以需要手动实现

    一、解决办法

    思路很简单,现在可以实现音频的文件录制和停止,并生成pcm文件,那么暂停时将这次文件先保存下来,恢复播放后开始新一轮的录制,那么最后会生成多个pcm音频,再将这些pcm文件进行合并,这样就实现了暂停/恢复的功能了。

    二、实现

    • 实现的重点在于如何控制录音的状态
    
    /**
     * @author zhaolewei on 2018/7/10.
     */
    public class RecordHelper {
        private volatile RecordState state = RecordState.IDLE;
        private AudioRecordThread audioRecordThread;
    
        private File recordFile = null;
        private File tmpFile = null;
        private List<File> files = new ArrayList<>();
    
        public void start(String filePath, RecordConfig config) {
            this.currentConfig = config;
            if (state != RecordState.IDLE) {
                Logger.e(TAG, "状态异常当前状态: %s", state.name());
                return;
            }
            recordFile = new File(filePath);
            String tempFilePath = getTempFilePath();
            Logger.i(TAG, "tmpPCM File: %s", tempFilePath);
            tmpFile = new File(tempFilePath);
            audioRecordThread = new AudioRecordThread();
            audioRecordThread.start();
        }
    
        public void stop() {
            if (state == RecordState.IDLE) {
                Logger.e(TAG, "状态异常当前状态: %s", state.name());
                return;
            }
    
            //若在暂停中直接停止,则直接合并文件即可
            if (state == RecordState.PAUSE) {
                makeFile();
                state = RecordState.IDLE;
            } else {
                state = RecordState.STOP;
            }
        }
    
        public void pause() {
            if (state != RecordState.RECORDING) {
                Logger.e(TAG, "状态异常当前状态: %s", state.name());
                return;
            }
            state = RecordState.PAUSE;
        }
    
        public void resume() {
            if (state != RecordState.PAUSE) {
                Logger.e(TAG, "状态异常当前状态: %s", state.name());
                return;
            }
            String tempFilePath = getTempFilePath();
            Logger.i(TAG, "tmpPCM File: %s", tempFilePath);
            tmpFile = new File(tempFilePath);
            audioRecordThread = new AudioRecordThread();
            audioRecordThread.start();
        }
    
        private class AudioRecordThread extends Thread {
            private AudioRecord audioRecord;
            private int bufferSize;
    
            AudioRecordThread() {
                bufferSize = AudioRecord.getMinBufferSize(currentConfig.getFrequency(),
                        currentConfig.getChannel(), currentConfig.getEncoding()) * RECORD_AUDIO_BUFFER_TIMES;
                audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, currentConfig.getFrequency(),
                        currentConfig.getChannel(), currentConfig.getEncoding(), bufferSize);
            }
    
            @Override
            public void run() {
                super.run();
                state = RecordState.RECORDING;
                notifyState();
                Logger.d(TAG, "开始录制");
                FileOutputStream fos = null;
                try {
                    fos = new FileOutputStream(tmpFile);
                    audioRecord.startRecording();
                    byte[] byteBuffer = new byte[bufferSize];
    
                    while (state == RecordState.RECORDING) {
                            int end = audioRecord.read(byteBuffer, 0, byteBuffer.length);
                        fos.write(byteBuffer, 0, end);
                        fos.flush();
                    }
                    audioRecord.stop();
                    //1. 将本次录音的文件暂存下来,用于合并
                    files.add(tmpFile);
                    //2. 再此判断终止循环的状态是暂停还是停止,并做相应处理
                    if (state == RecordState.STOP) {
                        makeFile();
                    } else {
                        Logger.i(TAG, "暂停!");
                    }
                } catch (Exception e) {
                    Logger.e(e, TAG, e.getMessage());
                } finally {
                    try {
                        if (fos != null) {
                            fos.close();
                        }
                    } catch (IOException e) {
                        Logger.e(e, TAG, e.getMessage());
                    }
                }
                if (state != RecordState.PAUSE) {
                    state = RecordState.IDLE;
                    notifyState();
                    Logger.d(TAG, "录音结束");
                }
            }
        }
    
        private void makeFile() {
            //合并文件
            boolean mergeSuccess = mergePcmFiles(recordFile, files);
    
            //TODO:转换wav
            Logger.i(TAG, "录音完成! path: %s ; 大小:%s", recordFile.getAbsoluteFile(), recordFile.length());
        }
    
        /**
         * 合并Pcm文件
         *
         * @param recordFile 输出文件
         * @param files      多个文件源
         * @return 是否成功
         */
        private boolean mergePcmFiles(File recordFile, List<File> files) {
            if (recordFile == null || files == null || files.size() <= 0) {
                return false;
            }
    
            FileOutputStream fos = null;
            BufferedOutputStream outputStream = null;
            byte[] buffer = new byte[1024];
            try {
                fos = new FileOutputStream(recordFile);
                outputStream = new BufferedOutputStream(fos);
    
                for (int i = 0; i < files.size(); i++) {
                    BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(files.get(i)));
                    int readCount;
                    while ((readCount = inputStream.read(buffer)) > 0) {
                        outputStream.write(buffer, 0, readCount);
                    }
                    inputStream.close();
                }
            } catch (Exception e) {
                Logger.e(e, TAG, e.getMessage());
                return false;
            } finally {
                try {
                    if (fos != null) {
                        fos.close();
                    }
                    if (outputStream != null) {
                        outputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            //3. 合并后记得删除缓存文件并清除list
            for (int i = 0; i < files.size(); i++) {
                files.get(i).delete();
            }
            files.clear();
            return true;
        }
    
    }
    
    

    其他

    • 在此后如若需要添加录音状态回调,记得使用Handler做好线程切换。

    相关文章

      网友评论

      • flynnny:用这个, 多段录音 合成后播放,当初暂停的位置 ,有明显的爆破音吧?

      本文标题:Android音频开发(3):使用AudioRecord实现录音

      本文链接:https://www.haomeiwen.com/subject/raycbftx.html