Flutter MIDI输入输出插件midi_io的使用
Flutter MIDI输入输出插件midi_io的使用
在Android、iOS和MacOS上发送和接收MIDI消息!
该插件为与MIDI设备交互提供了Flutter绑定。API设计大量借鉴了Web MIDI规范,提供了一个统一的API,封装了iOS和MacOS上的Core Midi以及Android上的androidx.media.midi。
术语
为了提供一个统一且平台无关的Flutter API,插件使用了以下术语:
-
设备:设备是提供一个或多个MIDI输入和输出端口的MIDI硬件或软件。该插件将设备抽象化,仅提供一个统一的源和目标端口列表供读取和写入。如果您来自Web MIDI或Core MIDI,这属于常规做法,但Android有设备抽象,因此这种工作方式可能是新的。
-
源:源是您的应用可以监听MIDI消息的端口。这相当于WebMIDI中的
MIDIInput
。 -
目标:目标是您的应用可以发送消息到的端口。这相当于WebMIDI中的
MIDIOutput
。
入门
Midi
类是进入MIDI系统的主入口点。
final midi = Midi();
列出已连接的端口
您可以像这样获取已连接的源和目标列表:
final midi = Midi();
final List<MidiSourcePort> sources = await midi.getSources();
final List<MidiDestinationPort> destinations = await midi.getDestinations();
此外,您还可以监听设备插入和拔出时发生的连接事件流:
await for (ConnectionEvent event in midi.onDevicesChanged) {
// 处理事件
}
onDevicesChanged
流的用例包括自动连接到特定设备等。
发送消息
要向目标端口发送MIDI消息,首先打开它,然后使用send
方法:
final List<MidiDestinationPort> destinations = await midi.getDestinations();
final myDestination = destinations.first;
await myDestination.open();
// 在中间C键按下
await myDestination.send([0x90, 0x3C, 0x40]);
监听消息
监听来自MIDI源的消息需要打开并订阅messages
流:
final List<MidiSourcePort> sources = await midi.getSources();
final mySource = sources.first;
await mySource.open();
await for (var message in mySource.messages) {
// 处理MIDI消息
}
过滤
由于消息是作为流发送的,您可以使用标准的Dart技术来过滤MIDI消息。
例如,我们提供了一个内置过滤器来隐藏MIDI时钟消息:
await for (var message in mySource.messages.where(excludeClock)) {
// 处理MIDI消息
}
贡献
该插件仍在开发中,由于MIDI使用的广泛性和不同平台的具体情况,有很多空间可以贡献。欢迎提交错误报告和拉取请求!
完整示例Demo
以下是一个完整的示例,展示了如何使用midi_io
插件来列出和操作MIDI设备。
import 'package:flutter/material.dart';
import 'package:midi_io/midi_io.dart';
import 'package:provider/provider.dart';
import './destinations_tab.dart';
import './sources_tab.dart';
void main() => runApp(MidiExample());
class MidiExample extends StatefulWidget {
[@override](/user/override)
State createState() {
return MidiExampleState();
}
}
class MidiExampleState extends State<MidiExample> {
int tabIndex = 0;
void setTab(int index) {
setState(() {
tabIndex = index;
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return Provider<Midi>(
create: (BuildContext context) => Midi(),
child: MaterialApp(
home: Scaffold(
body: tabIndex == 0 ? DestinationsTab() : SourcesTab(),
bottomNavigationBar: BottomNavigationBar(
currentIndex: tabIndex,
onTap: (int index) => setTab(index),
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.file_upload),
label: 'Destinations',
),
BottomNavigationBarItem(
icon: Icon(Icons.file_download),
label: 'Sources',
),
],
),
),
));
}
}
更多关于Flutter MIDI输入输出插件midi_io的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter MIDI输入输出插件midi_io的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用midi_io
插件进行MIDI输入和输出的示例代码。这个插件允许你与MIDI设备进行交互,读取和发送MIDI消息。
首先,确保你的Flutter项目已经添加了midi_io
依赖。在pubspec.yaml
文件中添加以下依赖:
dependencies:
flutter:
sdk: flutter
midi_io: ^x.y.z # 替换为最新版本号
然后运行flutter pub get
来安装依赖。
示例代码
主文件:main.dart
import 'package:flutter/material.dart';
import 'package:midi_io/midi_io.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
MidiAccess? _midiAccess;
List<MidiInputPort>? _inputPorts;
List<MidiOutputPort>? _outputPorts;
String _status = "No MIDI access yet.";
@override
void initState() {
super.initState();
requestMidiAccess();
}
Future<void> requestMidiAccess() async {
try {
_midiAccess = await navigator.requestMidiAccess();
setState(() {
_inputPorts = _midiAccess!.inputs.values.toList();
_outputPorts = _midiAccess!.outputs.values.toList();
_status = "MIDI access granted.";
});
// 监听输入端口
for (var port in _inputPorts!) {
port.onMidiMessage = (MidiMessage message) {
print("Received MIDI message: $message");
};
}
} catch (e) {
setState(() {
_status = "Failed to get MIDI access: $e";
});
}
}
void sendMidiMessage(int status, int data1, int data2) {
if (_outputPorts != null && _outputPorts!.isNotEmpty) {
var outputPort = _outputPorts!.first; // 使用第一个输出端口
var message = MidiMessage.fromStatusData1Data2(status, data1, data2);
outputPort.send([message.data1, message.data2, message.status]);
print("Sent MIDI message: $message");
} else {
print("No MIDI output ports available.");
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('MIDI IO Example'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(_status),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
// 发送一个示例MIDI消息(音符ON,C4,力度64)
sendMidiMessage(0x90, 60, 64);
},
child: Text('Send MIDI Note On'),
),
],
),
),
),
);
}
}
说明
-
请求MIDI访问权限:在
initState
方法中调用requestMidiAccess
函数,该函数会请求MIDI访问权限,并将获取到的MidiAccess
对象存储在_midiAccess
变量中。同时,列出所有的输入和输出端口。 -
监听MIDI消息:在获取到输入端口后,为每个输入端口设置
onMidiMessage
回调,以监听MIDI消息。 -
发送MIDI消息:
sendMidiMessage
函数用于发送MIDI消息。这里我们发送一个简单的音符开(Note On)消息。注意,MIDI消息的格式通常是[状态字节, 数据1, 数据2]
,其中状态字节包含通道和命令信息。 -
UI界面:在UI界面中,有一个按钮用于发送MIDI消息,以及一个文本显示当前的状态或错误信息。
确保在真实设备或支持MIDI的模拟器上运行该应用,以便正确访问MIDI设备。