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@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/2cacf04770abf2...
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@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@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@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@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@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@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel
_______________________________________________ MacRuby-devel mailing list MacRuby-devel@lists.macosforge.org http://lists.macosforge.org/mailman/listinfo.cgi/macruby-devel