Flutter快速傅里叶变换插件flutter_fft的使用

Flutter快速傅里叶变换插件flutter_fft的使用

插件在pub.dev上

该插件可以在以下链接找到:

https://pub.dev/packages/flutter_fft

警告: 目前仅支持Android!该插件利用了平台通道,但目前只有Java/Android平台通道被实现。

插件是在Pixel 2模拟器(API 29)上开发的,并在实际的Pixel 2设备(Android 11)和另一台Pixel 2模拟器(API 30)上进行了测试。目前不支持iOS,因为尚未实现平台通道。

最小SDK版本 >= 24:你可以在"/android/app/build.gradle"文件中更新最小SDK要求,将minSdkVersion 16改为minSdkVersion 24

在你的项目中的"android/app/src/main/AndroidManifest.xml"文件中,需要添加以下权限:

<uses-permission android:name="android.permission.RECORD_AUDIO" />

这是我的第一个(也是目前唯一的一个)Flutter插件和Java项目。

我在为个人吉他调音器应用收集想法时,意识到在Flutter中找不到任何音频分析/处理/操作的示例。由于Flutter刚开始流行,因此围绕它的实际项目或示例还很少。

最后,我想到的办法是为Android编写一个平台通道,这基本上是一种从Flutter内部调用原生代码的方式——即通过Dart代码调用Java函数。

问题在于,由于它调用了原生平台代码,因此“单一代码库”的Flutter特性变得无用,因为我必须为两个平台编写相同的功能(iOS使用Objective-C/Swift,而Android使用Java/Kotlin)。因此,目前我只实现了Android平台通道。

如何使用

如上所述,该插件最初是为了在我的个人项目中使用而开发的,但我找不到类似的实现,所以我决定在这里上传它,以防其他人也遇到同样的问题。

由于这个原因,你可以使用插件的功能非常严格:开始录音,从平台通道获取数据,然后停止录音。

如果你会编程,你可以轻松地修改代码以满足自己的需求。

有很多用于处理和默认数据的getter和setter,这些将在下面讨论。

简单示例:

import 'package:flutter/material.dart';
import 'package:flutter_fft/flutter_fft.dart';

void main() => runApp(Application());

class Application extends StatefulWidget {
  [@override](/user/override)
  ApplicationState createState() => ApplicationState();
}

class ApplicationState extends State<Application> {
  double? frequency;
  String? note;
  int? octave;
  bool? isRecording;

  FlutterFft flutterFft = new FlutterFft();

  _initialize() async {
    print("Starting recorder...");
    // print("Before");
    // bool hasPermission = await flutterFft.checkPermission();
    // print("After: " + hasPermission.toString());

    // Keep asking for mic permission until accepted
    while (!(await flutterFft.checkPermission())) {
      flutterFft.requestPermission();
      // IF DENY QUIT PROGRAM
    }

    // await flutterFft.checkPermissions();
    await flutterFft.startRecorder();
    print("Recorder started...");
    setState(() => isRecording = flutterFft.getIsRecording);

    flutterFft.onRecorderStateChanged.listen(
        (data) => {
              print("Changed state, received: $data"),
              setState(
                () => {
                  frequency = data[1] as double,
                  note = data[2] as String,
                  octave = data[5] as int,
                },
              ),
              flutterFft.setNote = note!,
              flutterFft.setFrequency = frequency!,
              flutterFft.setOctave = octave!,
              print("Octave: ${octave!.toString()}")
            },
        onError: (err) {
          print("Error: $err");
        },
        onDone: () => {print("Isdone")});
  }

  [@override](/user/override)
  void initState() {
    isRecording = flutterFft.getIsRecording;
    frequency = flutterFft.getFrequency;
    note = flutterFft.getNote;
    octave = flutterFft.getOctave;
    super.initState();
    _initialize();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
        title: "Simple flutter fft example",
        theme: ThemeData.dark(),
        color: Colors.blue,
        home: Scaffold(
          backgroundColor: Colors.purple,
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                isRecording!
                    ? Text("Current note: ${note!},${octave!.toString()}",
                        style: TextStyle(fontSize: 30))
                    : Text("Not Recording", style: TextStyle(fontSize: 35)),
                isRecording!
                    ? Text(
                        "Current frequency: ${frequency!.toStringAsFixed(2)}",
                        style: TextStyle(fontSize: 30))
                    : Text("Not Recording", style: TextStyle(fontSize: 35))
              ],
            ),
          ),
        ));
  }
}

更多关于Flutter快速傅里叶变换插件flutter_fft的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter快速傅里叶变换插件flutter_fft的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何使用Flutter中的flutter_fft插件进行快速傅里叶变换(FFT)的代码案例。这个插件允许你对音频信号进行频域分析。

首先,确保你已经在pubspec.yaml文件中添加了flutter_fft依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_fft: ^x.x.x  # 请替换为最新版本号

然后,运行flutter pub get来安装依赖。

以下是一个完整的Flutter应用程序示例,它展示了如何使用flutter_fft插件对生成的简单正弦波信号进行FFT分析:

import 'package:flutter/material.dart';
import 'package:flutter_fft/flutter_fft.dart';
import 'dart:typed_data';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter FFT Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: FFTExampleScreen(),
    );
  }
}

class FFTExampleScreen extends StatefulWidget {
  @override
  _FFTExampleScreenState createState() => _FFTExampleScreenState();
}

class _FFTExampleScreenState extends State<FFTExampleScreen> {
  List<double> _timeDomainData = [];
  List<Complex> _frequencyDomainData = [];
  FFT _fft = FFT(1024); // 1024 points FFT

  @override
  void initState() {
    super.initState();
    _generateTimeDomainData();
    _performFFT();
  }

  void _generateTimeDomainData() {
    final sampleRate = 44100.0; // Sampling rate in Hz
    final frequency = 440.0; // A4 note in Hz
    final duration = 2.0; // Duration in seconds

    final t = List.generate(1024, (i) => i / sampleRate);
    _timeDomainData = t.map((time) => 0.5 * math.sin(2 * math.pi * frequency * time)).toList();
  }

  void _performFFT() {
    final Float32List realInput = Float32List.fromList(_timeDomainData);
    final Float32List imagInput = Float32List.filled(1024, 0.0); // Imaginary part is zero for real signals

    final ComplexList input = ComplexList.fromRealAndImag(realInput, imagInput);
    final ComplexList output = _fft.forwardTransform(input);

    setState(() {
      _frequencyDomainData = output.toList();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter FFT Example'),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text('Time Domain Data:'),
          Text('${_timeDomainData.sublist(0, 10).join(", ")}...'), // Display first 10 samples for brevity
          SizedBox(height: 20),
          Text('Frequency Domain Data:'),
          Text('Magnitudes: ${_frequencyDomainData.map((c) => c.magnitude.toStringAsFixed(2)).join(", ")}...'),
        ],
      ),
    );
  }
}

解释

  1. 依赖添加:在pubspec.yaml中添加flutter_fft依赖。
  2. 生成时域数据:在_generateTimeDomainData方法中,生成一个简单的正弦波信号作为时域数据。这里我们生成了一个440Hz(A4音)的正弦波,持续2秒,采样率为44100Hz。
  3. 执行FFT:在_performFFT方法中,将生成的时域数据转换为复数列表(实部为信号值,虚部为0),然后执行FFT变换。结果存储在_frequencyDomainData中。
  4. 显示结果:在UI中显示时域数据和频域数据的部分样本。为了简洁起见,这里只显示了时域数据的前10个样本和频域数据的幅度值。

这个示例展示了如何使用flutter_fft插件进行基本的FFT分析。你可以根据具体需求对代码进行修改和扩展,例如处理来自麦克风的实时音频数据,或绘制频域数据的图形表示。

回到顶部