Class: Deftones::Source::UserMedia
Defined Under Namespace
Classes: DeviceInfo, PortAudioCapture
Instance Attribute Summary collapse
Attributes inherited from Core::Source
#mute, #onstop, #volume
Class Method Summary
collapse
Instance Method Summary
collapse
-
#backend_stat(name) ⇒ Object
private
-
#capture_channels ⇒ Object
private
-
#close(time = nil) ⇒ Object
-
#dispose ⇒ Object
-
#initialize(buffer: nil, provider: nil, loop: false, live: false, capture_backend: nil, device_id: nil, group_id: nil, label: nil, channels: nil, context: Deftones.context) ⇒ UserMedia
constructor
A new instance of UserMedia.
-
#live? ⇒ Boolean
-
#mark_capture_frame_underflow! ⇒ Object
private
-
#mark_provider_exhausted! ⇒ Object
private
-
#mark_underflow! ⇒ Object
private
-
#multichannel_process? ⇒ Boolean
private
-
#next_capture_frame ⇒ Object
private
-
#next_capture_sample ⇒ Object
private
-
#next_enumerator_sample ⇒ Object
private
-
#next_provider_sample ⇒ Object
private
-
#normalize_buffer(buffer) ⇒ Object
private
-
#normalize_capture_backend(capture_backend, live, channels) ⇒ Object
private
-
#normalize_channel_count(channels) ⇒ Object
private
-
#normalize_provider(provider) ⇒ Object
private
-
#open(time = nil, device_id: nil, group_id: nil, label: nil) ⇒ Object
-
#open_capture_backend! ⇒ Object
private
-
#opened? ⇒ Boolean
-
#overflow_count ⇒ Object
(also: #overflowCount)
-
#permission_state ⇒ Object
-
#permissionState ⇒ Object
-
#process(_input_buffer, num_frames, start_frame, _cache) ⇒ Object
-
#provider_exhausted? ⇒ Boolean
(also: #providerExhausted)
-
#render_buffer(num_frames, start_frame) ⇒ Object
private
-
#render_buffer_block(num_frames, start_frame) ⇒ Object
private
-
#render_capture(num_frames, start_frame) ⇒ Object
private
-
#render_capture_block(num_frames, start_frame) ⇒ Object
private
-
#reset_stats ⇒ Object
(also: #resetStats)
-
#rewind ⇒ Object
-
#start(time = nil) ⇒ Object
-
#state(time = context.current_time) ⇒ Object
-
#stop(time = nil) ⇒ Object
-
#sync_capture_metadata! ⇒ Object
private
-
#underflow_count ⇒ Object
(also: #underflowCount)
#active_at?, #apply_volume!, #cancel_stop, #clear_transport_event, #mute?, #notify_stop_in_window, #number_of_inputs, #render, #render_block, #resolve_time, #resolve_transport_time, #restart, #schedule_transport_event, #source_type, #sync, #synced?, #unsync, #uses_legacy_render_for_block?
Constructor Details
#initialize(buffer: nil, provider: nil, loop: false, live: false, capture_backend: nil, device_id: nil, group_id: nil, label: nil, channels: nil, context: Deftones.context) ⇒ UserMedia
Returns a new instance of UserMedia.
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
# File 'lib/deftones/source/user_media.rb', line 10
def initialize(buffer: nil, provider: nil, loop: false, live: false, capture_backend: nil,
device_id: nil, group_id: nil, label: nil, channels: nil, context: Deftones.context)
super(context: context)
@buffer = normalize_buffer(buffer)
@provider = normalize_provider(provider)
@loop = loop
@live_requested = live || !capture_backend.nil?
@capture_backend = normalize_capture_backend(capture_backend, live, channels)
@device_id = device_id
@group_id = group_id
@label = label
@channels = normalize_channel_count(channels)
@sample_cursor = 0
@provider_exhausted = false
@underflow_count = 0
@overflow_count = 0
@opened = false
end
|
Instance Attribute Details
#buffer ⇒ Object
Returns the value of attribute buffer.
8
9
10
|
# File 'lib/deftones/source/user_media.rb', line 8
def buffer
@buffer
end
|
#device_id ⇒ Object
Returns the value of attribute device_id.
8
9
10
|
# File 'lib/deftones/source/user_media.rb', line 8
def device_id
@device_id
end
|
#group_id ⇒ Object
Returns the value of attribute group_id.
8
9
10
|
# File 'lib/deftones/source/user_media.rb', line 8
def group_id
@group_id
end
|
#label ⇒ Object
Returns the value of attribute label.
8
9
10
|
# File 'lib/deftones/source/user_media.rb', line 8
def label
@label
end
|
#provider ⇒ Object
Returns the value of attribute provider.
8
9
10
|
# File 'lib/deftones/source/user_media.rb', line 8
def provider
@provider
end
|
Class Method Details
.build_device_info(device) ⇒ Object
166
167
168
169
170
171
172
173
174
|
# File 'lib/deftones/source/user_media.rb', line 166
def build_device_info(device)
DeviceInfo.new(
device_id: (device),
label: exposed_device_label(device),
group_id: exposed_device_group_id(device),
input_channels: (device, :input),
output_channels: (device, :output)
)
end
|
.enumerate_devices ⇒ Object
Also known as:
enumerateDevices
140
141
142
|
# File 'lib/deftones/source/user_media.rb', line 140
def enumerate_devices
input_devices
end
|
.exposed_device_group_id(device) ⇒ Object
203
204
205
|
# File 'lib/deftones/source/user_media.rb', line 203
def exposed_device_group_id(device)
permissions_granted? ? (device) : nil
end
|
.exposed_device_label(device) ⇒ Object
199
200
201
|
# File 'lib/deftones/source/user_media.rb', line 199
def exposed_device_label(device)
permissions_granted? ? (device) : ""
end
|
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
# File 'lib/deftones/source/user_media.rb', line 207
def (device, direction)
methods =
case direction
when :input then %i[max_input_channels input_channels channels]
when :output then %i[max_output_channels output_channels channels]
else []
end
methods.each do |method_name|
return device.public_send(method_name).to_i if device.respond_to?(method_name)
end
0
end
|
191
192
193
194
195
196
197
|
# File 'lib/deftones/source/user_media.rb', line 191
def (device)
return device.group_id if device.respond_to?(:group_id)
return device.host_api if device.respond_to?(:host_api)
return device.host_api_name if device.respond_to?(:host_api_name)
nil
end
|
176
177
178
179
180
181
182
|
# File 'lib/deftones/source/user_media.rb', line 176
def (device)
return device.device_id if device.respond_to?(:device_id)
return device.index if device.respond_to?(:index)
return device.device_index if device.respond_to?(:device_index)
(device)
end
|
184
185
186
187
188
189
|
# File 'lib/deftones/source/user_media.rb', line 184
def (device)
return device.label if device.respond_to?(:label)
return device.name if device.respond_to?(:name)
device.to_s
end
|
.grant_permissions! ⇒ Object
119
120
121
122
|
# File 'lib/deftones/source/user_media.rb', line 119
def grant_permissions!
@permission_state = :granted
self
end
|
133
134
135
136
137
138
|
# File 'lib/deftones/source/user_media.rb', line 133
def input_devices
portaudio_devices.filter_map do |device|
info = build_device_info(device)
info if info.input_channels.positive?
end
end
|
.permission_state ⇒ Object
Also known as:
permissionState
115
116
117
|
# File 'lib/deftones/source/user_media.rb', line 115
def permission_state
@permission_state ||= :prompt
end
|
.permissions_granted? ⇒ Boolean
150
151
152
|
# File 'lib/deftones/source/user_media.rb', line 150
def permissions_granted?
permission_state == :granted
end
|
.portaudio_devices ⇒ Object
154
155
156
157
158
159
160
161
162
163
164
|
# File 'lib/deftones/source/user_media.rb', line 154
def portaudio_devices
return [] unless Deftones.portaudio_available?
return [] unless defined?(PortAudio::Device)
return Array(PortAudio::Device.all) if PortAudio::Device.respond_to?(:all)
return Array(PortAudio::Device.devices) if PortAudio::Device.respond_to?(:devices)
[]
rescue StandardError
[]
end
|
.reset_permissions! ⇒ Object
124
125
126
127
|
# File 'lib/deftones/source/user_media.rb', line 124
def reset_permissions!
@permission_state = :prompt
self
end
|
.supported? ⇒ Boolean
Also known as:
supported
129
130
131
|
# File 'lib/deftones/source/user_media.rb', line 129
def supported?
true
end
|
Instance Method Details
#backend_stat(name) ⇒ Object
436
437
438
439
440
|
# File 'lib/deftones/source/user_media.rb', line 436
def backend_stat(name)
return 0 unless @capture_backend.respond_to?(name)
@capture_backend.public_send(name).to_i
end
|
#capture_channels ⇒ Object
243
244
245
246
247
248
|
# File 'lib/deftones/source/user_media.rb', line 243
def capture_channels
return @buffer.channels if @buffer
return normalize_channel_count(@capture_backend.channels) if @capture_backend.respond_to?(:channels)
@channels
end
|
#close(time = nil) ⇒ Object
51
52
53
54
55
56
|
# File 'lib/deftones/source/user_media.rb', line 51
def close(time = nil)
stop(time)
@capture_backend&.close if @capture_backend.respond_to?(:close)
@opened = false
self
end
|
#dispose ⇒ Object
109
110
111
112
|
# File 'lib/deftones/source/user_media.rb', line 109
def dispose
close
super
end
|
#live? ⇒ Boolean
66
67
68
|
# File 'lib/deftones/source/user_media.rb', line 66
def live?
@live_requested
end
|
#mark_capture_frame_underflow! ⇒ Object
431
432
433
434
|
# File 'lib/deftones/source/user_media.rb', line 431
def mark_capture_frame_underflow!
@underflow_count += 1
Array.new(capture_channels, 0.0)
end
|
#mark_provider_exhausted! ⇒ Object
420
421
422
423
424
|
# File 'lib/deftones/source/user_media.rb', line 420
def mark_provider_exhausted!
@provider_exhausted = true
@opened = false
mark_underflow!
end
|
#mark_underflow! ⇒ Object
426
427
428
429
|
# File 'lib/deftones/source/user_media.rb', line 426
def mark_underflow!
@underflow_count += 1
0.0
end
|
#multichannel_process? ⇒ Boolean
239
240
241
|
# File 'lib/deftones/source/user_media.rb', line 239
def multichannel_process?
(@buffer && @buffer.channels > 1) || capture_channels > 1
end
|
#next_capture_frame ⇒ Object
369
370
371
372
373
374
375
376
377
378
379
380
381
382
|
# File 'lib/deftones/source/user_media.rb', line 369
def next_capture_frame
return Array.new(capture_channels, 0.0) unless @capture_backend
frame = if @capture_backend.respond_to?(:next_frame)
@capture_backend.next_frame
else
@capture_backend.next_sample
end
return mark_capture_frame_underflow! if frame.nil?
frame = [frame] unless frame.is_a?(Array)
normalized = frame.map(&:to_f)
normalized.fill(0.0, normalized.length...capture_channels)
end
|
#next_capture_sample ⇒ Object
384
385
386
387
388
389
390
391
|
# File 'lib/deftones/source/user_media.rb', line 384
def next_capture_sample
return 0.0 unless @capture_backend
sample = @capture_backend.next_sample
return mark_underflow! if sample.nil?
sample.to_f
end
|
#next_enumerator_sample ⇒ Object
407
408
409
410
411
412
413
414
415
416
417
418
|
# File 'lib/deftones/source/user_media.rb', line 407
def next_enumerator_sample
@provider.next
rescue StopIteration
return mark_provider_exhausted! unless @loop && @provider.respond_to?(:rewind)
begin
@provider.rewind
@provider.next
rescue StopIteration
mark_provider_exhausted!
end
end
|
#next_provider_sample ⇒ Object
393
394
395
396
397
398
399
400
401
402
403
404
405
|
# File 'lib/deftones/source/user_media.rb', line 393
def next_provider_sample
return 0.0 unless @provider
sample = if @provider.respond_to?(:call)
@provider.call(@sample_cursor)
else
next_enumerator_sample
end
@sample_cursor += 1
return mark_underflow! if sample.nil?
sample.to_f
end
|
#normalize_buffer(buffer) ⇒ Object
250
251
252
253
254
|
# File 'lib/deftones/source/user_media.rb', line 250
def normalize_buffer(buffer)
return if buffer.nil?
buffer.is_a?(IO::Buffer) ? buffer : IO::Buffer.load(buffer)
end
|
#normalize_capture_backend(capture_backend, live, channels) ⇒ Object
263
264
265
266
267
268
269
270
271
272
|
# File 'lib/deftones/source/user_media.rb', line 263
def normalize_capture_backend(capture_backend, live, channels)
return capture_backend if capture_backend
return unless live && Deftones.portaudio_available?
PortAudioCapture.new(
sample_rate: context.sample_rate,
buffer_size: context.buffer_size,
channels: normalize_channel_count(channels || context.channels)
)
end
|
#normalize_channel_count(channels) ⇒ Object
274
275
276
|
# File 'lib/deftones/source/user_media.rb', line 274
def normalize_channel_count(channels)
[channels.to_i, 1].max
end
|
#normalize_provider(provider) ⇒ Object
256
257
258
259
260
261
|
# File 'lib/deftones/source/user_media.rb', line 256
def normalize_provider(provider)
return if provider.nil?
return provider if provider.respond_to?(:call)
provider.to_enum
end
|
#open(time = nil, device_id: nil, group_id: nil, label: nil) ⇒ Object
41
42
43
44
45
46
47
48
49
|
# File 'lib/deftones/source/user_media.rb', line 41
def open(time = nil, device_id: nil, group_id: nil, label: nil)
@device_id = device_id unless device_id.nil?
@group_id = group_id unless group_id.nil?
@label = label unless label.nil?
open_capture_backend!
sync_capture_metadata!
self.class.grant_permissions!
start(time)
end
|
#open_capture_backend! ⇒ Object
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
|
# File 'lib/deftones/source/user_media.rb', line 278
def open_capture_backend!
return self unless live?
raise Deftones::MissingRealtimeBackendError, "UserMedia live capture backend is unavailable" unless @capture_backend
if @capture_backend.respond_to?(:open)
@capture_backend.open(
device_id: @device_id,
group_id: @group_id,
label: @label,
channels: capture_channels
)
end
self
end
|
#opened? ⇒ Boolean
70
71
72
|
# File 'lib/deftones/source/user_media.rb', line 70
def opened?
@opened
end
|
#overflow_count ⇒ Object
Also known as:
overflowCount
82
83
84
|
# File 'lib/deftones/source/user_media.rb', line 82
def overflow_count
@overflow_count + backend_stat(:overflow_count)
end
|
#permission_state ⇒ Object
93
94
95
|
# File 'lib/deftones/source/user_media.rb', line 93
def permission_state
self.class.permission_state
end
|
#permissionState ⇒ Object
97
98
99
|
# File 'lib/deftones/source/user_media.rb', line 97
def permission_state
self.class.permission_state
end
|
#process(_input_buffer, num_frames, start_frame, _cache) ⇒ Object
223
224
225
226
227
228
229
230
231
232
233
234
235
|
# File 'lib/deftones/source/user_media.rb', line 223
def process(_input_buffer, num_frames, start_frame, _cache)
return render_buffer_block(num_frames, start_frame) if @buffer && @buffer.channels > 1
return render_capture_block(num_frames, start_frame) if @capture_backend && capture_channels > 1
return render_buffer(num_frames, start_frame) if @buffer
return render_capture(num_frames, start_frame) if @capture_backend
Array.new(num_frames) do |index|
current_time = (start_frame + index).to_f / context.sample_rate
next 0.0 unless active_at?(current_time)
next_provider_sample
end
end
|
#provider_exhausted? ⇒ Boolean
Also known as:
providerExhausted
74
75
76
|
# File 'lib/deftones/source/user_media.rb', line 74
def provider_exhausted?
@provider_exhausted
end
|
#render_buffer(num_frames, start_frame) ⇒ Object
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
|
# File 'lib/deftones/source/user_media.rb', line 304
def render_buffer(num_frames, start_frame)
Array.new(num_frames) do |index|
current_time = (start_frame + index).to_f / context.sample_rate
next 0.0 unless active_at?(current_time)
sample_position = (current_time - @start_time) * @buffer.sample_rate
if @loop && @buffer.frames.positive?
sample_position %= @buffer.frames
elsif sample_position >= @buffer.frames
@opened = false
next 0.0
end
@buffer.sample_at(sample_position)
end
end
|
#render_buffer_block(num_frames, start_frame) ⇒ Object
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
|
# File 'lib/deftones/source/user_media.rb', line 321
def render_buffer_block(num_frames, start_frame)
output = Array.new(@buffer.channels) { Array.new(num_frames, 0.0) }
num_frames.times do |index|
current_time = (start_frame + index).to_f / context.sample_rate
next unless active_at?(current_time)
sample_position = (current_time - @start_time) * @buffer.sample_rate
if @loop && @buffer.frames.positive?
sample_position %= @buffer.frames
elsif sample_position >= @buffer.frames
@opened = false
next
end
@buffer.channels.times do |channel_index|
output[channel_index][index] = @buffer.sample_at(sample_position, channel_index)
end
end
Core::AudioBlock.from_channel_data(output)
end
|
#render_capture(num_frames, start_frame) ⇒ Object
344
345
346
347
348
349
350
351
|
# File 'lib/deftones/source/user_media.rb', line 344
def render_capture(num_frames, start_frame)
Array.new(num_frames) do |index|
current_time = (start_frame + index).to_f / context.sample_rate
next 0.0 unless active_at?(current_time)
next_capture_sample
end
end
|
#render_capture_block(num_frames, start_frame) ⇒ Object
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
|
# File 'lib/deftones/source/user_media.rb', line 353
def render_capture_block(num_frames, start_frame)
output = Array.new(capture_channels) { Array.new(num_frames, 0.0) }
num_frames.times do |index|
current_time = (start_frame + index).to_f / context.sample_rate
next unless active_at?(current_time)
frame = next_capture_frame
capture_channels.times do |channel_index|
output[channel_index][index] = frame[channel_index] || 0.0
end
end
Core::AudioBlock.from_channel_data(output)
end
|
#reset_stats ⇒ Object
Also known as:
resetStats
86
87
88
89
90
91
|
# File 'lib/deftones/source/user_media.rb', line 86
def reset_stats
@underflow_count = 0
@overflow_count = 0
@capture_backend.reset_stats if @capture_backend.respond_to?(:reset_stats)
self
end
|
#rewind ⇒ Object
58
59
60
61
62
63
64
|
# File 'lib/deftones/source/user_media.rb', line 58
def rewind
@sample_cursor = 0
@provider_exhausted = false
@provider.rewind if @provider.respond_to?(:rewind)
@capture_backend&.rewind if @capture_backend.respond_to?(:rewind)
self
end
|
#start(time = nil) ⇒ Object
29
30
31
32
33
34
|
# File 'lib/deftones/source/user_media.rb', line 29
def start(time = nil)
rewind
@capture_backend&.start
@opened = true
super
end
|
#state(time = context.current_time) ⇒ Object
103
104
105
106
107
|
# File 'lib/deftones/source/user_media.rb', line 103
def state(time = context.current_time)
return :stopped unless @opened
active_at?(resolve_time(time)) ? :started : :stopped
end
|
#stop(time = nil) ⇒ Object
36
37
38
39
|
# File 'lib/deftones/source/user_media.rb', line 36
def stop(time = nil)
@capture_backend&.stop
super
end
|
295
296
297
298
299
300
301
302
|
# File 'lib/deftones/source/user_media.rb', line 295
def sync_capture_metadata!
return self unless @capture_backend
@device_id = @capture_backend.device_id if @capture_backend.respond_to?(:device_id) && !@capture_backend.device_id.nil?
@group_id = @capture_backend.group_id if @capture_backend.respond_to?(:group_id) && !@capture_backend.group_id.nil?
@label = @capture_backend.label if @capture_backend.respond_to?(:label) && !@capture_backend.label.nil?
self
end
|
#underflow_count ⇒ Object
Also known as:
underflowCount
78
79
80
|
# File 'lib/deftones/source/user_media.rb', line 78
def underflow_count
@underflow_count + backend_stat(:underflow_count)
end
|