使用Android AudioTrack播放PCM

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/myvest/article/details/90731805

简介

AudioTrack是Android系统中管理和播放单一音频资源的类,相对来说比较简单,但需要注意的是,它仅能播放已经解码出来的PCM数据。

对一个音频文件(如MP3文件),如何使用FFmpeg进行解码获取到PCM,之前的文章已经有相应的说明:
https://blog.csdn.net/myvest/article/details/89254452
使用AudioTrack来播放PCM数据之前,我们先对解码出来的PCM数据进行重采样,也即是转换为指定的格式,这样我们用AudioTrack播放时就固定格式即可。重采样可以参考:
https://blog.csdn.net/myvest/article/details/89442000

使用方法及API简介

AudioTrack使用方法如下:
1、创建:

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
            int bufferSizeInBytes, int mode);

参数说明:
1)int streamType:指定即将播放的声音类型,对于不同类型,Android的audio系统会有不同处理(如音量等级不同,音量控制不同等),一些常见类型如下,对于音乐文件,我们使用STREAM_MUSIC

  • STREAM_ALARM:警告声
  • STREAM_MUSIC:音乐声,例如music等
  • STREAM_RING:铃声
  • STREAM_SYSTEM:系统声音,例如低电提示音,锁屏音等
  • STREAM_VOCIE_CALL:通话声

AudioTrack有两种数据加载模式(MODE_STREAM和MODE_STATIC),对应的是数据加载模式和音频流类型, 对应着两种完全不同的使用场景。

2)int sampleRateInHz:采样率
3)int channelConfig:音频声道对应的layout,如立体声是AudioFormat.CHANNEL_OUT_STEREO
4)int audioFormat:音频格式
5)int bufferSizeInBytes:缓冲区大小
缓冲区大小可以通过函数getMinBufferSize获取,传入采样率、声道layout、音频格式即可,如下:

public AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);

6)int mode:数据加载模式(MODE_STREAM和MODE_STATIC),两种模式对应着两种不同的使用场景

  • MODE_STREAM:在这种模式下,通过write一次次把音频数据写到AudioTrack中。这和平时通过write系统调用往文件中写数据类似,但这种工作方式每次都需要把数据从用户提供的Buffer中拷贝到AudioTrack内部的Buffer中,这在一定程度上会使引入延时。为解决这一问题,AudioTrack就引入了第二种模式。
  • MODE_STATIC:这种模式下,在play之前只需要把所有数据通过一次write调用传递到AudioTrack中的内部缓冲区,后续就不必再传递数据了。这种模式适用于像铃声这种内存占用量较小,延时要求较高的文件。但它也有一个缺点,就是一次write的数据不能太多,否则系统无法分配足够的内存来存储全部数据。

2、启动:

public AudioTrack.play();

3、数据注入:

public int write(byte[] audioData, int offsetInBytes, int sizeInBytes);

参数比较简单,数据、偏移、size

4、停止:

public AudioTrack.stop();		

5、释放:

public   AudioTrack.release();

6、获取状态:
STATE_INITIALIZED和STATE_UNINITIALIZED就不用说明了。STATE_NO_STATIC_DATA是个中间状态,当使用MODE_STATIC模式时,创建AudioTrack ,首先会进入改状态,需要write数据后,才会变成STATE_INITIALIZED状态。非STATE_INITIALIZED状态下去进行play的话,会抛出异常,所以MODE_STATIC模式如果没有write就去play是不行的。

    /**
     * Returns the state of the AudioTrack instance. This is useful after the
     * AudioTrack instance has been created to check if it was initialized
     * properly. This ensures that the appropriate resources have been acquired.
     * @see #STATE_INITIALIZED
     * @see #STATE_NO_STATIC_DATA
     * @see #STATE_UNINITIALIZED
     */
    public int getState() {
        return mState;
    }

示例

	private AudioTrack audioTrack = null;
	private static int sampleRateInHz = 44100;
	private static int channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
	private static int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
	private int bufferSize = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);

	class processThread implements Runnable {
......省略
		public void run() {		    			
		    byte[] outBuf = new byte[DECODE_BUFFER_SIZE];
		    if(audioTrack == null){
				audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRateInHz, channelConfig, audioFormat, bufferSize,
					AudioTrack.MODE_STREAM);
				if(audioTrack == null){
					mFFdec.decodeDeInit();			
					return ;
				}
		    }
		    try {
			   if ( audioTrack.getState() != AudioTrack.STATE_UNINITIALIZED) {
					audioTrack.play();
			   }
			   while(true){
				   int size = mFFdec.decodeFrame(outBuf);
				   if(size > 0){
						if(mFFdec.getMediaType() == mFFdec.MEDIA_TYPE_AUDIO){//audio
							audioTrack.write(outBuf, 0, size);
						}
				   }else{
				   		break;
				   }
			   }			   
			   if (audioTrack.getState() != AudioTrack.STATE_UNINITIALIZED) {
					audioTrack.stop();
					audioTrack.release();
			   }			   
		    }catch (Exception ex) {
				   ex.printStackTrace();
		    } catch (Throwable t) {
				   t.printStackTrace();
		    }		    
		    audioTrack = null;
		    mFFdec.decodeDeInit();
		}
	}

猜你喜欢

转载自blog.csdn.net/myvest/article/details/90731805