Flutter-flutter_sound recording and playback

Plugin introduction:

flutter_soundThis plug-in can realize iOS和Androidthe recording and playback functions of the platform. That is, you can play local audio files and remote URL files. Here I will introduce the usage of this plugin and how to solve some common problems encountered.

  • flutter_sound supports multiple recording formats

  • flutter_sound supports multiple playback formats

  • flutter_sound supports audio amplitude size

Plugin info:

Plugin address: github.com/ryanheise/a…

Plugin version: 9.2.9

Preparation before using the plugin

Set microphone permission description

  • iOS: You need to add permissions to the info.plist file
<key>NSMicrophoneUsageDescription</key>
<string>描述你使用麦克风用来干嘛</string>
复制代码

Screenshot 2022-04-23 3.09.07 pm.png

注意:还需要在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
复制代码

You also need to add a libc++.tbdlibrary to the iOS project, the specific path

Screenshot 2022-05-14 17.05.47.png

  • Android: requires setupAndroidManifest.xml
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
复制代码

Screenshot 2022-04-23 3.11.31 pm.png

The following plugins are also used here

Permission management plugin permission_handler

Plugin info:permission_handler: ^9.2.0

Plugin address: pub.flutter-io.cn/packages/pe…

Audio hardware configuration plugin audio_session

Plugin info:audio_session: ^0.1.6

Plugin address: github.com/ryanheise/a…

animation plugin

Plugin info:lottie: 1.2.1

Plugin address: pub.flutter-io.cn/packages/fl…

Commonly used methods

Common methods of recording

Initialize the recording object

FlutterSoundRecorder recorderModule = FlutterSoundRecorder();
复制代码

Turn on recording

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

Microphone permissions

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();
  }
}
复制代码

start recording

/// 开始录音
_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();
    });
  }
}
复制代码

end recording

/// 结束录音
_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();
}
复制代码

Play common methods

Initialize the player

FlutterSoundPlayer playerModule = FlutterSoundPlayer();
复制代码

initialization operation

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,
  ));
}
复制代码

Start playing

///开始播放,这里做了一个播放状态的回调
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);
  }
}
复制代码

end playback

/// 结束播放
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();
}
复制代码

Animation implementation

In the process of recording and playback, it is inevitable to use animation. Here I will tell you how to load it.gif和动画文件

Loading animated gifs

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,
  ),
)
复制代码

Load animation file

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

upload files

Upload audio files

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

Summarize

The above describes how to record, how to play local and remote audio files, how to implement animation, and how to upload audio files after recording. These are the problems we usually encounter when using this function. The problems encountered in the process of use are also listed, I hope to help you.

Guess you like

Origin juejin.im/post/7098131435273322526