shmidi 0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/Gemfile +2 -0
- data/HISTORY.md +2 -0
- data/LICENSE.txt +20 -0
- data/README.md +17 -0
- data/Rakefile +8 -0
- data/bin/shmidi +51 -0
- data/lib/shmidi/base.rb +90 -0
- data/lib/shmidi/clock.rb +46 -0
- data/lib/shmidi/composite/rgy_led.rb +34 -0
- data/lib/shmidi/control.rb +21 -0
- data/lib/shmidi/controller.rb +24 -0
- data/lib/shmidi/controls/button.rb +52 -0
- data/lib/shmidi/controls/encoder.rb +10 -0
- data/lib/shmidi/controls/fader.rb +6 -0
- data/lib/shmidi/controls/knob.rb +32 -0
- data/lib/shmidi/controls/led.rb +37 -0
- data/lib/shmidi/event.rb +102 -0
- data/lib/shmidi/ffi-coremidi-patch.rb +259 -0
- data/lib/shmidi/led_button.rb +25 -0
- data/lib/shmidi/on_off_clock.rb +27 -0
- data/lib/shmidi/socket.rb +78 -0
- data/lib/shmidi/switch.rb +28 -0
- data/lib/shmidi/version.rb +5 -0
- data/lib/shmidi.rb +100 -0
- data/shmidi.gemspec +28 -0
- data/shmidi.sublime-project +30 -0
- data/test/xonek1-ledtest.json +42 -0
- metadata +184 -0
@@ -0,0 +1,259 @@
|
|
1
|
+
module CoreMIDI
|
2
|
+
|
3
|
+
# Type of endpoint used for input
|
4
|
+
class Source
|
5
|
+
|
6
|
+
include Endpoint
|
7
|
+
|
8
|
+
attr_reader :buffer
|
9
|
+
|
10
|
+
#
|
11
|
+
# An array of MIDI event hashes as such:
|
12
|
+
# [
|
13
|
+
# { :data => [144, 60, 100], :timestamp => 1024 },
|
14
|
+
# { :data => [128, 60, 100], :timestamp => 1100 },
|
15
|
+
# { :data => [144, 40, 120], :timestamp => 1200 }
|
16
|
+
# ]
|
17
|
+
#
|
18
|
+
# The data is an array of Numeric bytes
|
19
|
+
# The timestamp is the number of millis since this input was enabled
|
20
|
+
#
|
21
|
+
# @return [Array<Hash>]
|
22
|
+
def gets
|
23
|
+
# SINM ADDED
|
24
|
+
@queue.pop
|
25
|
+
# SINM COMMENTED OUT
|
26
|
+
# until queued_messages?
|
27
|
+
# # per https://github.com/arirusso/unimidi/issues/20#issuecomment-44761318
|
28
|
+
# sleep(0.0001) # patch to prevent 100% CPU issue with some midi controllers
|
29
|
+
# end
|
30
|
+
# messages = queued_messages
|
31
|
+
# @pointer = @buffer.length
|
32
|
+
# messages
|
33
|
+
end
|
34
|
+
alias_method :read, :gets
|
35
|
+
|
36
|
+
# Same as Source#gets except that it returns message data as string of hex
|
37
|
+
# digits as such:
|
38
|
+
# [
|
39
|
+
# { :data => "904060", :timestamp => 904 },
|
40
|
+
# { :data => "804060", :timestamp => 1150 },
|
41
|
+
# { :data => "90447F", :timestamp => 1300 }
|
42
|
+
# ]
|
43
|
+
#
|
44
|
+
# @return [Array<Hash>]
|
45
|
+
def gets_s
|
46
|
+
messages = gets
|
47
|
+
messages.each do |message|
|
48
|
+
message[:data] = TypeConversion.numeric_bytes_to_hex_string(message[:data])
|
49
|
+
end
|
50
|
+
messages
|
51
|
+
end
|
52
|
+
alias_method :gets_bytestr, :gets_s
|
53
|
+
|
54
|
+
# Enable this the input for use; can be passed a block
|
55
|
+
# @return [Source]
|
56
|
+
def enable(options = {}, &block)
|
57
|
+
@enabled = true unless @enabled
|
58
|
+
if block_given?
|
59
|
+
begin
|
60
|
+
yield(self)
|
61
|
+
ensure
|
62
|
+
close
|
63
|
+
end
|
64
|
+
end
|
65
|
+
self
|
66
|
+
end
|
67
|
+
alias_method :open, :enable
|
68
|
+
alias_method :start, :enable
|
69
|
+
|
70
|
+
# Close this input
|
71
|
+
# @return [Boolean]
|
72
|
+
def close
|
73
|
+
#error = API.MIDIPortDisconnectSource( @handle, @resource )
|
74
|
+
#raise "MIDIPortDisconnectSource returned error code #{error}" unless error.zero?
|
75
|
+
#error = API.MIDIClientDispose(@handle)
|
76
|
+
#raise "MIDIClientDispose returned error code #{error}" unless error.zero?
|
77
|
+
#error = API.MIDIPortDispose(@handle)
|
78
|
+
#raise "MIDIPortDispose returned error code #{error}" unless error.zero?
|
79
|
+
#error = API.MIDIEndpointDispose(@resource)
|
80
|
+
#raise "MIDIEndpointDispose returned error code #{error}" unless error.zero?
|
81
|
+
if @enabled
|
82
|
+
@enabled = false
|
83
|
+
true
|
84
|
+
else
|
85
|
+
false
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Shortcut to the first available input endpoint
|
90
|
+
# @return [Source]
|
91
|
+
def self.first
|
92
|
+
Endpoint.first(:source)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Shortcut to the last available input endpoint
|
96
|
+
# @return [Source]
|
97
|
+
def self.last
|
98
|
+
Endpoint.last(:source)
|
99
|
+
end
|
100
|
+
|
101
|
+
# All input endpoints
|
102
|
+
# @return [Array<Source>]
|
103
|
+
def self.all
|
104
|
+
Endpoint.all_by_type[:source]
|
105
|
+
end
|
106
|
+
|
107
|
+
protected
|
108
|
+
|
109
|
+
# Base initialization for this endpoint -- done whether or not the endpoint is enabled to check whether
|
110
|
+
# it is truly available for use
|
111
|
+
def connect
|
112
|
+
enable_client
|
113
|
+
initialize_port
|
114
|
+
@resource = API.MIDIEntityGetSource(@entity.resource, @resource_id)
|
115
|
+
error = API.MIDIPortConnectSource(@handle, @resource, nil )
|
116
|
+
initialize_buffer
|
117
|
+
# SINM ADDED
|
118
|
+
@queue = Queue.new
|
119
|
+
|
120
|
+
@sysex_buffer = []
|
121
|
+
@start_time = Time.now.to_f
|
122
|
+
|
123
|
+
error.zero?
|
124
|
+
end
|
125
|
+
alias_method :connect?, :connect
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# Add a single message to the buffer
|
130
|
+
# @param [Array<Fixnum>] bytes Message data
|
131
|
+
# @param [Float] timestamp The system float timestamp
|
132
|
+
# @return [Array<Hash>] The resulting buffer
|
133
|
+
def enqueue_message(bytes, timestamp)
|
134
|
+
if bytes.first.eql?(0xF0) || !@sysex_buffer.empty?
|
135
|
+
@sysex_buffer += bytes
|
136
|
+
if bytes.last.eql?(0xF7)
|
137
|
+
bytes = @sysex_buffer.dup
|
138
|
+
@sysex_buffer.clear
|
139
|
+
end
|
140
|
+
end
|
141
|
+
# SINM ADDED
|
142
|
+
@sysex_buffer.empty? ? get_message_formatted(bytes, timestamp) : nil
|
143
|
+
# SINM COMMENTED OUT
|
144
|
+
# @buffer << get_message_formatted(bytes, timestamp) if @sysex_buffer.empty?
|
145
|
+
# @buffer
|
146
|
+
end
|
147
|
+
|
148
|
+
# New MIDI messages from the queue
|
149
|
+
def queued_messages
|
150
|
+
@buffer.slice(@pointer, @buffer.length - @pointer)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Are there new MIDI messages in the queue?
|
154
|
+
def queued_messages?
|
155
|
+
@pointer < @buffer.length
|
156
|
+
end
|
157
|
+
|
158
|
+
# The callback fired by coremidi when new MIDI messages are in the buffer
|
159
|
+
def get_event_callback
|
160
|
+
Proc.new do |new_packets, refCon_ptr, connRefCon_ptr|
|
161
|
+
begin
|
162
|
+
# p "packets received: #{new_packets[:numPackets]}"
|
163
|
+
timestamp = Time.now.to_f
|
164
|
+
messages = get_messages(new_packets)
|
165
|
+
# SINM ADDED
|
166
|
+
@queue.push(messages.map do |message|
|
167
|
+
enqueue_message(message, timestamp)
|
168
|
+
end.compact)
|
169
|
+
# SINM COMMENTED OUT
|
170
|
+
# messages.each { |message| enqueue_message(message, timestamp) }
|
171
|
+
rescue Exception => exception
|
172
|
+
Thread.main.raise(exception)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Get MIDI messages from the given CoreMIDI packet list
|
178
|
+
# @param [API::MIDIPacketList] new_packets The packet list
|
179
|
+
# @return [Array<Array<Fixnum>>] A collection of MIDI messages
|
180
|
+
def get_messages(packet_list)
|
181
|
+
count = packet_list[:numPackets]
|
182
|
+
first = packet_list[:packet][0]
|
183
|
+
data = first[:data].to_a
|
184
|
+
messages = []
|
185
|
+
messages << data.slice!(0, first[:length])
|
186
|
+
(count - 1).times do |i|
|
187
|
+
length_index = find_next_length_index(data)
|
188
|
+
message_length = data[length_index]
|
189
|
+
unless message_length.nil?
|
190
|
+
packet_start_index = length_index + 2
|
191
|
+
packet_end_index = packet_start_index + message_length
|
192
|
+
if data.length >= packet_end_index + 1
|
193
|
+
packet = data.slice!(0..packet_end_index)
|
194
|
+
message = packet.slice(packet_start_index, message_length)
|
195
|
+
messages << message
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
messages
|
200
|
+
end
|
201
|
+
|
202
|
+
# Get the next index for "length" from the blob of MIDI data
|
203
|
+
# @param [Array<Fixnum>] data
|
204
|
+
# @return [Fixnum]
|
205
|
+
def find_next_length_index(data)
|
206
|
+
last_is_zero = false
|
207
|
+
data.each_with_index do |num, i|
|
208
|
+
if num.zero?
|
209
|
+
if last_is_zero
|
210
|
+
return i + 1
|
211
|
+
else
|
212
|
+
last_is_zero = true
|
213
|
+
end
|
214
|
+
else
|
215
|
+
last_is_zero = false
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# Timestamp for a received MIDI message
|
221
|
+
# @return [Fixnum]
|
222
|
+
def timestamp(now)
|
223
|
+
(now - @start_time) * 1000
|
224
|
+
end
|
225
|
+
|
226
|
+
# Give a message its timestamp and package it in a Hash
|
227
|
+
# @return [Hash]
|
228
|
+
def get_message_formatted(raw, time)
|
229
|
+
{
|
230
|
+
:data => raw,
|
231
|
+
:timestamp => timestamp(time)
|
232
|
+
}
|
233
|
+
end
|
234
|
+
|
235
|
+
# Initialize a coremidi port for this endpoint
|
236
|
+
# @return [Boolean]
|
237
|
+
def initialize_port
|
238
|
+
@callback = get_event_callback
|
239
|
+
port = API.create_midi_input_port(@client, @resource_id, @name, @callback)
|
240
|
+
@handle = port[:handle]
|
241
|
+
raise "MIDIInputPortCreate returned error code #{port[:error]}" unless port[:error].zero?
|
242
|
+
true
|
243
|
+
end
|
244
|
+
|
245
|
+
# Initialize the MIDI message buffer
|
246
|
+
# @return [Boolean]
|
247
|
+
def initialize_buffer
|
248
|
+
@pointer = 0
|
249
|
+
@buffer = []
|
250
|
+
def @buffer.clear
|
251
|
+
super
|
252
|
+
@pointer = 0
|
253
|
+
end
|
254
|
+
true
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Shmidi
|
3
|
+
class LedButton < Control
|
4
|
+
CTYPE = :LEDBUT
|
5
|
+
attr_reader :button, :led
|
6
|
+
|
7
|
+
def initialize(id, socket, channel, note, led_note = nil)
|
8
|
+
super(id, socket, channel, note)
|
9
|
+
@button = Button.new(id, socket, channel, note)
|
10
|
+
@led = Led.new(id, socket, channel, led_note || note)
|
11
|
+
@button.on_press(&lambda { |button| on_button_press(button) })
|
12
|
+
@button.on_release(&lambda { |button| on_button_release(button) })
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def on_button_press(button)
|
18
|
+
@led.turn_on
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_button_release(button)
|
22
|
+
@led.turn_off
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Shmidi
|
3
|
+
class OnOffClock < Clock
|
4
|
+
def initialize(socket, delay = 0.2, delay_off = 0.2)
|
5
|
+
@next_on = false
|
6
|
+
super(socket, delay)
|
7
|
+
@delay_off = delay_off
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def filter
|
13
|
+
b = @buffer
|
14
|
+
@buffer = []
|
15
|
+
bb = b.select { |e| e.message == (@next_on ? :on : :off) }
|
16
|
+
b = b - bb
|
17
|
+
@buffer = @buffer + b
|
18
|
+
bb
|
19
|
+
end
|
20
|
+
|
21
|
+
def wait
|
22
|
+
d = @next_on ? @delay : @delay_off
|
23
|
+
@next_on = !@next_on
|
24
|
+
sleep(d)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Shmidi
|
3
|
+
class Socket
|
4
|
+
include Base
|
5
|
+
|
6
|
+
@@controllers = {}
|
7
|
+
def self.[](name)
|
8
|
+
@@controllers[name]
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :name, :in, :out
|
12
|
+
|
13
|
+
def initialize(name, in_id, out_id)
|
14
|
+
@name = name
|
15
|
+
@in = in_id
|
16
|
+
@out = out_id
|
17
|
+
init
|
18
|
+
end
|
19
|
+
|
20
|
+
def init
|
21
|
+
@__in_dev = UniMIDI::Input.all.find { |d| d.name == @in }
|
22
|
+
@__out_dev = UniMIDI::Output.all.find { |d| d.name == @out }
|
23
|
+
|
24
|
+
@@controllers[@name] = self
|
25
|
+
@__queue = Queue.new
|
26
|
+
@__on_event = []
|
27
|
+
# @__sync_threads = Hash.new { |hash, key| hash[key] = Clock.new(key, self) }
|
28
|
+
@__listener = Thread.new do
|
29
|
+
loop do
|
30
|
+
break unless @__in_dev
|
31
|
+
begin
|
32
|
+
@__in_dev.gets.each do |event|
|
33
|
+
event[:source] = @name
|
34
|
+
event = Event.new(event)
|
35
|
+
Shmidi.TRACE_INTERNAL("> #{@name}\t#{event}")
|
36
|
+
@__queue.push(event)
|
37
|
+
@__on_event.each do |rule|
|
38
|
+
next if (channel = rule[:channel]) && channel != event.channel
|
39
|
+
next if (message = rule[:message]) && message != event.message
|
40
|
+
next if (note = rule[:note]) && note != event.note
|
41
|
+
next if (value = rule[:value]) && value != event.value
|
42
|
+
|
43
|
+
rule[:block].call(event) rescue Shmidi.ON_EXCEPTION
|
44
|
+
end
|
45
|
+
end
|
46
|
+
rescue
|
47
|
+
Shmidi.ON_EXCEPTION
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def on_event(channel = nil, message = nil, note = nil, value = nil, &block)
|
54
|
+
@__on_event << {
|
55
|
+
:block => block,
|
56
|
+
:channel => channel,
|
57
|
+
:message => message,
|
58
|
+
:note => note
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def push(events)
|
63
|
+
events = Array(events).reduce([]) do |array, event|
|
64
|
+
Shmidi.TRACE_EXTERNAL("< #{@name}\t#{event}")
|
65
|
+
array << event.data
|
66
|
+
array
|
67
|
+
end
|
68
|
+
@__out_dev.puts(*events)
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.print_device_list
|
72
|
+
$stdout.puts('Inputs:')
|
73
|
+
UniMIDI::Input.list
|
74
|
+
$stdout.puts('Outputs:')
|
75
|
+
UniMIDI::Output.list
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
module Shmidi
|
3
|
+
class Switch < LedButton
|
4
|
+
CTYPE = :SWI
|
5
|
+
attr_reader :switch_state
|
6
|
+
def initialize(id, socket, channel, note, led_note = nil)
|
7
|
+
super(id, socket, channel, note, led_note)
|
8
|
+
@switch_state = false
|
9
|
+
@on_switch = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def on_switch_state(&block)
|
13
|
+
@on_switch << block
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def on_button_press(button)
|
19
|
+
(@switch_state = !@switch_state) ? @led.turn_on : @led.turn_off
|
20
|
+
Shmidi.TRACE("#{CTYPE}\t#{@id}\tSTATE\t#{@switch_state ? :ON : :OFF}")
|
21
|
+
@on_switch.each { |b| b.call(self) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_button_release(button)
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/shmidi.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'shmidi/version'
|
3
|
+
|
4
|
+
require 'timeout'
|
5
|
+
|
6
|
+
require 'unimidi'
|
7
|
+
require 'shmidi/ffi-coremidi-patch'
|
8
|
+
|
9
|
+
module Shmidi
|
10
|
+
PROFILE = !!ENV['PROFILE']
|
11
|
+
TRACE = !!ENV['TRACE']
|
12
|
+
TRACE_EXTERNAL = !!ENV['TRACE_EXTERNAL']
|
13
|
+
TRACE_INTERNAL = !!ENV['TRACE_INTERNAL']
|
14
|
+
|
15
|
+
def self.timestamp
|
16
|
+
t = Time.now
|
17
|
+
(t.to_i * 1000) + (t.usec / 1000)
|
18
|
+
end
|
19
|
+
|
20
|
+
@@trace_queue = Queue.new
|
21
|
+
@@trace_thread = Thread.new do
|
22
|
+
loop do
|
23
|
+
begin
|
24
|
+
str = @@trace_queue.pop
|
25
|
+
$stderr.puts("#{timestamp}\t#{str}")
|
26
|
+
rescue
|
27
|
+
$stderr.puts("Error in trace thread: #{$!}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.TRACE(str)
|
33
|
+
return nil unless TRACE
|
34
|
+
@@trace_queue.push(str)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.TRACE_EXTERNAL(str)
|
38
|
+
return nil unless TRACE_EXTERNAL
|
39
|
+
@@trace_queue.push(str)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.TRACE_INTERNAL(str)
|
43
|
+
return nil unless TRACE_INTERNAL
|
44
|
+
@@trace_queue.push(str)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.ON_EXCEPTION
|
48
|
+
back = $!.backtrace.join("\n\t\t")
|
49
|
+
@@trace_queue.push("ERROR\t#{$!.class.name}:#{$!}\n\t\t#{back}")
|
50
|
+
end
|
51
|
+
|
52
|
+
require 'oj'
|
53
|
+
JSON_CREATE_ID = 'm_type'
|
54
|
+
::Oj.default_options = {
|
55
|
+
:mode => :compat,
|
56
|
+
:class_cache => true,
|
57
|
+
:create_id => JSON_CREATE_ID,
|
58
|
+
:time => :unix
|
59
|
+
}
|
60
|
+
|
61
|
+
def self.DUMP(obj, opts={})
|
62
|
+
Oj.dump((obj.kind_of?(Base) ? obj.to_hash : obj), opts)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.JSON_PARSE str, opts = {:warn => true}
|
66
|
+
return nil if str.nil?
|
67
|
+
Oj.load(str, opts)
|
68
|
+
rescue
|
69
|
+
if opts[:warn]
|
70
|
+
Shmidi::ON_EXCEPTION
|
71
|
+
TRACE "#{str}".force_encoding(Encoding::UTF_8)
|
72
|
+
end
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.PRETTY obj
|
77
|
+
DUMP((obj.kind_of?(String) ? JSON_PARSE(obj) : obj), :indent=>2)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
require 'shmidi/base'
|
82
|
+
|
83
|
+
require 'shmidi/socket'
|
84
|
+
require 'shmidi/event'
|
85
|
+
require 'shmidi/controller'
|
86
|
+
|
87
|
+
require 'shmidi/clock'
|
88
|
+
require 'shmidi/on_off_clock'
|
89
|
+
|
90
|
+
require 'shmidi/control'
|
91
|
+
require 'shmidi/controls/led'
|
92
|
+
require 'shmidi/composite/rgy_led'
|
93
|
+
require 'shmidi/controls/knob'
|
94
|
+
require 'shmidi/controls/fader'
|
95
|
+
require 'shmidi/controls/encoder'
|
96
|
+
require 'shmidi/controls/button'
|
97
|
+
require 'shmidi/led_button'
|
98
|
+
require 'shmidi/switch'
|
99
|
+
|
100
|
+
|
data/shmidi.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require File.expand_path('../lib/shmidi/version.rb', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'shmidi'
|
6
|
+
s.version = Shmidi::VERSION
|
7
|
+
s.authors = ['sinm']
|
8
|
+
s.email = 'sinm.sinm@gmail.com'
|
9
|
+
s.summary = 'Midi experiments'
|
10
|
+
s.description = '== Midi experiments'
|
11
|
+
s.homepage = 'https://github.com/sinm/shmidi'
|
12
|
+
s.license = 'MIT'
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- spec/`.split("\n")
|
15
|
+
s.require_paths = ['lib']
|
16
|
+
s.bindir = 'bin'
|
17
|
+
s.executables = `git ls-files -- bin/`.split("\n").map{|f| File.basename(f)}
|
18
|
+
s.add_development_dependency 'bundler', '~> 1.7'
|
19
|
+
s.add_development_dependency 'rake', '~> 10.1'
|
20
|
+
s.add_development_dependency 'minitest', '~> 4.7'
|
21
|
+
# NOTE: it's useful to install
|
22
|
+
# specified version of perftools.rb w/o bundler first
|
23
|
+
s.add_development_dependency 'perftools.rb', '~> 2.0.4'
|
24
|
+
s.add_dependency 'ffi-coremidi', '0.3.8'
|
25
|
+
s.add_dependency 'unimidi', '0.4.6'
|
26
|
+
s.add_dependency 'argparser', '~> 2.1'
|
27
|
+
s.add_dependency 'oj', '~> 2.12.12'
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
{
|
2
|
+
"folders":
|
3
|
+
[
|
4
|
+
{
|
5
|
+
"path": "."
|
6
|
+
},
|
7
|
+
{
|
8
|
+
"path": "~/.rbenv/versions/2.1.2/lib/ruby/gems/2.1.0/gems"
|
9
|
+
}
|
10
|
+
],
|
11
|
+
"build_systems":
|
12
|
+
[
|
13
|
+
{
|
14
|
+
"name": "Run tests",
|
15
|
+
"cmd": ["bundle", "exec", "rake", "test"],
|
16
|
+
"selector": "source.rb"
|
17
|
+
}
|
18
|
+
],
|
19
|
+
"settings":
|
20
|
+
{
|
21
|
+
"ensure_newline_at_eof_on_save": true,
|
22
|
+
"rulers":
|
23
|
+
[
|
24
|
+
80
|
25
|
+
],
|
26
|
+
"tab_size": 2,
|
27
|
+
"translate_tabs_to_spaces": true,
|
28
|
+
"trim_trailing_white_space_on_save": true
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
{
|
2
|
+
"m_type":"Shmidi::Controller",
|
3
|
+
"name":"xonek1-ledtest",
|
4
|
+
"internals":[
|
5
|
+
[
|
6
|
+
{
|
7
|
+
"m_type":"Shmidi::Socket",
|
8
|
+
"name":"xone",
|
9
|
+
"in":"ALLEN&HEATH LTD. XONE:K1",
|
10
|
+
"out":"ALLEN&HEATH LTD. XONE:K1"
|
11
|
+
}
|
12
|
+
],
|
13
|
+
{
|
14
|
+
"led":{
|
15
|
+
"m_type":"Shmidi::RGYLed",
|
16
|
+
"leds":{
|
17
|
+
"red":{
|
18
|
+
"m_type":"Shmidi::Led",
|
19
|
+
"socket":"xone",
|
20
|
+
"channel":15,
|
21
|
+
"note":"C0",
|
22
|
+
"turned_on":false
|
23
|
+
},
|
24
|
+
"green":{
|
25
|
+
"m_type":"Shmidi::Led",
|
26
|
+
"socket":"xone",
|
27
|
+
"channel":15,
|
28
|
+
"note":"G#0",
|
29
|
+
"turned_on":false
|
30
|
+
},
|
31
|
+
"yellow":{
|
32
|
+
"m_type":"Shmidi::Led",
|
33
|
+
"socket":"xone",
|
34
|
+
"channel":15,
|
35
|
+
"note":"E0",
|
36
|
+
"turned_on":false
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
]
|
42
|
+
}
|