-
-
Notifications
You must be signed in to change notification settings - Fork 206
Open
Description
Is your feature request related to a problem? Please describe.
Connecting Bluetooth MIDI devices is not trivial on all operating systems. And often cumbersome to have to go through system settings to do the connection.
Describe the solution you'd like
Integrate Bluetooth MIDI (through Web Bluetooth) support directly into SIgnal. Let the user directly connect to a device through the app.
Describe alternatives you've considered
Going through the OS
Additional context
- I'm the creator of Wavy MONKEY (https://wavyindustries.com/monkey/)
- Web Bluetooth is not supported in all browsers. But Chrome does support it. Would have to be grayed out for the browsers not supporting it (see example below). There exists Browsers supporting Web Bluetooth on all major platforms. For example Bluefy on iPad OS and iOS, which would make it very simple to use Signal with a Bluetooth device on these platforms.
- Here is a basic implementation, Web Bluetooth is actually very simple to use. Run the code in the browser and you will receive all MIDI events from the connected device
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Web Bluetooth MIDI - minimal demo</title>
</head>
<body>
<!-- A single button toggles connection state -->
<button id="connectBtn">Connect</button>
<div style="display: none;" id="noSupport">Browser does not support Web Bluetooth</div>
<!-- Incoming MIDI messages appear here (most‑recent first) -->
<ul id="log"></ul>
<script>
// BLE MIDI UUIDs (lower‑case)
const MIDI_SERVICE_UUID =
"03b80e5a-ede8-4b33-a751-6ce34ec4c700".toLowerCase();
const MIDI_CHARACTERISTIC_UUID =
"7772e5db-3868-4112-a1a9-f2669d106bf3".toLowerCase();
let device; // currently connected BluetoothDevice
let midiCharacteristic; // BLE MIDI IO characteristic
const connectBtn = document.getElementById("connectBtn");
// Check for Web Bluetooth support
if (!navigator.bluetooth) {
document.getElementById('noSupport').style.display = 'block';
connectBtn.disabled = true;
connectBtn.style.opacity = '0.5';
connectBtn.style.cursor = 'not-allowed';
}
// Helper that prepends timestamped lines to the <ul>
function log(text) {
const li = document.createElement("li");
li.textContent = `[${new Date().toLocaleTimeString()}] ${text}`;
document.getElementById("log").prepend(li);
}
async function connect() {
try {
log("Requesting device…");
// we only scan for devices which advertises themselves as MIDI devices
device = await navigator.bluetooth.requestDevice({
filters: [{ services: [MIDI_SERVICE_UUID] }],
});
device.addEventListener("gattserverdisconnected", onDisconnected);
log("Connecting…");
const server = await device.gatt.connect();
// now, we ask the device for its MIDI service, and its MIDI endpoint (characteristics)
const service = await server.getPrimaryService(MIDI_SERVICE_UUID);
midiCharacteristic = await service.getCharacteristic(
MIDI_CHARACTERISTIC_UUID
);
// we subscribe to notifications (events) fron the device. Each time the device has data to send through the MIDI characteristic, onMIDIMessage is called
await midiCharacteristic.startNotifications();
midiCharacteristic.addEventListener(
"characteristicvaluechanged",
onMIDIMessage
);
log("Connected ✔");
connectBtn.textContent = "Disconnect";
} catch (err) {
log("Error: " + err);
}
}
function disconnect() {
if (device && device.gatt.connected) {
device.gatt.disconnect();
}
}
function onDisconnected() {
log("Device disconnected");
connectBtn.textContent = "Connect";
}
// Show each raw BLE MIDI packet (hex‑encoded)
function onMIDIMessage(event) {
const bytes = new Uint8Array(event.target.value.buffer);
const hex = Array.from(bytes)
.map((b) => b.toString(16).padStart(2, "0"))
.join(" ");
log("MIDI → " + hex);
}
// Toggle connect / disconnect on button click
connectBtn.addEventListener("click", () => {
if (device && device.gatt.connected) {
disconnect();
} else {
connect();
}
});
</script>
</body>
</html>
ryohey
Metadata
Metadata
Assignees
Labels
No labels