Closed jaycliff closed 10 years ago
It's not quite so easy, as I insist the time parameter of send needs to be optional, and in MIDI even short messages are of variable length. We could define
send( octet firstByte, optional octet secondByte, optional octet thirdByte, optional time )
with the note that the messages are interpreted and any unnecessary bytes are dropped (e.g. if you wanted to send a message of the single byte "F8" (timing clock message), at time n, you would need to call:
send( 0xf8, 0, 0, n );
and the zeros would be ignored. I'm not wild about this design, though, and it's not compatible with the current array design, which means we'd need a second parameter (you need a variable-length array method for sysex messages).
I'm concerned that you're running in to large expense using arrays, though - can you provide some references, ideally test cases? Have you tried using typed arrays?
I haven't tried using Uint8Array, but I think it should cause the same GC issue since it's still an object. I have also tried 'recycling' a single array for storing the message(s) (just setting the array's length to 0 after calling .send() to refresh it), but strangely, it doesn't work (this could be a Jazz plugin issue) and this prompted me to stick to the no-array method of sending the midi message, sending the messages one-by-one through a for-loop (I'm glad that Jazz has the MidiOut() method, even though it's just a 'single-shot' way of sending midi message).
I agree with you on this:
send( 0xf8, 0, 0, n );
Although I still think it's worth adding to the spec as a separate method simply because of its 'lightweight', 'no-array-needed' approach (reminds me of Function.prototype.call(). The current send method is like Function.prototype.apply()):
output.sendSoloMessage(144, 60, 127); // I'm bad at naming methods
I do like the current array-as-parameter method since it's convenient to send multiple messages in one go. I just wish that the method implementation would make an internal copy (or an exact clone) of the parameter that won't cause GC hiccups to allow a 'recycled array' as a parameter. Here's a rough example:
var track = [
[60, 50], // track 1 notes
[64, 72] // track 2 notes
],
param = [144, 0, 127];
for (var i = 0; i < track.length; i += 1) {
for (var j = 0; j < track[i].length; j += 1) {
param[1] = track[i][j];
output.send(param); // send would make an internal copy of param, or copy the values of param to its internal memory/cache.
}
}
It sounds like you've only tried this with the Jazz plugin-based polyfill. Can you give Chrome's native implementation a try? (Turn on the "enable web midi" flag in chrome://flags.)
Just tried chrome's, and it works really well :) I noticed that it's using the old methods like outputs() though.
I'm going to inform sema about the MidiOutLong issue with 'recycled' arrays.
Thanks for helping me out and for your awesome work on the API!
I wish there would be an option to use a method that's similar to Jazz's MidiOut() method, in other words, a method that doesn't require an array/sequence to be passed as parameter.
Example: output.send(144, 63, 127); // No array, just individual parameters
Using arrays are quite expensive, and will cause some garbage collector 'hiccups' because a new array has to be created as a parameter. I'm currently having problems with GC for my current midi app project because of this (I added a new send method to my project's copy of the webmidiapi shim that doesn't use arrays and it quickly solved the GC issue). I hope that you will consider adding this option to the spec.