You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 05:54:02