-
Notifications
You must be signed in to change notification settings - Fork 273
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
Feature request: Detect/notify on port changes #191
Comments
This is an ongoing and unfixed issue primarily because there is no simple solution (that I am aware of) that can work across all supported APIs. I think there is a callback with CoreMidi to tell us when a device connection changes but as far as I remember, there was no such thing for Windows (but I admit to not looking into the Windows API in a while). If someone can find a way to implement something like a “deviceChangeCallback”, I’m happy to support the effort.
Given this problem, it is best to organize your code in such a way that it does not depend on devices always staying connected. For example, a device selection popup should only appear when the user wants to establish a connection. Obviously, a user should know that if they disconnect a device while it is in use, then the program behaviour might be a bit problematic.
As for your example at the bottom, I’m surprised you are getting that behaviour. My own use with CoreMidi has been pretty stable. Did you open/close programs that establish virtual ports (or connect / disconnect physical MIDI devices) while running that? The same thing happens in the midiProbe.cpp example. Does that produce the same result?
… On Feb 24, 2019, at 2:37 PM, mcclure ***@***.***> wrote:
I am attempting to deal with what happens when midi devices hot swap at runtime. I am building my own mapping of device id -> current port number so that if a device goes away and comes back I know it is the same device. The problem is I do not know when I need to rebuild my map. It is not clear at what times RTMidi's port view can change and I do not know how to detect a change except by iterating all ports and comparing the strings, which seems slightly wasteful.
I would like to request either some kind of a call like >havePortsChanged(), or a message that gets queued with midiin->getMessage which flags ports have changed. RTMidi is in a better position to determine when a port change has occurred than my client code is.
If this is not feasible, something that would help is better documentation around port changes. It would be very helpful to be able to know at what times ports can change or when I need to a port change. If I need to potentially be ready for ports to change between any two calls*, it would be useful to have that fact documented. Everything around ports is very unclear in the current docs, for example, although its usage is clear from the docs I cannot find an explanation anywhere of what openPort(0) actually does.
I am aware of #30 <#30> but I am slightly confused what to do with it. It looks like an entirely different fork with things like different build scripts and namespacing. This is a smaller, more attainable question. Since #30 <#30> has not been integrated over a period of five years maybe it is worth exploring smaller solutions.
* I did a test on OS X 10.13.6 which seems to indicate that port assignments can change at any time as if the change were occurring in a simultaneous thread. Perhaps this is the race condition other issues refer to. I ran:
int outc = midiOut.getPortCount();
int inc = midiIn.getPortCount();
ostringstream str;
str << "Out " << outc << endl;
for(int c = 0; c < outc; c++) {
str << c << ": " << midiOut.getPortName(c) << endl;
}
str << "In " << inc << endl;
for(int c = 0; c < inc; c++) {
str << c << ": " << midiIn.getPortName(c) << endl;
}
If I run this I frequently see
MidiInCore::getPortName: the 'portNumber' argument (1) is invalid.
This implies port 1 disappeared between midiIn.getPortCount() and midiIn.getPortName().
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub <#191>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AFOBpVoiBWMGwpySzOWgsE6l6rF1xMRwks5vQunvgaJpZM4bOuPW>.
{"api_version":"1.0","publisher":{"api_key":"05dde50f1d1a384dd78767c55493e4bb","name":"GitHub"},"entity":{"external_key":"github/thestk/rtmidi","title":"thestk/rtmidi","subtitle":"GitHub repository","main_image_url":"https://github.githubassets.com/images/email/message_cards/header.png","avatar_image_url":"https://github.githubassets.com/images/email/message_cards/avatar.png","action":{"name":"Open in GitHub","url":"https://github.com/thestk/rtmidi"}},"updates":{"snippets":[{"icon":"DESCRIPTION","message":"Feature request: Detect/notify on port changes (#191)"}],"action":{"name":"View Issue","url":"#191"}}} [ { ***@***.***": "http://schema.org", ***@***.***": "EmailMessage", "potentialAction": { ***@***.***": "ViewAction", "target": "#191", "url": "#191", "name": "View Issue" }, "description": "View this Issue on GitHub", "publisher": { ***@***.***": "Organization", "name": "GitHub", "url": "https://github.com" } } ]
|
I'm sorry, I messed up typing the above, the error:
I saw this while running the included code in a loop and repeatedly plugging in and unplugging a MIDI device. Is this still surprising? I can test with midiprobe but that of course does not run in a loop. "it is best to organize your code in such a way that it does not depend on devices always staying connected" The problem with this approach is that say I have two devices, and I am sending MIDI data to the device on port 0, and then the device at port 0 is disconnected. Now the device that was on port 1 is on port 0 and I am sending data to the wrong device. So it seems to me that literally all RtMIDI code "depends on devices always staying connected" or, at least, depends on being able to detect that a device has disconnected. |
Yes, if you ran the code in a loop and repeatedly plug and unplug devices, I can definitely see how you would get that error.
The device enumeration used by RtMidi is essentially what all the MIDI APIs use themselves. They simply enumerate a list of devices, the order of which is somewhat undefined. Then you use the device number to open a port. So, the APIs do not provide a mechanism (generally) that gets around the problem of having a device plugged or unplugged while being enumerated or used. But don’t you think that the user should be aware that this will potentially cause problems? If you are sending MIDI data to the device on port 0 and then someone unplugs it, that should be expected to produce undefined behaviour. I think the best scenario would be that you get an error from the system saying that the connection is lost and then you respond to that by telling the user they did something they shouldn’t have done. And I believe RtMidi will throw an error in that case.
… On Feb 24, 2019, at 4:45 PM, mcclure ***@***.***> wrote:
I'm sorry, I messed up typing the above, I saw the MidiInCore::getPortName: the 'portNumber' argument (1) is invalid error while running the included code in a loop and repeatedly plugging in and unplugging a MIDI device. Is this still surprising? I can test with midiprobe but that of course does not run in a loop.
"it is best to organize your code in such a way that it does not depend on devices always staying connected"
The problem with this approach is that say I have two devices, and I am sending MIDI data to the device on port 0, and then the device at port 0 is disconnected. Now the device that was on port 1 is on port 0 and I am sending data to the wrong device. So it seems to me that literally all RtMIDI code "depends on devices always staying connected" or, at least, depends on being able to detect that a device has disconnected.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub <#191 (comment)>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AFOBpWyHvU5hDBE5udUq6ZAd5_xcb6ewks5vQwgLgaJpZM4bOuPW>.
{"api_version":"1.0","publisher":{"api_key":"05dde50f1d1a384dd78767c55493e4bb","name":"GitHub"},"entity":{"external_key":"github/thestk/rtmidi","title":"thestk/rtmidi","subtitle":"GitHub repository","main_image_url":"https://github.githubassets.com/images/email/message_cards/header.png","avatar_image_url":"https://github.githubassets.com/images/email/message_cards/avatar.png","action":{"name":"Open in ***@***.*** in #191: I'm sorry, I messed up typing the above, I saw the `MidiInCore::getPortName: the 'portNumber' argument (1) is invalid` error while running the included code in a loop and repeatedly plugging in and unplugging a MIDI device. Is this still surprising? I can test with midiprobe but that of course does not run in a loop.\r\n\r\n\"it is best to organize your code in such a way that it does not depend on devices always staying connected\"\r\n\r\nThe problem with this approach is that say I have two devices, and I am sending MIDI data to the device on port 0, and then the device at port 0 is disconnected. Now the device that was on port 1 is on port 0 and I am sending data to the wrong device. So it seems to me that literally all RtMIDI code \"depends on devices always staying connected\" or, at least, depends on being able to detect that a device has disconnected."}],"action":{"name":"View Issue","url":"#191 (comment)"}}} [ { ***@***.***": "http://schema.org", ***@***.***": "EmailMessage", "potentialAction": { ***@***.***": "ViewAction", "target": "#191 (comment)", "url": "#191 (comment)", "name": "View Issue" }, "description": "View this Issue on GitHub", "publisher": { ***@***.***": "Organization", "name": "GitHub", "url": "https://github.com" } } ]
|
If that is the case it would still be helpful to explicitly explain this in the documentation, as currently it is not clear that a device could disappear between calling getPortCount() and acting on it on the next line. |
I just added a few sentences to the documentation about this:
======
Note that the port enumeration is system specific and will change if any devices are unplugged or plugged (or a new virtual port opened or closed) by the user. Thus, the port numbers should be verified immediately before opening a stream. As well, if a user unplugs a device (or closes a virtual port) while a port connection exists to that device/port, a MIDI system error will be generated.
======
It should be expanded but hopefully that at least gets things rolling in this direction.
… On Feb 24, 2019, at 9:12 PM, mcclure ***@***.***> wrote:
If that is the case it would still be helpful to explicitly explain this in the documentation, as currently it is not clear that a device could disappear between calling getPortCount() and acting on it on the next line.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub <#191 (comment)>, or mute the thread <https://github.com/notifications/unsubscribe-auth/AFOBpdYwqXUZDZiFv0e_vx8hJ1CdDYhpks5vQ0aPgaJpZM4bOuPW>.
{"api_version":"1.0","publisher":{"api_key":"05dde50f1d1a384dd78767c55493e4bb","name":"GitHub"},"entity":{"external_key":"github/thestk/rtmidi","title":"thestk/rtmidi","subtitle":"GitHub repository","main_image_url":"https://github.githubassets.com/images/email/message_cards/header.png","avatar_image_url":"https://github.githubassets.com/images/email/message_cards/avatar.png","action":{"name":"Open in ***@***.*** in #191: If that is the case it would still be helpful to explicitly explain this in the documentation, as currently it is not clear that a device could disappear between calling getPortCount() and acting on it on the next line."}],"action":{"name":"View Issue","url":"#191 (comment)"}}} [ { ***@***.***": "http://schema.org", ***@***.***": "EmailMessage", "potentialAction": { ***@***.***": "ViewAction", "target": "#191 (comment)", "url": "#191 (comment)", "name": "View Issue" }, "description": "View this Issue on GitHub", "publisher": { ***@***.***": "Organization", "name": "GitHub", "url": "https://github.com" } } ]
|
That helps! |
It might help a little, but for windows and mac you can identify a connection/disconnection by testing the handles. In the case of a disconnected device, trying to get the message would give a invalid handler error on windows. Don't know the code for MAC but it's work pretty much the same. You can do the same for sending message, you will get a invalid handle message. I didn't look into linux much considering I didn't have to use it yet. A little side note, some Synthesizer doesn't invalidate handle properly on shut down if the port was active. It's the case for VirtualMidiSynth on windows. I think that the issue happen only for Synth close to the OS. (VirtualMidiSynth use it's own driver) A quick solution to validate a port would simply be to send a DUMMY message to the port from time to time. Having an error will most likely mean that your port is invalidated. At this point, you can simply iterate over all available device to refresh your link with the port. If it's not available anymore you can store the name somewhere and try to update it until it's back in the port list. |
FWIW, Jack also allows to register callbacks for when new clients or ports are registered or ports are renamed: http://jackaudio.org/files/docs/html/group__ClientCallbacks.html |
If CoreMIDI and Jack support this feature but it turns out Windows does not, it seems like it would be possible for RtMidi to emulate the feature in a degraded way on Windows (ie a thread wakes up every 1ms and polls devices) and there would be significant benefit even if the Windows version didn't work as well. As with #195 this is something that RtMidi is more able to implement than user code because it is talking to the OS MIDI services directly. |
ALSA does not support it either. I tried to implement this feature in my own C# library but gave up for the same reason. If you read Chromium source code for Web MIDI support, you will find that it is quite complicated. If you are really serious about connection state change detection, run a loop like once in a few seconds to get list of devices and report the changes. That is ugly and I wouldn't implement such code for my library and wouldn't expect rtmidi to support that either... |
About Windows: One way to get notified whenever some MIDI port is added or removed is by taking a close look at And just for comparison: JUCE uses polling for changes to MIDI interfaces in its |
Just noticed that. Is there a way to access the underlying port/device in Linux? I'd want to use notify/select in order to mitigate it.
While I agree with that, my use case is a platform that is constantly connected to some device (say raspberry pi) and lets developers write React apps on top of that (midiate, very initial and WiP). So I would have to somehow restore the state automatically when the instrument shuts down and then reconnects, and all weird ways to do that caused me with ALSA |
I am attempting to deal with what happens when midi devices hot swap at runtime. I am building my own mapping of device id -> current port number so that if a device goes away and comes back I know it is the same device. The problem is I do not know when I need to rebuild my map. It is not clear at what times RTMidi's port view can change and I do not know how to detect a change except by iterating all ports and comparing the strings, which seems slightly wasteful.
I would like to request either some kind of a call like >havePortsChanged(), or a message that gets queued with midiin->getMessage which flags ports have changed. RTMidi is in a better position to determine when a port change has occurred than my client code is.
If this is not feasible, something that would help is better documentation around port changes. It would be very helpful to be able to know at what times ports can change or when I need to a port change. If I need to potentially be ready for ports to change between any two calls*, it would be useful to have that fact documented. Everything around ports is very unclear in the current docs, for example, although its usage is clear from the docs I cannot find an explanation anywhere of what openPort(0) actually does.
I am aware of #30 but I am slightly confused what to do with it. It looks like an entirely different fork with things like different build scripts and namespacing. This is a smaller, more attainable question. Since #30 has not been integrated over a period of five years maybe it is worth exploring smaller solutions.
* I did a test on OS X 10.13.6 which seems to indicate that port assignments can change at any time as if the change were occurring in a simultaneous thread. Perhaps this is the race condition other issues refer to. I ran:
If I run this I frequently see
This implies port 1 disappeared between midiIn.getPortCount() and midiIn.getPortName().
The text was updated successfully, but these errors were encountered: