This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 20505 - merge getInput, getOutput -> getPort() or getPortById()
Summary: merge getInput, getOutput -> getPort() or getPortById()
Status: CLOSED MOVED
Alias: None
Product: AudioWG
Classification: Unclassified
Component: MIDI API (show other bugs)
Version: unspecified
Hardware: PC All
: P2 normal
Target Milestone: TBD
Assignee: This bug has no owner yet - up for the taking
QA Contact: public-audio
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-12-24 08:15 UTC by Marcos Caceres
Modified: 2013-01-11 08:59 UTC (History)
4 users (show)

See Also:


Attachments

Description Marcos Caceres 2012-12-24 08:15:56 UTC
The methods getInput() and getOutput() effectively do the same thing (evident also by the amount of duplicate text in the spec); as such, they should be merged. Furthermore, it's not clear (in the spec) as why there are 3 different ways to get a MIDIPort. I would like to suggest that these two methods be merged into a single method and the way to be one way to get a MIDIPort: by its ID… hence getPort(id) or getPortById(id);

Consider:

1. getInput/Output(MIDIPort)… doesn't make much sense, as why is a developer requesting a MIDIPort with a MIDIPort they already have? I'm sure there is something I'm missing here.

2. getInput/Output(short): first, short here doesn't make much sense, given that a WebIDL short is defined as:
"The short type is a signed integer type that has values in the range [−32768, 32767]". So requesting negative indexes don't make any sense here. In addition, this is basically the same as doing:

var port = midiAccess.getInputs()[x];

Additionally, the ordering of the ports may or may not be consistent after each session, so saying getInput(number) is inherently unreliable. Also, it's kinda unhelpful is you want to, for instance, get the last available port. This is because midiAccess has no length property. The only way to get the length is to first call getInputs(), and get the length from there… but then you already have the list of inputs, in so you don't then need to call getInput( number ), because it's already the same as inputs[number].

3. getInput/Output( id ): this is probably the only one that makes sense. It's a cheap way of checking if a previously used exists:

var favInstrument = midiAccess.getInput(localStorage.fav)
if(favInstrument){ … }
Also, if target is is not found, just return null. Please don't throw an exception. Throwing exceptions should be done in "exceptional" circumstances, not on simple lookups.
Comment 1 Chris Wilson 2012-12-25 23:20:35 UTC
1) The separation between these is because they are, in fact, two different lists (inputs and outputs), and they're fundamentally different objects; one can only read, one can only write.  Merging these entirely will, I think, tend to be confusing, as output ports will have an onmessage handler, and input ports will have a send() method, even though they are orphans that don't do anything.  I'm not very comfortable with that, as I think the guard rails (guide rails?) of having the methods only where appropriate is good.

On why there are three methods to get an input/output - personally, I'd ditch the MIDIPort version, but I do want to keep the index version, as (if you look at most of my demos), it's fairly common to show a dropdown to select ports, and index is an easy way to do that.  No, it won't persist over sessions, but it doesn't need to.  In fact, if you were persisting over sessions, I'd expect an app to pop "pick your port" dialog that is index-based, and then persist the ID.
Comment 2 Jussi Kalliokoski 2012-12-25 23:48:44 UTC
(In reply to comment #1)
> 1) The separation between these is because they are, in fact, two different
> lists (inputs and outputs), and they're fundamentally different objects; one
> can only read, one can only write.  Merging these entirely will, I think,
> tend to be confusing, as output ports will have an onmessage handler, and
> input ports will have a send() method, even though they are orphans that
> don't do anything.  I'm not very comfortable with that, as I think the guard
> rails (guide rails?) of having the methods only where appropriate is good.
> 
> On why there are three methods to get an input/output - personally, I'd
> ditch the MIDIPort version, but I do want to keep the index version, as (if
> you look at most of my demos), it's fairly common to show a dropdown to
> select ports, and index is an easy way to do that.  No, it won't persist
> over sessions, but it doesn't need to.  In fact, if you were persisting over
> sessions, I'd expect an app to pop "pick your port" dialog that is
> index-based, and then persist the ID.

I agree with Chris, the port types (input, output) are very different, and having the MIDIPort interface as separate from those two helps with the fact that you don't have to capture the devices when you enumerate (e.g. winmm grants only one program exclusive, and exclusive only, access per MIDI device).

As for the getInput/getOutput merging, I'm quite fine with reducing the overloads to just the ID, the use case Chris mentioned is as simple and better handled with IDs anyway, to be more resilient to live hardware changes:

inputSelect = document.createElement('select')
document.body.appendChild(inputSelect)

midiaccess.enumerateInputs().forEach((input) => {
  var elem = document.createElement('option')
  elem.value = input.id
  elem.innerHTML = input.name
  inputSelect.appendChild(elem)
})

inputSelect.onchange = function () {
  var input = midiaccess.getInput(this.value)
}

But I'm not sure it's a good idea to merge the two different methods, unless we want to add a mandatory constraint that the fingerprint/ID needs to be different for input devices and output devices. It would be especially annoying if faced with a UA that doesn't have enough data to give reliable fingerprints, an application would've stored a fingerprint and assumed that the method would return an output port, but was given an input port instead, resulting in an error if it tried to send anything to it.
Comment 3 Marcos Caceres 2012-12-26 00:14:20 UTC
(In reply to comment #2)
> I agree with Chris, the port types (input, output) are very different, and
> having the MIDIPort interface as separate from those two helps with the fact
> that you don't have to capture the devices when you enumerate (e.g. winmm
> grants only one program exclusive, and exclusive only, access per MIDI
> device).

Ok, my objection was purely based on having to add more objects to the global scope (i.e., all WebIDL interfaces end up on the Window object... but c'est la vie). From an authoring perspective, it makes little difference and Chris' is right in that it may avoid some confusion.  
 
> As for the getInput/getOutput merging, I'm quite fine with reducing the
> overloads to just the ID, the use case Chris mentioned is as simple and
> better handled with IDs anyway, to be more resilient to live hardware
> changes:
> 
> inputSelect = document.createElement('select')
> document.body.appendChild(inputSelect)
> 
> midiaccess.enumerateInputs().forEach((input) => {
>   var elem = document.createElement('option')
>   elem.value = input.id
>   elem.innerHTML = input.name
>   inputSelect.appendChild(elem)
> })
> 
> inputSelect.onchange = function () {
>   var input = midiaccess.getInput(this.value)
> }
> 
> But I'm not sure it's a good idea to merge the two different methods, unless
> we want to add a mandatory constraint that the fingerprint/ID needs to be
> different for input devices and output devices.

I'm of the opinion that one should go for "the ideal" solution first, then backtrack if it fails. In this case, it would mean getting some implementation experience to see if we can get unique fingerprints. 

> It would be especially
> annoying if faced with a UA that doesn't have enough data to give reliable
> fingerprints, an application would've stored a fingerprint and assumed that
> the method would return an output port, but was given an input port instead,
> resulting in an error if it tried to send anything to it.

Agreed. But as I said above, at this stage in the standardisation game, it does not hurt to see if that problem can be solved somehow. 

Having said that, we should add a big red note to the spec asking implementers for feedback exactly about this.
Comment 4 Marcos Caceres 2012-12-26 15:27:24 UTC
(In reply to comment #3)
> (In reply to comment #2)
> > It would be especially
> > annoying if faced with a UA that doesn't have enough data to give reliable
> > fingerprints, an application would've stored a fingerprint and assumed that
> > the method would return an output port, but was given an input port instead,
> > resulting in an error if it tried to send anything to it.
> 
> Agreed. But as I said above, at this stage in the standardisation game, it
> does not hurt to see if that problem can be solved somehow. 
> 
> Having said that, we should add a big red note to the spec asking
> implementers for feedback exactly about this.

Just wanted to record a random thought here:

getPort(DOMString id, optional MIDIPortType type);

As in: 
var port = midi.getPort("12e23f3", "input");
Comment 5 Chris Wilson 2012-12-26 17:00:30 UTC
(In reply to comment #4)
> Just wanted to record a random thought here:
> 
> getPort(DOMString id, optional MIDIPortType type);
> 
> As in: 
> var port = midi.getPort("12e23f3", "input");

But why would you do that? For any given id, the type is predetermined (and fixed).  If you were going down this path (of collapsing Input and Output together), I would expect:

interface MIDIAccess {
    sequence<MIDIPort> listInputs ();
    sequence<MIDIPort> listOutputs ();
    MIDIPort          getPort (MIDIPort or DOMString or short target);
};
Comment 6 Marcos Caceres 2012-12-26 17:10:32 UTC
(In reply to comment #5)
> (In reply to comment #4)
> > Just wanted to record a random thought here:
> > 
> > getPort(DOMString id, optional MIDIPortType type);
> > 
> > As in: 
> > var port = midi.getPort("12e23f3", "input");
> 
> But why would you do that? For any given id, the type is predetermined (and
> fixed).

Jussi said that the fingerprint might not be reliable (i.e., an input and and output would have the same fingerprint): "It would be especially annoying if faced with a UA that doesn't have enough data to give reliable fingerprints,  an application would've stored a fingerprint and assumed that the method would return an output port, but was given an input port instead, resulting in an error if it tried to send anything to it."

>  If you were going down this path (of collapsing Input and Output
> together), I would expect:
> 
> interface MIDIAccess {
>     sequence<MIDIPort> listInputs ();
>     sequence<MIDIPort> listOutputs ();
>     MIDIPort          getPort (MIDIPort or DOMString or short target);
> };

Sorry to again be a dumbass, but I really don't understand why you send a MIDIPort to get a MIDIPort? Can you please explain the logic there?
Comment 7 Chris Wilson 2012-12-26 17:29:52 UTC
(In reply to comment #6)
> (In reply to comment #5)
> > (In reply to comment #4)
> > > Just wanted to record a random thought here:
> > > 
> > > getPort(DOMString id, optional MIDIPortType type);
> > > 
> > > As in: 
> > > var port = midi.getPort("12e23f3", "input");
> > 
> > But why would you do that? For any given id, the type is predetermined (and
> > fixed).
> 
> Jussi said that the fingerprint might not be reliable (i.e., an input and
> and output would have the same fingerprint): "It would be especially
> annoying if faced with a UA that doesn't have enough data to give reliable
> fingerprints,  an application would've stored a fingerprint and assumed that
> the method would return an output port, but was given an input port instead,
> resulting in an error if it tried to send anything to it."

I don't believe the system would ever confuse an input and an output fingerprint.  In my shim implementation, I'll simply prepend "i" or "o" to identify.  The confusion potential with fingerprints is across sessions, when you have multiple ports with the same name in the system, when the indices change (e.g. when a device is added or removed from the system) - For example, I have two Novation Launchpads (not an uncommon thing).  If I add a controller that ends up (according to the OS) showing up in the list of interfaces before those, suddenly one of those might be at index 5 instead of 4 - and the other one is at index 4, and looks just like the other one used to in terms of name, manufacturer, index... everything that persists.

> >     MIDIPort          getPort (MIDIPort or DOMString or short target);
> 
> Sorry to again be a dumbass, but I really don't understand why you send a
> MIDIPort to get a MIDIPort? Can you please explain the logic there?

I would be happy to drop it.  I was simply copying out of the current spec.  I think index (short) and id (string) are sufficient.

The MIDIPort type as a param predates the index; Jussi's original proposal had MIDIPort, I believe.
Comment 8 Jussi Kalliokoski 2012-12-26 18:12:03 UTC
(In reply to comment #7)
> I don't believe the system would ever confuse an input and an output
> fingerprint.  In my shim implementation, I'll simply prepend "i" or "o" to
> identify.  The confusion potential with fingerprints is across sessions,
> when you have multiple ports with the same name in the system, when the
> indices change (e.g. when a device is added or removed from the system) -
> For example, I have two Novation Launchpads (not an uncommon thing).  If I
> add a controller that ends up (according to the OS) showing up in the list
> of interfaces before those, suddenly one of those might be at index 5
> instead of 4 - and the other one is at index 4, and looks just like the
> other one used to in terms of name, manufacturer, index... everything that
> persists.

You're probably right, it seems unlikely that the IDs would get confused. Sounds good, I'm fine with going ahead and merging the methods.
Comment 9 Chris Wilson 2012-12-26 19:05:00 UTC
(In reply to comment #8)
> You're probably right, it seems unlikely that the IDs would get confused.
> Sounds good, I'm fine with going ahead and merging the methods.

I don't think merging the methods is the right thing to do, as you can't use indices then.  I just meant that if you DID have a getPort, it doesn't need a type if it's using ONLY fingerprint (or MIDIPort).
Comment 10 Olivier Thereaux 2013-01-11 08:58:55 UTC
MIDI API issues now tracked at https://github.com/WebAudio/web-midi-api/issues

This issue is at https://github.com/WebAudio/web-midi-api/issues/2