Android 音视频开发【音频篇】【三】音频播放

2,244 阅读3分钟

使用AudioTrack组件,对Pcm原始格式的音频文件进行播放

一、音频播放

1.1 MediaPlayer

通常,在Android开发中,对于播放音频,会考虑使用MediaPlayer进行播放,比如mp3aac文件的播放

MediaPlayer的使用流程一般是

  • 构建实例

    MediaPlayer mediaPlayer = new MediaPlayer()

  • 设置播放路径

    MediaPlayer mediaPlayer.setDataSource(path)

  • 准备

    MediaPlayer prepare()

  • 开始播放

    MediaPlayer.start()

  • 停止播放

    MediaPlayer.stop()

当然,MediaPlayer的功能还是比较强大的,除了上述的基本功能,还有暂停跳转等功能,还可以设置一些状态回调

正因如此,在一般的开发中,使用MediaPlayer播放音频是很方便的,不过,如果要播放Pcm格式的音频文件,那么MediaPlayer就行不通了

那该怎么办,其实Android提供了Pcm格式音频文件的播放组件,那就是AudioTrack,相比于MediaPlayerAudioTrack是专门播放Pcm格式音频文件的组件

1.2 AudioTrack

上一章,通过使用AudioRecord录制了一段Pcm格式的音频文件,正愁没播放器播放,也就不知道录制的Pcm是否正常,那么就可以通过AudioTrack来验证录制的Pcm是否正常

AudioTrack的使用流程和AudioRecord很像

  • 开启子线程
  • 构建实例
  • 开始播放
  • 循环读取文件数据,并写入AudioTrack
  • 停止播放,释放资源

下面会详细介绍AudioTrack的播放流程

二、AudioTrack播放流程

2.1 开启子线程

AudioRecord的录制流程一样,AudioTrack的播放流程也都是需要在子线程中执行

private static class PlayThread extends Thread {
    public PlayThread(){}
}

2.2 配置必要参数

private static class PlayThread extends Thread {
    /**
     * pcm播放组件
     */
    private AudioTrack audioTrack;
    /**
     * 文件输入
     */
    private FileInputStream fis;
    private final String path;
    /**
     * 音频流格式(一般使用music)
     */
    private final int streamType;
    /**
     * 采样率
     */
    private final int sampleRateInHz;
    /**
     * 声道设置
     */
    private final int channelConfig;
    /**
     * 编码格式
     */
    private final int audioFormat;
    /**
     * 播放模式(一般使用流模式)
     */
    private final int mod;
    /**
     * 音频缓存大小
     */
    private int bufferSizeInBytes;
    /**
     * 是否停止播放
     */
    private boolean isStopPlay = false;
    /**
     * 构造方法(传入必要的参数)
     */
    public PlayThread(String path,
                      int streamType,
                      int sampleRateInHz,
                      int channelConfig,
                      int audioFormat,
                      int mod
    ) {
        this.path = path;
        this.streamType = streamType;
        this.sampleRateInHz = sampleRateInHz;
        this.channelConfig = channelConfig;
        this.audioFormat = audioFormat;
        this.mod = mod;
    }
}

构建AudioTrack需要如下参数

  • streamType

    音频流的类型,在Android音频管理中,有很多音频流类型,一般使用AudioManager.STREAM_MUSIC即可

  • sampleRateInHz

    采样率,一般使用44100

  • channelConfig

    声道设置,单声道CHANNEL_OUT_MONO,双声道CHANNEL_OUT_STEREO,注意,区别于AudioRecordchannelConfig,一个是in,一个是out,分别表示输入和输出

  • audioFormat

    AudioRecord一样,同样可以选择ENCODING_PCM_8BITENCODING_PCM_16BIT

  • mod

    该参数表示AudioTrack的使用类型,通常使用AudioTrack.MODE_STREAM类型

  • bufferSizeInBytes

    该参数和AudioRecord的一样,都是表示buffer的缓存大小,在写入AudioTrack时使用

2.3 初始化

重写Threadrun方法

# run()

@Override
public void run() {
    super.run();
    initIo();
    initAudioTrack();
    play();
}

run方法里面调用了三个方法,分别是

  • initIo()

    初始化读取的文件IO流

  • initAudioTrack()

    初始化AudioTrack组件

  • play()

    真正开始播放

# initIo()

/**
 * 初始化IO
 */
private void initIo() {
    if (TextUtils.isEmpty(path)) {
        return;
    }
    try {
        fis = new FileInputStream(path);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        fis = null;
    }
}

# initAudioTrack()

/**
 * 初始化pcm播放组件
 */
private void initAudioTrack() {
    bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
    audioTrack = new AudioTrack(streamType, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes, mod);
}

2.4 播放

进入到play()方法后,就开始真正的播放 # play()

/**
 * 开始播放
 */
private void play() {
    if (audioTrack == null || fis == null) {
        return;
    }
    byte[] data = new byte[bufferSizeInBytes];
    audioTrack.play();
    for (; ; ) {
        if (isStopPlay) {
            release();
            break;
        }
        int readSize = -1;
        try {
            readSize = fis.read(data);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (readSize <= 0) {
            isStopPlay = true;
            continue;
        }
        audioTrack.write(data, 0, readSize);
    }
}

类似于AudioRecordAudioTrack中也使用for(;;)构建了一个死循环,里面也是通过isStopPlay标志位来控制调用release()方法和退出循环

AudioRecord相反的是,是从AudioRecord中读取数据,写入文件,而AudioTrack是从文件中读取数据后,写入到AudioTrack

在手动结束,或者文件读取结束后,释放资源

# release()

/**
 * 释放资源
 */
private void release() {
    if (audioTrack != null) {
        audioTrack.stop();
        audioTrack.release();
        audioTrack = null;
    }
    if (fis != null) {
        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在执行过程中,就能够听到该程序播放Pcm的声音

三、GitHub

相关的类均可在GitHub中找到

PcmPlayer.java

PcmActivity.java