鸿蒙(API 12 Beta2版)媒体开发【使用AudioRenderer开发音频播放功能】

news2025/2/22 14:53:30

音频播放开发概述

如何选择音频播放开发方式

系统提供了多样化的API,来帮助开发者完成音频播放的开发,不同的API适用于不同音频数据格式、音频资源来源、音频使用场景,甚至是不同开发语言。因此,选择合适的音频播放API,有助于降低开发工作量,实现更佳的音频播放效果。

  • [AudioRenderer]:用于音频输出的ArkTS/JS API,仅支持PCM格式,需要应用持续写入音频数据进行工作。应用可以在输入前添加数据预处理,如设定音频文件的采样率、位宽等,要求开发者具备音频处理的基础知识,适用于更专业、更多样化的媒体播放应用开发。
  • [AudioHaptic]:用于音振协同播放的ArkTS/JS API,适用于需要在播放音频时同步发起振动的场景,如来电铃声随振、键盘按键反馈、消息通知反馈等。
  • [OpenSL ES]:一套跨平台标准化的音频Native API,同样提供音频输出能力,仅支持PCM格式,适用于从其他嵌入式平台移植,或依赖在Native层实现音频输出功能的播放应用使用。
  • [OHAudio]:用于音频输出的Native API,此API在设计上实现归一,同时支持普通音频通路和低时延通路。仅支持PCM格式,适用于依赖Native层实现音频输出功能的场景。

除上述方式外,也可以通过Media Kit实现音频播放。

  • [AVPlayer]:用于音频播放的ArkTS/JS API,集成了流媒体和本地资源解析、媒体资源解封装、音频解码和音频输出功能。可用于直接播放mp3、m4a等格式的音频文件,不支持直接播放PCM格式文件。
  • [SoundPool]:低时延的短音播放ArkTS/JS API,适用于播放急促简短的音效,如相机快门音效、按键音效、游戏射击音效等。

开发音频播放应用须知

应用如果要实现后台播放或熄屏播放,需要同时满足:

  1. 使用媒体会话功能注册到系统内统一管理,否则在应用进入后台时,播放将被强制停止。
  2. 申请长时任务避免进入挂起(Suspend)状态。

当应用进入后台,播放被中断,如果被媒体会话管控,将打印日志“pause id”;如果没有该日志,则说明被长时任务管控。

AudioRenderer是音频渲染器,用于播放PCM(Pulse Code Modulation)音频数据,相比AVPlayer而言,可以在输入前添加数据预处理,更适合有音频开发经验的开发者,以实现更灵活的播放功能。

开发指导

使用AudioRenderer播放音频涉及到AudioRenderer实例的创建、音频渲染参数的配置、渲染的开始与停止、资源的释放等。本开发指导将以一次渲染音频数据的过程为例,向开发者讲解如何使用AudioRenderer进行音频渲染,建议搭配[AudioRenderer的API说明]阅读。

下图展示了AudioRenderer的状态变化,在创建实例后,调用对应的方法可以进入指定的状态实现对应的行为。需要注意的是在确定的状态执行不合适的方法可能导致AudioRenderer发生错误,建议开发者在调用状态转换的方法前进行状态检查,避免程序运行产生预期以外的结果。

为保证UI线程不被阻塞,大部分AudioRenderer调用都是异步的。对于每个API均提供了callback函数和Promise函数,以下示例均采用callback函数。

图1 AudioRenderer状态变化示意图

1

在进行应用开发的过程中,建议开发者通过[on(‘stateChange’)]方法订阅AudioRenderer的状态变更。因为针对AudioRenderer的某些操作,仅在音频播放器在固定状态时才能执行。如果应用在音频播放器处于错误状态时执行操作,系统可能会抛出异常或生成其他未定义的行为。

  • prepared状态: 通过调用[createAudioRenderer()]方法进入到该状态。
  • running状态: 正在进行音频数据播放,可以在prepared状态通过调用[start()]方法进入此状态,也可以在paused状态和stopped状态通过调用[start()]方法进入此状态。
  • paused状态: 在running状态可以通过调用[pause()]方法暂停音频数据的播放并进入paused状态,暂停播放之后可以通过调用[start()]方法继续音频数据播放。
  • stopped状态: 在paused/running状态可以通过[stop()]方法停止音频数据的播放。
  • released状态: 在prepared、paused、stopped等状态,用户均可通过[release()]方法释放掉所有占用的硬件和软件资源,并且不会再进入到其他的任何一种状态了。

开发步骤及注意事项

  1. 配置音频渲染参数并创建AudioRenderer实例,音频渲染参数的详细信息可以查看[AudioRendererOptions]。
import { audio } from '@kit.AudioKit';

let audioStreamInfo: audio.AudioStreamInfo = {
  samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率
  channels: audio.AudioChannel.CHANNEL_2, // 通道
  sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式
  encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式
};

let audioRendererInfo: audio.AudioRendererInfo = {
  usage: audio.StreamUsage.STREAM_USAGE_VOICE_COMMUNICATION,
  rendererFlags: 0
};

let audioRendererOptions: audio.AudioRendererOptions = {
  streamInfo: audioStreamInfo,
  rendererInfo: audioRendererInfo
};

audio.createAudioRenderer(audioRendererOptions, (err, data) => {
  if (err) {
    console.error(`Invoke createAudioRenderer failed, code is ${err.code}, message is ${err.message}`);
    return;
  } else {
    console.info('Invoke createAudioRenderer succeeded.');
    let audioRenderer = data;
  }
});
  1. 调用on(‘writeData’)方法,订阅监听音频数据写入回调。
import { BusinessError } from '@kit.BasicServicesKit';
import { fileIo } from '@kit.CoreFileKit';

let bufferSize: number = 0;
class Options {
  offset?: number;
  length?: number;
}

let path = getContext().cacheDir;
//确保该路径下存在该资源
let filePath = path + '/StarWars10s-2C-48000-4SW.wav';
let file: fileIo.File = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY);

let writeDataCallback = (buffer: ArrayBuffer) => {
  
  let options: Options = {
    offset: bufferSize,
    length: buffer.byteLength
  }
  fileIo.readSync(file.fd, buffer, options);
  bufferSize += buffer.byteLength;
}

audioRenderer.on('writeData', writeDataCallback);
  1. 调用start()方法进入running状态,开始渲染音频。
import { BusinessError } from '@kit.BasicServicesKit';

audioRenderer.start((err: BusinessError) => {
  if (err) {
    console.error(`Renderer start failed, code is ${err.code}, message is ${err.message}`);
  } else {
    console.info('Renderer start success.');
  }
});
  1. 调用stop()方法停止渲染。
import { BusinessError } from '@kit.BasicServicesKit';

audioRenderer.stop((err: BusinessError) => {
  if (err) {
    console.error(`Renderer stop failed, code is ${err.code}, message is ${err.message}`);
  } else {
    console.info('Renderer stopped.');
  }
});
  1. 调用release()方法销毁实例,释放资源。
import { BusinessError } from '@kit.BasicServicesKit';

audioRenderer.release((err: BusinessError) => {
  if (err) {
    console.error(`Renderer release failed, code is ${err.code}, message is ${err.message}`);
  } else {
    console.info('Renderer released.');
  } 
});

完整示例

下面展示了使用AudioRenderer渲染音频文件的示例代码。

import { audio } from '@kit.AudioKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { fileIo } from '@kit.CoreFileKit';

const TAG = 'AudioRendererDemo';

class Options {
  offset?: number;
  length?: number;
}

let context = getContext(this);
let bufferSize: number = 0;
let renderModel: audio.AudioRenderer | undefined = undefined;
let audioStreamInfo: audio.AudioStreamInfo = {
  samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率
  channels: audio.AudioChannel.CHANNEL_2, // 通道
  sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式
  encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式
}
let audioRendererInfo: audio.AudioRendererInfo = {
  usage: audio.StreamUsage.STREAM_USAGE_MUSIC, // 音频流使用类型
  rendererFlags: 0 // 音频渲染器标志
}
let audioRendererOptions: audio.AudioRendererOptions = {
  streamInfo: audioStreamInfo,
  rendererInfo: audioRendererInfo
}
let path = getContext().cacheDir;
//确保该路径下存在该资源
let filePath = path + '/StarWars10s-2C-48000-4SW.wav';
let file: fileIo.File = fileIo.openSync(filePath, fileIo.OpenMode.READ_ONLY);

let writeDataCallback = (buffer: ArrayBuffer) => {
  let options: Options = {
    offset: bufferSize,
    length: buffer.byteLength
  }
  fileIo.readSync(file.fd, buffer, options);
   bufferSize += buffer.byteLength;
}

// 初始化,创建实例,设置监听事件
function init() {
  audio.createAudioRenderer(audioRendererOptions, (err, renderer) => { // 创建AudioRenderer实例
    if (!err) {
      console.info(`${TAG}: creating AudioRenderer success`);
      renderModel = renderer;
      if (renderModel !== undefined) {
        (renderModel as audio.AudioRenderer).on('writeData', writeDataCallback);
      }
    } else {
      console.info(`${TAG}: creating AudioRenderer failed, error: ${err.message}`);
    }
  });
}

// 开始一次音频渲染
function start() {
  if (renderModel !== undefined) {
    let stateGroup = [audio.AudioState.STATE_PREPARED, audio.AudioState.STATE_PAUSED, audio.AudioState.STATE_STOPPED];
    if (stateGroup.indexOf((renderModel as audio.AudioRenderer).state.valueOf()) === -1) { // 当且仅当状态为prepared、paused和stopped之一时才能启动渲染
      console.error(TAG + 'start failed');
      return;
    }
    // 启动渲染
    (renderModel as audio.AudioRenderer).start((err: BusinessError) => {
      if (err) {
        console.error('Renderer start failed.');
      } else {
        console.info('Renderer start success.');
      }
    });
  }
}

// 暂停渲染
function pause() {
  if (renderModel !== undefined) {
    // 只有渲染器状态为running的时候才能暂停
    if ((renderModel as audio.AudioRenderer).state.valueOf() !== audio.AudioState.STATE_RUNNING) {
      console.info('Renderer is not running');
      return;
    }
    // 暂停渲染
    (renderModel as audio.AudioRenderer).pause((err: BusinessError) => {
      if (err) {
        console.error('Renderer pause failed.');
      } else {
        console.info('Renderer pause success.');
      }
    });
  }
}

// 停止渲染
async function stop() {
  if (renderModel !== undefined) {
    // 只有渲染器状态为running或paused的时候才可以停止
    if ((renderModel as audio.AudioRenderer).state.valueOf() !== audio.AudioState.STATE_RUNNING && (renderModel as audio.AudioRenderer).state.valueOf() !== audio.AudioState.STATE_PAUSED) {
      console.info('Renderer is not running or paused.');
      return;
    }
    // 停止渲染
    (renderModel as audio.AudioRenderer).stop((err: BusinessError) => {
      if (err) {
        console.error('Renderer stop failed.');
      } else {
        fileIo.close(file);
        console.info('Renderer stop success.');
      }
    });
  }
}

// 销毁实例,释放资源
async function release() {
  if (renderModel !== undefined) {
    // 渲染器状态不是released状态,才能release
    if (renderModel.state.valueOf() === audio.AudioState.STATE_RELEASED) {
      console.info('Renderer already released');
      return;
    }
    // 释放资源
    (renderModel as audio.AudioRenderer).release((err: BusinessError) => {
      if (err) {
        console.error('Renderer release failed.');
      } else {
        console.info('Renderer release success.');
      }
    });
  }
}

当同优先级或高优先级音频流要使用输出设备时,当前音频流会被中断,应用可以自行响应中断事件并做出处理。

最后呢

很多开发朋友不知道需要学习那些鸿蒙技术?鸿蒙开发岗位需要掌握那些核心技术点?为此鸿蒙的开发学习必须要系统性的进行。

而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造的《鸿蒙NEXT星河版OpenHarmony开发文档》里面内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点

如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。下面是鸿蒙开发的学习路线图。

在这里插入图片描述

针对鸿蒙成长路线打造的鸿蒙学习文档。话不多说,我们直接看详细鸿蒙(OpenHarmony )手册(共计1236页)与鸿蒙(OpenHarmony )开发入门视频,帮助大家在技术的道路上更进一步。

  • 《鸿蒙 (OpenHarmony)开发学习视频》
  • 《鸿蒙生态应用开发V2.0白皮书》
  • 《鸿蒙 (OpenHarmony)开发基础到实战手册》
  • OpenHarmony北向、南向开发环境搭建
  • 《鸿蒙开发基础》
  • 《鸿蒙开发进阶》
  • 《鸿蒙开发实战》

在这里插入图片描述

总结

鸿蒙—作为国家主力推送的国产操作系统。部分的高校已经取消了安卓课程,从而开设鸿蒙课程;企业纷纷跟进启动了鸿蒙研发。

并且鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,未来将会支持 50 万款的应用。那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行! 自↓↓↓拿

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

conda pack迁移环境

文章目录 下载conda pack打包已有环境还原环境 因为有的服务器没有网络,如果想要安装自己的虚拟环境,就需要在有网络的服务器安装好环境后迁移到没有网络的服务器。conda-pack是一个命令行工具,用于打包 conda 环境,pip inatall和…

【Python实战】完美实现 WPS 会员功能,自动化处理 PDF 文档(建议收藏)

数字化办公已成常态,文档管理和处理是很多小伙伴的日常工作。 PDF(Portable Document Format)文档因其跨平台兼容性和格式固定性而备受青睐。 然而,对于非WPS会员用户而言,一些高级功能如批量处理、格式转换、添加水…

【SpringMVC】详细介绍SpringMVC的执行流程

目录 1. 概念 2.SpringMVC工作原理 3. springMVC的简单使用 1.在pom.xml中导入相关依赖 2.在web.xml中配置dispatcherServlet 3.创建springMVC.xml核心配置文件 1. 概念 什么是MVC? MVC是下面三个组件的简写,模型(Model)、视图…

mathtype7永久激活密钥咋子哪里获取?2024最新破解版下载附安装教程

在数字化时代,我们每天都与文字和符号打交道。无论是撰写论文、准备报告还是编写程序,数学公式的输入都是不可或缺的一环。但你有没有遇到过这样的困扰:在Word文档中编辑复杂的数学公式时,操作繁琐且不直观? 别担心&a…

鸿蒙图形开发【3D引擎接口示例】

介绍 本实例主要介绍3D引擎提供的接口功能。提供了ohos.graphics.scene中接口的功能演示。 3D引擎渲染的画面会被显示在Component3D这一控件中。点击按钮触发不同的功能,用户可以观察渲染画面的改变。 效果预览 使用说明 在主界面,可以点击按钮进入不…

【书生大模型实战营第三期】基础岛 第1关 书生大模型全链路开源体系

欢迎大家参与第三期书生大模型实战营!!! 1. 书生浦语开源历程 从23年7月开始,直到今年7月,书生浦语先后开源了 InternLM、InternLM2 核性能更好的 InternLM2.5。 2. InternLM2.5 的优势 其中,最新的 Intern…

计算机语言-CSP初赛知识点整理

历年真题 [2020-CSP-J-第2题] 编译器的主要功能( ) A. 将源程序翻译成机器指令代码 B. 将源程序重新组合 C. 将低级语言翻译成高级语言 D. 将一种高级语言翻译成另一种高级语言 [2021-CSP-J-第1题] 以下不属于面向对象程序设计语言的是()。 A. C B. Pyt…

【读点论文】场景图像中文本检测和识别关键技术研究-博士学位论文

文本是人类获取信息及社会交流的重要手段,从图像准确读取文本对人类的生产生活至关重要。现有方法通常将文本读取细分为文本检测、文本识别、端到端文本识别三个子任务。其中文本检测的目的是定位出图像中文本的位置,文本识别旨在识别出文本区域的字符序…

高仲富:49岁搞AI,白天种菜卖菜,晚上学数学搞程序

这是《开发者说》的第13期,本期我们邀请的开发者是高仲富,曾是一位数学老师,自学成为一名程序员,在北京漂过,后逃回了成都,一边与病魔抗争,一边写代码,一写就是15年,制作…

Electron 集成SQlite FTS5 实现百万级数据的倒排索引

背景 在产品迭代时,个人版产品已经将联系人和消息实时备份到本地,而消息的备份的目的仍然是为了快速查询对自己有用的上下文,并能快速定位到这些用户以及这些有用的信息。另外包括未来喂给 chatgpt-4o 的数据也是需要调用搜索获取的&#xff…

39. 647. 回文子串,516.最长回文子序列, 动态规划总结

确定dp数组以及下标的含义。如果大家做了很多这种子序列相关的题目,在定义dp数组的时候 很自然就会想题目求什么,我们就如何定义dp数组。绝大多数题目确实是这样,不过本题如果我们定义,dp[i] 为 下标i结尾的字符串有 dp[i]个回文串…

Weblogic 漏洞(详细)

一.weblogic弱口令 访问一下默认用户名:weblogic 密码: Oracle123 然后点击安装 然后再点击上传文件 将jsp木马打包,改为war上传一直下一步,最后点完成 然后使用工具连接 二.CVE-2017-3506 使用工具检测,存在漏洞 …

【C++】模拟实现stack

🦄个人主页:修修修也 🎏所属专栏:实战项目集 ⚙️操作环境:Visual Studio 2022 ​ 目录 一.了解项目功能 📌了解stack官方标准 📌了解模拟实现stack 二.逐步实现项目功能模块及其逻辑详解 📌实现stack成员变量 &…

[pdf]240道《软件方法》强化自测题业务建模需求分析共201页(202408更新)

链接: http://www.umlchina.com/url/quizad.html 如果需要提取码:umlc 文件夹中的“潘加宇《软件方法》强化自测题业务建模需求分析共240题.pdf”

【MATLAB第107期】基于MATLAB的Morris全局敏感性分析模型(无目标函数)

【MATLAB第107期】基于MATLAB的Morris全局敏感性分析模型(无目标函数) 一、原理介绍 1.基本原理: Morris方法采用概率均匀抽样的方式估计每个模型输入因子在输出结果中的重要性,通过比较系统在不同输入参数值上的输出结果变化来…

智观察 | 行业赛道里的AI大模型

‍ “AI改变世界”被炒得热火朝天,结果就换来AI聊天? 实际上,在日常娱乐之下,AI正在暗暗“憋大招”,深入各行各业,发挥更专业的作用。 自动驾驶 最近“萝卜快跑”霸榜热搜长达一周,让无人驾…

ECMAScript 6 入门 学习 日志笔记 2024/8/6 13:59

就读书籍: ECMAScript 6 入门 作者:阮一峰https://www.ruanyifeng.com/ 个人理解笔记 { } 块级 函数不能先用后声明 Let 优先函数表达 不可重复声明同一变量 { letfunction (){ } } 不谈其他,只要在{ } 中即可 ,简单暴力理解 const 和 let 类似 …

语言模型-神经网络模型(二)

神经网络模型语言模型 神经网络模型神经网络的分类神经网络模型和Ngram对比应用一-话者分离对比优劣 应用二-数字归一化应用三-文本打标 神经网络模型 释义: 与ngram模型相似使用,前n个词预测下一个词,输出在字表上的概率分布;过…

【Playwright+Python】使用Playwright进行API接口测试

在当今的自动化测试领域,结合Web UI和API接口测试已成为提升测试覆盖率和效率的关键。Playwright作为一个强大的自动化测试工具,除了在Web UI测试中大放异彩,还能与Python结合,实现强大的API接口测试功能。本文将带你探索如何使用…