Android 音频 录音与播放

方风华
2023-12-01

声明:此文非本人原创,为整理网络资料加自己的一些注解所得。

参考:http://www.2cto.com/kf/201610/553744.html

            http://blog.csdn.net/azloong

一。录制声音MediaRecorder和AudioRecord 区别

      AudioRecord类相对于MediaRecorder来说,更加接近底层。MediaRecorder和AudioRecord都可以录制音频,区别是MediaRecorder录制的音频文件是经过压缩后的,需要设置编码器。并且录制的音频文件可以用系统自带的Music播放器播放。而AudioRecord录制的是PCM格式的音频文件,需要用AudioTrack来播放,AudioTrack更接近底层。在用MediaRecorder进行录制音视频时,最终还是会创建AudioRecord用来与AudioFlinger进行交互。C++层MediaRecorder创建AudioRecord类的代码位于AudioSource类构造函数中.MediaRecorder录制的数据是 amr MP3 格式
AudioRecorder录制出来的是比较原始的PCM音频格式。PCM经过编码压缩可以为 amr MP3 AAC。AudioRecord类相对于MediaRecorder来说,更加接近底层

二。播放声音时MediaPlayer和AudioTrack的区别

         播放声音可以用MediaPlayer和AudioTrack,两者都提供了java API供应用开发者使用。其中最大的区别是MediaPlayer可以播放多种格式的声音文件,例如 MP3,AAC,WAV,OGG,MIDI等。MediaPlayer会在framework层创建对应的音频解码器。而AudioTrack只能播放已 经解码的PCM流,如果是文件的话只支持wav格式的音频文件,因为wav格式的音频文件大部分都是PCM流。AudioTrack不创建解码器,所以只 能播放不需要解码的wav文件。MediaPlayer在framework层还是会创建AudioTrack,把解码后 的PCM数流传递给AudioTrack,AudioTrack再传递给AudioFlinger进行混音,然后才传递给硬件播放。

补充:SoundPool 则适合播放比较短的音频片段,比如游戏声音、按键声、铃声片段等等,它可以同时播放多个音频;

三。AudioRecord(/frameworks/base/media/java/android/media/AudioRecord.java)

      开始录音的时候,一个 AudioRecord需要初始化一个相关联的声音 buffer,这个 buffer主要是用来保存新的声音数据。这个 buffer的大小,我们可以在对象构造期间去指定。它表明一个 AudioRecord对象还没有被读取(同步)声音数据前能录多长的音 (即一次可以录制的声音容量 )。声音数据从音频硬件中被读出,数据大小不超过整个录音数据的大小(可以分多次读出),即每次读取初始化 buffer容量的数据。

public AudioRecord (int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)

audioSource(音频源:指的是从哪里采集音频。常用的值包括:DEFAULT(默认),VOICE_RECOGNITION(用于语音识别,等同于DEFAULT),MIC(由手机麦克风输入),VOICE_COMMUNICATION(用于VoIP应用)等。)
sampleRateInHz(采样率:音频的采样频率,每秒钟能够采样的次数,采样率越高,音质越高。给出的实例是44100、22050、11025但不限于这几个参数。例如要采集低质量的音频就可以使用4000、8000等低采样率。)
channelConfig(声道设置:android支持双声道立体声和单声道。MONO单声道,STEREO立体声)
audioFormat(编码制式和采样大小:采集来的数据当然使用PCM编码(脉冲代码调制编码,即PCM编码。PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。) android支持的采样大小16bit 或者8bit。当然采样大小越大,那么信息量越多,音质也越高,现在主流的采样大小都是16bit,在低质量的语音传输的时候8bit足够了。)
bufferSizeInBytes(采集数据需要的缓冲区的大小,如果不知道最小需要的大小可以在getMinBufferSize()查看。)

通过下面两个函数控制采集的开始/停止:

AudioRecord.startRecording();AudioRecord.stop();
一旦开始采集,必须通过线程循环尽快取走音频,否则系统会出现 overrun,调用的读取数据的接口是:
AudioRecord.read(byte[] audioData, int offsetInBytes, int sizeInBytes);

public int getState();获得AudioRecord实例的状态.该方法在AudioRecord实例被创建之后调用来判断是否成功的初始化。确保获取(占用)到了合适的硬件资源。
返回结果:
        STATE_INITIALIZED:1, 初始化成功,等待被使用;STATE_UNINITIALIZED:0, 初始化失败。

public int getRecordingState();获得AudioRecord实例的录音状态
    返回结果:
        RECORDSTATE_STOPPED:1,录音停止,表示没有在录音;RECORDSTATE_RECORDING:3, 正在录音。

public void startRecording();
获取录音数据:从录音硬件设备中读取录音数据,放入到字节数组中。具体我们使用那个方法来获取数据,根据我们设置的AudioRecord录制返回的音频数据的编码格式来定。由于ENCODING_PCM_16BIT是所有设备都支持的,所以我们也使用这个参数对应的read方法来获取录制的音频数据。
     参数:
     - audioData:需要写入音频数据的数组,即AudioRecord实例会将缓冲区中的数据根据指定的长度取出来放到这个给定的数组中,由于我们构造实例的时候设置的返回数据的格式是16bit,所以我们这里的audioData的格式为short[]
     - offsetInxxx:在audioData数组中写入数据的起始索引
     - sizeInShorts:请求的数据的大小
     - readMode:read模式
            READ_BLOCKING:获取数据会阻塞,之到所有的请求的数据被获得;
            READ_NON_BLOCKING:获取尽可能多的录音数据后会立刻返回,不会造成阻塞。
            该参数需要高版本的sdk,>=android.os.Build.VERSION_CODES.M (23),所以可以不设置。


       一般情况下录音实现的简单流程如下:

1.   创建一个数据流。

2.   构造一个AudioRecord对象,其中需要的最小录音缓存buffer大小可以通过getMinBufferSize方法得到。如果buffer容量过小,将导致对象构造的失败。

3.   初始化一个buffer,该buffer大于等于AudioRecord对象用于写声音数据的buffer大小。

4.   开始录音。

5.   AudioRecord中读取声音数据到初始化buffer,将buffer中数据导入数据流。

6.   停止录音。

7.   关闭数据流。

四。AudioTrack(/frameworks/base/media/java/android/media/AudioRecord.java)

初始化相关方法

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,int bufferSizeInBytes, int mode)
  构造方法
    - streamType:音频流的类型
        AudioManager.STREAM_VOICE_CALL:电话的音频流
        AudioManager.STREAM_SYSTEM:系统的音频流
        AudioManager.STREAM_RING:闹钟
        AudioManager.STREAM_MUSIC:音乐
        AudioManager.STREAM_ALARM:警告声
        AudioManager.STREAM_NOTIFICATION:通知
    - sampleRateInHz:来源的音频的采样频率,单位Hz
    - channelConfig:音频声道的配置
        AudioFormat.CHANNEL_OUT_MONO:单声道输出(左)
        AudioFormat.CHANNEL_OUT_STEREO:立体声输出(左和右)
    - audioFormat:音频格式
        AudioFormat.ENCODING_INVALID:无效的编码格式
        AudioFormat.ENCODING_DEFAULT:默认的编码格式
        AudioFormat.ENCODING_PCM_16BIT:每份采样数据为PCM 16bit,保证所有设备支持
        AudioFormat.ENCODING_PCM_8BIT:样本数据格式为PCM 8bit,不保证所有设备支持
        AudioFormat.ENCODING_PCM_FLOAT:单精度浮点样本
        ...
    - bufferSizeInBytes:缓冲区的大小
        该缓冲区是为了存放需要回放的音频流数据,单位为字节。AudioTrack实例不断的从该缓冲区内读取写入的音频流数据,然后播放出来。它的大小应该是框架层尺寸的数倍。
        如果该声轨的创建模式是"AudioTrack.MODE_STATIC",
    - mode:流或者是静态缓存
        AudioTrack.MODE_STATIC:创建模式-在音频开始播放之前,音频数据仅仅只会从Java层写入到本地层中一次。即开始播放前一次性写入音频数据。
        AudioTrack.MODE_STREAM:创建模式-在音频播放的时候,音频数据会同时会以流的形式写入到本地层中。即一边播放,一边写入数据。(很明显,如果实现一边录音一边播放的话,用这个模式创建声轨)

static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat);
    获取缓冲区大小
    返回使用音频流模式创建声轨的实例时所需要的最小的缓冲区的大小。注意,该尺寸并不会保证在加载音频数据的时候能够平滑的播放数据,应该根据预期的频率来选择更高的缓冲区大小,可以被填充额外的数据。例如,如果你想要动态地提高回放的采样频率,那么也要确保配置比之前设置的缓冲区尺寸更高。简单讲,就是如果提高音频的频率,那么就得相应的提高缓冲区的大小。
    - sampleRateInHz:采样频率
    - channelConfig:声轨的配置
    - audioFormat:音频的格式
    返回值:
      AudioTrack.ERROR_BAD_VALUE:有无效的参数 -2
      AudioTrack.ERROR:不能够查询音频输出的性能 -1
      最小的缓冲区的尺寸,单位为Hz

public int getState();
    返回AudioTrack实例的状态
    该方法十分有用,可以在创建完AudioTrack实例之后来检查该实例是否初始化成功,来确保获得了合适的硬件资源。返回值如下:
    AudioTrack.STATE_UNINITIALIZED:没有初始化成功
    AudioTrack.STATE_INITIALIZED:初始化成功AudioTrack实例,等待被使用
    AudioTrack.STATE_NO_STATIC_DATA:初始化成功一个使用静态数据的AudioTrack实例,但是该实例还没有获取到任何的数据呢。

播放音频相关方法

public int write(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes)
调用 write() 写入回放数据

public int getPlayState();
    获取AudioTrack实例的回放状态
    AudioTrack.PLAYSTATE_STOPPED:停止 1
    AudioTrack.PLAYSTATE_PAUSED:暂停  2
    AudioTrack.PLAYSTATE_PLAYING:正在播放   3

public void play();
    启动回放的实例,开始播放写入的音频数据
    如果AudioTrack的创建模式是"MODE_STATIC",即播放静态资源模式,必须在调用该方法之前调用写入数据的方法,一次性写入音频数据;
    如果AudioTrack的创建模式是"MODE_STREAM",即音频流模式,你可以随意的在调用play()方法之前,通过向缓冲区写入数据来填充音轨数据。

public void stop();
    停止播放音频数据
    当使用音频流模式创建的AudioTrack实例来播放音频数据的时候,该实例会在播放完上一个写入的音频缓冲数据之后停止播放。如果想要立刻马上停止播放,请见下面的方法。

public void pause();
    暂停音频数据的回放。
    立刻马上瞬间暂停,此时,未播放的数据不会被丢弃掉,如果重新调用play()方法,会继续之前的数据进行播放。如果想要丢弃剩下未播放的音频数据,应该在调用该方法之后调用flush()方法。

public void flush();
    立刻刷新已经排队等待回放的音频数据所有被写入的,但是还没有被播放的数据都会被丢弃。

public void release()
播放完成后,调用 release() 释放 AudioTrack 实例(有的还需要将AudioTrack 实例置为null)


 类似资料: