voicemeeter_api_ruby 4.3.1 → 4.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dfec081c74f1dbdd1b315bd0c3aeaff0b80da1e520b995c584d087f2753cb56e
4
- data.tar.gz: 5e40c74ebc5d8aef7f153f278b3737387f43748604d073d35a308fa817a21627
3
+ metadata.gz: 4b5218c12fdf2ff6d99a68ce6ac4e896f8fc213848c17301bbaaf1897b71446b
4
+ data.tar.gz: 66f1a8e56633c592e9fd78b0188ac5061328e6324dd7b23eaee7077a33228e1f
5
5
  SHA512:
6
- metadata.gz: 189d225992622d19c21d7eb6a3f81e08db74b5b5556b7f8794bcc3fe4c0efe17393fbd6897bae1c869d1b4ebef9eafae1433827a1ecafecb393e0349260bcfb8
7
- data.tar.gz: 933d748eb4ca2bcae4081a35c4b3cc9258b08defa79fca143c0caced8ec3c00773c55acc086f71f36c9ede579515dca945dcd26de335ed825dedd8a2529294a3
6
+ metadata.gz: efab5bb8f17a001f170cbf5f257ff523c1647a42b77dd962f1e95329b38b76ccfd458f5b18f9f66342e544b1e9fd6e1a2c4037a1411309f7a616e3c2b26e2ffc
7
+ data.tar.gz: b3a47db4e3cfa1f82baa9dfda397be0a0b64fd9fa91d4c483c387c97e99004f47efd8dc06c6fac077feef6f3c92c35d484ee27fd03eb4415b35abd13f928605d
data/CHANGELOG.md CHANGED
@@ -9,7 +9,26 @@ Before any major/minor/patch is released all unit tests will be run to verify th
9
9
 
10
10
  ## [Unreleased] - These changes have not been added to RubyGems yet
11
11
 
12
- - [ ]
12
+ - [ ] Add midi example
13
+
14
+ ## [4.4.0] - 2022-08-06
15
+
16
+ ### Added
17
+
18
+ - midi device support added.
19
+ - pdirty, mdirty, midi, ldirty kwargs added to Remote class. Initialize which events to sub to.
20
+ - Event class added. Toggle events, get list of currently subscribed.
21
+ - Voicemeeter.remote added to README in Remote class section.
22
+ - observer example updated to reflect changes.
23
+
24
+ ### Changed
25
+
26
+ - channel out props for strip/recorder now mixed in.
27
+ - By default no longer listen for level updates (high volume). Should be enabled explicitly.
28
+
29
+ ### Fixed
30
+
31
+ - bug in strip/bus levels if making capi calls directly.
13
32
 
14
33
  ## [4.3.0] - 2022-07-29
15
34
 
data/README.md CHANGED
@@ -229,7 +229,7 @@ puts vm.bus[0].levels.all
229
229
 
230
230
  ### Strip | Bus
231
231
 
232
- The following methods are Available
232
+ The following methods are available
233
233
 
234
234
  - `fadeto(amount, time)`: float, int
235
235
  - `fadeby(amount, time)`: float, int
@@ -266,7 +266,7 @@ The following properties accept boolean values.
266
266
  - `A1 - A5`: boolean
267
267
  - `B1 - A3`: boolean
268
268
 
269
- The following methods are Available
269
+ The following methods are available
270
270
 
271
271
  - `play`
272
272
  - `stop`
@@ -356,6 +356,27 @@ example:
356
356
  vm.run { (0...vm.device.ins).each { |i| puts vm.device.input(i) } }
357
357
  ```
358
358
 
359
+ ### Midi
360
+
361
+ The following properties are available:
362
+
363
+ - `channel`: int, returns the midi channel
364
+ - `current`: int, returns the current (or most recently pressed) key
365
+
366
+ The following methods are available:
367
+
368
+ - `get(key)`: int, returns most recent velocity value for a key
369
+
370
+
371
+ example:
372
+
373
+ ```ruby
374
+ puts vm.midi.get(12)
375
+ ```
376
+
377
+ get may return nil if no value for requested key in midi cache
378
+
379
+
359
380
  ### Multiple parameters
360
381
 
361
382
  - `apply`
@@ -411,6 +432,58 @@ will load a config file at mydir/configs/banana/example.toml for Voicemeeter Ban
411
432
 
412
433
  ### Remote class
413
434
 
435
+ #### Voicemeeter.remote
436
+
437
+ You may pass the following optional keyword arguments:
438
+
439
+ - `sync`: boolean=false, force the getters to wait for dirty parameters to clear. For most cases leave this as false.
440
+ - `ratelimit`: float=0.033, how often to check for updates in ms.
441
+ - `pdirty`: boolean=true, parameter updates
442
+ - `mdirty`: boolean=true, macrobutton updates
443
+ - `midi`: boolean=true, midi updates
444
+ - `ldirty`: boolean=false, level updates
445
+
446
+
447
+ #### Event updates
448
+
449
+ To receive event updates you should do the following:
450
+
451
+ - register your app to receive updates using the `vm.add_observer(observer)` method, where observer is your app.
452
+ - define an `update(subject)` callback function in your app. The value of subject may be checked for the type of event.
453
+
454
+ See `examples/observer` for a demonstration.
455
+
456
+ Level updates are considered high volume, by default they are NOT listened for. However, polling them with strip levels and bus levels methods will still work.
457
+
458
+ So if you don't wish to receive level updates, or you prefer to handle them yourself simply leave ldirty as default (False).
459
+
460
+ Each of the update types may be enabled/disabled separately. Don't use a midi controller? You have the option to disable midi updates.
461
+
462
+ example:
463
+
464
+ ```ruby
465
+ require 'voicemeeter'
466
+ # Set updates to occur every 50ms
467
+ # Listen for level updates but disable midi updates
468
+ vm = Voicemeeter.remote('banana', ratelimit: 0.05, ldirty: true, midi: false)
469
+ vm.run { ... }
470
+ ```
471
+
472
+ #### `vm.event`
473
+
474
+ You may also add/remove event subscriptions as necessary with the Event class.
475
+
476
+ example:
477
+
478
+ ```ruby
479
+ vm.event.add("ldirty")
480
+
481
+ vm.event.remove("pdirty")
482
+
483
+ # get a list of currently subscribed
484
+ p vm.event.get
485
+ ```
486
+
414
487
  Access to lower level Getters and Setters are provided with these functions:
415
488
 
416
489
  - `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.
@@ -429,7 +502,7 @@ vm.set('Strip[4].Label', 'stripname')
429
502
  vm.set('Strip[0].Gain', -3.6)
430
503
  ```
431
504
 
432
- #### Voicemeeter::start
505
+ #### Voicemeeter.start
433
506
 
434
507
  Use this function to start Voicemeeter of a kind independently of Remote class.
435
508
  for example:
@@ -3,6 +3,8 @@ require "observer"
3
3
  require_relative "runvm"
4
4
  require_relative "configs"
5
5
  require_relative "errors"
6
+ require_relative "event"
7
+ require_relative "midi"
6
8
 
7
9
  module Voicemeeter
8
10
  class Base
@@ -15,8 +17,15 @@ module Voicemeeter
15
17
  include Configs
16
18
  include RunVM
17
19
 
18
- attr_accessor :strip, :bus, :button, :vban, :command, :recorder, :device
19
- attr_accessor :strip_mode
20
+ attr_accessor :strip,
21
+ :bus,
22
+ :button,
23
+ :vban,
24
+ :command,
25
+ :recorder,
26
+ :device,
27
+ :midi
28
+ attr_accessor :strip_mode, :event
20
29
 
21
30
  attr_reader :kind, :p_in, :v_in, :p_out, :v_out, :retval, :cache
22
31
  attr_reader :running, :_strip_comp, :_bus_comp, :delay
@@ -28,31 +37,38 @@ module Voicemeeter
28
37
 
29
38
  def initialize(kind, **kwargs)
30
39
  @kind = kind
40
+ @sync = kwargs.delete(:sync) || SYNC
41
+ @ratelimit = kwargs.delete(:ratelimit) || RATELIMIT
31
42
  @p_in, @v_in = kind.layout[:strip].values
32
43
  @p_out, @v_out = kind.layout[:bus].values
33
- @cache = Hash.new
34
- @sync = kwargs[:sync] || SYNC
35
- @ratelimit = kwargs[:ratelimit] || RATELIMIT
36
44
  @running = false
37
45
  @strip_mode = 0
38
46
  @delay = DELAY
47
+ @cache = Hash.new
48
+ @midi = Midi.new
49
+ @event = Event.new(**kwargs)
39
50
  end
40
51
 
41
52
  def init_thread
42
- @running = true
53
+ puts "Listening for #{@event.get.join(", ")} events"
43
54
  @cache["strip_level"], @cache["bus_level"] = _get_levels
55
+ @running = true
44
56
  Thread.new do
45
57
  loop do
46
58
  Thread.stop if !@running
47
- if pdirty?
59
+ if @event.pdirty && pdirty?
48
60
  changed
49
61
  notify_observers("pdirty")
50
62
  end
51
- if mdirty?
63
+ if @event.mdirty && mdirty?
52
64
  changed
53
65
  notify_observers("mdirty")
54
66
  end
55
- if ldirty?
67
+ if @event.midi && get_midi_message
68
+ changed
69
+ notify_observers("midi")
70
+ end
71
+ if @event.ldirty && ldirty?
56
72
  changed
57
73
  @_strip_comp =
58
74
  @cache["strip_level"].map.with_index do |x, i|
@@ -190,11 +206,11 @@ module Voicemeeter
190
206
  raise VMRemoteErrors.new("expected in or out")
191
207
  end
192
208
  if direction == "in"
193
- val = @@cdll.call(:get_num_indevices)
209
+ res = @@cdll.call(:get_num_indevices)
194
210
  else
195
- val = @@cdll.call(:get_num_outdevices)
211
+ res = @@cdll.call(:get_num_outdevices)
196
212
  end
197
- val[0]
213
+ res[0]
198
214
  end
199
215
 
200
216
  def get_device_description(index, direction)
@@ -212,6 +228,22 @@ module Voicemeeter
212
228
  [c_name.read_string, c_type.read_long, c_hwid.read_string]
213
229
  end
214
230
 
231
+ def get_midi_message
232
+ c_msg = FFI::MemoryPointer.new(:string, 1024, true)
233
+ res = @@cdll.call(:get_midi_message, c_msg, 1024)
234
+ if res[0] > 0
235
+ vals = c_msg.read_string.bytes
236
+ vals.each_slice(3) do |ch, key, vel|
237
+ @midi.channel = ch if @midi.channel.nil? || @midi.channel != ch
238
+ @midi.current = key.to_i
239
+ @midi.set(key.to_i, vel.to_i)
240
+ end
241
+ elsif res[0] == -1 || res[0] == -2
242
+ raise CAPIErrors.new("VBVMR_GetMidiMessage returned #{msg[0]}")
243
+ end
244
+ res[0] > 0
245
+ end
246
+
215
247
  alias_method "set_multi", :set_parameter_multi
216
248
  alias_method "apply", :set_parameter_multi
217
249
  alias_method "get", :get_parameter
@@ -82,10 +82,10 @@ module Voicemeeter
82
82
  end
83
83
 
84
84
  def getter(mode)
85
- if @remote.running
85
+ if @remote.running && @remote.event.ldirty
86
86
  vals = @remote.cache["bus_level"][@init, @offset]
87
87
  else
88
- vals = (@init...@offset).map { |i| @remote.get_level(mode, i) }
88
+ vals = (@init...@init + @offset).map { |i| @remote.get_level(mode, i) }
89
89
  end
90
90
  vals.map { |x| x > 0 ? (20 * Math.log(x, 10)).round(1) : -200.0 }
91
91
  end
@@ -84,6 +84,11 @@ module Voicemeeter
84
84
  %i[long pointer pointer pointer],
85
85
  :long
86
86
 
87
+ attach_function :vm_get_midi_message,
88
+ :VBVMR_GetMidiMessage,
89
+ %i[pointer long],
90
+ :long
91
+
87
92
  @@cdll =
88
93
  lambda { |func, *args| retval = [send("vm_#{func}", *args), func] }
89
94
 
@@ -104,7 +109,7 @@ module Voicemeeter
104
109
  def retval=(values)
105
110
  " Writer validation for CAPI calls "
106
111
  retval, func = *values
107
- unless %i[get_num_indevices get_num_outdevices].include? func
112
+ unless %i[get_num_indevices get_num_outdevices get_midi_message].include? func
108
113
  raise CAPIErrors.new(retval, func) if retval&.nonzero?
109
114
  end
110
115
  @retval = retval
@@ -0,0 +1,31 @@
1
+ module Voicemeeter
2
+ class Event
3
+ attr_accessor :pdirty, :mdirty, :midi, :ldirty
4
+
5
+ def initialize(pdirty: true, mdirty: true, midi: true, ldirty: false)
6
+ @pdirty = pdirty
7
+ @mdirty = mdirty
8
+ @midi = midi
9
+ @ldirty = ldirty
10
+ end
11
+
12
+ def info(msg)
13
+ info_msg = ["#{msg} events", "Now listening for #{get.join(", ")} events"]
14
+ puts info_msg.join("\n")
15
+ end
16
+
17
+ def get
18
+ %w[pdirty mdirty midi ldirty].reject { |ev| !send("#{ev}") }
19
+ end
20
+
21
+ def add(event)
22
+ send("#{event}=", true)
23
+ info("#{event} added to")
24
+ end
25
+
26
+ def remove(event)
27
+ send("#{event}=", false)
28
+ info("#{event} removed from")
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ module Voicemeeter
2
+ class Midi
3
+ attr_accessor :cache, :current, :channel
4
+
5
+ def initialize
6
+ @cache = Hash.new
7
+ end
8
+
9
+ def get(key)
10
+ @cache[key]
11
+ end
12
+
13
+ def set(key, vel)
14
+ @cache[key] = vel
15
+ end
16
+ end
17
+ end
@@ -24,8 +24,10 @@ module Voicemeeter
24
24
 
25
25
  module Outputs
26
26
  include Channel_Meta_Functions
27
- def initialize(remote, i = nil)
27
+
28
+ def initialize(*args)
28
29
  super
30
+ remote, *rem = args
29
31
  num_A, num_B = remote.kind.layout[:bus].values
30
32
  channels =
31
33
  (1..(num_A + num_B)).map do |i|
@@ -96,10 +96,10 @@ module Voicemeeter
96
96
  end
97
97
 
98
98
  def get_level(mode)
99
- if @remote.running
99
+ if @remote.running && @remote.event.ldirty
100
100
  vals = @remote.cache["strip_level"][@init, @offset]
101
101
  else
102
- vals = (@init...@offset).map { |i| get_level(mode, i) }
102
+ vals = (@init...@init + @offset).map { |i| @remote.get_level(mode, i) }
103
103
  end
104
104
  vals.map { |x| x > 0 ? (20 * Math.log(x, 10)).round(1) : -200.0 }
105
105
  end
@@ -7,11 +7,11 @@ module Voicemeeter
7
7
  end
8
8
 
9
9
  def minor
10
- 3
10
+ 4
11
11
  end
12
12
 
13
13
  def patch
14
- 1
14
+ 0
15
15
  end
16
16
 
17
17
  def to_a
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: voicemeeter_api_ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.3.1
4
+ version: 4.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - onyx_online
@@ -93,10 +93,12 @@ files:
93
93
  - lib/voicemeeter/configs.rb
94
94
  - lib/voicemeeter/device.rb
95
95
  - lib/voicemeeter/errors.rb
96
+ - lib/voicemeeter/event.rb
96
97
  - lib/voicemeeter/inst.rb
97
98
  - lib/voicemeeter/iremote.rb
98
99
  - lib/voicemeeter/kinds.rb
99
100
  - lib/voicemeeter/meta.rb
101
+ - lib/voicemeeter/midi.rb
100
102
  - lib/voicemeeter/mixin.rb
101
103
  - lib/voicemeeter/recorder.rb
102
104
  - lib/voicemeeter/runvm.rb