Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ PATH
remote: .
specs:
rcoremidi (0.0.1)
concurrent-ruby
listen
thor

Expand All @@ -27,6 +28,7 @@ GEM
childprocess (0.8.0)
ffi (~> 1.0, >= 1.0.11)
coderay (1.1.2)
concurrent-ruby (1.0.5)
contracts (0.16.0)
cucumber (3.0.0)
builder (>= 2.1.2)
Expand All @@ -47,9 +49,10 @@ GEM
diff-lcs (1.3)
ffi (1.9.18)
gherkin (4.1.3)
listen (3.0.8)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
method_source (0.9.0)
multi_json (1.12.2)
multi_test (0.1.2)
Expand All @@ -62,7 +65,7 @@ GEM
rake (12.3.0)
rake-compiler (1.0.4)
rake
rb-fsevent (0.10.2)
rb-fsevent (0.10.3)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
rdoc (4.3.0)
Expand All @@ -82,6 +85,7 @@ GEM
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-support (3.7.0)
ruby_dep (1.5.0)
thor (0.20.0)

PLATFORMS
Expand All @@ -99,4 +103,4 @@ DEPENDENCIES
rspec-its

BUNDLED WITH
1.16.0
1.16.1
90 changes: 37 additions & 53 deletions ext/rcoremidi/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ static ByteCount max_packet_list_size = 65536;
static unsigned long mspm = 60000000;
static ByteCount note_on_packet_size = 2;
static VALUE cb_thread;


static int midi_beat_start = 0;
static int last_midi_beat = 0;
pthread_mutex_t g_callback_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t g_callback_cond = PTHREAD_COND_INITIALIZER;
callback_t *g_callback_queue = NULL;

/* http://www.burgestrand.se//articles/asynchronous-callbacks-in-ruby-c-extensions/ */
/* http://www.burgestrand.se/articles/asynchronous-callbacks-in-ruby-c-extensions/ */
void g_callback_queue_push(callback_t *callback)
{
callback->next = g_callback_queue;
Expand Down Expand Up @@ -51,6 +51,10 @@ static void stop_waiting_for_callback_signal(void *w)
pthread_mutex_unlock(&g_callback_mutex);
}

static VALUE handle_callback(VALUE midi_clock, VALUE client, int argc, VALUE* argv) {
rb_funcall(client, rb_intern("on_tick"), 1, midi_clock);
return Qnil;
}

static VALUE boot_callback_event_thread(void * data) {
callback_waiting_t waiting = {
Expand All @@ -61,16 +65,12 @@ static VALUE boot_callback_event_thread(void * data) {
rb_thread_call_without_gvl(wait_for_callback_signal, &waiting, stop_waiting_for_callback_signal, &waiting);
if (waiting.callback)
{
RCoremidiNode *clientNode = (RCoremidiNode *)waiting.callback->data;
pthread_mutex_lock(&waiting.callback->mutex);
rb_funcall(
clientNode->rb_client_obj,
rb_intern("on_tick"),
1,
UINT2NUM(clientNode->transport->bar)
);
/* printf ("TRANSPORT: %d \n", clientNode->transport->tick_count); */
pthread_mutex_unlock(&waiting.callback->mutex);
callback_t *cb = (callback_t*)waiting.callback;
RCoremidiNode *clientNode = (RCoremidiNode *)cb->data;
VALUE pool = rb_iv_get(clientNode->rb_client_obj, "@pool");
VALUE *block_args = &clientNode->rb_client_obj;
VALUE midi_beat = UINT2NUM(clientNode->transport->tick_count);
rb_block_call(pool, rb_intern("post"), 1, &midi_beat, handle_callback, clientNode->rb_client_obj);
}
}
return Qnil;
Expand All @@ -82,8 +82,6 @@ static void midi_node_free(void *ptr)
RCoremidiNode *tmp = ptr;
if(tmp) {
// May be (also) use MIDICLientDispose() from OSX API?
// Anyway is this usefull or should i just need to
// free the struct RCoremidiNode. Any just to make sure i free all
free(tmp->client);
free(tmp->transport);
free(tmp->name);
Expand Down Expand Up @@ -115,10 +113,7 @@ RCoremidiNode * client_get_data(VALUE self) {

static RCoreMidiTransport * reset_transport(RCoreMidiTransport * transport) {
transport->tick_count = 0;
transport->bar = 1;
transport->quarter = 0;
transport->eigth = 0;
transport->sixteinth = 0;
transport->state = kMIDIStop;
return transport;
}

Expand All @@ -137,7 +132,6 @@ VALUE client_alloc(VALUE klass)
clientNode->transport = transport_alloc();
clientNode->in = malloc(sizeof(MIDIPortRef));
clientNode->out = malloc(sizeof(MIDIPortRef));
clientNode->out = malloc(sizeof(MIDIPortRef));
clientNode->callback = malloc(sizeof(callback_t));
pthread_mutex_init(&clientNode->callback->mutex, NULL);
pthread_cond_init(&clientNode->callback->cond, NULL);
Expand Down Expand Up @@ -175,10 +169,12 @@ static void notifyProc(const MIDINotification *notification, void *refCon)
}
}

static int calculate_current_tick(lsb, msb) {
int midi_beat_start = (msb << 7) | lsb;
return midi_beat_start * 6;
}

static void MidiReadProc(const MIDIPacketList *pktlist, void *refCon, void *connRefCon) {

pthread_mutex_lock(&g_callback_mutex);
MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
unsigned int j;
int i;
Expand All @@ -192,45 +188,33 @@ static void MidiReadProc(const MIDIPacketList *pktlist, void *refCon, void *conn

switch(packet->data[i]) {
case kMIDIStart:
/* static MIDITimeStamp timestamp = 0; */
transport->state = kMIDIStart;
transport->current_timestamp = mach_absolute_time();
clientNode->callback->data = (void *)clientNode;


g_callback_queue_push(clientNode->callback);

transport->tick_count = -1;
break;
case kMIDIContinue:
transport->tick_count = last_midi_beat - 1;
break;
case kMIDIStop:
printf("Stoping Client...\n");
transport->state = kMIDIStop;
break;
case kMIDITick:
transport->tick_count++;
printf("%d\n", transport->tick_count);
if ((transport->tick_count % 96) == 0) {
clientNode->callback->data = (void *)clientNode;

g_callback_queue_push(clientNode->callback);

transport->bar++;
}
// quarter
if ((transport->tick_count % 24) == 0) {
transport->quarter++;
}
// eigth
if ((transport->tick_count % 12) == 0) {
transport->eigth++;
}
// sixteinth
if ((transport->tick_count % 8) == 0) {
transport->sixteinth++;
}
printf("C: tick_count %d\n", transport->tick_count);

pthread_mutex_lock(&g_callback_mutex);
clientNode->callback->data = (void *)clientNode;
g_callback_queue_push(clientNode->callback);
pthread_mutex_unlock(&g_callback_mutex);
pthread_cond_signal(&g_callback_cond);

break;
case kMIDISongPositionPointer:
reset_transport(transport);
last_midi_beat = calculate_current_tick(packet->data[i+1], packet->data[i+2]);
/* packet = MIDIPacketNext(packet); */
break;
default:
printf("DEFAULT packet : %04x\n", packet->data[i]);
}


Expand All @@ -245,8 +229,6 @@ static void MidiReadProc(const MIDIPacketList *pktlist, void *refCon, void *conn

packet = MIDIPacketNext(packet);
}
pthread_mutex_unlock(&g_callback_mutex);
pthread_cond_signal(&g_callback_cond);
}

VALUE connect_to(VALUE self, VALUE source)
Expand Down Expand Up @@ -428,14 +410,16 @@ VALUE client_init(VALUE self, VALUE name)
}

rb_iv_set(self, "@name", name);
rb_iv_set(self, "@started", Qfalse);


clientNode->rb_client_obj = self;

cb_thread = rb_thread_create(boot_callback_event_thread, NULL);
rb_funcall(cb_thread, rb_intern("abort_on_exception="), 1, Qtrue);
rb_iv_set(self, "@cb_thread", cb_thread);

rb_iv_set(self, "@midi_clock", UINT2NUM(0));
rb_iv_set(self, "@pool", rb_funcall(rb_cCachedThreadPool, new_intern, 0, Qnil));
return self;
}

Expand Down
2 changes: 1 addition & 1 deletion ext/rcoremidi/extconf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

$CFLAGS = "-Wno-error=shorten-64-to-32 -g"
$CPPFLAGS += " -g "
$CPPFLAGS += "-I/Users/yann/.rbenv/versions/2.1.1/include/ruby-2.1.0 -I/System/Library/Frameworks/CoreMIDI.framework/Headers "
$CPPFLAGS += "-I/System/Library/Frameworks/CoreMIDI.framework/Headers "
$LDFLAGS += " -F/System/Library/Frameworks -framework CoreMIDI -framework CoreFoundation -framework CoreAudio -framework Carbon"
dir_config("rcoremidi")
create_makefile("rcoremidi")
11 changes: 9 additions & 2 deletions ext/rcoremidi/rcoremidi.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "rcoremidi.h"

VALUE rb_cCachedThreadPool;
VALUE rb_mRCOREMIDI;
VALUE rb_cMIDIObject;
VALUE rb_cDevice;
Expand All @@ -20,7 +21,6 @@ ID empty_intern;
ID lock_intern;
ID length_intern;
ID pack_intern;
ID core_midi_cb_thread_intern;
ID on_intern;
ID off_intern;
ID on_timestamp_intern;
Expand Down Expand Up @@ -69,7 +69,6 @@ Init_rcoremidi()
devices_intern = rb_intern("@@devices");
empty_intern = rb_intern("empty?");
length_intern = rb_intern("length");
core_midi_cb_thread_intern = rb_intern("@@core_midi_cb_thread");
on_intern = rb_intern("on");
off_intern = rb_intern("off");
on_timestamp_intern = rb_intern("on_timestamp");
Expand Down Expand Up @@ -130,5 +129,13 @@ Init_rcoremidi()
rb_define_method(rb_cClient, "send_packets", send_packets, 2);
rb_define_attr(rb_cClient, "name", 1, 1);
rb_define_attr(rb_cClient, "is_connected", 1, 1);
rb_define_attr(rb_cClient, "midi_in", 1, 1);
rb_define_attr(rb_cClient, "midi_out", 1, 1);

rb_require("concurrent");

/* TODO: fix as per the last comment here: https://stackoverflow.com/questions/6442920/how-do-i-extend-my-ruby-class-with-a-c-extension */

VALUE rb_cConcurrent = rb_const_get(rb_cObject, rb_intern("Concurrent"));
rb_cCachedThreadPool = rb_const_get(rb_cConcurrent, rb_intern("CachedThreadPool"));
}
17 changes: 10 additions & 7 deletions ext/rcoremidi/rcoremidi.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include <stdio.h>
#include <ruby.h>
#include <ruby/thread.h>
#include "ruby.h"
#include "ruby/thread.h"
#include <Carbon/carbon.h>
#include <CoreAudio/CoreAudio.h>
#include <CoreMIDI/MIDIServices.h>
Expand All @@ -10,6 +10,9 @@
#include "client.h"
#include "midi_object.h"

extern VALUE rb_cCachedThreadPool;



extern VALUE rb_cConectionManager;
extern VALUE rb_cDevice;
Expand All @@ -35,18 +38,18 @@ extern const rb_data_type_t midi_endpoint_data_t;
extern const rb_data_type_t midi_object_data_t;

typedef struct callback_t {
pthread_mutex_t mutex;
pthread_cond_t cond;
pthread_mutex_t mutex;
pthread_cond_t cond;

struct callback_t *next;
void *data;
bool handled;
struct callback_t *next;
void *data;
} callback_t;

extern pthread_mutex_t g_callback_mutex;
extern pthread_cond_t g_callback_cond;
extern callback_t *g_callback_queue;

// callback queue
typedef struct callback_waiting_t {
callback_t *callback;
bool abort;
Expand Down
1 change: 1 addition & 0 deletions lib/rcoremidi.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require 'securerandom'
require 'rcoremidi.bundle'
require 'rcoremidi/app_pathname'
require 'rcoremidi/device'
require 'rcoremidi/entity'
require 'rcoremidi/source'
Expand Down
Loading