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

Feature request: Detect/notify on port changes #191

Open
mcclure opened this issue Feb 24, 2019 · 12 comments
Open

Feature request: Detect/notify on port changes #191

mcclure opened this issue Feb 24, 2019 · 12 comments

Comments

@mcclure
Copy link
Contributor

mcclure commented Feb 24, 2019

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:

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().

@garyscavone
Copy link
Contributor

garyscavone commented Feb 24, 2019 via email

@mcclure
Copy link
Contributor Author

mcclure commented Feb 24, 2019

I'm sorry, I messed up typing the above, the error:

MidiInCore::getPortName: the 'portNumber' argument (1) is invalid

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.

@garyscavone
Copy link
Contributor

garyscavone commented Feb 25, 2019 via email

@mcclure
Copy link
Contributor Author

mcclure commented Feb 25, 2019

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.

@garyscavone
Copy link
Contributor

garyscavone commented Feb 26, 2019 via email

@mcclure
Copy link
Contributor Author

mcclure commented Feb 27, 2019

That helps!

@iankiller77
Copy link

iankiller77 commented Apr 5, 2019

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.

@SpotlightKid
Copy link
Contributor

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

@mcclure
Copy link
Contributor Author

mcclure commented Apr 14, 2019

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.

@atsushieno
Copy link
Contributor

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...

@v1ne
Copy link

v1ne commented Nov 13, 2019

About Windows: One way to get notified whenever some MIDI port is added or removed is by taking a close look at WM_DEVICECHANGE messages.

And just for comparison: JUCE uses polling for changes to MIDI interfaces in its AudioDeviceSelectorComponent, on all platforms. ;)

@thebne
Copy link

thebne commented Apr 5, 2020

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.

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...

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 cannot allocate memory messages or so.
That's why I think some libraries could benefit from some (even if partial/POSIX-specific) port change detection

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

7 participants