Android音频系统
最近在做UAC的项目,大概就是接收内核UAC的事件,也就是声音相关事件。然后就是pcm_read和AudioTrackr->write之间互传。感觉略微有点奇怪,所以简单总结一下。
1 UAC的简要流程
open_netlink_socket 打开内核窗口,类似于ioctl。
recvfrom 接收数据。
UAC_CAP_START 处理开始播放事件。
host_to_device
tracker_data_thread 播放线程。
pcm_read->(AudioTrackr->write)
pcm_open
pcm_read
pcm_close
UAC_CAP_STOP 处理停止播放事件。
UAC_PLAY_START 处理开始录音事件。
device_to_host
recorder_data_thread
(AudioRecord->read)<-pcm_write
pcm_open
pcm_write
pcm_close
UAC_PLAY_STOP 处理停止录音事件。
2 安卓音频系统
https://source.android.com/docs/core/audio?hl=zh-cn
关于UAC的内容,居然也有说:
https://source.android.com/docs/core/audio/usb?hl=zh-cn
不过下面这两个图我觉得直观一丢丢。

下面这个都包浆了。。。

大致就是几层:
1 Java App层,这一层封装最完善,但是只有最常规的操作,给开发app的帅哥做傻瓜式操作的。使用android.media.MediaPlayer。
2 Framework层,这一层可以使用AudioTracker和AudioRecorder,这一层接口比较底层一点,提供的功能比较多。可以实现实时处理和一些特效。Java和C++都可以用。下面还有个AudioFlinger,是用来做混音的。也是上下层的分隔。所以绕过Framework层,直接用HAL的接口,可能就有问题。

3 HAL接口。有HIDL和AIDL的,这一层理论上可以用,但是貌似比较少,起码我们公司的大神都不在这层搞事。
4 ALSA接口,这一层是标准Linux的,花样也是非常多。
3 App接口
没啥好说的,这部分我也不是太熟悉,直接怼media.MediaPlayer即可。代码说明一切吧。
package com.example.audioplayer;import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {private MediaPlayer mediaPlayer;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button playButton = findViewById(R.id.play_button);Button stopButton = findViewById(R.id.stop_button);// 播放本地音频文件mediaPlayer = MediaPlayer.create(this, R.raw.example_audio);// 如果你想播放网络音频流,可以使用下面的代码// mediaPlayer = new MediaPlayer();// try {// mediaPlayer.setDataSource("http://your-audio-url.com/audio.mp3");// mediaPlayer.prepare(); // 同步准备,可能会阻塞主线程,建议使用异步准备// } catch (IOException e) {// e.printStackTrace();// }playButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mediaPlayer != null && !mediaPlayer.isPlaying()) {mediaPlayer.start();}}});stopButton.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (mediaPlayer != null && mediaPlayer.isPlaying()) {mediaPlayer.stop();// 重新准备MediaPlayermediaPlayer.prepareAsync();}}});}@Overrideprotected void onDestroy() {super.onDestroy();if (mediaPlayer != null) {mediaPlayer.release();mediaPlayer = null;}}
}
4 AudioTracker和AudioRecorder
我这次项目用的就是这两个,其实还是挺简单,看个例子就够了。。。
#include <android/media/AudioTrack.h>// 假设audioBuffer是一个已经加载好的音频数据的short数组
short audioBuffer[]; // 音频数据填充到这个数组中
int bufferSize = audioTrack->frameCount() * audioTrack->channelCount(); // 计算缓冲区大小// 创建一个AudioTrack实例
auto audioTrack = new android::media::AudioTrack(android::media::AudioTrack::STREAM_MUSIC, // 音频流类型44100, // 采样率44.1kHzandroid::media::AudioTrack::CHANNEL_OUT_STEREO, // 立体声输出android::media::AudioTrack::TRANSFER_MODE_STATIC, // 静态模式bufferSize, // 缓冲区大小android::media::AudioTrack::MODE_STATIC // 静态播放模式
);// 开始播放音频
audioTrack->start();// 写入数据到AudioTrack缓冲区
audioTrack->write(audioBuffer, bufferSize);// 播放完毕,暂停并释放资源
audioTrack->stop();
delete audioTrack;
5 HAL
这部分位于vendor,上面的是位于system,所以还是区别很大。如果要在vendor搞事情,还是要用这个部分。
定义是在这个地方:https://android.googlesource.com/platform/hardware/interfaces/+/refs/heads/master/audio/
但是比较疑惑的一点是单位有大神说直接调用Hal,会碰坏系统。。。存疑中。。。
用的话直接用hardware/audio.h就可以。
#include <jni.h>
#include <string>
#include <android/log.h>
#include <hardware/hardware.h>
#include <hardware/audio.h>#define LOG_TAG "NativeAudio"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)extern "C" JNIEXPORT void JNICALL
Java_com_example_audioplayer_MainActivity_nativeInitAudio(JNIEnv *env, jobject thiz) {LOGD("Initializing Audio HAL");hw_module_t *module = nullptr;hw_device_t *device = nullptr;// Load the audio hardware moduleif (hw_get_module(AUDIO_HARDWARE_MODULE_ID, (const hw_module_t **)&module) == 0) {LOGD("Audio module loaded");// Open the audio hardware deviceif (module->methods->open(module, AUDIO_HARDWARE_INTERFACE, &device) == 0) {LOGD("Audio device opened");audio_hw_device_t *audioDevice = (audio_hw_device_t *)device;if (audioDevice && audioDevice->init_check(audioDevice) == 0) {LOGD("Audio device initialized");// Set up and start playback using audio_stream_outaudio_stream_out_t *streamOut = nullptr;audioDevice->open_output_stream(audioDevice, 0, AUDIO_DEVICE_OUT_SPEAKER,AUDIO_OUTPUT_FLAG_NONE, nullptr, &streamOut, nullptr);if (streamOut) {LOGD("Audio stream out opened");// Simplified example to play a buffer (should use actual audio data)size_t bufferSize = streamOut->common.get_buffer_size(&streamOut->common);uint8_t *buffer = new uint8_t[bufferSize];memset(buffer, 0, bufferSize); // Fill buffer with silence or actual audio datastreamOut->write(streamOut, buffer, bufferSize);delete[] buffer;audioDevice->close_output_stream(audioDevice, streamOut);} else {LOGD("Failed to open audio stream out");}} else {LOGD("Audio device initialization failed");}device->close(device);} else {LOGD("Failed to open audio device");}} else {LOGD("Failed to load audio module");}
}
6 ALSA
这个部分有点略大,看看下次写吧。。。还有一个OMX,以后有心情再写吧。。。
最后回到一开始说的UAC,应该是新生成了音频的节点,然后可以从这个节点读取音频数据,但是最后要将声音从Android的接口放出去,所以那么搞。之前调试的时候,在UAC的模式下,好像也确实是生成了两张声卡。这部分感觉内容也挺多了,下次再总结。

参考:
https://source.android.com/docs/core/audio?hl=zh-cn
Android系统Audio框架介绍_android audio-CSDN博客
Android系统Audio框架介绍_android audio-CSDN博客
相关文章:
Android音频系统
最近在做UAC的项目,大概就是接收内核UAC的事件,也就是声音相关事件。然后就是pcm_read和AudioTrackr->write之间互传。感觉略微有点奇怪,所以简单总结一下。 1 UAC的简要流程 open_netlink_socket 打开内核窗口,类似于ioctl。…...
Android开发系列(九)Jetpack Compose之ConstraintLayout
ConstraintLayout是一个用于构建复杂布局的组件。它通过将子视图限制在给定的约束条件下来定位和排列视图。 使用ConstraintLayout,您可以通过定义视图之间的约束关系来指定它们的位置。这些约束可以是水平和垂直的对齐、边距、宽度和高度等。这允许您创建灵活而响…...
SpringMVC系列三: Postman(接口测试工具)
接口测试工具 💞Postman(接口测试工具)Postman介绍Postman是什么Postman相关资源Postman安装Postman快速入门Postman完成Controller层测试其它说明 💞课后作业 上一讲, 我们学习的是SpringMVC系列二: 请求方式介绍 现在打开springmvc项目 💞…...
项目实训-vue(十二)
项目实训-vue(十二) 文章目录 项目实训-vue(十二)1.概述2.处理进度可视化 1.概述 本篇博客将记录我在图片上传页面中的工作。 2.处理进度可视化 除了导航栏之外,我们还需要对上传图片以及图片处理的过程以及流程进行…...
达梦数据库的系统视图v$lock
达梦数据库的系统视图v$lock 在达梦数据库(DM)中,V$LOCK 系统视图用于查看当前数据库中的锁定状态。该视图提供了关于所有锁定详细信息,例如锁的内存地址、所属事务 ID,锁类型和锁模式等。这对于数据库管理员进行锁定…...
【无人机三维路径规划】基于树木生长算法TGA实现复杂城市地形下无人机避障三维航迹规划附Matlab代码
% 定义无人机起始位置和目标位置 start_point [0, 0, 0]; % 起始位置 [x, y, z] target_point [100, 100, 100]; % 目标位置 [x, y, z] % 定义城市地形和障碍物信息 city_map imread(‘city_map.png’); % 城市地形图像 obstacles [ 20, 30, 10; % 障碍物1位置 [x, y, z] …...
制造业工厂的管理到底有多难
一、引言 随着全球经济的不断发展,制造业作为实体经济的核心,对国家的经济增长起着至关重要的作用。然而,制造业工厂的管理却是一项复杂而艰巨的任务。本文将深入探讨制造业工厂管理所面临的挑战,并提出相应的应对策略。 二、制造…...
QTday5 2024-06-19
作业要求: 1.思维导图 2.整理代码:TCP服务器 作业1:思维导图 作业2:整理代码 运行代码: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTcpServer> #include <QList>…...
Node官网下载各个版本
node官网下载各个版本地址 例如 14.16.0 Index of /download/release/v14.16.0/...
备战秋招day4
算法 242. 有效的字母异位词 class Solution {public boolean isAnagram(String s, String t) {int[] count new int[26];for(int i 0;i<s.length();i){count[s.charAt(i)-a];}for(int i 0;i<t.length();i){count[t.charAt(i)-a]--;if(count[t.charAt(i)-a]<0){r…...
【华为OD机试B卷】服务器广播、需要广播的服务器数量(C++/Java/Python)
题目 题目描述 服务器连接方式包括直接相连,间接连接。 A和B直接连接,B和C直接连接,则A和C间接连接。 直接连接和间接连接都可以发送广播。 给出一个N*N数组,代表N个服务器, matrix[i][j] 1, 则代表i和j直…...
目标检测数据集 - 手机屏幕表面表面缺陷检测数据集下载「包含VOC、COCO、YOLO三种格式」
数据集介绍:手机屏幕表面缺陷检测数据集,真实采集高质量手机屏幕表面含缺陷图片数据,数据集含多款不同型号和品牌的手机屏幕表面图片数据,包括苹果手机屏、三星手机屏、华为手机屏等数据。数据标注标签包括 Bubble 气泡/水滴、Scr…...
语音相关算法学习整理
最近看了一下百度paddlespeech的一些公开课,把课程里的视频内容大体听了一下,现在整理一下笔记。教程链接见:飞桨AI Studio星河社区-人工智能学习与实训社区 语音识别的过程可以这样简单概括: 将声音信号经过预加重、加窗、fft等…...
[C#] opencvsharp对Mat数据进行序列化或者反序列化以及格式化输出
【简要介绍】 在OpenCVSharp中,FileStorage类用于将数据(包括OpenCV的Mat类型数据)序列化为XML或YAML格式的文件,以及从这些文件中反序列化数据。以下是关于FileStorage类用法的详细说明: 写入数据(序列化…...
Linux中的TCP与UDP:理解两者的差异
在计算机网络的世界中,TCP(传输控制协议)和UDP(用户数据报协议)是两种至关重要的传输层协议。它们就像是我们日常生活中的通信方式,有着不同的使用场景和优缺点。通过一个简单的比喻,我们可以更…...
通信系统网络架构_1.局域网网络架构
当今,通信网络从大的方面主要包括局域网、广域网、移动通信网等网络形式。不同的网络会采用不同的技术进行网络构建。以下针对不同的网络给出各自的网络架构以及所采用的技术。 1.概述 局域网,即计算机局部区域网络,是一种为单一机构所拥有的…...
Pycharm 启动 Django项目 —— python篇
1、打开你的工程,在菜单栏里找到Run-->Edit Configurations 2、在打开的对话框里边选择Python,点击号 3.选择Python 4.出现了一个新的项Unnamed,你可以把它改名叫debug,好听一点 5.脚本选择你网站的manage.py,脚本参…...
6-47选择整数计算
整数计算: 用swing组件来实现整数计算,需要对整数计算的值进行校验。 import javax.swing.*; import java.awt.*; import java.awt.event.*;public class IntegerCalculator extends JFrame implements ActionListener {private JCheckBox[] checkBoxe…...
什么是Redis?|介绍与使用及特点浅记
Redis简介 Redis(Remote Dictionary Server)是一种基于内存、支持持久化的键值对存储系统,具有丰富的数据结构和高性能的特性。它不仅可以作为数据库,还可以作为缓存和消息中间件使用。Redis是单线程模型,但利用IO多路…...
LeetCode题练习与总结:只出现一次的数字Ⅱ--137
一、题目描述 给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。 示例 1: 输入:n…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
Vue 模板语句的数据来源
🧩 Vue 模板语句的数据来源:全方位解析 Vue 模板(<template> 部分)中的表达式、指令绑定(如 v-bind, v-on)和插值({{ }})都在一个特定的作用域内求值。这个作用域由当前 组件…...
Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...
Django RBAC项目后端实战 - 03 DRF权限控制实现
项目背景 在上一篇文章中,我们完成了JWT认证系统的集成。本篇文章将实现基于Redis的RBAC权限控制系统,为系统提供细粒度的权限控制。 开发目标 实现基于Redis的权限缓存机制开发DRF权限控制类实现权限管理API配置权限白名单 前置配置 在开始开发权限…...
CMS内容管理系统的设计与实现:多站点模式的实现
在一套内容管理系统中,其实有很多站点,比如企业门户网站,产品手册,知识帮助手册等,因此会需要多个站点,甚至PC、mobile、ipad各有一个站点。 每个站点关联的有站点所在目录及所属的域名。 一、站点表设计…...
Copilot for Xcode (iOS的 AI辅助编程)
Copilot for Xcode 简介Copilot下载与安装 体验环境要求下载最新的安装包安装登录系统权限设置 AI辅助编程生成注释代码补全简单需求代码生成辅助编程行间代码生成注释联想 代码生成 总结 简介 尝试使用了Copilot,它能根据上下文补全代码,快速生成常用…...
STM32 低功耗设计全攻略:PWR 模块原理 + 睡眠 / 停止 / 待机模式实战(串口 + 红外 + RTC 应用全解析)
文章目录 PWRPWR(电源控制模块)核心功能 电源框图上电复位和掉电复位可编程电压监测器低功耗模式模式选择睡眠模式停止模式待机模式 修改主频一、准备工作二、修改主频的核心步骤:宏定义配置三、程序流程:时钟配置函数解析四、注意…...
