用 WebMIDI 踏入硬件世界

Avatar of George Mandis
George Mandis

DigitalOcean 为您的旅程的每个阶段提供云产品。立即开始使用 200 美元的免费积分!

您知道有一个得到广泛支持的浏览器 API,它允许您使用一个比网络历史更悠久的成熟协议来与有趣的甚至自定义构建的硬件交互吗?让我向您介绍 MIDI 和 WebMIDI API,并向您展示它如何为前端开发人员提供一个独特的机会,让他们打破浏览器的界限,在不离开 JavaScript 和 DOM 的相对舒适度的同时,涉足硬件编程的世界。

MIDI 和 WebMIDI 到底是什么?

MIDI 是一种专门为乐器之间通信而设计的协议。它于 1983 年标准化,至今仍由一个由音乐行业公司和代表组成的组织维护。从某种意义上说,它与 W3C 制定和维护网络标准的方式并没有太大的不同。

照片由 JiroeUnsplash 上拍摄

WebMIDI API 是该协议的基于浏览器的实现,它允许我们的 Web 应用程序“说”MIDI 并与连接到用户设备的任何支持 MIDI 的硬件通信。

不是音乐家?别担心!我们会很快发现,这种为电子乐器而设计的简单协议可以用来构建有趣、互动且完全非音乐的东西。

我为什么要这样做?

好问题。最短的答案:因为它很有趣!

如果这个答案不能让您满意,那么我会提供以下答案:创建一些东西,它跨越了物理世界和我们大部分时间都在为之构建东西的虚拟世界的界限,这是一种换个角度思考的好练习。这是一个创造性修补和考虑和创建新的用户界面和体验以进行导航的机会。我真心地认为,这种玩乐探索有助于我们利用大脑的不同部位,并使我们成为更优秀的开发人员,从而取得长远利益。

我能用它构建什么?

MIDI 控制器上的元胞自动机
玩围棋

 

打地鼠
用手的动作混合颜色

我需要什么才能开始?

以下是要开始使用 WebMIDI 的先决条件

MIDI 控制器

这可能是最棘手的部分。您需要购买一个支持 MIDI 的硬件来进行实验。您可能能够在 Craigslist、Amazon 或 AliExpress 上找到一些便宜的。或者,如果您真的很有雄心,并且有 Arduino,您可以自己构建一个(请参阅本文末尾有关此方面的更多信息)。

支持 WebMIDI 的浏览器

此浏览器支持数据来自 Caniuse,其中包含更多详细信息。数字表示该浏览器在该版本及更高版本上支持该功能。

桌面

ChromeFirefoxIEEdgeSafari
4310879

移动设备/平板电脑

Android ChromeAndroid FirefoxAndroidiOS Safari
127127

截至目前,根据 caniuse.com 的数据,它受到 大约 73% 浏览器的支持,不过大部分工作是由 Chromium 完成的。任何基于 Chromium 的浏览器都将支持 WebMIDI - 这包括 Electron 应用程序和基于 Chromium 的新版 Microsoft Edge。它还受到 Opera 和 Samsung Internet Browser 的支持。在 Firefox 上,它 仍在讨论中,但希望尽快推出。

你好,WebMIDI

一旦您获得了这两样东西,我们就可以开始编写代码了!使用 WebMIDI 与使用其他浏览器 API(如 Geolocation 或 MediaDevices API)并没有太大的区别,如果您熟悉这两个 API 的话。

高级流程如下

  • 我们检测浏览器中 WebMIDI API 的可用性。
  • 如果检测到,我们请求用户的许可以访问它。
  • 一旦我们获得许可,我们现在就可以访问其他方法来检测和与任何连接的 MIDI 设备进行通信。

让我们看看它是如何工作的

if ("requestMIDIAccess" in navigator) {
  // The Web MIDI API is available to us!
}

现在,假设我们使用的是支持 WebMIDI 的浏览器,让我们请求访问

navigator.requestMIDIAccess()
.then((access) => {
  // The user gave us permission. Now we can
  // access the MIDI-capable devices connected
  // to the user's machine.
})
.catch((error) => {
  // Permission was not granted. :(
});

如果用户允许我们,我们现在应该可以访问 MIDIAccess 接口。这有助于我们建立一个我们可以从其接收 MIDI 输入和发送 MIDI 输出的设备列表。

让我们接下来做这个。这是我们要传递到先前代码片段中 then 的函数内部的代码

const inputs = access.inputs;
const outputs = access.outputs;

// Iterate through each connected MIDI input device
inputs.forEach((midiInput) => {
  // Do something with the MIDI input device
});

// Iterate through each connected MIDI output device
outputs.forEach((midioutput) => {
  // Do something with the MIDI output device 
});

您可能想知道 MIDI 输入和输出设备之间有什么区别。某些设备被设置为仅将 MIDI 信息发送到计算机(这些将被列为输入),而另一些则可以接收来自计算机的信息(这些将显示为输出)。设备既可以发送也可以接收信息的情况并不少见,因此您会发现它同时列在两者之下。

现在我们有了可以遍历所有连接的 MIDI 设备的代码,基本上我们只需要做两件事;

  • 如果它是输入设备,我们将要监听从其发出的任何传入 MIDI 消息。
  • 如果它是输出设备,我们可能想要向它发送 MIDI 消息。

设置事件监听器以响应我们输入设备的任何传入 MIDI 消息的代码与您可能为其他 DOM 事件设置的事件监听器非常相似,除了在这种情况下,我们要监听的事件是 midimessage 事件

midiInput.addEventListener('midimessage', (event) => {
  // the `event` object will have a `data` property
  // that contains an array of 3 numbers. For examples:
  // [144, 63, 127]
})

如果我们要发送 MIDI 消息到输出设备,我们可以通过以下代码完成;

outputsend([144, 63, 127]);

这是一个将大部分代码组合在一起的 CodePen 演示。它将告诉您系统上连接的所有 MIDI 输入和输出设备,并在发生时显示传入的 MIDI 消息

查看 Pen
WebMIDI 基本测试
by George Mandis (@georgemandis)
CodePen 上。

WebMIDI 测试演示截图,突出显示了发现的 MIDI 输入和输出设备
WebMIDI 测试演示截图,突出显示了由一个 MIDI 输入设备接收到的 MIDI 消息。在本例中,我们看到的是在按下 MIDI 键盘上的一个键时的情况。
WebMIDI 测试演示截图,突出显示了由一个 MIDI 输入设备接收到的 MIDI 消息。在本例中,我们看到的是在松开 MIDI 键盘上的一个键时的情况。

在这一点上,您可能想知道一些事情

  • 当您监听 midimessage 事件时,我如何理解 event.data 中那个三个数字的数组?
  • 为什么您要向 MIDI 输出设备发送一个包含三个数字的数组,以及为什么您要发送那些特定数字?

这两个问题的答案都取决于更深入地探索和理解 MIDI 协议的工作原理以及它被设计用来解决的问题。

MIDI 消息的结构

当 MIDI 控制器与另一个支持 MIDI 的设备或计算机“通信”时,它们会互相发送和接收 MIDI 消息。这种通信背后的协议在实践中相当简单,但在解释时会显得冗长。不过,我会尽力解释。

每个 MIDI 消息由**三个字节**组成,每个字节包含 8 位(0-255)。用二进制表示,一条消息可能看起来像这样

10010000 | 00111100 | 01111111

MIDI 消息只有两种类型:状态和数据。每条消息都包含一个状态字节和两个数据字节。

状态字节用于传达要传递的消息类型,包括以下内容:

  • 音符开启
  • 音符关闭
  • 音高弯曲变化
  • 控制/模式变化
  • 程序变化

…以及其他许多

如果你来自非音乐背景,这些状态消息可能看起来有点奇怪,但不用担心。数据字节用于为状态提供更多信息和上下文。举个例子,如果我将 MIDI 钢琴连接到我的机器上,并按下键弹奏一个音符,它将发送一个“音符开启”状态字节,并附带指示我弹奏了哪个音符以及我按下音符的力度的数据字节。

状态字节始终以数字1开头,数据字节以数字0开头。

1x0010000 | 0x0111100 | 0x1111111
    ^status     ^data1      ^data2

对于数据字节,这留下了 7 位来表达该字节中的数据。这给了我们0-127的整数范围。

对于状态字节,第一个数字之后的 3 位描述了状态消息的类型,而剩下的 4 位描述了通道。分解我们的二进制表示

1x001x0000

如何在 WebMIDI 和 JavaScript 中使用这些信息

你可能已经从前面的代码示例中猜到了,使用 WebMIDI API,我们很少需要直接处理这些二进制表示。当我们在 JavaScript 中发送和接收这些消息时,我们只需使用如下数组:

[144, 63, 127]

如果你正在使用现有的音乐硬件,了解这些消息的结构方式以及原因将非常有用。了解在第一个字节中接收144意味着在第一个通道中开启一个音符,而128意味着关闭一个音符,这将非常有用。

但是,如果我们正在构建非音乐体验并创建自己的硬件,这些数字可以被重新用于表示你想要的任何东西!

我可以用什么硬件?

任何能够连接到计算机的,支持 MIDI 的设备也应该可以通过 WebMIDI API 访问。能够将 MIDI 数据发送到另一个支持 MIDI 的设备的设备通常被称为 MIDI 控制器。一个常见的例子是简单、类似钢琴的键盘,比如Korg nanoKey2

但是它们的外观和交互方式差异很大。按钮很常见,但你也可以找到一些包含旋钮或压力敏感垫的控制器,比如AKAI LPD8

其他控制器使用更抽象、更有趣的交互方式,包括将运动或呼吸映射到 MIDI 信号。例如,这个控制器(Source Audio 的 Hothand)使用三个加速度计将手势映射到 MIDI 消息

一些控制器可以发送和接收 MIDI 消息,让你可以与物理世界进行真正的双向对话。Novation Launchpad就是一个典型的例子——可以按下按钮发送消息,也可以接收消息来动态更改设备上的颜色

我可以自己制作硬件吗?

事实证明,它们并不难制作,你可以找到很多自制的 MIDI 控制器。它们可以很快变得更加复杂。有些甚至变得相当奇葩

香蕉通过电线连接到 Adafruit Circuit Playground,被编程为 MIDI 乐器

自己制作 MIDI 控制器会让你稍微离开 JavaScript 的世界,但如果你熟悉 Arduino 平台或对它感兴趣,它仍然非常容易上手。Adafruit 的 Circuit Playground Classic是一个很好的入门设备,你可以找到入门代码来刷入设备,并将其变成GitHub 上的多功能 MIDI 控制器

总结

WebMIDI API 是一种门槛较低的方式,让前端开发人员可以开始尝试基本的硬件和软件交互。与其他一些硬件 Web API(如蓝牙)相比,它的实现相对简单,并且 MIDI 标准有很好的文档记录。市面上有很多现有的支持 MIDI 的设备可以用来进行实验或构建有趣的东西,如果你真的想全力以赴,开始为自己的项目构建定制的 MIDI 硬件,你也可以做到。

去创造吧!