Re: ObjC Midi Input prob. (Was Re: Developing Tool for Audio program)
Re: ObjC Midi Input prob. (Was Re: Developing Tool for Audio program)
- Subject: Re: ObjC Midi Input prob. (Was Re: Developing Tool for Audio program)
- From: Brian Willoughby <email@hidden>
- Date: Tue, 30 Dec 2003 19:22:10 -0800
Iain,
[ Ok, here goes... I'm writing a program to control a synth via midi. I
[ choose ObjC, partly because it seems to be the easiest way to take
[ advantage of all the nice stuff in cocoa and interface builder, partly
[ because I like it better than C or C++.
[
[ In order to read midi I need to define a function to pass to
[ MIDIInputPortCreate. This would appear to have to be a C function, I
[ don't seem to be able to persuade MIDIInputPortCreate to accept an ObjC
[ method, either class or instance.
[
[ As far as I can tell, it won't be possible to set an ObjC class's
[ instance variables from within this C function. I will need to store
[ data in C global variables. This means that it wont be so easy to
[ update onscreen values that relate to incoming midi values. It would be
[ nice to have a midi message fed directly in to an NSSlider (and
[ vice-versa).
[
[ Anyone know of a neat way of integrating the C midi input stuff with
[ ObjC? I can't seem to find any sample code that does this. There are
[ plenty of C midi in/out programs, and the occasional ObjC midi output,
[ but no ObjC midiIn as far as I can see.
Good question.
You are correct that function callbacks for API like MIDIInputPortCreate
cannot be Objective-C methods. That's because methods are called a bit
differently than C functions. The API would need both a class instance and a
method signature, and there's no easy way to specify both with a single C
function address.
You are also correct that you cannot access ObjC instance variables from a C
function - that's part of the object-oriented paradigm. But it is perfectly
legal for a C function to call an Objective-C method, and you can provide any
parameters needed. You merely need to save your C code in a source file with
the .m extension (the Objective-C compiler makes no requirement that a file
contain a single class, unlike the Java compiler). Or, you can find a way to
provide the appropriate command-line options to the compiler which will enable
Objective-C within a .c file (the latter is a bit harder to do within
ProjectBuilder/XCode unless you want this enabled for all source files).
Another option is to place the C function within your Objective C source, since
it is perfectly legal to define and declare standard C anywhere within an ObjC
object.
The only trouble remains in how the C function is to determine which instance
to send the message to. Just because you may have written the C function
inside the ObjC class implementation, that still doesn't mean the C function
can find the instance at run time. The most straightforward way to handle this
is to store the instance pointer (id) in a global variable available to the C
function. A slightly fancier solution would be to write a class method which
returns what is called a class singleton - effectively a global storing a
single instance, but this implementation is hidden by the class method. All
class names are effectively globals which are known (everywhere the @interface
has been import'd) even to standard C. I do not prefer either of these two
solutions, but they are workable.
Both preceeding ideas will only work if you have a single instance of your
ObjC class handling the MIDIInputPortCreate callbacks. If you have more than
one class instance (probably the case if you have more than one input port),
you'll need a way for the C function to determine which instance to route the
method call to. It seems unwieldy to manage more than one global, since
determining the appropriate one could prove tricky, so another solution is
needed. Since nearly all C callback API provide a 32-bit "user" value, you can
use this as a place to store the appropriate instance pointer (id). When the
C function is called by CoreAudio, cast the void to id, or the appropriate
class instance. You can also copy the user value to a new variable of type id
or YourClass *. I have successfully used the refCon parameter to give my
ReadProc() the instance, and then pass on the pktlist to the method. In
MIDIInputPortCreate, the parameter right after the C function address, you can
simply provide self, or any other ObjC instance. When the ReadProc() is
called, the same refCon will appear as the second parameter. So far, I haven't
used the connRefCon.
In all cases, I assume that there will be no trouble determining which method
to call once you have an instance, because you will probably write a method
specifically to handle the callback. I named mine
-(void)readProc:(const MIDIPacketList *)pktlist
I hope this answers your questions and gives you a few options (as well as
explaining some of the "why"). I realize that full sample code might be
clearer. Please let me know if any of the above is not clear.
Brian Willoughby
Sound Consulting
_______________________________________________
coreaudio-api mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/coreaudio-api
Do not post admin requests to the list. They will be ignored.