简介:本篇文章主要是根据'flutter_sound_master'中的示例代码进行整理(没有根据对应代码的注释进行整理,因此会有遗漏),并以具体功能为分类标准介绍flutter_sound的用法,适合初学者
flutter_sound主要功能:flutter_sound是基于flutter开发的音频处理依赖包,主要实现用户的音频任务,包括录音任务和播放任务,支持实时流处理和文件处理。
Player
- 播放器类获取
- FlutterSoundPlayer player = FlutterSoundPlayer()
- 播放器初始化
- player.openPlayer()
- bool isBGService:是否后台播放
- 播放器初始化应当放在init生命周期中
- player.openPlayer()
- 开始播放
- 从文件中播放
- player.startPlayer()
- Codec codec:编码格式,如mp3,wav,aac等
- int sampleRate:采样率,只用于PCM格式音频
- int numChannels:声道数,只用于PCM格式音频
- void Function() whenFinished:播放结束时调用
- 两种数据获取方式
- String fromURI:从网址或指定路径获取
- 远程文件URL
- 本地文件路径
- 临时文件名
- Uint8List fromDataBuffer:从字节缓冲区获取
- Uint8List指的是8位无符号整数列表(字节列表)
- String fromURI:从网址或指定路径获取
- player.startPlayer()
- 从流中播放(可以播放实时音频,而不需要等待完整的音频流加载到文件或内存中)
- 流的播放关键在于Food,只有当buffer有内容时才可以进行播放,主要通过sink接口对流进行管理
- 流的获取
- 本地获取
- data = rootBundle.load(path).buffer.asUint8List()
- rootBundle主要用于加载应用的资源文件
- 加载对应的资源文件,以Uint8List格式访问
- data = rootBundle.load(path).buffer.asUint8List()
- 本地获取
- player.startPlayerFromStream()
- 介绍:进行流播放器的初始化操作(注意,并没有开始播放音频)
- Codec codec
- bool interleaved:音频数据的组织方式,交错模式(L1R1L2L2...)或计划模式(L1L2...R1R2...)
- int numChannels
- int sampleRate
- int bufferSize:缓冲区大小设置
- void Function() onBufferUnderflow:缓冲区溢出时调用
- player.uint8ListSink.add(Uint8List event)
- 直接打开播放器的流管理接口(uint8List格式)并向其中添加内容(无背压音频播放)
- 问题:数据直接添加进去,而播放器数据处理能力不足例如缓冲区大小不够,数据处理速度过慢则会导致数据丢失等问题
- Future<int> player.feedUint8FromStream(Uint8List event)
- 注意设置event的大小为min(剩余数据,bufferSize)防止溢出
- 这是一个耗时操作,await player.feedUint8FromStream(Uint8List event)就可以实现等待音频处理结束,然后输入新的数据
-
Future<void> feedHim(Uint8List buffer) async { var start = 0; var totalLength = buffer.length; while (totalLength > 0 && !_mPlayer.isStopped) { var ln = totalLength > kBLOCKSIZE ? kBLOCKSIZE : totalLength; if (flowControl) { await _mPlayer.feedUint8FromStream( buffer.sublist(start, start + ln)); // with await !!!! } else { _mPlayer.uint8ListSink!.add(data.sublist(start, start + ln)); } start += ln; totalLength -= ln; } _mPlayer.logger.d('Finished'); }
- 从文件中播放
- 停止播放
- player.stopPlayer()
- 暂停播放
- player.pausePlayer()
- 继续播放
- player.resumePlayer()
- 跳转到指定的播放时间
- player.seekToPlayer( Duration( ... ) )
- 跳转后会延续之前的播放状态
- 播放器销毁
- player.closePlayer()
- 在窗口销毁前应该销毁播放器
- 音量控制
- player.setVolume(0.9)
- 音量平衡(音量+左右声道音量比例)
- player.setVolumePan( double volume , double pan)
- pan(-1~1)代表左右音道比例
- 播放速度控制
- player.setSpeed( double speed )
- 订阅(监视器,这里是单个订阅,一个流只有一个订阅,获取流的状态,多订阅需要broadcast)
- 订阅器事件产生设置
- player.setSubscriptionDuration(
Duration(milliseconds: 400)
) - 设置订阅事件间隔,每隔400ms唤醒一次产生一次订阅事件
- player.setSubscriptionDuration(
- 订阅器
- 流订阅器类:
- StreamSubcription sub
- 订阅取消
- sub.cancel()
- 订阅事件onProgress(onProgress是一个流Stream<PlaybackDisposition>包含(均为Duration类)duration和position)
- sub = player.onProgress.listen((e){
pos = e.position.inMilliseconds;
}
) - listen订阅该流Stream并返回一个订阅器
- 这样就实现了对流进程的订阅
-
var date = DateTime.fromMillisecondsSinceEpoch(e.position.in...) var txt = FateFormat('mm:ss:SS','en_GB').format(date) txt.substring(0,8) //这段代码可以实现对于播放时间的显示
- sub = player.onProgress.listen((e){
- 流订阅器类:
- 订阅器事件产生设置
Recorder
- 录音器类获取
- recorder = FlutterSoundRecorder()
- 录音器类初始化
- recorder.openRecorder()
- 初始化前需要获取录音权限,Permission.microphone.request()
- 开始录音
- 准备工作
- 录音前需要检查设备的录音编码支持,设置为可行的的录音编码
- recorder.isEncoderSupported(codec)检查codec是否可行
- 保存文件路径和文件操作
- 目录获取
- 临时目录:getTemporaryDirectory
- 应用私有目录:getApplicationDocumentsDirectory
- 外部存储目录(需要权限):getExternalStorageDirectory
- 文件操作(这里只有使用到的操作)
- file = File(path)
- file.existesSync():检查文件是否存在
- file.delete():删除文件
- Future<IOSink> file.openWrite():简单的写入操作,并返回流操作接口sink
- 目录获取
- 录音到文件中
- recorder.startRecorder()
- toFile
- codec
- enableVoiceProcessing:是否启动语音处理如回声消除,噪声抑制等
- audioSource
- recorder.startRecorder()
- 录音到流中(主要使用流控制器StreamController和其接口sink)
- 流控制器
- 定义:controller = StreamController<Uint8List>()
- 为流控制器的流添加订阅(当buffer不足时,会删去旧数据,因此需要处理)
- StreamSink<List<int>> sink; (uint8List是List<int>的特殊形式)
- sink = outputFile.openWrite() //获取对应文件的写入流控制接口
- controller.stream.listen((buffer){
sink.add(buffer);//当stream接收到新传来的List<int>时调用
})
- recorder.startRecorder()
- toStream:controller.sink
- codec
- numChannels
- sampleRate
- bufferSize:单声道8192,双声道16384
- audioSource
- 每次接收到bufferSize大小的数据就会发送到stream中
- 设置录音进度更新频率(和streamcontroller无关)
- recorder.setSubscriptionDuration(...)
- 流控制器
- 准备工作
- 录音器销毁:recorder.closeRecorder()
其它
- 音频文件格式转换:FlutterSoundHelper().waveToPCMBuffer(inputBuffer:...)将wav转换为PCM文件,需要将wav文件以Uint8List形式导入
- 设置日志级别:player.setLogLevel
- player.log.d(...)
- 非uint8List格式的音频
- 上面的播放器使用的方法是player.feedUint8FromStream(Uint8List event),此外还有其他的方法如FeedF32和FeedInt16,分别表示不同的PCM格式
- 但是录音器的toStream要求Uint8List,此时需要改为对应的toStreamFloat32,to StreamInt 16
- 自制混合模式:这个原文中有,手动根据数据重新打包,感兴趣的可以去看看
- 属性辨析
- Codec:编码格式,指的是音频模拟信号转换为数字信号的方式,pcm16指的是2字节有符号数保存的采样点
- 音频数据输出到流的方法
- toStream:以Uint8(字节形式)输出,通常用于文件保存等要求字节形式的数据
- toStreamInt16:以Int16形式输出,适合int16采样点的处理,但是不适合保存文件,因为不是字节格式
- toStreamFloat32:以Float32形式输出
- 综上所述,toStream适合直接对数据进行操纵,会输出所有数据如音频格式头,而其他格式是直接对采样点进行处理,不会输出如音频格式头
- 流控制器:选择了流输出方法后应该使用对应格式的控制器进行接收
最后附上一张作者示例的运行截图,作者的example比较全面地展示了各种功能,除此之外,关于Flutter应用的整体架构和组件也涉及很多,通过这个具体的应用可以简略窥见Flutter应用设计的基本步骤和模板,还是多多推荐大家去看看源代码啊。