Flutter光谱分析插件spectrum_lib的使用

Flutter光谱分析插件spectrum_lib的使用

简介

spectrum_lib 是一个用于计算EEG信号频谱的数学库。其主要功能包括原始频谱值的计算以及EEG频谱值的计算。该库支持两种工作模式:迭代模式(添加新数据到内部缓冲区并计算频谱值)和一次性频谱计算模式(针对给定数组进行频谱计算)。在迭代模式下,频谱计算频率由初始化时设置的频率决定。

频谱通过FFT方法计算。可以单独计算任何信号的频谱,无论是原始信号还是经过滤波后的信号。

安装

支持平台

  • Android
  • iOS
  • MacOS
  • Windows
  • Linux

使用Flutter安装

运行以下命令:

$ flutter pub add spectrum_lib

手动安装

  1. pubspec.yaml 文件的依赖项中添加:
    dependencies:
      spectrum_lib: ^1.0.0
    
  2. 运行以下命令以获取依赖项:
    $ flutter pub get
    

然后在Dart代码中导入:

import 'package:spectrum_lib/spectrum_lib.dart';

注意:此SDK需要蓝牙和位置权限,因此需要请求用户授权。

最新版本为 1.0.0

初始化

主要参数

  1. 原始信号采样频率。需要大于等于1。
  2. 频谱计算频率。需要小于等于16 kHz。
  3. 频谱计算窗口长度。需要小于等于信号采样频率。

可选参数

  1. 频率计算上限。默认值为采样率的一半。
  2. 是否对EEG频谱进行带宽归一化。默认禁用。
  3. α、β、θ、γ、δ波的权重系数。默认值为1.0。

创建实例

int samplingRate = 250; // 原始信号采样频率
int fftWindow = 1000;   // 频谱计算窗口长度
int processWinRate = 5; // 频谱计算频率

final math = SpectrumLib(samplingRate, fftWindow, processWinRate);

可选初始化

添加额外频谱设置

int bordFrequency = 50; // 频率计算上限
bool normalizeSpectByBandwidth = true; // 是否对EEG频谱进行带宽归一化
math.initParams(bordFrequency, normalizeSpectByBandwidth);

设置波形系数

double deltaCoef = 0.0;
double thetaCoef = 1.0;
double alphaCoef = 1.0;
double betaCoef = 1.0;
double gammaCoef = 0.0;
math.setWavesCoeffs(deltaCoef, thetaCoef, alphaCoef, betaCoef, gammaCoef);

设置平滑窗口类型

math.setHammingWinSpectrum(); // 使用汉宁窗
math.setHanningWinSpectrum(); // 使用汉明窗

初始化数据数组

用于传递给库的数据数组应为双精度浮点数类型的数组,且长度不大于15倍的信号采样频率。

类型定义

RawSpectrumData

包含原始频谱值的结构体(带有边界频率)。

字段:

  • allBinsNums: 整数值。FFT条数量。
  • allBinsValues: 双精度浮点数组。原始FFT条值。
  • totalRawPow: 双精度浮点值。总原始频谱功率。

WavesSpectrumData

包含波形值的结构体。

绝对频率值(双精度类型):

  • delta_raw
  • theta_raw
  • alpha_raw
  • beta_raw
  • gamma_raw

相对值(百分比)(双精度类型):

  • delta_rel
  • theta_rel
  • alpha_rel
  • beta_rel
  • gamma_rel

FFT频段分辨率

库会根据指定的窗口长度自动匹配最优的FFT缓冲区长度(2的幂)。获取FFT频段分辨率(每1Hz的FFT条数):

math.getFFTBinsFor1Hz();

迭代模式下的频谱计算

添加和处理数据

List<double> data = List.empty();
math.pushAndProcessData(data);

获取结果

List<RawSpectrumData> rawSpectrumData = math.readRawSpectrumInfoArr();
List<WavesSpectrumData> wavesSpectrumData = math.readWavesSpectrumInfoArr();

更新样本大小

math.setNewSampleSize();

单次数组的频谱计算

计算频谱

List<double> data = List.empty();
math.computeSpectrum(data);

获取结果

RawSpectrumData rawSpectrumData = math.readRawSpectrumInfo();
WavesSpectrumData wavesSpectrumData = math.readWavesSpectrumInfo();

结束与释放资源

math.dispose();

示例代码

以下是完整的示例代码:

import 'dart:ffi';
import 'dart:math';

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

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

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

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  int samplingRate = 500;
  int processWinRate = 2;
  int fftWindow = 500 * 4;

  late final SpectrumLib spectrumLib;

  [@override](/user/override)
  void initState() {
    super.initState();

    spectrumLib = SpectrumLib(samplingRate, fftWindow, processWinRate);

    int bordFrequency = 30;
    spectrumLib.initParams(bordFrequency, true);
  }

  [@override](/user/override)
  void dispose() {
    spectrumLib.dispose();
    super.dispose();
  }

  void _onButtonPressed() {
    double deltaCoef = 0.0;
    double thetaCoef = 1.0;
    double alphaCoef = 1.0;
    double betaCoef = 1.0;
    double gammaCoef = 0.0;
    spectrumLib.setWavesCoeffs(deltaCoef, thetaCoef, alphaCoef, betaCoef, gammaCoef);

    double nBinsFor1hz = spectrumLib.getFFTBinsFor1Hz();

    int winLen = 500;
    List<double> data = List.filled(winLen, 0.0);

    for (var i = 0; i < 100; i++) {
      for (int j = 0; j < 40; j++) {
        for (int i = 0; i < winLen; i++) {
          data[i] = 10 * sin(10 * i * (2 * pi / samplingRate));
        }

        spectrumLib.pushAndProcessData(data);

        List<RawSpectrumData> rawSpectData = spectrumLib.readRawSpectrumInfoArr();
        nBinsFor1hz = spectrumLib.getFFTBinsFor1Hz();

        for (int i = 0; i < rawSpectData.length; i++) {
          print("Bins: $nBinsFor1hz AllBinsValue: ${rawSpectData[i].allBinsValues.length} RawPow: ${rawSpectData[i].totalRawPow}");
        }

        spectrumLib.setNewSampleSize();
        
      }
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('SpectrumLib example app'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: _onButtonPressed,
                child: const Text('Start Demo'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

更多关于Flutter光谱分析插件spectrum_lib的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter光谱分析插件spectrum_lib的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


spectrum_lib 是一个 Flutter 插件,用于在移动设备上进行光谱分析。这个插件通常用于处理音频信号,提取频率信息,并将其可视化为光谱图。以下是如何在 Flutter 项目中使用 spectrum_lib 的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 spectrum_lib 插件的依赖。

dependencies:
  flutter:
    sdk: flutter
  spectrum_lib: ^1.0.0  # 请检查最新版本

然后运行 flutter pub get 来获取依赖。

2. 导入插件

在你的 Dart 文件中导入 spectrum_lib

import 'package:spectrum_lib/spectrum_lib.dart';

3. 初始化频谱分析器

你可以通过创建 SpectrumAnalyzer 的实例来初始化频谱分析器。

SpectrumAnalyzer spectrumAnalyzer = SpectrumAnalyzer();

4. 开始频谱分析

你可以使用 start 方法来开始频谱分析。通常,你需要提供一个音频源(例如麦克风输入或音频文件)作为输入。

spectrumAnalyzer.start();

5. 获取频谱数据

你可以通过监听 spectrumAnalyzer.onSpectrumData 来获取实时的频谱数据。

spectrumAnalyzer.onSpectrumData.listen((spectrumData) {
  // 处理频谱数据
  print(spectrumData);
});

6. 停止频谱分析

当你不再需要频谱分析时,可以调用 stop 方法来停止分析。

spectrumAnalyzer.stop();

7. 可视化频谱数据

你可以使用 Flutter 的绘图功能(如 CustomPaint 或第三方库如 fl_chart)来将频谱数据可视化。

CustomPaint(
  size: Size(double.infinity, 200),
  painter: SpectrumPainter(spectrumData),
)

8. 处理权限

在 Android 和 iOS 上,你可能需要请求麦克风权限。你可以使用 permission_handler 插件来处理权限请求。

import 'package:permission_handler/permission_handler.dart';

void requestMicrophonePermission() async {
  var status = await Permission.microphone.status;
  if (!status.isGranted) {
    await Permission.microphone.request();
  }
}

9. 完整示例

以下是一个简单的完整示例,展示如何使用 spectrum_lib 进行频谱分析并将其可视化。

import 'package:flutter/material.dart';
import 'package:spectrum_lib/spectrum_lib.dart';
import 'package:permission_handler/permission_handler.dart';

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SpectrumScreen(),
    );
  }
}

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

class _SpectrumScreenState extends State<SpectrumScreen> {
  SpectrumAnalyzer spectrumAnalyzer = SpectrumAnalyzer();
  List<double> spectrumData = [];

  [@override](/user/override)
  void initState() {
    super.initState();
    requestMicrophonePermission();
    startSpectrumAnalysis();
  }

  void requestMicrophonePermission() async {
    var status = await Permission.microphone.status;
    if (!status.isGranted) {
      await Permission.microphone.request();
    }
  }

  void startSpectrumAnalysis() {
    spectrumAnalyzer.start();
    spectrumAnalyzer.onSpectrumData.listen((data) {
      setState(() {
        spectrumData = data;
      });
    });
  }

  [@override](/user/override)
  void dispose() {
    spectrumAnalyzer.stop();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Spectrum Analyzer'),
      ),
      body: Center(
        child: CustomPaint(
          size: Size(double.infinity, 200),
          painter: SpectrumPainter(spectrumData),
        ),
      ),
    );
  }
}

class SpectrumPainter extends CustomPainter {
  final List<double> spectrumData;

  SpectrumPainter(this.spectrumData);

  [@override](/user/override)
  void paint(Canvas canvas, Size size) {
    if (spectrumData.isEmpty) return;

    final paint = Paint()
      ..color = Colors.blue
      ..strokeWidth = 2.0;

    final barWidth = size.width / spectrumData.length;

    for (int i = 0; i < spectrumData.length; i++) {
      final barHeight = spectrumData[i] * size.height;
      canvas.drawRect(
        Rect.fromLTWH(
          i * barWidth,
          size.height - barHeight,
          barWidth,
          barHeight,
        ),
        paint,
      );
    }
  }

  [@override](/user/override)
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}
回到顶部