[MacRuby-devel] Using performRubySelector

Mike Laurence mklaurence at gmail.com
Thu May 28 11:57:03 PDT 2009


I'll try and post what I was working on to Github, but it's fairly
incomplete, as I didn't want to get too far and then find out I
couldn't send messages to ruby the way I thought :-)

This little MIDI project of mine was just to get my formerly-RubyCocoa
(and thus formerly Midiator / DL / rbcoremidi compatible) app working
in MacRuby, but the more I think about it, the more it feels like we
should do something about this fragmented ruby MIDI situation. Since
that's really a topic for the Ruby-MIDI forum, I posted my thoughts
over there:
http://groups.google.com/group/ruby-midi/browse_thread/thread/2cacf04770abf242

Let the firestorm begin! Or, maybe just a happy storm. That would be better.

Mike

On Thu, May 28, 2009 at 1:02 PM, Jeremy Voorhis <jvoorhis at gmail.com> wrote:
> I'd love to do it in FFI, but getting this far in C was a lot of work!
>
> ... so here's another crackpot idea of mine. Another project I have
> been following is the Pure Language
> <http://code.google.com/p/pure-lang>. It aims to be a simple,
> interpreted functional language based on term-rewriting, and also has
> an LLVM JIT compiler. Using LLVM, they support a literal syntax for
> declaring foreign functions.
>
> To assist in creating wrappers for libraries, they created a tool
> called pure-gen that parses C header files and dumps out a .pure file
> containing FFI declarations. The tool is written in Haskell using a
> module called Language.C, but I wonder if we could bootstrap something
> similar by wrapping up Clang's libparse. They've managed to create
> wrappers for gsl, gtk+ and some other libraries by starting with the
> generated FFI decls, and adding some syntactic sugar and abstraction
> within Pure as appropriate.
>
> Implementing such a tool is probably going to take a lot of effort,
> but the benefits are pretty clear – especially since generated
> wrappers might be usable in JRuby and MRI. I also don't know how much
> this overlaps with BridgeSupport, but I just wanted to put the idea
> out there.
>
> Best,
>
> Jeremy
>
> On Thu, May 28, 2009 at 10:38 AM, Matt Aimonetti
> <mattaimonetti at gmail.com> wrote:
>> I second what Jeremy said. A nice Midi wrapper would be great.
>> Jeremy, regarding your MusicPlayer lib, the other option is to port it to
>> FFI and therefore making it JRuby and MacRuby compatible.
>>
>> - Matt
>>
>> On Thu, May 28, 2009 at 10:32 AM, Jeremy Voorhis <jvoorhis at gmail.com> wrote:
>>>
>>> Mike,
>>>
>>> Is your code available anywhere, e.g. on github? I haven't looked at
>>> the PyObjC code myself, but I'd be interested in having really decent
>>> CoreMIDI support available in MacRuby.
>>>
>>> Btw, when C extensions are supported, simple apps will be able to use
>>> my MusicPlayer lib at
>>> http://github.com/jvoorhis/music_player/tree/master, which wraps much
>>> of the MusicPlayer/MusicSequence api in AudioToolbox.framework. It's
>>> short on documentation, but it's easy to get up and running on MRI
>>> 1.8.x or 1.9.1.
>>>
>>> Best,
>>>
>>> Jeremy
>>>
>>> On Wed, May 27, 2009 at 11:25 PM, Mike Laurence <mklaurence at gmail.com>
>>> wrote:
>>> > Hmmm... I'm doing several things different, perhaps one or more of
>>> > them are impossible :-)
>>> >
>>> > Basically, I'm trying to get MIDI support into Ruby via the PYMIDI
>>> > obj-c library, which is really just a wrapper around CoreMIDI. One way
>>> > I had thought of: create an additional obj-c class (MidiReceiver)
>>> > which processes incoming packets for a given MIDI source and then call
>>> > methods such as "note", "controlChange", "clockTick", etc. This
>>> > MidiReceiver class can then be overridden by a custom ruby class that
>>> > contains those same methods.
>>> >
>>> > Here is a quick version of the MidiReceiver class (with only the
>>> > clockTick method for simplicity):
>>> >
>>> > ------------- MidiReceiver --------------
>>> >
>>> > #import <MacRuby/MacRuby.h>
>>> > #include <CoreMIDI/CoreMIDI.h>
>>> >
>>> > @interface MidiReceiver : NSObject
>>> >
>>> > - (void) processMIDIPacket: (MIDIPacket*) packet;
>>> > - (void) clockTick;
>>> >
>>> > @end
>>> >
>>> > @implementation MidiReceiver
>>> >
>>> > - (void) processMIDIPacket: (MIDIPacket*) packet {
>>> >    if (packet->length > 0) {
>>> >
>>> >        int statusByte = packet->data[0];
>>> >        int status = statusByte >= 0xf0 ? statusByte : statusByte >> 4 <<
>>> > 4;
>>> >
>>> >        switch (status) {
>>> >        case 0x90: // Note on, etc...
>>> >        case 0xf8: // Clock tick
>>> >            [self performRubySelector:@selector(clockTick)];
>>> >            break;
>>> >        }
>>> >    }
>>> > }
>>> >
>>> > - (void) clockTick {
>>> > }
>>> >
>>> > @end
>>> >
>>> > --------------------------------------------------------
>>> >
>>> > Then, I have a ruby subclass of MidiReceiver that overrides clockTick,
>>> > etc.:
>>> >
>>> > class LiveMidiReceiver < MidiReceiver
>>> >  def clockTick
>>> >    puts "tick!"
>>> >  end
>>> > end
>>> >
>>> > And then, in my Ruby ApplicationController, I'm finding the MIDI
>>> > source and adding an instance of LiveMidiReceiver as a MIDI receiver:
>>> >
>>> > @src = PYMIDIManager.sharedInstance.realSources.find{ |s|
>>> > s.displayName == 'KONTROL49 PORT A' }
>>> > receiver = LiveMidiReceiver.new
>>> > @src.addReceiver(receiver)
>>> >
>>> > The LiveMidiReceiver instance, upon receiving a midi packet, is called
>>> > properly up to the point of the performRubySelector, but thereafter it
>>> > launches into the debugger with EXC_BAD_ACCESS messages or other
>>> > unsightly stack dumps.
>>> >
>>> > ---
>>> >
>>> > An even *better* interface would be to have the "clockTick" and other
>>> > calls be performable on an arbitrary ruby object without having to
>>> > subclass MidiReceiver (e.g., have MidiReceiver send "clockTick", etc.
>>> > to a delegate object which has been created solely in Ruby). I tried
>>> > that and it gave me similar results, although strangely it only
>>> > crashed the first time on a clean build - thereafter I saw no crashes,
>>> > but still no confirmation of ruby method calls either.
>>> >
>>> > To test that, I just added a delegate object to MidiReceiver, and then
>>> > I changed the clockTick recipient from self to delegate:
>>> >
>>> > [delegate performRubySelector:@selector(clockTick)];
>>> >
>>> > Then set receiver.delegate = self in my ApplicationController. I'll
>>> > bet I need some more hooks than that, although it sure would be nice
>>> > to send ruby messages from obj-c willy-nilly :-)
>>> >
>>> > ---
>>> >
>>> > I hope I'm making some sense here! I greatly appreciate any info that
>>> > you can send my way. Hopefully when I get this all figured out I can
>>> > write a nice, fat blog post about it :-)
>>> >
>>> > Regards,
>>> > Mike Laurence
>>> >
>>> > On Wed, May 27, 2009 at 8:18 PM, Laurent Sansonetti
>>> > <lsansonetti at apple.com> wrote:
>>> >> Hi Mike,
>>> >>
>>> >> On May 26, 2009, at 5:45 PM, Mike Laurence wrote:
>>> >>
>>> >>> Hello,
>>> >>>
>>> >>> I'm trying to get some obj-c code to talk back to my ruby. After
>>> >>> encountering some "EXC_BAD_ACCESS" messages and scouring the web, I've
>>> >>> concluded that I'm probably supposed to use performRubySelector
>>> >>> instead of just expecting selectors to work when overridden by ruby
>>> >>> subclasses, etc.
>>> >>>
>>> >>> What is the preferred way to get this to work? I looked through some
>>> >>> of Elysium's old code (which used performRubySelector), but I'm having
>>> >>> trouble wrapping my head around how you're supposed to use the MacRuby
>>> >>> sharedRuntime to get things to happen. If someone could give me a
>>> >>> quick example of how to call arbitrary ruby methods, I would highly
>>> >>> appreciate it.
>>> >>>
>>> >>> Of course, if I'm completely off base and there's some other way to
>>> >>> call ruby code, please let me know!
>>> >>
>>> >> Calling Ruby from Objective-C can be problematic, depending if you want
>>> >> to
>>> >> call a pure Ruby method or a Ruby method that overrides an Objective-C
>>> >> one.
>>> >> If you want to dispatch the method by yourself (and if it's a Ruby
>>> >> method
>>> >> that overrides a specialized Objective-C method), you may want to be
>>> >> very
>>> >> careful about the types of the arguments you are passing to, as well as
>>> >> the
>>> >> return value.
>>> >>
>>> >> In any case, using performRubySelector: is better because arguments
>>> >> will be
>>> >> converted from Objective-C objects (id) into the expected type, and the
>>> >> return value will be converted into an Objective-C object. Also,
>>> >> performRubySelector: can deal with Ruby methods that have optional or
>>> >> splat
>>> >> arguments.
>>> >>
>>> >> $ cat t.m
>>> >> #import <Foundation/Foundation.h>
>>> >> #import <MacRuby/MacRuby.h>
>>> >>
>>> >> @interface Foo : NSObject
>>> >> @end
>>> >>
>>> >> @implementation Foo
>>> >> - (int)aMethodReturningInt
>>> >> {
>>> >>    return 123;
>>> >> }
>>> >> @end
>>> >>
>>> >> int main(void)
>>> >> {
>>> >>    [[MacRuby sharedRuntime] evaluateString:@"class X; def foo(x=1, *a);
>>> >> p x,
>>> >> a; end; end"];
>>> >>    Class k = NSClassFromString(@"X");
>>> >>    id o = [k new];
>>> >>
>>> >>    [o performRubySelector:@selector(foo)];
>>> >>    [o performRubySelector:@selector(foo:) withArguments:@"1", NULL];
>>> >>    [o performRubySelector:@selector(foo:) withArguments:@"1", @"2",
>>> >> @"3",
>>> >> NULL];
>>> >>
>>> >>    [[MacRuby sharedRuntime] evaluateString:@"class Bar < Foo; def
>>> >> aMethodReturningInt; 42; end; end"];
>>> >>    k = NSClassFromString(@"Bar");
>>> >>    o = [k new];
>>> >>
>>> >>    NSLog(@"%d", [(Foo *)o aMethodReturningInt]);
>>> >>    NSLog(@"%@", [o
>>> >> performRubySelector:@selector(aMethodReturningInt)]);
>>> >>
>>> >>    return 0;
>>> >> }
>>> >> $ gcc t.m -o t -framework Foundation -framework MacRuby -fobjc-gc
>>> >> $ ./t
>>> >> 1
>>> >> []
>>> >> "1"
>>> >> []
>>> >> "1"
>>> >> ["2", "3"]
>>> >> 2009-05-27 18:16:36.092 t[11922:10b] 42
>>> >> 2009-05-27 18:16:36.095 t[11922:10b] 42
>>> >> $
>>> >>
>>> >> If you still have problems, it would be easier if you could paste some
>>> >> code,
>>> >> this way I can try to help you more.
>>> >>
>>> >> HTH,
>>> >> Laurent
>>> >> _______________________________________________
>>> >> MacRuby-devel mailing list
>>> >> MacRuby-devel at lists.macosforge.org
>>> >> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>>> >>
>>> > _______________________________________________
>>> > MacRuby-devel mailing list
>>> > MacRuby-devel at lists.macosforge.org
>>> > http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>>> >
>>> _______________________________________________
>>> MacRuby-devel mailing list
>>> MacRuby-devel at lists.macosforge.org
>>> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>>
>>
>> _______________________________________________
>> MacRuby-devel mailing list
>> MacRuby-devel at lists.macosforge.org
>> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>>
>>
> _______________________________________________
> MacRuby-devel mailing list
> MacRuby-devel at lists.macosforge.org
> http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
>


More information about the MacRuby-devel mailing list