voicemeeter_api_ruby 3.0.0 → 4.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 74364d7c7210a1b080f7903862f4a1811105a3b7cb69fb572cbc729325e577ca
4
- data.tar.gz: 87289218250b0177ed6064777edf199f02d40c69d84458185d6cff5f798d2fe0
3
+ metadata.gz: e1665d58c08963437a1f97aea620c0d92d660135c653791e136a8e16b468430e
4
+ data.tar.gz: 4c9b65a8bc057ecde1206007c48321ea90205828e7f5618279c283e621a35632
5
5
  SHA512:
6
- metadata.gz: be94880faab9294919932abc5b1dc61177798f60deb2847c7672e6be0e7ca6dcc770e0a70983ab003ca098d8b08705b397ca3db3062b0756534dc881e5ed5725
7
- data.tar.gz: 46f0cc75b33f53a4b35ac4f72064ac75f88dca55437b8c5727ae44c615bc0207546c9af8b9d83846750b777e186dc364008033b19c6688dfc55743ed73da2f9d
6
+ metadata.gz: 49412d436f0a24d23f68d06757f6b4c6f652ad1c3489f83483d98b1128f26e7d4c22db192248361ce223e57ee8c3cdf1ad4e3e513b3d6055e34fea8b0c5916ef
7
+ data.tar.gz: 7a95182d33363a4301a00c17329b0b782b084d9558d6fa348207508aec7d498a71644b5444677405d7e34282e5fd18fd9d825ecd9f30800b09c7e5aa15cd9669
data/CHANGELOG.md CHANGED
@@ -5,13 +5,20 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- Before any major/minor/patch is released all test units will be run to verify they pass.
8
+ Before any major/minor/patch is released all unit tests will be run to verify they pass.
9
9
 
10
10
  ## [Unreleased] - These changes have not been added to RubyGems yet
11
11
 
12
- - [ ] Update Readme for version 3 changes.
12
+ - [x] type, version added to base class
13
+ - [x] device class implemented
14
+ - [x] common interface iremote defined
15
+ - [x] configs reworked. configs are now loaded lazily.
16
+ - [x] TOMLStrBuilder added to config, builds a default reset config
17
+ - [x] command.reset now applies reset config from toml parser.
18
+ - [x] profiles dir renamed to configs
19
+ - [x] major version bump
13
20
 
14
- ## [3.0.0] - 2022-01-24
21
+ ## [3.0.0] - 2022-05-03
15
22
 
16
23
  ### Added
17
24
 
@@ -171,7 +178,7 @@ README is up to date with version 2.0.2 onwards.
171
178
 
172
179
  ### Added
173
180
 
174
- - Added base0 test units for alias functions. This includes:
181
+ - Added base0 unit tests for alias functions. This includes:
175
182
  SetParamMultiBase0
176
183
  MacroButtonStatusWithAliasBase0
177
184
  SetParamMultiWithAliasBase0
@@ -185,7 +192,7 @@ README is up to date with version 2.0.2 onwards.
185
192
 
186
193
  ### Added
187
194
 
188
- - Base0 test units for each type.
195
+ - Base0 unit tests for each type.
189
196
  - README updated to demonstrate set_multi, Macrobutton commands,
190
197
  Special commands and a brief description on how to run tests.
191
198
 
@@ -215,7 +222,7 @@ README is up to date with version 2.0.2 onwards.
215
222
 
216
223
  ### Added
217
224
 
218
- - test/<vbtype>/errors/errors_minitest.rb test unit files added to test
225
+ - test/<vbtype>/errors/errors_minitest.rb unit test files added to test
219
226
  custom error classes for each type of voicemeeter. Errors tests:
220
227
  APIError
221
228
  LoginError
@@ -267,5 +274,5 @@ README is up to date with version 2.0.2 onwards.
267
274
 
268
275
  - Initial Commit
269
276
  - Core of API
270
- - Test units
277
+ - Unit tests
271
278
  - README.md
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/onyx-and-iris/voicemeeter-api-ruby/blob/dev/LICENSE)
2
2
  [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/plugin-ruby)
3
3
  ![tests: failed count](https://img.shields.io/badge/dynamic/json?color=blue&label=tests&query=summary.failure_count&suffix=%20failed&url=https%3A%2F%2Fraw.githubusercontent.com%2Fonyx-and-iris%2Fvoicemeeter-api-ruby%2Fdev%2Fspec%2Frspec.json)
4
+ [![Gem Version](https://badge.fury.io/rb/voicemeeter_api_ruby.svg)](https://badge.fury.io/rb/voicemeeter_api_ruby)
5
+ ![](https://ruby-gem-downloads-badge.herokuapp.com/voicemeeter_api_ruby?type=total&color=red)
4
6
 
5
7
  # Ruby Wrapper for Voicemeeter API
6
8
 
@@ -25,13 +27,13 @@ For an outline of past/future changes refer to: [CHANGELOG](CHANGELOG.md)
25
27
 
26
28
  Install voicemeeter_api_ruby gem from your console
27
29
 
28
- `gem install voicemeeter_api_ruby`
30
+ `gem 'voicemeeter_api_ruby'`
29
31
 
30
32
  ### Bundler
31
33
 
32
34
  Put this in your Gemfile:
33
35
 
34
- `gem 'voicemeeter_api_ruby', require: false`
36
+ `gem 'voicemeeter_api_ruby'`
35
37
 
36
38
  or use bundlers built in git functionality:
37
39
 
@@ -39,12 +41,11 @@ or use bundlers built in git functionality:
39
41
 
40
42
  ## `Use`
41
43
 
42
- Simplest use case, request a Remote class of a kind, then pass a block to run:
44
+ Simplest use case, request a Remote class of a kind, then pass a block to run.
43
45
 
44
- as gem:
45
- `ruby .\main.rb`
46
- with bundler:
47
- `bundle exec ruby .\main.rb`
46
+ Login and logout are handled for you in this scenario.
47
+
48
+ #### `main.rb`
48
49
 
49
50
  ```ruby
50
51
  require 'voicemeeter'
@@ -54,21 +55,21 @@ kind_id = 'banana'
54
55
  # start Voicemeeter GUI
55
56
  Voicemeeter.start(kind_id)
56
57
 
57
- vmr = Voicemeeter.remote(kind_id)
58
+ vm = Voicemeeter.remote(kind_id)
58
59
 
59
- # vmr.run accepts a block
60
- vmr.run do
60
+ # vm.run accepts a block
61
+ vm.run do
61
62
  # mute the leftmost strip
62
- vmr.strip[0].mute = true
63
- puts vmr.strip[0].mute
63
+ vm.strip[0].mute = true
64
+ puts vm.strip[0].mute
64
65
 
65
66
  # disable eq for second from left bus
66
- vmr.bus[1].eq = false
67
- puts vmr.bus[1].eq
67
+ vm.bus[1].eq = false
68
+ puts vm.bus[1].eq
68
69
  end
69
70
  ```
70
71
 
71
- Login and logout are handled for you in this scenario.
72
+ Otherwise you must remember to call `vm.login` `vm.logout` at the start/end of your code.
72
73
 
73
74
  ## `kind_id`
74
75
 
@@ -80,18 +81,74 @@ Pass the kind of Voicemeeter as an argument. kind_id may be:
80
81
 
81
82
  ## `Available commands`
82
83
 
83
- ### Channels (strip/bus)
84
+ ### Strip
84
85
 
85
- The following properties exist for audio channels.
86
+ The following properties are available.
86
87
 
87
88
  - `mono`: boolean
89
+ - `solo`: boolean
88
90
  - `mute`: boolean
89
- - `gain`: float, from -60 to 12
90
- - `mc`, `k`: boolean
91
- - `comp`, `gate`: float, from 0 to 10
91
+ - `gain`: float, from -60.0 to 12.0
92
+ - `comp`: float, from 0.0 to 10.0
93
+ - `gate`: float, from 0.0 to 10.0
94
+ - `audibility`: float, from 0.0 to 10.0
92
95
  - `limit`: int, from -40 to 12
93
96
  - `A1 - A5`, `B1 - B3`: boolean
97
+ - `label`: string
98
+ - `device`: string
99
+ - `sr`: int
100
+ - `mc`: boolean
101
+ - `k`: int, from 0 to 4
102
+ - `bass`: float, from -12.0 to 12.0
103
+ - `mid`: float, from -12.0 to 12.0
104
+ - `treble`: float, from -12.0 to 12.0
105
+
106
+ example:
107
+
108
+ ```ruby
109
+ vm.strip[3].gain = 3.7
110
+ puts vm.strip[0].label
111
+ ```
112
+
113
+ The following methods are Available.
114
+
115
+ - `appgain(name, value)`: string, float, from 0.0 to 1.0
116
+
117
+ Set the gain in db by value for the app matching name.
118
+
119
+ - `appmute(name, value)`: string, bool
120
+
121
+ Set mute state as value for the app matching name.
122
+
123
+ example:
124
+
125
+ ```ruby
126
+ vm.strip[5].appmute('Spotify', true)
127
+ vm.strip[5].appgain('Spotify', 0.5)
128
+ ```
129
+
130
+ ##### Gainlayers
131
+
132
+ - `gain`: float, from -60.0 to 12.0
133
+
134
+ example:
135
+
136
+ ```ruby
137
+ vm.strip[3].gainlayer[3].gain = 3.7
138
+ ```
139
+
140
+ Gainlayers are defined for potato version only.
141
+
142
+ ### Bus
143
+
144
+ The following properties are available.
145
+
146
+ - `mono`: boolean
147
+ - `mute`: boolean
94
148
  - `eq`: boolean
149
+ - `eq_ab`: boolean
150
+ - `sel`: boolean
151
+ - `gain`: float, from -60.0 to 12.0
95
152
  - `label`: string
96
153
  - `device`: string
97
154
  - `sr`: int
@@ -99,10 +156,46 @@ The following properties exist for audio channels.
99
156
  example:
100
157
 
101
158
  ```ruby
102
- vmr.strip[3].gain = 3.7
103
- puts strip[0].label
159
+ vm.bus[3].gain = 3.7
160
+ puts vm.bus[0].label
104
161
 
105
- vmr.bus[4].mono = true
162
+ vm.bus[4].mono = true
163
+ ```
164
+
165
+ ##### Modes
166
+
167
+ - `normal`: boolean
168
+ - `amix`: boolean
169
+ - `bmix`: boolean
170
+ - `composite`: boolean
171
+ - `tvmix`: boolean
172
+ - `upmix21`: boolean
173
+ - `upmix41`: boolean
174
+ - `upmix61`: boolean
175
+ - `centeronly`: boolean
176
+ - `lfeonly`: boolean
177
+ - `rearonly`: boolean
178
+
179
+ example:
180
+
181
+ ```ruby
182
+ vm.bus[4].mode.amix = true
183
+ ```
184
+
185
+ ### Strip | Bus
186
+
187
+ The following methods are Available
188
+
189
+ - `fadeto(amount, time)`: float, int
190
+ - `fadeby(amount, time)`: float, int
191
+
192
+ Modify gain to or by the selected amount in db over a time interval in ms.
193
+
194
+ example:
195
+
196
+ ```ruby
197
+ vm.strip[0].fadeto(-10.3, 1000)
198
+ vm.bus[3].fadeby(-5.6, 500)
106
199
  ```
107
200
 
108
201
  ### Macrobuttons
@@ -116,12 +209,18 @@ Three modes defined: state, stateonly and trigger.
116
209
  example:
117
210
 
118
211
  ```ruby
119
- vmr.button[37].state = true
120
- vmr.button[55].trigger = false
212
+ vm.button[37].state = true
213
+ vm.button[55].trigger = false
121
214
  ```
122
215
 
123
216
  ### Recorder
124
217
 
218
+ The following properties accept boolean values.
219
+
220
+ - `loop`: boolean
221
+ - `A1 - A5`: boolean
222
+ - `B1 - A3`: boolean
223
+
125
224
  The following methods are Available
126
225
 
127
226
  - `play`
@@ -130,34 +229,31 @@ The following methods are Available
130
229
  - `record`
131
230
  - `ff`
132
231
  - `rew`
133
- The following properties accept boolean values.
134
- - `loop`: boolean
135
- - `A1 - A5`: boolean
136
- - `B1 - A3`: boolean
137
- Load accepts a string:
138
- - `load`: string
232
+ - `load(filepath)`: string
139
233
 
140
234
  example:
141
235
 
142
236
  ```ruby
143
- vmr.recorder.play
144
- vmr.recorder.stop
237
+ vm.recorder.play
238
+ vm.recorder.stop
145
239
 
146
240
  # Enable loop play
147
- vmr.recorder.loop = True
241
+ vm.recorder.loop = True
148
242
 
149
243
  # Disable recorder out channel B2
150
- vmr.recorder.B2 = False
244
+ vm.recorder.B2 = False
151
245
 
152
246
  # filepath as string
153
- vmr.recorder.load('C:\music\mytune.mp3')
247
+ vm.recorder.load('C:\music\mytune.mp3')
154
248
  ```
155
249
 
156
250
  ### VBAN
157
251
 
158
- - `vmr.vban.enable` `vmr.vban.disable` Turn VBAN on or off
252
+ - `vm.vban.enable` `vm.vban.disable` Turn VBAN on or off
253
+
254
+ ##### Instream | Outstream
159
255
 
160
- For each vban in/out stream the following properties are defined:
256
+ The following properties are available.
161
257
 
162
258
  - `on`: boolean
163
259
  - `name`: string
@@ -175,13 +271,13 @@ example:
175
271
 
176
272
  ```ruby
177
273
  # turn VBAN on
178
- vmr.vban.enable
274
+ vm.vban.enable
179
275
 
180
276
  # turn on vban instream 0
181
- vmr.vban.instream[0].on = True
277
+ vm.vban.instream[0].on = True
182
278
 
183
279
  # set bit property for outstream 3 to 24
184
- vmr.vban.outstream[3].bit = 24
280
+ vm.vban.outstream[3].bit = 24
185
281
  ```
186
282
 
187
283
  ### Command
@@ -200,8 +296,19 @@ The following properties are write only and accept boolean values.
200
296
  example:
201
297
 
202
298
  ```ruby
203
- vmr.command.restart
204
- vmr.command.showvbanchat = true
299
+ vm.command.restart
300
+ vm.command.showvbanchat = true
301
+ ```
302
+
303
+ ### Device
304
+
305
+ - `ins` `outs` : Returns the number of input/output devices
306
+ - `input(i)` `output(i)` : Returns a hash of device properties for device[i]
307
+
308
+ example:
309
+
310
+ ```ruby
311
+ vm.run { (0...vm.device.ins).each { |i| puts vm.device.input(i) } }
205
312
  ```
206
313
 
207
314
  ### Multiple parameters
@@ -210,7 +317,7 @@ vmr.command.showvbanchat = true
210
317
  Set many strip/bus/macrobutton/vban parameters at once, for example:
211
318
 
212
319
  ```ruby
213
- vmr.set_multi(
320
+ vm.set_multi(
214
321
  {
215
322
  strip_0: {
216
323
  mute: true,
@@ -235,30 +342,46 @@ vmr.set_multi(
235
342
  Or for each class you may do:
236
343
 
237
344
  ```ruby
238
- vmr.strip[0].set_multi(mute: true, gain: 3.2, A1: true)
239
- vmr.vban.outstream[0].set_multi(on: true, name: 'streamname', bit: 24)
345
+ vm.strip[0].set_multi(mute: true, gain: 3.2, A1: true)
346
+ vm.vban.outstream[0].set_multi(on: true, name: 'streamname', bit: 24)
240
347
  ```
241
348
 
349
+ ## Config Files
350
+
351
+ `vm.set_config(<configname>)`
352
+
353
+ You may load config files in TOML format.
354
+ Three example configs have been included with the package. Remember to save
355
+ current settings before loading a config. To set one you may do:
356
+
357
+ ```ruby
358
+ require 'voicemeeter'
359
+ vm = Voicemeeter.remote('banana')
360
+ vm.run { vm.set_profile('example') }
361
+ ```
362
+
363
+ will load a config file at configs/banana/example.toml for Voicemeeter Banana.
364
+
242
365
  ## `Voicemeeter Module`
243
366
 
244
367
  ### Remote class
245
368
 
246
369
  Access to lower level Getters and Setters are provided with these functions:
247
370
 
248
- - `vmr.get(param, string=false)`: For getting the value of any parameter. Set string to true if getting a property value expected to return a string.
249
- - `vmr.set(param, value)`: For setting the value of any parameter.
371
+ - `vm.get(param, string=false)`: For getting the value of any parameter. Set string to true if getting a property value expected to return a string.
372
+ - `vm.set(param, value)`: For setting the value of any parameter.
250
373
 
251
374
  Access to lower level polling functions are provided with these functions:
252
375
 
253
- - `vmr.pdirty?`: Returns true if a parameter has been updated.
254
- - `vmr.mdirty?`: Returns true if a macrobutton has been updated.
376
+ - `vm.pdirty?`: Returns true if a parameter has been updated.
377
+ - `vm.mdirty?`: Returns true if a macrobutton has been updated.
255
378
 
256
379
  example:
257
380
 
258
381
  ```ruby
259
- vmr.get('Strip[2].Mute')
260
- vmr.set('Strip[4].Label', 'stripname')
261
- vmr.set('Strip[0].Gain', -3.6)
382
+ vm.get('Strip[2].Mute')
383
+ vm.set('Strip[4].Label', 'stripname')
384
+ vm.set('Strip[0].Gain', -3.6)
262
385
  ```
263
386
 
264
387
  #### Voicemeeter::start
@@ -271,24 +394,6 @@ require 'voicemeeter'
271
394
  Voicemeeter.start('banana')
272
395
  ```
273
396
 
274
- ## Config Files
275
-
276
- `vmr.set_profile('config')`
277
-
278
- You may load config files in TOML format.
279
- Three example profiles have been included with the package. Remember to save
280
- current settings before loading a profile. To test them simply rename \_profiles
281
- directory to profiles. They will be loaded into memory but not set. To set one
282
- you may do:
283
-
284
- ```ruby
285
- require 'voicemeeter'
286
- vmr = Voicemeeter.remote('banana')
287
- vmr.run { vmr.set_profile('config') }
288
- ```
289
-
290
- will load a config file at profiles/banana/config.toml for Voicemeeter Banana.
291
-
292
397
  ### Run tests
293
398
 
294
399
  To run all tests:
@@ -305,4 +410,4 @@ Bundle exec rspec --tag 'higher'
305
410
 
306
411
  ### Official Documentation
307
412
 
308
- - [Voicemeeter Remote C API](https://forum.vb-audio.com/viewtopic.php?f=8&t=346)
413
+ - [Voicemeeter Remote C API](https://github.com/onyx-and-iris/Voicemeeter-SDK/blob/main/VoicemeeterRemoteAPI.pdf)
data/lib/base.rb CHANGED
@@ -1,12 +1,8 @@
1
+ require 'observer'
2
+
1
3
  require_relative 'runvm'
2
- require_relative 'profiles'
4
+ require_relative 'configs'
3
5
  require_relative 'errors'
4
- require_relative 'strip'
5
- require_relative 'bus'
6
- require_relative 'button'
7
- require_relative 'vban'
8
- require_relative 'command'
9
- require_relative 'recorder'
10
6
 
11
7
  class Base
12
8
  '
@@ -14,17 +10,18 @@ class Base
14
10
 
15
11
  Mixin required modules
16
12
  '
17
- include Profiles
13
+ include Observable
14
+ include Configs
18
15
  include RunVM
19
16
 
20
- attr_accessor :strip, :bus, :button, :vban, :command, :recorder
17
+ attr_accessor :strip, :bus, :button, :vban, :command, :recorder, :device
21
18
 
22
- attr_reader :kind, :retval, :cache, :profiles, :delay
19
+ attr_reader :kind, :retval, :cache, :delay
23
20
 
24
21
  DELAY = 0.001
25
22
  SYNC = false
23
+ RATELIMIT = 0.033
26
24
  SIZE = 1
27
- BUFF = 512
28
25
 
29
26
  def initialize(kind, **kwargs)
30
27
  @kind = kind
@@ -32,16 +29,33 @@ class Base
32
29
  @p_out, @v_out = kind.layout[:bus].values
33
30
  @cache = Hash.new
34
31
  @sync = kwargs[:sync] || SYNC
32
+ @ratelimit = kwargs[:ratelimit] || RATELIMIT
35
33
  @delay = DELAY
36
- @profiles = get_profiles(@kind)
37
- @cdll =
38
- lambda do |func, *args|
39
- self.retval = [send("vmr_#{func}", *args), func]
34
+ @running = true
35
+ end
36
+
37
+ def init_thread
38
+ Thread.new do
39
+ loop do
40
+ Thread.stop if !@running
41
+ if pdirty?
42
+ changed
43
+ notify_observers('pdirty')
44
+ elsif mdirty?
45
+ changed
46
+ notify_observers('mdirty')
47
+ end
48
+ sleep(@ratelimit)
40
49
  end
50
+ end
51
+ end
52
+
53
+ def end_thread
54
+ @running = false
41
55
  end
42
56
 
43
57
  def login
44
- @cdll.call('login')
58
+ @@cdll.call(:login)
45
59
  clear_polling
46
60
  rescue CAPIErrors => error
47
61
  case
@@ -56,18 +70,35 @@ class Base
56
70
  def logout
57
71
  clear_polling
58
72
  sleep(0.1)
59
- @cdll.call('logout')
73
+ @@cdll.call(:logout)
74
+ end
75
+
76
+ def type
77
+ c_type = FFI::MemoryPointer.new(:long, SIZE)
78
+ @@cdll.call(:vmtype, c_type)
79
+ types = { 1 => 'basic', 2 => 'banana', 3 => 'potato' }
80
+ types[c_type.read_long]
81
+ end
82
+
83
+ def version
84
+ c_ver = FFI::MemoryPointer.new(:long, SIZE)
85
+ @@cdll.call(:vmversion, c_ver)
86
+ v1 = (c_ver.read_long & 0xFF000000) >> 24
87
+ v2 = (c_ver.read_long & 0x00FF0000) >> 16
88
+ v3 = (c_ver.read_long & 0x0000FF00) >> 8
89
+ v4 = c_ver.read_long & 0x000000FF
90
+ "#{v1}.#{v2}.#{v3}.#{v4}"
60
91
  end
61
92
 
62
93
  def get_parameter(name, is_string = false)
63
94
  self.polling('get_parameter', name: name) do
64
95
  if is_string
65
- c_get = FFI::MemoryPointer.new(:string, BUFF, true)
66
- @cdll.call('get_parameter_string', name, c_get)
96
+ c_get = FFI::MemoryPointer.new(:string, 512, true)
97
+ @@cdll.call(:get_parameter_string, name, c_get)
67
98
  c_get.read_string
68
99
  else
69
100
  c_get = FFI::MemoryPointer.new(:float, SIZE)
70
- @cdll.call('get_parameter_float', name, c_get)
101
+ @@cdll.call(:get_parameter_float, name, c_get)
71
102
  c_get.read_float.round(1)
72
103
  end
73
104
  end
@@ -75,9 +106,9 @@ class Base
75
106
 
76
107
  def set_parameter(name, value)
77
108
  if value.is_a? String
78
- @cdll.call('set_parameter_string', name, value)
109
+ @@cdll.call(:set_parameter_string, name, value)
79
110
  else
80
- @cdll.call('set_parameter_float', name, value.to_f)
111
+ @@cdll.call(:set_parameter_float, name, value.to_f)
81
112
  end
82
113
  @cache.store(name, value)
83
114
  end
@@ -85,13 +116,13 @@ class Base
85
116
  def get_buttonstatus(id, mode)
86
117
  self.polling('get_buttonstatus', id: id, mode: mode) do
87
118
  c_get = FFI::MemoryPointer.new(:float, SIZE)
88
- @cdll.call('get_buttonstatus', id, c_get, mode)
119
+ @@cdll.call(:get_buttonstatus, id, c_get, mode)
89
120
  c_get.read_float.to_i
90
121
  end
91
122
  end
92
123
 
93
124
  def set_buttonstatus(id, state, mode)
94
- @cdll.call('set_buttonstatus', id, state, mode)
125
+ @@cdll.call(:set_buttonstatus, id, state, mode)
95
126
  @cache.store("mb_#{id}_#{mode}", state)
96
127
  end
97
128
 
@@ -124,13 +155,13 @@ class Base
124
155
 
125
156
  def get_level(type, index)
126
157
  c_get = FFI::MemoryPointer.new(:float, SIZE)
127
- @cdll.call('get_level', type, index, c_get)
158
+ @@cdll.call(:get_level, type, index, c_get)
128
159
  c_get.read_float
129
160
  end
130
161
 
131
162
  def strip_levels
132
163
  '
133
- Returns the full level array for strips, PREFADER mode,
164
+ Returns the full level array for strips, PREFADER mode,
134
165
  before math conversion
135
166
  '
136
167
  (0...(2 * @p_in + 8 * @v_in)).map { |i| get_level(0, i) }
@@ -143,9 +174,36 @@ class Base
143
174
  (0...(8 * (@p_out + @v_out))).map { |i| get_level(3, i) }
144
175
  end
145
176
 
177
+ def get_num_devices(direction)
178
+ unless %w[in out].include? direction
179
+ raise VMRemoteErrors.new('expected in or out')
180
+ end
181
+ if direction == 'in'
182
+ val = @@cdll.call(:get_num_indevices)
183
+ else
184
+ val = @@cdll.call(:get_num_outdevices)
185
+ end
186
+ val[0]
187
+ end
188
+
189
+ def get_device_description(index, direction)
190
+ unless %w[in out].include? direction
191
+ raise VMRemoteErrors.new('expected in or out')
192
+ end
193
+ c_type = FFI::MemoryPointer.new(:long, SIZE)
194
+ c_name = FFI::MemoryPointer.new(:string, 256, true)
195
+ c_hwid = FFI::MemoryPointer.new(:string, 256, true)
196
+ if direction == 'in'
197
+ @@cdll.call(:get_desc_indevices, index, c_type, c_name, c_hwid)
198
+ else
199
+ @@cdll.call(:get_desc_outdevices, index, c_type, c_name, c_hwid)
200
+ end
201
+ [c_name.read_string, c_type.read_long, c_hwid.read_string]
202
+ end
203
+
146
204
  alias_method 'set_multi', :set_parameter_multi
147
205
  alias_method 'get', :get_parameter
148
206
  alias_method 'set', :set_parameter
149
207
  alias_method 'pdirty', :pdirty?
150
- alias_method 'mdirty', :pdirty?
208
+ alias_method 'mdirty', :mdirty?
151
209
  end