[MacRuby-devel] Using performRubySelector

Jeremy Voorhis jvoorhis at gmail.com
Thu May 28 10:32:31 PDT 2009


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
>


More information about the MacRuby-devel mailing list