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;

    @Override
    protected 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() {
            @Override
            public void onClick(View v) {
                if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
                    mediaPlayer.start();
                }
            }
        });

        stopButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                    mediaPlayer.stop();
                    // 重新准备MediaPlayer
                    mediaPlayer.prepareAsync();
                }
            }
        });
    }

    @Override
    protected 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.1kHz
    android::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 module
    if (hw_get_module(AUDIO_HARDWARE_MODULE_ID, (const hw_module_t **)&module) == 0) {
        LOGD("Audio module loaded");

        // Open the audio hardware device
        if (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_out
                audio_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 data

                    streamOut->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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/743505.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

游戏AI的创造思路-技术基础-深度学习(1)

他来了&#xff0c;他来啦&#xff0c;后面歌词忘了~~~~~ 开谈深度学习&#xff0c;填上一点小坑&#xff0c;可又再次开掘大洞 -.-b 目录 1. 定义 2. 深度学习的发展历史和典型事件 3. 深度学习常用算法 3.1. 卷积神经网络&#xff08;CNN&#xff09; 3.1.1. 算法形成过…

【内网穿透】FRP 跨平台内网穿透 支持windows linux x86_64 arm64 端口范围映射

AI提供的资料&#xff1a; FRP&#xff08;Fast Reverse Proxy&#xff09;是一个专为内网穿透设计的高性能反向代理程序。以下是一些关于FRP的详细资料&#xff0c;帮助您更好地理解和使用这一工具&#xff1a; 核心特点&#xff1a; 内网穿透&#xff1a;能够将位于内网的…

异常处理的例子

多个except 示例代码如下 try:a input("请输入被除数&#xff1a;")b input("请输入除数&#xff1a;")c float(a)/float(b)print(c)except ZeroDivisionError:print("异常&#xff1a;除数不能为0") except TypeError:print("异常&am…

Vlog视频如何剪辑 Vlog视频剪辑逻辑 视频剪辑制作教程

剪出感觉、剪出情绪&#xff0c;给Vlog视频注入高级氛围感。不用购买昂贵的前期设备&#xff0c;正确地剪辑思维搭配一款好用的视频剪辑软件&#xff0c;你也能剪出令人惊艳的Vlog作品。请收藏本文并反复练习&#xff0c;相信在不久的将来&#xff0c;您的作品必会让人眼前一亮…

【linux基础awk】如何基于强大的awk打印列、计算

打印列 awk {print $1} test.txt#-F参数去指定分隔的字符 awk -F "," {print $1,$2} file 匹配打印列 awk /a/ {print $4 "\t" $3} test.txt筛选数值 仅打印那些含有多于18个字符的行。awk length($0) > 18 test.txt 统计数目 #统计行数 less num…

【Linux】进程信号_1

文章目录 八、进程信号1.信号 未完待续 八、进程信号 1.信号 信号和信号量之间没有任何关系。信号是Linux系统提供的让用户/进程给其他进程发送异步信息的一种方式。 常见信号&#xff1a; 当信号产生时&#xff0c;可选的处理方式有三种&#xff1a;①忽略此信号。②执行该…

机器人控制系列教程之运动规划(2)

简介 在笛卡尔坐标空间中轨迹规划时&#xff0c;首先用位置矢量和旋转矩阵表示所有相应的机器人节点&#xff0c;其次在所有路径段插值计算相对的位置矢量和旋转矩阵&#xff0c;依次得出笛卡尔坐标空间中的轨迹序列通过求解运动学逆问题得到相应关节位置参数。 优点&#xf…

鸿蒙北向开发 ubuntu20.04 gn + ninja环境傻瓜式搭建闭坑指南

ninja跟gn都是比较时髦的东西,由歪果仁维护,如果走下载源码并编译这种流程的话需要走github跟google官网下载,国内的用网环境相信各位傻瓜都知道,github跟google这几年基本是属于连不上的状态,好在你看的鸿蒙项目跟国内的一些软件大厂已经帮你爬过梯子了,ninja工具跟gn工具已经…

PTP时间同步协议简介

注意&#xff0c;上图中 t1 t4 都是主机时间。 t2 t3 都是从机时间。 总延迟为 (t4 - t1) - (t3 - t2) 单边的平均延迟为 ((t4 - t1) - (t3 - t2) ) / 2&#xff0c;公式d1 两边的时钟偏差为 (((t3 t2) / 2)) - ((t4 t1) / 2) 也可以通过下面2个式子 t2 offset delay t1…

CAN报文的发送类型-OnChange、OnWrite、IfActive、Repetition

CAN报文的发送类型分为基本发送类型和混合发送类型两大类 CAN基本发送类型包括Cyclic周期发送、OnChange变化时发送、OnWrite写入时发送和IfActive有效时发送。基本发送类型中的Cyclic称为周期型,而其他3个类型称为事件型(Event)。发送次数是通过定义Repetition重复次数来实…

实用软件下载:ABBYY Finereader 15最新安装包及详细安装教程

数据表明FineReader PDF提供实用、简易的工具&#xff0c;将纸质文档和PDF结合起来&#xff0c;提高了数字工作场所的效率。我们都知道 即时背景识别:使不可搜索的PDF能够在工作中立即使用。值得一提的是文档转换更精准&#xff1a;OCR技术&#xff0c;它提高了PDF转换、布局保…

深入解析Elasticsearch 8.4.1:Mapping与字段类型实战指南

标题Elasticsearch8.4.1常见字段类型介绍 Mapping概述 在Elasticsearch中&#xff0c;Mapping定义了索引中每个字段的类型、属性以及设置。Mapping的作用是告诉Elasticsearch如何索引文档中的字段&#xff0c;包括如何分析字段值以及如何存储它们。 常见字段类型 字符串类型…

el-progress进度条,format效果实现

需要实现如下效果图: 实现的需求是,取数组对象里面,amount最大的值作为100%,其余按照这个标准进行计算得到显示的进度条百分比 <template><div class="app-container"> <div class="chart-container"><el-row :gutter="1…

睡眠剥夺对记忆巩固的神经生物学影响

近期&#xff0c;《自然》杂志刊载的研究揭示了睡眠不足对记忆相关神经信号的不利影响&#xff0c;强调了即使在后续恢复充分睡眠的情况下&#xff0c;这种损害亦难以完全逆转。 神经元作为大脑的基本功能单位&#xff0c;其活动并非孤立进行&#xff0c;而是通过复杂的网络连接…

Day45

Day45 jQuery动画 显示和隐藏 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title><script src"js/jquery-1.8.2.js" type"text/javascript" charset"utf-8"></script&…

Webpack: 借助 Babel+TS+ESLint 构建现代 JS 工程环境

概述 Webpack 场景下处理 JavaScript 的三种常用工具&#xff1a;Babel、TypeScript、ESLint 的历史背景、功能以及接入 Webpack 的步骤借助这些工具&#xff0c;我们能构建出更健壮、优雅的 JavaScript 应用 使用 Babel ECMAScript 6.0(简称 ES6) 版本补充了大量提升 JavaSc…

【Linux】进程 | 控制块pcb | task_struct | 创建子进程fork

目录 Ⅰ. 进程的概念&#xff08;Process&#xff09; 1. 什么是进程&#xff1f; 2. 多进程管理 3. 进程控制块&#xff08;PCB&#xff09; task_struct 的结构 Ⅱ. 进程查看与管理 1. 使用指令查看进程 2. /proc 查看进程信息 3. 获取进程 ID 4. 创建子进程 原因…

Vue2-表单与v-model

1.Vue表单使用 表单输入绑定&#xff1a;表单输入绑定 — Vue.js #v-model双向数据绑定new Vue({template: <div>/* 用户修改 , vue-data数据同步修改(内存) */<input v-model"message" placeholder"edit..."><p>message: {{message}}&…

2024年虚拟现实、图像和信号处理国际学术会议(ICVISP 2024,8月2日-4)

2024年虚拟现实、图像和信号处理国际学术会议&#xff08;ICVISP 2024&#xff09;将于2024年8月2-4日在中国厦门召开。ICVISP 2024将围绕“虚拟现实、图像和信号处理”的最新研究领域&#xff0c; 为来自国内外高等院校、科学研究所、企事业单位的专家、教授、学者、工程师等提…

Python 参数类型

一 理解Python中的Parameters & Arguments Parameters&#xff1a;形参 Arguments&#xff1a;实参 二 Python的实参&#xff08;Arguments&#xff09;类型 实参类型总结 位置参数&#xff08;Positional Arguments&#xff09; &#xff1a;函数调用时通过入参的顺序来…