alsa-rawmidi 0.3.2 → 0.4.0
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 +5 -5
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/lib/alsa-rawmidi/api.rb +116 -124
- data/lib/alsa-rawmidi/device.rb +18 -22
- data/lib/alsa-rawmidi/input.rb +14 -41
- data/lib/alsa-rawmidi/output.rb +12 -14
- data/lib/alsa-rawmidi/soundcard.rb +12 -16
- data/lib/alsa-rawmidi/type_conversion.rb +33 -0
- data/lib/alsa-rawmidi/version.rb +3 -3
- data/lib/alsa-rawmidi.rb +11 -9
- metadata +76 -13
- data/test/helper.rb +0 -51
- data/test/input_buffer_test.rb +0 -46
- data/test/io_test.rb +0 -80
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 0be8f5761da9a747ce69063ebc6b5f4242986a37574c6119d5c909e30fd4b929
|
|
4
|
+
data.tar.gz: 3db20d183d1b117c18259dd06442efa6165bf7c99e1dc0746fc07e87fd906846
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a89bb97349a978c8557b31ed155f1d215e2aa9b957df291033c23ca51f064b08b6d7c58f808adc283783e34b0e72f2327cf1cba43d178c4be829225ece39828d
|
|
7
|
+
data.tar.gz: 7fdc7bfbce4f006f51c29a2dc9b45aebf64b92284be1fb50291267292c187d5fbc687daea44d9c2a1ea0e00761be4a57ca1f850dc78a7d6771d763c0895449b7
|
data/LICENSE
CHANGED
data/README.md
CHANGED
data/lib/alsa-rawmidi/api.rb
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module AlsaRawMIDI
|
|
3
4
|
# libasound RawMIDI struct, enum and function bindings
|
|
4
5
|
module API
|
|
5
|
-
|
|
6
6
|
extend FFI::Library
|
|
7
|
-
ffi_lib
|
|
7
|
+
ffi_lib 'libasound'
|
|
8
8
|
|
|
9
9
|
CONSTANTS = {
|
|
10
|
-
:
|
|
11
|
-
:
|
|
12
|
-
:
|
|
13
|
-
:
|
|
14
|
-
:
|
|
15
|
-
}
|
|
10
|
+
SND_RAWMIDI_STREAM_OUTPUT: 0,
|
|
11
|
+
SND_RAWMIDI_STREAM_INPUT: 1,
|
|
12
|
+
SND_RAWMIDI_APPEND: 0x0001,
|
|
13
|
+
SND_RAWMIDI_NONBLOCK: 0x0002,
|
|
14
|
+
SND_RAWMIDI_SYNC: 0x0004
|
|
15
|
+
}.freeze
|
|
16
16
|
|
|
17
17
|
typedef :ulong, :SndCtlType
|
|
18
18
|
typedef :ulong, :SndCtl
|
|
@@ -21,72 +21,72 @@ module AlsaRawMIDI
|
|
|
21
21
|
# snd_ctl
|
|
22
22
|
class SndCtl < FFI::Struct
|
|
23
23
|
layout :dl_handle, :pointer, # void*
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
:name, :pointer, # char*
|
|
25
|
+
:type, :SndCtlType,
|
|
26
|
+
:ops, :pointer, # const snd_ctl_ops_t*
|
|
27
|
+
:private_data, :pointer, # void*
|
|
28
|
+
:nonblock, :ulong,
|
|
29
|
+
:poll_fd, :ulong,
|
|
30
|
+
:async_handlers, :ulong
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
# snd_ctl_card_info
|
|
34
34
|
class SndCtlCardInfo < FFI::Struct
|
|
35
35
|
layout :card, :int, # card number
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
:pad, :int, # reserved for future (was type)
|
|
37
|
+
:id, [:uchar, 16], # ID of card (user selectable)
|
|
38
|
+
:driver, [:uchar, 16], # Driver name
|
|
39
|
+
:name, [:uchar, 32], # Short name of soundcard
|
|
40
|
+
:longname, [:uchar, 80], # name + info text about soundcard
|
|
41
|
+
:reserved_, [:uchar, 16], # reserved for future (was ID of mixer)
|
|
42
|
+
:mixername, [:uchar, 80], # visual mixer identification
|
|
43
|
+
:components, [:uchar, 128] # card components / fine identification, delimited with one space (AC97 etc..)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
# snd_rawmidi_info
|
|
47
47
|
class SndRawMIDIInfo < FFI::Struct
|
|
48
48
|
layout :device, :uint, # RO/WR (control): device number
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
:subdevice, :uint, # RO/WR (control): subdevice number
|
|
50
|
+
:stream, :int, # WR: stream
|
|
51
|
+
:card, :int, # R: card number
|
|
52
|
+
:flags, :uint, # SNDRV_RAWMIDI_INFO_XXXX
|
|
53
|
+
:id, [:uchar, 64], # ID (user selectable)
|
|
54
|
+
:name, [:uchar, 80], # name of device
|
|
55
|
+
:subname, [:uchar, 32], # name of active or selected subdevice
|
|
56
|
+
:subdevices_count, :uint,
|
|
57
|
+
:subdevices_avail, :uint,
|
|
58
|
+
:reserved, [:uchar, 64] # reserved for future use
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
# timespec
|
|
62
62
|
class Timespec < FFI::Struct
|
|
63
63
|
layout :tv_sec, :time_t, # Seconds since 00:00:00 GMT
|
|
64
|
-
|
|
64
|
+
:tv_nsec, :long # Additional nanoseconds since
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
# snd_rawmidi_status
|
|
68
68
|
class SndRawMIDIStatus < FFI::Struct
|
|
69
69
|
layout :stream, :int,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
:timestamp, Timespec.by_value, # Timestamp
|
|
71
|
+
:avail, :size_t, # available bytes
|
|
72
|
+
:xruns, :size_t, # count of overruns since last status (in bytes)
|
|
73
|
+
:reserved, [:uchar, 64] # reserved for future use
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
# Simple doubly linked list implementation
|
|
77
77
|
class LinkedList < FFI::Struct
|
|
78
78
|
layout :next, :pointer, # *LinkedList
|
|
79
|
-
|
|
79
|
+
:prev, :pointer # *LinkedList
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
# snd_rawmidi
|
|
83
83
|
class SndRawMIDI < FFI::Struct
|
|
84
84
|
layout :card, :pointer, # *snd_card
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
:list, LinkedList.by_value,
|
|
86
|
+
:device, :uint, # device number
|
|
87
|
+
:info_flags, :uint, # SNDRV_RAWMIDI_INFO_XXXX
|
|
88
|
+
:id, [:char, 64],
|
|
89
|
+
:name, [:char, 80]
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
# spinlock_t
|
|
@@ -97,7 +97,7 @@ module AlsaRawMIDI
|
|
|
97
97
|
# wait_queue_head_t
|
|
98
98
|
class WaitQueueHead < FFI::Struct
|
|
99
99
|
layout :lock, Spinlock.by_value,
|
|
100
|
-
|
|
100
|
+
:task_list, LinkedList.by_value
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
class AtomicT < FFI::Struct
|
|
@@ -105,43 +105,43 @@ module AlsaRawMIDI
|
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
class Tasklet < FFI::Struct
|
|
108
|
-
layout :next, :pointer,
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
108
|
+
layout :next, :pointer, # pointer to the next tasklet in the list / void (*func) (unsigned long)
|
|
109
|
+
:state, :ulong, # state of the tasklet
|
|
110
|
+
:count, AtomicT.by_value, # reference counter
|
|
111
|
+
:func, :pointer, # tasklet handler function / void (*func) (unsigned long)
|
|
112
|
+
:data, :ulong # argument to the tasklet function
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
# snd_rawmidi_runtime
|
|
116
116
|
class SndRawMIDIRuntime < FFI::Struct
|
|
117
117
|
layout :drain, :uint, 1, # drain stage
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
118
|
+
:oss, :uint, 1, # OSS compatible mode
|
|
119
|
+
# midi stream buffer
|
|
120
|
+
:buffer, :pointer, # uchar* / buffer for MIDI data
|
|
121
|
+
:buffer_size, :size_t, # size of buffer
|
|
122
|
+
:appl_ptr, :size_t, # application pointer
|
|
123
|
+
:hw_ptr, :size_t, # hardware pointer
|
|
124
|
+
:avail_min, :size_t, # min avail for wakeup
|
|
125
|
+
:avail, :size_t, # max used buffer for wakeup
|
|
126
|
+
:xruns, :size_t, # over/underruns counter
|
|
127
|
+
# misc
|
|
128
|
+
:lock, Spinlock.by_value,
|
|
129
|
+
:sleep, WaitQueueHead.by_value,
|
|
130
|
+
# event handler (new bytes, input only)
|
|
131
|
+
:substream, :pointer, # void (*event)(struct snd_rawmidi_substream *substream);
|
|
132
|
+
# defers calls to event [input] or ops->trigger [output]
|
|
133
|
+
:tasklet, Tasklet.by_value,
|
|
134
|
+
:private_data, :pointer, # void*
|
|
135
|
+
:private_free, :pointer # void (*private_free)(struct snd_rawmidi_substream *substream)
|
|
136
136
|
end
|
|
137
137
|
|
|
138
138
|
# snd_rawmidi_params
|
|
139
139
|
class SndRawMIDIParams < FFI::Struct
|
|
140
140
|
layout :stream, :int,
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
:buffer_size, :size_t, # queue size in bytes
|
|
142
|
+
:avail_min, :size_t, # minimum avail bytes for wakeup
|
|
143
|
+
:no_active_sensing, :uint, 1, # do not send active sensing byte in close()
|
|
144
|
+
:reserved, [:uchar, 16] # reserved for future use
|
|
145
145
|
end
|
|
146
146
|
|
|
147
147
|
#
|
|
@@ -155,30 +155,30 @@ module AlsaRawMIDI
|
|
|
155
155
|
# Convert card string to an integer value.
|
|
156
156
|
attach_function :snd_card_get_index, [:pointer], :int # (const char* name)
|
|
157
157
|
# Obtain the card name.
|
|
158
|
-
attach_function :snd_card_get_name, [
|
|
158
|
+
attach_function :snd_card_get_name, %i[int pointer], :int # (int card, char **name)
|
|
159
159
|
# Obtain the card long name.
|
|
160
|
-
attach_function :snd_card_get_longname, [
|
|
160
|
+
attach_function :snd_card_get_longname, %i[int pointer], :int # (int card, char **name)
|
|
161
161
|
|
|
162
162
|
#
|
|
163
163
|
# snd_ctl
|
|
164
164
|
#
|
|
165
165
|
|
|
166
166
|
# Opens a CTL.
|
|
167
|
-
attach_function :snd_ctl_open, [
|
|
167
|
+
attach_function :snd_ctl_open, %i[pointer pointer int], :int # (snd_ctl_t **ctl, const char *name, int mode)
|
|
168
168
|
# Opens a CTL using local configuration.
|
|
169
|
-
attach_function :snd_ctl_open_lconf, [
|
|
169
|
+
attach_function :snd_ctl_open_lconf, %i[pointer pointer int pointer], :int # (snd_ctl_t **ctl, const char *name, int mode, snd_config_t *lconf)
|
|
170
170
|
# close CTL handle
|
|
171
|
-
attach_function :snd_ctl_close, [:pointer], :int #(snd_ctl_t *ctl)
|
|
171
|
+
attach_function :snd_ctl_close, [:pointer], :int # (snd_ctl_t *ctl)
|
|
172
172
|
# set nonblock mode
|
|
173
|
-
attach_function :snd_ctl_nonblock, [
|
|
173
|
+
attach_function :snd_ctl_nonblock, %i[pointer int], :int # (snd_ctl_t *ctl, int nonblock)
|
|
174
174
|
# Get card related information.
|
|
175
|
-
attach_function :snd_ctl_card_info, [
|
|
175
|
+
attach_function :snd_ctl_card_info, %i[pointer pointer], :int # (snd_ctl_t *ctl, snd_ctl_card_info_t *info)
|
|
176
176
|
# Get card name from a CTL card info.
|
|
177
177
|
attach_function :snd_ctl_card_info_get_name, [:pointer], :string # (const snd_ctl_card_info_t *obj) / const char*
|
|
178
178
|
# Get info about a RawMidi device.
|
|
179
|
-
attach_function :snd_ctl_rawmidi_info, [
|
|
179
|
+
attach_function :snd_ctl_rawmidi_info, %i[SndCtl pointer], :int # (snd_ctl_t *ctl, snd_rawmidi_info_t *info)
|
|
180
180
|
# Get next RawMidi device number.
|
|
181
|
-
attach_function :snd_ctl_rawmidi_next_device, [
|
|
181
|
+
attach_function :snd_ctl_rawmidi_next_device, %i[SndCtl pointer], :int # (snd_ctl_t *ctl, int *device)
|
|
182
182
|
|
|
183
183
|
#
|
|
184
184
|
# snd_rawmidi
|
|
@@ -191,36 +191,36 @@ module AlsaRawMIDI
|
|
|
191
191
|
# drop all bytes in the rawmidi I/O ring buffer immediately
|
|
192
192
|
attach_function :snd_rawmidi_drop, [:SndRawMIDI], :int # int ( snd_rawmidi_t * rawmidi)
|
|
193
193
|
# set nonblock mode
|
|
194
|
-
attach_function :snd_rawmidi_nonblock, [
|
|
194
|
+
attach_function :snd_rawmidi_nonblock, %i[SndRawMIDI int], :int # (snd_rawmidi_t *rmidi, int nonblock)
|
|
195
195
|
# Opens a new connection to the RawMidi interface.
|
|
196
|
-
attach_function :snd_rawmidi_open, [
|
|
196
|
+
attach_function :snd_rawmidi_open, %i[pointer pointer string int], :int # (snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi, const char *name, int mode)
|
|
197
197
|
# Opens a new connection to the RawMidi interface using local configuration.
|
|
198
|
-
attach_function :snd_rawmidi_open_lconf, [
|
|
198
|
+
attach_function :snd_rawmidi_open_lconf, %i[pointer pointer string int pointer], :int # (snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi, const char *name, int mode, snd_config_t *lconf)
|
|
199
199
|
# read MIDI bytes from MIDI stream
|
|
200
|
-
attach_function :snd_rawmidi_read, [
|
|
200
|
+
attach_function :snd_rawmidi_read, %i[SndRawMIDI pointer size_t], :ssize_t # (snd_rawmidi_t *rmidi, void *buffer, size_t size)
|
|
201
201
|
# write MIDI bytes to MIDI stream
|
|
202
|
-
attach_function :snd_rawmidi_write, [
|
|
202
|
+
attach_function :snd_rawmidi_write, %i[SndRawMIDI ulong size_t], :ssize_t # (snd_rawmidi_t *rmidi, const void *buffer, size_t size)
|
|
203
203
|
|
|
204
204
|
#
|
|
205
205
|
# snd_rawmidi_info
|
|
206
206
|
#
|
|
207
207
|
|
|
208
208
|
enum :snd_rawmidi_stream, [
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
209
|
+
'SND_RAWMIDI_STREAM_OUTPUT', 0,
|
|
210
|
+
'SND_RAWMIDI_STREAM_INPUT', 1,
|
|
211
|
+
'SND_RAWMIDI_STREAM_LAST', 1
|
|
212
212
|
]
|
|
213
213
|
|
|
214
214
|
# get information about RawMidi handle
|
|
215
|
-
attach_function :snd_rawmidi_info, [
|
|
215
|
+
attach_function :snd_rawmidi_info, %i[pointer pointer], :int # (snd_rawmidi_t *rmidi, snd_rawmidi_info_t *info)
|
|
216
216
|
# get rawmidi count of subdevices
|
|
217
217
|
attach_function :snd_rawmidi_info_get_subdevices_count, [:pointer], :uint # (const snd_rawmidi_info_t *obj)
|
|
218
218
|
# set rawmidi device number
|
|
219
|
-
attach_function :snd_rawmidi_info_set_device, [
|
|
219
|
+
attach_function :snd_rawmidi_info_set_device, %i[pointer uint], :void # (snd_rawmidi_info_t *obj, unsigned int val)
|
|
220
220
|
# set rawmidi subdevice number
|
|
221
|
-
attach_function :snd_rawmidi_info_set_subdevice, [
|
|
221
|
+
attach_function :snd_rawmidi_info_set_subdevice, %i[pointer uint], :void # (snd_rawmidi_info_t *obj, unsigned int val)
|
|
222
222
|
# set rawmidi stream identifier
|
|
223
|
-
attach_function :snd_rawmidi_info_set_stream, [
|
|
223
|
+
attach_function :snd_rawmidi_info_set_stream, %i[pointer snd_rawmidi_stream], :void # (snd_rawmidi_info_t *obj, snd_rawmidi_stream_t val)
|
|
224
224
|
# get size of the snd_rawmidi_info_t structure in bytes
|
|
225
225
|
attach_function :snd_rawmidi_info_sizeof, [], :size_t # (void)
|
|
226
226
|
|
|
@@ -235,17 +235,16 @@ module AlsaRawMIDI
|
|
|
235
235
|
|
|
236
236
|
# Wrapper for ALSA methods dealing with input
|
|
237
237
|
module Input
|
|
238
|
-
|
|
239
238
|
BUFFER_SIZE = 256
|
|
240
239
|
|
|
241
|
-
|
|
240
|
+
module_function
|
|
242
241
|
|
|
243
242
|
# Open the output with the given ID
|
|
244
243
|
# @param [Integer] id
|
|
245
244
|
# @return [Integer]
|
|
246
245
|
def open(id)
|
|
247
246
|
API::Device.open(id) do |pointer|
|
|
248
|
-
API.snd_rawmidi_open(pointer, nil, id,
|
|
247
|
+
API.snd_rawmidi_open(pointer, nil, id, API::CONSTANTS[:SND_RAWMIDI_NONBLOCK])
|
|
249
248
|
end
|
|
250
249
|
end
|
|
251
250
|
|
|
@@ -253,30 +252,29 @@ module AlsaRawMIDI
|
|
|
253
252
|
# @return [String]
|
|
254
253
|
def poll(handle)
|
|
255
254
|
buffer = FFI::MemoryPointer.new(:uint8, BUFFER_SIZE)
|
|
256
|
-
if (err = API.snd_rawmidi_read(handle, buffer, BUFFER_SIZE)) < 0
|
|
257
|
-
raise "Can't read MIDI input: #{API.snd_strerror(err)}"
|
|
255
|
+
if (err = API.snd_rawmidi_read(handle, buffer, BUFFER_SIZE)) < (0) && !err.eql?(-11)
|
|
256
|
+
raise "Can't read MIDI input: #{API.snd_strerror(err)}"
|
|
258
257
|
end
|
|
258
|
+
|
|
259
259
|
# Upon success, err is positive and equal to the number of bytes read
|
|
260
260
|
# into the buffer.
|
|
261
|
-
if err
|
|
262
|
-
bytes = buffer.get_bytes(0,err).
|
|
261
|
+
if err.positive?
|
|
262
|
+
bytes = buffer.get_bytes(0, err).unpack1('a*').unpack('H*')
|
|
263
263
|
bytes.first.upcase
|
|
264
264
|
end
|
|
265
265
|
end
|
|
266
|
-
|
|
267
266
|
end
|
|
268
267
|
|
|
269
268
|
# Wrapper for ALSA methods dealing with output
|
|
270
269
|
module Output
|
|
271
|
-
|
|
272
|
-
extend self
|
|
270
|
+
module_function
|
|
273
271
|
|
|
274
272
|
# Send the given MIDI data to the output with the given handle
|
|
275
273
|
# @param [Integer] handle
|
|
276
274
|
# @param [Array<Integer>] data
|
|
277
275
|
# @return [Boolean]
|
|
278
276
|
def puts(handle, data)
|
|
279
|
-
format =
|
|
277
|
+
format = 'C' * data.size
|
|
280
278
|
pointer = FFI::MemoryPointer.new(data.size)
|
|
281
279
|
bytes = pointer.put_bytes(0, data.pack(format))
|
|
282
280
|
|
|
@@ -293,22 +291,20 @@ module AlsaRawMIDI
|
|
|
293
291
|
API.snd_rawmidi_open(nil, pointer, id, 0)
|
|
294
292
|
end
|
|
295
293
|
end
|
|
296
|
-
|
|
297
294
|
end
|
|
298
295
|
|
|
299
296
|
# Wrapper for ALSA methods dealing with devices
|
|
300
297
|
module Device
|
|
301
|
-
|
|
302
|
-
extend self
|
|
298
|
+
module_function
|
|
303
299
|
|
|
304
300
|
# @param [Integer] id
|
|
305
301
|
# @param [Symbol] direction
|
|
306
302
|
# @return [SndCtlCardInfo]
|
|
307
303
|
def get_info(id, direction)
|
|
308
304
|
stream_key = case direction
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
305
|
+
when :input then :SND_RAWMIDI_STREAM_INPUT
|
|
306
|
+
when :output then :SND_RAWMIDI_STREAM_OUTPUT
|
|
307
|
+
end
|
|
312
308
|
stream = API::CONSTANTS[stream_key]
|
|
313
309
|
info = API::SndRawMIDIInfo.new
|
|
314
310
|
API.snd_rawmidi_info_set_device(info.pointer, id)
|
|
@@ -329,18 +325,16 @@ module AlsaRawMIDI
|
|
|
329
325
|
# @param [Integer] id
|
|
330
326
|
# @param [Proc] block
|
|
331
327
|
# @return [Integer]
|
|
332
|
-
def open(
|
|
328
|
+
def open(_id)
|
|
333
329
|
handle_pointer = FFI::MemoryPointer.new(FFI.type_size(:int))
|
|
334
330
|
yield(handle_pointer)
|
|
335
331
|
handle_pointer.read_int
|
|
336
332
|
end
|
|
337
|
-
|
|
338
333
|
end
|
|
339
334
|
|
|
340
335
|
# Wrapper for ALSA methods dealing with the soundcard and subdevices
|
|
341
336
|
module Soundcard
|
|
342
|
-
|
|
343
|
-
extend self
|
|
337
|
+
module_function
|
|
344
338
|
|
|
345
339
|
# @param [SndCtlCardInfo] info
|
|
346
340
|
# @return [Integer]
|
|
@@ -355,9 +349,9 @@ module AlsaRawMIDI
|
|
|
355
349
|
# @param [Integer] id
|
|
356
350
|
# @return [String]
|
|
357
351
|
def get_subdevice_id(soundcard_id, device_id, subdev_count, id)
|
|
358
|
-
ext =
|
|
352
|
+
ext = subdev_count > 1 ? ",#{id}" : ''
|
|
359
353
|
name = API::Soundcard.get_name(soundcard_id)
|
|
360
|
-
"#{name},#{device_id
|
|
354
|
+
"#{name},#{device_id}#{ext}"
|
|
361
355
|
end
|
|
362
356
|
|
|
363
357
|
# @param [SndCtlCardInfo] info
|
|
@@ -374,7 +368,7 @@ module AlsaRawMIDI
|
|
|
374
368
|
# @param [Symbol] direction
|
|
375
369
|
# @param [Proc] block
|
|
376
370
|
# @return [Array<Object>]
|
|
377
|
-
def get_subdevices(direction, soundcard_id, device_id
|
|
371
|
+
def get_subdevices(direction, soundcard_id, device_id)
|
|
378
372
|
handle = API::Soundcard.get_handle(soundcard_id)
|
|
379
373
|
info = API::Device.get_info(device_id, direction)
|
|
380
374
|
i = 0
|
|
@@ -385,9 +379,9 @@ module AlsaRawMIDI
|
|
|
385
379
|
subdev_count = API::Soundcard.get_subdevice_count(info) if i.zero?
|
|
386
380
|
system_id = API::Soundcard.get_subdevice_id(soundcard_id, device_id, subdev_count, i)
|
|
387
381
|
device_hash = {
|
|
388
|
-
:
|
|
389
|
-
:
|
|
390
|
-
:
|
|
382
|
+
id: system_id,
|
|
383
|
+
name: info[:name].to_s,
|
|
384
|
+
subname: info[:subname].to_s
|
|
391
385
|
}
|
|
392
386
|
available << yield(device_hash)
|
|
393
387
|
i += 1
|
|
@@ -411,7 +405,7 @@ module AlsaRawMIDI
|
|
|
411
405
|
# @param [Integer] soundcard_id
|
|
412
406
|
# @return [String]
|
|
413
407
|
def get_name(soundcard_id)
|
|
414
|
-
"hw:#{soundcard_id
|
|
408
|
+
"hw:#{soundcard_id}"
|
|
415
409
|
end
|
|
416
410
|
|
|
417
411
|
# Does a soundcard exist for the given id?
|
|
@@ -428,8 +422,6 @@ module AlsaRawMIDI
|
|
|
428
422
|
API.snd_ctl_open(handle_pointer, get_name(soundcard_id), 0)
|
|
429
423
|
handle_pointer.read_int
|
|
430
424
|
end
|
|
431
|
-
|
|
432
425
|
end
|
|
433
|
-
|
|
434
426
|
end
|
|
435
427
|
end
|
data/lib/alsa-rawmidi/device.rb
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module AlsaRawMIDI
|
|
3
4
|
# Functionality common to both inputs and outputs
|
|
4
5
|
module Device
|
|
5
|
-
|
|
6
6
|
module ClassMethods
|
|
7
|
-
|
|
8
7
|
# Select the first device of the given direction
|
|
9
8
|
# @param [Symbol] direction
|
|
10
9
|
# @return [Input, Output]
|
|
@@ -37,38 +36,37 @@ module AlsaRawMIDI
|
|
|
37
36
|
# @return [Hash]
|
|
38
37
|
def get_devices
|
|
39
38
|
available_devices = {
|
|
40
|
-
:
|
|
41
|
-
:
|
|
39
|
+
input: [],
|
|
40
|
+
output: []
|
|
42
41
|
}
|
|
43
42
|
device_count = 0
|
|
44
43
|
32.times do |i|
|
|
45
44
|
card = Soundcard.find(i)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
available_devices[direction] += devices
|
|
45
|
+
next if card.nil?
|
|
46
|
+
|
|
47
|
+
available_devices.each_key do |direction|
|
|
48
|
+
devices = card.subdevices[direction]
|
|
49
|
+
devices.each do |dev|
|
|
50
|
+
dev.send(:id=, device_count)
|
|
51
|
+
device_count += 1
|
|
54
52
|
end
|
|
53
|
+
available_devices[direction] += devices
|
|
55
54
|
end
|
|
56
55
|
end
|
|
57
56
|
available_devices
|
|
58
57
|
end
|
|
59
|
-
|
|
60
58
|
end
|
|
61
59
|
|
|
62
60
|
extend ClassMethods
|
|
63
61
|
|
|
64
62
|
attr_reader :enabled, # has the device been initialized?
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
:system_id, # the alsa id of the device
|
|
64
|
+
:id, # a local uuid for the device
|
|
65
|
+
:name,
|
|
66
|
+
:subname,
|
|
67
|
+
:type # :input or :output
|
|
70
68
|
|
|
71
|
-
|
|
69
|
+
alias enabled? enabled
|
|
72
70
|
|
|
73
71
|
def self.included(base)
|
|
74
72
|
base.send(:extend, ClassMethods)
|
|
@@ -102,7 +100,5 @@ module AlsaRawMIDI
|
|
|
102
100
|
def get_type
|
|
103
101
|
self.class.name.split('::').last.downcase.to_sym
|
|
104
102
|
end
|
|
105
|
-
|
|
106
103
|
end
|
|
107
|
-
|
|
108
104
|
end
|
data/lib/alsa-rawmidi/input.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module AlsaRawMIDI
|
|
3
4
|
# Input device class
|
|
4
5
|
class Input
|
|
5
|
-
|
|
6
6
|
include Device
|
|
7
7
|
|
|
8
8
|
attr_reader :buffer
|
|
@@ -25,7 +25,7 @@ module AlsaRawMIDI
|
|
|
25
25
|
@pointer = @buffer.length
|
|
26
26
|
msgs
|
|
27
27
|
end
|
|
28
|
-
|
|
28
|
+
alias read gets
|
|
29
29
|
|
|
30
30
|
# Like Input#gets but returns message data as string of hex digits as such:
|
|
31
31
|
# [
|
|
@@ -37,17 +37,17 @@ module AlsaRawMIDI
|
|
|
37
37
|
# @return [Array<Hash>]
|
|
38
38
|
def gets_s
|
|
39
39
|
msgs = gets
|
|
40
|
-
msgs.each { |m| m[:data] = numeric_bytes_to_hex_string(m[:data]) }
|
|
40
|
+
msgs.each { |m| m[:data] = TypeConversion.numeric_bytes_to_hex_string(m[:data]) }
|
|
41
41
|
msgs
|
|
42
42
|
end
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
alias gets_bytestr gets_s
|
|
44
|
+
alias gets_hex gets_s
|
|
45
45
|
|
|
46
46
|
# Enable this the input for use; yields
|
|
47
47
|
# @param [Hash] options
|
|
48
48
|
# @param [Proc] block
|
|
49
49
|
# @return [Input] self
|
|
50
|
-
def enable(
|
|
50
|
+
def enable(_options = {})
|
|
51
51
|
unless @enabled
|
|
52
52
|
@start_time = Time.now.to_f
|
|
53
53
|
@resource = API::Input.open(@system_id)
|
|
@@ -64,8 +64,8 @@ module AlsaRawMIDI
|
|
|
64
64
|
end
|
|
65
65
|
self
|
|
66
66
|
end
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
alias open enable
|
|
68
|
+
alias start enable
|
|
69
69
|
|
|
70
70
|
# Close this input
|
|
71
71
|
# @return [Boolean]
|
|
@@ -125,8 +125,8 @@ module AlsaRawMIDI
|
|
|
125
125
|
# @return [Hash]
|
|
126
126
|
def get_message_formatted(hexstring, timestamp)
|
|
127
127
|
{
|
|
128
|
-
:
|
|
129
|
-
:
|
|
128
|
+
data: TypeConversion.hex_string_to_numeric_bytes(hexstring),
|
|
129
|
+
timestamp: timestamp
|
|
130
130
|
}
|
|
131
131
|
end
|
|
132
132
|
|
|
@@ -146,7 +146,7 @@ module AlsaRawMIDI
|
|
|
146
146
|
# and holds them for the next call to gets*
|
|
147
147
|
# @return [Thread]
|
|
148
148
|
def spawn_listener
|
|
149
|
-
interval = 1.0/1000
|
|
149
|
+
interval = 1.0 / 1000
|
|
150
150
|
@listener = Thread.new do
|
|
151
151
|
begin
|
|
152
152
|
loop do
|
|
@@ -155,8 +155,8 @@ module AlsaRawMIDI
|
|
|
155
155
|
end
|
|
156
156
|
populate_buffer(messages) unless messages.nil?
|
|
157
157
|
end
|
|
158
|
-
rescue Exception =>
|
|
159
|
-
Thread.main.raise(
|
|
158
|
+
rescue Exception => e
|
|
159
|
+
Thread.main.raise(e)
|
|
160
160
|
end
|
|
161
161
|
end
|
|
162
162
|
@listener.abort_on_exception = true
|
|
@@ -168,32 +168,5 @@ module AlsaRawMIDI
|
|
|
168
168
|
def populate_buffer(messages)
|
|
169
169
|
@buffer << get_message_formatted(messages, now) unless messages.nil?
|
|
170
170
|
end
|
|
171
|
-
|
|
172
|
-
# Convert a hex string to an array of numeric bytes eg "904040" -> [0x90, 0x40, 0x40]
|
|
173
|
-
# @param [String] string
|
|
174
|
-
# @return [Array<Integer>]
|
|
175
|
-
def hex_string_to_numeric_bytes(string)
|
|
176
|
-
string = string.dup
|
|
177
|
-
bytes = []
|
|
178
|
-
until string.length.zero?
|
|
179
|
-
string_byte = string.slice!(0, 2)
|
|
180
|
-
bytes << string_byte.hex
|
|
181
|
-
end
|
|
182
|
-
bytes
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
# Convert an array of numeric bytes to a hex string eg [0x90, 0x40, 0x40] -> "904040"
|
|
186
|
-
# @param [Array<Integer>] bytes
|
|
187
|
-
# @return [String]
|
|
188
|
-
def numeric_bytes_to_hex_string(bytes)
|
|
189
|
-
string_bytes = bytes.map do |byte|
|
|
190
|
-
string_byte = byte.to_s(16).upcase
|
|
191
|
-
string_byte = "0#{string_byte}" if byte < 16
|
|
192
|
-
string_byte
|
|
193
|
-
end
|
|
194
|
-
string_bytes.join
|
|
195
|
-
end
|
|
196
|
-
|
|
197
171
|
end
|
|
198
|
-
|
|
199
172
|
end
|
data/lib/alsa-rawmidi/output.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module AlsaRawMIDI
|
|
3
4
|
# Output device class
|
|
4
5
|
class Output
|
|
5
|
-
|
|
6
6
|
include Device
|
|
7
7
|
|
|
8
8
|
# Close this output
|
|
@@ -23,14 +23,14 @@ module AlsaRawMIDI
|
|
|
23
23
|
def puts_s(data)
|
|
24
24
|
data = data.dup
|
|
25
25
|
output = []
|
|
26
|
-
until (str = data.slice!(0,2)) ==
|
|
26
|
+
until (str = data.slice!(0, 2)) == ''
|
|
27
27
|
output << str.hex
|
|
28
28
|
end
|
|
29
29
|
puts_bytes(*output)
|
|
30
30
|
true
|
|
31
31
|
end
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
alias puts_bytestr puts_s
|
|
33
|
+
alias puts_hex puts_s
|
|
34
34
|
|
|
35
35
|
# Output a MIDI message in numeric byte format
|
|
36
36
|
# @param [*Integer] data
|
|
@@ -45,18 +45,18 @@ module AlsaRawMIDI
|
|
|
45
45
|
# @return [Boolean]
|
|
46
46
|
def puts(*args)
|
|
47
47
|
case args.first
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
when Array then args.each { |arg| puts(*arg) }
|
|
49
|
+
when Numeric then puts_bytes(*args)
|
|
50
|
+
when String then puts_bytestr(*args)
|
|
51
51
|
end
|
|
52
52
|
end
|
|
53
|
-
|
|
53
|
+
alias write puts
|
|
54
54
|
|
|
55
55
|
# Enable this device; yields
|
|
56
56
|
# @param [Hash] options
|
|
57
57
|
# @param [Proc] block
|
|
58
58
|
# @return [Output]
|
|
59
|
-
def enable(
|
|
59
|
+
def enable(_options = {})
|
|
60
60
|
unless @enabled
|
|
61
61
|
@resource = API::Output.open(@system_id)
|
|
62
62
|
@enabled = true
|
|
@@ -70,8 +70,8 @@ module AlsaRawMIDI
|
|
|
70
70
|
end
|
|
71
71
|
self
|
|
72
72
|
end
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
alias open enable
|
|
74
|
+
alias start enable
|
|
75
75
|
|
|
76
76
|
# The first available output
|
|
77
77
|
# @return [Output]
|
|
@@ -90,7 +90,5 @@ module AlsaRawMIDI
|
|
|
90
90
|
def self.all
|
|
91
91
|
Device.all_by_type[:output]
|
|
92
92
|
end
|
|
93
|
-
|
|
94
93
|
end
|
|
95
|
-
|
|
96
94
|
end
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
module AlsaRawMIDI
|
|
3
4
|
class Soundcard
|
|
4
|
-
|
|
5
5
|
attr_reader :id, :subdevices
|
|
6
6
|
|
|
7
7
|
# @param [Integer] id
|
|
8
8
|
def initialize(id)
|
|
9
9
|
@subdevices = {
|
|
10
|
-
:
|
|
11
|
-
:
|
|
10
|
+
input: [],
|
|
11
|
+
output: []
|
|
12
12
|
}
|
|
13
13
|
@id = id
|
|
14
14
|
populate_subdevices
|
|
@@ -19,9 +19,7 @@ module AlsaRawMIDI
|
|
|
19
19
|
# @return [Soundcard]
|
|
20
20
|
def self.find(id)
|
|
21
21
|
@soundcards ||= {}
|
|
22
|
-
if API::Soundcard.exists?(id)
|
|
23
|
-
@soundcards[id] ||= Soundcard.new(id)
|
|
24
|
-
end
|
|
22
|
+
@soundcards[id] ||= Soundcard.new(id) if API::Soundcard.exists?(id)
|
|
25
23
|
end
|
|
26
24
|
|
|
27
25
|
private
|
|
@@ -30,7 +28,7 @@ module AlsaRawMIDI
|
|
|
30
28
|
def populate_subdevices
|
|
31
29
|
device_ids = API::Soundcard.get_device_ids(@id)
|
|
32
30
|
device_ids.each do |device_id|
|
|
33
|
-
@subdevices.
|
|
31
|
+
@subdevices.each_key do |direction|
|
|
34
32
|
devices = API::Soundcard.get_subdevices(direction, @id, device_id) do |device_hash|
|
|
35
33
|
new_device(direction, device_hash)
|
|
36
34
|
end
|
|
@@ -45,17 +43,15 @@ module AlsaRawMIDI
|
|
|
45
43
|
# @return [Input, Output]
|
|
46
44
|
def new_device(direction, device_hash)
|
|
47
45
|
device_class = case direction
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
when :input then Input
|
|
47
|
+
when :output then Output
|
|
48
|
+
end
|
|
51
49
|
device_properties = {
|
|
52
|
-
:
|
|
53
|
-
:
|
|
54
|
-
:
|
|
50
|
+
system_id: device_hash[:id],
|
|
51
|
+
name: device_hash[:name],
|
|
52
|
+
subname: device_hash[:subname]
|
|
55
53
|
}
|
|
56
54
|
device_class.new(device_properties)
|
|
57
55
|
end
|
|
58
|
-
|
|
59
56
|
end
|
|
60
|
-
|
|
61
57
|
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AlsaRawMIDI
|
|
4
|
+
# Helper for converting MIDI data
|
|
5
|
+
module TypeConversion
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
# Convert a hex string to an array of numeric bytes eg "904040" -> [0x90, 0x40, 0x40]
|
|
9
|
+
# @param [String] string
|
|
10
|
+
# @return [Array<Integer>]
|
|
11
|
+
def hex_string_to_numeric_bytes(string)
|
|
12
|
+
string = string.dup
|
|
13
|
+
bytes = []
|
|
14
|
+
until string.length.zero?
|
|
15
|
+
string_byte = string.slice!(0, 2)
|
|
16
|
+
bytes << string_byte.hex
|
|
17
|
+
end
|
|
18
|
+
bytes
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Convert an array of numeric bytes to a hex string eg [0x90, 0x40, 0x40] -> "904040"
|
|
22
|
+
# @param [Array<Integer>] bytes
|
|
23
|
+
# @return [String]
|
|
24
|
+
def numeric_bytes_to_hex_string(bytes)
|
|
25
|
+
string_bytes = bytes.map do |byte|
|
|
26
|
+
string_byte = byte.to_s(16).upcase
|
|
27
|
+
string_byte = "0#{string_byte}" if byte < 16
|
|
28
|
+
string_byte
|
|
29
|
+
end
|
|
30
|
+
string_bytes.join
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
data/lib/alsa-rawmidi/version.rb
CHANGED
data/lib/alsa-rawmidi.rb
CHANGED
|
@@ -1,25 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
#
|
|
2
4
|
# alsa-rawmidi
|
|
3
5
|
# ALSA Driver Interface
|
|
4
6
|
#
|
|
5
|
-
# (c) 2010-
|
|
7
|
+
# (c) 2010-2022 Ari Russo
|
|
6
8
|
# Licensed under Apache 2.0
|
|
7
9
|
# https://github.com/arirusso/alsa-rawmidi
|
|
8
10
|
#
|
|
9
11
|
|
|
10
12
|
# libs
|
|
11
|
-
require
|
|
13
|
+
require 'ffi'
|
|
12
14
|
|
|
13
15
|
# modules
|
|
14
|
-
require
|
|
15
|
-
require
|
|
16
|
-
require
|
|
16
|
+
require 'alsa-rawmidi/api'
|
|
17
|
+
require 'alsa-rawmidi/device'
|
|
18
|
+
require 'alsa-rawmidi/type_conversion'
|
|
19
|
+
require 'alsa-rawmidi/version'
|
|
17
20
|
|
|
18
21
|
# class
|
|
19
|
-
require
|
|
20
|
-
require
|
|
21
|
-
require
|
|
22
|
+
require 'alsa-rawmidi/input'
|
|
23
|
+
require 'alsa-rawmidi/output'
|
|
24
|
+
require 'alsa-rawmidi/soundcard'
|
|
22
25
|
|
|
23
26
|
module AlsaRawMIDI
|
|
24
|
-
|
|
25
27
|
end
|
metadata
CHANGED
|
@@ -1,29 +1,95 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: alsa-rawmidi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ari Russo
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-02-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rake
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '13.0'
|
|
20
|
+
- - ">="
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: 13.0.6
|
|
23
|
+
type: :development
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - "~>"
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '13.0'
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 13.0.6
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: rspec
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '3.11'
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: 3.11.0
|
|
43
|
+
type: :development
|
|
44
|
+
prerelease: false
|
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - "~>"
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: '3.11'
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: 3.11.0
|
|
53
|
+
- !ruby/object:Gem::Dependency
|
|
54
|
+
name: rubocop
|
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - "~>"
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: '1.10'
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: 1.10.0
|
|
63
|
+
type: :development
|
|
64
|
+
prerelease: false
|
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - "~>"
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '1.10'
|
|
70
|
+
- - ">="
|
|
71
|
+
- !ruby/object:Gem::Version
|
|
72
|
+
version: 1.10.0
|
|
13
73
|
- !ruby/object:Gem::Dependency
|
|
14
74
|
name: ffi
|
|
15
75
|
requirement: !ruby/object:Gem::Requirement
|
|
16
76
|
requirements:
|
|
17
77
|
- - "~>"
|
|
18
78
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '1.
|
|
79
|
+
version: '1.15'
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: 1.15.5
|
|
20
83
|
type: :runtime
|
|
21
84
|
prerelease: false
|
|
22
85
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
86
|
requirements:
|
|
24
87
|
- - "~>"
|
|
25
88
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '1.
|
|
89
|
+
version: '1.15'
|
|
90
|
+
- - ">="
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
version: 1.15.5
|
|
27
93
|
description: Realtime MIDI IO with Ruby for Linux via the ALSA RawMIDI API.
|
|
28
94
|
email:
|
|
29
95
|
- ari.russo@gmail.com
|
|
@@ -39,15 +105,13 @@ files:
|
|
|
39
105
|
- lib/alsa-rawmidi/input.rb
|
|
40
106
|
- lib/alsa-rawmidi/output.rb
|
|
41
107
|
- lib/alsa-rawmidi/soundcard.rb
|
|
108
|
+
- lib/alsa-rawmidi/type_conversion.rb
|
|
42
109
|
- lib/alsa-rawmidi/version.rb
|
|
43
|
-
- test/helper.rb
|
|
44
|
-
- test/input_buffer_test.rb
|
|
45
|
-
- test/io_test.rb
|
|
46
110
|
homepage: http://github.com/arirusso/alsa-rawmidi
|
|
47
111
|
licenses:
|
|
48
112
|
- Apache-2.0
|
|
49
113
|
metadata: {}
|
|
50
|
-
post_install_message:
|
|
114
|
+
post_install_message:
|
|
51
115
|
rdoc_options: []
|
|
52
116
|
require_paths:
|
|
53
117
|
- lib
|
|
@@ -60,11 +124,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
60
124
|
requirements:
|
|
61
125
|
- - ">="
|
|
62
126
|
- !ruby/object:Gem::Version
|
|
63
|
-
version:
|
|
127
|
+
version: '0'
|
|
64
128
|
requirements: []
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
signing_key:
|
|
129
|
+
rubygems_version: 3.3.3
|
|
130
|
+
signing_key:
|
|
68
131
|
specification_version: 4
|
|
69
132
|
summary: Realtime MIDI IO with Ruby for Linux
|
|
70
133
|
test_files: []
|
data/test/helper.rb
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
dir = File.dirname(File.expand_path(__FILE__))
|
|
2
|
-
$LOAD_PATH.unshift dir + "/../lib"
|
|
3
|
-
|
|
4
|
-
require "test/unit"
|
|
5
|
-
require "mocha/test_unit"
|
|
6
|
-
require "shoulda-context"
|
|
7
|
-
require "alsa-rawmidi"
|
|
8
|
-
|
|
9
|
-
module TestHelper
|
|
10
|
-
|
|
11
|
-
extend self
|
|
12
|
-
|
|
13
|
-
def bytestrs_to_ints(arr)
|
|
14
|
-
data = arr.map { |m| m[:data] }.join
|
|
15
|
-
output = []
|
|
16
|
-
until (bytestr = data.slice!(0,2)).eql?("")
|
|
17
|
-
output << bytestr.hex
|
|
18
|
-
end
|
|
19
|
-
output
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# some MIDI messages
|
|
23
|
-
def numeric_messages
|
|
24
|
-
[
|
|
25
|
-
[0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7], # SysEx
|
|
26
|
-
[0x90, 100, 100], # note on
|
|
27
|
-
[0x90, 43, 100], # note on
|
|
28
|
-
[0x90, 76, 100], # note on
|
|
29
|
-
[0x90, 60, 100], # note on
|
|
30
|
-
[0x80, 100, 100] # note off
|
|
31
|
-
]
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# some MIDI messages
|
|
35
|
-
def string_messages
|
|
36
|
-
[
|
|
37
|
-
"F04110421240007F0041F7", # SysEx
|
|
38
|
-
"906440", # note on
|
|
39
|
-
"804340" # note off
|
|
40
|
-
]
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def input
|
|
44
|
-
AlsaRawMIDI::Input.first
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def output
|
|
48
|
-
AlsaRawMIDI::Output.first
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
end
|
data/test/input_buffer_test.rb
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
require "helper"
|
|
2
|
-
|
|
3
|
-
class AlsaRawMIDI::InputBufferTest < Test::Unit::TestCase
|
|
4
|
-
|
|
5
|
-
context "AlsaRawMIDI" do
|
|
6
|
-
|
|
7
|
-
setup do
|
|
8
|
-
sleep(1)
|
|
9
|
-
@input = TestHelper.input.open
|
|
10
|
-
@output = TestHelper.output.open
|
|
11
|
-
@input.buffer.clear
|
|
12
|
-
@pointer = 0
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
context "Source#buffer" do
|
|
16
|
-
|
|
17
|
-
setup do
|
|
18
|
-
@messages = TestHelper.numeric_messages
|
|
19
|
-
@messages_arr = @messages.inject(&:+).flatten
|
|
20
|
-
@received_arr = []
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
teardown do
|
|
24
|
-
@input.close
|
|
25
|
-
@output.close
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
should "have the correct messages in the buffer" do
|
|
29
|
-
bytes = []
|
|
30
|
-
@messages.each do |message|
|
|
31
|
-
p "sending: #{message}"
|
|
32
|
-
@output.puts(message)
|
|
33
|
-
bytes += message
|
|
34
|
-
|
|
35
|
-
sleep(1)
|
|
36
|
-
|
|
37
|
-
buffer = @input.buffer.map { |m| m[:data] }.flatten
|
|
38
|
-
p "received: #{buffer.to_s}"
|
|
39
|
-
assert_equal(bytes, buffer)
|
|
40
|
-
end
|
|
41
|
-
assert_equal(bytes.length, @input.buffer.map { |m| m[:data] }.flatten.length)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
data/test/io_test.rb
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
require "helper"
|
|
2
|
-
|
|
3
|
-
class AlsaRawMIDI::IoTest < Test::Unit::TestCase
|
|
4
|
-
|
|
5
|
-
# ** this test assumes that TestOutput is connected to TestInput
|
|
6
|
-
context "AlsaRawMIDI" do
|
|
7
|
-
|
|
8
|
-
setup do
|
|
9
|
-
sleep(1)
|
|
10
|
-
@input = TestHelper.input.open
|
|
11
|
-
@output = TestHelper.output.open
|
|
12
|
-
@input.buffer.clear
|
|
13
|
-
@pointer = 0
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
context "full IO" do
|
|
17
|
-
|
|
18
|
-
context "using Arrays" do
|
|
19
|
-
|
|
20
|
-
setup do
|
|
21
|
-
@messages = TestHelper.numeric_messages
|
|
22
|
-
@messages_arr = @messages.inject(&:+).flatten
|
|
23
|
-
@received_arr = []
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
teardown do
|
|
27
|
-
@input.close
|
|
28
|
-
@output.close
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
should "do IO" do
|
|
32
|
-
@messages.each do |message|
|
|
33
|
-
|
|
34
|
-
p "sending: #{message}"
|
|
35
|
-
|
|
36
|
-
@output.puts(message)
|
|
37
|
-
sleep(1)
|
|
38
|
-
received = @input.gets.map { |m| m[:data] }.flatten
|
|
39
|
-
|
|
40
|
-
p "received: #{received}"
|
|
41
|
-
|
|
42
|
-
assert_equal(@messages_arr.slice(@pointer, received.length), received)
|
|
43
|
-
@pointer += received.length
|
|
44
|
-
@received_arr += received
|
|
45
|
-
end
|
|
46
|
-
assert_equal(@messages_arr.length, @received_arr.length)
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
context "using byte Strings" do
|
|
51
|
-
|
|
52
|
-
setup do
|
|
53
|
-
@messages = TestHelper.string_messages
|
|
54
|
-
@messages_str = @messages.join
|
|
55
|
-
@received_str = ""
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
should "do IO" do
|
|
59
|
-
@messages.each do |message|
|
|
60
|
-
|
|
61
|
-
p "sending: #{message}"
|
|
62
|
-
|
|
63
|
-
@output.puts(message)
|
|
64
|
-
sleep(1)
|
|
65
|
-
received = @input.gets_bytestr.map { |m| m[:data] }.flatten.join
|
|
66
|
-
p "received: #{received}"
|
|
67
|
-
|
|
68
|
-
assert_equal(@messages_str.slice(@pointer, received.length), received)
|
|
69
|
-
@pointer += received.length
|
|
70
|
-
@received_str += received
|
|
71
|
-
end
|
|
72
|
-
assert_equal(@messages_str, @received_str)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
end
|