Forums > Dev

Access to object instance from function called from outside Max?

August 9, 2007 | 11:20 pm

For reasons I won’t bore you with, I’m experimenting with writing my own simple CoreMIDI i/o objects. These create a CoreMIDI client/port, find and connect a CoreMIDI endpoint, and (for inputs) register a MIDIProcess() function that is called directly by the OS X MIDIServer.

The MIDIProcess function needs to know about the object instance struct, in order to send data via the object outlets.

It is not called in response to a message to the object, so doesn’t get passed a pointer to the object. I can’t set a global pointer during object_new(), because that will just wind up referring to the last-instantiated instance of the object, for all instances of the object.

What’s the best way to handle this?


August 9, 2007 | 11:50 pm

On 07-08-09, at 1620, John Pitcairn wrote:
>
> For reasons I won’t bore you with, I’m experimenting with writing
> my own simple CoreMIDI i/o objects. These create a CoreMIDI client/
> port, find and connect a CoreMIDI endpoint, and (for inputs)
> register a MIDIProcess() function that is called directly by the OS
> X MIDIServer.
>
> The MIDIProcess function needs to know about the object instance
> struct, in order to send data via the object outlets.
>
> It is not called in response to a message to the object, so doesn’t
> get passed a pointer to the object. I can’t set a global pointer
> during object_new(), because that will just wind up referring to
> the last-instantiated instance of the object, for all instances of
> the object.
>
I assume you’re creating a single midi source in object_new() that
all the object instances are supposed to receive data from? I’ve
handled a similar situation with a networking object by creating a
singleton boxless object to manipulate the endpoint which then keeps
track of all other instances of the object and updates them
accordingly. There may be better ways of doing things now.

A simpler way would be to create a static global list of instances,
and pass a pointer to that as the refcon to MIDIPortConnectSource or
whatever you’re using. In your callback, grab the list of instances
and notify them. This saves you from having to create a boxless
object, but means (I _think) that you can’t shut down your connection
until Max exits. With a boxless object, you can lazy instantiate/
destroy the singleton as boxed objects are created and destroyed. Now
that I think about it, you should be able to do the same w/ a static
global list as well. Maybe I’ll try that next time =)

r.


August 10, 2007 | 1:20 am

Quote: maxmspjit wrote on Fri, 10 August 2007 11:50
—————————————————-
> I assume you’re creating a single midi source in object_new()
> that all the object instances are supposed to receive data
> from?

No, each instance has its own MIDIClientRef and MIDIPortRef struct members, finds the appropriate MIDIEndpointRef by portname (passed as box argument at init or as a message to the inlet), and uses MIDIPortConnectSource to connect that to the instance’s MIDIPortRef.

Individual instances may connect to any port in the system on the fly, as per standard Max MIDI i/o objects, they don’t necessarily all share the same connection.

> A simpler way would be to create a static global list of
> instances, and pass a pointer to that as the refcon to
> MIDIPortConnectSource or whatever you’re using. In your
> callback, grab the list of instances and notify them.

The refcon is passed to the MIDIPortRef creator function, not to MIDIPortConnectSource, and must be a reference to the callback function. Passing anything else won’t work.

The callback function needs to access the instance’s outlets to output data from that instance. Other instances do not need to be notified, and the instance doesn’t need to know about them.

My problem is: how to get a pointer to the object instance struct from inside a function that is not called by the instance itself, and so cannot have the pointer passed to it as an argument?


August 10, 2007 | 3:27 am

On 07-08-09, at 1820, John Pitcairn wrote:
>
> The callback function needs to access the instance’s outlets to
> output data from that instance. Other instances do not need to be
> notified, and the instance doesn’t need to know about them.
>
> My problem is: how to get a pointer to the object instance struct
> from inside a function that is not called by the instance itself,
> and so cannot have the pointer passed to it as an argument?
>
I’m still a little fuzzy on how the whole thing goes together (I
haven’t used midi at all), but it seems odd that there is a function
to register a callback that doesn’t allow you to provide some sort of
context pointer (I think I mis-used the term refcon earlier, I meant
context or userinfo). If you can post or mail source I’d be happy to
take a look at it, but without that I don’t understand the problem
well enough to be much help at this point. Sorry.

r.


August 10, 2007 | 3:52 am

No way man, when Apple API says

struct foo {
OSStatus (*bar)(…);
void* refcon;
}

Or your callback looks like

Void blah( void* refcon);

That means refcon is a pointer we’ll save for you, the client, to use
as state during the callback. Toss a pointer to your max obj in there
when you register the callback and, then cast and de-reference or
whatever when you get called. I haven’t even looked at that API but
the naming convention is that refcon means client state in
asynchronous C API.

_Mark

On Aug 9, 2007, at 6:20 PM, John Pitcairn
wrote:

>
> Quote: maxmspjit wrote on Fri, 10 August 2007 11:50
> —————————————————-
>> I assume you’re creating a single midi source in object_new()
>> that all the object instances are supposed to receive data
>> from?
>
> No, each instance has its own MIDIClientRef and MIDIPortRef struct
> members, finds the appropriate MIDIEndpointRef by portname (passed
> as box argument at init or as a message to the inlet), and uses
> MIDIPortConnectSource to connect that to the instance’s MIDIPortRef.
>
> Individual instances may connect to any port in the system on the
> fly, as per standard Max MIDI i/o objects, they don’t necessarily
> all share the same connection.
>
>> A simpler way would be to create a static global list of
>> instances, and pass a pointer to that as the refcon to
>> MIDIPortConnectSource or whatever you’re using. In your
>> callback, grab the list of instances and notify them.
>
> The refcon is passed to the MIDIPortRef creator function, not to
> MIDIPortConnectSource, and must be a reference to the callback
> function. Passing anything else won’t work.
>
> The callback function needs to access the instance’s outlets to
> output data from that instance. Other instances do not need to be
> notified, and the instance doesn’t need to know about them.
>
> My problem is: how to get a pointer to the object instance struct
> from inside a function that is not called by the instance itself,
> and so cannot have the pointer passed to it as an argument?
>


August 10, 2007 | 4:10 am

> it seems odd that there is a function to register a callback that
> doesn’t allow you to provide some sort of context pointer

exactly:

extern OSStatus MIDIClientCreate(
CFStringRef name,
MIDINotifyProc notifyProc,
void *notifyRefCon,
MIDIClientRef *outClient );

so you create a MIDIClient, pass your state in as the void*
notifyRefCon and you’ll get called on your notifyProc with the
notifyRefCon as an argument when something changes about the MIDI
setup. Put your max obj in there somewhere.

extern OSStatus MIDIInputPortCreate(
MIDIClientRef client,
CFStringRef portName,
MIDIReadProc readProc,
void *refCon,
MIDIPortRef *outPort );

again, refCon is where you store your state, it gets passed to your
MIDIReadProc when MIDI messages are available.
Same goes for MIDIDestinationCreate (I don’t understand the
difference between an input port and a destination).

You don’t need to worry about state with the MIDIOutputPort or
MIDISourceCreate because you basically just stuff MIDI messages on
there at your leasure using either MIDIReceived or MIDISend. I would
imagine you would do this when one of your objects receives a message
callback.

_Mark


August 10, 2007 | 4:13 am

Yup. I am stupid:

x->out = outlet_new(x, 0L);
MIDIInputPortCreate(x->client, CFSTR("Input port"), MIDIProcess, x->out, &(x->port));

then:

void MIDIProcess(const MIDIPacketList *pktlist, void *refCon, void *connRefCon)
{
outlet_list(refCon, …);
}

Gets me the outlet to use directly in the callback. Thanks for the kick in the pants, Mark.


August 10, 2007 | 4:13 pm

You’re not stupid! I’m glad to hear that your code works.

_Mark

On Aug 9, 2007, at 9:13 PM, John Pitcairn
wrote:

>
> Yup. I am stupid:
>
> x->out = outlet_new(x, 0L);
> MIDIInputPortCreate(x->client, CFSTR("Input port"), MIDIProcess, x-
> >out, &(x->port));
>
> then:
>
> void MIDIProcess(const MIDIPacketList *pktlist, void *refCon, void
> *connRefCon)
> {
> outlet_list(refCon, …);
> }
>
> Gets me the outlet to use directly in the callback. Thanks for the
> kick in the pants, Mark.


Viewing 8 posts - 1 through 8 (of 8 total)