HarmonyOS 鸿蒙Next 搞定了SoundPool,有了音效!做了小封装。开心。

发布于 1周前 作者 yibo5220 来自 鸿蒙OS

HarmonyOS 鸿蒙Next 搞定了SoundPool,有了音效!做了小封装。开心。

话说一心想给应用加点儿音效

前段时间学这用 avPlayer,感觉多个声音同时播放时会有杂音。

结果华为自己在文档中就推荐了 soundPool:

深色代码主题
复制
使用SoundPool(音频池)提供的接口,可以实现低时延短音播放。

当应用开发时,经常需要使用一些急促简短的音效(如相机快门音效、系统通知音效等),此时建议调用SoundPool,实现一次加载,多次低时延播放。

SoundPool当前支持播放1MB以下的音频资源,大小超过1MB的长音频将截取1MB大小数据进行播放。

毕竟是老白,业余玩家,看了文档半天没弄明白。

主要是涉及两个痛点:

  1. 如何加载rawfile下的资源文件?
  2. 异步操作的逻辑?

查了一堆网上资料,琢磨了两天。

参照华为技术文档,自己尝试着封装了一下功能,测试成功!

先上图片:

再上代码:

深色代码主题
复制
import { audio } from ‘@kit.AudioKit’;
import { media } from ‘@kit.MediaKit’;
import { BusinessError } from ‘@kit.BasicServicesKit’;

@Preview @Entry @ComponentV2 struct TestSound { @Local ppSound: soundHelper | undefined = undefined aboutToAppear() { this.ppSound = new soundHelper() //创建类实例 //在OnCreated中注册有关事件 this.ppSound.onCreated = () => { //注册 【错误回调函数】监听 this.ppSound?.setErrorCallback() //注册 【音乐文件加载完毕】监听 this.ppSound?.soundPool?.on(‘loadComplete’, (soundId_: number) => { this.ppSound?.PlaySoundPool() //加载完毕即播放一次,如不需要可不注册此事件 }) //注册 【播放完毕】监听 , this.ppSound?.soundPool?.on(‘playFinished’, () => { this.ppSound?.addSoundMsg(‘声音文件播放完毕’) }) //加载声音文件,此文件放在 resources/rawfile 目录下 this.ppSound?.loadSoundRawFile(‘firework.mp3’) } //开始创建 soundPool this.ppSound.create() } aboutToDisappear(): void { //注销有关监听 this.ppSound?.soundPool?.off(“playFinished”) this.ppSound?.soundPool?.off(“loadComplete”) this.ppSound?.soundPool?.off(“error”) } build() { Column() {

  <span class="hljs-title class_">Text</span>(<span class="hljs-string">"ArkTS 测试【soundPool】播放声音"</span>)
    .<span class="hljs-title function_">margin</span>({ <span class="hljs-attr">top</span>: <span class="hljs-number">20</span> })
    .<span class="hljs-title function_">fontSize</span>(<span class="hljs-string">'15'</span>)
    .<span class="hljs-title function_">fontWeight</span>(<span class="hljs-title class_">FontWeight</span>.<span class="hljs-property">Bolder</span>)
    .<span class="hljs-title function_">fontColor</span>(<span class="hljs-string">'#aaaaaa'</span>)

  <span class="hljs-title class_">Text</span>(<span class="hljs-string">"By 端州老白"</span>)
    .<span class="hljs-title function_">margin</span>({ <span class="hljs-attr">top</span>: <span class="hljs-number">5</span> })
    .<span class="hljs-title function_">fontSize</span>(<span class="hljs-string">'14'</span>)
    .<span class="hljs-title function_">fontColor</span>(<span class="hljs-string">'#888888'</span>)

  <span class="hljs-title class_">Button</span>(<span class="hljs-string">'点击重播'</span>)
    .<span class="hljs-title function_">margin</span>({ <span class="hljs-attr">top</span>: <span class="hljs-number">10</span> })
    .<span class="hljs-title function_">onClick</span>(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-variable language_">this</span>.<span class="hljs-property">ppSound</span>?.<span class="hljs-title class_">PlaySoundPool</span>()
    })

  <span class="hljs-title class_">Text</span>(<span class="hljs-string">"事件信息:\n\n"</span> + <span class="hljs-variable language_">this</span>.<span class="hljs-property">ppSound</span>?.<span class="hljs-property">soundMsg</span>)
    .<span class="hljs-title function_">fontSize</span>(<span class="hljs-number">12</span>)
    .<span class="hljs-title function_">margin</span>(({ <span class="hljs-attr">top</span>: <span class="hljs-number">10</span> }))
    .<span class="hljs-title function_">fontColor</span>((<span class="hljs-string">'#aaaaaa'</span>))
    .<span class="hljs-title function_">width</span>(<span class="hljs-string">'90%'</span>)
    .<span class="hljs-title function_">backgroundColor</span>(<span class="hljs-string">'#333333'</span>)
    .<span class="hljs-title function_">borderRadius</span>(<span class="hljs-number">5</span>)
    .<span class="hljs-title function_">padding</span>(<span class="hljs-number">5</span>)
}
.<span class="hljs-title function_">backgroundColor</span>(<span class="hljs-string">'#444444'</span>)
.<span class="hljs-title function_">width</span>(<span class="hljs-string">'100%'</span>)
.<span class="hljs-title function_">height</span>(<span class="hljs-string">'100%'</span>)

} }

@ObservedV2 class soundHelper { soundPool: media.SoundPool | undefined streamId: number = 0 soundId: number = 0 private fileSize: number = -1; private fd: number = 0; private rowFile: string = “” //soundMsg仅在本例子中使用,为了将调试信息加载到页面。 @Trace soundMsg: string = “” // audioRenderInfo中的参数usage取值为STREAM_USAGE_UNKNOWN,STREAM_USAGE_MUSIC,STREAM_USAGE_MOVIE, // STREAM_USAGE_AUDIOBOOK时,SoundPool播放短音时为混音模式,不会打断其他音频播放。 audioRendererInfo: audio.AudioRendererInfo = { usage: audio.StreamUsage.STREAM_USAGE_MUSIC, rendererFlags: 1 } playParameters: media.PlayParameters = { loop: 0, // 循环1次 rate: audio.AudioRendererRate.RENDER_RATE_NORMAL, // 正常倍速 leftVolume: 1, // range = 0.0-1.0 rightVolume: 1, // range = 0.0-1.0 priority: 0, // 最低优先级 } //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // onCreated: 在 soundPool创建成功时调用 // 注册 loadComplete、playFinished等事件应当在此函数内。 // 可在UI的 aboutToAppear() 中重写此函数 onCreated: () => void = () => { console.log(‘Created!’) }

//↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ //addSoundMsg: 仅在本例子中使用,为了将调试信息加载到页面。 addSoundMsg(msg: string) { this.soundMsg = ‘\n @’ + (new Date()).toLocaleString() + ‘\n’ + msg + this.soundMsg }

async create() { //创建soundPool实例 media.createSoundPool(20, this.audioRendererInfo).then(async (soundpool_: media.SoundPool) => { if (soundpool_ != null) { this.addSoundMsg(成功创建 soundPool) console.info(‘成功创建 soundPool’); this.soundPool = soundpool_; this.onCreated(); //调用 onCreated函数。关键步骤之一 } else { console.error(‘创建 soundPool 失败!’); this.addSoundMsg(创建 soundPool 失败!) } }).catch((error: BusinessError) => { console.error(soundpool catchCallback, error message:<span class="hljs-subst">${error.message}</span>); });

}

//↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ //loadSoundRawFile:加载声音文件。此函数指定的 soundFileName 应当在 resources/rowfile 下 async loadSoundRawFile(soundFileName: string) { this.rowFile = soundFileName this.addSoundMsg("即将加载声音文件 : " + this.rowFile) // 加载音频资源 let fileDescriptor = await getContext(this).resourceManager.getRawFd(soundFileName); //this.soundMsg = soundHelper:fd:${fileDescriptor.fd} \n + this.soundMsg this.soundPool?.load(fileDescriptor.fd, fileDescriptor.offset, fileDescriptor.length).then((soundid: number) => { this.soundId = soundid this.addSoundMsg(成功加载声音文件 : <span class="hljs-subst">${<span class="hljs-variable language_">this</span>.rowFile}</span> soundid:<span class="hljs-subst">${<span class="hljs-variable language_">this</span>.soundId}</span>) }) }

//设置错误类型监听 setErrorCallback() { this.soundPool?.on(‘error’, (error: BusinessError) => { this.addSoundMsg(发生错误: <span class="hljs-subst">${error.message}</span>) console.info(发生错误: <span class="hljs-subst">${error.message}</span>); }) }

//播放声音文件 async PlaySoundPool() { this.addSoundMsg(即将播放声音,soundid:<span class="hljs-subst">${<span class="hljs-variable language_">this</span>.soundId}</span>) this.soundPool?.play(this.soundId, this.playParameters, (error, streamID: number) => { if (error) { this.addSoundMsg(播放声音错误:<span class="hljs-subst">${error.code}</span>-&gt;<span class="hljs-subst">${error.message}</span>) console.info(播放声音错误:<span class="hljs-subst">${error.code}</span>-&gt;<span class="hljs-subst">${error.message}</span>) } else { this.streamId = streamID; this.addSoundMsg(成功播放声音,streamid:<span class="hljs-subst">${<span class="hljs-variable language_">this</span>.streamId}</span>) console.info(成功播放声音,streamid:<span class="hljs-subst">${<span class="hljs-variable language_">this</span>.streamId}</span>); } }); } }


更多关于HarmonyOS 鸿蒙Next 搞定了SoundPool,有了音效!做了小封装。开心。的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html

6 回复
问下楼主 这个可以实现短音频 重复播放吗 ? 一直重复那种

更多关于HarmonyOS 鸿蒙Next 搞定了SoundPool,有了音效!做了小封装。开心。的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


playParameters中有个参数,可以控制循环次数
另外好像只支持播放5秒钟短音频

要想一直播放,可能需要用到 background task kit。也可以用set TimeOut函数,但比较麻烦些。

找HarmonyOS工作还需要会Flutter技术的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17

感谢大佬分享!

恭喜你在HarmonyOS 鸿蒙Next上成功搞定SoundPool并实现了音效的小封装,这是一个非常实用的功能提升。

在HarmonyOS中,SoundPool主要用于播放较短的音频文件,如游戏中的音效、按钮点击声等。它提供了灵活的音频管理功能,允许你同时播放多个音频流,并控制每个音频流的音量、播放速度等属性。

你提到的小封装,很可能是对SoundPool的使用进行了一层封装,使得在项目中调用音效更加简便和统一。这样的封装有助于减少重复代码,提高开发效率,同时也便于后续的维护和升级。

值得注意的是,虽然你已经实现了音效的播放,但还需要确保音频文件的格式、大小等符合HarmonyOS的要求,以避免出现播放异常或性能问题。此外,对于音频资源的管理也是非常重要的,要避免内存泄漏和音频资源的浪费。

如果在后续的开发过程中遇到关于SoundPool的更多问题,比如音频播放异常、资源加载失败等,你可以查阅HarmonyOS的官方文档或相关开发社区获取更多的帮助。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html 。希望你在HarmonyOS的开发之路上越走越远,取得更多的成果!

回到顶部