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播放音频及控制播放、暂停、停止、设置音量等控制操作。

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

相关推荐
迷雾漫步者3 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
darkdragonking7 小时前
FLV视频封装格式详解
音视频
coder_pig7 小时前
📝小记:Ubuntu 部署 Jenkins 打包 Flutter APK
flutter·ubuntu·jenkins
元争栈道8 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
元争栈道10 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
捡芝麻丢西瓜10 小时前
flutter自学笔记5- dart 编码规范
flutter·dart
恋猫de小郭10 小时前
什么?Flutter 可能会被 SwiftUI/ArkUI 化?全新的 Flutter Roadmap
flutter·ios·swiftui
MediaTea13 小时前
Pr:音频仪表
音视频
桃园码工13 小时前
13_HTML5 Audio(音频) --[HTML5 API 学习之旅]
音视频·html5·audio
cuijiecheng201819 小时前
音视频入门基础:MPEG2-TS专题(21)——FFmpeg源码中,获取TS流的视频信息的实现
ffmpeg·音视频