Flutter音频识别插件tflite_audio的使用
Flutter音频识别插件tflite_audio的使用
简介
tflite_audio
是一个用于Flutter的音频分类插件,支持iOS和Android平台。它不仅可以处理存储的音频文件,还可以处理实时录音,并支持Google Teachable Machine模型。
插件特点
- 音频文件识别:支持存储的单声道WAV文件。
- 实时录音识别:支持实时录音并进行音频分类。
- 可调参数:提供多种参数供用户调整录音和推理过程。
- 自动重塑音频输入:自动调整音频输入的形状和转置。
- 多种模型类型支持:
- Google Teachable Machine(原始音频输入)
- 原始音频输入
- 解码WAV输入
- 实验性特征:频谱图、梅尔频谱图、MFCC输入
安装与配置
添加依赖
在pubspec.yaml
文件中添加tflite_audio
依赖:
dependencies:
tflite_audio: ^最新版本号
运行以下命令安装依赖:
flutter pub get
添加模型和标签
- 创建一个
assets
文件夹,并将你的tflite模型和标签文件放入其中。 - 在
pubspec.yaml
文件中添加模型和标签文件路径:
assets:
- assets/your_model.tflite
- assets/your_labels.txt
权限设置
Android
在AndroidManifest.xml
文件中添加录音权限:
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
在build.gradle
文件中添加以下配置:
aaptOptions {
noCompress 'tflite'
}
如果使用Google Teachable Machine模型,还需要在build.gradle
中启用select-ops:
dependencies {
implementation 'org.tensorflow:tensorflow-lite-select-tf-ops:+'
}
iOS
在Info.plist
文件中添加麦克风使用描述:
<key>NSMicrophoneUsageDescription</key>
<string>Record audio for playback</string>
在Podfile
中设置最低iOS版本:
platform :ios, '12.0'
如果使用Google Teachable Machine模型,还需要在Podfile
中添加以下行:
pod 'TensorFlowLiteSelectTfOps', '~> 2.6.0'
并在Xcode中强制加载Select Ops:
- 打开项目工作区:
<YourApp>/ios/Runner.xcworkspace
- 选择顶级Runner
- 选择Runner项目
- 在“Build Settings”选项卡中,点击“Other Linker Flags”
- 添加以下行:
-force_load $(SRCROOT)/Pods/TensorFlowLiteSelectTfOps/Frameworks/TensorFlowLiteSelectTfOps.framework/TensorFlowLiteSelectTfOps
使用示例
示例代码
以下是一个完整的示例代码,展示了如何使用tflite_audio
插件进行音频识别:
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:developer';
import 'package:tflite_audio/tflite_audio.dart';
import 'package:flutter/services.dart';
import 'dart:convert';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final isRecording = ValueNotifier<bool>(false);
Stream<Map<dynamic, dynamic>>? result;
// Google Teachable Machine模型示例
final String model = 'assets/google_teach_machine_model.tflite';
final String label = 'assets/google_teach_machine_label.txt';
final String inputType = 'rawAudio';
final String audioDirectory = 'assets/sample_audio_44k_mono.wav';
final int sampleRate = 44100;
final int bufferSize = 11016;
// 可选参数
final bool outputRawScores = false;
final int numOfInferences = 5;
final int numThreads = 1;
final bool isAsset = true;
// 调整模型检测参数
final double detectionThreshold = 0.3;
final int averageWindowDuration = 1000;
final int minimumTimeBetweenSamples = 30;
final int suppressionTime = 1500;
@override
void initState() {
super.initState();
TfliteAudio.loadModel(
inputType: inputType,
model: model,
label: label,
);
}
void getResult() {
result = TfliteAudio.startAudioRecognition(
sampleRate: sampleRate,
bufferSize: bufferSize,
numOfInferences: numOfInferences,
detectionThreshold: detectionThreshold,
averageWindowDuration: averageWindowDuration,
minimumTimeBetweenSamples: minimumTimeBetweenSamples,
suppressionTime: suppressionTime,
);
result?.listen((event) => log("Recognition Result: " + event["recognitionResult"].toString())).onDone(() => isRecording.value = false);
}
Future<List<String>> fetchLabelList() async {
List<String> _labelList = [];
await rootBundle.loadString(label).then((q) {
for (String i in const LineSplitter().convert(q)) {
_labelList.add(i);
}
});
return _labelList;
}
String showResult(AsyncSnapshot snapshot, String key) => snapshot.hasData ? snapshot.data[key].toString() : '0 ';
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: const Text('Tflite-audio/speech'),
),
body: StreamBuilder<Map<dynamic, dynamic>>(
stream: result,
builder: (BuildContext context, AsyncSnapshot<Map<dynamic, dynamic>> inferenceSnapshot) {
return FutureBuilder(
future: fetchLabelList(),
builder: (BuildContext context, AsyncSnapshot<List<String>> labelSnapshot) {
switch (inferenceSnapshot.connectionState) {
case ConnectionState.none:
if (labelSnapshot.hasData) {
return labelListWidget(labelSnapshot.data);
} else {
return const CircularProgressIndicator();
}
case ConnectionState.waiting:
return Stack(children: [
Align(
alignment: Alignment.bottomRight,
child: inferenceTimeWidget('calculating..'),
),
labelListWidget(labelSnapshot.data),
]);
default:
return Stack(children: [
Align(
alignment: Alignment.bottomRight,
child: inferenceTimeWidget(showResult(inferenceSnapshot, 'inferenceTime') + 'ms'),
),
labelListWidget(labelSnapshot.data, showResult(inferenceSnapshot, 'recognitionResult')),
]);
}
},
);
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: ValueListenableBuilder(
valueListenable: isRecording,
builder: (context, value, widget) {
if (value == false) {
return FloatingActionButton(
onPressed: () {
isRecording.value = true;
setState(() {
getResult();
});
},
backgroundColor: Colors.blue,
child: const Icon(Icons.mic),
);
} else {
return FloatingActionButton(
onPressed: () {
log('Audio Recognition Stopped');
TfliteAudio.stopAudioRecognition();
},
backgroundColor: Colors.red,
child: const Icon(Icons.adjust),
);
}
},
),
),
);
}
Widget labelListWidget(List<String>? labelList, [String? result]) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: labelList!.map((labels) {
if (labels == result) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: Text(
labels.toString(),
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
color: Colors.green,
),
),
);
} else {
return Padding(
padding: const EdgeInsets.all(5.0),
child: Text(
labels.toString(),
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
);
}
}).toList(),
),
);
}
Widget inferenceTimeWidget(String result) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Text(
result,
textAlign: TextAlign.center,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
color: Colors.black,
),
),
);
}
}
代码说明
- 初始化模型:在
initState
方法中加载模型和标签文件。 - 开始录音识别:通过
startAudioRecognition
方法启动录音识别,并设置相关参数。 - 结果监听:使用
StreamBuilder
监听识别结果,并在界面上显示。 - 浮行动作按钮:控制录音的开始和停止。
参数说明
outputRawScores
:是否输出原始分数。numThreads
:线程数,增加线程数可以减少推理时间,但会占用更多CPU资源。isAsset
:模型、标签或音频文件是否在资产文件中。numOfInferences
:录音和推理的次数。sampleRate
:采样率,推荐值为16000、22050、44100。bufferSize
:缓冲区大小,影响录音长度。detectionThreshold
:检测阈值,忽略概率低于该阈值的预测。averageWindowDuration
:用于移除过时的结果。minimumTimeBetweenSamples
:忽略频繁的结果。suppressionTime
:避免过早触发检测。
常见问题
-
如何调整录音长度/时间:
- 降低
bufferSize
值可以增加录音时间。 - 降低采样率也可以增加录音时间。
- 注意不要将值设置得过低,以免影响模型准确性。
- 降低
-
如何减少模型的误报:
- 增加
detectionThreshold
和averageWindowDuration
的默认值。
- 增加
-
iOS构建错误:
- 检查
Podfile
和build.gradle
中的配置。 - 运行
flutter clean
和flutter pub get
。
- 检查
-
TensorFlow Lite错误:
- 确保启用了select-ops。
- 尝试在实际设备上运行应用。
希望这些信息对你有所帮助!如果有任何问题,欢迎随时提问。
更多关于Flutter音频识别插件tflite_audio的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter音频识别插件tflite_audio的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用tflite_audio
插件进行音频识别的示例代码。这个插件允许你使用TensorFlow Lite模型进行音频识别。
首先,确保你已经在pubspec.yaml
文件中添加了tflite_audio
和tflite
依赖:
dependencies:
flutter:
sdk: flutter
tflite: ^2.0.0 # 请检查最新版本号
tflite_audio: ^0.6.0 # 请检查最新版本号
然后运行flutter pub get
来安装这些依赖。
接下来,你需要一个已经训练好的TensorFlow Lite模型。假设你已经有一个模型文件model.tflite
,并且这个模型接受音频输入并输出识别结果。
下面是一个简单的Flutter应用示例,展示如何使用tflite_audio
插件加载模型并进行音频识别:
import 'package:flutter/material.dart';
import 'package:tflite/tflite.dart';
import 'package:tflite_audio/tflite_audio.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late Interpreter _interpreter;
late TfliteAudio? _tfliteAudio;
@override
void initState() {
super.initState();
loadModel();
}
@override
void dispose() {
_interpreter.close();
_tfliteAudio?.dispose();
super.dispose();
}
Future<void> loadModel() async {
// Load TensorFlow Lite model
_interpreter = await Tflite.loadModel(
model: 'assets/model.tflite',
labels: 'assets/labels.txt', // 如果你的模型有标签文件
);
// Initialize TfliteAudio
_tfliteAudio = TfliteAudio(
interpreter: _interpreter,
sampleRate: 16000, // 根据你的模型要求设置采样率
numChannels: 1, // 根据你的模型要求设置通道数
audioInputFormat: AudioInputFormat.PCM16,
);
}
Future<void> recognizeSpeech(File audioFile) async {
try {
var recognitionResult = await _tfliteAudio!.recognizeSpeechFromFile(audioFile.path);
print('Recognition Result: $recognitionResult');
// 在这里处理识别结果
} catch (e) {
print('Error during speech recognition: $e');
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Audio Recognition'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: () async {
// 这里你需要让用户选择一个音频文件,或者录制一个音频文件
// 这里假设你已经有一个音频文件路径 audioFilePath
String audioFilePath = 'path/to/your/audio/file.wav'; // 替换为你的音频文件路径
File audioFile = File(audioFilePath);
await recognizeSpeech(audioFile);
},
child: Text('Recognize Speech'),
),
],
),
),
),
);
}
}
注意事项:
-
模型文件:确保你的
model.tflite
和labels.txt
(如果有)文件已经放在assets
文件夹中,并且在pubspec.yaml
中正确声明:flutter: assets: - assets/model.tflite - assets/labels.txt
-
音频文件:在上面的示例中,
recognizeSpeech
函数接受一个File
对象作为音频输入。你需要确保音频文件的格式和采样率与你的TensorFlow Lite模型相匹配。 -
权限:如果你的应用需要录制音频,确保在
AndroidManifest.xml
和Info.plist
中声明了相应的权限。 -
错误处理:在实际应用中,你应该添加更多的错误处理和用户反馈机制,以处理模型加载失败、音频文件读取错误等情况。
这个示例提供了一个基本的框架,你可以根据需要进行扩展和修改。