flutter开发实战-实现音效soundpool播放音频及控制播放暂停停止设置音量
flutter开发实战-实现音效soundpool播放音频
最近开发过程中遇到低配置设备时候,在Media播放音频时候出现音轨限制问题。所以将部分音频采用音效sound来播放。
一、音效类似iOS中的Sound
在iOS中使用sound来播放mp3音频示例如下
// 通过通知的Sound设置为voip_call.caf,这里播放一段空白音频,音频结束后结束震动NSString *path = [[NSBundle mainBundle] pathForResource:@"blank_call.mp3" ofType:nil];AudioServicesCreateSystemSoundID((__bridge CFURLRef)[NSURL fileURLWithPath:path], &soundID);AudioServicesAddSystemSoundCompletion(soundID, NULL, NULL, soundCompleteCallback, NULL);AudioServicesAddSystemSoundCompletion(kSystemSoundID_Vibrate, NULL, NULL, soundCompleteCallback, NULL);
[self startShakeSound];
/// 开始播放与震动
- (void)startShakeSound {// 声音AudioServicesPlaySystemSound(soundID);// 震动AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
/// 结束播放与震动
- (void)stopShakeSound {AudioServicesDisposeSystemSoundID(soundID);AudioServicesRemoveSystemSoundCompletion(soundID);
}
//AudioServicesAddSystemSoundCompletion的回调函数
void soundCompleteCallback(SystemSoundID sound,void * clientData) {if (sound == kSystemSoundID_Vibrate) {AudioServicesPlayAlertSound(sound);//重复响铃震动} else {// 移除AudioServicesDisposeSystemSoundID(sound);AudioServicesRemoveSystemSoundCompletion(sound);AudioServicesDisposeSystemSoundID(kSystemSoundID_Vibrate);AudioServicesRemoveSystemSoundCompletion(kSystemSoundID_Vibrate);}
}
在iOS中通过soundID,可以控制播放与暂停,当然iOS中sound播放完成有通知回调callback。soundpool暂时没找到播放完成回调callback。
二、使用soundpool播放音频
2.1、引入soundpool
在pubspec.yaml中引入
soundpool: ^2.3.0
2.2、使用soundpool来播放音频
Soundpool的初始化要用到SoundpoolOptions
SoundpoolOptions soundpoolOptions = SoundpoolOptions();
_soundpool = Soundpool.fromOptions(options: soundpoolOptions);
_soundId = await _soundpool!.loadUri(url);
播放控制需要使用AudioStreamControl,比如播放、暂停、停止、设置音量等控制操作
- 播放
_audioStreamControl = await _soundpool!.playWithControls(_soundId!);
- 暂停
await _audioStreamControl!.pause();
- 停止
await _audioStreamControl!.stop();
- 设置音量
await _audioStreamControl!.setVolume(volume: volume);
2.3、通过播放时长控制是否可以再次播放
由于soundpool没有播放音频完成回调,这里采用通过Media将音频时长获取到之后判断时间。判断当前播放时间与上次播放的时间间隔是否超过了音频时长Duration。
if (lastPlayMilliseconds == null) {lastPlayMilliseconds = TimeUtil.currentTimeMillis();_audioStreamControl = await _soundpool!.playWithControls(_soundId!);} else {int nowMilliseconds = TimeUtil.currentTimeMillis();bool shouldPlay = true;if (audioDurationInMilliseconds != null) {if (nowMilliseconds - lastPlayMilliseconds! <audioDurationInMilliseconds!) {shouldPlay = false;}}if (shouldPlay) {// 如果上次没有播放完成,不进行播放// 通过判断播放时长_audioStreamControl = await _soundpool!.playWithControls(_soundId!);lastPlayMilliseconds = nowMilliseconds;}}
整体使用soundpool播放音频的相关代码如下
import 'package:soundpool/soundpool.dart';// 使用SoundPool
class SoundPlayer implements BaseAudioPlayer {// AudioStreamControl// SoundpoolSoundpool? _soundpool;String? audioUrl;// 上次播放时长的毫秒数int? lastPlayMilliseconds;// 时长int? audioDurationInMilliseconds;// 优先级late AudioConfig __audioConfig;late bool _playing = false; // 是否正在播放int? _soundId;AudioStreamControl? _audioStreamControl;SoundPlayer(this.__audioConfig, {Duration? duration}) {if (duration != null) {audioDurationInMilliseconds = duration!.inMilliseconds;}print("SoundPlayer audioDurationInMilliseconds:${audioDurationInMilliseconds}");SoundpoolOptions soundpoolOptions = SoundpoolOptions();_soundpool = Soundpool.fromOptions(options: soundpoolOptions);setAudioUrl(__audioConfig.audioUrl);}Future<void> setAudioUrl(String url) async {if (_soundpool != null) {_soundId = await _soundpool!.loadUri(url);}}AudioConfig get_audioConfig() {return __audioConfig;}void play() async {// Usually you don't want to wait for playback to finish.if (__audioConfig.audioUrl != null &&__audioConfig.audioUrl.isNotEmpty &&_soundId != null &&_soundpool != null) {if (lastPlayMilliseconds == null) {lastPlayMilliseconds = TimeUtil.currentTimeMillis();_audioStreamControl = await _soundpool!.playWithControls(_soundId!);} else {int nowMilliseconds = TimeUtil.currentTimeMillis();bool shouldPlay = true;if (audioDurationInMilliseconds != null) {if (nowMilliseconds - lastPlayMilliseconds! <audioDurationInMilliseconds!) {shouldPlay = false;}}if (shouldPlay) {// 如果上次没有播放完成,不进行播放// 通过判断播放时长_audioStreamControl = await _soundpool!.playWithControls(_soundId!);lastPlayMilliseconds = nowMilliseconds;}}}}void pause() async {if (__audioConfig.audioUrl != null &&__audioConfig.audioUrl.isNotEmpty &&_audioStreamControl != null) {try {await _audioStreamControl!.pause();} catch(e) {print("audioStreamControl pause e:${e.toString()}");}}}void stop() async {if (__audioConfig.audioUrl != null &&__audioConfig.audioUrl.isNotEmpty &&_audioStreamControl != null) {try {await _audioStreamControl!.stop();} catch(e) {print("audioStreamControl stop e:${e.toString()}");}}}void setVolume(double volume) async {if (__audioConfig.audioUrl != null &&__audioConfig.audioUrl.isNotEmpty &&_audioStreamControl != null) {try {await _audioStreamControl!.setVolume(volume: volume);} catch(e) {print("audioStreamControl setVolume e:${e.toString()}");}}}// 不需要该播放器,则需要调用该方法void dispose() {if (_soundpool != null) {_soundpool!.dispose();}}
}
三、小结
flutter开发实战-实现音效soundpool播放音频及控制播放、暂停、停止、设置音量等控制操作。
学习记录,每天不停进步。
相关文章:
flutter开发实战-实现音效soundpool播放音频及控制播放暂停停止设置音量
flutter开发实战-实现音效soundpool播放音频 最近开发过程中遇到低配置设备时候,在Media播放音频时候出现音轨限制问题。所以将部分音频采用音效sound来播放。 一、音效类似iOS中的Sound 在iOS中使用sound来播放mp3音频示例如下 // 通过通知的Sound设置为voip_c…...
Sequence 2023牛客暑期多校训练营6 E
登录—专业IT笔试面试备考平台_牛客网 题目大意:有一长度为n的数组a,有q次询问,每次要求将[l,r]的区间分成k个连续区间,满足每个区间和都是偶数,能满足要求就输出YES 1<n,q<1e5;0<ai<1e10;1<l<r&l…...
【ASP.NET MVC】使用动软(二)(10)
一、添加动软生成工程 按前文添加动态到工程 双击动软 完成新建数据库服务器后 ,需要关闭重新打开 选择简单三层,注意保存位置 注意切换数据库: 生成后拷贝五个文件夹到工程目录 注意目录结构: 添加四个项目到原来的工程&…...
STM32入门学习之独立看门狗(IWDG)
1.STM32的独立看门狗是一个具有独立时钟的片上外设。通常,为了防止程序卡死,可以设置看门狗定时复位。当看看门狗被使能之后,会按初始化时设置的计数值进行计数。当根据计数值计数的倒数时间为0时,便会自动复位程序,即…...
抖音seo矩阵系统源码搭建开发详解
抖音SEO矩阵系统是一个用于提高抖音视频在搜索引擎排名的工具。如果你想开发自己的抖音SEO矩阵系统,以下是详细的步骤: 开发步骤详解: 确定你需要的功能和算法 抖音SEO矩阵系统包含很多功能,比如关键词研究、内容优化、链接建设、…...
2685. 统计完全连通分量的数量;2718. 查询后矩阵的和;1600. 王位继承顺序
2685. 统计完全连通分量的数量 核心思想:枚举所有的连通分量,然后判断这些连通分量是不是完全连通分量,完全连通分量满足边数2e 点数v(v-1)。 2718. 查询后矩阵的和 核心思想:后面的改变更重要,所以我们直接逆向思维…...
SpringBoot统一功能处理(AOP思想实现)(统一用户登录权限验证 / 异常处理 / 数据格式返回)
主要是三个处理: 1、统一用户登录权限验证; 2、统一异常处理; 3、统一数据格式返回。 目录 一、用户登录权限校验 🍅 1、使用拦截器 🎈 1.1自定义拦截器 🎈 1.2 设置自定义拦截器 🎈创建cont…...
git stash 用法
起始 今天在看一个bug,之前一个分支的版本是正常的,在新的分支上上加了很多日志没找到原因,希望回溯到之前的版本,确定下从哪个提交引入的问题,但是还不想把现在的修改提交,也不希望在Git上看到当前修改的…...
生鲜蔬果小程序的完整教程
随着互联网的发展,线上商城成为了人们购物的重要渠道。其中,小程序商城在近年来的发展中,备受关注和青睐。本文将介绍如何使用乔拓云网后台搭建生鲜果蔬配送小程序,并快速上线。 首先,登录乔拓云网后台,进入…...
De Bruijin序列与魔术(二)——魔术《De Bruijin序列》
早点关注我,精彩不错过! 上一篇我们介绍了De Bruijin序列的基本数学内容以及其如何应用在魔术上的一些基本内容,今天我们就来学习一下这个经典的《De Bruijin序列》魔术。 De bruijin序列魔术 先看视频。 视频1 De Bruijin序列的魔术 魔术来源…...
ARCGIS地理配准出现的问题
第一种。已有省级行政区矢量数据,在网上随便找一个相同省级行政区图片,利用地理配准工具给图片添加坐标信息。 依次添加省级行政区选择矢量数据、浙江省图片。 此时,图层默认的坐标系与第一个加载进来的省级行政区选择矢量数据的坐标系一致…...
redis原理 6:小道消息 —— PubSub
前面我们讲了 Redis 消息队列的使用方法,但是没有提到 Redis 消息队列的不足之处,那就是它不支持消息的多播机制。 img 消息多播 消息多播允许生产者生产一次消息,中间件负责将消息复制到多个消息队列,每个消息队列由相应的消费组…...
Android Studio 的Gradle版本修改
使用Android Studio构建项目时,需要配置Gradle,与Gradle插件。 Gradle是一个构建工具,用于管理和自动化Android项目的构建过程。它使用Groovy或Kotlin作为脚本语言,并提供了强大的配置能力来定义项目的依赖关系、编译选项、打包方…...
Redis的部分面试题
1.Redis是什么?简述它的优缺点? Redis的字符串类型是通过简单动态字符串SDS来实现的。简单动态字符串是Redis自己实现的一种字符串表示方式,相比于C语言中的传统字符串,它具有以下几个特点: 1. 动态调整大小:简单动态字符串可…...
单通道 6GSPS 16位采样DAC子卡模块--【资料下载】
FMC147是一款单通道6.4GSPS(或者配置成2通道3.2GSPS)采样率的12位AD采集、单通道6GSPS(或配置成2通道3GSPS)采样率16位DA输出子卡模块,该板卡为FMC标准,符合VITA57.4规范,该模块可以作为一个理想…...
Python 文件操作详解
概要 Python进行文件操作,在日常编程中是很常用的。为了方便大家,这里对各种文件操作的知识进行汇总。一文在手,无须它求!来一起学习吧。 一、文件的打开和关闭 open()函数 f1 open(rd:\测试文件.txt, moder, encodingutf-8) c…...
【Rust 基础篇】Rust Never类型:表示不会返回的类型
导言 Rust是一种以安全性和高效性著称的系统级编程语言,其设计哲学是在不损失性能的前提下,保障代码的内存安全和线程安全。在Rust中,Never类型是一种特殊的类型,它表示一个函数永远不会返回。Never类型在Rust中有着重要的应用场…...
error “Component name “*****“ should always be multi-word”解决方案
问题 在 vue-cli 创建的项目中,创建文件并命名后,会报 “Component name "*****" should always be multi-word” 报错; Component name "index" should always be multi-word.eslintvue/multi-word-component-names原…...
前后端开发的区别是什么?
VUE的开发方式为什么和后端的MVC开发方式不一样呢? 实际上,Vue 和后端开发的 MVC(Model-View-Controller)方式是不同的,因为它们面对的问题和场景也不同。 前端与后端的职责不同: 前端和后端的职责和任务不…...
小白电脑装机(自用)
几个月前买了配件想自己装电脑,结果最后无法成功点亮,出现的问题是主板上的DebugLED黄灯常亮,即DRAM灯亮。对于微星主板的Debug灯,其含义这篇博文中有说明。 根据另一篇博文,有两种可能。 我这边曾将内存条和主板一块…...
STEP3-VL-10B一文详解:多模态对齐损失函数设计与人类反馈强化学习细节
STEP3-VL-10B一文详解:多模态对齐损失函数设计与人类反馈强化学习细节 1. 引言:为什么一个“小”模型能比肩“大”模型? 最近,一个只有100亿参数的“小”模型在技术圈里引起了不小的轰动。它就是阶跃星辰开源的STEP3-VL-10B。你…...
Worldwide, Apr 2026 : PYPL 全球编程语言流行度排行榜火热出炉
根据本期榜单数据,以下是对各编程语言流行度和趋势的分析: 总体趋势:Python 继续稳居榜首,其流行度份额为 36.21%,并且增长了 5.7%。这一增长表明Python在数据科学、人工智能和Web开发等领域的应用继续受到广泛关注。C…...
在ABAQUS中用SPH模拟倒酒过程,超有趣的小探索
ABAQUS模拟倒酒过程,酒用sph模拟最近在玩ABAQUS,突发奇想试试模拟倒酒过程,酒用SPH(光滑粒子流体动力学)方法来模拟,那效果简直绝了,跟大家分享分享我的折腾经历。 为啥选SPH模拟酒?…...
SenseVoice-Small ONNX模型效果惊艳展示:中英粤日韩五语种同步识别样例
SenseVoice-Small ONNX模型效果惊艳展示:中英粤日韩五语种同步识别样例 今天,我想带大家看一个让我眼前一亮的语音识别模型——SenseVoice-Small的ONNX版本。它最吸引我的地方,是能同时识别中文、英文、粤语、日语和韩语,而且速度…...
CasRel模型部署教程:使用Triton推理服务器实现高并发SPO服务
CasRel模型部署教程:使用Triton推理服务器实现高并发SPO服务 1. 认识CasRel关系抽取模型 CasRel(Cascade Binary Tagging Framework)是一个专门从文本中提取结构化信息的强大模型。想象一下,你有一大段文字,里面包含…...
Qwen-Image-2512风格迁移实战:将名画风格应用于产品设计
Qwen-Image-2512风格迁移实战:将名画风格应用于产品设计 1. 引言 你有没有想过,把梵高《星空》的笔触用在你的咖啡杯上,或者让莫奈的睡莲色调渲染你的手机壳?听起来像是顶级设计师的专属魔法,但现在,借助…...
Qwen3.5-2B轻量部署教程:适配Jetson/树莓派的2B多模态模型实测
Qwen3.5-2B轻量部署教程:适配Jetson/树莓派的2B多模态模型实测 1. 模型概述 Qwen3.5-2B是阿里云推出的轻量化多模态基础模型,属于Qwen3.5系列的小参数版本(20亿参数)。这个模型专为边缘计算设备优化,主打低功耗、低门…...
OpenClaw+千问3.5-9B:个人知识库的自动构建与更新
OpenClaw千问3.5-9B:个人知识库的自动构建与更新 1. 为什么需要自动化知识管理 作为一个长期与技术文档打交道的开发者,我发现自己面临一个典型困境:每天接触大量有价值的信息——技术博客、论文片段、代码示例、会议记录——但它们最终都散…...
告别setData地狱!用miniprogram-computed给你的微信小程序组件加上计算属性
告别setData地狱!用miniprogram-computed给你的微信小程序组件加上计算属性 每次在小程序里处理复杂数据联动时,你是不是也经历过这样的痛苦?表单验证状态需要根据三个输入框内容实时更新,购物车总价要随着商品数量和优惠券动态计…...
Vue3 + Element Plus项目实战:如何封装一个带比例锁定和实时预览的智能图片裁剪上传组件?
Vue3 Element Plus实战:构建智能图片裁剪上传组件的工程化实践 在当今的Web应用中,图片上传几乎是每个系统的标配功能。但简单的文件选择器往往无法满足专业需求——设计师需要精确控制图片比例,产品经理要求实时预览效果,而开发…...
