JS `Web MIDI API` (浏览器):与 MIDI 音乐设备通信

各位音乐爱好者、代码狂人们,大家好!我是你们今天的Web MIDI API特别讲师,准备好一起摇滚你的浏览器了吗?今天咱们要聊的是如何让你的浏览器跟MIDI设备谈恋爱,让你的代码也能奏响美妙的音符!

第一节:Web MIDI API是什么鬼?

想象一下,你有一把酷炫的MIDI键盘,它能发出各种音符,但是只能连接到电脑上的音乐软件才能用。现在,Web MIDI API就像一个翻译官,让你的网页可以直接理解并控制这把键盘,或者把网页上的音乐信息发送给键盘。

简单来说,Web MIDI API就是一套JavaScript接口,允许你的网页应用程序直接与连接到电脑的MIDI设备进行通信。这意味着你可以用网页来制作音乐、控制合成器、甚至开发自己的音乐游戏!

第二节:准备工作,Let’s Get Ready to Rumble!

在使用Web MIDI API之前,我们需要确保以下几件事:

  1. 浏览器支持: 并非所有浏览器都支持Web MIDI API。Chrome、Edge和Opera的表现通常比较好。Safari也支持,但可能需要手动开启(启用“显示开发”菜单,然后在“开发”菜单中启用“Web MIDI API”)。Firefox的支持比较弱,可能需要一些额外的配置。
  2. MIDI设备: 你需要一个MIDI设备,比如MIDI键盘、合成器或者鼓机。
  3. 设备连接: 将MIDI设备通过USB或其他方式连接到电脑。
  4. 权限: 浏览器通常会询问用户是否允许网页访问MIDI设备。

第三节:代码时间,Show Me the Code!

现在,让我们开始写一些代码吧!

3.1 检查MIDI支持

首先,我们需要检查浏览器是否支持Web MIDI API:

if (navigator.requestMIDIAccess) {
  console.log('Web MIDI API is supported!');
} else {
  console.log('Web MIDI API is not supported in this browser.');
}

这段代码很简单,就是看看navigator对象里有没有requestMIDIAccess这个方法。如果有,就说明浏览器支持Web MIDI API。

3.2 请求MIDI访问权限

接下来,我们需要请求MIDI访问权限:

navigator.requestMIDIAccess()
  .then(onMIDISuccess, onMIDIFailure);

function onMIDISuccess(midiAccess) {
  console.log('MIDI Access Granted!');
  // 在这里处理MIDI设备
}

function onMIDIFailure(msg) {
  console.log(`Failed to get MIDI access - ${msg}`);
}

requestMIDIAccess()方法会弹出一个权限请求窗口,询问用户是否允许网页访问MIDI设备。如果用户同意,onMIDISuccess函数会被调用;如果用户拒绝,onMIDIFailure函数会被调用。

3.3 列出MIDI设备

onMIDISuccess函数中,我们可以列出所有可用的MIDI输入和输出设备:

function onMIDISuccess(midiAccess) {
  console.log('MIDI Access Granted!');

  const inputs = midiAccess.inputs;
  const outputs = midiAccess.outputs;

  console.log("Inputs:");
  inputs.forEach(input => {
    console.log("ID: " + input.id + " Name: " + input.name + " Manufacturer: " + input.manufacturer);
    input.onmidimessage = getMIDIMessage; // 添加监听器
  });

  console.log("Outputs:");
  outputs.forEach(output => {
    console.log("ID: " + output.id + " Name: " + output.name + " Manufacturer: " + output.manufacturer);
  });

}

这段代码会遍历所有的MIDI输入和输出设备,并打印出它们的ID、名称和制造商。注意,我们还给每个输入设备添加了一个onmidimessage事件监听器,用于接收MIDI消息。

3.4 处理MIDI消息

getMIDIMessage函数用于处理接收到的MIDI消息:

function getMIDIMessage(message) {
  const command = message.data[0];
  const note = message.data[1];
  const velocity = (message.data.length > 2) ? message.data[2] : 0; // velocity might be optional

  switch (command) {
    case 144: // Note on
      noteOn(note, velocity);
      break;
    case 128: // Note off
      noteOff(note, velocity);
      break;
    case 176: // Control Change
      controlChange(note, velocity);
      break;
    default:
      console.log("Unknown MIDI message:", message.data);
  }
}

function noteOn(note, velocity) {
  console.log(`Note on: ${note}, Velocity: ${velocity}`);
  // 在这里处理音符按下事件
  // 例如,播放一个声音
}

function noteOff(note, velocity) {
  console.log(`Note off: ${note}, Velocity: ${velocity}`);
  // 在这里处理音符释放事件
  // 例如,停止播放声音
}

function controlChange(controller, value) {
  console.log(`Control Change: Controller ${controller}, Value ${value}`);
  // 在这里处理控制变化事件
  // 例如,调整音量、调制等
}

MIDI消息通常包含三个字节:

  • 命令字节: 指示消息类型,例如音符按下、音符释放、控制变化等。
  • 音符字节: 指示音符的音高。
  • 力度字节: 指示音符的力度(音量)。

getMIDIMessage函数会根据命令字节来判断消息类型,并调用相应的处理函数。

3.5 发送MIDI消息

除了接收MIDI消息,我们还可以发送MIDI消息到MIDI设备,例如控制合成器或者发送音符:

function sendMIDIMessage(output, command, note, velocity) {
  const message = [command, note, velocity];
  output.send(message);
}

// 示例:发送一个音符按下消息
const outputs = midiAccess.outputs;
outputs.forEach(output => {
    if (output.name.includes("你的MIDI设备名称")) { // 替换成你的设备名称
        sendMIDIMessage(output, 144, 60, 127); // 发送C4音符,力度为127
    }
});

sendMIDIMessage函数接受一个输出设备对象、命令字节、音符字节和力度字节作为参数,并将它们组合成一个MIDI消息,然后通过output.send()方法发送到设备。

第四节:进阶技巧,Level Up!

4.1 MIDI Channel

MIDI设备通常有16个通道,每个通道可以独立控制一个乐器。MIDI消息的命令字节的高4位指示消息类型,低4位指示通道号。例如,命令字节144表示音符按下,通道号为1;命令字节145表示音符按下,通道号为2。

4.2 MIDI Control Change

MIDI Control Change消息用于控制合成器的各种参数,例如音量、调制、混响等。每个参数都有一个唯一的控制器编号。

控制器编号 功能
7 主音量
10 声像
91 混响深度
64 延音踏板
1 调制轮

你可以通过发送Control Change消息来调整这些参数。例如,要将通道1的音量设置为100,可以发送以下消息:

sendMIDIMessage(output, 176, 7, 100); // 176 = 160 + 16,其中160是Control Change命令,16是通道号

4.3 SysEx消息

SysEx(System Exclusive)消息是一种特殊的MIDI消息,用于发送设备特定的数据。你可以使用SysEx消息来控制合成器的更高级功能,例如加载音色、保存设置等。SysEx消息的格式比较复杂,需要参考设备的文档。

第五节:实战演练,Let’s Make Some Music!

现在,让我们用Web MIDI API来做一个简单的音乐应用:一个虚拟键盘。

  1. HTML结构:
<!DOCTYPE html>
<html>
<head>
  <title>Web MIDI Keyboard</title>
  <style>
    .key {
      width: 50px;
      height: 200px;
      border: 1px solid black;
      display: inline-block;
      text-align: center;
      vertical-align: bottom;
    }
    .key.black {
      width: 30px;
      height: 120px;
      background-color: black;
      color: white;
      position: relative;
      left: -15px;
      z-index: 1;
    }
  </style>
</head>
<body>
  <div id="keyboard"></div>
  <script src="script.js"></script>
</body>
</html>
  1. JavaScript代码:
const keyboard = document.getElementById('keyboard');
const notes = ['C4', 'C#4', 'D4', 'D#4', 'E4', 'F4', 'F#4', 'G4', 'G#4', 'A4', 'A#4', 'B4'];
let midiAccess;
let output;

function createKeyboard() {
  notes.forEach(note => {
    const key = document.createElement('div');
    key.classList.add('key');
    key.textContent = note;

    if (note.includes('#')) {
      key.classList.add('black');
    }

    key.addEventListener('mousedown', () => {
      noteOn(note);
    });

    key.addEventListener('mouseup', () => {
      noteOff(note);
    });

    keyboard.appendChild(key);
  });
}

function noteOn(note) {
  console.log(`Note on: ${note}`);
  const noteNumber = getNoteNumber(note);
  if (output) {
    sendMIDIMessage(output, 144, noteNumber, 127);
  }
}

function noteOff(note) {
  console.log(`Note off: ${note}`);
  const noteNumber = getNoteNumber(note);
  if (output) {
    sendMIDIMessage(output, 128, noteNumber, 0);
  }
}

function getNoteNumber(note) {
  const noteMap = {
    'C4': 60, 'C#4': 61, 'D4': 62, 'D#4': 63, 'E4': 64, 'F4': 65,
    'F#4': 66, 'G4': 67, 'G#4': 68, 'A4': 69, 'A#4': 70, 'B4': 71
  };
  return noteMap[note];
}

function sendMIDIMessage(output, command, note, velocity) {
  const message = [command, note, velocity];
  output.send(message);
}

if (navigator.requestMIDIAccess) {
  navigator.requestMIDIAccess()
    .then(onMIDISuccess, onMIDIFailure);
} else {
  console.log('Web MIDI API is not supported in this browser.');
}

function onMIDISuccess(midi) {
  midiAccess = midi;
  const outputs = midiAccess.outputs;
  outputs.forEach(o => {
    output = o; // 使用第一个输出设备
    console.log("Using MIDI output:", output.name);
  });

  if (!output) {
    console.warn("No MIDI output device found.");
  }

  createKeyboard(); // 在成功获取MIDI访问权限后创建键盘
}

function onMIDIFailure(msg) {
  console.log(`Failed to get MIDI access - ${msg}`);
}

这个例子创建了一个简单的虚拟键盘,当你点击键盘上的音符时,它会向MIDI设备发送相应的音符按下和音符释放消息。

第六节:常见问题,Troubleshooting Time!

  • 浏览器无法识别MIDI设备: 确保MIDI设备已正确连接到电脑,并且浏览器已获得MIDI访问权限。尝试重启浏览器或电脑。
  • MIDI消息没有发送到设备: 检查MIDI设备的名称是否正确,以及设备是否已开启并设置为接收MIDI消息。
  • 声音延迟: MIDI本身是很快的,延迟通常来自软件合成器。尝试使用更快的合成器或调整合成器的设置。

第七节:总结,The End!

Web MIDI API是一个强大的工具,可以让你在浏览器中与MIDI设备进行交互。通过学习本文,你应该已经掌握了Web MIDI API的基本用法,可以开始制作自己的音乐应用了!希望你们玩得开心,创作出更多美妙的音乐!记住,代码和音乐一样,都是创造力的表达!

现在,去吧,让你的代码奏响音乐!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注