Class: Voicemeeter::Base

Inherits:
Object
  • Object
show all
Includes:
Events::Director, Logging, Util::Cache, Util::Timeout, Worker
Defined in:
lib/voicemeeter/base.rb

Overview

Base class for Remote

Direct Known Subclasses

Remote::Remote

Constant Summary collapse

RATELIMIT =
0.033
DELAY =
0.001
LOGIN_TIMEOUT =
2

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Events::Director

#deregister, #fire, #observers, #on, #register

Methods included from Worker

#end_event_threads, #init_event_threads, #running?

Methods included from Logging

#logger

Constructor Details

#initialize(kind, **kwargs) ⇒ Base

Returns a new instance of Base.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/voicemeeter/base.rb', line 16

def initialize(kind, **kwargs)
  @kind = kind
  @sync = kwargs[:sync] || false
  @ratelimit = kwargs[:ratelimit] || RATELIMIT
  @delay = kwargs[:delay] || DELAY
  @login_timeout = kwargs[:login_timeout] || LOGIN_TIMEOUT
  @bits = kwargs[:bits] || 64
  if ![32, 64].include? @bits
    logger.warn "kwarg bits got #{@bits}, expected either 32 or 64, defaulting to 64"
    @bits = 64
  end
  @event =
    Events::Tracker.new(
      **(kwargs.select { |k, _| %i[pdirty mdirty ldirty midi].include? k })
    )
  @midi = Midi.new
  @cache = {strip_mode: 0}
end

Instance Attribute Details

#cacheObject (readonly)

Returns the value of attribute cache.



10
11
12
# File 'lib/voicemeeter/base.rb', line 10

def cache
  @cache
end

#delayObject (readonly)

Returns the value of attribute delay.



10
11
12
# File 'lib/voicemeeter/base.rb', line 10

def delay
  @delay
end

#eventObject (readonly)

Returns the value of attribute event.



10
11
12
# File 'lib/voicemeeter/base.rb', line 10

def event
  @event
end

#kindObject (readonly)

Returns the value of attribute kind.



10
11
12
# File 'lib/voicemeeter/base.rb', line 10

def kind
  @kind
end

#midiObject (readonly)

Returns the value of attribute midi.



10
11
12
# File 'lib/voicemeeter/base.rb', line 10

def midi
  @midi
end

Instance Method Details

#apply(data) ⇒ Object



214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/voicemeeter/base.rb', line 214

def apply(data)
  data.each do |key, hash|
    case key.to_s.split("-")
    in [/strip|bus|button/ => kls, /^[0-9]+$/ => index]
      target = public_send(kls)
    in ["vban", /in|instream|out|oustream/ => dir, /^[0-9]+$/ => index]
      target = vban.public_send("#{dir.chomp("stream")}stream")
    else
      raise KeyError, "invalid config key '#{key}'"
    end
    target[index.to_i].apply(hash)
  end
end

#apply_config(name) ⇒ Object



228
229
230
231
# File 'lib/voicemeeter/base.rb', line 228

def apply_config(name)
  apply(configs[name])
  logger.info "profile #{name} applied!"
end

#clear_dirtyObject



65
66
67
68
69
# File 'lib/voicemeeter/base.rb', line 65

def clear_dirty
  catch(:clear) do
    loop { throw(:clear) unless pdirty? || mdirty? }
  end
end

#get(name, is_string = false) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
# File 'lib/voicemeeter/base.rb', line 104

def get(name, is_string = false)
  if is_string
    cget = FFI::MemoryPointer.new(:string, 512, true)
    CBindings.call(:bind_get_parameter_string_a, name, cget)
    cget.read_string
  else
    cget = FFI::MemoryPointer.new(:float, 1)
    CBindings.call(:bind_get_parameter_float, name, cget)
    cget.read_float.round(1)
  end
end

#get_buttonstatus(id, mode) ⇒ Object



125
126
127
128
129
# File 'lib/voicemeeter/base.rb', line 125

def get_buttonstatus(id, mode)
  cget = FFI::MemoryPointer.new(:float, 1)
  CBindings.call(:bind_macro_button_get_status, id, cget, mode)
  cget.read_float.to_i
end

#get_device_description(index, dir) ⇒ Object



161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/voicemeeter/base.rb', line 161

def get_device_description(index, dir)
  unless %i[in out].include? dir
    raise Errors::VMError.new "dir got: #{dir}, expected :in or :out"
  end
  ctype = FFI::MemoryPointer.new(:long, 1)
  cname = FFI::MemoryPointer.new(:string, 256, true)
  chwid = FFI::MemoryPointer.new(:string, 256, true)
  if dir == :in
    CBindings.call(
      :bind_input_get_device_desc_a,
      index,
      ctype,
      cname,
      chwid
    )
  else
    CBindings.call(
      :bind_output_get_device_desc_a,
      index,
      ctype,
      cname,
      chwid
    )
  end
  [cname.read_string, ctype.read_long, chwid.read_string]
end

#get_level(mode, index) ⇒ Object



136
137
138
139
140
# File 'lib/voicemeeter/base.rb', line 136

def get_level(mode, index)
  cget = FFI::MemoryPointer.new(:float, 1)
  CBindings.call(:bind_get_level, mode, index, cget)
  cget.read_float
end

#get_midi_messageObject



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/voicemeeter/base.rb', line 188

def get_midi_message
  cmsg = FFI::MemoryPointer.new(:string, 1024, true)
  res =
    CBindings.call(
      :bind_get_midi_message,
      cmsg,
      1024,
      ok: [-5, -6],
      exp: ->(x) { x >= 0 }
    )
  if (got_midi = res > 0)
    data = cmsg.read_bytes(res).bytes
    data.each_slice(3) do |ch, key, velocity|
      midi.channel = ch
      midi.current = key
      midi.cache[key] = velocity
    end
  end
  got_midi
end

#get_num_devices(dir) ⇒ Object



150
151
152
153
154
155
156
157
158
159
# File 'lib/voicemeeter/base.rb', line 150

def get_num_devices(dir)
  unless %i[in out].include? dir
    raise Errors::VMError.new "dir got: #{dir}, expected :in or :out"
  end
  if dir == :in
    CBindings.call(:bind_input_get_device_number, exp: ->(x) { x >= 0 })
  else
    CBindings.call(:bind_output_get_device_number, exp: ->(x) { x >= 0 })
  end
end

#ldirty?Boolean

Returns:

  • (Boolean)


57
58
59
60
61
62
63
# File 'lib/voicemeeter/base.rb', line 57

def ldirty?
  cache[:strip_buf], cache[:bus_buf] = _get_levels
  !(
    cache[:strip_level] == cache[:strip_buf] &&
      cache[:bus_level] == cache[:bus_buf]
  )
end

#loginObject



39
40
41
# File 'lib/voicemeeter/base.rb', line 39

def 
  CBindings.call(:bind_login, ok: [0, 1]) == 1 and run_voicemeeter(kind.name)
end

#logoutObject



43
44
45
46
47
# File 'lib/voicemeeter/base.rb', line 43

def logout
  sleep(0.1)
  CBindings.call(:bind_logout)
  logger.info "Successfully logged out of #{self}"
end

#mdirty?Boolean

Returns:

  • (Boolean)


53
54
55
# File 'lib/voicemeeter/base.rb', line 53

def mdirty?
  CBindings.call(:bind_macro_button_is_dirty, ok: [0, 1]) == 1
end

#pdirty?Boolean

Returns:

  • (Boolean)


49
50
51
# File 'lib/voicemeeter/base.rb', line 49

def pdirty?
  CBindings.call(:bind_is_parameters_dirty, ok: [0, 1]) == 1
end

#run_voicemeeter(kind_id) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/voicemeeter/base.rb', line 71

def run_voicemeeter(kind_id)
  kinds = {
    basic: ([Install::OS_BITS, @bits].all? { |v| v == 64 }) ?
      Kinds::KindEnum::BASICX64 : Kinds::KindEnum::BASIC,
    banana: ([Install::OS_BITS, @bits].all? { |v| v == 64 }) ?
      Kinds::KindEnum::BANANAX64 : Kinds::KindEnum::BANANA,
    potato: ([Install::OS_BITS, @bits].all? { |v| v == 64 }) ?
      Kinds::KindEnum::POTATOX64 : Kinds::KindEnum::POTATO
  }
  if caller(1..1).first[/`(.*)'/, 1] == "login"
    logger.debug "Voicemeeter engine running but the GUI appears to be down... launching."
  end
  CBindings.call(:bind_run_voicemeeter, kinds[kind_id])
end

#sendtext(script) ⇒ Object

Raises:

  • (ArgumentError)


209
210
211
212
# File 'lib/voicemeeter/base.rb', line 209

def sendtext(script)
  raise ArgumentError, "script must not exceed 48kB" if script.length > 48000
  CBindings.call(:bind_set_parameters, script)
end

#set(name, value) ⇒ Object



116
117
118
119
120
121
122
123
# File 'lib/voicemeeter/base.rb', line 116

def set(name, value)
  if value.is_a? String
    CBindings.call(:bind_set_parameter_string_a, name, value)
  else
    CBindings.call(:bind_set_parameter_float, name, value.to_f)
  end
  cache.store(name, value)
end

#set_buttonstatus(id, mode, state) ⇒ Object



131
132
133
134
# File 'lib/voicemeeter/base.rb', line 131

def set_buttonstatus(id, mode, state)
  CBindings.call(:bind_macro_button_set_status, id, state, mode)
  cache.store("mb_#{id}_#{mode}", state)
end

#to_sObject



35
36
37
# File 'lib/voicemeeter/base.rb', line 35

def to_s
  "Voicemeeter #{kind}"
end

#typeObject



86
87
88
89
90
91
# File 'lib/voicemeeter/base.rb', line 86

def type
  ckind = FFI::MemoryPointer.new(:long, 1)
  CBindings.call(:bind_get_voicemeeter_type, ckind)
  kinds = [nil, :basic, :banana, :potato]
  kinds[ckind.read_long]
end

#versionObject



93
94
95
96
97
98
99
100
101
102
# File 'lib/voicemeeter/base.rb', line 93

def version
  cver = FFI::MemoryPointer.new(:long, 1)
  CBindings.call(:bind_get_voicemeeter_version, cver)
  [
    (cver.read_long & 0xFF000000) >> 24,
    (cver.read_long & 0x00FF0000) >> 16,
    (cver.read_long & 0x0000FF00) >> 8,
    cver.read_long & 0x000000FF
  ].join(".")
end