8

Android音频采集常用方式详解

 2 years ago
source link: https://www.51cto.com/article/716445.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
  • Android音频采集
  • android音频采集一般两种方式:
  • AudioRecord和MediaRecorder两种都可以录制音频,MediaRecorder已实现大量的封装,操作起来更加简单,而AudioRecord使用起来更加灵活,能实现更多的功能

一、Android音频采集

1.AudioRecord

  • 基于字节流录音;
  • 可以实现语音的实时处理,进行边录边播,对音频的实时处理;
  • AudioRecord是一个比较偏底层的API,它可以获取到一帧帧PCM数据,之后可以对这些数据进行处理;
  • 输出的是PCM的语音数据,如果保存成音频文件是不能被播放器播放的。要用到AudioTrack这个去进行处理;

2.MediaRecorder

  • 基于文件录音;
  • MediaRecorder 是基于 AudioRecorder 的 API(最终还是会创建AudioRecord用来与AudioFlinger进行交互) ,它可以直接将采集到的音频数据转化为执行的编码格式,并保存;
  • 已集成了录音,编码,压缩等,支持少量的音频格式文件;
  • 封装度很高,操作简单;

二、MediaRecorder实现

MediaRecorder(这里需要注意,无论录制还是播放都是一个耗时操作,需要在非主线程中去操作)。

实现录音的主要代码:

private boolean start() {
        try {
            //创建MediaRecorder
            mMediaRecorder = new MediaRecorder();
            //创建录音文件
            mRecorderFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
                    + "/recorderdemo/" + System.currentTimeMillis() + ".m4a");
            if (!mRecorderFile.getParentFile().exists()) mRecorderFile.getParentFile().mkdirs();
            mRecorderFile.createNewFile();
            //配置MediaRecorder
            //从麦克风采集
            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            //保存文件为MP4格式
            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            //所有android系统都支持的适中采样的频率
            mMediaRecorder.setAudioSamplingRate(44100);
            //通用的AAC编码格式
            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
            //设置音质频率
            mMediaRecorder.setAudioEncodingBitRate(96000);
            //设置文件录音的位置
            mMediaRecorder.setOutputFile(mRecorderFile.getAbsolutePath());
            //开始录音
            mMediaRecorder.prepare();
            mMediaRecorder.start();
            startRecorderTime = System.currentTimeMillis();
        } catch (Exception e) {
            Toast.makeText(FileActivity.this, "录音失败,请重试", Toast.LENGTH_SHORT).show();
            return false;
        }
        //记录开始录音时间,用于统计时长,小于3秒中,录音不发送
        return true;
    }

二、AudioRecord音频采集

1.参数介绍

  • AudioRecord的基本参数;
  • audioResource:音频采集的来源;
  • audioSampleRate:音频采样率;
  • channelConfig:声道;
  • audioFormat:音频采样精度,指定采样的数据的格式和每次采样的大小;
  • bufferSizeInBytes:AudioRecord 采集到的音频数据所存放的缓冲区大小;
//指定音频源 这个和MediaRecorder是相同的 MediaRecorder.AudioSource.MIC指的是麦克风
        private static final int mAudioSource = MediaRecorder.AudioSource.MIC;
        //指定采样率 (MediaRecoder 的采样率通常是8000Hz AAC的通常是44100Hz。设置采样率为44100,目前为常用的采样率,官方文档表示这个值可以兼容所有的设置)
        private static final int mSampleRateInHz=44100 ;
        //指定捕获音频的声道数目。在AudioFormat类中指定用于此的常量
        private static final int mChannelConfig= AudioFormat.CHANNEL_CONFIGURATION_MONO; //单声道
        //指定音频量化位数 ,在AudioFormaat类中指定了以下各种可能的常量。通常我们选择ENCODING_PCM_16BIT和ENCODING_PCM_8BIT PCM代表的是脉冲编码调制,它实际上是原始音频样本。
        //因此可以设置每个样本的分辨率为16位或者8位,16位将占用更多的空间和处理能力,表示的音频也更加接近真实。
        private static final int mAudioFormat=AudioFormat.ENCODING_PCM_16BIT;
        //指定缓冲区大小。调用AudioRecord类的getMinBufferSize方法可以获得。
        private int mBufferSizeInBytes= AudioRecord.getMinBufferSize(mSampleRateInHz,mChannelConfig, mAudioFormat);//计算最小缓冲区
        //创建AudioRecord。AudioRecord类实际上不会保存捕获的音频,因此需要手动创建文件并保存下载。 
        private AudioRecord mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,mSampleRateInHz,mChannelConfig,
                mAudioFormat, mBufferSizeInBytes);//创建AudioRecorder对象

2.音频采集

添加权限

<uses-permission android:name="android.permission.RECORD_AUDIO"/>  
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • 构造一个 AudioRecord 对象。
  • 开始采集;
  • 读取采集的数据;
  • 停止采集;

采集代码如下:

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class AudioRecordTest extends AppCompatActivity implements Runnable {
   private Button mBtnStartRecord,mBtnStopRecord;
   //指定音频源 这个和MediaRecorder是相同的 MediaRecorder.AudioSource.MIC指的是麦克风
   private static final int mAudioSource = MediaRecorder.AudioSource.MIC;
   //指定采样率 (MediaRecoder 的采样率通常是8000Hz AAC的通常是44100Hz。设置采样率为44100,目前为常用的采样率,官方文档表示这个值可以兼容所有的设置)
   private static final int mSampleRateInHz=44100 ;
   //指定捕获音频的声道数目。在AudioFormat类中指定用于此的常量
   private static final int mChannelConfig= AudioFormat.CHANNEL_CONFIGURATION_MONO; //单声道
   //指定音频量化位数 ,在AudioFormaat类中指定了以下各种可能的常量。通常我们选择ENCODING_PCM_16BIT和ENCODING_PCM_8BIT PCM代表的是脉冲编码调制,它实际上是原始音频样本。
   //因此可以设置每个样本的分辨率为16位或者8位,16位将占用更多的空间和处理能力,表示的音频也更加接近真实。
   private static final int mAudioFormat=AudioFormat.ENCODING_PCM_16BIT;
   //指定缓冲区大小。调用AudioRecord类的getMinBufferSize方法可以获得。
   private int mBufferSizeInBytes;
   private File mRecordingFile;//储存AudioRecord录下来的文件
   private boolean isRecording = false; //true表示正在录音
   private AudioRecord mAudioRecord=null;
   private File mFileRoot=null;//文件目录
   //存放的目录路径名称
   private static final String mPathName = getFilesDir()+ "/audiioRecordtest";
   //保存的音频文件名
   private static final String mFileName = "audiorecordtest.pcm";
   //缓冲区中数据写入到数据,因为需要使用IO操作,因此读取数据的过程应该在子线程中执行。
   private Thread mThread;
   private DataOutputStream mDataOutputStream;
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_audio_record);
       initDatas();
       initUI();
   }
   //初始化数据
   private void initDatas() {
       mBufferSizeInBytes = AudioRecord.getMinBufferSize(mSampleRateInHz,mChannelConfig, mAudioFormat);//计算最小缓冲区
       mAudioRecord = new AudioRecord(mAudioSource,mSampleRateInHz,mChannelConfig,
               mAudioFormat, mBufferSizeInBytes);//创建AudioRecorder对象
       mFileRoot = new File(mPathName);
       if(!mFileRoot.exists())
           mFileRoot.mkdirs();//创建文件夹
   }
   //初始化UI
   private void initUI() {
       mBtnStartRecord = findViewById(R.id.btn_start_record);
       mBtnStopRecord = findViewById(R.id.btn_stop_record);
       mBtnStartRecord.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               startRecord();
           }
       });
       mBtnStopRecord.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               stopRecord();
           }
       });
   }
 //开始录音
    public void startRecord() {
        //AudioRecord.getMinBufferSize的参数是否支持当前的硬件设备
        if (AudioRecord.ERROR_BAD_VALUE == mBufferSizeInBytes || AudioRecord.ERROR == mBufferSizeInBytes) {
            throw new RuntimeException("Unable to getMinBufferSize");
        }else{
            destroyThread();
            isRecording = true;
            if(mThread == null){
                mThread = new Thread(this);
                mThread.start();//开启线程
            }
        }
    }
    /**
     * 销毁线程方法
     */
    private void destroyThread() {
        try {
            isRecording = false;
            if (null != mThread && Thread.State.RUNNABLE == mThread.getState()) {
                try {
                    Thread.sleep(500);
                    mThread.interrupt();
                } catch (Exception e) {
                    mThread = null;
                }
            }
            mThread = null;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            mThread = null;
        }
    }
    //停止录音
    public void stopRecord() {
        isRecording = false;
        //停止录音,回收AudioRecord对象,释放内存
        if (mAudioRecord != null) {
            if (mAudioRecord.getState() == AudioRecord.STATE_INITIALIZED) {//初始化成功
                mAudioRecord.stop();
            }
            if (mAudioRecord  !=null ) {
                mAudioRecord.release();
            }
        }
    }
    @Override
    public void run() {
        //标记为开始采集状态
        isRecording = true;
        //创建一个流,存放从AudioRecord读取的数据
        mRecordingFile = new File(mFileRoot,mFileName);
        if(mRecordingFile.exists()){//音频文件保存过了删除
            mRecordingFile.delete();
        }
        try {
            mRecordingFile.createNewFile();//创建新文件
        } catch (IOException e) {
            e.printStackTrace();
            Log.e("lu","创建储存音频文件出错");
        }
        try {
            //获取到文件的数据流
            mDataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(mRecordingFile)));
            byte[] buffer = new byte[mBufferSizeInBytes];
            //判断AudioRecord未初始化,停止录音的时候释放了,状态就为STATE_UNINITIALIZED
            if(mAudioRecord.getState() == mAudioRecord.STATE_UNINITIALIZED){
                initDatas();
            }
            mAudioRecord.startRecording();//开始录音
            //getRecordingState获取当前AudioReroding是否正在采集数据的状态
            while (isRecording && mAudioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
                int bufferReadResult = mAudioRecord.read(buffer,0,mBufferSizeInBytes);
                for (int i = 0; i < bufferReadResult; i++)
                {
                    mDataOutputStream.write(buffer[i]);
                }
            }
            mDataOutputStream.close();
        } catch (Throwable t) {
            Log.e("lu", "Recording Failed");
            stopRecord();
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        destroyThread();
        stopRecord();
    }
}

AudioRecord这种方式采集最为灵活,使开发者最大限度的处理采集的音频,同时它捕获到的音频是原始音频PCM格式的!像做变声处理的需要就必须要用它收集音频;

图片

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK