alsa-rawmidi 0.2.14 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|