简单实现Android AudioReccord录音方式

前言

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

因为最近项目当中用到了录音的功能,所以就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);
}
@Override
public 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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值