xaviershay-rbcoremidi 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2008 Markus Prinz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,10 @@
1
+ Provide easy access to CoreMIDI for Ruby.
2
+
3
+ Please note that this is a work in progress. The code isn't tested and for all I know,
4
+ it sets your house on fire, steals your car and runs over your dog and/or cat. So proceed
5
+ with caution.
6
+
7
+ == Installation
8
+
9
+ $ gem sources -a http://gems.github.com
10
+ $ sudo gem install xaviershay-rbcoremidi
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rubygems/specification'
4
+ require 'date'
5
+ require 'spec/rake/spectask'
6
+
7
+ GEM = "rbcoremidi"
8
+ GEM_VERSION = "0.0.1"
9
+ AUTHOR = "Your Name"
10
+ EMAIL = "Your Email"
11
+ HOMEPAGE = "http://example.com"
12
+ SUMMARY = "A gem that provides..."
13
+
14
+ spec = Gem::Specification.new do |s|
15
+ s.name = GEM
16
+ s.version = GEM_VERSION
17
+ s.platform = Gem::Platform::RUBY
18
+ s.has_rdoc = true
19
+ s.extra_rdoc_files = ['README.rdoc', 'LICENSE']
20
+ s.summary = SUMMARY
21
+ s.description = s.summary
22
+ s.author = AUTHOR
23
+ s.email = EMAIL
24
+ s.homepage = HOMEPAGE
25
+
26
+ # Uncomment this to add a dependency
27
+ # s.add_dependency "foo"
28
+
29
+ s.require_path = 'lib'
30
+ s.extensions = ['ext/extconf.rb']
31
+ s.files = %w(LICENSE README.rdoc Rakefile) + Dir.glob("{examples,ext,lib,spec}/**/*")
32
+ end
33
+
34
+ task :default => :spec
35
+
36
+ desc "Run specs"
37
+ Spec::Rake::SpecTask.new do |t|
38
+ t.spec_files = FileList['spec/**/*_spec.rb']
39
+ t.spec_opts = %w(-fs --color)
40
+ end
41
+
42
+
43
+ Rake::GemPackageTask.new(spec) do |pkg|
44
+ pkg.gem_spec = spec
45
+ end
46
+
47
+ desc "install the gem locally"
48
+ task :install => [:package] do
49
+ sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
50
+ end
51
+
52
+ desc "create a gemspec file"
53
+ task :make_spec do
54
+ File.open("#{GEM}.gemspec", "w") do |file|
55
+ file.puts spec.to_ruby
56
+ end
57
+ end
@@ -0,0 +1,20 @@
1
+ require 'coremidi'
2
+
3
+ # Start archaeopteryx
4
+ # Start GarageBand (just to make sure it's all working)
5
+
6
+ # Open MIDI Patch Bay.app
7
+ # Create a new input (anyname)
8
+ # Create a new output (anyname)
9
+ # GarageBand will announce that it has found a new input
10
+ # You should have sound, yay
11
+
12
+ # Now run this script
13
+
14
+ midi_thread = Thread.new do
15
+ CoreMIDI::Input.register("Test", "Test", "Out") do |event|
16
+ puts event.inspect
17
+ end
18
+ end
19
+
20
+ gets # Stop on enter
data/ext/extconf.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'mkmf'
2
+ $CPPFLAGS += " -I/System/Library/Frameworks/CoreMIDI.framework/Versions/Current/Headers"
3
+ $LDFLAGS += " -framework CoreMIDI"
4
+
5
+ create_makefile('rbcoremidi')
data/ext/rbcoremidi.c ADDED
@@ -0,0 +1,391 @@
1
+ /*
2
+ * Copyright 2008 Markus Prinz
3
+ * Released unter an MIT licence
4
+ *
5
+ */
6
+
7
+ #include <ruby.h>
8
+ #include <CoreMIDI/MIDIServices.h>
9
+ #include <pthread.h>
10
+ #include <stdlib.h>
11
+
12
+ pthread_mutex_t mutex;
13
+
14
+ CFMutableArrayRef midi_data = NULL;
15
+
16
+ VALUE mCoreMIDI = Qnil;
17
+ VALUE mCoreMIDIAPI = Qnil;
18
+
19
+ VALUE cInputPort = Qnil;
20
+ VALUE cMIDIClient = Qnil;
21
+
22
+ // We need our own data structure since MIDIPacket defines data to be a 256 byte array,
23
+ // even though it can be larger than that
24
+ typedef struct RbMIDIPacket_t {
25
+ MIDITimeStamp timeStamp;
26
+ UInt16 length;
27
+ Byte* data;
28
+ } RbMIDIPacket;
29
+
30
+ // A struct that contains the input port
31
+ typedef struct RbInputPort_t {
32
+ MIDIPortRef input_port;
33
+ } RbInputPort;
34
+
35
+ // A struct that contains the midi client
36
+ typedef struct RbMIDIClient_t {
37
+ MIDIClientRef client;
38
+ } RbMIDIClient;
39
+
40
+ // Forward declare free_objects
41
+ static void free_objects();
42
+
43
+ // The callback function that we'll eventually supply to MIDIInputPortCreate
44
+ static void RbMIDIReadProc(const MIDIPacketList* packetList, void* readProcRefCon, void* srcConnRefCon)
45
+ {
46
+ if( pthread_mutex_lock(&mutex) != 0 )
47
+ {
48
+ // uh oh
49
+ // Not much we can do
50
+ return;
51
+ }
52
+
53
+ MIDIPacket* current_packet = (MIDIPacket*) packetList->packet;
54
+
55
+ unsigned int j;
56
+ for( j = 0; j < packetList->numPackets; ++j )
57
+ {
58
+ RbMIDIPacket* rb_packet = (RbMIDIPacket*) malloc( sizeof(RbMIDIPacket) );
59
+
60
+ if( rb_packet == NULL )
61
+ {
62
+ fprintf(stderr, "Failed to allocate memory for RbMIDIPacket!\n");
63
+ abort();
64
+ }
65
+
66
+ rb_packet->timeStamp = current_packet->timeStamp;
67
+ rb_packet->length = current_packet->length;
68
+
69
+ size_t size = sizeof(Byte) * rb_packet->length;
70
+ rb_packet->data = (Byte*) malloc( size );
71
+
72
+ if( rb_packet->data == NULL )
73
+ {
74
+ fprintf(stderr, "Failed to allocate memory for RbMIDIPacket data!\n");
75
+ abort();
76
+ }
77
+
78
+ memcpy(rb_packet->data, current_packet->data, size);
79
+
80
+ CFArrayAppendValue(midi_data, rb_packet);
81
+
82
+ current_packet = MIDIPacketNext(current_packet);
83
+ }
84
+
85
+ pthread_mutex_unlock(&mutex);
86
+ }
87
+
88
+ // Checks for new data and copies it over if there is some.
89
+ static VALUE t_check_for_new_data(VALUE self)
90
+ {
91
+ if( pthread_mutex_trylock(&mutex) != 0 )
92
+ {
93
+ // no data for us yet
94
+ return Qfalse;
95
+ }
96
+
97
+ // Switch out the arrays. Possibly evil
98
+ CFArrayRef data = midi_data;
99
+ midi_data = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
100
+
101
+ pthread_mutex_unlock(&mutex);
102
+
103
+ // We'll use a Ruby Struct to store the data
104
+ VALUE cMidiPacket = rb_const_get(mCoreMIDIAPI, rb_intern("MidiPacket"));
105
+
106
+ VALUE rb_midi_data = rb_ary_new();
107
+
108
+ CFIndex idx = 0;
109
+ CFIndex array_size = CFArrayGetCount(data);
110
+ const RbMIDIPacket* current_packet = NULL;
111
+
112
+ for( ; idx < array_size; ++idx )
113
+ {
114
+ current_packet = (const RbMIDIPacket*) CFArrayGetValueAtIndex(data, idx);
115
+
116
+ VALUE byte_array = rb_ary_new2(current_packet->length);
117
+
118
+ int i;
119
+ for (i = 0; i < current_packet->length; ++i)
120
+ {
121
+ rb_ary_push(byte_array, INT2FIX(current_packet->data[i]));
122
+ }
123
+
124
+ // relies on sizeof(MIDITimeStamp) == sizeof(unsigned long long)
125
+ assert(sizeof(MIDITimeStamp) == sizeof(unsigned long long));
126
+
127
+ VALUE midi_packet_args[2];
128
+ midi_packet_args[0] = ULL2NUM(current_packet->timeStamp);
129
+ midi_packet_args[1] = byte_array;
130
+
131
+ rb_ary_push(rb_midi_data, rb_class_new_instance((sizeof(midi_packet_args)/sizeof(midi_packet_args[0])), midi_packet_args, cMidiPacket));
132
+
133
+ // While we're at it..
134
+ // Free the memory! Save the whales!
135
+ free(current_packet->data);
136
+ }
137
+
138
+ // Free the memory! Save the whales! Part 2!
139
+ CFRelease(data);
140
+
141
+ return rb_midi_data;
142
+ }
143
+
144
+ static VALUE t_create_client(VALUE self, VALUE client_name)
145
+ {
146
+ VALUE midiclient_instance = rb_class_new_instance(0, 0, cMIDIClient);
147
+ if( midiclient_instance == Qnil )
148
+ {
149
+ free_objects();
150
+ rb_fatal("Couldn't create an instance of MIDIClient!");
151
+ }
152
+
153
+ MIDIClientRef midi_client;
154
+
155
+ CFStringRef client_str = CFStringCreateWithCString(kCFAllocatorDefault, RSTRING(client_name)->ptr, kCFStringEncodingASCII);
156
+ MIDIClientCreate(client_str, NULL, NULL, &midi_client);
157
+ CFRelease(client_str);
158
+
159
+ RbMIDIClient* client_struct;
160
+ Data_Get_Struct(midiclient_instance, RbMIDIClient, client_struct);
161
+
162
+ client_struct->client = midi_client;
163
+
164
+ return midiclient_instance;
165
+ }
166
+
167
+ // Create a new Input Port and saves the Ruby Callback proc.
168
+ static VALUE t_create_input_port(VALUE self, VALUE client_instance, VALUE port_name)
169
+ {
170
+ MIDIPortRef in_port;
171
+
172
+ RbMIDIClient* client;
173
+ Data_Get_Struct(client_instance, RbMIDIClient, client);
174
+
175
+ CFStringRef port_str = CFStringCreateWithCString(kCFAllocatorDefault, RSTRING(port_name)->ptr, kCFStringEncodingASCII);
176
+ MIDIInputPortCreate(client->client, port_str, RbMIDIReadProc, NULL, &in_port);
177
+ CFRelease(port_str);
178
+
179
+ VALUE inputport_instance = rb_class_new_instance(0, 0, cInputPort);
180
+ if( inputport_instance == Qnil )
181
+ {
182
+ free_objects();
183
+ rb_fatal("Couldn't create an instance of InputPort!");
184
+ }
185
+
186
+ RbInputPort* port_struct;
187
+ Data_Get_Struct(inputport_instance, RbInputPort, port_struct);
188
+
189
+ port_struct->input_port = in_port;
190
+
191
+ return inputport_instance;
192
+ }
193
+
194
+ // Return an array of all available sources, filled with the names of the sources
195
+ static VALUE t_get_sources(VALUE self)
196
+ {
197
+ int number_of_sources = MIDIGetNumberOfSources();
198
+
199
+ VALUE source_ary = rb_ary_new2(number_of_sources);
200
+
201
+ int idx;
202
+ for(idx = 0; idx < number_of_sources; ++idx)
203
+ {
204
+ MIDIEndpointRef src = MIDIGetSource(idx);
205
+ CFStringRef pname;
206
+ char name[64];
207
+
208
+ MIDIObjectGetStringProperty(src, kMIDIPropertyName, &pname);
209
+ CFStringGetCString(pname, name, sizeof(name), 0);
210
+ CFRelease(pname);
211
+
212
+ rb_ary_push(source_ary, rb_str_new2(name));
213
+ }
214
+
215
+ return source_ary;
216
+ }
217
+
218
+ static VALUE t_get_num_sources(VALUE self)
219
+ {
220
+ return INT2FIX(MIDIGetNumberOfSources());
221
+ }
222
+
223
+ // source is identified by the index in the array returned by get_sources
224
+ // input_port is an InputPort class
225
+ static VALUE t_connect_source_to_port(VALUE self, VALUE source_idx, VALUE input_port)
226
+ {
227
+ RbInputPort* port;
228
+ Data_Get_Struct(input_port, RbInputPort, port);
229
+
230
+ MIDIEndpointRef source = MIDIGetSource(FIX2INT(source_idx));
231
+
232
+ MIDIPortConnectSource(port->input_port, source, NULL);
233
+
234
+ return Qtrue;
235
+ }
236
+
237
+ // source is identified by the index in the array returned by get_sources
238
+ // input_port is an InputPort class
239
+ static VALUE t_disconnect_source_from_port(VALUE self, VALUE source_idx, VALUE input_port)
240
+ {
241
+ RbInputPort* port;
242
+ Data_Get_Struct(input_port, RbInputPort, port);
243
+
244
+ MIDIEndpointRef source = MIDIGetSource(FIX2INT(source_idx));
245
+
246
+ MIDIPortDisconnectSource(port->input_port, source);
247
+
248
+ return Qtrue;
249
+ }
250
+
251
+ /*
252
+ *
253
+ * RbInputPort related methods
254
+ *
255
+ */
256
+
257
+ static void inputport_free(void* ptr)
258
+ {
259
+ if( ptr != NULL)
260
+ free(ptr);
261
+ }
262
+
263
+ static VALUE inputport_alloc(VALUE klass)
264
+ {
265
+ RbInputPort* port = (RbInputPort*) malloc(sizeof(RbInputPort));
266
+ port->input_port = NULL;
267
+
268
+ VALUE obj;
269
+ obj = Data_Wrap_Struct(klass, 0, inputport_free, port);
270
+
271
+ return obj;
272
+ }
273
+
274
+ static VALUE inputport_initialize(VALUE self)
275
+ {
276
+ return self;
277
+ }
278
+
279
+ /*
280
+ *
281
+ * RbMIDIClient related methods
282
+ *
283
+ */
284
+
285
+ static void midiclient_free(void* ptr)
286
+ {
287
+ if( ptr != NULL)
288
+ free(ptr);
289
+ }
290
+
291
+ static VALUE midiclient_alloc(VALUE klass)
292
+ {
293
+ RbMIDIClient* client = (RbMIDIClient*) malloc(sizeof(RbMIDIClient));
294
+ client->client = NULL;
295
+
296
+ VALUE obj;
297
+ obj = Data_Wrap_Struct(klass, 0, midiclient_free, client);
298
+
299
+ return obj;
300
+ }
301
+
302
+ static VALUE midiclient_initialize(VALUE self)
303
+ {
304
+ return self;
305
+ }
306
+
307
+ /*
308
+ *
309
+ * util methods
310
+ *
311
+ */
312
+
313
+ static void free_objects()
314
+ {
315
+ pthread_mutex_destroy(&mutex);
316
+
317
+ if( midi_data != NULL )
318
+ {
319
+ if( CFArrayGetCount(midi_data) > 0 )
320
+ {
321
+ int i;
322
+ for( i = 0; i < CFArrayGetCount(midi_data); ++i )
323
+ {
324
+ free(((const RbMIDIPacket*) CFArrayGetValueAtIndex(midi_data, i))->data);
325
+ }
326
+ }
327
+
328
+ CFRelease(midi_data);
329
+ }
330
+ }
331
+
332
+ static void init_mutex()
333
+ {
334
+ int mutex_init_result = pthread_mutex_init(&mutex, NULL);
335
+
336
+ if( mutex_init_result != 0 )
337
+ {
338
+ rb_sys_fail("Failed to allocate mutex");
339
+ }
340
+ }
341
+
342
+ static void init_midi_data()
343
+ {
344
+ midi_data = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL);
345
+
346
+ if( midi_data == NULL )
347
+ {
348
+ free_objects();
349
+ rb_sys_fail("Failed to allocate CFMutableArray");
350
+ }
351
+ }
352
+
353
+ static void install_at_exit_handler()
354
+ {
355
+ // Poor Ruby programmers destructor
356
+ if( atexit(free_objects) != 0 )
357
+ {
358
+ free_objects();
359
+ rb_sys_fail("Failed to register atexit function");
360
+ }
361
+ }
362
+
363
+ void Init_rbcoremidi()
364
+ {
365
+ init_mutex();
366
+
367
+ init_midi_data();
368
+
369
+ install_at_exit_handler();
370
+
371
+ mCoreMIDI = rb_define_module("CoreMIDI");
372
+ mCoreMIDIAPI = rb_define_module_under(mCoreMIDI, "API");
373
+
374
+ rb_define_singleton_method(mCoreMIDIAPI, "create_input_port", t_create_input_port, 2);
375
+ rb_define_singleton_method(mCoreMIDIAPI, "create_client", t_create_client, 1);
376
+ rb_define_singleton_method(mCoreMIDIAPI, "get_sources", t_get_sources, 0);
377
+ rb_define_singleton_method(mCoreMIDIAPI, "get_num_sources", t_get_num_sources, 0);
378
+ rb_define_singleton_method(mCoreMIDIAPI, "connect_source_to_port", t_connect_source_to_port, 2);
379
+ rb_define_singleton_method(mCoreMIDIAPI, "disconnect_source_from_port", t_disconnect_source_from_port, 2);
380
+ rb_define_singleton_method(mCoreMIDIAPI, "check_for_new_data", t_check_for_new_data, 0);
381
+
382
+ // Define CoreMIDI::API::InputPort class
383
+ cInputPort = rb_define_class_under(mCoreMIDIAPI, "InputPort", rb_cObject);
384
+ rb_define_alloc_func(cInputPort, inputport_alloc);
385
+ rb_define_method(cInputPort, "initialize", inputport_initialize, 0);
386
+
387
+ // Define CoreMIDI::API::MIDIClient class
388
+ cMIDIClient = rb_define_class_under(mCoreMIDIAPI, "MIDIClient", rb_cObject);
389
+ rb_define_alloc_func(cMIDIClient, midiclient_alloc);
390
+ rb_define_method(cMIDIClient, "initialize", midiclient_initialize, 0);
391
+ }
data/lib/coremidi.rb ADDED
@@ -0,0 +1,68 @@
1
+ require File.dirname(__FILE__) + '/../ext/rbcoremidi.bundle'
2
+
3
+ module CoreMIDI
4
+ module API
5
+ MidiPacket = Struct.new(:timestamp, :data)
6
+ end
7
+
8
+ # Unused, but left here for documentation
9
+ def self.number_of_sources
10
+ API.get_num_sources
11
+ end
12
+
13
+ class Packet
14
+ # http://www.srm.com/qtma/davidsmidispec.html
15
+ def self.parse(data)
16
+ spec = {
17
+ 0x80 => Events::NoteOff,
18
+ 0x90 => lambda {|data| (data[Events::NoteOn.members.index("velocity")] == 0) ? Events::NoteOff : Events::NoteOn },
19
+ 0xA0 => Events::KeyPressure,
20
+ 0xC0 => Events::ProgramChange,
21
+ 0xD0 => Events::ChannelPressure
22
+ }
23
+
24
+ klass = spec.detect {|code, _|
25
+ data[0] & 0xF0 == code # First byte is the type code
26
+ }
27
+
28
+ return Events::Unknown.new(data) if klass.nil?
29
+
30
+ klass = klass.last
31
+ klass = klass.call(data) if klass.respond_to?(:call) # Resolve any lambdas into a class
32
+
33
+ klass.new(
34
+ data[0] & 0x0F, # Second byte contains the channel
35
+ *data[1..-1]
36
+ )
37
+ end
38
+ end
39
+
40
+ module Events
41
+ class NoteOn < Struct.new(:channel, :pitch, :velocity); end;
42
+ class NoteOff < Struct.new(:channel, :pitch, :velocity); end;
43
+ class KeyPressure < Struct.new(:channel, :pitch, :pressure); end;
44
+ class ProgramChange < Struct.new(:channel, :preset); end;
45
+ class ChannelPressure < Struct.new(:channel, :pressure); end;
46
+ class Unknown < Struct.new(:data); end;
47
+ end
48
+
49
+ class Input
50
+ def self.register(client_name, port_name, source)
51
+ raise "name must be a String!" unless client_name.class == String
52
+
53
+ client = API.create_client(client_name)
54
+ port = API.create_input_port(client, port_name)
55
+ API.connect_source_to_port(API.get_sources.index(source), port)
56
+
57
+ while true
58
+ data = API.check_for_new_data
59
+ if data && !data.empty?
60
+ data.each do |packet|
61
+ yield(Packet.parse(packet.data))
62
+ end
63
+ end
64
+ sleep 0.001
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe 'CoreMIDI::Packet.parse' do
4
+ def self.it_parses(data, expected)
5
+ describe "given data #{data.inspect}, creates an event" do
6
+ before(:each) do
7
+ @packet = CoreMIDI::Packet.parse(data)
8
+ end
9
+
10
+ it "of type #{expected.class}" do
11
+ @packet.class.should == expected.class
12
+ end
13
+
14
+ expected.members.each do |member|
15
+ it "that has a #{member} of #{expected.send(member).inspect}" do
16
+ @packet.send(member).should == expected.send(member)
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ it_parses([0x90, 0x3C, 0x40], CoreMIDI::Events::NoteOn.new(0x00, 0x3C, 0x40)) # Channel 0, Middle C, half velocity
23
+ it_parses([0x91, 0x3C, 0x40], CoreMIDI::Events::NoteOn.new(0x01, 0x3C, 0x40)) # Channel 1, Middle C, half velocity
24
+ it_parses([0x80, 0x3C, 0x40], CoreMIDI::Events::NoteOff.new(0x00, 0x3C, 0x40)) # Channel 0, Middle C, half velocity
25
+ it_parses([0x81, 0x3C, 0x40], CoreMIDI::Events::NoteOff.new(0x01, 0x3C, 0x40)) # Channel 1, Middle C, half velocity
26
+ it_parses([0xC0, 0x01], CoreMIDI::Events::ProgramChange.new(0x00, 0x01)) # Channel 0, Preset #1
27
+ it_parses([0xC1, 0x02], CoreMIDI::Events::ProgramChange.new(0x01, 0x02)) # Channel 1, Preset #2
28
+ it_parses([0xA0, 0x3C, 0x64], CoreMIDI::Events::KeyPressure.new(0x00, 0x3C, 0x64)) # Channel 0, Middle C, half pressure
29
+ it_parses([0xA1, 0x3C, 0xFF], CoreMIDI::Events::KeyPressure.new(0x01, 0x3C, 0xFF)) # Channel 1, Middle C, full pressure
30
+ it_parses([0xD0, 0x64], CoreMIDI::Events::ChannelPressure.new(0x00, 0x64)) # Channel 0, half pressure
31
+ it_parses([0xD1, 0xFF], CoreMIDI::Events::ChannelPressure.new(0x01, 0xFF)) # Channel 1, full pressure
32
+
33
+ # This is technically a NoteOn event, but convention uses it most often in place of a note off event (setting velocity to 0)
34
+ it_parses([0x90, 0x3C, 0x00], CoreMIDI::Events::NoteOff.new(0x00, 0x3C, 0x00)) # Channel 0, Middle C, no velocity
35
+
36
+ describe 'when data does not match a known MIDI event,' do
37
+ it_parses([0xFF, 0xFF], CoreMIDI::Events::Unknown.new([0xFF, 0xFF]))
38
+ end
39
+
40
+ it 'creates a NoteOn event when status byte was provided by the last packet'
41
+ end
@@ -0,0 +1,5 @@
1
+ $TESTING=true
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require 'coremidi'
5
+ require 'spec'
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xaviershay-rbcoremidi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Xavier Shay
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-09-22 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A gem that provides MIDI in to ruby via OSX CoreMIDI
17
+ email: contact@rhnh.net
18
+ executables: []
19
+
20
+ extensions:
21
+ - ext/extconf.rb
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ - LICENSE
25
+ files:
26
+ - LICENSE
27
+ - README.rdoc
28
+ - Rakefile
29
+ - examples/example.rb
30
+ - ext/extconf.rb
31
+ - ext/rbcoremidi.c
32
+ - lib/coremidi.rb
33
+ - spec/parsing_spec.rb
34
+ - spec/spec_helper.rb
35
+ has_rdoc: false
36
+ homepage: http://github.com/xaviershay/rbcoremidi
37
+ licenses:
38
+ post_install_message:
39
+ rdoc_options: []
40
+
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.3.5
59
+ signing_key:
60
+ specification_version: 2
61
+ summary: A gem that provides MIDI in to ruby via OSX CoreMIDI
62
+ test_files: []
63
+