前言

总听前辈们说,写博客是利人利己的好事,这总好事,必然要试试。但是头一次写博客,真不知道如何下手,就当练手了。

因为最近项目当中用到了录音的功能,所以就Android 录音来试试。

Android 小白  ,大神请多指教。

参考链接

https://blog.csdn.net/hesong1120/article/details/79043482

https://www.cnblogs.com/MMLoveMeMM/articles/3444718.html

https://www.jianshu.com/p/90c77197f1d4

https://www.jianshu.com/p/a72deab95b4c

简介

Android提供了两个API用于实现录音功能

一个是AudioRecord   另一个是MedioRecorder

一 AudioRecord

AudioRecord 输出的是PCM语音数据,是无法直接播放的,必须进行编码和压缩才能够播放。可以将PCM语音数据前拼接头文件,转为wav格式进行播放。

二 MedioRecorder

其实是对audioRecord的封装,集成了录音,编码,压缩,获取到的语音数据转换为文件可以直接进行播放

三 WAV和PCM的联系

Android手机要进行音频编辑操作(比如裁剪,插入,合成等),通常都是需要将音频文件解码为WAV格式的音频文件或者PCM文件来进行拼接处理。

PCM(Pulse Code Modulation—-脉码调制录音),PCM录音就是将声音等模拟信号变成数字信号,学过模拟电路和数字电路的人应该比较懂这个,其实就是一个没有压缩的编码方式。

WAV格式是微软公司开发的一种声音文件格式,也叫波形声音文件,是最早的数字音频格式,支持许多压缩算法,支持多种音频位数、采样频率和声道。

AudioRecord实现

audioRecord方式录取音频,获取的时PCM编码的音频流,需要我们在线程中循环的获取音频数据

我们首先定义一部分参数  因为我们项目中使用了科大讯飞的语音识别,所以按照要求我们使用16000的采样率,和单声道

下面上代码

/** * 音频采样率 */public static int SAMPLE_RATE = 16000;/** * 单声道 */public final static int CHANNEL = AudioFormat.CHANNEL_IN_MONO;/** * 16比特 */public final static int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;/** * 音频录制实例 */protected AudioRecord audioRecord;/** * 录制线程 */private Thread recordThread;/** * 输出的文件路径 */private String pcmPath;private String wavPath;/** * 缓冲区大小 */private int bufferSize = 0;/** * 是否正在录制 */private boolean isRecording = false;
/** * 构造方法传递路径 * * @param context */public AudioRecordRecorder(Context context) {    pcmPath = AudioRecordFilePath.getRawFilePath(context);    wavPath = AudioRecordFilePath.getWavFilePath(context);}/** * 初始化操作 **/public void initRecorder() {    if (null != audioRecord) {        audioRecord.release();    }    try {        // 获得缓冲区字节大小        bufferSize = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL, AUDIO_FORMAT) * 10;        //实例化录制实例        audioRecord = new AudioRecord(AUDIO_SOURCE, SAMPLE_RATE, CHANNEL, AUDIO_FORMAT, bufferSize);    } catch (Exception e) {        e.printStackTrace();    }}

我这里将缓冲区设置的比较大,是为了防止最终读取的数据有失真的情况

 

/** * 开始录制   **/public int recordStart() {    if (isRecording) {        return RECORD_STATE.STATE_RECORDING;    } else if (audioRecord != null && audioRecord.getState() == AudioRecord.STATE_INITIALIZED) {        try {            recordThread = new Thread(new AudioRecordRunnable());            recordThread.start();            return RECORD_STATE.STATE_SUCCESS;        } catch (Exception e) {            e.printStackTrace();        }    }    return RECORD_STATE.STATE_ERROR;}开始录制,我开启了一个线程  目的时为了循环读取数据
/** * 读取线程 */private class AudioRecordRunnable implements Runnable {    @Override    public void run() {        audioRecord.startRecording();        isRecording = true;        writeDateToFile();        copyWaveFile(pcmPath, wavPath);    }}
/** * 将录取的音频写入文件  此时的音频并不能播放 */private void writeDateToFile() {    FileOutputStream outputStream = null;    BufferedOutputStream bufferedOutputStream = null;    DataOutputStream dataOutputStream = null;    try {        if (!TextUtils.isEmpty(pcmPath)) {            outputStream = new FileOutputStream(pcmPath);            bufferedOutputStream = new BufferedOutputStream(outputStream);            dataOutputStream = new DataOutputStream(bufferedOutputStream);        }        // 此处其实可以使用byte数组来实现 ,这样也能避免为大小端的处理,而我时为了获取声音分贝值回调,偷个懒,使用了short数组,这个看个人        short[] audioBuffer = new short[bufferSize];        while (isRecording && audioRecord != null) {            audioSize = audioRecord.read(audioBuffer, 0, bufferSize);            if (audioSize > 0) {                //写入文件                if (outputStream != null) {                    for (int i = 0; i < audioSize; i++) {                        //此处一定要要进行一下大小端处理  不然音频会失真                        dataOutputStream.writeShort(Short.reverseBytes(audioBuffer[i]));                    }                }                //音量大小回调                getMicState(audioBuffer);            }        }    } catch (FileNotFoundException e) {        e.printStackTrace();    } catch (IOException e) {        e.printStackTrace();    } finally {        close(outputStream);        outputStream = null;    }}
/** * 这里得到可播放的音频文件 * * @param inFilename * @param outFilename */private void copyWaveFile(String inFilename, String outFilename) {    FileInputStream in;    FileOutputStream out;    long totalAudioLen;    long totalDataLen;    long longSampleRate = SAMPLE_RATE;    int channels = 1;    long byteRate = 16 * SAMPLE_RATE * channels / 8;    byte[] data = new byte[bufferSize];    try {        in = new FileInputStream(inFilename);        out = new FileOutputStream(outFilename);        totalAudioLen = in.getChannel().size();        totalDataLen = totalAudioLen + 36;        WriteWaveFileHeader(out, totalAudioLen, totalDataLen,                longSampleRate, channels, byteRate);        while (in.read(data) != -1) {            out.write(data);            out.flush();        }        in.close();        out.close();    } catch (FileNotFoundException e) {        e.printStackTrace();    } catch (IOException e) {        e.printStackTrace();    } finally {        uiPlayer.audioRecordFinish();        AudioRecordFilePath.deleteFile(pcmPath);        long duration = getWavLength(wavPath);        uiPlayer.getWavFilePath(wavPath);        uiPlayer.getWavFileDuration(duration);    }}

 

/** * 这里提供一个头信息。插入这些信息就可以得到可以播放的文件。 * 这个头文件我时上网上搜的一个,如何大家使用不好使,可以自行百度 * @param out * @param totalAudioLen  音频文件长度 * @param totalDataLen   加入头文件以后数据长度 * @param longSampleRate 采样率 * @param channels       通道数 * @param byteRate       位率 * @throws IOException */private void WriteWaveFileHeader(FileOutputStream out, long totalAudioLen,                                 long totalDataLen, long longSampleRate, int channels, long byteRate)        throws IOException {    byte[] header = new byte[44];    header[0] = 'R'; // RIFF/WAVE header    header[1] = 'I';    header[2] = 'F';    header[3] = 'F';    header[4] = (byte) (totalDataLen & 0xff);    header[5] = (byte) ((totalDataLen >> 8) & 0xff);    header[6] = (byte) ((totalDataLen >> 16) & 0xff);    header[7] = (byte) ((totalDataLen >> 24) & 0xff);    header[8] = 'W';    header[9] = 'A';    header[10] = 'V';    header[11] = 'E';    header[12] = 'f'; // 'fmt ' chunk    header[13] = 'm';    header[14] = 't';    header[15] = ' ';    header[16] = 16; // 4 bytes: size of 'fmt ' chunk    header[17] = 0;    header[18] = 0;    header[19] = 0;    header[20] = 1; // format = 1    header[21] = 0;    header[22] = (byte) channels;    header[23] = 0;    header[24] = (byte) (longSampleRate & 0xff);    header[25] = (byte) ((longSampleRate >> 8) & 0xff);    header[26] = (byte) ((longSampleRate >> 16) & 0xff);    header[27] = (byte) ((longSampleRate >> 24) & 0xff);    header[28] = (byte) (byteRate & 0xff);    header[29] = (byte) ((byteRate >> 8) & 0xff);    header[30] = (byte) ((byteRate >> 16) & 0xff);    header[31] = (byte) ((byteRate >> 24) & 0xff);    header[32] = (byte) (2 * 16 / 8); // block align    header[33] = 0;    header[34] = 16; // bits per sample    header[35] = 0;    header[36] = 'd';    header[37] = 'a';    header[38] = 't';    header[39] = 'a';    header[40] = (byte) (totalAudioLen & 0xff);    header[41] = (byte) ((totalAudioLen >> 8) & 0xff);    header[42] = (byte) ((totalAudioLen >> 16) & 0xff);    header[43] = (byte) ((totalAudioLen >> 24) & 0xff);    out.write(header, 0, 44);}
@Overridepublic void recordStop() {    try {        if (audioRecord != null) {            isRecording = false;            try {                if (recordThread != null) {                    recordThread.join();                    recordThread = null;                }            } catch (InterruptedException e) {                e.printStackTrace();            }            //释放资源            recordRelease();        }    } catch (Exception e) {        e.printStackTrace();        uiPlayer.audioRecordFail();    }}/** * 释放资源 */private void recordRelease() {    if (audioRecord != null) {        if (audioRecord.getState() == AudioRecord.STATE_INITIALIZED) {            audioRecord.stop();        }        audioRecord.release();        audioRecord = null;    }}
/** * 声音长度 */private int audioSize;/** * 获取音量分贝值 */private void getMicState(short[] audioVolume) {    if (audioSize > 0) {        long v = 0;        for (int i = 0; i < audioVolume.length; i++) {            v += audioVolume[i] * audioVolume[i];        }        // 平方和除以数据总长度,得到音量大小。        double mean = v / (double) audioSize;        double volume = 10 * Math.log10(mean);}

完结

就先写到这里啦,真的是第一次,如果错误,请大家多多指正。也希望能给刚刚使用的道友一些帮助。

这里面参考了很多前辈的文章,就不一一列举了。很感谢前辈们替我们负重前行。

更多相关文章

  1. mybatisplus的坑 insert标签insert into select无参数问题的解决
  2. python起点网月票榜字体反爬案例
  3. Android笔试的一部分知识点
  4. Android的Handler总结(1)
  5. Android(安卓)流媒体系列(一)
  6. Android(安卓)与Weex的交互
  7. Android的多媒体数据库
  8. Android(安卓)的用户层 uevent处理机制
  9. android私有文件资源文件的读取

随机推荐

  1. Android Studio 2.2 NDK开发环境搭建
  2. linux insufficient permissions for dev
  3. Android 进程保活
  4. Android+Jquery Mobile学习系列-目录
  5. Android(安卓)设备蓝牙连接扫描枪获取扫
  6. 前Sun开发人员为Android,iOS等其他移动平
  7. Android 上传头像(文件)到服务器
  8. android 实现QQ好友列表(扩展listview:Exp
  9. Android 广播接收者(具体例子)
  10. android 消息机制(2)