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

相关推荐
limuyang24 小时前
PDF Viewer KMP(基于 chrome 的 PDFium 内核)
android·kotlin
心平气和量大福大7 小时前
android-控件-搜索框SearchView
android·gitee
2601_962177138 小时前
【MySQL篇】聚合查询,联合查询
android·java·mysql
壮哥_icon8 小时前
【Android】批量 VectorDrawable 动态分别修改 fillColor 和 strokeColor 的踩坑与终极解决方案
android
齐天qaq9 小时前
Android 系统 APK 分区与权限
android
心平气和量大福大9 小时前
android-控件-多选框CheckBox
android·gitee
pengyu11 小时前
【Kotlin 协程修仙录 · 大乘境 · 初阶】 | 跳出三界:协程在 KMP 与后端开发中的跨平台之道
android·kotlin
XiaoLeisj11 小时前
Android Memory Profiler:堆内存指标、内存抖动与泄漏定位
android·性能优化·内存抖动·内存泄露·memory profiler
未来猫咪花11 小时前
Everything is ViewModel:让状态管理回到对象世界
android·flutter·ios