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

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

相关推荐
G佳伟4 小时前
【亲测有效】百度Ueditor富文本编辑器添加插入视频、视频不显示、和插入视频后二次编辑视频标签不显示,显示成img标签,二次保存视频被替换问题,解决方案
chrome·百度·音视频
灵感素材坊13 小时前
解锁音乐创作新技能:AI音乐网站的正确使用方式
人工智能·经验分享·音视频
modest —YBW14 小时前
视频大小怎么计算?视频码率是什么,构成视频清晰度的核心要素!
音视频
cuijiecheng201814 小时前
音视频入门基础:RTP专题(10)——FFmpeg源码中,解析RTP header的实现
ffmpeg·音视频
AI服务老曹14 小时前
运用先进的智能算法和优化模型,进行科学合理调度的智慧园区开源了
运维·人工智能·安全·开源·音视频
Macdo_cn18 小时前
My Metronome for Mac v1.4.2 我的节拍器 支持M、Intel芯片
macos·音视频
kiramario18 小时前
【结束】JS如何不通过input的onInputFileChange使用本地mp4文件并播放,nextjs下放入public文件的视频用video标签无法打开
开发语言·javascript·音视频
余~~1853816280020 小时前
矩阵碰一碰发视频的后端源码技术,支持OEM
线性代数·矩阵·音视频
划水哥~1 天前
高清下载油管视频到本地
音视频
Luke Ewin1 天前
根据音频中的不同讲述人声音进行分离音频 | 基于ai的说话人声音分离项目
人工智能·python·音视频·语音识别·声纹识别·asr·3d-speaker