alsa-rawmidi 0.2.14 → 0.3.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/LICENSE +2 -2
- data/README.md +48 -0
- data/lib/alsa-rawmidi.rb +20 -12
- data/lib/alsa-rawmidi/api.rb +435 -0
- data/lib/alsa-rawmidi/device.rb +85 -56
- data/lib/alsa-rawmidi/input.rb +199 -168
- data/lib/alsa-rawmidi/output.rb +63 -51
- data/lib/alsa-rawmidi/soundcard.rb +45 -62
- data/test/helper.rb +51 -0
- data/test/input_buffer_test.rb +46 -0
- data/test/io_test.rb +80 -0
- metadata +42 -47
- data/README.rdoc +0 -45
- data/lib/alsa-rawmidi/map.rb +0 -242
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e6767cbdb0a3a5164c941104b6e775f805290828
|
4
|
+
data.tar.gz: 69740ca23dc973a16300f8b443bef6a64a54dd8c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 22307cabe5577f9ef9dd779b6d1e169ac9507832e06f1d9fba835430127f8ce7fee7428b44ec382cf9700ef5d3ca42c0a3dcba715c2103e1f086fe65ffcbb89e
|
7
|
+
data.tar.gz: de620c6756658652814fd294bc1678679c0c1a90f470c30492b87344482e1c52ea3f36a47dc0832f7d512cc5670d8beff0d2507eba7bb57c952d3ca248dea532
|
data/LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright 2010-
|
1
|
+
Copyright 2010-2014 Ari Russo
|
2
2
|
|
3
3
|
Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
you may not use this file except in compliance with the License.
|
@@ -10,4 +10,4 @@ Unless required by applicable law or agreed to in writing, software
|
|
10
10
|
distributed under the License is distributed on an "AS IS" BASIS,
|
11
11
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
12
|
See the License for the specific language governing permissions and
|
13
|
-
limitations under the License.
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# alsa-rawmidi
|
2
|
+
|
3
|
+
#### Realtime MIDI IO with Ruby for Linux.
|
4
|
+
|
5
|
+
Access the ALSA RawMIDI API with Ruby.
|
6
|
+
|
7
|
+
Note that in the interest of allowing people on other platforms to utilize your code, you should consider using [unimidi](http://github.com/arirusso/unimidi). Unimidi is a platform independent wrapper that implements this gem and has a similar API.
|
8
|
+
|
9
|
+
## Features
|
10
|
+
|
11
|
+
* Simplified API
|
12
|
+
* Input and output on multiple devices concurrently
|
13
|
+
* Generalized handling of different MIDI Message types (including SysEx)
|
14
|
+
* Timestamped input events
|
15
|
+
|
16
|
+
## Requirements
|
17
|
+
|
18
|
+
* [ffi](http://github.com/ffi/ffi)
|
19
|
+
* libasound, libasound-dev packages
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
If you're using Bundler, add this line to your application's Gemfile:
|
24
|
+
|
25
|
+
`gem "alsa-rawmidi"`
|
26
|
+
|
27
|
+
Otherwise
|
28
|
+
|
29
|
+
`gem install alsa-rawmidi`
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
* [input](http://github.com/arirusso/alsa-rawmidi/blob/master/examples/input.rb)
|
34
|
+
* [output](http://github.com/arirusso/alsa-rawmidi/blob/master/examples/output.rb)
|
35
|
+
|
36
|
+
## Documentation
|
37
|
+
|
38
|
+
* [rdoc](http://rdoc.info/gems/alsa-rawmidi)
|
39
|
+
|
40
|
+
## Author
|
41
|
+
|
42
|
+
[Ari Russo](http://github.com/arirusso) <ari.russo at gmail.com>
|
43
|
+
|
44
|
+
## License
|
45
|
+
|
46
|
+
Apache 2.0, See the file LICENSE
|
47
|
+
|
48
|
+
Copyright (c) 2010-2014 Ari Russo
|
data/lib/alsa-rawmidi.rb
CHANGED
@@ -1,16 +1,24 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
#
|
4
|
-
#
|
2
|
+
# Modules and classes to interact with the ALSA Driver Interface
|
5
3
|
#
|
4
|
+
# Ari Russo
|
5
|
+
# (c) 2010-2014
|
6
|
+
# Licensed under Apache 2.0
|
7
|
+
|
8
|
+
# libs
|
9
|
+
require "ffi"
|
10
|
+
|
11
|
+
# modules
|
12
|
+
require "alsa-rawmidi/api"
|
13
|
+
require "alsa-rawmidi/device"
|
14
|
+
|
15
|
+
# class
|
16
|
+
require "alsa-rawmidi/input"
|
17
|
+
require "alsa-rawmidi/output"
|
18
|
+
require "alsa-rawmidi/soundcard"
|
19
|
+
|
6
20
|
module AlsaRawMIDI
|
7
|
-
VERSION = "0.2.14"
|
8
|
-
end
|
9
21
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
require 'alsa-rawmidi/input'
|
14
|
-
require 'alsa-rawmidi/map'
|
15
|
-
require 'alsa-rawmidi/output'
|
16
|
-
require 'alsa-rawmidi/soundcard'
|
22
|
+
VERSION = "0.3.1"
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,435 @@
|
|
1
|
+
module AlsaRawMIDI
|
2
|
+
|
3
|
+
# libasound RawMIDI struct, enum and function bindings
|
4
|
+
module API
|
5
|
+
|
6
|
+
extend FFI::Library
|
7
|
+
ffi_lib "libasound"
|
8
|
+
|
9
|
+
CONSTANTS = {
|
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
|
+
}
|
16
|
+
|
17
|
+
typedef :ulong, :SndCtlType
|
18
|
+
typedef :ulong, :SndCtl
|
19
|
+
typedef :ulong, :SndRawMIDI
|
20
|
+
|
21
|
+
# snd_ctl
|
22
|
+
class SndCtl < FFI::Struct
|
23
|
+
layout :dl_handle, :pointer, # void*
|
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
|
+
end
|
32
|
+
|
33
|
+
# snd_ctl_card_info
|
34
|
+
class SndCtlCardInfo < FFI::Struct
|
35
|
+
layout :card, :int, # card number
|
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
|
+
end
|
45
|
+
|
46
|
+
# snd_rawmidi_info
|
47
|
+
class SndRawMIDIInfo < FFI::Struct
|
48
|
+
layout :device, :uint, # RO/WR (control): device number
|
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
|
+
end
|
60
|
+
|
61
|
+
# timespec
|
62
|
+
class Timespec < FFI::Struct
|
63
|
+
layout :tv_sec, :time_t, # Seconds since 00:00:00 GMT
|
64
|
+
:tv_nsec, :long # Additional nanoseconds since
|
65
|
+
end
|
66
|
+
|
67
|
+
# snd_rawmidi_status
|
68
|
+
class SndRawMIDIStatus < FFI::Struct
|
69
|
+
layout :stream, :int,
|
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
|
+
end
|
75
|
+
|
76
|
+
# Simple doubly linked list implementation
|
77
|
+
class LinkedList < FFI::Struct
|
78
|
+
layout :next, :pointer, # *LinkedList
|
79
|
+
:prev, :pointer # *LinkedList
|
80
|
+
end
|
81
|
+
|
82
|
+
# snd_rawmidi
|
83
|
+
class SndRawMIDI < FFI::Struct
|
84
|
+
layout :card, :pointer, # *snd_card
|
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
|
+
end
|
91
|
+
|
92
|
+
# spinlock_t
|
93
|
+
class Spinlock < FFI::Struct
|
94
|
+
layout :lock, :uint
|
95
|
+
end
|
96
|
+
|
97
|
+
# wait_queue_head_t
|
98
|
+
class WaitQueueHead < FFI::Struct
|
99
|
+
layout :lock, Spinlock.by_value,
|
100
|
+
:task_list, LinkedList.by_value
|
101
|
+
end
|
102
|
+
|
103
|
+
class AtomicT < FFI::Struct
|
104
|
+
layout :counter, :int # volatile int counter
|
105
|
+
end
|
106
|
+
|
107
|
+
class Tasklet < FFI::Struct
|
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
|
+
end
|
114
|
+
|
115
|
+
# snd_rawmidi_runtime
|
116
|
+
class SndRawMIDIRuntime < FFI::Struct
|
117
|
+
layout :drain, :uint, 1, # drain stage
|
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
|
+
end
|
137
|
+
|
138
|
+
# snd_rawmidi_params
|
139
|
+
class SndRawMIDIParams < FFI::Struct
|
140
|
+
layout :stream, :int,
|
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
|
+
end
|
146
|
+
|
147
|
+
#
|
148
|
+
# snd_card
|
149
|
+
#
|
150
|
+
|
151
|
+
# Try to load the driver for a card.
|
152
|
+
attach_function :snd_card_load, [:int], :int # (int card)
|
153
|
+
# Try to determine the next card.
|
154
|
+
attach_function :snd_card_next, [:pointer], :int # (int* card)
|
155
|
+
# Convert card string to an integer value.
|
156
|
+
attach_function :snd_card_get_index, [:pointer], :int # (const char* name)
|
157
|
+
# Obtain the card name.
|
158
|
+
attach_function :snd_card_get_name, [:int, :pointer], :int # (int card, char **name)
|
159
|
+
# Obtain the card long name.
|
160
|
+
attach_function :snd_card_get_longname, [:int, :pointer], :int # (int card, char **name)
|
161
|
+
|
162
|
+
#
|
163
|
+
# snd_ctl
|
164
|
+
#
|
165
|
+
|
166
|
+
# Opens a CTL.
|
167
|
+
attach_function :snd_ctl_open, [:pointer, :pointer, :int], :int # (snd_ctl_t **ctl, const char *name, int mode)
|
168
|
+
# Opens a CTL using local configuration.
|
169
|
+
attach_function :snd_ctl_open_lconf, [:pointer, :pointer, :int, :pointer], :int # (snd_ctl_t **ctl, const char *name, int mode, snd_config_t *lconf)
|
170
|
+
# close CTL handle
|
171
|
+
attach_function :snd_ctl_close, [:pointer], :int #(snd_ctl_t *ctl)
|
172
|
+
# set nonblock mode
|
173
|
+
attach_function :snd_ctl_nonblock, [:pointer, :int], :int # (snd_ctl_t *ctl, int nonblock)
|
174
|
+
# Get card related information.
|
175
|
+
attach_function :snd_ctl_card_info, [:pointer, :pointer], :int # (snd_ctl_t *ctl, snd_ctl_card_info_t *info)
|
176
|
+
# Get card name from a CTL card info.
|
177
|
+
attach_function :snd_ctl_card_info_get_name, [:pointer], :string # (const snd_ctl_card_info_t *obj) / const char*
|
178
|
+
# Get info about a RawMidi device.
|
179
|
+
attach_function :snd_ctl_rawmidi_info, [:SndCtl, :pointer], :int # (snd_ctl_t *ctl, snd_rawmidi_info_t *info)
|
180
|
+
# Get next RawMidi device number.
|
181
|
+
attach_function :snd_ctl_rawmidi_next_device, [:SndCtl, :pointer], :int # (snd_ctl_t *ctl, int *device)
|
182
|
+
|
183
|
+
#
|
184
|
+
# snd_rawmidi
|
185
|
+
#
|
186
|
+
|
187
|
+
# close RawMidi handle
|
188
|
+
attach_function :snd_rawmidi_close, [:SndRawMIDI], :int # (snd_rawmidi_t *rmidi)
|
189
|
+
# drain all bytes in the rawmidi I/O ring buffer
|
190
|
+
attach_function :snd_rawmidi_drain, [:SndRawMIDI], :int # (snd_rawmidi_t *rmidi)
|
191
|
+
# drop all bytes in the rawmidi I/O ring buffer immediately
|
192
|
+
attach_function :snd_rawmidi_drop, [:SndRawMIDI], :int # int ( snd_rawmidi_t * rawmidi)
|
193
|
+
# set nonblock mode
|
194
|
+
attach_function :snd_rawmidi_nonblock, [:SndRawMIDI, :int], :int # (snd_rawmidi_t *rmidi, int nonblock)
|
195
|
+
# Opens a new connection to the RawMidi interface.
|
196
|
+
attach_function :snd_rawmidi_open, [:pointer, :pointer, :string, :int], :int # (snd_rawmidi_t **in_rmidi, snd_rawmidi_t **out_rmidi, const char *name, int mode)
|
197
|
+
# Opens a new connection to the RawMidi interface using local configuration.
|
198
|
+
attach_function :snd_rawmidi_open_lconf, [: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
|
+
# read MIDI bytes from MIDI stream
|
200
|
+
attach_function :snd_rawmidi_read, [:SndRawMIDI, :pointer, :size_t], :ssize_t # (snd_rawmidi_t *rmidi, void *buffer, size_t size)
|
201
|
+
# write MIDI bytes to MIDI stream
|
202
|
+
attach_function :snd_rawmidi_write, [:SndRawMIDI, :ulong, :size_t], :ssize_t # (snd_rawmidi_t *rmidi, const void *buffer, size_t size)
|
203
|
+
|
204
|
+
#
|
205
|
+
# snd_rawmidi_info
|
206
|
+
#
|
207
|
+
|
208
|
+
enum :snd_rawmidi_stream, [
|
209
|
+
"SND_RAWMIDI_STREAM_OUTPUT", 0,
|
210
|
+
"SND_RAWMIDI_STREAM_INPUT", 1,
|
211
|
+
"SND_RAWMIDI_STREAM_LAST", 1
|
212
|
+
]
|
213
|
+
|
214
|
+
# get information about RawMidi handle
|
215
|
+
attach_function :snd_rawmidi_info, [:pointer, :pointer], :int # (snd_rawmidi_t *rmidi, snd_rawmidi_info_t *info)
|
216
|
+
# get rawmidi count of subdevices
|
217
|
+
attach_function :snd_rawmidi_info_get_subdevices_count, [:pointer], :uint # (const snd_rawmidi_info_t *obj)
|
218
|
+
# set rawmidi device number
|
219
|
+
attach_function :snd_rawmidi_info_set_device, [:pointer, :uint], :void # (snd_rawmidi_info_t *obj, unsigned int val)
|
220
|
+
# set rawmidi subdevice number
|
221
|
+
attach_function :snd_rawmidi_info_set_subdevice, [:pointer, :uint], :void # (snd_rawmidi_info_t *obj, unsigned int val)
|
222
|
+
# set rawmidi stream identifier
|
223
|
+
attach_function :snd_rawmidi_info_set_stream, [:pointer, :snd_rawmidi_stream], :void # (snd_rawmidi_info_t *obj, snd_rawmidi_stream_t val)
|
224
|
+
# get size of the snd_rawmidi_info_t structure in bytes
|
225
|
+
attach_function :snd_rawmidi_info_sizeof, [], :size_t # (void)
|
226
|
+
|
227
|
+
#
|
228
|
+
# misc
|
229
|
+
#
|
230
|
+
|
231
|
+
# Convert an error code to a string
|
232
|
+
attach_function :snd_strerror, [:int], :string # (int errnum) / const char*
|
233
|
+
# Frees the global configuration tree in snd_config.
|
234
|
+
attach_function :snd_config_update_free_global, [], :int # (void)
|
235
|
+
|
236
|
+
# Wrapper for ALSA methods dealing with input
|
237
|
+
module Input
|
238
|
+
|
239
|
+
BUFFER_SIZE = 256
|
240
|
+
|
241
|
+
extend self
|
242
|
+
|
243
|
+
# Open the output with the given ID
|
244
|
+
# @param [Fixnum] id
|
245
|
+
# @return [Fixnum]
|
246
|
+
def open(id)
|
247
|
+
API::Device.open(id) do |pointer|
|
248
|
+
API.snd_rawmidi_open(pointer, nil, id, API::CONSTANTS[:SND_RAWMIDI_NONBLOCK])
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# Get the next bytes from the buffer
|
253
|
+
# @return [String]
|
254
|
+
def poll(handle)
|
255
|
+
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)}" unless err.eql?(-11)
|
258
|
+
end
|
259
|
+
# Upon success, err is positive and equal to the number of bytes read
|
260
|
+
# into the buffer.
|
261
|
+
if err > 0
|
262
|
+
bytes = buffer.get_bytes(0,err).unpack("a*").first.unpack("H*")
|
263
|
+
bytes.first.upcase
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
268
|
+
|
269
|
+
# Wrapper for ALSA methods dealing with output
|
270
|
+
module Output
|
271
|
+
|
272
|
+
extend self
|
273
|
+
|
274
|
+
# Send the given MIDI data to the output with the given handle
|
275
|
+
# @param [Fixnum] handle
|
276
|
+
# @param [Array<Fixnum>] data
|
277
|
+
# @return [Boolean]
|
278
|
+
def puts(handle, data)
|
279
|
+
format = "C" * data.size
|
280
|
+
pointer = FFI::MemoryPointer.new(data.size)
|
281
|
+
bytes = pointer.put_bytes(0, data.pack(format))
|
282
|
+
|
283
|
+
API.snd_rawmidi_write(handle, bytes.to_i, data.size)
|
284
|
+
API.snd_rawmidi_drain(handle)
|
285
|
+
true
|
286
|
+
end
|
287
|
+
|
288
|
+
# Open the output with the given ID
|
289
|
+
# @param [Fixnum] id
|
290
|
+
# @return [Fixnum]
|
291
|
+
def open(id)
|
292
|
+
API::Device.open(id) do |pointer|
|
293
|
+
API.snd_rawmidi_open(nil, pointer, id, 0)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
298
|
+
|
299
|
+
# Wrapper for ALSA methods dealing with devices
|
300
|
+
module Device
|
301
|
+
|
302
|
+
extend self
|
303
|
+
|
304
|
+
# @param [Fixnum] id
|
305
|
+
# @param [Symbol] direction
|
306
|
+
# @return [SndCtlCardInfo]
|
307
|
+
def get_info(id, direction)
|
308
|
+
stream_key = case direction
|
309
|
+
when :input then :SND_RAWMIDI_STREAM_INPUT
|
310
|
+
when :output then :SND_RAWMIDI_STREAM_OUTPUT
|
311
|
+
end
|
312
|
+
stream = API::CONSTANTS[stream_key]
|
313
|
+
info = API::SndRawMIDIInfo.new
|
314
|
+
API.snd_rawmidi_info_set_device(info.pointer, id)
|
315
|
+
API.snd_rawmidi_info_set_stream(info.pointer, stream)
|
316
|
+
info
|
317
|
+
end
|
318
|
+
|
319
|
+
# Close the device with the given handle
|
320
|
+
# @param [Fixnum] handle
|
321
|
+
# @return [Boolean]
|
322
|
+
def close(handle)
|
323
|
+
API.snd_rawmidi_drain(handle)
|
324
|
+
API.snd_rawmidi_close(handle)
|
325
|
+
true
|
326
|
+
end
|
327
|
+
|
328
|
+
# Open the device with the given id
|
329
|
+
# @param [Fixnum] id
|
330
|
+
# @param [Proc] block
|
331
|
+
# @return [Fixnum]
|
332
|
+
def open(id, &block)
|
333
|
+
handle_pointer = FFI::MemoryPointer.new(FFI.type_size(:int))
|
334
|
+
yield(handle_pointer)
|
335
|
+
handle_pointer.read_int
|
336
|
+
end
|
337
|
+
|
338
|
+
end
|
339
|
+
|
340
|
+
# Wrapper for ALSA methods dealing with the soundcard and subdevices
|
341
|
+
module Soundcard
|
342
|
+
|
343
|
+
extend self
|
344
|
+
|
345
|
+
# @param [SndCtlCardInfo] info
|
346
|
+
# @return [Fixnum]
|
347
|
+
def get_subdevice_count(info)
|
348
|
+
subdev_count = API.snd_rawmidi_info_get_subdevices_count(info.pointer)
|
349
|
+
subdev_count = 0 if subdev_count > 32
|
350
|
+
subdev_count
|
351
|
+
end
|
352
|
+
|
353
|
+
# @param [Fixnum, String] device_num
|
354
|
+
# @param [Fixnum] subdev_count
|
355
|
+
# @param [Fixnum] id
|
356
|
+
# @return [String]
|
357
|
+
def get_subdevice_id(soundcard_id, device_id, subdev_count, id)
|
358
|
+
ext = (subdev_count > 1) ? ",#{id}" : ''
|
359
|
+
name = API::Soundcard.get_name(soundcard_id)
|
360
|
+
"#{name},#{device_id.to_s}#{ext}"
|
361
|
+
end
|
362
|
+
|
363
|
+
# @param [SndCtlCardInfo] info
|
364
|
+
# @param [Fixnum] id
|
365
|
+
# @param [Fixnum] handle
|
366
|
+
# @return [Boolean]
|
367
|
+
def valid_subdevice?(info, id, handle)
|
368
|
+
API.snd_rawmidi_info_set_subdevice(info.pointer, id)
|
369
|
+
API.snd_ctl_rawmidi_info(handle, info.pointer) >= 0
|
370
|
+
end
|
371
|
+
|
372
|
+
# @param [Fixnum] soundcard_id
|
373
|
+
# @param [Fixnum] device_id
|
374
|
+
# @param [Symbol] direction
|
375
|
+
# @param [Proc] block
|
376
|
+
# @return [Array<Object>]
|
377
|
+
def get_subdevices(direction, soundcard_id, device_id, &block)
|
378
|
+
handle = API::Soundcard.get_handle(soundcard_id)
|
379
|
+
info = API::Device.get_info(device_id, direction)
|
380
|
+
i = 0
|
381
|
+
subdev_count = 1
|
382
|
+
available = []
|
383
|
+
while i <= subdev_count
|
384
|
+
if API::Soundcard.valid_subdevice?(info, i, handle)
|
385
|
+
subdev_count = API::Soundcard.get_subdevice_count(info) if i.zero?
|
386
|
+
system_id = API::Soundcard.get_subdevice_id(soundcard_id, device_id, subdev_count, i)
|
387
|
+
device_hash = {
|
388
|
+
:id => system_id,
|
389
|
+
:name => info[:name].to_s,
|
390
|
+
:subname => info[:subname].to_s
|
391
|
+
}
|
392
|
+
available << yield(device_hash)
|
393
|
+
i += 1
|
394
|
+
else
|
395
|
+
break
|
396
|
+
end
|
397
|
+
end
|
398
|
+
available
|
399
|
+
end
|
400
|
+
|
401
|
+
# @param [Fixnum] id
|
402
|
+
# @return [Array<Fixnum>]
|
403
|
+
def get_device_ids(id)
|
404
|
+
handle = API::Soundcard.get_handle(id)
|
405
|
+
(0..31).to_a.select do |n|
|
406
|
+
device_id = FFI::MemoryPointer.new(:int).write_int(n)
|
407
|
+
API.snd_ctl_rawmidi_next_device(handle, device_id) >= 0
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
# @param [Fixnum] soundcard_id
|
412
|
+
# @return [String]
|
413
|
+
def get_name(soundcard_id)
|
414
|
+
"hw:#{soundcard_id.to_s}"
|
415
|
+
end
|
416
|
+
|
417
|
+
# Does a soundcard exist for the given id?
|
418
|
+
# @param [Fixnum] id
|
419
|
+
# @return [Boolean]
|
420
|
+
def exists?(id)
|
421
|
+
API.snd_card_load(id) == 1
|
422
|
+
end
|
423
|
+
|
424
|
+
# @param [Fixnum] soundcard_id
|
425
|
+
# @return [Fixnum]
|
426
|
+
def get_handle(soundcard_id)
|
427
|
+
handle_pointer = FFI::MemoryPointer.new(FFI.type_size(:int))
|
428
|
+
API.snd_ctl_open(handle_pointer, get_name(soundcard_id), 0)
|
429
|
+
handle_pointer.read_int
|
430
|
+
end
|
431
|
+
|
432
|
+
end
|
433
|
+
|
434
|
+
end
|
435
|
+
end
|