Flutter MIDI控制插件flutter_midi_command的使用
Flutter MIDI控制插件flutter_midi_command的使用
flutter_midi_command
是一个用于在Flutter应用中发送和接收MIDI消息的插件,它支持与物理和虚拟MIDI设备进行通信。以下是关于该插件的详细介绍,包括安装、配置以及如何编写简单的示例代码。
支持平台
Transports | iOS | macOS | Android | Linux | Windows |
---|---|---|---|---|---|
USB | ✓ | ✓ | ✓ | ✓ | ✓ |
BLE | ✓ | ✓ | ✓ | ✗ | ✓ |
Virtual | ✓ | ✓ | ✓ | ✗ | ✗ |
Network Session | ✓ | ✓ | ✗ | ✗ | ✗ |
安装
-
确保项目使用Kotlin和Swift:创建Flutter项目时,请确保选择了Kotlin(Android)和Swift(iOS)作为原生语言。
-
添加依赖:在
pubspec.yaml
文件中添加以下行:dependencies: flutter_midi_command: ^0.5.1
-
配置iOS:
- 在
ios/Podfile
中设置最低版本为11.0:platform :ios, '11.0'
- 在
info.plist
文件中添加蓝牙和网络权限说明:<key>NSBluetoothAlwaysUsageDescription</key> <string>We need bluetooth permissions to connect to MIDI devices.</string> <key>NSLocalNetworkUsageDescription</key> <string>We need local network access for MIDI over network.</string>
- 在
-
Linux环境:确保已安装ALSA库。
使用指南
导入库
首先需要导入flutter_midi_command
库:
import 'package:flutter_midi_command/flutter_midi_command.dart';
基本操作
-
获取所有MIDI设备列表:
final MidiCommand midiCommand = MidiCommand(); List<MidiDevice> devices = await midiCommand.devices;
-
启动蓝牙中心(仅限BLE传输):
await midiCommand.startBluetoothCentral();
-
监听蓝牙状态变化:
StreamSubscription<BluetoothState>? _bluetoothStateSubscription = midiCommand.onBluetoothStateChanged.listen((state) { print('Bluetooth state changed to $state'); });
-
扫描并连接BLE MIDI设备:
// 开始扫描 await midiCommand.startScanningForBluetoothDevices(); // 连接到指定设备 await midiCommand.connectToDevice(selectedDevice); // 停止扫描 await midiCommand.stopScanningForBluetoothDevices();
-
监听MIDI设置更改:
StreamSubscription<String>? _setupSubscription = midiCommand.onMidiSetupChanged?.listen((data) { print("MIDI setup changed: $data"); });
-
接收MIDI数据:
StreamSubscription<List<int>>? _midiDataSubscription = midiCommand.onMidiDataReceived.listen((data) { print("Received MIDI data: ${data.toList()}"); });
-
发送MIDI消息:
// 发送Note On消息 List<int> noteOnMessage = [0x90, 60, 100]; // Channel 0, Note C4, Velocity 100 await midiCommand.sendData(noteOnMessage);
-
创建虚拟MIDI设备:
await midiCommand.addVirtualDevice(name: "My Virtual MIDI Device");
示例代码
下面是一个完整的Dart程序示例,展示了如何使用flutter_midi_command
插件来列出可用的MIDI设备,并允许用户通过界面与这些设备交互。
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_midi_command/flutter_midi_command.dart';
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> {
StreamSubscription<String>? _setupSubscription;
StreamSubscription<BluetoothState>? _bluetoothStateSubscription;
final MidiCommand _midiCommand = MidiCommand();
bool _virtualDeviceActivated = false;
bool _iOSNetworkSessionEnabled = false;
bool _didAskForBluetoothPermissions = false;
@override
void initState() {
super.initState();
_setupSubscription = _midiCommand.onMidiSetupChanged?.listen((data) async {
if (kDebugMode) {
print("setup changed $data");
}
setState(() {});
});
_bluetoothStateSubscription =
_midiCommand.onBluetoothStateChanged.listen((data) {
if (kDebugMode) {
print("bluetooth state change $data");
}
setState(() {});
});
_updateNetworkSessionState();
}
@override
void dispose() {
_setupSubscription?.cancel();
_bluetoothStateSubscription?.cancel();
super.dispose();
}
Future<void> _updateNetworkSessionState() async {
var nse = await _midiCommand.isNetworkSessionEnabled;
if (nse != null) {
setState(() {
_iOSNetworkSessionEnabled = nse;
});
}
}
IconData _deviceIconForType(String type) {
switch (type) {
case "native":
return Icons.devices;
case "network":
return Icons.language;
case "BLE":
return Icons.bluetooth;
default:
return Icons.device_unknown;
}
}
Future<void> _informUserAboutBluetoothPermissions(
BuildContext context) async {
if (_didAskForBluetoothPermissions) {
return;
}
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: const Text(
'Please Grant Bluetooth Permissions to discover BLE MIDI Devices.'),
content: const Text(
'In the next dialog we might ask you for bluetooth permissions.\n'
'Please grant permissions to make bluetooth MIDI possible.'),
actions: <Widget>[
TextButton(
child: const Text('Ok. I got it!'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
_didAskForBluetoothPermissions = true;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('FlutterMidiCommand Example'),
actions: <Widget>[
Switch(
value: _iOSNetworkSessionEnabled,
onChanged: (newValue) {
_midiCommand.setNetworkSessionEnabled(newValue);
setState(() {
_iOSNetworkSessionEnabled = newValue;
});
}),
Switch(
value: _virtualDeviceActivated,
onChanged: (newValue) {
setState(() {
_virtualDeviceActivated = newValue;
});
if (newValue) {
_midiCommand.addVirtualDevice(name: "Flutter MIDI Command");
} else {
_midiCommand.removeVirtualDevice(
name: "Flutter MIDI Command");
}
}),
Builder(builder: (context) {
return IconButton(
onPressed: () async {
// Ask for bluetooth permissions
await _informUserAboutBluetoothPermissions(context);
// Start bluetooth
if (kDebugMode) {
print("start ble central");
}
await _midiCommand
.startBluetoothCentral()
.catchError((err) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(err),
));
});
if (kDebugMode) {
print("wait for init");
}
await _midiCommand
.waitUntilBluetoothIsInitialized()
.timeout(const Duration(seconds: 5), onTimeout: () {
if (kDebugMode) {
print("Failed to initialize Bluetooth");
}
});
// If bluetooth is powered on, start scanning
if (_midiCommand.bluetoothState ==
BluetoothState.poweredOn) {
_midiCommand
.startScanningForBluetoothDevices()
.catchError((err) {
if (kDebugMode) {
print("Error $err");
}
});
if (context.mounted) {
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(
content: Text('Scanning for bluetooth devices ...'),
));
}
} else {
final messages = {
BluetoothState.unsupported:
'Bluetooth is not supported on this device.',
BluetoothState.poweredOff:
'Please switch on bluetooth and try again.',
BluetoothState.poweredOn: 'Everything is fine.',
BluetoothState.resetting:
'Currently resetting. Try again later.',
BluetoothState.unauthorized:
'This app needs bluetooth permissions. Please open settings, find your app and assign bluetooth access rights and start your app again.',
BluetoothState.unknown:
'Bluetooth is not ready yet. Try again later.',
BluetoothState.other:
'This should never happen. Please inform the developer of your app.',
};
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.red,
content: Text(messages[_midiCommand.bluetoothState] ??
'Unknown bluetooth state: ${_midiCommand.bluetoothState}'),
));
}
}
if (kDebugMode) {
print("done");
}
// If not show a message telling users what to do
setState(() {});
},
icon: const Icon(Icons.refresh));
}),
],
),
bottomNavigationBar: Container(
padding: const EdgeInsets.all(24.0),
child: const Text(
"Tap to connnect/disconnect, long press to control.",
textAlign: TextAlign.center,
),
),
body: Center(
child: FutureBuilder(
future: _midiCommand.devices,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData && snapshot.data != null) {
var devices = snapshot.data as List<MidiDevice>;
return ListView.builder(
itemCount: devices.length,
itemBuilder: (context, index) {
MidiDevice device = devices[index];
return ListTile(
title: Text(
device.name,
style: Theme.of(context).textTheme.headlineSmall,
),
subtitle: Text(
"ins:${device.inputPorts.length} outs:${device.outputPorts.length}, ${device.id}, ${device.type}"),
leading: Icon(device.connected
? Icons.radio_button_on
: Icons.radio_button_off),
trailing: Icon(_deviceIconForType(device.type)),
onLongPress: () {
_midiCommand.stopScanningForBluetoothDevices();
// Navigate to controller page or perform other actions
},
onTap: () {
if (device.connected) {
if (kDebugMode) {
print("disconnect");
}
_midiCommand.disconnectDevice(device);
} else {
if (kDebugMode) {
print("connect");
}
_midiCommand.connectToDevice(device).then((_) {
if (kDebugMode) {
print("device connected async");
}
}).catchError((err) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
"Error: ${(err as PlatformException?)?.message}")));
});
}
},
);
},
);
} else {
return const CircularProgressIndicator();
}
},
),
),
),
);
}
}
以上就是flutter_midi_command
插件的基本用法及示例代码。希望这能帮助你更好地理解和使用这个强大的MIDI控制工具!如果有任何问题或需要进一步的帮助,请随时提问。
更多关于Flutter MIDI控制插件flutter_midi_command的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter MIDI控制插件flutter_midi_command的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何使用Flutter MIDI控制插件 flutter_midi_command
的代码示例。这个示例将展示如何连接到MIDI设备、发送MIDI消息以及监听MIDI输入。
首先,你需要在你的 pubspec.yaml
文件中添加 flutter_midi_command
依赖:
dependencies:
flutter:
sdk: flutter
flutter_midi_command: ^最新版本号 # 请替换为实际的最新版本号
然后,运行 flutter pub get
来获取依赖。
接下来是示例代码:
import 'package:flutter/material.dart';
import 'package:flutter_midi_command/flutter_midi_command.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
MidiDevice? connectedDevice;
String midiInput = "";
@override
void initState() {
super.initState();
// 请求MIDI权限
MidiCommand.requestMidiAccess().then((access) {
setState(() {
access.inputs.forEach((input) {
print("Available MIDI input: ${input.name}");
// 这里可以添加选择设备的逻辑,例如通过UI让用户选择
// connectedDevice = input.port;
});
access.outputs.forEach((output) {
print("Available MIDI output: ${output.name}");
// 自动连接第一个输出设备(仅示例,实际使用可能需要用户选择)
if (access.outputs.isNotEmpty) {
connectedDevice = access.outputs.first.port;
}
});
});
// 监听MIDI输入
access.inputs.forEach((input) {
input.onmidimessage = (message) {
setState(() {
midiInput = "Received MIDI message: ${message.data.join(', ')}";
});
};
});
});
}
void sendMidiMessage() {
if (connectedDevice != null) {
// 示例:发送一个简单的音符消息(C4,即中央C,MIDI音符60)
List<int> noteOnMessage = [0x90, 60, 0x7F]; // Note On with velocity 127
List<int> noteOffMessage = [0x80, 60, 0x00]; // Note Off with velocity 0
MidiCommand.sendMidiMessage(connectedDevice!, noteOnMessage);
Future.delayed(Duration(seconds: 1), () {
MidiCommand.sendMidiMessage(connectedDevice!, noteOffMessage);
});
} else {
print("No MIDI device connected.");
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter MIDI Control Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(midiInput),
SizedBox(height: 20),
ElevatedButton(
onPressed: sendMidiMessage,
child: Text('Send MIDI Note'),
),
],
),
),
),
);
}
}
解释
-
依赖添加:在
pubspec.yaml
中添加flutter_midi_command
依赖。 -
权限请求:在
initState
方法中,使用MidiCommand.requestMidiAccess()
请求MIDI访问权限。 -
设备选择:遍历可用的MIDI输入和输出设备,并打印它们的名称。实际应用中,你可能需要通过UI让用户选择设备。
-
监听MIDI输入:为每个MIDI输入设备设置
onmidimessage
回调,以监听MIDI消息。 -
发送MIDI消息:定义一个
sendMidiMessage
方法,用于向已连接的MIDI设备发送MIDI消息(例如,发送一个音符消息)。 -
UI:使用Flutter的UI组件(如
Text
和ElevatedButton
)来显示接收到的MIDI消息并提供发送MIDI消息的按钮。
这个示例展示了基本的MIDI设备连接、消息发送和接收功能。实际应用中,你可能需要根据具体需求进行扩展和调整。