[MacRuby-devel] Using performRubySelector

Mike Laurence mklaurence at gmail.com
Tue Jun 2 10:21:55 PDT 2009


FYI to all MIDI-hungry folks:

I've gotten MIDI input to work perfectly with MacRuby (hooray!) I'm
working on output today, and hopefully I'll have something workable up
on github shortly.

As it turns out, I never could get performRubySelector to work the way
I wanted, but I realized I wanted to use a different program flow for
input anyway (pretty much just following rbcoremidi, with an array
that gets filled with midi data and emptied whenever you query it.)

~Mike

On Thu, May 28, 2009 at 1:57 PM, Mike Laurence <mklaurence at gmail.com> wrote:
> 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