shmidi 0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|