音频笔记-AudioTrack

AudioTrack是什么?

官方释义:管理和播放单个音频资源。将PCM音频缓冲区流传输到音频接收器以进行播放。

AudioTrack和其他播放器的区别

播放器区别/关系
AudioTrack只能播放PCM(.wav格式的音频文件)数据流
MediaPlayer本质是先将.mp3等格式解码成PCM流,然后底层会调用AudioTrack进行播放
SoundPool短音频,播放提示音,按键音之类的音频

AudioTrack的使用流程

1.初始化AudioTrack。
2.将数据从流或者文件中不断的写入缓存。(静态模式会一次性全部写入缓存)
2.不断的将音频流从缓存中拿出来(静态模式会一次性全部拿出来)
3.将拿到的缓存数据,不断的播放出来。
4.停止或者销毁。

使用AudioTrack前的基本概念

1.AudioTrack的两种传输模式(TransferMode):

传输模式(TransferMode)区别操作
AudioTrack.MODE_STATIC长(流)音频,多次读取,可能存在延迟,需要将数据一次一次拷贝到AudioTrack的缓存中。先play,然后不停的write。
AudioTrack.MODE_STREAM短音频,一次读完。如果数据过长,可能会直接奔掉,只需将数据一次性全部拷贝到AudioTrack的缓存中。必须先write 将数据一次加载完成,再play

2.AudioTrack的基本构造

audioTrack = new AudioTrack.Builder()
        .setAudioAttributes(new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build())
        .setAudioFormat(new AudioFormat.Builder()
                .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                .setSampleRate(8000)
                .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
                .build())
        .setTransferMode(AudioTrack.MODE_STREAM)
        .setSessionId(0)
        .build();

构造中各种参数的含义

1.setAudioAttributes()

主要用来设置音频属性

  • setUsage()
音频的用途:
比如是要作为音乐播放,
还是手机导航语音播放,
还是作为收到消息的提示音使用,等。

如:(还有很多,这里只列举3种)
AudioAttributes.USAGE_MEDIA
AudioAttributes.USAGE_ALARM
AudioAttributes.USAGE_VOICE_COMMUNICATION
  • setContentType()
音频的内容:
比如当前要播放的音频内容是聊天语音,
还是一段音乐,还是按键音效,等。

如:(还有很多,这里只列举3种)
AudioAttributes.CONTENT_TYPE_SPEECH
AudioAttributes.CONTENT_TYPE_MUSIC
AudioAttributes.CONTENT_TYPE_MOVIE

设置这些属性有到底有什么用?

这取决于Android系统的运行策略。
不知道大伙有没有这种情况:

正在听音乐,听的很high。突然电话来了,你会发现你的音乐声音会自动变小或者关闭。当你结束电话,音频又会开始播放。

这是就是setAudioAttributes()所起到的作用。帮你判断目前音频的使用环境,并做出相应处理。

2.setAudioFormat()
  • setEncoding(音频采样大小)
指一秒内传输的数据大小。采样大小越高声音越饱满。

目前只有两种模式:
ENCODING_PCM_8BIT
ENCODING_PCM_16BIT
  • setSampleRate(采样率)
指录音设备在一秒钟内对声音信号的采样次数,采样率越高声音越细腻。
我目前项目里是16000Hz。

官方说44100Hz可以兼容所有值。(目前大部分app用的应该也是44100Hz)
  • setChannelMask(通道数)
通道,通俗点就是耳机的声道,听歌的双声道,打游戏的5.1声道,7.1声道,就是这个声道。

AudioFormat.CHANNEL_OUT_MONO
AudioFormat.CHANNEL_OUT_STEREO
AudioFormat.CHANNEL_OUT_7POINT1_SURROUND
3.setTransferMode()
传输模式,上面讲到的 静态 和 流式。

AudioTrack.MODE_STREAM
AudioTrack.MODE_STATIC
4.setBufferSizeInBytes(最小缓存区大小)

还有个概念,就是minBufferSize,它是干嘛的?

应用层能分配多大的数据Buffer。也就是保障AudioTrack正常工作的最小缓冲区大小。

也就是播放当前音频每一帧需要的缓存大小。

它的计算方式

AudioTrack.getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)

举例:
int minBufferSize = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);

也就是上面构造中setAudioFormat()方法里的三个参数。
构造中有.setBufferSizeInBytes()这个方法,就是用来设置这个值的。

3.AudioTrack的其他构造

- new AudioTrack.Builder().setAudioAttributes().setAudioFormat().setBufferSizeInBytes().setTransferMode().build();	[最推荐的用法]

- new AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, int mode, int sessionId);	[和Builder类似,但是需要自行调用getMinBufferSize计算一下缓存大小]

- new AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode)	[早期API3-9的方法,官方已不推荐使用]

4.开始播放音频

不管哪种构造方法,播放还是一样的。

播放前的判空

audioTrack == null	//是否为空
audioTrack.getState() != AudioTrack.STATE_INITIALIZED	//是否已经初始化
audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) //是否正在播放(这一条根据自己业务需求而定)

初始化状态 和 播放状态

1.初始化状态

AudioTrack.STATE_UNINITIALIZED		未初始化(这个时候直接播放,会奔溃)
AudioTrack.STATE_INITIALIZED		已初始化
AudioTrack.STATE_NO_STATIC_DATA		静态播放流已加载

2.播放状态

AudioTrack.PLAYSTATE_STOPPED	播放停止
AudioTrack.PLAYSTATE_PAUSED		播放暂停
AudioTrack.PLAYSTATE_PLAYING	播放中

静态播放

先说静态播放,前面说了,一次性全部读取。

public void staticPlay() {

        //这里以raw资源文件为例:因为是短资源,所以直接放在资源文件中了。
        
        byte[] staticData = new byte[0];
        InputStream in = getResources().openRawResource(R.raw.msg);

        try {
            int sizeOfInputStram  = in.available();
            staticData = new byte[sizeOfInputStram];
            Log.i("ddd", "文件大小:" + sizeOfInputStram);
            ByteArrayOutputStream out = new ByteArrayOutputStream(sizeOfInputStram);

//            for (int b; (b = in.read()) != -1; ) {
//                out.write(b);
//            }

            int rc;
            while ((rc = in.read(staticData, 0, sizeOfInputStram)) != -1) {
                out.write(staticData, 0, rc);
            }

            staticData = out.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        staticAudioTrack = new AudioTrack.Builder()
                .setAudioAttributes(new AudioAttributes.Builder()
                        .setUsage(AudioAttributes.USAGE_MEDIA)
                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                        .setLegacyStreamType(AudioManager.STREAM_MUSIC)
                        .build())
                .setAudioFormat(new AudioFormat.Builder()
                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                        .setSampleRate(staticData.length)//由于短音频一般会在一秒内播完,所以推荐使用资源的大小作为参数
                        .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
                        .build())
                .setBufferSizeInBytes(staticData.length)//这里设置资源的长度,因为是静态的短音频
                .setTransferMode(AudioTrack.MODE_STATIC)//这里是静态模式
                .setSessionId(0)
                .build();

        Log.i("ddd", "静态大小:" + staticData.length);

        //一次性写入AudioTrack
        staticAudioTrack.write(staticData, 0, staticData.length);
        //播放
        staticAudioTrack.play();


    }

流式播放

先开始播放,再不断的读取


//这里以读取file文件为例,因为文件较大,放在资源文件中不太合理。

//流式初始化
audioTrack = new AudioTrack.Builder()
        .setAudioAttributes(new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build())
        .setAudioFormat(new AudioFormat.Builder()
                .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                .setSampleRate(44100)//根据实际项目而定
                .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
                .build())
        .setTransferMode(AudioTrack.MODE_STREAM)//流式模式
        .setSessionId(0)
        .build();

//计算一次读取的大小
int minBufferSize = AudioTrack.getMinBufferSize(8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);

//这里以读取一个长文件为例

file = new File("文件路径");
fileInputStream = new FileInputStream(file);
dataInputStream = new DataInputStream(new BufferedInputStream(fileInputStream))
byte[] bytes = new byte[minBufferSize];//(这里的bytes就是上面通过getMinBufferSize计算出来的长度值)
int len;
audioTrack.play();
while ((len = dataInputStream.read(bytes)) != -1) {
    //每次读取minBufferSize的长度
    audioTrack.write(bytes, 0, len);
}
audioTrack.stop();//这里在播放完成时调用stop

audioTrack.release()

释放资源。
当不需要或者使用完audioTrack时,可调用此方法释放掉资源和占用你的内存。
因为这个方法调用后,audioTrack状态将回到 未初始化 状态,需要重新调用构造函数。
所以,还需要使用时,无需调用此方法。只需stop停止即可。当确定不再使用(比如退出当前页面,这个音频只是播放一次)时,可调用此方法释放掉。

调用此方法后最好再置空。

audioTrack = null//将audioTrak置空

Demo地址

https://github.com/tc7326/audio.video.demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值