voicemeeter_api_ruby 4.1.4 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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