├── .gitignore ├── .travis.yml ├── .vscode ├── settings.json └── tasks.json ├── BluetoothMIDIService.cpp ├── BluetoothMIDIService.h ├── LICENSE ├── Makefile ├── README.md ├── SECURITY.md ├── bluetooth.ts ├── enums.d.ts ├── icon.png ├── midi.cpp ├── pxt.json ├── shims.d.ts ├── tests.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | built 2 | node_modules 3 | yotta_modules 4 | yotta_targets 5 | pxt_modules 6 | *.db 7 | *.tgz 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8.9.4" 4 | script: 5 | - "npm install -g pxt" 6 | - "pxt target microbit" 7 | - "pxt install" 8 | - "pxt build --cloud" 9 | sudo: false 10 | cache: 11 | directories: 12 | - npm_modules 13 | - pxt_modules 14 | 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnType": true, 3 | "files.autoSave": "afterDelay", 4 | "files.watcherExclude": { 5 | "**/.git/objects/**": true, 6 | "**/built/**": true, 7 | "**/node_modules/**": true, 8 | "**/yotta_modules/**": true, 9 | "**/yotta_targets": true, 10 | "**/pxt_modules/**": true 11 | }, 12 | "search.exclude": { 13 | "**/built": true, 14 | "**/node_modules": true, 15 | "**/yotta_modules": true, 16 | "**/yotta_targets": true, 17 | "**/pxt_modules": true 18 | } 19 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | 2 | // A task runner that calls the PXT compiler and 3 | { 4 | "version": "0.1.0", 5 | 6 | // The command is pxt. Assumes that PXT has been installed using npm install -g pxt 7 | "command": "pxt", 8 | 9 | // The command is a shell script 10 | "isShellCommand": true, 11 | 12 | // Show the output window always. 13 | "showOutput": "always", 14 | 15 | "tasks": [{ 16 | "taskName": "deploy", 17 | "isBuildCommand": true, 18 | "problemMatcher": "$tsc", 19 | "args": [""] 20 | }, { 21 | "taskName": "build", 22 | "isTestCommand": true, 23 | "problemMatcher": "$tsc", 24 | "args": [""] 25 | }, { 26 | "taskName": "clean", 27 | "isTestCommand": true, 28 | "problemMatcher": "$tsc", 29 | "args": [""] 30 | }, { 31 | "taskName": "serial", 32 | "isTestCommand": true, 33 | "problemMatcher": "$tsc", 34 | "args": [""] 35 | }] 36 | } 37 | -------------------------------------------------------------------------------- /BluetoothMIDIService.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014 mbed.org, MIT License 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 4 | * and associated documentation files (the "Software"), to deal in the Software without 5 | * restriction, including without limitation the rights to use, copy, modify, merge, publish, 6 | * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 7 | * Software is furnished to do so, subject to the following conditions: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or 10 | * substantial portions of the Software. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 13 | * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 15 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | #include "MicroBit.h" 20 | #include "pxt.h" 21 | #include "BluetoothMIDIService.h" 22 | 23 | 24 | // MIDI characteristic 25 | const uint8_t midiCharacteristicUuid[] = { 26 | 0x77, 0x72, 0xe5, 0xdb, 0x38, 0x68, 0x41, 0x12, 27 | 0xa1, 0xa9, 0xf2, 0x66, 0x9d, 0x10, 0x6b, 0xf3 28 | }; 29 | 30 | // MIDI service 31 | const uint8_t midiServiceUuid[] = { 32 | 0x03, 0xb8, 0x0e, 0x5a, 0xed, 0xe8, 0x4b, 0x33, 33 | 0xa7, 0x51, 0x6c, 0xe3, 0x4e, 0xc4, 0xc7, 0x00 34 | }; 35 | 36 | BluetoothMIDIService::BluetoothMIDIService(BLEDevice *dev): ble(*dev) { 37 | timestamp = 0; 38 | memset(midiBuffer, 0, sizeof(midiBuffer)); 39 | firstRead = true; 40 | 41 | GattCharacteristic midiCharacteristic(midiCharacteristicUuid, midiBuffer, 0, sizeof(midiBuffer), 42 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ 43 | | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY 44 | ); 45 | GattCharacteristic *midiChars[] = {&midiCharacteristic}; 46 | 47 | midiCharacteristic.requireSecurity(SecurityManager::MICROBIT_BLE_SECURITY_LEVEL); 48 | 49 | GattService midiService(midiServiceUuid, midiChars, sizeof(midiChars) / sizeof(GattCharacteristic *)); 50 | 51 | ble.addService(midiService); 52 | 53 | midiCharacteristicHandle = midiCharacteristic.getValueHandle(); 54 | 55 | ble.gattServer().onDataRead(this, &BluetoothMIDIService::onDataRead); 56 | ble.onDisconnection(this, &BluetoothMIDIService::onDisconnection); 57 | 58 | tick.start(); 59 | } 60 | 61 | void BluetoothMIDIService::onDataRead(const GattReadCallbackParams* params) 62 | { 63 | if (params->handle == midiCharacteristicHandle) 64 | { 65 | if (firstRead) { 66 | // send empty payload upon first connect 67 | ble.gattServer().notify(midiCharacteristicHandle, (uint8_t *)midiBuffer, 0); 68 | firstRead = false; 69 | } 70 | } 71 | } 72 | 73 | void BluetoothMIDIService::onDisconnection(const Gap::DisconnectionCallbackParams_t* params) { 74 | // clear read bit 75 | firstRead = true; 76 | } 77 | 78 | bool BluetoothMIDIService::connected() { 79 | return ble.getGapState().connected; 80 | } 81 | 82 | void BluetoothMIDIService::sendMidiMessage(uint8_t data0) { 83 | if (connected()) { 84 | unsigned int ticks = tick.read_ms() & 0x1fff; 85 | midiBuffer[0] = 0x80 | ((ticks >> 7) & 0x3f); 86 | midiBuffer[1] = 0x80 | (ticks & 0x7f); 87 | midiBuffer[2] = data0; 88 | 89 | ble.gattServer().notify(midiCharacteristicHandle, (uint8_t *)midiBuffer, 3); 90 | } 91 | } 92 | 93 | void BluetoothMIDIService::sendMidiMessage(uint8_t data0, uint8_t data1) { 94 | if (connected()) { 95 | unsigned int ticks = tick.read_ms() & 0x1fff; 96 | midiBuffer[0] = 0x80 | ((ticks >> 7) & 0x3f); 97 | midiBuffer[1] = 0x80 | (ticks & 0x7f); 98 | midiBuffer[2] = data0; 99 | midiBuffer[3] = data1; 100 | 101 | ble.gattServer().notify(midiCharacteristicHandle, (uint8_t *)midiBuffer, 4); 102 | } 103 | } 104 | 105 | void BluetoothMIDIService::sendMidiMessage(uint8_t data0, uint8_t data1, uint8_t data2) { 106 | if (connected()) { 107 | unsigned int ticks = tick.read_ms() & 0x1fff; 108 | midiBuffer[0] = 0x80 | ((ticks >> 7) & 0x3f); 109 | midiBuffer[1] = 0x80 | (ticks & 0x7f); 110 | midiBuffer[2] = data0; 111 | midiBuffer[3] = data1; 112 | midiBuffer[4] = data2; 113 | 114 | ble.gattServer().notify(midiCharacteristicHandle, (uint8_t *)midiBuffer, 5); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /BluetoothMIDIService.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2014 mbed.org, MIT License 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 4 | * and associated documentation files (the "Software"), to deal in the Software without 5 | * restriction, including without limitation the rights to use, copy, modify, merge, publish, 6 | * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 7 | * Software is furnished to do so, subject to the following conditions: 8 | * 9 | * The above copyright notice and this permission notice shall be included in all copies or 10 | * substantial portions of the Software. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 13 | * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 15 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | */ 18 | 19 | #ifndef __BLEMIDI_H__ 20 | #define __BLEMIDI_H__ 21 | 22 | #include "ble/BLE.h" 23 | 24 | /** 25 | * A class to communicate a BLE MIDI device 26 | */ 27 | class BluetoothMIDIService { 28 | public: 29 | /** 30 | * Constructor 31 | */ 32 | BluetoothMIDIService(BLEDevice *device); 33 | 34 | /** 35 | * Check if a BLE MIDI device is connected 36 | * 37 | * @returns true if a midi device is connected 38 | */ 39 | bool connected(); 40 | 41 | void sendMidiMessage(uint8_t data0); 42 | void sendMidiMessage(uint8_t data0, uint8_t data1); 43 | void sendMidiMessage(uint8_t data0, uint8_t data1, uint8_t data2); 44 | 45 | private: 46 | void onDataRead(const GattReadCallbackParams* params); 47 | void onDisconnection(const Gap::DisconnectionCallbackParams_t* params); 48 | 49 | uint16_t timestamp; 50 | uint8_t midiBuffer[5]; 51 | bool firstRead; 52 | 53 | BLEDevice &ble; 54 | GattAttribute::Handle_t midiCharacteristicHandle; 55 | Timer tick; 56 | }; 57 | 58 | #endif /* __BLEMIDI_H__ */ 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: deploy 2 | 3 | build: 4 | pxt build 5 | 6 | deploy: 7 | pxt deploy 8 | 9 | test: 10 | pxt test 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bluetooth-midi [![Build Status](https://travis-ci.org/Microsoft/pxt-bluetooth-midi.svg?branch=master)](https://travis-ci.org/Microsoft/pxt-bluetooth-midi) 2 | 3 | A [Bluetooth low energy MIDI](https://www.midi.org/specifications/item/bluetooth-le-midi) 4 | support for the @boardname@. 5 | **iOS only currently**. 6 | 7 | ### ~hint 8 | ![](/static/bluetooth/Bluetooth_SIG.png) 9 | 10 | For another device like a smartphone to use any of the Bluetooth "services" which the @boardname@ has, it must first be [paired with the @boardname@](/reference/bluetooth/bluetooth-pairing). Once paired, the other device may connect to the @boardname@ and exchange data relating to many of the @boardname@'s features. 11 | 12 | ### ~ 13 | 14 | ## Usage 15 | 16 | This package allows the @boardname@ to act as a MIDI peripherical, like a piano. It requires to connect to a BLE MIDI device to receive the commands and play them. 17 | 18 | This library uses the [MIDI package](/pkg/microsoft/pxt-midi). 19 | Please refer to that project documentation for further details on using MIDI commands. 20 | 21 | ## Supported Platforms 22 | 23 | This package is currently only supported on iOS (iPad/iPhone). 24 | Any app that supports a MIDI keyboard or instrument should work. 25 | 26 | ### Apple GarageBand 27 | 28 | * [iPhone/iPad App](https://itunes.apple.com/us/app/garageband/id408709785?mt=8) 29 | 30 | Go to settings (gearwheel), click **Advanced**, click **Bluetooth MIDI device** and connect to the @boardname@. 31 | If the @boardname@ is marked as offline, click **Edit** and **Forget** the device. 32 | 33 | ## Supported targets 34 | 35 | * for PXT/microbit 36 | * for PXT/calliope 37 | 38 | (The metadata above is needed for package search.) 39 | 40 | ## License 41 | 42 | MIT 43 | 44 | ## Code of Conduct 45 | 46 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 47 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /bluetooth.ts: -------------------------------------------------------------------------------- 1 | namespace bluetooth { 2 | /** 3 | * Starts the MIDI service over Bluetooth and registers it as the MIDI transport. 4 | */ 5 | //% blockId=bluetooth_start_midi block="bluetooth start midi service" 6 | //% part=bluetooth 7 | //% blockHidden=1 deprecated=true 8 | export function startMidiService() { 9 | function send(buffer: Buffer) { 10 | bluetooth.midiSendMessage(buffer); 11 | } 12 | midi.setTransport(send); 13 | bluetooth.midiSendMessage(pins.createBuffer(0)); // does nothing but starts service lazily 14 | } 15 | 16 | /** 17 | * Sends a MIDI message 18 | */ 19 | //% shim=bluetooth::midiSendMessage 20 | //% advanced=true 21 | export function midiSendMessage(data: Buffer) { 22 | return; 23 | } 24 | 25 | } 26 | // automatically start midi service 27 | bluetooth.startMidiService(); 28 | -------------------------------------------------------------------------------- /enums.d.ts: -------------------------------------------------------------------------------- 1 | // Auto-generated. Do not edit. 2 | declare namespace bluetooth { 3 | } 4 | 5 | // Auto-generated. Do not edit. Really. 6 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/pxt-bluetooth-midi/dedc805fd5d95374f8ce0b7449f8e239ee658f07/icon.png -------------------------------------------------------------------------------- /midi.cpp: -------------------------------------------------------------------------------- 1 | #include "pxt.h" 2 | #include "BluetoothMIDIService.h" 3 | using namespace pxt; 4 | 5 | // v0 backward compat support 6 | #ifndef PXT_BUFFER_DATA 7 | #define PXT_BUFFER_DATA(buffer) buffer->payload 8 | #endif 9 | /** 10 | * A set of functions to send MIDI commands over Bluetooth 11 | */ 12 | namespace bluetooth { 13 | BluetoothMIDIService* pMidi = NULL; 14 | BluetoothMIDIService* getMidi() 15 | { 16 | if (NULL == pMidi) 17 | pMidi = new BluetoothMIDIService(uBit.ble); 18 | return pMidi; 19 | } 20 | 21 | //% 22 | void midiSendMessage(Buffer data) { 23 | BluetoothMIDIService* pMidi = getMidi(); 24 | auto buf = PXT_BUFFER_DATA(data); 25 | 26 | switch(data->length) { 27 | case 1: 28 | pMidi->sendMidiMessage(buf[0]); 29 | break; 30 | case 2: 31 | pMidi->sendMidiMessage(buf[0], buf[1]); 32 | break; 33 | case 3: 34 | pMidi->sendMidiMessage(buf[0], buf[1], buf[2]); 35 | break; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pxt.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bluetooth-midi", 3 | "version": "2.0.13", 4 | "description": "A Bluetooth MIDI service", 5 | "license": "MIT", 6 | "dependencies": { 7 | "core": "*", 8 | "bluetooth": "*", 9 | "midi": "github:microsoft/pxt-midi#v2.1.11" 10 | }, 11 | "files": [ 12 | "README.md", 13 | "bluetooth.ts", 14 | "BluetoothMIDIService.cpp", 15 | "BluetoothMIDIService.h", 16 | "midi.cpp", 17 | "shims.d.ts", 18 | "enums.d.ts" 19 | ], 20 | "testFiles": [ 21 | "tests.ts" 22 | ], 23 | "public": true, 24 | "installedVersion": "workspace:ba1a3850-63c9-4062-aab8-393d43dccbb2" 25 | } -------------------------------------------------------------------------------- /shims.d.ts: -------------------------------------------------------------------------------- 1 | // Auto-generated. Do not edit. 2 | 3 | 4 | /** 5 | * A set of functions to send MIDI commands over Bluetooth 6 | */ 7 | 8 | declare namespace bluetooth { 9 | } 10 | 11 | // Auto-generated. Do not edit. Really. 12 | -------------------------------------------------------------------------------- /tests.ts: -------------------------------------------------------------------------------- 1 | bluetooth.onBluetoothConnected(() => { 2 | basic.showString("C") 3 | }) 4 | bluetooth.onBluetoothDisconnected(() => { 5 | basic.showString("D") 6 | }) 7 | 8 | // this is automatic 9 | // bluetooth.startMidiService(); 10 | 11 | basic.showString("S") 12 | input.onButtonPressed(Button.A, () => { 13 | basic.clearScreen(); 14 | led.toggle(0, 0); 15 | midi.playTone(Note.A, music.beat()) 16 | midi.playTone(Note.B, music.beat()) 17 | midi.playTone(Note.C, music.beat()) 18 | midi.playTone(Note.D, music.beat()) 19 | midi.playTone(Note.E, music.beat()) 20 | }) 21 | input.onButtonPressed(Button.B, () => { 22 | basic.clearScreen(); 23 | for (let note = 0; note < 127; note++) { 24 | led.toggle(1, 1); 25 | midi.playDrum(note); 26 | basic.pause(100); 27 | } 28 | }) 29 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "noImplicitAny": true, 5 | "outDir": "built", 6 | "rootDir": "." 7 | }, 8 | "exclude": ["pxt_modules/**/*test.ts"] 9 | } 10 | --------------------------------------------------------------------------------