burke-coremidi 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.
@@ -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 burke-coremidi
@@ -0,0 +1,29 @@
1
+ require 'rubygems'
2
+ require 'rubygems/specification'
3
+ require 'date'
4
+ require 'spec/rake/spectask'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |s|
9
+ s.name = "coremidi"
10
+ s.summary = "A very simple access layer to the OS X CoreMIDI library."
11
+ s.email = "burke@53cr.com"
12
+ s.homepage = "http://github.com/burke/coremidi"
13
+ s.description = "A very simple access layer to the OS X CoreMIDI library."
14
+ s.authors = ["Burke Libbey"]
15
+ s.files = FileList["[A-Z]*", "{lib,spec,examples}/**/*", "ext/extconf.rb", "ext/coremidi.c"]
16
+ s.extensions = ["ext/extconf.rb"]
17
+ end
18
+ rescue LoadError
19
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
20
+ end
21
+
22
+
23
+ task :default => :spec
24
+
25
+ desc "Run specs"
26
+ Spec::Rake::SpecTask.new do |t|
27
+ t.spec_files = FileList['spec/**/*_spec.rb']
28
+ t.spec_opts = %w(-fs --color)
29
+ end
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 5
3
+ :major: 0
4
+ :minor: 0
@@ -0,0 +1,24 @@
1
+ begin
2
+ require 'rubygems'
3
+ rescue LoadError
4
+ end
5
+ require 'coremidi'
6
+
7
+ # Start archaeopteryx
8
+ # Start GarageBand (just to make sure it's all working)
9
+
10
+ # Open MIDI Patch Bay.app
11
+ # Create a new input (anyname)
12
+ # Create a new output (anyname)
13
+ # GarageBand will announce that it has found a new input
14
+ # You should have sound, yay
15
+
16
+ # Now run this script
17
+
18
+ midi_thread = Thread.new do
19
+ CoreMIDI::Input.register("Test", "Test", "Out") do |event|
20
+ puts event.inspect
21
+ end
22
+ end
23
+
24
+ gets # Stop on enter
@@ -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_coremidi()
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
+ }
@@ -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('coremidi')
@@ -0,0 +1,69 @@
1
+ require File.dirname(__FILE__) + '/../ext/coremidi.bundle'
2
+ #require 'coremidi/constants'
3
+
4
+ module CoreMIDI
5
+ module API
6
+ MidiPacket = Struct.new(:timestamp, :data)
7
+ end
8
+
9
+ # Unused, but left here for documentation
10
+ def self.number_of_sources
11
+ API.get_num_sources
12
+ end
13
+
14
+ class Packet
15
+ # http://www.srm.com/qtma/davidsmidispec.html
16
+ def self.parse(data)
17
+ spec = {
18
+ 0x80 => Events::NoteOff,
19
+ 0x90 => lambda {|data| (data[Events::NoteOn.members.index("velocity")] == 0) ? Events::NoteOff : Events::NoteOn },
20
+ 0xA0 => Events::KeyPressure,
21
+ 0xC0 => Events::ProgramChange,
22
+ 0xD0 => Events::ChannelPressure
23
+ }
24
+
25
+ klass = spec.detect {|code, _|
26
+ data[0] & 0xF0 == code # First byte is the type code
27
+ }
28
+
29
+ return Events::Unknown.new(data) if klass.nil?
30
+
31
+ klass = klass.last
32
+ klass = klass.call(data) if klass.respond_to?(:call) # Resolve any lambdas into a class
33
+
34
+ klass.new(
35
+ data[0] & 0x0F, # Second byte contains the channel
36
+ *data[1..-1]
37
+ )
38
+ end
39
+ end
40
+
41
+ module Events
42
+ class NoteOn < Struct.new(:channel, :pitch, :velocity); end;
43
+ class NoteOff < Struct.new(:channel, :pitch, :velocity); end;
44
+ class KeyPressure < Struct.new(:channel, :pitch, :pressure); end;
45
+ class ProgramChange < Struct.new(:channel, :preset); end;
46
+ class ChannelPressure < Struct.new(:channel, :pressure); end;
47
+ class Unknown < Struct.new(:data); end;
48
+ end
49
+
50
+ class Input
51
+ def self.register(client_name, port_name, source)
52
+ raise "name must be a String!" unless client_name.class == String
53
+
54
+ client = API.create_client(client_name)
55
+ port = API.create_input_port(client, port_name)
56
+ API.connect_source_to_port(API.get_sources.index(source), port)
57
+
58
+ while true
59
+ data = API.check_for_new_data
60
+ if data && !data.empty?
61
+ data.each do |packet|
62
+ yield(Packet.parse(packet.data))
63
+ end
64
+ end
65
+ sleep 0.001
66
+ end
67
+ end
68
+ end
69
+ 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,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: burke-coremidi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Burke Libbey
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-22 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A very simple access layer to the OS X CoreMIDI library.
17
+ email: burke@53cr.com
18
+ executables: []
19
+
20
+ extensions:
21
+ - ext/extconf.rb
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.rdoc
25
+ files:
26
+ - LICENSE
27
+ - README.rdoc
28
+ - Rakefile
29
+ - VERSION.yml
30
+ - examples/example.rb
31
+ - ext/coremidi.c
32
+ - ext/extconf.rb
33
+ - lib/coremidi.rb
34
+ - spec/parsing_spec.rb
35
+ - spec/spec_helper.rb
36
+ has_rdoc: true
37
+ homepage: http://github.com/burke/coremidi
38
+ post_install_message:
39
+ rdoc_options:
40
+ - --charset=UTF-8
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.2.0
59
+ signing_key:
60
+ specification_version: 2
61
+ summary: A very simple access layer to the OS X CoreMIDI library.
62
+ test_files:
63
+ - spec/parsing_spec.rb
64
+ - spec/spec_helper.rb
65
+ - examples/example.rb