Flutter中如何通过just_audio实时生成播放可调频正弦波音频流
实时生成可控频率正弦波并同步播放的实现方案
核心思路
你现有的实现是预先生成完整的音频字节buffer再传给播放器,所以无法支持动态调整参数。只要把StreamAudioSource返回的流从一次性的固定数据,改成持续生成正弦波采样的无限流,就可以实现频率调整的实时响应。
实现代码
1. 改造StreamAudioSource为实时生成流
import 'dart:async'; import 'dart:typed_data'; import 'dart:math'; import 'package:just_audio/just_audio.dart'; import 'package:wave_generator/wave_generator.dart'; class RealtimeSineSource extends StreamAudioSource { // 音频基础参数,可根据需求自行调整 static const int sampleRate = 44100; static const int channels = 1; static const BitDepth bitDepth = BitDepth.bit16; // 外部直接修改该参数即可实时调整正弦波频率 double currentFreq = 440.0; final _streamController = StreamController<List<int>>(); bool _isGenerating = true; RealtimeSineSource() : super(tag: "RealtimeSineWave") { _startGenerateLoop(); } void _startGenerateLoop() async { final generator = WaveGenerator(sampleRate, bitDepth); double phase = 0.0; // 单块音频时长设置为100ms,兼顾响应速度和播放稳定性 const blockSampleCount = sampleRate ~/ 10; while (_isGenerating) { final blockBytes = <int>[]; for (int i = 0; i < blockSampleCount; i++) { final sample = generator.generate( Waveform.sine, currentFreq, 1.0, phase, ); blockBytes.addAll(sample); // 累加相位避免音频断层 phase += 2 * pi * currentFreq / sampleRate; if (phase >= 2 * pi) phase -= 2 * pi; } if (!_streamController.isClosed) { _streamController.add(Uint8List.fromList(blockBytes)); } // 间隔时间略小于块时长,避免缓冲区为空导致断音 await Future.delayed(const Duration(milliseconds: 80)); } } @override Future<StreamAudioResponse> request([int? start, int? end]) async { // sourceLength和contentLength传null表示为无限时长流 return StreamAudioResponse( sourceLength: null, contentLength: null, offset: 0, contentType: 'audio/pcm', stream: _streamController.stream, ); } void dispose() { _isGenerating = false; _streamController.close(); } }
2. 调用方式
final AudioPlayer _player = AudioPlayer(); late final RealtimeSineSource _sineSource; // 初始化仅需执行一次 Future<void> initPlayer() async { _sineSource = RealtimeSineSource(); await _player.setAudioSource(_sineSource); _player.play(); } // 用户调整频率时直接赋值即可,无需重新设置音频源 void updateFrequency(double newFreq) { _sineSource.currentFreq = newFreq; } // 资源释放 void release() { _sineSource.dispose(); _player.dispose(); }
注意事项
- 若需要使用wav格式而非raw pcm,需要在流最开头写入符合规范的wav头,同时将
contentType改为audio/wav - 单块音频的时长可根据需求调整:设置越短频率调整响应越快,但过短可能导致播放断音;建议取值范围在50ms~200ms之间
- 播放前需要提前申请对应平台的音频权限,否则会播放失败
- 若出现爆音可适当增大单块音频的长度,或优化音频生成逻辑避免CPU阻塞
内容的提问来源于stack exchange,提问作者Fabrizio
相关产品推荐
相关产品推荐

