voicemeeter_api_ruby 4.1.4 → 4.3.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.
@@ -0,0 +1,26 @@
1
+ require_relative "iremote"
2
+ require_relative "meta"
3
+
4
+ module Voicemeeter
5
+ class Device
6
+ def initialize(remote)
7
+ @remote = remote
8
+ end
9
+
10
+ def getter(**kwargs)
11
+ return @remote.get_num_devices(kwargs[:direction]) if kwargs[:index].nil?
12
+
13
+ vals = @remote.get_device_description(kwargs[:index], kwargs[:direction])
14
+ types = { 1 => "mme", 3 => "wdm", 4 => "ks", 5 => "asio" }
15
+ { name: vals[0], type: types[vals[1]], id: vals[2] }
16
+ end
17
+
18
+ def ins = getter(direction: "in")
19
+
20
+ def outs = getter(direction: "out")
21
+
22
+ def input(i) = getter(index: i, direction: "in")
23
+
24
+ def output(i) = getter(index: i, direction: "out")
25
+ end
26
+ end
@@ -0,0 +1,43 @@
1
+ module Voicemeeter
2
+ module Errors
3
+ class VMRemoteErrors < StandardError
4
+ end
5
+
6
+ class InstallErrors < VMRemoteErrors
7
+ end
8
+
9
+ class CAPIErrors < VMRemoteErrors
10
+ attr_accessor :value, :func
11
+
12
+ def initialize(value, func)
13
+ self.value = value
14
+ self.func = func
15
+ end
16
+
17
+ def message
18
+ "
19
+ When attempting to run function #{@func} the
20
+ C API returned value #{@value}. See documentation for further info
21
+ "
22
+ end
23
+ end
24
+
25
+ class OutOfBoundsErrors < VMRemoteErrors
26
+ attr_accessor :range
27
+
28
+ def initialize(range)
29
+ self.range = range
30
+ end
31
+
32
+ def message
33
+ if @range.kind_of?(Range)
34
+ "Value error, expected value in range (#{range.first}..#{range.last})"
35
+ elsif @range.kind_of?(Array)
36
+ "Value error, expected one of: #{@range}"
37
+ else
38
+ "Value error, expected #{@range}"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,27 @@
1
+ require "win32/registry"
2
+ require "pathname"
3
+ require_relative "errors"
4
+
5
+ module Voicemeeter
6
+ module InstallationFunctions
7
+ private
8
+
9
+ def get_vmpath(os_bits)
10
+ vm_key = "VB:Voicemeeter {17359A74-1236-5467}"
11
+ reg_key =
12
+ "Software#{os_bits == 64 ? "\\WOW6432Node" : ""}\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\"
13
+ Win32::Registry::HKEY_LOCAL_MACHINE.open(reg_key + vm_key) do |reg|
14
+ value = reg["UninstallString"]
15
+
16
+ pn = Pathname.new(value)
17
+ return pn.dirname
18
+ end
19
+ raise InstallErrors.new("Could not get the Voicemeeter path")
20
+ end
21
+
22
+ def vm_dll=(value)
23
+ raise InstallErrors.new("Could not fetch the dll file") unless value.file?
24
+ @vm_dll = value
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,34 @@
1
+ require_relative "meta"
2
+ require_relative "errors"
3
+
4
+ module Voicemeeter
5
+ class IRemote
6
+ "
7
+ Common interface between base class and higher classes.
8
+ "
9
+ include Meta_Functions
10
+
11
+ def initialize(remote, i = nil)
12
+ @remote = remote
13
+ @index = i
14
+ end
15
+
16
+ def getter(param, is_string = false)
17
+ @remote.get_parameter("#{self.identifier}.#{param}", is_string)
18
+ end
19
+
20
+ def setter(param, value)
21
+ @remote.set_parameter("#{self.identifier}.#{param}", value)
22
+ end
23
+
24
+ def identifier
25
+ raise "Called abstract method: identifier"
26
+ end
27
+
28
+ def set_multi(param_hash)
29
+ param_hash.each { |(key, val)| self.send("#{key}=", val) }
30
+ end
31
+
32
+ alias_method "apply", :set_multi
33
+ end
34
+ end
@@ -0,0 +1,79 @@
1
+ module Voicemeeter
2
+ module Kinds
3
+ "
4
+ A Kind struct for each version of Voicemeeter
5
+ "
6
+ attr_reader :kind_map, :kinds_all
7
+
8
+ Kind = Struct.new(:name, :layout)
9
+ basic =
10
+ Kind.new(
11
+ "basic",
12
+ {
13
+ strip: {
14
+ p_in: 2,
15
+ v_in: 1
16
+ },
17
+ bus: {
18
+ p_out: 1,
19
+ v_out: 1
20
+ },
21
+ vban: {
22
+ instream: 4,
23
+ outstream: 4
24
+ },
25
+ mb: 80
26
+ }
27
+ )
28
+
29
+ banana =
30
+ Kind.new(
31
+ "banana",
32
+ {
33
+ strip: {
34
+ p_in: 3,
35
+ v_in: 2
36
+ },
37
+ bus: {
38
+ p_out: 3,
39
+ v_out: 2
40
+ },
41
+ vban: {
42
+ instream: 8,
43
+ outstream: 8
44
+ },
45
+ mb: 80
46
+ }
47
+ )
48
+
49
+ potato =
50
+ Kind.new(
51
+ "potato",
52
+ {
53
+ strip: {
54
+ p_in: 5,
55
+ v_in: 3
56
+ },
57
+ bus: {
58
+ p_out: 5,
59
+ v_out: 3
60
+ },
61
+ vban: {
62
+ instream: 8,
63
+ outstream: 8
64
+ },
65
+ mb: 80
66
+ }
67
+ )
68
+
69
+ @kind_map = [basic, banana, potato].to_h { |kind| [kind.name, kind] }
70
+
71
+ def get_kind(kind_id)
72
+ @kind_map[kind_id]
73
+ end
74
+
75
+ @kinds_all = @kind_map.values
76
+
77
+ module_function :get_kind, :kind_map, :kinds_all
78
+ end
79
+ end
@@ -0,0 +1,255 @@
1
+ require_relative "errors"
2
+
3
+ module Voicemeeter
4
+ module Conversions
5
+ module_function
6
+
7
+ def Boolean(value)
8
+ case value
9
+ when true, 1
10
+ true
11
+ when false, nil, 0
12
+ false
13
+ else
14
+ raise ArgumentError, "invalid value for Boolean(): \"#{value.inspect}\""
15
+ end
16
+ end
17
+ end
18
+
19
+ module Meta_Functions
20
+ private
21
+
22
+ include Conversions
23
+
24
+ def make_accessor_bool(*params)
25
+ params.each do |param|
26
+ define_singleton_method("#{param}") do
27
+ return !(self.getter("#{param}")).zero?
28
+ end
29
+
30
+ define_singleton_method("#{param}=") do |value|
31
+ self.setter("#{param}", Boolean(value) ? 1 : 0)
32
+ end
33
+ end
34
+ end
35
+
36
+ def make_accessor_int(*params)
37
+ params.each do |param|
38
+ define_singleton_method("#{param}") do
39
+ return self.getter("#{param}").to_i
40
+ end
41
+
42
+ define_singleton_method("#{param}=") do |value|
43
+ self.setter("#{param}", value)
44
+ end
45
+ end
46
+ end
47
+
48
+ def make_accessor_float(*params)
49
+ params.each do |param|
50
+ define_singleton_method("#{param}") { return self.getter("#{param}") }
51
+
52
+ define_singleton_method("#{param}=") do |value|
53
+ self.setter("#{param}", value)
54
+ end
55
+ end
56
+ end
57
+
58
+ def make_accessor_string(*params)
59
+ params.each do |param|
60
+ define_singleton_method("#{param}") do
61
+ return self.getter("#{param}", true)
62
+ end
63
+
64
+ define_singleton_method("#{param}=") do |value|
65
+ self.setter("#{param}", value)
66
+ end
67
+ end
68
+ end
69
+
70
+ def make_reader_only(*params)
71
+ params.each do |param|
72
+ define_singleton_method("#{param}") { return self.getter("#{param}") }
73
+ end
74
+ end
75
+
76
+ def make_writer_only(*params)
77
+ params.each do |param|
78
+ define_singleton_method("#{param}=") do |value = 1|
79
+ self.setter("#{param}", value)
80
+ end
81
+ end
82
+ end
83
+
84
+ def make_channel_props(num_A, num_B)
85
+ (1..(num_A + num_B)).map { |i| i <= num_A ? "A#{i}" : "B#{i - num_A}" }
86
+ end
87
+
88
+ def make_action_prop(*params)
89
+ params.each do |param|
90
+ define_singleton_method("#{param}") { self.setter("#{param}", 1) }
91
+ end
92
+ end
93
+ end
94
+
95
+ module Channel_Meta_Functions
96
+ private
97
+
98
+ include Meta_Functions
99
+
100
+ def make_accessor_bool(*params)
101
+ params.each do |param|
102
+ cmds = { eq: "EQ.on", eq_ab: "EQ.ab" }
103
+ if cmds.key? param
104
+ cmd = cmds[param]
105
+ else
106
+ cmd = param
107
+ end
108
+
109
+ define_singleton_method("#{param}") do
110
+ return !(self.getter("#{cmd}")).zero?
111
+ end
112
+
113
+ define_singleton_method("#{param}=") do |value|
114
+ self.setter("#{cmd}", Boolean(value) ? 1 : 0)
115
+ end
116
+ end
117
+ end
118
+
119
+ def make_reader_only(*params)
120
+ params.each do |param|
121
+ cmds = { device: "device.name", sr: "device.sr" }
122
+ if cmds.key? param
123
+ cmd = cmds[param]
124
+ else
125
+ cmd = param
126
+ end
127
+
128
+ define_singleton_method("#{param}") do
129
+ case param
130
+ when :device
131
+ return self.getter("#{cmd}", true)
132
+ when :sr
133
+ return self.getter("#{cmd}").to_i
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ def make_bus_modes(*params)
140
+ params.each do |param|
141
+ define_singleton_method("#{param}") do
142
+ @remote.clear_polling
143
+ return !(self.getter("#{param}")).zero?
144
+ end
145
+
146
+ define_singleton_method("#{param}=") do |value|
147
+ self.setter("#{param}", Boolean(value) ? 1 : 0)
148
+ end
149
+ end
150
+ end
151
+ end
152
+
153
+ module Vban_Meta_Functions
154
+ private
155
+
156
+ include Meta_Functions
157
+
158
+ def make_reader_int(*params)
159
+ params.each do |param|
160
+ define_singleton_method("#{param}") do
161
+ case param
162
+ when :bit
163
+ return self.getter("#{param}").to_i == 1 ? 16 : 24
164
+ else
165
+ return self.getter("#{param}").to_i
166
+ end
167
+ end
168
+ end
169
+ end
170
+
171
+ def make_accessor_int(*params)
172
+ params.each do |param|
173
+ define_singleton_method("#{param}") do
174
+ case param
175
+ when :bit
176
+ return self.getter("#{param}").to_i == 1 ? 16 : 24
177
+ else
178
+ return self.getter("#{param}").to_i
179
+ end
180
+ end
181
+
182
+ opts = {
183
+ sr: [
184
+ 11_025,
185
+ 16_000,
186
+ 22_050,
187
+ 24_000,
188
+ 32_000,
189
+ 44_100,
190
+ 48_000,
191
+ 64_000,
192
+ 88_200,
193
+ 96_000
194
+ ],
195
+ channel: (1..8),
196
+ bit: [16, 24],
197
+ quality: (0..4),
198
+ route: (0..8)
199
+ }
200
+
201
+ define_singleton_method("#{param}=") do |value|
202
+ unless opts[param].member? value
203
+ raise OutOfBoundsErrors.new(opts[param])
204
+ end
205
+ case param
206
+ when :bit
207
+ self.setter("#{param}", value == 16 ? 1 : 2)
208
+ else
209
+ self.setter("#{param}", value)
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
215
+
216
+ module MacroButton_Meta_Functions
217
+ private
218
+
219
+ include Conversions
220
+
221
+ def make_accessor_macrobutton(*params)
222
+ params.each do |param|
223
+ mode = { state: 1, stateonly: 2, trigger: 3 }
224
+
225
+ define_singleton_method("#{param}") do
226
+ return !(self.getter(mode[param])).zero?
227
+ end
228
+
229
+ define_singleton_method("#{param}=") do |value|
230
+ self.setter(Boolean(value) ? 1 : 0, mode[param])
231
+ end
232
+ end
233
+ end
234
+ end
235
+
236
+ module Commands_Meta_Functions
237
+ private
238
+
239
+ include Meta_Functions
240
+ def make_writer_bool(*params)
241
+ params.each do |param|
242
+ cmds = { showvbanchat: "DialogShow.VBANCHAT" }
243
+ if cmds.key? param
244
+ cmd = cmds[param]
245
+ else
246
+ cmd = param
247
+ end
248
+
249
+ define_singleton_method("#{param}=") do |value|
250
+ self.setter("#{cmd}", Boolean(value) ? 1 : 0)
251
+ end
252
+ end
253
+ end
254
+ end
255
+ end
@@ -0,0 +1,61 @@
1
+ module Voicemeeter
2
+ module Mixin
3
+ module Fades
4
+ def fadeto(target, time)
5
+ self.setter("FadeTo", "(#{target}, #{time})")
6
+ sleep(@remote.delay)
7
+ end
8
+
9
+ def fadeby(change, time)
10
+ self.setter("FadeBy", "(#{change}, #{time})")
11
+ sleep(@remote.delay)
12
+ end
13
+ end
14
+
15
+ module Apps
16
+ def appgain(name, gain)
17
+ self.setter("AppGain", "(\"#{name}\", #{gain})")
18
+ end
19
+
20
+ def appmute(name, mute)
21
+ self.setter("AppMute", "(\"#{name}\", #{mute ? 1 : 0})")
22
+ end
23
+ end
24
+
25
+ module Xy
26
+ include Channel_Meta_Functions
27
+
28
+ def initialize(remote, i)
29
+ super
30
+ self.make_accessor_float :pan_x,
31
+ :pan_y,
32
+ :color_x,
33
+ :color_y,
34
+ :fx_x,
35
+ :fx_y
36
+ end
37
+ end
38
+
39
+ module Fx
40
+ include Channel_Meta_Functions
41
+
42
+ def initialize(remote, i)
43
+ super
44
+ self.make_accessor_float :reverb, :delay, :fx1, :fx2
45
+ self.make_accessor_bool :postreverb, :postdelay, :postfx1, :postfx2
46
+ end
47
+ end
48
+
49
+ module Return
50
+ include Channel_Meta_Functions
51
+
52
+ def initialize(remote, i)
53
+ super
54
+ self.make_accessor_float :returnreverb,
55
+ :returndelay,
56
+ :returnfx1,
57
+ :returnfx2
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,22 @@
1
+ require_relative "iremote"
2
+ require_relative "meta"
3
+
4
+ module Voicemeeter
5
+ class Recorder < IRemote
6
+ "
7
+ Concrete Recorder class
8
+ "
9
+
10
+ def initialize(remote)
11
+ super
12
+ self.make_action_prop :play, :stop, :record, :ff, :rew
13
+
14
+ num_A, num_B = remote.kind.layout[:bus].values
15
+ self.make_accessor_bool *make_channel_props(num_A, num_B)
16
+ end
17
+
18
+ def identifier
19
+ :recorder
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ require_relative "cbindings"
2
+ require_relative "kinds"
3
+
4
+ module Voicemeeter
5
+ module RunVM
6
+ "
7
+ Starts Voicemeeter of the Kind requested.
8
+ "
9
+ include CBindings
10
+ include Kinds
11
+
12
+ def start(kind_id)
13
+ unless Kinds.kind_map.key? kind_id
14
+ raise VMRemoteErrors.new("Unknown Voicemeeter Kind.")
15
+ end
16
+
17
+ enums =
18
+ Kinds.kinds_all.map.with_index do |kind, i|
19
+ if CBindings::OS_BITS == 64 && kind.name.to_s == "potato"
20
+ [kind.name.to_s, i + 4]
21
+ else
22
+ [kind.name.to_s, i + 1]
23
+ end
24
+ end
25
+ exes = enums.to_h { |k, v| [k, v.to_i] }
26
+
27
+ CBindings.vm_runvm(exes[kind_id])
28
+ sleep(1)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,126 @@
1
+ require_relative "iremote"
2
+ require_relative "mixin"
3
+
4
+ module Voicemeeter
5
+ class Strip < IRemote
6
+ "
7
+ Concrete Strip class
8
+ "
9
+ include Channel_Meta_Functions
10
+ include Mixin::Fades
11
+
12
+ attr_accessor :gainlayer, :levels
13
+
14
+ def self.make(remote, layout_strip)
15
+ "
16
+ Factory function for Strip classes.
17
+ "
18
+ p_in, v_in = layout_strip.values
19
+ (0...(p_in + v_in)).map do |i|
20
+ i < p_in ? PhysicalStrip.new(remote, i) : VirtualStrip.new(remote, i)
21
+ end
22
+ end
23
+
24
+ def initialize(remote, i)
25
+ super
26
+ self.make_accessor_bool :solo, :mute, :mono
27
+ self.make_accessor_float :gain
28
+ self.make_accessor_int :limit
29
+ self.make_accessor_string :label
30
+
31
+ num_A, num_B = remote.kind.layout[:bus].values
32
+ self.make_accessor_bool *make_channel_props(num_A, num_B)
33
+
34
+ @gainlayer = (0...8).map { |j| GainLayer.new(remote, i, j) }
35
+ @levels = StripLevels.new(remote, i)
36
+ end
37
+
38
+ def identifier
39
+ "strip[#{@index}]"
40
+ end
41
+ end
42
+
43
+ class PhysicalStrip < Strip
44
+ include Mixin::Xy
45
+ include Mixin::Fx
46
+
47
+ def initialize(remote, i)
48
+ super
49
+ self.make_accessor_float :comp, :gate, :audibility
50
+ self.make_reader_only :device, :sr
51
+ end
52
+ end
53
+
54
+ class VirtualStrip < Strip
55
+ include Mixin::Apps
56
+
57
+ def initialize(remote, i)
58
+ super
59
+ self.make_accessor_bool :mc
60
+ self.make_accessor_int :k
61
+ self.make_accessor_float :bass, :mid, :treble
62
+ end
63
+ end
64
+
65
+ class GainLayer < IRemote
66
+ def initialize(remote, i, j)
67
+ super(remote, i)
68
+ @j = j
69
+ end
70
+
71
+ def identifier
72
+ "strip[#{@index}]"
73
+ end
74
+
75
+ def gain
76
+ self.getter("gainlayer[#{@j}]")
77
+ end
78
+
79
+ def gain=(value)
80
+ self.setter("gainlayer[#{@j}]", value)
81
+ end
82
+ end
83
+
84
+ class StripLevels < IRemote
85
+ def initialize(remote, i)
86
+ super
87
+ if i < @remote.p_in
88
+ @init = i * 2
89
+ @offset = 2
90
+ else
91
+ @init = (@remote.p_in * 2) + ((i - @remote.p_in) * 8)
92
+ @offset = 8
93
+ end
94
+ end
95
+
96
+ def identifier
97
+ "strip[#{@index}]"
98
+ end
99
+
100
+ def get_level(mode)
101
+ if @remote.running
102
+ vals = @remote.cache["strip_level"][@init, @offset]
103
+ else
104
+ vals = (@init...@offset).map { |i| get_level(mode, i) }
105
+ end
106
+ vals.map { |x| x > 0 ? (20 * Math.log(x, 10)).round(1) : -200.0 }
107
+ end
108
+
109
+ def prefader
110
+ @remote.strip_mode = 0
111
+ get_level(0)
112
+ end
113
+
114
+ def postfader
115
+ @remote.strip_mode = 1
116
+ get_level(1)
117
+ end
118
+
119
+ def postmute
120
+ @remote.strip_mode = 2
121
+ get_level(2)
122
+ end
123
+
124
+ def isdirty? = @remote._strip_comp[@init, @offset].any?
125
+ end
126
+ end