flutter开发实战-实现音效soundpool播放音频及控制播放暂停停止设置音量

flutter开发实战-实现音效soundpool播放音频

最近开发过程中遇到低配置设备时候,在Media播放音频时候出现音轨限制问题。所以将部分音频采用音效sound来播放。

一、音效类似iOS中的Sound

在iOS中使用sound来播放mp3音频示例如下

objectivec 复制代码
// 通过通知的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];
objectivec 复制代码
/// 开始播放与震动
- (void)startShakeSound {
    // 声音
    AudioServicesPlaySystemSound(soundID);
    
    // 震动
    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
objectivec 复制代码
/// 结束播放与震动
- (void)stopShakeSound {
    AudioServicesDisposeSystemSoundID(soundID);
    AudioServicesRemoveSystemSoundCompletion(soundID);
}
objectivec 复制代码
//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中引入

dart 复制代码
soundpool: ^2.3.0

2.2、使用soundpool来播放音频

Soundpool的初始化要用到SoundpoolOptions

dart 复制代码
SoundpoolOptions soundpoolOptions = SoundpoolOptions();
_soundpool = Soundpool.fromOptions(options: soundpoolOptions);
_soundId = await _soundpool!.loadUri(url);

播放控制需要使用AudioStreamControl,比如播放、暂停、停止、设置音量等控制操作

  • 播放
dart 复制代码
_audioStreamControl = await _soundpool!.playWithControls(_soundId!);
  • 暂停
dart 复制代码
await _audioStreamControl!.pause();
  • 停止
dart 复制代码
await _audioStreamControl!.stop();
  • 设置音量
dart 复制代码
await _audioStreamControl!.setVolume(volume: volume);

2.3、通过播放时长控制是否可以再次播放

由于soundpool没有播放音频完成回调,这里采用通过Media将音频时长获取到之后判断时间。判断当前播放时间与上次播放的时间间隔是否超过了音频时长Duration。

dart 复制代码
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播放音频的相关代码如下

dart 复制代码
import 'package:soundpool/soundpool.dart';

// 使用SoundPool
class SoundPlayer implements BaseAudioPlayer {
  // AudioStreamControl
  // Soundpool
  Soundpool? _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;
  }

  @override
  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;
        }
      }
    }
  }

  @override
  void pause() async {
    if (__audioConfig.audioUrl != null &&
        __audioConfig.audioUrl.isNotEmpty &&
        _audioStreamControl != null) {
      try {

        await _audioStreamControl!.pause();
      } catch(e) {
        print("audioStreamControl pause e:${e.toString()}");
      }
    }
  }

  @override
  void stop() async {
    if (__audioConfig.audioUrl != null &&
        __audioConfig.audioUrl.isNotEmpty &&
        _audioStreamControl != null) {
      try {
        await _audioStreamControl!.stop();
      } catch(e) {
        print("audioStreamControl stop e:${e.toString()}");
      }
    }
  }

  @override
  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()}");
      }
    }
  }

  // 不需要该播放器,则需要调用该方法
  @override
  void dispose() {
    if (_soundpool != null) {
      _soundpool!.dispose();
    }
  }
}

三、小结

flutter开发实战-实现音效soundpool播放音频及控制播放、暂停、停止、设置音量等控制操作。

学习记录,每天不停进步。

相关推荐
GitLqr19 小时前
Flutter 实战:使用 local_auth 实现生物识别(指纹/Face ID)
安全·flutter·全栈
OpenCutAI20 小时前
无字幕视频怎么翻译?跨境出海视频本地化实操指南
音视频
大龄秃头程序员21 小时前
【Flutter 性能踩坑小记】相册选个图卡了
flutter
科技小E1 天前
国标GB28181视频平台EasyGBS监控为什么会“瞎”:视频质量诊断EasyVQD如何揪出黑屏卡顿偏色
大数据·人工智能·音视频
ACP广源盛139246256731 天前
Qwen3.8‑2.4T 开源落地@ACP#国产 Serdes 长距离视频传输芯片 GSV5800 在私有化 AI 服务中的价值与应用场景
大数据·数据库·人工智能·嵌入式硬件·矩阵·开源·音视频
深圳市宝华视联1 天前
院感约束下,手术室音视频布线与设备摆放实施注意事项
嵌入式硬件·音视频·实时音视频·视频编解码·嵌入式实时数据库
东莞市奥普新音频技术有限公司1 天前
国产音频分析仪怎么选?从性能参数、测试软件到自动化能力
人工智能·科技·测试工具·自动化·音视频·音频
hz567891 天前
视频会议终端的核心功能与采购指南
运维·音视频·信息与通信·智能硬件
美狐美颜sdk1 天前
从开发角度分析直播APP:视频美颜SDK接入过程中的技术难点
大数据·人工智能·音视频·美颜sdk·美颜api