Skip to content

API function to get macOS LocationID and Windows ContainerID of an USB device #107

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

Closed
JoergAtGithub opened this issue Apr 4, 2024 · 19 comments

Comments

@JoergAtGithub
Copy link

For compound USB devices, that have not only a MIDI interface, but interfaces used with other USB device classes (e.g. a display) as well, it is needed to group them. The operating systems macOS and Windows provide identifiers for this purpose:

  • macOS: LocationID
  • Windows: ContainerID
    It would be nice, if the libremidi API would provide getters, to get these IDs for an USB device.
@jcelerier
Copy link
Member

The operating systems macOS and Windows provide identifiers for this purpose:

Do you have some sample code of how to do this ?

@JoergAtGithub
Copy link
Author

JoergAtGithub commented Apr 5, 2024

@jcelerier
Copy link
Member

Thanks. Now I have to find how I get these device nodes in MS Windows, as right now the code does not use these APIs AFAIK.
For macOS it looks pretty doable.

@jcelerier
Copy link
Member

jcelerier commented Aug 25, 2024

I'm revisiting this and am wondering: port_information already stores the MIDIObjectRef's UUID so you can get it back in the following way (typing this without access to macOS so likely there are some mistakes but you get the gist):

int32_t usb_id_from_port(const libremidi::port_information& info) {
  // Get the MIDI object from the uid
  auto uid = std::bit_cast<std::int32_t>((uint32_t)info.port);
  MIDIObjectRef object{};
  MIDIObjectType type{};
  auto ret = MIDIObjectFindByUniqueID(uid, &object, &type);
  assert(type == kMIDIObjectType_Source || type == kMIDIObjectType_Destination);

  // Get the MIDI entity from the object
  MIDIEntityRef entity{};
  MIDIEndpointGetEntity(object, &entity); 
  if(!entity) 
    throw; // It means it's not a hardware port

  // Get the MIDI device from the entity
  MIDIDeviceRef device{};
  MIDIEntityGetDevice(entity, &device);
  if(!device)
    throw;

  SInt32 res{};
  MIDIObjectGetIntegerProperty(device, CFSTR("USBDeviceID"), &res);
  return res;
}

For Windows I investigated but I see absolutely no way to get the device ID from the WinMM API. :/

@JoergAtGithub
Copy link
Author

For Windows I investigated but I see absolutely no way to get the device ID from the WinMM API. :/

What about this approach:

#include <windows.h>
#include <setupapi.h>
#include <devpkey.h>
#include <iostream>
#include <string>
#include <initguid.h>

#pragma comment(lib, "setupapi.lib")

void CheckMidiDevices() {
    // Get the device information set for all MIDI devices
    HDEVINFO deviceInfoSet = SetupDiGetClassDevs(NULL, "SWD\\MMDEVAPI\\{0.0.1.00000000}.{00000000-0000-0000-0000-000000000000}", NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES);
    if (deviceInfoSet == INVALID_HANDLE_VALUE) {
        std::cerr << "Failed to get device information set." << std::endl;
        return;
    }

    SP_DEVINFO_DATA deviceInfoData;
    deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);

    // Enumerate through all devices in the set
    for (DWORD i = 0; SetupDiEnumDeviceInfo(deviceInfoSet, i, &deviceInfoData); ++i) {
        // Get the device instance ID
        char deviceInstanceId[MAX_DEVICE_ID_LEN];
        if (CM_Get_Device_ID(deviceInfoData.DevInst, deviceInstanceId, MAX_DEVICE_ID_LEN, 0) != CR_SUCCESS) {
            continue;
        }

        // Get the device description
        char deviceDescription[256];
        if (SetupDiGetDeviceRegistryProperty(deviceInfoSet, &deviceInfoData, SPDRP_DEVICEDESC, NULL, (PBYTE)deviceDescription, sizeof(deviceDescription), NULL)) {
            std::cout << "Device: " << deviceDescription << std::endl;
        }

        // Get the ContainerID
        DEVPROPTYPE propType;
        GUID containerId;
        if (SetupDiGetDeviceProperty(deviceInfoSet, &deviceInfoData, &DEVPKEY_Device_ContainerId, &propType, (PBYTE)&containerId, sizeof(containerId), NULL, 0)) {
            LPOLESTR guidString;
            StringFromCLSID(containerId, &guidString);
            std::wcout << L"ContainerID: " << guidString << std::endl;
            CoTaskMemFree(guidString);
        }

        // Get the device's parent to determine the connection type
        DEVINST parentDevInst;
        if (CM_Get_Parent(&parentDevInst, deviceInfoData.DevInst, 0) == CR_SUCCESS) {
            char parentDeviceInstanceId[MAX_DEVICE_ID_LEN];
            if (CM_Get_Device_ID(parentDevInst, parentDeviceInstanceId, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS) {
                // Check if the parent device is a USB or Bluetooth device
                if (strstr(parentDeviceInstanceId, "USB") != NULL) {
                    std::cout << "Connection Type: USB" << std::endl;
                } else if (strstr(parentDeviceInstanceId, "BTH") != NULL) {
                    std::cout << "Connection Type: Bluetooth" << std::endl;
                } else {
                    std::cout << "Connection Type: Other" << std::endl;
                }
            }
        }
    }

    // Clean up
    SetupDiDestroyDeviceInfoList(deviceInfoSet);
}

int main() {
    CheckMidiDevices();
    return 0;
}

@jcelerier
Copy link
Member

hm but do we know for sure that the index of

for (DWORD i = 0; SetupDiEnumDeviceInfo(deviceInfoSet, i, &deviceInfoData); ++i) 

is the same index than the one used by WinMM to enumerate MIDI devices ?

@jcelerier
Copy link
Member

especially in "borderline" cases where we plug multiple identical MIDI devices which have historically been iffy in winmm

@JoergAtGithub
Copy link
Author

I don't know unfortunately.
But in general the purpose of the ContainerId is to handle these "iffy" devices, multi functional(e.g. MIDI and screens) but without proper serial number or with multiple physical interfaces.
The most common use case for the ContainerId is a multifunctional printer/scanner connected via USB and WLAN at the same time. The ContainerId groups everything and ensures, that it appears as one device.

@jcelerier
Copy link
Member

jcelerier commented Mar 11, 2025

hmm so I've been trying to add the windows code you sent but here it doesn't work:

HDEVINFO deviceInfoSet = SetupDiGetClassDevs(NULL, "SWD\\MMDEVAPI\\{0.0.1.00000000}.{00000000-0000-0000-0000-000000000000}", NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES);

gives me the same error than here: https://stackoverflow.com/questions/14288626/obtain-device-information-set-for-monitors-returned-handle-is-always-invalid-ha

I tried to be more general and ask for every MMDEVAPI device:

// seen online, didn't work:  
HDEVINFO deviceInfoSet = SetupDiGetClassDevs(NULL, "SWD\\MMDEVAPI\\MIDII*", NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES);

// more general:
HDEVINFO deviceInfoSet = SetupDiGetClassDevs(NULL, "SWD\\MMDEVAPI", NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES);

but the reported devices don't match the ones I get from midiInGetDevCaps / midiOutGetDevCaps - it contains audio devices, and does not contain every midi device.
I even tried listing every single device with:

HDEVINFO deviceInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES);

but for instance this does not list my RME Multiface II's MIDI inputs.
Here's for instance the list I get from `SetupDiGetClassDevs(NULL, "SWD\MMDEVAPI":


Checking device 0
SPDRP_DEVICEDESC: Generic software device
SPDRP_COMPATIBLEIDS: SWD\GenericRaw
SPDRP_CLASS: SoftwareDevice
SPDRP_CLASSGUID: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}
SPDRP_DRIVER: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}\0049
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: Capture
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\0000006f
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {00000000-0000-0000-FFFF-FFFFFFFFFFFF}
Connection Type: Other

Checking device 1
SPDRP_DEVICEDESC: Generic software device
SPDRP_COMPATIBLEIDS: SWD\GenericRaw
SPDRP_CLASS: SoftwareDevice
SPDRP_CLASSGUID: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}
SPDRP_DRIVER: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}\0050
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: Render
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\00000070
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {00000000-0000-0000-FFFF-FFFFFFFFFFFF}
Connection Type: Other

Checking device 2
SPDRP_DEVICEDESC: Audio Endpoint
SPDRP_HARDWAREID: MMDEVAPI\AudioEndpoints
SPDRP_COMPATIBLEIDS: GenericAudioEndpoint
SPDRP_CLASS: AudioEndpoint
SPDRP_CLASSGUID: {c166523c-fe0c-4a94-a586-f1a80cfbbf3e}
SPDRP_DRIVER: {c166523c-fe0c-4a94-a586-f1a80cfbbf3e}\0001
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: Speakers (Steam Streaming Microphone)
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\00000069
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {00000000-0000-0000-FFFF-FFFFFFFFFFFF}
Connection Type: Other

Checking device 3
SPDRP_DEVICEDESC: Audio Endpoint
SPDRP_HARDWAREID: MMDEVAPI\AudioEndpoints
SPDRP_COMPATIBLEIDS: GenericAudioEndpoint
SPDRP_CLASS: AudioEndpoint
SPDRP_CLASSGUID: {c166523c-fe0c-4a94-a586-f1a80cfbbf3e}
SPDRP_DRIVER: {c166523c-fe0c-4a94-a586-f1a80cfbbf3e}\0007
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: XB323U (NVIDIA High Definition Audio)
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\0000006b
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {056C1622-5B17-5987-A678-812D3C84FE5C}
Connection Type: Other

Checking device 4
SPDRP_DEVICEDESC: Audio Endpoint
SPDRP_HARDWAREID: MMDEVAPI\AudioEndpoints
SPDRP_COMPATIBLEIDS: GenericAudioEndpoint
SPDRP_CLASS: AudioEndpoint
SPDRP_CLASSGUID: {c166523c-fe0c-4a94-a586-f1a80cfbbf3e}
SPDRP_DRIVER: {c166523c-fe0c-4a94-a586-f1a80cfbbf3e}\0020
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: Speakers (RME Hammerfall DSP)
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\0000006c
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {00000000-0000-0000-FFFF-FFFFFFFFFFFF}
Connection Type: Other

Checking device 5
SPDRP_DEVICEDESC: Generic software device
SPDRP_COMPATIBLEIDS: SWD\GenericRaw
SPDRP_CLASS: SoftwareDevice
SPDRP_CLASSGUID: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}
SPDRP_DRIVER: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}\0005
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: MIDI
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\00000072
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {00000000-0000-0000-FFFF-FFFFFFFFFFFF}
Connection Type: Other

Checking device 6
SPDRP_DEVICEDESC: Generic software device
SPDRP_COMPATIBLEIDS: SWD\GenericRaw
SPDRP_CLASS: SoftwareDevice
SPDRP_CLASSGUID: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}
SPDRP_DRIVER: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}\0006
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: MIDI
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\00000073
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {00000000-0000-0000-FFFF-FFFFFFFFFFFF}
Connection Type: Other

Checking device 7
SPDRP_DEVICEDESC: Audio Endpoint
SPDRP_HARDWAREID: MMDEVAPI\AudioEndpoints
SPDRP_COMPATIBLEIDS: GenericAudioEndpoint
SPDRP_CLASS: AudioEndpoint
SPDRP_CLASSGUID: {c166523c-fe0c-4a94-a586-f1a80cfbbf3e}
SPDRP_DRIVER: {c166523c-fe0c-4a94-a586-f1a80cfbbf3e}\0011
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: Microphone (Steam Streaming Microphone)
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\0000006d
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {00000000-0000-0000-FFFF-FFFFFFFFFFFF}
Connection Type: Other

Checking device 8
SPDRP_DEVICEDESC: Audio Endpoint
SPDRP_HARDWAREID: MMDEVAPI\AudioEndpoints
SPDRP_COMPATIBLEIDS: GenericAudioEndpoint
SPDRP_CLASS: AudioEndpoint
SPDRP_CLASSGUID: {c166523c-fe0c-4a94-a586-f1a80cfbbf3e}
SPDRP_DRIVER: {c166523c-fe0c-4a94-a586-f1a80cfbbf3e}\0021
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: Analog (1+2) (RME Hammerfall DSP)
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\0000006e
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {00000000-0000-0000-FFFF-FFFFFFFFFFFF}
Connection Type: Other

Checking device 9
SPDRP_DEVICEDESC: Generic software device
SPDRP_COMPATIBLEIDS: SWD\GenericRaw
SPDRP_CLASS: SoftwareDevice
SPDRP_CLASSGUID: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}
SPDRP_DRIVER: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}\0000
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: Microsoft GS Wavetable Synth
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\0000007c
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {00000000-0000-0000-FFFF-FFFFFFFFFFFF}
Connection Type: Other

Checking device 10
SPDRP_DEVICEDESC: Audio Endpoint
SPDRP_HARDWAREID: MMDEVAPI\AudioEndpoints
SPDRP_COMPATIBLEIDS: GenericAudioEndpoint
SPDRP_CLASS: AudioEndpoint
SPDRP_CLASSGUID: {c166523c-fe0c-4a94-a586-f1a80cfbbf3e}
SPDRP_DRIVER: {c166523c-fe0c-4a94-a586-f1a80cfbbf3e}\0000
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: Speakers (Steam Streaming Speakers)
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\0000006a
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {00000000-0000-0000-FFFF-FFFFFFFFFFFF}
Connection Type: Other

Checking device 11
SPDRP_DEVICEDESC: Generic software device
SPDRP_COMPATIBLEIDS: SWD\GenericRaw
SPDRP_CLASS: SoftwareDevice
SPDRP_CLASSGUID: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}
SPDRP_DRIVER: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}\0065
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: 2 - BCF2000 [0]
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\00000095
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {584BBCCF-F82E-11EF-8393-2C56DC395547}
Connection Type: USB

Checking device 12
SPDRP_DEVICEDESC: Generic software device
SPDRP_COMPATIBLEIDS: SWD\GenericRaw
SPDRP_CLASS: SoftwareDevice
SPDRP_CLASSGUID: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}
SPDRP_DRIVER: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}\0066
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: 2 - BCF2000 [1]
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\00000096
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {584BBCCF-F82E-11EF-8393-2C56DC395547}
Connection Type: USB

Checking device 13
SPDRP_DEVICEDESC: Generic software device
SPDRP_COMPATIBLEIDS: SWD\GenericRaw
SPDRP_CLASS: SoftwareDevice
SPDRP_CLASSGUID: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}
SPDRP_DRIVER: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}\0067
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: 2 - BCF2000 [2]
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\00000097
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {584BBCCF-F82E-11EF-8393-2C56DC395547}
Connection Type: USB

Checking device 14
SPDRP_DEVICEDESC: Generic software device
SPDRP_COMPATIBLEIDS: SWD\GenericRaw
SPDRP_CLASS: SoftwareDevice
SPDRP_CLASSGUID: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}
SPDRP_DRIVER: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}\0068
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: 2 - BCF2000 [3]
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\00000098
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {584BBCCF-F82E-11EF-8393-2C56DC395547}
Connection Type: USB

Checking device 15
SPDRP_DEVICEDESC: Generic software device
SPDRP_COMPATIBLEIDS: SWD\GenericRaw
SPDRP_CLASS: SoftwareDevice
SPDRP_CLASSGUID: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}
SPDRP_DRIVER: {62f9c741-b25a-46ce-b54c-9bccce08b6f2}\0074
SPDRP_CONFIGFLAGS: 
SPDRP_MFG: Microsoft
SPDRP_FRIENDLYNAME: 2 - BCF2000 [4]
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\00000099
SPDRP_ENUMERATOR_NAME: SWD
ContainerID: {584BBCCF-F82E-11EF-8393-2C56DC395547}
Connection Type: USB

and the ports in the order reported by winmm:

3 MIDI input sources:
 - 0: [ client: 0, port: 0, portname: Multiface Midi, display: Multiface Midi 1]
 - 1: [ client: 0, port: 1, portname: BCF2000, display: BCF2000 1]
 - 2: [ client: 0, port: 2, portname: MIDIIN2 (BCF2000), display: MIDIIN2 (BCF2000) 1]
5 MIDI output sinks:
 - 0: [ client: 0, port: 0, portname: Microsoft GS Wavetable Synth, display: Microsoft GS Wavetable Synth 1]
 - 1: [ client: 0, port: 1, portname: Multiface Midi, display: Multiface Midi 1]
 - 2: [ client: 0, port: 2, portname: BCF2000, display: BCF2000 1]
 - 3: [ client: 0, port: 3, portname: MIDIOUT2 (BCF2000), display: MIDIOUT2 (BCF2000) 1]
 - 4: [ client: 0, port: 4, portname: MIDIOUT3 (BCF2000), display: MIDIOUT3 (BCF2000) 1]

Do you have any idea ?

@jcelerier
Copy link
Member

the good thing though is that the more recent Windows MIDI API, Windows MIDI Services, directly gives access to the container ID, so I'll be adding it to the API anyways

@jcelerier
Copy link
Member

done in HEAD :)

here's the API, the info will just be provided by default as part of port_information:

https://github.com/celtera/libremidi/blob/master/include/libremidi/observer_configuration.hpp#L53

@jcelerier
Copy link
Member

jcelerier commented Mar 12, 2025

I also added an enum that gives some info about what kind of port this is (hardware, software etc)

@JoergAtGithub
Copy link
Author

Great! Thank you very much for implementing this! This provides us on the MIDI side with the basis for hotplugging complex USB devices using multiple USB protocol classes on different interfaces.
It's a shame that it doesn't work with WinMM. But maybe sometime in the future someone will have a better idea of how to implement it than I had.

@jcelerier
Copy link
Member

yep, all the infrastructure is there to add it to any backend :)
next step for me would be to have some high-level abstraction that more closely matches the "one device with multiple ports" concept of MIDI 2 - i started this in client.{h/c}pp but not too satisfied with the abstraction so far..

@jcelerier
Copy link
Member

just added the implementation for ALSA based on what useful information I could gather from udev (although the ALSA card was already accessible in a way):

Displaying ports for: ALSA (sequencer)
4 MIDI input sources:
 - 0: [ client: 103331089176480, port: 14, device_name: Midi Through, port_name: Midi Through Port-0, display_name: Midi Through Port-0, type: software]
 - 1: [ client: 103331089176480, container: pci-0000:02:00.0, device_id: /sys/devices/pci0000:00/0000:00:02.2/0000:02:00.0/sound/card0/controlC0, port: 16, device_name: Hammerfall DSP, port_name: HDSP MIDI 1, display_name: HDSP MIDI 1, type: hardware, pci]
 - 2: [ client: 103331089176480, container: pci-0000:00:14.0-usb-0:12:1.0, device_id: /sys/devices/pci0000:00/0000:00:14.0/usb1/1-12/1-12:1.0/sound/card1/controlC1, port: 20, device_name: BCF2000, port_name: BCF2000 MIDI 1, display_name: BCF2000 MIDI 1, type: hardware, usb]
 - 3: [ client: 103331089176480, container: pci-0000:00:14.0-usb-0:12:1.0, device_id: /sys/devices/pci0000:00/0000:00:14.0/usb1/1-12/1-12:1.0/sound/card1/controlC1, port: 4294967316, device_name: BCF2000, port_name: BCF2000 MIDI 2, display_name: BCF2000 MIDI 2, type: hardware, usb]
6 MIDI output sinks:
 - 0: [ client: 103331089176480, port: 14, device_name: Midi Through, port_name: Midi Through Port-0, display_name: Midi Through Port-0, type: software]
 - 1: [ client: 103331089176480, container: pci-0000:02:00.0, device_id: /sys/devices/pci0000:00/0000:00:02.2/0000:02:00.0/sound/card0/controlC0, port: 16, device_name: Hammerfall DSP, port_name: HDSP MIDI 1, display_name: HDSP MIDI 1, type: hardware, pci]
 - 2: [ client: 103331089176480, container: pci-0000:00:14.0-usb-0:12:1.0, device_id: /sys/devices/pci0000:00/0000:00:14.0/usb1/1-12/1-12:1.0/sound/card1/controlC1, port: 20, device_name: BCF2000, port_name: BCF2000 MIDI 1, display_name: BCF2000 MIDI 1, type: hardware, usb]
 - 3: [ client: 103331089176480, container: pci-0000:00:14.0-usb-0:12:1.0, device_id: /sys/devices/pci0000:00/0000:00:14.0/usb1/1-12/1-12:1.0/sound/card1/controlC1, port: 4294967316, device_name: BCF2000, port_name: BCF2000 MIDI 2, display_name: BCF2000 MIDI 2, type: hardware, usb]
 - 4: [ client: 103331089176480, container: pci-0000:00:14.0-usb-0:12:1.0, device_id: /sys/devices/pci0000:00/0000:00:14.0/usb1/1-12/1-12:1.0/sound/card1/controlC1, port: 8589934612, device_name: BCF2000, port_name: BCF2000 MIDI 3, display_name: BCF2000 MIDI 3, type: hardware, usb]
 - 5: [ client: 103331089176480, port: 130, device_name: Client-130, port_name: qpwgraph_alsamidi, display_name: qpwgraph_alsamidi, type: software]

Displaying ports for: ALSA (raw)
3 MIDI input sources:
 - 0: [ client: 0, container: pci-0000:02:00.0, device_id: /sys/devices/pci0000:00/0000:00:02.2/0000:02:00.0/sound/card0/controlC0, port: 0, manufacturer: Hammerfall DSP, device_name: HDSP MIDI 1, display_name: HDSP MIDI 1, type: hardware, pci]
 - 1: [ client: 0, container: pci-0000:00:14.0-usb-0:12:1.0, device_id: /sys/devices/pci0000:00/0000:00:14.0/usb1/1-12/1-12:1.0/sound/card1/controlC1, port: 1, manufacturer: BCF2000, device_name: BCF2000, port_name: BCF2000 MIDI 1, display_name: BCF2000 MIDI 1, type: hardware, usb]
 - 2: [ client: 0, container: pci-0000:00:14.0-usb-0:12:1.0, device_id: /sys/devices/pci0000:00/0000:00:14.0/usb1/1-12/1-12:1.0/sound/card1/controlC1, port: 4294967297, manufacturer: BCF2000, device_name: BCF2000, port_name: BCF2000 MIDI 2, display_name: BCF2000 MIDI 2, type: hardware, usb]
4 MIDI output sinks:
 - 0: [ client: 0, container: pci-0000:02:00.0, device_id: /sys/devices/pci0000:00/0000:00:02.2/0000:02:00.0/sound/card0/controlC0, port: 0, manufacturer: Hammerfall DSP, device_name: HDSP MIDI 1, display_name: HDSP MIDI 1, type: hardware, pci]
 - 1: [ client: 0, container: pci-0000:00:14.0-usb-0:12:1.0, device_id: /sys/devices/pci0000:00/0000:00:14.0/usb1/1-12/1-12:1.0/sound/card1/controlC1, port: 1, manufacturer: BCF2000, device_name: BCF2000, port_name: BCF2000 MIDI 1, display_name: BCF2000 MIDI 1, type: hardware, usb]
 - 2: [ client: 0, container: pci-0000:00:14.0-usb-0:12:1.0, device_id: /sys/devices/pci0000:00/0000:00:14.0/usb1/1-12/1-12:1.0/sound/card1/controlC1, port: 4294967297, manufacturer: BCF2000, device_name: BCF2000, port_name: BCF2000 MIDI 2, display_name: BCF2000 MIDI 2, type: hardware, usb]
 - 3: [ client: 0, container: pci-0000:00:14.0-usb-0:12:1.0, device_id: /sys/devices/pci0000:00/0000:00:14.0/usb1/1-12/1-12:1.0/sound/card1/controlC1, port: 8589934593, manufacturer: BCF2000, device_name: BCF2000, port_name: BCF2000 MIDI 3, display_name: BCF2000 MIDI 3, type: hardware, usb]

@JoergAtGithub
Copy link
Author

I don't think that "pci-0000:00:14.0-usb-0:12:1.0" is compareable in functionality to ContainerID or LocationID. The later are containers that group everything that belongs to a device, independent of the physical interface. Especially relevant in case of devices with multiple physical interfaces for the same functionality, e.g. a printer with LAN, WLAN and USB will have one single ContainerID or LocationID and this stays the same when switching from WLAN to USB. And if you have two of these printers connected, you will be able to distinguish by ContainerID or LocationID, even if they have excatly the same type.
Unfortunately I'm not very familar with Linux and can't tell you what is the equivalent to ContainerID and LocationID on this OS.

@jcelerier
Copy link
Member

jcelerier commented Mar 16, 2025

hmm I see. I think none of the devices I have support this to try and see what's useful :/ do you know of some MIDI-related device that exhibits this functionality ?

From the MSDN docs: "The manufacturer of the USB device specifies the ContainerID in the device's firmware by using a Microsoft OS ContainerID descriptor.
The Microsoft USB hub driver automatically creates a ContainerID for the device from the combination of the device's product ID (PID), vendor ID (VID), revision number, and serial number. " - all the midi devices I could plug only exhibit a pid / vid for instance

@jcelerier
Copy link
Member

Also it seems to be pretty different from macOS's location ID, which is not related to the device but changes every time the device is unplugged / replugged

@JoergAtGithub
Copy link
Author

I don't have such a device, but I'm thinking about MIDI devices like the Hercules Universal DJ ( https://support.hercules.com/en/product/universaldj-en/ ) which can use MIDI over USB and MIDI over Bluetooth at the same time.

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

2 participants