voicemeeter_api_ruby 4.1.5 → 4.3.1

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,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