sessionId)
第一个参数StreamType,这个参数和Android中的AudioManager有关系,涉及到手机上的音频管理策略。
Android将系统的声音分为以下几类常见的(未写全):
l STREAM_ALARM:警告声
l STREAM_MUSCI:音乐声,例如music等
l STREAM_RING:铃声
l STREAM_SYSTEM:系统声音
l STREAM_VOCIE_CALL:电话声音
AudioAttributes类说明:
用于封装描述有关音频流信息的属性集合,AudioAttributes取代了流类型的概念以定义音频播放的行为。 通过允许应用程序定义属性,属性允许应用程序指定比在流类型中传达的更多信息。
AudioFormat类简单说明:
AudioFormat类用于访问许多音频格式和通道配置常量,它们例如在AudioRecord和AudioTrack中用作构造函数的单个参数。AudioFormat常量也用于mediaformat中指定媒体中常用的音频相关值。
AudioFormat.Builder类可用于创建AudioFormat格式类的实例。
在这里我们描述AudioFormat类允许您在每种情况下传达的主要概念,它们是:
Sample rate
以Hz为AudioFormat表示, AudioFormat实例中的采样率表示您正在播放或录制的内容中每秒每个声道的音频采样数。 这不是内容呈现或制作的采样率。 例如,在采样速率为48000Hz的设备上可以播放8000Hz的媒体采样率的声音; 采样率转换由平台自动处理,不会以6倍速播放。
正如API的M ,采样率高达192kHz都支持AudioRecord和AudioTrack ,并根据需要进行采样速率转换。 为了提高效率并避免有损转换,建议将AudioRecord和AudioTrack的采样率与端点设备采样率相匹配,并将采样率限制为不超过48kHz,除非有特殊的设备功能才能保证更高的速率。
Encoding
音频编码用于描述音频数据的位表示,可以是线性PCM或压缩音频,如AC3或DTS。
对于线性PCM,音频编码描述样本大小,8位,16位或32位,以及样本表示,整数或浮点数。
对于压缩音频,编码指定压缩方法,例如ENCODING_AC3和ENCODING_DTS 。 压缩音频数据通常作为字节存储在字节数组或ByteBuffer中。 当为AudioTrack指定压缩音频编码时,它会创建一个直接(非混合)音轨输出到能够解码压缩音频的端点(如HDMI)。 对于无法解码此类压缩音频的(大多数)其他端点,您需要先解码数据,通常是创建一个MediaCodec 。 或者,可以使用MediaPlayer播放压缩的音频文件或流。
当通过直接发送压缩音频AudioTrack ,它不需要以音频存取单元的精确倍数写入; 这与MediaCodec输入缓冲区不同。
Channel mask
在AudioTrack和AudioRecord中使用通道掩码来描述音频帧中的采样及其排列。 它们也用于端点(例如USB音频接口,连接到耳机的DAC)以指定特定设备的允许配置。
从API M ,有两种类型的通道掩码:通道位置掩码和通道索引掩码。
紧密地与相关联 AudioFormat是一个概念 audio frame ,其用于在整个文档,以表示音频数据的最小尺寸完整的单元。
Audio Frame
对于线性PCM,音频帧由一组同时捕获的样本组成,其计数和通道关联由channel mask给出,其样本内容由encoding指定。1单位的Frame等于1个采样点的字节数×声道数(比如PCM16,双声道的1个Frame等于2×2=4字节)。 对于压缩音频,音频帧可以交替地指代被压缩的数据字节的访问单元,其被逻辑地组合在一起用于解码和比特流访问(例如MediaCodec ),或单个字节的压缩数据(例如AudioTrack.getBufferSizeInFrames() )或线性PCM帧来自解码压缩数据(例如AudioTrack.getPlaybackHeadPosition() ),这取决于使用音频帧的上下文。
audiotrack使用过程
声明相关的变量
public AudioTrack audioTrack;
private FileInputStream fileInputStream;
private ReadDataThread readDataThread;
private DataInputStream dataInputStream;
private int minBufferSize;
初始化AudioTrack对象
public void init (){
//大小只是一个估计值,因为它既不考虑线路也不考虑汇(因为它们都不知道),所以此大小不能保证在负载下顺畅播放
//并且应根据预期的频率选择较高的值,在此频率下缓冲区将被重新填充额外的数据播放。
//例如,如果您打算将AudioTrack的源采样率动态设置为比初始源采样率更高的值, 请确保根据最高计划采样率配置缓冲区大小。
minBufferSize = AudioTrack.getMinBufferSize(16000,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
AudioAttributes attributes = new AudioAttributes.Builder()
//设置内容类型为语音时使用的内容类型
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
.setUsage(AudioAttributes.USAGE_ASSISTANT)
.build();
AudioFormat audioFormat = new AudioFormat.Builder()
.setSampleRate(16000)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.build();
audioTrack = new AudioTrack(attributes,audioFormat, minBufferSize,AudioTrack.MODE_STREAM,
AudioManager.AUDIO_SESSION_ID_GENERATE);
}else{
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,16000,AudioFormat.CHANNEL_OUT_MONO
,AudioFormat.ENCODING_PCM_16BIT, minBufferSize,AudioTrack.MODE_STREAM);
mStatus = AudioTrackPlayStatus.STATUS_READY;
public void startPlay(){
Log.e(TAG, "startPlay:audioTrack: "+audioTrack+":mStatus:"+mStatus );
if (audioTrack == null || mStatus == AudioTrackPlayStatus.STATUS_NO_READY){
return;
if (mStatus == AudioTrackPlayStatus.STATUS_START){
return;
try {
fileInputStream = new FileInputStream(Environment.getExternalStorageDirectory().getPath()+ File.separator+
"voiceTest/voiceTts.pcm");
dataInputStream = new DataInputStream(fileInputStream);
readDataThread = new ReadDataThread();
readDataThread.start();
} catch (FileNotFoundException e) {
Log.e(TAG, "startPlay: "+e.getMessage());
e.printStackTrace();
class ReadDataThread extends Thread{
@Override
public void run() {
super.run();
mStatus = AudioTrackPlayStatus.STATUS_START;
byte[] bytes = new byte[minBufferSize];
int len = 0;
Log.e(TAG, "run: start play");
audioTrack.play();
// write 是阻塞的方法
while (mStatus == AudioTrackPlayStatus.STATUS_START) {
try {
if ((len = dataInputStream.read(bytes)) == -1) break;
} catch (IOException e) {
e.printStackTrace();
try {
dataInputStream.close();
} catch (IOException ex) {
ex.printStackTrace();
audioTrack.write(bytes, 0, len);
Log.e(TAG, "run: end play" );
//播放完成了
try {
dataInputStream.close();
} catch (IOException e) {
e.printStackTrace();
结束播放以及释放资源
public void stopPlay() {
if (mStatus == AudioTrackPlayStatus.STATUS_NO_READY || mStatus == AudioTrackPlayStatus.STATUS_READY) {
Log.e(TAG, "stopPlay: 播放还未开始" );
} else {
mStatus = AudioTrackPlayStatus.STATUS_STOP;
audioTrack.stop();
release();
public void release() {
if (audioTrack != null) {
audioTrack.release();
audioTrack = null;
if (dataInputStream != null)
dataInputStream = null;
mStatus = AudioTrackPlayStatus.STATUS_NO_READY;
这样就可以将之前保存的pcm数据通过audiotrack播放出来了
接下来我们即将学习----音视频开发四:使用 Camera API 进行视频的采集和预览