From 5947b00b640c4c4a6c67ee09cc5cbe02ccaa1ea6 Mon Sep 17 00:00:00 2001 From: bigtongue5566 Date: Fri, 7 Apr 2023 13:02:20 +0800 Subject: [PATCH 1/2] feat: support Bluetooth LE MIDI --- globals.js | 83 ++++++++++++++++++++++++++++++++++++++++++------------ index.html | 32 +++++++++++---------- style.css | 7 +++++ 3 files changed, 89 insertions(+), 33 deletions(-) diff --git a/globals.js b/globals.js index f9bf036..cec610f 100644 --- a/globals.js +++ b/globals.js @@ -59,8 +59,10 @@ WebMidi.enable(function (err) { //check if WebMidi.js is enabled midiSelectSlider = select("#slider"); midiSelectSlider.attribute("max", WebMidi.inputs.length - 1); midiSelectSlider.input(inputChanged); - midiIn = WebMidi.inputs[midiSelectSlider.value()] - inputChanged(); + if (midiSelectSlider.value()) { + midiIn = WebMidi.inputs[midiSelectSlider.value()] + inputChanged(); + } }); function inputChanged() { @@ -78,10 +80,10 @@ function inputChanged() { console.log("Received 'noteoff' message (" + e.note.number + ", " + e.velocity + ")."); noteOff(e.note.number, e.velocity); }) - midiIn.addListener('controlchange', 'all', function(e) { + midiIn.addListener('controlchange', 'all', function (e) { console.log("Received control change message:", e.controller.number, e.value); controllerChange(e.controller.number, e.value) - }); + }); console.log(midiIn.name); select("#device").html(midiIn.name); }; @@ -90,11 +92,11 @@ function noteOn(pitch, velocity) { totalNotesPlayed++; notesThisFrame++; totalIntensityScore += velocity; - + // piano visualizer isKeyOn[pitch] = 1; if (nowPedaling) { - isPedaled[pitch] = 1; + isPedaled[pitch] = 1; } } @@ -105,20 +107,20 @@ function noteOff(pitch, velocity) { function controllerChange(number, value) { // Receive a controllerChange if (number == 64) { - cc64now = value; + cc64now = value; if (value >= 64) { - nowPedaling = true; - for (let i = 0; i < 128; i++) { - // copy key on to pedal - isPedaled[i] = isKeyOn[i]; - } + nowPedaling = true; + for (let i = 0; i < 128; i++) { + // copy key on to pedal + isPedaled[i] = isKeyOn[i]; + } } else if (value < 64) { - nowPedaling = false; - for (let i = 0; i < 128; i++) { - // reset isPedaled - isPedaled[i] = 0; - } + nowPedaling = false; + for (let i = 0; i < 128; i++) { + // reset isPedaled + isPedaled[i] = 0; + } } } @@ -141,4 +143,49 @@ function changeColor() { darkenedColor = keyOnColor.levels.map(x => floor(x * .7)); pedaledColor = color(`rgb(${darkenedColor[0]}, ${darkenedColor[1]}, ${darkenedColor[2]})`) console.log(pedaledColor.levels); -} \ No newline at end of file +} + +async function requestBleMidi() { + const MIDI_SERVICE = '03b80e5a-ede8-4b33-a751-6ce34ec4c700'; + const MIDI_CHARACTERISTIC = '7772e5db-3868-4112-a1a9-f2669d106bf3' + const bluetoothDevice = await navigator.bluetooth.requestDevice({ + filters: [{ + services: [MIDI_SERVICE] + }] + }); + console.log(`Name: ${bluetoothDevice.name}`); + document.querySelector('#device').textContent = bluetoothDevice.name; + bluetoothDevice.addEventListener('gattserverdisconnected', onDisconnected); + const server = await bluetoothDevice.gatt.connect(); + const service = await server.getPrimaryService(MIDI_SERVICE); + const characteristic = await service.getCharacteristic(MIDI_CHARACTERISTIC); + await characteristic.startNotifications(); + characteristic.addEventListener('characteristicvaluechanged', handleMidiMessageReceived); +} + +function onDisconnected(event) { + const device = event.target; + console.log('Device ' + device.name + ' is disconnected.'); +} + +function handleMidiMessageReceived(event) { + const { buffer } = event.target.value; + const eventData = new Uint8Array(buffer); + for (let i = 0; i < (eventData.length - 1) / 4; i++) { + const index = 4 * i; + const status = eventData[index + 2]; + const value1 = eventData[index + 3]; + const value2 = eventData[index + 4]; + switch (status) { + case 144: + noteOn(value1, value2); + break; + case 128: + noteOff(value1, value2); + case 176: + controllerChange(value1, value2); + default: + break; + } + } +} diff --git a/index.html b/index.html index 06517f1..19098cf 100644 --- a/index.html +++ b/index.html @@ -14,14 +14,15 @@
-
+

鋼琴鍵盤顯示器 by NiceChord

選擇 MIDI 裝置
+ -
Select Input:
+
Select Input:
@@ -39,27 +40,28 @@
選擇 MIDI 裝置

- -
+
+
-
- -
- -
- +
+ +
+ +
+ TIME:使用時間 | NOTE COUNT:總彈奏音符數 | NPS:最近一秒鐘彈奏音符數(括號為歷史最大值) | LEGATO:圓滑指數(最近一秒鐘平均來說有幾個鍵被同時按住)
CALORIES:消耗熱量(估計值,好玩就好)| PEDALS:左右踏板深度顯示
(密技:點鍵盤最左上角的角落,可以儲存截圖)

- 覺得好用嗎?到 NiceChord.com 逛逛支持我!原始碼在 GitHub。 -
- - -
+ 覺得好用嗎?到 NiceChord.com 逛逛支持我!原始碼在 GitHub。 +
+ + +
\ No newline at end of file diff --git a/style.css b/style.css index d6a410b..e361cb6 100644 --- a/style.css +++ b/style.css @@ -586,4 +586,11 @@ label[for=rainbow-mode-checkbox]:active:after { input:checked + label[for=rainbow-mode-checkbox]:after { left: calc(100% - 1.25px); transform: translateX(-100%); +} + +.bluetooth-icon { + display: inline-block; + background-image: url("data:image/svg+xml,%3Csvg fill='%230082FC' version='1.1' id='Capa_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 217.499 217.499' xml:space='preserve'%3E%3Cg id='SVGRepo_bgCarrier' stroke-width='0'%3E%3C/g%3E%3Cg id='SVGRepo_tracerCarrier' stroke-linecap='round' stroke-linejoin='round'%3E%3C/g%3E%3Cg id='SVGRepo_iconCarrier'%3E%3Cg%3E%3Cpath d='M123.264 108.749l45.597-44.488c1.736-1.693 2.715-4.016 2.715-6.441s-0.979-4.748-2.715-6.441l-50.038-48.82 c-2.591-2.528-6.444-3.255-9.78-1.853c-3.336 1.406-5.505 4.674-5.505 8.294v80.504l-42.331-41.3 c-3.558-3.471-9.255-3.402-12.727 0.156c-3.471 3.558-3.401 9.256 0.157 12.727l48.851 47.663l-48.851 47.663 c-3.558 3.471-3.628 9.169-0.157 12.727s9.17 3.628 12.727 0.156l42.331-41.3v80.504c0 3.62 2.169 6.888 5.505 8.294 c1.128 0.476 2.315 0.706 3.493 0.706c2.305 0 4.572-0.886 6.287-2.559l50.038-48.82c1.736-1.693 2.715-4.016 2.715-6.441 s-0.979-4.748-2.715-6.441L123.264 108.749z M121.539 30.354l28.15 27.465l-28.15 27.465V30.354z M121.539 187.143v-54.93 l28.15 27.465L121.539 187.143z'%3E%3C/path%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); + width: 15px; + height: 15px; } \ No newline at end of file From a6de74306a9d3bff708bb3a605a8a2f211432f72 Mon Sep 17 00:00:00 2001 From: bigtongue5566 Date: Fri, 7 Apr 2023 17:23:28 +0800 Subject: [PATCH 2/2] fix: add missing break in switch case --- globals.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/globals.js b/globals.js index cec610f..7509adf 100644 --- a/globals.js +++ b/globals.js @@ -182,8 +182,10 @@ function handleMidiMessageReceived(event) { break; case 128: noteOff(value1, value2); + break; case 176: controllerChange(value1, value2); + break; default: break; }