Closed mat1jaczyyy closed 1 year ago
@micdah ?
@mat1jaczyyy Just took a look into rtmidi
to see if there are any promising handles we could expose through the C-interface we are using - but alas I cannot find anything useful.
As far as I can see, it is not implemented in rtmidi
- each platform might make available a system event about when midi devices are added/removed, but seems like it's not implemented. Might also be because such event is not consistently available on all platforms.
Would require to figure out how to receive those events via Windows MM and macOS Core as minimum, and at best including ALSA and Jack as well as these are the underlying system-specific API's used.
Of course one could do a poll-based solution as well, but that would require a timer or other thread-based entity doing a regular poll.
I think such a thing might be better placed in the application though, I wouldn't expect a library to start a background thread for doing a regular background poll.
Oh, so RtMidi asks the system as well when we ask it to enumerate the devices? If so, it's going to be a tricky thing to implement. For now, I'll stick to a 200ms Timer, should be enough. Thanks for the clarification
Is this worth making an issue about in the main rtmidi repository?
Just had a look in WinMM, to see if there were any Windows API's to support such an event, and I cannot seem to find anything: https://msdn.microsoft.com/en-us/library/windows/desktop/dd798495(v=vs.85).aspx
Might also be why there is nothing in rtmidi, because it's not widely supported or easily available
Okay. I know for sure Ableton does it on both Windows and macOS. Maybe they have a poll based solution as well. I will let you know how it works out for me in the end
After finishing other work, I finally got around to implementing a MIDI device management system with hot-plugging support (with a 200ms periodic Timer) on the application side.
While I believe my implementation should work okay, it actually doesn't work because it seems RtMidi.Core only gets the MIDI devices when the app is initalized - and never changes the info enumerables found in the MIDI manager. This means unplugging a currently connected device doesn't remove it from the list, and connecting a new device doesn't add it to the list - the enumerables never change.
Obviously, upon inspecting the source and debugging for a while, RtMidi.Core is asking RtMidi for a new port count and port names. It seems that the issue is in the underlying RtMidi library - it always returns the same port count and the same port names. Maybe we are somewhere forgetting to ask RtMidi to re-enumerate USB devices again? I'm not sure.
This severely limits my application and would be nice if it could be addressed as soon as possible. The original issue report is not important anymore, because an application-side implementation will most likely work properly to achieve behavior that is very close to the original report.
@micdah
After further inspection it seems the input/output device info enumerables properly update on Windows, but not on macOS which is my main working environment. I am targeting both operating systems.
That is strange, I have just tested this sample program:
while (true)
{
Log.Information("Found the following available devices");
foreach (var info in MidiDeviceManager.Default.InputDevices)
Log.Information(" {Name}", info.Name);
Thread.Sleep(TimeSpan.FromSeconds(2));
}
I ran it with no MIDI devices connected, and then connect a device, and proceeded to disconnect it again. This is the output.
2018-09-19 17:49:22 INF Found the following available devices
2018-09-19 17:49:23 DBG Creating default input device
2018-09-19 17:49:23 DBG Setting types to ignore
2018-09-19 17:49:23 DBG Setting input callback
2018-09-19 17:49:25 INF Found the following available devices
2018-09-19 17:49:27 INF Found the following available devices
2018-09-19 17:49:29 INF Found the following available devices
2018-09-19 17:49:29 INF "X-TOUCH MINI"
2018-09-19 17:49:31 INF Found the following available devices
2018-09-19 17:49:31 INF "X-TOUCH MINI"
2018-09-19 17:49:33 INF Found the following available devices
2018-09-19 17:49:35 INF Found the following available devices
Process finished with exit code 0.
I'm running from macOS 10.13.6 - running it via .Net Core 2.1.402.
For good meassure, I also tried compiling the program using mono-mdk
(5.14.0.177) and running the code - same output. So it doesn't seem to be related to the runtime either.
Used included nuget
and msbuild
binaries from mono-mdk to build the solution (msbuild /t:Publish
) and then simply ran using mono RtMidi.Core.Samples.dll
After testing your sample, seems like you're in the right. Must be I've messed up somewhere in my application. I'll have a look, sorry for the confusion.
Original issue about connection events could still be addressed at some point in the future so I'm leaving this open.
Looks like it was the Timer doing weird stuff. Instead I made the function async
and created a background thread for the using Task.Run
. I have my device checking code run in a while (true)
there with a Thread.Sleep
to not waste CPU resources. Not sure why, but that seems to work.
Here's the commit should you want to take a look inside.
It's me again! 😝 Been getting a weird segfault recently after idling my app for just a few minutes.
Pinpointed it to the thread where I'm checking for devices every 200ms. I guess this happens if the thread falls behind or something. Really no idea what it is about, but that seems to have eliminated that. I guess I'm falling back to manually rescanning device for the time being.
I'm really dumbfounded about what's actually going on, thought I'd just document it here.
@mat1jaczyyy We discussed having some timer-based polling on devices and firing an event when new devices become available - is this still something you would like to see included in RtMidi.Core?
As we discussed there doesn't seem to be any such event from the underlying rtmidi
library that we could make use of, so regular polling seems to be the only other option.
Would we be interested in disconnection events as well, i.e. when a device previously available becomes unavailable?
well, i do polling every 100ms in my app in a background thread right now, but i absolutely will segfault UNLESS i write something to the console in the timer event. i have no clue why. so my timer will output a simple "rescan" text into the console, and it works. this would be nice to have in the library, for sure though...
well, i do polling every 100ms in my app in a background thread right now, but i absolutely will segfault UNLESS i write something to the console in the timer event. i have no clue why. so my timer will output a simple "rescan" text into the console, and it works. this would be nice to have in the library, for sure though...
Looked into the commit you linked to earlier, assuming it's the code in there you are referring to.
Cannot see any obvious reason for a segfault either - although I did notice that you only call Refresh()
when calling Start()
once - so wouldn't the timer loop continouly look at the same old array of input and output devices?
The code might have changed since that commit of course, just something that popped into my eyes.
Another thought I had, is if the loop was a no-op, I'm not sure if C# might do some optimization. In general segfaults are rare in managed world :smile:
Check out https://github.com/mat1jaczyyy/apollo-studio/blob/master/Apollo/Core/MIDI.cs BTW, I no longer use RtMidi.Core directly, I stripped away all the stuff I didn't need like other MIDI messages as well as validation, for a slight performance boost (I send a LOT of midi, dozens of kB/s and USB is finnicky)
Hotplugging would be really nice to have as this is a much better UX (as an event, not polling), enumerating in runtime works, so the solution to have a timer is a workaround but it is always better to not eat resources for polling.
Closing issue as there is currently no available API which can provide an event on device connect/disconnect, so any solution would be based on polling - in which case I think it's better for the user application to decide which polling strategy to take (which interval, potential exponential backoff strategy etc.).
FROM CHATGPT 4
In CoreMIDI on Mac, there is an event that is triggered when a MIDI interface is connected or disconnected. This is handled through MIDI notifications. You can use MIDIClientCreate to create a MIDI client and then MIDINotification to receive notifications when MIDI devices are connected or disconnected.
The basic process would be something like this:
When a MIDI device is connected or disconnected, your callback function will be called with a notification indicating the type of event (such as kMIDIMsgObjectAdded or kMIDIMsgObjectRemoved). You can use this information to update the list of available MIDI devices in your application.
It's important to check the CoreMIDI documentation for specific details on how to implement these features and handle the different types of MIDI notifications.
For Linux, handling MIDI device connection and disconnection events can be done using both ALSA (Advanced Linux Sound Architecture) and JACK (Jack Audio Connection Kit). Here's an overview of how you can manage these events in both systems:
ALSA (Advanced Linux Sound Architecture)
JACK (Jack Audio Connection Kit)
Additional Notes:
Both ALSA and JACK are powerful tools for handling MIDI on Linux, and your choice between them may depend on the specific requirements of your application, such as whether you need advanced routing capabilities (JACK) or direct MIDI hardware control (ALSA).
On Windows, handling MIDI device connection and disconnection events in the Multimedia system involves using the Windows Multimedia API (WinMM). Here's a general outline of how you can manage these events:
Additional Considerations:
Direct support for MIDI device connection/disconnection events might vary depending on the version of Windows and its MIDI implementation. Using external libraries may be an option.
When a MIDI device is connected, its ports become available and can be checked for manually via
RtMidiDeviceManager
.I was trying to come up with a way to turn this into an event, so that I can listen for when a device is connected or disconnected (or, when a MIDI port appears or disappears from the list). Tracing the code, I realized the manual checks directly query RtMidi's available ports via
GetPortCount
andGetPortName
.I don't know if there is a different call in the RtMidi API that can, similarly to the input listener callback, listen to changes in available ports? The event doesn't even necessarily need to carry information of what's changed - we can simply ask for the list manually once the event is fired and our method executes.