Naming of virtual ports

473 views
Skip to first unread message

Nic G (Audeonic Apps)

unread,
Oct 24, 2011, 4:38:59 AM10/24/11
to Open Music App Collaboration
A quick suggestion regarding how devs name their virtual ports. Here's
an excerpt from Rolf's manifesto:

// Make virtual input & output
ret = MIDIDestinationCreate(client, (CFStringRef)name,
NLogMIDIReadProc, self, &virtInput);
ret = MIDIObjectSetIntegerProperty(virtInput,
kMIDIPropertyUniqueID, NLOG_VIRT_INPUT_ID);

ret = MIDISourceCreate(client, (CFStringRef)name, &virtOutput);
ret = MIDIObjectSetIntegerProperty(virtOutput,
kMIDIPropertyUniqueID, NLOG_VIRT_OUTPUT_ID);

Rolf passes the same name for both input and output. This string
should not include any reference to input or output and should be the
same string for the following reasons:

- Other apps can determine easily what type of port it is (source/
destination) so it shouldn't be needed.
- In another app, input may mean output and vice versa. Leave it to
the app to decide how to label it.
- For other apps that need to match an input with an output, being
able to match on the name is the only reliable way of doing this.

I'd suggest just the name of your app and nothing else for both source
and destination in the MIDI.*Create function. Add 'input' or 'output'
in your app's display labels separately if you need this.

Regards, Nic

nlogmusic (TempoRubato - NLogSynth)

unread,
Oct 26, 2011, 4:30:08 AM10/26/11
to Open Music App Collaboration
Yes, that were my intentions. Funny enough, Apple's network port
names are not following our rule. Shame on them ;-) Bot since
currently on iOS only one session is allowed, there are other ways
to relate their input & output.

On Oct 24, 10:38 am, "Nic G (Audeonic Apps)" <a...@audeonic.com>
wrote:

Nic G (Audeonic Apps)

unread,
Oct 26, 2011, 4:40:36 AM10/26/11
to Open Music App Collaboration
And just one other point - it would be a good idea to use that same
name string to
identify the app's entry in the forthcoming fast-switch strategy as
well as the
virtual port set.

Then an app can use the strings in the midi endpoints to match running
apps
to their fast-switch URL ( a feature I would like to add to MidiBridge
is to be
able to double tap a virtual MIDI port and switch to that app :-) )

Regards, Nic.

Chris Randall

unread,
Nov 25, 2011, 8:02:59 PM11/25/11
to Open Music App Collaboration
This is tertiary to this discussion; sorry for the thread hijack.
However, I'm having a devil of a time actually _getting_ the virtual
port names, and would appreciate some help. I'm using PGMidi, and his
name code uses an NSDictionary like so:


CFPropertyListRef properties = nil;
OSStatus s = MIDIObjectGetProperties(entity, &properties, true);
if (s)
{
string = @"Unknown name";
}
else
{
NSDictionary *dictionary = (NSDictionary*)properties;
string = [NSString stringWithFormat:@"%@", [dictionary
valueForKey:@"name"]];
CFRelease(properties);
}


That's all well and good, but any property call for a virtual port
returns -50, and thus his code gives me "Unknown Name." I tried
swapping his dictionary call and OSSError thing out with something
like this:

CFStringRef pname;
MIDIObjectGetStringProperty(entity, kMIDIPropertyDisplayName, &pname);
string = (NSString *)pname;

And all I get for my efforts is (null) if I use
kMIDIPropertyDisplayName (even the network session returns this) or
"Session 1" plus a "(null)" for every background port if I use
kMIDIPropertyName.

What on earth am I doing wrong here? Thanks in advance for the helps.

Support (One Red Dog)

unread,
Nov 25, 2011, 8:23:11 PM11/25/11
to open-music-app...@googlegroups.com
Here's a function that will get you all the details, you'd probably want to store this in a data structure like std::map or something

virtDest and virtSrc are your app's own virtual ports

(quickly copied and pasted so no guarantee this will actually compile, but the gist of it is there)


void getAllPortInfo()
{
// Enumerate devices
int n;

// Enumerare Source Ports / Those we receive from
n = MIDIGetNumberOfSources();
for (int i = 0; i < n; ++i)
{
MIDIEndpointRef src = MIDIGetSource(i);

// special case for virtual ports
bool input;
if (src == virtSrc)
{
input = false;
}
else
{
input = true;
}

addPortInfo(src, input);
}

// Enumerate Destination Ports
n = MIDIGetNumberOfDestinations();


for (int i = 0; i < n; ++i)
{
MIDIEndpointRef dest = MIDIGetDestination(i);

// special case for virtual ports
bool input;
if (dest == virtDest)
{
input = true;
}
else
{
input = false;
}


addPortInfo(dest, input);
}
}


void addPortInfo(const MIDIEndpointRef ref, const bool input)
{
CFStringRef pname, pmanuf, pmodel, pdispname;
MIDIUniqueID id;
        SInt32 musec = 0;
char name[128], manuf[128], model[128], dispname[128];

memset(name, 0, sizeof(name) / sizeof(name[0]));
memset(manuf, 0, sizeof(manuf) / sizeof(manuf[0]));
memset(model, 0, sizeof(model) / sizeof(model[0]));
memset(dispname, 0, sizeof(dispname) / sizeof(dispname[0]));

OSStatus res = noErr;


res = MIDIObjectGetIntegerProperty(ref, kMIDIPropertyUniqueID, &id);


res = MIDIObjectGetStringProperty(ref, kMIDIPropertyName, &pname);
if (pname != NULL)
{
CFStringGetCString(pname, name, sizeof(name), 0);
CFRelease(pname);
}

res = MIDIObjectGetStringProperty(ref, kMIDIPropertyManufacturer, &pmanuf);
if (pmanuf != NULL)
{
CFStringGetCString(pmanuf, manuf, sizeof(manuf), 0);
CFRelease(pmanuf);
}

res = MIDIObjectGetStringProperty(ref, kMIDIPropertyModel, &pmodel);
if (pmodel != NULL)
{
CFStringGetCString(pmodel, model, sizeof(model), 0);
CFRelease(pmodel);
}

        res = MIDIObjectGetIntegerProperty(ref, kMIDIPropertyAdvanceScheduleTimeMuSec, &musec);


res = MIDIObjectGetStringProperty(ref, kMIDIPropertyDisplayName, &pdispname);
if (pdispname != NULL)
{
CFStringGetCString(pdispname, dispname, sizeof(dispname), 0);
CFRelease(pdispname);

log_debug("[ %d ] name=\"%s\", manuf=\"%s\", model=\"%s\", disp=\"%s\"", name, manuf, model, dispname);

// Store this info somewhere for later retrieval by the GUI etc 

// Connect to this port
if (input && ref != virtDest)
{
OSStatus err = MIDIPortConnectSource(inPort, ref, (void*)id);
if (err != noErr)
{
cerr << "addPortInfo error " << err << endl;
}
}
}
}

Chris Randall

unread,
Nov 25, 2011, 8:33:41 PM11/25/11
to Open Music App Collaboration

On Nov 25, 6:23 pm, "Support (One Red Dog)" <supp...@onereddog.com.au>
wrote:


> Here's a function that will get you all the details, you'd probably want to store this in a data structure like std::map or something

Thanks, but that's really just a verbose way of doing what I'm already
doing. It's still the same basic call (kMIDIPropertyDisplayName) and
is going to return "(null)". The OSSStatus result is -50.

For what it's worth, the ports work fine; I'm able to send to and read
from all of them. I just can't seem to get the damn names. I tried out
a modified version of Apple's code from here:
http://developer.apple.com/library/mac/#qa/qa1374/_index.html and it
returns a much better name for the hardware and network stuff (I have
a feeling this is the code that Genome runs, since the names are
identical) but the virtual ports still return NULL.

Support (One Red Dog)

unread,
Nov 25, 2011, 8:41:39 PM11/25/11
to open-music-app...@googlegroups.com


The -50 is a paramErr, so it's possible you have something wrong with the arguments you are passing, e.g. something is null when it shouldn't be.

Christopher Randall

unread,
Nov 25, 2011, 8:51:08 PM11/25/11
to open-music-app...@googlegroups.com
I just tried the app that comes with PGMidi, MidiMonitor, and it does the same thing, so it's a problem with his implementation. Nothing like debugging someone else's code to put you in a good mood. Surely someone else must have solved this problem? At least half of you are using PGMidi.

Chris Randall
Audio Damage, Inc.
http://www.audiodamage.com

Chris Randall

unread,
Nov 25, 2011, 10:29:08 PM11/25/11
to Open Music App Collaboration
Well, I figured it out. Sort of. Virtual endpoints don't have
entities, and in PGMidi, the naming loop is passed a MIDIEndpointRef,
which is used to get an endpoint's entity via MIDIEndpointGetEntity.

Ergo, the virtual ports all return -50.

Not sure what to do about this, exactly, but at least I know _why_ it
is happening. Pulling the name off the virtual ports' endpoints
directly works, so I just have to figure out how to get that in to the
cycle.

Times like these, I think it might have just been easier to write my
own MIDI class. I've been dicking with this for two days.

Art Kerns (Synthetic Bits)

unread,
Nov 26, 2011, 3:25:12 AM11/26/11
to Open Music App Collaboration
This likely doesn't help, because what I've got seems similar to
yours, but when I looked up that part of PGMIDI in my app it turns out
I'd commented out most of his code and modified it to look like this:

--

static
NSString *NameOfEndpoint(MIDIEndpointRef ref)
{
NSString *string = nil;

CFStringRef str;
str = NULL;
MIDIObjectGetStringProperty(ref, kMIDIPropertyName, &str);

if (str != NULL)
{
string = [NSString stringWithString:(NSString *)str];
CFRelease(str);
}

/*
MIDIEntityRef entity = 0;
MIDIEndpointGetEntity(ref, &entity);

CFPropertyListRef properties = nil;
OSStatus s = MIDIObjectGetProperties(entity, &properties, true);
if (s)
{
string = @"Unknown name";
}
else
{

//NSLog(@"Properties = %@", properties);


NSDictionary *dictionary = (NSDictionary*)properties;
string = [NSString stringWithFormat:@"%@", [dictionary
valueForKey:@"name"]];
CFRelease(properties);
}

*/

return string;
}

--

Haven't looked at it in half a year so I don't remember the whats or
whys, but my apps can see the virtual MIDI port names fine so I dunno.
This was also compiled under iOS 4 so there's a small chance something
changed with 5.

Debugging this stuff sucks, no doubt.

Nic G (Audeonic Apps)

unread,
Nov 26, 2011, 4:40:22 AM11/26/11
to Open Music App Collaboration
Sources/Destinations are in a hierarchy:

Device -> Entitity - Endpoint

Each of these have a name attribute which may/may not have data
in it. The PGMidi method is to look at the entity name which works
for physical ports but not virtual ones. Network MIDI has a Device
and Entity name and hardware is all over the place.

In the end you probably need to look at all three and decide
how to identify the port. One Red Dog's code posting is
doing this. My own apps do something similar but have
extra logic to handle device/entity dupe information and
a 'kludge map' to map known 3rd party app ports that
don't conform to OMAC into something reasonable.

With all respect to Pete, PGMidi is a fantastic resource
and starting point, but to use it 'as is' in an app is
(IMNSHO) not recommended since it only implements
part of the picture.

Christopher Randall

unread,
Nov 26, 2011, 10:31:08 AM11/26/11
to open-music-app...@googlegroups.com
Actually, I found a very simple solution, specific to PGMidi. Since I'm already importing CoreMidi in the ViewController that needs the names to do Other Stuff, this was the easiest method, and avoids his naming loop entirely. I just have an NSMutableArray called midiDestNames, and bang it with this bad boy: 

for (PGMidiDestination *destination in midi.destinations)
    {
        CFStringRef tmp;
        MIDIObjectGetStringProperty(destination.endpoint, kMIDIPropertyDisplayName, &tmp);
        NSString *description = (NSString *)tmp;
        [_midiDestNames addObject:description];
    }

This results in the correct "long form" name for every port, real and virtual. You can use the same method to get the other properties, of course. Couldn't be easier. (Side note: if the app isn't ARC, you're gonna want to release that CFStringRef, of course, or else Leakus Maximus.) I couldn't create a scenario on my iPad where this didn't work. That doesn't, of course, mean that it will always work. But I couldn't break it. 

Regarding apps that don't follow OMAC specs, honestly, if an app doesn't conform to OMAC, there's only so much I'm going to care about it at this juncture. I'm already adding OMAC as an after-thought, rather than the raison d'être of the app; I'm not going to spend a lot of time on apps that don't even conform to that. 

Chris Randall
Audio Damage, Inc.

Michael Tyson

unread,
Nov 26, 2011, 1:08:17 PM11/26/11
to open-music-app...@googlegroups.com
I've made some improvements to PGMidi that I've given to Pete to work in, which address virtual ports (listing, easy creation/destruction, and a wrapper for MIDIReceived, to send), among other things.

Till he gets around to it (Pete?) you can grab the changes here: http://resources.atastypixel.com/PGMidi+TPAdditions.zip

Cheers,
Michael



Quick summary of changes:

Addition of properties:

@property (nonatomic,assign) BOOL virtualSourceEnabled;
@property (nonatomic,assign) BOOL virtualDestinationEnabled;
@property (nonatomic,readonly) PGMidiSource      *virtualDestinationSource;
@property (nonatomic,readonly) PGMidiDestination *virtualSourceDestination;
@property (nonatomic,retain)   NSString          *virtualEndpointName;

Replacement of single PGMidiSource delegate with an array of delegates:

- (void)addDelegate:(id<PGMidiSourceDelegate>)delegate;
- (void)removeDelegate:(id<PGMidiSourceDelegate>)delegate;
@property (nonatomic,readonly) NSArray           *delegates;


Couple bugfixes (like correct enumeration of virtual ports)





-- 
Michael Tyson | atastypixel.com
A Tasty Pixel: Artisan apps

Loopy HD has been released! Savvy, tactile live looping on the iPad.

Find us on Facebook, and Twitter
Subscribe to our newsletter

aim: mikerusselltyson
twitter: MichaelTyson

Christopher Randall

unread,
Nov 26, 2011, 1:32:25 PM11/26/11
to open-music-app...@googlegroups.com
Oh, very nice. I had found a couple others where people had done this, but nothing as nice as yours. Swapping that out now. Thanks! 

I think, perhaps, we should all consider an OMAC-certified CoreMidi engine that is turnkey. I wouldn't be any use to this endeavor except for moral support, but it is probably a good idea. Maybe one that has a properly-built CoreAudio timer loop already in place, that the dev could then alter to needs or remain as-is. 

(Since I know CoreAudio so well, I'm using a CoreAudio playback callback for my MIDI timing, even though my app is MIDI-only. Seemed the easiest way.) 

Chris Randall
Audio Damage, Inc.


Michael Tyson

unread,
Nov 26, 2011, 5:30:19 PM11/26/11
to open-music-app...@googlegroups.com
Cheers, Chris =)

I agree!  That would be a great way to increase adoption in other apps, which most certainly helps all of us.  PGMidi's probably an excellent starting point.  I'd be up for helping out.



-- 
Michael Tyson | atastypixel.com
A Tasty Pixel: Artisan apps

Loopy HD has been released! Savvy, tactile live looping on the iPad.

Find us on Facebook, and Twitter
Subscribe to our newsletter

aim: mikerusselltyson
twitter: MichaelTyson

Adil Sherwani

unread,
Mar 15, 2013, 12:25:04 AM3/15/13
to open-music-app...@googlegroups.com
Ok so I'm obviously super-late to the party, but after having scoured through every single thread on this forum I'm happy to say that...

... I really really want this PGMidi+TPAdditions.zip file! :)

The link is no longer working but I'm hoping someone has a copy? Pretty please!

Adil Sherwani

Bastus

unread,
Mar 16, 2013, 5:22:17 AM3/16/13
to open-music-app...@googlegroups.com
As far as I can see it, Michael has integrated all of his changes to Pete's original PGMIDI master branch on github.

https://github.com/petegoodliffe/PGMidi

Just update and everything works like a charm (At least it did for me, so Orphion will also have virtual MIDI in the next update)

Best,

Bastus

Adil Sherwani

unread,
Mar 20, 2013, 4:23:45 PM3/20/13
to open-music-app...@googlegroups.com
Ah, looks like I'd downloaded my copy right before Michael's changes were integrated - thanks for the tip! Got it all working nice and smooth now.

Adil
--
You received this message because you are subscribed to a topic in the Google Groups "Open Music App Collaboration" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/open-music-app-collaboration/LgMyw0tDVEQ/unsubscribe?hl=en.
To unsubscribe from this group and all its topics, send an email to open-music-app-colla...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Reply all
Reply to author
Forward
0 new messages