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.
@@ -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-2011 Ari Russo
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.
@@ -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
@@ -1,16 +1,24 @@
1
- #!/usr/bin/env ruby
2
-
3
1
  #
4
- # Set of modules and classes for interacting with the ALSA Driver Interface
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
- require 'ffi'
11
-
12
- require 'alsa-rawmidi/device'
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