【Android 高性能音频】AAudio 状态机 ( 创建 | 打开 Open | 开始 Started | 暂停 Paused | 刷写 Flushed | 停止 Stopped | 关闭 )

简介: 【Android 高性能音频】AAudio 状态机 ( 创建 | 打开 Open | 开始 Started | 暂停 Paused | 刷写 Flushed | 停止 Stopped | 关闭 )

文章目录

I . AAudio 音频流 创建 配置 使用 销毁 流程

II . AAudio 音频流 稳定状态 与 过渡状态

III . AAudio 音频流 状态改变 监听

IV . AAudio 音频流 状态改变 监听 实例 ( 暂停操作 )

V . AAudio 音频流 状态改变 监听 注意事项



I . AAudio 音频流 创建 配置 使用 销毁 流程


红色标题是本博客讲解的内容 , 黑色是前几篇讲过的内容 ;


使用 AAudio 音频库 , 首先需要导入 AAudio.h 头文件 ;


#include <AAudio.h>


创建 AAudio 音频流 , 需要先创建 AAudio 音频流构建器 , 然后在通过该构建器创建音频流 ;


 

//创建构建器 , AAudio 音频流通过该构建器创建
    //声明 AAudio 音频流构建器 指针
    AAudioStreamBuilder *builder = nullptr;
    //创建 AAudio 音频流构建器 , 注意传入二维指针
    aaudio_result_t result = AAudio_createStreamBuilder(&builder);


设置音频设备 ID ;


   // 设置音频流设备 ID

   AAudioStreamBuilder_setDeviceId(builder, playbackDeviceId_);

1

2

设置音频流方向 ;


   // 设置音频流方向

   AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);

1

2

设置音频设备共享模式 ;


   // 设置共享模式 , 独占模式性能更高 , 延迟更低 ; 如果 该音频设备正在被使用 , 设置失败会自动设置成 共享模式

   AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);

1

2

设置性能模式 ;


   // 设置性能模式

   AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);

1

2

设置 AAudio 音频流通道数 :


   // 设置通道个数

   AAudioStreamBuilder_setChannelCount(builder, sampleChannels_);

1

2

设置 AAudio 音频流样本格式 :


   // 设置音频格式

   AAudioStreamBuilder_setFormat(builder, sampleFormat_);

1

2

设置 AAudio 音频流缓冲区大小 : 这里的缓冲区是播放器的缓冲区 , 单位是帧 , 每帧的采样数就是通道数 , 单声道 每帧 1 个采样, 双声道立体声每帧 2 个采样 , 分别对应左右声道的采样 ;


   // 设置每帧的缓冲区大小 , 可以通过该设置达到尽可能低的延迟

   AAudioStream_setBufferSizeInFrames(playStream_, framesPerBurst_);

1

2

创建 AAudio 音频流 : 创建 AAudio 音频流 , 就是打开音频流 , 注意要在音频流参数设置完毕后才能打开音频流 ;


   // 打开音频流 ( 该步骤就是创建音频流 )

   aaudio_result_t result = AAudioStreamBuilder_openStream(builder, &playStream_);

1

2

销毁 AAudio 音频流构建器 : 在音频流创建 ( 打开 ) 完毕后 , 应该马上销毁 AAudio 音频流构建器 ;


   //销毁音频流构建器

   AAudioStreamBuilder_delete(builder);

1

2

使用 AAudio 音频流 进行 录音 或 播放操作 , 使用完毕后需要 销毁 AAudio 音频流 ;


停止 AAudio 音频流 : 如果 AAudio 音频流不再使用 , 需要马上销毁 AAudio 音频流 , 销毁前需要先将音频流停止 , 然后才能销毁 ;


//先停止音频流 , 然后才能关闭
    aaudio_result_t result = AAudioStream_requestStop(playStream_);


关闭 AAudio 音频流 : 如果 AAudio 音频流不再使用 , 需要马上销毁 AAudio 音频流 , 该流会占据音频设备资源 , 不用应马上销毁 ;


   //关闭音频流 , 关闭后 , 该音频流就彻底释放了 , 如果在使用 , 必须重新创建

   result = AAudioStream_close(playStream_);

1

2



II . AAudio 音频流 稳定状态 与 过渡状态


1 . AAudio 音频流有 6 种稳定状态 :


① Open : 音频流打开后的状态 , 就是 Open 状态 , 该状态时间很短 , 马上回自动转到下一状态 ;

② Started : 音频流打开后 , 会自动从 Open 状态转为 Started 状态 , 该状态下音频流的音频数据 , 处于流动状态 , 这个过程占生命周期的 99.999% 的时间 ;

③ Paused : 暂停状态 , 在 Started 状态下 , 如果调用 AAudioStream_requestPause() 方法 , 就会进入该状态 ; 此时播放器是暂停的 , 可以随时恢复播放 , 调用 AAudioStream_requestStart() 方法 , 可以恢复播放 , 进入 Started 状态 ;

④ Flushed : 刷写状态 , 在 Paused 状态下 , 调用 AAudioStream_requestFlush() 方法 , 就会进入该状态 , 这是将播放器缓冲区中的数据播放完毕 , 可以清空缓冲区 ; 调用 AAudioStream_requestStart() 方法 , 可以恢复播放 , 进入 Started 状态 ;

⑤ Stopped : 停止状态 , 在 Started 状态下 , 如果调用 AAudioStream_requestStop() 方法 , 就会进入该状态 ; 此时如果要恢复成 Started 状态 , 需要调用 AAudioStream_requestStart() 方法 ;

⑥ Closed : 关闭状态 , 在 Stopped 状态下 , 如果调用 AAudioStream_close() 方法 , 就会进入 Closed 状态 ; 该状态意味着 AAudio 音频流被销毁 , 无法再继续使用 ;

总结 :

处于 暂停 ( Paused ) , 停止 ( Stopped ) , 刷写 ( Flushed ) 状态下 , 可以调用 AAudioStream_requestStart() 方法 , 恢复成 Started 状态 ;

刷写 ( Flushed ) 状态 必须 有前置状态 暂停状态 ( Paused ) 才能进入该状态 , 其它状态下是无法进入 刷写状态的 ;



2 . AAudio 音频流有 5 种 过渡状态 : 过渡状态是两种稳定状态之间的状态 ;


① Starting 状态 : Open 状态 与 Started 状态 之间的 过渡状态 ;

② Pausing 状态 : Started 状态 与 Paused 状态之间的 过渡状态 ;

③ Flushing 状态 : Paused 状态 与 Flushed 状态之间的 过渡状态 ;

④ Stopping 状态 : Started 状态 与 Stopped 状态 之间的过渡状态 ;

⑤ Closing 状态 : Stopped 状态 与 Closed 状态 之间的过渡状态 ;


3 . 11 个状态之间的状态机转化关系如下图 :





III . AAudio 音频流 状态改变 监听


1 . AAudio 音频流状态监听简介 :


① 没有回调函数 : AAudio 没有提供 监听 音频流状态的 回调函数 ;

② 等待变更方法 : 目前只能使用 AAudioStream_waitForStateChange() 方法 , 该方法调用后 , 开始阻塞 , 等待 AAudio 音频流变更成 不同于 开发者指定的状态 的 其它状态后 , 继续执行下面的代码 ;


2 . AAudioStream_waitForStateChange 方法简介 :


① 函数原型 : 调用该函数时 , 当前状态应该是 inputState 状态 , 之后一直阻塞 , 该函数会等待 当前状态 , 不是 inputState 状态时 , 接触阻塞 , 继续执行下面的代码 ;

AAUDIO_API aaudio_result_t AAudioStream_waitForStateChange(
  AAudioStream *stream,
  aaudio_stream_state_t inputState,
  aaudio_stream_state_t *nextState,
  int64_t timeoutNanoseconds


② 参数 1 AAudioStream *stream : 状态机所属的 AAudio 音频流 ;

③ 参数 2 aaudio_stream_state_t inputState : 初始状态 , 调用该方法时的状态 ; 当 AAudio 音频流状态不是该状态时 , 方法阻塞解除 ;

④ 参数 3 aaudio_stream_state_t *nextState : 下一个状态的指针 , 指向一个状态值 , 该值是 解除阻塞的时刻的 AAudio 音频流状态 ; 用于在后续执行时获取当前是什么状态 ;

⑤ 参数 4 int64_t timeoutNanoseconds : 超时时间 , 该方法不可能一直阻塞代码执行 , 当超过一定时间后 , 继续执行后续的代码 ;

⑥ 返回值 aaudio_result_t : 如果成功 , 返回 AAUDIO_OK , 如果失败会返回对应的错误码 ;



IV . AAudio 音频流 状态改变 监听 实例 ( 暂停操作 )


1 . 监听暂停操作 : 在 Started 状态下 , 调用 AAudioStream_requestPause() 方法 , 设置 AAudio 音频流暂停操作 ;


2 . 理论上的状态改变 : 方法调用后 , AAudio 音频流 会立刻进入 Pausing 过渡状态 , 然后处理过渡操作 , 处理完毕后 , 进入 Paused 状态 ;


3 . 代码实现 :


① 申请暂停 : 调用 AAudioStream_requestPause() 方法之后 ;

② 当前状态 : 申请暂停后 , 当前状态马上切换成了 Pausing 状态 ;

③ 阻塞程序 : 此时 立刻调用 AAudioStream_waitForStateChange() 方法 , 其中的 第二个参数 inputState 设置成 Pausing 状态 , 该方法阻塞了程序运行 ;

④ 解除阻塞 : 当状态由 Pausing 转为其它状态 ( 一般是 Paused 状态 ) , 或者超时 , 阻塞解除 , 继续执行下面的代码 ;

//设置当状态与该状态不一致时解除阻塞
aaudio_stream_state_t inputState = AAUDIO_STREAM_STATE_PAUSING;
//用于记录下一个状态 , 将其地址传入 AAudioStream_waitForStateChange 方法 第三个参数
aaudio_stream_state_t nextState = AAUDIO_STREAM_STATE_UNINITIALIZED;
//超时时间
int64_t timeoutNanos = 100 * AAUDIO_NANOS_PER_MILLISECOND;
//申请暂停 , 方法执行后 , 状态立刻变成 Pausing 状态
result = AAudioStream_requestPause(stream);
//阻塞程序 , 直到状态由 Pausing 转为 Paused 状态 , 或超时 , 才能解除阻塞 
result = AAudioStream_waitForStateChange(stream, inputState, &nextState, timeoutNanos);




V . AAudio 音频流 状态改变 监听 注意事项


1 . 申请关闭 操作 无法监听状态 : 当前如果是 Stopped 状态 , 调用 AAudioStream_close() 方法后 AAudio 音频流会直接被删除 , 无法调用 AAudioStream_waitForStateChange 方法监听 音频流 状态 ;


2 . 监听时不要关闭流 : 如果调用了 AAudioStream_waitForStateChange () 方法监听 AAudio 音频流 状态 , 当前线程虽然在阻塞状态 , 无法操作 , 但是不要在另外的线程中关闭该 AAudio 音频流 ;


目录
相关文章
|
7月前
|
存储 缓存 Android开发
安卓Jetpack Compose+Kotlin, 使用ExoPlayer播放多个【远程url】音频,搭配Okhttp库进行下载和缓存,播放完随机播放下一首
这是一个Kotlin项目,使用Jetpack Compose和ExoPlayer框架开发Android应用,功能是播放远程URL音频列表。应用会检查本地缓存,如果文件存在且大小与远程文件一致则使用缓存,否则下载文件并播放。播放完成后或遇到异常,会随机播放下一首音频,并在播放前随机设置播放速度(0.9到1.2倍速)。代码包括ViewModel,负责音频管理和播放逻辑,以及UI层,包含播放和停止按钮。
|
7月前
|
存储 数据库 Android开发
安卓Jetpack Compose+Kotlin,支持从本地添加音频文件到播放列表,支持删除,使用ExoPlayer播放音乐
为了在UI界面添加用于添加和删除本地音乐文件的按钮,以及相关的播放功能,你需要实现以下几个步骤: 1. **集成用户选择本地音乐**:允许用户从设备中选择音乐文件。 2. **创建UI按钮**:在界面中创建添加和删除按钮。 3. **数据库功能**:使用Room数据库来存储音频文件信息。 4. **更新ViewModel**:处理添加、删除和播放音频文件的逻辑。 5. **UI实现**:在UI层支持添加、删除音乐以及播放功能。
|
7月前
|
缓存 Android开发 Kotlin
【安卓app开发】kotlin Jetpack Compose框架 | 先用OKhttp下载远程音频文件再使用ExoPlayer播放
使用 Kotlin 的 Jetpack Compose 开发安卓应用时,可以结合 OkHttp 下载远程音频文件和 ExoPlayer 进行播放。在 `build.gradle` 添加相关依赖后,示例代码展示了如何下载音频并用 ExoPlayer 播放。代码包括添加依赖、下载文件、播放文件及简单的 Compose UI。注意,示例未包含完整错误处理和资源释放,实际应用需补充这些内容。
|
7月前
|
存储 Android开发 Kotlin
开发安卓app OKhttp下载后使用MediaPlayer播放
在Android Jetpack Compose应用程序中,要使用OkHttp下载远程音频文件并在本地播放,你需要完成以下几个步骤: 1. **添加依赖**:确保`build.gradle`文件包含OkHttp和Jetpack Compose的相关依赖。 2. **下载逻辑**:创建一个`suspend`函数,使用OkHttp发起网络请求下载音频文件到本地。 3. **播放逻辑**:利用`MediaPlayer`管理音频播放状态。 4. **Compose UI**:构建用户界面,包含下载和播放音频的按钮。
|
7月前
|
Android开发 Kotlin
安卓Jetpack Compose+Kotlin, 使用ExoPlayer播放多个【本地】音频,播放完随机播放下一首,遇到播放错误,也自动播放下一首
使用Kotlin和Jetpack Compose开发的安卓应用中,实现了两个EvoPlayer同时播放res/raw目录下的音频。一个音轨播放人声(顺序播放),另一个播放背景音乐(随机播放)。每个音轨都有独立的播放和停止控制,且在播放结束或遇到错误时会自动切换到下一首。MediaPlayer置于ViewModel中,UI界面包含播放和停止按钮,控制两个音轨。每次切换音频前,还会随机调整播放速度在0.9到1.2之间。代码示例展示了如何创建ViewModel和UI以实现这一功能。
|
2月前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
29天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
53 19
|
2月前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
29天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
58 14
|
1月前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。