WebAudio / web-midi-api

The Web MIDI API, developed by the W3C Audio WG
http://webaudio.github.io/web-midi-api/
Other
323 stars 49 forks source link

Having Buffering Issues with Data Flow #152

Closed cynex closed 8 years ago

cynex commented 9 years ago

Hey, I just want to start of and say thank you to all who work on this API. I have been working on a project called http://www.launchpaddr.com for a while now, and plan on implementing it into the tool.

In my preliminary tests of the web midi api, however, I realized there might be a challenge that is difficult to overcome with the available options of the API. The launchpad has 64 pads, which light up when sent a midi note command, however, you have to send each one individually. Not only that, when you send multiple frames, there is no way of knowing that the buffer has finished before sending the next frame. The result is that the launchpad gets caught in a buffer underrun.

It would be great if the API could do two things. 1 - Have a MIDI buffer status variable on the Midi Out object, which could either read as busy or ready. That way, you would simply check the status before sending a frame, if it's busy, wait until the next iteration (frame skip essentially).

2 - It would be a nice to have if you could also send an array of notes, and not just one at a time. I think that might improve overall performance, but correct me if I am wrong.

I suppose a short term workaround would be to wait X number of milliseconds before sending the next frame, but then that is a very poor workaround as you never know the speed and capability of each machine, and there are different versions of the launchpad for example, and I believe the newer ones can receive data at a faster rate.

cynex commented 9 years ago

I suppose another option might be to add a callback after the .send command similar to how you connect the web midi with :

navigator.requestMIDIAccess().then( onsuccesscallback, onerrorcallback );

so it would look like:

MidiOut.send ( noteParameters ).then ( allClear, uhOh );

and you could use that on the last note you send. Whatever workaround is easiest for you guys to implement, is fine by me.

Thanks again, and I really look forward to integrating this API into my tool !

cwilso commented 9 years ago

Hey cynex - we considered this, but one problem is that there isn't necessarily just one MIDI buffer in the system; we don't have a whole lot of control, particularly on Windows (maybe the new Windows 10 APIs will change this, I don't know), in knowing what in the send buffer (Windows passes it off to the device driver and returns synchronously, with no insight into what's buffered.

That said - you do realize you can already send() an array containing multiple messages, right? That was by design.

cynex commented 9 years ago

Hey Chris, I understand this is a complicated issue. The launchpad has had this issue since launch, however, some DAW's have found a workaround. In FL Studio for example, since version 11, they have managed to workaround the buffer underrun issue, by having a unique port handler ( Port 115 ) which handles the buffer and all midi data for the launchpad. I'm not sure if this is of any help to you, but if it is, I can ask the Image-Line guys if they could share any info related to how it was accomplished.

I understand my case is unique and poses a challenge, but there must be a better workaround then setting a timeout and hoping that the buffer is ready for new data.

As for sending an array of data, maybe I am missing something ?

function test () { var Parameters = [ [114, 112, 30], [114, 113, 30], [114, 114, 30], ]; midiOut.send (Parameters); }

cwilso commented 9 years ago

Ah, yeah - it shouldn't be an array of arrays of bytes, but an array of bytes.

function test () { var Parameters = [ [114, 112, 30, 114, 113, 30, 114, 114, 30 ]; midiOut.send (Parameters); }

bome commented 9 years ago

IMHO: this is a problem with that (old) Launchpad firmware. It cannot be fixed by a generic solution on the host, because apparently, the Launchpad does not limit the USB data rate as needed. If it is not able to cope with the full USB data speed (or whatever speed the OS is sending out), it should use USB's mechanisms to limit the data rate -- but apparently, it does not. And, unfortunately, quite a few USB-MIDI devices don't, though often nobody notices because the full USB bandwidth is not used (for various reasons).

So, throttling on your end makes sense if you know that you're talking to a Launchpad. You can also use the Launchpad's double buffering feature...

Btw, I have the very first version of Launchpad (not class compliant) and the newer Launchpad Mini, and they both don't exhibit any problems when blasting MIDI data at them.

cynex commented 9 years ago

Hey Bome, I think we've come up with a fair workaround. using the double buffering helps but not by much. what I have made use of though is the rapid mode which sends 2 velocities per note. if using the standard method (one note at a time) it will wait for 3ms * the number of notes (pads) sent. If using rapid mode, you can wait 1.5ms per pair of pads. This seems to work quite nicely at the moment. I'm going to work in a method to pre-count the next "frame" of information to see if less than half is being updated. if so, use the standard mode, otherwise use the rapid mode.

Feel free to take a look at the js class function I am writing that will handle the launchpad. currently its only for the MK1 Model but I am getting an MK2 here shortly and will update.

http://launchpaddr.com/inc/launchpad.js

test page :

http://launchpaddr.com/test2.php (only works in canary currently -- and sometimes it will fail to autodetect due to a bug that sometimes canary only detects the input still, if so, refresh the page, and it will detect the output, and auto run the script)

bome commented 9 years ago

Hi Cynex, yes, that sounds reasonable to me. For everything else, I'd rather take this discussion off of this issue tracker, but I have not found a way to contact you. You can get hold of me here.