voicemeeter 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,82 @@
1
+ module Voicemeeter
2
+ module Events
3
+ module Director
4
+ def observers
5
+ @observers ||= {}
6
+ end
7
+
8
+ def on(event, method = nil, &block)
9
+ (observers[event] ||= []) << (block || method)
10
+ end
11
+
12
+ def register(cbs)
13
+ cbs = Array(cbs) unless cbs.respond_to? :each
14
+ cbs.each { |cb| on(cb.name[3..].to_sym, cb) }
15
+ end
16
+
17
+ def deregister(cbs)
18
+ cbs = Array(cbs) unless cbs.respond_to? :each
19
+ cbs.each { |cb| observers[cb.name[3..].to_sym]&.reject! { |o| cbs.include? o } }
20
+ end
21
+
22
+ def fire(event)
23
+ observers[event]&.each { |block| block.call }
24
+ end
25
+ end
26
+
27
+ class Tracker
28
+ include Logging
29
+
30
+ attr_reader :pdirty, :mdirty, :midi, :ldirty
31
+
32
+ def initialize(**kwargs)
33
+ make_writer_methods :pdirty, :mdirty, :midi, :ldirty
34
+
35
+ kwargs.each do |key, value|
36
+ instance_variable_set("@#{key}", value || false)
37
+ end
38
+ end
39
+
40
+ def to_s
41
+ self.class.name.split("::").last.to_s
42
+ end
43
+
44
+ def info(msg = nil)
45
+ info_msg = msg ? ["#{msg} events."] : []
46
+ info_msg << if any?
47
+ ["Now listening for #{get.join(", ")} events"]
48
+ else
49
+ ["Not listening for any events"]
50
+ end
51
+ logger.info info_msg.join(" ")
52
+ end
53
+
54
+ private def make_writer_methods(*params)
55
+ params.each do |param|
56
+ define_singleton_method("#{param}=") do |value|
57
+ instance_variable_set("@#{param}", value)
58
+ info("#{param} #{value ? "added to" : "removed from"}")
59
+ end
60
+ end
61
+ end
62
+
63
+ def get
64
+ %i[pdirty mdirty midi ldirty].reject { |ev| !send(ev) }
65
+ end
66
+
67
+ def any?
68
+ [pdirty, mdirty, midi, ldirty].any?
69
+ end
70
+
71
+ def add(events)
72
+ events = [events] unless events.respond_to? :each
73
+ events.each { |e| send("#{e}=", true) }
74
+ end
75
+
76
+ def remove(events)
77
+ events = [events] unless events.respond_to? :each
78
+ events.each { |e| send("#{e}=", false) }
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,42 @@
1
+ module Voicemeeter
2
+ class Fx
3
+ include IRemote
4
+ attr_reader :reverb, :delay
5
+
6
+ def initialize(remote)
7
+ super
8
+ @reverb = FxReverb.new(remote)
9
+ @delay = FxDelay.new(remote)
10
+ end
11
+
12
+ def identifier
13
+ :fx
14
+ end
15
+ end
16
+
17
+ class FxReverb
18
+ include IRemote
19
+
20
+ def initialize(remote)
21
+ super
22
+ make_accessor_bool :on, :ab
23
+ end
24
+
25
+ def identifier
26
+ "fx.reverb"
27
+ end
28
+ end
29
+
30
+ class FxDelay
31
+ include IRemote
32
+
33
+ def initialize(remote)
34
+ super
35
+ make_accessor_bool :on, :ab
36
+ end
37
+
38
+ def identifier
39
+ "fx.delay"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,38 @@
1
+ module Voicemeeter
2
+ private
3
+
4
+ module Install
5
+ extend Logging
6
+
7
+ OS_BITS = (FFI::Platform::CPU.downcase == "x64") ? 64 : 32
8
+
9
+ def get_vmpath
10
+ reg_key = [
11
+ :Software,
12
+ ((OS_BITS == 64) ? :WOW6432Node : nil),
13
+ :Microsoft,
14
+ :Windows,
15
+ :CurrentVersion,
16
+ :Uninstall,
17
+ :"VB:Voicemeeter {17359A74-1236-5467}"
18
+ ]
19
+
20
+ Win32::Registry::HKEY_LOCAL_MACHINE.open(
21
+ reg_key.compact.join("\\")
22
+ ) do |reg|
23
+ value = reg["UninstallString"]
24
+
25
+ Pathname.new(value).dirname
26
+ end
27
+ rescue Win32::Registry::Error => e
28
+ err_msg = [
29
+ "#{e.class.name}: #{e.message}",
30
+ *e.backtrace
31
+ ]
32
+ logger.error err_msg.join("\n")
33
+ raise Errors::VMInstallError.new "unable to read Voicemeeter path from the registry"
34
+ end
35
+
36
+ module_function :get_vmpath
37
+ end
38
+ end
@@ -0,0 +1,51 @@
1
+ module Voicemeeter
2
+ # Common interface with the base Remote class.
3
+ module IRemote
4
+ include Logging
5
+ include MetaFunctions
6
+
7
+ def initialize(remote, i = nil)
8
+ @remote = remote
9
+ @index = i
10
+ end
11
+
12
+ def to_s
13
+ "#{self.class.name.split("::").last}#{@index}#{@remote.kind}"
14
+ end
15
+
16
+ private
17
+
18
+ def getter(param, is_string = false)
19
+ logger.debug "getter: #{_cmd(param)}"
20
+ @remote.get(_cmd(param), is_string)
21
+ end
22
+
23
+ def setter(param, value)
24
+ logger.debug "setter: #{_cmd(param)}=#{value}"
25
+ @remote.set(_cmd(param), value)
26
+ end
27
+
28
+ def _cmd(param)
29
+ param.empty? ? identifier : "#{identifier}.#{param}"
30
+ end
31
+
32
+ def identifier
33
+ raise "Called abstract method: identifier"
34
+ end
35
+
36
+ public
37
+
38
+ def apply(params)
39
+ params.each do |key, val|
40
+ if val.is_a? Hash
41
+ target = send(key)
42
+ target.apply(val)
43
+ elsif key == :mode
44
+ mode.send("#{val}=", true)
45
+ else
46
+ send("#{key}=", val)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,51 @@
1
+ module Voicemeeter
2
+ module Kinds
3
+ private
4
+
5
+ module KindEnum
6
+ BASIC = 1
7
+ BANANA = 2
8
+ POTATO = 3
9
+ POTATOX64 = 6
10
+ end
11
+
12
+ KindMap =
13
+ Data.define(:name, :ins, :outs, :vban, :asio, :insert, :num_buttons) do
14
+ def phys_in = ins.first
15
+
16
+ def virt_in = ins.last
17
+
18
+ def phys_out = outs.first
19
+
20
+ def virt_out = outs.last
21
+
22
+ def num_strip = ins.sum
23
+
24
+ def num_bus = outs.sum
25
+
26
+ def num_strip_levels = 2 * phys_in + 8 * virt_in
27
+
28
+ def num_bus_levels = 8 * (phys_out + virt_out)
29
+
30
+ def to_s = name.to_s.capitalize
31
+ end
32
+
33
+ basic = KindMap.new(:basic, [2, 1], [1, 1], [4, 4, 1, 1], [0, 0], 0, 80)
34
+
35
+ banana = KindMap.new(:banana, [3, 2], [3, 2], [8, 8, 1, 1], [6, 8], 22, 80)
36
+
37
+ potato = KindMap.new(:potato, [5, 3], [5, 3], [8, 8, 1, 1], [10, 8], 34, 80)
38
+
39
+ KIND_MAPS = [basic, banana, potato].to_h { |kind| [kind.name, kind] }
40
+
41
+ public
42
+
43
+ def get(kind_id)
44
+ KIND_MAPS.fetch(kind_id)
45
+ end
46
+
47
+ ALL = KIND_MAPS.values
48
+
49
+ module_function :get
50
+ end
51
+ end
@@ -0,0 +1,9 @@
1
+ module Voicemeeter
2
+ module Logging
3
+ def logger
4
+ @logger ||= Logger.new($stdout, level: ENV.fetch("VM_LOG_LEVEL", "WARN"))
5
+ @logger.progname = instance_of?(::Module) ? name : self.class.name
6
+ @logger
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,75 @@
1
+ module Voicemeeter
2
+ module MetaFunctions
3
+ private
4
+
5
+ # Accessor methods
6
+ def make_accessor_bool(*params)
7
+ params.each do |param|
8
+ define_singleton_method(param) { getter(param).to_i == 1 }
9
+
10
+ define_singleton_method("#{param}=") do |value|
11
+ setter(param, value && 1 || 0)
12
+ end
13
+ end
14
+ end
15
+
16
+ def make_accessor_string(*params)
17
+ params.each do |param|
18
+ define_singleton_method(param) { getter(param, true) }
19
+
20
+ define_singleton_method("#{param}=") { |value| setter(param, value) }
21
+ end
22
+ end
23
+
24
+ def make_accessor_int(*params)
25
+ params.each do |param|
26
+ define_singleton_method(param) { getter(param).to_i }
27
+
28
+ define_singleton_method("#{param}=") { |value| setter(param, value) }
29
+ end
30
+ end
31
+
32
+ def make_accessor_float(*params)
33
+ params.each do |param|
34
+ define_singleton_method(param) { getter(param) }
35
+
36
+ define_singleton_method("#{param}=") { |value| setter(param, value) }
37
+ end
38
+ end
39
+
40
+ # reader methods
41
+ def make_reader_string(*params)
42
+ params.each do |param|
43
+ define_singleton_method(param) { getter(param, true) }
44
+ end
45
+ end
46
+
47
+ def make_reader_int(*params)
48
+ params.each do |param|
49
+ define_singleton_method(param) { getter(param).to_i }
50
+ end
51
+ end
52
+
53
+ # writer methods
54
+ def make_writer_bool(*params)
55
+ params.each do |param|
56
+ define_singleton_method("#{param}=") do |value|
57
+ setter(param, value && 1 || 0)
58
+ end
59
+ end
60
+ end
61
+
62
+ def make_writer_string(*params)
63
+ params.each do |param|
64
+ define_singleton_method("#{param}=") { |value| setter(param, value) }
65
+ end
66
+ end
67
+
68
+ # methods for performing certain actions as opposed to setting values
69
+ def make_action_method(*params)
70
+ params.each do |param|
71
+ define_singleton_method(param) { setter(param, 1) }
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,14 @@
1
+ module Voicemeeter
2
+ class Midi
3
+ attr_accessor :current, :channel
4
+ attr_reader :cache
5
+
6
+ def initialize
7
+ @cache = {}
8
+ end
9
+
10
+ def get(key)
11
+ cache[key]
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,83 @@
1
+ module Voicemeeter
2
+ module Mixins
3
+ module Fades
4
+ def fadeto(target, time)
5
+ setter("FadeTo", "(#{target}, #{time})")
6
+ sleep(@remote.delay)
7
+ end
8
+
9
+ def fadeby(change, time)
10
+ setter("FadeBy", "(#{change}, #{time})")
11
+ sleep(@remote.delay)
12
+ end
13
+ end
14
+
15
+ module Return
16
+ def initialize(remote, i)
17
+ super
18
+ make_accessor_float :returnreverb, :returndelay, :returnfx1, :returnfx2
19
+ end
20
+ end
21
+
22
+ module Apps
23
+ def appgain(name, gain)
24
+ setter("AppGain", "(\"#{name}\", #{gain})")
25
+ end
26
+
27
+ def appmute(name, mute)
28
+ setter("AppMute", "(\"#{name}\", #{mute ? 1 : 0})")
29
+ end
30
+ end
31
+
32
+ module Outputs
33
+ def initialize(*args)
34
+ super
35
+ remote, *_ = args
36
+ num_a, num_b = remote.kind.outs
37
+ channels =
38
+ (1..(num_a + num_b)).map do |i|
39
+ (i <= num_a) ? "A#{i}" : "B#{i - num_a}"
40
+ end
41
+ make_accessor_bool(*channels)
42
+ end
43
+ end
44
+
45
+ module Xy
46
+ module Pan
47
+ def initialize(remote, i)
48
+ super
49
+ make_accessor_float :pan_x, :pan_y
50
+ end
51
+ end
52
+
53
+ module Color
54
+ def initialize(remote, i)
55
+ super
56
+ make_accessor_float :color_x, :color_y
57
+ end
58
+ end
59
+
60
+ module Fx
61
+ def initialize(remote, i)
62
+ super
63
+ make_accessor_float :fx_x, :fx_y
64
+ end
65
+ end
66
+ end
67
+
68
+ module Fx
69
+ def initialize(remote, i)
70
+ super
71
+ make_accessor_float :reverb, :delay, :fx1, :fx2
72
+ make_accessor_bool :postreverb, :postdelay, :postfx1, :postfx2
73
+ end
74
+ end
75
+
76
+ module LevelEnum
77
+ PREFADER = 0
78
+ POSTFADER = 1
79
+ POSTMUTE = 2
80
+ BUS = 3
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,69 @@
1
+ module Voicemeeter
2
+ module Option
3
+ class Base
4
+ include IRemote
5
+ attr_reader :delay, :buffer, :mode
6
+
7
+ def initialize(remote)
8
+ super
9
+ make_accessor_int :sr
10
+ make_accessor_bool :asiosr, :monitoronsel, :slidermode
11
+
12
+ @delay = (0...remote.kind.phys_out).map { OptionDelay.new(remote, _1) }
13
+ @buffer = OptionBuffer.new(remote)
14
+ @mode = OptionMode.new(remote)
15
+ end
16
+
17
+ def identifier
18
+ :option
19
+ end
20
+ end
21
+
22
+ class OptionDelay
23
+ include IRemote
24
+
25
+ def initialize(remote, i)
26
+ super
27
+ make_accessor_bool :on, :ab
28
+ end
29
+
30
+ def identifier
31
+ "option.delay"
32
+ end
33
+
34
+ def get
35
+ getter("[#{@index}]").to_i
36
+ end
37
+
38
+ def set(val)
39
+ setter("[#{@index}]", val)
40
+ end
41
+ end
42
+
43
+ class OptionBuffer
44
+ include IRemote
45
+
46
+ def initialize(remote)
47
+ super
48
+ make_accessor_int :mme, :wdm, :ks, :asio
49
+ end
50
+
51
+ def identifier
52
+ "option.buffer"
53
+ end
54
+ end
55
+
56
+ class OptionMode
57
+ include IRemote
58
+
59
+ def initialize(remote)
60
+ super
61
+ make_accessor_bool :exclusif, :swift
62
+ end
63
+
64
+ def identifier
65
+ "option.mode"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,79 @@
1
+ module Voicemeeter
2
+ module Patch
3
+ # Base class for Patch
4
+ class Base
5
+ include IRemote
6
+ attr_reader :asio, :A2, :A3, :A4, :A5, :composite, :insert
7
+
8
+ def initialize(remote)
9
+ super
10
+ make_accessor_bool :postfadercomposite, :postfxinsert
11
+
12
+ asio_in, asio_out = remote.kind.asio
13
+ @asio = (0...asio_in).map { PatchAsioIn.new(remote, _1) }
14
+ %i[A2 A3 A4 A5].each do |param|
15
+ instance_variable_set("@#{param}", (0...asio_out).map { PatchAsioOut.new(remote, _1, param) })
16
+ end
17
+ @composite = (0...8).map { PatchComposite.new(remote, _1) }
18
+ @insert = (0...remote.kind.insert).map { PatchInsert.new(remote, _1) }
19
+ end
20
+ end
21
+
22
+ class PatchAsio
23
+ include IRemote
24
+
25
+ def identifier
26
+ :patch
27
+ end
28
+ end
29
+
30
+ class PatchAsioIn < PatchAsio
31
+ def get
32
+ getter("asio[#{@index}]").to_i
33
+ end
34
+
35
+ def set(val)
36
+ setter("asio[#{@index}]", val)
37
+ end
38
+ end
39
+
40
+ class PatchAsioOut < PatchAsio
41
+ def initialize(remote, i, param)
42
+ super(remote, i)
43
+ @param = param
44
+ end
45
+
46
+ def get
47
+ getter("out#{@param}[#{@index}]").to_i
48
+ end
49
+
50
+ def set(val)
51
+ setter("out#{@param}[#{@index}]", val)
52
+ end
53
+ end
54
+
55
+ class PatchComposite
56
+ include IRemote
57
+
58
+ def get
59
+ getter("composite[#{@index}]").to_i
60
+ end
61
+
62
+ def set(val)
63
+ setter("composite[#{@index}]", val)
64
+ end
65
+ end
66
+
67
+ class PatchInsert
68
+ include IRemote
69
+
70
+ def get
71
+ getter("insert[#{@index}]").to_i == 1
72
+ end
73
+
74
+ def set(val)
75
+ setter("insert[#{@index}]", val && 1 || 0)
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,90 @@
1
+ module Voicemeeter
2
+ module Recorder
3
+ module FileTypeEnum
4
+ WAV = 1
5
+ AIFF = 2
6
+ BWF = 3
7
+ MP3 = 100
8
+ end
9
+
10
+ # Base class for Recorder
11
+ class Base
12
+ include IRemote
13
+ include Mixins::Outputs
14
+
15
+ attr_reader :mode, :armstrip, :armbus
16
+
17
+ def initialize(remote)
18
+ super
19
+ make_action_method :play, :stop, :pause, :replay, :record, :ff, :rew
20
+ make_accessor_int :bitresolution, :channel, :kbps
21
+ make_accessor_float :gain
22
+
23
+ @mode = RecorderMode.new(remote)
24
+ @armstrip = (0...remote.kind.num_strip).map { RecorderArmStrip.new(remote, _1) }
25
+ @armbus = (0...remote.kind.num_bus).map { RecorderArmBus.new(remote, _1) }
26
+ end
27
+
28
+ def identifier
29
+ :recorder
30
+ end
31
+
32
+ def load(filepath)
33
+ setter("load", filepath)
34
+ end
35
+
36
+ def goto(timestr)
37
+ unless /^(2[0-3]|[01]?[0-9]):([0-5]?[0-9]):([0-5]?[0-9])$/.match?(timestr)
38
+ logger.error "goto got: '#{timestr}', but expects a time string in the format 'hh:mm:ss'"
39
+ return
40
+ end
41
+ dt = DateTime.parse(timestr)
42
+ seconds = dt.hour * 3600 + dt.min * 60 + dt.second
43
+ setter("goto", seconds)
44
+ end
45
+
46
+ def filetype(val)
47
+ opts = {wav: FileTypeEnum::WAV, aiff: FileTypeEnum::AIFF, bwf: FileTypeEnum::BWF, mp3: FileTypeEnum::MP3}
48
+ setter("filetype", opts[val])
49
+ end
50
+ end
51
+
52
+ class RecorderMode
53
+ include IRemote
54
+
55
+ def initialize(remote)
56
+ super
57
+ make_accessor_bool :recbus, :playonload, :loop, :multitrack
58
+ end
59
+
60
+ def identifier
61
+ "recorder.mode"
62
+ end
63
+ end
64
+
65
+ class RecorderArmChannel
66
+ include IRemote
67
+
68
+ def initialize(remote, j)
69
+ super(remote)
70
+ @j = j
71
+ end
72
+
73
+ def set
74
+ setter("", val && 1 || 0)
75
+ end
76
+ end
77
+
78
+ class RecorderArmStrip < RecorderArmChannel
79
+ def identifier
80
+ "recorder.armstrip[#{@j}]"
81
+ end
82
+ end
83
+
84
+ class RecorderArmBus < RecorderArmChannel
85
+ def identifier
86
+ "recorder.armbus[#{@j}]"
87
+ end
88
+ end
89
+ end
90
+ end