thestk / rtmidi

A set of C++ classes that provide a common API for realtime MIDI input/output across Linux (ALSA & JACK), Macintosh OS X (CoreMIDI) and Windows (Multimedia)
Other
986 stars 274 forks source link

RtMidi is not able to see PureData #107

Closed jcelerier closed 7 years ago

jcelerier commented 7 years ago

In the software I work on I couldn't see Pd's virtual ports (Linux ALSA). So I tested with the Python mido library which is a wrapper over RtMidi and PortMidi, and it does not see them either. However, mido with the PortMidi backend is able to see the Pd ports.

import rtmidi
import mido

portmidi = mido.Backend('mido.backends.portmidi')
rtmidi = mido.Backend('mido.backends.rtmidi')

rtmidi.get_input_names()
> ['KMidimon:KMidimon 128:0', 'Midi Through:Midi Through Port-0 14:0']

portmidi.get_input_names()
> ['KMidimon', 'Midi Through Port-0', 'Pure Data Midi-Out 1', 'Pure Data Midi-Out 2']
radarsat1 commented 7 years ago

It seems worth noting that PureData users PortMidi as well, so it's not super surprising that the portmidi backend is compatible. Also, from a glance at RtMidi.cpp, it appears there are two separate functions for normal ports (MidiInAlsa::openPort) and virtual ports (MidiInAlsa::openVirtualPort), so I assume that has something to do with it.

When running PureData on my Ubuntu laptop, with 2 open virtual midi ports, here is some output of relevant commands:

rtmidi's midiproble,

$ ./midiprobe 

Compiled APIs:
  Linux ALSA

Current input API: Linux ALSA

There are 1 MIDI input sources available.
  Input Port #1: Midi Through 14:0

Current output API: Linux ALSA

There are 1 MIDI output ports available.
  Output Port #1: Midi Through 14:0

alsa midi utility,

$ amidi -l
Dir Device    Name

aconnect,

$ aconnect -l
client 0: 'System' [type=kernel]
    0 'Timer           '
    1 'Announce        '
client 14: 'Midi Through' [type=kernel]
    0 'Midi Through Port-0'
client 128: 'Pure Data' [type=user]
    0 'Pure Data Midi-In 1'
    1 'Pure Data Midi-In 2'
    2 'Pure Data Midi-Out 1'
    3 'Pure Data Midi-Out 2'

So, it would appear that RtMidi is not showing the "type=user" ports.

radarsat1 commented 7 years ago

The function used to get port information is called portInfo() in RtMidi.cpp. (It should really be static..)

Anyways it contains a predicate that causes it specifically to skip "synth" ports,

      if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) &&                         
        ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) ) continue;   

If I comment this out, I get the following output from midiprobe,

$ ./midiprobe 

Compiled APIs:
  Linux ALSA

Current input API: Linux ALSA

There are 3 MIDI input sources available.
  Input Port #1: Midi Through 14:0
  Input Port #2: Pure Data 128:2
  Input Port #3: Pure Data 128:3

Current output API: Linux ALSA

There are 3 MIDI output ports available.
  Output Port #1: Midi Through 14:0
  Output Port #2: Pure Data 128:0
  Output Port #3: Pure Data 128:1

I am not entirely sure what the motivation for this filtering predicate is, so I hesitate to change it. Perhaps @garyscavone can comment? I think the idea actually is to specifically restrict the list to hardware ports, but I am not sure why.

jcelerier commented 7 years ago

this was apparently added in this commit: https://github.com/thestk/rtmidi/commit/28321c051ed849b0fbd1f7b1aaee19b8ca807390 so there must have been a good reason :p

jcelerier commented 7 years ago

Apparently, the flag means:

This port understands SND_SEQ_EVENT_SAMPLE_xxx messages (these are not MIDI messages)

jcelerier commented 7 years ago

Sorry, this was dumb: just removing the second flag does not change anything. Removing the whole check however seems to work for me.

jcelerier commented 7 years ago

When printing all flags, it seems that the only flag set for puredata is :

SND_SEQ_PORT_TYPE_APPLICATION


      std::cerr << snd_seq_client_info_get_name(cinfo) << ": "
              << " " << bool( atyp & SND_SEQ_PORT_TYPE_SPECIFIC )
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC )
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_MIDI_GM )
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_MIDI_GS )
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_MIDI_XG )
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_MIDI_MT32 )
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_MIDI_GM2 )
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_SYNTH )
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_DIRECT_SAMPLE )
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_SAMPLE )
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_HARDWARE)
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_SOFTWARE)
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_SYNTHESIZER)
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_PORT)
             << " " << bool( atyp & SND_SEQ_PORT_TYPE_APPLICATION)
             << std::endl;

==>

Pure Data: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1

jcelerier commented 7 years ago

Interestingly, portmidi seems to set both APPLICATION and GENERIC:

https://github.com/pure-data/pure-data/blob/master/portmidi/portmidi/pm_linux/pmlinuxalsa.c#L143

garyscavone commented 7 years ago

I think that code snippet was intended to filter out all MIDI port types other than GENERIC and SYNTHs (rather than skip “synth” ports). But the documentation for ALSA has always been a bit limited and perhaps the library behaviour has changed over time too. So, perhaps someone can explore what the most appropriate flags should be and submit a pr.

On Apr 21, 2017, at 10:24 AM, Stephen Sinclair notifications@github.com wrote:

The function used to get port information is called portInfo() in RtMidi.cpp. (It should really be static..)

Anyways it contains a predicate that causes it specifically to skip "synth" ports,

  if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) &&                         
    ( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) ) continue;   

If I comment this out, I get the following output from midiprobe,

$ ./midiprobe

Compiled APIs: Linux ALSA

Current input API: Linux ALSA

There are 3 MIDI input sources available. Input Port #1: Midi Through 14:0 Input Port #2: Pure Data 128:2 Input Port #3: Pure Data 128:3

Current output API: Linux ALSA

There are 3 MIDI output ports available. Output Port #1: Midi Through 14:0 Output Port #2: Pure Data 128:0 Output Port #3: Pure Data 128:1

I am not entirely sure what the motivation for this filtering predicate is, so I hesitate to change it. Perhaps @garyscavone can comment? I think the idea actually is to specifically restrict the list to hardware ports, but I am not sure why.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

{"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://cloud.githubusercontent.com/assets/143418/17495839/a5054eac-5d88-11e6-95fc-7290892c7bb5.png","avatar_image_url":"https://cloud.githubusercontent.com/assets/143418/15842166/7c72db34-2c0b-11e6-9aed-b52498112777.png","action":{"name":"Open in GitHub","url":"https://github.com/thestk/rtmidi"}},"updates":{"snippets":[{"icon":"PERSON","message":"@radarsat1 in #107: The function used to get port information is called portInfo() in RtMidi.cpp. (It should really be static..)\r\n\r\nAnyways it contains a predicate that causes it specifically to skip \"synth\" ports,\r\n~\r\n if ( ( ( atyp \u0026 SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) \u0026\u0026 \r\n ( ( atyp \u0026 SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) ) continue; \r\n~\r\n\r\nIf I comment this out, I get the following output from midiprobe,\r\n\r\n~\r\n$ ./midiprobe \r\n\r\nCompiled APIs:\r\n Linux ALSA\r\n\r\nCurrent input API: Linux ALSA\r\n\r\nThere are 3 MIDI input sources available.\r\n Input Port #1: Midi Through 14:0\r\n Input Port #2: Pure Data 128:2\r\n Input Port #3: Pure Data 128:3\r\n\r\nCurrent output API: Linux ALSA\r\n\r\nThere are 3 MIDI output ports available.\r\n Output Port #1: Midi Through 14:0\r\n Output Port #2: Pure Data 128:0\r\n Output Port #3: Pure Data 128:1\r\n~\r\n\r\nI am not entirely sure what the motivation for this filtering predicate is, so I hesitate to change it. Perhaps @garyscavone can comment? I think the idea actually is to specifically restrict the list to hardware ports, but I am not sure why."}],"action":{"name":"View Issue","url":"https://github.com/thestk/rtmidi/issues/107#issuecomment-296204832"}}}

jcelerier commented 7 years ago

@garyscavone : https://github.com/thestk/rtmidi/pull/108 . Thanks ! I could not see another flag that could make sense alone.