天天看点

Android AudioRecord 和MediaRecord 音频开发

Android AudioRecord 和MediaRecord 音频开发

临时需求做了一个录音上传功能,如上图,但要求上传16k采样率以及16Kbps比特率的wav音频。

先使用的是MediaRecord :

1.开始录制按钮监听事件

record.setOnClickListener(new View.OnClickListener() {
          @Override
        public void onClick(View v) {
            if (!isStart) {
                //开始录制
                startrecord();
                record.setText("停止录制");
                isStart = true;
            } else {
                stopRecord();
                record.setText("开始录制");
                isStart = false;
            }
        }
    });
           

2.开始录音和停止录音方法

String filename;
File soundFile;
private void startrecord() {
    if (mr == null) {      
        File dir = new File(Environment.getExternalStorageDirectory(), "sounds");
        if (!dir.exists()) {
            dir.mkdirs();
        }
         filename = System.currentTimeMillis() + ".wav";
        soundFile = new File(dir, filename);
       
        if (!soundFile.exists()) {
            try {
                soundFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        mr = new MediaRecorder();
        mr.setAudioSource(MediaRecorder.AudioSource.MIC);  //音频输入源
        mr.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); //录音文件保存的格式  AAC_ADTS AMR_NB
        mr.setOutputFile(soundFile.getAbsolutePath());
        mr.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);//  文件输出格式  default,AAC,HE_AAC,AAC_ELD,AMR_NB,AMR_WB,VORBIS
        mr.setAudioSamplingRate(16000);  //采样率16K
        mr.setAudioChannels(1);//比特率16bit
        mr.setAudioEncodingBitRate(AudioFormat.ENCODING_PCM_16BIT);//音频编码比特率
        try {
            mr.prepare();
            mr.start();  //开始录制
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

//停止录制,资源释放
private void stopRecord() {
    if (mr != null) {
        try {
            mr.stop();
        } catch (IllegalStateException e) {
            // TODO 如果当前java状态和jni里面的状态不一致,
            e.printStackTrace();
            mr = null;
            mr = new MediaRecorder();
        }
        mr.release();
        mr = null;
    }
}
           

传入后台发现一个问题 MediaRecord其实不支持wav格式,以及MediaRecord设置任何编码比特率都不起作用,所以改用AudioRecord,(当然如果没有那么多要求,还是用MediaRecord方便)。

下面介绍AudioRecord

1.audiorecord录音的工具类奉上

public class AudioUtil {

private AudioRecord recorder;
//录音源
private static int audioSource = MediaRecorder.AudioSource.MIC;
//录音的采样频率  16k采样率
private static int audioRate = 16000;
//录音的声道,单声道
private static int audioChannel = AudioFormat.CHANNEL_IN_MONO;
//量化的深度  16bps比特率
private static int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
//缓存的大小
private static int bufferSize = AudioRecord.getMinBufferSize(audioRate, audioChannel, audioFormat);
//记录播放状态
private boolean isRecording = false;
//数字信号数组
private byte[] noteArray;
//PCM文件
private File pcmFile;
//WAV文件
private File wavFile;
//文件输出流
private OutputStream os;
//文件根目录
private String basePath = Environment.getExternalStorageDirectory().getAbsolutePath();
 //rootFolder 子文件夹
public AudioUtil(String rootFolder) {
    basePath = basePath + "/" + rootFolder;
    createFile();//创建文件
    recorder = new AudioRecord(audioSource, audioRate, audioChannel, audioFormat, bufferSize);
}
//调用时 公用
public String getBasePath() {
    return basePath;
}

//读取录音数字数据线程
class WriteThread implements Runnable {
    public void run() {
        writeData();
    }
}

//开始录音
public void startRecord() {
    isRecording = true;
    recorder.startRecording();
}

//停止录音
public void stopRecord() {
    isRecording = false;
    recorder.stop();
}

//将数据写入文件夹,文件的写入没有做优化
public void writeData() {
    noteArray = new byte[bufferSize];
    //建立文件输出流
    try {
        os = new BufferedOutputStream(new FileOutputStream(pcmFile));
    } catch (IOException e) {

    }
    while (isRecording == true) {
        int recordSize = recorder.read(noteArray, 0, bufferSize);
        if (recordSize > 0) {
            try {
                os.write(noteArray);
            } catch (IOException e) {

            }
        }
    }
    if (os != null) {
        try {
            os.close();
        } catch (IOException e) {

        }
    }
}

// 这里得到可播放的音频文件
public void convertWaveFile(String outFileName) {
    FileInputStream in = null;
    FileOutputStream out = null;
    long totalAudioLen = 0;
    long totalDataLen = totalAudioLen + 36;
    long longSampleRate = AudioUtil.audioRate;
    int channels = 1;
    long byteRate = 16 * AudioUtil.audioRate * channels / 8;
    byte[] data = new byte[bufferSize];
    try {
        in = new FileInputStream(pcmFile);
        out = new FileOutputStream(basePath + "/" + outFileName);
        totalAudioLen = in.getChannel().size();
        //由于不包括RIFF和WAV
        totalDataLen = totalAudioLen + 36;
        WriteWaveFileHeader(out, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate);
        while (in.read(data) != -1) {
            out.write(data);
        }
    } catch (FileNotFoundException e) {
        Log.d("Regist", "convertWaveFile: " + e.getMessage());
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
        Log.d("Regist", "IOException: " + e.getMessage());
    }finally {
        try {
            if (in!=null){
                in.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (out!=null){
                out.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(pcmFile.isFile() && pcmFile.exists()) {
            pcmFile.delete();
        }
    }


}

/* 任何一种文件在头部添加相应的头文件才能够确定的表示这种文件的格式,wave是RIFF文件结构,
每一部分为一个chunk,其中有RIFF WAVE chunk, FMT Chunk,Fact chunk,Data chunk,其中Fact chunk是可以选择的, */
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
    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';//WAVE
    header[9] = 'A';
    header[10] = 'V';
    header[11] = 'E';
    //FMT Chunk
    header[12] = 'f'; // 'fmt '
    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;
    //编码方式 10H为PCM编码格式
    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);
    //音频数据传送速率,采样率*通道数*采样深度/8
    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) (1 * 16 / 8);
    header[33] = 0;
    //每个样本的数据位数
    header[34] = 16;
    header[35] = 0;
    //Data chunk
    header[36] = 'd';//data
    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);
}

//创建文件夹,首先创建目录,然后创建对应的文件
public void createFile() {
    File baseFile = new File(basePath);
    if (!baseFile.exists())
        baseFile.mkdirs();
    pcmFile = new File(basePath + "/tmp_" + UUID.randomUUID().toString().replaceAll("-", "") + ".pcm");

    if (pcmFile.exists()) {
        pcmFile.delete();
    }
    try {
        pcmFile.createNewFile();
    } catch (IOException e) {
    }
}

//记录数据
public void recordData() {
    new Thread(new WriteThread()).start();
}
           

}

2. 调用如下

record.setOnClickListener(new View.OnClickListener() {

@Override
        public void onClick(View v) {

            if (!isStart) {
                //开始录制
                util = new AudioUtil("audio");
                filename = System.currentTimeMillis() + ".wav";
                util.startRecord();
                util.recordData();
                record.setText("停止录制");
                isStart = true;
            } else {
                util.stopRecord();
                util.convertWaveFile(filename);
                record.setText("开始录制");
                isStart = false;
            }
        }
    });
           

这里用AudioRecord录制pcm音频,加上wav头即可得到.wav的音频文件,也可以随集设置AudioFormat.ENCODING_PCM_16BIT;比特率等,最后完美解决我的问题,嘻嘻嘻,以此记录今日收获。