voicemeeter_api_ruby 2.0.2 → 3.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +271 -0
- data/LICENSE +21 -0
- data/README.md +308 -0
- data/lib/base.rb +151 -132
- data/lib/bus.rb +64 -31
- data/lib/button.rb +36 -35
- data/lib/cbindings.rb +103 -0
- data/lib/channel.rb +46 -25
- data/lib/command.rb +41 -41
- data/lib/errors.rb +39 -37
- data/lib/inst.rb +38 -40
- data/lib/kinds.rb +77 -0
- data/lib/meta.rb +274 -242
- data/lib/profiles.rb +28 -0
- data/lib/recorder.rb +37 -40
- data/lib/runvm.rb +29 -20
- data/lib/strip.rb +80 -49
- data/lib/vban.rb +103 -92
- data/lib/version.rb +3 -3
- data/lib/voicemeeter.rb +73 -30
- metadata +22 -26
- data/lib/routines.rb +0 -128
data/lib/base.rb
CHANGED
@@ -1,132 +1,151 @@
|
|
1
|
-
|
2
|
-
require_relative '
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
end
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
@
|
83
|
-
end
|
84
|
-
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
1
|
+
require_relative 'runvm'
|
2
|
+
require_relative 'profiles'
|
3
|
+
require_relative 'errors'
|
4
|
+
require_relative 'strip'
|
5
|
+
require_relative 'bus'
|
6
|
+
require_relative 'button'
|
7
|
+
require_relative 'vban'
|
8
|
+
require_relative 'command'
|
9
|
+
require_relative 'recorder'
|
10
|
+
|
11
|
+
class Base
|
12
|
+
'
|
13
|
+
Base class responsible for wrapping the C Remote API
|
14
|
+
|
15
|
+
Mixin required modules
|
16
|
+
'
|
17
|
+
include Profiles
|
18
|
+
include RunVM
|
19
|
+
|
20
|
+
attr_accessor :strip, :bus, :button, :vban, :command, :recorder
|
21
|
+
|
22
|
+
attr_reader :kind, :retval, :cache, :profiles, :delay
|
23
|
+
|
24
|
+
DELAY = 0.001
|
25
|
+
SYNC = false
|
26
|
+
SIZE = 1
|
27
|
+
BUFF = 512
|
28
|
+
|
29
|
+
def initialize(kind, **kwargs)
|
30
|
+
@kind = kind
|
31
|
+
@p_in, @v_in = kind.layout[:strip].values
|
32
|
+
@p_out, @v_out = kind.layout[:bus].values
|
33
|
+
@cache = Hash.new
|
34
|
+
@sync = kwargs[:sync] || SYNC
|
35
|
+
@delay = DELAY
|
36
|
+
@profiles = get_profiles(@kind)
|
37
|
+
@cdll =
|
38
|
+
lambda do |func, *args|
|
39
|
+
self.retval = [send("vmr_#{func}", *args), func]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def login
|
44
|
+
@cdll.call('login')
|
45
|
+
clear_polling
|
46
|
+
rescue CAPIErrors => error
|
47
|
+
case
|
48
|
+
when error.value == 1
|
49
|
+
self.start(@kind.name)
|
50
|
+
clear_polling
|
51
|
+
when error.value < 0
|
52
|
+
raise
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def logout
|
57
|
+
clear_polling
|
58
|
+
sleep(0.1)
|
59
|
+
@cdll.call('logout')
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_parameter(name, is_string = false)
|
63
|
+
self.polling('get_parameter', name: name) do
|
64
|
+
if is_string
|
65
|
+
c_get = FFI::MemoryPointer.new(:string, BUFF, true)
|
66
|
+
@cdll.call('get_parameter_string', name, c_get)
|
67
|
+
c_get.read_string
|
68
|
+
else
|
69
|
+
c_get = FFI::MemoryPointer.new(:float, SIZE)
|
70
|
+
@cdll.call('get_parameter_float', name, c_get)
|
71
|
+
c_get.read_float.round(1)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def set_parameter(name, value)
|
77
|
+
if value.is_a? String
|
78
|
+
@cdll.call('set_parameter_string', name, value)
|
79
|
+
else
|
80
|
+
@cdll.call('set_parameter_float', name, value.to_f)
|
81
|
+
end
|
82
|
+
@cache.store(name, value)
|
83
|
+
end
|
84
|
+
|
85
|
+
def get_buttonstatus(id, mode)
|
86
|
+
self.polling('get_buttonstatus', id: id, mode: mode) do
|
87
|
+
c_get = FFI::MemoryPointer.new(:float, SIZE)
|
88
|
+
@cdll.call('get_buttonstatus', id, c_get, mode)
|
89
|
+
c_get.read_float.to_i
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def set_buttonstatus(id, state, mode)
|
94
|
+
@cdll.call('set_buttonstatus', id, state, mode)
|
95
|
+
@cache.store("mb_#{id}_#{mode}", state)
|
96
|
+
end
|
97
|
+
|
98
|
+
def set_parameter_multi(param_hash)
|
99
|
+
param_hash.each do |(key, val)|
|
100
|
+
prop, m2, m3, *rem = key.to_s.split('_')
|
101
|
+
if m2.to_i.to_s == m2
|
102
|
+
m2 = m2.to_i
|
103
|
+
elsif m3.to_i.to_s == m3
|
104
|
+
m3 = m3.to_i
|
105
|
+
end
|
106
|
+
|
107
|
+
case prop
|
108
|
+
when 'strip'
|
109
|
+
self.strip[m2].set_multi(val)
|
110
|
+
when 'bus'
|
111
|
+
self.bus[m2].set_multi(val)
|
112
|
+
when 'button', 'mb'
|
113
|
+
self.button[m2].set_multi(val)
|
114
|
+
when 'vban'
|
115
|
+
if %w[instream in].include? m2
|
116
|
+
self.vban.instream[m3].set_multi(val)
|
117
|
+
elsif %w[outstream out].include? m2
|
118
|
+
self.vban.outstream[m3].set_multi(val)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
sleep(DELAY)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def get_level(type, index)
|
126
|
+
c_get = FFI::MemoryPointer.new(:float, SIZE)
|
127
|
+
@cdll.call('get_level', type, index, c_get)
|
128
|
+
c_get.read_float
|
129
|
+
end
|
130
|
+
|
131
|
+
def strip_levels
|
132
|
+
'
|
133
|
+
Returns the full level array for strips, PREFADER mode,
|
134
|
+
before math conversion
|
135
|
+
'
|
136
|
+
(0...(2 * @p_in + 8 * @v_in)).map { |i| get_level(0, i) }
|
137
|
+
end
|
138
|
+
|
139
|
+
def bus_levels
|
140
|
+
'
|
141
|
+
Returns the full level array for buses, before math conversion
|
142
|
+
'
|
143
|
+
(0...(8 * (@p_out + @v_out))).map { |i| get_level(3, i) }
|
144
|
+
end
|
145
|
+
|
146
|
+
alias_method 'set_multi', :set_parameter_multi
|
147
|
+
alias_method 'get', :get_parameter
|
148
|
+
alias_method 'set', :set_parameter
|
149
|
+
alias_method 'pdirty', :pdirty?
|
150
|
+
alias_method 'mdirty', :pdirty?
|
151
|
+
end
|
data/lib/bus.rb
CHANGED
@@ -1,31 +1,64 @@
|
|
1
|
-
require_relative 'channel'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
|
31
|
-
|
1
|
+
require_relative 'channel'
|
2
|
+
|
3
|
+
class Bus < IChannel
|
4
|
+
'
|
5
|
+
Concrete Bus class
|
6
|
+
'
|
7
|
+
include Fades
|
8
|
+
|
9
|
+
attr_accessor :mode
|
10
|
+
|
11
|
+
def self.make(remote, layout_bus)
|
12
|
+
'
|
13
|
+
Factory function for Bus classes.
|
14
|
+
'
|
15
|
+
p_out, v_out = layout_bus.values
|
16
|
+
(0...(p_out + v_out)).map do |i|
|
17
|
+
i < p_out ? PhysicalBus.new(remote, i) : VirtualBus.new(remote, i)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(remote, i)
|
22
|
+
super
|
23
|
+
self.make_accessor_bool :mute, :mono, :eq
|
24
|
+
self.make_accessor_float :gain
|
25
|
+
self.make_accessor_string :label
|
26
|
+
|
27
|
+
@mode = BusModes.new(remote, i)
|
28
|
+
end
|
29
|
+
|
30
|
+
def identifier
|
31
|
+
:bus
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class PhysicalBus < Bus
|
36
|
+
end
|
37
|
+
class VirtualBus < Bus
|
38
|
+
end
|
39
|
+
|
40
|
+
class BusModes < IChannel
|
41
|
+
def initialize(remote, i)
|
42
|
+
super
|
43
|
+
self.make_bus_modes :normal,
|
44
|
+
:amix,
|
45
|
+
:bmix,
|
46
|
+
:repeat,
|
47
|
+
:composite,
|
48
|
+
:tvmix,
|
49
|
+
:upmix21,
|
50
|
+
:upmix41,
|
51
|
+
:upmix61,
|
52
|
+
:centeronly,
|
53
|
+
:lfeonly,
|
54
|
+
:rearonly
|
55
|
+
end
|
56
|
+
|
57
|
+
def identifier
|
58
|
+
:bus
|
59
|
+
end
|
60
|
+
|
61
|
+
def cmd
|
62
|
+
"#{super}.mode"
|
63
|
+
end
|
64
|
+
end
|
data/lib/button.rb
CHANGED
@@ -1,35 +1,36 @@
|
|
1
|
-
require_relative 'meta'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
self.
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
1
|
+
require_relative 'meta'
|
2
|
+
|
3
|
+
class IMacroButton
|
4
|
+
include MacroButton_Meta_Functions
|
5
|
+
|
6
|
+
attr_accessor :remote, :index
|
7
|
+
|
8
|
+
def initialize(remote, index)
|
9
|
+
self.remote = remote
|
10
|
+
self.index = index
|
11
|
+
end
|
12
|
+
|
13
|
+
def getter(mode)
|
14
|
+
@remote.get_buttonstatus(@index, mode)
|
15
|
+
end
|
16
|
+
|
17
|
+
def setter(set, mode)
|
18
|
+
@remote.set_buttonstatus(@index, set, mode)
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_multi(param_hash)
|
22
|
+
param_hash.each { |(key, val)| self.send("#{key}=", val) }
|
23
|
+
sleep(remote.delay)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class MacroButton < IMacroButton
|
28
|
+
def self.make(remote, num_buttons)
|
29
|
+
(0...num_buttons).map { |i| MacroButton.new(remote, i) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(remote, i)
|
33
|
+
super
|
34
|
+
self.make_accessor_macrobutton :state, :stateonly, :trigger
|
35
|
+
end
|
36
|
+
end
|
data/lib/cbindings.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
require_relative 'inst'
|
3
|
+
|
4
|
+
include InstallationFunctions
|
5
|
+
|
6
|
+
module CBindings
|
7
|
+
'
|
8
|
+
Creates Ruby bindings to the C DLL
|
9
|
+
|
10
|
+
Performs other low level tasks
|
11
|
+
'
|
12
|
+
extend FFI::Library
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
begin
|
17
|
+
OS_BITS = get_arch
|
18
|
+
VM_PATH = get_vmpath(OS_BITS)
|
19
|
+
DLL_NAME = "VoicemeeterRemote#{OS_BITS == 64 ? '64' : ''}.dll"
|
20
|
+
|
21
|
+
self.vmr_dll = VM_PATH.join(DLL_NAME)
|
22
|
+
rescue InstallErrors => error
|
23
|
+
puts "ERROR: #{error.message}"
|
24
|
+
raise
|
25
|
+
end
|
26
|
+
|
27
|
+
ffi_lib @vmr_dll
|
28
|
+
ffi_convention :stdcall
|
29
|
+
|
30
|
+
attach_function :vmr_login, :VBVMR_Login, [], :long
|
31
|
+
attach_function :vmr_logout, :VBVMR_Logout, [], :long
|
32
|
+
attach_function :vmr_runvm, :VBVMR_RunVoicemeeter, [:long], :long
|
33
|
+
attach_function :vmr_vmtype, :VBVMR_GetVoicemeeterType, [:pointer], :long
|
34
|
+
|
35
|
+
attach_function :vmr_mdirty, :VBVMR_MacroButton_IsDirty, [], :long
|
36
|
+
attach_function :vmr_get_buttonstatus,
|
37
|
+
:VBVMR_MacroButton_GetStatus,
|
38
|
+
%i[long pointer long],
|
39
|
+
:long
|
40
|
+
attach_function :vmr_set_buttonstatus,
|
41
|
+
:VBVMR_MacroButton_SetStatus,
|
42
|
+
%i[long float long],
|
43
|
+
:long
|
44
|
+
|
45
|
+
attach_function :vmr_pdirty, :VBVMR_IsParametersDirty, [], :long
|
46
|
+
attach_function :vmr_get_parameter_float,
|
47
|
+
:VBVMR_GetParameterFloat,
|
48
|
+
%i[string pointer],
|
49
|
+
:long
|
50
|
+
attach_function :vmr_set_parameter_float,
|
51
|
+
:VBVMR_SetParameterFloat,
|
52
|
+
%i[string float],
|
53
|
+
:long
|
54
|
+
|
55
|
+
attach_function :vmr_get_parameter_string,
|
56
|
+
:VBVMR_GetParameterStringA,
|
57
|
+
%i[string pointer],
|
58
|
+
:long
|
59
|
+
attach_function :vmr_set_parameter_string,
|
60
|
+
:VBVMR_SetParameterStringA,
|
61
|
+
%i[string string],
|
62
|
+
:long
|
63
|
+
|
64
|
+
attach_function :vmr_set_parameter_multi,
|
65
|
+
:VBVMR_SetParameters,
|
66
|
+
[:string],
|
67
|
+
:long
|
68
|
+
|
69
|
+
attach_function :vmr_get_level,
|
70
|
+
:VBVMR_GetLevel,
|
71
|
+
%i[long long pointer],
|
72
|
+
:long
|
73
|
+
|
74
|
+
def polling(func, **kwargs)
|
75
|
+
params = {
|
76
|
+
'get_parameter' => kwargs[:name],
|
77
|
+
'get_buttonstatus' => "mb_#{kwargs[:id]}_#{kwargs[:mode]}",
|
78
|
+
}
|
79
|
+
return @cache.delete(params[func]) if @cache.key? params[func]
|
80
|
+
|
81
|
+
self.clear_polling if @sync
|
82
|
+
|
83
|
+
yield
|
84
|
+
end
|
85
|
+
|
86
|
+
def retval=(values)
|
87
|
+
' Writer validation for CAPI calls '
|
88
|
+
retval, func = *values
|
89
|
+
raise CAPIErrors.new(retval, func) if retval&.nonzero?
|
90
|
+
@retval = retval
|
91
|
+
end
|
92
|
+
|
93
|
+
public
|
94
|
+
|
95
|
+
def clear_polling
|
96
|
+
while self.pdirty? || self.mdirty?
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def pdirty?() = vmr_pdirty&.nonzero?
|
101
|
+
|
102
|
+
def mdirty?() = vmr_mdirty&.nonzero?
|
103
|
+
end
|
data/lib/channel.rb
CHANGED
@@ -1,25 +1,46 @@
|
|
1
|
-
require_relative 'meta'
|
2
|
-
require_relative 'errors'
|
3
|
-
|
4
|
-
class IChannel
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
1
|
+
require_relative 'meta'
|
2
|
+
require_relative 'errors'
|
3
|
+
|
4
|
+
class IChannel
|
5
|
+
'
|
6
|
+
Base Channel class
|
7
|
+
'
|
8
|
+
include Channel_Meta_Functions
|
9
|
+
|
10
|
+
def initialize(remote, i)
|
11
|
+
@remote = remote
|
12
|
+
@index = i
|
13
|
+
end
|
14
|
+
|
15
|
+
def getter(param, is_string = false)
|
16
|
+
@remote.get_parameter("#{self.cmd}.#{param}", is_string)
|
17
|
+
end
|
18
|
+
|
19
|
+
def setter(param, value)
|
20
|
+
@remote.set_parameter("#{self.cmd}.#{param}", value)
|
21
|
+
end
|
22
|
+
|
23
|
+
def cmd
|
24
|
+
"#{self.identifier}[#{@index}]"
|
25
|
+
end
|
26
|
+
|
27
|
+
def identifier
|
28
|
+
raise 'Called abstract mehod: identifier'
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_multi(param_hash)
|
32
|
+
param_hash.each { |(key, val)| self.send("#{key}=", val) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Fades
|
37
|
+
def fadeto(target, time)
|
38
|
+
self.setter('FadeTo', "(#{target}, #{time})")
|
39
|
+
sleep(@remote.delay)
|
40
|
+
end
|
41
|
+
|
42
|
+
def fadeby(change, time)
|
43
|
+
self.setter('FadeBy', "(#{change}, #{time})")
|
44
|
+
sleep(@remote.delay)
|
45
|
+
end
|
46
|
+
end
|