voicemeeter_api_ruby 4.1.5 → 4.3.1

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,251 @@
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_action_prop(*params)
85
+ params.each do |param|
86
+ define_singleton_method("#{param}") { self.setter("#{param}", 1) }
87
+ end
88
+ end
89
+ end
90
+
91
+ module Channel_Meta_Functions
92
+ private
93
+
94
+ include Meta_Functions
95
+
96
+ def make_accessor_bool(*params)
97
+ params.each do |param|
98
+ cmds = { eq: "EQ.on", eq_ab: "EQ.ab" }
99
+ if cmds.key? param
100
+ cmd = cmds[param]
101
+ else
102
+ cmd = param
103
+ end
104
+
105
+ define_singleton_method("#{param}") do
106
+ return !(self.getter("#{cmd}")).zero?
107
+ end
108
+
109
+ define_singleton_method("#{param}=") do |value|
110
+ self.setter("#{cmd}", Boolean(value) ? 1 : 0)
111
+ end
112
+ end
113
+ end
114
+
115
+ def make_reader_only(*params)
116
+ params.each do |param|
117
+ cmds = { device: "device.name", sr: "device.sr" }
118
+ if cmds.key? param
119
+ cmd = cmds[param]
120
+ else
121
+ cmd = param
122
+ end
123
+
124
+ define_singleton_method("#{param}") do
125
+ case param
126
+ when :device
127
+ return self.getter("#{cmd}", true)
128
+ when :sr
129
+ return self.getter("#{cmd}").to_i
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ def make_bus_modes(*params)
136
+ params.each do |param|
137
+ define_singleton_method("#{param}") do
138
+ @remote.clear_polling
139
+ return !(self.getter("#{param}")).zero?
140
+ end
141
+
142
+ define_singleton_method("#{param}=") do |value|
143
+ self.setter("#{param}", Boolean(value) ? 1 : 0)
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ module Vban_Meta_Functions
150
+ private
151
+
152
+ include Meta_Functions
153
+
154
+ def make_reader_int(*params)
155
+ params.each do |param|
156
+ define_singleton_method("#{param}") do
157
+ case param
158
+ when :bit
159
+ return self.getter("#{param}").to_i == 1 ? 16 : 24
160
+ else
161
+ return self.getter("#{param}").to_i
162
+ end
163
+ end
164
+ end
165
+ end
166
+
167
+ def make_accessor_int(*params)
168
+ params.each do |param|
169
+ define_singleton_method("#{param}") do
170
+ case param
171
+ when :bit
172
+ return self.getter("#{param}").to_i == 1 ? 16 : 24
173
+ else
174
+ return self.getter("#{param}").to_i
175
+ end
176
+ end
177
+
178
+ opts = {
179
+ sr: [
180
+ 11_025,
181
+ 16_000,
182
+ 22_050,
183
+ 24_000,
184
+ 32_000,
185
+ 44_100,
186
+ 48_000,
187
+ 64_000,
188
+ 88_200,
189
+ 96_000
190
+ ],
191
+ channel: (1..8),
192
+ bit: [16, 24],
193
+ quality: (0..4),
194
+ route: (0..8)
195
+ }
196
+
197
+ define_singleton_method("#{param}=") do |value|
198
+ unless opts[param].member? value
199
+ raise OutOfBoundsErrors.new(opts[param])
200
+ end
201
+ case param
202
+ when :bit
203
+ self.setter("#{param}", value == 16 ? 1 : 2)
204
+ else
205
+ self.setter("#{param}", value)
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ module MacroButton_Meta_Functions
213
+ private
214
+
215
+ include Conversions
216
+
217
+ def make_accessor_macrobutton(*params)
218
+ params.each do |param|
219
+ mode = { state: 1, stateonly: 2, trigger: 3 }
220
+
221
+ define_singleton_method("#{param}") do
222
+ return !(self.getter(mode[param])).zero?
223
+ end
224
+
225
+ define_singleton_method("#{param}=") do |value|
226
+ self.setter(Boolean(value) ? 1 : 0, mode[param])
227
+ end
228
+ end
229
+ end
230
+ end
231
+
232
+ module Commands_Meta_Functions
233
+ private
234
+
235
+ include Meta_Functions
236
+ def make_writer_bool(*params)
237
+ params.each do |param|
238
+ cmds = { showvbanchat: "DialogShow.VBANCHAT" }
239
+ if cmds.key? param
240
+ cmd = cmds[param]
241
+ else
242
+ cmd = param
243
+ end
244
+
245
+ define_singleton_method("#{param}=") do |value|
246
+ self.setter("#{cmd}", Boolean(value) ? 1 : 0)
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
@@ -0,0 +1,74 @@
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 Outputs
26
+ include Channel_Meta_Functions
27
+ def initialize(remote, i = nil)
28
+ super
29
+ num_A, num_B = remote.kind.layout[:bus].values
30
+ channels =
31
+ (1..(num_A + num_B)).map do |i|
32
+ i <= num_A ? "A#{i}" : "B#{i - num_A}"
33
+ end
34
+ self.make_accessor_bool *channels
35
+ end
36
+ end
37
+
38
+ module Xy
39
+ include Channel_Meta_Functions
40
+
41
+ def initialize(remote, i)
42
+ super
43
+ self.make_accessor_float :pan_x,
44
+ :pan_y,
45
+ :color_x,
46
+ :color_y,
47
+ :fx_x,
48
+ :fx_y
49
+ end
50
+ end
51
+
52
+ module Fx
53
+ include Channel_Meta_Functions
54
+
55
+ def initialize(remote, i)
56
+ super
57
+ self.make_accessor_float :reverb, :delay, :fx1, :fx2
58
+ self.make_accessor_bool :postreverb, :postdelay, :postfx1, :postfx2
59
+ end
60
+ end
61
+
62
+ module Return
63
+ include Channel_Meta_Functions
64
+
65
+ def initialize(remote, i)
66
+ super
67
+ self.make_accessor_float :returnreverb,
68
+ :returndelay,
69
+ :returnfx1,
70
+ :returnfx2
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,20 @@
1
+ require_relative "iremote"
2
+ require_relative "meta"
3
+
4
+ module Voicemeeter
5
+ class Recorder < IRemote
6
+ "
7
+ Concrete Recorder class
8
+ "
9
+ include Mixin::Outputs
10
+
11
+ def initialize(remote)
12
+ super
13
+ self.make_action_prop :play, :stop, :record, :ff, :rew
14
+ end
15
+
16
+ def identifier
17
+ :recorder
18
+ end
19
+ end
20
+ 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,124 @@
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::Outputs
11
+ include Mixin::Fades
12
+
13
+ attr_accessor :gainlayer, :levels
14
+
15
+ def self.make(remote, layout_strip)
16
+ "
17
+ Factory function for Strip classes.
18
+ "
19
+ p_in, v_in = layout_strip.values
20
+ (0...(p_in + v_in)).map do |i|
21
+ i < p_in ? PhysicalStrip.new(remote, i) : VirtualStrip.new(remote, i)
22
+ end
23
+ end
24
+
25
+ def initialize(remote, i)
26
+ super
27
+ self.make_accessor_bool :solo, :mute, :mono
28
+ self.make_accessor_float :gain
29
+ self.make_accessor_int :limit
30
+ self.make_accessor_string :label
31
+
32
+ @gainlayer = (0...8).map { |j| GainLayer.new(remote, i, j) }
33
+ @levels = StripLevels.new(remote, i)
34
+ end
35
+
36
+ def identifier
37
+ "strip[#{@index}]"
38
+ end
39
+ end
40
+
41
+ class PhysicalStrip < Strip
42
+ include Mixin::Xy
43
+ include Mixin::Fx
44
+
45
+ def initialize(remote, i)
46
+ super
47
+ self.make_accessor_float :comp, :gate, :audibility
48
+ self.make_reader_only :device, :sr
49
+ end
50
+ end
51
+
52
+ class VirtualStrip < Strip
53
+ include Mixin::Apps
54
+
55
+ def initialize(remote, i)
56
+ super
57
+ self.make_accessor_bool :mc
58
+ self.make_accessor_int :k
59
+ self.make_accessor_float :bass, :mid, :treble
60
+ end
61
+ end
62
+
63
+ class GainLayer < IRemote
64
+ def initialize(remote, i, j)
65
+ super(remote, i)
66
+ @j = j
67
+ end
68
+
69
+ def identifier
70
+ "strip[#{@index}]"
71
+ end
72
+
73
+ def gain
74
+ self.getter("gainlayer[#{@j}]")
75
+ end
76
+
77
+ def gain=(value)
78
+ self.setter("gainlayer[#{@j}]", value)
79
+ end
80
+ end
81
+
82
+ class StripLevels < IRemote
83
+ def initialize(remote, i)
84
+ super
85
+ if i < @remote.p_in
86
+ @init = i * 2
87
+ @offset = 2
88
+ else
89
+ @init = (@remote.p_in * 2) + ((i - @remote.p_in) * 8)
90
+ @offset = 8
91
+ end
92
+ end
93
+
94
+ def identifier
95
+ "strip[#{@index}]"
96
+ end
97
+
98
+ def get_level(mode)
99
+ if @remote.running
100
+ vals = @remote.cache["strip_level"][@init, @offset]
101
+ else
102
+ vals = (@init...@offset).map { |i| get_level(mode, i) }
103
+ end
104
+ vals.map { |x| x > 0 ? (20 * Math.log(x, 10)).round(1) : -200.0 }
105
+ end
106
+
107
+ def prefader
108
+ @remote.strip_mode = 0
109
+ get_level(0)
110
+ end
111
+
112
+ def postfader
113
+ @remote.strip_mode = 1
114
+ get_level(1)
115
+ end
116
+
117
+ def postmute
118
+ @remote.strip_mode = 2
119
+ get_level(2)
120
+ end
121
+
122
+ def isdirty? = @remote._strip_comp[@init, @offset].any?
123
+ end
124
+ end