Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Midi CC receive #1

Open
freekatet opened this issue Apr 26, 2023 · 12 comments
Open

Midi CC receive #1

freekatet opened this issue Apr 26, 2023 · 12 comments

Comments

@freekatet
Copy link

Would it be possible to add a function to receive midi CC also ? (to control led, servos etc )
thanks!

@joebowbeer
Copy link
Owner

I don't think this would be difficult to do.

I think you'd need to extend the ble_midi code (copy/paste?) in order to add your own onWrite function implementation to the setServices call:

http://www.espruino.com/Reference#l_NRF_setServices

Your implementation would need to decode the CC event data, which I expect to be an array of 5 bytes, similar to the CC bytes that are sent.

Some onWrite code here, FWIW: https://www.espruino.com/BLE+Communications

@freekatet
Copy link
Author

thanks!
???
exports.init = function() {
NRF.setServices({
"03B80E5A-EDE8-4B33-A751-6CE34EC4C700": { // MIDI
"7772E5DB-3868-4112-A1A9-F2669D106BF3": {
readable: true,
writable: true,
notify: true,
value: [0x80, 0x80, 0x00, 0x00, 0x00],
onWrite: function(evt) {
if (evt.data.length == 3 && evt.data[0] >= 0xB0 && evt.data[0] <= 0xBF) {
// Received a CC message
ccMessage = evt.data;
receiveCC(ccMessage[0] & 0x0F, ccMessage[1], ccMessage[2]);
}
}
}
}
});

@freekatet
Copy link
Author

I am fairly new to this and still struggle to understand it ::)

@joebowbeer
Copy link
Owner

joebowbeer commented Apr 27, 2023

The relevant specs are:

https://www.midi.org/specifications/midi-transports-specifications/midi-over-bluetooth-low-energy-ble-midi

https://www.midi.org/specifications-old/item/table-1-summary-of-midi-message

The first spec defines the transport: a header and timestamp byte followed by data bytes, possibly with some intervening timestamps.

The second table defines the data bytes.

In the simplest case you'll receive 5 bytes:

  1. 0x80 + 6 high bits of timestamp
  2. 0x80 + 7 low bits of timestamp
  3. 0xB0 + 4 bits MIDI channel
  4. controller number
  5. controller value

@freekatet
Copy link
Author

thank you! I gonna try my best and get back to you if I get anything interesting!

@freekatet
Copy link
Author

I didnt went too far for now but in parallel I am trying to add a function to be able to rename the device, using midi.setName("PUCKIO"); ( where the name is always 6 letters to keep it easy with the first byte (length of the name+1) "0x07"

my code doesn't throw error but the device isnt showing up as a midi device anymore, any idea what could be the problem?
btw I didn't find the github for the ble_midi module or I would post there if possible

thank you!

/// Sets the name of the MIDI device
exports.setName = function(name) {
var nameBytes = [];
for (var i = 0; i < name.length; ++i)
nameBytes.push(name.charCodeAt(i));
NRF.setAdvertising([
// Flags: LE Limited Discoverable Mode, BR/EDR Not Supported
0x02, 0x01, 0x05,
// Complete Local Name
0x07, 0x09,nameBytes,
// MIDI
0x11, 0x06, 0x00, 0xC7, 0xC4, 0x4E, 0xE3, 0x6C, 0x51,
0xA7, 0x33, 0x4B, 0xE8, 0xED, 0x5A, 0x0E, 0xB8, 0x03
]);

};

@joebowbeer
Copy link
Owner

setAdvertising needs a flat array of bytes, not a nested array:

NRF.setAdvertising([
    // Flags: LE Limited Discoverable Mode, BR/EDR Not Supported
    0x02, 0x01, 0x05,
    // Complete Local Name: PuckCC
    0x07, 0x09, 0x50, 0x75, 0x63, 0x6B, 0x43, 0x43,
    // MIDI
    0x11, 0x06, 0x00, 0xC7, 0xC4, 0x4E, 0xE3, 0x6C, 0x51,
    0xA7, 0x33, 0x4B, 0xE8, 0xED, 0x5A, 0x0E, 0xB8, 0x03
  ]);

Maybe you can assign these bytes to an array variable and then overwrite the name chars at indices 5..10 before calling setAdvertising.

With BLE, by the way, you'll often need to clear the on-device cache in order to see changes. So, for example, on an Android, you'd turn off Bluetooth and forget the old device to clear the cache.

@freekatet
Copy link
Author

"flat array of bytes, not a nested array"

That's what I thought but I have no idea how to do that, actually I thought it was already what I was doing 😅

@freekatet
Copy link
Author

for the receiveCC that's where I am at but still no luck:

onWrite: function (evt) {
if (
evt.data.length == 6 &&
evt.data[0] == 0x80 &&
evt.data[1] == 0x80 &&
(evt.data[2] & 0xf0) == 0xb0
) {
var timestamp = ((evt.data[0] & 0x3f) << 7) | (evt.data[1] & 0x7f);
var channel = evt.data[2] & 0x0f;
var controller = evt.data[3];
var value = evt.data[4];
exports.onCC(timestamp, channel, controller, value);
}
}

@joebowbeer
Copy link
Owner

joebowbeer commented Apr 29, 2023

/// Receives MIDI events
function recv(evt) {
  var data = evt.data;
  var cmd = data[2] & 0xF0; // command
  var chn = data[2] & 0x0F; // channel
  var num = data[3]; // note or controller number
  var val = data[4]; // velocity or controller value
  switch(cmd) {
    case 0x80: // noteOff
    case 0x90: // noteOn
      if (val) LED1.set(); else LED1.reset();
      break;
    case 0xB0: // CC
      if (val) LED2.set(); else LED2.reset();
      break;
    default:
      if (val) LED3.set(); else LED3.reset();
  }
}

/// Turns the device into a MIDI controller
function init(name) {
  NRF.setServices({
    "03B80E5A-EDE8-4B33-A751-6CE34EC4C700": { // MIDI
      "7772E5DB-3868-4112-A1A9-F2669D106BF3": {
        readable: true,
        writable: true,
        notify: true,
        value: [0x80, 0x80, 0x00, 0x00, 0x00],
        onWrite: recv
      }
    }
  });
  NRF.setAdvertising([
    // Flags: LE Limited Discoverable Mode, BR/EDR Not Supported
    0x02, 0x01, 0x05,
    // Complete Local Name: PuckCC
    0x07, 0x09,
    name.charCodeAt(0),
    name.charCodeAt(1),
    name.charCodeAt(2),
    name.charCodeAt(3),
    name.charCodeAt(4),
    name.charCodeAt(5),
    // MIDI
    0x11, 0x06, 0x00, 0xC7, 0xC4, 0x4E, 0xE3, 0x6C, 0x51,
    0xA7, 0x33, 0x4B, 0xE8, 0xED, 0x5A, 0x0E, 0xB8, 0x03
  ]);
}

init("PuckCC");

@freekatet
Copy link
Author

::) effortless !
Thank you so much ! works perfectly.
Just a tiny issue that I need to be debug, I noticed that if you don't send any message for a while (lets say a few minutes) and then send some again, a crazy latency will start to happen for a little bit till it gets back to normal.
Any way you can confirm you have the same behavior ?
maybe something is going to sleep?

@joebowbeer
Copy link
Owner

The weird latency is likely due to low power mode kicking in and lowering the connection interval.

Try calling the following and see if that helps:

NRF.setConnectionInterval(20)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants