Android Audio模块框架和基础属性概念讲解

在学习Android Audio模块之前,我们先了解整体的Audio模块框架和一些涉及到的基础属性概念。如下图是Android Audio模块整体框架图

以播放为例,使用AudioTrack播放会通过JNI在C++层也创建相应的AudioTrack类,这个相当于客户端,会通过binder最终在AudioFlinger中创建相应的Track类,播放的音频数据通过共享环形缓存区进行传递给AudioFlinger,AudioFlinger中创建了回放线程如MixerThread将多个track混音后传递给Hal层,hal使用tinyalsa库发送给内核驱动进行播放。这个链路中有三个进程,一个是App自己播放的进程,如在App创建AudioTrack播放PCM数据,AudioTrack 通过JNI创建对应的C++层AudioTrack.cpp 然后就是AudioFlinger 和AudioPolicy所在的进程即audio_server进程。audioFlinger发送音频数据给Hal,Hal层一般在高版本也是单独的进程。

java 复制代码
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;

public class AudioTrackPlayer {

    private static final int SAMPLE_RATE = 44100; // 采样率
    private AudioTrack mAudioTrack;
    private boolean isPlaying = false;

    public void startPlay() {
        // 1. 设置音频参数
        int channelConfig = AudioFormat.CHANNEL_OUT_STEREO; // 双声道
        int audioFormat = AudioFormat.ENCODING_PCM_16BIT;  // 16位PCM

        // 2. 计算系统要求的最小缓冲区大小
        int minBufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, channelConfig, audioFormat);

        // 3. 使用 Builder 模式构建实例
        mAudioTrack = new AudioTrack.Builder()
                .setAudioAttributes(new AudioAttributes.Builder()
                        .setUsage(AudioAttributes.USAGE_MEDIA)
                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                        .build())
                .setAudioFormat(new AudioFormat.Builder()
                        .setEncoding(audioFormat)
                        .setSampleRate(SAMPLE_RATE)
                        .setChannelMask(channelConfig)
                        .build())
                .setBufferSizeInBytes(minBufferSize)
                // 设置为流模式 (MODE_STREAM)
                .setTransferMode(AudioTrack.MODE_STREAM) 
                .build();

        // 4. 开始播放状态(此时还没数据,是在等待 write)
        mAudioTrack.play();
        isPlaying = true;

        // 5. 在子线程中不断写入数据
        new Thread(() -> {
            // 生成一段简单的 440Hz 正弦波数据
            byte[] pcmData = generateSineWaveData(SAMPLE_RATE, 5); // 5秒长
            
            int offset = 0;
            while (isPlaying && offset < pcmData.length) {
                // 将数据写入 AudioTrack 的缓冲区
                int written = mAudioTrack.write(pcmData, offset, Math.min(minBufferSize, pcmData.length - offset));
                if (written > 0) {
                    offset += written;
                } else {
                    break; // 写入出错
                }
            }
            stopPlay();
        }).start();
    }

    public void stopPlay() {
        isPlaying = false;
        if (mAudioTrack != null) {
            mAudioTrack.stop();
            mAudioTrack.release();
            mAudioTrack = null;
        }
    }

    /**
     * 工具方法:生成一段正弦波 PCM 数据
     */
    private byte[] generateSineWaveData(int sampleRate, int durationSeconds) {
        int waveCount = sampleRate * durationSeconds;
        byte[] data = new byte[waveCount * 2 * 2]; // 16bit, 双声道
        double frequency = 440.0; // A4 键频率

        for (int i = 0; i < waveCount; i++) {
            // 生成正弦波采样点
            short sample = (short) (Math.sin(2 * Math.PI * i / (sampleRate / frequency)) * 0x7FFF);
            // 左声道
            data[i * 4] = (byte) (sample & 0xff);
            data[i * 4 + 1] = (byte) ((sample >> 8) & 0xff);
            // 右声道
            data[i * 4 + 2] = (byte) (sample & 0xff);
            data[i * 4 + 3] = (byte) ((sample >> 8) & 0xff);
        }
        return data;
    }
}

在上面播放demo中涉及2个重要的类

AudioFormat:描述采样率 采样格式 采样率

AudioAttributes:描述音频流信息,AudioFlinger和AudioPolicyService通过这个来选择哪个策略,选择哪个输出设备在哪个回放线程上播放,很重要。其中有几个属性

USAGE 音频流用途

ContentType 音频流内容

StreamType :使用哪个音频流如MUSIC,这个是兼容以前的

上面三个参数影响了AudioPolicyService使用哪个策略

mFlags 标志位如FLAG_HW_AV_SYNC FLAG_LOW_LATENCY 会影响选择使用哪个AudioFlinger使用哪个回放线程OutputThread

其他AudioFlinger AudioPolicyService中的概念参考https://blog.csdn.net/weixin_43013761/article/details/89418633

相关推荐
回到原点的码农2 小时前
MySQL-mysql zip安装包配置教程
android·mysql·adb
mygljx11 小时前
【MySQL 的 ONLY_FULL_GROUP_BY 模式】
android·数据库·mysql
冬奇Lab13 小时前
AudioTrack音频播放流程深度解析
android·音视频开发·源码阅读
青莲84315 小时前
查找算法详解
android·前端
青莲84315 小时前
排序算法详解
android·前端
zd20057216 小时前
用摩斯密码「听」时间:一款安卓报时应用的诞生
android
不会写代码的猴子16 小时前
Android17版本更新预览
android·android studio
用户416596736935518 小时前
记一次深坑:RecyclerView + FlexboxLayoutManager 导致 canScrollVertically 误判的剖析与修复
android