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

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

相关推荐
Zender Han2 小时前
Flutter 进阶:实现带圆角的 CircularProgressIndicator
flutter
音视频牛哥2 小时前
计算机视觉的新浪潮:扩散模型(Diffusion Models)技术剖析与应用前景
人工智能·计算机视觉·ai·音视频·实时音视频·扩散模型
电子科技圈3 小时前
SmartDV推出先进的H.264和H.265视频编码器和解码器IP
音视频·h.265·h.264
feiyangqingyun5 小时前
Qt音视频开发技巧/推流带旋转角度/rtsprtmp推流/保存文件到MP4/拉流解析旋转角度
qt·音视频·qt旋转角度推流
Despacito0o5 小时前
ESP32-s3摄像头驱动开发实战:从零搭建实时图像显示系统
人工智能·驱动开发·嵌入式硬件·音视频·嵌入式实时数据库
nc_kai6 小时前
Flutter 之 每日翻译 PreferredSizeWidget
java·前端·flutter
littlegnal6 小时前
Flutter Add-to-app profiling
flutter
0wioiw013 小时前
Flutter基础(FFI)
flutter
Georgewu10 天前
【HarmonyOS 5】鸿蒙跨平台开发方案详解(一)
flutter·harmonyos