马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
前言
音视频这块,起首是要先收罗音频。今天我们就来深入探讨一下 Android 音频收罗的两大类型:Mic 音频收罗和系统音频收罗。
Mic音频收罗
Android提供了两个API用于实现录音功能:android.media.AudioRecord、android.media.MediaRecorder。
AudioRecord和MediaRecorder两种都可以录制音频,MediaRecorder已实现大量的封装,操作起来更加简单,而AudioRecord使用起来更加灵活,能实现更多的功能。
AudioRecord
主要是实现边录边播(AudioRecord+AudioTrack)以及对音频的及时处置处罚(如会说话的汤姆猫、语音)
优点:语音的及时处置处罚,可以用代码实现各种音频的封装
缺点:输出是PCM语音数据,如果生存成音频文件,是不能够被播放器播放的,以是必须先写代码实现数据编码以及压缩
示例:
使用AudioRecord类录音,并实现WAV格式封装。录音20s,输出的音频文件大概为3.5M左(已写测试代码)
特点:
- 基于字节省录音;
- 可以实现语音的及时处置处罚,举行边录边播,对音频的及时处置处罚;
- AudioRecord是一个比较偏底层的API,它可以获取到一帧帧PCM数据,之后可以对这些数据举行处置处罚;
- 输出的是PCM的语音数据,如果生存成音频文件是不能被播放器播放的。要用到AudioTrack这个去举行处置处罚;
MediaRecorder
已经集成了录音、编码、压缩等,支持少量的录音音频格式,大概有.aac(API = 16) .amr .3gp
优点:大部分以及集成,直接调用相干接口即可,代码量小
缺点:无法及时处置处罚音频;输出的音频格式不是很多,例如没有输出mp3格式文件
示例:
使用MediaRecorder类录音,输出amr格式文件。录音20s,输出的音频文件大概为33K(已写测试代码)
特点:
- 基于文件录音;
- MediaRecorder 是基于 AudioRecorder 的 API(终极还是会创建AudioRecord用来与AudioFlinger举行交互) ,它可以直接将收罗到的音频数据转化为执行的编码格式,并生存;
- 已集成了录音,编码,压缩等,支持少量的音频格式文件;
- 封装度很高,操作简单;
音频格式比较
WAV格式:录音质量高,但是压缩率小,文件大。
AAC格式:相对于mp3,AAC格式的音质更佳,文件更小;有损压缩;一般苹果或者Android SDK4.1.2(API 16)及以上版本支持播放 。
AMR格式:压缩比比较大,但相对其他的压缩格式质量比较差,多用于人声,通话录音
mp3格式:使用MediaRecorder没有该视频格式输出。一些人的做法是使用AudioRecord录音,然后编码成wav格式,再转换成mp3格式。
测试代码
audioRecord 封装wav 格式。
- package com.example.audiorecordtest;
-
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
-
- import android.media.AudioFormat;
- import android.media.AudioRecord;
-
- public class AudioRecordFunc {
- // 缓冲区字节大小
- private int bufferSizeInBytes = 0;
-
- //AudioName裸音频数据文件 ,麦克风
- private String AudioName = "";
-
- //NewAudioName可播放的音频文件
- private String NewAudioName = "";
-
- private AudioRecord audioRecord;
- private boolean isRecord = false;// 设置正在录制的状态
-
-
- private static AudioRecordFunc mInstance;
-
- private AudioRecordFunc(){
-
- }
-
- public synchronized static AudioRecordFunc getInstance()
- {
- if(mInstance == null)
- mInstance = new AudioRecordFunc();
- return mInstance;
- }
-
- public int startRecordAndFile() {
- //判断是否有外部存储设备sdcard
- if(AudioFileFunc.isSdcardExit())
- {
- if(isRecord)
- {
- return ErrorCode.E_STATE_RECODING;
- }
- else
- {
- if(audioRecord == null)
- creatAudioRecord();
-
- audioRecord.startRecording();
- // 让录制状态为true
- isRecord = true;
- // 开启音频文件写入线程
- new Thread(new AudioRecordThread()).start();
-
- return ErrorCode.SUCCESS;
- }
-
- }
- else
- {
- return ErrorCode.E_NOSDCARD;
- }
-
- }
-
- public void stopRecordAndFile() {
- close();
- }
-
-
- public long getRecordFileSize(){
- return AudioFileFunc.getFileSize(NewAudioName);
- }
-
-
- private void close() {
- if (audioRecord != null) {
- System.out.println("stopRecord");
- isRecord = false;//停止文件写入
- audioRecord.stop();
- audioRecord.release();//释放资源
- audioRecord = null;
- }
- }
-
-
- private void creatAudioRecord() {
- // 获取音频文件路径
- AudioName = AudioFileFunc.getRawFilePath();
- NewAudioName = AudioFileFunc.getWavFilePath();
-
- // 获得缓冲区字节大小
- bufferSizeInBytes = AudioRecord.getMinBufferSize(AudioFileFunc.AUDIO_SAMPLE_RATE,
- AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
-
- // 创建AudioRecord对象
- audioRecord = new AudioRecord(AudioFileFunc.AUDIO_INPUT, AudioFileFunc.AUDIO_SAMPLE_RATE,
- AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, bufferSizeInBytes);
- }
-
-
- class AudioRecordThread implements Runnable {
- @Override
- public void run() {
- writeDateTOFile();//往文件中写入裸数据
- copyWaveFile(AudioName, NewAudioName);//给裸数据加上头文件
- }
- }
-
- /**
- * 这里将数据写入文件,但是并不能播放,因为AudioRecord获得的音频是原始的裸音频,
- * 如果需要播放就必须加入一些格式或者编码的头信息。但是这样的好处就是你可以对音频的 裸数据进行处理,比如你要做一个爱说话的TOM
- * 猫在这里就进行音频的处理,然后重新封装 所以说这样得到的音频比较容易做一些音频的处理。
- */
- private void writeDateTOFile() {
- // new一个byte数组用来存一些字节数据,大小为缓冲区大小
- byte[] audiodata = new byte[bufferSizeInBytes];
- FileOutputStream fos = null;
- int readsize = 0;
- try {
- File file = new File(AudioName);
- if (file.exists()) {
- file.delete();
- }
- fos = new FileOutputStream(file);// 建立一个可存取字节的文件
- } catch (Exception e) {
- e.printStackTrace();
- }
- while (isRecord == true) {
- readsize = audioRecord.read(audiodata, 0, bufferSizeInBytes);
- if (AudioRecord.ERROR_INVALID_OPERATION != readsize && fos!=null) {
- try {
- fos.write(audiodata);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- try {
- if(fos != null)
- fos.close();// 关闭写入流
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- // 这里得到可播放的音频文件
- private void copyWaveFile(String inFilename, String outFilename) {
- FileInputStream in = null;
- FileOutputStream out = null;
- long totalAudioLen = 0;
- long totalDataLen = totalAudioLen + 36;
- long longSampleRate = AudioFileFunc.AUDIO_SAMPLE_RATE;
- int channels = 2;
- long byteRate = 16 * AudioFileFunc.AUDIO_SAMPLE_RATE * channels / 8;
- byte[] data = new byte[bufferSizeInBytes];
- 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);
- }
- in.close();
- out.close();
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- /**
- * 这里提供一个头信息。插入这些信息就可以得到可以播放的文件。
- * 为我为啥插入这44个字节,这个还真没深入研究,不过你随便打开一个wav
- * 音频的文件,可以发现前面的头文件可以说基本一样哦。每种格式的文件都有
- * 自己特有的头文件。
- */
- 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);
- }
- }
复制代码
MediaRecorder录音,输出amr格式音频
- package com.example.audiorecordtest;
-
- import java.io.File;
- import java.io.IOException;
-
- import android.media.MediaRecorder;
-
- public class MediaRecordFunc {
- private boolean isRecord = false;
-
- private MediaRecorder mMediaRecorder;
- private MediaRecordFunc(){
- }
-
- private static MediaRecordFunc mInstance;
- public synchronized static MediaRecordFunc getInstance(){
- if(mInstance == null)
- mInstance = new MediaRecordFunc();
- return mInstance;
- }
-
- public int startRecordAndFile(){
- //判断是否有外部存储设备sdcard
- if(AudioFileFunc.isSdcardExit())
- {
- if(isRecord)
- {
- return ErrorCode.E_STATE_RECODING;
- }
- else
- {
- if(mMediaRecorder == null)
- createMediaRecord();
-
- try{
- mMediaRecorder.prepare();
- mMediaRecorder.start();
- // 让录制状态为true
- isRecord = true;
- return ErrorCode.SUCCESS;
- }catch(IOException ex){
- ex.printStackTrace();
- return ErrorCode.E_UNKOWN;
- }
- }
-
- }
- else
- {
- return ErrorCode.E_NOSDCARD;
- }
- }
-
-
- public void stopRecordAndFile(){
- close();
- }
-
- public long getRecordFileSize(){
- return AudioFileFunc.getFileSize(AudioFileFunc.getAMRFilePath());
- }
-
-
- private void createMediaRecord(){
- /* ①Initial:实例化MediaRecorder对象 */
- mMediaRecorder = new MediaRecorder();
-
- /* setAudioSource/setVedioSource*/
- mMediaRecorder.setAudioSource(AudioFileFunc.AUDIO_INPUT);//设置麦克风
-
- /* 设置输出文件的格式:THREE_GPP/MPEG-4/RAW_AMR/Default
- * THREE_GPP(3gp格式,H263视频/ARM音频编码)、MPEG-4、RAW_AMR(只支持音频且音频编码要求为AMR_NB)
- */
- mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
-
- /* 设置音频文件的编码:AAC/AMR_NB/AMR_MB/Default */
- mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
-
- /* 设置输出文件的路径 */
- File file = new File(AudioFileFunc.getAMRFilePath());
- if (file.exists()) {
- file.delete();
- }
- mMediaRecorder.setOutputFile(AudioFileFunc.getAMRFilePath());
- }
-
-
- private void close(){
- if (mMediaRecorder != null) {
- System.out.println("stopRecord");
- isRecord = false;
- mMediaRecorder.stop();
- mMediaRecorder.release();
- mMediaRecorder = null;
- }
- }
- }
复制代码
其他文件
- package com.example.audiorecordtest;
-
- import java.io.File;
-
- import android.media.MediaRecorder;
- import android.os.Environment;
-
- public class AudioFileFunc {
- //音频输入-麦克风
- public final static int AUDIO_INPUT = MediaRecorder.AudioSource.MIC;
-
- //采用频率
- //44100是目前的标准,但是某些设备仍然支持22050,16000,11025
- public final static int AUDIO_SAMPLE_RATE = 44100; //44.1KHz,普遍使用的频率
- //录音输出文件
- private final static String AUDIO_RAW_FILENAME = "RawAudio.raw";
- private final static String AUDIO_WAV_FILENAME = "FinalAudio.wav";
- public final static String AUDIO_AMR_FILENAME = "FinalAudio.amr";
-
- /**
- * 判断是否有外部存储设备sdcard
- * @return true | false
- */
- public static boolean isSdcardExit(){
- if (Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
- return true;
- else
- return false;
- }
-
- /**
- * 获取麦克风输入的原始音频流文件路径
- * @return
- */
- public static String getRawFilePath(){
- String mAudioRawPath = "";
- if(isSdcardExit()){
- String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath();
- mAudioRawPath = fileBasePath+"/"+AUDIO_RAW_FILENAME;
- }
-
- return mAudioRawPath;
- }
-
- /**
- * 获取编码后的WAV格式音频文件路径
- * @return
- */
- public static String getWavFilePath(){
- String mAudioWavPath = "";
- if(isSdcardExit()){
- String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath();
- mAudioWavPath = fileBasePath+"/"+AUDIO_WAV_FILENAME;
- }
- return mAudioWavPath;
- }
-
-
- /**
- * 获取编码后的AMR格式音频文件路径
- * @return
- */
- public static String getAMRFilePath(){
- String mAudioAMRPath = "";
- if(isSdcardExit()){
- String fileBasePath = Environment.getExternalStorageDirectory().getAbsolutePath();
- mAudioAMRPath = fileBasePath+"/"+AUDIO_AMR_FILENAME;
- }
- return mAudioAMRPath;
- }
-
-
- /**
- * 获取文件大小
- * @param path,文件的绝对路径
- * @return
- */
- public static long getFileSize(String path){
- File mFile = new File(path);
- if(!mFile.exists())
- return -1;
- return mFile.length();
- }
-
- }
复制代码
总结
AudioRecord这种方式收罗最为灵活,使开辟者最大限度的处置处罚收罗的音频,同时它捕获到的音频是原始音频PCM格式的!像做变声处置处罚的必要就必须要用它收集音频;
系统音频收罗
系统音频收罗有两种方法,但都有范围性。
2.1 REMOTE_SUBMIX
2.2 AudioPlaybackCapture
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |