Android audio and video development (five) AudioRecord recording audio

Introduction

AudioRecord is a tool for recording audio in the Android multimedia framework. It supports the recording of original audio data, that is, PCM data. PCM data cannot be played directly by the player, and needs to be encoded and compressed into a common audio format before it can be recognized by the player. The native api also provides AudioTrack to play PCM data.
Google Api Documentation

recording process

AudioRecord continuously reads the data stream (byte stream) input from the audio source through the read method, and then saves the data stream as PCM data.
When recording starts, AudioRecord needs to create a buffer. This buffer is mainly used to save new audio data. It is used to identify how long an AudioRecord object can record before the sound data is read (synchronized) ( That is, the sound capacity that can be recorded at one time). The sound data is continuously read from the audio hardware, and the size of the data read each time does not exceed the capacity of the initialization buffer (the size of the recording data).
The process is as follows:

  1. Constructs an AudioRecord object. The buffer size of the minimum recording data buffer can be obtained through the getMinBufferSize method. If the buffer capacity is too small, the object construction will fail.
  2. Initialize a buffer whose size is greater than or equal to the buffer size used by the AudioRecord object for writing sound data, and for caching the read audio data.
  3. startRecording start recording
  4. Create a data stream, continuously read the sound data from the AudioRecord to the initialized buffer, and then output the data in the buffer.
  5. close stream
  6. stop recording

example

The following uses Kotlin code to show how AudioRecord records audio data:

class AudioActivity : AppCompatActivity() {
    
    
    //音频录制
    private var audioRecord: AudioRecord? = null
    //缓冲区大小,缓冲区用于保存音频数据流
    private var bufferSize: Int = 0
    //记录是否正在录制音频
    @Volatile private var isRecording = false
	//录音线程
    private var recordThread: Thread? = null

    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_media)
        initRecoder()
    }

    /**
     * 初始化
     */
    private fun initRecoder() {
    
    
        /*
            getMinBufferSize用于获取成功创建AudioRecord对象所需的最小缓冲区大小,
            此大小不能保证在负载下能顺利录制,应根据预期的频率选择更高的值,
            在该频率下,将对AudioRecord实例进行轮询以获取新数据
            参数介绍:(具体看官网api介绍)
            sampleRateInHz:采样率,以赫兹为单位
            channelConfig:音频通道的配置
            audioFormat:音频数据的格式
         */
        bufferSize = AudioRecord.getMinBufferSize(
            44100,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT
        )

        /*
            构建AudioRecord对象。
            参数介绍:
            audioSource:音频来源
            sampleRateInHz:采样率,以赫兹为单位。目前,只有44100Hz是保证在所有设备上都可以使用的速率(最适合人耳的),但是其他速率(例如22050、16000和11025)可能在某些设备上可以使用
            channelConfig:音频通道的配置
            audioFormat:音频数据的格式
            bufferSizeInBytes:在录制期间写入音频数据的缓冲区的总大小(以字节为单位)
        */
        audioRecord = AudioRecord(
            MediaRecorder.AudioSource.MIC,
            44100,
            AudioFormat.CHANNEL_IN_MONO,
            AudioFormat.ENCODING_PCM_16BIT,
            bufferSize * 2
        )
    }

    /**
     * 开始录制
     */
    fun startRecord(view: View) {
    
    
        if (isRecording) {
    
    
            return
        }
        isRecording = true
        if (recordThread == null) {
    
    
            recordThread = Thread(recordRunnable)
        }
        recordThread!!.start()
    }

    /**
     * 停止录制
     */
    fun stopRecord(view: View) {
    
    
    	//置为false,表示线程循环就结束了,线程也执行完毕了
    	//也可以直接中断线程
        isRecording = false
        audioRecord = null
        recordThread = null
    }

    /**
     * 录音线程
     *
     * 由于需要不断读取音频数据,所以放在子线程操作
     */
    private val recordRunnable = Runnable {
    
    
        //设置线程优先级
        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO)
        //创建文件
        val tmpFile: File? = FileUtil.createFile("${
      
      System.currentTimeMillis()}.pcm")
        //文件输出流
        var fos: FileOutputStream = FileOutputStream(tmpFile?.getAbsoluteFile())
        try {
    
    

            if (audioRecord?.getState() !== AudioRecord.STATE_INITIALIZED) {
    
    
                //没有初始化成功
                return@Runnable
            }
            //开始录制
            audioRecord?.startRecording()

            var buffer = 0
            val bytes = ByteArray(bufferSize)
            //轮询读取数据
            while (isRecording) {
    
    
                if (audioRecord != null) {
    
    
                    buffer = audioRecord!!.read(bytes, 0, bufferSize)
                    if (buffer == AudioRecord.ERROR_INVALID_OPERATION || buffer == AudioRecord.ERROR_BAD_VALUE) {
    
    
                        continue
                    }
                    if (buffer == 0 || buffer == -1) {
    
    
                        break
                    }
                    //在此可以对录制音频的数据进行二次处理 如变声,压缩,降噪等操作
                    //也可以直接发送至服务器(实时语音传输) 对方可采用AudioTrack进行播放
                    //这里直接将pcm音频数据写入文件
                    fos.write(bytes)
                }
            }
        } catch (e: Exception) {
    
    
            Log.e("Test", "出错了", e)
        } finally {
    
    
            try {
    
    
                fos?.close()
            } catch (ex: IOException) {
    
    
            }
        	audioRecord?.stop()
        	audioRecord?.release()
        }
    }
}

Guess you like

Origin blog.csdn.net/gs12software/article/details/105111803