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

Flutter中获取麦克风语音频率问题求助(附fftea代码)

问题分析与解决

你的代码存在几个关键问题,导致无法得到正确的语音频率结果,逐个解决如下:

1. 原始音频数据转换错误

麦克风采集的PCM数据通常是16位有符号整数(int16),直接把List<int>强制转换为double会导致数值完全失真,需要先将每个int16值归一化到[-1.0, 1.0]的范围。

2. 缺少采样率参数

FFT返回的是频域的bin索引,要转换成实际频率值必须知道音频的采样率(比如常见的44100Hz、48000Hz),需要确保原生端传递音频数据时同时提供采样率。

3. 未提取有效频率值

FFT返回的是复数数组,需要计算每个bin的幅度,找到幅度最大的bin,再转换成对应的实际频率值。

4. 未更新UI

没有通过setState将计算出的频率更新到文本组件中,导致UI始终显示固定内容。


修正后的完整代码

前提:原生端调整

需要修改原生代码,让EventChannel返回包含audioData(List)和sampleRate(int)的Map,确保传递采样率信息。

Flutter端修正代码

import 'dart:async';
import 'dart:math';
import 'package:fftea/impl.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Microphone Frequency Analyzer',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const AudioStreamPage(),
    );
  }
}

class AudioStreamPage extends StatefulWidget {
  const AudioStreamPage({super.key});

  @override
  _AudioStreamPageState createState() => _AudioStreamPageState();
}

class _AudioStreamPageState extends State<AudioStreamPage> {
  static const _audioStreamChannel = EventChannel('audio_stream');
  StreamSubscription? _streamSubscription;
  String _currentFrequency = '0 Hz';
  int _sampleRate = 44100; // 默认采样率,实际从原生获取

  @override
  void initState() {
    super.initState();
    _startAudioStream();
  }

  @override
  void dispose() {
    _streamSubscription?.cancel();
    super.dispose();
  }

  void _startAudioStream() {
    _streamSubscription = _audioStreamChannel.receiveBroadcastStream().listen((dynamic event) {
      final dataMap = event as Map<String, dynamic>;
      final audioData = dataMap['audioData'] as List<int>;
      _sampleRate = dataMap['sampleRate'] as int;
      analyzeAudioData(audioData);
    }, onError: (error) {
      debugPrint('音频流错误: $error');
    });
  }

  void analyzeAudioData(List<int> audioData) {
    // 将int16 PCM数据归一化为[-1.0, 1.0]的double数组
    final doubleSamples = audioData.map((int value) {
      return value / 32768.0;
    }).toList();

    // 确保FFT输入长度为2的幂(fftea要求,否则报错)
    final fftSize = _nextPowerOfTwo(doubleSamples.length);
    final paddedSamples = _padToLength(doubleSamples, fftSize);

    // 执行FFT
    final fft = FFT(fftSize);
    final complexResult = fft.realFft(paddedSamples);

    // 计算每个频域bin的幅度
    final magnitudes = <double>[];
    for (var i = 0; i < fftSize ~/ 2; i++) {
      final real = complexResult[i].real;
      final imag = complexResult[i].imag;
      magnitudes.add(sqrt(real * real + imag * imag));
    }

    // 找到幅度最大的bin索引
    int dominantBin = 0;
    double maxMagnitude = 0;
    for (var i = 1; i < magnitudes.length; i++) {
      if (magnitudes[i] > maxMagnitude) {
        maxMagnitude = magnitudes[i];
        dominantBin = i;
      }
    }

    // 将bin索引转换为实际频率:频率 = (bin索引 * 采样率) / FFT长度
    final dominantFrequency = (dominantBin * _sampleRate) / fftSize;

    // 更新UI
    setState(() {
      _currentFrequency = '${dominantFrequency.round()} Hz';
    });
  }

  // 找到大于等于输入值的最小2的幂
  int _nextPowerOfTwo(int value) {
    int result = 1;
    while (result < value) {
      result <<= 1;
    }
    return result;
  }

  // 填充数组到指定长度(补0)
  List<double> _padToLength(List<double> list, int length) {
    final padded = List<double>.filled(length, 0.0);
    padded.setRange(0, min(list.length, length), list);
    return padded;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('麦克风频率分析仪')),
      body: Center(
        child: Text(
          _currentFrequency,
          style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold),
        ),
      ),
    );
  }
}

额外注意事项

  • 原生端必须确保传递的是16位有符号整数格式的PCM数据,采样率要和实际采集一致。
  • 实际场景中可以给音频数据加汉明窗,减少频谱泄漏,提升频率检测的准确性。
  • 如果需要更稳定的结果,可以对多次FFT的结果做平均处理。

内容的提问来源于stack exchange,提问作者Yash

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 22:27:46