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

sysex identity messages from hardware synths in Firefox are split into 3 chunks #2

Closed
jeffgca opened this issue Feb 9, 2021 · 11 comments

Comments

@jeffgca
Copy link

jeffgca commented Feb 9, 2021

I am testing out how to detect synthesizers from browsers and ran into what looks like a low-level bug in the JAZZ plugin or webmidi-api browser extension. I'm logging the bug here because the lower-level code involved is in the JZZ library but feel free to move this issue if I've gotten that wrong.

To reproduce:

  1. install the Jazz plugin and web-midi-api plugin in Firefox and ensure that they are working eg that the WebMIDI implementation exists. As well, ensure that a MIDI instrument of some sort ( I tested with a Waldorf Blofeld ) is connected to the computer via a MIDI interface ( USB, Bluetooth, etc )

  2. open this url in both chrome and Firefox:

https://codepen.io/canuckistani-1471644214/pen/xxREwGE

  1. open firefox and chrome devtools

  2. click the "scan" button in either browser

You should see several objects printed out in the console in both browsers.

Expected results

The messages printed out from the top of the onMSG handler should be the same

Actual results

Firefox prints out 5 messages ( see below ) for me whereas Chrome prints two. Firefox's messages appear to be split at 3 or 6 bytes in length

Firefox devtools console:

{…}
0: 240
1: 126
2: 1
_from: Array [ {…} ]
length: 3
: Array []
iframeConsoleRunner-7f4d47902dc785f30dedcac9c996b9f31d4dfcc33567cc48f0431bc918c2bf05.js:1:4080
{…}
0: 6
1: 2
2: 62
_from: Array [ {…} ]
length: 3
: Array []
iframeConsoleRunner-7f4d47902dc785f30dedcac9c996b9f31d4dfcc33567cc48f0431bc918c2bf05.js:1:4080
{…}
0: 19
1: 0
2: 0
3: 0
4: 49
5: 46
_from: Array [ {…} ]
length: 6
: Array []
iframeConsoleRunner-7f4d47902dc785f30dedcac9c996b9f31d4dfcc33567cc48f0431bc918c2bf05.js:1:4080
{…}
0: 50
1: 52
2: 247
_from: Array [ {…} ]
length: 3
: Array []
iframeConsoleRunner-7f4d47902dc785f30dedcac9c996b9f31d4dfcc33567cc48f0431bc918c2bf05.js:1:4080
{…}
0: 240
1: 126
2: 127
3: 6
4: 1
5: 247
_from: Array [ {…} ]
length: 6
: Array []
iframeConsoleRunner-7f4d47902dc785f30dedcac9c996b9f31d4dfcc33567cc48f0431bc918c2bf05.js:1:4080

Chrome Console:

gt(6) [240, 126, 127, 6, 1, 247, _from: Array(1)]0: 2401: 1262: 1273: 64: 15: 247length: 6_from: [f]__proto__: Array(0)
pen.js:5 

gt(15) [240, 126, 1, 6, 2, 62, 19, 0, 0, 0, 49, 46, 50, 52, 247, _from: Array(1)]0: 2401: 1262: 13: 64: 25: 626: 197: 08: 09: 010: 4911: 4612: 5013: 5214: 247length: 15_from: [f]__proto__: Array(0)
@jazz-soft
Copy link
Owner

Is that Windows or other OS?
Does it work properly if you run it with the node script?

@jeffgca
Copy link
Author

jeffgca commented Feb 10, 2021

Apologies - this is macOS Mojave ( 10 .14 ).

Happy to test in node - which script are you referring to?

@jazz-soft
Copy link
Owner

Try to run via Node some code similar to what you run in browser.

@jeffgca
Copy link
Author

jeffgca commented Feb 10, 2021

I tried with a script ( see below ) and also tested using the jzz-midi-gear demo page in Firefox and Chrome. Chrome works, but I never see the identity response messages from the attached synths in Firefox or the node script.

Browsers:

  • Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:86.0) Gecko/20100101 Firefox/86.0
  • Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36

I seem to be running the latest versions of jazz-midi and webmidi-api Firefox extensions. Happy to test further if you have suggestions of how to get better debugging info.

var JZZ = require('jzz');
require('jzz-midi-gear')(JZZ);
const _ = require('lodash');

function onMidiMsg(msg) {
  // debugger;
  if (msg.isIdResponse()) {
    console.log('ID RESP>', _.toArray(msg));
    
  }
  else {
    console.log('RAW>', _.toArray(msg))
  }
}

JZZ({ sysex:true })
  .or(function(){ console.warn('Cannot start MIDI engine!'); })
  .and(function() {
    var info = this.info();

    let inputs = _.map(info.inputs, (_in, i) => {
      this.openMidiIn(i).connect(onMidiMsg);
      return [info.inputs[i].name, info.inputs[i].id]
    })

    console.log(inputs);

    for (var i in info.outputs) { 
      this.openMidiOut(i).sxIdRequest();
    }

    let outputs = _.map(info.inputs, (_in, i) => {
      // this.openMidiIn(i).connect(onMidiMsg);
      setTimeout(() => {
        this.openMidiOut(i).sxIdRequest();
      }, 200);
      return [info.outputs[i].name, info.outputs[i].id]
    })
  });

@jazz-soft
Copy link
Owner

Can you use msg.toString() instead of _.toArray(msg) ? - that would be easier to understand...
Also, can you please run the script above from node? (If you are not well familiar with node, I can provide more detailed instructions)

@jeffgca
Copy link
Author

jeffgca commented Feb 11, 2021

I did, I ran the above script using node.

_.toArray is a lodash function that just casts an array-like object to a real array:

https://lodash.com/docs/#toArray

@jazz-soft
Copy link
Owner

What did the node script output - chunked or complete sysex?

@jeffgca
Copy link
Author

jeffgca commented Feb 11, 2021

Thanks for getting back to me.

What did the node script output - chunked or complete sysex?

The node script didn't get any sysex messages from the hardware. That's the interesting thing, and I'll try to provide some additional context in case it's helpful.

I have a very simple node[1] script that impersonates a specific synthesizer, it's handy for testing if you are at a cafe or something. As well, I have a Waldorf Blofeld connected via USB to my laptop.

I have also reduced the node jzz script to as small an example as possible [2]

As I sit here and test the codepen example, I see three distinct behaviours:

  1. In Chrome both the hardware Blofeld and emulated script are detected properly. The sysex message that gets passed into jzz's input message callback gets full, complete sysex messages. The input callback gets called only twice, one for each response message.
  2. In Firefox I see a single, complete response coming from the emulation script but 4 messages coming from the hardware Blofeld.
  3. in the jzz node script I don't see any sysex messages coming the the hardware blofeld, the callback isn't called. I do see complete messages coming from my emulation script.

In order to make sure the behaviour I was seeing from the Blofeld could be generalized, I also plugged in a Novation Peak and saw that neither the Peak nor the Blofeld were detected, and that in Firefox there were 3 ( Peak ) or 4 ( Blofeld ) messages sent to the input callback instead of the expected single callback. In the jzz-based node script ( below ) the input callback is never called for messages from the Peak or the Blofeld, but it is called for messages sent from the emulation script.

I've also got MIDI Monitor open, and MIDI monitor logs the correct messages from the Peak and Blofeld, similar to chrome.

[1] node-midi based local mock synth

const midi = require('midi');

let mopho = {
  rawId: 'f0 7e 00 06 02 01 25 01 00 00 21 00 00 f7',
  brand: 'Dave Smith',
  model: 'Mopho',
  type: 'Synth Module',
  arrId: [
    240, 126, 0, 6,  2, 1,
     37,   1, 0, 0, 33, 0,
      0, 247
  ],
  vendorId: [ 1 ],
  strVendorId: '1'
};

let name = 'FakeMopho';
let out = new midi.Output();
out.openVirtualPort(name);

out.openPort(0);

let input = new midi.Input();
input.openVirtualPort(name);
input.ignoreTypes(false, true, true);

input.on('message', (ts, msg) => {
  console.log(`IN> ${ts}\t${msg}`);
  // naive comparison
  if (msg.join('-') === '240-126-127-6-1-247') {
    console.log('Got sysex ID request.');
    out.sendMessage(mopho.arrId);
  }
})

[2] JZZ node script

var JZZ = require('jzz');
require('jzz-midi-gear')(JZZ);

JZZ({ sysex:true })
  .or(function(){ console.warn('Cannot start MIDI engine!'); })
  .and(function() {
    var info = this.info();

    for ( var i in info.inputs) {
      let _i = i;
      this.openMidiIn(i).connect(function (msg) {
        console.log(`${info.inputs[_i].name} ${msg.isIdResponse()}`, msg.toString());
      });
    }

    for (var i in info.outputs) { 
      this.openMidiOut(i).sxIdRequest();
    }
  });

@jeffgca jeffgca changed the title Truncated sysex messages in Firefox sysex identity messages from hardware synths in Firefox are split into 3 chunks Feb 11, 2021
@jazz-soft
Copy link
Owner

jazz-soft commented Feb 12, 2021

Strange... everything works for me in Node, Chrome, Firefox, and even Safari...
Screen Shot 2021-02-11 at 9 46 14 PM

here are my test scripts:

var MT = require('midi-test');
var src = new MT.MidiSrc('Fake device');
src.connect();

var dst = new MT.MidiDst('Fake device');
dst.receive = function(msg) {
  if (msg[0] == 240 && msg[1] == 126 && msg[3] == 6 && msg[4] == 1 && msg[5] == 247) {
    console.log('received:', msg);
    console.log('Sending ID response');
    src.emit([240, 126, 0, 6, 2, 1, 37, 1, 0, 0, 33, 0, 0, 247]);
    src.emit([240, 126, 1, 6, 2, 62, 19, 0, 0, 0, 49, 46, 50, 52, 247]);
  }
};
dst.connect();
var JZZ = require('jzz');
require('jzz-midi-gear')(JZZ);

var inputs;
var outputs;

JZZ().or('Cannot start MIDI!').and(function() {
  var info = this.info();
  inputs = info.inputs;
  outputs = info.outputs;
  console.log('Inputs:', inputs);
  console.log('Outputs:', outputs);
  for (var i = 0; i < inputs.length; i++) this.openMidiIn(i).or('Cannot open MIDI-In').connect(midi_handle(inputs[i].name));
  setTimeout(send_id_requests, 100);
  setTimeout(function() { JZZ().close(); }, 1000);
});

function send_id_requests() {
  for (var i = 0; i < outputs.length; i++) {
    JZZ().openMidiOut(i).or('Cannot open MIDI-out').sxIdRequest();
  }
}

function midi_handle(name) {
  return function(msg) {
    console.log(name + ': ' + msg);
    if (msg.isIdResponse()) console.log(msg.gearInfo());
  }
}

@jeffgca
Copy link
Author

jeffgca commented Feb 12, 2021

Interesting! Definitely seems to be something with my system then - I'll look into this more tomorrow because I'd really like to be able to test with different browsers. Thanks again for the help.

@jazz-soft jazz-soft transferred this issue from jazz-soft/JZZ Feb 21, 2021
@jazz-soft
Copy link
Owner

I once saw a similar issue when using a generic-made MIDI cable. I threw the cable away :)

thirtythreeforty added a commit to thirtythreeforty/jazz-midi that referenced this issue Oct 10, 2023
The pthread and lock are never created if the "if" in the constructor
fails, and the members are never initialized.  But the destructor
unconditionally destroys them.  Fix this by adding a guard flag.

This fixes a segfault I encountered on Arch with Pipewire as the ALSA
server.

    Thread 1 "node" received signal SIGSEGV, Segmentation fault.
    0x00007ffff428aebd in pthread_cancel () from /usr/bin/../lib/libc.so.6
    (gdb) bt
    #0  0x00007ffff428aebd in pthread_cancel () from /usr/bin/../lib/libc.so.6
    jazz-soft#1  0x00007ffff3b773ff in CMidiInHW::~CMidiInHW (this=0x55555572a430, __in_chrg=<optimized out>) at ../../midi/MidiALSA.cpp:366
    jazz-soft#2  0x00007ffff3b77452 in CMidiInHW::~CMidiInHW (this=0x55555572a430, __in_chrg=<optimized out>) at ../../midi/MidiALSA.cpp:369
    jazz-soft#3  0x00007ffff3b77105 in CMidiALSA::MidiInOpen[abi:cxx11](wchar_t const*, void*) (this=0x5555557b9880, name=0x5555556b3cf0 L"input", p=0x55555579e3a0) at ../../midi/MidiALSA.cpp:329
    jazz-soft#4  0x00007ffff3b6f0c8 in MidiInOpen (env=0x55555579e410, args=0x7fffffff9e20) at ../jazz-midi.cpp:461
    jazz-soft#5  0x00007ffff535bd33 in ?? () from /usr/bin/../lib/libnode.so.115
    jazz-soft#6  0x00007ffff5840d97 in ?? () from /usr/bin/../lib/libnode.so.115
    jazz-soft#7  0x00007ffff5841942 in v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) () from /usr/bin/../lib/libnode.so.115
    jazz-soft#8  0x00007ffff56cadf6 in ?? () from /usr/bin/../lib/libnode.so.115
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