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

How to identify when a midi input device got disconnected? #35

Open
Boscop opened this issue Jan 16, 2018 · 14 comments
Open

How to identify when a midi input device got disconnected? #35

Boscop opened this issue Jan 16, 2018 · 14 comments

Comments

@Boscop
Copy link

Boscop commented Jan 16, 2018

How to identify when a midi input device got disconnected?
When I open the ports as described here.
Is it possible to check the MidiInputConnection if the device was disconnected?
My use case is, I want auto reconnect every few seconds if a midi device was disconnected.
I'm already successfully auto reconnecting output devices because they will give a SendError on send() if they are disconnected, but how to detect it for input devices? (on windows)

@Boddlnagg
Copy link
Owner

On WinRT this would be rather easy, but unfortunately at least the WinMM and the ALSA backend don't provide native ways to get device change notifications. I'd need to use other Windows resp. Linux OS APIs. In the long run I really want to have this, but it's quite a bit of work ...

@Boscop
Copy link
Author

Boscop commented Jan 16, 2018

Hm, I would already be happy if this only worked on windows (8.1+), maybe with an extension trait?

Btw, any idea how portmidi does it?
https://github.com/whentze/launch-rs/blob/master/lib/src/mk2.rs#L198

@Boddlnagg
Copy link
Owner

Boddlnagg commented Jan 16, 2018

I can see if I can come up with an extension trait similar to what's being done for virtual ports on non-Windows.

The implementation of poll() for input ports in PortMidi for WinMM seems to be this (see http://svn.code.sf.net/p/portmedia/code/portmidi/trunk/pm_win/pmwinmm.c):

static PmError winmm_in_poll(PmInternal *midi) {
    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
    return m->error;
}

So it's basically only returning an error if one has been set somewhere before, but I can't find a place in the code that sets m->error outside of the places where midir would report an error as well. If a device is disconnected, I guess that the input event callback will just not be called anymore, and that's quite hard to diagnose ... does PortMidi actually reliably identify this case?

@Bendrien
Copy link

Bendrien commented Sep 4, 2019

I'm already successfully auto reconnecting output devices because they will give a SendError on send() if they are disconnected

Unfortunately i dont get a SendError message ond send() of a MidiOutputConnection (linux).

Whats the current state of this?

@avindra
Copy link

avindra commented Dec 1, 2020

If I run aseqdump, this is the "event" that gets generated when I turn off the connected device:

  0:1   Port unsubscribed          16:0 -> 128:0

The 0:1 seems to indidicate that there is a special "root" port maybe, where these events go. I otherwise see the port (in my case 16) of the instrument:

Source  Event                  Ch  Data
 16:0   Note on                 0, note 77, velocity 43
 16:0   Note off                0, note 77

@agersant
Copy link

agersant commented Feb 7, 2021

While trying to find a way to identify midi input device disconnections (and before reading this ticket), I found this snippet in the documentation:

An error will be returned when the port is no longer valid (e.g. the respective device has been disconnected).

I tried using get_port_name to poll for availability but this seems to always return a valid name, even after unplugging the corresponding device. Does this behavior differ per backend or is it simply an error in the documentation?
(tested on Windows 10 w/ MSVC toolchain, default backend)

As a side note, I wonder if a feasible solution would be for MidiInputPort to implement PartialEq so that it's possible to identify a previously seen port within the list exposed by MidiInput.

@Boddlnagg
Copy link
Owner

@agersant Actually, this is specific to the default Windows backend ... after #38 the port name for that backend is stored in the port identifier itself, so there's no call back to a Windows API when port_name() is called. This means that unfortunately the documentation is currently wrong.

I'm not sure whether the implementation or the documentation should be changed ...

@Boscop
Copy link
Author

Boscop commented Feb 8, 2021

I think it makes sense to change the implementation of port_name to check if the port of that name still exists.
So that we have a way to check for disconnect periodically.

@Boscop
Copy link
Author

Boscop commented Apr 11, 2021

@Boddlnagg I'm using this function to open a midi port:

pub fn open_input(
	port_name: &str,
	idx: Option<usize>,
) -> Result<(MidiInputConnection<()>, Receiver<LowLvlMsg>)> {
	let midi = MidiInput::new("")?;
	let port = midi
		.ports()
		.into_iter()
		.filter_map(|port| midi.port_name(&port).ok().map(|name| (port, name)))
		.filter_map(|(port, name)| (name == port_name).then_some(port))
		.nth(idx.unwrap_or(0))
		.ok_or_else(|| anyhow!("midi input port '{}' not found", port_name))?;

	let (tx_midi, rx_midi) = channel();
	Ok((
		midi.connect(
			&port,
			"",
			move |_stamp, msg, _| {
				if let Some(msg) = LowLvlMsg::parse(msg) {
					tx_midi.send(msg).unwrap();
				}
			},
			(),
		)
		.map_err(|e| anyhow!("midi connect: {}", e))?,
		rx_midi,
	))
}

I thought I could just detect if my input device got disconnected by checking if rx_midi.try_recv() returns Err(TryRecvError::Disconnected), because it would do that if the sender (tx_midi) was dropped, which would happen when the closure passed to MidiInput::connect gets dropped.
But rx_midi.try_recv() never returns Err(TryRecvError::Disconnected) even when I unplug the device.
Why isn't the closure dropped when the midi device got disconnected?

@Boddlnagg
Copy link
Owner

Boddlnagg commented Apr 11, 2021

Probably the underlying thread that receives the MIDI events just continues to run (just won't receive events anymore) even if the device is no longer available 😕

Seems like other people also have problem with that functionality in RtMidi (which midir is based upon): micdah/RtMidi.Core#18.

This is also related to #78, because even a polling solution wouldn't currently work on macOS ...

@Boscop: You are using the default Windows backend (WinMM), right? (just so I can focus my efforts on that platform first)

@Boscop
Copy link
Author

Boscop commented Apr 14, 2021

Yes, I'm using midir on Windows.

@staurosKaragiannis
Copy link

Hello, did you find a solution to your problem? if so then it would be nice to post it here and close this issue 😄

@Boscop
Copy link
Author

Boscop commented Dec 19, 2022

@staurosKaragiannis Not yet.

@shallowclouds
Copy link

Any solutions guys? I really need this. But after looking through this issue comments from 2018 to 2022, it seems hard to be solved.😭

Do we have another rust crate implemented this?

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