目录
  • 插件介绍:
  • 插件信息:
  • 插件使用前的准备工作
    • 设置麦克风权限描述
    • 权限管理插件 permission_handler
    • 音频硬件配置插件 audio_session
    • 动画插件
  • 常用的方法
    • 录音常见的方法
    • 初始化录音对象
    • 开启录音
    • 麦克风权限
    • 开始录音
    • 结束录音
    • 播放常见的方法
    • 初始化播放器
    • 初始化操作
    • 开始播放
    • 结束播放
  • 动画实现
    • 加载GIF动画
    • 加载动画文件
  • 上传文件
    • 上传音频文件
  • 总结

    插件介绍:

    flutter_sound这个插件可以实现iOS和Android平台的录音和播放功能。即可以播放本地音频文件,也可以播放远程URL文件。在这里我讲介绍这个插件的用法以及碰到的一些常见问题如何解决。

    • flutter_sound支持多种录音格式

    • flutter_sound支持多种播放格式

    • flutter_sound支持音频振幅大小

    插件信息:

    插件地址:github.com/ryanheise/a…

    插件版本:9.2.9

    插件使用前的准备工作

    设置麦克风权限描述

    • iOS:需要在info.plist文件添加一下权限
    <key>NSMicrophoneUsageDescription</key>
    <string>描述你使用麦克风用来干嘛</string>

    基于flutter sound插件实现录音与播放功能

    注意:还需要在Podfile文件中配置

    post_install do |installer|
      installer.pods_project.targets.each do |target|
        flutter_additional_ios_build_settings(target)
      target.build_configurations.each do |config|
            config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
              '$(inherited)',
              ## dart: PermissionGroup.microphone
              'PERMISSION_MICROPHONE=1',
            ]
          end
        end
      end

    还需要在iOS工程中增加libc++.tbd库,具体路径

    基于flutter sound插件实现录音与播放功能

    • Android:需要设置AndroidManifest.xml
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    基于flutter sound插件实现录音与播放功能

    这里还用了下面几个插件

    权限管理插件 permission_handler

    插件信息:permission_handler: ^9.2.0

    插件地址:pub.flutter-io.cn/packages/pe…

    音频硬件配置插件 audio_session

    插件信息:audio_session: ^0.1.6

    插件地址:github.com/ryanheise/a…

    动画插件

    插件信息:lottie: 1.2.1

    插件地址:pub.flutter-io.cn/packages/fl…

    常用的方法

    录音常见的方法

    初始化录音对象

    FlutterSoundRecorder recorderModule = FlutterSoundRecorder();

    开启录音

    Future<void> init() async {
      //开启录音
      await recorderModule.openRecorder();
      //设置订阅计时器
      await recorderModule
          .setSubscriptionDuration(const Duration(milliseconds: 10));
      //初始化日期插件
      await initializeDateFormatting();
    }

    麦克风权限

    Future<bool> getPermissionStatus() async {
      Permission permission = Permission.microphone;
      //granted 通过,denied 被拒绝,permanentlyDenied 拒绝且不在提示
      PermissionStatus status = await permission.status;
      if (status.isGranted) {
        return true;
      } else if (status.isDenied) {
        requestPermission(permission);
      } else if (status.isPermanentlyDenied) {
        openAppSettings();
      } else if (status.isRestricted) {
        requestPermission(permission);
      } else {}
      return false;
    }
    
    ///申请权限
    void requestPermission(Permission permission) async {
      PermissionStatus status = await permission.request();
      if (status.isPermanentlyDenied) {
        openAppSettings();
      }
    }

    开始录音

    /// 开始录音
    _startRecorder() async {
      try {
        //获取麦克风权限
        await getPermissionStatus().then((value) async {
          if (!value) {
            return;
          }
          //用户允许使用麦克风之后开始录音
          Directory tempDir = await getTemporaryDirectory();
          var time = DateTime.now().millisecondsSinceEpoch;
          String path = '${tempDir.path}/$time${ext[Codec.aacADTS.index]}';
          
          //这里我录制的是aac格式的,还有其他格式
          await recorderModule.startRecorder(
            toFile: path,
            codec: Codec.aacADTS,
            bitRate: 8000,
            numChannels: 1,
            sampleRate: 8000,
          );
          /// 监听录音
          _recorderSubscription = recorderModule.onProgress!.listen((e) {
            var date = DateTime.fromMillisecondsSinceEpoch(
                e.duration.inMilliseconds,
                isUtc: true);
            var txt = DateFormat('mm:ss:SS', 'en_GB').format(date);
            //设置了最大录音时长
            if (date.second >= _maxLength) {
              _stopRecorder();
              return;
            }
            setState(() {
              //更新录音时长
              _recordText = txt.substring(1, 5);
            });
          });
          setState(() {
             //更新录音状态和录音文件路径
            _state = RecordPlayState.recording;
            _path = path;
          });
        });
      } catch (err) {
        setState(() {
          _stopRecorder();
          _state = RecordPlayState.record;
          _cancelRecorderSubscriptions();
        });
      }
    }

    结束录音

    /// 结束录音
    _stopRecorder() async {
      try {
        await recorderModule.stopRecorder();
        _cancelRecorderSubscriptions();
        // _getDuration();
      } catch (err) {}
      setState(() {
        _state = RecordPlayState.record;
      });
    }
    ///销毁录音
    void dispose() {
      super.dispose();
      _cancelRecorderSubscriptions();
      _releaseFlauto();
    }
    /// 取消录音监听
    void _cancelRecorderSubscriptions() {
      if (_recorderSubscription != null) {
        _recorderSubscription!.cancel();
        _recorderSubscription = null;
      }
    }
    /// 释放录音
    Future<void> _releaseFlauto() async {
      try {
        await recorderModule.closeRecorder();
      } catch (e) {}
    }
    /// 判断文件是否存在
    Future<bool> _fileExists(String path) async {
      return await File(path).exists();
    }

    播放常见的方法

    初始化播放器

    FlutterSoundPlayer playerModule = FlutterSoundPlayer();

    初始化操作

    init() async {
      await playerModule.closePlayer();
      await playerModule.openPlayer();
      await playerModule
          .setSubscriptionDuration(const Duration(milliseconds: 10));
    //这块是设置音频,暂时没用到可以不用设置
      final session = await AudioSession.instance;
      await session.configure(AudioSessionConfiguration(
        avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
        avAudioSessionCategoryOptions:
            AVAudioSessionCategoryOptions.allowBluetooth |
                AVAudioSessionCategoryOptions.defaultToSpeaker,
        avAudioSessionMode: AVAudioSessionMode.spokenAudio,
        avAudioSessionRouteSharingPolicy:
            AVAudioSessionRouteSharingPolicy.defaultPolicy,
        avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
        androidAudioAttributes: const AndroidAudioAttributes(
          contentType: AndroidAudioContentType.speech,
          flags: AndroidAudioFlags.none,
          usage: AndroidAudioUsage.voiceCommunication,
        ),
        androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
        androidWillPauseWhenDucked: true,
      ));
    }

    开始播放

    ///开始播放,这里做了一个播放状态的回调
    void startPlayer(PlayStateBack callBack) async {
      try {
        if (path.contains('http')) {
          await playerModule.startPlayer(
              fromURI: path,
              codec: Codec.mp3,
              sampleRate: 44000,
              whenFinished: () {
                stopPlayer();
                callBack(0);
              });
        } else {
          //判断文件是否存在
          if (await _fileExists(path)) {
            if (playerModule.isPlaying) {
              playerModule.stopPlayer();
            }
            await playerModule.startPlayer(
                fromURI: path,
                codec: Codec.aacADTS,
                sampleRate: 44000,
                whenFinished: () {
                  stopPlayer();
                  callBack(0);
                });
          } else {}
        }
        //监听播放进度
        _playerSubscription = playerModule.onProgress!.listen((e) {});
        callBack(1);
      } catch (err) {
        callBack(0);
      }
    }

    结束播放

    /// 结束播放
    void stopPlayer() async {
      try {
        await playerModule.stopPlayer();
        cancelPlayerSubscriptions();
      } catch (err) {}
    }
    /// 取消播放监听
    void cancelPlayerSubscriptions() {
      if (_playerSubscription != null) {
        _playerSubscription!.cancel();
        _playerSubscription = null;
      }
    }
    ///获取播放状态
    Future<PlayerState> getPlayState() async {
      return await playerModule.getPlayerState();
    }
    /// 释放播放器
    void releaseFlauto() async {
      try {
        await playerModule.closePlayer();
      } catch (e) {
        print(e);
      }
    }
    /// 判断文件是否存在
    Future<bool> _fileExists(String path) async {
      return await File(path).exists();
    }

    动画实现

    在进行录音和播放的过程中难免使用到动画,这里我说下如何加载gif和动画文件

    加载GIF动画

    Visibility(
      visible: (item.playing.value == 1) ? true : false,
      child: Image.asset('assets/-comm/comm_audio_paly.gif', width: 20, height: 20,),
      replacement: const Image(
        image: AssetImage('assets/-comm/comm_audio_icon.png'),
        width: 20,
        height: 20,
      ),
    )

    加载动画文件

    Lottie.asset('assets/-comm/record_audio_animation.json',
        height: 25,
        width: ScreenAdapter.screenWidth() -ScreenAdapter.width(160),
        animate: true)

    上传文件

    上传音频文件

    var map = {
      "file": MultipartFile.fromBytes(
          await File.fromUri(Uri(path: path)).readAsBytes(),
          filename: "$fileName.mp3",
          contentType: MediaType.parse("audio/mp3"))
    };

    总结

    上面介绍了如何录音,如何播放本地和远程音频文件,以及如何实现动画,在录制完音频文件后如何上传,这些都是我们平常使用这个功能会遇到的问题。在使用的过程中遇到的问题也有列出,希望对您有所帮助。

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。