Flutter MIDI输入输出插件midi_io的使用

发布于 1周前 作者 caililin 来自 Flutter

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

1 回复

更多关于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'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

说明

  1. 请求MIDI访问权限:在initState方法中调用requestMidiAccess函数,该函数会请求MIDI访问权限,并将获取到的MidiAccess对象存储在_midiAccess变量中。同时,列出所有的输入和输出端口。

  2. 监听MIDI消息:在获取到输入端口后,为每个输入端口设置onMidiMessage回调,以监听MIDI消息。

  3. 发送MIDI消息sendMidiMessage函数用于发送MIDI消息。这里我们发送一个简单的音符开(Note On)消息。注意,MIDI消息的格式通常是[状态字节, 数据1, 数据2],其中状态字节包含通道和命令信息。

  4. UI界面:在UI界面中,有一个按钮用于发送MIDI消息,以及一个文本显示当前的状态或错误信息。

确保在真实设备或支持MIDI的模拟器上运行该应用,以便正确访问MIDI设备。

回到顶部